From a7b35a18f5655d50cf8e94ebae0c633670d3dab3 Mon Sep 17 00:00:00 2001 From: Dana Sherson Date: Sat, 25 Nov 2023 15:11:17 +1300 Subject: [PATCH] Remove bang methods --- .leftovers.yml | 5 - lib/path_list.rb | 213 +++++++------------------------------- spec/path_list_spec.rb | 228 +---------------------------------------- 3 files changed, 40 insertions(+), 406 deletions(-) diff --git a/.leftovers.yml b/.leftovers.yml index 24e1857..e78d530 100644 --- a/.leftovers.yml +++ b/.leftovers.yml @@ -14,14 +14,9 @@ keep: # build - ignore - - ignore! - only - - only! - - union! - union - - intersection! - intersection - '&' - '|' - gitignore - - gitignore! diff --git a/lib/path_list.rb b/lib/path_list.rb index 39abd67..61f8d79 100644 --- a/lib/path_list.rb +++ b/lib/path_list.rb @@ -53,16 +53,14 @@ def initialize # @return [PathList] # a new PathList # @example - # PathList.gitignore.only('*.rb') - # path_list = PathList.only('*.rb'); path_list.gitignore! - # PathList.only('*.rb').gitignore(root: Dir.pwd) # don't look for a .git directory - # PathList.only('*.rb').gitignore(root: '../../') # the project root is two directories up - # PathList.only('*.rb').gitignore(config: false) # don't look for a configured core.excludesFile + # PathList.only('*.rb').gitignore # can be chained + # PathList.gitignore(root: Dir.pwd) # don't look for a .git directory + # PathList.gitignore(root: '../../') # the project root is two directories up + # PathList.gitignore(config: false) # don't look for a configured core.excludesFile # @see #gitignore - # @see #gitignore! # @see https://git-scm.com/docs/gitignore#_pattern_format def self.gitignore(root: nil, config: true) - new.gitignore!(root: root, config: config) + new.gitignore(root: root, config: config) end # Return a new PathList that filters files using .gitignore files. @@ -81,43 +79,14 @@ def self.gitignore(root: nil, config: true) # @return [PathList] # a new PathList # @example - # PathList.gitignore.only('*.rb') - # path_list = PathList.only('*.rb'); path_list.gitignore! - # PathList.only('*.rb').gitignore(root: Dir.pwd) # don't look for a .git directory - # PathList.only('*.rb').gitignore(root: '../../') # the project root is two directories up - # PathList.only('*.rb').gitignore(config: false) # don't look for a configured core.excludesFile + # PathList.only('*.rb').gitignore # can be chained + # PathList.gitignore(root: Dir.pwd) # don't look for a .git directory + # PathList.gitignore(root: '../../') # the project root is two directories up + # PathList.gitignore(config: false) # don't look for a configured core.excludesFile # @see .gitignore - # @see #gitignore! # @see https://git-scm.com/docs/gitignore#_pattern_format def gitignore(root: nil, config: true) - dup.gitignore!(root: root, config: config) - end - - # Update self to filter files using .gitignore files. - # - # This matcher aims for fidelity with `git ls-files`, - # reading the configured core.excludesFile and .gitignore files in subdirectories - # - # @param root [String, Pathname, #to_s, nil] - # The git repo root. When nil, PathList will search up from the current directory like git does for a directory - # containing `.git/`. If it doesn't find anything, it will default to the current directory. - # @param config [Boolean] - # Whether to load the configured `core.excludesFile`. - # When this is false this will only load patterns in `.gitignore` files in the `root:` directory and its children, - # and the `.git/info/exclude` file in the `root:` directory. When true it will also load config files in all the - # locations that git would also look, to find the core.excludesFile. - # @return [self] - # @example - # PathList.gitignore.only('*.rb') - # path_list = PathList.only('*.rb'); path_list.gitignore! - # PathList.only('*.rb').gitignore(root: Dir.pwd) # don't look for a .git directory - # PathList.only('*.rb').gitignore(root: '../../') # the project root is two directories up - # PathList.only('*.rb').gitignore(config: false) # don't look for a configured core.excludesFile - # @see .gitignore - # @see #gitignore - # @see https://git-scm.com/docs/gitignore#_pattern_format - def gitignore!(root: nil, config: true) - and_matcher(Gitignore.build(root: root, config: config)) + new_and_matcher(Gitignore.build(root: root, config: config)) end # @!group Ignore methods @@ -150,12 +119,11 @@ def gitignore!(root: nil, config: true) # a new PathList # @example # PathList.ignore('*.md', root: './docs').ignore(patterns_from_file: '.dockerignore') - # PathList.ignore('/bin').ignore!("ruby", format: :shebang) + # PathList.ignore('/bin').ignore("ruby", format: :shebang) # PathList.gitignore.ignore('spec', format: :exact) # @see #ignore - # @see #ignore! def self.ignore(*patterns, patterns_from_file: nil, format: :gitignore, root: nil) - new.ignore!(*patterns, patterns_from_file: patterns_from_file, format: format, root: root) + new.ignore(*patterns, patterns_from_file: patterns_from_file, format: format, root: root) end # Return a new PathList that filters out files using the given patterns. @@ -186,48 +154,15 @@ def self.ignore(*patterns, patterns_from_file: nil, format: :gitignore, root: ni # a new PathList # @example # PathList.ignore('*.md', root: './docs').ignore(patterns_from_file: '.dockerignore') - # PathList.ignore('/bin').ignore!("ruby", format: :shebang) + # PathList.ignore('/bin').ignore("ruby", format: :shebang) # PathList.gitignore.ignore('spec', format: :exact) # @see .ignore - # @see #ignore! def ignore(*patterns, patterns_from_file: nil, format: :gitignore, root: nil) - dup.ignore!(*patterns, patterns_from_file: patterns_from_file, format: format, root: root) - end - - # Update self to filter out files using the given patterns. - # - # @overload ignore!(*patterns, format: :gitignore, root: nil) - # @param patterns [Array] - # The list of patterns. Within an array, or as a line-separated string. - # The individual pattern format depends on the `format:` param - # @param root [String, Pathname, #to_s, nil] - # The root for any patterns that need it (e.g. gitignore patterns starting with `/`), - # defaults to the current directory when nil. - # @param format [:gitignore, :glob_gitignore, :exact, :shebang] - # The format of the patterns, see {PatternParser::Gitignore}, - # {PatternParser::GlobGitignore}, - # {PatternParser::ExactPath}, - # {PatternParser::Shebang} - # @return [self] - # @overload ignore!(patterns_from_file:, format: :gitignore, root: nil) - # @param patterns_from_file [String, Pathname, #to_s] - # A file to read the list of patterns from, with each pattern on its own line - # @param root [String, Pathname, #to_s, nil] - # The root for any patterns that need it (e.g. gitignore patterns starting with `/`), when nil, - # defaults to the directory containing the patterns_from_file file. - # @param format [:gitignore, :glob_gitignore, :exact, :shebang] - # The format of the rules - # @return [self] - # @example - # PathList.ignore('*.md', root: './docs').ignore(patterns_from_file: '.dockerignore') - # PathList.ignore('/bin').ignore!("ruby", format: :shebang) - # PathList.gitignore.ignore('spec', format: :exact) - # @see .ignore - # @see #ignore - def ignore!(*patterns, patterns_from_file: nil, format: :gitignore, root: nil) - and_matcher(PatternParser.build( - patterns: patterns, patterns_from_file: patterns_from_file, format: format, root: root, polarity: :ignore - )) + new_and_matcher( + PatternParser.build( + patterns: patterns, patterns_from_file: patterns_from_file, format: format, root: root, polarity: :ignore + ) + ) end # @!group Only methods @@ -263,9 +198,8 @@ def ignore!(*patterns, patterns_from_file: nil, format: :gitignore, root: nil) # PathList.only(patterns_from_file: './files_to_copy.txt', exact: true) # PathList.only(['bin', 'lib', 'exe', 'README.md', 'LICENSE']) # @see #only - # @see #only! def self.only(*patterns, patterns_from_file: nil, format: :gitignore, root: nil) - new.only!(*patterns, patterns_from_file: patterns_from_file, format: format, root: root) + new.only(*patterns, patterns_from_file: patterns_from_file, format: format, root: root) end # Return a new PathList that selects only those files that match given patterns. @@ -299,43 +233,8 @@ def self.only(*patterns, patterns_from_file: nil, format: :gitignore, root: nil) # PathList.only(patterns_from_file: './files_to_copy.txt', exact: true) # PathList.only(['bin', 'lib', 'exe', 'README.md', 'LICENSE']) # @see .only - # @see #only! def only(*patterns, patterns_from_file: nil, format: :gitignore, root: nil) - dup.only!(*patterns, patterns_from_file: patterns_from_file, format: format, root: root) - end - - # Update self to select only those files that match given patterns. - # - # @overload only!(*patterns, format: :gitignore, root: nil) - # @param patterns [Array, Array>, String] - # The list of patterns. Within an array, or as a line-separated string. - # The individual pattern format depends on the `format:` param - # @param root [String, Pathname, #to_s, nil] - # The root for any patterns that need it (e.g. gitignore-style patterns starting with `/`), - # defaults to the current directory when nil. - # @param format [:gitignore, :glob_gitignore, :exact, :shebang] - # The format of the patterns, see {PatternParser::Gitignore}, - # {PatternParser::GlobGitignore}, - # {PatternParser::ExactPath}, - # {PatternParser::Shebang} - # @return [self] - # @overload only!(patterns_from_file:, format: :gitignore, root: nil) - # @param patterns_from_file [String, Pathname, #to_s] - # A file to read the list of patterns from, with each pattern on its own line - # @param root [String, Pathname, #to_s, nil] - # The root for any patterns that need it (e.g. gitignore-style patterns starting with `/`), - # when nil, defaults to the directory containing the patterns_from_file file. - # @param format [:gitignore, :glob_gitignore, :exact, :shebang] - # The format of the rules - # @return [self] - # @example - # PathList.ignore('CHANGELOG.md').only!('*.md', root: './docs') - # PathList.only(patterns_from_file: './files_to_copy.txt', exact: true) - # PathList.only(['bin', 'lib', 'exe', 'README.md', 'LICENSE']) - # @see .only - # @see #only - def only!(*patterns, patterns_from_file: nil, format: :gitignore, root: nil) - and_matcher( + new_and_matcher( PatternParser.build( patterns: patterns, patterns_from_file: patterns_from_file, format: format, root: root, polarity: :allow ) @@ -354,7 +253,6 @@ def only!(*patterns, patterns_from_file: nil, format: :gitignore, root: nil) # # is equivalent to # PathList.only(["*.ts", "*.tsx"]) # @see #union - # @see #union! # @see #| def self.union(path_list, *path_lists) path_list.union(*path_lists) @@ -370,10 +268,11 @@ def self.union(path_list, *path_lists) # # is equivalent to # PathList.gitignore.only(["*.js", "*.jsx", "*.ts", "*.tsx"]) # @see .union - # @see #union! # @see #| def union(*path_lists) - dup.union!(*path_lists) + new_with_matcher( + Matcher::Any.build([@matcher, *path_lists.map { |l| l.matcher }]) # rubocop:disable Style/SymbolProc + ) end # Return a new PathList that matches the receiver OR other. @@ -387,27 +286,8 @@ def union(*path_lists) # PathList.gitignore.only(["*.rb", "*.sh"]) # @see .union # @see #union - # @see #union! def |(other) - dup.union!(other) - end - - # Update self with path_lists as alternate matchers. - # - # @param other [PathList] - # @return [self] - # @example - # my_path_list = PathList.new - # my_path_list.union!(PathList.only("*.rb"), PathList.ignore("*.py")) - # # my_path_list is now equivalent to - # PathList.only("*.rb", "*.py") - # @see .union - # @see #union - # @see #| - def union!(*path_lists) - self.matcher = Matcher::Any.build([@matcher, *path_lists.map { |l| l.matcher }]) # rubocop:disable Style/SymbolProc - - self + union(other) end # @!group Intersection methods @@ -422,10 +302,9 @@ def union!(*path_lists) # # is equivalent to # PathList.only("*.rb").ignore("/vendor/") # @see #intersection - # @see #intersection! # @see #& def self.intersection(*path_lists) - new.intersection!(*path_lists) + new.intersection(*path_lists) end # Return a new PathList that matches the receiver AND all of path_lists. @@ -438,10 +317,9 @@ def self.intersection(*path_lists) # # is equivalent to # PathList.gitignore.only("*.rb").ignore("/vendor/") # @see .intersection - # @see #intersection! # @see #& def intersection(*path_lists) - dup.intersection!(*path_lists) + new_and_matcher(Matcher::All.build(path_lists.map { |l| l.matcher })) # rubocop:disable Style/SymbolProc end # Return a new PathList that matchers the receiver AND other. @@ -455,25 +333,8 @@ def intersection(*path_lists) # PathList.gitignore.only("*.rb").ignore("/vendor/") # @see .intersection # @see #intersection - # @see #intersection! def &(other) - dup.intersection!(other) - end - - # Update self with path_lists as additional matchers - # - # @param other [PathList] - # @return [self] - # @example - # my_path_list = PathList.gitignore - # my_path_list.intersection!(PathList.only("*.rb"), PathList.ignore("/vendor/")) - # # my_path_list is now equivalent to - # PathList.gitignore.only("*.rb").ignore("/vendor/") - # @see .intersection - # @see #intersection - # @see #& - def intersection!(*path_lists) - and_matcher(Matcher::All.build(path_lists.map { |l| l.matcher })) # rubocop:disable Style/SymbolProc + intersection(other) end # @!group Querying methods @@ -590,6 +451,12 @@ def each(root = '.', &block) attr_reader :matcher + def matcher=(new_matcher) + @matcher = new_matcher + @dir_matcher = nil + @file_matcher = nil + end + private def recursive_each(candidate, relative_root, dir_matcher, file_matcher, &block) @@ -612,16 +479,14 @@ def recursive_match?(candidate, matcher) recursive_match?(candidate.parent, matcher) && matcher.match(candidate) == :allow end - def and_matcher(new_matcher) - self.matcher = Matcher::All.build([@matcher, new_matcher]) - - self + def new_with_matcher(matcher) + path_list = self.class.new + path_list.matcher = matcher + path_list end - def matcher=(new_matcher) - @matcher = new_matcher - @dir_matcher = nil - @file_matcher = nil + def new_and_matcher(matcher) + new_with_matcher(Matcher::All.build([@matcher, matcher])) end def dir_matcher diff --git a/spec/path_list_spec.rb b/spec/path_list_spec.rb index 18aa6e5..dac45c1 100644 --- a/spec/path_list_spec.rb +++ b/spec/path_list_spec.rb @@ -477,7 +477,7 @@ 10.times { described_class.only('a').gitignore } 10.times { described_class.gitignore.only('a') } 10.times { described_class.union(described_class.gitignore, described_class.only('a')).gitignore } - 10.times { described_class.only('a').intersection!(described_class.gitignore, described_class.only('a')) } + 10.times { described_class.only('a').intersection(described_class.gitignore, described_class.only('a')) } expect(PathList::Gitignore).to have_received(:new).once expect(PathList::PatternParser).to have_received(:new).exactly(4).times @@ -514,24 +514,6 @@ expect(gitignore_only_path_list).to allow_files('baz') end - it 'works for .gitignore and #only!' do - gitignore 'bar' - - gitignore_path_list = described_class.gitignore - - expect(gitignore_path_list).not_to allow_files('bar') - expect(gitignore_path_list).to allow_files('baz', 'foo') - - gitignore_only_path_list = gitignore_path_list.only!(['bar', 'baz']) - expect(gitignore_path_list).to be gitignore_only_path_list - - expect(gitignore_path_list).not_to allow_files('foo', 'bar') - expect(gitignore_path_list).to allow_files('baz') - - expect(gitignore_only_path_list).not_to allow_files('foo', 'bar') - expect(gitignore_only_path_list).to allow_files('baz') - end - it 'works for .only and #gitignore' do gitignore 'bar' @@ -546,24 +528,6 @@ expect(gitignore_only_path_list).to allow_files('baz') end - it 'works for .only and #gitignore!' do - gitignore 'bar' - - only_path_list = described_class.only(['bar', 'baz']) - - expect(only_path_list).not_to allow_files('foo') - expect(only_path_list).to allow_files('baz', 'bar') - - gitignore_only_path_list = only_path_list.gitignore! - expect(only_path_list).to be gitignore_only_path_list - - expect(only_path_list).not_to allow_files('foo', 'bar') - expect(only_path_list).to allow_files('baz') - - expect(gitignore_only_path_list).not_to allow_files('foo', 'bar') - expect(gitignore_only_path_list).to allow_files('baz') - end - it 'works for .ignore and #only' do ignore_path_list = described_class.ignore('bar') ignore_only_path_list = ignore_path_list.only(['bar', 'baz']) @@ -576,22 +540,6 @@ expect(ignore_only_path_list).to allow_files('baz') end - it 'works for .ignore and #only!' do - ignore_path_list = described_class.ignore('bar') - - expect(ignore_path_list).not_to allow_files('bar') - expect(ignore_path_list).to allow_files('baz', 'foo') - - ignore_only_path_list = ignore_path_list.only!(['bar', 'baz']) - expect(ignore_path_list).to be ignore_only_path_list - - expect(ignore_path_list).not_to allow_files('foo', 'bar') - expect(ignore_path_list).to allow_files('baz') - - expect(ignore_only_path_list).not_to allow_files('foo', 'bar') - expect(ignore_only_path_list).to allow_files('baz') - end - it 'works for .only and #ignore' do only_path_list = described_class.only(['bar', 'baz']) ignore_only_path_list = only_path_list.ignore('bar') @@ -604,22 +552,6 @@ expect(ignore_only_path_list).to allow_files('baz') end - it 'works for .only and #ignore!' do - only_path_list = described_class.only(['bar', 'baz']) - - expect(only_path_list).not_to allow_files('foo') - expect(only_path_list).to allow_files('baz', 'bar') - - ignore_only_path_list = only_path_list.ignore!('bar') - expect(only_path_list).to be ignore_only_path_list - - expect(only_path_list).not_to allow_files('foo', 'bar') - expect(only_path_list).to allow_files('baz') - - expect(ignore_only_path_list).not_to allow_files('foo', 'bar') - expect(ignore_only_path_list).to allow_files('baz') - end - it 'works for .gitignore and #ignore' do gitignore 'bar' @@ -634,24 +566,6 @@ expect(gitignore_ignore_path_list).to allow_files('baz') end - it 'works for .gitignore and #ignore!' do - gitignore 'bar' - - gitignore_path_list = described_class.gitignore - - expect(gitignore_path_list).not_to allow_files('bar') - expect(gitignore_path_list).to allow_files('baz', 'foo') - - gitignore_ignore_path_list = gitignore_path_list.ignore!(['foo']) - expect(gitignore_path_list).to be gitignore_ignore_path_list - - expect(gitignore_path_list).not_to allow_files('foo', 'bar') - expect(gitignore_path_list).to allow_files('baz') - - expect(gitignore_ignore_path_list).not_to allow_files('foo', 'bar') - expect(gitignore_ignore_path_list).to allow_files('baz') - end - it 'works for .ignore and #gitignore' do gitignore 'bar' @@ -666,24 +580,6 @@ expect(gitignore_ignore_path_list).to allow_files('baz') end - it 'works for .ignore and #gitignore!' do - gitignore 'bar' - - ignore_path_list = described_class.ignore(['foo']) - - expect(ignore_path_list).not_to allow_files('foo') - expect(ignore_path_list).to allow_files('baz', 'bar') - - gitignore_ignore_path_list = ignore_path_list.gitignore! - expect(ignore_path_list).to be gitignore_ignore_path_list - - expect(ignore_path_list).not_to allow_files('foo', 'bar') - expect(ignore_path_list).to allow_files('baz') - - expect(gitignore_ignore_path_list).not_to allow_files('foo', 'bar') - expect(gitignore_ignore_path_list).to allow_files('baz') - end - context 'when combined with #intersection' do it 'works for .gitignore and .only' do gitignore 'bar' @@ -766,89 +662,6 @@ end end - context 'when combined with #intersection!' do - it 'works for .gitignore and .only' do - gitignore 'bar' - - gitignore_path_list = described_class.gitignore - - expect(gitignore_path_list).not_to allow_files('bar') - expect(gitignore_path_list).to allow_files('baz', 'foo') - - gitignore_only_path_list = gitignore_path_list.intersection!(described_class.only(['bar', 'baz'])) - expect(gitignore_path_list).to be gitignore_only_path_list - - expect(gitignore_only_path_list).not_to allow_files('foo', 'bar') - expect(gitignore_only_path_list).to allow_files('baz') - end - - it 'works for .only and .gitignore' do - gitignore 'bar' - - only_path_list = described_class.only(['bar', 'baz']) - - expect(only_path_list).not_to allow_files('foo') - expect(only_path_list).to allow_files('baz', 'bar') - gitignore_only_path_list = only_path_list.intersection!(described_class.gitignore) - expect(only_path_list).to be gitignore_only_path_list - - expect(gitignore_only_path_list).not_to allow_files('foo', 'bar') - expect(gitignore_only_path_list).to allow_files('baz') - end - - it 'works for .ignore and .only' do - ignore_path_list = described_class.ignore('bar') - - expect(ignore_path_list).not_to allow_files('bar') - expect(ignore_path_list).to allow_files('baz', 'foo') - ignore_only_path_list = ignore_path_list.intersection!(described_class.only(['bar', 'baz'])) - expect(ignore_path_list).to be ignore_only_path_list - - expect(ignore_only_path_list).not_to allow_files('foo', 'bar') - expect(ignore_only_path_list).to allow_files('baz') - end - - it 'works for .only and .ignore' do - only_path_list = described_class.only(['bar', 'baz']) - - expect(only_path_list).not_to allow_files('foo') - expect(only_path_list).to allow_files('baz', 'bar') - ignore_only_path_list = only_path_list.intersection!(described_class.ignore('bar')) - expect(only_path_list).to be ignore_only_path_list - - expect(ignore_only_path_list).not_to allow_files('foo', 'bar') - expect(ignore_only_path_list).to allow_files('baz') - end - - it 'works for .gitignore and .ignore' do - gitignore 'bar' - - gitignore_path_list = described_class.gitignore - - expect(gitignore_path_list).not_to allow_files('bar') - expect(gitignore_path_list).to allow_files('baz', 'foo') - gitignore_ignore_path_list = gitignore_path_list.intersection!(described_class.ignore(['foo'])) - expect(gitignore_path_list).to be gitignore_ignore_path_list - - expect(gitignore_ignore_path_list).not_to allow_files('foo', 'bar') - expect(gitignore_ignore_path_list).to allow_files('baz') - end - - it 'works for .ignore and .gitignore' do - gitignore 'bar' - - ignore_path_list = described_class.ignore(['foo']) - - expect(ignore_path_list).not_to allow_files('foo') - expect(ignore_path_list).to allow_files('baz', 'bar') - gitignore_ignore_path_list = ignore_path_list.intersection!(described_class.gitignore) - expect(ignore_path_list).to be gitignore_ignore_path_list - - expect(gitignore_ignore_path_list).not_to allow_files('foo', 'bar') - expect(gitignore_ignore_path_list).to allow_files('baz') - end - end - context 'when combined with #union' do it 'works for .gitignore and .only' do gitignore 'bar', 'foo' @@ -974,25 +787,6 @@ end end - describe '#intersection!' do - it 'can combine with AND any number of path lists, modifying the receiver' do - path_list = described_class.only('a', 'b', 'c') - expect(path_list).to allow_files('a', 'b', 'c') - expect(path_list).not_to allow_files('d') - - intersection_path_list = path_list.intersection!( - described_class.only('b', 'c', 'd'), - described_class.only('a', 'b', 'd') - ) - - expect(path_list).to allow_files('b') - expect(path_list).not_to allow_files('a', 'c', 'd') - - expect(intersection_path_list).to allow_files('b') - expect(intersection_path_list).not_to allow_files('a', 'c', 'd') - end - end - describe '#union' do it 'can combine with OR any number of path lists' do path_list = described_class.only('a', 'b', 'c') @@ -1035,26 +829,6 @@ end end - describe '#union!' do - it 'can combine with AND any number of path lists, modifying the receiver' do - path_list = described_class.only('a', 'b', 'c') - - expect(path_list).to allow_files('a', 'b', 'c') - expect(path_list).not_to allow_files('d') - - union_path_list = path_list.union!( - described_class.only('b', 'c', 'd'), - described_class.only('a', 'e') - ) - - expect(path_list).to allow_files('a', 'b', 'c', 'd', 'e') - expect(path_list).not_to allow_files('f', 'g', 'h') - - expect(union_path_list).to allow_files('a', 'b', 'c', 'd', 'e') - expect(union_path_list).not_to allow_files('f', 'g', 'h') - end - end - describe '.only' do context 'with blank only value' do subject(:path_list) { described_class.only([]) }