ActiveSupport Option Hashes

A small blog item, for remembering this method…

When using a (normal) Hash in Ruby on Rails accessing an element with a symbol key or string key results in a different item.

Example
(Notice the use of a string and symbol to access a key)

normal = {'foo'=>"lish"}
normal[:foo] = 'bar'

results in the hash:

{"foo"=>"lish", :foo=>"bar"}

To solve this issue you can convert every Hash to HashWithInDifferentAccess

special = {'foo'=>'lish'}.with_indifferent_access
special[:foo] = 'bar'

Results in

{"foo"=>"bar"}
special.class => ActiveSupport::HashWithIndifferentAccess

that more like it :-)

“2008-03-30″.to_date.to_time.tomorrow == “2008-03-30″

I wrote a very nice routine which would iterate over a few days.
Today I found my loop never ending !?! And this is very scary because the routine is a background process that needs to iterate over the last x-days.

But there’s a problem when you’re living in The Netherlands and you have to use daylight savings.

Try this code:


>> "2008-03-30".to_date.to_time.tomorrow.to_date
=> Sun Mar 30

WTF !! 2008-03-30 => Tomorrow => 30 March 2008 ?!?

I indeed complained, that time was passing so quickly. But I didn’t mean to keep it stuck at 30 March!
Oh.. I’ts only my Rails Application…

How’s this possible?

>> "2008-03-30".to_date.to_time.tomorrow
=> Sun Mar 30 23:00:00 +0200 2008

At 30-03-2008 the clock has been set back for daylight savings. Well I assume rails simply adds 24 hours with the method tomorrow. And yesterday we had 25 hours.

More info about this bug: http://dev.rubyonrails.org/ticket/2353

My temporary solution is to add some hours to tomorrow.


>> tomorrow_time = "2008-03-30".to_date.to_time.tomorrow + 12*60*60
=> Mon Mar 31 11:00:00 +0200 2008
>> tomorrow_time.to_date
=> Mon, 31 Mar 2008

Sometimes I hate Ruby on Rails!

why don’t we collectively protest agianst the oppression of daylight saving time?
Well Tijn, I agree!

Ruby on Rails, ReXML Document serializing / deserializing

I’m storing an XML document into a database field. I’m having a lot of trouble loading and saving the same XML in the database.

Here’s a script/console session:

>> xml = REXML::Document.new("")   =>  ...   # strange response!!
>> xml.to_s   => " "   # seems ok!
>> xml.root.attributes['value'] = '<'
>> xml.to_s => ""  # fine by me, no problem...

>> # now for the scary part ;-)
>> xml2 = REXML::Document.new( xml.to_s )
>> xml2.to_s  => ""

The HORROR!

ReXML seems to escape items very nicely when setting values.
But it doesn’t unescape the values with REXML::Document.new( … )..

Current Progress:
* I found a method REXML::Document.write( ) which seems to do the same..

Today (24-8-2007) I’m a bit further, It seems it works correctly with the text content of elements:

>> xml = REXML::Document.new("")   =>  ...   # strange response!!
>> xml.to_s   => " "   # seems ok!
>> xml.root.attributes['value'] = '<'
>> xml.root.text = '>'
>> xml.to_s => ">"  # fine by me, no problem...

>> xml2 = REXML::Document.new( xml.to_s )
>> xml2.to_s  => ">"

Update 2 (24-8-2007) I found my Windows Ruby on Rails REXML (1.8.4) installation is working perfectly. It seems a bug in the FreeBSD version which is REXML (1.8.6).
I’m trying to submit a bug report to the REXML authors, but the server keeps timing out :(

I found the solution, there’s indeed a bug in REXML (1.8.6)

Change the code at line +/- 291 in text.rb: ( /usr/local/lib/ruby/1.8/rexml/text.rb )

#copy = copy.gsub( EREFERENCE, '&' )
copy = copy.gsub( "&", "&" )

To

copy = copy.gsub( EREFERENCE, '&' )
#copy = copy.gsub( "&", "&" )

Rails url_for and params missery

Assume you have a params array with the following items:

params[:qry][:name] = 'jo'
params[:qry][:city] = ''

You would like to build a new URL with the current params appended. Should be as simple as:

url_for( :action => 'list', :params => params.merge( { :sort => key, :page => nil } ) )

Well that’s not the case it results in the following URL:

http://localhost/controller/list?qry=namejocity&sort=created_at

Long live Rails! The frustrating part of it, is that it first converts inner Hashmaps to a string. I don’t have a clue why!

The solution, put the following code in your ApplicationHelper. (Is a nice method to have ;-)

#
# Converts the given HASH array like 'params' to a flat
# HASH array that's compatible with url_for and link_to
#
def flatten_param_hash( params )
  found = true

  while found
    found = false
    new_hash = {}

    params.each do |key,value|
      if value.is_a?( Hash )
        found = true
        value.each do |key2,value2|
          new_hash[ key.to_s + '[' + key2.to_s + ']' ] = value2
        end
      else
        new_hash[ key.to_s ] = value
      end
    end
    params = new_hash
  end
  params
end

Adjust your code to the following

url_for( :action => 'list', :params => flatten_param_hash( params.merge( { :sort => key, :page => nil } ) ) )

And voila the result is now a little bit better:

http://localhost/controller/list?qry[name]=jo&qry[city]=&sort=created_at