alba 0.0.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1a21e18114f54b4039fc354a92a248e3bb6d1d67680c086d8d7827ef201ffec0
4
- data.tar.gz: 507160ee7625df8bfb18955cca17b1eef804be1783ce4c61fac8f383eeac6301
3
+ metadata.gz: 89b4df7ce18a63e20cee9e87bd2855ef9c4735c4f6e4e1ef55433c5ba0a2fbd0
4
+ data.tar.gz: 63fa94fa17cdf3f5bfeef5f2e7b0d6efeec1fd06ad8d5386fb389fea3db0343a
5
5
  SHA512:
6
- metadata.gz: e97a450e7cbfaea63a048d20c396b6abfaae1cc55957e2e07cc1170fe4be5cc4174d807c1c398fb2534d120e9c3fa0cbb6f8cc9dd5c7026ddb448577a66ddf0f
7
- data.tar.gz: '08e02980bbe0ae8c9dc355f483399a87b120c3817bb3d7ddbfaec4ccd584c76d08825bca81088a3004636b14373d675c3ca861ce82fd41efabc0669b1ab701f8'
6
+ metadata.gz: 063d913715faa3917df5658d798ceb1619b9a2aef1dc0875c92c1592b3d3f9025bec60a364ebe0d54fb0769e18ba78f5786caa288159d304dfeca7ab00149d2b
7
+ data.tar.gz: 66aed62d355cd6fd8527ad4da27c53c9c42bf98606586a562b84dc377e6041125653d9669cf77c14427b4a1796b6c7abd7e6b2861323bac51fde28bfe05a7e7a
@@ -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,9 @@ 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'
7
8
  gem 'rake', '~> 13.0'
8
9
  gem 'rubocop', '>= 0.79.0', require: false
9
- gem 'rubocop-performance', '~> 1.5.0', require: false
10
+ gem 'rubocop-performance', '~> 1.7.1', require: false
10
11
  gem 'rubocop-sensible', '~> 0.3.0', require: false
