flipper-ui 0.10.2 → 0.11.0.beta1
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/flipper-ui.gemspec +12 -12
- data/lib/flipper/ui.rb +6 -4
- data/lib/flipper/ui/action.rb +22 -21
- data/lib/flipper/ui/action_collection.rb +2 -2
- data/lib/flipper/ui/actions/actors_gate.rb +7 -7
- data/lib/flipper/ui/actions/add_feature.rb +6 -6
- data/lib/flipper/ui/actions/boolean_gate.rb +2 -2
- data/lib/flipper/ui/actions/feature.rb +5 -6
- data/lib/flipper/ui/actions/features.rb +9 -10
- data/lib/flipper/ui/actions/file.rb +0 -1
- data/lib/flipper/ui/actions/gate.rb +5 -2
- data/lib/flipper/ui/actions/groups_gate.rb +16 -14
- data/lib/flipper/ui/actions/home.rb +1 -2
- data/lib/flipper/ui/actions/percentage_of_actors_gate.rb +2 -2
- data/lib/flipper/ui/actions/percentage_of_time_gate.rb +2 -2
- data/lib/flipper/ui/decorators/feature.rb +9 -9
- data/lib/flipper/ui/decorators/gate.rb +7 -6
- data/lib/flipper/ui/middleware.rb +2 -27
- data/lib/flipper/ui/util.rb +2 -2
- data/lib/flipper/version.rb +1 -1
- data/spec/flipper/ui/action_spec.rb +22 -22
- data/spec/flipper/ui/actions/actors_gate_spec.rb +49 -45
- data/spec/flipper/ui/actions/add_feature_spec.rb +9 -9
- data/spec/flipper/ui/actions/boolean_gate_spec.rb +22 -22
- data/spec/flipper/ui/actions/feature_spec.rb +34 -34
- data/spec/flipper/ui/actions/features_spec.rb +44 -40
- data/spec/flipper/ui/actions/file_spec.rb +8 -8
- data/spec/flipper/ui/actions/gate_spec.rb +17 -15
- data/spec/flipper/ui/actions/groups_gate_spec.rb +57 -50
- data/spec/flipper/ui/actions/home_spec.rb +4 -4
- data/spec/flipper/ui/actions/percentage_of_actors_gate_spec.rb +24 -22
- data/spec/flipper/ui/actions/percentage_of_time_gate_spec.rb +24 -22
- data/spec/flipper/ui/decorators/feature_spec.rb +27 -27
- data/spec/flipper/ui/decorators/gate_spec.rb +10 -10
- data/spec/flipper/ui/util_spec.rb +4 -4
- data/spec/flipper/ui_spec.rb +58 -59
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c04c941ffe3af3df192e5a29093f9d9c0efbc6dd
|
4
|
+
data.tar.gz: 82bf9b5cb5981e7153da0246e6cefa2ca9e807a5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bf1562cc247ec64099c378500f4860aa9fb84f55529404f3e85a6b3e0ddf5134a3cf05f2bfeda09e0699f0fe0d432f6af3c2178d026c5c0d639e8ddbe4e85e1f
|
7
|
+
data.tar.gz: 2eeab039b13a065810e28b85711d4cb88565195a15ae8f10e99a3e24d4682f991fd1db1d105d1e5d7368d61a1debfda50e3f2d07d06d8f4ec0125150cc177ccd
|
data/flipper-ui.gemspec
CHANGED
@@ -1,22 +1,22 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
require File.expand_path('../lib/flipper/version', __FILE__)
|
3
3
|
|
4
|
-
flipper_ui_files = lambda
|
5
|
-
file =~
|
6
|
-
|
4
|
+
flipper_ui_files = lambda do |file|
|
5
|
+
file =~ %r{(docs|examples|flipper)[\/-]ui}
|
6
|
+
end
|
7
7
|
|
8
8
|
Gem::Specification.new do |gem|
|
9
|
-
gem.authors = [
|
10
|
-
gem.email = [
|
11
|
-
gem.summary =
|
12
|
-
gem.description =
|
13
|
-
gem.license =
|
14
|
-
gem.homepage =
|
9
|
+
gem.authors = ['John Nunemaker']
|
10
|
+
gem.email = ['nunemaker@gmail.com']
|
11
|
+
gem.summary = 'UI for the Flipper gem'
|
12
|
+
gem.description = 'Rack middleware that provides a fully featured web interface for the flipper gem.'
|
13
|
+
gem.license = 'MIT'
|
14
|
+
gem.homepage = 'https://github.com/jnunemaker/flipper'
|
15
15
|
|
16
|
-
gem.files = `git ls-files`.split("\n").select(&flipper_ui_files) + [
|
16
|
+
gem.files = `git ls-files`.split("\n").select(&flipper_ui_files) + ['lib/flipper/version.rb']
|
17
17
|
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n").select(&flipper_ui_files)
|
18
|
-
gem.name =
|
19
|
-
gem.require_paths = [
|
18
|
+
gem.name = 'flipper-ui'
|
19
|
+
gem.require_paths = ['lib']
|
20
20
|
gem.version = Flipper::VERSION
|
21
21
|
|
22
22
|
gem.add_dependency 'rack', '>= 1.4', '< 3'
|
data/lib/flipper/ui.rb
CHANGED
@@ -9,6 +9,7 @@ end
|
|
9
9
|
require 'rack/protection'
|
10
10
|
|
11
11
|
require 'flipper'
|
12
|
+
require 'flipper/middleware/setup_env'
|
12
13
|
require 'flipper/middleware/memoizer'
|
13
14
|
|
14
15
|
require 'flipper/ui/actor'
|
@@ -34,15 +35,16 @@ module Flipper
|
|
34
35
|
@root ||= Pathname(__FILE__).dirname.expand_path.join('ui')
|
35
36
|
end
|
36
37
|
|
37
|
-
def self.app(flipper
|
38
|
-
app =
|
38
|
+
def self.app(flipper = nil)
|
39
|
+
app = ->() { [200, { 'Content-Type' => 'text/html' }, ['']] }
|
39
40
|
builder = Rack::Builder.new
|
40
41
|
yield builder if block_given?
|
41
42
|
builder.use Rack::Protection
|
42
43
|
builder.use Rack::Protection::AuthenticityToken
|
43
44
|
builder.use Rack::MethodOverride
|
44
|
-
builder.use Flipper::Middleware::
|
45
|
-
builder.use Middleware
|
45
|
+
builder.use Flipper::Middleware::SetupEnv, flipper
|
46
|
+
builder.use Flipper::Middleware::Memoizer
|
47
|
+
builder.use Middleware
|
46
48
|
builder.run app
|
47
49
|
klass = self
|
48
50
|
builder.define_singleton_method(:inspect) { klass.inspect } # pretty rake routes output
|
data/lib/flipper/ui/action.rb
CHANGED
@@ -9,11 +9,11 @@ module Flipper
|
|
9
9
|
extend Forwardable
|
10
10
|
|
11
11
|
VALID_REQUEST_METHOD_NAMES = Set.new([
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
12
|
+
'get'.freeze,
|
13
|
+
'post'.freeze,
|
14
|
+
'put'.freeze,
|
15
|
+
'delete'.freeze,
|
16
|
+
]).freeze
|
17
17
|
|
18
18
|
# Public: Call this in subclasses so the action knows its route.
|
19
19
|
#
|
@@ -60,14 +60,16 @@ module Flipper
|
|
60
60
|
def_delegator :@request, :params
|
61
61
|
|
62
62
|
def initialize(flipper, request)
|
63
|
-
@flipper
|
63
|
+
@flipper = flipper
|
64
|
+
@request = request
|
64
65
|
@code = 200
|
65
|
-
@headers = {
|
66
|
-
@breadcrumbs =
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
66
|
+
@headers = { 'Content-Type' => 'text/plain' }
|
67
|
+
@breadcrumbs =
|
68
|
+
if Flipper::UI.application_breadcrumb_href
|
69
|
+
[Breadcrumb.new('App', Flipper::UI.application_breadcrumb_href)]
|
70
|
+
else
|
71
|
+
[]
|
72
|
+
end
|
71
73
|
end
|
72
74
|
|
73
75
|
# Public: Runs the request method for the provided request.
|
@@ -77,7 +79,8 @@ module Flipper
|
|
77
79
|
if valid_request_method? && respond_to?(request_method_name)
|
78
80
|
catch(:halt) { send(request_method_name) }
|
79
81
|
else
|
80
|
-
raise UI::RequestMethodNotSupported,
|
82
|
+
raise UI::RequestMethodNotSupported,
|
83
|
+
"#{self.class} does not support request method #{request_method_name.inspect}"
|
81
84
|
end
|
82
85
|
end
|
83
86
|
|
@@ -110,7 +113,7 @@ module Flipper
|
|
110
113
|
#
|
111
114
|
# Returns a response.
|
112
115
|
def view_response(name)
|
113
|
-
header
|
116
|
+
header 'Content-Type', 'text/html'
|
114
117
|
body = view_with_layout { view_without_layout name }
|
115
118
|
halt [@code, @headers, [body]]
|
116
119
|
end
|
@@ -132,8 +135,8 @@ module Flipper
|
|
132
135
|
# location - The String location to set the Location header to.
|
133
136
|
def redirect_to(location)
|
134
137
|
status 302
|
135
|
-
header
|
136
|
-
halt [@code, @headers, [
|
138
|
+
header 'Location', "#{script_name}#{location}"
|
139
|
+
halt [@code, @headers, ['']]
|
137
140
|
end
|
138
141
|
|
139
142
|
# Public: Set the status code for the response.
|
@@ -188,13 +191,11 @@ module Flipper
|
|
188
191
|
def view(name)
|
189
192
|
path = views_path.join("#{name}.erb")
|
190
193
|
|
191
|
-
unless path.exist?
|
192
|
-
raise "Template does not exist: #{path}"
|
193
|
-
end
|
194
|
+
raise "Template does not exist: #{path}" unless path.exist?
|
194
195
|
|
195
196
|
contents = path.read
|
196
197
|
compiled = Eruby.new(contents)
|
197
|
-
compiled.result
|
198
|
+
compiled.result proc {}.binding
|
198
199
|
end
|
199
200
|
|
200
201
|
# Internal: The path the app is mounted at.
|
@@ -218,7 +219,7 @@ module Flipper
|
|
218
219
|
end
|
219
220
|
|
220
221
|
def csrf_input_tag
|
221
|
-
%
|
222
|
+
%(<input type="hidden" name="authenticity_token" value="#{@request.session[:csrf]}">)
|
222
223
|
end
|
223
224
|
|
224
225
|
def valid_request_method?
|
@@ -13,10 +13,10 @@ module Flipper
|
|
13
13
|
feature = flipper[feature_name.to_sym]
|
14
14
|
@feature = Decorators::Feature.new(feature)
|
15
15
|
|
16
|
-
breadcrumb
|
17
|
-
breadcrumb
|
16
|
+
breadcrumb 'Home', '/'
|
17
|
+
breadcrumb 'Features', '/features'
|
18
18
|
breadcrumb @feature.key, "/features/#{@feature.key}"
|
19
|
-
breadcrumb
|
19
|
+
breadcrumb 'Add Actor'
|
20
20
|
|
21
21
|
view_response :add_actor
|
22
22
|
end
|
@@ -24,7 +24,7 @@ module Flipper
|
|
24
24
|
def post
|
25
25
|
feature_name = Rack::Utils.unescape(request.path.split('/')[-2])
|
26
26
|
feature = flipper[feature_name.to_sym]
|
27
|
-
value = params[
|
27
|
+
value = params['value'].to_s.strip
|
28
28
|
|
29
29
|
if Util.blank?(value)
|
30
30
|
error = Rack::Utils.escape("#{value.inspect} is not a valid actor value.")
|
@@ -33,10 +33,10 @@ module Flipper
|
|
33
33
|
|
34
34
|
actor = Flipper::UI::Actor.new(value)
|
35
35
|
|
36
|
-
case params[
|
37
|
-
when
|
36
|
+
case params['operation']
|
37
|
+
when 'enable'
|
38
38
|
feature.enable_actor actor
|
39
|
-
when
|
39
|
+
when 'disable'
|
40
40
|
feature.disable_actor actor
|
41
41
|
end
|
42
42
|
|
@@ -11,16 +11,16 @@ module Flipper
|
|
11
11
|
unless Flipper::UI.feature_creation_enabled
|
12
12
|
status 403
|
13
13
|
|
14
|
-
breadcrumb
|
15
|
-
breadcrumb
|
16
|
-
breadcrumb
|
14
|
+
breadcrumb 'Home', '/'
|
15
|
+
breadcrumb 'Features', '/features'
|
16
|
+
breadcrumb 'Noooooope'
|
17
17
|
|
18
18
|
halt view_response(:feature_creation_disabled)
|
19
19
|
end
|
20
20
|
|
21
|
-
breadcrumb
|
22
|
-
breadcrumb
|
23
|
-
breadcrumb
|
21
|
+
breadcrumb 'Home', '/'
|
22
|
+
breadcrumb 'Features', '/features'
|
23
|
+
breadcrumb 'Add'
|
24
24
|
|
25
25
|
view_response :add_feature
|
26
26
|
end
|
@@ -8,11 +8,11 @@ module Flipper
|
|
8
8
|
route %r{features/[^/]*/boolean/?\Z}
|
9
9
|
|
10
10
|
def post
|
11
|
-
feature_name = Rack::Utils.unescape(request.path.split(
|
11
|
+
feature_name = Rack::Utils.unescape(request.path.split('/')[-2])
|
12
12
|
feature = flipper[feature_name.to_sym]
|
13
13
|
@feature = Decorators::Feature.new(feature)
|
14
14
|
|
15
|
-
if params[
|
15
|
+
if params['action'] == 'Enable'
|
16
16
|
feature.enable
|
17
17
|
else
|
18
18
|
feature.disable
|
@@ -5,27 +5,26 @@ module Flipper
|
|
5
5
|
module UI
|
6
6
|
module Actions
|
7
7
|
class Feature < UI::Action
|
8
|
-
|
9
8
|
route %r{features/[^/]*/?\Z}
|
10
9
|
|
11
10
|
def get
|
12
|
-
feature_name = Rack::Utils.unescape(request.path.split(
|
11
|
+
feature_name = Rack::Utils.unescape(request.path.split('/').last)
|
13
12
|
@feature = Decorators::Feature.new(flipper[feature_name])
|
14
13
|
@page_title = "#{@feature.key} // Features"
|
15
14
|
@percentages = [0, 1, 5, 10, 15, 25, 50, 75, 100]
|
16
15
|
|
17
|
-
breadcrumb
|
18
|
-
breadcrumb
|
16
|
+
breadcrumb 'Home', '/'
|
17
|
+
breadcrumb 'Features', '/features'
|
19
18
|
breadcrumb @feature.key
|
20
19
|
|
21
20
|
view_response :feature
|
22
21
|
end
|
23
22
|
|
24
23
|
def delete
|
25
|
-
feature_name = Rack::Utils.unescape(request.path.split(
|
24
|
+
feature_name = Rack::Utils.unescape(request.path.split('/').last)
|
26
25
|
feature = flipper[feature_name]
|
27
26
|
flipper.adapter.remove(feature)
|
28
|
-
redirect_to
|
27
|
+
redirect_to '/features'
|
29
28
|
end
|
30
29
|
end
|
31
30
|
end
|
@@ -6,19 +6,18 @@ module Flipper
|
|
6
6
|
module UI
|
7
7
|
module Actions
|
8
8
|
class Features < UI::Action
|
9
|
-
|
10
9
|
route %r{features/?\Z}
|
11
10
|
|
12
11
|
def get
|
13
|
-
@page_title =
|
14
|
-
@features = flipper.features.map
|
12
|
+
@page_title = 'Features'
|
13
|
+
@features = flipper.features.map do |feature|
|
15
14
|
Decorators::Feature.new(feature)
|
16
|
-
|
15
|
+
end.sort
|
17
16
|
|
18
17
|
@show_blank_slate = @features.empty?
|
19
18
|
|
20
|
-
breadcrumb
|
21
|
-
breadcrumb
|
19
|
+
breadcrumb 'Home', '/'
|
20
|
+
breadcrumb 'Features'
|
22
21
|
|
23
22
|
view_response :features
|
24
23
|
end
|
@@ -27,14 +26,14 @@ module Flipper
|
|
27
26
|
unless Flipper::UI.feature_creation_enabled
|
28
27
|
status 403
|
29
28
|
|
30
|
-
breadcrumb
|
31
|
-
breadcrumb
|
32
|
-
breadcrumb
|
29
|
+
breadcrumb 'Home', '/'
|
30
|
+
breadcrumb 'Features', '/features'
|
31
|
+
breadcrumb 'Noooooope'
|
33
32
|
|
34
33
|
halt view_response(:feature_creation_disabled)
|
35
34
|
end
|
36
35
|
|
37
|
-
value = params[
|
36
|
+
value = params['value'].to_s.strip
|
38
37
|
|
39
38
|
if Util.blank?(value)
|
40
39
|
error = Rack::Utils.escape("#{value.inspect} is not a valid feature name.")
|
@@ -8,7 +8,8 @@ module Flipper
|
|
8
8
|
route %r{features/[^/]*/[^/]*/?\Z}
|
9
9
|
|
10
10
|
def post
|
11
|
-
feature_name, gate_name = request.path.split('/').pop(2)
|
11
|
+
feature_name, gate_name = request.path.split('/').pop(2)
|
12
|
+
.map(&Rack::Utils.method(:unescape))
|
12
13
|
update_gate_method_name = "update_#{gate_name}"
|
13
14
|
|
14
15
|
feature = flipper[feature_name.to_sym]
|
@@ -27,7 +28,9 @@ module Flipper
|
|
27
28
|
|
28
29
|
# Private: Returns error response that gate update method is not defined.
|
29
30
|
def update_gate_method_undefined(gate_name)
|
30
|
-
error = Rack::Utils.escape(
|
31
|
+
error = Rack::Utils.escape(
|
32
|
+
"#{gate_name.inspect} gate does not exist therefore it cannot be updated."
|
33
|
+
)
|
31
34
|
redirect_to("/features/#{@feature.key}?error=#{error}")
|
32
35
|
end
|
33
36
|
end
|
@@ -12,10 +12,10 @@ module Flipper
|
|
12
12
|
feature = flipper[feature_name.to_sym]
|
13
13
|
@feature = Decorators::Feature.new(feature)
|
14
14
|
|
15
|
-
breadcrumb
|
16
|
-
breadcrumb
|
15
|
+
breadcrumb 'Home', '/'
|
16
|
+
breadcrumb 'Features', '/features'
|
17
17
|
breadcrumb @feature.key, "/features/#{@feature.key}"
|
18
|
-
breadcrumb
|
18
|
+
breadcrumb 'Add Group'
|
19
19
|
|
20
20
|
view_response :add_group
|
21
21
|
end
|
@@ -23,19 +23,21 @@ module Flipper
|
|
23
23
|
def post
|
24
24
|
feature_name = Rack::Utils.unescape(request.path.split('/')[-2])
|
25
25
|
feature = flipper[feature_name.to_sym]
|
26
|
-
value = params[
|
26
|
+
value = params['value'].to_s.strip
|
27
27
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
28
|
+
if Flipper.group_exists?(value)
|
29
|
+
case params['operation']
|
30
|
+
when 'enable'
|
31
|
+
feature.enable_group value
|
32
|
+
when 'disable'
|
33
|
+
feature.disable_group value
|
34
|
+
end
|
34
35
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
36
|
+
redirect_to("/features/#{feature.key}")
|
37
|
+
else
|
38
|
+
error = Rack::Utils.escape("The group named #{value.inspect} has not been registered.")
|
39
|
+
redirect_to("/features/#{feature.key}/groups?error=#{error}")
|
40
|
+
end
|
39
41
|
end
|
40
42
|
end
|
41
43
|
end
|
@@ -8,12 +8,12 @@ module Flipper
|
|
8
8
|
route %r{features/[^/]*/percentage_of_actors/?\Z}
|
9
9
|
|
10
10
|
def post
|
11
|
-
feature_name = Rack::Utils.unescape(request.path.split(
|
11
|
+
feature_name = Rack::Utils.unescape(request.path.split('/')[-2])
|
12
12
|
feature = flipper[feature_name.to_sym]
|
13
13
|
@feature = Decorators::Feature.new(feature)
|
14
14
|
|
15
15
|
begin
|
16
|
-
feature.enable_percentage_of_actors params[
|
16
|
+
feature.enable_percentage_of_actors params['value']
|
17
17
|
rescue ArgumentError => exception
|
18
18
|
error = Rack::Utils.escape("Invalid percentage of actors value: #{exception.message}")
|
19
19
|
redirect_to("/features/#{@feature.key}?error=#{error}")
|
@@ -8,12 +8,12 @@ module Flipper
|
|
8
8
|
route %r{features/[^/]*/percentage_of_time/?\Z}
|
9
9
|
|
10
10
|
def post
|
11
|
-
feature_name = Rack::Utils.unescape(request.path.split(
|
11
|
+
feature_name = Rack::Utils.unescape(request.path.split('/')[-2])
|
12
12
|
feature = flipper[feature_name.to_sym]
|
13
13
|
@feature = Decorators::Feature.new(feature)
|
14
14
|
|
15
15
|
begin
|
16
|
-
feature.enable_percentage_of_time params[
|
16
|
+
feature.enable_percentage_of_time params['value']
|
17
17
|
rescue ArgumentError => exception
|
18
18
|
error = Rack::Utils.escape("Invalid percentage of time value: #{exception.message}")
|
19
19
|
redirect_to("/features/#{@feature.key}?error=#{error}")
|