I – Introdução
- Neste post vamos permitir que o usuário possa fazer o login usando nome de usuário ou email, usando a gem Devise com o Rails versão 3.2.1.
- Para demonstrar isso, vamos continuar a construir um app web, que iniciamos no artigo em Aplicação “mestre detalhe” em Rails 3.2.1 e depois em Autenticação em Ruby On Rails 3.2.1 com Devise.
- O código fonte gerado no artigo anterior, está hospedado no Github em https://github.com/sergiosouzalima/service_desk
- O código fonte gerado para este artigo, também está hospedado no Github em https://github.com/sergiosouzalima/service_desk_v03
II – Principais tecnologias usadas neste artigo
- Linux Ubuntu 11.10
- Ruby 1.9.2
- Rails 3.2.1
- gem Devise 2.0
III – O que não precisamos instalar
- Git, RVM, openssl, sqlite3 e outros pacotes necessários.
- Como dependemos de instalações e configurações anteriores, caso seu ambiente não esteja preparado, veja os posts:
- Para instalar a versão do Rails 3.2.1, siga o tutorial acima (item 1 e 2). Apenas informe a versão do Rails 3.2.1 no lugar das versões anteriores.
IV – O que precisa ser feito antes
- Este post é continuação do trabalho que fizemos em: Aplicação “mestre detalhe” em Rails 3.2.1
- e depois em Autenticação em Ruby On Rails 3.2.1 com Devise.
V – Sequência de passos para implementação
Passo 01 – Entre na aplicação que criamos anteriormente
$ cd service_desk
Passo 02 – Corrigir mensagens
- Para que o app tenha menos código e fique mais DRY (Don’t repeat yourself), vamos corrigir duas views.
- Em app/views/tickets/show.html.erb, retire a seguinte linha
<p id="notice"><%= notice %></p>
- Em app/views/layouts/application.html.erb, modifique a “section main” para que fique da seguinte maneira:
<section id="main"> <p class="notice"><%= notice %></p> <p class="alert"><%= alert %></p> <%= yield %> </section>
Passo 03 – Inclua a coluna username na tabela de usuários (Users)
- A coluna username será única (uniq) na tabela Users.
- A migration irá gerar um índice na mesma tabela Users, para otimizar os acessos pela chave username.
$ rails g migration add_username_to_users username:string:uniq $ bundle exec rake db:migrate
Passo 04 – Gere as views do Devise
- Para incluir o campo username, que terá a label “login” nas views do Devise, é necessário personalizar as mesmas views.
- Vamos, agora, gerar todas a views do Devise para que seja possível personalizar as telas de registro de usuário, de login de usuário, e tela de modificação de senha.
$ rails g devise:views
- Veja abaixo que foram criados a pasta “devise” e as pastas e arquivos (views) do Devise.
$ tree app/views/devise/
app/views/devise/
├── confirmations
│ └── new.html.erb
├── _links.erb
├── mailer
│ ├── confirmation_instructions.html.erb
│ ├── reset_password_instructions.html.erb
│ └── unlock_instructions.html.erb
├── passwords
│ ├── edit.html.erb
│ └── new.html.erb
├── registrations
│ ├── edit.html.erb
│ └── new.html.erb
├── sessions
│ └── new.html.erb
└── unlocks
└── new.html.erb
6 directories, 11 files
Passo 05 – Modifique as views do Devise a seguir
- Na view app/views/devise/sessions/new.html.erb:
<div><%= f.label :email %><br /> # delete essa linha <%= f.email_field :email %></div> # delete essa linha <div><%= f.label :login %><br /> # insira essa linha <%= f.text_field :login %></div> # insira essa linha
- Na view app/views/devise/registrations/new.html.erb:
<div><%= f.label :username %><br /> # insira essa linha <%= f.text_field :username %></div> # insira essa linha <div><%= f.label :email %><br /> # mantenha essa linha <%= f.email_field :email %></div> # mantenha essa linha
- Na view app/views/devise/registrations/edit.html.erb:
<div><%= f.label :username %><br /> # insira essa linha <%= f.text_field :username %></div> # insira essa linha <div><%= f.label :email %><br /> # mantenha essa linha <%= f.email_field :email %></div> # mantenha essa linha
- Na view app/views/devise/passwords/new.html.erb:
<div><%= f.label :email %><br /> # delete essa linha <%= f.email_field :email %></div> # delete essa linha <div><%= f.label :login %><br /> # insira essa linha <%= f.text_field :login %></div> # insira essa linha
Passo 06 – Modifique a label das views para que mostre “username or email” ao usuário
- Modifique o arquivo /config/locales/en.yml para que fique parecido com o trecho abaixo:
en:
hello: "Hello world"
activerecord:
attributes:
user:
login: "Username or email"
Passo 07 – Indique ao Devise para que use :login no authentication_keys
- Modifique o arquivo /config/initializers/devise.rb para que tenha:
config.authentication_keys = [ :login ]
Passo 08 – Agora várias alterações do model User serão necessárias
- Modifique o arquivo /app/model/user.rb para que tenha:
attr_accessible :username, :login
# Virtual attribute for authenticating by either username or email
# This is in addition to a real persisted field like 'username'
attr_accessor :login
# Overwrite Devise’s find_for_database_authentication method
def self.find_for_database_authentication(warden_conditions)
conditions = warden_conditions.dup
login = conditions.delete(:login)
where(conditions).where(["lower(username) = :value OR lower(email) = :value", { :value =>
login.strip.downcase }]).first
end
protected
# Attempt to find a user by it's email. If a record is found, send new
# password instructions to it. If not user is found, returns a new user
# with an email not found error.
def self.send_reset_password_instructions(attributes={})
recoverable = find_recoverable_or_initialize_with_errors(reset_password_keys, attributes, :not_found)
recoverable.send_reset_password_instructions if recoverable.persisted?
recoverable
end
def self.find_recoverable_or_initialize_with_errors(required_attributes, attributes, error=:invalid)
(case_insensitive_keys || []).each { |k| attributes[k].try(:downcase!) }
attributes = attributes.slice(*required_attributes)
attributes.delete_if { |key, value| value.blank? }
if attributes.size == required_attributes.size
if attributes.has_key?(:login)
login = attributes[:login]
record = find_record(login)
else
record = where(attributes).first
end
end
unless record
record = new
required_attributes.each do |key|
value = attributes[key]
record.send("#{key}=", value)
record.errors.add(key, value.present? ? error : :blank)
end
end
record
end
def self.find_record(login)
where(["username = :value OR email = :value", { :value => login }]).first
end
Passo 09 – Alimentar dados automaticamente no sistema
- Vamos usar comandos de inserção de dados, dentro do arquivo “seed.rb”
- Cada vez que alguém iniciar este projeto do zero, será possível inicia-lo com esses dados de exemplo.
- Desta forma, teremos quatro usuários já cadastrados no sistema, todos com a senha 123456:
- superuser
- admin
- user
- user1
- Modifique o arquivo /db/migrate/seeds.rb para que tenha:
superuser_user = User.create!(:username => 'superuser',
:email => 'superuser@example.com',
:password => '123456',
:password_confirmation => '123456',
:confirmed_at => Time.now )
admin_user = User.create!(:username => 'admin',
:email => 'admin@example.com',
:password => '123456',
:password_confirmation => '123456',
:confirmed_at => Time.now )
user_user = User.create!(:username => 'user',
:email => 'user@example.com',
:password => '123456',
:password_confirmation => '123456',
:confirmed_at => Time.now )
user1_user = User.create!(:username => 'user1',
:email => 'user1@example.com',
:password => '123456',
:password_confirmation => '123456',
:confirmed_at => Time.now )
# create ticket #1
tkt1 = Ticket.create!( ticket_number: "00001", title: "ticket number 00001" )
Task.create!( description: "task 001 of ticket 00001", ticket_id: tkt1.id )
# create ticket #2
tkt2 = Ticket.create!( ticket_number: "00002", title: "ticket number 00002" )
Task.create!( description: "task 001 of ticket 00002", ticket_id: tkt2.id )
Task.create!( description: "task 002 of ticket 00002", ticket_id: tkt2.id )
# create ticket #3
tkt3 = Ticket.create!( ticket_number: "00003", title: "ticket number 00003" )
Task.create!( description: "task 001 of ticket 00003", ticket_id: tkt3.id )
Task.create!( description: "task 002 of ticket 00003", ticket_id: tkt3.id )
Task.create!( description: "task 003 of ticket 00003", ticket_id: tkt3.id )
- Para que o seed acima seja executado, execute o comando:
$ bundle exec rake db:seed
Passo 10 – Conferencia dos Models
- Agora o seu model Ticket (app/model/ticket.rb) terá que ficar assim:
class Ticket < ActiveRecord::Base
has_many :tasks validates :ticket_number, presence: true
accepts_nested_attributes_for :tasks, :reject_if => proc { |a| a[:description].blank? }, allow_destroy:
...true
attr_accessible :ticket_number, :title
end
- E seu model Task (app/model/task.rb) assim:
class Task < ActiveRecord::Base belongs_to :ticket attr_accessible :description, :ticket_id end
Passo 11 – Execute a aplicação localmente
- Execute o servidor e carregue o browser e na linha de endereços digite http://localhost:3000
$ rails server
VI – Final Feliz
Pronto!
Fizemos o básico para que nosso app web aceite username ou o email do usuário.
Também criamos um arquivo “seed” que poderá ser usado para criar dados iniciais (inclusive usuários) de exemplo no sistema.
Discussão
Nenhum comentário ainda.