flipper-ui 0.11.0 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: aee5a23a8c95d9e413f5e8ed6c4b6c6b1d38abc0
4
- data.tar.gz: c5a37136c9775aa074bef533f6ebda3d8f1af81f
3
+ metadata.gz: 07623fc292dfbcf82da226e3e276dfd81ea3a88d
4
+ data.tar.gz: '08462fe8a654b112bb37e2a750690350ba809c7e'
5
5
  SHA512:
6
- metadata.gz: bcf40fc34509dceced76f8753ea7a404d1ac0d98805044ee94a9518deff1701c7fb69c085a99f82e660224cd3e6dbf15c29050ab8294443d2fe80fb75678ce80
7
- data.tar.gz: f1b1bffd6a61049e8d4e05c407868ef6d64113f59c38a70a089c5e72875dd4e3f33531e661b6a3cfbd8824d3a0f45f759d86537a2dcb6fe98db56c8e89fa307a
6
+ metadata.gz: 7c213e63ff643e4877f9a782722bdb7d69983273cbfd01ca5af0a494fff9875c6a93530e72e73a1d06bbc2c3170382171566f0e91d65d43c195f625d48121602
7
+ data.tar.gz: ac3bcc212f9299d3cbdbd58b09014eeda7f043d3fa9f1ad906d5b4d1b64dae6110a0072b25783858d26bc8b5cf34300e910a9ef0953494a6b2405ef40f28d43c
data/docs/ui/README.md CHANGED
@@ -52,7 +52,7 @@ end
52
52
 
53
53
  #### Security
54
54
 
55
- You almost certainly want to limit access when using Flipper::UI in production.
55
+ You almost certainly want to limit access when using Flipper::UI in production.
56
56
 
57
57
  ##### Basic Authentication via Rack
58
58
  The `Flipper::UI.app` method yields a builder instance prior to any predefined middleware. You can insert the `Rack::Auth::Basic` middleware, that'll prompt for a username and password when visiting the defined (i.e., `/flipper`) route.
@@ -125,6 +125,33 @@ RuntimeError: you need to set up a session middleware *before* Rack::Protection:
125
125
 
