flipper-ui 0.11.0 → 0.12.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 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