surrealist 1.1.1 → 1.1.2

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
  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