126
126
  See [examples/ui/basic.ru](https://github.com/jnunemaker/flipper/blob/master/examples/ui/basic.ru) for a more full example
127
127
 
128
+ ### Configuration
129
+
130
+ Flipper UI can be customized via `configure`, which yields a configuration instance for setting the text on the five main sections of the UI feature view.
131
+
132
+ * `config.actors`
133
+ * `config.groups`
134
+ * `config.percentage_of_actors`
135
+ * `config.percentage_of_time`
136
+ * `config.delete`
137
+
138
+ Each of these methods returns a [Flipper::UI::Option](https://github.com/jnunemaker/flipper/blob/master/lib/flipper/ui/configuration/option.rb) that responds to `title=`, `description=` as seen below.
139
+
140
+ *e.g. customzing the percentage_of_actors and delete sections' titles and descriptions*
141
+ ```ruby
142
+ Flipper::UI.configure do |config|
143
+ config.percentage_of_actors.title = "My Custom Title"
144
+ config.percentage_of_actors.description = "My custom description"
145
+
146
+ config.delete.title = "BE VERY CAREFUL!"
147
+ config.delete.description = "YOU'VE BEEN WARNED!"
148
+ end
149
+ ```
150
+
151
+ results in:
152
+
153
+ ![configure](images/configured-ui.png)
154
+
128
155
  ## Contributing
129
156
 
130
157
  1. Fork it
Binary file
data/lib/flipper/ui.rb CHANGED
@@ -12,6 +12,7 @@ require 'flipper'
12
12
  require 'flipper/middleware/setup_env'
13
13
  require 'flipper/middleware/memoizer'
14
14
  require 'flipper/ui/middleware'
15
+ require 'flipper/ui/configuration'
15
16
 
16
17
  module Flipper
17
18
  module UI
@@ -23,11 +24,19 @@ module Flipper
23
24
 
24
25
  # Public: Is feature creation allowed from the UI? Defaults to true. If
25
26
  # set to false, users of the UI cannot create features. All feature
26
- # creation will need to be done through the conigured flipper instance.
27
+ # creation will need to be done through the configured flipper instance.
27
28
  attr_accessor :feature_creation_enabled
29
+
30
+ # Public: Is feature deletion allowed from the UI? Defaults to true. If
31
+ # set to false, users won't be able to delete features from the UI.
32
+ attr_accessor :feature_removal_enabled
33
+
34
+ # Public: Set attributes on this instance to customize UI text
35
+ attr_reader :configuration
28
36
  end
29
37
 
30
38
  self.feature_creation_enabled = true
39
+ self.feature_removal_enabled = true
31
40
 
32
41
  def self.root
33
42
  @root ||= Pathname(__FILE__).dirname.expand_path.join('ui')
@@ -49,5 +58,14 @@ module Flipper
49
58
  builder.define_singleton_method(:inspect) { klass.inspect } # pretty rake routes output
50
59
  builder
51
60
  end
61
+
62
+ # Public: yields configuration instance for customizing UI text
63
+ def self.configure
64
+ yield(configuration)
65
+ end
66
+
67
+ def self.configuration
68
+ @configuration ||= ::Flipper::UI::Configuration.new
69
+ end
52
70
  end
53
71
  end
@@ -21,9 +21,18 @@ module Flipper
21
21
  end
22
22
 
23
23
  def delete
24
+ unless Flipper::UI.feature_removal_enabled
25
+ status 403
26
+
27
+ breadcrumb 'Home', '/'
28
+ breadcrumb 'Features', '/features'
29
+
30
+ halt view_response(:feature_removal_disabled)
31
+ end
32
+
24
33
  feature_name = Rack::Utils.unescape(request.path.split('/').last)
25
34
  feature = flipper[feature_name]
26
- flipper.adapter.remove(feature)
35
+ feature.remove
27
36
  redirect_to '/features'
28
37
  end
29
38
  end
@@ -40,7 +40,8 @@ module Flipper
40
40
  redirect_to("/features/new?error=#{error}")
41
41
  end
42
42
 
43
- flipper.adapter.add(flipper[value])
43
+ feature = flipper[value]
44
+ feature.add
44
45
 
45
46
  redirect_to "/features/#{Rack::Utils.escape_path(value)}"
46
47
  end
@@ -0,0 +1,21 @@
1
+ require 'flipper/ui/configuration/option'
2
+
3
+ module Flipper
4
+ module UI
5
+ class Configuration
6
+ attr_reader :actors,
7
+ :delete,
8
+ :groups,
9
+ :percentage_of_actors,
10
+ :percentage_of_time
11
+
12
+ def initialize
13
+ @actors = Option.new("Actors", "Enable actors using the form above.")
14
+ @groups = Option.new("Groups", "Enable groups using the form above.")
15
+ @percentage_of_actors = Option.new("Percentage of Actors", "Percentage of actors functions independently of percentage of time. If you enable 50% of Actors and 25% of Time then the feature will always be enabled for 50% of users and occasionally enabled 25% of the time for everyone.") # rubocop:disable Metrics/LineLength
16
+ @percentage_of_time = Option.new("Percentage of Time", "Percentage of actors functions independently of percentage of time. If you enable 50% of Actors and 25% of Time then the feature will always be enabled for 50% of users and occasionally enabled 25% of the time for everyone.") # rubocop:disable Metrics/LineLength
17
+ @delete = Option.new("Danger Zone", "Deleting a feature removes it from the list of features and disables it for everyone.") # rubocop:disable Metrics/LineLength
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,12 @@
1
+ module Flipper
2
+ module UI
3
+ class Option
4
+ attr_accessor :title, :description
5
+
6
+ def initialize(title, description)
7
+ @title = title
8
+ @description = description
9
+ end
10
+ end
11
+ end
12
+ end
@@ -11,7 +11,7 @@
11
11
  <div class="panel-body">
12
12
  <form action="<%= script_name %>/features" method="post">
13
13
  <%== csrf_input_tag %>
14
- <input type="text" name="value" size="30" placeholder="ie: search, new_pricing, etc.">
14
+ <input type="text" name="value" size="30" placeholder="ie: search, new_pricing, etc." autofocus>
15
15
  <input type="submit" value="Add Feature" class="btn">
16
16
  </form>
17
17
  <p class="help">
@@ -37,7 +37,7 @@
37
37
  <div class="column one-half">
38
38
  <div class="panel panel-default">
39
39
  <div class="panel-heading">
40
- <h3 class="panel-title">Percentage of Actors</h3>
40
+ <h3 class="panel-title"><%= Flipper::UI.configuration.percentage_of_actors.title %></h3>
41
41
  </div>
42
42
  <div class="panel-body">
43
43
  <form action="<%= script_name %>/features/<%= @feature.key %>/percentage_of_actors" method="post">
@@ -56,14 +56,14 @@
56
56
  <input type="text" name="value" <% if @feature.percentage_of_actors_value > 0 %>value="<%= @feature.percentage_of_actors_value %>"<% end %> placeholder="custom (ie: 26, 32, etc.)" class="input-mini">
57
57
  <input type="submit" name="action" value="Enable" class="btn btn-sm">
58
58
  </form>
59
- <p class="help"><small>Percentage of actors functions independently of percentage of time. If you enable 50% of Actors and 25% of Time then the feature will always be enabled for 50% of users and occasionally enabled 25% of the time for everyone.</small></p>
59
+ <p class="help"><small><%= Flipper::UI.configuration.percentage_of_actors.description %></small></p>
60
60
  </div>
61
61
  </div>
62
62
  </div>
63
63
  <div class="column one-half">
64
64
  <div class="panel panel-default">
65
65
  <div class="panel-heading">
66
- <h3 class="panel-title">Percentage of Time</h3>
66
+ <h3 class="panel-title"><%= Flipper::UI.configuration.percentage_of_time.title %></h3>
67
67
  </div>
68
68
  <div class="panel-body">
69
69
  <form action="<%= script_name %>/features/<%= @feature.key %>/percentage_of_time" method="post">
@@ -83,7 +83,7 @@
83
83
  <input type="submit" name="action" value="Enable" class="btn btn-sm">
84
84
  </form>
85
85
 
86
- <p class="help"><small>Percentage of time functions independently of percentage of actors. If you enable 50% of Actors and 25% of Time then the feature will always be enabled for 50% of users and occasionally enabled 25% of the time for everyone.</small></p>
86
+ <p class="help"><small><%= Flipper::UI.configuration.actors.description %></small></p>
87
87
  </div>
88
88
  </div>
89
89
  </div>
@@ -110,7 +110,7 @@
110
110
  <input type="submit" value="Add Group" class="btn btn-sm">
111
111
  </form>
112
112
  <% end %>
113
- <h3 class="panel-title">Groups</h3>
113
+ <h3 class="panel-title"><%= Flipper::UI.configuration.groups.title %></h3>
114
114
  </div>
115
115
  <% if @feature.groups_value.empty? %>
116
116
  <div class="blankslate">
@@ -118,7 +118,7 @@
118
118
  <span class="mega-octicon octicon-squirrel"></span>
119
119
  <span class="mega-octicon octicon-zap"></span>
120
120
  <h3>No Enabled Groups</h3>
121
- <p>Enable groups using the form above.</p>
121
+ <p><%= Flipper::UI.configuration.groups.description %></p>
122
122
  </div>
123
123
  <% else %>
124
124
  <ul class="list-group">
@@ -154,7 +154,7 @@
154
154
  <input type="text" name="value" placeholder="ie: User:6" class="input-mini">
155
155
  <input type="submit" value="Add Actor" class="btn btn-sm">
156
156
  </form>
157
- <h3 class="panel-title">Actors</h3>
157
+ <h3 class="panel-title"><%= Flipper::UI.configuration.actors.title %></h3>
158
158
  </div>
159
159
  <% if @feature.actors_value.empty? %>
160
160
  <div class="blankslate">
@@ -162,7 +162,7 @@
162
162
  <span class="mega-octicon octicon-squirrel"></span>
163
163
  <span class="mega-octicon octicon-zap"></span>
164
164
  <h3>No Enabled Actors</h3>
165
- <p>Enable actors using the form above.</p>
165
+ <p><%= Flipper::UI.configuration.actors.description %></p>
166
166
  </div>
167
167
  <% else %>
168
168
  <ul class="list-group">
@@ -192,19 +192,21 @@
192
192
  </div>
193
193
  </div>
194
194
 
195
- <div class="panel panel-danger">
196
- <div class="panel-heading">
197
- <h3 class="panel-title">Danger Zone</h3>
198
- </div>
199
- <div class="panel-body">
200
- <p>
201
- Deleting a feature removes it from the list of features and disables it for everyone.
202
- </p>
195
+ <% if Flipper::UI.feature_removal_enabled %>
196
+ <div class="panel panel-danger">
197
+ <div class="panel-heading">
198
+ <h3 class="panel-title"><%= Flipper::UI.configuration.delete.title %></h3>
199
+ </div>
200
+ <div class="panel-body">
201
+ <p>
202
+ <%= Flipper::UI.configuration.delete.description %>
203
+ </p>
203
204
 
204
- <form action="<%= script_name %>/features/<%= @feature.key %>" method="post" onsubmit="return confirm('Are you sure you want to remove <%= @feature.key %> from the list of features and disable it for everyone?')">
205
- <%== csrf_input_tag %>
206
- <input type="hidden" name="_method" value="DELETE">
207
- <button type="submit" name="action" value="Delete" class="btn btn-danger tooltipped tooltipped-ne" aria-label="Remove feature from list of features and disable it.">Delete</button>
208
- </form>
205
+ <form action="<%= script_name %>/features/<%= @feature.key %>" method="post" onsubmit="return confirm('Are you sure you want to remove <%= @feature.key %> from the list of features and disable it for everyone?')">
206
+ <%== csrf_input_tag %>
207
+ <input type="hidden" name="_method" value="DELETE">
208
+ <button type="submit" name="action" value="Delete" class="btn btn-danger tooltipped tooltipped-ne" aria-label="Remove feature from list of features and disable it.">Delete</button>
209
+ </form>
210
+ </div>
209
211
  </div>
210
- </div>
212
+ <% end %>
@@ -0,0 +1,3 @@
1
+ <div class="flash flash-error">
2
+ Feature removal from the UI is disabled. To enable, you'll need to set <code>Flipper::UI.feature_removal_enabled = true</code> wherever flipper is running from.
3
+ </div>
@@ -1,3 +1,3 @@
1
1
  module Flipper
2
- VERSION = '0.11.0'.freeze
2
+ VERSION = '0.12.0'.freeze
3
3
  end
@@ -28,6 +28,26 @@ RSpec.describe Flipper::UI::Actions::Feature do
28
28
  expect(last_response.status).to be(302)
29
29
  expect(last_response.headers['Location']).to eq('/features')
30
30
  end
31
+
32
+ context 'when feature_removal_enabled is set to false' do
33
+ around do |example|
34
+ begin
35
+ @original_feature_removal_enabled = Flipper::UI.feature_removal_enabled
36
+ Flipper::UI.feature_removal_enabled = false
37
+ example.run
38
+ ensure
39
+ Flipper::UI.feature_removal_enabled = @original_feature_removal_enabled
40
+ end
41
+ end
42
+
43
+ it 'returns with 403 status' do
44
+ expect(last_response.status).to be(403)
45
+ end
46
+
47
+ it 'renders feature removal disabled template' do
48
+ expect(last_response.body).to include('Feature removal from the UI is disabled')
49
+ end
50
+ end
31
51
  end
32
52
 
33
53
  describe 'POST /features/:feature with _method=DELETE' do
@@ -0,0 +1,45 @@
1
+ require 'helper'
2
+
3
+ RSpec.describe Flipper::UI::Configuration do
4
+ let(:configuration) { described_class.new }
5
+
6
+ describe "#actors" do
7
+ it "has default text" do
8
+ expect(configuration.actors.title).to eq("Actors")
9
+ expect(configuration.actors.description).to eq("Enable actors using the form above.")
10
+ end
11
+
12
+ it "can be updated" do
13
+ configuration.actors.title = "Actors Section"
14
+ expect(configuration.actors.title).to eq("Actors Section")
15
+ end
16
+ end
17
+
18
+ describe "#groups" do
19
+ it "has default text" do
20
+ expect(configuration.groups.title).to eq("Groups")
21
+ expect(configuration.groups.description).to eq("Enable groups using the form above.")
22
+ end
23
+ end
24
+
25
+ describe "#percentage_of_actors" do
26
+ it "has default text" do
27
+ expect(configuration.percentage_of_actors.title).to eq("Percentage of Actors")
28
+ expect(configuration.percentage_of_actors.description).to eq("Percentage of actors functions independently of percentage of time. If you enable 50% of Actors and 25% of Time then the feature will always be enabled for 50% of users and occasionally enabled 25% of the time for everyone.") # rubocop:disable Metrics/LineLength
29
+ end
30
+ end
31
+
32
+ describe "#percentage_of_time" do
33
+ it "has default text" do
34
+ expect(configuration.percentage_of_time.title).to eq("Percentage of Time")
35
+ expect(configuration.percentage_of_time.description).to eq("Percentage of actors functions independently of percentage of time. If you enable 50% of Actors and 25% of Time then the feature will always be enabled for 50% of users and occasionally enabled 25% of the time for everyone.") # rubocop:disable Metrics/LineLength
36
+ end
37
+ end
38
+
39
+ describe "#delete" do
40
+ it "has default text" do
41
+ expect(configuration.delete.title).to eq("Danger Zone")
42
+ expect(configuration.delete.description).to eq("Deleting a feature removes it from the list of features and disables it for everyone.") # rubocop:disable Metrics/LineLength
43
+ end
44
+ end
45
+ end
@@ -146,4 +146,12 @@ RSpec.describe Flipper::UI do
146
146
  described_class.feature_creation_enabled = @original_feature_creation_enabled
147
147
  end
148
148
  end
149
+
150
+ describe 'configure' do
151
+ it 'yields configuration instance' do
152
+ described_class.configure do |config|
153
+ expect(config).to be_instance_of(Flipper::UI::Configuration)
154
+ end
155
+ end
156
+ end
149
157
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flipper-ui
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.0
4
+ version: 0.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Nunemaker
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-11-16 00:00:00.000000000 Z
11
+ date: 2018-01-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -56,14 +56,14 @@ dependencies:
56
56
  requirements:
57
57
  - - "~>"
58
58
  - !ruby/object:Gem::Version
59
- version: 0.11.0
59
+ version: 0.12.0
60
60
  type: :runtime
61
61
  prerelease: false
62
62
  version_requirements: !ruby/object:Gem::Requirement
63
63
  requirements:
64
64
  - - "~>"
65
65
  - !ruby/object:Gem::Version
66
- version: 0.11.0
66
+ version: 0.12.0
67
67
  - !ruby/object:Gem::Dependency
68
68
  name: erubis
69
69
  requirement: !ruby/object:Gem::Requirement
@@ -87,6 +87,7 @@ extensions: []
87
87
  extra_rdoc_files: []
88
88
  files:
89
89
  - docs/ui/README.md
90
+ - docs/ui/images/configured-ui.png
90
91
  - docs/ui/images/feature.png
91
92
  - docs/ui/images/features.png
92
93
  - examples/ui/basic.ru
@@ -203,6 +204,8 @@ files:
203
204
  - lib/flipper/ui/assets/stylesheets/primer/_utility.scss
204
205
  - lib/flipper/ui/assets/stylesheets/primer/_variables.scss
205
206
  - lib/flipper/ui/assets/stylesheets/primer/primer.scss
207
+ - lib/flipper/ui/configuration.rb
208
+ - lib/flipper/ui/configuration/option.rb
206
209
  - lib/flipper/ui/decorators/feature.rb
207
210
  - lib/flipper/ui/decorators/gate.rb
208
211
  - lib/flipper/ui/error.rb
@@ -251,6 +254,7 @@ files:
251
254
  - lib/flipper/ui/views/add_group.erb
252
255
  - lib/flipper/ui/views/feature.erb
253
256
  - lib/flipper/ui/views/feature_creation_disabled.erb
257
+ - lib/flipper/ui/views/feature_removal_disabled.erb
254
258
  - lib/flipper/ui/views/features.erb
255
259
  - lib/flipper/ui/views/layout.erb
256
260
  - lib/flipper/version.rb
@@ -266,6 +270,7 @@ files:
266
270
  - spec/flipper/ui/actions/home_spec.rb
267
271
  - spec/flipper/ui/actions/percentage_of_actors_gate_spec.rb
268
272
  - spec/flipper/ui/actions/percentage_of_time_gate_spec.rb
273
+ - spec/flipper/ui/configuration_spec.rb
269
274
  - spec/flipper/ui/decorators/feature_spec.rb
270
275
  - spec/flipper/ui/decorators/gate_spec.rb
271
276
  - spec/flipper/ui/util_spec.rb
@@ -307,6 +312,7 @@ test_files:
307
312
  - spec/flipper/ui/actions/home_spec.rb
308
313
  - spec/flipper/ui/actions/percentage_of_actors_gate_spec.rb
309
314
  - spec/flipper/ui/actions/percentage_of_time_gate_spec.rb
315
+ - spec/flipper/ui/configuration_spec.rb
310
316
  - spec/flipper/ui/decorators/feature_spec.rb
311
317
  - spec/flipper/ui/decorators/gate_spec.rb
312
318
  - spec/flipper/ui/util_spec.rb