@@ -1,42 +1,67 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- alba (0.0.1)
4
+ alba (0.5.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
+ parallel (1.19.2)
20
+ parser (2.7.1.4)
21
+ ast (~> 2.4.1)
15
22
  rainbow (3.0.0)
16
23
  rake (13.0.1)
17
- rubocop (0.79.0)
18
- jaro_winkler (~> 1.5.1)
24
+ regexp_parser (1.7.1)
25
+ rexml (3.2.4)
26
+ rubocop (0.88.0)
19
27
  parallel (~> 1.10)
20
- parser (>= 2.7.0.1)
28
+ parser (>= 2.7.1.1)
21
29
  rainbow (>= 2.2.2, < 4.0)
30
+ regexp_parser (>= 1.7)
31
+ rexml
32
+ rubocop-ast (>= 0.1.0, < 1.0)
22
33
  ruby-progressbar (~> 1.7)
23
- unicode-display_width (>= 1.4.0, < 1.7)
24
- rubocop-performance (1.5.2)
25
- rubocop (>= 0.71.0)
34
+ unicode-display_width (>= 1.4.0, < 2.0)
35
+ rubocop-ast (0.2.0)
36
+ parser (>= 2.7.0.1)
37
+ rubocop-performance (1.7.1)
38
+ rubocop (>= 0.82.0)
26
39
  rubocop-sensible (0.3.0)
27
40
  rubocop (>= 0.60.0)
28
41
  ruby-progressbar (1.10.1)
29
- unicode-display_width (1.6.1)
42
+ simplecov (0.16.1)
43
+ docile (~> 1.1)
44
+ json (>= 1.8, < 3)
45
+ simplecov-html (~> 0.10.0)
46
+ simplecov-html (0.10.2)
47
+ sync (0.5.0)
48
+ term-ansicolor (1.7.1)
49
+ tins (~> 1.0)
50
+ thor (1.0.1)
51
+ tins (1.25.0)
52
+ sync
53
+ unicode-display_width (1.7.0)
30
54
 
31
55
  PLATFORMS
32
56
  ruby
33
57
 
34
58
  DEPENDENCIES
35
59
  alba!
60
+ coveralls
36
61
  minitest (~> 5.0)
37
62
  rake (~> 13.0)
38
63
  rubocop (>= 0.79.0)
39
- rubocop-performance (~> 1.5.0)
64
+ rubocop-performance (~> 1.7.1)
40
65
  rubocop-sensible (~> 0.3.0)
41
66
 
42
67
  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,15 +24,115 @@ Or install it yourself as:
20
24
 
21
25
  ## Usage
22
26
 
27
+ ### Simple serialization with key
28
+
23
29
  ```ruby
24
- # If you don't bundle 'oj', that's file
25
- Alba.serialize({foo: 42}) # => '{"foo":42}'
30
+ class User
31
+ attr_accessor :id, :name, :email, :created_at, :updated_at
32
+ def initialize(id, name, email)
33
+ @id = id
34
+ @name = name
35
+ @email = email
36
+ @created_at = Time.now
37
+ @updated_at = Time.now
38
+ end
39
+ end
40
+
41
+ class UserResource
42
+ include Alba::Resource
43
+
44
+ attributes :id, :name
45
+
46
+ attribute :name_with_email do |resource|
47
+ "#{resource.name}: #{resource.email}"
48
+ end
49
+ end
50
+
51
+ class SerializerWithKey
52
+ include Alba::Serializer
53
+
54
+ set key: :user
55
+ end
56
+
57
+ user = User.new(1, 'Masafumi OKURA', 'masafumi@example.com')
58
+ UserResource.new(user).serialize
59
+ # => "{\"id\":1,\"name\":\"Masafumi OKURA\",\"name_with_email\":\"Masafumi OKURA: masafumi@example.com\"}"
60
+ ```
61
+
62
+ ### Serialization with associations
26
63
 
27
- # bundle 'oj' in your Gemfile, then
28
- Alba.backend = :oj
29
- Alba.serialize({foo: 42}) # => '{"foo":42}'
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"}]}'
30
109
  ```
31
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
+
32
136
  ## Development
33
137
 
34
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,30 +1,32 @@
1
1
  require 'alba/version'
2
- require 'json'
2
+ require 'alba/serializers/default_serializer'
3
+ require 'alba/serializer'
4
+ require 'alba/resource'
5
+ require 'alba/resources/default_resource'
3
6
 
4
7
  # Core module
5
8
  module Alba
6
9
  class Error < StandardError; end
7
10
 
8
- def self.backend=(backend)
9
- @backend = backend&.to_sym
10
- end
11
+ class << self
12
+ attr_reader :backend
11
13
 
12
- def self.backend
13
- @backend
14
- end
14
+ def backend=(backend)
15
+ @backend = backend&.to_sym
16
+ end
17
+
18
+ def serialize(object, with: nil, &block)
19
+ raise ArgumentError, 'Block required' unless block
20
+
21
+ resource_class.class_eval(&block)
22
+ resource = resource_class.new(object)
23
+ resource.serialize(with: with)
24
+ end
25
+
26
+ private
15
27
 
16
- def self.serialize(object)
17
- fallback = ->(resource) { resource.to_json }
18
- case backend
19
- when :oj
20
- begin
21
- require 'oj'
22
- ->(resource) { Oj.dump(resource) }
23
- rescue LoadError
24
- fallback
25
- end
26
- else
27
- fallback
28
- end.call(object)
28
+ def resource_class
29
+ ::Alba::Resources::DefaultResource.clone
30
+ end
29
31
  end
30
32
  end
@@ -0,0 +1,19 @@
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
@@ -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
@@ -0,0 +1,91 @@
1
+ require 'alba/serializer'
2
+ require 'alba/attribute'
3
+ require 'alba/one'
4
+ require 'alba/many'
5
+ require 'alba/serializers/default_serializer'
6
+
7
+ module Alba
8
+ # This module represents what should be serialized
9
+ module Resource
10
+ DSLS = [:_attributes, :_serializer].freeze
11
+ def self.included(base)
12
+ base.class_eval do
13
+ # Initialize
14
+ DSLS.each do |name|
15
+ initial = name == :_serializer ? nil : {}
16
+ instance_variable_set("@#{name}", initial) unless instance_variable_defined?("@#{name}")
17
+ end
18
+ end
19
+ base.include InstanceMethods
20
+ base.extend ClassMethods
21
+ end
22
+
23
+ # Instance methods
24
+ module InstanceMethods
25
+ def initialize(resource)
26
+ @_resource = resource
27
+ DSLS.each { |name| instance_variable_set("@#{name}", self.class.public_send(name)) }
28
+ end
29
+
30
+ def serialize(with: nil)
31
+ serializer = case with
32
+ when nil
33
+ @_serializer || Alba::Serializers::DefaultSerializer
34
+ when ->(obj) { obj.is_a?(Class) && obj <= Alba::Serializer }
35
+ with
36
+ when Proc
37
+ inline_extended_serializer(with)
38
+ else
39
+ raise ArgumentError, 'Unexpected type for with, possible types are Class or Proc'
40
+ end
41
+ serializer.new(serializable_hash).serialize
42
+ end
43
+
44
+ def serializable_hash
45
+ @_attributes.transform_values do |attribute|
46
+ attribute.to_hash(@_resource)
47
+ end
48
+ end
49
+ alias to_hash serializable_hash
50
+
51
+ private
52
+
53
+ def inline_extended_serializer(with)
54
+ klass = ::Alba::Serializers::DefaultSerializer.clone
55
+ klass.class_eval(&with)
56
+ klass
57
+ end
58
+ end
59
+
60
+ # Class methods
61
+ module ClassMethods
62
+ attr_accessor(*DSLS)
63
+
64
+ def inherited(subclass)
65
+ DSLS.each { |name| subclass.public_send("#{name}=", instance_variable_get("@#{name}")) }
66
+ end
67
+
68
+ def attributes(*attrs)
69
+ attrs.each { |attr_name| @_attributes[attr_name] = Attribute.new(name: attr_name, method: attr_name) }
70
+ end
71
+
72
+ def attribute(name, &block)
73
+ raise ArgumentError, 'No block given in attribute method' unless block
74
+
75
+ @_attributes[name] = Attribute.new(name: name, method: block)
76
+ end
77
+
78
+ def one(name, resource: nil, &block)
79
+ @_attributes[name.to_sym] = One.new(name: name, resource: resource, &block)
80
+ end
81
+
82
+ def many(name, resource: nil, &block)
83
+ @_attributes[name.to_sym] = Many.new(name: name, resource: resource, &block)
84
+ end
85
+
86
+ def serializer(name)
87
+ @_serializer = name <= Alba::Serializer ? name : nil
88
+ end
89
+ end
90
+ end
91
+ 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
@@ -0,0 +1,48 @@
1
+ module Alba
2
+ # This module represents how a resource should be serialized.
3
+ #
4
+ module Serializer
5
+ def self.included(base)
6
+ base.include InstanceMethods
7
+ base.extend ClassMethods
8
+ end
9
+
10
+ # Instance methods
11
+ module InstanceMethods
12
+ def initialize(resource)
13
+ @_resource = resource
14
+ @_opts = self.class._opts || {}
15
+ key = @_opts[:key]
16
+ @_resource = {key.to_sym => @_resource} if key
17
+ end
18
+
19
+ 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
32
+ else
33
+ fallback
34
+ end.call
35
+ end
36
+ end
37
+
38
+ # Class methods
39
+ module ClassMethods
40
+ attr_reader :_opts
41
+
42
+ def set(key: false)
43
+ @_opts ||= {}
44
+ @_opts[:key] = key
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,11 @@
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
@@ -1,3 +1,3 @@
1
1
  module Alba
2
- VERSION = '0.0.1'.freeze
2
+ VERSION = '0.5.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.0.1
4
+ version: 0.5.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-01-21 00:00:00.000000000 Z
11
+ date: 2020-07-30 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Fast and flexible JSON serializer
14
14
  email:
@@ -30,7 +30,15 @@ files:
30
30
  - bin/console
31
31
  - bin/setup
32
32
  - lib/alba.rb
33
+ - lib/alba/attribute.rb
34
+ - lib/alba/many.rb
35
+ - lib/alba/one.rb
36
+ - lib/alba/resource.rb
37
+ - lib/alba/resources/default_resource.rb
38
+ - lib/alba/serializer.rb
39
+ - lib/alba/serializers/default_serializer.rb
33
40
  - lib/alba/version.rb
41
+ - sider.yml
34
42
  homepage: https://github.com/okuramasafumi/alba
35
43
  licenses:
36
44
  - MIT
@@ -38,7 +46,7 @@ metadata:
38
46
  homepage_uri: https://github.com/okuramasafumi/alba
39
47
  source_code_uri: https://github.com/okuramasafumi/alba
40
48
  changelog_uri: https://github.com/okuramasafumi/alba/CHANGELOG.md
41
- post_install_message:
49
+ post_install_message:
42
50
  rdoc_options: []
43
51
  require_paths:
44
52
  - lib
@@ -53,8 +61,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
53
61
  - !ruby/object:Gem::Version
54
62
  version: '0'
55
63
  requirements: []
56
- rubygems_version: 3.1.2
57
- signing_key:
64
+ rubygems_version: 3.1.4
65
+ signing_key:
58
66
  specification_version: 4
59
67
  summary: Fast and flexible JSON serializer
60
68
  test_files: []