safestruct 0.1.0 → 0.2.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/README.md +183 -2
- data/lib/safestruct.rb +9 -10
- data/lib/safestruct/safe_array.rb +3 -0
- data/lib/safestruct/safe_hash.rb +4 -2
- data/lib/safestruct/safe_struct.rb +11 -4
- data/lib/safestruct/version.rb +1 -1
- data/test/test_array.rb +2 -0
- data/test/test_hash.rb +8 -6
- data/test/test_struct.rb +2 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 74b37cac746195871cc30eec8b8b54622958c6a5
|
4
|
+
data.tar.gz: 80def63037cd23ce8812529a4f208e1ebfbbab7a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 508027ee5576e342154bc62d6d5b5e25cf2f829a075a7b942fc9a3af2bb2295f6e6d71eec58597b7de5c69106ecde9ee6fb1f8d0c503215ab7f15735afa01f62
|
7
|
+
data.tar.gz: a9172d32b881902e273bc54cb4e41875edb0c2dd40efd62ca19a07170e8cf01596457b31fa1c63fe244096c3a69b2ab9a280c93241656abedf99f1fd5eeb4c60
|
data/README.md
CHANGED
@@ -6,16 +6,197 @@ safestruct gem / library - safe data structures (array, hash, struct) - say good
|
|
6
6
|
* bugs :: [github.com/s6ruby/safestruct/issues](https://github.com/s6ruby/safestruct/issues)
|
7
7
|
* gem :: [rubygems.org/gems/safestruct](https://rubygems.org/gems/safestruct)
|
8
8
|
* rdoc :: [rubydoc.info/gems/safestruct](http://rubydoc.info/gems/safestruct)
|
9
|
-
* forum :: [wwwmake](http://groups.google.com/group/wwwmake)
|
10
9
|
|
11
10
|
|
12
11
|
## Usage
|
13
12
|
|
14
|
-
|
13
|
+
[Safe Struct](#safe-struct) •
|
14
|
+
[Safe Array](#safe-array) •
|
15
|
+
[Safe Hash](#safe-hash)
|
16
|
+
|
17
|
+
|
18
|
+
### Null / Nil - The Billion Dollar Mistake ++ Zero - The Billion Dollar Fix
|
19
|
+
|
20
|
+
> I call it my billion-dollar mistake. It was the invention of the null reference in 1965.
|
21
|
+
> At that time, I was designing the first comprehensive type system for references
|
22
|
+
> in an object oriented language (ALGOL W).
|
23
|
+
> My goal was to ensure that all use of references should be absolutely safe,
|
24
|
+
> with checking performed automatically by the compiler.
|
25
|
+
> But I couldn't resist the temptation to put in a null reference,
|
26
|
+
> simply because it was so easy to implement.
|
27
|
+
> This has led to innumerable errors, vulnerabilities, and system crashes,
|
28
|
+
> which have probably caused a billion dollars of pain and damage in the last forty years.
|
29
|
+
>
|
30
|
+
> -- [Sir Tony Hoare](https://en.wikipedia.org/wiki/Tony_Hoare)
|
31
|
+
|
32
|
+
|
33
|
+
Let's make the code safer and
|
34
|
+
let's say goodbye to null / nil (and maybe).
|
35
|
+
How can the code work without nil?
|
36
|
+
|
37
|
+
|
38
|
+
Let's say hello to zero.
|
39
|
+
The new rule for NO null/nil ever (again) is:
|
40
|
+
|
41
|
+
**All variables - including structs, arrays and hash mappings -
|
42
|
+
MUST ALWAYS get set (initialized) to ZERO (default) values.**
|
43
|
+
|
44
|
+
What's zero?
|
45
|
+
|
46
|
+
| Type | Value |
|
47
|
+
|----------------|----------------------------|
|
48
|
+
| Integer | `0` or `Integer.zero` |
|
49
|
+
| Bool | `false` or `Bool.zero` |
|
50
|
+
| String | `''` or `String.zero` |
|
51
|
+
| Array | `[]` or `Array.zero` |
|
52
|
+
| Hash | `{}` or `Hash.zero` |
|
53
|
+
| Vote (Struct) | `Vote.new( 0, false, 0, '0x0000')` or `Vote.zero` |
|
54
|
+
| Address | `0x0000` or `Address.zero` or `Address(0)` |
|
55
|
+
| ... | |
|
56
|
+
|
57
|
+
|
58
|
+
### Safe Struct
|
59
|
+
|
60
|
+
Let's you define (auto-build) new struct classes.
|
61
|
+
Example:
|
62
|
+
|
63
|
+
``` ruby
|
64
|
+
Voter = SafeStruct.new( weight: 0, voted: false, vote: 0, delegate: '0x0000' )
|
65
|
+
|
66
|
+
voter1 = Voter.new # or Voter.new_zero
|
67
|
+
voter1.weight #=> 0
|
68
|
+
voter1.voted? #=> false
|
69
|
+
voter1.vote #=> 0
|
70
|
+
voter1.delegate #=> '0x0000'
|
71
|
+
voter1.frozen? #=> false
|
72
|
+
|
73
|
+
voter1 == Voter.zero #=> true
|
74
|
+
|
75
|
+
voter1.delegate = '0x1111'
|
76
|
+
voter1 == Voter.zero #=> false
|
77
|
+
|
78
|
+
voter2 = Voter.new( 0, false, 0, '0x0000')
|
79
|
+
|
80
|
+
voter2.voted = true
|
81
|
+
voter2.delegate = '0x2222'
|
82
|
+
voter2 == Voter.zero #=> false
|
83
|
+
|
84
|
+
Voter.zero.frozen? #=> true
|
85
|
+
|
86
|
+
voter3 = Voter.new( 0 ) #=> ArgumentError - wrong number of arguments
|
87
|
+
# for Voter.new - 1 for 4
|
88
|
+
```
|
89
|
+
|
90
|
+
|
91
|
+
Note: You can use `Struct` as an alias for `SafeStruct`
|
92
|
+
(in the `Safe` namespace / module context).
|
93
|
+
|
94
|
+
|
95
|
+
### Safe Array
|
96
|
+
|
97
|
+
Let's you define (auto-build) new (type safe) array classes.
|
98
|
+
Example:
|
99
|
+
|
100
|
+
``` ruby
|
101
|
+
ArrayInteger = SafeArray.build_class( Integer )
|
102
|
+
ary = ArrayInteger.new
|
103
|
+
ary.size #=> 0
|
104
|
+
ary[0] #=> IndexError
|
105
|
+
ary.size = 2 #=> [0,0]
|
106
|
+
ary[0] #=> 0
|
107
|
+
```
|
108
|
+
|
109
|
+
or use the `Array.of` convenience shortcut:
|
110
|
+
|
111
|
+
``` ruby
|
112
|
+
ary = Array.of( Integer )
|
113
|
+
ary.size #=> 0
|
114
|
+
ary[0] #=> IndexError
|
115
|
+
ary.size = 2 #=> [0, 0]
|
116
|
+
ary[0] #=> 0
|
117
|
+
|
118
|
+
## or
|
119
|
+
|
120
|
+
another_ary = Array.of( Bool )
|
121
|
+
another_ary.size #=> 0
|
122
|
+
another_ary[0] #=> IndexError
|
123
|
+
another_ary.size = 2 #=> [false, false]
|
124
|
+
another_ary[0] #=> false
|
125
|
+
|
126
|
+
## or
|
127
|
+
|
128
|
+
another_ary = Array.of( Bool, 2 )
|
129
|
+
another_ary.size #=> 2
|
130
|
+
another_ary[0] #=> false
|
131
|
+
```
|
132
|
+
|
133
|
+
Yes, Safe Array works with structs (or nested arrays or hash mappings) too. Example:
|
134
|
+
|
135
|
+
``` ruby
|
136
|
+
ary = Array.of( Vote )
|
137
|
+
|
138
|
+
ary[0] #=> IndexError
|
139
|
+
ary.size = 2 #=> [#<Vote @weight=0, @voted=false, @vote=0, @delegate='0x0000'>,
|
140
|
+
# #<Vote @weight=0, @voted=false, @vote=0, @delegate='0x0000'>]
|
141
|
+
ary[0] #=> #<Vote @weight=0, @voted=false, @vote=0, @delegate='0x0000'>
|
142
|
+
ary[0].voted? #=> false
|
143
|
+
```
|
144
|
+
|
145
|
+
|
146
|
+
### Safe Hash
|
147
|
+
|
148
|
+
Let's you define (auto-build) new (type safe) hash classes.
|
149
|
+
Example:
|
150
|
+
|
151
|
+
``` ruby
|
152
|
+
Hash_X_Integer = SafeHash.build_class( String, Integer )
|
153
|
+
hash = Hash_X_Integer.new
|
154
|
+
|
155
|
+
hash['0x0000'] #=> 0
|
156
|
+
```
|
157
|
+
|
158
|
+
or use the `Hash.of` convenience shortcut:
|
159
|
+
|
160
|
+
``` ruby
|
161
|
+
hash = Hash.of( String => Integer )
|
162
|
+
|
163
|
+
hash['0x0000'] #=> 0
|
164
|
+
hash['0x0000'] += 42
|
165
|
+
hash['0x0000'] #=> 42
|
166
|
+
```
|
167
|
+
|
168
|
+
Note: Safe Hash will ALWAYS return a value.
|
169
|
+
If the key is missing in the hash mapping on lookup,
|
170
|
+
the key gets auto-added with a zero value.
|
171
|
+
Use `has_key?` or `key?` to check if a key is present.
|
172
|
+
|
173
|
+
|
174
|
+
Yes, Safe Hash works with structs (or arrays or nested hash mappings) too. Example:
|
175
|
+
|
176
|
+
``` ruby
|
177
|
+
hash = Hash.of( String => Vote )
|
178
|
+
|
179
|
+
hash['0x0000'] #=> #<Vote @weight=0, @voted=false, @vote=0, @delegate='0x0000'>
|
180
|
+
hash['0x0000'].voted? #=> false
|
181
|
+
hash['0x0000'].voted = true
|
182
|
+
hash['0x0000'].voted? #=> true
|
183
|
+
```
|
184
|
+
|
185
|
+
|
186
|
+
|
187
|
+
|
188
|
+
## More "Real World" Safe Data Structures (Array, Hash, Struct) Samples
|
189
|
+
|
190
|
+
- [The "Red Paper" about sruby](https://github.com/s6ruby/redpaper) - Small, Smart, Secure, Safe, Solid & Sound (S6) Ruby - The Ruby Programming Language for Contract / Transaction Scripts on the Blockchain World Computer - Yes, It's Just Ruby
|
191
|
+
- [Programming Crypto Blockchain Contracts Step-by-Step Book / Guide](https://github.com/s6ruby/programming-cryptocontracts). Let's Start with Ponzi & Pyramid Schemes. Run Your Own Lotteries, Gambling Casinos and more on the Blockchain World Computer...
|
192
|
+
- [Ruby Sample Contracts for the Universum Blockchain/World Computer Runtime](https://github.com/s6ruby/universum-contracts)
|
193
|
+
|
15
194
|
|
16
195
|
|
17
196
|
## License
|
18
197
|
|
198
|
+

|
199
|
+
|
19
200
|
The `safestruct` scripts are dedicated to the public domain.
|
20
201
|
Use it as you please with no restrictions whatsoever.
|
21
202
|
|
data/lib/safestruct.rb
CHANGED
@@ -28,7 +28,7 @@ end
|
|
28
28
|
#####
|
29
29
|
# add "beautiful" convenience helpers
|
30
30
|
|
31
|
-
class
|
31
|
+
class Hash
|
32
32
|
|
33
33
|
def self.of( *args )
|
34
34
|
## e.g. gets passed in [{Address=>Integer}]
|
@@ -38,7 +38,7 @@ class Mapping
|
|
38
38
|
arg = args[0].to_a ## convert to array (for easier access)
|
39
39
|
klass_key = arg[0][0]
|
40
40
|
klass_value = arg[0][1]
|
41
|
-
klass = SafeHash.build_class( klass_key, klass_value )
|
41
|
+
klass = Safe::SafeHash.build_class( klass_key, klass_value )
|
42
42
|
klass.new
|
43
43
|
else
|
44
44
|
## todo/fix: throw argument error/exception
|
@@ -52,17 +52,16 @@ class Array
|
|
52
52
|
## "typed" safe array "constructor"
|
53
53
|
## e.g. Array.of( Address ) or Array.of( Money ) or Array.of( Proposal, size: 2 ) etc.
|
54
54
|
def self.of( klass_value )
|
55
|
-
klass = SafeArray.build_class( klass_value )
|
55
|
+
klass = Safe::SafeArray.build_class( klass_value )
|
56
56
|
klass.new ## todo: add klass.new( **kwargs ) for size: 2 etc.
|
57
57
|
end
|
58
58
|
end
|
59
59
|
|
60
|
-
|
61
|
-
############################
|
62
|
-
# note: HACK redefine built in struct
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
60
|
+
module Safe
|
61
|
+
############################
|
62
|
+
# note: HACK redefine built in struct in module Safe "context"
|
63
|
+
ClassicStruct = ::Struct ## save old classic struct class
|
64
|
+
Struct = SafeStruct
|
65
|
+
end
|
67
66
|
|
68
67
|
puts Safe.banner ## say hello
|
data/lib/safestruct/safe_hash.rb
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
+
module Safe
|
3
4
|
|
4
5
|
class SafeHash
|
5
6
|
|
6
7
|
## e.g.
|
7
|
-
##
|
8
|
+
## Hash.of( Address => Money )
|
8
9
|
|
9
|
-
## note: need to create new class!! for every
|
10
|
+
## note: need to create new class!! for every safe hash
|
10
11
|
## make klass_key class and
|
11
12
|
## klass_value class into class instance variables
|
12
13
|
## that can get used by zero
|
@@ -75,3 +76,4 @@ RUBY
|
|
75
76
|
def size() @h.size; end
|
76
77
|
def length() size; end
|
77
78
|
end # class SafeHash
|
79
|
+
end # module Safe
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
+
module Safe
|
4
|
+
|
3
5
|
class SafeStruct
|
4
6
|
|
5
7
|
def self.build_class( **attributes )
|
@@ -36,11 +38,15 @@ def self.build_class( **attributes )
|
|
36
38
|
|
37
39
|
## add self.new too - note: call/forward to "old" orginal self.new of Event (base) class
|
38
40
|
klass.define_singleton_method( :new ) do |*args|
|
39
|
-
if args.
|
40
|
-
|
41
|
-
|
41
|
+
if args.empty? ## no args - use new_zero and set (initialize) all ivars to zero
|
42
|
+
new_zero
|
43
|
+
else
|
44
|
+
if args.size != attributes.size
|
45
|
+
## check for required args/params - all MUST be passed in!!!
|
46
|
+
raise ArgumentError.new( "[SafeStruct] wrong number of arguments for #{name}.new - #{args.size} for #{attributes.size}" )
|
47
|
+
end
|
48
|
+
old_new( *args )
|
42
49
|
end
|
43
|
-
old_new( *args )
|
44
50
|
end
|
45
51
|
|
46
52
|
klass.define_singleton_method( :new_zero ) do
|
@@ -77,3 +83,4 @@ def self.zero
|
|
77
83
|
@zero ||= new_zero.freeze
|
78
84
|
end
|
79
85
|
end # class SafeStruct
|
86
|
+
end # module Safe
|
data/lib/safestruct/version.rb
CHANGED
data/test/test_array.rb
CHANGED
data/test/test_hash.rb
CHANGED
@@ -10,6 +10,8 @@ require 'helper'
|
|
10
10
|
|
11
11
|
class TestHash < MiniTest::Test
|
12
12
|
|
13
|
+
include Safe
|
14
|
+
|
13
15
|
## sig: [Integer, Bool, Integer, Address]
|
14
16
|
Voter = SafeStruct.new( weight: 0, voted: false, vote: 0, delegate: '0x0000' )
|
15
17
|
|
@@ -30,8 +32,8 @@ def test_integer
|
|
30
32
|
assert_equal 101, h['0x1111']
|
31
33
|
assert_equal 102, h['0x2222']
|
32
34
|
|
33
|
-
## check
|
34
|
-
assert_equal Hash_X_Integer,
|
35
|
+
## check Hash.of (uses cached classes)
|
36
|
+
assert_equal Hash_X_Integer, Hash.of( String => Integer ).class
|
35
37
|
end
|
36
38
|
|
37
39
|
|
@@ -48,8 +50,8 @@ def test_bool
|
|
48
50
|
assert_equal true, h['0x1111']
|
49
51
|
assert_equal true, h['0x2222']
|
50
52
|
|
51
|
-
## check
|
52
|
-
assert_equal Hash_X_Bool,
|
53
|
+
## check Hash.of (uses cached classes)
|
54
|
+
assert_equal Hash_X_Bool, Hash.of( String => Bool ).class
|
53
55
|
end
|
54
56
|
|
55
57
|
|
@@ -69,8 +71,8 @@ def test_voter
|
|
69
71
|
pp h['0x1111']
|
70
72
|
pp h['0x2222']
|
71
73
|
|
72
|
-
## check
|
73
|
-
assert_equal Hash_X_Voter,
|
74
|
+
## check Hash.of (uses cached classes)
|
75
|
+
assert_equal Hash_X_Voter, Hash.of( String => Voter ).class
|
74
76
|
end
|
75
77
|
|
76
78
|
|
data/test/test_struct.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: safestruct
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gerald Bauer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-02-
|
11
|
+
date: 2019-02-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rdoc
|