poise 2.2.3 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +14 -0
- data/Gemfile +0 -2
- data/lib/poise.rb +3 -1
- data/lib/poise/backports.rb +27 -0
- data/lib/poise/backports/not_passed.rb +52 -0
- data/lib/poise/helpers.rb +1 -0
- data/lib/poise/helpers/chefspec_matchers.rb +5 -1
- data/lib/poise/helpers/inversion.rb +36 -13
- data/lib/poise/helpers/option_collector.rb +15 -4
- data/lib/poise/helpers/resource_name.rb +1 -1
- data/lib/poise/helpers/resource_subclass.rb +81 -0
- data/lib/poise/helpers/subresources/child.rb +50 -9
- data/lib/poise/helpers/subresources/container.rb +33 -6
- data/lib/poise/resource.rb +3 -1
- data/lib/poise/subcontext/resource_collection.rb +20 -1
- data/lib/poise/utils.rb +79 -7
- data/lib/poise/utils/resource_provider_mixin.rb +2 -2
- data/lib/poise/version.rb +1 -1
- data/test/spec/backports/not_passed_spec.rb +29 -0
- data/test/spec/helpers/chefspec_matchers_spec.rb +17 -0
- data/test/spec/helpers/inversion_spec.rb +72 -0
- data/test/spec/helpers/lwrp_polyfill_spec.rb +9 -0
- data/test/spec/helpers/option_collector_spec.rb +66 -30
- data/test/spec/helpers/resource_name_spec.rb +15 -2
- data/test/spec/helpers/resource_subclass_spec.rb +97 -0
- data/test/spec/helpers/subresources/child_spec.rb +234 -2
- data/test/spec/helpers/subresources/container_spec.rb +37 -0
- data/test/spec/resource_spec.rb +31 -2
- data/test/spec/subcontext/resource_collection_spec.rb +99 -0
- data/test/spec/utils/resource_provider_mixin_spec.rb +22 -0
- data/test/spec/utils_spec.rb +187 -1
- metadata +11 -3
- data/Berksfile.lock +0 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9346bbce570e141a731a72396f1d1ce0e6da5bae
|
4
|
+
data.tar.gz: 5a0d996080b83353b59eee2484b657613472d12c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f53889732b8ffe28dd15938fcb24d87686e4dfc984b3b44807fdbffe3412e0dce62263efa200faf3617f7bfe9beb4b6873a4121d2fbee92adf7477452110e735
|
7
|
+
data.tar.gz: d06c1b67c3bb6fd46edc21794cd1e28af06d89938e973da7dddeccf1f0eced60ef32295d14b78e6d927e4d8299dbdbdebd49b24c27308097746563b7b880ba32
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,19 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## v2.3.0
|
4
|
+
|
5
|
+
* New helper: `ResourceSubclass`, a helper for subclassing a resource while
|
6
|
+
still using the providers as the base class.
|
7
|
+
* New feature: Non-default containers. Use `container_default: false` to mark
|
8
|
+
a container class as ineligible for default lookup.
|
9
|
+
* New feature: parent attribute defaults. You can set a `parent_default` to
|
10
|
+
provide a default value for the parent of a resource. This supports the
|
11
|
+
`lazy { }` helper as with normal default values.
|
12
|
+
* New feature: use `forced_keys: [:name]` on an option collector property to
|
13
|
+
force keys that would otherwise be clobbered by resource methods.
|
14
|
+
* Can enable verbose logging mode via a node attribute in addition to an
|
15
|
+
environment variable.
|
16
|
+
|
3
17
|
## v2.2.3
|
4
18
|
|
5
19
|
* Add `ancestor_send` utility method for use in other helpers.
|
data/Gemfile
CHANGED
data/lib/poise.rb
CHANGED
@@ -22,7 +22,9 @@ require 'poise/utils/resource_provider_mixin'
|
|
22
22
|
|
23
23
|
module Poise
|
24
24
|
include Poise::Utils::ResourceProviderMixin
|
25
|
+
autoload :Backports, 'poise/backports'
|
25
26
|
autoload :Helpers, 'poise/helpers'
|
27
|
+
autoload :NOT_PASSED, 'poise/backports/not_passed'
|
26
28
|
autoload :Provider, 'poise/provider'
|
27
29
|
autoload :Resource, 'poise/resource'
|
28
30
|
autoload :Subcontext, 'poise/subcontext'
|
@@ -57,7 +59,7 @@ def Poise(options={})
|
|
57
59
|
# Resource-specific options.
|
58
60
|
if klass < Chef::Resource
|
59
61
|
klass.poise_subresource(options[:parent], options[:parent_optional], options[:parent_auto]) if options[:parent]
|
60
|
-
klass.poise_subresource_container(options[:container_namespace]) if options[:container]
|
62
|
+
klass.poise_subresource_container(options[:container_namespace], options[:container_default]) if options[:container]
|
61
63
|
klass.poise_fused if options[:fused]
|
62
64
|
klass.poise_inversion(options[:inversion_options_resource]) if options[:inversion]
|
63
65
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2015, Noah Kantrowitz
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
|
17
|
+
|
18
|
+
module Poise
|
19
|
+
# Backported features from Chef to be able to use them with older versions.
|
20
|
+
#
|
21
|
+
# @since 2.3.0
|
22
|
+
module Backports
|
23
|
+
autoload :NOT_PASSED, 'poise/backports/not_passed'
|
24
|
+
end
|
25
|
+
|
26
|
+
autoload :NOT_PASSED, 'poise/backports/not_passed'
|
27
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2015, Noah Kantrowitz
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
|
17
|
+
begin
|
18
|
+
require 'chef/constants'
|
19
|
+
rescue LoadError
|
20
|
+
# This space left intentionally blank.
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
module Poise
|
25
|
+
module Backports
|
26
|
+
# A sentinel value for optional arguments where nil is a valid value.
|
27
|
+
# @since 2.3.0
|
28
|
+
# @!parse NOT_PASSED = Object.new
|
29
|
+
NOT_PASSED = if defined?(Chef::NOT_PASSED)
|
30
|
+
Chef::NOT_PASSED
|
31
|
+
else
|
32
|
+
# Copyright 2015, Chef Software Inc.
|
33
|
+
# Used under Apache License, Version 2.0.
|
34
|
+
Object.new.tap do |not_passed|
|
35
|
+
def not_passed.to_s
|
36
|
+
"NOT_PASSED"
|
37
|
+
end
|
38
|
+
def not_passed.inspect
|
39
|
+
to_s
|
40
|
+
end
|
41
|
+
not_passed.freeze
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
# An alias to {Backports::NOT_PASSED} to avoid typing so much.
|
48
|
+
#
|
49
|
+
# @since 2.3.0
|
50
|
+
# @see Backports::NOT_PASSED
|
51
|
+
NOT_PASSED = Backports::NOT_PASSED
|
52
|
+
end
|
data/lib/poise/helpers.rb
CHANGED
@@ -28,6 +28,7 @@ module Poise
|
|
28
28
|
autoload :OptionCollector, 'poise/helpers/option_collector'
|
29
29
|
autoload :ResourceCloning, 'poise/helpers/resource_cloning'
|
30
30
|
autoload :ResourceName, 'poise/helpers/resource_name'
|
31
|
+
autoload :ResourceSubclass, 'poise/helpers/resource_subclass'
|
31
32
|
autoload :Subresources, 'poise/helpers/subresources'
|
32
33
|
autoload :TemplateContent, 'poise/helpers/template_content'
|
33
34
|
end
|
@@ -62,6 +62,10 @@ module Poise
|
|
62
62
|
# @see Resource::ResourceName.provides
|
63
63
|
def provides(name, *args, &block)
|
64
64
|
ChefSpec.define_matcher(name) if defined?(ChefSpec)
|
65
|
+
# Call #actions here to grab any actions from a parent class.
|
66
|
+
actions.each do |action|
|
67
|
+
ChefspecMatchers.create_matcher(name, action)
|
68
|
+
end
|
65
69
|
super(name, *args, &block)
|
66
70
|
end
|
67
71
|
|
@@ -72,7 +76,7 @@ module Poise
|
|
72
76
|
super.tap do |actions|
|
73
77
|
actions.each do |action|
|
74
78
|
ChefspecMatchers.create_matcher(resource_name, action)
|
75
|
-
end
|
79
|
+
end if resource_name
|
76
80
|
end
|
77
81
|
end
|
78
82
|
|
@@ -19,6 +19,7 @@ require 'chef/node_map'
|
|
19
19
|
require 'chef/provider'
|
20
20
|
require 'chef/resource'
|
21
21
|
|
22
|
+
require 'poise/backports'
|
22
23
|
require 'poise/helpers/defined_in'
|
23
24
|
require 'poise/error'
|
24
25
|
require 'poise/helpers/inversion/options_resource'
|
@@ -117,6 +118,9 @@ module Poise
|
|
117
118
|
define_singleton_method(:name) do
|
118
119
|
"#{enclosing_class}::OptionsResource"
|
119
120
|
end
|
121
|
+
define_singleton_method(:inversion_resource_class) do
|
122
|
+
enclosing_class
|
123
|
+
end
|
120
124
|
provides(options_resource_name)
|
121
125
|
inversion_resource(name)
|
122
126
|
end
|
@@ -126,6 +130,9 @@ module Poise
|
|
126
130
|
define_singleton_method(:name) do
|
127
131
|
"#{enclosing_class}::OptionsProvider"
|
128
132
|
end
|
133
|
+
define_singleton_method(:inversion_resource_class) do
|
134
|
+
enclosing_class
|
135
|
+
end
|
129
136
|
provides(options_resource_name)
|
130
137
|
end
|
131
138
|
end
|
@@ -170,40 +177,48 @@ module Poise
|
|
170
177
|
module ClassMethods
|
171
178
|
# @overload inversion_resource()
|
172
179
|
# Return the inversion resource name for this class.
|
173
|
-
# @return [
|
180
|
+
# @return [Symbo, nill]
|
174
181
|
# @overload inversion_resource(val)
|
175
182
|
# Set the inversion resource name for this class. You can pass either
|
176
183
|
# a symbol in DSL format or a resource class that uses Poise. This
|
177
184
|
# name is used to determine which resources the inversion provider is
|
178
185
|
# a candidate for.
|
179
186
|
# @param val [Symbol, Class] Name to set.
|
180
|
-
# @return [Symbol]
|
181
|
-
def inversion_resource(val=
|
182
|
-
if val
|
187
|
+
# @return [Symbol, nil]
|
188
|
+
def inversion_resource(val=Poise::NOT_PASSED)
|
189
|
+
if val != Poise::NOT_PASSED
|
183
190
|
val = val.resource_name if val.is_a?(Class)
|
184
191
|
Chef::Log.debug("[#{self.name}] Setting inversion resource to #{val}")
|
185
192
|
@poise_inversion_resource = val.to_sym
|
186
193
|
end
|
187
|
-
|
194
|
+
if defined?(@poise_inversion_resource)
|
195
|
+
@poise_inversion_resource
|
196
|
+
else
|
197
|
+
Poise::Utils.ancestor_send(self, :inversion_resource, default: nil)
|
198
|
+
end
|
188
199
|
end
|
189
200
|
|
190
201
|
# @overload inversion_attribute()
|
191
202
|
# Return the inversion attribute name(s) for this class.
|
192
|
-
# @return [Array<String
|
203
|
+
# @return [Array<String>, nil]
|
193
204
|
# @overload inversion_attribute(val)
|
194
205
|
# Set the inversion attribute name(s) for this class. This is
|
195
206
|
# used by {.resolve_inversion_attribute} to load configuration data
|
196
207
|
# from node attributes. To specify a nested attribute pass an array
|
197
208
|
# of strings corresponding to the keys.
|
198
209
|
# @param val [String, Array<String>] Attribute path.
|
199
|
-
# @return [Array<String
|
200
|
-
def inversion_attribute(val=
|
201
|
-
if val
|
210
|
+
# @return [Array<String>, nil]
|
211
|
+
def inversion_attribute(val=Poise::NOT_PASSED)
|
212
|
+
if val != Poise::NOT_PASSED
|
202
213
|
# Coerce to an array of strings.
|
203
214
|
val = Array(val).map {|name| name.to_s }
|
204
215
|
@poise_inversion_attribute = val
|
205
216
|
end
|
206
|
-
|
217
|
+
if defined?(@poise_inversion_attribute)
|
218
|
+
@poise_inversion_attribute
|
219
|
+
else
|
220
|
+
Poise::Utils.ancestor_send(self, :inversion_attribute, default: nil)
|
221
|
+
end
|
207
222
|
end
|
208
223
|
|
209
224
|
# Default attribute paths to check for inversion options. Based on
|
@@ -235,6 +250,7 @@ module Poise
|
|
235
250
|
def resolve_inversion_attribute(node)
|
236
251
|
# Default to using just the name of the cookbook.
|
237
252
|
attribute_names = inversion_attribute || default_inversion_attributes(node)
|
253
|
+
return {} if attribute_names.empty?
|
238
254
|
attribute_names.inject(node) do |memo, key|
|
239
255
|
memo[key] || begin
|
240
256
|
raise Poise::Error.new("Attribute #{key} not set when expanding inversion attribute for #{self.name}: #{memo}")
|
@@ -257,13 +273,13 @@ module Poise
|
|
257
273
|
# Class-level defaults.
|
258
274
|
opts.update(default_inversion_options(node, resource))
|
259
275
|
# Resource options for all providers.
|
260
|
-
opts.update(resource.options)
|
276
|
+
opts.update(resource.options) if resource.respond_to?(:options)
|
261
277
|
# Global provider from node attributes.
|
262
278
|
opts.update(provider: attrs['provider']) if attrs['provider']
|
263
279
|
# Attribute options for all providers.
|
264
280
|
opts.update(attrs['options']) if attrs['options']
|
265
281
|
# Resource options for this provider.
|
266
|
-
opts.update(resource.options(provides))
|
282
|
+
opts.update(resource.options(provides)) if resource.respond_to?(:options)
|
267
283
|
# Attribute options for this resource name.
|
268
284
|
opts.update(attrs[resource.name]) if attrs[resource.name]
|
269
285
|
# Options resource options for all providers.
|
@@ -323,7 +339,14 @@ module Poise
|
|
323
339
|
# @return [Boolean]
|
324
340
|
def provides?(node, resource)
|
325
341
|
raise Poise::Error.new("Inversion resource name not set for #{self.name}") unless inversion_resource
|
326
|
-
|
342
|
+
resource_name_equivalents = {resource.resource_name => true}
|
343
|
+
# If subclass_providers! might be in play, check for those names too.
|
344
|
+
if resource.class.respond_to?(:subclass_resource_equivalents)
|
345
|
+
resource.class.subclass_resource_equivalents.each do |name|
|
346
|
+
resource_name_equivalents[name] = true
|
347
|
+
end
|
348
|
+
end
|
349
|
+
return false unless resource_name_equivalents[inversion_resource]
|
327
350
|
provider_name = resolve_inversion_provider(node, resource)
|
328
351
|
Chef::Log.debug("[#{resource}] Checking provides? on #{self.name}. Got provider_name #{provider_name.inspect}")
|
329
352
|
provider_name == provides.to_s || ( provider_name == 'auto' && provides_auto?(node, resource) )
|
@@ -44,18 +44,25 @@ module Poise
|
|
44
44
|
class OptionEvalContext
|
45
45
|
attr_reader :_options
|
46
46
|
|
47
|
-
def initialize(parent)
|
47
|
+
def initialize(parent, forced_keys)
|
48
48
|
@parent = parent
|
49
|
+
@forced_keys = forced_keys
|
49
50
|
@_options = {}
|
50
51
|
end
|
51
52
|
|
52
53
|
def method_missing(method_sym, *args, &block)
|
54
|
+
# Deal with forced keys.
|
55
|
+
if @forced_keys.include?(method_sym)
|
56
|
+
@_options[method_sym] = args.first || block if !args.empty? || block
|
57
|
+
return @_options[method_sym]
|
58
|
+
end
|
59
|
+
# Try the resource context.
|
53
60
|
@parent.send(method_sym, *args, &block)
|
54
61
|
rescue NameError
|
55
62
|
# Even though method= in the block will set a variable instead of
|
56
63
|
# calling method_missing, still try to cope in case of self.method=.
|
57
64
|
method_sym = method_sym.to_s.chomp('=').to_sym
|
58
|
-
if args.
|
65
|
+
if !args.empty? || block
|
59
66
|
@_options[method_sym] = args.first || block
|
60
67
|
elsif !@_options.include?(method_sym)
|
61
68
|
# We haven't seen this name before, re-raise the NameError.
|
@@ -86,8 +93,12 @@ module Poise
|
|
86
93
|
# @param parser [Proc, Symbol] Optional parser method. If a symbol it is
|
87
94
|
# called as a method on self. Takes a non-hash value and returns a
|
88
95
|
# hash of its parsed representation.
|
89
|
-
|
96
|
+
# @param forced_keys [Array<Symbol>, Set<Symbol>] Method names that will be forced
|
97
|
+
# to be options rather than calls to the parent resource.
|
98
|
+
def option_collector_attribute(name, default: {}, parser: nil, forced_keys: Set.new)
|
90
99
|
raise Poise::Error.new("Parser must be a Proc or Symbol: #{parser.inspect}") if parser && !(parser.is_a?(Proc) || parser.is_a?(Symbol))
|
100
|
+
# Cast to a set at definition time.
|
101
|
+
forced_keys = Set.new(forced_keys) unless forced_keys.is_a?(Set)
|
91
102
|
# Unlike LWRPBase.attribute, I don't care about Ruby 1.8. Worlds tiniest violin.
|
92
103
|
define_method(name.to_sym) do |arg=nil, &block|
|
93
104
|
iv_sym = :"@#{name}"
|
@@ -110,7 +121,7 @@ module Poise
|
|
110
121
|
value.update(arg)
|
111
122
|
end
|
112
123
|
if block
|
113
|
-
ctx = OptionEvalContext.new(self)
|
124
|
+
ctx = OptionEvalContext.new(self, forced_keys)
|
114
125
|
ctx.instance_exec(&block)
|
115
126
|
value.update(ctx._options)
|
116
127
|
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2015, Noah Kantrowitz
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
|
17
|
+
require 'poise/error'
|
18
|
+
require 'poise/helpers/resource_name'
|
19
|
+
|
20
|
+
|
21
|
+
module Poise
|
22
|
+
module Helpers
|
23
|
+
# A resource mixin to help subclass existing resources.
|
24
|
+
#
|
25
|
+
# @since 2.3.0
|
26
|
+
module ResourceSubclass
|
27
|
+
include ResourceName
|
28
|
+
|
29
|
+
module ClassMethods
|
30
|
+
def subclass_providers!(superclass_resource_name=nil)
|
31
|
+
resource_name = self.resource_name
|
32
|
+
superclass_resource_name ||= if superclass.respond_to?(:resource_name)
|
33
|
+
superclass.resource_name
|
34
|
+
elsif superclass.respond_to?(:dsl_name)
|
35
|
+
superclass.dsl_name
|
36
|
+
else
|
37
|
+
raise Poise::Error.new("Unable to determine superclass resource name for #{superclass}. Please specify name manually via subclass_providers!('name').")
|
38
|
+
end.to_sym
|
39
|
+
# Deal with the node maps.
|
40
|
+
node_maps = {}
|
41
|
+
node_maps['handler map'] = Chef.provider_handler_map if defined?(Chef.provider_handler_map)
|
42
|
+
node_maps['priority map'] = Chef.provider_priority_map if defined?(Chef.provider_priority_map)
|
43
|
+
# Patch anything in the descendants tracker.
|
44
|
+
Chef::Provider.descendants.each do |provider|
|
45
|
+
node_maps["#{provider} node map"] = provider.node_map if defined?(provider.node_map)
|
46
|
+
end if defined?(Chef::Provider.descendants)
|
47
|
+
node_maps.each do |map_name, node_map|
|
48
|
+
map = node_map.respond_to?(:map, true) ? node_map.send(:map) : node_map.instance_variable_get(:@map)
|
49
|
+
if map.include?(superclass_resource_name)
|
50
|
+
Chef::Log.debug("[#{self}] Copying provider mapping in #{map_name} from #{superclass_resource_name} to #{resource_name}")
|
51
|
+
map[resource_name] = map[superclass_resource_name].dup
|
52
|
+
end
|
53
|
+
end
|
54
|
+
# Add any needed equivalent names.
|
55
|
+
if superclass.respond_to?(:subclass_resource_equivalents)
|
56
|
+
subclass_resource_equivalents.concat(superclass.subclass_resource_equivalents)
|
57
|
+
else
|
58
|
+
subclass_resource_equivalents << superclass_resource_name
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# An array of names for the resources this class is equivalent to for
|
63
|
+
# the purposes of provider resolution.
|
64
|
+
#
|
65
|
+
# @return [Array<Symbol>]
|
66
|
+
def subclass_resource_equivalents
|
67
|
+
@subclass_resource_names ||= [resource_name.to_sym]
|
68
|
+
end
|
69
|
+
|
70
|
+
# @api private
|
71
|
+
def included(klass)
|
72
|
+
super
|
73
|
+
klass.extend(ClassMethods)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
extend ClassMethods
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
@@ -61,7 +61,7 @@ module Poise
|
|
61
61
|
if self.class.parent_type == true
|
62
62
|
raise NoMethodError.new("undefined method `parent' for #{self}")
|
63
63
|
end
|
64
|
-
_parent(:parent, self.class.parent_type, self.class.parent_optional, self.class.parent_auto, *args)
|
64
|
+
_parent(:parent, self.class.parent_type, self.class.parent_optional, self.class.parent_auto, self.class.parent_default, *args)
|
65
65
|
end
|
66
66
|
|
67
67
|
# Register ourself with parents in case this is not a nested resource.
|
@@ -71,7 +71,7 @@ module Poise
|
|
71
71
|
super
|
72
72
|
self.class.parent_attributes.each_key do |name|
|
73
73
|
parent = self.send(name)
|
74
|
-
parent.register_subresource(self) if parent
|
74
|
+
parent.register_subresource(self) if parent && parent.respond_to?(:register_subresource)
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|
@@ -81,7 +81,7 @@ module Poise
|
|
81
81
|
#
|
82
82
|
# @since 2.0.0
|
83
83
|
# @see #parent
|
84
|
-
def _parent(name, parent_type, parent_optional, parent_auto, *args)
|
84
|
+
def _parent(name, parent_type, parent_optional, parent_auto, parent_default, *args)
|
85
85
|
# Allow using a DSL symbol as the parent type.
|
86
86
|
if parent_type.is_a?(Symbol)
|
87
87
|
parent_type = Chef::Resource.resource_for_node(parent_type, node)
|
@@ -96,7 +96,18 @@ module Poise
|
|
96
96
|
else
|
97
97
|
if val.is_a?(String) && !val.include?('[')
|
98
98
|
raise Poise::Error.new("Cannot use a string #{name} without defining a parent type") if parent_type == Chef::Resource
|
99
|
-
|
99
|
+
# Try to find the most recent instance of parent_type with a
|
100
|
+
# matching name. This takes subclassing parent_type into account.
|
101
|
+
found_val = nil
|
102
|
+
iterator = run_context.resource_collection.respond_to?(:recursive_each) ? :recursive_each : :each
|
103
|
+
# This will find the last matching value due to overwriting
|
104
|
+
# found_val as it goes. Will be the nearest match.
|
105
|
+
run_context.resource_collection.public_send(iterator) do |res|
|
106
|
+
found_val = res if res.is_a?(parent_type) && res.name == val
|
107
|
+
end
|
108
|
+
# If found_val is nil, fall back to using lookup even though
|
109
|
+
# it won't work with subclassing, better than nothing?
|
110
|
+
val = found_val || "#{parent_type.resource_name}[#{val}]"
|
100
111
|
end
|
101
112
|
if val.is_a?(String) || val.is_a?(Hash)
|
102
113
|
parent = @run_context.resource_collection.find(val)
|
@@ -109,7 +120,14 @@ module Poise
|
|
109
120
|
end
|
110
121
|
parent_ref = ParentRef.new(parent)
|
111
122
|
elsif !parent_ref || !parent_ref.resource
|
112
|
-
if
|
123
|
+
if parent_default
|
124
|
+
parent = if parent_default.is_a?(Chef::DelayedEvaluator)
|
125
|
+
instance_eval(&parent_default)
|
126
|
+
else
|
127
|
+
parent_default
|
128
|
+
end
|
129
|
+
end
|
130
|
+
if !parent && parent_auto
|
113
131
|
# Automatic sibling lookup for sequential composition.
|
114
132
|
# Find the last instance of the parent class as the default parent.
|
115
133
|
# This is super flaky and should only be a last resort.
|
@@ -121,6 +139,7 @@ module Poise
|
|
121
139
|
else
|
122
140
|
parent = parent_ref.resource
|
123
141
|
end
|
142
|
+
raise Poise::Error.new("Cannot set the #{name} of #{self} to itself") if parent.equal?(self)
|
124
143
|
# Store the ivar back.
|
125
144
|
instance_variable_set(:"@#{name}", parent_ref)
|
126
145
|
# Return the actual resource.
|
@@ -141,7 +160,9 @@ module Poise
|
|
141
160
|
# Setting to true shouldn't actually do anything if a type was already set.
|
142
161
|
@parent_type = type unless type == true && !@parent_type.nil?
|
143
162
|
end
|
144
|
-
|
163
|
+
# First ancestor_send looks for a non-true && non-default value,
|
164
|
+
# second one is to check for default vs true if no real value is found.
|
165
|
+
@parent_type || Poise::Utils.ancestor_send(self, :parent_type, ignore: [Chef::Resource, true]) || Poise::Utils.ancestor_send(self, :parent_type, default: Chef::Resource)
|
145
166
|
end
|
146
167
|
|
147
168
|
# @overload parent_optional()
|
@@ -180,6 +201,26 @@ module Poise
|
|
180
201
|
end
|
181
202
|
end
|
182
203
|
|
204
|
+
# @overload parent_default()
|
205
|
+
# Get the default value for the default parent link on this resource.
|
206
|
+
# @since 2.3.0
|
207
|
+
# @return [Object, Chef::DelayedEvaluator]
|
208
|
+
# @overload parent_default(val)
|
209
|
+
# Set the default value for the default parent link on this resource.
|
210
|
+
# @since 2.3.0
|
211
|
+
# @param val [Object, Chef::DelayedEvaluator] Default value to set.
|
212
|
+
# @return [Object, Chef::DelayedEvaluator]
|
213
|
+
def parent_default(*args)
|
214
|
+
unless args.empty?
|
215
|
+
@parent_default = args.first
|
216
|
+
end
|
217
|
+
if defined?(@parent_default)
|
218
|
+
@parent_default
|
219
|
+
else
|
220
|
+
Poise::Utils.ancestor_send(self, :parent_default)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
183
224
|
# Create a new kind of parent link.
|
184
225
|
#
|
185
226
|
# @since 2.0.0
|
@@ -189,11 +230,11 @@ module Poise
|
|
189
230
|
# @param optional [Boolean] If the parent is optional.
|
190
231
|
# @param auto [Boolean] If the parent is auto-detected.
|
191
232
|
# @return [void]
|
192
|
-
def parent_attribute(name, type: Chef::Resource, optional: false, auto: true)
|
233
|
+
def parent_attribute(name, type: Chef::Resource, optional: false, auto: true, default: nil)
|
193
234
|
name = :"parent_#{name}"
|
194
235
|
(@parent_attributes ||= {})[name] = type
|
195
236
|
define_method(name) do |*args|
|
196
|
-
_parent(name, type, optional, auto, *args)
|
237
|
+
_parent(name, type, optional, auto, default, *args)
|
197
238
|
end
|
198
239
|
end
|
199
240
|
|
@@ -204,7 +245,7 @@ module Poise
|
|
204
245
|
def parent_attributes
|
205
246
|
{}.tap do |attrs|
|
206
247
|
# Grab superclass's attributes if possible.
|
207
|
-
attrs.update(
|
248
|
+
attrs.update(Poise::Utils.ancestor_send(self, :parent_attributes, default: {}))
|
208
249
|
# Local default parent.
|
209
250
|
attrs[:parent] = parent_type
|
210
251
|
# Extra locally defined parents.
|