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.
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