Error sideloading JSON for act-as-taggable tags us

2019-03-27 15:25发布

问题:

In a Ruby 2/Rails 4 app, I am trying to use acts-as-taggable-on in conjunction with active_model_serializers in order to create a JSON API that would output my tags along with other model parameters.

First some background/motivation for this issue: The JSON is being fed into ember/ember-data, which as of the time of this writing has removed support for embedded records in JSON. There is a supposed fix in the documentation about this, but I find it clumsy and also haven't actually gotten it to work. Since I'm an Ember newbie and am a little more comfortable with Rails, I figure I'll try to solve the problem another way, by sideloading the tag record alongside the document record. I like this solution better because it makes more sense for my application, but I can't get this to work either.

Here's an example: Let's say I have a Document model that is using acts-as-taggable-on:

class Document < ActiveRecord::Base
  acts_as_taggable
  # other code omitted

I've set up the database with one document with one tag. Now we consider the following cases:

1. Full Object Embed: With the following serializer:

class DocumentSerializer < ActiveModel::Serializer
  attributes :id
  has_many :tags

My JSON has the following format (using Rails 4 UUIDs):

{
  "documents": [
    {
      "id": "c41460fa-2427-11e3-8702-0800270f33f4",
      "tags": [
        {
          "id": "a33fc396-2428-11e3-8eeb-0800270f33f4",
          "name": "test"
        }
      ]
    }
  ]
}

2. ID Embed: Using the serializer

class DocumentSerializer < ActiveModel::Serializer
  attributes :id
  has_many :tags, embed: :id

My JSON now looks like:

{
  "documents": [
    {
      "id": "c41460fa-2427-11e3-8702-0800270f33f4",
      "tag_ids": [
        "a33fc396-2428-11e3-8eeb-0800270f33f4"
      ]
    }
  ]
}

3. ID Embed with Tags Sideloaded: According to active_model_serializers documentation, I should be able to do

class DocumentSerializer < ActiveModel::Serializer
  attributes :id
  has_many :tags, embed: :id, include: true

but this doesn't work. Instead, I get a NoMethodError:

undefined method `object' for #<ActsAsTaggableOn::Tag:0x007f258cf1db38>

I've tried searching for this problem but haven't found anything useful so far. I also couldn't find any documentation on either gem regarding usage with the other gem. My suspicion right now that it has something to do with how acts-as-taggable-on is implemented, that it's not a straightforward has_many relationship? Is anybody able to offer some input on this issue? Thanks in advance!

回答1:

Fixed! Turns out to sideload the tags, the corresponding serializer must be defined. I did not know this, because the documentation seems to imply that having a serializer is optional, and some default will be used in the absence of one. Apparently this is not the case if you wish to use the include: true option. Key in sight came from here, thanks very much!

For completeness, I'll show what I did. I created tag_serializer.rb with the following code:

module ActsAsTaggableOn
  class TagSerializer < ActiveModel::Serializer
    attributes :id, :name
  end
end

and now my JSON:

{
   "tags": [
      {
         "id": "a33fc396-2428-11e3-8eeb-0800270f33f4",
         "name": "test"
      }
   ],
   "documents": [
      {
         "id": "c41460fa-2427-11e3-8702-0800270f33f4",
         "tag_ids": [
            "a33fc396-2428-11e3-8eeb-0800270f33f4"
         ]
      }
   ]
}