yasl 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/README.md +6 -4
- data/VERSION +1 -1
- data/lib/yasl/dumper.rb +47 -43
- data/lib/yasl/ext/struct.rb +32 -11
- data/lib/yasl/loader.rb +26 -24
- data/yasl.gemspec +3 -3
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ad1fc5f516869a322107b0bb86c96cf46ea789960e920a14fefbf4173294a52f
|
4
|
+
data.tar.gz: 1bacb014f802c4295ebaa4b4db5ca6a99120f1cd10ac4f469cf77da2457cfcaa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 97579e85d7bdd8e7d7feec52e0ce6567782c67466dffe103e95261d52628d1c0e3b4a1f40897d39401e5d05d2123ea22b7f4ff98f5134f76a46e32b5103a64a4
|
7
|
+
data.tar.gz: 5f7d04c0a0e821d88bf3cafdba3c497df92f1dd92d4f2e8c8222f5c903cbc866767666d10ee8b09d0fd84d05ad03105ba9fc7f0784e4521060a7aa671005bf77
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## 0.2.1
|
4
|
+
|
5
|
+
- Make `YASL::UNSERIALIZABLE_DATA_TYPES` work with regular classes too
|
6
|
+
- Make load `whitelist_classes` work with string class names
|
7
|
+
- Make load `whitelist_classes` work with a single class or class name string
|
8
|
+
- Support deserialization fallback of instance variables for Struct member values
|
9
|
+
- Added missing hash method to optional pure Ruby Struct implementation
|
10
|
+
- Fixed issue with requiring one arg minimum, using `select`, and using undefined `upcase?` in implementation of optional pure Ruby Struct
|
11
|
+
|
3
12
|
## 0.2.0
|
4
13
|
|
5
14
|
- Support Boolean serialization in Opal (instead of TrueClass and FalseClass)
|
data/README.md
CHANGED
@@ -11,7 +11,7 @@ A pure Ruby serialization library that works across different Ruby implementatio
|
|
11
11
|
## Requirements
|
12
12
|
|
13
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.
|
14
|
+
- Zero required configuration. Developers are too busy solving business domain problems to worry about low-level serialization details (no mucking around `as_json` methods and serializer classes).
|
15
15
|
- Silently ignore non-serializable objects (unlike Marshal), such as `Proc`, `Binding`, and `IO`.
|
16
16
|
- No special performance requirements. No high throughput usage. Average Internet speeds.
|
17
17
|
- Ensure system safety through secure deserialization.
|
@@ -26,7 +26,7 @@ Run:
|
|
26
26
|
Or add to Gemfile:
|
27
27
|
|
28
28
|
```ruby
|
29
|
-
gem 'yasl', '~> 0.2.
|
29
|
+
gem 'yasl', '~> 0.2.1'
|
30
30
|
```
|
31
31
|
|
32
32
|
And, run:
|
@@ -315,8 +315,6 @@ To avoid some JS and `keyword_init` issues with `Struct` in [Opal](https://opalr
|
|
315
315
|
require 'yasl/ext/struct'
|
316
316
|
```
|
317
317
|
|
318
|
-
This ensures successful serialization in YASL.
|
319
|
-
|
320
318
|
## Contributing
|
321
319
|
|
322
320
|
- Check out the latest master to make sure the feature hasn't been
|
@@ -333,6 +331,10 @@ This ensures successful serialization in YASL.
|
|
333
331
|
is fine, but please isolate to its own commit so I can cherry-pick
|
334
332
|
around it.
|
335
333
|
|
334
|
+
## Software Process
|
335
|
+
|
336
|
+
[Glimmer Process](https://github.com/AndyObtiva/glimmer/blob/master/PROCESS.md)
|
337
|
+
|
336
338
|
## TODO
|
337
339
|
|
338
340
|
[TODO.md](TODO.md)
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.2.
|
1
|
+
0.2.1
|
data/lib/yasl/dumper.rb
CHANGED
@@ -36,14 +36,11 @@ module YASL
|
|
36
36
|
end
|
37
37
|
|
38
38
|
def dump_structure(object, for_classes: false)
|
39
|
+
return if unserializable?(object)
|
39
40
|
structure = {}
|
40
41
|
if top_level_class?(object, for_classes)
|
41
|
-
|
42
|
-
|
43
|
-
else
|
44
|
-
structure[:_class] = object.name
|
45
|
-
add_to_classes(object)
|
46
|
-
end
|
42
|
+
structure[:_class] = object.name
|
43
|
+
add_to_classes(object)
|
47
44
|
elsif YASL.json_basic_data_type?(object)
|
48
45
|
structure = object
|
49
46
|
elsif YASL.ruby_basic_data_type?(object)
|
@@ -73,22 +70,25 @@ module YASL
|
|
73
70
|
dump_structure(klass, for_classes: true) unless klass.class_variables.empty? && klass.instance_variables.empty?
|
74
71
|
end
|
75
72
|
|
76
|
-
def dump_ruby_basic_data_type_data(
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
73
|
+
def dump_ruby_basic_data_type_data(obj)
|
74
|
+
class_ancestors_names_include = lambda do |*class_names|
|
75
|
+
lambda { |obj| class_names.any? { |class_name| obj.class.ancestors.map(&:name).include?(class_name) } }
|
76
|
+
end
|
77
|
+
case obj
|
78
|
+
when class_ancestors_names_include['Time']
|
79
|
+
obj.to_datetime.marshal_dump
|
80
|
+
when class_ancestors_names_include['Date']
|
81
|
+
obj.marshal_dump
|
82
|
+
when class_ancestors_names_include['Complex', 'Rational', 'Regexp', 'Symbol', 'BigDecimal']
|
83
|
+
obj.to_s
|
84
|
+
when class_ancestors_names_include['Set']
|
85
|
+
obj.to_a.uniq.map {|element| dump_structure(element)}
|
86
|
+
when class_ancestors_names_include['Range']
|
87
|
+
[obj.begin, obj.end, obj.exclude_end?]
|
88
|
+
when class_ancestors_names_include['Array']
|
89
|
+
obj.map {|element| dump_structure(element)}
|
90
|
+
when class_ancestors_names_include['Hash']
|
91
|
+
obj.reject do |key, value|
|
92
92
|
[key, value].detect {|element| unserializable?(element)}
|
93
93
|
end.map do |pair|
|
94
94
|
pair.map {|element| dump_structure(element)}
|
@@ -122,10 +122,7 @@ module YASL
|
|
122
122
|
def dump_class_variables(object)
|
123
123
|
structure = {}
|
124
124
|
if object.respond_to?(:class_variables) && !object.class_variables.empty?
|
125
|
-
structure[:_class_variables] = object
|
126
|
-
value = object.class_variable_get(var)
|
127
|
-
unserializable?(value) ? class_vars : class_vars.merge(var.to_s.sub('@@', '') => dump_structure(value))
|
128
|
-
end
|
125
|
+
structure[:_class_variables] = dump_class_variables_hash(object)
|
129
126
|
structure.delete(:_class_variables) if structure[:_class_variables].empty?
|
130
127
|
end
|
131
128
|
structure
|
@@ -134,10 +131,7 @@ module YASL
|
|
134
131
|
def dump_instance_variables(object)
|
135
132
|
structure = {}
|
136
133
|
if !object.instance_variables.empty?
|
137
|
-
structure[:_instance_variables] = object
|
138
|
-
value = object.instance_variable_get(var)
|
139
|
-
unserializable?(value) ? instance_vars : instance_vars.merge(var.to_s.sub('@', '') => dump_structure(value))
|
140
|
-
end
|
134
|
+
structure[:_instance_variables] = dump_instance_variables_hash(object)
|
141
135
|
structure.delete(:_instance_variables) if structure[:_instance_variables].empty?
|
142
136
|
end
|
143
137
|
structure
|
@@ -146,17 +140,14 @@ module YASL
|
|
146
140
|
def dump_struct_member_values(object)
|
147
141
|
structure = {}
|
148
142
|
if object.is_a?(Struct)
|
149
|
-
structure[:_struct_member_values] = object
|
150
|
-
value = object[member]
|
151
|
-
value.nil? || unserializable?(value) ? member_values : member_values.merge(member => dump_structure(value))
|
152
|
-
end
|
143
|
+
structure[:_struct_member_values] = dump_struct_member_values_hash(object)
|
153
144
|
structure.delete(:_struct_member_values) if structure[:_struct_member_values].empty?
|
154
145
|
end
|
155
146
|
structure
|
156
147
|
end
|
157
148
|
|
158
149
|
def unserializable?(value)
|
159
|
-
result = UNSERIALIZABLE_DATA_TYPES.detect {|class_name| value.class.ancestors.map(&:name).include?(class_name)}
|
150
|
+
result = UNSERIALIZABLE_DATA_TYPES.detect {|class_name| value.class.ancestors.map(&:name).include?(class_name.to_s)}
|
160
151
|
result = ((value.is_a?(Class) || value.is_a?(Module)) && value.name.nil?) if result.nil?
|
161
152
|
result
|
162
153
|
end
|
@@ -181,6 +172,27 @@ module YASL
|
|
181
172
|
(object_class_array_index + 1) unless object_class_array_index.nil?
|
182
173
|
end
|
183
174
|
|
175
|
+
def dump_class_variables_hash(object)
|
176
|
+
object.class_variables.reduce({}) do |class_vars, var|
|
177
|
+
value = object.class_variable_get(var)
|
178
|
+
unserializable?(value) ? class_vars : class_vars.merge(var.to_s.sub('@@', '') => dump_structure(value))
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def dump_instance_variables_hash(object)
|
183
|
+
object.instance_variables.sort.reduce({}) do |instance_vars, var|
|
184
|
+
value = object.instance_variable_get(var)
|
185
|
+
unserializable?(value) ? instance_vars : instance_vars.merge(var.to_s.sub('@', '') => dump_structure(value))
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
def dump_struct_member_values_hash(object)
|
190
|
+
object.members.reduce({}) do |member_values, member|
|
191
|
+
value = object[member]
|
192
|
+
value.nil? || unserializable?(value) ? member_values : member_values.merge(member => dump_structure(value))
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
184
196
|
def add_to_class_array(object)
|
185
197
|
object_class = class_for(object)
|
186
198
|
class_objects[object_class] ||= []
|
@@ -188,14 +200,6 @@ module YASL
|
|
188
200
|
class_objects[object_class].index(object) + 1
|
189
201
|
end
|
190
202
|
|
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
|
-
|
199
203
|
end
|
200
204
|
|
201
205
|
end
|
data/lib/yasl/ext/struct.rb
CHANGED
@@ -24,10 +24,8 @@ Object.send(:remove_const, :Struct) if Object.constants.include?(:Struct)
|
|
24
24
|
# Optional re-implmentation of Struct in Pure Ruby (to get around JS issues in Opal Struct)
|
25
25
|
class Struct
|
26
26
|
class << self
|
27
|
-
|
28
|
-
|
29
|
-
attributes = class_name_and_or_attributes.compact.map(&:to_s).map(&:to_sym)
|
30
|
-
struct_class = Class.new do
|
27
|
+
CLASS_DEFINITION_FOR_ATTRIBUTES = lambda do |attributes, keyword_init|
|
28
|
+
lambda do |defined_class|
|
31
29
|
members_array = attributes
|
32
30
|
|
33
31
|
define_method(:members) do
|
@@ -72,16 +70,23 @@ class Struct
|
|
72
70
|
end
|
73
71
|
|
74
72
|
def select(&block)
|
75
|
-
|
73
|
+
to_a.select(&block)
|
76
74
|
end
|
77
75
|
|
78
76
|
def eql?(other)
|
79
|
-
instance_of?(other.class) &&
|
77
|
+
instance_of?(other.class) &&
|
78
|
+
members.all? { |key| self[key].eql?(other[key]) }
|
80
79
|
end
|
81
80
|
|
82
81
|
def ==(other)
|
83
82
|
other = coerce(other).first if respond_to?(:coerce, true)
|
84
|
-
other.kind_of?(self.class) &&
|
83
|
+
other.kind_of?(self.class) &&
|
84
|
+
members.all? { |key| self[key] == other[key] }
|
85
|
+
end
|
86
|
+
|
87
|
+
def hash
|
88
|
+
self.class.hash +
|
89
|
+
to_a.each_with_index.map {|value, i| (i+1) * value.hash}.sum
|
85
90
|
end
|
86
91
|
|
87
92
|
if keyword_init
|
@@ -117,11 +122,27 @@ class Struct
|
|
117
122
|
end
|
118
123
|
end
|
119
124
|
end
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
125
|
+
end
|
126
|
+
|
127
|
+
ARG_VALIDATION = lambda do |class_name_or_attribute, *attributes|
|
128
|
+
class_name_or_attribute.nil? || attributes.any?(&:nil?)
|
129
|
+
end
|
130
|
+
|
131
|
+
CLASS_NAME_EXTRACTION = lambda do |class_name_or_attribute|
|
132
|
+
if class_name_or_attribute.is_a?(String)
|
133
|
+
raise NameError, "identifier name needs to be constant" unless class_name_or_attribute.match(/^[A-Z]/)
|
134
|
+
class_name_or_attribute
|
124
135
|
end
|
125
136
|
end
|
137
|
+
|
138
|
+
def new(class_name_or_attribute, *attributes, keyword_init: false)
|
139
|
+
raise 'Arguments cannot be nil' if ARG_VALIDATION[class_name_or_attribute, *attributes]
|
140
|
+
class_name = CLASS_NAME_EXTRACTION[class_name_or_attribute]
|
141
|
+
attributes.unshift(class_name_or_attribute) if class_name.nil?
|
142
|
+
attributes = attributes.map(&:to_sym)
|
143
|
+
struct_class = Class.new(&CLASS_DEFINITION_FOR_ATTRIBUTES[attributes, keyword_init])
|
144
|
+
class_name.nil? ? struct_class : const_set(class_name, struct_class)
|
145
|
+
end
|
146
|
+
|
126
147
|
end
|
127
148
|
end
|
data/lib/yasl/loader.rb
CHANGED
@@ -46,15 +46,23 @@ module YASL
|
|
46
46
|
class_for(structure['_class'])
|
47
47
|
elsif YASL.json_basic_data_type?(structure) && !(structure.is_a?(String) && structure.start_with?('_'))
|
48
48
|
structure
|
49
|
-
elsif (structure
|
49
|
+
elsif ruby_basic_data_type_structure?(structure)
|
50
50
|
load_ruby_basic_data_type_object(structure['_class'], structure['_data'])
|
51
|
-
elsif
|
51
|
+
elsif load_non_basic_data_type_structure?(structure)
|
52
52
|
load_non_basic_data_type_object(structure)
|
53
53
|
end
|
54
54
|
end
|
55
55
|
|
56
56
|
private
|
57
57
|
|
58
|
+
def ruby_basic_data_type_structure?(structure)
|
59
|
+
(structure['_class'] && (structure['_data'] || !structure.keys.detect {|key| key.start_with?('_') && key != '_class'} ))
|
60
|
+
end
|
61
|
+
|
62
|
+
def load_non_basic_data_type_structure?(structure)
|
63
|
+
structure['_class'] && (structure['_id'] || structure['_instance_variables'] || structure['_class_variables'] || structure['_struct_member_values'])
|
64
|
+
end
|
65
|
+
|
58
66
|
def load_non_basic_data_type_object(structure, for_classes: false)
|
59
67
|
class_name = structure['_class']
|
60
68
|
object_class = class_for(class_name)
|
@@ -62,20 +70,11 @@ module YASL
|
|
62
70
|
object = object_for_id(object_class, structure['_id'])
|
63
71
|
if object.nil?
|
64
72
|
object = for_classes ? object_class : object_class.new
|
65
|
-
add_to_class_array(object, structure['_id'])
|
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)
|
73
|
+
add_to_class_array(object, structure['_id'])
|
78
74
|
end
|
75
|
+
structure['_class_variables'].to_a.each { |class_var, value| object.class_variable_set("@@#{class_var}".to_sym, load_structure(value)) }
|
76
|
+
structure['_instance_variables'].to_a.each { |instance_var, value| object.instance_variable_set("@#{instance_var}".to_sym, load_structure(value)) }
|
77
|
+
structure['_struct_member_values'].to_a.each { |member, value| load_struct_member_value(object, member, value) }
|
79
78
|
object
|
80
79
|
ensure
|
81
80
|
object_class&.define_method(:initialize, object_class.instance_method(:initialize_without_yasl))
|
@@ -106,9 +105,7 @@ module YASL
|
|
106
105
|
when 'Array'
|
107
106
|
data.map {|element| load_structure(element)}
|
108
107
|
when 'Hash'
|
109
|
-
data.reduce({})
|
110
|
-
new_hash.merge(load_structure(pair.first) => load_structure(pair.last))
|
111
|
-
end
|
108
|
+
data.reduce({}) { |new_hash, pair| new_hash.merge(load_structure(pair.first) => load_structure(pair.last)) }
|
112
109
|
end
|
113
110
|
end
|
114
111
|
|
@@ -120,7 +117,7 @@ module YASL
|
|
120
117
|
object_class = class_name_components.reduce(Object) do |result_class, class_name|
|
121
118
|
result_class.const_get(class_name)
|
122
119
|
end
|
123
|
-
if
|
120
|
+
if ![@whitelist_classes].compact.flatten.map(&:to_s).include?(object_class.to_s)
|
124
121
|
raise "Class `#{class_name}` is not mentioned in `whitelist_classes` (e.g. `YASL.load(data, whitelist_classes: [#{class_name}])`)!"
|
125
122
|
end
|
126
123
|
object_class
|
@@ -133,10 +130,6 @@ module YASL
|
|
133
130
|
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?
|
134
131
|
end
|
135
132
|
|
136
|
-
def add_to_classes(object)
|
137
|
-
classes << object unless classes.include?(object)
|
138
|
-
end
|
139
|
-
|
140
133
|
def class_objects_for(object_class)
|
141
134
|
class_objects[object_class] ||= {}
|
142
135
|
end
|
@@ -148,11 +141,20 @@ module YASL
|
|
148
141
|
end
|
149
142
|
|
150
143
|
def add_to_class_array(object, class_object_id)
|
151
|
-
return if class_object_id.nil?
|
144
|
+
return if object.is_a?(Class) || object.is_a?(Module) || class_object_id.nil?
|
152
145
|
object_class = object.class
|
153
146
|
found_object = object_for_id(object_class, class_object_id)
|
154
147
|
class_objects_for(object_class)[class_object_id.to_i] = object unless found_object
|
155
148
|
end
|
156
149
|
|
150
|
+
def load_struct_member_value(object, member, value)
|
151
|
+
value = load_structure(value)
|
152
|
+
begin
|
153
|
+
object[member.to_sym] = value
|
154
|
+
rescue => e
|
155
|
+
puts "#{e.message}. Setting `@#{member}` instance variable instead."
|
156
|
+
object.instance_variable_set("@#{member}".to_sym, value)
|
157
|
+
end
|
158
|
+
end
|
157
159
|
end
|
158
160
|
end
|
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.2.
|
5
|
+
# stub: yasl 0.2.1 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "yasl".freeze
|
9
|
-
s.version = "0.2.
|
9
|
+
s.version = "0.2.1"
|
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 = "
|
14
|
+
s.date = "2021-01-01"
|
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 = [
|
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.2.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andy Maleh
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-01-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|