scale_rb 0.3.3 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6df0775bbed0fb5886ee7aba23efa8660dfde37a1f437d1d83958a6cc3f82c1f
4
- data.tar.gz: a6ad3ab392fdaa8b9a3f431f98a6d1e7b099b5ce43cb966e5055267d5515322d
3
+ metadata.gz: 51cde569bc10814d0163c4b972c1d926a5ef69a044d02e88085db5bfdd9119a0
4
+ data.tar.gz: 694a2356b2626d1491c2dbb5cca8207179e614a88579c99bbd43a312c2435f9d
5
5
  SHA512:
6
- metadata.gz: fd7a0fbc2f605c3fef4a0b88e7c8475db9ac7e63223130536fddb786040fc4af79e9306f101697e2ae990f04d913755086df2965e8b922638c7c8498455c0fc8
7
- data.tar.gz: 378a219709b0cce2ea277b3e717bd2b0653984519c7593e5525a841155a47b1478c6a10cebf29480cc862cac78b33287e34c1a9a7f433679a8d9e66cfe0ce356
6
+ metadata.gz: cbe20c371a268df323e64fcd9b142a8d04b87dc00e7079e1fd5e0979cbd4311de10b20687928200c427c407d839ce6ba0c804d5e08c72001a05d16b25b6faee8
7
+ data.tar.gz: 7e36ec4ef1d6e0b5584464498836fa5f19f4f7d194856c15bd25165bc6dd7f9f01e97cf0c736bc734806be83a26cc70b1fcb3b5986f95ba8fde0361dc76227b5
data/.gitignore CHANGED
@@ -11,3 +11,4 @@
11
11
  # rspec failure tracking
12
12
  .rspec_status
13
13
  /metadata
14
+ Gemfile.lock
data/.rspec CHANGED
File without changes
data/.rubocop.yml CHANGED
File without changes
data/.travis.yml CHANGED
File without changes
data/CODE_OF_CONDUCT.md CHANGED
File without changes
data/Gemfile CHANGED
@@ -5,3 +5,4 @@ gemspec
5
5
 
6
6
  gem "rake", "~> 12.0"
7
7
  gem "rspec", "~> 3.0"
8
+ gem 'rubocop', group: 'development', require: false
data/LICENSE.txt CHANGED
File without changes
data/README.md CHANGED
@@ -7,7 +7,7 @@ It is still under heavy development. Use the latest version.
7
7
  Add this line to your application's Gemfile:
8
8
 
9
9
  ```ruby
10
- gem 'scale_rb', '~> 0.3.3'
10
+ gem 'scale_rb', '~> 0.3.5'
11
11
  ```
12
12
 
13
13
  And then execute:
data/Rakefile CHANGED
File without changes
File without changes
@@ -1,15 +1,20 @@
1
1
  require 'scale_rb'
2
2
 
3
+ def fetch_some_storages(client, block_number)
4
+ start_time = Time.now
5
+
6
+ block_hash = client.chain_getBlockHash(block_number)
7
+ metadata = client.get_metadata(block_hash)
8
+ puts "event count: #{client.get_storage('System', 'EventCount', block_hash:, metadata:)}"
9
+ puts "treasury proposal #854: #{client.get_storage('Treasury', 'Proposals', [854], block_hash:, metadata:)}"
10
+ puts "all treasury proposals: #{client.get_storage('Treasury', 'Proposals', block_hash:, metadata:)}"
11
+ puts "child bounties: #{client.get_storage('ChildBounties', 'ChildBounties', [11, 1646], block_hash:, metadata:)}"
12
+
13
+ end_time = Time.now
14
+ puts "Time taken: #{end_time - start_time} seconds"
15
+ end
16
+
3
17
  ScaleRb.logger.level = Logger::DEBUG
4
18
 
5
19
  client = ScaleRb::HttpClient.new('https://polkadot-rpc.dwellir.com')
