flip_fork 0.1.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 +7 -0
- data/.gitignore +4 -0
- data/.travis.yml +7 -0
- data/Gemfile +2 -0
- data/README.md +161 -0
- data/Rakefile +10 -0
- data/TODO +3 -0
- data/app/assets/stylesheets/flip.css +70 -0
- data/app/controllers/flip/features_controller.rb +47 -0
- data/app/controllers/flip/strategies_controller.rb +31 -0
- data/app/helpers/flip_helper.rb +9 -0
- data/app/views/flip/features/index.html.erb +62 -0
- data/config/routes.rb +14 -0
- data/flip.gemspec +25 -0
- data/lib/flip/abstract_strategy.rb +26 -0
- data/lib/flip/controller_filters.rb +21 -0
- data/lib/flip/cookie_strategy.rb +62 -0
- data/lib/flip/database_strategy.rb +40 -0
- data/lib/flip/declarable.rb +24 -0
- data/lib/flip/declaration_strategy.rb +20 -0
- data/lib/flip/definition.rb +21 -0
- data/lib/flip/engine.rb +9 -0
- data/lib/flip/facade.rb +18 -0
- data/lib/flip/feature_set.rb +57 -0
- data/lib/flip/forbidden.rb +7 -0
- data/lib/flip/version.rb +3 -0
- data/lib/flip.rb +27 -0
- data/lib/generators/flip/install/install_generator.rb +9 -0
- data/lib/generators/flip/migration/USAGE +5 -0
- data/lib/generators/flip/migration/migration_generator.rb +22 -0
- data/lib/generators/flip/migration/templates/create_features.rb +10 -0
- data/lib/generators/flip/model/USAGE +8 -0
- data/lib/generators/flip/model/model_generator.rb +8 -0
- data/lib/generators/flip/model/templates/feature.rb +15 -0
- data/lib/generators/flip/routes/USAGE +7 -0
- data/lib/generators/flip/routes/routes_generator.rb +7 -0
- data/spec/abstract_strategy_spec.rb +11 -0
- data/spec/controller_filters_spec.rb +27 -0
- data/spec/cookie_strategy_spec.rb +112 -0
- data/spec/database_strategy_spec.rb +66 -0
- data/spec/declarable_spec.rb +32 -0
- data/spec/declaration_strategy_spec.rb +39 -0
- data/spec/definition_spec.rb +19 -0
- data/spec/feature_set_spec.rb +67 -0
- data/spec/flip_spec.rb +33 -0
- data/spec/spec_helper.rb +1 -0
- metadata +156 -0
data/lib/flip/facade.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
module Flip
|
2
|
+
module Facade
|
3
|
+
|
4
|
+
def on?(feature)
|
5
|
+
FeatureSet.instance.on? feature
|
6
|
+
end
|
7
|
+
|
8
|
+
def reset
|
9
|
+
FeatureSet.reset
|
10
|
+
end
|
11
|
+
|
12
|
+
def method_missing(method, *parameters)
|
13
|
+
super unless method =~ %r{^(.*)\?$}
|
14
|
+
FeatureSet.instance.on? $1.to_sym
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Flip
|
2
|
+
class FeatureSet
|
3
|
+
|
4
|
+
def self.instance
|
5
|
+
@instance ||= self.new
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.reset
|
9
|
+
@instance = nil
|
10
|
+
end
|
11
|
+
|
12
|
+
# Sets the default for definitions which fall through the strategies.
|
13
|
+
# Accepts boolean or a Proc to be called.
|
14
|
+
attr_writer :default
|
15
|
+
|
16
|
+
def initialize
|
17
|
+
@definitions = Hash.new { |_, k| raise "No feature declared with key #{k.inspect}" }
|
18
|
+
@strategies = Hash.new { |_, k| raise "No strategy named #{k}" }
|
19
|
+
@default = false
|
20
|
+
end
|
21
|
+
|
22
|
+
# Whether the given feature is switched on.
|
23
|
+
def on? key
|
24
|
+
d = @definitions[key]
|
25
|
+
@strategies.each_value { |s| return s.on?(d) if s.knows?(d) }
|
26
|
+
default_for d
|
27
|
+
end
|
28
|
+
|
29
|
+
# Adds a feature definition to the set.
|
30
|
+
def << definition
|
31
|
+
@definitions[definition.key] = definition
|
32
|
+
end
|
33
|
+
|
34
|
+
# Adds a strategy for determing feature status.
|
35
|
+
def add_strategy(strategy)
|
36
|
+
strategy = strategy.new if strategy.is_a? Class
|
37
|
+
@strategies[strategy.name] = strategy
|
38
|
+
end
|
39
|
+
|
40
|
+
def strategy(klass)
|
41
|
+
@strategies[klass]
|
42
|
+
end
|
43
|
+
|
44
|
+
def default_for(definition)
|
45
|
+
@default.is_a?(Proc) ? @default.call(definition) : @default
|
46
|
+
end
|
47
|
+
|
48
|
+
def definitions
|
49
|
+
@definitions.values
|
50
|
+
end
|
51
|
+
|
52
|
+
def strategies
|
53
|
+
@strategies.values
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
data/lib/flip/version.rb
ADDED
data/lib/flip.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# ActiveSupport dependencies.
|
2
|
+
%w{
|
3
|
+
concern
|
4
|
+
inflector
|
5
|
+
core_ext/hash/reverse_merge
|
6
|
+
core_ext/object/blank
|
7
|
+
}.each { |name| require "active_support/#{name}" }
|
8
|
+
|
9
|
+
# Flip files.
|
10
|
+
%w{
|
11
|
+
abstract_strategy
|
12
|
+
controller_filters
|
13
|
+
cookie_strategy
|
14
|
+
database_strategy
|
15
|
+
declarable
|
16
|
+
declaration_strategy
|
17
|
+
definition
|
18
|
+
facade
|
19
|
+
feature_set
|
20
|
+
forbidden
|
21
|
+
}.each { |name| require "flip/#{name}" }
|
22
|
+
|
23
|
+
require "flip/engine" if defined?(Rails)
|
24
|
+
|
25
|
+
module Flip
|
26
|
+
extend Facade
|
27
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require "rails/generators/active_record/migration"
|
2
|
+
|
3
|
+
class Flip::MigrationGenerator < Rails::Generators::Base
|
4
|
+
include Rails::Generators::Migration
|
5
|
+
|
6
|
+
source_root File.expand_path("../templates", __FILE__)
|
7
|
+
|
8
|
+
def create_migration_file
|
9
|
+
migration_template "create_features.rb", "db/migrate/create_features.rb"
|
10
|
+
end
|
11
|
+
|
12
|
+
# Stubbed in railties/lib/rails/generators/migration.rb
|
13
|
+
#
|
14
|
+
# This implementation a simplified version of:
|
15
|
+
# activerecord/lib/rails/generators/active_record/migration.rb
|
16
|
+
#
|
17
|
+
# See: http://www.ruby-forum.com/topic/203205
|
18
|
+
def self.next_migration_number(dirname)
|
19
|
+
Time.now.utc.strftime("%Y%m%d%H%M%S")
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class Feature < ActiveRecord::Base
|
2
|
+
extend Flip::Declarable
|
3
|
+
|
4
|
+
strategy Flip::CookieStrategy
|
5
|
+
strategy Flip::DatabaseStrategy
|
6
|
+
strategy Flip::DeclarationStrategy
|
7
|
+
default false
|
8
|
+
|
9
|
+
# Declare your features here, e.g:
|
10
|
+
#
|
11
|
+
# feature :world_domination,
|
12
|
+
# default: true,
|
13
|
+
# description: "Take over the world."
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
# Perhaps this is silly, but it provides some
|
4
|
+
# coverage to an important base class.
|
5
|
+
describe Flip::AbstractStrategy do
|
6
|
+
|
7
|
+
its(:name) { should == "abstract" }
|
8
|
+
its(:description) { should == "" }
|
9
|
+
it { should_not be_switchable }
|
10
|
+
|
11
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
class ControllerWithFlipFilters
|
4
|
+
include Flip::ControllerFilters
|
5
|
+
end
|
6
|
+
|
7
|
+
describe ControllerWithFlipFilters do
|
8
|
+
|
9
|
+
describe ".require_feature" do
|
10
|
+
|
11
|
+
it "adds before_filter without options" do
|
12
|
+
ControllerWithFlipFilters.tap do |klass|
|
13
|
+
klass.should_receive(:before_filter).with({})
|
14
|
+
klass.send(:require_feature, :testable)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
it "adds before_filter with options" do
|
19
|
+
ControllerWithFlipFilters.tap do |klass|
|
20
|
+
klass.should_receive(:before_filter).with({ only: [ :show ] })
|
21
|
+
klass.send(:require_feature, :testable, only: [ :show ])
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
class ControllerWithoutCookieStrategy; end
|
4
|
+
class ControllerWithCookieStrategy
|
5
|
+
def self.before_filter(_); end
|
6
|
+
def self.after_filter(_); end
|
7
|
+
def cookies; []; end
|
8
|
+
include Flip::CookieStrategy::Loader
|
9
|
+
end
|
10
|
+
|
11
|
+
describe Flip::CookieStrategy do
|
12
|
+
|
13
|
+
let(:cookies) do
|
14
|
+
{ strategy.cookie_name(:one) => "true",
|
15
|
+
strategy.cookie_name(:two) => "false" }
|
16
|
+
end
|
17
|
+
let(:strategy) do
|
18
|
+
Flip::CookieStrategy.new.tap do |s|
|
19
|
+
s.stub(:cookies) { cookies }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
its(:description) { should be_present }
|
24
|
+
it { should be_switchable }
|
25
|
+
|
26
|
+
describe "cookie interrogration" do
|
27
|
+
context "enabled feature" do
|
28
|
+
specify "#knows? is true" do
|
29
|
+
strategy.knows?(:one).should be_true
|
30
|
+
end
|
31
|
+
specify "#on? is true" do
|
32
|
+
strategy.on?(:one).should be_true
|
33
|
+
end
|
34
|
+
end
|
35
|
+
context "disabled feature" do
|
36
|
+
specify "#knows? is true" do
|
37
|
+
strategy.knows?(:two).should be_true
|
38
|
+
end
|
39
|
+
specify "#on? is false" do
|
40
|
+
strategy.on?(:two).should be_false
|
41
|
+
end
|
42
|
+
end
|
43
|
+
context "feature with no cookie present" do
|
44
|
+
specify "#knows? is false" do
|
45
|
+
strategy.knows?(:three).should be_false
|
46
|
+
end
|
47
|
+
specify "#on? is false" do
|
48
|
+
strategy.on?(:three).should be_false
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "cookie manipulation" do
|
54
|
+
it "can switch known features on" do
|
55
|
+
strategy.switch! :one, true
|
56
|
+
strategy.on?(:one).should be_true
|
57
|
+
end
|
58
|
+
it "can switch unknown features on" do
|
59
|
+
strategy.switch! :three, true
|
60
|
+
strategy.on?(:three).should be_true
|
61
|
+
end
|
62
|
+
it "can switch features off" do
|
63
|
+
strategy.switch! :two, false
|
64
|
+
strategy.on?(:two).should be_false
|
65
|
+
end
|
66
|
+
it "can delete knowledge of a feature" do
|
67
|
+
strategy.delete! :one
|
68
|
+
strategy.on?(:one).should be_false
|
69
|
+
strategy.knows?(:one).should be_false
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
describe Flip::CookieStrategy::Loader do
|
76
|
+
|
77
|
+
it "adds filters when included in controller" do
|
78
|
+
ControllerWithoutCookieStrategy.tap do |klass|
|
79
|
+
klass.should_receive(:before_filter).with(:flip_cookie_strategy_before)
|
80
|
+
klass.should_receive(:after_filter).with(:flip_cookie_strategy_after)
|
81
|
+
klass.send :include, Flip::CookieStrategy::Loader
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe "filter methods" do
|
86
|
+
let(:strategy) { Flip::CookieStrategy.new }
|
87
|
+
let(:controller) { ControllerWithCookieStrategy.new }
|
88
|
+
describe "#flip_cookie_strategy_before" do
|
89
|
+
it "passes controller cookies to CookieStrategy" do
|
90
|
+
controller.should_receive(:cookies).and_return(strategy.cookie_name(:test) => "true")
|
91
|
+
expect {
|
92
|
+
controller.flip_cookie_strategy_before
|
93
|
+
}.to change {
|
94
|
+
[ strategy.knows?(:test), strategy.on?(:test) ]
|
95
|
+
}.from([false, false]).to([true, true])
|
96
|
+
end
|
97
|
+
end
|
98
|
+
describe "#flip_cookie_strategy_after" do
|
99
|
+
before do
|
100
|
+
Flip::CookieStrategy.cookies = { strategy.cookie_name(:test) => "true" }
|
101
|
+
end
|
102
|
+
it "passes controller cookies to CookieStrategy" do
|
103
|
+
expect {
|
104
|
+
controller.flip_cookie_strategy_after
|
105
|
+
}.to change {
|
106
|
+
[ strategy.knows?(:test), strategy.on?(:test) ]
|
107
|
+
}.from([true, true]).to([false, false])
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Flip::DatabaseStrategy do
|
4
|
+
|
5
|
+
let(:definition) { double("definition").tap{ |d| d.stub(:key) { :one } } }
|
6
|
+
let(:strategy) { Flip::DatabaseStrategy.new(model_klass) }
|
7
|
+
let(:model_klass) do
|
8
|
+
Class.new do
|
9
|
+
extend Flip::Declarable
|
10
|
+
feature :one
|
11
|
+
feature :two, description: "Second one."
|
12
|
+
feature :three, default: true
|
13
|
+
end
|
14
|
+
end
|
15
|
+
let(:enabled_record) { model_klass.new.tap { |m| m.stub(:enabled?) { true } } }
|
16
|
+
let(:disabled_record) { model_klass.new.tap { |m| m.stub(:enabled?) { false } } }
|
17
|
+
|
18
|
+
subject { strategy }
|
19
|
+
|
20
|
+
its(:switchable?) { should be_true }
|
21
|
+
its(:description) { should be_present }
|
22
|
+
|
23
|
+
describe "#knows?" do
|
24
|
+
it "does not know features that cannot be found" do
|
25
|
+
model_klass.stub(:find_by_key) { nil }
|
26
|
+
strategy.knows?(definition).should be_false
|
27
|
+
end
|
28
|
+
it "knows features that can be found" do
|
29
|
+
model_klass.stub(:find_by_key) { disabled_record }
|
30
|
+
strategy.knows?(definition).should be_true
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "#on?" do
|
35
|
+
it "is true for an enabled record from the database" do
|
36
|
+
model_klass.stub(:find_by_key) { enabled_record }
|
37
|
+
strategy.on?(definition).should be_true
|
38
|
+
end
|
39
|
+
it "is false for a disabled record from the database" do
|
40
|
+
model_klass.stub(:find_by_key) { disabled_record }
|
41
|
+
strategy.on?(definition).should be_false
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "#switch!" do
|
46
|
+
it "can switch a feature on" do
|
47
|
+
model_klass.should_receive(:find_or_initialize_by_key).with('one').and_return(disabled_record)
|
48
|
+
disabled_record.should_receive(:update_attributes!).with(enabled: true)
|
49
|
+
strategy.switch! :one, true
|
50
|
+
end
|
51
|
+
it "can switch a feature off" do
|
52
|
+
model_klass.should_receive(:find_or_initialize_by_key).with('one').and_return(enabled_record)
|
53
|
+
enabled_record.should_receive(:update_attributes!).with(enabled: false)
|
54
|
+
strategy.switch! :one, false
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "#delete!" do
|
59
|
+
it "can delete a feature record" do
|
60
|
+
model_klass.should_receive(:find_by_key).with('one').and_return(enabled_record)
|
61
|
+
enabled_record.should_receive(:try).with(:destroy)
|
62
|
+
strategy.delete! :one
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Flip::Declarable do
|
4
|
+
|
5
|
+
let!(:model_class) do
|
6
|
+
Class.new do
|
7
|
+
extend Flip::Declarable
|
8
|
+
|
9
|
+
strategy Flip::DeclarationStrategy
|
10
|
+
default false
|
11
|
+
|
12
|
+
feature :one
|
13
|
+
feature :two, description: "Second one."
|
14
|
+
feature :three, default: true
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
subject { Flip::FeatureSet.instance }
|
19
|
+
|
20
|
+
describe "the .on? class method" do
|
21
|
+
context "with default set to false" do
|
22
|
+
it { should_not be_on(:one) }
|
23
|
+
it { should be_on(:three) }
|
24
|
+
end
|
25
|
+
context "with default set to true" do
|
26
|
+
before(:all) { model_class.send(:default, true) }
|
27
|
+
it { should be_on(:one) }
|
28
|
+
it { should be_on(:three) }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Flip::DeclarationStrategy do
|
4
|
+
|
5
|
+
def definition(default)
|
6
|
+
Flip::Definition.new :feature, default: default
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "#knows?" do
|
10
|
+
it "does not know definition with no default specified" do
|
11
|
+
subject.knows?(Flip::Definition.new :feature).should be_false
|
12
|
+
end
|
13
|
+
it "does not know definition with default of nil" do
|
14
|
+
subject.knows?(definition(nil)).should be_false
|
15
|
+
end
|
16
|
+
it "knows definition with default set to true" do
|
17
|
+
subject.knows?(definition(true)).should be_true
|
18
|
+
end
|
19
|
+
it "knows definition with default set to false" do
|
20
|
+
subject.knows?(definition(false)).should be_true
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "#on? for Flip::Definition" do
|
25
|
+
subject { Flip::DeclarationStrategy.new.on? definition(default) }
|
26
|
+
[
|
27
|
+
{ default: true, result: true },
|
28
|
+
{ default: false, result: false },
|
29
|
+
{ default: proc { true }, result: true, name: "proc returning true" },
|
30
|
+
{ default: proc { false }, result: false, name: "proc returning false" },
|
31
|
+
].each do |parameters|
|
32
|
+
context "with default of #{parameters[:name] || parameters[:default]}" do
|
33
|
+
let(:default) { parameters[:default] }
|
34
|
+
it { should == parameters[:result] }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Flip::Definition do
|
4
|
+
|
5
|
+
subject { Flip::Definition.new :the_key, description: "The description" }
|
6
|
+
|
7
|
+
[:key, :name, :to_s].each do |method|
|
8
|
+
its(method) { should == :the_key }
|
9
|
+
end
|
10
|
+
|
11
|
+
its(:description) { should == "The description" }
|
12
|
+
its(:options) { should == { description: "The description" } }
|
13
|
+
|
14
|
+
context "without description specified" do
|
15
|
+
subject { Flip::Definition.new :the_key }
|
16
|
+
its(:description) { should == "The key." }
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
class NullStrategy < Flip::AbstractStrategy
|
4
|
+
def knows?(d); false; end
|
5
|
+
end
|
6
|
+
|
7
|
+
class TrueStrategy < Flip::AbstractStrategy
|
8
|
+
def knows?(d); true; end
|
9
|
+
def on?(d); true; end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe Flip::FeatureSet do
|
13
|
+
|
14
|
+
let :feature_set_with_null_strategy do
|
15
|
+
Flip::FeatureSet.new.tap do |s|
|
16
|
+
s << Flip::Definition.new(:feature)
|
17
|
+
s.add_strategy NullStrategy
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
let :feature_set_with_null_then_true_strategies do
|
22
|
+
feature_set_with_null_strategy.tap do |s|
|
23
|
+
s.add_strategy TrueStrategy
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe ".instance" do
|
28
|
+
it "returns a singleton instance" do
|
29
|
+
Flip::FeatureSet.instance.should equal(Flip::FeatureSet.instance)
|
30
|
+
end
|
31
|
+
it "can be reset" do
|
32
|
+
instance_before_reset = Flip::FeatureSet.instance
|
33
|
+
Flip::FeatureSet.reset
|
34
|
+
Flip::FeatureSet.instance.should_not equal(instance_before_reset)
|
35
|
+
end
|
36
|
+
it "can be reset multiple times without error" do
|
37
|
+
2.times { Flip::FeatureSet.reset }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "#default= and #on? with null strategy" do
|
42
|
+
subject { feature_set_with_null_strategy }
|
43
|
+
it "defaults to false" do
|
44
|
+
subject.on?(:feature).should be_false
|
45
|
+
end
|
46
|
+
it "can default to true" do
|
47
|
+
subject.default = true
|
48
|
+
subject.on?(:feature).should be_true
|
49
|
+
end
|
50
|
+
it "accepts a proc returning true" do
|
51
|
+
subject.default = proc { true }
|
52
|
+
subject.on?(:feature).should be_true
|
53
|
+
end
|
54
|
+
it "accepts a proc returning false" do
|
55
|
+
subject.default = proc { false }
|
56
|
+
subject.on?(:feature).should be_false
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "feature set with null strategy then always-true strategy" do
|
61
|
+
subject { feature_set_with_null_then_true_strategies }
|
62
|
+
it "returns true due to second strategy" do
|
63
|
+
subject.on?(:feature).should be_true
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
data/spec/flip_spec.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Flip do
|
4
|
+
|
5
|
+
before(:all) do
|
6
|
+
Class.new do
|
7
|
+
extend Flip::Declarable
|
8
|
+
strategy Flip::DeclarationStrategy
|
9
|
+
default false
|
10
|
+
feature :one, default: true
|
11
|
+
feature :two, default: false
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
after(:all) do
|
16
|
+
Flip.reset
|
17
|
+
end
|
18
|
+
|
19
|
+
describe ".on?" do
|
20
|
+
it "returns true for enabled features" do
|
21
|
+
Flip.on?(:one).should be_true
|
22
|
+
end
|
23
|
+
it "returns false for disabled features" do
|
24
|
+
Flip.on?(:two).should be_false
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "dynamic predicate methods" do
|
29
|
+
its(:one?) { should be_true }
|
30
|
+
its(:two?) { should be_false }
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "flip"
|