flipper-ui 0.16.2 → 0.17.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 71eccacf760968860d08a08de30d86c435314c3a
4
- data.tar.gz: 9e6a0d87e9158c108dbf7c80fbf663576899fcf3
2
+ SHA256:
3
+ metadata.gz: 0bbc8229a6919e543d4f8ed77215b14d6e13cb68dfc24a0455a2aff99df88c50
4
+ data.tar.gz: 1e47a5645b80ca500a8ae212bce1bad4f2c34b7fc6a3473f59f937a5d1581b70
5
5
  SHA512:
6
- metadata.gz: 5790f438fba84b3a34301fc004fab83f6609c249026438639395781b474848e2c57f38aa612a5864e6365b5e2e853b00a7c72c96c69e6b9074e37fd5e55da44b
7
- data.tar.gz: ed1011c894f34b0ebd3ff87a06ed5687428c7ba8d76466d8e18a49ccfc595716eae65afdb6f2b54786ebde372f8ad367b6bea079773da00977d155d06d9b2d64
6
+ metadata.gz: 8d3aa432ff0629d2bff12fc556c18de7edac91dd3aab2c30e9057d974ddc76382972856930934075e144085dca9be736f69aed48d09c83f6d60a0a89dbed7538
7
+ data.tar.gz: 571dfd938b2742706f67f75117f76a4d6397cb1fddef2bef5334af2a92738c5e3391517abd1a73c239d60685f2026191328b9067c0f0317ba220e40880ecc53f
data/docs/ui/README.md CHANGED
@@ -37,14 +37,15 @@ YourRailsApp::Application.routes.draw do
37
37
  end
38
38
  ```
39
39
 
40
- If you'd like to lazy load flipper, you can pass a block instead:
40
+ If you'd like to lazy load flipper, you can instead pass a block to initialize it:
41
41
 
42
42
  ```ruby
43
43
  # config/routes.rb
44
44
  YourRailsApp::Application.routes.draw do
45
45
  flipper_block = lambda {
46
46
  # some flipper initialization here, for example:
47
- # YourRailsApp.flipper
47
+ adapter = Flipper::Adapters::Memory.new
48
+ Flipper.new(adapter)
48
49
  }
49
50
  mount Flipper::UI.app(flipper_block) => '/flipper'
50
51
  end
data/flipper-ui.gemspec CHANGED
@@ -10,12 +10,12 @@ Gem::Specification.new do |gem|
10
10
  gem.authors = ['John Nunemaker']
11
11
  gem.email = ['nunemaker@gmail.com']
12
12
  gem.summary = 'UI for the Flipper gem'
13
- gem.description = 'Rack middleware that provides a fully featured web interface for the flipper gem.'
13
+ gem.description = 'Rack middleware that provides a fully featured web interface for the flipper gem.' # rubocop:disable Metrics/LineLength
14
14
  gem.license = 'MIT'
15
15
  gem.homepage = 'https://github.com/jnunemaker/flipper'
16
16
 
17
- gem.files = `git ls-files`.split("\n").select(&flipper_ui_files) + ['lib/flipper/version.rb']
18
- gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n").select(&flipper_ui_files)
17
+ gem.files = `git ls-files`.split("\n").select(&flipper_ui_files) + ['lib/flipper/version.rb'] # rubocop:disable Metrics/LineLength
18
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n").select(&flipper_ui_files) # rubocop:disable Metrics/LineLength
19
19
  gem.name = 'flipper-ui'
20
20
  gem.require_paths = ['lib']
21
21
  gem.version = Flipper::VERSION
@@ -24,5 +24,5 @@ Gem::Specification.new do |gem|
24
24
  gem.add_dependency 'rack', '>= 1.4', '< 3'
25
25
  gem.add_dependency 'rack-protection', '>= 1.5.3', '< 2.1.0'
26
26
  gem.add_dependency 'flipper', "~> #{Flipper::VERSION}"
27
- gem.add_dependency 'erubis', '~> 2.7.0'
27
+ gem.add_dependency 'erubi', '>= 1.0.0', '< 2.0.0'
28
28
  end
data/lib/flipper/ui.rb CHANGED
@@ -41,15 +41,15 @@ module Flipper
41
41
 
42
42
  def self.app(flipper = nil, options = {})
43
43
  env_key = options.fetch(:env_key, 'flipper')
