Rogeriolol

Ruby é a esperança.

Criando E Manipulando String Com Gsub Em Lua

| Comments

Hum demorei bastante para fazer outro post rs, então vamos seguindo com minha linha de estudos para desenvolver para TV Digital. Se você ainda não tem Lua instalado na sua máquina pode fazer isso seguindo esse meu post.

Gsub é uma função que faz replace de palavras dentro de uma string.

Agora vamos abrir o terminal e digitar o seguinte comando lua:

1
2
3
4
a = "Criando E Manipulando String Com Gsub" --> preenche a variável "a" com Criando E Manipulando String Com Gsub
b = string.gsub(a, " E", ",")
print(a)
print(b)

Você pode verificar que dessa forma a variavel a não foi alterada, para que isso aconteça é necessario fazer com que ela mesmo receba o retorno da função gsub:

1
2
a = string.gsub(a, " E", ",")
print(a)

Você pode se aprofundar mais sobre como manipular string em Lua aqui.

Simple e fácil lol.

Minha Mais Nova Gem Address by Cep

| Comments

Address By Cep

É uma gem que gera um model address e partial para cadastro de endereço de forma polimórfica, fazendo uma busca de um endereço ao web service dado um cep.

Tudo sobre como usar aqui Github.

Simples e fácil lol.

Cont Preenchendo ComboBox Com Cidades De Acordo Com O Estado Selecionado Em Rails

| Comments

Seguindo esse meu post e para tirar a duvida do Romulo e Tiago no grupo rails-br, resolvi ir um pouco mais longe sobre o post de preencher combobox de acordo com o estado selecionado.

Vamos as seguintes alterações:

1) Sigua esse post, pois vamos verificar o terminal para saber como esta sendo passado o parametro state_id;

2) Veja que ao abrir a url do sistema e modificar o estado e cidade os mesmos não se mantem na página ao pressionar o botão de criar ou atualizar;

3) Adicione: attr_accessor :state_id no model User e em seguida faça a seguinte mudança na partial form de User:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
###### Antes
  <div class="field">
    <%= f.label :state %><br />
    <%= collection_select(:state ,:id, State.all, :id, :name, :include_blank => 'SELECT A STATE') %>
  </div>
    $(document).ready(function() {
   $("#state_id").change(function() {
      getCitiesByState("id="+$("#state_id").val());
    });
 });

 ##### Depois
   <div class="field">
    <%= f.label :state %><br />
    <%= f.collection_select :state_id, State.all, :id, :name, :selected => @user.state_id, :include_blank => 'SELECT A STATE' %>
  </div>
    $(document).ready(function() {
   $("#user_state_id").change(function() {
      getCitiesByState("id="+$("#user_state_id").val());
    });
 });

Com essas modificações podemos observar que agora o estado se mantem ao selecionar um no combobox, mas a cidade não. Veja que foi adicionado um parametro no collection, então essa sera uma das soluções para o caso cidade, vamos la. Criamos um helper e logo em seguida alteramos o form com o seguinte código:

1
2
3
4
5
6
7
8
9
10
11
12
13
###helper
  def cities(state)
      City.where("state_id = ?", state)
  end
###form
  <div class="field">
    <%= f.label :city %><br />
    <% if @user.state_id.present? %>
      <%= f.collection_select :city_id, cities(@user.state_id), :id, :name, :selected => @user.city_id, :include_blank => 'SELECT A CITY'%>
    <% else %>
      <%= f.collection_select :city_id, [], :id, :name, :selected => @user.city_id, :include_blank => 'SELECT FIRST STATE' %>
    <% end %>
  </div>

Essa opção :selected fará com que ao abrir a página se existir a cidade ela venha selecionada no combobox. Com isso concluimos a parte de cadastro.

Agora podemos verificar que na parte de edição nada aparece, então ja que sabemos para que serve o :selected vamos as modificaçãoes:

