Você está na página 1de 81

APOSTILA RUBY ON RAILS

Marcus Vincius de S. Lemos


www.marvinlemos.net

Teresina/PI

MODELOS, VISES E CONTROLADORES O modelo responsvel por manter o estado da aplicao. Algumas vezes o estado transiente, durando apenas algumas simples interaes com o usurio. Algumas vezes o estado permanente e ser armazenado fora da aplicao, em um banco de dados, por exemplo. O modelo mais do que dados, assegura que todas as regras de negcio sero aplicadas a esses dados. Por exemplo, se um desconto no pode ser aplicado aos pedidos com valores menores que R$ 20,00, o modelo dever encarrega-se de aplicar essa restrio. Colocando essa regra de negcio no modelo, podemos garantir que nenhuma parte da aplicao possa invalidar o dado. O modelo age como um porteiro e um armazenador de dados. A viso responsvel por gerar uma interface com o usurio, normalmente baseado em dados lidos do modelo, mas nem sempre. Por exemplo, uma loja on-line possui uma lista de produtos cadastrados que necessita ser exibido na tela do usurio. A lista ser montada via modelo, mas ser a viso responsvel por formatar os dados e mostrar para o usurio. Embora essa formatao possa ser realizada de qualquer forma, a viso nunca manipula os dados, ou seja, a camada de viso no pode alterar o preo do produto nem alterar o estoque, por exemplo. O controlador (controler), como o prprio nome sugere, controla a aplicao. um intermedirio entre a viso e o modelo. Recebe dados do mundo externo(requisies do usurio), interage com o modelo e mostra uma viso apropriada para o usurio. A figura abaixo, refora a idia do funcionamento desses trs elementos que compe o MVC:

O Ruby on Rails uma framework MVC. Ela garante que essas trs unidades trabalhem juntas enquanto a aplicao est rodando. Uma das grandes vantagens do Rails que o seu funcionamento baseado no uso de padres inteligentes, evitando,

assim, a necessidade de escrever configuraes externas de metadados para que esses processos funcionem. Numa aplicao Rails, as requisies de entrada so primeiramente encaminhadas a um roteador, o qual determina para qual parte da aplicao a requisio deve ser encaminhada e como ela deve ser tratada. Esse ltimo processo realizado por um mtodo particular conhecido como action (ao). O action pode olhar os dados dentro da requisio, interagir com o modelo. A figura abaixo demonstra como o Rails manipula a requisio. O endereo da url http://www.marvinlemos.net/store/add_cart/123, onde 123 o cdigo interno do produto selecionado.

O roteador intercepta essa url e quebra-o em partes. A primeira parte do caminho, store, corresponde ao nome do controlador e a segunda parte, add_cart, o nome da ao. A ltima parte, 111, por conveno, corresponde a um parmetro interno chamado de id. Aps o resultado dessa analise, o roteador sabe que deve invocar o mtodo add do controlador StoreController , o qual representado por uma classe (Falaremos adiante sobre convenes de nomes). O mtodo add lida com as requisies do usurio. Neste caso, ele localiza o carrinho de compras atual do usurio (o qual gerenciado pelo modelo). Tambm se comunica com o modelo pedindo informaes sobre o produto 111. Ento, informa ao carrinho de comprar para adicionar o produto 111 a ele. Agora que o carrinho incluiu um novo produto, podemos mostrar ao usurio. O controlador arruma as coisas de forma que a viso possa acessar o carrinho do modelo, e invocar o cdigo de viso(html e javascripts, caso o sistema seja uma pgina de web). A est o MVC em funcionamento. Seguindo um conjunto de convenes e particionando suas funcionalidades apropriadamente, percebe-se que fica muito mais fcil manter e estender a aplicao.

A grande vantagem de uma framework, como o Rails, reside no fato de que toda a infra-estrutura criada automaticamente. Ficando a cargo do programador lidar somente com o mais importante: as regras de negcio da aplicao. ACTIVE RECORD: A CAMADA MODELO DO RAILS O mais comum em uma aplicao web manter as informaes gravadas em um banco de dados relacional. O grande problema, que as melhores linguagens de programao para web so orientadas a objeto, o que torna complicado a integrao entre esses bancos e essas linguagens. Alm disso, mesmo em linguagens estruturadas como Pascal e C, trabalhar com banco de dados tornava-se uma tarefa rdua medida que a aplicao ia crescendo. Um modo de programao muito usada, mesmo em linguagens tipicamente orientadas a objeto como PHP e JSP, misturar lgica de negcio com cdigo SQL. Se o banco de dados da aplicao mudasse, seria necessrio reescrever praticamente toda a aplicao, para dar suporte ao novo banco. Uma tcnica bastante conhecida da orientao a objetos o encapsulamento, onde podemos esconder nossas regras dentro de objetos e definir alguns mtodos nesses objetos que o mundo externo poder usar para ter acesso ao resultado de nossos cdigos. Essa idia foi adaptada programao com banco de dados. Os mtodos necessrios ao acesso e manipulao do banco ficam escondidos dentro de classes bsicas. As outras partes da aplicao usam essas classes e seus objetos, ou seja, a aplicao nunca ter acesso diretamente ao nosso banco. MAPEAMENTO OBJETO/RELACIONAL As bibliotecas de Mapeamento Objeto/Relacional fazem o mapeamento de tabelas para classes. Se o banco de dados possui uma tabela chamada Pedidos, a aplicao possuir uma classe denominada Pedido. Essa classe definir atributos, que sero usados para receber e alterar os dados dos campos das tabelas, alm de mtodos para calcular os juros, valor total do pedido e etc. Alm disso, as classes Rails que fazem essa interface com as tabelas do banco de dados, provem um conjunto de mtodos de alto-nvel que servem para realizar operaes bsicas nas tabelas, como recuperar um registro atravs de um id, dentre outros.

ACTIVE RECORD O Active Record a camada de Mapeamento Objeto Relacional fornecido pelo Rails, fazendo todo o mapeamento de Tabelas para classes, registros para objetos e colunas para atributos de objetos. O Active Record minimiza grau de configuraes que devem ser realizadas pelo programador, atravs do uso de convenes e de configuraes default. require 'active_record' class Order < ActiveRecord::Base end

order = Order.find(1) order.discount = 0.5 order.save O cdigo acima mostra um programa que usa o Active Record para acessar uma tabela. O cdigo usa a classe Order para recuperar o pedido cujo Id 1 e modificar o desconto. O Active Record torna muito simples o acesso a banco de dados, deixando o programador livre para trabalhar com a regra de negcio. Alm disso, o Active Record capaz de realizar outras tarefas, como sofisticadas validaes de dados do modelo.
VISO E CONTROLADOR

Basicamente, o Controlador fornece dados para a viso e recebe de volta eventos das pginas geradas pela viso. Por causa dessa grande integrao entre esses dois, viso e controlador esto juntos no mesmo pacote, chamado de Action Pack. Apesar desses dois componentes estarem no mesmo pacote, suas tarefas so claramente divididas. Rails permite o desenvolver escrever aplicaes web separando claramente os cdigo para controle e lgica de apresentao. VISO No Rails, responsabilidade da viso toda ou partes da pgina que ser visualizada no navegador, basicamente cdigo html que mostra algum texto fixo. Tipicamente, o desenvolvedor poder acrescentar algum contedo dinmico atravs do uso de alguma ao(action) no controlador. No Rails, contedos dinmicos so gerados via templates, que pode ocorrer de duas formas.Uma forma consiste em intercalar pedaos de cdigo Ruby dentro dos htmls da viso, atravs de uma ferramenta Ruby chamada de ERb (ou Embedded Ruby). Esta abordagem muito flexvel, mas os puristas podem reclamar que viola todo o esprito do MVC. Intercalando cdigo na viso, podemos correr o risco de adicionar lgica que deveria estar no controlador ou modelo. Rails tambm suporta vises builder-style. Estas permitem a construo de documentos XML usando cdigo Ruby a estrutura dos XML gerados automaticamente seguira a estrutura do cdigo. CONTROLADOR O controlador Rails o centro lgico da aplicao. Ele coordena a interao entre o usurio, a viso e o modelo. Contudo, Rails realiza quase toda essa operao detrs das cenas; o cdigo escrito pelo desenvolvedor ser concentrado nas funcionalidades a nvel de aplicao. Esta caracterstica torna o cdigo de controlador do Rails surpreendentemente fcil de desenvolver e manter. O controlador tambm responsvel por importantes servios auxiliares, tais como: ! sua responsabilidade realizar o roteamento requisies externas para actions internas. Ele manuseia URL extremamente bem. (It handles people-friendly URLs extremely well.)

! ! !

Realiza cache, o que poder resultar em um grande aumento de desempenho. Gerencia modules de ajudar (helper), o qual estende as capacidades dos templates da viso sem aumentar o cdigo. Gerencia sesses.

3. INSTALANDO O RAILS Antes de comear uma aplicao Rails, necessrio realizar o download do mesmo e instala-lo no computador. Para instalar o Rails, preciso antes instalar a linguagem Ruby, que pode ter seu download feito em: http://www . ruby-lang.org L encontramos os fontes da linguagem. Se voc utiliza alguma distribuio que utiliza pacotes, por favor verifique se o pacote do Ruby se encontra disponvel. Depois precisamos do RubyGems, que um gerenciador de pacotes para o Ruby. Pode ter seu download feito em: http://docs.rubygems.org/ Aps instalados o Ruby e o RubyGems, podemos instalar o pacote do Rails facilmente com o comando: gem install rails Digite rails no seu console e verifique o que acontece. Se no acontecer nada, houve algum problema. Verifique a sua instalao. RAILS E BANCO DE DADOS Se a aplicao usa banco de dados, h mais alguns passos necessrios a serem realizados antes de comear o desenvolvimento da aplicao. Rails trabalha com DB2, MySQL, Oracle, Postgres, SQL Server e SQLite. Para todos esses bancos, com exceo do Mysql, ser necessrio instalar um driver. O Rails vem com um driver built-in para MySql. Caso seja necessrio conectar sua aplicao a um banco de dados diferente do MySql, ser necessrio o download do driver especifico para o banco. Geralmente esse driver est na forma de cdigo-fonte, tornando necessria a compilao do mesmo antes de us-lo.

4. ESCREVENDO A PRIMEIRA APLICAO Vamos escrever uma aplicao web trivial para termos o primeiro contato com o Rails e verificarmos o modo como uma aplicao Rails funciona. CRIANDO UMA NOVA APLICAO Quando a framework rails instalada, uma nova ferramenta de linha de comando, rails, adicionada. Essa ferramenta usada para construir uma nova aplicao. Uma aplicao Rails, consiste basicamente de cdigo Ruby, poder-se-ia construir uma aplicao, sem a necessidade dessa nova ferramenta. No entanto, Rails, faz um pouco de mgica por detrs das cortinas, fazendo sua aplicao funcionar com um mnimo de configurao necessria. Para criar sua primeira aplicao, abra um shell (terminal) e navegue at o diretrio onde deseja-se criar a estrutura de diretrios da nova aplicao. Neste exemplo, o projeto ser criado dentro de um diretrio chamada curso. Dentro deste diretrio, use o rails para criar uma aplicao chamada demo. Muito cuidado, caso haja um sub-diretrio chamado demo, ser perguntado se voc deseja sobrescrever qualquer arquivo existente. marvin> cd teste curso> rails demo create create app/controllers create app/helpers create app/models create app/views/layouts . . . . . . . . . create log/server.log create log/production.log create log/development.log create log/test.log teste> O comando criou um diretrio chamado demo. Listando o contedo deste diretrio, ls -p no unix ou dir no windows, poderemos ver a estrutura criada. teste> cd demo demo> ls p app/ component/ config/

db/ doc/ lib/

log/ public/ script/

teste/ vendor/

Por enquanto, vamos dar uma olhada apenas do diretrio public. Dentre vrios, h trs arquivos que chamam a ateno. Os arquivos dispatch.cgi, dispatch.fcgi e o

