Models e Migrations mais espertos

Posted on July 15, 2007

Da série “Plugins que uso ;)”: Sexy Migrations, Validation Reflection e Validate Attributes.


Sexy Migrations

Nome sugestivo? :) Talvez seu chefe não goste… (renomeie), mas a satisfação é garantida (ops!).

Por exemplo, sem Sexy Migrations:

    create_table :funcionarios do |t|
      t.column :nome, :string
      t.column :email, :string
      t.column :nascimento, :date, :null => true
      t.column :empresa_id, :integer
      t.column :created_at
      t.column :updated_at
    end

Com Sexy Migrations:

    create_table :funcionarios do |t|
      string :nome, :email
      date :nascimento, :null => true
      fkey :empresa
      timestamps
    end

Gostou? Acesse o ‘post’ do projeto. :D

Vaditation Reflection

Que tal ter informações sobre as validações de um model? Para que? Para utilizar nos testes ou nos formulários. Como? É só instalar o Validation Reflection e usar:

Funcionario.reflect_on_all_validations
Funcionario.reflect_on_validations_for(:nome)

Isto retornará um vetor de MacroReflections , com informações sobre as validações do Model(ou de apenas um atributo). MacroReflections também são os mesmos utilizados para armazenar informações sobre as agregações e associações.

Exemplo:

Funcionario.reflect_on_validations_for(:nome).detect do |v|
   v.macro == "validates_uniqueness_of" 
end.options[:scope]

O Validation Reflection também possui três métodos que define algumas validações baseadas no metadados da tabela:

validates_presence_of_mandatory_content_columns # v*_presence_of para atributos :null => false
validates_lengths_of_string_attributes #v*_size_of para strings
validates_all_associated # v*_associated para todas as associações
E se não fosse suficiente, ainda adiciona no class das tags dos campos dos atributos informações sobre as validações, fazendo text_field(‘funcionario’,’nome’) obrigatório com limite 30 retornar:
<input id="funcionario_nome" name="funcionario[nome]" class="mandatory  validate_maxlength_30" size="30" type="text" />

Validate Attributes

Com o Validate Attributes podemos chamar o velho valid? para um ou mais atributos. Exemplo:
@funcionario.validate_attributes(:only => ["nome", "email"])

Interessante para testes, wizards e validações com ajax.


Espero que (alguém) tenha gostado! :) E até as próximas dicas! :D

Flexperimentando com o Rails/WebOrb

Posted on July 08, 2007

Tirei um tempinho para testar o Flex e sua integração com o Rails utilizando o plugin WebOrb. Segue o passo a passo que realizei, que deve servir para quem quer iniciar e não sabe como. Boa leitura! :D

Antes de começar, aqui vai uma imagem do resultado: Demo Flex Usuários

Utilizei o Flex2 SDK e o plugin 9 do Flash no Windows. Como só utilizaremos o SDK no final, pode deixar baixando enquanto está implementando. :)

Crie o projeto:
rails demo_flex
Instale o plugin WebOrb (lembre de executar na pasta raiz do projeto):
ruby script/plugin install http://themidnightcoders.net:8089/svn/weborb

Para verificar se tudo ocorreu bem já pode iniciar o servidor (ruby script/server ) e acessar: http://localhost:3000/examples/main.html ou http://localhost:3000/examples/example.html.

Entre os arquivos e pastas adicionados pela instalação do plugin temos:
  • A pasta app/services onde implementamos os serviços disponíveis para serem acessados via RPC. Nela encontramos alguns exemplos.
  • O arquivo config/WEB-INF/flex/remoting-config.xml onde declaramos os serviços implementados.
  • A pasta public/examples onde temos o código dos exemplos.

Partindo para a criação do nosso próprio exemplo: listar registros de uma tabela de usuários, crie seu database e edite o config/database.yml de acordo.

Caso esteja usando MySQL, segue uma ajuda:
create database demo_flex_dev;
grant all on demo_flex_dev.* to 'demo_flex'@'localhost' identified by '123';
flush privileges;
E no database.yml:
development:
  adapter: mysql
  database: demo_flex_dev
  username: demo_flex
  password: 123
  host: localhost

Depois:

ruby script/generate model Usuario login:string senha:string nome:string
rake db:migrate
Para verificar se até agora está tudo ok e aproveitar para adicionar alguns dados:
ruby script/console
Usuario.create!(:login => 'roberto', :senha => '123', :nome => 'Roberto Soares')
Agora crie o arquivo app/services/UsuarioService.rb:
require 'weborb/context'
require 'rbconfig'

