firm 0.9.8 → 1.0.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: 70fc573f0deb51e97747caf3a36ccfabb8e93a0ff8cb72f0de16e877eeb9713b
4
- data.tar.gz: 66811b4933baeaa1e555223fa554292339534a5de8d4de66bbd2fe4d26db1633
3
+ metadata.gz: 0ebb1f6594b3ecc4e56863a0f04ce3eb44fc5bfad0218a183298b44e56e9cf8e
4
+ data.tar.gz: 4e2349b45ae0479c968406fcd2eafb7ac4e76c8317cbbeeea20ecb784e998115
5
5
  SHA512:
6
- metadata.gz: a37bb0eb3d1aaffed3531979640070f7048e899c93206c61f2e9377bf256f923b4c70f19d1cd8bedc486a94b124803e6f7cd2b42419a65c2746e8f19ca1db432
7
- data.tar.gz: 8fe43c7d9828632f27c0e12271250c85b06662ffd9969d3feefb4c9e78f653fc993c42361fcb38dc8e719c2c75601e9da603b26c8ee83736b9d3d991750ef963
6
+ metadata.gz: 247ca7e7c9eb24fc5ca31c8cfb3f06ec9e4c3779627f3e35722d2c51a630c255cc4d182d434d35da7207f112b0905635a5717fd767f3c904a251b7ab84873cbd
7
+ data.tar.gz: 2953c510c88ed9078f777a0c8e23399f48339ef5f6c85fcf264fe025de795ecc187407d7d7bddf77b89dbb182eb158123e62aad909c91fbfffc04f613cf8280f
data/README.md CHANGED
@@ -8,11 +8,11 @@
8
8
 
9
9
  ## Introduction
10
10
 
11
- FIRM is a pure Ruby library that works across different Ruby implementations like MRI Ruby and JRuby providing output
12
- format independent object (de-)serialization support.
11
+ FIRM is a pure Ruby library that works across different Ruby implementations like MRI Ruby and JRuby providing format
12
+ independent object (de-)serialization support.
13
13
 
