scale_rb 0.4.2 → 0.5.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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.devcontainer/devcontainer.json +21 -0
  3. data/Dockerfile +16 -0
  4. data/Gemfile +4 -4
  5. data/README.md +19 -7
  6. data/bin/console +0 -0
  7. data/bin/setup +0 -0
  8. data/examples/http_client_2.rb +0 -2
  9. data/exe/metadata +9 -11
  10. data/lib/address.rb +1 -1
  11. data/lib/custom_assign.rb +92 -0
  12. data/lib/scale_rb/call_helper.rb +42 -0
  13. data/lib/{client → scale_rb/client}/client_ext.rb +12 -13
  14. data/lib/{client → scale_rb/client}/http_client.rb +1 -1
  15. data/lib/{client → scale_rb/client}/ws_client.rb +11 -15
  16. data/lib/scale_rb/codec.rb +25 -0
  17. data/lib/scale_rb/codec_utils.rb +128 -0
  18. data/lib/scale_rb/decode.rb +164 -0
  19. data/lib/scale_rb/encode.rb +150 -0
  20. data/lib/{hasher.rb → scale_rb/hasher.rb} +10 -8
  21. data/lib/scale_rb/metadata/metadata.rb +114 -0
  22. data/lib/{metadata → scale_rb/metadata}/metadata_v10.rb +0 -17
  23. data/lib/{metadata → scale_rb/metadata}/metadata_v11.rb +0 -17
  24. data/lib/{metadata → scale_rb/metadata}/metadata_v12.rb +0 -17
  25. data/lib/{metadata → scale_rb/metadata}/metadata_v13.rb +0 -17
  26. data/lib/{metadata → scale_rb/metadata}/metadata_v14.rb +18 -18
  27. data/lib/{metadata → scale_rb/metadata}/metadata_v9.rb +0 -17
  28. data/lib/scale_rb/metadata/registry.rb +263 -0
  29. data/lib/scale_rb/metadata/type_exp.rb +286 -0
  30. data/lib/scale_rb/portable_registry.rb +133 -0
  31. data/lib/{storage_helper.rb → scale_rb/storage_helper.rb} +16 -4
  32. data/lib/scale_rb/types.rb +233 -0
  33. data/lib/scale_rb/utils.rb +125 -0
  34. data/lib/scale_rb/version.rb +1 -1
  35. data/lib/scale_rb.rb +20 -26
  36. data/lib/type_enforcer.rb +170 -0
  37. data/scale_rb.gemspec +3 -0
  38. metadata +57 -19
  39. data/lib/codec.rb +0 -450
  40. data/lib/metadata/metadata.rb +0 -137
  41. data/lib/monkey_patching.rb +0 -115
  42. data/lib/portable_codec.rb +0 -285
  43. data/lib/registry.rb +0 -13
@@ -20,7 +20,7 @@ module ScaleRb
20
20
  ]
21
21
  else
22
22
  [
23
- registry[key[:type]]._get(:def)._get(:tuple).first(key[:value].length),
23
+ registry[key[:type]].tuple.first(key[:value].length),
24
24
  key[:value],
25
25
  key[:hashers].first(key[:value].length)
26
26
  ]
@@ -32,12 +32,24 @@ module ScaleRb
32
32
  raise "Key's value doesn't match key's type, key's value: #{key_values.inspect}, but key's type: #{key_types.inspect}. Please check your key's value."
33
33
  end
34
34
 
35
- storage_key + PortableCodec._encode_types_with_hashers(key_types, key_values, registry, key_hashers)
35
+ storage_key + _encode_types_with_hashers(key_types, key_values, registry, key_hashers)
36
36
  else
37
37
  storage_key
38
38
  end
39
39
  end
40
40
 
