yasl 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/CHANGELOG.md +23 -0
- data/LICENSE.txt +20 -0
- data/README.md +333 -0
- data/VERSION +1 -0
- data/lib/yasl.rb +59 -0
- data/lib/yasl/dumper.rb +193 -0
- data/lib/yasl/loader.rb +156 -0
- data/yasl.gemspec +59 -0
- metadata +153 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: ede09b21493ce4855f31b11dace169aeb8a218d2f524c86758ca5643fcfe29e5
|
4
|
+
data.tar.gz: 9c34b64eea2244ba1673a2d643872832d694f07c6b644a413824607c9be6469b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6afd16380e645f3b972508747b3e41c1254058e25f2f55ab7a872140b4f06aa566a165687c162777c6dcae470d4a1b91aea25a5e82d321ae720644a4b927b353
|
7
|
+
data.tar.gz: f976082dea29c4b130bb6394cc7f1d7a6d71bbef708b9312d61bad265a62e6e6519c12159a6667dcf50618475fdd5474542d959cb415e0984e7ed14355d4946e
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# Change Log
|
2
|
+
|
3
|
+
## 0.1.0
|
4
|
+
|
5
|
+
- Serialize JSON basic data types
|
6
|
+
- Serialize Ruby basic data types
|
7
|
+
- Serialize instance variables as JSON
|
8
|
+
- Serialize class variables as JSON
|
9
|
+
- Serialize struct member values as JSON
|
10
|
+
- Serialize top-level class/module as JSON
|
11
|
+
- Serialize cycles by using object ID references
|
12
|
+
- Support `include_classes` option on dump
|
13
|
+
- Silently ignore non-serializable objects like `Proc`, `Binding`, and `IO`.
|
14
|
+
- Deserialize instance variables from JSON
|
15
|
+
- Deserialize Class occurence in variables from JSON
|
16
|
+
- Deserialize Module occurence in variables from JSON
|
17
|
+
- Deserialize class variables from JSON
|
18
|
+
- Deserialize Struct members from JSON
|
19
|
+
- Deserialize cycles with object ID references
|
20
|
+
- Deserialize top-level class/module from JSON
|
21
|
+
- Support `include_classes` option on load
|
22
|
+
- Raise error for deserialization not finding a class mentioned in the data
|
23
|
+
- Require passing `whitelist_classes` to `YASL#load` or else raise error for illegal classes
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2020 Andy Maleh
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,333 @@
|
|
1
|
+
# YASL - Yet Another Serialization Library
|
2
|
+
[](http://badge.fury.io/rb/yasl)
|
3
|
+
[](https://github.com/AndyObtiva/yasl/actions?query=workflow%3ARuby)
|
4
|
+
[](https://coveralls.io/github/AndyObtiva/yasl?branch=master)
|
5
|
+
[](https://codeclimate.com/github/AndyObtiva/yasl/maintainability)
|
6
|
+
|
7
|
+
A pure Ruby serialization library that works across different Ruby implementations like Opal and JRuby as an alternative to YAML/Marshal.
|
8
|
+
|
9
|
+
## Requirements
|
10
|
+
|
11
|
+
- Portablity across different Ruby implementations, especially Opal and JRuby.
|
12
|
+
- Zero configuration. Developers are too busy solving business domain problems to worry about low-level serialization details.
|
13
|
+
- Silently ignore non-serializable objects like `Proc`, `Binding`, and `IO`.
|
14
|
+
- Support serializing classes and modules, not just object instances.
|
15
|
+
- JSON encoding is good enough. No need for premature optimization.
|
16
|
+
|
17
|
+
## Usage Instructions
|
18
|
+
|
19
|
+
Run:
|
20
|
+
|
21
|
+
`gem install yasl`
|
22
|
+
|
23
|
+
Or add to Gemfile:
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
gem 'yasl', '~> 0.1.0'
|
27
|
+
```
|
28
|
+
|
29
|
+
And, run:
|
30
|
+
|
31
|
+
`bundle`
|
32
|
+
|
33
|
+
Finally, require in Ruby code:
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
require 'yasl'
|
37
|
+
```
|
38
|
+
|
39
|
+
### Serialize
|
40
|
+
|
41
|
+
To serialize, use the `YASL#dump(object)` method.
|
42
|
+
|
43
|
+
Keep in mind that `YASL::UNSERIALIZABLE_DATA_TYPES` classes are unserializable, and will serialize as `nil` (feel free to add more classes that you would like filtered out):
|
44
|
+
|
45
|
+
`Proc`, `Binding`, `IO`, `File::Stat`, `Dir`, `BasicSocket`, `MatchData`, `Method`, `UnboundMethod`, `Thread`, `ThreadGroup`, `Continuation`
|
46
|
+
|
47
|
+
Example (from [samples/dump_basic.rb](samples/dump_basic.rb)):
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
require 'yasl'
|
51
|
+
require 'date'
|
52
|
+
|
53
|
+
class Car
|
54
|
+
attr_accessor :make,
|
55
|
+
:model,
|
56
|
+
:year,
|
57
|
+
:registration_time,
|
58
|
+
:registration_date,
|
59
|
+
:registration_date_time,
|
60
|
+
:complex_number,
|
61
|
+
:complex_polar_number,
|
62
|
+
:rational_number
|
63
|
+
end
|
64
|
+
|
65
|
+
car = Car.new
|
66
|
+
car.make = 'Mitsubishi'
|
67
|
+
car.model = 'Eclipse'
|
68
|
+
car.year = '2002'
|
69
|
+
car.registration_time = Time.new(2003, 10, 19, 10, 39, 37.092, '-03:00')
|
70
|
+
car.registration_date = Date.new(2003, 10, 19)
|
71
|
+
car.registration_date_time = DateTime.new(2003, 10, 19, 10, 39, 37.092, '-03:00')
|
72
|
+
car.complex_number = Complex(2,37)
|
73
|
+
car.complex_polar_number = Complex.polar(-23,28)
|
74
|
+
car.rational_number = Rational(22/7)
|
75
|
+
|
76
|
+
dump = YASL.dump(car)
|
77
|
+
|
78
|
+
puts dump.inspect
|
79
|
+
|
80
|
+
# => "{\"_class\":\"Car\",\"_id\":1,\"_instance_variables\":{\"make\":\"Mitsubishi\",\"model\":\"Eclipse\",\"year\":\"2002\",\"registration_time\":{\"_class\":\"Time\",\"_data\":[0,2452932,49177,\"12644383719423828125/137438953472\",-10800,2299161.0]},\"registration_date\":{\"_class\":\"Date\",\"_data\":[0,2452932,0,0,0,2299161.0]},\"registration_date_time\":{\"_class\":\"DateTime\",\"_data\":[0,2452932,49177,92000000,-10800,2299161.0]},\"complex_number\":{\"_class\":\"Complex\",\"_data\":\"2+37i\"},\"complex_polar_number\":{\"_class\":\"Complex\",\"_data\":\"22.13993492521203-6.230833131080988i\"},\"rational_number\":{\"_class\":\"Rational\",\"_data\":\"3/1\"}}}"
|
81
|
+
```
|
82
|
+
|
83
|
+
#### Cycles
|
84
|
+
|
85
|
+
YASL automatically detects cycles when serializing bidirectional object references.
|
86
|
+
|
87
|
+
Example (from [samples/dump_cycle.rb](samples/dump_cycle.rb)):
|
88
|
+
|
89
|
+
```ruby
|
90
|
+
require 'yasl'
|
91
|
+
require 'date'
|
92
|
+
require 'set'
|
93
|
+
|
94
|
+
class Car
|
95
|
+
attr_accessor :make,
|
96
|
+
:model,
|
97
|
+
:year,
|
98
|
+
:owner
|
99
|
+
end
|
100
|
+
|
101
|
+
class Person
|
102
|
+
class << self
|
103
|
+
def reset_count!
|
104
|
+
@count = 0
|
105
|
+
end
|
106
|
+
|
107
|
+
def increment_count!
|
108
|
+
@count ||= 0
|
109
|
+
@count += 1
|
110
|
+
end
|
111
|
+
|
112
|
+
def reset_class_count!
|
113
|
+
@@class_count = 0
|
114
|
+
end
|
115
|
+
|
116
|
+
def increment_class_count!
|
117
|
+
@@class_count = 0 unless defined?(@@class_count)
|
118
|
+
@@class_count += 1
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
attr_accessor :name, :dob, :cars
|
123
|
+
|
124
|
+
def initialize
|
125
|
+
self.class.increment_count!
|
126
|
+
self.class.increment_class_count!
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
person = Person.new
|
131
|
+
person.name = 'Sean Hux'
|
132
|
+
person.dob = Time.new(2017, 10, 17, 10, 3, 4)
|
133
|
+
|
134
|
+
car = Car.new
|
135
|
+
car.make = 'Mitsubishi'
|
136
|
+
car.model = 'Eclipse'
|
137
|
+
car.year = '2002'
|
138
|
+
|
139
|
+
car.owner = person
|
140
|
+
person.cars = [car]
|
141
|
+
|
142
|
+
dump = YASL.dump(car)
|
143
|
+
|
144
|
+
puts dump.inspect
|
145
|
+
|
146
|
+
# => "{\"_class\":\"Car\",\"_id\":1,\"_instance_variables\":{\"make\":\"Mitsubishi\",\"model\":\"Eclipse\",\"owner\":{\"_class\":\"Person\",\"_id\":1,\"_instance_variables\":{\"cars\":{\"_class\":\"Array\",\"_data\":[{\"_class\":\"Car\",\"_id\":1}]},\"dob\":{\"_class\":\"Time\",\"_data\":[0,2458044,50584,0,-14400,2299161.0]},\"name\":\"Sean Hux\"}},\"year\":\"2002\"}}"
|
147
|
+
```
|
148
|
+
|
149
|
+
### Deserialize
|
150
|
+
|
151
|
+
To deserialize, use the `YASL#load(data, whitelist_classes: [])` method. The value of `whitelist_classes` must mention all classes expected to appear in the serialized data to load. This is required to ensure software security by not allowing arbitrary unexpected classes to be deserialized.
|
152
|
+
|
153
|
+
By default, only `YASL::RUBY_BASIC_DATA_TYPES` classes are deserialized:
|
154
|
+
|
155
|
+
`NilClass`, `String`, `Integer`, `Float`, `TrueClass`, `FalseClass`, `Time`, `Date`, `Complex`, `Rational`, `Regexp`, `Symbol`, `Set`, `Range`, `Array`, `Hash`
|
156
|
+
|
157
|
+
Example (from [samples/load_basic.rb](samples/load_basic.rb)):
|
158
|
+
|
159
|
+
```ruby
|
160
|
+
require 'yasl'
|
161
|
+
require 'date'
|
162
|
+
|
163
|
+
class Car
|
164
|
+
attr_accessor :make,
|
165
|
+
:model,
|
166
|
+
:year,
|
167
|
+
:registration_time,
|
168
|
+
:registration_date,
|
169
|
+
:registration_date_time,
|
170
|
+
:complex_number,
|
171
|
+
:complex_polar_number,
|
172
|
+
:rational_number
|
173
|
+
end
|
174
|
+
|
175
|
+
car = Car.new
|
176
|
+
car.make = 'Mitsubishi'
|
177
|
+
car.model = 'Eclipse'
|
178
|
+
car.year = '2002'
|
179
|
+
car.registration_time = Time.new(2003, 10, 19, 10, 39, 37.092, '-03:00')
|
180
|
+
car.registration_date = Date.new(2003, 10, 19)
|
181
|
+
car.registration_date_time = DateTime.new(2003, 10, 19, 10, 39, 37.092, '-03:00')
|
182
|
+
car.complex_number = Complex(2,37)
|
183
|
+
car.complex_polar_number = Complex.polar(-23,28)
|
184
|
+
car.rational_number = Rational(22/7)
|
185
|
+
|
186
|
+
dump = YASL.dump(car)
|
187
|
+
car2 = YASL.load(dump, whitelist_classes: [Car])
|
188
|
+
|
189
|
+
puts car2.make
|
190
|
+
# => Mitsubishi
|
191
|
+
|
192
|
+
puts car2.model
|
193
|
+
# => Eclipse
|
194
|
+
|
195
|
+
puts car2.year
|
196
|
+
# => 2002
|
197
|
+
|
198
|
+
puts car2.registration_time
|
199
|
+
# => 2003-10-19 10:39:37 -0300
|
200
|
+
|
201
|
+
puts car2.registration_date
|
202
|
+
# => 2003-10-19
|
203
|
+
|
204
|
+
puts car2.registration_date_time
|
205
|
+
# => 2003-10-19T10:39:37-03:00
|
206
|
+
|
207
|
+
puts car2.complex_number
|
208
|
+
# => 2+37i
|
209
|
+
|
210
|
+
puts car2.complex_polar_number
|
211
|
+
# => 22.13993492521203-6.230833131080988i
|
212
|
+
|
213
|
+
puts car2.rational_number
|
214
|
+
# => 3/1
|
215
|
+
|
216
|
+
```
|
217
|
+
|
218
|
+
#### Cycles
|
219
|
+
|
220
|
+
YASL automatically restores cycles when deserializing bidirectional object references.
|
221
|
+
|
222
|
+
Example (from [samples/load_cycle.rb](samples/load_cycle.rb)):
|
223
|
+
|
224
|
+
```ruby
|
225
|
+
require 'yasl'
|
226
|
+
require 'date'
|
227
|
+
require 'set'
|
228
|
+
|
229
|
+
class Car
|
230
|
+
attr_accessor :make,
|
231
|
+
:model,
|
232
|
+
:year,
|
233
|
+
:owner
|
234
|
+
end
|
235
|
+
|
236
|
+
class Person
|
237
|
+
class << self
|
238
|
+
def reset_count!
|
239
|
+
@count = 0
|
240
|
+
end
|
241
|
+
|
242
|
+
def increment_count!
|
243
|
+
@count ||= 0
|
244
|
+
@count += 1
|
245
|
+
end
|
246
|
+
|
247
|
+
def reset_class_count!
|
248
|
+
@@class_count = 0
|
249
|
+
end
|
250
|
+
|
251
|
+
def increment_class_count!
|
252
|
+
@@class_count = 0 unless defined?(@@class_count)
|
253
|
+
@@class_count += 1
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
attr_accessor :name, :dob, :cars
|
258
|
+
|
259
|
+
def initialize
|
260
|
+
self.class.increment_count!
|
261
|
+
self.class.increment_class_count!
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
person = Person.new
|
266
|
+
person.name = 'Sean Hux'
|
267
|
+
person.dob = Time.new(2017, 10, 17, 10, 3, 4)
|
268
|
+
|
269
|
+
car = Car.new
|
270
|
+
car.make = 'Mitsubishi'
|
271
|
+
car.model = 'Eclipse'
|
272
|
+
car.year = '2002'
|
273
|
+
|
274
|
+
car.owner = person
|
275
|
+
person.cars = [car]
|
276
|
+
|
277
|
+
dump = YASL.dump(car)
|
278
|
+
car2 = YASL.load(dump, whitelist_classes: [Car, Person])
|
279
|
+
|
280
|
+
puts car2.make
|
281
|
+
# => Mitsubishi
|
282
|
+
|
283
|
+
puts car2.model
|
284
|
+
# => Eclipse
|
285
|
+
|
286
|
+
puts car2.year
|
287
|
+
# => 2002
|
288
|
+
|
289
|
+
puts car2.owner
|
290
|
+
# => #<Person:0x00007ffdf008dc20>
|
291
|
+
|
292
|
+
puts car2.owner.name
|
293
|
+
# => Sean Hux
|
294
|
+
|
295
|
+
puts car2.owner.dob
|
296
|
+
# => 2017-10-17 10:03:04 -0400
|
297
|
+
|
298
|
+
puts car2.owner.cars.inspect
|
299
|
+
# => [#<Car:0x00007ffdf008e120 @make="Mitsubishi", @model="Eclipse", @year="2002", @owner=#<Person:0x00007ffdf008dc20 @name="Sean Hux", @dob=2017-10-17 10:03:04 -0400, @cars=[...]>>]
|
300
|
+
|
301
|
+
puts car2.inspect
|
302
|
+
# => #<Car:0x00007ffdf008e120 @make="Mitsubishi", @model="Eclipse", @year="2002", @owner=#<Person:0x00007ffdf008dc20 @name="Sean Hux", @dob=2017-10-17 10:03:04 -0400, @cars=[#<Car:0x00007ffdf008e120 ...>]>>
|
303
|
+
```
|
304
|
+
|
305
|
+
## Contributing
|
306
|
+
|
307
|
+
- Check out the latest master to make sure the feature hasn't been
|
308
|
+
implemented or the bug hasn't been fixed yet.
|
309
|
+
- Check out the issue tracker to make sure someone already hasn't
|
310
|
+
requested it and/or contributed it.
|
311
|
+
- Fork the project.
|
312
|
+
- Start a feature/bugfix branch.
|
313
|
+
- Commit and push until you are happy with your contribution.
|
314
|
+
- Make sure to add tests for it. This is important so I don't break it
|
315
|
+
in a future version unintentionally.
|
316
|
+
- Please try not to mess with the Rakefile, version, or history. If
|
317
|
+
you want to have your own version, or is otherwise necessary, that
|
318
|
+
is fine, but please isolate to its own commit so I can cherry-pick
|
319
|
+
around it.
|
320
|
+
|
321
|
+
## TODO
|
322
|
+
|
323
|
+
[TODO.md](TODO.md)
|
324
|
+
|
325
|
+
## Change Log
|
326
|
+
|
327
|
+
[CHANGELOG.md](CHANGELOG.md)
|
328
|
+
|
329
|
+
## Copyright
|
330
|
+
|
331
|
+
[MIT](LICENSE.txt)
|
332
|
+
|
333
|
+
Copyright (c) 2020 Andy Maleh.
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
data/lib/yasl.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# Copyright (c) 2020 Andy Maleh
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
# a copy of this software and associated documentation files (the
|
5
|
+
# "Software"), to deal in the Software without restriction, including
|
6
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
# the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be
|
12
|
+
# included in all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
22
|
+
require 'json'
|
23
|
+
|
24
|
+
require 'yasl/dumper'
|
25
|
+
require 'yasl/loader'
|
26
|
+
|
27
|
+
module YASL
|
28
|
+
JSON_BASIC_DATA_TYPES = ['NilClass', 'String', 'Integer', 'Float', 'TrueClass', 'FalseClass']
|
29
|
+
RUBY_ONLY_BASIC_DATA_TYPES = ['Time', 'Date', 'Complex', 'Rational', 'Regexp', 'Symbol', 'Set', 'Range', 'Array', 'Hash']
|
30
|
+
RUBY_BASIC_DATA_TYPES = RUBY_ONLY_BASIC_DATA_TYPES + JSON_BASIC_DATA_TYPES
|
31
|
+
UNSERIALIZABLE_DATA_TYPES = ['Proc', 'Binding', 'IO', 'File::Stat', 'Dir', 'BasicSocket', 'MatchData', 'Method', 'UnboundMethod', 'Thread', 'ThreadGroup', 'Continuation']
|
32
|
+
|
33
|
+
class << self
|
34
|
+
def dump(object, include_classes: false)
|
35
|
+
JSON.dump(Dumper.new(object).dump(include_classes: include_classes))
|
36
|
+
end
|
37
|
+
|
38
|
+
def load(data, include_classes: false, whitelist_classes: [])
|
39
|
+
Loader.new(JSON.load(data), whitelist_classes: whitelist_classes).load(include_classes: include_classes)
|
40
|
+
end
|
41
|
+
|
42
|
+
def json_basic_data_type?(object)
|
43
|
+
type_in?(object, JSON_BASIC_DATA_TYPES)
|
44
|
+
end
|
45
|
+
|
46
|
+
def ruby_basic_data_type?(object)
|
47
|
+
type_in?(object, RUBY_BASIC_DATA_TYPES)
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def type_in?(object, types)
|
53
|
+
types.reduce(false) do |result, class_name|
|
54
|
+
result || object.class.ancestors.map(&:name).include?(class_name)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
data/lib/yasl/dumper.rb
ADDED
@@ -0,0 +1,193 @@
|
|
1
|
+
# Copyright (c) 2020 Andy Maleh
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
# a copy of this software and associated documentation files (the
|
5
|
+
# "Software"), to deal in the Software without restriction, including
|
6
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
# the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be
|
12
|
+
# included in all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
22
|
+
module YASL
|
23
|
+
class Dumper
|
24
|
+
attr_reader :object, :classes, :class_objects
|
25
|
+
|
26
|
+
def initialize(object)
|
27
|
+
@object = object
|
28
|
+
@classes = []
|
29
|
+
@class_objects = {}
|
30
|
+
end
|
31
|
+
|
32
|
+
def dump(include_classes: false)
|
33
|
+
structure = dump_structure(object)
|
34
|
+
structure.merge!(dump_classes_structure) if include_classes && structure.is_a?(Hash)
|
35
|
+
structure
|
36
|
+
end
|
37
|
+
|
38
|
+
def dump_structure(object, for_classes: false)
|
39
|
+
structure = {}
|
40
|
+
if top_level_class?(object, for_classes)
|
41
|
+
if object.name.nil?
|
42
|
+
return nil
|
43
|
+
else
|
44
|
+
structure[:_class] = object.name
|
45
|
+
add_to_classes(object)
|
46
|
+
end
|
47
|
+
elsif YASL.json_basic_data_type?(object)
|
48
|
+
structure = object
|
49
|
+
elsif YASL.ruby_basic_data_type?(object)
|
50
|
+
structure[:_class] = object.class.name
|
51
|
+
structure[:_data] = dump_ruby_basic_data_type_data(object)
|
52
|
+
else
|
53
|
+
structure.merge!(dump_non_basic_data_type_structure(object))
|
54
|
+
end
|
55
|
+
structure
|
56
|
+
end
|
57
|
+
|
58
|
+
def dump_classes_structure
|
59
|
+
structure = {}
|
60
|
+
structure[:_classes] ||= []
|
61
|
+
@original_classes = []
|
62
|
+
while classes.size > @original_classes.size
|
63
|
+
diff = (classes - @original_classes)
|
64
|
+
@original_classes = classes.clone
|
65
|
+
diff.each { |klass| structure[:_classes] << dump_class_structure(klass) }
|
66
|
+
end
|
67
|
+
structure[:_classes] = structure[:_classes].compact
|
68
|
+
structure.delete(:_classes) if structure[:_classes].empty?
|
69
|
+
structure
|
70
|
+
end
|
71
|
+
|
72
|
+
def dump_class_structure(klass)
|
73
|
+
dump_structure(klass, for_classes: true) unless klass.class_variables.empty? && klass.instance_variables.empty?
|
74
|
+
end
|
75
|
+
|
76
|
+
def dump_ruby_basic_data_type_data(object)
|
77
|
+
case object
|
78
|
+
when Time
|
79
|
+
object.to_datetime.marshal_dump
|
80
|
+
when Date
|
81
|
+
object.marshal_dump
|
82
|
+
when Complex, Rational, Regexp, Symbol
|
83
|
+
object.to_s
|
84
|
+
when Set
|
85
|
+
object.to_a.uniq.map {|element| dump_structure(element) unless unserializable?(element)}
|
86
|
+
when Range
|
87
|
+
[object.begin, object.end, object.exclude_end?]
|
88
|
+
when Array
|
89
|
+
object.map {|element| dump_structure(element) unless unserializable?(element)}
|
90
|
+
when Hash
|
91
|
+
object.reject do |key, value|
|
92
|
+
[key, value].detect {|element| unserializable?(element)}
|
93
|
+
end.map do |pair|
|
94
|
+
pair.map {|element| dump_structure(element)}
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def dump_non_basic_data_type_structure(object)
|
100
|
+
structure = {}
|
101
|
+
klass = class_for(object)
|
102
|
+
add_to_classes(klass)
|
103
|
+
structure[:_class] = klass.name
|
104
|
+
the_class_object_id = class_object_id(object)
|
105
|
+
if the_class_object_id.nil?
|
106
|
+
structure.merge!(dump_new_non_basic_data_type_structure(object))
|
107
|
+
else
|
108
|
+
structure[:_id] = the_class_object_id
|
109
|
+
end
|
110
|
+
structure
|
111
|
+
end
|
112
|
+
|
113
|
+
def dump_new_non_basic_data_type_structure(object)
|
114
|
+
structure = {}
|
115
|
+
structure[:_id] = add_to_class_array(object) unless object.is_a?(Class) || object.is_a?(Module)
|
116
|
+
structure.merge!(dump_class_variables(object))
|
117
|
+
structure.merge!(dump_instance_variables(object))
|
118
|
+
structure.merge!(dump_struct_member_values(object))
|
119
|
+
structure
|
120
|
+
end
|
121
|
+
|
122
|
+
def dump_class_variables(object)
|
123
|
+
structure = {}
|
124
|
+
if object.respond_to?(:class_variables) && !object.class_variables.empty?
|
125
|
+
structure[:_class_variables] = object.class_variables.reduce({}) do |class_vars, var|
|
126
|
+
value = object.class_variable_get(var)
|
127
|
+
unserializable?(value) ? class_vars : class_vars.merge(var.to_s.sub('@@', '') => dump_structure(value))
|
128
|
+
end
|
129
|
+
structure.delete(:_class_variables) if structure[:_class_variables].empty?
|
130
|
+
end
|
131
|
+
structure
|
132
|
+
end
|
133
|
+
|
134
|
+
def dump_instance_variables(object)
|
135
|
+
structure = {}
|
136
|
+
if !object.instance_variables.empty?
|
137
|
+
structure[:_instance_variables] = object.instance_variables.sort.reduce({}) do |instance_vars, var|
|
138
|
+
value = object.instance_variable_get(var)
|
139
|
+
unserializable?(value) ? instance_vars : instance_vars.merge(var.to_s.sub('@', '') => dump_structure(value))
|
140
|
+
end
|
141
|
+
structure.delete(:_instance_variables) if structure[:_instance_variables].empty?
|
142
|
+
end
|
143
|
+
structure
|
144
|
+
end
|
145
|
+
|
146
|
+
def dump_struct_member_values(object)
|
147
|
+
structure = {}
|
148
|
+
if object.is_a?(Struct)
|
149
|
+
structure[:_struct_member_values] = object.members.reduce({}) do |member_values, member|
|
150
|
+
value = object[member]
|
151
|
+
value.nil? || unserializable?(value) ? member_values : member_values.merge(member => dump_structure(value))
|
152
|
+
end
|
153
|
+
structure.delete(:_struct_member_values) if structure[:_struct_member_values].empty?
|
154
|
+
end
|
155
|
+
structure
|
156
|
+
end
|
157
|
+
|
158
|
+
def unserializable?(value)
|
159
|
+
result = UNSERIALIZABLE_DATA_TYPES.detect {|class_name| value.class.ancestors.map(&:name).include?(class_name)}
|
160
|
+
result = ((value.is_a?(Class) || value.is_a?(Module)) && value.name.nil?) if result.nil?
|
161
|
+
result
|
162
|
+
end
|
163
|
+
|
164
|
+
private
|
165
|
+
|
166
|
+
def top_level_class?(object, for_classes)
|
167
|
+
(object.is_a?(Class) || object.is_a?(Module)) && !for_classes
|
168
|
+
end
|
169
|
+
|
170
|
+
def class_for(object)
|
171
|
+
object.is_a?(Class) || object.is_a?(Module) ? object : object.class
|
172
|
+
end
|
173
|
+
|
174
|
+
def add_to_classes(object)
|
175
|
+
classes << object unless classes.include?(object)
|
176
|
+
end
|
177
|
+
|
178
|
+
def class_object_id(object)
|
179
|
+
object_class_array = class_objects[class_for(object)]
|
180
|
+
object_class_array_index = object_class_array&.index(object)
|
181
|
+
(object_class_array_index + 1) unless object_class_array_index.nil?
|
182
|
+
end
|
183
|
+
|
184
|
+
def add_to_class_array(object)
|
185
|
+
object_class = class_for(object)
|
186
|
+
class_objects[object_class] ||= []
|
187
|
+
class_objects[object_class] << object unless class_objects[object_class].include?(object)
|
188
|
+
class_objects[object_class].index(object) + 1
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
192
|
+
|
193
|
+
end
|
data/lib/yasl/loader.rb
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
# Copyright (c) 2020 Andy Maleh
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
# a copy of this software and associated documentation files (the
|
5
|
+
# "Software"), to deal in the Software without restriction, including
|
6
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
# the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be
|
12
|
+
# included in all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
22
|
+
module YASL
|
23
|
+
class Loader
|
24
|
+
attr_reader :structure, :whitelist_classes, :class_objects
|
25
|
+
|
26
|
+
def initialize(structure, whitelist_classes: [])
|
27
|
+
@structure = structure
|
28
|
+
@whitelist_classes = whitelist_classes
|
29
|
+
@class_objects = {}
|
30
|
+
end
|
31
|
+
|
32
|
+
def load(include_classes: false)
|
33
|
+
load_structure(structure).tap do
|
34
|
+
load_classes_structure if include_classes
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def load_classes_structure
|
39
|
+
structure['_classes'].to_a.each do |class_structure|
|
40
|
+
load_non_basic_data_type_object(class_structure, for_classes: true)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def load_structure(structure, for_classes: false)
|
45
|
+
if top_level_class?(structure)
|
46
|
+
class_for(structure['_class'])
|
47
|
+
elsif YASL.json_basic_data_type?(structure) && !(structure.is_a?(String) && structure.start_with?('_'))
|
48
|
+
structure
|
49
|
+
elsif (structure['_class'] && (structure['_data'] || !structure.keys.detect {|key| key.start_with?('_') && key != '_class'} ))
|
50
|
+
load_ruby_basic_data_type_object(structure['_class'], structure['_data'])
|
51
|
+
elsif structure['_class'] && (structure['_id'] || structure['_instance_variables'] || structure['_class_variables'] || structure['_struct_member_values'])
|
52
|
+
load_non_basic_data_type_object(structure)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def load_non_basic_data_type_object(structure, for_classes: false)
|
59
|
+
class_name = structure['_class']
|
60
|
+
object_class = class_for(class_name)
|
61
|
+
object_class.alias_method(:initialize_without_yasl, :initialize)
|
62
|
+
object = object_for_id(object_class, structure['_id'])
|
63
|
+
if object.nil?
|
64
|
+
object = for_classes ? object_class : object_class.new
|
65
|
+
add_to_class_array(object, structure['_id']) if !object.is_a?(Class) && !object.is_a?(Module)
|
66
|
+
end
|
67
|
+
structure['_instance_variables'].to_a.each do |instance_var, value|
|
68
|
+
value = load_structure(value)
|
69
|
+
object.instance_variable_set("@#{instance_var}".to_sym, value)
|
70
|
+
end
|
71
|
+
structure['_struct_member_values'].to_a.each do |member, value|
|
72
|
+
value = load_structure(value)
|
73
|
+
object[member.to_sym] = value
|
74
|
+
end
|
75
|
+
structure['_class_variables'].to_a.each do |class_var, value|
|
76
|
+
value = load_structure(value)
|
77
|
+
object.class_variable_set("@@#{class_var}".to_sym, value)
|
78
|
+
end
|
79
|
+
object
|
80
|
+
ensure
|
81
|
+
object_class&.define_method(:initialize, object_class.instance_method(:initialize_without_yasl))
|
82
|
+
end
|
83
|
+
|
84
|
+
def load_ruby_basic_data_type_object(class_name, data)
|
85
|
+
case class_name
|
86
|
+
when 'Time'
|
87
|
+
DateTime.new.marshal_load(data.map(&:to_r)).to_time
|
88
|
+
when 'Date'
|
89
|
+
Date.new.marshal_load(data.map(&:to_r))
|
90
|
+
when 'DateTime'
|
91
|
+
DateTime.new.marshal_load(data.map(&:to_r))
|
92
|
+
when 'Complex'
|
93
|
+
Complex(data)
|
94
|
+
when 'Rational'
|
95
|
+
Rational(data)
|
96
|
+
when 'Regexp'
|
97
|
+
Regexp.new(data)
|
98
|
+
when 'Symbol'
|
99
|
+
data.to_sym
|
100
|
+
when 'Set'
|
101
|
+
Set.new(data.map {|element| load_structure(element)})
|
102
|
+
when 'Range'
|
103
|
+
Range.new(*data)
|
104
|
+
when 'Array'
|
105
|
+
data.map {|element| load_structure(element)}
|
106
|
+
when 'Hash'
|
107
|
+
data.reduce({}) do |new_hash, pair|
|
108
|
+
new_hash.merge(load_structure(pair.first) => load_structure(pair.last))
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
def class_for(class_name)
|
116
|
+
class_name_components = class_name.to_s.split('::')
|
117
|
+
current_class = Object
|
118
|
+
object_class = class_name_components.reduce(Object) do |result_class, class_name|
|
119
|
+
result_class.const_get(class_name)
|
120
|
+
end
|
121
|
+
if !@whitelist_classes.include?(object_class)
|
122
|
+
raise "Class `#{class_name}` is not mentioned in `whitelist_classes` (e.g. `YASL.load(data, whitelist_classes: [#{class_name}])`)!"
|
123
|
+
end
|
124
|
+
object_class
|
125
|
+
rescue NameError
|
126
|
+
# TODO materialize a class matching the non-existing class
|
127
|
+
raise "Class `#{class_name}` does not exist! YASL expects the same classes used for serialization to exist during deserialization."
|
128
|
+
end
|
129
|
+
|
130
|
+
def top_level_class?(structure)
|
131
|
+
structure && structure.is_a?(Hash) && structure['_class'] && structure['_id'].nil? && structure['_instance_variables'].nil? && structure['_class_variables'].nil? && structure['_struct_member_values'].nil? && structure['_data'].nil?
|
132
|
+
end
|
133
|
+
|
134
|
+
def add_to_classes(object)
|
135
|
+
classes << object unless classes.include?(object)
|
136
|
+
end
|
137
|
+
|
138
|
+
def class_objects_for(object_class)
|
139
|
+
class_objects[object_class] ||= {}
|
140
|
+
end
|
141
|
+
|
142
|
+
def object_for_id(object_class, class_object_id)
|
143
|
+
return if class_object_id.nil?
|
144
|
+
return unless (object_class.is_a?(Class) || object_class.is_a?(Module))
|
145
|
+
class_objects_for(object_class)[class_object_id.to_i]
|
146
|
+
end
|
147
|
+
|
148
|
+
def add_to_class_array(object, class_object_id)
|
149
|
+
return if class_object_id.nil?
|
150
|
+
object_class = object.class
|
151
|
+
found_object = object_for_id(object_class, class_object_id)
|
152
|
+
class_objects_for(object_class)[class_object_id.to_i] = object unless found_object
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
156
|
+
end
|
data/yasl.gemspec
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# Generated by juwelier
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Juwelier::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
# stub: yasl 0.1.0 ruby lib
|
6
|
+
|
7
|
+
Gem::Specification.new do |s|
|
8
|
+
s.name = "yasl".freeze
|
9
|
+
s.version = "0.1.0"
|
10
|
+
|
11
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
|
12
|
+
s.require_paths = ["lib".freeze]
|
13
|
+
s.authors = ["Andy Maleh".freeze]
|
14
|
+
s.date = "2020-12-30"
|
15
|
+
s.description = "A pure Ruby serialization library that works across different Ruby implementations like Opal and JRuby as an alternative to YAML/Marshal.".freeze
|
16
|
+
s.email = "andy.am@gmail.com".freeze
|
17
|
+
s.extra_rdoc_files = [
|
18
|
+
"CHANGELOG.md",
|
19
|
+
"LICENSE.txt",
|
20
|
+
"README.md"
|
21
|
+
]
|
22
|
+
s.files = [
|
23
|
+
"CHANGELOG.md",
|
24
|
+
"LICENSE.txt",
|
25
|
+
"README.md",
|
26
|
+
"VERSION",
|
27
|
+
"lib/yasl.rb",
|
28
|
+
"lib/yasl/dumper.rb",
|
29
|
+
"lib/yasl/loader.rb",
|
30
|
+
"yasl.gemspec"
|
31
|
+
]
|
32
|
+
s.homepage = "http://github.com/AndyObtiva/yasl".freeze
|
33
|
+
s.licenses = ["MIT".freeze]
|
34
|
+
s.rubygems_version = "3.1.4".freeze
|
35
|
+
s.summary = "A pure Ruby serialization library that works across different Ruby implementations like Opal and JRuby as an alternative to YAML/Marshal.".freeze
|
36
|
+
|
37
|
+
if s.respond_to? :specification_version then
|
38
|
+
s.specification_version = 4
|
39
|
+
end
|
40
|
+
|
41
|
+
if s.respond_to? :add_runtime_dependency then
|
42
|
+
s.add_development_dependency(%q<rspec>.freeze, ["~> 3.5.0"])
|
43
|
+
s.add_development_dependency(%q<bundler>.freeze, [">= 1.0"])
|
44
|
+
s.add_development_dependency(%q<juwelier>.freeze, ["~> 2.1.0"])
|
45
|
+
s.add_development_dependency(%q<simplecov>.freeze, ["~> 0.16.0"])
|
46
|
+
s.add_development_dependency(%q<coveralls>.freeze, [">= 0"])
|
47
|
+
s.add_development_dependency(%q<puts_debuggerer>.freeze, [">= 0"])
|
48
|
+
s.add_development_dependency(%q<equalizer>.freeze, [">= 0"])
|
49
|
+
else
|
50
|
+
s.add_dependency(%q<rspec>.freeze, ["~> 3.5.0"])
|
51
|
+
s.add_dependency(%q<bundler>.freeze, [">= 1.0"])
|
52
|
+
s.add_dependency(%q<juwelier>.freeze, ["~> 2.1.0"])
|
53
|
+
s.add_dependency(%q<simplecov>.freeze, ["~> 0.16.0"])
|
54
|
+
s.add_dependency(%q<coveralls>.freeze, [">= 0"])
|
55
|
+
s.add_dependency(%q<puts_debuggerer>.freeze, [">= 0"])
|
56
|
+
s.add_dependency(%q<equalizer>.freeze, [">= 0"])
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
metadata
ADDED
@@ -0,0 +1,153 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: yasl
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Andy Maleh
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-12-30 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 3.5.0
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 3.5.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.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: juwelier
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 2.1.0
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 2.1.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: simplecov
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.16.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.16.0
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: coveralls
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: puts_debuggerer
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: equalizer
|
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 pure Ruby serialization library that works across different Ruby implementations
|
112
|
+
like Opal and JRuby as an alternative to YAML/Marshal.
|
113
|
+
email: andy.am@gmail.com
|
114
|
+
executables: []
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files:
|
117
|
+
- CHANGELOG.md
|
118
|
+
- LICENSE.txt
|
119
|
+
- README.md
|
120
|
+
files:
|
121
|
+
- CHANGELOG.md
|
122
|
+
- LICENSE.txt
|
123
|
+
- README.md
|
124
|
+
- VERSION
|
125
|
+
- lib/yasl.rb
|
126
|
+
- lib/yasl/dumper.rb
|
127
|
+
- lib/yasl/loader.rb
|
128
|
+
- yasl.gemspec
|
129
|
+
homepage: http://github.com/AndyObtiva/yasl
|
130
|
+
licenses:
|
131
|
+
- MIT
|
132
|
+
metadata: {}
|
133
|
+
post_install_message:
|
134
|
+
rdoc_options: []
|
135
|
+
require_paths:
|
136
|
+
- lib
|
137
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
138
|
+
requirements:
|
139
|
+
- - ">="
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
142
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
143
|
+
requirements:
|
144
|
+
- - ">="
|
145
|
+
- !ruby/object:Gem::Version
|
146
|
+
version: '0'
|
147
|
+
requirements: []
|
148
|
+
rubygems_version: 3.1.4
|
149
|
+
signing_key:
|
150
|
+
specification_version: 4
|
151
|
+
summary: A pure Ruby serialization library that works across different Ruby implementations
|
152
|
+
like Opal and JRuby as an alternative to YAML/Marshal.
|
153
|
+
test_files: []
|