Você está na página 1de 128

RUBY ON RAILS

APRENDENDO

ndice

O que Ruby on Rails. 4 A linguagem Ruby. 4 A quem se destina este livro. 5 Congurao e instalao. 6 Instalao em Windows . 7 Instalao em Mac OS . 8 Instalao em Linux . 9 Iniciando em Rails. 10 Desenvolvendo uma Aplicao completa. 10 Congurando o banco de dados. 13 MVC Models, Views e Controllers . 16 O primeiro controller. 18 A primeira View. 21 Incrementando as Views com uso de layouts. 23 Models. 29 Gerando migraes. 30 Rake . 32 Cadastro manual no banco de dados. 36 Primeira listagem de dados do banco 39 REST . 46 O que REST. 46 map.resources 48 Relacionamento entre models 51 Insero, edio e excluso de registros. 54 Helpers para formulrios (form_for). 55 Insero de dados . 59
2

Edio de dados. 63 Excluso de dados 68 Validaes. 72 Scaffolding. 79 Entendendo os arquivos gerados pelo Scaffold: O controller Categorias 82 Entendendo os arquivos gerados pelo Scaffold: As Views 85 Avanando no Rails. 90 Roteamento padro 90 URLs Amigveis. 93 Reaproveitamento de cdigo com Partials. 97 Ajax. 104 Preparando as pginas para utilizar AJAX. 105 link_to_remote. 106 Templates JS.RJS . 108 remote_form_for. 110 Desenvolvimento baseado em Testes. 114 Testes de unidade e testes funcionais. 115 Criando nosso primeiro teste de unidade. 115 Executando os testes. 118 Fixtures . 119 Testes Funcionais. 121

O que Ruby on Rails


Ruby on Rails uma tecnologia que permite desenvolver sites dinmicos orientados a banco de dados e aplicaes para a web - de forma semelhante a tantas outras linguagens de programao como PHP ou ASP. Porm, apesar de ser mais novo que estas duas linguagens, o Ruby on Rails vm crescendo de forma espantosa e tm chamado a ateno de desenvolvedores de todo o mundo, isso porque ele permite aumentar a velocidade e facilidade no desenvolvimento de projetos. Tecnicamente falando, Rails um framework criado na linguagem de programao Ruby (da o nome Ruby on Rails), Um framework como um esqueleto em cima do qual se desenvolve uma aplicao completa. Existem dezenas de frameworks disponveis e muitos deles existem a mais tempo que o Rails, ento o que faz do Rails algo to importante? A resposta simples: O Rails foi criado com o intuito de permitir o desenvolvimento gil, com alta produtividade, escrevendo poucas linhas de cdigo e tendo muito resultado como consequncia. Aplicaes que levam semanas ou meses para desenvolver em linguagens tradicionais podem ser feitas em horas ou dias com Ruby on Rails.

A LINGUAGEM RUBY
Ruby uma linguagem de script interpretada para programao orientada a objetos com uma losoa e sintaxe muito limpa que fazem com que programar seja elegante e divertido. A linguagem foi criada no incio da dcada de 90 no Japo e rapidamente ganhou popularidade no mundo inteiro por sua losoa de ter foco nas pessoas. Para se tornar um desenvolvedor Ruby on Rails pleno importante conhecer fundo a linguagem Ruby. Existem dezenas de livros, tutoriais e sites que podem lhe 4

