jserializer 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: []