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