RubyOnRails

End of my Rubygems mirror

With the advent of Gemcutter, I don’t feel the need to maintain my own rubygems mirror anymore.

Please remove it from your gem source if you’re using it and replace it by Gemcutter. To install gemcutter as the main source for your gems :

gem install gemcutter
gem tumble

Published on Sat, 05 Dec 2009 17:58

RSpec and HAML Helpers

Today I faced a problem with specing a helper where I use the haml_tag/puts methods (haml_tag was previously open).

Here’s a solution working with Rails 2.0.2, RSpec 1.0.3 and HAML 1.8.2 :

in spec_helper.rb add :

Spec::Runner.configure do |config|
  ...
  
  # Activate haml to spec helpers
  config.with_options(:behaviour_type => :helpers) do |config|  
    config.include Haml::Helpers  
    config.include ActionView::Helpers  
    config.prepend_before :all do  
       # Update from Evgeny comment, with HAML >= 1.8.2, you can call
       init_haml_helpers

       # Old way for HAML <= 1.8.2
       # @haml_is_haml = true  
       # @haml_stack = [Haml::Buffer.new]  
    end  
  end

  ...
end

then you can write your helper spec like this :

describe ApplicationHelper do
  describe "#top_navigation_menu when logged in" do
    it "returns the menu" do
      @user = mock_model(User)
      capture_haml {
        top_navigation_menu(@user)
      }.should_not be_empty
    end
end

The interesting method is capture_haml, which does the same as Rails builtin capture but for HAML.
haml_tag/puts methods write output directly to the buffer and does not return the generated content as a String, thus we cannot just test on the method output.

Published on Wed, 21 May 2008 21:35

RSpec 1.1

David Chelimsky anounced this morning the release of RSpec 1.1 (as of now the website is not up to date) RSpec 1.1 and just now the brand new website hosting.

What’s in this release ?

  • Nested Example Groups : allows you to nest your describe blocks resulting in group sharing common specifications.
  • Support for Rails 2.0.1
  • Test::Unit interoperability : switch smoothly from Test::Unit to RSpec by allowing you to run your Test::Unit tests with RSpec. The goal here is to provide a way to progressively transition your tests to RSpec syntax.

More infos on David blog post about the RSpec 1.1 release

Congratulation and many thanks to everyone involved in this release !

Published on Fri, 14 Dec 2007 09:55

RSpec and Inline RJS

Rails give us the ability to write inline RJS via render :update syntax, as in :

render :update do |page|
  page['addressPreviewStatus'].update 'Address Not Found'
end

Previous code will update the content of tag with “addressPreviewStatus” id with ‘Address Not Found’.

But how can we spec that out ? I needed to search a little as there seems to be very little examples.

First in rails there is a special assertion assert_select_rjs, merged from the assert_select plugin, it let you test your RJS with a syntax similar to RJS itself.
RSpecOnRails has a special matcher wrapping assert_select_rjs : has_rjs.

You can use it on response to specify what should be generated, for exemple you can :

# Specify response should contains an update or insert of some kind
response.should have_rjs

# Specify response should contains an update or insert for the tag with given id
response.should have_rjs('id')

# Specify response should contains a specific update, insert, etc. for the given tag
response.should have_rjs(:replace, 'id')

You get the point. Now, a nice syntax allows you to write RJS this way :

render :update do |page|
  page['id'].update('replacement text')
end

So my first try was to use :

response.should have_rjs(:update, 'id', 'replacement text')

but this fail miserably with an error Unknown RJS statement type update. I tried different syntax but none worked.

Finally browsing through source of assert_select_rjs I found what I was looking for. When using this syntax you should use one of :chained_replace or :chained_replace_html depending what you want to test :replace or :update.

Now here is the solution :

response.should have_rjs(:chained_replace_html, 'id', 'replacement text')
Published on Sat, 24 Nov 2007 16:17

Date, Time and old days

Not so long ago a bug emerged on my current application, a vicious one.
Let’s say you have a user and store its bithday date in database (say MySQL) as a DateTime (yep I know, it’s a little to precise for this kind of data).
Rails conveniently retrieve this field as a Time, but as you probably do not be aware of is the range limitation of Time’s dates. In fact the Time class is often used because it’s faster than Date or DateTime, but the tradeoff is it handle date based on epoch time (01-01-1970), where Date/DateTime use Complex class and as such can handle a broader range of date.

