Skip to content

Commit 3099283

Browse files
authored
Merge pull request #140 from enterprisemodules/parameter_and_properties
Move parameter and property logic to separate classes
2 parents 9dd87a9 + 83f99db commit 3099283

File tree

9 files changed

+689
-150
lines changed

9 files changed

+689
-150
lines changed

lib/puppet/resource_api.rb

Lines changed: 41 additions & 150 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
require 'pathname'
22
require 'puppet/resource_api/data_type_handling'
33
require 'puppet/resource_api/glue'
4+
require 'puppet/resource_api/parameter'
5+
require 'puppet/resource_api/property'
46
require 'puppet/resource_api/puppet_context' unless RUBY_PLATFORM == 'java'
7+
require 'puppet/resource_api/read_only_parameter'
58
require 'puppet/resource_api/type_definition'
9+
require 'puppet/resource_api/value_creator'
610
require 'puppet/resource_api/version'
711
require 'puppet/type'
812
require 'puppet/util/network_device'
@@ -174,13 +178,23 @@ def to_resource
174178
# TODO: using newparam everywhere would suppress change reporting
175179
# that would allow more fine-grained reporting through context,
176180
# but require more invest in hooking up the infrastructure to emulate existing data
177-
param_or_property = if [:read_only, :parameter, :namevar].include? options[:behaviour]
178-
:newparam
179-
else
180-
:newproperty
181-
end
181+
if [:parameter, :namevar].include? options[:behaviour]
182+
param_or_property = :newparam
183+
parent = Puppet::ResourceApi::Parameter
184+
elsif options[:behaviour] == :read_only
185+
param_or_property = :newparam
186+
parent = Puppet::ResourceApi::ReadOnlyParameter
187+
else
188+
param_or_property = :newproperty
189+
parent = Puppet::ResourceApi::Property
190+
end
182191

183-
send(param_or_property, name.to_sym) do
192+
# This call creates a new parameter or property with all work-arounds or
193+
# customizations required by the Resource API applied. Under the hood,
194+
# this maps to the relevant DSL methods in Puppet::Type. See
195+
# https://puppet.com/docs/puppet/6.0/custom_types.html#reference-5883
196+
# for details.
197+
send(param_or_property, name.to_sym, parent: parent) do
184198
unless options[:type]
185199
raise Puppet::DevError, "#{definition[:name]}.#{name} has no type"
186200
end
@@ -191,144 +205,32 @@ def to_resource
191205
warn("#{definition[:name]}.#{name} has no docs")
192206
end
193207

194-
if options[:behaviour] == :namevar
195-
isnamevar
196-
end
197-
198-
# read-only values do not need type checking, but can have default values
199-
if options[:behaviour] != :read_only && options.key?(:default)
200-
if options.key? :default
201-
if options[:default] == false
202-
# work around https://tickets.puppetlabs.com/browse/PUP-2368
203-
defaultto :false # rubocop:disable Lint/BooleanSymbol
204-
elsif options[:default] == true
205-
# work around https://tickets.puppetlabs.com/browse/PUP-2368
206-
defaultto :true # rubocop:disable Lint/BooleanSymbol
207-
else
208-
# marshal the default option to decouple that from the actual value.
209-
# we cache the dumped value in `marshalled`, but use a block to unmarshal
210-
# everytime the value is requested. Objects that can't be marshalled
211-
# See https://stackoverflow.com/a/8206537/4918
212-
marshalled = Marshal.dump(options[:default])
213-
defaultto { Marshal.load(marshalled) } # rubocop:disable Security/MarshalLoad
214-
end
215-
end
208+
# The initialize method is called when puppet core starts building up
209+
# type objects. The core passes in a hash of shape { resource:
210+
# #<Puppet::Type::TypeName> }. We use this to pass through the
211+
# required configuration data to the parent (see
212+
# Puppet::ResourceApi::Property, Puppet::ResourceApi::Parameter and
213+
# Puppet::ResourceApi::ReadOnlyParameter).
214+
define_method(:initialize) do |resource_hash|
215+
super(definition[:name], self.class.data_type, name, resource_hash)
216216
end
217217