1
2
3
4
5
6
7
8
9
  <div class="field">
    <%= f.label :state %><br />
    <%= f.collection_select :state_id, State.all, :id, :name, :selected => @user.city.present? ? @user.city.state_id : @user.state_id, :include_blank => 'SELECT A STATE' %>
  </div>

  <div class="field">
    <%= f.label :city %><br />
      <%= f.collection_select :city_id, cities(@user.city.present? ? @user.city.state_id : @user.state_id), :id, :name, :selected => @user.city_id, :include_blank => @user.city.present? || @user.state_id.present? ? 'SELECT A CITY' : 'SELECT FIRST STATE' %>
  </div>

Agora cabe a você fazer as modificações necessárias. O código esta no github. Simple e fácil lol.

Exibindo Versao Ruby Gemset E Branch Atual No Terminal

| Comments

Se você é como eu e vive se perdendo sem saber em qual versão do ruby, ou qual gemset ou ate mesmo branch você esta, seus problemas acabaram-se

Abra o terminal, execute o seguinte comando: sudo vim ~/.bashrc, e adicione a linha logo abaixo no final do arquivo:

export PS1='[\u@\h]\[\033[32m\]\w \[\033[1;33m\]$(rvm-prompt i v)\[\033[00m\]@\[\e[0;34m\]$(rvm gemset name) \[\033[0;31m\]$(__git_ps1 "(%s)")\[\033[00m\] '

Simples e fácil lol.

Um Mes De Rio De Janeiro

| Comments

Nossa passou bem rápido, novo trabalho, casa nova e cidade maravilhosa. Com a mudança acabou vindo. Vamos ver se consigo completar todas até o fim do ano.

Saudade bem grande da familia e namorada lol, mas ta dando pra aguentar. Uma coisa que posso garantir é que não quero nem saber do calor de Teresina kkkkkkkk.

O pior é que não acredito que moro a 3 min da praia e ate hoje não fui, mas tambem fim de semana to acordando as 5 da tarde.

Depois da ida ate o Costão - Niteroi, a galera da Helabs marcou uma corrida de Kart, irei aproveitar pra conhecer outro cantindo no Rio.

Bom é isso ai, fuiz.

Url Amigavel Sem Plugin E Callback

| Comments

Certamente muito interessante este post do Cairo Noleto. Depois de uma explicação dele comecei a usar em alguns projetos que fiz recentemente. Na verdade depois de encher muito o saco dele com minhas perguntas.

Simples e fácil lol.

Criando Uma App Com Devise CanCan E Bootstrap

| Comments

Recentemente surgiu essa dúvida no GuruPI. Para tentar ajudar aqueles que estão começando, bem como meu colega que estava com a dúvida, resolvi criar esse post e colocar a app criada no Github.

Sobre as gems usadas no post

Devise

Uma gem bastante fexível para autenticação em aplicações web, bem como genrenciamento dos dados do usuário.

CanCan

Gem que irá nos permitir fazer o gerenciamento de permissões para cada usuário do sistema.

Twitter Bootstrap Rails

Esta gem nos ajudará a reconfigurar toda a estrutura de layout padrão do Rails, nos dando uma visualização mais agradável da aplicação atraves do bootstrap.

Seguindo com o nosso post, essa será mais ou menos a estrutura de nossa aplicação:

1
2
3
4
5
6
7
8
9
User
  belongs_to :role

Role
  has_many :users
  has_and_belongs_to_many :permissions

Permission
  has_and_belongs_to_many :roles

Vamos criar nossa aplicação com o seguinte comando:

rails new exemplo_cancan_e_devise

Depois de criar vamos deletar o seguinte arquivo public/index.html

rm public/index.html

Agora colocaremos as gems que iremos usar no arquivo Gemfile:

1
2
3
4
gem 'devise'
gem 'cancan'
gem 'commonjs', '= 0.2.0' # essa foi pq deu um probleminha quando coloquei a gem do bootstrap
gem 'twitter-bootstrap-rails'

Com isso nossa app sabe que queremos usar essas gems, então vamos executar o seguinte comando, para podermos usar os geradores de cada gem:

bundle install

Eu iriei fazer um breve comentário sobre cada comando usado de cada gem aqui, para se aprofundar mais você deve ir ate a url colocada no inicio do post de cada gem.

