flip2 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +5 -0
  5. data/Gemfile +2 -0
  6. data/README.md +180 -0
  7. data/Rakefile +11 -0
  8. data/TODO +3 -0
  9. data/app/assets/stylesheets/flip.css +70 -0
  10. data/app/controllers/flip/features_controller.rb +47 -0
  11. data/app/controllers/flip/strategies_controller.rb +31 -0
  12. data/app/helpers/flip_helper.rb +9 -0
  13. data/app/views/flip/features/index.html.erb +62 -0
  14. data/config/routes.rb +14 -0
  15. data/flip.gemspec +27 -0
  16. data/lib/flip/abstract_strategy.rb +26 -0
  17. data/lib/flip/cacheable.rb +25 -0
  18. data/lib/flip/controller_filters.rb +25 -0
  19. data/lib/flip/cookie_strategy.rb +67 -0
  20. data/lib/flip/database_strategy.rb +46 -0
  21. data/lib/flip/declarable.rb +24 -0
  22. data/lib/flip/declaration_strategy.rb +20 -0
  23. data/lib/flip/definition.rb +21 -0
  24. data/lib/flip/engine.rb +9 -0
  25. data/lib/flip/facade.rb +18 -0
  26. data/lib/flip/feature_set.rb +57 -0
  27. data/lib/flip/forbidden.rb +7 -0
  28. data/lib/flip/version.rb +3 -0
  29. data/lib/flip2.rb +28 -0
  30. data/lib/generators/flip/install/install_generator.rb +9 -0
  31. data/lib/generators/flip/migration/USAGE +5 -0
  32. data/lib/generators/flip/migration/migration_generator.rb +22 -0
  33. data/lib/generators/flip/migration/templates/create_features.rb +11 -0
  34. data/lib/generators/flip/model/USAGE +8 -0
  35. data/lib/generators/flip/model/model_generator.rb +8 -0
  36. data/lib/generators/flip/model/templates/feature.rb +15 -0
  37. data/lib/generators/flip/routes/USAGE +7 -0
  38. data/lib/generators/flip/routes/routes_generator.rb +7 -0
  39. data/lib/generators/flip/views/USAGE +8 -0
  40. data/lib/generators/flip/views/templates/index.html.erb +54 -0
  41. data/lib/generators/flip/views/views_generator.rb +8 -0
  42. data/spec/abstract_strategy_spec.rb +11 -0
  43. data/spec/cacheable_spec.rb +49 -0
  44. data/spec/controller_filters_spec.rb +27 -0
  45. data/spec/cookie_strategy_spec.rb +112 -0
  46. data/spec/database_strategy_spec.rb +110 -0
  47. data/spec/declarable_spec.rb +32 -0
  48. data/spec/declaration_strategy_spec.rb +39 -0
  49. data/spec/definition_spec.rb +19 -0
  50. data/spec/feature_set_spec.rb +67 -0
  51. data/spec/flip_spec.rb +33 -0
  52. data/spec/spec_helper.rb +2 -0
  53. metadata +172 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 25447912d81c33420156bf08bbbb30b1e82fb60f
