How to install ruby-debug on Ruby 1.9

One of the major problem which prevent the migration to Ruby 1.9 is the lack of the porting of ruby-debug.

Here the steps I followed to made it working on my MacBook with Multiruby:

   1  sudo gem install rake-compiler ruby_core_source --no-rdoc --no-ri
   2  sudo gem install ruby-debug19 --no-rdoc --no-ri

I hope it works for you too!

advertising

Rails: Unobtrusive and i18n Javascript confirm

How many times do you heard you should separate content from behavior? Never? Hmmm don't try to cheat me.. So, why do you still use :confirm option for link_to helper?

Here a little snippet to archive our goal, and as extra bonus, you get it i18n:

   1  <script type="text/javascript">
   2  //<![CDATA[
   3  window._ authenticity_token = "<%= form_authenticity_token %>";
   4  //]]>
   5  </script>
   6  
   7  <%= link_to t(:'delete'), asset_path(@asset),
   8        :title          => t(:'asset.destroy'),
   9        :class          => "delete",
  10        :'data-confirm' => t(:'asset.destroy_confirm') %>



   1  $(document).ready(function(){
   2    $(".delete").bind("click", function() {
   3      if(window.confirm($(this).attr("data-confirm"))) {
   4        $.ajax({
   5          url: $(element).attr("href"),
   6          data: {
   7            _method: "delete",
   8            authenticity_token: window._authenticity_token
   9          },
  10          type: 'post',
  11          dataType: 'script'
  12        });
  13      }
  14      return false;
  15    });
  16  });



Ok, first of all, I'm using jQuery. When the DOM is ready I'm going to bind all the elements with the delete class to the click event. When a user clicks on the link, it will use the HTML5 custom attribute data-confirm as window confirmation content.

advertising

Memoria: Statistics for Redis

A couple of days ago I setup this blog with Redis as cache container, thanks to my Ruby gem: redis-store.

Now, following the old itch-your-own-scratches rule, I created a little Sinatra statistics web application for Redis statistics: Memoria.

Memoria screenshot

Getting Started

   1  (sudo) gem install sinatra dm-core do_sqlite3 rspec
   2  (sudo) gem install ezmobius-redis-rb jodosha-memoria -s http://gems.github.com
   3  memoria /path/to/installation
   4  cd /path/to/installation
   5  cp memoria.yml.example memoria.yml
   6  rackup config.ru --port 9292 mongrel

Memoria is still in an embryonic stage, feel free to suggest features.

advertising

Hanoi: Automated jQuery tests with QUnit

About a month ago, we have released adva-cms 0.2.0, the most relevant change was the migration from Prototype to jQuery. I always been a loyal Prototype fan, and, until then, I always tested my code with unittest.js, combined with jstest.rb.

Like other rewrite we needed to rely on a solid test layer, but what I noticed was the lack of a valid automation support for QUnit, the test framework used internally by the jQuery team. So, in a couple of days I built what we need, just creating a custom version of jstest.rb and changing a little bit QUnit, in order to communicate with it.

Now I extracted a the general purpose code for testing other JavaScript projects, the result is Hanoi

Hanoi needs a working Rake installation and it's capable to test your JavaScript code, collecting the results inside the shell. It currently supports assets, and fixtures, and it's pretty easy to use.

First of all you need to install the gem:

   1  (sudo) gem install jodosha-hanoi -s http://gems.github.com

Then you have to initialize your project path, with the hanoi executable:

   1  hanoi /path/to/project

Now, you can easily run the test suite:

   1  rake test:js

You can find more details on the GitHub page.

advertising

Click To Globalize for Rails 2.3

As announced in my previous post I'm working to update my Open Source projects. Today's the time of Click to Globalize.

For all of you who don't know what I'm talking about, Click to Globalize is a Rails plugin for in-place translations. Now it's compatible with Rails 2.3 and It's also the most clean and polish version of ever, let me explain what's changed.

Engine

Yes, Click to Globalize is now an engine, this means all the controllers, the helpers, the routes and the other stuff, now lives in vendor/plugins. No more clutter with flying files, except for one javascript and one stylesheet.

I18n

The i18n team (myself included) worked hard to build a consistent API for i18n. Starting from Rails 2.2 we have bundled a gem for this purpose, and now my plugin is totally compliant with this system, this agnosticism allow you to use whatever i18n backend you want. Click to Globalize is no longer a Globalize extension!!

RESTful

The plugin now bundles two full RESTful controllers: TranslationsController and LocalesController, respectively /translations and /locales.

Lightweight

I replaced the annoying around_filter system, with a more lightweight one, based on the observation of ActionView#render.

Formatting

I removed the support for Markdown and Textile, personally I never used so much, and don't think they are so related with i18n.

Deprecations

The old globalize? method is deprecated in favor of in_place_translations?.

You can find all the new instructions to the project page or on GitHub.
Enjoy!

advertising

Boost Rails Sessions

There are many dark corners and hidden features in a big framework like Rails.

Digging inside the code of ActiveRecord I discovered an interesting class: SqlBypass. When you set your session store on ActiveRecord it will use a Base subclass for deal with database store. This configuration adds an useless overhead for your application, because each session read/write will go through the complex callbacks system of ActiveRecord.

For these reason is highly advisable to use SqlBypass. As the name suggest it bypasses all the callbacks and simply performs read/write operations.

Try It By Yourself

In session_store.rb:

   1  # config/initializers/session_store.rb
   2  ActionController::Base.session_store = :active_record_store
   3  ActiveRecord::SessionStore.session_class = ActiveRecord::SessionStore::SqlBypass

.. then in session_controller.rb

   1  # app/controllers/session_controller.rb
   2  class SessionController < ApplicationController
   3    def read
   4      render :text => session[:user_id], :status => :ok
   5    end
   6  
   7    def write
   8      render :text => session[:user_id] = 23, :status => :ok
   9    end
  10  end

I ran raw benchmarks with one Mongrel instance in production mode:

  • read:

    • Session: 93.05 reqs/sec
    • SqlBypass: 132.78 reqs/sec
  • write:

    • Session: 99.89 reqs/sec
    • SqlBypass: 128.17 reqs/sec

You can find the whole benchmark results at the dedicated gist.

UPDATE: Unfortunately there is a bug in SqlBypass, just apply this patch for make it working.

advertising

Vanilla!

blog screenshot

I'm really proud to announce that now this blog is running on a custom application called Vanilla!.
I worked on it about for two weeks, in the evening. Yes, this is the power of Ruby on Rails! :)

advertising

Status of my Open Source projects

Starting an open source project is like to adopt a child, it takes time and cure, and, in general, you should help it to grow up.
But we are human, and often we don't pay enough attention to our children, so, the more faster the environment where they live evolves, the less they are useful. A great and paradoxical example is Click to Globalize: I ideated it, created, developed, presented at Rails to Italy and at Euro RailsConf, but still not compatible with Rails 2.2.x! And I contributed both to the 2.2.x branch and with the rails-i18n team!

I felt guilty in the past months, for turning from my projects, but the answer was: the OSS ecosystem will survive, and if a project is unmaintained it will be forked (if worthy) or it will die. And it was my excuse for spend time with music or photography, and to the other stuff I like to call analogue life.

I believe that hiatus could be a good thing if it helps the author of a project to understand and focus on the direction the project should take.
I love Open Source it made me a better programmer, and now it's time to be honest with myself and with the Community and decide which project will be maintained.

