xrbp 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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,28 @@
|
|
1
|
+
module XRBP
|
2
|
+
module DSL
|
3
|
+
# Default websocket endpoints. Override to specify
|
4
|
+
# different ones.
|
5
|
+
def websocket_endpoints
|
6
|
+
["wss://s1.ripple.com:443", "wss://s2.ripple.com:443"]
|
7
|
+
end
|
8
|
+
|
9
|
+
# Client which may be used to access websocket endpoints.
|
10
|
+
#
|
11
|
+
# By default a RoundRobin strategy will be used to cycle
|
12
|
+
# through specified endpoints.
|
13
|
+
def websocket
|
14
|
+
@websocket ||= WebSocket::RoundRobin.new *websocket_endpoints
|
15
|
+
end
|
16
|
+
|
17
|
+
# Register a callback to be invoked when messages are received
|
18
|
+
# via websocket connections
|
19
|
+
def websocket_msg(&bl)
|
20
|
+
websocket.on :message, &bl
|
21
|
+
end
|
22
|
+
|
23
|
+
# Block until all websocket connections are closed
|
24
|
+
def websocket_wait
|
25
|
+
websocket.wait_for_close
|
26
|
+
end
|
27
|
+
end # module DSL
|
28
|
+
end # module XRBP
|
data/lib/xrbp/model.rb
CHANGED
data/lib/xrbp/model/account.rb
CHANGED
@@ -1,27 +1,168 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require_relative './parsers/account'
|
3
|
+
|
1
4
|
module XRBP
|
2
5
|
module Model
|
3
6
|
class Account < Base
|
4
7
|
extend Base::ClassMethods
|
5
8
|
|
9
|
+
DATE_FORMAT = "%Y-%m-%dT%H:%M:%SZ"
|
10
|
+
|
6
11
|
attr_accessor :id
|
7
12
|
|
8
|
-
|
13
|
+
# Local data cache location
|
14
|
+
def self.cache
|
15
|
+
@cache ||= File.expand_path("~/.xrbp/accounts/")
|
16
|
+
end
|
17
|
+
|
18
|
+
# All cached accounts
|
19
|
+
def self.cached
|
20
|
+
Dir.glob("#{cache}/*").collect { |f|
|
21
|
+
next nil if f == "#{cache}marker" ||
|
22
|
+
f == "#{cache}start"
|
23
|
+
begin
|
24
|
+
JSON.parse(File.read(f))
|
25
|
+
rescue
|
26
|
+
nil
|
27
|
+
end
|
28
|
+
}.flatten.compact
|
9
29
|
end
|
10
30
|
|
31
|
+
# TODO invoke gen account command via websocket
|
32
|
+
#def self.create(opts={})
|
33
|
+
#end
|
34
|
+
|
35
|
+
# TODO 'parallel accounts' method,
|
36
|
+
# - split period between Genesis and Time.now into
|
37
|
+
# N-segments (of configurable length: hours, days,
|
38
|
+
# months, etc)
|
39
|
+
# - parallel retrieve segments specifying start param
|
40
|
+
# & following markers until data is no longer
|
41
|
+
# in-domain
|
42
|
+
# - emit :account signal w/ each account during process
|
43
|
+
# & reassemble complete set after
|
44
|
+
|
45
|
+
# Retrieve all accounts via WebClient::Connection
|
46
|
+
#
|
47
|
+
# @param opts [Hash] options to retrieve accounts with
|
48
|
+
# @option opts [WebClient::Connection] :connection Connection
|
49
|
+
# to use to retrieve accounts
|
50
|
+
def self.all(opts={})
|
51
|
+
set_opts(opts)
|
52
|
+
FileUtils.mkdir_p(cache) unless File.exist?(cache)
|
53
|
+
|
54
|
+
# start at last marker
|
55
|
+
marker = File.exist?("#{cache}/marker") ?
|
56
|
+
File.read("#{cache}/marker") : nil
|
57
|
+
|
58
|
+
# load start time, if set
|
59
|
+
start = File.exist?("#{cache}/start") ?
|
60
|
+
File.read("#{cache}/start") :
|
61
|
+
GENESIS_TIME.strftime(DATE_FORMAT)
|
62
|
+
|
63
|
+
# Parse results
|
64
|
+
connection.add_plugin :result_parser unless connection.plugin?(:result_parser)
|
65
|
+
connection.add_plugin Parsers::AccountInfo unless connection.plugin?(Parsers::AccountInfo)
|
66
|
+
|
67
|
+
# Retrieve data until complete
|
68
|
+
accounts = []
|
69
|
+
finished = false
|
70
|
+
until finished || connection.force_quit?
|
71
|
+
# HTTP request
|
72
|
+
connection.url = "https://data.ripple.com/v2/accounts/?"\
|
73
|
+
"start=#{start}&limit=1000&marker=#{marker}"
|
74
|
+
res = connection.perform
|
75
|
+
|
76
|
+
# Cache data
|
77
|
+
cache_file = "#{cache}/#{marker || "genesis"}"
|
78
|
+
File.write(cache_file, res[:accounts].to_json)
|
79
|
+
|
80
|
+
# Emit signal
|
81
|
+
res[:accounts].each { |acct|
|
82
|
+
break if connection.force_quit?
|
83
|
+
connection.emit :account, acct
|
84
|
+
}
|
85
|
+
|
86
|
+
break if connection.force_quit?
|
87
|
+
|
88
|
+
marker = res[:marker]
|
89
|
+
accounts += res[:accounts]
|
90
|
+
|
91
|
+
# Store state, eval exit condition
|
92
|
+
File.write("#{cache}/marker", marker.to_s)
|
93
|
+
finished = !marker
|
94
|
+
end
|
95
|
+
|
96
|
+
# Store state for next run
|
97
|
+
File.write("#{cache}/start",
|
98
|
+
accounts.last[:inception]) unless marker
|
99
|
+
|
100
|
+
accounts
|
101
|
+
end
|
102
|
+
|
103
|
+
# Retrieve latest account using specified WebClient::Connection
|
104
|
+
#
|
105
|
+
# @param opts [Hash] options to retrieve account with
|
106
|
+
# @option opts [WebClient::Connection] :connection Connection
|
107
|
+
# to use to retrieve account
|
108
|
+
def self.latest(opts={})
|
109
|
+
set_opts(opts)
|
110
|
+
|
111
|
+
connection.add_plugin :result_parser unless connection.plugin?(:result_parser)
|
112
|
+
connection.add_plugin Parsers::AccountInfo unless connection.plugin?(Parsers::AccountInfo)
|
113
|
+
|
114
|
+
connection.url = "https://data.ripple.com/v2/accounts/?"\
|
115
|
+
"descending=true&limit=1000"
|
116
|
+
res = connection.perform
|
117
|
+
res[:accounts].first
|
118
|
+
end
|
119
|
+
|
120
|
+
###
|
121
|
+
|
122
|
+
# Initialize new Account instance
|
123
|
+
#
|
124
|
+
# @param opts [Hash] options to initialize account with
|
125
|
+
# @option opts [String] :id id of account
|
126
|
+
# to use to retrieve account
|
11
127
|
def initialize(opts={})
|
12
128
|
@id = opts[:id]
|
13
129
|
super(opts)
|
14
130
|
end
|
15
131
|
|
132
|
+
# Retrieve account info via WebSocket::Connection
|
133
|
+
#
|
134
|
+
# @param opts [Hash] options to retrieve account info with
|
135
|
+
# @option opts [WebSocket::Connection] :connection Connection
|
136
|
+
# to use to retrieve account info
|
16
137
|
def info(opts={}, &bl)
|
17
138
|
set_opts(opts)
|
18
139
|
connection.cmd(WebSocket::Cmds::AccountInfo.new(id, full_opts), &bl)
|
19
140
|
end
|
20
141
|
|
142
|
+
# Retrieve account objects via WebSocket::Connection
|
143
|
+
#
|
144
|
+
# @param opts [Hash] options to retrieve account objects with
|
145
|
+
# @option opts [WebSocket::Connection] :connection Connection
|
146
|
+
# to use to retrieve account objects
|
21
147
|
def objects(opts={}, &bl)
|
22
148
|
set_opts(opts)
|
23
149
|
connection.cmd(WebSocket::Cmds::AccountObjects.new(id, full_opts), &bl)
|
24
150
|
end
|
151
|
+
|
152
|
+
# Retrieve account username via WebClient::Connection
|
153
|
+
#
|
154
|
+
# @param opts [Hash] options to retrieve account username with
|
155
|
+
# @option opts [WebClient::Connection] :connection Connection
|
156
|
+
# to use to retrieve account username
|
157
|
+
def username(opts={}, &bl)
|
158
|
+
set_opts(opts)
|
159
|
+
connection.url = "https://id.ripple.com/v1/authinfo?username=#{id}"
|
160
|
+
|
161
|
+
connection.add_plugin :result_parser unless connection.plugin?(:result_parser)
|
162
|
+
connection.add_plugin Parsers::AccountUsername unless connection.plugin?(Parsers::AccountUsername)
|
163
|
+
|
164
|
+
connection.perform
|
165
|
+
end
|
25
166
|
end # class Account
|
26
167
|
end # module Model
|
27
168
|
end # module XRBP
|
data/lib/xrbp/model/base.rb
CHANGED
@@ -0,0 +1,24 @@
|
|
1
|
+
require_relative './parsers/gateway'
|
2
|
+
|
3
|
+
module XRBP
|
4
|
+
module Model
|
5
|
+
class Gateway < Base
|
6
|
+
extend Base::ClassMethods
|
7
|
+
|
8
|
+
# Retrieve list of gateways provided WebClient::Connection.
|
9
|
+
#
|
10
|
+
# @param opts [Hash] options to retrieve gateway list with
|
11
|
+
# @option opts [WebClient::Connection] :connection Connection
|
12
|
+
# to use to retrieve gateway list
|
13
|
+
def self.all(opts={})
|
14
|
+
set_opts(opts)
|
15
|
+
connection.url = "https://data.ripple.com/v2/gateways"
|
16
|
+
|
17
|
+
connection.add_plugin :result_parser unless connection.plugin?(:result_parser)
|
18
|
+
connection.add_plugin Parsers::Gateway unless connection.plugin?(Parsers::Gateway)
|
19
|
+
|
20
|
+
connection.perform
|
21
|
+
end
|
22
|
+
end # class Gateway
|
23
|
+
end # module Model
|
24
|
+
end # module XRBP
|
data/lib/xrbp/model/ledger.rb
CHANGED
@@ -10,14 +10,43 @@ module XRBP
|
|
10
10
|
super(opts)
|
11
11
|
end
|
12
12
|
|
13
|
+
# Retreive specified ledger via WebSocket::Connection
|
14
|
+
#
|
15
|
+
# @param opts [Hash] options to retrieve ledger with
|
16
|
+
# @option opts [WebSocket::Connection] :connection Connection
|
17
|
+
# to use to retrieve ledger
|
13
18
|
def sync(opts={}, &bl)
|
14
19
|
set_opts(opts)
|
15
20
|
connection.cmd(WebSocket::Cmds::Ledger.new(id, full_opts), &bl)
|
16
21
|
end
|
17
22
|
|
23
|
+
# Subscribe to ledger stream via WebSocket::Connection
|
24
|
+
#
|
25
|
+
# @param opts [Hash] options to subscribe to ledger stream with
|
26
|
+
# @option opts [WebSocket::Connection] :connection Connection
|
27
|
+
# to use to subscribe to ledger stream
|
18
28
|
def self.subscribe(opts={}, &bl)
|
19
29
|
set_opts(opts)
|
20
|
-
|
30
|
+
conn = connection
|
31
|
+
conn.cmd(WebSocket::Cmds::Subscribe.new(:streams => ["ledger"]), &bl)
|
32
|
+
conn.on :message do |*args|
|
33
|
+
c,msg = args.size > 1 ? [args[0], args[1]] :
|
34
|
+
[nil, args[0]]
|
35
|
+
|
36
|
+
begin
|
37
|
+
i = JSON.parse(msg.to_s)
|
38
|
+
if i["ledger_hash"] &&
|
39
|
+
i["ledger_index"]
|
40
|
+
if c
|
41
|
+
conn.emit :ledger, c, i
|
42
|
+
else
|
43
|
+
conn.emit :ledger, i
|
44
|
+
conn.parent.emit :ledger, conn, i if conn.parent
|
45
|
+
end
|
46
|
+
end
|
47
|
+
rescue
|
48
|
+
end
|
49
|
+
end
|
21
50
|
end
|
22
51
|
end # class Ledger
|
23
52
|
end # module Model
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require_relative './parsers/market'
|
2
|
+
require_relative './parsers/quote'
|
3
|
+
|
4
|
+
module XRBP
|
5
|
+
module Model
|
6
|
+
class Market < Base
|
7
|
+
extend Base::ClassMethods
|
8
|
+
# TODO plugabble system to pull in markets from other sources
|
9
|
+
|
10
|
+
# Retrieve list of markets via WebClient::Connection
|
11
|
+
#
|
12
|
+
# @param opts [Hash] options to retrieve market list with
|
13
|
+
# @option opts [WebClient::Connection] :connection Connection
|
14
|
+
# to use to retrieve market list
|
15
|
+
def self.all(opts={})
|
16
|
+
set_opts(opts)
|
17
|
+
connection.url = "https://api.cryptowat.ch/assets/xrp"
|
18
|
+
|
19
|
+
connection.add_plugin :result_parser unless connection.plugin?(:result_parser)
|
20
|
+
connection.add_plugin Parsers::Market unless connection.plugin?(Parsers::Market)
|
21
|
+
|
22
|
+
connection.perform
|
23
|
+
end
|
24
|
+
|
25
|
+
attr_accessor :route
|
26
|
+
|
27
|
+
def initialize(opts={})
|
28
|
+
set_opts(opts)
|
29
|
+
end
|
30
|
+
|
31
|
+
def set_opts(opts={})
|
32
|
+
super opts
|
33
|
+
@route = opts[:route] if opts[:route]
|
34
|
+
end
|
35
|
+
|
36
|
+
# Retrieve list of quotes for market via WebClient::Connection
|
37
|
+
#
|
38
|
+
# @param opts [Hash] options to retrieve quotes with
|
39
|
+
# @option opts [WebClient::Connection] :connection Connection
|
40
|
+
# to use to retrieve quotes
|
41
|
+
def quotes(opts={})
|
42
|
+
set_opts(opts)
|
43
|
+
connection.url = self.route
|
44
|
+
|
45
|
+
connection.add_plugin :result_parser unless connection.plugin?(:result_parser)
|
46
|
+
connection.add_plugin Parsers::Quote unless connection.plugin?(Parsers::Quote)
|
47
|
+
|
48
|
+
connection.perform
|
49
|
+
end
|
50
|
+
end # class Market
|
51
|
+
end # module Model
|
52
|
+
end # module XRBP
|
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'resolv'
|
3
|
+
|
4
|
+
require_relative './parsers/node'
|
5
|
+
|
6
|
+
module XRBP
|
7
|
+
module Model
|
8
|
+
class Node < Base
|
9
|
+
extend Base::ClassMethods
|
10
|
+
|
11
|
+
DEFAULT_CRAWL_PORT = 51235
|
12
|
+
|
13
|
+
attr_accessor :ip, :port
|
14
|
+
attr_accessor :addr, :version, :uptime, :type
|
15
|
+
|
16
|
+
# Return unique node id
|
17
|
+
def id
|
18
|
+
"#{ip}:#{port}"
|
19
|
+
end
|
20
|
+
|
21
|
+
# Return node url
|
22
|
+
def url
|
23
|
+
"https://#{ip}:#{port}/crawl"
|
24
|
+
end
|
25
|
+
|
26
|
+
# Return bool indicating if this node is valid for crawling
|
27
|
+
def valid?
|
28
|
+
return false unless ip && port
|
29
|
+
|
30
|
+
# ensure no parsing errs
|
31
|
+
begin
|
32
|
+
# FIXME URI.parse is limiting our ability to traverse entire node-set,
|
33
|
+
# some nodes are represented as IPv6 addresses which is throwing
|
34
|
+
# things off.
|
35
|
+
URI.parse(url)
|
36
|
+
rescue
|
37
|
+
false
|
38
|
+
end
|
39
|
+
|
40
|
+
true
|
41
|
+
end
|
42
|
+
|
43
|
+
def ==(o)
|
44
|
+
ip == o.ip && port == o.port
|
45
|
+
end
|
46
|
+
|
47
|
+
# Return new node from the specified url
|
48
|
+
#
|
49
|
+
# @param url [String] node url
|
50
|
+
# @return [Node] new node instance
|
51
|
+
def self.parse_url(url)
|
52
|
+
n = new
|
53
|
+
|
54
|
+
uri = URI.parse(url)
|
55
|
+
n.ip = Resolv.getaddress(uri.host)
|
56
|
+
n.port = uri.port
|
57
|
+
|
58
|
+
n
|
59
|
+
end
|
60
|
+
|
61
|
+
# Return new node from the specified peer object
|
62
|
+
#
|
63
|
+
# @param p [Hash] peer data
|
64
|
+
# @return [Node] new node instance
|
65
|
+
def self.from_peer(p)
|
66
|
+
n = new
|
67
|
+
|
68
|
+
n.addr = p["public_key"]
|
69
|
+
n.ip = p["ip"]
|
70
|
+
n.port = p["port"] || DEFAULT_CRAWL_PORT
|
71
|
+
n.version = p["version"].split("-").last
|
72
|
+
n.uptime = p["uptime"]
|
73
|
+
n.type = p["type"]
|
74
|
+
|
75
|
+
n
|
76
|
+
end
|
77
|
+
|
78
|
+
# Crawl nodes via WebClient::Connection
|
79
|
+
#
|
80
|
+
# @param opts [Hash] options to crawl nodes with
|
81
|
+
# @option opts [WebSocket::Connection] :connection Connection
|
82
|
+
# to use to crawl nodes
|
83
|
+
# @option opts [Integer] :delay optional delay to wait between
|
84
|
+
# crawl iterations
|
85
|
+
def self.crawl(start, opts={})
|
86
|
+
set_opts(opts)
|
87
|
+
delay = opts[:delay] || 1
|
88
|
+
|
89
|
+
queue = Array.new
|
90
|
+
queue << start
|
91
|
+
|
92
|
+
connection.add_plugin :result_parser unless connection.plugin?(:result_parser)
|
93
|
+
connection.add_plugin Parsers::NodePeers unless connection.plugin?(Parsers::NodePeers)
|
94
|
+
|
95
|
+
connection.ssl_verify_peer = false
|
96
|
+
connection.ssl_verify_host = false
|
97
|
+
|
98
|
+
until connection.force_quit?
|
99
|
+
node = queue.shift
|
100
|
+
node = parse_url node unless node.is_a?(Node)
|
101
|
+
|
102
|
+
connection.emit :precrawl, node
|
103
|
+
connection.url = node.url
|
104
|
+
|
105
|
+
peers = connection.perform
|
106
|
+
if peers.nil? || peers.empty?
|
107
|
+
queue << node
|
108
|
+
connection.emit :crawlerr, node
|
109
|
+
connection.rsleep(delay) unless connection.force_quit?
|
110
|
+
next
|
111
|
+
end
|
112
|
+
|
113
|
+
connection.emit :peers, node, peers
|
114
|
+
peers.each { |peer|
|
115
|
+
break if connection.force_quit?
|
116
|
+
|
117
|
+
peer = Node.from_peer peer
|
118
|
+
next unless peer.valid? # skip unless valid
|
119
|
+
|
120
|
+
connection.emit :peer, node, peer
|
121
|
+
queue << peer unless queue.include?(peer)
|
122
|
+
}
|
123
|
+
|
124
|
+
queue << node
|
125
|
+
connection.emit :postcrawl, node
|
126
|
+
connection.rsleep(delay) unless connection.force_quit?
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end # module Model
|
131
|
+
end # module XRBP
|