rollout-dynamodb 0.0.3 → 0.0.4
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 +4 -4
- data/.env.test +6 -0
- data/README.md +19 -0
- data/Rakefile +0 -86
- data/lib/rollout-dynamodb.rb +4 -1
- data/lib/rollout/dynamodb/storage.rb +47 -1
- data/rollout-dynamodb.gemspec +1 -0
- data/spec/dynamodb_integration_spec.rb +58 -0
- data/spec/rollout_dynamodb_integration_spec.rb +322 -0
- data/spec/spec_helper.rb +23 -2
- data/spec/{dynamodb_spec.rb → storage_spec.rb} +16 -0
- metadata +23 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dbe73c5e20cb8e44d3a6b3c039c98c07c7b35090
|
4
|
+
data.tar.gz: bea9f702c2b8ea0d33a87c39d5ca6107ff541122
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fd41ae08bb42d32fb069badc5ec43f692aef3603cc45e63f01dfd6aac0c863350a2eb37fe22e5a6c5efc8cb084907ccd72aca07db97fa2cdc9dd42b4d3b2fdfd
|
7
|
+
data.tar.gz: 9334c5fc8f01ac16411228cd89ae9bd4bee1bf04e482e5a0981d66d3e7c7578815142c399c9254bfe74a77048f30939ee5e2d1c92a349bb008921172db072a4a
|
data/.env.test
ADDED
data/README.md
CHANGED
@@ -40,6 +40,25 @@ $rollout.active?(:chat)
|
|
40
40
|
# -> true
|
41
41
|
```
|
42
42
|
|
43
|
+
### Testing
|
44
|
+
|
45
|
+
You might want to test this code. You can get started like so:
|
46
|
+
|
47
|
+
```bash
|
48
|
+
foreman run rake
|
49
|
+
```
|
50
|
+
|
51
|
+
Note that there are integration tests bundled in with the specs. If you want to run them, follow the steps below, otherwise they will be skipped.
|
52
|
+
|
53
|
+
1. Go to the AWS console for DynamoDB
|
54
|
+
2. Create a new table, indexed by hash key named `id` of type `String`
|
55
|
+
3. Set up the environment
|
56
|
+
|
57
|
+
```bash
|
58
|
+
mv .env.test .env
|
59
|
+
$EDITOR .env # set config vars
|
60
|
+
```
|
61
|
+
|
43
62
|
## Contributing
|
44
63
|
|
45
64
|
1. Fork it ( https://github.com/chooper/rollout-dynamodb/fork )
|
data/Rakefile
CHANGED
@@ -4,89 +4,3 @@ require 'rspec/core/rake_task'
|
|
4
4
|
RSpec::Core::RakeTask.new(:spec)
|
5
5
|
task :default => :spec
|
6
6
|
|
7
|
-
def name
|
8
|
-
@name ||= Dir['*.gemspec'].first.split('.').first
|
9
|
-
end
|
10
|
-
|
11
|
-
def version
|
12
|
-
line = File.read("lib/#{name}.rb")[/^\s*VERSION\s*=\s*.*/]
|
13
|
-
line.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1]
|
14
|
-
end
|
15
|
-
|
16
|
-
def date
|
17
|
-
Date.today.to_s
|
18
|
-
end
|
19
|
-
|
20
|
-
def rubyforge_project
|
21
|
-
name
|
22
|
-
end
|
23
|
-
|
24
|
-
def gemspec_file
|
25
|
-
"#{name}.gemspec"
|
26
|
-
end
|
27
|
-
|
28
|
-
def gem_file
|
29
|
-
"#{name}-#{version}.gem"
|
30
|
-
end
|
31
|
-
|
32
|
-
def replace_header(head, header_name)
|
33
|
-
head.sub!(/(\.#{header_name}\s*= ').*'/) { "#{$1}#{send(header_name)}'"}
|
34
|
-
end
|
35
|
-
|
36
|
-
desc "Open an irb session preloaded with this library"
|
37
|
-
task :console do
|
38
|
-
sh "irb -rubygems -r ./lib/#{name}.rb"
|
39
|
-
end
|
40
|
-
|
41
|
-
desc "Create tag v#{version} and build and push #{gem_file} to Rubygems"
|
42
|
-
task :release => :build do
|
43
|
-
unless `git branch` =~ /^\* master$/
|
44
|
-
puts "You must be on the master branch to release!"
|
45
|
-
exit!
|
46
|
-
end
|
47
|
-
sh "git commit --allow-empty -a -m 'Release #{version}'"
|
48
|
-
sh "git tag v#{version}"
|
49
|
-
sh "git push origin master"
|
50
|
-
sh "git push origin v#{version}"
|
51
|
-
sh "gem push pkg/#{name}-#{version}.gem"
|
52
|
-
end
|
53
|
-
|
54
|
-
desc "Build #{gem_file} into the pkg directory"
|
55
|
-
task :build => :gemspec do
|
56
|
-
sh "mkdir -p pkg"
|
57
|
-
sh "gem build #{gemspec_file}"
|
58
|
-
sh "mv #{gem_file} pkg"
|
59
|
-
end
|
60
|
-
|
61
|
-
desc "Generate #{gemspec_file}"
|
62
|
-
task :gemspec => :validate do
|
63
|
-
# read spec file and split out manifest section
|
64
|
-
spec = File.read(gemspec_file)
|
65
|
-
#head, manifest, tail = spec,split(" # = MANIFEST =\n")
|
66
|
-
|
67
|
-
# replace name version and date
|
68
|
-
replace_header(spec, :name)
|
69
|
-
replace_header(spec, :version)
|
70
|
-
replace_header(spec, :date)
|
71
|
-
#comment this out if your rubyforge_project has a different name
|
72
|
-
replace_header(spec, :rubyforge_project)
|
73
|
-
|
74
|
-
# piece file back together and write
|
75
|
-
File.open(gemspec_file, 'w') { |io| io.write(spec) }
|
76
|
-
puts "Updated #{gemspec_file}"
|
77
|
-
end
|
78
|
-
|
79
|
-
desc "Validate #{gemspec_file}"
|
80
|
-
task :validate do
|
81
|
-
prefix = name[/^([^-]*)/, 1]
|
82
|
-
libfiles = Dir['lib/*'] - ["lib/#{name}.rb", "lib/#{name}", "lib/#{prefix}"]
|
83
|
-
unless libfiles.empty?
|
84
|
-
puts "Directory `lib` should only contain a `#{name}.rb` file and `#{name}` dir."
|
85
|
-
exit!
|
86
|
-
end
|
87
|
-
unless Dir['VERSION*'].empty?
|
88
|
-
puts "A `VERSION` file at root level violates Gem best practices."
|
89
|
-
exit!
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
data/lib/rollout-dynamodb.rb
CHANGED
@@ -1,8 +1,21 @@
|
|
1
1
|
|
2
2
|
require 'fog'
|
3
3
|
|
4
|
+
##
|
5
|
+
# This module is a container for all things related to Rollout+DynamoDB functionality
|
4
6
|
module Rollout::DynamoDB
|
7
|
+
|
8
|
+
##
|
9
|
+
# This class implements the Storage API for Rollout
|
5
10
|
class Storage
|
11
|
+
|
12
|
+
##
|
13
|
+
# `options` is a Hash which may include:
|
14
|
+
# * `:connection_options` - a Hash which may in turn include:
|
15
|
+
# * `:connect_timeout` - Connection timeout to DynamoDB in seconds (default: `3`)
|
16
|
+
# * `:read_timeout` - Read timeout to DynamoDB in seconds (default: `3`)
|
17
|
+
# * `:write_timeout` - Write timeout to DynamoDB in seconds (default: `3`)
|
18
|
+
# * `:retry_limit` - Number of times to retry HTTP requests to DynamoDB (default: `4`)
|
6
19
|
def initialize(aws_access_key, aws_secret_key, table_name, region, options = {})
|
7
20
|
@aws_access_key = aws_access_key
|
8
21
|
@aws_secret_key = aws_secret_key
|
@@ -18,23 +31,42 @@ module Rollout::DynamoDB
|
|
18
31
|
@options[:connection_options][:retry_limit] ||= 4
|
19
32
|
end
|
20
33
|
|
34
|
+
##
|
35
|
+
# Gets the value (*String*) of a key (*String*) from DynamoDB. Returns the
|
36
|
+
# value or raises an exception on error
|
21
37
|
def get(key)
|
22
38
|
response = db.get_item(@table_name, {'HashKeyElement' => {'S' => key}})
|
23
39
|
get_item_from_body(response.body)
|
24
40
|
end
|
25
41
|
|
42
|
+
##
|
43
|
+
# Sets the value (*String*) of a key (*String*) in DynamoDB. Returns `true`
|
44
|
+
# on success, raises an exception on error
|
26
45
|
def set(key, value)
|
27
46
|
response = db.put_item(@table_name, {'id' => {'S' => key}, 'value' => {'S' => value}})
|
28
47
|
true
|
29
48
|
end
|
30
49
|
|
50
|
+
##
|
51
|
+
# Deletes a key (*String*) from DynamoDB. Returns `true` on success or
|
52
|
+
# raises an exception on errror
|
31
53
|
def del(key)
|
32
54
|
response = db.delete_item(@table_name, {'HashKeyElement' => {'S' => key}})
|
33
55
|
true
|
34
56
|
end
|
35
57
|
|
58
|
+
##
|
59
|
+
# Returns an `Array` of keys (`String`) from DynamoDB. Not used by the
|
60
|
+
# Storage API, but provided as a convenience and used in tests.
|
61
|
+
def list
|
62
|
+
response = db.scan(@table_name)
|
63
|
+
response.body['Items'].map { |x| get_key_from_result(x) }
|
64
|
+
end
|
65
|
+
|
36
66
|
private
|
37
67
|
|
68
|
+
##
|
69
|
+
# Returns the DynamoDB connection object
|
38
70
|
def db
|
39
71
|
@db ||= Fog::AWS::DynamoDB.new(
|
40
72
|
@options.merge(
|
@@ -43,8 +75,22 @@ module Rollout::DynamoDB
|
|
43
75
|
:region => @region))
|
44
76
|
end
|
45
77
|
|
78
|
+
##
|
79
|
+
# Extracts and returns a key (*String*) from a DynamoDB result set. May be `nil`.
|
80
|
+
def get_key_from_result(res)
|
81
|
+
res.fetch('id', {}).fetch('S', nil)
|
82
|
+
end
|
83
|
+
|
84
|
+
##
|
85
|
+
# Extracts and returns a value (*String*) from a DynamoDB result set. May be `nil`.
|
86
|
+
def get_item_from_result(res)
|
87
|
+
res.fetch('value', {}).fetch('S', nil)
|
88
|
+
end
|
89
|
+
|
90
|
+
##
|
91
|
+
# Returns an item (*Hash*) from a DynamoDB result body
|
46
92
|
def get_item_from_body(body)
|
47
|
-
body.fetch('Item', {})
|
93
|
+
get_item_from_result(body.fetch('Item', {}))
|
48
94
|
end
|
49
95
|
end
|
50
96
|
end
|
data/rollout-dynamodb.gemspec
CHANGED
@@ -21,6 +21,7 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.add_development_dependency "bundler", "~> 1.7"
|
22
22
|
spec.add_development_dependency "foreman", "~> 0.77"
|
23
23
|
spec.add_development_dependency "rake", "~> 10.0"
|
24
|
+
spec.add_development_dependency "bourne", "~> 1.6.0"
|
24
25
|
spec.add_development_dependency "rspec", "~> 3.2"
|
25
26
|
spec.add_development_dependency "webmock", "~> 1.20"
|
26
27
|
spec.add_dependency 'rollout', "~> 2.0"
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Rollout::DynamoDB do
|
4
|
+
before do
|
5
|
+
skip("DynamoDB env vars not set") unless dynamodb_configured?
|
6
|
+
WebMock.allow_net_connect!
|
7
|
+
|
8
|
+
aws_access_key = ENV['TEST_DYNAMO_ACCESS_KEY']
|
9
|
+
aws_secret_key = ENV['TEST_DYNAMO_SECRET_KEY']
|
10
|
+
aws_table_name = ENV['TEST_DYNAMO_TABLE_NAME']
|
11
|
+
aws_region = ENV['TEST_DYNAMO_REGION']
|
12
|
+
|
13
|
+
@storage = Rollout::DynamoDB::Storage.new(aws_access_key, aws_secret_key, aws_table_name, aws_region)
|
14
|
+
clear_table!(@storage, aws_table_name) if dynamodb_configured? and !skip_slow_tests?
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "DynamoDB integration" do
|
18
|
+
it "get works correctly when key doesn't exist" do
|
19
|
+
value = @storage.get("my-key")
|
20
|
+
assert_nil value
|
21
|
+
end
|
22
|
+
|
23
|
+
it "set and get work correctly for new keys" do
|
24
|
+
value = @storage.set("my-key", "my-value")
|
25
|
+
assert value
|
26
|
+
|
27
|
+
value = @storage.get("my-key")
|
28
|
+
assert_equal "my-value", value
|
29
|
+
end
|
30
|
+
|
31
|
+
it "set and get work correctly for existing keys" do
|
32
|
+
value = @storage.set("my-reused-key", "my-old-value")
|
33
|
+
assert value
|
34
|
+
|
35
|
+
value = @storage.set("my-reused-key", "my-new-value")
|
36
|
+
assert value
|
37
|
+
|
38
|
+
value = @storage.get("my-reused-key")
|
39
|
+
assert_equal "my-new-value", value
|
40
|
+
end
|
41
|
+
|
42
|
+
it "delete works on key that doesn't exist" do
|
43
|
+
value = @storage.del("my-nonexistent-key")
|
44
|
+
assert value
|
45
|
+
end
|
46
|
+
|
47
|
+
it "delete works for existing keys" do
|
48
|
+
value = @storage.set("my-deleted-key", "delete me")
|
49
|
+
assert value
|
50
|
+
|
51
|
+
value = @storage.del("my-deleted-key")
|
52
|
+
assert value
|
53
|
+
|
54
|
+
value = @storage.get("my-deleted-key")
|
55
|
+
assert_nil value
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,322 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
require 'rollout'
|
3
|
+
|
4
|
+
describe "Rollout" do
|
5
|
+
before do
|
6
|
+
skip("Skipping slow tests") if skip_slow_tests?
|
7
|
+
skip("DynamoDB env vars not set") unless dynamodb_configured?
|
8
|
+
WebMock.allow_net_connect!
|
9
|
+
aws_access_key = ENV['TEST_DYNAMO_ACCESS_KEY']
|
10
|
+
aws_secret_key = ENV['TEST_DYNAMO_SECRET_KEY']
|
11
|
+
aws_table_name = ENV['TEST_DYNAMO_TABLE_NAME']
|
12
|
+
aws_region = ENV['TEST_DYNAMO_REGION']
|
13
|
+
|
14
|
+
@storage = Rollout::DynamoDB::Storage.new(aws_access_key, aws_secret_key, aws_table_name, aws_region)
|
15
|
+
@rollout = Rollout.new(@storage)
|
16
|
+
clear_table!(@storage, aws_table_name) if dynamodb_configured? and !skip_slow_tests?
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "when a group is activated" do
|
20
|
+
before do
|
21
|
+
@rollout.define_group(:fivesonly) { |user| user.id == 5 }
|
22
|
+
@rollout.activate_group(:chat, :fivesonly)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "the feature is active for users for which the block evaluates to true" do
|
26
|
+
@rollout.should be_active(:chat, stub(:id => 5))
|
27
|
+
end
|
28
|
+
|
29
|
+
it "is not active for users for which the block evaluates to false" do
|
30
|
+
@rollout.should_not be_active(:chat, stub(:id => 1))
|
31
|
+
end
|
32
|
+
|
33
|
+
it "is not active if a group is found in Redis but not defined in Rollout" do
|
34
|
+
@rollout.activate_group(:chat, :fake)
|
35
|
+
@rollout.should_not be_active(:chat, stub(:id => 1))
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "the default all group" do
|
40
|
+
before do
|
41
|
+
@rollout.activate_group(:chat, :all)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "evaluates to true no matter what" do
|
45
|
+
@rollout.should be_active(:chat, stub(:id => 0))
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "deactivating a group" do
|
50
|
+
before do
|
51
|
+
@rollout.define_group(:fivesonly) { |user| user.id == 5 }
|
52
|
+
@rollout.activate_group(:chat, :all)
|
53
|
+
@rollout.activate_group(:chat, :some)
|
54
|
+
@rollout.activate_group(:chat, :fivesonly)
|
55
|
+
@rollout.deactivate_group(:chat, :all)
|
56
|
+
@rollout.deactivate_group(:chat, "some")
|
57
|
+
end
|
58
|
+
|
59
|
+
it "deactivates the rules for that group" do
|
60
|
+
@rollout.should_not be_active(:chat, stub(:id => 10))
|
61
|
+
end
|
62
|
+
|
63
|
+
it "leaves the other groups active" do
|
64
|
+
@rollout.get(:chat).groups.should == [:fivesonly]
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe "deactivating a feature completely" do
|
69
|
+
before do
|
70
|
+
@rollout.define_group(:fivesonly) { |user| user.id == 5 }
|
71
|
+
@rollout.activate_group(:chat, :all)
|
72
|
+
@rollout.activate_group(:chat, :fivesonly)
|
73
|
+
@rollout.activate_user(:chat, stub(:id => 51))
|
74
|
+
@rollout.activate_percentage(:chat, 100)
|
75
|
+
@rollout.activate(:chat)
|
76
|
+
@rollout.deactivate(:chat)
|
77
|
+
end
|
78
|
+
|
79
|
+
it "removes all of the groups" do
|
80
|
+
@rollout.should_not be_active(:chat, stub(:id => 0))
|
81
|
+
end
|
82
|
+
|
83
|
+
it "removes all of the users" do
|
84
|
+
@rollout.should_not be_active(:chat, stub(:id => 51))
|
85
|
+
end
|
86
|
+
|
87
|
+
it "removes the percentage" do
|
88
|
+
@rollout.should_not be_active(:chat, stub(:id => 24))
|
89
|
+
end
|
90
|
+
|
91
|
+
it "removes globally" do
|
92
|
+
@rollout.should_not be_active(:chat)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
describe "activating a specific user" do
|
97
|
+
before do
|
98
|
+
@rollout.activate_user(:chat, stub(:id => 42))
|
99
|
+
end
|
100
|
+
|
101
|
+
it "is active for that user" do
|
102
|
+
@rollout.should be_active(:chat, stub(:id => 42))
|
103
|
+
end
|
104
|
+
|
105
|
+
it "remains inactive for other users" do
|
106
|
+
@rollout.should_not be_active(:chat, stub(:id => 24))
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
describe "activating a specific user by ID" do
|
111
|
+
before do
|
112
|
+
@rollout.activate_user(:chat, 42)
|
113
|
+
end
|
114
|
+
|
115
|
+
it "is active for that user" do
|
116
|
+
@rollout.should be_active(:chat, stub(:id => 42))
|
117
|
+
end
|
118
|
+
|
119
|
+
it "remains inactive for other users" do
|
120
|
+
@rollout.should_not be_active(:chat, stub(:id => 24))
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
describe "activating a specific user with a string id" do
|
125
|
+
before do
|
126
|
+
@rollout.activate_user(:chat, stub(:id => 'user-72'))
|
127
|
+
end
|
128
|
+
|
129
|
+
it "is active for that user" do
|
130
|
+
@rollout.should be_active(:chat, stub(:id => 'user-72'))
|
131
|
+
end
|
132
|
+
|
133
|
+
it "remains inactive for other users" do
|
134
|
+
@rollout.should_not be_active(:chat, stub(:id => 'user-12'))
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
describe "deactivating a specific user" do
|
139
|
+
before do
|
140
|
+
@rollout.activate_user(:chat, stub(:id => 42))
|
141
|
+
@rollout.activate_user(:chat, stub(:id => 4242))
|
142
|
+
@rollout.activate_user(:chat, stub(:id => 24))
|
143
|
+
@rollout.deactivate_user(:chat, stub(:id => 42))
|
144
|
+
@rollout.deactivate_user(:chat, stub(:id => "4242"))
|
145
|
+
end
|
146
|
+
|
147
|
+
it "that user should no longer be active" do
|
148
|
+
@rollout.should_not be_active(:chat, stub(:id => 42))
|
149
|
+
end
|
150
|
+
|
151
|
+
it "remains active for other active users" do
|
152
|
+
@rollout.get(:chat).users.should == %w(24)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
describe "activating a feature globally" do
|
157
|
+
before do
|
158
|
+
@rollout.activate(:chat)
|
159
|
+
end
|
160
|
+
|
161
|
+
it "activates the feature" do
|
162
|
+
@rollout.should be_active(:chat)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
describe "activating a feature for a percentage of users" do
|
167
|
+
before do
|
168
|
+
@rollout.activate_percentage(:chat, 20)
|
169
|
+
end
|
170
|
+
|
171
|
+
it "activates the feature for that percentage of the users" do
|
172
|
+
(1..120).select { |id| @rollout.active?(:chat, stub(:id => id)) }.length.should be_within(1).of(20)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
describe "activating a feature for a percentage of users" do
|
177
|
+
before do
|
178
|
+
@rollout.activate_percentage(:chat, 20)
|
179
|
+
end
|
180
|
+
|
181
|
+
it "activates the feature for that percentage of the users" do
|
182
|
+
(1..200).select { |id| @rollout.active?(:chat, stub(:id => id)) }.length.should be_within(5).of(40)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
describe "activating a feature for a percentage of users" do
|
187
|
+
before do
|
188
|
+
@rollout.activate_percentage(:chat, 5)
|
189
|
+
end
|
190
|
+
|
191
|
+
it "activates the feature for that percentage of the users" do
|
192
|
+
(1..100).select { |id| @rollout.active?(:chat, stub(:id => id)) }.length.should be_within(2).of(5)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
describe "activating a feature for a group as a string" do
|
197
|
+
before do
|
198
|
+
@rollout.define_group(:admins) { |user| user.id == 5 }
|
199
|
+
@rollout.activate_group(:chat, 'admins')
|
200
|
+
end
|
201
|
+
|
202
|
+
it "the feature is active for users for which the block evaluates to true" do
|
203
|
+
@rollout.should be_active(:chat, stub(:id => 5))
|
204
|
+
end
|
205
|
+
|
206
|
+
it "is not active for users for which the block evaluates to false" do
|
207
|
+
@rollout.should_not be_active(:chat, stub(:id => 1))
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
describe "deactivating the percentage of users" do
|
212
|
+
before do
|
213
|
+
@rollout.activate_percentage(:chat, 100)
|
214
|
+
@rollout.deactivate_percentage(:chat)
|
215
|
+
end
|
216
|
+
|
217
|
+
it "becomes inactivate for all users" do
|
218
|
+
@rollout.should_not be_active(:chat, stub(:id => 24))
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
describe "deactivating the feature globally" do
|
223
|
+
before do
|
224
|
+
@rollout.activate(:chat)
|
225
|
+
@rollout.deactivate(:chat)
|
226
|
+
end
|
227
|
+
|
228
|
+
it "becomes inactivate" do
|
229
|
+
@rollout.should_not be_active(:chat)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
describe "setting a feature on" do
|
234
|
+
before do
|
235
|
+
@rollout.set(:chat, true)
|
236
|
+
end
|
237
|
+
|
238
|
+
it "becomes activated" do
|
239
|
+
@rollout.should be_active(:chat)
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
describe "setting a feature off" do
|
244
|
+
before do
|
245
|
+
@rollout.set(:chat, false)
|
246
|
+
end
|
247
|
+
|
248
|
+
it "becomes activated" do
|
249
|
+
@rollout.should_not be_active(:chat)
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
describe "keeps a list of features" do
|
254
|
+
it "saves the feature" do
|
255
|
+
@rollout.activate(:chat)
|
256
|
+
@rollout.features.should be_include(:chat)
|
257
|
+
end
|
258
|
+
|
259
|
+
it "does not contain doubles" do
|
260
|
+
@rollout.activate(:chat)
|
261
|
+
@rollout.activate(:chat)
|
262
|
+
@rollout.features.size.should == 1
|
263
|
+
end
|
264
|
+
|
265
|
+
it "does not contain doubles when using string" do
|
266
|
+
@rollout.activate(:chat)
|
267
|
+
@rollout.activate("chat")
|
268
|
+
@rollout.features.size.should == 1
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
describe "#get" do
|
273
|
+
before do
|
274
|
+
@rollout.activate_percentage(:chat, 10)
|
275
|
+
@rollout.activate_group(:chat, :caretakers)
|
276
|
+
@rollout.activate_group(:chat, :greeters)
|
277
|
+
@rollout.activate(:signup)
|
278
|
+
@rollout.activate_user(:chat, stub(:id => 42))
|
279
|
+
end
|
280
|
+
|
281
|
+
it "returns the feature object" do
|
282
|
+
feature = @rollout.get(:chat)
|
283
|
+
feature.groups.should == [:caretakers, :greeters]
|
284
|
+
feature.percentage.should == 10
|
285
|
+
feature.users.should == %w(42)
|
286
|
+
feature.to_hash.should == {
|
287
|
+
:groups => [:caretakers, :greeters],
|
288
|
+
:percentage => 10,
|
289
|
+
:users => %w(42)
|
290
|
+
}
|
291
|
+
|
292
|
+
feature = @rollout.get(:signup)
|
293
|
+
feature.groups.should be_empty
|
294
|
+
feature.users.should be_empty
|
295
|
+
feature.percentage.should == 100
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
describe "#clear" do
|
300
|
+
let(:features) { %w(signup beta alpha gm) }
|
301
|
+
|
302
|
+
before do
|
303
|
+
features.each { |f| @rollout.activate(f) }
|
304
|
+
|
305
|
+
@rollout.clear!
|
306
|
+
end
|
307
|
+
|
308
|
+
it "each feature is cleared" do
|
309
|
+
features.each do |feature|
|
310
|
+
@rollout.get(feature).to_hash.should == {
|
311
|
+
:percentage => 0,
|
312
|
+
:users => [],
|
313
|
+
:groups => []
|
314
|
+
}
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
it "removes all features" do
|
319
|
+
@rollout.features.should be_empty
|
320
|
+
end
|
321
|
+
end
|
322
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -3,10 +3,31 @@ require "bundler"
|
|
3
3
|
|
4
4
|
Bundler.require(:default, :development)
|
5
5
|
|
6
|
-
require "webmock/rspec"
|
7
6
|
|
8
7
|
require "rollout/dynamodb/storage"
|
8
|
+
require "rollout"
|
9
|
+
require "webmock/rspec"
|
10
|
+
require "rspec"
|
11
|
+
require "bourne"
|
12
|
+
|
13
|
+
def dynamodb_configured?
|
14
|
+
[
|
15
|
+
ENV['TEST_DYNAMO_ACCESS_KEY'],
|
16
|
+
ENV['TEST_DYNAMO_SECRET_KEY'],
|
17
|
+
ENV['TEST_DYNAMO_TABLE_NAME'],
|
18
|
+
ENV['TEST_DYNAMO_REGION'],
|
19
|
+
].all?
|
20
|
+
end
|
21
|
+
|
22
|
+
def skip_slow_tests?
|
23
|
+
ENV['TEST_SKIP_SLOW'] && !["false", "0"].include?(ENV['TEST_SKIP_SLOW'].upcase)
|
24
|
+
end
|
25
|
+
|
26
|
+
def clear_table!(storage, table)
|
27
|
+
storage.list.map { |item| storage.del(item) }
|
28
|
+
end
|
9
29
|
|
10
30
|
RSpec.configure do |config|
|
11
|
-
config.expect_with :test_unit
|
31
|
+
config.expect_with :rspec, :test_unit
|
32
|
+
config.mock_with :mocha
|
12
33
|
end
|
@@ -84,6 +84,22 @@ describe Rollout::DynamoDB do
|
|
84
84
|
assert response
|
85
85
|
end
|
86
86
|
end
|
87
|
+
|
88
|
+
describe "#list" do
|
89
|
+
it "sends a valid post and returns valid data" do
|
90
|
+
stub_request(:post, @dynamo_url).
|
91
|
+
to_return(:body => "{\"ConsumedCapacityUnits\":0.5,\"Count\":4,\"Items\":[{\"id\":{\"S\":\"my-reused-key\"},\"value\":{\"S\":\"my-new-value\"}},{\"value\":{\"S\":\"0||\"},\"id\":{\"S\":\"feature:chat\"}},{\"id\":{\"S\":\"my-key\"},\"value\":{\"S\":\"my-value\"}},{\"value\":{\"S\":\"feature:__features__,chat\"},\"id\":{\"S\":\"feature:__features__\"}}],\"ScannedCount\":4}")
|
92
|
+
|
93
|
+
result_set = @storage.list
|
94
|
+
assert_requested(:post, @dynamo_url) do |req|
|
95
|
+
body = MultiJson.decode(req.body)
|
96
|
+
|
97
|
+
body == {"TableName"=>"table"} &&
|
98
|
+
req.headers["X-Amz-Target"] == "DynamoDB_20111205.Scan"
|
99
|
+
end
|
100
|
+
assert_equal result_set, ["my-reused-key", "feature:chat", "my-key", "feature:__features__"]
|
101
|
+
end
|
102
|
+
end
|
87
103
|
# ...
|
88
104
|
end
|
89
105
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rollout-dynamodb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Charles Hooper
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-03-
|
11
|
+
date: 2015-03-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '10.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: bourne
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.6.0
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 1.6.0
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: rspec
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -115,6 +129,7 @@ executables: []
|
|
115
129
|
extensions: []
|
116
130
|
extra_rdoc_files: []
|
117
131
|
files:
|
132
|
+
- ".env.test"
|
118
133
|
- ".gitignore"
|
119
134
|
- ".rspec"
|
120
135
|
- ".travis.yml"
|
@@ -125,8 +140,10 @@ files:
|
|
125
140
|
- lib/rollout-dynamodb.rb
|
126
141
|
- lib/rollout/dynamodb/storage.rb
|
127
142
|
- rollout-dynamodb.gemspec
|
128
|
-
- spec/
|
143
|
+
- spec/dynamodb_integration_spec.rb
|
144
|
+
- spec/rollout_dynamodb_integration_spec.rb
|
129
145
|
- spec/spec_helper.rb
|
146
|
+
- spec/storage_spec.rb
|
130
147
|
- spec/version_spec.rb
|
131
148
|
homepage: https://github.com/chooper/rollout-dynamodb
|
132
149
|
licenses:
|
@@ -153,6 +170,8 @@ signing_key:
|
|
153
170
|
specification_version: 4
|
154
171
|
summary: DynamoDB storage adapter for Rollout
|
155
172
|
test_files:
|
156
|
-
- spec/
|
173
|
+
- spec/dynamodb_integration_spec.rb
|
174
|
+
- spec/rollout_dynamodb_integration_spec.rb
|
157
175
|
- spec/spec_helper.rb
|
176
|
+
- spec/storage_spec.rb
|
158
177
|
- spec/version_spec.rb
|