alba 0.11.1 → 1.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/lib/alba/many.rb CHANGED
@@ -9,9 +9,12 @@ module Alba
9
9
  # @param params [Hash] user-given Hash for arbitrary data
10
10
  # @return [Array<Hash>]
11
11
  def to_hash(target, params: {})
12
- objects = target.public_send(@name)
13
- objects = @condition.call(objects, params) if @condition
14
- objects.map { |o| @resource.new(o, params: params).to_hash }
12
+ @object = target.public_send(@name)
13
+ @object = @condition.call(@object, params) if @condition
14
+ return if @object.nil?
15
+
16
+ @resource = constantize(@resource)
17
+ @object.map { |o| @resource.new(o, params: params).to_hash }
15
18
  end
16
19
  end
17
20
  end
data/lib/alba/one.rb CHANGED
@@ -9,8 +9,11 @@ module Alba
9
9
  # @param params [Hash] user-given Hash for arbitrary data
10
10
  # @return [Hash]
11
11
  def to_hash(target, params: {})
12
- object = target.public_send(@name)
13
- object = @condition.call(object, params) if @condition
12
+ @object = target.public_send(@name)
13
+ @object = @condition.call(object, params) if @condition
14
+ return if @object.nil?
15
+
16
+ @resource = constantize(@resource)
14
17
  @resource.new(object, params: params).to_hash
15
18
  end
16
19
  end
data/lib/alba/resource.rb CHANGED
@@ -1,4 +1,3 @@
1
- require_relative 'serializer'
2
1
  require_relative 'one'
3
2
  require_relative 'many'
4
3
 
@@ -7,7 +6,7 @@ module Alba
7
6
  module Resource
8
7
  # @!parse include InstanceMethods
9
8
  # @!parse extend ClassMethods
10
- DSLS = {_attributes: {}, _serializer: nil, _key: nil}.freeze
9
+ DSLS = {_attributes: {}, _key: nil, _transform_keys: nil, _on_error: nil}.freeze
11
10
  private_constant :DSLS
12
11
 
13
12
  # @private
@@ -25,7 +24,7 @@ module Alba
25
24
 
26
25
  # Instance methods
27
26
  module InstanceMethods
28
- attr_reader :object, :_key, :params
27
+ attr_reader :object, :params
29
28
 
30
29
  # @param object [Object] the object to be serialized
31
30
  # @param params [Hash] user-given Hash for arbitrary data
@@ -35,22 +34,14 @@ module Alba
35
34
  DSLS.each_key { |name| instance_variable_set("@#{name}", self.class.public_send(name)) }
36
35
  end
37
36
 
38
- # Get serializer with `with` argument and serialize self with it
37
+ # Serialize object into JSON string
39
38
  #
40
- # @param with [nil, Proc, Alba::Serializer] selializer
39
+ # @param key [Symbol]
41
40
  # @return [String] serialized JSON string
42
- def serialize(with: nil)
43
- serializer = case with
44
- when nil
45
- @_serializer || empty_serializer
46
- when ->(obj) { obj.is_a?(Class) && obj <= Alba::Serializer }
47
- with
48
- when Proc
49
- inline_extended_serializer(with)
50
- else
51
- raise ArgumentError, 'Unexpected type for with, possible types are Class or Proc'
52
- end
53
- serializer.new(self).serialize
41
+ def serialize(key: nil)
42
+ key = key.nil? ? _key : key
43
+ hash = key && key != '' ? {key.to_s => serializable_hash} : serializable_hash
44
+ Alba.encoder.call(hash)
54
45
  end
55
46
 
56
47
  # A Hash for serialization
@@ -61,40 +52,88 @@ module Alba
61
52
  end
62
53
  alias to_hash serializable_hash
63
54
 
64
- # @return [Symbol]
65
- def key
66
- @_key || self.class.name.delete_suffix('Resource').downcase.gsub(/:{2}/, '_').to_sym
67
- end
68
-
69
55
  private
70
56
 
57
+ # @return [String]
58
+ def _key
59
+ if @_key == true && Alba.inferring
60
+ demodulized = ActiveSupport::Inflector.demodulize(self.class.name)
61
+ meth = collection? ? :tableize : :singularize
62
+ ActiveSupport::Inflector.public_send(meth, demodulized.delete_suffix('Resource').downcase)
63
+ else
64
+ @_key.to_s
65
+ end
66
+ end
67
+
71
68
  def converter
72
- lambda do |resource|
73
- @_attributes.transform_values do |attribute|
74
- case attribute
75
- when Symbol
76
- resource.public_send attribute
77
- when Proc
78
- instance_exec(resource, &attribute)
79
- when Alba::One, Alba::Many
80
- attribute.to_hash(resource, params: params)
69
+ lambda do |object|
70
+ arrays = @_attributes.map do |key, attribute|
71
+ key = transform_key(key)
72
+ if attribute.is_a?(Array) # Conditional
73
+ conditional_attribute(object, key, attribute)
81
74
  else
