fars 0.0.1
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.
- data/.gitignore +22 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +80 -0
- data/LICENSE +22 -0
- data/README.md +4 -0
- data/Rakefile +7 -0
- data/fars.gemspec +26 -0
- data/lib/fars/base_collection_serializer.rb +72 -0
- data/lib/fars/base_model_serializer.rb +231 -0
- data/lib/fars/version.rb +3 -0
- data/lib/fars.rb +5 -0
- data/spec/config/database.example.yml +5 -0
- data/spec/fars/api_version_spec.rb +77 -0
- data/spec/fars/fars_spec.rb +75 -0
- data/spec/spec_helper.rb +34 -0
- data/spec/tasks/db_setup.rake +68 -0
- metadata +163 -0
data/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
coverage
|
6
|
+
InstalledFiles
|
7
|
+
lib/bundler/man
|
8
|
+
pkg
|
9
|
+
rdoc
|
10
|
+
spec/reports
|
11
|
+
test/tmp
|
12
|
+
test/version_tmp
|
13
|
+
tmp
|
14
|
+
|
15
|
+
# YARD artifacts
|
16
|
+
.yardoc
|
17
|
+
_yardoc
|
18
|
+
doc/
|
19
|
+
|
20
|
+
.ruby-*
|
21
|
+
.rspec
|
22
|
+
spec/config/database.yml
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
fars (0.0.1)
|
5
|
+
activerecord (>= 3.2)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
activemodel (4.0.4)
|
11
|
+
activesupport (= 4.0.4)
|
12
|
+
builder (~> 3.1.0)
|
13
|
+
activerecord (4.0.4)
|
14
|
+
activemodel (= 4.0.4)
|
15
|
+
activerecord-deprecated_finders (~> 1.0.2)
|
16
|
+
activesupport (= 4.0.4)
|
17
|
+
arel (~> 4.0.0)
|
18
|
+
activerecord-deprecated_finders (1.0.3)
|
19
|
+
activesupport (4.0.4)
|
20
|
+
i18n (~> 0.6, >= 0.6.9)
|
21
|
+
minitest (~> 4.2)
|
22
|
+
multi_json (~> 1.3)
|
23
|
+
thread_safe (~> 0.1)
|
24
|
+
tzinfo (~> 0.3.37)
|
25
|
+
arel (4.0.2)
|
26
|
+
atomic (1.1.16)
|
27
|
+
builder (3.1.4)
|
28
|
+
coderay (1.1.0)
|
29
|
+
columnize (0.3.6)
|
30
|
+
database_cleaner (1.2.0)
|
31
|
+
debugger (1.6.6)
|
32
|
+
columnize (>= 0.3.1)
|
33
|
+
debugger-linecache (~> 1.2.0)
|
34
|
+
debugger-ruby_core_source (~> 1.3.2)
|
35
|
+
debugger-linecache (1.2.0)
|
36
|
+
debugger-ruby_core_source (1.3.2)
|
37
|
+
diff-lcs (1.2.5)
|
38
|
+
i18n (0.6.9)
|
39
|
+
method_source (0.8.2)
|
40
|
+
minitest (4.7.5)
|
41
|
+
multi_json (1.9.2)
|
42
|
+
pg (0.17.1)
|
43
|
+
pry (0.9.12.6)
|
44
|
+
coderay (~> 1.0)
|
45
|
+
method_source (~> 0.8)
|
46
|
+
slop (~> 3.4)
|
47
|
+
pry-debugger (0.2.2)
|
48
|
+
debugger (~> 1.3)
|
49
|
+
pry (~> 0.9.10)
|
50
|
+
rake (10.1.1)
|
51
|
+
rspec (2.14.1)
|
52
|
+
rspec-core (~> 2.14.0)
|
53
|
+
rspec-expectations (~> 2.14.0)
|
54
|
+
rspec-mocks (~> 2.14.0)
|
55
|
+
rspec-core (2.14.8)
|
56
|
+
rspec-expectations (2.14.5)
|
57
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
58
|
+
rspec-mocks (2.14.6)
|
59
|
+
shoulda (3.5.0)
|
60
|
+
shoulda-context (~> 1.0, >= 1.0.1)
|
61
|
+
shoulda-matchers (>= 1.4.1, < 3.0)
|
62
|
+
shoulda-context (1.1.6)
|
63
|
+
shoulda-matchers (2.5.0)
|
64
|
+
activesupport (>= 3.0.0)
|
65
|
+
slop (3.5.0)
|
66
|
+
thread_safe (0.2.0)
|
67
|
+
atomic (>= 1.1.7, < 2)
|
68
|
+
tzinfo (0.3.39)
|
69
|
+
|
70
|
+
PLATFORMS
|
71
|
+
ruby
|
72
|
+
|
73
|
+
DEPENDENCIES
|
74
|
+
database_cleaner
|
75
|
+
fars!
|
76
|
+
pg
|
77
|
+
pry-debugger
|
78
|
+
rake
|
79
|
+
rspec (>= 2.11)
|
80
|
+
shoulda
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Denis Yagofarov, Vitaliy Bezkrovny
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
data/Rakefile
ADDED
data/fars.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/fars/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ['Denyago', 'Lightpower']
|
6
|
+
gem.email = ["bva@aejis.eu"]
|
7
|
+
gem.description = %q{Fast ActiveRecord Serializer}
|
8
|
+
gem.summary = "Gem provedes classes for fast serialisation of ActiveRecord relations."
|
9
|
+
gem.homepage = "https://github.com/Lightpower/fars"
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "fars"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = Fars::VERSION
|
17
|
+
|
18
|
+
gem.add_dependency 'activerecord', '>= 3.2'
|
19
|
+
|
20
|
+
gem.add_development_dependency 'rspec', '>= 2.11'
|
21
|
+
gem.add_development_dependency 'rake'
|
22
|
+
gem.add_development_dependency 'shoulda'
|
23
|
+
gem.add_development_dependency 'pg'
|
24
|
+
gem.add_development_dependency 'database_cleaner'
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
##
|
2
|
+
# Class: BaseCollectionSerializer
|
3
|
+
#
|
4
|
+
# It is used to represent collections
|
5
|
+
#
|
6
|
+
class Fars::BaseCollectionSerializer
|
7
|
+
class << self
|
8
|
+
# Returns {String} capitalized API version
|
9
|
+
def api_version
|
10
|
+
namespace_array = name.split('::')
|
11
|
+
namespace_array.size > 1 ? namespace_array[0] : nil
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(objects, opts={})
|
16
|
+
@objects = objects
|
17
|
+
@scope = opts[:scope]
|
18
|
+
@fields = opts[:fields]
|
19
|
+
@add_metadata = opts[:add_metadata]
|
20
|
+
@root_key = opts.fetch(:root_key, get_root_key)
|
21
|
+
@item_serializer_class = get_item_serializer_class
|
22
|
+
end
|
23
|
+
|
24
|
+
def as_json
|
25
|
+
items = []
|
26
|
+
|
27
|
+
the_class = item_serializer_class.new(
|
28
|
+
nil,
|
29
|
+
scope: @scope,
|
30
|
+
add_metadata: add_metadata,
|
31
|
+
fields: fields,
|
32
|
+
root_key: get_instance_root_key,
|
33
|
+
)
|
34
|
+
|
35
|
+
objects.each do |object|
|
36
|
+
items << the_class.with_object(object).as_json
|
37
|
+
end
|
38
|
+
|
39
|
+
root_key ? {root_key => items} : items
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_json
|
43
|
+
MultiJson.dump(as_json)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns {String} - API version is got by instance class
|
47
|
+
def api_version
|
48
|
+
self.class.api_version
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
attr_reader :objects, :scope, :fields, :add_metadata, :root_key, :item_serializer_class
|
54
|
+
|
55
|
+
def get_root_key
|
56
|
+
(self.to_s.match /#{api_prefix}(\w+)Serializer/)[1].underscore.to_sym
|
57
|
+
end
|
58
|
+
|
59
|
+
def get_instance_root_key
|
60
|
+
# If root_key is false, get a real one
|
61
|
+
(root_key || get_root_key).to_s.singularize.to_sym
|
62
|
+
end
|
63
|
+
|
64
|
+
def get_item_serializer_class
|
65
|
+
(self.class.to_s.gsub('Serializer', '').singularize + 'Serializer').constantize
|
66
|
+
end
|
67
|
+
|
68
|
+
# Returns {String} prefix for serializer class name using API version
|
69
|
+
def api_prefix
|
70
|
+
api_version ? api_version + '::' : ''
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,231 @@
|
|
1
|
+
##
|
2
|
+
# Class: BaseModelSerializer
|
3
|
+
#
|
4
|
+
# Naming convention: <Name of Model, singular!>Serializer
|
5
|
+
#
|
6
|
+
# Ways to use:
|
7
|
+
# - create new instance, call to_json
|
8
|
+
# - create new instance, add objects to serialize one by one.
|
9
|
+
# This lets you reuse it without determining known data
|
10
|
+
class Fars::BaseModelSerializer
|
11
|
+
class << self
|
12
|
+
def attributes(*attrs)
|
13
|
+
@attributes = attrs.map(&:to_sym)
|
14
|
+
end
|
15
|
+
|
16
|
+
def all_attributes
|
17
|
+
@attributes
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns {Object} of class, that corresponds to model,
|
21
|
+
# this class meant to serialize
|
22
|
+
def model
|
23
|
+
@klass ||= get_model
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns {Symbol} instance hash root key, as an underscored Model name
|
27
|
+
def root_key
|
28
|
+
model.to_s.underscore.to_sym
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns {String} capitalized API version
|
32
|
+
def api_version
|
33
|
+
namespace_array = name.split('::')
|
34
|
+
namespace_array.size > 1 ? namespace_array[0] : nil
|
35
|
+
end
|
36
|
+
|
37
|
+
# Returns {Array} with names of Model relations. Consists of Symbols.
|
38
|
+
# Filtrated by #all_attributes
|
39
|
+
def model_relations
|
40
|
+
@model_relations ||= get_model_relations
|
41
|
+
end
|
42
|
+
|
43
|
+
# Returns {Array} with names of Model methods. Consists of Symbols
|
44
|
+
# Filtrated by #all_attributes
|
45
|
+
def model_methods
|
46
|
+
@model_methods ||= get_model_methods
|
47
|
+
end
|
48
|
+
|
49
|
+
# Returns {Array} with names of this serializer instance methods. Consists of Symbols
|
50
|
+
# Filtrated by #all_attributes
|
51
|
+
def serializer_methods
|
52
|
+
@serializer_methods ||= get_serializer_methods
|
53
|
+
end
|
54
|
+
|
55
|
+
def serializer_for_relation(name, _api_version=nil)
|
56
|
+
serializers_cache[name] ||= resolve_serializer(name, _api_version)
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def serializers_cache
|
62
|
+
@serializers_cache ||= {}
|
63
|
+
end
|
64
|
+
|
65
|
+
def resolve_serializer(relation_name, _api_version=nil)
|
66
|
+
(
|
67
|
+
api_prefix +
|
68
|
+
model.reflect_on_all_associations.
|
69
|
+
find { |assoc| assoc.name == relation_name }.
|
70
|
+
class_name.pluralize +
|
71
|
+
'Serializer'
|
72
|
+
).constantize
|
73
|
+
end
|
74
|
+
|
75
|
+
def get_model
|
76
|
+
(self.to_s.match /#{api_prefix}(\w+)Serializer/)[1].singularize.constantize
|
77
|
+
end
|
78
|
+
|
79
|
+
def get_model_relations
|
80
|
+
(model.reflect_on_all_associations.map { |r| r.name.to_sym } - serializer_methods) & all_attributes
|
81
|
+
end
|
82
|
+
|
83
|
+
def get_model_methods
|
84
|
+
((model.attribute_names.map(&:to_sym) | model.instance_methods) - model_relations - serializer_methods) & all_attributes
|
85
|
+
end
|
86
|
+
|
87
|
+
def get_serializer_methods
|
88
|
+
self.instance_methods & all_attributes
|
89
|
+
end
|
90
|
+
|
91
|
+
# Returns {String} prefix for serializer class name using API version
|
92
|
+
def api_prefix
|
93
|
+
api_version ? api_version + '::' : ''
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# Initialize new instance
|
98
|
+
#
|
99
|
+
# Params:
|
100
|
+
# - object {Object} to serialize
|
101
|
+
# - opts {Hash} of options:
|
102
|
+
# - fields {Array} of attributes to serialize. Can be {NilClass}.
|
103
|
+
# If so - will use all available.
|
104
|
+
# - scope {Object} context of request. Usually current user
|
105
|
+
# or current ability. Can be passed as a {Proc}. If so -
|
106
|
+
# evaluated only when actually called.
|
107
|
+
# - :add_metadata {Boolean} if to add a node '_metadata'
|
108
|
+
# - :root_key {Symbol} overwrites the default one from serializer's Class
|
109
|
+
def initialize(object, opts={})
|
110
|
+
@object = object
|
111
|
+
@scope = opts[:scope]
|
112
|
+
@fields = opts[:fields]
|
113
|
+
@add_metadata = opts.fetch(:add_metadata, true)
|
114
|
+
@root_key = opts.fetch(:root_key, self.class.root_key)
|
115
|
+
|
116
|
+
@serialized_klass = self.class.model # this goes first
|
117
|
+
@all_attributes = self.class.all_attributes
|
118
|
+
@object_relations = self.class.model_relations
|
119
|
+
@object_attributes = self.class.model_methods
|
120
|
+
@serializer_methods = self.class.serializer_methods
|
121
|
+
end
|
122
|
+
|
123
|
+
def with_object(object)
|
124
|
+
@object = object
|
125
|
+
self
|
126
|
+
end
|
127
|
+
|
128
|
+
def as_json
|
129
|
+
# as we can re-use one class of serializer for
|
130
|
+
# many objects, we need to re-evaluate list
|
131
|
+
# of available_attributes for each of them
|
132
|
+
all_attrs = available_attributes
|
133
|
+
item = {}
|
134
|
+
|
135
|
+
(requested_object_attributes & all_attrs).each do |attr|
|
136
|
+
item[attr] = object.send(attr)
|
137
|
+
end
|
138
|
+
|
139
|
+
(requested_serializer_methods & all_attrs).each do |meth|
|
140
|
+
item[meth] = self.send(meth)
|
141
|
+
end
|
142
|
+
|
143
|
+
(requested_object_relations & all_attrs).each do |rel|
|
144
|
+
item[rel] = serialize_relation(rel)
|
145
|
+
end
|
146
|
+
|
147
|
+
hash = {root_key => item}
|
148
|
+
hash[:_metadata] = meta if add_metadata?
|
149
|
+
hash
|
150
|
+
end
|
151
|
+
|
152
|
+
def to_json
|
153
|
+
MultiJson.dump(as_json)
|
154
|
+
end
|
155
|
+
|
156
|
+
protected
|
157
|
+
|
158
|
+
# TODO: Document
|
159
|
+
def serialize_relation(relation_name, relation=nil)
|
160
|
+
klass = self.class.serializer_for_relation(relation_name)
|
161
|
+
relation ||= object.send(relation_name)
|
162
|
+
klass.new(relation,
|
163
|
+
root_key: false,
|
164
|
+
scope: @scope,
|
165
|
+
add_metadata: add_metadata?).as_json
|
166
|
+
end
|
167
|
+
|
168
|
+
private
|
169
|
+
|
170
|
+
# Things we get from options
|
171
|
+
attr_reader :object, :fields, :root_key
|
172
|
+
|
173
|
+
# Utility things
|
174
|
+
attr_reader :serialized_klass
|
175
|
+
|
176
|
+
# Sets of attributes/methods/relations
|
177
|
+
attr_reader :all_attributes, :object_attributes, :serializer_methods, :object_relations
|
178
|
+
|
179
|
+
# List of attributes requested to be shown.
|
180
|
+
# This is frequently done by :fields HTTP request
|
181
|
+
# parameter
|
182
|
+
def requested_attributes
|
183
|
+
@requested_attributes ||= get_requested_attributes
|
184
|
+
end
|
185
|
+
|
186
|
+
def scope
|
187
|
+
if @scope.is_a? Proc
|
188
|
+
@scope = @scope.call
|
189
|
+
else
|
190
|
+
@scope
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def add_metadata?
|
195
|
+
@add_metadata
|
196
|
+
end
|
197
|
+
|
198
|
+
def requested_object_attributes
|
199
|
+
@requested_object_attributes ||= object_attributes & requested_attributes
|
200
|
+
end
|
201
|
+
|
202
|
+
def requested_serializer_methods
|
203
|
+
@requested_serializer_methods ||= serializer_methods & requested_attributes
|
204
|
+
end
|
205
|
+
|
206
|
+
def requested_object_relations
|
207
|
+
@requested_object_relations ||= object_relations & requested_attributes
|
208
|
+
end
|
209
|
+
|
210
|
+
# List of attributes available to be shown
|
211
|
+
# by current security context.
|
212
|
+
#
|
213
|
+
# Can be re-defined in children classes
|
214
|
+
def available_attributes
|
215
|
+
all_attributes
|
216
|
+
end
|
217
|
+
|
218
|
+
def get_requested_attributes
|
219
|
+
case fields
|
220
|
+
when NilClass then all_attributes
|
221
|
+
when Array then fields.map(&:to_sym) | [:id]
|
222
|
+
when Symbol then [fields.to_sym]
|
223
|
+
when String then [fields]
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
# Returns {String} - API version is got by instance class
|
228
|
+
def api_version
|
229
|
+
self.class.api_version
|
230
|
+
end
|
231
|
+
end
|
data/lib/fars/version.rb
ADDED
data/lib/fars.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Fars::BaseModelSerializer do
|
4
|
+
before :all do
|
5
|
+
ActiveSupport::Inflector.inflections do |inflect|
|
6
|
+
inflect.irregular 'slave', 'slaves'
|
7
|
+
end
|
8
|
+
|
9
|
+
class Master < ActiveRecord::Base
|
10
|
+
has_many :slaves
|
11
|
+
end
|
12
|
+
class Slave < ActiveRecord::Base
|
13
|
+
belongs_to :master
|
14
|
+
end
|
15
|
+
|
16
|
+
module V1
|
17
|
+
class MasterSerializer < Fars::BaseModelSerializer
|
18
|
+
attributes :id, :name, :data, # attrs
|
19
|
+
:slaves
|
20
|
+
|
21
|
+
def meta
|
22
|
+
{'metadata' => :present}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
class SlaveSerializer < Fars::BaseModelSerializer
|
26
|
+
attributes :id, :name, :data # attrs
|
27
|
+
end
|
28
|
+
|
29
|
+
class MastersSerializer < Fars::BaseCollectionSerializer ; end
|
30
|
+
class SlavesSerializer < Fars::BaseCollectionSerializer ; end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context '#to_json' do
|
35
|
+
before :each do
|
36
|
+
@object = Master.create(id: 1, name: 'Object1', data: '123')
|
37
|
+
2.times.each {|i| Slave.create(id: i+1, master_id: @object.id, name: "Slave #{i+1}") }
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'returns all fields with metadata' do
|
41
|
+
json_data = {
|
42
|
+
master: {id: 1, name: 'Object1', data: '123'},
|
43
|
+
_metadata: {metadata: :present}
|
44
|
+
}.to_json
|
45
|
+
|
46
|
+
V1::MasterSerializer.new(
|
47
|
+
@object,
|
48
|
+
add_metadata: true,
|
49
|
+
fields: [:id, :name, :data]
|
50
|
+
).to_json.should == json_data
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'returns not all field, whithout metadata' do
|
54
|
+
json_data = {master: {id: 1, name: 'Object1'}}.to_json
|
55
|
+
|
56
|
+
V1::MasterSerializer.new(
|
57
|
+
@object,
|
58
|
+
add_metadata: false,
|
59
|
+
fields: [:id, :name]
|
60
|
+
).to_json.should == json_data
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'returns all field with slaves' do
|
64
|
+
json_data = {master: {id: 1, name: 'Object1', data: '123',
|
65
|
+
slaves: [
|
66
|
+
{slave: {id: 1, name: 'Slave 1', data: nil}},
|
67
|
+
{slave: {id: 2, name: 'Slave 2', data: nil}},
|
68
|
+
]}}.to_json
|
69
|
+
|
70
|
+
V1::MasterSerializer.new(
|
71
|
+
@object,
|
72
|
+
add_metadata: false,
|
73
|
+
fields: [:id, :name, :data, :slaves]
|
74
|
+
).to_json.should == json_data
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Fars::BaseModelSerializer do
|
4
|
+
before :all do
|
5
|
+
ActiveSupport::Inflector.inflections do |inflect|
|
6
|
+
inflect.irregular 'slave', 'slaves'
|
7
|
+
end
|
8
|
+
|
9
|
+
class Master < ActiveRecord::Base
|
10
|
+
has_many :slaves
|
11
|
+
end
|
12
|
+
class Slave < ActiveRecord::Base
|
13
|
+
belongs_to :master
|
14
|
+
end
|
15
|
+
|
16
|
+
class MasterSerializer < Fars::BaseModelSerializer
|
17
|
+
attributes :id, :name, :data, # attrs
|
18
|
+
:slaves
|
19
|
+
|
20
|
+
def meta
|
21
|
+
{'metadata' => :present}
|
22
|
+
end
|
23
|
+
end
|
24
|
+
class SlaveSerializer < Fars::BaseModelSerializer
|
25
|
+
attributes :id, :name, :data # attrs
|
26
|
+
end
|
27
|
+
|
28
|
+
class MastersSerializer < Fars::BaseCollectionSerializer ; end
|
29
|
+
class SlavesSerializer < Fars::BaseCollectionSerializer ; end
|
30
|
+
end
|
31
|
+
|
32
|
+
context '#to_json' do
|
33
|
+
before :each do
|
34
|
+
@object = Master.create(id: 1, name: 'Object1', data: '123')
|
35
|
+
2.times.each {|i| Slave.create(id: i+1, master_id: @object.id, name: "Slave #{i+1}") }
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'returns all fields with metadata' do
|
39
|
+
json_data = {
|
40
|
+
master: {id: 1, name: 'Object1', data: '123'},
|
41
|
+
_metadata: {metadata: :present}
|
42
|
+
}.to_json
|
43
|
+
|
44
|
+
MasterSerializer.new(
|
45
|
+
@object,
|
46
|
+
add_metadata: true,
|
47
|
+
fields: [:id, :name, :data]
|
48
|
+
).to_json.should == json_data
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'returns not all field, whithout metadata' do
|
52
|
+
json_data = {master: {id: 1, name: 'Object1'}}.to_json
|
53
|
+
|
54
|
+
MasterSerializer.new(
|
55
|
+
@object,
|
56
|
+
add_metadata: false,
|
57
|
+
fields: [:id, :name]
|
58
|
+
).to_json.should == json_data
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'returns all field with slaves' do
|
62
|
+
json_data = {master: {id: 1, name: 'Object1', data: '123',
|
63
|
+
slaves: [
|
64
|
+
{slave: {id: 1, name: 'Slave 1', data: nil}},
|
65
|
+
{slave: {id: 2, name: 'Slave 2', data: nil}},
|
66
|
+
]}}.to_json
|
67
|
+
|
68
|
+
MasterSerializer.new(
|
69
|
+
@object,
|
70
|
+
add_metadata: false,
|
71
|
+
fields: [:id, :name, :data, :slaves]
|
72
|
+
).to_json.should == json_data
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require 'shoulda'
|
3
|
+
require 'database_cleaner'
|
4
|
+
require 'pry'
|
5
|
+
|
6
|
+
require 'fars'
|
7
|
+
|
8
|
+
require 'yaml'
|
9
|
+
require 'active_record'
|
10
|
+
|
11
|
+
RSpec.configure do |config|
|
12
|
+
config.color_enabled = true
|
13
|
+
|
14
|
+
config.before(:suite) do
|
15
|
+
DatabaseCleaner.strategy = :transaction
|
16
|
+
end
|
17
|
+
|
18
|
+
config.after(:suite) do
|
19
|
+
DatabaseCleaner.strategy = :truncation
|
20
|
+
DatabaseCleaner.clean
|
21
|
+
end
|
22
|
+
|
23
|
+
config.before(:each) do
|
24
|
+
DatabaseCleaner.start
|
25
|
+
end
|
26
|
+
|
27
|
+
config.after(:each) do
|
28
|
+
DatabaseCleaner.clean
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
root = File.dirname(__FILE__)
|
33
|
+
db_config = YAML.load_file("#{root}/config/database.yml")
|
34
|
+
ActiveRecord::Base.establish_connection(db_config)
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
namespace :spec do
|
4
|
+
namespace :db do
|
5
|
+
desc 'Setup DB for tests'
|
6
|
+
task :setup do
|
7
|
+
puts 'Create database\n'
|
8
|
+
Rake::Task['spec:db:create'].invoke
|
9
|
+
puts 'Migrate database\n'
|
10
|
+
Rake::Task['spec:db:migrate'].invoke
|
11
|
+
end
|
12
|
+
|
13
|
+
desc 'Add tables required for tests'
|
14
|
+
task :migrate do
|
15
|
+
ActiveRecord::Base.establish_connection(db_config)
|
16
|
+
|
17
|
+
ActiveRecord::Base.connection.execute(<<SQL
|
18
|
+
DROP TABLE IF EXISTS "public"."masters";
|
19
|
+
|
20
|
+
CREATE TABLE "public"."masters" (
|
21
|
+
"id" int4 NOT NULL,
|
22
|
+
"name" varchar(255) NOT NULL,
|
23
|
+
"data" varchar(255),
|
24
|
+
CONSTRAINT "masters_pkey" PRIMARY KEY ("id") NOT DEFERRABLE INITIALLY IMMEDIATE
|
25
|
+
)
|
26
|
+
WITH (OIDS=FALSE);
|
27
|
+
ALTER TABLE "public"."masters" OWNER TO "rails";
|
28
|
+
|
29
|
+
DROP TABLE IF EXISTS "public"."slaves";
|
30
|
+
|
31
|
+
CREATE TABLE "public"."slaves" (
|
32
|
+
"id" int4 NOT NULL,
|
33
|
+
"master_id" int4,
|
34
|
+
"name" varchar(255) NOT NULL,
|
35
|
+
"data" varchar(255),
|
36
|
+
CONSTRAINT "slaves_pkey" PRIMARY KEY ("id") NOT DEFERRABLE INITIALLY IMMEDIATE
|
37
|
+
)
|
38
|
+
WITH (OIDS=FALSE);
|
39
|
+
ALTER TABLE "public"."slaves" OWNER TO "rails";
|
40
|
+
|
41
|
+
SQL
|
42
|
+
)
|
43
|
+
end
|
44
|
+
|
45
|
+
desc 'Create DB for tests'
|
46
|
+
task :create do
|
47
|
+
encoding = db_config[:encoding] || ENV['CHARSET'] || 'utf8'
|
48
|
+
begin
|
49
|
+
ActiveRecord::Base.establish_connection(db_config.merge('database' => 'postgres', 'schema_search_path' => 'public'))
|
50
|
+
ActiveRecord::Base.connection.drop_database(db_config['database']) and puts "previously dropping DB..." if db_present?
|
51
|
+
ActiveRecord::Base.connection.create_database(db_config['database'], db_config.merge('encoding' => encoding))
|
52
|
+
ActiveRecord::Base.establish_connection(db_config)
|
53
|
+
rescue
|
54
|
+
$stderr.puts $!, *($!.backtrace)
|
55
|
+
$stderr.puts "Couldn't create database for #{db_config.inspect}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def db_config
|
60
|
+
root = File.expand_path('../../', __FILE__)
|
61
|
+
@db_conf ||= YAML.load_file("#{root}/config/database.yml")
|
62
|
+
end
|
63
|
+
|
64
|
+
def db_present?
|
65
|
+
ActiveRecord::Base.connection.execute("SELECT count(*) FROM pg_database where datname = '#{db_config['database']}'").values.flatten.first.to_i == 1
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
metadata
ADDED
@@ -0,0 +1,163 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fars
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Denyago
|
9
|
+
- Lightpower
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2014-03-26 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: activerecord
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ! '>='
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '3.2'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ! '>='
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: '3.2'
|
31
|
+
- !ruby/object:Gem::Dependency
|
32
|
+
name: rspec
|
33
|
+
requirement: !ruby/object:Gem::Requirement
|
34
|
+
none: false
|
35
|
+
requirements:
|
36
|
+
- - ! '>='
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '2.11'
|
39
|
+
type: :development
|
40
|
+
prerelease: false
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ! '>='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '2.11'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rake
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ! '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
name: shoulda
|
65
|
+
requirement: !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
67
|
+
requirements:
|
68
|
+
- - ! '>='
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '0'
|
71
|
+
type: :development
|
72
|
+
prerelease: false
|
73
|
+
version_requirements: !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ! '>='
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
79
|
+
- !ruby/object:Gem::Dependency
|
80
|
+
name: pg
|
81
|
+
requirement: !ruby/object:Gem::Requirement
|
82
|
+
none: false
|
83
|
+
requirements:
|
84
|
+
- - ! '>='
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: '0'
|
87
|
+
type: :development
|
88
|
+
prerelease: false
|
89
|
+
version_requirements: !ruby/object:Gem::Requirement
|
90
|
+
none: false
|
91
|
+
requirements:
|
92
|
+
- - ! '>='
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
- !ruby/object:Gem::Dependency
|
96
|
+
name: database_cleaner
|
97
|
+
requirement: !ruby/object:Gem::Requirement
|
98
|
+
none: false
|
99
|
+
requirements:
|
100
|
+
- - ! '>='
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0'
|
103
|
+
type: :development
|
104
|
+
prerelease: false
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
106
|
+
none: false
|
107
|
+
requirements:
|
108
|
+
- - ! '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
description: Fast ActiveRecord Serializer
|
112
|
+
email:
|
113
|
+
- bva@aejis.eu
|
114
|
+
executables: []
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- .gitignore
|
119
|
+
- Gemfile
|
120
|
+
- Gemfile.lock
|
121
|
+
- LICENSE
|
122
|
+
- README.md
|
123
|
+
- Rakefile
|
124
|
+
- fars.gemspec
|
125
|
+
- lib/fars.rb
|
126
|
+
- lib/fars/base_collection_serializer.rb
|
127
|
+
- lib/fars/base_model_serializer.rb
|
128
|
+
- lib/fars/version.rb
|
129
|
+
- spec/config/database.example.yml
|
130
|
+
- spec/fars/api_version_spec.rb
|
131
|
+
- spec/fars/fars_spec.rb
|
132
|
+
- spec/spec_helper.rb
|
133
|
+
- spec/tasks/db_setup.rake
|
134
|
+
homepage: https://github.com/Lightpower/fars
|
135
|
+
licenses: []
|
136
|
+
post_install_message:
|
137
|
+
rdoc_options: []
|
138
|
+
require_paths:
|
139
|
+
- lib
|
140
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
141
|
+
none: false
|
142
|
+
requirements:
|
143
|
+
- - ! '>='
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
147
|
+
none: false
|
148
|
+
requirements:
|
149
|
+
- - ! '>='
|
150
|
+
- !ruby/object:Gem::Version
|
151
|
+
version: '0'
|
152
|
+
requirements: []
|
153
|
+
rubyforge_project:
|
154
|
+
rubygems_version: 1.8.23
|
155
|
+
signing_key:
|
156
|
+
specification_version: 3
|
157
|
+
summary: Gem provedes classes for fast serialisation of ActiveRecord relations.
|
158
|
+
test_files:
|
159
|
+
- spec/config/database.example.yml
|
160
|
+
- spec/fars/api_version_spec.rb
|
161
|
+
- spec/fars/fars_spec.rb
|
162
|
+
- spec/spec_helper.rb
|
163
|
+
- spec/tasks/db_setup.rake
|