Class: Releasy::DSLWrapper

Inherits:
Object
  • Object
show all
Defined in:
lib/releasy/dsl_wrapper.rb

Overview

Wraps an object and redirects public methods to it, to allow for a terse, block-based API.

  • Safer alternative to running Object#instance_eval directly, since protected/private methods and instance variables are not exposed.

  • Less wordy than a system which operates like Object#tap (`object.tap {|o| o.fish = 5; o.run }`)

A method call, #meth called on the wrapper will try to call #meth or #meth= on the owner, as appropriate.

Examples:

# To create a DSL block for a given object.
class Cheese
  attr_accessor :value
  attr_accessor :list
  def initialize; @value = 0; @list = []; end
  def invert; @list.reverse!; end
end

object = Cheese.new
Releasy::DSLWrapper.wrap object do
  list [1, 2, 3]      # Calls object.list = [1, 2, 3]
  list << 4           # Calls object.list << 4
  value 5             # Calls object.value = 5
  value list.size     # Calls object.value = object.list.size
  invert              # Calls object.invert
end

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(owner, &block) ⇒ DSLWrapper

If passed a block, the DSLWrapper will #instance_eval it automatically.

Parameters:

  • owner (Object)

    Object to redirect the public methods of.



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/releasy/dsl_wrapper.rb', line 39

def initialize(owner, &block)
  @owner = owner

  metaclass = class << self; self; end

  (@owner.public_methods - Object.public_instance_methods).each do |target_method|
    redirection_method = target_method.to_s.chomp('=').to_sym

    metaclass.class_eval do
      define_method redirection_method do |*args, &inner_block|
        if @owner.respond_to? "#{redirection_method}=" and (args.any? or not @owner.respond_to? redirection_method)
          # Has a setter and we are passing argument(s) or if we haven't got a corresponding getter.
          @owner.send "#{redirection_method}=", *args, &inner_block
        elsif @owner.respond_to? redirection_method
          # We have a getter or general method
          @owner.send redirection_method, *args, &inner_block
        else
          # Should never reach here, but let's be paranoid.
          raise NoMethodError, "#{owner} does not have a public method, ##{redirection_method}"
        end
      end
    end
  end

  instance_eval &block if block_given?
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(meth, *args, &block) ⇒ Object (private)

Raises:

  • (NoMethodError)


67
68
69
# File 'lib/releasy/dsl_wrapper.rb', line 67

def method_missing(meth, *args, &block)
  raise NoMethodError, "#{owner} does not have either public method, ##{meth} or ##{meth}="
end

Instance Attribute Details

#ownerObject (readonly)

Returns Object that the DSLWrapper object is redirecting to.

Returns:

  • (Object)

    Object that the DSLWrapper object is redirecting to.



29
30
31
# File 'lib/releasy/dsl_wrapper.rb', line 29

def owner
  @owner
end