restpack_serializer 0.5.3 → 0.5.4
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/lib/restpack_serializer/serializable/side_load_data_builder.rb +56 -0
- data/lib/restpack_serializer/serializable/side_loading.rb +40 -73
- data/lib/restpack_serializer/serializable.rb +10 -11
- data/lib/restpack_serializer/version.rb +1 -1
- data/spec/fixtures/db.rb +17 -0
- data/spec/fixtures/serializers.rb +6 -1
- data/spec/serializable/serializer_spec.rb +18 -5
- data/spec/serializable/side_loading/has_and_belongs_many_spec.rb +44 -0
- data/spec/support/factory.rb +13 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 59940462e061cdebfa6cdcee2e8614a0c4907fd7
|
4
|
+
data.tar.gz: 6d9baf69729c965e1a45e1a99e6592d30e73552b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e22ce4ca562d1a086d63268a735a9b37ce77e80995cee9fdfd61b220fa3810011f8ab89a202e6b1e1603afe051dba6380cd2ee75eb22b2ff947816eb7d69105d
|
7
|
+
data.tar.gz: 4a14b79434d7ae0c01e9796183f62547ac90424b4b781d2149d26d623f36e5f3dbfca5baa2586bc69cbbfb5bfd114427cab002a86c1e22604a4b9315b03de563
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module RestPack
|
2
|
+
module Serializer
|
3
|
+
class SideLoadDataBuilder
|
4
|
+
|
5
|
+
def initialize(association, models, serializer)
|
6
|
+
@association = association
|
7
|
+
@models = models
|
8
|
+
@serializer = serializer
|
9
|
+
end
|
10
|
+
|
11
|
+
def side_load_belongs_to
|
12
|
+
foreign_keys = @models.map { |model| model.send(@association.foreign_key) }.uniq
|
13
|
+
side_load = @association.klass.find(foreign_keys)
|
14
|
+
json_model_data = side_load.map { |model| @serializer.as_json(model) }
|
15
|
+
{ @association.plural_name.to_sym => json_model_data, meta: { } }
|
16
|
+
end
|
17
|
+
|
18
|
+
def side_load_has_many
|
19
|
+
has_association_relation do |options|
|
20
|
+
if join_table = @association.options[:through]
|
21
|
+
options.scope = options.scope.joins(join_table)
|
22
|
+
association_fk = @association.through_reflection.foreign_key.to_sym
|
23
|
+
options.filters = { join_table => { association_fk => model_ids } }
|
24
|
+
else
|
25
|
+
options.filters = { @association.foreign_key.to_sym => model_ids }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def side_load_has_and_belongs_to_many
|
31
|
+
has_association_relation do |options|
|
32
|
+
join_table_name = @association.join_table
|
33
|
+
join_clause = "join #{join_table_name} on #{@association.plural_name}.id = #{join_table_name}.#{@association.class_name.foreign_key}"
|
34
|
+
options.scope = options.scope.joins(join_clause)
|
35
|
+
association_fk = @association.foreign_key.to_sym
|
36
|
+
options.filters = { join_table_name.to_sym => { association_fk => model_ids } }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def model_ids
|
43
|
+
@models.map(&:id)
|
44
|
+
end
|
45
|
+
|
46
|
+
def has_association_relation
|
47
|
+
return {} if @models.empty?
|
48
|
+
serializer_class = @serializer.class
|
49
|
+
options = RestPack::Serializer::Options.new(serializer_class)
|
50
|
+
yield options
|
51
|
+
options.include_links = false
|
52
|
+
serializer_class.page_with_options(options)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -3,17 +3,15 @@ module RestPack::Serializer::SideLoading
|
|
3
3
|
|
4
4
|
module ClassMethods
|
5
5
|
def side_loads(models, options)
|
6
|
-
|
7
|
-
|
8
|
-
}
|
9
|
-
return side_loads if models.empty? || options.include.nil?
|
6
|
+
{ meta: { } }.tap do |side_loads|
|
7
|
+
return side_loads if models.empty? || options.include.nil?
|
10
8
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
9
|
+
options.include.each do |include|
|
10
|
+
side_load_data = side_load(include, models, options)
|
11
|
+
side_loads[:meta].merge!(side_load_data[:meta] || {})
|
12
|
+
side_loads.merge! side_load_data.except(:meta)
|
13
|
+
end
|
15
14
|
end
|
16
|
-
side_loads
|
17
15
|
end
|
18
16
|
|
19
17
|
def can_includes
|
@@ -26,91 +24,60 @@ module RestPack::Serializer::SideLoading
|
|
26
24
|
end
|
27
25
|
|
28
26
|
def links
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
27
|
+
{}.tap do |links|
|
28
|
+
associations.each do |association|
|
29
|
+
if association.macro == :belongs_to
|
30
|
+
link_key = "#{self.key}.#{association.name}"
|
31
|
+
href = "/#{association.plural_name}/{#{link_key}}"
|
32
|
+
elsif association.macro.to_s.match(/has_/)
|
33
|
+
singular_key = self.key.to_s.singularize
|
34
|
+
link_key = "#{self.key}.#{association.plural_name}"
|
35
|
+
href = "/#{association.plural_name}?#{singular_key}_id={#{key}.id}"
|
36
|
+
end
|
37
|
+
links.merge!(link_key => {
|
38
|
+
:href => RestPack::Serializer.config.href_prefix + href,
|
39
|
+
:type => association.plural_name.to_sym
|
40
|
+
}
|
41
|
+
)
|
39
42
|
end
|
40
|
-
|
41
|
-
links[link_key] = {
|
42
|
-
:href => RestPack::Serializer.config.href_prefix + href,
|
43
|
-
:type => association.plural_name.to_sym
|
44
|
-
}
|
45
43
|
end
|
46
|
-
|
47
|
-
links
|
48
44
|
end
|
49
45
|
|
50
46
|
def associations
|
51
|
-
|
52
|
-
can_includes.each do |include|
|
47
|
+
can_includes.map do |include|
|
53
48
|
association = association_from_include(include)
|
54
|
-
|
55
|
-
end
|
56
|
-
associations
|
49
|
+
association if supported_association?(association.macro)
|
50
|
+
end.compact
|
57
51
|
end
|
58
52
|
|
59
53
|
private
|
60
54
|
|
61
55
|
def side_load(include, models, options)
|
62
56
|
association = association_from_include(include)
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
def supported_association?(association)
|
73
|
-
[:belongs_to, :has_many].include?(association.macro)
|
74
|
-
end
|
75
|
-
|
76
|
-
def side_load_belongs_to(association, models, serializer)
|
77
|
-
foreign_keys = models.map { |model| model.send(association.foreign_key) }.uniq
|
78
|
-
side_load = association.klass.find(foreign_keys)
|
79
|
-
|
80
|
-
return {
|
81
|
-
association.plural_name.to_sym => side_load.map { |model| serializer.as_json(model) },
|
82
|
-
:meta => { }
|
83
|
-
}
|
57
|
+
return {} unless supported_association?(association.macro)
|
58
|
+
serializer = RestPack::Serializer::Factory.create(association.class_name)
|
59
|
+
builder = RestPack::Serializer::SideLoadDataBuilder.new(association,
|
60
|
+
models,
|
61
|
+
serializer)
|
62
|
+
builder.send("side_load_#{association.macro}")
|
84
63
|
end
|
85
64
|
|
86
|
-
def
|
87
|
-
|
88
|
-
|
89
|
-
join_table = association.options[:through]
|
90
|
-
|
91
|
-
filters = if join_table
|
92
|
-
{ join_table => { association.through_reflection.foreign_key.to_sym => models.map(&:id) } }
|
93
|
-
else
|
94
|
-
{ association.foreign_key.to_sym => models.map(&:id) }
|
95
|
-
end
|
96
|
-
|
97
|
-
options = RestPack::Serializer::Options.new(serializer.class)
|
98
|
-
options.scope = options.scope.joins(join_table) if join_table
|
99
|
-
options.filters = filters
|
100
|
-
options.include_links = false
|
101
|
-
|
102
|
-
serializer.class.page_with_options(options)
|
65
|
+
def supported_association?(association_macro)
|
66
|
+
[:belongs_to, :has_many, :has_and_belongs_to_many].include?(association_macro)
|
103
67
|
end
|
104
68
|
|
105
69
|
def association_from_include(include)
|
106
70
|
raise_invalid_include(include) unless self.can_includes.include?(include)
|
107
|
-
|
108
71
|
possible_relations = [include.to_s.singularize.to_sym, include]
|
72
|
+
select_association_from_possibles(possible_relations)
|
73
|
+
end
|
74
|
+
|
75
|
+
def select_association_from_possibles(possible_relations)
|
109
76
|
possible_relations.each do |relation|
|
110
|
-
association = self.model_class.reflect_on_association(relation)
|
111
|
-
|
77
|
+
if association = self.model_class.reflect_on_association(relation)
|
78
|
+
return association
|
79
|
+
end
|
112
80
|
end
|
113
|
-
|
114
81
|
raise_invalid_include(include)
|
115
82
|
end
|
116
83
|
|
@@ -6,6 +6,7 @@ require_relative "serializable/paging"
|
|
6
6
|
require_relative "serializable/resource"
|
7
7
|
require_relative "serializable/single"
|
8
8
|
require_relative "serializable/side_loading"
|
9
|
+
require_relative "serializable/side_load_data_builder"
|
9
10
|
require_relative "serializable/symbolizer"
|
10
11
|
require_relative "serializable/sortable"
|
11
12
|
|
@@ -65,17 +66,15 @@ module RestPack
|
|
65
66
|
|
66
67
|
def add_links(model, data)
|
67
68
|
self.class.associations.each do |association|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
data[:links] ||= {}
|
78
|
-
data[:links][association.name.to_sym] = ids
|
69
|
+
data[:links] ||= {}
|
70
|
+
links_value = case
|
71
|
+
when association.macro == :belongs_to
|
72
|
+
model.send(association.foreign_key).try(:to_s)
|
73
|
+
when association.macro.to_s.match(/has_/)
|
74
|
+
model.send(association.name).pluck(:id).map(&:to_s)
|
75
|
+
end
|
76
|
+
unless links_value.blank?
|
77
|
+
data[:links][association.name.to_sym] = links_value
|
79
78
|
end
|
80
79
|
end
|
81
80
|
data
|
data/spec/fixtures/db.rb
CHANGED
@@ -51,6 +51,17 @@ ActiveRecord::Schema.define(:version => 1) do
|
|
51
51
|
t.datetime "created_at"
|
52
52
|
t.datetime "updated_at"
|
53
53
|
end
|
54
|
+
|
55
|
+
create_table "stalkers", :force => true do |t|
|
56
|
+
t.string "name"
|
57
|
+
t.datetime "created_at"
|
58
|
+
t.datetime "updated_at"
|
59
|
+
end
|
60
|
+
|
61
|
+
create_table "artists_stalkers", force: true, id: false do |t|
|
62
|
+
t.integer :artist_id
|
63
|
+
t.integer :stalker_id
|
64
|
+
end
|
54
65
|
end
|
55
66
|
|
56
67
|
module MyApp
|
@@ -61,6 +72,7 @@ module MyApp
|
|
61
72
|
has_many :songs
|
62
73
|
has_many :payments
|
63
74
|
has_many :fans, :through => :payments
|
75
|
+
has_and_belongs_to_many :stalkers
|
64
76
|
end
|
65
77
|
|
66
78
|
class Album < ActiveRecord::Base
|
@@ -98,4 +110,9 @@ module MyApp
|
|
98
110
|
has_many :payments
|
99
111
|
has_many :artists, :through => :albums
|
100
112
|
end
|
113
|
+
|
114
|
+
class Stalker < ActiveRecord::Base
|
115
|
+
attr_accessible :name
|
116
|
+
has_and_belongs_to_many :artists
|
117
|
+
end
|
101
118
|
end
|
@@ -27,11 +27,16 @@ module MyApp
|
|
27
27
|
class ArtistSerializer
|
28
28
|
include RestPack::Serializer
|
29
29
|
attributes :id, :name, :website
|
30
|
-
can_include :albums, :songs, :fans
|
30
|
+
can_include :albums, :songs, :fans, :stalkers
|
31
31
|
end
|
32
32
|
|
33
33
|
class FanSerializer
|
34
34
|
include RestPack::Serializer
|
35
35
|
attributes :id, :name
|
36
36
|
end
|
37
|
+
|
38
|
+
class StalkerSerializer
|
39
|
+
include RestPack::Serializer
|
40
|
+
attributes :id, :name
|
41
|
+
end
|
37
42
|
end
|
@@ -152,14 +152,27 @@ describe RestPack::Serializer do
|
|
152
152
|
end
|
153
153
|
end
|
154
154
|
|
155
|
-
context "
|
155
|
+
context "with a serializer with has_* associations" do
|
156
156
|
let(:artist_serializer) { MyApp::ArtistSerializer.new }
|
157
|
+
let(:json) { artist_serializer.as_json(artist_factory) }
|
158
|
+
let(:side_load_ids) { artist_has_association.map {|obj| obj.id.to_s } }
|
157
159
|
|
158
|
-
|
159
|
-
|
160
|
+
describe "'has_many, through' associations" do
|
161
|
+
let(:artist_factory) { FactoryGirl.create :artist_with_fans }
|
162
|
+
let(:artist_has_association) { artist_factory.fans }
|
160
163
|
|
161
|
-
|
162
|
-
|
164
|
+
it "includes 'links' data when there are associated records" do
|
165
|
+
expect(json[:links][:fans]).to match_array(side_load_ids)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
describe "'has_and_belongs_to_many' associations" do
|
170
|
+
let(:artist_factory) { FactoryGirl.create :artist_with_stalkers }
|
171
|
+
let(:artist_has_association) { artist_factory.stalkers }
|
172
|
+
|
173
|
+
it "includes 'links' data when there are associated records" do
|
174
|
+
expect(json[:links][:stalkers]).to match_array(side_load_ids)
|
175
|
+
end
|
163
176
|
end
|
164
177
|
end
|
165
178
|
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe RestPack::Serializer::SideLoading do
|
4
|
+
context "when side-loading" do
|
5
|
+
let(:side_loads) { MyApp::ArtistSerializer.side_loads(models, options) }
|
6
|
+
|
7
|
+
describe ".has_and_belongs_to_many" do
|
8
|
+
|
9
|
+
before(:each) do
|
10
|
+
@artist1 = FactoryGirl.create(:artist_with_stalkers, stalker_count: 2)
|
11
|
+
@artist2 = FactoryGirl.create(:artist_with_stalkers, stalker_count: 3)
|
12
|
+
end
|
13
|
+
|
14
|
+
context "with a single model" do
|
15
|
+
let(:models) { [@artist1] }
|
16
|
+
|
17
|
+
context "when including :albums" do
|
18
|
+
let(:options) { RestPack::Serializer::Options.new(MyApp::ArtistSerializer, { "include" => "stalkers" }) }
|
19
|
+
let(:stalker_count) { @artist1.stalkers.count }
|
20
|
+
|
21
|
+
it "returns side-loaded albums" do
|
22
|
+
side_loads[:stalkers].count.should == stalker_count
|
23
|
+
side_loads[:meta][:stalkers][:page].should == 1
|
24
|
+
side_loads[:meta][:stalkers][:count].should == stalker_count
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context "with two models" do
|
30
|
+
let(:models) { [@artist1, @artist2] }
|
31
|
+
|
32
|
+
context "when including :albums" do
|
33
|
+
let(:options) { RestPack::Serializer::Options.new(MyApp::ArtistSerializer, { "include" => "stalkers" }) }
|
34
|
+
let(:stalker_count) { @artist1.stalkers.count + @artist2.stalkers.count }
|
35
|
+
|
36
|
+
it "returns side-loaded albums" do
|
37
|
+
side_loads[:stalkers].count.should == stalker_count
|
38
|
+
side_loads[:meta][:stalkers][:count].should == stalker_count
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/spec/support/factory.rb
CHANGED
@@ -23,6 +23,15 @@ FactoryGirl.define do
|
|
23
23
|
create_list(:payment, evaluator.fans_count, artist: artist)
|
24
24
|
end
|
25
25
|
end
|
26
|
+
|
27
|
+
factory :artist_with_stalkers do
|
28
|
+
ignore do
|
29
|
+
stalker_count 2
|
30
|
+
end
|
31
|
+
after(:create) do |artist, evaluator|
|
32
|
+
create_list(:stalker, evaluator.stalker_count, artists: [ artist ])
|
33
|
+
end
|
34
|
+
end
|
26
35
|
end
|
27
36
|
|
28
37
|
factory :album, :class => MyApp::Album do
|
@@ -56,4 +65,8 @@ FactoryGirl.define do
|
|
56
65
|
factory :fan, :class => MyApp::Fan do
|
57
66
|
sequence(:name) {|n| "Fan ##{n}"}
|
58
67
|
end
|
68
|
+
|
69
|
+
factory :stalker, :class => MyApp::Stalker do
|
70
|
+
sequence(:name) {|n| "Stalker ##{n}"}
|
71
|
+
end
|
59
72
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: restpack_serializer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gavin Joyce
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-09-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -229,6 +229,7 @@ files:
|
|
229
229
|
- lib/restpack_serializer/serializable/filterable.rb
|
230
230
|
- lib/restpack_serializer/serializable/paging.rb
|
231
231
|
- lib/restpack_serializer/serializable/resource.rb
|
232
|
+
- lib/restpack_serializer/serializable/side_load_data_builder.rb
|
232
233
|
- lib/restpack_serializer/serializable/side_loading.rb
|
233
234
|
- lib/restpack_serializer/serializable/single.rb
|
234
235
|
- lib/restpack_serializer/serializable/sortable.rb
|
@@ -247,6 +248,7 @@ files:
|
|
247
248
|
- spec/serializable/resource_spec.rb
|
248
249
|
- spec/serializable/serializer_spec.rb
|
249
250
|
- spec/serializable/side_loading/belongs_to_spec.rb
|
251
|
+
- spec/serializable/side_loading/has_and_belongs_many_spec.rb
|
250
252
|
- spec/serializable/side_loading/has_many_spec.rb
|
251
253
|
- spec/serializable/side_loading/side_loading_spec.rb
|
252
254
|
- spec/serializable/single_spec.rb
|
@@ -289,6 +291,7 @@ test_files:
|
|
289
291
|
- spec/serializable/resource_spec.rb
|
290
292
|
- spec/serializable/serializer_spec.rb
|
291
293
|
- spec/serializable/side_loading/belongs_to_spec.rb
|
294
|
+
- spec/serializable/side_loading/has_and_belongs_many_spec.rb
|
292
295
|
- spec/serializable/side_loading/has_many_spec.rb
|
293
296
|
- spec/serializable/side_loading/side_loading_spec.rb
|
294
297
|
- spec/serializable/single_spec.rb
|