flipper-ui 0.16.2 → 0.17.1

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