restpack_serializer 0.4.27 → 0.4.28
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 +11 -0
- data/lib/restpack_serializer.rb +0 -1
- data/lib/restpack_serializer/options.rb +26 -12
- data/lib/restpack_serializer/serializable.rb +5 -0
- data/lib/restpack_serializer/serializable/side_loading.rb +13 -2
- data/lib/restpack_serializer/version.rb +1 -1
- data/restpack_serializer.gemspec +1 -1
- data/spec/fixtures/db.rb +15 -0
- data/spec/fixtures/serializers.rb +6 -1
- data/spec/serializable/serializer_spec.rb +24 -8
- data/spec/serializable/side_loading/has_many_spec.rb +30 -1
- data/spec/support/factory.rb +14 -0
- metadata +16 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 349f4fd2ffe9d0c1d9a7ccf4bcf724dd6ef59580
|
4
|
+
data.tar.gz: 9884153b0699c9f7fe9ced987b8284561cff48d3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b4792303f29da79c3dcf8518df080260d63dcdd89d620ca37b77388f2efdf0b2985d6e0d063624dc0d711c6aeadd6e12497ba0dc42266c2222bd5330bf1b88db
|
7
|
+
data.tar.gz: fb129097386e039f39fc6aa4964e97cdd36cf0a2825d53b27181c8d58486f5b9fef78856b76f083a0f9acae98aad29b4add0d5cb97b0c959179f4b900ef91b52
|
data/README.md
CHANGED
@@ -12,6 +12,17 @@ restpack_serializer allows you to quickly provide a set of RESTful endpoints for
|
|
12
12
|
* [An overview of RestPack](http://www.slideshare.net/gavinjoyce/taming-monolithic-monsters)
|
13
13
|
* [JSON API](http://jsonapi.org/)
|
14
14
|
|
15
|
+
## Getting Started
|
16
|
+
|
17
|
+
### For rails projects:
|
18
|
+
After adding the gem `restpack_serializer` to your Gemfile, add this code to `config/initializers/restpack_serializer.rb`:
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
Dir[Rails.root.join('app/serializers/**/*.rb')].each do |path|
|
22
|
+
require path
|
23
|
+
end
|
24
|
+
```
|
25
|
+
|
15
26
|
## Serialization
|
16
27
|
|
17
28
|
Let's say we have an `Album` model:
|
data/lib/restpack_serializer.rb
CHANGED
@@ -7,9 +7,9 @@ module RestPack::Serializer
|
|
7
7
|
def initialize(serializer, params = {}, scope = nil, context = {})
|
8
8
|
params.symbolize_keys! if params.respond_to?(:symbolize_keys!)
|
9
9
|
|
10
|
-
@page = 1
|
11
|
-
@page_size = RestPack::Serializer.config.page_size
|
12
|
-
@include = []
|
10
|
+
@page = params[:page] ? params[:page].to_i : 1
|
11
|
+
@page_size = params[:page_size] ? params[:page_size].to_i : RestPack::Serializer.config.page_size
|
12
|
+
@include = params[:include] ? params[:include].split(',').map(&:to_sym) : []
|
13
13
|
@filters = filters_from_params(params, serializer)
|
14
14
|
@sorting = sorting_from_params(params, serializer)
|
15
15
|
@serializer = serializer
|
@@ -17,19 +17,13 @@ module RestPack::Serializer
|
|
17
17
|
@scope = scope || model_class.send(:all)
|
18
18
|
@context = context
|
19
19
|
@include_links = true
|
20
|
-
|
21
|
-
@page = params[:page].to_i if params[:page]
|
22
|
-
@page_size = params[:page_size].to_i if params[:page_size]
|
23
|
-
@include = params[:include].split(',').map(&:to_sym) if params[:include]
|
24
20
|
end
|
25
21
|
|
26
22
|
def scope_with_filters
|
27
23
|
scope_filter = {}
|
24
|
+
|
28
25
|
@filters.keys.each do |filter|
|
29
|
-
value = @filters[filter]
|
30
|
-
if value.is_a?(String)
|
31
|
-
value = value.split(',')
|
32
|
-
end
|
26
|
+
value = query_to_array(@filters[filter])
|
33
27
|
scope_filter[filter] = value
|
34
28
|
end
|
35
29
|
|
@@ -41,7 +35,7 @@ module RestPack::Serializer
|
|
41
35
|
end
|
42
36
|
|
43
37
|
def filters_as_url_params
|
44
|
-
@filters.sort.map {|k,v|
|
38
|
+
@filters.sort.map { |k,v| map_filter_ids(k,v) }.join('&')
|
45
39
|
end
|
46
40
|
|
47
41
|
def sorting_as_url_params
|
@@ -73,5 +67,25 @@ module RestPack::Serializer
|
|
73
67
|
end
|
74
68
|
sorting_parameters
|
75
69
|
end
|
70
|
+
|
71
|
+
def map_filter_ids(key,value)
|
72
|
+
case value
|
73
|
+
when Hash
|
74
|
+
value.map { |k,v| map_filter_ids(k,v) }
|
75
|
+
else
|
76
|
+
"#{key}=#{value.join(',')}"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def query_to_array(value)
|
81
|
+
case value
|
82
|
+
when String
|
83
|
+
value.split(',')
|
84
|
+
when Hash
|
85
|
+
value.each { |k, v| value[k] = query_to_array(v) }
|
86
|
+
else
|
87
|
+
value
|
88
|
+
end
|
89
|
+
end
|
76
90
|
end
|
77
91
|
end
|
@@ -71,6 +71,11 @@ module RestPack
|
|
71
71
|
if foreign_key_value
|
72
72
|
data[:links][association.name.to_sym] = foreign_key_value.to_s
|
73
73
|
end
|
74
|
+
elsif association.macro == :has_many && association.options[:through]
|
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
|
74
79
|
end
|
75
80
|
end
|
76
81
|
data
|
@@ -85,10 +85,21 @@ module RestPack::Serializer::SideLoading
|
|
85
85
|
|
86
86
|
def side_load_has_many(association, models, serializer)
|
87
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
|
+
|
88
97
|
options = RestPack::Serializer::Options.new(serializer.class)
|
89
|
-
options.
|
98
|
+
options.scope = options.scope.joins(join_table) if join_table
|
99
|
+
options.filters = filters
|
90
100
|
options.include_links = false
|
91
|
-
|
101
|
+
|
102
|
+
serializer.class.page_with_options(options)
|
92
103
|
end
|
93
104
|
|
94
105
|
def association_from_include(include)
|
data/restpack_serializer.gemspec
CHANGED
@@ -19,7 +19,6 @@ Gem::Specification.new do |gem|
|
|
19
19
|
|
20
20
|
gem.add_dependency 'activesupport', ['>= 4.0.3', '< 5.0']
|
21
21
|
gem.add_dependency 'activerecord', ['>= 4.0.3', '< 5.0']
|
22
|
-
gem.add_dependency 'protected_attributes', '~> 1.0.5'
|
23
22
|
gem.add_dependency 'kaminari', '~> 0.15.1'
|
24
23
|
|
25
24
|
gem.add_development_dependency 'restpack_gem', '~> 0.0.9'
|
@@ -31,4 +30,5 @@ Gem::Specification.new do |gem|
|
|
31
30
|
gem.add_development_dependency 'database_cleaner', '~> 1.0.1'
|
32
31
|
gem.add_development_dependency 'rspec'
|
33
32
|
gem.add_development_dependency 'bump'
|
33
|
+
gem.add_development_dependency 'protected_attributes', '~> 1.0.5'
|
34
34
|
end
|
data/spec/fixtures/db.rb
CHANGED
@@ -41,6 +41,13 @@ ActiveRecord::Schema.define(:version => 1) do
|
|
41
41
|
create_table "payments", :force => true do |t|
|
42
42
|
t.integer "amount"
|
43
43
|
t.integer "artist_id"
|
44
|
+
t.integer "fan_id"
|
45
|
+
t.datetime "created_at"
|
46
|
+
t.datetime "updated_at"
|
47
|
+
end
|
48
|
+
|
49
|
+
create_table "fans", :force => true do |t|
|
50
|
+
t.string "name"
|
44
51
|
t.datetime "created_at"
|
45
52
|
t.datetime "updated_at"
|
46
53
|
end
|
@@ -53,6 +60,7 @@ module MyApp
|
|
53
60
|
has_many :albums
|
54
61
|
has_many :songs
|
55
62
|
has_many :payments
|
63
|
+
has_many :fans, :through => :payments
|
56
64
|
end
|
57
65
|
|
58
66
|
class Album < ActiveRecord::Base
|
@@ -82,5 +90,12 @@ module MyApp
|
|
82
90
|
attr_accessible :amount, :artist
|
83
91
|
|
84
92
|
belongs_to :artist
|
93
|
+
belongs_to :fan
|
94
|
+
end
|
95
|
+
|
96
|
+
class Fan < ActiveRecord::Base
|
97
|
+
attr_accessible :name
|
98
|
+
has_many :payments
|
99
|
+
has_many :artists, :through => :albums
|
85
100
|
end
|
86
101
|
end
|
@@ -27,6 +27,11 @@ module MyApp
|
|
27
27
|
class ArtistSerializer
|
28
28
|
include RestPack::Serializer
|
29
29
|
attributes :id, :name, :website
|
30
|
-
can_include :albums, :songs
|
30
|
+
can_include :albums, :songs, :fans
|
31
|
+
end
|
32
|
+
|
33
|
+
class FanSerializer
|
34
|
+
include RestPack::Serializer
|
35
|
+
attributes :id, :name
|
31
36
|
end
|
32
37
|
end
|
@@ -139,14 +139,30 @@ describe RestPack::Serializer do
|
|
139
139
|
end
|
140
140
|
|
141
141
|
context "links" do
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
142
|
+
context "'belongs to' associations" do
|
143
|
+
let(:serializer) { MyApp::SongSerializer.new }
|
144
|
+
|
145
|
+
it "includes 'links' data for :belongs_to associations" do
|
146
|
+
@album1 = FactoryGirl.create(:album_with_songs, song_count: 11)
|
147
|
+
json = serializer.as_json(@album1.songs.first)
|
148
|
+
json[:links].should == {
|
149
|
+
artist: @album1.artist_id.to_s,
|
150
|
+
album: @album1.id.to_s
|
151
|
+
}
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
context "'has_many, through' associations" do
|
156
|
+
let(:artist_serializer) { MyApp::ArtistSerializer.new }
|
157
|
+
|
158
|
+
it "includes 'links' data when there are associated records" do
|
159
|
+
artist_with_fans = FactoryGirl.create :artist_with_fans
|
160
|
+
|
161
|
+
json = artist_serializer.as_json(artist_with_fans)
|
162
|
+
json[:links].should == {
|
163
|
+
fans: artist_with_fans.fans.collect {|obj| obj.id.to_s }
|
164
|
+
}
|
165
|
+
end
|
150
166
|
end
|
151
167
|
end
|
152
168
|
end
|
@@ -2,13 +2,14 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe RestPack::Serializer::SideLoading do
|
4
4
|
context "when side-loading" do
|
5
|
+
let(:side_loads) { MyApp::ArtistSerializer.side_loads(models, options) }
|
6
|
+
|
5
7
|
describe ".has_many" do
|
6
8
|
|
7
9
|
before(:each) do
|
8
10
|
@artist1 = FactoryGirl.create(:artist_with_albums, album_count: 2)
|
9
11
|
@artist2 = FactoryGirl.create(:artist_with_albums, album_count: 1)
|
10
12
|
end
|
11
|
-
let(:side_loads) { MyApp::ArtistSerializer.side_loads(models, options) }
|
12
13
|
|
13
14
|
context "with a single model" do
|
14
15
|
let(:models) { [@artist1] }
|
@@ -37,7 +38,35 @@ describe RestPack::Serializer::SideLoading do
|
|
37
38
|
end
|
38
39
|
end
|
39
40
|
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '.has_many through' do
|
44
|
+
context 'when including :fans' do
|
45
|
+
let(:options) { RestPack::Serializer::Options.new(MyApp::ArtistSerializer, { "include" => "fans" }) }
|
46
|
+
let(:artist_1) {FactoryGirl.create :artist_with_fans}
|
47
|
+
let(:artist_2) {FactoryGirl.create :artist_with_fans}
|
40
48
|
|
49
|
+
context "with a single model" do
|
50
|
+
let(:models) {[artist_1]}
|
51
|
+
|
52
|
+
it 'returns side-loaded fans' do
|
53
|
+
side_loads[:fans].count.should == artist_1.fans.count
|
54
|
+
side_loads[:meta][:fans][:page].should == 1
|
55
|
+
side_loads[:meta][:fans][:count].should == artist_1.fans.count
|
56
|
+
end
|
57
|
+
end
|
58
|
+
context "with a multiple models" do
|
59
|
+
let(:models) {[artist_1, artist_2]}
|
60
|
+
|
61
|
+
it 'returns side-loaded fans' do
|
62
|
+
expected_count = artist_1.fans.count + artist_2.fans.count
|
63
|
+
|
64
|
+
side_loads[:fans].count.should == expected_count
|
65
|
+
side_loads[:meta][:fans][:page].should == 1
|
66
|
+
side_loads[:meta][:fans][:count].should == expected_count
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
41
70
|
end
|
42
71
|
end
|
43
72
|
end
|
data/spec/support/factory.rb
CHANGED
@@ -14,6 +14,15 @@ FactoryGirl.define do
|
|
14
14
|
create_list(:album_with_songs, evaluator.album_count, artist: artist)
|
15
15
|
end
|
16
16
|
end
|
17
|
+
|
18
|
+
factory :artist_with_fans do
|
19
|
+
ignore do
|
20
|
+
fans_count 3
|
21
|
+
end
|
22
|
+
after(:create) do |artist, evaluator|
|
23
|
+
create_list(:payment, evaluator.fans_count, artist: artist)
|
24
|
+
end
|
25
|
+
end
|
17
26
|
end
|
18
27
|
|
19
28
|
factory :album, :class => MyApp::Album do
|
@@ -41,5 +50,10 @@ FactoryGirl.define do
|
|
41
50
|
factory :payment, :class => MyApp::Payment do
|
42
51
|
amount 999
|
43
52
|
artist
|
53
|
+
fan
|
54
|
+
end
|
55
|
+
|
56
|
+
factory :fan, :class => MyApp::Fan do
|
57
|
+
sequence(:name) {|n| "Fan ##{n}"}
|
44
58
|
end
|
45
59
|
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.4.
|
4
|
+
version: 0.4.28
|
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-08-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -50,20 +50,6 @@ dependencies:
|
|
50
50
|
- - "<"
|
51
51
|
- !ruby/object:Gem::Version
|
52
52
|
version: '5.0'
|
53
|
-
- !ruby/object:Gem::Dependency
|
54
|
-
name: protected_attributes
|
55
|
-
requirement: !ruby/object:Gem::Requirement
|
56
|
-
requirements:
|
57
|
-
- - "~>"
|
58
|
-
- !ruby/object:Gem::Version
|
59
|
-
version: 1.0.5
|
60
|
-
type: :runtime
|
61
|
-
prerelease: false
|
62
|
-
version_requirements: !ruby/object:Gem::Requirement
|
63
|
-
requirements:
|
64
|
-
- - "~>"
|
65
|
-
- !ruby/object:Gem::Version
|
66
|
-
version: 1.0.5
|
67
53
|
- !ruby/object:Gem::Dependency
|
68
54
|
name: kaminari
|
69
55
|
requirement: !ruby/object:Gem::Requirement
|
@@ -204,6 +190,20 @@ dependencies:
|
|
204
190
|
- - ">="
|
205
191
|
- !ruby/object:Gem::Version
|
206
192
|
version: '0'
|
193
|
+
- !ruby/object:Gem::Dependency
|
194
|
+
name: protected_attributes
|
195
|
+
requirement: !ruby/object:Gem::Requirement
|
196
|
+
requirements:
|
197
|
+
- - "~>"
|
198
|
+
- !ruby/object:Gem::Version
|
199
|
+
version: 1.0.5
|
200
|
+
type: :development
|
201
|
+
prerelease: false
|
202
|
+
version_requirements: !ruby/object:Gem::Requirement
|
203
|
+
requirements:
|
204
|
+
- - "~>"
|
205
|
+
- !ruby/object:Gem::Version
|
206
|
+
version: 1.0.5
|
207
207
|
description: Model serialization, paging, side-loading and filtering
|
208
208
|
email:
|
209
209
|
- gavinjoyce@gmail.com
|