So, here my plans:

  • Click to Globalize: it's most used and well-known project of mine, it will be compatible with the new i18n API, it will be framework agnostic and I'm working to avoid the around_filter technique.
  • Sashimi: I will fix svn and git issues, and make compatible with Ruby 1.9.1. Probably I will rewrite a part of it and cover with rSpec. It was my pilot project for Ruby gems, Open Source activities and it will survive :)
  • Assets Packager: it's very tiny and very useful for me, I recently made compatible with Sprockets. A survivor.
  • Acts As Resource: it's my first Rails plugin, I'm really proud of it, but never used and it will die. Maybe it could be useful for ActiveModel contributions.
  • Nested Models: no longer needed, due to the merge with the great patch by Eloy Duran, available in Rails 2.3.
  • Plugin Migrations: I'm waiting for the adoption of my patch, if not released with Rails 2.3, I will continue to maintain.
  • Cached Models: last but not least. I'm really really really proud of this plugin. It has been mentioned by RailsEnvy #047, but I didn't received the feedback I expected. Since it's a really expensive, hard-to-maintain project, I'll soon decide about its destiny.

advertising

Rake tasks for run Rails engines migrations

I created a patch for run engine's migrations, it adds two Rake tasks:

  • db:migrate:engines
  • db:migrate:engines:down

The first one allows to run all the migrations stored in the db/migrate directory of each plugin.
It runs migrations in the same order Rails::Initializer register the plugins, this means if you force an order by environment.rb, it will be reflected on migrations order.

Example:
You have four plugins in your app: apple, bar, foo, pear.

   1  # environment.rb
   2  Rails::Initializer.run do |config|
   3    config.plugins = [ :foo, :bar, :all ]
   4  end

The task will run migrations in the following order: foo, bar, apple, pear.

It also add table structure declaration to your db/schema.rb (only if ActiveRecord::Base.schema_format == :ruby) and leave untouched your original version value.
This make totally independent app migrations from engine migrations.

Example:

   1  db/migrate/20090224121645_create_birds.rb
   2  
   3  vendor/plugins/foo/db/migrate/20080224121645_create_foos.rb
   4  vendor/plugins/foo/db/migrate/20080224121646_create_bars.rb
   5  
   6  $ rake db:migrate              # => create birds         (current tables: birds)
   7  $ rake db:migrate:engines      # => create foos and bars (current tables: birds, foos and bars)
   8  $ rake db:migrate VERSION=0    # => delete birds         (current tables: foos and bars)
   9  $ rake db:migrate              # => create birds, again  (current tables: birds, foos and bars)
  10  $ rake db:migrate:engines:down # => delete foos and bars (current tables: birds)

You can also specify if run migrations only from a specific engine:

   1  $ rake db:migrate:engines ENGINE=foo      # => run migrations from 'foo' engine only
   2  $ rake db:migrate:engines:down ENGINE=foo # => run down migrations from 'foo' engine only
   3  $ rake db:migrate:engines ENGINE=bar      # => raise an exception if 'bar' engine is missing

This patch also supports mixed migrations versioning, this means you can use both timestamped and numeric migrations in your plugins.

Example:

   1  db/migrate/20090224121645_create_birds.rb
   2  
   3  vendor/plugins/bar/db/migrate/001_create_bars.rb
   4  vendor/plugins/foo/db/migrate/20080224121645_create_foos.rb
   5  
   6  $ rake db:migrate                         # => create birds            (current tables: birds)
   7  $ rake db:migrate:engines                 # => create from bar and foo (current tables: birds, bars and foos)
   8  $ rake db:migrate:engines:down ENGINE=bar # => delete from bar         (current tables: birds and foos)

You can also run migrations from engines first, then yours.

   1  $ rake db:migrate:engines # => create from bar and foo (current tables: bars and foos)
   2  $ rake db:migrate         # => create birds            (current tables: birds, bars and foos)

As you can see running migrations is an "engine atomic" operation, and it makes sense, because engines are pluggedin applications and you want to run all the migrations from a single engine, in order to make it full working.

