Curso de Ruby on Rails - Aula 04

2,910 views
2,805 views

Published on

Material do curso de Ruby on Rails.

Published in: Technology
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
2,910
On SlideShare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
90
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

Curso de Ruby on Rails - Aula 04

  1. 1. Aprendendo Ruby on Rails – Aula 4 Maurício Linhares
  2. 2. Mas o design do meu formulário não está igual ao do Bootstrap! Form Builders to the rescue!
  3. 3. Ao trabalhar com formulários em Rails, usesempre form-builders}  Forma padronizada de lidar com formulários no Rails;}  Faz com que você organize o markup/html/css da sua aplicação de forma mais limpa;}  Oficializa o reuso de formulários HTML por todo o site, além de garantir que os estilos são os mesmos em todos os lugares;
  4. 4. Como implementar um form-builder?}  O que você quer fazer é implementar um Decorator que vai cobrir o form builder padrão do Rails;}  Reuse as chamadas que já existem e adicione ou empacote o resultado do form builder padrão com os seus estilos personalizados;}  O Bootstrap do twitter foi feito sob medida pra ser utilizado com form builders do Rails;
  5. 5. Iniciando a implementação – app/helpers/bootstrap_form_builder.rbclass BootstrapFormBuilder < ActionView::Helpers::FormBuilder def get_error_text(field) self.object && self.object.errors[field].first end def content_tag(*args) @template.content_tag(*args).html_safe end def render_label(field, title) label(title.blank? ? self.object.class.human_attribute_name(field) : title) endend
  6. 6. Começando a magia – define_methodclass BootstrapFormBuilder < ActionView::Helpers::FormBuilderbasic_helpers = %w{text_field text_area select password_field file_field} basic_helpers.each do |name| define_method(name) do |field, *args| options = args.last.is_a?(Hash) ? args.last : {} label = render_label(field, options[:label]) error_text = get_error_text(field) wrap_field(label, super(field, *args), error_text) end endend
  7. 7. Como assim define_method?}  Define um método em tempo de execução dentro de um objeto (a técnica é comumente conhecida como metaprogramação);}  No nosso caso, que é a implementação de um Decorator, é a solução ideal, pois provê uma forma genérica de adicionar a mesma funcionalidade aos vários métodos que precisam dela, sem repetição;
  8. 8. Implementando wrap_fielddef wrap_field(label, content, error_text) wrapper_class = [clearfix] unless error_text.blank? wrapper_class << error end result = [content] unless error_text.blank? result << content_tag(:span, error_text, :class => help-inline) end result = result.join( ).html_safe @template.content_tag(:div, label + content_tag(:div, result, :class => input), :class =>wrapper_class.join( )) end
  9. 9. Implementando para campos que tem váriositens de formuláriomultipart_helpers = %w{date_select datetime_select}multipart_helpers.each do |name| define_method(name) do |field, *args| options = args.last.is_a?(Hash) ? args.last : {} label = render_label(field, options[:label]) error_text = get_error_text(object, field) wrap_multipart_field(label, super(field, *args), error_text) endend
  10. 10. Empacotando campos com vários itensdef wrap_multipart_field(label, content, error_text) wrapper_class = [clearfix] unless error_text.blank? wrapper_class << error end result = [content] unless error_text.blank? result << content_tag(:span, error_text, :class => help-inline) end result = content_tag(:div, result.join( ).html_safe, :class => inline-inputs) @template.content_tag(:div, label + content_tag(:div, result, :class => input), :class =>wrapper_class.join( )) end
  11. 11. Detalhe especial}  Ao usar human_attribute_name o seu objeto precisa ter a tradução dos atributos definida, como em:“pt-BR”:! activerecord:! models:! produto:! one: Produto! other: Produtos! attributes:! produto:! preco: Preço! nome: Nome! descricao: Descrição!
  12. 12. Alterando o formulário de produtos pra usaro novo Form Builder<%= admin_form_for_produto do |f| %> <fieldset> <legend> Criar/Editar Produto </legend> <%= error_messages_for @produto %> <%= f.text_field :nome %> <%= f.text_field :preco %> <%= f.text_area :descricao %> <div class="actions"> <%= submit_tag Enviar, :class => btn primary %> </div> </fieldset><% end %>
  13. 13. Alterando o método que abre o formulário –app/helpers/admin/produtos_helper.rbmodule 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 }, :builder => BootstrapFormBuilder, &block ) endend
  14. 14. Criando os usuários}  Os usuários da aplicação englobam tanto as pessoas que vão vir fazer compras no site como também os administradores que vão alimentar a administração;}  Os usuários serão cadastrados através de suas contas de email e serão marcados como usuários comuns ou administradores;
  15. 15. Migração da tabela de usuários -CriarUsuariosdef self.up create_table :usuarios do |t| t.string :email, :null => false t.boolean :administrador, :default => false, :null => false t.string :nome, :null => false t.string :salt, :null => false t.string :senha_em_hash, :null => false t.datetime :ultimo_acesso_em t.timestamps end add_index :usuarios, :email, :unique => true add_index :usuarios, :ultimo_acesso_em add_column :pedidos, :usuario_id, :integer, :null => true add_index :pedidos, :usuario_idenddef self.down drop_table :usuarios remove_column :pedidos, :usuario_idend
  16. 16. Criando a classe Usuario – validações ecampos virtuaisclass Usuario < ActiveRecord::Base attr_accessor :senha, :termos_e_condicoes validates_presence_of :nome, :email validates_acceptance_of :termos_e_condicoes, :if => :new_record? validates_presence_of :senha_em_hash, :if => :senha_necessaria? validates_confirmation_of :senha, :if => :senha_necessaria? validates_length_of :senha, :within => 4..40, :if => :senha_necessaria?end
  17. 17. Senhas e segurança}  Por motivos de segurança, você nunca deve gravar uma senha de um dos seus usuários no banco de dados em um formato onde os dados possam ser reconstruídos;}  Senhas devem sempre ser gravadas em na forma de hashes, onde não é possível obter a senha novamente, apenas através de força bruta;}  Não criptografe senhas, não as guarde em formatos que possam ser descobertos;
  18. 18. Definindo o controle de senhas do usuárioclass Usuario < ActiveRecord::Base before_validation :hashear_senha def senha_necessaria? self.senha_em_hash.blank? || !self.senha.blank? end def senha_correta?( _senha ) self.senha_em_hash == Usuario.hashear( _senha, self.salt ) end protected def hashear_senha return true if self.senha.blank? self.salt = Digest::SHA1.hexdigest("--#{Time.now.to_s(:db)}--#{self.email}--#{self.nome}") if self.new_record? self.senha_em_hash = Usuario.hashear(self.senha, self.salt) endend
  19. 19. Métodos de classe complementaresclass Usuario < ActiveRecord::Baseclass << self def hashear( senha, salt ) Digest::SHA1.hexdigest("--#{salt}--#{senha}--") end def autenticar( email, senha ) usuario = Usuario.first( :conditions => { :email => email } ) if usuario && usuario.senha_correta?( senha ) usuario else nil end end endend
  20. 20. Usuários e Senhas}  Os campos senha e senha_confirmation são ambos campos virtuais, eles existem apenas para ajudar a geração do hash da senha;}  A senha nunca é gravada em lugar nenhum como o usuário a digitou, ela será sempre gravada como hash;}  Ao fazer o login do usuário, nós recebemos o email e a senha digitada, mas transformamos ela no hash para poder fazer a comparação no método “autenticar”;
  21. 21. Preparando o applicaction_controller.rb parao controle de usuáriosdef usuario_atual if @usuario_atual != false @usuario_atual = login_pela_sessao end @usuario_atualenddef usuario_atual=( usuario ) @usuario_atual = usuario session[:usuario_id] = usuario.idenddef login_pela_sessao resultado = !session[:usuario_id].blank? && Usuario.find_by_id( session[:usuario_id] ) resultado ? resultado : falseend
  22. 22. Definindo o controle geral}  O usuário que está atualmente logado na aplicação está acessível pelo método “usuario_atual”;}  O método avalia se há um usuário na sessão, na chave :usuario_id e se não houver ele retorna “false”, para que o código saiba que não há um usuário logado no momento;}  Se a variável @usuario_atual já estiver definida como “false”, ele não repete o teste, pois sabe que não há usuário logado no momento;
  23. 23. Preparando o application_controller.rb parao controle de acesso de usuários helper_method :pedido_atual, :usuario_atual, :logged_in?, : administrador? def logged_in? self.usuario_atual end def administrador? logged_in? && self.usuario_atual.administrador? end
  24. 24. Filtros para controle de acessodef login_necessario unless self.logged_in? respond_to do |format| format.html do flash[:erro] = Você precisa estar logado para acessar esta página redirect_to sessao_path end end else true endend
  25. 25. Filtros para controle de acessodef administrador_necessario unless self.administrador? respond_to do |format| format.html do flash[:erro] = Você precisa ser um administrador para visualizar esta página redirect_to sessao_path end end else true endend
  26. 26. Filtros para controle de acesso}  Os métodos login_necessario e administrador_necessario devem ser utilizados como “before_filter” em ações que requerem que o usuário esteja logado ou que seja um administrador;}  O primeiro caso do uso é para os controllers da administração do site, apenas administradores devem ter acesso aquelas páginas;}  Como o código causa um redirect se o usuário não for o que se espera, a execução da requisição pára no filtro;
  27. 27. Bloqueio de acesso apenas paraadministradoresclass Admin::BaseController < ApplicationController layout administracao before_filter :administrador_necessarioend
  28. 28. Criando as contas de usuários –usuarios_controller.rbclass UsuariosController < ApplicationController before_filter :login_necessario, :only => [ :edit, :update ] before_filter :load_usuario def new respond_to do |format| format.html { render :new } end end alias :edit :new alias :show :new protected def load_usuario @usuario = logged_in? ? self.usuario_atual : Usuario.new endend
  29. 29. Criando contas de usuários –usuarios_controller.rbclass UsuariosController < ApplicationController def create @usuario.attributes = params[:usuario] if @usuario.save self.usuario_atual = @usuario unless logged_in? respond_to do |format| format.html do flash[:notice] = Dados recebidos com sucesso redirect_to produtos_path end end else self.new end endend
  30. 30. Criando contas de usuários}  Apenas usuários que estejam logados podem chamar as ações edit, já que há um filtro que faz o bloqueio das chamadas;}  Se o usuário estiver sendo criado neste momento, ele é automaticamente logado no sistema, através da chamada: }  self.usuario_atual = @usuario}  Se os dados não forem válidos, ele é retornado para a página do formulário;
  31. 31. Formulário de criação/edição de usuários<%= form_for @usuario, :url => usuario_path, :html => { :method => :post} do |f| %> <%= f.error_messages %> <p> Nome: <br/> <%= f.text_field :nome %> </p> <p> Email: <br/> <%= f.text_field :email %> </p> <p> Senha: <br/> <%= f.password_field :senha %> </p> <p> Confirme a senha: <br/> <%= f.password_field :senha_confirmation %> </p> <% if @usuario.new_record? %> <p> <%= f.check_box :termos_e_condicoes %> Eu li e aceito os termos e condições </p> <% end %> <p> <%= submit_tag Enviar %> </p><% end %>
  32. 32. Formulário de criação de usuários}  O formulário não referencia os campos salt ou senha_em_hash, apenas os campos virtuais senha, senha_confirmation e termos_e_condicoes;}  Não é necessário gravar no banco de dados se o usuário aceitou ou não os termos e condições, se ele criou a conta, isso quer dizer que ele aceitou o campo;}  Se nós não estamos mais criando um novo usuário, não é necessário mostrar o campo :termos_e_condicoes;
  33. 33. Rotas do tipo resource}  Quando uma rota é definida como “resource” (no singular), significa que o recurso que ela representa é único (ou único para a aplicação ou único para o usuário atual);}  Exemplos disso são o usuário que está atualmente logado ou os dados da sua conta;}  Quando você define uma rota como “resource :usuario” as chamadas vão ser no singular, mas o nome do controller deve ser no plural, como em “UsuariosController”;
  34. 34. Criando rotas para usuários e sessõesresource :sessaoresource :usuario
  35. 35. Fazendo o login de usuários já existentes –sessoes_controller.rbclass SessoesController < ApplicationController def new respond_to do |format| format.html do render :new end end end alias :show :new #logoff def destroy reset_session respond_to do |format| format.html do flash[:aviso] = Você saiu da aplicação com sucesso redirect_to sessao_path end end endend
  36. 36. Fazendo o login de usuários já existentes def create @usuario = Usuario.autenticar( params[:email] , params[:senha]) if @usuario self.usuario_atual = @usuario respond_to do |format| format.html do flash[:aviso] = "Seja bem vindo a nossa loja, #{self.usuario_atual.nome}" redirect_to produtos_path end end else respond_to do |format| format.html do flash.now[:erro] = Não foi encontrado um usuário com o email e a senha que você forneceu new end end endend
  37. 37. Formulário de login de usuários – sessoes/new.html.erb<%= form_tag sessao_path do %> <p> Email: <br/> <%= text_field_tag :email, params[:email] %> </p> <p> Senha: <br/> <%= password_field_tag :senha %> </p> <p> <%= submit_tag Enviar %> </p><% end %>
  38. 38. Apagando as senhas até mesmo dos logs daaplicaçãoclass ApplicationController < ActionController::Base #remover estes parâmetros dos logs filter_parameter_logging :senha, :senha_confirmationend
  39. 39. Fazendo a união do carrinho de compras dousuário não logado com o usuário logadodef usuario_atual=( usuario ) @usuario_atual = usuario session[:usuario_id] = usuario.id usuario.create_pedido_atual unless usuario.pedido_atual unless self.pedido_atual.blank? usuario.pedido_atual.unir( self.pedido_atual ) self.pedido_atual.destroy end session[:pedido_id] = usuario.pedido_atual.idend
  40. 40. Unindo carrinhos de compra}  Se o usuário já estava adicionando itens ao carrinho antes de efetuar a compra, esses itens não podem ser perdidos;}  Assim que o usuário é logado dentro do sistema, o seu pedido atual é unido com o pedido atual do usuário, de forma que nenhum dos itens é perdido;
  41. 41. Template para criar conta ou login –compartilhados/_login_logout.html.erb<% if logged_in? %> <p> Olá <%= usuario_atual.nome %>, seja bem vindo! <br/> <%= link_to Clique aqui para atualizar os seus dados de usuário, usuario_path %> </p> <p> <%= link_to Logoff, sessao_path, :method => :delete, :confirm => Tem certeza de que deseja sair da aplicação? %> </p><% else %> <p> <%= link_to Já é usuário? Clique aqui para fazer login, sessao_path %> | <%= link_to Ainda não é usuário? Clique aqui para se cadastrar, usuario_path %> </p><% end %>
  42. 42. Enviando emails com Rails}  O Rails vem com um componente padrão para o envio de emails, o ActionMailer;}  Com ele, para enviar um email, você só precisa criar um objeto que herde de ActionMailer::Base e criar o template do texto do email a ser enviado;}  Os emails podem conter texto em quaisquer formato, assim como anexos;
  43. 43. Configurando o SMTP – config/initializers/email_configuration.rbActionMailer::Base.smtp_settings = { :enable_starttls_auto => true, :address => "smtp.gmail.com", :port => 587, :domain => "gmail.com", :authentication => :plain, :user_name => "linuxfi.ror.julho.2010@gmail.com", :password => "rubyonrails"}
  44. 44. Criando um diretório para guardar osmailers}  Crie uma pasta chamada mailers em “app”}  No seu environment.rb }  config.load_paths += [ "#{RAILS_ROOT}/app/mailers" ]
  45. 45. Criando uma classe base para os mailers –app/mailers/base_mailer.rbclass BaseMailer < ActionMailer::Basedefault_url_options[:host] = Rails.env.production? ? ’loja.com.br : localhost:3000 default :from => contato@loja.com.brend
  46. 46. Por que uma classe base?}  Adicionar métodos padrão para todos os mailers;}  Adicionar configurações padrão que vão valer para todos os mailers da aplicação;}  Facilitar a migração futura para uma fila de emails, como ar_mailer;
  47. 47. Implementação do mailer de usuarios –app/mailers/usuarios_mailer.rbclass UsuariosMailer < BaseMailer def registro( usuario ) @usuario = usuario mail( :to => usuario.email, :subject => ‘Seja bem vindo a loja virtual em Rails ‘) endend
  48. 48. Email de registro de usuários – app/views/usuarios_mailer/registro.text.erbOlá <%= @usuario.nome %> ( <%= @usuario.email %> ),Seja vem vindo a loja de exemplo do curso de Rails da LinuxFi.
  49. 49. Adicionando o código de envio de email nahora que o usuário for criadoclass Usuario < ActiveRecord::Base after_create :enviar_email def enviar_email UsuariosMailer.registro( self ).deliver endend
  50. 50. Enviando emails}  Quando você define um método para envio de emails no seu mailer, a forma de chamar ele é: }  NomeDaClasse. nome_do_metodo( parametros ).deliver}  Como em: }  UsuariosMailer. registro( usuario ).deliver}  Isso vai fazer com que o ActionMailer gere o texto do email e o envie para o destinatário definido;

×