jserializer 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
+ SHA1:
3
+ metadata.gz: eff59fc3af48019c3a9800760d5d09c58a934c78
4
+ data.tar.gz: 308fc34cd019fd9c29a0e5b06dc9068ee19c431c
5
+ SHA512:
6
+ metadata.gz: 87093b362a7810c4a65475790f91e80c55ea450c9c459c053e7bd0582eb402b46583d5332e5d47d62444fd7b71ea189ba84ba0d0deb73e82a2f2f21697c7a53b
7
+ data.tar.gz: be66e3b62de854ceb486e329f8678a2ea767b54352452911d39eccffbaeeb650003a1e4dea9f9542c98206a3bd571b02dc1073d7332a95c87c596a2240321f2b
@@ -0,0 +1,85 @@
1
+ module Jserializer
2
+ class Association
3
+ attr_reader :relation_type, :serializer, :id_only
4
+
5
+ def initialize(name, relation_type, key:, serializer:, id_only:, embed_key:)
6
+ @attribute_name = name.to_s
7
+ @relation_type = relation_type
8
+ @key = key
9
+ @serializer = serializer
10
+ @id_only = id_only || false
11
+ @embed_key = embed_key || :id
12
+ end
13
+
14
+ def key
15
+ return @key if @key || !@id_only
16
+ case @relation_type
17
+ when :has_many
18
+ :"#{singularize_attribute_name}_ids"
19
+ when :has_one
20
+ :"#{@attribute_name}_id"
21
+ end
22
+ end
23
+
24
+ # The method name to access the data of the attribute
25
+ def access_name
26
+ return @attribute_name unless @id_only
27
+ # for simplicity without guessing
28
+ # the access method is post_ids for posts if associated with has_many
29
+ # and post.id for post if associated with has_one
30
+ # This also means for serializing a Hash object with has_one association
31
+ # It must provide access key like "post.id" to get data
32
+ case @relation_type
33
+ when :has_many
34
+ "#{singularize_attribute_name}_#{@embed_key}s"
35
+ when :has_one
36
+ "#{@attribute_name}.#{@embed_key}"
37
+ else
38
+ @attribute_name
39
+ end
40
+ end
41
+
42
+ def serialize(record)
43
+ return nil if record.nil?
44
+ return [] if relation_type == :has_many && record.empty?
45
+ return serialize_collection(record) if relation_type == :has_many
46
+ return serialize_one(record) if relation_type == :has_one
47
+ raise "Unable to serialize association type: #{relation_type}"
48
+ end
49
+
50
+ private
51
+
52
+ def serialize_collection(records)
53
+ klass = find_serializer_class(records.first)
54
+ return klass.new(records).serializable_collection if klass
55
+
56
+ unless records.first.respond_to?(:as_json)
57
+ raise "Unable to find serializer for #{records.first.class.name}"
58
+ end
59
+
60
+ # loop through collection and serialize with as_json
61
+ records.map { |record| record.as_json(root: false) }
62
+ end
63
+
64
+ def serialize_one(record)
65
+ klass = find_serializer_class(record)
66
+ return klass.new(record).serializable_hash if klass
67
+
68
+ unless record.respond_to?(:as_json)
69
+ raise "Unable to find serializer for #{record.class.name}"
70
+ end
71
+ record.as_json(root: false)
72
+ end
73
+
74
+ def find_serializer_class(model)
75
+ return serializer if serializer
76
+ return nil unless model.respond_to?(:active_model_serializer)
77
+ model.active_model_serializer
78
+ end
79
+
80
+ def singularize_attribute_name
81
+ return @attribute_name unless @attribute_name.end_with?('s')
82
+ @attribute_name[0...-1]
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,202 @@
1
+ module Jserializer
2
+ class Base
3
+ @_attributes = {}
4
+ @_embed = :objects
5
+
6
+ class << self
7
+ attr_accessor :_attributes, :_root_key, :_embed
8
+
9
+ def attributes(*names)
10
+ names.each { |name| _add_attribute(name, nil) }
11
+ end
12
+
13
+ def attribute(name, key: nil)
14
+ _add_attribute(name, key)
15
+ end
16
+
17
+ def root(name)
18
+ self._root_key = name
19
+ end
20
+
21
+ # Define how associations should be embedded.
22
+ #
23
+ # embed :objects # Embed associations as full objects
24
+ # embed :ids # Embed only the association ids
25
+ #
26
+ def embed(type = :objects)
27
+ self._embed = type
28
+ end
29
+
30
+ # embed: :objects || :ids
31
+ # the embed_key only works when embed: ids
32
+ def has_many(name, serializer: nil, key: nil, embed: nil, embed_key: nil)
33
+ association = _build_association(
34
+ name, :has_many, key, serializer, embed, embed_key
35
+ )
36
+ _add_attribute(name, key, association: association)
37
+ end
38
+
39
+ def has_one(name, serializer: nil, key: nil, embed: nil, embed_key: nil)
40
+ association = _build_association(
41
+ name, :has_one, key, serializer, embed, embed_key
42
+ )
43
+ _add_attribute(name, key, association: association)
44
+ end
45
+
46
+ def _build_association(name, type, key, serializer, embed, embed_key)
47
+ id_only = embed == :ids || (embed.nil? && self._embed == :ids)
48
+ Association.new(
49
+ name, type,
50
+ key: key, serializer: serializer,
51
+ id_only: id_only, embed_key: embed_key
52
+ )
53
+ end
54
+
55
+ def _add_attribute(name, key, association: nil)
56
+ self._attributes[name] = {
57
+ key: key,
58
+ include_method: "include_#{name}?".to_sym
59
+ }
60
+ if association
61
+ self._attributes[name][:association] = association
62
+ self._attributes[name][:key] = association.key
63
+ end
64
+ access_name = association ? association.access_name : name
65
+ generate_attribute_methods(name, access_name)
66
+ end
67
+
68
+ # Generate attribute access and inclusion check methods
69
+ # This improves performance by avoiding method lookups like:
70
+ # public_send(name) if respond_to?(name)
71
+ def generate_attribute_methods(name, access_name)
72
+ class_eval <<-METHOD, __FILE__, __LINE__ + 1
73
+ def #{name}
74
+ if ::Hash === @object
75
+ @object.fetch(#{access_name})
76
+ else
77
+ @object.#{access_name}
78
+ end
79
+ end
80
+
81
+ def include_#{name}?
82
+ true
83
+ end
84
+ METHOD
85
+ end
86
+
87
+ def inherited(subclass)
88
+ super(subclass)
89
+ subclass._attributes = _attributes.dup
90
+ end
91
+ end
92
+
93
+ attr_reader :object, :options, :current_user
94
+
95
+ # supported options:
96
+ # root:
97
+ # meta:
98
+ # meta_key:
99
+ # current_user:
100
+ # is_collection:
101
+ # only: []
102
+ # except: []
103
+ def initialize(object, options = {})
104
+ @object = object
105
+ @current_user = options[:current_user]
106
+ @is_collection = options.delete(:is_collection) || false
107
+ @options = options
108
+ _update_attributes_filter
109
+ end
110
+
111
+ # reset object to reuse the serializer instance
112
+ # clear any cached or memoized things
113
+ def reset(object)
114
+ @object = object
115
+ end
116
+
117
+ # Returns a hash representation without the root
118
+ def serializable_hash
119
+ return serializable_collection if collection?
120
+ self.class._attributes.each_with_object({}) do |(name, option), hash|
121
+ if _include_from_options?(name) && public_send(option[:include_method])
122
+ hash[option[:key] || name] = _set_value(name, option)
123
+ end
124
+ end
125
+ end
126
+
127
+ def serializable_collection
128
+ serializer_object = self.class.new(nil, @options)
129
+ @object.map do |record|
130
+ serializer_object.reset(record)
131
+ serializer_object.serializable_hash
132
+ end
133
+ end
134
+
135
+ def collection?
136
+ @is_collection
137
+ end
138
+
139
+ def root_name
140
+ return nil if @options[:root] == false
141
+ @options[:root] || self.class._root_key
142
+ end
143
+
144
+ def meta_key
145
+ @options[:meta_key] || :meta
146
+ end
147
+
148
+ def to_json(*)
149
+ ::Oj.dump(as_json, mode: :compat)
150
+ end
151
+
152
+ # Returns a hash representation with the root
153
+ # Available options:
154
+ # :root => true or false
155
+ def as_json(options = {})
156
+ root = options.key?(:root) ? options[:root] : true
157
+ hash = if root && root_name
158
+ { root_name => serializable_hash }
159
+ else
160
+ serializable_hash
161
+ end
162
+ hash[meta_key] = @options[:meta] if @options.key?(:meta)
163
+ hash
164
+ end
165
+
166
+ private
167
+
168
+ def _include_from_options?(name)
169
+ return true unless @attributes_filter_list
170
+ return @filter_include_return if @attributes_filter_list.include?(name)
171
+ !@filter_include_return
172
+ end
173
+
174
+ def _set_value(name, option)
175
+ if option.key?(:association)
176
+ return _build_from_association(name, option[:association])
177
+ end
178
+ public_send(name)
179
+ end
180
+
181
+ def _build_from_association(name, association)
182
+ resource = public_send(name)
183
+ return resource if association.id_only
184
+ association.serialize(resource)
185
+ end
186
+
187
+ # either only or except can be used to filter attributes
188
+ def _update_attributes_filter
189
+ if @options.key?(:only) && @options[:only].is_a?(Array)
190
+ @attributes_filter_list = @options[:only]
191
+ # if the attribute is included in :only, we will serialize it
192
+ @filter_include_return = true
193
+ elsif @options.key?(:except) && @options[:except].is_a?(Array)
194
+ @attributes_filter_list = @options[:except]
195
+ # if the attribute is included in :except, we will not serialize it
196
+ @filter_include_return = false
197
+ else
198
+ @attributes_filter_list = nil
199
+ end
200
+ end
201
+ end
202
+ end
@@ -0,0 +1,8 @@
1
+ module Jserializer
2
+ class NoAttributeError < ::StandardError
3
+ def self.build(klass, name, object)
4
+ message = "undefined attribute `#{name}' for #{object} in #{klass.name}"
5
+ new(message)
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,3 @@
1
+ require 'oj'
2
+ require 'jserializer/association'
3
+ require 'jserializer/base'
metadata ADDED
@@ -0,0 +1,105 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jserializer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Steven Yue
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-09-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: oj
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '3.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '3.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.16'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.16'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: minitest
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '5.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '5.0'
69
+ description: A simple JSON serializer used as a drop-in replacement of Active Model
70
+ Serializer
71
+ email: ''
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - lib/jserializer.rb
77
+ - lib/jserializer/association.rb
78
+ - lib/jserializer/base.rb
79
+ - lib/jserializer/errors.rb
80
+ homepage: http://www.github.com/distil/jserializer
81
+ licenses:
82
+ - MIT
83
+ metadata:
84
+ allowed_push_host: https://rubygems.org
85
+ post_install_message:
86
+ rdoc_options: []
87
+ require_paths:
88
+ - lib
89
+ required_ruby_version: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ version: 2.0.0
94
+ required_rubygems_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ requirements: []
100
+ rubyforge_project:
101
+ rubygems_version: 2.6.13
102
+ signing_key:
103
+ specification_version: 4
104
+ summary: A JSON Serializer for Ruby Objects
105
+ test_files: []