abstract_feature_branch 1.3.0 → 1.3.1

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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/README.md +10 -7
  4. data/VERSION +1 -1
  5. data/abstract_feature_branch.gemspec +6 -40
  6. data/lib/abstract_feature_branch/configuration.rb +8 -2
  7. data/lib/abstract_feature_branch/redis/connection_pool_to_redis_adapter.rb +34 -0
  8. data/lib/abstract_feature_branch.rb +3 -1
  9. data/lib/generators/templates/config/initializers/abstract_feature_branch.rb +4 -1
  10. metadata +5 -39
  11. data/.coveralls.yml +0 -1
  12. data/.travis.yml +0 -30
  13. data/RELEASE_NOTES.md +0 -55
  14. data/TODO.md +0 -3
  15. data/config/features/admin.local.yml +0 -15
  16. data/config/features/admin.yml +0 -17
  17. data/config/features/internal/wiki.local.yml +0 -15
  18. data/config/features/internal/wiki.yml +0 -17
  19. data/config/features/public.local.yml +0 -15
  20. data/config/features/public.yml +0 -17
  21. data/img/BigAstronaut-Logo.png +0 -0
  22. data/img/EarlyShares-Logo.svg +0 -22
  23. data/img/Factor75-Logo.svg +0 -54
  24. data/ruby187.Gemfile +0 -13
  25. data/spec/abstract_feature_branch/file_beautifier_spec.rb +0 -384
  26. data/spec/ext/feature_branch__feature_branch_per_user_spec.rb +0 -122
  27. data/spec/ext/feature_branch__feature_branch_spec.rb +0 -148
  28. data/spec/ext/feature_branch__feature_enabled_spec.rb +0 -274
  29. data/spec/fixtures/application_development_config/config/features.reference.yml +0 -21
  30. data/spec/fixtures/application_no_config/no_config +0 -1
  31. data/spec/fixtures/application_rails_config/config/features.local.yml +0 -16
  32. data/spec/fixtures/application_rails_config/config/features.yml +0 -20
  33. data/spec/fixtures/application_ugly_config_reference/config/another_application_configuration.yml +0 -31
  34. data/spec/fixtures/application_ugly_config_reference/config/database.yml +0 -17
  35. data/spec/fixtures/application_ugly_config_reference/config/features/admin.local.yml +0 -44
  36. data/spec/fixtures/application_ugly_config_reference/config/features/admin.yml +0 -44
  37. data/spec/fixtures/application_ugly_config_reference/config/features/empty.local.yml +0 -0
  38. data/spec/fixtures/application_ugly_config_reference/config/features/feature_empty_config.local.yml +0 -13
  39. data/spec/fixtures/application_ugly_config_reference/config/features/including_comments.local.yml +0 -52
  40. data/spec/fixtures/application_ugly_config_reference/config/features/internal/wiki.local.yml +0 -44
  41. data/spec/fixtures/application_ugly_config_reference/config/features/internal/wiki.yml +0 -44
  42. data/spec/fixtures/application_ugly_config_reference/config/features/public.local.yml +0 -44
  43. data/spec/fixtures/application_ugly_config_reference/config/features/public.yml +0 -44
  44. data/spec/fixtures/application_ugly_config_reference/config/features.local.yml +0 -44
  45. data/spec/fixtures/application_ugly_config_reference/config/features.yml +0 -49
