1

I want to create a special settings class Settings. The class should be able to handle cases when a user types something like Settings.new.method_1.method_2.method_3 and it's translated to something like:

result = nil
if ConfigurationSettings['method_1'].present? 
  result = ConfigurationSettings['method_1'] 
  if result['method_2'].present?
  result = result['method_2']
  ...
end

return result 

Of course, I'll make it more flexible later so it can have more than 2/3 "methods".

benams
  • 4,308
  • 9
  • 32
  • 74
  • I think a hashie/mash will help you here. e.g. ```ConfigurationSettings = { 'method_1' => { 'method_2' => 'asdf' } };``` ```Hashie::Mash.new(ConfigurationSettings).method_1.method_2``` – punkle Feb 13 '15 at 14:17
  • I can’t see what you’re asking here, but in particular why do you mention Yaml in the title and tags? Nothing in the question seems to have anything to do with it. – matt Feb 13 '15 at 17:11

3 Answers3

0

I guess this is the issue you are facing:

class Settings
 def abc
   puts "abc"
 end
 def xyz
   puts "xyz"
 end
end

s = Settings.new
s.abc
#abc
# => nil
s.xyz
#xyz
# => nil
s.abc.xyz
#abc
#NoMethodError: undefined method `xyz' for nil:NilClass

The issue here is s.abc is returning nil and xyz is called over nil. What you are trying to achieve is called Method Chaining. Now, xyz needs an Settings object. Simplest thing to do here is:

class Settings2
  def abc
    puts "abc"
    self
  end
  def xyz
    puts "xyz"
    self
  end
end

s2 = Settings2.new
s2.abc.xyz
#abc
#xyz
shivam
  • 16,048
  • 3
  • 56
  • 71
0

method_missing is available for your use and can be used to help you solve this problem. Coupling this with method chaining and you're good to go. For example:

class Settings
    def method_missing(meth)
        puts "Missing #{meth}"
        self
    end

    def test
        puts "Test"
        self
    end
end

a = Settings.new
a.test
a.test.b
a.b.test
David Hoelzer
  • 15,862
  • 4
  • 48
  • 67
0

The trouble with the other answers is all the methods return "self" so if you want to access a nested value...

final_value = Settings.new.method_1.method_2.method_3 

You're just going to get the whole settings hash instead.

Try this instead...

class Settings
  class SubSettings
    def initialize(sub_setting)
      @sub_setting = sub_setting
    end
    def method_missing(method, *arguments, &block)
      if @sub_setting[method].is_a?(Hash)
        SubSettings.new @sub_setting[method]
      else
        @sub_setting[method]
      end
    end
    def answer
      @sub_setting
    end
  end
  def initialize
    @settings = ConfigurationSettings
  end
  def method_missing(method, *arguments, &block)
    SubSettings.new @settings[method]
  end
end

ConfigurationSettings = {level1a: {level2a: {level3a: "hello", level3b: "goodbye"}, level2b: {level3b: "howdy"}}}

result = Settings.new.level1a.level2a.level3b
p result
=> "goodbye"

What this does is take the initial method and takes the associated sub-hash of the ConfigurationSettings hash and stored it into a new object of class SubSettings. It applies the next method and if the result is another sub-hash it iterates to create another SubSettings, etc. It only returns the actual result when it no longer sees hashes.

SteveTurczyn
  • 36,057
  • 6
  • 41
  • 53