Class: Puppet::Pops::Binder::Binder::LayerProcessor Private

Inherits:
Object
  • Object
show all
Defined in:
lib/puppet/pops/binder/binder.rb

Overview

This class is part of a private API. You should avoid using this class if possible, as it may be removed or be changed in the future.

Processes the information in a layer, aggregating it to the injector_entries hash in its parent binder. A LayerProcessor holds the intermediate state required while processing one layer.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(binder, key_factory) ⇒ LayerProcessor

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns a new instance of LayerProcessor.



178
179
180
181
182
183
184
185
# File 'lib/puppet/pops/binder/binder.rb', line 178

def initialize(binder, key_factory)
  @binder = binder
  @binder_precedence = binder.binder_precedence
  @key_factory = key_factory
  @bindings = []
  @contributions = []
  @@bind_visitor ||= Puppet::Pops::Visitor.new(nil,"bind",0,0)
end

Instance Attribute Details

#binderObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



173
174
175
# File 'lib/puppet/pops/binder/binder.rb', line 173

def binder
  @binder
end

#binder_precedenceObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



176
177
178
# File 'lib/puppet/pops/binder/binder.rb', line 176

def binder_precedence
  @binder_precedence
end

#bindingsObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



172
173
174
# File 'lib/puppet/pops/binder/binder.rb', line 172

def bindings
  @bindings
end

#contributionsObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



175
176
177
# File 'lib/puppet/pops/binder/binder.rb', line 175

def contributions
  @contributions
end

#key_factoryObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



174
175
176
# File 'lib/puppet/pops/binder/binder.rb', line 174

def key_factory
  @key_factory
end

Instance Method Details

#add(b) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Add the binding to the list of potentially effective bindings from this layer



190
191
192
# File 'lib/puppet/pops/binder/binder.rb', line 190

def add(b)
  bindings << Puppet::Pops::Binder::InjectorEntry.new(b, binder_precedence)
end

#add_contribution(b) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Add a multibind contribution



197
198
199
# File 'lib/puppet/pops/binder/binder.rb', line 197

def add_contribution(b)
  contributions << Puppet::Pops::Binder::InjectorEntry.new(b, binder_precedence)
end

#bind(binding) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Bind given abstract binding



204
205
206
# File 'lib/puppet/pops/binder/binder.rb', line 204

def bind(binding)
  @@bind_visitor.visit_this_0(self, binding)
end

#bind_Binding(o) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



290
291
292
293
294
295
296
# File 'lib/puppet/pops/binder/binder.rb', line 290

def bind_Binding(o)
  if is_contribution?(o)
    add_contribution(o)
  else
    add(o)
  end
end

#bind_Bindings(o) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



299
300
301
# File 'lib/puppet/pops/binder/binder.rb', line 299

def bind_Bindings(o)
  o.bindings.each {|b| bind(b) }
end

#bind_LayeredBindings(o) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Process layered bindings from highest to lowest layer



312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
# File 'lib/puppet/pops/binder/binder.rb', line 312

def bind_LayeredBindings(o)
  o.layers.each do |layer|
    processor = LayerProcessor.new(binder, key_factory)
    # All except abstract (==error) are transferred to injector_entries

    processor.bind(layer).each do |k, v|
      entry = binder.injector_entries[k]
      unless key_factory.is_contributions_key?(k)
        if v.is_abstract?()
          layer_name, bindings_name = Puppet::Pops::Binder::Binder.get_named_binding_layer_and_name(v.binding)
          type_name = Puppet::Pops::Types::TypeCalculator.singleton.string(v.binding.type)
          raise ArgumentError, "The abstract binding '#{type_name}/#{v.binding.name}' in '#{bindings_name}' in layer '#{layer_name}' was not overridden"
        end
        raise ArgumentError, "Internal Error - redefinition of key: #{k}, (should never happen)" if entry
        binder.injector_entries[k] = v
      else
        # add contributions to existing contributions, else set them
        binder.injector_entries[k] = entry ? entry + v : v
      end
    end
  end
end

#bind_NamedBindings(o) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



304
305
306
307
# File 'lib/puppet/pops/binder/binder.rb', line 304

def bind_NamedBindings(o)
  # Name is ignored here, it should be introspected when needed (in case of errors)
  o.bindings.each {|b| bind(b) }
end

#bind_NamedLayer(o) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes one named (“top level”) layer consisting of a list of NamedBindings



338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
# File 'lib/puppet/pops/binder/binder.rb', line 338

