segunda-feira, 14 de março de 2011

Otimizando seu controller

  Hoje vou voltar um pouco para o scaffold de post que fiz, vou implementar coisas novas lá, deixar ele com uma "cara" melhor. O que já foi feito:
    - Scaffold de Post feito com a gem Simple Form.
    - Um Controller Home que vai simular uma página do site.
    - Implementação básica do Devise.
    - Gravatar.
  
  O projeto está aqui(github.com/vagnerzampieri/zblog).
 
  Primeiro vamos definir visualmente o que seria a capa do site da administração do site, o scaffold post simula o começo de uma administração, então ele precisa ter um layout modificado. Então vamos criar um layout específico para ele.
  Entre 'app/views/layouts/' e crie um arquivo chamado 'admin.html.erb', e copie o código abaixo:
 
  <!DOCTYPE html>
  <html>
    <head>
      <title>Blog</title>
      <%= stylesheet_link_tag :all %>
      <%= javascript_include_tag :defaults %>
      <%= csrf_meta_tag %>
    </head>
    <body>
      <container>
        <p class="notice"><%= notice %></p>
        <p class="alert"><%= alert %></p>

        <%= yield %>
      </div>
    </container>
  </html>
 
  Se já teve algum contato com HTML5 vai notar que é um layout do mesmo, o scaffold do rails já faz isso, o que tem de diferente que eu coloquei foi a tag "<container>". No 'app/views/layouts/application.html.erb' coloque esse mesmo HTMl, mas substitua o "<content>". Se você estiver se perguntando o que é "<%= yield %>" é aonde todas as telas que você criar irá entrar. Agora vamos diferenciar o layout no CSS.
 
  Em 'public/stylesheets/scaffold.css, ajuste a primeira linha para isso:
 
  body { background-color: #000; color: #000; }
 
  Depois adicione isto:
 
  th, tr, td {
    border: solid 1px black;
  }
 
  container, content {
    display:block;
  }
 
  container {
    width: 75%;
    margin: 0 auto;
    background-color: #ccc;
    padding: 20px 40px;
    border: solid 1px black;
    margin-top: 20px;
  }

  content {
    width: 75%;
    margin: 0 auto;
    background-color: #493233;
    padding: 20px 40px;
    border: solid 1px black;
    margin-top: 20px;
  }
 
  Agora no controller de 'app/controllers/posts_controller.rb', adicione na primeira linha depois que entrar dentro da classe:
 
  layout 'admin'
 
  Ele vai definir o layout que a página vai usar, o padrão é "application" mas eu quero o admin para "posts", então vá olhar como ficou a "home" e "posts". Eu não sou designer e uso layout escuro para não incomodar minha vista.
 
  Se não existir conteúdo em posts, adicione alguns, depois veja como ele está listando. Entre no código da view em "app/views/posts/index.html.erb" e no controller 'app/controllers/posts_controller.rb'. O que vou fazer agora é explicar código, otimizar e trazer para a realidade de Rails3.
 
  No método "index" faremos algumas modificações, esse método gera uma saída para a view "index.html.erb":
 
  def index
    @posts = Post.all

    respond_to do |format|
      format.html
      format.xml  { render :xml => @posts }
    end
  end
 
  No Ruby a última linha de um método, ou um bloco é seu retorno, o método respond_to é um bloco que gera duas saídas, uma em html e a outra em xml. O método "index" gera uma lista de todos os posts, essa lista é pega de sua tabela "posts", ela é acessada através da consulta "Post.all", que será atribuída para uma variável chamada "@posts", que é uma variável de instância, ela irá trazer em forma de array todos os posts. O "Post" é o model relativo a tabela posts. Vamos deixar esse código com uma cara de Rails3.
 
  Primeiro, no controller adicione a linha abaixo, logo depois de "layout 'admin'":
 
  respond_to :html, :xml, :js
 
  Depois modifique nosso método index para:
 
  def index
    @posts = Post.order "publication DESC"

    respond_with @posts
  end
 
  Esse novo "respond_to" vai gerar os mesmos formatos que antes mais o formato "js"(javascript), será utilizado depois quando fizermos saídas com ajax, é só passar os parâmetros que quiser. Na primeira linha do método eu modifiquei para que retorne todos os posts em ordem decrescente por data. E o "respond_with" é meu complemento do método "respond_to", eu passo como parâmetro a variável de instância "@posts". Se estiver achando desconfortável a falta de parênteses quando chamo um método, é só adicionar, o espaço simples substitue os parênteses.
 
  def index
    @posts = Post.where(:enabled => 1)

    respond_with(@posts)
  end
 
  Em 'app/views/posts/index.html.erb', note a nossa variável de instância "@posts", é passado um método "each" que irá acessar o array desta linha, e gerar a saída uma por uma de todos os conteúdos de dentro dele, através do seu bloco.
   
  Se colocou em "body" texto suficiente, notou que ficou estranho o layout, vamos formatar isso usando o método "truncate".
 
  <td><%= truncate post.body, :length => 20 %></td>
 
  Estou passando como parâmetros, o campo "body" e um tamanho de caracteres. Vou formatar a data também:
 
  <td><%= post.publication.strftime "%Y-%m-%d %H:%M:%S" %></td>
 
  Nos métodos, "show", "new", "edit" e "destroy", faça a modificação do "respond_with".
 
  Agora vamos otimizar o método create, por enquanto ele está assim:
 
  def create
    @post = Post.new params[:post]

    respond_to do |format|
      if @post.save
        format.html { redirect_to(@post, :notice => 'Post was successfully created.') }
        format.xml  { render :xml => @post, :status => :created, :location => @post }
      else
        format.html { render :action => "new" }
        format.xml  { render :xml => @post.errors, :status => :unprocessable_entity }
      end
    end
  end
 
  Ele pega os dados vindos do parâmetro "params[:post]", que vem do formulário que está em "app/views/posts/_form.html.erb". Se os parâmetros estiverem certos, ele salva e avisa que foi criado com sucesso, senão ele se matem na página que está. Vamos otimizar esse código. Outra coisa que irão notar é que eu coloquei :new e não "new", simples explicação, da primeira forma eu crio um símbolo e da segunda é uma string. Usando símbolos, o consumo de memória pode ser drasticamente reduzido, uma vez que você estará usando o mesmo objeto, sem necessidade de crias várias cópias, quando estiver em um projeto grande essas pequenas diferenças fazem diferença no tempo total de processamento.
 
  def create
    @post = Post.new params[:post]
   
    if @post.save
      flash[:notice] = 'Post was successfully created.'
      respond_with @post
    else
      render :action => :new 
    end
  end
 
  A ideia foi a mesma que já tinha usado, eu diminuo o código quando utilizo o "respond_with". Veja como vai ficar o "update".
 
  def update
    @post = Post.find params[:id]

    if @post.update_attributes params[:post]
      flash[:notice] = 'Post was successfully updated.'
      respond_with @post
    else
      render :action => :edit
    end
  end
 
  O método "update" pega o id do post em questão, armazena na variável @post, depois atualiza ele passando o parâmetro "params[:post]" que vem do formulário. Para você o "else" funcionar valide a presença dos campos "name" e "body", por exemplo. Faça em "app/models/post.rb"
 
  validates_presence_of :title, :body
 
  Para não deixar esse post grande vou encerrar aqui e falar sobre Rspec no próximo, iremos validar o modelo post.
 
  Até a próxima!!

Nenhum comentário:

Postar um comentário