Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Evaluate relationship blocks in Resource context #51

Open
richmolj opened this issue Jan 3, 2017 · 8 comments
Open

Evaluate relationship blocks in Resource context #51

richmolj opened this issue Jan 3, 2017 · 8 comments

Comments

@richmolj
Copy link
Contributor

richmolj commented Jan 3, 2017

Consider a serializer:

class SerializableEmployee < JSONAPI::Serializable::Resource
  attribute :title do
    primary_office.title
  end

  has_one :primary_office do
    data { primary_office }
  end

  private

  def primary_office
    @object.offices.sort_by(&:rank).first
  end
end

The above code will not work, because the has_one data block will eval in the context of a Relationship instance (ie self is_a JSONAPI::Serializable::Relationship). This means I cannot access other methods within the serializable (ie primary_office).

It's confusing, as we would have access to @model and other blocks (like the attribute block above), do eval in the context of the serializable.

I'd prefer this logic live in the serializable, not the model, as it's serialization-specific (For instance, a primary_office method on the resource may fire a query instead of sorting already-loaded objects).

@beauby
Copy link
Member

beauby commented Jan 4, 2017

You are right. The way things work currently is that relationship blocks are evald in a specific context in which exposures are forwarded.
A quick workaround for your situation would be to move the method definition inside the has_one block.
I am not sure what the best tradeoff is here.

@richmolj
Copy link
Contributor Author

richmolj commented Jan 4, 2017

@beauby the method needs to be shared with the rest of the serializable, though, so I'm not sure that's a DRY solution. Is there a downside to evaling these blocks in the serializable context?

@andreaseger
Copy link

IMHO if it's eval'd in the resource context there needs to be another variable to access the relationship object

@beauby
Copy link
Member

beauby commented Jul 12, 2017

So one possible solution to your problem @richmolj would be to forward to base object upon missing method. What do you think?

@richmolj
Copy link
Contributor Author

Sounds like a performance issue?

@beauby
Copy link
Member

beauby commented Jul 12, 2017

I doubt it would be a bottleneck but I'll try to hack something together and we'll benchmark it.

@aprescott
Copy link

I'm experiencing this same problem with a serializer I'm trying to define. I'm not too familiar with the jsonapi-rb internals, but I have a couple of ideas for tackling this.

If method_missing is too much of a concern, one idea could be to provide a way of accessing the resource object from within the relationship context.

attribute :title do
  primary_office.title
end

has_one :primary_office do
  # `resource` could instead be `@resource`, but that would conflict with exposures
  data { resource.primary_office }
end

private

def primary_office
  @object.offices.sort_by(&:rank).first
end

Another idea might be to fully scope helper methods within a defined abstraction. That would force attribute and relationships to use the same interface:

attribute :title do
  helpers.primary_office.title
end

has_one :primary_office do
  data { helpers.primary_office }
end

helpers do
  def primary_office
    @object.offices.sort_by(&:rank).first
  end
end

Maybe an arbitrary def foo outside of helpers (at the same level as attribute do ... end) is unavailable within blocks in this case?

Depending on how complicated this gets with all the overlapping contexts and generic instance variables, methods defined in helpers could be required to take parameters to enforce structure:

attribute :title do
  helpers.primary_office(@object)
end

helpers do
  def primary_office(employee)
    employee.offices.sort_by(&:rank).first
  end
end

@aprescott
Copy link

Related to this problem: link has the same issue:

link :related do
  # unable to reference methods defined at the serializer level
end

I'm not sure there's a work-around either. def foo won't quite work the same way because there's no nested DSL form for link.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants