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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/flipper-ui.gemspec +12 -12
  3. data/lib/flipper/ui.rb +6 -4
  4. data/lib/flipper/ui/action.rb +22 -21
  5. data/lib/flipper/ui/action_collection.rb +2 -2
  6. data/lib/flipper/ui/actions/actors_gate.rb +7 -7
  7. data/lib/flipper/ui/actions/add_feature.rb +6 -6
  8. data/lib/flipper/ui/actions/boolean_gate.rb +2 -2
  9. data/lib/flipper/ui/actions/feature.rb +5 -6
  10. data/lib/flipper/ui/actions/features.rb +9 -10
  11. data/lib/flipper/ui/actions/file.rb +0 -1
  12. data/lib/flipper/ui/actions/gate.rb +5 -2
  13. data/lib/flipper/ui/actions/groups_gate.rb +16 -14
  14. data/lib/flipper/ui/actions/home.rb +1 -2
  15. data/lib/flipper/ui/actions/percentage_of_actors_gate.rb +2 -2
  16. data/lib/flipper/ui/actions/percentage_of_time_gate.rb +2 -2
  17. data/lib/flipper/ui/decorators/feature.rb +9 -9
  18. data/lib/flipper/ui/decorators/gate.rb +7 -6
  19. data/lib/flipper/ui/middleware.rb +2 -27
  20. data/lib/flipper/ui/util.rb +2 -2
  21. data/lib/flipper/version.rb +1 -1
  22. data/spec/flipper/ui/action_spec.rb +22 -22
  23. data/spec/flipper/ui/actions/actors_gate_spec.rb +49 -45
  24. data/spec/flipper/ui/actions/add_feature_spec.rb +9 -9
  25. data/spec/flipper/ui/actions/boolean_gate_spec.rb +22 -22
  26. data/spec/flipper/ui/actions/feature_spec.rb +34 -34
  27. data/spec/flipper/ui/actions/features_spec.rb +44 -40
  28. data/spec/flipper/ui/actions/file_spec.rb +8 -8
  29. data/spec/flipper/ui/actions/gate_spec.rb +17 -15
  30. data/spec/flipper/ui/actions/groups_gate_spec.rb +57 -50
  31. data/spec/flipper/ui/actions/home_spec.rb +4 -4
  32. data/spec/flipper/ui/actions/percentage_of_actors_gate_spec.rb +24 -22
  33. data/spec/flipper/ui/actions/percentage_of_time_gate_spec.rb +24 -22
  34. data/spec/flipper/ui/decorators/feature_spec.rb +27 -27
  35. data/spec/flipper/ui/decorators/gate_spec.rb +10 -10
  36. data/spec/flipper/ui/util_spec.rb +4 -4
  37. data/spec/flipper/ui_spec.rb +58 -59
  38. metadata +7 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: aecff7000b2350bc4238cd50dcfc9cad07c8bb27
4
- data.tar.gz: 86be7431a5eca0a785984083c645dbf6fa7e510e
3
+ metadata.gz: c04c941ffe3af3df192e5a29093f9d9c0efbc6dd
4
+ data.tar.gz: 82bf9b5cb5981e7153da0246e6cefa2ca9e807a5
5
5
  SHA512:
6
- metadata.gz: 7945d54ccb03a0d2b3b309175a601d797b68f1c96fa80d66354d1028aafe12abc7fc2b1eb83b40577180a8dec3450044ad86b8ae58bf4d7653c0d3d3890e6185
7
- data.tar.gz: d5d68823b6d56dfe8069792b5e18c52530400967ca8ff078cf0590dc9768d0c61c2a8ea44780a0cf2b290ac9181673e795c84ec7690af53c300e12db4e788e98
6
+ metadata.gz: bf1562cc247ec64099c378500f4860aa9fb84f55529404f3e85a6b3e0ddf5134a3cf05f2bfeda09e0699f0fe0d432f6af3c2178d026c5c0d639e8ddbe4e85e1f
7
+ data.tar.gz: 2eeab039b13a065810e28b85711d4cb88565195a15ae8f10e99a3e24d4682f991fd1db1d105d1e5d7368d61a1debfda50e3f2d07d06d8f4ec0125150cc177ccd
@@ -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 { |file|
5
- file =~ /(docs|examples|flipper)[\/-]ui/
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 = ["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"
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) + ["lib/flipper/version.rb"]
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 = "flipper-ui"
19
- gem.require_paths = ["lib"]
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'
@@ -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, options = {})
38
- app = lambda { |env| [200, {'Content-Type' => 'text/html'}, ['']] }
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::Memoizer, flipper
45
- builder.use Middleware, flipper
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
@@ -9,11 +9,11 @@ module Flipper
9
9
  extend Forwardable
