alba 0.8.0 → 0.9.0

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: 592412974accb17b359ab871e293e2c839519f7cfffb31a04485bfe261b88202
4
- data.tar.gz: d8fbb2ec751eac8e7dbe278e8993da6641eaa88f302b3438055b2062c08bd229
3
+ metadata.gz: 3a92c56eee7615f823efc93ed7edb87ba6c47a4bcf8e8b11e48edfc62c380545
4
+ data.tar.gz: 310eca11a4a5a83287d3d87c1346c2436900bf1283e5642baa3878c69087812d
5
5
  SHA512:
6
- metadata.gz: 97c3fc639d1d6b7fa588c78fa84f4be7318982ebf3154b9037e1b9c6ac38d1d9a0528bcf45618a5d1b1dc009df30441589eb27d1bdfbd8afaca1e61a0708e31e
7
- data.tar.gz: 7abd201ba80bdfff652840e948e8aede24e90d0cfc26722f08044916f239387fd0bada0a1e9a0d71cf252ced9400d1549124349a1070ea7bd8c8e5a7be0ebc38
6
+ metadata.gz: 2fed0c44e90d2d876b2db635867741b3520a21c470acfb07a0218b7e0b627ce718edb1da964dfafdf0c79b3fe16bf7502a4fd873e998778c5307bd0c37d0c7c0
7
+ data.tar.gz: 334edc5389f1fceba2ee880a322f894ccb145533793a885777ac29c08c273b5dda9a31cf9885b493b6ec3fabcf859a1d1643ce37b848c225db66b658b36e3e34
@@ -16,6 +16,10 @@ AllCops:
16
16
  Layout/SpaceInsideHashLiteralBraces:
17
17
  EnforcedStyle: no_space
18
18
 
19
+ Metrics/ClassLength:
20
+ Exclude:
21
+ - 'test/alba_test.rb'
22
+
19
23
  Metrics/MethodLength:
20
24
  Max: 20
21
25
 
@@ -5,4 +5,5 @@ rvm:
5
5
  - 2.5.8
6
6
  - 2.6.6
7
7
  - 2.7.1
8
+ - truffleruby
8
9
  before_install: gem install bundler -v 2.1.4
data/Gemfile CHANGED
@@ -3,9 +3,10 @@ source 'https://rubygems.org'
3
3
  # Specify your gem's dependencies in alba.gemspec
4
4
  gemspec
5
5
 
6
+ gem 'activesupport', require: false
6
7
  gem 'coveralls', require: false
7
8
  gem 'minitest', '~> 5.0'
8
- gem 'oj', '~> 3.10'
9
+ gem 'oj', '~> 3.10', platforms: :ruby
9
10
  gem 'rake', '~> 13.0'
10
11
  gem 'rubocop', '>= 0.79.0', require: false
11
12
  gem 'rubocop-minitest', '~> 0.10.1', require: false
@@ -1,12 +1,19 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- alba (0.8.0)
4
+ alba (0.9.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
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)
9
15
  ast (2.4.1)
16
+ concurrent-ruby (1.1.6)
10
17
  coveralls (0.8.23)
11
18
  json (>= 1.8, < 3)
12
19
  simplecov (~> 0.16.1)
@@ -14,6 +21,8 @@ GEM
14
21
  thor (>= 0.19.4, < 2.0)
15
22
  tins (~> 1.6)
16
23
  docile (1.3.2)
24
+ i18n (1.8.5)
25
+ concurrent-ruby (~> 1.0)
17
26
  json (2.3.1)
18
27
  minitest (5.14.1)
19
28
  oj (3.10.8)
@@ -24,17 +33,17 @@ GEM
24
33
  rake (13.0.1)
25
34
  regexp_parser (1.7.1)
26
35
  rexml (3.2.4)
27
- rubocop (0.88.0)
36
+ rubocop (0.89.1)
28
37
  parallel (~> 1.10)
29
38
  parser (>= 2.7.1.1)
30
39
  rainbow (>= 2.2.2, < 4.0)
31
40
  regexp_parser (>= 1.7)
32
41
  rexml
33
- rubocop-ast (>= 0.1.0, < 1.0)
42
+ rubocop-ast (>= 0.3.0, < 1.0)
34
43
  ruby-progressbar (~> 1.7)
35
44
  unicode-display_width (>= 1.4.0, < 2.0)