ajudar, com grande destaque para o prprio site da linguagem Ruby (http:// www.ruby-lang.org), e o livro virtual Why's (Poignant) Guide to Ruby (http:// poignantguide.net/ruby/ - com verso em portugus em http://github.com/ carlosbrando/poignant-br/).

Dica de Ruby
Este livro voltado para o estudo do framework Ruby on Rails, mas ao longo dele voc vai encontrar caixas como esta com dicas especcas da lingagem Ruby.

A quem se destina este livro


O objetivo deste livro servir como um primeiro contato com o framework Ruby on Rails - no se trata de uma referncia completa sobre o assunto. Trata-se de um material para desenvolvedores que se interessem por Ruby on Rails que queiram aprender os conceitos fundamentais na prtica, criando uma aplicao real. Ao completar este livro, voc ir adquirir a base mnima necessria para comear a trabalhar e estudar mais fundo a linguagem Ruby e o Framework Rails.

Congurao e instalao
Este material assume que voc tenha familiaridade com programao Web em geral, e com pelo menos um banco de dados relacional. Na elaborao deste material foi utilizado o MySQL, por ele ser um banco de fcil instalao e por estar amplamente difundido na web, mas voc pode usar o que mais se adaptar ao seu ambiente, principalmente considerando que o Rails esconde a maior parte da complexidade nesssa rea e que ele suporta os principais bancos de dados existentes no mercado. Para comear, vamos assumir que voc tenha um banco de dados instalado e que esteja pronto para instalar a linguagem Ruby e o Rails.

Dica de Ruby - GEM


Nas instalaes para Windows, Mac e Linux ser utilizado o GEM. Gem o gerenciador de pacotes do Ruby, capaz de baixar os arquivos necessrios da Web e instal-los no local apropriado dentro das bibliotecas do Ruby sem que o desenvolvedor tenha que se preocupar com os detalhes do processo.

INSTALAO EM WINDOWS
No Windows existe o Ruby One-Click Installer. Esse instalador pode ser baixado de http://rubyforge.org/projects/rubyinstaller/ e basta rod-lo para ter um ambiente Ruby para Windows (apenas Ruby, sem Rails por enquanto). Vamos agora utilizar o gerenciador de pacotes GEM para instalar o prprio Rails e mais algumas outras GEMS que sero necessrias durante o desenvolvimento. Abra a linha de comando do seu Windows e digite.
gem install sqlite3-ruby gem install mysql gem install rails

Pronto, agora voc tem um ambiente completo Ruby on Rails em Windows. Alternativamente, ao invs de realizar manualmente a instalao voc pode baixar o InstantRails, uma aplicao que contm tudo o que necessrio para comear o desenvolvimento: Ruby, Rails, servidores Apache e Mongrel alm do mySQL. O InstantRails to simples que nem necessrio instal-lo basta descompact-lo na pasta desejada e rodar a aplicao. O InstantRails pode ser baixado em http://instantrails.rubyforge.org

INSTALAO EM MAC OS
No Mac OS 10.5 em diante, o ruby e o rails j vm pr-instalados por padro. O nico problema que a verso do Rails que j vm instalada no a verso mais recente. Para atualizar o seu sistema siga os seguintes procedimentos: 1-Instale o XCode Tools, presente no CD de instalao do MAC OS, na categoria Optional Installs (arquivoXcodeTools.mpkg) 2-Abra o terminal e digite:
sudo gem update --include-dependencies

3 - Selecione as opes padres de intalao e alps alguns instantes seu sistema estar atualizado.

INSTALAO EM LINUX
Em distribuies do Linux como o Ubuntu ou Debian, com facilidades para a instalao de aplicativos, uma maneira rpida usar as ferramentas da prpria distribuies para baixar os pacotes e dependncias necessrias. Assim, se voc est utilizando o Linux, eu recomendo que voc compile a sua prpria verso do Ruby. A verso 1.8.6 a estvel atualmente. Para compil-la, utilize o procedimento abaixo:
wget ftp://ftp.ruby-lang.org/pub/ruby/ruby-1.8.6.tar.gz tar xvfz ruby-1.8.6.tar.gz cd ruby-1.8.6 ./configure --prefix=/usr make make install

Para vericar a verso instalada do Ruby, use o seguinte comando:


ruby -v ruby 1.8.6 (2007-09-24) [i486-linux]

Uma vez que o Ruby esteja instalado, hora de fazer o mesmo com o Rails. O procedimento bem simples: basta executar o comando abaixo1:
gem install rails --include-dependencies

Com os passos acima nalizados, voc est pronto para comear o desenvolvimento em Rails.

Iniciando em Rails
DESENVOLVENDO UMA APLICAO COMPLETA
Para exemplicar os conceitos abordados neste material, vamos desenvolver uma aplicao completa de agendamento de eventos. Esta aplicao listar uma srie de eventos como cursos, encontros e seminrios com suas respectivas descries e datas, e permitir que se faa a gesto destas informaes com o cadastramento, edio e excluso de eventos. A primeira coisa a fazer, ento, criar o diretrio de trabalho da aplicao. Isso feito digitando o comando abaixo no console (ou no prompt de comando, no windows):
rails eventos

O comando rails gera o esqueleto completo de uma aplicao, pronta para rodar. Esse comando foi criado em seu sistema quando voc instalou as bibliotecas necessrias. Aps a digitao voc ver o resultado do processo de criao, conforme demonstrado abaixo:
create create create create create create create create create create create ...

app/controllers app/helpers app/models app/views/layouts config/environments components db doc lib lib/tasks

10

No esqueleto gerado, cada parte da aplicao tem um local especco para ser colocado. Isso deriva de uma das losoas por trs do Rails que pode ser descrita pela frase conveno ao invs de congurao. Conveno, nesse caso, signica que h um acordo entre o framework e voc, o desenvolvedor, de modo que voc no precisa de preocupar com certos detalhes que, de outra forma, teriam que se descritos em um arquivo de congurao. No vamos entrar em detalhes sobre a estrutura de diretrios agora porque ela car evidente medida que avanarmos no livro. No se preocupe: ela bem lgica e, na maior parte do tempo, automaticamente gerenciada pelo Rails. Agora que temos uma aplicao bsica, vamos rod-la e ver o resultado. Para facilitar a vida, o Rails vem com seu prprio servidor Web, utilizando as bibliotecas do prprio Ruby. Para rodar o servidor, basta usar um dos scripts utilitrios presentes em cada aplicao gerada pelo Rails (veja o diretrio script sob o esqueleto da aplicao). Entre no diretrio recm-criado e digite:
script/server start => Booting Mongrel (use 'script/server webrick' to force WEBrick) => Rails application starting on http://0.0.0.0:3000 => Call with -d to detach => Ctrl-C to shutdown server ** Starting Mongrel listening at :3000 ** Starting Rails with development environment... ** Rails loaded. ** Loading any Rails specific GemPlugins ** Signals ready. TERM => stop. USR2 => restart. INT => stop ** Rails signals registered. HUP => reload (without restart). ** Mongrel available at 0.0.0.0:3000 ** Use CTRL-C to stop.

11

Como voc poder ver, o comando inicia uma instncia do servidor Mongrel, capaz de servir aplicaes Rails sem necessidade de qualquer congurao adicional. O servidor roda localmente e recebe requisies na porta 3000. Lembre-se que em ambiente Windows, voc deve digitar ruby script/server start.

Acessando o endereo da aplicao voc tem o seguinte:

Como mostrado acima, temos uma aplicao inicial rodando que, inclusive, mostra quais so os prximos passos para continuar o desenvolvimento da mesma. 12

CONFIGURANDO O BANCO DE DADOS


Vamos agora congurar o banco de dados da aplicao. Esse primeiro passo muito importante porque o Rails usa as informaes proveniente do schema do banco de dados para gerar automaticamente arquivos e conguraes adicionais. Esse mais um exemplo de conveno ao invs de congurao. Dessa forma, garantir que o banco est funcionando corretamente, congurado para uso da aplicao, o passo inicial de qualquer desenvolvimento em Rails. O arquivo de congurao de banco de dados se chamada database.yml e est localizado no diretrio cong, junto com os demais arquivos de congurao da aplicao.

Dica de Ruby - Arquivos YAML (*.yml)


Um arquivo do tipo YAML um arquivo de dados como o XML, por exemplo, mas com uma sintaxe mais limpa e enxuta. Um documento YAML bastante til para guardar arquivos de congurao.

Abrindo o arquivo database.yml, repare que uma aplicao Rails geralmente utiliza trs bancos de dados, um para cada ambiente de desenvolvimento padro. Repare tambm que por padro o arquivo j vem previamente congurado para o banco de dados SQLite. Na nossa aplicao utilizaremos o banco MySQL, portanto nossa congurao car assim:

13

development: adapter: mysql encoding: utf8 database: eventos_development pool: 5 username: root password: host: localhost test: adapter: mysql encoding: utf8 database: eventos_test pool: 5 username: root password: host: localhost production: adapter: mysql encoding: utf8 database: eventos_production pool: 5 username: root password: host: localhost

Um banco de dados utilizado para o desenvolvimento, onde todas as mudanas so aplicadas. Esse banco tem seu correspondente em um banco de produo, onde modicaes somente so aplicadas uma vez que estejam completas. O arquivo permite congurar, inclusive, um banco remoto para onde suas modicaes nais sero redirecionadasembora geralmente seja melhor utilizar um outro mtodo de implantao, como veremos mais adiante. O terceiro banco mostrado acima um banco de testes, utilizado pelo Rails para a execuo de unit testing. Esse banco deve ser mantido necessariamente parte j que todos os dados e tabelas presentes no mesmo so excludos e recriados a cada teste completo efetuado na aplicao. 14

Voltando nossa aplicao, como um arquivo de congurao importante foi mudado, o servidor Web precisa ser reiniciado. Isso acontece porque ele somente l essas conguraes no incio de execuo. Esse um dos raros casos em que um servidor de desenvolvimento precisa ser reiniciado j que o Rails recarrega praticamente qualquer modicao feita na aplicao quando est no modo de desenvolvimento. Agora temos uma aplicao e um banco de dados congurado. Com isso j podemos iniciar o processo de desenvolvimento propriamente dito.

15

MVC Models, Views e Controllers


O Rails utiliza uma estratgia de desenvolvimento chamada de MVC (Model-ViewController). Essa estratgia separa os componentes da aplicao em 3 partes distintas: Model O Model a parte da aplicao que faz ligao como o banco de dados. Tecnicamente, o model implementado pela classe ActiveRecord. View O View a interface com o usurio um sistema de template que gera documentos HTML (entre outros) que so enviados para o usurio. As classes conhecidas como ActionPack, implementam o View e tambm o Controller. Controller Um controller uma classe responsvel por receber as requisies feitas pela aplicao e executar as aes necessrias para atender essas requisies. no controller que denimos a lgica do funcionamento da nossa aplicao. O controller quem ser efetivamente solicitado pelo navegador. Quando necessrio, o controller utiliza um Model previamente denido para acessar o banco de dados e, nalmente, encaminha um arquivo de visualizao (uma View) para o usurio.

16

Apesar de parecer complicado e trabalhoso demais primeira vista, este modo de trabalhar traz uma srie de vantagens em relao ao modelo tradicional de desenvolvimento em que uma nica pgina mistura cdigos responsveis pelo seu funcionamento, pelo acesso ao banco de dados e por exibir dados ao usurio. Algumas vantagens so: A manuteno ca mais fcil por haver uma clara distino entre as partes da aplicao possvel manter um controle centralizado de todo o site num nico (ou em poucos) arquivo, ao invs de espalhado em dezenas deles.

17

O PRIMEIRO CONTROLLER
Vamos comear criando um controller para lidar com a pgina inicial de nossa aplicao. Para gerar um controller, usamos o comando abaixo:
script/generate controller eventos exists exists create exists create create create app/controllers/ app/helpers/ app/views/eventos test/functional/ app/controllers/eventos_controller.rb test/functional/eventos_controller_test.rb app/helpers/eventos_helper.rb

Utilizaremos muito o comando generate, que gera documentos automaticamente. O nome passado para o comando eventos, que ser usado para compor todos os arquivos gerados pelo mesmo.

Se voc acessar a URL desse controller agora, voc vai receber a seguinte tela: .

18

Repare que, nesse caso, eu informei somente /controller/ como a URL, sem passar nem a parte correspondente a uma ao ou a um id. Nesse caso, o Rails assume que estamos invocando a ao index sem id. Esse um mais exemplo de conveno ao invs de congurao. Ao invs de ter sempre que especicar uma ao padro em algum lugar, o Rails convenciona que a ao padro index, poupando o desenvolvedor sem afetar a aplicao. Vamos olhar o arquivo app/controllers/eventos_controller.rb gerado por nosso comando. Como j mencionamos, o Rails segue uma estrutura xa de diretrios, poupando mais uma vez o desenvolvedor. Arquivos relacionados ao banco vo em no diretrio db, arquivos de congurao em cong e tudo relacionado a MVC vai em app. Dentro de app temos vrios outros diretrios que contm mais partes especcas da aplicao. Em alguns casos, o Rails tambm adiciona um suxo ao arquivo, evitando colises de nomes e problemas estranhos na aplicao, como o caso aqui. O arquivo do controller criado contm o seguinte:
class EventosController < ApplicationController end

A classe EventosController dene quem que ir responder a requisies padro em /home. Ela herda da classe ApplicationController, que est denida no arquivo application.rb, no mesmo diretrio. Sendo assim, qualquer mtodo criado na classe ApplicationController estar automaticamente disponvel nos controllers gerados para a aplicao.

19

Para criarmos a ao index, basta adicionarmos um mtodo classe:


class EventosController < ApplicationController def index end end

O princpio que usamos acima comum a tudo em Rails. Basicamente tudo o que fazemos em uma aplicao usando o mesmo consiste em extender alguma classe por meio da adio de mtodos customizados. Recarregando a pgina no navegador, camos com o seguinte:

Vamos que, dessa vez, o Rails identicou a ao a ser executada, mas, como a aplicao no especicou nenhum retorno, houve um erro. Isso aconteceu porque o Rails tentou aplicar automaticamente uma view para aquela ao do controller. Como a view ainda no existe, temos o erro. 20

A PRIMEIRA VIEW
Vamos criar a nossa primeira view. O rails automaticamente congurado para procurar pela view com o mesmo nome da ao. Nossa ao se chama index, portanto precisamos criar uma view com o mesmo nome index. As views so salvas numa pasta com o mesmo nome do controller, portanto no nosso caso criamos o arquivo app/views/eventos/index.html.erb As extenses colocadas em sequencia .html.erb indicam que esse um arquivo HTML com cdigo Ruby embutido (Embed RuBy - .erb). Vamos criar o seguinte arquivo, ento:
<p>Ol, mundo!</p>

Agora, recarregando a pgina, temos:

21

Sem que haja necessidade de especicar qualquer coisa, o Rails est usando o arquivo adequado. Veja que no conguramos qualquer coisa. Criamos um controller, denimos um mtodo no mesmo, e criamos um arquivo que contm o que queremos que seja retornado por aquele mtodo. A simples existncia desses arquivos representa uma cadeia de execuo sem que precisamos nos preocupar com que parte da aplicao faz isso ou aquilo. Qualquer outra ao que fosse criada dentro desse controller seguiria o mesmo padro. O nome da ao seria associado a um nome de arquivo dentro de um diretrio em app/views cujo nome seria o do prprio controller. O Rails tambm inteligente ao ponto de responder a uma ao mesmo que o mtodo no exista, desde que a view esteja presente no diretrio correto.

22

INCREMENTANDO AS VIEWS COM USO DE LAYOUTS


Para facilitar o desenvolvimento da parte visual de uma aplicao, o Rails possui um conceito denominado layouts. Na maioria das aplicaes Web, as pginas variam somente no seu contedo principal, possuindo cabealhos, rodaps e barras de navegao em comum. Obviamente, um framework cujo maior objetivo aumentar a produtividade do desenvolvedor no exigiria que o cdigo para esses elementos tivesse que ser repetido em cada view. Um layout funciona como um arquivo raiz, dentro do qual o resultado de uma view inserido automaticamente. Mais uma vez favorecendo conveno ao invs de congurao, o Rails dene um arquivo padro de layout que usado automaticamente por qualquer view a no ser que haja especicao em contrrio. O Rails tambm inteligente o bastante para somente usar um layout em views com a mesma extenso. O layout padro para a aplicao ca em um arquivo chamado application.html.erb, dentro do diretrio app/views/layouts. Se olharmos agora o cdigo gerado pela pgina que criamos no nosso primeiro exemplo de Controller e View, teremos o seguinte:

23

Como voc pode notar, no estamos gerando nenhum dos elementos HTML geralmente vistos em uma pgina comum. Vamos criar o arquivo application.html.erb no diretrio especicado, com o seguinte contedo:
<html> <head> <title>Central Eventos</title> </head> <body> <div id=principal> <%= yield %> </div> </body> </html>

Temos no arquivo acima, o primeiro exemplo de uso de cdigo Ruby dentro de uma view, delimitado pelos marcadores <% e %>. Aqueles familiarizados com PHP e ASP reconhecero o estilo de marcadores, com o uso de <%= objeto %> para retornar contedo. No caso acima, o mtodo especial yield retorna o contedo atual gerado pela ao, seja por meio de uma view ou usando render diretamente. O mtodo yield tem uma conotao especial no Ruby, servindo para invocar o bloco associado ao contexto. Inserido em um layout do Rails, o bloco dene a execuo da ao, com seu conseqente retorno de contedo.

24

A nossa pgina recarregada agora ca como mostrado abaixo:

No parece muita coisa, mas, olhando o cdigo, voc ver o seguinte:

fcil perceber que o cdigo do layout foi aplicado sobre o cdigo da view, gerando a sada nal da aplicao.

25

O que precisamos fazer agora extender o nosso layout. O nosso arquivo application.html.erb poderia ser mudado para o seguinte:
<html> <head> <title>Central de Eventos</title> <%= stylesheet_link_tag "default" %> </head> <body> <div id=principal> <%= yield %> </div> </body> </html>

O mtodo stylesheet_link_tag recebe o nome de uma stylesheet como parmetro e gera um link para a mesma. O nosso cdigo gerado agora cou assim:

Note que mais uma vez o Rails assumiu um caminho padro. Nesse caso, o arquivo servido diretamente da raiz da aplicao, que o diretrio public. Voc pode criar um arquivo chamado default.css no diretrio public/stylesheets para adicion-lo aplicao. 26

Um exemplo disso seria:


body{ background-color:#763A3A; } h1{ font-family:Verdana, Arial, Helvetica, sans-serif; font-variant:small-caps; font-size:30px; margin:0px; color:#572B2B; } h2{ font-family:Arial, Helvetica, sans-serif; font-size:25px; margin:0px; } p{ font-family:Arial, Helvetica, sans-serif; font-size:12px; color:#333333; } #principal{ width:760px; height:600px; border:solid 2px #000000; background-color:#FFFFFF; margin:20px auto 0px auto; padding:15px; }

Uma coisa a manter em mente que o layout padro da aplicao no , de forma alguma, o nico que pode ser gerado. Voc pode criar tantos layouts quanto precisar, colocando-os no mesmo diretrio, de onde estaro acessveis a toda aplicao.

27

Para usar um layout diferente em um controler voc poderia fazer algo assim:
class EventosController < ApplicationController layout "home" def index end end

O layout cujo nome home seria especicado no arquivo app/views/layouts/ home.html.erb, com a mesma funcionalidade do arquivo application.html.erb.

28

MODELS
Models correspondem camada de acesso ao banco de dados, implementada no Rails por um componente denominado ActiveRecord. O Rails automaticamente entende que todas as tabelas criadas no banco para uso em models satisfaro a duas condies: tero um nome plural em ingls e tero uma chave primria auto-incrementada chamada id. possvel mudar ambas as condies, mas inicialmente caremos com elas para no ter que modicar o que o Rails gera e usa automaticamente. Para gerar um modelo, utilizamos o seguinte comando:
script/generate model categoria exists exists exists create create create create create app/models/ test/unit/ test/fixtures/ app/models/categoria.rb test/unit/categoria_test.rb test/fixtures/categorias.yml db/migrate db/migrate/20081011091001_create_categorias.rb

O comando acima gera toda estrutura de suporte ao modelo, incluindo testes e migraes para o mesmo (abordaremos estes assuntos adiante neste livro). O arquivo responsvel pela implementao do modelo est em app/model/ categoria.rb e contm apenas o seguinte:

class Categoria < ActiveRecord::Base end

Essas duas linhas, apoiadas pelo Rails, j providenciam uma riqueza de implementao que nos permite recuperar, inserir e atualizar dados no banco, e executar uma srie de outras operaes complexas sem a necessidade de qualquer comando SQL direto. 29

GERANDO MIGRAES
O Rails tem uma soluo inovadora para criar as tabelas do seu banco de dados.: Ele criou o conceito de migrations (migraes). Migrations so scripts em Ruby que descrevem operaes e modicaes de qualquer natureza no banco de dados. Migraes so a maneira mais fcil de acrescentar tabelas no banco.

No caso acima, estamos gerando uma migrao, cujo nome create_categorias.

class CreateCategorias < ActiveRecord::Migration def self.up create_table :categorias do |t| t.timestamps end end def self.down drop_table :categorias end end

O mtodo self.up utilizado para efetuar as modicaes. O seu oposto, self.down, usado para desfazer essas modicaes caso voc esteja revertendo para uma verso anterior do banco.

30

Vamos editar o arquivo agora para incluir a tabela que desejamos criar:
class CreateCategorias < ActiveRecord::Migration def self.up create_table :categorias do |t| t.string :nome_categoria t.timestamps end end def self.down drop_table :categorias end end

Para efetuar suas migraes, o Rails utiliza uma linguagem de domnio que permite especicar operaes de banco de dados de forma abstrata, que no est presa a um servidor especco. possvel executar operaes diretamente via SQL no banco, mas isso no recomendado por quebrar essa abstrao. No caso acima, o mtodo create_table dentro de self.up serve para especicar a tabela que ser criada. Esse mtodo recebe um bloco como parmetro (indicado pela palavra-chave do) que especica as colunas que sero adicionadas. Voltando ao nosso cdigo, como a tabela ter somente uma coluna inicialmente, que o nome da categoria, somente precisamos de uma linha, identicando o tipo da coluna e seu nome. Mais frente veremos outros tipos e opes.

31

RAKE
Agora hora de aplicar essa migrao ao banco, subindo sua verso. Para isso, temos o utilitrio rake, que parte do Ruby. O rake capaz de realizar vrias de suas tarefas diferentes, sendo grande parte delas destinadas manipulao de bancos de dados. A primeira tarefa que utilizaremos cria o banco de dados automaticamente para voc. Basta digitar o comando rake db:create Na sequencia utilizaremos o rake db:migrate, que aplica as migraes ainda no efetuadas a um banco. Rodamos o comando da seguinte forma:
rake db:migrate == 20081011091001 CreateCategorias: migrating ================================= -- create_table(:categorias) -> 0.0021s == 20081011091001 CreateCategorias: migrated (0.0022s) ========================

Se observamos o banco agora, teremos o seguinte schema: Tables in eventos categorias schema_migrations Duas tabelas foram criadas, categorias e schema_migrations. A primeira a que especicamos. A segunda descreve informaes do banco para o Rails, para controlar o versionamento do mesmo.

32

A tabela categorias cou assim:

Field id

Type int (11)

Null NO

Key PRI

Default NULL NULL

Extra auto_increment

nome_categoria varchar (255) YES

Como voc pode ver, no preciso especicar a chave primria, que automaticamente criada pelo Rails. A tabela schema_migrations possui os seguintes dados: version 200810110910 01

Esse nmero de verso um timestamp, e cada nova migrao ter uma verso.

Vamos criar agora um novo modelo correspondendo tabela de eventos. comando :


script/generate model evento exists exists exists create create create exists create app/models/ test/unit/ test/fixtures/ app/models/evento.rb test/unit/evento_test.rb test/fixtures/eventos.yml db/migrate db/migrate/20081011092126_create_eventos.rb

33

Voc pode ver que o Rails gerou um template da migrao, e basta edit-la:

class CreateEventos < ActiveRecord::Migration def self.up create_table :eventos do |t| t.timestamps end end def self.down drop_table :projects end end

O arquivo nal seria, com base no que pensamos at o momento:


class CreateEventos < ActiveRecord::Migration def self.up create_table :eventos do |t| t.string :titulo t.datetime :data t.text :descricao t.integer :categoria_id t.timestamps end end def self.down drop_table :eventos end end

Voc no precisa se preocupar em criar todos os campos inicialmente. Voc pode sempre usar outra migrao para isso.

34

Rodando o comando rake para carregar as migraes mais uma vez, temos o seguinte:
rake db:migrate

== 20081011092126 CreateEventos: migrating ==================================== -- create_table(:eventos) -> 0.0311s == 20081011092126 CreateEventos: migrated (0.0314s) ===========================

O resultado nal do banco seria:

Tables in Eventos categorias eventos schema_migrations

35

CADASTRO MANUAL NO BANCO DE DADOS


Quando criamos uma nova aplicao Rails, normalmente usamos as prprias funcionalidades desenvolvidas na aplicao ou xtures para inserir dados de teste no banco de dados. Entretanto, como estamos aprendendo, precisaremos de cadastrar manualmente algumas informaes em nosso banco de dados. O cadastramento manual de dados pode ser feito de duas formas Utilizando um programa especco que se conecte ao banco de dados ou via cdigos Rails: Utilizando um programa especco: Existem diversos softwares que se conectam a uma base de dados e mostram uma interface grca para voc fazer manipulaes. Alguns so at mesmo gratuitos, como o CocoaMySQL para mac ou o HeidiSQL para Windows.

Cocoa MySQL para Mac

36

HeidiSQL para Windows Via cdigo Rails atravs do console: possvel tambm fazer a incluso manual destes dados em linha de comandos atravs do console do Rails. Rode o console do rails digitando:
script/console Loading development environment (Rails 2.3.1)

Na sequencia, acrescente dados no banco diretamente pelo nome do model seguindo pela instruo create. Por exemplo, para acresentar uma nova categoria nossa tabela de categorias, digitamos:
>> Categoria.create(:nome_categoria => 'Palestra') => #<Categoria id: 1, nome_categoria: "Palestra"> >>

37

Para acresentar um novo evento, digitamos:


>> Evento.create( ?> :titulo => 'Desenvolvimento agil com Rails', ?> :descricao => 'Palestra sobre desenvolvimento de aplicacoes para web.', ?> :data => '2008-03-01', ?> :categoria_id => '1') => #<Evento id: 1, titulo: "Desenvolvimento agil com Rails", data: "2008-03-01 00:00:00", descricao: "Palestra sobre desenvolvimento agil de aplicacoes p...", categoria_id: 1, created_at: "2008-02-09 11:17:07", updated_at: "2008-02-09 11:17:07"> >>

38

PRIMEIRA LISTAGEM DE DADOS DO BANCO


Agora que j sabemos congurar a aplicao e temos o banco de dados e as tabelas criadas com alguns dados cadastrados, vamos aprender a listar os dados do banco em pginas para o usurio. Para isso, vamos alterar a ao index do nosso controller Home (at o presente momento, essa ao apenas responde Ol Mundo). Agora, utilizaremos o mtodo nd para localizar as informaes no banco de dados. O mtodo nd localiza um registro, ou um conjunto de registros, baseado nos argumentos que lhe so fornecidos. No exemplo abaixo, o mtodo nd do model Evento localizar apenas o registro cujo ID 2 na tabela eventos:
class EventosController < ApplicationController def index Evento.find(2) end end

Caso voc queira listar todos os registros de uma tabela, o argumento :all pode ser fornecido. Como desejamos listar todos os registros do nosso banco, a ao listagem do nosso controller ca assim:
class EventosController < ApplicationController def index @eventos = Evento.find(:all) end end

39

Dica de Ruby - Variveis


A criao de variveis extremamente simples na liguagem Ruby, bastando utilizar o operador =, como no exemplo: valor = 10 Escopo de variveis em Ruby Outro aspecto interessante da linguagem Ruby que o escopo das variveis pode ser determinado simplesmente acrescentando um caractere especial na frente do nome. O caractere $ torna o escopo da varivel pblico, j o caractere @ cria uma varivel de instncia uma varivel que pode ser posteriormente referenciada fora do controller (na view, por exemplo).

No exemplo de cdigo acima pegamos todos os eventos e armazenamos na varivel de instncia @eventos, que utilizaremos posteriormente na view. Precisamos agora de criar a View que ser retornada para o usurio. Localize o arquivo de view que haviamos criado enteriormente na pasta app/views/eventos/ index.html.erb Neste documento, criaremos uma estrutura de loop do tipo for...in que permitir repetir a mesma poro de cdigos para cada um dos registros encontrados no banco de dados.
<h1>Listagem de Eventos</h1> <% for evento in @eventos %> <% end %>

40

Dica de Ruby - Loops


A linguagem Ruby trabalha com diversos tipos de Loops, sendo o mais comum deles o for. Em ruby o for pode ser criado referenciando um intervalo numrico simples ou uma varivel contendo multiplos dados. No primeiro caso, determinamos um intervalo numrico utilizando .., como no exemplo:

for i in 0..5 ... end No segundo caso basta colocar a varivel no lugar do intervalo for i in @variavel ... end

Dentro da rea do loop inserimos o cdigo para exibir o ttulo dos eventos. O cdigo completo ca assim:
<h1>Listagem de Eventos</h1> <% for evento in @eventos %> <h2><%= evento.titulo %></h2> <% end %>

41

Com o server iniciado e digitando o endereo localhost:3000/eventos/listagem temos o seguinte resultado nal

Nosso prximo prximo passo tornar o ttulo de cada evento um link para uma nova pgina onde possamos ver os detalhes do evento clicado. O Rails torna extremamente simples o processo de criar essa pgina de detalhes, acompanhe passo a passo: Primeiro crie uma nova ao para o controller home. Vamos chamar esta ao de show.
class EventosController < ApplicationController def index @eventos = Evento.find(:all) end def show @evento = Evento.find(params[:id]) end end

42

Assim como na ao index, utilizamos o mtodo nd do model Evento para retornar um registro do banco de dados. Neste caso especicamente utilizamos o parmetro params[:id] que faz com que o nd retorne apenas o registro com o id passado como parmetro atravs de um link. Na sequencia, edite o arquivo index.html.erb, nele vamos fazer com que o ttulo do evento seja um link utilizando o mtodo link_to. Veja o cdigo:
<h1>Listagem de Eventos</h1> <% for evento in @eventos %> <h2><%= link_to evento.titulo, :action=>'show', :id=>evento.id %></h2> <% end %>

O primeiro parmetro do link_to corresponde ao texto do link. Os outros dois parmetros, como est claro no cdigo, representam a ao para onde ser direcionado o link e o id que ser enviado junto. O mtodo link_to possui vrias outras capacidades entre as quais gerar conrmaes em JavaScript e criar forms para submisso convel de links que modicam destrutivamente seus dados, como veremos mais adiante. Por m, crie uma nova View chamada show.html.erb Esta view segue o mesmo padro de funcionamento da View que lista todos os eventos, porm no ter uma estrutura de loop (uma vez que apenas um registro ser exibido):
<h2><%= @evento.titulo %></h2> <br /> <h3>Descrio</h3> <p><%= @evento.descricao %></p> <br /><br /> <h4>Data do evento: <%= @evento.data %></h4>

43

Veja a nossa pgina de listagem de eventos com os titulos transformados em links:

Clicando em um dos links, a pgina de detalhes exibida

44

Um ltimo detalhe: para modicar a forma como a data exibida, podemos utilizar o mtodo strftime:
<h2><%= @evento.titulo %></h2> <br /> <h3>Descrio</h3> <p><%= @evento.descricao %></p> <br /><br /> <h4>Data do evento: <%= @evento.data.strftime("%d/%m/%Y") %></ h4>

Mais adiante neste livro voc aprender tambm como inserir novos registro, editar e apagar registros existentes.

45

REST
At agora zemos dois controllers diferentes que exibem duas views para os usurios: index e show. De maneira geral, acessamos os controllers e views no navegador com a seguinte regra para a URL:
localhost:3000/nome_do_controller/nome_da_ao/id

Por exemplo, para acessar o controller index, que exibe a lista de eventos, usamos o seguinte endereo:
localhost:3000/eventos

Como o controller index usado automaticamente se nenhuma outra ao for especicada, no precisamos preencher seu nome J a URL abaixo aponta para a nossa segunda ao, que mostra os dados especcos de um nico evento:
localhost:3000/eventos/show/2

Alm de especicar o nome do controller (home) e o nome da ao (show), um parmetro passado no nal da URL. No exemplo acima o parmetro 2 est sendo enviado. Existe porm uma maneira melhor, mais organizada e enxuta de estruturar nossa aplicao, seus controllers e views, conhecida como REST.

O QUE REST
O conceito principal do REST consiste em utilizar os mtodos HTTP no lugar do nome das aoes para rotear a ao correta dentro do controller. Isto , ao invs de declarar explicitamente na URL o nome da ao (como listagem ou detalhes), o prprio Rails determina a ao correta dependendo do mtodo de envio HTTP. 46

Qualquer desenvolvedor que j lidou com formulrios conhece os mtodos http como GET e POST. O primeiro envia dados para o servidor na prpria URL (algo como "?nome=valor&nome2=valor2") e o ltimo envia dados atravs do cabealho HTTP. O que talvez voc no saiba que todas as vezes que carrega uma pgina (exceto atravs do envio de um formulrio com POST) voc est fazendo uma solicitao GET. Existem outros dois mtodos pouco conhecidos: PUT e DELETE. O mtodo DELETE bastante bvio - ele instrui o servidor para excluir algo. O mtodo PUT um pouco mais complicado - funciona de modo parecido ao POST no que diz respeito a enviar dados no cabealho HTTP, mas ele foi feito para modicar algo. Como voc deve ter percebido, o mtodo GET tem muito a ver com leitura de dados, POST est relacionado criao de um novo registro, PUT o mesmo que atualizar e DELETE faz a excluso de um registro. exatamente assim que o REST funciona no Rails: dependendo do mtodo HTTP o Rails encaminha automaticamente para a ao correspondente dentro do controller: Solicitaes com o mtodo GET so encaminhadas para as aes index e show, POST encaminha automaticamente para a ao create, o mtodo PUT encaminha para a ao updatee o DELETE encaminha para destroy

47

MAP.RESOURCES
Para criar uma aplicao RESTful (nome que se d a uma aplicao que utiliza os conceitos de REST), o Rails disponibiliza um recurso de roteamento chamado routes. O sistema de roteamento no Rails o sistema que examina a URL digitada e determina a ao que deve ser executada. Quando congurado no arquivo cong/routes.rb o map.resources cria novas rotas e helpers conrgurados para trabalhar com sete aes com nomes padronizados dentro do seu controller: index, show, create, new, update, edit e destroy. Para congurar nossa aplicao de eventos, abra o arquivo cong/routes.rb. Excluindo todos os comentrios, o arquivo deve estar assim neste momento:
ActionController::Routing::Routes.draw do |map| map.connect ':controller/:action/:id' map.connect ':controller/:action/:id.:format' end

Como queremos que nosso controller eventos seja RESTfull, acrescentamos o map.resources para eventos. O arquivo car assim

ActionController::Routing::Routes.draw do |map| map.resources :eventos map.connect ':controller/:action/:id' map.connect ':controller/:action/:id.:format' end

Como falamos anteriormente, uma aplicao RESTfull em Rails usa sete nomes padronizados de aes nos controllers. Estas sete aes so responsveis por todas 48

as manipulaes mais comuns que precisam ser feitas em um banco de dados, mas claro que novas aes customizadas podem ser inseridas conforme a necessidade. Os dois primeiros nomes padronizados que ns j empregamos em nossa aplicao so index e show Se voc testar novamente a nossa apicao na URL localhost:3000/eventos, ver a listagem de eventos executada pela ao index

princpio nada mudou em nossa aplicao (exceto pelo fato de que eu cadastrei novos eventos). A diferena acontece quando voc clica em um dos links: Apenas pela passagem de um parmetro (o nmero no nal da URL) sem mencionar nenhum nome de ao, voc ver os detalhes deste registro fornecidos pela ao show.

49

Isso acontece porque quando o parmetro enviado pela URL, o mtodo HTTP em questo o GET. Numa aplicao RESTfull parmetros enviados com o mtodo GET so automaticamente mapeados para a ao show. Apesar do link continuar funcionando, em uma aplicao RESTful a criao do link entre uma pgina index e show pode ser facilitado: ao invs de congurar novamente action e id, utilizaremos um atalho que o map.resources criou: evento_path(id). Este atalho aponta automaticamente para o endereo localhost: 3000/evento/id. O novo cdigo completo da view index.html.erb ca assim:
<%= link_to evento.titulo, evento_path(evento) %>

Repare que dentro dos parnteses, ao invs de colocarmos o nmero do id, colocamos a varivel evento inteira. A funo evento_path localizar automaticamente o id e o link voltar a funcionar.

50

Relacionamento entre models


Se observarmos novamente os models que geramos para nossa aplicao, veremos que apesar deles no conterem praticamente nenhum cdigo eles possibilitam a interao com os dados do nosso banco de forma muito fcil. Se acrescentarmos algumas poucas instrues a mais em nossos models podemos expandir a sua funcionalidade de modo a tornar ainda mais prtica a manipulao dos dados do banco. Um exemplo so as instrues que estabelecem o relacionamento entre models (e, consequentemente entre tabelas) has_many e belongs_to.

has_many
Estabelece uma relao de um-para-muitos entre models. Por exemplo, para categoria que temos cadastrada, temos muitos eventos correspondentes.

belongs_to
Estabelece uma relao de muitos-para-um, de modo inverso ao has_many. No nosso caso, cada um dos eventos pertence a uma categoria. Para relacionar nossos models, abra primeiro o arquivo de modelo das categorias (models/categoria.rb) e acrescente a instruo has_many indicando para o model de eventos:
class Categoria < ActiveRecord::Base has_many :eventos end

Faa o mesmo com o model Evento (models/evento.rb), que deve pertencer a uma categoria.
class Evento < ActiveRecord::Base belongs_to :categoria end

51

Parece pouco, mas nossas tabelas esto relacionadas agora, sem necessidade de digitar nenhuma clusula SQL nem nada mais complexo.

Dica de Ruby - Smbolos


princpio o conceito de Smbolos pode parecer estranho e confuso pois trata-se de algo especco de Ruby que voc no vai encontrar na maioria das outras linguagens. Smbolos so textos iniciados com o sinal de dois pontos : - Eles esto presentes por todo o framework Rails (como nos exemplos de relacionamento entre models que zemos acima em que usamos os smbolos :eventos e :categoria) Pense em smbolos como String levinhas: Como tudo na linguagem Ruby um objeto, todas as vezes que voc cria um string um novo objeto instanciado na memria. Os smbolos representam nomes mas no so to pesadas como strings.

52

Para testar o que podemos fazer com este relacionamento que acabamos de estabelecer, abra novamente o arquivo view de listagem (views/eventos/ index.html.erb) e acresente o seguinte cdigo:
<h1>Listagem de Eventos</h1> <% for evento in @eventos %> <h2> <%= evento.categoria.nome_categoria %> <%= link_to evento.titulo, evento_path(evento) %> </h2> <% end %>

Automaticamente ele consegue resgatar o nome da categoria atralada a cada evento pelo campo categoria_id.

53

Insero, edio e excluso de registros


At agora contrumos as pginas que exibem os eventos cadastrados em nosso sitema, mas no criamos um modo para manipular tais dados. Atravs de Ruby on Rails fcils criar um sistema CRUD (Create, Read, Update, Delete) completo. Para fazer a insero dos dados, vamos comear criando uma nova ao chamada new no controller eventos. Essa ao ter uma programao muito simples que cria uma nova instncia do model Evento. Alm disso, seu propsito ser exibir o arquivo de visualizao new.html.erb que conter um formulrio de cadastro. Nosso controller, portanto, car assim:

class EventosController < ApplicationController def index @eventos = Evento.find(:all) end def show @evento = Evento.find(params[:id]) end def new @evento = Evento.new end end

Crie agora um novo arquivo chamado new.html.erb na pasta views/ eventos. Vamos acrescentar um formulrio de insero de dados neste

documento atravs do helper form_for

54

HELPERS PARA FORMULRIOS (FORM_FOR)


Um dos recursos mais facilitadores do Rails so os Helpers. Como o prprio nome j diz, os helpers ajudam na cansativa tarefa de criar elementos html como formulrios, por exemplo. O helper "form_for" permite criar um formulrio completo atrelado a uma instncia de algum modelo. No nosso exemplo, criaremos um formulrio atrelado instncia @evento do nosso model Evento. O cdigo car assim:
<h1>Cadastrar novo evento</h1> <% form_for(@evento) do |f| %> <% end %>

55

Esses comandos apenas criam um formulrio, sem nenhum campo para pessoa preencher. Atravs dos mtodos text_eld, text_area, check_box, radio_buttom, password_eld, select, datetime_select, submit e outros, podemos colocar os campos para a pessoa preencher:
<h1>Cadastrar novo Evento</h1> <% form_for(@evento) do |f| %> <h4>Titulo</h4> <%= f.text_field :titulo %> <h4>Descrio</h4> <%= f.text_area :descricao %> <h4>Categoria</h4> <%= f.select :categoria_id, Categoria.find(:all).collect { | item| [item.nome_categoria,item.id] } %> <h4>Data</h4> <%= f.date_select(:data, :order => [:day, :month, :year]) %> <br /> <%= f.submit "Cadastrar" %> <% end %>

Observe que via de regra utilizamos o mtodo do formulrio seguido de um smbolo (o nome iniciando com dois pontos), que corresponde ao nome utilizado no banco de dados. As excesses so os campos do tipo select f.select e f.date_select. No primeiro caso, alm de especicar o nome do campo, fazemos uma pesquisa de todos os registros do model Categoria e utilizamos (valor enviado para o servidor) deste menu. o collect para estabelecer quais dados sero utilizados como label (valor exibido para o usurio) e value

56

No caso do date_select, alm do nome especicamos tambm a ordem em que os menus drop-down devem aparecer. Sem esta opo, os menus seriam exibidos no formato americano, com o ano primeiro. Nosso formulrio est pronto mas no existe nenhum link que leve at ele. Vamos acrescentar tal link na pgina de listagem. At o presente momento o arquivo index.html.erb dever estar assim:
<h1>Listagem de Eventos</h1> <% for evento in @eventos %> <h2> <%= evento.categoria.nome_categoria %> <%= link_to evento.titulo, evento_path(evento) %> </h2> <% end %>

Vamos acrescentar um link na ltima linha utilizando o atalho new_post_path que foi gerado pelo routes.
<h1>Listagem de Eventos</h1> <% for evento in @eventos %> <h2> <%= evento.categoria.nome_categoria %> <%= link_to evento.titulo, evento_path(evento) %> </h2> <% end %> <br /> <p><%= link_to 'Cadastrar novo evento', new_evento_path %></p>

57

Se voc testar agora e clicar no link para cadastrar um novo evento voc ver o formulrio pronto:

58

INSERO DE DADOS
Ao clicar no boto de enviar, o formulrio vai enviar as informaes para o controller eventos atravs do mtodo POST. Como nossa aplicao est sendo construda com REST, o Rails automaticamente mapeia este envio de dados por POST para uma ao create. A ao create deve conter uma instruo Evento.new que recebe os parmetros vindos do formulrio. Na sequencia, ele verica se os dados foram registrados no banco e, em caso positivo, abre a pgina que mostra os dados do novo evento recm-criado.
class EventosController < ApplicationController
def index @eventos = Evento.find(:all) end def show @evento = Evento.find(params[:id]) end def new @evento = Evento.new end

def create @evento = Evento.new(params[:evento]) if @evento.save flash[:aviso] = 'Evento cadastrado com sucesso.' redirect_to(@evento) end end end

59

Repare ainda que adicionamos uma linha ash[:aviso] = 'Evento cadastrado com sucesso'. O ash armazena temporariamente uma informao. Por temporariamente entenda: At que a pgina seja redirecionada, depois a informao se perde. O ash usado para transmitir pequenas mensagens de status entre aes e views. Para exibir os dados do ash, abra o arquivo de layout application.html.erb e acrescente os seguintes cdigos:
<html> <head> <title>Central Eventos</title> <%= stylesheet_link_tag "default" %> </head> <body> <div id="principal">

<% unless flash[:aviso].blank? %> <div id="aviso"><%= flash[:aviso] %></div> <% end %>
<%= yield %> </div> </body> </html>

60

Dica de Ruby - Estruturas condicionais


A linguagem Ruby trabalha com diversos tipos de estruturas condicionais - blocos de cdigo que s so executados dependendo de certas condies. A estrutura if...end um exemplo disso. Na ao Create, utilizamos esta estrutura para vericar se os dados foram salvos, e somente em caso positivo a pgina redirecionada. A estrutura unless...end funciona mais ou menos como um if ao contrrio, ou seja, o bloco de cdigos sempre ser executado, a no ser que a condio estipulada seja verdadeira. No arquivo de layout, a estrutura unless faz com que o div aviso seja criado sempre, a no ser que o ash no contenha nenhuma informao.

Vamos tambm acrescentar algumas instrues CSS para congurar o visual deste div aviso. Acrescente no arquivo public/stylesheets/default.css o seguinte:
#aviso{ font-family:Arial, Helvetica, sans-serif; font-size:13px; font-weight:bold; color:#000000; border: 5px solid #990000; background-color: #ff9999; padding: 5px; margin: 10px 0; }

61

Se voc tentar inserir um novo evento voc ver a mensagem do ash na parte superior da pgina:

62

EDIO DE DADOS
Assim como a insero de novos registros, precisaremos criar duas aes no controller eventos para fazer a atualizao de dados. A primeira ao apenas mostra o formulrio, e a segunda efetivamento o processa fazendo as alteraes cabveis no banco. Vamos comear criando a ao Edit no controller. Diferentemente da ao new que instanciava um novo objeto vazio do Model Evento, precisaremos localizar o registro especco que o usurio deseja alterar. Fazemos isso da mesma forma que a ao show:
class EventosController < ApplicationController
def index @eventos = Evento.find(:all) end def show @evento = Evento.find(params[:id]) end def new @evento = Evento.new end def create @evento = Evento.new(params[:evento]) if @evento.save flash[:aviso] = 'Evento cadastrado com sucesso.' redirect_to(@evento) end end

def edit @evento = Evento.find(params[:id]) end


end

63

Crie tambm um novo arquivo de view chamado edit.html.erb. Ele conter um formulrio criado atravs do helper form_for idntico ao formulrio criado para a view new.html.erb:
<h1>Editar Evento</h1> <% form_for(@evento) do |f| %> <h4>Titulo</h4> <%= f.text_field :titulo %> <h4>Descrio</h4> <%= f.text_area :descricao %> <h4>Categoria</h4> <%= f.select :categoria_id,Categoria.find(:all).collect{ |item| [item.nome_categoria,item.id] } %> <h4>Data</h4> <%= f.date_select(:data, :order => [:day, :month, :year]) %> <br /> <%= f.submit "Editar" %> <% end %>

Se queremos que o usurio possa editar os eventos, precisamos colocar um link disposio para este propsito. Para tanto, abra o arquivo de view views/eventos/ show.html.erb e acrescente o link atravs do link_to:
<h2><%= @evento.titulo %></h2> <h3>Descrio</h3> <p><%= @evento.descricao %></p> <h4>Data do evento: <%= @evento.data.strftime("%d/%m/%Y") %></ h4>

<p><%= link_to 'Editar', edit_evento_path %></p> Teste escolhendo um dos eventos cadastrados e, na sequencia, clicando em editar. 64

O resultado ser:

Repare que no apenas os campos de texto j vieram preenchidos como todos os campos de seleo drop-down j vieram com as opes certas marcadas, isso tudo feito automaticamente pelo Rails sem que tivssemos que fazer nenhuma programao a mais.

65

O cdigo fonte do formulrio inclui o seguinte trecho:


<form action="/eventos/1" class="edit_evento" id="edit_evento_1" method="post"> <input name="_method" type="hidden" value="put" />

O formulrio est utilizando o mtodo POST, porm veja que logo na sequencia o Rails criou um campo invisvel (hidden) onde passa a informao de mtodo PUT. Ele fez isso para contornar uma limitao dos navegadores tradicionais que no suportam o mtodo HTTP PUT. Como j havamos visto na seo REST deste guia, sabemos que o mtodo PUT est relacionado edio de dados, e automaticamente o Rails encaminha dados enviados por PUT para a ao update. Vamos portanto criar esta ao:
class EventosController < ApplicationController
def index @eventos = Evento.find(:all) end def show @evento = Evento.find(params[:id]) end def new @evento = Evento.new end def create @evento = Evento.new(params[:evento]) if @evento.save flash[:aviso] = 'Evento cadastrado com sucesso.' redirect_to(@evento) end end def edit @evento = Evento.find(params[:id]) end

66

def update @evento = Evento.find(params[:id]) if @evento.update_attributes(params[:evento]) flash[:aviso] = 'Evento atualizado.' redirect_to(@evento) end end
end

A ao update primeiro localiza o evento em questo atravs do id que foi enviado pelo formulrio. Na sequencia ele faz a atualizao e, se a atualizao foi executada com sucesso, cria um ash e redireciona para a ao show para exibir o elemento recm-editado. Se voc tentar editar algum elemento, ver algo como:

67

EXCLUSO DE DADOS
Para a excluso de dados, precisaremos de uma nica ao no controller chamada destroy, alm de um link na pgina de exibio. Vamos comear pelo link, acrescentando um novo link_to no arquivo views/eventos/show.html.erb
<h2><%= @evento.titulo %></h2> <h3>Descrio</h3> <p><%= @evento.descricao %></p> <h4>Data do evento: <%= @evento.data.strftime("%d/%m/%Y") %></ h4> <p><%= link_to 'Editar', edit_evento_path %></p> <p><%= link_to 'Excluir', evento_path, :confirm => 'Confirma excluso?', :method => :delete %></p>

Assim como no link anterior, preenchemos o nosso novo link_to com dois parmetros iniciais que representam o texto e a url do link. Nesta caso, entretanto, existem outros dois parmetros na sequncia, o primeiro (:conrm) exibe uma mensagem de conrmao antes de enviar o link, j o segundo estipula que o mtodo HTTP dever ser DELETE ao invs do GET que seria padro. Como vimos no nosso estudo sobre REST, o mtodo HTTP DELETE est relacionado excluso de dados e mapeado automaticamente para a ao destroy.

68

Vamos abrir novamente o nosso controller eventos para criar nossa ao destroy que ir excuir o elemento do banco:
class EventosController < ApplicationController
def index @eventos = Evento.find(:all) end def show @evento = Evento.find(params[:id]) end def new @evento = Evento.new end def create @evento = Evento.new(params[:evento]) if @evento.save flash[:aviso] = 'Evento cadastrado com sucesso.' redirect_to(@evento) end end def edit @evento = Evento.find(params[:id]) end def update @evento = Evento.find(params[:id]) if @evento.update_attributes(params[:evento]) flash[:aviso] = 'Evento atualizado.' redirect_to(@evento) end end

def destroy @evento = Evento.find(params[:id]) @evento.destroy flash[:aviso] = "Evento excludo com sucesso" redirect_to(eventos_path) end
end

69

Em primeiro lugar, a ao destroy localiza o elemento a ser exludo atravs do parmetro que foi enviado pelo link. Na sequencia ele efetivamente exclui o registro atravs do mtodo @evento.destroy e, nalmente, cria uma nova mensagem temporria e redireciona para a listagem de eventos. Ao tentar excluir algum evento voc vai se deparar com uma mensagem de alerta como essa:

70

Aps conrmar, o usurio redirecionado para a pgina de listagem com a mensagem temporria de evento excludo com sucesso:

71

VALIDAES
Validaes so um modo de garantir a integridade dos dados em uma aplicao. O modelo de validaes que o Rails fornece bastante completo e pode ser facilmente expandido pelo desenvolvedor caso ele necessite. No caso da nossa classe de dados inicial, precisamos validar pelo menos o fato do usurio ter informado o ttulo do evento ao cadastr-lo. Para isso, vamos abrir o arquivo do model, que est em model/evento.rb, e edit-lo, inserindo uma validao simples:
class Evento < ActiveRecord::Base belongs_to :categoria

validates_presence_of :titulo
end

Se voc tentar inserir agora um novo evento sem ttulo, ver o seguinte:

72

Para entender porque isso aconteceu, vamos olhar novamente a ao create do nosso controller eventos:
def create @evento = Evento.new(params[:evento]) if @evento.save flash[:aviso] = 'Evento cadastrado com sucesso.' redirect_to(@evento) end end

Perceba que ao no passar no critrio de validao devido falta de um ttulo, o @evento.save no executado. Como consequencia, a pgina no redirecionada para exibir o evento.

73

Vamos resolver este problema acrescentando um else nesta estrutura:


def create @evento = Evento.new(params[:evento]) if @evento.save flash[:aviso] = 'Evento cadastrado com sucesso.' redirect_to(@evento)

else flash[:aviso] = 'O formulrio contm erros. Verifique os campos digitados e envie novamente.' render :action => "new"
end end

Este else ser executado caso o novo evento no possa ser salvo. Ele determina que neste caso seja criada uma informao temporria do tipo ash e redireciona de volta para a ao new (nosso formulrio de cadastro).

74

Ao tentar cadastrar novamente um evento sem ttulo a pgina retornada ca assim:

75

Alm da mensagem temporria, no parece que nada mudou. Mas houve alteraes que voc pode observar neste trecho cdigo-fonte gerado:
<form action="/eventos" class="new_evento" id="new_evento" method="post"> <h4>Titulo</h4>

<div class="fieldWithErrors">
<input id="evento_titulo" name="evento[titulo]" size="30" type="text" value="" />

</div>
<h4>Descrio</h4> <textarea cols="40" id="evento_descricao" name="evento[descricao]" rows="20"> Tentando cadastrar um evento sem ttulo </textarea>

Repare que o Rails colocou automaticamente um novo div com a class eldWithErrors ao redor do campo que falhou na validao. Para darmos um retorno melhor para o usurio, basta acrescentar esta classe no nosso arquivo de estilo public/stylesheets/default.css, conforme o exemplo:
.fieldWithErrors{ background-color:#ff0000; padding:3px; display:table; }

76

Agora, alm da mensagem temporria de erro, o usurio sabe claramente qual campo falhou na validao:

Se voc quiser, poderia ainda incluir um comando <%= error_messages_for :evento %> no incio da view new.html.erb para que a lista de erros no formulrio seja detalhada por escrito na pgina. Para este nosso exemplo no precisaremos detalhar os erros de preenchimento por texto, a mensagem temporria de erro e o feedback na borda colorida j so sucientes para alertar o usurio.

77

Mltiplas validaes podem ser efetuadas em um mesmo campo. Por exemplo, para prevenir a insero de ttulos duplicados, a seguinte condio poderia ser colocada:
class Evento < ActiveRecord::Base belongs_to :categoria validates_presence_of :titulo validates_uniqueness_of :titulo end

78

Scaffolding
At agora, criamos todas as principais operaes - Conseguimos listar, criar, editar e excluir informaes de eventos. Apesar de ser interessante fazer tuda essa programao pela primeira vez, este processo se repete para cada novo model de cada nova aplicao que voc criar em Rails. Recriar as operaes bsicas de novo e de novo pode se tornar muito tedioso, e exatamente por este motivo que existe o recurso de scaffolding. O scaffolding cria um esqueleto com toda uma estrutura bsica para operaes CRUD (Create, Retrieve, Update and Delete), e que voc pode posteriormente incrementar e modicar conforme a sua necessidade. Ou seja, tudas as aes e views que programamos manualmente para os eventos sero criadas automaticamente. A sintaxe para criao de um Scaffold :

script/generate scaffold nome_do_modelo_de_dados campos_da_tabela

Na nova verso do Rails, o prprio Scaffold cria o model, o arquivo de migrao, o controller e as views automaticamente. Para criar o scaffold para as categorias, utilizaramos o comando:
script/generate scaffold categoria nome_categoria:string

79

Entretanto, como ns j havamos criado o modelo e o arquivo de migrao das categorias anteriormente, para ns de aprendizado, precisamos instruir o Rails para criar apenas o controller e as views do scaffold - sem recriar o arquivo de migrations. Isso pode ser feito acrescentando a opo --skip-migration:

script/generate scaffold categoria nome_categoria:string -skip-migration exists app/models/ exists app/controllers/ exists app/helpers/ create app/views/categorias exists app/views/layouts/ exists test/functional/ exists test/unit/ exists public/stylesheets/ create app/views/categorias/index.html.erb create app/views/categorias/show.html.erb create app/views/categorias/new.html.erb create app/views/categorias/edit.html.erb create app/views/layouts/categorias.html.erb create public/stylesheets/scaffold.css create app/controllers/categorias_controller.rb create test/functional/categorias_controller_test.rb create app/helpers/categorias_helper.rb route map.resources : categorias dependency model exists app/models/ exists test/unit/ exists test/fixtures/ identical app/models/categoria.rb identical test/unit/categoria_test.rb skip test/fixtures/categorias.yml

Note que, para a gerao de um scaffold por esse comando, o nome do modelo de dados que deve ser passado como parmetro, e no o nome do controller. O Rails capaz de derivar um do outro. Alm disso, necessrio passar tambm como parmetro os campos do banco de dados separados apenas por espaos. No nosso caso, apenas o nome_categoria, do tipo string.

80

Esse comando gera a classe do modelo de dados, o controller e views para cada ao CRUD necessria. No caso acima, como o model j existia, ele no foi criado, e foi necessrio sobrescrever alguns arquivos. O scaffold gerou o seu prprio layout, com sua prpria stylesheet. Por causa disso, o layout original da aplicao foi perdido. Uma soluo aqui ser remover o arquivo app/views/layouts/categorias.html.erb, j que no precisamos dele, e adicionar a stylesheet scaffold.css ao arquivo application.html.erb.

81

ENTENDENDO OS ARQUIVOS GERADOS PELO SCAFFOLD: O CONTROLLER CATEGORIAS


Como voc poder observar, o controller gerado pelo scaffold muito parecido com o controller de eventos que criamos manualmente, excesso de alguns comentrios que ele insere automaticamente para cada ao e do respond_to, que permite retornar para o usurio documentos em formatos diversos alm do html.
class CategoriasController < ApplicationController # GET /categorias # GET /categorias.xml def index @categorias = Categoria.find(:all) respond_to do |format| format.html # index.html.erb format.xml { render :xml => @categorias } end end # GET /categorias/1 # GET /categorias/1.xml def show @categoria = Categoria.find(params[:id]) respond_to do |format| format.html # show.html.erb format.xml { render :xml => @categoria } end end # GET /categorias/new # GET /categorias/new.xml def new @categoria = Categoria.new respond_to do |format| format.html # new.html.erb format.xml { render :xml => @categoria } end end

82

# GET /categorias/1/edit def edit @categoria = Categoria.find(params[:id]) End # POST /categorias # POST /categorias.xml def create @categoria = Categoria.new(params[:categoria]) respond_to do |format| if @categoria.save flash[:notice] = 'Categoria was successfully created.' format.html { redirect_to(@categoria) } format.xml { render :xml => @categoria, :status => :created, :location => @categoria } else format.html { render :action => "new" } format.xml { render :xml => @categoria.errors, :status => :unprocessable_entity } end end end # PUT /categorias/1 # PUT /categorias/1.xml def update @categoria = Categoria.find(params[:id]) respond_to do |format| if @categoria.update_attributes(params[:categoria]) flash[:notice] = 'Categoria was successfully updated.' format.html { redirect_to(@categoria) } format.xml { head :ok } else format.html { render :action => "edit" } format.xml { render :xml => @categoria.errors, :status => :unprocessable_entity } end end end

83

# DELETE /categorias/1 # DELETE /categorias/1.xml def destroy @categoria = Categoria.find(params[:id]) @categoria.destroy respond_to do |format| format.html { redirect_to(categorias_url) } format.xml { head :ok } end end end

Como possvel observar, as aes deste controller foram criadas num modelo RESTfull. O Scaffold at gerou um roteamento para as categorias no arquivo cong/ routes.rb

84

ENTENDENDO OS ARQUIVOS GERADOS PELO SCAFFOLD: AS VIEWS


Vamos acompanhar cada uma das aes e suas respectivas views geradas automaticamente pelo scaffold.

Index

<h1>Listing categorias</h1> <table> <tr> <th>Nome cat</th> </tr> <% for categoria in @categorias %> <tr> <td><%=h categoria.nome_categoria %></td> <td><%= link_to 'Show', categoria %></td> <td><%= link_to 'Edit', edit_categoria_path(categoria) %></ td> <td><%= link_to 'Destroy', categoria, :confirm => 'Are you sure?', :method => :delete %></td> </tr> <% end %> </table> <%= link_to 'New categoria', new_categoria_path %>

85

Show

<p> <b>Nome cat:</b> <%=h @categoria.nome_categoria %> </p>

<%= link_to 'Edit', edit_categoria_path(@categoria) %> | <%= link_to 'Back', categorias_path %>

86

Edit

<h1>Editing categoria</h1> <%= error_messages_for :categoria %> <% form_for(@categoria) do |f| %> <p> <b>Nome cat</b><br /> <%= f.text_field :nome_categoria %> </p> <p> <%= f.submit "Update" %> </p> <% end %> <%= link_to 'Show', @categoria %> | <%= link_to 'Back', categorias_path %>

Lembrando que aps o envio, os dados do formulrio so processados pela ao update

87

New

<h1>Editing categoria</h1> <%= error_messages_for :categoria %> <% form_for(@categoria) do |f| %> <p> <b>Nome cat</b><br /> <%= f.text_field :nome_categoria %> </p> <p> <%= f.submit "Update" %> </p> <% end %> <%= link_to 'Show', @categoria %> | <%= link_to 'Back', categorias_path %>

Lembrando que aps o formulrio ser enviado, seus dados sero processados pela ao create

88

Claro que o scaffold faz apenas uma base, um esqueleto para sua aplicao que voc deve editar e renar de acordo com as suas necessidades. Ns poderamos por exemplo traduzir todos os textos como new e show para portugus. Com algumas alteraes assim a pgina de listagem caria assim:

89

Avanando no Rails
ROTEAMENTO PADRO
Toda a nossa parte de eventos est pronta, mas se voc acessar a URL raiz da aplicao, voc vai notar que ela no mudou, e continua apresentado a pgina de boas-vindas do rails:

90

Isso acontece porque o Rails dene um arquivo index.html que serve como padro. Se removermos esse arquivo do diretrio public, caremos com a seguinte pgina:

Esse erro indica que o Rails no consegue encontrar um roteamento que satisfaa a URL que especicamos. O que roteamento? Para identicar qual controller ser responsvel por atender uma determinada solicitao da aplicao, o Rails utiliza uma mecanismo de roteamento de requisies automtico que no necessita de conguraes complexas, mas que pode tambm ser customizado de acordo com as necessidades de cada aplicao. Voc deve se lembrar que no captulo sobre REST acrescentamos um roteamento a mais no arquivo cong/routes.rb, que dever ter um cdigo semelhante a este (excluindo-se os comentrios):

ActionController::Routing::Routes.draw do |map| map.resources :eventos map.connect ':controller/:action/:id' map.connect ':controller/:action/:id.:format' end

91

Para mapear o controller eventos como nossa pgina inicial, basta acrescentar a linha para dizer o seguinte:
map.root :controller => "eventos"

Estamos dizendo, com o cdigo acima que se a rota raiz for invocada, a requisio deve ser servida pelo controller eventos, que haviamos criado anteriormente. Agora, recarregando nossa pgina inicial temos:

Note que agora o mesmo controller serve dois roteamentos. Essa apenas uma das possibilidades mais simples que esto disponveis com o roteamento do Rails.

92

URLS AMIGVEIS
Para navegar entre as diversas views, muitas vezes precisamos passar o ID do evento atravs da URL. Se clicarmos em um dos eventos para ver seus detalhes a URL gerada ca assim:

Apesar de funcional, a URL no diz nada respeito do contedo. Existe uma forma de construir URLs mais amigveis, que so muito teis no apenas porque so mais fceis de serem lidas e entendidas pelos usurios como tambm porque fazem com que um sistema de busca como o Google entenda melhor do que se trata seu site e suas pginas.

93

O Rails j tem por padro suporte para URLs do tipo /controller/action/ 1-meu_item. Isto possvel de ser feito implementando o recurso to_param em nossos modelos. Abra o arquivo models/evento.rb e acrescento o cdigo:
class Evento < ActiveRecord::Base belongs_to :categoria validates_presence_of :titulo

def to_param "#{id}-#{titulo}" end


end

Estas novas URLs vo funcionar automaticamente, desde que o incio da URL seja o ID. Isso acontece porque o Ruby vai converter uma URL do tipo '123-ola-mundo' automaticamente em 123. Se voc tentar clicar novamente em um dos links da pgina de listagem ver os detalhes do evento com uma URL mais amigvel:

94

Dica de Ruby - Concatenando strings e variveis


No mtodo to_param criamos um string concatenado com variveis. Na linguagem Ruby, possvel concatenar um string com o valor de uma varivel atravs do uso de substituies - bastando usar a construo #{}. Assim, no string abaixo: fruta = "morangos" "Adoro laranjas e #{fruta}" O string nal ser Adoro laranjas e morangos J no caso do mtodo to_param, o string nal gerado ser algo como 2-Ruby on Rails para iniciantes

Apesar do Firefox 3 ter exibido corretamento a URL (incluindo at mesmo os espaos) outros navegadores substituiro os espaos, pontuao e acentos por caracteres estranhos (espaos, por exemplo, so exibidos como "%20"). Para retirar estes caracteres estranhos da URL, usamos o mtodo parameterize no ttulo. Esse mtodo retira do texto tudo que no for letras ou nmeros e substitui por hfens, alm de substituir caracteres acentuados pelos caracteres correspondentes sem acentos ( para e, para c etc..):
class Evento < ActiveRecord::Base belongs_to :categoria validates_presence_of :titulo

def to_param "#{id}-#{titulo.parameterize}" end


end

95

Se testarmos mais uma vez, clicando em um dos links da pgina principal, teremos agora:

Claro que voc pode fazer URLs ainda mais amigveis suprimindo completamente o ID e fazendo com que o Rails localize o registro no banco de dados atravs de outro campo. Existe um plugin que facilita muito o processo de criao de URLs amigveis deste tipo chamado Permalink Fu (http://agilewebdevelopment.com/ plugins/permalink_fu).

96

REAPROVEITAMENTO DE CDIGO COM PARTIALS


No comeo deste guia falamos que um dos princpios do Rails evitar a repetio desnecessria de cdigos. Um dos recursos que auxiliam neste objetivo o partial, que permite o reaproveitamento de fragmentos de pgina em diferentes views. Um caso onde um partial poderia ser utilizado so os formulrios de cadastro e edio de eventos. So dois formulrios idnticos, que poderiam ser compartilhados uma nica vez atravs do uso de partials. Veja os cdigos:

views/eventos/new.html.erb
<h1>Cadastrar novo Evento</h1> <% form_for(@evento) do |f| %> <h4>Titulo</h4> <%= f.text_field :titulo %> <br /> <h4>Descrio</h4> <%= f.text_area :descricao %> <br /> <h4>Categoria</h4> <%= f.select :categoria_id, Categoria.find(:all).collect { | item| [item.nome_categoria,item.id] } %> <br /> <h4>Data</h4> <%= f.date_select(:data, :order => [:day, :month, :year]) %> <br /> <br /> <%= f.submit "Cadastrar" %> <% end %>

97

Views/eventos/edit.html.erb
<h1>Editar Evento</h1> <% form_for(@evento) do |f| %> <h4>Titulo</h4> <%= f.text_field :titulo %> <h4>Descrio</h4> <%= f.text_area :descricao %> <h4>Categoria</h4> <%= f.select :categoria_id, Categoria.find(:all).collect { |item| [item.nome_categoria,item.id] } %> <h4>Data</h4> <%= f.date_select(:data, :order => [:day, :month, :year]) %> <br /> <%= f.submit "Editar" %> <% end %>

Vamos portanto transformar todo o formulrio num partial. Crie um novo arquivo na pasta views/eventos chamado _formulario.html.erb. Por padro, partials devem ser salvos com um nome de arquivo iniciado em underscore (_). Coloque neste novo documento todo o cdigo do formulrio.

98

O arquivo dever car assim:


<% form_for(@evento) do |f| %> <h4>Titulo</h4> <%= f.text_field :titulo %> <h4>Descrio</h4> <%= f.text_area :descricao %> <h4>Categoria</h4> <%= f.select :categoria_id, Categoria.find(:all).collect { |item| [item.nome_categoria,item.id] } %> <h4>Data</h4> <%= f.date_select(:data, :order => [:day, :month, :year]) %> <br /> <%= f.submit "Enviar" %> <% end %>

Nos arquivos views/eventos/new.html.erb e views/eventos/edit.html.erb, remova todo o formulrio e substitua por uma nica instruo que indica que um partial deve ser utilizado:

views/eventos/new.html.erb
<h1>Cadastrar novo Evento</h1> <%=render :partial => 'formulario'%>

views/eventos/edit.html.erb
<h1>Editar Evento</h1> <%=render :partial => 'formulario'%>

Repare que no preciso indicar nem a pasta nem o nome completo do arquivo de partial o Rails o localiza automaticamente e o resultado nal pode ser visto na prxima pgina:

99

new

edit

100

Passando objetos para dentro dos partials


Em alguns casos, o fragmento de pgina que voc usa dentro do partial precisa de exibir informaes adicionais, muitas vezes provenientes de um Model. Voc pode passar informaes da view para um partial atravs do parmetro :locals Para entender melhor, vamos fazer um novo partial com a lista de categorias. Abra o arquivo views/categorias/index.html.erb:

101

O cdigo deste arquivo dever estar assim:


<h1>Listagem de categorias</h1> <table id='teste'> <tr> <th>Categorias</th> </tr> <% for categoria in @categorias %>

<tr> <td><%=h categoria.nome_categoria %></td> <td><%= link_to 'Editar', edit_categoria_path(categoria) %></td> <td> <%= link_to 'Excluir', categoria, :confirm => 'Confirma excluso?', :method => :delete %> </td> </tr>
<% end %> </table> <br /> <%= link_to 'Nova categoria', new_categoria_path %>

A poro de cdigo em destaque responsvel por gerar cada uma das linhas da tabela listando as categorias. Ns vamos extrair todo este cdigo e colocar num novo arquivo de partial chamado views/categorias/_lista.html.erb.

102

O novo arquivo car assim: _lista.html.erb <tr> <td><%=h categoria.nome_categoria %></td> <td><%= link_to 'Editar', edit_categoria_path(categoria) %></td> <td> <%= link_to 'Excluir', categoria, :confirm => 'Confirma excluso?', :method => :delete%> </td> </tr> De volta ao arquivo de view em views/categorias/index.html.erb, carregamos a nova partial passando um parmetro adicional: Alm do nome do arquivo passaremos o objeto categoria que utilizando dentro do partial usando o recurso :locals.
<h1>Listagem de categorias</h1> <table id='teste'> <tr> <th>Categorias</th> </tr> <% for categoria in @categorias %>

<%= render :partial =>"lista", :locals =>{:categoria => categoria} %>


<% end %> </table> <br />

Partials so uma das grandes facilidades do Rails, sendo utilizados principalmente em aplicaes Ajax para gerar somente os fragmentos do cdigo necessrios para atualizaes de partes de uma pgina, conforme veremos no prximo captulo.

103

Ajax
Uma das grandes tendncias atuais no desenvolvimento de aplicaes Web o que se tornou conhecido como Web 2.0, que uma tentantiva de aproximar as aplicaes Web de aplicaes desktop. De modo prtico, isso signica criar aplicaes web mais responsivas, que possam atualizar partes do contedo da tela dinmicamente sem que uma nova pgina precise ser carregada todas as vezes que o usurio clicar em algum link ou boto. Aplicaes feitas no estilo da assim chamada Web 2.0 possuem caractersticas de integrao e usabilidade muito superiores s aplicaes que vinham sendo comumente desenvolvidas e esto se transformando em uma referncia para o que deve ou no deve ser feito na Internet. AJAX o brao tecnologico da Internet. No um produto novo, apenas uma sigla para Asynchronous JavaScript and XML, e atravs desta tecnologia que conseguimos recuperar dados no servidor sem a necessidade de recarregamento de pginas, ou seja, de forma assncrona. Um dos grandes motivos da popularizao do Rails nos ltimos tempos tem sido o suporte integrado ao desenvolvimento de aplicaes com recursos AJAX no framework, de modo fcil o suciente para beneciar mesmo o desenvolvedor iniciante.

104

PREPARANDO AS PGINAS PARA UTILIZAR AJAX


O Ruby on Rails utiliza duas bibliotecas de javascript para criao das interaes e efeitos em AJAX: Prototype e Script.aculo.us. Para fazer uso de AJAX em nossas pginas, precisamos instru-las para carregar estas bilbiotecas. Podemos fazer isso inserindo a instruo javascript_include_tag :defaults no layout padro das nossas views (views/layouts/ applications.html.erb):
<html> <head> <title>Central Eventos</title> <%= stylesheet_link_tag "scaffold" %> <%= stylesheet_link_tag "default" %>

<%= javascript_include_tag :defaults %>


</head> <body> <div id="principal"> <% unless flash[:aviso].blank? %> <div id="aviso"><%= flash[:aviso] %></div> <% end %> <%= yield %> </div> </body> </html>

105

LINK_TO_REMOTE
O link_to_remote funciona de forma semelhante ao link_to que j utilizamos em diversos lugares na aplicao de eventos. Ambos fazem uma solicitao ao servidor, sendo que a nica diferena que no link_to, o servidor responde com uma nova pgina a ser carregada, enquanto no caso do link_to_remote, o servidor envia a resposta de forma assncrona, sem que uma nova pgina precise ser carregada. A diferena pode parecer sutil, mas o resultado nal bem diferente. Vamos utilizar o link_to_remote para manipular as categorias. Abra o partial responsvel por listar as categorias na pgina de categorias (views/categorias/_lista.html.erb):
<tr> <td><%=h categoria.nome_categoria %></td> <td><%= link_to 'Editar', edit_categoria_path(categoria) %></ td> <td> <%= link_to 'Excluir', categoria, :confirm => 'Confirma excluso?', :method => :delete %> </td> </tr>

106

Vamos comear alterando o helper link_to de excluso para link_to_remote. Alm disso vamos acrescentar um parmetro ID no tag <tr>, responsvel por cada linha da tabela que lista as categorias. O parmetro ID vai permitir que cada linha tenha uma identicao nica na tabela e isso ser til na hora em que aplicarmos efeitos especiais para eliminar a linha do item que o usuario excluiu. Para identicarmos cada linha, utilizamos o helper dom_id, que gera automaticamente um texto de identicao: <tr id='<%= dom_id(categoria) %>'>
<td ><%=h categoria.nome_categoria %></td> <td><%= link_to 'Editar', edit_categoria_path(categoria) %></td> <td>

<%= link_to_remote'Excluir', :url=>categoria_path(categoria), :confirm => 'Confirma excluso?', :method => :delete %>
</td> </tr>

Se voc quiser testar, vai ver que ao clicar o link o item realmente excludo do banco de dados, mas ele permanece escrito na pgina. Para a aplicao de efeitos e interaes mais avanadas, precisamos de trabalhar com templates do tipo rjs.

107

TEMPLATES JS.RJS
Assim como templates .html.erb so pginas escritas em Ruby que geram documentos HTML, templates.js.rjs so documentos escritos em Ruby que se transformam em uma srie de instrues em javascript no navegador. No nosso caso, queremos que aps clicar no link de excluso e conrmar, o item excludo suma da lista atravs de um efeito de animao. Para fazer isso, crie um novo documento em views/categorias/destroy.js.rjs e escreva o cdigo a seguir: page.visual_effect :fade, dom_id(@categoria) Para terminar, abra o controller de categorias (controller/categorias_controller.rb), e localize a ao destroy no nal do arquivo. A ao destroy chamada quando o usurio clica no link excluir e, alm de efetivamente excluir o registro, responde de modo diferente caso a solicitao tenha sido feita via html ou xml atravs do respond_to, como voc pode ver abaixo:
# DELETE /categorias/1 # DELETE /categorias/1.xml def destroy @categoria = Categoria.find(params[:id]) @categoria.destroy respond_to do |format| format.html { redirect_to(categorias_url) } format.xml { head :ok } end end

O respond_to determina o que fazer quando a solicitao de excluso vem a partir de um documento HTML ou XML, por exemplo. Como o link_to_remote uma solicitao feita via Javascript, necessria acrescentar a possibilidade da ao destroy responder tambm a javascript (utilizando um template js.rjs com mesmo nome da ao - destroy.js.rjs).

108

Na verdade, como no faremos solicitaes a ao destroy de outras formas, manteremos apenas a resposta a javascript, e a ao destroy car assim:
# DELETE /categorias/1 # DELETE /categorias/1.xml def destroy @categoria = Categoria.find(params[:id]) @categoria.destroy respond_to do |format|

format.js
end end end

109

REMOTE_FORM_FOR
De modo anlogo ao link_to_remote, o remote_form_for permite que voc envie dados preenchidos em um formulrio para o controller adequado sem troca de pgina, de modo assncrono. Para testarmos o remote_form_for, vamos substituir o link Nova categoria por um formulrio completo na pgina de listagem de categorias. Quando enviado, o controller create vai cadastrar a nova categoria e, atravs de efeitos de javascript, vamos fazer o novo item aparecer na lista sem que a pgina seja recarregada. Comearemos criando um id para a tabela e um formulrio de cadastro na pgina de listagem (views/categorias/index.html.erb)
<h1>Listagem de categorias</h1> <table id='tabela'> <tr> <th>Categorias</th> </tr> <% for categoria in @categorias %> <%= render :partial =>"lista", :locals =>{:categoria => categoria} %> <% end %> </table> <br />

<% remote_form_for(@categoria) do |f| %> Categoria: <%= f.text_field :nome_categoria %> <%= f.submit "Criar" %> <% end %>

110

O remote_form_for, assim como o form_for que empregamos anteriormente neste guia, precisa de uma instncia do modelo de dados para funcionar. Vamos acrescentar esta nova instncia na ao index do controller controllers/ categorias_controller.rb:
class CategoriasController < ApplicationController # GET /categorias # GET /categorias.xml def index @categorias = Categoria.find(:all)

@categoria = Categoria.new
respond_to do |format| format.html # index.html.erb format.xml { render :xml => @categorias } end end . . .

Se voc testar, o formulrio j funciona, enviando os dados para a ao create e cadastrando os dados no banco. Mas a pgina no atualizada instantaneamente, voc s v o novo item se recarregar manualmente a pgina.

111

Agora vamos construir um template js.rjs para exibir o item novo com um efeito de animao, assim como zemos nos exemplos anteriores de excluso de categorias. Ainda no controller controllers/categorias_controller.rb, localise a ao create e acrescente o formato javascript no respond_to, da mesma maneira que zemos com a ao destroy nos exemplos anteriores:
def create @categoria = Categoria.new(params[:categoria]) respond_to do |format| if @categoria.save flash[:notice] = 'Categoria was successfully created.' format.html { redirect_to(@categoria) } format.xml { render :xml => @categoria, :status => :created, :location => @categoria }

format.js
else format.html { render :action => "new" } format.xml { render :xml => @categoria.errors, :status => :unprocessable_entity }

format.js
end end end

Atente para o detalhe que esta ao tem uma estrutura condicional (if...else). Voc deve acrescentar a instruo format.js em ambos.

112

Finalmente, crie um novo arquivo chamado views/categorias/create.js.rjs, onde instruir o javascript para acrescentar o novo item ao nal da tabela (cujo id tabela) e fazer uma animao. page.insert_html :bottom,'tabela',:partial => "lista", :locals =>{:categoria => @categoria} page.visual_effect :highlight, dom_id(@categoria) A primeira linha manda acrescentar um partial ao nal (:bottom) do elemento cujo ID tabela. Este partial listar o mais recente item acrescentado no banco. A segunda acrescenta um animao pintando momentaneamente o item recmacrescentado na pgina de amarelo.

113

Desenvolvimento baseado em Testes


Embora tenhamos ignorado esse assunto durante todo livro, indiscutivelmente uma das partes mais importantes de qualquer aplicao Rails so os testes. Todo o framework Rails j vem com uma estrutura pronta para a criao de testes automatizados para seu site ou aplicao, e voc perceber que no difcil comear a escrever seus testes. Porque escrever testes? Todo programador, mesmo iniciante, testa suas aplicaes enquanto as desenvolvem, porm de modo manual: acessando individualmente cada uma das sees do site e agindo como se fossem usurios - listando e cadastrando alguns registros e at tentando provocar alguns erros para ver se a aplicao est preparada. O problema que essa abordagem ca cada vez mais limitada quanto mais o site ou aplicao cresce em tamanho e recursos. O desenvolvimento baseado em testes faz parte de um conceito mais amplo chamado metodologia Extreme Programming (XP). Na metodologia XP, aps o levantamento de quais funcionalidades uma aplicao precisa ter, o desenvolvimento sempre comea com a criao dos testes automatizados. Uma vez escritos, os testes server como uma linha guia, denindo as condies segundo as quais o cdigo a ser desenvolvido deve ser criado. Em resumo: Somente aps os testes terem sido escritos que as funcionalidades da aplicao so desenvolvidas. Apesar disso, para os propsitos deste livro, os testes foram ignorados at o momento porque introduzi-los signicaria desviar a nossa ateno da demonstrao do Rails em si.

114

TESTES DE UNIDADE E TESTES FUNCIONAIS


O Rails oferece duas formas principais de testes: testes de unidade e testes funcionais. Os primeiros testam os modelos de dados, garantindo que validaes, associaes etc funcionam correstamente. Os ltimos testam se a lgica funcional da aplicao, presente em controllers e views, est correta.

CRIANDO NOSSO PRIMEIRO TESTE DE UNIDADE


Vamos comear criando alguns testes de unidade em nossa aplicao. Abra o arquivo test/unit/evento_test.rb Este arquivo dever estar assim.

require 'test_helper' class EventoTest < ActiveSupport::TestCase # Replace this with your real tests. test "the truth" do assert true end end

Repare que essa classe j contm um teste (chamado the truth) que serve apenas como exemplo mas no faz absolutamente nada - voc pode simplesmente apag-lo.

115

Todos os blocos de teste so iniciados pela instruo test seguido por um nome ou uma descrio do que o teste faz. Algumas pessoas preferem nomear seus testes explicitando o que a aplicao deveria fazer em determinadas circunstncias. Em ingls, podemos traduzir deveria como should, assim um bom nome para um teste que vai assegurar que possvel gravar dados no banco : should create evento (Deveria criar um evento) Vamos criar este teste:

require 'test_helper' class EventoTest < ActiveSupport::TestCase test "should create evento" do end end

O mnimo que todo e qualquer teste deve ter um (ou mais) assert. Em portugus, a palavra assert signica armao, assero - mas para compreender melhor o seu uso em testes uma traduo melhor seria Assegure que... ou Verique que... Vamos completar o cdigo do primeiro teste para entender seu funcionamento:

require 'test_helper' class EventoTest < ActiveSupport::TestCase test "should create evento" do evento = Evento.create(:titulo => "Aprendendo Rails") assert evento.valid? end end

116

Na primeira linha do teste, utilizamos um comando Rails para inserir um novo registro na tabela de eventos, na sequencia utilizamos um assert para assegurar que o evento foi realmente salvo na tabela e vlido. Quando criamos os testes para nossas models e controllers, importante testar o lado positivo e tambm o lado negativo. Mas o que isso quer dizer? Quer dizer que alm de cadastrar um registro e certicar de que ele foi inserido no banco, devemos tambm tentar inserir um registro invlido e ver se nossa aplicao o rejeita. Um exemplo disso seria criar um teste que tenta cadastrar um novo evento sem ttulo. Voc deve se lembrar que anteriormente neste livro ns acrescentamos uma validao no model Evento que requeria que os eventos tenham ttulo. Vamos acrescentar este teste e ver se a aplicao rejeita o registro.
require 'test_helper' class EventoTest < ActiveSupport::TestCase test "should create evento" do evento = Evento.create(:titulo => "Aprendendo Rails") assert evento.valid? end test "should require titulo" do evento = Evento.create(:titulo => nil) assert !evento.valid? end end

Repare primeiro no nome que demos para o teste (should require titulo - deveria requerer o ttulo). Neste teste tentamos inserir um novo evento com o ttulo vazio (nil) e o assert verica que o registro no vlido (a exclamao serve como negao.). Mais adiante veremos que alm do assert simples, que verica se determinada condio verdadeira, existem outros tipos de assert usados em situaes especcas, como o assert_response, assert_select, assert_equal, assert_not_nil e muitos outros. 117

EXECUTANDO OS TESTES
O Ruby on Rails trabalha com um banco de dados exclusivo para testes, cujos dados so excludos e recriados a cada novo teste. Antes de executar os testes, precisamos de garantir que todos os bancos de dados da sua aplicao foram criados (e no apenas o de desenvolvimento). Para isso, utilize o comando rake db:create:all

Executando os testes de Unidade Para fazer os testes de unidade de uma aplicao em Rails, usamos o comando abaixo:
rake test:units

O resultado do comando abaixo, na aplicao que temos agora, algo assim:

in /Users/cassio/Sites/eventos) /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ ruby -Ilib:test "/Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/ rake/rake_test_loader.rb" "test/unit/categoria_test.rb" "test/ unit/evento_test.rb" Loaded suite /Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake/ rake_test_loader Started .. Finished in 0.061371 seconds. 2 tests, 2 assertions, 0 failures, 0 errors

Como podemos ver, o que essa tarefa do rake faz invocar a biblitoeca test/unit para rodar cada um dos testes j criados at o momento, que neste caso passaram. Tambm possvel rodar apenas os testes funcionais, utilizando o comando rake test:functionals. J o comando rake:test roda todos os testes. Faremos testes funcionais na sequencia. 118

FIXTURES
Para realizar os testes um pouco mais sosticados, importante que o banco de dados de teste conhenha alguns dados j cadastrados para manipularmos. Isso pode ser feito atravs do uso de xtures. Fixtures so arquivos yaml que contm dados que so carregados no banco de dados durante a execuo dos testes. Vamos abrir o xture de eventos no diretrio test/xtures/eventos.yml, que dever estar assim:

# Read about fixtures at http://ar.rubyonrails.org/classes/ Fixtures.html # one: # column: value # # two: # column: value

Vamos modicar o arquivo de xtures acrecentando alguns dados. No nal o arquivo car assim:
# Read about fixtures at //ar.rubyonrails.org/classes/ Fixtures.html palestra_rails: id: 1 titulo: Desenvolvimento gil com Rails data: 2009-01-22 descricao: Palestra sobre desenvolvimento agil de aplicacoes para a web categoria_id: 1 curso_rails: id: 2 titulo: Ruby on Rails para iniciantes data: 2009-03-15 descricao: Curso de Ruby on Rails para iniciantes categoria_id: 2

119

Os dados que acabamos de introduzir sero automaticamente inseridos em nosso banco de testes quando precisarmos dos mesmos. Perceba que cada um dos dados tem um identicador, que neste caso so palestra_rails e curso_rails. Estes identicadores podem ser utilizados nos mtodos de testes para se referir aos dados. Vamos aproveitar para inserir alguns dados tambm na xtures de categorias (test/ xtures/categorias.yml). O arquivo dever car assim:

one: id: 1 nome_categoria: Palestra two: id: 2 nome_categoria: Treinamento

Repare que nesta xture vamos manter os ndices one e two. O motivo simples: Como usamos um scaffold para gerar o controller e as views de categorias, o scaffold j criou tambm uma srie de testes funcionais para o controller categorias fazendo uso destes ndices.

120

TESTES FUNCIONAIS
Vamos agora realizar alguns testes funcionais para aferir o funcionamento do nosso controller e das views de eventos. importante lembrar que apesar de haver a distino entre testes de unidade (que testam o modelo de dados) e testes funcionais (direcionados para testes no controller), perfeitamente possvel fazer testes funcionais que realizem manipulaes no banco de dados. Abra o arquivo de testes test/functional/eventos_controller_test, que deve estar assim:

require 'test_helper' class EventosControllerTest < ActionController::TestCase # Replace this with your real tests. test "the truth" do assert true end end

Assim como no arquivo de testes de unidade, esse arquivo j contm um mtodo de teste, porm este teste no faz absolutamente nada e pode ser excludo.

121

Testando as Exibies Vamos comear inserindo um teste que testa simplesmente se a view index encaminhada para o usurio:

require 'test_helper' class EventosControllerTest < ActionController::TestCase test "should get index" do get :index assert_response :success end end

O mtodo http GET faz uma solicitao ao servidor pelo index e a nossa primeira assero feita no mais com o assert, mas com o assert_response, que verica a resposta dada pelo servidor (neste caso testamos se a resposta e :success, ou seja, a view foi aberta) Ainda complementando este teste, podemos ver se a lista de eventos foi carregada do banco - para isso utilizaremos o assings. Assigns um mtodo auxiliar (um helper) disponvel para os testes que pode ser utilizado para acessar todas as variveis de instncia denidas na ao solicitada. Com este acrscimo nosso teste ca assim:

require 'test_helper' class EventosControllerTest < ActionController::TestCase test "should get index" do get :index assert_response :success assert_not_nil assigns(:eventos) end end

122

Desta vez utilizamos o assert_not_nil para assegurar que a varivel de instncia que armazena os dados vindos do banco no est vazia. No nosso prximo teste funcional, vamos vericar a ao show do controller de eventos.

require 'test_helper' class EventosControllerTest < ActionController::TestCase test "should get index" do get :index assert_response :success assert_not_nil assigns(:eventos) end test "should show evento" do get :show, :id => eventos(:palestra_rails).id assert_response :success end end

Mais uma vez usamos o mtodo get para solicitar uma view, s que neste caso passamos um id como parmetro. Repare que buscamos dinamicamente o id da xture cuja identicao palestra_rails. Na sequencia, usamos o assert_response para assegurar que a view foi encaminhada com sucesso.

123

Testando a insero de dados Vamos agora fazer alguns testes relacionados insero de dados. Primeiro, vamos escrever um teste simples para saber se a view new encaminhada com sucesso: Logo em seguida, vamos fazer um teste que tenta cadastrar um novo evento e verica duas coisas: Se o novo dado foi registrado no banco e se o sistema redirecionou para a pgina que exibe o novo evento recm-criado. Com os novos testes, o nosso arquivo dever estar assim:

require 'test_helper' class EventosControllerTest < ActionController::TestCase test "should get index" do get :index assert_response :success assert_not_nil assigns(:eventos) end test "should show evento" do get :show, :id => eventos(:palestra_rails).id assert_response :success end test "should get new" do get :new assert_response :success end test "should create evento" do assert_difference('Evento.count') do post :create, :evento => {:titulo => "Novo Evento"} end assert_redirected_to evento_path(assigns(:evento)) end end

Neste timo exemplo empregamos dois novos tipos de asseres. Primeiro, utilizamos o assert_diference. O assert_difference cria um bloco dentro do qual realizamos manipulao no banco, e no nal ele avalia se houve diferena no 124

nmero de registros no banco de dados antes e depois desta manipulao. Depois vericamos atravs do assert_redirected_to se a view show foi encaminhada para mostrar os dados do evento criado.

125

Testando a edio de dados De modo similar aos testes de insero de dados, onde primeiro vericamos se a view enviada e depois vericamos se os dados foram gravados no banco, vamos agora testar de a view edit encaminhada para o usurio e se o dado atualizado no banco pela ao update. Nossos novos testes caro assim:
require 'test_helper' class EventosControllerTest < ActionController::TestCase test "should get index" do get :index assert_response :success assert_not_nil assigns(:eventos) end test "should show evento" do get :show, :id => eventos(:palestra_rails).id assert_response :success end test "should get new" do get :new assert_response :success end test "should create evento" do assert_difference('Evento.count') do post :create, :evento => {:titulo => "Novo Evento"} end assert_redirected_to evento_path(assigns(:evento)) end test "should get edit" do get :edit, :id => eventos(:palestra_rails).id assert_response :success end test "should update evento" do put :update, :id => eventos(:palestra_rails).id, :evento => {:titulo => "Titulo atualizado" } assert_redirected_to evento_path(assigns(:evento)) end end

126

O primeiro teste - should get edit, utiliza o mtodo get passando como parmetro o id do primeiro registro do banco de dados de teste (conforme havamos congurado no arquivo de xtures) para carregar solicitar a view edit. O assert_response verica se esta view foi encaminhada corretamente. O segundo teste - should update evento, utiliza o mtodo PUT para enviar dados atualizados do evento para o banco de dados de teste(neste caso, tentamos atualizar o ttulo do evento para Titulo atualizado). A assero assert_redirectes_to verica se a view show com os dados atualizados do evento foi encaminhada. Testando a excluso de dados Finalmente, vamos fazer um ltimo teste que tenta excluir um registro do banco e verica se o registro de fato excluido e se a view de eventos encaminhada. Nosso teste car assim:
class EventosControllerTest < ActionController::TestCase test "should get index" do get :index assert_response :success assert_not_nil assigns(:eventos) end test "should show evento" do get :show, :id => eventos(:palestra_rails).id assert_response :success end test "should get new" do get :new assert_response :success end test "should create evento" do assert_difference('Evento.count') do post :create, :evento => {:titulo => "Novo Evento"} end assert_redirected_to evento_path(assigns(:evento)) end

127

test "should get edit" do get :edit, :id => eventos(:palestra_rails).id assert_response :success end test "should update evento" do put :update, :id => eventos(:palestra_rails).id, :evento => {:titulo => "Titulo atualizado" } assert_redirected_to evento_path(assigns(:evento)) end test "should destroy evento" do assert_difference('Evento.count', -1) do delete :destroy, :id => eventos(:palestra_rails).id end assert_redirected_to eventos_path end end

Como voc pode notar, este teste faz a solicitao para o servidor utilizando o mtodo http DELETE, enviando como parmetro o id do primeiro registro do banco de dados de teste. Utilizando um bloco assert_diference, primeiro vericamos se aps a manipulao o banco de dados ca com um registro a menos que antes. Na sequencia, vericamos se a view de eventos encaminhada corretamente atravs do assert_redirects_to.

128

Você também pode gostar