solidity-typed 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|