82
- raise ::Alba::Error, "Unsupported type of attribute: #{attribute.class}"
75
+ [key, fetch_attribute(object, attribute)]
83
76
  end
77
+ rescue ::Alba::Error, FrozenError
78
+ raise
79
+ rescue StandardError => e
80
+ handle_error(e, object, key, attribute)
84
81
  end
82
+ arrays.reject(&:empty?).to_h
83
+ end
84
+ end
85
+
86
+ def conditional_attribute(object, key, attribute)
87
+ condition = attribute.last
88
+ arity = condition.arity
89
+ return [] if arity <= 1 && !condition.call(object)
90
+
91
+ fetched_attribute = fetch_attribute(object, attribute.first)
92
+ attr = if attribute.first.is_a?(Alba::Association)
93
+ attribute.first.object
94
+ else
95
+ fetched_attribute
96
+ end
97
+ return [] if arity >= 2 && !condition.call(object, attr)
98
+
99
+ [key, fetched_attribute]
100
+ end
101
+
102
+ def handle_error(error, object, key, attribute)
103
+ on_error = @_on_error || Alba._on_error
104
+ case on_error
105
+ when :raise, nil
106
+ raise
107
+ when :nullify
108
+ [key, nil]
109
+ when :ignore
110
+ []
111
+ when Proc
112
+ on_error.call(error, object, key, attribute, self.class)
113
+ else
114
+ raise ::Alba::Error, "Unknown on_error: #{on_error.inspect}"
85
115
  end
86
116
  end
87
117
 
88
- def empty_serializer
89
- klass = Class.new
90
- klass.include Alba::Serializer
91
- klass
118
+ # Override this method to supply custom key transform method
119
+ def transform_key(key)
120
+ return key unless @_transform_keys
121
+
122
+ require_relative 'key_transformer'
123
+ KeyTransformer.transform(key, @_transform_keys)
92
124
  end
93
125
 
94
- def inline_extended_serializer(with)
95
- klass = empty_serializer
96
- klass.class_eval(&with)
97
- klass
126
+ def fetch_attribute(object, attribute)
127
+ case attribute
128
+ when Symbol
129
+ object.public_send attribute
130
+ when Proc
131
+ instance_exec(object, &attribute)
132
+ when Alba::One, Alba::Many
133
+ attribute.to_hash(object, params: params)
134
+ else
135
+ raise ::Alba::Error, "Unsupported type of attribute: #{attribute.class}"
136
+ end
98
137
  end
99
138
 
100
139
  def collection?
@@ -115,19 +154,24 @@ module Alba
115
154
  # Set multiple attributes at once
116
155
  #
117
156
  # @param attrs [Array<String, Symbol>]
118
- def attributes(*attrs)
119
- attrs.each { |attr_name| @_attributes[attr_name.to_sym] = attr_name.to_sym }
157
+ # @param options [Hash] option hash including `if` that is a condition to render these attributes
158
+ def attributes(*attrs, **options)
159
+ attrs.each do |attr_name|
160
+ attr = options[:if] ? [attr_name.to_sym, options[:if]] : attr_name.to_sym
161
+ @_attributes[attr_name.to_sym] = attr
162
+ end
120
163
  end
121
164
 
122
165
  # Set an attribute with the given block
123
166
  #
124
167
  # @param name [String, Symbol] key name
168
+ # @param options [Hash] option hash including `if` that is a condition to render
125
169
  # @param block [Block] the block called during serialization
126
170
  # @raise [ArgumentError] if block is absent
127
- def attribute(name, &block)
171
+ def attribute(name, **options, &block)
128
172
  raise ArgumentError, 'No block given in attribute method' unless block
129
173
 
130
- @_attributes[name.to_sym] = block
174
+ @_attributes[name.to_sym] = options[:if] ? [block, options[:if]] : block
131
175
  end
132
176
 
133
177
  # Set One association
@@ -136,11 +180,15 @@ module Alba
136
180
  # @param condition [Proc]
137
181
  # @param resource [Class<Alba::Resource>]
138
182
  # @param key [String, Symbol] used as key when given
183
+ # @param options [Hash] option hash including `if` that is a condition to render
139
184
  # @param block [Block]
140
185
  # @see Alba::One#initialize
141
- def one(name, condition = nil, resource: nil, key: nil, &block)
142
- @_attributes[key&.to_sym || name.to_sym] = One.new(name: name, condition: condition, resource: resource, &block)
186
+ def one(name, condition = nil, resource: nil, key: nil, **options, &block)
187
+ nesting = self.name&.rpartition('::')&.first
188
+ one = One.new(name: name, condition: condition, resource: resource, nesting: nesting, &block)
189
+ @_attributes[key&.to_sym || name.to_sym] = options[:if] ? [one, options[:if]] : one
143
190
  end
191
+ alias has_one one
144
192
 
145
193
  # Set Many association
146
194
  #
@@ -148,24 +196,27 @@ module Alba
148
196
  # @param condition [Proc]
149
197
  # @param resource [Class<Alba::Resource>]
150
198
  # @param key [String, Symbol] used as key when given
199
+ # @param options [Hash] option hash including `if` that is a condition to render
151
200
  # @param block [Block]
152
201
  # @see Alba::Many#initialize
153
- def many(name, condition = nil, resource: nil, key: nil, &block)
154
- @_attributes[key&.to_sym || name.to_sym] = Many.new(name: name, condition: condition, resource: resource, &block)
155
- end
156
-
157
- # Set serializer for the resource
158
- #
159
- # @param name [Alba::Serializer]
160
- def serializer(name)
161
- @_serializer = name <= Alba::Serializer ? name : nil
202
+ def many(name, condition = nil, resource: nil, key: nil, **options, &block)
203
+ nesting = self.name&.rpartition('::')&.first
204
+ many = Many.new(name: name, condition: condition, resource: resource, nesting: nesting, &block)
205
+ @_attributes[key&.to_sym || name.to_sym] = options[:if] ? [many, options[:if]] : many
162
206
  end
207
+ alias has_many many
163
208
 
164
209
  # Set key
165
210
  #
166
211
  # @param key [String, Symbol]
167
212
  def key(key)
168
- @_key = key.to_sym
213
+ @_key = key.respond_to?(:to_sym) ? key.to_sym : key
214
+ end
215
+
216
+ # Set key to true
217
+ #
218
+ def key!
219
+ @_key = true
169
220
  end
170
221
 
171
222
  # Delete attributes
@@ -177,6 +228,24 @@ module Alba
177
228
  @_attributes.delete(attr_name.to_sym)
178
229
  end
179
230
  end
231
+
232
+ # Transform keys as specified type
233
+ #
234
+ # @param type [String, Symbol]
235
+ def transform_keys(type)
236
+ @_transform_keys = type.to_sym
237
+ end
238
+
239
+ # Set error handler
240
+ #
241
+ # @param [Symbol] handler
242
+ # @param [Block]
243
+ def on_error(handler = nil, &block)
244
+ raise ArgumentError, 'You cannot specify error handler with both Symbol and block' if handler && block
245
+ raise ArgumentError, 'You must specify error handler with either Symbol or block' unless handler || block
246
+
247
+ @_on_error = handler || block
248
+ end
180
249
  end
181
250
  end
182
251
  end
data/lib/alba/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Alba
2
- VERSION = '0.11.1'.freeze
2
+ VERSION = '1.0.1'.freeze
3
3
  end
data/sider.yml CHANGED
@@ -7,6 +7,7 @@ linter:
7
7
  - "rubocop-minitest"
8
8
  - "rubocop-performance"
9
9
  - "rubocop-sensible"
10
+ - "rubocop-rake"
10
11
  safe: false
11
12
 
12
13
  # # https://help.sider.review/tools/ruby/reek
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: 0.11.1
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - OKURA Masafumi
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-09-02 00:00:00.000000000 Z
11
+ date: 2021-04-15 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Alba is designed to be a simple, easy to use and fast alternative to
14
14
  existing JSON serializers. Its performance is better than almost all gems which
@@ -19,25 +19,29 @@ executables: []
19
19
  extensions: []
20
20
  extra_rdoc_files: []
21
21
  files:
22
+ - ".github/workflows/main.yml"
22
23
  - ".gitignore"
23
24
  - ".rubocop.yml"
24
- - ".travis.yml"
25
25
  - ".yardopts"
26
+ - CHANGELOG.md
26
27
  - CODE_OF_CONDUCT.md
27
28
  - Gemfile
28
- - Gemfile.lock
29
29
  - LICENSE.txt
30
30
  - README.md
31
31
  - Rakefile
32
32
  - alba.gemspec
33
+ - benchmark/local.rb
33
34
  - bin/console
34
35
  - bin/setup
36
+ - gemfiles/all.gemfile
37
+ - gemfiles/without_active_support.gemfile
38
+ - gemfiles/without_oj.gemfile
35
39
  - lib/alba.rb
36
40
  - lib/alba/association.rb
41
+ - lib/alba/key_transformer.rb
37
42
  - lib/alba/many.rb
38
43
  - lib/alba/one.rb
39
44
  - lib/alba/resource.rb
40
- - lib/alba/serializer.rb
41
45
  - lib/alba/version.rb
42
46
  - sider.yml
43
47
  homepage: https://github.com/okuramasafumi/alba
@@ -46,7 +50,7 @@ licenses:
46
50
  metadata:
47
51
  homepage_uri: https://github.com/okuramasafumi/alba
48
52
  source_code_uri: https://github.com/okuramasafumi/alba
49
- changelog_uri: https://github.com/okuramasafumi/alba/CHANGELOG.md
53
+ changelog_uri: https://github.com/okuramasafumi/alba/blob/master/CHANGELOG.md
50
54
  post_install_message:
51
55
  rdoc_options: []
52
56
  require_paths:
@@ -55,14 +59,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
55
59
  requirements:
56
60
  - - ">="
57
61
  - !ruby/object:Gem::Version
58
- version: 2.5.7
62
+ version: 2.5.0
59
63
  required_rubygems_version: !ruby/object:Gem::Requirement
60
64
  requirements:
61
65
  - - ">="
62
66
  - !ruby/object:Gem::Version
63
67
  version: '0'
64
68
  requirements: []
65
- rubygems_version: 3.1.4
69
+ rubygems_version: 3.2.14
66
70
  signing_key:
67
71
  specification_version: 4
68
72
  summary: Alba is the fastest JSON serializer for Ruby.
data/.travis.yml DELETED
@@ -1,10 +0,0 @@
1
- ---
2
- language: ruby
3
- cache: bundler
4
- rvm:
5
- - 2.5.8
6
- - 2.6.6
7
- - 2.7.1
8
- # - ruby-head # oj doesn't work with Ruby 2.8.0
9
- - truffleruby
10
- before_install: gem install bundler -v 2.1.4
data/Gemfile.lock DELETED
@@ -1,89 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- alba (0.11.1)
5
-
6
- GEM
7
- remote: https://rubygems.org/
8
- specs:
9
- activesupport (6.0.3.2)
10
- concurrent-ruby (~> 1.0, >= 1.0.2)
11
- i18n (>= 0.7, < 2)
12
- minitest (~> 5.1)
13
- tzinfo (~> 1.1)
14
- zeitwerk (~> 2.2, >= 2.2.2)
15
- ast (2.4.1)
16
- concurrent-ruby (1.1.6)
17
- coveralls (0.8.23)
18
- json (>= 1.8, < 3)
19
- simplecov (~> 0.16.1)
20
- term-ansicolor (~> 1.3)
21
- thor (>= 0.19.4, < 2.0)
22
- tins (~> 1.6)
23
- docile (1.3.2)
24
- i18n (1.8.5)
25
- concurrent-ruby (~> 1.0)
26
- json (2.3.1)
27
- minitest (5.14.2)
28
- oj (3.10.13)
29
- parallel (1.19.2)
30
- parser (2.7.1.4)
31
- ast (~> 2.4.1)
32
- rainbow (3.0.0)
33
- rake (13.0.1)
34
- regexp_parser (1.7.1)
35
- rexml (3.2.4)
36
- rubocop (0.90.0)
37
- parallel (~> 1.10)
38
- parser (>= 2.7.1.1)
39
- rainbow (>= 2.2.2, < 4.0)
40
- regexp_parser (>= 1.7)
41
- rexml
42
- rubocop-ast (>= 0.3.0, < 1.0)
43
- ruby-progressbar (~> 1.7)
44
- unicode-display_width (>= 1.4.0, < 2.0)
45
- rubocop-ast (0.3.0)
46
- parser (>= 2.7.1.4)
47
- rubocop-minitest (0.10.1)
48
- rubocop (>= 0.87)
49
- rubocop-performance (1.7.1)
50
- rubocop (>= 0.82.0)
51
- rubocop-sensible (0.3.0)
52
- rubocop (>= 0.60.0)
53
- ruby-progressbar (1.10.1)
54
- simplecov (0.16.1)
55
- docile (~> 1.1)
56
- json (>= 1.8, < 3)
57
- simplecov-html (~> 0.10.0)
58
- simplecov-html (0.10.2)
59
- sync (0.5.0)
60
- term-ansicolor (1.7.1)
61
- tins (~> 1.0)
62
- thor (1.0.1)
63
- thread_safe (0.3.6)
64
- tins (1.25.0)
65
- sync
66
- tzinfo (1.2.7)
67
- thread_safe (~> 0.1)
68
- unicode-display_width (1.7.0)
69
- yard (0.9.25)
70
- zeitwerk (2.4.0)
71
-
72
- PLATFORMS
73
- ruby
74
-
75
- DEPENDENCIES
76
- activesupport
77
- alba!
78
- coveralls
79
- minitest (~> 5.14)
80
- oj (~> 3.10)
81
- rake (~> 13.0)
82
- rubocop (>= 0.79.0)
83
- rubocop-minitest (~> 0.10.1)
84
- rubocop-performance (~> 1.7.1)
85
- rubocop-sensible (~> 0.3.0)
86
- yard
87
-
88
- BUNDLED WITH
89
- 2.1.4