alba 0.11.1 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
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