Help me understand this technique from an open source app?

Discussion in 'Ruby' started by Pito Salas, Jan 24, 2009.

  1. Pito Salas

    Pito Salas Guest

    n the code at the bottom (from HTTParty), the module is included in
    some other class ("O"), and among the effects is that (for example)
    the method default_params becomes available in "O". I don't understand
    the technique. Given that the module is used as a mixin, then if I
    wanted to have that method, I could just define it under module
    HTTParty with a normal def. Moreover, the following doesn't work:

    class Parent
    include HTTParty

    def my_method
    default_params :x => "X"
    end
    end

    class Child < Parent
    def other
    my_method
    end
    end

    There's some sophisticated meta programming going on. Any help
    understanding it would be greatly appreciated!

    Pito

    =============from HTTParty==============
    module HTTParty
    AllowedFormats = {:xml => 'text/xml', :json => 'application/json',
    :html => 'text/html'}

    def self.included(base)
    base.extend ClassMethods
    base.send :include, ModuleLevelInheritableAttributes
    base.send:)mattr_inheritable, :default_options)
    base.instance_variable_set("@default_options", {})
    end

    module ClassMethods
    def default_options
    @default_options
    end

    def http_proxy(addr=nil, port = nil)
    default_options[:http_proxyaddr] = addr
    default_options[:http_proxyport] = port
    end

    def base_uri(uri=nil)
    return default_options[:base_uri] unless uri
    default_options[:base_uri] = HTTParty.normalize_base_uri(uri)
    end

    def basic_auth(u, p)
    default_options[:basic_auth] = {:username => u, :password => p}
    end

    def default_params(h={})
    raise ArgumentError, 'Default params must be a hash' unless
    h.is_a?(Hash)
    default_options[:default_params] ||= {}
    default_options[:default_params].merge!(h)
    end

    def headers(h={})
    raise ArgumentError, 'Headers must be a hash' unless h.is_a?
    (Hash)
    default_options[:headers] ||= {}
    default_options[:headers].merge!(h)
    end

    def format(f)
    raise UnsupportedFormat, "Must be one of:
    #{AllowedFormats.keys.join(', ')}" unless AllowedFormats.key?(f)
    default_options[:format] = f
    end

    def get(path, options={})
    perform_request Net::HTTP::Get, path, options
    end

    def post(path, options={})
    perform_request Net::HTTP::post, path, options
    end

    def put(path, options={})
    perform_request Net::HTTP::put, path, options
    end

    def delete(path, options={})
    perform_request Net::HTTP::Delete, path, options
    end

    private
    def perform_request(http_method, path, options) #:nodoc:
    Request.new(http_method, path,
    default_options.dup.merge(options)).perform
    end
    end
    --
    Posted via http://www.ruby-forum.com/.
     
    Pito Salas, Jan 24, 2009
    #1
    1. Advertising

  2. Pito Salas wrote:
    > n the code at the bottom (from HTTParty), the module is included in
    > some other class ("O"), and among the effects is that (for example)
    > the method default_params becomes available in "O".


    Well, consider this first:

    module ClassMethods
    def foo
    puts "hello from class"
    end
    end

    module InstanceMethods
    def bar
    puts "hello from instance"
    end
    end

    class Parent
    include InstanceMethods
    extend ClassMethods
    end

    Parent.foo
    Parent.new.bar

    However this pattern is very common, so it's refactored in two ways.

    1. put the module containing class methods *inside* the module
    containing the instance methods

    module MyTools
    ... instance methods go here

    module ClassMethods
    ... class methods go here
    end
    end

    2. use the "included" hook so that when you include MyTools, it
    automatically does extend MyTools::ClassMethods at the same time.

    The example above becomes:

    module MyTools
    def self.included(base)
    base.extend ClassMethods
    end

    module ClassMethods
    def foo
    puts "hello from class"
    end
    end

    def bar
    puts "hello from instance"
    end
    end

    class Parent
    include MyTools
    end

    Parent.foo
    Parent.new.bar

    At least, I *think* that's what you were asking about. There's also the
    ModuleLevelInheritableAttributes there, but you didn't post all the code
    for that. But notice that the include hook is calling

    mattr_inheritable :default_options

    in the base class, and also setting a class instance variable.

    HTH,

    Brian.
    --
    Posted via http://www.ruby-forum.com/.
     
    Brian Candler, Jan 25, 2009
    #2
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Weng Tianxiang
    Replies:
    4
    Views:
    3,147
    Weng Tianxiang
    Apr 7, 2005
  2. Replies:
    4
    Views:
    565
    Chris Uppal
    May 5, 2005
  3. baumann@pan
    Replies:
    6
    Views:
    431
    Bronek Kozicki
    May 19, 2005
  4. www
    Replies:
    3
    Views:
    357
  5. pat eyler
    Replies:
    1
    Views:
    497
    Masayoshi Takahashi
    Mar 5, 2005
Loading...

Share This Page