2. partials – reorganizando as views
} Quando uma view cresce demais ou tem pedaços que
podem ser reutilizados em outros lugares, esses pedaços
podem ser transformados em um “partial”;
} Partials são arquivos de view onde o nome começa com
um “_” (underline) como em “_items.html.erb”;
} É possível passar variáveis para partials e eles também
tem acesso a todas as variáveis de instância que estejam
nas views;
2
4. Fazendo a chamada do partial – app/views/
itens/index.html.erb
<table>
<thead>
<tr>
<td> Nome </td>
<td> Quantidade </td>
<td> Preço unitário </td>
<td> Preço total </td>
</tr>
</thead>
<tbody>
<% pedido_atual.itens.each do |item| %>
<%= render 'item', :item => item %>
<% end %>
</tbody>
</table>
4
5. <%= render ‘ item‘, :item => item %>
} Gere o partial “_item” dentro da pasta da view atual;
} Passe a variável item com o nome “:item” para o partial;
} Quando o caminho completo não for dado, o partial vai
ser sempre procurado na pasta de view do controller
atual;
} render :partial => “items/item”, :locals => { :item =>
item }
5
6. Alterando vários itens ao mesmo tempo no
carrinho – formulários aninhados
} A solução mais simples pra editar vários elementos ao
mesmo tempo em uma página;
} Rails tem suporte direto a formulários aninhados dentro
do framework;
} Para usar formulários aninhados (para os objetos
relacionados a outro) nós utilizamos o helper
“accepts_nested_attributes_for”;
6
7. Na classe Pedido
class Pedido < ActiveRecord::Base
has_many :itens
accepts_nested_attributes_for :itens
#resto dos métodos
end
7
8. Formulário no carrinho
<%= form_tag atualizar_carrinho_itens_path, :method => :post do %>
<table>
<thead>
<tr>
<td> Nome </td>
<td> Quantidade </td>
<td> Preço unitário </td>
<td> Preço total </td>
</tr>
</thead>
<tbody>
<% pedido_atual.itens.each do |item| %>
<%= render 'item', :item => item %>
<% end %>
</tbody>
</table>
<% end %>
8
10. Formato do formulário para itens aninhados
} 'pedido[itens_attributes][][id]’
} Cada item deve vir com o seu próprio id, para que o item
correto seja atualizado;
} 'pedido[itens_attributes][][quantidade]’
} Junto do ID vem o atributo (ou os atributos) que você deseja
alterar;
} Veja que entre o [itens_attributes] e [id] existe um “[]”,
esse par de colchetes serve pra definir que você está
alterando um array de itens;
10
11. Formato do formulário que está sendo
enviado para o controller
} "pedido"=>
} {"itens_attributes"=>[
} {"id"=>"1","quantidade"=>"5"},
} {"id"=>"2","quantidade"=>"4"},
} {"id"=>"3","quantidade"=>"8”}
} ]}
11
12. Adicionando uma nova rota
} map.resources :itens, :collection => { :atualizar_carrinho
=> :post }
} Ações do tipo “collection” são definidas quando você vai
chamar um método do controller que não afeta um
recurso em específico, a URL gerada seria “/itens/
atualizar_carrinho”;
} A outra opção seria usar ações do tipo “member”, que
afetam recursos específicos, a URL gerada seria “/itens/
nome_da_acao/1”
12
13. Implementando o controller
def atualizar_carrinho
pedido_atual.update_attributes( params[:pedido] )
respond_to do |format|
format.html do
flash[:success] = 'Carrinho de compras atualizado com
sucesso'
redirect_to itens_path
end
end
end
13
14. Removendo itens que tenham a quantidade
menor do que 1
} O ActiveRecord define vários hooks (“ganchos”) onde
você pode executar código durante o ciclo de vida de um
objeto:
} before_validation
} before_validation_on_create
} validate_on_create
} after_validation
} after_validation_on_create
} before_save
} before_create
} after_create
} after_save
14
15. Ganchos do ActiveRecord
} Esses ganchos podem ser utilizados para executar código
nas suas classes, como fazer cache de valores, preencher
os objetos com dados padrão;
} Você pode definir vários métodos para um mesmo
gancho, todos eles são executados na sequência em que
foram definidos;
} Quando um método retorna “false”, ele pára a execução
de todos os ganchos que vem após ele;
15
16. Implementando um gancho
class Pedido < ActiveRecord::Base
has_many :itens, :dependent => :destroy
accepts_nested_attributes_for :itens
after_save :remover_itens_zerados
# resto do código
protected
def remover_itens_zerados
zerados = self.itens.find_all { |item| item.quantidade.to_i < 1 }
self.itens.delete( *zerados )
end
end
16
17. self.itens.delete( *args ) – métodos
encontrados em associações
} itens.delete – remove objetos da associação (apagando ele do
banco se o :dependent for :destroy ou :delete_all)
} itens(true) – atualiza a coleção pra os valores mais atuais no
banco
} itens.item_ids – traz os ids dos objetos associados
} itens.find – faz uma consulta no banco apenas nos objetos que
fazem parte da associação
} Outros métodos em ->
http://api.rubyonrails.org/classes/ActiveRecord/Associations/
ClassMethods.html#method-i-has_many
17
18. Montando a administração do site
} Vive em um caminho/namespace separado do site
principal (como em “/admin”);
} Normalmente precisa de um controle de acesso mais
complexo, liberando apenas para usuários que sejam
administradores;
} Também precisa de um layout específico pra ela, em vez
de usar o layout comum do site;
18
19. Criando o controller base da administraçào
class Admin::BaseController < ApplicationController
layout 'administracao'
end
19
20. Pastas
} O arquivo do controller deve ser base_controller.rb e
deve ser criado dentro da pasta “app/controllers/admin”;
} Quando um controller tem um namespace
( Admin::BaseController ), a pasta onde ele fica deve
representar o mesmo caminho do namespace dele;
} O nome final do arquivo é o nome do controller SEM o
namespace;
20
21. Criando o controller base da administração
} No controller fazemos a uma chamada ao método
“layout” e passamos como parâmetro a string
“administracao”;
} Isso define que o controller Admin::BaseController não
vai mais utilizar o layout “application.html.erb” mas sim o
“administracao.html.erb”;
} Cada controller pode definir o seu próprio layout base
com a chamada do método “layout”;
21
23. rake routes | grep admin
} admin_produtos GET /admin/produtos
} POST /admin/produtos
} new_admin_produto GET /admin/produtos/new
} edit_admin_produto GET /admin/produtos/:id/edit
} admin_produto GET admin/produtos/:id
} PUT /admin/produtos/:id
} DELETE /admin/produtos/:id
23
24. Instalando a gem de paginação
} Instalando a gem
} gem install will_paginate
} Configurando ela no environment.rb
} config.gem “will_paginate”
24
25. Adicionando métodos de paginação em
ApplicationController
class ApplicationController < ActionController::Base
#outros métodos protected
def load_page
@page = params[:page] || 1
@per_page = params[:per_page] || 10
end
def paginate( scope, options = {} )
load_page
scope.paginate(
:page => @page,
:per_page => @per_page )
end
end
25
26. Métodos básicos - admin/
produtos_controller.rb
class Admin::ProdutosController < Admin::BaseController
before_filter :load_produto, :only => [ :new, :edit, :create, :update, :destroy ]
#mais código aqui
protected
def load_produto
@produto = params[:id].blank? ? Produto.new : Produto.find( params[:id] )
end
def ir_para_listagem( mensagem )
respond_to do |format|
format.html do
flash[:success] = mensagem
redirect_to admin_produtos_path
end
end
end
end
26
27. before_filter
} Define métodos a serem executados antes da ação do
controller ser executada;
} Normalmente são utilizados para transformar dados da
sessão em variáveis para o controller ou validar se a
requisição é válida ou não;
} Podem ser aplicados a todos os métodos de um
controller ou apenas a alguns com :only e :except;
27
28. Criando e editando produtos – admin/
produtos_controller.rb
def new
respond_to do |format|
format.html do
render :new
end
end
end
alias :edit :new
def create
if @produto.update_attributes( params[:produto] )
ir_para_listagem( 'Produto criado/atualizado com sucesso' )
else
new
end
end
alias :update :create
28
29. alias
} alias :edit :new
} Crie um método “edit” que aponte para o método “new”
} Simplifica a definição de controllers, já que os métodos
“new” e “edit” fazem a mesma coisa;
} O funcionamento é o mesmo para “create” e “update”, os
métodos fazem a mesma coisa, então definir um alias é
mais prático;
29
30. Listando e removendo produtos – admin/
produtos_controller.rb
def index
@produtos = paginate( Produto )
respond_to do |format|
format.html
end
end
def destroy
@produto.destroy
ir_para_listagem( 'Produto removido com sucesso' )
end
30
31. Preparando a listagem de produtos da
administração – admin/produtos/index
<% unless @produtos.blank? %>
<%= will_paginate @produtos %>
<table>
<%= cabecalho_de_tabela 'Nome', 'Preço', 'Ações' %>
<tbody>
<%= render @produtos %> </tbody>
</table>
<%= will_paginate @produtos %>
<% else %>
<p> Não há produtos cadastrados no sistema. </p>
<% end %>
31
32. Definindo um método em
application_helper.rb
module ApplicationHelper
def cabecalho_de_tabela( *nomes )
colunas = nomes.map { |nome| "<th>#{nome}</th>" }
linha = content_tag( :tr, colunas.join("n").html_safe )
content_tag( :thead, linha ).html_safe
end
end
32
33. Definindo um método em
application_helper.rb
} Métodos definidos em quaisquer arquivos dentro da pasta
helper estão disponíveis por padrão em todas as views da
sua aplicação;
} Você deve agrupar os métodos em helpers dependendo
da funcionalidade que implementam ou modelo sobre o
qual interagem;
} Não defina todos os seus métodos em
application_helper.rb, crie outros arquivos de helper para
organizar o seu código J
33
34. <%= will_paginate @produtos %>
} O método de views will_paginate vai gerar na sua view
um controle de paginação com os números de páginas e
os botões anterior e próximo;
} O parâmetro que ele recebe é uma coleção que tenha
sido retornada através de um método paginate em um
dos seus models, uma coleção normal não vai ser aceita;
} Você pode personalizar as mensagens ou o HTML gerado
através de opções passadas para esse método;
34
35. <%= render @produtos %>
} Atalho para gerar uma lista de objetos que compartilham
o mesmo partial;
No caso do exemplo, é equivalente a fazer:
}
<% @produtos.each do |produto| %>
<%= render “admin/produtos/produto”, :produto =>
produto %>
<% end %>
} O caminho do partial a ser buscado vai ser sempre
“caminho_atual/nome_da_classe_no_singular”
35
36. Linha de produto – admin/produtos/
_produto.html.erb
<tr>
<td> <%= produto.nome %> </td>
<td> <%= number_to_currency produto.preco %> </td>
<td>
<%= link_to 'Editar', edit_admin_produto_path(produto) %> |
<%= link_to 'Remover’, admin_produto_path(produto),
:method => :delete,
:confirm => 'Tem certeza de que deseja remover este
produto?' %>
</td>
</tr>
36
37. link_to - :method => :delete
} Links em HTML geram, por padrão, uma requisição do
tipo GET, mas no nosso caso precisamos que um link gere
uma requisição do tipo :delete;
} O Rails oferece uma opção chamada :method para links
para que eles possam executar chamadas a outros
métodos HTTP;
} Além disso também é possível usar a opção :confirm para
validar se o usuário quer realmente efetuar aquela ação;
37
38. Criando um helper para simplificar o
formulário de produtos
module Admin::ProdutosHelper
def admin_form_for_produto( &block )
opcoes = if @produto.new_record?
[admin_produtos_path, :post]
else
[admin_produto_path( @produto ), :put]
end
form_for( @produto,
:url => opcoes.first,
:html => { :method => opcoes.last }, &block )
end
end
38
39. RESTful forms
} Formulários em Rails seguem a padronização RESTful que
as rotas para os métodos definem;
} Para criar um novo produto, é necessário um POST em “/
admin/produtos”, para editar um produto é necessário
um PUT em “/admin/produtos/ID”;
} O método admin_form_for_produto usa o método
new_record? de Produto para saber se ele está criando
um novo ou editando um produto existente;
39
41. Implementando o helper para mostrar os
erros
module ApplicationHelper
def error_messages_for( object, title = 'Foram encontrados
erros nos seus dados' )
if object
render 'compartilhados/erros',
:title => title,
:errors => object.errors.full_messages
end
end
end
41
42. E o HTML – app/views/compartilhados/
_erros.html.erb
<div class="alert-message block-message error">
<a class="close" href="#">×</a>
<p><strong> <%= title %> </strong></p>
<ul>
<% errors.each do |error| %>
<li><%= error %></li>
<% end %>
</ul>
</div>
42