Nos dias atuais, para postar, acompanhar notícias ou comprar produtos, o usuário online precisa estar inscrito no site que deseja. Então, por que não implementar o sign up utilizando o Rails?

Como esse framework trabalha com o modelo MVC, uma série de medidas serão necessárias:

  • No model é preciso representar o usuário com uma estrutura de dados padrão, então, o modelo User será criado junto com as validações para que os campos importantes ao acesso do site não possam ser salvos vazios.
  • Na view será necessária uma página referente ao preenchimento dos dados do usuário, com os campos necessários para identificá-lo no contexto do site.
  • No controller será o lugar no projeto onde as ações referentes a desenvoltura de criar, editar, atualizar, apagar, mostrar o usuário serão feitas.

O Banco de dados, obviamente, não exclusivo do Rails, também tem grande importância, afinal, é preciso que a tabela User esteja registrada antes de qualquer ação. Através dele, sé possível salvar um usuário.

NOTA: Lembre-se que o responsável por interagir com o banco de dados será a biblioteca Active Record, ou seja, não é preciso usar o SQL (structured query language).

Criando o Projeto

Antes de tudo, é preciso que o projeto seja criado. Lembre-se que ao dar o comando, a pasta deste já será feita pelo Rails. Acesse seu Terminal ou CMD e digite o local em que deseja que essa pasta fique. Para isso, use o seguinte comando:

rails new blog

O new será responsável por criar diversas pastas do projeto e todas elas serão mostradas como resposta pelo prompt, como mostra a Figura 1.

Pastas criadas do novo blog

Figura 1. Pastas criadas do novo blog

Nesse momento você deve estar com dúvida sobre a função de todas essas pastas que Rails determinou como necessárias. Obviamente, todas possuem um propósito. Vejamos a seguir:

  • app/: Contém models, controllers, helpers, mailers, views e assets;
  • bin/: Contém o script do Rails e pode conter scripts próprios para fazer deploy ou rodar o programa;
  • config/: Contém a configuração de rotas, banco e outros;
  • db/: Contém a atual tabela do banco de dados e as migrações;
  • Gemfile, Gemfile.lock: Esses arquivos permitem que você especifique quais gems a sua aplicação dependerá, utilizando o Bundler;
  • lib/: Contém os módulos para a aplicação;
  • public/: Contém arquivos estáticos e assets copilados;
  • Rakefile: Esse arquivo loca e carrega os comandos;
  • test/: Contém os testes feitos paras as partes da aplicação;
  • tmp/: Contém os arquivos temporários (cache, pid, arquivos de sessões);
  • README.doc: Instrução manual para a aplicação.

A partir de agora já é possível criar e saber onde está cada parte de código do seu programa. O próximo passo é o futuro usuário.

Criando o modelo User

O objetivo é criar a representação do usuário no projeto para termos futuramente uma página Sign Up (Cadastro). Então, abra o prompt do seu computador, entre na pasta onde quer criar o futuro site e digite o seguinte comando:

rails generate model User nome:string email:string

Ao entrar, verá que foi criado no projeto o blog /app/models/user.rb, porém vazio, sem validações ou métodos referentes ao usuário.

Em seguida, digite esse código:


  class User < ActiveRecord::Base
   end

No comando generate, os atributos usam parâmetros opcionais. Foi dito ao Rails, que são desejados dois campos no usuário e seus tipos, que nesse caso são strings.

Ao gerar o modelo, alguns arquivos são criados referentes ao user, inclusive, um arquivo na pasta db/migrate/. Este chamado de migration. Migrations são responsáveis por fornecer uma forma de alterar incrementando a estrutura do banco de dados. No caso do modelo User, a migration é criada automaticamente pelo script model generate e através dela, uma tabela users com duas colunas: nome e email também. O arquivo é desta forma: >blog/db/migrate/tempodacriação_create_users.rb.

Veja como criar com o código da Listagem 1.

Listagem 1. Incrementando a Migration


  class CreateUsers < ActiveRecord::Migration 
       def change create_table :users do |t| 
  t.string :nome
  t.string :email 
   
  t.timestamps
  end 
       end
  end

O nome da classe mostra uma convenção do Rails, que consiste em: o modelo representa apenas um usuário, enquanto o banco, vários usuários. Por isso, o nome em plural (CreateUsers). A migration em si consiste em um método de mudança para determinar o banco de dados. O método change chama o método create_table, que criará uma tabela para salvar usuários no programa.

No final do código, encontra-se t.timestamps que é um comando especial responsável por criar duas colunas chamadas created_at e updated_at. Essas duas serão responsáveis por registrar o usuário foi criado e modificado.

