json_convertible 0.0.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.
- checksums.yaml +7 -0
- data/lib/json_convertible.rb +250 -0
- metadata +141 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: c3e54292e01cbe5d9ccdcfc1af48aabd142500b2e076eb8ff44fd9ccde51119c
|
4
|
+
data.tar.gz: 51f005e040de492a4bb071a8f5b0ccb72b8d4ec3463b8cdf466ce6f66d4145fb
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7156554c12f21c0aa2b0dbe427188bf0f83b82ee9e8c4b3f1f6f7a98c47152300a5588cc71e27ed3dcc4c38a19f11f92f1da441499de017a91be6347a1e96436
|
7
|
+
data.tar.gz: ce8eaf1471ad3be846ccf81183868f48a064e8d9eee99d1fa5f0b87ca26fb7e9153fed7ba8fc46c4503c17a864bbd5edb7535692cd72c084462b8dca7c03e0e5
|
@@ -0,0 +1,250 @@
|
|
1
|
+
require "json"
|
2
|
+
|
3
|
+
# Mixin for encoding/decoding JSON data classes.
|
4
|
+
module JSONConvertible
|
5
|
+
|
6
|
+
class JSONConvertibleError < StandardError
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.included(base)
|
10
|
+
base.send(:include, InstanceMethods)
|
11
|
+
base.extend(ClassMethods)
|
12
|
+
end
|
13
|
+
|
14
|
+
# add these as instance methods
|
15
|
+
module InstanceMethods
|
16
|
+
def to_json(options = {})
|
17
|
+
to_object_dictionary.to_json(options)
|
18
|
+
end
|
19
|
+
|
20
|
+
# @ignore_instance_variables: optional to provide a list of
|
21
|
+
# variables to attributes to ignore
|
22
|
+
# @example
|
23
|
+
#
|
24
|
+
# ignore_instance_variables: [:@project, :@something_else]
|
25
|
+
#
|
26
|
+
def to_object_dictionary(ignore_instance_variables: [])
|
27
|
+
object_hash = {}
|
28
|
+
instance_variables.each do |var|
|
29
|
+
next if ignore_instance_variables.include?(var)
|
30
|
+
|
31
|
+
# If we encounter with a `var` which value is an `Array`, we should iterate
|
32
|
+
# over its value and use its own `to_object_dictionary`.
|
33
|
+
if instance_variable_get(var).kind_of?(Array)
|
34
|
+
object_array = []
|
35
|
+
instance_variable_get(var).each do |obj|
|
36
|
+
# If the `Array` type does not include the JSONConvertible mixin, don't
|
37
|
+
# call `to_object_dictionary` on the elements, since the method does not exist
|
38
|
+
if obj.class.include?(JSONConvertible)
|
39
|
+
object_array << obj.to_object_dictionary
|
40
|
+
else
|
41
|
+
object_array << obj
|
42
|
+
end
|
43
|
+
end
|
44
|
+
# In this step we have all the objects, lastly we need the key of the array.
|
45
|
+
var_name, = _to_object_dictionary(var)
|
46
|
+
object_hash[var_name] = object_array
|
47
|
+
else
|
48
|
+
var_name, instance_variable_value = _to_object_dictionary(var)
|
49
|
+
object_hash[var_name] = instance_variable_value
|
50
|
+
end
|
51
|
+
end
|
52
|
+
return object_hash
|
53
|
+
end
|
54
|
+
|
55
|
+
protected
|
56
|
+
|
57
|
+
def _to_object_dictionary(var)
|
58
|
+
# For a given object variable we check if it includes some custom value mapping to JSON.
|
59
|
+
if self.class.attribute_name_to_json_proc_map.key?(var)
|
60
|
+
instance_variable_value = self.class.attribute_name_to_json_proc_map[var].call(instance_variable_get(var))
|
61
|
+
else
|
62
|
+
instance_variable_value = instance_variable_get(var)
|
63
|
+
end
|
64
|
+
# For a given object variable we check if it includes some custom property mapping to JSON
|
65
|
+
if self.class.attribute_key_name_map.key?(var)
|
66
|
+
var_name = self.class.attribute_key_name_map[var]
|
67
|
+
else
|
68
|
+
var_name = var.to_s[1..-1]
|
69
|
+
end
|
70
|
+
return var_name, instance_variable_value
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# add these as class methods
|
75
|
+
module ClassMethods
|
76
|
+
def from_json!(json_object)
|
77
|
+
instance, json_object = _initialize_using!(json_object)
|
78
|
+
json_object.each do |var, val|
|
79
|
+
# If we encounter with a value that is represented by an array, iterate over it.
|
80
|
+
if val.kind_of?(Array)
|
81
|
+
# For each of the objects in the array, we call the protected method to build the object array.
|
82
|
+
array_property = []
|
83
|
+
array_name = nil
|
84
|
+
val.each do |array_val|
|
85
|
+
array_name, this_instance = _from_json!(var, array_val, is_iterable: true)
|
86
|
+
array_property << this_instance
|
87
|
+
end
|
88
|
+
if array_name.nil?
|
89
|
+
if attribute_key_name_map.key(var)
|
90
|
+
array_name = attribute_key_name_map.key(var).to_sym
|
91
|
+
else
|
92
|
+
array_name = "@#{var}".to_sym
|
93
|
+
end
|
94
|
+
end
|
95
|
+
instance.instance_variable_set(array_name, array_property)
|
96
|
+
else
|
97
|
+
var_name, var_value = _from_json!(var, val)
|
98
|
+
instance.instance_variable_set(var_name, var_value)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
return instance
|
102
|
+
end
|
103
|
+
|
104
|
+
# class method
|
105
|
+
# This method is intended to be overridden by any
|
106
|
+
# class that implements `JSONConvertible` and need
|
107
|
+
# to use a custom mapping of the attributes to JSON keys.
|
108
|
+
#
|
109
|
+
# @example
|
110
|
+
#
|
111
|
+
# def self.attribute_key_name_map
|
112
|
+
# return { :@some_key => "some_key_in_json" }
|
113
|
+
# end
|
114
|
+
#
|
115
|
+
# @return [Hash] of mapping properties to keys in the JSON
|
116
|
+
def attribute_key_name_map
|
117
|
+
return {}
|
118
|
+
end
|
119
|
+
|
120
|
+
# class method
|
121
|
+
# This method is intended to be overridden by any
|
122
|
+
# class that implements `JSONConvertible` and need
|
123
|
+
# to encode the result of the class attributes in a
|
124
|
+
# certain format into the JSON.
|
125
|
+
#
|
126
|
+
# @example
|
127
|
+
#
|
128
|
+
# def self.attribute_name_to_json_proc_map
|
129
|
+
# timestamp_to_json_proc = proc { |timestamp|
|
130
|
+
# timestamp.strftime('%Q')
|
131
|
+
# }
|
132
|
+
# return { :@timestamp => timestamp_to_json_proc }
|
133
|
+
# end
|
134
|
+
#
|
135
|
+
# @return [Hash] of properties and procs formatting to JSON
|
136
|
+
def attribute_name_to_json_proc_map
|
137
|
+
return {}
|
138
|
+
end
|
139
|
+
|
140
|
+
# class method
|
141
|
+
# This method is intended to be overridden by any
|
142
|
+
# class that implements `JSONConvertible` and need
|
143
|
+
# to decode the JSON values back to the original types
|
144
|
+
# of the class attributes.
|
145
|
+
#
|
146
|
+
# @example
|
147
|
+
#
|
148
|
+
# def self.json_to_attribute_name_proc_map
|
149
|
+
# json_to_timestamp_proc = proc { |json|
|
150
|
+
# Time.at(json.to_i)
|
151
|
+
# }
|
152
|
+
# return { :@timestamp => json_to_timestamp_proc }
|
153
|
+
# end
|
154
|
+
#
|
155
|
+
# @return [Hash] of properties and procs formatting from JSON
|
156
|
+
def json_to_attribute_name_proc_map
|
157
|
+
return {}
|
158
|
+
end
|
159
|
+
|
160
|
+
# class method
|
161
|
+
# This method is intended to be overridden by any
|
162
|
+
# class that implements `JSONConvertible` and need
|
163
|
+
# to provide the encoder information about which types
|
164
|
+
# are each attribute of the class.
|
165
|
+
#
|
166
|
+
# @example
|
167
|
+
#
|
168
|
+
#
|
169
|
+
# def attribute_to_type_map
|
170
|
+
# return { :@string_attribute => String, :@custom_class_attribute => CustomClass }
|
171
|
+
# end
|
172
|
+
#
|
173
|
+
# @return [Hash] of properties and their types
|
174
|
+
def attribute_to_type_map
|
175
|
+
return {}
|
176
|
+
end
|
177
|
+
|
178
|
+
# class method
|
179
|
+
# This method is intended to be overridden by any
|
180
|
+
# class that implements `JSONConvertible` and need
|
181
|
+
# to provide a custom mapping for a enumerable property
|
182
|
+
# in the JSON.
|
183
|
+
#
|
184
|
+
# @param enumerable_property_name [Any] the property name of the object.
|
185
|
+
# @param current_json_object [Hash] the hash object of `enumerable_property_name` for a given iteration step.
|
186
|
+
#
|
187
|
+
# @example
|
188
|
+
#
|
189
|
+
# def map_enumerable_type(enumerable_property_name: nil, current_json_object: nil)
|
190
|
+
# if enumerable_property_name == :@job_triggers
|
191
|
+
# JobTrigger needs a factory method that reads `json[:type]` and instantiates the proper type
|
192
|
+
# return FastlaneCI::JobTrigger.create(json: current_json_object)
|
193
|
+
# end
|
194
|
+
# end
|
195
|
+
# @return [Any] object in the array by the given `property_name` and `json_object`.
|
196
|
+
def map_enumerable_type(enumerable_property_name: nil, current_json_object: nil)
|
197
|
+
return nil
|
198
|
+
end
|
199
|
+
|
200
|
+
protected
|
201
|
+
|
202
|
+
def _from_json!(var, val, is_iterable: false)
|
203
|
+
if attribute_key_name_map.key(var)
|
204
|
+
var_name = attribute_key_name_map.key(var).to_sym
|
205
|
+
else
|
206
|
+
var_name = "@#{var}".to_sym
|
207
|
+
end
|
208
|
+
|
209
|
+
if json_to_attribute_name_proc_map.key?(var_name)
|
210
|
+
var_value = json_to_attribute_name_proc_map[var_name].call(val)
|
211
|
+
else
|
212
|
+
var_value = val
|
213
|
+
end
|
214
|
+
|
215
|
+
if attribute_to_type_map.key?(var_name)
|
216
|
+
if attribute_to_type_map[var_name].include?(JSONConvertible)
|
217
|
+
# classes that include `JSONConvertible` take precedence over custom mapping.
|
218
|
+
var_value = attribute_to_type_map[var_name].from_json!(val)
|
219
|
+
else
|
220
|
+
raise TypeError, "#{var_name} does not implement `FastlaneCI::JSONConvertible`"
|
221
|
+
end
|
222
|
+
elsif is_iterable
|
223
|
+
# This is only intended for array properties, it passes the final variable name and a single object of
|
224
|
+
# the variable array. Expects to return and object.
|
225
|
+
var_value = map_enumerable_type(enumerable_property_name: var_name, current_json_object: val)
|
226
|
+
end
|
227
|
+
|
228
|
+
return var_name, var_value
|
229
|
+
end
|
230
|
+
|
231
|
+
def _initialize_using!(json_object)
|
232
|
+
instance = allocate
|
233
|
+
required_init_params = instance.method(:initialize).parameters
|
234
|
+
.select { |arg| arg[0] == :keyreq }
|
235
|
+
.map(&:last)
|
236
|
+
unless (required_init_params - json_object.keys).empty?
|
237
|
+
raise JSONConvertibleError.new, "Required initialization parameters not found in the object: #{json_object}"
|
238
|
+
end
|
239
|
+
|
240
|
+
init_params_hash = json_object.select { |key, _value| required_init_params.include?(key) }
|
241
|
+
if instance.method(:initialize).parameters.empty?
|
242
|
+
instance.send(:initialize)
|
243
|
+
else
|
244
|
+
instance.send(:initialize, init_params_hash)
|
245
|
+
end
|
246
|
+
clean_json_object = json_object.reject { |key| required_init_params.include?(key) }
|
247
|
+
return instance, clean_json_object
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
metadata
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: json_convertible
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jorge Revuelta H
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-05-14 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: json
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "<"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 3.0.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.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.5'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.5'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: coveralls
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
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: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '3.10'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '3.10'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rubocop
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 1.12.1
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 1.12.1
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rubocop-performance
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
description: A lightweight Ruby mixin for encoding/decoding JSON data classes.
|
112
|
+
email: minuscorp@gmail.com
|
113
|
+
executables: []
|
114
|
+
extensions: []
|
115
|
+
extra_rdoc_files: []
|
116
|
+
files:
|
117
|
+
- "./lib/json_convertible.rb"
|
118
|
+
homepage: https://rubygems.org/gems/json_convertible
|
119
|
+
licenses:
|
120
|
+
- Apache-2.0
|
121
|
+
metadata: {}
|
122
|
+
post_install_message:
|
123
|
+
rdoc_options: []
|
124
|
+
require_paths:
|
125
|
+
- lib
|
126
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - ">="
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: 2.4.0
|
131
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
132
|
+
requirements:
|
133
|
+
- - ">="
|
134
|
+
- !ruby/object:Gem::Version
|
135
|
+
version: '0'
|
136
|
+
requirements: []
|
137
|
+
rubygems_version: 3.0.1
|
138
|
+
signing_key:
|
139
|
+
specification_version: 4
|
140
|
+
summary: A lightweight Ruby mixin for encoding/decoding JSON data classes.
|
141
|
+
test_files: []
|