dpay-ruby 0.1.1

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.
data/Rakefile ADDED
@@ -0,0 +1,332 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+ require 'yard'
4
+ require 'dpay'
5
+
6
+ Rake::TestTask.new(test: ['clean:vcr', 'test:threads']) do |t|
7
+ t.libs << 'test'
8
+ t.libs << 'lib'
9
+ t.test_files = FileList['test/**/*_test.rb']
10
+ t.ruby_opts << if ENV['HELL_ENABLED']
11
+ '-W2'
12
+ else
13
+ '-W1'
14
+ end
15
+ end
16
+
17
+ namespace :test do
18
+ Rake::TestTask.new(static: 'clean:vcr') do |t|
19
+ t.description = <<-EOD
20
+ Run static tests, which are those that have static request/responses.
21
+ These are tests that are typically read-only and do not require heavy
22
+ matches on the json-rpc request body. Often, the only difference between
23
+ one execution and another is the json-rpc-id.
24
+ EOD
25
+ t.libs << 'test'
26
+ t.libs << 'lib'
27
+ t.test_files = [
28
+ 'test/dpay/account_by_key_api_test.rb',
29
+ 'test/dpay/account_history_api_test.rb',
30
+ 'test/dpay/block_api_test.rb',
31
+ 'test/dpay/database_api_test.rb',
32
+ 'test/dpay/follow_api_test.rb',
33
+ 'test/dpay/jsonrpc_test.rb',
34
+ 'test/dpay/market_history_api_test.rb',
35
+ 'test/dpay/tags_api_test.rb',
36
+ 'test/dpay/witness_api_test.rb'
37
+ ]
38
+ t.ruby_opts << if ENV['HELL_ENABLED']
39
+ '-W2'
40
+ else
41
+ '-W1'
42
+ end
43
+ end
44
+
45
+ Rake::TestTask.new(broadcast: 'clean:vcr') do |t|
46
+ t.description = <<-EOD
47
+ Run broadcast tests, which are those that only use network_broadcast_api
48
+ and/or database_api.verify_authority (pretend: true).
49
+ EOD
50
+ t.libs << 'test'
51
+ t.libs << 'lib'
52
+ t.test_files = [
53
+ 'test/dpay/broadcast_test.rb',
54
+ 'test/dpay/transaction_builder_test.rb'
55
+ ]
56
+ t.ruby_opts << if ENV['HELL_ENABLED']
57
+ '-W2'
58
+ else
59
+ '-W1'
60
+ end
61
+ end
62
+
63
+ Rake::TestTask.new(testnet: 'clean:vcr') do |t|
64
+ t.description = <<-EOD
65
+ Run testnet tests, which are those that use network_broadcast_api to do
66
+ actual broadcast operations, on a specified (or default) testnet.
67
+ EOD
68
+ t.libs << 'test'
69
+ t.libs << 'lib'
70
+ t.test_files = [
71
+ 'test/dpay/testnet_test.rb'
72
+ ]
73
+ t.ruby_opts << if ENV['HELL_ENABLED']
74
+ '-W2'
75
+ else
76
+ '-W1'
77
+ end
78
+ end
79
+
80
+ desc 'Tests the API using multiple threads.'
81
+ task :threads do
82
+ next if !!ENV['TEST']
83
+
84
+ threads = []
85
+ api = DPay::Api.new(url: ENV['TEST_NODE'])
86
+ database_api = DPay::DatabaseApi.new(url: ENV['TEST_NODE'])
87
+ witnesses = {}
88
+ keys = %i(created url total_missed props running_version
89
+ hardfork_version_vote hardfork_time_vote)
90
+
91
+ if defined? Thread.report_on_exception
92
+ Thread.report_on_exception = true
93
+ end
94
+
95
+ database_api.get_active_witnesses do |result|
96
+ print "Found #{result.witnesses.size} witnesses ..."
97
+
98
+ result.witnesses.each do |witness_name|
99
+ threads << Thread.new do
100
+ api.get_witness_by_account(witness_name) do |witness|
101
+ witnesses[witness.owner] = witness.map do |k, v|
102
+ [k, v] if keys.include? k.to_sym
103
+ end.compact.to_h
104
+
105
+ bbd_exchange_rate = witness[:bbd_exchange_rate]
106
+ base = bbd_exchange_rate[:base].to_f
107
+
108
+ if (quote = bbd_exchange_rate[:quote].to_f) > 0
109
+ rate = (base / quote).round(3)
110
+ witnesses[witness.owner][:bbd_exchange_rate] = rate
111
+ else
112
+ witnesses[witness.owner][:bbd_exchange_rate] = nil
113
+ end
114
+
115
+ last_bbd_exchange_update = witness[:last_bbd_exchange_update]
116
+ last_bbd_exchange_update = Time.parse(last_bbd_exchange_update + 'Z')
117
+ last_bbd_exchange_elapsed = '%.2f hours ago' % ((Time.now.utc - last_bbd_exchange_update) / 60)
118
+ witnesses[witness.owner][:last_bbd_exchange_elapsed] = last_bbd_exchange_elapsed
119
+ end
120
+ end
121
+ end
122
+ end
123
+
124
+ threads.each do |thread|
125
+ print '.'
126
+ thread.join
127
+ end
128
+
129
+ puts ' done!'
130
+
131
+ if threads.size != witnesses.size
132
+ puts "Bug: expected #{threads.size} witnesses, only found #{witnesses.size}."
133
+ else
134
+ puts JSON.pretty_generate witnesses rescue puts witnesses
135
+ end
136
+ end
137
+ end
138
+
139
+ namespace :stream do
140
+ desc 'Test the ability to stream a block range.'
141
+ task :block_range, [:mode, :at_block_num] do |t, args|
142
+ mode = (args[:mode] || 'irreversible').to_sym
143
+ first_block_num = args[:at_block_num].to_i if !!args[:at_block_num]
144
+ stream = DPay::Stream.new(url: ENV['TEST_NODE'], mode: mode)
145
+ api = DPay::Api.new(url: ENV['TEST_NODE'])
146
+ last_block_num = nil
147
+ last_timestamp = nil
148
+ range_complete = false
149
+
150
+ api.get_dynamic_global_properties do |properties|
151
+ current_block_num = if mode == :head
152
+ properties.head_block_number
153
+ else
154
+ properties.last_irreversible_block_num
155
+ end
156
+
157
+ # First pass replays latest a random number of blocks to test chunking.
158
+ first_block_num ||= current_block_num - (rand * 200).to_i
159
+
160
+ range = first_block_num..current_block_num
161
+ puts "Initial block range: #{range.size}"
162
+
163
+ stream.blocks(at_block_num: range.first) do |block, block_num|
164
+ current_timestamp = Time.parse(block.timestamp + 'Z')
165
+
166
+ if !range_complete && block_num > range.last
167
+ puts 'Done with initial range.'
168
+ range_complete = true
169
+ end
170
+
171
+ if !!last_timestamp && block_num != last_block_num + 1
172
+ puts "Bug: Last block number was #{last_block_num} then jumped to: #{block_num}"
173
+ exit
174
+ end
175
+
176
+ if !!last_timestamp && current_timestamp < last_timestamp
177
+ puts "Bug: Went back in time. Last timestamp was #{last_timestamp}, then jumped back to #{current_timestamp}"
178
+ exit
179
+ end
180
+
181
+ puts "\t#{block_num} Timestamp: #{current_timestamp}, witness: #{block.witness}"
182
+ last_block_num = block_num
183
+ last_timestamp = current_timestamp
184
+ end
185
+ end
186
+ end
187
+
188
+ desc 'Test the ability to stream a block range of transactions.'
189
+ task :trx_range, [:mode, :at_block_num] do |t, args|
190
+ mode = (args[:mode] || 'irreversible').to_sym
191
+ first_block_num = args[:at_block_num].to_i if !!args[:at_block_num]
192
+ stream = DPay::Stream.new(url: ENV['TEST_NODE'], mode: mode)
193
+ api = DPay::Api.new(url: ENV['TEST_NODE'])
194
+
195
+ api.get_dynamic_global_properties do |properties|
196
+ current_block_num = if mode == :head
197
+ properties.head_block_number
198
+ else
199
+ properties.last_irreversible_block_num
200
+ end
201
+
202
+ # First pass replays latest a random number of blocks to test chunking.
203
+ first_block_num ||= current_block_num - (rand * 200).to_i
204
+
205
+ stream.transactions(at_block_num: first_block_num) do |trx, trx_id, block_num|
206
+ puts "#{block_num} :: #{trx_id}; ops: #{trx.operations.map(&:type).join(', ')}"
207
+ end
208
+ end
209
+ end
210
+
211
+ desc 'Test the ability to stream a block range of operations.'
212
+ task :op_range, [:mode, :at_block_num] do |t, args|
213
+ mode = (args[:mode] || 'irreversible').to_sym
214
+ first_block_num = args[:at_block_num].to_i if !!args[:at_block_num]
215
+ stream = DPay::Stream.new(url: ENV['TEST_NODE'], mode: mode)
216
+ api = DPay::Api.new(url: ENV['TEST_NODE'])
217
+
218
+ api.get_dynamic_global_properties do |properties|
219
+ current_block_num = if mode == :head
220
+ properties.head_block_number
221
+ else
222
+ properties.last_irreversible_block_num
223
+ end
224
+
225
+ # First pass replays latest a random number of blocks to test chunking.
226
+ first_block_num ||= current_block_num - (rand * 200).to_i
227
+
228
+ stream.operations(at_block_num: first_block_num) do |op, trx_id, block_num|
229
+ puts "#{block_num} :: #{trx_id}; op: #{op.type}"
230
+ end
231
+ end
232
+ end
233
+
234
+ desc 'Test the ability to stream a block range of virtual operations.'
235
+ task :vop_range, [:mode, :at_block_num] do |t, args|
236
+ mode = (args[:mode] || 'irreversible').to_sym
237
+ first_block_num = args[:at_block_num].to_i if !!args[:at_block_num]
238
+ stream = DPay::Stream.new(url: ENV['TEST_NODE'], mode: mode)
239
+ api = DPay::Api.new(url: ENV['TEST_NODE'])
240
+
241
+ api.get_dynamic_global_properties do |properties|
242
+ current_block_num = if mode == :head
243
+ properties.head_block_number
244
+ else
245
+ properties.last_irreversible_block_num
246
+ end
247
+
248
+ # First pass replays latest a random number of blocks to test chunking.
249
+ first_block_num ||= current_block_num - (rand * 200).to_i
250
+
251
+ stream.operations(at_block_num: first_block_num, only_virtual: true) do |op, trx_id, block_num|
252
+ puts "#{block_num} :: #{trx_id}; op: #{op.type}"
253
+ end
254
+ end
255
+ end
256
+
257
+ desc 'Test the ability to stream a block range of all operations (including virtual).'
258
+ task :all_op_range, [:mode, :at_block_num] do |t, args|
259
+ mode = (args[:mode] || 'irreversible').to_sym
260
+ first_block_num = args[:at_block_num].to_i if !!args[:at_block_num]
261
+ stream = DPay::Stream.new(url: ENV['TEST_NODE'], mode: mode)
262
+ api = DPay::Api.new(url: ENV['TEST_NODE'])
263
+
264
+ api.get_dynamic_global_properties do |properties|
265
+ current_block_num = if mode == :head
266
+ properties.head_block_number
267
+ else
268
+ properties.last_irreversible_block_num
269
+ end
270
+
271
+ # First pass replays latest a random number of blocks to test chunking.
272
+ first_block_num ||= current_block_num - (rand * 200).to_i
273
+
274
+ stream.operations(at_block_num: first_block_num, include_virtual: true) do |op, trx_id, block_num|
275
+ puts "#{block_num} :: #{trx_id}; op: #{op.type}"
276
+ end
277
+ end
278
+ end
279
+ end
280
+
281
+ YARD::Rake::YardocTask.new do |t|
282
+ t.files = ['lib/**/*.rb']
283
+ end
284
+
285
+ task default: :test
286
+
287
+ desc 'Ruby console with dpay already required.'
288
+ task :console do
289
+ exec 'irb -r dpay -I ./lib'
290
+ end
291
+
292
+ namespace :clean do
293
+ desc 'Remove test/fixtures/vcr_cassettes/*.yml so they can be rebuilt fresh.'
294
+ task :vcr do |t|
295
+ cmd = 'echo Cleaned cassettes: $(rm -v test/fixtures/vcr_cassettes/*.yml | wc -l)'
296
+ system cmd
297
+ end
298
+ end
299
+
300
+ namespace :show do
301
+ desc 'Shows known API names.'
302
+ task :apis do
303
+ url = ENV['URL']
304
+ jsonrpc = DPay::Jsonrpc.new(url: url)
305
+ api_methods = jsonrpc.get_api_methods
306
+ puts api_methods.keys
307
+ end
308
+
309
+ desc 'Shows known method names for specified API.'
310
+ task :methods, [:api] do |t, args|
311
+ url = ENV['URL']
312
+ jsonrpc = DPay::Jsonrpc.new(url: url)
313
+ api_methods = jsonrpc.get_api_methods
314
+ api_methods[args[:api]].each do |method|
315
+ jsonrpc.get_signature(method: "#{args[:api]}.#{method}") do |signature|
316
+ print "#{method} "
317
+ params = signature.args.map do |k, v|
318
+ if v =~ /\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2]\d|3[0-1])T(2[0-3]|[01]\d):[0-5]\d:[0-5]\d/
319
+ "#{k}: Time"
320
+ elsif v.class == Hashie::Array
321
+ "#{k}: []"
322
+ elsif v.class == Hashie::Mash
323
+ "#{k}: {}"
324
+ else
325
+ "#{k}: #{v.class}"
326
+ end
327
+ end
328
+ puts params.join(', ')
329
+ end
330
+ end
331
+ end
332
+ end
data/dpay-ruby.gemspec ADDED
@@ -0,0 +1,37 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'dpay/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'dpay-ruby'
8
+ spec.version = DPay::VERSION
9
+ spec.authors = ['dPay, DAO']
10
+ spec.email = ['labs@dpays.io']
11
+
12
+ spec.summary = %q{dPay Ruby Client}
13
+ spec.description = %q{Client for accessing the dPay blockchain.}
14
+ spec.homepage = 'https://github.com/dpays/dpay-ruby'
15
+ spec.license = 'MIT'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test)/}) }
18
+ spec.require_paths = ['lib']
19
+
20
+ spec.add_development_dependency 'bundler', '~> 1.16', '>= 1.16.1'
21
+ spec.add_development_dependency 'rake', '~> 12.3', '>= 12.3.0'
22
+ spec.add_development_dependency 'minitest', '~> 5.10', '>= 5.10.3'
23
+ spec.add_development_dependency 'minitest-line', '~> 0.6', '>= 0.6.4'
24
+ spec.add_development_dependency 'minitest-proveit', '~> 1.0', '>= 1.0.0'
25
+ spec.add_development_dependency 'webmock', '~> 3.3', '>= 3.3.0'
26
+ spec.add_development_dependency 'simplecov', '~> 0.15', '>= 0.15.1'
27
+ spec.add_development_dependency 'vcr', '~> 4.0', '>= 4.0.0'
28
+ spec.add_development_dependency 'yard', '~> 0.9', '>= 0.9.12'
29
+ spec.add_development_dependency 'pry', '~> 0.11', '>= 0.11.3'
30
+ spec.add_development_dependency 'awesome_print', '~> 1.8', '>= 1.8.0'
31
+
32
+ spec.add_dependency 'json', '~> 2.1', '>= 2.1.0'
33
+ spec.add_dependency 'logging', '~> 2.2', '>= 2.2.0'
34
+ spec.add_dependency 'hashie', '~> 3.5', '>= 3.5.7'
35
+ spec.add_dependency 'bitcoin-ruby', '~> 0.0', '>= 0.0.18'
36
+ spec.add_dependency 'ffi', '~> 1.9', '>= 1.9.23'
37
+ end
data/gource.sh ADDED
@@ -0,0 +1,6 @@
1
+ #!/bin/bash
2
+
3
+ gource ./ --user-image-dir images --hide usernames -s 0.5 -b 000000 \
4
+ -1280x720 --output-ppm-stream - |\
5
+ ffmpeg -y -r 28 -f image2pipe -vcodec ppm -i - -vcodec libx264 -preset slow \
6
+ -crf 28 -threads 0 output.mp4
Binary file
data/lib/dpay.rb ADDED
@@ -0,0 +1,37 @@
1
+ # encoding: UTF-8
2
+ require 'json' unless defined?(JSON)
3
+ require 'net/https'
4
+
5
+ require 'hashie'
6
+ require 'dpay/version'
7
+ require 'dpay/utils'
8
+ require 'dpay/base_error'
9
+ require 'dpay/mixins/retriable'
10
+ require 'dpay/chain_config'
11
+ require 'dpay/type/base_type'
12
+ require 'dpay/type/amount'
13
+ require 'dpay/transaction_builder'
14
+ require 'dpay/rpc/base_client'
15
+ require 'dpay/rpc/http_client'
16
+ require 'dpay/rpc/thread_safe_http_client'
17
+ require 'dpay/api'
18
+ require 'dpay/jsonrpc'
19
+ require 'dpay/block_api'
20
+ require 'dpay/formatter'
21
+ require 'dpay/broadcast'
22
+ require 'dpay/stream'
23
+
24
+ module DPay
25
+ def self.api_classes
26
+ @api_classes ||= {}
27
+ end
28
+
29
+ def self.const_missing(api_name)
30
+ api = api_classes[api_name]
31
+ api ||= Api.clone(freeze: true) rescue Api.clone
32
+ api.api_name = api_name
33
+ api_classes[api_name] = api
34
+ end
35
+ end
36
+
37
+ Hashie.logger = Logger.new(ENV['HASHIE_LOGGER'])
data/lib/dpay/api.rb ADDED
@@ -0,0 +1,229 @@
1
+ module DPay
2
+ # This ruby API works with
3
+ # {https://github.com/dpays/dpay/releases dpayd-0.19.10} and other AppBase
4
+ # compatible upstreams. To access different API namespaces, use the
5
+ # following:
6
+ #
7
+ # api = DPay::Api.new
8
+ # api.get_dynamic_global_properties
9
+ #
10
+ # The above example will make an instance that can access the
11
+ # {https://developers.dpays.io/apidefinitions/condenser-api condenser_api}
12
+ # namespace. Alternatively, you may also create a direct instances with its
13
+ # full name, if you prefer:
14
+ #
15
+ # api = DPay::CondenserApi.new
16
+ # api.get_dynamic_global_properties
17
+ #
18
+ # If you know the name of another API that is supported by the remote node,
19
+ # you can create an instance to that instead, for example:
20
+ #
21
+ # api = DPay::MarketHistoryApi.new
22
+ # api.get_volume
23
+ #
24
+ # All known API by namespace:
25
+ #
26
+ # * {AccountByKeyApi}
27
+ # * {AccountHistoryApi}
28
+ # * {BlockApi}
29
+ # * {DatabaseApi}
30
+ # * {FollowApi}
31
+ # * {Jsonrpc}
32
+ # * {MarketHistoryApi}
33
+ # * {NetworkBroadcastApi}
34
+ # * {TagsApi}
35
+ # * {WitnessApi}
36
+ #
37
+ # Also see: {https://developers.dpays.io/apidefinitions/ Complete API Definitions}
38
+ class Api
39
+ attr_accessor :chain, :methods, :rpc_client
40
+
41
+ # Use this for debugging naive thread handler.
42
+ # DEFAULT_RPC_CLIENT_CLASS = RPC::HttpClient
43
+ DEFAULT_RPC_CLIENT_CLASS = RPC::ThreadSafeHttpClient
44
+
45
+ def self.api_name=(api_name)
46
+ @api_name = api_name.to_s.
47
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
48
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
49
+ tr('-', '_').downcase.to_sym
50
+ end
51
+
52
+ def self.api_name
53
+ @api_name
54
+ end
55
+
56
+ def self.api_class_name
57
+ @api_name.to_s.split('_').map(&:capitalize).join
58
+ end
59
+
60
+ def self.jsonrpc=(jsonrpc, url = nil)
61
+ @jsonrpc ||= {}
62
+ @jsonrpc[url || jsonrpc.rpc_client.uri.to_s] = jsonrpc
63
+ end
64
+
65
+ def self.jsonrpc(url = nil)
66
+ if @jsonrpc.size < 2 && url.nil?
67
+ @jsonrpc.values.first
68
+ else
69
+ @jsonrpc[url]
70
+ end
71
+ end
72
+
73
+ # Override this if you want to just use your own client. Otherwise, inject
74
+ # the default using:
75
+ #
76
+ # DPay::Api.register default_rpc_client_class: MyClient
77
+ def self.default_rpc_client_class
78
+ if !!@injected_dependencies && !!@injected_dependencies[:default_rpc_client_class]
79
+ @injected_dependencies[:default_rpc_client_class]
80
+ else
81
+ DEFAULT_RPC_CLIENT_CLASS
82
+ end
83
+ end
84
+
85
+ # Used for dependency injection. Currently, the only key supported is:
86
+ #
87
+ # `default_rpc_client_class`
88
+ def self.register(register)
89
+ @injected_dependencies ||= {}
90
+ @injected_dependencies = @injected_dependencies.merge register
91
+ end
92
+
93
+ def initialize(options = {})
94
+ @chain = options[:chain] || :dpay
95
+ @error_pipe = options[:error_pipe] || STDERR
96
+ @api_name = self.class.api_name ||= :condenser_api
97
+
98
+ @rpc_client = if !!options[:rpc_client]
99
+ options[:rpc_client]
100
+ else
101
+ rpc_client_class = self.class.default_rpc_client_class
102
+ rpc_client_class.new(options.merge(api_name: @api_name))
103
+ end
104
+
105
+ if @api_name == :jsonrpc
106
+ Api::jsonrpc = self
107
+ else
108
+ # Note, we have to wait until initialize to check this because we don't
109
+ # have access to instance options until now.
110
+
111
+ Api::jsonrpc = Jsonrpc.new(options)
112
+ @methods = Api::jsonrpc(rpc_client.uri.to_s).get_api_methods
113
+
114
+ unless !!@methods[@api_name]
115
+ raise UnknownApiError, "#{@api_name} (known APIs: #{@methods.keys.join(' ')})"
116
+ end
117
+
118
+ @methods = @methods[@api_name]
119
+ end
120
+
121
+ @try_count = 0
122
+ end
123
+
124
+ def inspect
125
+ properties = %w(chain methods).map do |prop|
126
+ if !!(v = instance_variable_get("@#{prop}"))
127
+ case v
128
+ when Array then "@#{prop}=<#{v.size} #{v.size == 1 ? 'element' : 'elements'}>"
129
+ else; "@#{prop}=#{v}"
130
+ end
131
+ end
132
+ end.compact.join(', ')
133
+
134
+ "#<#{self.class.api_class_name} [#{properties}]>"
135
+ end
136
+ private
137
+ # @private
138
+ def args_keys_to_s(rpc_method_name)
139
+ args = signature(rpc_method_name).args
140
+ args_keys = JSON[args.to_json]
141
+ end
142
+
143
+ # @private
144
+ def signature(rpc_method_name)
145
+ url = rpc_client.uri.to_s
146
+
147
+ @@signatures ||= {}
148
+ @@signatures[url] ||= {}
149
+ @@signatures[url][rpc_method_name] ||= Api::jsonrpc(url).get_signature(method: rpc_method_name).result
150
+ end
151
+
152
+ # @private
153
+ def raise_error_response(rpc_method_name, rpc_args, response)
154
+ raise UnknownError, "#{rpc_method_name}: #{response}" if response.error.nil?
155
+
156
+ error = response.error
157
+
158
+ if error.message == 'Invalid Request'
159
+ raise DPay::ArgumentError, "Unexpected arguments: #{rpc_args.inspect}. Expected: #{rpc_method_name} (#{args_keys_to_s(rpc_method_name)})"
160
+ end
161
+
162
+ BaseError.build_error(error, rpc_method_name)
163
+ end
164
+
165
+ # @private
166
+ def respond_to_missing?(m, include_private = false)
167
+ methods.nil? ? false : methods.include?(m.to_sym)
168
+ end
169
+
170
+ # @private
171
+ def method_missing(m, *args, &block)
172
+ super unless respond_to_missing?(m)
173
+
174
+ rpc_method_name = "#{@api_name}.#{m}"
175
+ rpc_args = case @api_name
176
+ when :condenser_api then args
177
+ when :jsonrpc then args.first
178
+ else
179
+ expected_args = signature(rpc_method_name).args || []
180
+ expected_args_key_string = if expected_args.size > 0
181
+ " (#{args_keys_to_s(rpc_method_name)})"
182
+ end
183
+ expected_args_size = expected_args.size
184
+
185
+ begin
186
+ args = args.first.to_h
187
+ args_size = args.size
188
+
189
+ # Some argument are optional, but if the arguments passed are greater
190
+ # than the expected arguments size, we can warn.
191
+ if args_size > expected_args_size
192
+ @error_pipe.puts "Warning #{rpc_method_name} expects arguments: #{expected_args_size}, got: #{args_size}"
193
+ end
194
+ rescue NoMethodError => e
195
+ error = DPay::ArgumentError.new("#{rpc_method_name} expects arguments: #{expected_args_size}", e)
196
+ raise error
197
+ rescue => e
198
+ raise UnknownError.new("#{rpc_method_name} unknown error.", e)
199
+ end
200
+
201
+ args
202
+ end
203
+
204
+ response = rpc_client.rpc_execute(@api_name, m, rpc_args)
205
+
206
+ if defined?(response.error) && !!response.error
207
+ if !!response.error.message
208
+ raise_error_response rpc_method_name, rpc_args, response
209
+ else
210
+ raise DPay::ArgumentError, response.error.inspect
211
+ end
212
+ end
213
+
214
+ if !!block
215
+ case response
216
+ when Hashie::Mash then yield response.result, response.error, response.id
217
+ when Hashie::Array
218
+ response.each do |r|
219
+ r = Hashie::Mash.new(r)
220
+ yield r.result, r.error, r.id
221
+ end
222
+ else; yield response
223
+ end
224
+ else
225
+ return response
226
+ end
227
+ end
228
+ end
229
+ end