Allow serialized ID to be overwritten for belongs-to relationships

If the `id` attribute for a class isn't taken directly from the object when
serializing it, it may be desirible for other classes that serialize a
relationship with that class to overwrite the relationship IDs they serialize.

For example, suppose we have:

```(ruby)
class Repo < Model
  attributes :id, :github_id, :name
  associations :configs
end

class Config < Model
  attributes :id
  belongs_to :repo
end

class RepoSerializer < ActiveModel::Serializer
  attributes :id, :name

  has_many :update_configs

  def id
    object.github_id
  end
end

class ConfigSerializer < ActiveModel::Serializer
  attributes :id
  belongs_to :repo
end
```

In the above example, serializing a list of `Repo`s will give the `github_id`
for each one, but serializing a `Config` will give the `id` for its parent repo.

Ideally AMS would inspect the `RepoSerializer` when serializing the `Config`,
and realise it can't just output the foreign key. Unfortunately, getting the
serialization class for the child repo currently requires loading the record
(via evaluating `lazy_assocation`), and loses the performance benefit of the
existing `belongs_to?` path. Instead, I've opted to use
`read_attribute_for_serialization` instead of `object.send` to fetch the
serialized foreign key. This allows the serialized relationship ID to be
overwritten using

```(ruby)
class ConfigSerializer < ActiveModel::Serializer
  ...

  def repo_id
    object.repo.github_id
  end
end
```
This commit is contained in:
Grey Baker
2017-05-10 00:21:56 +01:00
parent b48aeeef1e
commit be7ee70376
3 changed files with 32 additions and 1 deletions

View File

@@ -165,6 +165,36 @@ module ActiveModel
assert_equal expected, actual
end
class ExternalBlog < Blog
attributes :external_id
end
class BelongsToExternalBlogModel < ::Model
attributes :id, :title, :external_blog_id
associations :external_blog
end
class BelongsToExternalBlogModelSerializer < ActiveModel::Serializer
type :posts
belongs_to :external_blog
def external_blog_id
object.external_blog.external_id
end
end
def test_belongs_to_allows_id_overwriting
attributes = {
id: 1,
title: 'Title',
external_blog: ExternalBlog.new(id: 5, external_id: 6)
}
post = BelongsToExternalBlogModel.new(attributes)
actual = serializable(post, adapter: :json_api, serializer: BelongsToExternalBlogModelSerializer).as_json
expected = { data: { id: '1', type: 'posts', relationships: { :'external-blog' => { data: { id: '6', type: 'external-blogs' } } } } }
assert_equal expected, actual
end
class InlineAssociationTestPostSerializer < ActiveModel::Serializer
has_many :comments
has_many :comments, key: :last_comments do