File: pathspec.rb

package info (click to toggle)
ruby-pathspec 3.0.1-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 248 kB
  • sloc: ruby: 1,037; makefile: 9; sh: 5
file content (116 lines) | stat: -rw-r--r-- 2,596 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# 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
  alias match? match

  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