rails_omnibar 1.3.2 → 1.5.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.
@@ -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'