Ruby Time marshaling bug in pre-1.9 0

Posted by luca
on Friday, August 29

Ruby's Time has a bug: when try to serialize an timezoned time, then unserialize it back, the result will use the server local time, instead of use the original time zone.

>> utc = Time.now.utc
=> Fri Aug 29 09:07:37 UTC 2008
>> marshaled = Marshal.dump utc
=> "\004\bu:\tTime\r\251\037\e\200\344\254T\036"
>> Marshal.load marshaled
=> Fri Aug 29 11:07:37 +0200 2008

This bug doesn't affects Ruby 1.9, but we still don't use that version for production purpose. If you use 1.8.x with Rails 2.1.0, your cached timestamps (including ActiveRecord objects), are probably wrong.

>> comment = Comment.first
=> #<Comment id: 865423346, post_id: 1, text: "Nice post.", created_at: "2008-08-29 09:27:48", updated_at: "2008-08-29 09:27:48">
>> Rails.cache.write('comment', comment)
=> true
>> Rails.cache.read('comment')

=> #<Comment id: 865423346, post_id: 1, text: "Nice post.", created_at: "2008-08-29 09:27:48", updated_at: "2008-08-29 09:27:48">
>> comment.update_attributes :text => "Nice post!"
=> true
>> Rails.cache.write('comment', comment)
=> true
>> Rails.cache.read('comment')
=> #<Comment id: 865423346, post_id: 1, text: "Nice post!", created_at: "2008-08-29 09:27:48", updated_at: "2008-08-29 11:28:42">


Look at the last updated_at attribute, it uses local time instead of UTC time zone.
The first time everything goes right, because #updated_at wasn't invoked and casted to a Time instance. It's a string, and the marshaling is ok.
But, when I update the object, ActiveRecord changes the value of that timestamp, but before, it cast it to a Time, and everything goes wrong.

I submitted a patch for this bug, it was committed, and will be available in the upcoming Rails 2.2. You are strongly encouraged to update your Rails version.

Click To Globalize: Ready for Rails 2.1!

Posted by luca
on Monday, July 21

Click to Globalize is ready for Rails 2.1!

I spent a lot of time to make this release the best of ever! I know that a lot of time has passed since the latest Rails major release, and I apologize for this.
The official Globalize it seems dead, but the great work of Nate Clark (aka heythisisnate) has made it compatible with Rails 2.1. Thanks Nate!
The Rails Edge now includes a basic support for i18n!

What's Changed?

The first big change is about the configuration, past versions has cluttered a bit your ApplicationController, I know, but now Click uses a YAML file configuration file for locales.

As you can see, it's more readable than put an Hash inside your main controller, and it also take the advantage to separate configuration from logic.

The second change regards the default language, you can use the above file to declare it, or use the Globalize syntax (Globalize::Locale.set_base_language('en-US')), but you have to choose a locale before the app starts.
This because Click needs a secure fallback mechanism, if no active Locale was set, it's always guarantees your application to work.

The third feature is about formatting: you can always choose between textile and markdown without editing ApplicationController, but using the same configuration file.

I removed the deprecated class method ApplicationController#globalize? in favor of the instance one. This means you can use it to turn on/off Click to Globalize, just returning a boolean value:

This version also includes tiny fixes, refactoring and cleanup.




You may wish to learn a bit about Click to Globalize, so I encourage you to visit the official page.
I hope you enjoy your i18n on Rails!

What's New In Rails Edge: i18n 1

Posted by luca
on Friday, July 18

Ruby on Rails has just integrated a basic support for i18n.

ActiveSupport

ActiveSupport now includes the i18n gem which provides the API and the settings for the default locale: en-US.
The gem abstracts the repository where the translations are stored, so all the plugin authors could write their own mechanism. The bundled repository is called Simple and stores all the settings in memory.

Declaring a locale is quite easy:

