ripple-rest 0.0.2 → 1.0.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.
- data/README.md +8 -0
- data/lib/ripple-rest.rb +4 -8
- data/lib/ripple-rest/{schemas/json.rb → generated-schemas.rb} +154 -149
- data/lib/ripple-rest/{schemas/payments.rb → helpers.rb} +129 -22
- data/lib/ripple-rest/rest-object.rb +191 -0
- data/lib/ripple-rest/schemas.rb +82 -0
- data/lib/ripple-rest/version.rb +2 -2
- metadata +6 -43
- data/lib/ripple-rest/schemas/account.rb +0 -84
- data/lib/ripple-rest/schemas/account_settings.rb +0 -22
- data/lib/ripple-rest/schemas/amount.rb +0 -21
- data/lib/ripple-rest/schemas/balance.rb +0 -8
- data/lib/ripple-rest/schemas/json/AccountSettings.json +0 -63
- data/lib/ripple-rest/schemas/json/Amount.json +0 -28
- data/lib/ripple-rest/schemas/json/Balance.json +0 -23
- data/lib/ripple-rest/schemas/json/Currency.json +0 -7
- data/lib/ripple-rest/schemas/json/FloatString.json +0 -7
- data/lib/ripple-rest/schemas/json/Hash128.json +0 -7
- data/lib/ripple-rest/schemas/json/Hash256.json +0 -7
- data/lib/ripple-rest/schemas/json/Notification.json +0 -58
- data/lib/ripple-rest/schemas/json/Order.json +0 -82
- data/lib/ripple-rest/schemas/json/Payment.json +0 -98
- data/lib/ripple-rest/schemas/json/ResourceId.json +0 -7
- data/lib/ripple-rest/schemas/json/RippleAddress.json +0 -7
- data/lib/ripple-rest/schemas/json/Timestamp.json +0 -7
- data/lib/ripple-rest/schemas/json/Trustline.json +0 -58
- data/lib/ripple-rest/schemas/json/UINT32.json +0 -7
- data/lib/ripple-rest/schemas/json/URL.json +0 -7
- data/lib/ripple-rest/schemas/json/_generate.rb +0 -73
- data/lib/ripple-rest/schemas/notifications.rb +0 -19
- data/lib/ripple-rest/schemas/trustlines.rb +0 -40
@@ -1,35 +1,103 @@
|
|
1
1
|
module RippleRest
|
2
|
-
class
|
3
|
-
#
|
4
|
-
|
5
|
-
|
2
|
+
class Account
|
3
|
+
# Account's Address (rXXXXXX...)
|
4
|
+
# @return [String]
|
5
|
+
attr_accessor :address
|
6
|
+
|
7
|
+
# Account's secret
|
8
|
+
# @return [String]
|
9
|
+
attr_accessor :secret
|
10
|
+
|
11
|
+
def initialize address, secret = nil
|
12
|
+
@address = address
|
13
|
+
@secret = secret
|
6
14
|
end
|
7
15
|
|
8
|
-
#
|
9
|
-
#
|
10
|
-
|
11
|
-
|
12
|
-
|
16
|
+
# Get an account's existing balances.
|
17
|
+
# This includes XRP balance (which does not include a counterparty) and trustline balances.
|
18
|
+
# @return [Array<Balance>]
|
19
|
+
# @raise [RippleRestError] if RippleRest server returns an error
|
20
|
+
# @raise [ProtocolError] if protocol is wrong or network is down
|
21
|
+
def balances
|
22
|
+
RippleRest
|
23
|
+
.get("v1/accounts/#{@address}/balances")["balances"]
|
24
|
+
.map(&Balance.method(:new))
|
13
25
|
end
|
14
26
|
|
15
|
-
#
|
16
|
-
# @return [
|
17
|
-
# @raise [ArgumentError] if secret is missing from the Account object
|
27
|
+
# Returns a Trustlines object for this account.
|
28
|
+
# @return [Trustlines]
|
18
29
|
# @raise [RippleRestError] if RippleRest server returns an error
|
19
30
|
# @raise [ProtocolError] if protocol is wrong or network is down
|
20
|
-
def
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
RippleRest.post("v1/payments", hash)["client_resource_id"]
|
31
|
+
def trustlines
|
32
|
+
data = RippleRest
|
33
|
+
.get("v1/accounts/#{@address}/trustlines")["trustlines"]
|
34
|
+
.map(&Trustline.method(:new))
|
35
|
+
obj = Trustlines.new data
|
36
|
+
obj.account = self
|
37
|
+
obj
|
29
38
|
end
|
30
39
|
|
40
|
+
# Returns a AccountSettings object for this account.
|
41
|
+
# @return [AccountSettings]
|
42
|
+
# @raise [RippleRestError] if RippleRest server returns an error
|
43
|
+
# @raise [ProtocolError] if protocol is wrong or network is down
|
44
|
+
def settings
|
45
|
+
data = RippleRest.get("v1/accounts/#{@address}/settings")["settings"]
|
46
|
+
obj = AccountSettings.new data
|
47
|
+
obj.account = self
|
48
|
+
obj
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns a Notifications object for this account.
|
52
|
+
# @return [Notifications]
|
53
|
+
def notifications
|
54
|
+
@notifications ||= lambda {
|
55
|
+
obj = Notifications.new
|
56
|
+
obj.account = self
|
57
|
+
obj
|
58
|
+
}.call
|
59
|
+
end
|
60
|
+
|
61
|
+
# Returns a Payments object for this account.
|
62
|
+
# @return [Payments]
|
63
|
+
def payments
|
64
|
+
payments ||= lambda {
|
65
|
+
obj = Payments.new
|
66
|
+
obj.account = self
|
67
|
+
obj
|
68
|
+
}.call
|
69
|
+
end
|
70
|
+
|
71
|
+
# Returns the address of attribute address.
|
31
72
|
# @return [String]
|
32
|
-
|
73
|
+
def to_s
|
74
|
+
address
|
75
|
+
end
|
76
|
+
|
77
|
+
# @!group Private APIs
|
78
|
+
# @api private
|
79
|
+
def require_secret
|
80
|
+
raise ArgumentError.new("Secret is required for this operation.") unless secret
|
81
|
+
end
|
82
|
+
# @!endgroup
|
83
|
+
end
|
84
|
+
|
85
|
+
class Notifications
|
86
|
+
# @return [Account]
|
87
|
+
attr_accessor :account
|
88
|
+
|
89
|
+
# Get notifications.
|
90
|
+
#
|
91
|
+
# Clients using notifications to monitor their account activity should pay particular attention to the `state` and `result` fields. The `state` field will either be `validated` or `failed` and represents the finalized status of that transaction. The `result` field will be `tesSUCCESS` if the `state` was validated. If the transaction failed, `result` will contain the `rippled` or `ripple-lib` error code.
|
92
|
+
#
|
93
|
+
# Notifications have `next_notification_url` and `previous_notification_url`'s. Account notifications can be polled by continuously following the `next_notification_url`, and handling the resultant notifications, until the `next_notification_url` is an empty string. This means that there are no new notifications but, as soon as there are, querying the same URL that produced this notification in the first place will return the same notification but with the `next_notification_url` set.
|
94
|
+
# @raise [RippleRestError] if RippleRest server returns an error
|
95
|
+
# @raise [ProtocolError] if protocol is wrong or network is down
|
96
|
+
# @return [Notification]
|
97
|
+
def [] hash
|
98
|
+
Notification.new RippleRest
|
99
|
+
.get("v1/accounts/#{account.address}/notifications/#{hash}")["notification"]
|
100
|
+
end
|
33
101
|
end
|
34
102
|
|
35
103
|
class Payments
|
@@ -103,4 +171,43 @@ module RippleRest
|
|
103
171
|
end
|
104
172
|
end
|
105
173
|
end
|
174
|
+
|
175
|
+
class Trustlines
|
176
|
+
include Enumerable
|
177
|
+
|
178
|
+
# @return [Account]
|
179
|
+
attr_accessor :account
|
180
|
+
|
181
|
+
def initialize data
|
182
|
+
@data = data
|
183
|
+
end
|
184
|
+
|
185
|
+
# Use with Enumerable
|
186
|
+
def each *args, &block
|
187
|
+
@data.each *args, &block
|
188
|
+
end
|
189
|
+
|
190
|
+
# Add trustline
|
191
|
+
# @param obj [String, Hash] Either a string representation of trustline limit, Hash containing value, currency, counterparty or a string form value/currency/counterparty.
|
192
|
+
# @param allow_rippling [Boolean] See [here](https://ripple.com/wiki/No_Ripple) for details
|
193
|
+
# @raise [ArgumentError] if secret is missing from the Account object
|
194
|
+
# @raise [RippleRestError] if RippleRest server returns an error
|
195
|
+
# @raise [ProtocolError] if protocol is wrong or network is down
|
196
|
+
def add obj, allow_rippling = true
|
197
|
+
raise ArgumentError.new("Account is missing.") unless account
|
198
|
+
account.require_secret
|
199
|
+
|
200
|
+
hash = {}
|
201
|
+
hash["allow_rippling"] = allow_rippling
|
202
|
+
hash["secret"] = account.secret
|
203
|
+
|
204
|
+
if obj.is_a? String
|
205
|
+
hash["trustline"] = { "limit" => obj }
|
206
|
+
else
|
207
|
+
hash["trustline"] = obj.to_hash
|
208
|
+
end
|
209
|
+
|
210
|
+
RippleRest.post "v1/accounts/#{account.address}/trustlines", hash
|
211
|
+
end
|
212
|
+
end
|
106
213
|
end
|
@@ -0,0 +1,191 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'time'
|
3
|
+
|
4
|
+
module RippleRest
|
5
|
+
# @api private
|
6
|
+
module RestTypeHelper
|
7
|
+
# @!visibility private
|
8
|
+
# @api private
|
9
|
+
def self.exist? key
|
10
|
+
@converter ||= {}
|
11
|
+
@converter.keys.include? key
|
12
|
+
end
|
13
|
+
|
14
|
+
# @!visibility private
|
15
|
+
# @api private
|
16
|
+
def self.register key, to, from, check, name = nil
|
17
|
+
return key if exist? key
|
18
|
+
|
19
|
+
@converter ||= {}
|
20
|
+
@converter[key] = [to, from, check, name ? name : key.to_s]
|
21
|
+
|
22
|
+
key
|
23
|
+
end
|
24
|
+
|
25
|
+
# @!visibility private
|
26
|
+
# @api private
|
27
|
+
def self.register_string key, regexp
|
28
|
+
reg = Regexp.new regexp
|
29
|
+
|
30
|
+
register key,
|
31
|
+
RETURN_SELF,
|
32
|
+
RETURN_SELF,
|
33
|
+
lambda { |x| (x.is_a?(String) && x.match(reg)) },
|
34
|
+
"String<#{key}>: #{reg.inspect}"
|
35
|
+
end
|
36
|
+
|
37
|
+
# @!visibility private
|
38
|
+
# @api private
|
39
|
+
def self.register_array type
|
40
|
+
register :"Array<#{type}>",
|
41
|
+
lambda { |x| x.map { |i| self.convert_to(type, i) } },
|
42
|
+
lambda { |x| x.map { |i| self.convert_from(type, i) } },
|
43
|
+
lambda { |x| (x.is_a?(Array) && x.all? {|i| self.convert_check(type, i) }) },
|
44
|
+
"Array<#{type}>"
|
45
|
+
end
|
46
|
+
|
47
|
+
# @!visibility private
|
48
|
+
# @api private
|
49
|
+
def self.register_string_otg regexp
|
50
|
+
register_string :"String<#{regexp.inspect}>", regexp
|
51
|
+
end
|
52
|
+
|
53
|
+
# @!visibility private
|
54
|
+
# @api private
|
55
|
+
def self.register_object key, klass
|
56
|
+
register key,
|
57
|
+
lambda { |x| x.to_hash },
|
58
|
+
lambda { |x| x.is_a?(klass) ? x : klass.new(x) },
|
59
|
+
lambda { |x| x.is_a? klass },
|
60
|
+
"#{key}"
|
61
|
+
end
|
62
|
+
|
63
|
+
# @!visibility private
|
64
|
+
# @api private
|
65
|
+
def self.convert_to type, obj
|
66
|
+
return nil if obj.nil?
|
67
|
+
@converter[type][0].call obj
|
68
|
+
end
|
69
|
+
|
70
|
+
# @!visibility private
|
71
|
+
# @api private
|
72
|
+
def self.convert_from type, obj
|
73
|
+
return nil if obj.nil?
|
74
|
+
@converter[type][1].call obj
|
75
|
+
end
|
76
|
+
|
77
|
+
# @!visibility private
|
78
|
+
# @api private
|
79
|
+
def self.convert_check type, obj
|
80
|
+
return true if obj.nil?
|
81
|
+
@converter[type][2].call obj
|
82
|
+
end
|
83
|
+
|
84
|
+
# @!visibility private
|
85
|
+
# @api private
|
86
|
+
def self.convert_raise type, obj
|
87
|
+
raise ArgumentError.new "#{obj.inspect} cannot be casted to #{@converter[type][3]}"
|
88
|
+
end
|
89
|
+
|
90
|
+
# @api private
|
91
|
+
RETURN_SELF = lambda { |x| x }
|
92
|
+
|
93
|
+
register :String,
|
94
|
+
RETURN_SELF,
|
95
|
+
RETURN_SELF,
|
96
|
+
lambda { |x| x.is_a?(String) }
|
97
|
+
|
98
|
+
register :Boolean,
|
99
|
+
RETURN_SELF,
|
100
|
+
RETURN_SELF,
|
101
|
+
lambda { |x| [true, false].include? x }
|
102
|
+
|
103
|
+
register :FloatString,
|
104
|
+
lambda { |x| x.is_a?(BigDecimal) ? x.to_s("F") : BigDecimal.new(x.to_s).to_s("F") },
|
105
|
+
lambda { |x| x.is_a?(BigDecimal) ? x : BigDecimal.new(x) },
|
106
|
+
lambda { |x| x.is_a?(BigDecimal) || x.respond_to?(:to_f)}
|
107
|
+
|
108
|
+
register :UINT32,
|
109
|
+
lambda { |x| x.to_i.to_s },
|
110
|
+
lambda { |x| x.to_i },
|
111
|
+
lambda { |x| x.is_a?(Numeric) && x >= 0 && x <= 4294967295 }
|
112
|
+
|
113
|
+
register :Timestamp,
|
114
|
+
lambda { |x| x.iso8601 },
|
115
|
+
lambda { |x| x.is_a?(Time) ? x : Time.iso8601(x) },
|
116
|
+
lambda { |x| x.is_a?(Time) || x.is_a?(String) }
|
117
|
+
|
118
|
+
register :URL,
|
119
|
+
lambda { |x| x.to_s },
|
120
|
+
lambda { |x| URI(x.to_s) },
|
121
|
+
lambda { |x| x.is_a?(URI) || x.respond_to?(:to_s) }
|
122
|
+
end
|
123
|
+
|
124
|
+
class RestObject
|
125
|
+
# @param data [Hash]
|
126
|
+
def initialize data = nil
|
127
|
+
data.each do |key, value|
|
128
|
+
if @@properties[self.class][key.to_sym]
|
129
|
+
self.instance_variable_set :"@#{key}", RestTypeHelper.convert_from(@@properties[self.class][key.to_sym], value)
|
130
|
+
else
|
131
|
+
warn "Cannot found field `#{key}' in RippleRestObject #{self.class}. The value will leave as-is. However, this will not be serialized to JSON."
|
132
|
+
self.instance_variable_set :"@#{key}", value
|
133
|
+
end
|
134
|
+
end if data
|
135
|
+
end
|
136
|
+
|
137
|
+
# Convert to a hash
|
138
|
+
# @return [Hash]
|
139
|
+
def to_hash
|
140
|
+
result = {}
|
141
|
+
@@properties[self.class].each do |key, type|
|
142
|
+
value = self.instance_variable_get(:"@#{key}")
|
143
|
+
if @@required[self.class][key] && value == nil
|
144
|
+
raise ArgumentError.new("Field `#{key}' is required in RippleRestObject #{self.class}.")
|
145
|
+
end
|
146
|
+
result[key] = RestTypeHelper.convert_to(type, value) if value != nil
|
147
|
+
end
|
148
|
+
|
149
|
+
result
|
150
|
+
end
|
151
|
+
|
152
|
+
# @!visibility protected
|
153
|
+
# @api private
|
154
|
+
def self.required symbol
|
155
|
+
@@required[self][symbol] = true
|
156
|
+
end
|
157
|
+
|
158
|
+
# @!visibility protected
|
159
|
+
# @api private
|
160
|
+
def self.property symbol, typeobj
|
161
|
+
if typeobj.is_a? Symbol
|
162
|
+
type = typeobj
|
163
|
+
elsif typeobj.is_a?(Array) && typeobj[0] == :String
|
164
|
+
type = RestTypeHelper.register_string_otg typeobj[1]
|
165
|
+
elsif typeobj.is_a?(Array) && typeobj[0] == :Array
|
166
|
+
type = RestTypeHelper.register_array typeobj[1]
|
167
|
+
end
|
168
|
+
|
169
|
+
define_method :"#{symbol}", &lambda { self.instance_variable_get :"@#{symbol}" }
|
170
|
+
define_method :"#{symbol}=", &lambda { |value|
|
171
|
+
begin
|
172
|
+
raise "" unless RestTypeHelper.convert_check(type, value)
|
173
|
+
self.instance_variable_set :"@#{symbol}", RestTypeHelper.convert_from(type, value)
|
174
|
+
rescue
|
175
|
+
RestTypeHelper.convert_raise type, value
|
176
|
+
end
|
177
|
+
}
|
178
|
+
|
179
|
+
@@properties[self][symbol] = type
|
180
|
+
end
|
181
|
+
|
182
|
+
@@required ||= Hash.new { |h, k| h[k] = Hash.new }
|
183
|
+
@@properties ||= Hash.new { |h, k| h[k] = Hash.new }
|
184
|
+
end
|
185
|
+
|
186
|
+
|
187
|
+
class << RestObject
|
188
|
+
protected :property
|
189
|
+
protected :required
|
190
|
+
end
|
191
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module RippleRest
|
2
|
+
class AccountSettings
|
3
|
+
# @return [Account]
|
4
|
+
attr_accessor :account
|
5
|
+
|
6
|
+
# Save the account settings
|
7
|
+
# @raise [ArgumentError] if secret is missing from the Account object
|
8
|
+
# @raise [RippleRestError] if RippleRest server returns an error
|
9
|
+
# @raise [ProtocolError] if protocol is wrong or network is down
|
10
|
+
# @return [void]
|
11
|
+
def save
|
12
|
+
raise ArgumentError.new("Account is missing.") unless account
|
13
|
+
|
14
|
+
account.require_secret
|
15
|
+
|
16
|
+
hash = to_hash
|
17
|
+
hash["secret"] = account.secret
|
18
|
+
|
19
|
+
RippleRest.post "v1/accounts/#{account.address}/settings", hash
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class Amount
|
24
|
+
# @return [String]
|
25
|
+
def to_s
|
26
|
+
"#{value}+#{currency}#{issuer.to_s.size > 0 ? ("+" + issuer) : ""}"
|
27
|
+
end
|
28
|
+
|
29
|
+
# @param s [String, Amount] an Amount object or a String like "1+XRP" or "1+USD+r..."
|
30
|
+
# @return [Amount]
|
31
|
+
def self.from_string s
|
32
|
+
return s if s.is_a?(Amount)
|
33
|
+
|
34
|
+
arr = s.split("+")
|
35
|
+
Amount.new({
|
36
|
+
"value" => arr[0],
|
37
|
+
"currency" => arr[1],
|
38
|
+
"issuer" => arr[2]
|
39
|
+
})
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class Balance
|
44
|
+
# @return [String]
|
45
|
+
def inspect
|
46
|
+
"#{value.to_s} #{currency}#{counterparty.to_s.size > 0 ? " (#{counterparty})" : ""}"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class Payment
|
51
|
+
# Gets Account object of this Payment's source account
|
52
|
+
def account
|
53
|
+
@account
|
54
|
+
end
|
55
|
+
|
56
|
+
# Sets source account and secret for this Payment
|
57
|
+
# @param val [Account]
|
58
|
+
def account= val
|
59
|
+
@account = val
|
60
|
+
self.source_account = val.address
|
61
|
+
end
|
62
|
+
|
63
|
+
# Submits a payment
|
64
|
+
# @return [String] Client resource ID
|
65
|
+
# @raise [ArgumentError] if secret is missing from the Account object
|
66
|
+
# @raise [RippleRestError] if RippleRest server returns an error
|
67
|
+
# @raise [ProtocolError] if protocol is wrong or network is down
|
68
|
+
def submit
|
69
|
+
@account.require_secret
|
70
|
+
|
71
|
+
hash = {}
|
72
|
+
hash["payment"] = self.to_hash
|
73
|
+
hash["secret"] = @account.secret
|
74
|
+
hash["client_resource_id"] = client_resource_id = RippleRest.next_uuid
|
75
|
+
|
76
|
+
RippleRest.post("v1/payments", hash)["client_resource_id"]
|
77
|
+
end
|
78
|
+
|
79
|
+
# @return [String]
|
80
|
+
attr_accessor :client_resource_id
|
81
|
+
end
|
82
|
+
end
|
data/lib/ripple-rest/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
1
|
module RippleRest
|
2
|
-
VERSION = "0.0
|
3
|
-
end
|
2
|
+
VERSION = "1.0.0"
|
3
|
+
end
|