dispatch.rb, tambm como conhecidos como despachantes (dispatchers), so responsveis por receber as requisies feitas pelos usurios atreves dos seus navegadores e redirecionar esses pedidos ao cdigo da sua aplicao. H tambm um diretrio chamado de script. Contm alguns utilitrios que usaremos enquanto desenvolvemos nossa aplicao. Por enquanto, usaremos um script chamado server. Ele inicia um servidor web chamado WEBrick, o qual poderemos usar para rodar nossas aplicaes web. Vamos iniciar nossa aplicao chamada demo. demo> ruby script/server => Booting WEBrick... => Rails application started on http://0.0.0.0:3000 => Ctrl-C to shutdown server; call with --help for options [2006-10-20 22:40:41] INFO WEBrick 1.3.1 [2006-10-20 22:40:41] INFO ruby 1.8.4 (2006-04-14) [i386-mswin32] [2006-10-20 22:40:41] INFO WEBrick::HTTPServer#start: pid=3016 port=3000 A ltima linha indica que o servidor foi iniciado com sucesso na porta 3000. Podemos acessar nossa aplicao apontando o navegador para http://localhost:3000. O resultado esta indicado na figura abaixo:

At este momento, temos uma nova aplicao rodando, mas nenhum cdigo. Vamos fazer um pouco de mgica.

OL MUNDO Como foi visto anteriormente, a arquitetura do Rails baseado no modelo MVC. Rails aceita pedidos de entrada de algum navegador, decodifica esse pedido para encontrar um controlador e chamar um mtodo daquele controlador. O controlador invoca uma viso particular para mostrar o resultado para o usurio. A boa notcia que o rails resolve tudo isso para voc. Para escrever nossa aplicao Ol Mundo, precisamos de um cdigo para um controlador e uma viso. Por enquanto, no precisamos de um cdigo para o modelo, uma vez que no estamos manipulando dados. Vamos iniciar o controlador. Para criar um novo controlador, vamos usar um utilitrio chamado generate que est dentro do diretrio script. demo> ruby script/generate controller Mundo exists app/controllers/ exists app/helpers/ create app/views/mundo exists test/functional/ create app/controllers/mundo_controller.rb create test/functional/mundo_controller_test.rb create app/helpers/mundo_helper.rb Acabamos de criar um controlador chamado de Mundo. Foram criados trs arquivos: create app/controllers/mundo_controller.rb create test/functional/mundo_controller_test.rb create app/helpers/mundo_helper.rb Vamos olhar o cdigo do nosso controlador mundo_controller.rb: class MundoController < ApplicationController end MundoController uma classe vazia que herda de ApplicationController, por isso possui o comportamento default de um controlador. Precisamos adicionar algum cdigo de modo que nosso controlador possa tratar requisies de entrada. Antes, vamos olhar o modo que o Rails trata as requisies. RAILS E AS URLS Como qualquer outra aplicao, uma aplicao Rails acessada pelo usurio atravs de sua URL. Vamos analisar o formato de uma URL de uma aplicao Rails:

http://www.marvinlemos.net/sistema/demo/mundo/ola 1 www.marvinlemos.net/sistema/demo : identifica a aplicao 2 mundo: identifica um controlador 3 ola: identifica a ao a ser invocada PRIMEIRA AO Vamos adicionar uma ao chamada de ola, que consiste basicamente de um mtodo da classe MundoController. Por enquanto, o mtodo no vai realizar nada. class MundoController < ApplicationController def ola end end Vamos tentar chamar nossa ao a partir do navegador: http://localhost:3000/mundo/ola

O erro acima era esperado. Criamos uma classe Controlador e uma ao, mas no informamos ao Rails o que ele deve exibir para o usurio. a aonde a viso entra. Quando usamos o script para criar nosso controlador, foram criados trs arquivos e um diretrio chamado app/view. Esse diretrio contm os arquivos de templates para as vises do controlador. No nosso caso um controlador chamado mundo, por isso, as vises para esse controlador sero armazenados dentro de um diretrio chamando app/views/mundo. Para completar nossa aplicao Ol, mundo!, vamos criar um template. Por default, Rails procurar por templeates em um arquivo com o mesmo nome da ao. Neste caso, precisamos criar um arquivo chamado app/views/mundo/ola.rhtml, com o seguinte contedo:

<html> <head> <title>Minha primeira aplicao Rails</title> </head> <body> <h1>Ol, Mundo!</h1> </body> </html> Salve o arquivo e atualize o seu navegador. O resultado dever ser a como mostrador na figura abaixo:

EXIBINDO CONTEDOS DINMICOS H dois modos de criao de templates dinmicos no Rails. A primeira forma, chamada de Builder, discutiremos mais na frente. A segunda forma, usada no exemplo anterior, embutir cdigo rails no template. por isso que o arquivo tem a extenso .rhtml: o rhtml diz ao Rails para expandir o contedo do arquivo usando um sistema chamado ERb (Embedded Ruby). ERb um filtro que pega um arquivo html e retorna uma verso transformada. A sada frequentemente um HTML. Contedos entre <%= e %> so interpretados como cdigo Ruby e executados. O resultado da execuo convertido em string, e o valor substitudo no arquivo no lugar do cdigo limitado pelo <%= e %>. Por exemplo, mude o ola.rhtml para: <html> <head> <title>Minha primeira aplicao Rails</title> </head> <body> <ul> <li>Soma: <%= 1+2 %> </li> <li>Concatenao: <%= "Ruby "+ "on "+"Rails" %> </li>

<li>Tempo em uma hora: <%= 1.hour.from_now %> </li> </ul> </body> </html>

Ao atualizar o navegador, o template ir gerar o seguinte resultado:

Alm disso, cdigo compreendido entre <% e %> (sem o smbolo =) interpretado como cdigo Ruby que executado sem substituio na sada. O interessante nessa caracterstica a possibilidade de misturar com cdigo no-Ruby. Repare uma verso modificado do ola.rhtml: <ul> <li>Soma: <%= 1+2 %> </li> <li>Concatenao: <%= "Ruby "+ "on "+"Rails" %> </li> <li>Tempo em uma hora: <%= 1.hour.from_now %> </li> </ul> <% 3.times do %> Ho! <br /> <% end %> Feliz Natal! O resultado no navegador ser:

Perceba como o texto dentro do lao Ruby enviado para a sada uma vez a cada interao do loop. Podemos misturar as duas formas: <% 3.downto(1) do |count| %> <%= count %>...<br> <% end %> Bum! O resultado no navegador ser:

ADICIONANDO HORA Para adicionar a hora podemos chamar um mtodo do Ruby chamado Time.now a partir do nosso template: <html> <head> <title>Minha primeira aplicao Rails</title> </head> <body> <h1>Ol, Mundo!</h1>

<p> Hora.....: <%= Time.now %> </p> </body> </html> Desse modo, cada vez que o usurio acessar a pgina, a hora corrente ser substituda no corpo da resposta que ser enviada de pgina para o navegador do usurio. Embora nossa aplicao esteja funcionado, interessante deixarmos nosso controlador determinar a hora corrente, deixando para o template a simples funo de exibir o resultado. Mudaremos nossa action ola no controlador mundo para calcular a hora corrente e o resultado armazenar numa varivel de instncia chamada @hora. class MundoController < ApplicationController def ola @hora = Time.now end end No template .rhtml usaremos a varivel de instncia para substituir a hora na sada: <html> <head> <title>Minha primeira aplicao Rails</title> </head> <body> <h1>Ol, Mundo!</h1> <p> Hora.....: <%= @hora %> </p> </body> </html> Ao atualizar o navegador, veremos a data ser mostrada, como na figura abaixo:

Por que tivemos esse trabalho extra de adicionar o clculo da hora no controlador? Porque os benefcios que esse servio traz, vale a pena. Vamos supor que voc queira estender sua aplicao no futuro para suportar usurios de vrios pases. Neste caso, interessante que a data seja formatada de acordo com o fuso horrio do pas do usurio. Teramos apenas que alterar nosso controlador, com isso, nossa aplicao tornar-se- bem mais flexvel.

LINKANDO PGINAS Normalmente, cada pgina da aplicao corresponder a uma viso separada. No nosso caso, usaremos uma nova ao para manusear a pgina (Embora nem sempre seja assim). Usaremos o mesmo controlador para ambas as aes. Vamos adicionar a ao tchau: class MundoController < ApplicationController def ola @hora = Time.now end def tchau end end E o nosso arquivo app/views/mundo/tchau.rhtml: O resultado dever sem parecido com a figura abaixo:

Vamos agora adicionar um link na pgina ola.rhtml apontando para tchau.rhtml e um link em tchau.rhtml apontando para ola.rhtml. No arquivo ola.rhml poderamos fazer assim: <html> <head> <title>Minha primeira aplicao Rails</title> </head> <body> ... <p> Diga <a href="/mundo/tchau">Tchau</a>! </p> </p> </body> </html> E no arquivo tchau.rhtml: <html> <head> <title>V em paz :P </title> </head> <body> <h1>Tchau</h1> <p> <h3>Espero que tenha gostado do Rails</h3> </p> <p> Diga <a href="/mundo/ola">Ola</a>! </p> </body> </html> Essa forma com certeza funcionaria, mas um pouco frgil. Se movssemos a aplicao para um lugar diferente no servidor web, as URLs no seriam mais vlidas. O rails vem com um conjunto de mtodos conhecido como helpers que pode ser usado nos templates da viso. Aqui, usaremos um mtodo chamado de link_to(), o qual cria um hiperlink (dentre outra coisas). Assim, nosso arquivo ola.rhtml ficaria: <html> <head> <title>Minha primeira aplicao Rails</title> </head> <body> <h1>Ol, Mundo!</h1> <p> Hora.....: <%= Time.now %> </p> <p>

Ora de dizer <%= link_to "tchau!", :action => "tchau" %> </p> </body> </html> Vamos analisar o mtodo link_to que acabamos de escrever: link_to "tchau!", :action => "tchau" O primeiro parmetro do mtodo tchau, que corresponde ao texto que do link a ser exibido. O segundo parmetro ,:action, aponta para a ao que ser invocado ao clicarmos no link, nesse caso, a ao tchau. Compreendido o funcionamento do mtodo link_to, vamos dar uma olhada no arquivo tchau.rhtml: <html> <head> <title>V em paz :P </title> </head> <body> <h1>Tchau</h1> <p> <h3>Espero que tenha gostado do Rails</h3> </p> <p> Diga <%= link_to "ola", :action => "ola" %> de novo </p> </body> </html>

CONSTRUINDO NOSSA APLICAO O objetivo desta apostila demonstrar a construo de uma aplicao web usando a framework Rails. Vamos tomar como base, uma aplicao muito comum hoje na internet: uma loja on-line, mais precisamente uma livraria. Vamos chamar nossa aplicao de livrovirtual. CRIAR A APLICAO RAILS No console do sistema operacional, digite rails seguido do nome do projeto, no nosso caso: livrovirtual. curso> rails livrovirtual Dever aparecer na tela algo mais ou menos perecido com isso: create create create create create create create app/controllers app/helpers app/models app/views/layouts config/environments components ... create log/production.log create log/development.log create log/test.log Nossa aplicao foi criada com sucesso. CRIAR O BANCO DE DADOS Para essa aplicao, usaremos o banco de dados open-source MySQL Sever. No cobriremos nessa apostila como instalar o Banco de Dados. H vrias fontes disponveis na internet mostrando como realizar essa tarefa. Usaremos o cliente chamado mysql para trabalhar com nosso banco de dados. uma ferramenta usada em linha de comando, entretanto sinta-se livre para usar qualquer cliente GUI de sua preferncia. No console do sistema, digite o seguinte comando: curso > mysql -u root p Ser solicitado, em seguida, a senha do usurio root do MySql: Enter password: *******

