Ruby
Rails
garage

Garage::Representer::NonEncodableValueが出たとき

このエラーが出たときは、だいたいGarage::Repesenter忘れなのですが、他のミスの場合もこのエラーが出るので、ググった時に引っかかるよう書いておきます。

前置き

code.rb
class User < ApplicationRecord
  include Garage::Representer
  has_many :group_user
  has_many :groups, through: :group_user

  property :id
  property :name
  property :groups
end

class Group < ApplicationRecord
  include Garage::Representer
  has_many :group_user
  has_many :users, through: :group_user

  property :id
  property :name
  property :groups
end

class UsersController < ApplicationController
  def require_resource
    @resource = User.find_by(id: params[:id])
  end
end

こんな風のコードがあるとします。

問題

Postsを取得するために GET /users/10 するとこんな感じのエラーが出ます。困った...。

Garage::Representer::NonEncodableValue in UsersController#show
Groups::ActiveRecord_Associations_CollectionProxy can not be encoded directly. Forgot to include Garage::Representer?

ハマってしまい困っていたんですが、間違いはシンプルでUserproperty :groupsが、collection :groupsになるべきでした。

おまけ

最初、挙動がよく分からずコードを追ってしまっていました。このあたりの動きはgarage/representerを読むと書いてあったので共有です。

include Garage::Representerした時にextendされるクラスメソッドがClassMethodで定義され、self.included(base)で適用されます。(これよくあるパターンぽい)

garage/representer.rb
module Garage::Representer
  def self.included(base)
    self.representers << base

    base.class_eval do
      if Rails.application
        include Rails.application.routes.url_helpers
      end
      extend ClassMethods
    end
  end

  module ClassMethods
    attr_writer :representer_attrs

    # ...

    def representer_attrs
      @representer_attrs ||= []
    end

    def property(name, options={})
      representer_attrs << Definition.new(name, options)
    end

    def collection(name, options={})
      representer_attrs << Collection.new(name, options)
    end

    # ...
  end

  # ...

  class Collection < Definition
    def encode(object, responder, selector = nil)
      value = object.send(@name)
      value.map do |item|
        encode_value(item, responder, selector)
      end
    end
  end
end

モデルのハッシュが必要になった場合、Definition#encode_valueでhashへ変換作業をしているのですが、ここでコケていたという雰囲気です。

Collectionの場合はmapしながらhashへの変換をしていくので、ActiveRecord_Associations_CollectionProxyでも問題ないですね。