I18n.backend.store_translations :'it-IT', {
  :support => {
    :array => {
      :sentence_connector => 'e'
    }
  },
  :date => {
    :formats => {
      :default => "%d/%m/%Y",
      :short => "%d %b",
      :long => "%d %B %Y",
    },
    :day_names => %w(Luned&igrave; Marted&igrave; Mercoled&igrave; Gioved&igrave; Venerd&igrave; Sabato Domenica),
    :abbr_day_names => %w(Lun Mar Mer Gio Ven Sab Dom),
    :month_names => %w(Gennaio Febbraio Marzo Aprile Maggio Giugno Luglio Agosto Settembre Ottobre Novembre Dicembre).unshift(nil),
    :abbr_month_names => %w(Gen Feb Mar Apr Mag Giu Lug Ago Set Ott Nov Dic).unshift(nil),
    :order => [:day, :month, :year]
  },
  :time => {
    :formats => {
      :default => "%a, %d %b %Y %H:%M:%S %z",
      :short => "%d %b %H:%M",
      :long => "%B %d, %Y %H:%M",
    },
    :am => 'am',
    :pm => 'pm'
  }
}

How can I translate or localize?

I18n.locale = 'it-IT'
I18n.t :hello   # => Ciao
I18n.l Time.now # => "Ven, 18 Lug 2008 10:58:14 +0200"

I18n#t is also useful to fetch locale defaults:
I18n.t :'time.formats.short' # => %d %b %H:%M

ActiveRecord

ActiveRecord now returns localized error messages for validations.

You may wish to declare your messages:

I18n.backend.store_translations :'it-IT', {
  :active_record => {
    :error_messages => {
      :inclusion => "non &egrave; incluso nella lista"
      # ...
    }
  }
}

ActionPack

ActionView now supports translations and localization for time and currency helpers (i.e. distance_of_time_in_words, number_to_currency).


UPDATE 2008-07-19: Sven Fuchs wrote a i18n Rails manifesto and a technical post about the i18n API.

Sashimi 0.2.0! 0

Posted by luca
on Monday, July 14

Sashimi has just hit 0.2.0!

This release has no sugar for you! No new features, just test improvements and code robustness.

Sashimi: Getting Started Tutorial 0

Posted by luca
on Monday, June 30

Here a getting started tutorial for Sashimi

. Sashimi getting started tutorial

Released Sashimi 0.1.6 0

Posted by luca
on Monday, June 23

I just released a new version of Sashimi, with tiny fixes and a new home! In fact the project is also hosted on RubyForge, now you can install the gem with:

$ (sudo) gem install sashimi
or with:
$ (sudo) gem install jodosha-sashimi --source=http://gems.github.com

If you wish, you can visit the project pages on GitHub and on RubyForge.

UPDATE The release 0.1.6 is broken, you are strongly encouraged to update your gem with the newest 0.1.7.

Speaker at RailsConf Europe 2008

Posted by luca
on Wednesday, June 18

I'm proud to announce my presence as speaker at the RailsConf Europe 2008 with a speech on Click to Globalize.

Rails: Single File App 0

Posted by luca
on Tuesday, June 03

I took inspiration from the Pratik Naik post, and realized a more simplistic version of its Rails single file app. My implementation has only Rails as unique dependency.

require 'rubygems'
require 'action_controller'
require 'webrick'
require 'webrick_server'

class HelloWorldController < ActionController::Base
  session :off
  def index; render :text => 'Hello World!' end
end

ActionController::Routing::Routes.draw do |map|
  map.root :controller => "hello_world"
end

DispatchServlet.dispatch :port => 3000,
    :server_root  => File.dirname(__FILE__)

Update 2008-06-04: I just wrote another version which also uses ActiveRecord and a template.

require 'rubygems'
require 'activerecord'
require 'action_controller'
require 'webrick'
require 'webrick_server'

ActiveRecord::Base.establish_connection(
  :adapter  => 'sqlite3',
  :database => 'tiny_rails.sqlite3',
  :timeout  => 5000)

ActiveRecord::Schema.define do
  create_table :people, :force => true do |t|
    t.string :first_name
  end
end
class Person < ActiveRecord::Base; end
Person.create :first_name => 'Luca'

File.open('index.html.erb', 'w') do |f|
  f << "Hello, my name is <%=h @person.first_name %>!\n"
end

class HelloWorldController < ActionController::Base
  session :off
  def index
    @person = Person.find :first
    render :file => 'index.html.erb'
  end
end

ActionController::Routing::Routes.draw do |map|
  map.root :controller => "hello_world"
end

DispatchServlet.dispatch :port => 3000,
    :server_root  => File.dirname(__FILE__)

Just start the script and point your browser at http://localhost:3000!

Sashimi: Just Received A Tasteful Update 0

Posted by luca
on Tuesday, May 20

Yesterday I announced Sashimi and now it's already time of tasteful updates.

Changes

First, thanks to all the people that appreciated my work. Second, new commands and options are avaliable for Sashimi.

Update all plugins in your repository:

$ sashimi update --all

Install a plugin to your Rails app:

$ sashimi install --rails click-to-globalize
NOTE this is an alias for the add command.

Update a plugin of a Rails app:

$ sashimi update --rails click-to-globalize
NOTE If your application is versioned with Svn or Git, Sashimi will automatically schedules for add/remove the added/removed files.

How To Update

$ (sudo) gem update jodosha-sashimi --source=http://gems.github.com

I hope you enjoy those addictional options.

Sashimi: A Rails Plugins Manager Gem 3

Posted by luca
on Monday, May 19

I have a really, really bad memory: each time I need to install a Rails plugin, I Google to find the repository, then try to download the code. Often, I remember the url, but the server is down. Damn!

All this annoying issues kill the Rails rapidity on application prototyping. But, what if all the plugins which I need are available offline on my notebook? I can forget about all that urls, and I should stop to worry about the server status.

To solve this problems, I wrote Sashimi, it's a gem that manages you favourite Rails plugins in a local repository.

How It Works?

First you need to install it with:

$ (sudo) gem install jodosha-sashimi --source=http://gems.github.com

Now you can install a plugin on your local repository:
$ sashimi install git://github.com/jodosha/click-to-globalize.git

Ta-daaaa!! Now Click to Globalize is available offline:
$ sashimi list

click-to-globalize

If you need to add it to your Rails app, just move to your app root, then type:
$ sashimi add click-to-globalize

Conclusion

I found Sashimi really useful, I hope you too.

For the complete reference, please check out at the official gem page.

Sashimi 0

Posted by luca
on Monday, May 19

Sashimi

Sashimi is a Rails plugins manager.
It allows to install your favourite plugins in a local repository and add them to your Rails apps.
The main goal of this gem is to allow the plugins offline installation.

Installation

$ (sudo) gem install sashimi
or:
$ (sudo) gem install jodosha-sashimi --source=http://gems.github.com

Usage

Install a plugin from a subversion URL:

$ sashimi install http://dev.rubyonrails.com/svn/rails/plugins/continuous_builder

Install a plugin from a git URL:

$ sashimi install git://github.com/jodosha/click-to-globalize.git

Uninstall a plugin:

$ sashimi uninstall continuous_builder

Update a plugin:

$ sashimi update click-to-globalize

Update all installed plugins:

$ sashimi update --all

Update a plugin of a Rails app:

$ sashimi update --rails click-to-globalize
NOTE: If your application is versioned with Svn or Git, Sashimi will automatically schedules for add/remove the added/removed files.

List all installed plugins:

$ sashimi list

Add installed plugin(s) to a Rails app:

$ sashimi add click-to-globalize
or
$ sashimi install --rails click-to-globalize

Known And Common Issues

  • When add a plugin to an app, make sure your current directory is the rails root.
  • Only Subversion and Git repositories are currently supported.

Contribute

Check out the code and test it:

$ git clone git://github.com/jodosha/sashimi.git
$ rake
Create a ticket to: http://sushistar.lighthouseapp.com
Create a patch and add as attachement to the ticket.

Credits

Partially inspired to RaPT.

Copyright

Copyright © 2008 Luca Guidi - http://lucaguidi.com, released under the MIT license

Acts As Resource: Rails 2.1 ready and moved to GitHub 0

Posted by luca
on Wednesday, May 14

Acts As Resource is ready for the imminent Rails 2.1!

