active_model_serializers_pg 0.0.5 → 0.2.0

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: 0fb040d4836aa84e29ba59590ac8bc4373528e31e5186cec1168538d52384fd0
4
- data.tar.gz: a2ef8e881e358597aeb30145a73cbfdff5b7ed663d1c614ba5a40cbceba9d772
3
+ metadata.gz: '0692a6200c2d79a0704f65112f64c150bca435c24f82409c6b7e327ecb8e5a62'
4
+ data.tar.gz: c4a9b76cb7e6f5f896ddacf01fd6d4ae2c7e0b43f14692f3877a0afaec024953
5
5
  SHA512:
6
- metadata.gz: 30889a0c217b98a0a79296379fd7c399b414b223a3568133e796ef605ae938d013957052c939c65bed5a75fed3e64d08025bc83729a92d620d3fdb905aca13dd
7
- data.tar.gz: 7ff699ff57696cece4ed3f54a2f3d8583f1d2bd2089f65ab842972d9c22c9a2a86360c20e4bd0e043f2532db8f54c332804e3038c3d65f35c3af6be4f297a56e
6
+ metadata.gz: fda8dad0da5f0b7e877e348b7b42494b74b6378e9a84977e276f3cbe1595a259eaef4a2d708747d1d6b307f24b5c4f6949c3e7aec6210670030e2b52cf2a9a9c
7
+ data.tar.gz: a81b5904a40a60a482bd3745f289c9a9c774a73b2bc2ce1c3ed26efb518e21318270ad7b401338e34dadb537793cb5ea67d845942ee936f6df25bb4d64a4e488
data/README.md CHANGED
@@ -31,6 +31,13 @@ Or install it yourself as:
31
31
 
32
32
  gem install active_model_serializers_pg
33
33
 
34
+ ### Migrations
35
+
36
+ This gem depends on a SQL function to dasherize hstore/json/jsonb columns, so you must add its migration to your project and run it, like this:
37
+
38
+ rails g active_model_serializers_pg
39
+ rake db:migrate
40
+
34
41
  ## Usage
35
42
 
36
43
  You can enable in-Postgres serialization for everything by putting this in a Rails initializer:
@@ -52,8 +59,10 @@ Here are some other details we support:
52
59
  - `belongs_to`, `has_one`, and `has_many` associations.
53
60
  - If you serialize an `enum` you get the string values, not integers.
54
61
  - You can serialize an `alias`'d association.
62
+ - You can serialize an `alias_attribute`'d column.
55
63
  - We preserve SQL ordering from a model's `default_scope`.
56
64
  - We preserve SQL ordering attached to an association.
65
+ - When dasherizing we also dasherize json/jsonb/hstore contents (like standard AMS).
57
66
 
58
67
  ### Methods in Serializers and Models
59
68
 
@@ -110,7 +119,7 @@ To work on active\_model\_serializers\_pg locally, follow these steps:
110
119
  4. Run `bundle exec rake db:create`, this will create the test database.
111
120
  5. Run `bundle exec rake db:migrate`, this will set up the database tables required
112
121
  by the test.
113
- 6. Run `bundle exec rake test:all` to run tests against all supported versions of Active Record (currently 5.0.x, 5.1.x, 5.2.x).
122
+ 6. Run `bundle exec rake test:all` to run tests against all supported versions of Active Record (currently 5.0.x, 5.1.x, 5.2.x, 6.0.x).
114
123
  You can also say `BUNDLE_GEMFILE=gemfiles/Gemfile.activerecord-5.2.x bundle exec rspec spec` to run against a specific version (and select specific tests).
115
124
 
116
125
  Commands for building/releasing/installing:
data/Rakefile CHANGED
@@ -72,9 +72,17 @@ namespace :db do
72
72
  task :migrate => :load_db_settings do
73
73
  ActiveRecord::Base.establish_connection
74
74
 
75
+ ActiveRecord::Base.connection.execute "CREATE EXTENSION hstore"
76
+
75
77
  ActiveRecord::Base.connection.create_table :people, force: true do |t|
76
78
  t.string "first_name"
77
79
  t.string "last_name"
80
+ t.json "options"
81
+ t.jsonb "prefs"
82
+ t.hstore "settings"
83
+ t.json "selfies", array: true
84
+ t.jsonb "portraits", array: true
85
+ t.hstore "landscapes", array: true
78
86
  t.datetime "created_at"
