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