flip_fab 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +29 -0
  3. data/example/rails_app/Gemfile +2 -19
  4. data/example/rails_app/app/assets/javascripts/application.js +0 -1
  5. data/example/rails_app/app/controllers/beavers_controller.rb +11 -12
  6. data/example/rails_app/bin/rails +1 -1
  7. data/example/rails_app/config.ru +1 -1
  8. data/example/rails_app/config/environments/development.rb +1 -1
  9. data/example/rails_app/config/environments/test.rb +2 -2
  10. data/example/rails_app/config/initializers/cookies_serializer.rb +1 -1
  11. data/example/rails_app/db/schema.rb +5 -7
  12. data/example/rails_app/spec/rails_helper.rb +2 -2
  13. data/example/rails_app/spec/spec_helper.rb +0 -1
  14. data/example/rails_app/test/controllers/beavers_controller_test.rb +12 -12
  15. data/flip_fab.gemspec +9 -8
  16. data/lib/flip_fab.rb +3 -3
  17. data/lib/flip_fab/contextual_feature.rb +11 -17
  18. data/lib/flip_fab/cookie_persistence.rb +11 -16
  19. data/lib/flip_fab/feature.rb +2 -3
  20. data/lib/flip_fab/features_by_name.rb +4 -4
  21. data/lib/flip_fab/helper.rb +0 -1
  22. data/lib/flip_fab/persistence.rb +2 -3
  23. data/lib/flip_fab/version.rb +1 -1
  24. data/script/cibuild +10 -0
  25. data/spec/lib/flip_fab/contextual_feature_spec.rb +47 -56
  26. data/spec/lib/flip_fab/cookie_persistence_spec.rb +40 -43
  27. data/spec/lib/flip_fab/feature_spec.rb +6 -9
  28. data/spec/lib/flip_fab/features_by_name_spec.rb +3 -6
  29. data/spec/lib/flip_fab/helper_spec.rb +35 -38
  30. data/spec/lib/flip_fab/persistence_spec.rb +2 -5
  31. data/spec/lib/flip_fab_spec.rb +11 -15
  32. data/spec/spec_helper.rb +47 -49
  33. data/spec/support/test_app.rb +2 -2
  34. data/spec/support/test_context.rb +1 -1
  35. data/spec/support/test_multiple_persistence.rb +2 -3
  36. data/spec/support/test_persistence.rb +2 -3
  37. data/spec/support/test_rack_context.rb +3 -3
  38. metadata +38 -25
  39. data/example/rails_app/README.rdoc +0 -28
  40. data/example/rails_app/config/rabbit_feed.yml +0 -8
  41. data/example/rails_app/config/unicorn.rb +0 -4
@@ -1,11 +1,10 @@
1
1
  module FlipFab
2
2
  describe Feature do
3
3
  let(:name) { :example_test }
4
- let(:options) {{ default: :enabled, persistence_adapters: [] }}
4
+ let(:options) { { default: :enabled, persistence_adapters: [] } }
5
5
  subject { described_class.new name, options }
6
6
 
7
7
  describe '.new' do
8
-
9
8
  it 'assigns the name' do
10
9
  expect(subject.name).to eq(:example_test)
11
10
  end
@@ -36,9 +35,8 @@ module FlipFab
36
35
  end
37
36
 
38
37
  describe '#enabled?' do
39
-
40
38
  context 'when the feature is enabled' do
41
- let(:options) {{ default: :enabled, persistence_adapters: [] }}
39
+ let(:options) { { default: :enabled, persistence_adapters: [] } }
42
40
 
43
41
  it 'returns true' do
44
42
  expect(subject.enabled?).to be_truthy
@@ -46,7 +44,7 @@ module FlipFab
46
44
  end
47
45
 
48
46
  context 'when the feature is disabled' do
49
- let(:options) {{ default: :disabled, persistence_adapters: [] }}
47
+ let(:options) { { default: :disabled, persistence_adapters: [] } }
50
48
 
51
49
  it 'returns false' do
52
50
  expect(subject.enabled?).to be_falsey
@@ -55,9 +53,8 @@ module FlipFab
55
53
  end
56
54
 
57
55
  describe '#disabled?' do
58
-
59
56
  context 'when the feature is disabled' do
60
- let(:options) {{ default: :disabled, persistence_adapters: [] }}
57
+ let(:options) { { default: :disabled, persistence_adapters: [] } }
61
58
 
62
59
  it 'returns true' do
63
60
  expect(subject.disabled?).to be_truthy
