flip 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://travis-ci.org/pda/flip.png)](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
|