From 74012d46953eded69bc1de7e0ae88666131c832a Mon Sep 17 00:00:00 2001 From: Sean Millichamp Date: Sun, 20 Apr 2025 18:49:28 -0500 Subject: [PATCH] Fix given_or_derived behavior in Pops Object initialization The PCore specification states "The given_or_derived attribute kind states that if the attribute is given when the object is instantiated this value is used, otherwise it is a computed value." and "It is allowed to give the value when instantiating in which case the given value overrides what would be the computed value." The current implementation treats an attribute with the kind given_or_derived as a required parameter during initialization (in the init_hash used in from_asserted_hash or in the generated `initialize` method). This change enforces an implicit `value` parameter of `nil` which causes the generated init_hash type check and the generated initialize method to treat given_or_derived attributes as optional. --- lib/puppet/pops/types/p_object_type.rb | 4 +++- spec/unit/pops/types/p_object_type_spec.rb | 14 +++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/puppet/pops/types/p_object_type.rb b/lib/puppet/pops/types/p_object_type.rb index 2290245344..6fcb567593 100644 --- a/lib/puppet/pops/types/p_object_type.rb +++ b/lib/puppet/pops/types/p_object_type.rb @@ -316,6 +316,8 @@ def initialize(name, container, init_hash) v = init_hash[KEY_VALUE] @value = v == :default ? v : TypeAsserter.assert_instance_of(nil, type, v) { "#{label} #{KEY_VALUE}" } + elsif @kind == ATTRIBUTE_KIND_GIVEN_OR_DERIVED + @value = nil else raise Puppet::ParseError, _("%{label} of kind 'constant' requires a value") % { label: label } if @kind == ATTRIBUTE_KIND_CONSTANT @@ -341,7 +343,7 @@ def _pcore_init_hash hash[KEY_KIND] = @kind hash.delete(KEY_FINAL) if @kind == ATTRIBUTE_KIND_CONSTANT # final is implied end - hash[KEY_VALUE] = @value unless @value == :undef + hash[KEY_VALUE] = @value unless @value == :undef || @kind == ATTRIBUTE_KIND_GIVEN_OR_DERIVED hash end diff --git a/spec/unit/pops/types/p_object_type_spec.rb b/spec/unit/pops/types/p_object_type_spec.rb index 8d8f8254a9..d119d0de08 100644 --- a/spec/unit/pops/types/p_object_type_spec.rb +++ b/spec/unit/pops/types/p_object_type_spec.rb @@ -697,18 +697,16 @@ def parse_object(name, body_string) end context 'when producing an init_hash_type' do - it 'produces a struct of all attributes that are not derived or constant' do + it 'produces a struct of all attributes excepting derived or constant' do t = parse_object('MyObject', <<-OBJECT) attributes => { a => { type => Integer }, - b => { type => Integer, kind => given_or_derived }, - c => { type => Integer, kind => derived }, - d => { type => Integer, kind => constant, value => 4 } + b => { type => Integer, kind => derived }, + c => { type => Integer, kind => constant, value => 4 } } OBJECT expect(t.init_hash_type).to eql(factory.struct({ 'a' => factory.integer, - 'b' => factory.integer })) end @@ -716,12 +714,14 @@ def parse_object(name, body_string) t = parse_object('MyObject', <<-OBJECT) attributes => { a => { type => Integer }, - b => { type => Integer, value => 4 } + b => { type => Integer, value => 4 }, + c => { type => Integer, kind => given_or_derived }, } OBJECT expect(t.init_hash_type).to eql(factory.struct({ 'a' => factory.integer, - factory.optional('b') => factory.integer + factory.optional('b') => factory.integer, + factory.optional('c') => factory.integer, })) end