rails_omnibar 1.3.2 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,23 @@
1
1
  class RailsOmnibar
2
+ def configure(&block)
3
+ check_const_and_clear_cache
4
+ tap(&block)
5
+ end
6
+
7
+ attr_reader :auth
8
+ def auth=(arg)
9
+ @auth = arg.try(:arity) == 0 ? arg : RailsOmnibar.cast_to_proc(arg)
10
+ end
11
+ def authorize(controller)
12
+ if auth.nil?
13
+ true
14
+ elsif auth.arity == 0
15
+ controller.instance_exec(&auth)
16
+ else
17
+ auth.call(controller, controller: controller, omnibar: self)
18
+ end
19
+ end
20
+
2
21
  def max_results=(arg)
3
22
  arg.is_a?(Integer) && arg > 0 || raise(ArgumentError, 'max_results must be > 0')
4
23
  @max_results = arg
@@ -32,4 +51,16 @@ class RailsOmnibar
32
51
  help_item = items.find { |i| i.type == :help }
33
52
  help_item && "Hint: Type `#{help_item.title}` for help"
34
53
  end
54
+
55
+ private
56
+
57
+ def omnibar_class
58
+ self.class.name || raise(<<~EOS)
59
+ RailsOmnibar subclasses must be assigned to constants
60
+ before configuring or rendering them. E.g.:
61
+
62
+ Foo = Class.new(RailsOmnibar)
63
+ Foo.configure { ... }
64
+ EOS
65
+ end
35
66
  end
@@ -1,7 +1,6 @@
1
1
  class RailsOmnibar
2
2
  def add_help(**kwargs)
3
3
  add_item Item::Help.new(for_commands: commands, **kwargs)
4
- self.class
5
4
  end
6
5
 
7
6
  module Item
@@ -0,0 +1,45 @@
1
+ class RailsOmnibar
2
+ # adds fuzzy-searchable links to each defined resource index of ActiveAdmin etc.
3
+ def add_webadmin_items(icon: default_admin_item_icon, prefix: nil, suffix: nil, suggested: false)
4
+ if defined?(ActiveAdmin)
5
+ add_active_admin_items(icon: icon, prefix: prefix, suffix: suffix, suggested: suggested)
6
+ elsif defined?(RailsAdmin)
7
+ add_rails_admin_items(icon: icon, prefix: prefix, suffix: suffix, suggested: suggested)
8
+ else
9
+ raise "#{__method__} currently only works with ActiveAdmin or RailsAdmin"
10
+ end
11
+ end
12
+
13
+ def add_active_admin_items(icon: default_admin_item_icon, prefix: nil, suffix: nil, suggested: false)
14
+ ActiveAdmin.load!
15
+
16
+ ActiveAdmin.application.namespaces.each do |namespace|
17
+ namespace.fetch_menu(ActiveAdmin::DEFAULT_MENU) # ensure menu is loaded
18
+
19
+ namespace.resources.each do |res|
20
+ next unless (res.controller.action_methods & ['index', :index]).any?
21
+ next unless index = res.route_collection_path rescue next
22
+ next unless label = res.menu_item&.label.presence
23
+
24
+ title = [prefix, label, suffix].compact.join(' ')
25
+ add_item(title: title, url: index, icon: icon, suggested: suggested)
26
+ end
27
+ end
28
+ end
29
+
30
+ def add_rails_admin_items(icon: default_admin_item_icon, prefix: nil, suffix: nil, suggested: false)
31
+ admin_urls = RailsAdmin::Engine.routes.url_helpers
32
+
33
+ RailsAdmin::Config.models.select(&:visible?).each do |model|
34
+ next unless index = admin_urls.index_path(model.abstract_model.to_param)
35
+ next unless label = model.label_plural # as used by rails_admin in sidebar
36
+
37
+ title = [prefix, label, suffix].compact.join(' ')
38
+ add_item(title: title, url: index, icon: icon, suggested: suggested)
39
+ end
40
+ end
41
+
42
+ def default_admin_item_icon
43
+ :document
44
+ end
45
+ end
@@ -1,13 +1,13 @@
1
1
  class RailsOmnibar
2
2
  def add_item(item)
3
+ check_const_and_clear_cache
3
4
  items << RailsOmnibar.cast_to_item(item)
4
- clear_cache
5
- self.class
5
+ self
6
6
  end
7
7
 
8
8
  def add_items(*args)
9
9
  args.each { |arg| add_item(arg) }
10
- self.class
10
+ self
11
11
  end
12
12
 
13
13
  def self.cast_to_item(arg)
