class Builderator::Config::Attributes
Shared Attribute Mixin
Attributes
Public Class Methods
# File lib/builderator/config/attributes.rb, line 17 def attribute(attribute_name, default = nil, **options) ## # Helpers for Array-type attributes ## if options[:type] == :list define_method(attribute_name) do |*arg, **run_options| ## Instantiate List if it doesn't exist yet. `||=` will always return a new Rash. @attributes[attribute_name] = Config::List.new(run_options) unless @attributes.has?(attribute_name, Config::List) unless arg.empty? @attributes[attribute_name].set(*arg.flatten) @attributes[attribute_name].set(*arg) if options[:flatten] == false end @attributes[attribute_name] end define_method(options[:singular]) do |*arg, **run_options| send(attribute_name, run_options).push(*arg.flatten) end if options.include?(:singular) return end ## # Helpers for Hash-type attributes ## if options[:type] == :hash define_method(attribute_name) do |arg = nil| ## Instantiate List if it doesn't exist yet. `||=` will always return a new Rash. @attributes[attribute_name] = Config::Rash.new unless @attributes.has?(attribute_name, Config::Rash) dirty(@attributes[attribute_name].merge!(Config::Rash.coerce(arg)).any?) unless arg.nil? @attributes[attribute_name] end return end ## Getter/Setter define_method(attribute_name) do |*arg| set_or_return(attribute_name, arg.first, default, options) end ## Setter define_method("#{attribute_name}=") do |arg| set_if_valid(attribute_name, arg, options) end end
A Collection
is a named-set of items in a sub-node of the attribute-set.
Like Namespaces, Collections map to a top-level key, but they also have multiple second-order keys:
e.g. `collection :vagrant …` adds a DSL method `vagrant(name = :default, &block)` which maps to `attributes[<name>]`
Multiple entities can be added to the collection by calling the DSL method with unique `name` arguments. Multiple calls to the DSL method with the same name argument will update the existing entity in place
An entry can be defined as an extension of another node by passing a hash as the instance name: `name => Config.node(:name)`. This will use the values defined in `Config.node(:name)` as defaults for the new entry
# File lib/builderator/config/attributes.rb, line 111 def collection(collection_name, &definition) collection_class = Collection.create(collection_name, &definition) define_method(collection_name) do |instance_name = nil, &block| extension_base = nil ## Allow extension to be defined as a key-value if instance_name.is_a?(Hash) extension_base = instance_name.first.last instance_name = instance_name.first.first end nodes[collection_name] ||= collection_class.new( @attributes[collection_name], :parent => self) return nodes[collection_name] if instance_name.nil? nodes[collection_name].fetch(instance_name, :extends => extension_base, &block) end end
A Namespace
is a singleton sub-node of the attribute-set
e.g. `namespace :chef …` maps to `attributes` and adds a method `chef(&block)` to the DSL which is used as follows:
“` chef do
run_list 'foo', 'bar' ...
end “`
Multiple calls to the DSL method are safe and will update the same sub-node.
# File lib/builderator/config/attributes.rb, line 82 def namespace(namespace_name, &definition) namespace_class = Namespace.create(namespace_name, &definition) define_method(namespace_name) do |&block| nodes[namespace_name] ||= namespace_class.new( @attributes[namespace_name], :name => namespace_name, :parent => self, &block) end end
# File lib/builderator/config/attributes.rb, line 181 def initialize(attributes = {}, options = {}, &block) @attributes = Rash.coerce(attributes) @nodes = {} @block = block ## Track change status for consumers @parent = options.fetch(:parent, self) @extends = options[:extends] @dirty = false end
Public Instance Methods
# File lib/builderator/config/attributes.rb, line 172 def ==(other) attributes == other.attributes end
Clear dirty state flag
# File lib/builderator/config/attributes.rb, line 193 def clean @dirty = false end
# File lib/builderator/config/attributes.rb, line 203 def compile(evaluate = true) ## Underlay base values if present if extends.is_a?(Attributes) previous_state = attributes dirty_state = dirty attributes.merge!(extends.attributes) @block.call(self) if @block && evaluate nodes.each { |_, node| node.compile } root.dirty!(dirty_state || previous_state.diff(attributes).any?) return self end ## Compile this node and its children @block.call(self) if @block && evaluate nodes.each { |_, node| node.compile } self end
All dirty state should aggregate at the root node
# File lib/builderator/config/attributes.rb, line 163 def dirty(update = false) return @dirty ||= update if root? root.dirty(update) end
# File lib/builderator/config/attributes.rb, line 168 def dirty!(set) @dirty = set end
# File lib/builderator/config/attributes.rb, line 226 def merge(other) dirty(attributes.merge!(other.attributes).any?) self end
# File lib/builderator/config/attributes.rb, line 197 def reset! @attributes = Config::Rash.new @nodes = {} @dirty = false end
Get the root Attributes
object
# File lib/builderator/config/attributes.rb, line 152 def root return self if root? parent.root end
# File lib/builderator/config/attributes.rb, line 158 def root? parent == self end
# File lib/builderator/config/attributes.rb, line 141 def seal attributes.seal self end
# File lib/builderator/config/attributes.rb, line 231 def to_json(*_) JSON.pretty_generate(to_hash) end
# File lib/builderator/config/attributes.rb, line 146 def unseal attributes.unseal self end
Protected Instance Methods
# File lib/builderator/config/attributes.rb, line 237 def set_if_valid(key, arg, options = {}) ## TODO: define validation interface ## Mutation helpers # Input is a path relative to the working directory arg = Util.relative_path(arg).to_s if options[:relative] # Input is a path relative to the workspace arg = Util.workspace(arg).to_s if options[:workspace] ## Unchanged return if @attributes[key] == arg dirty(true) ## A mutation has occured @attributes[key] = arg end
# File lib/builderator/config/attributes.rb, line 255 def set_or_return(key, arg = nil, default = nil, **options) if arg.nil? return @attributes[key] if @attributes.has?(key) ## Default return if default.is_a?(NilClass) ## No default ## Allow a default to be a static value, or instantiated ## at call-time from a class (e.g. Array or Hash) default_value = default.is_a?(Class) ? default.new : default return default_value if @attributes.sealed return set_if_valid(key, default_value, options) end ## Set value set_if_valid(key, arg, options) end