diff options
author | John Keiser <john@johnkeiser.com> | 2015-11-06 10:39:41 -0800 |
---|---|---|
committer | John Keiser <john@johnkeiser.com> | 2015-11-06 10:40:16 -0800 |
commit | c37e8934f995db7caa235544886dc648b2cb38aa (patch) | |
tree | 65e2dc79a26621f9ef66d727ba3588b4604c5cab | |
parent | f474f98750fe70e0264bf80793e578b739c08206 (diff) | |
download | chef-jk/recipe_block.tar.gz |
IN PROGRESS IGNOREjk/recipe_block
-rw-r--r-- | lib/chef/dsl/cheffish_resources.rb | 0 | ||||
-rw-r--r-- | lib/chef/dsl/resources.rb | 3 | ||||
-rw-r--r-- | lib/chef/property/standard_types.rb | 43 | ||||
-rw-r--r-- | lib/chef/property/standard_types/array_property.rb | 13 | ||||
-rw-r--r-- | lib/chef/property/standard_types/proc_property.rb | 60 | ||||
-rw-r--r-- | lib/chef/property/standard_types/set_property.rb | 13 | ||||
-rw-r--r-- | lib/chef/resource.rb | 4 | ||||
-rw-r--r-- | lib/chef/resource/recipe_block.rb | 80 | ||||
-rw-r--r-- | spec/unit/property/standard_types_spec.rb | 35 |
9 files changed, 251 insertions, 0 deletions
diff --git a/lib/chef/dsl/cheffish_resources.rb b/lib/chef/dsl/cheffish_resources.rb new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/lib/chef/dsl/cheffish_resources.rb diff --git a/lib/chef/dsl/resources.rb b/lib/chef/dsl/resources.rb index 49588ed516..ef2487ebcc 100644 --- a/lib/chef/dsl/resources.rb +++ b/lib/chef/dsl/resources.rb @@ -7,6 +7,9 @@ class Chef # # @api private module Resources + def self.add_lazy_resource_dsl(dsl_name, requires: nil, resource_class: nil, provider_class: nil) + + end def self.add_resource_dsl(dsl_name) begin module_eval(<<-EOM, __FILE__, __LINE__+1) diff --git a/lib/chef/property/standard_types.rb b/lib/chef/property/standard_types.rb new file mode 100644 index 0000000000..e43177b7a3 --- /dev/null +++ b/lib/chef/property/standard_types.rb @@ -0,0 +1,43 @@ +# +# Author:: John Keiser (<jkeiser@chef.io) +# Copyright:: Copyright (c) 2015 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/mixin/properties' +require 'chef/property/standard_types/proc_property' + +class Chef + class Property + module StandardTypes + include Chef::Mixin::Properties + Boolean = property_type(is: [ true, false ], default: false) + + # + # Treat `property :x, Proc, ...` the same as `property :x, ProcProperty, ...` + # so that you get the nicer property DSL letting you say x { ... } + # instead of just x proc { ... } + # + def property(name, type=NOT_PASSED, **options) + if type != NOT_PASSED && type.is_a?(Class) + if type <= Proc + type = ProcProperty + end + end + super + end + end + end +end diff --git a/lib/chef/property/standard_types/array_property.rb b/lib/chef/property/standard_types/array_property.rb new file mode 100644 index 0000000000..a091888ead --- /dev/null +++ b/lib/chef/property/standard_types/array_property.rb @@ -0,0 +1,13 @@ +class Chef + class Property + module StandardTypes + class ArrayProperty < Property + def initialize(**options) + options[:is] ||= Array + options[:coerce] ||= proc { |v| v.is_a?(Array) ? v : Array(v) } + super + end + end + end + end +end diff --git a/lib/chef/property/standard_types/proc_property.rb b/lib/chef/property/standard_types/proc_property.rb new file mode 100644 index 0000000000..09461d76ba --- /dev/null +++ b/lib/chef/property/standard_types/proc_property.rb @@ -0,0 +1,60 @@ +# +# Author:: John Keiser (<jkeiser@chef.io) +# Copyright:: Copyright (c) 2015 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +class Chef + class Property + module StandardTypes + class ProcProperty < Property + # "is" defaults to `Proc` (we validate that it is a Proc) + def initialize(is: Proc, **options) + super + end + + # Allow the + def emit_dsl + # We don't create the getter/setter if it's a custom property; we will + # be using the existing getter/setter to manipulate it instead. + return if !instance_variable_name + + # We prefer this form because the property name won't show up in the + # stack trace if you use `define_method`. + declared_in.class_eval <<-EOM, __FILE__, __LINE__+1 + def #{name}(value=NOT_PASSED, &block) + raise ArgumentError, "Cannot specify both value and block when setting #{name}" if block && value != NOT_PASSED + value = block if block + self.class.properties[#{name.inspect}].call(self, value) + end + def #{name}=(value) + self.class.properties[#{name.inspect}].set(self, value) + end + EOM + rescue SyntaxError + # If the name is not a valid ruby name, we use define_method. + declared_in.define_method(name) do |value=NOT_PASSED| + raise ArgumentError, "Cannot specify both value and block when setting #{name}" if block && value != NOT_PASSED + value = block if block + self.class.properties[name].call(self, value) + end + declared_in.define_method("#{name}=") do |value| + self.class.properties[name].set(self, value) + end + end + end + end + end +end diff --git a/lib/chef/property/standard_types/set_property.rb b/lib/chef/property/standard_types/set_property.rb new file mode 100644 index 0000000000..6e7606bc81 --- /dev/null +++ b/lib/chef/property/standard_types/set_property.rb @@ -0,0 +1,13 @@ +class Chef + class Property + module StandardTypes + class SetProperty < Property + def initialize(**options) + options[:is] ||= Set + options[:coerce] ||= proc { |v| v.is_a?(Set) ? v : Set.new(v) } + super + end + end + end + end +end diff --git a/lib/chef/resource.rb b/lib/chef/resource.rb index 90453bd00e..fe2fcc6d9b 100644 --- a/lib/chef/resource.rb +++ b/lib/chef/resource.rb @@ -36,6 +36,7 @@ require 'chef/platform' require 'chef/resource/resource_notification' require 'chef/provider_resolver' require 'chef/resource_resolver' +require 'chef/property/standard_types' require 'set' require 'chef/mixin/deprecation' @@ -64,6 +65,9 @@ class Chef # Bring in `property` and `property_type` include Chef::Mixin::Properties + # Bring in standard types like Boolean and ProcProperty + include Chef::Property::StandardTypes + # # The name of this particular resource. # diff --git a/lib/chef/resource/recipe_block.rb b/lib/chef/resource/recipe_block.rb new file mode 100644 index 0000000000..f823a97a06 --- /dev/null +++ b/lib/chef/resource/recipe_block.rb @@ -0,0 +1,80 @@ +# +# Author:: John Keiser (<jkeiser@chef.io) +# Copyright:: Copyright (c) 2015 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/resource' + +class Chef + class Resource + # + # A converge-time recipe. + # + # Resources inside the block behave exactly the same as resources + # in a Chef::Resource#action block. + # + # Example: + # + # file '/x.txt' do + # content 'hi' + # end + # recipe_block 'make a file with the length of /x.txt in it' do + # block do + # # Since we are in a recipe_block, the file '/x.txt' block has + # # already converged and we can do this calculation here! + # length_of_file = IO.read('/x.txt').length + # file '/x-length.txt' do + # content "Length: #{length_of_file}" + # end + # # However, if we tried to IO.read('/x-length.txt') here, it would + # # fail, because the entire block is first compiled and then converged. + # end + # end + # + class RecipeBlock < Chef::Resource + resource_name :recipe_block + + # + # A ruby block containing the recipe that will be run. + # + # All recipe DSL is valid here too. + # + # @example Using a block argument + # recipe_block 'hi' do + # block do + # file '/x.txt' + # end + # end + # + # @example Passing the proc directly + # recipe_block 'hi' do + # x = proc { file '/x.txt '} + # block x + # end + # + property :block, ProcProperty + + # + # Compiles and converges the block as a recipe. The resource will be + # marked updated (and notify) if any of the resources in the recipe update. + # Behavior is identical to Chef::Resource#action. + # + action :run do + instance_eval(&recipe_block) + end + end + end +end diff --git a/spec/unit/property/standard_types_spec.rb b/spec/unit/property/standard_types_spec.rb new file mode 100644 index 0000000000..12e18b45d3 --- /dev/null +++ b/spec/unit/property/standard_types_spec.rb @@ -0,0 +1,35 @@ +require 'support/shared/integration/integration_helper' + +module StandardTypesTests + + describe Chef::Property::StandardTypes do + include IntegrationSupport + + describe "Boolean" do + Boolean + context "With a resource with a Boolean property" do + class BooleanResource < Chef::Resource + property :x, Boolean + end + + let(:resource) { BooleanResource.new("blah") } + + it "defaults to false" do + expect(resource.x).to eq false + end + it "accepts true" do + resource.x true + expect(resource.x).to eq true + end + it "accepts false" do + resource.x false + expect(resource.x).to eq false + end + it "does not accept 1" do + expect { resource.x 1 }.to raise Chef::Exceptions::ValidationError + end + end + end + end + +end |