Well, my user’s birthday date was somewhere in 60s and so can’t be handle by a the Time class. But it’s not as simple, Rails fallback to DateTime if parsing MySQL datetime field does not work with Time.parse. This results in an object perfectly valid when using it, the problem arise when you try to update the corresponding record in database.

Remember my initial datetime field was fetched as a DateTime ruby object and as Rails include all fields from the record on update it tries to translate the birthday to something MySQL recognize. ActiveSupport include some convenients methods to do that : Time#to_s and Date#to_s takes a parameter allowing us to do something like Time.today.to_s(:db) as well as some convenient Date to Time conversion. Unfortunately, no out of range detection is done in Date#to_time which leads me to the first headache. Then ActiveSupport::CoreExtensions::Date::Conversions::DATE_FORMATS hash does not contains a :db key so calling Date#to_s(:db) results in an erroneous string for the db. (this is corrected in rails edge but not in 1.2-stable branch).

All in all :

  1. be aware of Time class limitation
  2. ruby open classes are wonderful : just drop this code in your lib directory and that should make it until rails is patched.
module ActiveSupport
  module CoreExtensions
    module Date
      module Conversions
        # Add db and rfc822 format
        DATE_FORMATS.merge!({
          :db       => "%Y-%m-%d %H:%M:%S",
          :rfc822   => "%a, %d %b %Y %H:%M:%S %z"
        })
      end
    end
  end
end 
Published on Thu, 23 Aug 2007 16:07

Ruby 1.8.6 and Date

If you have a localized application and use Gettext, you probably use some method to handle localized Date.
Mine was a widely used approach consisting of redefining various Date.rb constants (Date::ABBR_DAYNAMES, Date::MONTHNAMES, etc.) to use the Gettext Object#_() method.

Recently I was forced to use Windows™® as my primary work OS, so I installed latest Ruby One-Click Installer which comes with Ruby 1.8.6. (I’m running 1.8.5 on all other machines).

This is where I was bitten by the Ruby 1.8.6 Date change, all my lovely constants are now frozen and I can’t localized them anymore (at least so simply).

So stick with 1.8.5 as long as you don’t really need to upgrade, there are also some threads issues with this release… so you also have weird issues if you use backgroundrb.

Published on Wed, 01 Aug 2007 10:14

Graticule, Google and UTF8 are in a boat

I faced a problem recently using Graticule with Google geocoder api. Whenever I check for locations whose name contains accentuated chars (like “Orléans”), I was rewarded with a nice REXML exception telling me google xml was not correct (some tags ending missing).

After about an hour of fight with Graticule, then Open-URI, then Net::HTTP, then StringIO and finally google I discovered the problem : Google Geocoder API send xml response with iso-8859-1 unless you fake being…say “Mozilla/5.0”. No matter you specify Accept or Accept-Charset headers the only way to have the xml as utf8 is sending “User-Agent: Mozilla/5.0”.

In fact it works with different versions :“Mozilla/5.0”, “Mozilla 5.0”, “Mozilla 5.0 (give me utf8 please)”, etc.

So if you want to use Graticule to geolocalize locations with accentuated chars change in graticule’s “rest.rb” (around line 80) :

url.open do |response|

to

url.open('User-Agent' => 'Mozilla 5.0 (Some utf8 please)') do |response|

UPDATE :
Brandon Keepers tackles on this lightning fast and release Graticule 0.2.3, one bug down !!

Published on Fri, 27 Apr 2007 13:12

Gettext and Rails

I encountered some problems with gettext rails integration recently, so here they are with explanation and resolution (if I found it).

Localization of ActiveRecordHelper error messages :

I choose to change the error message as explained by the gettext tutorial :

in application.rb

ActionView::Helpers::ActiveRecordHelper::L10n.set_error_message_title(Nn_("An error is occured", "%{num} errors are occured"))  
ActionView::Helpers::ActiveRecordHelper::L10n.set_error_message_explanation(Nn_("The error is:", "The errors are:"))

this works great, the error message displayed by the helper is the good one… except it only appears in the base language (English in my case). And the text never appears in my .pot file. After some searches in the gettext files, I found the problem : the textdomain for the helper is ‘rails’, which is great because you got validations and errors localized by gettext team for free. But it means you can’t set your own message without modifying the rails.pot in gettext package, which is not a solution because you need to maintain your own gettext gem or apply modifications to all you servers installs, and no you can’t install gettext as a rails plugin as gettext gem compile a gettext.so|.dylib on install which depends of your system.

