Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Aprendendo Ruby on Rails – Aula 4                     Maurício Linhares
Mas o design do meu formulário não         está igual ao do Bootstrap!                   Form Builders to the rescue!
Ao trabalhar com formulários em Rails, usesempre form-builders}    Forma padronizada de lidar com formulários no Rails;}...
Como implementar um form-builder?}    O que você quer fazer é implementar um Decorator que      vai cobrir o form builder...
Iniciando a implementação – app/helpers/bootstrap_form_builder.rbclass BootstrapFormBuilder < ActionView::Helpers::FormBui...
Começando a magia – define_methodclass BootstrapFormBuilder < ActionView::Helpers::FormBuilderbasic_helpers = %w{text_fiel...
Como assim define_method?}    Define um método em tempo de execução dentro de um      objeto (a técnica é comumente conhe...
Implementando wrap_fielddef wrap_field(label, content, error_text)  wrapper_class = [clearfix]  unless error_text.blank?  ...
Implementando para campos que tem váriositens de formuláriomultipart_helpers = %w{date_select datetime_select}multipart_he...
Empacotando campos com vários itensdef wrap_multipart_field(label, content, error_text)  wrapper_class = [clearfix]  unles...
Detalhe especial}    Ao usar human_attribute_name o seu objeto precisa ter a tradução      dos atributos definida, como e...
Alterando o formulário de produtos pra usaro novo Form Builder<%= admin_form_for_produto do |f| %>  <fieldset>   <legend> ...
Alterando o método que abre o formulário –app/helpers/admin/produtos_helper.rbmodule Admin::ProdutosHelper def admin_form_...
Criando os usuários}    Os usuários da aplicação englobam tanto as pessoas que      vão vir fazer compras no site como ta...
Migração da tabela de usuários -CriarUsuariosdef self.up  create_table :usuarios do |t|    t.string :email, :null => false...
Criando a classe Usuario – validações ecampos virtuaisclass Usuario < ActiveRecord::Base attr_accessor :senha, :termos_e_c...
Senhas e segurança}    Por motivos de segurança, você nunca deve gravar uma      senha de um dos seus usuários no banco d...
Definindo o controle de senhas do usuárioclass Usuario < ActiveRecord::Base before_validation :hashear_senha def senha_nec...
Métodos de classe complementaresclass Usuario < ActiveRecord::Baseclass << self  def hashear( senha, salt )   Digest::SHA1...
Usuários e Senhas}    Os campos senha e senha_confirmation são ambos      campos virtuais, eles existem apenas para ajuda...
Preparando o applicaction_controller.rb parao controle de usuáriosdef usuario_atual if @usuario_atual != false   @usuario_...
Definindo o controle geral}    O usuário que está atualmente logado na aplicação está      acessível pelo método “usuario...
Preparando o application_controller.rb parao controle de acesso de usuários helper_method :pedido_atual, :usuario_atual, :...
Filtros para controle de acessodef login_necessario  unless self.logged_in?   respond_to do |format|    format.html do    ...
Filtros para controle de acessodef administrador_necessario unless self.administrador?   respond_to do |format|    format....
Filtros para controle de acesso}    Os métodos login_necessario e      administrador_necessario devem ser utilizados como...
Bloqueio de acesso apenas paraadministradoresclass Admin::BaseController < ApplicationController layout administracao befo...
Criando as contas de usuários –usuarios_controller.rbclass UsuariosController < ApplicationController before_filter :login...
Criando contas de usuários –usuarios_controller.rbclass UsuariosController < ApplicationController def create  @usuario.at...
Criando contas de usuários}    Apenas usuários que estejam logados podem chamar as      ações edit, já que há um filtro q...
Formulário de criação/edição de usuários<%= form_for @usuario, :url => usuario_path, :html => { :method => :post} do |f| %...
Formulário de criação de usuários}    O formulário não referencia os campos salt ou      senha_em_hash, apenas os campos ...
Rotas do tipo resource}    Quando uma rota é definida como “resource” (no      singular), significa que o recurso que ela...
Criando rotas para usuários e sessõesresource :sessaoresource :usuario
Fazendo o login de usuários já existentes –sessoes_controller.rbclass SessoesController < ApplicationController def new  r...
Fazendo o login de usuários já existentes def create  @usuario = Usuario.autenticar( params[:email] , params[:senha])  if ...
Formulário de login de usuários – sessoes/new.html.erb<%= form_tag sessao_path do %> <p>  Email:  <br/>  <%= text_field_ta...
Apagando as senhas até mesmo dos logs daaplicaçãoclass ApplicationController < ActionController::Base #remover estes parâm...
Fazendo a união do carrinho de compras dousuário não logado com o usuário logadodef usuario_atual=( usuario )  @usuario_at...
Unindo carrinhos de compra}    Se o usuário já estava adicionando itens ao carrinho antes      de efetuar a compra, esses...
Template para criar conta ou login –compartilhados/_login_logout.html.erb<% if logged_in? %> <p>  Olá <%= usuario_atual.no...
Enviando emails com Rails}    O Rails vem com um componente padrão para o envio de      emails, o ActionMailer;}    Com ...
Configurando o SMTP – config/initializers/email_configuration.rbActionMailer::Base.smtp_settings = {  :enable_starttls_aut...
Criando um diretório para guardar osmailers}    Crie uma pasta chamada mailers em “app”}    No seu environment.rb      }...
Criando uma classe base para os mailers –app/mailers/base_mailer.rbclass BaseMailer < ActionMailer::Basedefault_url_option...
Por que uma classe base?}    Adicionar métodos padrão para todos os mailers;}    Adicionar configurações padrão que vão ...
Implementação do mailer de usuarios –app/mailers/usuarios_mailer.rbclass UsuariosMailer < BaseMailer def registro( usuario...
Email de registro de usuários – app/views/usuarios_mailer/registro.text.erbOlá <%= @usuario.nome %> ( <%= @usuario.email %...
Adicionando o código de envio de email nahora que o usuário for criadoclass Usuario < ActiveRecord::Base after_create     ...
Enviando emails}    Quando você define um método para envio de emails no      seu mailer, a forma de chamar ele é:      }...
Upcoming SlideShare
Loading in …5
×

Curso de Ruby on Rails - Aula 04

3,183 views

Published on

Material do curso de Ruby on Rails.

Published in: Technology
  • Be the first to comment

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;

×