Esse comando irar criar um arquivo com o nome devise.rb, dentro de config/initializers com as configurações padrões do devise e colocará devise_for :users, dentro do arquivo config/routes, assim teremos todas as rotas do devise.    
rails generate devise:install

Esse irá criar um model com as definições do devise, bem como uma migration com os campos padrões do devise
rails generate devise user

Comando que executa todas as migrations(nada mais é que uma forma organizada de criar tabelas, deletar, alterar ou criar novos campos no banco de dados).
rake db:migrate

Irá gerar a views usadas pelo devise dentro de app/views/devise
rails generate devise:views

Como esse comando vamos criar um controller dentro da pasta app/controllers, assim poderemos acessar alguns dados do usuário nas views que o devise nao cria.
rails g controller users

Aqui o cancan cria uma classe dentro da pasta models com o nome ability, essa classe irá manter, gerenciar as permissões em cada usuário
rails g cancan:ability

Com o comando scaffold eu posso criar um CRUD rapidamente, passando o nome e logo em seguida os campos com seus tipos.
Aqui ele cria um controller um model uma migration com o campo name do tipo string
rails g scaffold roles name:string

O paramentro role:references irar criar um campo role_id do tipo inteiro dentro da tabela e adiciona belongs_to role, dentro do model Permission
rails g scaffold permissions action:string subject_class:string role:references

rake db:migrate

Com isso vamos colocar os arquivos js, bem como suas chamadas dentro do arquivo application.js encontrado em app/assets/javascritps e de css em app/assets/stylesheets do boostrap.
rails g bootstrap:install

Aqui estou criando um arquivo com nome application com o css do boostrap dentro de views/layouts
rails g bootstrap:layout application fixed

Esses comando da gem bootstrap ira varer as tabelas e inserir dentro das views de cada pasta passada, dentro de app/views, substituindo as tags em html e css, colocando as do boostrap em cada campo da tabela.
rails g bootstrap:themed Users
rails g bootstrap:themed Roles
rails g bootstrap:themed Permissions

Agora vamos criar 2 migrations uma para adicionar role_id dentro da tabela users e a outra para fazer o gerenciamento de muitos para muitos entre Permission e Role.

rails g migration add_role_id_to_users
rails g migration create_table_permissions_roles

Abra os arquivos que se encontram em db/migrate e modifique-os:

1
2
3
def change
  add_column :users, :role_id, :integer #adicionando coluna role_id do tipo inteiro na tabela users
end
1
2
3
4
5
create_table :permissions_roles, :id => false do |t|
  t.integer :permission_id
  t.integer :role_id
end
#criando a tabela permissions_roles, o id => false, estou dizendo para o rails nao criar a tabela com o campo id, e lembrando que nossa tabela muitos para muitos o nome deve ser em ordem alfabetica dos models

Execute novamente:

rake db:migrate

Depois de tudo isso feito nossa aplicação esta quase pronta, faltando apenas algumas validações e modificações no layout e a configuração do arquivo ability.

Começaremos pelas validações e relacionamentos, como existia a necessidade de termos a permissão para cada view no banco, apareceu o relacionamento muitos para muitos entre Permission e Role, logo vamos deixa-los assim:

1
2
3
4
5
6
class Permission < ActiveRecord::Base
  has_and_belongs_to_many :roles

  validates_uniqueness_of :action, :scope => :subject_class
  validates_presence_of :action, :subject_class
end
1
2
3
4
5
6
7
class Role < ActiveRecord::Base
  has_many :users, :dependent => :restrict
  has_and_belongs_to_many :permissions

  validates_presence_of :name, :permissions
  validates_uniqueness_of :name
end

Aproveitando vamos colocar também no model User.

1
2
3
4
5
6
7
8
9
10
11
class User < ActiveRecord::Base
  # Include default devise modules. Others available are:
  # :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable

  # Setup accessible (or protected) attributes for your model
  attr_accessible :email, :password, :password_confirmation, :remember_me, :role_id
  validates_presence_of :role_id
  belongs_to :role
end

Explicando:

Com o has_and_belongs_to_many, estou dizendo que existe um relacionameto muitos para muitos.