I also moved it to GitHub, the new repo is http://github.com/jodosha/acts-as-resource/tree/master, the SVN one is deprecated.

If you enjoyed this post, feel free to recommend me on Working With Rails.

Click to Globalize: rewritten and moved to GitHub

Posted by luca
on Monday, May 12

Click to Globalize has been rewritten, in order to use all instance methods of the JavaScript class, instead of class methods.

I also fixed some tiny issues:

  • The form_authentication_token method is called only if the application is protected against the CSRF attacks (protect_against_forgery? returns true)
  • It uses as parameter key what request_forgery_protection_token returns, instead of the hard-coded authenticity_token
  • The JavaScript class uses Prototype's dom:loaded custom event.

I also migrated the plugin GitHub. The new repo is http://github.com/jodosha/click-to-globalize/tree/master, so the oldest one is deprecated.
Have a nice globalization!

If you enjoyed this post, feel free to recommend me on Working With Rails.

Ruby on Rails: Test Model Domain Changes 0

Posted by luca
on Monday, April 28
You know how tests are fundamental for a well-developed project, for this reason we should create step-by-step a net of test cases. Of course we also should be able to change rapidly our tests as the same we do with our code. Ruby on Rails is a great framework, because its shortcuts, the wide usage of DSL etc.. All this stuff can save a lot of time, but what about tests? Are we really able to follow our code?

Create and Destroy

ActiveSupport provides few useful tools to improve our test, I really appreciate assert_difference and assert_no_difference. Basically, this two methods accepts as arguments a code chunk (as string) and a block. When the test run, it binds the block first, then it assert if the changements caused by the block call are the same expected by first argument.
def test_should_be_created
  assert_difference 'Person.count' do
    create_person
  end
end
We are testing a Person creation, we pass as first argument 'Person.count', and the code that should correctly save the person. If the model will be saved, a new record should exists into the database table. At this moment assert_difference evaluates the first argument, and assert if there are differences in the Person count.
def test_should_be_destroyed
  assert_difference 'Person.count', -1 do
    destroy_person
  end
end
This example is just a bit different, we are also passing a Fixnum as argument. This because we want assert another difference from the default one, which is +1. So, if the model will be correctly saved, we will have a negative difference, of one, into the Person count.
def test_should_require_first_name_on_create
  assert_no_difference 'Person.count' do
    create_person
    assert person.errors.on(:first_name)
  end
end
The third example uses assert_no_difference, to test aganist model validations. ActiveRecord, by default, prevents the creation of a model if a validation doesn't pass. In this case our model requires first_name as mandatory attribute, but unfortunately it's nil, so the creation fails and the brand new record will be not created.

Update

As you can see, those two methods are very useful for test creation and destruction of models, but totally missing the goal of the update. In fact, the update process of a record, doesn't produces numerical differences. I created two methods to supply this lack.
def assert_updated(model, message = nil, &block) 
  yield
  assert_not_equal model.attributes, model.reload.attributes, message
end
def assert_not_updated(model, message = nil, &block)
  yield
  assert_equal model.attributes, model.reload.attributes, message
end
Just add them to your test/test_helper.rb, and they will be available in all your test cases.
def test_should_update
  assert_updated person do
    update_person
  end
end
First, you should notice that the first argument it isn't a string but an ActiveRecord. The behaviour of this method is similar to the previous I illustrated, it first bind the block, then assert if the attributes of the model are different. It internally uses ActiveRecord::Base#attributes which returns an hash of model attributes, then assert the differences with Ruby's assert_not_equal.
def test_should_require_first_name_on_update
  assert_not_updated person do
    update_person
    assert person.errors.on(:first_name)
  end
end
Similarly to all other examples, it first performs the block, call then assert there are no changes in the model attributes.

Conclusion

Those methods should provide a rapid way to write and mantain your test cases. If you enjoyed this post, feel free to recommend me on Working With Rails.

Click To Globalize 0

Posted by luca
on Saturday, January 26

Click to Globalize

Click to Globalize is an extension for Globalize plugin, it allows to edit in place globalized labels. With this plugin you don't have to create a globalization back-end, but just edit your interface in place!!

