Posts Tagged ‘Ruby on Rails’

Trânsito Não! Um experimento com Twitter, Trânsito e Ruby on Rails

Sunday, April 26th, 2009

O Twitter se tornou uma grande ferramenta para interação social, já até ameaçando o conteúdo oferecido em blogs.

Indo ainda mais longe, alguns já falam que não importa mais quantos seguidores você tenha no Twitter, mas que a pessoa com maior autoridade é aquela com o maior número de mensagens “retweetadas”, mostrando então que o que esta pessoa escreve é conteúdo bom e merece ser repassado a todos.

O projeto Trânsito Não! é um experimento social com Twitter e um assunto que deixa todo mundo fora de si: o trânsito. Este website conta tweets enviados como idéias e/ou votos, gerando um espaço público de discussão sobre um dos problemas mais urgentes destes dias.

Este projeto é direcionado a todo o Brasil, só que mais especialmente aquelas pessoas que vivem na Grande São Paulo, uma região com cerca de 17 milhões de habitantes que experimentam um dos maiores volumes de tráfego de veículos do mundo.

Usei Ruby on Rails para construir este site. Este framework web oferece grande produtividade ao desenvolvedor. Se você ainda não assistiu, não deixe de ver este vídeo que mostra a criação de um blog em apenas 15 minutos usando Ruby on Rails.

O sistema de votação funciona assim: você envia um tweet (no Twitter) com uma idéia ou solução para resolver o problema do trânsito. Esta idéia vira então uma “idéia” dentro do Trânsito Não! Cada retweet da sua idéia, vira um voto. Então quanto mais a sua idéia se espalhar no Twitter, mais votos ela recebe no site. Os tweets são obtidos do Twitter através de uma API que é lida a cada minuto. As idéias ou votos são adicionados ao banco de dados, e as páginas são geradas novamente.

Você pode me encontrar em meu blog, Twitter, ou por e-mail.

Transito Não! An experiment with Twitter, Traffic Jams and Ruby on Rails

Sunday, April 26th, 2009

Twitter has become a great tool for social interaction, and it’s also gaining more authority than content in blogs.

Going even further, some people say it doesn’t matter how many followers you have on Twitter, but instead what matters is how many times your tweets get forwarded by those following you, showing that this person writes more valuable content, which deserves to be sent over to other people.

The project Trânsito Não! (which stands for “No To Traffic!”) is a social experiment with Twitter and a subject that gets everyone mad: the traffic jams. This web app cast tweets as ideas and/or votes, generating a public web space to debate on one of the most urgent problems of these days.

This project is aimed at the Brazilian people, specially those people living in the greater area of Sao Paulo, a region with about 17 million people experiencing one of the biggest car traffics in the world.

I developed this website with Ruby on Rails. This web framework offers great productivity to the developer. If you haven’t the chance yet, don’t miss this video showing a complete web blog written in only 15 minutes using Ruby on Rails.

The voting works like this: you tweet some idea or solution for the traffic problems, followed by the hashtag #transitonao. This tweet becomes a “idea” on TransitoNao website. If your tweet gets retweeded X times, these X times become votes for your idea. So the longer your idea spreads, the more votes you get. New tweets are read from the Twitter API every minute, being added to the database, and the pages being also refreshed (using page caching).

You can find me on my blog, Twitter, or at my personal email.

Scaling Rails

Friday, February 6th, 2009

Gregg Pollack and New Relic just launched a Screencast series on Scaling Rails. And, it’s free! :)

“Scaling Rails Screencast Series” produced by Gregg Pollack and supported by New Relic.

“Learn everything you need to know about Scaling your Rails app through 13 informative Screencasts produced by Gregg Pollack with the support of New Relic. In the next few weeks we’re going to bring you 13 educational videos, teaching you just about everything you need to know to create a Rails application that can scale.”

Topics covered:

Tratando Múltiplos Models em Um Único Form

Tuesday, January 13th, 2009

