abstract_feature_branch 1.2.2 → 1.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +15 -0
  3. data/LICENSE.txt +1 -1
  4. data/README.md +74 -43
  5. data/VERSION +1 -1
  6. data/abstract_feature_branch.gemspec +31 -56
  7. data/lib/abstract_feature_branch/configuration.rb +22 -7
  8. data/lib/abstract_feature_branch/redis/connection_pool_to_redis_adapter.rb +34 -0
  9. data/lib/abstract_feature_branch.rb +75 -7
  10. data/lib/ext/feature_branch.rb +4 -3
  11. data/lib/generators/templates/config/initializers/abstract_feature_branch.rb +7 -5
  12. metadata +76 -55
  13. data/.coveralls.yml +0 -1
  14. data/.travis.yml +0 -40
  15. data/RELEASE_NOTES.md +0 -55
  16. data/config/features/admin.local.yml +0 -15
  17. data/config/features/admin.yml +0 -17
  18. data/config/features/internal/wiki.local.yml +0 -15
  19. data/config/features/internal/wiki.yml +0 -17
  20. data/config/features/public.local.yml +0 -15
  21. data/config/features/public.yml +0 -17
  22. data/ruby187.Gemfile +0 -12
  23. data/ruby187.Gemfile.lock +0 -63
  24. data/spec/abstract_feature_branch/file_beautifier_spec.rb +0 -384
  25. data/spec/ext/feature_branch__feature_branch_per_user_spec.rb +0 -113
  26. data/spec/ext/feature_branch__feature_branch_spec.rb +0 -125
  27. data/spec/ext/feature_branch__feature_enabled_spec.rb +0 -260
  28. data/spec/fixtures/application_development_config/config/features.reference.yml +0 -21
  29. data/spec/fixtures/application_no_config/no_config +0 -1
  30. data/spec/fixtures/application_rails_config/config/features.local.yml +0 -16
  31. data/spec/fixtures/application_rails_config/config/features.yml +0 -20
  32. data/spec/fixtures/application_ugly_config_reference/config/another_application_configuration.yml +0 -31
  33. data/spec/fixtures/application_ugly_config_reference/config/database.yml +0 -17
  34. data/spec/fixtures/application_ugly_config_reference/config/features/admin.local.yml +0 -44
  35. data/spec/fixtures/application_ugly_config_reference/config/features/admin.yml +0 -44
  36. data/spec/fixtures/application_ugly_config_reference/config/features/empty.local.yml +0 -0
  37. data/spec/fixtures/application_ugly_config_reference/config/features/feature_empty_config.local.yml +0 -13
  38. data/spec/fixtures/application_ugly_config_reference/config/features/including_comments.local.yml +0 -52
  39. data/spec/fixtures/application_ugly_config_reference/config/features/internal/wiki.local.yml +0 -44
  40. data/spec/fixtures/application_ugly_config_reference/config/features/internal/wiki.yml +0 -44
  41. data/spec/fixtures/application_ugly_config_reference/config/features/public.local.yml +0 -44
  42. data/spec/fixtures/application_ugly_config_reference/config/features/public.yml +0 -44
  43. data/spec/fixtures/application_ugly_config_reference/config/features.local.yml +0 -44
  44. data/spec/fixtures/application_ugly_config_reference/config/features.yml +0 -49
