flip 0.1.0 → 0.2.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/.travis.yml +6 -0
- data/README.md +78 -58
- data/Rakefile +2 -1
- data/config/routes.rb +1 -1
- data/flip.gemspec +1 -1
- data/lib/flip/database_strategy.rb +5 -3
- data/lib/flip/version.rb +1 -1
- data/spec/database_strategy_spec.rb +28 -16
- data/spec/declarable_spec.rb +1 -1
- metadata +24 -27
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a4385dc45cee88a9a76ac1e4a95137f2cbc5a790
|
4
|
+
data.tar.gz: 99d8d801d5a81cf84419a5631cc2072bf2b45453
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 16474ee1caa55c414e4813ae31b16b76a51832b4e9e61ec719641122a087782883d5e19f83deed11c9851fa012648a5d22e7f22a1f3c662ccc62d4ee27c9abb1
|
7
|
+
data.tar.gz: 99c9fc607b5bb2dea98b01b19ec205c9a706a251ca0167cffcf5665306e5039918f368ea66b347980bc9702563d7a6c5e647d0ba3b1451ba9055041ae2e63054
|
data/.travis.yml
ADDED
data/README.md
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
Flip — flip your features
|
2
2
|
================
|
3
3
|
|
4
|
+
[](https://travis-ci.org/pda/flip)
|
5
|
+
|
4
6
|
**Flip** provides a declarative, layered way of enabling and disabling application functionality at run-time.
|
5
7
|
|
6
8
|
This gem optimizes for:
|
@@ -24,7 +26,7 @@ Flip has a dashboard UI that's easy to understand and use.
|
|
24
26
|
Install
|
25
27
|
-------
|
26
28
|
|
27
|
-
**Rails 3.0 and 3.
|
29
|
+
**Rails 3.0, 3.1 and 3.2+**
|
28
30
|
|
29
31
|
# Gemfile
|
30
32
|
gem "flip"
|
@@ -35,36 +37,40 @@ Install
|
|
35
37
|
# Run the migration
|
36
38
|
> rake db:migrate
|
37
39
|
|
40
|
+
# Include the Feature model, e.g. config/initializers/feature.rb:
|
41
|
+
require 'feature'
|
38
42
|
|
39
43
|
Declaring Features
|
40
44
|
------------------
|
41
45
|
|
42
|
-
|
43
|
-
|
44
|
-
|
46
|
+
```ruby
|
47
|
+
# This is the model class generated by rails g flip:install
|
48
|
+
class Feature < ActiveRecord::Base
|
49
|
+
include Flip::Declarable
|
45
50
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
# A basic feature declaration.
|
53
|
-
feature :shiny_things
|
51
|
+
# The recommended Flip strategy stack.
|
52
|
+
strategy Flip::CookieStrategy
|
53
|
+
strategy Flip::DatabaseStrategy
|
54
|
+
strategy Flip::DefaultStrategy
|
55
|
+
default false
|
54
56
|
|
55
|
-
|
56
|
-
|
57
|
+
# A basic feature declaration.
|
58
|
+
feature :shiny_things
|
57
59
|
|
58
|
-
|
59
|
-
|
60
|
-
default: proc { rand(2).zero? }
|
60
|
+
# Override the system-wide default.
|
61
|
+
feature :world_domination, default: true
|
61
62
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
63
|
+
# Enabled half the time..? Sure, we can do that.
|
64
|
+
feature :flakey,
|
65
|
+
default: proc { rand(2).zero? }
|
66
|
+
|
67
|
+
# Provide a description, normally derived from the feature name.
|
68
|
+
feature :something,
|
69
|
+
default: true,
|
70
|
+
description: "Ability to purchase enrollments in courses",
|
71
|
+
|
72
|
+
end
|
73
|
+
```
|
68
74
|
|
69
75
|
|
70
76
|
Checking Features
|
@@ -72,19 +78,23 @@ Checking Features
|
|
72
78
|
|
73
79
|
`Flip.on?` or the dynamic predicate methods are used to check feature state:
|
74
80
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
81
|
+
```ruby
|
82
|
+
Flip.on? :world_domination # true
|
83
|
+
Flip.world_domination? # true
|
84
|
+
|
85
|
+
Flip.on? :shiny_things # false
|
86
|
+
Flip.shiny_things? # false
|
87
|
+
```
|
80
88
|
|
81
89
|
Views and controllers use the `feature?(key)` method:
|
82
90
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
91
|
+
```erb
|
92
|
+
<div>
|
93
|
+
<% if feature? :world_domination %>
|
94
|
+
<%= link_to "Dominate World", world_dominations_path %>
|
95
|
+
<% end %>
|
96
|
+
</div>
|
97
|
+
```
|
88
98
|
|
89
99
|
|
90
100
|
Feature Flipping Controllers
|
@@ -92,17 +102,19 @@ Feature Flipping Controllers
|
|
92
102
|
|
93
103
|
The `Flip::ControllerFilters` module is mixed into the base `ApplicationController` class. The following controller will respond with 404 Page Not Found to all but the `index` action unless the :new_stuff feature is enabled:
|
94
104
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
105
|
+
```ruby
|
106
|
+
class SampleController < ApplicationController
|
107
|
+
|
108
|
+
require_feature :something, :except => :index
|
109
|
+
|
110
|
+
def show
|
111
|
+
end
|
112
|
+
|
113
|
+
def index
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
```
|
106
118
|
|
107
119
|
Dashboard
|
108
120
|
---------
|
@@ -111,32 +123,40 @@ The dashboard provides visibility and control over the features.
|
|
111
123
|
|
112
124
|
The gem includes some basic styles:
|
113
125
|
|
114
|
-
|
115
|
-
|
126
|
+
```haml
|
127
|
+
= content_for :stylesheets_head do
|
128
|
+
= stylesheet_link_tag "flip"
|
129
|
+
```
|
116
130
|
|
117
131
|
You probably don't want the dashboard to be public. Here's one way of implementing access control.
|
118
132
|
|
119
133
|
app/controllers/admin/features_controller.rb:
|
120
134
|
|
121
|
-
|
122
|
-
|
123
|
-
|
135
|
+
```ruby
|
136
|
+
class Admin::FeaturesController < Flip::FeaturesController
|
137
|
+
before_filter :assert_authenticated_as_admin
|
138
|
+
end
|
139
|
+
```
|
124
140
|
|
125
141
|
app/controllers/admin/feature_strategies_controller.rb:
|
126
142
|
|
127
|
-
|
128
|
-
|
129
|
-
|
143
|
+
```ruby
|
144
|
+
class Admin::FeatureStrategiesController < Flip::FeaturesController
|
145
|
+
before_filter :assert_authenticated_as_admin
|
146
|
+
end
|
147
|
+
```
|
130
148
|
|
131
149
|
routes.rb:
|
132
150
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
151
|
+
```ruby
|
152
|
+
namespace :admin do
|
153
|
+
resources :features, only: [ :index ] do
|
154
|
+
resources :feature_strategies, only: [ :update, :destroy ]
|
155
|
+
end
|
156
|
+
end
|
138
157
|
|
139
|
-
|
158
|
+
mount Flip::Engine => "/admin/features"
|
159
|
+
```
|
140
160
|
|
141
161
|
----
|
142
162
|
Created by Paul Annesley
|
data/Rakefile
CHANGED
@@ -6,5 +6,6 @@ task :default => :spec
|
|
6
6
|
|
7
7
|
desc "Run specs"
|
8
8
|
task :spec do
|
9
|
-
|
9
|
+
command = "bundle exec rspec --color --format documentation spec/*_spec.rb"
|
10
|
+
system(command) || raise("specs returned non-zero code")
|
10
11
|
end
|
data/config/routes.rb
CHANGED
data/flip.gemspec
CHANGED
@@ -19,7 +19,7 @@ Gem::Specification.new do |s|
|
|
19
19
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
20
|
s.require_paths = ["lib"]
|
21
21
|
|
22
|
-
s.add_dependency("activesupport", "
|
22
|
+
s.add_dependency("activesupport", ">= 3.0", "< 5")
|
23
23
|
s.add_dependency("i18n")
|
24
24
|
|
25
25
|
s.add_development_dependency("rspec", "~> 2.5")
|
@@ -23,17 +23,19 @@ module Flip
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def switch! key, enable
|
26
|
-
@klass.
|
26
|
+
@klass.where(key: key.to_s).first_or_initialize.update_attributes!(
|
27
|
+
enabled: enable
|
28
|
+
)
|
27
29
|
end
|
28
30
|
|
29
31
|
def delete! key
|
30
|
-
@klass.
|
32
|
+
@klass.where(key: key.to_s).first.try(:destroy)
|
31
33
|
end
|
32
34
|
|
33
35
|
private
|
34
36
|
|
35
37
|
def feature(definition)
|
36
|
-
@klass.
|
38
|
+
@klass.where(key: definition.key.to_s).first
|
37
39
|
end
|
38
40
|
|
39
41
|
end
|
data/lib/flip/version.rb
CHANGED
@@ -2,7 +2,7 @@ require "spec_helper"
|
|
2
2
|
|
3
3
|
describe Flip::DatabaseStrategy do
|
4
4
|
|
5
|
-
let(:definition) { double("definition"
|
5
|
+
let(:definition) { double("definition", key: "one") }
|
6
6
|
let(:strategy) { Flip::DatabaseStrategy.new(model_klass) }
|
7
7
|
let(:model_klass) do
|
8
8
|
Class.new do
|
@@ -20,44 +20,56 @@ describe Flip::DatabaseStrategy do
|
|
20
20
|
its(:switchable?) { should be_true }
|
21
21
|
its(:description) { should be_present }
|
22
22
|
|
23
|
+
let(:db_result) { [] }
|
24
|
+
before do
|
25
|
+
allow(model_klass).to(receive(:where).with(key: "one").and_return(db_result))
|
26
|
+
end
|
27
|
+
|
23
28
|
describe "#knows?" do
|
24
|
-
|
25
|
-
|
26
|
-
|
29
|
+
context "for unknown key" do
|
30
|
+
it "returns true" do
|
31
|
+
expect(strategy.knows?(definition)).to eq(false)
|
32
|
+
end
|
27
33
|
end
|
28
|
-
|
29
|
-
|
30
|
-
|
34
|
+
context "for known key" do
|
35
|
+
let(:db_result) { [disabled_record] }
|
36
|
+
it "returns false" do
|
37
|
+
expect(strategy.knows?(definition)).to eq(true)
|
38
|
+
end
|
31
39
|
end
|
32
40
|
end
|
33
41
|
|
34
42
|
describe "#on?" do
|
35
|
-
|
36
|
-
|
37
|
-
|
43
|
+
context "for an enabled record" do
|
44
|
+
let(:db_result) { [enabled_record] }
|
45
|
+
it "returns true" do
|
46
|
+
expect(strategy.on?(definition)).to eq(true)
|
47
|
+
end
|
38
48
|
end
|
39
|
-
|
40
|
-
|
41
|
-
|
49
|
+
context "for a disabled record" do
|
50
|
+
let(:db_result) { [disabled_record] }
|
51
|
+
it "returns true" do
|
52
|
+
expect(strategy.on?(definition)).to eq(false)
|
53
|
+
end
|
42
54
|
end
|
43
55
|
end
|
44
56
|
|
45
57
|
describe "#switch!" do
|
46
58
|
it "can switch a feature on" do
|
47
|
-
|
59
|
+
expect(db_result).to receive(:first_or_initialize).and_return(disabled_record)
|
48
60
|
disabled_record.should_receive(:update_attributes!).with(enabled: true)
|
49
61
|
strategy.switch! :one, true
|
50
62
|
end
|
51
63
|
it "can switch a feature off" do
|
52
|
-
|
64
|
+
expect(db_result).to receive(:first_or_initialize).and_return(enabled_record)
|
53
65
|
enabled_record.should_receive(:update_attributes!).with(enabled: false)
|
54
66
|
strategy.switch! :one, false
|
55
67
|
end
|
56
68
|
end
|
57
69
|
|
58
70
|
describe "#delete!" do
|
71
|
+
let(:db_result) { [enabled_record] }
|
59
72
|
it "can delete a feature record" do
|
60
|
-
model_klass.should_receive(:find_by_key).with(:one).and_return(enabled_record)
|
61
73
|
enabled_record.should_receive(:try).with(:destroy)
|
62
74
|
strategy.delete! :one
|
63
75
|
end
|
data/spec/declarable_spec.rb
CHANGED
@@ -23,7 +23,7 @@ describe Flip::Declarable do
|
|
23
23
|
it { should be_on(:three) }
|
24
24
|
end
|
25
25
|
context "with default set to true" do
|
26
|
-
before
|
26
|
+
before { model_class.send(:default, true) }
|
27
27
|
it { should be_on(:one) }
|
28
28
|
it { should be_on(:three) }
|
29
29
|
end
|
metadata
CHANGED
@@ -1,80 +1,77 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: flip
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.2.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Paul Annesley
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date: 2013-
|
11
|
+
date: 2013-09-30 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: activesupport
|
16
|
-
prerelease: false
|
17
15
|
requirement: !ruby/object:Gem::Requirement
|
18
16
|
requirements:
|
19
|
-
- -
|
17
|
+
- - '>='
|
20
18
|
- !ruby/object:Gem::Version
|
21
19
|
version: '3.0'
|
22
|
-
|
20
|
+
- - <
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '5'
|
23
23
|
type: :runtime
|
24
|
+
prerelease: false
|
24
25
|
version_requirements: !ruby/object:Gem::Requirement
|
25
26
|
requirements:
|
26
|
-
- -
|
27
|
+
- - '>='
|
27
28
|
- !ruby/object:Gem::Version
|
28
29
|
version: '3.0'
|
29
|
-
|
30
|
+
- - <
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '5'
|
30
33
|
- !ruby/object:Gem::Dependency
|
31
34
|
name: i18n
|
32
|
-
prerelease: false
|
33
35
|
requirement: !ruby/object:Gem::Requirement
|
34
36
|
requirements:
|
35
|
-
- -
|
37
|
+
- - '>='
|
36
38
|
- !ruby/object:Gem::Version
|
37
39
|
version: '0'
|
38
|
-
none: false
|
39
40
|
type: :runtime
|
41
|
+
prerelease: false
|
40
42
|
version_requirements: !ruby/object:Gem::Requirement
|
41
43
|
requirements:
|
42
|
-
- -
|
44
|
+
- - '>='
|
43
45
|
- !ruby/object:Gem::Version
|
44
46
|
version: '0'
|
45
|
-
none: false
|
46
47
|
- !ruby/object:Gem::Dependency
|
47
48
|
name: rspec
|
48
|
-
prerelease: false
|
49
49
|
requirement: !ruby/object:Gem::Requirement
|
50
50
|
requirements:
|
51
51
|
- - ~>
|
52
52
|
- !ruby/object:Gem::Version
|
53
53
|
version: '2.5'
|
54
|
-
none: false
|
55
54
|
type: :development
|
55
|
+
prerelease: false
|
56
56
|
version_requirements: !ruby/object:Gem::Requirement
|
57
57
|
requirements:
|
58
58
|
- - ~>
|
59
59
|
- !ruby/object:Gem::Version
|
60
60
|
version: '2.5'
|
61
|
-
none: false
|
62
61
|
- !ruby/object:Gem::Dependency
|
63
62
|
name: rake
|
64
|
-
prerelease: false
|
65
63
|
requirement: !ruby/object:Gem::Requirement
|
66
64
|
requirements:
|
67
|
-
- -
|
65
|
+
- - '>='
|
68
66
|
- !ruby/object:Gem::Version
|
69
67
|
version: '0'
|
70
|
-
none: false
|
71
68
|
type: :development
|
69
|
+
prerelease: false
|
72
70
|
version_requirements: !ruby/object:Gem::Requirement
|
73
71
|
requirements:
|
74
|
-
- -
|
72
|
+
- - '>='
|
75
73
|
- !ruby/object:Gem::Version
|
76
74
|
version: '0'
|
77
|
-
none: false
|
78
75
|
description: Declarative API for specifying features, switchable in declaration, database
|
79
76
|
and cookies.
|
80
77
|
email:
|
@@ -84,6 +81,7 @@ extensions: []
|
|
84
81
|
extra_rdoc_files: []
|
85
82
|
files:
|
86
83
|
- .gitignore
|
84
|
+
- .travis.yml
|
87
85
|
- Gemfile
|
88
86
|
- README.md
|
89
87
|
- Rakefile
|
@@ -129,27 +127,26 @@ files:
|
|
129
127
|
- spec/spec_helper.rb
|
130
128
|
homepage: https://github.com/pda/flip
|
131
129
|
licenses: []
|
130
|
+
metadata: {}
|
132
131
|
post_install_message:
|
133
132
|
rdoc_options: []
|
134
133
|
require_paths:
|
135
134
|
- lib
|
136
135
|
required_ruby_version: !ruby/object:Gem::Requirement
|
137
136
|
requirements:
|
138
|
-
- -
|
137
|
+
- - '>='
|
139
138
|
- !ruby/object:Gem::Version
|
140
139
|
version: '0'
|
141
|
-
none: false
|
142
140
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
143
141
|
requirements:
|
144
|
-
- -
|
142
|
+
- - '>='
|
145
143
|
- !ruby/object:Gem::Version
|
146
144
|
version: '0'
|
147
|
-
none: false
|
148
145
|
requirements: []
|
149
146
|
rubyforge_project: flip
|
150
|
-
rubygems_version: 1.
|
147
|
+
rubygems_version: 2.1.2
|
151
148
|
signing_key:
|
152
|
-
specification_version:
|
149
|
+
specification_version: 4
|
153
150
|
summary: A feature flipper for Rails web applications.
|
154
151
|
test_files:
|
155
152
|
- spec/abstract_strategy_spec.rb
|