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

- (DSLWrapper) initialize(owner, &block)

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



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

- (Object) method_missing(meth, *args, &block) (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

- (Object) owner (readonly)



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

def owner
  @owner
end