spackle-ruby 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +4 -3
- data/Gemfile.lock +4 -4
- data/README.md +92 -1
- data/Rakefile +11 -4
- data/lib/spackle/customer.rb +20 -17
- data/lib/spackle/spackle_configuration.rb +18 -3
- data/lib/spackle/stores/base.rb +11 -0
- data/lib/spackle/{dynamodb.rb → stores/dynamodb.rb} +34 -17
- data/lib/spackle/stores/file.rb +28 -0
- data/lib/spackle/stores/memory.rb +21 -0
- data/lib/spackle/util.rb +10 -4
- data/lib/spackle/waiters.rb +37 -0
- data/lib/spackle.rb +9 -13
- data/spackle.gemspec +1 -1
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e8cf2b94103517a295249a12015de47bba73211680e6414e171df2bc9ab18ceb
|
4
|
+
data.tar.gz: 694bb847e82273d30bb5f7e325bdb795e56a5d4555bcefe01783dc597d22c3d6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e8e7b93be2dc27f5b823741bed3c29b05113811fb37ae6769e89cf323eb5f6b34c2f6aa1d402e15b064dae86e6864ac155cf6f3fb565fa3c873107020119384c
|
7
|
+
data.tar.gz: 0e4f7073e81f0bba5a572f9ff5e1ad9670cbf0f5e5ec9afc93d3de20c2c2d0cfe476208a232403aaa61046abed6f1e826c3b98fdd9e373bff61d0410067fcbb7
|
data/Gemfile
CHANGED
@@ -4,8 +4,9 @@ source "https://rubygems.org"
|
|
4
4
|
|
5
5
|
git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
|
6
6
|
|
7
|
-
gem
|
7
|
+
gem "aws-sdk", "~> 3"
|
8
8
|
gem "rake", "~> 13.0"
|
9
|
-
gem "test-unit", "~> 3.5"
|
10
|
-
|
11
9
|
gem "nokogiri", "~> 1.13"
|
10
|
+
gem "minitest", "~> 5.18"
|
11
|
+
|
12
|
+
gem "stripe", "~> 8.3"
|
data/Gemfile.lock
CHANGED
@@ -1356,23 +1356,23 @@ GEM
|
|
1356
1356
|
aws-eventstream (~> 1, >= 1.0.2)
|
1357
1357
|
jmespath (1.6.2)
|
1358
1358
|
mini_portile2 (2.8.1)
|
1359
|
+
minitest (5.18.0)
|
1359
1360
|
nokogiri (1.13.10)
|
1360
1361
|
mini_portile2 (~> 2.8.0)
|
1361
1362
|
racc (~> 1.4)
|
1362
|
-
power_assert (2.0.3)
|
1363
1363
|
racc (1.6.2)
|
1364
1364
|
rake (13.0.6)
|
1365
|
-
|
1366
|
-
power_assert
|
1365
|
+
stripe (8.3.0)
|
1367
1366
|
|
1368
1367
|
PLATFORMS
|
1369
1368
|
ruby
|
1370
1369
|
|
1371
1370
|
DEPENDENCIES
|
1372
1371
|
aws-sdk (~> 3)
|
1372
|
+
minitest (~> 5.18)
|
1373
1373
|
nokogiri (~> 1.13)
|
1374
1374
|
rake (~> 13.0)
|
1375
|
-
|
1375
|
+
stripe (~> 8.3)
|
1376
1376
|
|
1377
1377
|
BUNDLED WITH
|
1378
1378
|
1.17.2
|
data/README.md
CHANGED
@@ -63,10 +63,37 @@ customer.enabled("feature_key")
|
|
63
63
|
customer.limit("feature_key")
|
64
64
|
```
|
65
65
|
|
66
|
+
### Examine a customer's subscriptions
|
67
|
+
|
68
|
+
A customer's current subscriptions are available on the `subscriptions` property. These are valid `Stripe::Subscription` objects as defined in the [Stripe Ruby library](https://stripe.com/docs/api/subscriptions/object?lang=ruby).
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
customer.subscriptions
|
72
|
+
```
|
73
|
+
|
74
|
+
## Waiters
|
75
|
+
|
76
|
+
There is a brief delay between when an action takes place in Stripe and when it is reflected in Spackle. To account for this, Spackle provides a `Waiters` module that can be used to wait for a Stripe object to be updated and replicated.
|
77
|
+
|
78
|
+
1. Wait for a customer to be created
|
79
|
+
```ruby
|
80
|
+
Spackle::Waiters.wait_for_customer("cus_00000000")
|
81
|
+
```
|
82
|
+
2. Wait for a subscription to be created
|
83
|
+
```ruby
|
84
|
+
Spackle::Waiters.wait_for_subscription("cus_000000000", "sub_00000000")
|
85
|
+
```
|
86
|
+
3. Wait for a subscription to be updated
|
87
|
+
```ruby
|
88
|
+
Spackle::Waiters.wait_for_subscription("cus_000000000", "sub_00000000", status: "active")
|
89
|
+
```
|
90
|
+
|
91
|
+
These will block until Spackle is updated with the latest information from Stripe or until a timeout occurs.
|
92
|
+
|
66
93
|
## Logging
|
67
94
|
The Spackle Ruby library emits logs as it performs various internal tasks. You can control the verbosity of Spackle's logging a few different ways:
|
68
95
|
|
69
|
-
1. Set the environment variable SPACKLE_LOG to the value `debug`, `info`, or `error`
|
96
|
+
1. Set the environment variable SPACKLE_LOG to the value `debug`, `info`, `warn` or `error`
|
70
97
|
|
71
98
|
```sh
|
72
99
|
$ export SPACKLE_LOG=debug
|
@@ -77,3 +104,67 @@ The Spackle Ruby library emits logs as it performs various internal tasks. You c
|
|
77
104
|
```ruby
|
78
105
|
Spackle.log_level = 'debug'
|
79
106
|
```
|
107
|
+
|
108
|
+
## Usage in development environments
|
109
|
+
In production, Spackle requires a valid Stripe customer. However, that is not development environments where state needs to be controlled. As an alternative, you can use a file store to test your application with seed data.
|
110
|
+
|
111
|
+
```json
|
112
|
+
/app/spackle.json
|
113
|
+
|
114
|
+
{
|
115
|
+
"cus_000000000": {
|
116
|
+
"features": [
|
117
|
+
{
|
118
|
+
"key": "flag_feature",
|
119
|
+
"value_flag": true
|
120
|
+
},
|
121
|
+
{
|
122
|
+
"key": "limit_feature",
|
123
|
+
"value_limit": 100
|
124
|
+
}
|
125
|
+
],
|
126
|
+
"subscriptions": [
|
127
|
+
{
|
128
|
+
"id": "sub_000000000",
|
129
|
+
"status": "trialing",
|
130
|
+
"quantity": 1
|
131
|
+
}
|
132
|
+
]
|
133
|
+
}
|
134
|
+
}
|
135
|
+
```
|
136
|
+
|
137
|
+
Then configure the file store in your application:
|
138
|
+
|
139
|
+
```ruby
|
140
|
+
Spackle.store = Spackle::FileStore.new('/app/spackle.json')
|
141
|
+
```
|
142
|
+
|
143
|
+
## Usage in test environments
|
144
|
+
|
145
|
+
In production, Spackle requires a valid Stripe customer. However, that is not ideal in testing or some development environments. As an alternative, you can use an in-memory store to test your application with seed data.
|
146
|
+
|
147
|
+
```ruby
|
148
|
+
Spackle.store = Spackle::MemoryStore.new()
|
149
|
+
Spackle.store.set_customer_data("cus_000000000", {
|
150
|
+
"features": [
|
151
|
+
{
|
152
|
+
"key": "flag_feature",
|
153
|
+
"value_flag": True,
|
154
|
+
},
|
155
|
+
{
|
156
|
+
"key": "limit_feature",
|
157
|
+
"value_limit": 100,
|
158
|
+
},
|
159
|
+
],
|
160
|
+
"subscriptions": [
|
161
|
+
{
|
162
|
+
"id": "sub_000000000",
|
163
|
+
"status": "trialing",
|
164
|
+
"quantity": 1,
|
165
|
+
}
|
166
|
+
]
|
167
|
+
})
|
168
|
+
```
|
169
|
+
|
170
|
+
**Note:** The in-memory store is not thread-safe and state will reset on each application restart.
|
data/Rakefile
CHANGED
@@ -1,7 +1,14 @@
|
|
1
|
-
require "
|
1
|
+
require "minitest/test_task"
|
2
2
|
|
3
|
-
|
3
|
+
Minitest::TestTask.create # named test, sensible defaults
|
4
4
|
|
5
|
-
|
6
|
-
|
5
|
+
# or more explicitly:
|
6
|
+
|
7
|
+
Minitest::TestTask.create(:test) do |t|
|
8
|
+
t.libs << "test"
|
9
|
+
t.libs << "lib"
|
10
|
+
t.warning = false
|
11
|
+
t.test_globs = ["test/**/*_test.rb"]
|
7
12
|
end
|
13
|
+
|
14
|
+
task :default => :test
|
data/lib/spackle/customer.rb
CHANGED
@@ -1,28 +1,19 @@
|
|
1
|
+
require 'stripe'
|
2
|
+
|
1
3
|
module Spackle
|
2
4
|
class Customer
|
3
|
-
|
5
|
+
attr_accessor :id
|
6
|
+
attr_accessor :data
|
4
7
|
|
5
8
|
def self.retrieve(id)
|
6
9
|
Util.log_debug("Retrieving customer data for #{id}")
|
7
|
-
data = Spackle.
|
8
|
-
key_condition_expression: 'CustomerId = :customer_id',
|
9
|
-
filter_expression: 'Version = :version',
|
10
|
-
expression_attribute_values: {
|
11
|
-
':customer_id' => id,
|
12
|
-
':version' => Spackle.version
|
13
|
-
},
|
14
|
-
limit: 1
|
15
|
-
})
|
16
|
-
|
17
|
-
if not data.items.any?
|
18
|
-
raise SpackleError.new "Customer #{id} not found"
|
19
|
-
end
|
20
|
-
|
10
|
+
data = Spackle.store.get_customer_data(id)
|
21
11
|
Util.log_debug("Retrieved customer data for #{id}: #{data}")
|
22
|
-
Customer.new(
|
12
|
+
Customer.new(id, data)
|
23
13
|
end
|
24
14
|
|
25
|
-
def initialize(data)
|
15
|
+
def initialize(id, data)
|
16
|
+
@id = id
|
26
17
|
@data = data
|
27
18
|
end
|
28
19
|
|
@@ -30,6 +21,18 @@ module Spackle
|
|
30
21
|
return @data['features']
|
31
22
|
end
|
32
23
|
|
24
|
+
def subscriptions
|
25
|
+
subscriptions = []
|
26
|
+
|
27
|
+
@data['subscriptions'].each do |s|
|
28
|
+
subscription = Stripe::Subscription.new(s['id'])
|
29
|
+
subscription.update_attributes(s)
|
30
|
+
subscriptions.push(subscription)
|
31
|
+
end
|
32
|
+
|
33
|
+
subscriptions
|
34
|
+
end
|
35
|
+
|
33
36
|
def flag_features
|
34
37
|
features.select { |f| f['type'] == 0 }
|
35
38
|
end
|
@@ -4,20 +4,35 @@ module Spackle
|
|
4
4
|
class SpackleConfiguration
|
5
5
|
attr_accessor :api_key
|
6
6
|
attr_accessor :api_base
|
7
|
-
attr_accessor :log_level
|
8
7
|
attr_reader :logger
|
9
8
|
attr_reader :version
|
10
9
|
|
11
10
|
def initialize
|
12
|
-
@api_base = 'https://api.spackle.so'
|
13
|
-
@log_level = Logger::
|
11
|
+
@api_base = 'https://api.spackle.so/v1'
|
12
|
+
@log_level = Logger::WARN
|
14
13
|
@logger = Logger.new(STDOUT, level: @log_level)
|
15
14
|
@version = 1
|
15
|
+
@store = nil
|
16
|
+
end
|
17
|
+
|
18
|
+
def log_level()
|
19
|
+
@log_level
|
16
20
|
end
|
17
21
|
|
18
22
|
def log_level=(level)
|
19
23
|
@log_level = level
|
20
24
|
@logger.level = level
|
21
25
|
end
|
26
|
+
|
27
|
+
def store()
|
28
|
+
if @store == nil
|
29
|
+
@store = DynamoDBStore.new
|
30
|
+
end
|
31
|
+
@store
|
32
|
+
end
|
33
|
+
|
34
|
+
def store=(store)
|
35
|
+
@store = store
|
36
|
+
end
|
22
37
|
end
|
23
38
|
end
|
@@ -1,20 +1,41 @@
|
|
1
1
|
require 'aws-sdk'
|
2
2
|
require 'net/http'
|
3
3
|
require 'json'
|
4
|
+
require 'logger'
|
4
5
|
|
5
6
|
module Spackle
|
6
|
-
class
|
7
|
+
class DynamoDBStore < BaseStore
|
7
8
|
@client = nil
|
8
|
-
@
|
9
|
-
@table_name = nil
|
9
|
+
@store_config = {}
|
10
10
|
|
11
|
-
def initialize
|
12
|
-
@
|
11
|
+
def initialize(client = nil, store_config = nil)
|
12
|
+
@store_config = store_config || {}
|
13
|
+
@client = client || bootstrap_client
|
13
14
|
end
|
14
15
|
|
16
|
+
def get_customer_data(id)
|
17
|
+
data = query({
|
18
|
+
key_condition_expression: 'CustomerId = :customer_id',
|
19
|
+
filter_expression: 'Version = :version',
|
20
|
+
expression_attribute_values: {
|
21
|
+
':customer_id' => id,
|
22
|
+
':version' => Spackle.version
|
23
|
+
},
|
24
|
+
limit: 1
|
25
|
+
})
|
26
|
+
|
27
|
+
if not data.items.any?
|
28
|
+
raise SpackleError.new "Customer #{id} not found"
|
29
|
+
end
|
30
|
+
|
31
|
+
JSON.parse(data.items[0]['State'])
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
15
36
|
def get_item(key)
|
16
37
|
key = key.merge({
|
17
|
-
'AccountId' => @identity_id,
|
38
|
+
'AccountId' => @store_config['identity_id'],
|
18
39
|
})
|
19
40
|
|
20
41
|
response = @client.get_item({
|
@@ -26,19 +47,17 @@ module Spackle
|
|
26
47
|
end
|
27
48
|
|
28
49
|
def query(query)
|
29
|
-
query[:table_name] = @table_name
|
50
|
+
query[:table_name] = @store_config['table_name']
|
30
51
|
query[:key_condition_expression] = 'AccountId = :account_id AND ' + query[:key_condition_expression]
|
31
52
|
query[:expression_attribute_values] = query[:expression_attribute_values].merge({
|
32
|
-
':account_id' => @identity_id
|
53
|
+
':account_id' => @store_config['identity_id']
|
33
54
|
})
|
34
|
-
|
55
|
+
@client.query(query)
|
35
56
|
end
|
36
57
|
|
37
|
-
private
|
38
|
-
|
39
58
|
def bootstrap_client
|
40
59
|
Util.log_debug('Bootstrapping DynamoDB client...')
|
41
|
-
uri = URI(Spackle.api_base + '/
|
60
|
+
uri = URI(Spackle.api_base + '/sessions')
|
42
61
|
https = Net::HTTP.new(uri.host, uri.port)
|
43
62
|
https.use_ssl = true
|
44
63
|
|
@@ -49,12 +68,10 @@ module Spackle
|
|
49
68
|
data = JSON.parse(response.body)
|
50
69
|
Util.log_debug("Created session: #{data}")
|
51
70
|
|
52
|
-
@
|
53
|
-
@table_name = data['table_name']
|
54
|
-
|
71
|
+
@store_config = data['adapter']
|
55
72
|
credentials = SpackleCredentials.new(
|
56
|
-
|
57
|
-
|
73
|
+
@store_config['role_arn'],
|
74
|
+
@store_config['token']
|
58
75
|
)
|
59
76
|
|
60
77
|
Aws::DynamoDB::Client.new(
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Spackle
|
2
|
+
class FileStore < BaseStore
|
3
|
+
def initialize(path)
|
4
|
+
@path = path
|
5
|
+
end
|
6
|
+
|
7
|
+
def get_customer_data(id)
|
8
|
+
content = File.read(@path)
|
9
|
+
data = JSON.parse(content)
|
10
|
+
data[id]
|
11
|
+
end
|
12
|
+
|
13
|
+
def set_customer_data(id, customer_data)
|
14
|
+
data = {}
|
15
|
+
if File.exist?(@path)
|
16
|
+
content = File.read(@path)
|
17
|
+
data = JSON.parse(content)
|
18
|
+
end
|
19
|
+
|
20
|
+
if !data.has_key?(id)
|
21
|
+
raise SpackleError.new "Customer #{id} not found"
|
22
|
+
end
|
23
|
+
|
24
|
+
data[id] = customer_data
|
25
|
+
File.write(@path, JSON.pretty_generate(data))
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Spackle
|
2
|
+
class MemoryStore < BaseStore
|
3
|
+
@data = {}
|
4
|
+
|
5
|
+
def initialize()
|
6
|
+
@data = {}
|
7
|
+
end
|
8
|
+
|
9
|
+
def get_customer_data(id)
|
10
|
+
if !@data.has_key?(id)
|
11
|
+
raise SpackleError.new "Customer #{id} not found"
|
12
|
+
end
|
13
|
+
|
14
|
+
@data[id]
|
15
|
+
end
|
16
|
+
|
17
|
+
def set_customer_data(id, customer_data)
|
18
|
+
@data[id] = customer_data
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/spackle/util.rb
CHANGED
@@ -1,15 +1,21 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
1
3
|
module Spackle
|
2
4
|
module Util
|
3
|
-
def self.
|
4
|
-
Spackle.logger.
|
5
|
+
def self.log_debug(message)
|
6
|
+
Spackle.logger.debug(message)
|
5
7
|
end
|
6
8
|
|
7
9
|
def self.log_info(message)
|
8
10
|
Spackle.logger.info(message)
|
9
11
|
end
|
10
12
|
|
11
|
-
def self.
|
12
|
-
Spackle.logger.
|
13
|
+
def self.log_warn(message)
|
14
|
+
Spackle.logger.warn(message)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.log_error(message)
|
18
|
+
Spackle.logger.error(message)
|
13
19
|
end
|
14
20
|
end
|
15
21
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module Spackle
|
4
|
+
module Waiters
|
5
|
+
def self.wait_for_customer(customer_id, timeout = 15)
|
6
|
+
start = Time.now.to_i
|
7
|
+
while Time.now.to_i - start < timeout do
|
8
|
+
begin
|
9
|
+
return Spackle::Customer.retrieve(customer_id)
|
10
|
+
rescue SpackleError
|
11
|
+
sleep 1
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
raise SpackleError.new "Timeout waiting for customer #{customer_id}"
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.wait_for_subscription(customer_id, subscription_id, timeout=15, **filters)
|
19
|
+
start = Time.now.to_i
|
20
|
+
while Time.now.to_i - start < timeout do
|
21
|
+
begin
|
22
|
+
customer = Spackle::Customer.retrieve(customer_id)
|
23
|
+
customer.subscriptions.each do |s|
|
24
|
+
if s.id == subscription_id and filters.all? { |k, v| s.send(k) == v }
|
25
|
+
return s
|
26
|
+
end
|
27
|
+
end
|
28
|
+
sleep 1
|
29
|
+
rescue SpackleError
|
30
|
+
sleep 1
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
raise SpackleError.new "Timeout waiting for subscription #{subscription_id}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/spackle.rb
CHANGED
@@ -2,17 +2,23 @@ require 'logger'
|
|
2
2
|
require 'forwardable'
|
3
3
|
|
4
4
|
require 'spackle/customer'
|
5
|
-
require 'spackle/dynamodb'
|
6
5
|
require 'spackle/spackle_configuration'
|
7
6
|
require 'spackle/util'
|
7
|
+
require 'spackle/waiters'
|
8
|
+
|
9
|
+
require 'spackle/stores/base'
|
10
|
+
require 'spackle/stores/dynamodb'
|
11
|
+
require 'spackle/stores/file'
|
12
|
+
require 'spackle/stores/memory'
|
8
13
|
|
9
14
|
module Spackle
|
10
15
|
@config = Spackle::SpackleConfiguration.new
|
11
16
|
@client = nil
|
12
17
|
|
13
18
|
LEVEL_DEBUG = Logger::DEBUG
|
14
|
-
LEVEL_ERROR = Logger::ERROR
|
15
19
|
LEVEL_INFO = Logger::INFO
|
20
|
+
LEVEL_WARN = Logger::WARN
|
21
|
+
LEVEL_ERROR = Logger::ERROR
|
16
22
|
|
17
23
|
class << self
|
18
24
|
extend Forwardable
|
@@ -22,21 +28,11 @@ module Spackle
|
|
22
28
|
def_delegators :@config, :api_key, :api_key=
|
23
29
|
def_delegators :@config, :api_base, :api_base=
|
24
30
|
def_delegators :@config, :log_level, :log_level=
|
31
|
+
def_delegators :@config, :store, :store=
|
25
32
|
def_delegators :@config, :logger, :logger=
|
26
33
|
def_delegators :@config, :version, :version=
|
27
34
|
end
|
28
35
|
|
29
|
-
def self.client
|
30
|
-
unless Spackle.api_key.nil?
|
31
|
-
@client ||= Spackle::DynamoDB.new()
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def self.bootstrap
|
36
|
-
self.client
|
37
|
-
nil
|
38
|
-
end
|
39
|
-
|
40
36
|
class SpackleError < StandardError
|
41
37
|
end
|
42
38
|
end
|
data/spackle.gemspec
CHANGED
@@ -2,7 +2,7 @@ $LOAD_PATH.unshift(::File.join(::File.dirname(__FILE__), "lib"))
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = "spackle-ruby"
|
5
|
-
s.version = "0.0.
|
5
|
+
s.version = "0.0.3"
|
6
6
|
s.summary = "Spackle Ruby gem"
|
7
7
|
s.description = "Spackle is the easiest way to integrate your Ruby app with Stripe Billing. " \
|
8
8
|
"See https://www.spackle.so for details."
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: spackle-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Spackle
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-03-06 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Spackle is the easiest way to integrate your Ruby app with Stripe Billing.
|
14
14
|
See https://www.spackle.so for details.
|
@@ -26,9 +26,13 @@ files:
|
|
26
26
|
- bin/spackle-console
|
27
27
|
- lib/spackle.rb
|
28
28
|
- lib/spackle/customer.rb
|
29
|
-
- lib/spackle/dynamodb.rb
|
30
29
|
- lib/spackle/spackle_configuration.rb
|
30
|
+
- lib/spackle/stores/base.rb
|
31
|
+
- lib/spackle/stores/dynamodb.rb
|
32
|
+
- lib/spackle/stores/file.rb
|
33
|
+
- lib/spackle/stores/memory.rb
|
31
34
|
- lib/spackle/util.rb
|
35
|
+
- lib/spackle/waiters.rb
|
32
36
|
- spackle.gemspec
|
33
37
|
homepage: https://docs.spackle.so/ruby
|
34
38
|
licenses:
|