active_model_serializers_pg 0.0.5 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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