Skip to content
/ fuzz Public

Interactively select Ruby objects with rofi, dmenu, and friends!

License

Notifications You must be signed in to change notification settings

hrs/fuzz

Repository files navigation

Fuzz: A Ruby wrapper around interactive filtering tools

Build Status Maintainability Coverage Status License: GPL v3

I often write scripts in which a user needs to choose one of a number of possibilities: maybe they need to select a directory, or a file, or an action, or just an arbitrary object.

rofi and dmenu are really cool tools for that! They provide a visual way for a user to use fuzzy searching to choose among a selection of strings. pick, peco, and selecta provide similar services on the command line.

Unfortunately, though, these tools do just choose between strings. But my scripts would often be a lot simpler if the user were able to select arbitrary Ruby objects. Fuzz manages the translation between lists of strings that can be selected through these visual pickers and the associated collection of Ruby objects, which makes it a bit easier to write interactive and OOP-friendly Ruby scripts.

For example

There are more interesting examples in the wiki, but here's a simple one to get started.

I'm a fan of the excellent RubyTapas screencasts, and I'd like a script to let me interactively search through their titles to pick one to play. Here's a way I could do that:

require "fuzz"

# Instantiate some File objects:
episodes = Dir.glob("~/ruby_tapas_episodes/*")

# Have the user pick one with rofi:
choice = Fuzz::Selector.new(episodes).pick

# Play the selected file with VLC:
system("vlc \"#{ choice.path }\"")

The call to #pick will call #to_s on every episode, display the results through rofi (the default picker), get the user's choice, and use that to return the corresponding object.

Caching selections

If you run your script frequently you may find that you often make the same selections. It's convenient to have those selections appear near the top of the list.

Fuzz can maintain a script-specific record of your previous selections and order your options from most to least popular. Just include an optional :cache argument when creating a Fuzz::Selector:

Fuzz::Selector.new(
  some_objects,
  cache: Fuzz::Cache.new("~/.cache/fuzz/my_script_cache"),
)

You'll want to use a different file for each script. Using the same cache file for different scripts will probably yield weird results.

I think ~/.cache/fuzz/ is a nice directory for your personal script caches, and it complies with the XDG Base Directory Specification, but you can keep 'em wherever you'd like.

Supplying a default value

If the user enters a string that doesn't correspond to an object, Fuzz::Selector#pick will normally return nil.

However, you can manually specify a default return value with the default: option:

Fuzz::Selector.new(
  [1, 2, 3],
  default: 42,
).pick # => returns 42 if the user picks a value other than 1, 2, or 3

Choosing a different picker

Fuzz ships with support for numerous fuzzy-matching tools, which it calls "pickers." The Fuzz::Selector constructor takes an optional picker: argument to pick a picker.

Executable Creating an instance
dmenu Fuzz::DmenuPicker.new
peco Fuzz::PecoPicker.new
pick Fuzz::PickPicker.new
rofi Fuzz::RofiPicker.new (default)
selecta Fuzz::SelectaPicker.new

For example, to use dmenu as your picker:

Fuzz::Selector.new(
  some_objects,
  picker: Fuzz::DmenuPicker.new,
)

If the executable associated with your chosen picker can't be found (perhaps it's not installed, or not in your $PATH), Fuzz::Selector#pick will raise a Fuzz::MissingExecutableError.

Extending fuzz with new pickers

It's possible to extend fuzz to use a picker of your choice. The object supplied to picker: must implement a #pick method, which should take an array of strings and return a string.

Here's a simple example with a silly picker that always chooses the first option:

def PickFirst
  def pick(choices)
    choices.first
  end
end

selector = Fuzz::Selector.new(
  [1, 2, 3, 4, 5],
  picker: PickFirst.new,
)

selector.pick # => 1

Your custom pickers will probably be more interactive than this! =)

Installation

Add this line to your application's Gemfile:

gem "fuzz"

And then execute:

$ bundle

Or install it yourself as:

$ gem install fuzz

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/hrs/fuzz. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.

Code of Conduct

Everyone interacting in the Fuzz project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.

About

Interactively select Ruby objects with rofi, dmenu, and friends!

Topics

Resources

License

Code of conduct

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published