quinta-feira, 28 de abril de 2011

Instalando Nginx e Unicorn no Rails 3

Nginx e Unicorn

    O Nginx com o Unicorn é uma ótima solução para colocar seu projeto em produção. É bem fácil a instalação deles. Eu utilizo essa combinação na minha máquina para testar meus projetos que desenvolvo. Quer saber mais sobre o que os dois fazem ? Vai pesquisar no Google, eu vou mostrar só a implementação básica.

        sudo apt-get install nginx
   
    Para poder usar o Nginx e o Apache sem ter que desligar um deles é só mudar a porta padrão do Nginx. Abra o arquivo default do Nginx para mudar a porta.
   
        sudo nano /etc/nginx/sites-available/default
   
    Comente a linha:
   
        #listen   [::]:80 default ipv6only=on; ## listen for ipv6
   
    Modifique a porta, eu coloco 8080:
   
        listen   8080; ## listen for ipv4
   
    Crie um arquivo nesta mesma página com um nome que identifique o site que está fazendo.
   
        sudo nano sua_app
       
    O meu arquivo está assim:

        upstream app_server {
            # fail_timeout=0 means we always retry an upstream even if it failed
            # to return a good HTTP response (in case the Unicorn master nukes a
            # single worker for timing out).

            # for UNIX domain socket setups:
            server unix:/tmp/.sock fail_timeout=0;

            # for TCP setups, point these to your backend servers
            # server 192.168.0.7:8080 fail_timeout=0;
            # server 192.168.0.8:8080 fail_timeout=0;
            # server 192.168.0.9:8080 fail_timeout=0;
          }

          server {
            # enable one of the following if you're on Linux or FreeBSD
            listen 8080 default deferred; # for Linux
            # listen 80 default accept_filter=httpready; # for FreeBSD

            client_max_body_size 4G;
            server_name sua_app.local;
           
            # ~2 seconds is often enough for most folks to parse HTML/CSS and
            # retrieve needed images/icons/frames, connections are cheap in
            # nginx so increasing this is generally safe...
            keepalive_timeout 5;

            # path for static files
            root /home/seu_usuario/seu_caminho/sua_app/public;

            # Prefer to serve static files directly from nginx to avoid unnecessary
            # data copies from the application server.
            #
            # try_files directive appeared in in nginx 0.7.27 and has stabilized
            # over time.  Older versions of nginx (e.g. 0.6.x) requires
            # "if (!-f $request_filename)" which was less efficient:
            # http://bogomips.org/unicorn.git/tree/examples/nginx.conf?id=v3.3.1#n127
            try_files $uri/index.html $uri.html $uri @app;

            location @app {
              # an HTTP header important enough to have its own Wikipedia entry:
              #   http://en.wikipedia.org/wiki/X-Forwarded-For
              proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

              # enable this if and only if you use HTTPS, this helps Rack
              # set the proper protocol for doing redirects:
              # proxy_set_header X-Forwarded-Proto https;

              # pass the Host: header from the client right along so redirects
              # can be set properly within the Rack application
              proxy_set_header Host $http_host;

              # we don't want nginx trying to do something clever with
              # redirects, we set the Host: header above already.
              proxy_redirect off;

              # set "proxy_buffering off" *only* for Rainbows! when doing
              # Comet/long-poll stuff.  It's also safe to set if you're
              # using only serving fast clients with Unicorn + nginx.
              # Otherwise you _want_ nginx to buffer responses to slow
              # clients, really.
              # proxy_buffering off;

              proxy_pass http://app_server;
            }

            # Rails error pages
            error_page 500 502 503 504 /500.html;
            location = /500.html {
              root /home/seu_usuario/seu_caminho/sua_app/public;
            }
          }


    Os pontos importantes desse arquivo são:
       
        # para definir a porta.
        listen 8080 default deferred;
       
        # definição do host chamado do navegador.
        server_name sua_app.local;
       
        # caminho estático da sua app.
        root /home/seu_usuario/seu_caminho/sua_app/public;
       
    Crie um link simbólico em sites-enabled:
   
        cd /etc/nginx/sites-enabled/
        sudo ln -s /etc/nginx/sites-available/sua_app sua_app
       
    Defina o nome da sua app no arquivo hosts:
       
        sudo nano /etc/hosts
   
    Dentro do arquivo:
   
        127.0.0.1   sua_app.local
   
    Start no nginx:
       
        sudo /etc/init.d/nginx start
       
    Teste o endereço com http://sua_app.local:8080/, vai dar uma mensagem. Vamos instalar o Unicorn. No arquivo Gemfile de sua app, adicione:
       
        gem 'unicorn', '3.5.0'
       
     Depois rode o 'bundle install' no terminal. Em 'config/' crie um arquivo 'unicorn.rb' e coloque o conteúdo abaixo conforme sua máquina:
    
        # See http://unicorn.bogomips.org/Unicorn/Configurator.html for complete
        # documentation.
        worker_processes 2
        # Help ensure your application will always spawn in the symlinked
        # "current" directory that Capistrano sets up.
        working_directory "/home/deployer/myapp.com/current"
        # listen on both a Unix domain socket and a TCP port,
        # we use a shorter backlog for quicker failover when busy
        listen "/tmp/myapp.com.sock", :backlog => 64
        listen 8080, :tcp_nopush => true
        # nuke workers after 30 seconds instead of 60 seconds (the default)
        timeout 30
        # feel free to point this anywhere accessible on the filesystem
        user 'deployer', 'staff'
        shared_path = "/home/deployer/myapp.com/shared"
        pid "#{shared_path}/pids/unicorn.pid"
        stderr_path "#{shared_path}/log/unicorn.stderr.log"
        stdout_path "#{shared_path}/log/unicorn.stdout.log"
       
     Esse é o arquivo default dele encontrado aqui. Para colocar o unicorn em produção é só rodar essa linha no terminal dentro da sua app:
    
        unicorn_rails -c config/unicorn.rb -E production -D
       
     Para matar ele é só rodar:
    
        kill -quit `cat tmp/pids/unicorn.pid`
       
     É só isso, bem tranquilo é rápido, se tiver alguma dúvida comenta que eu ajudo no que puder.
     Até a próxima!!

