alba 0.1.0 → 0.6.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: 62d87c709e80629fa4f9373e113a141da7489703d309fd1ed6b9b411e427a101
4
- data.tar.gz: 3aacb3b018632b50cd2c71052c783e6aae97d6d79c2d1f0bb107ba07cc0d5bbd
3
+ metadata.gz: efa21967c65ad6880f19fbb2ec2fd795ed1b71f477084056068b23984df60834
4
+ data.tar.gz: 556f95a3ad12a7fdc591f8b7e336a7bc2d6c4b99710198f9bdc4e36b1389e0d2
5
5
  SHA512:
6
- metadata.gz: 3eb9360c0f0a608252d521b89911308b45584c4456040579d844cc7cf09fc839f12f8a908cd20c9f6fd421123f17ca6248948d80e7d36e82adb5a6a42e82b460
7
- data.tar.gz: d5ec4be5ede9d7cd359ed5f50af51d37c6926b99d5130f679655fec3949962c57d4c215dc212413e76f85ef20b74aa036e2b1955837ff113bcf41d3dc6f180bc
6
+ metadata.gz: 9f2bc512373e64a1ecda15c5797a5e9a1944f9fdc2a81f34f4af063d54c288fa53710bba9645fdbdec922b2642491bf0983830b7b806c3e6706099d48ed31c29
7
+ data.tar.gz: 99c355f1ad8d2c26786b79d3f7889ac00b08444f72352a14f300eb25049efbe81792242d48c0c9bf760e5bce3e93d61f408a99ac38a93a252ca55954c0880162
@@ -10,6 +10,7 @@ AllCops:
10
10
  Exclude:
11
11
  - 'Rakefile'
12
12
  - 'alba.gemspec'
13
+ NewCops: enable
13
14
 
14
15
  Layout/SpaceInsideHashLiteralBraces:
15
16
  EnforcedStyle: no_space
@@ -17,5 +18,10 @@ Layout/SpaceInsideHashLiteralBraces:
17
18
  Metrics/MethodLength:
18
19
  Max: 20
19
20
 
21
+ Naming/PredicateName:
22
+ AllowedMethods:
23
+ - 'has_one'
24
+ - 'has_many'
25
+
20
26
  Style/FrozenStringLiteralComment:
21
27
  Enabled: false
data/Gemfile CHANGED
@@ -3,8 +3,10 @@ source 'https://rubygems.org'
3
3
  # Specify your gem's dependencies in alba.gemspec
4
4
  gemspec
5
5
 
6
+ gem 'coveralls', require: false
6
7
  gem 'minitest', '~> 5.0'
8
+ gem 'oj', '~> 3.10'
7
9
  gem 'rake', '~> 13.0'
8
10
  gem 'rubocop', '>= 0.79.0', require: false
9
- gem 'rubocop-performance', '~> 1.5.0', require: false
11
+ gem 'rubocop-performance', '~> 1.7.1', require: false
10
12
  gem 'rubocop-sensible', '~> 0.3.0', require: false