36
- rubocop-ast (0.2.0)
37
- parser (>= 2.7.0.1)
45
+ rubocop-ast (0.3.0)
46
+ parser (>= 2.7.1.4)
38
47
  rubocop-minitest (0.10.1)
39
48
  rubocop (>= 0.87)
40
49
  rubocop-performance (1.7.1)
@@ -51,14 +60,19 @@ GEM
51
60
  term-ansicolor (1.7.1)
52
61
  tins (~> 1.0)
53
62
  thor (1.0.1)
63
+ thread_safe (0.3.6)
54
64
  tins (1.25.0)
55
65
  sync
66
+ tzinfo (1.2.7)
67
+ thread_safe (~> 0.1)
56
68
  unicode-display_width (1.7.0)
69
+ zeitwerk (2.4.0)
57
70
 
58
71
  PLATFORMS
59
72
  ruby
60
73
 
61
74
  DEPENDENCIES
75
+ activesupport
62
76
  alba!
63
77
  coveralls
64
78
  minitest (~> 5.0)
data/README.md CHANGED
@@ -25,6 +25,24 @@ Or install it yourself as:
25
25
 
26
26
  ## Usage
27
27
 
28
+ ### Configuration
29
+
30
+ Alba's configuration is fairly simple.
31
+
32
+ #### Backend
33
+
34
+ Backend is the actual part serializing an object into JSON. Alba supports these backends.
35
+
36
+ * Oj, the fastest. Gem installation required.
37
+ * active_support, mostly for Rails. Gem installation required.
38
+ * default or json, with no external dependencies.
39
+
40
+ You can set a backend like this:
41
+
42
+ ```ruby
43
+ Alba.backend = :oj
44
+ ```
45
+
28
46
  ### Simple serialization with key
29
47
 
30
48
  ```ruby
@@ -125,11 +143,49 @@ end
125
143
 
126
144
  Although this might be useful sometimes, it's generally recommended to define a class for both Resource and Serializer.
127
145
 
146
+ ### Inheritance and Ignorance
147
+
148
+ You can `exclude` or `ignore` certain attributes using `ignoring`.
149
+
150
+ ```ruby
151
+ class Foo
152
+ attr_accessor :id, :name, :body
153
+
154
+ def initialize(id, name, body)
155
+ @id = id
156
+ @name = name
157
+ @body = body
158
+ end
159
+ end
160
+
161
+ class GenericFooResource
162
+ include Alba::Resource
163
+
164
+ attributes :id, :name, :body
165
+ end
166
+
167
+ class RestrictedFooResouce < GenericFooResource
168
+ ignoring :id, :body
169
+ end
170
+
171
+ RestrictedFooResouce.new(foo).serialize
172
+ # => '{"name":"my foo"}'
173
+ end
174
+ ```
175
+
128
176
  ## Comparison
129
177
 
130
178
  Alba is faster than alternatives.
131
179
  For a performance benchmark, see https://gist.github.com/okuramasafumi/4e375525bd3a28e4ca812d2a3b3e5829.
132
180
 
181
+ ## Rails
182
+
183
+ When you use Alba in Rails, you can create an initializer file with the line below for compatibility with Rails JSON encoder.
184
+
185
+ ```ruby
186
+ Alba.backend = :active_support
187
+ ```
188
+
133
189
  ## Why named "Alba"?
134
190
 
135
191
  The name "Alba" comes from "albatross", a kind of birds. In Japanese, this bird is called "Aho-dori", which means "stupid bird". I find it funny because in fact albatrosses fly really fast. I hope Alba looks stupid but in fact it does its job quick.
@@ -142,7 +198,7 @@ Alba has three component, `Serializer`, `Resource` and `Value` (`Value` is conce
142
198
 
143
199
  `Resource` is a component responsible for defining how an object (or a collection of objects) is converted into JSON. The difference between `Serializer` and `Resource` is that while `Serializer` can add arbitrary data into JSON, `Resource` can get data only from the object under it. The main interface is `#serializable_hash`.
144
200
 
145
- `Value` is either `Attribute`, `One` or `Many`. They are responsible for fetching data from the object for `Resource`. The main interface is `#to_hash`.
201
+ `One` and `Many` are the special object fetching other resources and converting them into Hash.
146
202
 
147
203
  The main `Alba` module holds config values and one convenience method, `.serialize`.
148
204
 
@@ -9,11 +9,12 @@ module Alba
9
9
  class Error < StandardError; end
10
10
 
11
11
  class << self
