curso de ruby on rails - aula 04

50
Aprendendo Ruby on Rails – Aula 4 Maurício Linhares

Upload: mauricio-linhares

Post on 12-May-2015

2.705 views

Category:

Technology


17 download

DESCRIPTION

Material do curso de Ruby on Rails.

TRANSCRIPT

Page 1: Curso de Ruby on Rails - Aula 04

Aprendendo Ruby on Rails – Aula 4

Maurício Linhares

Page 2: Curso de Ruby on Rails - Aula 04

Mas o design do meu formulário não está igual ao do Bootstrap!

Form Builders to the rescue!

Page 3: Curso de Ruby on Rails - Aula 04

Ao trabalhar com formulários em Rails, use sempre 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;

Page 4: Curso de Ruby on Rails - Aula 04

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;

Page 5: Curso de Ruby on Rails - Aula 04

Iniciando a implementação – app/helpers/bootstrap_form_builder.rb class 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) end end

Page 6: Curso de Ruby on Rails - Aula 04

Começando a magia – define_method class BootstrapFormBuilder < ActionView::Helpers::FormBuilder basic_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 end end

Page 7: Curso de Ruby on Rails - Aula 04

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;

Page 8: Curso de Ruby on Rails - Aula 04

Implementando wrap_field def 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

Page 9: Curso de Ruby on Rails - Aula 04

Implementando para campos que tem vários itens de formulário multipart_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) end end

Page 10: Curso de Ruby on Rails - Aula 04

Empacotando campos com vários itens def 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

Page 11: Curso de Ruby on Rails - Aula 04

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!

Page 12: Curso de Ruby on Rails - Aula 04

Alterando o formulário de produtos pra usar o 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 %>

Page 13: Curso de Ruby on Rails - Aula 04

Alterando o método que abre o formulário – app/helpers/admin/produtos_helper.rb 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 }, :builder => BootstrapFormBuilder, &block ) end end

Page 14: Curso de Ruby on Rails - Aula 04

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;

Page 15: Curso de Ruby on Rails - Aula 04

Migração da tabela de usuários - CriarUsuarios def 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_id end def self.down drop_table :usuarios

remove_column :pedidos, :usuario_id end

Page 16: Curso de Ruby on Rails - Aula 04

Criando a classe Usuario – validações e campos virtuais class 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

Page 17: Curso de Ruby on Rails - Aula 04

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;

Page 18: Curso de Ruby on Rails - Aula 04

Definindo o controle de senhas do usuário class 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) end

end

Page 19: Curso de Ruby on Rails - Aula 04

Métodos de classe complementares class Usuario < ActiveRecord::Base class << 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 end end

Page 20: Curso de Ruby on Rails - Aula 04

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”;

Page 21: Curso de Ruby on Rails - Aula 04

Preparando o applicaction_controller.rb para o controle de usuários def usuario_atual if @usuario_atual != false @usuario_atual = login_pela_sessao end @usuario_atual end def usuario_atual=( usuario ) @usuario_atual = usuario session[:usuario_id] = usuario.id end def login_pela_sessao resultado = !session[:usuario_id].blank? &&

Usuario.find_by_id( session[:usuario_id] ) resultado ? resultado : false end

Page 22: Curso de Ruby on Rails - Aula 04

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;

Page 23: Curso de Ruby on Rails - Aula 04

Preparando o application_controller.rb para o 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

Page 24: Curso de Ruby on Rails - Aula 04

Filtros para controle de acesso def 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 end end

Page 25: Curso de Ruby on Rails - Aula 04

Filtros para controle de acesso def 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 end end

Page 26: Curso de Ruby on Rails - Aula 04

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;

Page 27: Curso de Ruby on Rails - Aula 04

Bloqueio de acesso apenas para administradores

class Admin::BaseController < ApplicationController layout 'administracao' before_filter :administrador_necessario end

Page 28: Curso de Ruby on Rails - Aula 04

Criando as contas de usuários – usuarios_controller.rb class 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 end

end

Page 29: Curso de Ruby on Rails - Aula 04

Criando contas de usuários – usuarios_controller.rb class 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 end end

Page 30: Curso de Ruby on Rails - Aula 04

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;

Page 31: Curso de Ruby on Rails - Aula 04

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 %>

Page 32: Curso de Ruby on Rails - Aula 04

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;

Page 33: Curso de Ruby on Rails - Aula 04

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”;

Page 34: Curso de Ruby on Rails - Aula 04

Criando rotas para usuários e sessões resource :sessao resource :usuario

Page 35: Curso de Ruby on Rails - Aula 04

Fazendo o login de usuários já existentes – sessoes_controller.rb class 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 end

end

Page 36: Curso de Ruby on Rails - Aula 04

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 end end

Page 37: Curso de Ruby on Rails - Aula 04

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 %>

Page 38: Curso de Ruby on Rails - Aula 04

Apagando as senhas até mesmo dos logs da aplicação

class ApplicationController < ActionController::Base #remover estes parâmetros dos logs filter_parameter_logging :senha, :senha_confirmation end

Page 39: Curso de Ruby on Rails - Aula 04

Fazendo a união do carrinho de compras do usuário não logado com o usuário logado def 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.id end

Page 40: Curso de Ruby on Rails - Aula 04

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;

Page 41: Curso de Ruby on Rails - Aula 04

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 %>

Page 42: Curso de Ruby on Rails - Aula 04

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;

Page 43: Curso de Ruby on Rails - Aula 04

Configurando o SMTP – config/initializers/email_configuration.rb

ActionMailer::Base.smtp_settings = { :enable_starttls_auto => true, :address => "smtp.gmail.com", :port => 587, :domain => "gmail.com", :authentication => :plain, :user_name => "[email protected]", :password => "rubyonrails" }

Page 44: Curso de Ruby on Rails - Aula 04

Criando um diretório para guardar os mailers

}  Crie uma pasta chamada mailers em “app”

}  No seu environment.rb }  config.load_paths += [ "#{RAILS_ROOT}/app/mailers" ]

Page 45: Curso de Ruby on Rails - Aula 04

Criando uma classe base para os mailers – app/mailers/base_mailer.rb

class BaseMailer < ActionMailer::Base default_url_options[:host] = Rails.env.production? ?

’loja.com.br' : 'localhost:3000' default :from => '[email protected]' end

Page 46: Curso de Ruby on Rails - Aula 04

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;

Page 47: Curso de Ruby on Rails - Aula 04

Implementação do mailer de usuarios – app/mailers/usuarios_mailer.rb

class UsuariosMailer < BaseMailer def registro( usuario ) @usuario = usuario mail( :to => usuario.email, :subject => ‘Seja bem vindo a loja virtual em Rails ‘) end end

Page 48: Curso de Ruby on Rails - Aula 04

Email de registro de usuários – app/views/usuarios_mailer/registro.text.erb

Olá <%= @usuario.nome %> ( <%= @usuario.email %> ), Seja vem vindo a loja de exemplo do curso de Rails da

LinuxFi.

Page 49: Curso de Ruby on Rails - Aula 04

Adicionando o código de envio de email na hora que o usuário for criado

class Usuario < ActiveRecord::Base after_create :enviar_email def enviar_email UsuariosMailer.registro( self ).deliver end end

Page 50: Curso de Ruby on Rails - Aula 04

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;