yasl 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Gem Version](https://badge.fury.io/rb/yasl.svg)](http://badge.fury.io/rb/yasl)
|
3
|
+
[![Ruby](https://github.com/AndyObtiva/yasl/workflows/Ruby/badge.svg)](https://github.com/AndyObtiva/yasl/actions?query=workflow%3ARuby)
|
4
|
+
[![Coverage Status](https://coveralls.io/repos/github/AndyObtiva/yasl/badge.svg?branch=master)](https://coveralls.io/github/AndyObtiva/yasl?branch=master)
|
5
|
+
[![Maintainability](https://api.codeclimate.com/v1/badges/e8d043b8c78c801f0aa3/maintainability)](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: []
|