Rails escala? Quem se importa!

07/05/08

Image by Reuters Um verdadeiro flamewar se levantou em torno do assunto na lista rails-br nos últimos dias.

Mas e então, Rails escala? NÃO, assim como PHP ou Java também não escalam. O Rails permite que você faça isso de maneira mais simples que outras tecnologias.

A linguagem/framework não é a única responsável pela escalabilidade. Para escalar um website, você precisa de uma infraestrutura que permita fazer isso. Isso pode ser feito de diversas maneiras, como adicionar múltiplos bancos de dados, adicionar mais servidores, adicionar cache, otimizar pontos críticos de seu código, e por aí vai! Não sou especialista no assunto, mas este é, ao meu ver, o básico para se fazer!

Muitas pessoas citam exemplos de sites com grande volume de acessos, normalmente de fora de país. Não precisamos ir tão longe para ver um exemplo em Ruby on Rails que deu certo. O BlogBlogs, criado pelo Manoel Lemos, possui um widget 1 milhão de visualizações por dia e 35TB de transferência por mês! A maioria dos sites jamais chegará a um nível como este. E se chegar, você provavelmente terá recursos para levar seu site a um próximo nível, contratando pessoas competentes que saberão como lidar com o aumento de tráfego.

Para finalizar, o Google não é feito em Python, o Yahoo! não é feito em PHP, e o Twitter não é feito em Rails. Todos eles são uma mescla de diversas tecnologias, que permitem suportar, em maior ou menor escala, um grande volume de usuários. Alguns fazem isso muito bem, outros nem tanto.

Usando arrays no Ruby

17/04/08

O Ruby tem algumas particularidades que muitas pessoas não conhecem. Algumas são especialmente úteis, como é o caso do atalho de array. Normalmente, utilizamos a seguinte sintaxe:

items = ['mac', 'linux', 'windows', 'unix']
puts items.size # returns 4

Esse formato é bastante simples, mas se você tiver muitos itens pode se tornar um pouco cansativo para se digitar. No Ruby, você também pode utilizar esta sintaxe:

items = %w[mac linux windows unix]
puts items.size # returns 4

É importante notar que você deve escapar o espaço se quiser usar um termo composto por mais de uma palavra.

items = %w[mac linux windows\ vista unix]
puts items.size # returns 4

Note que você pode usar qualquer caracter como agrupador. Veja alguns exemplos:

items = %w{mac linux windows unix}
puts items.size # returns 4
 
items = %w"mac linux windows unix"
puts items.size # returns 4
 
items = %w(mac linux windows unix)
puts items.size # returns 4

Uma dica muito simples, mas elegante!

Relacionamento muitos-para-muitos com ActiveRecord no Rails

16/04/08

Esta semana recebi dois e-mails com a mesma dúvida: como funciona o relacionamento muitos-para-muitos no ActiveRecord. Aparentemente, este é um assunto que muitas pessoas têm dúvidas, mas cuja resposta é bastante simples, até. Você pode fazer tal relacionamento de duas maneiras diferentes, como veremos abaixo.

Utilizando o método has_and_belongs_to_many

O método has_and_belongs_to_many possui uma convenção para se nomear a tabela que persistirá os relacionamentos. Você deve colocar ambas as tabelas relacionadas no nome da tabela de relacionamentos, ordenados alfabeticamente e separados por underscore. Se você tem as tabelas "posts" e "categories", o nome de sua tabela de relacionamentos será "categories_posts". Os campos também possuem uma convenção, que é colocar o nome da tabela no singular, adicionando o sufixo "_id". Neste caso, teríamos um arquivo de migração como este:

class AddPostsAndCategories < ActiveRecord::Migration
  def self.up
    create_table :categories_posts do |t|
      t.references :category, :post
    end
  end
 
  def self.down
    drop_table :categories_posts
  end
end

Para relacionar ambos os modelos, precisaríamos colocar o seguinte método no modelo Post:

class Post < ActiveRecord::Base
  has_and_belongs_to_many :categories
end

Já no modelo Category, o relacionamento seria o seguinte:

class Category < ActiveRecord::Base
  has_and_belongs_to_many :posts
end

Muito simples! Apenas com estas poucas linhas você já pode usar diversos métodos, adicionados pelo próprio ActiveRecord.

post = Post.find(:first)
category = Category.find(:category)
 
# creating relationship through post
post.categories << category
 
# creating relationship through category
category.posts << post
 
# getting all posts
categories.posts.each do |post|
	puts post.title
end
 
# getting all categories from a post
post.categories.each do |cat|
	puts cat.title
end
 
# getting all the post ids
category.post_ids

O método has_and_belongs_to_many possui uma grande desvantagem. Você não pode adicionar campos extras aos relacionamentos. No entanto, o método has_many permite que você faça isso.

Utilizando o método has_many

Ao contrário do método has_and_belongs_to_many, você pode ter quantos campos adicionais precisar se fizer o relacionamento com o método has_many. Isso é extremamente útil e bastante utilizado por plugins que lidam com ActiveRecord. A convenção para este tipo de relacionamento é um pouco diferente. Sua tabela pode ter um nome específico que identifica o tipo de relacionamento que está sendo feito, já que ele terá um modelo próprio, que fará o papel intermediário entre as tabelas relacionadas.

Crie um modelo chamado Categorization. Ele deve ter os seguintes campos:

class CreateCategorizations < ActiveRecord::Migration
  def self.up
    create_table :categorizations do |t|
      t.references :post, :category
      t.timestamps
    end
  end
 
  def self.down
    drop_table :categorizations
  end
end
 

Agora, você precisa criar relacionamentos entre este modelo e os demais (Post e Category). No seu modelo Categorization, adicione os seguintes relacionamentos:

class Categorization < ActiveRecord::Base
  belongs_to :category
  belongs_to :post
end

E em cada um dos outros modelos faça o relacionamento contrário, utilizando o método has_many.

class Post < ActiveRecord::Base
  has_many :categorizations
end
class Category < ActiveRecord::Base
  has_many :categorizations
end

E aqui entra realmente a parte importante: você irá usar um relacionamento comumente chamado de "has_many :through", que fará uma consulta SQL, unindo os resultados em uma única consulta. No seu modelo Post, adicione mais um relacionamento.

class Post < ActiveRecord::Base
  has_many :categorizations
  has_many :categories,
    :through => :categorizations
end

Faça a mesma coisa no modelo Category.

class Category < ActiveRecord::Base
  has_many :categorizations
  has_many :posts,
    :through => :categorizations
end

Pronto! Seu relacionamento já foi criado. Para testar, faça algo como isto usando o console.

post = Post.find(:first)
# get post categories
post.categories.collect(&:title).to_sentence
 
category = Category.find(:first)
# get posts by categories
category.posts.collect(&:title).to_sentence

Para criar uma nova associação, você precisa fazer isso através do relacionamento categorizations; caso contrário, você receberá um erro dizendo que não possui um campo para o id.

# through post
post.categorizations.create(:category => category)
 
# through category
category.categorizations.create(:post => post)

É isso! Se você tem alguma dúvida, envie um e-mail. Se for um assunto que me interessa, ou que interessa a mais pessoas, eu posso escrever algo aqui!