Para salvar a nova tabela users, é preciso fazer um comando com o rake, responsável por rodar migrations no Rails. Então, digite no prompt o seguinte comando:

rake db:migrate

Quando o rake roda pela primeira vez, o arquivo db/development.sqlite3 é criado. Esse é o banco de dados SQLite3, padrão do Rails (pode-se mudar no arquivo Gemfile).

A maioria das migrações são reversíveis; pode-se utilizar db:rollback para voltar o rake anterior ou db:drop para apagar tudo do banco, além de outros que podem ser checados na documentação.

Enfim, a representação do usuário está feita. Agora faltam as ações, os HTMLs e uma senha encriptada para salvar o usuário corretamente.

Criando o controller User

Como o artigo é principalmente sobre criar o usuário, serão de extrema importância os métodos new e create no controller. Seguindo a convenção REST arquiteture, vamos chamar o método new no comando a seguir, assim ele será criado automaticamente:

rails generate controller Users new

Com esse comando, a view referente à ação já foi criada, junto com os testes e o caminho da URL (route). O controller e a view estão, respectivamente, nos códigos da Listagem 2 e Listagem 3.

Listagem 2. >blog/app/controllers/users_controller.rb


  class UsersController < ApplicationController
  def new
  end
  end 

Listagem 3. >blog/app/views/users/new.html.erb


  <h1>Users#new</h1> 
  <p>Find me in app/views/users/new.html.erb</p>

Ao dar rails server, será possível encontrar essa view na URL localhost3000/users/new.

Criando uma senha encriptada

Um usuário não pode ter sua senha revelada tão facilmente durante seu cadastro. Então, para isso é de extrema importância a criação de uma senha encriptada, onde apenas essa é mostrada em consultas no banco. Grande parte do trabalho será resolvido utilizando um método Rails no modelo User , como mostra o código a seguir:


  class User < ActiveRecord::Base
                    has_secure_password 
  end

Ao adicionar esse método, as seguintes funcionalidades são adicionadas:

  • A habilidade de salvar o password_digest, ou seja, a senha encriptada no banco;
  • Atributos virtuais password e password_confirmation, inclusive uma possível validação;
  • Um método de autenticação que retornará o usuário quando a senha estiver correta.

O único requisito para funcionamento do has_secure_password é a criação do atributo password_digest no modelo. Para implementar no banco, primeiramente, adiciona-se uma migration para a criação da coluna da senha. Depois, fazemos uma atualização com o comando rake para salvar o novo campo definitivamente na tabela.

É de conveniência do Rails que o novo atributo quando pertence a uma tabela já existente, utiliza-se no final da ação do comando o to_users. Assim, será automático para o interpretador introduzir o novo campo na tabela determinada. No prompt de comando digite:

 rails generate migration add_password_digest_to_users password_digest:string

Depois para salvar no banco use o seguinte comando:

rake db:migrate

O has_secure_password utiliza uma segurança com o bcrypt. Através dele, pode-se garantir que um hacker não será capaz de fazer login no site, mesmo se tiverem uma cópia do banco de dados. Para te-lo, é preciso adicioná-lo no Gemfile, que fica na pasta blog.

source 'https://rubygems.org'
  gem 'bcrypt'

Para o Rails saber da nova gem necessária no projeto é preciso fazer um comando bundle no Terminal ou CMD, responsável por atualizar gems, conforme mostra o código abaixo:

bundle install

A partir de agora o usuário tem uma senha encriptada e já é possível criar validações, inclusive, para os campos password e password_confirmation.

Validando Usuário

Os campos de cadastro, obviamente, não podem ficar em branco. Então, é preciso que no modelo sejam feitas verificações para cada usuário que for criado. Entre no blog/app/models/user.rb e modifique o código de acordo com a Listagem 4.

Listagem 4. Modificação no código


  class User < ActiveRecord::Base 
  validates :nome, presence: true, length: { maximum: 50 }
   
  before_save { self.email = email.downcase }
  validates :email, presence: true, format: {with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i, on: :create}, uniqueness: {case_sensitive: false}
   
  validates :password, presence: true, length: { minimum: 6 }
   
  has_secure_password
   end

Todos os campos terão que ser preenchidos, esse fato é definido pelo presence: true. O campo nome não poderá ter um tamanho acima de 50 caracteres e o campo password não pode ter menos que seis caracteres. O campo email antes de ser salvo, será transformado em letra minúscula e seu formato com @ é definido pelo algoritmo colocado no código.

O uniqueness colocado no email significa que antes de criar o usuário será verificado nos registros se existe algum campo igual, porém ainda é permitido que dois emails sejam criados iguais ao mesmo tempo. Para resolver esse problema é preciso criar uma validação no banco de dados. Uma migração para criar um índice em cada email na tabela user será necessário:

 rails generate migration add_index_to_users_email