4
+ data.tar.gz: 329f735ba5dc4f9b79141be8e6f61e3e52320713
5
+ SHA512:
6
+ metadata.gz: 39ba6d8717253a8b1780c59d0b31bed522ba0ce8408212d80e99c1f33a84f85e0d8ee19dc3cc3da69e69aedca8b8694c45dd113377dd25f028b89016096a7716
7
+ data.tar.gz: f0267a9848aa22d6e43ecfbd8fadd90c0b8dce38572d1e13e6f20a0f8f94d4e2ec8016c3274f067765e62c9622c2bc8c28b2629e83455dd1cea9f99e823bbb43
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.3.0
4
+ - 2.2.4
5
+ - jruby-9.0.5.0
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "https://rubygems.org"
2
+ gemspec
data/README.md ADDED
@@ -0,0 +1,180 @@
1
+ Flip — flip your features
2
+ ================
3
+
4
+ [![Build Status](https://travis-ci.org/pda/flip.png)](https://travis-ci.org/pda/flip)
5
+
6
+ **Flip** provides a declarative, layered way of enabling and disabling application functionality at run-time.
7
+
8
+ This gem optimizes for:
9
+
10
+ * developer ease-of-use,
11
+ * visibility and control for other stakeholders (like marketing); and
12
+ * run-time performance
13
+
14
+ There are three layers of strategies per feature:
15
+
16
+ * default
17
+ * database, to flip features site-wide for all users
18
+ * cookie, to flip features just for you (or someone else)
19
+
20
+ There is also a configurable system-wide default - !Rails.env.production?` works nicely.
21
+
22
+ Flip has a dashboard UI that's easy to understand and use.
23
+
24
+ ![Feature Flipper Dashboard](https://cloud.githubusercontent.com/assets/828243/4934741/a5773568-65a4-11e4-98d8-5e9a32720b2e.png)
25
+
26
+ Install
27
+ -------
28
+
29
+ **Rails 3.0, 3.1 and 3.2+**
30
+
31
+ # Gemfile
32
+ gem "flip"
33
+
34
+ # Generate the model and migration
35
+ > rails g flip:install
36
+
37
+ # Run the migration
38
+ > rake db:migrate
39
+
40
+ # Include the Feature model, e.g. config/initializers/feature.rb:
41
+ require 'feature'
42
+
43
+ Declaring Features
44
+ ------------------
45
+
46
+ ```ruby
47
+ # This is the model class generated by rails g flip:install
48
+ class Feature < ActiveRecord::Base
49
+ extend Flip::Declarable
50
+
51
+ # The recommended Flip strategy stack.
52
+ strategy Flip::CookieStrategy
53
+ strategy Flip::DatabaseStrategy
54
+ strategy Flip::DefaultStrategy
55
+ default false
56
+
57
+ # A basic feature declaration.
58
+ feature :shiny_things
59
+
60
+ # Override the system-wide default.
61
+ feature :world_domination, default: true
62
+
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
+ ```
74
+
75
+
76
+ Checking Features
77
+ -----------------
78
+
79
+ `Flip.on?` or the dynamic predicate methods are used to check feature state:
80
+
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
+ ```
88
+
89
+ Views and controllers use the `feature?(key)` method:
90
+
91
+ ```erb
92
+ <div>
93
+ <% if feature? :world_domination %>
94
+ <%= link_to "Dominate World", world_dominations_path %>
95
+ <% end %>
96
+ </div>
97
+ ```
98
+
99
+
100
+ Feature Flipping Controllers
101
+ ----------------------------
102
+
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 `:something` feature is enabled:
104
+
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
+ ```
118
+
119
+ Dashboard
120
+ ---------
121
+
122
+ The dashboard provides visibility and control over the features.
123
+
124
+ The gem includes some basic styles:
125
+
126
+ ```haml
127
+ = content_for :stylesheets_head do
128
+ = stylesheet_link_tag "flip"
129
+ ```
130
+
131
+ You probably don't want the dashboard to be public. Here's one way of implementing access control.
132
+
133
+ app/controllers/admin/features_controller.rb:
134
+
135
+ ```ruby
136
+ class Admin::FeaturesController < Flip::FeaturesController
137
+ before_action :assert_authenticated_as_admin
138
+ end
139
+ ```
140
+
141
+ app/controllers/admin/strategies_controller.rb:
142
+
143
+ ```ruby
144
+ class Admin::StrategiesController < Flip::StrategiesController
145
+ before_action :assert_authenticated_as_admin
146
+ end
147
+ ```
148
+
149
+ routes.rb:
150
+
151
+ ```ruby
152
+ namespace :admin do
153
+ resources :features, only: [ :index ] do
154
+ resources :strategies, only: [ :update, :destroy ]
155
+ end
156
+ end
157
+
158
+ mount Flip::Engine => "/admin/features"
159
+ ```
160
+
161
+ Cacheable
162
+ ---------
163
+
164
+ You can optimize your feature to ensure that it doesn't make a ton of feature
165
+ calls by adding Cacheable to your model.
166
+ ```ruby
167
+ extend Flip::Cacheable
168
+ ```
169
+
170
+ This will ensure that your features are eager loaded with one call to the database
171
+ instead of every call to Flip#on? generating a call to the database. This is
172
+ helpful if you have a larger Rails application and more than a few features
173
+ defined.
174
+
175
+ To start or reset the cache, just call #start_feature_cache.
176
+
177
+
178
+ ----
179
+ Created by Paul Annesley
180
+ Copyright © 2011-2013 Learnable Pty Ltd, [MIT Licence](http://www.opensource.org/licenses/mit-license.php).
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ desc "Run all tests"
5
+ task :default => :spec
6
+
7
+ desc "Run specs"
8
+ task :spec do
9
+ command = "bundle exec rspec --color --format documentation spec/*_spec.rb"
10
+ system(command) || raise("specs returned non-zero code")
11
+ end
data/TODO ADDED
@@ -0,0 +1,3 @@
1
+ FlipHelper view/controller helper.
2
+ Optimize DatabaseStrategy.
3
+ CookieStrategy::Loader before/after, not around.
@@ -0,0 +1,70 @@
1
+ /* Flip */
2
+
3
+ .flip {
4
+ margin: 0;
5
+ }
6
+
7
+ .flip h1 {
8
+ color:#666666;
9
+ font-size: 229%;
10
+ line-height: 44.928px;
11
+ margin: 13.5px 0;
12
+ }
13
+
14
+ .flip th.name, .flip th.description, .flip th.status {
15
+ visibility: hidden;
16
+ }
17
+
18
+ .flip td.name {
19
+ font-family: Monaco, sans-serif;
20
+ font-weight: bold;
21
+ }
22
+
23
+ .flip td.name, .flip td.description {
24
+ vertical-align: top;
25
+ }
26
+
27
+ .flip th {
28
+ font-weight: normal;
29
+ text-align: left;
30
+ vertical-align: top;
31
+ }
32
+
33
+ .flip th .description {
34
+ font-weight: normal;
35
+ display: block;
36
+ font-size: 80%;
37
+ }
38
+
39
+ .flip th, .flip td {
40
+ padding: 5px 10px;
41
+ width: 160px;
42
+ height: 40px;
43
+ }
44
+
45
+ .flip td.off, .flip td.on, .flip td.pass {
46
+ text-align: center;
47
+ text-transform: capitalize;
48
+ }
49
+
50
+ .flip td.off {
51
+ background-color: #fbb;
52
+ }
53
+
54
+ .flip td.on {
55
+ background-color: #cfc;
56
+ }
57
+
58
+ .flip td.pass {
59
+ background-color: #eef;
60
+ }
61
+
62
+ .flip form {
63
+ display: inline;
64
+ }
65
+
66
+ .flip form input[type=submit] {
67
+ font-size: 80%;
68
+ padding: 2px 5px;
69
+ margin: 0;
70
+ }
@@ -0,0 +1,47 @@
1
+ module Flip
2
+ class FeaturesController < ApplicationController
3
+
4
+ def index
5
+ @p = FeaturesPresenter.new(FeatureSet.instance)
6
+ end
7
+
8
+ class FeaturesPresenter
9
+
10
+ include Flip::Engine.routes.url_helpers
11
+
12
+ def initialize(feature_set)
13
+ @feature_set = feature_set
14
+ end
15
+
16
+ def strategies
17
+ @feature_set.strategies
18
+ end
19
+
20
+ def definitions
21
+ @feature_set.definitions
22
+ end
23
+
24
+ def status(definition)
25
+ @feature_set.on?(definition.key) ? "on" : "off"
26
+ end
27
+
28
+ def default_status(definition)
29
+ @feature_set.default_for(definition) ? "on" : "off"
30
+ end
31
+
32
+ def strategy_status(strategy, definition)
33
+ if strategy.knows? definition
34
+ strategy.on?(definition) ? "on" : "off"
35
+ end
36
+ end
37
+
38
+ def switch_url(strategy, definition)
39
+ feature_strategy_path \
40
+ definition.key,
41
+ strategy.name.underscore
42
+ end
43
+
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,31 @@
1
+ module Flip
2
+ class StrategiesController < ApplicationController
3
+
4
+ include Flip::Engine.routes.url_helpers
5
+
6
+ def update
7
+ strategy.switch! feature_key, turn_on?
8
+ redirect_to features_url
9
+ end
10
+
11
+ def destroy
12
+ strategy.delete! feature_key
13
+ redirect_to features_url
14
+ end
15
+
16
+ private
17
+
18
+ def turn_on?
19
+ params[:commit] == "Switch On"
20
+ end
21
+
22
+ def feature_key
23
+ params[:feature_id].to_sym
24
+ end
25
+
26
+ def strategy
27
+ FeatureSet.instance.strategy(params[:id])
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,9 @@
1
+ # Access to feature-flipping configuration.
2
+ module FlipHelper
3
+
4
+ # Whether the given feature is switched on
5
+ def feature?(key)
6
+ Flip.on? key
7
+ end
8
+
9
+ end
@@ -0,0 +1,62 @@
1
+ <div class="flip">
2
+ <h1>Feature Flippers</h1>
3
+
4
+ <table>
5
+ <thead>
6
+ <th class="name">Feature Name</th>
7
+ <th class="description">Description</th>
8
+ <th class="status">Status</th>
9
+ <% @p.strategies.each do |strategy| %>
10
+ <th>
11
+ <%= strategy.name %>
12
+ <span class="description"><%= strategy.description %></span>
13
+ </th>
14
+ <% end %>
15
+ <th>
16
+ Default
17
+ <span class="description">The system default when no strategies match.</span>
18
+ </th>
19
+ </thead>
20
+ <tbody>
21
+ <% @p.definitions.each do |definition| %>
22
+ <tr>
23
+ <td class="name"><%= definition.name %></td>
24
+
25
+ <td class="description"><%= definition.description %></td>
26
+
27
+ <%= content_tag :td, class: @p.status(definition) do %>
28
+ <%= @p.status definition %>
29
+ <% end %>
30
+
31
+ <% @p.strategies.each do |strategy| %>
32
+ <%= content_tag :td, class: @p.strategy_status(strategy, definition) || "pass" do %>
33
+ <%= @p.strategy_status strategy, definition %>
34
+
35
+ <% if strategy.switchable? %>
36
+ <%= form_tag(@p.switch_url(strategy, definition), method: :put) do %>
37
+ <% unless @p.strategy_status(strategy, definition) == "on" %>
38
+ <%= submit_tag "Switch On" %>
39
+ <% end %>
40
+ <% unless @p.strategy_status(strategy, definition) == "off" %>
41
+ <%= submit_tag "Switch Off" %>
42
+ <% end %>
43
+ <% end %>
44
+ <% unless @p.strategy_status(strategy, definition).blank? %>
45
+ <%= form_tag(@p.switch_url(strategy, definition), method: :delete) do %>
46
+ <%= submit_tag "Delete" %>
47
+ <% end %>
48
+ <% end %>
49
+ <% end %>
50
+
51
+ <% end %>
52
+ <% end %>
53
+
54
+ <%= content_tag :td, class: @p.default_status(definition) do %>
55
+ <%= @p.default_status definition %>
56
+ <% end %>
57
+
58
+ </tr>
59
+ <% end %>
60
+ </tbody>
61
+ </table>
62
+ </div>