safe_and_sound 0.2.0 → 0.3.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/CHANGELOG.md +10 -0
- data/README.md +18 -0
- data/lib/safe_and_sound/json.rb +30 -8
- data/lib/safe_and_sound/variant.rb +38 -2
- data/lib/safe_and_sound/version.rb +1 -1
- metadata +21 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9890b2b829aa88cdea2c36e78a032c747d148acf3f6e48c2fc3146db811309b5
|
4
|
+
data.tar.gz: 92316a4c52f9cb868615e219172cdc04392e12af889353612e11eb6aef9a4ed9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e41b58b277085199a6fa2b70bedae6323cfaae551f56198da26ef6d004c0f63a2109ae4589f002eb139464da5e6ffcb6e8738608720ea4f00379bc96f30ebb41
|
7
|
+
data.tar.gz: f38e036748e434f4fca480c973b26b1eb8dbb385df483456ef78f89fa88f2f2a74117bfd20f62beb8d0a0fc7d608c8f93f9bbb99ad1c7a61e34264f08d9c0a3b
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 0.0.3 (21-Apr-22)
|
4
|
+
|
5
|
+
* Add serialization support for array fields
|
6
|
+
* Add ability to declare Array types
|
7
|
+
* Allow to omit 'type' specifier in JSON when type has only one variant
|
8
|
+
* Test and fix deserialization
|
9
|
+
* Update minimum Ruby version to 3
|
10
|
+
* Make sure JSON serialization works on nested types
|
11
|
+
* Add deserialization for nested types
|
12
|
+
|
3
13
|
## 0.0.2 (17-Apr-22)
|
4
14
|
|
5
15
|
* added `#to_json` and `#from_json` methods for serialization
|
data/README.md
CHANGED
@@ -32,6 +32,8 @@ bike = Vehicle.bike(gears: 'twentyone')
|
|
32
32
|
|
33
33
|
To add polymorphic behavior we can write functions __without__ having to touch the new types themselves.
|
34
34
|
|
35
|
+
## Safe, polymorphic functions
|
36
|
+
|
35
37
|
By including the `SafeAndSound::Functions` module we get access to the `chase` function.
|
36
38
|
It immitates the `case` statement but uses the knowledge about our types to make it more safe.
|
37
39
|
|
@@ -60,4 +62,20 @@ of making a syntax for it look like the syntax in languages where this concept i
|
|
60
62
|
|
61
63
|
Check out more examples in the [examples folder](examples).
|
62
64
|
|
65
|
+
## JSON serialization/deserialization included
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
irb(main)> car = Vehicle.Car(horsepower: 100)
|
69
|
+
|
70
|
+
irb(main)> car.as_json # converts to a Hash of primitives
|
71
|
+
=> {"type"=>"Car", "horsepower"=>100}
|
63
72
|
|
73
|
+
irb(main)> puts car.to_json # converts to actual JSON string
|
74
|
+
{"type":"Car","horsepower":100}
|
75
|
+
|
76
|
+
irb(main)> Vehicle.from_hash({"type"=>"Car", "horsepower"=>100})
|
77
|
+
=> #<Vehicle::Car:0x000000010ef49a48 @horsepower=100>
|
78
|
+
|
79
|
+
irb(main)> Vehicle.from_json('{"type":"Car","horsepower":100}')
|
80
|
+
=> #<Vehicle::Car:0x000000010ef6ae00 @horsepower=100>
|
81
|
+
```
|
data/lib/safe_and_sound/json.rb
CHANGED
@@ -1,11 +1,27 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'json'
|
4
|
+
|
3
5
|
module SafeAndSound
|
4
6
|
module ToJson
|
5
7
|
def as_json
|
6
8
|
hash = { 'type' => self.class.variant_name.to_s }
|
7
9
|
self.class.fields.each do |field_name, _|
|
8
|
-
|
10
|
+
value = send(field_name)
|
11
|
+
hash[field_name.to_s] =
|
12
|
+
if value.is_a?(Array)
|
13
|
+
value.map do |item|
|
14
|
+
if item.respond_to?(:as_json)
|
15
|
+
item.as_json
|
16
|
+
else
|
17
|
+
item
|
18
|
+
end
|
19
|
+
end
|
20
|
+
elsif value.respond_to?(:as_json)
|
21
|
+
value.as_json
|
22
|
+
else
|
23
|
+
value
|
24
|
+
end
|
9
25
|
end
|
10
26
|
hash
|
11
27
|
end
|
@@ -17,13 +33,19 @@ module SafeAndSound
|
|
17
33
|
|
18
34
|
module FromJson
|
19
35
|
def from_json(json_string)
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
36
|
+
from_hash(JSON.parse(json_string))
|
37
|
+
end
|
38
|
+
|
39
|
+
def from_hash(hash_)
|
40
|
+
hash = hash_.transform_keys(&:to_sym)
|
41
|
+
variant_name = hash[:type].to_sym if hash[:type]
|
42
|
+
variant =
|
43
|
+
if variant_name.nil? && variants.length == 1
|
44
|
+
variants.first
|
45
|
+
else
|
46
|
+
variants.find { |v| v.variant_name == variant_name }
|
47
|
+
end
|
48
|
+
raise ArgumentError, "Unable to find #{variant_name} for #{name}" unless variant
|
27
49
|
|
28
50
|
variant.new(**hash.except(:type))
|
29
51
|
end
|
@@ -41,11 +41,47 @@ module SafeAndSound
|
|
41
41
|
"#{self.class.name} does not have a constructor argument #{field_name}"
|
42
42
|
end
|
43
43
|
|
44
|
-
|
44
|
+
if field_type_matches?(field_type, field_name, value)
|
45
|
+
instance_variable_set("@#{field_name}", value)
|
46
|
+
else
|
47
|
+
instance_variable_set("@#{field_name}", try_to_initialize_from_hash(field_type, field_name, value))
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def field_type_matches?(field_type, field_name, value)
|
52
|
+
return value.is_a?(field_type) unless field_type.is_a?(Array)
|
53
|
+
|
54
|
+
expected_item_type = field_type.first
|
55
|
+
unless value.is_a?(Array)
|
56
|
+
raise WrgonConstructorArgType,
|
57
|
+
"#{field_name} must be an Array of #{expected_item_type} but was #{value.class}"
|
58
|
+
end
|
59
|
+
|
60
|
+
mismatched_types =
|
61
|
+
value.map do |item|
|
62
|
+
if item.is_a?(expected_item_type)
|
63
|
+
nil
|
64
|
+
else
|
65
|
+
item.class
|
66
|
+
end
|
67
|
+
end.compact
|
68
|
+
mismatched_types.empty?
|
69
|
+
end
|
70
|
+
|
71
|
+
def try_to_initialize_from_hash(field_type, field_name, value)
|
72
|
+
if field_type.is_a?(Array)
|
73
|
+
item_type = field_type.first
|
74
|
+
return value.map { |item_hash| item_type.from_hash(item_hash) } if item_type.superclass == Type
|
75
|
+
|
76
|
+
raise WrgonConstructorArgType,
|
77
|
+
"#{field_name} must be array of type #{item_type}"
|
78
|
+
|
79
|
+
end
|
80
|
+
unless field_type.superclass == Type && value.is_a?(Hash)
|
45
81
|
raise WrgonConstructorArgType,
|
46
82
|
"#{field_name} must be of type #{field_type} but was #{value.class.name}"
|
47
83
|
end
|
48
|
-
|
84
|
+
field_type.from_hash(value)
|
49
85
|
end
|
50
86
|
end
|
51
87
|
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: safe_and_sound
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Axel Tetzlaff
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-04-
|
11
|
+
date: 2022-04-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: debug
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.0.0
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.0.0
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: minitest
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -89,7 +103,7 @@ licenses:
|
|
89
103
|
- MIT
|
90
104
|
metadata:
|
91
105
|
rubygems_mfa_required: 'true'
|
92
|
-
post_install_message:
|
106
|
+
post_install_message:
|
93
107
|
rdoc_options: []
|
94
108
|
require_paths:
|
95
109
|
- lib
|
@@ -97,15 +111,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
97
111
|
requirements:
|
98
112
|
- - ">="
|
99
113
|
- !ruby/object:Gem::Version
|
100
|
-
version:
|
114
|
+
version: 3.1.1
|
101
115
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
102
116
|
requirements:
|
103
117
|
- - ">="
|
104
118
|
- !ruby/object:Gem::Version
|
105
119
|
version: '0'
|
106
120
|
requirements: []
|
107
|
-
rubygems_version: 3.
|
108
|
-
signing_key:
|
121
|
+
rubygems_version: 3.3.7
|
122
|
+
signing_key:
|
109
123
|
specification_version: 4
|
110
124
|
summary: Sum Data Types
|
111
125
|
test_files: []
|