@@ -8,6 +8,10 @@ class RailsOmnibar
8
8
  HTML
9
9
  end
10
10
 
11
+ def html_url
12
+ urls.html_path(omnibar_class: omnibar_class)
13
+ end
14
+
11
15
  require 'js_regex'
12
16
 
13
17
  def as_json(*)
@@ -19,7 +23,7 @@ class RailsOmnibar
19
23
  maxResults: max_results,
20
24
  modal: modal?,
21
25
  placeholder: placeholder,
22
- queryPath: urls.query_path(omnibar_class: self.class),
26
+ queryPath: urls.query_path(omnibar_class: omnibar_class),
23
27
  }
24
28
  end
25
29
 
@@ -29,7 +33,8 @@ class RailsOmnibar
29
33
 
30
34
  private
31
35
 
32
- def clear_cache
36
+ def check_const_and_clear_cache
37
+ omnibar_class # trigger constant assignment check
33
38
  @cached_html = nil
34
39
  end
35
40
  end
@@ -1,3 +1,3 @@
1
1
  class RailsOmnibar
2
- VERSION = '1.3.2'
2
+ VERSION = '1.5.0'
3
3
  end
data/lib/rails_omnibar.rb CHANGED
@@ -3,15 +3,11 @@ require 'rails'
3
3
  Dir[File.join(__dir__, 'rails_omnibar', '**', '*.rb')].sort.each { |f| require(f) }
4
4
 
5
5
  class RailsOmnibar
6
- def self.configure(&block)
7
- @singleton = block_given? ? new.tap(&block) : new
8
- end
9
- private_class_method :new
10
-
11
6
  singleton_class.delegate(*public_instance_methods(false), to: :singleton)
12
7
 
13
- private_class_method\
14
8
  def self.singleton
15
- @singleton || raise("Please `.configure' your omnibar first")
9
+ @singleton ||= new
16
10
  end
11
+ private_class_method :singleton
12
+ private_class_method :new
17
13
  end
@@ -20,12 +20,4 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.add_dependency 'js_regex'
22
22
  spec.add_dependency 'rails', ['>= 6.0', '< 8.0']
23
-
24
- spec.add_development_dependency 'capybara', '~> 3.0'
25
- spec.add_development_dependency 'factory_bot_rails', '~> 6.0'
26
- spec.add_development_dependency 'puma', '~> 5.0'
27
- spec.add_development_dependency 'rake', '~> 13.0'
28
- spec.add_development_dependency 'rspec-rails', '~> 5.0'
29
- spec.add_development_dependency 'sqlite3', '>= 1.3.6'
30
- spec.add_development_dependency 'webdrivers', '~> 5.0'
31
23
  end
@@ -1,7 +1,9 @@
1
1
  require 'rails_helper'
2
2
 
3
+ TestBar = Class.new(RailsOmnibar)
4
+
3
5
  describe RailsOmnibar do
4
- subject { Class.new(RailsOmnibar).configure }
6
+ subject { TestBar }
5
7
 
6
8
  it 'has a configurable max_results' do
7
9
  expect(subject.max_results).to eq 10
@@ -50,4 +52,11 @@ describe RailsOmnibar do
50
52
  subject.placeholder = false
51
53
  expect(subject.placeholder).to eq nil
52
54
  end
55
+
56
+ it 'raises when trying to configure or render from an anonymous class' do
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
+ expect { klass.render }.to raise_error(/constant/)
61
+ end
53
62
  end
data/spec/rails_helper.rb CHANGED
@@ -15,8 +15,4 @@ RSpec.configure do |config|
15
15
  config.before(:each, type: :system) do
16
16
  driven_by :selenium_chrome_headless
17
17
  end
18
- config.after(:each, type: :system) do
19
- logs = page.driver.browser.logs.get(:browser)
20
- logs.empty? || raise("JS errors: #{logs.map(&:message)}")
21
- end
22
18
  end
@@ -4,5 +4,7 @@ FactoryBot.define do
4
4
  factory :user do
5
5
  first_name { 'John' }
6
6
  last_name { 'Doe' }
7
+ sequence(:email) { |n| "person#{n}@example.com" }
8
+ password { '12345678' }
7
9
  end
8
10
  end
@@ -12,12 +12,14 @@ describe RailsOmnibar do
12
12
  # test visibility toggling with hotkey
13
13
  expect(page).not_to have_selector 'input'
14
14
 
15
- send_keys([:control, 'm']) # custom hotkey, c.f. app_template.rb
15
+ send_keys([:control, 'm']) # custom hotkey, c.f. my_omnibar_template.rb
16
16
  expect(page).to have_selector 'input'
17
17
 
18
+ sleep 0.1
18
19
  send_keys([:control, 'm'])
19
20
  expect(page).not_to have_selector 'input'
20
21
 
22
+ sleep 0.1
21
23
  send_keys([:meta, 'm']) # meta modifier (⌘) should also be supported
22
24
  expect(page).to have_selector 'input'
23
25
 
@@ -65,7 +67,7 @@ describe RailsOmnibar do
65
67
 
66
68
  # test navigation for item with #url
67
69
  submit
68
- expect(page.current_url).to end_with '/users/2'
70
+ expect(page).to have_current_path '/users/2'
69
71
  send_keys([:control, 'm']) # make omnibar visible again
70
72
 
71
73
  # test generic search and multi-itemization
@@ -78,6 +80,43 @@ describe RailsOmnibar do
78
80
  # test custom command
79
81
  type('count users')
80
82
  expect(page).to have_content '2'
83
+
84
+ # test auth with devise
85
+ auth = ->(controller) { controller.user_signed_in? || true }
86
+ MyOmnibar.auth = auth
87
+ expect(auth).to receive(:call).at_least(:once).and_call_original
88
+ sleep 0.1 # not sure why this is needed ...
89
+ type('count users')
90
+ expect(page).to have_content '2'
91
+
92
+ # test activeadmin integration
93
+ # stub sprockets so we don't need sassc etc.
94
+ allow_any_instance_of(Sprockets::Rails::Helper)
95
+ .to receive(:compute_asset_path)
96
+ .and_return('')
97
+
98
+ submit('Admin: Useroos')
99
+ expect(page).to have_current_path('/admin/users')
100
+ end
101
+
102
+ it 'can have more than one omnibar' do
103
+ visit main_app.root_path
104
+ expect(page).to have_selector '#mount-rails-omnibar' # sanity check
105
+
106
+ expect(page).not_to have_selector 'input'
107
+ send_keys([:meta, 'a']) # custom hotkey, c.f. other_omnibar_template.rb
108
+ expect(page).to have_selector 'input'
109
+
110
+ # test suggested content from other omnibar is not there
111
+ expect(page).not_to have_content 'important URL'
112
+
113
+ # test fuzzy search for this bars' static items
114
+ type('dis')
115
+ expect(page).to have_content 'disney'
116
+
117
+ # test command from other omnibar does not run
118
+ type('g foobar')
119
+ expect(page).not_to have_content 'fake_result_1'
81
120
  end
82
121
 
83
122
  def type(str)
@@ -0,0 +1,40 @@
1
+ # template for dummy rails app used in specs
2
+
3
+ gem 'rails_omnibar', path: __dir__ + '/../../'
4
+ gem 'csv'
5
+ gem 'devise'
6
+ gem 'activeadmin'
7
+
8
+ insert_into_file 'config/application.rb', <<~RUBY, after: /action_mailer.*\n/
9
+ require 'action_mailer/railtie' # needed to make devise work
10
+ require 'devise'
11
+ require 'sprockets/railtie' # needed to make activeadmin work
12
+ RUBY
13
+
14
+ # https://github.com/activeadmin/activeadmin/pull/7235#issuecomment-1000823435
15
+ insert_into_file 'config/environments/test.rb', 'false # ', after: /config.eager_load *=/
16
+
17
+ generate 'model', 'User first_name:string last_name:string admin:boolean --no-test-framework'
18
+ generate 'devise:install'
19
+ generate 'devise User'
20
+ generate 'active_admin:install --skip-users'
21
+
22
+ insert_into_file 'config/initializers/active_admin.rb', <<-RUBY, after: "|config|\n"
23
+ config.load_paths = [File.join(Rails.root, 'app/lib/aa')]
24
+ RUBY
25
+
26
+ file 'app/lib/my_omnibar.rb', File.read(__dir__ + '/my_omnibar_template.rb')
27
+ file 'app/lib/other_omnibar.rb', File.read(__dir__ + '/other_omnibar_template.rb')
28
+ file 'app/lib/aa/users.rb', File.read(__dir__ + '/user_resource_template.rb')
29
+
30
+ inject_into_class 'app/controllers/application_controller.rb', 'ApplicationController', <<-RUBY
31
+ def index
32
+ render html: (MyOmnibar.render + OtherOmnibar.render)
33
+ end
34
+ RUBY
35
+
36
+ route 'mount RailsOmnibar::Engine => "/rails_omnibar"'
37
+ route 'root "application#index"'
38
+ route 'get "users/(*path)" => "application#index"'
39
+
40
+ rake 'db:migrate db:test:prepare'
@@ -4,6 +4,8 @@ MyOmnibar = RailsOmnibar.configure do |c|
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
6
 