6
- block_number = 21711742
7
- block_hash = client.chain_getBlockHash(block_number)
8
- metadata = client.get_metadata(block_hash)
9
-
10
- storage_query = ScaleRb::WsClient::StorageQuery.new(
11
- pallet_name: 'System',
12
- storage_name: 'Events',
13
- )
14
- storage = client.get_storage(block_hash, storage_query, metadata)
15
- puts "block #{block_number}(#{block_hash}) has #{storage.length} events"
20
+ fetch_some_storages(client, 21711742)
@@ -2,12 +2,12 @@ require 'scale_rb'
2
2
 
3
3
  # ScaleRb.logger.level = Logger::DEBUG
4
4
 
5
- # Unsubscribe after receiving 5 new heads
5
+ # Unsubscribe after receiving 4 new heads
6
6
  ScaleRb::WsClient.start('wss://polkadot-rpc.dwellir.com') do |client|
7
7
  count = 0
8
8
 
9
9
  subscription_id = client.chain_subscribeNewHead do |head|
10
- count = count + 1
10
+ count += 1
11
11
 
12
12
  if count < 5
13
13
  block_number = head[:number].to_i(16)
@@ -1,43 +1,21 @@
1
1
  require 'scale_rb'
2
2
 
3
- # ScaleRb.logger.level = Logger::DEBUG
3
+ def fetch_some_storages(client, block_number)
4
+ start_time = Time.now
4
5
 
5
- ScaleRb::WsClient.start('wss://polkadot-rpc.dwellir.com') do |client|
6
- block_number = 21711742
7
6
  block_hash = client.chain_getBlockHash(block_number)
8
7
  metadata = client.get_metadata(block_hash)
8
+ puts "event count: #{client.get_storage('System', 'EventCount', block_hash:, metadata:)}"
9
+ puts "treasury proposal #854: #{client.get_storage('Treasury', 'Proposals', [854], block_hash:, metadata:)}"
10
+ puts "all treasury proposals: #{client.get_storage('Treasury', 'Proposals', block_hash:, metadata:)}"
11
+ puts "child bounties: #{client.get_storage('ChildBounties', 'ChildBounties', [11, 1646], block_hash:, metadata:)}"
9
12
 
10
- storage_query = ScaleRb::WsClient::StorageQuery.new(
11
- pallet_name: 'System',
12
- storage_name: 'EventCount',
13
- )
14
- puts "event count: #{client.get_storage(block_hash, storage_query, metadata)}"
15
- # event count: 48
16
-
17
-
18
- storage_query = ScaleRb::WsClient::StorageQuery.new(
19
- pallet_name: 'Treasury',
20
- storage_name: 'Proposals',
21
- key_part1: 854,
22
- )
23
- puts "treasury proposal #854: #{client.get_storage(block_hash, storage_query, metadata)}"
24
- # treasury proposal #854: {:proposer=>"0xb6f0f10eec993f3e6806eb6cc4d2f13d5f5a90a17b855a7bf9847a87e07ee322", :value=>82650000000000, :beneficiary=>"0xb6f0f10eec993f3e6806eb6cc4d2f13d5f5a90a17b855a7bf9847a87e07ee322", :bond=>0}
25
-
26
-
27
- storage_query = ScaleRb::WsClient::StorageQuery.new(
28
- pallet_name: 'Treasury',
29
- storage_name: 'Proposals',
30
- )
31
- puts "all treasury proposals: #{client.get_storage(block_hash, storage_query, metadata)}"
32
- # all treasury proposals: [{:storage_key=>"0x89d139e01a5eb2256f222e5fc5dbe6b388c2f7188c6fdd1dffae2fa0d171f4400c1910093df9204856030000", :storage=>{:proposer=>"0xb6f0f10eec993f3e6806eb6cc4d2f13d5f5a90a17b855a7bf9847a87e07ee322", :value=>82650000000000, :beneficiary=>"0xb6f0f10eec993f3e6806eb6cc4d2f13d5f5a90a17b855a7bf9847a87e07ee322", :bond=>0}}, ...]
13
+ end_time = Time.now
14
+ puts "Time taken: #{end_time - start_time} seconds"
15
+ end
33
16
 
17
+ ScaleRb.logger.level = Logger::DEBUG
34
18
 
