Forum: Ruby on Rails with_options Confusion

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Ddf9c98db9ac40c6875fd2a5b2d4fa72?d=identicon&s=25 Eric Northam (Guest)
on 2007-03-02 16:27
(Received via mailing list)
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
Bce1d1b7c3ec7b577dcb42e254899e6b?d=identicon&s=25 Michael Schuerig (Guest)
on 2007-03-02 17:04
(Received via mailing list)
On Friday 02 March 2007, Eric Northam 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 Schuerig
mailto:michael@schuerig.de
http://www.schuerig.de/michael/
Ddf9c98db9ac40c6875fd2a5b2d4fa72?d=identicon&s=25 Eric Northam (Guest)
on 2007-03-02 17:43
(Received via mailing list)
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
This topic is locked and can not be replied to.