alba 2.0.1 → 2.2.0
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/.github/workflows/main.yml +1 -1
- data/.rubocop.yml +6 -1
- data/CHANGELOG.md +36 -3
- data/CONTRIBUTING.md +1 -1
- data/Gemfile +3 -3
- data/README.md +102 -34
- data/alba.gemspec +1 -1
- data/benchmark/collection.rb +9 -147
- data/benchmark/prep.rb +82 -0
- data/benchmark/single_resource.rb +3 -154
- data/docs/migrate_from_active_model_serializers.md +2 -2
- data/docs/rails.md +1 -1
- data/lib/alba/association.rb +6 -4
- data/lib/alba/conditional_attribute.rb +1 -1
- data/lib/alba/layout.rb +2 -2
- data/lib/alba/railtie.rb +8 -0
- data/lib/alba/resource.rb +38 -32
- data/lib/alba/version.rb +1 -1
- data/lib/alba.rb +29 -4
- metadata +6 -4
data/benchmark/prep.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
# --- Bundle dependencies ---
|
2
|
+
|
3
|
+
require "bundler/inline"
|
4
|
+
|
5
|
+
gemfile(true) do
|
6
|
+
source "https://rubygems.org"
|
7
|
+
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
|
8
|
+
|
9
|
+
gem "active_model_serializers"
|
10
|
+
gem "activerecord", "6.1.3"
|
11
|
+
gem "alba", path: '../'
|
12
|
+
gem "benchmark-ips"
|
13
|
+
gem "benchmark-memory"
|
14
|
+
gem "blueprinter"
|
15
|
+
gem "fast_serializer_ruby"
|
16
|
+
gem "jbuilder"
|
17
|
+
gem 'turbostreamer'
|
18
|
+
gem "jserializer"
|
19
|
+
gem "multi_json"
|
20
|
+
gem "panko_serializer"
|
21
|
+
gem "pg"
|
22
|
+
gem "primalize"
|
23
|
+
gem "oj"
|
24
|
+
gem "representable"
|
25
|
+
gem "simple_ams"
|
26
|
+
gem "sqlite3"
|
27
|
+
end
|
28
|
+
|
29
|
+
# --- Test data model setup ---
|
30
|
+
|
31
|
+
require "pg"
|
32
|
+
require "active_record"
|
33
|
+
require "active_record/connection_adapters/postgresql_adapter"
|
34
|
+
require "logger"
|
35
|
+
require "oj"
|
36
|
+
require "sqlite3"
|
37
|
+
Oj.optimize_rails unless ENV['NO_OJ_OPTIMIZE_RAILS']
|
38
|
+
|
39
|
+
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
|
40
|
+
# ActiveRecord::Base.logger = Logger.new($stdout)
|
41
|
+
|
42
|
+
ActiveRecord::Schema.define do
|
43
|
+
create_table :posts, force: true do |t|
|
44
|
+
t.string :body
|
45
|
+
end
|
46
|
+
|
47
|
+
create_table :comments, force: true do |t|
|
48
|
+
t.integer :post_id
|
49
|
+
t.string :body
|
50
|
+
t.integer :commenter_id
|
51
|
+
end
|
52
|
+
|
53
|
+
create_table :users, force: true do |t|
|
54
|
+
t.string :name
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class Post < ActiveRecord::Base
|
59
|
+
has_many :comments
|
60
|
+
has_many :commenters, through: :comments, class_name: 'User', source: :commenter
|
61
|
+
|
62
|
+
def attributes
|
63
|
+
{id: nil, body: nil, commenter_names: commenter_names}
|
64
|
+
end
|
65
|
+
|
66
|
+
def commenter_names
|
67
|
+
commenters.pluck(:name)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
class Comment < ActiveRecord::Base
|
72
|
+
belongs_to :post
|
73
|
+
belongs_to :commenter, class_name: 'User'
|
74
|
+
|
75
|
+
def attributes
|
76
|
+
{id: nil, body: nil}
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
class User < ActiveRecord::Base
|
81
|
+
has_many :comments
|
82
|
+
end
|
@@ -1,88 +1,7 @@
|
|
1
1
|
# Benchmark script to run varieties of JSON serializers
|
2
2
|
# Fetch Alba from local, otherwise fetch latest from RubyGems
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
require "bundler/inline"
|
7
|
-
|
8
|
-
gemfile(true) do
|
9
|
-
source "https://rubygems.org"
|
10
|
-
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
|
11
|
-
|
12
|
-
gem "active_model_serializers"
|
13
|
-
gem "activerecord", "6.1.3"
|
14
|
-
gem "alba", path: '../'
|
15
|
-
gem "benchmark-ips"
|
16
|
-
gem "benchmark-memory"
|
17
|
-
gem "blueprinter"
|
18
|
-
gem "jbuilder"
|
19
|
-
gem 'turbostreamer'
|
20
|
-
gem "jserializer"
|
21
|
-
gem "jsonapi-serializer" # successor of fast_jsonapi
|
22
|
-
gem "multi_json"
|
23
|
-
gem "panko_serializer"
|
24
|
-
gem "pg"
|
25
|
-
gem "primalize"
|
26
|
-
gem "oj"
|
27
|
-
gem "representable"
|
28
|
-
gem "simple_ams"
|
29
|
-
gem "sqlite3"
|
30
|
-
end
|
31
|
-
|
32
|
-
# --- Test data model setup ---
|
33
|
-
|
34
|
-
require "pg"
|
35
|
-
require "active_record"
|
36
|
-
require "active_record/connection_adapters/postgresql_adapter"
|
37
|
-
require "logger"
|
38
|
-
require "oj"
|
39
|
-
require "sqlite3"
|
40
|
-
Oj.optimize_rails
|
41
|
-
|
42
|
-
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
|
43
|
-
# ActiveRecord::Base.logger = Logger.new($stdout)
|
44
|
-
|
45
|
-
ActiveRecord::Schema.define do
|
46
|
-
create_table :posts, force: true do |t|
|
47
|
-
t.string :body
|
48
|
-
end
|
49
|
-
|
50
|
-
create_table :comments, force: true do |t|
|
51
|
-
t.integer :post_id
|
52
|
-
t.string :body
|
53
|
-
t.integer :commenter_id
|
54
|
-
end
|
55
|
-
|
56
|
-
create_table :users, force: true do |t|
|
57
|
-
t.string :name
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
class Post < ActiveRecord::Base
|
62
|
-
has_many :comments
|
63
|
-
has_many :commenters, through: :comments, class_name: 'User', source: :commenter
|
64
|
-
|
65
|
-
def attributes
|
66
|
-
{id: nil, body: nil, commenter_names: commenter_names}
|
67
|
-
end
|
68
|
-
|
69
|
-
def commenter_names
|
70
|
-
commenters.pluck(:name)
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
class Comment < ActiveRecord::Base
|
75
|
-
belongs_to :post
|
76
|
-
belongs_to :commenter, class_name: 'User'
|
77
|
-
|
78
|
-
def attributes
|
79
|
-
{id: nil, body: nil}
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
class User < ActiveRecord::Base
|
84
|
-
has_many :comments
|
85
|
-
end
|
4
|
+
require_relative 'prep'
|
86
5
|
|
87
6
|
# --- Alba serializers ---
|
88
7
|
|
@@ -177,70 +96,8 @@ class JserializerPostSerializer < Jserializer::Base
|
|
177
96
|
end
|
178
97
|
end
|
179
98
|
|
180
|
-
|
181
|
-
# --- JSONAPI:Serializer serializers / (successor of fast_jsonapi) ---
|
182
|
-
|
183
|
-
class JsonApiStandardCommentSerializer
|
184
|
-
include JSONAPI::Serializer
|
185
|
-
|
186
|
-
attribute :id
|
187
|
-
attribute :body
|
188
|
-
end
|
189
|
-
|
190
|
-
class JsonApiStandardPostSerializer
|
191
|
-
include JSONAPI::Serializer
|
192
|
-
|
193
|
-
# set_type :post # optional
|
194
|
-
attribute :id
|
195
|
-
attribute :body
|
196
|
-
attribute :commenter_names
|
197
|
-
|
198
|
-
attribute :comments do |post|
|
199
|
-
post.comments.map { |comment| JsonApiSameFormatCommentSerializer.new(comment) }
|
200
|
-
end
|
201
|
-
end
|
202
|
-
|
203
|
-
# --- JSONAPI:Serializer serializers that format the code the same flat way as the other gems here ---
|
204
|
-
|
205
|
-
# code to convert from JSON:API output to "flat" JSON, like the other serializers build
|
206
|
-
class JsonApiSameFormatSerializer
|
207
|
-
include JSONAPI::Serializer
|
208
|
-
|
209
|
-
def as_json(*_options)
|
210
|
-
hash = serializable_hash
|
211
|
-
|
212
|
-
if hash[:data].is_a? Hash
|
213
|
-
hash[:data][:attributes]
|
214
|
-
|
215
|
-
elsif hash[:data].is_a? Array
|
216
|
-
hash[:data].pluck(:attributes)
|
217
|
-
|
218
|
-
elsif hash[:data].nil?
|
219
|
-
{ }
|
220
|
-
|
221
|
-
else
|
222
|
-
raise "unexpected data type #{hash[:data].class}"
|
223
|
-
end
|
224
|
-
end
|
225
|
-
end
|
226
|
-
|
227
|
-
class JsonApiSameFormatCommentSerializer < JsonApiSameFormatSerializer
|
228
|
-
attribute :id
|
229
|
-
attribute :body
|
230
|
-
end
|
231
|
-
|
232
|
-
class JsonApiSameFormatPostSerializer < JsonApiSameFormatSerializer
|
233
|
-
attribute :id
|
234
|
-
attribute :body
|
235
|
-
attribute :commenter_names
|
236
|
-
|
237
|
-
attribute :comments do |post|
|
238
|
-
post.comments.map { |comment| JsonApiSameFormatCommentSerializer.new(comment) }
|
239
|
-
end
|
240
|
-
end
|
241
|
-
|
242
99
|
# --- Panko serializers ---
|
243
|
-
|
100
|
+
|
244
101
|
require "panko_serializer"
|
245
102
|
|
246
103
|
class PankoCommentSerializer < Panko::Serializer
|
@@ -259,7 +116,7 @@ class PankoPostSerializer < Panko::Serializer
|
|
259
116
|
end
|
260
117
|
|
261
118
|
# --- Primalize serializers ---
|
262
|
-
|
119
|
+
|
263
120
|
class PrimalizeCommentResource < Primalize::Single
|
264
121
|
attributes id: integer, body: string
|
265
122
|
end
|
@@ -376,8 +233,6 @@ ams = Proc.new { AMSPostSerializer.new(post, {}).to_json }
|
|
376
233
|
blueprinter = Proc.new { PostBlueprint.render(post) }
|
377
234
|
jbuilder = Proc.new { post.to_builder.target! }
|
378
235
|
jserializer = Proc.new { JserializerPostSerializer.new(post).to_json }
|
379
|
-
jsonapi = proc { JsonApiStandardPostSerializer.new(post).to_json }
|
380
|
-
jsonapi_same_format = proc { JsonApiSameFormatPostSerializer.new(post).to_json }
|
381
236
|
panko = proc { PankoPostSerializer.new.serialize_to_json(post) }
|
382
237
|
primalize = proc { PrimalizePostResource.new(post).to_json }
|
383
238
|
rails = Proc.new { ActiveSupport::JSON.encode(post.serializable_hash(include: :comments)) }
|
@@ -395,8 +250,6 @@ puts "Serializer outputs ----------------------------------"
|
|
395
250
|
blueprinter: blueprinter,
|
396
251
|
jbuilder: jbuilder, # different order
|
397
252
|
jserializer: jserializer,
|
398
|
-
jsonapi: jsonapi, # nested JSON:API format
|
399
|
-
jsonapi_same_format: jsonapi_same_format,
|
400
253
|
panko: panko,
|
401
254
|
primalize: primalize,
|
402
255
|
rails: rails,
|
@@ -417,8 +270,6 @@ Benchmark.ips do |x|
|
|
417
270
|
x.report(:blueprinter, &blueprinter)
|
418
271
|
x.report(:jbuilder, &jbuilder)
|
419
272
|
x.report(:jserializer, &jserializer)
|
420
|
-
x.report(:jsonapi, &jsonapi)
|
421
|
-
x.report(:jsonapi_same_format, &jsonapi_same_format)
|
422
273
|
x.report(:panko, &panko)
|
423
274
|
x.report(:primalize, &primalize)
|
424
275
|
x.report(:rails, &rails)
|
@@ -438,8 +289,6 @@ Benchmark.memory do |x|
|
|
438
289
|
x.report(:blueprinter, &blueprinter)
|
439
290
|
x.report(:jbuilder, &jbuilder)
|
440
291
|
x.report(:jserializer, &jserializer)
|
441
|
-
x.report(:jsonapi, &jsonapi)
|
442
|
-
x.report(:jsonapi_same_format, &jsonapi_same_format)
|
443
292
|
x.report(:panko, &panko)
|
444
293
|
x.report(:primalize, &primalize)
|
445
294
|
x.report(:rails, &rails)
|
@@ -12,7 +12,7 @@ This guide is aimed at helping ActiveModelSerializers users transition to Alba,
|
|
12
12
|
|
13
13
|
## Example class
|
14
14
|
|
15
|
-
Example
|
15
|
+
Example class is inherited `ActiveRecord::Base`, because [serializing PORO with AMS is pretty hard](https://github.com/rails-api/active_model_serializers/blob/0-10-stable/docs/howto/serialize_poro.md).
|
16
16
|
|
17
17
|
```rb
|
18
18
|
class User < ActiveRecord::Base
|
@@ -175,7 +175,7 @@ class UserSerializer < ActiveModel::Serializer
|
|
175
175
|
has_many :articles, serializer: ArticleSerializer # For has_many relation
|
176
176
|
end
|
177
177
|
user = User.create!
|
178
|
-
user.
|
178
|
+
user.create_profile! email: email
|
179
179
|
user.articles.create! title: title, body: body
|
180
180
|
ActiveModelSerializers::SerializableResource.new(
|
181
181
|
user
|
data/docs/rails.md
CHANGED
@@ -14,7 +14,7 @@ You might want to add some configurations to initializer file such as `alba.rb`
|
|
14
14
|
```ruby
|
15
15
|
# alba.rb
|
16
16
|
Alba.backend = :active_support
|
17
|
-
Alba.enable_inference!(:active_support)
|
17
|
+
Alba.enable_inference!(with: :active_support)
|
18
18
|
```
|
19
19
|
|
20
20
|
You can also use `:oj_rails` for backend if you prefer using Oj.
|
data/lib/alba/association.rb
CHANGED
@@ -37,8 +37,10 @@ module Alba
|
|
37
37
|
@object = @condition.call(object, params, target) if @condition
|
38
38
|
return if @object.nil?
|
39
39
|
|
40
|
-
if @resource.is_a?(Proc)
|
41
|
-
to_h_with_each_resource(within, params)
|
40
|
+
if @resource.is_a?(Proc)
|
41
|
+
return to_h_with_each_resource(within, params) if @object.is_a?(Enumerable)
|
42
|
+
|
43
|
+
@resource.call(@object).new(@object, within: within, params: params).to_h
|
42
44
|
else
|
43
45
|
to_h_with_constantize_resource(within, params)
|
44
46
|
end
|
@@ -63,10 +65,10 @@ module Alba
|
|
63
65
|
klass.transform_keys(key_transformation)
|
64
66
|
klass.class_eval(&block)
|
65
67
|
klass
|
66
|
-
elsif Alba.
|
68
|
+
elsif Alba.inflector
|
67
69
|
Alba.infer_resource_class(@name, nesting: nesting)
|
68
70
|
else
|
69
|
-
raise ArgumentError, 'When Alba.
|
71
|
+
raise ArgumentError, 'When Alba.inflector is nil, either resource or block is required'
|
70
72
|
end
|
71
73
|
end
|
72
74
|
|
@@ -20,7 +20,7 @@ module Alba
|
|
20
20
|
return Alba::REMOVE_KEY unless condition_passes?(resource, object)
|
21
21
|
|
22
22
|
fetched_attribute = yield(@body)
|
23
|
-
return fetched_attribute if
|
23
|
+
return fetched_attribute if !with_two_arity_proc_condition
|
24
24
|
|
25
25
|
return Alba::REMOVE_KEY unless resource.instance_exec(object, attribute_from_association_body_or(fetched_attribute), &@condition)
|
26
26
|
|
data/lib/alba/layout.rb
CHANGED
@@ -8,8 +8,8 @@ module Alba
|
|
8
8
|
|
9
9
|
def_delegators :@resource, :object, :params, :serializable_hash, :to_h
|
10
10
|
|
11
|
-
# @
|
12
|
-
# @
|
11
|
+
# @param file [String] name of the layout file
|
12
|
+
# @param inline [Proc] a proc returning JSON string or a Hash representing JSON
|
13
13
|
def initialize(file:, inline:)
|
14
14
|
if file
|
15
15
|
raise ArgumentError, 'File layout must be a String representing filename' unless file.is_a?(String)
|
data/lib/alba/railtie.rb
ADDED
data/lib/alba/resource.rb
CHANGED
@@ -11,7 +11,7 @@ module Alba
|
|
11
11
|
module Resource
|
12
12
|
# @!parse include InstanceMethods
|
13
13
|
# @!parse extend ClassMethods
|
14
|
-
DSLS = {_attributes: {}, _key: nil, _key_for_collection: nil, _meta: nil, _transform_type: :none, _transforming_root_key: false, _on_error: nil, _on_nil: nil, _layout: nil, _collection_key: nil}.freeze # rubocop:disable Layout/LineLength
|
14
|
+
DSLS = {_attributes: {}, _key: nil, _key_for_collection: nil, _meta: nil, _transform_type: :none, _transforming_root_key: false, _key_transformation_cascade: true, _on_error: nil, _on_nil: nil, _layout: nil, _collection_key: nil}.freeze # rubocop:disable Layout/LineLength
|
15
15
|
private_constant :DSLS
|
16
16
|
|
17
17
|
WITHIN_DEFAULT = Object.new.freeze
|
@@ -41,7 +41,6 @@ module Alba
|
|
41
41
|
@object = object
|
42
42
|
@params = params
|
43
43
|
@within = within
|
44
|
-
@method_existence = {} # Cache for `respond_to?` result
|
45
44
|
DSLS.each_key { |name| instance_variable_set("@#{name}", self.class.__send__(name)) }
|
46
45
|
end
|
47
46
|
|
@@ -79,7 +78,6 @@ module Alba
|
|
79
78
|
#
|
80
79
|
# @param root_key [Symbol, nil, true]
|
81
80
|
# @param meta [Hash] metadata for this seialization
|
82
|
-
# @param symbolize_root_key [Boolean] determines if root key should be symbolized
|
83
81
|
# @return [Hash]
|
84
82
|
def as_json(root_key: nil, meta: {})
|
85
83
|
key = root_key.nil? ? fetch_key : root_key.to_s
|
@@ -149,7 +147,7 @@ module Alba
|
|
149
147
|
end
|
150
148
|
|
151
149
|
def _key_for_collection
|
152
|
-
if Alba.
|
150
|
+
if Alba.inflector
|
153
151
|
@_key_for_collection == true ? resource_name(pluralized: true) : @_key_for_collection.to_s
|
154
152
|
else
|
155
153
|
@_key_for_collection == true ? raise_root_key_inference_error : @_key_for_collection.to_s
|
@@ -158,7 +156,7 @@ module Alba
|
|
158
156
|
|
159
157
|
# @return [String]
|
160
158
|
def _key
|
161
|
-
if Alba.
|
159
|
+
if Alba.inflector
|
162
160
|
@_key == true ? resource_name(pluralized: false) : @_key.to_s
|
163
161
|
else
|
164
162
|
@_key == true ? raise_root_key_inference_error : @_key.to_s
|
@@ -174,7 +172,7 @@ module Alba
|
|
174
172
|
end
|
175
173
|
|
176
174
|
def raise_root_key_inference_error
|
177
|
-
raise Alba::Error, 'You must
|
175
|
+
raise Alba::Error, 'You must set inflector when setting root key as true.'
|
178
176
|
end
|
179
177
|
|
180
178
|
def transforming_root_key?
|
@@ -182,27 +180,27 @@ module Alba
|
|
182
180
|
end
|
183
181
|
|
184
182
|
def converter
|
185
|
-
lambda do |
|
186
|
-
attributes_to_hash(
|
183
|
+
lambda do |obj|
|
184
|
+
attributes_to_hash(obj, {})
|
187
185
|
end
|
188
186
|
end
|
189
187
|
|
190
188
|
def collection_converter
|
191
|
-
lambda do |
|
189
|
+
lambda do |obj, a|
|
192
190
|
a << {}
|
193
191
|
h = a.last
|
194
|
-
attributes_to_hash(
|
192
|
+
attributes_to_hash(obj, h)
|
195
193
|
a
|
196
194
|
end
|
197
195
|
end
|
198
196
|
|
199
|
-
def attributes_to_hash(
|
197
|
+
def attributes_to_hash(obj, hash)
|
200
198
|
attributes.each do |key, attribute|
|
201
|
-
set_key_and_attribute_body_from(
|
199
|
+
set_key_and_attribute_body_from(obj, key, attribute, hash)
|
202
200
|
rescue ::Alba::Error, FrozenError, TypeError
|
203
201
|
raise
|
204
202
|
rescue StandardError => e
|
205
|
-
handle_error(e,
|
203
|
+
handle_error(e, obj, key, attribute, hash)
|
206
204
|
end
|
207
205
|
hash
|
208
206
|
end
|
@@ -213,20 +211,28 @@ module Alba
|
|
213
211
|
@_attributes
|
214
212
|
end
|
215
213
|
|
216
|
-
|
214
|
+
# Default implementation for selecting attributes
|
215
|
+
# Override this method to filter attributes based on key and value
|
216
|
+
def select(_key, _value)
|
217
|
+
true
|
218
|
+
end
|
219
|
+
|
220
|
+
def set_key_and_attribute_body_from(obj, key, attribute, hash)
|
217
221
|
key = transform_key(key)
|
218
|
-
value = fetch_attribute(
|
222
|
+
value = fetch_attribute(obj, key, attribute)
|
223
|
+
return unless select(key, value)
|
224
|
+
|
219
225
|
hash[key] = value unless value == Alba::REMOVE_KEY
|
220
226
|
end
|
221
227
|
|
222
|
-
def handle_error(error,
|
228
|
+
def handle_error(error, obj, key, attribute, hash)
|
223
229
|
on_error = @_on_error || :raise
|
224
230
|
case on_error # rubocop:disable Style/MissingElse
|
225
231
|
when :raise, nil then raise(error)
|
226
232
|
when :nullify then hash[key] = nil
|
227
233
|
when :ignore then nil
|
228
234
|
when Proc
|
229
|
-
key, value = on_error.call(error,
|
235
|
+
key, value = on_error.call(error, obj, key, attribute, self.class)
|
230
236
|
hash[key] = value
|
231
237
|
end
|
232
238
|
end
|
@@ -237,7 +243,7 @@ module Alba
|
|
237
243
|
return key if @_transform_type == :none || key.empty? # We can skip transformation
|
238
244
|
|
239
245
|
inflector = Alba.inflector
|
240
|
-
raise Alba::Error, 'Inflector is nil. You
|
246
|
+
raise Alba::Error, 'Inflector is nil. You must set inflector before transforming keys.' unless inflector
|
241
247
|
|
242
248
|
case @_transform_type # rubocop:disable Style/MissingElse
|
243
249
|
when :camel then inflector.camelize(key)
|
@@ -247,23 +253,23 @@ module Alba
|
|
247
253
|
end
|
248
254
|
end
|
249
255
|
|
250
|
-
def fetch_attribute(
|
256
|
+
def fetch_attribute(obj, key, attribute) # rubocop:disable Metrics/CyclomaticComplexity
|
251
257
|
value = case attribute
|
252
|
-
when Symbol then fetch_attribute_from_object_and_resource(
|
253
|
-
when Proc then instance_exec(
|
254
|
-
when Alba::Association then yield_if_within(attribute.name.to_sym) { |within| attribute.to_h(
|
255
|
-
when TypedAttribute, NestedAttribute then attribute.value(
|
256
|
-
when ConditionalAttribute then attribute.with_passing_condition(resource: self, object:
|
258
|
+
when Symbol then fetch_attribute_from_object_and_resource(obj, attribute)
|
259
|
+
when Proc then instance_exec(obj, &attribute)
|
260
|
+
when Alba::Association then yield_if_within(attribute.name.to_sym) { |within| attribute.to_h(obj, params: params, within: within) }
|
261
|
+
when TypedAttribute, NestedAttribute then attribute.value(obj)
|
262
|
+
when ConditionalAttribute then attribute.with_passing_condition(resource: self, object: obj) { |attr| fetch_attribute(obj, key, attr) }
|
257
263
|
else
|
258
264
|
raise ::Alba::Error, "Unsupported type of attribute: #{attribute.class}"
|
259
265
|
end
|
260
|
-
value.nil? && nil_handler ? instance_exec(
|
266
|
+
value.nil? && nil_handler ? instance_exec(obj, key, attribute, &nil_handler) : value
|
261
267
|
end
|
262
268
|
|
263
|
-
def fetch_attribute_from_object_and_resource(
|
264
|
-
|
265
|
-
|
266
|
-
|
269
|
+
def fetch_attribute_from_object_and_resource(obj, attribute)
|
270
|
+
obj.__send__(attribute)
|
271
|
+
rescue NoMethodError
|
272
|
+
__send__(attribute, obj)
|
267
273
|
end
|
268
274
|
|
269
275
|
def nil_handler
|
@@ -313,7 +319,7 @@ module Alba
|
|
313
319
|
# @param attrs_with_types [Hash<[Symbol, String], [Array<Symbol, Proc>, Symbol]>]
|
314
320
|
# attributes with name in its key and type and optional type converter in its value
|
315
321
|
# @return [void]
|
316
|
-
def attributes(*attrs, if: nil, **attrs_with_types)
|
322
|
+
def attributes(*attrs, if: nil, **attrs_with_types)
|
317
323
|
if_value = binding.local_variable_get(:if)
|
318
324
|
assign_attributes(attrs, if_value)
|
319
325
|
assign_attributes_with_types(attrs_with_types, if_value)
|
@@ -435,8 +441,8 @@ module Alba
|
|
435
441
|
|
436
442
|
# Set layout
|
437
443
|
#
|
438
|
-
# @
|
439
|
-
# @
|
444
|
+
# @param file [String] name of the layout file
|
445
|
+
# @param inline [Proc] a proc returning JSON string or a Hash representing JSON
|
440
446
|
def layout(file: nil, inline: nil)
|
441
447
|
@_layout = Layout.new(file: file, inline: inline)
|
442
448
|
end
|
data/lib/alba/version.rb
CHANGED
data/lib/alba.rb
CHANGED
@@ -4,13 +4,15 @@ require_relative 'alba/errors'
|
|
4
4
|
require_relative 'alba/resource'
|
5
5
|
require_relative 'alba/deprecation'
|
6
6
|
|
7
|
+
require_relative 'alba/railtie' if defined?(Rails::Railtie)
|
8
|
+
|
7
9
|
# Core module
|
8
10
|
module Alba
|
9
11
|
class << self
|
10
|
-
attr_reader :backend, :encoder
|
12
|
+
attr_reader :backend, :encoder
|
11
13
|
|
12
|
-
#
|
13
|
-
|
14
|
+
# Getter for inflector, a module responsible for inflecting strings
|
15
|
+
attr_reader :inflector
|
14
16
|
|
15
17
|
# Set the backend, which actually serializes object into JSON
|
16
18
|
#
|
@@ -54,14 +56,36 @@ module Alba
|
|
54
56
|
# @param with [Symbol, Class, Module] inflector
|
55
57
|
# When it's a Symbol, it sets inflector with given name
|
56
58
|
# When it's a Class or a Module, it sets given object to inflector
|
59
|
+
# @deprecated Use {#inflector=} instead
|
57
60
|
def enable_inference!(with:)
|
61
|
+
Alba::Deprecation.warn('Alba.enable_inference! is deprecated. Use `Alba.inflector=` instead.')
|
58
62
|
@inflector = inflector_from(with)
|
59
63
|
@inferring = true
|
60
64
|
end
|
61
65
|
|
62
66
|
# Disable inference for key and resource name
|
67
|
+
#
|
68
|
+
# @deprecated Use {#inflector=} instead
|
63
69
|
def disable_inference!
|
70
|
+
Alba::Deprecation.warn('Alba.disable_inference! is deprecated. Use `Alba.inflector = nil` instead.')
|
64
71
|
@inferring = false
|
72
|
+
@inflector = nil
|
73
|
+
end
|
74
|
+
|
75
|
+
# @deprecated Use {#inflector} instead
|
76
|
+
# @return [Boolean] whether inference is enabled or not
|
77
|
+
def inferring
|
78
|
+
Alba::Deprecation.warn('Alba.inferring is deprecated. Use `Alba.inflector` instead.')
|
79
|
+
@inferring
|
80
|
+
end
|
81
|
+
|
82
|
+
# Set an inflector
|
83
|
+
#
|
84
|
+
# @param inflector [Symbol, Class, Module] inflector
|
85
|
+
# When it's a Symbol, it accepts `:default`, `:active_support` or `:dry`
|
86
|
+
# When it's a Class or a Module, it should have some methods, see {Alba::DefaultInflector}
|
87
|
+
def inflector=(inflector)
|
88
|
+
@inflector = inflector_from(inflector)
|
65
89
|
end
|
66
90
|
|
67
91
|
# @param block [Block] resource body
|
@@ -77,7 +101,7 @@ module Alba
|
|
77
101
|
# @param nesting [String, nil] namespace Alba tries to find resource class in
|
78
102
|
# @return [Class<Alba::Resource>] resource class
|
79
103
|
def infer_resource_class(name, nesting: nil)
|
80
|
-
raise Alba::Error, 'Inference is disabled so Alba cannot infer resource name.
|
104
|
+
raise Alba::Error, 'Inference is disabled so Alba cannot infer resource name. Set inflector before use.' unless Alba.inflector
|
81
105
|
|
82
106
|
const_parent = nesting.nil? ? Object : Object.const_get(nesting)
|
83
107
|
const_parent.const_get("#{inflector.classify(name)}Resource")
|
@@ -95,6 +119,7 @@ module Alba
|
|
95
119
|
|
96
120
|
def inflector_from(name_or_module)
|
97
121
|
case name_or_module
|
122
|
+
when nil then nil
|
98
123
|
when :default, :active_support
|
99
124
|
require_relative 'alba/default_inflector'
|
100
125
|
Alba::DefaultInflector
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: alba
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0
|
4
|
+
version: 2.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- OKURA Masafumi
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-02-17 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Alba is the fastest JSON serializer for Ruby. It focuses on performance,
|
14
14
|
flexibility and usability.
|
@@ -40,6 +40,7 @@ files:
|
|
40
40
|
- alba.gemspec
|
41
41
|
- benchmark/README.md
|
42
42
|
- benchmark/collection.rb
|
43
|
+
- benchmark/prep.rb
|
43
44
|
- benchmark/single_resource.rb
|
44
45
|
- bin/console
|
45
46
|
- bin/setup
|
@@ -58,6 +59,7 @@ files:
|
|
58
59
|
- lib/alba/errors.rb
|
59
60
|
- lib/alba/layout.rb
|
60
61
|
- lib/alba/nested_attribute.rb
|
62
|
+
- lib/alba/railtie.rb
|
61
63
|
- lib/alba/resource.rb
|
62
64
|
- lib/alba/typed_attribute.rb
|
63
65
|
- lib/alba/version.rb
|
@@ -82,14 +84,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
82
84
|
requirements:
|
83
85
|
- - ">="
|
84
86
|
- !ruby/object:Gem::Version
|
85
|
-
version: 2.
|
87
|
+
version: 2.7.0
|
86
88
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
87
89
|
requirements:
|
88
90
|
- - ">="
|
89
91
|
- !ruby/object:Gem::Version
|
90
92
|
version: '0'
|
91
93
|
requirements: []
|
92
|
-
rubygems_version: 3.
|
94
|
+
rubygems_version: 3.4.6
|
93
95
|
signing_key:
|
94
96
|
specification_version: 4
|
95
97
|
summary: Alba is the fastest JSON serializer for Ruby.
|