35
- storage_query = ScaleRb::WsClient::StorageQuery.new(
36
- pallet_name: 'ChildBounties',
37
- storage_name: 'ChildBounties',
38
- key_part1: 11,
39
- key_part2: 1646
40
- )
41
- puts "child bounties: #{client.get_storage(block_hash, storage_query, metadata)}"
42
- # child bounties: {:parent_bounty=>11, :value=>3791150000000, :fee=>0, :curator_deposit=>0, :status=>{:PendingPayout=>{:curator=>"0xb1725c0de514e0df808b19dbfca26672019ea5f9e2eb69c0055c7f1d01b4f18a", :beneficiary=>"0xb089dedc24a15308874dc862b035d74f2f7b45cad475d6121a2d944921bbe237", :unlock_at=>21703671}}}
19
+ ScaleRb::WsClient.start('wss://polkadot-rpc.dwellir.com') do |client|
20
+ fetch_some_storages(client, 21711742)
43
21
  end
@@ -0,0 +1,9 @@
1
+ require 'scale_rb'
2
+
3
+ begin
4
+ ScaleRb::WsClient.start('wss://polkadot-rpc.dwellir.com') do |_client|
5
+ raise 'MyError'
6
+ end
7
+ rescue StandardError => e
8
+ p e.message # "MyError"
9
+ end
data/lib/address.rb CHANGED
File without changes
@@ -2,37 +2,31 @@ module ScaleRb
2
2
 
3
3
  # This module is used to add extra methods to both the ScaleRb::WsClient ScaleRb::HttpClient
4
4
  module ClientExt
5
- StorageQuery = Struct.new(:pallet_name, :storage_name, :key_part1, :key_part2, keyword_init: true) do
6
- def initialize(pallet_name:, storage_name:, key_part1: nil, key_part2: nil)
7
- super
8
- end
9
- end
10
-
11
5
  # get decoded metadata at block_hash
12
- def get_metadata(block_hash)
6
+ def get_metadata(block_hash = nil)
7
+ block_hash ||= chain_getHead
13
8
  metadata_hex = state_getMetadata(block_hash)
14
9
  ScaleRb::Metadata.decode_metadata(metadata_hex.strip._to_bytes)
15
10
  end
16
11
 
17
12
  # Get decoded storage at block_hash
18
- def get_storage(block_hash, storage_query, metadata = nil)
13
+ def get_storage(pallet_name, storage_name, params = [], block_hash: nil, metadata: nil)
14
+ block_hash ||= chain_getHead
19
15
  metadata ||= get_metadata(block_hash)
20
16
 
21
17
  # storeage item
22
- pallet_name = convert_to_camel_case storage_query.pallet_name
23
- storage_name = convert_to_camel_case storage_query.storage_name
18
+ pallet_name = convert_to_camel_case pallet_name
19
+ storage_name = convert_to_camel_case storage_name
24
20
 
25
21
  # storage param
26
- key = [storage_query.key_part1, storage_query.key_part2].compact
27
- ScaleRb.logger.debug "#{pallet_name}.#{storage_name}(#{key.join(', ')})"
28
- key = key.map { |part_of_key| c(part_of_key) }
29
- ScaleRb.logger.debug "converted key: #{key}"
22
+ ScaleRb.logger.debug "#{pallet_name}.#{storage_name}(#{params.inspect})"
23
+ params = params.map { |param| c(param) }
30
24
 
31
25
  get_storage2(
32
26
  block_hash, # at
33
27
  pallet_name,
34
28
  storage_name,
35
- key,
29
+ params,
36
30
  metadata
37
31
  )
38
32
  end
@@ -102,7 +96,7 @@ module ScaleRb
102
96
  #
103
97
  # key is for the param, value is for the return
104
98
  def get_storage1(block_hash, pallet_name, item_name, key, value, registry)
105
- ScaleRb::logger.debug "get_storage1: #{pallet_name}.#{item_name} key: #{key} value: #{value}"
99
+ ScaleRb.logger.debug "#{pallet_name}.#{item_name}, key: #{key.inspect}, value: #{value}"
106
100
 
107
101
  if key
108
102
  if key[:value].nil? || key[:value].empty?
@@ -138,7 +132,6 @@ module ScaleRb
138
132
  end
139
133
 
140
134
  def get_storage2(block_hash, pallet_name, item_name, params, metadata)
