fuzz 0.1.1 → 0.1.2

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: 0f44c39816f92d7768e60a4f294c88e1dc56f1f9bda7f2c6dea0324721de1cb9
4
- data.tar.gz: a3f71c073214cd1b1762c81651acc15e44bc8640cfa025a2d45d0907017b60e1
3
+ metadata.gz: f9d42afd0f155728e3950089a89cf53ea30567cd151f40726a3e554e0a734daa
4
+ data.tar.gz: 0f384622fe5d6c66e6ac7c09b2221a972a4c7155ed51513c2fde9fab9266cf50
5
5
  SHA512:
6
- metadata.gz: 4b2977173802a64185ec315cced434cb0f47c5bb6a3ec7d1ad1f1309aca215ed4fa8c0d6f222270b14535fa022de83e8b699cf4a572d95e966fe48f9c5f69f23
7
- data.tar.gz: 7d2854f73da095b8c140424a190b39c3f2401132fcacb7bd7373c9fa98c4544cc296ac4761b5df14df866141f4797ae03d9a7a73a14816434ac4aab7b4522bed
6
+ metadata.gz: 2e25ce9074901620fdeeb50a4aaaa90ec0b39d7c7ba0af27797d186e6fe9f63f8a52772ff81a59d13b9c87bfd8cc16912308ae1888d27a8936d354e1ae610039
7
+ data.tar.gz: 23c5622897c83d6dbb517adaa6dae68004b29761403b9339a7633cffdee55ff7addb2af0ee31099f7e7c322f7a807fa9e563a05be3c0f3536311b1c3e9f77bd9
data/Gemfile.lock CHANGED
@@ -1,12 +1,20 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- fuzz (0.1.0)
4
+ fuzz (0.1.2)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
+ coveralls (0.8.21)
10
+ json (>= 1.8, < 3)
11
+ simplecov (~> 0.14.1)
12
+ term-ansicolor (~> 1.3)
13
+ thor (~> 0.19.4)
14
+ tins (~> 1.6)
9
15
  diff-lcs (1.3)
16
+ docile (1.1.5)
17
+ json (2.1.0)
10
18
  rake (10.5.0)
11
19
  rspec (3.7.0)
12
20
  rspec-core (~> 3.7.0)
@@ -21,12 +29,22 @@ GEM
21
29
  diff-lcs (>= 1.2.0, < 2.0)
22
30
  rspec-support (~> 3.7.0)
23
31
  rspec-support (3.7.1)
32
+ simplecov (0.14.1)
33
+ docile (~> 1.1.0)
34
+ json (>= 1.8, < 3)
35
+ simplecov-html (~> 0.10.0)
36
+ simplecov-html (0.10.2)
37
+ term-ansicolor (1.6.0)
38
+ tins (~> 1.0)
39
+ thor (0.19.4)
40
+ tins (1.16.3)
24
41
 
25
42
  PLATFORMS
26
43
  ruby
27
44
 
28
45
  DEPENDENCIES
29
46
  bundler (~> 1.16)
47
+ coveralls
30
48
  fuzz!
31
49
  rake (~> 10.0)
32
50
  rspec (~> 3.0)
data/README.md CHANGED
@@ -1,19 +1,28 @@
1
- ## Fuzz: interactively select Ruby objects with `rofi`
1
+ ## Fuzz: interactively select Ruby objects with `rofi`, `dmenu`, or `pick`
2
+
3
+ [![Build Status](https://travis-ci.org/hrs/fuzz.svg?branch=master)](https://travis-ci.org/hrs/fuzz)
4
+ [![Maintainability](https://api.codeclimate.com/v1/badges/326b820a889742177ec2/maintainability)](https://codeclimate.com/github/hrs/fuzz/maintainability)
5
+ [![Coverage Status](https://coveralls.io/repos/github/hrs/fuzz/badge.svg?branch=master)](https://coveralls.io/github/hrs/fuzz?branch=master)
6
+ [![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](http://www.gnu.org/licenses/gpl-3.0)
2
7
 
3
8
  I often write scripts in which a user needs to choose one of a number of
4
9
  possibilities: maybe they need to select a directory, or a file, or an action,
5
10
  or just an arbitrary object.
6
11
 
7
- [rofi][] is a really cool tool for that! It provides a visual way for a user to
8
- use fuzzy searching to choose among a selection of strings.
12
+ [rofi][] and [dmenu][] are really cool tools for that! They provide a visual way
13
+ for a user to use fuzzy searching to choose among a selection of strings.
14
+ [pick][] provides a similar service on the command line with ncurses.
9
15
 
10
- Unfortunately, though, it does *just* choose between strings. But my scripts
11
- would often be a lot simpler if the user were able to select arbitrary Ruby
12
- objects. Fuzz manages the translation between lists of strings that can be
13
- selected through `rofi` and the associated collection of Ruby objects, and thus
14
- makes it a bit easier to write interactive and OOP-friendly Ruby scripts.
16
+ Unfortunately, though, these tools do *just* choose between strings. But my
17
+ scripts would often be a lot simpler if the user were able to select arbitrary
18
+ Ruby objects. Fuzz manages the translation between lists of strings that can be
19
+ selected through these visual pickers and the associated collection of Ruby
20
+ objects, which makes it a bit easier to write interactive and OOP-friendly Ruby
21
+ scripts.
15
22
 
16
23
  [rofi]: https://github.com/DaveDavenport/rofi
24
+ [dmenu]: https://tools.suckless.org/dmenu
25
+ [pick]: https://github.com/calleerlandsson/pick
17
26
 
18
27
  ### For example
19
28
 
@@ -35,14 +44,14 @@ system("vlc \"#{ choice.path }\"")
35
44
  ```
36
45
 
37
46
  The call to `#pick` will call `#to_s` on every episode, display the results
38
- through `rofi`, get the user's choice, and use that to return the corresponding
39
- object.
47
+ through `rofi` (the default picker), get the user's choice, and use that to
48
+ return the corresponding object.
40
49
 
41
50
  [RubyTapas screencasts]: https://www.rubytapas.com/
42
51
 
43
52
  ### Caching selections
44
53
 
45
- If you run your script frequently, you may find that you often make the same
54
+ If you run your script frequently you may find that you often make the same
46
55
  selections. It's convenient to have those selections appear near the top of the
47
56
  list.
48
57
 
@@ -66,11 +75,62 @@ and it complies with the [XDG Base Directory Specification][], but you can keep
66
75
 
67
76
  [XDG Base Directory Specification]: https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
68
77
 
69
- ### Extending `fuzz` beyond `rofi`
78
+ ### Supplying a default value
79
+
80
+ If the user enters a string that doesn't correspond to an object,
81
+ `Fuzz::Selector#pick` will normally return `nil`.
82
+
83
+ However, you can manually specify a default return value with the `default:`
84
+ option:
85
+
86
+ ```ruby
87
+ Fuzz::Selector.new(
88
+ [1, 2, 3],
89
+ default: 42,
90
+ ).pick # => returns 42 if the user picks a value other than 1, 2, or 3
91
+ ```
92
+
93
+ ### Choosing a different picker
70
94
 
71
- It's possible to use `fuzz` without `rofi`. The `Fuzz::Selector` constructor
72
- takes an optional `:picker` argument. The supplied object must implement a
73
- `#pick` method, which should take an array of strings and return a string.
95
+ Fuzz ships with support for `rofi` and `dmenu`. The `Fuzz::Selector` constructor
96
+ takes an optional `picker:` argument to pick a picker.
97
+
98
+ For example, to use `dmenu` as your picker:
99
+
100
+ ```ruby
101
+ Fuzz::Selector.new(
102
+ some_objects,
103
+ picker: Fuzz::DmenuPicker.new,
104
+ )
105
+ ```
106
+
107
+ Or to search in a terminal with the `pick` tool:
108
+
109
+ ```ruby
110
+
111
+ Fuzz::Selector.new(
112
+ some_objects,
113
+ picker: Fuzz::PickPicker.new,
114
+ )
115
+ ```
116
+
117
+ The `rofi` picker is the default, but you can also explicitly specify it:
118
+
119
+ ```ruby
120
+ Fuzz::Selector.new(
121
+ some_objects,
122
+ picker: Fuzz::RofiPicker.new,
123
+ )
124
+ ```
125
+
126
+ If your chosen picker can't be found (perhaps it's not installed, or not in your
127
+ `$PATH`), `Fuzz::Selector#pick` will raise a `Fuzz::MissingExecutableError`.
128
+
129
+ ### Extending `fuzz` with new pickers
130
+
131
+ It's possible to extend `fuzz` to use a picker of your choice. The object
132
+ supplied to `picker:` must implement a `#pick` method, which should take an
133
+ array of strings and return a string.
74
134
 
75
135
  Here's a simple example with a silly picker that always chooses the first
76
136
  option:
@@ -90,11 +150,7 @@ selector = Fuzz::Selector.new(
90
150
  selector.pick # => 1
91
151
  ```
92
152
 
93
- Pickers will usually be more interactive than this, I hope! You might shell out
94
- to [dmenu][], [pick][], or whatever else you'd like.
95
-
96
- [dmenu]: https://wiki.archlinux.org/index.php/Dmenu
97
- [pick]: https://github.com/calleerlandsson/pick
153
+ Your custom pickers will probably be more interactive than this! =)
98
154
 
99
155
  ## Installation
100
156
 
data/fuzz.gemspec CHANGED
@@ -7,7 +7,7 @@ Gem::Specification.new do |spec|
7
7
  spec.version = Fuzz::VERSION
8
8
  spec.authors = ["Harry Schwartz"]
9
9
  spec.email = ["hello@harryrschwartz.com"]
10
- spec.summary = "Wrap command-line tools to graphically select from a list of Ruby objects!"
10
+ spec.summary = "Use command-line tools to choose from a list of Ruby objects!"
11
11
  spec.homepage = "https://github.com/hrs/fuzz"
12
12
  spec.license = "GPL-3.0"
13
13
 
@@ -20,6 +20,7 @@ Gem::Specification.new do |spec|
20
20
  spec.require_paths = ["lib"]
21
21
 
22
22
  spec.add_development_dependency "bundler", "~> 1.16"
23
+ spec.add_development_dependency "coveralls"
23
24
  spec.add_development_dependency "rake", "~> 10.0"
24
25
  spec.add_development_dependency "rspec", "~> 3.0"
25
26
  end
data/lib/fuzz.rb CHANGED
@@ -1,8 +1,11 @@
1
1
  module Fuzz
2
2
  end
3
3
 
4
+ require "fuzz/executable"
4
5
  require "fuzz/cache"
6
+ require "fuzz/dmenu_picker"
5
7
  require "fuzz/entry"
6
8
  require "fuzz/null_cache"
9
+ require "fuzz/rofi_picker"
7
10
  require "fuzz/selector"
8
11
  require "fuzz/version"
data/lib/fuzz/cache.rb CHANGED
@@ -1,53 +1,55 @@
1
1
  require "fileutils"
2
2
 
3
- class Fuzz::Cache
4
- def initialize(cache_file)
5
- @cache_file = File.expand_path(cache_file)
6
- @entries = cache_entries(@cache_file)
7
- end
3
+ module Fuzz
4
+ class Cache
5
+ def initialize(cache_file)
6
+ @cache_file = File.expand_path(cache_file)
7
+ @entries = cache_entries(@cache_file)
8
+ end
8
9
 
9
- def weight(title)
10
- entries.fetch(title, 0)
11
- end
10
+ def weight(title)
11
+ entries.fetch(title, 0)
12
+ end
12
13
 
13
- def increment!(title)
14
- entries[title] = weight(title) + 1
15
- write
16
- end
14
+ def increment(title)
15
+ entries[title] = weight(title) + 1
16
+ write
17
+ end
17
18
 
18
- private
19
+ private
19
20
 
20
- attr_reader :cache_file, :entries
21
+ attr_reader :cache_file, :entries
21
22
 
22
- def write
23
- File.open(cache_file, "w") do |file|
24
- entries.each do |title, count|
25
- file.puts("#{ count } #{ title }")
23
+ def write
24
+ File.open(cache_file, "w") do |file|
25
+ entries.each do |title, count|
26
+ file.puts("#{ count } #{ title }")
27
+ end
26
28
  end
27
29
  end
28
- end
29
30
 
30
- def cache_entries(cache_file)
31
- if File.exist?(cache_file)
32
- hash_from_file(File.new(cache_file))
33
- else
34
- {}
31
+ def cache_entries(cache_file)
32
+ if File.exist?(cache_file)
33
+ hash_from_file(File.new(cache_file))
34
+ else
35
+ {}
36
+ end
35
37
  end
36
- end
37
38
 
38
- def hash_from_file(cache_file)
39
- cache_file.readlines.inject({}) { |hash, line|
40
- count, title = line.split(" ", 2)
41
- hash[title.strip] = count.to_i
42
- hash
43
- }
44
- end
39
+ def hash_from_file(cache_file)
40
+ cache_file.readlines.inject({}) { |hash, line|
41
+ count, title = line.split(" ", 2)
42
+ hash[title.strip] = count.to_i
43
+ hash
44
+ }
45
+ end
45
46
 
46
- def directory
47
- File.dirname(cache_file)
48
- end
47
+ def directory
48
+ File.dirname(cache_file)
49
+ end
49
50
 
50
- def ensure_directory_exists
51
- FileUtils.mkdir_p(directory)
51
+ def ensure_directory_exists
52
+ FileUtils.mkdir_p(directory)
53
+ end
52
54
  end
53
55
  end
@@ -0,0 +1,9 @@
1
+ module Fuzz
2
+ class DmenuPicker
3
+ def pick(keys)
4
+ Fuzz::Executable.new("dmenu").error_if_missing
5
+
6
+ `echo "#{ keys.join("\n") }" | dmenu -i`.strip
7
+ end
8
+ end
9
+ end
data/lib/fuzz/entry.rb CHANGED
@@ -1,19 +1,23 @@
1
- class Fuzz::Entry
2
- include Comparable
1
+ module Fuzz
2
+ class Entry
3
+ include Comparable
3
4
 
4
- attr_reader :title, :object, :weight
5
+ attr_reader :title, :object, :weight
5
6
 
6
- def initialize(title:, object:, weight:)
7
- @title = title
8
- @object = object
9
- @weight = weight
10
- end
7
+ def initialize(title:, object:, weight:)
8
+ @title = title
9
+ @object = object
10
+ @weight = weight
11
+ end
12
+
13
+ def <=>(other)
14
+ other_weight = other.weight
11
15
 
12
- def <=>(other)
13
- if weight != other.weight
14
- other.weight <=> weight
15
- else
16
- title <=> other.title
16
+ if weight != other_weight
17
+ other_weight <=> weight
18
+ else
19
+ title <=> other.title
20
+ end
17
21
  end
18
22
  end
19
23
  end
@@ -0,0 +1,28 @@
1
+ module Fuzz
2
+ class MissingExecutableError < StandardError
3
+ end
4
+
5
+ class Executable
6
+ def initialize(command)
7
+ @command = command
8
+ end
9
+
10
+ def error_if_missing
11
+ if !installed?
12
+ raise(
13
+ MissingExecutableError,
14
+ "Can't find the `#{ command }` executable!",
15
+ )
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ attr_reader :command
22
+
23
+ def installed?
24
+ system("which #{ command }")
25
+ $?.success?
26
+ end
27
+ end
28
+ end
@@ -1,14 +1,16 @@
1
- class Fuzz::NullCache
2
- def initialize(*)
3
- end
1
+ module Fuzz
2
+ class NullCache
3
+ def initialize(*)
4
+ end
4
5
 
5
- def weight(*)
6
- 0
7
- end
6
+ def weight(*)
7
+ 0
8
+ end
8
9
 
9
- def increment!(*)
10
- end
10
+ def increment(*)
11
+ end
11
12
 
12
- def write
13
+ def write
14
+ end
13
15
  end
14
16
  end
@@ -0,0 +1,9 @@
1
+ module Fuzz
2
+ class PickPicker
3
+ def pick(keys)
4
+ Fuzz::Executable.new("pick").error_if_missing
5
+
6
+ `echo "#{ keys.join("\n") }" | pick`.strip
7
+ end
8
+ end
9
+ end
@@ -1,26 +1,15 @@
1
- class Fuzz::RofiPicker
2
- def initialize
3
- assert_executable_available
4
- end
1
+ module Fuzz
2
+ class RofiPicker
3
+ def pick(keys)
4
+ Fuzz::Executable.new("rofi").error_if_missing
5
5
 
6
- def pick(keys)
7
- `echo "#{ keys.join("\n") }" | #{ command }`.strip
8
- end
6
+ `echo "#{ keys.join("\n") }" | #{ command }`.strip
7
+ end
9
8
 
10
- private
9
+ private
11
10
 
12
- def command
13
- "rofi -show run -matching fuzzy -dmenu -i"
14
- end
15
-
16
- def assert_executable_available
17
- if !installed?
18
- raise "Can't find the `rofi` executable!"
11
+ def command
12
+ "rofi -show run -matching fuzzy -dmenu -i"
19
13
  end
20
14
  end
21
-
22
- def installed?
23
- `which rofi`
24
- $?.success?
25
- end
26
15
  end
data/lib/fuzz/selector.rb CHANGED
@@ -3,42 +3,52 @@ require_relative "entry"
3
3
  require_relative "rofi_picker"
4
4
  require_relative "null_cache"
5
5
 
6
- class Fuzz::Selector
7
- def initialize(items, options = {})
8
- @cache = options.fetch(:cache, Fuzz::NullCache.new)
9
- @picker = options.fetch(:picker, Fuzz::RofiPicker.new)
10
- @entries = items.map { |item| make_entry(item, @cache) }
11
- end
6
+ module Fuzz
7
+ class Selector
8
+ def initialize(items, options = {})
9
+ @cache = options.fetch(:cache, Fuzz::NullCache.new)
10
+ @default = options.fetch(:default, nil)
11
+ @picker = options.fetch(:picker, Fuzz::RofiPicker.new)
12
+ @entries = items.map { |item| make_entry(item, @cache) }
13
+ end
12
14
 
13
- def pick
14
- title = picker.pick(titles)
15
- chosen_entry = find_entry_by_title(title)
15
+ def pick
16
+ title = picker.pick(titles)
17
+ chosen_entry = find_entry_by_title(title)
16
18
 
17
- if chosen_entry.nil?
18
- nil
19
- else
20
- cache.increment!(chosen_entry.title)
21
- chosen_entry.object
19
+ if chosen_entry.nil?
20
+ default
21
+ else
22
+ cache.increment(chosen_entry.title)
23
+ chosen_entry.object
24
+ end
22
25
  end
23
- end
24
26
 
25
- private
27
+ private
26
28
 
27
- attr_reader :entries, :picker, :cache
29
+ attr_reader(
30
+ :cache,
31
+ :default,
32
+ :entries,
33
+ :picker,
34
+ )
28
35
 
29
- def titles
30
- entries.sort.map(&:title)
31
- end
36
+ def titles
37
+ entries.sort.map(&:title)
38
+ end
32
39
 
33
- def find_entry_by_title(title)
34
- entries.detect { |entry| entry.title == title }
35
- end
40
+ def find_entry_by_title(title)
41
+ entries.detect { |entry| entry.title == title }
42
+ end
36
43
 
37
- def make_entry(item, cache)
38
- Fuzz::Entry.new(
39
- title: item.to_s,
40
- object: item,
41
- weight: cache.weight(item.to_s) || 0,
42
- )
44
+ def make_entry(item, cache)
45
+ title = item.to_s
46
+
47
+ Fuzz::Entry.new(
48
+ title: title,
49
+ object: item,
50
+ weight: cache.weight(title) || 0,
51
+ )
52
+ end
43
53
  end
44
54
  end
data/lib/fuzz/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Fuzz
2
- VERSION = "0.1.1"
2
+ VERSION = "0.1.2".freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fuzz
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Harry Schwartz
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-02-25 00:00:00.000000000 Z
11
+ date: 2018-03-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.16'
27
+ - !ruby/object:Gem::Dependency
28
+ name: coveralls
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: rake
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -73,8 +87,11 @@ files:
73
87
  - fuzz.gemspec
74
88
  - lib/fuzz.rb
75
89
  - lib/fuzz/cache.rb
90
+ - lib/fuzz/dmenu_picker.rb
76
91
  - lib/fuzz/entry.rb
92
+ - lib/fuzz/executable.rb
77
93
  - lib/fuzz/null_cache.rb
94
+ - lib/fuzz/pick_picker.rb
78
95
  - lib/fuzz/rofi_picker.rb
79
96
  - lib/fuzz/selector.rb
80
97
  - lib/fuzz/version.rb
@@ -101,5 +118,5 @@ rubyforge_project:
101
118
  rubygems_version: 2.7.3
102
119
  signing_key:
103
120
  specification_version: 4
104
- summary: Wrap command-line tools to graphically select from a list of Ruby objects!
121
+ summary: Use command-line tools to choose from a list of Ruby objects!
105
122
  test_files: []