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 +7 -0
- data/lib/jserializer/association.rb +85 -0
- data/lib/jserializer/base.rb +202 -0
- data/lib/jserializer/errors.rb +8 -0
- data/lib/jserializer.rb +3 -0
- metadata +105 -0
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
|
data/lib/jserializer.rb
ADDED
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: []
|