41
+ def _encode_types_with_hashers(type_ids, values, registry, hashers)
42
+ if !hashers.nil? && hashers.length != type_ids.length
43
+ raise "type_ids length: #{type_ids.length}, hashers length: #{hashers.length}"
44
+ end
45
+
46
+ type_ids
47
+ .map.with_index { |type_id, i| ScaleRb::Codec.encode(type_id, values[i], registry) }
48
+ .each_with_index.reduce([]) do |memo, (bytes, i)|
49
+ memo + Hasher.apply_hasher(hashers[i], bytes)
50
+ end
51
+ end
52
+
41
53
  # data: hex string
42
54
  # type: portable type id
43
55
  # optional: boolean
@@ -45,14 +57,14 @@ module ScaleRb
45
57
  # returns nil or data
46
58
  def decode_storage(data, type, optional, fallback, registry)
47
59
  data ||= (optional ? nil : fallback)
48
- PortableCodec.decode(type, data._to_bytes, registry)[0] if data
60
+ ScaleRb::Codec.decode(type, Utils.hex_to_u8a(data), registry)[0] if data
49
61
  end
50
62
 
51
63
  # storage_item: the storage item from metadata
52
64
  def decode_storage2(data, storage_item, registry)
53
65
  modifier = storage_item._get(:modifier) # Default | Optional
54
66
  fallback = storage_item._get(:fallback)
55
- type = storage_item._get(:type)._get(:plain) || storage_item._get(:type)._get(:map)._get(:value)
67
+ type = storage_item._get(:type, :plain) || storage_item._get(:type, :map, :value)
56
68
  decode_storage(data, type, modifier == 'Optional', fallback, registry)
57
69
  end
58
70
 
