ribbon-flow 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +2 -0
- data/lib/flow.rb +31 -0
- data/lib/flow/config.rb +50 -0
- data/lib/flow/connection.rb +187 -0
- data/lib/flow/errors.rb +12 -0
- data/lib/flow/models.rb +4 -0
- data/lib/flow/models/card.rb +39 -0
- data/lib/flow/models/model.rb +40 -0
- data/lib/flow/models/pool.rb +15 -0
- data/lib/flow/models/transaction.rb +35 -0
- data/lib/flow/response.rb +58 -0
- data/lib/flow/version.rb +4 -0
- metadata +92 -0
- metadata.gz.sig +0 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 07298ede14e9864a03a7e433a43b9e387c540ee1
|
4
|
+
data.tar.gz: 757937ae1b637a82724df958ab7b6a0a641889dc
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a3950406361a0e8ac3f0e5ef27e3103466115a54ee6ab4dfd4dac89b34407a670ea5f9b7423ec03e98e8fd15064a1602bbcab940c859703afd7d1269b1fc637c
|
7
|
+
data.tar.gz: c1555ebc95da219a0f1d2b65b155039a49f8741eccd53d7086f712bb4fff39ecea6bf3248b051de1cff6a4fc6736c703d7b82ef2341f8469c7a7f07a6caa7feb
|
checksums.yaml.gz.sig
ADDED
Binary file
|
data.tar.gz.sig
ADDED
data/lib/flow.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
|
2
|
+
require 'flow/version'
|
3
|
+
require 'flow/errors'
|
4
|
+
require 'flow/config'
|
5
|
+
require 'flow/connection'
|
6
|
+
require 'flow/models'
|
7
|
+
require 'flow/response'
|
8
|
+
|
9
|
+
module Flow
|
10
|
+
|
11
|
+
def self.config
|
12
|
+
Flow::Config.instance
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.connection
|
16
|
+
@__connection_cache ||= Flow::Connection.new(config.api_key, config.url)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.pool(pool_token)
|
20
|
+
Flow::Models::Pool.load(connection, pool_token)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.card(card_token)
|
24
|
+
Flow::Models::Card.load(connection, card_token)
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.transaction(transaction_token)
|
28
|
+
Flow::Models::Transaction.load(connection, transaction_token)
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
data/lib/flow/config.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
|
2
|
+
require 'singleton'
|
3
|
+
|
4
|
+
module Flow
|
5
|
+
class Config
|
6
|
+
include Singleton
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@config = self.defaults
|
10
|
+
end
|
11
|
+
|
12
|
+
def defaults
|
13
|
+
{
|
14
|
+
url: 'https://flow.ribbon.co'.freeze,
|
15
|
+
api_key: nil
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
def method_missing(meth, *args, &block)
|
20
|
+
meth_str = meth.to_s
|
21
|
+
|
22
|
+
if /^(\w+)=$/.match(meth_str)
|
23
|
+
_set($1, args.first)
|
24
|
+
else
|
25
|
+
_get(meth)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def url=(url)
|
30
|
+
url = url[0..-2] if url[-1] == '/' # No trailing forward-slash
|
31
|
+
_set(:url, url)
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_s
|
35
|
+
return @config.to_s
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def _set(key, value)
|
41
|
+
# Changing the config value should require calling the
|
42
|
+
# _set method again.
|
43
|
+
return @config[key.to_sym] = value.dup.freeze
|
44
|
+
end
|
45
|
+
|
46
|
+
def _get(key)
|
47
|
+
return @config[key.to_sym]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,187 @@
|
|
1
|
+
|
2
|
+
require 'rest-client'
|
3
|
+
require 'openssl'
|
4
|
+
require 'securerandom'
|
5
|
+
|
6
|
+
module Flow
|
7
|
+
|
8
|
+
class Connection
|
9
|
+
attr_reader :url
|
10
|
+
attr_reader :access_key
|
11
|
+
attr_reader :version
|
12
|
+
|
13
|
+
attr_reader :scheme
|
14
|
+
attr_reader :host
|
15
|
+
attr_reader :port
|
16
|
+
|
17
|
+
def initialize(api_key, url='https://flow.ribbon.co', version=1)
|
18
|
+
@version = version
|
19
|
+
|
20
|
+
@url = (url[-1] == '/' ? url[0..-2] : url).dup.freeze # Remove trailing slash.
|
21
|
+
|
22
|
+
if /^([A-Za-z0-9]{20})\.([A-Za-z0-9]{43})$/.match(api_key)
|
23
|
+
@access_key = $1.dup.freeze
|
24
|
+
@signing_key = $2.dup.freeze
|
25
|
+
else
|
26
|
+
raise Flow::Errors::ConnectionError, "Invalid API Key format."
|
27
|
+
end
|
28
|
+
|
29
|
+
URI.parse(@url).tap do |uri|
|
30
|
+
@scheme = uri.scheme
|
31
|
+
@host = uri.host
|
32
|
+
@port = uri.port
|
33
|
+
end
|
34
|
+
|
35
|
+
# RestClient::Resource.new(
|
36
|
+
# 'https://example.com',
|
37
|
+
# :ssl_client_cert => OpenSSL::X509::Certificate.new(File.read("cert.pem")),
|
38
|
+
# :ssl_client_key => OpenSSL::PKey::RSA.new(File.read("key.pem"), "passphrase, if any"),
|
39
|
+
# :ssl_ca_file => "ca_certificate.pem",
|
40
|
+
# :verify_ssl => OpenSSL::SSL::VERIFY_PEER
|
41
|
+
# )
|
42
|
+
|
43
|
+
@api = RestClient::Resource.new(
|
44
|
+
@url,
|
45
|
+
verify_ssl: true,
|
46
|
+
headers: {
|
47
|
+
"Content-Type" => "application/json"
|
48
|
+
}
|
49
|
+
) {|response, request, result| response}
|
50
|
+
end
|
51
|
+
|
52
|
+
def get(path, params={})
|
53
|
+
path = _build_path(path)
|
54
|
+
auth_header = _authorization_header(:get, path, params)
|
55
|
+
response = @api[path].get(authorization: auth_header, params: params)
|
56
|
+
Flow::Response.new(self, response)
|
57
|
+
end
|
58
|
+
|
59
|
+
def post(path, params={}, &block)
|
60
|
+
if block_given?
|
61
|
+
body_hash = block.call
|
62
|
+
if body_hash && !body_hash.empty?
|
63
|
+
body = body_hash.to_json
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
path = _build_path(path)
|
68
|
+
auth_header = _authorization_header(:post, path, params, body)
|
69
|
+
response = @api[path].post(body, authorization: auth_header, params: params)
|
70
|
+
|
71
|
+
Flow::Response.new(self, response)
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def _signing_key
|
77
|
+
@signing_key
|
78
|
+
end
|
79
|
+
|
80
|
+
def _build_path(path)
|
81
|
+
path = '/' + path unless path[0] == '/'
|
82
|
+
|
83
|
+
if path.start_with?('/api/')
|
84
|
+
return path
|
85
|
+
else
|
86
|
+
return "/api/v#{version}#{path}"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def _gen_nonce
|
91
|
+
begin
|
92
|
+
nonce = SecureRandom.hex(8)
|
93
|
+
end until _valid_nonce?(nonce)
|
94
|
+
|
95
|
+
return nonce.freeze
|
96
|
+
end
|
97
|
+
|
98
|
+
def _valid_nonce?(nonce)
|
99
|
+
record = @__nonce_record ||= {}
|
100
|
+
nonce = nonce.to_s
|
101
|
+
|
102
|
+
# Try to limit the record store to about 2MB
|
103
|
+
# of data, assuming 16 single-byte characters
|
104
|
+
# and 4 byte timestamps.
|
105
|
+
# Supports 3,333 requests per second before exceeding
|
106
|
+
# the limit.
|
107
|
+
if record.length > 100_000
|
108
|
+
threshold_ts = (Time.now.to_i - 30).freeze
|
109
|
+
record.delete_if {|k,v| v < threshold_ts}
|
110
|
+
end
|
111
|
+
|
112
|
+
if record[nonce] && record[nonce] >= (Time.now.to_i - 30)
|
113
|
+
return false
|
114
|
+
else
|
115
|
+
record[nonce] = Time.now.to_i.freeze
|
116
|
+
return true
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def _authorization_header(verb, path, query_hash, body=nil)
|
121
|
+
timestamp = Time.now.to_i.freeze
|
122
|
+
nonce = _gen_nonce
|
123
|
+
|
124
|
+
# Calculate request hash
|
125
|
+
path = _uri_encode(path[0] == '/' ? path : ('/' + path), false)
|
126
|
+
query_string = _hash_to_query_string(query_hash)
|
127
|
+
body_hash = _sha256(body.to_s)
|
128
|
+
request_hash = _sha256(
|
129
|
+
verb.to_s.upcase + "\n" +
|
130
|
+
host + "\n" +
|
131
|
+
path + "\n" +
|
132
|
+
query_string + "\n" +
|
133
|
+
body_hash
|
134
|
+
)
|
135
|
+
|
136
|
+
# Construct the message
|
137
|
+
message = timestamp.to_s + nonce + request_hash
|
138
|
+
|
139
|
+
signature = _hmac_sha256_hex(_signing_key, message)
|
140
|
+
|
141
|
+
return "RBN1-HMAC-SHA256 #{access_key};#{timestamp};#{nonce};#{signature}"
|
142
|
+
end
|
143
|
+
|
144
|
+
######################################################################
|
145
|
+
#
|
146
|
+
# Encoding and signing helper methods
|
147
|
+
#
|
148
|
+
######################################################################
|
149
|
+
|
150
|
+
def _hmac_sha256_hex(key, message)
|
151
|
+
digest = OpenSSL::Digest::SHA256.new
|
152
|
+
OpenSSL::HMAC.hexdigest(digest, key, message)
|
153
|
+
end
|
154
|
+
|
155
|
+
def _sha256(str)
|
156
|
+
::OpenSSL::Digest::SHA256.hexdigest(str)
|
157
|
+
end
|
158
|
+
|
159
|
+
def _uri_encode(string, encode_fslash=true)
|
160
|
+
retval = ""
|
161
|
+
|
162
|
+
string.chars.each do |ch|
|
163
|
+
if (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') ||
|
164
|
+
(ch >= '0' && ch <= '9') || ch == '-' || ch == '_' || ch == '.' || ch == '~'
|
165
|
+
retval << ch
|
166
|
+
elsif ch == '/'
|
167
|
+
retval << (encode_fslash ? '%2F' : ch)
|
168
|
+
else
|
169
|
+
retval << '%' << ch.bytes.first.to_s(16).upcase
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
return retval
|
174
|
+
end
|
175
|
+
|
176
|
+
def _hash_to_query_string(data)
|
177
|
+
pairs = []
|
178
|
+
|
179
|
+
data.sort{|a,b| a[0] <=> b[0]}.each do |key, value|
|
180
|
+
pairs << (_uri_encode(key.to_s) << '=' << _uri_encode(value.to_s))
|
181
|
+
end
|
182
|
+
|
183
|
+
pairs.join('&')
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
end
|
data/lib/flow/errors.rb
ADDED
data/lib/flow/models.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
module Flow
|
2
|
+
module Models
|
3
|
+
class Card < Model
|
4
|
+
|
5
|
+
def self.load(connection, card_token)
|
6
|
+
r = connection.get("cards/#{card_token}")
|
7
|
+
|
8
|
+
raise Flow::Errors::LoadError.new(r.message) unless r.success?
|
9
|
+
|
10
|
+
return r.card
|
11
|
+
end
|
12
|
+
|
13
|
+
def ready?
|
14
|
+
self.status == 'ready'
|
15
|
+
end
|
16
|
+
|
17
|
+
def supported?
|
18
|
+
self.ready?
|
19
|
+
end
|
20
|
+
|
21
|
+
def unsupported?
|
22
|
+
self.status == 'unsupported'
|
23
|
+
end
|
24
|
+
|
25
|
+
def credit!(amount, options={})
|
26
|
+
r = connection.post("cards/#{self.token}/credits") do
|
27
|
+
{
|
28
|
+
amount: amount
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
raise Flow::Errors::ResponseError.new(r.message) unless r.success?
|
33
|
+
|
34
|
+
r.transaction
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
|
2
|
+
module Flow
|
3
|
+
module Models
|
4
|
+
|
5
|
+
class Model
|
6
|
+
attr_reader :model_hash
|
7
|
+
attr_reader :connection
|
8
|
+
|
9
|
+
def initialize(connection, model_hash)
|
10
|
+
@connection = connection
|
11
|
+
@model_hash = model_hash
|
12
|
+
end
|
13
|
+
|
14
|
+
def method_missing(meth, *args, &block)
|
15
|
+
if model_hash.key?(meth.to_s)
|
16
|
+
return Model.load_model(meth, connection, model_hash[meth.to_s])
|
17
|
+
end
|
18
|
+
|
19
|
+
super
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.load_model(name, connection, model_hash)
|
23
|
+
name = name.to_sym
|
24
|
+
|
25
|
+
case name
|
26
|
+
when :pool
|
27
|
+
return Flow::Models::Pool.new(connection, model_hash)
|
28
|
+
when :card
|
29
|
+
return Flow::Models::Card.new(connection, model_hash)
|
30
|
+
when :transaction
|
31
|
+
return Flow::Models::Transaction.new(connection, model_hash)
|
32
|
+
else
|
33
|
+
return model_hash
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Flow
|
2
|
+
module Models
|
3
|
+
class Pool < Model
|
4
|
+
|
5
|
+
def self.load(connection, pool_token)
|
6
|
+
r = connection.get("pools/#{pool_token}")
|
7
|
+
|
8
|
+
raise Flow::Errors::LoadError.new(r.message) unless r.success?
|
9
|
+
|
10
|
+
return r.pool
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Flow
|
2
|
+
module Models
|
3
|
+
class Transaction < Model
|
4
|
+
|
5
|
+
def self.load(connection, tx_token)
|
6
|
+
r = connection.get("transactions/#{tx_token}")
|
7
|
+
|
8
|
+
raise Flow::Errors::LoadError.new(r.message) unless r.success?
|
9
|
+
|
10
|
+
return r.transaction
|
11
|
+
end
|
12
|
+
|
13
|
+
def pending?
|
14
|
+
self.status == 'pending'
|
15
|
+
end
|
16
|
+
|
17
|
+
def scheduled?
|
18
|
+
self.status == 'scheduled'
|
19
|
+
end
|
20
|
+
|
21
|
+
def cleared?
|
22
|
+
self.status == 'cleared'
|
23
|
+
end
|
24
|
+
|
25
|
+
def failed?
|
26
|
+
self.status == 'failed'
|
27
|
+
end
|
28
|
+
|
29
|
+
def error?
|
30
|
+
self.status == 'error'
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Flow
|
5
|
+
|
6
|
+
class Response
|
7
|
+
attr_reader :body
|
8
|
+
attr_reader :connection
|
9
|
+
|
10
|
+
def initialize(connection, rest_client_response)
|
11
|
+
@connection = connection
|
12
|
+
@response = rest_client_response
|
13
|
+
|
14
|
+
begin
|
15
|
+
body = @response.body
|
16
|
+
@body = JSON.parse(body) unless body.empty?
|
17
|
+
@body ||= {}
|
18
|
+
_deep_freeze(@body)
|
19
|
+
rescue JSON::ParserError => e
|
20
|
+
raise Flow::Errors::ResponseError, "Invalid JSON returned: #{e.message}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def http_success?
|
25
|
+
@response.code >= 200 && @response.code < 300
|
26
|
+
end
|
27
|
+
|
28
|
+
def success?
|
29
|
+
body['status'] == 'success'
|
30
|
+
end
|
31
|
+
|
32
|
+
def error?
|
33
|
+
body['status'] == 'error'
|
34
|
+
end
|
35
|
+
|
36
|
+
def method_missing(meth, *args, &block)
|
37
|
+
if body.key?(meth.to_s)
|
38
|
+
return Flow::Models::Model.load_model(meth, connection, body[meth.to_s])
|
39
|
+
end
|
40
|
+
|
41
|
+
super
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def _deep_freeze(obj)
|
48
|
+
if obj.respond_to?(:each)
|
49
|
+
obj.each do |elem|
|
50
|
+
_deep_freeze(elem)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
obj.freeze
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
data/lib/flow/version.rb
ADDED
metadata
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ribbon-flow
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Robert Honer
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain:
|
11
|
+
- |
|
12
|
+
-----BEGIN CERTIFICATE-----
|
13
|
+
MIIDfDCCAmSgAwIBAgIBATANBgkqhkiG9w0BAQUFADBCMRQwEgYDVQQDDAtlbmdp
|
14
|
+
bmVlcmluZzEWMBQGCgmSJomT8ixkARkWBnJpYmJvbjESMBAGCgmSJomT8ixkARkW
|
15
|
+
AmNvMB4XDTE0MDYwOTE2MjY0NFoXDTE1MDYwOTE2MjY0NFowQjEUMBIGA1UEAwwL
|
16
|
+
ZW5naW5lZXJpbmcxFjAUBgoJkiaJk/IsZAEZFgZyaWJib24xEjAQBgoJkiaJk/Is
|
17
|
+
ZAEZFgJjbzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOqAiBev9e7J
|
18
|
+
D3X3A+SXpIbnG71jweN7Q7+QMhscA6+s8X91aG2cvNPpk7OSMaoU7Q81v+I9VMYu
|
19
|
+
n0JhOh/O86nYv0Ig/FKcDRzZIGgY0bjJV/5mokQ45xgseUTjJ3ocqOXncRPNx4xm
|
20
|
+
Am3eyb43aDqUFnowXcH84ZXMTFew+dekov6644pN14u8aXGWPmZxOQhTOi5+ESAe
|
21
|
+
+RON7v2xRq1OFcupEHeKHMhAMCbOy19NqTq/1yA8Pd940OfVRisCnXoQqOCP9H86
|
22
|
+
gakm8XcLrUdJupV6OxCMd5NZ3itRTFEWOMy6ZRkj2IVdFQ4ZPQjIHR2f253DF7BW
|
23
|
+
dUStbmkKuyECAwEAAaN9MHswCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0O
|
24
|
+
BBYEFPjdKqT6jDUZj755LAOopw41RGO/MCAGA1UdEQQZMBeBFWVuZ2luZWVyaW5n
|
25
|
+
QHJpYmJvbi5jbzAgBgNVHRIEGTAXgRVlbmdpbmVlcmluZ0ByaWJib24uY28wDQYJ
|
26
|
+
KoZIhvcNAQEFBQADggEBACmYnS5b0mBhurs/U+oTUiY6nL4/xTx1poZxUNJ9Emag
|
27
|
+
y/vnnbMxiZXXz4Y6vWdFiOg02N6BiBrvyjzkleOyw41lcNDj9pH6HyLFAKR4lU5V
|
28
|
+
HKaahXt474aWouDfvENcaINNtgLrPAqisgRckuyzfM/bU1a8Aj2OSLsSEh69giTi
|
29
|
+
0WxI115SHQG+8CyzpRQIBteomCInG1ymSyvEUxpytSW39KBVKViB84Ss5vcMvtv7
|
30
|
+
grVI0T63q4/t9yjlCPkbK1VTObseWNPA2/7twecdkCVN3VaWEml4xf2KiOwnKDfk
|
31
|
+
aZOXvndbbL+k3uaxs/Fpfsi0AD02HiceGjSOZFd9Wyk=
|
32
|
+
-----END CERTIFICATE-----
|
33
|
+
date: 2014-06-09 00:00:00.000000000 Z
|
34
|
+
dependencies:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: rest-client
|
37
|
+
requirement: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - '>='
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
type: :runtime
|
43
|
+
prerelease: false
|
44
|
+
version_requirements: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - '>='
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
description: With this gem, you can connect and perform any operation on Ribbon Flow.
|
50
|
+
It supports loading pool, card and transaction tokens. You can also easily credit
|
51
|
+
a card.
|
52
|
+
email: engineering@ribbon.co
|
53
|
+
executables: []
|
54
|
+
extensions: []
|
55
|
+
extra_rdoc_files: []
|
56
|
+
files:
|
57
|
+
- lib/flow/config.rb
|
58
|
+
- lib/flow/connection.rb
|
59
|
+
- lib/flow/errors.rb
|
60
|
+
- lib/flow/models/card.rb
|
61
|
+
- lib/flow/models/model.rb
|
62
|
+
- lib/flow/models/pool.rb
|
63
|
+
- lib/flow/models/transaction.rb
|
64
|
+
- lib/flow/models.rb
|
65
|
+
- lib/flow/response.rb
|
66
|
+
- lib/flow/version.rb
|
67
|
+
- lib/flow.rb
|
68
|
+
homepage: https://flow.ribbon.co
|
69
|
+
licenses:
|
70
|
+
- BSD
|
71
|
+
metadata: {}
|
72
|
+
post_install_message:
|
73
|
+
rdoc_options: []
|
74
|
+
require_paths:
|
75
|
+
- lib
|
76
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - '>='
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '0'
|
81
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
82
|
+
requirements:
|
83
|
+
- - '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
requirements: []
|
87
|
+
rubyforge_project:
|
88
|
+
rubygems_version: 2.0.3
|
89
|
+
signing_key:
|
90
|
+
specification_version: 4
|
91
|
+
summary: Ruby SDK for the Ribbon Flow API.
|
92
|
+
test_files: []
|
metadata.gz.sig
ADDED
Binary file
|