O índice, por si só, não impõe a exclusividade, mas a opção unique: true faz. Entre no blog/db/migrate/tempo_add_index_to_users_email e digite o seguinte código da Listagem 5.

Listagem 5. Definindo exclusividade no índice


  class AddIndexToUsersEmail < ActiveRecord::Migration 
  def change
  add_index :users, :email, unique: true
  end 
  end

O último passo é implementar com o rake no banco. Por isso, através do prompt digite o seguinte código:

rake db:migrate

No momento, os campos estão definidos e validados, mas falta a view para cadastro e ações referente à ela. A seguir será criado a futura página.

Fazendo página de Cadastro

No Rails, cada ação possui uma view correspondente, inclusive pelo nome. Como a ação que será criada futuramente é a new, o objetivo é fazer o arquivo new.html.erb ficar parecido com a Figura 2 a seguir:

Figura 2. Futura página de Cadastro com Balsamiq.

Para isso, é preciso de um formulário. Mas caso após esse artigo você queira implementar a função editar, o mesmo formulário será utilizado. Como não é aconselhado repetir código, será feito um novo arquivo chamado _form.html.erb dentro de blog/app/views/users/ para que esse template seja chamado quantas vezes for necessário em outros HTMLs. Abra seu editor de texto e crie esse arquivo com o código da Listagem 6.

Listagem 6. blog/app/views/users/_form.html.erb


  <%= form_for @user do |f| %>
              < div class="field">
                          <%= f.label :nome %>
                          <%= f.text_field :nome %>
              </div>
              <div class="field">
                          <%= f.label :email %>
                          <%= f.text_field :email %>
              </div>
              <div class="field">
                          <%= f.label :password %>
                          <%= f.password_field :password %>
              </div>
              <div class="field">
                          <%= f.label :password_confirmation %>
                          <%= f.password_field :password_confirmation %>
              </div>
              <div class="actions">
                          <%= f.submit “Cadastrar”%>
              </div>
  <% end %>

Os atributos foram criados com campos textos e as senhas com campos que escondem-nas (password_field). A ação cadastrar é criada com o botão submit.

Nesse formulário existe apenas um problema: se for encontrado um erro pelas validações, como isso será mostrado no projeto? Então, será necessário acessar novamente o arquivo para modificá-lo, de acordo com a Listagem 7.

Listagem 7. >blog/app/views/users/_form.html.erb


  <%= form_for @user do |f| %>
              <% if @user.errors.any? %>
              <div id="error_explanation">
                          <div class="alert-error">
                                     O formulário contém <%= pluralize(@user.errors.count, "erro") %>.
                          </div>
                          <ul>
                                     <% @user.errors.full_messages.each do |msg| %>
                                     <li><%= msg %></li>
                                     <% end %>
                          </ul>
              </div>
  <% end %>
  <div class="field">
                          <%= f.label :nome %>
                          <%= f.text_field :nome %>
              </div>
              <div class="field">
                          <%= f.label :email %>
                          <%= f.text_field :email %>
              </div>
              <div class="field">
                          <%= f.label :password %>
                          <%= f.password_field :password %>
              </div>
              <div class="field">
                          <%= f.label :password_confirmation, "Confirmação" %>
                          <%= f.password_field :password_confirmation %>
              </div>
              <div class="actions">
                          <%= f.submit "Cadastrar"%>
              </div>
  <% end %> 

Agora, toda vez que for encontrado um erro, estes serão contados e caso exista mais de um, a palavra “error” será pluralizada. Após a contagem, cada erro será listado.

No momento, o código do formulário está no form, porém esse não será o template chamado pela ação new. Entre no arquivo new.html.erb e escreva o seguinte comando:


  <h1>Sign Up</h1>
  <%= render 'form' %>

O método render será responsável por chamar todo o código no template form. A partir de agora, se você der o comando rails server aparece a seguinte página (Figura 3) no URL http://localhost:3000/users/new.

Figura 3. Erro ao tentar acessar página criada.

O Rails está indicando que não existe usuário porque ele procurou nas ações do controller e nada estava escrito lá. A seguir para resolver esse problema, modificaremos o controller do projeto Blog.

Detalhe: Nesse artigo não será utilizado CSS, porém para editar o user no front-end acesse blog/app/assets/stylesheets/user.css.scss

Fazendo Controller Users

Para uma aplicação funcionar é preciso que as funcionalidades estejam no código. Acesse o users_controller e digite os commandos da Listagem 8.

Listagem 8. >blog/app/controllers/users_controller.rb


  class UsersController < ApplicationController
    def new
              @user = User.new
    end
  end

