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

Expose different types of objects in the same array

Open waynn opened this issue 10 years ago • 8 comments

I'd like to return a JSON array that has two different types of models, each with its own Grape::Entity. So for example, I have a package, which has this

class Entity < Grape::Entity
  expose :id, :foo
  expose :image, format_with: :image_absolute_url
end

And then I have experience, which has this

  class Entity < Grape::Entity
    expose :id, :bar
    expose :image, format_with: :image_absolute_url
  end

In my API call, I create an array like this.

present [ Package.last, Experience.last ]

One way I thought of was to define a new entity, and pass it in using "with", but then I'm just repeating my previous definitions. And I also wasn't sure how to do it besides something like

class NewEntity < Grape::Entity
  expose :id, if: lambda { |object, options| object.is_a?(Package) }
  expose :bar, if: lambda { |object, options| object.is_a?(Experience) }
end

Is there a better solution for this?

waynn avatar Nov 14 '15 01:11 waynn

This isn't ideal, maybe think of what you'd like to write and we can discuss how to implement it? Other than that I strongly recommend not doing this and refactoring your entities with OO so that you don't have to do IFs here.

dblock avatar Nov 15 '15 14:11 dblock

Yeah, it's definitely clunky. So I'm trying to build a home feed for an application, but the content is going to be mixed and composed of these two types (like Yelp mixing categories and specific restaurants, or Airbnb mixing cities and specific houses). I wanted to use an array because there's a defined order to the feed. One other way is I could add a new module to both classes that implements some subset of the functionality, so that the logic gets abstracted there instead of inside the entity, though it probably makes sense to just keep it in in the entity.

Is it possible to put all that logic inside the entity? I couldn't access the instance outside of the lambda block.

Or any other suggestions more than welcome. :)

waynn avatar Nov 15 '15 19:11 waynn

I would build this with a HAL or JSON+API today (and would use grape + roar). That layout allows you to combine different types of things under _embeded in a way that doesn't mix apples and oranges.

dblock avatar Nov 17 '15 12:11 dblock

Ah, ok. One more quick follow up, that might make things a little easier. Is there a way to present different attributes all at once, instead of with an if: clause on each attribute? Something like (in a custom entity):

if object.is_a?(Category)
  expose :id, :name, :description, :foo
else
  expose :bar, :baz
end

waynn avatar Nov 17 '15 19:11 waynn

Not ATM. IMHO this should not be a requirement.

dblock avatar Nov 17 '15 22:11 dblock

google protobuff added the one of feature, https://developers.google.com/protocol-buffers/docs/proto#oneof . it's relatively easy to manipulate grape to return that kind of proto

DolevG avatar Sep 05 '18 09:09 DolevG

I would build this with a HAL or JSON+API today (and would use grape + roar)

Could someone expand on this comment a little bit? I am new to both ruby and grape and I'm struggling to present a compound document as defined by the json:api spec. The spec required that all included resources MUST be represented as an array of resource objects in a top-level included member. What is the recommended way to create the entity below?

{
  included: [
    { type: "some_type", ...},
    { type: "some_other_type", ...}
   ]
}

JeffKandel avatar Nov 29 '18 18:11 JeffKandel

I would build this with a HAL or JSON+API today (and would use grape + roar)

Could someone expand on this comment a little bit?

I was saying that I wouldn't choose Grape::Entity if I wanted something like this.

dblock avatar Nov 30 '18 14:11 dblock