With_options Confusion


#1

I was looking through the code of attachment_fu and came across the
following block pulled from attachment_fu:60

with_options :foreign_key => ‘parent_id’ do |m|
m.has_many :thumbnails, :dependent => :destroy, :class_name =>
options[:thumbnail_class].to_s
m.belongs_to :parent, :class_name => base_class.to_s
end

I’ve never seen with_options called without being tied to a map, e.g.
map.with_options, so I opened up the with_options code to see what was
going on and with my newbie Ruby knowledge can’t seem to figure this
one out.

Here’s the object extension in ActiveSupport that gets called

def with_options(options)
yield ActiveSupport::OptionMerger.new(self, options)
end

That seems straight forward but the OptionMerger boggles me. Here’s
the full module

module ActiveSupport
class OptionMerger #:nodoc:
instance_methods.each do |method|
undef_method(method) if method !~ /^(__|instance_eval|class)/
end

def initialize(context, options)
  @context, @options = context, options
end

private
  def method_missing(method, *arguments, &block)
    merge_argument_options! arguments
    @context.send(method, *arguments, &block)
  end

  def merge_argument_options!(arguments)
    arguments << if arguments.last.respond_to? :to_hash
      @options.merge(arguments.pop)
    else
      @options.dup
    end
  end

end
end

I take it that the object is initialized and then the instance methods
are iterated over undeffing methods that don’t match the give regex.
Why? I don’t know. The instance_methods method would also just apply
to the OptionMerger class methods right? I also don’t see how the
private methods are called. Any help to further expand my ruby/
ActiveSupport knowledge is much appreciated.

Eric


#2

On Friday 02 March 2007, Eric N. wrote:

map.with_options, so I opened up the with_options code to see what
was going on and with my newbie Ruby knowledge can’t seem to figure
this one out.

with_options is general, not tied to map/routes. Think of it as a
decorator (http://en.wikipedia.org/wiki/Decorator_pattern) or AOP-style
before advice. Its sole assumption is that the last argument of any
method of the decorated object takes an options hash.

When used as map.with_options { … } it is the map object that is
decorated. When no receiver is explicitly given, self is decorated in
the block.

class OptionMerger #:nodoc:
merge_argument_options! arguments
end
end

I take it that the object is initialized and then the instance
methods are iterated over undeffing methods that don’t match the give
regex. Why? I don’t know.

Because that ensures that the existing instance methods that plain Ruby
objects come with don’t get in the way. Therefore, when you call a
method on an OptionMerger instance, its method_missing method is
called, which in turn merges the common options, defined in the
enclosing with_options call, into the options of the current call and
delegates to the real receiver.

The instance_methods method would also just
apply to the OptionMerger class methods right?

Yes.

I also don’t see how the private methods are called.

method_missing is called by the Ruby runtime, not explicitly.

HTH,
Michael


Michael S.
mailto:removed_email_address@domain.invalid
http://www.schuerig.de/michael/


#3

Not exactly intuitive but very cool. I don’t know why I didn’t realize
the method_missing method was called due to the missing methods.

Vielen dank,

Eric