safestruct 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 +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
|
+
![](https://publicdomainworks.github.io/buttons/zero88x31.png)
|
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
|