module BBLib::SimpleInit

Allows any public setter method to be called during initialization using keyword arguments. Add include BBLib::SimpleInit or prepend BBLib::SimpleInit to classes to add this behavior.

Constants

INIT_TYPES

strict - Raise exceptions for unknown named attributes loose - Ignore unknown named attributes collect - Put unknown named attributes into the attribute

configured by collect_attribute

Attributes

_init_type[R]

Public Class Methods

included(base) click to toggle source
# File lib/bblib/core/mixins/simple_init.rb, line 15
def self.included(base)
  base.extend ClassMethods
  base.class_eval do
    define_method(:initialize) do |*args, &block|
      send(:simple_setup) if respond_to?(:simple_setup, true)
      send(:simple_preinit, *args, &block) if respond_to?(:simple_preinit, true)
      _initialize(*args, &block)
      send(:simple_init, *args, &block) if respond_to?(:simple_init, true)
      if block && !_attrs.any? { |k, v| v[:options][:arg_at] == :block }
        result = instance_eval(&block)
        simple_init_block_result(result) if respond_to?(:simple_init_block_result, true)
      end
      send(:simple_postinit, *args, &block) if respond_to?(:simple_postinit, true)
      self
    end
  end

  if BBLib.in_opal?
    base.singleton_class.class_eval do
      alias __new new

      def new(*args, &block)
        named = BBLib.named_args(*args)
        if init_foundation && named[init_foundation_method] && ((named[init_foundation_method] != self.send(init_foundation_method)) rescue false)
          klass = [self, descendants].flatten.find do |k|
            if init_foundation_compare
              init_foundation_compare.call(k.send(init_foundation_method), named[init_foundation_method])
            else
              k.send(init_foundation_method).to_s == named[init_foundation_method].to_s
            end
          end
          raise ArgumentError, "Unknown #{init_foundation_method} \"#{named[init_foundation_method]}\" for #{self}" unless klass
          klass == self ? __new(*args, &block) : klass.new(*args, &block)
        elsif named[init_foundation_method].nil? && init_foundation_default_class != self
          init_foundation_default_class.new(*args, &block)
        else
          __new(*args, &block)
        end
      end
    end
  end
end

Public Instance Methods

new(*args, &block) click to toggle source
# File lib/bblib/core/mixins/simple_init.rb, line 36
def new(*args, &block)
  named = BBLib.named_args(*args)
  if init_foundation && named[init_foundation_method] && ((named[init_foundation_method] != self.send(init_foundation_method)) rescue false)
    klass = [self, descendants].flatten.find do |k|
      if init_foundation_compare
        init_foundation_compare.call(k.send(init_foundation_method), named[init_foundation_method])
      else
        k.send(init_foundation_method).to_s == named[init_foundation_method].to_s
      end
    end
    raise ArgumentError, "Unknown #{init_foundation_method} \"#{named[init_foundation_method]}\" for #{self}" unless klass
    klass == self ? __new(*args, &block) : klass.new(*args, &block)
  elsif named[init_foundation_method].nil? && init_foundation_default_class != self
    init_foundation_default_class.new(*args, &block)
  else
    __new(*args, &block)
  end
end

Protected Instance Methods

_collect_attribute(method, value) click to toggle source
# File lib/bblib/core/mixins/simple_init.rb, line 221
def _collect_attribute(method, value)
  inst_name = "@#{self.class.collect_method}"
  hash = instance_variable_get(inst_name)
  hash = instance_variable_set(inst_name, {}) unless hash.is_a?(Hash)
  hash[method] = value
end
_initialize(*args, &block) click to toggle source
# File lib/bblib/core/mixins/simple_init.rb, line 181
def _initialize(*args, &block)
  named = BBLib.named_args(*args)
  if self.class.respond_to?(:_attrs)
    set_v_arg = self.class._attrs.map do |method, details|
      next unless details[:options][:arg_at] && (details[:options][:arg_at].is_a?(Integer) || details[:options][:arg_at] == :block)
      if details[:options][:arg_at] == :block
        send("#{method}=", block) if block
        method
      else
      index = details[:options][:arg_at]
        if args.size > index
          accept = details[:options][:arg_at_accept]
          next if args[index].is_a?(Hash) && (accept.nil? || ![accept].flatten.include?(Hash))
          if accept.nil? || [accept].flatten.any? { |a| a >= args[index].class }
            send("#{method}=", args[index])
            method
          end
        end
      end
    end.compact
    missing = self.class._attrs.map do |method, details|
      next unless !set_v_arg.include?(method) && details[:options][:required] && !named.include?(method) && !send(method)
      method
    end.compact
    raise ArgumentError, "You are missing the following required #{BBLib.pluralize('argument', missing.size)} for #{self.class}: #{missing.join_terms}" unless missing.empty?
  end
  named.each do |method, value|
    next if method == self.class.init_foundation_method
    setter = "#{method}="
    exists = respond_to?(setter)
    if !exists && self.class.init_type == :strict
      raise ArgumentError, "Undefined attribute #{setter} for class #{self.class}."
    elsif !exists && self.class.init_type == :collect
      _collect_attribute(method, value)
    end
    next unless exists
    send(setter, value)
  end
end