Unobtrusive Javascript

Um monte de gente tem falado sobre Unobtrusive Javascript (ou UJS, para simplificar), mas há poucos que mencionam COMO fazer. Da mesma forma, o próprio Rails parece não ser muito decidido às melhores práticas (o que, sinceramente, é uma pena dada a idéia original do Rails de facilitar o desenvolvimento web). Aliás, talvez algumas mudanças do Rails valeriam para outro post, mas por hora, vamos para UJS.

A idéia do UJS, basicamente, é não misturar Javascript com HTML nas views. O que, sinceramente, é uma ótima idéia ao meu ver. O problema é como fazer isso de forma não-traumática. Para este exemplo, vamos ver inicialmente qual era a abordagem que eu usei, no passado, sobre UJS:

Antes, eu usava algum helper do Rails e criava as tags tipo <a href="algumaUrl" data-remote="true>, e no controller eu pedia para renderizar um template .js.erb. Isso trazia diversos problemas, por exemplo:

$('div').html(&quot;&lt;%= @user.name %&gt;&quot;);

Mas se o usuário tiver espaços no nome, precisaremos de "escape_javascript"… e aí começam alguns antipatterns (imagine HTML+ERB, mas dessa vez com todos os problemas de Javascript incorporados).

O ideal, e algo que tanto o Twitter como outros serviços web estão fazendo, é manter uma API web que renderiza os dados, e seu ambiente web nada mais é do que uma interface que consome esses dados (e renderiza algo na tela). Para esse exemplo, só vou usar jQuery e nada de Rails/Ruby/Whatever…
(more…)

Efeitos Colaterais e Códigos

Esse assunto é bem complexo, e envolve certas situações interessantes. Mas antes, precisamos de uma definição do que é um efeito colateral em código: quando se escreve um código em uma linguagem, cada procedimento/função/método deveria fazer uma, e apenas uma tarefa. Mais do que isso, e estamos inserindo um efeito colateral no código. Quando programamos em linguagens puramente funcionais, como por exemplo Haskell, fica evidente que estamos mexendo com uma função com efeitos colaterais porque ela fica, de certa forma, “marcada”.

Ok, e em Ruby, por exemplo? Como saber? A idéia é simples (e talvez isso possa até ser uma definição formal): “Uma função não tem efeitos colaterais quando não importa quantas vezes ela é chamada, ela sempre traz os mesmos resultados”. Embora simples, na teoria, identificar uma função assim nem sempre é fácil. Por exemplo, imaginemos um caso bem interessante: a função “save” do ActiveRecord. Ela parece, em teoria, não ter efeitos colaterais, mas ela possui um: vamos para um código na prática:

class Person &lt; ActiveRecord::Base
  validates_uniqueness_of :name
end

me = Person.new :name =&gt; &quot;Maurício&quot;
myself = Person.new :name =&gt; &quot;Maurício&quot;
me.save #true.
me.save #false. FALSE?

Tecnicamente falando, as duas funções deveriam retornar true: eu passei dois objetos exatamente iguais para elas. O segredo, se é que há algum, é que o método “save” na verdade faz duas coisas: valida os objetos e salva-os no banco. O principal problema é o “salvar no banco”, na verdade: estamos definindo um estado “global”, digamos assim. Todos os nossos novos objetos “Person” vão, automaticamente, ter que consultar o banco (nosso “estado global”) e verificar se a propriedade “name” já existe. Como se, por causa desse “estado” todos os saves automaticamente ganham um “if” a mais. Outra coisa também que o “save” faz que indica um efeito colateral é mudar o “id” do objeto:

me = Person.new :name =&gt; &quot;Maurício&quot;
p me.id #retorna nil
me.save
p me.id #retorna um número qualquer

(more…)

O Método “Extend” e Seus Usos

Provavelmente muita gente conhece o método “extend”, usado principalmente em classes para adicionar métodos, tais como:

