alba 0.7.0 → 0.11.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: 405e177e9e1fcadf7dbb164d3ac6924e1b3563547f715fedb698211b527a9604
4
- data.tar.gz: 2c534708583345ac9090c83b3ddc939b0c8bd335ed77017f554edb28578d0396
3
+ metadata.gz: f9adc72b171ef6c74c4198ddad4cc8f629c416c62c6b8d8a9698efdbe9dccc44
4
+ data.tar.gz: af33f05822bfee2bdfe455d4b7a9e75b0e2486c692297e24ab28768c0a839b24
5
5
  SHA512:
6
- metadata.gz: 71f4712ac28d457fe9bf997fb127288ed66d7d2231ffd6dd438b2eb5c1dd82ff9e3e5c4000415c513012a582cda3d3c3e73fdf0beb4cc96b8583ff9a658dfa75
7
- data.tar.gz: '07439ccffc5b48a0d5d4286236acadf4cc3e89ff1afa0a882a87c1fe5640015096a4cd85839cebdc4d6ef350a77043a494bfe4d7b6cff99f8e7db2ab9f8b3db0'
6
+ metadata.gz: 2b289f590bf7b4cc3682b13f7841e9408f61ba7d49c0521b05649fb658745cd76c91032f3c2ee83224a1407f17686dc01eb383d90219877ac6f077b05095ce4c
7
+ data.tar.gz: f842f9775b8355ed1b0dd88ea14aaecd57699fc578a11c38bf3a3d12ad04bbf01dbe0867bb30efc4cc73f1e7766d6e0e0e750741c6130dd33562434bfea6ba8b
@@ -4,6 +4,7 @@ inherit_gem:
4
4
  rubocop-sensible: 'config/rubocop.yml'
5
5
 
6
6
  require:
7
+ - rubocop-minitest
7
8
  - rubocop-performance
8
9
 
9
10
  AllCops:
@@ -11,17 +12,45 @@ AllCops:
11
12
  - 'Rakefile'
12
13
  - 'alba.gemspec'
13
14
  NewCops: enable
15
+ EnabledByDefault: true
16
+
17
+ # Oneline comment is not valid so until it gets valid, we disable it
18
+ Bundler/GemComment:
19
+ Enabled: false
20
+
21
+ Layout/ClassStructure:
22
+ Enabled: true
14
23
 
15
24
  Layout/SpaceInsideHashLiteralBraces:
16
25
  EnforcedStyle: no_space
17
26
 
27
+ Layout/MultilineAssignmentLayout:
28
+ EnforcedStyle: same_line
29
+
30
+ Lint/ConstantResolution:
31
+ Enabled: false
32
+
33
+ Metrics/ClassLength:
34
+ Exclude:
35
+ - 'test/alba_test.rb'
36
+
18
37
  Metrics/MethodLength:
19
- Max: 20
38
+ Max: 15
20
39
 
21
- Naming/PredicateName:
22
- AllowedMethods:
23
- - 'has_one'
24
- - 'has_many'
40
+ Style/ConstantVisibility:
41
+ Enabled: false
42
+
43
+ Style/Copyright:
44
+ Enabled: false
45
+
46
+ Style/DocumentationMethod:
47
+ Enabled: false
25
48
 
26
49
  Style/FrozenStringLiteralComment:
27
50
  Enabled: false
51
+
52
+ Style/InlineComment:
53
+ Enabled: false
54
+
55
+ Style/MethodCallWithArgsParentheses:
56
+ Enabled: false
@@ -2,5 +2,8 @@
2
2
  language: ruby
3
3
  cache: bundler
4
4
  rvm:
5
- - 2.6.5
5
+ - 2.5.8
6
+ - 2.6.6
7
+ - 2.7.1
8
+ - truffleruby
6
9
  before_install: gem install bundler -v 2.1.4
data/Gemfile CHANGED
@@ -3,10 +3,12 @@ source 'https://rubygems.org'
3
3
  # Specify your gem's dependencies in alba.gemspec
4
4
  gemspec
5
5
 
