diff --git a/CHANGELOG.md b/CHANGELOG.md index 98b796c..6568d3d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ ### Next Release +* [#406](https://github.com/ruby-grape/grape-entity/pull/406): Handle symbol-to-proc wrappers (`&:method_name`) where the method uses `delegate` or `method_missing` - [@marcrohloff](https://github.com/marcrohloff). + #### Features * Your contribution here. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6023a84..655e94e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -44,7 +44,7 @@ Implement your feature or bug fix. Ruby style is enforced with [Rubocop](https://github.com/bbatsov/rubocop), run `bundle exec rubocop` and fix any style issues highlighted. -Make sure that `bundle exec rake` completes without errors. +Make sure that `bundle exec rake` and `rubocop` completes without errors. #### Write Documentation diff --git a/lib/grape_entity/entity.rb b/lib/grape_entity/entity.rb index cd14033..1ca44ef 100644 --- a/lib/grape_entity/entity.rb +++ b/lib/grape_entity/entity.rb @@ -548,7 +548,8 @@ def ensure_block_arity!(block) end arity = object.method(origin_method_name).arity - return if arity.zero? + # functions defined using `delegate` or `method_missing` have an arity of -1 + return if arity <= 0 raise ArgumentError, <<~MSG Cannot use `&:#{origin_method_name}` because that method expects #{arity} argument#{'s' if arity != 1}. diff --git a/spec/grape_entity/entity_spec.rb b/spec/grape_entity/entity_spec.rb index 5bdd73e..527aa5b 100644 --- a/spec/grape_entity/entity_spec.rb +++ b/spec/grape_entity/entity_spec.rb @@ -400,6 +400,14 @@ class BogusEntity < Grape::Entity describe 'blocks' do class SomeObject + class SomeObjectDelegate + def method_using_delegation + 'delegated-result' + end + end + + delegate :method_using_delegation, to: :delegate_object + def method_without_args 'result' end @@ -415,6 +423,23 @@ def method_with_multiple_args(_object, _options) def raises_argument_error raise ArgumentError, 'something different' end + + def method_missing(method, ...) + return 'missing-result' if method.to_sym == :method_using_missing + + super + end + + def delegate_object + @delegate_object ||= SomeObjectDelegate.new + end + + private + + def respond_to_missing?(method, include_private = false) # rubocop:disable Style/OptionalBooleanParameter + method.to_sym == :method_using_missing || + super + end end describe 'with block passed in' do @@ -459,6 +484,28 @@ def raises_argument_error end end + context 'with block passed in via & that uses `missing_method`' do + specify do + subject.expose :using_missing, &:method_using_missing + + object = SomeObject.new + expect(object.method(:method_using_missing).arity).to eq(-1) + value = subject.represent(object).value_for(:using_missing) + expect(value).to eq('missing-result') + end + end + + context 'with block passed in via & that uses `delegate`' do + specify do + subject.expose :using_delegation, &:method_using_delegation + + object = SomeObject.new + expect(object.method(:method_using_delegation).arity).to eq(-1) + value = subject.represent(object).value_for(:using_delegation) + expect(value).to eq('delegated-result') + end + end + context 'with block passed in via &' do specify do subject.expose :that_method_with_one_arg, &:method_with_one_arg