12
- attr_reader :backend
12
+ attr_reader :backend, :encoder
13
13
  attr_accessor :default_serializer
14
14
 
15
15
  def backend=(backend)
16
16
  @backend = backend&.to_sym
17
+ set_encoder
17
18
  end
18
19
 
19
20
  def serialize(object, with: nil, &block)
@@ -27,8 +28,40 @@ module Alba
27
28
 
28
29
  private
29
30
 
31
+ def set_encoder
32
+ @encoder = case @backend
33
+ when :oj
34
+ begin
35
+ require 'oj'
36
+ ->(hash) { Oj.dump(hash, mode: :strict) }
37
+ rescue LoadError
38
+ default_encoder
39
+ end
40
+ when :active_support
41
+ begin
42
+ require 'active_support/json'
43
+ ->(hash) { ActiveSupport::JSON.encode(hash) }
44
+ rescue LoadError
45
+ default_encoder
46
+ end
47
+ when nil, :default, :json
48
+ default_encoder
49
+ else
50
+ raise Alba::Error, "Unsupported backend, #{backend}"
51
+ end
52
+ end
53
+
54
+ def default_encoder
55
+ lambda do |hash|
56
+ require 'json'
57
+ JSON.dump(hash)
58
+ end
59
+ end
60
+
30
61
  def resource_class
31
62
  ::Alba::Resources::DefaultResource.clone
32
63
  end
33
64
  end
65
+
66
+ @encoder = default_encoder
34
67
  end
@@ -0,0 +1,25 @@
1
+ module Alba
2
+ # Base class for `One` and `Many`
3
+ # Child class should implement `to_hash` method
4
+ class Association
5
+ def initialize(name:, resource: nil, &block)
6
+ @name = name
7
+ @resource = resource
8
+ @block = block
9
+ raise ArgumentError, 'resource or block is required' if @resource.nil? && @block.nil?
10
+ end
11
+
12
+ def to_hash
13
+ :not_implemented
14
+ end
15
+
16
+ private
17
+
18
+ def resource_class
19
+ klass = ::Alba::Resources::DefaultResource.dup
20
+ klass.reset
21
+ klass.class_eval(&@block)
22
+ klass
23
+ end
24
+ end
25
+ end
@@ -1,26 +1,12 @@
1
+ require 'alba/association'
2
+
1
3
  module Alba
2
4
  # Representing many association
3
- class Many
4
- def initialize(name:, resource: nil, &block)
5
- @name = name
6
- @resource = resource
7
- @block = block
8
- raise ArgumentError, 'resource or block is required' if @resource.nil? && @block.nil?
9
- end
10
-
5
+ class Many < Association
11
6
  def to_hash(target)
12
7
  objects = target.public_send(@name)
13
8
  @resource ||= resource_class
14
9
  objects.map { |o| @resource.new(o).to_hash }
15
10
  end
16
-
17
- private
18
-
19
- def resource_class
20
- klass = Class.new
21
- klass.include(::Alba::Resource)
22
- klass.class_exec(&@block)
23
- klass
24
- end
25
11
  end
26
12
  end
@@ -1,26 +1,12 @@
1
+ require 'alba/association'
2
+
1
3
  module Alba
2
4
  # Representing one association
3
- class One
4
- def initialize(name:, resource: nil, &block)
5
- @name = name
6
- @resource = resource
7
- @block = block
8
- raise ArgumentError, 'resource or block is required' if @resource.nil? && @block.nil?
9
- end
10
-
5
+ class One < Association
11
6
  def to_hash(target)
12
7
  object = target.public_send(@name)
13
8
  @resource ||= resource_class
14
9
  @resource.new(object).to_hash
15
10
  end
16
-
17
- private
18
-
19
- def resource_class
20
- klass = Class.new
21
- klass.include(::Alba::Resource)
22
- klass.class_exec(&@block)
23
- klass
24
- end
25
11
  end
26
12
  end
@@ -1,5 +1,4 @@
1
1
  require 'alba/serializer'
2
- require 'alba/attribute'
3
2
  require 'alba/one'
4
3
  require 'alba/many'
5
4
  require 'alba/serializers/default_serializer'
@@ -7,18 +6,13 @@ require 'alba/serializers/default_serializer'
7
6
  module Alba
8
7
  # This module represents what should be serialized
9
8
  module Resource
10
- DSLS = [:_attributes, :_serializer, :_key].freeze
9
+ DSLS = {_attributes: {}, _serializer: nil, _key: nil}.freeze
11
10
  def self.included(base)