141
- ScaleRb.logger.debug "get_storage2: #{pallet_name}.#{item_name} params: #{params}"
142
135
  raise 'Metadata should not be nil' if metadata.nil?
143
136
 
144
137
  registry = Metadata.build_registry(metadata)
@@ -50,7 +50,7 @@ module ScaleRb
50
50
  end
51
51
 
52
52
  def method_missing(method, *args)
53
- ScaleRb.logger.debug "#{method}(#{args.join(', ')})"
53
+ # ScaleRb.logger.debug "#{method}(#{args.join(', ')})"
54
54
 
55
55
  request(method.to_s, args)
56
56
  end
@@ -1,42 +1,55 @@
1
1
  require 'async'
2
2
  require 'async/websocket/client'
3
3
  require 'async/http/endpoint'
4
- require 'async/queue'
5
- require 'json'
6
4
 
7
5
  require_relative 'client_ext'
8
6
 
9
7
  module ScaleRb
10
8
  class WsClient
11
- def self.start(url)
12
9
 
13
- Sync do |task|
14
- endpoint = Async::HTTP::Endpoint.parse(url, alpn_protocols: Async::HTTP::Protocol::HTTP11.names)
15
-
16
- Async::WebSocket::Client.connect(endpoint) do |connection|
17
- client = WsClient.new(connection)
18
-
19
- recv_task = task.async do
20
- while message = connection.read
21
- data = message.parse
22
- ScaleRb.logger.debug "Received message: #{data}"
23
-
24
- task.async do
25
- client.handle_response(data)
10
+ class << self
11
+ # @param [string] url
12
+ def start(url)
13
+ Sync do
14
+ endpoint = Async::HTTP::Endpoint.parse(url, alpn_protocols: Async::HTTP::Protocol::HTTP11.names)
15
+
16
+ Async::WebSocket::Client.connect(endpoint) do |connection|
17
+ client = WsClient.new(connection)
18
+
19
+ # `recv_task` does not raise errors (subclass of StandardError), so it will not be stopped by any errors.
20
+ recv_task = Async do
21
+ while (message = client.read_message)
22
+ data = parse_message(message)
23
+ next if data.nil?
24
+
25
+ ScaleRb.logger.debug "Response: #{data}"
26
+ Async do
27
+ client.handle_response(data)
28
+ end
26
29
  end
27
30
  end
28
- end
29
31
 
30
- client.supported_methods = client.rpc_methods()[:methods]
31
- yield client
32
+ client.supported_methods = client.rpc_methods()[:methods]
33
+ yield client
32
34
 
33
- recv_task.wait
34
- ensure
35
- recv_task&.stop
35
+ recv_task.wait
36
+ ensure
37
+ recv_task&.stop
38
+ end
36
39
  end
37
- end # Sync
38
40
 
39
- end # start
41
+ end
42
+
43
+ private
44
+
45
+ def parse_message(message)
46
+ message.parse
47
+ rescue StandardError => e
48
+ ScaleRb.logger.error "Error while parsing message: #{e.inspect}, message: #{message}"
49
+ nil
50
+ end
51
+ end
52
+
40
53
  end
41
54
  end
42
55
 
@@ -58,7 +71,7 @@ module ScaleRb
58
71
 
59
72
  def method_missing(method, *args)
60
73
  method = method.to_s
61
- ScaleRb.logger.debug "#{method}(#{args.join(', ')})"
74
+ # ScaleRb.logger.debug "#{method}(#{args.join(', ')})"
62
75
 
63
76
  # why not check 'rpc_methods', because there is no @supported_methods when initializing
64
77
  if method != 'rpc_methods' && !@supported_methods.include?(method)
@@ -68,7 +81,7 @@ module ScaleRb
68
81
  if method.include?('unsubscribe')
69
82
  unsubscribe(method, args[0])
70
83
  elsif method.include?('subscribe')
71
- raise "A subscribe method needs a block" unless block_given?
84
+ raise 'A subscribe method needs a block' unless block_given?
72
85
 
73
86
  subscribe(method, args) do |notification|
74
87
  yield notification[:params][:result]
@@ -101,7 +114,21 @@ module ScaleRb
101
114
  elsif response.key?(:method)