O livro Advanced Rails Recipes é um livro muito interessante para quem quer aprender algumas dicas mais avançadas de Rails. Especialmente aquele tipo de dicas que você não encontra em tutoriais ou blogs por aí. Da descrição do site:

“Você irá aprender como os profissionais resolveram problemas complicados usando as técnicas mais atualizadas de Rails 2, para que você possa entregar a sua impressionante aplicação Web de forma mais fácil e mais rápida.”

Esta é uma tradução para o português, com autorização de Dave Thomas de Pragmatic Programers. Aproveite! :)

Tratando Múltiplos Models em Um Único Form

Por Ryan Bates (http://railscasts.com/). Ryan trabalha com desenvolvimento Web desde 1998. Começou a trabalhar profissionalmente com Ruby on Rails em 2005 e é mais conhecido pelo seu trabalho com os Railscasts, uma série gratuita de screencasts sobre Ruby on Rails.

Problema

A maioria do código Rails que você encontra geralmente trata um model de cada vez. Isso nem sempre é prático. Algumas vezes você precisa criar e/ou editar dois (ou mais) models em um único form, nas situações onde você tem uma associação one-to-many entre os models.

Solução

Digamos que nós precisamos salvar todas as tarefas pendentes para vários projetos. Quando nós criarmos ou atualizarmos um projeto, nós gostaríamos de adicionar, remover e atualizar suas tarefas, em um único form. O que estamos imaginando é:

Vamos começar criando um relacionamento has_many entre Project e Task. Para simplificar, vamos dar para cada model um atributo obrigatório chamado name.


# Arquivo: app/models/project.rb
class Project < ActiveRecord::Base
  has_many :tasks, :dependent => :destroy
  validates_presence_of :name
end

# Arquivo: app/models/task.rb
class Task < ActiveRecord::Base
  belongs_to :project
  validates_presence_of :name
end

Nós vamos usar a biblioteca de Javascript Prototype, então antes de qualquer coisa vamos garantir que ela está carregada em nosso arquivo de layout:


# Arquivo: app/views/layouts/application.html.erb
<%= javascript_include_tag :defaults %>

Vamos voltar a nossa atenção para o form, para criar um projeto com suas múltiplas tarefas, e associadas ao projeto. Quando precisamos tratar múltiplos models em um form, é muito útil eleger um model como sendo o foco primário ou principal, e a partir daí construir os models adicionais através da associação entre eles.

Neste caso, vamos fazer de Project o nosso model primário e construir suas tarefas através da associação has_many. Então para a action new de nosso ProjectsController, nós criamos um objeto Project da forma usual. No entanto, nós também inicializamos uma nova Task (em memória) que é associada com o Project de forma que nosso form tem alguma coisa para começar a trabalhar:


# Arquivo: app/controllers/projects_controller.rb
def new
  @project = Project.new
  @project.tasks.build
end

O template para o form precisa de algumas “manhas” já que nós precisamos tratar os campos para o model Project e também os campos de cada um dos models Task. Então, vamos quebrar o problema em partes menores e usar um partial para renderizar os campos da Task e adicionar um helper add_task_link para criar o link que adiciona uma nova tarefa:


# Arquivo: app/views/projects/_form.html.erb
<%= error_messages_for :project %>

<% form_for @project do |f| -%>
  <p>
  Name: <%= f.text_field :name %>
  </p>
  <div id="tasks">
    <%= render :partial => 'task', :collection => @project.tasks %>
  </div>
  <p>
    <%= add_task_link "Add a task" %>
  </p>
  <p>
    <%= f.submit "Submit" %>
  </p>
<% end -%>

Os templates new e edit simplesmente renderizam este partial de formulário, de forma que nós temos uma forma consistente para criar e atualizar um projeto. A partial do form vai e renderiza uma partial de tarefa para cada uma das tarefas do projeto. Antes de entrarmos no conteúdo da partial de tarefa, vamos dar uma olhada naquele helper add_task_link:


# Arquivo: app/helpers/projects_helper.rb
def add_task_link(name)
  link_to_function name do |page|
    page.insert_html :bottom, :tasks, :partial => 'task' , :o bject => Task.new
  end
end

Quando nós clicamos no link “Add a Task”, nós queremos um novo conjunto de campos de tarefas aparecendo abaixo dos campos de tarefa que já existem no formulário. Ao invés de ocupar o servidor com isto, nós podemos usar JavaScript para adicionar os campos dinamicamente. O método link_to_function aceita um bloco de código RJS. Geralmente nós associamos código RJS com chamadas assíncronas indo para o servidor. Mas neste caso o código RJS gera JavaScript que é executado no browser imediatamente quando o usuário clica no link. O resultado é que renderizar os campos para adicionar uma nova tarefa não requer uma requisição enviada ao servidor, o que leva a um tempo de resposta mais rápido no uso da aplicação.

Voltando ao partial do form, nós estamos usando form_for para dedicar o form para o model @project. Como então nós vamos adicionar campos para cada uma das tarefas do projeto? A resposta está dentro da partial de tarefa:


# Arquivo: app/views/projects/_task.html.erb
<div class="task">
<% new_or_existing = task.new_record? ? 'new' : 'existing' %>
<% prefix = "project[#{new_or_existing}_task_attributes][]" %>

<% fields_for prefix, task do |task_form| -%>
  <p>
    Task: <%= task_form.text_field :name %>
    <%= link_to_function "remove" , "$(this).up('.task').remove()" %>
  </p>
<% end -%>
</div>

O ingrediente chave aqui é o método fields_for. Ele se comporta de forma muito parecida com form_for mas ele não renderiza a tag HTML form. Isto nos permite mudar o contexto para um model diferente, no meio do form principal – como se nós estivéssemos embutindo um form dentro de outro.

O primeiro parâmetro para fields_for é muito importante. Esta string será usada como um prefixo para cada campo de tarefa. Como vamos usar esta partial também para renderizar tarefas existentes – e nós queremos mantê-las separadas quando o form é enviado – no prefixo nós incluímos uma indicação dizendo se a tarefa é nova ou existente. (O ideal seria criar esta string de prefixo em um helper, mas vamos simplificar um pouco as coisas aqui.)

O HTML gerado para uma nova tarefa se parece com isto:


<input name="project[new_task_attributes][][name]" size="30" type="text" />

Se esta fosse uma tarefa existente, o Rails iria colocar automaticamente o ID da tarefa entre as chaves, assim:


<input name="project[existing_task_attributes][7][name]" size="30" type="text" />

Agora, quando o form é enviado, o Rails irá decodificar o nome do campo de entrada, para forçar uma estrutura no hash params. As chaves ([]) que estão preenchidas se tornam keys em um hash aninhado. As chaves que estão vazias se tornam um array. Por exemplo, se nós enviarmos o form com duas novas tarefas, o hash params vai ficar assim:


"project" => {
  "name" => "Yard Work" ,
  "new_task_attributes" => [
    { "name" => "rake the leaves" },
    { "name" => "paint the fence" }
  ]
}

Note que os atributos para o projeto e cada tarefa estão aninhados dentro do hash project. Isto é conveniente porque significa que a action create em nosso controller pode simplesmente passar todos os atributos de projeto ao model Project sem se preocupar sobre o que está dentro do hash project:


# Arquivo: app/controllers/projects_controller.rb
def create
  @project = Project.new(params[:project])
  if @project.save
    flash[:notice] = "Successfully created project and tasks."
    redirect_to projects_path
  else
    render :action => 'new'
  end
end

Este código se parece com uma action create padrão, para um form de um único model. Mas existe algo sútil acontecendo aqui. Quando chamamos Project.new(params[:project]), o Active Record assume que nosso model Project tem um atributo correspondente chamado new_task_attributes porque ele procura uma key chamada new_task_attributes dentro do hash params[:project]. Isto é, o Active Record tentará fazer uma atribuição em massa (mass assign) de todos os dados contidos neste hash, para os atributos correspondentes no model Project. Mas nós não temos um atributo new_task_attributes em nosso model Project.

Um jeito conveniente de manter tudo isto transparente, da ponto de vista do controller, é usar um atributo virtual. Para fazer isso, nós simplesmente criamos um método setter em nosso model Project, chamado new_task_attributes=, que recebe um array e constrói a tarefa para cada elemento:


# Arquivo: app/models/project.rb
def new_task_attributes=(task_attributes)
  task_attributes.each do |attributes|
    tasks.build(attributes)
  end
end

Pode parecer que estas tarefas não estão sendo salvas em lugar nenhum. De fato, o Rails vai fazer isso automaticamente quando o projeto é salvo, porque ambos o projeto e suas tarefas associadas, são novos registros.

E isso é tudo que precisamos para criar um projeto. Vamos ver agora como atualizá-lo.

Assim como antes, nós precisamos de uma forma de adicionar e remover tarefas dinamicamente, mas desta vez se uma tarefa já existe, ela deve ser atualizada. As actions do controller só precisam se preocupar sobre o projeto, então elas são bem convencionais. Como antes, a atualização de tarefas será tratada no model Project:


# Arquivo: app/controllers/projects_controller.rb
def edit
  @project = Project.find(params[:id])
end

def update
  params[:project][:existing_task_attributes] ||= {}

  @project = Project.find(params[:id])
  if @project.update_attributes(params[:project])
    flash[:notice] = "Successfully updated project and tasks."
    redirect_to project_path(@project)
  else
    render :action => 'edit'
  end
end

Um detalhe importante: a primera linha da action update seta o parâmetro existing_task_attributes para um hash vazio, se ele já não está setado. Sem esta linha, não teria como deletar a última tarefa de um projeto. Se não existem campos de tarefa no form (porque nós removemos todos eles com JavaScript), então existing_task_attributes() não será setado pelo form, o que significa que o nosso método Project#existing_task_attributes= não será invocado. Setando um hash vazio aqui, se existing_task_attributes() está vazio, garante que o método Project#existing_task_attributes= é chamado ao deletar a última tarefa.

A partial de form não precisa de alterações. No entanto, quando nós submetemos o form com tarefas existentes, o hash params[:project] irá incluir uma key chamada existing_task_attributes. Isto é, quando nós atualizamos o projeto, os parâmetros do POST irão se parecer com isto:


"project" => {
  "name" => "Yard Work" ,
  "existing_task_attributes" => [
    {
      "1" => {"name" => "rake the leaves" },
      "2" => {"name" => "paint the fence" },
    }
  ]
  "new_task_attributes" => [
    { "name" => "clean the gutters" }
  ]
}

Para tratar isto, nós precisamos adicionar um método existing_task_attributes= ao nosso model Project, que irá receber cada tarefa existente e aí vai: ou atualizar a tarefa ou deletá-la, dependendo se os atributos são passados:


# Arquivo: app/models/project.rb
after_update :save_tasks

def existing_task_attributes=(task_attributes)
  tasks.reject(&:new_record?).each do |task|
    attributes = task_attributes[task.id.to_s]
    if attributes
      task.attributes = attributes
    else
      tasks.delete(task)
    end
  end
end

def save_tasks
  tasks.each do |task|
    task.save(false)
  end
end

Note que nós estamos salvando as tarefas em um callback chamado after_update. Isto é importante porque, diferente de antes, as tarefas existentes não serão automaticamente salvas quando o projeto for atualizado. E já que os callbacks são encapsulados em uma transação, se algum problema inesperado acontecer será feito um roll back.

Ao passar false para o método task.save, os dados são salvos sem passar pela validação. Ao invés disso, para garantir que todas as tarefas sejam validadas quando o projeto é validado, nós simplesmente adicionamos esta linha ao model Project:


validates_associated :tasks

Isto vai garantir que tudo é valido antes de salvar. E se a validação falha, então o uso de error_messages_for :project no template de formulário inclui os erros de validação para o projeto e qualquer uma de suas tarefas.

Então agora nós podemos criar e editar projetos e suas tarefas em uma tacada só. E ao usar atributos virtuais, nós mantemos o código do controller felizmente ignorante que nós estamos tratando vários models a partir de um único formulário.

Discussão

Uma vez que você começa a colocar mais de um model em um único formulário, você provavelmente vai querer criar um helper customizado para mensagens de erro, para fazer coisas como ignorar certos erros e detalhar outros. Veja a receita 17 "Customize as Mensagens de Erro", na página 91 para saber como escrever um método error_messages_for customizado. (Nota do tradutor: capítulo disponível no livro Advanced Rails Recipes)

Campos de data causam alguns problemas porque, por algum motivo, o Rails remove as chaves [] do nome do campo. Isto pode ser arrumado especificando manualmente a opção :index e setá-lo para uma string vazia se a tarefa é nova:


<%= task_form.date_select :completed_at,
:index => (task.new_record? ? '' : nil) %>

Infelizmente, campos do tipo checkbox não vão funcionar com esta receita porque o valor destes campos não é passado pelo browser quando o checkbox é desmarcado. Portanto, você não tem como saber a qual tarefa um checkbox pertence quando estiver criando um novo projeto. Para resolver este problema, você pode usar um menu select para campos booleanos:


<%= task_form.select :completed, [['No' , false], ['Yes' , true]] %>

Copyright (c) 2008 The Pragmatic Programmers, LLC

ASCIIcasts or RailsCasts made text

Tuesday, January 6th, 2009

Imagine you are doing something in Rails, and you need to remember or even learn how to it.

Maybe you, like me, will try to look at the Railscasts videos. But… searching something that Ryan Bates talked about during the video is not so easy to find. Or maybe you can’t watch a video at all, right now.

Here’s a solution. Eifion Bedford is creating text versions of the well-know screencasts at RailsCasts and posting at this new site called ASCIIcasts.

Great find.

Scaling microblogging services such as Twitter

Friday, December 19th, 2008

There’s a joke in the Ruby and Rails world that says ‘Rails doesn’t scale’, which it’s just a joke to really say: what is scallable is the way you create your app, and that does not depend whether is written in Rails, Perl or even Bash.

So it’s important to know what matters in terms of building a scallable web app. As you know:

Scalability: ability to either handle growing amounts of work in a graceful manner, or to be readily enlarged.

And how do you accomplish that in Ruby/Rails/whatever app you’re writing?

Well, one way to gain further knowledge on the subject is by reading this article. The author explains what is envolved when you need to scale a microblogging service such as Twitter. The author created Nouncer (a developer platform for building microblogs and similar services) and ended up learning quite a lot about the inner-workings and challenges of such an application.

Please do read it, it’s a series of 3 articles:

My thoughts? Scalling is not a solved subject, and it depends on the expertise of the developer/architect on finding ways to identify the fine grained causes for bottlenecks and to deal with them in a rather creative way, cycling between endless monitoring and improvements down the road.

Fun, isn’t? :)

