alba 0.4.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: 115b71c658fcda000dd3e4d4ccc0306c3cf94033f42fd96eb8e26223cba1ed3a
4
- data.tar.gz: 8c9f023c9d60c905c35b6f285120f6de536b4a21258fdf42b867a5601c2162d6
3
+ metadata.gz: 3a92c56eee7615f823efc93ed7edb87ba6c47a4bcf8e8b11e48edfc62c380545
4
+ data.tar.gz: 310eca11a4a5a83287d3d87c1346c2436900bf1283e5642baa3878c69087812d
5
5
  SHA512:
6
- metadata.gz: 66bc3c85b71f412c761629a6eae760c038115edd826e1d2cc34d204e2cdb4164ababdc31fc3f9569eb89ea51d364176cfd2a53b9ffbfb995fb3daeada1c110e3
7
- data.tar.gz: 6f8b53f011829b941ea6ebb6237f96f52dbc7cd36042db6cab996c7e4a4a3ead931f3801243c64be6f7c60f8d144cb85d5beea62f020f95e6d1bc3585bbadd1b
6
+ metadata.gz: 2fed0c44e90d2d876b2db635867741b3520a21c470acfb07a0218b7e0b627ce718edb1da964dfafdf0c79b3fe16bf7502a4fd873e998778c5307bd0c37d0c7c0
7
+ data.tar.gz: 334edc5389f1fceba2ee880a322f894ccb145533793a885777ac29c08c273b5dda9a31cf9885b493b6ec3fabcf859a1d1643ce37b848c225db66b658b36e3e34
@@ -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:
@@ -15,13 +16,12 @@ AllCops:
15
16
  Layout/SpaceInsideHashLiteralBraces:
16
17
  EnforcedStyle: no_space
17
18
 
19
+ Metrics/ClassLength:
20
+ Exclude:
21
+ - 'test/alba_test.rb'
22
+
18
23
  Metrics/MethodLength:
19
24
  Max: 20
20
25
 
21
- Naming/PredicateName:
22
- AllowedMethods:
23
- - 'has_one'
24
- - 'has_many'
25
-
26
26
  Style/FrozenStringLiteralComment:
27
27
  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,9 +3,12 @@ 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'
9
+ gem 'oj', '~> 3.10', platforms: :ruby
8
10
  gem 'rake', '~> 13.0'
9
11
  gem 'rubocop', '>= 0.79.0', require: false
12
+ gem 'rubocop-minitest', '~> 0.10.1', require: false
10
13
  gem 'rubocop-performance', '~> 1.7.1', require: false
11
14
  gem 'rubocop-sensible', '~> 0.3.0', require: false
@@ -1,12 +1,19 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- alba (0.4.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,8 +21,11 @@ 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)
28
+ oj (3.10.8)
19
29
  parallel (1.19.2)
20
30
  parser (2.7.1.4)
21
31
  ast (~> 2.4.1)
@@ -23,17 +33,19 @@ GEM
23
33
  rake (13.0.1)
24
34
  regexp_parser (1.7.1)
25
35
  rexml (3.2.4)
26
- rubocop (0.88.0)
36
+ rubocop (0.89.1)
27
37
  parallel (~> 1.10)
28
38
  parser (>= 2.7.1.1)
29
39
  rainbow (>= 2.2.2, < 4.0)
30
40
  regexp_parser (>= 1.7)
31
41
  rexml
32
- rubocop-ast (>= 0.1.0, < 1.0)
42
+ rubocop-ast (>= 0.3.0, < 1.0)
33
43
  ruby-progressbar (~> 1.7)
34
44
  unicode-display_width (>= 1.4.0, < 2.0)
35
- rubocop-ast (0.2.0)
36
- parser (>= 2.7.0.1)
45
+ rubocop-ast (0.3.0)
46
+ parser (>= 2.7.1.4)
47
+ rubocop-minitest (0.10.1)
48
+ rubocop (>= 0.87)
37
49
  rubocop-performance (1.7.1)
38
50
  rubocop (>= 0.82.0)
39
51
  rubocop-sensible (0.3.0)
@@ -48,19 +60,26 @@ GEM
48
60
  term-ansicolor (1.7.1)
49
61
  tins (~> 1.0)
50
62
  thor (1.0.1)
63
+ thread_safe (0.3.6)
51
64
  tins (1.25.0)
52
65
  sync
66
+ tzinfo (1.2.7)
67
+ thread_safe (~> 0.1)
53
68
  unicode-display_width (1.7.0)
69
+ zeitwerk (2.4.0)
54
70
 
55
71
  PLATFORMS
56
72
  ruby
57
73
 
58
74
  DEPENDENCIES
75
+ activesupport
59
76
  alba!
60
77
  coveralls
61
78
  minitest (~> 5.0)
79
+ oj (~> 3.10)
62
80
  rake (~> 13.0)
63
81
  rubocop (>= 0.79.0)
82
+ rubocop-minitest (~> 0.10.1)
64
83
  rubocop-performance (~> 1.7.1)
