How to factor this code ? (not trivial)

34 views
Skip to first unread message

Nicolas Desprès

unread,
Mar 4, 2013, 7:59:23 AM3/4/13
to rubyonra...@googlegroups.com
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

Frederick Cheung

unread,
Mar 4, 2013, 1:36:07 PM3/4/13
to rubyonra...@googlegroups.com
On Monday, March 4, 2013 12:59:23 PM UTC, Nicolas Desprès wrote:
> Hi,
>
>
> 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.
>
>

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

Nicolas Desprès

unread,
Mar 5, 2013, 4:37:24 AM3/5/13
to rubyonra...@googlegroups.com
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

===========================
My goal is to provide a way for users to write their "id" method once and to work in both cases.

I am thinking about something like that:

def id(*args)
  if name_required?
    name = name_given? ? given_name : "id"
  end

  integer name, an_option: true
end

def integer(*args)
  value = MyInteger.new(options)
  if name_required?
    if name_given?
      store_result given_name, value
    else
      raise ArgumentError
    end
  else
    if name_given?
      raise ArgumentError
    end
    store_result value
  end
end

where "name_required?", "given_name" and "name_given?" would be defined appropriately based on the arguments passed to the method.

I hope this is clearer now.

--
Nicolas Desprès

Nicolas Desprès

unread,
Mar 12, 2013, 4:45:31 AM3/12/13
to rubyonra...@googlegroups.com
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. 


--
Nicolas Desprès
Reply all
Reply to author
Forward
0 new messages