@@ -0,0 +1,233 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry-struct'
4
+ require 'dry-types'
5
+
6
+ module ScaleRb
7
+ module Types
8
+ include Dry.Types()
9
+
10
+ Primitive = Types::Strict::Symbol.enum(
11
+ :I8, :U8, :I16, :U16, :I32, :U32, :I64, :U64, :I128, :U128, :I256, :U256, :Bool, :Str, :Char
12
+ )
13
+ Ti = Types::Strict::Integer.constrained(gteq: 0)
14
+ U8 = Types::Strict::Integer.constrained(gteq: 0, lt: 256)
15
+ U8Array = Types::Strict::Array.of(U8)
16
+ Hex = Types::Strict::String.constrained(format: /\A0x[0-9a-fA-F]+\z/)
17
+
18
+ Registry = Types.Interface(:[])
19
+
20
+ HashMap = lambda do |key_type, value_type|
21
+ Types::Hash.map(key_type, value_type)
22
+ end
23
+ UnsignedInteger = Types::Strict::Integer.constrained(gteq: 0)
24
+ TypedArray = ->(type) { Types::Array.of(type) }
25
+
26
+ class Base < Dry::Struct
27
+ attribute? :registry, Registry
28
+ attribute? :path, Types::Strict::Array.of(Types::Strict::String)
29
+
30
+ def t(type_id)
31
+ raise 'No registry' unless registry
32
+
33
+ pt = registry[type_id]
34
+ raise "Unknown type: #{type_id}" unless pt
35
+
36
+ pt
37
+ end
38
+
39
+ def to_s
40
+ to_string
41
+ end
42
+
43
+ MAX_DEPTH = 2
44
+ def to_string(_depth = 0)
45
+ raise NotImplementedError, "#{self.class} must implement to_string"
46
+ end
47
+ end
48
+
49
+ class PrimitiveType < Base
50
+ attribute :primitive, Primitive
51
+
52
+ def to_string(_depth = 0)
53
+ primitive.to_s
54
+ end
55
+ end
56
+
57
+ class CompactType < Base
58
+ attribute? :type, Ti
59
+
60
+ def to_string(depth = 0)
61
+ if type
62
+ if depth > MAX_DEPTH
63
+ 'Compact<...>'
64
+ else
65
+ "Compact<#{t(type).to_string(depth + 1)}>"
66
+ end
67
+ else
68
+ 'Compact'
69
+ end
70
+ end
71
+ end
72
+
73
+ class SequenceType < Base
74
+ attribute :type, Ti
75
+
76
+ def to_string(depth = 0)
77
+ if depth > MAX_DEPTH
78
+ '[...]'
79
+ else
80
+ "[#{t(type).to_string(depth + 1)}]"
81
+ end
82
+ end
83
+ end
84
+
85
+ class BitSequenceType < Base
86
+ attribute :bit_store_type, Ti
87
+ attribute :bit_order_type, Ti
88
+
89
+ def to_string(depth = 0)
90
+ if depth > MAX_DEPTH
91
+ 'BitSequence<...>'
92
+ else
93
+ "BitSequence<#{t(bit_store_type).to_string(depth + 1)}, #{t(bit_order_type).to_string(depth + 1)}>"
94
+ end
95
+ end
96
+ end
97
+
98
+ class ArrayType < Base
99
+ attribute :len, Types::Strict::Integer
100
+ attribute :type, Ti
101
+
102
+ def to_string(depth = 0)
103
+ if depth > MAX_DEPTH
104
+ '[...]'
105
+ else
106
+ "[#{t(type).to_string(depth + 1)}; #{len}]"
107
+ end
108
+ end
109
+ end
110
+
111
+ class TupleType < Base
112
+ attribute :tuple, Types::Strict::Array.of(Ti)
113
+
114
+ def to_string(depth = 0)
115
+ if depth > MAX_DEPTH
116
+ '(...)'
117
+ else
118
+ "(#{tuple.map { |t| t(t).to_string(depth + 1) }.join(', ')})"
119
+ end
120
+ end
121
+ end
122
+
123
+ class Field < Dry::Struct
124
+ attribute :name, Types::Strict::String
125
+ attribute :type, Ti
126
+ end
127
+
128
+ class StructType < Base
129
+ attribute :fields, Types::Strict::Array.of(Field)
130
+
131
+ def to_string(depth = 0)
132
+ if depth > MAX_DEPTH
133
+ '{ ... }'
134
+ else
135
+ "{ #{fields.map { |field| "#{field.name}: #{t(field.type).to_string(depth + 1)}" }.join(', ')} }"
136
+ end
137
+ end
138
+ end
139
+
140
+ class UnitType < Base
141
+ def to_string(_depth = 0)
142
+ '()'
143
+ end
144
+ end
145
+
146
+ class SimpleVariant < Dry::Struct
147
+ attribute :name, Types::Strict::Symbol
148
+ attribute :index, Types::Strict::Integer
149
+ end
150
+
151
+ class TupleVariant < Dry::Struct
152
+ attribute :name, Types::Strict::Symbol
153
+ attribute :index, Types::Strict::Integer
154
+ attribute :tuple, TupleType
155
+ end
156
+
157
+ class StructVariant < Dry::Struct
158
+ attribute :name, Types::Strict::Symbol
159
+ attribute :index, Types::Strict::Integer
160
+ attribute :struct, StructType
161
+ end
162
+
163
+ VariantKind = Instance(SimpleVariant) | Instance(TupleVariant) | Instance(StructVariant)
164
+
165
+ class VariantType < Base
166
+ attribute :variants, Types::Array.of(VariantKind)
167
+
168
+ def to_string(depth = 0)
169
+ if depth > MAX_DEPTH
170
+ variants.sort_by(&:index).map { |v| v.name.to_s }.join(' | ')
171
+ else
172
+ variants.sort_by(&:index).map do |v|
173
+ case v
174
+ when SimpleVariant
175
+ v.name.to_s
176
+ when TupleVariant
177
+ "#{v.name}#{v.tuple.to_string(depth + 1)}"
178
+ when StructVariant
179
+ "#{v.name} #{v.struct.to_string(depth + 1)}"
180
+ end
181
+ end.join(' | ')
182
+ end
183
+ end
184
+
185
+ def self.option(type, registry)
186
+ VariantType.new(
187
+ variants: [
188
+ SimpleVariant.new(name: :None, index: 0),
189
+ TupleVariant.new(name: :Some, index: 1, tuple: TupleType.new(tuple: [type], registry:))
190
+ ],
191
+ registry:
192
+ )
193
+ end
194
+
195
+ def option?
196
+ variants.length == 2 &&
197
+ variants.any? { |v| v.is_a?(SimpleVariant) && v.name == :None && v.index == 0 } &&
198
+ variants.any? { |v| v.is_a?(TupleVariant) && v.name == :Some && v.index == 1 }
199
+ end
200
+
201
+ def self.result(ok_type, err_type, registry)
202
+ VariantType.new(
203
+ variants: [
204
+ TupleVariant.new(name: :Ok, index: 0, tuple: TupleType.new(tuple: [ok_type], registry:)),
205
+ TupleVariant.new(name: :Err, index: 1, tuple: TupleType.new(tuple: [err_type], registry:))
206
+ ],
207
+ registry:
208
+ )
209
+ end
210
+ end
211
+
212
+ PortableType = Instance(VariantType) |
213
+ Instance(StructType) |
214
+ Instance(TupleType) |
215
+ Instance(ArrayType) |
216
+ Instance(CompactType) |
217
+ Instance(PrimitiveType) |
218
+ Instance(UnitType) |
219
+ Instance(SequenceType) |
220
+ Instance(BitSequenceType)
221
+
222
+ DecodeResult = lambda do |type|
223
+ Types::Array.of(Types::Any).constrained(size: 2).constructor do |arr|
224
+ [type[arr[0]], arr[1]] # U8Array[arr[1]], but performance is not good.
225
+ end
226
+ end
227
+ end
228
+ end
229
+
230
+ # type = ScaleRb::Types::TypedArray[ScaleRb::Types::UnsignedInteger]
231
+ # p type
232
+ # p type[[1, 2]] # => [1, 2]
233
+ # # p type[[-1, -2]] # => -1 violates constraints (gteq?(0, -1) failed)
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Hash
4
+ # Check if the key exists in the hash
5
+ # @param key [String | Symbol] Key to check
6
+ # @return [Boolean] True if the key exists, false otherwise
7
+ def _key?(key)
8
+ ScaleRb::Utils.key?(self, key)
9
+ end
10
+
11
+ # Get the value from the hash
12
+ # @param keys [Array<String | Symbol>] Keys to get the value from
13
+ # @return [Object | NilClass] Value if the key exists, nil otherwise
14
+ def _get(*keys)
15
+ ScaleRb::Utils.get(self, *keys)
16
+ end
17
+ end
18
+
19
+ module ScaleRb
20
+ module Utils
21
+ class << self
22
+ # https://www.rubyguides.com/2017/01/read-binary-data/
23
+ def hex_to_u8a(str)
24
+ data = str.start_with?('0x') ? str[2..] : str
25
+ raise 'Not valid hex string' if data =~ /[^\da-f]+/i
26
+
27
+ data = "0#{data}" if data.length.odd?
28
+ data.scan(/../).map(&:hex)
29
+ end
30
+
31
+ def camelize(str)
32
+ str.split('_').collect(&:capitalize).join
33
+ end
34
+
35
+ def underscore(str)
36
+ str.gsub(/::/, '/')
37
+ .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
38
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
39
+ .tr('-', '_')
40
+ .downcase
41
+ end
42
+
43
+ def int_to_u8a(int, bit_length = nil)
44
+ hex = bit_length ? int.to_s(16).rjust(bit_length / 4, '0') : int.to_s(16)
45
+ hex_to_u8a(hex)
46
+ end
47
+
48
+ def uint_to_int(unsigned, bit_length)
49
+ unsigned_mid = 2**(bit_length - 1)
50
+ unsigned_ceiling = 2**bit_length
51
+ unsigned >= unsigned_mid ? unsigned - unsigned_ceiling : unsigned
52
+ end
53
+
54
+ def int_to_uint(signed, bit_length)
55
+ unsigned_mid = 2**(bit_length - 1)
56
+ unsigned_ceiling = 2**bit_length
57
+ raise 'Out of scope' if signed >= unsigned_mid || signed <= -unsigned_mid
58
+
59
+ signed.negative? ? unsigned_ceiling + signed : signed
60
+ end
61
+
62
+ # unix timestamp to utc
63
+ def unix_to_utc(unix)
64
+ Time.at(unix).utc.asctime
65
+ end
66
+
67
+ # utc to unix timestamp
68
+ def utc_to_unix(utc_asctime)
69
+ Time.parse(utc_asctime)
70
+ end
71
+
72
+ def u8a?(arr)
73
+ arr.all? { |e| e >= 0 && e <= 255 }
74
+ end
75
+
76
+ def u8a_to_hex(u8a)
77
+ raise 'Not a byte array' unless u8a?(u8a)
78
+
79
+ u8a.reduce('0x') { |hex, u8| hex + u8.to_s(16).rjust(2, '0') }
80
+ end
81
+
82
+ def u8a_to_bin(u8a)
83
+ raise 'Not a byte array' unless u8a?(u8a)
84
+
85
+ u8a.reduce('0b') { |bin, u8| bin + u8.to_s(2).rjust(8, '0') }
86
+ end
87
+
88
+ def u8a_to_utf8(u8a)
89
+ raise 'Not a byte array' unless u8a?(u8a)
90
+
91
+ u8a.pack('C*').force_encoding('utf-8')
92
+ end
93
+
94
+ def u8a_to_uint(u8a)
95
+ u8a_to_hex(u8a).to_i(16)
96
+ end
97
+
98
+ def u8a_to_int(u8a, bit_length)
99
+ uint_to_int(u8a_to_uint(u8a), bit_length)
100
+ end
101
+
102
+ def key?(hash, key)
103
+ if key.instance_of?(String)
104
+ hash.key?(key) || hash.key?(key.to_sym)
105
+ else
106
+ hash.key?(key) || hash.key?(key.to_s)
107
+ end
108
+ end
109
+
110
+ def get(hash, *keys)
111
+ keys.reduce(hash) do |h, key|
112
+ break nil unless h.is_a?(Hash)
113
+
114
+ if key.instance_of?(String)
115
+ h[key] || h[key.to_sym]
116
+ elsif key.instance_of?(Symbol)
117
+ h[key] || h[key.to_s]
118
+ else
119
+ h[key]
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
@@ -1,3 +1,3 @@
1
1
  module ScaleRb