Aps o login, ser mostrado o seguinte resultado na tela: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 1 to server version: 5.0.21-community Type 'help;' or '\h' for help. Type '\c' to clear the buffer. mysql> No prompt do mysql, digite os seguintes comandos: mysql> create database livrovirtual_development; mysql> create database livrovirtual_test; mysql> create database livrovirtual_production; mysql> grant all on livrovirtual_development.* to curso@localhost; mysql> grant all on livrovirtual_test.* to curso@localhost; mysql> grant all on livrovirtual_production.* to curso@localhost identified by 'livr0pr0d'; mysql> flush privileges; CRIAR AS TABELAS DO BANCO Vamos criar primeiro a tabela de Produtos. Segue abaixo a DDL (Data Definition Language) para a criao da mesma no MySql: drop table if exists produtos; create table products ( id int nome varchar(100) descricao text image_url varchar(100) preco decimal(10,2) primary key(id) );

not null auto_increment, not null, not null, not null, not null,

No prompt do mysql, execute o comando abaixo, e, logo em seguida, digite a DDL: mysql> use livrovirtual_development O comando acima foi necessrio para especificar que queremos trabalhar com o banco livrovirtual_development Uma observao muito importante: o Rails assume que toda tabela possui um campo chave-primria chamado de id do tipo int. Alm disso, ele faz distino entre maisculo e minsculo. Lembre-se sempre de criar as chaves primrias de suas tabelas com o nome id.

CONFIGUR A APLICAO Agora vamos configurar o Rails para acessar o banco de dados que criamos. Usando algum editor de texto, abra o arquivo config/database.yml e deixe-o conforme especificado abaixo: development: adapter: mysql database: livrovirtual_development username: curso password: host: localhost test: adapter: mysql database: livrovirtual_test username: curso password: host: localhost production: adapter: mysql database: livrovirtual_production username: prod password: livr0pr0d host: localhost CRIANDO A TELA DE MANUTENO DE PRODUTOS No prompt do console, dentro do diretrio da aplicao, execute o comando abaixo: livrovirtual> ruby script/generate scaffold Produto produto O resultado do comando dever ser como mostrado abaixo: exists app/controllers/ exists app/helpers/ exists app/views/produto exists test/functional/ dependency model exists app/models/ exists test/unit/ exists test/fixtures/ identical app/models/produto.rb identical test/unit/produto_test.rb identical test/fixtures/produtos.yml create app/views/layouts/produto.rhtml create public/stylesheets/scaffold.css

O comando usado anteriormente, criou uma tela bsica de manuteno para a nossa tabela de produtos (Um pouco de mgica). Antes de preocuparmos com o que aconteceu, vamos iniciar nossa aplicao e v-la funcionando. livrovirtual> ruby script/server => Booting WEBrick... => Rails application started on http://0.0.0.0:3000 => Ctrl-C to shutdown server; call with --help for options [2006-10-22 16:14:03] INFO WEBrick 1.3.1 [2006-10-22 16:14:03] INFO ruby 1.8.4 (2006-04-14) [i386-mswin32] [2006-10-22 16:14:03] INFO WEBrick::HTTPServer#start: pid=5264 port=3000

CONHECENDO O SCAFFOLD Para criar nossa lgica CRUD, usamos um script conhecido como scaffold: livrovirtual> ruby script/generate scaffold Produto produto Um scaffold um meio de criar cdigo para um determinado modelo (que indicamos) atravs de um determinado controlador (que indicamos tambm). um meio de comearmos rapidamente a ver os resultados no nosso navegador web e um mtodo muito rpido de implementar o CRUD (Create, Retrieve, Update, Delete) na sua aplicao. Lembrando que o scaffold cria cdigo que, fatalmente, vai ser alterado depois, a no ser que voc deseje manter o nvel muito bsico de interface e controle padro de campos que ele proporciona. No Rails, um modelo automaticamente mapeado para uma tabela no banco de dados cujo nome est no plural. No nosso caso, nos pedimos um modelo chamado Produto, por isso o Rails associou com a tabela produtos.

Adicionando um novo Produto.

Listando os produtos adicionados.

ADICIONANDO UM NOVO CAMPO Executar a seguinte instruo SQL no prompt do mysql: mysql> alter table produtos add column data_inclusao datetime; O esquema de banco de dados foi alterado, por isso devemos alterar nosso cdigo scaffold para refletir essas nova mudanas: livrovirtual> ruby script/generate scaffold Produto produto exists app/controllers/ exists app/helpers/ exists app/views/produto exists test/functional/ dependency model exists app/models/ exists test/unit/ exists test/fixtures/ identical app/models/produto.rb identical test/unit/produto_test.rb identical test/fixtures/produtos.yml overwrite app/views/produto/_form.rhtml? [Ynaq] a forcing scaffold force app/views/produto/_form.rhtml identical app/views/produto/list.rhtml identical app/views/produto/show.rhtml identical app/views/produto/new.rhtml identical app/views/produto/edit.rhtml identical app/controllers/produto_controller.rb identical test/functional/produto_controller_test.rb identical app/helpers/produto_helper.rb identical app/views/layouts/produto.rhtml identical public/stylesheets/scaffold.css Ao atualizar o navegador, a tela de cadastrar um novo produto dever um conter um novo campo do tipo datetime, correspondente ao campo que criamos no banco de dados.

VALIDAO A validao de dados um processo importante que qualquer cadastro. Ele evita que o usurio insira dados que no so permitidos pela regra de negcio, por exemplo, cadastrar um produto com preo negativo ou um e-mail que no possua um @ (ou possua mais de um). Antes do cadastro ser efetivado no banco de dados, o sistema checa todos os valores fornecidos pelo usurio, caso algum esteja incorreto, uma mensagem dever avisar o usurio disso. No Rails, as validaes so colocadas nas classes de modelo. No nosso exemplo de produto, o arquivo corresponde ao app/models/produto.rb: class Produto < ActiveRecord::Base end Basicamente a definio da classe Produto. Note que essa classe herda todas as caractersticas da classe ActiveRecord::Base. Vamos comear validando os campos cujo preenchimento obrigatrio: class Produto < ActiveRecord::Base validates_presence_of :nome, :descricao, :image_url, :preco end

O mtodo validades_presence_of: um validador padro do Rails. Ele checa se um campo, ou um conjunto de campos fornecidos no possuem o contedo vazio.

Agora vamos validar se o preo fornecido realmente numrico: class Produto < ActiveRecord::Base validates_presence_of :nome, :descricao, :image_url, :preco validates_numericality_of :preco end

Em seguida, precisamos testar se o preo maior do que zero. Para isso, precisamos escrever um mtodo chamado validade() na nossa classe de modelo. O Rails sempre vai executar esse mtodo antes de salvar qualquer instncia do nosso produto. Esse mtodo ser protegido (protected), pois no queremos que ele seja chamado de fora do contexto do nosso modelo.
protected def validate errors.add(:preco, "Deve ser maior do que zero") unless preco.nil? || preco >= 0.01 end

Precisamos garantir tambm que cada produto tenha um nome nico dentro do banco de dados:

validates_uniqueness_of :nome Por ltimo, vamos testar se a URL fornecida para a imagem vlido. Para isso, vamos usar o mtodo validates_format_of(), o qual compara o campo com uma expresso regular. Por enquanto, vamos apenas testar se a URL comea com http e termina com jpg, gif ou png.
validates_format_of :image_url, :with => %r{^http:.+\.(gif|jpg|png)$}i, :message => " deve apontar para uma imagem GIF, JPG, or PNG"

Abaixo, uma listagem completa da nossa classe produto.rb: class Produto < ActiveRecord::Base validates_presence_of :nome, :descricao, :image_url, :preco validates_numericality_of :preco validates_uniqueness_of :nome validates_format_of :image_url, :with => %r{^http:.+\.(gif|jpg|png)$}i, :message => " deve apontar para uma imagem GIF, JPG, or PNG" protected def validate errors.add(:preco, "Deve ser maior do que zero") unless preco.nil? || preco >= 0.01 end end CUSTOMIZANDO A VISO A framework Rails automaticamente cria templates para a camada de viso, por isso que conseguimos listar e cadastrar nossos produtos. Contudo, o layout bastante simples, alm das palavras estarem em ingls. No seria interessante colocar-mos o sistema em produo dessa forma. Dessa forma, vamos criar uma tela mais apresentvel para o usurio. O arquivo app/views/produto/list.rhtml produz a pgina de listagem de arquivos. Esse arquivo gerado durante o processo de scaffold. Vamos dar uma olhada no arquivo: <h1>Listing produtos</h1> <table> <tr> <% for column in Produto.content_columns %> <th><%= column.human_name %></th> <% end %> </tr> <% for produto in @produtos %> <tr> <% for column in Produto.content_columns %> <td><%=h produto.send(column.name) %></td> <% end %> <td><%= link_to 'Show', :action => 'show', :id => produto %></td> <td><%= link_to 'Edit', :action => 'edit', :id => produto %></td> <td><%= link_to 'Destroy', { :action => 'destroy', :id => produto }, :confirm => 'Are you sure?', :post => true %></td> </tr> <% end %> </table>

<%= link_to 'Previous page', { :page => @produto_pages.current.previous } if @produto_pages.current.previous %> <%= link_to 'Next page', { :page => @produto_pages.current.next } if @produto_pages.current.next %> <br /> <%= link_to 'New produto', :action => 'new' %> A camada de viso usa o ERb para interagir sobre cada coluna no modelo Produto. Cada tupla dessa tabela adicionada ao array @produtos (Esse array definido no mtodo list do controlador). Vamos alterar esse arquivo para produzir uma layout mais agradvel: <h1>Lista de Produtos</h1> <table cellpadding="5" cellspacing="0"> <% odd_or_even = 0 for produto in @produtos odd_or_even = 1 - odd_or_even %> <tr valign="top" class="ListLine<%= odd_or_even %>"> <td> <img width="60" height="70" src="<%= produto.image_url %>"/> </td> <td width="60%"> <span class="ListTitle"><%= h(produto.nome) %></span><br /> <%= h(truncate(produto.descricao, 80)) %> </td> <td align="right"> <%= produto.data_inclusao.strftime("%y-%m-%d") %><br/> <strong>$<%= sprintf("%0.2f", produto.preco) %></strong> </td> <td class="ListActions"> <%= link_to 'Mostrar', :action => 'show', :id => produto %><br/> <%= link_to 'Editar', :action => 'edit', :id => produto %><br/> <%= link_to 'Excluir', { :action => 'destroy', :id => produto },:confirm => "Confirma excluso?" %> </td> </tr> <% end %> </table> <%= if @produto_pages.current.previous link_to("Previous page", { :page => @produto_pages.current.previous }) end %> <%= if @produto_pages.current.next link_to("Next page", { :page => @produto_pages.current.next }) end %>

<br /> <%= link_to 'Novo Produto', :action => 'new' %> Todas as aplicaes geradas via scaffold usam o sylesheet chamado scaffold.css dentro do diretrio public/stylesheets. Adicione seus prprios estilos nesse arquivo: .ListTitle { color: #244; font-weight: bold; font-size: larger; } .ListActions { font-size: x-small; text-align: right; padding-left: 1em; } .ListLine0 { background: #e0f8f8; } .ListLine1 { background: #45Add0; } O resultado aps as modificaes ser a seguinte:

CATLOGO DA LOJA ( DISPLAY CATALOG) Vamos criar um controlador para interagir com os usurios que esto visitando a loja virtual: livrovirtual> ruby script/generate controller Loja index Acabamos de definir um controlador chamado Loja contendo um mtodo chamado index. Por que escolhemos o nome do mtodo index. Porque esse o mtodo padro de cada controlador. Se voc invocar um controlador Rails sem especificar um mtodo, o Rails automaticamente chama o index. Vamos tentar, aponte o seu navegador para http://localhost:3000/loja.

Lembre-se que o Rails ir buscar um arquivo chamado index.rhtml para montar a viso que ser disponibilizado para o usurio. Como o arquivo no existe, o resultado foi o mostrado acima. Vamos comear com uma lista de todos os produtos no nosso banco de dados. Temos que alterar o mtodo index no controlador loja_controller.rb. class LojaController < ApplicationController def index @produtos = Produto.mostrar_produtos end end Criamos uma varivel chamado @produtps contendo todos os produtos retornados pelo mtodo mostrar_produtos do modelo produto.rb. def self.mostrar_produtos find(:all, :order => "nome") end

O mtodo find() retorna um array contendo um objeto produto para cada linha recuperada do banco de dados. O mtodo mostrar produtos simplesmente passa esse array para o controlador. Agora precisamos escrever nosso template de viso. Por enquanto escreveremos os produtos numa simples tabela. Para fazer isso, edite o arquivo app/views/loja/index.rhtml.
<table cellpadding="5" cellspacing="0"> <% for produto in @produtos %> <tr valign="top"> <td> <img src="<%= produto.image_url %>"/> </td> <td width="450"> <h3><%=h produto.nome %></h3> <small> <%= produto.descricao %> </small> <br/> <strong>$<%= sprintf("%0.2f", produto.preco) %></strong> <%= link_to 'Adicionar ao carrinho', :action => 'add_to_cart', :id => produto %> <br/> </td> </tr> <tr><td colspan="2"><hr/></td></tr> <% end %> </table>

Atualizando o navegador, o tela abaixo dever aparecer:

MELHORANDO A DECORAO O Rails permite o compartilhamento de layouts entre vrias pginas, bastando criar um arquivo com o layout desejado dentro de app/views/layouts. A forma mais simples para fazer isso criar um arquivo com o mesmo nome do controlador. Todas as vises criadas por esse controlador usaro esse layout. No nosso exemplo, o controlador chama-se loja, ento vamos criar um arquivo loja.rhtml dentro de app/views/layouts. <html> <head> <title>Livraria Virtual</title> <%= stylesheet_link_tag "livrovirtual", :media => "all" %> </head> <body> <div id="banner"> || <%= @page_title || "Livraria Virtual" %> </div> <div id="columns"> <div id="side"> <a href="http://www....">Home</a><br /> <a href="http://www..../faq">FAQ</a><br /> <a href="http://www..../news">Notcias</a><br /> <a href="http://www..../contact">Contatos</a><br /> </div> <div id="main"> <%= @content_for_layout %> </div> </div> </body> </html> A mgica encontra-se em @content_for_layout. Essa varivel ser substituda por todo o resultado gerado pela viso invocada por esta requisio. Ou seja, o resultado do processamento do arquivo index.rhtml ser includo no lugar de @content_for_layout e o resultado final aparecer no navegador do usurio. Vamos alterar tambm o arquivo index.rhml. <% for produto in @produtos %> <div class="catalogentry"> <img src="<%= produto.image_url %>"/> <h3><%= h(produto.nome) %></h3> <%= produto.descricao %> <span class="catalogprice"><%= sprintf("$%0.2f", produto.preco) %></span> <%= link_to 'Adicionar ao carrinho', {:action => 'add_to_cart', :id => produto }, :class => 'addtocart' %><br/> </div> <div class="separator">&nbsp;</div>

<% end %> <%= link_to "Mostrar meu carrinho", :action => "mostrar_carro" %> Precisamos incluir o arquivo livrovirtual.css dentro de public/stylesheets. #banner { background: #9c9; padding-top: 10px; padding-bottom: 10px; border-bottom: 2px solid; font: small-caps 40px/40px "Times New Roman", serif; color: #282; text-align: center; } #banner img { float: left; } #columns { background: #141; } #main { margin-left: 7em; padding-top: 4ex; padding-left: 2em; background: white; } #side { float: left; padding-top: 1em; padding-left: 1em; padding-bottom: 1em; width: 6em; background: #141; } #notice { border: 2px solid red; padding: 1em; margin-bottom: 2em; background-color: #f0f0f0; font: bold smaller sans-serif; } a { text-decoration: none; font: smaller sans-serif; } a.addtocart { padding-left: 1em; padding-right: 1em; color: #141; background: #cec; font-weight: bold;

} a.addtocart:hover { color: #000; background: #eec; } #side a { color: #ada; font: smaller sans-serif; } #side a:hover { color: #fff; } /**** styles for the catalog ***/ /* === Use the Holly Hack to fix layout bugs in IE on Windows === */ /* Hide from IE-mac \*/ * html .catalogentry { height: 1%; } /* End hide from IE-mac */ .catalogentry { padding: 1ex 0ex; } .catalogentry img { float: left; margin-right: 2em; } .catalogentry h3 { font: larger bold; color: #282; margin-top: 0ex; margin-bottom: 0.5ex; } .catalogentry p { font: smaller sans-serif; margin-bottom: .5ex; } .catalogprice { padding-right: 4em; } /* Shoppng cart screen */ .carttitle { background: #282; color: #dfd; font: bold smaller sans-serif; text-align: center; } .carttitle TD { padding-bottom: 0px; } #cartmenu { float: right; border-left: 1px dotted #282;

} #totalcell { font-weight: bold; border-top: 1px solid #282; border-bottom: 2px solid #282; text-align: right; } .separator { border-bottom: 1px dotted #282; clear: both; } O resultado final ser:

CRIAO DO CARRINHO DE COMPRAS SESSES Enquanto o usurio navega pelo catlogo da loja, ele ir selecionar produtos para comprar. Esses produtos sero adicionados a um carrinho virtual, at ele proceder com o checkout onde ir pagar pelos itens contidos no carrinho. Parece simples, entretanto h um problema. O protocolo http usado para a navegao web o que chamamos de sem estado (stateless). Ou seja, o navegador no teria como armazenar essas informaes acerca do carrinho de compra. A cada vez que a aplicao receber uma requisio do navegador, como se fosse a primeira vez. Uma soluo para esse problema simular uma camada com estado (stateful) sobre o http. Uma camada da aplicao que tenta mapear uma determinada requisio para uma determinada sesso. Ou seja, requisies que venham de um determinado navegador em particular, a aplicao sabe qual a sesso pertencente a ele e consequentemente todas as informaes a respeito do carrinho, bem como outras informaes pertinentes. Os mecanismos usados para fazer esse rastreamento de sesses so variados. As aplicaes podem codificar a informao de sesso no form de cada pgina. Outra tcnica usada incluir um identificador de sesso no final de cada URL (atravs de uma tcnica conhecida como reescrita de URL). Uma outra forma de codificar sesses atravs de cookies. O Rails utiliza essa ltima forma. Um cookie simplesmente um pequeno arquivo que a aplicao web passa para um navegador web. Com isso, sempre que o navegador enviar uma requisio para a aplicao, os dados contidos no cookie do cliente iro juntos. A aplicao usa a informao do cookie para descobrir qual as informaes de sesses armazenadas no servidor que pertencem quele usurio. Todo esse preciso encapsulado no Rails, ou seja, o programador no precisa preocupar-se em como manipular cookies e sesses. Todo esse trabalho sujo realizado pela framework. O controlador mantm uma coleo denominada session (o qual nada mais do que uma estrutura hash). Quaisquer pares de chaves armazenados nesse hash durante o processamento de uma requisio estaro disponveis durante subseqentes pedidos do mesmo navegador. Como implementar sesso na aplicao? Precisamos ser capaz de registrar um novo carrinho para uma sesso na primeira vez que for necessrio e encontrar esse mesmo carrinho toda vez que for necessrio na mesma sesso. Para fazer isso, necessrio implementar um mtodo helper no controlador da loja: private def find_cart session[:cart] ||= Cart.new end

O mtodo bastante simples. Usa o operador condicional do Ruby ||=. Se o hash da sesso tem um valor correspondente para a chave :cart, o valor retornado imediatamente, caso contrrio um novo objeto cart criado e registrado para a sesso. O novo carrinho retornado. MAIS TABELAS, MAIS MODELOS Vamos criar a tabela que ir representar o carrinho de compras no banco de dados.
drop table if exists line_items; create table line_items ( id int not null auto_increment, produto_id int not null, quantidade int not null default 0, preco decimal(10,2) not null, constraint fk_items_produtos foreign key (produto_id) references produtos(id), primary key (id) );

Temos que criar o modelo para essa nova tabela. Um classe chamada LineItems ser mapeada para a tabela line_items. livrovirtual> ruby script/generate model LineItem Temos que dizer o Rails sobre o relacionamento entre line_items e produtos. Especificamos isso diretamente no arquivo de modelo da classe LineItem, mais precisamente app/models/line_item.rb class LineItem < ActiveRecord::Base belongs_to :produto end CRIANDO O CARRINHO O mostrurio da nossa loja virtual possui um link para Adicionar ao carrinho para cada link listado. <%= link_to 'Adicionar ao carrinho', {:action => 'add_to_cart', :id => produto }, :class => 'addtocart' %><br/> O link aponta para uma ao chamada add_to_cart do controlador loja e passar o id do produto como parmetro (o Rails interpreta :id => produto da mesma forma que :id => produto.id). Precisamos implementar o mtodo add_to_cart agora. necessrio encontrar o carrinho de compras da sesso atual (ou criar uma se ainda no existe), adicionar o item selecionado e mostrar o contedo do carrinho para o usurio Primeiramente, criaremos o mtodo add_to_cart em app/controllers/loja_controller.rb.

def add_to_cart produto = Produto.find(params[:id]) @cart = find_cart @cart.add_produto(produto) redirect_to(:action => "mostrar_carro") end Esse mtodo ainda no funcionar, pois no temos a classe Cart. Vamos criar agora. Como essa classe armazena dados da aplicao, logicamente parte do nosso modelo. Vamos criar um arquivo chamado cart.rb dentro do diretrio app/models. Como essa classe no est vinculado a nenhuma tabela do banco de dados, no uma sub-classe de ActiveRecord::Base. class Cart attr_reader :items attr_reader :preco_total def initialize @items = [] @preco_total = 0.0 end def add_produto(produto) @items << LineItem.for_produto(produto) @preco_total += produto.preco end end Criamos um novo item baseado no produto e o adicionamos lista. Precisamos, agora, criar agora o mtodo for_produto na classe LineItem, pois ainda no temos um mtodo que cria um item baseado nas informaes do produto. class LineItem < ActiveRecord::Base belongs_to :produto def self.for_produto(produto) item = self.new item.quantidade = 1; item.produto = produto item.preco = produto.preco item end end Temos de criar agora o mtodo mostrar_carro e a viso correspondente. No arquivo loja_controller.rb, adicione o seguinte mtodo:

def mostrar_carro @cart = find_cart @items = @cart.items end No diretrio app/views/loja, crie um arquivo chamado mostrar_carro.rhtml, com o seguinte contedo: <h1>Carrinho de Compras</h1> <p> Seu carrinho contm <%= @items.size %> itens. </p> Antes de qualquer coisa, temos de informar ao Rails sobre nossas classes Cart e LineItem. Edite o arquivo app/controllers/application.rb e adicione o seguinte contedo: class ApplicationController < ActionController::Base model :cart model :line_item end Esse arquivo usado para estabelecer um contexto para toda a aplicao. Apontando seu navegador para http://localhost:3000/loja, e clicando no link Mostrar meu carrinho, devemos obter o seguinte resultado:

MELHORANDO A TELA DO CARRINHO DE COMPRAS Vamos alterar o cdigo contido no arquivo mostrar_carro.rhtml com o seguinte contedo: <h1>Carrinho de Compras</h1> <table> <% for item in @items produto = item.produto -%> <tr> <td><%= item.quantidade %></td> <td><%= h(produto.nome) %></td> <td align="right"><%= item.preco %></td> <td align="right"><%= item.preco * item.quantidade %></td> </tr> <% end -%> </table> Obteremos o seguinte resultado:

Parece interessante, mas temos um problema. Clique no boto de voltar e selecione novamente o mesmo produto:

