panko_serializer 0.1.1

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.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.clang-format +102 -0
  3. data/.gitignore +12 -0
  4. data/.rspec +2 -0
  5. data/.rubocop.yml +22 -0
  6. data/.travis.yml +8 -0
  7. data/Gemfile +36 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +8 -0
  10. data/Rakefile +62 -0
  11. data/benchmarks/BENCHMARKS.md +48 -0
  12. data/benchmarks/allocs.rb +23 -0
  13. data/benchmarks/app.rb +13 -0
  14. data/benchmarks/benchmarking_support.rb +43 -0
  15. data/benchmarks/bm_active_model_serializers.rb +45 -0
  16. data/benchmarks/bm_controller.rb +81 -0
  17. data/benchmarks/bm_panko_json.rb +60 -0
  18. data/benchmarks/bm_panko_object.rb +69 -0
  19. data/benchmarks/profile.rb +88 -0
  20. data/benchmarks/sanity.rb +67 -0
  21. data/benchmarks/setup.rb +62 -0
  22. data/benchmarks/type_casts/bm_active_record.rb +57 -0
  23. data/benchmarks/type_casts/bm_panko.rb +67 -0
  24. data/benchmarks/type_casts/bm_pg.rb +35 -0
  25. data/benchmarks/type_casts/support.rb +16 -0
  26. data/ext/panko_serializer/attributes_iterator.c +62 -0
  27. data/ext/panko_serializer/attributes_iterator.h +17 -0
  28. data/ext/panko_serializer/extconf.rb +8 -0
  29. data/ext/panko_serializer/panko_serializer.c +189 -0
  30. data/ext/panko_serializer/panko_serializer.h +17 -0
  31. data/ext/panko_serializer/serialization_descriptor.c +166 -0
  32. data/ext/panko_serializer/serialization_descriptor.h +30 -0
  33. data/ext/panko_serializer/time_conversion.c +94 -0
  34. data/ext/panko_serializer/time_conversion.h +6 -0
  35. data/ext/panko_serializer/type_cast.c +271 -0
  36. data/ext/panko_serializer/type_cast.h +74 -0
  37. data/lib/panko/array_serializer.rb +40 -0
  38. data/lib/panko/cache.rb +35 -0
  39. data/lib/panko/serialization_descriptor.rb +119 -0
  40. data/lib/panko/serializer.rb +57 -0
  41. data/lib/panko/version.rb +4 -0
  42. data/lib/panko.rb +8 -0
  43. data/panko_serializer.gemspec +31 -0
  44. metadata +171 -0
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+ require "concurrent"
3
+
4
+
5
+ module Panko
6
+ # Serialer Descriptor Cache
7
+ class Cache
8
+ def initialize
9
+ @_cache = Concurrent::Map.new
10
+ end
11
+
12
+ def fetch(serializer_const, options)
13
+ serializer_key = build_key(serializer_const, options[:only], options[:except])
14
+ serializer = @_cache.compute_if_absent(serializer_key) {
15
+ SerializationDescriptorBuilder.build(serializer_const,
16
+ only: options.fetch(:only, []),
17
+ except: options.fetch(:except, [])
18
+ )
19
+ }
20
+
21
+ serializer
22
+ end
23
+
24
+ private
25
+
26
+ #
27
+ # Create key based on what we define unique serializer
28
+ #
29
+ def build_key(serializer_const, only, except)
30
+ [serializer_const, only, except].hash
31
+ end
32
+ end
33
+
34
+ CACHE = Panko::Cache.new
35
+ end
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+ module Panko
3
+ module SerializationDescriptorBuilder
4
+ def self.build(serializer, options={})
5
+ backend = Panko::SerializationDescriptor.new
6
+
7
+ serializer_only_filters, attributes_only_filters = resolve_filters(options, :only)
8
+ serializer_except_filters, attributes_except_filters = resolve_filters(options, :except)
9
+
10
+ fields, method_fields = fields_of(serializer)
11
+
12
+ backend.type = serializer
13
+
14
+ backend.fields = apply_fields_filters(
15
+ fields,
16
+ serializer_only_filters,
17
+ serializer_except_filters
18
+ )
19
+
20
+ backend.method_fields = apply_fields_filters(
21
+ method_fields,
22
+ serializer_only_filters,
23
+ serializer_except_filters
24
+ )
25
+
26
+ backend.has_many_associations = build_associations(
27
+ apply_association_filters(
28
+ serializer._has_many_associations,
29
+ serializer_only_filters,
30
+ serializer_except_filters
31
+ ),
32
+ attributes_only_filters,
33
+ attributes_except_filters
34
+ )
35
+
36
+ backend.has_one_associations = build_associations(
37
+ apply_association_filters(
38
+ serializer._has_one_associations,
39
+ serializer_only_filters,
40
+ serializer_except_filters
41
+ ),
42
+ attributes_only_filters,
43
+ attributes_except_filters
44
+ )
45
+
46
+ backend
47
+ end
48
+
49
+ def self.fields_of(serializer)
50
+ fields = []
51
+ method_fields = []
52
+
53
+ serializer._attributes.each do |attribute|
54
+ if serializer.method_defined? attribute
55
+ method_fields << attribute
56
+ else
57
+ fields << attribute
58
+ end
59
+ end
60
+
61
+ return fields, method_fields
62
+ end
63
+
64
+ def self.build_associations(associations, attributes_only_filters, attributes_except_filters)
65
+ associations.map do |association|
66
+ options = association[:options]
67
+ serializer_const = resolve_serializer(options[:serializer])
68
+
69
+ options[:only] = options.fetch(:only, []) + attributes_only_filters.fetch(association[:name], [])
70
+ options[:except] = options.fetch(:except, []) + attributes_except_filters.fetch(association[:name], [])
71
+
72
+ [
73
+ association[:name],
74
+ SerializationDescriptorBuilder.build(serializer_const, options.except(:serializer))
75
+ ]
76
+ end
77
+ end
78
+
79
+ def self.resolve_filters(options, filter)
80
+ filters = options.fetch(filter, {})
81
+ if filters.is_a? Array
82
+ return filters, {}
83
+ end
84
+
85
+ # hash filters looks like this
86
+ # { instance: [:a], foo: [:b] }
87
+ # which mean, for the current instance use `[:a]` as filter
88
+ # and for association named `foo` use `[:b]`
89
+
90
+ serializer_filters = filters.fetch(:instance, [])
91
+ association_filters = filters.except(:instance)
92
+
93
+ return serializer_filters, association_filters
94
+ end
95
+
96
+ def self.apply_fields_filters(fields, only, except)
97
+ return fields & only if only.present?
98
+ return fields - except if except.present?
99
+
100
+ fields
101
+ end
102
+
103
+ def self.apply_association_filters(associations, only, except)
104
+ if only.present?
105
+ return associations.select { |assoc| only.include?(assoc[:name]) }
106
+ end
107
+
108
+ if except.present?
109
+ return associations.reject { |assoc| except.include?(assoc[:name]) }
110
+ end
111
+
112
+ associations
113
+ end
114
+
115
+ def self.resolve_serializer(serializer)
116
+ Object.const_get(serializer.name)
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+ require_relative "cache"
3
+ require_relative "serialization_descriptor"
4
+ require "oj"
5
+
6
+ module Panko
7
+ class Serializer
8
+ class << self
9
+ def inherited(base)
10
+ base._attributes = (_attributes || []).dup
11
+ base._has_one_associations = (_has_one_associations || []).dup
12
+ base._has_many_associations = (_has_many_associations || []).dup
13
+
14
+ @_attributes = []
15
+ @_has_one_associations = []
16
+ @_has_many_associations = []
17
+ end
18
+
19
+ attr_accessor :_attributes, :_has_one_associations, :_has_many_associations
20
+
21
+ def attributes(*attrs)
22
+ @_attributes.push(*attrs).uniq!
23
+ end
24
+
25
+ def has_one(name, options)
26
+ @_has_one_associations << { name: name, options: options }
27
+ end
28
+
29
+ def has_many(name, options)
30
+ @_has_many_associations << { name: name, options: options }
31
+ end
32
+ end
33
+
34
+ def initialize(options = {})
35
+ @descriptor = Panko::CACHE.fetch(self.class, options)
36
+
37
+ @context = options.fetch(:context, nil)
38
+ @object = nil
39
+ end
40
+
41
+ attr_reader :object, :context
42
+ attr_writer :object
43
+
44
+ def serialize(object, writer = nil)
45
+ Oj.load(serialize_to_json(object, writer))
46
+ end
47
+
48
+ def serialize_to_json(object, writer = nil)
49
+ @object = object
50
+
51
+ writer ||= Oj::StringWriter.new(mode: :rails)
52
+ Panko::serialize_subject(object, writer, @descriptor, @context)
53
+
54
+ writer.to_s
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ module Panko
3
+ VERSION = "0.1.1".freeze
4
+ end
data/lib/panko.rb ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+ require "panko/version"
3
+ require "panko/serializer"
4
+ require "panko/array_serializer"
5
+
6
+
7
+ # C Extension
8
+ require "panko_serializer/panko_serializer"
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ # frozen_string_literal: true
3
+ lib = File.expand_path("../lib", __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require "panko/version"
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "panko_serializer"
9
+ spec.version = Panko::VERSION
10
+ spec.authors = ["Yosi Attias"]
11
+ spec.email = ["yosy101@gmail.com"]
12
+
13
+ spec.summary = "Fast serialization for ActiveModel"
14
+ spec.homepage = ""
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(test|spec|features)/})
19
+ end
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.extensions << "ext/panko_serializer/extconf.rb"
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.14"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "rspec", "~> 3.0"
27
+ spec.add_development_dependency "rake-compiler"
28
+
29
+ spec.add_dependency "oj", "~> 3.2.0"
30
+ spec.add_dependency "concurrent-ruby"
31
+ end
metadata ADDED
@@ -0,0 +1,171 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: panko_serializer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Yosi Attias
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-08-13 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.14'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.14'
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: rake-compiler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
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: oj
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 3.2.0
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 3.2.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: concurrent-ruby
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description:
98
+ email:
99
+ - yosy101@gmail.com
100
+ executables: []
101
+ extensions:
102
+ - ext/panko_serializer/extconf.rb
103
+ extra_rdoc_files: []
104
+ files:
105
+ - ".clang-format"
106
+ - ".gitignore"
107
+ - ".rspec"
108
+ - ".rubocop.yml"
109
+ - ".travis.yml"
110
+ - Gemfile
111
+ - LICENSE.txt
112
+ - README.md
113
+ - Rakefile
114
+ - benchmarks/BENCHMARKS.md
115
+ - benchmarks/allocs.rb
116
+ - benchmarks/app.rb
117
+ - benchmarks/benchmarking_support.rb
118
+ - benchmarks/bm_active_model_serializers.rb
119
+ - benchmarks/bm_controller.rb
120
+ - benchmarks/bm_panko_json.rb
121
+ - benchmarks/bm_panko_object.rb
122
+ - benchmarks/profile.rb
123
+ - benchmarks/sanity.rb
124
+ - benchmarks/setup.rb
125
+ - benchmarks/type_casts/bm_active_record.rb
126
+ - benchmarks/type_casts/bm_panko.rb
127
+ - benchmarks/type_casts/bm_pg.rb
128
+ - benchmarks/type_casts/support.rb
129
+ - ext/panko_serializer/attributes_iterator.c
130
+ - ext/panko_serializer/attributes_iterator.h
131
+ - ext/panko_serializer/extconf.rb
132
+ - ext/panko_serializer/panko_serializer.c
133
+ - ext/panko_serializer/panko_serializer.h
134
+ - ext/panko_serializer/serialization_descriptor.c
135
+ - ext/panko_serializer/serialization_descriptor.h
136
+ - ext/panko_serializer/time_conversion.c
137
+ - ext/panko_serializer/time_conversion.h
138
+ - ext/panko_serializer/type_cast.c
139
+ - ext/panko_serializer/type_cast.h
140
+ - lib/panko.rb
141
+ - lib/panko/array_serializer.rb
142
+ - lib/panko/cache.rb
143
+ - lib/panko/serialization_descriptor.rb
144
+ - lib/panko/serializer.rb
145
+ - lib/panko/version.rb
146
+ - panko_serializer.gemspec
147
+ homepage: ''
148
+ licenses:
149
+ - MIT
150
+ metadata: {}
151
+ post_install_message:
152
+ rdoc_options: []
153
+ require_paths:
154
+ - lib
155
+ required_ruby_version: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ required_rubygems_version: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - ">="
163
+ - !ruby/object:Gem::Version
164
+ version: '0'
165
+ requirements: []
166
+ rubyforge_project:
167
+ rubygems_version: 2.6.12
168
+ signing_key:
169
+ specification_version: 4
170
+ summary: Fast serialization for ActiveModel
171
+ test_files: []