ignore
A .gitignore compatible pattern parser for Crystal.
Installation
-
Add the dependency to your
shard.yml:dependencies: ignore: github: trans/ignore -
Run
shards install
Usage
require "ignore"
# Parse gitignore content
matcher = Ignore.parse(<<-GITIGNORE
build/
*.o
*.log
!important.log
GITIGNORE
)
matcher.ignores?("build/") # => true (directory)
matcher.ignores?("main.o") # => true
matcher.ignores?("debug.log") # => true
matcher.ignores?("important.log") # => false (negated)
# Quick one-liner
Ignore.ignores?("foo.log", "*.log") # => true
Directory Matching
Use a trailing slash to check directories:
matcher = Ignore.parse("build/")
matcher.ignores?("build/") # => true (directory)
matcher.ignores?("build") # => false (file)
Building Patterns Incrementally
matcher = Ignore::Matcher.new
matcher.add("*.o")
matcher.add("*.log")
matcher.add("!important.log")
matcher.ignores?("test.o") # => true
# Inspect and manage patterns
matcher.patterns # => ["*.o", "*.log", "!important.log"]
matcher.size # => 3
matcher.clear # remove all patterns
# Enumerable support
matcher.each { |pattern| puts pattern }
matcher.select { |p| p.starts_with?("*") }
Loading from a Directory Tree
Load all .gitignore files from a project, with patterns scoped to their directories:
matcher = Ignore.root("/path/to/project")
matcher.ignores?("src/debug.log")
This loads .gitignore files from the root and all subdirectories. Patterns from deeper directories take precedence, so a !debug.log in src/.gitignore will override *.log in the root .gitignore.
You can also load other ignore file formats:
# Load .dockerignore files
matcher = Ignore.root("/path/to/project", ".dockerignore")
# Load .npmignore files
matcher = Ignore.root("/path/to/project", ".npmignore")
Loading Individual Files
matcher = Ignore::Matcher.new
matcher.add_file(".gitignore")
matcher.add_file("src/.gitignore", base: "src/")
Managing Ignore Files
Use Ignore::File to read, modify, and save ignore files:
file = Ignore::File.new(".gitignore")
# Inspect patterns
file.patterns # => ["*.log", "build/"]
file.lines # => ["# Build artifacts", "*.log", "", "build/"]
file.size # => 2 (pattern count, excludes comments/blanks)
# Modify patterns
file.add("*.tmp")
file.remove("*.log")
file.includes?("*.tmp") # => true
# Check paths against patterns
file.ignores?("debug.tmp") # => true
# Save changes (preserves comments and blank lines)
file.save
# Reload from disk (discards unsaved changes)
file.reload
# Enumerable support (iterates over patterns only)
file.each { |pattern| puts pattern }
Filtered Directory Operations
Use Ignore::Dir for filtered directory listings and glob results:
dir = Ignore::Dir.new("/path/to/project", "*.log", "build/")
dir.glob("**/*.cr") # filtered glob
dir.children # filtered directory children
dir.entries # filtered entries (includes . and ..)
dir.each_child { |entry| ... } # filtered iteration
# Include hidden files in glob
dir.glob("**/*", match: :dot_files)
# Load patterns from .gitignore files automatically
dir = Ignore::Dir.new("/path/to/project", root: ".gitignore")
# Load from a single ignore file
dir = Ignore::Dir.new("/path/to/project", file: ".gitignore")
Directory patterns like build/ will filter out the directory and all its contents.
Inverse Filtering
Get only the ignored files (useful for cleanup tools):
dir = Ignore::Dir.new("/path/to/project", "*.log", "build/")
dir.ignored_glob("**/*") # only ignored paths
dir.ignored_children # only ignored children
dir.ignored_entries # only ignored entries
dir.each_ignored_child { |e| } # iterate ignored children
Enumerable Support
Ignore::Dir includes Enumerable, iterating over non-ignored children:
dir = Ignore::Dir.new("/path/to/project", "*.log")
dir.select { |entry| entry.ends_with?(".cr") }
dir.map { |entry| entry.upcase }
Dir Monkey Patch (Optional)
For convenience, you can optionally load a monkey patch that adds ignore methods to Dir:
require "ignore/core_ext"
# Class method (uses current directory)
Dir.ignore("*.log", "build/").glob("**/*")
Dir.ignore(root: ".gitignore").glob("**/*")
# Instance method
Dir.new("/path/to/project").ignore("*.log").glob("**/*")
Dir.new("/path/to/project").ignore(root: ".gitignore").children
Supported Patterns
| Pattern | Description |
|---------|-------------|
| *.txt | Wildcard, matches any .txt file |
| ? | Single character wildcard |
| [abc] | Character class |
| [a-z] | Character range |
| [!abc] | Negated character class |
| build/ | Directory only (trailing slash) |
| /root | Anchored to root (leading slash) |
| foo/bar | Anchored (contains slash) |
| !pattern | Negation (un-ignore) |
| **/foo | Match in all directories |
| foo/** | Match everything inside |
| a/**/b | Zero or more directories between |
| \# \! | Escaped special characters |
License
MIT