From 1195772b02e9e8b6613cd02daa9823ff2b6c49a1 Mon Sep 17 00:00:00 2001 From: Douglas Eichelberger Date: Mon, 6 Mar 2023 22:37:09 -0800 Subject: [PATCH 1/3] Refactor Searchable into Homebrew::Search --- Library/Homebrew/cask/cask.rb | 2 - Library/Homebrew/description_cache_store.rb | 3 -- Library/Homebrew/search.rb | 43 ++++++++++++++++----- Library/Homebrew/search.rbi | 5 +++ Library/Homebrew/searchable.rb | 39 ------------------- 5 files changed, 38 insertions(+), 54 deletions(-) create mode 100644 Library/Homebrew/search.rbi delete mode 100644 Library/Homebrew/searchable.rb diff --git a/Library/Homebrew/cask/cask.rb b/Library/Homebrew/cask/cask.rb index 684969ac97..0ddf372e84 100644 --- a/Library/Homebrew/cask/cask.rb +++ b/Library/Homebrew/cask/cask.rb @@ -5,7 +5,6 @@ require "cask/cask_loader" require "cask/config" require "cask/dsl" require "cask/metadata" -require "searchable" require "utils/bottles" module Cask @@ -16,7 +15,6 @@ module Cask extend T::Sig extend Forwardable - extend Searchable extend Predicable include Metadata diff --git a/Library/Homebrew/description_cache_store.rb b/Library/Homebrew/description_cache_store.rb index fe6df26d22..daa9016dc4 100644 --- a/Library/Homebrew/description_cache_store.rb +++ b/Library/Homebrew/description_cache_store.rb @@ -3,15 +3,12 @@ require "set" require "cache_store" -require "searchable" # # {DescriptionCacheStore} provides methods to fetch and mutate formula descriptions used # by the `brew desc` and `brew search` commands. # class DescriptionCacheStore < CacheStore - include Searchable - # Inserts a formula description into the cache if it does not exist or # updates the formula description if it does exist. # diff --git a/Library/Homebrew/search.rb b/Library/Homebrew/search.rb index 5544f24845..a415d5d33f 100644 --- a/Library/Homebrew/search.rb +++ b/Library/Homebrew/search.rb @@ -1,7 +1,6 @@ -# typed: false +# typed: true # frozen_string_literal: true -require "searchable" require "description_cache_store" module Homebrew @@ -98,11 +97,7 @@ module Homebrew end aliases = Formula.alias_full_names - results = (Formula.full_names + aliases) - .extend(Searchable) - .search(string_or_regex) - .sort - + results = search(Formula.full_names + aliases, string_or_regex).sort results |= Formula.fuzzy_search(string_or_regex).map { |n| Formulary.factory(n).full_name } results.map do |name| @@ -141,9 +136,7 @@ module Homebrew cask_tokens += Homebrew::API::Cask.all_casks.keys end - results = cask_tokens.extend(Searchable) - .search(string_or_regex) - + results = search(cask_tokens, string_or_regex) results += DidYouMean::SpellChecker.new(dictionary: cask_tokens) .correct(string_or_regex) @@ -176,5 +169,35 @@ module Homebrew [all_formulae, all_casks] end + + def search(array, string_or_regex, &block) + case string_or_regex + when Regexp + search_regex(array, string_or_regex, &block) + else + search_string(array, string_or_regex.to_str, &block) + end + end + + def simplify_string(string) + string.downcase.gsub(/[^a-z\d]/i, "") + end + + def search_regex(array, regex) + array.select do |*args| + args = yield(*args) if block_given? + args = Array(args).flatten.compact + args.any? { |arg| arg.match?(regex) } + end + end + + def search_string(array, string) + simplified_string = simplify_string(string) + array.select do |*args| + args = yield(*args) if block_given? + args = Array(args).flatten.compact + args.any? { |arg| simplify_string(arg).include?(simplified_string) } + end + end end end diff --git a/Library/Homebrew/search.rbi b/Library/Homebrew/search.rbi new file mode 100644 index 0000000000..2c8f4bdb6e --- /dev/null +++ b/Library/Homebrew/search.rbi @@ -0,0 +1,5 @@ +# typed: strict + +module Homebrew::Search + include Kernel +end diff --git a/Library/Homebrew/searchable.rb b/Library/Homebrew/searchable.rb deleted file mode 100644 index 18dd8e25a5..0000000000 --- a/Library/Homebrew/searchable.rb +++ /dev/null @@ -1,39 +0,0 @@ -# typed: false -# frozen_string_literal: true - -# Helper module for making a class searchable with both regular expressions and strings. -# -# @api private -module Searchable - def search(string_or_regex, &block) - case string_or_regex - when Regexp - search_regex(string_or_regex, &block) - else - search_string(string_or_regex.to_str, &block) - end - end - - private - - def simplify_string(string) - string.downcase.gsub(/[^a-z\d]/i, "") - end - - def search_regex(regex) - select do |*args| - args = yield(*args) if block_given? - args = Array(args).flatten.compact - args.any? { |arg| arg.match?(regex) } - end - end - - def search_string(string) - simplified_string = simplify_string(string) - select do |*args| - args = yield(*args) if block_given? - args = Array(args).flatten.compact - args.any? { |arg| simplify_string(arg).include?(simplified_string) } - end - end -end From 23587b70a19d5be7d17a2f191756968864e95a69 Mon Sep 17 00:00:00 2001 From: Douglas Eichelberger Date: Tue, 7 Mar 2023 09:08:14 -0800 Subject: [PATCH 2/3] Port Descriptions --- Library/Homebrew/descriptions.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Library/Homebrew/descriptions.rb b/Library/Homebrew/descriptions.rb index ff252b5798..725ff2354f 100644 --- a/Library/Homebrew/descriptions.rb +++ b/Library/Homebrew/descriptions.rb @@ -3,7 +3,7 @@ require "formula" require "formula_versions" -require "searchable" +require "search" # Helper class for printing and searching descriptions. # @@ -15,11 +15,11 @@ class Descriptions results = case field when :name - cache_store.search(string_or_regex) { |name, _| name } + Homebrew::Search.search(cache_store, string_or_regex) { |name, _| name } when :desc - cache_store.search(string_or_regex) { |_, desc| desc } + Homebrew::Search.search(cache_store, string_or_regex) { |_, desc| desc } when :either - cache_store.search(string_or_regex) + Homebrew::Search.search(cache_store, string_or_regex) end new(results) From 1f742727afb251e2f56db049968b2dc7259dc8c3 Mon Sep 17 00:00:00 2001 From: Douglas Eichelberger Date: Tue, 7 Mar 2023 09:08:43 -0800 Subject: [PATCH 3/3] Move tests --- Library/Homebrew/test/search_spec.rb | 41 ++++++++++++++++++++ Library/Homebrew/test/searchable_spec.rb | 49 ------------------------ 2 files changed, 41 insertions(+), 49 deletions(-) delete mode 100644 Library/Homebrew/test/searchable_spec.rb diff --git a/Library/Homebrew/test/search_spec.rb b/Library/Homebrew/test/search_spec.rb index 6a37c77183..5a2cea48a7 100644 --- a/Library/Homebrew/test/search_spec.rb +++ b/Library/Homebrew/test/search_spec.rb @@ -59,4 +59,45 @@ describe Homebrew::Search do expect { described_class.query_regexp("/+/") }.to raise_error(/not a valid regex/) end end + + describe "#search" do + let(:collection) { ["with-dashes"] } + + context "when given a block" do + let(:collection) { [["with-dashes", "withdashes"]] } + + it "searches by the selected argument" do + expect(described_class.search(collection, /withdashes/) { |_, short_name| short_name }).not_to be_empty + expect(described_class.search(collection, /withdashes/) { |long_name, _| long_name }).to be_empty + end + end + + context "when given a regex" do + it "does not simplify strings" do + expect(described_class.search(collection, /with-dashes/)).to eq ["with-dashes"] + end + end + + context "when given a string" do + it "simplifies both the query and searched strings" do + expect(described_class.search(collection, "with dashes")).to eq ["with-dashes"] + end + end + + context "when searching a Hash" do + let(:collection) { { "foo" => "bar" } } + + it "returns a Hash" do + expect(described_class.search(collection, "foo")).to eq "foo" => "bar" + end + + context "with a nil value" do + let(:collection) { { "foo" => nil } } + + it "does not raise an error" do + expect(described_class.search(collection, "foo")).to eq "foo" => nil + end + end + end + end end diff --git a/Library/Homebrew/test/searchable_spec.rb b/Library/Homebrew/test/searchable_spec.rb deleted file mode 100644 index eee316379f..0000000000 --- a/Library/Homebrew/test/searchable_spec.rb +++ /dev/null @@ -1,49 +0,0 @@ -# typed: false -# frozen_string_literal: true - -require "searchable" - -describe Searchable do - subject(:searchable_collection) { collection.extend(described_class) } - - let(:collection) { ["with-dashes"] } - - describe "#search" do - context "when given a block" do - let(:collection) { [["with-dashes", "withdashes"]] } - - it "searches by the selected argument" do - expect(searchable_collection.search(/withdashes/) { |_, short_name| short_name }).not_to be_empty - expect(searchable_collection.search(/withdashes/) { |long_name, _| long_name }).to be_empty - end - end - - context "when given a regex" do - it "does not simplify strings" do - expect(searchable_collection.search(/with-dashes/)).to eq ["with-dashes"] - end - end - - context "when given a string" do - it "simplifies both the query and searched strings" do - expect(searchable_collection.search("with dashes")).to eq ["with-dashes"] - end - end - - context "when searching a Hash" do - let(:collection) { { "foo" => "bar" } } - - it "returns a Hash" do - expect(searchable_collection.search("foo")).to eq "foo" => "bar" - end - - context "with a nil value" do - let(:collection) { { "foo" => nil } } - - it "does not raise an error" do - expect(searchable_collection.search("foo")).to eq "foo" => nil - end - end - end - end -end