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 +4 -4
- data/docs/ui/README.md +28 -1
- data/docs/ui/images/configured-ui.png +0 -0
- data/lib/flipper/ui.rb +19 -1
- data/lib/flipper/ui/actions/feature.rb +10 -1
- data/lib/flipper/ui/actions/features.rb +2 -1
- data/lib/flipper/ui/configuration.rb +21 -0
- data/lib/flipper/ui/configuration/option.rb +12 -0
- data/lib/flipper/ui/views/add_feature.erb +1 -1
- data/lib/flipper/ui/views/feature.erb +24 -22
- data/lib/flipper/ui/views/feature_removal_disabled.erb +3 -0
- data/lib/flipper/version.rb +1 -1
- data/spec/flipper/ui/actions/feature_spec.rb +20 -0
- data/spec/flipper/ui/configuration_spec.rb +45 -0
- data/spec/flipper/ui_spec.rb +8 -0
- metadata +10 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 07623fc292dfbcf82da226e3e276dfd81ea3a88d
|
4
|
+
data.tar.gz: '08462fe8a654b112bb37e2a750690350ba809c7e'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
+

|
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
|
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
|
-
|
35
|
+
feature.remove
|
27
36
|
redirect_to '/features'
|
28
37
|
end
|
29
38
|
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
|
@@ -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"
|
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
|
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"
|
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
|
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"
|
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
|
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"
|
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
|
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
|
-
|
196
|
-
<div class="panel-
|
197
|
-
<
|
198
|
-
|
199
|
-
|
200
|
-
<
|
201
|
-
|
202
|
-
|
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
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
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
|
-
|
212
|
+
<% end %>
|
data/lib/flipper/version.rb
CHANGED
@@ -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
|
data/spec/flipper/ui_spec.rb
CHANGED
@@ -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.
|
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:
|
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.
|
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.
|
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
|