flipper-ui 0.10.2 → 0.11.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
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}")