feature_creep 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile.lock +8 -4
- data/README.md +8 -2
- data/feature_creep.gemspec +2 -1
- data/lib/feature_creep/version.rb +1 -1
- data/spec/feature_creep_spec.rb +4 -6
- data/spec/spec_helper.rb +5 -4
- metadata +19 -5
- data/lib/feature_creep/default_config.rb +0 -36
- data/lib/feature_creep/redis_datastore.rb +0 -116
data/Gemfile.lock
CHANGED
@@ -1,22 +1,24 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
feature_creep (0.2.
|
5
|
-
redis
|
4
|
+
feature_creep (0.2.1)
|
6
5
|
|
7
6
|
GEM
|
8
7
|
remote: http://rubygems.org/
|
9
8
|
specs:
|
10
9
|
bourne (1.0)
|
11
10
|
mocha (= 0.9.8)
|
12
|
-
coderay (1.0.
|
11
|
+
coderay (1.0.8)
|
13
12
|
diff-lcs (1.1.3)
|
13
|
+
feature_creep-redis (0.0.1)
|
14
|
+
redis
|
15
|
+
feature_creep-simple_strategy (0.0.2)
|
14
16
|
git (1.2.5)
|
15
17
|
jeweler (1.6.4)
|
16
18
|
bundler (~> 1.0)
|
17
19
|
git (>= 1.2.5)
|
18
20
|
rake
|
19
|
-
method_source (0.8)
|
21
|
+
method_source (0.8.1)
|
20
22
|
mocha (0.9.8)
|
21
23
|
rake
|
22
24
|
pry (0.9.10)
|
@@ -42,6 +44,8 @@ DEPENDENCIES
|
|
42
44
|
bourne (= 1.0)
|
43
45
|
bundler (>= 1.0.0)
|
44
46
|
feature_creep!
|
47
|
+
feature_creep-redis
|
48
|
+
feature_creep-simple_strategy (= 0.0.2)
|
45
49
|
jeweler (~> 1.6.4)
|
46
50
|
mocha (= 0.9.8)
|
47
51
|
pry (= 0.9.10)
|
data/README.md
CHANGED
@@ -11,6 +11,11 @@ This is not a drop in replacement. The API has aleadey changed.
|
|
11
11
|
Namely, `groups` are now `scopes` and `users` are `agent_ids`.
|
12
12
|
Agent_ids are expected to be uuids, not the object itself.
|
13
13
|
|
14
|
+
There are currently 3 gems in this ecosystem:
|
15
|
+
`gem 'feature_creep'`
|
16
|
+
`gem 'feature_creep-redis'`
|
17
|
+
`gem 'feature_creep-simple_strategy'`
|
18
|
+
|
14
19
|
The class constructor now takes
|
15
20
|
|
16
21
|
`datastore` -- FeatureCreep::RedisDatastore is provided with more to come.
|
@@ -25,8 +30,9 @@ The class constructor now takes
|
|
25
30
|
|
26
31
|
`options[:features]` is an array of strings or symbols that will add to the list of all possible features.
|
27
32
|
|
28
|
-
@feature_creep = FeatureCreep.new(
|
29
|
-
FeatureCreep::
|
33
|
+
@feature_creep = FeatureCreep.new(FeatureCreep::RedisDataStore.new(Redis.new,"parent_namespace"),
|
34
|
+
FeatureCreep::SimpleStrategy.warden,
|
35
|
+
FeatureCreep::SimpleStrategy.info,
|
30
36
|
{:scopes => {:some_scope_name => lambda { |agent_id| User.find(agent_id).can?(:some_scope) }}, :features => [:feature_1, :feature_2]})
|
31
37
|
|
32
38
|
At this point it should be easy to extend.
|
data/feature_creep.gemspec
CHANGED
@@ -27,6 +27,7 @@ Gem::Specification.new do |s|
|
|
27
27
|
s.add_development_dependency "bourne", "1.0"
|
28
28
|
s.add_development_dependency "mocha", "0.9.8"
|
29
29
|
s.add_development_dependency "pry", "0.9.10"
|
30
|
+
s.add_development_dependency "feature_creep-redis"
|
31
|
+
s.add_development_dependency "feature_creep-simple_strategy", "0.0.2"
|
30
32
|
|
31
|
-
s.add_runtime_dependency "redis"
|
32
33
|
end
|
data/spec/feature_creep_spec.rb
CHANGED
@@ -3,12 +3,10 @@ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
|
3
3
|
describe "FeatureCreep" do
|
4
4
|
before do
|
5
5
|
@datastore = FeatureCreep::RedisDataStore.new
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
FeatureCreep::DefaultConfig.info,
|
11
|
-
{:scopes => scopes, :features => [:test1, :test2]})
|
6
|
+
@feature_creep = FeatureCreep.new(@datastore,FeatureCreep::SimpleStrategy.warden,
|
7
|
+
FeatureCreep::SimpleStrategy.info,
|
8
|
+
{:scopes => {:fivesonly => lambda { |agent_id| agent_id == 5 }},
|
9
|
+
:features => [:test1, :test2]})
|
12
10
|
end
|
13
11
|
|
14
12
|
describe ".new" do
|
data/spec/spec_helper.rb
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
2
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
|
-
require '
|
4
|
-
require 'feature_creep/redis_datastore'
|
5
|
-
require 'feature_creep/default_config'
|
3
|
+
require 'pry'
|
6
4
|
require 'rspec'
|
7
5
|
require 'bourne'
|
8
6
|
require 'redis'
|
9
|
-
require '
|
7
|
+
require 'feature_creep'
|
8
|
+
require 'feature_creep/redis_data_store'
|
9
|
+
require 'feature_creep/simple_strategy'
|
10
10
|
|
11
11
|
RSpec.configure do |config|
|
12
12
|
config.mock_with :mocha
|
13
|
+
# Redis Data Store
|
13
14
|
config.before { Redis.new.flushdb }
|
14
15
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: feature_creep
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -108,14 +108,14 @@ dependencies:
|
|
108
108
|
- !ruby/object:Gem::Version
|
109
109
|
version: 0.9.10
|
110
110
|
- !ruby/object:Gem::Dependency
|
111
|
-
name: redis
|
111
|
+
name: feature_creep-redis
|
112
112
|
requirement: !ruby/object:Gem::Requirement
|
113
113
|
none: false
|
114
114
|
requirements:
|
115
115
|
- - ! '>='
|
116
116
|
- !ruby/object:Gem::Version
|
117
117
|
version: '0'
|
118
|
-
type: :
|
118
|
+
type: :development
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
none: false
|
@@ -123,6 +123,22 @@ dependencies:
|
|
123
123
|
- - ! '>='
|
124
124
|
- !ruby/object:Gem::Version
|
125
125
|
version: '0'
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: feature_creep-simple_strategy
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - '='
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: 0.0.2
|
134
|
+
type: :development
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - '='
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: 0.0.2
|
126
142
|
description: Feature Flag implementation
|
127
143
|
email:
|
128
144
|
- xn@gotham.us
|
@@ -137,8 +153,6 @@ files:
|
|
137
153
|
- README.md
|
138
154
|
- feature_creep.gemspec
|
139
155
|
- lib/feature_creep.rb
|
140
|
-
- lib/feature_creep/default_config.rb
|
141
|
-
- lib/feature_creep/redis_datastore.rb
|
142
156
|
- lib/feature_creep/version.rb
|
143
157
|
- spec/feature_creep_spec.rb
|
144
158
|
- spec/spec.opts
|
@@ -1,36 +0,0 @@
|
|
1
|
-
class FeatureCreep
|
2
|
-
class DefaultConfig
|
3
|
-
def self.info
|
4
|
-
lambda { |creep,feature|
|
5
|
-
if feature
|
6
|
-
{
|
7
|
-
:percentage => (creep.active_percentage(feature) || 0).to_i,
|
8
|
-
:scopes => creep.active_scopes(feature).map { |g| g.to_sym },
|
9
|
-
:agent_ids => creep.active_agent_ids(feature),
|
10
|
-
:global => creep.active_global_features,
|
11
|
-
:available_features => creep.features
|
12
|
-
}
|
13
|
-
else
|
14
|
-
{
|
15
|
-
:global => creep.active_global_features,
|
16
|
-
:available_features => creep.features
|
17
|
-
}
|
18
|
-
end
|
19
|
-
}
|
20
|
-
|
21
|
-
end
|
22
|
-
|
23
|
-
def self.warden
|
24
|
-
lambda { |creep,feature,agent_id|
|
25
|
-
if agent_id
|
26
|
-
creep.active_globally?(feature) ||
|
27
|
-
creep.agent_id_in_active_scope?(feature, agent_id) ||
|
28
|
-
creep.agent_id_active?(feature, agent_id) ||
|
29
|
-
creep.agent_id_within_active_percentage?(feature, agent_id)
|
30
|
-
else
|
31
|
-
creep.active_globally?(feature)
|
32
|
-
end
|
33
|
-
}
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
@@ -1,116 +0,0 @@
|
|
1
|
-
class FeatureCreep
|
2
|
-
class RedisDataStore
|
3
|
-
|
4
|
-
def initialize(datastore = nil, key_prefix = "feature_creep")
|
5
|
-
@key_prefix = key_prefix
|
6
|
-
@redis = datastore || Redis.new
|
7
|
-
end
|
8
|
-
|
9
|
-
# Activate Methods
|
10
|
-
def activate_globally(feature)
|
11
|
-
@redis.sadd(global_key, feature)
|
12
|
-
end
|
13
|
-
|
14
|
-
def activate_scope(feature, scope)
|
15
|
-
@redis.sadd(scope_key(feature), scope)
|
16
|
-
end
|
17
|
-
|
18
|
-
def activate_agent_id(feature, agent_id)
|
19
|
-
@redis.sadd(agent_id_key(feature), agent_id)
|
20
|
-
end
|
21
|
-
|
22
|
-
def activate_percentage(feature, percentage)
|
23
|
-
@redis.set(percentage_key(feature), percentage)
|
24
|
-
end
|
25
|
-
|
26
|
-
# Deactivate Methods
|
27
|
-
def deactivate_globally(feature)
|
28
|
-
@redis.srem(global_key, feature)
|
29
|
-
end
|
30
|
-
|
31
|
-
def deactivate_scope(feature, scope)
|
32
|
-
@redis.srem(scope_key(feature), scope)
|
33
|
-
end
|
34
|
-
|
35
|
-
def deactivate_agent_id(feature, agent_id)
|
36
|
-
@redis.srem(agent_id_key(feature), agent_id)
|
37
|
-
end
|
38
|
-
|
39
|
-
def deactivate_percentage(feature)
|
40
|
-
@redis.del(percentage_key(feature))
|
41
|
-
end
|
42
|
-
|
43
|
-
def deactivate_all(feature)
|
44
|
-
@redis.del(scope_key(feature))
|
45
|
-
@redis.del(agent_id_key(feature))
|
46
|
-
@redis.del(percentage_key(feature))
|
47
|
-
deactivate_globally(feature)
|
48
|
-
end
|
49
|
-
|
50
|
-
# Reporting Methods
|
51
|
-
def active_global_features
|
52
|
-
(@redis.smembers(global_key) || []).map(&:to_sym)
|
53
|
-
end
|
54
|
-
|
55
|
-
def active_scopes(feature)
|
56
|
-
@redis.smembers(scope_key(feature)) || []
|
57
|
-
end
|
58
|
-
|
59
|
-
def active_agent_ids(feature)
|
60
|
-
@redis.smembers(agent_id_key(feature))
|
61
|
-
end
|
62
|
-
|
63
|
-
def active_percentage(feature)
|
64
|
-
@redis.get(percentage_key(feature))
|
65
|
-
end
|
66
|
-
|
67
|
-
# Boolean Methods
|
68
|
-
def active_globally?(feature)
|
69
|
-
@redis.sismember(global_key, feature)
|
70
|
-
end
|
71
|
-
|
72
|
-
def agent_id_active?(feature, agent_id)
|
73
|
-
@redis.sismember(agent_id_key(feature), agent_id)
|
74
|
-
end
|
75
|
-
|
76
|
-
def agent_id_within_active_percentage?(feature, agent_id)
|
77
|
-
percentage = active_percentage(feature)
|
78
|
-
return false if percentage.nil?
|
79
|
-
agent_id % 100 < percentage.to_i
|
80
|
-
end
|
81
|
-
|
82
|
-
# Utility Methods
|
83
|
-
def features
|
84
|
-
@redis.smembers(@key_prefix).map(&:to_sym)
|
85
|
-
end
|
86
|
-
|
87
|
-
def add_feature(feature)
|
88
|
-
@redis.sadd(@key_prefix, feature)
|
89
|
-
end
|
90
|
-
|
91
|
-
def remove_feature(feature)
|
92
|
-
@redis.srem(@key_prefix, feature)
|
93
|
-
end
|
94
|
-
|
95
|
-
private
|
96
|
-
def key(name)
|
97
|
-
"#{@key_prefix}:#{name}"
|
98
|
-
end
|
99
|
-
|
100
|
-
def scope_key(name)
|
101
|
-
"#{key(name)}:scopes"
|
102
|
-
end
|
103
|
-
|
104
|
-
def agent_id_key(name)
|
105
|
-
"#{key(name)}:agent_ids"
|
106
|
-
end
|
107
|
-
|
108
|
-
def percentage_key(name)
|
109
|
-
"#{key(name)}:percentage"
|
110
|
-
end
|
111
|
-
|
112
|
-
def global_key
|
113
|
-
"#{@key_prefix}:__global__"
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|