feature_creep 0.2.1 → 0.3.0
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.
- 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
|