65
84
  rubocop-sensible (~> 0.3.0)
66
85
 
data/README.md CHANGED
@@ -1,10 +1,11 @@
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.
8
9
 
9
10
  ## Installation
10
11
 
@@ -24,6 +25,24 @@ Or install it yourself as:
24
25
 
25
26
  ## Usage
26
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
+
27
46
  ### Simple serialization with key
28
47
 
29
48
  ```ruby
@@ -108,15 +127,81 @@ UserResource1.new(user).serialize
108
127
  # => '{"id":1,"articles":[{"title":"Hello World!"},{"title":"Super nice"}]}'
109
128
  ```
110
129
 
130
+ ### Inline definition with `Alba.serialize`
131
+
132
+ `Alba.serialize` method is a shortcut to define everything inline.
133
+
134
+ ```ruby
135
+ Alba.serialize(user, with: proc { set key: :foo }) do
136
+ attributes :id
137
+ many :articles do
138
+ attributes :title, :body
139
+ end
140
+ end
141
+ # => '{"foo":{"id":1,"articles":[{"title":"Hello World!","body":"Hello World!!!"},{"title":"Super nice","body":"Really nice!"}]}}'
142
+ ```
143
+
144
+ Although this might be useful sometimes, it's generally recommended to define a class for both Resource and Serializer.
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
+
111
176
  ## Comparison
112
177
 
113
- 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.
178
+ Alba is faster than alternatives.
114
179
  For a performance benchmark, see https://gist.github.com/okuramasafumi/4e375525bd3a28e4ca812d2a3b3e5829.
115
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
+
116
189
  ## Why named "Alba"?
117
190
 
118
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.
119
192
 