has_many :users, :dependent => :restrict, aqui estou dizendo que Role pode ter muitos usuários, mas que se existir algum usuário com um role, esse role nao pode ser deletado(:dependent => :restrict).

belongs_to aqui entende-se que dentro dessa tabela que o usa nos temos um campo id de outra tabela.

validates_uniqueness_of :action, :scope => :subject_class, aqui eu estou dizendo que o campo action deve ser unico e passando :scope eu faço uma verificação de exclusividade com o campo subject_class, assim eu nao vou poder ter 2x action index para subject_class User. Isso simplesmente irá evitar de termos 2x a mesma permissão dentro de Role.

validates_presence_of aqui ele verifica se o campo foi preenchido.

Assim como o cancan, o :dependent => :restrict gera uma exception, fazendo com que a aplicação não tenha um coportamento adequado, para ajustarmos isso, adicionamos os seguintes códigos dentro de app/controllers/application_controller.rb

1
2
3
4
5
6
7
  rescue_from CanCan::AccessDenied do |exception|
    redirect_to root_url, :alert => exception.message
  end

  rescue_from ActiveRecord::DeleteRestrictionError do |exception|
      redirect_to :back, :alert => exception.message
  end

Agora vamos até o arquivo app/models/ability.rb e modifica-lo:

1
2
3
4
5
6
7
8
9
10
11
12
class Ability
  include CanCan::Ability

  def initialize(user)
  can :manage, :all if user.role.name == 'admin'
    user.role.permissions.each do |permission|
      can permission.action.to_sym, permission.subject_class.constantize do
          user.id.nil? || user.role_id?
      end
    end
  end
end

O que basicamente ele faz é verifica se o role do usuario logado é admin, se for o usuário tera todas as permissões na aplicação, agora senão for admin, ele irá verificar as permissões do usuário para cada action em cada model.

Agora vamos fazer as modificações na view devise/registration/edit.html.erb e new.html.erb, para podermos modificar e adicionar o role do usuário, adicione o seguinte trecho:

1
2
3
4
5
6
<div class="control-group">
  <%= f.label :role_id, :class => 'control-label' %>
  <div class="controls">
    <%= f.collection_select :role_id, roles, :id, :name, :include_blank => "--- Select ---" %>
  </div>
</div>

Hum têm alguns helpers que devem ser criados, vamos ao arquivo app/helpers/application_helper.rb e coloque isso:

1
2
3
4
5
6
7
  def get_model_names_sub
      Dir['app/models/*.rb'].map {|f| File.basename(f, '.*').camelize.constantize.name }.reject!{|m| m.constantize.superclass != ActiveRecord::Base }
  end

  def roles
  Role.all
  end

O primeiro me retorna todos os Models da aplicação e o segundo todos os roles.

Seguindo a leitura da dúvida do meu colega, tem-se uma imagem, como eu não queria criar uma tabela para fazer os gerenciamentos dos models da app, pensei em usar somente um metodo para fazer isso é aqui que entra o helper get_model_names_sub lol.

Abrimos o arquivo _form.html.erb da pasta views/roles e modificamos:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<%= form_for @role, :html => { :class => 'form-horizontal' } do |f| %>
  <fieldset>
    <legend><%= controller.action_name.capitalize %> /Role</legend>

    <div class="control-group">
      <%= f.label :name, :class => 'control-label' %>
      <div class="controls">
        <%= f.text_field :name, :class => 'text_field' %>
      </div>
    </div>

  <% get_model_names_sub.each do |models_names| %>    
    <div class="control-group">
      <%= field_set_tag models_names do %>
        <% Permission.where(:subject_class => models_names).each do |permission| %>
          <label class="checkbox">
            <%= check_box_tag 'role[permission_ids][]', permission.id, @role.permissions.include?(permission) %>
            <%= permission.action %>      
          </label>
        <%end%>
      <%end%>
    </div>
  <%end%>

    <div class="form-actions">
      <%= f.submit nil, :class => 'btn btn-primary' %>
      <%= link_to 'Cancel', roles_path, :class => 'btn' %>
    </div>
  </fieldset>
<% end %>

Ficou um pouco feio, mas o post ja estava muito grande, então vamos seguindo.