Agora, ao tentar entrar na página, o seguinte erro será dado pelo interpretador: “undefined method `users_path’”, isso porque o Rails procura o caminho, mas nas routes apenas existe o código da Listagem 9:

Listagem 9. >blog/config/routes.rb


  Rails.application.routes.draw do
    get 'users/new'
  end

É preciso caminhos correspondentes a futuras ações e essa também. Então, o resources será capaz de fazer tudo isso. Modifique routes e substitua pelo seguinte código:


  Rails.application.routes.draw do
    resources :users
  end

Pronto, agora a página e a ação funcionam. Com exceção de um detalhe: quando o usuário é criado este é salvo no método create. O Rails te mostra quando tenta clicar no botão (Figura 4):

Figura 4. Não foi possível salvar o novo Objeto.

O objeto não foi cadastrado devido à não programação do método no UsersController. Acesse o arquivo para modificá-lo, conforme o código da Listagem 10.

Listagem 10. >blog/app/controllers/users_controller.rb


  class UsersController < ApplicationController
              def new
                          @user = User.new
              end
              def create
                          @user = User.new(params[:user])
                          if @user.save
                                      redirect_to users_path, notice: "Usuário foi criado!"
                          else 
                                      render action: :new
                          end
              end
  end

A primeira linha do método create tem @user = User.new(params[:user]) que é parcialmente equivalente à @user = User.new(nome: “Nathália”, email: “nathalia@email.com”, password: “123456”, password_confirmarion: “123456”), pois em versões anteriores funcionava. Porém, essa forma é insegura porque usuários maliciosos podem modificar o banco de dados da sua aplicação, como por exemplo, mudando o id pra 1 e assim transformando-se em admin do site. Portanto, será gerado um erro ActiveModel::ForbiddenAttributesError.

Então, o Rails criou o Strong Parameters. A ideia é uma atribuição em massa que envolve inicialização de uma variável Ruby utilizando um valor Hash. Através dele é possível saber quais parâmetros são requeridos e permitidos. Acesse novamente o users_controller e modifique-o conforme a Listagem 11.

Listagem 11. >blog/app/controllers/users/users_controller.rb


  class UsersController < ApplicationController
              def new
                          @user = User.new
              end
              def create
              @user = User.new(user_params)
                          if @user.save
                                      redirect_to user_path(@user), notice: "Usuário foi criado!"
                          else 
                                      render action: :new
                          end
              end
              private
              def user_params
  params.require(:user).permit(:nome, :email, :password, :password_confirmation)
              end
  end 

No método modificado, cria-se o usuário e checa-se caso ele tenha sido salvo. Se algum campo estiver vazio, ele não salvará e a página new se renderiza. Se tudo estiver conforme as validações, a aplicação é redirecionada para mostrar os dados do novo usuário.

Caso tente-se criar um usuário agora, percebe-se que está funcionando. Porém um novo erro aparece: “The action ‘show’ could not be found for UsersController”, afinal não foi criado a ação show e nem sua view para mostrar determinado objeto.

Para checar, pode-se acessar o console do Rails. Dê control+C no servidor e digite no prompt:

rails console

Para mostrar todos os usuários já criados escreva o código da Listagem 12.

Listagem 12 Usuários já cadastrados


  2.0.0-p451 :001 > User.all
    User Load (0.9ms)  SELECT "users".* FROM "users"
   => #<ActiveRecord::Relation [#<User id: 1, nome: "Nathalia", email: "nathalia@example.com", created_at: "2014-10-27 19:30:17", updated_at: "2014-10-27 19:30:17", password_digest:$2a$10$Wsz30WN78.VgFI0slFlQtuQEL.JKA5DaucIQ0.VPNvT...">

Se quiser achar um específico, pode utilizar o método find (Listagem 13):

Listagem 13. Listar um usuário específico


  2.0.0-p451 :002 > User.find(1)
    User Load (0.2ms)  SELECT  "users".* FROM "users"  WHERE "users"."id" = ? LIMIT 1  [["id", 1]]
   => #<User id: 1, nome: "Nathalia", email: "nathalia@example.com", created_at: "2014-10-27 19:30:17", updated_at: "2014-10-27 19:30:17", password_digest: "$2a$10$Wsz30WN78.VgFI0slFlQtuQEL.JKA5DaucIQ0.VPNvT...">  

O Rails conta com outras formas de encriptar senhas de usuários, como o Devise. Nesse artigo, muitos conceitos foram reduzidos, porém a documentação sempre terá mais exemplos definidos para qualquer problema em seu código. É de extrema importância que a aplicação utilizada como exemplo acima tenha sido útil para tirar suas dúvidas do desenvolvimento web.