xrbp 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +103 -5
- data/examples/accounts.rb +13 -0
- data/examples/autoconnect_timeout.rb +5 -1
- data/examples/autorety.rb +7 -0
- data/examples/crawl_nodes.rb +32 -0
- data/examples/dsl/account.rb +16 -0
- data/examples/dsl/ledger.rb +7 -0
- data/examples/dsl/ledger_subscribe.rb +18 -0
- data/examples/dsl/validators.rb +8 -0
- data/examples/gateways.rb +8 -0
- data/examples/latest_account.rb +4 -0
- data/examples/ledger_multi_subscribe.rb +1 -1
- data/examples/ledger_subscribe.rb +2 -2
- data/examples/market.rb +13 -0
- data/examples/username.rb +12 -0
- data/examples/validator.rb +8 -0
- data/lib/xrbp.rb +5 -1
- data/lib/xrbp/common.rb +10 -0
- data/lib/xrbp/core_ext.rb +24 -0
- data/lib/xrbp/dsl.rb +25 -0
- data/lib/xrbp/dsl/accounts.rb +13 -0
- data/lib/xrbp/dsl/ledgers.rb +23 -0
- data/lib/xrbp/dsl/validators.rb +10 -0
- data/lib/xrbp/dsl/webclient.rb +8 -0
- data/lib/xrbp/dsl/websocket.rb +28 -0
- data/lib/xrbp/model.rb +4 -0
- data/lib/xrbp/model/account.rb +142 -1
- data/lib/xrbp/model/base.rb +1 -0
- data/lib/xrbp/model/gateway.rb +24 -0
- data/lib/xrbp/model/ledger.rb +30 -1
- data/lib/xrbp/model/market.rb +52 -0
- data/lib/xrbp/model/node.rb +131 -0
- data/lib/xrbp/model/parsers/account.rb +44 -0
- data/lib/xrbp/model/parsers/gateway.rb +40 -0
- data/lib/xrbp/model/parsers/market.rb +28 -0
- data/lib/xrbp/model/parsers/node.rb +19 -0
- data/lib/xrbp/model/parsers/quote.rb +47 -0
- data/lib/xrbp/model/parsers/validator.rb +25 -0
- data/lib/xrbp/model/validator.rb +24 -0
- data/lib/xrbp/plugins.rb +6 -0
- data/lib/xrbp/plugins/base.rb +10 -0
- data/lib/xrbp/plugins/has_plugin.rb +45 -0
- data/lib/xrbp/plugins/has_result_parsers.rb +27 -0
- data/lib/xrbp/plugins/plugin_registry.rb +20 -0
- data/lib/xrbp/plugins/result_parser.rb +19 -0
- data/lib/xrbp/terminatable.rb +19 -0
- data/lib/xrbp/thread_registry.rb +22 -0
- data/lib/xrbp/version.rb +1 -1
- data/lib/xrbp/webclient.rb +2 -0
- data/lib/xrbp/webclient/connection.rb +100 -0
- data/lib/xrbp/webclient/plugins.rb +8 -0
- data/lib/xrbp/webclient/plugins/autoretry.rb +54 -0
- data/lib/xrbp/webclient/plugins/result_parser.rb +23 -0
- data/lib/xrbp/websocket/client.rb +85 -24
- data/lib/xrbp/websocket/cmds/account_info.rb +4 -0
- data/lib/xrbp/websocket/cmds/account_lines.rb +5 -0
- data/lib/xrbp/websocket/cmds/account_objects.rb +4 -0
- data/lib/xrbp/websocket/cmds/account_offers.rb +5 -0
- data/lib/xrbp/websocket/cmds/account_tx.rb +4 -0
- data/lib/xrbp/websocket/cmds/book_offers.rb +4 -0
- data/lib/xrbp/websocket/cmds/ledger.rb +3 -0
- data/lib/xrbp/websocket/cmds/ledger_entry.rb +4 -0
- data/lib/xrbp/websocket/cmds/paginated.rb +4 -1
- data/lib/xrbp/websocket/cmds/server_info.rb +4 -0
- data/lib/xrbp/websocket/cmds/subscribe.rb +4 -0
- data/lib/xrbp/websocket/command.rb +11 -0
- data/lib/xrbp/websocket/connection.rb +75 -22
- data/lib/xrbp/websocket/message.rb +5 -2
- data/lib/xrbp/websocket/multi/fallback.rb +2 -4
- data/lib/xrbp/websocket/multi/multi_connection.rb +37 -2
- data/lib/xrbp/websocket/multi/parallel.rb +2 -4
- data/lib/xrbp/websocket/multi/prioritized.rb +2 -4
- data/lib/xrbp/websocket/multi/round_robin.rb +4 -0
- data/lib/xrbp/websocket/plugins.rb +1 -7
- data/lib/xrbp/websocket/plugins/autoconnect.rb +25 -5
- data/lib/xrbp/websocket/plugins/command_dispatcher.rb +5 -0
- data/lib/xrbp/websocket/plugins/command_paginator.rb +9 -8
- data/lib/xrbp/websocket/plugins/connection_timeout.rb +27 -16
- data/lib/xrbp/websocket/plugins/message_dispatcher.rb +60 -30
- data/lib/xrbp/websocket/plugins/result_parser.rb +19 -19
- data/lib/xrbp/websocket/socket.rb +23 -6
- metadata +118 -8
- data/lib/xrbp/network.rb +0 -6
- data/lib/xrbp/network/nodes.rb +0 -8
- data/lib/xrbp/websocket/has_plugin.rb +0 -30
@@ -0,0 +1,44 @@
|
|
1
|
+
module XRBP
|
2
|
+
module Model
|
3
|
+
# @private
|
4
|
+
module Parsers
|
5
|
+
# Account Info data parser
|
6
|
+
#
|
7
|
+
# @private
|
8
|
+
class AccountInfo < PluginBase
|
9
|
+
def parser_priority
|
10
|
+
0
|
11
|
+
end
|
12
|
+
|
13
|
+
def parse_result(res, req)
|
14
|
+
j = JSON.parse(res)
|
15
|
+
# return res unless j.key?("accounts")
|
16
|
+
|
17
|
+
accts = (j["accounts"] || []).collect { |a|
|
18
|
+
{:id => a["account"],
|
19
|
+
:inception => a["inception"],
|
20
|
+
:parent_id => a["parent"]}
|
21
|
+
}
|
22
|
+
|
23
|
+
{:marker => j["marker"],
|
24
|
+
:accounts => accts}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Account Username data parser
|
29
|
+
#
|
30
|
+
# @private
|
31
|
+
class AccountUsername < PluginBase
|
32
|
+
def parser_priority
|
33
|
+
0
|
34
|
+
end
|
35
|
+
|
36
|
+
def parse_result(res, req)
|
37
|
+
j = JSON.parse(res)
|
38
|
+
# return res unless j.key?("exists")
|
39
|
+
j["exists"] ? j["username"] : nil
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end # module Parsers
|
43
|
+
end # module Model
|
44
|
+
end # module XRBP
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module XRBP
|
2
|
+
module Model
|
3
|
+
# @private
|
4
|
+
module Parsers
|
5
|
+
# Gateway list data parser
|
6
|
+
#
|
7
|
+
# @private
|
8
|
+
class Gateway < PluginBase
|
9
|
+
def parser_priority
|
10
|
+
0
|
11
|
+
end
|
12
|
+
|
13
|
+
def parse_result(res, req)
|
14
|
+
gateways = []
|
15
|
+
|
16
|
+
j = JSON.parse(res)
|
17
|
+
j.each_key { |currency|
|
18
|
+
j[currency].each { |currency_gateway|
|
19
|
+
id = currency_gateway["account"]
|
20
|
+
name = currency_gateway["name"]
|
21
|
+
gateway = gateways.find { |gw| gw[:id] == id }
|
22
|
+
if gateway
|
23
|
+
gateway[:currencies] << "#{currency}"
|
24
|
+
gateway[:names] << "#{name}" unless gateway[:names].include?(name)
|
25
|
+
|
26
|
+
else
|
27
|
+
gateways << {:id => id,
|
28
|
+
:names => [name],
|
29
|
+
:currencies => [currency],
|
30
|
+
:start_date => currency_gateway["start_date"]}
|
31
|
+
end
|
32
|
+
}
|
33
|
+
}
|
34
|
+
|
35
|
+
gateways
|
36
|
+
end
|
37
|
+
end # class GatewayParser
|
38
|
+
end # module Parsers
|
39
|
+
end # module Model
|
40
|
+
end # module XRBP
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module XRBP
|
2
|
+
module Model
|
3
|
+
# @private
|
4
|
+
module Parsers
|
5
|
+
# Market List data parser
|
6
|
+
#
|
7
|
+
# @private
|
8
|
+
class Market < PluginBase
|
9
|
+
def parser_priority
|
10
|
+
0
|
11
|
+
end
|
12
|
+
|
13
|
+
def parse_result(res, req)
|
14
|
+
j = JSON.parse(res)
|
15
|
+
return res unless j["result"] &&
|
16
|
+
j["result"]["markets"] &&
|
17
|
+
j["result"]["markets"]["base"]
|
18
|
+
j["result"]["markets"]["base"]
|
19
|
+
.collect { |market|
|
20
|
+
{:exchange => market["exchange"],
|
21
|
+
:currency => market["pair"][3..-1],
|
22
|
+
:route => market["route"]}
|
23
|
+
}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end # module Parsers
|
27
|
+
end # module Model
|
28
|
+
end # module XRBP
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module XRBP
|
2
|
+
module Model
|
3
|
+
# @private
|
4
|
+
module Parsers
|
5
|
+
# Node Peers data parser
|
6
|
+
#
|
7
|
+
# @private
|
8
|
+
class NodePeers < PluginBase
|
9
|
+
def parser_priority
|
10
|
+
0
|
11
|
+
end
|
12
|
+
|
13
|
+
def parse_result(res, req)
|
14
|
+
JSON.parse(res)["overlay"]["active"]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end # module Parsers
|
18
|
+
end # module Model
|
19
|
+
end # module XRBP
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module XRBP
|
2
|
+
module Model
|
3
|
+
# @private
|
4
|
+
module Parsers
|
5
|
+
# Market quotes data parser
|
6
|
+
#
|
7
|
+
# @private
|
8
|
+
class Quote < PluginBase
|
9
|
+
def parser_priority
|
10
|
+
0
|
11
|
+
end
|
12
|
+
|
13
|
+
def parse_result(res, req)
|
14
|
+
return [] unless res && res != ''
|
15
|
+
|
16
|
+
j = JSON.parse(res)
|
17
|
+
return [] unless j["result"]
|
18
|
+
|
19
|
+
j["result"].collect { |p, quotes|
|
20
|
+
next nil unless quotes
|
21
|
+
quotes.collect { |q|
|
22
|
+
t = q[0]
|
23
|
+
o = q[1]
|
24
|
+
h = q[2]
|
25
|
+
l = q[3]
|
26
|
+
c = q[4]
|
27
|
+
vol = q[5]
|
28
|
+
|
29
|
+
# discard invalid data
|
30
|
+
# (some exchanges periodically
|
31
|
+
# return '0's for some timestamps,
|
32
|
+
# perhaps for periods with no trades?)
|
33
|
+
next nil if o.zero? || h.zero? || l.zero? || c.zero? || vol.zero?
|
34
|
+
|
35
|
+
{:timestamp => Time.at(t).to_datetime,
|
36
|
+
:open => o,
|
37
|
+
:high => h,
|
38
|
+
:low => l,
|
39
|
+
:close => c,
|
40
|
+
:volume => vol}
|
41
|
+
}
|
42
|
+
}.flatten.compact
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end # module Parsers
|
46
|
+
end # module Model
|
47
|
+
end # module XRBP
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module XRBP
|
2
|
+
module Model
|
3
|
+
# @private
|
4
|
+
module Parsers
|
5
|
+
# Validator list data parser
|
6
|
+
#
|
7
|
+
# @private
|
8
|
+
class Validator < PluginBase
|
9
|
+
def parser_priority
|
10
|
+
0
|
11
|
+
end
|
12
|
+
|
13
|
+
def parse_result(res, req)
|
14
|
+
JSON.parse(res)["validators"].collect { |v|
|
15
|
+
id = v["validation_public_key"]
|
16
|
+
next nil unless id
|
17
|
+
|
18
|
+
{:id => id,
|
19
|
+
:domain => v["domain"]}
|
20
|
+
}.compact
|
21
|
+
end
|
22
|
+
end # class ValidatorParser
|
23
|
+
end # module Parsers
|
24
|
+
end # module Model
|
25
|
+
end # module XRBP
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require_relative './parsers/validator'
|
2
|
+
|
3
|
+
module XRBP
|
4
|
+
module Model
|
5
|
+
class Validator < Base
|
6
|
+
extend Base::ClassMethods
|
7
|
+
|
8
|
+
# Retrieve list of validators via WebClient::Connection
|
9
|
+
#
|
10
|
+
# @param opts [Hash] options to retrieve validator list with
|
11
|
+
# @option opts [WebClient::Connection] :connection Connection
|
12
|
+
# to use to retrieve validator list
|
13
|
+
def self.all(opts={})
|
14
|
+
set_opts(opts)
|
15
|
+
connection.url = "https://data.ripple.com/v2/network/validators/"
|
16
|
+
|
17
|
+
connection.add_plugin :result_parser unless connection.plugin?(:result_parser)
|
18
|
+
connection.add_plugin Parsers::Validator unless connection.plugin?(Parsers::Validator)
|
19
|
+
|
20
|
+
connection.perform
|
21
|
+
end
|
22
|
+
end # class Validator
|
23
|
+
end # module Model
|
24
|
+
end # module XRBP
|
data/lib/xrbp/plugins.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
module XRBP
|
2
|
+
# Helper mixin provding plugin management capabilities.
|
3
|
+
#
|
4
|
+
# @private
|
5
|
+
module HasPlugin
|
6
|
+
# should be overridden
|
7
|
+
def plugin_namespace
|
8
|
+
raise
|
9
|
+
end
|
10
|
+
|
11
|
+
def plugins
|
12
|
+
@plugins ||= []
|
13
|
+
end
|
14
|
+
|
15
|
+
def add_plugin(*plgs)
|
16
|
+
plgs.each { |plg|
|
17
|
+
plg = plugin_namespace.plugins[plg] if plg.is_a?(Symbol)
|
18
|
+
raise ArgumentError unless !!plg
|
19
|
+
plg = plg.new(self)
|
20
|
+
plugins << plg
|
21
|
+
plg.added if plg.respond_to?(:added)
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
def plugin?(plg)
|
26
|
+
clss = plugins.collect { |plg| plg.class }
|
27
|
+
cls = plugin_namespace.plugins[plg]
|
28
|
+
clss.include?(plg) || clss.include?(cls)
|
29
|
+
end
|
30
|
+
|
31
|
+
def plugin(plg)
|
32
|
+
cls = plugin_namespace.plugins[plg]
|
33
|
+
plugins.find { |_plg|
|
34
|
+
(plg.is_a?(Class) && _plg.kind_of?(plg)) ||
|
35
|
+
(cls.is_a?(Class) && _plg.class.kind_of?(cls))
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
def define_instance_method(name, &block)
|
40
|
+
(class << self; self; end).class_eval do
|
41
|
+
define_method name, &block
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end # module HasPlugin
|
45
|
+
end # module XRBP
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module XRBP
|
2
|
+
# Helper mixing providing result parser management capabilities.
|
3
|
+
#
|
4
|
+
# @private
|
5
|
+
module HasResultParsers
|
6
|
+
def parsing_plugins
|
7
|
+
raise
|
8
|
+
end
|
9
|
+
|
10
|
+
def parse_result(res, req)
|
11
|
+
_res = res
|
12
|
+
|
13
|
+
prioritized = parsing_plugins.select { |p|
|
14
|
+
p != self && p.respond_to?(:parse_result)
|
15
|
+
|
16
|
+
}.sort { |p1, p2|
|
17
|
+
(p1.respond_to?(:parser_priority) ? p1.parser_priority : 1) <=>
|
18
|
+
(p2.respond_to?(:parser_priority) ? p2.parser_priority : 1)
|
19
|
+
}
|
20
|
+
|
21
|
+
prioritized.each { |plg|
|
22
|
+
_res = plg.parse_result(_res, req)
|
23
|
+
}
|
24
|
+
_res
|
25
|
+
end
|
26
|
+
end # module HasResultParsers
|
27
|
+
end # module XRBP
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module XRBP
|
2
|
+
# Helper mixin providing list of plugins.
|
3
|
+
#
|
4
|
+
# @private
|
5
|
+
module PluginRegistry
|
6
|
+
module ClassMethods
|
7
|
+
def plugins
|
8
|
+
@plugins ||= {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def register_plugin(label, cls)
|
12
|
+
plugins[label] = cls
|
13
|
+
end
|
14
|
+
end # module ClassMethods
|
15
|
+
|
16
|
+
def self.included(base)
|
17
|
+
base.extend(ClassMethods)
|
18
|
+
end
|
19
|
+
end # module PluginRegistry
|
20
|
+
end # module XRBP
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module XRBP
|
2
|
+
# Result Parser plugin base class, allows request results
|
3
|
+
# to be converted before returning / invoking callback.
|
4
|
+
class ResultParserBase < PluginBase
|
5
|
+
attr_accessor :parser
|
6
|
+
|
7
|
+
def added
|
8
|
+
plugin = self
|
9
|
+
connection.define_instance_method(:parse_results) do |&bl|
|
10
|
+
plugin.parser = bl
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def parse_result(res, req)
|
15
|
+
return res unless parser
|
16
|
+
parser.call(res, req)
|
17
|
+
end
|
18
|
+
end # class ResultParserBase
|
19
|
+
end # module XRBP
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module XRBP
|
2
|
+
# Helper mixin facilitating controlled termination of
|
3
|
+
# asynchronous components.
|
4
|
+
#
|
5
|
+
# @private
|
6
|
+
module Terminatable
|
7
|
+
def terminate_queue
|
8
|
+
@terminate_queue ||= Queue.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def terminate?
|
12
|
+
!!terminate_queue.pop_or_nil
|
13
|
+
end
|
14
|
+
|
15
|
+
def terminate!
|
16
|
+
terminate_queue << true
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'concurrent'
|
2
|
+
|
3
|
+
module XRBP
|
4
|
+
# Helper mixin providing internal thread management.
|
5
|
+
#
|
6
|
+
# @private
|
7
|
+
module ThreadRegistry
|
8
|
+
def thread_registry
|
9
|
+
@thread_registry ||= Concurrent::Array.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def rsleep(t)
|
13
|
+
thread_registry << Thread.current
|
14
|
+
sleep(t)
|
15
|
+
thread_registry.delete(Thread.current)
|
16
|
+
end
|
17
|
+
|
18
|
+
def wake_all
|
19
|
+
thread_registry.each { |th| th.wakeup }
|
20
|
+
end
|
21
|
+
end # module ThreadRegistry
|
22
|
+
end # module XRBP
|
data/lib/xrbp/version.rb
CHANGED
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'curb'
|
2
|
+
require_relative '../thread_registry'
|
3
|
+
|
4
|
+
module XRBP
|
5
|
+
module WebClient
|
6
|
+
# HTTP interface, use Connection to perform web requests.
|
7
|
+
#
|
8
|
+
# @example retrieve data from the web
|
9
|
+
# connection = WebClient::Connection.new
|
10
|
+
# connection.url = "https://devnull.network"
|
11
|
+
# connection.perform
|
12
|
+
class Connection
|
13
|
+
include EventEmitter
|
14
|
+
include HasPlugin
|
15
|
+
include HasResultParsers
|
16
|
+
include ThreadRegistry
|
17
|
+
|
18
|
+
DELEGATED_METHODS = [:url=,
|
19
|
+
:timeout=,
|
20
|
+
:ssl_verify_peer=,
|
21
|
+
:ssl_verify_host=]
|
22
|
+
|
23
|
+
# @private
|
24
|
+
def plugin_namespace
|
25
|
+
WebClient
|
26
|
+
end
|
27
|
+
|
28
|
+
# @private
|
29
|
+
def parsing_plugins
|
30
|
+
plugins
|
31
|
+
end
|
32
|
+
|
33
|
+
# Return current url
|
34
|
+
def url
|
35
|
+
c.url
|
36
|
+
end
|
37
|
+
|
38
|
+
# delegated methods
|
39
|
+
DELEGATED_METHODS.each { |m|
|
40
|
+
define_method(m) do |v|
|
41
|
+
c.send(m, v)
|
42
|
+
end
|
43
|
+
}
|
44
|
+
|
45
|
+
def initialize(url=nil)
|
46
|
+
self.url = url
|
47
|
+
@force_quit = false
|
48
|
+
|
49
|
+
yield self if block_given?
|
50
|
+
end
|
51
|
+
|
52
|
+
def force_quit?
|
53
|
+
@force_quit
|
54
|
+
end
|
55
|
+
|
56
|
+
# Immediate terminate outstanding requests
|
57
|
+
def force_quit!
|
58
|
+
@force_quit = true
|
59
|
+
wake_all
|
60
|
+
# TODO immediate terminate outstanding requests
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def c
|
66
|
+
@curl ||= Curl::Easy.new
|
67
|
+
end
|
68
|
+
|
69
|
+
def handle_error
|
70
|
+
plugins.select { |plg|
|
71
|
+
plg.respond_to?(:handle_error)
|
72
|
+
}.last&.handle_error
|
73
|
+
end
|
74
|
+
|
75
|
+
public
|
76
|
+
|
77
|
+
# Execute web request, retrieving results and returning
|
78
|
+
def perform
|
79
|
+
# TODO fault tolerance plugins:
|
80
|
+
# configurable timeout,
|
81
|
+
# round-robin urls,
|
82
|
+
# redirect handling, etc
|
83
|
+
begin
|
84
|
+
c.perform
|
85
|
+
rescue => e
|
86
|
+
emit :error, e
|
87
|
+
return handle_error
|
88
|
+
end
|
89
|
+
|
90
|
+
if c.response_code != 200
|
91
|
+
emit :http_error, c.response_code
|
92
|
+
return handle_error
|
93
|
+
end
|
94
|
+
|
95
|
+
emit :success, c.body_str
|
96
|
+
parse_result(c.body_str, c)
|
97
|
+
end
|
98
|
+
end # class Connection
|
99
|
+
end # module WebClient
|
100
|
+
end # module XRBP
|