Rails Templates

Thursday, December 4th, 2008

Let’s say you want to create a new Rails app. Every time you run “rails my_app” you usually also do some other steps such as:


# You need to remove the index.html for your app to work:
rm public/index.html

# Maybe you also create some basic Models everytime:
./script/generate scaffold person name:string

# Adding a route to the homepage
# route "map.root :controller => :person"

# Migrating the database
rake db:migrate

# The first commit in Git
git init
git add .
git commit -a -m 'Initial commit'

This problem’s gone. Edge Rails now supports a solution using “Templates”. Running a command like this:


rails blog -m ~/template.rb

You’re passing parameters to the ‘rails’ command, telling everything it needs to be done after the creation of the app directory. Things like installing plugins, removing files, putting some default css and image files in /public, the works! So, inside the template.rb file you’d put something like this:


# template.rb
run "rm public/index.html"
generate(:scaffold, "person name:string")
route "map.root :controller => :person"
rake("db:migrate")

git :init
git :add => "."
git :commit => "-a -m 'Initial commit'"

Cool, isn’t? :)

Better yet, you can use an URL to the rails command, so from any machine you’re using you’ll always have the initialization commands at your disposal. How great is that? :)


rails blog -m http://gist.github.com/31208.txt

For more information on Rails Templates check here and here.

