pax_global_header 0000666 0000000 0000000 00000000064 14605360331 0014513 g ustar 00root root 0000000 0000000 52 comment=ed1437d66b09ea8c7e13443fa109cf4d4e123008
pathspec-ruby-2.1.0/ 0000775 0000000 0000000 00000000000 14605360331 0014301 5 ustar 00root root 0000000 0000000 pathspec-ruby-2.1.0/.github/ 0000775 0000000 0000000 00000000000 14605360331 0015641 5 ustar 00root root 0000000 0000000 pathspec-ruby-2.1.0/.github/workflows/ 0000775 0000000 0000000 00000000000 14605360331 0017676 5 ustar 00root root 0000000 0000000 pathspec-ruby-2.1.0/.github/workflows/gem-push.yml 0000664 0000000 0000000 00000001572 14605360331 0022153 0 ustar 00root root 0000000 0000000 name: Ruby Gem
on:
push:
tags:
- '*'
workflow_dispatch:
jobs:
build:
name: Build + Publish
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Set up Ruby 3.1
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.1'
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
- name: Generate man page
run: bundle exec rake docs
- name: Publish to RubyGems
run: |
mkdir -p $HOME/.gem
touch $HOME/.gem/credentials
chmod 0600 $HOME/.gem/credentials
printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
gem build *.gemspec
gem push *.gem
env:
GEM_HOST_API_KEY: "${{secrets.RUBYGEMS_AUTH_TOKEN}}"
pathspec-ruby-2.1.0/.github/workflows/ruby.yml 0000664 0000000 0000000 00000002720 14605360331 0021403 0 ustar 00root root 0000000 0000000 # This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
# This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
# For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
name: Ruby
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
ruby-version: ['3.1', '3.2', '3.3']
steps:
- uses: actions/checkout@v4
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby-version }}
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
- name: Gem Build
run: gem build pathspec.gemspec
- name: Gem Install
run: gem install ./pathspec*.gem
test:
runs-on: ubuntu-latest
strategy:
matrix:
ruby-version: ['3.1', '3.2', '3.3']
raketasks: ['rubocop', 'spec', 'docs']
steps:
- uses: actions/checkout@v4
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby-version }}
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
- name: Rake Tasks
env:
TASK: ${{matrix.raketasks}}
run: bundle exec rake $TASK
pathspec-ruby-2.1.0/.gitignore 0000664 0000000 0000000 00000001274 14605360331 0016275 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-2.1.0/.rubocop.yml 0000664 0000000 0000000 00000000462 14605360331 0016555 0 ustar 00root root 0000000 0000000 inherit_from: .rubocop_todo.yml
AllCops:
TargetRubyVersion: 3.1
NewCops: enable
Style/NumericPredicate:
Enabled: false
Layout/ClosingHeredocIndentation:
Enabled: false
# This rule is silly
# https://github.com/rubocop/rubocop/issues/10080
Lint/AmbiguousOperatorPrecedence:
Enabled: false
pathspec-ruby-2.1.0/.rubocop_todo.yml 0000664 0000000 0000000 00000007151 14605360331 0017604 0 ustar 00root root 0000000 0000000 # This configuration was generated by
# `rubocop --auto-gen-config`
# on 2024-04-08 19:36:04 UTC using RuboCop version 1.63.0.
# 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: 7
# Configuration parameters: EnforcedStyle, AllowedGems, Include.
# SupportedStyles: Gemfile, gems.rb, gemspec
# Include: **/*.gemspec, **/Gemfile, **/gems.rb
Gemspec/DevelopmentDependencies:
Exclude:
- 'pathspec.gemspec'
# Offense count: 2
# This cop supports safe autocorrection (--autocorrect).
Layout/BlockEndNewline:
Exclude:
- 'spec/unit/pathspec_spec.rb'
# Offense count: 6
# This cop supports safe autocorrection (--autocorrect).
Layout/HeredocIndentation:
Exclude:
- 'spec/unit/pathspec_spec.rb'
# Offense count: 8
# This cop supports safe autocorrection (--autocorrect).
# 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: 1
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: AutoCorrect, AllowUnusedKeywordArguments, IgnoreEmptyMethods, IgnoreNotImplementedMethods.
Lint/UnusedMethodArgument:
Exclude:
- 'lib/pathspec/spec.rb'
# Offense count: 3
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: AutoCorrect.
Lint/UselessAssignment:
Exclude:
- 'spec/unit/pathspec_spec.rb'
# Offense count: 1
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: AutoCorrect, CheckForMethodsWithNoSideEffects.
Lint/Void:
Exclude:
- 'lib/pathspec.rb'
# Offense count: 3
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
Metrics/AbcSize:
Max: 63
# Offense count: 8
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
# AllowedMethods: refine
Metrics/BlockLength:
Max: 300
# Offense count: 2
# Configuration parameters: CountBlocks.
Metrics/BlockNesting:
Max: 4
# Offense count: 1
# Configuration parameters: CountComments, CountAsOne.
Metrics/ClassLength:
Max: 123
# Offense count: 1
# Configuration parameters: AllowedMethods, AllowedPatterns.
Metrics/CyclomaticComplexity:
Max: 18
# Offense count: 3
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
Metrics/MethodLength:
Max: 69
# Offense count: 2
# Configuration parameters: AllowedMethods, AllowedPatterns.
Metrics/PerceivedComplexity:
Max: 31
# Offense count: 25
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: EnforcedStyle, ProceduralMethods, FunctionalMethods, AllowedMethods, AllowedPatterns, AllowBracesOnProceduralOneLiners, BracesRequiredMethods.
# SupportedStyles: line_count_based, semantic, braces_for_chaining, always_braces
# ProceduralMethods: benchmark, bm, bmbm, create, each_with_object, measure, new, realtime, tap, with_object
# FunctionalMethods: let, let!, subject, watch
# AllowedMethods: lambda, proc, it
Style/BlockDelimiters:
Exclude:
- 'spec/unit/pathspec_spec.rb'
# Offense count: 1
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: AllowMethodsWithArguments, AllowedMethods, AllowedPatterns, AllowComments.
# AllowedMethods: define_method
Style/SymbolProc:
Exclude:
- 'lib/pathspec.rb'
pathspec-ruby-2.1.0/.tool-versions 0000664 0000000 0000000 00000000013 14605360331 0017117 0 ustar 00root root 0000000 0000000 ruby 3.3.0
pathspec-ruby-2.1.0/CHANGELOG.md 0000664 0000000 0000000 00000004470 14605360331 0016117 0 ustar 00root root 0000000 0000000 # pathspec-ruby CHANGELOG
## 2.1.0
## refactor/perf
- Add missing frozen_string_literal comments to reduce object allocations
## build
- Updated Rubocop to 1.18.3
- Fixed/re-enabled Rubocop
- Updated fakefs to 1.3
- Cleaned up unnecessary spec files from the Gem
Thanks for the above contributions @ericproulx! #50
## 2.0.0
- (Maint) Remove deprecated/security release versions of Ruby. The Gem will now only support and be tested against >= 3.1.0 e.g. 3.1, 3.2, and 3.3.
## 1.1.3 (Patch/Bug Fix Release)
- Fixed Man page generation bug in GH Actions
## 1.1.1 (Patch/Bug Fix Release)
- (Maint) Updated Supported Ruby Versions (>= 2.6.9 is the earliest supported now)
- (Maint) Linting corrections
- Setup a CI system with GH Actions to do better validation of the gem before release.
## 1.1.0 (Minor Release)
:alert: This release was mis-tagged. Use 1.1.1 instead. :alert:
- (Maint) Updated Supported Ruby Versions
- (Maint) Linting corrections
## 1.0.0 (Major Release)
- Adds a required ruby version of 2.6 (reason for major version bump)
- Adds man/html docs
## 0.2.1 (Patch/Bug Fix Release)
- Fixes incorrectly pushed gem on Rubygems.org
## 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-2.1.0/Gemfile 0000664 0000000 0000000 00000000106 14605360331 0015571 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
source 'https://rubygems.org'
gemspec
pathspec-ruby-2.1.0/LICENSE 0000664 0000000 0000000 00000026073 14605360331 0015316 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-2.1.0/README.md 0000664 0000000 0000000 00000010103 14605360331 0015553 0 ustar 00root root 0000000 0000000 # pathspec-ruby
[](https://badge.fury.io/rb/pathspec) [](https://github.com/highb/pathspec-ruby/actions/workflows/ruby.yml) [](https://codeclimate.com/github/highb/pathspec-ruby/maintainability)
[man Page as HTML](http://highb.github.io/pathspec-ruby/)
[Supported Rubies](https://www.ruby-lang.org/en/downloads/):
- 3.1 (Stable, Tested)
- 3.2 (Stable, Tested)
- 3.3 (Stable, Tested)
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
➜ cat .gitignore
*.swp
/coverage/
➜ bundle exec pathspec-rb specs_match "coverage/foo"
/coverage/
➜ bundle exec pathspec-rb specs_match "file.swp"
*.swp
➜ bundle exec pathspec-rb match "file.swp"
➜ echo $?
0
➜ ls
Gemfile Gemfile.lock coverage file.swp source.rb
➜ bundle exec 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.
```
## Example Usage in Gemspec
```
lib = File.expand_path("lib", __dir__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require "gemspec_pathspec_test/version"
require 'pathspec'
Gem::Specification.new do |spec|
spec.name = "gemspec_pathspec_test"
spec.version = GemspecPathspecTest::VERSION
spec.authors = ["Brandon High"]
spec.email = ["highb@users.noreply.github.com"]
spec.summary = "whatever"
spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
ps = PathSpec.from_filename('.gitignore')
spec.files = Dir['lib/*.rb'].reject { |f| ps.match(f) }
spec.bindir = "exe"
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
spec.require_paths = ["lib"]
spec.add_development_dependency "bundler", "~> 2.0"
spec.add_development_dependency "rake", "~> 10.0"
spec.add_development_dependency "rspec", "~> 3.0"
end
```
## 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.
## Releasing
This is mainly a reminder to myself but the release process is:
1. Make sure CI is passing
2. Update the CHANGELOG with relevant changes to Gem consumers
3. Update version in gemspec with correct SemVer bump for scope of changes
4. Tag/release using GitHub UI and the Build & Push workflow should do the rest.
pathspec-ruby-2.1.0/Rakefile 0000664 0000000 0000000 00000001170 14605360331 0015745 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
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'))
File.write('docs/index.html', kramdown.to_html)
FileUtils.mkdir_p 'docs/man'
File.write('docs/man/pathspec-rb.man.1', kramdown.to_man)
end
pathspec-ruby-2.1.0/bin/ 0000775 0000000 0000000 00000000000 14605360331 0015051 5 ustar 00root root 0000000 0000000 pathspec-ruby-2.1.0/bin/pathspec-rb 0000775 0000000 0000000 00000004200 14605360331 0017203 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-2.1.0/docs/ 0000775 0000000 0000000 00000000000 14605360331 0015231 5 ustar 00root root 0000000 0000000 pathspec-ruby-2.1.0/docs/index.html 0000664 0000000 0000000 00000004416 14605360331 0017233 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-2.1.0/docs/pathspec-rb.md 0000664 0000000 0000000 00000002733 14605360331 0017770 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-2.1.0/install_from_source.sh 0000775 0000000 0000000 00000000452 14605360331 0020712 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
# Clear old artifacts
rm pathspec-*.gem
# Build and install!
bundle exec gem build pathspec.gemspec && bundle exec gem install pathspec-*.gem
pathspec-ruby-2.1.0/lib/ 0000775 0000000 0000000 00000000000 14605360331 0015047 5 ustar 00root root 0000000 0000000 pathspec-ruby-2.1.0/lib/pathspec.rb 0000664 0000000 0000000 00000005017 14605360331 0017206 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
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-2.1.0/lib/pathspec/ 0000775 0000000 0000000 00000000000 14605360331 0016656 5 ustar 00root root 0000000 0000000 pathspec-ruby-2.1.0/lib/pathspec/gitignorespec.rb 0000664 0000000 0000000 00000022165 14605360331 0022053 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
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-2.1.0/lib/pathspec/regexspec.rb 0000664 0000000 0000000 00000000530 14605360331 0021166 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
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-2.1.0/lib/pathspec/spec.rb 0000664 0000000 0000000 00000000433 14605360331 0020135 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
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-2.1.0/pathspec.gemspec 0000664 0000000 0000000 00000002170 14605360331 0017455 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
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 = '2.1.0'
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,docs}/**/*') + %w[LICENSE README.md CHANGELOG.md]
s.bindir = 'bin'
s.executables << 'pathspec-rb'
s.require_paths = ['lib']
s.metadata['allowed_push_host'] = 'https://rubygems.org'
s.metadata['rubygems_mfa_required'] = 'true'
s.homepage = 'https://github.com/highb/pathspec-ruby'
s.license = 'Apache-2.0'
s.required_ruby_version = '>= 3.1.0'
s.add_development_dependency 'bundler', '~> 2.2'
s.add_development_dependency 'fakefs', '~> 2.5'
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.63.0'
s.add_development_dependency 'simplecov', '~> 0.21'
end
pathspec-ruby-2.1.0/renovate.json 0000664 0000000 0000000 00000000162 14605360331 0017016 0 ustar 00root root 0000000 0000000 {
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended"
]
}
pathspec-ruby-2.1.0/spec/ 0000775 0000000 0000000 00000000000 14605360331 0015233 5 ustar 00root root 0000000 0000000 pathspec-ruby-2.1.0/spec/files/ 0000775 0000000 0000000 00000000000 14605360331 0016335 5 ustar 00root root 0000000 0000000 pathspec-ruby-2.1.0/spec/files/gitignore_readme 0000664 0000000 0000000 00000000030 14605360331 0021555 0 ustar 00root root 0000000 0000000 !**/important.txt
abc/** pathspec-ruby-2.1.0/spec/files/gitignore_ruby 0000664 0000000 0000000 00000002143 14605360331 0021310 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-2.1.0/spec/files/gitignore_simple 0000664 0000000 0000000 00000000005 14605360331 0021613 0 ustar 00root root 0000000 0000000 *.md
pathspec-ruby-2.1.0/spec/files/regex_simple 0000664 0000000 0000000 00000000007 14605360331 0020740 0 ustar 00root root 0000000 0000000 .*\.md
pathspec-ruby-2.1.0/spec/spec_helper.rb 0000664 0000000 0000000 00000000265 14605360331 0020054 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
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-2.1.0/spec/unit/ 0000775 0000000 0000000 00000000000 14605360331 0016212 5 ustar 00root root 0000000 0000000 pathspec-ruby-2.1.0/spec/unit/pathspec/ 0000775 0000000 0000000 00000000000 14605360331 0020021 5 ustar 00root root 0000000 0000000 pathspec-ruby-2.1.0/spec/unit/pathspec/gitignorespec_spec.rb 0000664 0000000 0000000 00000027713 14605360331 0024234 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
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-2.1.0/spec/unit/pathspec/spec_spec.rb 0000664 0000000 0000000 00000000445 14605360331 0022315 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
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-2.1.0/spec/unit/pathspec_spec.rb 0000664 0000000 0000000 00000023344 14605360331 0021366 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
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