79
87
  t.datetime "updated_at"
80
88
  end
@@ -131,6 +139,8 @@ namespace :db do
131
139
  t.datetime "updated_at"
132
140
  end
133
141
 
142
+ ActiveRecord::Base.connection.execute File.read(File.expand_path('../lib/generators/active_record/templates/jsonb_dasherize.sql', __FILE__))
143
+
134
144
  puts 'Database migrated'
135
145
  end
136
146
  end
@@ -141,7 +151,7 @@ namespace :test do
141
151
  # Escape current bundler environment
142
152
  Bundler.with_clean_env do
143
153
  # Currently only supports Active Record v5.0-v5.2
144
- %w(5.0.x 5.1.x 5.2.x).each do |version|
154
+ %w(5.0.x 5.1.x 5.2.x 6.0.x).each do |version|
145
155
  sh "BUNDLE_GEMFILE='gemfiles/Gemfile.activerecord-#{version}' bundle install --quiet"
146
156
  sh "BUNDLE_GEMFILE='gemfiles/Gemfile.activerecord-#{version}' bundle exec rspec spec"
147
157
  end
@@ -19,12 +19,14 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ["lib"]
20
20
 
21
21
  spec.add_runtime_dependency 'active_model_serializers', '~> 0.10.8'
22
- spec.add_runtime_dependency 'activerecord', '~> 5.0'
22
+ spec.add_runtime_dependency 'activerecord', '>= 5.0'
23
23
 
24
24
  spec.add_development_dependency 'bundler', '~> 1.3'
25
25
  spec.add_development_dependency 'actionpack', '> 4.0'
26
26
  spec.add_development_dependency 'rake'
27
27
  spec.add_development_dependency 'rspec'
28
+ spec.add_development_dependency 'rspec-rails'
29
+ spec.add_development_dependency 'generator_spec'
28
30
  spec.add_development_dependency 'bourne', '~> 1.3.0'
29
31
  spec.add_development_dependency 'database_cleaner'
30
32
  spec.add_development_dependency 'dotenv'
@@ -2,4 +2,4 @@ source "https://rubygems.org"
2
2
 
3
3
  gemspec :path => '..'
4
4
 
5
- gem "activerecord", "~>5.0.0"
5
+ gem "rails", "~>5.0.0"
@@ -2,4 +2,4 @@ source "https://rubygems.org"
2
2
 
3
3
  gemspec :path => '..'
4
4
 
5
- gem "activerecord", "~>5.1.0"
5
+ gem "rails", "~>5.1.0"
@@ -2,4 +2,4 @@ source "https://rubygems.org"
2
2
 
3
3
  gemspec :path => '..'
4
4
 
5
- gem "activerecord", "~>5.2.0"
5
+ gem "rails", "~>5.2.0"
@@ -0,0 +1,5 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec :path => '..'
4
+
5
+ gem "rails", "~>6.0.0"
@@ -77,7 +77,7 @@ module ActiveModelSerializers
77
77
  end
78
78
 
79
79
  def self.warn_about_collection_serializer
80
- 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."
80
+ 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 JSON-building query in Postgres."
81
81
  if Object.const_defined? 'Rails'
82
82
  Rails.logger.warn msg
83
83
  else
@@ -105,8 +105,8 @@ end
105
105
  # i.e. how you got here, not how you'd leave:
106
106
  # "Reflection" seems to be the internal ActiveRecord lingo
107
107
  # for a belongs_to or has_many relationship.
108
- # (The public documentation calls these "associations",
109
- # I think think older versions of Rails even used that internally,
108
+ # (The public documentation calls these "associations".
109
+ # I think older versions of Rails even used that internally,
110
110
  # but nowadays the method names use "reflection".)
111
111
  class JsonThing
112
112
  attr_reader :ar_class, :full_name, :name, :serializer, :serializer_options, :json_key, :json_type, :reflection, :parent, :cte_name, :jbs_name
@@ -141,28 +141,10 @@ class JsonThing
141
141
  JsonThing.new(refl.klass, "#{full_name}.#{reflection_name}", nil, serializer_options, refl, self)
142
142
  end
143
143
 
