xrbp 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +103 -5
  3. data/examples/accounts.rb +13 -0
  4. data/examples/autoconnect_timeout.rb +5 -1
  5. data/examples/autorety.rb +7 -0
  6. data/examples/crawl_nodes.rb +32 -0
  7. data/examples/dsl/account.rb +16 -0
  8. data/examples/dsl/ledger.rb +7 -0
  9. data/examples/dsl/ledger_subscribe.rb +18 -0
  10. data/examples/dsl/validators.rb +8 -0
  11. data/examples/gateways.rb +8 -0
  12. data/examples/latest_account.rb +4 -0
  13. data/examples/ledger_multi_subscribe.rb +1 -1
  14. data/examples/ledger_subscribe.rb +2 -2
  15. data/examples/market.rb +13 -0
  16. data/examples/username.rb +12 -0
  17. data/examples/validator.rb +8 -0
  18. data/lib/xrbp.rb +5 -1
  19. data/lib/xrbp/common.rb +10 -0
  20. data/lib/xrbp/core_ext.rb +24 -0
  21. data/lib/xrbp/dsl.rb +25 -0
  22. data/lib/xrbp/dsl/accounts.rb +13 -0
  23. data/lib/xrbp/dsl/ledgers.rb +23 -0
  24. data/lib/xrbp/dsl/validators.rb +10 -0
  25. data/lib/xrbp/dsl/webclient.rb +8 -0
  26. data/lib/xrbp/dsl/websocket.rb +28 -0
  27. data/lib/xrbp/model.rb +4 -0
  28. data/lib/xrbp/model/account.rb +142 -1
  29. data/lib/xrbp/model/base.rb +1 -0
  30. data/lib/xrbp/model/gateway.rb +24 -0
  31. data/lib/xrbp/model/ledger.rb +30 -1
  32. data/lib/xrbp/model/market.rb +52 -0
  33. data/lib/xrbp/model/node.rb +131 -0
  34. data/lib/xrbp/model/parsers/account.rb +44 -0
  35. data/lib/xrbp/model/parsers/gateway.rb +40 -0
  36. data/lib/xrbp/model/parsers/market.rb +28 -0
  37. data/lib/xrbp/model/parsers/node.rb +19 -0
  38. data/lib/xrbp/model/parsers/quote.rb +47 -0
  39. data/lib/xrbp/model/parsers/validator.rb +25 -0
  40. data/lib/xrbp/model/validator.rb +24 -0
  41. data/lib/xrbp/plugins.rb +6 -0
  42. data/lib/xrbp/plugins/base.rb +10 -0
  43. data/lib/xrbp/plugins/has_plugin.rb +45 -0
  44. data/lib/xrbp/plugins/has_result_parsers.rb +27 -0
  45. data/lib/xrbp/plugins/plugin_registry.rb +20 -0
  46. data/lib/xrbp/plugins/result_parser.rb +19 -0
  47. data/lib/xrbp/terminatable.rb +19 -0
  48. data/lib/xrbp/thread_registry.rb +22 -0
  49. data/lib/xrbp/version.rb +1 -1
  50. data/lib/xrbp/webclient.rb +2 -0
  51. data/lib/xrbp/webclient/connection.rb +100 -0
  52. data/lib/xrbp/webclient/plugins.rb +8 -0
  53. data/lib/xrbp/webclient/plugins/autoretry.rb +54 -0
  54. data/lib/xrbp/webclient/plugins/result_parser.rb +23 -0
  55. data/lib/xrbp/websocket/client.rb +85 -24
  56. data/lib/xrbp/websocket/cmds/account_info.rb +4 -0
  57. data/lib/xrbp/websocket/cmds/account_lines.rb +5 -0
  58. data/lib/xrbp/websocket/cmds/account_objects.rb +4 -0
  59. data/lib/xrbp/websocket/cmds/account_offers.rb +5 -0
  60. data/lib/xrbp/websocket/cmds/account_tx.rb +4 -0
  61. data/lib/xrbp/websocket/cmds/book_offers.rb +4 -0
  62. data/lib/xrbp/websocket/cmds/ledger.rb +3 -0
  63. data/lib/xrbp/websocket/cmds/ledger_entry.rb +4 -0
  64. data/lib/xrbp/websocket/cmds/paginated.rb +4 -1
  65. data/lib/xrbp/websocket/cmds/server_info.rb +4 -0
  66. data/lib/xrbp/websocket/cmds/subscribe.rb +4 -0
  67. data/lib/xrbp/websocket/command.rb +11 -0
  68. data/lib/xrbp/websocket/connection.rb +75 -22
  69. data/lib/xrbp/websocket/message.rb +5 -2
  70. data/lib/xrbp/websocket/multi/fallback.rb +2 -4
  71. data/lib/xrbp/websocket/multi/multi_connection.rb +37 -2
  72. data/lib/xrbp/websocket/multi/parallel.rb +2 -4
  73. data/lib/xrbp/websocket/multi/prioritized.rb +2 -4
  74. data/lib/xrbp/websocket/multi/round_robin.rb +4 -0
  75. data/lib/xrbp/websocket/plugins.rb +1 -7
  76. data/lib/xrbp/websocket/plugins/autoconnect.rb +25 -5
  77. data/lib/xrbp/websocket/plugins/command_dispatcher.rb +5 -0
  78. data/lib/xrbp/websocket/plugins/command_paginator.rb +9 -8
  79. data/lib/xrbp/websocket/plugins/connection_timeout.rb +27 -16
  80. data/lib/xrbp/websocket/plugins/message_dispatcher.rb +60 -30
  81. data/lib/xrbp/websocket/plugins/result_parser.rb +19 -19
  82. data/lib/xrbp/websocket/socket.rb +23 -6
  83. metadata +118 -8
  84. data/lib/xrbp/network.rb +0 -6
  85. data/lib/xrbp/network/nodes.rb +0 -8
  86. 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
@@ -0,0 +1,6 @@
1
+ require_relative './plugins/base'
2
+ require_relative './plugins/has_plugin'
3
+ require_relative './plugins/plugin_registry'
4
+
5
+ require_relative './plugins/result_parser'
6
+ require_relative './plugins/has_result_parsers'
@@ -0,0 +1,10 @@
1
+ module XRBP
2
+ # Base plugin definition, common logic shared by all connection plugins.
3
+ class PluginBase
4
+ attr_accessor :connection
5
+
6
+ def initialize(connection)
7
+ @connection = connection
8
+ end
9
+ end # class PluginBase
10
+ end # module XRBP
@@ -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
@@ -1,3 +1,3 @@
1
1
  module XRBP
2
- VERSION = '0.0.1'
2
+ VERSION = '0.1.0'
3
3
  end # module XRBP
@@ -0,0 +1,2 @@
1
+ require 'xrbp/webclient/connection'
2
+ require 'xrbp/webclient/plugins'
@@ -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