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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: dfbd8796c92fa7467d0ffac9b47477668189212b
4
- data.tar.gz: 41d7e43eb3cd37b6bebdd491e3c278d084effc1d
3
+ metadata.gz: 59940462e061cdebfa6cdcee2e8614a0c4907fd7
4
+ data.tar.gz: 6d9baf69729c965e1a45e1a99e6592d30e73552b
5
5
  SHA512:
6
- metadata.gz: a0b65750fcc4de192ff9841a9c7f406a78339116fe6daa0c8ab94cf37f2d655140c87b06a23e923d4c493579e15e2a206e3f50982291783fde1e3e316ca9b533
7
- data.tar.gz: 998f3f1cdd6cdcdfbfb7cf3678a37c677e468ea1163e632acdd27368b1925718d35d029dbc8f8290db9fc9ffb21f123c07d756003ba687475a2ac1d74b33f3d7
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
- side_loads = {
7
- :meta => { }
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
- options.include.each do |include|
12
- side_load_data = side_load(include, models, options)
13
- side_loads[:meta].merge!(side_load_data[:meta] || {})
14
- side_loads.merge! side_load_data.except(:meta)
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
- links = {}
30
-
31
- associations.each do |association|
32
- if association.macro == :belongs_to
33
- link_key = "#{self.key}.#{association.name}"
34
- href = "/#{association.plural_name}/{#{link_key}}"
35
- elsif association.macro == :has_many
36
- singular_key = self.key.to_s.singularize
37
- link_key = "#{self.key}.#{association.plural_name}"
38
- href = "/#{association.plural_name}?#{singular_key}_id={#{key}.id}"
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
- associations = []
52
- can_includes.each do |include|
47
+ can_includes.map do |include|
53
48
  association = association_from_include(include)
54
- associations << association if supported_association?(association)
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
- if supported_association?(association)
65
- serializer = RestPack::Serializer::Factory.create(association.class_name)
66
- return send("side_load_#{association.macro}", association, models, serializer)
67
- else
68
- return {}
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 side_load_has_many(association, models, serializer)
87
- return {} if models.empty?
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
- return association unless association.nil?
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
- if association.macro == :belongs_to
69
- data[:links] ||= {}
70
- foreign_key_value = model.send(association.foreign_key)
71
- if foreign_key_value
72
- data[:links][association.name.to_sym] = foreign_key_value.to_s
73
- end
74
- elsif association.macro == :has_many
75
- ids = model.send(association.name).pluck(:id).map { |id| id.to_s }
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
@@ -1,5 +1,5 @@
1
1
  module RestPack
2
2
  module Serializer
3
- VERSION = '0.5.3'
3
+ VERSION = '0.5.4'
4
4
  end
5
5
  end
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 "'has_many, through' associations" do
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
- it "includes 'links' data when there are associated records" do
159
- artist_with_fans = FactoryGirl.create :artist_with_fans
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
- json = artist_serializer.as_json(artist_with_fans)
162
- json[:links][:fans].should == artist_with_fans.fans.collect {|obj| obj.id.to_s }
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
@@ -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.3
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-08-16 00:00:00.000000000 Z
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