144
- # Gets the attributes (i.e. scalar fields) on the AR class
145
- # as a Set of symbols.
146
- # TODO: tests
147
- def declared_attributes
148
- @declared_attributes ||= Set.new(@ar_class.attribute_types.keys.map(&:to_sym))
149
- end
150
-
151
144
  def enum?(field)
152
145
  @ar_class.attribute_types[field.to_s].is_a? ActiveRecord::Enum::EnumType
153
146
  end
154
147
 
155
- # Gets the reflections (aka associations) of the AR class
156
- # as a Hash from symbol to a subclass of ActiveRecord::Reflection.
157
- # TODO: tests
158
- def declared_reflections
159
- @declared_reflections ||= Hash[
160
- @ar_class.reflections.map{|k, v|
161
- [k.to_sym, v]
162
- }
163
- ]
164
- end
165
-
166
148
  # Checks for alias_attribute and gets to the real attribute name.
167
149
  def unaliased(field_name)
168
150
  ret = field_name
@@ -191,6 +173,20 @@ class JsonThing
191
173
  (@sql_methods[field] ||= _sql_method(field))[0]
192
174
  end
193
175
 
176
+ # Returns the primary key column as a string,
177
+ # but if there is a "#{primary_key}__sql" method,
178
+ # then call that and return it instead.
179
+ # We use this for the id reported in the jsonapi output,
180
+ # but not for foreign key relationships.
181
+ def primary_key_attr
182
+ pk = primary_key
183
+ if has_sql_method?(pk)
184
+ sql_method(pk)
185
+ else
186
+ pk
187
+ end
188
+ end
189
+
194
190
  private
195
191
 
196
192
  # This needs to be globally unique within the SQL query,
@@ -210,7 +206,7 @@ class JsonThing
210
206
  end
211
207
  end
212
208
 
213
- # Each thing has bother a `cte_foo` CTE and a `jbs_foo` CTE.
209
+ # Each thing has both a `cte_foo` CTE and a `jbs_foo` CTE.
214
210
  # (jbs stands for "JSONBs" and is meant to take 3 chars like `cte`.)
215
211
  # The former is just the relevant records,
216
212
  # and the second builds the JSON object for each record.
@@ -376,6 +372,15 @@ end
376
372
  class JsonApiPgSql
377
373
  attr_reader :base_serializer, :base_relation
378
374
 
375
+ def self.json_column_type
376
+ # These classes may not exist, depending on the Rails version:
377
+ @@json_column_type = if Rails::VERSION::STRING >= '5.2'
378
+ 'ActiveRecord::Type::Json'
379
+ else
380
+ 'ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Json'
381
+ end.constantize
382
+ end
383
+
379
384
  def initialize(base_serializer, base_relation, instance_options, options)
380
385
  @base_relation = base_relation
381
386
  @instance_options = instance_options
@@ -452,14 +457,58 @@ class JsonApiPgSql
452
457
  elsif resource.has_sql_method?(field)
453
458
  resource.sql_method(field)
454
459
  else
455
- %Q{"#{resource.table_name}"."#{resource.unaliased(field)}"}
460
+ field = resource.unaliased(field)
461
+ # Standard AMS dasherizes json/jsonb/hstore columns,
462
+ # so we have to do the same:
463
+ if ActiveModelSerializers.config.key_transform == :dash
464
+ cl = resource.ar_class.attribute_types[field.to_s]
465
+ if column_is_jsonb? cl
466
+ %Q{jsonb_dasherize("#{resource.table_name}"."#{field}")}
467
+ elsif column_is_jsonb_array? cl
468
+ # TODO: Could be faster:
469
+ # If we made the jsonb_dasherize function smarter so it could handle jsonb[],
470
+ # we wouldn't have to build a json object from the array then cast to jsonb[].
471
+ %Q{jsonb_dasherize(array_to_json("#{resource.table_name}"."#{field}")::jsonb)}
472
+ elsif column_is_castable_to_jsonb? cl
473
+ # Fortunately we can cast hstore to jsonb,
474
+ # which gives us a solution that works whether or not the hstore extension is installed.
475
+ # Defining an hstore_dasherize function would work only if the extension were present.
476
+ %Q{jsonb_dasherize("#{resource.table_name}"."#{field}"::jsonb)}
477
+ elsif column_is_castable_to_jsonb_array? cl
478
+ # TODO: Could be faster (see above):
479
+ %Q{jsonb_dasherize(array_to_json("#{resource.table_name}"."#{field}"::jsonb[])::jsonb)}
480
+ else
481
+ %Q{"#{resource.table_name}"."#{field}"}
482
+ end
483
+ else
484
+ %Q{"#{resource.table_name}"."#{field}"}
485
+ end
456
486
  end
