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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5c4491f82bad72b2981e4bbbf35001f30c95be844ebf4bc3c8f904e019d1e0cd
4
- data.tar.gz: eb27247b7913c181fb409f8e07f8735d42bf1ac73e55fb6905ed9c931826449b
3
+ metadata.gz: ad1fc5f516869a322107b0bb86c96cf46ea789960e920a14fefbf4173294a52f
4
+ data.tar.gz: 1bacb014f802c4295ebaa4b4db5ca6a99120f1cd10ac4f469cf77da2457cfcaa
5
5
  SHA512:
6
- metadata.gz: 199b1226c4a33b6bc4c1daa6ab04c34c7beecfc2c8eb82bdf27d7cc434b7c2537f1d0b806886f2f1dcd83df1a3a55d5af9be9a9b589d5cc7c06b39d508c01446
7
- data.tar.gz: eb167b327b5041ff1087381d2f999c9d42f8942ff617ef850700a3be282732a017af0e9cdcfe04e0527979c531bfc9db31d250e64a82a8c46a91af5e9f5c36f6
6
+ metadata.gz: 97579e85d7bdd8e7d7feec52e0ce6567782c67466dffe103e95261d52628d1c0e3b4a1f40897d39401e5d05d2123ea22b7f4ff98f5134f76a46e32b5103a64a4
7
+ data.tar.gz: 5f7d04c0a0e821d88bf3cafdba3c497df92f1dd92d4f2e8c8222f5c903cbc866767666d10ee8b09d0fd84d05ad03105ba9fc7f0784e4521060a7aa671005bf77
@@ -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.0'
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.0
1
+ 0.2.1
@@ -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
- if object.name.nil?
42
- return nil
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(object)
77
- case object
78
- when class_ancestor_names_include?('Time')
79
- object.to_datetime.marshal_dump
80
- when class_ancestor_names_include?('Date')
81
- object.marshal_dump
82
- when class_ancestor_names_include?('Complex', 'Rational', 'Regexp', 'Symbol', 'BigDecimal')
83
- object.to_s
84
- when class_ancestor_names_include?('Set')
85
- object.to_a.uniq.map {|element| dump_structure(element) unless unserializable?(element)}
86
- when class_ancestor_names_include?('Range')
87
- [object.begin, object.end, object.exclude_end?]
88
- when class_ancestor_names_include?('Array')
89
- object.map {|element| dump_structure(element) unless unserializable?(element)}
90
- when class_ancestor_names_include?('Hash')
91
- object.reject do |key, value|
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.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
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.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
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.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
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
@@ -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
- 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
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
- @to_a.select(&block)
73
+ to_a.select(&block)
76
74
  end
77
75
 
78
76
  def eql?(other)
79
- instance_of?(other.class) && members.all? { |key| __send__(key).public_send(:eql?, other.__send__(key)) }
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) && members.all? { |key| __send__(key).public_send(:==, other.__send__(key)) }
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
- if class_name.nil?
121
- struct_class
122
- else
123
- const_set(class_name, struct_class)
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
@@ -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['_class'] && (structure['_data'] || !structure.keys.detect {|key| key.start_with?('_') && key != '_class'} ))
49
+ elsif ruby_basic_data_type_structure?(structure)
50
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'])
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']) 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)
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({}) do |new_hash, pair|
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 !@whitelist_classes.include?(object_class)
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
@@ -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.0 ruby lib
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.0"
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 = "2020-12-31"
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.0
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: 2020-12-31 00:00:00.000000000 Z
11
+ date: 2021-01-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec