Rails deployment made easy with Inploy

After working in some systems made in Rails, Capistrano became one of the things that began to bother me more and more. I tried to find other alternatives, including Vlad, but none satisfied me, so I decided to create a solution and called it Inploy.

Before Capistrano fans crucify me, I wanted to let it clear that I don’t think Capistrano is a bad tool, just that it’s bad for my needs, which are much simpler than Capistrano can attend.

One of the things I don’t like about Capistrano is that it creates a lot of folders, one for each release. Some people argued with me that they are useful when you want to rollback a release, but that never happened to me, as never happened with most of the people I talked with. The rest of the people told me that in some circumstance they had to rollback a deploy, but the circumstance was that the deployed code broke the build.

Inploy uses Git, so in case we need to rollback our deploy to a version, we can use git reset. Of course this solution is more limited that the one that Capistrano uses, but it’s a solution to a problem that probably should never occur, a problem caused probably by bad practices in the development.

As a consequence of Capistrano strategies, they deploys are a little slow for my taste. I know there are some configurations that can speed up the process, but to configure is a thing I don’t want to do. I want to use a tool that works the best way by default and in most cases, the best way to a fast deploy is the simplest one: git clone in the setup and git pull in the update, the same that Inploy does.

Speaking about defaults, it’s very annoying to me having to define in Capistrano how to restart the server, how to clean the cache and what tasks it should rake, like more:parse and asset:packager:build_all. Inploy tries to solve this problems by executing common tasks by default, being smart enough to identify what tasks it should rake and in which order. The idea behind the plugin is that you shouldn’t worry about how to deploy that new tool you’re using, that things just work without the possibility of something going wrong.

Another advantage from Inploy over Capistrano for most cases is that it just have one way to deploy and its called update. This task updates the code, migrates the database, does some other tasks and restarts the server. It makes not sense to me to update the code without running migrations. In all teams I worked with, everyone could deploy the application and in most cases someone executed cap deploy instead of cap deploy:migrations. I know this is a human error but I prefer to use a tool that doesn’t let people make mistakes.

A need I had with deployment tools is that it could work both remotely and locally. I worked in a project that was delivered to a foreign client and the people responsible of updating the application in each release couldn’t do it otherwise that by connecting to a server and running a script. All of them used Windows and were used to work with Java systems that required them to access a machine, replace a war file and execute some commands. In that opportunity, the solution was to create a deploy.sh file in the root folder on the application. This file was executed by somebody from the client every time they needed to update the application.

The problem mentioned above doesn’t happen with Inploy, as it can be run both remotely or locally. When running remotely, the only only thing it does is to connect to a list of servers and run the tasks like being locally logged. It’s because of this that Inploy is delivered as a plugin, so it’s available in all environments.

As today, Inploy will only work for you if you’re working with Git and Passenger.

To install Inploy you can execute:

script/plugin install git://github.com/dcrec1/inploy.git

On installation, Inploy will write to a file in config/deploy.rb. This file is read by the plugin and it should be something like this:

deploy.application = "inploy"
deploy.repository = 'git://github.com/dcrec1/inploy.git'
deploy.user = 'dcrec1'
deploy.hosts = ['hooters', 'geni']
deploy.path = '/var/local/apps'

After that, we’re ready to execute the tasks that the plugin has:

rake inploy:local:setup
rake inploy:local:update
rake inploy:remote:setup
rake inploy:remote:update

