2

I've a module Settings like the following:

module Settings
  extend self

  @_settings = {user: "user1"} #@_seetings would normally be filled with different (dynamic) options
  attr_reader :_settings

  def method_missing(name, *args, &block)
    @_settings.has_key?(name.to_sym) ? @_settings[name.to_sym] :
      raise(NoMethodError, "unknown configuration root #{name}", caller)
  end
end

In my application I can use Settings.user to access the option user

Now I would like to do something like this Settings.user = "some_user"

I've tried to add this:

def method_missing=(name, *args, &block)
  #some code to assign the value
end

Unfortunately this is not working. (unknown configuration root user= (NoMethodError)). What is the 'right' way to do this?

Benedikt Bock
  • 1,007
  • 15
  • 37

2 Answers2

3

A method name of something= referring to a nonexistent method will still be caught by method_missing.

You might want to modify method_missing to see if the name ends with '=' and behave accordingly.

That said, if you say Settings.method_name, then you are calling a method on the Settings module, and not the instance that includes that module.

Also, modules do not contain state; this is one of the things that distinguishes them from classes. Also, are you sure you would want to override a method_missing method of the including class (or have it ignored if the class' method prevails?).

Can you explain why you are using a Module for this? Maybe instead you'd want to define a class and just contain an instance of it (composition rather than inheritance) in the class that uses it?

Keith Bennett
  • 4,722
  • 1
  • 25
  • 35
  • The application is a cli tool. The Settings module parses the commandline options and provides them to the application. It is not inherited or included in other classes/modules. It's just required. – Benedikt Bock May 30 '16 at 16:32
  • I think the module approach you're using adds *way* more complexity than is necessary. You may be able to use an `OpenStruct` for this. Check it out at http://ruby-doc.org/stdlib-2.0.0/libdoc/ostruct/rdoc/OpenStruct.html. I use this all the time for command line options. – Keith Bennett May 30 '16 at 16:54
1

The short answer is that you can't override = in Ruby, and it's considered dangerous to override these types of operators. See this answer for more details.

However, this exact use case is well handled by ruby. You have two options:

def user= (some_object)
   @_settings = {user: some_object}
end

or make user an attribute of Setting and use attr_accessor

module Settings
  attr_accessor :user
  def new(user)
    @user = user
  end
end

You're almost there with attr_reader it is half of attr_accessor with the other half being attr_writer. Check out this other SO thread

Community
  • 1
  • 1
Chase
  • 2,748
  • 2
  • 23
  • 32