102
115
  @subscription_handler.handle(response)
103
116
  else
104
- puts "Received an unknown message: #{response}"
117
+ ScaleRb.logger.info "Received an unknown response: #{response}"
118
+ end
119
+ rescue StandardError => e
120
+ ScaleRb.logger.error "Error while handling response: #{e.inspect}"
121
+ ScaleRb.logger.debug e.backtrace.join("\n")
122
+ end
123
+
124
+ def read_message
125
+ loop do
126
+ return @connection.read
127
+ rescue StandardError => e
128
+ ScaleRb.logger.error "Error while read message from connection: #{e.inspect}"
129
+ ScaleRb.logger.debug e.backtrace.join("\n")
130
+ sleep 1
131
+ retry
105
132
  end
106
133
  end
107
134
 
@@ -115,7 +142,7 @@ module ScaleRb
115
142
  })
116
143
 
117
144
  request = { jsonrpc: '2.0', id: @request_id, method: method, params: params }
118
- ScaleRb.logger.debug "Sending request: #{request}"
145
+ ScaleRb.logger.debug "Request: #{request}"
119
146
  @connection.write(request.to_json)
120
147
 
121
148
  @request_id += 1
@@ -140,7 +167,7 @@ module ScaleRb
140
167
  callback.call(response)
141
168
  @callbacks.delete(id)
142
169
  else
143
- ScaleRb.logger.debug "Received a message with unknown id: #{response}"
170
+ ScaleRb.logger.info "Received a message with unknown id: #{response}"
144
171
  end
145
172
  end
146
173
  end
data/lib/codec.rb CHANGED
@@ -404,6 +404,8 @@ module ScaleRb
404
404
  end
405
405
 
406
406
  def encode_uint(type, value)
407
+ raise InvalidValueError, "type: #{type}, value: #{value.inspect}" unless value.instance_of?(Integer)
408
+
407
409
  bit_length = type[1..].to_i
408
410
  value._to_bytes(bit_length)._flip
409
411
  end
data/lib/hasher.rb CHANGED
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
data/lib/registry.rb CHANGED
File without changes
@@ -1,3 +1,3 @@
1
1
  module ScaleRb
2
- VERSION = '0.3.3'
2
+ VERSION = '0.4.0'
3
3
  end
data/lib/scale_rb.rb CHANGED
File without changes
@@ -20,12 +20,14 @@ module ScaleRb
20
20
  ]
21
21
  else
22
22
  [
23
- registry[key[:type]]._get(:def)._get(:tuple),
23
+ registry[key[:type]]._get(:def)._get(:tuple).first(key[:value].length),
24
24
  key[:value],
25
- key[:hashers]
25
+ key[:hashers].first(key[:value].length)
26
26
  ]
27
27
  end
28
28
 
29
+ ScaleRb.logger.debug "encode_storage_key: key_values: #{key_values.inspect}, key_types: #{key_types.inspect}, key_hashers: #{key_hashers.inspect}"
30
+
29
31
  if key_types.class != key_values.class || key_types.length != key_values.length
30
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."
31
33
  end
data/scale_rb.gemspec CHANGED
File without changes
data/tea.yaml CHANGED
File without changes
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: scale_rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.3
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aki Wu
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-07-30 00:00:00.000000000 Z
11
+ date: 2024-08-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: base58
@@ -109,7 +109,6 @@ files:
109
109
  - ".travis.yml"
110
110
  - CODE_OF_CONDUCT.md
111
111
  - Gemfile
112
- - Gemfile.lock
113
112
  - LICENSE.txt
114
113
  - README.md
115
114
  - Rakefile
@@ -121,6 +120,7 @@ files:
121
120
  - examples/ws_client_2.rb
122
121
  - examples/ws_client_3.rb
123
122
  - examples/ws_client_4.rb
123
+ - examples/ws_client_error_handling.rb
124
124
  - exe/metadata
125
125
  - lib/address.rb
126
126
  - lib/client/client_ext.rb
@@ -149,7 +149,7 @@ licenses:
149
149
  metadata:
150
150
  bug_tracker_uri: https://github.com/wuminzhe/scale_rb/issues/