If you have a previous globalization experience, you probably noticed that the main problem of dedicated back-ends is the lack of the context where the string will be placed in. It isn't a trivial issue: you'll never know if the meaning of your string is harmonious with other labels and how your string will be rendered, until the page load.

Now you can forget this issues, you can directly globalize in place!!

Getting Started

  1. Install Globalize 1, if you don't already done.
  2. Install Click to Globalize:
    $ ./scripts/plugin install git://github.com/jodosha/click-to-globalize.git
  3. Run the setup task:
    $ rake click:setup
  4. Edit config/click.yml
  5. Add at the end of your layout the following code:
    <%= click_to_globalize -%>
  6. Put in your view:
    <%= 'hello_world'.t %>
  7. Start the server and click to globalize.

Prerequisites

  • Globalize 1 plugin: www.globalize-rails.org.
  • Prototype >= 1.5.1.1, this version was bundled in the outdated versions.
  • RedCloth gem (optional) for textile formatting.
  • BlueCloth gem (optional) for markdown formatting.

Install

NOTE the following instructions are for Click to Globalize v0.4.0 (Rails 2.1.x), for older version, please check your README.

  1. Install Globalize 1, if you don't already done.
  2. Install Click to Globalize:
    Rails 2.1.x
    $ ./script/plugin install git://github.com/jodosha/click-to-globalize.git
    Rails 2.0.x
    $ ./script/plugin install http://dev.23labs.net/svn/rails/plugins/click_to_globalize/trunk
    Rails 1.2.x
    $ ./script/plugin install http://dev.23labs.net/svn/rails/plugins/click_to_globalize/branches/for-1.2.x
  3. Run the setup task:
    $ rake click:setup
  4. Run the tests (optional):
    $ rake click:test
  5. Edit config/click.yml, adding your locales (including the default one), and, optionally, the formatting style:
  6. locales:
      english: en-US
      italian: it-IT
    
    #optional
    default: :english
    
    # optional
    formatting: textile

    NOTE: Make sure to choose a default locale before the app starts.
  7. Personalize the access to the globalization features, overriding #globalize:
    class ApplicationController < ActionController::Base
      def self.globalize?
        current_user.admin?
      end
    end

    NOTE: #globalize? is method to turn on/off all the Click to Globalize features.
  8. Add at the end of your layout the following code:
    <%= click_to_globalize -%>

Uninstall

$ ./script/plugin remove click_to_globalize

Features

  • In-place editing for each globalized string.
  • Easy and painless plug-in/plug-out process.
  • Unobtrusive Javascript.
  • Auto transformation from input text to textarea for long strings.

Common Issues

  • The install process (for Rails 1.2.x) upgrades your Prototype version to 1.5.1.1 only if needed, but it's advisable to backup your scripts.
  • Due to unobtrusive nature of the plugin, each page is parsed by javascript to find the right element and bind to it an Ajax.InPlaceEditor.
    Be sure your (X)HTML is wellformed.
    NOTICE: If you use the Rails 1.2.x scaffold system, Click to Globalize doesn't works. Cleanup the code before to use.
  • If you use a wiki-formatting style it could be advisable to use #h to avoid security problems (i.e. XSS).
    <%= textilize(h('some_text'.t)) %>
  • The official Globalize repository is still not compatible with Rails 2.1, use the heythisisnate's fork instead.

Contribute

  • Check out the code and test it:
    $ git clone git://github.com/jodosha/click-to-globalize.git
    $ rake click
  • Create tickets at the Sushistar Lighthouse Page.
  • Create a patches and add as attachement to the tickets.

Repository

http://github.com/jodosha/click-to-globalize/tree/master

Credits

Javascript tests are based on Prototype test libs.

Vote

If you find it useful feel free to add to your favs on agilewebdevelopment.com.

Copyright

Copyright © 2007 - 2008 http://lucaguidi.com, released under the MIT license.

Tutorials

  • Getting Started --high resolution
    Click to Globalize: Getting Started [High Resolution]
  • Getting Started
  • Restrict The Access
  • Text Area Transformation
  • Formatting
  • How to Prevent XSS