class UsuarioService
  def list
    Usuario.find(:all)
  end
end
E declare-o no arquivo config\WEB-INF\flex\remoting-config.xml:
    <destination id="UsuarioService">
        <properties>
            <source>UsuarioService</source>
        </properties>
    </destination>

Copie o arquivo public/examples/example.html para public/examples/usuarios.html substituindo todos ‘example’ por ‘usuarios’ para ganhar tempo. :)

Para finalizar crie o arquivo public/examples/usuarios.mxml que pode ser baixado aqui.
<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" 
    creationComplete="setUp();loadList();">

    <mx:Script>
    <![CDATA[    
        import mx.rpc.remoting.RemoteObject;
        import mx.controls.Alert;
        import mx.rpc.events.ResultEvent;
        import mx.rpc.events.FaultEvent;     

        private var remoteObject:RemoteObject;

        public function setUp():void
        {
            //objeto que representa o nosso servico
            remoteObject = new RemoteObject();
            remoteObject.destination = "UsuarioService";

            //em caso de sucesso do método list do servico,
            //será chamado o método onList recebendo o resultado
            remoteObject.list.addEventListener("result", onList);

            //em caso de falha será chamado o onFault
            remoteObject.addEventListener("fault", onFault);        
        }

        public function loadList():void
        {
            //aqui é chamado o list do UsuarioService
            remoteObject.list();               
        }

        public function onList(event:ResultEvent):void
        {
            var usuarios:Array = event.result as Array;

            //atualizando o DataGrid
            listDataGrid.dataProvider = usuarios;
        }

        public function onFault (event:FaultEvent):void 
        {
            Alert.show(event.fault.faultString, 'Error');
        }

    ]]>
    </mx:Script>

    <mx:TitleWindow title="Exemplo" layout="vertical" height="100%" width="100%">

            <mx:Button label="Atualizar Lista" click="loadList()"/>

            <mx:DataGrid id="listDataGrid" width="100%" height="100%">
                <mx:columns>
                    <mx:DataGridColumn dataField="login" headerText="Login"/>
                    <mx:DataGridColumn dataField="senha" headerText="Senha"/>
                    <mx:DataGridColumn dataField="nome" headerText="Nome"/>
                </mx:columns>
            </mx:DataGrid>

    </mx:TitleWindow>

</mx:Application>
Para terminar, compile o usuarios.mxml usando o mxmlc do Flex2SDK:
endereco_do_sdk\bin\mxmlc --compiler.services ../../config/WEB-INF/flex/services-config.xml -compiler.context-root ../../config/WEB-INF/flex usuarios.xml

Dica: coloque a pasta bin do FlexSDK no PATH do sistema. ;)

Pronto! É só rodar o servidor ( ruby script/server ) e acessar http://localhost:3000/examples/usuarios.html.

Download do projeto completo do exemplo(1,09MB).

Gostou? Vá dar uma olhada em outras fontes: http://blog.egenial.com.br #olá instrutor :D http://flexonrails.net http://flexiblerails.com #primeiro livro FlexOnRails http://flex.org http://livedocs.adobe.com/flex/2/langref/ #doc das classes do flex

Na próxima testarei com o FoRc que não utiliza RemoteObject, mas sim HTTPService.

t+!

Aptana suportando Adobe AIR

Posted on June 20, 2007

O pessoal do aptana parece estar realmente com fome. Se já não bastasse ter engolido o Rails, agora quer abocanhar também o Adobe AIR. Eu, que estou no curso FlexOnRails da e-Genial, até gostei da idéia! :P

Ainda não experimentei, mas pelo screencast dá para saber até onde já avançaram.

Por enquanto, continuo com meu vim. :D

Estudando a língua do Ruby: Japonês

Posted on May 27, 2007

Algumas dicas para os interessados em aprender japonês:

Livros: Vídeos:
  • Curso de Nihongo em DVD: Professora japonesa particular com direito a pause! Fantástico. (Desejava saber a origem deste vídeo…)
Sites:
  • Otaku Project: Excelente apoio, com letras de músicas, rádio, banco de kanjis, fórum e muito mais.

Conhece outro recurso? Comenta! :)

Radiant 0.6

Posted on April 24, 2007

Radiant é um ‘simples’ Content Management System(CMS) desenvolvido com RubyOnRails e utilizado no próprio site do Ruby.

Após 6 meses de jejum, lançaram uma nova versão, tendo como maior novidade uma forma fácil de extendê-lo (ainda vou dar uma olhada nisso).