Automatic file type detection for Rspec and Rails in Textmate

Saturday, November 29th, 2008

If your TextMate is:

  • using the Ruby Bundle for files that are Ruby on Rails;
  • using the Ruby on Rails Bundle for files that are Rspec;
  • any other wrong association.

There are some ways to fix. I followed these steps to fix it:

1. Remove the manual associations. When you use the popup bar at the status bar, in Textmate, you may not know but you are associating files to Bundles, manually, which is bad. To fix it, let’s first read what’s already in there. Run this command in the Terminal:


defaults read com.macromates.textmate OakLanguageFileBindings

Check the manual associations you’ve made so far. And remove them all with:


defaults delete com.macromates.textmate OakLanguageFileBindings

2. Specify inside a Bundle, which files it should be used for. Open Bundles → Bundle Editor → Edit Languages… and locate the Ruby Grammar. Inside this text area, locate a line that looks like this:


fileTypes = ( 'rb', 'rbx', 'rjs', 'Rakefile', 'rake',
  'cgi', 'fcgi', 'gemspec', 'irbrc', 'capfile' );

Remove the rb extension. And add it to the Ruby on Rails grammar instead. At the end my lines became like this:


# my Ruby Language grammar
fileTypes = ( 'rbx', 'rjs', 'Rakefile', 'rake', 
  'cgi', 'fcgi', 'gemspec', 'irbrc', 'capfile' );

