Satchel
Satchel

Reputation: 16724

can I pass a hash to an instance method to create instance variables?

I have a has like this:

reminder_hash = {"bot_client_id"=>"test-client-id", "recurring"=>true, "recurring_natural_language"=>"everyday", "time_string"=>"10AM", "time_zone"=>"America/Los_Angeles", "via"=>"slack", "keyword"=>"test-keyword", "status"=>"active", "created_time"=>1444366166000}

I would like to pass to method like this (from Rspec):

let(:reminder) { Reminder.new( reminder_hash ) }

I would like the instance variables for Reminder to be based on the hash.

expect(reminder.bot_client_id).to eq('test-client-id')

I can't get it to work. I tried the following:

class Reminder

attr_accessor :bot_client_id

def initiate(hash)
    # http://stackoverflow.com/questions/1615190/declaring-instance-variables-iterating-over-a-hash

    hash.each do |k,v|

      instance_variable_set("@#{k}",v)
      # if you want accessors:

      eigenclass = class<<self; self; end

      eigenclass.class_eval do

        attr_accessor k
      end

    end

  end

Rspec gives the following error:

Failure/Error: let(:reminder) { Reminder.new( reminder_hash ) }
 ArgumentError:
   wrong number of arguments (1 for 0)

Question: How do I pass a hash to an instance method for an object, so that the hash values are the instance variables for that newly created object?

Upvotes: 1

Views: 874

Answers (2)

steenslag
steenslag

Reputation: 80065

Ruby has OpenStruct to do that.

require 'ostruct'

reminder_hash = {"bot_client_id"=>"test-client-id", "recurring"=>true, "recurring_natural_language"=>"everyday", "time_string"=>"10AM", "time_zone"=>"America/Los_Angeles", "via"=>"slack", "keyword"=>"test-keyword", "status"=>"active", "created_time"=>1444366166000}
reminder = OpenStruct.new(reminder_hash)

p reminder.bot_client_id # => "test-client-id"

Use a Struct for a full blown Class:

Reminder = Struct.new(*reminder_hash.keys.map(&:to_sym))
r = Reminder.new(*reminder_hash.values)

p r.bot_client_id # => "test-client-id"

Upvotes: 0

Anthony
Anthony

Reputation: 15967

Sure you can do something like this in your initialize method:

     hash.each do |k, v|
        instance_variable_set("@#{k}", v)
        self.class.send(:attr_reader, k)
      end

Here's an example using your input hash:

class Reminder
  def initialize(hash)
    hash.each do |k, v|
      instance_variable_set("@#{k}", v)
      self.class.send(:attr_reader, k)
    end
  end
end


reminder_hash = {"bot_client_id"=>"test-client-id", "recurring"=>true, "recurring_natural_language"=>"everyday", "time_string"=>"10AM", "time_zone"=>"America/Los_Angeles", "via"=>"slack", "keyword"=>"test-keyword", "status"=>"active", "created_time"=>1444366166000}

reminder = Reminder.new(reminder_hash)
puts reminder

puts reminder.bot_client_id

Output:

#<Reminder:0x007f8a48831498>
test-client-id

Upvotes: 1

Related Questions