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