How to hook to an Array instance in a Ruby module

On some project we needed to create a module containing an Array attribute. We wanted to hook onto some of the methods of this particular Array instance to call our own code. Here is how we did it:

module TheModule
attr_reader :items
def initialize(*args)
@items = []
def @items.<<(item)
super
"You added the item #{item} with <<!"
end
def @items.push(item)
super
"You added the item #{item} with push!"
end
super
end
end
class TheClass
include TheModule
end

And now the output:

x = TheClass.new
x.items # => []
x.items << 'blue' # => You added the item 'blue' with <<!
x.items.push 'orange' # => You added the item 'orange' with push!
x.items # => ["blue", "orange"]

Every Little Things Capistrano Does Is Magic

During all these years, Capistrano has been for me a magical gnome that I invoke by saying “cap deploy, my magical friend!” and then I close my eyes, sing a happy song in my head and when the gnome has finished his magic, I hit F5 to see if all went well. And of course, if it didn’t, I blame the damn elf.
I am proud of this introduction. I will take a sip of coffee and read it again, hold on. Yes this intro was good, I think I should write about magical little beings more often.
If you are like me, you never paid much attention to capistrano and the way it works. At the exception of the famous “cap deploy” command, you never really wanted to get involved in the process. For this reason you have copied and pasted the same old deployment recipe in all of your projects over the years, only changing some parameters like the application name and the emplacement of the git repository.
The fact that capistrano is doing magical things doesn’t really help in this regard. My goal in this post is to explain some of the not so obvious parts of capistrano. With minimal efforts we can have a better idea of what capistrano does and feel more in control when deploying an application.
Capistrano is magic
Like many things in the ruby world, there is a fair bit of magic involved in capistrano. For example, when you type “cap deploy”, there is a bunch of things that happen, but what things exactly? What tasks are being called? This is not obvious. First of all, “deploy” is not a task written in your own recipe file, it is a namespace located in the capistrano gem. Take a look:
[ruby]
namespace :deploy do
desc <<-DESC
Deploys your project. This calls both `update’ and `restart’. Note that \
this will generally only work for applications that have already been deployed \
once. For a "cold" deploy, you’ll want to take a look at the `deploy:cold’ \
task, which handles the cold start specifically.
DESC
task :default do
update
restart
end
#…
[/ruby]
As you can see, the :default task is invoked when you type “cap deploy”, which in turns will call “update” and “restart”.
This is why you never got to see a call to the :restart task in your own recipe file. I know that during all these years there was a secret part of your brain that was¸wondering about this stuff, while the not-so-secret part really didn’t care.
Capistrano and Bundler
[ruby]
require ‘bundler/capistrano’
[/ruby]
This line adds some bundler magical stuff to your recipe. It will install your bundle (with the –deployment flag) when deploying, meaning that all your gems will be vendored to yourapp/shared/bundle.
Capistrano and the Asset Pipeline
[ruby]
load "deploy/assets"
[/ruby]
This line adds some magical stuff for compatibility with the Rails 3.1+ asset pipeline. More precisely, it will precompile your assets. This is an almost essential part when you use the asset pipeline in production. The other solution is to precompile locally and push the assets to your repository, not a good idea in my opinion. The sad part is that precompiling assets can slow down your deployment quite severly. If you use GIT, you might like this smart solution from Ben Curtis to skip assets precompilation.
Capistrano and RVM
[ruby]
$:.unshift(File.expand_path(‘./lib’, ENV[‘rvm_path’]))
require "rvm/capistrano"
set :rvm_ruby_string, ‘[email protected]
set :rvm_type, :user
[/ruby]
This tells capistrano to switch to a specific ruby version + gemset when connected to the server. The gemset (i.e. @rails3.2) is the tricky part because it makes you think that, when capistrano will invoke bundle install, your gems will be installed in .rvm/gems/[email protected]/gems. This is not the case. Remember that we have just told bundler to install the gems in yourapp/shared/bundle/.
So why do we bother with the gemset anyway?
One reason is that the bundler gem itself will be invoked by capistrano on your server in the gemset specified in the rvm_ruby_string variable. It can be useful to specify a gemset if you want to use a specific version of bundler.
That's all folks!