substrate_client.rb 0.1.7 → 0.1.8

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: af04674d5e75dbafa82eade829b5798fe87e015a51a8edb63d7845cdd38c27b9
4
- data.tar.gz: 8afa612ea099169396f4ef3155c5158ba188b09ff602b5d515fcf6deadfe5739
3
+ metadata.gz: bb233970cdc1b51057d436d0ffc2ae411b99ce3c2e5c51ba3dfb5dcdae0fc960
4
+ data.tar.gz: 35750c213d2fbf7ae223cdd7b9f7a8bc40c7c7b8fab6a09603a0604db6533562
5
5
  SHA512:
6
- metadata.gz: f5c5379451ece56ac1a868092446f66929c83eafb36dace7007b2f479616c3bb2c7a9aff2f77fd1ef25b0bdf5ebb0a431b17308d892ecc4ad3cad54a03700a2e
7
- data.tar.gz: e179bd7fb5a421f1de150463d09a04ad369a15eb1f1027c5c21d9fdc837dda5ebed1671cac093e2a584b9c356ad3112881b990d0763394d533ddb7d3e22c7f00
6
+ metadata.gz: 5731212fc4ad5791bb607f132e660b89f7ff652937029e986040aeee85335939d5a5edf9dd9a21a91f346c2b88711657586747a01727f35eae8318b9b944a62f
7
+ data.tar.gz: 69514b6b9b3eb085f7fefc672dff0b3a85cdfb64babfad28f6ab788f98ee950e2a2e9fcf2e89ab448b7e8c7137da20ba6ec0e68d375ece1b9c41e5f77d3d6964
@@ -1,16 +1,16 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- substrate_client.rb (0.1.7)
4
+ substrate_client.rb (0.1.8)
5
5
  activesupport (~> 5.2.4)
6
- eventmachine (~> 1.2.7)
7
- faye-websocket (~> 0.10.9)
8
- scale.rb (~> 0.2.5)
6
+ kontena-websocket-client (~> 0.1.1)
7
+ scale.rb (~> 0.2.11)
8
+ substrate_common.rb (~> 0.1.9)
9
9
 
10
10
  GEM
11
11
  remote: https://rubygems.org/
12
12
  specs:
13
- activesupport (5.2.4.3)
13
+ activesupport (5.2.4.4)
14
14
  concurrent-ruby (~> 1.0, >= 1.0.2)
15
15
  i18n (>= 0.7, < 2)
16
16
  minitest (~> 5.1)
@@ -18,17 +18,15 @@ GEM
18
18
  base58 (0.2.3)
19
19
  blake2b (0.10.0)
20
20
  coderay (1.1.2)
21
- concurrent-ruby (1.1.6)
21
+ concurrent-ruby (1.1.7)
22
22
  diff-lcs (1.3)
23
- eventmachine (1.2.7)
24
- faye-websocket (0.10.9)
25
- eventmachine (>= 0.12.0)
26
- websocket-driver (>= 0.5.1)
27
- i18n (1.8.2)
23
+ i18n (1.8.5)
28
24
  concurrent-ruby (~> 1.0)
29
- json (2.3.0)
25
+ json (2.3.1)
26
+ kontena-websocket-client (0.1.1)
27
+ websocket-driver (~> 0.6.5)
30
28
  method_source (0.9.2)
31
- minitest (5.14.1)
29
+ minitest (5.14.2)
32
30
  pry (0.12.2)
33
31
  coderay (~> 1.1.0)
34
32
  method_source (~> 0.9.0)
@@ -46,12 +44,12 @@ GEM
46
44
  diff-lcs (>= 1.2.0, < 2.0)
47
45
  rspec-support (~> 3.9.0)
48
46
  rspec-support (3.9.2)
49
- scale.rb (0.2.5)
47
+ scale.rb (0.2.11)
50
48
  activesupport (>= 4.0.0)
51
49
  json (~> 2.3.0)
52
- substrate_common.rb (~> 0.1.8)
50
+ substrate_common.rb (~> 0.1.9)
53
51
  thor (~> 0.19.0)
54
- substrate_common.rb (0.1.8)
52
+ substrate_common.rb (0.1.9)
55
53
  base58
56
54
  blake2b
57
55
  xxhash
@@ -59,9 +57,9 @@ GEM
59
57
  thread_safe (0.3.6)
60
58
  tzinfo (1.2.7)
61
59
  thread_safe (~> 0.1)
62
- websocket-driver (0.7.1)
60
+ websocket-driver (0.6.5)
63
61
  websocket-extensions (>= 0.1.0)
64
- websocket-extensions (0.1.4)
62
+ websocket-extensions (0.1.5)
65
63
  xxhash (0.4.0)
66
64
 
67
65
  PLATFORMS
data/README.md CHANGED
@@ -22,205 +22,116 @@ Or install it yourself as:
22
22
 
23
23
  ### Supported rpc methods
24
24
 
25
- #### rpc method list
26
-
27
25
  ```ruby
28
26
  require "substrate_client"
29
27
 
30
28
  client = SubstrateClient.new("wss://kusama-rpc.polkadot.io/")
31
- client.method_list do |methods|
32
- p methods
33
- end
29
+ client.methods
30
+ ```
31
+ returns like:
32
+ ```shell
33
+ [
34
+ "account_nextIndex",
35
+ "author_hasKey",
36
+ ...
37
+ "chain_getBlock",
38
+ "chain_getBlockHash",
39
+ ...
40
+ ]
34
41
  ```
