I have a file that uses a DSL for complex configuration, part of that larger DSL is a name/value settings DSL.
Whenever I use a setting called name, I get an error.
Failure/Error: name 'SomeName' ArgumentError: wrong number of arguments (given 1, expected 0)
I'm looking for a solution to this edge-case.
Any instance of the SettingsDsl can have a unique name to identify itself, this is an optional parameter for the initializer.
example
main_dsl = MainDsl.new do
settings do
rails_port 3000
end
settings :key_values do
rails_port 3000
end
end
This would create two instances of SettingsDsl, one with a name of :settings and the other with a name of :key_values
This is the code for MainDsl and SettingsDsl
class MainDsl
attr_reader :meta_data
def initialize(&block)
@meta_data = HashWithIndifferentAccess.new
if block_given?
self.instance_eval(&block)
end
end
def settings(name = nil,&block)
settings = SettingsDsl.new(@meta_data, name, &block)
puts setting.name
settings
end
end
class SettingsDsl
attr_reader :name
def initialize(data, name = nil, &block)
@data = data
@name = name ||= :settings
@data[name] = {}
self.instance_eval(&block) if block_given?
end
def method_missing(key, *args, &block)
@data[@name][key] = args[0]
end
def get_value(key)
@data[@name][key]
end
end
All works well until I use an internal key/value pair called name
I use method_missing to find new keys and store those values into a Hash, but in the case of name, there is already an attr_reader and this uses a slightly different signature and causes an argument error.
main_dsl = MainDsl.new do
settings do
rails_port 3000
name 'SomeName' # Intercept the error for this and store 'SomeName' into @data[@name]['name']
another_property 'Something Else'
end
settings :more do
hello 'world'
end
end
Failure/Error: name 'SomeName' ArgumentError: wrong number of arguments (given 1, expected 0)
The problem happens because internally there is a name attribute for the settings group, e.g.
SettingsDsl.new('name_goes_here')
# and gets stored in @name
# and is accessible via attr_reader :name
# which creates a read-only method called name. e.g.
SettingsDsl.new('name_goes_here').name
I would like to intercept the ArgumentError for this one method call and handle this edge-case appropriately
Final output could then look like
{
'settings': {
'rails_port': 3000,
'name': 'SomeName',
'another_property': 'Something Else'
},
'more': {
'hello': 'world'
}
}
method_missingcalls to the target mutator.