@@ -65,7 +62,7 @@ module FlipFab
65
62
  end
66
63
 
67
64
  context 'when the feature is enabled' do
68
- let(:options) {{ default: :enabled, persistence_adapters: [] }}
65
+ let(:options) { { default: :enabled, persistence_adapters: [] } }
69
66
 
70
67
  it 'returns false' do
71
68
  expect(subject.disabled?).to be_falsey
@@ -77,7 +74,7 @@ module FlipFab
77
74
  let(:context) { double(:context) }
78
75
 
79
76
  it 'returns a contextual feature' do
80
- expect(subject.with_context context).to be_a ContextualFeature
77
+ expect(subject.with_context(context)).to be_a ContextualFeature
81
78
  expect((subject.with_context context).feature).to eq(subject)
82
79
  expect((subject.with_context context).context).to eq(context)
83
80
  end
@@ -2,21 +2,18 @@ module FlipFab
2
2
  describe FeaturesByName do
3
3
  let(:feature) { Feature.new :example_feature }
4
4
  let(:features) { { example_feature: feature } }
5
- subject{ described_class.new features }
5
+ subject { described_class.new features }
6
6
 
7
7
  describe '#[]' do
8
-
9
8
  context 'when the feature exists' do
10
-
11
9
  it 'returns the feature' do
12
10
  expect(subject[:example_feature]).to eq(feature)
13
11
  end
14
12
  end
15
13
 
16
14
  context 'when the feature does not exist' do
17
-
18
15
  it 'raises' do
19
- expect{ subject[:no_feature] }.to raise_error 'no feature has been defined with the name: no_feature'
16
+ expect { subject[:no_feature] }.to raise_error 'no feature has been defined with the name: no_feature'
20
17
  end
21
18
  end
22
19
  end
@@ -25,7 +22,7 @@ module FlipFab
25
22
  let(:context) { double(:context) }
26
23
 
27
24
  it 'returns contextual features by name' do
28
- expect(subject.with_context context).to be_a described_class
25
+ expect(subject.with_context(context)).to be_a described_class
29
26
  expect((subject.with_context context)[:example_feature]).to be_a ContextualFeature
30
27
  expect((subject.with_context context)[:example_feature].feature).to eq(feature)
31
28
  end
@@ -4,51 +4,49 @@ module FlipFab
4
4
  describe Helper do
5
5
  include Rack::Test::Methods
6
6
  let(:app) { TestApp.new }
7
- after { FlipFab.features.clear }
7
+ after { FlipFab.features.clear }
8
8
 
9
- it 'runs the feature' do
10
- feature
11
- end
12
-
13
- step 'there is a feature with a default state of :default_state' do |default_state|
14
- FlipFab.define_feature :example_feature, { default: default_state.to_sym, persistence_adapters: [TestPersistence] }
15
- end
9
+ feature do
10
+ step 'there is a feature with a default state of :default_state' do |default_state|
11
+ FlipFab.define_feature :example_feature, default: default_state.to_sym, persistence_adapters: [TestPersistence]
12
+ end
16
13
 
17
- step 'there are two contexts' do
18
- @first_context = TestContext.new
19
- @second_context = TestContext.new
20
- end
14
+ step 'there are two contexts' do
15
+ @first_context = TestContext.new
16
+ @second_context = TestContext.new
17
+ end
21
18
 
22
- step 'the feature is :expected_state_in_first_context in the first context, :expected_state_in_second_context in the second context' do |expected_state_in_first_context, expected_state_in_second_context|
23
- expect(@first_context.features[:example_feature].enabled?).to eq(expected_state_in_first_context == 'enabled')
24
- expect(@second_context.features[:example_feature].enabled?).to eq(expected_state_in_second_context == 'enabled')
25
- end
19
+ step 'the feature is :expected_state_in_first_context in the first context, :expected_state_in_second_context in the second context' do |expected_state_in_first_context, expected_state_in_second_context|
20
+ expect(@first_context.features[:example_feature].enabled?).to eq(expected_state_in_first_context == 'enabled')
21
+ expect(@second_context.features[:example_feature].enabled?).to eq(expected_state_in_second_context == 'enabled')
22
+ end
26
23
 
27
- step 'I :enable_or_disable the feature in the first context' do |enable_or_disable|
28
- @first_context.features[:example_feature].send(enable_or_disable.to_sym)
29
- end
24
+ step 'I :enable_or_disable the feature in the first context' do |enable_or_disable|
25
+ @first_context.features[:example_feature].send(enable_or_disable.to_sym)
26
+ end
30
27
 
