Module: Yaks::Configurable

Included in:
Builder, Mapper
Defined in:
lib/yaks/configurable.rb

Overview

A “Configurable” class is one that keeps a configuration in a separate immutable object, of type class::Config. say you have

class MyMapper < Yaks::Mapper
  # use yaks configuration DSL in here
end

The links, associations, etc, that you set up for MyMapper, will be available in MyMapper.config, which is an instance of Yaks::Mapper::Config.

Each configuration step, like ‘link`, `has_many`, will replace MyMapper.config with an updated version, discarding the old config.

By extending Configurable, a number of “macros” become available to describe the DSL that subclasses can use. See the docs for ‘def_set`. `def_forward`, and `def_add`.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#configObject

Returns the value of attribute config.



21
22
23
# File 'lib/yaks/configurable.rb', line 21

def config
  @config
end

Class Method Details

.extended(child) ⇒ Object



23
24
25
# File 'lib/yaks/configurable.rb', line 23

def self.extended(child)
  child.config = child::Config.new
end

Instance Method Details

#def_add(name, options) ⇒ Object

Generate a DSL method that creates a certain type of domain object, and adds it to a list on the config.

def_add :fieldset, create: Fieldset, append_to: :fields

This will generate a ‘fieldset` method, which will call `Fieldset.create`, and append the result to `config.fields`



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/yaks/configurable.rb', line 76

def def_add(name, options)
  old_verbose, $VERBOSE = $VERBOSE, false # skip method redefinition warning
  define_singleton_method name do |*args, &block|
    defaults = options.fetch(:defaults, {})
    klass    = options.fetch(:create)

    if args.last.instance_of?(Hash)
      args[-1] = defaults.merge(args[-1])
    else
      args << defaults
    end

    self.config = config.append_to(
      options.fetch(:append_to),
      klass.create(*args, &block)
    )
  end
ensure
  $VERBOSE = old_verbose
end

#def_forward(mappings, *names) ⇒ Object

Forward a method to the config object. This assumes the method will return an updated config instance.

Either takes a list of methods to forward, or a mapping (hash) of source to destination method name.



57
58
59
60
61
62
63
64
65
66
67
# File 'lib/yaks/configurable.rb', line 57

def def_forward(mappings, *names)
  if mappings.instance_of? Hash
    mappings.each do |method_name, target|
      define_singleton_method method_name do |*args, &block|
        self.config = config.public_send(target, *args, &block)
      end
    end
  else
    def_forward([mappings, *names].map{|name| {name => name}}.inject(:merge))
  end
end

#def_set(*method_names) ⇒ Object

Create a DSL method to set a certain config property. The generated method will take either a plain value, or a block, which will be captured and stored instead.



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/yaks/configurable.rb', line 34

def def_set(*method_names)
  method_names.each do |method_name|
    define_singleton_method method_name do |arg = Undefined, &block|
      if arg.equal?(Undefined)
        unless block
          raise ArgumentError, "setting #{method_name}: no value and no block given"
        end
        self.config = config.with(method_name => block)
      else
        if block
          raise ArgumentError, "ambiguous invocation setting #{method_name}: give either a value or a block, not both."
        end
        self.config = config.with(method_name => arg)
      end
    end
  end
end

#inherited(child) ⇒ Object



27
28
29
# File 'lib/yaks/configurable.rb', line 27

def inherited(child)
  child.config = config
end