I created a short video in which I demonstrate how to use the different tasks of the plugin. In the video I’m inside the Signal project, which already has Inploy installed and I remove it along with the configuration file to demonstrate how easily it is to install and configure it. After that I execute a remote setup and then a remote update. We can see in the video that the commands that Inploy runs are logged with the corresponding outputs and that config/*.sample files are renamed to config/*, another feature of Inploy. In the second part of the video a local update is ran and then deleted the repository, cloned and executed another setup, this time locally.

Deploy easily to Locaweb with Inploy from Diego Carrion on Vimeo.

More details about the plugin can be obtained in the official Inploy repository, where I’ll try to keep the README up-to-date. I hope it’s clear the way that Inploy works, but just in case, looking at the code is an option to eliminate doubts. The code is quite small (50 LoC moreless) and also self explanatory. Follows an snippet:

def local_setup
  copy_sample_files
  create_folders 'tmp/pids', 'db'
  run "./init.sh" if File.exists?("init.sh")
  after_update_code
end
 
def remote_update
  remote_run "cd #{application_path} && rake inploy:local:update"
end
 
def local_update
  run "git pull origin master"
  after_update_code
end
 
def after_update_code
  install_gems
  migrate_database
  run "rm -R -f public/cache"
  rake_if_included "more:parse"
  rake_if_included "asset:packager:build_all"
  run "touch tmp/restart.txt"
end

I wish this simple code motivates people to contribute with the project and make it smarter.

If this plugins helps you, please consider to recommend me at Working With Rails.

Common attributes translations with activerecord_i18n_defaults

The plugin activerecord_i18n_defaults lets you write locale’s files in a Rails 2.2 application like this:

pt-BR:
  activerecord:
    attributes:
      _all:
        login: "Identificação"
        name: "Nome"
      admin:
        age: "Idade"
      customer:
        nickname: "Apelido"
      user:
        email: "Endereço de e-mail"

instead of like this:

pt-BR:
  activerecord:
    attributes:
      admin:
        login: "Identificação"
        name: "Nome"
        age: "Idade"
      customer:
        login: "Identificação"
        name: "Nome"
        nickname: "Apelido"
      user:
        login: "Identificação"
        name: "Nome"
        email: "Endereço de e-mail"

Next time you need to create a locale’s file put your common attributes in the key _all and DRY.

If you like this plugin please consider to recommend me at Working With Rails.

Rocking your application with Rails 2.2 i18n support

I know Sven Fuchs wrote about the Rails i18n core API back in July (when Rails 2.2 was not yet released), but since then, the i18n API has changed a little and now that Rails 2.2 is out I’ll write the steps you need to rock your application with this new excellent support.

1. Configure the I18n module

You need to select the translations files the module will load and the default locale in case the user don’t specify one. As you need to do this only once, you can put the next code in the config/environment.rb file:

I18n.default_locale = "pt-BR"
I18n.load_path += Dir.glob("config/locales/*.yml")

Update: We should add elements to the load_path instead of assigning a new array to not overwrite Rails 2.2 provided paths.

2. Set the locale in each request

You need to tell the I18n module which locale it should use in the current process. This can be done in a filter, before the controller executes the action. A good place to do the filter should be the application controller:

before_filter :set_locale
 
def set_locale
  I18n.locale = params[:locale] || I18n.default_locale
end

3. Internationalize

From now on the only thing you need to do is translate and localize, so from any place in your application you can call the translate and localize methods (t and l are alias) , for example:

I18n.t 'store.title'
I18n.l Time.now

Now that we know how to get our applications internationalized, I’ll talk about the process.

How does the Rails 2.2 i18n work?

The I18n module stores a backend that implements the localize and translate methods. Every time we call I18n.translate or I18n.localize, the I18n module delegates this calls to the configured backend. Rails 2.2 comes with a default backend called Simple Backend.

The first time the Simple Backend internationalizes something it loads the internationalizations files defined in I18n.load_path .

The internationalizations files

The internationalizations files can be a Ruby Hash or a YAML script:

Ruby Hash

{
  :'pt-BR' => {
    :foo => {
      :bar => "baz"
    }
  }
}

YAML

pt-BR:
  foo:
    bar: baz

Reloading internationalizations files

If for any reason you are using the Simple Backend and you want to load/reload an internationalization file after the first internationalization has been done you can call the load_translations(*file_names) method:

I18n.backend.load_translations("config/locales/es-PE.yml", "config/locales/en-US.yml")

Update: This is considered a hack because no methods in the backend should be called directly and should only be used for testing purpose, when working with script/console for example.

Update2: Since this post there have been created a new method in the I18n module that reloads the translations:

I18n.reload!

This method is not considered a hack! :)

More information

If you want to know about interpolation, pluralization, default values and scopes you should read the corresponded sections in the post from Sven Fuchs. I’m not talking about this features here because nothing has changed and because Sven Fuchs did a great thing explaining them in a simple way.