11
+ super
12
12
  base.class_eval do
13
13
  # Initialize
14
- DSLS.each do |name|
15
- initial = case name
16
- when :_attributes
17
- {}
18
- when :_serializer, :_name
19
- nil
20
- end
21
- instance_variable_set("@#{name}", initial) unless instance_variable_defined?("@#{name}")
14
+ DSLS.each do |name, initial|
15
+ instance_variable_set("@#{name}", initial.dup) unless instance_variable_defined?("@#{name}")
22
16
  end
23
17
  end
24
18
  base.include InstanceMethods
@@ -27,11 +21,12 @@ module Alba
27
21
 
28
22
  # Instance methods
29
23
  module InstanceMethods
30
- attr_reader :_object
24
+ attr_reader :object, :_key, :params
31
25
 
32
- def initialize(object)
33
- @_object = object
34
- DSLS.each { |name| instance_variable_set("@#{name}", self.class.public_send(name)) }
26
+ def initialize(object, params: {})
27
+ @object = object
28
+ @params = params
29
+ DSLS.each_key { |name| instance_variable_set("@#{name}", self.class.public_send(name)) }
35
30
  end
36
31
 
37
32
  def serialize(with: nil)
@@ -48,18 +43,8 @@ module Alba
48
43
  serializer.new(self).serialize
49
44
  end
50
45
 
51
- def serializable_hash(with_key: true)
52
- get_attribute = lambda do |resource|
53
- @_attributes.transform_values do |attribute|
54
- attribute.to_hash(resource)
55
- end
56
- end
57
- serializable_hash = if collection?
58
- @_object.map(&get_attribute)
59
- else
60
- get_attribute.call(@_object)
61
- end
62
- with_key && @_key ? {@_key => serializable_hash} : serializable_hash
46
+ def serializable_hash
47
+ collection? ? @object.map(&converter) : converter.call(@object)
63
48
  end
64
49
  alias to_hash serializable_hash
65
50
 
@@ -69,6 +54,21 @@ module Alba
69
54
 
70
55
  private
71
56
 
57
+ def converter
58
+ lambda do |resource|
59
+ @_attributes.transform_values do |attribute|
60
+ case attribute
61
+ when Symbol
62
+ resource.public_send attribute
63
+ when Proc
64
+ instance_exec(resource, &attribute)
65
+ when Alba::One, Alba::Many
66
+ attribute.to_hash(resource)
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
72
  def inline_extended_serializer(with)
73
73
  klass = ::Alba::Serializers::DefaultSerializer.clone
74
74
  klass.class_eval(&with)
@@ -76,26 +76,27 @@ module Alba
76
76
  end
77
77
 
78
78
  def collection?
79
- @_object.is_a?(Enumerable)
79
+ @object.is_a?(Enumerable)
80
80
  end
81
81
  end
82
82
 
83
83
  # Class methods
84
84
  module ClassMethods
85
- attr_reader(*DSLS)
85
+ attr_reader(*DSLS.keys)
86
86
 
87
87
  def inherited(subclass)
88
- DSLS.each { |name| subclass.instance_variable_set("@#{name}", instance_variable_get("@#{name}")) }
88
+ super
89
+ DSLS.each_key { |name| subclass.instance_variable_set("@#{name}", instance_variable_get("@#{name}").clone) }
89
90
  end
90
91
 
91
92
  def attributes(*attrs)
92
- attrs.each { |attr_name| @_attributes[attr_name] = Attribute.new(name: attr_name, method: attr_name) }
93
+ attrs.each { |attr_name| @_attributes[attr_name] = attr_name }
93
94
  end
94
95
 
95
96
  def attribute(name, &block)
96
97
  raise ArgumentError, 'No block given in attribute method' unless block
97
98
 
98
- @_attributes[name] = Attribute.new(name: name, method: block)
99
+ @_attributes[name] = block
99
100
  end
100
101
 
101
102
  def one(name, resource: nil, &block)
@@ -113,6 +114,13 @@ module Alba
113
114
  def key(key)
114
115
  @_key = key.to_sym
115
116
  end
117
+
118
+ # Use this DSL in child class to ignore certain attributes
119
+ def ignoring(*attributes)
120
+ attributes.each do |attr_name|
121
+ @_attributes.delete(attr_name)
122
+ end
123
+ end
116
124
  end
117
125
  end
118
126
  end
