summaryrefslogtreecommitdiff
path: root/chef
diff options
context:
space:
mode:
authorDan DeLeo <danielsdeleo@mac.com>2009-11-14 17:33:38 -0700
committerDan DeLeo <danielsdeleo@mac.com>2009-11-23 20:06:04 -0700
commitf6812e0814a611f9e4548913fa76cb8b9a6c6f0e (patch)
treecf3a981398040db5f825b56b651fa68c0795b1bf /chef
parent2d03e74e0b12ef2a0f15a76033c2eee7b252a0f5 (diff)
downloadchef-f6812e0814a611f9e4548913fa76cb8b9a6c6f0e.tar.gz
stepable iterator gives execution control
StepableIterator iterates over an array, but also allows execution to be paused, resumed, skipped forward and back, rewound, and stepped through
Diffstat (limited to 'chef')
-rw-r--r--chef/lib/chef/resource_collection.rb19
-rw-r--r--chef/lib/chef/resource_collection/stepable_iterator.rb153
-rw-r--r--chef/spec/unit/resource_collection/stepable_iterator_spec.rb133
3 files changed, 296 insertions, 9 deletions
diff --git a/chef/lib/chef/resource_collection.rb b/chef/lib/chef/resource_collection.rb
index 7681e630a6..8941683776 100644
--- a/chef/lib/chef/resource_collection.rb
+++ b/chef/lib/chef/resource_collection.rb
@@ -67,23 +67,24 @@ class Chef
end
def push(*args)
- args.flatten.each do |a|
- is_chef_resource(a)
- @resources.push(a)
- @resources_by_name[a.to_s] = @resources.length - 1
+ args.flatten.each do |arg|
+ is_chef_resource(arg)
+ @resources.push(arg)
+ @resources_by_name[arg.to_s] = @resources.length - 1
end
end
def each
- @resources.each do |r|
- yield r
+ @resources.each do |resource|
+ yield resource
end
end
- def execute_each_resource
- @resources.each_with_index do |r, idx|
+ def execute_each_resource(&resource_exec_block)
+ @iterator = StepableIterator.for_collection(@resources)
+ @iterator.each_with_index do |resource, idx|
@insert_after_idx = idx
- yield r
+ yield resource
end
end
diff --git a/chef/lib/chef/resource_collection/stepable_iterator.rb b/chef/lib/chef/resource_collection/stepable_iterator.rb
new file mode 100644
index 0000000000..b0770977df
--- /dev/null
+++ b/chef/lib/chef/resource_collection/stepable_iterator.rb
@@ -0,0 +1,153 @@
+# Author:: Daniel DeLeo (<dan@kallistec.com>)
+# Copyright:: Copyright (c) 2009 Daniel DeLeo
+# 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 ResourceCollection
+ class StepableIterator
+ include Singleton
+
+ def self.for_collection(new_collection)
+ instance.reset!
+ instance.collection = new_collection
+ instance
+ end
+
+ def self.pause
+ instance.pause
+ instance
+ end
+
+ def self.resume
+ instance.resume
+ instance
+ end
+
+ def self.rewind
+ instance.rewind
+ instance
+ end
+
+ def self.skip_back(*args)
+ instance.skip_back(*args)
+ instance
+ end
+
+ def self.skip_forward(*args)
+ instance.skip_forward(*args)
+ instance
+ end
+
+ def self.step
+ instance.step
+ instance
+ end
+
+ attr_accessor :collection
+ attr_reader :position
+
+ def initialize
+ reset!
+ end
+
+ def reset!
+ @paused = false
+ @collection = []
+ end
+
+ def size
+ collection.size
+ end
+
+ def each(&block)
+ reset_iteration(block)
+ @iterator_type = :element
+ iterate
+ end
+
+ def each_index(&block)
+ reset_iteration(block)
+ @iterator_type = :index
+ iterate
+ end
+
+ def each_with_index(&block)
+ reset_iteration(block)
+ @iterator_type = :element_with_index
+ iterate
+ end
+
+ def paused?
+ @paused
+ end
+
+ def pause
+ @paused = true
+ end
+
+ def resume
+ @paused = false
+ iterate
+ end
+
+ def rewind
+ @position = 0
+ end
+
+ def skip_back(skips=1)
+ @position -= skips
+ end
+
+ def skip_forward(skips=1)
+ @position += skips
+ end
+
+ def step
+ call_iterator_block
+ @position += 1
+ end
+
+ private
+
+ def reset_iteration(iterator_block)
+ @iterator_block = iterator_block
+ @position = 0
+ @paused = false
+ end
+
+ def iterate
+ while @position < size && !paused?
+ step
+ end
+ collection
+ end
+
+ def call_iterator_block
+ case @iterator_type
+ when :element
+ @iterator_block.call(collection[@position])
+ when :index
+ @iterator_block.call(@position)
+ when :element_with_index
+ @iterator_block.call(collection[@position], @position)
+ else
+ raise "42error: someone forgot to set @iterator_type, wtf?"
+ end
+ end
+
+ end
+ end
+end \ No newline at end of file
diff --git a/chef/spec/unit/resource_collection/stepable_iterator_spec.rb b/chef/spec/unit/resource_collection/stepable_iterator_spec.rb
new file mode 100644
index 0000000000..03d1c4e0ed
--- /dev/null
+++ b/chef/spec/unit/resource_collection/stepable_iterator_spec.rb
@@ -0,0 +1,133 @@
+# Author:: Daniel DeLeo (<dan@kallistec.com>)
+# Copyright:: Copyright (c) 2009 Daniel DeLeo
+# 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 File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_helper"))
+
+describe Chef::ResourceCollection::StepableIterator do
+ CRSI = Chef::ResourceCollection::StepableIterator
+
+ before do
+ CRSI.instance.reset!
+ end
+
+ it "has an empty array for its collection by default" do
+ CRSI.instance.collection.should == []
+ end
+
+ describe "doing basic iteration" do
+ before do
+ @simple_collection = [1,2,3,4]
+ @iterator = CRSI.for_collection(@simple_collection)
+ end
+
+ it "re-initializes the instance with a collection" do
+ @iterator.collection.should equal @simple_collection
+ @iterator.size.should == 4
+ end
+
+ it "iterates over the collection" do
+ sum = 0
+ @iterator.each do |int|
+ sum += int
+ end
+ sum.should == 10
+ end
+
+ it "iterates over the collection with each_index" do
+ collected_by_index = []
+ @iterator.each_index do |idx|
+ collected_by_index << @simple_collection[idx]
+ end
+ collected_by_index.should == @simple_collection
+ collected_by_index.should_not equal @simple_collection
+ end
+
+ it "iterates over the collection with index and element" do
+ collected = {}
+ @iterator.each_with_index do |element, index|
+ collected[index] = element
+ end
+ collected.should == {0=>1, 1=>2, 2=>3, 3=>4}
+ end
+
+ end
+
+ describe "pausing and resuming iteration" do
+
+ before do
+ @collection = []
+ @snitch_var = nil
+ @collection << lambda { @snitch_var = 23 }
+ @collection << lambda { CRSI.pause }
+ @collection << lambda { @snitch_var = 42 }
+
+ @iterator = CRSI.for_collection(@collection)
+ @iterator.each { |proc| proc.call }
+ end
+
+ it "allows the iteration to be paused" do
+ @snitch_var.should == 23
+ end
+
+ it "allows the iteration to be resumed" do
+ @snitch_var.should == 23
+ @iterator.resume
+ @snitch_var.should == 42
+ end
+
+ it "allows iteration to be rewound" do
+ @iterator.skip_back(2)
+ @iterator.resume
+ @snitch_var.should == 23
+ @iterator.resume
+ @snitch_var.should == 42
+ end
+
+ it "allows iteration to be fast forwarded" do
+ @iterator.skip_forward
+ @iterator.resume
+ @snitch_var.should == 23
+ end
+
+ it "allows iteration to be rewound" do
+ @snitch_var = nil
+ @iterator.rewind
+ @iterator.position.should == 0
+ @iterator.resume
+ @snitch_var.should == 23
+ end
+
+ it "allows iteration to be stepped" do
+ @snitch_var = nil
+ @iterator.rewind
+ @iterator.step
+ @iterator.position.should == 1
+ @snitch_var.should == 23
+ end
+
+ it "should work correctly when elements are added to the collection during iteration" do
+ @collection.insert(2, lambda { @snitch_var = 815})
+ @collection.insert(3, lambda { CRSI.pause })
+ @iterator.resume
+ @snitch_var.should == 815
+ @iterator.resume
+ @snitch_var.should == 42
+ end
+
+ end
+
+end \ No newline at end of file