Eu criei um arquivo seed, então é so dar uma olhada aqui, para os que não sabem esse arquivo serve para popular o banco de dados e fica dentro da pasta db. O comando é o seguinte:

rake db:seed

Ah falta modificar o menu, abra o arquivo app/layouts/application.html.erb:

1
2
3
4
5
6
7
8
9
10
11
        <% if user_signed_in? %>
          <ul class="nav">
            <li><%= link_to "Users", users_path if can? :read, 'User' %></li>
            <li><%= link_to "Roles", roles_path if can? :read, 'Role' %></li>
            <li><%= link_to "Permissions", permissions_path if can? :read, 'Permission' %></li>
          </ul>
          <ul class="nav pull-right">
            <li><%= link_to current_user.email, edit_user_registration_path %></li>
            <li><%= link_to "Logout", destroy_user_session_path, :method => :delete %></li>
          </ul>
        <%end%>

Assim o menu só será exibido se o usuário tiver permissão, também ficou faltando adicionar load_and_authorize_resource dentro dos controllers, sem ele o cancan não vai poder fazer as verificações de permissão.

E finalizando o nosso controller de users:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class UsersController < ApplicationController
  
 before_filter :authenticate_user!
 load_and_authorize_resource

 def index
    @users = User.all

    respond_to do |format|
      format.html # index.html.erb
      format.json { render json: @users }
    end
  end

  # GET /users/1
  # GET /users/1.json
  def show
    @user = User.find(params[:id])

    respond_to do |format|
      format.html # show.html.erb
      format.json { render json: @user }
    end
  end

end

Teve tambem essa pequena adição no arquivo app/views/layouts/application.html.erb:

1
2
3
<% flash.each do |name, msg| %>
    <%= content_tag :div, msg, :class => "alert alert-#{name}" %>
<% end %>

Essa dentro do arquivo app/assets/javascript/boostrap_and_overrides.css.less:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.alert-notice {
    background-color: #DFF0D8;
    border-color: #D6E9C6;
    color: #468847;
}

.form-horizontal .control-group:before, .form-horizontal .control-group:after {
    content: "";
    display: inline !important;
}

.radio, .checkbox {
  display: inline-block;
}

E essa dentro do config/application.rb:

1
2
3
4
5
6
7
8
9
ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|
    if html_tag =~ /<input/ || html_tag =~/<select/
        %|<div class="control-group error">#{html_tag} <span class="help-inline">#{[instance.error_message].join(', ')}</span></div>|.html_safe
    elsif html_tag =~ /<label/
        %|<div class="control-group error">#{html_tag}|.html_safe
    else
        html_tag
    end
end

Essa é parra mudar as mensagens de validação, as outras são mudanças no comportamento do css.

Simples e fácil lol, o código esta aqui, espero não ter esquecido de nada.

Dando Uma Limpada No Log No Modo De Desenvolvimento Do Rails

| Comments

Ah não queria esperar até sexta-feira para um novo post, então vamos lá.

Este é um post bem curto basicamente só iremos adicionar 2 gems no grupo development, aqui eu coloco o link para você pegar mais informações sobre as gems.

THIN, que irá remover essa parte:

[2012-04-19 20:37:38] WARN  Could not determine content-length of response body. Set content-length of the response or set Response#chunked = true

E a QUIET ASSETS, que irá remover essa:

Started GET "/assets/comments.js?body=1" for 127.0.0.1 at 2012-04-19 20:38:20 -0300
Served asset /comments.js - 304 Not Modified (1ms)

Depois de dar uma lida sobre as gems, abra seu arquivo Gemfile e adicione as gems assim:

1
2
3
4
group :development do
  gem 'thin'
  gem 'quiet_assets'
end

Agora seu log de development esta mais limpo, simples e fácil lol.

Continuando a Saga Sobre Ajax E Rails

| Comments

Bom pessoal, demorei um pouco a fazer outro post devido ter me mudado para o Rio de Janeiro lol, trabalhando aqui agora. Não sei se posso falar a empresa que estou agora, ah vou falar rs. Bom fui contratado pela Helabs empresa do Sylvestre Mergulhão e Rafael Lima. Não tenho nem palavras para expressar a felicidade, que dizer tenho sim “lol”. Será um experiência única então vamos ao post.

