Tutorial Ruby on Rails – Primeira Aplicação – Parte 2
Introdução
No último post da série vimos como fazer uma simples aplicação Ruby on Rails para armazenar geofences. Neste terceiro post da série de tutoriais continuaremos a trabalhar na aplicação anterior, deixando-a mais robusta.
Vamos à nossa primeira modificação:
Autentificação de usuário
Criaremos agora usuários que terão permissões de administrar apenas seus próprios geofences.
A comunidade do Rails é bem ativa, e muitas coisas que você precisa fazer já foram desenvolvidas e você provavelmente irá encontrar alguma gem que satisfaça suas necessidades. Autentificação de usuário é uma tarefa comum, e existem várias opções. Vamos utilizar o Devise.
Na raiz de seu projeto, edite o arquivo Gemfile e adiciona a seguinte linha:
[code language=”ruby” collapse=”false”]gem ‘devise'[/code]
Para instalar a gem, execute o comando
[code collapse=”false”]bundle install[/code]
E use o gerador do Devise:
[code collapse=”false”]rails generate devise:install[/code]
Poderá ver que foram criados dois arquivos no diretório geofences/config:
- geofences/config/initializers/devise.rb: Aqui você pode alterar várias configurações do Devise, mas utilizaremos as configurações padrões por agora.
- geofences/config/locales/devise.en.yml: Este arquivo contem traduções i18n para o Devise. Note que geofences/config/locales/ é o local padrão para suas traduções.
Quando executamos a instalação do Devise, ele também nos pediu para checar 5 passos de configuração:
- Adicione
[code language=”ruby” collapse=”false”]config.action_mailer.default_url_options = { :host => ‘localhost:3000’ }[/code]
no arquivo geofences/config/environments/development.rb
- Definir um root no arquivo geofences/config/routes.rb. Já completamos essa etapa no post anterior quando fizemos nossa aplicação começar diretamente na lista de geofences.
- Adicionar duas partes de código em HTML ao arquivo geofences/app/views/layouts/application.html.erb:
[code language=”ruby” collapse=”false”]
<p class="notice"><%= notice %></p>
<p class="alert"><%= alert %></p>
[/code] - Podemos ignorar o quarto passo por ele não se aplicar ao nosso caso.
- Gerar as views do Devise com o comando
[code collapse=”false”]rails generate devise:views[/code]
As views geradas contem os códigos necessários para o funcionamento do Devise. Note que várias views serão criadas, uma vez que Devise tem vários recursos, mas não vamos usar todos eles.
Acabamos, então, de configurar o Devise, mas ainda não temos nenhum model para o usuário.
Usuários
Gere o modelo de usuários com o comando:
[code collapse=”false”]rails generate devise User[/code]
Este comando acabou de gerar para nós o model User, sua migration e editou as routes necessárias para acessar as manipulações de Users (os caminhos /users/*).
Repare que a gem Devise já cria o User com várias funcionalidades prontas que você pode escolher usar ou não. Por questões de simplicidade, podemos deixar como está. Lembre-se que se mudar algo na migration para habilitar recursos para Users, o model em users.rb deve ser alterado para refletir as mudanças feitas na migration.
Por último, execute o comando
[code collapse=”false”]rake db:migrate[/code]
Assim temos um simples sistema de autentificação que permite o usuário se registrar, efetuar login e logout, além de já manter dados das sessões do usuário.
Para facilitar o acesso às manipulações de usuário em nossos testes, adicione o seguinte código no arquivo geofences/app/views/layouts/application.html.erb depois de <%= yield %>
[code collapse=”false” language=”ruby”]
<p>
<%if user_signed_in?%>
<%= link_to "Sign out", destroy_user_session_path, method: :delete %>
<%else%>
<%= link_to "Sign in", new_user_session_path, method: :get %>
<%end%>
</p>
[/code]
Associações
Agora que temos Users e Geofences, precisamos associá-los. Neste caso é uma relação “um para muitos”. Por convenção, chaves estrangeiras são nomeadas pelo nome do modelo seguido pelo sufixo “_id”. Vamos então criar uma migration para fazer essa alteração em nosso banco de dados com o seguinte comando:
[code collapse=”false”]rails generate migration AddUserIdToGeofence user_id:integer[/code]
A migration gerada irá adicionar uma coluna de inteiros “user_id” à tabela de geofences. Podemos então executar a migration.
[code collapse=”false”]rake db:migrate[/code]
No momento, nenhum dos nosso modelos sabem da existência um do outro, pois simplesmente adicionamos um campo no banco de dados. Para corrigir isso, precisamos fazer uma pequena alteração em cada um dos nossos modelos envolvidos:
Adicionar
[code collapse=”false” language=”ruby”]has_many :geofences[/code]
no arquivo geofences/app/models/user.rb
Adicionar
[code collapse=”false” language=”ruby”]belongs_to :user[/code]
no arquivo geofences/app/models/geofence.rb
Com essas mudanças, uma instância de User terá automaticamente um método “geofences” (entre outros métodos) que irá referenciar todas as Geofences com um user_id específico.
Autentificação para alterar geofences
Atualmente nossos models estão funcionando apropriadamente, mas o GeofencesController criado pelo scaffold não faz nenhuma verificação de usuário. Para corrigir isso, vamos exigir autentificação de usuário antes de executar qualquer ação nas geofences. Para isso precisamos adicionar a seguinte linha de código no arquivo geofences/app/controllers/geofences_controller.rb
[code collapse=”false” language=”ruby”]before_action :authenticate_user![/code]
Agora todas as vezes que se for manipular (ou até mesmo visualizar) uma geofence, é necessário estar logado com algum usuário.
Mas ainda não conferimos se o usuário que está logado é o mesmo que criou uma geofence. Para isso, altere a action “create” de GeofencesController para:
[code collapse=”false” language=”ruby”]
def create
@geofence = Geofence.new(geofence_params)
@geofence.user_id = current_user.id # Preenchendo o campo com o id do usuário logado
respond_to do |format|
if @geofence.save
format.html { redirect_to @geofence, notice: ‘Geofence was successfully created.’ }
format.json { render :show, status: :created, location: @geofence }
else
format.html { render :new }
format.json { render json: @geofence.errors, status: :unprocessable_entity }
end
end
end
[/code]
Altere também a action “index” para:
[code collapse=”false” language=”ruby”]
def index
@geofences = current_user.geofences
end
[/code]
Mude, por último, o método privado “set_geofence” para:
[code collapse=”false” language=”ruby”]
def set_geofence
@geofence = current_user.geofences.find(params[:id])
end
[/code]
Agora todos os geofences criados pertencerão ao usuário logado e só poderão ser vistos por ele.
Validações
Se você tentou registrar um usuário com e-mail mal formatado ou com uma senha de menos de 8 dígitos, viu que o Devise identificou e tratou os erros. Precisamos fazer o mesmo para nossos models.
Existem várias validações possíveis em Rails e você ainda pode criar as suas. Mas vamos utilizar apenas duas bem simples. Verificaremos se o geofence possui um user_id e evitaremos raios negativos. No model Geofence, adicione a seguinte linha de código
[code collapse=”false” language=”ruby”]
validates :user_id, presence: true
validates :radius, :numericality => { :greater_than_or_equal_to => 0 }
[/code]
Se você tentar criar um geofence com raio negativo agora, será retornado um erro na tela indicando que o raio não pode ser menor que 0 e o formulário é renderizado novamente para que o erro seja corrigido pelo usuário.
Quando um recurso não é encontrado por seu argumento recebido, você vai ver que a tela exibida é extremamente inapropriada para uma aplicação real. Para ter um exemplo, rode o server e entre no seguinte link:
http://localhost:3000/geofences/957
Para evitar esse tipo de erro, vamos ao GeofencesController e editar novamente o método privado “set_bookmark” e deixá-lo assim:
[code collapse=”false” language=”ruby”]
def set_geofence
begin
@geofence = current_user.geofences.find(params[:id])
rescue Exception => e
flash[:alert] = ‘Geofence not found.’
redirect_to root_url
end
end
[/code]
Este método “set_geofence” é chamado no before_action das actions show, edit, update e destroy, que são as actions que necessitam do id do objeto.
Se tentarmos entrar novamente no link anterior, seremos redirecionados à página inicial de nossa aplicação e uma mensagem ‘Geofence not found.’ será exibida.
Conclusão
Acabamos de fazer autentificação de usuário e algumas validações em nossa aplicação de Geofences.
Lembre-se de procurar gems para os problemas que você procura resolver. Rails tem uma comunidade bem ativa de usuários e gems podem te poupar muito trabalho, e o que procuramos aqui é produtividade.