rails_omnibar 1.6.0 → 1.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/Gemfile +1 -1
- data/README.md +33 -9
- data/app/controllers/rails_omnibar/html_controller.rb +1 -1
- data/bin/console +1 -0
- data/lib/rails_omnibar/command/base.rb +9 -2
- data/lib/rails_omnibar/command/search.rb +2 -1
- data/lib/rails_omnibar/commands.rb +16 -7
- data/lib/rails_omnibar/conditions.rb +27 -0
- data/lib/rails_omnibar/config.rb +29 -19
- data/lib/rails_omnibar/inheritance.rb +9 -0
- data/lib/rails_omnibar/item/base.rb +9 -3
- data/lib/rails_omnibar/items.rb +2 -3
- data/lib/rails_omnibar/rendering.rb +5 -11
- data/lib/rails_omnibar/version.rb +1 -1
- data/spec/benchmarks/benchmark.rb +16 -0
- data/spec/lib/rails_omnibar/config_spec.rb +1 -3
- data/spec/lib/rails_omnibar/inheritance_spec.rb +79 -0
- data/spec/system/rails_omnibar_spec.rb +27 -0
- data/spec/templates/app_template.rb +1 -1
- data/spec/templates/my_omnibar_template.rb +8 -0
- metadata +9 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d860d3dad8c472ba1b2ea46adc5b99c22eea4a846ad98ec025d78b36ecede83e
|
4
|
+
data.tar.gz: fac209673acca1f5102c6162ff1eb597ff6b9d3588987535c991dd17cbd3c40a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 76cdf9f017b33261eecf4e84c4f00f6dcca277c84f21107073eca5f4e53c50370d33697a7f56db22cb9b8a51645d21c082d94cecb1f21c09b876f3b46a4a855d
|
7
|
+
data.tar.gz: 6d8bb4548482b948ce79b1f4d38189453b37d5d95ddd8e7cac9c7b095b263c8ae369feb8b8d9a5a8cb57a844a0c9e1bbb34b562ba41f62acb72903e113b05e2c
|
data/CHANGELOG.md
CHANGED
@@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file.
|
|
4
4
|
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
5
5
|
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
6
6
|
|
7
|
+
## [1.7.0] - 2024-07-02
|
8
|
+
|
9
|
+
### Added
|
10
|
+
|
11
|
+
- conditional rendering of items (`add_item(if: ->{ ... })`)
|
12
|
+
- conditional execution of commands (`add_command(if: ->{ ... })`)
|
13
|
+
- inheritance of items, commands, and configuration (`Bar2 = Class.new(Bar1)`)
|
14
|
+
|
7
15
|
## [1.6.0] - 2024-01-25
|
8
16
|
|
9
17
|
### Added
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -89,14 +89,14 @@ end
|
|
89
89
|
Render it somewhere. E.g. `app/views/layouts/application.html.erb`:
|
90
90
|
|
91
91
|
```erb
|
92
|
-
<%= Omnibar.render %>
|
92
|
+
<%= Omnibar.render(self) %>
|
93
93
|
```
|
94
94
|
|
95
95
|
If you have a fully decoupled frontend, use `Omnibar.html_url` instead, fetch the omnibar HTML from there, and inject it.
|
96
96
|
|
97
97
|
### Authorization
|
98
98
|
|
99
|
-
You can limit access to commands (e.g. search commands)
|
99
|
+
You can limit access to commands (e.g. search commands) and items.
|
100
100
|
|
101
101
|
#### Option 1: globally limit engine access
|
102
102
|
|
@@ -119,19 +119,35 @@ MyOmnibar.auth = ->(controller, omnibar:) do
|
|
119
119
|
end
|
120
120
|
```
|
121
121
|
|
122
|
+
#### Option 3: Item-level conditionality
|
123
|
+
|
124
|
+
Items and commands can have an `if` proc that is executed in the controller context. If it returns false, the item is not shown and the command is not executed.
|
125
|
+
|
126
|
+
```ruby
|
127
|
+
MyOmnibar.add_item(
|
128
|
+
title: 'Admin link',
|
129
|
+
url: admin_url,
|
130
|
+
if: ->{ current_user.admin? }
|
131
|
+
)
|
132
|
+
```
|
133
|
+
|
134
|
+
For this to work, the controller context must be given to the omnibar when rendering (e.g. by passing `self` in a view):
|
135
|
+
|
136
|
+
```erb
|
137
|
+
<%= Omnibar.render(self) %>
|
138
|
+
```
|
139
|
+
|
122
140
|
### Using multiple different omnibars
|
123
141
|
|
124
142
|
```ruby
|
125
|
-
BaseOmnibar = Class.new(RailsOmnibar)
|
126
|
-
BaseOmnibar.configure do |c|
|
143
|
+
BaseOmnibar = Class.new(RailsOmnibar).configure do |c|
|
127
144
|
c.add_item(
|
128
145
|
title: 'Log in',
|
129
146
|
url: Rails.application.routes.url_helpers.log_in_url
|
130
147
|
)
|
131
148
|
end
|
132
149
|
|
133
|
-
UserOmnibar = Class.new(RailsOmnibar)
|
134
|
-
UserOmnibar.configure do |c|
|
150
|
+
UserOmnibar = Class.new(RailsOmnibar).configure do |c|
|
135
151
|
c.auth = ->{ user_signed_in? }
|
136
152
|
c.add_item(
|
137
153
|
title: 'Log out',
|
@@ -143,7 +159,15 @@ end
|
|
143
159
|
Then, in some layout:
|
144
160
|
|
145
161
|
```erb
|
146
|
-
<%= (user_signed_in? ? UserOmnibar : BaseOmnibar).render %>
|
162
|
+
<%= (user_signed_in? ? UserOmnibar : BaseOmnibar).render(self) %>
|
163
|
+
```
|
164
|
+
|
165
|
+
Omnibars can also inherit commands, configuration, and items from existing omnibars:
|
166
|
+
|
167
|
+
```ruby
|
168
|
+
SuperuserOmnibar = Class.new(UserOmnibar).configure do |c|
|
169
|
+
# add more items that only superusers should get
|
170
|
+
end
|
147
171
|
```
|
148
172
|
|
149
173
|
### Other options and usage patterns
|
@@ -172,7 +196,7 @@ end
|
|
172
196
|
```ruby
|
173
197
|
module AddOmnibar
|
174
198
|
def build_page(...)
|
175
|
-
within(super) { text_node(MyOmnibar.render) }
|
199
|
+
within(super) { text_node(MyOmnibar.render(self)) }
|
176
200
|
end
|
177
201
|
end
|
178
202
|
ActiveAdmin::Views::Pages::Base.prepend(AddOmnibar)
|
@@ -180,7 +204,7 @@ ActiveAdmin::Views::Pages::Base.prepend(AddOmnibar)
|
|
180
204
|
|
181
205
|
##### To render in RailsAdmin
|
182
206
|
|
183
|
-
Add `MyOmnibar.render` to `app/views/layouts/rails_admin/application.*`.
|
207
|
+
Add `MyOmnibar.render(self)` to `app/views/layouts/rails_admin/application.*`.
|
184
208
|
|
185
209
|
#### Adding all index routes as searchable items
|
186
210
|
|
data/bin/console
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
class RailsOmnibar
|
2
2
|
module Command
|
3
3
|
class Base
|
4
|
-
attr_reader :pattern, :resolver, :description, :example
|
4
|
+
attr_reader :pattern, :resolver, :description, :example, :if
|
5
5
|
|
6
|
-
def initialize(pattern:, resolver:, description: nil, example: nil)
|
6
|
+
def initialize(pattern:, resolver:, description: nil, example: nil, if: nil)
|
7
7
|
@pattern = cast_to_pattern(pattern)
|
8
8
|
@resolver = RailsOmnibar.cast_to_proc(resolver)
|
9
9
|
@description = description
|
10
10
|
@example = example
|
11
|
+
@if = RailsOmnibar.cast_to_condition(binding.local_variable_get(:if))
|
11
12
|
end
|
12
13
|
|
13
14
|
def call(input, controller:, omnibar:)
|
@@ -19,6 +20,12 @@ class RailsOmnibar
|
|
19
20
|
results.map { |e| RailsOmnibar.cast_to_item(e) }
|
20
21
|
end
|
21
22
|
|
23
|
+
def handle?(input, controller:, omnibar:)
|
24
|
+
return false unless pattern.match?(input)
|
25
|
+
|
26
|
+
RailsOmnibar.evaluate_condition(self.if, context: controller, omnibar: omnibar)
|
27
|
+
end
|
28
|
+
|
22
29
|
private
|
23
30
|
|
24
31
|
def cast_to_pattern(arg)
|
@@ -29,7 +29,7 @@ class RailsOmnibar
|
|
29
29
|
|
30
30
|
# ActiveRecord-specific search.
|
31
31
|
class RecordSearch < Search
|
32
|
-
def initialize(model:, columns: :id, pattern: nil, finder: nil, itemizer: nil, example: nil)
|
32
|
+
def initialize(model:, columns: :id, pattern: nil, finder: nil, itemizer: nil, example: nil, if: nil)
|
33
33
|
# casting and validations
|
34
34
|
model = model.to_s.classify.constantize unless model.is_a?(Class)
|
35
35
|
model < ActiveRecord::Base || raise(ArgumentError, 'model: must be a model')
|
@@ -64,6 +64,7 @@ class RailsOmnibar
|
|
64
64
|
pattern: pattern,
|
65
65
|
finder: finder,
|
66
66
|
itemizer: itemizer,
|
67
|
+
if: binding.local_variable_get(:if),
|
67
68
|
)
|
68
69
|
end
|
69
70
|
end
|
@@ -1,17 +1,15 @@
|
|
1
1
|
class RailsOmnibar
|
2
2
|
def handle(input, controller)
|
3
|
-
handler = commands.find
|
3
|
+
handler = commands.find do |cmd|
|
4
|
+
cmd.handle?(input, controller: controller, omnibar: self)
|
5
|
+
end
|
4
6
|
handler&.call(input, controller: controller, omnibar: self) || []
|
5
7
|
end
|
6
8
|
|
7
|
-
def command_pattern
|
8
|
-
commands.any? ? Regexp.union(commands.map(&:pattern)) : /$NO_COMMANDS/
|
9
|
-
end
|
10
|
-
|
11
9
|
def add_command(command)
|
12
|
-
check_const_and_clear_cache
|
13
10
|
commands << RailsOmnibar.cast_to_command(command)
|
14
|
-
|
11
|
+
clear_command_pattern_cache
|
12
|
+
self.class
|
15
13
|
end
|
16
14
|
|
17
15
|
def self.cast_to_command(arg)
|
@@ -43,6 +41,17 @@ class RailsOmnibar
|
|
43
41
|
|
44
42
|
private
|
45
43
|
|
44
|
+
def command_pattern
|
45
|
+
@command_pattern ||= begin
|
46
|
+
re = commands.any? ? Regexp.union(commands.map(&:pattern)) : /$NO_COMMANDS/
|
47
|
+
JsRegex.new!(re, target: 'ES2018')
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def clear_command_pattern_cache
|
52
|
+
@command_pattern = nil
|
53
|
+
end
|
54
|
+
|
46
55
|
def commands
|
47
56
|
@commands ||= []
|
48
57
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class RailsOmnibar
|
2
|
+
def self.cast_to_condition(arg)
|
3
|
+
case arg
|
4
|
+
when nil, true, false then arg
|
5
|
+
else
|
6
|
+
arg.try(:arity) == 0 ? arg : RailsOmnibar.cast_to_proc(arg)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.evaluate_condition(condition, context:, omnibar:)
|
11
|
+
case condition
|
12
|
+
when nil, true then true
|
13
|
+
when false then false
|
14
|
+
else
|
15
|
+
context || raise(<<~EOS)
|
16
|
+
Missing context for condition, please render the omnibar with `.render(self)`
|
17
|
+
EOS
|
18
|
+
if condition.try(:arity) == 0
|
19
|
+
context.instance_exec(&condition)
|
20
|
+
elsif condition.respond_to?(:call)
|
21
|
+
condition.call(context, controller: context, omnibar: omnibar)
|
22
|
+
else
|
23
|
+
raise("unsupported condition type: #{condition.class}")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/rails_omnibar/config.rb
CHANGED
@@ -1,12 +1,14 @@
|
|
1
1
|
class RailsOmnibar
|
2
2
|
def configure(&block)
|
3
|
-
check_const_and_clear_cache
|
4
3
|
tap(&block)
|
4
|
+
self.class
|
5
5
|
end
|
6
6
|
|
7
|
-
attr_reader :auth
|
8
7
|
def auth=(arg)
|
9
|
-
|
8
|
+
config[:auth] = arg.try(:arity) == 0 ? arg : RailsOmnibar.cast_to_proc(arg)
|
9
|
+
end
|
10
|
+
def auth
|
11
|
+
config[:auth]
|
10
12
|
end
|
11
13
|
def authorize(controller)
|
12
14
|
if auth.nil?
|
@@ -20,33 +22,39 @@ class RailsOmnibar
|
|
20
22
|
|
21
23
|
def max_results=(arg)
|
22
24
|
arg.is_a?(Integer) && arg > 0 || raise(ArgumentError, 'max_results must be > 0')
|
23
|
-
|
25
|
+
config[:max_results] = arg
|
24
26
|
end
|
25
27
|
def max_results
|
26
|
-
|
28
|
+
config[:max_results] || 10
|
27
29
|
end
|
28
30
|
|
29
|
-
|
31
|
+
def modal=(arg)
|
32
|
+
config[:modal] = arg
|
33
|
+
end
|
30
34
|
def modal?
|
31
|
-
|
35
|
+
config.key?(:modal) ? !!config[:modal] : false
|
32
36
|
end
|
33
37
|
|
34
|
-
|
38
|
+
def calculator=(arg)
|
39
|
+
config[:calculator] = arg
|
40
|
+
end
|
35
41
|
def calculator?
|
36
|
-
|
42
|
+
config.key?(:calculator) ? !!config[:calculator] : true
|
37
43
|
end
|
38
44
|
|
39
|
-
def hotkey
|
40
|
-
@hotkey || 'k'
|
41
|
-
end
|
42
45
|
def hotkey=(arg)
|
43
46
|
arg.to_s.size == 1 || raise(ArgumentError, 'hotkey must have length 1')
|
44
|
-
|
47
|
+
config[:hotkey] = arg.to_s.downcase
|
48
|
+
end
|
49
|
+
def hotkey
|
50
|
+
config[:hotkey] || 'k'
|
45
51
|
end
|
46
52
|
|
47
|
-
|
53
|
+
def placeholder=(arg)
|
54
|
+
config[:placeholder] = arg
|
55
|
+
end
|
48
56
|
def placeholder
|
49
|
-
return
|
57
|
+
return config[:placeholder].presence unless config[:placeholder].nil?
|
50
58
|
|
51
59
|
help_item = items.find { |i| i.type == :help }
|
52
60
|
help_item && "Hint: Type `#{help_item.title}` for help"
|
@@ -54,13 +62,15 @@ class RailsOmnibar
|
|
54
62
|
|
55
63
|
private
|
56
64
|
|
65
|
+
def config
|
66
|
+
@config ||= {}
|
67
|
+
end
|
68
|
+
|
57
69
|
def omnibar_class
|
58
70
|
self.class.name || raise(<<~EOS)
|
59
|
-
RailsOmnibar subclasses must be assigned to constants
|
60
|
-
before configuring or rendering them. E.g.:
|
71
|
+
RailsOmnibar subclasses must be assigned to constants, e.g.:
|
61
72
|
|
62
|
-
Foo = Class.new(RailsOmnibar)
|
63
|
-
Foo.configure { ... }
|
73
|
+
Foo = Class.new(RailsOmnibar).configure { ... }
|
64
74
|
EOS
|
65
75
|
end
|
66
76
|
end
|
@@ -1,9 +1,9 @@
|
|
1
1
|
class RailsOmnibar
|
2
2
|
module Item
|
3
3
|
class Base
|
4
|
-
attr_reader :title, :url, :icon, :modal_html, :suggested, :type
|
4
|
+
attr_reader :title, :url, :icon, :modal_html, :suggested, :type, :if
|
5
5
|
|
6
|
-
def initialize(title:, url: nil, icon: nil, modal_html: nil, suggested: false, type: :default)
|
6
|
+
def initialize(title:, url: nil, icon: nil, modal_html: nil, suggested: false, type: :default, if: nil)
|
7
7
|
url.present? && modal_html.present? && raise(ArgumentError, 'use EITHER url: OR modal_html:')
|
8
8
|
|
9
9
|
@title = validate_title(title)
|
@@ -12,10 +12,16 @@ class RailsOmnibar
|
|
12
12
|
@modal_html = modal_html
|
13
13
|
@suggested = !!suggested
|
14
14
|
@type = type
|
15
|
+
@if = RailsOmnibar.cast_to_condition(binding.local_variable_get(:if))
|
15
16
|
end
|
16
17
|
|
17
18
|
def as_json(*)
|
18
|
-
|
19
|
+
@as_json ||=
|
20
|
+
{ title: title, url: url, icon: icon, modalHTML: modal_html, suggested: suggested, type: type }
|
21
|
+
end
|
22
|
+
|
23
|
+
def render?(context:, omnibar:)
|
24
|
+
RailsOmnibar.evaluate_condition(self.if, context: context, omnibar: omnibar)
|
19
25
|
end
|
20
26
|
|
21
27
|
private
|
data/lib/rails_omnibar/items.rb
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
class RailsOmnibar
|
2
2
|
def add_item(item)
|
3
|
-
check_const_and_clear_cache
|
4
3
|
items << RailsOmnibar.cast_to_item(item)
|
5
|
-
self
|
4
|
+
self.class
|
6
5
|
end
|
7
6
|
|
8
7
|
def add_items(*args)
|
9
8
|
args.each { |arg| add_item(arg) }
|
10
|
-
self
|
9
|
+
self.class
|
11
10
|
end
|
12
11
|
|
13
12
|
def self.cast_to_item(arg)
|
@@ -1,6 +1,7 @@
|
|
1
1
|
class RailsOmnibar
|
2
|
-
def render
|
3
|
-
@
|
2
|
+
def render(context = nil)
|
3
|
+
@context = context
|
4
|
+
<<~HTML.html_safe
|
4
5
|
<script src='#{urls.js_path}?v=#{RailsOmnibar::VERSION}' type='text/javascript'></script>
|
5
6
|
<div id='mount-rails-omnibar'>
|
6
7
|
<script type="application/json">#{to_json}</script>
|
@@ -17,9 +18,9 @@ class RailsOmnibar
|
|
17
18
|
def as_json(*)
|
18
19
|
{
|
19
20
|
calculator: calculator?,
|
20
|
-
commandPattern:
|
21
|
+
commandPattern: command_pattern,
|
21
22
|
hotkey: hotkey,
|
22
|
-
items: items,
|
23
|
+
items: items.select { |i| i.render?(context: @context, omnibar: self) },
|
23
24
|
maxResults: max_results,
|
24
25
|
modal: modal?,
|
25
26
|
placeholder: placeholder,
|
@@ -30,11 +31,4 @@ class RailsOmnibar
|
|
30
31
|
def urls
|
31
32
|
@urls ||= RailsOmnibar::Engine.routes.url_helpers
|
32
33
|
end
|
33
|
-
|
34
|
-
private
|
35
|
-
|
36
|
-
def check_const_and_clear_cache
|
37
|
-
omnibar_class # trigger constant assignment check
|
38
|
-
@cached_html = nil
|
39
|
-
end
|
40
34
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require_relative '../rails_helper'
|
2
|
+
require 'bundler/inline'
|
3
|
+
|
4
|
+
gemfile do
|
5
|
+
source 'https://rubygems.org'
|
6
|
+
gem 'benchmark-ips', require: 'benchmark/ips'
|
7
|
+
end
|
8
|
+
|
9
|
+
BenchmarkBar = Class.new(RailsOmnibar).configure do |c|
|
10
|
+
100.times { c.add_item(title: rand.to_s, url: rand.to_s) }
|
11
|
+
10.times { c.add_command(description: rand.to_s, pattern: /#{rand}/, example: rand.to_s, resolver: ->(_){}) }
|
12
|
+
end
|
13
|
+
|
14
|
+
Benchmark.ips do |x|
|
15
|
+
x.report('render') { BenchmarkBar.render } # ~3k ips
|
16
|
+
end
|
@@ -53,10 +53,8 @@ describe RailsOmnibar do
|
|
53
53
|
expect(subject.placeholder).to eq nil
|
54
54
|
end
|
55
55
|
|
56
|
-
it 'raises when trying to
|
56
|
+
it 'raises when trying to render from an anonymous class' do
|
57
57
|
klass = Class.new(RailsOmnibar)
|
58
|
-
expect { klass.configure {} }.to raise_error(/constant/)
|
59
|
-
expect { klass.add_item(title: 'a', url: 'b') }.to raise_error(/constant/)
|
60
58
|
expect { klass.render }.to raise_error(/constant/)
|
61
59
|
end
|
62
60
|
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
describe RailsOmnibar do
|
4
|
+
it 'inherits commands from a parent class' do
|
5
|
+
bar1 = Class.new(RailsOmnibar).configure do |c|
|
6
|
+
c.add_command(pattern: /foo1/, resolver: ->(_){})
|
7
|
+
end
|
8
|
+
bar2 = Class.new(bar1).configure do |c|
|
9
|
+
c.add_command(pattern: /foo2/, resolver: ->(_){})
|
10
|
+
end
|
11
|
+
bar3 = Class.new(bar2).configure do |c|
|
12
|
+
c.add_command(pattern: /foo3/, resolver: ->(_){})
|
13
|
+
end
|
14
|
+
|
15
|
+
commands = ->(bar) do
|
16
|
+
bar.send(:singleton).send(:commands).map { |c| c.pattern.source }
|
17
|
+
end
|
18
|
+
|
19
|
+
expect(commands.call(bar1)).to eq ['foo1']
|
20
|
+
expect(commands.call(bar2)).to eq ['foo1', 'foo2']
|
21
|
+
expect(commands.call(bar3)).to eq ['foo1', 'foo2', 'foo3']
|
22
|
+
|
23
|
+
# later changes to commands should not affect child classes
|
24
|
+
bar1.add_command(pattern: /foo1B/, resolver: ->(_){})
|
25
|
+
expect(commands.call(bar1)).to eq ['foo1', 'foo1B']
|
26
|
+
expect(commands.call(bar2)).to eq ['foo1', 'foo2']
|
27
|
+
expect(commands.call(bar3)).to eq ['foo1', 'foo2', 'foo3']
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'inherits configuration from a parent class' do
|
31
|
+
bar1 = Class.new(RailsOmnibar).configure do |c|
|
32
|
+
c.max_results = 7
|
33
|
+
c.placeholder = 'hi'
|
34
|
+
c.hotkey = 'j'
|
35
|
+
end
|
36
|
+
bar2 = Class.new(bar1).configure do |c|
|
37
|
+
c.max_results = 8
|
38
|
+
end
|
39
|
+
bar3 = Class.new(bar2).configure do |c|
|
40
|
+
c.hotkey = 'k'
|
41
|
+
end
|
42
|
+
|
43
|
+
config = ->(bar) { bar.send(:singleton).send(:config) }
|
44
|
+
|
45
|
+
expect(config.call(bar1)).to eq(max_results: 7, placeholder: 'hi', hotkey: 'j')
|
46
|
+
expect(config.call(bar2)).to eq(max_results: 8, placeholder: 'hi', hotkey: 'j')
|
47
|
+
expect(config.call(bar3)).to eq(max_results: 8, placeholder: 'hi', hotkey: 'k')
|
48
|
+
|
49
|
+
# later changes to config should not affect child classes
|
50
|
+
bar1.placeholder = 'bye'
|
51
|
+
expect(bar1.placeholder).to eq 'bye'
|
52
|
+
expect(bar2.placeholder).to eq 'hi'
|
53
|
+
expect(bar3.placeholder).to eq 'hi'
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'inherits items from a parent class' do
|
57
|
+
bar1 = Class.new(RailsOmnibar).configure do |c|
|
58
|
+
c.add_item(title: 'foo1', url: 'bar1')
|
59
|
+
end
|
60
|
+
bar2 = Class.new(bar1).configure do |c|
|
61
|
+
c.add_item(title: 'foo2', url: 'bar2')
|
62
|
+
end
|
63
|
+
bar3 = Class.new(bar2).configure do |c|
|
64
|
+
c.add_item(title: 'foo3', url: 'bar3')
|
65
|
+
end
|
66
|
+
|
67
|
+
items = ->(bar) { bar.send(:singleton).send(:items).map(&:title) }
|
68
|
+
|
69
|
+
expect(items.call(bar1)).to eq ['foo1']
|
70
|
+
expect(items.call(bar2)).to eq ['foo1', 'foo2']
|
71
|
+
expect(items.call(bar3)).to eq ['foo1', 'foo2', 'foo3']
|
72
|
+
|
73
|
+
# later changes to items should not affect child classes
|
74
|
+
bar1.add_item(title: 'foo1B', url: 'bar1B')
|
75
|
+
expect(items.call(bar1)).to eq ['foo1', 'foo1B']
|
76
|
+
expect(items.call(bar2)).to eq ['foo1', 'foo2']
|
77
|
+
expect(items.call(bar3)).to eq ['foo1', 'foo2', 'foo3']
|
78
|
+
end
|
79
|
+
end
|
@@ -119,6 +119,33 @@ describe RailsOmnibar do
|
|
119
119
|
expect(page).not_to have_content 'fake_result_1'
|
120
120
|
end
|
121
121
|
|
122
|
+
it 'can have conditional items and commands' do
|
123
|
+
visit main_app.root_path
|
124
|
+
send_keys([:control, 'm']) # custom hotkey, c.f. my_omnibar_template.rb
|
125
|
+
expect(page).to have_selector 'input'
|
126
|
+
|
127
|
+
type('condi')
|
128
|
+
expect(page).not_to have_content 'conditional item'
|
129
|
+
|
130
|
+
FactoryBot.create(:user)
|
131
|
+
expect { type('DELETE users'); sleep 0.3 }.not_to change { User.count }
|
132
|
+
|
133
|
+
# now again with truthy condition
|
134
|
+
ENV['FAKE_OMNIBAR_IF'] = '1'
|
135
|
+
refresh # reload page
|
136
|
+
|
137
|
+
send_keys([:control, 'm']) # custom hotkey, c.f. my_omnibar_template.rb
|
138
|
+
expect(page).to have_selector 'input'
|
139
|
+
|
140
|
+
type('condi')
|
141
|
+
expect(page).to have_content 'conditional item'
|
142
|
+
|
143
|
+
FactoryBot.create(:user)
|
144
|
+
expect { type('DELETE users'); sleep 0.3 }.to change { User.count }.to(0)
|
145
|
+
ensure
|
146
|
+
ENV['FAKE_OMNIBAR_IF'] = nil
|
147
|
+
end
|
148
|
+
|
122
149
|
def type(str)
|
123
150
|
find('input').set(str)
|
124
151
|
end
|
@@ -29,7 +29,7 @@ file 'app/lib/aa/users.rb', File.read(__dir__ + '/user_resource_template.rb
|
|
29
29
|
|
30
30
|
inject_into_class 'app/controllers/application_controller.rb', 'ApplicationController', <<-RUBY
|
31
31
|
def index
|
32
|
-
render html: (MyOmnibar.render + OtherOmnibar.render)
|
32
|
+
render html: (MyOmnibar.render(self) + OtherOmnibar.render(self))
|
33
33
|
end
|
34
34
|
RUBY
|
35
35
|
|
@@ -3,6 +3,7 @@ MyOmnibar = RailsOmnibar.configure do |c|
|
|
3
3
|
|
4
4
|
c.add_item(title: 'important URL', url: 'https://www.disney.com', suggested: true)
|
5
5
|
c.add_item(title: 'boring URL', url: 'https://www.github.com')
|
6
|
+
c.add_item(title: 'conditional item', url: '#', if: -> { ENV['FAKE_OMNIBAR_IF'] })
|
6
7
|
|
7
8
|
c.add_webadmin_items(prefix: 'Admin:')
|
8
9
|
|
@@ -43,6 +44,13 @@ MyOmnibar = RailsOmnibar.configure do |c|
|
|
43
44
|
end,
|
44
45
|
)
|
45
46
|
|
47
|
+
c.add_command(
|
48
|
+
description: 'Delete users',
|
49
|
+
pattern: /DELETE (.+)/i,
|
50
|
+
resolver: ->(value){ { title: value.classify.constantize.delete_all.to_s } },
|
51
|
+
if: ->{ ENV['FAKE_OMNIBAR_IF'] },
|
52
|
+
)
|
53
|
+
|
46
54
|
c.add_help
|
47
55
|
|
48
56
|
# Use a hotkey that is the same in most keyboard layouts to work around
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rails_omnibar
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Janosch Müller
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
|
-
date: 2024-
|
10
|
+
date: 2024-07-02 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: js_regex
|
@@ -45,7 +44,6 @@ dependencies:
|
|
45
44
|
- !ruby/object:Gem::Version
|
46
45
|
version: '8.0'
|
47
46
|
description: Omnibar for Rails
|
48
|
-
email:
|
49
47
|
executables: []
|
50
48
|
extensions: []
|
51
49
|
extra_rdoc_files: []
|
@@ -79,8 +77,10 @@ files:
|
|
79
77
|
- lib/rails_omnibar/command/base.rb
|
80
78
|
- lib/rails_omnibar/command/search.rb
|
81
79
|
- lib/rails_omnibar/commands.rb
|
80
|
+
- lib/rails_omnibar/conditions.rb
|
82
81
|
- lib/rails_omnibar/config.rb
|
83
82
|
- lib/rails_omnibar/engine.rb
|
83
|
+
- lib/rails_omnibar/inheritance.rb
|
84
84
|
- lib/rails_omnibar/item/base.rb
|
85
85
|
- lib/rails_omnibar/item/help.rb
|
86
86
|
- lib/rails_omnibar/item/webadmin.rb
|
@@ -89,7 +89,9 @@ files:
|
|
89
89
|
- lib/rails_omnibar/version.rb
|
90
90
|
- package.json
|
91
91
|
- rails_omnibar.gemspec
|
92
|
+
- spec/benchmarks/benchmark.rb
|
92
93
|
- spec/lib/rails_omnibar/config_spec.rb
|
94
|
+
- spec/lib/rails_omnibar/inheritance_spec.rb
|
93
95
|
- spec/lib/rails_omnibar/item/base_spec.rb
|
94
96
|
- spec/lib/rails_omnibar/version_spec.rb
|
95
97
|
- spec/rails_helper.rb
|
@@ -105,7 +107,6 @@ homepage: https://github.com/jaynetics/rails_omnibar
|
|
105
107
|
licenses:
|
106
108
|
- MIT
|
107
109
|
metadata: {}
|
108
|
-
post_install_message:
|
109
110
|
rdoc_options: []
|
110
111
|
require_paths:
|
111
112
|
- lib
|
@@ -120,12 +121,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
120
121
|
- !ruby/object:Gem::Version
|
121
122
|
version: '0'
|
122
123
|
requirements: []
|
123
|
-
rubygems_version: 3.
|
124
|
-
signing_key:
|
124
|
+
rubygems_version: 3.6.0.dev
|
125
125
|
specification_version: 4
|
126
126
|
summary: Omnibar for Rails
|
127
127
|
test_files:
|
128
|
+
- spec/benchmarks/benchmark.rb
|
128
129
|
- spec/lib/rails_omnibar/config_spec.rb
|
130
|
+
- spec/lib/rails_omnibar/inheritance_spec.rb
|
129
131
|
- spec/lib/rails_omnibar/item/base_spec.rb
|
130
132
|
- spec/lib/rails_omnibar/version_spec.rb
|
131
133
|
- spec/rails_helper.rb
|