31
- step 'there is a feature with a default state of :default_state with cookie persistence' do |default_state|
32
- FlipFab.define_feature :example_feature, { default: default_state.to_sym }
33
- end
28
+ step 'there is a feature with a default state of :default_state with cookie persistence' do |default_state|
29
+ FlipFab.define_feature :example_feature, default: default_state.to_sym
30
+ end
34
31
 
35
- step 'I override the state in the URL parameters with :overridden_state' do |overridden_state|
36
- get "/?example_feature=#{overridden_state}"
37
- end
32
+ step 'I override the state in the URL parameters with :overridden_state' do |overridden_state|
33
+ get "/?example_feature=#{overridden_state}"
34
+ end
38
35
 
39
- step 'the feature is :state for the user' do |state|
40
- expect(app.contextual_features[:example_feature].enabled?).to eq(state=='enabled')
41
- end
36
+ step 'the feature is :state for the user' do |state|
37
+ expect(app.contextual_features[:example_feature].enabled?).to eq(state == 'enabled')
38
+ end
42
39
 
43
- step 'I :enable_or_disable the feature for the user' do |enable_or_disable|
44
- app.contextual_features[:example_feature].send(enable_or_disable.to_sym)
40
+ step 'I :enable_or_disable the feature for the user' do |enable_or_disable|
41
+ app.contextual_features[:example_feature].send(enable_or_disable.to_sym)
42
+ end
45
43
  end
46
44
 
47
45
  describe '#features' do
48
- let(:params) {{}}
49
- let(:feature_states) {{ example_feature: :enabled }}
46
+ let(:params) { {} }
47
+ let(:feature_states) { { example_feature: :enabled } }
50
48
  let(:context) { (TestContext.new feature_states, params) }
51
- before { FlipFab.define_feature :example_feature, { persistence_adapters: [TestPersistence] } }
49
+ before { FlipFab.define_feature :example_feature, persistence_adapters: [TestPersistence] }
52
50
  subject { context.features }
53
51
 
54
52
  it 'maps the features to contextual features by feature name' do
@@ -60,25 +58,24 @@ module FlipFab
60
58
  end
61
59
 
62
60
  context 'when the feature is overridden in the params' do
63
- let(:params) {{'example_feature' => 'disabled'}}
61
+ let(:params) { { 'example_feature' => 'disabled' } }
64
62
 
65
63
  it 'applies the override to the feature' do
66
64
  expect(subject[:example_feature].disabled?).to be_truthy
67
65
  end
68
66
 
69
67
  it 'prevents the feature\'s state from being changed' do
70
- expect{ subject[:example_feature].enable }.not_to change{ subject[:example_feature].enabled? }.from(false)
68
+ expect { subject[:example_feature].enable }.not_to change { subject[:example_feature].enabled? }.from(false)
71
69
  end
72
70
  end
73
71
 
74
72
  context 'passing the context to the feature' do
75
-
76
73
  it 'feature toggling is applied to the context' do
77
- expect{ subject[:example_feature].disable }.to change{ subject[:example_feature].enabled? }.from(true).to(false)
74
+ expect { subject[:example_feature].disable }.to change { subject[:example_feature].enabled? }.from(true).to(false)
78
75
  end
79
76
 
80
77
  it 'feature toggling is persisted in the context' do
81
- expect{ subject[:example_feature].disable }.to change{ feature_states }.from({ example_feature: :enabled }).to({ example_feature: :disabled })
78
+ expect { subject[:example_feature].disable }.to change { feature_states }.from(example_feature: :enabled).to(example_feature: :disabled)
82
79
  end
83
80
  end
84
81
 
@@ -5,7 +5,6 @@ module FlipFab
5
5
  subject { described_class.new feature_name, context }
6
6
 
7
7
  describe '.new' do
8
-
9
8
  it 'assigns the feature name' do
10
9
  expect(subject.feature_name).to eq(:example_test)
11
10
  end
@@ -16,16 +15,14 @@ module FlipFab
16
15
  end
17
16
 
18
17
  describe '#read' do
19
-
20
18
  it 'is not implemented' do
21
- expect{ subject.read }.to raise_error NotImplementedError
19
+ expect { subject.read }.to raise_error NotImplementedError
22
20
  end
23
21
  end
24
22
 
25
23
  describe '#write' do
26
-
27
24
  it 'is not implemented' do
28
- expect{ subject.write feature_name }.to raise_error NotImplementedError
25
+ expect { subject.write feature_name }.to raise_error NotImplementedError
29
26
  end