data/ruby187.Gemfile DELETED
@@ -1,13 +0,0 @@
1
- source 'http://rubygems.org'
2
-
3
- gem 'deep_merge', '1.0.0', :require => false #avoid loading to use only if Rails is unavailable
4
- gem 'redis', '~> 3.0.0', :require => false
5
-
6
- group :development do
7
- gem 'jeweler', '1.8.8'
8
- gem 'rspec', '2.14.1'
9
-
10
- #Ruby 1.8.7 compatible versions
11
- gem "nokogiri", "~> 1.5.0"
12
- gem "highline", "~> 1.6.21"
13
- end
@@ -1,384 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe AbstractFeatureBranch::FileBeautifier do
4
- describe '#process' do
5
- before do
6
- @ugly_config_application_root = File.expand_path(File.join(__FILE__, '..', '..', 'fixtures', 'application_ugly_config'))
7
- @ugly_config_application_reference_root = File.expand_path(File.join(__FILE__, '..', '..', 'fixtures', 'application_ugly_config_reference'))
8
- FileUtils.rm_rf( @ugly_config_application_root)
9
- FileUtils.cp_r(@ugly_config_application_reference_root, @ugly_config_application_root)
10
- end
11
- after do
12
- FileUtils.rm_rf( @ugly_config_application_root)
13
- end
14
-
15
- context "a file is specified" do
16
- it 'gets rid of extra empty lines' do
17
- feature_file_path = File.expand_path(File.join(__FILE__, '..', '..', 'fixtures', 'application_ugly_config', 'config', 'features.yml'))
18
- AbstractFeatureBranch::FileBeautifier.process(feature_file_path)
19
- File.open(feature_file_path, 'r') do |file|
20
- file.readlines.join.should == <<-EXPECTED_FILE_CONTENT
21
- defaults: &defaults
22
- FEATURE1: true
23
- Feature2: true
24
- feature3: false
25
- feature4: true
26
- feature4a: true
27
-
28
- development:
29
- <<: *defaults
30
- FEATURE1: true
31
- Feature2: true
32
- feature3: false
33
- feature4: true
34
- feature4a: true
35
-
36
- test:
37
- <<: *defaults
38
- FEATURE1: true
39
- Feature2: true
40
- feature3: false
41
- feature4: true
42
- feature4a: true
43
-
44
- staging:
45
- <<: *defaults
46
- FEATURE1: true
47
- Feature2: true
48
- feature3: false
49
- feature4: true
50
- feature4a: true
51
-
52
- production:
53
- <<: *defaults
54
- FEATURE1: true
55
- Feature2: true
56
- feature3: false
57
- feature4: true
58
- feature4a: true
59
-
60
- EXPECTED_FILE_CONTENT
61
- end
62
- end
63
-
64
- it 'sorts features by name under each section (e.g. environment)' do
65
- local_feature_file_path = File.expand_path(File.join(__FILE__, '..', '..', 'fixtures', 'application_ugly_config', 'config', 'features.local.yml'))
66
- AbstractFeatureBranch::FileBeautifier.process(local_feature_file_path)
67
- File.open(local_feature_file_path, 'r') do |file|
68
- file.readlines.join.should == <<-EXPECTED_FILE_CONTENT
69
- defaults: &defaults
70
- FEATURE1: true
71
- Feature2: true
72
- feature3: false
73
- feature4: true
74
- feature4a: true
75
-
76
- development:
77
- <<: *defaults
78
- FEATURE1: true
79
- Feature2: true
80
- feature3: false
81
- feature4: true
82
- feature4a: true
83
-
84
- test:
85
- <<: *defaults
86
- FEATURE1: true
87
- Feature2: true
88
- feature3: false
89
- feature4: true
90
- feature4a: true
91
-
92
- staging:
93
- <<: *defaults
94
- FEATURE1: true
95
- Feature2: true
96
- feature3: false
97
- feature4: true
98
- feature4a: true
99
-
100
- production:
101
- <<: *defaults
102
- FEATURE1: true
103
- Feature2: true
104
- feature3: false
105
- feature4: true
106
- feature4a: true
107
-
108
- EXPECTED_FILE_CONTENT
109
- end
110
- end
111
-
112
- it 'handles comments by ignoring comments on top and deleting comments in the middle' do
113
- feature_file_path = File.expand_path(File.join(__FILE__, '..', '..', 'fixtures', 'application_ugly_config', 'config', 'features', 'including_comments.local.yml'))
114
- AbstractFeatureBranch::FileBeautifier.process(feature_file_path)
115
- File.open(feature_file_path, 'r') do |file|
116
- file.readlines.join.should == <<-EXPECTED_FILE_CONTENT
117
- # This file allows you to override any feature configuration locally without it being committed to git
118
- # It is recommended to use this file only for temporary overrides. Once done, make final change in main .yml
119
- defaults: &defaults
120
- FEATURE1: true
121
- Feature2: true
122
- feature3: false
123
- feature4: true
124
- feature4a: true
125
-
126
- development:
127
- <<: *defaults
128
- FEATURE1: true
129
- Feature2: true
130
- feature3: false
131
- feature4: true
132
- feature4a: true
133
-
134
- test:
135
- <<: *defaults
136
- FEATURE1: true
137
- Feature2: true
138
- feature3: false
139
- feature4: true
140
- feature4a: true
141
-
142
- staging:
143
- <<: *defaults
144
- FEATURE1: true
145
- Feature2: true
146
- feature3: false
147
- feature4: true
148
- feature4a: true
149
-
150
- production:
151
- <<: *defaults
152
- FEATURE1: true
153
- Feature2: true
154
- feature3: false
155
- feature4: true
156
- feature4a: true
157
-
158
- EXPECTED_FILE_CONTENT
159
- end
160
- end
161
-
162
- it 'processes a feature empty config file' do
163
- feature_file_path = File.expand_path(File.join(__FILE__, '..', '..', 'fixtures', 'application_ugly_config', 'config', 'features', 'feature_empty_config.local.yml'))
164
- AbstractFeatureBranch::FileBeautifier.process(feature_file_path)
165
- File.open(feature_file_path, 'r') do |file|
166
- file.readlines.join.should == <<-EXPECTED_FILE_CONTENT
167
- defaults: &defaults
168
-
169
-
170
- development:
171
- <<: *defaults
172
-
173
- test:
174
- <<: *defaults
175
-
176
- staging:
177
- <<: *defaults
178
-
179
- production:
180
- <<: *defaults
181
-
182
- EXPECTED_FILE_CONTENT
183
- end
184
- end
185
-
186
- it 'processes an empty file without change or exceptions' do
187
- feature_file_path = File.expand_path(File.join(__FILE__, '..', '..', 'fixtures', 'application_ugly_config', 'config', 'features', 'empty.local.yml'))
188
- AbstractFeatureBranch::FileBeautifier.process(feature_file_path)
189
- File.open(feature_file_path, 'r') do |file|
190
- file.readlines.join.should be_empty
191
- end
192
- end
193
-
194
- end
195
-
196
- context "a directory is specified" do
197
- it 'beautifies all YAML files under specified directory recursively' do
198
- feature_directory_path = File.expand_path(File.join(__FILE__, '..', '..', 'fixtures', 'application_ugly_config', 'config', 'features'))
199
- AbstractFeatureBranch::FileBeautifier.process(feature_directory_path)
200
-
201
- ['public.yml', 'public.local.yml', 'admin.yml', 'admin.local.yml', 'internal/wiki.yml', 'internal/wiki.local.yml'].each do |file_path_suffix|
202
- file_path = File.join(feature_directory_path, file_path_suffix)
203
- File.open(file_path, 'r') do |file|
204
- file.readlines.join.should == <<-EXPECTED_FILE_CONTENT
205
- defaults: &defaults
206
- FEATURE1: true
207
- Feature2: true
208
- feature3: false
209
- feature4: true
210
- feature4a: true
211
-
212
- development:
213
- <<: *defaults
214
- FEATURE1: true
215
- Feature2: true
216
- feature3: false
217
- feature4: true
218
- feature4a: true
219
-
220
- test:
221
- <<: *defaults
222
- FEATURE1: true
223
- Feature2: true
224
- feature3: false
225
- feature4: true
226
- feature4a: true
227
-
228
- staging:
229
- <<: *defaults
230
- FEATURE1: true
231
- Feature2: true
232
- feature3: false
233
- feature4: true
234
- feature4a: true
235
-
236
- production:
237
- <<: *defaults
238
- FEATURE1: true
239
- Feature2: true
240
- feature3: false
241
- feature4: true
242
- feature4a: true
243
-
244
- EXPECTED_FILE_CONTENT
245
- end
246
- end
247
- end
248
-
249
- context "no file or directory is specified (process all feature files)" do
250
- after do
251
- AbstractFeatureBranch.initialize_application_root
252
- AbstractFeatureBranch.load_application_features
253
- end
254
- it 'beautifies all feature files in the application' do
255
- AbstractFeatureBranch.application_root = File.expand_path(File.join(__FILE__, '..', '..', 'fixtures', 'application_ugly_config'))
256
- AbstractFeatureBranch.load_application_features
257
- AbstractFeatureBranch::FileBeautifier.process
258
-
259
- [
260
- 'features.yml',
261
- 'features.local.yml',
262
- 'features/public.yml',
263
- 'features/public.local.yml',
264
- 'features/admin.yml',
265
- 'features/admin.local.yml',
266
- 'features/internal/wiki.yml',
267
- 'features/internal/wiki.local.yml'
268
- ].each do |file_path_suffix|
269
- file_path = File.join(AbstractFeatureBranch.application_root, 'config', file_path_suffix)
270
- File.open(file_path, 'r') do |file|
271
- file.readlines.join.should == <<-EXPECTED_FILE_CONTENT
272
- defaults: &defaults
273
- FEATURE1: true
274
- Feature2: true
275
- feature3: false
276
- feature4: true
277
- feature4a: true
278
-
279
- development:
280
- <<: *defaults
281
- FEATURE1: true
282
- Feature2: true
283
- feature3: false
284
- feature4: true
285
- feature4a: true
286
-
287
- test:
288
- <<: *defaults
289
- FEATURE1: true
290
- Feature2: true
291
- feature3: false
292
- feature4: true
293
- feature4a: true
294
-
295
- staging:
296
- <<: *defaults
297
- FEATURE1: true
298
- Feature2: true
299
- feature3: false
300
- feature4: true
301
- feature4a: true
302
-
303
- production:
304
- <<: *defaults
305
- FEATURE1: true
306
- Feature2: true
307
- feature3: false
308
- feature4: true
309
- feature4a: true
310
-
311
- EXPECTED_FILE_CONTENT
312
- end
313
- end
314
- end
315
-
316
- it 'does not beautify non-feature files in the application' do
317
- AbstractFeatureBranch.application_root = File.expand_path(File.join(__FILE__, '..', '..', 'fixtures', 'application_ugly_config'))
318
- AbstractFeatureBranch.load_application_features
319
- AbstractFeatureBranch::FileBeautifier.process
320
-
321
- file_path = File.join(AbstractFeatureBranch.application_root, 'config', 'another_application_configuration.yml')
322
- File.open(file_path, 'r') do |file|
323
- file.readlines.join.should == <<-ANOTHER_APPLICATION_CONFIGURATION_CONTENT
324
- common: &default_settings
325
- license_key: <%= ENV["LICENSE_KEY"] %>
326
- app_name: <%= ENV["APP_NAME"] %>
327
- monitor_mode: true
328
- developer_mode: false
329
- log_level: info
330
-
331
- browser_monitoring:
332
- auto_instrument: true
333
-
334
- audit_log:
335
- enabled: false
336
-
337
-
338
- development:
339
- <<: *default_settings
340
- monitor_mode: false
341
- developer_mode: true
342
-
343
- test:
344
- <<: *default_settings
345
- monitor_mode: false
346
-
347
- production:
348
- <<: *default_settings
349
- monitor_mode: true
350
-
351
- staging:
352
- <<: *default_settings
353
- monitor_mode: true
354
- app_name: <%= ENV["APP_NAME"] %> (Staging)
355
- ANOTHER_APPLICATION_CONFIGURATION_CONTENT
356
- end
357
-
358
- file_path = File.join(AbstractFeatureBranch.application_root, 'config', 'database.yml')
359
- File.open(file_path, 'r') do |file|
360
- file.readlines.join.should == <<-DATABASE_CONFIGURATION_CONTENT
361
- development:
362
- adapter: sqlite3
363
- database: db/development.sqlite3
364
- pool: 5
365
- timeout: 5000
366
-
367
- test:
368
- adapter: sqlite3
369
- database: db/test.sqlite3
370
- pool: 5
371
- timeout: 5000
372
-
373
- production:
374
- adapter: sqlite3
375
- database: db/production.sqlite3
376
- pool: 5
377
- timeout: 5000
378
- DATABASE_CONFIGURATION_CONTENT
379
- end
380
- end
381
- end
382
- end
383
- end
384
- end
@@ -1,122 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe 'feature_branch object extensions' do
4
- before do
5
- @app_env_backup = AbstractFeatureBranch.application_environment
6
- @app_root_backup = AbstractFeatureBranch.application_root
7
- AbstractFeatureBranch.logger.warn 'Environment variable ABSTRACT_FEATURE_BRANCH_FEATURE1 already set, potentially conflicting with another test' if ENV.keys.include?('ABSTRACT_FEATURE_BRANCH_FEATURE1')
8
- AbstractFeatureBranch.logger.warn 'Environment variable Abstract_Feature_Branch_Feature2 already set, potentially conflicting with another test' if ENV.keys.include?('Abstract_Feature_Branch_Feature2')
9
- AbstractFeatureBranch.logger.warn 'Environment variable abstract_feature_branch_feature3 already set, potentially conflicting with another test' if ENV.keys.include?('abstract_feature_branch_feature3')
10
- begin
11
- AbstractFeatureBranch.user_features_storage.flushall
12
- rescue => e
13
- #noop
14
- end
15
- end
16
- after do
17
- ENV.delete('ABSTRACT_FEATURE_BRANCH_FEATURE1')
18
- ENV.delete('Abstract_Feature_Branch_Feature2')
19
- ENV.delete('abstract_feature_branch_feature3')
20
- AbstractFeatureBranch.application_root = @app_root_backup
21
- AbstractFeatureBranch.application_environment = @app_env_backup
22
- AbstractFeatureBranch.unload_application_features
23
- begin
24
- AbstractFeatureBranch.user_features_storage.flushall
25
- rescue => e
26
- #noop
27
- end
28
- AbstractFeatureBranch.user_features_storage.keys.each do |key|
29
- AbstractFeatureBranch.user_features_storage.del(key)
30
- end
31
- end
32
- describe '#feature_branch' do
33
- context 'per user' do
34
- it 'feature branches correctly after storing feature configuration per user in a separate process (ensuring persistence)' do
35
- user_id = 'email1@example.com'
36
- ruby_code = <<-RUBY_CODE
37
- $:.unshift('.')
38
- require 'redis'
39
- require 'lib/abstract_feature_branch'
40
- AbstractFeatureBranch.initialize_user_features_storage
41
- AbstractFeatureBranch.toggle_features_for_user('#{user_id}', :feature1 => false, :feature3 => true, :feature6 => true, :feature7 => false)
42
- RUBY_CODE
43
- system "ruby -e \"#{ruby_code}\""
44
- features_enabled = []
45
- feature_branch :feature1, user_id do
46
- features_enabled << :feature1
47
- end
48
- feature_branch :feature3, user_id do
49
- features_enabled << :feature3
50
- end
51
- feature_branch :feature6, user_id do
52
- features_enabled << :feature6
53
- end
54
- feature_branch :feature6, 'otheruser@example.com' do
55
- features_enabled << :feature6_otheruser
56
- end
57
- feature_branch :feature6 do
58
- features_enabled << :feature6_nouserspecified
59
- end
60
- feature_branch :feature7, user_id do
61
- features_enabled << :feature7
62
- end
63
- features_enabled.should include(:feature1) #remains like features.yml
64
- features_enabled.should_not include(:feature3) #remains like features.yml
65
- features_enabled.should include(:feature6) #per user honored as true
66
- features_enabled.should_not include(:feature6_otheruser) #per user honored as false
67
- features_enabled.should_not include(:feature6_nouserspecified) #per user requires user id or it returns false
68
- features_enabled.should_not include(:feature7) #per user honored as false
69
- end
70
- it 'update feature branching (disabling some features) after having stored feature configuration per user in a separate process (ensuring persistence)' do
71
- user_id = 'email1@example.com'
72
- ruby_code = <<-RUBY_CODE
73
- $:.unshift('.')
74
- require 'redis'
75
- require 'lib/abstract_feature_branch'
76
- AbstractFeatureBranch.initialize_user_features_storage
77
- AbstractFeatureBranch.toggle_features_for_user('#{user_id}', :feature6 => true, :feature7 => false)
78
- AbstractFeatureBranch.toggle_features_for_user('#{user_id}', :feature6 => false, :feature7 => true)
79
- RUBY_CODE
80
- system "ruby -e \"#{ruby_code}\""
81
- features_enabled = []
82
- feature_branch :feature6, user_id do
83
- features_enabled << :feature6
84
- end
85
- feature_branch :feature7, user_id do
86
- features_enabled << :feature7
87
- end
88
- features_enabled.should_not include(:feature6)
89
- features_enabled.should include(:feature7)
90
- end
91
-
92
- end
93
- end
94
- describe 'self#feature_branch' do
95
- after do
96
- Object.send(:remove_const, :TestObject)
97
- end
98
- # No need to retest all instance test cases, just a spot check due to implementation reuse
99
- it 'feature branches instance level behavior (case-insensitive feature names)' do
100
- class TestObject
101
- def self.features_enabled
102
- @features_enabled ||= []
103
- end
104
- def self.hit_me
105
- feature_branch :feature1 do
106
- self.features_enabled << :feature1
107
- end
108
- feature_branch :feature2 do
109
- self.features_enabled << :feature2
110
- end
111
- feature_branch :feature3 do
112
- self.features_enabled << :feature3
113
- end
114
- end
115
- end
116
- TestObject.hit_me
117
- TestObject.features_enabled.should include(:feature1)
118
- TestObject.features_enabled.should include(:feature2)
119
- TestObject.features_enabled.should_not include(:feature3)
120
- end
121
- end
122
- end
@@ -1,148 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe 'feature_branch object extensions' do
4
- before do
5
- @app_env_backup = AbstractFeatureBranch.application_environment
6
- @app_root_backup = AbstractFeatureBranch.application_root
7
- AbstractFeatureBranch.logger.warn 'Environment variable ABSTRACT_FEATURE_BRANCH_FEATURE1 already set, potentially conflicting with another test' if ENV.keys.include?('ABSTRACT_FEATURE_BRANCH_FEATURE1')
8
- AbstractFeatureBranch.logger.warn 'Environment variable Abstract_Feature_Branch_Feature2 already set, potentially conflicting with another test' if ENV.keys.include?('Abstract_Feature_Branch_Feature2')
9
- AbstractFeatureBranch.logger.warn 'Environment variable abstract_feature_branch_feature3 already set, potentially conflicting with another test' if ENV.keys.include?('abstract_feature_branch_feature3')
10
- end
11
- after do
12
- ENV.delete('ABSTRACT_FEATURE_BRANCH_FEATURE1')
13
- ENV.delete('Abstract_Feature_Branch_Feature2')
14
- ENV.delete('abstract_feature_branch_feature3')
15
- AbstractFeatureBranch.application_root = @app_root_backup
16
- AbstractFeatureBranch.application_environment = @app_env_backup
17
- AbstractFeatureBranch.unload_application_features
18
- AbstractFeatureBranch.user_features_storage.keys.each do |key|
19
- AbstractFeatureBranch.user_features_storage.del(key)
20
- end
21
- end
22
- describe '#feature_branch' do
23
- context 'class level behavior (case-insensitive string or symbol feature names)' do
24
- {
25
- 'Feature1' => true,
26
- :FEATURE2 => true,
27
- :feature3 => false,
28
- :admin_feature1 => true,
29
- :admin_feature2 => false,
30
- :public_feature1 => true,
31
- :public_feature2 => false,
32
- :wiki_feature1 => true,
33
- :wiki_feature2 => false,
34
- }.each do |feature_name, expected_branch_run|
35
- it "feature branches correctly for feature #{feature_name} with expected branch run #{expected_branch_run}" do
36
- feature_branch_run = false
37
- feature_branch feature_name do
38
- feature_branch_run = true
39
- end
40
- feature_branch_run.should == expected_branch_run
41
- end
42
- end
43
- end
44
- it 'returns nil and does not execute block for an invalid feature name' do
45
- return_value = feature_branch :invalid_feature_that_does_not_exist do
46
- fail 'feature branch block must not execute, but did.'
47
- end
48
- return_value.should be_nil
49
- end
50
- it 'allows environment variables (case-insensitive booleans) to override configuration file' do
51
- ENV['ABSTRACT_FEATURE_BRANCH_FEATURE1'] = 'FALSE'
52
- ENV['Abstract_Feature_Branch_Feature2'] = 'False'
53
- ENV['abstract_feature_branch_feature3'] = 'true'
54
- AbstractFeatureBranch.load_application_features
55
- features_enabled = []
56
- feature_branch :feature1 do
57
- features_enabled << :feature1
58
- end
59
- feature_branch :feature2 do
60
- features_enabled << :feature2
61
- end
62
- feature_branch :feature3 do
63
- features_enabled << :feature3
64
- end
65
- features_enabled.should_not include(:feature1)
66
- features_enabled.should_not include(:feature2)
67
- features_enabled.should include(:feature3)
68
- end
69
- it 'allows redis variables (case-insensitive booleans) to override configuration file' do
70
- AbstractFeatureBranch.unload_application_features
71
- AbstractFeatureBranch.user_features_storage.hset('abstract_feature_branch', 'FEATURE1', 'FALSE')
72
- AbstractFeatureBranch.user_features_storage.hset('abstract_feature_branch', 'Feature2', 'False')
73
- AbstractFeatureBranch.user_features_storage.hset('abstract_feature_branch', 'feature3', 'true')
74
- AbstractFeatureBranch.load_application_features
75
- features_enabled = []
76
- feature_branch :feature1 do
77
- features_enabled << :feature1
78
- end
79
- feature_branch :feature2 do
80
- features_enabled << :feature2
81
- end
82
- feature_branch :feature3 do
83
- features_enabled << :feature3
84
- end
85
- features_enabled.should_not include(:feature1)
86
- features_enabled.should_not include(:feature2)
87
- features_enabled.should include(:feature3)
88
- end
89
- it 'allows local configuration file to override main configuration file' do
90
- features_enabled = []
91
- feature_branch :feature4 do
92
- features_enabled << :feature4
93
- end
94
- feature_branch :feature5 do
95
- features_enabled << :feature5
96
- end
97
- feature_branch :admin_feature3 do
98
- features_enabled << :admin_feature3
99
- end
100
- feature_branch :public_feature3 do
101
- features_enabled << :public_feature3
102
- end
103
- feature_branch :wiki_feature3 do
104
- features_enabled << :wiki_feature3
105
- end
106
- features_enabled.should_not include(:feature4)
107
- features_enabled.should include(:feature5)
108
- features_enabled.should include(:admin_feature3)
109
- features_enabled.should include(:public_feature3)
110
- features_enabled.should include(:wiki_feature3)
111
- end
112
- it 'works with an application that has no configuration files' do
113
- AbstractFeatureBranch.application_root = File.join(__FILE__, '..', '..', 'fixtures', 'application_no_config')
114
- AbstractFeatureBranch.load_application_features
115
- feature_branch :feature1 do
116
- fail 'feature branch block must not execute, but did.'
117
- end
118
- end
119
- end
120
- describe 'self#feature_branch' do
121
- after do
122
- Object.send(:remove_const, :TestObject)
123
- end
124
- # No need to retest all instance test cases, just a spot check due to implementation reuse
125
- it 'feature branches instance level behavior (case-insensitive feature names)' do
126
- class TestObject
127
- def self.features_enabled
128
- @features_enabled ||= []
129
- end
130
- def self.hit_me
131
- feature_branch :feature1 do
132
- self.features_enabled << :feature1
133
- end
134
- feature_branch :feature2 do
135
- self.features_enabled << :feature2
136
- end
137
- feature_branch :feature3 do
138
- self.features_enabled << :feature3
139
- end
140
- end
141
- end
142
- TestObject.hit_me
143
- TestObject.features_enabled.should include(:feature1)
144
- TestObject.features_enabled.should include(:feature2)
145
- TestObject.features_enabled.should_not include(:feature3)
146
- end
147
- end
148
- end