Seria interessante que vrias referencias do mesmo produto fossem agrupados em apenas uma linha. Vamos alterar o mtodo add_produto() no modelo Cart. Quando um novo produto for adicionado, vamos determinar se o mesmo produto j foi inserido no carrinho. Tendo sido, vamos incrementar o campo quantidade ao invs de adicionar uma nova linha. def add_produto(produto) item = @items.find {|i| i.produto_id == produto.id} if item item.quantidade += 1 else item = LineItem.for_produto(produto) @items << item end @preco_total += produto.preco end Alterando o contedo de mostrar_carro.rhml: <div id="cartmenu"> <ul> <li><%= link_to 'Continuar Comprar', :action => "index" %></li> <li><%= link_to 'Limpar Carrinho', :action => "empty_cart" %></li> <li><%= link_to 'Finalizar Compra', :action => "checkout" %></li> </ul> </div> <table cellpadding="10" cellspacing="0"> <tr class="carttitle"> <td rowspan="2">Qtd</td> <td rowspan="2">Descrio</td> <td colspan="2">Preo</td> </tr> <tr class="carttitle">

<td>Each</td> <td>Total</td> </tr> <% for item in @items produto = item.produto -%> <tr> <td><%= item.quantidade %></td> <td><%= h(produto.nome) %></td> <td align="right"><%= item.preco %></td> <td align="right"><%= item.preco * item.quantidade %></td> </tr> <% end %> <tr> <td colspan="3" align="right"><strong>Total:</strong></td> <td id="totalcell"><%= @cart.preco_total %></td> </tr> </table> Como resultado:

TRATANDO ERROS Nossa aplicao permite um erro, casso o usurio digite na barra de endereo a seguinte url http://localhost:3000/loja/add_to_cart/aaaa ocorrer o seguinte erro:

Nossa aplicao sempre tenta localizar o produto pelo id: produto = Produto.find(params[:id]) Caso algum id que no exista no banco de dados seja passado como parmetro, o mtodo add_to_cart no encontrar e o erro acima aparecer no navegador do usurio. interessante, contudo que o Rails saiba como tratar quando erros desses tipos ocorrerem, pois no faz sentindo o usurio encontrar tal tela enquanto navega pela aplicao. FLASH O Rails define uma estrutura denominada Flash. Seria mais ou menos como uma tabela hash, onde possvel armazenar dados enquanto a requisio processada. Na nossa aplicao iremos usar o Flash para armazenar as mensagens de erro. Quando o mtodo add_to_cart descobrir que foi passado um produto inexistente no banco de dados, ele pode armazenar a mensagem de erro na rea de flash e redirecionar a ao index e mostrar novamente o catlogo juntamente com o contedo do Flash. Vamos alterar o mtodo add_to_cart para interceptar o erro e armazenar no varivel flash.