7
+ c.add_webadmin_items(prefix: 'Admin:')
8
+
7
9
  c.add_record_search(pattern: /^u(\d+)/, model: User, example: 'u123')
8
10
 
9
11
  c.add_record_search(pattern: /^u (.+)/, model: User, columns: %i[first_name last_name],
@@ -34,7 +36,7 @@ MyOmnibar = RailsOmnibar.configure do |c|
34
36
  description: 'Get count of a DB table',
35
37
  pattern: /COUNT (.+)/i,
36
38
  example: 'COUNT users',
37
- resolver: ->(value, _omnibar) do
39
+ resolver: ->(value) do
38
40
  { title: value.classify.constantize.count.to_s }
39
41
  rescue => e
40
42
  { title: e.message }
@@ -0,0 +1,11 @@
1
+ OtherOmnibar = Class.new(RailsOmnibar)
2
+ OtherOmnibar.configure do |c|
3
+ c.modal = true
4
+
5
+ c.add_item(title: 'disney', url: 'https://www.disney.com', suggested: true)
6
+
7
+ # Use a hotkey that is the same in most keyboard layouts to work around
8
+ # https://bugs.chromium.org/p/chromedriver/issues/detail?id=553
9
+ # (This is only relevant for testing with chromedriver.)
10
+ c.hotkey = 'a'
11
+ end
@@ -0,0 +1,5 @@
1
+ ActiveAdmin.register User do
2
+ menu label: 'Useroos', parent: 'Stuff'
3
+ actions :all
4
+ config.filters = false
5
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_omnibar
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.2
4
+ version: 1.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Janosch Müller
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-01-27 00:00:00.000000000 Z
11
+ date: 2024-01-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: js_regex
@@ -44,104 +44,6 @@ dependencies:
44
44
  - - "<"
45
45
  - !ruby/object:Gem::Version
46
46
  version: '8.0'
47
- - !ruby/object:Gem::Dependency
48
- name: capybara
49
- requirement: !ruby/object:Gem::Requirement
50
- requirements:
51
- - - "~>"
52
- - !ruby/object:Gem::Version
53
- version: '3.0'
54
- type: :development
55
- prerelease: false
56
- version_requirements: !ruby/object:Gem::Requirement
57
- requirements:
58
- - - "~>"
59
- - !ruby/object:Gem::Version
60
- version: '3.0'
61
- - !ruby/object:Gem::Dependency
62
- name: factory_bot_rails
63
- requirement: !ruby/object:Gem::Requirement
64
- requirements:
65
- - - "~>"
66
- - !ruby/object:Gem::Version
67
- version: '6.0'
68
- type: :development
69
- prerelease: false
70
- version_requirements: !ruby/object:Gem::Requirement
71
- requirements:
72
- - - "~>"
73
- - !ruby/object:Gem::Version
74
- version: '6.0'
75
- - !ruby/object:Gem::Dependency
76
- name: puma
77
- requirement: !ruby/object:Gem::Requirement
78
- requirements:
79
- - - "~>"
80
- - !ruby/object:Gem::Version
81
- version: '5.0'
82
- type: :development
83
- prerelease: false
84
- version_requirements: !ruby/object:Gem::Requirement
85
- requirements:
86
- - - "~>"
87
- - !ruby/object:Gem::Version
88
- version: '5.0'
89
- - !ruby/object:Gem::Dependency
90
- name: rake
91
- requirement: !ruby/object:Gem::Requirement
92
- requirements:
93
- - - "~>"
94
- - !ruby/object:Gem::Version
95
- version: '13.0'
96
- type: :development
97
- prerelease: false
98
- version_requirements: !ruby/object:Gem::Requirement
99
- requirements:
100
- - - "~>"
101
- - !ruby/object:Gem::Version
102
- version: '13.0'
103
- - !ruby/object:Gem::Dependency
104
- name: rspec-rails
105
- requirement: !ruby/object:Gem::Requirement
106
- requirements:
107
- - - "~>"
108
- - !ruby/object:Gem::Version
109
- version: '5.0'
110
- type: :development
111
- prerelease: false
112
- version_requirements: !ruby/object:Gem::Requirement
113
- requirements:
114
- - - "~>"
115
- - !ruby/object:Gem::Version
116
- version: '5.0'
117
- - !ruby/object:Gem::Dependency
118
- name: sqlite3
119
- requirement: !ruby/object:Gem::Requirement
120
- requirements:
121
- - - ">="
122
- - !ruby/object:Gem::Version
123
- version: 1.3.6
124
- type: :development
125
- prerelease: false
126
- version_requirements: !ruby/object:Gem::Requirement
127
- requirements:
128
- - - ">="
129
- - !ruby/object:Gem::Version
130
- version: 1.3.6
131
- - !ruby/object:Gem::Dependency
132
- name: webdrivers
133
- requirement: !ruby/object:Gem::Requirement
134
- requirements:
135
- - - "~>"
136
- - !ruby/object:Gem::Version
137
- version: '5.0'
138
- type: :development
139
- prerelease: false
140
- version_requirements: !ruby/object:Gem::Requirement
141
- requirements:
142
- - - "~>"
143
- - !ruby/object:Gem::Version
144
- version: '5.0'
145
47
  description: Omnibar for Rails
146
48
  email:
147
49
  executables: []
@@ -156,6 +58,7 @@ files:
156
58
  - README.md
157
59
  - Rakefile
158
60
  - app/controllers/rails_omnibar/base_controller.rb
61
+ - app/controllers/rails_omnibar/html_controller.rb
159
62
  - app/controllers/rails_omnibar/js_controller.rb
160
63
  - app/controllers/rails_omnibar/queries_controller.rb
161
64
  - bin/console
@@ -180,19 +83,22 @@ files:
180
83
  - lib/rails_omnibar/engine.rb
181
84
  - lib/rails_omnibar/item/base.rb
182
85
  - lib/rails_omnibar/item/help.rb
86
+ - lib/rails_omnibar/item/webadmin.rb
183
87
  - lib/rails_omnibar/items.rb
184
88
  - lib/rails_omnibar/rendering.rb
185
89
  - lib/rails_omnibar/version.rb
186
90
  - package.json
187
91
  - rails_omnibar.gemspec
188
- - spec/app_template.rb
189
92
  - spec/lib/rails_omnibar/config_spec.rb
190
93
  - spec/lib/rails_omnibar/item/base_spec.rb
191
94
  - spec/lib/rails_omnibar/version_spec.rb
192
- - spec/my_omnibar_template.rb
193
95
  - spec/rails_helper.rb
194
96
  - spec/support/factories.rb
195
97
  - spec/system/rails_omnibar_spec.rb
98
+ - spec/templates/app_template.rb
99
+ - spec/templates/my_omnibar_template.rb
100
+ - spec/templates/other_omnibar_template.rb
101
+ - spec/templates/user_resource_template.rb
196
102
  - tsconfig.json
197
103
  - webpack.config.js
198
104
  homepage: https://github.com/jaynetics/rails_omnibar
@@ -214,16 +120,18 @@ required_rubygems_version: !ruby/object:Gem::Requirement
214
120
  - !ruby/object:Gem::Version
215
121
  version: '0'
216
122
  requirements: []
217
- rubygems_version: 3.4.1
123
+ rubygems_version: 3.5.0.dev
218
124
  signing_key:
219
125
  specification_version: 4
220
126
  summary: Omnibar for Rails
221
127
  test_files:
222
- - spec/app_template.rb
223
128
  - spec/lib/rails_omnibar/config_spec.rb
224
129
  - spec/lib/rails_omnibar/item/base_spec.rb
225
130
  - spec/lib/rails_omnibar/version_spec.rb
226
- - spec/my_omnibar_template.rb
227
131
  - spec/rails_helper.rb
228
132
  - spec/support/factories.rb
229
133
  - spec/system/rails_omnibar_spec.rb
134
+ - spec/templates/app_template.rb
135
+ - spec/templates/my_omnibar_template.rb
136
+ - spec/templates/other_omnibar_template.rb
137
+ - spec/templates/user_resource_template.rb
data/spec/app_template.rb DELETED
@@ -1,17 +0,0 @@
1
- gem 'rails_omnibar', path: __dir__ + '/../'
2
-
3
- generate 'model', 'User first_name:string last_name:string admin:boolean --no-test-framework'
4
-
5
- file 'app/lib/my_omnibar.rb', File.read(__dir__ + '/my_omnibar_template.rb')
6
-
7
- inject_into_class 'app/controllers/application_controller.rb', 'ApplicationController', <<-RUBY
8
- def index
9
- render html: MyOmnibar.render
10
- end
11
- RUBY
12
-
13
- route 'mount RailsOmnibar::Engine => "/rails_omnibar"'
14
- route 'root "application#index"'
15
- route 'get "users/(*path)" => "application#index"'
16
-
17
- rake 'db:migrate db:test:prepare'