14
14
  FIRM is explicitly **NOT** intended as a non-discriminative marshaling library (dumping any object's attributes)
15
- but rather as structured and safe serialization library requiring users to think about what state they want
15
+ but rather as a structured and safe serialization library requiring users to think about what state they want
16
16
  persisted (and possibly in what form) and what not.
17
17
  Straightforward attribute serialization is simple with minimal intrusion on user code.
18
18
  In addition various customization options are available to tweak (de-)serialization for a perfect fit if needed.
@@ -39,20 +39,21 @@ FIRM supports (de-)serializing many core Ruby objects out of the box including:
39
39
  - `Time`
40
40
  - `Struct`
41
41
  - `Set`
42
- - `OpenStruct`
42
+ - `OpenStruct` (optional starting from Ruby 3.5 which removes this class from the standard library)
43
43
  - `Date`
44
44
  - `DateTime`
45
45
 
46
- For security reasons FIRM does **not** support direct (de-)serializing of `Class` objects but will rather
46
+ For simplicity and security reasons FIRM does **not** support direct (de-)serializing of `Class` objects but will rather
47
47
  serialize (and deserialize) these as their scoped string names. Customized property setters can be used to
48
48
  resolve Class objects from these names if really needed.
49
49
 
50
- FIRM also supports a simple scheme to provide (de-)serialization support for user defined classes.
50
+ Serialization support for user defined classes is available through a simple DSL scheme.
51
51
 
52
52
  FIRM provides object aliasing support for JSON and XML in a similar fashion as the standard support provided
53
- by YAML.
53
+ by YAML.<br>
54
+ In addition FIRM automatically recognizes and handles cyclic references of aliasable objects.
54
55
 
55
- FIRM also automatically recognizes and handles cyclic references of aliasable objects.
56
+ FIRM serialization is also thread safe and supports re-entrancy (i.e. nested serialization).
56
57
 
57
58
  ## Installing FIRM
58
59
 
@@ -32,12 +32,20 @@ module FIRM
32
32
  end
33
33
 
34
34
  require 'set'
35
- require 'ostruct'
35
+ # from Ruby 3.5.0 OpenStruct will not be available by default anymore
36
+ begin
37
+ require 'ostruct'
38
+ rescue LoadError
39
+ end
36
40
 
37
- [::Array, ::Hash, ::Struct, ::Range, ::Rational, ::Complex, ::Regexp, ::Set, ::OpenStruct, ::Time, ::Date, ::DateTime].each do |c|
41
+ [::Array, ::Hash, ::Struct, ::Range, ::Rational, ::Complex, ::Regexp, ::Set, ::Time, ::Date, ::DateTime].each do |c|
38
42
  c.include FIRM::Serializable::CoreExt
39
43
  end
40
44
 
45
+ if ::Object.const_defined?(:OpenStruct)
46
+ ::OpenStruct.include FIRM::Serializable::CoreExt
47
+ end
48
+
41
49
  if ::Object.const_defined?(:BigDecimal)
42
50
  ::BigDecimal.include FIRM::Serializable::CoreExt
43
51
  end
@@ -14,7 +14,12 @@ require 'json/add/bigdecimal' if ::Object.const_defined?(:BigDecimal)
14
14
  require 'json/add/rational'
15
15
  require 'json/add/complex'
16
16
  require 'json/add/set'
17
- require 'json/add/ostruct'
17
+ # from Ruby 3.5.0 OpenStruct will not be available by default anymore
18
+ begin
19
+ require 'ostruct'
20
+ require 'json/add/ostruct'
21
+ rescue LoadError
22
+ end
18
23
 
19
24
  module FIRM
20
25
 
@@ -22,6 +27,8 @@ module FIRM
22
27
 
23
28
  module JSON
24
29
 
30
+ CREATE_ID = 'rbklass'.freeze
31
+
25
32
  # Derived Hash class to use for deserialized JSON object data which
26
33
  # supports using Symbol keys.
27
34
  class ObjectHash < ::Hash
@@ -107,7 +114,8 @@ module FIRM
107
114
  class << self
108
115
  def serializables
109
116
  set = ::Set.new( [::NilClass, ::TrueClass, ::FalseClass, ::Integer, ::Float, ::String, ::Array, ::Hash,
110
- ::Date, ::DateTime, ::Range, ::Rational, ::Complex, ::Regexp, ::Struct, ::Symbol, ::Time, ::Set, ::OpenStruct])
117
+ ::Date, ::DateTime, ::Range, ::Rational, ::Complex, ::Regexp, ::Struct, ::Symbol, ::Time, ::Set])
118
+ set << ::OpenStruct if ::Object.const_defined?(:OpenStruct)
111
119
  set << ::BigDecimal if ::Object.const_defined?(:BigDecimal)
112
120
  set
113
121
  end
@@ -156,6 +164,8 @@ module FIRM
156
164
  begin
157
165
  # initialize anchor registry
158
166
  Serializable::Aliasing.start_anchor_object_registry
167
+ # set custom (more compact) create_id
168
+ ::JSON.create_id = Serializable::JSON::CREATE_ID
159
169
  for_json = obj.respond_to?(:as_json) ? obj.as_json : obj
160
170
  if pretty
161
171
  if io || io.respond_to?(:write)
@@ -179,6 +189,8 @@ module FIRM
179
189
  Serializable::Aliasing.start_anchor_references
180
190
  # enable safe deserializing
181
191
  self.start_safe_deserialize
192
+ # set custom (more compact) create_id
193
+ ::JSON.create_id = Serializable::JSON::CREATE_ID
182
194
  ::JSON.parse!(source,
183
195
  create_additions: true,
184
196
  object_class: Serializable::JSON::ObjectHash)
@@ -407,21 +419,23 @@ class ::Struct
407
419
  end
408
420
  end
409
421
 
410
- class ::OpenStruct
411
- include FIRM::Serializable::JSON::ContainerPatch
422
+ if ::Object.const_defined?(:OpenStruct)
423
+ class ::OpenStruct
424
+ include FIRM::Serializable::JSON::ContainerPatch
412
425
 
