firm 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/firm/serializable.rb +53 -12
- data/lib/firm/version.rb +1 -1
- data/tests/serializer_tests.rb +63 -0
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f36086871785c4f7a5db75ed9e4b9272a4285c80c76335c7e09bd4eccdb2c939
|
4
|
+
data.tar.gz: 9936ecd659270cedddfd0d0d18fac176783b97aa7f9c52739fbe45abf1a107d8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d4800195157b45b817aa2a700113c126b466b9ec53c342fb6e4473ae4bf6d9b463fe05b839ef65f1aa12db1d4e0a128d7026b10b6eacf5bb5e31c4891de1349b
|
7
|
+
data.tar.gz: 6893d0692e160627a8a2e1f5ed32de5181dde889778874d05955469e12074c90d7a3f377321dea7b25dd2178e3a97eddf55986f5ea65d6fe4e64f0130e9f0c4b
|
data/lib/firm/serializable.rb
CHANGED
@@ -12,16 +12,33 @@ module FIRM
|
|
12
12
|
class Exception < RuntimeError; end
|
13
13
|
|
14
14
|
class Property
|
15
|
-
def initialize(klass, prop, proc=nil, force: false, handler: nil, &block)
|
15
|
+
def initialize(klass, prop, proc=nil, force: false, handler: nil, optional: false, &block)
|
16
16
|
::Kernel.raise ArgumentError, "Invalid property id [#{prop}]" unless ::String === prop || ::Symbol === prop
|
17
17
|
::Kernel.raise ArgumentError, "Duplicate property id [#{prop}]" if klass.has_serializer_property?(prop)
|
18
18
|
@klass = klass
|
19
19
|
@id = prop.to_sym
|
20
20
|
@forced = force
|
21
|
+
@optional = optional.nil? || optional
|
22
|
+
@default = if @optional
|
23
|
+
case optional
|
24
|
+
when Proc
|
25
|
+
::Kernel.raise ArgumentError,
|
26
|
+
'Invalid optional value proc' unless optional.arity.abs == 2
|
27
|
+
optional
|
28
|
+
when UnboundMethod
|
29
|
+
::Kernel.raise ArgumentError,
|
30
|
+
'Invalid optional value method' unless optional.arity.abs == 1
|
31
|
+
->(obj, id) { optional.bind(obj).call(id) }
|
32
|
+
else
|
33
|
+
optional == true ? nil : optional
|
34
|
+
end
|
35
|
+
else
|
36
|
+
nil
|
37
|
+
end
|
21
38
|
if block || handler
|
22
39
|
if handler
|
23
40
|
::Kernel.raise ArgumentError,
|
24
|
-
"Invalid property handler #{handler} for #{prop}" unless ::Proc === handler || ::Symbol === handler
|
41
|
+
"Invalid property handler #{handler} for #{prop}" unless ::Proc === handler || ::Symbol === handler || ::String === handler
|
25
42
|
if handler.is_a?(::Proc)
|
26
43
|
::Kernel.raise ArgumentError, "Invalid property block #{proc} for #{prop}" unless block.arity == -3
|
27
44
|
@getter = ->(obj) { handler.call(@id, obj) }
|
@@ -56,7 +73,7 @@ module FIRM
|
|
56
73
|
def serialize(obj, data, excludes)
|
57
74
|
unless excludes.include?(@id)
|
58
75
|
val = getter.call(obj)
|
59
|
-
unless Serializable === val && val.serialize_disabled? && !@forced
|
76
|
+
unless optional?(obj, val) || (Serializable === val && val.serialize_disabled? && !@forced)
|
60
77
|
data[@id] = case val
|
61
78
|
when ::Array
|
62
79
|
val.select { |elem| !(Serializable === elem && elem.serialize_disabled?) }
|
@@ -75,9 +92,10 @@ module FIRM
|
|
75
92
|
end
|
76
93
|
end
|
77
94
|
|
78
|
-
def
|
79
|
-
|
95
|
+
def optional?(obj, val)
|
96
|
+
@optional && val == (Proc === @default ? @default.call(obj, @id) : @default)
|
80
97
|
end
|
98
|
+
private :optional?
|
81
99
|
|
82
100
|
def get_method(id)
|
83
101
|
begin
|
@@ -312,15 +330,22 @@ module FIRM
|
|
312
330
|
module SerializeClassMethods
|
313
331
|
|
314
332
|
# Adds (a) serializable property(-ies) for instances of his class (and derived classes)
|
315
|
-
# @overload property(*props, force: false)
|
333
|
+
# @overload property(*props, force: false, optional: false)
|
316
334
|
# Specifies one or more serialized properties.
|
317
335
|
# The serialization framework will determine the availability of setter and getter methods
|
318
336
|
# automatically by looking for methods <code>"#{prop_id}=(v)"</code>, <code>"#set_{prop_id}(v)"</code> or <code>"#{prop_id}(v)"</code>
|
319
337
|
# for setters and <code>"#{prop_id}()"</code> or <code>"#get_{prop_id}"</code> for getters.
|
320
338
|
# @param [Symbol,String] props one or more ids of serializable properties
|
321
339
|
# @param [Boolean] force overrides any #disable_serialize for the properties specified
|
340
|
+
# @param [Object] optional indicates optionality;
|
341
|
+
# if `false` the property will not be optional;
|
342
|
+
# `true` means optional if the serialized value == `nil`;
|
343
|
+
# any value other than 'false' or 'true' means optional if the serialize value equals that value;
|
344
|
+
# alternatively a Proc, Lambda (gets the object and the property id passed) or
|
345
|
+
# UnboundMethod (gets the property id passed) can be specified which
|
346
|
+
# is called at serialization time to determine the default (optional) value
|
322
347
|
# @return [void]
|
323
|
-
# @overload property(hash, force: false)
|
348
|
+
# @overload property(hash, force: false, optional: false)
|
324
349
|
# Specifies one or more serialized properties with associated setter/getter method ids/procs/lambda-s.
|
325
350
|
# @example
|
326
351
|
# property(
|
@@ -340,8 +365,15 @@ module FIRM
|
|
340
365
|
# to be able to support setting explicit nil values.
|
341
366
|
# @param [Hash] hash a hash of pairs of property ids and getter/setter procs
|
342
367
|
# @param [Boolean] force overrides any #disable_serialize for the properties specified
|
368
|
+
# @param [Object] optional indicates optionality;
|
369
|
+
# if `false` the property will not be optional;
|
370
|
+
# `true` means optional if the serialized value == `nil`;
|
371
|
+
# any value other than 'false' or 'true' means optional if the serialize value equals that value;
|
372
|
+
# alternatively a Proc, Lambda (gets the object and the property id passed) or
|
373
|
+
# UnboundMethod (gets the property id passed) can be specified which
|
374
|
+
# is called at serialization time to determine the default (optional) value
|
343
375
|
# @return [void]
|
344
|
-
# @overload property(*props, force: false, handler: nil, &block)
|
376
|
+
# @overload property(*props, force: false, handler: nil, optional: false, &block)
|
345
377
|
# Specifies one or more serialized properties with a getter/setter handler proc/method/block.
|
346
378
|
# The getter/setter proc or block should accept either 2 (property id and object for getter) or 3 arguments
|
347
379
|
# (property id, object and value for setter) and is assumed to handle getter/setter requests
|
@@ -364,29 +396,38 @@ module FIRM
|
|
364
396
|
# to be able to support setting explicit nil values.
|
365
397
|
# @param [Symbol,String] props one or more ids of serializable properties
|
366
398
|
# @param [Boolean] force overrides any #disable_serialize for the properties specified
|
399
|
+
# @param [Symbol,String,Proc] handler serialization handler method name or Proc
|
400
|
+
# @param [Object] optional indicates optionality;
|
401
|
+
# if `false` the property will not be optional;
|
402
|
+
# `true` means optional if the serialized value == `nil`;
|
403
|
+
# any value other than 'false' or 'true' means optional if the serialize value equals that value;
|
404
|
+
# alternatively a Proc, Lambda (gets the object and the property id passed) or
|
405
|
+
# UnboundMethod (gets the property id passed) can be specified which
|
406
|
+
# is called at serialization time to determine the default (optional) value
|
367
407
|
# @yieldparam [Symbol,String] id property id
|
368
408
|
# @yieldparam [Object] obj object instance
|
369
409
|
# @yieldparam [Object] val optional property value to set in case of setter request
|
370
410
|
# @return [void]
|
371
411
|
def property(*props, **kwargs, &block)
|
372
412
|
forced = !!kwargs.delete(:force)
|
413
|
+
optional = kwargs.has_key?(:optional) ? kwargs.delete(:optional) : false
|
373
414
|
if block || kwargs[:handler]
|
374
415
|
props.each do |prop|
|
375
|
-
serializer_properties << Property.new(self, prop, force: forced, handler: kwargs[:handler], &block)
|
416
|
+
serializer_properties << Property.new(self, prop, force: forced, handler: kwargs[:handler], optional: optional, &block)
|
376
417
|
end
|
377
418
|
else
|
378
419
|
props.flatten.each do |prop|
|
379
420
|
if ::Hash === prop
|
380
421
|
prop.each_pair do |pn, pp|
|
381
|
-
serializer_properties << Property.new(self, pn, pp, force: forced)
|
422
|
+
serializer_properties << Property.new(self, pn, pp, force: forced, optional: optional)
|
382
423
|
end
|
383
424
|
else
|
384
|
-
serializer_properties << Property.new(self, prop, force: forced)
|
425
|
+
serializer_properties << Property.new(self, prop, force: forced, optional: optional)
|
385
426
|
end
|
386
427
|
end
|
387
428
|
unless kwargs.empty?
|
388
429
|
kwargs.each_pair do |pn, pp|
|
389
|
-
serializer_properties << Property.new(self, pn, pp, force: forced)
|
430
|
+
serializer_properties << Property.new(self, pn, pp, force: forced, optional: optional)
|
390
431
|
end
|
391
432
|
end
|
392
433
|
end
|
data/lib/firm/version.rb
CHANGED
data/tests/serializer_tests.rb
CHANGED
@@ -691,6 +691,69 @@ module SerializerTestMixin
|
|
691
691
|
|
692
692
|
end
|
693
693
|
|
694
|
+
class SomeClass
|
695
|
+
|
696
|
+
include FIRM::Serializable
|
697
|
+
|
698
|
+
property :always
|
699
|
+
property :not_when_nil, optional: true # alternatively you can use `optional: nil`
|
700
|
+
property :not_when_some_value, optional: -1
|
701
|
+
property :not_when_some_derived_value, optional: ->(_obj, _id) { :derived_value }
|
702
|
+
|
703
|
+
def initialize(always, not_when_nil = nil, not_when_some_value = -1, not_when_some_derived_value = :derived_value)
|
704
|
+
@always = always
|
705
|
+
@not_when_nil = not_when_nil
|
706
|
+
@not_when_some_value = not_when_some_value
|
707
|
+
@not_when_some_derived_value = not_when_some_derived_value
|
708
|
+
end
|
709
|
+
|
710
|
+
attr_accessor :always, :not_when_nil, :not_when_some_value, :not_when_some_derived_value
|
711
|
+
|
712
|
+
class << self
|
713
|
+
|
714
|
+
def deserialize_initializer
|
715
|
+
@init ||= ->(obj, _data) { obj }
|
716
|
+
end
|
717
|
+
|
718
|
+
def deserialize_initializer=(init)
|
719
|
+
@init = init
|
720
|
+
end
|
721
|
+
|
722
|
+
end
|
723
|
+
|
724
|
+
|
725
|
+
def init_from_serialized(_data)
|
726
|
+
initialize(nil) # first call the initialize method
|
727
|
+
self.class.deserialize_initializer.call(self, _data)
|
728
|
+
self
|
729
|
+
end
|
730
|
+
protected :init_from_serialized
|
731
|
+
|
732
|
+
end
|
733
|
+
|
734
|
+
def test_optional
|
735
|
+
SomeClass.deserialize_initializer = ->(obj, _data) {
|
736
|
+
obj.always = 'NOT'
|
737
|
+
obj.not_when_nil = :not_nil
|
738
|
+
obj.not_when_some_value = 10
|
739
|
+
obj.not_when_some_derived_value = nil
|
740
|
+
}
|
741
|
+
|
742
|
+
obj_serial = SomeClass.new('Always').serialize
|
743
|
+
obj_new = assert_nothing_raised { SomeClass.deserialize(obj_serial) }
|
744
|
+
assert_equal('Always', obj_new.always) # always (de-)serialized, so overwrites custom init on deserialize
|
745
|
+
assert_equal(:not_nil, obj_new.not_when_nil) # not serialized since nil, so leaves custom init
|
746
|
+
assert_equal(10, obj_new.not_when_some_value) # not serialized since default value, so leaves custom init
|
747
|
+
assert_nil(obj_new.not_when_some_derived_value) # not serialized since default value, so leaves custom init
|
748
|
+
|
749
|
+
obj_serial = SomeClass.new('Always', :ok, 99, :specific_value).serialize
|
750
|
+
obj_new = assert_nothing_raised { SomeClass.deserialize(obj_serial) }
|
751
|
+
assert_equal('Always', obj_new.always) # always (de-)serialized, so overwrites custom init on deserialize
|
752
|
+
assert_equal(:ok, obj_new.not_when_nil) # serialized since not default, so overwrites custom init
|
753
|
+
assert_equal(99, obj_new.not_when_some_value) # serialized since not default, so overwrites custom init
|
754
|
+
assert_equal(:specific_value, obj_new.not_when_some_derived_value) # serialized since not default, so overwrites custom init
|
755
|
+
end
|
756
|
+
|
694
757
|
class House
|
695
758
|
|
696
759
|
include FIRM::Serializable
|
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: 1.
|
4
|
+
version: 1.1.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-
|
11
|
+
date: 2024-11-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -103,7 +103,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
103
103
|
- !ruby/object:Gem::Version
|
104
104
|
version: '0'
|
105
105
|
requirements: []
|
106
|
-
rubygems_version: 3.5.
|
106
|
+
rubygems_version: 3.5.22
|
107
107
|
signing_key:
|
108
108
|
specification_version: 4
|
109
109
|
summary: Format independent Ruby object marshalling
|