Class: Builderator::Config::Attributes

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Includes:
Enumerable
Defined in:
lib/builderator/config/attributes.rb

Overview

Shared Attribute Mixin

Direct Known Subclasses

Collection, Namespace, File

Defined Under Namespace

Classes: Collection, Namespace

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(attributes = {}, options = {}, &block) ⇒ Attributes

Returns a new instance of Attributes.



181
182
183
184
185
186
187
188
189
190
# 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

Instance Attribute Details

#attributesObject (readonly)

Returns the value of attribute attributes.



176
177
178
# File 'lib/builderator/config/attributes.rb', line 176

def attributes
  @attributes
end

#extendsObject (readonly)

Returns the value of attribute extends.



179
180
181
# File 'lib/builderator/config/attributes.rb', line 179

def extends
  @extends
end

#nodesObject (readonly)

Returns the value of attribute nodes.



177
178
179
# File 'lib/builderator/config/attributes.rb', line 177

def nodes
  @nodes
end

#parentObject (readonly)

Returns the value of attribute parent.



178
179
180
# File 'lib/builderator/config/attributes.rb', line 178

def parent
  @parent
end

Class Method Details

.attribute(attribute_name, default = nil, **options) ⇒ Object



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# 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

.collection(collection_name, &definition) ⇒ Object

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



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# 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

.namespace(namespace_name, &definition) ⇒ Object

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.



82
83
84
85
86
87
88
89
90
91
# 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

Instance Method Details

#==(other) ⇒ Object



172
173
174
# File 'lib/builderator/config/attributes.rb', line 172

def ==(other)
  attributes == other.attributes
end

#cleanObject

Clear dirty state flag



193
194
195
# File 'lib/builderator/config/attributes.rb', line 193

def clean
  @dirty = false
end

#compile(evaluate = true) ⇒ Object



203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
# 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

#dirty(update = false) ⇒ Object

All dirty state should aggregate at the root node



163
164
165
166
# File 'lib/builderator/config/attributes.rb', line 163

def dirty(update = false)
  return @dirty ||= update if root?
  root.dirty(update)
end

#dirty!(set) ⇒ Object



168
169
170
# File 'lib/builderator/config/attributes.rb', line 168

def dirty!(set)
  @dirty = set
end

#merge(other) ⇒ Object



226
227
228
229
# File 'lib/builderator/config/attributes.rb', line 226

def merge(other)
  dirty(attributes.merge!(other.attributes).any?)
  self
end

#reset!Object



197
198
199
200
201
# File 'lib/builderator/config/attributes.rb', line 197

def reset!
  @attributes = Config::Rash.new
  @nodes = {}
  @dirty = false
end

#rootObject

Get the root Attributes object



152
153
154
155
156
# File 'lib/builderator/config/attributes.rb', line 152

def root
  return self if root?

  parent.root
end

#root?Boolean

Returns:

  • (Boolean)


158
159
160
# File 'lib/builderator/config/attributes.rb', line 158

def root?
  parent == self
end

#sealObject



141
142
143
144
# File 'lib/builderator/config/attributes.rb', line 141

def seal
  attributes.seal
  self
end

#to_json(*_) ⇒ Object



231
232
233
# File 'lib/builderator/config/attributes.rb', line 231

def to_json(*_)
  JSON.pretty_generate(to_hash)
end

#unsealObject



146
147
148
149
# File 'lib/builderator/config/attributes.rb', line 146

def unseal
  attributes.unseal
  self
end