# my Ruby on Rails Language grammar
fileTypes = ( 'rb', 'rxml', 'builder' );

As you see, the rb extension now belongs to the Rails Language Grammar.

Now whenever I open a *.rb file, the Bundle is Ruby on Rails. Whenever I open a *_spec.rb file, the Bundle is RSpec. You can always improve on that idea, and do what you think it’s best for you. For example, you can edit Ruby on Rails grammar for .erb files, .rjs, and the like.

Further information in the TextMate Blog’s post File Type Detection (RSpec & Rails).

New official 15-minute video for Rails 2.2

Thursday, November 27th, 2008

Finally, an update to the famous video on creating a blog with Rails in 15 minutes. The old version was made back in 2005!

New official 15-minute blog video for Rails 2.2 by Ryan Bates.

Como adicionar suporte a feeds

Wednesday, November 5th, 2008

Imaginei que adicionar suporte a feeds em uma aplicação Rails fosse complicado. Mas uma vez “tomei na cabeça” vendo que o Rails realmente é produtivo de se trabalhar :)

1. Método de consulta que retorna todos os posts. Para criar uma URL de feed para o seu blog, antes de tudo você precisa criar um método que faça uma query que liste todos os posts do blog. Seguindo a filosofia “fat model / skinny controller” este método deve ficar dentro do próprio model “Post”. Vamos criar um método chamado “all_posts”.