10
10
 
11
11
  VALID_REQUEST_METHOD_NAMES = Set.new([
12
- "get".freeze,
13
- "post".freeze,
14
- "put".freeze,
15
- "delete".freeze,
16
- ]).freeze
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, @request = flipper, request
63
+ @flipper = flipper
64
+ @request = request
64
65
  @code = 200
65
- @headers = {"Content-Type" => "text/plain"}
66
- @breadcrumbs = if Flipper::UI.application_breadcrumb_href
67
- [Breadcrumb.new("App", Flipper::UI.application_breadcrumb_href)]
68
- else
69
- []
70
- end
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, "#{self.class} does not support request method #{request_method_name.inspect}"
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 "Content-Type", "text/html"
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 "Location", "#{script_name}#{location}"
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 Proc.new {}.binding
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
- %Q(<input type="hidden" name="authenticity_token" value="#{@request.session[:csrf]}">)
222
+ %(<input type="hidden" name="authenticity_token" value="#{@request.session[:csrf]}">)
222
223
  end
223
224
 
224
225
  def valid_request_method?
@@ -11,9 +11,9 @@ module Flipper
11
11
  end
12
12
 
13
13
  def action_for_request(request)
14
- @action_classes.detect { |action_class|
14
+ @action_classes.detect do |action_class|
15
15
  request.path_info =~ action_class.regex
16
- }
16
+ end
17
17
  end
18
18
  end
19
19
  end
@@ -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 "Home", "/"
17
- breadcrumb "Features", "/features"
16
+ breadcrumb 'Home', '/'
17
+ breadcrumb 'Features', '/features'
18
18
  breadcrumb @feature.key, "/features/#{@feature.key}"
19
- breadcrumb "Add Actor"
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["value"].to_s.strip
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["operation"]
37
- when "enable"
36
+ case params['operation']
37
+ when 'enable'
38
38
  feature.enable_actor actor
39
- when "disable"
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 "Home", "/"
15
- breadcrumb "Features", "/features"
16
- breadcrumb "Noooooope"
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 "Home", "/"
22
- breadcrumb "Features", "/features"
23
- breadcrumb "Add"
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("/")[-2])
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["action"] == "Enable"
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("/").last)
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 "Home", "/"
18
- breadcrumb "Features", "/features"
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("/").last)
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 "/features"
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 = "Features"
14
- @features = flipper.features.map { |feature|
12
+ @page_title = 'Features'
13
+ @features = flipper.features.map do |feature|
15
14
  Decorators::Feature.new(feature)
16
- }.sort
15
+ end.sort
17
16
 
18
17
  @show_blank_slate = @features.empty?
19
18
 
20
- breadcrumb "Home", "/"
21
- breadcrumb "Features"
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 "Home", "/"
31
- breadcrumb "Features", "/features"
32
- breadcrumb "Noooooope"
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["value"].to_s.strip
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.")
@@ -5,7 +5,6 @@ module Flipper
5
5
  module UI
6
6
  module Actions
7
7
  class File < UI::Action
8
-
9
8
  route %r{(images|css|js|octicons|fonts)/.*\Z}
10
9
 
11
10
  def get
@@ -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).map{ |value| Rack::Utils.unescape value }
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("#{gate_name.inspect} gate does not exist therefore it cannot be updated.")
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 "Home", "/"
16
- breadcrumb "Features", "/features"
15
+ breadcrumb 'Home', '/'
16
+ breadcrumb 'Features', '/features'
17
17
  breadcrumb @feature.key, "/features/#{@feature.key}"
18
- breadcrumb "Add Group"
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["value"].to_s.strip
26
+ value = params['value'].to_s.strip
27
27
 
28
- case params["operation"]
29
- when "enable"
30
- feature.enable_group value
31
- when "disable"
32
- feature.disable_group value
33
- end
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
- redirect_to("/features/#{feature.key}")
36
- rescue Flipper::GroupNotRegistered => e
37
- error = Rack::Utils.escape("The group named #{value.inspect} has not been registered.")
38
- redirect_to("/features/#{feature.key}/groups?error=#{error}")
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
@@ -5,11 +5,10 @@ module Flipper
5
5
  module UI
6
6
  module Actions
7
7
  class Home < UI::Action
8
-
9
8
  route %r{/?\Z}
10
9
 
11
10
  def get
12
- redirect_to "/features"
11
+ redirect_to '/features'
13
12
  end
14
13
  end
15
14
  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("/")[-2])
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["value"]
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("/")[-2])
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["value"]
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}")