pax_global_header 0000666 0000000 0000000 00000000064 13774751200 0014520 g ustar 00root root 0000000 0000000 52 comment=32f017b976671758224b63ce76e36e0c68a813bf
pathspec-ruby-1.0.0/ 0000775 0000000 0000000 00000000000 13774751200 0014304 5 ustar 00root root 0000000 0000000 pathspec-ruby-1.0.0/.gitignore 0000664 0000000 0000000 00000001274 13774751200 0016300 0 ustar 00root root 0000000 0000000 *.gem
*.rbc
/.config
/coverage/
/InstalledFiles
/pkg/
/spec/reports/
/test/tmp/
/test/version_tmp/
/tmp/
## Specific to RubyMotion:
.dat*
.repl_history
build/
## Documentation cache and generated files:
/.yardoc/
/_yardoc/
/doc/
/rdoc/
/docs/man/*.1
## Environment normalisation:
/.bundle/
/lib/bundler/man/
# for a library or gem, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
Gemfile.lock
.ruby-version
.ruby-gemset
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
.rvmrc
# OS generated files #
######################
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
pathspec-ruby-1.0.0/.rubocop.yml 0000664 0000000 0000000 00000000263 13774751200 0016557 0 ustar 00root root 0000000 0000000 inherit_from: .rubocop_todo.yml
AllCops:
TargetRubyVersion: 2.6
NewCops: enable
Style/NumericPredicate:
Enabled: false
Layout/ClosingHeredocIndentation:
Enabled: false
pathspec-ruby-1.0.0/.rubocop_todo.yml 0000664 0000000 0000000 00000006734 13774751200 0017615 0 ustar 00root root 0000000 0000000 # This configuration was generated by
# `rubocop --auto-gen-config`
# on 2018-01-11 16:42:16 -0800 using RuboCop version 0.52.1.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
# versions of RuboCop, may require this file to be generated again.
# Offense count: 2
# Cop supports --auto-correct.
Layout/BlockEndNewline:
Exclude:
- 'spec/unit/pathspec_spec.rb'
# Offense count: 6
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
# SupportedStyles: auto_detection, squiggly, active_support, powerpack, unindent
Layout/HeredocIndentation:
Exclude:
- 'spec/unit/pathspec_spec.rb'
# Offense count: 8
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces, SpaceBeforeBlockParameters.
# SupportedStyles: space, no_space
# SupportedStylesForEmptyBraces: space, no_space
Layout/SpaceInsideBlockBraces:
Exclude:
- 'lib/pathspec.rb'
- 'spec/unit/pathspec_spec.rb'
# Offense count: 2
Lint/ImplicitStringConcatenation:
Exclude:
- 'lib/pathspec/gitignorespec.rb'
# Offense count: 1
# Cop supports --auto-correct.
# Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods.
Lint/UnusedMethodArgument:
Exclude:
- 'lib/pathspec/spec.rb'
# Offense count: 3
Lint/UselessAssignment:
Exclude:
- 'spec/unit/pathspec_spec.rb'
# Offense count: 2
Lint/Void:
Exclude:
- 'lib/pathspec.rb'
- 'lib/pathspec/gitignorespec.rb'
# Offense count: 3
Metrics/AbcSize:
Enabled: false
Max: 62
# Offense count: 7
# Configuration parameters: CountComments, ExcludedMethods.
Metrics/BlockLength:
Enabled: false
Max: 270
# Offense count: 2
# Configuration parameters: CountBlocks.
Metrics/BlockNesting:
Max: 4
# Offense count: 1
# Configuration parameters: CountComments.
Metrics/ClassLength:
Max: 146
# Offense count: 2
Metrics/CyclomaticComplexity:
Max: 26
# Offense count: 3
# Configuration parameters: CountComments.
Metrics/MethodLength:
Max: 77
# Offense count: 2
Metrics/PerceivedComplexity:
Max: 32
# Offense count: 25
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, ProceduralMethods, FunctionalMethods, IgnoredMethods.
# SupportedStyles: line_count_based, semantic, braces_for_chaining
# ProceduralMethods: benchmark, bm, bmbm, create, each_with_object, measure, new, realtime, tap, with_object
# FunctionalMethods: let, let!, subject, watch
# IgnoredMethods: lambda, proc, it
Style/BlockDelimiters:
Exclude:
- 'spec/unit/pathspec_spec.rb'
# Offense count: 11
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
# SupportedStyles: when_needed, always, never
Style/FrozenStringLiteralComment:
Exclude:
- 'Gemfile'
- 'Rakefile'
- 'lib/pathspec.rb'
- 'lib/pathspec/gitignorespec.rb'
- 'lib/pathspec/regexspec.rb'
- 'lib/pathspec/spec.rb'
- 'pathspec.gemspec'
- 'spec/spec_helper.rb'
- 'spec/unit/pathspec/gitignorespec_spec.rb'
- 'spec/unit/pathspec/spec_spec.rb'
- 'spec/unit/pathspec_spec.rb'
# Offense count: 1
# Cop supports --auto-correct.
# Configuration parameters: IgnoredMethods.
# IgnoredMethods: respond_to, define_method
Style/SymbolProc:
Exclude:
- 'lib/pathspec.rb'
# Offense count: 7
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
# URISchemes: http, https
Layout/LineLength:
Max: 108
pathspec-ruby-1.0.0/.tool-versions 0000664 0000000 0000000 00000000013 13774751200 0017122 0 ustar 00root root 0000000 0000000 ruby 2.7.2
pathspec-ruby-1.0.0/.travis.yml 0000664 0000000 0000000 00000000165 13774751200 0016417 0 ustar 00root root 0000000 0000000 language: ruby
before_install:
- gem install bundler
script: 'bundle exec rake'
rvm:
- 2.6.5
- 2.7.2
- 3.0.0
pathspec-ruby-1.0.0/CHANGELOG.md 0000664 0000000 0000000 00000002305 13774751200 0016115 0 ustar 00root root 0000000 0000000 # pathspec-ruby CHANGELOG
## 0.2.0 (Minor Release)
- (Feature) A CLI tool, pathspec-rb, is now provided with the gem.
- (API Change) New namespace for gem: `PathSpec`: Everything is now namespaced under `PathSpec`, to prevent naming collisions with other libraries. Thanks @tenderlove!
- (License) License version updated to Apache 2. Thanks @kytrinyx!
- (Maint) Pruned Supported Ruby Versions. We now test: 2.2.9, 2.3.6 and 2.4.3.
- (Maint) Ruby 2.5.0 testing is blocked on Travis, but should work locally. Thanks @SumLare!
- (Maint) Added Rubocop and made some corrections
## 0.1.2 (Patch/Bug Fix Release)
- Fix for regexp matching Thanks @incase! #16
- File handling cleanup Thanks @martinandert! #13
- `from_filename` actually works now! Thanks @martinandert! #12
## 0.1.0 (Minor Release)
- Port new edgecase handling from [python-path-specification](https://github.com/cpburnz/python-path-specification/pull/8). Many thanks to @jdpace! :)
- Removed EOL Ruby support
- Added current Ruby stable to Travis testing
## 0.0.2 (Patch/Bug Fix Release)
- Fixed issues with Ruby 1.8.7/2.1.1
- Added more testing scripts
- Fixed Windows path related issues
- Cleanup unnecessary things in gem
## 0.0.1
- Initial version.
pathspec-ruby-1.0.0/Gemfile 0000664 0000000 0000000 00000000047 13774751200 0015600 0 ustar 00root root 0000000 0000000 source 'https://rubygems.org'
gemspec
pathspec-ruby-1.0.0/LICENSE 0000664 0000000 0000000 00000026073 13774751200 0015321 0 ustar 00root root 0000000 0000000 Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. pathspec-ruby-1.0.0/README.md 0000664 0000000 0000000 00000005454 13774751200 0015573 0 ustar 00root root 0000000 0000000 # pathspec-ruby
[](https://badge.fury.io/rb/pathspec) [](https://travis-ci.org/highb/pathspec-ruby) [](https://codeclimate.com/github/highb/pathspec-ruby/maintainability)
[Supported Rubies](https://www.ruby-lang.org/en/downloads/):
- 2.4.6 (Maintenance)
- 2.5.6 (Stable)
- 2.6.4 (Stable)
Match Path Specifications, such as .gitignore, in Ruby!
Follows .gitignore syntax defined on [gitscm](http://git-scm.com/docs/gitignore)
.gitignore functionality ported from [Python pathspec](https://pypi.python.org/pypi/pathspec/0.2.2) by [@cpburnz](https://github.com/cpburnz/python-path-specification)
## Build/Install from Rubygems
```shell
gem install pathspec
```
## CLI Usage
```bash
➜ test-pathspec cat .gitignore
*.swp
/coverage/
➜ test-pathspec be pathspec-rb specs_match "coverage/foo"
/coverage/
➜ test-pathspec be pathspec-rb specs_match "file.swp"
*.swp
➜ test-pathspec be pathspec-rb match "file.swp"
➜ test-pathspec echo $?
0
➜ test-pathspec ls
Gemfile Gemfile.lock coverage file.swp source.rb
➜ test-pathspec be pathspec-rb tree .
./coverage
./coverage/index.html
./file.swp
```
## Usage
```ruby
require 'pathspec'
# Create a .gitignore-style Pathspec by giving it newline separated gitignore
# lines, an array of gitignore lines, or any other enumable object that will
# give strings matching the .gitignore-style (File, etc.)
gitignore = Pathspec.from_filename('spec/files/gitignore_readme')
# Our .gitignore in this example contains:
# !**/important.txt
# abc/**
# true, matches "abc/**"
gitignore.match 'abc/def.rb'
# CLI equivalent: pathspec.rb -f spec/files/gitignore_readme match 'abc/def.rb'
# false, because it has been negated using the line "!**/important.txt"
gitignore.match 'abc/important.txt'
# CLI equivalent: pathspec.rb -f spec/files/gitignore_readme match 'abc/important.txt'
# Give a path somewhere in the filesystem, and the Pathspec will return all
# matching files underneath.
# Returns ['/src/repo/abc/', '/src/repo/abc/123']
gitignore.match_tree '/src/repo'
# CLI equivalent: pathspec.rb -f spec/files/gitignore_readme tree /src/repo
# Give an enumerable of paths, and Pathspec will return the ones that match.
# Returns ['/abc/123', '/abc/']
gitignore.match_paths ['/abc/123', '/abc/important.txt', '/abc/']
# There is no CLI equivalent to this.
```
## Building/Installing from Source
```shell
git clone git@github.com:highb/pathspec-ruby.git
cd pathspec-ruby && bash ./build_from_source.sh
```
## Contributing
Pull requests, bug reports, and feature requests welcome! :smile: I've tried to write exhaustive tests but who knows what cases I've missed.
pathspec-ruby-1.0.0/Rakefile 0000664 0000000 0000000 00000001276 13774751200 0015757 0 ustar 00root root 0000000 0000000 begin
require 'rspec/core/rake_task'
RSpec::Core::RakeTask.new(:spec)
rescue LoadError
puts 'rspec rake task failed to load'
end
require 'rubocop/rake_task'
require 'kramdown'
require 'fileutils'
RuboCop::RakeTask.new(:rubocop) do |t|
t.options = ['--display-cop-names']
end
task default: %i[rubocop spec docs]
desc 'Generate man page for executable script'
task :docs do
kramdown = Kramdown::Document.new(File.read('docs/pathspec-rb.md'))
FileUtils.mkdir_p 'docs/man'
File.open('docs/man/pathspec-rb.man.1', 'w') do |f|
f.write(kramdown.to_man)
end
FileUtils.mkdir_p 'docs/html'
File.open('docs/html/pathspec-rb.html', 'w') do |f|
f.write(kramdown.to_html)
end
end
pathspec-ruby-1.0.0/bin/ 0000775 0000000 0000000 00000000000 13774751200 0015054 5 ustar 00root root 0000000 0000000 pathspec-ruby-1.0.0/bin/pathspec-rb 0000775 0000000 0000000 00000004200 13774751200 0017206 0 ustar 00root root 0000000 0000000 #!/usr/bin/env ruby
# frozen_string_literal: true
require 'optionparser'
require 'pathspec'
options = {
spec_type: :git,
spec_filename: '.gitignore'
}
optparser = OptionParser.new do |opts|
opts.banner = 'Usage: pathspec-rb [options] [subcommand] [path]
Subcommands:
specs_match: Finds all specs matching path.
tree: Finds all files under path matching the spec.
match: Checks if the path matches any spec.
EXIT STATUS:
0 Matches found.
1 No matches found.
>1 An error occured.
'
opts.on('-f', '--file FILENAME', String,
'A spec file to load. Default: .gitignore') do |filename|
unless File.readable?(filename)
puts "Error: I couldn't read #{filename}"
exit 2
end
options[:spec_filename] = filename
end
opts.on('-t', '--type [git|regex]', %i[git regex],
'Spec file type in FILENAME. Default: git. Available: git and regex.') do |type|
options[:spec_type] = type
end
opts.on('-v', '--verbose', 'Only output if there are matches.') do |_verbose|
options[:verbose] = true
end
end
optparser.parse!
command = ARGV[0]
path = ARGV[1]
if path
spec = PathSpec.from_filename(options[:spec_filename], options[:spec_type])
else
puts optparser.help
exit 2
end
case command
when 'specs_match'
if spec.match?(path)
puts "#{path} matches the following specs from #{options[:spec_filename]}:" if options[:verbose]
puts spec.specs_matching(path)
else
puts "#{path} does not match any specs from #{options[:spec_filename]}" if options[:verbose]
exit 1
end
when 'tree'
tree_matches = spec.match_tree(path)
if tree_matches.any?
puts "Files in #{path} that match #{options[:spec_filename]}" if options[:verbose]
puts tree_matches
else
puts "No file in #{path} matched #{options[:spec_filename]}" if options[:verbose]
exit 1
end
when 'match', ''
if spec.match?(path)
puts "#{path} matches a spec in #{options[:spec_filename]}" if options[:verbose]
else
puts "#{path} does not match any specs in #{options[:spec_filename]}" if options[:verbose]
exit 1
end
else
puts "Unknown sub-command #{command}."
puts optparser.help
exit 2
end
pathspec-ruby-1.0.0/docs/ 0000775 0000000 0000000 00000000000 13774751200 0015234 5 ustar 00root root 0000000 0000000 pathspec-ruby-1.0.0/docs/html/ 0000775 0000000 0000000 00000000000 13774751200 0016200 5 ustar 00root root 0000000 0000000 pathspec-ruby-1.0.0/docs/html/pathspec-rb.html 0000664 0000000 0000000 00000004416 13774751200 0021303 0 ustar 00root root 0000000 0000000
pathspec-rb(1)
NAME
pathspec - Test pathspecs against a specific path
SYNOPSIS
pathspec-rb
[OPTIONS
] [SUBCOMMAND
] [PATH
] NAME PATH
DESCRIPTION
pathspc-rb
is a tool that accompanies the pathspec-ruby library to help
you test what match results the library would find using path specs. You can
either find all specs matching a path, find all files matching specs, or
verify that a path would match any spec.
https://github.com/highb/pathspec-ruby
SUB-COMMANDS
Name |
Description |
specs_match |
Find all specs matching path |
tree |
Find all files under path matching the spec |
match |
Check if the path matches any spec |
OPTIONS
-f <FILENAME>
, --file <FILENAME>
- Load path specs from the file passed in as argument. If this option is not specified,
pathspec-rb
defaults to loading .gitignore
.
-t [git|regex]
, --type [git|regex]
- Type of spec expected in the loaded specs file (see
-f
option). Defaults to git
.
-v
, --verbose
- Only output if there are matches.
EXAMPLE
Find all files ignored by git under your source directory:
$ pathspec-rb tree src/
List all spec rules that would match for the specified path:
$ pathspec-rb specs_match build/
Check that a path matches at least one of the specs in a new version of a
gitignore file:
$ pathspec-rb match -f .gitignore.new spec/fixtures/
AUTHOR
Brandon High highb@users.noreply.github.com
Gabriel Filion
pathspec-ruby-1.0.0/docs/pathspec-rb.md 0000664 0000000 0000000 00000002733 13774751200 0017773 0 ustar 00root root 0000000 0000000 # pathspec-rb(1)
{:data-date="2020/01/04"}
## NAME
pathspec - Test pathspecs against a specific path
## SYNOPSIS
`pathspec-rb` [`OPTIONS`] [`SUBCOMMAND`] [`PATH`] NAME PATH
## DESCRIPTION
`pathspc-rb` is a tool that accompanies the pathspec-ruby library to help
you test what match results the library would find using path specs. You can
either find all specs matching a path, find all files matching specs, or
verify that a path would match any spec.
https://github.com/highb/pathspec-ruby
## SUB-COMMANDS
|-
| Name | Description
|-
| *specs_match* | Find all specs matching path
|-
| *tree* | Find all files under path matching the spec
|-
| *match* | Check if the path matches any spec
|-
## OPTIONS
`-f `, `--file `
: Load path specs from the file passed in as argument. If this option is not specified, `pathspec-rb` defaults to loading `.gitignore`.
`-t [git|regex]`, `--type [git|regex]`
: Type of spec expected in the loaded specs file (see `-f` option). Defaults to `git`.
`-v`, `--verbose`
: Only output if there are matches.
## EXAMPLE
Find all files ignored by git under your source directory:
$ pathspec-rb tree src/
List all spec rules that would match for the specified path:
$ pathspec-rb specs_match build/
Check that a path matches at least one of the specs in a new version of a
gitignore file:
$ pathspec-rb match -f .gitignore.new spec/fixtures/
## AUTHOR
Brandon High highb@users.noreply.github.com
Gabriel Filion
pathspec-ruby-1.0.0/install_from_source.sh 0000775 0000000 0000000 00000000405 13774751200 0020713 0 ustar 00root root 0000000 0000000 #!/bin/bash
bundle install
# Ensure this build is sane
bundle exec rspec spec
# Ensure there is no existing version
bundle exec gem uninstall pathspec
# Build and install!
bundle exec gem build pathspec.gemspec && bundle exec gem install pathspec-0.2.0.gem
pathspec-ruby-1.0.0/lib/ 0000775 0000000 0000000 00000000000 13774751200 0015052 5 ustar 00root root 0000000 0000000 pathspec-ruby-1.0.0/lib/pathspec.rb 0000664 0000000 0000000 00000004760 13774751200 0017215 0 ustar 00root root 0000000 0000000 require 'pathspec/gitignorespec'
require 'pathspec/regexspec'
require 'find'
require 'pathname'
# Main PathSpec class, provides interfaces to various spec implementations
class PathSpec
attr_reader :specs
def initialize(lines = nil, type = :git)
@specs = []
add(lines, type) if lines
self
end
# Check if a path matches the pathspecs described
# Returns true if there are matches and none are excluded
# Returns false if there aren't matches or none are included
def match(path)
matches = specs_matching(path.to_s)
!matches.empty? && matches.all? {|m| m.inclusive?}
end
def specs_matching(path)
@specs.select do |spec|
spec if spec.match(path)
end
end
# Check if any files in a given directory or subdirectories match the specs
# Returns matched paths or nil if no paths matched
def match_tree(root)
rootpath = Pathname.new(root)
matching = []
Find.find(root) do |path|
relpath = Pathname.new(path).relative_path_from(rootpath).to_s
relpath += '/' if File.directory? path
matching << path if match(relpath)
end
matching
end
def match_path(path, root = '/')
rootpath = Pathname.new(drive_letter_to_path(root))
relpath = Pathname.new(drive_letter_to_path(path)).relative_path_from(rootpath).to_s
relpath += '/' if path[-1].chr == '/'
match(relpath)
end
def match_paths(paths, root = '/')
matching = []
paths.each do |path|
matching << path if match_path(path, root)
end
matching
end
def drive_letter_to_path(path)
path.gsub(%r{^([a-zA-Z]):/}, '/\1/')
end
# Generate specs from a filename, such as a .gitignore
def self.from_filename(filename, type = :git)
File.open(filename, 'r') { |io| from_lines(io, type) }
end
def self.from_lines(lines, type = :git)
new lines, type
end
# Generate specs from lines of text
def add(obj, type = :git)
spec_class = spec_type(type)
if obj.respond_to?(:each_line)
obj.each_line do |l|
spec = spec_class.new(l.rstrip)
@specs << spec if !spec.regex.nil? && !spec.inclusive?.nil?
end
elsif obj.respond_to?(:each)
obj.each do |l|
add(l, type)
end
else
raise 'Cannot make Pathspec from non-string/non-enumerable object.'
end
self
end
def empty?
@specs.empty?
end
def spec_type(type)
case type
when :git
GitIgnoreSpec
when :regex
RegexSpec
else
raise "Unknown spec type #{type}"
end
end
end
pathspec-ruby-1.0.0/lib/pathspec/ 0000775 0000000 0000000 00000000000 13774751200 0016661 5 ustar 00root root 0000000 0000000 pathspec-ruby-1.0.0/lib/pathspec/gitignorespec.rb 0000664 0000000 0000000 00000022076 13774751200 0022057 0 ustar 00root root 0000000 0000000 require 'pathspec/regexspec'
class PathSpec
# Class for parsing a .gitignore spec
class GitIgnoreSpec < RegexSpec
attr_reader :regex, :pattern
def initialize(original_pattern) # rubocop:disable Metrics/CyclomaticComplexity
pattern = original_pattern.strip unless original_pattern.nil?
# A pattern starting with a hash ('#') serves as a comment
# (neither includes nor excludes files). Escape the hash with a
# back-slash to match a literal hash (i.e., '\#').
if pattern.start_with?('#')
@regex = nil
@inclusive = nil
# A blank pattern is a null-operation (neither includes nor
# excludes files).
elsif pattern.empty? # rubocop:disable Lint/DuplicateBranch
@regex = nil
@inclusive = nil
# Patterns containing three or more consecutive stars are invalid and
# will be ignored.
elsif /\*\*\*+/.match?(pattern) # rubocop:disable Lint/DuplicateBranch
@regex = nil
@inclusive = nil
# EDGE CASE: According to git check-ignore (v2.4.1)), a single '/'
# does not match any file
elsif pattern == '/' # rubocop:disable Lint/DuplicateBranch
@regex = nil
@inclusive = nil
# We have a valid pattern!
else
# A pattern starting with an exclamation mark ('!') negates the
# pattern (exclude instead of include). Escape the exclamation
# mark with a back-slash to match a literal exclamation mark
# (i.e., '\!').
if pattern.start_with?('!')
@inclusive = false
# Remove leading exclamation mark.
pattern = pattern[1..]
else
@inclusive = true
end
# Remove leading back-slash escape for escaped hash ('#') or
# exclamation mark ('!').
pattern = pattern[1..] if pattern.start_with?('\\')
# Split pattern into segments. -1 to allow trailing slashes.
pattern_segs = pattern.split('/', -1)
# Normalize pattern to make processing easier.
# A pattern beginning with a slash ('/') will only match paths
# directly on the root directory instead of any descendant
# paths. So, remove empty first segment to make pattern relative
# to root.
if pattern_segs[0].empty?
pattern_segs.shift
elsif pattern_segs.length == 1 ||
pattern_segs.length == 2 && pattern_segs[-1].empty?
# A pattern without a beginning slash ('/') will match any
# descendant path. This is equivilent to "**/{pattern}". So,
# prepend with double-asterisks to make pattern relative to
# root.
# EDGE CASE: This also holds for a single pattern with a
# trailing slash (e.g. dir/).
pattern_segs.insert(0, '**') if pattern_segs[0] != '**'
end
# A pattern ending with a slash ('/') will match all descendant
# paths of if it is a directory but not if it is a regular file.
# This is equivilent to "{pattern}/**". So, set last segment to
# double asterisks to include all descendants.
pattern_segs[-1] = '**' if pattern_segs[-1].empty? && pattern_segs.length > 1
# Handle platforms with backslash separated paths
path_sep = if File::SEPARATOR == '\\'
'\\\\'
else
'/'
end
# Build regular expression from pattern.
regex = '^'
need_slash = false
regex_end = pattern_segs.size - 1
pattern_segs.each_index do |i|
seg = pattern_segs[i]
case seg
when '**'
# A pattern consisting solely of double-asterisks ('**')
# will match every path.
if i == 0 && i == regex_end
regex.concat('.+')
# A normalized pattern beginning with double-asterisks
# ('**') will match any leading path segments.
elsif i == 0
regex.concat("(?:.+#{path_sep})?")
need_slash = false
# A normalized pattern ending with double-asterisks ('**')
# will match any trailing path segments.
elsif i == regex_end
regex.concat("#{path_sep}.*")
# A pattern with inner double-asterisks ('**') will match
# multiple (or zero) inner path segments.
else
regex.concat("(?:#{path_sep}.+)?")
need_slash = true
end
# Match single path segment.
when '*'
regex.concat(path_sep) if need_slash
regex.concat("[^#{path_sep}]+")
need_slash = true
else
# Match segment glob pattern.
regex.concat(path_sep) if need_slash
regex.concat(translate_segment_glob(seg))
if i == regex_end && @inclusive
# A pattern ending without a slash ('/') will match a file
# or a directory (with paths underneath it).
# e.g. foo matches: foo, foo/bar, foo/bar/baz, etc.
# EDGE CASE: However, this does not hold for exclusion cases
# according to `git check-ignore` (v2.4.1).
regex.concat("(?:#{path_sep}.*)?")
end
need_slash = true
end
end
regex.concat('$')
super(regex)
# Copy original pattern
@pattern = original_pattern.dup
end
end
def translate_segment_glob(pattern)
''"
Translates the glob pattern to a regular expression. This is used in
the constructor to translate a path segment glob pattern to its
corresponding regular expression.
*pattern* (``str``) is the glob pattern.
Returns the regular expression (``str``).
"''
# NOTE: This is derived from `fnmatch.translate()` and is similar to
# the POSIX function `fnmatch()` with the `FNM_PATHNAME` flag set.
escape = false
regex = ''
i = 0
while i < pattern.size
# Get next character.
char = pattern[i].chr
i += 1
# Escape the character.
if escape
escape = false
regex += Regexp.escape(char)
# Escape character, escape next character.
elsif char == '\\'
escape = true
# Multi-character wildcard. Match any string (except slashes),
# including an empty string.
elsif char == '*'
regex += '[^/]*'
# Single-character wildcard. Match any single character (except
# a slash).
elsif char == '?'
regex += '[^/]'
# Braket expression wildcard. Except for the beginning
# exclamation mark, the whole braket expression can be used
# directly as regex but we have to find where the expression
# ends.
# - "[][!]" matchs ']', '[' and '!'.
# - "[]-]" matchs ']' and '-'.
# - "[!]a-]" matchs any character except ']', 'a' and '-'.
elsif char == '['
j = i
# Pass brack expression negation.
j += 1 if j < pattern.size && pattern[j].chr == '!'
# Pass first closing braket if it is at the beginning of the
# expression.
j += 1 if j < pattern.size && pattern[j].chr == ']'
# Find closing braket. Stop once we reach the end or find it.
j += 1 while j < pattern.size && pattern[j].chr != ']'
if j < pattern.size
expr = '['
# Braket expression needs to be negated.
case pattern[i].chr
when '!'
expr += '^'
i += 1
# POSIX declares that the regex braket expression negation
# "[^...]" is undefined in a glob pattern. Python's
# `fnmatch.translate()` escapes the caret ('^') as a
# literal. To maintain consistency with undefined behavior,
# I am escaping the '^' as well.
when '^'
expr += '\\^'
i += 1
end
# Escape brackets contained within pattern
if pattern[i].chr == ']' && i != j
expr += '\]'
i += 1
end
# Build regex braket expression. Escape slashes so they are
# treated as literal slashes by regex as defined by POSIX.
expr += pattern[i..j].sub('\\', '\\\\')
# Add regex braket expression to regex result.
regex += expr
# Found end of braket expression. Increment j to be one past
# the closing braket:
#
# [...]
# ^ ^
# i j
#
j += 1
# Set i to one past the closing braket.
i = j
# Failed to find closing braket, treat opening braket as a
# braket literal instead of as an expression.
else
regex += '\['
end
# Regular character, escape it for regex.
else
regex << Regexp.escape(char)
end
end
regex
end
def inclusive?
@inclusive
end
end
end
pathspec-ruby-1.0.0/lib/pathspec/regexspec.rb 0000664 0000000 0000000 00000000471 13774751200 0021175 0 ustar 00root root 0000000 0000000 require 'pathspec/spec'
class PathSpec
# Simple regex-based spec
class RegexSpec < Spec
def initialize(pattern)
@pattern = pattern.dup
@regex = Regexp.compile pattern
super
end
def inclusive?
true
end
def match(path)
@regex&.match(path)
end
end
end
pathspec-ruby-1.0.0/lib/pathspec/spec.rb 0000664 0000000 0000000 00000000374 13774751200 0020144 0 ustar 00root root 0000000 0000000 class PathSpec
# Abstract spec
class Spec
attr_reader :regex, :pattern
def initialize(*_); end
def match(files)
raise 'Unimplemented'
end
def inclusive?
true
end
def to_s
@pattern
end
end
end
pathspec-ruby-1.0.0/pathspec.gemspec 0000664 0000000 0000000 00000002063 13774751200 0017461 0 ustar 00root root 0000000 0000000 lib = File.expand_path('lib', __dir__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
Gem::Specification.new do |s|
s.name = 'pathspec'
s.version = '1.0.0'
s.date = '2018-01-11'
s.summary = 'PathSpec: for matching path patterns'
s.description = 'Use to match path patterns such as gitignore'
s.authors = ['Brandon High']
s.email = 'highb@users.noreply.github.com'
s.files = Dir.glob('{lib,spec,docs}/**/*') + %w[LICENSE README.md CHANGELOG.md]
s.bindir = 'bin'
s.executables << 'pathspec-rb'
s.test_files = s.files.grep(%r{^spec/})
s.require_paths = ['lib']
s.homepage = 'https://github.com/highb/pathspec-ruby'
s.license = 'Apache-2.0'
s.required_ruby_version = '>= 2.6.0'
s.add_development_dependency 'bundler', '~> 2.2'
s.add_development_dependency 'fakefs', '~> 1.3'
s.add_development_dependency 'kramdown', '~> 2.3'
s.add_development_dependency 'rake', '~> 13.0'
s.add_development_dependency 'rspec', '~> 3.10'
s.add_development_dependency 'rubocop', '~> 1.7'
s.add_development_dependency 'simplecov', '~> 0.21'
end
pathspec-ruby-1.0.0/rspec_all_versions.sh 0000775 0000000 0000000 00000000634 13774751200 0020542 0 ustar 00root root 0000000 0000000 #!/bin/bash
function testversion {
echo Testing Ruby $1
rbenv install -s $1
rbenv local $1
gem install bundler --quiet
bundle install --quiet
bundle exec rspec
if [ $? -eq 0 ]; then
echo -e "\033[32mSuccess testing Ruby $1\033[0m"
else
echo -e "\033[31mFailed testing Ruby $1 Exit code was $?\033[0m"
fi
echo
}
for VERSION in 2.2.9 2.3.6 2.4.3 2.5.0; do
testversion $VERSION
done
pathspec-ruby-1.0.0/spec/ 0000775 0000000 0000000 00000000000 13774751200 0015236 5 ustar 00root root 0000000 0000000 pathspec-ruby-1.0.0/spec/files/ 0000775 0000000 0000000 00000000000 13774751200 0016340 5 ustar 00root root 0000000 0000000 pathspec-ruby-1.0.0/spec/files/gitignore_readme 0000664 0000000 0000000 00000000030 13774751200 0021560 0 ustar 00root root 0000000 0000000 !**/important.txt
abc/** pathspec-ruby-1.0.0/spec/files/gitignore_ruby 0000664 0000000 0000000 00000002143 13774751200 0021313 0 ustar 00root root 0000000 0000000 # Source: https://github.com/github/gitignore/blob/master/Ruby.gitignore
*.gem
*.rbc
/.config
/coverage/
/InstalledFiles
/pkg/
/spec/reports/
/spec/examples.txt
/test/tmp/
/test/version_tmp/
/tmp/
# Used by dotenv library to load environment variables.
# .env
## Specific to RubyMotion:
.dat*
.repl_history
build/
*.bridgesupport
build-iPhoneOS/
build-iPhoneSimulator/
## Specific to RubyMotion (use of CocoaPods):
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
# vendor/Pods/
## Documentation cache and generated files:
/.yardoc/
/_yardoc/
/doc/
/rdoc/
## Environment normalization:
/.bundle/
/vendor/bundle
/lib/bundler/man/
# for a library or gem, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# Gemfile.lock
# .ruby-version
# .ruby-gemset
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
.rvmrc
pathspec-ruby-1.0.0/spec/files/gitignore_simple 0000664 0000000 0000000 00000000005 13774751200 0021616 0 ustar 00root root 0000000 0000000 *.md
pathspec-ruby-1.0.0/spec/files/regex_simple 0000664 0000000 0000000 00000000007 13774751200 0020743 0 ustar 00root root 0000000 0000000 .*\.md
pathspec-ruby-1.0.0/spec/spec_helper.rb 0000664 0000000 0000000 00000000226 13774751200 0020054 0 ustar 00root root 0000000 0000000 begin
require 'simplecov'
SimpleCov.start
rescue StandardError
puts 'SimpleCov failed to start, most likely this due to running Ruby 1.8.7'
end
pathspec-ruby-1.0.0/spec/unit/ 0000775 0000000 0000000 00000000000 13774751200 0016215 5 ustar 00root root 0000000 0000000 pathspec-ruby-1.0.0/spec/unit/pathspec/ 0000775 0000000 0000000 00000000000 13774751200 0020024 5 ustar 00root root 0000000 0000000 pathspec-ruby-1.0.0/spec/unit/pathspec/gitignorespec_spec.rb 0000664 0000000 0000000 00000027654 13774751200 0024243 0 ustar 00root root 0000000 0000000 require 'spec_helper'
require 'pathspec/gitignorespec'
describe PathSpec::GitIgnoreSpec do
# Original specification by http://git-scm.com/docs/gitignore
# A blank line matches no files, so it can serve as a separator for
# readability.
describe 'does nothing for newlines' do
subject { PathSpec::GitIgnoreSpec.new "\n" }
it { is_expected.to_not match('foo.tmp') }
it { is_expected.to_not match(' ') }
it { is_expected.to_not be_inclusive }
end
describe 'does nothing for blank strings' do
subject { PathSpec::GitIgnoreSpec.new '' }
it { is_expected.to_not match 'foo.tmp' }
it { is_expected.to_not match ' ' }
it { is_expected.to_not be_inclusive }
end
# A line starting with # serves as a comment. Put a backslash ("\") in front
# of the first hash for patterns that begin with a hash.
describe 'does nothing for comments' do
subject { PathSpec::GitIgnoreSpec.new '# this is a gitignore style comment' }
it { is_expected.to_not match('foo.tmp') }
it { is_expected.to_not match(' ') }
it { is_expected.to_not be_inclusive }
end
describe 'ignores comment char with a slash' do
subject { PathSpec::GitIgnoreSpec.new '\#averystrangefile' }
it { is_expected.to match('#averystrangefile') }
it { is_expected.to_not match('foobar') }
it { is_expected.to be_inclusive }
end
describe 'escapes characters with slashes' do
subject { PathSpec::GitIgnoreSpec.new 'twinkletwinkle\*' }
it { is_expected.to match('twinkletwinkle*') }
it { is_expected.to_not match('twinkletwinkletwinkle') }
it { is_expected.to be_inclusive }
end
# Trailing spaces are ignored unless they are quoted with backlash ("\").
describe 'ignores trailing spaces' do
subject { PathSpec::GitIgnoreSpec.new 'foo ' }
it { is_expected.to match('foo') }
it { is_expected.to_not match('foo ') }
it { is_expected.to be_inclusive }
end
# This is not handled properly yet
describe 'does not ignore escaped trailing spaces'
# An optional prefix "!" which negates the pattern; any matching file excluded
# by a previous pattern will become included again. It is not possible to
# re-include a file if a parent directory of that file is excluded. Git
# doesn't list excluded directories for performance reasons, so any patterns
# on contained files have no effect, no matter where they are defined. Put a
# backslash ("\") in front of the first "!" for patterns that begin with a
# literal "!", for example, "\!important!.txt".
describe 'is exclusive of !' do
subject { PathSpec::GitIgnoreSpec.new '!important.txt' }
it { is_expected.to match('important.txt') }
it { is_expected.to_not be_inclusive }
it { is_expected.to_not match('!important.txt') }
end
# If the pattern ends with a slash, it is removed for the purpose of the
# following description, but it would only find a match with a directory. In
# other words, foo/ will match a directory foo and paths underneath it, but
# will not match a regular file or a symbolic link foo (this is consistent
# with the way how pathspec works in general in Git).
describe 'trailing slashes match directories and their contents but not regular files or symlinks' do
subject { PathSpec::GitIgnoreSpec.new 'foo/' }
it { is_expected.to match('foo/') }
it { is_expected.to match('foo/bar') }
it { is_expected.to match('baz/foo/bar') }
it { is_expected.to_not match('foo') }
it { is_expected.to be_inclusive }
end
# If the pattern does not contain a slash '/', Git treats it as a shell glob
# pattern and checks for a match against the pathname relative to the location
# of the .gitignore file (relative to the toplevel of the work tree if not
# from a .gitignore file).
describe 'handles basic globbing' do
subject { PathSpec::GitIgnoreSpec.new '*.tmp' }
it { is_expected.to match('foo.tmp') }
it { is_expected.to match('foo/bar.tmp') }
it { is_expected.to match('foo/bar.tmp/baz') }
it { is_expected.to_not match('foo.rb') }
it { is_expected.to be_inclusive }
end
describe 'handles inner globs' do
subject { PathSpec::GitIgnoreSpec.new 'foo-*-bar' }
it { is_expected.to match('foo--bar') }
it { is_expected.to match('foo-hello-bar') }
it { is_expected.to match('a/foo-hello-bar') }
it { is_expected.to match('foo-hello-bar/b') }
it { is_expected.to match('a/foo-hello-bar/b') }
it { is_expected.to_not match('foo.tmp') }
end
describe 'handles postfix globs' do
subject { PathSpec::GitIgnoreSpec.new '~temp-*' }
it { is_expected.to match('~temp-') }
it { is_expected.to match('~temp-foo') }
it { is_expected.to match('foo/~temp-bar') }
it { is_expected.to match('foo/~temp-bar/baz') }
it { is_expected.to_not match('~temp') }
end
describe 'handles multiple globs' do
subject { PathSpec::GitIgnoreSpec.new '*.middle.*' }
it { is_expected.to match('hello.middle.rb') }
it { is_expected.to_not match('foo.rb') }
it { is_expected.to be_inclusive }
end
describe 'handles dir globs' do
subject { PathSpec::GitIgnoreSpec.new 'dir/*' }
it { is_expected.to match('dir/foo') }
it { is_expected.to_not match('foo/') }
it { is_expected.to be_inclusive }
end
# Otherwise, Git treats the pattern as a shell glob suitable for consumption
# by fnmatch(3) with the FNM_PATHNAME flag: wildcards in the pattern will not
# match a / in the pathname. For example, "Documentation/*.html" matches
# "Documentation/git.html" but not "Documentation/ppc/ppc.html" or
# "tools/perf/Documentation/perf.html".
describe 'handles dir globs' do
subject { PathSpec::GitIgnoreSpec.new 'dir/*' }
it { is_expected.to match('dir/foo') }
it { is_expected.to_not match('foo/') }
it { is_expected.to be_inclusive }
end
describe 'handles globs inside of dirs' do
subject { PathSpec::GitIgnoreSpec.new 'Documentation/*.html' }
it { is_expected.to match('Documentation/git.html') }
it { is_expected.to_not match('Documentation/ppc/ppc.html') }
it { is_expected.to_not match('tools/perf/Documentation/perf.html') } # TODO: Or is it? Git 2 weirdness?
it { is_expected.to be_inclusive }
end
describe 'handles wildcards' do
subject { PathSpec::GitIgnoreSpec.new 'jokeris????' }
it { is_expected.to match('jokeriswild') }
it { is_expected.to_not match('jokerisfat') }
it { is_expected.to be_inclusive }
end
describe 'handles brackets' do
subject { PathSpec::GitIgnoreSpec.new '*[eu][xl]*' }
it { is_expected.to match('youknowregex') }
it { is_expected.to match('youknowregularexpressions') }
it { is_expected.to_not match('youknownothing') }
it { is_expected.to be_inclusive }
end
describe 'handles unmatched brackets' do
subject { PathSpec::GitIgnoreSpec.new '*[*[*' }
it { is_expected.to match('bracket[oh[wow') }
it { is_expected.to be_inclusive }
end
describe 'handles brackets with carats' do
subject { PathSpec::GitIgnoreSpec.new '*[^]' }
it { is_expected.to match('myfavorite^') }
it { is_expected.to be_inclusive }
end
describe 'handles brackets for brackets' do
subject { PathSpec::GitIgnoreSpec.new '*[]]' }
it { is_expected.to match('yodawg[]]') }
it { is_expected.to be_inclusive }
end
describe 'handles brackets with escaped characters' do
# subject { GitIgnoreSpec.new 'back[\\]slash' }
# it { is_expected.to match('back\\slash') }
# it { is_expected.to_not match('back\\\\slash') }
# it { is_expected.to be_inclusive }
end
describe 'handles negated brackets' do
subject { PathSpec::GitIgnoreSpec.new 'ab[!cd]ef' }
it { is_expected.to_not match('abcef') }
it { is_expected.to match('abzef') }
it { is_expected.to be_inclusive }
end
# A leading slash matches the beginning of the pathname. For example, "/*.c"
# matches "cat-file.c" but not "mozilla-sha1/sha1.c".
describe 'handles leading / as relative to base directory' do
subject { PathSpec::GitIgnoreSpec.new '/*.c' }
it { is_expected.to match('cat-file.c') }
it { is_expected.to_not match('mozilla-sha1/sha1.c') }
it { is_expected.to be_inclusive }
end
describe 'handles simple single paths' do
subject { PathSpec::GitIgnoreSpec.new 'spam' }
it { is_expected.to match('spam') }
it { is_expected.to match('spam/') }
it { is_expected.to match('foo/spam') }
it { is_expected.to match('spam/foo') }
it { is_expected.to match('foo/spam/bar') }
it { is_expected.to_not match('foo') }
end
# Two consecutive asterisks ("**") in patterns matched against full pathname
# may have special meaning:
# A leading "**" followed by a slash means match in all directories. For
# example, "**/foo" matches file or directory "foo" anywhere, the same as
# pattern "foo". "**/foo/bar" matches file or directory "bar" anywhere that is
# directly under directory "foo".
describe 'handles prefixed ** as searching any location' do
subject { PathSpec::GitIgnoreSpec.new '**/foo' }
it { is_expected.to match('foo') }
it { is_expected.to match('bar/foo') }
it { is_expected.to match('baz/bar/foo') }
it { is_expected.to_not match('baz/bar/foo.rb') }
it { is_expected.to be_inclusive }
end
describe 'handles prefixed ** with a directory as searching a file under a directory in any location' do
subject { PathSpec::GitIgnoreSpec.new '**/foo/bar' }
it { is_expected.to_not match('foo') }
it { is_expected.to match('foo/bar') }
it { is_expected.to match('baz/foo/bar') }
it { is_expected.to match('baz/foo/bar/sub') }
it { is_expected.to_not match('baz/foo/bar.rb') }
it { is_expected.to_not match('baz/bananafoo/bar') }
it { is_expected.to be_inclusive }
end
# A trailing "/**" matches everything inside. For example, "abc/**" matches
# all files inside directory "abc", relative to the location of the .gitignore
# file, with infinite depth.
describe 'handles leading /** as all files inside a directory' do
subject { PathSpec::GitIgnoreSpec.new 'abc/**' }
it { is_expected.to match('abc/') }
it { is_expected.to match('abc/def') }
it { is_expected.to_not match('123/abc/def') }
it { is_expected.to_not match('123/456/abc/') }
it { is_expected.to be_inclusive }
end
# A slash followed by two consecutive asterisks then a slash matches zero or
# more directories. For example, "a/**/b" matches "a/b", "a/x/b", "a/x/y/b"
# and so on.
describe 'handles /** in the middle of a path' do
subject { PathSpec::GitIgnoreSpec.new 'a/**/b' }
it { is_expected.to match('a/b') }
it { is_expected.to match('a/x/b') }
it { is_expected.to match('a/x/y/b') }
it { is_expected.to_not match('123/a/b') }
it { is_expected.to_not match('123/a/x/b') }
it { is_expected.to be_inclusive }
end
describe 'matches all paths when given **' do
subject { PathSpec::GitIgnoreSpec.new '**' }
it { is_expected.to match('a/b') }
it { is_expected.to match('a/x/b') }
it { is_expected.to match('a/x/y/b') }
it { is_expected.to match('123/a/b') }
it { is_expected.to match('123/a/x/b') }
end
# Other consecutive asterisks are considered invalid.
describe 'considers other consecutive asterisks invalid' do
subject { PathSpec::GitIgnoreSpec.new 'a/***/b' }
it { is_expected.to_not match('a/b') }
it { is_expected.to_not match('a/x/b') }
it { is_expected.to_not match('a/x/y/b') }
it { is_expected.to_not match('123/a/b') }
it { is_expected.to_not match('123/a/x/b') }
it { is_expected.to_not be_inclusive }
end
describe 'does not match single absolute paths' do
subject { PathSpec::GitIgnoreSpec.new '/' }
it { is_expected.to_not match('foo.tmp') }
it { is_expected.to_not match(' ') }
it { is_expected.to_not match('a/b') }
end
describe 'nested paths are relative to the file' do
subject { PathSpec::GitIgnoreSpec.new 'foo/spam' }
it { is_expected.to match('foo/spam') }
it { is_expected.to match('foo/spam/bar') }
it { is_expected.to_not match('bar/foo/spam') }
end
end
pathspec-ruby-1.0.0/spec/unit/pathspec/spec_spec.rb 0000664 0000000 0000000 00000000406 13774751200 0022315 0 ustar 00root root 0000000 0000000 require 'spec_helper'
require 'pathspec/spec'
describe PathSpec::Spec do
subject { PathSpec::Spec.new }
it 'does not allow matching' do
expect { subject.match 'anything' }.to raise_error(/Unimplemented/)
end
it { is_expected.to be_inclusive }
end
pathspec-ruby-1.0.0/spec/unit/pathspec_spec.rb 0000664 0000000 0000000 00000023305 13774751200 0021366 0 ustar 00root root 0000000 0000000 require 'spec_helper'
require 'fakefs/safe'
require 'pathspec'
require 'fakefs/spec_helpers'
describe PathSpec do
shared_examples 'standard gitignore negation' do
it { is_expected.not_to match('important.txt') }
it { is_expected.not_to match('abc/important.txt') }
it { is_expected.to match('bar/baz/') }
it { is_expected.to match('foo/file') }
it { is_expected.not_to match('foo/important.txt') }
it { is_expected.to match('foo/subdir/file') }
end
# Specs that should be kept up to date with the README
context 'README.md' do
subject { PathSpec.from_filename 'spec/files/gitignore_readme' }
it { is_expected.to match('abc/def.rb') }
it { is_expected.not_to match('abc/important.txt') }
it do
expect(subject.match_paths(['/abc/123', '/abc/important.txt', '/abc/'])).to contain_exactly(
'/abc/123',
'/abc/'
)
end
end
context 'initialization' do
context 'from multilines' do
context '#new' do
subject {
PathSpec.new <<-IGNORELINES
!important.txt
foo/**
/bar/baz
IGNORELINES
}
it_behaves_like 'standard gitignore negation'
end
end
context 'from a string with no newlines' do
let(:str) { 'foo/**' }
context '#new' do
subject { PathSpec.new str }
it { is_expected.to match('foo/important.txt') }
it { is_expected.to match('foo/bar/') }
end
end
context 'from a non-string/non-enumerable' do
it 'throws an exception' do
expect { PathSpec.new Object.new }.to raise_error(/Cannot make Pathspec/)
end
end
context 'from array of gitignore strings' do
let(:arr) { ['!important.txt', 'foo/**', '/bar/baz'] }
context '#new' do
subject { PathSpec.new arr }
it_behaves_like 'standard gitignore negation'
end
context '#from_lines' do
subject {
PathSpec.from_lines(arr)
}
it_behaves_like 'standard gitignore negation'
end
context '#add array' do
subject {
ps = PathSpec.new []
ps.add arr
}
it_behaves_like 'standard gitignore negation'
end
end
context 'from linedelimited gitignore string' do
let(:line) { "!important.txt\nfoo/**\n/bar/baz\n" }
context '#new' do
subject { PathSpec.new line }
it_behaves_like 'standard gitignore negation'
end
context '#from_lines' do
subject {
PathSpec.from_lines(line)
}
it_behaves_like 'standard gitignore negation'
end
context '#add' do
subject {
ps = PathSpec.new
ps.add line
}
it_behaves_like 'standard gitignore negation'
end
end
context 'from a gitignore file' do
include FakeFS::SpecHelpers
let(:filename) { '.gitignore' }
before(:each) do
file = File.open(filename, 'w') { |f|
f << "!important.txt\n"
f << "foo/**\n"
f << "/bar/baz\n"
}
end
context '#new' do
subject {
PathSpec.new File.open(filename, 'r')
}
it_behaves_like 'standard gitignore negation'
end
context '#from_filename' do
subject {
PathSpec.from_filename(filename)
}
it_behaves_like 'standard gitignore negation'
end
end
context 'from multiple gitignore files' do
include FakeFS::SpecHelpers
let(:filenames) { ['.gitignore', '.otherignore'] }
before(:each) do
file = File.open('.gitignore', 'w') { |f|
f << "!important.txt\n"
f << "foo/**\n"
f << "/bar/baz\n"
}
file = File.open('.otherignore', 'w') { |f|
f << "ban*na\n"
f << "!banana\n"
}
end
context '#new' do
subject {
arr = filenames.collect { |f| File.open(f, 'r') }
PathSpec.new arr
}
it_behaves_like 'standard gitignore negation'
it { is_expected.to_not match('banana') }
it { is_expected.to match('banananananana') }
end
context '#add' do
subject {
arr = filenames.collect { |f| File.open(f, 'r') }
ps = PathSpec.new
ps.add arr
}
it_behaves_like 'standard gitignore negation'
it { is_expected.to_not match('banana') }
it { is_expected.to match('banananananana') }
end
end
end
context '#match_tree' do
include FakeFS::SpecHelpers
context 'unix' do
let(:root) {'/tmp/project'}
let(:gitignore) {
<<-GITIGNORE
!**/important.txt
abc/**
GITIGNORE
}
before(:each) {
FileUtils.mkdir_p root
FileUtils.mkdir_p "#{root}/abc"
FileUtils.touch "#{root}/abc/1"
FileUtils.touch "#{root}/abc/2"
FileUtils.touch "#{root}/abc/important.txt"
}
subject {
PathSpec.new(gitignore).match_tree(root)
}
it { is_expected.to include "#{root}/abc".to_s }
it { is_expected.to include "#{root}/abc/1".to_s }
it { is_expected.not_to include "#{root}/abc/important.txt".to_s }
it { is_expected.not_to include root.to_s.to_s }
end
context 'windows' do
let(:root) {'C:/project'}
let(:gitignore) {
<<-GITIGNORE
!**/important.txt
abc/**
GITIGNORE
}
before(:each) {
FileUtils.mkdir_p root
FileUtils.mkdir_p "#{root}/abc"
FileUtils.touch "#{root}/abc/1"
FileUtils.touch "#{root}/abc/2"
FileUtils.touch "#{root}/abc/important.txt"
}
subject {
PathSpec.new(gitignore).match_tree(root)
}
it { is_expected.to include "#{root}/abc".to_s }
it { is_expected.to include "#{root}/abc/1".to_s }
it { is_expected.not_to include "#{root}/abc/important.txt".to_s }
it { is_expected.not_to include root.to_s.to_s }
end
end
context '#match_paths' do
let(:gitignore) {
<<-GITIGNORE
!**/important.txt
/abc/**
GITIGNORE
}
context 'with no root arg' do
subject { PathSpec.new(gitignore).match_paths(['/abc/important.txt', '/abc/', '/abc/1']) }
it { is_expected.to include '/abc/' }
it { is_expected.to include '/abc/1' }
it { is_expected.not_to include '/abc/important.txt' }
end
context 'relative to non-root dir' do
subject {
PathSpec.new(gitignore).match_paths([
'/def/abc/important.txt',
'/def/abc/',
'/def/abc/1'
], '/def') }
it { is_expected.to include '/def/abc/' }
it { is_expected.to include '/def/abc/1' }
it { is_expected.not_to include '/def/abc/important.txt' }
end
context 'relative to windows drive letter' do
subject {
PathSpec.new(gitignore).match_paths([
'C:/def/abc/important.txt',
'C:/def/abc/',
'C:/def/abc/1'
], 'C:/def/') }
it { is_expected.to include 'C:/def/abc/' }
it { is_expected.to include 'C:/def/abc/1' }
it { is_expected.not_to include 'C:/def/abc/important.txt' }
end
end
# Example to exclude everything except a specific directory foo/bar (note
# the /* - without the slash, the wildcard would also exclude everything
# within foo/bar): (from git-scm.com)
context 'very specific gitignore' do
let(:gitignore) {
<<-GITIGNORE
# exclude everything except directory foo/bar
/*
!/foo
/foo/*
!/foo/bar
GITIGNORE
}
subject { PathSpec.new(gitignore) }
it { is_expected.not_to match('foo/bar') }
it { is_expected.to match('anything') }
it { is_expected.to match('foo/otherthing') }
end
context '#empty' do
let(:gitignore) {
<<-GITIGNORE
# A comment
GITIGNORE
}
subject { PathSpec.new gitignore }
it 'is empty when there are no valid lines' do
expect(subject.empty?).to be true
end
end
context 'regex file' do
let(:regexfile) {
<<-REGEX
ab*a
REGEX
}
subject { PathSpec.new regexfile, :regex}
it 'matches the regex' do
expect(subject.match('anna')).to be false
expect(subject.match('abba')).to be true
end
context '#from_filename' do
it 'forwards the type argument' do
io = double
expect(File).to receive(:open).and_yield(io)
expect(PathSpec).to receive(:from_lines).with(io, :regex)
PathSpec.from_filename '/some/file', :regex
end
it 'reads a simple regex file' do
spec = PathSpec.from_filename 'spec/files/regex_simple', :regex
expect(spec.match('artifact.md')).to be true
expect(spec.match('code.rb')).to be false
end
it 'reads a simple gitignore file' do
spec = PathSpec.from_filename 'spec/files/gitignore_simple', :git
expect(spec.match('artifact.md')).to be true
expect(spec.match('code.rb')).to be false
end
it 'reads an example ruby gitignore file' do
spec = PathSpec.from_filename 'spec/files/gitignore_ruby', :git
expect(spec.match('coverage/')).to be true
expect(spec.match('coverage/index.html')).to be true
expect(spec.match('pathspec-0.0.1.gem')).to be true
expect(spec.match('lib/pathspec')).to be false
expect(spec.match('Gemfile')).to be false
end
end
end
context 'unsuppored spec type' do
let(:file) {
<<-REGEX
This is some kind of nonsense.
REGEX
}
it 'does not allow an unknown spec type' do
expect { PathSpec.new file, :foo}.to raise_error(/Unknown/)
end
end
end