Skip to content

Evaluate relationship blocks in Resource context #51

Open

Description

@richmolj

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).

Activity

  1. beauby commented on Jan 4, 2017

    @beauby
    Member

    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.

  2. richmolj commented on Jan 4, 2017

    @richmolj
    ContributorAuthor

    @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?

  3. andreaseger commented on Jan 19, 2017

    @andreaseger

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

  4. beauby commented on Jul 12, 2017

    @beauby
    Member

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

  5. richmolj commented on Jul 12, 2017

    @richmolj
    ContributorAuthor

    Sounds like a performance issue?

  6. beauby commented on Jul 12, 2017

    @beauby
    Member

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

  7. aprescott commented on Aug 30, 2018

    @aprescott

    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
  8. aprescott commented on Aug 30, 2018

    @aprescott

    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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions