active_model_serializers_pg 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 749a92cbe1a5ef80dbfcf46cda82127108e0497465258e19a2023e0847b9696d
4
- data.tar.gz: 32956f1353aa108fd173d02b2da79391ba45c286efc1e6080bb4e4927f630bf2
3
+ metadata.gz: 4bdfe8c6e1f9a0fbca6e444a7594474d43d9415008c86f6fe30964088fc8c1f7
4
+ data.tar.gz: 0e5a9f884cda2663bf2f36051ad42367381ea5f61798048cd4ae774c19c76265
5
5
  SHA512:
6
- metadata.gz: f9861a254e392c60be71bb396497e2f9fb226c6c9b1054635d1bd6dcb3bbd04992e5126d084a2f80d277c1d78603e9f82fb9eb166f6d545c373576eefa8d7fc7
7
- data.tar.gz: ac39133be71300ecea935c95f090e2282b48bd55cd00431e905e04cf32974efec50e8fa34f6b2b4ec10652ae24201eb33c55b7c0c6ef42d8731a35144d761f42
6
+ metadata.gz: 44df1b79df5306e323a62c6fd0a6d0b52e6f2da6da8d6a6eb494fb440a9e09909ecd476cf563fa873a42bddedc40dbdc0efefb95cf13982ca69684b11c93050d
7
+ data.tar.gz: d08476c02adb38cb0978910b0d2448c0f09019ba52592132105612846e51d883d5ab0f26c6ad0f3eaf66c3757f3a93ab59831bfb6652459d47cdf8de37ba342d
data/README.md CHANGED
@@ -6,7 +6,7 @@ This gem adds support for Rails 5 and AMS 0.10.
6
6
  In addition we provide output in [JSON:API](https://jsonapi.org/) format.
7
7
  (I'd like to add normal JSON output too, so let me know if that would be helpful to you.)
8
8
 
9
- Building your JSON is Postgres can reduce your response time 10-100x.
9
+ Building your JSON in Postgres can reduce your response time 10 – 100x.
10
10
  You skip instantiating thousands of Ruby objects (and garbage collecting them later),
11
11
  and Postgres can generate the JSON far more quickly than AMS.
12
12
 
@@ -45,6 +45,16 @@ You could also turn it on for everything but then set `adapter: :json_api` for a
45
45
 
46
46
  Note this gem also respects `ActiveModelSerializers.config.key_transform = :dash`, if you are using that.
47
47
 
48
+ ### Supports
49
+
50
+ Here are some other details we support:
51
+
52
+ - `belongs_to`, `has_one`, and `has_many` associations.
53
+ - If you serialize an `enum` you get the string values, not integers.
54
+ - You can serialize an `alias`'d association.
55
+ - We preserve SQL ordering from a model's `default_scope`.
56
+ - We preserve SQL ordering attached to an association.
57
+
48
58
  ### Methods in Serializers and Models
49
59
 
50
60
  If you are using methods to compute properties for your JSON responses
@@ -68,6 +78,26 @@ There is no instance of MyModel created so sql computed properties needs to be
68
78
  a class method. Right now, this string is used as a SQL literal, so be sure to
69
79
  *not* use untrusted values in the return value.
70
80
 
81
+ Similarly we also look for a `foo__sql` method
82
+ for relationships that aren't ActiveRecord associations.
83
+ It must return an `ActiveRecord::Relation` (not a `String`),
84
+ and we will run its SQL inside a `LEFT OUTER JOIN LATERAL`
85
+ (so it has access to the parent table). For example:
86
+
87
+ ```ruby
88
+ class Book < ActiveRecord::Base
89
+
90
+ def essays_by_same_author
91
+ Essay.where(author_id: author_id)
92
+ end
93
+
94
+ def self.essays_by_same_author__sql
95
+ Essay.where("books.author_id = essays.author_id")
96
+ end
97
+
98
+ end
99
+ ```
100
+
71
101
  ## Developing
72
102
 
73
103
  To work on active\_model\_serializers\_pg locally, follow these steps:
@@ -89,6 +119,18 @@ Commands for building/releasing/installing:
89
119
  * `rake install`
90
120
  * `rake release`
91
121
 
122
+ ### TODO
123
+
124
+ Here are things I'd like to support but don't yet:
125
+
126
+ - Use Arel to generate all the SQL.
127
+ - More support of custom scopes attached to associations.
128
+ - Add a non-JSON:API adapter, for traditional JSON output.
129
+ - Have all the tests verify they output the asme JSON as the built-in AMS serializers.
130
+ - Look at AMS's own tests for more features we should support.
131
+ - HABTM associations?
132
+ - `has_many through:` associations?
133
+
92
134
  ## Authors
93
135
 
94
136
  Paul Jungwirth
data/Rakefile CHANGED
@@ -82,6 +82,14 @@ namespace :db do
82
82
  ActiveRecord::Base.connection.create_table :notes, force: true do |t|
83
83
  t.string "name"
84
84
  t.string "content"
85
+ t.integer "state", null: false, default: 0
86
+ t.datetime "created_at"
87
+ t.datetime "updated_at"
88
+ end
89
+
90
+ ActiveRecord::Base.connection.create_table :long_notes, force: true do |t|
91
+ t.string "name"
92
+ t.text "long_content"
85
93
  t.datetime "created_at"
86
94
  t.datetime "updated_at"
87
95
  end
@@ -94,6 +102,14 @@ namespace :db do
94
102
  t.datetime "updated_at"
95
103
  end
96
104
 
105
+ ActiveRecord::Base.connection.create_table :long_tags, force: true do |t|
106
+ t.integer "long_note_id"
107
+ t.string "long_name"
108
+ t.boolean "popular"
109
+ t.datetime "created_at"
110
+ t.datetime "updated_at"
111
+ end
112
+
97
113
  ActiveRecord::Base.connection.create_table :offers, force: true do |t|
98
114
  t.integer "created_by_id"
99
115
  t.integer "reviewed_by_id"
@@ -1,10 +1,13 @@
1
1
  require 'active_model_serializers'
2
2
 
3
+ # We don't really need this
4
+ # because we try to inject our own CollectionSerializer,
5
+ # but keeping it lets us give a nice warning message
6
+ # instead of simply crashing:
3
7
  module ActiveModel
4
8
  class Serializer
5
9
  class CollectionSerializer
6
10
  def element_serializer
7
- # TODO: This is probably not set every time
8
11
  options[:serializer]
9
12
  end
10
13
  end
@@ -56,6 +59,15 @@ module ActiveModelSerializers
56
59
  sql
57
60
  end
58
61
 
62
+ def self.warn_about_collection_serializer
63
+ msg = "You are using an ordinary AMS CollectionSerializer with the json_api_pg adapter, which probably means Rails is pointlessly loading all your ActiveRecord instances *and* running the build JSON-building query in Postgres."
64
+ if Object.const_defined? 'Rails'
65
+ Rails.logger.warn msg
66
+ else
67
+ puts "WARN: #{msg}"
68
+ end
69
+ end
70
+
59
71
  end
60
72
  end
61
73
  end
@@ -317,12 +329,15 @@ class JsonApiPgSql
317
329
  # Make a JsonThing for everything,
318
330
  # cached as the full_name:
319
331
 
320
- # User.where is a Relation, but plain User is not:
332
+ # Watch out: User.where is a Relation, but plain User is not:
321
333
  ar_class = ActiveRecord::Relation === base_relation ? base_relation.klass : base_relation
322
334
 
323
335
  case base_serializer
324
336
  when ActiveModel::Serializer::CollectionSerializer
325
- # base_serializer = base_serializer.to_a.first
337
+ ActiveModelSerializers::Adapter::JsonApiPg.warn_about_collection_serializer
338
+ base_serializer = base_serializer.element_serializer
339
+ @many = true
340
+ when ActiveModelSerializersPg::CollectionSerializer
326
341
  base_serializer = base_serializer.element_serializer
327
342
  @many = true
328
343
  else
@@ -1 +1,6 @@
1
1
  require 'active_model_serializers/adapter/json_api_pg'
2
+ require 'active_model_serializers_pg/collection_serializer'
3
+
4
+ # We have to inject our own CollectionSerializer to avoid materializing ActiveRecord::Relations.
5
+ # See more detailed notes in our class:
6
+ ActiveModel::Serializer.config.collection_serializer = ActiveModelSerializersPg::CollectionSerializer
@@ -0,0 +1,117 @@
1
+ # This is a near-verbatim copy of ActiveModel::Serializer::CollectionSerializer,
2
+ # but we patch the `initialize` method to avoid loading the ActiveRecord::Relation.
3
+ # It's still possible to load it if you call `each`, but we do it lazily.
4
+ # This is based on AMS 0.10.8. For each of updates we mark each of our changes
5
+ # with a `PATCHED` comment.
6
+ #
7
+ # It would be nicer to keep this class mostly empty
8
+ # and just delegate to an ActiveModel::Serializer::CollectionSerializer instance when needed,
9
+ # but then we'd still have to instantiate it eventually,
10
+ # any time we proxy a call (not just each),
11
+ # and that materializes the Relation.
12
+ # TODO: Is it possible to replace its `initialize` class? Or is that too wild even for Ruby?
13
+ module ActiveModelSerializersPg
14
+ class CollectionSerializer
15
+ include Enumerable
16
+ # PATCHED: implement this below so we don't need @serializers
17
+ # delegate :each, to: :@serializers
18
+
19
+ attr_reader :object, :root
20
+
21
+ def initialize(resources, options = {})
22
+ @object = resources
23
+ @options = options
24
+ @root = options[:root]
25
+ # PATCHED: We don't want to iterate unless we have to:
26
+ # @serializers = serializers_from_resources
27
+ end
28
+
29
+ # PATCH: Give ourselves access to the serializer for the individual elements:
30
+ def element_serializer
31
+ options[:serializer]
32
+ end
33
+
34
+ def success?
35
+ true
36
+ end
37
+
38
+ # @api private
39
+ def serializable_hash(adapter_options, options, adapter_instance)
40
+ options[:include_directive] ||= ActiveModel::Serializer.include_directive_from_options(adapter_options)
41
+ options[:cached_attributes] ||= ActiveModel::Serializer.cache_read_multi(self, adapter_instance, options[:include_directive])
42
+ serializers.map do |serializer|
43
+ serializer.serializable_hash(adapter_options, options, adapter_instance)
44
+ end
45
+ end
46
+
47
+ # TODO: unify naming of root, json_key, and _type. Right now, a serializer's
48
+ # json_key comes from the root option or the object's model name, by default.
49
+ # But, if a dev defines a custom `json_key` method with an explicit value,
50
+ # we have no simple way to know that it is safe to call that instance method.
51
+ # (which is really a class property at this point, anyhow).
52
+ # rubocop:disable Metrics/CyclomaticComplexity
53
+ # Disabling cop since it's good to highlight the complexity of this method by
54
+ # including all the logic right here.
55
+ def json_key
56
+ return root if root
57
+ # 1. get from options[:serializer] for empty resource collection
58
+ key = object.empty? &&
59
+ (explicit_serializer_class = options[:serializer]) &&
60
+ explicit_serializer_class._type
61
+ # 2. get from first serializer instance in collection
62
+ key ||= (serializer = serializers.first) && serializer.json_key
63
+ # 3. get from collection name, if a named collection
64
+ key ||= object.respond_to?(:name) ? object.name && object.name.underscore : nil
65
+ # 4. key may be nil for empty collection and no serializer option
66
+ key &&= key.pluralize
67
+ # 5. fail if the key cannot be determined
68
+ key || fail(ArgumentError, 'Cannot infer root key from collection type. Please specify the root or each_serializer option, or render a JSON String')
69
+ end
70
+ # rubocop:enable Metrics/CyclomaticComplexity
71
+
72
+ def paginated?
73
+ ActiveModelSerializers.config.jsonapi_pagination_links_enabled &&
74
+ object.respond_to?(:current_page) &&
75
+ object.respond_to?(:total_pages) &&
76
+ object.respond_to?(:size)
77
+ end
78
+
79
+ # PATCHED: Add a replacement to `each` so we don't change the interface:
80
+ def each
81
+ Rails.logger.debug caller.join("\n")
82
+ Enumerator.new do |y|
83
+ serializers_from_resources.each do |ser|
84
+ y.yield ser
85
+ end
86
+ end
87
+ end
88
+
89
+ protected
90
+
91
+ attr_reader :serializers, :options
92
+
93
+ private
94
+
95
+ def serializers_from_resources
96
+ puts "options here"
97
+ pp options
98
+ serializer_context_class = options.fetch(:serializer_context_class, ActiveModel::Serializer)
99
+ object.map do |resource|
100
+ serializer_from_resource(resource, serializer_context_class, options)
101
+ end
102
+ end
103
+
104
+ def serializer_from_resource(resource, serializer_context_class, options)
105
+ serializer_class = options.fetch(:serializer) do
106
+ serializer_context_class.serializer_for(resource, namespace: options[:namespace])
107
+ end
108
+
109
+ if serializer_class.nil?
110
+ ActiveModelSerializers.logger.debug "No serializer found for resource: #{resource.inspect}"
111
+ throw :no_serializer
112
+ else
113
+ serializer_class.new(resource, options.except(:serializer))
114
+ end
115
+ end
116
+ end
117
+ end
@@ -1,3 +1,3 @@
1
1
  module ActiveModelSerializersPg
2
- VERSION = '0.0.1'
2
+ VERSION = '0.0.2'
3
3
  end
@@ -134,6 +134,68 @@ describe 'ArraySerializer' do
134
134
  }.to_json
135
135
  expect(json_data).to eq json_expected
136
136
  end
137
+
138
+ end
139
+
140
+ context 'with dasherized keys and types' do
141
+ let(:relation) { LongNote.where(name: 'Title').first }
142
+ let(:controller) { LongNotesController.new }
143
+ let(:options) { { include: ['long_tags'] } }
144
+
145
+ before do
146
+ @note = LongNote.create long_content: 'Test', name: 'Title'
147
+ @tag = LongTag.create long_name: 'My tag', long_note: @note, popular: true
148
+ @old_key_setting = ActiveModelSerializers.config.key_transform
149
+ ActiveModelSerializers.config.key_transform = :dash
150
+ end
151
+
152
+ after do
153
+ ActiveModelSerializers.config.key_transform = @old_key_setting
154
+ end
155
+
156
+ it 'generates the proper json output' do
157
+ json_expected = {
158
+ data: {
159
+ id: @note.id.to_s,
160
+ type: 'long-notes',
161
+ attributes: { name: 'Title', 'long-content' => 'Test' },
162
+ relationships: { 'long-tags': { data: [{id: @tag.id.to_s, type: 'long-tags'}] } }
163
+ },
164
+ included: [
165
+ {
166
+ id: @tag.id.to_s,
167
+ type: 'long-tags',
168
+ attributes: { 'long-name' => 'My tag' },
169
+ relationships: { 'long-note' => { data: { id: @note.id.to_s, type: 'long-notes' } } },
170
+ }
171
+ ]
172
+ }.to_json
173
+ expect(json_data).to eq json_expected
174
+ end
175
+ end
176
+
177
+ context 'with aliased association' do
178
+ let(:relation) { Tag.first }
179
+ let(:controller) { TagsController.new }
180
+ let(:options) { { serializer: TagWithAliasedNoteSerializer } }
181
+
182
+ before do
183
+ @note = Note.create content: 'Test', name: 'Title'
184
+ @tag = Tag.create name: 'My tag', note: @note, popular: true
185
+ end
186
+
187
+ it 'generates the proper json output' do
188
+ json_expected = {
189
+ data: {
190
+ id: @tag.id.to_s,
191
+ type: 'tags',
192
+ attributes: { name: 'My tag' },
193
+ relationships: { aliased_note: { data: {id: @note.id.to_s, type: 'notes'} } }
194
+ }
195
+ }.to_json
196
+ expect(json_data).to eq json_expected
197
+ end
198
+
137
199
  end
138
200
 
139
201
  context 'serialize single record with custom serializer' do
@@ -241,7 +303,21 @@ describe 'ArraySerializer' do
241
303
  end
242
304
 
243
305
  it 'does not instantiate ruby objects for relations' do
244
- expect(relation).not_to receive(:to_a)
306
+ # It would be nice to do this instead:
307
+ #
308
+ # expect(relation).not_to receive(:load)
309
+ #
310
+ # or
311
+ #
312
+ # expect(relation).not_to receive(:records)
313
+ #
314
+ # But that causes an infinite loop because rspec works by raising an exception,
315
+ # and ActiveRecord notices that and wants to print an error message,
316
+ # calling `inspect` on the Relation, which tries to load the relation, which....
317
+ # The test still fails, but not in an articulate or informative way.
318
+ # TODO: We could probably write our own customer matcher to work around that,
319
+ # but this does the job for now:
320
+ expect(ActiveModelSerializers::Adapter::JsonApiPg).not_to receive(:warn_about_collection_serializer)
245
321
  json_data
246
322
  end
247
323
  end
@@ -273,7 +349,7 @@ describe 'ArraySerializer' do
273
349
  end
274
350
 
275
351
  it 'does not instantiate ruby objects for relations' do
276
- expect(relation).not_to receive(:to_a)
352
+ expect(ActiveModelSerializers::Adapter::JsonApiPg).not_to receive(:warn_about_collection_serializer)
277
353
  json_data
278
354
  end
279
355
  end
@@ -307,12 +383,11 @@ describe 'ArraySerializer' do
307
383
  end
308
384
 
309
385
  it 'generates the proper json output for the serializer' do
310
- puts json_data
311
386
  expect(json_data).to eq @json_expected
312
387
  end
313
388
 
314
389
  it 'does not instantiate ruby objects for relations' do
315
- expect(relation).not_to receive(:to_a)
390
+ expect(ActiveModelSerializers::Adapter::JsonApiPg).not_to receive(:warn_about_collection_serializer)
316
391
  json_data
317
392
  end
318
393
  end
@@ -354,7 +429,7 @@ describe 'ArraySerializer' do
354
429
  end
355
430
 
356
431
  it 'does not instantiate ruby objects for relations' do
357
- expect(relation).not_to receive(:to_a)
432
+ expect(ActiveModelSerializers::Adapter::JsonApiPg).not_to receive(:warn_about_collection_serializer)
358
433
  json_data
359
434
  end
360
435
  end
@@ -373,7 +448,7 @@ describe 'ArraySerializer' do
373
448
  end
374
449
 
375
450
  it 'does not instantiate ruby objects for relations' do
376
- expect(relation).not_to receive(:to_a)
451
+ expect(ActiveModelSerializers::Adapter::JsonApiPg).not_to receive(:warn_about_collection_serializer)
377
452
  json_data
378
453
  end
379
454
  end
@@ -402,11 +477,40 @@ describe 'ArraySerializer' do
402
477
  end
403
478
 
404
479
  it 'does not instantiate ruby objects for relations' do
405
- expect(relation).not_to receive(:to_a)
480
+ expect(ActiveModelSerializers::Adapter::JsonApiPg).not_to receive(:warn_about_collection_serializer)
406
481
  json_data
407
482
  end
408
483
  end
409
484
 
485
+ context 'with enums' do
486
+ let(:relation) { Note.order(:id) }
487
+ let(:controller) { NotesController.new }
488
+ let(:options) { { each_serializer: NoteWithStateSerializer, fields: { note: [:name, :state] } } }
489
+
490
+ before do
491
+ @note1 = Note.create content: 'Test', name: 'Title 1', state: Note::Published
492
+ @note2 = Note.create content: 'Test', name: 'Title 2', state: Note::Deleted
493
+ end
494
+
495
+ it 'converts enum ints to strings' do
496
+ json_expected = {
497
+ data: [
498
+ {
499
+ id: @note1.id.to_s,
500
+ type: 'notes',
501
+ attributes: { name: 'Title 1', state: 'published' },
502
+ },
503
+ {
504
+ id: @note2.id.to_s,
505
+ type: 'notes',
506
+ attributes: { name: 'Title 2', state: 'deleted' },
507
+ }
508
+ ]
509
+ }.to_json
510
+ expect(json_data).to eq json_expected
511
+ end
512
+ end
513
+
410
514
  context 'support for include_[attrbute]' do
411
515
  let(:relation) { User.all }
412
516
  let(:controller) { UsersController.new }
@@ -565,15 +669,7 @@ describe 'ArraySerializer' do
565
669
 
566
670
  pending 'obeys serializer option in has_many relationship' # Does AMS 0.10 still support this?
567
671
 
568
- pending 'obeys overall :include option'
569
-
570
672
  pending 'obeys :include option in serializer association' # Does AMS 0.10 still support this?
571
673
 
572
- pending 'figures out aliased associations'
573
-
574
- pending 'makes dasherized keys and types'
575
-
576
- pending 'serializes enums'
577
-
578
674
  pending 'uses __sql methods for relationships'
579
675
  end
data/spec/spec_helper.rb CHANGED
@@ -61,6 +61,10 @@ class Note < ActiveRecord::Base
61
61
  has_many :sorted_tags
62
62
  has_many :custom_sorted_tags, lambda { order(:name) }, class_name: 'Tag'
63
63
  has_many :popular_tags, lambda { where(popular: true) }, class_name: 'Tag'
64
+ Draft = :draft
65
+ Published = :published
66
+ Deleted = :deleted
67
+ enum state: [Draft, Published, Deleted]
64
68
  end
65
69
 
66
70
  class NotesController < TestController; end
@@ -70,6 +74,22 @@ class NoteSerializer < ActiveModel::Serializer
70
74
  has_many :tags
71
75
  end
72
76
 
77
+ class NoteWithStateSerializer < ActiveModel::Serializer
78
+ attributes :content, :name, :state
79
+ has_many :tags
80
+ end
81
+
82
+ class LongNote < ActiveRecord::Base
83
+ has_many :long_tags
84
+ end
85
+
86
+ class LongNotesController < TestController; end
87
+
88
+ class LongNoteSerializer < ActiveModel::Serializer
89
+ attributes :long_content, :name
90
+ has_many :long_tags
91
+ end
92
+
73
93
  class ShortTagSerializer < ActiveModel::Serializer
74
94
  attributes :id, :name
75
95
  end
@@ -105,6 +125,7 @@ end
105
125
 
106
126
  class Tag < ActiveRecord::Base
107
127
  belongs_to :note
128
+ alias :aliased_note :note
108
129
  end
109
130
 
110
131
  class SortedTag < Tag
@@ -112,6 +133,15 @@ class SortedTag < Tag
112
133
  default_scope { order(:name) }
113
134
  end
114
135
 
136
+ class LongTag < ActiveRecord::Base
137
+ belongs_to :long_note
138
+ end
139
+
140
+ class LongTagSerializer < ActiveModel::Serializer
141
+ attributes :long_name
142
+ belongs_to :long_note
143
+ end
144
+
115
145
  class TagWithNote < Tag
116
146
  belongs_to :note
117
147
  default_scope { joins(:note) }
@@ -129,6 +159,11 @@ class TagWithNoteSerializer < ActiveModel::Serializer
129
159
  has_one :note
130
160
  end
131
161
 
162
+ class TagWithAliasedNoteSerializer < ActiveModel::Serializer
163
+ attributes :name
164
+ has_one :aliased_note
165
+ end
166
+
132
167
  class User < ActiveRecord::Base
133
168
  has_many :offers, foreign_key: :created_by_id, inverse_of: :created_by
134
169
  has_many :reviewed_offers, foreign_key: :reviewed_by_id, inverse_of: :reviewed_by, class_name: 'Offer'
@@ -172,7 +207,6 @@ DatabaseCleaner.strategy = :deletion
172
207
  RSpec.configure do |config|
173
208
  config.before(:each) do
174
209
  DatabaseCleaner.start
175
- # FactoryBot.lint
176
210
  end
177
211
 
178
212
  config.after(:each) do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_model_serializers_pg
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paul A. Jungwirth
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-10-12 00:00:00.000000000 Z
11
+ date: 2019-10-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: active_model_serializers
@@ -170,6 +170,7 @@ files:
170
170
  - gemfiles/Gemfile.activerecord-5.2.x
171
171
  - lib/active_model_serializers/adapter/json_api_pg.rb
172
172
  - lib/active_model_serializers_pg.rb
173
+ - lib/active_model_serializers_pg/collection_serializer.rb
173
174
  - lib/active_model_serializers_pg/version.rb
174
175
  - spec/serializer_spec.rb
175
176
  - spec/spec_helper.rb