summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLamont Granquist <lamont@scriptkiddie.org>2015-10-19 09:52:13 -0700
committerLamont Granquist <lamont@scriptkiddie.org>2016-01-07 11:24:28 -0800
commit21770dcdb3f7a62a57c2019129522a73d63d5168 (patch)
treea19fb5e88e4fb41cb60e8cabdbd4d15b9c1a45c0
parent7637515eb55dd982d6192d055af7e746cc61f0ae (diff)
downloadchef-21770dcdb3f7a62a57c2019129522a73d63d5168.tar.gz
more tests
-rw-r--r--lib/chef/node/attribute_trait/decorator.rb124
-rw-r--r--spec/unit/node/attribute_trait/decorator_spec.rb114
2 files changed, 174 insertions, 64 deletions
diff --git a/lib/chef/node/attribute_trait/decorator.rb b/lib/chef/node/attribute_trait/decorator.rb
index 6effd389d8..59d71febfa 100644
--- a/lib/chef/node/attribute_trait/decorator.rb
+++ b/lib/chef/node/attribute_trait/decorator.rb
@@ -5,12 +5,11 @@ class Chef
attr_accessor :wrapped_object
#
- # Delegate methods common to Array and Hash
- # - this is done for speed over method_missing
- # - some of these methods are overridden later with different semantics
+ # Performance Delegation
#
+
methods = ( Array.instance_methods & Hash.instance_methods ) - Object.instance_methods +
- [ :!, :!=, :<=>, :==, :===, :eql?, :to_s, :hash ]
+ [ :!, :!=, :<=>, :==, :===, :eql?, :to_s, :hash, :key, :has_key? ]
methods.each do |method|
define_method method do |*args, &block|
@@ -18,6 +17,10 @@ class Chef
end
end
+ #
+ # Construction
+ #
+
def self.included(base)
base.extend(ClassMethods)
end
@@ -32,6 +35,14 @@ class Chef
@wrapped_object = wrapped_object
end
+ def new_decorator(**args)
+ self.class.new(**args)
+ end
+
+ #
+ # Conversion
+ #
+
def ffi_yajl(*opts)
for_json.ffi_yajl(*opts)
end
@@ -50,6 +61,10 @@ class Chef
end
end
+ #
+ # Inspection
+ #
+
def is_a?(klass)
wrapped_object.is_a?(klass) || super
end
@@ -58,6 +73,10 @@ class Chef
wrapped_object.kind_of?(klass) || super
end
+ #
+ # Extended API
+ #
+
def regular_writer(*path, value)
last_key = path.pop
obj = path.inject(wrapped_object) { |memo, key| memo[key] }
@@ -85,25 +104,13 @@ class Chef
hash.delete(last)
end
- def [](key)
- maybe_decorated_value(wrapped_object[key])
- end
-
- def maybe_decorated_value(val)
- if val.is_a?(Hash) || val.is_a?(Array)
- new_decorator(wrapped_object: val)
- else
- val
- end
- end
-
- def new_decorator(**args)
- self.class.new(**args)
- end
+ #
+ # method_missing
+ #
def method_missing(method, *args, &block)
if wrapped_object.respond_to?(method, false)
- # cannot define_method here
+ # do not define_method here
wrapped_object.public_send(method, *args, &block)
else
super
@@ -122,34 +129,41 @@ class Chef
wrapped_object.respond_to?(method, false) || super
end
+ #
+ # Decorated Methods
+ #
- def initialize_copy(source)
- super
- @wrapped_object = safe_dup(source.wrapped_object)
- end
-
- def include?(key)
- wrapped_object.include?(key)
- end
-
- # performance
- def key?(key)
- wrapped_object.key?(key)
+ def [](key)
+ maybe_decorated_value(wrapped_object[key])
end
- # performance
- def has_key?(key)
- wrapped_object.has_key?(key)
+ def any?(&block)
+ block ||= ->(o) { o }
+ if is_a?(Hash)
+ each do |key, value|
+ if yield key, value
+ return true
+ end
+ end
+ elsif is_a?(Array)
+ each do |key|
+ if yield key
+ return true
+ end
+ end
+ else
+ return wrapped_object.any?(&block)
+ end
+ false
end
- # we need to be careful to return decorated values when appropriate
def each(&block)
return enum_for(:each) unless block_given?
- if wrapped_object.is_a?(Array)
+ if is_a?(Array)
wrapped_object.each_with_index do |value, i|
yield self[i]
end
- elsif wrapped_object.is_a?(Hash)
+ elsif is_a?(Hash)
if block.arity > 1
wrapped_object.each do |key, value|
yield key, self[key]
@@ -160,14 +174,29 @@ class Chef
end
end
else
- # dunno...
wrapped_object.each(&block)
end
end
- # return decorated values when appropriate
alias_method :each_pair, :each
+ def map(&block)
+ return enum_for(:map) unless block_given?
+ if is_a?(Array)
+ ret = []
+ each do |elem|
+ ret.push(yield elem)
+ end
+ ret
+ else
+ wrapped_object.map(&block)
+ end
+ end
+
+ #
+ # Copying and dup'ing
+ #
+
# nil, true, false and Fixnums are not dup'able
def safe_dup(e)
e.dup
@@ -187,6 +216,21 @@ class Chef
end
end
+ def initialize_copy(source)
+ super
+ @wrapped_object = safe_dup(source.wrapped_object)
+ end
+
+ private
+
+ def maybe_decorated_value(val)
+ if val.is_a?(Hash) || val.is_a?(Array)
+ new_decorator(wrapped_object: val)
+ else
+ val
+ end
+ end
+
end
end
end
diff --git a/spec/unit/node/attribute_trait/decorator_spec.rb b/spec/unit/node/attribute_trait/decorator_spec.rb
index a87109216f..3275320313 100644
--- a/spec/unit/node/attribute_trait/decorator_spec.rb
+++ b/spec/unit/node/attribute_trait/decorator_spec.rb
@@ -6,40 +6,106 @@ describe Chef::Node::AttributeTrait::Decorator do
include Chef::Node::AttributeTrait::Decorator
end
- let(:test) { Test[] }
+ context "as a Hash" do
+ let(:test) { Test[] }
- context "#to_hash" do
- it "return a real hash" do
- test['foo'] = 'bar'
- expect(test.to_hash).to be_instance_of(Hash)
+ context "Hash#any" do
+ it "yields decorated objects when nested" do
+ test['foo'] = { 'bar' => 'baz' }
+ test['bar'] = [ 1, 2 ]
+ test.any? { |k, v| expect(v).to be_instance_of(Test) }
+ end
+
+ it "yields undecorated objects when not nested" do
+ test['foo'] = 1
+ test['bar'] = "string"
+ test.any? { |k, v| expect(v).not_to be_instance_of(Test) }
+ end
end
- end
- context "#each" do
- it "yields decorated objects when nested" do
- test['foo'] = { 'bar' => 'baz' }
- test['bar'] = [ 1, 2 ]
- test.each { |k, v| expect(v).to be_instance_of(Test) }
+ context "Hash#assoc" do
+ it "returns decorated objects when nested" do
+ test['foo'] = { 'bar' => 'baz' }
+ test['bar'] = [ 1, 2 ]
+ expect(test.assoc('foo')).to be_instance_of(Test)
+ expect(test.assoc('bar')).to be_instance_of(Test)
+ end
+
+ it "yields undecorated objects when not nested" do
+ test['foo'] = 1
+ test['bar'] = "string"
+ expect(test.assoc('foo')).not_to be_instance_of(Test)
+ expect(test.assoc('bar')).not_to be_instance_of(Test)
+ end
+ end
+
+ context "Hash#delete_if" do
+ it "returns decorated objects when nested" do
+ test['foo'] = { 'bar' => 'baz' }
+ test['bar'] = [ 1, 2 ]
+ expect(test.delete('foo')).to be_instance_of(Test)
+ expect(test.delete('bar')).to be_instance_of(Test)
+ end
+
+ it "yields undecorated objects when not nested" do
+ test['foo'] = 1
+ test['bar'] = "string"
+ expect(test.delete('foo')).not_to be_instance_of(Test)
+ expect(test.delete('bar')).not_to be_instance_of(Test)
+ end
end
- it "yields undecorated objects when not nested" do
- test['foo'] = 1
- test['bar'] = "string"
- test.each { |k, v| expect(v).not_to be_instance_of(Test) }
+ context "Hash#to_hash" do
+ it "return a real hash" do
+ test['foo'] = 'bar'
+ expect(test.to_hash).to be_instance_of(Hash)
+ end
+ end
+
+ context "Hash#each" do
+ it "yields decorated objects when nested" do
+ test['foo'] = { 'bar' => 'baz' }
+ test['bar'] = [ 1, 2 ]
+ test.each { |k, v| expect(v).to be_instance_of(Test) }
+ end
+
+ it "yields undecorated objects when not nested" do
+ test['foo'] = 1
+ test['bar'] = "string"
+ test.each { |k, v| expect(v).not_to be_instance_of(Test) }
+ end
end
end
- context "Array#map" do
- it "yields decorated objects when nested" do
- test['array'] = [ { a: 'a' }, { b: 'b' } ]
- map = test['array'].map { |e| expect(e).to be_instance_of(Test) }
- expect(map[0]).to be_instance_of(Test)
+ context "as an Array" do
+ let(:test) { t = Test.new; t.wrapped_object = []; t }
+
+ context "Array#any" do
+ it "yields decorated objects when nested" do
+ test.wrapped_object = [ { a: 'a' }, { b: 'b' } ]
+ test.any? { |e| expect(e).to be_instance_of(Test) }
+ end
+
+ it "yields undecorated objects when not nested" do
+ test.wrapped_object = [ 1, true, "string" ]
+ test.any? { |e| expect(e).not_to be_instance_of(Test) }
+ end
end
- it "yields undecorated objects when not nested" do
- test['array'] = [ 1, true, "string" ]
- map = test['array'].map { |e| expect(e).not_to be_instance_of(Test) }
- expect(map[0]).not_to be_instance_of(Test)
+ context "Array#map" do
+ it "yields decorated objects when nested" do
+ test.wrapped_object = [ { a: 'a' }, { b: 'b' } ]
+ map = test.map { |e| expect(e).to be_instance_of(Test); e }
+ expect(map[0]).to be_instance_of(Test)
+ expect(map[0]).to eql({ a: 'a' })
+ end
+
+ it "yields undecorated objects when not nested" do
+ test.wrapped_object = [ 1, true, "string" ]
+ map = test.map { |e| expect(e).not_to be_instance_of(Test); e }
+ expect(map[0]).not_to be_instance_of(Test)
+ expect(map[0]).to eql(1)
+ end
end
end
end