How to factor this code ? (not trivial)

Hi,

I’m having trouble to factor some simple code. Since, I have been stuck for several days now I ask the question here.

Beginning of code

module DefWithName

def integer(name, options = {})

store_result name, MyInteger.new(options)

end

def id(name)

integer(name, an_options: true)

end

end

module DefWithoutName

def integer(options = {})

store_result MyInteger.new(options)

end

def id

integer(an_options: true)

end

end

class A; include DefWithName; end

class AA; include DefWithName; end

class B; include DefWithoutName; end

class BB; include DefWithoutName; end

End of code

Constraints:

  • The “id” function has to call “integer” and not “store_result” because it may be defined as a plugin.

  • The functions must still raise ArgumentError as they already do.

Any ideas?

Regards,

-Nico

Could you give some examples of what you're trying to do or problems you've run into? You've posted some code but haven't described how you'll be using it and in what way it isn't adequate.In fewer words: I don't see what your question is.

Fred

Thanks for replying.

Yes sorry. I realize that some context is missing.

I am writing an API where the users are allowed to write something like that:

FooContextWithName.new.eval do |o|

o.integer “id”, an_option: true # [1]

o.integer “foo”, an_option: true # [2]

o.bar “a_name” do |o|

o.integer an_option: true # [3]

end

end

I would like that users are able to factor [1] and [2] by writing a small plugin like this:

module MyHelpers

def id(name = “id”)

integer name, an_option: true

end

end

MyLib.add_helpers(MyHelpers) # add_helpers would be defined appropriately

To factor [3] users should be able to write something like that:

module MyHelpers

def id

integer an_option: true

end

end

To implement this API I have something like this

module DefWithName

def integer(name, options = {})

store_result name, MyInteger.new(options)

end

end

module DefWithoutName

def integer(options = {})

store_result MyInteger.new(options)

end

end

class FooWithName < BasicObject

include DefWithName

def store_result(name, object)

@props[name] = object

end

end

class BarWithoutName < BasicObject

include DefWithoutName

def store_result(object)

@items << object

end

end

I finally found a good way to factor this.

Instead of having two modules DefWithoutName and DefWithName I have only one:

module BasicDef

def integer(name, options = {})

store_result name, MyInteger.new(options)

end

end

Then I use a proxy for evaluation that will

  • in the case of “with name” will call directly the target method and pass it all the arguments

  • in the case of “without name” will call the target method by passing “nil” as first arguments and then the rest of the arguments.

The proxy checks when the target method requires a name as first argument using the Method#parameters method.