@@ -1,42 +1,69 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- alba (0.1.0)
4
+ alba (0.6.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
- ast (2.4.0)
10
- jaro_winkler (1.5.4)
11
- minitest (5.14.0)
12
- parallel (1.19.1)
13
- parser (2.7.0.2)
14
- ast (~> 2.4.0)
9
+ ast (2.4.1)
10
+ coveralls (0.8.23)
11
+ json (>= 1.8, < 3)
12
+ simplecov (~> 0.16.1)
13
+ term-ansicolor (~> 1.3)
14
+ thor (>= 0.19.4, < 2.0)
15
+ tins (~> 1.6)
16
+ docile (1.3.2)
17
+ json (2.3.1)
18
+ minitest (5.14.1)
19
+ oj (3.10.8)
20
+ parallel (1.19.2)
21
+ parser (2.7.1.4)
22
+ ast (~> 2.4.1)
15
23
  rainbow (3.0.0)
16
24
  rake (13.0.1)
17
- rubocop (0.79.0)
18
- jaro_winkler (~> 1.5.1)
25
+ regexp_parser (1.7.1)
26
+ rexml (3.2.4)
27
+ rubocop (0.88.0)
19
28
  parallel (~> 1.10)
20
- parser (>= 2.7.0.1)
29
+ parser (>= 2.7.1.1)
21
30
  rainbow (>= 2.2.2, < 4.0)
31
+ regexp_parser (>= 1.7)
32
+ rexml
33
+ rubocop-ast (>= 0.1.0, < 1.0)
22
34
  ruby-progressbar (~> 1.7)
23
- unicode-display_width (>= 1.4.0, < 1.7)
24
- rubocop-performance (1.5.2)
25
- rubocop (>= 0.71.0)
35
+ unicode-display_width (>= 1.4.0, < 2.0)
36
+ rubocop-ast (0.2.0)
37
+ parser (>= 2.7.0.1)
38
+ rubocop-performance (1.7.1)
39
+ rubocop (>= 0.82.0)
26
40
  rubocop-sensible (0.3.0)
27
41
  rubocop (>= 0.60.0)
28
42
  ruby-progressbar (1.10.1)
29
- unicode-display_width (1.6.1)
43
+ simplecov (0.16.1)
44
+ docile (~> 1.1)
45
+ json (>= 1.8, < 3)
46
+ simplecov-html (~> 0.10.0)
47
+ simplecov-html (0.10.2)
48
+ sync (0.5.0)
49
+ term-ansicolor (1.7.1)
50
+ tins (~> 1.0)
51
+ thor (1.0.1)
52
+ tins (1.25.0)
53
+ sync
54
+ unicode-display_width (1.7.0)
30
55
 
31
56
  PLATFORMS
32
57
  ruby
33
58
 
34
59
  DEPENDENCIES
35
60
  alba!
61
+ coveralls
36
62
  minitest (~> 5.0)
63
+ oj (~> 3.10)
37
64
  rake (~> 13.0)
38
65
  rubocop (>= 0.79.0)
39
- rubocop-performance (~> 1.5.0)
66
+ rubocop-performance (~> 1.7.1)
40
67
  rubocop-sensible (~> 0.3.0)
41
68
 
42
69
  BUNDLED WITH
data/README.md CHANGED
@@ -1,6 +1,10 @@
1
+ [![Build Status](https://travis-ci.com/okuramasafumi/alba.svg?branch=master)](https://travis-ci.com/okuramasafumi/alba)
2
+ [![Coverage Status](https://coveralls.io/repos/github/okuramasafumi/alba/badge.svg?branch=master)](https://coveralls.io/github/okuramasafumi/alba?branch=master)
3
+ [![Maintainability](https://api.codeclimate.com/v1/badges/fdab4cc0de0b9addcfe8/maintainability)](https://codeclimate.com/github/okuramasafumi/alba/maintainability)
4
+
1
5
  # Alba
2
6
 
3
- `Alba` is a fast and flexible JSON serializer.
7
+ `Alba` is a stupid, fast and easy to use JSON serializer.
4
8
 
5
9
  ## Installation
6
10
 
@@ -20,6 +24,8 @@ Or install it yourself as:
20
24
 
21
25
  ## Usage
22
26
 
27
+ ### Simple serialization with key
28
+
23
29
  ```ruby
24
30
  class User
25
31
  attr_accessor :id, :name, :email, :created_at, :updated_at
@@ -53,6 +59,80 @@ UserResource.new(user).serialize
53
59
  # => "{\"id\":1,\"name\":\"Masafumi OKURA\",\"name_with_email\":\"Masafumi OKURA: masafumi@example.com\"}"
54
60
  ```
55
61
 
62
+ ### Serialization with associations
63
+
64
+ ```ruby
65
+ class User
66
+ attr_reader :id, :created_at, :updated_at
67
+ attr_accessor :articles
68
+
69
+ def initialize(id)
70
+ @id = id
71
+ @created_at = Time.now
72
+ @updated_at = Time.now
73
+ @articles = []
74
+ end
75
+ end
76
+
77
+ class Article
78
+ attr_accessor :user_id, :title, :body
79
+
80
+ def initialize(user_id, title, body)
81
+ @user_id = user_id
82
+ @title = title
83
+ @body = body
84
+ end
85
+ end
86
+
87
+ class ArticleResource
88
+ include Alba::Resource
89
+
90
+ attributes :title
91
+ end
92
+
93
+ class UserResource1
94
+ include Alba::Resource
95
+
96
+ attributes :id
97
+
98
+ many :articles, resource: ArticleResource
99
+ end
100
+
101
+ user = User.new(1)
102
+ article1 = Article.new(1, 'Hello World!', 'Hello World!!!')
103
+ user.articles << article1
104
+ article2 = Article.new(2, 'Super nice', 'Really nice!')
105
+ user.articles << article2
106
+
107
+ UserResource1.new(user).serialize
108
+ # => '{"id":1,"articles":[{"title":"Hello World!"},{"title":"Super nice"}]}'
109
+ ```
110
+
111
+ ### Inline definition with `Alba.serialize`
112
+
113
+ `Alba.serialize` method is a shortcut to define everything inline.
114
+
115
+ ```ruby
116
+ Alba.serialize(user, with: proc { set key: :foo }) do
117
+ attributes :id
118
+ many :articles do
119
+ attributes :title, :body
120
+ end
121
+ end
122
+ # => '{"foo":{"id":1,"articles":[{"title":"Hello World!","body":"Hello World!!!"},{"title":"Super nice","body":"Really nice!"}]}}'
123
+ ```
124
+
125
+ Although this might be useful sometimes, it's generally recommended to define a class for both Resource and Serializer.
126
+
127
+ ## Comparison
128
+
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.
130
+ For a performance benchmark, see https://gist.github.com/okuramasafumi/4e375525bd3a28e4ca812d2a3b3e5829.
131
+
132
+ ## Why named "Alba"?
133
+
134
+ 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
+
56
136
  ## Development
57
137
 
58
138
  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,6 +1,8 @@
1
1
  require 'alba/version'
2
+ require 'alba/serializers/default_serializer'
3
+ require 'alba/serializer'
2
4
  require 'alba/resource'
3
- require 'json'
5
+ require 'alba/resources/default_resource'
4
6
 
5
7
  # Core module
6
8
  module Alba
@@ -8,24 +10,25 @@ module Alba
8
10
 
9
11
  class << self
10
12
  attr_reader :backend
11
- end
13
+ attr_accessor :default_serializer
12
14
 
13
- def self.backend=(backend)
14
- @backend = backend&.to_sym
15
- end
15
+ def backend=(backend)
16
+ @backend = backend&.to_sym
17
+ end
18
+
19
+ def serialize(object, with: nil, &block)
20
+ raise ArgumentError, 'Block required' unless block
21
+
22
+ resource_class.class_eval(&block)
23
+ resource = resource_class.new(object)
24
+ with ||= @default_serializer
25
+ resource.serialize(with: with)
26
+ end
27
+
28
+ private
16
29
 
17
- def self.serialize(object)
18
- fallback = ->(resource) { resource.to_json }
19
- case backend
20
- when :oj
21
- begin
22
- require 'oj'
23
- ->(resource) { Oj.dump(resource) }
24
- rescue LoadError
25
- fallback
26
- end
27
- else
28
- fallback
29
- end.call(object)
30
+ def resource_class
31
+ ::Alba::Resources::DefaultResource.clone
32
+ end
30
33
  end
31
34
  end
@@ -7,12 +7,12 @@ module Alba
7
7
  @method = method
8
8
  end
9
9
 
10
- def serialize(target)
10
+ def to_hash(target)
11
11
  case @method
12
12
  when Symbol, String
13
13
  target.public_send(@method)
14
14
  when Proc
15
- @method.call(target)
15
+ @method.arity.zero? ? target.instance_exec(&@method) : @method.call(target)
16
16
  end
17
17
  end
18
18
  end
@@ -0,0 +1,26 @@
1
+ module Alba
2
+ # 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)
12
+ 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
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ module Alba
2
+ # 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)
12
+ 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
24
+ end
25
+ end
26
+ end
@@ -1,11 +1,26 @@
1
1
  require 'alba/serializer'
2
2
  require 'alba/attribute'
3
+ require 'alba/one'
4
+ require 'alba/many'
3
5
  require 'alba/serializers/default_serializer'
4
6
 
5
7
  module Alba
6
8
  # This module represents what should be serialized
7
9
  module Resource
10
+ DSLS = [:_attributes, :_serializer, :_key].freeze
8
11
  def self.included(base)
12
+ base.class_eval do
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}")
22
+ end
23
+ end
9
24
  base.include InstanceMethods
10
25
  base.extend ClassMethods
11
26
  end
@@ -14,42 +29,53 @@ module Alba
14
29
  module InstanceMethods
15
30
  def initialize(resource)
16
31
  @_resource = resource
17
- @_attributes = self.class._attributes
18
- @_serializer_class = self.class._serializer_class
32
+ DSLS.each { |name| instance_variable_set("@#{name}", self.class.public_send(name)) }
19
33
  end
20
34
 
21
35
  def serialize(with: nil)
22
- serializer_class = case with
23
- when ->(obj) { obj.is_a?(Class) && obj.ancestors.include?(Alba::Serializer) }
24
- with
25
- when Symbol
26
- const_get(with.to_s.capitalize)
27
- when String
28
- const_get(with)
29
- when nil
30
- @_serializer_class || Alba::Serializers::DefaultSerializer
31
- end
32
- # opts = serializer.opts
33
- serialiable_hash = @_attributes.map do |name, attribute|
34
- [name, attribute.serialize(@_resource)]
35
- end.to_h
36
- serializer_class.new(serialiable_hash).serialize
36
+ serializer = case with
37
+ when nil
38
+ @_serializer || Alba::Serializers::DefaultSerializer
39
+ when ->(obj) { obj.is_a?(Class) && obj <= Alba::Serializer }
40
+ with
41
+ when Proc
42
+ inline_extended_serializer(with)
43
+ else
44
+ raise ArgumentError, 'Unexpected type for with, possible types are Class or Proc'
45
+ end
46
+ serializer.new(self).serialize
47
+ end
48
+
49
+ def serializable_hash(with_key: true)
50
+ serializable_hash = @_attributes.transform_values do |attribute|
51
+ attribute.to_hash(@_resource)
52
+ end
53
+ with_key && @_key ? {@_key => serializable_hash} : serializable_hash
54
+ end
55
+ alias to_hash serializable_hash
56
+
57
+ def key
58
+ @_key || self.class.name.delete_suffix('Resource').downcase.gsub(/:{2}/, '_').to_sym
59
+ end
60
+
61
+ private
62
+
63
+ def inline_extended_serializer(with)
64
+ klass = ::Alba::Serializers::DefaultSerializer.clone
65
+ klass.class_eval(&with)
66
+ klass
37
67
  end
38
68
  end
39
69
 
40
70
  # Class methods
41
71
  module ClassMethods
42
- attr_accessor :_attributes, :_serializer_class
72
+ attr_accessor(*DSLS)
43
73
 
44
74
  def inherited(subclass)
45
- @_attributes = {} unless defined?(@_attributes)
46
- @_serializer_class = nil unless defined?(@_serializer_class)
47
- subclass._attributes = @_attributes
48
- subclass._serializer_class = @_serializer_class
75
+ DSLS.each { |name| subclass.public_send("#{name}=", instance_variable_get("@#{name}")) }
49
76
  end
50
77
 
51
78
  def attributes(*attrs)
52
- @_attributes = {} unless defined? @_attributes
53
79
  attrs.each { |attr_name| @_attributes[attr_name] = Attribute.new(name: attr_name, method: attr_name) }
54
80
  end
55
81
 
@@ -59,8 +85,20 @@ module Alba
59
85
  @_attributes[name] = Attribute.new(name: name, method: block)
60
86
  end
61
87
 
88
+ def one(name, resource: nil, &block)
89
+ @_attributes[name.to_sym] = One.new(name: name, resource: resource, &block)
90
+ end
91
+
92
+ def many(name, resource: nil, &block)
93
+ @_attributes[name.to_sym] = Many.new(name: name, resource: resource, &block)
94
+ end
95
+
62
96
  def serializer(name)
63
- @_serializer_class = name.ancestors.include?(Alba::Serializer) ? name : nil
97
+ @_serializer = name <= Alba::Serializer ? name : nil
98
+ end
99
+
100
+ def key(key)
101
+ @_key = key.to_sym
64
102
  end
65
103
  end
66
104
  end
@@ -0,0 +1,9 @@
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,6 +1,5 @@
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)
6
5
  base.include InstanceMethods
@@ -10,19 +9,27 @@ module Alba
10
9
  # Instance methods
11
10
  module InstanceMethods
12
11
  def initialize(resource)
13
- @_resource = resource
14
12
  @_opts = self.class._opts || {}
15
- key = @_opts[:key]
16
- @_resource = {key.to_sym => @_resource} if key
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)
20
+ @hash = {key.to_sym => @hash} if key
17
21
  end
18
22
 
19
23
  def serialize
20
- fallback = -> { @_resource.to_json }
24
+ fallback = lambda do
25
+ require 'json'
26
+ JSON.dump(@hash)
27
+ end
21
28
  case Alba.backend
22
29
  when :oj
23
30
  begin
24
31
  require 'oj'
25
- -> { Oj.dump(@_resource) }
32
+ -> { Oj.dump(@hash, mode: :strict) }
26
33
  rescue LoadError
27
34
  fallback
28
35
  end
@@ -1,3 +1,5 @@
1
+ require 'alba/serializer'
2
+
1
3
  module Alba
2
4
  module Serializers
3
5
  # DefaultSerializer class is used when a user doesn't specify serializer opt.
@@ -1,3 +1,3 @@
1
1
  module Alba
2
- VERSION = '0.1.0'.freeze
2
+ VERSION = '0.6.0'.freeze
3
3
  end
@@ -0,0 +1,60 @@
1
+ linter:
2
+ # # https://help.sider.review/getting-started/custom-configuration
3
+
4
+ # # https://help.sider.review/tools/ruby/rubocop
5
+ rubocop:
6
+ gems:
7
+ - "rubocop-performance"
8
+ - "rubocop-sensible"
9
+ safe: false
10
+
11
+ # # https://help.sider.review/tools/ruby/reek
12
+ # reek:
13
+ # gems:
14
+ # - name: "reek"
15
+ # version: "5.2.0"
16
+ # target:
17
+ # - lib/
18
+ # - test/
19
+ # config: config/.reek.yml
20
+
21
+ # # https://help.sider.review/tools/ruby/querly
22
+ # querly:
23
+ # gems:
24
+ # - "slim"
25
+
26
+ # # https://help.sider.review/tools/others/misspell
27
+ # misspell:
28
+ # exclude:
29
+ # - vendor
30
+ # - "**/*.min.js"
31
+ # - exclude_file.rb
32
+ # targets:
33
+ # - target_directory
34
+ # - another_target_directory/foo.rb
35
+ # - bar.rb
36
+ # locale: UK
37
+ # ignore: center,behavior
38
+
39
+ # # https://help.sider.review/tools/shellscript/shellcheck
40
+ # shellcheck:
41
+ # target: "src/**/*.{sh,bash}"
42
+ # include: "SC2104,SC2105"
43
+ # exclude: "SC1000,SC1118"
44
+ # enable: "all"
45
+ # shell: "bash"
46
+ # severity: "error"
47
+ # norc: true
48
+
49
+ # # https://help.sider.review/getting-started/custom-configuration#ignore
50
+ # ignore:
51
+ # - "*.pdf"
52
+ # - "*.mp4"
53
+ # - "images/**"
54
+
55
+ # # https://help.sider.review/getting-started/custom-configuration#branchesexclude
56
+ # branches:
57
+ # exclude:
58
+ # - master
59
+ # - development
60
+ # - /^release-.*$/
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.1.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - OKURA Masafumi
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-02-11 00:00:00.000000000 Z
11
+ date: 2020-07-31 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Fast and flexible JSON serializer
14
14
  email:
@@ -31,10 +31,14 @@ files:
31
31
  - bin/setup
32
32
  - lib/alba.rb
33
33
  - lib/alba/attribute.rb
34
+ - lib/alba/many.rb
35
+ - lib/alba/one.rb
34
36
  - lib/alba/resource.rb
37
+ - lib/alba/resources/default_resource.rb
35
38
  - lib/alba/serializer.rb
36
39
  - lib/alba/serializers/default_serializer.rb
37
40
  - lib/alba/version.rb
41
+ - sider.yml
38
42
  homepage: https://github.com/okuramasafumi/alba
39
43
  licenses:
40
44
  - MIT
@@ -42,7 +46,7 @@ metadata:
42
46
  homepage_uri: https://github.com/okuramasafumi/alba
43
47
  source_code_uri: https://github.com/okuramasafumi/alba
44
48
  changelog_uri: https://github.com/okuramasafumi/alba/CHANGELOG.md
45
- post_install_message:
49
+ post_install_message:
46
50
  rdoc_options: []
47
51
  require_paths:
48
52
  - lib
@@ -57,8 +61,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
57
61
  - !ruby/object:Gem::Version
58
62
  version: '0'
59
63
  requirements: []
60
- rubygems_version: 3.1.2
61
- signing_key:
64
+ rubygems_version: 3.1.4
65
+ signing_key:
62
66
  specification_version: 4
63
67
  summary: Fast and flexible JSON serializer
64
68
  test_files: []