# app/models/post.rb
class Post < ActiveRecord::Base
  has_many :comments

  def self.all_posts
    find(:all, :o rder => "created_at DESC")
  end
end

2. Criando uma action para feeds. Agora vamos adicionar uma action chamada “feed” dentro do controller “posts’. Repare que aqui já estamos usando o método “all_posts” criado no passo anterior.


# app/controllers/posts_controller.rb
class PostsController < ApplicationController
  def feed
    @posts = Post.all_posts

    respond_to do |format|
      format.atom
    end
  end
end

3. Adicionando uma rota. Do jeito que está, não existe como acessar a URL http//www.myblog.com.br/feed, porque ainda não temos uma rota para esta URL. Precisamos adicionar esta rota no arquivo config/routes.rb. Note a última linha do arquivo.


# config/routes.rb
  map.root :controller => 'posts'
  map.resources :posts, :has_many => :comments
  map.resources :sessions
  map.feed 'feed', :controller => 'posts', :action => 'feed'

4. Adicionando um template para o feed.. Ok, agora o Rails já sabe que existe uma URL /feed em nosso blog. Mas ele não sabe o que renderizar ali, na action "feed". Ele só sabe que possui um objeto @posts disponível. Vamos dizer ao Rails como ele deve formatar esse retorno. O exemplo abaixo foi retirado da própria documentação do Rails, do Helper AtomFeedHelper.