@@ -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,113 +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
- end
29
- describe '#feature_branch' do
30
- context 'per user' do
31
- it 'feature branches correctly after storing feature configuration per user in a separate process (ensuring persistence)' do
32
- user_id = 'email1@example.com'
33
- Process.fork do
34
- AbstractFeatureBranch.initialize_user_features_storage
35
- AbstractFeatureBranch.toggle_features_for_user(user_id, :feature1 => false, :feature3 => true, :feature6 => true, :feature7 => false)
36
- end
37
- Process.wait
38
- features_enabled = []
39
- feature_branch :feature1, user_id do
40
- features_enabled << :feature1
41
- end
42
- feature_branch :feature3, user_id do
43
- features_enabled << :feature3
44
- end
45
- feature_branch :feature6, user_id do
46
- features_enabled << :feature6
47
- end
48
- feature_branch :feature6, 'otheruser@example.com' do
49
- features_enabled << :feature6_otheruser
50
- end
51
- feature_branch :feature6 do
52
- features_enabled << :feature6_nouserspecified
53
- end
54
- feature_branch :feature7, user_id do
55
- features_enabled << :feature7
56
- end
57
- features_enabled.should include(:feature1) #remains like features.yml
58
- features_enabled.should_not include(:feature3) #remains like features.yml
59
- features_enabled.should include(:feature6) #per user honored as true
60
- features_enabled.should_not include(:feature6_otheruser) #per user honored as false
61
- features_enabled.should_not include(:feature6_nouserspecified) #per user requires user id or it returns false
62
- features_enabled.should_not include(:feature7) #per user honored as false
63
- end
64
- it 'update feature branching (disabling some features) after having stored feature configuration per user in a separate process (ensuring persistence)' do
65
- user_id = 'email1@example.com'
66
- Process.fork do
67
- AbstractFeatureBranch.initialize_user_features_storage
68
- AbstractFeatureBranch.toggle_features_for_user(user_id, :feature6 => true, :feature7 => false)
69
- AbstractFeatureBranch.toggle_features_for_user(user_id, :feature6 => false, :feature7 => true)
70
- end
71
- Process.wait
72
- features_enabled = []
73
- feature_branch :feature6, user_id do
74
- features_enabled << :feature6
75
- end
76
- feature_branch :feature7, user_id do
77
- features_enabled << :feature7
78
- end
79
- features_enabled.should_not include(:feature6)
80
- features_enabled.should include(:feature7)
81
- end
82
-
83
- end
84
- end
85
- describe 'self#feature_branch' do
86
- after do
87
- Object.send(:remove_const, :TestObject)
88
- end
89
- # No need to retest all instance test cases, just a spot check due to implementation reuse
90
- it 'feature branches instance level behavior (case-insensitive feature names)' do
91
- class TestObject
92
- def self.features_enabled
93
- @features_enabled ||= []
94
- end
95
- def self.hit_me
96
- feature_branch :feature1 do
97
- self.features_enabled << :feature1
98
- end
99
- feature_branch :feature2 do
100
- self.features_enabled << :feature2
101
- end
102
- feature_branch :feature3 do
103
- self.features_enabled << :feature3
104
- end
105
- end
106
- end
107
- TestObject.hit_me
108
- TestObject.features_enabled.should include(:feature1)
109
- TestObject.features_enabled.should include(:feature2)
110
- TestObject.features_enabled.should_not include(:feature3)
111
- end
112
- end
113
- end
@@ -1,125 +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
- end
19
- describe '#feature_branch' do
20
- context 'class level behavior (case-insensitive string or symbol feature names)' do
21
- {
22
- 'Feature1' => true,
23
- :FEATURE2 => true,
24
- :feature3 => false,
25
- :admin_feature1 => true,
26
- :admin_feature2 => false,
27
- :public_feature1 => true,
28
- :public_feature2 => false,
29
- :wiki_feature1 => true,
30
- :wiki_feature2 => false,
31
- }.each do |feature_name, expected_branch_run|
32
- it "feature branches correctly for feature #{feature_name} with expected branch run #{expected_branch_run}" do
33
- feature_branch_run = false
34
- feature_branch feature_name do
35
- feature_branch_run = true
36
- end
37
- feature_branch_run.should == expected_branch_run
38
- end
39
- end
40
- end
41
- it 'returns nil and does not execute block for an invalid feature name' do
42
- return_value = feature_branch :invalid_feature_that_does_not_exist do
43
- fail 'feature branch block must not execute, but did.'
44
- end
45
- return_value.should be_nil
46
- end
47
- it 'allows environment variables (case-insensitive booleans) to override configuration file' do
48
- ENV['ABSTRACT_FEATURE_BRANCH_FEATURE1'] = 'FALSE'
49
- ENV['Abstract_Feature_Branch_Feature2'] = 'False'
50
- ENV['abstract_feature_branch_feature3'] = 'true'
51
- AbstractFeatureBranch.load_application_features
52
- features_enabled = []
53
- feature_branch :feature1 do
54
- features_enabled << :feature1
55
- end
56
- feature_branch :feature2 do
57
- features_enabled << :feature2
58
- end
59
- feature_branch :feature3 do
60
- features_enabled << :feature3
61
- end
62
- features_enabled.should_not include(:feature1)
63
- features_enabled.should_not include(:feature2)
64
- features_enabled.should include(:feature3)
65
- end
66
- it 'allows local configuration file to override main configuration file' do
67
- features_enabled = []
68
- feature_branch :feature4 do
69
- features_enabled << :feature4
70
- end
71
- feature_branch :feature5 do
72
- features_enabled << :feature5
73
- end
74
- feature_branch :admin_feature3 do
75
- features_enabled << :admin_feature3
76
- end
77
- feature_branch :public_feature3 do
78
- features_enabled << :public_feature3
79
- end
80
- feature_branch :wiki_feature3 do
81
- features_enabled << :wiki_feature3
82
- end
83
- features_enabled.should_not include(:feature4)
84
- features_enabled.should include(:feature5)
85
- features_enabled.should include(:admin_feature3)
86
- features_enabled.should include(:public_feature3)
87
- features_enabled.should include(:wiki_feature3)
88
- end
89
- it 'works with an application that has no configuration files' do
90
- AbstractFeatureBranch.application_root = File.join(__FILE__, '..', '..', 'fixtures', 'application_no_config')
91
- AbstractFeatureBranch.load_application_features
92
- feature_branch :feature1 do
93
- fail 'feature branch block must not execute, but did.'
94
- end
95
- end
96
- end
97
- describe 'self#feature_branch' do
98
- after do
99
- Object.send(:remove_const, :TestObject)
100
- end
101
- # No need to retest all instance test cases, just a spot check due to implementation reuse
102
- it 'feature branches instance level behavior (case-insensitive feature names)' do
103
- class TestObject
104
- def self.features_enabled
105
- @features_enabled ||= []
106
- end
107
- def self.hit_me
108
- feature_branch :feature1 do
109
- self.features_enabled << :feature1
110
- end
111
- feature_branch :feature2 do
112
- self.features_enabled << :feature2
113
- end
114
- feature_branch :feature3 do
115
- self.features_enabled << :feature3
116
- end
117
- end
118
- end
119
- TestObject.hit_me
120
- TestObject.features_enabled.should include(:feature1)
121
- TestObject.features_enabled.should include(:feature2)
122
- TestObject.features_enabled.should_not include(:feature3)
123
- end
124
- end
125
- end