151
151
  source_code_uri: https://github.com/wuminzhe/scale_rb.git
152
- post_install_message:
152
+ post_install_message:
153
153
  rdoc_options: []
154
154
  require_paths:
155
155
  - lib
@@ -164,8 +164,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
164
164
  - !ruby/object:Gem::Version
165
165
  version: '0'
166
166
  requirements: []
167
- rubygems_version: 3.4.19
168
- signing_key:
167
+ rubygems_version: 3.3.7
168
+ signing_key:
169
169
  specification_version: 4
170
170
  summary: A Ruby SCALE Codec Library, and, Substrate RPC Client
171
171
  test_files: []
data/Gemfile.lock DELETED
@@ -1,97 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- scale_rb (0.3.3)
5
- async
6
- async-http (~> 0.69.0)
7
- async-websocket (~> 0.26.2)
8
- base58
9
- blake2b_rs (~> 0.1.4)
10
- xxhash
11
-
12
- GEM
13
- remote: https://rubygems.org/
14
- specs:
15
- async (2.14.2)
16
- console (~> 1.25, >= 1.25.2)
17
- fiber-annotation
18
- io-event (~> 1.6, >= 1.6.5)
19
- async-http (0.69.0)
20
- async (>= 2.10.2)
21
- async-pool (~> 0.7)
22
- io-endpoint (~> 0.11)
23
- io-stream (~> 0.4)
24
- protocol-http (~> 0.26)
25
- protocol-http1 (~> 0.19)
26
- protocol-http2 (~> 0.18)
27
- traces (>= 0.10)
28
- async-pool (0.7.0)
29
- async (>= 1.25)
30
- async-websocket (0.26.2)
31
- async-http (~> 0.54)
32
- protocol-rack (~> 0.5)
33
- protocol-websocket (~> 0.14)
34
- base58 (0.2.3)
35
- blake2b_rs (0.1.4)
36
- ffi (~> 1.0)
37
- thermite (~> 0)
38
- console (1.27.0)
39
- fiber-annotation
40
- fiber-local (~> 1.1)
41
- json
42
- diff-lcs (1.5.0)
43
- ffi (1.17.0)
44
- fiber-annotation (0.2.0)
45
- fiber-local (1.1.0)
46
- fiber-storage
47
- fiber-storage (0.1.2)
48
- io-endpoint (0.13.0)
49
- io-event (1.6.5)
50
- io-stream (0.4.0)
51
- json (2.7.2)
52
- minitar (0.9)
53
- protocol-hpack (1.5.0)
54
- protocol-http (0.28.1)
55
- protocol-http1 (0.19.1)
56
- protocol-http (~> 0.22)
57
- protocol-http2 (0.18.0)
58
- protocol-hpack (~> 1.4)
59
- protocol-http (~> 0.18)
60
- protocol-rack (0.6.0)
61
- protocol-http (~> 0.23)
62
- rack (>= 1.0)
63
- protocol-websocket (0.15.0)
64
- protocol-http (~> 0.2)
65
- rack (3.1.7)
66
- rake (12.3.3)
67
- rspec (3.11.0)
68
- rspec-core (~> 3.11.0)
69
- rspec-expectations (~> 3.11.0)
70
- rspec-mocks (~> 3.11.0)
71
- rspec-core (3.11.0)
72
- rspec-support (~> 3.11.0)
73
- rspec-expectations (3.11.0)
74
- diff-lcs (>= 1.2.0, < 2.0)
75
- rspec-support (~> 3.11.0)
76
- rspec-mocks (3.11.1)
77
- diff-lcs (>= 1.2.0, < 2.0)
78
- rspec-support (~> 3.11.0)
79
- rspec-support (3.11.0)
80
- thermite (0.13.0)
81
- minitar (~> 0.5)
82
- rake (>= 10)
83
- tomlrb (~> 1.2)
84
- tomlrb (1.3.0)
85
- traces (0.11.1)
86
- xxhash (0.5.0)
87
-
88
- PLATFORMS
89
- ruby
90
-
91
- DEPENDENCIES
92
- rake (~> 12.0)
93
- rspec (~> 3.0)
94
- scale_rb!
95
-
96
- BUNDLED WITH
97
- 2.1.4