rails_omnibar 1.6.0 → 1.7.0

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
2
  SHA256:
3
- metadata.gz: 07132bcc31a15ed0d5ba925a00bf25ac2656babd01c15e8df03ac41b46897512
4
- data.tar.gz: f18ef9526a35db7790b4c79ae597df3bbcf4ab93273e245ba753650b427453ca
3
+ metadata.gz: d860d3dad8c472ba1b2ea46adc5b99c22eea4a846ad98ec025d78b36ecede83e
4
+ data.tar.gz: fac209673acca1f5102c6162ff1eb597ff6b9d3588987535c991dd17cbd3c40a
5
5
  SHA512:
6
- metadata.gz: 25e3eb51a271cb5c229a6850c138f783a828b6b08434625ebca9aa75ea16c7368073eaf52260270ea1599c74734a7662cc0f0c8be700295c4aa062b26018efc8
7
- data.tar.gz: a4e5a52e52eadaedc34018b95f79aaf084c6310d0bb1a33fd68188736e7a7028bbaa6c3ed090788d7e256b8df5f15989c562791f6fe389192a8220f0596fe6ca
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
@@ -12,7 +12,7 @@ group :development, :test do
12
12
  gem 'puma', '~> 6.0'
13
13
  gem 'rake', '~> 13.0'
14
14
  gem 'rspec-rails', '~> 5.0'
15
- gem 'sqlite3', '>= 1.3.6'
15
+ gem 'sqlite3', '~> 1.4'
16
16
  gem 'sprockets-rails', '~> 3.4' # for activeadmin
17
17
  gem 'webdrivers', '~> 5.0'
18
18
  end
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). This will not limit access to plain items.
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
 
@@ -1,5 +1,5 @@
1
1
  class RailsOmnibar::HtmlController < RailsOmnibar::BaseController
2
2
  def show
3
- render html: omnibar.render
3
+ render html: omnibar.render(self)
4
4
  end
5
5
  end
data/bin/console CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'bundler/setup'
4
4
  require 'rails_omnibar'
5
+ require_relative '../spec/dummy/config/environment'
5
6
 
6
7
  require 'irb'
7
8
  IRB.start(__FILE__)
@@ -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 { |h| h.pattern.match?(input) }
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
- self
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
@@ -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
- @auth = arg.try(:arity) == 0 ? arg : RailsOmnibar.cast_to_proc(arg)
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
- @max_results = arg
25
+ config[:max_results] = arg
24
26
  end
25
27
  def max_results
26
- @max_results || 10
28
+ config[:max_results] || 10
27
29
  end
28
30
 
29
- attr_writer :modal
31
+ def modal=(arg)
32
+ config[:modal] = arg
33
+ end
30
34
  def modal?
31
- instance_variable_defined?(:@modal) ? !!@modal : false
35
+ config.key?(:modal) ? !!config[:modal] : false
32
36
  end
33
37
 
34
- attr_writer :calculator
38
+ def calculator=(arg)
39
+ config[:calculator] = arg
40
+ end
35
41
  def calculator?
36
- instance_variable_defined?(:@calculator) ? !!@calculator : true
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
- @hotkey = arg.to_s.downcase
47
+ config[:hotkey] = arg.to_s.downcase
48
+ end
49
+ def hotkey
50
+ config[:hotkey] || 'k'
45
51
  end
46
52
 
47
- attr_writer :placeholder
53
+ def placeholder=(arg)
54
+ config[:placeholder] = arg
55
+ end
48
56
  def placeholder
49
- return @placeholder.presence unless @placeholder.nil?
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
@@ -0,0 +1,9 @@
1
+ class RailsOmnibar
2
+ def self.inherited(subclass)
3
+ bar1 = singleton
4
+ bar2 = subclass.send(:singleton)
5
+ %i[@commands @config @items].each do |ivar|
6
+ bar2.instance_variable_set(ivar, bar1.instance_variable_get(ivar).dup)
7
+ end
8
+ end
9
+ 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
- { title: title, url: url, icon: icon, modalHTML: modal_html, suggested: suggested, type: type }
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
@@ -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
- @cached_html ||= <<~HTML.html_safe
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: JsRegex.new!(command_pattern, target: 'ES2018'),
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
@@ -1,3 +1,3 @@
1
1
  class RailsOmnibar
2
- VERSION = '1.6.0'
2
+ VERSION = '1.7.0'
3
3
  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 configure or render from an anonymous class' do
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.6.0
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-01-25 00:00:00.000000000 Z
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.5.0.dev
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