alba 0.0.1 → 0.5.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: 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: []