module Nameable
  def set_name(name)
    @name = name
  end
end

class MyClass
  extend Nameable
  set_name &quot;Foo Bar&quot;
end

Claro que há pessoas que fazem verdadeiras aberrações, tipo um “module” que define o callback “included” que chama um “extend”, tipo essa situação:

module Nameable
  def self.included(klass)
    klass.extend Nameable::ClassMethods
  end

module ClassMethods
    def set_name(name)
      @name = name
    end
  end
end

class MyClass
  include Nameable
  set_name &quot;Foo Bar&quot;
end

Mas vamos ignorar esse tipo de coisa e pensar em outras formas de usar o “extend”. Digamos que temos uma classe como a seguir:

class Authenticator
  def login(username, password)
    if User.find_by_username_and_password(username, password)
      return true
    else
      return false
    end
  end

Ok, temos uma regra para autenticar (aviso: não use isso em produção, o código prevê que os usuários tem suas senhas gravadas no banco sem criptografia nenhuma). Digamos, agora, que em um determinado cliente, esse código só não é o suficiente: o cliente quer que, antes de autenticar no banco, se autentique no sistema

Uma solução é usar monkey-patch. Nesse caso, teríamos um código em outro lugar que redefiniria a classe e adicionaria novos métodos, tipo:
(more…)

Testes de Controller – a Saga

Semana passada comecei finalmente um projeto do zero usando Rails 3.1. A experiência foi novidade para mim, que por causa de uma série de legados (e também por questão de performance) estava preso no Rails 2.3, e não tive a oportunidade de ver como os testes funcionam no Rails 3.

Mas antes de chegar no assunto, vamos rever que o Rails não é puramente MVC. O “Controller” do Rails agrega coisas que deveriam ser feitas na view (basicamente, buscar o objeto para ser exibido). Para mais detalhes, ver meu post anterior.

Por esse motivo, e unicamente por este motivo, eu não acredito ser possível fazer teste unitários de controllers.

Um teste unitário deve, em teoria, testar um pedaço do sistema, isoladamente de outras partes. Como fazer um teste unitário de algo que é, essencialmente, um “glue code”, ou seja, um código que une regras de negócio (Models) e interfaces (Views)?

Antes do Rails 3, eu usava uma abordagem mais “integrada” para esses specs. No controller, eu usava a palavra-chave do rspec-rails “integrate_views”, e testava o par “controller-view”. Os specs ficavam mais ou menos assim:

describe PeopleController do
  integrate_views

it 'should show people on &quot;index&quot;' do
    sessions[:user_id] = Factory(:user).id
    Factory :person, :name =&gt; &quot;Foo Bar Baz&quot;
    get :index
    response.should be_success
    response.body.should include(&quot;Foo Bar Baz&quot;)
  end

it 'should render &quot;new&quot; view if validation failed&quot; do
    sessions[:user_id] = Factory(:user).id
    post :create, :person =&gt; { }
    response.should render_template(&quot;new&quot;)
  end
end

Claramente, isso não é um teste unitário, mas há um grande ganho nessa abordagem: se eu resolver mudar a variável “@users” para “@records”, e atualizar a view, não preciso mexer em nenhum spec. Na prática mesmo, eu não preciso mexer em nenhum SPEC se eu mudar o layout, adicionar mais informações na view, buscar mais registros no controller e atualizá-los na view, enfim, em qualquer momento eu sei, exatamente, se o teste está falhando ou passando, sem as fragilidades que mocks podem oferecer.
(more…)

Model View Controller

Ultimamente, Rails tem se popularizado, e com ele o famoso (e já antigo) MVC. Porém, como todas as coisas, a prática acabou sobrepondo a teoria, então achei que seria interessante falar um pouco sobre MVC, já que parece-me que algumas confusões começaram a acontecer. Esse post é imensamente baseado nesse post do Martin Fowler sobre arquiteturas GUI, então seria interessante ler ele também.

