quinta-feira, 10 de março de 2011

Ruby on Rails do zero, Devise

Continuação do post "Ruby on Rails do zero" no repositório github.com/vagnerzampieri/zblog tem o projeto que estamos fazendo aqui.

Devise:
  Agora vamos ver como o Devise funciona, se você for dar uma olhada no post anterior irá notar que já baixamos o Devise, mas ainda não instalamos ele. A linha abaixo é a linha que fica no arquivo Gemfile que fica na raiz principal da sua aplicação.
 
  gem 'devise', '1.1.rc2'
 
  O Devise é um autenticador, através dele você poderá criar usuário, logar, recuperar senha, sessão de usuário aberta pelo tempo que desejar, criptografa senha, e muito mais. É uma ferramenta muito completa e muito usada no mundo Rails e não é difícil de usar, o básico dela qualquer nível de programador pode usar, até o menos experiente. Esse Devise que estou usando provavelmente já deve existir um mais recente, então use um mais recente, não acredito que vai quebrar a aplicação ou algo assim, mas se estiver com receio utilize essa versão mesmo.
 
  Vamos dizer que não fizemos download do Devise, se estiver usando RVM coloque aquela linha que dei um pouco acima e coloque ela em Gemfile e faça um "bundle install". Note que ela será baixada para o seu projeto.
 
  Agora vamos instalar o Devise:
 
  rails g devise:install
 
  Irá instalar dois arquivos na sua aplicação, o mais importante é o arquivo de configuração dele 'config/initializers/devise.rb'. É importante dar uma olhada nele, ele já é bem explicado, quando eu for fazer alguma alteração nele irei explicar o por que, mas saiba que ele existe e é muito útil. Outra coisa legal que ele instala é em 'config/locales/devise.en.yml' para dar a saída na língua que você quiser.
 
  O Devise além de te criar os arquivos vai mostrar uma saída que  pedirá que você faça 3 passos. O primeiro é configirar uma url default, isso é só para quando for desenvolvimento. A segunda é para configurar uma rota padrão, esse passo foi feito no post passado, eu cheguei a criar um controller home e coloquei a rota da forma que pede o arquivo, o Devise pede esse passo por que ele usa a rota "root" para os seus redirecionamentos. O terceiro passo são alertas para o Devise mostrar para o usuário se a ação feita foi correspondida da forma esperada ou se ocorreu algum erro. Faça os 3 passos na sua aplicação, se estiver seguindo o post passado o segundo não precisará fazer, por que já está feito.
 
  Ao usar o Devise é habilitado um gerador, então vamos utilizar ele e ver a mágica dele.
 
  rails g devise User
 
  As importantes coisas que ele faz eu vou listar abaixo:
 
  create    app/models/user.rb
  create    db/migrate/20110307130914_devise_create_users.rb
  route  devise_for :users
 
  Utilizando o model User que você colocou como opção ele cria uma série de arquivos com o nome do seu modelo, mas antes de falar sobre o que ele criou vamos logo adicionar as views do Devise na sua aplicação.
 
  rails g devise:views 
 
  Vou começar pelo model, 'app/models/user.rb':
 
  class User < ActiveRecord::Base
    # Include default devise modules. Others available are:
    # :token_authenticatable, :confirmable, :lockable and :timeoutable
    devise :database_authenticatable, :registerable,
           :recoverable, :rememberable, :trackable, :validatable

    # Setup accessible (or protected) attributes for your model
    attr_accessible :email, :password, :password_confirmation, :remember_me
  end
 
  O Devise inclui alguns modulos que serão usados por ele. Na linha que tem 'attr_accessible' é para habilitar os campos que você for querer utilizar quando criar a tabela 'users', se colocar um campo novo e não adicionar nesta linha do model não vai funcionar. Vamos deixar usuário um pouco mais interessante, coloque como abaixo:
 
  attr_accessible :name, :login, :enabled, :url_image, :email, :password, :password_confirmation, :remember_me
 
  Eu adicionei 'name', 'login', 'enabled', 'url_image', os dois primeiros ta na cara. O 'enabled' é para se eu quiser ativar, ou desativar um usuário, eu não vou precisar excluir ele de vez, é só desativar. O 'url_image' é para se o usuário não quiser usar a opção do Gravatar.
 
  Agora no arquivo 'db/migrate/20110307130914_devise_create_users.rb', ele vai ficar assim com as modificações.
 
  class DeviseCreateUsers < ActiveRecord::Migration
    def self.up
      create_table(:users) do |t|
        t.string :name
        t.string :login
        t.boolean :enabled, :default => true
        t.string :url_image
        t.database_authenticatable :null => false
        t.recoverable
        t.rememberable
        t.trackable

        # t.confirmable
        # t.lockable :lock_strategy => :failed_attempts, :unlock_strategy => :both
        # t.token_authenticatable


        t.timestamps
      end

      add_index :users, :name
      add_index :users, :login, :unique => true
      add_index :users, :enabled
      add_index :users, :url_image
      add_index :users, :email,                :unique => true
      add_index :users, :reset_password_token, :unique => true
      # add_index :users, :confirmation_token,   :unique => true
      # add_index :users, :unlock_token,         :unique => true
    end

    def self.down
      drop_table :users
    end
  end
 
  Se você comparar linha por linha, irá notar que eu adicionaei os 4 campos e adicionei os index deles. Antes de rodar o 'rake db:migrate', vamos nas views do Devise e colocar os campos que agente adicionou.
 
  Em 'app/views/devise/registrations/new.html.erb' e 'app/views/devise/registrations/edit.html.erb' ficará assim:
 
  <h2>Sign up</h2>

  <%= form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %>
    <%= devise_error_messages! %>
   
    <p><%= f.label :name %><br />
    <%= f.text_field :name %></p>
   
    <p><%= f.label :email %><br />
    <%= f.text_field :email %></p>

    <p><%= f.label :login %><br />
    <%= f.text_field :login %></p>

    <p><%= f.label :password %><br />
    <%= f.password_field :password %></p>

    <p><%= f.label :password_confirmation %><br />
    <%= f.password_field :password_confirmation %></p>
   
    <p><%= f.label :enabled %><br />
    <%= f.check_box :enabled %></p>
   
    <p><%= f.label :url_image %><br />
    <%= f.text_field :url_image %></p>

    <p><%= f.submit "Sign up" %></p>
  <% end %>

  <%= render :partial => "devise/shared/links" %>
 
  Para você não ficar maluco por que não viu saída de nada até agora, roda 'rake db:create' e 'rake db:migrate' no terminal e vai em 'http://localhost:3000/users/sign_up', mas eu quero duas coisas, uma quero acessar essas rotas através da minha home que criei e quero mudar essas rotas. Primeiro vamos para 'app/home/index'.
 
  <div id="user_nav">
    <% if user_signed_in? %>
      Logado como <%= current_user.email %>. Não é você?
      <%= link_to 'Sair', destroy_user_session_path, :method => :delete %>
      </br>
      <%= link_to 'Post', posts_path %>
    <% else %>
      <%= link_to 'Cadastrar', new_user_registration_path %> ou <%= link_to 'Logar', new_user_session_path %>
    <% end %> 
  </div>
 
  Eu adicionei o código acima, ma o que ele faz, é bem simples. Se o usuário estiver logado ele cai no 'if', que vai mostrar qual email do usuário logado, dar um link para ele destruir a sessão atual dele e um link para 'Post', senão dará duas opções, uma se cadastrar e a outra se logar. Lembre o Devise só faz a autenticação, autorização tem outras formas, se colocar 'posts' na url o Devise não vai te impedir de ir para página. Crie um usuário qualquer. Ao criar irá notar que ele já se loga automaticamente, e mostra uma mensagem que foi logado com sucesso.
 
  Vá na sua tabela users e veja como esse registro de usuário foi criado. Notará que a senha por exemplo está criptografada em 'encrypted_password', que o Devise conta quantas vezes o seu usuário se conectou ao sistema em 'sign_in_count', entre outras informações.
 
  Outra coisa, o model user não tem validação ainda, só para os campos que o Devise criou, então vamos colocar uma validação simples, de pressença, se existe ou não dado naquele campo.
  
  Em 'app/models/user.rb':
 
  validates_presence_of :name, :login
 
  Tente criar um usuário agora sem nome e login para ver o que acontece. Erro, agora tem uma pequena validação, mais a frente vou usar RSpec para testar as nossas validações de formulários.
 
  Se você fez testes se logando viu que a opção é de logar é com email, mas se você não quiser usar email ? Em 'config/initializers/devise.rb' tem uma linha comentada:
 
  config.authentication_keys = [ :email ]
 
  Teste colocando login:
 
  config.authentication_keys = [ :login ]
 
  Em 'app/views/devise/sessions' troque o campo email por login. Vamos configurar um Range de quantos dígitos poderá ter em um password:
 
  config.password_length = 4..20 # Coloquei de 4 à 20 para ilustração.
 
  Leia com atenção as inúmeras codificações que poderá fazer em termos de sessão de usuário. Se quiser configurar a rota para ficar de uma maniera que vai agradar mais você, faça assim:
 
  Em 'config/routes.rb':
 
  devise_for :users, :path => 'usuarios', :path_names => {:sign_in => 'logar', :sign_out => 'sair', :sign_up => 'cadastrar'}
 
  Para encerrar o post de hoje vamos usar o Gravatar. Eu ainda não falei de tudo que da pra fazer com o Devise, quando eu precisar fazer mais coisas nele vou escrever aqui.
 
  O Gravatar é bem simples, coloque a linha abaixo no seu helper, 'app/helpers/application_helper.rb':
 
  def avatar_url(user)
    if user.url_image.present?
      user.url_image
    else
      gravatar_id = Digest::MD5.hexdigest(user.email.downcase)
      "http://gravatar.com/avatar/#{gravatar_id}.png?s=48"
    end
  end
 
  E em 'app/views/home/index.html', chame o helper passando o usuario atual como parâmetro:
 
  Logado como <%= avatar_url(current_user) %> <%= current_user.email %>. Não é você?
 
  O código do método é bem simples, primeiro ele verifica se existe alguma url de imagem no campo 'url_image' que você colocou anteriormente, senão ele vai utilizar o email do usuário como um id para buscar a imagem que está armazenada no Gravatar, o parâmetro 's=48' no final é para definir o tamanho que será dimensionada a imagem.
 
  Chega por hoje, eu decidi fazer logo a autenticação por um simples motivo, todo site tem usuários que tem permissões específicas, áreas que só ele pode entrar, então é geralmente a parte em que se trabalha mais em um site. No próximo post vamos voltar a mexer no scaffold que fizemos de post.
 
  Até a próxima!!

6 comentários:

  1. Parabéns pelo excelente post!
    Estou começando com ruby on rails agora, pois estou cansado de programar em java e percebi que com ror é muito, mas muito mais simples de fazer as coisas.

    ResponderExcluir
  2. Muito boas as instruções para incluir o Devise no projeto.. Vai rolar outro post sobre autorização com cancan ou outra gem assim? Parabéns

    ResponderExcluir
  3. Fabrizio, pode ser, estava pensando em fazer algo com cancan e depois fazer um post com Authlogic em Engines.

    ResponderExcluir
  4. Parabéns pelo excelente post!
    Estou começando com ruby on rails agora, sei como é simples de fazer as coisas.

    ResponderExcluir
  5. Boa tarde Vagner.
    Por exemplo, eu sou o adm da Pagina e quiser cadastrar um usuário, o devise não deixa cadastrar alguém enquanto você estiver logado. Tem alguma solução?

    ResponderExcluir