44
- app = ->() { [200, { 'Content-Type' => 'text/html' }, ['']] }
44
+ rack_protection_options = options.fetch(:rack_protection, use: :authenticity_token)
45
+ app = ->(_) { [200, { 'Content-Type' => 'text/html' }, ['']] }
45
46
  builder = Rack::Builder.new
46
47
  yield builder if block_given?
47
- builder.use Rack::Protection
48
- builder.use Rack::Protection::AuthenticityToken
48
+ builder.use Rack::Protection, rack_protection_options
49
49
  builder.use Rack::MethodOverride
50
50
  builder.use Flipper::Middleware::SetupEnv, flipper, env_key: env_key
51
51
  builder.use Flipper::Middleware::Memoizer, env_key: env_key
52
- builder.use Middleware, env_key: env_key
52
+ builder.use Flipper::UI::Middleware, env_key: env_key
53
53
  builder.run app
54
54
  klass = self
55
55
  builder.define_singleton_method(:inspect) { klass.inspect } # pretty rake routes output
@@ -1,6 +1,6 @@
1
1
  require 'forwardable'
2
2
  require 'flipper/ui/error'
3
- require 'flipper/ui/eruby'
3
+ require 'erubi'
4
4
  require 'json'
5
5
 
6
6
  module Flipper
@@ -205,12 +205,8 @@ module Flipper
205
205
  # Private
206
206
  def view(name)
207
207
  path = views_path.join("#{name}.erb")
208
-
209
208
  raise "Template does not exist: #{path}" unless path.exist?
210
-
211
- contents = path.read
212
- compiled = Eruby.new(contents)
213
- compiled.result proc {}.binding
209
+ eval(Erubi::Engine.new(path.read, escape: true).src) # rubocop:disable Security/Eval
214
210
  end
215
211
 
216
212
  # Internal: The path the app is mounted at.
@@ -26,6 +26,15 @@ module Flipper
26
26
  # set to false, users won't be able to delete features from the UI.
27
27
  attr_accessor :feature_removal_enabled
28
28
 
