class KeywordBuilder

Abstract builder interface for types with keyword argument constructors.

Constants

VERSION

Attributes

attrs[R]

Public Class Methods

build!(**initial_attrs, &block) click to toggle source
# File lib/keyword_builder.rb, line 26
def build!(**initial_attrs, &block)
  builder = self.new(initial_attrs)
  builder.instance_eval(&block) if block_given?
  clazz.public_send(constructor, **builder.attrs)
end
create(clazz, constructor: :new) click to toggle source
# File lib/keyword_builder.rb, line 8
def create(clazz, constructor: :new)
  keywords, wildcard = parse_constructor_parameters(clazz, constructor)

  Class.new(self) do
    define_singleton_method(:clazz) { clazz }
    define_singleton_method(:constructor) { constructor }
    define_singleton_method(:keywords) { keywords }
    define_singleton_method(:wildcard?) { wildcard }
    keywords.each do |keyword|
      define_method(keyword) { |*args, &block| _set_attribute(keyword, *args, &block) }
    end
  end
end
new(initial_attrs) click to toggle source
# File lib/keyword_builder.rb, line 64
def initialize(initial_attrs)
  @attrs = initial_attrs.dup
end
valid_keyword?(param) click to toggle source
# File lib/keyword_builder.rb, line 22
def valid_keyword?(param)
  wildcard? || keywords.include?(param)
end

Private Class Methods

parse_constructor_parameters(clazz, constructor) click to toggle source
# File lib/keyword_builder.rb, line 34
def parse_constructor_parameters(clazz, constructor)
  parameters =
    if constructor == :new
      clazz.instance_method(:initialize).parameters
    else
      clazz.method(constructor).parameters
    end

  keywords = Set.new
  wildcard = false

  parameters.each do |type, name|
    case type
    when :opt, :rest
      next
    when :key, :keyreq
      keywords << name
    when :keyrest
      wildcard = true
    else
      raise ArgumentError.new("Invalid builder method, contains required non-keyword parameter #{name}")
    end
  end

  [keywords.freeze, wildcard]
end

Public Instance Methods

_set_attribute(attr, *args, &block) click to toggle source
# File lib/keyword_builder.rb, line 68
def _set_attribute(attr, *args, &block)
  if attrs.has_key?(attr)
    raise BuilderError.new("Invalid builder state: #{attr} already provided")
  end

  value =
    if block_given?
      raise ArgumentError.new('Cannot provide both immediate and block value') unless args.blank?

      block
    elsif args.size == 1
      args[0]
    else
      raise ArgumentError.new('Wrong number of arguments: expected 1 or block')
    end

  attrs[attr] = value
end
method_missing(attr, *args, &block) click to toggle source
Calls superclass method
# File lib/keyword_builder.rb, line 87
def method_missing(attr, *args, &block)
  if self.class.wildcard?
    _set_attribute(attr, *args, &block)
  else
    super
  end
end
respond_to_missing?(attr, _include_all = false) click to toggle source
Calls superclass method
# File lib/keyword_builder.rb, line 95
def respond_to_missing?(attr, _include_all = false)
  self.class.wildcard? || super
end