active_model_serializers_pg 0.0.5 → 0.0.6
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 +4 -4
- data/README.md +7 -0
- data/Rakefile +7 -0
- data/active_model_serializers_pg.gemspec +2 -0
- data/gemfiles/Gemfile.activerecord-5.0.x +1 -1
- data/gemfiles/Gemfile.activerecord-5.1.x +1 -1
- data/gemfiles/Gemfile.activerecord-5.2.x +1 -1
- data/lib/active_model_serializers/adapter/json_api_pg.rb +29 -19
- data/lib/active_model_serializers_pg/version.rb +1 -1
- data/lib/generators/active_model_serializers_pg/active_model_serializers_pg_generator.rb +20 -0
- data/lib/generators/active_record/active_model_serializers_pg_generator.rb +43 -0
- data/lib/generators/active_record/templates/jsonb_dasherize.sql +32 -0
- data/lib/generators/active_record/templates/migration.rb +11 -0
- data/spec/generators/main_generator_spec.rb +23 -0
- data/spec/serializer_spec.rb +47 -0
- data/spec/spec_helper.rb +18 -2
- metadata +36 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d35fba508c14ac3f2dac43976aa98853847901d1854a60cdfc17e9307d7bc054
|
4
|
+
data.tar.gz: de31287ee80e62dbc4ea06dcef31269d70f1ecdb7611ab6021d56d671057aea7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ef15cf70f75a4d508d963593fc806ab388d9f07b295e5a8a393bdcd2ddb1b95094c47f7229ab4113646454211b1dd4550111dc79902c6baf39ddaaa36a4c16a2
|
7
|
+
data.tar.gz: 6221b9fa05569ad2fdd6dd874d326363ea698c6a7442a253e25d349adc046814edf1c67ec68a9ddb0f9fe0126052006261eee969093b78462a027bdb4fa94245
|
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:
|
data/Rakefile
CHANGED
@@ -72,9 +72,14 @@ 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"
|
78
83
|
t.datetime "created_at"
|
79
84
|
t.datetime "updated_at"
|
80
85
|
end
|
@@ -131,6 +136,8 @@ namespace :db do
|
|
131
136
|
t.datetime "updated_at"
|
132
137
|
end
|
133
138
|
|
139
|
+
ActiveRecord::Base.connection.execute File.read(File.expand_path('../lib/generators/active_record/templates/jsonb_dasherize.sql', __FILE__))
|
140
|
+
|
134
141
|
puts 'Database migrated'
|
135
142
|
end
|
136
143
|
end
|
@@ -25,6 +25,8 @@ Gem::Specification.new do |spec|
|
|
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'
|
@@ -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
|
@@ -376,6 +358,15 @@ end
|
|
376
358
|
class JsonApiPgSql
|
377
359
|
attr_reader :base_serializer, :base_relation
|
378
360
|
|
361
|
+
def self.json_column_type
|
362
|
+
# These classes may not exist, depending on the Rails version:
|
363
|
+
@@json_column_type = if Rails::VERSION::STRING >= '5.2'
|
364
|
+
'ActiveRecord::Type::Json'
|
365
|
+
else
|
366
|
+
'ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Json'
|
367
|
+
end.constantize
|
368
|
+
end
|
369
|
+
|
379
370
|
def initialize(base_serializer, base_relation, instance_options, options)
|
380
371
|
@base_relation = base_relation
|
381
372
|
@instance_options = instance_options
|
@@ -452,7 +443,26 @@ class JsonApiPgSql
|
|
452
443
|
elsif resource.has_sql_method?(field)
|
453
444
|
resource.sql_method(field)
|
454
445
|
else
|
455
|
-
|
446
|
+
field = resource.unaliased(field)
|
447
|
+
# Standard AMS dasherizes json/jsonb/hstore columns,
|
448
|
+
# so we have to do the same:
|
449
|
+
if ActiveModelSerializers.config.key_transform == :dash
|
450
|
+
case resource.ar_class.attribute_types[field.to_s]
|
451
|
+
when ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Hstore
|
452
|
+
# Fortunately we can cast hstore to jsonb,
|
453
|
+
# which gives us a solution that works whether or not the hstore extension is installed.
|
454
|
+
# Defining an hstore_dasherize function would work only if the extension were present.
|
455
|
+
%Q{jsonb_dasherize("#{resource.table_name}"."#{field}"::jsonb)}
|
456
|
+
when ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Jsonb
|
457
|
+
%Q{jsonb_dasherize("#{resource.table_name}"."#{field}")}
|
458
|
+
when self.class.json_column_type
|
459
|
+
%Q{jsonb_dasherize("#{resource.table_name}"."#{field}"::jsonb)}
|
460
|
+
else
|
461
|
+
%Q{"#{resource.table_name}"."#{field}"}
|
462
|
+
end
|
463
|
+
else
|
464
|
+
%Q{"#{resource.table_name}"."#{field}"}
|
465
|
+
end
|
456
466
|
end
|
457
467
|
end
|
458
468
|
|
@@ -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(k, '_', '-'), 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,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
|
data/spec/serializer_spec.rb
CHANGED
@@ -209,6 +209,53 @@ 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', bar: [{ 'jar_jar': 'binks' }] },
|
219
|
+
prefs: { 'foo_foo': 'baz', bar: [{ 'jar_jar': 'binks' }] },
|
220
|
+
settings: { 'foo_foo': 'bar' }
|
221
|
+
}
|
222
|
+
let(:options) { { each_serializer: PersonWithJsonSerializer } }
|
223
|
+
|
224
|
+
before do
|
225
|
+
@old_key_setting = ActiveModelSerializers.config.key_transform
|
226
|
+
ActiveModelSerializers.config.key_transform = :dash
|
227
|
+
end
|
228
|
+
|
229
|
+
after do
|
230
|
+
ActiveModelSerializers.config.key_transform = @old_key_setting
|
231
|
+
end
|
232
|
+
|
233
|
+
it 'generates the proper json output' do
|
234
|
+
json_expected = {
|
235
|
+
data: [
|
236
|
+
{
|
237
|
+
id: person.id.to_s,
|
238
|
+
type: 'people',
|
239
|
+
attributes: {
|
240
|
+
prefs: {
|
241
|
+
bar: [{ 'jar-jar' => 'binks'}],
|
242
|
+
'foo-foo' => 'baz',
|
243
|
+
},
|
244
|
+
options: {
|
245
|
+
bar: [{ 'jar-jar' => 'binks'}],
|
246
|
+
'foo-foo' => 'baz',
|
247
|
+
},
|
248
|
+
settings: {
|
249
|
+
'foo-foo' => 'bar'
|
250
|
+
},
|
251
|
+
},
|
252
|
+
}
|
253
|
+
]
|
254
|
+
}.to_json
|
255
|
+
expect(json_data).to eq json_expected
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
212
259
|
context 'with aliased association' do
|
213
260
|
let(:relation) { Tag.first }
|
214
261
|
let(:controller) { TagsController.new }
|
data/spec/spec_helper.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
|
-
require '
|
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,10 @@ class PersonSerializer < ActiveModel::Serializer
|
|
56
68
|
end
|
57
69
|
end
|
58
70
|
|
71
|
+
class PersonWithJsonSerializer < ActiveModel::Serializer
|
72
|
+
attributes :id, :options, :prefs, :settings
|
73
|
+
end
|
74
|
+
|
59
75
|
class Note < ActiveRecord::Base
|
60
76
|
has_many :tags
|
61
77
|
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.
|
4
|
+
version: 0.0.6
|
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-
|
11
|
+
date: 2019-10-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: active_model_serializers
|
@@ -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
|
@@ -172,6 +200,11 @@ files:
|
|
172
200
|
- lib/active_model_serializers_pg.rb
|
173
201
|
- lib/active_model_serializers_pg/collection_serializer.rb
|
174
202
|
- lib/active_model_serializers_pg/version.rb
|
203
|
+
- lib/generators/active_model_serializers_pg/active_model_serializers_pg_generator.rb
|
204
|
+
- lib/generators/active_record/active_model_serializers_pg_generator.rb
|
205
|
+
- lib/generators/active_record/templates/jsonb_dasherize.sql
|
206
|
+
- lib/generators/active_record/templates/migration.rb
|
207
|
+
- spec/generators/main_generator_spec.rb
|
175
208
|
- spec/serializer_spec.rb
|
176
209
|
- spec/spec_helper.rb
|
177
210
|
homepage: https://github.com/pjungwir/active_model_serializers_pg
|
@@ -198,5 +231,6 @@ signing_key:
|
|
198
231
|
specification_version: 4
|
199
232
|
summary: Harness the power of PostgreSQL when crafting JSON reponses
|
200
233
|
test_files:
|
234
|
+
- spec/generators/main_generator_spec.rb
|
201
235
|
- spec/serializer_spec.rb
|
202
236
|
- spec/spec_helper.rb
|