Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

rails_omnibar

Package Overview
Dependencies
Maintainers
1
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

rails_omnibar

  • 1.9.0
  • Rubygems
  • Socket score

Version published
Maintainers
1
Created
Source

Gem Version Build Status

RailsOmnibar

Add an Omnibar to Ruby on Rails.

Screenshot

Installation

Add rails_omnibar to your bundle and add the following line to your config/routes.rb:

mount RailsOmnibar::Engine => '/rails_omnibar'

You can pick any path. See Authorization for limiting access to the engine.

Configuration

Basic Usage

# app/lib/omnibar.rb

Omnibar = RailsOmnibar.configure do |c|
  c.max_results = 5 # default is 10

  # Render as a modal that is hidden by default and toggled with a hotkey.
  # The default value for this setting is false - i.e. render inline.
  c.modal = true # default is false

  # Use a custom hotkey to focus the omnibar (or toggle it if it is a modal).
  # CTRL+<hotkey> and CMD+<hotkey> will both work.
  c.hotkey = 't' # default is 'k'

  # Add static items that can be found by fuzzy typing.
  # "suggested" items will be displayed before user starts typing.
  c.add_item(title: 'Important link', url: 'https://www.disney.com', suggested: true)
  c.add_item(title: 'Important info', modal_html: '<b>You rock</b>')

  # Add all backoffice URLs as searchable items
  Rails.application.routes.routes.each do |route|
    next unless route.defaults[:action] == 'index'
    next unless name = route.name[/^backoffice_(.+)/, 1]

    # items can have icons
    # arrows, cloud, cog, dev, document, home, question, search, sparkle, user, wallet, x
    c.add_item(title: name.humanize, url: route.format({}), icon: :cog)
  end

  # Add commands

  # This command will allow searching users by id by typing e.g. 'u123'.
  # The capture group is used to extract the value for the DB query.
  c.add_record_search(
    pattern: /^u(\d+)/,
    example: 'u123',
    model:   User,
  )

  # Search records by column(s) other than id (via LIKE query by default)
  c.add_record_search(
    pattern: /^u (.+)/,
    example: 'u Joe',
    model:   User,
    columns: %i[first_name last_name],
  )

  # The special #add_help method must be called last if you want a help entry.
  # It shows a modal with descriptions and examples of the commands, e.g.:
  #
  #   * Search User by id
  #     Example: `u123`
  #   * Search User by first_name OR last_name
  #     Example: `u Joe`
  #   * Search User by id
  #     Example: `U123`
  #   * Search Google
  #     Example: `g kittens`
  #   * Get count of a DB table
  #     Example: `COUNT users`
  #
  c.add_help
end

Render it somewhere. E.g. app/views/layouts/application.html.erb:

<%= Omnibar.render(self) %>

If you have a fully decoupled frontend, use Omnibar.html_url instead, fetch the omnibar HTML from there, and inject it.

Authorization

You can limit access to commands (e.g. search commands) and items.

Option 1: globally limit engine access
authenticate :user, ->(user){ user.superadmin? } do
  mount RailsOmnibar::Engine => '/rails_omnibar'
end
Option 2: use RailsOmnibar::auth=

This is useful for fine-grained authorization, e.g. if there is more than one omnibar or multiple permission levels.

# the auth proc is executed in the controller context by default,
# but can also take the controller and omnibar as arguments
MyOmnibar.auth = ->{ user_signed_in? }
MyOmnibar.auth = ->(controller, omnibar:) do
  controller.user_signed_in? && omnibar.is_a?(NormalUserOmnibar)
end
Option 3: Item-level conditionality

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.

MyOmnibar.add_item(
  title: 'Admin link',
  url:   admin_url,
  if:    ->{ current_user.admin? }
)

For this to work, the controller context must be given to the omnibar when rendering (e.g. by passing self in a view):

<%= Omnibar.render(self) %>

Using multiple different omnibars

BaseOmnibar = Class.new(RailsOmnibar).configure do |c|
  c.add_item(
    title: 'Log in',
    url:    Rails.application.routes.url_helpers.log_in_url
  )
end

UserOmnibar = Class.new(RailsOmnibar).configure do |c|
  c.auth = ->{ user_signed_in? }
  c.add_item(
    title: 'Log out',
    url:    Rails.application.routes.url_helpers.log_out_url
  )
end

Then, in some layout:

<%= (user_signed_in? ? UserOmnibar : BaseOmnibar).render(self) %>

Omnibars can also inherit commands, configuration, and items from existing omnibars:

SuperuserOmnibar = Class.new(UserOmnibar).configure do |c|
  # add more items that only superusers should get
end

Other options and usage patterns

Adding multiple items at once
MyOmnibar.add_items(
  *MyRecord.all.map { |rec| { title: rec.title, url: url_for(rec) } }
)
Adding all ActiveAdmin or RailsAdmin index routes as searchable items

Simply call ::add_webadmin_items and use the modal mode.

MyOmnibar.configure do |c|
  c.add_webadmin_items
  c.modal = true
end
To render in ActiveAdmin
module AddOmnibar
  def build_page(...)
    within(super) { text_node(MyOmnibar.render(self)) }
  end
end
ActiveAdmin::Views::Pages::Base.prepend(AddOmnibar)
To render in RailsAdmin

Add MyOmnibar.render(self) to app/views/layouts/rails_admin/application.*.

Adding all index routes as searchable items
Rails.application.routes.routes.each do |route|
  next unless route.defaults.values_at(:action, :format) == ['index', nil]
  MyOmnibar.add_item(title: route.name.humanize, url: route.format({}))
end
Custom record lookup and rendering
MyOmnibar.add_record_search(
  pattern:  /^U(\d+)/,
  example:  'U123',
  model:    User,
  finder:   ->(id) { User.find_by(admin: true, id: id) },
  itemizer: ->(user) do
    { title: "Admin #{user.name}", url: admin_url(user), icon: :user }
  end
)
Custom search, plus mapping to multiple results
MyOmnibar.add_search(
  description: 'Google',
  pattern:     /^g (.+)/,
  example:     'g kittens',
  # omnibar: and controller: keyword args are provided to command procs
  finder:      ->(value, omnibar:) do
    Google.search(value, limit: omnibar.max_results)
  end,
  itemizer:    ->(res) do
    [
      { title: res.title, url: res.url },
      { title: "#{res.title} @archive", url: "web.archive.org/web/#{res.url}" }
    ]
  end,
)
Completely custom command
MyOmnibar.add_command(
  description: 'Get count of a DB table',
  pattern:     /COUNT (.+)/i,
  example:     'COUNT users',
  resolver:    ->(value, controller:) do
    if controller.current_user.client?
      { title: (value.classify.constantize.count * 2).to_s }
    else
      { title: value.classify.constantize.count.to_s }
    end
  rescue => e
    { title: e.message }
  end,
)

Development

Setup

  • Clone the repository
  • Go into the directory
  • Run bin/setup to install Ruby and JS dependencies

License

This program is provided under an MIT open source license, read the LICENSE.txt file for details.

FAQs

Package last updated on 12 Nov 2024

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc