Module: SleepingKingStudios::Tools::Toolbox::Delegator

Defined in:
lib/sleeping_king_studios/tools/toolbox/delegator.rb

Overview

Module for extending classes with basic delegation. Supports passing arguments, keywords, and blocks to the delegated method.

Examples:

class MyClass
  extend SleepingKingStudios::Tools::Delegator

  delegate :my_method, :to => MyService
end # class

Instance Method Summary collapse

Instance Method Details

#delegate(*method_names, to: nil, allow_nil: false) ⇒ Object

Defines a wrapper method to delegate implementation of the specified method or methods to an object, to the object at another specified method, or to the object at a specified instance variable.

Examples:

Delegate to an object

class MyModule
  extend SleepingKingStudios::Tools::Toolbox::Delegator

  delegate :my_method, :to => MyService
end # class

Delegate to a method

class MyModule
  extend SleepingKingStudios::Tools::Toolbox::Delegator

  def my_service
    MyService.new
  end # method my_service

  delegate :my_method, :to => :my_service
end # class

Delegate to an instance variable

class MyModule
  extend SleepingKingStudios::Tools::Toolbox::Delegator

  def initialize
    @my_service = MyService.new
  end # constructor

  delegate :my_method, :to => :@my_service
end # class

Parameters:

  • method_names (Array<String, Symbol>)

    The names of the methods to delegate.

  • to (Object, String, Symbol) (defaults to: nil)

    The object, method, or instance variable to delegate to. If the object is not a string or symbol, the generated method will call ‘method_name` on the object. If the object is a string or symbol, but does not start with an `@`, the generated method will call the method of that name on the instance, and then call `method_name` on the result. If the object is a string or symbol and does start with an `@`, the generated method will get the instance variable of that name and call `method_name` on the result.

Raises:

  • ArgumentError if no delegate is specified.



61
62
63
64
65
66
67
# File 'lib/sleeping_king_studios/tools/toolbox/delegator.rb', line 61

def delegate *method_names, to: nil, allow_nil: false
  raise ArgumentError.new('must specify a delegate') if to.nil? && !allow_nil

  method_names.each do |method_name|
    delegate_method method_name, to, { :allow_nil => !!allow_nil }
  end # each
end

#wrap_delegate(target, klass: nil, except: [], only: []) ⇒ Object

Wraps a delegate object by automatically delegating each method that is defined on the delegate class from the instance to the delegate. The delegate can be specified with an object literal or with the name of an instance method or instance variable.

Only methods that are defined at the time #wrap_delegate is called will be delegated, so make sure to call #wrap_delegate after loading any gems or libraries that extend your delegate class, such as ActiveSupport.

Examples:

Create a class that wraps a Hash

class Errors
  extend SleepingKingStudios::Tools::Delegator

  wrap_delegate Hash.new { |hsh, key| hsh[key] = Errors.new }, :klass => Hash

  def messages
    @messages ||= []
  end # method messages
end # class

errors = Errors.new
errors[:post].messages << "title can't be blank"

Parameters:

  • target (Object, String, Symbol)

    The object, method, or instance variable to delegate to. If the object is not a string or symbol, the generated method will call each method on the object. If the object is a string or symbol, but does not start with an ‘@`, the generated method will call the method of that name on the instance, and then call each method on the result. If the object is a string or symbol and does start with an `@`, the generated method will get the instance variable of that name and call each method on the result.

  • klass (Module) (defaults to: nil)

    The class or module whose methods are delegated to the target. If target is the name of an instance variable or an instance method, the klass must be specified. If target is an object literal, the klass is optional, in which case all methods from the target will be delegated to the target.

  • except (Array<String, Symbol>) (defaults to: [])

    An optional list of method names. Any names on the list will not be delegated, even if the method is defined by the klass or defined on the target literal.

  • only (Array<String, Symbol>) (defaults to: [])

    An optional list of method names. Only names on the list will be delegated, and only if the method is defined by the klass or defined on the target literal.

Raises:

  • ArgumentError if no delegate is specified.

  • ArgumentError if the target is the name of an instance method or an instance variable and no klass is specified.

  • ArgumentError if the target is an object literal that does not belong to the specified klass.

See Also:



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/sleeping_king_studios/tools/toolbox/delegator.rb', line 119

def wrap_delegate target, klass: nil, except: [], only: []
  if klass.is_a?(Module)
    unless target.is_a?(String) || target.is_a?(Symbol) || target.is_a?(klass)
      raise ArgumentError.new "expected delegate to be a #{klass.name}"
    end # unless

    method_names = klass.instance_methods - Object.instance_methods
  elsif target.is_a?(String) || target.is_a?(Symbol)
    raise ArgumentError.new 'must specify a delegate class'
  else
    method_names = target.methods - Object.new.methods
  end # if-elsif-else

  if except.is_a?(Array) && !except.empty?
    method_names = method_names - except.map(&:intern)
  end # if

  if only.is_a?(Array) && !only.empty?
    method_names = method_names & only.map(&:intern)
  end # if

  delegate *method_names, :to => target
end