solidity-typed 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,145 @@
1
+
2
+ module Types
3
+ class Struct
4
+
5
+
6
+ # todo/fix: scope: keep empty by default
7
+
8
+ def self.build_class( class_name, scope: Types, **attributes )
9
+
10
+ ## todo/fix:
11
+ ## add self.class.type class method
12
+ ## returns TypedStruct<> instance with typedef info
13
+ ##
14
+ ## add name & format
15
+ ## e.g. :struct (why? why not? or :tuple? if using abi names?)
16
+ ## tuple( types,... ) - flattend (recursive)!!! - why? why not?
17
+
18
+
19
+ ## todo/fix:
20
+ ## check if valid class_name MUST start with uppercase letter etc.
21
+ ## todo/fix: check if constant is undefined in scoped namespace!!!!
22
+
23
+ ## map type symbols (:uint, :address, etc.)
24
+ ## to type for now "by hand" here
25
+
26
+ attributes = attributes.map do |key,type|
27
+ ## t =type
28
+ ## t = t.type if t.is_a?( Class ) && t.ancestors.include?( Typed )
29
+ [key, typeof( type )]
30
+ end.to_h
31
+
32
+
33
+ klass = Class.new( Struct ) do
34
+ attributes.each do |key,type|
35
+ define_method( key ) do
36
+ instance_variable_get( "@#{key}" )
37
+ end
38
+ ## note: for Bool auto-add getter with question mark (e.g. voted? etc.)
39
+ ## why? why not?
40
+ if type == BoolType.instance
41
+ define_method( "#{key}?" ) do
42
+ instance_variable_get( "@#{key}" )
43
+ end
44
+ end
45
+ define_method( "#{key}=" ) do |value|
46
+ ## todo/fix:
47
+ ## check if arg is typed && type match?
48
+ ## if not (assume literal) try to convert to type!!!!
49
+
50
+ value = if value.is_a?(Typed)
51
+ ## fix-fix-fix - check type match here!!!
52
+ value
53
+ else
54
+ type.new( value )
55
+ end
56
+
57
+ instance_variable_set( "@#{key}", value )
58
+ end
59
+ end
60
+
61
+
62
+
63
+
64
+ alias_method :old_freeze, :freeze # note: store "old" orginal version of freeze
65
+ define_method( :freeze ) do
66
+ old_freeze ## same as calling super
67
+ attributes.keys.each do |key|
68
+ instance_variable_get( "@#{key}" ).freeze
69
+ end
70
+ self # return reference to self
71
+ end
72
+ end
73
+
74
+
75
+ type = StructType.new( class_name, klass )
76
+ klass.define_singleton_method( :type ) do
77
+ @type ||= type
78
+ end
79
+
80
+ ## add attributes (with keys / types) class method
81
+ klass.define_singleton_method( :attributes ) do
82
+ attributes
83
+ end
84
+
85
+ ## add self.new too - note: call/forward to "old" orginal self.new of Event (base) class
86
+ klass.define_singleton_method( :new ) do |*args, **kwargs|
87
+ if kwargs.size > 0 ## assume kwargs
88
+ ## -fix-fix-fix- check all keywords if part or struct here too!!!
89
+ old_new( **kwargs )
90
+ else
91
+ if args.empty? ## no args - use new_zero and set (initialize) all ivars to zero
92
+ new_zero
93
+ else
94
+ if args.size != attributes.size
95
+ ## check for required args/params - all MUST be passed in!!!
96
+ raise ArgumentError, "[Struct] wrong number of arguments for #{name}.new - #{args.size} for #{attributes.size}"
97
+ end
98
+ old_new( *args )
99
+ end
100
+ end
101
+ end
102
+
103
+
104
+ klass.define_singleton_method( :new_zero ) do
105
+ values = attributes.values.map do |type|
106
+ if type.respond_to?( :new_zero )
107
+ type.new_zero
108
+ else
109
+ raise ArgumentError, "[Struct] no new_zero support for type #{type}; sorry"
110
+ end
111
+ end
112
+ old_new( *values )
113
+ end
114
+
115
+
116
+ =begin
117
+ ## note: use Kernel for "namespacing"
118
+ ## make all enums convenience converters (always) global
119
+ ## including uppercase methods (e.g. State(), Color(), etc.) does NOT work otherwise (with other module includes)
120
+
121
+ ## add global "Kernel" convenience converter function
122
+ ## e.g. Vote(0) is same as Vote.convert(0)
123
+ Kernel.class_eval( <<RUBY )
124
+ def #{class_name}( arg )
125
+ #{class_name}.convert( arg )
126
+ end
127
+ RUBY
128
+ =end
129
+
130
+ ## note: use scoped (module) and NOT Object for namespacing
131
+ ## use include Safe to make all structs global
132
+ ## fix-fix-fix - make class_name unique across contracts (e.g. reuse same name in different contract)
133
+ scope.const_set( class_name, klass ) ## returns klass (plus sets global constant class name)
134
+ end # method build_class
135
+
136
+
137
+
138
+ class << self
139
+ alias_method :old_new, :new # note: store "old" orginal version of new
140
+ alias_method :new, :build_class # replace original version with create
141
+ end
142
+
143
+
144
+ end # class Struct
145
+ end # module Types
@@ -0,0 +1,114 @@
1
+
2
+ =begin
3
+ class Object ### move to core_ext/object - why? why not?
4
+ ## check - add scoped class here too - why? why not?
5
+ ## e.g. is_a?( Typed ) || is_a?( ContractBase )
6
+ ## or add a TypedContract delagate class or such - why? why not?
7
+ ## fix - check for class has singelton method type - why? why not?
8
+ def typed?() is_a?( Typed ); end
9
+ end
10
+ =end
11
+
12
+
13
+
14
+ module Types
15
+
16
+
17
+ ##
18
+ # note:
19
+ # use class Typed as namespace for metatypes e.g. Type, StringType, AddressType,
20
+ # the idea is to avoid confusion about metatypes and typed classes
21
+ # by "hiding" metatypes from top-level (inside typed)
22
+
23
+ class Typed
24
+
25
+
26
+ ### use like:
27
+ ## Typed.serialize( obj ) or
28
+ ## Typed.dump( obj )
29
+ ## keep serialize/dump here in Typed - why? why not?
30
+ def self.serialize( obj )
31
+ obj.as_data
32
+ end
33
+ class << self
34
+ alias_method :dump, :serialize
35
+ end
36
+
37
+
38
+
39
+ def self.type
40
+ raise "no required typed class accessor/ getter method defined for Typed subclass #{self.class.name}; sorry"
41
+ end
42
+ def type() self.class.type; end
43
+
44
+ def as_data ## kind of like as_json (in rails/ActiveModel/Serializers/JSON/as_json)
45
+ raise "no required as_data method defined for Typed subclass #{self.class.name}; sorry"
46
+ end
47
+
48
+
49
+ ## keep serialize and/or as_json - why? why not?
50
+ def serialize() as_data; end
51
+ def as_json() as_data; end
52
+
53
+ =begin
54
+ def as_json( args={} ) serialize; end
55
+ def serialize
56
+ raise "no required serialize method defined for Typed subclass #{self.class.name}; sorry"
57
+ end
58
+
59
+ def deserialize( serialized_value ) replace( serialized_value ); end
60
+ def replace( new_value )
61
+ raise "no required replace method defined for Typed subclass #{self.class.name}; sorry"
62
+ end
63
+ =end
64
+ end # class Typed
65
+
66
+
67
+ ## add value & reference type base - why? why not?
68
+ class TypedValue < Typed
69
+
70
+ ## todo/check: make "internal" value (string/integer) available? why? why not?
71
+ ## attr_reader :value
72
+
73
+ ## todo/check -- use self.zero or such - why? why not?
74
+ def as_data() @value; end
75
+
76
+ def pretty_print( printer ) printer.text( "<val #{type}:#{@value.inspect}>" ); end
77
+
78
+ def to_s
79
+ if @value.is_a?(::String) || @value.is_a?(::Integer)
80
+ @value.to_s
81
+ else
82
+ raise "no string conversion of value possible; sorry"
83
+ end
84
+ end
85
+
86
+ def ==(other)
87
+ other.is_a?(self.class) &&
88
+ type == other.type && ## note: type for no redundant (always the same if same class AND TypedValue)
89
+ @value == other.instance_variable_get( :@value ) ## compare value via as_data!!!
90
+ end
91
+
92
+ def hash() [@value, type].hash; end
93
+ ## todo/check - hash == other.hash is default any way??
94
+ def eql?(other) hash == other.hash; end
95
+ end # TypedValue
96
+
97
+
98
+
99
+ class TypedReference < Typed
100
+
101
+
102
+ def ==(other)
103
+ other.is_a?(self.class) &&
104
+ type == other.type &&
105
+ @data == other.instance_variable_get( :@data )
106
+ end
107
+
108
+ def hash() [@data, type].hash; end
109
+ ## todo/check - hash == other.hash is default any way??
110
+ ## or is it object_id == other.object_id ????
111
+ def eql?(other) hash == other.hash; end
112
+ end
113
+
114
+ end # module Types
@@ -0,0 +1,113 @@
1
+ module Types
2
+
3
+
4
+
5
+
6
+ class String < TypedValue
7
+ def self.type() StringType.instance; end
8
+ def self.zero() @zero ||= new; end
9
+ def zero?() @value.empty?; end
10
+
11
+
12
+ def initialize( initial_value = STRING_ZERO )
13
+ ## was: initial_value ||= type.zero
14
+ ## check if nil gets passed in - default not used?
15
+
16
+ raise ArgumentError, "expected literal of type #{type}; got typed #{initial_value.pretty_print_inspect}" if initial_value.is_a?( Typed )
17
+
18
+ @value = type.check_and_normalize_literal( initial_value )
19
+ @value.freeze ## freeze here - why? why not?
20
+ @value
21
+ end
22
+
23
+
24
+ extend Forwardable ## pulls in def_delegator
25
+ ## add more String forwards here!!!!
26
+ ## fix!! - wrap returned values in typed value!!!
27
+ ## use type_cast_to_literal or such - why? why not?
28
+ def_delegators :@value, :downcase,
29
+ :index, :include?,
30
+ :+
31
+
32
+ def to_str() @value; end ## "automagilally" support implicit string conversion - why? why not?
33
+ end # class String
34
+
35
+
36
+
37
+ class Address < TypedValue
38
+ def self.type() AddressType.instance; end
39
+ def self.zero() @zero ||= new; end
40
+ def zero?() @value == ADDRESS_ZERO; end
41
+
42
+
43
+ def initialize( initial_value = ADDRESS_ZERO )
44
+ ## was: initial_value ||= type.zero
45
+ ## check if nil gets passed in - default not used?
46
+
47
+ raise ArgumentError, "expected literal of type #{type}; got typed #{initial_value.pretty_print_inspect}" if initial_value.is_a?( Typed )
48
+
49
+ @value = type.check_and_normalize_literal( initial_value )
50
+ @value.freeze ## freeze here - why? why not?
51
+ @value
52
+ end
53
+ end # class Address
54
+
55
+
56
+ class InscriptionId < TypedValue
57
+ def self.type() InscriptionIdType.instance; end
58
+ def self.zero() @zero ||= new; end
59
+ def zero?() @value == INSCRIPTION_ID_ZERO; end
60
+
61
+ def initialize( initial_value = INSCRIPTION_ID_ZERO )
62
+ ## was: nitial_value ||= type.zero
63
+ ## check if nil gets passed in - default not used?
64
+
65
+ raise ArgumentError, "expected literal of type #{type}; got typed #{initial_value.pretty_print_inspect}" if initial_value.is_a?( Typed )
66
+
67
+ @value = type.check_and_normalize_literal( initial_value )
68
+ @value.freeze ## freeze here - why? why not?
69
+ @value
70
+ end
71
+ end # class InscriptionId
72
+
73
+
74
+
75
+ class Bytes32 < TypedValue
76
+ def self.type() Bytes32Type.instance; end
77
+ def self.zero() @zero ||= new; end
78
+ def zero?() @value == BYTES32_ZERO; end
79
+
80
+ def initialize( initial_value = BYTES32_ZERO )
81
+ ## was: initial_value ||= type.zero
82
+ ## check if nil gets passed in - default not used?
83
+
84
+ raise ArgumentError, "expected literal of type #{type}; got typed #{initial_value.pretty_print_inspect}" if initial_value.is_a?( Typed )
85
+
86
+ @value = type.check_and_normalize_literal( initial_value )
87
+ @value.freeze ## freeze here - why? why not?
88
+ @value
89
+ end
90
+ end # class Bytes32
91
+
92
+
93
+ class Bytes < TypedValue
94
+ def self.type() BytesType.instance; end
95
+ def self.zero() @zero ||= new; end
96
+ def zero?() @value == BYTES_ZERO; end
97
+
98
+ def initialize( initial_value = BYTES_ZERO )
99
+ ## was: initial_value ||= type.zero
100
+ ## check if nil gets passed in - default not used?
101
+
102
+ raise ArgumentError, "expected literal of type #{type}; got typed #{initial_value.pretty_print_inspect}" if initial_value.is_a?( Typed )
103
+
104
+ @value = type.check_and_normalize_literal( initial_value )
105
+ @value.freeze ## freeze here - why? why not?
106
+ @value
107
+ end
108
+ end # class Bytes
109
+
110
+
111
+
112
+ end # module Types
113
+
@@ -0,0 +1,23 @@
1
+ module Solidity
2
+ module Module
3
+ module Typed
4
+ MAJOR = 0
5
+ MINOR = 1
6
+ PATCH = 0
7
+ VERSION = [MAJOR,MINOR,PATCH].join('.')
8
+
9
+ def self.version
10
+ VERSION
11
+ end
12
+
13
+ def self.banner
14
+ "solidity-typed/#{VERSION} on Ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}] in (#{root})"
15
+ end
16
+
17
+ def self.root
18
+ File.expand_path( File.dirname(File.dirname(File.dirname(File.dirname(__FILE__)))) )
19
+ end
20
+
21
+ end # module Typed
22
+ end # module Module
23
+ end # module Solidity
@@ -0,0 +1,128 @@
1
+
2
+
3
+ require 'forwardable' ## def_delegate
4
+
5
+
6
+ ##
7
+ ### add more erros - why? why not?
8
+ class ValueError < StandardError; end
9
+ ## if type is ok, but value of type not in range (e.g. uint with negative numbers)
10
+ ## or maybe enum out-of-range - why? why not?
11
+
12
+
13
+
14
+
15
+
16
+ ## forward declare contract base (from solidity)
17
+ ## for type checking
18
+ class ContractBase
19
+ end
20
+
21
+
22
+
23
+ ## our own code
24
+ require_relative 'typed/version'
25
+ require_relative 'typed/metatypes/types'
26
+ require_relative 'typed/metatypes/bool'
27
+ require_relative 'typed/metatypes/literals'
28
+ require_relative 'typed/metatypes/array'
29
+ require_relative 'typed/metatypes/mapping'
30
+
31
+
32
+ require_relative 'typed/typed'
33
+ require_relative 'typed/bool'
34
+ require_relative 'typed/values'
35
+ require_relative 'typed/numbers'
36
+
37
+ require_relative 'typed/array'
38
+ require_relative 'typed/array_builder'
39
+ require_relative 'typed/mapping'
40
+ require_relative 'typed/mapping_builder'
41
+
42
+ require_relative 'typed/struct'
43
+ require_relative 'typed/struct_builder'
44
+ require_relative 'typed/event'
45
+ require_relative 'typed/event_builder'
46
+
47
+
48
+ require_relative 'typed/enum'
49
+ require_relative 'typed/enum_builder'
50
+
51
+ require_relative 'typed/conversion'
52
+
53
+
54
+
55
+ #############
56
+ # convenience helpers
57
+
58
+ ## note: Bool is now (global) "monkey-patched" - no longer a wrapper
59
+ ## TypedBool = Types::Bool
60
+
61
+
62
+ TypedString = Types::String
63
+ TypedAddress = Types::Address
64
+ TypedInscriptionId = Types::InscriptionId
65
+ TypedBytes32 = Types::Bytes32
66
+ TypedBytes = Types::Bytes
67
+ TypedUInt = Types::UInt
68
+ TypedInt = Types::Int
69
+ TypedTimestamp = Types::Timestamp
70
+ TypedTimedelta = Types::Timedelta
71
+
72
+ TypedArray = Types::Array
73
+ TypedMapping = Types::Mapping
74
+ TypedEnum = Types::Enum
75
+ TypedStruct = Types::Struct
76
+ TypedEvent = Types::Event
77
+
78
+ T = Types ## make T an alias for Types - why? why not?
79
+
80
+
81
+
82
+ ####
83
+ ## (global) convenience helper to get type
84
+ ##
85
+ ## use a different name
86
+ ## e.g. typedef( obj ) or
87
+ ## typedclass_to_type( obj ) or
88
+ ## Type( obj ) or type() ??
89
+ def typeof( obj )
90
+ ## case 1) already a metatype?
91
+ return obj if obj.is_a?( Types::Typed::Type )
92
+ ## case 2a) check for (typed) class
93
+ ## check for Typed ancestor in class - why? why not?
94
+ ## e.g. obj.ancestors.include?( Types::Typed )
95
+ ## 2b) Bool module
96
+ return obj.type if obj.instance_of?( Class ) && obj.respond_to?( :type )
97
+ return obj.type if obj == Bool ## special case for module Bool!!!
98
+
99
+ ## support plain objects too here - why? why not?
100
+ ## -- check for "plain objects"
101
+ ## return obj.class.type if obj.class.respond_to?( :type )
102
+
103
+ raise ArgumentError, "metatype or typedclass expected; got #{obj.inspect}"
104
+ end
105
+
106
+
107
+
108
+
109
+
110
+ # "sandbox helper"
111
+ module Sandbox
112
+ include Types
113
+ end
114
+ #
115
+ ## use like:
116
+ ## module Sandbox
117
+ ## str = String.new
118
+ ## Array‹String› = Array.new( String )
119
+ ## ary = Array‹String›.new
120
+ ## ...
121
+ ##
122
+ ## to access "old/classic" string or array use:
123
+ ## str = ::String.new
124
+ ## ary = ::Array.new
125
+ ## end
126
+
127
+
128
+ puts Solidity::Module::Typed.banner ## say hello
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: solidity-typed
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Gerald Bauer
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-10-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rdoc
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '4.0'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '7'
23
+ type: :development
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '4.0'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '7'
33
+ - !ruby/object:Gem::Dependency
34
+ name: hoe
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '4.0'
40
+ type: :development
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '4.0'
47
+ description: solidity-typed - "zero-dependency" 100%-solidity compatible data type
48
+ and application binary interface (abi) machinery incl. bool, (frozen) string, address,
49
+ bytes, uint, int, enum, struct, array, mapping, event, and more for solidity-inspired
50
+ contract (blockchain) programming languages incl. rubidity et al
51
+ email: gerald.bauer@gmail.com
52
+ executables: []
53
+ extensions: []
54
+ extra_rdoc_files:
55
+ - CHANGELOG.md
56
+ - Manifest.txt
57
+ - README.md
58
+ files:
59
+ - CHANGELOG.md
60
+ - Manifest.txt
61
+ - README.md
62
+ - Rakefile
63
+ - lib/solidity/typed.rb
64
+ - lib/solidity/typed/array.rb
65
+ - lib/solidity/typed/array_builder.rb
66
+ - lib/solidity/typed/bool.rb
67
+ - lib/solidity/typed/conversion.rb
68
+ - lib/solidity/typed/enum.rb
69
+ - lib/solidity/typed/enum_builder.rb
70
+ - lib/solidity/typed/mapping.rb
71
+ - lib/solidity/typed/mapping_builder.rb
72
+ - lib/solidity/typed/metatypes/array.rb
73
+ - lib/solidity/typed/metatypes/bool.rb
74
+ - lib/solidity/typed/metatypes/literals.rb
75
+ - lib/solidity/typed/metatypes/mapping.rb
76
+ - lib/solidity/typed/metatypes/types.rb
77
+ - lib/solidity/typed/numbers.rb
78
+ - lib/solidity/typed/struct.rb
79
+ - lib/solidity/typed/struct_builder.rb
80
+ - lib/solidity/typed/typed.rb
81
+ - lib/solidity/typed/values.rb
82
+ - lib/solidity/typed/version.rb
83
+ homepage: https://github.com/s6ruby/rubidity
84
+ licenses:
85
+ - Public Domain
86
+ metadata: {}
87
+ post_install_message:
88
+ rdoc_options:
89
+ - "--main"
90
+ - README.md
91
+ require_paths:
92
+ - lib
93
+ required_ruby_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '2.3'
98
+ required_rubygems_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ requirements: []
104
+ rubygems_version: 3.4.10
105
+ signing_key:
106
+ specification_version: 4
107
+ summary: solidity-typed - "zero-dependency" 100%-solidity compatible data type and
108
+ application binary interface (abi) machinery incl. bool, (frozen) string, address,
109
+ bytes, uint, int, enum, struct, array, mapping, event, and more for solidity-inspired
110
+ contract (blockchain) programming languages incl. rubidity et al
111
+ test_files: []