terça-feira, 19 de abril de 2011

Ajax no Rails 3

    Era para eu falar neste post sobre teste de controller com RSpec, mas estava fazendo alguns testes com Ajax, então aproveitei o momento para falar de Ajax. Desculpe a demora por novos posts, estava em período de entrega de trabalhos e provas na faculdade, não deu tempo de preparar nada. Todos os códigos que faço aqui estão no Github(github.com/vagnerzampieri/zblog).
    Pegue aquele CRUD que fizemos sobre Post, ele vai ser modificado e dará lugar a documentos com extensão '.js.erb'. A ideia é que todas as ações fiquem na index, sendo assim, ficará um carregamento mais rápido e uma página mais dinâmica.
    Primeiro coloque a gem 'jquery-rails' no arquivo Gemfile da sua aplicação. A versão que estou usando está com o jquery 1.5.
        gem 'jquery-rails', '0.2.7'
    Logo em seguida, vá em um terminal e rode o 'bundle install'. Agora rode no terminal 'rails generate jquery:install', ele irá substtuir o prototype pelo jquery.
    Primeiro, vamos começar modificando a criação de post. Ele vai ficar da seguinte forma, ao clicar no link 'New' irá carregar abaixo um formulário, ao ser preenchido e enviado irá aparecer na lista o item que foi criado.
    Na index, vou modificar a tabela e colocar um id, esse id irá servir depois para identificar aonde irá adicionar na lista o post que for criado.
        posts/index.html.erb:
       
        <table id="posts">
       
    Crie um partial que irá virar uma lista dinâmica, já que será muito utilizada no ajax:
        posts/_post.html.erb:
       
        <tr id="post_<%= post.id %>">
          <td><%= post.title %></td>
          <td><%= truncate post.body, :length => 20 %></td>
          <td><%= post.publication.strftime "%Y-%m-%d %H:%M:%S" %></td>
          <td><%= post.enabled %></td>
          <td><%= link_to 'Show', post, :remote => true %></td>
          <td><%= link_to 'Edit', edit_post_path(post), :remote => true %></td>
          <td><%= link_to 'Destroy', post, :confirm => 'Are you sure?', :method => :delete, :remote => true %></td>
        </tr>
       
    Note que coloquei um id dinâmico no 'tr', e nos liks adicionei ':remote => true' é ele que irá fazer a mágica no Ajax, coloque ele também no 'form'.
        <%= simple_form_for(@post, :remote => true) do |f| %>
   
    Na index coloque um render para esse partial.
        posts/index.html.erb:
       
        <% @posts.each do |post| %> 
          <%= render post%>
        <% end %>
       
    Adicione também o link 'New' um pouco abaixo:
        <%= link_to 'New', '/posts/new', :remote => true %>
       
    E uma div onde será chamado o formulário:
        <div id="form">
        </div>
       
    Crie um arquivo 'new.js.erb' e dentro dele coloque o código abaixo:
   
        $('#form').empty();
        $("#form").append("<%= escape_javascript(render(:partial => "form"))%>");
       
    Esse código é bem simples, a primeira linha vai deixar vazio a div 'form', por um simples motivo, se você não colocar essa linha sempre que clicar em um botão ele vai criar um novo elemento em vez de excluir o antigo. A segunda linha chama o partial do 'form'. Crie um arquivo 'create.js.erb' e adicione o código abaixo:
   
        $('#posts').append('<%= escape_javascript(render(@post)) %>');
        $("#new_post")[0].reset();
       
    A primeira linha chama o partial 'post' que nós criamos e adiciona mais um elemento na lista. A segunda limpa o formulário para poder ocorrer mais um cadastro. Para finalisar, melhore o controller assim:
        def create
          @post = Post.new params[:post]

          flash[:notice] = 'Post was successfully created.' if @post.save
          respond_with @post
        end
    
    Agora o 'Show', mais acima irá notar que o link para o 'Show' já fi criado. Adicione a div abaixo na index, ela será o guia que utilizaremos para a aparição do show.
       
        <div id="show"> 
        </div>
       
    Crie um arquivo 'show.js.erb' e adicione o código abaixo:
       
        $('#show').empty();
        $('#show').append('<h2>Show post</h2><p><b>Title: </b><%= @post.title %></p><p><b>Body: </b><%= @post.body %></p><p><b>Publication: </b><%= @post.publication.strftime "%Y-%m-%d %H:%M:%S" %></p><p><b>Enabled: </b><%= @post.enabled %></p>');
   
    Como acontece no 'New' a primeira linha é para limpar se existir algo antes. A segunda irá exibir o item ao ser clicado, o append vai exibir tudo que estiver dentro dele. O próximo é o destroy, é o mais simples de todos, crie o arquivo 'destroy.js.erb' e adicione:
   
        $('#post_<%= @post.id %>').remove();
       
    Vai remover a linha exata que você destruir. O último é editar, é bem parecido que o criar, crie os arquivos 'edit.js.erb' e 'update.js.erb':
        posts/edit.js.erb:
       
        $('#form').empty();
        $("#form").append("<%= escape_javascript(render(:partial => "form"))%>")
       
    Mesma explicação do 'New'.
   
        posts/update.js.erb:
       
        $("#post_<%= @post.id %>").replaceWith("<%= escape_javascript(render(:partial => @post)) %>");
        <% @post = Post.new %>
        $(".simple_form").replaceWith("<%= escape_javascript(render(:partial => "form"))%>")
       
    Primeira linha, vai adicionar o elemento criado na lista. Segundo e terceira linha, vai resetar a variável de instância para ao editar o formulário, se modifique de edição para criação. Deixe o método update no controller da forma abaixo:
   
        def update
          @post = Post.find params[:id]

          flash[:notice] = 'Post was successfully updated.' if @post.update_attributes params[:post]
          respond_with @post
        end
       
    Pode apagar agora os arquivos abaixo, as suas funcionalidades não serão mais necessárias já que foram migradas.
        new.html.erb
        edit.html.erb
        show.html.erb
   
    Chega por hoje, ainda não pensei em um próximo post, talvez um banco de imagem com mais brincadeiras com Ajax.
    Até a próxima!!