# app/views/posts/feed.atom.builder
atom_feed(:language => "pt-BR") do |feed|
  feed.title(h "My blog")
  feed.updated((@posts.first.created_at))

  for post in @posts
    feed.entry(post) do |entry|
      entry.title(post.title)
      entry.content(post.body, :type => 'html')

      entry.author do |author|
        author.name(h "João da Esquina")
      end
    end
  end
end

Você pode testar essa URL em seu browser. Mas provavelmente é melhor testar com o comando curl e ver o resultado completo:


curl --get http://www.myblog.com.br/feed

5. Feed no <HEAD> do site. Pronto, já temos um feed funcionando. Agora precisamos colocar isto em nosso HTML, para dizer ao mundo que nosso blog já tem suporte a feeds :) A melhor forma é usar o helper auto_discovery_link_tag do Rails:


# app/views/layouts/application.html.erb
<head>
  <%= auto_discovery_link_tag(:atom, :controller => 'posts', :action => 'feed') %>
</head>

Agora nosso blog informa aos leitores de feeds, que nossa URL oficial é o /feed que criamos até agora. Mas se você como eu, prefere usar o FeedBurner para isso, ao invés disso faça assim:


# app/views/layouts/application.html.erb
<head>
 <%= auto_discovery_link_tag(:atom, 'http://feeds.feedburner.com/LevyOnRails', :title => "My blog's feed") %>
</head>

Ok. Agora se você informar a url de seu blog, por exemplo, http://www.myblog.com.br/ (sem o /feed) no Google Reader ou outro leitor de feeds experto, ele vai usar o feed do FeedBurner, no meu caso a URL http://feeds.feedburner.com/LevyOnRails. Perfeito!

Mas e se alguém usar a url http://www.myblog.com.br/feed, desconsiderando o que está na tag auto_discovery_link_tag acima? Para isso precisamos adicionar um redirect para o feed.

6. Redirect para o feed. Vamos criar um redirect, de forma que somente o FeedBurner consiga ler a nossa URL /feed. O restante do mundo será redirecionada para a URL do http://feeds.feedburner.com/LevyOnRails. Qual a vantagem disso? Você não terá metade de seus leitores usando o FeedBurner e outra metade usando o feed direto de teu site. Assim você consegue medir 100% a leitura/visitação de seu feed, usando as facilidades disponíveis no site do FeedBurner. Vamos precisar alterar o nosso controller:


# app/controllers/posts_controller.rb
class PostsController < ApplicationController
  def feed
    redirect_to 'http://feeds.feedburner.com/LevyOnRails', :status=>307 and return unless request.env['HTTP_USER_AGENT'].match(/feedburner|feedvalidator/i)

    @posts = Post.all_posts

    respond_to do |format|
      format.atom
    end
  end
end

Com isso verificamos a variável do servidor HTTP_USER_AGENT, se esta contém a string "feedburner" ou "feedvalidator". Se tiver, nós acreditamos que trata-se do FeedBurner, e somente este agent pode acessar nossa URL feed real.

E isso é tudo. Simples, não? :)