yasl 0.1.0 → 0.2.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 +4 -4
- data/CHANGELOG.md +7 -0
- data/README.md +25 -10
- data/VERSION +1 -1
- data/lib/yasl.rb +2 -2
- data/lib/yasl/dumper.rb +15 -7
- data/lib/yasl/ext/struct.rb +127 -0
- data/lib/yasl/loader.rb +2 -0
- data/yasl.gemspec +4 -3
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5c4491f82bad72b2981e4bbbf35001f30c95be844ebf4bc3c8f904e019d1e0cd
|
4
|
+
data.tar.gz: eb27247b7913c181fb409f8e07f8735d42bf1ac73e55fb6905ed9c931826449b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 199b1226c4a33b6bc4c1daa6ab04c34c7beecfc2c8eb82bdf27d7cc434b7c2537f1d0b806886f2f1dcd83df1a3a55d5af9be9a9b589d5cc7c06b39d508c01446
|
7
|
+
data.tar.gz: eb167b327b5041ff1087381d2f999c9d42f8942ff617ef850700a3be282732a017af0e9cdcfe04e0527979c531bfc9db31d250e64a82a8c46a91af5e9f5c36f6
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## 0.2.0
|
4
|
+
|
5
|
+
- Support Boolean serialization in Opal (instead of TrueClass and FalseClass)
|
6
|
+
- Support BigDecimal serialization
|
7
|
+
- Include optional pure Ruby reimplementaiton of Struct to avoid JS issues in Opal Struct when needed
|
8
|
+
- Fix issue with dumping not working when some ruby basic data type libraries (e.g. 'date') are not loaded by comparing to class name string instead of actual class object
|
9
|
+
|
3
10
|
## 0.1.0
|
4
11
|
|
5
12
|
- Serialize JSON basic data types
|
data/README.md
CHANGED
@@ -1,17 +1,20 @@
|
|
1
1
|
# YASL - Yet Another Serialization Library
|
2
2
|
[](http://badge.fury.io/rb/yasl)
|
3
|
-
[](https://github.com/AndyObtiva/yasl/actions?query=workflow%3Arspec)
|
4
4
|
[](https://coveralls.io/github/AndyObtiva/yasl?branch=master)
|
5
5
|
[](https://codeclimate.com/github/AndyObtiva/yasl/maintainability)
|
6
6
|
|
7
|
-
A pure Ruby serialization library that works across different Ruby implementations like Opal and JRuby as an alternative to YAML/Marshal.
|
7
|
+
A pure Ruby serialization library that works across different Ruby implementations like [Opal](https://opalrb.com/) and [JRuby](https://www.jruby.org/) as an alternative to YAML/Marshal.
|
8
|
+
|
9
|
+
(Note: this is an early alpha gem, so please use with caution, and report any encountered issues or feature suggestions to help improve)
|
8
10
|
|
9
11
|
## Requirements
|
10
12
|
|
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
|
14
|
-
-
|
13
|
+
- Portablity across different Ruby implementations, especially [Opal](https://opalrb.com/) in the web browser and [JRuby](https://www.jruby.org/).
|
14
|
+
- Zero required configuration. Developers are too busy solving business domain problems to worry about low-level serialization details.
|
15
|
+
- Silently ignore non-serializable objects (unlike Marshal), such as `Proc`, `Binding`, and `IO`.
|
16
|
+
- No special performance requirements. No high throughput usage. Average Internet speeds.
|
17
|
+
- Ensure system safety through secure deserialization.
|
15
18
|
- JSON encoding is good enough. No need for premature optimization.
|
16
19
|
|
17
20
|
## Usage Instructions
|
@@ -23,7 +26,7 @@ Run:
|
|
23
26
|
Or add to Gemfile:
|
24
27
|
|
25
28
|
```ruby
|
26
|
-
gem 'yasl', '~> 0.
|
29
|
+
gem 'yasl', '~> 0.2.0'
|
27
30
|
```
|
28
31
|
|
29
32
|
And, run:
|
@@ -40,9 +43,9 @@ require 'yasl'
|
|
40
43
|
|
41
44
|
To serialize, use the `YASL#dump(object)` method.
|
42
45
|
|
43
|
-
Keep in mind that `YASL::UNSERIALIZABLE_DATA_TYPES`
|
46
|
+
Keep in mind that `YASL::UNSERIALIZABLE_DATA_TYPES` class names are unserializable, and will serialize as `nil` (feel free to add more class names that you would like filtered out):
|
44
47
|
|
45
|
-
`Proc`, `Binding`, `IO`, `File::Stat`, `Dir`, `BasicSocket`, `MatchData`, `Method`, `UnboundMethod`, `Thread`, `ThreadGroup`, `Continuation`
|
48
|
+
`'Proc'`, `'Binding'`, `'IO'`, `'File::Stat'`, `'Dir'`, `'BasicSocket'`, `'MatchData'`, `'Method'`, `'UnboundMethod'`, `'Thread'`, `'ThreadGroup'`, `'Continuation'`
|
46
49
|
|
47
50
|
Example (from [samples/dump_basic.rb](samples/dump_basic.rb)):
|
48
51
|
|
@@ -148,7 +151,7 @@ puts dump.inspect
|
|
148
151
|
|
149
152
|
### Deserialize
|
150
153
|
|
151
|
-
To deserialize, use the `YASL#load(data, whitelist_classes: [])` method. The
|
154
|
+
To deserialize, use the `YASL#load(data, whitelist_classes: [])` method. The `whitelist_classes` array 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
155
|
|
153
156
|
By default, only `YASL::RUBY_BASIC_DATA_TYPES` classes are deserialized:
|
154
157
|
|
@@ -302,6 +305,18 @@ puts car2.inspect
|
|
302
305
|
# => #<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
306
|
```
|
304
307
|
|
308
|
+
### Struct
|
309
|
+
|
310
|
+
Struct serialization/deserialization works out of the box in standard [MRI Ruby](https://www.ruby-lang.org/) and [JRuby](https://www.jruby.org/).
|
311
|
+
|
312
|
+
To avoid some JS and `keyword_init` issues with `Struct` in [Opal](https://opalrb.com/), you may use the optional pure Ruby Struct re-implementation that comes with YASL:
|
313
|
+
|
314
|
+
```ruby
|
315
|
+
require 'yasl/ext/struct'
|
316
|
+
```
|
317
|
+
|
318
|
+
This ensures successful serialization in YASL.
|
319
|
+
|
305
320
|
## Contributing
|
306
321
|
|
307
322
|
- Check out the latest master to make sure the feature hasn't been
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
data/lib/yasl.rb
CHANGED
@@ -25,8 +25,8 @@ require 'yasl/dumper'
|
|
25
25
|
require 'yasl/loader'
|
26
26
|
|
27
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']
|
28
|
+
JSON_BASIC_DATA_TYPES = ['NilClass', 'String', 'Integer', 'Float', 'TrueClass', 'FalseClass', 'Boolean']
|
29
|
+
RUBY_ONLY_BASIC_DATA_TYPES = ['Time', 'Date', 'BigDecimal', 'Complex', 'Rational', 'Regexp', 'Symbol', 'Set', 'Range', 'Array', 'Hash']
|
30
30
|
RUBY_BASIC_DATA_TYPES = RUBY_ONLY_BASIC_DATA_TYPES + JSON_BASIC_DATA_TYPES
|
31
31
|
UNSERIALIZABLE_DATA_TYPES = ['Proc', 'Binding', 'IO', 'File::Stat', 'Dir', 'BasicSocket', 'MatchData', 'Method', 'UnboundMethod', 'Thread', 'ThreadGroup', 'Continuation']
|
32
32
|
|
data/lib/yasl/dumper.rb
CHANGED
@@ -75,19 +75,19 @@ module YASL
|
|
75
75
|
|
76
76
|
def dump_ruby_basic_data_type_data(object)
|
77
77
|
case object
|
78
|
-
when Time
|
78
|
+
when class_ancestor_names_include?('Time')
|
79
79
|
object.to_datetime.marshal_dump
|
80
|
-
when Date
|
80
|
+
when class_ancestor_names_include?('Date')
|
81
81
|
object.marshal_dump
|
82
|
-
when Complex, Rational, Regexp, Symbol
|
82
|
+
when class_ancestor_names_include?('Complex', 'Rational', 'Regexp', 'Symbol', 'BigDecimal')
|
83
83
|
object.to_s
|
84
|
-
when Set
|
84
|
+
when class_ancestor_names_include?('Set')
|
85
85
|
object.to_a.uniq.map {|element| dump_structure(element) unless unserializable?(element)}
|
86
|
-
when Range
|
86
|
+
when class_ancestor_names_include?('Range')
|
87
87
|
[object.begin, object.end, object.exclude_end?]
|
88
|
-
when Array
|
88
|
+
when class_ancestor_names_include?('Array')
|
89
89
|
object.map {|element| dump_structure(element) unless unserializable?(element)}
|
90
|
-
when Hash
|
90
|
+
when class_ancestor_names_include?('Hash')
|
91
91
|
object.reject do |key, value|
|
92
92
|
[key, value].detect {|element| unserializable?(element)}
|
93
93
|
end.map do |pair|
|
@@ -188,6 +188,14 @@ module YASL
|
|
188
188
|
class_objects[object_class].index(object) + 1
|
189
189
|
end
|
190
190
|
|
191
|
+
def class_ancestor_names_include?(*class_names)
|
192
|
+
lambda do |object|
|
193
|
+
class_names.reduce(false) do |result, class_name|
|
194
|
+
result || object.class.ancestors.map(&:name).include?(class_name)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
191
199
|
end
|
192
200
|
|
193
201
|
end
|
@@ -0,0 +1,127 @@
|
|
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
|
+
Object.send(:remove_const, :Struct) if Object.constants.include?(:Struct)
|
23
|
+
|
24
|
+
# Optional re-implmentation of Struct in Pure Ruby (to get around JS issues in Opal Struct)
|
25
|
+
class Struct
|
26
|
+
class << self
|
27
|
+
def new(*class_name_and_or_attributes, keyword_init: false)
|
28
|
+
class_name = class_name_and_or_attributes.shift if class_name_and_or_attributes.first.to_s.chars.first.to_s.upcase?
|
29
|
+
attributes = class_name_and_or_attributes.compact.map(&:to_s).map(&:to_sym)
|
30
|
+
struct_class = Class.new do
|
31
|
+
members_array = attributes
|
32
|
+
|
33
|
+
define_method(:members) do
|
34
|
+
members_array
|
35
|
+
end
|
36
|
+
|
37
|
+
def []=(attribute, value)
|
38
|
+
normalized_attribute = attribute.to_s.to_sym
|
39
|
+
raise NameError, "no member #{attribute} in struct" unless members.include?(normalized_attribute)
|
40
|
+
@member_values[normalized_attribute] = value
|
41
|
+
end
|
42
|
+
|
43
|
+
def [](attribute)
|
44
|
+
normalized_attribute = attribute.to_s.to_sym
|
45
|
+
raise NameError, "no member #{attribute} in struct" unless members.include?(normalized_attribute)
|
46
|
+
@member_values[normalized_attribute]
|
47
|
+
end
|
48
|
+
|
49
|
+
def each(&block)
|
50
|
+
to_a.each(&block)
|
51
|
+
end
|
52
|
+
|
53
|
+
def each_pair(&block)
|
54
|
+
@member_values.each_pair(&block)
|
55
|
+
end
|
56
|
+
|
57
|
+
def to_h
|
58
|
+
@member_values.clone
|
59
|
+
end
|
60
|
+
|
61
|
+
def to_a
|
62
|
+
@member_values.values
|
63
|
+
end
|
64
|
+
|
65
|
+
def size
|
66
|
+
members.size
|
67
|
+
end
|
68
|
+
alias length size
|
69
|
+
|
70
|
+
def dig(*args)
|
71
|
+
@member_values.dig(*args)
|
72
|
+
end
|
73
|
+
|
74
|
+
def select(&block)
|
75
|
+
@to_a.select(&block)
|
76
|
+
end
|
77
|
+
|
78
|
+
def eql?(other)
|
79
|
+
instance_of?(other.class) && members.all? { |key| __send__(key).public_send(:eql?, other.__send__(key)) }
|
80
|
+
end
|
81
|
+
|
82
|
+
def ==(other)
|
83
|
+
other = coerce(other).first if respond_to?(:coerce, true)
|
84
|
+
other.kind_of?(self.class) && members.all? { |key| __send__(key).public_send(:==, other.__send__(key)) }
|
85
|
+
end
|
86
|
+
|
87
|
+
if keyword_init
|
88
|
+
def initialize(struct_class_keyword_args = {})
|
89
|
+
@member_values = {}
|
90
|
+
members.each do |attribute|
|
91
|
+
singleton_class.define_method(attribute) do
|
92
|
+
self[attribute]
|
93
|
+
end
|
94
|
+
singleton_class.define_method("#{attribute}=") do |value|
|
95
|
+
self[attribute] = value
|
96
|
+
end
|
97
|
+
end
|
98
|
+
struct_class_keyword_args.each do |attribute, value|
|
99
|
+
self[attribute] = value
|
100
|
+
end
|
101
|
+
end
|
102
|
+
else
|
103
|
+
def initialize(*attribute_values)
|
104
|
+
@member_values = {}
|
105
|
+
members.each do |attribute|
|
106
|
+
singleton_class.define_method(attribute) do
|
107
|
+
self[attribute]
|
108
|
+
end
|
109
|
+
singleton_class.define_method("#{attribute}=") do |value|
|
110
|
+
self[attribute] = value
|
111
|
+
end
|
112
|
+
end
|
113
|
+
attribute_values.each_with_index do |value, i|
|
114
|
+
attribute = members[i]
|
115
|
+
self[attribute] = value
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
if class_name.nil?
|
121
|
+
struct_class
|
122
|
+
else
|
123
|
+
const_set(class_name, struct_class)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
data/lib/yasl/loader.rb
CHANGED
data/yasl.gemspec
CHANGED
@@ -2,16 +2,16 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Juwelier::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
-
# stub: yasl 0.
|
5
|
+
# stub: yasl 0.2.0 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "yasl".freeze
|
9
|
-
s.version = "0.
|
9
|
+
s.version = "0.2.0"
|
10
10
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
|
12
12
|
s.require_paths = ["lib".freeze]
|
13
13
|
s.authors = ["Andy Maleh".freeze]
|
14
|
-
s.date = "2020-12-
|
14
|
+
s.date = "2020-12-31"
|
15
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
16
|
s.email = "andy.am@gmail.com".freeze
|
17
17
|
s.extra_rdoc_files = [
|
@@ -26,6 +26,7 @@ Gem::Specification.new do |s|
|
|
26
26
|
"VERSION",
|
27
27
|
"lib/yasl.rb",
|
28
28
|
"lib/yasl/dumper.rb",
|
29
|
+
"lib/yasl/ext/struct.rb",
|
29
30
|
"lib/yasl/loader.rb",
|
30
31
|
"yasl.gemspec"
|
31
32
|
]
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: yasl
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andy Maleh
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-12-
|
11
|
+
date: 2020-12-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -124,6 +124,7 @@ files:
|
|
124
124
|
- VERSION
|
125
125
|
- lib/yasl.rb
|
126
126
|
- lib/yasl/dumper.rb
|
127
|
+
- lib/yasl/ext/struct.rb
|
127
128
|
- lib/yasl/loader.rb
|
128
129
|
- yasl.gemspec
|
129
130
|
homepage: http://github.com/AndyObtiva/yasl
|