The solution ? I didn’t find any satisfying one, if you force the textdomain to your app’s one then you need to duplicate and update all the rails translations strings from gettext package.

Gettext#updatepo and models with observers :

One of gettext nifty feature is its ability to extract your model columns names for translation. But the implementation cause some trouble when you have declared some observers in your environment.rb.

To accomplish its tasks, gettext first load rails environment. Then each files are parsed by Gettext::ActiveRecordParser#parse where you have :

old_constants = Object.constants
begin
  eval(open(file).read, TOPLEVEL_BINDING)
rescue
  $stderr.puts _("Ignored '%{file}'. Solve dependencies first.") % {:file => file}
  $stderr.puts $! 
end
loaded_constants = Object.constants - old_constants

This code try to catch the constants the current file add to Object.constants and then parse them either as ruby or ActiveRecord subclass. But if you declared an observer in your environment.rb, the model and its observer is already in the Object.constants and are just ignored by the gettext active record parser.

Solution ? Comment the config.observers line in your environment.rb each time you need to run gettext:updatepo task, not really sexy but it works.

Published on Fri, 30 Mar 2007 10:49

RSpec On Rails and HAML

As my incursion into RSpec continue, I encountered a strange problem on my CC machine. I switched my default layout (aka application.rhtml) to HAML, so the file became application.haml. No problem on my development machine running specs either with spec command or with rake. But the CC machine start sending me error in my specs, telling me application.rhtml does not exists.

I searched some looooong minutes which stack generates this error, four possibilities : RoR, RSpec, Gettext, HAML plugin.

The last thing I did before the specs breaks was to switch a controller spec from view isolation to view integration. So I looked more closely to RSpec On Rails plugin and found something interesting, to run in isolation mode, RSpec on rails stub those methods :

@template.stub!(:file_exists?).and_return(true)
@template.should_receive(:render_template).any_number_of_times.and_return(true) { |*args|
  @first_render ||= args[2]
}
@template.stub!(:find_template_extension_for).and_return("rhtml")
@template.stub!(:read_template_file).and_return("this is fake content generated by rspec")

So it faint Rails about an existing application.rhtml which in fact does not. Rails in its great willingness cache the different template/template extension lookups even in test mode. Then when mixing view integration and view isolation in specs, if those with view integration run after those in isolation, it breaks… or it should, as I can’t reproduce this in my development machine nor can I reproduce this on my CC machine when launching the specs by hand with the spec command (it does occurs only with rake).

About the Rails choice to cache, I fully understand why it is as it is, there should be no template rename during test as it is possible in development mode. So it’s not Rails fault.

But what can we do about this, the simplest solution I came to was to set the template extension caching to off in config/environments/test.rb :

config.action_view.cache_template_extensions         = false

Hope this helps someone else using RSpecOnRails with a custom template system.

[Update]
The difference between my two machine was revealed to me in just after I post this. The rake task on the CC machine, does not sort the controller spec files as my development machine, the problematic file is called addresses_controller_spec.rb so it was the first one evaluate in my development machine and is drowned between others in my CC machine.

Published on Sat, 24 Feb 2007 13:21

How to use a custom RDoc template from a rake task

I just need to do that, use a custom rdoc template to generate a documentation for a Rails project.

My main concern, regarding the different solutions I found on the web, was I didn’t want to have a template in the ruby installation directory but one that resides in a subdirectory of my rails application.

In fact it’s not a problem … when you find the good command. Here’s my rake task to generate the documentation (I put the Jamis Buck customized template in RAILS_ROOT/doc/rdoc_template/jamis.rb)

namespace :doc do 
  desc "Generate documentation for the application"
  Rake::RDocTask.new("app_jamis") { |rdoc|
    rdoc.rdoc_dir = 'doc/app_jamis'
    rdoc.title    = "My Website Documentation"
    rdoc.template = "doc/rdoc_template/jamis.rb"
    rdoc.options << '--line-numbers' << '--inline-source'
    rdoc.rdoc_files.include('doc/README_FOR_APP')
    rdoc.rdoc_files.include('app/**/*.rb')
    rdoc.rdoc_files.include('lib/**/*.rb')
  }
end

The important part is :

rdoc.template = "doc/rdoc_template/jamis.rb"

I tried to use the —template= and -T options in rdoc.options but it didn’t work… then I found that the RDocTask appends its template variable to the options passed to RDoc bypassing mine.

Published on Thu, 15 Feb 2007 13:35

RSS