193
+ ## Alba internals
194
+
195
+ Alba has three component, `Serializer`, `Resource` and `Value` (`Value` is conceptual and not implemented directly).
196
+
197
+ `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`.
198
+
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`.
200
+
201
+ `One` and `Many` are the special object fetching other resources and converting them into Hash.
202
+
203
+ The main `Alba` module holds config values and one convenience method, `.serialize`.
204
+
120
205
  ## Development
121
206
 
122
207
  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.8')
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
@@ -2,20 +2,66 @@ require 'alba/version'
2
2
  require 'alba/serializers/default_serializer'
3
3
  require 'alba/serializer'
4
4
  require 'alba/resource'
5
+ require 'alba/resources/default_resource'
5
6
 
6
7
  # Core module
7
8
  module Alba
8
9
  class Error < StandardError; end
9
10
 
10
11
  class << self
11
- attr_reader :backend
12
- end
12
+ attr_reader :backend, :encoder
13
+ attr_accessor :default_serializer
13
14
 
14
- def self.backend=(backend)
15
- @backend = backend&.to_sym
16
- end
15
+ def backend=(backend)
16
+ @backend = backend&.to_sym
17
+ set_encoder
18
+ end
19
+
20
+ def serialize(object, with: nil, &block)
21
+ raise ArgumentError, 'Block required' unless block
22
+
23
+ resource_class.class_eval(&block)
24
+ resource = resource_class.new(object)
25
+ with ||= @default_serializer
26
+ resource.serialize(with: with)
27
+ end
17
28
 
18
- def self.serialize(object)
19
- Serializers::DefaultSerializer.new(object).serialize
29
+ private
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
+
61
+ def resource_class
62
+ ::Alba::Resources::DefaultResource.clone
63
+ end
20
64
  end
65
+
66
+ @encoder = default_encoder
21
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,13 +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].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 = name == :_serializer ? nil : {}
16
- 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}")
17
16
  end
18
17
  end
19
18
  base.include InstanceMethods
@@ -22,9 +21,12 @@ module Alba
22
21
 
23
22
  # Instance methods
24
23
  module InstanceMethods
25
- def initialize(resource)
26
- @_resource = resource
27
- DSLS.each { |name| instance_variable_set("@#{name}", self.class.public_send(name)) }
24
+ attr_reader :object, :_key, :params
25
+
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)) }
28
30
  end
29
31
 
30
32
  def serialize(with: nil)
@@ -33,38 +35,68 @@ module Alba
33
35
  @_serializer || Alba::Serializers::DefaultSerializer
34
36
  when ->(obj) { obj.is_a?(Class) && obj <= Alba::Serializer }
35
37
  with
36
- when Symbol
37
- const_get(with.to_s.capitalize)
38
- when String
39
- const_get(with)
38
+ when Proc
39
+ inline_extended_serializer(with)
40
+ else
41
+ raise ArgumentError, 'Unexpected type for with, possible types are Class or Proc'
40
42
  end
41
- serializer.new(serializable_hash).serialize
43
+ serializer.new(self).serialize
42
44
  end
43
45
 
44
46
  def serializable_hash
45
- @_attributes.transform_values do |attribute|
46
- attribute.to_hash(@_resource)
47
- end
47
+ collection? ? @object.map(&converter) : converter.call(@object)
48
48
  end
49
49
  alias to_hash serializable_hash
50
+
51
+ def key
52
+ @_key || self.class.name.delete_suffix('Resource').downcase.gsub(/:{2}/, '_').to_sym
53
+ end
54
+
55
+ private
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
+ def inline_extended_serializer(with)
73
+ klass = ::Alba::Serializers::DefaultSerializer.clone
74
+ klass.class_eval(&with)
75
+ klass
76
+ end
77
+
78
+ def collection?
79
+ @object.is_a?(Enumerable)
80
+ end
50
81
  end
51
82
 
52
83
  # Class methods
53
84
  module ClassMethods
54
- attr_accessor(*DSLS)
85
+ attr_reader(*DSLS.keys)
55
86
 
56
87
  def inherited(subclass)
57
- DSLS.each { |name| subclass.public_send("#{name}=", instance_variable_get("@#{name}")) }
88
+ super
89
+ DSLS.each_key { |name| subclass.instance_variable_set("@#{name}", instance_variable_get("@#{name}").clone) }
58
90
  end
59
91
 
60
92
  def attributes(*attrs)
61
- 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 }
62
94
  end
63
95
 
64
96
  def attribute(name, &block)
65
97
  raise ArgumentError, 'No block given in attribute method' unless block
66
98
 
67
- @_attributes[name] = Attribute.new(name: name, method: block)
99
+ @_attributes[name] = block
68
100
  end
69
101
 
70
102
  def one(name, resource: nil, &block)
@@ -78,6 +110,17 @@ module Alba
78
110
  def serializer(name)
79
111
  @_serializer = name <= Alba::Serializer ? name : nil
80
112
  end
113
+
114
+ def key(key)
115
+ @_key = key.to_sym
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
81
124
  end
82
125
  end
83
126
  end
@@ -0,0 +1,13 @@
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
+
8
+ def self.reset
9
+ @_attributes = {}
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,8 +1,8 @@
1
1
  module Alba
2
2
  # This module represents how a resource should be serialized.
3
- #
4
3
  module Serializer
5
4
  def self.included(base)
5
+ super
6
6
  base.include InstanceMethods
7
7
  base.extend ClassMethods
8
8
  end
@@ -10,39 +10,53 @@ module Alba
10
10
  # Instance methods
11
11
  module InstanceMethods
12
12
  def initialize(resource)
13
- @_resource = resource
14
- @_opts = self.class._opts || {}
15
- key = @_opts[:key]
16
- @_resource = {key.to_sym => @_resource} if key
13
+ @resource = resource
14
+ @hash = resource.serializable_hash
15
+ @hash = {key.to_sym => @hash} if key
16
+ # @hash is either Hash or Array
17
+ @hash.is_a?(Hash) ? @hash.merge!(metadata.to_h) : @hash << metadata
17
18
  end
18
19
 
19
20
  def serialize
20
- fallback = lambda do
21
- require 'json'
22
- JSON.dump(@_resource)
23
- end
24
- case Alba.backend
25
- when :oj
26
- begin
27
- require 'oj'
28
- -> { Oj.dump(@_resource, mode: :strict) }
29
- rescue LoadError
30
- fallback
31
- 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
32
31
  else
33
- fallback
34
- 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) }
35
39
  end
36
40
  end
37
41
 
38
42
  # Class methods
39
43
  module ClassMethods
40
- attr_reader :_opts
44
+ attr_reader :_opts, :_metadata
45
+
46
+ def inherited(subclass)
47
+ super
48
+ %w[_opts _metadata].each { |name| subclass.instance_variable_set("@#{name}", instance_variable_get("@#{name}")) }
49
+ end
41
50
 
42
51
  def set(key: false)
43
52
  @_opts ||= {}
44
53
  @_opts[:key] = key
45
54
  end
55
+
56
+ def metadata(name, &block)
57
+ @_metadata ||= {}
58
+ @_metadata[name] = block
59
+ end
46
60
  end
47
61
  end
48
62
  end
@@ -1,3 +1,3 @@
1
1
  module Alba
2
- VERSION = '0.4.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,16 +1,18 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: alba
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.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-07-29 00:00:00.000000000 Z
11
+ date: 2020-08-11 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,10 +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
39
+ - lib/alba/resources/default_resource.rb
37
40
  - lib/alba/serializer.rb
38
41
  - lib/alba/serializers/default_serializer.rb
39
42
  - lib/alba/version.rb
@@ -53,7 +56,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
53
56
  requirements:
54
57
  - - ">="
55
58
  - !ruby/object:Gem::Version
56
- version: 2.3.0
59
+ version: 2.5.8
57
60
  required_rubygems_version: !ruby/object:Gem::Requirement
58
61
  requirements:
59
62
  - - ">="
@@ -63,5 +66,5 @@ requirements: []
63
66
  rubygems_version: 3.1.4
64
67
  signing_key:
65
68
  specification_version: 4
66
- summary: Fast and flexible JSON serializer
69
+ summary: Alba is the fastest JSON serializer for Ruby.
67
70
  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