Archive for the ‘Desenvolvimento’ Category
Como criar gems que instalam executáveis na sua máquina
Precisei criar uma gem que gerasse um executável no ambiente, sendo possível assim executar a biblioteca como um utilitário de linha de comando. Encontrei muito pouca documentação sobre como fazer isso, então vou deixar aqui caso seja útil para mais alguém.
O segredo é manter uma pasta bin dentro da estrutura de diretórios da sua gem e colocar ali dentro os arquivos que você deseja que se tornem executáveis após a instalação da gem. Você deve salvar estes arquivos sem a extensão .rb, caso contrário terá que escrever .rb na linha de comando também para inicializar o programa. Em sistemas POSIX você pode criar arquivos executáveis e usar o “shebang” para instruir o shell sobre qual programa deve ser utilizado para executar o conteúdo do arquivo. Assim, basta adicionar a seguinte linha ao início do arquivo:
#! /usr/bin/env ruby
Após criar seu arquivo dentro de bin, basta adicionar o mesmo como um executável à sua gemspec. Estou usando o Jeweler para criar minhas gems, então tudo o que preciso fazer é gerar minha gemspec usando a seguinte raketask que o arquivo será criado corretamente:
rake gemspec
Isso vai criar um arquivo gemspec contendo a seguinte linha:
Gem::Specification.new do |s| ... s.executables = ["bla", "ble", "bli"] ... end
Onde bla, ble, bli são arquivos dentro de bin que devem se transformar em executáveis após a instalação da gem. Quando sua gem for instalada, estes arquivos será copiados para dentro de /usr/bin e receberão permissão para serem executados.
A Cucumber step to generate any number of objects, for any model
A simple approach using Factory-Girl and some Ruby to prepare Cucumber scenarios with any number of objects, for any model
The Problem
When we write Cucumber scenarios, many times we’ll have to previously generate a certain number of records in the database. Then it becomes necessary to create several steps, each one capable to create objects for a specific model. It can be become very hard if we have many different models.
Given there are 3 products
Here we have ‘products’, which probably means we have a Product model and a step similar to this:
Given /^there are (\d+) products$/ do |n|
n.to_i.times { Product.create(:description => 'foo', :price => 15.00) }
end
Now imagine writing this kind of code for several models. Also, imagine the hell it will be if you have several validates_uniqueness_of statements in these models…
The Solution
We need to create a generic way to create objects for our scenarios. With some help from Factory-Girl and some Ruby code, it can become very easy!
1 – Configuring Cucumber to use Factory-Girl
I’ve explained it here before, it’s simples!
- Create a new folder to group all your factories. I use the same factories both for Cucumber and Rspec, so I put everything inside spec/factories. You can put it anywhere that makes sense for your project.
-
Inside this folder, create a file for each factory. It helps to keep everything organized. By convention, the name of each factory should be the name of the model, in singular, with camelCase replaced by underlines. For example, the definition of a factory for a ProductCategory model, inside a spec/factories/product_category_factory.rb file would look something like this
Factory.define(:product_category) do |pc| # ... end -
Configure Cucumber to include the factories when the scenarios are executed. We just need to add two lines of code in the features/support/env.rb file:
require 'factory_girl' Dir.glob(File.join(File.dirname(__FILE__), '../../spec/factories/*.rb')).each {|f| require f }
2 -Creating our magic step definition
The idea is to always write our steps with the pluralized name of the model, with camelCase replaced by spaces. This helps to make everything more readable e takes advantage of the convention we defined for the factories’ names to easily the right factory to use to build our objects. Another detail is that if we specify that in our step there should be “x” objects for a certain model, then all the objects currently in the database must be deleted. Let’s see some code! This code can be defined in any file inside features/step_definitions:
Given /^there are (\d+) (.+)$/ do |n, model_str|
model_str = model_str.gsub(/\s/, '_').singularize
model_sym = model_str.to_sym
klass = eval(model_str.camelize)
klass.transaction do
klass.destroy_all
n.to_i.times do |i|
Factory(model_sym)
end
end
end
This code does the following:
- Create any number of objects for any model.
- Opens a transaction and delete all objects for this model from the database.
- Creates n objects using Factory Girl (assumes there is a factory defined with a symbol which is the name of the model used in the scenario, with spaces replaced by underline and singular)
Now we can write steps like
Given there are 123 clients
or
Given there are 123 clients
And there are 7 product categories
and never worry with the definition for these steps again.
Cucumber: Um step para criar qualquer número de objetos, para qualquer model
Uma simples técnica utilizando Factory-Girl e um pouco de Ruby para preparar cenários do Cucumber com qualquer quantidade de objetos, de qualquer model.
O problema
Quando escrevemos cenários com o Cucumber, muitas vezes é necessário criar um determinado número de registros no banco de dados para preparar um determinado cenário. Isso faz com que tenhamos que criar vários steps, cada um capaz de criar objetos para um determinado tipo de model. Isso pode se tornal realmente trabalhoso se tivermos muitos models diferentes.
Imagine um trecho de um cenário como este:
Given there are 3 products
Aqui temos ‘products’, que provavelmente exige a existência de um model Product e de um step parecido com
Given /^there are (\d+) products$/ do |n|
n.to_i.times { Product.create(:description => 'foo', :price => 15.00) }
end
Agora imagine fazer esse tipo de coisa para vários models. Imagine também o inferno que isso se tornará se você tiver um monte de declarações do tipo validates_uniqueness_of nesses models…
A solução
Precisamos criar uma forma genérica de criar objetos nos nosso cenários. Com a ajuda do Factory-Girl e com um pouquinho de Ruby, a coisa fica fácil!
1 – Configurando o Cucumber para usar o Factory-Girl
Já expliquei isso aqui antes, é bem simples!
- Crie uma pasta para guardar suas factories em algum lugar. Eu uso as mesmas factories tanto pro Cucumber quanto pro Rspec, então acabo deixando tudo em spec/factories. Coloque onde fizer mais sentido para o seu projeto.
-
Dentro dessa pasta, crie uma factory por arquivo. Isso ajuda a manter tudo organizado. Convencione o nome de cada factory como sendo o nome do model, no singular, com o camelCase substituído por underlines. Por exemplo, a definição de uma factory para o model ProductCategory, dentro de um arquivo em spec/factories/product_category_factory.rb ficaria mais ou menos assim:
Factory.define(:product_category) do |pc| # ... end -
Configure o Cucumber para incluir as factories quando os cenários forem executados. Basta adicionar duas linhas no arquivo features/support/env.rb:
require 'factory_girl' Dir.glob(File.join(File.dirname(__FILE__), '../../spec/factories/*.rb')).each {|f| require f }
2 – Criando nossa definição de step mágico
A idéia é escrever os steps utilizando sempre o nome do model no plural, com camelCase substituído por espaços. Isso ajuda a deixar as coisas mais legíveis nos nossos cenários e se aproveita da convenção que criamos para o nome das factories para encontrar facilmente a factory a ser utilizada. Outro detalhe é que se especificarmos em nosso step que existem x objetos de um determinado model, todos os objetos previamente existentes deverão ser removidos. Vamos ao código, que pode ser definido em qualquer arquivo dentro de features/step_definitions:
Given /^there are (\d+) (.+)$/ do |n, model_str|
model_str = model_str.gsub(/\s/, '_').singularize
model_sym = model_str.to_sym
klass = eval(model_str.camelize)
klass.transaction do
klass.destroy_all
n.to_i.times do |i|
Factory(model_sym)
end
end
end
O que esse código faz é
- Cria qualquer quantidade de instâncias de qualquer model.
- Abre uma transação e apaga todos os objetos deste model do banco
- Cria n objetos utilizando o Factory Girl (assume que existe uma factory definida com um symbol que é o nome do model passado no cenário, com os espaços substituídos por underlines e no singular)
Ou seja, agora podemos escrever steps como:
Given there are 123 clients
ou ainda
Given there are 123 clients
And there are 7 product categories
e nunca mais nos preocupar com as definições destes steps. Detalhe: Estamos usando as regras para pluralização em inglês. Se for programar em português, lembre-se de definir novas regras de pluralização no Inflector.
Métodos de classe adicionados através de modules
Para implementar algo semelhante à herança múltipla em Ruby usamos modules. Através deles podemos usar o conceito de mix-ins, ou seja, podemos misturar os comportamentos descritos na implementação de diversos modules em uma única classe concreta.
Algo assim:
module Bar def metodo_legal_de_bar end end module Foo def metodo_legal_de_foo end end class FooBar include Foo include Bar end f = FooBar.new f.metodo_legal_de_bar f.metodo_legal_de_foo
Simples, muito simples. Acabamos de criar dois modules que definem um método cada. O único detalhe é que estes dois métodos são métodos de instância, ou seja, são mensagens que só podem ser passadas para objetos da classe que inclui o módulo. E se eu quiser criar um módulo que define um método de classe que poderá ser utilizado em diversas outras classes?
Bom, o código abaixo não funciona
module Foo def self.um_metodo_de_classe_declararo_em_Foo end end class Blergh include Foo end Blergh.um_metodo_de_classe_declararo_em_Foo # NoMethodError: undefined method 'um_metodo_de_classe_declararo_em_Foo' for Blergh:Class
Hum… e agora, ferrou? Não caro amigo… Ruby é seu amigo e nada lhe faltará. Todo module possui um método muito interessante chamado included. Na verdade esse método é um “hook”, executado automaticamente sempre que um module é incluído em alguma classe. Ok, e para que serve esse treco? Como eu costumo dizer quando vejo algumas coisas em Ruby, serve para fazer macumba! Você pode criar certos comportamentos muito interessantes escrevendo código dentro do método included, como por exemplo resolver nosso problema de criação de métodos de classe através de modules.
O método included recebe um único argumento: A classe que está incluindo aquele método. Assim:
module Bla def self.included(base) puts "Acabei de ser incluido pela classe #{base}!" end end class Blergh include Bla end
e só por fazer um “require” no arquivo que declara a classe Blergh você já vai ver no irb:
cassio@cassio-laptop:~$ irb >> require 'exemplo_module' Acabei de ser incluido pela classe Blergh => true
Interessante hein? Tudo bem, mas como vamos usar isso para “incrementar” nossa classe e criar novos métodos que podem ser chamados sem uma instância? Usamos o método extend. Este método adiciona os métodos de um module no nível da classe, ao contrário do método include, que adiciona os métodos de um module no nível de instância. A técnica a seguir é amplamente utilizada em Ruby para adicionar métodos de classe a uma classe existente, sendo inclusive amplamente utilizada dentro dos fontes do Rails:
module Bla def self.included(base) base.extend(ClassMethods) end module ClassMethods def blabla end def blibli end end end
Aqui foi declarado um module dentro de outro, algo bastante comum. Este module foi chamado de ClassMethods, mas qualquer outro nome poderia ter sido usado. É meio que uma convenção em Ruby chamar de ClassMethods, porque assim qualquer um que ler seu código vai entender que ali dentro estão sendo declarados métodos que serão adicionados às classes que incluirem o module em questão.
Quando o module é adicionado em alguma classe, o método included do module é chamado automaticamente e este por sua vez extende esta classe com os métodos declarados no module ClassMethods. Agora podemos fazer assim:
class Blergh include Bla end Blergh.blabla Blergh.blibli
Agora, pensando em escopo: O que significa self dentro dos métodos declarados em ClassMethods? Hum… self é a própria classe que incluiu o module e que agora está chamando o método declararo em ClassMethods.
module Bla def self.included(base) base.extend(ClassMethods) end module ClassMethods def blabla puts "Chamando o metodo de classe 'blabla' a partir da classe #{self}" end end end class Blergh include Bla end Blergh.blabla # <= Imprime "Chamando o metodo 'blabla' a partir da classe Blergh"
Com isso dá pra fazer muita macumba…
Um pouco de Erlang, programação funcional e recursividade
Comecei a estudar um pouco de Erlang recentemente. A linguagem realmente é bem diferente daquilo que estou acostumado, principalmente por ser um linguagem totalmente funcional e possuir certos conceitos que em um primeiro momento realmente podem confundir a cabeça de quem está mais acostumado com linguagens imperativas.
Na verdade, eu já acabei usando diversos conceitos de programação funcional em Ruby e até mesmo em Javascript. A existência de closures ajuda muito para que isso seja possível e para muitos problemas uma solução funcional pode se apresentar a mais natural.
Entretanto, em Erlang a coisa é diferente. Tudo é baseado em programação funcional.
Uma das estrututas de dados mais comumente utilizadas em praticamente qualquer linguagem de programação são as listas. Quer essas listas sejam representadas por simples arrays ou por estruturas mais complexas como listas encadeadas, as listas estão presentes sempre que desejamos realizar qualquer tipo de operação sobre conjuntos.
Em Erlang, as listas são representadas através de estruturas do tipo [el1, el2, el3, ..., eln], onde eli pode assumir formas mais complexas, como em Lista = [{banana, 3}, {pera, 5}]. Neste exemplo estamos representando, por exemplo, uma lista de compras que contém 3 bananas e 5 pêras. O problema é não confundir as palavras “banana” e “pera” com strings ou variáveis: neste exemplo, banana e pera são atoms, ou seja, apenas identificadores para elementos da lista.
Outro detalhe importante é que em Erlang um símbolo somente será considerado como uma variável se começar por uma letra maiúscula.
Mas vamos ver então em detalhes como Erlang opera sobre listas!
Vamos partir de um exemplo bastante simples. Suponha que você possui uma lista de números inteiros e deseja calcular o produtório desta lista, ou seja, sair multiplicando número por número até chegar em um total.
Em uma linguagem imperativa como C, isso poderia facilmente ser feito assim:
#include <stdio.h>
int main() {
int lista[5] = {3, 5, 7, 2, 8};
int prod = 1, i = 0;
for(i = 0; i < 5; ++i) { prod *= lista[i]; }
printf("Produto = %d\n", prod);
}
Até aqui, ok, Basta fazermos um laço e sair multiplicando os números dentro do array prod que representa nossa lista.
Mas e em Erlang, como ficaria isso? Bom, primeiro precisamos entender como Erlang processa os elementos de uma lista. Em Erlang, pode-se assumir que toda e qualquer lista é formada de duas partes: HEAD e TAIL. HEAD é sempre o primeiro elemento da lista, ou seja, um escalar. Já TAIL representa o restante da lista. Assim, na lista [1,2,3,4] temos HEAD = 1 e TAIL = [2,3,4]. Na verdade os nomes HEAD e TAIL são apenas convenções, não é obrigatório que você use estes termos para representar as duas “partes” das listas em Erlang. Mas convenções são boas e facilitam a compreensão do código que escrevemos em qualquer linguagem.
Como podemos então extrair e armazenar em variáveis os valores de HEAD e TAIL de uma lista? Usando pattern matchers. Na verdade, em Erlang todas as atribuições de valores a variáveis são feitas através da utilização de pattern matchers. O operador utilizado para isso é o =. Mas aqui, = tem um significado diferente do que você está acostumado! Sim! Igual não é mais igual e o mundo está perdido…
A verdade é que em Erlang = não representa necessariamente igualdade. = serve para “casar” padrões. Cada variável pode armazenar um determinado padrão e você pode utilizar o operador = para verificar se este padrão “casa” com algum outro e então fazer associações de valores dentro destes padrões. Assim:
%% Em Erlang, linhas que começam com % são comentários. %% Todas as linhas que representam "comandos" devem terminar com um ponto 1> X = 2.
Ok, agora a variável X vale 2. Mas isso porque antes disso não associamos nenhum outro valor a X, ela era uma variável “vazia”, ou, usando a terminologia do Erlang, X era uma unbound variable. Agora, se tentarmos alterar o valor de X, olhe o que acontece:
2> X = 4.
=ERROR REPORT==== 17-Aug-2008::14:34:44 ===
Error in process with exit value: {{badmatch,4},[{erl_eval,expr,3}]}
** exited: {{badmatch,4},[{erl_eval,expr,3}]} **
Que erro bizarro! Que porcaria de linguagem é essa que não me permite alterar o valor de uma variável? O que ocorre é que Erlang é uma linguagem para processamento paralelo, onde diferentes processos trocam mensagens entre si. Se temos por exemplo 3 processos A, B e C que se comunicam entre si e uma variável X que é usada pelos 3 processos, se A passar o valor para B e este alterá-lo, quando C for usar esta variável pode ser que ocorram resultados inesperados. Por isso Erlang mantém as variáveis imutáveis durante toda sua existência.
Partindo deste princípio, podemos fazer o seguinte:
1> X = 2. %%X era 'unbound' e acabou de receber seu valor (2) 2> X = 1 + 1. %% funciona, porque 1 + 1 representa o mesmo padrão que 2
Ok, vamos parar de enrolar e voltar para a nossa lista, de onde queremos extrair nossas partes HEAD e TAIL.
Podemos usar o operador | (nosso velho amigo ‘pipe’) para fazer o trabalho. Podemos então escrever:
Numeros = [3, 5, 7, 2, 8]. [H|T] = Numeros.
E após isso teremos a variável H armazenando 3 (nosso HEAD) e a variável T armazenando [5, 7 , 2, 8] (o resto da lista ou TAIL).
Para podermos calcular nosso produtório precisamos ainda dar um olhadinha em como funcionam funções em Erlang. O processo todo funciona através da utilização de modules. Um module é um elemento de software onde você pode declarar suas funções. Em seguinda você precisa exportar essas funções para que as mesmas possam ser utilizadas por código fora do módulo. Algo assim:
-module(produtorio). -export([prod/1]). prod([H|T]) -> H * prod(T); prod([]) -> 1.
Na primeira linha estamos fazendo a declaracao de um modulo chamado “produtorio”. Na segunda linha estamos exportando uma função chamada “prod” que recebe um único argumento (por isso o “/1″), para que a função possa ser usada por código em outras partes do programa.
A terceira e quarta linhas definem nossa função prod, a qual efetivamente realiza o nosso cálculo. Para explicar o que está acontecendo aqui, precisamos relembrar alguns conceitos de recursividade.
Uma função recursiva é uma função que, para atingir seu objetivo, realiza chamadas a si mesma. Assim, podemos por exemplo escrever uma função recursiva soma([1,2,3]) como soma(1, soma([2,3])).
Por definição, uma função recursiva deve possuir duas características:
1) Deve possuir uma solução trivial, ou seja, uma situação onde a função pára de chamar a si mesma recursivamente e a pilha de chamadas passa a ser desfeita, até a primeira chamada feita, onde o resultado finalmente é retornado ao ponto do código que fez a chamada a nossa função.
2) A função deve ser escrita de forma que caminhe naturalmente para a solução do problema ou, em outras palavras, a função deve ser convergente, caso contrário as chamadas recursivas ocorrerão infinitamente, levando a um estouro na pilha de chamadas da função.
Essas duas características podem ser facilmente observadas na nossa função prod acima. Na linha
prod([H|T]) -> H * prod(T);
temos a forma em que as chamadas recursivas ocorrerão, o que demonstra claramente que há convergência para a solução. A função recebe uma lista de valores, extraí o primeiro elemento da lista e retorna como resultado desta chamada específica o resultado da multiplicação deste elemento pelo resultado de uma chamada a si mesma, passando o restante da lista (ou TAIL) como argumento. É fácil observar que a cada nova chamada, TAIL diminui, até chegar a 0 (zero) elementos. Essa é a nossa solução trivial, encontrada pela linha
prod([]) -> 1.
Aqui, não é mais necessário realizar chamadas recursivas, pois sabe-se que para a lista vazia basta multiplicar por 1, ou seja, o elemento neutro da multiplição. A partir deste ponto a pilha de chamadas recursivas passa a ser esvaziada até chegarmos à sua basse, ou seja, a primeira chamada feita à função prod().
Por fim, podemos usar nossa função assim (através do erl, o console do Erlang):
1> c(produtorio).
{ok,produtorio}
2> Lista = [3,6,8,4,7].
[3,6,8,4,7]
3> P = produtorio:prod(Lista).
4032
4>
Em (1) estamos compilando nosso module produtorio para um arquivo com extensão .bean, para que possamos usá-lo por todo nosso programa. Em (2) declaramos uma variável Lista, que representa uma lista de valores sobre os quais desejamos calcular o produtório. Em (3) realizamos efetivamente a chamada à função prod. A sintaxe produtorio:prod indica que a função prod faz parte do module produtorio. Você pode pensar nisso como um esquema de namespaces.
O cálculo ocorre seguindo a seguinte lógica:
Chamada 1) HEAD -> 3, TAIL -> [6,8,4,7], retorno -> 3 * prod([6, 8, 4, 7])
Chamada 2) HEAD -> 6, TAIL -> [8,4,7], retorno -> 18 * prod([8,4,7])
Chamada 3) HEAD -> 8, TAIL -> [4, 7], retorno -> 144 * prod([4, 7])
Chamada 4) HEAD -> 4, TAIL -> [7], retorno -> 576 * prod([7])
Chamada 5) HEAD -> 7, TAIL -> [], retorno -> 4032 * prod([])
Chamada 6) Utiliza-se a solução trivial, retornando-se 4032 * 1 e dando fim à cadeia de chamadas recursivas.
Conforme eu for estudando mais coisas sobre Erlang eu vou postando aqui o que achar interessante!
Helper para usar o datepicker do JQuery no Rails
Nos dois projetos Rails em que estou trabalhando atualmente deixei de usar a dupla Prototype + Scriptaculous (padrão do Rails) e instalei o plugin jrails. Esse plugin possibilita substituir o Prototype e o Scriptaculous pelo JQuery nas aplicações Rails, mantendo funcionalidade da grande maioria dos helpers para javascript (toda aquelas coisas de rjs, etc), mas gerando código javascript compatível com o JQuery.
Fiz essa mudança porque o JQuery facilita muito a criação de interfaces mais atrativas, coisa muito importante para quem cria aplicações web corporativas. Com o esquema de plugins do JQuery, é possível criar aplicações muito responsivas, funcionais e bonitas.
Recentemente foi criada uma biblioteca para o JQuery chamada JQuery-UI, ou seja, um conjunto de funcionalidades específicos para a criação de interfaces com o usuário. Um dos componentes que acho realmente interessantes nesta biblioteca é o Datepicker, o qual cria um calendário bem bacana em campos de formulário que devem receber datas.
A idéia então foi: Como usar isso “The Rails Way”, sem ter que ficar escrevendo toda hora o código javascript necessário para criar uma instância do datepicker associada a um campo dos meus formulários? Simples: Criei um helper para isso. Mais precisamente, criei um helper no ApplicationHelper, para que o código fique disponível nas views de toda a aplicação.
Vamos ao código!
module ApplicationHelper
def datepicker_tag(model, attribute, options ={}, datepicker_options = {})
field_id = "#{model}_#{attribute}"
field_name = "#{model}[#{attribute}]"
field = ::ActionView::Helpers::InstanceTag.new(model, attribute, self)
options = {:id => field_id, :name => field_name}.merge(options)
datepicker_options = options_for_javascript(datepicker_options)
js = "$(document).ready(function() { $(\"\##{field_id}\").datepicker(#{datepicker_options});});"
field.tag(:input, options) + javascript_tag(js)
end
end
O método acima cria uma tag html input e gera o código javascript necessário para fazer com que este input torne-se um datepicker. Basicamente eu utilizo helpers já existentes no Rails e crio uma instância de ActionView::Helpers::InstanceTag, a qual fica associada ao model do form. Isso é importante porque quando você precisar trazer o formulário para edição, por exemplo, o campo já virá preenchido certinho.
Em seguida utilizo essa instância para criar uma tag do tipo input e concateno a saída disso tudo com o código javascript necessário para criar um datepicker associado ao nosso input.
Notem que as opcões recebidas como argumentos do método são utilizadas para inicializar o input e o objeto javascript criados.
Os parâmetros para o método são:
model: O model que está sendo criado/editado no form.
attribute: O atributo do model ao qual o datepicker será associado
options: hash de opções que devem ser passadas para a criação do input. Basicamente as mesmas utilizadas em helpers como o text_field_tag, etc.
datapicker_options: hash com opções que serão passadas para a criação do javascript que inicializa o datepicker. Possíveis valores para este campo podem ser obtidas na documentação do datepicker.
Para usar isso, precisamos incluir os arquivos do JQuery no seu layout. Como o JQuery-UI pode ser baixado de forma personalizada, ou seja, somente os arquivos que lhe interessam (para diminuir o footprint da biblioteca), o nome dos arquivos podem variar um pouco. De qualquer forma, basta baixar os arquivos javascript necessários (o core do jquery-ui e o arquivinho para o datepicker) e os arquivos CSS. Coloque tudo em seu lugar dentro da pasta public do seu projeto e adicione tudo na sua view, usando javascript_include_tag e stylesheet_include_tag.
Considere agora que você quer criar um formulário para um (adivinhem!) aluno, e que este cara possui uma data de nascimento. Seria legal que esse campo para a data de nascimento tivesse um datepicker bonitinho né?
<% form_for(@aluno) do |form| %> <%= datepicker_tag :aluno, :nascimento, :size => 10 %> <% end %>
A saída disso é algo assim:
<input id="aluni_nascimento" name="aluno[nascimento]" size="10" />
<script type="text/javascript">
//<![CDATA[
$(document).ready(function() { $("#aluno_nascimento").datepicker({});});
//]]>
</script>
Se quiser pode passar opções específicas do datepicker em um segundo hash.
Estou pensando em juntar isso com mais algumas coisas que usam o JQuery-UI e criar um plugin, vamos ver como anda meu tempo…
Não confunda ActiveRecord.find com Enumerable.find no Rails!
Eu primeiro aprendi Ruby para depois começar a brincar com o Rails. Esse deveria ser o processo natural e correto, mas sei que nem todo mundo tem começado a usar Rails dessa forma. De qualquer maneira, antes de escrever meus primeiros pet-projects com o Rails eu escrevi muito código Ruby, basicamente scripts de propósito geral.
Uma coisa com a qual me habituei bastante foi a utilizar o método find do módulo Enumerable. Esse método é uma mensagem que pode ser enviada a qualquer objeto de uma classe que inclua o módulo Enumerable em sua definição e serve, óbviamente, para procurar por algo dentro deste objeto. O caso mais comum é a classe Array, ou seja, uma lista de objetos quaisquer.
array = [1, 2, 3, 4, 5]
resultado = array.find { |i| i%2 == 0 }
No exemplo acima o método find vai retornar o primeiro elemento da lista que seja par. Retornaria nil caso nenhum valor atendesse à condição valor%2 == 0.
O problema é que eu fui tentar usar algo parecido dentro de um model em um dos projetos Rails no qual venho trabalhando.
Esse model segue um esquema mais ou menos assim:
class User < ActiveRecord::Base has_many :roles, :through => :permissions end class Role < AtiveRecord::Base has_many :users, :through => :permissions end class Permission < ActiveRecord::Base belongs_to :user belongs_to :role end
Em um determinado momento eu preciso verificar se um usuário possui um determinado role, entao tentei, ingenuamente, fazer algo como
class User < ActiveRecord::Base
def has_role?(role)
roles.find{ |r| r.description == role }
end
end
Simples, deveria funcionar perfeitamente. Só que quando eu rodava esse código, recebia uma mensagem do tipo “can’t find Role without an id”. Hummmmmm… Bingo! O problema é que quando acesso uma lista que representa um associação com outro objetos de outro model, o active record faz um pesquisa no banco e usa joins em cima dos ids dos objetos, ou seja, o código acima espera receber algo do tipo
def has_role?(role) roles.find :conditions => "blablabla..." end
Dessa forma o active record usa o valor recebido em :conditions para criar a cláusula “where” do sql enviado ao banco… E não é isso que eu quero, não quero que ele busque filtrando pelo banco, quero que ele busque todos os roles do usuário de uma vez só e pesquise na aplicação.
Mas se eu não posso usar o método find, porque o dito cujo existe na classe ActiveRecord::Base e no módulo Enumerable com o mesmo nome mas funcionalidade completamente diferente, o que fazer?
Simples: O método Enumerable.find na verdade é um alias (um sinônimo) do método Enumerable.detect. Ou seja: se eu enviar para minha lista a mensagem minha_lista.detect { …} eu tenho a funcioalidade que eu quero. E o código fica assim:
def has_role?(role)
roles.detect { |r| r.description == role }
end
Problema resolvido e mais uma lição aprendida.
Quanto maior o problema, mais você aprende
Seres humanos têm o hábito de fugir ao máximo de seus problemas. É algo automático, um mecanismo de auto-proteção. Em grande parte está ligado à nossa constante vontade de nos mantermos confortáveis. Mas o quão bom é estar sempre confortável? O quão bom é não ter problemas e ver que tudo corre bem, que não temos com o que nos preocupar?
Desenvolvimento de software tem muito disso. Problemas… E eles têm que ser resolvidos para ontem. Sim, é desconfortável e sempre estamos fugindo disso. O método que não se comporta como esperado, aquele algorítmo maluco que por mais que você rabisque, desenhe, calcule e imagine, quando você passa para o código acaba com algo que não passa em boa parte dos testes. Ou ainda aquela biblioteca que você precisa instalar e que quando vai compilar deixa seu console doido girando com tanto output de erro…
É, profissão ingrata…. será?
Programar é uma das atividades que, ao menos na minha opinião, faz que o profissional mais aprenda. A cada dia se aprende algo novo. Quer dizer, se o programador quiser, é claro. Se o programador não se acomodar, se ele não passar a gostar demais de se sentir confortável. O inverso natural de se sentir confortável neste cenário é enfrentar problemas. De frente. Se afundar até o pescoço naquela merda toda. Porque meu amigo, quando você sair dali, você não será mais a mesma pessoa, pode acreditar.
Resolver problemas complexos nos expõe a situações novas, a desafios novos. Quando mais cabeludo o problema, em mais lugares você vai ter que procurar as peças que juntas formarão sua solução e mais soluções diferentes você irá tentar. A experiência é sempre proveitável: você aprenderá tanto com as soluções que tiveram sucesso quanto com as que fracassaram. É importante saber porque uma solução fracassou, até mais importante do que saber porque outra obteve sucesso, afinal em um conjunto qualquer, é mais fácil ir separando aquilo que não te interessa do que tentar acertar o seu objetivo em meio ao caos de opções.
Portanto, não fuja dos seus problemas. Pelo menos não daqueles que podem lhe trazer novas idéias e uma nova percepção do seu trabalho. Quer seu trabalho seja desenvolvimento de software ou não, muito provavelmente assumir os problemas e se empenhar para resolvê-los irá lhe tornar um profissional melhor. E sabe o que é melhor? Com certeza quando você sair daquela merda toda, vai olhar a solução do problema por cima, pensar em quão boa ficou e em tudo que aprendeu no caminho e se orgulhar. É isso que separa os que se contentam com a mesmice e que são infelizes com o que fazem daqueles que encontram realização pessoal a cada dia.
Desenvolver software é isso. É se enfiar de cabeça na lama, chafurdar ali até encontrar a pérola, a solução. O código perfeito, o algorítmo perfeito, a arquitetura perfeita. É surpreender seus clientes e, mais importante, a você mesmo.
Não se sinta confortável demais, talvez isso seja um sinal de que você não anda aprendendo muito no seu dia e está ficando estagnado. Evolução é a palavra…
Integração Mentawai + DWR
Quando o modelo não serve
Sem sombra de dúvidas é isso que meu cérebro diz a cada vez que preciso alterar qualquer parte do sistema legado que estou cuidando atualmente: um tremendo spaguetti, onde model-view-controller se torna moew-controdel-llervi e demais permutações, tudo ao mesmo tempo. Código não documentado, C++ programado de maneira estruturada de cabo a rabo. O sistema funciona, sim senhor, e até consigo adicionar novas funcionalidades. Mas sabe qual a sensação que me dá quando finalmente coloco a coisa toda em produção? A sensação de que estou engordando um monstro que, se eu não tomar cuidado, em breve irá me engolir.
Mas o que me fez ter vontade de escrever sobre este assunto foi o que começou a acontecer hoje. algo que sei que vai perdurar até o fim do meu projeto atual.
Trata-se de uma aplicação web escrita em Java que acessa um banco de dados legado. o banco de dados da aplicação citada acima. O banco é bastante organizado e a princípio tudo parece fazer sentido na maneira com a qual ele foi construído. Mas quando tentei modelar alguns POJOs a partir das tabelas do banco, percebi que algo estava tremendamente errado. Tabelas com 45 colunas não podem estar corretas… Imagina uma classe com 45 atributos! E o pior: Não sei se vale à pena dividir as coisas… ou melhor, não vale à pena! Perceber que tudo aquilo que você gostaria de aplicar, toda aquela história sobre design, não irão funcionar desta vez, é meio desanimador…
E fico aqui pensando em uma solução. Onde fica a linha que separa um programa bem escrito e algo que cheira (e parece) gambiarra?