def bind_NamedLayer(o)
  o.bindings.each {|b| bind(b) }
  this_layer = {}

  # process regular bindings
  bindings.each do |b|
    bkey = key(b.binding)

    # ignore if a higher layer defined it (unless the lower is final), but ensure override gets resolved
    # (override is not resolved across binders)
    if x = binder.injector_entries[bkey]
      if b.is_final?
        raise_conflicting_binding(x, b)
      end
      x.mark_override_resolved()
      next
    end

    # If a lower (parent) binder exposes a final binding it may not be overridden
    #
    if (x = binder.lookup_in_parent(bkey)) && x.is_final?
      raise_conflicting_binding(x, b)
    end

    # if already found in this layer, one wins (and resolves override), or it is an error
    existing = this_layer[bkey]
    winner = existing ? highest(existing, b) : b
    this_layer[bkey] = winner
    if existing
      winner.mark_override_resolved()
    end
  end

  # Process contributions
  # - organize map multibind_id to bindings with this id
  # - for each id, create an array with the unique anonymous keys to the contributed bindings
  # - bind the index to a special multibind contributions key (these are aggregated)
  #
  c_hash = Hash.new {|hash, key| hash[ key ] = [] }
  contributions.each {|b| c_hash[ b.binding.multibind_id ] << b }
  # - for each id
  c_hash.each do |k, v|
    index = v.collect do |b|
      bkey = key(b.binding)
      this_layer[bkey] = b
      bkey
    end.flatten
    contributions_key = key_factory.multibind_contributions(k)
    unless this_layer[contributions_key]
      this_layer[contributions_key] = []
    end
    this_layer[contributions_key] += index
  end
  this_layer
end

#highest(b1, b2) ⇒ Puppet::Pops::Binder::InjectorEntry

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns the entry with the highest precedence.

Returns:



210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
# File 'lib/puppet/pops/binder/binder.rb', line 210

def highest(b1, b2)
  if b1.is_abstract? != b2.is_abstract?
    # if one is abstract and the other is not, the non abstract wins
    b1.is_abstract? ? b2 : b1
  else
    case b1.precedence <=> b2.precedence
    when 1
      b1
    when -1
      b2
    when 0
      raise_conflicting_binding(b1, b2)
    end
  end
end

#is_contribution?(binding) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


285
286
287
# File 'lib/puppet/pops/binder/binder.rb', line 285

def is_contribution?(binding)
  ! binding.multibind_id.nil?
end

#key(binding) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Produces the key for the given Binding.

Parameters:

Returns:



275
276
277
278
279
280
281
282
# File 'lib/puppet/pops/binder/binder.rb', line 275

def key(binding)
  k = if is_contribution?(binding)
    # contributions get a unique (sequential) key
    binder.next_anonymous_key()
  else
    key_factory.binding_key(binding)
  end
end

#raise_conflicting_binding(b1, b2) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Raises a conflicting bindings error given two InjectorEntry’s with same precedence in the same layer (if they are in different layers, something is seriously wrong)

Raises:

  • (ArgumentError)


228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
# File 'lib/puppet/pops/binder/binder.rb', line 228

def raise_conflicting_binding(b1, b2)
  b1_layer_name, b1_bindings_name = binder.class.get_named_binding_layer_and_name(b1.binding)
  b2_layer_name, b2_bindings_name = binder.class.get_named_binding_layer_and_name(b2.binding)

  finality_msg = (b1.is_final? || b2.is_final?) ? ". Override of final binding not allowed" : ''

  # TODO: Use of layer_name is not very good, it is not guaranteed to be unique
  unless b1_layer_name == b2_layer_name
    raise ArgumentError, [
      'Conflicting binding for',
      "'#{b1.binding.name}'",
      'being resolved across layers',
      "'#{b1_layer_name}' and",
      "'#{b2_layer_name}'"
      ].join(' ')+finality_msg
  end

  # Conflicting bindings made from the same source
  if b1_bindings_name == b2_bindings_name
    raise ArgumentError, [
      'Conflicting binding for name:',
      "'#{b1.binding.name}'",
      'in layer:',
      "'#{b1_layer_name}', ",
      'both from:',
      "'#{b1_bindings_name}'"
      ].join(' ')+finality_msg
  end

  # Conflicting bindings from different sources
  raise ArgumentError, [
    'Conflicting binding for name:',
    "'#{b1.binding.name}'",
    'in layer:',
    "'#{b1_layer_name}',",
    'from:',
    "'#{b1_bindings_name}', and",
    "'#{b2_bindings_name}'"
    ].join(' ')+finality_msg
end