Now imagine to have the 0.0.1 version of a plugin called 'foo' with the following migrations:

   1  vendor/plugins/foo/db/migrate/20080224121645_create_foos.rb
   2  vendor/plugins/foo/db/migrate/20080224121646_create_bars.rb


Your run your migrations, alongside with yours, so you have birds, foos and bars tables.
When the authors will release the 0.0.2, adding a third migration, if you execute db:migrate:engines, only the last migration will be ran, instead of the full suite.
And of course this is a bless if you want to keep up-to-date your plugins.

I created a Lighthouse ticket for this patch, so if you appreciate it, please vote with a +1.




UPDATE 2009-02-28: I completely rewrote the patch, changing its behavior, fixing stuff and adding a version column to schema_migrations.

Two important changes are about db:migrate and test tasks: they both automatically check/run migrations from plugins. This means test suite will be halted if there are pending migrations from plugins.

I also added a lot of new rake tasks (also for migrating from old Rails versions), and a script for rename a plugin.


For a full review, please visit the Lighthouse ticket.




UPDATE 2009-02-28 (2): I extracted the patch into a plugin: plugin_migrations.

advertising

How to use Sprockets with Rails

Sam Stephenson has recently released a Javascript preprocessor called Sprockets.
It's distributed as Ruby gem, and it helps to declare scripts dependencies through special comments, and safely build them one script. It's very useful for maintain your Javascript projects, extract reusable code and share across applications.

Installation

   1  $ (sudo) gem install sprockets

It will also install sprocketize executable.

How it works?

To declare a dependency use require directive:

   1  //= require <prototype>
   2  //= require "cookies"


When you use angular brackets the script will be searched in all the Sprockets load path, if you use quotes instead, you are forcing to load the file from the current directory. Usually you should use the first way to require frameworks or libraries and the second one for your scripts.

How it works with Rails?

Sam has wrote a plugin to use Sprockets with Rails, let's install it:

   1  $ ./script/install plugin git://github.com/sstephenson/sprockets-rails.git


It automatically creates two folders: app/javascripts and vendor/sprockets.

Move all the files from public/javascripts to the first folder.

Now let's sprocketize our files:

   1  /* controls.js */
   2  //= require <prototype>
   3  //= require <effects>
   4  
   5  /* dragdrop.js */
   6  //= require <prototype>
   7  //= require <effects>
   8  
   9  /* effects.js */
  10  //= require <prototype>
  11  
  12  /* lowpro.js */
  13  //= require <prototype>
  14  
  15  /* prototype.js */
  16  // nothing to do
  17  
  18  /* application.js */
  19  //= require <prototype>
  20  //= require <lowpro>
  21  //= require <effects>
  22  //= require <dragdrop>
  23  //= require <controls>
  24  
  25  /* comments.js */
  26  //= require <prototype>
  27  //= require "application"


Of course application.js and comments.js configurations depends on which libraries you need for your scripts.

One last step, we have to configure our routes with:

   1  # config/routes.rb
   2  SprocketsApplication.routes(map)

Now forget about javascriptincludetag helper and place one time at the bottom of your template:

   1  <%= sprockets_include_tag %>


sprockets-rails will provide all the scripts needed by your application.

Advanced sprocketing

Sprockets help you to bundle all kind of assets (HTML, CSS, images) your Javascript project needs. Imagine to have a plugin with assets and javascripts folders, in your script you can declare:

   1  //= provide "../assets"


Just running sprockets:install_assets rake task, sprockets-rails will copy all the assets and scripts to the public folder.

If you want to use the edge of favorite javascript frameworks like jQuery Sprockets will bundle for you. For example if you want to add Prototype's edge, just copy src folder under vendored folder of Sprockets:

   1  vendor/sprockets/prototype/src

Deployment

Add sprockets:install_script as Capistrano post-deploy hook to generate the all-in-bundle script.

One more thing

If you use assets_packager, I just committed a sprocketized version of rake tasks.

advertising