{"id":111,"date":"2008-01-06T02:15:00","date_gmt":"2008-01-06T02:15:00","guid":{"rendered":"\/2008\/08\/06\/convert-a-ruby-hash-into-a-class-object"},"modified":"2009-08-30T04:45:53","modified_gmt":"2009-08-30T04:45:53","slug":"convert-a-ruby-hash-into-a-class-object","status":"publish","type":"post","link":"http:\/\/pullmonkey.com\/2008\/01\/06\/convert-a-ruby-hash-into-a-class-object\/","title":{"rendered":"Convert a Ruby hash into a class object"},"content":{"rendered":"

I first saw the need to convert a hash object to a class when answering this post<\/a>.
\nIn the post, the user wanted to load a YAML object into his hash and then present the data from the hash in a form. Needless to say it was not very DRY the way it had to be implemented. So I started looking into it, I found
this<\/a>. This solution was a great starting point for where I ended up, but it was not general enough, it was hard coded, plus it was missing the getters and setters. So it turns out that in ruby it wasn't too much trouble to convert a hash into a class object. So let's get started:
\nI have implemented this for use in Rails, so let's start with the model that does all the magic:<\/p>\n\n\n
\n
1\n<\/tt>2\n<\/tt>3\n<\/tt>4\n<\/tt>5<\/strong>\n<\/tt>6\n<\/tt>7\n<\/tt>8\n<\/tt>9\n<\/tt>10<\/strong>\n<\/tt>11\n<\/tt><\/pre>\n<\/td>\n
\n
\n<\/tt>class<\/span> Hashit<\/span>\n<\/tt>  def<\/span> initialize<\/span>(hash)\n<\/tt>    hash.each do<\/span> |k,v|\n<\/tt>      self<\/span>.instance_variable_set("<\/span>@<\/span>#{<\/span>k}<\/span><\/span>"<\/span><\/span>, v)  ## create and initialize an instance variable for this key\/value pair<\/span>\n<\/tt>      self<\/span>.class.send(:define_method<\/span>, k, proc{self<\/span>.instance_variable_get("<\/span>@<\/span>#{<\/span>k}<\/span><\/span>"<\/span><\/span>)})  ## create the getter that returns the instance variable<\/span>\n<\/tt>      self<\/span>.class.send(:define_method<\/span>, "<\/span>#{<\/span>k}<\/span><\/span>=<\/span>"<\/span><\/span>, proc{|v| self<\/span>.instance_variable_set("<\/span>@<\/span>#{<\/span>k}<\/span><\/span>"<\/span><\/span>, v)})  ## create the setter that sets the instance variable<\/span>\n<\/tt>    end<\/span>\n<\/tt>  end<\/span>\n<\/tt>end<\/span>\n<\/tt>\n<\/tt><\/pre>\n<\/td>\n<\/tr>\n<\/table>\n

Notice the self.class.send(:define_method ...) rather than self.define_method, this is a hack to overcome the fact that define_method() is private. I had come across this when trying to figure out the post mentioned above. Found the information to solve this here<\/a>.
\nOk, so on to the Controller that creates the Hashit object:<\/p>\n\n\n
\n
1\n<\/tt>2\n<\/tt>3\n<\/tt>4\n<\/tt>5<\/strong>\n<\/tt>6\n<\/tt>7\n<\/tt>8\n<\/tt>9\n<\/tt><\/pre>\n<\/td>\n
\n
\n<\/tt>class<\/span> TestItController<\/span> < ApplicationController<\/span>\n<\/tt>  def<\/span> index<\/span>\n<\/tt>    hashit = {:support_email<\/span>  => "<\/span>test@test.com<\/span>"<\/span><\/span>,\n<\/tt>              :allow_comments<\/span> => 0<\/span>}\n<\/tt>    @hashit<\/span>  = Hashit<\/span>.new(hashit)\n<\/tt>  end<\/span>\n<\/tt>end<\/span>\n<\/tt>\n<\/tt><\/pre>\n<\/td>\n<\/tr>\n<\/table>\n

Well that is easy, pass in a hash and get an object. Here is what @hashit looks like at this point:<\/p>\n\n\n
\n
1\n<\/tt>2\n<\/tt><\/pre>\n<\/td>\n
\n
\n<\/tt>#<Hashit:0xb6a65110 @allow_comments=0, @support_email="test@test.com"><\/span>\n<\/tt><\/pre>\n<\/td>\n<\/tr>\n<\/table>\n

And of course, now the view, what we wanted to clean up and make more elegant. Here is what the user started with:
\nNote: In this example @hashit is an actual hash, not a class object.<\/p>\n\n\n
\n
1\n<\/tt>2\n<\/tt>3\n<\/tt>4\n<\/tt>5<\/strong>\n<\/tt>6\n<\/tt><\/pre>\n<\/td>\n
\n
\n<\/tt><% form_tag :action<\/span> => '<\/span>config<\/span>'<\/span><\/span>, :method<\/span> => :post<\/span> do<\/span> %><\/span>\n<\/tt>  <%= text_field 'settings',  'support_email', :size =<\/span>><\/span><\/span> 20<\/span>, :value<\/span> => @hashit<\/span>['<\/span>support_email<\/span>'<\/span><\/span>]%>\n<\/tt>  ...\n<\/tt><% end<\/span> %><\/span>\n<\/tt>\n<\/tt><\/span><\/span><\/pre>\n<\/td>\n<\/tr>\n<\/table>\n

And here is what our view code looks like now:<\/p>\n\n\n
\n
1\n<\/tt>2\n<\/tt>3\n<\/tt>4\n<\/tt>5<\/strong>\n<\/tt><\/pre>\n<\/td>\n
\n
\n<\/tt><% form_for :hashit<\/span>, :url<\/span> => {:action<\/span> => '<\/span>index<\/span>'<\/span><\/span>} do<\/span> |f| %><\/span>\n<\/tt>  <%= f.text_field :support_email %<\/span>><\/span><\/span>\n<\/tt><% end<\/span> %><\/span>\n<\/tt>\n<\/tt><\/span><\/span><\/pre>\n<\/td>\n<\/tr>\n<\/table>\n

Much simpler, DRYer \ud83d\ude42
\nWell that is pretty much it, I suppose the next step would be to have a save method that updates the hash? This way we can do @hashit.save() and it will return a new hash that you can use. Well actually, that probably isn't too hard, lets see if I can do it real quick, class object back to hash ...
\nWell, I am back and I was able to figure it out, here is the new class:<\/p>\n\n\n
\n
1\n<\/tt>2\n<\/tt>3\n<\/tt>4\n<\/tt>5<\/strong>\n<\/tt>6\n<\/tt>7\n<\/tt>8\n<\/tt>9\n<\/tt>10<\/strong>\n<\/tt>11\n<\/tt>12\n<\/tt>13\n<\/tt>14\n<\/tt>15<\/strong>\n<\/tt>16\n<\/tt>17\n<\/tt>18\n<\/tt>19\n<\/tt><\/pre>\n<\/td>\n
\n
\n<\/tt>class<\/span> Hashit<\/span>\n<\/tt>  def<\/span> initialize<\/span>(hash)\n<\/tt>    hash.each do<\/span> |k,v|\n<\/tt>      self<\/span>.instance_variable_set("<\/span>@<\/span>#{<\/span>k}<\/span><\/span>"<\/span><\/span>, v)\n<\/tt>      self<\/span>.class.send(:define_method<\/span>, k, proc{self<\/span>.instance_variable_get("<\/span>@<\/span>#{<\/span>k}<\/span><\/span>"<\/span><\/span>)})\n<\/tt>      self<\/span>.class.send(:define_method<\/span>, "<\/span>#{<\/span>k}<\/span><\/span>=<\/span>"<\/span><\/span>, proc{|v| self<\/span>.instance_variable_set("<\/span>@<\/span>#{<\/span>k}<\/span><\/span>"<\/span><\/span>, v)})\n<\/tt>    end<\/span>\n<\/tt>  end<\/span>\n<\/tt>\n<\/tt>  def<\/span> save<\/span>\n<\/tt>    hash_to_return = {}\n<\/tt>    self<\/span>.instance_variables.each do<\/span> |var|\n<\/tt>      hash_to_return[var.gsub("<\/span>@<\/span>"<\/span><\/span>,"<\/span>"<\/span><\/span>)] = self<\/span>.instance_variable_get(var)\n<\/tt>    end<\/span>\n<\/tt>    return<\/span> hash_to_return\n<\/tt>  end<\/span>\n<\/tt>end<\/span>\n<\/tt>\n<\/tt><\/pre>\n<\/td>\n<\/tr>\n<\/table>\n

Just added the save() method, that takes all the instance variables and sets them as keys in our new hash. So here is the outcome of our save():<\/p>\n\n\n
\n
1\n<\/tt>2\n<\/tt><\/pre>\n<\/td>\n
\n
\n<\/tt>{"<\/span>support_email<\/span>"<\/span><\/span>=>"<\/span>new@some_email<\/span>"<\/span><\/span>, "<\/span>allow_comments<\/span>"<\/span><\/span>=>0<\/span>}\n<\/tt><\/pre>\n<\/td>\n<\/tr>\n<\/table>\n","protected":false},"excerpt":{"rendered":"

I first saw the need to convert a hash object to a class when answering this post. In the post, the user wanted to load a YAML object into his hash and then present the data from the hash in a form. Needless to say it was not very DRY the way it had to […]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[6,3,5,9],"tags":[211],"_links":{"self":[{"href":"http:\/\/pullmonkey.com\/wp-json\/wp\/v2\/posts\/111"}],"collection":[{"href":"http:\/\/pullmonkey.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/pullmonkey.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/pullmonkey.com\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/pullmonkey.com\/wp-json\/wp\/v2\/comments?post=111"}],"version-history":[{"count":1,"href":"http:\/\/pullmonkey.com\/wp-json\/wp\/v2\/posts\/111\/revisions"}],"predecessor-version":[{"id":57366,"href":"http:\/\/pullmonkey.com\/wp-json\/wp\/v2\/posts\/111\/revisions\/57366"}],"wp:attachment":[{"href":"http:\/\/pullmonkey.com\/wp-json\/wp\/v2\/media?parent=111"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/pullmonkey.com\/wp-json\/wp\/v2\/categories?post=111"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/pullmonkey.com\/wp-json\/wp\/v2\/tags?post=111"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}