413
- class << self
414
- # Create a new OpenStruct instance from deserialized JSON data.
415
- # @param [Hash] object deserialized JSON object
416
- # @return [OpenStruct] restored OpenStruct instance
417
- def json_create(object)
418
- json_new(object) { |instance| object['t'].each { |k,v| instance[k] = v } }
426
+ class << self
427
+ # Create a new OpenStruct instance from deserialized JSON data.
428
+ # @param [Hash] object deserialized JSON object
429
+ # @return [OpenStruct] restored OpenStruct instance
430
+ def json_create(object)
431
+ json_new(object) { |instance| object['t'].each { |k,v| instance[k] = v } }
432
+ end
419
433
  end
420
- end
421
434
 
422
- def as_json(*)
423
- build_json do |json_data|
424
- json_data['t'] = table.collect { |k,v| [k.respond_to?(:as_json) ? k.as_json : k, v.respond_to?(:as_json) ? v.as_json : v] }
435
+ def as_json(*)
436
+ build_json do |json_data|
437
+ json_data['t'] = table.collect { |k,v| [k.respond_to?(:as_json) ? k.as_json : k, v.respond_to?(:as_json) ? v.as_json : v] }
438
+ end
425
439
  end
426
440
  end
427
441
  end
@@ -3,7 +3,11 @@
3
3
 
4
4
 
5
5
  require 'set'
6
- require 'ostruct'
6
+ # from Ruby 3.5.0 OpenStruct will not be available by default anymore
7
+ begin
8
+ require 'ostruct'
9
+ rescue LoadError
10
+ end
7
11
  require 'date'
8
12
 
9
13
  module FIRM
@@ -60,7 +64,7 @@ module FIRM
60
64
  def to_xml(_, _)
61
65
  raise Serializable::Exception, "Missing serialization method for #{klass} XML handler"
62
66
  end
63
- def from_xml(xml)
67
+ def from_xml(_xml)
64
68
  raise Serializable::Exception, "Missing serialization method for #{klass} XML handler"
65
69
  end
66
70
  end
@@ -181,7 +185,7 @@ module FIRM
181
185
  create_type_node(xml).add_child(Nokogiri::XML::CDATA.new(xml.document, value.name))
182
186
  xml
183
187
  end
184
- def from_xml(xml)
188
+ def from_xml(_xml)
185
189
  # should never be called
186
190
  raise Serializable::Exception, 'Unsupported Class deserialization'
187
191
  end
@@ -291,10 +295,9 @@ module FIRM
291
295
  end
292
296
  def from_xml(xml)
293
297
  create_from_xml(xml) do |instance|
294
- xml.elements.inject(instance) do |hash, pair|
298
+ xml.elements.each do |pair|
295
299
  k, v = pair.elements
296
300
  instance[Serializable::XML.from_xml(k)] = Serializable::XML.from_xml(v)
297
- instance
298
301
  end
299
302
  end
300
303
  end
@@ -434,22 +437,23 @@ module FIRM
434
437
  end
435
438
  end
436
439
 
437
- define_xml_handler(::OpenStruct, aliasable: true) do
438
- def to_xml(xml, value)
439
- build_xml(xml, value) do |node|
440
- value.each_pair do |k,v|
441
- pair = node.add_child(Nokogiri::XML::Node.new('P', node.document))
442
- Serializable::XML.to_xml(pair, k)
443
- Serializable::XML.to_xml(pair, v)
440
+ if ::Object.const_defined?(:OpenStruct)
441
+ define_xml_handler(::OpenStruct, aliasable: true) do
442
+ def to_xml(xml, value)
443
+ build_xml(xml, value) do |node|
444
+ value.each_pair do |k,v|
445
+ pair = node.add_child(Nokogiri::XML::Node.new('P', node.document))
446
+ Serializable::XML.to_xml(pair, k)
447
+ Serializable::XML.to_xml(pair, v)
448
+ end
444
449
  end
445
450
  end
446
- end
447
- def from_xml(xml)
448
- create_from_xml(xml) do |instance|
449
- xml.elements.inject(::OpenStruct.new) do |hash, pair|
450
- k, v = pair.elements
451
- instance[Serializable::XML.from_xml(k)] = Serializable::XML.from_xml(v)
452
- instance
451
+ def from_xml(xml)
452
+ create_from_xml(xml) do |instance|
453
+ xml.elements.each do |pair|
454
+ k, v = pair.elements
455
+ instance[Serializable::XML.from_xml(k)] = Serializable::XML.from_xml(v)
456
+ end
453
457
  end