Enfim, vamos lá: no Rails, quando fazemos um “scaffold”, é criada uma combinação de coisas para nós: uma “migration”, que cria uma tabela no banco. Um “model”, que basicamente é o mapeamento dessa tabela para um objeto. Um “controller”, que faz a busca do registro certo e repassa para a renderização da tela. Por exemplo, a ação “edit”:

  def edit
    @foo = Foo.find(params[:id])
    render :action =&gt; 'edit' #Isso é redundante, mas deixa explícito um aspecto importante.
  end

Além disso, há uma série de boas-práticas, tais como não concentrar código de regra de negócio no controller, não colocar lógica nas views, enfim. Porém, essas “regra gerais” pecam em um ponto:

Rails não é 100% MVC…
(more…)

Redes Sociais

Bom, dei uma parada sobre os posts mais técnicos, mas estou preparando um sobre alguns estudos que fiz em Scala. Até lá, vou falar de um assunto que ultimamente tem me deixado meio pensativo: redes sociais.

Para os que me conhecem mais a fundo, sabem que eu sou meio “alérgico” a redes sociais. Aderi ao twitter e ao facebook depois que muita gente já estava usando, e não tenho linkedin, nem google buzz ou google+. Só que eu acho que algumas pessoas estão “perdendo o fio” do que é cada rede social. Usei o twitter, sempre, mais como uma forma de me manter atualizado tecnicamente do que para encontrar amigos-afinal, eu não vejo o motivo de ficar trocando mensagens de 140 caracteres com amigos meus (e deixar isso disponibilizado para o mundo). Aliás, cabe aqui uma observação interessante: a língua, o idioma, é absolutamente frágil. Por isso existem tantas formas de dizer algo, de falar ou pronunciar uma palavra, um sentido… por isso o “teste de Turing” ainda não foi vencido, por isso que é tão difícil processar linguagem natural, por isso que existem trocadilhos e poesias-todos, abusando de características da linguagem que podem dar duplo-sentido, dupla-interpretação, etc.

Isso, na linguagem falada. A linguagem escrita é MUITO pior.

Porque, na linguagem escrita, você não tem aspectos simples como o olhar da pessoa, sorrisos, movimento corporal, tom de voz, e às vezes é difícil de dar a entender que uma coisa é sarcasmo quando se está escrevendo (antes, se usava itálico, tipo “Hahaha, claro que eu vou fazer isso!”). E em 140 caracteres, é MUITO mais difícil. Especialmente em português, que é um idioma que notavelmente se escreve demais (“Right click” vs “Clique com o botão esquerdo do mouse” é um exemplo bem notável). Percebo isso no twitter quando eu não gosto de algo e posto lá: pra não ser subentendido, eu preciso escrever mais de 140 caracteres…
(more…)

Ensaio Sobre Graduação

Depois de assistir a palestra do Tenderlove no RubyConfBR (e ficar impressionado porque estava estudando EXATAMENTE os assuntos de Automatos há pouco tempo atrás), vi uns tweets sobre graduação. Na verdade, foi principalmente o tweet do @dannluciano que trouxe uma série de respostas de outros tweets que eu concordo até certo ponto, e discordo de muitos outros pontos.

Proponho uma pergunta: qual é a idéia da graduação, nos tempos como hoje? Explicando, em tempos de Wikipedia, de Google, aonde entra uma graduação? Acho que aqui, temos que separar duas coisas: Faculdade, Universidade, e Colegial Técnico.

Pra maior parte das pessoas (e infelizmente, para nosso país e o mercado de trabalho e governo dele), a graduação é um “upgrade” da formação média, e o “colegial técnico” fica meio perdido nesse meio do caminho… além disso, muitos professores (e infelizmente eu conheço isso, tendo trabalhado numa universidade federal) ainda acham, nos dias da Wikipedia, que são donos do conhecimento, e ninguém chega ao conhecimento sem o intermédio deles.