218-
if name == :ensure
219-
def insync?(is)
220-
rs_value.to_s == is.to_s
221-
end
218+
# get pops data type object for this parameter or property
219+
define_singleton_method(:data_type) do
220+
@rsapi_data_type ||= Puppet::ResourceApi::DataTypeHandling.parse_puppet_type(
221+
name,
222+
options[:type],
223+
)
222224
end
223225

224-
type = Puppet::ResourceApi::DataTypeHandling.parse_puppet_type(
225-
name,
226-
options[:type],
226+
# from ValueCreator call create_values which makes alias values and
227+
# default values for properties and params
228+
Puppet::ResourceApi::ValueCreator.create_values(
229+
self,
230+
data_type,
231+
param_or_property,
232+
options,
227233
)
228-
229-
if param_or_property == :newproperty
230-
define_method(:should) do
231-
if name == :ensure && rs_value.is_a?(String)
232-
rs_value.to_sym
233-
elsif rs_value == false
234-
# work around https://tickets.puppetlabs.com/browse/PUP-2368
235-
:false # rubocop:disable Lint/BooleanSymbol
236-
elsif rs_value == true
237-
# work around https://tickets.puppetlabs.com/browse/PUP-2368
238-
:true # rubocop:disable Lint/BooleanSymbol
239-
else
240-
rs_value
241-
end
242-
end
243-
244-
define_method(:should=) do |value|
245-
@shouldorig = value
246-
247-
if name == :ensure
248-
value = value.to_s
249-
end
250-
251-
# Puppet requires the @should value to always be stored as an array. We do not use this
252-
# for anything else
253-
# @see Puppet::Property.should=(value)
254-
@should = [
255-
Puppet::ResourceApi::DataTypeHandling.mungify(
256-
type,
257-
value,
258-
"#{definition[:name]}.#{name}",
259-
Puppet::ResourceApi.caller_is_resource_app?,
260-
),
261-
]
262-
end
263-
264-
# used internally
265-
# @returns the final mungified value of this property
266-
define_method(:rs_value) do
267-
@should ? @should.first : @should
268-
end
269-
else
270-
define_method(:value) do
271-
@value
272-
end
273-
274-
define_method(:value=) do |value|
275-
if options[:behaviour] == :read_only
276-
raise Puppet::ResourceError, "Attempting to set `#{name}` read_only attribute value to `#{value}`"
277-
end
278-
279-
@value = Puppet::ResourceApi::DataTypeHandling.mungify(
280-
type,
281-
value,
282-
"#{definition[:name]}.#{name}",
283-
Puppet::ResourceApi.caller_is_resource_app?,
284-
)
285-
end
286-
287-
# used internally
288-
# @returns the final mungified value of this parameter
289-
define_method(:rs_value) do
290-
@value
291-
end
292-
end
293-
294-
# puppet symbolizes some values through puppet/parameter/value.rb (see .convert()), but (especially) Enums
295-
# are strings. specifying a munge block here skips the value_collection fallback in puppet/parameter.rb's
296-
# default .unsafe_munge() implementation.
297-
munge { |v| v }
298-
299-
# provide hints to `puppet type generate` for better parsing
300-
if type.instance_of? Puppet::Pops::Types::POptionalType
301-
type = type.type
302-
end
303-
304-
case type
305-
when Puppet::Pops::Types::PStringType
306-
# require any string value
307-
Puppet::ResourceApi.def_newvalues(self, param_or_property, %r{})
308-
when Puppet::Pops::Types::PBooleanType
309-
Puppet::ResourceApi.def_newvalues(self, param_or_property, 'true', 'false')
310-
aliasvalue true, 'true'
311-
aliasvalue false, 'false'
312-
aliasvalue :true, 'true' # rubocop:disable Lint/BooleanSymbol
313-
aliasvalue :false, 'false' # rubocop:disable Lint/BooleanSymbol
314-
315-
when Puppet::Pops::Types::PIntegerType
316-
Puppet::ResourceApi.def_newvalues(self, param_or_property, %r{^-?\d+$})
317-
when Puppet::Pops::Types::PFloatType, Puppet::Pops::Types::PNumericType
318-
Puppet::ResourceApi.def_newvalues(self, param_or_property, Puppet::Pops::Patterns::NUMERIC)
319-
end
320-
321-
if param_or_property == :newproperty
322-
# stop puppet from trying to call into the provider when
323-
# no pre-defined values have been specified
324-
# "This is not the provider you are looking for." -- Obi-Wan Kaniesobi.
325-
def call_provider(value); end
326-
end
327-
328-
case options[:type]
329-
when 'Enum[present, absent]'
330-
Puppet::ResourceApi.def_newvalues(self, param_or_property, 'absent', 'present')
331-
end
332234
end
333235
end
334236

@@ -582,17 +484,6 @@ def self.class_name_from_type_name(type_name)
582484
type_name.to_s.split('_').map(&:capitalize).join
583485
end
584486

585-
# Add the value to `this` property or param, depending on whether param_or_property is `:newparam`, or `:newproperty`
586-
def self.def_newvalues(this, param_or_property, *values)
587-
if param_or_property == :newparam
588-
this.newvalues(*values)
589-
else
590-
values.each do |v|
591-
this.newvalue(v) {}
592-
end
593-
end
594-
end
595-
596487
def self.caller_is_resource_app?
597488
caller.any? { |c| c.match(%r{application/resource.rb:}) }
598489
end

lib/puppet/resource_api/parameter.rb

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
require 'puppet/util'
2+
require 'puppet/parameter'
3+
4+
module Puppet; module ResourceApi; end; end # predeclare the main module # rubocop:disable Style/Documentation,Style/ClassAndModuleChildren
5+
6+
# Class containing parameter functionality for ResourceApi.
7+
class Puppet::ResourceApi::Parameter < Puppet::Parameter
8+
attr_reader :value
9+
10+
# This initialize takes arguments and sets up new parameter.
11+
# @param type_name the name of the Puppet Type
12+
# @param data_type the data type of parameter instance
13+
# @param attribute_name the name of attribue of the parameter
14+
# @param resource_hash the resource hash instance which is passed to the
15+
# parent class.
16+
def initialize(type_name, data_type, attribute_name, resource_hash)
17+
@type_name = type_name
18+
@data_type = data_type
19+
@attribute_name = attribute_name
20+
super(resource_hash) # Pass resource to parent Puppet class.
21+
end
22+
23+
# This method assigns value to the parameter and cleans value.
24+
# @param value the value to be set and clean
25+
# @return [type] the cleaned value
26+
def value=(value)
27+
@value = Puppet::ResourceApi::DataTypeHandling.mungify(
28+
@data_type,
29+
value,
30+
"#{@type_name}.#{@attribute_name}",
31+
Puppet::ResourceApi.caller_is_resource_app?,
32+
)
33+
end
34+
35+
# used internally
36+
# @returns the final mungified value of this parameter
37+
def rs_value
38+
@value
39+
end
40+
41+
# puppet symbolizes some values through puppet/parameter/value.rb
42+
# (see .convert()), but (especially) Enums are strings. specifying a
43+
# munge block here skips the value_collection fallback in
44+
# puppet/parameter.rb's default .unsafe_munge() implementation.
45+
munge { |v| v }
46+
end

lib/puppet/resource_api/property.rb

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
require 'puppet/util'
2+
require 'puppet/property'
3+
4+
module Puppet; module ResourceApi; end; end # predeclare the main module # rubocop:disable Style/Documentation,Style/ClassAndModuleChildren
5+
6+
# Class containing property functionality for ResourceApi.
7+
class Puppet::ResourceApi::Property < Puppet::Property
8+
# This initialize takes arguments and sets up new property.
9+
# @param type_name the name of the Puppet Type
10+
# @param data_type the data type of property instance
11+
# @param attribute_name the name of attribue of the property
12+
# @param resource_hash the resource hash instance which is passed to the
13+
# parent class.
14+
def initialize(type_name, data_type, attribute_name, resource_hash)
15+
@type_name = type_name
16+
@data_type = data_type
17+
@attribute_name = attribute_name
18+
# Define class method insync?(is) if the name is :ensure
19+
def_insync? if @attribute_name == :ensure && self.class != Puppet::ResourceApi::Property
20+
# Pass resource to parent Puppet class.
21+
super(resource_hash)
22+
end
23+
24+
# This method returns value of the property.
25+
# @return [type] the property value
26+
def should
27+
if @attribute_name == :ensure && rs_value.is_a?(String)
28+
rs_value.to_sym
29+
elsif rs_value == false
30+
# work around https://tickets.puppetlabs.com/browse/PUP-2368
31+
:false # rubocop:disable Lint/BooleanSymbol
32+
elsif rs_value == true
33+
# work around https://tickets.puppetlabs.com/browse/PUP-2368
34+
:true # rubocop:disable Lint/BooleanSymbol
35+
else
36+
rs_value
37+
end
38+
end
39+
40+
# This method sets and returns value of the property and sets @shouldorig.
41+
# @param value the value to be set and clean
42+
# @return [type] the property value
43+
def should=(value)
44+
@shouldorig = value
45+
46+
if @attribute_name == :ensure
47+
value = value.to_s
48+
end
49+
50+
# Puppet requires the @should value to always be stored as an array. We do not use this
51+
# for anything else
52+
# @see Puppet::Property.should=(value)
53+
@should = [
54+
Puppet::ResourceApi::DataTypeHandling.mungify(
55+
@data_type,
56+
value,
57+
"#{@type_name}.#{@attribute_name}",
58+
Puppet::ResourceApi.caller_is_resource_app?,
59+
),
60+
]
61+
end
62+
63+
# used internally
64+
# @returns the final mungified value of this property
65+
def rs_value
66+
@should ? @should.first : @should
67+
end
68+
69+
# method overloaded only for the :ensure property, add option to check if the
70+
# rs_value matches is. Only if the class is child of
71+
# Puppet::ResourceApi::Property.
72+
def def_insync?
73+
define_singleton_method(:insync?) { |is| rs_value.to_s == is.to_s }
74+
end
75+
76+
# puppet symbolizes some values through puppet/parameter/value.rb
77+
# (see .convert()), but (especially) Enums are strings. specifying a
78+
# munge block here skips the value_collection fallback in
79+
# puppet/parameter.rb's default .unsafe_munge() implementation.
80+
munge { |v| v }
81+
82+
# stop puppet from trying to call into the provider when
83+
# no pre-defined values have been specified
84+
# "This is not the provider you are looking for." -- Obi-Wan Kaniesobi.
85+
def call_provider(_value); end
86+
end
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
require 'puppet/util'
2+
require 'puppet/resource_api/parameter'
3+
4+
# Class containing read only parameter functionality for ResourceApi.
5+
class Puppet::ResourceApi::ReadOnlyParameter < Puppet::ResourceApi::Parameter
6+
# This method raises error if the there is attempt to set value in parameter.
7+
# @return [Puppet::ResourceError] the error with information.
8+
def value=(value)
9+
raise Puppet::ResourceError,
10+
"Attempting to set `#{@attribute_name}` read_only attribute value " \
11+
"to `#{value}`"
12+
end
13+
14+
# used internally
15+
# @returns the final mungified value of this parameter
16+
def rs_value
17+
@value
18+
end
19+
end

0 commit comments

Comments
 (0)