ribbon-flow 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
- 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
|