1

How can I populate instance variables from a config hash that I want to park inside my class definition? I have a development and production set of values that I want to shove into instance variables at initialize(). How can I accomplish this?

Here is what I have:

class Alpha

    CONFIG = {  development: {mid: '123', uid: 'trial', pin: 'abcde' },
        production: {mid: '65432', uid: 'go_go_go', pin: 'kwir7' }
        }

    def initialize(environment = 'development')
        @env = environment
        @mid = nil
        @uid = nil
        @pin = nil
    end

end

a = Alpha.new('development)
puts a.inspect      
# ==> #<Alpha:0x00007fb1228d6b68 @env="development", @mid=nil, @uid=nil, @pin=nil>

I would like to use the Config constant to populate the three empty instance variables. Ideally I would like to set them inside initalize(), however a call to a private method to set those values can be done. The challenge remains the same, using either a class variable or constant to grab it from, and I can't seem to achieve this. I am assuming that parking such account key-value pairs inside the class definition, is best practice for such an application. i.e.: eCommerce account metrics.

I've tried the following, which can't seem to get the Config hash values referenced properly.

@mid = Alpha::Config[environment.to_s][:mid]

Just so that nobody balks on this post, the question is not answered in these posts:

Ruby: defining class level hash with default values

Confusion about ruby class variable

Rich_F
  • 1,830
  • 3
  • 24
  • 45

1 Answers1

3

Remember that in Ruby symbols and strings are not equivalent, so you must explicitly convert when looking up in a Hash with symbol keys:

class Alpha
  CONFIG = {
    development: { mid: '123', uid: 'trial', pin: 'abcde' },
    production: { mid: '65432', uid: 'go_go_go', pin: 'kwir7' }
  }

  attr_reader :mid, :uid, :pid

  def initialize(environment = 'development')
    @env = environment.to_sym

    @mid = CONFIG.dig(@env, :mid)
    @uid = CONFIG.dig(@env, :uid)
    @pid = CONFIG.dig(@env, :pid)
  end
end

Now it works:

Alpha.new('development').mid
# => "123"

The .to_sym call when assigning @env takes care of the conversion, then it can be used past that point. dig is used to avoid crashing if a particular environment isn't defined, the values just come up nil instead.

tadman
  • 208,517
  • 23
  • 234
  • 262
  • OK, I was also unclear if instance vars were visible by the next assignments inside `initalize()`. All this has fixed it. – Rich_F Feb 01 '19 at 17:52
  • Instance variables are "visible" to any method being executed by the *same instance*, so within an initializer they become immediately available for use in other parts of that same method's code, or in any instance methods that code calls. – tadman Feb 01 '19 at 17:53
  • OK, but I was more concerned about the actual first `init()` method, as opposed to other methods. Technically, the object isn't considered instantiated until the end of that first oncreate method. – Rich_F Feb 01 '19 at 17:54
  • It "exists" as soon as `initialize` is called, so you're free to call other instance methods at that point. – tadman Feb 01 '19 at 17:55
  • OK, just to be clear, I'll reference your post: `@env` is defined. `@mid` is still within the `init()` so I wasn't sure if the object was complete to be referenced at this point. – Rich_F Feb 01 '19 at 17:57
  • 1
    What I mean is just prior to `initialize` being called the instance of `Alpha` is a fully operational object. You can call methods, set instance variables, or whatever you want, without restriction. The only thing unusual about `initialize` is that you aren't supposed to explicitly call it, and secondly, that the return value is ignored. If you define `@env` then a subsequent line of code can reference it. All `initialize` does is give you a chance to set up the object however you see fit, including, but not necessarily, calling `super` to deal with inherited behaviour if applicable. – tadman Feb 01 '19 at 18:00
  • So if I make a `populate()` method that could set `ivars`, and even set it to `protected` or `private`, I can shove that into `init()` and it should work fine? Ya I never want to call `init()` because that would be a repeat of an automatic oncreate. – Rich_F Feb 01 '19 at 18:02
  • You can break out certain operations into other methods like `populate` if you want, absolutely. – tadman Feb 01 '19 at 18:06
  • I always disliked multiple options. Hah. OK, Cheers – Rich_F Feb 01 '19 at 18:06
  • Ruby doesn't have one way to do anything, which is why it's so expressive, but if you come up with conventions you like that work for you and your team, by all means, use those. – tadman Feb 01 '19 at 18:08