Alternative HashWithIndifferentAccess

After having revisited ActiveSupport a few weeks ago to investigate Hash#reverse_merge!, I noticed ActiveSupport::HashWithIndifferentAccess.Excessive object allocation and branching in the critical path scratched an itch and spawned hwia.

Ruby hashes and the symbol table

MRI ships with a generic hash table for storage of variables, methods, class inheritance chains and the like.Please head off to an excellent overview on this subject by Jake Douglas and please do come back.

Hashes is tightly coupled with the symbol table implementation and as such requires a comparison (2 values as arguments) function

as well as a hashing function for each value (single argument)

A new object hash structure wraps the hash callback functions and is assigned as the hash type to the symbol table that represent the hash object.

Hashing symbols and strings

Symbols and strings are somewhat interchangeable at a lower level and conversion between either is relatively snappy.We extract a strhash function ( leeched from st.c ) and define


String#strhash
Symbol#strhash # thanks raggi!
'key'.strhash == :key.strhash #=> true

We also now need to define new hash and comparison functions to take advantage of this feature.Apologies for the fugly 1.8 / 1.9 conditionals.

and let the symbol table that powers our string like hash call them

Conversion and coercion

Special case handling for certain value assignments is required for the most prolific use case, that of representing request parameters, in a possibly nested fashion.

  • Hash converts to StrHash
  • Array converts Hash elements to StrHash

Installation


sudo gem install methodmissing-hwia
'require "hwia_rails"' # from within an initializer

This extension is compatible with MRI > 1.8.6 && 1.9.2 and Rails > 2.3.x

Examples

The Rails compatibility layer …


methodmissing:~ lourens$ irb
>> require 'rubygems'
=> true
>> require 'hwia_rails'
LoadError: Rails environment required!
from /opt/local/lib/ruby/gems/1.8/gems/hwia-1.0.0/lib/hwia_rails.rb:2
from /opt/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:36:in `gem_original_require'
from /opt/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:36:in `require'
from (irb):2
>> require 'activesupport'
=> true
>> require 'hwia_rails'
=> []
>> HashWithIndifferentAccess
=> StrHash

… basic usage


methodmissing:~ lourens$ irb
>> require 'rubygems'
=> true
>> require 'hwia'
=> true
>> h = StrHash[:a => 1, 'b' => 2]
=> {:a=>1, "b"=>2}
>> h[:a]
=> 1
>> h['a']
=> 1
>> h[:b]
=> 2
>> h.class
=> StrHash
>> h = { 'a' => 1, 'b' => 2 }
=> {"a"=>1, "b"=>2}
>> h = h.strhash
=> {"a"=>1, "b"=>2}
>> h[:a]
=> 1
>> h[:b]
=> 2

This implementation is compatible with the current (30/08/2009) Rails master branch’s test suite.Please refer to the test cases of either project for further examples.

Performance

Benchmarks available from the README.

You’d notice very little, if any, performance improvements using this extension in the request / response cycle for the vast majority of apps.HashWithIndifferentAccess is limited to query params and ActiveRecord nested attributes assignment.

Consider this a step towards an extraction of a query param object, reusable across many different frameworks and projects.

Follow me @ github or twitter if you enjoyed this article.Thanks for reading!

12 Responses to “Alternative HashWithIndifferentAccess”

  1. apeiros  on August 30th, 2009

    I fail to see the use of HWIA. In fact, I considered the use of such a construct in rails a code smell.
    Why do people use it?
    Is it so hard to remember whether your keys are strings or symbols?
    Or is it so hard to decide whether to use symbols or strings as keys?
    If the latter, the rule is simple: if you know what keys are going to be in your hash, use symbols. If you don’t, use strings.

    regards

  2. admin  on August 30th, 2009

    apeiros,

    It’s very much a legacy artifact from the 0./1.x releases.The YARV interpreter bridges most of this divide with a more string like Symbol implementation and subsequently almost performs as fast as this extension.

    The interchangeable string / symbol keys has since spilled over into other frameworks / extension libs as well.

    Point being that formal deprecation will break most existing apps as even developers on the same projects sometimes have different styles wrt. this pattern.Style guides ftw! :-)

    Thanks for the feedback,
    - Lourens

  3. rick  on August 30th, 2009

    aperios: It’s not that we can’t remember, it’s just that we prefer to use symbols for web params. The fact that symbols are not garbage collected means its a very bad idea to convert params to symbols.

  4. admin  on August 31st, 2009

    rick,

    Correct, 4 bytes ( sizeof(VALUE) ) on most systems per Symbol param if that was the case.20 bytes per 5 param request.

  5. Dialearn  on November 25th, 2009

    Счастье – не награда за добродетель, а сама добродетель; не потому мы наслаждаемся счастьем, что обуздали свои страсти, а наоборот, наслаждение счастьем делает нас способными обуздать их. – Б. Спиноза

  6. Vladislav27  on March 15th, 2010

    Ингаляторы

  7. Vladislav54  on March 20th, 2010

    экология

  8. Makayla  on July 16th, 2014

    Finally i quit my day job, now i earn a lot of money on-line you should try
    too, just type in google – bluehand roulette system

  9. Vernell  on August 30th, 2014

    I read a lot of interesting content here. Probably you spend a lot of time writing, i know how to save you a lot of time, there is an online tool
    that creates readable, google friendly articles in minutes, just type in google – laranitas free content source

  10. Anisha  on November 23rd, 2014

    That’s the thiiknng of a creative mind


Leave a Reply