surrealist 1.1.1 → 1.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0a5a2bfdc5cbfe0e1a5f8fbc9e8213da087e2d149ac40857bf8a7e3730c52b87
4
- data.tar.gz: 9b28deb85b05ba4a6baf61eb0b9d52f5746fc9d819765175dd74dd45fae53f3d
3
+ metadata.gz: a430c1d28309d9ef1c1626e64fa3e0f482abef0215307a8ac8ea19083a44eb8b
4
+ data.tar.gz: dc45cc49c8b1af7676a9ad6738c42c83e10397587d7d21908d5345ee74248ff4
5
5
  SHA512:
6
- metadata.gz: 6d37a7cc610900148814b0eda41e4d5d1d12ee6eeacba3bddeaa947d7793992b4983b893aa2a1b22cf8641fc2a8954384f52bc92aa5db2a45ef1aa8d3938f63d
7
- data.tar.gz: 8d6ba4e24d856e9498f2bea63a9570d9b04439ad9efb84913c43bd575a1a19a442282f87a40a6244e4386869aab85bb836a6e5eea5abae476017a4f69aba7456
6
+ metadata.gz: f2ae0819199d9c5ef9d0e221f3a743612fb18c1b392922788c9741eb6ed0172c2ba9aeef566d408b30bfd4a19df2a468edf98cef025923c70e0e2a30fb17d39c
7
+ data.tar.gz: e958a7d97360166bf852d8cb69b294b452176b0f981537df80478e1c5daaa817964a3d877746307d86d0018dc808ec89d58635eee594a7180c5af7a8b6070ba2
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ # 1.1.2
2
+
3
+ ## Fixed
4
+ * Bug with inheritance and mixins ([@nesaulov][]) [#93](https://github.com/nesaulov/surrealist/pull/93)
5
+
1
6
  # 1.1.1
2
7
 
3
8
  ## Fixed
data/Gemfile CHANGED
@@ -7,6 +7,7 @@ group :development, :test do
7
7
  gem 'active_model_serializers', '~> 0.10.0'
8
8
  gem 'activerecord'
9
9
  gem 'benchmark-ips'
10
+ gem 'blueprinter'
10
11
  gem 'coveralls', require: false
11
12
  gem 'dry-struct'
12
13
  gem 'dry-types'
@@ -1,8 +1,10 @@
1
+ # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
1
2
  require_relative '../lib/surrealist'
2
3
  require 'benchmark/ips'
3
4
  require 'active_record'
4
5
  require 'active_model'
5
6
  require 'active_model_serializers'
7
+ require 'blueprinter'
6
8
 
7
9
  ActiveRecord::Base.establish_connection(
8
10
  adapter: 'sqlite3',
@@ -25,7 +27,7 @@ ActiveRecord::Schema.define do
25
27
  create_table :books do |table|
26
28
  table.column :title, :string
27
29
  table.column :year, :string
28
- table.belongs_to :author
30
+ table.belongs_to :author, foreign_key: true
29
31
  end
30
32
  end
31
33
 
@@ -49,11 +51,23 @@ class UserSurrealistSerializer < Surrealist::Serializer
49
51
  json_schema { { name: String, email: String } }
50
52
  end
51
53
 
54
+ class UserAMSSerializer < ActiveModel::Serializer
55
+ attributes :name, :email
56
+ end
57
+
58
+ class UserBlueprint < Blueprinter::Base
59
+ fields :name, :email
60
+ end
61
+
52
62
  ### Associations ###
53
63
 
54
64
  class AuthorSurrealistSerializer < Surrealist::Serializer
55
65
  json_schema do
56
- { name: String, last_name: String, full_name: String, age: Integer, books: Object }
66
+ { name: String, last_name: String, full_name: String, age: Integer, books: Array }
67
+ end
68
+
69
+ def books
70
+ object.books.to_a
57
71
  end
58
72
 
59
73
  def full_name
@@ -65,125 +79,159 @@ class BookSurrealistSerializer < Surrealist::Serializer
65
79
  json_schema { { title: String, year: String } }
66
80
  end
67
81
 
82
+ class BookAMSSerializer < ActiveModel::Serializer
83
+ attributes :title, :year
84
+ end
85
+
86
+ class BookBlueprint < Blueprinter::Base
87
+ fields :title, :year
88
+ end
89
+
90
+ class AuthorAMSSerializer < ActiveModel::Serializer
91
+ attributes :name, :last_name, :full_name, :age
92
+ has_many :books, serializer: BookAMSSerializer
93
+ end
94
+
95
+ class AuthorBlueprint < Blueprinter::Base
96
+ fields :name, :last_name, :age
97
+ field :full_name do |author|
98
+ "#{author.name} #{author.last_name}"
99
+ end
100
+ association :books, blueprint: BookBlueprint
101
+ end
102
+
68
103
  class Author < ActiveRecord::Base
69
104
  include Surrealist
70
105
  surrealize_with AuthorSurrealistSerializer
71
106
 
72
107
  has_many :books
108
+
109
+ def full_name
110
+ "#{name} #{last_name}"
111
+ end
73
112
  end
74
113
 
75
114
  class Book < ActiveRecord::Base
76
115
  include Surrealist
77
116
  surrealize_with BookSurrealistSerializer
78
117
 
79
- belongs_to :author
118
+ belongs_to :author, required: true
80
119
  end
81
120
 
82
- class AuthorSerializer < ActiveModel::Serializer
83
- attributes :name, :last_name, :age
121
+ N = 3000
122
+ N.times { User.create!(name: random_name, email: "#{random_name}@test.com") }
123
+ (N / 2).times { Author.create!(name: random_name, last_name: random_name, age: rand(80)) }
124
+ N.times { Book.create!(title: random_name, year: "19#{rand(10..99)}", author_id: rand(1..N / 2)) }
84
125
 
85
- attribute :full_name do
86
- "#{object.name} #{object.last_name}"
126
+ def sort(obj)
127
+ case obj
128
+ when Array then obj.map { |el| sort(el) }
129
+ when Hash then obj.transform_values { |v| sort(v) }
130
+ else obj
87
131
  end
88
-
89
- has_many :books
90
132
  end
91
133
 
92
- class BookSerializer < ActiveModel::Serializer
93
- attributes :title, :year
134
+ def check_correctness(serializers)
135
+ results = serializers.map(&:call).map { |r| sort(JSON.parse(r)) }
136
+ raise 'Results are not the same' if results.uniq.size > 1
94
137
  end
95
138
 
96
- N = 3000
97
- N.times { User.create!(name: random_name, email: "#{random_name}@test.com") }
98
- (N / 2).times { Author.create!(name: random_name, last_name: random_name, age: rand(80)) }
99
- N.times { Book.create!(title: random_name, year: "19#{rand(10..99)}", author_id: rand(1..N)) }
100
-
101
- def benchmark_instance(ams_arg = '')
102
- user = User.find(rand(1..N))
139
+ def benchmark(names, serializers)
140
+ check_correctness(serializers)
103
141
 
104
142
  Benchmark.ips do |x|
105
143
  x.config(time: 5, warmup: 2)
106
144
 
107
- x.report("AMS#{ams_arg}: instance") do
108
- ActiveModelSerializers::SerializableResource.new(user).to_json
109
- end
110
-
111
- x.report('Surrealist: instance through .surrealize') do
112
- user.surrealize
113
- end
114
-
115
- x.report('Surrealist: instance through Surrealist::Serializer') do
116
- UserSurrealistSerializer.new(user).surrealize
117
- end
145
+ names.zip(serializers).each { |name, proc| x.report(name, &proc) }
118
146
 
119
147
  x.compare!
120
148
  end
121
149
  end
122
150
 
123
- def benchmark_collection(ams_arg = '')
124
- users = User.all
151
+ def benchmark_instance(ams_arg: '', oj_arg: '')
152
+ user = User.find(rand(1..N))
125
153
 
126
- Benchmark.ips do |x|
127
- x.config(time: 5, warmup: 2)
154
+ names = ["AMS#{[ams_arg, oj_arg].join(' ')}: instance",
155
+ 'Surrealist: instance through .surrealize',
156
+ 'Surrealist: instance through Surrealist::Serializer',
157
+ "ActiveModel::Serializers::JSON#{oj_arg} instance",
158
+ "Blueprinter#{oj_arg}"]
128
159
 
129
- x.report("AMS#{ams_arg}: collection") do
130
- ActiveModelSerializers::SerializableResource.new(users).to_json
131
- end
160
+ serializers = [-> { UserAMSSerializer.new(user).to_json },
161
+ -> { user.surrealize },
162
+ -> { UserSurrealistSerializer.new(user).surrealize },
163
+ -> { user.to_json(only: %i[name email]) },
164
+ -> { UserBlueprint.render(user) }]
132
165
 
133
- x.report('Surrealist: collection through Surrealist.surrealize_collection()') do
134
- Surrealist.surrealize_collection(users)
135
- end
166
+ benchmark(names, serializers)
167
+ end
136
168
 
137
- x.report('Surrealist: collection through Surrealist::Serializer') do
138
- UserSurrealistSerializer.new(users).surrealize
139
- end
169
+ def benchmark_collection(ams_arg: '', oj_arg: '')
170
+ users = User.all
140
171
 
141
- x.compare!
142
- end
172
+ names = ["AMS#{[ams_arg, oj_arg].join(' ')}: collection",
173
+ 'Surrealist: collection through Surrealist.surrealize_collection()',
174
+ 'Surrealist: collection through Surrealist::Serializer',
175
+ "ActiveModel::Serializers::JSON#{oj_arg} collection",
176
+ "Blueprinter collection#{oj_arg}"]
177
+
178
+ serializers = [lambda do
179
+ ActiveModel::Serializer::CollectionSerializer.new(
180
+ users, root: nil, serializer: UserAMSSerializer
181
+ ).to_json
182
+ end,
183
+ -> { Surrealist.surrealize_collection(users) },
184
+ -> { UserSurrealistSerializer.new(users).surrealize },
185
+ -> { users.to_json(only: %i[name email]) },
186
+ -> { UserBlueprint.render(users) }]
187
+
188
+ benchmark(names, serializers)
143
189
  end
144
190
 
145
191
  def benchmark_associations_instance
146
192
  instance = Author.find(rand((1..(N / 2))))
147
193
 
148
- Benchmark.ips do |x|
149
- x.config(time: 5, warmup: 2)
150
-
151
- x.report('AMS (associations): instance') do
152
- ActiveModelSerializers::SerializableResource.new(instance).to_json
153
- end
154
-
155
- x.report('Surrealist (associations): instance through .surrealize') do
156
- instance.surrealize
157
- end
158
-
159
- x.report('Surrealist (associations): instance through Surrealist::Serializer') do
160
- AuthorSurrealistSerializer.new(instance).surrealize
161
- end
162
-
163
- x.compare!
164
- end
194
+ names = ['AMS (associations): instance',
195
+ 'Surrealist (associations): instance through .surrealize',
196
+ 'Surrealist (associations): instance through Surrealist::Serializer',
197
+ 'ActiveModel::Serializers::JSON (associations)',
198
+ 'Blueprinter (associations)']
199
+
200
+ serializers = [-> { AuthorAMSSerializer.new(instance).to_json },
201
+ -> { instance.surrealize },
202
+ -> { AuthorSurrealistSerializer.new(instance).surrealize },
203
+ lambda do
204
+ instance.to_json(only: %i[name last_name age], methods: %i[full_name],
205
+ include: { books: { only: %i[title year] } })
206
+ end,
207
+ -> { AuthorBlueprint.render(instance) }]
208
+
209
+ benchmark(names, serializers)
165
210
  end
166
211
 
167
212
  def benchmark_associations_collection
168
213
  collection = Author.all
169
214
 
170
- Benchmark.ips do |x|
171
- x.config(time: 5, warmup: 2)
172
-
173
- x.report('AMS (associations): collection') do
174
- ActiveModelSerializers::SerializableResource.new(collection).to_json
175
- end
176
-
177
- x.report('Surrealist (associations): collection through Surrealist.surrealize_collection()') do
178
- Surrealist.surrealize_collection(collection)
179
- end
180
-
181
- x.report('Surrealist (associations): collection through Surrealist::Serializer') do
182
- AuthorSurrealistSerializer.new(collection).surrealize
183
- end
184
-
185
- x.compare!
186
- end
215
+ names = ['AMS (associations): collection',
216
+ 'Surrealist (associations): collection through Surrealist.surrealize_collection()',
217
+ 'Surrealist (associations): collection through Surrealist::Serializer',
218
+ 'ActiveModel::Serializers::JSON (associations): collection',
219
+ 'Blueprinter (associations): collection']
220
+
221
+ serializers = [lambda do
222
+ ActiveModel::Serializer::CollectionSerializer.new(
223
+ collection, root: nil, serializer: AuthorAMSSerializer
224
+ ).to_json
225
+ end,
226
+ -> { Surrealist.surrealize_collection(collection) },
227
+ -> { AuthorSurrealistSerializer.new(collection).surrealize },
228
+ lambda do
229
+ collection.to_json(only: %i[name last_name age], methods: %i[full_name],
230
+ include: { books: { only: %i[title year] } })
231
+ end,
232
+ -> { AuthorBlueprint.render(collection) }]
233
+
234
+ benchmark(names, serializers)
187
235
  end
188
236
 
189
237
  # Default configuration
@@ -194,8 +242,21 @@ benchmark_collection
194
242
  puts "\n------- Turning off AMS logger -------\n"
195
243
  ActiveModelSerializers.logger.level = Logger::Severity::UNKNOWN
196
244
 
197
- benchmark_instance('(without logging)')
198
- benchmark_collection('(without logging)')
245
+ benchmark_instance(ams_arg: '(without logging)')
246
+ benchmark_collection(ams_arg: '(without logging)')
247
+
248
+ # Associations
249
+ benchmark_associations_instance
250
+ benchmark_associations_collection
251
+
252
+ puts "\n------- Enabling Oj.optimize_rails() & Blueprinter config.generator = Oj -------\n"
253
+ Oj.optimize_rails
254
+ Blueprinter.configure do |config|
255
+ config.generator = Oj
256
+ end
257
+
258
+ benchmark_instance(ams_arg: '(without logging)', oj_arg: '(with Oj)')
259
+ benchmark_collection(ams_arg: '(without logging)', oj_arg: '(with Oj)')
199
260
 
200
261
  # Associations
201
262
  benchmark_associations_instance
@@ -241,3 +302,4 @@ benchmark_associations_collection
241
302
  # Surrealist (associations): collection through Surrealist.surrealize_collection(): 2.4 i/s
242
303
  # Surrealist (associations): collection through Surrealist::Serializer: 2.4 i/s - 1.03x slower
243
304
  # AMS (associations): collection: 1.5 i/s - 1.60x slower
305
+ # rubocop:enable Metrics/AbcSize,Metrics/MethodLength
@@ -19,42 +19,6 @@ module Surrealist
19
19
  raise Surrealist::InvalidSchemaError, SCHEMA_TYPE_ERROR unless hash.is_a?(Hash)
20
20
 
21
21
  Surrealist::VarsHelper.set_schema(klass, hash)
22
- define_missing_methods(klass, hash) if klass < Surrealist::Serializer
23
- end
24
-
25
- private
26
-
27
- # Defines all methods from the json_schema on Serializer instance in order to increase
28
- # performance (comparing to using method_missing)
29
- #
30
- # @param [Object] klass class of the object where methods will be defined
31
- #
32
- # @param [Hash] hash the schema hash
33
- def define_missing_methods(klass, hash)
34
- methods = find_methods(hash)
35
- klass.include(Module.new do
36
- instance_exec do
37
- methods.each do |method|
38
- define_method method do
39
- if (object = instance_variable_get('@object'))
40
- object.public_send(method)
41
- end
42
- end
43
- end
44
- end
45
- end)
46
- end
47
-
48
- # Takes out all keys from a hash
49
- #
50
- # @param [Hash] hash a hash to take keys from
51
- #
52
- # @return [Array] an array of keys
53
- def find_methods(hash)
54
- hash.each_with_object([]) do |(k, v), keys|
55
- keys.push(k)
56
- keys.concat(find_methods(v)) if v.is_a? Hash
57
- end
58
22
  end
59
23
  end
60
24
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Surrealist
4
4
  # Defines the version of Surrealist
5
- VERSION = '1.1.1'.freeze
5
+ VERSION = '1.1.2'.freeze
6
6
  end
data/lib/surrealist.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'oj'
4
+ require 'set'
4
5
  require_relative 'surrealist/any'
5
6
  require_relative 'surrealist/bool'
6
7
  require_relative 'surrealist/builder'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: surrealist
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nikita Esaulov
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-03-24 00:00:00.000000000 Z
11
+ date: 2018-04-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: oj