poise 2.0.1 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
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