30
27
  end
31
28
  end
@@ -1,16 +1,14 @@
1
1
  describe FlipFab do
2
2
  let(:name) { :example_feature }
3
3
 
4
- it 'runs the feature' do
5
- feature
6
- end
7
-
8
- step 'I define a feature that is :enabled_or_disabled' do |enabled_or_disabled|
9
- described_class.define_feature name, { default: enabled_or_disabled.to_sym }
10
- end
4
+ feature do
5
+ step 'I define a feature that is :enabled_or_disabled' do |enabled_or_disabled|
6
+ described_class.define_feature name, default: enabled_or_disabled.to_sym
7
+ end
11
8
 
12
- step 'the feature is :enabled_or_disabled' do |enabled_or_disabled|
13
- expect(described_class.features[name].enabled?).to eq(enabled_or_disabled == 'enabled')
9
+ step 'the feature is :enabled_or_disabled' do |enabled_or_disabled|
10
+ expect(described_class.features[name].enabled?).to eq(enabled_or_disabled == 'enabled')
11
+ end
14
12
  end
15
13
 
16
14
  it 'initializes features' do
@@ -18,8 +16,8 @@ describe FlipFab do
18
16
  end
19
17
 
20
18
  context '.define_feature' do
21
- subject{ described_class.define_feature name }
22
- after{ described_class.features.clear }
19
+ subject { described_class.define_feature name }
20
+ after { described_class.features.clear }
23
21
 
24
22
  it 'returns the feature' do
25
23
  expect(subject).to be_a FlipFab::Feature
@@ -27,18 +25,16 @@ describe FlipFab do
27
25
  end
28
26
 
29
27
  context 'when the feature exists' do
30
-
31
28
  it 'overwrites the existing feature' do
32
29
  existing_feature = described_class.define_feature name
33
- expect{ subject }.not_to change{ described_class.features.count }.from(1)
30
+ expect { subject }.not_to change { described_class.features.count }.from(1)
34
31
  expect(subject).not_to eq(existing_feature)
35
32
  end
36
33
  end
37
34
 
38
35
  context 'when the feature does not exist' do
39
-
40
36
  it 'saves the feature' do
41
- expect{ subject }.to change{ described_class.features.count }.from(0).to(1)
37
+ expect { subject }.to change { described_class.features.count }.from(0).to(1)
42
38
  expected_feature = subject
43
39
  expect(described_class.features[:example_feature]).to eq(expected_feature)
44
40
  end
@@ -1,6 +1,6 @@
1
1
  require 'flip_fab'
2
2
 
3
- Dir['./spec/support/**/*.rb'].sort.each { |f| require f}
3
+ Dir['./spec/support/**/*.rb'].sort.each { |f| require f }
4
4
 
5
5
  # This file was generated by the `rspec --init` command. Conventionally, all
6
6
  # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
@@ -42,52 +42,50 @@ RSpec.configure do |config|
42
42
  mocks.verify_partial_doubles = true
43
43
  end
44
44
 