6
- gem 'coveralls', require: false
7
- gem 'minitest', '~> 5.0'
8
- gem 'oj', '~> 3.10'
9
- gem 'rake', '~> 13.0'
10
- gem 'rubocop', '>= 0.79.0', require: false
11
- gem 'rubocop-performance', '~> 1.7.1', require: false
12
- gem 'rubocop-sensible', '~> 0.3.0', require: false
6
+ gem 'activesupport', require: false # For backend
7
+ gem 'coveralls', require: false # For test coverage
8
+ gem 'minitest', '~> 5.0' # For test
9
+ gem 'oj', '~> 3.10', platform: :ruby, require: false # For backend
10
+ gem 'rake', '~> 13.0' # For test and automation
11
+ gem 'rubocop', '>= 0.79.0', require: false # For lint
12
+ gem 'rubocop-minitest', '~> 0.10.1', require: false # For lint
13
+ gem 'rubocop-performance', '~> 1.7.1', require: false # For lint
14
+ gem 'rubocop-sensible', '~> 0.3.0', require: false # For lint
@@ -1,12 +1,20 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- alba (0.7.0)
4
+ alba (0.11.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
+ bigdecimal (2.0.0)
17
+ concurrent-ruby (1.1.6)
10
18
  coveralls (0.8.23)
11
19
  json (>= 1.8, < 3)
12
20
  simplecov (~> 0.16.1)
@@ -14,9 +22,12 @@ GEM
14
22
  thor (>= 0.19.4, < 2.0)
15
23
  tins (~> 1.6)
16
24
  docile (1.3.2)
25
+ i18n (1.8.5)
26
+ concurrent-ruby (~> 1.0)
17
27
  json (2.3.1)
18
28
  minitest (5.14.1)
19
- oj (3.10.8)
29
+ oj (3.10.12)
30
+ bigdecimal (>= 1.0, < 3)
20
31
  parallel (1.19.2)
21
32
  parser (2.7.1.4)
22
33
  ast (~> 2.4.1)
@@ -24,17 +35,19 @@ GEM
24
35
  rake (13.0.1)
25
36
  regexp_parser (1.7.1)
26
37
  rexml (3.2.4)
27
- rubocop (0.88.0)
38
+ rubocop (0.89.1)
28
39
  parallel (~> 1.10)
29
40
  parser (>= 2.7.1.1)
30
41
  rainbow (>= 2.2.2, < 4.0)
31
42
  regexp_parser (>= 1.7)
32
43
  rexml
33
- rubocop-ast (>= 0.1.0, < 1.0)
44
+ rubocop-ast (>= 0.3.0, < 1.0)
34
45
  ruby-progressbar (~> 1.7)
35
46
  unicode-display_width (>= 1.4.0, < 2.0)
36
- rubocop-ast (0.2.0)
37
- parser (>= 2.7.0.1)
47
+ rubocop-ast (0.3.0)
48
+ parser (>= 2.7.1.4)
49
+ rubocop-minitest (0.10.1)
50
+ rubocop (>= 0.87)
38
51
  rubocop-performance (1.7.1)
39
52
  rubocop (>= 0.82.0)
40
53
  rubocop-sensible (0.3.0)
@@ -49,20 +62,26 @@ GEM
49
62
  term-ansicolor (1.7.1)
50
63
  tins (~> 1.0)
51
64
  thor (1.0.1)
65
+ thread_safe (0.3.6)
52
66
  tins (1.25.0)
53
67
  sync
68
+ tzinfo (1.2.7)
69
+ thread_safe (~> 0.1)
54
70
  unicode-display_width (1.7.0)
71
+ zeitwerk (2.4.0)
55
72
 
56
73
  PLATFORMS
57
74
  ruby
58
75
 
59
76
  DEPENDENCIES
77
+ activesupport
60
78
  alba!
61
79
  coveralls
62
80
  minitest (~> 5.0)
63
81
  oj (~> 3.10)
64
82
  rake (~> 13.0)
65
83
  rubocop (>= 0.79.0)
84
+ rubocop-minitest (~> 0.10.1)
66
85
  rubocop-performance (~> 1.7.1)
67
86
  rubocop-sensible (~> 0.3.0)
68
87
 
data/README.md CHANGED
@@ -1,10 +1,27 @@
1
+ [![Gem Version](https://badge.fury.io/rb/alba.svg)](https://badge.fury.io/rb/alba)
1
2
  [![Build Status](https://travis-ci.com/okuramasafumi/alba.svg?branch=master)](https://travis-ci.com/okuramasafumi/alba)
2
3
  [![Coverage Status](https://coveralls.io/repos/github/okuramasafumi/alba/badge.svg?branch=master)](https://coveralls.io/github/okuramasafumi/alba?branch=master)
3
4
  [![Maintainability](https://api.codeclimate.com/v1/badges/fdab4cc0de0b9addcfe8/maintainability)](https://codeclimate.com/github/okuramasafumi/alba/maintainability)
4
5
 
5
6
  # Alba
6
7
 
7
- `Alba` is a stupid, fast and easy to use JSON serializer.
8
+ `Alba` is the fastest JSON serializer for Ruby.
9
+
10
+ # Why yet another JSON serializer?
11
+
12
+ We know that there are several other JSON serializers for Ruby around, but none of them made us satisfied.
13
+
14
+ Alba has some advantages over other JSON serializers which we've wanted to have.
15
+
16
+ ## Easy to understand
17
+
18
+ DSL is great. It makes the coding experience natural and intuitive. However, remembering lots of DSL requires us a lot of effort. Unfortunately, most of the existing libraries have implemented their features via DSL and it's not easy to understand how they behave entirely. Alba's core DSL are only four (`attributes`, `attribute`, `one` and `many`) so it's easy to understand how to use.
19
+
20
+ Alba is also understandable internally. The codebase is much smaller than the alternatives. In fact, it's less than 300 lines of code. Look at the code on [GitHub](https://github.com/okuramasafumi/alba/tree/master/lib) and you'll be surprised how simple it is!
21
+
22
+ ## Performance
23
+
24
+ Alba is faster than most of the alternatives. We have a [benchmark](https://gist.github.com/okuramasafumi/4e375525bd3a28e4ca812d2a3b3e5829).
8
25
 
9
26
  ## Installation
10
27
 
@@ -22,8 +39,30 @@ Or install it yourself as:
22
39
 
23
40
  $ gem install alba
24
41
 
42
+ ## Supported Ruby versions
43
+
44
+ Alba supports CRuby 2.5.7 and higher and latest TruffleRuby.
45
+
25
46
  ## Usage
26
47
 
48
+ ### Configuration
49
+
50
+ Alba's configuration is fairly simple.
51
+
52
+ #### Backend
53
+
54
+ Backend is the actual part serializing an object into JSON. Alba supports these backends.
55
+
56
+ * Oj, the fastest. Gem installation required.
57
+ * active_support, mostly for Rails. Gem installation required.
58
+ * default or json, with no external dependencies.
59
+
60
+ You can set a backend like this:
61
+
62
+ ```ruby
63
+ Alba.backend = :oj
64
+ ```
65
+
27
66
  ### Simple serialization with key
28
67
 
29
68
  ```ruby
@@ -124,15 +163,65 @@ end
124
163
 
125
164
  Although this might be useful sometimes, it's generally recommended to define a class for both Resource and Serializer.
126
165
 
166
+ ### Inheritance and Ignorance
167
+
168
+ You can `exclude` or `ignore` certain attributes using `ignoring`.
169
+
170
+ ```ruby
171
+ class Foo
172
+ attr_accessor :id, :name, :body
173
+
174
+ def initialize(id, name, body)
175
+ @id = id
176
+ @name = name
177
+ @body = body
178
+ end
179
+ end
180
+
181
+ class GenericFooResource
182
+ include Alba::Resource
183
+
184
+ attributes :id, :name, :body
185
+ end
186
+
187
+ class RestrictedFooResouce < GenericFooResource
188
+ ignoring :id, :body
189
+ end
190
+
191
+ RestrictedFooResouce.new(foo).serialize
192
+ # => '{"name":"my foo"}'
193
+ end
194
+ ```
195
+
127
196
  ## Comparison
128
197
 
129
- Since Alba is intended to be stupid, there are many things Alba can't do while other gems can. However, from the same reason, it's extremely faster than alternatives.
198
+ Alba is faster than alternatives.
130
199
  For a performance benchmark, see https://gist.github.com/okuramasafumi/4e375525bd3a28e4ca812d2a3b3e5829.
131
200
 
201
+ ## Rails
202
+
203
+ When you use Alba in Rails, you can create an initializer file with the line below for compatibility with Rails JSON encoder.
204
+
205
+ ```ruby
206
+ Alba.backend = :active_support
207
+ ```
208
+
132
209
  ## Why named "Alba"?
133
210
 
134
211
  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.
135
212
 
213
+ ## Alba internals
214
+
215
+ Alba has three component, `Serializer`, `Resource` and `Value` (`Value` is conceptual and not implemented directly).
216
+
217
+ `Serializer` is a component responsible for rendering JSON output with `Resource`. `Serializer` can add more data to `Resource` such as `metadata`. Users can define one single `Serializer` and reuse it for all `Resource`s. The main interface is `#serialize`.
218
+
219
+ `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`.
220
+
221
+ `One` and `Many` are the special object fetching other resources and converting them into Hash.
222
+
223
+ The main `Alba` module holds config values and one convenience method, `.serialize`.
224
+
136
225
  ## Development
137
226
 
138
227
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -1,27 +1,27 @@
1
1
  require_relative 'lib/alba/version'
2
2
 
3
3
  Gem::Specification.new do |spec|
4
- spec.name = "alba"
4
+ spec.name = 'alba'
5
5
  spec.version = Alba::VERSION
6
- spec.authors = ["OKURA Masafumi"]
7
- spec.email = ["masafumi.o1988@gmail.com"]
6
+ spec.authors = ['OKURA Masafumi']
7
+ spec.email = ['masafumi.o1988@gmail.com']
8
8
 
9
- spec.summary = "Fast and flexible JSON serializer"
10
- spec.description = "Fast and flexible JSON serializer"
11
- spec.homepage = "https://github.com/okuramasafumi/alba"
12
- spec.license = "MIT"
13
- spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
9
+ spec.summary = 'Alba is the fastest JSON serializer for Ruby.'
10
+ spec.description = "Alba is designed to be a simple, easy to use and fast alternative to existing JSON serializers. Its performance is better than almost all gems which do similar things. The internal is so simple that it's easy to hack and maintain."
11
+ spec.homepage = 'https://github.com/okuramasafumi/alba'
12
+ spec.license = 'MIT'
13
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.5.7')
14
14
 
15
- spec.metadata["homepage_uri"] = spec.homepage
16
- spec.metadata["source_code_uri"] = "https://github.com/okuramasafumi/alba"
17
- spec.metadata["changelog_uri"] = "https://github.com/okuramasafumi/alba/CHANGELOG.md"
15
+ spec.metadata['homepage_uri'] = spec.homepage
16
+ spec.metadata['source_code_uri'] = 'https://github.com/okuramasafumi/alba'
17
+ spec.metadata['changelog_uri'] = 'https://github.com/okuramasafumi/alba/CHANGELOG.md'
18
18
 
19
19
  # Specify which files should be added to the gem when it is released.
20
20
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
21
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
21
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
22
22
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
23
23
  end
24
- spec.bindir = "exe"
24
+ spec.bindir = 'exe'
25
25
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
- spec.require_paths = ["lib"]
26
+ spec.require_paths = ['lib']
27
27
  end
@@ -1,19 +1,19 @@
1
1
  require 'alba/version'
2
- require 'alba/serializers/default_serializer'
3
2
  require 'alba/serializer'
4
3
  require 'alba/resource'
5
- require 'alba/resources/default_resource'
6
4
 
7
5
  # Core module
8
6
  module Alba
9
7
  class Error < StandardError; end
8
+ class UnsupportedBackend < Error; end
10
9
 
11
10
  class << self
12
- attr_reader :backend
11
+ attr_reader :backend, :encoder
13
12
  attr_accessor :default_serializer
14
13
 
15
14
  def backend=(backend)
16
15
  @backend = backend&.to_sym
16
+ set_encoder
17
17
  end
18
18
 
19
19
  def serialize(object, with: nil, &block)
@@ -27,8 +27,47 @@ module Alba
27
27
 
28
28
  private
29
29
 
30
+ def set_encoder
31
+ @encoder = case @backend
32
+ when :oj
33
+ try_oj
34
+ when :active_support
35
+ try_active_support
36
+ when nil, :default, :json
37
+ default_encoder
38
+ else
39
+ raise Alba::UnsupportedBackend, "Unsupported backend, #{backend}"
40
+ end
41
+ end
42
+
43
+ def try_oj
44
+ require 'oj'
45
+ ->(hash) { Oj.dump(hash, mode: :strict) }
46
+ rescue LoadError
47
+ default_encoder
48
+ end
49
+
50
+ def try_active_support
51
+ require 'active_support/json'
52
+ ->(hash) { ActiveSupport::JSON.encode(hash) }
53
+ rescue LoadError
54
+ default_encoder
55
+ end
56
+
57
+ def default_encoder
58
+ lambda do |hash|
59
+ require 'json'
60
+ JSON.dump(hash)
61
+ end
62
+ end
63
+
30
64
  def resource_class
31
- ::Alba::Resources::DefaultResource.clone
65
+ @resource_class ||= begin
66
+ klass = Class.new
67
+ klass.include(Alba::Resource)
68
+ end
32
69
  end
33
70
  end
71
+
72
+ @encoder = default_encoder
34
73
  end
@@ -0,0 +1,26 @@
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:, condition: nil, resource: nil, &block)
6
+ @name = name
7
+ @condition = condition
8
+ @block = block
9
+ @resource = resource || resource_class
10
+ raise ArgumentError, 'resource or block is required' if @resource.nil? && @block.nil?
11
+ end
12
+
13
+ def to_hash
14
+ :not_implemented
15
+ end
16
+
17
+ private
18
+
19
+ def resource_class
20
+ klass = Class.new
21
+ klass.include(Alba::Resource)
22
+ klass.class_eval(&@block)
23
+ klass
24
+ end
25
+ end
26
+ 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
-
11
- def to_hash(target)
5
+ class Many < Association
6
+ def to_hash(target, params: {})
12
7
  objects = target.public_send(@name)
13
- @resource ||= resource_class
14
- objects.map { |o| @resource.new(o).to_hash }
15
- 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
8
+ objects = @condition.call(objects, params) if @condition
9
+ objects.map { |o| @resource.new(o, params: params).to_hash }
24
10
  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
-
11
- def to_hash(target)
5
+ class One < Association
6
+ def to_hash(target, params: {})
12
7
  object = target.public_send(@name)
13
- @resource ||= resource_class
14
- @resource.new(object).to_hash
15
- 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
8
+ object = @condition.call(object, params) if @condition
9
+ @resource.new(object, params: params).to_hash
24
10
  end
25
11
  end
26
12
  end
@@ -1,24 +1,17 @@
1
1
  require 'alba/serializer'
2
- require 'alba/attribute'
3
2
  require 'alba/one'
4
3
  require 'alba/many'
5
- require 'alba/serializers/default_serializer'
6
4
 
7
5
  module Alba
8
6
  # This module represents what should be serialized
9
7
  module Resource
10
- DSLS = [:_attributes, :_serializer, :_key].freeze
8
+ DSLS = {_attributes: {}, _serializer: nil, _key: nil}.freeze
11
9
  def self.included(base)
10
+ super
12
11
  base.class_eval do
13
12
  # 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}")
13
+ DSLS.each do |name, initial|
14
+ instance_variable_set("@#{name}", initial.dup) unless instance_variable_defined?("@#{name}")
22
15
  end
23
16
  end
24
17
  base.include InstanceMethods
@@ -27,15 +20,18 @@ module Alba
27
20
 
28
21
  # Instance methods
29
22
  module InstanceMethods
30
- def initialize(resource)
31
- @_resource = resource
32
- DSLS.each { |name| instance_variable_set("@#{name}", self.class.public_send(name)) }
23
+ attr_reader :object, :_key, :params
24
+
25
+ def initialize(object, params: {})
26
+ @object = object
27
+ @params = params
28
+ DSLS.each_key { |name| instance_variable_set("@#{name}", self.class.public_send(name)) }
33
29
  end
34
30
 
35
31
  def serialize(with: nil)
36
32
  serializer = case with
37
33
  when nil
38
- @_serializer || Alba::Serializers::DefaultSerializer
34
+ @_serializer || empty_serializer
39
35
  when ->(obj) { obj.is_a?(Class) && obj <= Alba::Serializer }
40
36
  with
41
37
  when Proc
@@ -46,18 +42,8 @@ module Alba
46
42
  serializer.new(self).serialize
47
43
  end
48
44
 
49
- def serializable_hash(with_key: true)
50
- get_attribute = lambda do |resource|
51
- @_attributes.transform_values do |attribute|
52
- attribute.to_hash(resource)
53
- end
54
- end
55
- serializable_hash = if collection?
56
- @_resource.map(&get_attribute)
57
- else
58
- get_attribute.call(@_resource)
59
- end
60
- with_key && @_key ? {@_key => serializable_hash} : serializable_hash
45
+ def serializable_hash
46
+ collection? ? @object.map(&converter) : converter.call(@object)
61
47
  end
62
48
  alias to_hash serializable_hash
63
49
 
@@ -67,41 +53,65 @@ module Alba
67
53
 
68
54
  private
69
55
 
56
+ def converter
57
+ lambda do |resource|
58
+ @_attributes.transform_values do |attribute|
59
+ case attribute
60
+ when Symbol
61
+ resource.public_send attribute
62
+ when Proc
63
+ instance_exec(resource, &attribute)
64
+ when Alba::One, Alba::Many
65
+ attribute.to_hash(resource, params: params)
66
+ else
67
+ raise ::Alba::Error, "Unsupported type of attribute: #{attribute.class}"
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+ def empty_serializer
74
+ klass = Class.new
75
+ klass.include Alba::Serializer
76
+ klass
77
+ end
78
+
70
79
  def inline_extended_serializer(with)
71
- klass = ::Alba::Serializers::DefaultSerializer.clone
80
+ klass = empty_serializer
72
81
  klass.class_eval(&with)
73
82
  klass
74
83
  end
75
84
 
76
85
  def collection?
77
- @_resource.is_a?(Enumerable)
86
+ @object.is_a?(Enumerable)
78
87
  end
79
88
  end
80
89
 
81
90
  # Class methods
82
91
  module ClassMethods
83
- attr_accessor(*DSLS)
92
+ attr_reader(*DSLS.keys)
84
93
 
85
94
  def inherited(subclass)
86
- DSLS.each { |name| subclass.public_send("#{name}=", instance_variable_get("@#{name}")) }
95
+ super
96
+ DSLS.each_key { |name| subclass.instance_variable_set("@#{name}", instance_variable_get("@#{name}").clone) }
87
97
  end
88
98
 
89
99
  def attributes(*attrs)
90
- attrs.each { |attr_name| @_attributes[attr_name] = Attribute.new(name: attr_name, method: attr_name) }
100
+ attrs.each { |attr_name| @_attributes[attr_name.to_sym] = attr_name.to_sym }
91
101
  end
92
102
 
93
103
  def attribute(name, &block)
94
104
  raise ArgumentError, 'No block given in attribute method' unless block
95
105
 
96
- @_attributes[name] = Attribute.new(name: name, method: block)
106
+ @_attributes[name.to_sym] = block
97
107
  end
98
108
 
99
- def one(name, resource: nil, &block)
100
- @_attributes[name.to_sym] = One.new(name: name, resource: resource, &block)
109
+ def one(name, condition = nil, resource: nil, key: nil, &block)
110
+ @_attributes[key&.to_sym || name.to_sym] = One.new(name: name, condition: condition, resource: resource, &block)
101
111
  end
102
112
 
103
- def many(name, resource: nil, &block)
104
- @_attributes[name.to_sym] = Many.new(name: name, resource: resource, &block)
113
+ def many(name, condition = nil, resource: nil, key: nil, &block)
114
+ @_attributes[key&.to_sym || name.to_sym] = Many.new(name: name, condition: condition, resource: resource, &block)
105
115
  end
106
116
 
107
117
  def serializer(name)
@@ -111,6 +121,13 @@ module Alba
111
121
  def key(key)
112
122
  @_key = key.to_sym
113
123
  end
124
+
125
+ # Use this DSL in child class to ignore certain attributes
126
+ def ignoring(*attributes)
127
+ attributes.each do |attr_name|
128
+ @_attributes.delete(attr_name.to_sym)
129
+ end
130
+ end
114
131
  end
115
132
  end
116
133
  end
@@ -2,6 +2,11 @@ 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
6
+ base.class_eval do
7
+ @_opts = {} unless instance_variable_defined?('@_opts')
8
+ @_metadata = {} unless instance_variable_defined?('@_metadata')
9
+ end
5
10
  base.include InstanceMethods
6
11
  base.extend ClassMethods
7
12
  end
@@ -9,44 +14,46 @@ module Alba
9
14
  # Instance methods
10
15
  module InstanceMethods
11
16
  def initialize(resource)
12
- @_opts = self.class._opts || {}
13
- key = case @_opts[:key]
14
- when true
15
- resource.key
16
- else
17
- @_opts[:key]
18
- end
19
- @hash = resource.serializable_hash(with_key: false)
17
+ @resource = resource
18
+ @hash = resource.serializable_hash
20
19
  @hash = {key.to_sym => @hash} if key
20
+ # @hash is either Hash or Array
21
+ @hash.is_a?(Hash) ? @hash.merge!(metadata.to_h) : @hash << metadata
21
22
  end
22
23
 
23
24
  def serialize
24
- fallback = lambda do
25
- require 'json'
26
- JSON.dump(@hash)
27
- end
28
- case Alba.backend
29
- when :oj
30
- begin
31
- require 'oj'
32
- -> { Oj.dump(@hash, mode: :strict) }
33
- rescue LoadError
34
- fallback
35
- end
36
- else
37
- fallback
38
- end.call
25
+ Alba.encoder.call(@hash)
26
+ end
27
+
28
+ private
29
+
30
+ def key
31
+ opts = self.class._opts
32
+ opts[:key] == true ? @resource.key : opts[:key]
33
+ end
34
+
35
+ def metadata
36
+ metadata = self.class._metadata
37
+ metadata.transform_values { |block| block.call(@resource.object) }
39
38
  end
40
39
  end
41
40
 
42
41
  # Class methods
43
42
  module ClassMethods
44
- attr_reader :_opts
43
+ attr_reader :_opts, :_metadata
44
+
45
+ def inherited(subclass)
46
+ super
47
+ %w[_opts _metadata].each { |name| subclass.instance_variable_set("@#{name}", public_send(name).clone) }
48
+ end
45
49
 
46
50
  def set(key: false)
47
- @_opts ||= {}
48
51
  @_opts[:key] = key
49
52
  end
53
+
54
+ def metadata(name, &block)
55
+ @_metadata[name] = block
56
+ end
50
57
  end
51
58
  end
52
59
  end
@@ -1,3 +1,3 @@
1
1
  module Alba
2
- VERSION = '0.7.0'.freeze
2
+ VERSION = '0.11.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,16 +1,18 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: alba
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.11.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-07-31 00:00:00.000000000 Z
11
+ date: 2020-08-26 00:00:00.000000000 Z
12
12
  dependencies: []
13
- description: Fast and flexible JSON serializer
13
+ description: Alba is designed to be a simple, easy to use and fast alternative to
14
+ existing JSON serializers. Its performance is better than almost all gems which
15
+ do similar things. The internal is so simple that it's easy to hack and maintain.
14
16
  email:
15
17
  - masafumi.o1988@gmail.com
16
18
  executables: []
@@ -30,13 +32,11 @@ files:
30
32
  - bin/console
31
33
  - bin/setup
32
34
  - lib/alba.rb
33
- - lib/alba/attribute.rb
35
+ - lib/alba/association.rb
34
36
  - lib/alba/many.rb
35
37
  - lib/alba/one.rb
36
38
  - lib/alba/resource.rb
37
- - lib/alba/resources/default_resource.rb
38
39
  - lib/alba/serializer.rb
39
- - lib/alba/serializers/default_serializer.rb
40
40
  - lib/alba/version.rb
41
41
  - sider.yml
42
42
  homepage: https://github.com/okuramasafumi/alba
@@ -54,7 +54,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
54
54
  requirements:
55
55
  - - ">="
56
56
  - !ruby/object:Gem::Version
57
- version: 2.3.0
57
+ version: 2.5.7
58
58
  required_rubygems_version: !ruby/object:Gem::Requirement
59
59
  requirements:
60
60
  - - ">="
@@ -64,5 +64,5 @@ requirements: []
64
64
  rubygems_version: 3.1.4
65
65
  signing_key:
66
66
  specification_version: 4
67
- summary: Fast and flexible JSON serializer
67
+ summary: Alba is the fastest JSON serializer for Ruby.
68
68
  test_files: []
@@ -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
@@ -1,9 +0,0 @@
1
- module Alba
2
- module Resources
3
- # Empty resource class, use this with `class_eval` for
4
- # inline associations and serializations.
5
- class DefaultResource
6
- include ::Alba::Resource
7
- end
8
- end
9
- end
@@ -1,11 +0,0 @@
1
- require 'alba/serializer'
2
-
3
- module Alba
4
- module Serializers
5
- # DefaultSerializer class is used when a user doesn't specify serializer opt.
6
- # It's basically an alias of Alba::Serializer, but since it's a module this class simply include it.
7
- class DefaultSerializer
8
- include Alba::Serializer
9
- end
10
- end
11
- end