2
- VERSION = '0.4.2'
2
+ VERSION = '0.5.0'
3
3
  end
data/lib/scale_rb.rb CHANGED
@@ -3,32 +3,6 @@
3
3
  require 'scale_rb/version'
4
4
  require 'console'
5
5
 
6
- # scale codec
7
- require 'monkey_patching'
8
- require 'codec'
9
- require 'portable_codec'
10
-
11
- # metadata types, decoding and helpers
12
- require 'metadata/metadata_v9'
13
- require 'metadata/metadata_v10'
14
- require 'metadata/metadata_v11'
15
- require 'metadata/metadata_v12'
16
- require 'metadata/metadata_v13'
17
- require 'metadata/metadata_v14'
18
- require 'metadata/metadata'
19
-
20
- require 'hasher'
21
- require 'storage_helper'
22
-
23
- # get registry from config
24
- require 'registry'
25
-
26
- require 'address'
27
-
28
- # clients
29
- require 'client/http_client'
30
- require 'client/ws_client'
31
-
32
6
  module ScaleRb
33
7
  class << self
34
8
  attr_accessor :logger
@@ -40,3 +14,23 @@ module ScaleRb
40
14
  end
41
15
 
42
16
  ScaleRb.logger = Console
17
+
18
+ require 'scale_rb/utils'
19
+
20
+ require 'type_enforcer'
21
+
22
+ require 'scale_rb/types'
23
+ require 'scale_rb/portable_registry'
24
+ require 'scale_rb/codec'
25
+
26
+ require 'scale_rb/metadata/metadata'
27
+
28
+ require 'scale_rb/hasher'
29
+ require 'scale_rb/storage_helper'
30
+ require 'scale_rb/call_helper'
31
+
32
+ require 'address'
33
+
34
+ # clients
35
+ require 'scale_rb/client/http_client'
36
+ require 'scale_rb/client/ws_client'
@@ -0,0 +1,170 @@
1
+ require_relative 'custom_assign'
2
+ require 'benchmark'
3
+
4
+ # rubocop:disable all
5
+ module TypeEnforcer
6
+
7
+ def self.extended(base)
8
+ base.instance_variable_set(:@type_enforcements, {})
9
+ base.instance_variable_set(:@applying_enforcement, false)
10
+ end
11
+
12
+ def __(method_name, param_types, return_type = nil, level: 1, skip: [])
13
+ return unless type_enforcement_enabled?
14
+
15
+ @type_enforcements[method_name] = {
16
+ params: param_types,
17
+ return: return_type,
18
+ level: level,
19
+ skip: skip
20
+ }
21
+ end
22
+
23
+ def method_added(method_name)
24
+ super
25
+ apply_enforcement(method_name)
26
+ end
27
+
28
+ private
29
+
30
+ def apply_enforcement(method_name)
31
+ return unless type_enforcement_enabled?
32
+ return if @applying_enforcement
33
+ return unless @type_enforcements.key?(method_name)
34
+
35
+ @applying_enforcement = true
36
+ begin
37
+ result = @type_enforcements[method_name]
38
+ if result[:level] > type_enforcement_level
39
+ decorate(method_name, result[:params], result[:return], result[:skip])
40
+ end
41
+ ensure
42
+ @applying_enforcement = false
43
+ end
44
+ end
45
+
46
+ def type_enforcement_enabled?
47
+ return true if ENV['ENABLE_TYPE_ENFORCEMENT'] && ENV['ENABLE_TYPE_ENFORCEMENT'] == 'true'
48
+
49
+ false
50
+ end
51
+
52
+ def type_enforcement_level
53
+ ENV['TYPE_ENFORCEMENT_LEVEL'].to_i || 2
54
+ end
55
+
56
+ def decorate(method_name, param_types, return_type, skip)
57
+ target = self
58
+ original_method = target.instance_method(method_name)
59
+ method_parameters = original_method.parameters
60
+
61
+ # only support positional args and keyword args for now
62
+ # TODO: support splat(rest), double splat(keyrest) args, and block
63
+ target.define_method(method_name) do |*args, **kwargs|
64
+ ScaleRb.logger.debug("----------------------------------------------------------")
65
+ ScaleRb.logger.debug("method: #{method_name}")
66
+ ScaleRb.logger.debug("params: args: #{args}, kwargs: #{kwargs}")
67
+ ScaleRb.logger.debug("param kinds: #{method_parameters}")
68
+ ScaleRb.logger.debug("param types: #{param_types}")
69
+
70
+ validated_args = []
71
+ validated_kwargs = {}
72
+
73
+ # build a hash of param_name => value | default_value
74
+ defaults = method_parameters.each_with_object({}) do |(_param_kind, param_name), memo|
75
+ type = param_types[param_name]
76
+ memo[param_name] = type.value if type.respond_to?(:value)
77
+ end
78
+ assigned_params = build_assigned_params(original_method, defaults, args, kwargs)
79
+ ScaleRb.logger.debug("assigned params: #{assigned_params}")
80
+
81
+ # validate each param
82
+ method_parameters.each do |param_kind, param_name|
83
+ case param_kind
84
+ when :req, :opt
85
+ value = assigned_params[param_name]
86
+ if skip.include?(param_name)
87
+ validated_args << value
88
+ else
89
+ type = param_types[param_name]
90
+ validated_args << type[value]
91
+ end
92
+ when :keyreq, :key
93
+ value = assigned_params[param_name]
94
+ if skip.include?(param_name)
95
+ validated_kwargs[param_name] = value
96
+ else
97
+ type = param_types[param_name]
98
+ validated_kwargs[param_name] = type[value]
99
+ end
100
+ when :rest, :keyrest
101
+ raise NotImplementedError, 'rest and keyrest args not supported'
102
+ end
103
+ end
104
+
105
+ result = original_method.bind(self).call(*validated_args, **validated_kwargs)
106
+
107
+ if skip.include?(:returns) && return_type
108
+ return_type[result]
109
+ else
110
+ result
111
+ end
112
+ end
113
+ end
114
+ end
115
+
116
+ # require 'dry-types'
117
+
118
+ # module Types
119
+ # include Dry.Types()
120
+ # end
121
+
122
+ # class Example
123
+ # extend TypeEnforcer
124
+
125
+ # __ :add, { a: Types::Strict::Integer, b: Types::Strict::Integer }, Types::Strict::Integer
126
+ # def self.add(a, b)
127
+ # a + b
128
+ # end
129
+
130
+ # __ :subtract, { a: Types::Strict::Integer, b: Types::Strict::Integer }, Types::Strict::Integer
131
+ # def subtract(a, b)
132
+ # a - b
133
+ # end
134
+
135
+ # __ :my_method, {
136
+ # a: Types::Strict::Integer,
137
+ # b: Types::Strict::Integer.default(2),
138
+ # c: Types::Strict::Integer,
139
+ # d: Types::Strict::Integer.default(4)
140
+ # }, Types::Strict::String
141
+ # def my_method(a, b = 2, c:, d: 4)
142
+ # "a: #{a}, b: #{b}, c: #{c}, d: #{d}"
143
+ # end
144
+ # end
145
+
146
+ # puts Example.add(1, 2) # => 3
147
+
148
+ # puts Example.new.subtract(3, 1) # => 2
149
+
150
+ # begin
151
+ # puts Example.new.subtract(3, '1')
152
+ # rescue StandardError => e
153
+ # puts e.class # => Dry::Types::ConstraintError
154
+ # puts e.message # => "1" violates constraints (type?(Integer, "1") failed)
155
+ # end
156
+
157
+ # puts Example.new.my_method(5, c: 3) # => "a: 5, b: 2, c: 3, d: 4"
158
+ # puts Example.new.my_method(5, 6, c: 3) # => "a: 5, b: 6, c: 3, d: 4"
159
+ # puts Example.new.my_method(5, 6, c: 3, d: 10) # => "a: 5, b: 6, c: 3, d: 10"
160
+ # begin
161
+ # puts Example.new.my_method(5, 6, c: 3, d: '10')
162
+ # rescue StandardError => e
163
+ # puts e.class # => Dry::Types::ConstraintError
164
+ # puts e.message # => "10" violates constraints (type?(Integer, "10") failed)
165
+ # end
166
+ # begin
167
+ # puts Example.new.my_method(5, 6, d: 10)
168
+ # rescue StandardError => e
169
+ # puts e.message # => Missing required keyword argument: c
170
+ # end
data/scale_rb.gemspec CHANGED
@@ -40,4 +40,7 @@ Gem::Specification.new do |spec|
40
40
  spec.add_dependency 'async-websocket', '~> 0.26.2'
41
41
  # for logger
42
42
  spec.add_dependency 'console'
43
+ # for types restriction
44
+ spec.add_dependency 'dry-struct'
45
+ spec.add_dependency 'dry-types'
43
46
  end