firm 0.9.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/.yardopts +12 -0
- data/LICENSE +21 -0
- data/README.md +209 -0
- data/lib/firm/serializable.rb +733 -0
- data/lib/firm/serialize/core.rb +43 -0
- data/lib/firm/serialize/id.rb +104 -0
- data/lib/firm/serializer/json.rb +394 -0
- data/lib/firm/serializer/xml.rb +544 -0
- data/lib/firm/serializer/yaml.rb +118 -0
- data/lib/firm/version.rb +9 -0
- data/lib/firm.rb +5 -0
- data/rakelib/yard/templates/default/fulldoc/html/css/firm.css +97 -0
- data/rakelib/yard/templates/default/fulldoc/html/setup.rb +25 -0
- data/rakelib/yard/templates/default/layout/html/setup.rb +5 -0
- data/rakelib/yard/yard/relative_markdown_links/version.rb +8 -0
- data/rakelib/yard/yard/relative_markdown_links.rb +39 -0
- data/rakelib/yard/yard-custom-templates.rb +2 -0
- data/rakelib/yard/yard-relative_markdown_links.rb +4 -0
- data/tests/serializer_tests.rb +945 -0
- data/tests/test_serialize.rb +8 -0
- data/tests/test_serialize_xml.rb +22 -0
- data/tests/test_serialize_yaml.rb +18 -0
- metadata +110 -0
@@ -0,0 +1,43 @@
|
|
1
|
+
# FIRM::Serializer - Ruby core serializer extensions
|
2
|
+
# Copyright (c) M.J.N. Corino, The Netherlands
|
3
|
+
|
4
|
+
|
5
|
+
module FIRM
|
6
|
+
module Serializable
|
7
|
+
|
8
|
+
# FIRM::Serializable is not included for the Ruby core classes as the would
|
9
|
+
# also extend these classes with the engine specific extension that we do not
|
10
|
+
# need nor want here.
|
11
|
+
# Instead we define the (slim) mixin module CoreExt to extend the non-POD core classes.
|
12
|
+
# POD classes (nil, boolean, integer, float) cannot be serialized separately but only
|
13
|
+
# as properties of complex serializables.
|
14
|
+
module CoreExt
|
15
|
+
def serialize(io = nil, pretty: false, format: FIRM::Serializable.default_format)
|
16
|
+
FIRM::Serializable[format].dump(self, io, pretty: pretty)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.included(base)
|
20
|
+
base.class_eval do
|
21
|
+
# Deserializes object from source data
|
22
|
+
# @param [IO,String] source source data (String or IO(-like object))
|
23
|
+
# @param [Symbol, String] format data format of source
|
24
|
+
# @return [Object] deserialized object
|
25
|
+
def self.deserialize(source, format: Serializable.default_format)
|
26
|
+
Serializable.deserialize(source, format: format)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
require 'set'
|
35
|
+
require 'ostruct'
|
36
|
+
|
37
|
+
[::Array, ::Hash, ::Struct, ::Range, ::Rational, ::Complex, ::Regexp, ::Set, ::OpenStruct, ::Time, ::Date, ::DateTime].each do |c|
|
38
|
+
c.include FIRM::Serializable::CoreExt
|
39
|
+
end
|
40
|
+
|
41
|
+
if ::Object.const_defined?(:BigDecimal)
|
42
|
+
::BigDecimal.include FIRM::Serializable::CoreExt
|
43
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# FIRM::Serializer - FIRM serializable ID class
|
2
|
+
# Copyright (c) M.J.N. Corino, The Netherlands
|
3
|
+
|
4
|
+
|
5
|
+
module FIRM
|
6
|
+
|
7
|
+
module Serializable
|
8
|
+
|
9
|
+
class ID
|
10
|
+
|
11
|
+
include FIRM::Serializable
|
12
|
+
|
13
|
+
class << self
|
14
|
+
|
15
|
+
# Deserializes object from source data
|
16
|
+
# @param [IO,String] source source data (String or IO(-like object))
|
17
|
+
# @param [Symbol, String] format data format of source
|
18
|
+
# @return [Object] deserialized object
|
19
|
+
def deserialize(source, format: Serializable.default_format)
|
20
|
+
Serializable.deserialize(source, format: format)
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
# Serialize this object
|
26
|
+
# @overload serialize(pretty: false, format: Serializable.default_format)
|
27
|
+
# @param [Boolean] pretty if true specifies to generate pretty formatted output if possible
|
28
|
+
# @param [Symbol,String] format specifies output format
|
29
|
+
# @return [String] serialized data
|
30
|
+
# @overload serialize(io, pretty: false, format: Serializable.default_format)
|
31
|
+
# @param [IO] io output stream to write serialized data to
|
32
|
+
# @param [Boolean] pretty if true specifies to generate pretty formatted output if possible
|
33
|
+
# @param [Symbol,String] format specifies output format
|
34
|
+
# @return [IO]
|
35
|
+
def serialize(io = nil, pretty: false, format: Serializable.default_format)
|
36
|
+
Serializable[format].dump(self, io, pretty: pretty)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Initializes a newly allocated instance for subsequent deserialization (optionally initializing
|
40
|
+
# using the given data hash).
|
41
|
+
# The default implementation calls the standard #initialize method without arguments (default constructor)
|
42
|
+
# and leaves the property restoration to a subsequent call to the instance method #from_serialized(data).
|
43
|
+
# Classes that do not support a default constructor can override this class method and
|
44
|
+
# implement a custom initialization scheme.
|
45
|
+
# @param [Object] _data hash-like object containing deserialized property data (symbol keys)
|
46
|
+
# @return [Object] the initialized object
|
47
|
+
def init_from_serialized(_data)
|
48
|
+
initialize
|
49
|
+
self
|
50
|
+
end
|
51
|
+
protected :init_from_serialized
|
52
|
+
|
53
|
+
# Noop for ID instances.
|
54
|
+
# @param [Object] hash hash-like property serialization container
|
55
|
+
# @param [Set] _excludes ignored
|
56
|
+
# @return [Object] property hash-like serialization container
|
57
|
+
def for_serialize(hash, _excludes = nil)
|
58
|
+
hash
|
59
|
+
end
|
60
|
+
|
61
|
+
protected :for_serialize
|
62
|
+
|
63
|
+
# Noop for ID instances.
|
64
|
+
# @param [Hash] _hash ignored
|
65
|
+
# @return [self]
|
66
|
+
def from_serialized(_hash)
|
67
|
+
# no deserializing necessary
|
68
|
+
self
|
69
|
+
end
|
70
|
+
|
71
|
+
protected :from_serialized
|
72
|
+
|
73
|
+
# Noop for ID instances.
|
74
|
+
# @return [self]
|
75
|
+
def finalize_from_serialized
|
76
|
+
# no finalization necessary
|
77
|
+
self
|
78
|
+
end
|
79
|
+
|
80
|
+
protected :finalize_from_serialized
|
81
|
+
|
82
|
+
# Always returns false for IDs.
|
83
|
+
# @return [Boolean]
|
84
|
+
def serialize_disabled?
|
85
|
+
false
|
86
|
+
end
|
87
|
+
|
88
|
+
def to_s
|
89
|
+
"FIRM::Serializable::ID<#{object_id}>"
|
90
|
+
end
|
91
|
+
|
92
|
+
def inspect
|
93
|
+
to_s
|
94
|
+
end
|
95
|
+
|
96
|
+
def to_i
|
97
|
+
object_id
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
@@ -0,0 +1,394 @@
|
|
1
|
+
# FIRM::Serializer - shape serializer module
|
2
|
+
# Copyright (c) M.J.N. Corino, The Netherlands
|
3
|
+
|
4
|
+
|
5
|
+
require 'json'
|
6
|
+
require 'json/add/date'
|
7
|
+
require 'json/add/date_time'
|
8
|
+
require 'json/add/range'
|
9
|
+
require 'json/add/regexp'
|
10
|
+
require 'json/add/struct'
|
11
|
+
require 'json/add/symbol'
|
12
|
+
require 'json/add/time'
|
13
|
+
require 'json/add/bigdecimal' if ::Object.const_defined?(:BigDecimal)
|
14
|
+
require 'json/add/rational'
|
15
|
+
require 'json/add/complex'
|
16
|
+
require 'json/add/set'
|
17
|
+
require 'json/add/ostruct'
|
18
|
+
|
19
|
+
module FIRM
|
20
|
+
|
21
|
+
module Serializable
|
22
|
+
|
23
|
+
module JSON
|
24
|
+
|
25
|
+
# Derived Hash class to use for deserialized JSON object data which
|
26
|
+
# supports using Symbol keys.
|
27
|
+
class ObjectHash < ::Hash
|
28
|
+
# Returns the object associated with given key.
|
29
|
+
# @param [String,Symbol] key key value
|
30
|
+
# @return [Object] associated object
|
31
|
+
# @see ::Hash#[]
|
32
|
+
def [](key)
|
33
|
+
super(key.to_s)
|
34
|
+
end
|
35
|
+
# Returns true if the given key exists in self otherwise false.
|
36
|
+
# @param [String,Symbol] key key value
|
37
|
+
# @return [Boolean]
|
38
|
+
# @see ::Hash#include?
|
39
|
+
def include?(key)
|
40
|
+
super(key.to_s)
|
41
|
+
end
|
42
|
+
alias member? include?
|
43
|
+
alias has_key? include?
|
44
|
+
alias key? include?
|
45
|
+
end
|
46
|
+
|
47
|
+
# Mixin module to patch singleton_clas of the Hash class to make Hash-es
|
48
|
+
# JSON creatable (#json_creatable? returns true).
|
49
|
+
module HashClassPatch
|
50
|
+
# Create a new Hash instance from deserialized JSON data.
|
51
|
+
# @param [Hash] object deserialized JSON object
|
52
|
+
# @return [Hash] restored Hash instance
|
53
|
+
def json_create(object)
|
54
|
+
object['data'].to_h
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class << self
|
59
|
+
def serializables
|
60
|
+
set = ::Set.new( [::NilClass, ::TrueClass, ::FalseClass, ::Integer, ::Float, ::String, ::Array, ::Hash,
|
61
|
+
::Date, ::DateTime, ::Range, ::Rational, ::Complex, ::Regexp, ::Struct, ::Symbol, ::Time, ::Set, ::OpenStruct])
|
62
|
+
set << ::BigDecimal if ::Object.const_defined?(:BigDecimal)
|
63
|
+
set
|
64
|
+
end
|
65
|
+
|
66
|
+
TLS_SAFE_DESERIALIZE_KEY = :firm_json_safe_deserialize.freeze
|
67
|
+
private_constant :TLS_SAFE_DESERIALIZE_KEY
|
68
|
+
|
69
|
+
TLS_PARSE_STACK_KEY = :firm_json_parse_stack.freeze
|
70
|
+
private_constant :TLS_PARSE_STACK_KEY
|
71
|
+
|
72
|
+
def safe_deserialize
|
73
|
+
::Thread.current[TLS_SAFE_DESERIALIZE_KEY] ||= []
|
74
|
+
end
|
75
|
+
private :safe_deserialize
|
76
|
+
|
77
|
+
def start_safe_deserialize
|
78
|
+
safe_deserialize.push(true)
|
79
|
+
end
|
80
|
+
|
81
|
+
def end_safe_deserialize
|
82
|
+
safe_deserialize.pop
|
83
|
+
end
|
84
|
+
|
85
|
+
def parse_stack
|
86
|
+
::Thread.current[TLS_PARSE_STACK_KEY] ||= []
|
87
|
+
end
|
88
|
+
private :parse_stack
|
89
|
+
|
90
|
+
def start_parse
|
91
|
+
parse_stack.push(safe_deserialize.pop)
|
92
|
+
end
|
93
|
+
|
94
|
+
def end_parse
|
95
|
+
unless (val = parse_stack.pop).nil?
|
96
|
+
safe_deserialize.push(val)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def safe_parsing?
|
101
|
+
!!parse_stack.last
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.dump(obj, io=nil, pretty: false)
|
106
|
+
# obj.extend(HashInstancePatch) if obj.is_a?(::Hash)
|
107
|
+
begin
|
108
|
+
# initialize anchor registry
|
109
|
+
Serializable::Aliasing.start_anchor_object_registry
|
110
|
+
for_json = obj.respond_to?(:as_json) ? obj.as_json : obj
|
111
|
+
if pretty
|
112
|
+
if io || io.respond_to?(:write)
|
113
|
+
io.write(::JSON.pretty_generate(for_json))
|
114
|
+
io
|
115
|
+
else
|
116
|
+
::JSON.pretty_generate(for_json)
|
117
|
+
end
|
118
|
+
else
|
119
|
+
::JSON.dump(for_json, io)
|
120
|
+
end
|
121
|
+
ensure
|
122
|
+
# reset anchor registry
|
123
|
+
Serializable::Aliasing.clear_anchor_object_registry
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def self.load(source)
|
128
|
+
begin
|
129
|
+
# initialize alias anchor restoration map
|
130
|
+
Serializable::Aliasing.start_anchor_references
|
131
|
+
# enable safe deserializing
|
132
|
+
self.start_safe_deserialize
|
133
|
+
::JSON.parse!(source,
|
134
|
+
**{create_additions: true,
|
135
|
+
object_class: Serializable::JSON::ObjectHash})
|
136
|
+
ensure
|
137
|
+
# reset safe deserializing
|
138
|
+
self.end_safe_deserialize
|
139
|
+
# reset alias anchor restoration map
|
140
|
+
Serializable::Aliasing.clear_anchor_references
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
|
145
|
+
# extend serialization class methods
|
146
|
+
module SerializeClassMethods
|
147
|
+
|
148
|
+
def json_create(object)
|
149
|
+
data = object['data']
|
150
|
+
# deserializing (anchor) object or alias
|
151
|
+
if data.has_key?('*id')
|
152
|
+
if Serializable::Aliasing.restored?(self, data['*id'])
|
153
|
+
# resolving an already restored anchor for this alias
|
154
|
+
Serializable::Aliasing.resolve_anchor(self, data['*id'])
|
155
|
+
else
|
156
|
+
# in case of cyclic references JSON will restore aliases before the anchors
|
157
|
+
# so in this case we allocate an instance here and register it as
|
158
|
+
# the anchor; when the anchor is restored it will re-use this instance to initialize & restore
|
159
|
+
# the properties
|
160
|
+
Serializable::Aliasing.restore_anchor(data['*id'], self.allocate)
|
161
|
+
end
|
162
|
+
else
|
163
|
+
instance = if data.has_key?('&id')
|
164
|
+
anchor_id = data.delete('&id') # extract anchor id
|
165
|
+
if Serializable::Aliasing.restored?(self, anchor_id)
|
166
|
+
# in case of cyclic references an alias will already have restored the anchor instance
|
167
|
+
# (default constructed); retrieve that instance here for deserialization of properties
|
168
|
+
Serializable::Aliasing.resolve_anchor(self, anchor_id)
|
169
|
+
else
|
170
|
+
# restore the anchor here with a newly allocated instance
|
171
|
+
Serializable::Aliasing.restore_anchor(anchor_id, self.allocate)
|
172
|
+
end
|
173
|
+
else
|
174
|
+
self.allocate
|
175
|
+
end
|
176
|
+
instance.__send__(:init_from_serialized, data)
|
177
|
+
.__send__(:from_serialized, data)
|
178
|
+
.__send__(:finalize_from_serialized)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
end
|
183
|
+
|
184
|
+
# extend instance serialization methods
|
185
|
+
module SerializeInstanceMethods
|
186
|
+
|
187
|
+
def as_json(*)
|
188
|
+
json_data = {
|
189
|
+
::JSON.create_id => self.class.name
|
190
|
+
}
|
191
|
+
if (anchor = Serializable::Aliasing.get_anchor(self))
|
192
|
+
anchor_data = Serializable::Aliasing.get_anchor_data(self)
|
193
|
+
# retroactively insert the anchor in the anchored instance's serialization data
|
194
|
+
anchor_data['&id'] = anchor unless anchor_data.has_key?('&id')
|
195
|
+
json_data["data"] = {
|
196
|
+
'*id' => anchor
|
197
|
+
}
|
198
|
+
else
|
199
|
+
# register anchor object **before** serializing properties to properly handle cycling (bidirectional
|
200
|
+
# references)
|
201
|
+
json_data['data'] = for_serialize(Serializable::Aliasing.register_anchor_object(self, {}))
|
202
|
+
json_data['data'].transform_values! { |v| v.respond_to?(:as_json) ? v.as_json : v }
|
203
|
+
end
|
204
|
+
json_data
|
205
|
+
end
|
206
|
+
|
207
|
+
end
|
208
|
+
|
209
|
+
end
|
210
|
+
|
211
|
+
module Aliasing
|
212
|
+
class << self
|
213
|
+
include Serializable::AliasManagement
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
# extend serialization class methods
|
218
|
+
module SerializeClassMethods
|
219
|
+
|
220
|
+
include JSON::SerializeClassMethods
|
221
|
+
|
222
|
+
end
|
223
|
+
|
224
|
+
# extend instance serialization methods
|
225
|
+
module SerializeInstanceMethods
|
226
|
+
|
227
|
+
include JSON::SerializeInstanceMethods
|
228
|
+
|
229
|
+
end
|
230
|
+
|
231
|
+
class ID
|
232
|
+
include JSON::SerializeInstanceMethods
|
233
|
+
class << self
|
234
|
+
include JSON::SerializeClassMethods
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
register(:json, JSON)
|
239
|
+
|
240
|
+
end
|
241
|
+
|
242
|
+
end
|
243
|
+
|
244
|
+
module ::JSON
|
245
|
+
class << self
|
246
|
+
|
247
|
+
alias :pre_firm_parse! :parse!
|
248
|
+
def parse!(*args, **kwargs)
|
249
|
+
begin
|
250
|
+
# setup parsing stack for safe or normal deserializing
|
251
|
+
# the double bracketing provided from FIRM::Serializable::JSON#load and here
|
252
|
+
# makes sure to support both nested Wx::SF deserializing as well as nested
|
253
|
+
# hybrid deserializing (Wx::SF -> common JSON -> ...)
|
254
|
+
FIRM::Serializable::JSON.start_parse
|
255
|
+
pre_firm_parse!(*args, **kwargs)
|
256
|
+
ensure
|
257
|
+
# reset parsing stack
|
258
|
+
FIRM::Serializable::JSON.end_parse
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
class ::Class
|
266
|
+
|
267
|
+
# override this to be able to do safe deserializing
|
268
|
+
def json_creatable?
|
269
|
+
if FIRM::Serializable::JSON.safe_parsing?
|
270
|
+
return false unless FIRM::Serializable::JSON.serializables.include?(self) ||
|
271
|
+
FIRM::Serializable.serializables.include?(self) ||
|
272
|
+
::Struct > self
|
273
|
+
end
|
274
|
+
respond_to?(:json_create)
|
275
|
+
end
|
276
|
+
|
277
|
+
end
|
278
|
+
|
279
|
+
class ::Array
|
280
|
+
def as_json(*)
|
281
|
+
collect { |e| e.respond_to?(:as_json) ? e.as_json : e }
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
class ::Hash
|
286
|
+
class << self
|
287
|
+
include FIRM::Serializable::JSON::HashClassPatch
|
288
|
+
end
|
289
|
+
def as_json(*)
|
290
|
+
{
|
291
|
+
::JSON.create_id => self.class.name,
|
292
|
+
'data' => collect { |k,v| [k.respond_to?(:as_json) ? k.as_json : k, v.respond_to?(:as_json) ? v.as_json : v] }
|
293
|
+
}
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
class ::Set
|
298
|
+
def as_json(*)
|
299
|
+
{
|
300
|
+
JSON.create_id => self.class.name,
|
301
|
+
'a' => to_a.as_json,
|
302
|
+
}
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
class ::Struct
|
307
|
+
class << self
|
308
|
+
def json_create(object)
|
309
|
+
# deserializing (anchor) object or alias
|
310
|
+
if object.has_key?('*id')
|
311
|
+
if FIRM::Serializable::Aliasing.restored?(self, object['*id'])
|
312
|
+
# resolving an already restored anchor for this alias
|
313
|
+
FIRM::Serializable::Aliasing.resolve_anchor(self, object['*id'])
|
314
|
+
else
|
315
|
+
# in case of cyclic references JSON will restore aliases before the anchors
|
316
|
+
# so in this case we allocate an instance here and register it as
|
317
|
+
# the anchor; when the anchor is restored it will re-use this instance to restore
|
318
|
+
# the properties
|
319
|
+
FIRM::Serializable::Aliasing.restore_anchor(object['*id'], self.allocate)
|
320
|
+
end
|
321
|
+
else
|
322
|
+
if object.has_key?('&id')
|
323
|
+
anchor_id = object['&id'] # extract anchor id
|
324
|
+
instance = if FIRM::Serializable::Aliasing.restored?(self, anchor_id)
|
325
|
+
# in case of cyclic references an alias will already have restored the anchor instance
|
326
|
+
# (default constructed); retrieve that instance here for deserialization of properties
|
327
|
+
FIRM::Serializable::Aliasing.resolve_anchor(self, anchor_id)
|
328
|
+
else
|
329
|
+
# restore the anchor here with a newly instantiated instance
|
330
|
+
FIRM::Serializable::Aliasing.restore_anchor(anchor_id, self.allocate)
|
331
|
+
end
|
332
|
+
instance.__send__(:initialize, *object['v'])
|
333
|
+
instance
|
334
|
+
else
|
335
|
+
self.new(*object['v'])
|
336
|
+
end
|
337
|
+
end
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
def as_json(*)
|
342
|
+
klass = self.class.name
|
343
|
+
klass.to_s.empty? and raise JSON::JSONError, "Only named structs are supported!"
|
344
|
+
# {
|
345
|
+
# JSON.create_id => klass,
|
346
|
+
# 'v' => values.as_json,
|
347
|
+
# }
|
348
|
+
json_data = {
|
349
|
+
::JSON.create_id => klass
|
350
|
+
}
|
351
|
+
if (anchor = FIRM::Serializable::Aliasing.get_anchor(self))
|
352
|
+
anchor_data = FIRM::Serializable::Aliasing.get_anchor_data(self)
|
353
|
+
# retroactively insert the anchor in the anchored instance's serialization data
|
354
|
+
anchor_data['&id'] = anchor unless anchor_data.has_key?('&id')
|
355
|
+
json_data['*id'] = anchor
|
356
|
+
else
|
357
|
+
# register anchor object **before** serializing properties to properly handle cycling (bidirectional
|
358
|
+
# references)
|
359
|
+
FIRM::Serializable::Aliasing.register_anchor_object(self, json_data)
|
360
|
+
json_data['v'] = values.as_json
|
361
|
+
end
|
362
|
+
json_data
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
class ::OpenStruct
|
367
|
+
def as_json(*)
|
368
|
+
klass = self.class.name
|
369
|
+
klass.to_s.empty? and raise JSON::JSONError, "Only named structs are supported!"
|
370
|
+
{
|
371
|
+
JSON.create_id => klass,
|
372
|
+
't' => table.as_json,
|
373
|
+
}
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
# fix flawed JSON serializing
|
378
|
+
class ::DateTime
|
379
|
+
|
380
|
+
def as_json(*)
|
381
|
+
{
|
382
|
+
JSON.create_id => self.class.name,
|
383
|
+
'y' => year,
|
384
|
+
'm' => month,
|
385
|
+
'd' => day,
|
386
|
+
'H' => hour,
|
387
|
+
'M' => min,
|
388
|
+
'S' => sec_fraction.to_f+sec,
|
389
|
+
'of' => offset.to_s,
|
390
|
+
'sg' => start,
|
391
|
+
}
|
392
|
+
end
|
393
|
+
|
394
|
+
end
|