userializer 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 8542c96c9a4351c5f5938bf260c61f6f97bf0e7a5cf9763325a76c45b70e3741
4
+ data.tar.gz: fb19b8e8ddab2c52402c5273d129000a10f607d2b0b9d53a7ac3aa56d09bfed7
5
+ SHA512:
6
+ metadata.gz: 6e9d0f07bf62c25b23c88974bcc536c8ce42500bdfeb4c800d19d63f5d2b630212cd4bc67aabd403b2ec4e1a8364a5847c0fd8564abec5d9feeb4149b4ab9a17
7
+ data.tar.gz: dca8b690e2fb0095acb779a2849f304b6dac736d50759eb2de2c978db10268b3bdbcb95ad8abc465a72f7505c2e16a8575b83c0696f920c5aaa5a6fa4fac587e
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.5.0
5
+ before_install: gem install bundler -v 1.16.1
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in userializer.gemspec
6
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,50 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ userializer (0.1.0)
5
+ activesupport
6
+ oj
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ activesupport (5.2.3)
12
+ concurrent-ruby (~> 1.0, >= 1.0.2)
13
+ i18n (>= 0.7, < 2)
14
+ minitest (~> 5.1)
15
+ tzinfo (~> 1.1)
16
+ concurrent-ruby (1.1.5)
17
+ diff-lcs (1.3)
18
+ i18n (1.6.0)
19
+ concurrent-ruby (~> 1.0)
20
+ minitest (5.11.3)
21
+ oj (3.7.12)
22
+ rake (10.4.2)
23
+ rspec (3.8.0)
24
+ rspec-core (~> 3.8.0)
25
+ rspec-expectations (~> 3.8.0)
26
+ rspec-mocks (~> 3.8.0)
27
+ rspec-core (3.8.0)
28
+ rspec-support (~> 3.8.0)
29
+ rspec-expectations (3.8.3)
30
+ diff-lcs (>= 1.2.0, < 2.0)
31
+ rspec-support (~> 3.8.0)
32
+ rspec-mocks (3.8.0)
33
+ diff-lcs (>= 1.2.0, < 2.0)
34
+ rspec-support (~> 3.8.0)
35
+ rspec-support (3.8.0)
36
+ thread_safe (0.3.6)
37
+ tzinfo (1.2.5)
38
+ thread_safe (~> 0.1)
39
+
40
+ PLATFORMS
41
+ ruby
42
+
43
+ DEPENDENCIES
44
+ bundler (~> 1.16)
45
+ rake (~> 10.0)
46
+ rspec (~> 3.0)
47
+ userializer!
48
+
49
+ BUNDLED WITH
50
+ 1.16.1
data/README.md ADDED
@@ -0,0 +1,33 @@
1
+ # Userializer
2
+
3
+ Ruby object JSON serializer.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'userializer'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install userializer
20
+
21
+ ## Usage
22
+
23
+ TODO: Write usage instructions here
24
+
25
+ ## Development
26
+
27
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
28
+
29
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
30
+
31
+ ## Contributing
32
+
33
+ Bug reports and pull requests are welcome on GitHub at https://github.com/AlexisMontagne/userializer.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "userializer"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,19 @@
1
+ require 'userializer/version'
2
+ require 'userializer/base_serializer'
3
+ require 'userializer/array_serializer'
4
+
5
+ module USerializer
6
+ NS_SEPARATOR = '::'.freeze
7
+
8
+ class << self
9
+ def serializer_for(obj)
10
+ infered_serializer_class(obj.class)
11
+ end
12
+
13
+ def infered_serializer_class(kls)
14
+ (kls.name + 'Serializer').split(NS_SEPARATOR).inject(Object) do |o, c|
15
+ o.const_get(c)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,47 @@
1
+ require 'oj'
2
+
3
+ module USerializer
4
+ class HeterogeneousArray < StandardError; end
5
+
6
+ class ArraySerializer
7
+ def initialize(objs, opts = {})
8
+ @objs = objs.compact
9
+ @opts = opts
10
+ @meta = opts[:meta]
11
+
12
+ clss = @objs.map(&:class).uniq
13
+ obj_class = clss.first
14
+
15
+ raise HeterogeneousArray if clss.count > 1
16
+
17
+ @root_key = opts[:root] || ActiveSupport::Inflector.pluralize(
18
+ ActiveSupport::Inflector.underscore(obj_class.name).split('/').last
19
+ ).to_sym
20
+
21
+ @serializer = opts[:each_serializer] || USerializer.infered_serializer_class(
22
+ obj_class
23
+ )
24
+ end
25
+
26
+ def merge_root(res, opts)
27
+ @objs.each do |obj|
28
+ @serializer.new(obj, @opts).merge_root(res, @root_key, false, opts)
29
+ end
30
+ end
31
+
32
+ def to_hash
33
+ res = {}
34
+
35
+ merge_root(res, @opts)
36
+ res[:meta] = @meta if @meta
37
+
38
+ res
39
+ end
40
+
41
+ def to_json
42
+ Oj.dump(to_hash, mode: :compat)
43
+ end
44
+
45
+ def scope; @opts[:scope]; end
46
+ end
47
+ end
@@ -0,0 +1,19 @@
1
+ module USerializer
2
+ class Attribute
3
+ attr_reader :key
4
+
5
+ def initialize(key, opts, block)
6
+ @key = key
7
+ @opts = opts
8
+ @block = block
9
+
10
+ @conditional_block = opts[:if] || proc { true }
11
+ end
12
+
13
+ def merge_attributes(res, ser, opts)
14
+ return unless @conditional_block.call(ser.object, opts)
15
+
16
+ res[@key] = @block ? @block.call(ser.object, opts) : ser.send(@key)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,113 @@
1
+ require 'oj'
2
+ require 'active_support/inflector/methods'
3
+ require 'userializer/has_one'
4
+ require 'userializer/has_many'
5
+ require 'userializer/attribute'
6
+
7
+ module USerializer
8
+ class BaseSerializer
9
+ class << self
10
+ def inherited(subclass)
11
+ subclass.attrs = self.attrs || { id: Attribute.new(:id, {}, nil) }
12
+ subclass.relations = self.relations || {}
13
+ end
14
+
15
+ def attributes(*attrs, &block)
16
+ attrs = attrs.first if attrs.first.class.is_a?(Array)
17
+ opts = attrs.last.is_a?(Hash) ? attrs.pop : {}
18
+
19
+ attrs.each { |attr| @attrs[attr] = Attribute.new(attr, opts, block) }
20
+ end
21
+
22
+ def has_one(*attrs)
23
+ attrs = attrs.first if attrs.first.class.is_a?(Array)
24
+ opts = attrs.last.is_a?(Hash) ? attrs.pop : {}
25
+
26
+ attrs.each { |attr| @relations[attr] = HasOne.new(attr, opts) }
27
+ end
28
+
29
+ def has_many(*attrs)
30
+ attrs = attrs.first if attrs.first.class.is_a?(Array)
31
+ opts = attrs.last.is_a?(Hash) ? attrs.pop : {}
32
+
33
+ attrs.each { |attr| @relations[attr] = HasMany.new(attr, opts) }
34
+ end
35
+
36
+ attr_accessor :attrs, :relations
37
+ end
38
+
39
+ attr_reader :obj, :meta, :opts
40
+
41
+ alias object obj
42
+
43
+ def initialize(obj, opts = {})
44
+ @obj = obj
45
+ @opts = opts
46
+ @meta = opts[:meta]
47
+ @except = Set.new([opts[:except]].flatten.compact)
48
+
49
+ @root_key = (opts[:root] || ActiveSupport::Inflector.underscore(
50
+ obj.class.name
51
+ ).split('/').last).to_sym
52
+ end
53
+
54
+ def serializable_hash(opts)
55
+ res = {}
56
+
57
+ attributes.each { |attr| attr.merge_attributes(res, self, opts) }
58
+ relations.each do |rel|
59
+ rel.merge_attributes(res, self, opts)
60
+ end
61
+
62
+ res
63
+ end
64
+
65
+ def merge_root(res, key, single, opts)
66
+ if single
67
+ res[key] = serializable_hash(opts)
68
+ else
69
+ res[key] ||= []
70
+
71
+ id = @obj.id
72
+
73
+ if res[key].detect { |v| id && v[:id] == id }
74
+ return
75
+ else
76
+ res[key] << serializable_hash(opts)
77
+ end
78
+ end
79
+
80
+ relations.each { |rel| rel.merge_root(res, self, opts) }
81
+ end
82
+
83
+ def to_hash
84
+ res = {}
85
+
86
+ merge_root(res, @root_key, true, @opts.slice(:scope))
87
+
88
+ res[:meta] = @meta if @meta
89
+
90
+ res
91
+ end
92
+
93
+ def to_json
94
+ Oj.dump(to_hash, mode: :compat)
95
+ end
96
+
97
+ def method_missing(mth); @obj.send(mth); end
98
+
99
+ private
100
+
101
+ def attributes
102
+ @attributes ||= (self.class.attrs || {}).values.reject do |attr|
103
+ @except.include?(attr.key)
104
+ end
105
+ end
106
+
107
+ def relations
108
+ @relations ||= (self.class.relations || {}).values.reject do |rel|
109
+ @except.include?(rel.key)
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,28 @@
1
+ module USerializer
2
+ class HasMany
3
+ attr_reader :key
4
+
5
+ def initialize(key, opts)
6
+ @key = key
7
+
8
+ @opts = opts
9
+ @id_key = "#{ActiveSupport::Inflector.singularize(key)}_ids".to_sym
10
+
11
+ @conditional_block = opts[:if] || proc { true }
12
+ end
13
+
14
+ def merge_attributes(res, ser, opts)
15
+ return unless @conditional_block.call(ser.object, opts)
16
+
17
+ res[@id_key] = (ser.send(@key) || []).compact.map(&:id).compact
18
+ end
19
+
20
+ def merge_root(res, ser, opts)
21
+ objs = ser.send(@key) || []
22
+
23
+ return if objs.empty? || !@conditional_block.call(ser.object, opts)
24
+
25
+ ArraySerializer.new(objs, @opts).merge_root(res, opts)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,48 @@
1
+ module USerializer
2
+ class HasOne
3
+ def initialize(key, opts)
4
+ @key = key
5
+ @opts = opts
6
+ @id_key = "#{key}_id".to_sym
7
+ @root_key = opts[:root]&.to_sym
8
+
9
+ @serializer = opts[:serializer]
10
+
11
+ @conditional_block = opts[:if] || proc { true }
12
+ end
13
+
14
+ attr_reader :id_key, :key
15
+
16
+ def merge_attributes(res, ser, opts)
17
+ return unless @conditional_block.call(ser.object, opts)
18
+
19
+ obj = ser.send(@key)
20
+ res[@id_key] = obj&.id
21
+ end
22
+
23
+ def merge_root(res, ser, opts)
24
+ obj = ser.send(@key)
25
+
26
+ return if obj.nil? || !@conditional_block.call(ser.object, opts)
27
+
28
+ serializer(obj).merge_root(res, root_key(obj), false, opts)
29
+ end
30
+
31
+ private
32
+
33
+ def serializer(obj)
34
+ return @serializer.new(obj, @opts) if @serializer
35
+ return obj.serialize if obj.respond_to?(:serialize)
36
+
37
+ USerializer.infered_serializer_class(obj.class).new(obj, @opts)
38
+ end
39
+
40
+ def root_key(obj)
41
+ return @root_key if @root_key
42
+
43
+ ActiveSupport::Inflector.pluralize(
44
+ ActiveSupport::Inflector.underscore(obj.class.name).split('/').last
45
+ ).to_sym
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,3 @@
1
+ module USerializer
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,28 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "userializer/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "userializer"
8
+ spec.version = USerializer::VERSION
9
+ spec.authors = ["Alexis Montagne"]
10
+ spec.email = ["alexis.montagne@gmail.com"]
11
+
12
+ spec.summary = %q{Write a short summary, because RubyGems requires one.}
13
+ spec.description = %q{Write a longer description or delete this line.}
14
+ spec.homepage = 'https://github.com/upfluence/userializer'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
19
+ spec.bindir = "exe"
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.require_paths = ["lib"]
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.16"
24
+ spec.add_development_dependency "rake", "~> 10.0"
25
+ spec.add_development_dependency "rspec", "~> 3.0"
26
+ spec.add_dependency "oj"
27
+ spec.add_dependency "activesupport"
28
+ end
metadata ADDED
@@ -0,0 +1,130 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: userializer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Alexis Montagne
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2019-05-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.16'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.16'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: oj
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: activesupport
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Write a longer description or delete this line.
84
+ email:
85
+ - alexis.montagne@gmail.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - ".rspec"
92
+ - ".travis.yml"
93
+ - Gemfile
94
+ - Gemfile.lock
95
+ - README.md
96
+ - Rakefile
97
+ - bin/console
98
+ - bin/setup
99
+ - lib/userializer.rb
100
+ - lib/userializer/array_serializer.rb
101
+ - lib/userializer/attribute.rb
102
+ - lib/userializer/base_serializer.rb
103
+ - lib/userializer/has_many.rb
104
+ - lib/userializer/has_one.rb
105
+ - lib/userializer/version.rb
106
+ - userializer.gemspec
107
+ homepage: https://github.com/upfluence/userializer
108
+ licenses: []
109
+ metadata: {}
110
+ post_install_message:
111
+ rdoc_options: []
112
+ require_paths:
113
+ - lib
114
+ required_ruby_version: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ required_rubygems_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ requirements: []
125
+ rubyforge_project:
126
+ rubygems_version: 2.7.3
127
+ signing_key:
128
+ specification_version: 4
129
+ summary: Write a short summary, because RubyGems requires one.
130
+ test_files: []