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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ede09b21493ce4855f31b11dace169aeb8a218d2f524c86758ca5643fcfe29e5
4
- data.tar.gz: 9c34b64eea2244ba1673a2d643872832d694f07c6b644a413824607c9be6469b
3
+ metadata.gz: 5c4491f82bad72b2981e4bbbf35001f30c95be844ebf4bc3c8f904e019d1e0cd
4
+ data.tar.gz: eb27247b7913c181fb409f8e07f8735d42bf1ac73e55fb6905ed9c931826449b
5
5
  SHA512:
6
- metadata.gz: 6afd16380e645f3b972508747b3e41c1254058e25f2f55ab7a872140b4f06aa566a165687c162777c6dcae470d4a1b91aea25a5e82d321ae720644a4b927b353
7
- data.tar.gz: f976082dea29c4b130bb6394cc7f1d7a6d71bbef708b9312d61bad265a62e6e6519c12159a6667dcf50618475fdd5474542d959cb415e0984e7ed14355d4946e
6
+ metadata.gz: 199b1226c4a33b6bc4c1daa6ab04c34c7beecfc2c8eb82bdf27d7cc434b7c2537f1d0b806886f2f1dcd83df1a3a55d5af9be9a9b589d5cc7c06b39d508c01446
7
+ data.tar.gz: eb167b327b5041ff1087381d2f999c9d42f8942ff617ef850700a3be282732a017af0e9cdcfe04e0527979c531bfc9db31d250e64a82a8c46a91af5e9f5c36f6
@@ -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
- [![Ruby](https://github.com/AndyObtiva/yasl/workflows/Ruby/badge.svg)](https://github.com/AndyObtiva/yasl/actions?query=workflow%3ARuby)
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 like `Proc`, `Binding`, and `IO`.
14
- - Support serializing classes and modules, not just object instances.
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.1.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` classes are unserializable, and will serialize as `nil` (feel free to add more classes that you would like filtered out):
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 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.
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
1
+ 0.2.0
@@ -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
 
@@ -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
@@ -85,6 +85,8 @@ module YASL
85
85
  case class_name
86
86
  when 'Time'
87
87
  DateTime.new.marshal_load(data.map(&:to_r)).to_time
88
+ when 'BigDecimal'
89
+ BigDecimal(data)
88
90
  when 'Date'
89
91
  Date.new.marshal_load(data.map(&:to_r))
90
92
  when 'DateTime'
@@ -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.1.0 ruby lib
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.1.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-30"
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.1.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-30 00:00:00.000000000 Z
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