35
- The rpc methods can be dynamically called by its name, so the methods returned by this method can all be used.
36
-
37
-
38
-
39
- #### rpc methods
40
-
41
- But, in order to show the parameters more clearly, some important or frequently used methods are hard-coded:
42
-
43
- - chain_get_finalised_head(&callback)
44
-
45
- Get hash of the last finalized block in the canon chain
46
-
47
- ```ruby
48
- client.chain_get_finalised_head do |head|
49
- p head
50
- end
51
- ```
52
-
53
-
54
-
55
- - chain_get_head(&callback)
56
-
57
- Retrieves the header
58
-
59
-
60
-
61
- - chain_get_header(block_hash = nil, &callback)
62
-
63
- Retrieves the header for a specific block
64
-
65
-
66
-
67
- - chain_get_block(block_hash = nil, &callback)
68
-
69
- Get header and body of a relay chain block
70
-
71
-
72
-
73
- - chain_get_block_hash(block_id, &callback)
74
-
75
- Get the block hash for a specific block
76
-
77
-
78
-
79
- - chain_get_runtime_version(block_hash = nil, &callback)
80
-
81
- Get the runtime version for a specific block
82
-
83
-
84
-
85
- - state_get_metadata(block_hash = nil, &callback)
86
-
87
- Returns the runtime metadata by block
88
-
89
-
90
-
91
- - state_get_storage(storage_key, block_hash = nil, &callback)
92
-
93
- Retrieves the storage for a key
94
-
95
-
96
-
97
- - system_name(&callback)
98
-
99
-
100
-
101
- - system_version(&callback)
102
-
103
-
104
-
105
- - chain_subscribe_all_heads(&callback)
106
-
107
- Retrieves the newest header via subscription. This will return data continuously until you unsubscribe the subscription.
108
-
109
- ```ruby
110
- subscription = client.chain_subscribe_all_heads do |data|
111
- p data
112
- end
113
- ```
114
-
115
- - chain_unsubscribe_all_heads(subscription)
116
-
117
- Unsubscribe newest header subscription.
118
-
119
- ```ruby
120
- client.chain_unsubscribe_all_heads(subscription)
121
- ```
122
-
123
-
124
-
125
- - chain_subscribe_new_heads(&callback)
126
-
127
- Retrieves the best header via subscription. This will return data continuously until you unsubscribe the subscription.
128
-
129
- - chain_unsubscribe_new_heads(subscription)
130
-
131
- Unsubscribe the best header subscription.
132
-
133
-
134
-
135
-
136
- - chain_subscribe_finalized_heads(&callback)
137
-
138
- Retrieves the best finalized header via subscription. This will return data continuously until you unsubscribe the subscription.
139
-
140
- - chain_unsubscribe_finalized_heads(subscription)
141
-
142
- Unsubscribe the best finalized header subscription.
143
-
144
-
145
-
146
- - state_subscribe_runtime_version(&callback)
147
-
148
- Retrieves the runtime version via subscription.
149
-
150
- - state_unsubscribe_runtime_version(subscription)
151
-
152
- Unsubscribe the runtime version subscription.
153
-
154
-
155
-
156
- - state_subscribe_storage(keys, &callback)
157
-
158
- Subscribes to storage changes for the provided keys until unsubscribe.
159
42
 
160
- ```ruby
161
- subscription = client.state_subscribe_storage ["0x26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7"] do |data|
162
- p data
163
- end
164
- ```
43
+ The rpc methods can be dynamically called by its name, so you can call it like:
165
44
 
166
- - state_unsubscribe_storage(subscription)
45
+ ```ruby
46
+ client.chain_getBlockHash(1024)
47
+ ```
167
48
 
168
- Unsubscribe storage changes.
169
-
170
-
171
- ### Cutom methods based on rpc methods
49
+ ### Origin rpc methods
50
+
51
+ - `account_nextIndex`
52
+ * `author_hasKey`
53
+ * `author_hasSessionKeys`
54
+ * `author_insertKey`
55
+ * `author_pendingExtrinsics`
56
+ * `author_removeExtrinsic`
57
+ * `author_rotateKeys`
58
+ * `author_submitExtrinsic`
59
+ * `babe_epochAuthorship`
60
+ * `chain_getBlock(block_hash = nil)`
61
+ * `chain_getBlockHash(block_id)`
62
+ * `chain_getFinalisedHead`
63
+ * `chain_getFinalizedHead`
64
+ * `chain_getHead`
65
+ * `chain_getHeader(block_hash = nil)`
66
+ * `chain_getRuntimeVersion(block_hash = nil)`
67
+ * `childstate_getKeys`
68
+ * `childstate_getStorage`
69
+ * `childstate_getStorageHash`
70
+ * `childstate_getStorageSize`
71
+ * `grandpa_roundState`
72
+ * `offchain_localStorageGet`
73
+ * `offchain_localStorageSet`
74
+ * `payment_queryInfo`
75
+ * `state_call`
76
+ * `state_callAt`
77
+ * `state_getKeys`
78
+ * `state_getKeysPaged`
79
+ * `state_getKeysPagedAt`
80
+ * `state_getMetadata(block_hash = nil)`
81
+ * `state_getPairs`
82
+ * `state_getReadProof`
83
+ * `state_getRuntimeVersion`
84
+ * `state_getStorage(storage_key, block_hash = nil)`
85
+ * `state_getStorageAt`
86
+ * `state_getStorageHash`
87
+ * `state_getStorageHashAt`
88
+ * `state_getStorageSize`
89
+ * `state_getStorageSizeAt`
90
+ * `state_queryStorage`
91
+ * `state_queryStorageAt`
92
+ * `system_accountNextIndex`
93
+ * `system_addReservedPeer`
94
+ * `system_chain`
95
+ * `system_chainType`
96
+ * `system_dryRun`
97
+ * `system_dryRunAt`
98
+ * `system_health`
99
+ * `system_localListenAddresses`
100
+ * `system_localPeerId`
101
+ * `system_name`
102
+ * `system_networkState`
103
+ * `system_nodeRoles`
104
+ * `system_peers`
105
+ * `system_properties`
106
+ * `system_removeReservedPeer`
107
+ - `system_version`
108
+
109
+ ### Wrap methods
172
110
 
173
111
  These methods will encode the parameters and decode the returned data
174
112
 
175
- - get_block_number(block_hash, &callback)
176
-
177
- - get_metadata(block_hash, &callback)
113
+ - `get_block_number(block_hash)`
178
114
 
179
- - get_block(block_hash=nil, &callback)
115
+ - `get_metadata(block_hash)`
180
116
 