Aptana. merge!(RadRails). merge!(RDT)!!!

Posted on April 21, 2007

O pessoal do Aptana anunciou a contratação do líder do RDT para trabalhar no “Ajax and Rails IDE. Com certeza uma excelente notícia para muitos desenvolvedores. :D

Cheguei até a pensar que o RadRails morreria :( . Ainda bem que não. :)

UPDATE: Download disponível do “Aptana Rails IDE Beta with RadRails and RDT

Ubuntu Feisty Fawn Lançado

Posted on April 19, 2007

Hoje foi ‘oficialmente’ lançado. Como já estava usando-o, a única novidade que lembro é o melhor suporte a wireless, realmente significativo para mim. ;) A página inicial do site oficial foi substituída por uma com mirrors para download da nova versão (pelo menos por hoje deve ficar assim).

Liberado Código do ClockingIt

Posted on April 14, 2007

Para quem não conhece, ClockingIt é uma ferramenta de gerenciamento de projetos (com um excelente sistema de timetracking) desenvolvida em Rails :) por um casal da Noruega (Erlend and Ellen Simonsen, um desenvolvedor e uma webdesigner).

Disponível como serviço desde o ano passado(conheci em setembro/2006) e, felizmente, ‘agora’ (3 de abril) teve seu código liberado sob a licença MIT.

Dando uma olhada no código já deu para perceber que necessita de algumas modificações para se tornar ‘distribuível’. Muitos links tem referência ao domínio do projeto, por exemplo. :(

Estou finalizando a tradução (pt_BR) e depois estudarei que pontos poderei auxiliar no projeto.

Tendo novidades posto aqui. :)

UPDATE: A tradução já está no wiki do projeto.

RSpec: Behaviour-Driven Development On Ruby

Posted on April 06, 2007

Escrevi um pouco apressado, estarei viajando daqui a pouco e como já tinha uma parte no ‘draft’, resolvi publicar logo. :)

O código apresentado nos exemplos está utilizando os métodos da versão do RSpec 0.9, que foi lançado ontem na lista de usuários e o site do projeto ainda apresenta a versão 0.8.2(até o momento), porém nada complicado de “converter”.

Então, aqui vamos. :)

Pequena Introdução

Muito, e há muito tempo, tem se falado de Desenvolvimento orientado a Testes, ou TDD(Test-Driven Development). Metodologias, como a Extreme Programming, que fazem sua utilização, além de frameworks e ferramentas para as mais diversas linguagens. “Mas será que estamos fazendo corretamente? TDD é realmente sobre testes?”. Foram alguns dos questionamentos que levaram a Dave Astels e Dan North a criarem outra alternativa: BDD, Behaviour Driven Development, ou Desenvolvimento orientado a Comportamento. Notaram que experientes praticantes de TDD chegavam a um ponto que percebiam que não estavam escrevendo testes e sim definindo comportamentos. Tendo isso em mente, desenvolveram frameworks que permitissem um melhor foco nessas definições: JBehave e RSpec(tema deste post).


Instalando


gem install rspec

Possui algumas dependências como o rcov e o rake, acesse a documentação para outras.

Estrutura básica


#no "describe" descrevemos o que estamos especificando no momento
describe Carteira do 

    #este método é chamado antes de cada "it", pode ser utilizado para indicar um estado dos objetos envolvidos e mocks.
    setup do
        ...
    end

    #nos métodos "it" é onde realmente trabalhamos
    it "deve ter um método para entrada $" do
        ...
    end

    it "não deve estar vazia" {
        ...
    }

end

#podemos definir mais de um describe por arquivo.
describe Carteira, " (validações)" do #este describe resultaria em: "Carteira (validações)" 
    ...
end

Should e Expectations


O RSpec adiciona os métodos should(‘devia’) e should_not(‘não devia’) à Object, tornando disponível para qualquer objeto de sua aplicação (durantes os testes :) ):
#exemplos
carteira.should_not ...
post.should_not ...
post.save!.should ...

E também define um conjunto de Expectations(‘expectativas’) para que possamos definir os comportamentos esperados.
#exemplos
be_...(...)
have(...).(...)
raise_error(...)
‘Juntando’:
carteira.should_not be_a_kind_of(Carro)
#carteira não devia ser do tipo Carro

post.should_not have(3).comentarios
#post não deve ter 3 comentários

post.save!.should raise_error
#post.save! devia causar erro

A leitura flui melhor do que ‘assert aquilo’, não? :) Veja outros Expectations.

Exemplo


Como exemplo vamos definir o comportamento de Carteira.

Primeiro criamos o “esqueleto”:
#arquivo carteira_spec.rb
require 'carteira' #vamos criar mais abaixo
describe Carteira do

    setup do
        @carteira = Carteira.new
    end
end

Agora vamos para o primeiro comportamento:

  • o valor inicial de dinheiro deve ser 0. :|

it "o valor inicial de dinheiro deve ser 0 :|" do
    @carteira.dinheiro.should == 0
    @carteira.dinheiro.should eql?(0) #alternativa 1
    @carteira.dinheiro.should be_zero #alternativa 2        
end

Três formas de (d)escrever. Na última o “be_zero” na verdade está utilizando o método “zero?” de dinheiro(Fixnum), servindo para qualquer ‘predicate’ (método terminado em ’?’). Ex: be_include (Array:include?), be_empty(String:empty?).

Mais dois comportamentos:
  • o método ganho(x) deve incrementar o dinheiro em x. :)
  • o método gasto(x) deve decrementar o dinheiro em x, lançar erro SemGranaError caso o valor for se tornar negativo. :(

it "o método ganho(x) deve incrementar o dinheiro em x. :)" do
    @carteira.ganho(3)
    @carteira.dinheiro.should == 3 #outra forma! =]        
end

it "o método gasto(x) deve decrementar o dinheiro em x, lançar erro SemGranaError caso o valor for se tornar negativo. :(" do
    lambda{ @carteira.gasto(3) }.should raise_error(SemGranaError)

    @carteira.ganho(3)
    @carteira.gasto(3).should == 0        
end

Fácil, não? Agora vamos criar nossa Carteira, para podermos executar os testes(?):

#arquivo carteira.rb
class Carteira

    attr_accessor :dinheiro

    def initialize
    end

    def ganho(dinheiro)
    end

    def gasto(dinheiro)
    end

end
Para executar: spec carteira_spec.rb. Como saída teremos:
FFF

1)
NoMethodError in 'Carteira o valor inicial de dinheiro deve ser 0 :|'
undefined method `dinheiro' for #<Carteira:0xb7c51270>
./carteira_spec.rb:10:

2)
NoMethodError in 'Carteira o método ganho(x) deve incrementar o dinheiro em x. :)'
undefined method `dinheiro' for #<Carteira:0xb7c3b484>
./carteira_spec.rb:17:

3)
NameError in 'Carteira o método gasto(x) deve decrementar o dinheiro em x, lançar erro SemGranaError caso o valor for se tornar negativo. :('
uninitialized constant SemGranaError
./carteira_spec.rb:21:

Finished in 0.009035 seconds

3 examples, 3 failures

Cada ‘F’ no início representa uma falha e cada tópico descreve uma falha. É interessante que tudo falhe na primeira vez, para sabermos que funcionou depois que implementamos (oras!).

Implementando a primeira especificação:
    def initialize
        @dinheiro = 0
    end
Executando(spec carteira_spec.rb), temos:
.FF

1)
'Carteira o método ganho(x) deve incrementar o dinheiro em x. :)' FAILED
expected 3, got 0 (using ==)
./carteira_spec.rb:17:

2)
NameError in 'Carteira o método gasto(x) deve decrementar o dinheiro em x, lançar erro SemGranaError caso o valor for se tornar negativo. :('
uninitialized constant SemGranaError
./carteira_spec.rb:21:

Finished in 0.008884 seconds

3 examples, 2 failures

“E cadê o que eu fiz?”. Virou um ’.’, aquele pontinho quer dizer que funcionou.

Finalizando implementando o resto:
#arquivo carteira.rb
class Carteira

    attr_accessor :dinheiro

    def initialize
        @dinheiro = 0
    end

    def ganho(dinheiro)
        @dinheiro = @dinheiro + dinheiro
    end

    def gasto(dinheiro)
        raise SemGranaError if (@dinheiro - dinheiro) < 0
        @dinheiro = @dinheiro - dinheiro
    end

end

class SemGranaError < StandardError

end
Saída:
...

Finished in 0.007725 seconds

3 examples, 0 failures

Experimente outras opções de formatação, para olhar as opções execute: spec – -help

Fim!

Gostou? Se sim, no próximo projeto utilize o RSpec, ou alguma ferramenta BDD ou TDD, seu aplicativo ficará saudável, não tem mais desculpa. (Não preparei nada para a alternativa ‘não’ :D ).

