diff options
author | Lamont Granquist <lamont@scriptkiddie.org> | 2015-10-19 09:52:13 -0700 |
---|---|---|
committer | Lamont Granquist <lamont@scriptkiddie.org> | 2016-01-07 11:24:28 -0800 |
commit | 21770dcdb3f7a62a57c2019129522a73d63d5168 (patch) | |
tree | a19fb5e88e4fb41cb60e8cabdbd4d15b9c1a45c0 | |
parent | 7637515eb55dd982d6192d055af7e746cc61f0ae (diff) | |
download | chef-21770dcdb3f7a62a57c2019129522a73d63d5168.tar.gz |
more tests
-rw-r--r-- | lib/chef/node/attribute_trait/decorator.rb | 124 | ||||
-rw-r--r-- | spec/unit/node/attribute_trait/decorator_spec.rb | 114 |
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 |