poise 2.0.1 → 2.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 139be873d0ca15732ed1e63cda3c64fb5f66df07
4
- data.tar.gz: 5c320b061767f73716e12d14cd4435d2ac454ade
3
+ metadata.gz: a493c9d1d4c7f307cfa38c39d5e9de3d7242c3b6
4
+ data.tar.gz: 6b700222a7634a9e301b55dc3be8a41d61a15e04
5
5
  SHA512:
6
- metadata.gz: 3cad8094aa339ae2da67533a14be99ad2ad855f7f9e1896540db99a23a129c714e1cc906e89fa1c46be36a417bc5dfa4b24cf7882d51cfe73b881d1bfb98dabf
7
- data.tar.gz: c924334c534a43fc017a2b2437479ff8b1e73460485df2559ebceb750178ec204219a6075185c2194a214b75cf061aeb7f9e714cee8845ee47a6e678fda28915
6
+ metadata.gz: 3d90977a6a46d00298dddf93479023d27364aa73049e89ef97c4671af27847b695de39ac5af04e962d449818f5fd556ec4cbf168ac5f275c88d35e91c711f722
7
+ data.tar.gz: 81c4de3bb755d2d8c5c2f7e6837eea4f16fab1c942b6d60d6ac611fb8f0867c3ec07153c58156402f00331b4b38e0e3671d9fce3207ec408f737b6a5efa25ff6
@@ -22,4 +22,5 @@ gemfile:
22
22
  - test/gemfiles/chef-12.0.gemfile
23
23
  - test/gemfiles/chef-12.1.gemfile
24
24
  - test/gemfiles/chef-12.2.gemfile
25
+ - test/gemfiles/chef-12.3.gemfile
25
26
  - test/gemfiles/master.gemfile
@@ -5,6 +5,6 @@ DEPENDENCIES
5
5
  path: test/cookbooks/poise_test
6
6
 
7
7
  GRAPH
8
- poise (2.0.0)
8
+ poise (2.0.2)
9
9
  poise_test (0.0.0)
10
10
  poise (>= 0.0.0)
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## v2.1.0
4
+
5
+ * Compatibility with Chef 12.4.
6
+ * Add `#property` as an alias for `#attribute` in resources. This provides
7
+ forward compatibility with future versions of Chef.
8
+ * Freeze default resource attribute values. **This may break your code**,
9
+ however this is not a major release because any code broken by this change
10
+ was itself already a bug.
11
+
3
12
  ## v2.0.1
4
13
 
5
14
  * Make the ChefspecHelpers helper a no-op if chefspec is not already loaded.
data/README.md CHANGED
@@ -21,35 +21,51 @@ the resource, which is in turn based on the class name. This means that the file
21
21
  An example of a simple shell to start from:
22
22
 
23
23
  ```ruby
24
- class Chef
25
- class Resource::MyApp < Resource
26
- include Poise
24
+ require 'poise'
25
+ require 'chef/resource'
26
+ require 'chef/provider'
27
27
 
28
+ module MyApp
29
+ class Resource < Chef::Resource
30
+ include Poise
31
+ provides(:my_app)
28
32
  actions(:enable)
29
33
 
30
34
  attribute(:path, kind_of: String)
31
- ... # Other attribute definitions
35
+ # Other attribute definitions.
32
36
  end
33
37
 
34
- class Provider::MyApp < Provider
38
+ class Provider < Chef::Provider
35
39
  include Poise
40
+ provides(:my_app)
36
41
 
37
42
  def action_enable
38
- converge_by("enable resource #{new_resource.name}") do
39
- notifying_block do
40
- ... # Normal Chef recipe code goes here
41
- end
43
+ notifying_block do
44
+ ... # Normal Chef recipe code goes here
42
45
  end
43
46
  end
44
47
  end
45
48
  end
46
49
  ```
47
50
 
48
- Starting from the top, first we declare the resource class, which inherits from
49
- `Chef::Resource`. This is similar to the `resources/` file in an LWRP, and a similar DSL can be used. In order to load the helpers into the class, we
50
- include the `Poise` mixin. Then we use the familiar DSL, though with a few additions we'll cover later.
51
-
52
- Then we declare the provider class, again similar to the `providers/` file in an LWRP. We include the `Poise` mixin again to get access to all the helpers. Rather than use the `action :enable do ... end` DSL from LWRPs, we just define the action method directly, and use the `converge_by` method to provide a description of what the action does. The implementation of action comes from a block of recipe code wrapped with `notifying_block` to capture changes in much the same way as `use_inline_resources`, see below for more information about all the features of `notifying_block`.
51
+ Starting from the top, first we require the libraries we will be using. Then we
52
+ create a module to hold our resource and provider. If your cookbook declares
53
+ multiple resources and/or providers, you might want additional nesting here.
54
+ Then we declare the resource class, which inherits from `Chef::Resource`. This
55
+ is similar to the `resources/` file in an LWRP, and a similar DSL can be used.
56
+ We then include the `Poise` mixin to load our helpers, and then call
57
+ `provides(:my_app)` to tell Chef this class will implement the `my_app`
58
+ resource. Then we use the familiar DSL, though with a few additions we'll cover
59
+ later.
60
+
61
+ Then we declare the provider class, again similar to the `providers/` file in an
62
+ LWRP. We include the `Poise` mixin again to get access to all the helpers and
63
+ call `provides()` to tell Chef what provider this is. Rather than use the
64
+ `action :enable do ... end` DSL from LWRPs, we just define the action method
65
+ directly. The implementation of action comes from a block of recipe code
66
+ wrapped with `notifying_block` to capture changes in much the same way as
67
+ `use_inline_resources`, see below for more information about all the features of
68
+ `notifying_block`.
53
69
 
54
70
  We can then use this resource like any other Chef resource:
55
71
 
@@ -177,6 +193,16 @@ end
177
193
 
178
194
  This will be converted to `{key1: 'value1', key2: 'value2'}`. You can also pass a Hash to an option collector attribute just as you would with a normal attribute.
179
195
 
196
+ ## Upgrading from Poise 1.x
197
+
198
+ The biggest change when upgrading from Poise 1.0 is that the mixin is no longer
199
+ loaded automatically. You must add `require 'poise'` to your code is you want to
200
+ load it, as you would with normal Ruby code outside of Chef. It is also highly
201
+ recommended to add `provides(:name)` calls to your resources and providers, this
202
+ will be required in Chef 13 and will display a deprecation warning if you do
203
+ not. This also means you can move your code out of the `Chef` module namespace
204
+ and instead declare it in your own namespace. An example of this is shown above.
205
+
180
206
  ## Sponsors
181
207
 
182
208
  The Poise test server infrastructure is generously sponsored by [Rackspace](https://rackspace.com/). Thanks Rackspace!
@@ -14,6 +14,8 @@
14
14
  # limitations under the License.
15
15
  #
16
16
 
17
+ require 'chef/version'
18
+
17
19
 
18
20
  module Poise
19
21
  module Helpers
@@ -29,11 +31,21 @@ module Poise
29
31
  # attribute(:path, default: lazy { name + '_temp' })
30
32
  # end
31
33
  module LazyDefault
34
+ # Check if this version of Chef already supports lazy defaults. This is
35
+ # true for Chef 12.5+.
36
+ #
37
+ # @since 2.0.3
38
+ # @api private
39
+ # @return [Boolean]
40
+ def self.needs_polyfill?
41
+ @needs_polyfill ||= Gem::Requirement.new('< 12.5.pre').satisfied_by?(Gem::Version.new(Chef::VERSION))
42
+ end
43
+
32
44
  # Override the default set_or_return to support lazy evaluation of the
33
45
  # default value. This only actually matters when it is called from a class
34
46
  # level context via #attributes.
35
47
  def set_or_return(symbol, arg, validation)
36
- if validation && validation[:default].is_a?(Chef::DelayedEvaluator)
48
+ if LazyDefault.needs_polyfill? && validation && validation[:default].is_a?(Chef::DelayedEvaluator)
37
49
  validation = validation.dup
38
50
  validation[:default] = instance_eval(&validation[:default])
39
51
  end
@@ -14,6 +14,8 @@
14
14
  # limitations under the License.
15
15
  #
16
16
 
17
+ require 'chef/resource'
18
+
17
19
  require 'poise/utils/resource_provider_mixin'
18
20
 
19
21
 
@@ -30,35 +32,83 @@ module Poise
30
32
  module Resource
31
33
  def initialize(*args)
32
34
  super
33
- # Try to not stomp on stuff if already set in a parent
35
+ # Try to not stomp on stuff if already set in a parent.
34
36
  @action = self.class.default_action if @action == :nothing
35
37
  (@allowed_actions << self.class.actions).flatten!.uniq!
36
38
  end
37
39
 
38
- # @!classmethods
39
40
  module ClassMethods
41
+ # @overload default_action()
42
+ # Get the default action for this resource class. If no explicit
43
+ # default is set, the first action in the list will be used.
44
+ # @see #actions
45
+ # @return [Symbol]
46
+ # @overload default_action(name)
47
+ # Set the default action for this resource class. If this action is
48
+ # not already allowed, it will be added.
49
+ # @note It is idiomatic to use {#actions} instead, with the first
50
+ # action specified being the default.
51
+ # @param name [Symbol] Name of the action.
52
+ # @return [Symbol]
53
+ # @example
54
+ # class MyApp < Chef::Resource
55
+ # include Poise
56
+ # default_action(:install)
57
+ # end
40
58
  def default_action(name=nil)
41
59
  if name
42
60
  @default_action = name
43
61
  actions(name)
44
62
  end
45
- @default_action || ( respond_to?(:superclass) && superclass.respond_to?(:default_action) && superclass.default_action ) || actions.first || :nothing
63
+ @default_action || ( respond_to?(:superclass) && superclass != Chef::Resource && superclass.respond_to?(:default_action) && superclass.default_action ) || actions.first || :nothing
46
64
  end
47
65
 
66
+ # @overload actions()
67
+ # Get all actions allowed for this resource class. This includes
68
+ # any actions allowed on parent classes.
69
+ # @return [Array<Symbol>]
70
+ # @overload actions(*names)
71
+ # Set actions as allowed for this resource class. These must
72
+ # correspond with action methods in the provider class(es).
73
+ # @param names [Array<Symbol>] One or more actions to set.
74
+ # @return [Array<Symbol>]
75
+ # @example
76
+ # class MyApp < Chef::Resource
77
+ # include Poise
78
+ # actions(:install, :uninstall)
79
+ # end
48
80
  def actions(*names)
49
- @actions ||= ( respond_to?(:superclass) && superclass.respond_to?(:actions) ? superclass.actions.dup : [] )
81
+ @actions ||= ( respond_to?(:superclass) && superclass.respond_to?(:actions) && superclass.actions.dup ) || ( respond_to?(:superclass) && superclass != Chef::Resource && superclass.respond_to?(:allowed_actions) && superclass.allowed_actions.dup ) || []
50
82
  (@actions << names).flatten!.uniq!
51
83
  @actions
52
84
  end
53
85
 
54
- def attribute(name, opts)
55
- # Ruby 1.8 can go to hell
86
+ # Create a resource property (née attribute) on this resource class.
87
+ # This follows the same usage as the helper of the same name in Chef
88
+ # LWRPs.
89
+ #
90
+ # @param name [Symbol] Name of the property.
91
+ # @param opts [Hash<Symbol, Object>] Validation options and flags.
92
+ # @return [void]
93
+ # @example
94
+ # class MyApp < Chef::Resource
95
+ # include Poise
96
+ # attribute(:path, name_attribute: true)
97
+ # attribute(:port, kind_of: Integer, default: 8080)
98
+ # end
99
+ def attribute(name, opts={})
100
+ # Freeze the default value. This is done upstream too in Chef 12.5+.
101
+ opts[:default].freeze if opts && opts[:default]
102
+ # Ruby 1.8 can go to hell.
56
103
  define_method(name) do |arg=nil, &block|
57
- arg = block if arg.nil? # Try to allow passing either
104
+ arg = block if arg.nil? # Try to allow passing either.
58
105
  set_or_return(name, arg, opts)
59
106
  end
60
107
  end
61
108
 
109
+ # For forward compat with Chef 12.5+.
110
+ alias_method :property, :attribute
111
+
62
112
  def included(klass)
63
113
  super
64
114
  klass.extend(ClassMethods)
@@ -72,7 +72,9 @@ module Poise
72
72
  # @param auto [Boolean] Try to auto-detect based on class name.
73
73
  # @return [Symbol]
74
74
  def resource_name(auto=true)
75
- return @provides_name if @provides_name
75
+ # In 12.4+ we need to proxy through the super class for setting.
76
+ return super(auto) if defined?(super) && (auto.is_a?(Symbol) || auto.is_a?(String))
77
+ return @provides_name unless auto
76
78
  @provides_name || if name && name.start_with?('Chef::Resource')
77
79
  Chef::Mixin::ConvertToClassName.convert_to_snake_case(name, 'Chef::Resource').to_sym
78
80
  elsif name
@@ -65,6 +65,8 @@ module Poise
65
65
  # @param run_context [Chef::RunContext] Context of the current run.
66
66
  # @return [Array<Chef::Resource>]
67
67
  def self.containers(run_context)
68
+ # For test cases where nil gets used sometimes.
69
+ return [] unless run_context && run_context.node && run_context.node.run_state
68
70
  run_context.node.run_state[:poise_default_containers] ||= []
69
71
  end
70
72
  end
@@ -34,7 +34,7 @@ module Poise
34
34
  module Resource
35
35
  include Poise::Helpers::ChefspecMatchers
36
36
  include Poise::Helpers::DefinedIn
37
- include Poise::Helpers::LazyDefault
37
+ include Poise::Helpers::LazyDefault if Poise::Helpers::LazyDefault.needs_polyfill?
38
38
  include Poise::Helpers::LWRPPolyfill
39
39
  include Poise::Helpers::OptionCollector
40
40
  include Poise::Helpers::ResourceName
@@ -16,5 +16,5 @@
16
16
 
17
17
 
18
18
  module Poise
19
- VERSION = '2.0.1'
19
+ VERSION = '2.1.0'
20
20
  end
@@ -0,0 +1,19 @@
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
+ eval_gemfile File.expand_path('../../../Gemfile', __FILE__)
18
+
19
+ gem 'chef', '~> 12.3.0'
@@ -39,7 +39,7 @@ describe Poise::Helpers::LazyDefault do
39
39
  end
40
40
 
41
41
  it { is_expected.to run_poise_test('test').with(value: 'value') }
42
- end
42
+ end # /context with an explicit value
43
43
 
44
44
  context 'in a subclass' do
45
45
  resource(:poise_sub, parent: :poise_test)
@@ -49,7 +49,7 @@ describe Poise::Helpers::LazyDefault do
49
49
  end
50
50
 
51
51
  it { is_expected.to run_poise_test('test').with(value: 'test_lazy') }
52
- end
52
+ end # /context in a subclass
53
53
 
54
54
  context 'with an external global' do
55
55
  resource(:poise_test) do
@@ -70,5 +70,21 @@ describe Poise::Helpers::LazyDefault do
70
70
  LAZY_DEFAULT_GLOBAL[0] = 42
71
71
  is_expected.to run_poise_test('test').with(value: 0)
72
72
  end
73
- end
73
+ end # /context with an external global
74
+
75
+ context 'with a mutable value' do
76
+ resource(:poise_test) do
77
+ include Poise::Helpers::LWRPPolyfill
78
+ include described_class
79
+ attribute(:value, default: lazy { [] })
80
+ end
81
+ recipe do
82
+ poise_test 'test' do
83
+ value << 1
84
+ value << 2
85
+ end
86
+ end
87
+
88
+ it { is_expected.to run_poise_test('test').with(value: [1, 2]) }
89
+ end # /context with a mutable value
74
90
  end
@@ -23,38 +23,36 @@ describe Poise::Helpers::LWRPPolyfill do
23
23
  include described_class
24
24
  attribute(:boolean, equal_to: [true, false])
25
25
  end
26
-
27
26
  provider(:poise_test)
28
-
29
- context 'with default value' do
30
- recipe do
31
- poise_test 'nil'
32
- end
33
-
34
- it { is_expected.to run_poise_test('nil').with(boolean: nil) }
27
+ recipe do
28
+ poise_test 'test'
35
29
  end
36
30
 
37
- context 'with true value' do
31
+ context 'with no value' do
32
+ it { is_expected.to run_poise_test('test').with(boolean: nil) }
33
+ end # /context with no value
34
+
35
+ context 'with a true value' do
38
36
  recipe do
39
- poise_test 'true' do
37
+ poise_test 'test' do
40
38
  boolean true
41
39
  end
42
40
  end
43
41
 
44
- it { is_expected.to run_poise_test('true').with(boolean: true) }
45
- end
42
+ it { is_expected.to run_poise_test('test').with(boolean: true) }
43
+ end # /context with a true value
46
44
 
47
- context 'with false value' do
45
+ context 'with a false value' do
48
46
  recipe do
49
- poise_test 'false' do
47
+ poise_test 'test' do
50
48
  boolean false
51
49
  end
52
50
  end
53
51
 
54
- it { is_expected.to run_poise_test('false').with(boolean: false) }
55
- end
52
+ it { is_expected.to run_poise_test('test').with(boolean: false) }
53
+ end # /context with a false value
56
54
 
57
- context 'with string value' do
55
+ context 'with a string value' do
58
56
  recipe do
59
57
  poise_test 'true' do
60
58
  boolean 'boom'
@@ -62,7 +60,36 @@ describe Poise::Helpers::LWRPPolyfill do
62
60
  end
63
61
 
64
62
  it { expect { subject }.to raise_error Chef::Exceptions::ValidationFailed }
65
- end
63
+ end # /context with a string value
64
+
65
+ context 'with a default value' do
66
+ resource(:poise_test) do
67
+ include described_class
68
+ attribute(:value, default: 'default value')
69
+ end
70
+
71
+ it { is_expected.to run_poise_test('test').with(value: 'default value') }
72
+ end # /context with a default value
73
+
74
+ context 'with a mutable default value' do
75
+ resource(:poise_test) do
76
+ include described_class
77
+ attribute(:value, default: [])
78
+ end
79
+
80
+ it { is_expected.to run_poise_test('test').with(value: []) }
81
+ it { expect(chef_run.poise_test('test').value).to be_frozen }
82
+
83
+ context 'and trying to change it' do
84
+ recipe do
85
+ poise_test 'test' do
86
+ value << 1
87
+ end
88
+ end
89
+
90
+ it { expect { chef_run }.to raise_error RuntimeError }
91
+ end # /context and trying to change it
92
+ end # /context with a mutable default value
66
93
  end # /describe #attribute
67
94
 
68
95
  describe '#default_action' do
@@ -93,7 +120,7 @@ describe Poise::Helpers::LWRPPolyfill do
93
120
  end
94
121
 
95
122
  it { is_expected.to be_nil }
96
- end
123
+ end # /context with a direct Provider subclass
97
124
 
98
125
  context 'with an intermediary class' do
99
126
  provider(:poise_parent, auto: false) do
@@ -106,8 +133,8 @@ describe Poise::Helpers::LWRPPolyfill do
106
133
  end
107
134
 
108
135
  it { is_expected.to eq 'helper' }
109
- end
110
- end
136
+ end # /context with an intermediary class
137
+ end # /describe load_current_resource override
111
138
 
112
139
  describe 'Chef::DSL::Recipe include' do
113
140
  resource(:poise_test)
@@ -123,6 +150,6 @@ describe Poise::Helpers::LWRPPolyfill do
123
150
  end
124
151
 
125
152
  it { is_expected.to run_ruby_block('test') }
126
- end
153
+ end # /describe Chef::DSL::Recipe include
127
154
  end # /describe Poise::Helpers::LWRPPolyfill::Provider
128
155
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: poise
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.1
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Noah Kantrowitz
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-05-29 00:00:00.000000000 Z
11
+ date: 2015-06-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: halite
@@ -100,6 +100,7 @@ files:
100
100
  - test/gemfiles/chef-12.0.gemfile
101
101
  - test/gemfiles/chef-12.1.gemfile
102
102
  - test/gemfiles/chef-12.2.gemfile
103
+ - test/gemfiles/chef-12.3.gemfile
103
104
  - test/gemfiles/chef-12.gemfile
104
105
  - test/gemfiles/master.gemfile
105
106
  - test/integration/default/serverspec/default_spec.rb
@@ -145,7 +146,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
145
146
  version: '0'
146
147
  requirements: []
147
148
  rubyforge_project:
148
- rubygems_version: 2.4.5
149
+ rubygems_version: 2.4.8
149
150
  signing_key:
150
151
  specification_version: 4
151
152
  summary: Helpers for writing extensible Chef cookbooks.
@@ -162,6 +163,7 @@ test_files:
162
163
  - test/gemfiles/chef-12.0.gemfile
163
164
  - test/gemfiles/chef-12.1.gemfile
164
165
  - test/gemfiles/chef-12.2.gemfile
166
+ - test/gemfiles/chef-12.3.gemfile
165
167
  - test/gemfiles/chef-12.gemfile
166
168
  - test/gemfiles/master.gemfile
167
169
  - test/integration/default/serverspec/default_spec.rb