flip_fab 1.0.1 → 1.0.2

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 (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