29
+ # Public: Are you feeling lucky? Defaults to true. If set to false, users
30
+ # won't see a videoclip of Taylor Swift when there aren't any features
31
+ attr_accessor :fun
32
+
33
+ # Public: What should show up in the form to add actors. This can be
34
+ # different per application since flipper_id's can be whatever an
35
+ # application needs. Defaults to "a flipper id".
36
+ attr_accessor :add_actor_placeholder
37
+
29
38
  VALID_BANNER_CLASS_VALUES = %w(
30
39
  danger
31
40
  dark
@@ -47,6 +56,8 @@ module Flipper
47
56
  @banner_class = 'danger'
48
57
  @feature_creation_enabled = true
49
58
  @feature_removal_enabled = true
59
+ @fun = true
60
+ @add_actor_placeholder = "a flipper id"
50
61
  end
51
62
 
52
63
  def banner_class=(value)
@@ -13,7 +13,7 @@
13
13
  <form action="<%= script_name %>/features/<%= @feature.key %>/actors" method="post" class="form-inline">
14
14
  <%== csrf_input_tag %>
15
15
  <input type="hidden" name="operation" value="enable">
16
- <input type="text" name="value" placeholder="ie: User:6" class="form-control mr-2">
16
+ <input type="text" name="value" placeholder="<%= Flipper::UI.configuration.add_actor_placeholder %>" class="form-control mr-2">
17
17
  <input type="submit" value="Add Actor" class="btn btn-light">
18
18
  </form>
19
19
  </div>
@@ -9,7 +9,7 @@
9
9
  <div class="card-body">
10
10
  <form action="<%= script_name %>/features" method="post" class="form-inline mb-2">
11
11
  <%== csrf_input_tag %>
12
- <input type="text" name="value" size="30" placeholder="ie: search, new_pricing, etc." autofocus class="form-control mr-2 mb-2 mb-md-0">
12
+ <input type="text" name="value" size="30" placeholder="search, new_pricing, etc." autofocus class="form-control mr-2 mb-2 mb-md-0">
13
13
  <input type="submit" value="Add Feature" class="btn btn-light">
14
14
  </form>
15
15
  <p class="text-muted">
@@ -60,7 +60,7 @@
60
60
  <div class="col">
61
61
  <form action="<%= script_name %>/features/<%= @feature.key %>/percentage_of_actors" method="post" class="form-inline">
62
62
  <%== csrf_input_tag %>
63
- <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="form-control form-control-sm mr-sm-2 mb-2 mb-md-0">
63
+ <input type="text" name="value" <% if @feature.percentage_of_actors_value > 0 %>value="<%= @feature.percentage_of_actors_value %>"<% end %> placeholder="custom (26, 32, etc.)" class="form-control form-control-sm mr-sm-2 mb-2 mb-md-0">
64
64
  <input type="submit" name="action" value="Enable" class="btn btn-light btn-sm">
65
65
  </form>
66
66
  </div>
@@ -98,7 +98,7 @@
98
98
  <div class="col">
99
99
  <form action="<%= script_name %>/features/<%= @feature.key %>/percentage_of_time" method="post" class="form-inline">
100
100
  <%== csrf_input_tag %>
101
- <input type="text" name="value" <% if @feature.percentage_of_time_value > 0 %>value="<%= @feature.percentage_of_time_value %>"<% end %> placeholder="custom (ie: 26, 32, etc.)" class="form-control form-control-sm mr-sm-2 mb-2 mb-md-0">
101
+ <input type="text" name="value" <% if @feature.percentage_of_time_value > 0 %>value="<%= @feature.percentage_of_time_value %>"<% end %> placeholder="custom (26, 32, etc.)" class="form-control form-control-sm mr-sm-2 mb-2 mb-md-0">
102
102
  <input type="submit" name="action" value="Enable" class="btn btn-light btn-sm">
103
103
  </form>
104
104
  </div>
@@ -186,7 +186,7 @@
186
186
  <form action="<%= script_name %>/features/<%= @feature.key %>/actors" method="post" class="form-inline">
187
187
  <%== csrf_input_tag %>
188
188
  <input type="hidden" name="operation" value="enable">
189
- <input type="text" name="value" placeholder="ie: User:6" class="form-control form-control-sm mr-sm-2 mb-2 mb-sm-0">
189
+ <input type="text" name="value" placeholder="<%= Flipper::UI.configuration.add_actor_placeholder %>" class="form-control form-control-sm mr-sm-2 mb-2 mb-sm-0">
190
190
  <input type="submit" value="Add Actor" class="btn btn-light btn-sm">
191
191
  </form>
192
192
  </div>
@@ -239,7 +239,7 @@
239
239
  <%= Flipper::UI.configuration.delete.description %>
240
240
  </p>
241
241
 
242
- <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?')">
242
+ <form action="<%= script_name %>/features/<%= @feature.key %>" method="post" onsubmit="return confirm('Are you sure you want to remove this feature from the list of features and disable it for everyone?')">
243
243
  <%== csrf_input_tag %>
244
244
  <input type="hidden" name="_method" value="DELETE">
245
245
  <button type="submit" name="action" value="Delete" class="btn btn-danger" data-toggle="tooltip" title="Remove feature from list of features and disable it." data-placement="right">Delete</button>
@@ -3,11 +3,19 @@
3
3
  <span class="mega-octicon octicon-plus"></span>
4
4
  <span class="mega-octicon octicon-list-unordered"></span>
5
5
  <span class="mega-octicon octicon-zap"></span>
6
- <h4>But I've got a blank space baby...</h4>
7
- <p>And I'll flip your features.</p>
8
- <div class="embed-responsive embed-responsive-16by9">
9
- <iframe class="embed-responsive-item" width="560" height="315" src="https://www.youtube.com/embed/e-ORhEE9VVg" frameborder="0" allowfullscreen></iframe>
10
- </div>
6
+ <% if Flipper::UI.configuration.fun %>
7
+ <h4>But I've got a blank space baby...</h4>
8
+ <p>And I'll flip your features.</p>
9
+ <div class="embed-responsive embed-responsive-16by9">
10
+ <iframe class="embed-responsive-item" width="560" height="315" src="https://www.youtube.com/embed/e-ORhEE9VVg" frameborder="0" allowfullscreen></iframe>
11
+ </div>
12
+ <% else %>
13
+ <h4>There aren't any features to configure.</h4>
14
+ <p>
15
+ Check the <a href="https://github.com/jnunemaker/flipper#examples">examples</a> to
16
+ learn how to add one.
17
+ </p>
18
+ <% end %>
11
19
  </div>
12
20
  <% else %>
13
21
  <div class="card">
@@ -1,3 +1,3 @@
1
1
  module Flipper
2
- VERSION = '0.16.2'.freeze
2
+ VERSION = '0.17.1'.freeze
3
3
  end
@@ -44,7 +44,7 @@ RSpec.describe Flipper::UI::Actions::ActorsGate do
44
44
 
45
45
  describe 'POST /features/:feature/actors' do
46
46
  context 'enabling an actor' do
47
- let(:value) { 'User:6' }
47
+ let(:value) { 'User;6' }
48
48
 
49
49
  before do
50
50
  post 'features/search/actors',
@@ -53,7 +53,7 @@ RSpec.describe Flipper::UI::Actions::ActorsGate do
53
53
  end
54
54
 
55
55
  it 'adds item to members' do
56
- expect(flipper[:search].actors_value).to include('User:6')
56
+ expect(flipper[:search].actors_value).to include('User;6')
57
57
  end
58
58
 
59
59
  it 'redirects back to feature' do
@@ -62,10 +62,10 @@ RSpec.describe Flipper::UI::Actions::ActorsGate do
62
62
  end
63
63
 
64
64
  context 'value contains whitespace' do
65
- let(:value) { ' User:6 ' }
65
+ let(:value) { ' User;6 ' }
66
66
 
67
67
  it 'adds item without whitespace' do
68
- expect(flipper[:search].actors_value).to include('User:6')
68
+ expect(flipper[:search].actors_value).to include('User;6')
69
69
  end
70
70
  end
71
71
 
@@ -95,17 +95,17 @@ RSpec.describe Flipper::UI::Actions::ActorsGate do
95
95
  end
96
96
 
97
97
  context 'disabling an actor' do
98
- let(:value) { 'User:6' }
98
+ let(:value) { 'User;6' }
99
99
 
100
100
  before do
101
- flipper[:search].enable_actor Flipper::Actor.new('User:6')
101
+ flipper[:search].enable_actor Flipper::Actor.new('User;6')
102
102
  post 'features/search/actors',
103
103
  { 'value' => value, 'operation' => 'disable', 'authenticity_token' => token },
104
104
  'rack.session' => session
105
105
  end
106
106
 
107
107
  it 'removes item from members' do
108
- expect(flipper[:search].actors_value).not_to include('User:6')
108
+ expect(flipper[:search].actors_value).not_to include('User;6')
109
109
  end
110
110
 
111
111
  it 'redirects back to feature' do
@@ -114,10 +114,10 @@ RSpec.describe Flipper::UI::Actions::ActorsGate do
114
114
  end
115
115
 
116
116
  context 'value contains whitespace' do
117
- let(:value) { ' User:6 ' }
117
+ let(:value) { ' User;6 ' }
118
118
 
119
119
  it 'removes item without whitespace' do
120
- expect(flipper[:search].actors_value).not_to include('User:6')
120
+ expect(flipper[:search].actors_value).not_to include('User;6')
121
121
  end
122
122
  end
123
123
  end
@@ -13,19 +13,58 @@ RSpec.describe Flipper::UI::Actions::Features do
13
13
  end
14
14
 
15
15
  describe 'GET /features' do
16
- before do
17
- flipper[:stats].enable
18
- flipper[:search].enable
19
- get '/features'
20
- end
16
+ context "when there are some features" do
17
+ before do
18
+ flipper[:stats].enable
19
+ flipper[:search].enable
20
+ get '/features'
21
+ end
21
22
 
22
- it 'responds with success' do
23
- expect(last_response.status).to be(200)
23
+ it 'responds with success' do
24
+ expect(last_response.status).to be(200)
25
+ end
26
+
27
+ it 'renders template' do
28
+ expect(last_response.body).to include('stats')
29
+ expect(last_response.body).to include('search')
30
+ end
24
31
  end
25
32
 
26
- it 'renders template' do
27
- expect(last_response.body).to include('stats')
28
- expect(last_response.body).to include('search')
33
+ context "when there are no features to list" do
34
+ before do
35
+ @original_fun_enabled = Flipper::UI.configuration.fun
36
+ Flipper::UI.configuration.fun = fun_mode
37
+ end
38
+
39
+ after do
40
+ Flipper::UI.configuration.fun = @original_fun_enabled
41
+ end
42
+
43
+ context "when fun mode is enabled" do
44
+ let(:fun_mode) { true }
45
+ before { get '/features' }
46
+
47
+ it 'responds with success' do
48
+ expect(last_response.status).to be(200)
49
+ end
50
+
51
+ it 'renders template' do
52
+ expect(last_response.body).to include('And I\'ll flip your features.')
53
+ end
54
+ end
55
+
56
+ context "when fun mode is disabled" do
57
+ let(:fun_mode) { false }
58
+ before { get '/features' }
59
+
60
+ it 'responds with success' do
61
+ expect(last_response.status).to be(200)
62
+ end
63
+
64
+ it 'renders template' do
65
+ expect(last_response.body).to include('There aren\'t any features to configure.')
66
+ end
67
+ end
29
68
  end
30
69
  end
31
70
 
@@ -102,4 +102,15 @@ RSpec.describe Flipper::UI::Configuration do
102
102
  expect(configuration.feature_removal_enabled).to eq(false)
103
103
  end
104
104
  end
105
+
106
+ describe "#fun" do
107
+ it "has default value" do
108
+ expect(configuration.fun).to eq(true)
109
+ end
110
+
111
+ it "can be updated" do
112
+ configuration.fun = false
113
+ expect(configuration.fun).to eq(false)
114
+ end
115
+ end
105
116
  end
@@ -77,11 +77,11 @@ RSpec.describe Flipper::UI::Decorators::Feature do
77
77
  end
78
78
 
79
79
  it 'sorts :on before :off' do
80
- expect((on <=> conditional)).to be(-1)
80
+ expect((on <=> off)).to be(-1)
81
81
  end
82
82
 
83
83
  it 'sorts :conditional before :off' do
84
- expect((on <=> conditional)).to be(-1)
84
+ expect((conditional <=> off)).to be(-1)
85
85
  end
86
86
 
87
87
  it 'sorts on key for identical states' do
@@ -54,7 +54,7 @@ RSpec.describe Flipper::UI do
54
54
  # See https://github.com/jnunemaker/flipper/issues/80
55
55
  it 'can route features with names that match static directories' do
56
56
  post 'features/refactor-images/actors',
57
- { 'value' => 'User:6', 'operation' => 'enable', 'authenticity_token' => token },
57
+ { 'value' => 'User;6', 'operation' => 'enable', 'authenticity_token' => token },
58
58
  'rack.session' => session
59
59
  expect(last_response.status).to be(302)
60
60
  expect(last_response.headers['Location']).to eq('/features/refactor-images')
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.16.2
4
+ version: 0.17.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Nunemaker
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-04-20 00:00:00.000000000 Z
11
+ date: 2019-09-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -56,28 +56,34 @@ dependencies:
56
56
  requirements:
57
57
  - - "~>"
58
58
  - !ruby/object:Gem::Version
59
- version: 0.16.2
59
+ version: 0.17.1
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.16.2
66
+ version: 0.17.1
67
67
  - !ruby/object:Gem::Dependency
68
- name: erubis
68
+ name: erubi
69
69
  requirement: !ruby/object:Gem::Requirement
70
70
  requirements:
71
- - - "~>"
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: 1.0.0
74
+ - - "<"
72
75
  - !ruby/object:Gem::Version
73
- version: 2.7.0
76
+ version: 2.0.0
74
77
  type: :runtime
75
78
  prerelease: false
76
79
  version_requirements: !ruby/object:Gem::Requirement
77
80
  requirements:
78
- - - "~>"
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: 1.0.0
84
+ - - "<"
79
85
  - !ruby/object:Gem::Version
80
- version: 2.7.0
86
+ version: 2.0.0
81
87
  description: Rack middleware that provides a fully featured web interface for the
82
88
  flipper gem.
83
89
  email:
@@ -220,7 +226,6 @@ files:
220
226
  - lib/flipper/ui/decorators/feature.rb
221
227
  - lib/flipper/ui/decorators/gate.rb
222
228
  - lib/flipper/ui/error.rb
223
- - lib/flipper/ui/eruby.rb
224
229
  - lib/flipper/ui/middleware.rb
225
230
  - lib/flipper/ui/public/css/application.css
226
231
  - lib/flipper/ui/public/fonts/bootstrap/glyphicons-halflings-regular.eot
@@ -287,8 +292,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
287
292
  - !ruby/object:Gem::Version
288
293
  version: '0'
289
294
  requirements: []
290
- rubyforge_project:
291
- rubygems_version: 2.4.5.4
295
+ rubygems_version: 3.0.3
292
296
  signing_key:
293
297
  specification_version: 4
294
298
  summary: UI for the Flipper gem
@@ -1,11 +0,0 @@
1
- require 'erubis'
2
-
3
- module Flipper
4
- module UI
5
- # Version of erubis just for flipper.
6
- class Eruby < ::Erubis::Eruby
7
- # switches '<%= ... %>' to escaped and '<%== ... %>' to unescaped.
8
- include ::Erubis::EscapeEnhancer
9
- end
10
- end
11
- end