module Dry::Initializer # Rebuilds the initializer every time a new argument defined # # @api private # class Builder include Plugins def initialize @signature = Signature.new @plugins = Set.new [VariableSetter, TypeConstraint, DefaultProc] @parts = [] end # Register new plugin to be applied as a chunk of code, or a proc # to be evaluated in the instance's scope # # @param [Dry::Initializer::Plugin] # # @return [Dry::Initializer::Builder] # def register(plugin) plugins = @plugins + [plugin] copy { @plugins = plugins } end # Makes builder to provide options-tolerant initializer # # @return [Dry::Initializer::Builder] # def tolerant_to_unknown_options copy { @tolerant = "**" } end # Makes builder to provide options-intolerant initializer # # @return [Dry::Initializer::Builder] # def intolerant_to_unknown_options copy { @tolerant = nil } end # Defines new agrument and reloads mixin definitions # # @param [#to_sym] name # @param [Hash<Symbol, Object>] settings # # @return [Dry::Initializer::Builder] # def define(name, settings) signature = @signature.add(name, settings) parts = @parts + @plugins.map { |p| p.call(name, settings) }.compact copy do @signature = signature @parts = parts end end # Redeclares initializer and readers in the mixin module # # @param [Module] mixin # def call(mixin) define_readers(mixin) reload_initializer(mixin) reload_callback(mixin) mixin end private def copy(&block) dup.tap { |instance| instance.instance_eval(&block) } end def define_readers(mixin) readers = @signature.select { |item| item.settings[:reader] != false } .map(&:name) mixin.send :attr_reader, *readers if readers.any? end def reload_initializer(mixin) strings = @parts.select { |part| String === part } signature = [@signature.call, @tolerant].map(&:to_s) .reject(&:empty?) .join(", ") mixin.class_eval <<-RUBY def initialize(#{signature}) #{strings.join("\n")} __after_initialize__ end RUBY end def reload_callback(mixin) blocks = @parts.select { |part| Proc === part } mixin.send :define_method, :__after_initialize__ do blocks.each { |block| instance_eval(&block) } end mixin.send :private, :__after_initialize__ end end end