diff --git a/CHANGELOG.md b/CHANGELOG.md index 63021438..ac73d919 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ Next Release * [#16](https://github.com/intridea/grape-entity/pull/16): Add `using` option to `expose SYMBOL BLOCK` - [@fahchen](https://github.com/fahchen). * [#24](https://github.com/intridea/grape-entity/pull/24): Return documentation with `as` param considered - [@drakula2k](https://github.com/drakula2k). * [#27](https://github.com/intridea/grape-entity/pull/27): Properly serializing hashes - [@clintonb](https://github.com/clintonb). +* [#28](https://github.com/intridea/grape-entity/pull/28): Look for method on entity before calling on the object - [@MichaelXavier](https://github.com/MichaelXavier). * Your contribution here. 0.3.0 (2013-03-29) diff --git a/README.md b/README.md index 95fa6e68..36375a41 100644 --- a/README.md +++ b/README.md @@ -103,6 +103,21 @@ end expose :digest, proc: ... # equivalent to a block ``` +You can also define a method on the entity and it will try that before trying +on the object the entity wraps. + +``` +class ExampleEntity < Grape::Entity + expose :attr_not_on_wrapped_object + # ... +private + + def attr_not_on_wrapped_object + 42 + end +end +``` + #### Aliases Expose under a different name with `:as`. diff --git a/lib/grape_entity/entity.rb b/lib/grape_entity/entity.rb index ed7cc12d..56259537 100644 --- a/lib/grape_entity/entity.rb +++ b/lib/grape_entity/entity.rb @@ -390,17 +390,25 @@ def value_for(attribute, options = {}) using_options = options.dup using_options.delete(:collection) using_options[:root] = nil - exposure_options[:using].represent(object.send(attribute), using_options) + exposure_options[:using].represent(delegate_attribute(attribute), using_options) elsif exposure_options[:format_with] format_with = exposure_options[:format_with] if format_with.is_a?(Symbol) && formatters[format_with] - formatters[format_with].call(object.send(attribute)) + formatters[format_with].call(delegate_attribute(attribute)) elsif format_with.is_a?(Symbol) - self.send(format_with, object.send(attribute)) + self.send(format_with, delegate_attribute(attribute)) elsif format_with.respond_to? :call - format_with.call(object.send(attribute)) + format_with.call(delegate_attribute(attribute)) end + else + delegate_attribute(attribute) + end + end + + def delegate_attribute(attribute) + if respond_to?(attribute, true) + send(attribute) else object.send(attribute) end diff --git a/spec/grape_entity/entity_spec.rb b/spec/grape_entity/entity_spec.rb index 43ee3a14..449edacf 100644 --- a/spec/grape_entity/entity_spec.rb +++ b/spec/grape_entity/entity_spec.rb @@ -602,6 +602,26 @@ class FriendEntity < Grape::Entity it 'returns a formatted value if format_with is passed a lambda' do subject.send(:value_for, :fantasies).should == ['Nessy', 'Double Rainbows', 'Unicorns'] end + + it "tries instance methods on the entity first" do + module EntitySpec + class DelegatingEntity < Grape::Entity + root 'friends', 'friend' + expose :name + expose :email + + private + def name + "cooler name" + end + end + end + + friend = double("Friend", :name => "joe", :email => "joe@example.com") + rep = EntitySpec::DelegatingEntity.new(friend) + rep.send(:value_for, :name).should == "cooler name" + rep.send(:value_for, :email).should == "joe@example.com" + end end describe '#documentation' do