181
- - get_block_events(block_hash, &callback)
117
+ - `get_block(block_hash=nil)`
182
118
 
183
- - subscribe_block_events(&callback, &callback)
119
+ - `get_block_events(block_hash)`
184
120
 
185
- - get_storage(module_name, storage_name, params = nil, block_hash = nil, &callback)
121
+ - `get_storage(module_name, storage_name, params = nil, block_hash = nil)`
186
122
 
187
123
  ```ruby
188
- client.get_storage("Balances", "TotalIssuance", nil, nil) do |storage|
189
- p storage
190
- end
191
-
192
- client.get_storage("System", "Account", ["0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"], nil) do |storage|
193
- p storage
194
- end
195
-
196
- client.get_storage("ImOnline", "AuthoredBlocks", [2818, "0x749ddc93a65dfec3af27cc7478212cb7d4b0c0357fef35a0163966ab5333b757"], nil) do |storage|
197
- p storage
198
- end
124
+ client.get_storage("Balances", "TotalIssuance", nil, nil)
125
+ client.get_storage("System", "Account", ["0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"], nil)
126
+ client.get_storage("ImOnline", "AuthoredBlocks", [2818, "0x749ddc93a65dfec3af27cc7478212cb7d4b0c0357fef35a0163966ab5333b757"], nil)
199
127
  ```
200
-
201
- - compose_call(module_name, call_name, params, block_hash=nil, &callback)
128
+
129
+ - `compose_call(module_name, call_name, params, block_hash=nil)`
202
130
 
203
131
  ```ruby
204
- compose_call "Balances", "Transfer", { dest: "0x586cb27c291c813ce74e86a60dad270609abf2fc8bee107e44a80ac00225c409", value: 1_000_000_000_000 }, nil do |hex|
205
- p hex
206
- end
132
+ client.compose_call "Balances", "Transfer", { dest: "0x586cb27c291c813ce74e86a60dad270609abf2fc8bee107e44a80ac00225c409", value: 1_000_000_000_000 }, nil
207
133
  ```
208
134
 
209
-
210
-
211
- ## Synchronized client
212
-
213
- There is also a synchronized version of the client, which is disconnected every time the data is retrieved. It is very convenient in some situations. But this version cannot use the subscription interface.
214
-
215
- ```ruby
216
- client = SubstrateClientSync.new "wss://kusama-rpc.polkadot.io/"
217
- p client.method_list
218
- p client.chain_get_head
219
- p client.chain_get_finalised_head
220
- p client.chain_get_header(block_hash = nil)
221
- ...
222
- ```
223
-
224
135
  ## Docker
225
136
 
226
137
  1. update to latest image
@@ -253,22 +164,13 @@ p client.chain_get_header(block_hash = nil)
253
164
  /usr/src/app # ./bin/console
254
165
  [1] pry(main)> client = SubstrateClient.new("wss://kusama-rpc.polkadot.io/")
255
166
  => #<SubstrateClient:0x000055a78f124f58 ...
256
- [2] pry(main)> client.method_list do |methods| p methods end
167
+ [2] pry(main)> client.methods
257
168
  => ...
258
- [3] pry(main)> subscription = client.chain_subscribe_new_heads do |data| p data end
259
- ...
260
- ```
261
-
262
- 5. Or, SubstrateClientSync:
263
-
264
- ```shell
265
- /usr/src/app # ./bin/console
266
- [1] pry(main)> client = SubstrateClientSync.new("wss://kusama-rpc.polkadot.io/", spec_name: "kusama")
267
- => #<SubstrateClientSync:0x000055a78edfd6e0 @request_id=1, @spec_name="kusama", @url="wss://kusama-rpc.polkadot.io/">
268
- [2] pry(main)> client.chain_get_head
169
+ [3] pry(main)> client.chain_getHead
269
170
  => "0xb3c3a220d4639b7c62f179f534b3a66336a115ebc18f13db053f0c57437c45fc"