457
487
  end
458
488
 
489
+ def column_is_jsonb?(column_class)
490
+ column_class.is_a? ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Jsonb
491
+ end
492
+
493
+ def column_is_jsonb_array?(column_class)
494
+ column_class.is_a?(ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Array) and
495
+ column_class.subtype.is_a?(ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Jsonb)
496
+ end
497
+
498
+ def column_is_castable_to_jsonb?(column_class)
499
+ column_class.is_a?(ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Hstore) or
500
+ column_class.is_a?(self.class.json_column_type)
501
+ end
502
+
503
+ def column_is_castable_to_jsonb_array?(column_class)
504
+ column_class.is_a?(ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Array) and
505
+ column_is_castable_to_jsonb?(column_class.subtype)
506
+ end
507
+
459
508
  def select_resource_relationship_links(resource, reflection)
460
509
  reflection.links.map {|link_name, link_parts|
461
510
  <<~EOQ
462
- '#{link_name}', CONCAT(#{link_parts.join(%Q{, "#{resource.parent.table_name}"."#{resource.parent.primary_key}", })})
511
+ '#{link_name}', CONCAT(#{link_parts.join(%Q{, "#{resource.parent.table_name}"."#{resource.parent.primary_key_attr}", })})
463
512
  EOQ
464
513
  }.join(",\n")
465
514
  end
@@ -510,12 +559,8 @@ class JsonApiPgSql
510
559
  # or from the class's default scope.
511
560
  # TODO: preserve the whole custom relation, not just ordering
512
561
  p = refl.ar_class.new
513
- ordering = nil
514
- ActiveSupport::Deprecation.silence do
515
- # TODO: Calling `orders` prints deprecation warnings, so find another way:
516
- ordering = p.send(refl.name).orders
517
- ordering = child_resource.ar_class.default_scoped.orders if ordering.empty?
518
- end
562
+ ordering = p.send(refl.name).arel.orders
563
+ ordering = child_resource.ar_class.default_scoped.arel.orders if ordering.empty?
519
564
  ordering = ordering.map{|o|
520
565
  case o
521
566
  # TODO: The gsub is pretty awful....
@@ -530,7 +575,7 @@ class JsonApiPgSql
530
575
  ordering = "ORDER BY #{ordering}" if ordering
531
576
  <<~EOQ
532
577
  LEFT OUTER JOIN LATERAL (
533
- SELECT coalesce(jsonb_agg(jsonb_build_object('id', rel."#{child_resource.primary_key}"::text,
578
+ SELECT coalesce(jsonb_agg(jsonb_build_object('id', rel."#{child_resource.primary_key_attr}"::text,
534
579
  'type', '#{child_resource.json_type}') #{ordering}), '[]') AS j
535
580
  FROM "#{child_resource.table_name}" rel
536
581
  WHERE rel."#{child_resource.foreign_key}" = "#{resource.table_name}"."#{resource.primary_key}"
@@ -543,7 +588,7 @@ class JsonApiPgSql
543
588
  when ActiveRecord::Relation
544
589
  rel = refl.reflection_sql
545
590
  sql = rel.select(<<~EOQ).to_sql
546
- coalesce(jsonb_agg(jsonb_build_object('id', "#{child_resource.table_name}"."#{child_resource.primary_key}"::text,
591
+ coalesce(jsonb_agg(jsonb_build_object('id', "#{child_resource.table_name}"."#{child_resource.primary_key_attr}"::text,
547
592
  'type', '#{child_resource.json_type}')), '[]') AS j
548
593
  EOQ
549
594
  <<~EOQ
@@ -556,7 +601,7 @@ class JsonApiPgSql
556
601
  elsif refl.has_one?
557
602
  <<~EOQ
558
603
  LEFT OUTER JOIN LATERAL (
559
- SELECT jsonb_build_object('id', rel."#{child_resource.primary_key}"::text,
604
+ SELECT jsonb_build_object('id', rel."#{child_resource.primary_key_attr}"::text,
560
605
  'type', '#{child_resource.json_type}') AS j
561
606
  FROM "#{child_resource.table_name}" rel
562
607
  WHERE rel."#{child_resource.foreign_key}" = "#{resource.table_name}"."#{resource.primary_key}"
@@ -593,8 +638,6 @@ class JsonApiPgSql
593
638
 
594
639
  # See note in _jbs_name method for why we split each thing into two CTEs.
595
640
  def include_cte(resource)
596
- # Sometimes options[:fields] has plural keys and sometimes singular,
597
- # so try both:
598
641
  parent = resource.parent
599
642
  <<~EOQ
600
643
  SELECT DISTINCT ON ("#{resource.table_name}"."#{resource.primary_key}")
@@ -674,7 +717,7 @@ class JsonApiPgSql
674
717
  def select_resource(resource)
675
718
  fields = fields_for(resource)
676
719
  <<~EOQ
677
- jsonb_build_object('id', "#{resource.table_name}"."#{resource.primary_key}"::text,
720
+ jsonb_build_object('id', "#{resource.table_name}"."#{resource.primary_key_attr}"::text,
678
721
  'type', '#{resource.json_type}',
679
722
  'attributes', #{select_resource_attributes(resource)}
680
723
  #{maybe_select_resource_relationships(resource)})
@@ -1,3 +1,3 @@
1
1
  module ActiveModelSerializersPg
2
- VERSION = '0.0.5'
2
+ VERSION = '0.2.0'
3
3
  end
@@ -0,0 +1,20 @@
1
+ require 'rails/generators'
2
+
3
+ module ActiveModelSerializersPg
4
+ module Generators
5
+ class ActiveModelSerializersPgGenerator < Rails::Generators::NamedBase
6
+ Rails::Generators::ResourceHelpers
7
+
8
+ # The ORM generator assumes you're passing a name argument,
9
+ # but we don't need one, so we give it a default value:
10
+ argument :name, type: :string, default: "ignored"
11
+ source_root File.expand_path("../templates", __FILE__)
12
+
13
+ namespace :active_model_serializers_pg
14
+ hook_for :orm, required: true, name: "ignored"
15
+
16
+ desc "Creates an active_model_serialiers_pg database migration"
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,43 @@
1
+ require 'rails/generators/active_record'
2
+
3
+ module ActiveRecord
4
+ module Generators
5
+ class ActiveModelSerializersPgGenerator < ActiveRecord::Generators::Base
6
+ argument :name, type: :string, default: "ignored"
7
+ source_root File.expand_path("../templates", __FILE__)
8
+
9
+ def write_migration
10
+ migration_template "migration.rb", "#{migration_path}/ams_pg_create_dasherize_functions.rb"
11
+ end
12
+
13
+ private
14
+
15
+ def read_sql(funcname)
16
+ File.read(File.join(File.expand_path('../templates', __FILE__), "#{funcname}.sql"))
17
+ end
18
+
19
+ def migration_exists?(table_name)
20
+ Dir.glob("#{File.join destination_root, migration_path}/[0-9]*_*.rb").grep(/\d+_ams_pg_create_dasherize_functions.rb$/).first
21
+ end
22
+
23
+ def migration_path
24
+ if Rails.version >= '5.0.3'
25
+ db_migrate_path
26
+ else
27
+ @migration_path ||= File.join "db", "migrate"
28
+ end
29
+ end
30
+
31
+ def migration_version
32
+ if Rails.version.start_with? '5'
33
+ "[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
34
+ end
35
+ end
36
+
37
+ def jsonb_dasherize
38
+ read_sql('jsonb_dasherize')
39
+ end
40
+
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,32 @@
1
+ CREATE FUNCTION jsonb_dasherize(j jsonb)
2
+ RETURNS jsonb
3
+ IMMUTABLE
4
+ AS
5
+ $$
6
+ DECLARE
7
+ t text;
8
+ ret jsonb;
9
+ BEGIN
10
+ t := jsonb_typeof(j);
11
+ IF t = 'object' THEN
12
+ SELECT COALESCE(jsonb_object_agg(replace(lower(regexp_replace(k, '([A-Z])', '_\1', 'g')), '_', '-'), jsonb_dasherize(v)), '{}')
13
+ INTO ret
14
+ FROM jsonb_each(j) AS t(k, v);
15
+ RETURN ret;
16
+ ELSIF t = 'array' THEN
17
+ SELECT COALESCE(jsonb_agg(jsonb_dasherize(elem)), '[]')
18
+ INTO ret
19
+ FROM jsonb_array_elements(j) AS t(elem);
20
+ RETURN ret;
21
+ ELSIF t IS NULL THEN
22
+ -- This should never happen internally
23
+ -- (thankfully, lest jsonb_set return NULL and destroy everything),
24
+ -- but only from a passed-in NULL.
25
+ RETURN NULL;
26
+ ELSE
27
+ -- string/number/null:
28
+ RETURN j;
29
+ END IF;
30
+ END;
31
+ $$
32
+ LANGUAGE plpgsql;
@@ -0,0 +1,11 @@
1
+ class AmsPgCreateDasherizeFunctions < ActiveRecord::Migration<%= migration_version %>
2
+
3
+ def up
4
+ execute %q{<%= jsonb_dasherize %>}
5
+ end
6
+
7
+ def down
8
+ execute "DROP FUNCTION jsonb_dasherize(jsonb)"
9
+ end
10
+
11
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+ require 'generators/active_model_serializers_pg/active_model_serializers_pg_generator'
3
+
4
+ describe ActiveModelSerializersPg::Generators::ActiveModelSerializersPgGenerator, type: :generator do
5
+ destination File.expand_path "../../../tmp", __FILE__
6
+
7
+ before :each do
8
+ prepare_destination
9
+ end
10
+
11
+ it "creates the migration file" do
12
+ run_generator
13
+ expect(destination_root).to have_structure {
14
+ directory "db" do
15
+ directory "migrate" do
16
+ migration "ams_pg_create_dasherize_functions" do
17
+ contains "class AmsPgCreateDasherizeFunctions"
18
+ end
19
+ end
20
+ end
21
+ }
22
+ end
23
+ end
@@ -209,6 +209,65 @@ describe 'ArraySerializer' do
209
209
  end
210
210
  end
211
211
 
212
+ context 'with dasherized json columns' do
213
+ let(:relation) { Person.all }
214
+ let(:controller) { PeopleController.new }
215
+ let(:person) {
216
+ Person.create first_name: 'Test',
217
+ last_name: 'User',
218
+ options: { 'foo_foo': 'baz', barBar: [{ 'jar_jar': 'binks' }] },
219
+ prefs: { 'foo_foo': 'baz', barBar: [{ 'jar_jar': 'binks' }] },
220
+ settings: { 'foo_foo': 'bar' },
221
+ selfies: [ { 'photo_resolution': '200x200' } ],
222
+ portraits: [ { 'photo_resolution': '150x200' } ],
223
+ landscapes: [ { 'photo_resolution': '200x150' } ]
224
+ }
225
+ let(:options) { { each_serializer: PersonWithJsonSerializer } }
226
+
227
+ before do
228
+ @old_key_setting = ActiveModelSerializers.config.key_transform
229
+ ActiveModelSerializers.config.key_transform = :dash
230
+ end
231
+
232
+ after do
233
+ ActiveModelSerializers.config.key_transform = @old_key_setting
234
+ end
235
+
236
+ it 'generates the proper json output' do
237
+ json_expected = {
238
+ data: [
239
+ {
240
+ id: person.id.to_s,
241
+ type: 'people',
242
+ attributes: {
243
+ prefs: {
244
+ 'bar-bar': [{ 'jar-jar' => 'binks'}],
245
+ 'foo-foo' => 'baz',
246
+ },
247
+ options: {
248
+ 'bar-bar': [{ 'jar-jar' => 'binks'}],
249
+ 'foo-foo' => 'baz',
250
+ },
251
+ selfies: [
252
+ { 'photo-resolution' => '200x200' },
253
+ ],
254
+ settings: {
255
+ 'foo-foo' => 'bar'
256
+ },
257
+ portraits: [
258
+ { 'photo-resolution' => '150x200' },
259
+ ],
260
+ landscapes: [
261
+ { 'photo-resolution' => '200x150' },
262
+ ],
263
+ },
264
+ }
265
+ ]
266
+ }.to_json
267
+ expect(json_data).to eq json_expected
268
+ end
269
+ end
270
+
212
271
  context 'with aliased association' do
213
272
  let(:relation) { Tag.first }
214
273
  let(:controller) { TagsController.new }
data/spec/spec_helper.rb CHANGED
@@ -1,11 +1,12 @@
1
- require 'active_record'
2
- require 'action_controller'
1
+ require 'rails/all'
3
2
  require 'rspec'
3
+ require 'rspec/rails'
4
4
  require 'bourne'
5
5
  require 'database_cleaner'
6
6
  require 'active_model_serializers'
7
7
  require 'action_controller/serialization'
8
8
  require 'active_model_serializers_pg'
9
+ require 'generator_spec'
9
10
  if ENV['TEST_UNPATCHED_AMS']
10
11
  ActiveModelSerializers.config.adapter = :json_api
11
12
  else
@@ -24,6 +25,17 @@ end
24
25
  require 'dotenv'
25
26
  Dotenv.load
26
27
 
28
+ # Need this or Rails.application is nil just below:
29
+ module TestApp
30
+ class Application < ::Rails::Application
31
+ config.root = File.dirname(__FILE__)
32
+ end
33
+ end
34
+
35
+ # Need this line or `hook_for` in our generators is ignored,
36
+ # so our migration generator doesn't run:
37
+ Rails.application.load_generators
38
+
27
39
  ActiveRecord::Base.establish_connection(ENV['DATABASE_URL'])
28
40
 
29
41
  class TestController < ActionController::Base
@@ -56,6 +68,16 @@ class PersonSerializer < ActiveModel::Serializer
56
68
  end
57
69
  end
58
70
 
71
+ class PersonWithJsonSerializer < ActiveModel::Serializer
72
+ attributes :id,
73
+ :options, # json
74
+ :prefs, # jsonb
75
+ :settings, # hstore
76
+ :selfies, # json[]
77
+ :portraits, # jsonb[]
78
+ :landscapes # hstore[]
79
+ end
80
+
59
81
  class Note < ActiveRecord::Base
60
82
  has_many :tags
61
83
  has_many :sorted_tags
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.5
4
+ version: 0.2.0
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-22 00:00:00.000000000 Z
11
+ date: 2021-07-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: active_model_serializers
@@ -28,14 +28,14 @@ dependencies:
28
28
  name: activerecord
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: '5.0'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '5.0'
41
41
  - !ruby/object:Gem::Dependency
@@ -94,6 +94,34 @@ dependencies:
94
94
  - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rspec-rails
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: generator_spec
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
97
125
  - !ruby/object:Gem::Dependency
98
126
  name: bourne
99
127
  requirement: !ruby/object:Gem::Requirement
@@ -168,10 +196,16 @@ files:
168
196
  - gemfiles/Gemfile.activerecord-5.0.x
169
197
  - gemfiles/Gemfile.activerecord-5.1.x
170
198
  - gemfiles/Gemfile.activerecord-5.2.x
199
+ - gemfiles/Gemfile.activerecord-6.0.x
171
200
  - lib/active_model_serializers/adapter/json_api_pg.rb
172
201
  - lib/active_model_serializers_pg.rb
173
202
  - lib/active_model_serializers_pg/collection_serializer.rb
174
203
  - lib/active_model_serializers_pg/version.rb
204
+ - lib/generators/active_model_serializers_pg/active_model_serializers_pg_generator.rb
205
+ - lib/generators/active_record/active_model_serializers_pg_generator.rb
206
+ - lib/generators/active_record/templates/jsonb_dasherize.sql
207
+ - lib/generators/active_record/templates/migration.rb
208
+ - spec/generators/main_generator_spec.rb
175
209
  - spec/serializer_spec.rb
176
210
  - spec/spec_helper.rb
177
211
  homepage: https://github.com/pjungwir/active_model_serializers_pg
@@ -193,10 +227,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
193
227
  - !ruby/object:Gem::Version
194
228
  version: '0'
195
229
  requirements: []
196
- rubygems_version: 3.0.1
230
+ rubygems_version: 3.0.3
197
231
  signing_key:
198
232
  specification_version: 4
199
233
  summary: Harness the power of PostgreSQL when crafting JSON reponses
200
234
  test_files:
235
+ - spec/generators/main_generator_spec.rb
201
236
  - spec/serializer_spec.rb
202
237
  - spec/spec_helper.rb