solidity-typed 0.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 +7 -0
- data/CHANGELOG.md +4 -0
- data/Manifest.txt +24 -0
- data/README.md +318 -0
- data/Rakefile +27 -0
- data/lib/solidity/typed/array.rb +166 -0
- data/lib/solidity/typed/array_builder.rb +53 -0
- data/lib/solidity/typed/bool.rb +47 -0
- data/lib/solidity/typed/conversion.rb +52 -0
- data/lib/solidity/typed/enum.rb +116 -0
- data/lib/solidity/typed/enum_builder.rb +101 -0
- data/lib/solidity/typed/mapping.rb +108 -0
- data/lib/solidity/typed/mapping_builder.rb +54 -0
- data/lib/solidity/typed/metatypes/array.rb +56 -0
- data/lib/solidity/typed/metatypes/bool.rb +39 -0
- data/lib/solidity/typed/metatypes/literals.rb +186 -0
- data/lib/solidity/typed/metatypes/mapping.rb +46 -0
- data/lib/solidity/typed/metatypes/types.rb +492 -0
- data/lib/solidity/typed/numbers.rb +108 -0
- data/lib/solidity/typed/struct.rb +73 -0
- data/lib/solidity/typed/struct_builder.rb +145 -0
- data/lib/solidity/typed/typed.rb +114 -0
- data/lib/solidity/typed/values.rb +113 -0
- data/lib/solidity/typed/version.rb +23 -0
- data/lib/solidity/typed.rb +128 -0
- metadata +111 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 634cc8c8ee7cf10c3a67c0a8dbcc7c5efad7fe17a5a961128a5140f7648a8ff0
|
4
|
+
data.tar.gz: f6956a85d7b3e7ae5aa021052ddcc9981f0c5f394acb29f0457e2587a3cc8e56
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9537d4935ea267c3ace17ac3625552fab903dee944718e9163392167c39cdcff478f3494c9780e09db327c6228458dac5d33e3738fddd3fa08463322d9fff99e
|
7
|
+
data.tar.gz: 6210074c6e54f778fca18fbb738f8a884421702ceede73d73e4b5efd32ccce35ea2b30fd7507ed76f0d820753723c735bdd0f3af58ff252b480dfed2c9cf5833
|
data/CHANGELOG.md
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
CHANGELOG.md
|
2
|
+
Manifest.txt
|
3
|
+
README.md
|
4
|
+
Rakefile
|
5
|
+
lib/solidity/typed.rb
|
6
|
+
lib/solidity/typed/array.rb
|
7
|
+
lib/solidity/typed/array_builder.rb
|
8
|
+
lib/solidity/typed/bool.rb
|
9
|
+
lib/solidity/typed/conversion.rb
|
10
|
+
lib/solidity/typed/enum.rb
|
11
|
+
lib/solidity/typed/enum_builder.rb
|
12
|
+
lib/solidity/typed/mapping.rb
|
13
|
+
lib/solidity/typed/mapping_builder.rb
|
14
|
+
lib/solidity/typed/metatypes/array.rb
|
15
|
+
lib/solidity/typed/metatypes/bool.rb
|
16
|
+
lib/solidity/typed/metatypes/literals.rb
|
17
|
+
lib/solidity/typed/metatypes/mapping.rb
|
18
|
+
lib/solidity/typed/metatypes/types.rb
|
19
|
+
lib/solidity/typed/numbers.rb
|
20
|
+
lib/solidity/typed/struct.rb
|
21
|
+
lib/solidity/typed/struct_builder.rb
|
22
|
+
lib/solidity/typed/typed.rb
|
23
|
+
lib/solidity/typed/values.rb
|
24
|
+
lib/solidity/typed/version.rb
|
data/README.md
ADDED
@@ -0,0 +1,318 @@
|
|
1
|
+
# Solidity Typed
|
2
|
+
|
3
|
+
solidity-typed - "zero-dependency" 100%-solidity compatible data type and application binary interface (abi) machinery incl. bool, (frozen) string, address, bytes, uint, int, enum, struct, array, mapping, event, and more for solidity-inspired contract (blockchain) programming languages incl. rubidity et al
|
4
|
+
|
5
|
+
|
6
|
+
|
7
|
+
|
8
|
+
* home :: [github.com/s6ruby/rubidity](https://github.com/s6ruby/rubidity)
|
9
|
+
* bugs :: [github.com/s6ruby/rubidity/issues](https://github.com/s6ruby/rubidity/issues)
|
10
|
+
* gem :: [rubygems.org/gems/solidity-typed](https://rubygems.org/gems/solidity-typed)
|
11
|
+
* rdoc :: [rubydoc.info/gems/solidity-typed](http://rubydoc.info/gems/solidity-typed)
|
12
|
+
|
13
|
+
|
14
|
+
## What's Solidity?! What's Rubidity?!
|
15
|
+
|
16
|
+
See [**Solidity - Contract Application Binary Interface (ABI) Specification** »](https://docs.soliditylang.org/en/latest/abi-spec.html)
|
17
|
+
|
18
|
+
See [**Rubidity - Ruby for Layer 1 (L1) Contracts / Protocols with "Off-Chain" Indexer** »](https://github.com/s6ruby/rubidity)
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
|
23
|
+
## Data Types
|
24
|
+
|
25
|
+
|
26
|
+
|
27
|
+
### Available Value & Reference Types
|
28
|
+
|
29
|
+
Value Types
|
30
|
+
|
31
|
+
* `String`: Text-based data (in utf8 encoding). Note: Solditity (-typed) strings are immutable (frozen).
|
32
|
+
* `Address`: User or contract (blockchain) address (in hexadecimal) - 20 bytes (40 hexchars).
|
33
|
+
* `InscriptionId`: Unique identifiers for inscriptions (in hexadecimal) - 32 bytes (64 hexchars).
|
34
|
+
* `Bool`: Boolean values (true or false).
|
35
|
+
* `UInt`: Unsigned (natural) 256-bit integer numbers (0..2^256-1)
|
36
|
+
* `Int`: Signed (negative or positive) 256-bit integer numbers.
|
37
|
+
* `Timestamp`: Date and time (stored as unsigned 256-bit integers. Seconds since "unix epoch" starting on January 1st, 1970 at 0:00).
|
38
|
+
|
39
|
+
|
40
|
+
<!--
|
41
|
+
* `:dumbContract`: A specific type of contract ID (hexadecimal).
|
42
|
+
* `:addressOrDumbContract`: Either an Ethereum address or a specific type of contract ID.
|
43
|
+
-->
|
44
|
+
|
45
|
+
|
46
|
+
Reference Types
|
47
|
+
|
48
|
+
* `Mapping`: Key-value storage for different types.
|
49
|
+
* `Array`: Lists of other types.
|
50
|
+
|
51
|
+
|
52
|
+
### Zero (Default) Values
|
53
|
+
|
54
|
+
In Solidity, every type comes with a zero (default) value
|
55
|
+
that gets assigned when a variable is declared but not initialized.
|
56
|
+
Understanding these defaults is crucial for avoiding unintended behavior in your code.
|
57
|
+
Here is the rundown:
|
58
|
+
|
59
|
+
* **Integers (Int, UInt, Timestamp, Timedelta)**: Default to `0`.
|
60
|
+
* **Address Types (Address)**: Default to a zero-address, which is `0x0000000000000000000000000000000000000000`.
|
61
|
+
* **Inscription Identifiers (InscriptionId)**: Default to a zero-identifier, `0x0000000000000000000000000000000000000000000000000000000000000000`.
|
62
|
+
* **String (String)**: Default to an empty string `''`.
|
63
|
+
* **Boolean (Bool)**: Default to `false`.
|
64
|
+
* **Mapping (Mapping)**: Default to an empty mapping object. The key and value types are set according to your specifications.
|
65
|
+
* **Array (Array)**: Default to an empty array object. The sub type is set according to your specification.
|
66
|
+
|
67
|
+
|
68
|
+
|
69
|
+
### Literals & Type Coercion and Validation
|
70
|
+
|
71
|
+
Solidity (-Typed) employs a strong system of type validation and coercion to ensure that variables adhere to their declared types. This involves transforming literal values into the corresponding solidity types and reporting type mismatches.
|
72
|
+
|
73
|
+
Here's a brief rundown of type coercion rules:
|
74
|
+
|
75
|
+
* **Address**: Accepts hexadecimal strings that match the contract or user (blockchain) address format (`0x` followed by 40 hexadecimal characters). The address is then normalized to lowercase.
|
76
|
+
* **UInt and Int**: These types accept both integer and string representations. Strings are attempted to be coerced into integers. uint and int cannot be out of the range of their Solidity counterparts.
|
77
|
+
* **String**: Only accepts string literals. Note: strings are immutable (frozen).
|
78
|
+
* **Bool**: Accepts only `true` or `false`.
|
79
|
+
* **InscriptionId**: Accepts hexadecimal strings matching specific patterns (`0x` followed by 64 hexadecimal characters).
|
80
|
+
* **Timestamp**: Relies on `UInt` type coercion, as it's represented as an unsigned integer (32-bit) internally.
|
81
|
+
* **Mapping**: Accepts a Hash and ensures that keys and values match the specified types. Coerces these into a typed mapping object (`Mapping`).
|
82
|
+
* **Array**: Accepts an array and ensures that the values match the specified type. Coerces these into a typed array object (`Array`).
|
83
|
+
|
84
|
+
|
85
|
+
|
86
|
+
|
87
|
+
## Usage
|
88
|
+
|
89
|
+
|
90
|
+
Let's try some random use:
|
91
|
+
|
92
|
+
|
93
|
+
|
94
|
+
|
95
|
+
``` ruby
|
96
|
+
require 'solidity/typed'
|
97
|
+
|
98
|
+
|
99
|
+
module Sandbox ## note: "wrap" in sandbox (auto-)incl. (solidity) types
|
100
|
+
|
101
|
+
#####################################
|
102
|
+
# frozen (immutable ) value types
|
103
|
+
|
104
|
+
|
105
|
+
# note: (typed) strings always use utf-8 encoding AND
|
106
|
+
# are frozen/immutable!!!
|
107
|
+
a = String.new #=> <val string:"">
|
108
|
+
a = String.new( 'hello, world!' ) #=> <val string:"hello, world!">
|
109
|
+
|
110
|
+
|
111
|
+
a = UInt.new #=> <val uint:0>
|
112
|
+
a = UInt.new( 100 ) #=> <val uint:100>
|
113
|
+
a += 100 #=> <val uint:200>
|
114
|
+
a -= 100 #=> <val uint:100>
|
115
|
+
|
116
|
+
# use/add TypedNat(ural) (natural integer number) alias - why? why not?
|
117
|
+
# check if natural numbers start at 0 (or exclude 0 ????)
|
118
|
+
|
119
|
+
a = Int.new #=> <val int:0>
|
120
|
+
a = Int.new( 100 ) #=> <val int:100>
|
121
|
+
a += 100 #=> <val int:200>
|
122
|
+
a -= 100 #=> <val int:100>
|
123
|
+
|
124
|
+
#
|
125
|
+
# idea - use "plain" integer as TypedInt - why? why not?
|
126
|
+
|
127
|
+
|
128
|
+
a = false #=> <val bool:false>
|
129
|
+
a = true #=> <val bool:true>
|
130
|
+
|
131
|
+
#
|
132
|
+
# idea - use "plain" true|false as TypedBool (frozen|typed) - done
|
133
|
+
|
134
|
+
|
135
|
+
a = Address.new
|
136
|
+
#=> <val address:"0x0000000000000000000000000000000000000000">
|
137
|
+
a = Address.new( '0x'+ 'aa'*20 )
|
138
|
+
#=> <val address:"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">
|
139
|
+
|
140
|
+
|
141
|
+
a = InscriptionId.new
|
142
|
+
#=> <val inscriptionId:"0x0000000000000000000000000000000000000000000000000000000000000000">
|
143
|
+
a = InscriptionId.new( '0x'+'ab'*32 )
|
144
|
+
#=> <val inscriptionId:"0xabababababababababababababababababababababababababababababababab">
|
145
|
+
|
146
|
+
|
147
|
+
a = Bytes32.new
|
148
|
+
#=> <val bytes32:"0x0000000000000000000000000000000000000000000000000000000000000000">
|
149
|
+
a = Bytes32.new( '0x'+'ab'*32 )
|
150
|
+
#=> <val bytes32:"0xabababababababababababababababababababababababababababababababab">
|
151
|
+
|
152
|
+
a = Timestamp.new #=> <val timestamp:0>
|
153
|
+
|
154
|
+
# use/change/rename to Timestamp - why? why not?
|
155
|
+
# ALWAYS uses epoch time starting at 0 (no time zone or such)
|
156
|
+
|
157
|
+
a = Timedelta.new #=> <val timedelta:0>
|
158
|
+
|
159
|
+
|
160
|
+
#
|
161
|
+
# todo/check: is bytes a (mutabale)bytebuffer or a frozen/immutable?
|
162
|
+
a = Bytes.new #=> <val bytes:"">
|
163
|
+
|
164
|
+
|
165
|
+
###########################
|
166
|
+
# reference types
|
167
|
+
|
168
|
+
|
169
|
+
Array‹String› = Array.new( String )
|
170
|
+
Array‹String›.type #=> <type string[]>
|
171
|
+
|
172
|
+
a = Array‹String›.new #=> <ref string[]:[]>
|
173
|
+
|
174
|
+
a = Array‹String›.new( ['zero', 'one', 'two'] )
|
175
|
+
#=> <ref string[]:
|
176
|
+
# [<val string:"zero">, <val string:"one">, <val string:"two">]>
|
177
|
+
a[0] #=> <val string:"zero">
|
178
|
+
a[1] #=> <val string:"one">
|
179
|
+
a[2] #=> <val string:"two">
|
180
|
+
a.length #=> 3
|
181
|
+
a.push( 'three' )
|
182
|
+
a[3] #=> <val string:"three">
|
183
|
+
a.push( 'four' )
|
184
|
+
a[4] #=> <val string:"four">
|
185
|
+
a.length #=> 5
|
186
|
+
a.serialize #=> ["zero", "one", "two", "three", "four"]
|
187
|
+
|
188
|
+
|
189
|
+
|
190
|
+
Array‹UInt› = Array.new( UInt )
|
191
|
+
Array‹UInt›.type #=> <type uint[]>
|
192
|
+
|
193
|
+
a = Array‹UInt›.new #=> <ref uint[]:[]>
|
194
|
+
|
195
|
+
a = Array‹UInt›.new( [0,1,2] )
|
196
|
+
#=> <ref uint[]:
|
197
|
+
# [<val uint:0>, <val uint:1>, <val uint:2>]>
|
198
|
+
a[0] #=> <val uint:0>
|
199
|
+
a[1] #=> <val uint:1>
|
200
|
+
a[2] #=> <val uint:2>
|
201
|
+
a.length #=> 3
|
202
|
+
a.push( 3 )
|
203
|
+
a[3] #=> <val uint:3>
|
204
|
+
a.push( 4 )
|
205
|
+
a[4] #=> <val uint:4>
|
206
|
+
a.length #=> 5
|
207
|
+
a.serialize #=> [0, 1, 2, 3, 4]
|
208
|
+
|
209
|
+
# todo/check: add a "convenience" TypedUIntArray or TypedArray<UInt>
|
210
|
+
# use special unicode-chars for <>??
|
211
|
+
|
212
|
+
|
213
|
+
alice = '0x'+ 'aa'*20
|
214
|
+
bob = '0x'+ 'bb'*20
|
215
|
+
charlie = '0x'+ 'cc'*20
|
216
|
+
|
217
|
+
|
218
|
+
Mapping‹Address→UInt› = Mapping.new( Address, UInt )
|
219
|
+
Mapping‹Address→UInt›.type #=> <type mapping(address=>uint)>
|
220
|
+
|
221
|
+
a = Mapping‹Address→UInt›.new
|
222
|
+
#=> <ref mapping(address=>uint):{}>
|
223
|
+
|
224
|
+
a = Mapping‹Address→UInt›.new( { alice => 100,
|
225
|
+
bob => 200 },
|
226
|
+
)
|
227
|
+
#=> <ref mapping(address=>uint):
|
228
|
+
# {<val address:"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">=><val uint:100>,
|
229
|
+
# <val address:"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb">=><val uint:200>}>
|
230
|
+
|
231
|
+
a[ alice ] #=> <val uint:100>
|
232
|
+
a[ bob ] #=> <val uint:200>
|
233
|
+
a[ charlie ] #=> <val uint:0>
|
234
|
+
a[ charlie ] = 300
|
235
|
+
a[ charlie ] #=> <val uint:300>
|
236
|
+
|
237
|
+
a.serialize
|
238
|
+
#=> {"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"=>100,
|
239
|
+
# "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"=>200,
|
240
|
+
# "0xcccccccccccccccccccccccccccccccccccccccc"=>300}
|
241
|
+
|
242
|
+
|
243
|
+
#
|
244
|
+
# more - enums, structs, etc.
|
245
|
+
|
246
|
+
Color = Enum.new( :Color, :red, :green, :blue )
|
247
|
+
Color.type #=> <type Color enum(red,green,blue)>
|
248
|
+
|
249
|
+
Color::RED #=> <val Color enum(red,green,blue):red(0)>
|
250
|
+
Color.red #=> <val Color enum(red,green,blue):red(0)>
|
251
|
+
|
252
|
+
Color::GREEN #=> <val Color enum(red,green,blue):red(0)>
|
253
|
+
Color.green #=> <val Color enum(red,green,blue):red(0)>
|
254
|
+
|
255
|
+
Color.min #=> <val Color enum(red,green,blue):red(0)>
|
256
|
+
Color.max #=> <val Color enum(red,green,blue):blue(2)>
|
257
|
+
|
258
|
+
color = Color.green
|
259
|
+
color.serialize #=> 1
|
260
|
+
|
261
|
+
color = Color.red
|
262
|
+
color.serialize #=> 0
|
263
|
+
|
264
|
+
|
265
|
+
|
266
|
+
Bet = Struct.new( :Bet,
|
267
|
+
user: Address,
|
268
|
+
block: UInt,
|
269
|
+
cap: UInt,
|
270
|
+
amount: UInt )
|
271
|
+
Bet.type
|
272
|
+
|
273
|
+
|
274
|
+
bet = Bet.new
|
275
|
+
bet.user
|
276
|
+
bet.amount
|
277
|
+
|
278
|
+
bet.user = Address.new( '0x'+'aa'*20 )
|
279
|
+
bet.amount = UInt.new( 123 )
|
280
|
+
|
281
|
+
bet.user = '0x'+'bb'*20 ## literal assign (with typecheck)
|
282
|
+
bet.amount = 234 ## literal assign (with typecheck)
|
283
|
+
|
284
|
+
bet.serialize
|
285
|
+
|
286
|
+
bet = Bet.new( '0x'+'cc'*20, 0, 0, 456,
|
287
|
+
bet.serialize
|
288
|
+
|
289
|
+
# ...
|
290
|
+
|
291
|
+
end # module Sandbox
|
292
|
+
```
|
293
|
+
|
294
|
+
|
295
|
+
And so on. To be continued ...
|
296
|
+
|
297
|
+
|
298
|
+
|
299
|
+
|
300
|
+
|
301
|
+
|
302
|
+
## Bonus - More Blockchain (Crypto) Tools, Libraries & Scripts In Ruby
|
303
|
+
|
304
|
+
See [**/blockchain**](https://github.com/rubycocos/blockchain)
|
305
|
+
at the ruby code commons (rubycocos) org.
|
306
|
+
|
307
|
+
|
308
|
+
|
309
|
+
|
310
|
+
|
311
|
+
|
312
|
+
## Questions? Comments?
|
313
|
+
|
314
|
+
Join us in the [Rubidity (community) discord (chat server)](https://discord.gg/3JRnDUap6y). Yes you can.
|
315
|
+
Your questions and commetary welcome.
|
316
|
+
|
317
|
+
Or post them over at the [Help & Support](https://github.com/geraldb/help) page. Thanks.
|
318
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'hoe'
|
2
|
+
require './lib/solidity/typed/version.rb'
|
3
|
+
|
4
|
+
|
5
|
+
Hoe.spec 'solidity-typed' do
|
6
|
+
self.version = Solidity::Module::Typed::VERSION
|
7
|
+
|
8
|
+
self.summary = 'solidity-typed - "zero-dependency" 100%-solidity compatible data type and application binary interface (abi) machinery incl. bool, (frozen) string, address, bytes, uint, int, enum, struct, array, mapping, event, and more for solidity-inspired contract (blockchain) programming languages incl. rubidity et al'
|
9
|
+
self.description = summary
|
10
|
+
|
11
|
+
self.urls = { home: 'https://github.com/s6ruby/rubidity' }
|
12
|
+
|
13
|
+
self.author = 'Gerald Bauer'
|
14
|
+
self.email = 'gerald.bauer@gmail.com'
|
15
|
+
|
16
|
+
# switch extension to .markdown for gihub formatting
|
17
|
+
self.readme_file = 'README.md'
|
18
|
+
self.history_file = 'CHANGELOG.md'
|
19
|
+
|
20
|
+
self.extra_deps = []
|
21
|
+
|
22
|
+
self.licenses = ['Public Domain']
|
23
|
+
|
24
|
+
self.spec_extras = {
|
25
|
+
required_ruby_version: '>= 2.3'
|
26
|
+
}
|
27
|
+
end
|
@@ -0,0 +1,166 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module Types
|
4
|
+
class Array < TypedReference
|
5
|
+
|
6
|
+
## todo/check: make "internal" data (array) available? why? why not?
|
7
|
+
## attr_reader :data
|
8
|
+
|
9
|
+
def self.zero() @zero ||= new; end
|
10
|
+
def zero?()
|
11
|
+
if type.size == 0 ## assume dynamic (starts with empty array)
|
12
|
+
@data.empty?
|
13
|
+
else ## assume fixed size array (initialized with x zero items)
|
14
|
+
## use zero to check - why? why not?
|
15
|
+
## fix-fix-fix - add support for fixed Array here or use a new class - why? why not?
|
16
|
+
self == self.class.zero
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
def initialize( initial_value = [] )
|
23
|
+
## was: initial_value ||= []
|
24
|
+
## check if nil gets passed in - default not used?
|
25
|
+
raise ArgumentError, "expected literal of type #{type}; got typed #{initial_value.pretty_print_inspect}" if initial_value.is_a?( Typed )
|
26
|
+
|
27
|
+
@data = type.check_and_normalize_literal( initial_value ).map do |item|
|
28
|
+
type.sub_type.new( item )
|
29
|
+
end
|
30
|
+
|
31
|
+
## check size if fixed (must all set to zero up to size!!!)
|
32
|
+
if type.size > 0 ## auto-init with zeros
|
33
|
+
if @data.size >= type.size ## note: allow ("fixed") array to grow bigger for now - why? why not?
|
34
|
+
# bingo! already setup with inital values
|
35
|
+
else
|
36
|
+
self.size = type.size
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
|
43
|
+
extend Forwardable ## pulls in def_delegator
|
44
|
+
## add more Array forwards here!!!!
|
45
|
+
## todo/fix: wrap size, empty? return value literals into typed values - why? why not?
|
46
|
+
def_delegators :@data, :size, :length,
|
47
|
+
:empty?,
|
48
|
+
:each, :each_with_index
|
49
|
+
|
50
|
+
|
51
|
+
def []( index )
|
52
|
+
## use serialize to get value (why not value) - why? why not?
|
53
|
+
index = index.is_a?( Typed ) ? index.as_data : index
|
54
|
+
|
55
|
+
## fix: use index out of bounds error - why? why not?
|
56
|
+
raise ArgumentError, "Index out of bounds - #{index} : #{index.class.name} >= #{@data.size}" if index >= @data.size
|
57
|
+
|
58
|
+
obj = @data[ index ]
|
59
|
+
if obj.nil?
|
60
|
+
obj = type.sub_type.new_zero
|
61
|
+
@data[ index ] = obj
|
62
|
+
end
|
63
|
+
obj
|
64
|
+
end
|
65
|
+
|
66
|
+
def []=(index, new_value)
|
67
|
+
## use serialize to get value (why not value) - why? why not?
|
68
|
+
index = index.is_a?( Typed ) ? index.as_data : index
|
69
|
+
|
70
|
+
raise ArgumentError, "Sparse arrays are not supported; index out of bounds - sorry" if index >= @data.size
|
71
|
+
|
72
|
+
# fix-fix-fix: allow typed value here (not only literals)!!!
|
73
|
+
obj = if new_value.is_a?( Typed )
|
74
|
+
new_value
|
75
|
+
else
|
76
|
+
type.sub_type.new( new_value )
|
77
|
+
end
|
78
|
+
|
79
|
+
@data[ index ] = obj
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
def pop
|
84
|
+
## note: pop will decrease size/lenght!!!
|
85
|
+
## will remove LAST item in array!!!
|
86
|
+
##
|
87
|
+
## Pop is used to delete or remove an element
|
88
|
+
## in a dynamic array from the end.
|
89
|
+
@data.pop
|
90
|
+
end
|
91
|
+
|
92
|
+
def push( new_value )
|
93
|
+
## note: push will increase size/length!!!
|
94
|
+
##
|
95
|
+
## Push is used to add new element to a dynamic array,
|
96
|
+
## when you push a value to an array, it becomes the last value
|
97
|
+
obj = if new_value.is_a?( Typed )
|
98
|
+
new_value
|
99
|
+
else
|
100
|
+
type.sub_type.new( new_value )
|
101
|
+
end
|
102
|
+
@data.push( obj )
|
103
|
+
|
104
|
+
## note: returns array.size (NOT array itself!!!) to keep compatible with solidity - why? why not?
|
105
|
+
@data.size
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
def delete( index )
|
110
|
+
## note sets the element to zero!!!
|
111
|
+
## will NOT remove from array itself!!!
|
112
|
+
|
113
|
+
## use serialize to get value (why not value) - why? why not?
|
114
|
+
index = index.is_a?( Typed ) ? index.as_data : index
|
115
|
+
|
116
|
+
## fix: use index out of bounds error - why? why not?
|
117
|
+
raise ArgumentError, "Index out of bounds - #{index} : #{index.class.name} >= #{@data.size}" if index >= @data.size
|
118
|
+
|
119
|
+
@data[ index ] = type.sub_type.new_zero
|
120
|
+
self
|
121
|
+
end
|
122
|
+
|
123
|
+
|
124
|
+
def size=( new_size )
|
125
|
+
## todo/check: value must be greater 0 and greater than current size
|
126
|
+
diff = new_size - @data.size
|
127
|
+
if diff == 0
|
128
|
+
## stay as is; do nothing
|
129
|
+
elsif diff > 0
|
130
|
+
## todo/check:
|
131
|
+
## always return (deep) frozen zero object - why? why not?
|
132
|
+
## let user change the returned zero object - why? why not?
|
133
|
+
if type.sub_type.respond_to?( :new_zero )
|
134
|
+
## note: use a new unfrozen copy of the zero object
|
135
|
+
## changes to the object MUST be possible (new "empty" modifable object expected)
|
136
|
+
diff.times { @data << type.sub_type.new_zero }
|
137
|
+
else
|
138
|
+
raise "[Array]#length= cannot create new_zero for type #{type.sub_type} - sorry"
|
139
|
+
end
|
140
|
+
else ## diff < 0
|
141
|
+
## fix-fix-fix - raise error here - cannot shrink/delete via length!!!
|
142
|
+
end
|
143
|
+
self # return reference to self
|
144
|
+
end
|
145
|
+
alias_method :length=, :size= ## always use length (and remove size?) - why? why not?
|
146
|
+
|
147
|
+
|
148
|
+
def clear
|
149
|
+
## note: reset ary to zero (NOT empty e.g. [])
|
150
|
+
## differes for "fixed" size arrays
|
151
|
+
@data.clear
|
152
|
+
self.size = type.size if type.size > 0 ## auto-init with zeros
|
153
|
+
self # return reference to self
|
154
|
+
end
|
155
|
+
|
156
|
+
|
157
|
+
def as_data
|
158
|
+
@data.map {|item| item.as_data }
|
159
|
+
end
|
160
|
+
|
161
|
+
def pretty_print( printer )
|
162
|
+
printer.text( "<ref #{type}:#{@data.pretty_print_inspect}>" );
|
163
|
+
end
|
164
|
+
end # class Array
|
165
|
+
end # module Types
|
166
|
+
|
@@ -0,0 +1,53 @@
|
|
1
|
+
|
2
|
+
module Types
|
3
|
+
class Array
|
4
|
+
|
5
|
+
## note: add size option here (size=0) default is dynamic (not fixed)!!!
|
6
|
+
def self.build_class( sub_type, size=0 )
|
7
|
+
## add convenience sub_type helper here - why? why not?
|
8
|
+
## sub_type = Type.create( sub_type ) if sub_type.is_a?( Symbol ) ||
|
9
|
+
## sub_type.is_a?( String )
|
10
|
+
## sub_type = sub_type.type if sub_type.is_a?( Class ) && sub_type.ancestors.include?( Typed )
|
11
|
+
sub_type = typeof( sub_type )
|
12
|
+
|
13
|
+
type = ArrayType.instance( sub_type, size )
|
14
|
+
|
15
|
+
class_name = type.typedclass_name
|
16
|
+
|
17
|
+
## note: keep a class cache
|
18
|
+
## note: klasses may have different init sizes (default 0)
|
19
|
+
cache = @@cache ||= {}
|
20
|
+
klass = cache[ class_name ]
|
21
|
+
## fix-fix-fix - check if const klass defined for cache (no cache needed)!!!!!!!!
|
22
|
+
|
23
|
+
if klass.nil?
|
24
|
+
klass = Class.new( Array )
|
25
|
+
klass.define_singleton_method( :type ) do
|
26
|
+
@type ||= type
|
27
|
+
end
|
28
|
+
|
29
|
+
## add self.new too - note: call/forward to "old" orginal self.new of Event (base) class
|
30
|
+
klass.define_singleton_method( :new ) do |*args|
|
31
|
+
old_new( *args )
|
32
|
+
end
|
33
|
+
|
34
|
+
## add to cache for later (re)use
|
35
|
+
cache[ class_name ] = klass
|
36
|
+
|
37
|
+
## for default scope use Kernel - why? why not?
|
38
|
+
## or Globals or Typed - why? why not?
|
39
|
+
Types.const_set( class_name, klass )
|
40
|
+
end
|
41
|
+
|
42
|
+
klass
|
43
|
+
end # method self.build_class
|
44
|
+
|
45
|
+
class << self
|
46
|
+
alias_method :old_new, :new # note: store "old" orginal version of new
|
47
|
+
alias_method :new, :build_class # replace original version with create
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
end # class Array
|
52
|
+
end # module Types
|
53
|
+
|
@@ -0,0 +1,47 @@
|
|
1
|
+
#########
|
2
|
+
### monkey patch built-in FalseClass & TrueClass
|
3
|
+
## move to core-ext directory or such - why? why not?
|
4
|
+
|
5
|
+
|
6
|
+
## note. Bool is a module (NOT a class)
|
7
|
+
## adding a new superclass in ruby NOT possible
|
8
|
+
## still waiting for common base clase for bools in ruby!!!
|
9
|
+
## see https://github.com/geraldb/talks/blob/master/bool.md
|
10
|
+
## or https://github.com/rubycocos/core/tree/master/safebool
|
11
|
+
|
12
|
+
|
13
|
+
module Bool
|
14
|
+
def type() self.class.type; end
|
15
|
+
def as_data() self; end ## that is, return true or false
|
16
|
+
|
17
|
+
## note:
|
18
|
+
## self.type or such WILL not get included
|
19
|
+
## via module include in TrueClass & FalseClass
|
20
|
+
## add in TrueClass & FalseClass
|
21
|
+
## TrueClass.type will NOT work via "inheritance" like a "true" superclass - why? why not?
|
22
|
+
def self.type() Types::Typed::BoolType.instance; end
|
23
|
+
## note: lets you use Bool.type
|
24
|
+
def self.zero() false; end
|
25
|
+
end # module Bool
|
26
|
+
|
27
|
+
|
28
|
+
|
29
|
+
|
30
|
+
class FalseClass
|
31
|
+
include Bool ## "hack" - enables false.is_a?(Bool)
|
32
|
+
## and false.type
|
33
|
+
## and false.as_data
|
34
|
+
|
35
|
+
def self.type() Types::Typed::BoolType.instance; end
|
36
|
+
def zero?() true; end
|
37
|
+
end
|
38
|
+
|
39
|
+
class TrueClass
|
40
|
+
include Bool ## "hack" - enables true.is_a?(Bool)
|
41
|
+
## and true.type
|
42
|
+
## and true.as_data
|
43
|
+
|
44
|
+
def self.type() Types::Typed::BoolType.instance; end
|
45
|
+
def zero?() false; end
|
46
|
+
end
|
47
|
+
|