270
171
  ```
271
172
 
173
+
272
174
  ## Development
273
175
 
274
176
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -3,10 +3,10 @@
3
3
  require "substrate_client"
4
4
  require "json"
5
5
 
6
- url = ARGV[0] || "wss://kusama-rpc.polkadot.io/"
7
- client = SubstrateClientSync.new(url)
6
+ url = ARGV[0] || "wss://kusama-rpc.polkadot.io"
7
+ client = SubstrateClient.new(url)
8
8
 
9
- block_hash = ARGV[1] || client.chain_get_finalised_head
9
+ block_hash = ARGV[1] || client.chain_getFinalisedHead
10
10
 
11
11
  metadata = client.get_metadata(block_hash)
12
12
  puts JSON.pretty_generate(metadata.value.to_human)
@@ -1,7 +1,7 @@
1
1
 
2
2
  class SubstrateClient::Helper
3
3
  class << self
4
- def generate_storage_hash_from_metadata(metadata, module_name, storage_name, params = nil)
4
+ def generate_storage_key_from_metadata(metadata, module_name, storage_name, params = nil)
5
5
  # find the storage item from metadata
6
6
  metadata_modules = metadata.value.value[:metadata][:modules]
7
7
  metadata_module = metadata_modules.detect { |mm| mm[:name] == module_name }
@@ -18,7 +18,8 @@ class SubstrateClient::Helper
18
18
  return_type = map[:value]
19
19
  # TODO: decode to account id if param is address
20
20
  # params[0] = decode(params[0]) if map[:key] == "AccountId"
21
- params[0] = Scale::Types.get(map[:key]).new(params[0]).encode
21
+ type = Scale::Types.get(map[:key])
22
+ params[0] = type.new(params[0]).encode
22
23
  elsif map = storage_item[:type][:DoubleMap]
23
24
  raise "Storage call of type \"DoubleMapType\" requires 2 parameters" if params.nil? || params.length != 2
24
25
 
@@ -31,7 +32,7 @@ class SubstrateClient::Helper
31
32
  raise NotImplementedError
32
33
  end
33
34
 
34
- storage_hash = generate_storage_hash(
35
+ storage_key = generate_storage_key(
35
36
  module_name,
36
37
  storage_name,
37
38
  params,
@@ -39,12 +40,12 @@ class SubstrateClient::Helper
39
40
  hasher2,
40
41
  metadata.value.value[:metadata][:version]
41
42
  )
42
- [storage_hash, return_type]
43
+ [storage_key, return_type]
43
44
  end
44
45
 
45
- def generate_storage_hash(module_name, storage_name, params = nil, hasher = nil, hasher2 = nil, metadata_version = nil)
46
+ def generate_storage_key(module_name, storage_name, params = nil, hasher = nil, hasher2 = nil, metadata_version = nil)
46
47
  if metadata_version and metadata_version >= 9
47
- storage_hash = Crypto.twox128(module_name) + Crypto.twox128(storage_name)
48
+ storage_key = Crypto.twox128(module_name) + Crypto.twox128(storage_name)
48
49
 
49
50
  params&.each_with_index do |param, index|
50
51
  if index == 0
@@ -57,22 +58,22 @@ class SubstrateClient::Helper
57
58
 
58
59
  param_key = param.hex_to_bytes
59
60
  param_hasher = "Twox128" if param_hasher.nil?
60
- storage_hash += Crypto.send param_hasher.underscore, param_key
61
+ storage_key += Crypto.send param_hasher.underscore, param_key
61
62
  end
62
63
 
63
- "0x#{storage_hash}"
64
+ "0x#{storage_key}"
64
65
  else
65
66
  # TODO: add test
66
- storage_hash = module_name + " " + storage_name
67
+ storage_key = module_name + " " + storage_name
67
68
 
68
69
  unless params.nil?
69
70
  params = [params] if params.class != ::Array
70
71
  params_key = params.join("")
71
72
  hasher = "Twox128" if hasher.nil?
72
- storage_hash += params_key.hex_to_bytes.bytes_to_utf8
73
+ storage_key += params_key.hex_to_bytes.bytes_to_utf8
73
74
  end
74
75
 
75
- "0x#{Crypto.send( hasher.underscore, storage_hash )}"
76
+ "0x#{Crypto.send( hasher.underscore, storage_key )}"
76
77
  end
77
78
  end
78
79
 
@@ -114,16 +115,5 @@ class SubstrateClient::Helper
114
115
  block
115
116
  end
116
117
 
117
- # chain_unsubscribe_runtime_version
118
- # =>
119
- # chain_unsubscribeRuntimeVersion
120
- def real_method_name(method_name)
121
- segments = method_name.to_s.split("_")
122
- if segments.length == 1
123
- segments[0]
124
- else
125
- segments[0] + "_" + segments[1] + segments[2..].map(&:capitalize).join
126
- end
127
- end
128
118
  end
129
119
  end
@@ -5,40 +5,42 @@ require "scale.rb"
5
5
  require "json"
6
6
  require "active_support"
7
7
  require "active_support/core_ext/string"
8
- require "websocket"
9
8
  require "helper"
10
- require "timeout_queue"
11
- require "substrate_client_sync"
9
+ require 'kontena-websocket-client'
10
+
11
+ def ws_request(url, payload)
12
+ result = nil
13
+ Kontena::Websocket::Client.connect(url, {}) do |client|
14
+ client.send(payload.to_json)
15
+
16
+ client.read do |message|
17
+ result = JSON.parse message
18
+ client.close(1000)
19
+ end
20
+ end
21
+
22
+ return result
23
+ rescue Kontena::Websocket::CloseError => e
24
+ raise SubstrateClient::WebsocketError, e.reason
25
+ rescue Kontena::Websocket::Error => e
26
+ raise SubstrateClient::WebsocketError, e.reason
27
+ end
12
28
 
13
29
  class SubstrateClient
30
+ class WebsocketError < StandardError; end
14
31
  class RpcError < StandardError; end
15
32
  class RpcTimeout < StandardError; end
16
- class << self
17
- attr_accessor :logger
18
- end
19
- SubstrateClient.logger = Logger.new(STDOUT)
20
- SubstrateClient.logger.level = Logger::INFO
21
33
 
22
34
  attr_accessor :spec_name, :spec_version, :metadata
23
- attr_accessor :ws
24
35
 
25
- def initialize(url, spec_name: nil, onopen: nil)
36
+ def initialize(url, spec_name: nil)
26
37
  @url = url
27
38
  @request_id = 1
28
39
  @spec_name = spec_name
29
- @onopen = onopen
30
- Scale::TypeRegistry.instance.load(spec_name)
31
-
32
- init_ws
33
-
34
- at_exit { self.close }
35
- end
36
-
37
- def close
38
- @ws.close
40
+ Scale::TypeRegistry.instance.load(spec_name: spec_name)
39
41
  end
40
42
 
41
- def request(method, params, callback: nil, subscription_callback: nil)
43
+ def request(method, params)
42
44
  payload = {
43
45
  "jsonrpc" => "2.0",
44
46
  "method" => method,
@@ -46,274 +48,98 @@ class SubstrateClient
46
48
  "id" => @request_id
47
49
  }
48
50
 
49
- while @callbacks.nil?
50
- sleep(1)
51
+ data = ws_request(@url, payload)
52
+ if data["error"]
53
+ raise RpcError, data["error"]
54
+ else
55
+ data["result"]
51
56
  end
52
-
53
- @callbacks[@request_id] = proc do |data|
54
- if not subscription_callback.nil? && data["result"]
55
- @subscription_callbacks[data["result"]] = subscription_callback
56
- end
57
-
58
- callback.call data if callback
59
- end
60
- @ws.send(payload.to_json)
61
- @request_id += 1
62
57
  end
63
58
 
64
- def init_runtime(block_hash=nil, &callback)
59
+ def init_runtime(block_hash=nil)
65
60
  # set current runtime spec version
66
- self.state_get_runtime_version(block_hash) do |runtime_version|
67
- @spec_version = runtime_version["specVersion"]
68
- Scale::TypeRegistry.instance.spec_version = @spec_version
69
-
70
- # set current metadata
71
- self.get_metadata(block_hash) do |metadata|
72
- @metadata = metadata
73
- Scale::TypeRegistry.instance.metadata = @metadata.value
74
- callback.call
75
- end
76
- end
77
- end
61
+ runtime_version = self.state_getRuntimeVersion(block_hash)
62
+ @spec_version = runtime_version["specVersion"]
63
+ Scale::TypeRegistry.instance.spec_version = @spec_version
78
64
 
79
- def do_init_runtime(block_hash, &callback)
65
+ # set current metadata
66
+ @metadata = self.get_metadata(block_hash)
67
+ Scale::TypeRegistry.instance.metadata = @metadata.value
68
+ true
80
69
  end
81
70
 
82
- def invoke(method, params, callback)
83
- request method, params, callback: proc { |data|
84
- if data["error"]
85
- Thread.main.raise RpcError, data["error"]
86
- else
87
- callback.call data["result"] unless callback.nil?
88
- end
89
- }
90
- end
91
-
92
- def rpc_method(method_name)
93
- Helper.real_method_name(method_name.to_s)
71
+ def invoke(method, *params)
72
+ # params.reject! { |param| param.nil? }
73
+ request(method, params)
94
74
  end
95
75
 
96
76
  # ################################################
97
77
  # origin rpc methods
98
78
  # ################################################
99
- def method_missing(method, args, &callback)
100
- rpc_method = Helper.real_method_name(method)
101
- invoke rpc_method, args, callback
102
- end
103
-
104
- def state_get_runtime_version(block_hash=nil, &callback)
105
- invoke rpc_method(__method__), [ block_hash ], callback
106
- end
107
-
108
- def rpc_methods(&callback)
109
- invoke rpc_method(__method__), [], callback
110
- end
111
-
112
- def chain_get_head(&callback)
113
- invoke rpc_method(__method__), [], callback
114
- end
115
-
116
- def chain_get_finalised_head(&callback)
117
- invoke rpc_method(__method__), [], callback
118
- end
119
-
120
- def chain_get_header(block_hash = nil, &callback)
121
- invoke rpc_method(__method__), [ block_hash ], callback
122
- end
123
-
124
- def chain_get_block(block_hash = nil, &callback)
125
- invoke rpc_method(__method__), [ block_hash ], callback
126
- end
127
-
128
- def chain_get_block_hash(block_id, &callback)
129
- invoke rpc_method(__method__), [ block_id ], callback
130
- end
131
-
132
- def chain_get_runtime_version(block_hash = nil, &callback)
133
- invoke rpc_method(__method__), [ block_hash ], callback
134
- end
135
-
136
- def state_get_metadata(block_hash = nil, &callback)
137
- invoke rpc_method(__method__), [ block_hash ], callback
138
- end
139
-
140
- def state_get_storage(storage_key, block_hash = nil, &callback)
141
- invoke rpc_method(__method__), [ storage_key, block_hash ], callback
142
- end
143
-
144
- def system_name(&callback)
145
- invoke rpc_method(__method__), [], callback
146
- end
147
-
148
- def system_version(&callback)
149
- invoke rpc_method(__method__), [], callback
150
- end
151
-
152
- def chain_subscribe_all_heads(&callback)
153
- request rpc_method(__method__), [], subscription_callback: callback
154
- end
155
-
156
- def chain_unsubscribe_all_heads(subscription)
157
- invoke rpc_method(__method__), [ subscription ], nil
158
- end
159
-
160
- def chain_subscribe_new_heads(&callback)
161
- request rpc_method(__method__), [], subscription_callback: callback
162
- end
163
-
164
- def chain_unsubscribe_new_heads(subscription)
165
- invoke rpc_method(__method__), [ subscription ], nil
166
- end
167
-
168
- def chain_subscribe_finalized_heads(&callback)
169
- request rpc_method(__method__), [], subscription_callback: callback
170
- end
171
-
172
- def chain_unsubscribe_finalized_heads(subscription)
173
- invoke rpc_method(__method__), [ subscription ], nil
174
- end
175
-
176
- def state_subscribe_runtime_version(&callback)
177
- request rpc_method(__method__), [], subscription_callback: callback
178
- end
179
-
180
- def state_unsubscribe_runtime_version(subscription)
181
- invoke rpc_method(__method__), [ subscription ], nil
182
- end
183
-
184
- def state_subscribe_storage(keys, &callback)
185
- request rpc_method(__method__), [keys], subscription_callback: callback
186
- end
187
-
188
- def state_unsubscribe_storage(subscription)
189
- invoke rpc_method(__method__), [ subscription ], nil
79
+ def method_missing(method, *args)
80
+ invoke method, *args
190
81
  end
191
82
 
192
83
  # ################################################
193
84
  # custom methods based on origin rpc methods
194
85
  # ################################################
195
- def method_list(&callback)
196
- self.rpc_methods do |result|
197
- callback.call result["methods"].map(&:underscore)
198
- end
199
- end
200
-
201
- def get_block_number(block_hash, &callback)
202
- self.chain_get_header(block_hash) do |header|
203
- callback.call header["number"].to_i(16)
204
- end
86
+ def methods
87
+ invoke("rpc_methods")["methods"]
205
88
  end
206
89
 
207
- def get_metadata(block_hash=nil, &callback)
208
- self.state_get_metadata(block_hash) do |hex|
209
- callback.call Scale::Types::Metadata.decode(Scale::Bytes.new(hex))
210
- end
90
+ def get_block_number(block_hash)
91
+ header = self.chain_getHeader(block_hash)
92
+ header["number"].to_i(16)
211
93
  end
212
94
 
213
- def get_block(block_hash=nil, &callback)
214
- self.init_runtime block_hash do
215
- self.chain_get_block(block_hash) do |block|
216
- block = Helper.decode_block block
217
- callback.call block
218
- end
219
- end
95
+ def get_metadata(block_hash=nil)
96
+ hex = self.state_getMetadata(block_hash)
97
+ Scale::Types::Metadata.decode(Scale::Bytes.new(hex))
220
98
  end
221
99
 
222
- def get_block_events(block_hash=nil, &callback)
223
- self.init_runtime(block_hash) do
224
- storage_key = "0x26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7"
225
- self.state_get_storage storage_key, block_hash do |events_data|
226
- scale_bytes = Scale::Bytes.new(events_data)
227
- events = Scale::Types.get("Vec<EventRecord>").decode(scale_bytes).to_human
228
- callback.call events
229
- end
230
- end
100
+ def get_block(block_hash=nil)
101
+ self.init_runtime(block_hash)
102
+ block = self.chain_getBlock(block_hash)
103
+ SubstrateClient::Helper.decode_block(block)
231
104
  end
232
105
 
233
- def subscribe_block_events(&callback)
234
- self.chain_subscribe_finalized_heads do |data|
106
+ def get_block_events(block_hash=nil)
107
+ self.init_runtime(block_hash)
235
108
 
236
- block_number = data["params"]["result"]["number"].to_i(16) - 1
237
- block_hash = data["params"]["result"]["parentHash"]
109
+ storage_key = "0x26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7"
110
+ events_data = state_getStorage storage_key, block_hash
238
111
 
239
- EM.defer(
240
-
241
- proc {
242
- self.get_block_events block_hash do |events|
243
- begin
244
- result = { block_number: block_number, events: events }
245
- callback.call result
246
- rescue => ex
247
- SubstrateClient.logger.error ex.message
248
- SubstrateClient.logger.error ex.backtrace.join("\n")
249
- end
250
- end
251
- },
112
+ scale_bytes = Scale::Bytes.new(events_data)
113
+ Scale::Types.get("Vec<EventRecord>").decode(scale_bytes).to_human
114
+ end
252
115
 
253
- proc { |result|
254
- },
116
+ # Plain: client.get_storage("Sudo", "Key")
117
+ # Plain: client.get_storage("Balances", "TotalIssuance")
118
+ # Map: client.get_storage("System", "Account", ["0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"])
119
+ # DoubleMap: client.get_storage("ImOnline", "AuthoredBlocks", [2818, "0x749ddc93a65dfec3af27cc7478212cb7d4b0c0357fef35a0163966ab5333b757"])
120
+ def get_storage(module_name, storage_name, params = nil, block_hash = nil)
121
+ self.init_runtime(block_hash)
255
122
 
256
- proc { |e|
257
- SubstrateClient.logger.error e
258
- }
123
+ storage_key, return_type = SubstrateClient::Helper.generate_storage_key_from_metadata(@metadata, module_name, storage_name, params)
259
124
 
260
- )
261
- end
125
+ result = self.state_getStorage(storage_key, block_hash)
126
+ return unless result
127
+ Scale::Types.get(return_type).decode(Scale::Bytes.new(result))
262
128
  end
263
129
 
264
- def get_storage(module_name, storage_name, params = nil, block_hash = nil, &callback)
265
- self.init_runtime(block_hash) do
266
- storage_hash, return_type = Helper.generate_storage_hash_from_metadata(@metadata, module_name, storage_name, params)
267
- self.state_get_storage(storage_hash, block_hash) do |result|
268
- if result
269
- storage = Scale::Types.get(return_type).decode(Scale::Bytes.new(result))
270
- callback.call storage
271
- else
272
- callback.call nil
273
- end
274
- end
275
- end
130
+ def generate_storage_key(module_name, storage_name, params = nil, block_hash = nil)
131
+ self.init_runtime(block_hash)
132
+ SubstrateClient::Helper.generate_storage_key_from_metadata(@metadata, module_name, storage_name, params)
276
133
  end
277
134
 
278
- def compose_call(module_name, call_name, params, block_hash=nil, &callback)
279
- self.init_runtime(block_hash) do
280
- hex = Helper.compose_call_from_metadata(@metadata, module_name, call_name, params)
281
- callback.call hex
282
- end
135
+ # compose_call "Balances", "Transfer", { dest: "0x586cb27c291c813ce74e86a60dad270609abf2fc8bee107e44a80ac00225c409", value: 1_000_000_000_000 }
136
+ def compose_call(module_name, call_name, params, block_hash=nil)
137
+ self.init_runtime(block_hash)
138
+ SubstrateClient::Helper.compose_call_from_metadata(@metadata, module_name, call_name, params)
283
139
  end
284
140
 
285
- private
286
- def init_ws
287
- @ws = Websocket.new(@url,
288
-
289
- onopen: proc do |event|
290
- @callbacks = {}
291
- @subscription_callbacks = {}
292
- @onopen.call event if not @onopen.nil?
293
- end,
294
-
295
- onmessage: proc do |event|
296
- if event.data.include?("jsonrpc")
297
- begin
298
- data = JSON.parse event.data
299
-
300
- if data["params"]
301
- if @subscription_callbacks[data["params"]["subscription"]]
302
- @subscription_callbacks[data["params"]["subscription"]].call data
303
- end
304
- else
305
- @callbacks[data["id"]].call data
306
- @callbacks.delete(data["id"])
307
- end
308
-
309
- rescue => ex
310
- SubstrateClient.logger.error ex.message
311
- SubstrateClient.logger.error ex.backtrace.join("\n")
312
- end
313
- end
314
- end
315
-
316
- )
141
+ def generate_storage_hash_from_data(storage_hex_data)
142
+ "0x" + Crypto.blake2_256(Scale::Bytes.new(storage_hex_data).bytes)
317
143
  end
318
144
 
319
145
  end
@@ -1,3 +1,3 @@
1
1
  class SubstrateClient
2
- VERSION = "0.1.7"
2
+ VERSION = "0.1.8"
3
3
  end
@@ -36,10 +36,10 @@ Gem::Specification.new do |spec|
36
36
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
37
37
  spec.require_paths = ["lib"]
38
38
 
39
- spec.add_dependency "faye-websocket", "~> 0.10.9"
40
- spec.add_dependency "eventmachine", "~> 1.2.7"
41
39
  spec.add_dependency "activesupport", "~> 5.2.4"
42
- spec.add_dependency "scale.rb", "~> 0.2.5"
40
+ spec.add_dependency "scale.rb", "~> 0.2.11"
41
+ spec.add_dependency "kontena-websocket-client", "~> 0.1.1"
42
+ spec.add_dependency "substrate_common.rb", "~> 0.1.9"
43
43
 
44
44
  spec.add_development_dependency "bundler", "~> 1.17"
45
45
  spec.add_development_dependency "rake", ">= 12.3.3"
metadata CHANGED
@@ -1,71 +1,71 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: substrate_client.rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.7
4
+ version: 0.1.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Wu Minzhe
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-06-11 00:00:00.000000000 Z
11
+ date: 2020-11-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: faye-websocket
14
+ name: activesupport
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.10.9
19
+ version: 5.2.4
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 0.10.9
26
+ version: 5.2.4
27
27
  - !ruby/object:Gem::Dependency
28
- name: eventmachine
28
+ name: scale.rb
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 1.2.7
33
+ version: 0.2.11
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 1.2.7
40
+ version: 0.2.11
41
41
  - !ruby/object:Gem::Dependency
42
- name: activesupport
42
+ name: kontena-websocket-client
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 5.2.4
47
+ version: 0.1.1
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 5.2.4
54
+ version: 0.1.1
55
55
  - !ruby/object:Gem::Dependency
56
- name: scale.rb
56
+ name: substrate_common.rb
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 0.2.5
61
+ version: 0.1.9
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: 0.2.5
68
+ version: 0.1.9
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: bundler
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -146,9 +146,6 @@ files:
146
146
  - lib/helper.rb
147
147
  - lib/substrate_client.rb
148
148
  - lib/substrate_client/version.rb
149
- - lib/substrate_client_sync.rb
150
- - lib/timeout_queue.rb
151
- - lib/websocket.rb
152
149
  - substrate_client.gemspec
153
150
  homepage: https://github.com/itering/substrate_client.rb
154
151
  licenses:
@@ -1,190 +0,0 @@
1
- def ws_request(url, payload)
2
- queue = TimeoutQueue.new
3
-
4
- thread = Thread.new do
5
- EM.run do
6
- ws = Faye::WebSocket::Client.new(url)
7
-
8
- ws.on :open do |event|
9
- ws.send(payload.to_json)
10
- end
11
-
12
- ws.on :message do |event|
13
- if event.data.include?("jsonrpc")
14
- queue << JSON.parse(event.data)
15
- ws.close(3001, "data received")
16
- Thread.kill thread
17
- end
18
- end
19
-
20
- ws.on :close do |event|
21
- ws = nil
22
- end
23
- end
24
- end
25
-
26
- queue.pop true, 10
27
- rescue ThreadError => ex
28
- raise SubstrateClientSync::RpcTimeout
29
- end
30
-
31
- class SubstrateClientSync
32
- class RpcError < StandardError; end
33
- class RpcTimeout < StandardError; end
34
-
35
- attr_accessor :spec_name, :spec_version, :metadata
36
-
37
- def initialize(url, spec_name: nil)
38
- @url = url
39
- @request_id = 1
40
- @spec_name = spec_name
41
- Scale::TypeRegistry.instance.load(spec_name)
42
- end
43
-
44
- def request(method, params)
45
- payload = {
46
- "jsonrpc" => "2.0",
47
- "method" => method,
48
- "params" => params,
49
- "id" => @request_id
50
- }
51
-
52
- data = ws_request(@url, payload)
53
- if data["error"]
54
- raise RpcError, data["error"]
55
- else
56
- data["result"]
57
- end
58
- end
59
-
60
- def init_runtime(block_hash=nil)
61
- # set current runtime spec version
62
- runtime_version = self.state_get_runtime_version(block_hash)
63
- @spec_version = runtime_version["specVersion"]
64
- Scale::TypeRegistry.instance.spec_version = @spec_version
65
-
66
- # set current metadata
67
- @metadata = self.get_metadata(block_hash)
68
- Scale::TypeRegistry.instance.metadata = @metadata.value
69
- true
70
- end
71
-
72
- def invoke(method, *params)
73
- # params.reject! { |param| param.nil? }
74
- request(method, params)
75
- end
76
-
77
- def rpc_method(method_name)
78
- SubstrateClient::Helper.real_method_name(method_name.to_s)
79
- end
80
-
81
- # ################################################
82
- # origin rpc methods
83
- # ################################################
84
- def method_missing(method, *args)
85
- rpc_method = SubstrateClient::Helper.real_method_name(method)
86
- invoke rpc_method, *args
87
- end
88
-
89
- def state_get_runtime_version(block_hash=nil)
90
- invoke rpc_method(__method__), block_hash
91
- end
92
-
93
- def rpc_methods
94
- invoke rpc_method(__method__)
95
- end
96
-
97
- def chain_get_head
98
- invoke rpc_method(__method__)
99
- end
100
-
101
- def chain_get_finalised_head
102
- invoke rpc_method(__method__)
103
- end
104
-
105
- def chain_get_header(block_hash = nil)
106
- invoke rpc_method(__method__), block_hash
107
- end
108
-
109
- def chain_get_block(block_hash = nil)
110
- invoke rpc_method(__method__), block_hash
111
- end
112
-
113
- def chain_get_block_hash(block_id)
114
- invoke rpc_method(__method__), block_id
115
- end
116
-
117
- def chain_get_runtime_version(block_hash = nil)
118
- invoke rpc_method(__method__), block_hash
119
- end
120
-
121
- def state_get_metadata(block_hash = nil)
122
- invoke rpc_method(__method__), block_hash
123
- end
124
-
125
- def state_get_storage(storage_key, block_hash = nil)
126
- invoke rpc_method(__method__), storage_key, block_hash
127
- end
128
-
129
- def system_name
130
- invoke rpc_method(__method__)
131
- end
132
-
133
- def system_version
134
- invoke rpc_method(__method__)
135
- end
136
-
137
- # ################################################
138
- # custom methods based on origin rpc methods
139
- # ################################################
140
- def method_list
141
- self.rpc_methods["methods"].map(&:underscore)
142
- end
143
-
144
- def get_block_number(block_hash)
145
- header = self.chain_get_header(block_hash)
146
- header["number"].to_i(16)
147
- end
148
-
149
- def get_metadata(block_hash=nil)
150
- hex = self.state_get_metadata(block_hash)
151
- Scale::Types::Metadata.decode(Scale::Bytes.new(hex))
152
- end
153
-
154
- def get_block(block_hash=nil)
155
- self.init_runtime(block_hash)
156
- block = self.chain_get_block(block_hash)
157
- SubstrateClient::Helper.decode_block(block)
158
- end
159
-
160
- def get_block_events(block_hash=nil)
161
- self.init_runtime(block_hash)
162
-
163
- storage_key = "0x26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7"
164
- events_data = state_get_storage storage_key, block_hash
165
-
166
- scale_bytes = Scale::Bytes.new(events_data)
167
- Scale::Types.get("Vec<EventRecord>").decode(scale_bytes).to_human
168
- end
169
-
170
- # Plain: client.get_storage("Sudo", "Key")
171
- # Plain: client.get_storage("Balances", "TotalIssuance")
172
- # Map: client.get_storage("System", "Account", ["0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"])
173
- # DoubleMap: client.get_storage("ImOnline", "AuthoredBlocks", [2818, "0x749ddc93a65dfec3af27cc7478212cb7d4b0c0357fef35a0163966ab5333b757"])
174
- def get_storage(module_name, storage_name, params = nil, block_hash = nil)
175
- self.init_runtime(block_hash)
176
-
177
- storage_hash, return_type = SubstrateClient::Helper.generate_storage_hash_from_metadata(@metadata, module_name, storage_name, params)
178
-
179
- result = self.state_get_storage(storage_hash, block_hash)
180
- return unless result
181
- Scale::Types.get(return_type).decode(Scale::Bytes.new(result))
182
- end
183
-
184
- # compose_call "Balances", "Transfer", { dest: "0x586cb27c291c813ce74e86a60dad270609abf2fc8bee107e44a80ac00225c409", value: 1_000_000_000_000 }
185
- def compose_call(module_name, call_name, params, block_hash=nil)
186
- self.init_runtime(block_hash)
187
- SubstrateClient::Helper.compose_call_from_metadata(@metadata, module_name, call_name, params)
188
- end
189
-
190
- end
@@ -1,34 +0,0 @@
1
- # https://vaneyckt.io/posts/ruby_concurrency_building_a_timeout_queue/
2
- class TimeoutQueue
3
- def initialize
4
- @elems = []
5
- @mutex = Mutex.new
6
- @cond_var = ConditionVariable.new
7
- end
8
-
9
- def <<(elem)
10
- @mutex.synchronize do
11
- @elems << elem
12
- @cond_var.signal
13
- end
14
- end
15
-
16
- def pop(blocking = true, timeout = nil)
17
- @mutex.synchronize do
18
- if blocking
19
- if timeout.nil?
20
- while @elems.empty?
21
- @cond_var.wait(@mutex)
22
- end
23
- else
24
- timeout_time = Time.now.to_f + timeout
25
- while @elems.empty? && (remaining_time = timeout_time - Time.now.to_f) > 0
26
- @cond_var.wait(@mutex, remaining_time)
27
- end
28
- end
29
- end
30
- raise ThreadError, 'queue empty' if @elems.empty?
31
- @elems.shift
32
- end
33
- end
34
- end
@@ -1,79 +0,0 @@
1
- require "faye/websocket"
2
- require "eventmachine"
3
-
4
- class SubstrateClient::Websocket
5
- HEARTBEAT_INTERVAL = 3
6
- RECONNECT_INTERVAL = 3
7
-
8
- def initialize(url, onopen: nil, onmessage: nil)
9
- @url = url
10
- @onopen = onopen || proc { p [:open] }
11
- @onmessage = onmessage || proc { |event| p [:message, event.data] }
12
-
13
- @thread = Thread.new do
14
- EM.run do
15
- start_connection
16
- end
17
- SubstrateClient.logger.info "Event loop stopped"
18
- end
19
- @heartbeat_thread = start_heartbeat
20
- end
21
-
22
- def start_connection
23
- SubstrateClient.logger.info "Start to connect"
24
- @close = false
25
- @missed_heartbeats = 0
26
- @ping_id = 0
27
- @ws = Faye::WebSocket::Client.new(@url)
28
- @ws.on :open do |event|
29
- @do_heartbeat = true
30
- @onopen.call event
31
- end
32
-
33
- @ws.on :message do |event|
34
- @onmessage.call event
35
- end
36
-
37
- @ws.on :close do |event|
38
- # p [:close, event.code, event.reason]
39
- if @close == false
40
- @do_heartbeat = false
41
- sleep RECONNECT_INTERVAL
42
- start_connection
43
- end
44
- end
45
-
46
- end
47
-
48
- def start_heartbeat
49
- Thread.new do
50
- loop do
51
- send_heartbeat if @do_heartbeat
52
- sleep HEARTBEAT_INTERVAL
53
- end
54
- end
55
- end
56
-
57
- def send_heartbeat
58
- if @missed_heartbeats < 2
59
- # puts "ping_#{@ping_id}"
60
- @ws.ping @ping_id.to_s do
61
- # puts "pong"
62
- @missed_heartbeats -= 1
63
- end
64
- @missed_heartbeats += 1
65
- @ping_id += 1
66
- end
67
- end
68
-
69
- def send(message)
70
- @ws.send message
71
- end
72
-
73
- def close
74
- @close = true
75
- Thread.kill @heartbeat_thread
76
- Thread.kill @thread
77
- @ws = nil
78
- end
79
- end