@@ -3,7 +3,11 @@ module Alba
3
3
  # Empty resource class, use this with `class_eval` for
4
4
  # inline associations and serializations.
5
5
  class DefaultResource
6
- include ::Alba::Resource
6
+ include Alba::Resource
7
+
8
+ def self.reset
9
+ @_attributes = {}
10
+ end
7
11
  end
8
12
  end
9
13
  end
@@ -2,6 +2,7 @@ module Alba
2
2
  # This module represents how a resource should be serialized.
3
3
  module Serializer
4
4
  def self.included(base)
5
+ super
5
6
  base.include InstanceMethods
6
7
  base.extend ClassMethods
7
8
  end
@@ -9,37 +10,32 @@ module Alba
9
10
  # Instance methods
10
11
  module InstanceMethods
11
12
  def initialize(resource)
12
- @_opts = self.class._opts || {}
13
- @_metadata = self.class._metadata || {}
14
- @_metadata = @_metadata.transform_values { |block| block.call(resource._object) }
15
- key = case @_opts[:key]
16
- when true
17
- resource.key
18
- else
19
- @_opts[:key]
20
- end
21
- @hash = resource.serializable_hash(with_key: false)
13
+ @resource = resource
14
+ @hash = resource.serializable_hash
22
15
  @hash = {key.to_sym => @hash} if key
23
16
  # @hash is either Hash or Array
24
- @hash.is_a?(Hash) ? @hash.merge!(@_metadata.to_h) : @hash << @_metadata
17
+ @hash.is_a?(Hash) ? @hash.merge!(metadata.to_h) : @hash << metadata
25
18
  end
26
19
 
27
20
  def serialize
28
- fallback = lambda do
29
- require 'json'
30
- JSON.dump(@hash)
31
- end
32
- case Alba.backend
33
- when :oj
34
- begin
35
- require 'oj'
36
- -> { Oj.dump(@hash, mode: :strict) }
37
- rescue LoadError
38
- fallback
39
- end
21
+ Alba.encoder.call(@hash)
22
+ end
23
+
24
+ private
25
+
26
+ def key
27
+ opts = self.class._opts || {}
28
+ case opts[:key]
29
+ when true
30
+ @resource.key
40
31
  else
41
- fallback
42
- end.call
32
+ opts[:key]
33
+ end
34
+ end
35
+
36
+ def metadata
37
+ metadata = self.class._metadata || {}
38
+ metadata.transform_values { |block| block.call(@resource.object) }
43
39
  end
44
40
  end
45
41
 
@@ -48,6 +44,7 @@ module Alba
48
44
  attr_reader :_opts, :_metadata
49
45
 
50
46
  def inherited(subclass)
47
+ super
51
48
  %w[_opts _metadata].each { |name| subclass.instance_variable_set("@#{name}", instance_variable_get("@#{name}")) }
52
49
  end
53
50
 
@@ -1,3 +1,3 @@
1
1
  module Alba
2
- VERSION = '0.8.0'.freeze
2
+ VERSION = '0.9.0'.freeze
3
3
  end
data/sider.yml CHANGED
@@ -4,6 +4,7 @@ linter:
4
4
  # # https://help.sider.review/tools/ruby/rubocop
5
5
  rubocop:
6
6
  gems:
7
+ - "rubocop-minitest"
7
8
  - "rubocop-performance"
8
9
  - "rubocop-sensible"
9
10
  safe: false
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.8.0
4
+ version: 0.9.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: 2020-08-06 00:00:00.000000000 Z
11
+ date: 2020-08-11 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
@@ -32,7 +32,7 @@ files:
32
32
  - bin/console
33
33
  - bin/setup
34
34
  - lib/alba.rb
35
- - lib/alba/attribute.rb
35
+ - lib/alba/association.rb
36
36
  - lib/alba/many.rb
37
37
  - lib/alba/one.rb
38
38
  - lib/alba/resource.rb
@@ -1,19 +0,0 @@
1
- module Alba
2
- # This class represents an attribute, which is serialized
3
- # by either sending message or calling a Proc.
4
- class Attribute
5
- def initialize(name:, method:)
6
- @name = name
7
- @method = method
8
- end
9
-
10
- def to_hash(target)
11
- case @method
12
- when Symbol, String
13
- target.public_send(@method)
14
- when Proc
15
- @method.arity.zero? ? target.instance_exec(&@method) : @method.call(target)
16
- end
17
- end
18
- end
19
- end