45
- # The settings below are suggested to provide a good initial experience
46
- # with RSpec, but feel free to customize to your heart's content.
47
- =begin
48
- # These two settings work together to allow you to limit a spec run
49
- # to individual examples or groups you care about by tagging them with
50
- # `:focus` metadata. When nothing is tagged with `:focus`, all examples
51
- # get run.
52
- config.filter_run :focus
53
- config.run_all_when_everything_filtered = true
54
-
55
- # Limits the available syntax to the non-monkey patched syntax that is recommended.
56
- # For more details, see:
57
- # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
58
- # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
59
- # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
60
- config.disable_monkey_patching!
61
-
62
- # This setting enables warnings. It's recommended, but in some cases may
63
- # be too noisy due to issues in dependencies.
64
- config.warnings = true
65
-
66
- # Many RSpec users commonly either run the entire suite or an individual
67
- # file, and it's useful to allow more verbose output when running an
68
- # individual spec file.
69
- if config.files_to_run.one?
70
- # Use the documentation formatter for detailed output,
71
- # unless a formatter has already been configured
72
- # (e.g. via a command-line flag).
73
- config.default_formatter = 'doc'
74
- end
75
-
76
- # Print the 10 slowest examples and example groups at the
77
- # end of the spec run, to help surface which specs are running
78
- # particularly slow.
79
- config.profile_examples = 10
80
-
81
- # Run specs in random order to surface order dependencies. If you find an
82
- # order dependency and want to debug it, you can fix the order by providing
83
- # the seed, which is printed after each run.
84
- # --seed 1234
85
- config.order = :random
86
-
87
- # Seed global randomization in this process using the `--seed` CLI option.
88
- # Setting this allows you to use `--seed` to deterministically reproduce
89
- # test failures related to randomization by passing the same `--seed` value
90
- # as the one that triggered the failure.
91
- Kernel.srand config.seed
92
- =end
45
+ # The settings below are suggested to provide a good initial experience
46
+ # with RSpec, but feel free to customize to your heart's content.
47
+ # # These two settings work together to allow you to limit a spec run
48
+ # # to individual examples or groups you care about by tagging them with
49
+ # # `:focus` metadata. When nothing is tagged with `:focus`, all examples
50
+ # # get run.
51
+ # config.filter_run :focus
52
+ # config.run_all_when_everything_filtered = true
53
+ #
54
+ # # Limits the available syntax to the non-monkey patched syntax that is recommended.
55
+ # # For more details, see:
56
+ # # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
57
+ # # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
58
+ # # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
59
+ # config.disable_monkey_patching!
60
+ #
61
+ # # This setting enables warnings. It's recommended, but in some cases may
62
+ # # be too noisy due to issues in dependencies.
63
+ # config.warnings = true
64
+ #
65
+ # # Many RSpec users commonly either run the entire suite or an individual
66
+ # # file, and it's useful to allow more verbose output when running an
67
+ # # individual spec file.
68
+ # if config.files_to_run.one?
69
+ # # Use the documentation formatter for detailed output,
70
+ # # unless a formatter has already been configured
71
+ # # (e.g. via a command-line flag).
72
+ # config.default_formatter = 'doc'
73
+ # end
74
+ #
75
+ # # Print the 10 slowest examples and example groups at the
76
+ # # end of the spec run, to help surface which specs are running
77
+ # # particularly slow.
78
+ # config.profile_examples = 10
79
+ #
80
+ # # Run specs in random order to surface order dependencies. If you find an
81
+ # # order dependency and want to debug it, you can fix the order by providing
82
+ # # the seed, which is printed after each run.
83
+ # # --seed 1234
84
+ # config.order = :random
85
+ #
86
+ # # Seed global randomization in this process using the `--seed` CLI option.
87
+ # # Setting this allows you to use `--seed` to deterministically reproduce
88
+ # # test failures related to randomization by passing the same `--seed` value
89
+ # # as the one that triggered the failure.
90
+ # Kernel.srand config.seed
93
91
  end
@@ -3,14 +3,14 @@ class TestApp
3
3
 
4
4
  attr_reader :request, :response, :params, :contextual_features
5
5
 
6
- def call env
6
+ def call(env)
7
7
  @request = Rack::Request.new env
8
8
  @response = Rack::Response.new
9
9
  @params = request.params
10
10
 
11
11
  @contextual_features = features
12
12
 
13
- request.cookies.each{|k,v| response.set_cookie k, v }
13
+ request.cookies.each { |k, v| response.set_cookie k, v }
14
14
  [response.status, response.header, response.body]
15
15
  end
16
16
  end
@@ -3,7 +3,7 @@ class TestContext
3
3
 
4
4
  attr_reader :feature_states, :params
5
5
 
6
- def initialize feature_states={}, params={}
6
+ def initialize(feature_states = {}, params = {})
7
7
  @feature_states = feature_states
8
8
  @params = params
9
9
  end
@@ -1,6 +1,5 @@
1
1
  class TestMultiplePersistence < FlipFab::Persistence
2
-
3
- def initialize feature_name, context
2
+ def initialize(feature_name, context)
4
3
  super
5
4
  end
6
5
 
@@ -8,7 +7,7 @@ class TestMultiplePersistence < FlipFab::Persistence
8
7
  context.feature_states[:different_example_feature]
9
8
  end
10
9
 
11
- def write value
10
+ def write(value)
12
11
  context.feature_states[:different_example_feature] = value
13
12
  end
14
13
  end
@@ -1,6 +1,5 @@
1
1
  class TestPersistence < FlipFab::Persistence
2
-
3
- def initialize feature_name, context
2
+ def initialize(feature_name, context)
4
3
  super
5
4
  end
6
5
 
@@ -8,7 +7,7 @@ class TestPersistence < FlipFab::Persistence
8
7
  context.feature_states[feature_name]
9
8
  end
10
9
 
11
- def write state
10
+ def write(state)
12
11
  context.feature_states[feature_name] = state
13
12
  end
14
13
  end