def add_to_cart produto = Produto.find(params[:id]) @cart = find_cart @cart.add_produto(produto) redirect_to(:action => "mostrar_carro") rescue logger.error("Produto invlido #{params[:id]}") flash[:notice] = 'Produto Invlido' redirect_to(:action => 'index') end O comando rescue intercepta a exceo levantada pelo find. Neste caso, o Rails registra o erro (logger.error), cria uma mensagem explanando o erro e redireciona para o index. Neste momento, caso o usurio tente acessar um produto que no existe, diretamente pela url (http://localhost:3000/loja/add_to_cart/aaaa), no ira aparecer nenhuma mensagem de erro. O usurio simplesmente ser redirecionado para a pgina principal como se nada tivesse acontecido. interessante que a mensagem flash aparea para o usurio. Vamos alterar o layout index.rhtml (dentro de app/views/layouts) <html> <head> <title>Livraria Virtual</title> <%= stylesheet_link_tag "livrovirtual", :media => "all" %> </head> <body> <div id="banner"> || <%= @page_title || "Livraria Virtual" %> </div> <div id="columns"> <div id="side"> <a href="http://www....">Home</a><br /> <a href="http://www..../faq">FAQ</a><br /> <a href="http://www..../news">Notcias</a><br /> <a href="http://www..../contact">Contatos</a><br /> </div> <div id="main"> <% if @flash[:notice] -%> <div id="notice"><%= @flash[:notice] %></div> <% end -%> <%= @content_for_layout %> </div> </div> </body> </html>

Como resultado, termos a seguinte tela, caso o usurio tente acessar um produto que no exista:

Vamos alterar o mtodo mostrar_carro() para testar se o carrinho est vazio: def mostrar_carro @cart = find_cart @items = @cart.items if @items.empty? flash[:notice] = "Seu carrinho atua esta vazio" redirect_to(:action => 'index') end end

TERMINANDO O CARRINHO Temos de implementar o mtodo limpar_carro(), para o usurio poder limpar seu carrinho caso desista daqueles produtos. Na classe Cart, implementaremos o mtodo limpar!. (Nomes de mtodos Ruby podem terminar com ! ou ?. Usaremos a exclamao para indicar para desenvolvedores, que forem ler nosso cdigo, que esse mtodo faz algo destrutivo). def limpar! @items = [] @preco_total = 0.0 end Agora, no controlador Loja, temos de incluir o mtodo limpar_carro(). def limpar_carro @cart = find_cart @cart.limpar! redirecionar_para_index('Carrinho foi esvaziado') end private def redirecionar_para_index(msg = nil) flash[:notice] = msg if msg redirect_to(:action => 'index') end O construtor de Cart faz realiza a mesma tarefa que o mtodo limpar! Podemos resolver isso da seguinte forma: def initialize limpar! end def limpar!

@items = [] @preco_total = 0.0 end

HELPERS Helper consiste basicamente em um pedao de cdigo que automaticamente includo nas vises. Os arquivos de helpers so definidos dentro do diretrio app/helpers. Um helper chamado xyz_helper.rb define um mtodo que ser sempre executado nas vises invocadas pelo controlador xyz. Mtodos de helper definidos no arquivo app/helpers/application_helper.rb sero disponibilizados para todas as vises. Por exemplo, edite o arquivo app/helpers/application_helper.rb e adicione o seguinte cdigo: module ApplicationHelper def fmt_real(amt) sprintf("R$ %0.2f", amt) end end Devemos atualizar o mostrar_carro.rhtml: <% for item in @items produto = item.produto -%> <tr> <td><%= item.quantidade %></td> <td><%= h(produto.nome) %></td> <td align="right"><%= fmt_real(item.preco) %></td> <td align="right"><%= fmt_real(item.preco * item.quantidade) %></td> </tr> <% end %> <tr> <td colspan="3" align="right"><strong>Total:</strong></td> <td id="totalcell"><%= fmt_real(@cart.preco_total) %></td> </tr> Como resultador:

CHECKOUT Nesse momento, vamos criar a tabela de pedidos:


drop table if exists pedidos; create table pedidos ( id int nome varchar(100) email varchar(255) endereco text tipo_pagamento char(10) primary key (id) );

not null not null, not null, not null, not null,

auto_increment,

Um pedido ser associado com um vrios itens, ou seja, nossa tabela line_items dever possuir o cdigo do pedido. Devemos, ento, alterar a estrutura da tabela line_items:
drop table if exists line_items; create table line_items ( id int not null auto_increment, produto_id int not null, quantidade int not null default 0, preco decimal(10,2) not null, constraint fk_items_pedido foreign key (pedido_id) references pedidos(id), constraint fk_items_produtos foreign key (produto_id) references produtos(id), primary key (id) );

Criando o modelo de Pedido: L livrovirtual> ruby script/generate model Pedido

Temos de informar ao Rails, sobre o relacionamento da tabela pedidos com a tabela line_items.

Temos de editar o arquivo app/models/pedido.db e adicionar a chamada hs_many: class Pedido < ActiveRecord::Base has_many :line_items end Agora o inverso, vamos indicar ao Rails que um item de produto pertence a um pedido. No arquivo app/models/line_item.rb: class LineItem < ActiveRecord::Base belongs_to :produto belongs_to :pedido # ... No arquivo mostrar_carro.rhtml colocamos um link para uma ao chamada checkout. Devemos, agora, implementar essa ao no controlador da loja: def checkout @cart = find_cart @items = @cart.items if @items.empty? redirecionar_para_index("Carrinho de compras esta vazio") else @order = Pedido.new end end Criando o arquivo de viso para a ao checkout. Crie um arquivo chamado checkout.rhtml dentro de app/views/loja, com o seguinte contedo:
<% @page_title = "Checkout" -%> <%= start_form_tag(:action => " gravar_pedido ") %> <table> <tr> <td>Nome:</td> <td><%= text_field("pedido", "nome", "size" => 40 ) %></td> </tr> <tr> <td>EMail:</td> <td><%= text_field("pedido", "email", "size" => 40 ) %></td> </tr> <tr valign="top"> <td>Address:</td> <td><%= text_area("pedido", "endereco", "cols" => 40, "rows" => 5) %></td> </tr> <tr> <td>Pay using:</td> <td><%=

options = [["Selecione uma forma de pagamento", ""]] + Pedido::PAYMENT_TYPES select("pedido", "tipo_pagamento", options) %></td> </tr> <tr> <td></td> <td><%= submit_tag(" CHECKOUT ") %></td> </tr> </table> <%= end_form_tag %> [ OBS COLOCAR EXPLICAES SOBRE O FORM ACIMA PAG 103/104 DO LIVRO]

Clicando-se no link checkout na tela de display do carrinho de compras, deveremos ser levado seguinte tela:

Clicando no boto CHECKOUT, o erro abaixo ocorrer, uma vez que ainda no implementamos o mtodo gravar_pedido.

O mtodo gravar_pedido deve ser adicionado no controlador loja: def gravar_pedido @cart = find_cart @pedido = Pedido.new(params[:pedido]) @pedido.line_items << @cart.items if @pedido.save @cart.limpar! redirecionar_para_index('Obrigado pela compra e volte sempre.') else render(:action => 'checkout') end end Como resultado fina, aps o usurio clicar em checkout:

ADMINISTRANDO A APLICAO Nossa prxima tarefa ser criar um sistema de login onde somente usurios devidamente cadastrados podero acessar certas reas da nossa aplicao. O primeiro passo consiste em criar uma tabela no banco de dados para armazenar os nomes dos usurios e suas respectivas senhas. drop table if exists usuarios; create table usuarios ( id int nome varchar(100) senha char(40) primary key (id) ); Segundo passo, criar um modelo no Rails: livrovirtual> ruby script/generate model Usuario Precisamos agora, criar uma tela que permita cadastrar, listar, remover bem como outras aes em cima dos usurios. Poderamos usar a tcnica de scaffolding, mas iremos fazer manualmente, para demonstrar algumas novas tcnicas do Rails. livrovirtual> ruby script/generate controller Login adicionar_user login logout remover_user listar_user Definimos um controlador chamado Login, com os seguintes mtodos: adicionar_user, login, logout, remover_user, listar_user. Vamos olhar o contedo do arquivo app/controllers/login_controller.rb:
class LoginController < ApplicationController def adicionar_user end def login end def logout end def remover_user end def listar_user end end

not null auto_increment, not null, null,

O mtodo adicionar_user, ao ser invocado, dever verificar a requiso http para checar se a mesma contem dados de um form: contendo dados, a requisio vir na forma de um POST, caso no possua dados, a requisio vir na forma de GET. Dentro da framework Rails, as informaes acerca da requisio esto dentro do atributo request.

Podemos checar o contedo deste atributo usando os mtodos get?() e post?(). Vamos alterar o login_controller para: class LoginController < ApplicationController layout "admin" def adicionar_user if request.get? @usuario = Usuario.new else @usuario = Usuario.new(params[:usuario]) if @usuario.save redirecionar_para_index("Usuario #{@usuario.nome} criado") end end end ... Caso a requisio de entrada seja um GET, o mtodo adicionar_user sabe que no existe dados, ento cria um novo objeto Usurio para a viso usar. Mas se a requisio no for um GET, o mtodo assume que um post, logo h dados presentes. Ele carrega um objeto Usurio com os dados do form e tenta salva-lo. Obtendo sucesso, ele redireciona para a pgina de index, caso contrrio mostra a viso novamente, permitindo o usurio corrigir qualquer erro. Nosso prximo passo consiste em criar a viso para o nosso mtodo adicionar_user. Edite o arquivo chamado app/views/login/adicionar_user.rhtml e adicione o seguinte contedo: <% @page_title = "Adicionar um usurio" -%> <%= error_messages_for 'usuario' %> <%= form_tag %> <table> <tr> <td>User name:</td> <td><%= text_field("usuario", "nome") %></td> </tr> <tr> <td>Password:</td> <td><%= password_field("usuario", "senha") %></td> </tr> <tr> <td></td> <td><input type="submit" value=" Adicionar " /></td> </tr> </table> <%= end_form_tag %> Temos um pequeno problema: ao cadastrar um novo usurio, a senha digitada estar no formato texto plano, entretanto, seria interessante gravarmos a senha no banco

no formato hash, para que ningum pudesse identificar as senhas dos usurios cadastrados. Por isso, criamos um atributo no modelo chamado senha_texto que ira receber a senha digita no form pelo usurio. Em seguida, o valor armazenado nesse atributo ser convertido em hash e armazenado na coluna senha da tabela usurios. class Usuario < ActiveRecord::Base attr_accessor :senha_texto O Active Record define um grande nmero de ganchos (denominados callback hooks) que so invocados em vrios pontos durante a vida de um objeto modelo. Temos, por exemplos, callbacks que so invocados antes de um modelo ser validado, antes de um registro ser salvo, aps um registro ter sido criado, e etc. No nosso caso. Depois que um registro for salvo, usaremos o gancho after_create() para limpar a senha texto plano do campo. Faremos isso porque eventualmente os dados do objeto usurio ficaro armazenados na sesso, e no queremos que as senhas digitadas fiquem gravadas em discos com risco de algum ver. Vamos alterar nosso modelo Usurio: require "digest/sha1" class Usuario < ActiveRecord::Base attr_accessor :senha_texto attr_accessible :nome, :senha_texto validates_uniqueness_of :nome validates_presence_of :nome, :senha_texto def before_create self.senha = Usuario.senha(self.senha_texto) end def after_create @senha_texto = nil end private def self.senha(senha_texto) Digest::SHA1.hexdigest(senha_texto) end end O mtodo adicionar_user no controlador chama o mtodo redirecionar_para_index(). Ns definimos esse mtodo anteriormente, mas no acessvel para o controlador login. Para isso, temos que mover o mtodo para dentro de app/controllers/application.rb. A classe Application Controller pai de todas as classes controladores de nosso sistema. Mtodos definidos aqui so disponibilizados para todos os controladores:

class ApplicationController < ActionController::Base model :cart model :line_item private def redirecionar_para_index(msg = nil) flash[:notice] = msg if msg redirect_to(:action => 'index') end end Agora podemos cadastrar usurios no nosso banco de dados. Para isso, aponte o navegador para http://localhost:3000/login/adicionar_user.

LOG IN Precisamos escrever o mtodo login(): def login if request.get? session[:usuario_id] = nil @usuario = Usuario.new else @usuario = Usuario.new(params[:usuario]) logged_in_user = @usuario.tentar_login if logged_in_user session[:usuario_id] = logged_in_user.id redirect_to(:action => "index") else flash[:notice] = "Usuario ou senha invlido" end end end

Caso o mtodo login receba um POST, extrai em um objeto Usurio. Em seguida, invoca o mtodo tentar_login do objeto. Este mtodo retorna um objeto Usurio correspondente ao registro no banco de dados, mas somente se o nome e a senha corresponderem. As alteraes no modelo Usurio (usurio.rb) segue logo abaixo: def self.login(nome, senha_texto) hashed_senha = senha(senha_texto || "") find(:first, :conditions => ["nome = ? and senha = ?", nome, hashed_senha]) end def tentar_login Usuario.login(self.nome, self.senha_texto) end Contedo do arquivo app/views/login/login.rhml: <%= form_tag %> <table> <tr> <td>User name:</td> <td><%= text_field("usuario", "nome") %></td> </tr> <tr> <td>Password:</td> <td><%= password_field("usuario", "senha_texto") %></td> </tr> <tr> <td></td> <td><input type="submit" value=" LOGIN " /></td> </tr> </table> <%= end_form_tag %> Por ltimo, vamos criar a viso do mtodo index do controlador Login. Edite o arquivo app/views/login/index.rhtml como especificado abaixo: <% @page_title = "Administrao" -%> <h1>Livraria Virtual</h1> <p> Total de Pedidos no Sistema: <%= @total_pedidos %> </p>

Vamos testar nosso login. Aponte o http://localhost:3000/login/login. Dever aparecer a tela abaixo:

navegador

para

Digite um usurio e uma senha vlida e clique em login:

Caso fosse fornecido um usurio ou uma senha invlido:

LIMITANDO O ACESSO Precisamos impedir que usurios sem poderes administrativos possam acessar as reas restritas da aplicao. O Rails implementa um artifcio que torna muito simples essa tarefa. O nome desse artifcio filtro(filter). Os filtros do Rails permitem voc interceptar chamadas para aes e adicionar seu prprio processamento antes do mtodo ser invocado. No nosso caso, usaremos um filtro denominado before filter para interceptar todas as chamadas para aes nos controladores administrativos. O interceptador poder checar o contedo da varivel session[:user_id] . Se existir, a aplicao sabe que um administrador est logado e a chamada pode prosseguir, caso contrrio, o interceptador por fazer um redirecionamento, neste caso, para a tela de login. Vamos criar um mtodo chamado autorizar no ApplicationController para realizar esse teste: def autorizar unless session[:usuario_id] flash[:notice] = "Usuario nao logado" redirect_to(:controller => "login", :action => "login") end end O mtodo autorizar pode ser invocado antes de qualquer ao nos controles administrativos: class ProdutoController < ApplicationController before_filter :autorizar ... class LoginController < ApplicationController before_filter :autorizar, :except => login Caso o usurio tente acessar http://localhost:3000/produto/list sem estar logado primeiro:

RAILS AVANADO Uma das coisas mais interessantes sobre o Rails a forma como ele construdo. Quando um desenvolvedor est programando, ele passa mais tempo tratando com componentes de alto-nvel como Action Record e Action View. H um componente chamado Rails, mas encontra-se por detrs dos outros componentes, orquestrando silenciosamente o que eles fazem e fazendo tudo funcionar perfeitamente. Sem o componente Rails, nada aconteceria muito. Entretanto, somente uma pequena parte da desta infra-estrutura relevante para desenvolvedores no seu dia-a-dia. Vamos falar agora sobre essas partes mais relevantes. ESTRUTURA DE DIRETRIO Vamos olhar mais precisamente, nesse momento, a estrutura de diretrios (e o que existe dentro deles) criados pelo comando rails minha_aplicacao. minha_aplicacao/ |app/ - Arquivo de modelo, viso e controladores subdiretrio app |components/ Componentes Reusveis |config/ - Configurao de parmetros de configurao de banco de dados |db/ - Informao de esquema |doc/ - Documentao autogerada |lib/ - Bibliotecas compartilhadas |log/ - Arquivos de logs produzidos pelas aplicaes em uso |public/ - Diretrio acessvel via web. |Rakefile/ - Scripts de construo para documentaes e testes |script/ - Scripts de utilitrios |teste/ - Testes de unidades, funcionais, simulao e instalaes |vendor/ - Cdigos de terceiros ficam no

O contedo do diretrio app seria parecido com o exemplo abaixo: app/ controllers/ - application.rb - loja_controller.rb helpers/ - application_helper.rb - loja_helper.rb - produto.rb - layouts/ - loja - index.rhtml - mostrar_carro.rhtml

models/ views

Grande parte do trabalho do desenvolvedor concentra-se nos disretrios app e test. O cdigo principal da aplicao localiza-se abaixo do diretrio app. Vamos falar mais sobre esse diretrio quando estivermos tratando de Active Record, Action Controller, e Action View. Podemos tambm escrever cdigos no diretrio components (falaremos mais a frente nesta apostila). O diretrio doc usado para a documentao da aplicao, produzido atravs do RDoc. Se voc executar rake appdoc, voc ter uma documentao em html localizado dentro do diretrio doc/app. Voc pode criar uma pagina inicial para esta documentao editando o arquivo doc/README_FOR_APP. Os diretrios lib e vendor servem para propsitos similares. Ambos armazenam cdigos que so usados na aplicao mas no pertencem exclusivamente aplicao. O diretrio lib ser usado para armazenar cdigos que voc ou outro desenvolvedor do seu projeto escreveu, enquanto o diretrio vendor para cdigos de terceiros. O diretrio public a face externa da sua aplicao. O servidor de web usa este diretrio como se fosse a base da sua aplicao. Os programas armazenados no diretrio script so bastantes teis para os desenvolvedores. Rode qualquer desses programas sem argumentos para visualizar as informaes de uso: ! ! ! ! ! benchmarker Consiga informaes de performance em um ou mais mtodos da sua aplicao. breakpointer Um cliente que permite voc interagir com aplicaes Rails que estejam rodando. Falaremos mais adiante. console - Permite voc usar o irb para interagir com seus mtodos Rails. destroy Remover arquivos gerados automaticamente pelo generate. generate Um gerador de cdigo. Cria automaticamente controladores, mailers, modelos, scaffold e web services. Voc pode fazer o download de mdulos adicionais de generate no site do Rails*.

! ! !

profiler Cria um um sumrio de um pedao de cdigo da sua aplicao. runner Executa mtodos da sua aplicao fora do contexto de web. server Um servidor de web baseado no WEBrick para rodar suas aplicaes.

Os diretrios config e db requerem um pouco mais de discusso, por isso cada um ter sua prpria sesso. CONFIGURAO DO RAILS As configuraes runtime do rails so controladas pelos arquivos no diretrio config. Esses arquivos trabalham em conjunto com o conceito de runtime environments.(ambientes de execuo) Ambientes de Execuo(Runtime Environments) Rails define trs ambientes de execuo. Cada um com seus prprios conjuntos de parmetros. Rodar a mesma aplicao em ambientes diferentes, muda a personalidade da aplicao. Alterar o ambiente de execuo feito externamente aplicao, ou seja, nenhum cdigo precisa ser adicionado sua aplicao. O modo como voc especifica o ambiente depende de como voc roda a sua aplicao. Se voc usa o script/server, use a opo -e: livrovirtual> ruby script/server e development Os trs ambientes so: development, test e production. Se voc usa Apache, use a varivel de ambiente RAILS_ENV conforme descrito abaixo: ! Apache/CGI Nas configuraes de vhost do httpd.conf ou no arquivo .htaccess, adicione a seguinte linha: SetEnv RAILS_ENV production (ou development ou test) ! Apache/FastCGI No arquivo httpd.conf, nas definies de FastCgiServer, adicione a seguinte linha: -initial-env RAILS_ENV=production

ACTIVE RECORD BSICO Active Record a camada de mapeamento objeto-relacional (ORM) fornecido com o Rails: Tabelas so mapeadas para classes, registros para objetos e colunas para atributos dos objetos. Difere da maioria das bibliotecas de ORM pela forma como configurado. Active Record minimiza a quantidade de configuraes que os desenvolvedores precisam realizar. Como exemplo, segue um programa que usa o AR para acessar uma tabela de pedidos em um banco de dados MySQL. Depois de encontrar um registro com um determinado id, o nome do comprador alterado e salvo de volta no banco de dados, atualizando o registro original. require "rubygems" require_gem "activerecord" ActiveRecord::Base.establish_connection(:adapter => "mysql", :host => "127.0.0.1", :database => "marvinlemos_develop", :username => "marvin", :password => "xy123") class Pedido < ActiveRecord::Base end pedido = Pedido.find(1); pedido.nome = "Marvin Lemos" pedido.save

Relativamente simples, fora a configurao do banco, nenhuma configurao a mais foi requerida. De alguma forma, o Active Record descobriu o que era necessrio realizar. Vamos olhar como isso funciona. Tabelas e Classes Quando criamos uma sublasse de ActiveRecord::Base, criamos, na verdade, uma classe que encapsula uma tabela no banco de dados. Por default, Active Record assume que o nome da tabela est no plural (em ingls, claro). Caso o nome da classe possua mltiplas palavras em caixa altas (por exemplo, PedidoItem), o nome da tabela definida como tendo sublinhado entres as palavras (por exemplo, pedido_items). Segue uma tabela com outros exemplos: Nome da Classe Order TaxAgency Batch Quantity Nome da Tabela Orders Tax_agencies Batches Quantities

Entretanto, muito esquisito para ns, brasileiros, trabalharmos com palavras plurazidadas em ingls, por isso, recomenda-se desabilitar esse comportamento. Basta adicionar a seguinte configurao no arquivo de configurao da aplicao: config/environment.rb. ActiveRecord::Base.pluralize_table_names = false

Colunas e Atributos Objetos no Active Record correspondem a registros na tabela. Os objetos possuem atributos que correspondem a colunas na tabela. Na definio de uma classe que estende Active Record, no necessrio especificar os atributos, uma vez que eles so determinados dinamicamente em tempo de execuo. Por exemplo, temos a tabela cidade que foi criado com o seguinte comando SQL: CREATE TABLE cidade ( id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, nome VARCHAR(60) NOT NULL, PRIMARY KEY(id) ); Podemos criar uma classe Active Record para encapsular essa tabela: require "rubygems" require_gem "activerecord" # Cdigo de conexo foi omitido ... ActiveRecord::Base.pluralize_table_names = false class Cidade < ActiveRecord::Base end Uma vez definido a classe Cidade, podemos interroga-la sobre atributos(colunas) que ela contem. O cdigo que se segue, usa o mtodo columns(), o qual retorna um array de objetos de tipo Column. A partir desses objetos, podemos mostrar apenas o nome de cada coluna da tabela cidade (perceba que desabilitamos a pluralizao) e algumas informaes a respeito de uma coluna chamada nome. (Este cdigo usa a biblioteca pp para formatar os objetos de uma forma mais agradvel.) require "rubygems" require_gem "activerecord" # Cdigo de conexo foi omitido ... ActiveRecord::Base.pluralize_table_names = false class Cidade < ActiveRecord::Base end require 'pp' pp Cidade.columns.map {|col| col.name} pp Cidade.columns_hash['nome']

Quando o cdigo for executado, o seguinte resultado ser mostrado: ["id", "nome"] #<ActiveRecord::ConnectionAdapters::MysqlColumn:0x2ce2514 @default="", @limit=60, @name="nome", @null=false, @number=false, @primary=false, @sql_type="varchar(60)", @text=true, @type=:string> Acessando Atributos O nosso modelo Pedido, possui um atributo chamado de nome, podemos acessar o valor do atributo usando o operador indexado, passando como parmetro o atributo a ser acessado. Como exemplo: pedido[:nome] pedido[:nome] = "Maria" # => Retorna o valor Corrente # => Altera o valor do nome

Contudo, esta forma est deprecated, para acessar os atributos de um modelo, recomenda-se usar os mtodos acessores do Ruby. pedido.nome pedido.nome = Maria Os valores retornados usando as duas tcnicas sero transformado pelo Active Record para um tipo Ruby apropriado. Caso queira pegar o valor em estado natural de um atributo, adicione _before_type_cast no mtodo, como mostrado no cdigo abaixo: pedido.nome_before_type_cast

Sobrescrevendo Atributos do Modelo Aqui est um dos benefcios de usar mtodos acessores para os atributos do modelo. Nosso modelo de pedido ir levantar uma exceo caso o valor do pedido seja menor ou igual a zero. class Pedido < ActiveRecord::Base def valor=(valornew) raise ValorMenorIgualZero if valornew <=0 self[:valor] = valornew end end

Atributos Booleanos Ao verificar o valor de atributo, Ruby considera como falso somente se o nmero for zero; umas das strings 0, f, false ou (string vazio); ou a constante false. Qualquer outro valor interpretado como verdadeiro. Para consultar se uma coluna verdadeiro ou falso, adicione o ? ao final do mtodo acessor: usuario = Usuario.find_by_name(Marvin) if usuario.superuser? grant_privileges end

Chaves Primrias e IDs conveno do Rails chamar todas as chaves primrias da tabela de id. Entretanto, muito comum, principalmente se for trabalhar em sistemas legados, encontrar tabelas cuja chave primria seja um campo cujo nome no seja id. Neste caso, necessrio dizer para o modelo qual o nome da chave primrio. O Active Record define uma maneira simples de sobrescrever o nome da chave primria para uma tabela. class Pedido < ActiveRecord::Base set_primary_key "codped" end O Rails, normalmente, preocupa-se em incrementar o automaticamente o id da tabela. Entretanto, definindo-se outra chave primria, tarefa do programador em fazer o incremento sempre um novo registro for criado. Detalhe importante: mesmo definindo como chave primria da nossa classe pedido o campo codped, sempre que for necessrio alterar o identificador, usa-se o atributo id. Em qualquer outra situao, use o nome do campo. pedido = Pedido.new pedido.id = "000123-01" pedido.cliente = "Maria Pereira" pedido.save pedido = Pedido.find("000123-01") puts pedido.cliente Conectando ao Banco de Dados funo do Active Record realizar todas as operaes de acesso aos bancos de dados, tirando da aplicao, todo o peso de acesso aos mais diferentes bancos existentes. Com isso, basta define qual o banco deseja-se realizar que o Active Record faz todo o resto. A sua aplicao no se comunica diretamente com o banco, mas sim com o AR.

Um modo de especificar a conexo usar o mtodo de classe establish_connection. Por exemplo, o cdigo abaixo especifica uma conexo com um banco MySql: ActiveRecord::Base.establish_connection( :adapter => "mysql", :host => "marvinlemos.net", :database => "cursodb", :username => "cursouser", :password => "curso@y123" ) Active Record vem com suporte ao Oracle, DB2, MySql, Postgres, Sql Server e SQLite. Para cada banco, os parmetros de configurao podem variar. Conexes so associadas com classes de modelo. Cada classe herda a conexo de sua classe-pai.. Como o ActiveRecord::Base a classe base para todas as classes ActiveRecord, definindo uma conexo para ele, automaticamente, todas as classes-filhas tambm herdaro essa conexo. Contudo, essa conexo pode ser sobrescrita quando necessria. O Rails permite um outro mtodo de configurao do banco: Ele define um arquivo chamado config/database.yml. Essa forma mais interessante, pois mantm todas as informaes acerca da conexo fora do cdigo. CRUD (Create, Read, Update, Delete) Active Record torna fcil nossa vida no que diz respeito a criao de lgicas CRUD (Create, Read, Update, Delete) ou seja, Criar, Ler, Atualizar, Remover. Vamos novamente retornar nossa tabela de pedido: require "rubygems" require_gem "activerecord" class Pedido < ActiveRecord::Base end Criando Novos Registros Para criar novos registros em uma tabela, devemos, ento, criar novos objetos da classe apropriada. Por exemplo, podemos criar novos objetos representando registros da tabela pedidos chamando Pedido.new(). Podemos preencher os atributos da classe, que correspondem aos campos da tabela. Finalmente, chamamos o mtodo save() do objeto.

class Teste pedido = Pedido.new pedido.nome = "Raul Seixas" pedido.valor = 10 pedido.save end Os construtores do Active Record aceitam um hash de valores como um parmetro opcional. Cada entrada nesse hash corresponde ao nome e o valor de um atributo da classe: class Teste pedido = Pedido.new( :nome => "Bruce Dickinson", :valor => 100 ) pedido.save end

Recuperando registros Existentes As classes de modelo possuem um mtodo find, o qual aceita alguns valores como parmetro. Passando uma chave primria como parmetro, o mtodo retorna um objeto contendo dados do registro correspondente: class Teste pedido = Pedido.find(1); puts pedido.nome end Caso vrios valores de chaves primrias sejam fornecidos, o mtodo retorna um array de objetos: class Teste2 pedido = Pedido.find(1,2,3); pedido.each { |ped| puts ped.nome } end Muitas vezes necessrio ler registros de uma tabela baseando em um determinado critrio, ao invs de uma chave primria. Active Record prov uma srie de opes para melhorar suas consultas:

pedido = Pedido.find(:all) Retorna todos os registros da tabela de Pedidos:

pedido = Pedido.find(:all, :conditions => "nome = 'Kurt' and valor < 100") Retorna todos os clientes cujo nome seja Kurt e o valor menor que 100. cliente = params[:nome] pedido = Pedido.find(:all, :conditions => [nome = ? and valor < 100, cliente]) Armazena na varivel cliente, o nome recebido do formulrio web e passa como parmetro para o find. cliente = params[:nome] valor = params[:valor] pedido = Pedido.find(:all, :conditions => [nome = :cliente and valor < :valor, {:cliente => cliente, :valor => valor}]) Parecido com o exemplo acima, entretanto est recebendo como parmetro tambm o valor. pedido = Pedido.find(:all, :conditions => "nome = 'Raul Seixas' ", :order => "valor DESC", :limit => 3) Neste caso, estamos ordenando o resultado de acordo com o valor em ordem decrescente e limitando a consulta aos trs primeiros registros. H ainda o mtodo find_by_sql, o qual permite especificar o sql que ser usado pela consulta. Os atributos desse modelo sero as colunas retornadas pela consulta. Cuidado, caso a consulta no retorne o campo chave primria, no ser possvel a atualizao do registro. pedido = Pedido.find_by_sql("select id,nome from pedidos order by nome"); pedido.each { |ped| puts ped.nome } Contando Registros Existem, basicamente, dois mtodos para contar registros: o count e o count_by_sql. O mtodo count retorna a quantidade de registros que satisfazem um

determinado critrio ou todos os registros, caso nenhum critrio seja passado. O mtodo count_by_sql retorna um gerado por uma sentena SQL (geralmente um select count(*) from...). quant_total = Pedido.count quant = Pedido.count(["nome = ?","Marvin Lemos"]) puts "Quantidade Total: #{quant_total}" puts "Quantidade do Cliente Marvin Lemos: #{quant}"

Outro recurso bem interessante do Active Record a possibilidade de usar variantes do find para consultar registros por uma coluna especifica: find_by_nome_do_atributo: Pedido = Pedido.find_by_nome(Ozzy) Pedido = Pedido.find_all_by_nome(Lois)

Apagando Registros Para remover registros, podemos usar o mtodo delete: Pedido.delete(3) Pedido.delete([1,2,3]) Pedido.delete_all("valor < 20")

Relacionamentos entre tabelas << COMPLETAR COM LIVRO >>

Validaes Active Record pode validar o contedo de objetos de modelo. Uma das formas de realizar validaes implementar os mtodos validate(), validate_on_create() e validate_on_update(). O validate() invocado em qualquer operao de gravao. Os outros dois, dependem do modo da gravao, criao ou atualizao respectivamente. Por exemplo, o cdigo abaixo garante que o nome no esteja em branco e o valor seja maior que zero. def validate unless nome errors.add(:nome, "Nome em branco ou invlido") end end

def validate_on_create if self.valor <= 0 errors.add(:valor,"Valor menor ou igual a zero") end end Quando o mtodo validate() encontra um problema, adiciona uma mensagem lista de erros para este objeto usando erros.add(). O primeiro parmetro o nome do atributo validado e o segundo a mensagem de erro. Essa lista de erros, pode, depois, ser mostrada na viso do usurio. Helpers de Validao Para algumas validaes mais comuns, o Active Record j definiu alguns helpers que ajudaro bastante. Segue, abaixo, uma lista de alguns helpers de validao que voc pode utilizar: 1 validates_acceptance_of Verifica se um campo do tipo caixa de marcao foi marcado. validates_acceptance_of :termo, :message => Voc deve aceitar o termo para continuar! 2 validates_confirmation_of Verifica se dois campos possuem o mesmo valor. Muito til para confirmao de senha, e-mails, etc. Se voc usar a conveno de nomes o segundo atributo deve possuir o nome do primeiro concatenado com _confirmation. O segundo campo no precisa ser gravado em banco. Por exemplo, a viso pode conter: <%= password_field user, password %><br /> <%= password_field user, password_confirmation %><br /> No modelo Usurio: class Usurio < ActiveRecord::Base validates_confirmation_of :password end 3 validates_format_of 4 validates_length_of 5 validates_numericality_of

6 validates_presence_of 7 validates_uniqueness_of

ACTION CONTROLLER Quando uma requisio chega, por http://www.marvinlemos.net/usuario/list, Rails faz o seguinte: exemplo, para

1. Carrega o arquivo usuario_controller.rb do diretrio app/controllers. (Obs: essa carga ocorre apenas uma vez em um ambiente de produo). 2. Instancia um objeto da classe UsuarioController. 3. Procura no diretrio app/helpers por um arquivo chamado usurio_helper.rb. Caso encontre, carrega-o e o mdulo UsuarioHelper adicionado ao objeto controller. 4. Procura no diretrio app/models por um arquivo de modelo chamado usuario.rb e carrega-o se encontrado. O Bsico Basicamente, uma aplicao web aceita requisies de um navegador, processa essa requisio e envia uma resposta. Mas como a aplicao sabe o que fazer com a requisio? Rails codifica essa informao na URL de requisio e usa um subsistema chamado routing para determinar o que deve ser feito com o pedido. O processo, na verdade, bastante simples, por exemplo um pedido para nossa aplicao pode ser algo parecido com http://www.marvinlemos.net/usuario/excluir/16. Essa URL interpretado pela aplicao como um pedido para invocar o mtodo excluir() da classe UsuarioController, pedido esse para excluir o usurio 16. Mtodos de Ao Quando um objeto de controle processa um pedido, ele procura por um mtodo publico da instncia com o mesmo nome da ao de entrada. Encontrando algum, o mtodo invocado, se no, mas se o controlador implementar o mtodo o method_missing(), esse mtodo chamado, passando como parmetro o nome da action que no foi encontrado. Caso nenhum mtodo possa ser chamado, o controlador procura por um template depois do controlador e ao atual(???). Caso nenhuma dessas coisas aconteam, um erro de Unknown Action gerado. Por padro, qualquer mtodo publico em um controlador pode ser invocado como um mtodo de ao. Voc pode prevenir que um mtodo particular seja acessvel como ao definindo-o como protected, privaye ou usando hide_action().

class MatriculaController < ApplicationController def index redirect_to(:controller => "turma", :action => "index") end hide_action :checar_media def checar_media redirect_to(:controller => "turma", :action => "index") end end

Controllers de Ambiente O controlador estabelece o ambiente para a ao (e, por extenso, para as vises que so chamadas). O ambiente definido em variveis de instncia, mas deve-se usar o mtodos acessores correspondente no controlador. request O objeto de requisio de entrada. Atributos teis desse objeto so: o domain(), o qual retorna os dois ltimos componentes do domnio do qual partiu a requisio. o remote_ip(), o qual retorna o endereo ip remoto como uma string. o env, o ambiente da requisio. Voc pode usa-lo para acessar valores criados pelo navegador, como: ! request.env[http_ACCEPT_LANGUAGE] o method, retorna o mtodo da requisio (:delete, :get, :head, :post ou :put). o delete?, get?, head? e put?, retornam verdadeiro ou falso baseado no mtodo da requisio. class TesteController < ApplicationController def teste @ip_remoto = request.remote_ip @host_remoto = request.domain end end Obs: Para mais detalhes, ActionController::AbstractRequest consulte o RDoc do

params Um objeto hash contendo todos os parmetros da requisio. cookies Os cookies associados com a requisio.

response O objeto de resposta, preenchido durante o processamento da requisio. Normalmente esse objeto gerenciado pelo Rails para voc. session Um objeto hash contendo dados da sesso atual. headers Um hash dos cabealhos http que sero usados na resposta. Respondendo ao Usurio Parte do trabalho do controlador responder ao usurio. H, basicamente, trs modos de fazer isso: 1. O modo mais comum oferecer um template. 2. O controlador pode retornar uma string diretamente para o navegador sem invocar a viso. 3. O controlador pode enviar outros tipos de dados para o cliente. Tipicamente um download (PDF, programas, etc.). Templates Um template um arquivo que define o contedo de uma resposta da aplicao. Rails suporta dois formatos de templates: rhtml o qual HTML embutido com cdigo Ruby, e builder, um modo mais pragmtico de construir contedos. Por conveno, os templates para uma ao action de controlador control ser o arquivo app/views/control/action.rhtml ou app/views/control/action.rxml (a extenso .rxml indica o formato de template builder). O mtodo render() o corao de todas as rendenrizaes no Rails. render(:text => string) Envia uma string para o cliente. class TesteController < ApplicationController def index render(:text => "Ola Mundo") end end render(:inline => string, [:type => rhtml|rxml]) Interpreta string como a origem do template, renderizando o resultado de volta para o cliente.

class TesteController < ApplicationController if RAILS_ENV == "development" def method_missing(name, *args) render(:inline => %{<h2>Unknown action: #{name}</h2> Here are the request parameters:<br/> <%= debug(params) %> }) end end end

render(:action => action_name) Renderiza o template para uma ao do controlador

render(:file =>path, [ :use_full_path =>true|false] ) render(:template =>name) render(:partial =>name, ...) Renderiza um template parcialmente render(:nothing => true) Envia um corpo vazio para o navegador

Enviando Arquivos e Outros Dados send_data Envia uma string contendo dados binrios para o cliente. O cliente ira fazer uma combinao do tipo de contedo(type content) e a disposio para determinar que tipo de arquivo consiste esses dados. send_data(data, options...) def grafico_vendas png_data = Sales.plot_for(Date.today.month) send_data(png_data, :type => "image/png", :disposition => "inline") end Opes :filename string uma sugesto de nome para o navegador usar quando for salvar o arquivo. :type - string O tipo do contedo (padro application/octet-stream arquivo binrio)

:disposition string Diz para o navegador se o arquivo deve ser mostrado inline (opo inline) ou se deve ser salvo no computador do cliente (opo attachment, o padro)

send_file Envia o contedo de um arquivo para o cliente. send_file(path, options...) Opes :filename string - uma sugesto de nome para o navegador usar quando for salvar o arquivo. Caso no seja usado, o padro para o nome do arquivo o path. :type string - O tipo do contedo (padro application/octetstream arquivo binrio) :disposition string - Diz para o navegador se o arquivo deve ser mostrado inline (opo inline) ou se deve ser salvo no computador do cliente (opo attachment, o padro). :streaming - true ou false - Se false, o arquivo inteiro carrgeado na memria e enviado para o cliente, caso contrrio, o arquivo buferizado e enviado para o cliente. def send_secret_file send_file("/files/secret_list") headers["Content-Description"] = "Top secret" end Redirects Um redirecionamento http enviado do servidor para o cliente em resposta a um pedido. A resposta de redirecionamento inclui a URL que o cliente dever seguir. redirect_to (options...) Redireciona para uma ao

redirect_to (path) Redireciona para um caminho fixo na aplicao. def save order = Order.new(params[:order]) if order.save redirect_to :action => "display" else session[:error_count] ||= 0 session[:error_count] += 1 if session[:error_count] < 4 flash[:notice] = "Please try again" else redirect_to("/help/order_entry.html") end end end

redirect_to (url) Redireciona para uma URL. def portal_link link = Links.find(params[:id]) redirect_to(link.url) end

ACTION VIEW Quando voc escreve uma viso, voc esta escrevendo um template: alguma coisa que ser exibida no navegador do cliente. Para entender como esses templates funcionam preciso verificar trs situaes: ! Onde os templates ficam; ! O ambiente em que eles so executados ! O que fica dentro deles. Onde os templates ficam O mtodo render() espera encontrar os templates no diretrio especificado na opo de configurao global template_root. Por padro, esse diretrio o app/views da aplicao corrente. O ambiente do Template Os templates possuem uma mistura de texto fixo e cdigo. O cdigo usado para adicionar cdigo dinmico ao contedo do template. Esse cdigo executado em uma ambiente que d acesso s informaes estabelecidas pelo controlador. ! Todas as variveis de instncia do controlador tambm esto disponveis no template. desta forma que as aes comunicam-se com os templates. Os objetos do controlador (headers, params, request, response e session) tambm esto disponveis na viso via mtodos assessores. O objeto controlador corrente acessvel usando um atributo chamado controller. Isto permite o template chamar qualquer mtodo publico do controlador (incluindo os mtodos no ActionController). O caminho para o diretrio base dos templates disponibilizado no atributo base_path.

! !

Contedo dos Templates Rails suporta dois modos de templates ! ! Templates rxml usam a biblioteca Builder para construir resposta XML (No abordaremos templates rxml nesta apostila). Templates rhtml consistem em uma mistura de HTML embutido com cdigo Ruby. Consiste na forma mais usada de gera pginas HTML.

Template RHTML Um arquivo HTML regular. Se o template no contm contedos dinmicos, ele simplesmente enviado para o navegador do usurio. Por exemplo:

<h1> Template de Teste </h1> <p> Ol mundo !!! </p> Vejamos agora um exemplo que usa contedo dinmico: <h1> Template de Teste </h1> <p> Hora atual: <%= Time.now %> </p> Qualquer cdigo compreendido entre <%= e %> avaliado, o resultado convertido para string usando o to_s, e essa string substituda na pgina resultante. Obs: Cuidado para no incluir lgica de negcio dentro dos templates. Algumas vezes necessrio cdigo em um template que no gere nenhuma sada, nesses casos, deve-se retirar o sinal de =. <% 3.times do %> Ho ! <br/> <% end %> Desconsiderando Valores Substitudos H um aspecto muito importante a ser considerado quando se usa templates. Quando voc insere um valor usando <%= ... %>, ele vai diretamente para o fluxo de sada. Por exemplo: O valor do nome <%= params[:nome] %> No curso normal, este cdigo ser substitudo pelo parmetro nome. Mas e se o usurio digitar a seguinte URL: http://x.y.com/myapp?name=Hello%20%3cb%3ethere%3c/b%3e A seqncia estranha %20%3cb%3ethere%3c/b%3e uma verso codificada da URL que representa o HTML <b>there</b>. Nosso template ir substituir isto na pgina, e o resultado ser mostrado na tela do navegado do cliente com o there em negrito. Com isso, percebe-se que a aplicao tem uma falha de segurana que deixa seu site vulnervel a ataques e a perca de dados. A soluo, contudo, simples. Sempre desconsidere qualquer texto que voc substitui nos templates que representam HTML. Templates rhtml vem com o mtodo html_espace(), carinhosamente apelidado de h(), que realiza esse processo automaticamente. O valor do nome : <%= h(params[:nome]) %>

Helpers Um helper consiste basicamente de um mdulo que contm mtodos para auxiliar uma viso. Por padro, cada controlador possui seu prprio mdulo de helper. Se um controlador chamado de LojaController, ele automaticamente ir procurar por um helper chamado loja_helper.rb dentro do diretrio app/helpers. loja_helper.rb: module LojaHelper def fmt_real(amt) sprintf("R$ %0.2f", amt) end end No arquivo de viso: <td align="right"><%= fmt_real(item.preco) %></td>

Compartilhando Helpers Quando for preciso que um helper seja compartilhando entre todos os outros controladores, pode-se adicionar o cdigo no arquivo application_helper.rb dentro de app/helpers. Ou ento, pode-se informar diretamente para um controlador especifico para incluir mdulos helpers, usando a declarao helper. Por exemplo, se o helper para formatao de data est no arquivo date_format_helper.rb em app/helpers e queremos que TesteController o use: class TesteController < ApplicationController helper :date_format # ....

Você também pode gostar