Gerenciando plugins no Rails com Pez
03/09/08
Ultimamente, tenho usado muito mais Git, mas ainda tenho projetos versionados com Subversion. E como todos os meus plugins estão no Github, sinto uma grande dificuldade em mantê-los atualizados, já que utilizam SCMs diferentes.
Como já tinha lido muito sobre algumas ferramentas para esta finalidade, como Braid, resolvi testá-los. Infelizmente, nenhuma delas funcionava da maneira como eu gostaria. Então, como bom geek que sou, decidi fazer minha própria implementação: conheçam o Pez.
Pez é um gerenciador de plugins para Rails muito simples. A diferença principal em relação às ferramentas semelhantes é que ele não tenta adicionar as alterações/atualizações destes plugins no repositório do projeto. Em vez disso, ele cria um diretório central e faz apenas links simbólicos para o diretório vendor/plugins do projeto. Essa abordagem permite, por exemplo, que você mantenha um único diretório de plugins para diferentes projetos, desde que eles utilizem as mesmas revisões/branches.
Mas como não é só de notícias boas que se vive, aqui vem o lado negativo desta história: você não conseguirá usar o Pez se usa Windows, já que muitos comandos utilizados só estão disponíveis em sistemas *nix.
Se você se interessou, veja à seguir como instalá-lo e quais são os comandos disponíveis.
Instalando o Pez
O Pez está hospedado no Github e pode ser instalado com o comando abaixo.
sudo gem install fnando-pez --source=http://gems.github.com
Se quiser dar uma olhada no código ou gerar sua própria versão, siga os passos abaixo.
git clone git://github.com/fnando/pez.git
cd pez
rake gem:install
Usando o Pez na prática
Depois que o Pez tiver sido instalado, você deve executar o comando pez setup
à partir da raíz de seu projeto. Isso irá criar um arquivo config/plugins.yml,
que terá todas as informações dos plugins adicionados.
---
plugins:
rateableplugin:
repo: svn://rubyforge.org/var/svn/rateableplugin/trunk
type: svn
has_cache:
repo: git://github.com/fnando/has_cache.git
type: git
revision: 8aafca390796d79ed200000f3f13243b471b96fc
cucumber:
repo: git://github.com/aslakhellesoy/cucumber.git
type: git
revision: 32d4f03d19bf33172bb7b48fed48e906a56598a7
branch: html-visitor
content_cache:
repo: http://svn.codahale.com/content_cache/
type: svn
revision: 20
development: /Users/fnando/Sites/sample/tmp/plugins
production: /Users/fnando/Sites/sample/tmp/plugins
Por padrão, os plugins são mantidos no diretório tmp/plugins. Você pode alterar essa configuração a qualquer momento, alterando o caminho relativo ao ambiente de seu projeto.
O Pez possui uma série de comandos. Para ver a lista completa, execute o comando
pez help. Você pode visualizar a ajuda para um comando específico com o comando
pez help [command].
Como você pode perceber, não tem muito segredo! Se você tiver alguma sugestão, envie um comentário. Se quiser contribuir, o projeto está no Github: http://github.com/fnando/pez.
Trivia: O nome foi tirado de um album do Less Than Jake chamado Pezcore, que eu estava ouvindo na hora que eu o programava. Ainda bem que não tenho gostos musicais bizarros! :)
- Permalink
- Trackback
- Comentários (4)
- Ao som de: Less Than Jake – Handshake meet pokerface
has_cache: cache no Rails de maneira simples
23/08/08
Há algum tempo atrás mostrei como utilizar as opções de cache disponíveis no Ruby on Rails 2.1. Embora esta tarefa tenha se tornado mais simples, ainda exige um processo um tanto quanto manual.
Pensando nisso, comecei a estudar formas mais automáticas de se fazer isso. Eu já simpatizava com a idéia implementada pelo Geoffrey Grosenbach, onde ele de mostrou uma forma muito inteligente de lidar com cache ou, melhor dizendo, com sua expiração.
A idéia consiste basicamente em utilizar uma data de atualização do objeto para manter o controle de cache. Desta forma, sempre que a data for atualizada, o cache irá expirar automaticamente. Esta abordagem é especialmente útil quando utilizada com o Memcache, já que ele irá utilizar uma quantidade pré-definida de memória, descartando os itens mais antigos quando ela se esgotar.
A solução que encontrei para este problema foi empacotada na forma de um plugin: has_cache.
Utilizando o plugin has_cache
Para usar este plugin, basta você instalá-lo através do comando abaixo:
script/plugin install git://github.com/fnando/has_cache.git
Vale lembrar que este plugin exige as versões 2.1 ou superior do Ruby on Rails.
O plugin possui funcionalidades específicas para models, actions e views, como você verá a seguir.
Modelos
Depois de instalado, basta adicionar a seguinte chamada ao seu modelo:
class Game < ActiveRecord::Base
has_many :comments
has_many :publishers
belongs_to :category
has_cache
def recent_comments
comments.recent :limit => 5
end
end
Apenas por adicionar a chamada ao método has_cache, todas as associações has_many e belongs_to irão ter uma versão com cache. Por exemplo, em vez de utilizar @game.comments, você pode utilizar @game.cached_comments. Simples assim!
Você também pode adicionar métodos que não são relacionamentos; basta utilizar a opção :include.
has_cache :include => :recent_comments
O exemplo acima irá adicionar o método de instância recent_comments. Se precisar adicionar métodos de classe, pode adicionar uma chamada como esta:
has_cache :include => {
:class_methods => %w(all find),
:instance_methods => :recent_comments
}
Os métodos de classe possuem um argumento obrigatório, que é a chave que irá identificar aquele cache.
Game.cached_all :sorted_by_title, :order => 'title asc'
Se você instalar o plugin has_paginate, poderá fazer consultas paginadas com cache.
Game.cached_paginate %w(all @page), :order => 'title asc', :page => @page
@game.cached_comments(:page => @page)
Se quiser evitar a paginação, basta passar a opção :paginate com o valor false.
@game.cached_comments(:paginate => false)
ATENÇÃO: Se o plugin has_paginate estiver instalado, todas as associações has_many serão paginadas por padrão.
Mais à frente você verá como o cache é expirado, e como definir novas chaves que serão expiradas.
Controllers
No controller, você pode utilizar o método cached_render:
class GamesController < ApplicationController
def index
@page = [params[:page].to_i, 1].max
cached_render :cache_name => %w(games index#{@page}) do
@games = Game.cached_paginate %w(all #{@page}), @page
end
end
end
Você pode especificar o tempo de vida do cache com a opção :expires_in.
cached_render :expires_in => 15.minutes do
# do something
end
Sempre que puder, utilize esta abordagem. Porém, se algum detalhe da tela é diferente para os usuários — um usuário logado tem um box com alguma identificação —, você não conseguirá fazer cache de toda a action. Mas poderá ter uma boa performance fazendo cache de pedaços da tela.
Views
Você pode fazer cache de fragmentos de um template. O plugin has_cache adiciona um método chamado cached_block.
<h1>Games</h1>
<% cached_block [:game_list, @page] do %>
<ul>
<% each_paginate @games do |game, i| %>
<li>
<%= game.title %>
</li>
<% end %>
</ul>
<%= paginate @games, url_for(:action => 'index') %>
<% end %>
Você também pode definir o tempo de vida do cache com a opção :expires_in.
<% cached_block [:game_list, @page], :expires_in => 1.hour do %>
<!-- do something -->
<% end %>
Como funciona a expiração do cache
Todo relacionamento has_many precisa de um campo com o nome do relacionamento, que servirá como o controle de expiração do cache. No nosso exemplo, nosso modelo deveria ser criado da seguinte forma:
class CreateGames < ActiveRecord::Migration
def self.up
create_table :games do |t|
t.references :category
t.datetime :comments_updated_at, :publishers_updated_at
t.string :title
t.timestamps
end
end
def self.down
drop_table :games
end
end
Toda vez que um comentário for criado, o plugin irá atualizar automaticamente o campo comments_updated_at, que é utilizado na composição da chave que irá identificar o cache. O mesmo irá acontecer quando um novo publisher for adicionado.
As seguintes estruturas de chave são expiradas quando um objeto é salvo ou destruído:
- próprio objeto
:table/:id:table/:id-:updated_at- associações
has_many :table/:id/:updated_at/:association/:association_updated_at- associações
belongs_to :table/:id- métodos de instância
:table/:id/:updated_at/:method_name- métodos de classe
- Não são expirados automaticamente.
Como os métodos de classe recebem uma chave na hora que são chamados, não podem ser expirados automaticamente. Neste caso, você pode deixar o método expirar por tempo ou forçar sua expiração.
has_cache :before_expire => proc {|game|
Game.has_cache_options[:to_expire] << "games/sorted_by_title"
}
E para finalizar…
Se você tiver alguma sugestão, faça um fork do has_cache e envie um patch. Dúvidas? Mande um comentário.
- Permalink
- Trackback
- Comentários (3)
- Ao som de: The Offspring – You're Gonna Go Far, Kid
Removendo plugins instalados como svn:externals no Rails
24/01/08
Se você utiliza plugins no Rails em um projeto versionado com Subversion, provavelmente já deve ter visto que é possível fazer tal instalação utilizando o svn:externals, através do argumento -x.
script/plugin install -x http://code.bitsweat.net/svn/object_transactions/
A diferença é que toda vez que você fizer o checkout de seu projeto, o Subversion irá buscar a última versão disponível no repositório que você adicionou. Isso é uma excelente maneira de deixar o plugin sempre atualizado. Mas e se por algum motivo você não precisa mais do plugin e quer removê-lo?
Você terá que fazer isso através de um comando do Subversion. Vá ao diretório de plugins e execute as linhas abaixo. Isso irá abrir o arquivo com a lista de repositórios externos do seu projeto. Neste exemplo editaremos tal arquivo usando o Vi.
$ cd vendor/plugins
$ svn propedit svn:externals . --editor-cmd vi

Vá até a linha do repositório e pressione CTRL + → para removê-lo. Para salvar o arquivo, pressione ESC, digite :wq! e, então, pressione Enter. Remova o diretório do plugin com o comando rm -rf object_transactions.

Agora, basta fazer o commit de seu projeto!
- Permalink
- Trackback
- Comentários (6)
- Ao som de: The Offspring – (Can't Get My) Head Around You / Next to You