Antes de começar esse post você deve seguir esse aqui e esse outro

Depois de completar os 2 posts acima, iremos colocar nosso projeto ajax e rails para exibir as mensagens de erro ao criar um post, bem como a mensagem flash. Não é nada muito complicado so iremos fazer uma simples condição no arquivo /views/post/create.js.erb, adicionar uma nova div no form que irá exibir a mensagem de erro, adicionar a div para flash_notice na index de post e uma pequena alteração no controller de post ao criar um.

Vamos comerçar com a parte de mensagem de erro, hum bom vamos fazer tudo logo, assim era como estava o arquivo create.js.erb de post:

1
2
3
$('#comments').prepend('<%= escape_javascript(render(@comment)) %>');
$('#comments > li').first().effect('highlight', {color: 'cyan', mode: 'show'}, 2000);
$('#comment_form > form')[0].reset();

Alteramos para:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<% if @post.errors.any? -%>
  /*Hide the flash notice div*/
  $('#flash_notice').hide(300);

  /*Update the html of the div post_errors with the new one*/
  $('#post_errors').html('<%= escape_javascript(error_messages_for(@post))%>');

  /*Show the div post_errors*/
  $('#post_errors').show(300);
<% else -%>
  /*Hide the div post_errors*/
  $('#post_errors').hide(300);

  /*Update the html of the div flash_notice with the new one*/
  $('#flash_notice').html('<%= escape_javascript(flash[:notice])%>');

  /*Show the flash_notice div*/
  $('#flash_notice').show(300);

  $('#posts').prepend('<%= escape_javascript(render(@post)) %>');
  $('#posts > li').first().effect('highlight', {color: 'cyan', mode: 'show'}, 2000);
  $('#post_form > form')[0].reset();

<% end -%>

Aqui eu ja inclui a parte para exibir a mensagem flash, veja que agora nós temos uma condição, que verifica se algum erro foi lançado, se existe ele dar um hide na div de flash_notice, atualiza nossa div de post_erros com o helper que eu criei aqui e exibe-a em seguida, senão ele da um hide na div de post_erros, atualiza a div da mensagem flash, mostra o objeto criado e limpa o formulário.

Com nosso arquivo create.js.erb no ponto, vamos adicionar as divs nas views, primeiro abra o form de post e deixe-o assim:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<%= form_for(@post, :remote => true) do |f| %>

  <div id= "post_errors" style="display:none"></div>

  <div class="field">
    <%= f.label :title %><br />
    <%= f.text_field :title %>
  </div>
  <div class="field">
    <%= f.label :email %><br />
    <%= f.text_field :email %>
  </div>
  <div class="field">
    <%= f.label :content %><br />
    <%= f.text_area :content %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

Nossa única alteração foi a inclusão da div para exibir as mensagens de erro. Em seguida vamos abrir a index de post e incluir a div para a mensagem flash:

1
2
3
4
5
6
7
8
9
10
11
12
<div id="flash_notice" style="display:none"></div>

<h1>Posts</h1>
<div id="timer">Rendered at: <%= Time.now %></div> 
 
<div id="post_form">
  <%= render 'form' %>
</div>
 
<ul id="posts">
  <%= render :partial => @posts.reverse %>
</ul>

E finalizando abra o controller de post e deixei-o assim:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  def create
    @post = Post.new(params[:post])

    respond_to do |format|
      if @post.save
        format.html { redirect_to posts_url }
        format.json { render json: @post, status: :created, location: @post }
        format.js
        flash[:notice] = 'Post was successfully created.'
      else
        format.html { render action: "index" }
        format.json { render json: @post.errors, status: :unprocessable_entity }
        format.js
      end
    end
  end

Fiz uma pequena alteção, pois como estava antes não exibia a mensagem flash. Agora você ja pode verificar se esta exibindo ou não as mensagens de erro e mensagem flash, lembrando que você deve seguir esse aqui e esse outro, antes de começar aqui.

Simples e fácil lol