grape-roar icon indicating copy to clipboard operation
grape-roar copied to clipboard

Telling a representer to represent nil adds all of that class's methods to nil

Open j-clark opened this issue 8 years ago • 10 comments

The code causing this behavior is https://github.com/ruby-grape/grape-roar/blob/master/lib/grape/roar/representer.rb#L10

Example

user = User.find_by(email: '[email protected]')
# user => nil
present user, with: User::Representer
# User::Representer.ancestors => [..., Grape::Roar::Representer, ...]

Obviously there's a bug in this application code, but the result is that, since nil is a singleton, now every time the application asks for nil, it gets it, but with all kinds of fun stuff mixed in, like a custom #to_hash that, for instance, mucks with the instantiation of ActionController::Parameters and causes completely unrelated pages in an app to blow up

Thoughts? I'd be happy to submit a PR, but i only have a cursory understanding of all the interplay between grape, roar, grape-roar, representable, etc. and just tossing in a nil check might not be the best approach

j-clark avatar Nov 24 '15 20:11 j-clark

This is happening here, maybe we should protect the code from extending singletons, nil being one, maybe there's a better way than comparing to a list? Also is there any case where we would want to represent what would otherwise be a singleton?

dblock avatar Nov 25 '15 13:11 dblock

It appears to be a design consequence of representable using inheritance (via mixin) instead of delegation (in the style of the Presenter pattern). One option aside from checking against a list is to see if the object is dupable.

def represent(object, _options = {})
  dup = object.dup
  dup.extend self
  dup
rescue TypeError
  fail TypeError.new "Can't represent #{object.class}"
end

This would be great, because even non-singletons aren't getting polluted when they're represented and then can continue to be used in another context. But this means that we can't represent anything that's not dupable. After a quick google, integers, symbols, true, false and floats aren't dupable and therefore would not be representable. I'm not sure if this is desirable behavior for grape-roar

j-clark avatar Nov 25 '15 17:11 j-clark

Maybe we can just detect whether these are singleton types and make them non-representable?

dblock avatar Nov 25 '15 18:11 dblock

This bit us suddenly, and very strangely. We also were mistakenly calling present nil, ... in one endpoint. A later request to the same process would fail with surprising errors.

We're attempting to patch this by simply guarding against the nil case (object.extend self unless object.nil?), but as discussed above that might not be a complete fix.

joeyAghion avatar Oct 25 '17 22:10 joeyAghion

@joeyAghion, I'm sorry that this bug has affected you today! My proposed solution can be one of two things:

  • We can use a blacklist, even though it is not my preferred solution, but it is well defined and I can have it out for you quickly + later reconcile it out properly if time to fix is a problem.
  • We can instead decorate the singleton classes of the instances of the objects that we receive. Additionally, we have a nice predicate for the objects we should avoid touching.

While not immediately obvious, the magic is in doing checks like this:

nil.singleton_class == nil.class #=> true

We would avoid that object, but we wouldn't avoid this one:

irb(main):013:0> class Foo; end;
=> nil
irb(main):014:0> x = Foo.new
=> #<Foo:0x007fc0e00ff138>
irb(main):015:0> x.singleton_class == x.class
=> false

Thoughts?

mach-kernel avatar Oct 26 '17 00:10 mach-kernel

@ashkan18 was this your problem?

dblock avatar Oct 26 '17 03:10 dblock

I'm interested in this (I work with @joeyAghion here :)) - I think the next step should be to properly spec it, @mach-kernel do you think you understand this enough to code up a failing spec?

dblock avatar Oct 26 '17 03:10 dblock

Yes @dblock this was the issue in my case as well.

ashkan18 avatar Oct 26 '17 12:10 ashkan18

@dblock Yup! I'll draft up a spec for this.

mach-kernel avatar Oct 26 '17 16:10 mach-kernel

I've opened #23 with the spec so we can explore fixes.

mach-kernel avatar Oct 26 '17 17:10 mach-kernel