454
458
  end
455
459
  end
@@ -5,7 +5,11 @@
5
5
  require 'yaml'
6
6
  require 'date'
7
7
  require 'set'
8
- require 'ostruct'
8
+ # from Ruby 3.5.0 OpenStruct will not be available by default anymore
9
+ begin
10
+ require 'ostruct'
11
+ rescue LoadError
12
+ end
9
13
 
10
14
  module FIRM
11
15
 
@@ -15,7 +19,8 @@ module FIRM
15
19
 
16
20
  class << self
17
21
  def serializables
18
- list = [::Date, ::DateTime, ::Range, ::Rational, ::Complex, ::Regexp, ::Struct, ::Symbol, ::Time, ::Set, ::OpenStruct]
22
+ list = [::Date, ::DateTime, ::Range, ::Rational, ::Complex, ::Regexp, ::Struct, ::Symbol, ::Time, ::Set]
23
+ list.push(::OpenStruct) if ::Object.const_defined?(:OpenStruct)
19
24
  list.push(::BigDecimal) if ::Object.const_defined?(:BigDecimal)
20
25
  list
21
26
  end
data/lib/firm/version.rb CHANGED
@@ -4,6 +4,6 @@
4
4
  module FIRM
5
5
 
6
6
  # FIRM version
7
- VERSION = "0.9.8"
7
+ VERSION = "1.0.0"
8
8
 
9
9
  end
@@ -1084,4 +1084,62 @@ module SerializerTestMixin
1084
1084
  assert_equal(obj.symbol, obj_new.symbol)
1085
1085
  end
1086
1086
 
1087
+ def run_test_threads
1088
+ data = { list: ::Array.new(5, [ PropTest.new, Point.new(0, 0), Point.new(10, 10), Point.new(100, 400), Rect.new(20, 20, 40, 40) ]) }
1089
+ results = []
1090
+ threads = ::Array.new(10) do
1091
+ Thread.new do
1092
+ results << run_test_fibers(data)
1093
+ end
1094
+ end
1095
+ threads.each { |t| t.join }
1096
+ results
1097
+ end
1098
+
1099
+ def run_test_fibers(data)
1100
+ fibers = ::Array.new(100) do |_|
1101
+ Fiber.new do
1102
+ s = assert_nothing_raised { data.serialize }
1103
+ Fiber.yield nil
1104
+ new_data = assert_nothing_raised { FIRM.deserialize(s) }
1105
+ Fiber.yield nil
1106
+ assert_instance_of(::Hash, new_data)
1107
+ assert_instance_of(::Array, new_data[:list])
1108
+ assert_equal(5, new_data[:list].size)
1109
+ assert_true(new_data[:list].all? { |e| e.is_a?(::Array) && e.size == 5 })
1110
+ 5.times { |i| assert_equal(data[:list].first[i], new_data[:list].first[i]) }
1111
+ 4.times do |n|
1112
+ 5.times { |i| assert_equal(new_data[:list].first[i].object_id, new_data[:list][n+1][i].object_id) }
1113
+ end
1114
+ new_data
1115
+ end
1116
+ end
1117
+ results = []
1118
+ begin
1119
+ fibers = fibers.select do |fiber|
1120
+ if (rc = fiber.resume)
1121
+ results << rc
1122
+ false
1123
+ else
1124
+ true
1125
+ end
1126
+ end
1127
+ end until fibers.empty?
1128
+ results
1129
+ end
1130
+
1131
+ def test_threading
1132
+
1133
+ results = run_test_threads
1134
+
1135
+ set = results.inject(::Set.new) do |set, fiber_results|
1136
+ fiber_results.inject(set) { |set_, fiber_result| set_.merge(fiber_result[:list][1].collect { |o| o.object_id }) }
1137
+ end
1138
+
1139
+ # although we started with a single unique dataset, distributing that through 10 threads * 100 fibers
1140
+ # to serialize and deserialize should result in 1000 distinct datasets with each 5 distinct data instances
1141
+ assert_equal(5000, set.size)
1142
+
1143
+ end
1144
+
1087
1145
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: firm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.8
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Martin Corino
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-09-23 00:00:00.000000000 Z
11
+ date: 2024-10-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake