Anda di halaman 1dari 69

Dextra Sistemas

Desenvolvimento gil com Rails


Bill Coutinho e Andr Pasti
Junho/2007

Agenda
Ruby Rails Enterprise on Rails Dextra on Rails Mais informao Desenvolvimento gil com Rails

www.dextra.com.br

Para quem no me conhece...


Bill Coutinho
Arquiteto Chefe da Dextra Sistemas 17 anos de experincia
Desenvolvimento
De device drivers Unix em 1990 a aplicaes Java EE e Rails em 2007, uma longa estrada...

Infra-estrutura de TI

Computao Unicamp, 1989 MBA Marketing ESPM, 2003

www.dextra.com.br

Sobre a Dextra
Dextra Sistemas
Empresa de TI com sede em Campinas Fundada em 1995 Atua intensamente com tecnologias de Software Livre

reas de atuao
Desenvolvimento de Sistemas Consultoria em
Arquitetura e Integrao de Sistemas Software Livre

Treinamento profissional em tecnologias de Software Livre


www.dextra.com.br

Agenda
Ruby Rails Enterprise on Rails Dextra on Rails Mais informao Desenvolvimento gil com Rails

www.dextra.com.br

O que Ruby
Linguagem de programao
Dinmica e interpretada (como Perl e Python) Orientada a Objetos (como SmallTalk, Java e C#)
Mas, em Ruby, tudo objeto (1.next = 2)

Open Source

Ruby uma boa combinao de


Elegncia conceitual de Smalltalk Facilidade de aprendizado e uso de Python Pragmatismo de Perl

www.dextra.com.br

O que Ruby
Fonte: OReilly Radar (http://radar.oreilly.com)

www.dextra.com.br

Por qu Ruby
Caractersticas de dinamismo da linguagem
Duck typing: usa assinaturas de mtodos, no herana de classe
Se nada como um pato e grasna como um pato, ento um pato!

Classes abertas todas as classes! Reflexo (com sintaxe natural, sem APIs desajeitadas) Closures (blocos de cdigo) Mixins (mdulos)

Resumo da pera: Cdigo mais expressivo menos linhas de cdigo, Menos linhas de cdigo menos bugs!
www.dextra.com.br

JRuby
Implementao de Ruby 100% Java
Charles Nutter e Thomas Enebo (criadores) contratados pela Sun
Total apoio da Sun ao JRuby

Roda diretamente na JVM Release 1.0


Executa aplicaes Rails sem problemas

Integrao com Java


Interface para a carga e uso de classes Java em aplicaes Ruby e vice-versa

Known issues
Incompatibilidade no suporte a threads Continuations ainda no implementado www.dextra.com.br

Agenda
Ruby Rails Enterprise on Rails Dextra on Rails Mais informao Desenvolvimento gil com Rails

www.dextra.com.br

O que Rails
Framework para desenvolvimento de aplicaes Web baseadas em banco de dados
Alta produtividade de desenvolvimento e manuteno Framework completo para o padro Model-View-Controller Biblioteca de mapeamento Objeto-Relacional Aplicaes totalmente desenvolvidas em Ruby
Desde o Ajax na camada view, passando pelas aes no controller, at o modelo de domnio e o acesso ao banco de dados.

Open Source

www.dextra.com.br

Por qu Rails
Menos cdigo
Requer menos linhas de cdigo que os outros frameworks gastam apenas com arquivos de configurao XML

Convention over Configuration


O cdigo e o esquema de banco de dados so toda a configurao que voc precisa!

Dont Repeat Yourself gil


Sem fases de compilao, gerao de cdigo, deploy, re-inicializao... just code & shoot
www.dextra.com.br

Por qu Rails
Rails e o padro MVC
Model: ActiveRecord
Mantm o relacionamento entre objetos e o banco de dados responsvel por: validaes, associaes, transaes, etc.

View: ActionView
Templates HTML com scripts Ruby in-line (como JSP, ASP, PHP, etc.) Suporte a AJAX de fcil utilizao

Controller: ActionController
Aes so mapeadas por reflexo a mtodos no controller Suporte para: filtros, layouts, caching, sesses de usurio, etc. URLs so tipicamente na forma /controller/action

www.dextra.com.br

Por qu Rails
Convention over Configuration: Model
class Weblog < ActiveRecord::Base ... end class Post < ActiveRecord::Base belongs_to :weblog ... end

E no banco de dados:
CREATE TABLE weblogs (id INTEGER PRIMARY KEY, ...); CREATE TABLE posts ( id INTEGER PRIMARY KEY..., weblog_id INTEGER, ... );

www.dextra.com.br

Por qu Rails
class Weblog < ActiveRecord::Base ... end class Post < ActiveRecord::Base belongs_to :weblog ... end

CREATE TABLE weblogs (id INTEGER PRIMARY KEY, ...); CREATE TABLE posts ( id INTEGER PRIMARY KEY..., weblog_id INTEGER, ... );

www.dextra.com.br

Por qu Rails
Convention over Configuration: Controller e View
class WeblogController < ActionController::Base ... def list_entries; ... @entries = ...; end ... end

weblog/list_entries.rhtml
<h1>Entries...</h1> <table> <% for entry in @entries %> <tr><td><%= entry.feed.title+": "+entry.title %></td></tr> <tr><td><%= entry.body %></td></tr> <% end %> </table>

www.dextra.com.br

Por qu Rails
class WeblogController < ActionController::Base ... def list_entries; ... @entries = ...; end ... end

weblog/list_entries.rhtml
<h1>Entries...</h1> <table> <% for entry in @entries %> <tr><td><%= entry.feed.title+": "+entry.title %></td></tr> <tr><td><%= entry.body %></td></tr> <% end %> </table>

www.dextra.com.br

Por qu Rails
class WeblogController < ActionController::Base ... def list_entries; ... @entries = ...; end ... end

weblog/list_entries.rhtml
<h1>Entries...</h1> <table> <% for entry in @entries %> <tr><td><%= entry.feed.title+": "+entry.title %></td></tr> <tr><td><%= entry.body %></td></tr> <% end %> </table>

www.dextra.com.br

Por qu Rails
Mapeamento de URLs
ActionController::Routing::Routes.draw do |map| map.connect 'dextra/is/friendly', :controller => :feeds, :action => :list map.connect '', :controller => :feeds, :action => :list # Default Route map.connect ':controller/:action/:id' end

www.dextra.com.br

Por qu Rails
Mapeamento de URLs
ActionController::Routing::Routes.draw do |map| map.connect 'dextra/is/friendly', :controller => :feeds, :action => :list map.connect '', :controller => :feeds, :action => :list # Default Route map.connect ':controller/:action/:id' end

http://localhost/dextra/is/friendly
Chama mtodo list do controller FeedsController

www.dextra.com.br

Por qu Rails
Mapeamento de URLs
ActionController::Routing::Routes.draw do |map| map.connect 'dextra/is/friendly', :controller => :feeds, :action => :list map.connect '', :controller => :feeds, :action => :list # Default Route map.connect ':controller/:action/:id' end

http://localhost/
Chama mtodo list do controller FeedsController

www.dextra.com.br

Por qu Rails
Mapeamento de URLs
ActionController::Routing::Routes.draw do |map| map.connect 'dextra/is/friendly', :controller => :feeds, :action => :list map.connect '', :controller => :feeds, :action => :list # Default Route map.connect ':controller/:action/:id' end

http://localhost/feeds/list
Chama mtodo list do controller FeedsController

www.dextra.com.br

Por qu Rails
Queries
def Entry.recent(count) Entry.find :all, :order =>'created_on DESC', :limit => count, :include => [:feed] end def Entry.after(date) Entry.find :all, :conditions => ['created_on >= ?', date], :order => 'created_on DESC', :include => [:feed] end def Entry.before(date) Entry.find_by_sql ["select e.* from entries e where created_on < ?", date] end

www.dextra.com.br

Por qu Rails
Queries
def Entry.recent(count) Entry.find :all, :order =>'created_on DESC', :limit => count, :include => [:feed] end def Entry.after(date) Entry.find :all, :conditions => ['created_on >= ?', date], :order => 'created_on DESC', :include => [:feed] end def Entry.before(date) Entry.find_by_sql ["select e.* from entries e where created_on < ?", date] end

www.dextra.com.br

Por qu Rails
Queries
def Entry.recent(count) Entry.find :all, :order =>'created_on DESC', :limit => count, :include => [:feed] end def Entry.after(date) Entry.find :all, :conditions => ['created_on >= ?', date], :order => 'created_on DESC', :include => [:feed] end def Entry.before(date) Entry.find_by_sql ["select e.* from entries e where created_on < ?", date] end

www.dextra.com.br

Por qu Rails
Queries
def Entry.recent(count) Entry.find :all, :order =>'created_on DESC', :limit => count, :include => [:feed] end def Entry.after(date) Entry.find :all, :conditions => ['created_on >= ?', date], :order => 'created_on DESC', :include => [:feed] end def Entry.before(date) Entry.find_by_sql ["select e.* from entries e where created_on < ?", date] end

www.dextra.com.br

Agenda
Ruby Rails Enterprise on Rails Dextra on Rails Mais informao Desenvolvimento gil com Rails

www.dextra.com.br

Enterprise on Rails
Rails se destaca em uma rea de desenvolvimento:
Aplicaes Web baseadas em banco de dados
Atualmente, qual a porcentagem do desenvolvimento de aplicaes se enquadra nesta categoria?

excelente para
Aplicaes Web de pequeno a mdio porte com metas muito agressivas de time-to-market Aplicaes Web com grandes possibilidades de alterao/evoluo Aplicaes Web 2.0 que fazem uso extensivo de AJAX e/ou REST APIs soft layer sobre sistemas transacionais corporativos

www.dextra.com.br

Enterprise on Rails
Produtividade e baixo custo de desenvolvimento
Alguns casos famosos demonstram uma diminuio de 10:1 ou mais no nmero de linhas de cdigo comparado a Java EE (incluindo XMLs)
EJB 3.0 com annotations est mudando um pouco esta relao, mas...

Apresenta vantagens parecidas a linguagens 4GL, mas sem recorrer a gerao de cdigo
Baixo custo de manuteno

Grande facilidade na re-utilizao de cdigo


Inclusive na camada Web (componentes), principalmente quando comparado a frameworks Java EE ou a PHP Classes, mdulos (mixins), extenses www.dextra.com.br

Enterprise on Rails
Servidores de produo
Apache
CGI mod_ruby Fastcgi

Mongrel

Para testes
Webrick (includo na distribuio)

No futuro
Servidores de aplicao Java EE: Glass Fish, JBoss, etc.
www.dextra.com.br

Enterprise on Rails
A pulga atrs da orelha...
Escalabilidade?
Caractersticas muito semelhantes plataforma LAMP, escalabilidade provada: Linux, Apache, banco de dados, Perl/Python/PHP

Alta-disponibilidade
Linux LVS, etc.

Transaes distribudas
Voc realmente precisa?

Que mais?

Mas, do que voc precisa?


Rails: just-good-enough
www.dextra.com.br

Enterprise on Rails
Algumas aplicaes comerciais (famosas) em Rails
Basecamp (http://www.basecamphq.com/) 43 Things (http://43things.com/) Ta-Da Lists (http://tadalists.com/) Snowdevil (http://www.snowdevil.ca/) Bellybutton (http://www.bellybutton.de/) Backpack (http://www.backpackit.com/) ...

www.dextra.com.br

Agenda
Ruby Rails Enterprise on Rails Dextra on Rails Mais informao Desenvolvimento gil com Rails

www.dextra.com.br

A luta por produtividade


Desenvolvimento Java EE
Sedimentado Aceito pelo mercado Modelo de desenvolvimento robusto Muitas ferramentas, bibliotecas, frameworks Desenvolvedores bem formados mas... muito pesado! (Ou seja: caro)

Um mundo (um pouco) melhor: Java EE sob-medida


Spring Framework, Hibernate, Tapestry, POJOs, AOP... Better, Faster, Lighter Java, Bruce Tate e Justin Gehtland
www.dextra.com.br

A luta por produtividade


Um mundo melhor ainda?
Java no a derradeira linguagem! Beyond Java, Bruce Tate (ele de novo!)

O que temos aqui?


Muito oba-oba? Muitas promessas? Uma grande oportunidade!

www.dextra.com.br

Estratgia de adoo
Verificar a curva de aprendizado
Preparar algumas pessoas chaves

Experimentar, com segurana


Projeto interno Requisitos mnimos, mas com muitas possibilidades de evoluo
Agilidade e facilidade de evoluo e manuteno so pontos chaves

Resultados satisfatrios, agora no cliente


Aplicao Web de mdio porte com metas muito agressivas de time-to-market e com grandes possibilidades de evoluo Confiana e disponibilidade do cliente
www.dextra.com.br

Resultados (at agora)


Cumpre o que promete!
2 projetos internos finalizados, desenvolvimento para clientes em andamento No sweet spot, realmente muito mais produtiva que Java EE
Medidas apontam para o dobro da produtividade

Curva de aprendizado extremamente baixa Grande agilidade de manuteno e extenso


Testes unitrios com fixtures Migrations: uma das melhores ferramentas do Rails Grande reaproveitamento de cdigo

Mas, nada substitui o talento!


Bons programadores (ainda ;)) so essenciais www.dextra.com.br

Agenda
O que e por qu Ruby? O que e por qu Rails? Enterprise on Rails Dextra on Rails Mais informao Desenvolvimento gil com Rails

www.dextra.com.br

Mais informao
Ruby
Site principal: http://www.ruby-lang.org/en/ One-Click Ruby Installer for Windows
http://rubyinstaller.rubyforge.org/

RubyForge open source project repository


http://rubyforge.org/

Whys Poignant Guide to Ruby


http://poignantguide.net/

www.dextra.com.br

Mais informao
Rails
Site principal: http://www.rubyonrails.org/ Tutorial (srie com 2 partes)
http://www.onlamp.com/pub/a/onlamp/2005/01/20/rails.html http://www.onlamp.com/pub/a/onlamp/2005/03/03/rails.html http://www.pragmaticprogrammer.com/titles/rails/ http://www.onlamp.com/pub/a/onlamp/2005/06/09/rails_ajax. html

Agile Web Development with Rails (livro) Ajax on Rails

No Brasil: Repensando a Web com Rails, Fabio Akita

www.dextra.com.br

Agenda
Ruby Rails Enterprise on Rails Dextra on Rails Mais informao Desenvolvimento gil com Rails

www.dextra.com.br

Desenvolvimento gil com Rails


Foco no cdigo desenvolvido e no em ferramentas
No em IDEs No em geradores de cdigo No em manipuladores de cdigo

Aproveitamento mximo das caractersticas dinmicas de Ruby


Reflexo, classes abertas, mixins, closures, ...

Fortemente baseado em tcnicas de OO Test Driven Development


Testes unitrios
www.dextra.com.br

Desenvolvimento gil com Rails

www.dextra.com.br

Mo na massa!
www.dextra.com.br

Desenvolvimento gil com Rails


Criao do projeto
$ rails pm create create create create create create ... $ cd pm app/controllers app/helpers app/models app/views/layouts config/environments

Cria a estrutura de diretrios para o projeto, scripts e arquivos de configurao

www.dextra.com.br

Desenvolvimento gil com Rails


Configurao do banco de dados
Arquivo config/database.yml
development: adapter: mysql database: pm_development username: root password: host: localhost

Criao do banco de dados


$ mysqladmin -u root create pm_development

www.dextra.com.br

Desenvolvimento gil com Rails


Execuo do server
$ ruby script/server ruby script/server => Booting WEBrick... => Rails application started on http://0.0.0.0:3000 => Ctrl-C to shutdown server; call with --help for options [2006-10-27 22:55:36] INFO WEBrick 1.3.1 [2006-10-27 22:55:36] INFO ruby 1.8.2 (2004-12-25) [i586-linux] [2006-10-27 22:55:36] INFO WEBrick::HTTPServer#start: pid=23941 port=3000

O servidor pode ser acessado pela URL


http://localhost:3000/

www.dextra.com.br

Desenvolvimento gil com Rails


Criao do modelo
$ ruby script/generate model Task exists app/models/ exists test/unit/ exists test/fixtures/ create app/models/task.rb create test/unit/task_test.rb create test/fixtures/tasks.yml create db/migrate create db/migrate/001_create_tasks.rb

www.dextra.com.br

Desenvolvimento gil com Rails


Criao da tabela (com a utilizao do migration)
Arquivo db/migrate/001_create_tasks.rb
class CreateItems < ActiveRecord::Migration def self.up create_table :items do |t| t.column :description, :string t.column :priority, :integer t.column :start, :datetime t.column :finish, :datetime t.column :percent_done, :integer end end def self.down drop_table :items end end

www.dextra.com.br

Desenvolvimento gil com Rails


Criao da tabela (com a utilizao do migration)
Execuo da migrao (com o rake)
$ rake db:migrate (in /home/bill/tmp/rails/pm) == CreateTasks: migrating =========================================== -- create_table(:tasks) -> 0.3672s == CreateTasks: migrated (0.3674s) ==================================

www.dextra.com.br

Desenvolvimento gil com Rails


Criao da tabela (com a utilizao do migration)
Verificando no MySQL
mysql> describe tasks; +--------------+-------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +--------------+-------------+------+-----+---------+----------------+ | id | int(11) | | PRI | NULL | auto_increment | | description | varchar(32) | | | | | | priority | int(11) | YES | | 3 | | | start | datetime | YES | | NULL | | | finish | datetime | YES | | NULL | | | percent_done | int(11) | YES | | 0 | | +--------------+-------------+------+-----+---------+----------------+ 6 rows in set (0.00 sec)

www.dextra.com.br

Desenvolvimento gil com Rails


Manipulando o modelo com o console
$ ruby script/console Loading development environment. >> t = Task.new => #<Task:0x4094af5c @attributes={"finish"=>nil, "priority"=>3, "start"=>nil, "description"=>"", "percent_done"=>0}, @new_record=true> >> t.description = "Nova tarefa" => "Nova tarefa" >> t.save => true >> t = Task.find(1) => #<Task:0x4093fd00 @attributes={"finish"=>nil, "priority"=>"3", "id"=>"1", "start"=>nil, "description"=>"Nova tarefa", "percent_done"=>"0"}>

www.dextra.com.br

Desenvolvimento gil com Rails


Manipulando o modelo com o console
No MySQL
mysql> select * from tasks; +----+-------------+----------+-------+--------+--------------+ | id | description | priority | start | finish | percent_done | +----+-------------+----------+-------+--------+--------------+ | 1 | Nova tarefa | 3 | NULL | NULL | 0 | +----+-------------+----------+-------+--------+--------------+ 1 row in set (0.00 sec)

www.dextra.com.br

Desenvolvimento gil com Rails


Trabalhando com testes
No desenvolvimento gil, testes tm um papel fundamental
Test Driven Development

Rails tem suporte sofisticado para testes unitrios Criando a tabela de testes
$ mysqladmin -u root create pm_test $ rake db:test:clone_structure

A estrutura da tabela de testes ser copiada do desenvolvimento

www.dextra.com.br

Desenvolvimento gil com Rails


Trabalhando com testes
Fixtures populam o banco para os testes Criando fixtures no arquivo test/fixtures/tasks.yml
first id: 1 description: Tarefa de numero um priority: 3 start: 2006-10-28 finish: 2006-11-28 percent_done: 10 second: id: 2 description: Tarefa de numero dois priority: 3 start: 2006-11-28 finish: 2006-12-23 percent_done: 0

www.dextra.com.br

Desenvolvimento gil com Rails


Trabalhando com testes
Criando testes no arquivo test/unit/task_test.rb
require File.dirname(__FILE__) + '/../test_helper' class TaskTest < Test::Unit::TestCase fixtures :tasks # count the fixtures def test_count_my_fixtures assert_equal 2, Task.count end end

www.dextra.com.br

Desenvolvimento gil com Rails


Trabalhando com testes
Executando os testes
$ rake test (in /home/bill/tmp/rails/pm) /usr/bin/ruby -Ilib:test "/usr/lib/ruby/gems/1.8/gems/rake0.7.1/lib/rake/rake_test_loader.rb" "test/unit/task_test.rb" Loaded suite /usr/lib/ruby/gems/1.8/gems/rake0.7.1/lib/rake/rake_test_loader Started . Finished in 0.076511 seconds. 1 tests, 1 assertions, 0 failures, 0 errors /usr/bin/ruby -Ilib:test "/usr/lib/ruby/gems/1.8/gems/rake0.7.1/lib/rake/rake_test_loader.rb" /usr/bin/ruby -Ilib:test "/usr/lib/ruby/gems/1.8/gems/rake0.7.1/lib/rake/rake_test_loader.rb"

www.dextra.com.br

Desenvolvimento gil com Rails


Criao do scaffold (CRUD)
Cdigo base (andaime) para comear o desenvolvimento
Models, views e controllers baseados no banco de dados
$ ruby script/generate scaffold Task exists app/controllers/ exists app/helpers/ create app/views/tasks exists test/functional/ dependency model exists app/models/ exists test/unit/ exists test/fixtures/ identical app/models/task.rb ...

Pode ser acessado pela URL


http://localhost:3000/tasks www.dextra.com.br

Desenvolvimento gil com Rails


Criao do scaffold (CRUD)
Outra opo de scaffold: o Ajax scaffold
$ ruby script/generate ajax_scaffold Task exists app/controllers/ exists app/helpers/ create app/views/tasks exists test/functional/ dependency model exists app/models/ exists test/unit/ exists test/fixtures/ identical app/models/task.rb ...

www.dextra.com.br

Desenvolvimento gil com Rails


Customizao da aplicao
As aes list, create, edit, destroy, etc. so mtodos definidos em app/controllers/tasks_controller.rb
So acessadas por URLs no formato
http://localhost:3000/tasks/action

Por default, cada ao renderizada por um template action.rhtml contido no diretrio models. O principal do desenvolvimento Rails envolve modificar estes arquivos com cdigo customizado
Transformar o front-end bsico para o banco de dados na aplicao propriamente dita.

www.dextra.com.br

Desenvolvimento gil com Rails


Customizao da aplicao
Controller: arquivo app/controllers/tasks_controller.rb
class TasksController < ApplicationController def index list render :action => 'list' end verify :method => :post, :only => [ :destroy, :create, :update ], :redirect_to => { :action => :list } def list @task_pages, @tasks = paginate :tasks, :per_page => 10 end def show @task = Task.find(params[:id]) end

www.dextra.com.br

Desenvolvimento gil com Rails


Customizao da aplicao
Controller: arquivo app/controllers/tasks_controller.rb
def new @task = Task.new end def create @task = Task.new(params[:task]) if @task.save flash[:notice] = 'Task was successfully created.' redirect_to :action => 'list' else render :action => 'new' end end def edit @task = Task.find(params[:id]) end

www.dextra.com.br

Desenvolvimento gil com Rails


Customizao da aplicao
Controller: arquivo app/controllers/tasks_controller.rb
def update @task = Task.find(params[:id]) if @task.update_attributes(params[:task]) flash[:notice] = 'Task was successfully updated.' redirect_to :action => 'show', :id => @task else render :action => 'edit' end end def destroy Task.find(params[:id]).destroy redirect_to :action => 'list' end end

www.dextra.com.br

Desenvolvimento gil com Rails


Customizao da aplicao
View: arquivo app/views/tasks/list.rhtml
<h1>Listing tasks</h1> <table> <tr> <% for column in Task.content_columns %> <th><%= column.human_name %></th> <% end %> </tr> <% for task in @tasks %> <tr> <% for column in Task.content_columns %> <td><%=h task.send(column.name) %></td> <% end %> <td><%= link_to 'Show', :action => 'show', :id => task %></td> <td><%= link_to 'Edit', :action => 'edit', :id => task %></td> <td><%= link_to 'Destroy', { :action => 'destroy', :id => task }, :confirm => 'Are you sure?', :post => true %></td> </tr> <% end %> </table>

www.dextra.com.br

Desenvolvimento gil com Rails


Customizao da aplicao
View: arquivo app/views/tasks/list.rhtml
<%= link_to 'Previous page', { :page => @task_pages.current.previous } if @task_pages.current.previous %> <%= link_to 'Next page', { :page => @task_pages.current.next } if @task_pages.current.next %> <br /> <%= link_to 'New task', :action => 'new' %>

www.dextra.com.br

Desenvolvimento gil com Rails


Customizao da aplicao
View: arquivo app/views/tasks/show.rhtml
<% for column in Task.content_columns %> <p> <b><%= column.human_name %>:</b> <%=h @task.send(column.name) %> </p> <% end %> <%= link_to 'Edit', :action => 'edit', :id => @task %> | <%= link_to 'Back', :action => 'list' %>

www.dextra.com.br

Desenvolvimento gil com Rails


Customizao da aplicao
Model: app/models/task.rb
class Task < ActiveRecord::Base validates_length_of :description, :within => 3..32 validates_inclusion_of :priority, :in => 1..5, :message => "must be between 1 (high) and 5 (low)" validates_inclusion_of :percent_done, :in => 0..100, :message => "must be between 0 and 100" end

www.dextra.com.br

Perguntas?

www.dextra.com.br

Thats all, folks!

www.dextra.com.br

E-mail: contato@dextra.com.br Fone: (19) 3256-6722

www.dextra.com.br

Anda mungkin juga menyukai