feature 1.3.0 → 1.4.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/Gemfile +5 -2
- data/README.md +152 -139
- data/Rakefile +2 -2
- data/lib/feature.rb +26 -11
- data/lib/feature/repository/active_record_repository.rb +2 -2
- data/lib/feature/repository/redis_repository.rb +17 -5
- data/lib/feature/repository/simple_repository.rb +2 -2
- data/lib/feature/repository/yaml_repository.rb +2 -2
- data/spec/feature/active_record_repository_spec.rb +1 -1
- data/spec/feature/feature_spec.rb +37 -0
- data/spec/feature/redis_repository_spec.rb +6 -0
- data/spec/integration/rails/gemfiles/rails4.gemfile +1 -2
- data/spec/spec_helper.rb +1 -0
- metadata +3 -4
- data/spec/integration/rails/gemfiles/rails4.gemfile.lock +0 -85
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7dfe050498795b0fa100aa76b2f366152762a73f
|
4
|
+
data.tar.gz: 4826e039f1642c887d1a62ce917d20cc54f597ec
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0670fda64dac5f6a932cd5fa88d257b53269a5a304037a3b8f984e1f865411a5fe5df6ea6a7e3e2b23f9311a2d27be3aeb0d3faeaf611d555143c8ba854695e3
|
7
|
+
data.tar.gz: d434bc4e9f2977d8aa864531a0dab79059fe5682b74d0194464e9e2fcae0381efed9514c565702b529315d3e755189ad04a993193fd603c16bdd2f68e95b0646
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
## 1.4.0 (2016-06-11)
|
2
|
+
|
3
|
+
* possibility to refresh feature list after certain time (enc)
|
4
|
+
* call to get all active features (pmeskers)
|
5
|
+
* Improve and prettify README (glaucocustodio)
|
6
|
+
* Fixing typos in README (adarsh)
|
7
|
+
|
1
8
|
## 1.3.0 (2015-06-12)
|
2
9
|
|
3
10
|
* Support testing with multiple features (stevenwilkin)
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -11,13 +11,13 @@ Feature is a battle-tested [feature toggle](http://martinfowler.com/bliki/Featur
|
|
11
11
|
|
12
12
|
The feature toggle functionality has to be configured by feature repositories. A feature repository simply provides lists of active features (symbols!). Unknown features are assumed inactive.
|
13
13
|
|
14
|
-
With this approach Feature is
|
14
|
+
With this approach Feature is highly configurable and not bound to a specific kind of configuration.
|
15
15
|
|
16
|
-
**NOTE:**
|
16
|
+
**NOTE:** The current gem version works with Ruby 2+ and supports Ruby on Rails 4+.
|
17
17
|
|
18
|
-
**NOTE:** Ruby 1.
|
18
|
+
**NOTE:** Ruby 1.9 is supported until version 1.2.0, Ruby 1.8 is supported until version 0.7.0.
|
19
19
|
|
20
|
-
**NOTE:**
|
20
|
+
**NOTE:** ActiveRecord / Rails 3 is supported until version 1.1.0.
|
21
21
|
|
22
22
|
## Installation
|
23
23
|
|
@@ -26,41 +26,53 @@ With this approach Feature is higly configurable and not bound to a specific kin
|
|
26
26
|
## How to use
|
27
27
|
|
28
28
|
* Setup Feature
|
29
|
-
* Create a repository (see
|
30
|
-
|
29
|
+
* Create a repository (for more infos about configuration backends, see section below)
|
30
|
+
```ruby
|
31
|
+
require 'feature'
|
32
|
+
repo = Feature::Repository::SimpleRepository.new
|
33
|
+
```
|
31
34
|
|
32
|
-
|
35
|
+
* Set repository to Feature
|
36
|
+
```ruby
|
37
|
+
Feature.set_repository(repo)
|
38
|
+
```
|
33
39
|
|
34
40
|
* Use Feature in your production code
|
41
|
+
```ruby
|
42
|
+
Feature.active?(:feature_name) # => true/false
|
35
43
|
|
36
|
-
|
44
|
+
Feature.inactive?(:feature_name) # => true/false
|
37
45
|
|
38
|
-
|
46
|
+
Feature.active_features # => [:list, :of, :features]
|
39
47
|
|
40
|
-
|
41
|
-
|
42
|
-
|
48
|
+
Feature.with(:feature_name) do
|
49
|
+
# code
|
50
|
+
end
|
43
51
|
|
44
|
-
|
45
|
-
|
46
|
-
|
52
|
+
Feature.without(:feature_name) do
|
53
|
+
# code
|
54
|
+
end
|
47
55
|
|
48
|
-
|
56
|
+
# this returns value_true if :feature_name is active, otherwise value_false
|
57
|
+
Feature.switch(:feature_name, value_true, value_false)
|
49
58
|
|
50
|
-
|
51
|
-
|
59
|
+
# switch may also take Procs that will be evaluated and it's result returned.
|
60
|
+
Feature.switch(:feature_name, -> { code... }, -> { code... })
|
61
|
+
```
|
52
62
|
|
53
63
|
* Use Feature in your test code (for reliable testing of feature depending code)
|
64
|
+
```ruby
|
65
|
+
require 'feature/testing'
|
54
66
|
|
55
|
-
|
67
|
+
Feature.run_with_activated(:feature) do
|
68
|
+
# your test code
|
69
|
+
end
|
56
70
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
# your test code
|
63
|
-
end
|
71
|
+
# you also can give a list of features
|
72
|
+
Feature.run_with_deactivated(:feature, :another_feature) do
|
73
|
+
# your test code
|
74
|
+
end
|
75
|
+
```
|
64
76
|
|
65
77
|
* Feature-toggle caching
|
66
78
|
|
@@ -68,123 +80,124 @@ With this approach Feature is higly configurable and not bound to a specific kin
|
|
68
80
|
underlying repository the first time you try to check whether a
|
69
81
|
feature is set or not.
|
70
82
|
|
71
|
-
* Subsequent
|
72
|
-
representation of the
|
83
|
+
* Subsequent calls to Feature will access the cached in-memory
|
84
|
+
representation of the list of features. So changes to toggles in the
|
73
85
|
underlying repository would not be reflected in the application
|
74
|
-
until you restart the application or
|
75
|
-
|
76
|
-
|
86
|
+
until you restart the application or manually call
|
87
|
+
```ruby
|
88
|
+
Feature.refresh!
|
89
|
+
```
|
77
90
|
|
78
91
|
* You can optionally pass in true as a second argument on
|
79
92
|
set_repository, to force Feature to auto-refresh the feature list
|
80
93
|
on every feature-toggle check you make.
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
<% end %>
|
94
|
+
```ruby
|
95
|
+
Feature.set_repository(your_repository, true)
|
96
|
+
```
|
97
|
+
|
98
|
+
* You can also optionally pass in a number as second argument on
|
99
|
+
set_repository, to force Feature to refresh the feature list
|
100
|
+
after X seconds. This will be done only on demand by a request.
|
101
|
+
```ruby
|
102
|
+
Feature.set_repository(your_repository, 60)
|
103
|
+
```
|
104
|
+
|
105
|
+
## How to setup different backends
|
106
|
+
|
107
|
+
### SimpleRepository (in-memory)
|
108
|
+
```ruby
|
109
|
+
# File: Gemfile
|
110
|
+
gem 'feature'
|
111
|
+
```
|
112
|
+
|
113
|
+
```ruby
|
114
|
+
# setup code
|
115
|
+
require 'feature'
|
116
|
+
|
117
|
+
repo = Feature::Repository::SimpleRepository.new
|
118
|
+
repo.add_active_feature :be_nice
|
119
|
+
|
120
|
+
Feature.set_repository repo
|
121
|
+
```
|
122
|
+
|
123
|
+
### RedisRepository (features configured in redis server)
|
124
|
+
```ruby
|
125
|
+
# See here to learn how to configure redis: https://github.com/redis/redis-rb
|
126
|
+
|
127
|
+
# File: Gemfile
|
128
|
+
gem 'feature'
|
129
|
+
gem 'redis'
|
130
|
+
```
|
131
|
+
|
132
|
+
```ruby
|
133
|
+
# setup code (or Rails initializer: config/initializers/feature.rb)
|
134
|
+
require 'feature'
|
135
|
+
|
136
|
+
# "feature_toggles" will be the key name in redis
|
137
|
+
repo = Feature::Repository::RedisRepository.new("feature_toggles")
|
138
|
+
Feature.set_repository repo
|
139
|
+
|
140
|
+
# add/toggle features in Redis
|
141
|
+
Redis.current.hset("feature_toggles", "ActiveFeature", true)
|
142
|
+
Redis.current.hset("feature_toggles", "InActiveFeature", false)
|
143
|
+
```
|
144
|
+
|
145
|
+
### YamlRepository (features configured in static yml file)
|
146
|
+
```ruby
|
147
|
+
# File: Gemfile
|
148
|
+
gem 'feature'
|
149
|
+
```
|
150
|
+
|
151
|
+
```
|
152
|
+
# File: config/feature.yml
|
153
|
+
features:
|
154
|
+
an_active_feature: true
|
155
|
+
an_inactive_feature: false
|
156
|
+
```
|
157
|
+
|
158
|
+
```ruby
|
159
|
+
# setup code (or Rails initializer: config/initializers/feature.rb)
|
160
|
+
repo = Feature::Repository::YamlRepository.new("#{Rails.root}/config/feature.yml")
|
161
|
+
Feature.set_repository repo
|
162
|
+
```
|
151
163
|
|
152
164
|
You may also specify a Rails environment to use a new feature in development and test, but not production:
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
###
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
165
|
+
```
|
166
|
+
# File: config/feature.yml
|
167
|
+
test:
|
168
|
+
features:
|
169
|
+
a_new_feature: true
|
170
|
+
production:
|
171
|
+
features:
|
172
|
+
a_new_feature: false
|
173
|
+
```
|
174
|
+
|
175
|
+
```ruby
|
176
|
+
# File: config/initializers/feature.rb
|
177
|
+
repo = Feature::Repository::YamlRepository.new("#{Rails.root}/config/feature.yml", Rails.env)
|
178
|
+
Feature.set_repository repo
|
179
|
+
```
|
180
|
+
|
181
|
+
### ActiveRecordRepository (features configured in a database) using Rails
|
182
|
+
|
183
|
+
```ruby
|
184
|
+
# File: Gemfile
|
185
|
+
gem 'feature'
|
186
|
+
```
|
187
|
+
|
188
|
+
```
|
189
|
+
# Run generator and migrations
|
190
|
+
$ rails g feature:install
|
191
|
+
$ rake db:migrate
|
192
|
+
```
|
193
|
+
|
194
|
+
```ruby
|
195
|
+
# Add Features to table FeaturesToggle for example in
|
196
|
+
# File: db/schema.rb
|
197
|
+
FeatureToggle.create!(name: "ActiveFeature", active: true)
|
198
|
+
FeatureToggle.create!(name: "InActiveFeature", active: false)
|
199
|
+
|
200
|
+
# or in initializer
|
201
|
+
# File: config/initializers/feature.rb
|
202
|
+
repo.add_active_feature(:active_feature)
|
203
|
+
```
|
data/Rakefile
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
require 'rake/testtask'
|
2
2
|
require 'rspec/core/rake_task'
|
3
3
|
require 'rubocop/rake_task'
|
4
|
-
require 'mutant'
|
5
4
|
|
6
5
|
RuboCop::RakeTask.new
|
7
6
|
|
@@ -11,8 +10,9 @@ RSpec::Core::RakeTask.new(:spec) do |spec|
|
|
11
10
|
end
|
12
11
|
|
13
12
|
task :mutant do
|
13
|
+
require 'mutant'
|
14
14
|
result = Mutant::CLI.run(%w(--include lib --require feature --use rspec Feature*))
|
15
|
-
|
15
|
+
raise unless result == Mutant::CLI::EXIT_SUCCESS
|
16
16
|
end
|
17
17
|
|
18
18
|
task default: [:spec, :rubocop]
|
data/lib/feature.rb
CHANGED
@@ -31,16 +31,22 @@ module Feature
|
|
31
31
|
# The given repository has to respond to method 'active_features' with an array of symbols
|
32
32
|
#
|
33
33
|
# @param [Object] repository the repository to get the features from
|
34
|
-
# @param [Boolean]
|
34
|
+
# @param [Boolean|Integer] refresh optional (default: false) - auto refresh or refresh after given number of seconds
|
35
35
|
#
|
36
|
-
def self.set_repository(repository,
|
36
|
+
def self.set_repository(repository, refresh = false)
|
37
37
|
unless repository.respond_to?(:active_features)
|
38
|
-
|
38
|
+
raise ArgumentError, 'given repository does not respond to active_features'
|
39
39
|
end
|
40
40
|
|
41
|
-
@auto_refresh = auto_refresh
|
42
41
|
@perform_initial_refresh = true
|
43
42
|
@repository = repository
|
43
|
+
if [true, false].include?(refresh)
|
44
|
+
@auto_refresh = refresh
|
45
|
+
else
|
46
|
+
@auto_refresh = false
|
47
|
+
@refresh_after = refresh
|
48
|
+
@next_refresh_after = Time.now + @refresh_after
|
49
|
+
end
|
44
50
|
end
|
45
51
|
|
46
52
|
# Refreshes list of active features from repository.
|
@@ -48,6 +54,7 @@ module Feature
|
|
48
54
|
#
|
49
55
|
def self.refresh!
|
50
56
|
@active_features = @repository.active_features
|
57
|
+
@next_refresh_after = Time.now + @refresh_after if @refresh_after
|
51
58
|
@perform_initial_refresh = false
|
52
59
|
end
|
53
60
|
|
@@ -58,11 +65,7 @@ module Feature
|
|
58
65
|
# @return [Boolean]
|
59
66
|
#
|
60
67
|
def self.active?(feature)
|
61
|
-
|
62
|
-
|
63
|
-
refresh! if @auto_refresh || @perform_initial_refresh
|
64
|
-
|
65
|
-
@active_features.include?(feature)
|
68
|
+
active_features.include?(feature)
|
66
69
|
end
|
67
70
|
|
68
71
|
# Requests if feature is inactive (or unknown)
|
@@ -79,7 +82,7 @@ module Feature
|
|
79
82
|
# @param [Symbol] feature
|
80
83
|
#
|
81
84
|
def self.with(feature)
|
82
|
-
|
85
|
+
raise ArgumentError, "no block given to #{__method__}" unless block_given?
|
83
86
|
|
84
87
|
yield if active?(feature)
|
85
88
|
end
|
@@ -89,7 +92,7 @@ module Feature
|
|
89
92
|
# @param [Symbol] feature
|
90
93
|
#
|
91
94
|
def self.without(feature)
|
92
|
-
|
95
|
+
raise ArgumentError, "no block given to #{__method__}" unless block_given?
|
93
96
|
|
94
97
|
yield if inactive?(feature)
|
95
98
|
end
|
@@ -107,4 +110,16 @@ module Feature
|
|
107
110
|
l2.instance_of?(Proc) ? l2.call : l2
|
108
111
|
end
|
109
112
|
end
|
113
|
+
|
114
|
+
# Return list of active feature flags.
|
115
|
+
#
|
116
|
+
# @return [Array] list of symbols
|
117
|
+
#
|
118
|
+
def self.active_features
|
119
|
+
raise 'missing Repository for obtaining feature lists' unless @repository
|
120
|
+
|
121
|
+
refresh! if @auto_refresh || @perform_initial_refresh || (@next_refresh_after && Time.now > @next_refresh_after)
|
122
|
+
|
123
|
+
@active_features
|
124
|
+
end
|
110
125
|
end
|
@@ -39,7 +39,7 @@ module Feature
|
|
39
39
|
# @param [Sybmol] feature the feature to be checked
|
40
40
|
#
|
41
41
|
def check_feature_is_not_symbol(feature)
|
42
|
-
|
42
|
+
raise ArgumentError, "#{feature} is not a symbol" unless feature.instance_of?(Symbol)
|
43
43
|
end
|
44
44
|
private :check_feature_is_not_symbol
|
45
45
|
|
@@ -49,7 +49,7 @@ module Feature
|
|
49
49
|
# @param [Symbol] feature the feature to be checked
|
50
50
|
#
|
51
51
|
def check_feature_already_in_list(feature)
|
52
|
-
|
52
|
+
raise ArgumentError, "feature :#{feature} already added" if @model.exists?(name: feature.to_s)
|
53
53
|
end
|
54
54
|
private :check_feature_already_in_list
|
55
55
|
end
|
@@ -10,12 +10,15 @@ module Feature
|
|
10
10
|
# the Redis hash that will store all of your feature toggles.
|
11
11
|
#
|
12
12
|
class RedisRepository
|
13
|
+
attr_writer :redis
|
14
|
+
|
13
15
|
# Constructor
|
14
16
|
#
|
15
17
|
# @param redis_key the key of the redis hash where all the toggles will be stored
|
16
18
|
#
|
17
|
-
def initialize(redis_key)
|
19
|
+
def initialize(redis_key, client = nil)
|
18
20
|
@redis_key = redis_key
|
21
|
+
@redis = client unless client.nil?
|
19
22
|
end
|
20
23
|
|
21
24
|
# Returns list of active features
|
@@ -23,7 +26,7 @@ module Feature
|
|
23
26
|
# @return [Array<Symbol>] list of active features
|
24
27
|
#
|
25
28
|
def active_features
|
26
|
-
|
29
|
+
redis.hgetall(@redis_key).select { |_k, v| v.to_s == 'true' }.map { |k, _v| k.to_sym }
|
27
30
|
end
|
28
31
|
|
29
32
|
# Add an active feature to repository
|
@@ -33,7 +36,7 @@ module Feature
|
|
33
36
|
def add_active_feature(feature)
|
34
37
|
check_feature_is_not_symbol(feature)
|
35
38
|
check_feature_already_in_list(feature)
|
36
|
-
|
39
|
+
redis.hset(@redis_key, feature, true)
|
37
40
|
end
|
38
41
|
|
39
42
|
# Checks that given feature is a symbol, raises exception otherwise
|
@@ -41,7 +44,7 @@ module Feature
|
|
41
44
|
# @param [Sybmol] feature the feature to be checked
|
42
45
|
#
|
43
46
|
def check_feature_is_not_symbol(feature)
|
44
|
-
|
47
|
+
raise ArgumentError, "#{feature} is not a symbol" unless feature.instance_of?(Symbol)
|
45
48
|
end
|
46
49
|
private :check_feature_is_not_symbol
|
47
50
|
|
@@ -51,9 +54,18 @@ module Feature
|
|
51
54
|
# @param [Symbol] feature the feature to be checked
|
52
55
|
#
|
53
56
|
def check_feature_already_in_list(feature)
|
54
|
-
|
57
|
+
raise ArgumentError, "feature :#{feature} already added" if redis.hexists(@redis_key, feature)
|
55
58
|
end
|
56
59
|
private :check_feature_already_in_list
|
60
|
+
|
61
|
+
# Returns the currently specified redis client
|
62
|
+
#
|
63
|
+
# @return [Redis] Currently set redis client
|
64
|
+
#
|
65
|
+
def redis
|
66
|
+
@redis ||= Redis.current
|
67
|
+
end
|
68
|
+
private :redis
|
57
69
|
end
|
58
70
|
end
|
59
71
|
end
|
@@ -39,7 +39,7 @@ module Feature
|
|
39
39
|
# @param [Sybmol] feature the feature to be checked
|
40
40
|
#
|
41
41
|
def check_feature_is_not_symbol(feature)
|
42
|
-
|
42
|
+
raise ArgumentError, "#{feature} is not a symbol" unless feature.instance_of?(Symbol)
|
43
43
|
end
|
44
44
|
private :check_feature_is_not_symbol
|
45
45
|
|
@@ -49,7 +49,7 @@ module Feature
|
|
49
49
|
# @param [Symbol] feature the feature to be checked
|
50
50
|
#
|
51
51
|
def check_feature_already_in_list(feature)
|
52
|
-
|
52
|
+
raise ArgumentError, "feature :#{feature} already added" if @active_features.include?(feature)
|
53
53
|
end
|
54
54
|
private :check_feature_already_in_list
|
55
55
|
end
|
@@ -66,7 +66,7 @@ module Feature
|
|
66
66
|
data = data[selector] unless selector.empty?
|
67
67
|
|
68
68
|
if !data.is_a?(Hash) || !data.key?('features')
|
69
|
-
|
69
|
+
raise ArgumentError, 'yaml config does not contain proper config'
|
70
70
|
end
|
71
71
|
|
72
72
|
return [] unless data['features']
|
@@ -84,7 +84,7 @@ module Feature
|
|
84
84
|
def check_valid_feature_data(features)
|
85
85
|
features.values.each do |value|
|
86
86
|
unless [true, false].include?(value)
|
87
|
-
|
87
|
+
raise ArgumentError, "#{value} is not allowed value in config, use true/false"
|
88
88
|
end
|
89
89
|
end
|
90
90
|
end
|
@@ -22,7 +22,7 @@ describe Feature::Repository::ActiveRecordRepository do
|
|
22
22
|
end
|
23
23
|
|
24
24
|
it 'should add an active feature' do
|
25
|
-
expect(@features).to receive(:exists?).with('feature_a').and_return(false)
|
25
|
+
expect(@features).to receive(:exists?).with(name: 'feature_a').and_return(false)
|
26
26
|
expect(@features).to receive(:create!).with(name: 'feature_a', active: true)
|
27
27
|
|
28
28
|
@repository.add_active_feature :feature_a
|
@@ -27,6 +27,12 @@ describe Feature do
|
|
27
27
|
end
|
28
28
|
end.to raise_error('missing Repository for obtaining feature lists')
|
29
29
|
end
|
30
|
+
|
31
|
+
it 'should raise an exception when calling active_features' do
|
32
|
+
expect do
|
33
|
+
Feature.active_features
|
34
|
+
end.to raise_error('missing Repository for obtaining feature lists')
|
35
|
+
end
|
30
36
|
end
|
31
37
|
|
32
38
|
context 'setting Repository' do
|
@@ -75,6 +81,31 @@ describe Feature do
|
|
75
81
|
Feature.active?(:feature_a)
|
76
82
|
end
|
77
83
|
end
|
84
|
+
|
85
|
+
context 'with timeout set to 30 seconds' do
|
86
|
+
before(:each) do
|
87
|
+
Timecop.freeze(Time.now)
|
88
|
+
Feature.set_repository @repository, 30
|
89
|
+
@repository.add_active_feature(:feature_a)
|
90
|
+
Feature.active?(:feature_a)
|
91
|
+
end
|
92
|
+
|
93
|
+
after(:each) do
|
94
|
+
Timecop.return
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'should not update after 10 seconds' do
|
98
|
+
Timecop.freeze(Time.now + 10)
|
99
|
+
expect(@repository).not_to receive(:active_features)
|
100
|
+
Feature.active?(:feature_a)
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'should update after 40 seconds' do
|
104
|
+
Timecop.freeze(Time.now + 40)
|
105
|
+
expect(@repository).to receive(:active_features).and_return(@repository.active_features)
|
106
|
+
Feature.active?(:feature_a)
|
107
|
+
end
|
108
|
+
end
|
78
109
|
end
|
79
110
|
|
80
111
|
context 'refresh features' do
|
@@ -190,5 +221,11 @@ describe Feature do
|
|
190
221
|
end
|
191
222
|
end
|
192
223
|
end
|
224
|
+
|
225
|
+
describe 'active_features' do
|
226
|
+
it 'should return an array of active feature flags' do
|
227
|
+
expect(Feature.active_features).to eq([:feature_active])
|
228
|
+
end
|
229
|
+
end
|
193
230
|
end
|
194
231
|
end
|
@@ -37,4 +37,10 @@ describe Feature::Repository::RedisRepository do
|
|
37
37
|
@repository.add_active_feature :feature_a
|
38
38
|
end.to raise_error(ArgumentError, 'feature :feature_a already added')
|
39
39
|
end
|
40
|
+
|
41
|
+
let(:specified_redis) { double }
|
42
|
+
let(:repo) { RedisRepository.new('application_features', specified_redis) }
|
43
|
+
it 'should allow you to specify the redis instance to use' do
|
44
|
+
expect(repo.send(:redis)).to eq specified_redis
|
45
|
+
end
|
40
46
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: feature
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Markus Gerdes
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-06-11 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description:
|
14
14
|
email: github@mgsnova.de
|
@@ -36,7 +36,6 @@ files:
|
|
36
36
|
- spec/feature/testing_spec.rb
|
37
37
|
- spec/feature/yaml_repository_spec.rb
|
38
38
|
- spec/integration/rails/gemfiles/rails4.gemfile
|
39
|
-
- spec/integration/rails/gemfiles/rails4.gemfile.lock
|
40
39
|
- spec/integration/rails/test-against-several-rails-versions.sh
|
41
40
|
- spec/integration/rails/test-against-specific-rails-version.sh
|
42
41
|
- spec/spec_helper.rb
|
@@ -60,7 +59,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
60
59
|
version: '0'
|
61
60
|
requirements: []
|
62
61
|
rubyforge_project:
|
63
|
-
rubygems_version: 2.
|
62
|
+
rubygems_version: 2.5.1
|
64
63
|
signing_key:
|
65
64
|
specification_version: 4
|
66
65
|
summary: Feature Toggle library for ruby
|
@@ -1,85 +0,0 @@
|
|
1
|
-
GEM
|
2
|
-
remote: https://rubygems.org/
|
3
|
-
specs:
|
4
|
-
actionmailer (4.1.4)
|
5
|
-
actionpack (= 4.1.4)
|
6
|
-
actionview (= 4.1.4)
|
7
|
-
mail (~> 2.5.4)
|
8
|
-
actionpack (4.1.4)
|
9
|
-
actionview (= 4.1.4)
|
10
|
-
activesupport (= 4.1.4)
|
11
|
-
rack (~> 1.5.2)
|
12
|
-
rack-test (~> 0.6.2)
|
13
|
-
actionview (4.1.4)
|
14
|
-
activesupport (= 4.1.4)
|
15
|
-
builder (~> 3.1)
|
16
|
-
erubis (~> 2.7.0)
|
17
|
-
activemodel (4.1.4)
|
18
|
-
activesupport (= 4.1.4)
|
19
|
-
builder (~> 3.1)
|
20
|
-
activerecord (4.1.4)
|
21
|
-
activemodel (= 4.1.4)
|
22
|
-
activesupport (= 4.1.4)
|
23
|
-
arel (~> 5.0.0)
|
24
|
-
activesupport (4.1.4)
|
25
|
-
i18n (~> 0.6, >= 0.6.9)
|
26
|
-
json (~> 1.7, >= 1.7.7)
|
27
|
-
minitest (~> 5.1)
|
28
|
-
thread_safe (~> 0.1)
|
29
|
-
tzinfo (~> 1.1)
|
30
|
-
arel (5.0.1.20140414130214)
|
31
|
-
builder (3.2.2)
|
32
|
-
erubis (2.7.0)
|
33
|
-
hike (1.2.3)
|
34
|
-
i18n (0.6.11)
|
35
|
-
json (1.8.1)
|
36
|
-
mail (2.5.4)
|
37
|
-
mime-types (~> 1.16)
|
38
|
-
treetop (~> 1.4.8)
|
39
|
-
mime-types (1.25.1)
|
40
|
-
minitest (5.4.0)
|
41
|
-
multi_json (1.10.1)
|
42
|
-
polyglot (0.3.5)
|
43
|
-
rack (1.5.2)
|
44
|
-
rack-test (0.6.2)
|
45
|
-
rack (>= 1.0)
|
46
|
-
rails (4.1.4)
|
47
|
-
actionmailer (= 4.1.4)
|
48
|
-
actionpack (= 4.1.4)
|
49
|
-
actionview (= 4.1.4)
|
50
|
-
activemodel (= 4.1.4)
|
51
|
-
activerecord (= 4.1.4)
|
52
|
-
activesupport (= 4.1.4)
|
53
|
-
bundler (>= 1.3.0, < 2.0)
|
54
|
-
railties (= 4.1.4)
|
55
|
-
sprockets-rails (~> 2.0)
|
56
|
-
railties (4.1.4)
|
57
|
-
actionpack (= 4.1.4)
|
58
|
-
activesupport (= 4.1.4)
|
59
|
-
rake (>= 0.8.7)
|
60
|
-
thor (>= 0.18.1, < 2.0)
|
61
|
-
rake (10.3.2)
|
62
|
-
sprockets (2.12.1)
|
63
|
-
hike (~> 1.2)
|
64
|
-
multi_json (~> 1.0)
|
65
|
-
rack (~> 1.0)
|
66
|
-
tilt (~> 1.1, != 1.3.0)
|
67
|
-
sprockets-rails (2.1.3)
|
68
|
-
actionpack (>= 3.0)
|
69
|
-
activesupport (>= 3.0)
|
70
|
-
sprockets (~> 2.8)
|
71
|
-
thor (0.19.1)
|
72
|
-
thread_safe (0.3.4)
|
73
|
-
tilt (1.4.1)
|
74
|
-
treetop (1.4.15)
|
75
|
-
polyglot
|
76
|
-
polyglot (>= 0.3.1)
|
77
|
-
tzinfo (1.2.1)
|
78
|
-
thread_safe (~> 0.1)
|
79
|
-
|
80
|
-
PLATFORMS
|
81
|
-
ruby
|
82
|
-
|
83
|
-
DEPENDENCIES
|
84
|
-
rails (~> 4.1.4)
|
85
|
-
railties
|