flipper-ui 0.21.0 → 0.23.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/examples/ui/authorization.ru +11 -49
- data/examples/ui/basic.ru +10 -10
- data/flipper-ui.gemspec +1 -0
- data/lib/flipper/ui/action.rb +50 -1
- data/lib/flipper/ui/actions/actors_gate.rb +11 -8
- data/lib/flipper/ui/actions/features.rb +2 -2
- data/lib/flipper/ui/actions/file.rb +1 -1
- data/lib/flipper/ui/actions/groups_gate.rb +1 -1
- data/lib/flipper/ui/actions/percentage_of_actors_gate.rb +1 -1
- data/lib/flipper/ui/actions/percentage_of_time_gate.rb +1 -1
- data/lib/flipper/ui/configuration.rb +6 -0
- data/lib/flipper/ui/decorators/feature.rb +3 -3
- data/lib/flipper/ui/middleware.rb +2 -1
- data/lib/flipper/ui/public/css/application.css +7 -0
- data/lib/flipper/ui/views/add_actor.erb +1 -1
- data/lib/flipper/ui/views/feature.erb +6 -6
- data/lib/flipper/ui/views/features.erb +2 -2
- data/lib/flipper/ui/views/layout.erb +5 -6
- data/lib/flipper/ui.rb +2 -2
- data/lib/flipper/version.rb +1 -1
- data/spec/flipper/ui/action_spec.rb +0 -2
- data/spec/flipper/ui/actions/actors_gate_spec.rb +70 -7
- data/spec/flipper/ui/actions/add_feature_spec.rb +0 -2
- data/spec/flipper/ui/actions/boolean_gate_spec.rb +18 -2
- data/spec/flipper/ui/actions/feature_spec.rb +18 -2
- data/spec/flipper/ui/actions/features_spec.rb +16 -5
- data/spec/flipper/ui/actions/file_spec.rb +0 -12
- data/spec/flipper/ui/actions/groups_gate_spec.rb +20 -5
- data/spec/flipper/ui/actions/home_spec.rb +0 -2
- data/spec/flipper/ui/actions/percentage_of_actors_gate_spec.rb +18 -3
- data/spec/flipper/ui/actions/percentage_of_time_gate_spec.rb +18 -3
- data/spec/flipper/ui/configuration_spec.rb +0 -2
- data/spec/flipper/ui/decorators/feature_spec.rb +0 -2
- data/spec/flipper/ui/decorators/gate_spec.rb +0 -1
- data/spec/flipper/ui/util_spec.rb +0 -1
- data/spec/flipper/ui_spec.rb +0 -15
- metadata +19 -18
- data/docs/ui/README.md +0 -190
- data/docs/ui/images/banner.png +0 -0
- data/docs/ui/images/description.png +0 -0
- data/docs/ui/images/feature.png +0 -0
- data/docs/ui/images/features.png +0 -0
- data/lib/flipper/ui/public/octicons/LICENSE.txt +0 -9
- data/lib/flipper/ui/public/octicons/README.md +0 -1
- data/lib/flipper/ui/public/octicons/octicons-local.ttf +0 -0
- data/lib/flipper/ui/public/octicons/octicons.css +0 -236
- data/lib/flipper/ui/public/octicons/octicons.eot +0 -0
- data/lib/flipper/ui/public/octicons/octicons.svg +0 -200
- data/lib/flipper/ui/public/octicons/octicons.ttf +0 -0
- data/lib/flipper/ui/public/octicons/octicons.woff +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3a6e8757e6a2726e4cf55323286a04caf186af13c6841a4e8b82b813b3c69f2f
|
4
|
+
data.tar.gz: 9ea49e24c52b723f7385da8c90dcae9daf96bef269f0b0e6066df1bc11065411
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c7e435cc10e1b05c2282c022d977666479de870dc14cad3914e19e17acb6eacaf644fe37bf751f2931fb26525079a07b00b374f4815afdd10ab0e4192448ad27
|
7
|
+
data.tar.gz: a3a8fa7a1be586667573b98ee311a157f23591ee4f7bad15b1638aa25a1ec6444f75c909f90cd331d9cd17d8ba8869367755c299a9798e6eedef8583c2833dc7
|
@@ -5,50 +5,13 @@
|
|
5
5
|
# http://localhost:9999/
|
6
6
|
#
|
7
7
|
require 'bundler/setup'
|
8
|
-
require "logger"
|
9
|
-
|
10
8
|
require "flipper/ui"
|
11
9
|
require "flipper/adapters/pstore"
|
12
|
-
require "active_support/notifications"
|
13
10
|
|
14
11
|
Flipper.register(:admins) { |actor|
|
15
12
|
actor.respond_to?(:admin?) && actor.admin?
|
16
13
|
}
|
17
14
|
|
18
|
-
Flipper.register(:early_access) { |actor|
|
19
|
-
actor.respond_to?(:early?) && actor.early?
|
20
|
-
}
|
21
|
-
|
22
|
-
# Setup logging of flipper calls.
|
23
|
-
if ENV["LOG"] == "1"
|
24
|
-
$logger = Logger.new(STDOUT)
|
25
|
-
require "flipper/instrumentation/log_subscriber"
|
26
|
-
Flipper::Instrumentation::LogSubscriber.logger = $logger
|
27
|
-
end
|
28
|
-
|
29
|
-
adapter = Flipper::Adapters::PStore.new
|
30
|
-
flipper = Flipper.new(adapter, instrumenter: ActiveSupport::Notifications)
|
31
|
-
|
32
|
-
Flipper::UI.configure do |config|
|
33
|
-
# config.banner_text = 'Production Environment'
|
34
|
-
# config.banner_class = 'danger'
|
35
|
-
config.feature_creation_enabled = true
|
36
|
-
config.feature_removal_enabled = true
|
37
|
-
# config.show_feature_description_in_list = true
|
38
|
-
config.descriptions_source = lambda do |_keys|
|
39
|
-
{
|
40
|
-
"search_performance_another_long_thing" => "Just to test feature name length.",
|
41
|
-
"gauges_tracking" => "Should we track page views with gaug.es.",
|
42
|
-
"unused" => "Not used.",
|
43
|
-
"suits" => "Are suits necessary in business?",
|
44
|
-
"secrets" => "Secrets are lies.",
|
45
|
-
"logging" => "Log all the things.",
|
46
|
-
"new_cache" => "Like the old cache but newer.",
|
47
|
-
"a/b" => "Why would someone use a slash? I don't know but someone did. Let's make this really long so they regret using slashes. Please don't use slashes.",
|
48
|
-
}
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
15
|
# Example middleware to allow reading the Flipper UI but nothing else.
|
53
16
|
class FlipperReadOnlyMiddleware
|
54
17
|
def initialize(app)
|
@@ -67,18 +30,17 @@ class FlipperReadOnlyMiddleware
|
|
67
30
|
end
|
68
31
|
|
69
32
|
# You can uncomment these to get some default data:
|
70
|
-
#
|
71
|
-
#
|
72
|
-
#
|
73
|
-
#
|
74
|
-
#
|
75
|
-
#
|
76
|
-
#
|
77
|
-
#
|
78
|
-
#
|
79
|
-
|
80
|
-
|
81
|
-
run Flipper::UI.app(flipper) { |builder|
|
33
|
+
# Flipper.enable(:search_performance_another_long_thing)
|
34
|
+
# Flipper.disable(:gauges_tracking)
|
35
|
+
# Flipper.disable(:unused)
|
36
|
+
# Flipper.enable_actor(:suits, Flipper::Actor.new('1'))
|
37
|
+
# Flipper.enable_actor(:suits, Flipper::Actor.new('6'))
|
38
|
+
# Flipper.enable_group(:secrets, :admins)
|
39
|
+
# Flipper.enable_percentage_of_time(:logging, 5)
|
40
|
+
# Flipper.enable_percentage_of_actors(:new_cache, 15)
|
41
|
+
# Flipper.add("a/b")
|
42
|
+
|
43
|
+
run Flipper::UI.app { |builder|
|
82
44
|
builder.use Rack::Session::Cookie, secret: "_super_secret"
|
83
45
|
builder.use FlipperReadOnlyMiddleware
|
84
46
|
}
|
data/examples/ui/basic.ru
CHANGED
@@ -42,16 +42,16 @@ Flipper::UI.configure do |config|
|
|
42
42
|
end
|
43
43
|
|
44
44
|
# You can uncomment these to get some default data:
|
45
|
-
#
|
46
|
-
#
|
47
|
-
#
|
48
|
-
#
|
49
|
-
#
|
50
|
-
#
|
51
|
-
#
|
52
|
-
#
|
53
|
-
#
|
54
|
-
#
|
45
|
+
# Flipper.enable(:search_performance_another_long_thing)
|
46
|
+
# Flipper.disable(:gauges_tracking)
|
47
|
+
# Flipper.disable(:unused)
|
48
|
+
# Flipper.enable_actor(:suits, Flipper::Actor.new('1'))
|
49
|
+
# Flipper.enable_actor(:suits, Flipper::Actor.new('6'))
|
50
|
+
# Flipper.enable_group(:secrets, :admins)
|
51
|
+
# Flipper.enable_group(:secrets, :early_access)
|
52
|
+
# Flipper.enable_percentage_of_time(:logging, 5)
|
53
|
+
# Flipper.enable_percentage_of_actors(:new_cache, 15)
|
54
|
+
# Flipper.add("a/b")
|
55
55
|
|
56
56
|
run Flipper::UI.app { |builder|
|
57
57
|
builder.use Rack::Session::Cookie, secret: "_super_secret"
|
data/flipper-ui.gemspec
CHANGED
data/lib/flipper/ui/action.rb
CHANGED
@@ -3,6 +3,7 @@ require 'flipper/ui/configuration'
|
|
3
3
|
require 'flipper/ui/error'
|
4
4
|
require 'erubi'
|
5
5
|
require 'json'
|
6
|
+
require 'sanitize'
|
6
7
|
|
7
8
|
module Flipper
|
8
9
|
module UI
|
@@ -26,6 +27,36 @@ module Flipper
|
|
26
27
|
'delete'.freeze,
|
27
28
|
]).freeze
|
28
29
|
|
30
|
+
SOURCES = {
|
31
|
+
bootstrap_css: {
|
32
|
+
src: 'https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/css/bootstrap.min.css'.freeze,
|
33
|
+
hash: 'sha384-B0vP5xmATw1+K9KRQjQERJvTumQW0nPEzvF6L/Z6nronJ3oUOFUFpCjEUQouq2+l'.freeze
|
34
|
+
}.freeze,
|
35
|
+
jquery_js: {
|
36
|
+
src: 'https://code.jquery.com/jquery-3.6.0.slim.js'.freeze,
|
37
|
+
hash: 'sha256-HwWONEZrpuoh951cQD1ov2HUK5zA5DwJ1DNUXaM6FsY='.freeze
|
38
|
+
}.freeze,
|
39
|
+
popper_js: {
|
40
|
+
src: 'https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js'.freeze,
|
41
|
+
hash: 'sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q'.freeze
|
42
|
+
}.freeze,
|
43
|
+
bootstrap_js: {
|
44
|
+
src: 'https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/js/bootstrap.min.js'.freeze,
|
45
|
+
hash: 'sha384-+YQ4JLhjyBLPDQt//I+STsc9iw4uQqACwlvpslubQzn4u2UU2UFM80nGisd026JF'.freeze
|
46
|
+
}.freeze
|
47
|
+
}.freeze
|
48
|
+
SCRIPT_SRCS = SOURCES.values_at(:jquery_js, :popper_js, :bootstrap_js).map { |s| s[:src] }
|
49
|
+
STYLE_SRCS = SOURCES.values_at(:bootstrap_css).map { |s| s[:src] }
|
50
|
+
CONTENT_SECURITY_POLICY = <<-CSP.delete("\n")
|
51
|
+
default-src 'none';
|
52
|
+
img-src 'self';
|
53
|
+
font-src 'self';
|
54
|
+
script-src 'report-sample' 'self' #{SCRIPT_SRCS.join(' ')};
|
55
|
+
style-src 'self' 'unsafe-inline' #{STYLE_SRCS.join(' ')};
|
56
|
+
style-src-attr 'unsafe-inline' ;
|
57
|
+
style-src-elem 'self' #{STYLE_SRCS.join(' ')};
|
58
|
+
CSP
|
59
|
+
|
29
60
|
# Public: Call this in subclasses so the action knows its route.
|
30
61
|
#
|
31
62
|
# regex - The Regexp that this action should run for.
|
@@ -130,6 +161,7 @@ module Flipper
|
|
130
161
|
# Returns a response.
|
131
162
|
def view_response(name)
|
132
163
|
header 'Content-Type', 'text/html'
|
164
|
+
header 'Content-Security-Policy', CONTENT_SECURITY_POLICY
|
133
165
|
body = view_with_layout { view_without_layout name }
|
134
166
|
halt [@code, @headers, [body]]
|
135
167
|
end
|
@@ -151,7 +183,7 @@ module Flipper
|
|
151
183
|
# location - The String location to set the Location header to.
|
152
184
|
def redirect_to(location)
|
153
185
|
status 302
|
154
|
-
header 'Location', "#{script_name}#{location}"
|
186
|
+
header 'Location', "#{script_name}#{Rack::Utils.escape_path(location)}"
|
155
187
|
halt [@code, @headers, ['']]
|
156
188
|
end
|
157
189
|
|
@@ -207,6 +239,7 @@ module Flipper
|
|
207
239
|
def view(name)
|
208
240
|
path = views_path.join("#{name}.erb")
|
209
241
|
raise "Template does not exist: #{path}" unless path.exist?
|
242
|
+
|
210
243
|
eval(Erubi::Engine.new(path.read, escape: true).src)
|
211
244
|
end
|
212
245
|
|
@@ -237,6 +270,22 @@ module Flipper
|
|
237
270
|
def valid_request_method?
|
238
271
|
VALID_REQUEST_METHOD_NAMES.include?(request_method_name)
|
239
272
|
end
|
273
|
+
|
274
|
+
def bootstrap_css
|
275
|
+
SOURCES[:bootstrap_css]
|
276
|
+
end
|
277
|
+
|
278
|
+
def bootstrap_js
|
279
|
+
SOURCES[:bootstrap_js]
|
280
|
+
end
|
281
|
+
|
282
|
+
def popper_js
|
283
|
+
SOURCES[:popper_js]
|
284
|
+
end
|
285
|
+
|
286
|
+
def jquery_js
|
287
|
+
SOURCES[:jquery_js]
|
288
|
+
end
|
240
289
|
end
|
241
290
|
end
|
242
291
|
end
|
@@ -25,19 +25,22 @@ module Flipper
|
|
25
25
|
def post
|
26
26
|
feature = flipper[feature_name]
|
27
27
|
value = params['value'].to_s.strip
|
28
|
+
values = value.split(UI.configuration.actors_separator).map(&:strip).uniq
|
28
29
|
|
29
|
-
if
|
30
|
-
error =
|
30
|
+
if values.empty?
|
31
|
+
error = "#{value.inspect} is not a valid actor value."
|
31
32
|
redirect_to("/features/#{feature.key}/actors?error=#{error}")
|
32
33
|
end
|
33
34
|
|
34
|
-
|
35
|
+
values.each do |value|
|
36
|
+
actor = Flipper::Actor.new(value)
|
35
37
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
38
|
+
case params['operation']
|
39
|
+
when 'enable'
|
40
|
+
feature.enable_actor actor
|
41
|
+
when 'disable'
|
42
|
+
feature.disable_actor actor
|
43
|
+
end
|
41
44
|
end
|
42
45
|
|
43
46
|
redirect_to("/features/#{feature.key}")
|
@@ -49,14 +49,14 @@ module Flipper
|
|
49
49
|
value = params['value'].to_s.strip
|
50
50
|
|
51
51
|
if Util.blank?(value)
|
52
|
-
error =
|
52
|
+
error = "#{value.inspect} is not a valid feature name."
|
53
53
|
redirect_to("/features/new?error=#{error}")
|
54
54
|
end
|
55
55
|
|
56
56
|
feature = flipper[value]
|
57
57
|
feature.add
|
58
58
|
|
59
|
-
redirect_to "/features/#{
|
59
|
+
redirect_to "/features/#{value}"
|
60
60
|
end
|
61
61
|
end
|
62
62
|
end
|
@@ -35,7 +35,7 @@ module Flipper
|
|
35
35
|
|
36
36
|
redirect_to("/features/#{feature.key}")
|
37
37
|
else
|
38
|
-
error =
|
38
|
+
error = "The group named #{value.inspect} has not been registered."
|
39
39
|
redirect_to("/features/#{feature.key}/groups?error=#{error}")
|
40
40
|
end
|
41
41
|
end
|
@@ -16,7 +16,7 @@ module Flipper
|
|
16
16
|
begin
|
17
17
|
feature.enable_percentage_of_actors params['value']
|
18
18
|
rescue ArgumentError => exception
|
19
|
-
error =
|
19
|
+
error = "Invalid percentage of actors value: #{exception.message}"
|
20
20
|
redirect_to("/features/#{@feature.key}?error=#{error}")
|
21
21
|
end
|
22
22
|
|
@@ -16,7 +16,7 @@ module Flipper
|
|
16
16
|
begin
|
17
17
|
feature.enable_percentage_of_time params['value']
|
18
18
|
rescue ArgumentError => exception
|
19
|
-
error =
|
19
|
+
error = "Invalid percentage of time value: #{exception.message}"
|
20
20
|
redirect_to("/features/#{@feature.key}?error=#{error}")
|
21
21
|
end
|
22
22
|
|
@@ -44,6 +44,11 @@ module Flipper
|
|
44
44
|
# Default false. Only works when using descriptions.
|
45
45
|
attr_accessor :show_feature_description_in_list
|
46
46
|
|
47
|
+
# Public: What should be used to denote you are trying to add multiple
|
48
|
+
# actors at once (instead of just a single actor).
|
49
|
+
# Default is comma ",".
|
50
|
+
attr_accessor :actors_separator
|
51
|
+
|
47
52
|
VALID_BANNER_CLASS_VALUES = %w(
|
48
53
|
danger
|
49
54
|
dark
|
@@ -68,6 +73,7 @@ module Flipper
|
|
68
73
|
@add_actor_placeholder = "a flipper id"
|
69
74
|
@descriptions_source = DEFAULT_DESCRIPTIONS_SOURCE
|
70
75
|
@show_feature_description_in_list = false
|
76
|
+
@actors_separator = ','
|
71
77
|
end
|
72
78
|
|
73
79
|
def using_descriptions?
|
@@ -12,6 +12,7 @@ module Flipper
|
|
12
12
|
def initialize(app, options = {})
|
13
13
|
@app = app
|
14
14
|
@env_key = options.fetch(:env_key, 'flipper')
|
15
|
+
@flipper = options.fetch(:flipper) { Flipper }
|
15
16
|
|
16
17
|
@action_collection = ActionCollection.new
|
17
18
|
|
@@ -43,7 +44,7 @@ module Flipper
|
|
43
44
|
if action_class.nil?
|
44
45
|
@app.call(env)
|
45
46
|
else
|
46
|
-
flipper = env.fetch(@env_key)
|
47
|
+
flipper = env.fetch(@env_key) { Flipper }
|
47
48
|
action_class.run(flipper, request)
|
48
49
|
end
|
49
50
|
end
|
@@ -8,7 +8,7 @@
|
|
8
8
|
<h4 class="card-header">Enable Actor for <%= @feature.key %></h4>
|
9
9
|
<div class="card-body">
|
10
10
|
<p>
|
11
|
-
Turn on this feature for
|
11
|
+
Turn on this feature for actors.
|
12
12
|
</p>
|
13
13
|
<form action="<%= script_name %>/features/<%= @feature.key %>/actors" method="post" class="form-inline">
|
14
14
|
<%== csrf_input_tag %>
|
@@ -9,7 +9,7 @@
|
|
9
9
|
<div class="col">
|
10
10
|
<div class="card">
|
11
11
|
<div class="card-body">
|
12
|
-
|
12
|
+
<%== Sanitize.fragment(@feature.description, Sanitize::Config::BASIC) %>
|
13
13
|
</div>
|
14
14
|
</div>
|
15
15
|
</div>
|
@@ -21,7 +21,7 @@
|
|
21
21
|
<div class="card">
|
22
22
|
<%# Gate State Header %>
|
23
23
|
<div class="card-header">
|
24
|
-
<span class="
|
24
|
+
<span class="status <%= @feature.color_class %> mr-2"></span>
|
25
25
|
<%= @feature.gate_state_title %>
|
26
26
|
</div>
|
27
27
|
|
@@ -69,8 +69,8 @@
|
|
69
69
|
<%== csrf_input_tag %>
|
70
70
|
<input type="hidden" name="operation" value="disable">
|
71
71
|
<input type="hidden" name="value" value="<%= item %>">
|
72
|
-
<button type="submit" value="Disable" class="btn btn-
|
73
|
-
|
72
|
+
<button type="submit" value="Disable" class="btn btn-outline-danger" data-toggle="tooltip" title="Disable <%= item %>" data-placement="left">
|
73
|
+
Remove
|
74
74
|
</button>
|
75
75
|
</form>
|
76
76
|
</div>
|
@@ -130,8 +130,8 @@
|
|
130
130
|
<%== csrf_input_tag %>
|
131
131
|
<input type="hidden" name="operation" value="disable">
|
132
132
|
<input type="hidden" name="value" value="<%= item %>">
|
133
|
-
<button type="submit" value="Disable" class="btn btn-
|
134
|
-
|
133
|
+
<button type="submit" value="Disable" class="btn btn-outline-danger" data-toggle="tooltip" title="Disable <%= item %>" data-placement="left">
|
134
|
+
Remove
|
135
135
|
</button>
|
136
136
|
</form>
|
137
137
|
</div>
|
@@ -40,14 +40,14 @@
|
|
40
40
|
<% @features.each do |feature| %>
|
41
41
|
<div class="feature row align-items-center mt-0 px-3 border-bottom">
|
42
42
|
<div class="col-1 col-md-auto">
|
43
|
-
<span class="
|
43
|
+
<span class="status <%= feature.color_class %>" data-toggle="tooltip" title=<%= feature.state.to_s.capitalize %>></span>
|
44
44
|
</div>
|
45
45
|
<div class="col-10">
|
46
46
|
<a href="<%= "#{script_name}/features/#{feature.key}" %>" class="d-block px-0 py-3 btn text-left text-dark">
|
47
47
|
<div class="text-truncate" style="font-weight: 500"><%= feature.key %></div>
|
48
48
|
<% if Flipper::UI.configuration.show_feature_description_in_list? && Flipper::UI::Util.present?(feature.description) %>
|
49
49
|
<div class="text-muted font-weight-light" style="line-height: 1.4; white-space: initial; padding: 8px 0">
|
50
|
-
|
50
|
+
<%== Sanitize.fragment(feature.description, Sanitize::Config::BASIC) %>
|
51
51
|
</div>
|
52
52
|
<% end %>
|
53
53
|
<div class="text-muted text-truncate">
|
@@ -6,15 +6,14 @@
|
|
6
6
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
7
7
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
8
8
|
|
9
|
-
<link rel="stylesheet" href="
|
10
|
-
<link rel="stylesheet" href="<%= script_name %>/octicons/octicons.css">
|
9
|
+
<link rel="stylesheet" href="<%= bootstrap_css[:src] %>" integrity="<%= bootstrap_css[:hash] %>" crossorigin="anonymous">
|
11
10
|
<link rel="stylesheet" href="<%= script_name %>/css/application.css">
|
12
11
|
</head>
|
13
12
|
<body class="py-4">
|
14
13
|
<div class="container mw-600">
|
15
14
|
<%- unless Flipper::UI.configuration.banner_text.nil? -%>
|
16
15
|
<div class="alert alert-<%= Flipper::UI.configuration.banner_class %> text-center font-weight-bold">
|
17
|
-
|
16
|
+
<%== Sanitize.fragment(Flipper::UI.configuration.banner_text, Sanitize::Config::BASIC) %>
|
18
17
|
</div>
|
19
18
|
<%- end -%>
|
20
19
|
|
@@ -52,9 +51,9 @@
|
|
52
51
|
<% end %>
|
53
52
|
</div>
|
54
53
|
|
55
|
-
<script src="
|
56
|
-
<script src="
|
57
|
-
<script src="
|
54
|
+
<script src="<%= jquery_js[:src] %>" integrity="<%= jquery_js[:hash] %>" crossorigin="anonymous"></script>
|
55
|
+
<script src="<%= popper_js[:src] %>" integrity="<%= popper_js[:hash] %>" crossorigin="anonymous"></script>
|
56
|
+
<script src="<%= bootstrap_js[:src] %>" integrity="<%= bootstrap_js[:hash] %>" crossorigin="anonymous"></script>
|
58
57
|
<script src="<%= script_name %>/js/application.js"></script>
|
59
58
|
</body>
|
60
59
|
</html>
|
data/lib/flipper/ui.rb
CHANGED
@@ -39,14 +39,14 @@ module Flipper
|
|
39
39
|
def self.app(flipper = nil, options = {})
|
40
40
|
env_key = options.fetch(:env_key, 'flipper')
|
41
41
|
rack_protection_options = options.fetch(:rack_protection, use: :authenticity_token)
|
42
|
+
|
42
43
|
app = ->(_) { [200, { 'Content-Type' => 'text/html' }, ['']] }
|
43
44
|
builder = Rack::Builder.new
|
44
45
|
yield builder if block_given?
|
45
46
|
builder.use Rack::Protection, rack_protection_options
|
46
47
|
builder.use Rack::MethodOverride
|
47
48
|
builder.use Flipper::Middleware::SetupEnv, flipper, env_key: env_key
|
48
|
-
builder.use Flipper::Middleware
|
49
|
-
builder.use Flipper::UI::Middleware, env_key: env_key
|
49
|
+
builder.use Flipper::UI::Middleware, flipper: flipper, env_key: env_key
|
50
50
|
builder.run app
|
51
51
|
klass = self
|
52
52
|
builder.define_singleton_method(:inspect) { klass.inspect } # pretty rake routes output
|
data/lib/flipper/version.rb
CHANGED