Os que me acompanham no Twitter devem ter visto que eu prestei o Poscomp. Num dos livros que peguei, havia a seguinte frase:

Função injetora é aquela que, para dois elementos distintos a, a’ ∈ A, f(a) ≠ f(a’)

Uhn… então, eu resolvi ir para a Wikipedia:

Uma função diz-se injectiva (ou injetora) se e somente se quaisquer que sejam x1 e x2 pertencentes ao domínio da função, se x1 é diferente de x2 implica que f(x1) é diferente de f(x2)

Há um “Link” para “domínio”, e há um gráfico mostrando graficamente essa explicação. Não precisa ser um gênio para entender que a explicação da Wikipedia está mais simples e melhor explicada e exemplificada. Ok. Mas, e isso é importante notar, eu não chegaria ao artigo da Wikipedia sem o livro, sem o poscomp, e não teria entendido a palestra do Tenderlove sem ter estudado para o Poscomp (ainda que eu tenha usado os livros como “grandes índices” e a Wikipedia como forma de aprender, propriamente dita).
(more…)

Sou um Anti-Identação

O título desse post parece estranho, mas vamos lá. De fato, eu sou um anti-identação, não gosto de ver meu código identado.

Não estou dizendo que eu não idento meu código-longe disso. Apenas que eu prefiro evitar a identação sempre que for possível. Mas vamos por partes…

Primeira coisa, isso começou há algum tempo quando uma pessoa propôs o seguinte problema: montar uma lista encadeada (Linked List) com as seguintes regras:

1) A lista deve validar se ela está vazia, e lançar um erro caso tente-se retirar um item da lista vazia
2) Cada método pode ter, no máximo, uma linha
3) Não se pode usar “if” em momento algum

Claro que é uma bela loucura, mas depois que eu consegui resolver esse código, pensei: se eu consegui resolver um problema desses com apenas uma linha por método, será que meus códigos não tem coisa demais?

Aí, comecei a experimentar isso em código de produção. Isso meio que criou um estilo de codificar bem interessante, e vou tentar mostrar aqui com alguns exemplos:
(more…)

Orientado a Objetos versus Funcional

Bom, esses dias estava estudando Scala. Uma linguagem multi-paradigma, mas que parece mais “funcional” do que “imperativa”. Scala cai numa posição ainda nebulosa para a maior parte das pessoas (e acho até que para o mercado também). Afinal, imutabilidade é “programação funcional”? Scala não faz nada que impede “side-effects” no código, como Haskell por exemplo, então ela é funcional mesmo?

Como mesmo eu não tenho muitos conhecimentos em linguagens funcionais, resolvi propor um problema para mim mesmo: implementar uma árvore binária em Ruby, e depois portá-la para Scala, tentar uma abordagem imutável em Scala, e depois portar para Haskell. O código está no github, mas algumas coisas vão ser discutidas aqui.

Primeiramente, a árvore imutável é feita recriando a árvore inteira. Claro, não podemos re-criar apenas um nó e apontar, digamos, a referencia de seu pai para esse novo nó, porque o pai é imutável (assim como qualquer outro aspecto do programa).

class Node[A &amp;lt;% Ordered[A]](value: A = None, left: Option[Node[A]] = None, right: Option[Node[A]] = None) {
    def insertNew(newValue: A): Node[A] = value match {
        case v if(newValue &amp;lt; v) =&amp;gt; insertLeft(newValue)
        case _ =&amp;gt; insertRight(newValue)
    }

<pre><code>private def insertLeft(newValue: A) = new Node(value, newChild(left, newValue), right)
private def insertRight(newValue: A) = new Node(value, left, newChild(right, newValue))
private def newChild(child: Option[Node[A]], newValue: A) = child match {
    case Some(child) =&amp;amp;gt; Some(child insertNew newValue)
    case None =&amp;amp;gt; Some(new Node(newValue))
}
</code></pre>

}

(more…)