Já estou preparando posts sobre mock e rspec_on_rails.

Comporte-se e até a próxima.

Prêmio: Disciplina e diversão com XP

Posted on April 05, 2007

Meu prêmio do RailsRally2007 chegou hoje: “Extreme Programming: Aprenda como encantar seus usuários desenvolvendo software com agilidade e alta qualidade”, de Vinícius Teles. Desde o início da empresa que nosso processo ‘segue rumo à XP’, onde fomos aderindo às suas práticas incrementalmente. Para escrever as histórias/tarefas já utilizamos cartões, passamos para Wiki e atualmente usando o activecollab, já de olho no redmine. De práticas programamos em par(de vez em quando em trio, quarteto), todos na mesma sala (que possui mural e quadro branco), desenvolvendo orientado a testes (no ‘estilo’ BDD), etc e tal (ufa!). :)

E esse livro só vem a agregar.

Obrigado RubyOnBr! Obrigado Vinícius!

pdf.write "Hello World"

Posted on April 03, 2007
gem install pdf-writer -y
rails recibo
cd recibo
ruby script/plugin install svn://rubyforge.org//var/svn/railspdfplugin/railspdf/
script/generate controller recibos
echo "pdf.text 'Hello, World', :font_size => 72, :justification => :center " > app/views/recibos/print.rpdf
ruby script/server

Pronto, agora é só jogar em um .sh, rodar e abrir o navegador :D .

FkSearch: meu 1° plugin rails

Posted on March 30, 2007

Utilizando o auto_completer do script.aculo.us , criei um helper para ser usado para indicar uma associação “belongs_to”.

Escrevendo:
Category <%= fk_field :post, 'category_id', :search_by => 'name' %>

Resultaria em:

screenshot 1 de fk_search

O fk_field gera, além do text_field(para a busca), um hidden_field para armazenar o id, que no exemplo seria post[category_id]. Também foi implementado o preenchimento do valor inicial, tanto do text_field quanto do hidden_field, para o caso de telas de edição.

Para instalar: ruby script/plugin install http://svn.roberto.techmobil.com.br/plugins/fk_search

O código foi extraído de um projeto da minha empresa e ainda não foi finalizado. No arquivo TODO tem algumas features que pretendo implementar e também estou aceitando sugestões. As modificações podem ser acompanhadas via rss.

Tendo maiores novidades, escrevo outro post. :)

svn feed com Subtlety

Posted on March 26, 2007

Que tal acompanhar versões daquele plugin ou projeto que você adora?

Adicione o endereço do repositório (público) no Subtlety e ganhe um RSS feed das mudanças do mesmo.

Use e abuse.

Último lugar no RailsRally

Posted on March 23, 2007
O pneu não furou, o carburador não entupiu, o radiador não perdeu água, a suspensão aguentou até o final, mas acabamos cruzando a linha de chegada em último lugar, ou melhor, segundo, tanto faz. (Giovanni, co-piloto da equipe)

Parabéns a Nando Vieira e um abraço para toda turma do RubyOnBr.

Em posts futuros escreverei sobre algumas ferramentas e técnicas usadas no XodóWEB e sobre meu prêmio (valeu Vinícius).

Sobre a competição.

Resultado.

Definitivamente não implemento mais nada “com paquera”. :D

UPDATE: Post de Nando Vieira, vencedor do Rally.

Aptana.merge! RadRails

Posted on March 17, 2007

Mal acordo, vou dar uma espiada no fórum do RubyOnBr e Carlos (o cara e-genial) manda uma bomba (notícias do tipo deveriam ser dadas apenas a partir do meio-dia nos finais de semana). Pelo menos era bomba de chocolate.

Anúncio no site do Aptana [com comentários]:

Dear RadRails Users[está falando comigo],

There was a recent outage with the DNS servers that the radrails.org site relied on[tentei entrar a semana toda…]. Unfortunately, this coincided with our announcement of the Aptana IDE / RadRails merger[aqui a bomba]. I want to assure you that the two events are completely unrelated and merely coincidental. We have created a link to the mirrored radrails.org site here: http://www.aptana.com/radrails. Aptana is excited to embrace the Ruby on Rails developer and we’ll work hard to create the very best Rails development experience possible[aê!].

Thank you, Paul Colton, Founder Aptana, Inc.

No site do radrails dá para encontrar a outra versão visão da história :-D .

Por enquanto o download é separado, mas no site do aptana encontra-se o velho guia para integração.

Sucesso ao projeto!