xrbp 0.0.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.
- checksums.yaml +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +7 -0
- data/examples/autoconnect_timeout.rb +20 -0
- data/examples/cmd.rb +13 -0
- data/examples/ledger.rb +15 -0
- data/examples/ledger_multi_subscribe.rb +20 -0
- data/examples/ledger_subscribe.rb +18 -0
- data/examples/multi.rb +10 -0
- data/examples/paginate.rb +13 -0
- data/examples/prioritized.rb +13 -0
- data/examples/round_robin.rb +11 -0
- data/examples/websocket.rb +20 -0
- data/lib/xrbp/model/account.rb +27 -0
- data/lib/xrbp/model/base.rb +31 -0
- data/lib/xrbp/model/ledger.rb +24 -0
- data/lib/xrbp/model.rb +3 -0
- data/lib/xrbp/network/nodes.rb +8 -0
- data/lib/xrbp/network.rb +6 -0
- data/lib/xrbp/version.rb +3 -0
- data/lib/xrbp/websocket/client.rb +127 -0
- data/lib/xrbp/websocket/cmds/account_info.rb +20 -0
- data/lib/xrbp/websocket/cmds/account_lines.rb +33 -0
- data/lib/xrbp/websocket/cmds/account_objects.rb +33 -0
- data/lib/xrbp/websocket/cmds/account_offers.rb +33 -0
- data/lib/xrbp/websocket/cmds/account_tx.rb +20 -0
- data/lib/xrbp/websocket/cmds/book_offers.rb +38 -0
- data/lib/xrbp/websocket/cmds/ledger.rb +25 -0
- data/lib/xrbp/websocket/cmds/ledger_entry.rb +16 -0
- data/lib/xrbp/websocket/cmds/paginated.rb +43 -0
- data/lib/xrbp/websocket/cmds/server_info.rb +11 -0
- data/lib/xrbp/websocket/cmds/subscribe.rb +18 -0
- data/lib/xrbp/websocket/cmds.rb +12 -0
- data/lib/xrbp/websocket/command.rb +19 -0
- data/lib/xrbp/websocket/connection.rb +169 -0
- data/lib/xrbp/websocket/has_plugin.rb +30 -0
- data/lib/xrbp/websocket/message.rb +41 -0
- data/lib/xrbp/websocket/multi/fallback.rb +18 -0
- data/lib/xrbp/websocket/multi/multi_connection.rb +55 -0
- data/lib/xrbp/websocket/multi/parallel.rb +28 -0
- data/lib/xrbp/websocket/multi/prioritized.rb +15 -0
- data/lib/xrbp/websocket/multi/round_robin.rb +19 -0
- data/lib/xrbp/websocket/plugins/autoconnect.rb +42 -0
- data/lib/xrbp/websocket/plugins/command_dispatcher.rb +39 -0
- data/lib/xrbp/websocket/plugins/command_paginator.rb +45 -0
- data/lib/xrbp/websocket/plugins/connection_timeout.rb +54 -0
- data/lib/xrbp/websocket/plugins/message_dispatcher.rb +141 -0
- data/lib/xrbp/websocket/plugins/result_parser.rb +31 -0
- data/lib/xrbp/websocket/plugins.rb +18 -0
- data/lib/xrbp/websocket/socket.rb +109 -0
- data/lib/xrbp/websocket.rb +15 -0
- data/lib/xrbp.rb +5 -0
- metadata +123 -0
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'active_support/core_ext/hash/except'
|
2
|
+
|
3
|
+
module XRBP
|
4
|
+
module WebSocket
|
5
|
+
module Cmds
|
6
|
+
module Paginated
|
7
|
+
attr_reader :prev_cmd
|
8
|
+
|
9
|
+
def root_cmd
|
10
|
+
return self unless prev_cmd
|
11
|
+
prev_cmd.root_cmd
|
12
|
+
end
|
13
|
+
|
14
|
+
def each_ancestor(&bl)
|
15
|
+
bl.call self
|
16
|
+
prev_cmd.each_ancestor &bl if prev_cmd
|
17
|
+
end
|
18
|
+
|
19
|
+
def parse_paginate(args)
|
20
|
+
@paginate = args[:paginate]
|
21
|
+
@prev_cmd = args[:prev_cmd]
|
22
|
+
end
|
23
|
+
|
24
|
+
def paginate_args
|
25
|
+
return :prev_cmd #, :paginate # XXX need to forward paginate
|
26
|
+
end
|
27
|
+
|
28
|
+
def args_without_paginate
|
29
|
+
args.except(*paginate_args)
|
30
|
+
end
|
31
|
+
|
32
|
+
def paginate?
|
33
|
+
!!@paginate
|
34
|
+
end
|
35
|
+
|
36
|
+
def next_page(marker)
|
37
|
+
self.class.from_h(to_h.merge({:marker => marker,
|
38
|
+
:prev_cmd => self}))
|
39
|
+
end
|
40
|
+
end # module Paginated
|
41
|
+
end # module Cmds
|
42
|
+
end # module WebSocket
|
43
|
+
end # module Wipple
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module XRBP
|
2
|
+
module WebSocket
|
3
|
+
module Cmds
|
4
|
+
class Subscribe < Command
|
5
|
+
attr_accessor :args
|
6
|
+
|
7
|
+
def initialize(args={})
|
8
|
+
@args = args
|
9
|
+
super(to_h)
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_h
|
13
|
+
args.merge(:command => :subscribe)
|
14
|
+
end
|
15
|
+
end # class Subscribe
|
16
|
+
end # module Cmds
|
17
|
+
end # module WebSocket
|
18
|
+
end # module Wipple
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require_relative './cmds/paginated'
|
2
|
+
require_relative './cmds/server_info'
|
3
|
+
require_relative './cmds/ledger'
|
4
|
+
require_relative './cmds/account_lines'
|
5
|
+
require_relative './cmds/account_info'
|
6
|
+
require_relative './cmds/account_info'
|
7
|
+
require_relative './cmds/account_objects'
|
8
|
+
require_relative './cmds/account_offers'
|
9
|
+
require_relative './cmds/account_tx'
|
10
|
+
require_relative './cmds/book_offers'
|
11
|
+
require_relative './cmds/subscribe'
|
12
|
+
require_relative './cmds/ledger_entry'
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module XRBP
|
4
|
+
module WebSocket
|
5
|
+
class Command < Message
|
6
|
+
attr_accessor :id
|
7
|
+
|
8
|
+
def initialize(data)
|
9
|
+
@@id ||= 0
|
10
|
+
@id = (@@id += 1)
|
11
|
+
|
12
|
+
json = Hash[data]
|
13
|
+
json['id'] = id
|
14
|
+
|
15
|
+
super(json.to_json)
|
16
|
+
end
|
17
|
+
end # class Command
|
18
|
+
end # module WebSocket
|
19
|
+
end # module XRBP
|
@@ -0,0 +1,169 @@
|
|
1
|
+
require_relative './has_plugin'
|
2
|
+
|
3
|
+
module XRBP
|
4
|
+
module WebSocket
|
5
|
+
class Connection
|
6
|
+
include EventEmitter
|
7
|
+
include HasPlugin
|
8
|
+
|
9
|
+
attr_reader :url
|
10
|
+
attr_accessor :parent
|
11
|
+
|
12
|
+
def initialize(url)
|
13
|
+
@url = url
|
14
|
+
@force_quit = false
|
15
|
+
end
|
16
|
+
|
17
|
+
###
|
18
|
+
|
19
|
+
def connect
|
20
|
+
client.connect
|
21
|
+
end
|
22
|
+
|
23
|
+
def next_connection(prev)
|
24
|
+
return nil unless !!parent
|
25
|
+
parent.next_connection(prev)
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialized?
|
29
|
+
!!@client
|
30
|
+
end
|
31
|
+
|
32
|
+
def open?
|
33
|
+
initialized? && client.open?
|
34
|
+
end
|
35
|
+
|
36
|
+
def closed?
|
37
|
+
!open?
|
38
|
+
end
|
39
|
+
|
40
|
+
def close!
|
41
|
+
client.close if open?
|
42
|
+
end
|
43
|
+
|
44
|
+
def send_data(data)
|
45
|
+
client.send_data(data)
|
46
|
+
end
|
47
|
+
|
48
|
+
###
|
49
|
+
|
50
|
+
def force_quit?
|
51
|
+
@force_quit
|
52
|
+
end
|
53
|
+
|
54
|
+
def force_quit!
|
55
|
+
@force_quit = true
|
56
|
+
wake_all
|
57
|
+
end
|
58
|
+
|
59
|
+
def thread_registry
|
60
|
+
@thread_registry ||= Concurrent::Array.new
|
61
|
+
end
|
62
|
+
|
63
|
+
def rsleep(t)
|
64
|
+
thread_registry << Thread.current
|
65
|
+
sleep(t)
|
66
|
+
thread_registry.delete(Thread.current)
|
67
|
+
end
|
68
|
+
|
69
|
+
def wake_all
|
70
|
+
thread_registry.each { |th| th.wakeup }
|
71
|
+
end
|
72
|
+
|
73
|
+
###
|
74
|
+
|
75
|
+
def wait_for_open
|
76
|
+
return unless initialized?
|
77
|
+
|
78
|
+
state_mutex.synchronize {
|
79
|
+
open_cv.wait(state_mutex, 0.1)
|
80
|
+
} until force_quit? || open?
|
81
|
+
end
|
82
|
+
|
83
|
+
def wait_for_close
|
84
|
+
return unless initialized?
|
85
|
+
|
86
|
+
state_mutex.synchronize {
|
87
|
+
close_cv.wait(state_mutex, 0.1)
|
88
|
+
} while !force_quit? && open?
|
89
|
+
end
|
90
|
+
|
91
|
+
def state_mutex
|
92
|
+
@state_mutex ||= Mutex.new
|
93
|
+
end
|
94
|
+
|
95
|
+
def open_cv
|
96
|
+
@open_cv ||= ConditionVariable.new
|
97
|
+
end
|
98
|
+
|
99
|
+
def close_cv
|
100
|
+
@close_cv ||= ConditionVariable.new
|
101
|
+
end
|
102
|
+
|
103
|
+
###
|
104
|
+
|
105
|
+
def client
|
106
|
+
@client ||= begin
|
107
|
+
client = Client.new(@url)
|
108
|
+
conn = self
|
109
|
+
|
110
|
+
client.on :connecting do
|
111
|
+
conn.emit :connecting
|
112
|
+
conn.parent.emit :connecting if conn.parent
|
113
|
+
end
|
114
|
+
|
115
|
+
client.on :open do
|
116
|
+
conn.emit :open
|
117
|
+
conn.parent.emit :open if conn.parent
|
118
|
+
|
119
|
+
conn.state_mutex.synchronize {
|
120
|
+
conn.open_cv.signal
|
121
|
+
}
|
122
|
+
|
123
|
+
conn.plugins.each { |plg|
|
124
|
+
plg.opened if plg.respond_to?(:opened)
|
125
|
+
}
|
126
|
+
end
|
127
|
+
|
128
|
+
client.on :close do
|
129
|
+
conn.emit :close
|
130
|
+
conn.parent.emit :close if conn.parent
|
131
|
+
|
132
|
+
conn.state_mutex.synchronize {
|
133
|
+
conn.close_cv.signal
|
134
|
+
}
|
135
|
+
|
136
|
+
conn.plugins.each { |plg|
|
137
|
+
plg.closed if plg.respond_to?(:closed)
|
138
|
+
}
|
139
|
+
end
|
140
|
+
|
141
|
+
client.on :completed do |err|
|
142
|
+
conn.emit :completed
|
143
|
+
conn.parent.emit :completed if conn.parent
|
144
|
+
end
|
145
|
+
|
146
|
+
client.on :error do |err|
|
147
|
+
conn.emit :error, err
|
148
|
+
conn.parent.emit :error, err if conn.parent
|
149
|
+
|
150
|
+
conn.plugins.each { |plg|
|
151
|
+
plg.error err if plg.respond_to?(:error)
|
152
|
+
}
|
153
|
+
end
|
154
|
+
|
155
|
+
client.on :message do |msg|
|
156
|
+
conn.emit :message, msg
|
157
|
+
conn.parent.emit :message, msg if conn.parent
|
158
|
+
|
159
|
+
conn.plugins.each { |plg|
|
160
|
+
plg.message msg if plg.respond_to?(:message)
|
161
|
+
}
|
162
|
+
end
|
163
|
+
|
164
|
+
client
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end # class Connection
|
168
|
+
end # module WebSocket
|
169
|
+
end # module XRBP
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module XRBP
|
2
|
+
module WebSocket
|
3
|
+
module HasPlugin
|
4
|
+
def plugins
|
5
|
+
@plugins ||= []
|
6
|
+
end
|
7
|
+
|
8
|
+
def add_plugin(*plgs)
|
9
|
+
plgs.each { |plg|
|
10
|
+
plg = WebSocket.plugins[plg] if plg.is_a?(Symbol)
|
11
|
+
raise ArgumentError unless !!plg
|
12
|
+
plg = plg.new(self)
|
13
|
+
plugins << plg
|
14
|
+
plg.added if plg.respond_to?(:added)
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
def plugin?(plg)
|
19
|
+
plugins.collect { |plg| plg.class }.include?(plg)
|
20
|
+
end
|
21
|
+
|
22
|
+
def define_instance_method(name, &block)
|
23
|
+
(class << self; self; end).class_eval do
|
24
|
+
define_method name, &block
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end # module HasPlugin
|
29
|
+
end # module WebSocket
|
30
|
+
end # module XRBP
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module XRBP
|
2
|
+
module WebSocket
|
3
|
+
class Message
|
4
|
+
attr_reader :result, :time
|
5
|
+
attr_accessor :connection
|
6
|
+
|
7
|
+
def initialize(data)
|
8
|
+
@data = data
|
9
|
+
@result = nil
|
10
|
+
@cv = ConditionVariable.new
|
11
|
+
@signalled = false
|
12
|
+
@time = Time.now
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_s
|
16
|
+
@data
|
17
|
+
end
|
18
|
+
|
19
|
+
def signal
|
20
|
+
@signalled = true
|
21
|
+
@cv.signal
|
22
|
+
end
|
23
|
+
|
24
|
+
def wait
|
25
|
+
connection.state_mutex.synchronize {
|
26
|
+
# only wait if we haven't received response
|
27
|
+
@cv.wait(connection.state_mutex) unless connection.closed? || @signalled
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
attr_writer :bl
|
32
|
+
|
33
|
+
def bl
|
34
|
+
@bl ||= proc { |res|
|
35
|
+
@result = res
|
36
|
+
signal
|
37
|
+
}
|
38
|
+
end
|
39
|
+
end # class Message
|
40
|
+
end # module WebSocket
|
41
|
+
end # module XRBP
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module XRBP
|
2
|
+
module WebSocket
|
3
|
+
class Fallback < MultiConnection
|
4
|
+
def initialize(*urls)
|
5
|
+
super(*urls)
|
6
|
+
end
|
7
|
+
|
8
|
+
def next_connection(prev=nil)
|
9
|
+
unless prev.nil?
|
10
|
+
return nil if connections.last == prev
|
11
|
+
return connections[(connections.index(prev) + 1)..-1].find { |c| !c.closed? }
|
12
|
+
end
|
13
|
+
|
14
|
+
connections.find { |c| !c.closed? }
|
15
|
+
end
|
16
|
+
end # class Fallback
|
17
|
+
end # module WebSocket
|
18
|
+
end # module XRBP
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require_relative '../has_plugin'
|
2
|
+
|
3
|
+
module XRBP
|
4
|
+
module WebSocket
|
5
|
+
class MultiConnection
|
6
|
+
include EventEmitter
|
7
|
+
include HasPlugin
|
8
|
+
|
9
|
+
attr_reader :connections
|
10
|
+
|
11
|
+
def initialize(*urls)
|
12
|
+
@connections = []
|
13
|
+
|
14
|
+
urls.each { |url|
|
15
|
+
@connections << Connection.new(url)
|
16
|
+
}
|
17
|
+
|
18
|
+
connections.each { |c| c.parent = self }
|
19
|
+
|
20
|
+
yield self if block_given?
|
21
|
+
end
|
22
|
+
|
23
|
+
def force_quit!
|
24
|
+
connections.each { |c| c.force_quit! }
|
25
|
+
end
|
26
|
+
|
27
|
+
def close!
|
28
|
+
connections.each { |c| c.close! }
|
29
|
+
end
|
30
|
+
|
31
|
+
alias :_add_plugin :add_plugin
|
32
|
+
|
33
|
+
def add_plugin(*plg)
|
34
|
+
connections.each { |c|
|
35
|
+
c.add_plugin *plg
|
36
|
+
}
|
37
|
+
|
38
|
+
_add_plugin(*plg)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Always return first connection by default,
|
42
|
+
# override in subclasses
|
43
|
+
def next_connection(prev=nil)
|
44
|
+
return nil unless prev.nil?
|
45
|
+
@connections.first
|
46
|
+
end
|
47
|
+
|
48
|
+
def connect
|
49
|
+
@connections.each { |c|
|
50
|
+
c.connect
|
51
|
+
}
|
52
|
+
end
|
53
|
+
end # class MultiConnection
|
54
|
+
end # module WebSocket
|
55
|
+
end # module XRBP
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module XRBP
|
2
|
+
module WebSocket
|
3
|
+
class Parallel < MultiConnection
|
4
|
+
class All
|
5
|
+
attr_accessor :connections
|
6
|
+
|
7
|
+
def initialize(connections)
|
8
|
+
@connections = connections
|
9
|
+
end
|
10
|
+
|
11
|
+
def method_missing(m, *args, &bl)
|
12
|
+
connections.collect { |c|
|
13
|
+
c.send(m, *args, &bl) if c.open?
|
14
|
+
}
|
15
|
+
end
|
16
|
+
end # class All
|
17
|
+
|
18
|
+
def initialize(*urls)
|
19
|
+
super(*urls)
|
20
|
+
end
|
21
|
+
|
22
|
+
def next_connection(prev=nil)
|
23
|
+
return nil unless prev.nil?
|
24
|
+
All.new connections
|
25
|
+
end
|
26
|
+
end # class RoundRobin
|
27
|
+
end # module WebSocket
|
28
|
+
end # module XRBP
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module XRBP
|
2
|
+
module WebSocket
|
3
|
+
class Prioritized < MultiConnection
|
4
|
+
def initialize(*urls)
|
5
|
+
super(*urls)
|
6
|
+
end
|
7
|
+
|
8
|
+
def next_connection(prev=nil)
|
9
|
+
return nil if prev == connections.last
|
10
|
+
return super if prev.nil?
|
11
|
+
connections[connections.index(prev)+1]
|
12
|
+
end
|
13
|
+
end # class Prioritized
|
14
|
+
end # module WebSocket
|
15
|
+
end # module XRBP
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module XRBP
|
2
|
+
module WebSocket
|
3
|
+
class RoundRobin < MultiConnection
|
4
|
+
def initialize(*urls)
|
5
|
+
super(*urls)
|
6
|
+
@current = 0
|
7
|
+
end
|
8
|
+
|
9
|
+
def next_connection(prev=nil)
|
10
|
+
return nil unless prev.nil?
|
11
|
+
|
12
|
+
c = connections[@current]
|
13
|
+
@current += 1
|
14
|
+
@current = 0 if @current >= connections.size
|
15
|
+
c
|
16
|
+
end
|
17
|
+
end # class RoundRobin
|
18
|
+
end # module WebSocket
|
19
|
+
end # module XRBP
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module XRBP
|
2
|
+
module WebSocket
|
3
|
+
module Plugins
|
4
|
+
# Automatically reconnects on close events
|
5
|
+
class AutoConnect
|
6
|
+
attr_reader :connection
|
7
|
+
|
8
|
+
def initialize(connection)
|
9
|
+
@connection = connection
|
10
|
+
end
|
11
|
+
|
12
|
+
def added
|
13
|
+
return if connection.kind_of?(MultiConnection)
|
14
|
+
|
15
|
+
conn = connection
|
16
|
+
|
17
|
+
connection.on :completed do
|
18
|
+
connected = false
|
19
|
+
until conn.force_quit? || connected
|
20
|
+
begin
|
21
|
+
conn.connect
|
22
|
+
connected = true
|
23
|
+
rescue
|
24
|
+
conn.rsleep(3)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
until connection.force_quit? || connection.open?
|
30
|
+
begin
|
31
|
+
connection.connect
|
32
|
+
rescue
|
33
|
+
connection.rsleep(3)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end # class AutoConnect
|
38
|
+
|
39
|
+
WebSocket.register_plugin :autoconnect, AutoConnect
|
40
|
+
end # module Plugins
|
41
|
+
end # module WebSocket
|
42
|
+
end # module XRBP
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module XRBP
|
2
|
+
module WebSocket
|
3
|
+
module Plugins
|
4
|
+
# Dispatch commands (based on message dispatcher)
|
5
|
+
class CommandDispatcher < MessageDispatcher
|
6
|
+
def added
|
7
|
+
super
|
8
|
+
|
9
|
+
plugin = self
|
10
|
+
|
11
|
+
connection.define_instance_method(:cmd) do |cmd, &bl|
|
12
|
+
return next_connection.cmd cmd, &bl if self.kind_of?(MultiConnection)
|
13
|
+
|
14
|
+
cmd = Command.new(cmd) unless cmd.kind_of?(Command)
|
15
|
+
msg(cmd, &bl)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def match_message(msg)
|
20
|
+
begin
|
21
|
+
return nil if msg.data == ""
|
22
|
+
parsed = JSON.parse(msg.data)
|
23
|
+
|
24
|
+
rescue => e
|
25
|
+
return nil
|
26
|
+
end
|
27
|
+
|
28
|
+
id = parsed['id']
|
29
|
+
msg = messages.find { |msg| msg.kind_of?(Command) && msg.id == id }
|
30
|
+
|
31
|
+
return nil unless msg
|
32
|
+
[msg, parsed]
|
33
|
+
end
|
34
|
+
end # class CommandDispatcher
|
35
|
+
|
36
|
+
WebSocket.register_plugin :command_dispatcher, CommandDispatcher
|
37
|
+
end # module Plugins
|
38
|
+
end # module WebSocket
|
39
|
+
end # module XRBP
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module XRBP
|
2
|
+
module WebSocket
|
3
|
+
module Plugins
|
4
|
+
# Handles multi-page messages
|
5
|
+
class CommandPaginator
|
6
|
+
attr_reader :connection
|
7
|
+
|
8
|
+
def initialize(connection)
|
9
|
+
@connection = connection
|
10
|
+
end
|
11
|
+
|
12
|
+
def added
|
13
|
+
raise "Must also include CommandDispatcher plugin" unless connection.plugin?(CommandDispatcher)
|
14
|
+
end
|
15
|
+
|
16
|
+
def unlock!(cmd, res)
|
17
|
+
return true unless cmd.respond_to?(:paginate?) && cmd.paginate?
|
18
|
+
|
19
|
+
marker = res["result"]["marker"]
|
20
|
+
page = res["result"][cmd.page_title]
|
21
|
+
|
22
|
+
if marker && next_cmd = cmd.next_page(marker)
|
23
|
+
connection.cmd next_cmd do
|
24
|
+
page
|
25
|
+
end
|
26
|
+
|
27
|
+
else
|
28
|
+
# XXX can't recursively use stack to unwind
|
29
|
+
# callbacks as there may be too many pages.
|
30
|
+
# Do it serially.
|
31
|
+
res = Array.new(page)
|
32
|
+
cmd.each_ancestor { |page_cmd|
|
33
|
+
page_res = page_cmd.bl.call res
|
34
|
+
res = page_res + res if page_cmd.prev_cmd
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
false
|
39
|
+
end
|
40
|
+
end # class CommandPaginator
|
41
|
+
|
42
|
+
WebSocket.register_plugin :command_paginator, CommandPaginator
|
43
|
+
end # module Plugins
|
44
|
+
end # module WebSocket
|
45
|
+
end # module XRBP
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module XRBP
|
2
|
+
module WebSocket
|
3
|
+
module Plugins
|
4
|
+
# Automatic disconnection if no server data in certain time
|
5
|
+
class ConnectionTimeout
|
6
|
+
attr_reader :connection
|
7
|
+
attr_accessor :connection_timeout
|
8
|
+
|
9
|
+
DEFAULT_TIMEOUT = 10
|
10
|
+
|
11
|
+
def initialize(connection)
|
12
|
+
@connection = connection
|
13
|
+
@connection_timeout = DEFAULT_TIMEOUT
|
14
|
+
end
|
15
|
+
|
16
|
+
def added
|
17
|
+
plugin = self
|
18
|
+
connection.define_instance_method(:connection_timeout=) do |t|
|
19
|
+
plugin.connection_timeout = t
|
20
|
+
|
21
|
+
self.plugins.find { |plg|
|
22
|
+
plg.is_a?(ConnectionTimeout)
|
23
|
+
}.connection_timeout = t if self.kind_of?(MultiConnection)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def message(msg)
|
28
|
+
@last_msg = Time.now
|
29
|
+
end
|
30
|
+
|
31
|
+
def opened
|
32
|
+
@thread = Thread.new {
|
33
|
+
@last_msg = Time.now
|
34
|
+
until connection.force_quit? ||
|
35
|
+
connection.closed? ||
|
36
|
+
Thread.current != @thread
|
37
|
+
if Time.now - @last_msg > @connection_timeout
|
38
|
+
connection.close!
|
39
|
+
end
|
40
|
+
|
41
|
+
connection.rsleep(0.1)
|
42
|
+
end
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
def closed
|
47
|
+
@thread.join unless @thread == Thread.current
|
48
|
+
end
|
49
|
+
end # class ConnectionTimeout
|
50
|
+
|
51
|
+
WebSocket.register_plugin :connection_timeout, ConnectionTimeout
|
52
|
+
end # module Plugins
|
53
|
+
end # module WebSocket
|
54
|
+
end # module XRBP
|