flip2 1.1.1

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