Class: GitIgnoreSpec
- Defined in:
- lib/puppet/vendor/pathspec/lib/pathspec/gitignorespec.rb
Instance Attribute Summary collapse
-
#regex ⇒ Object
readonly
Returns the value of attribute regex.
Instance Method Summary collapse
- #inclusive? ⇒ Boolean
-
#initialize(pattern) ⇒ GitIgnoreSpec
constructor
A new instance of GitIgnoreSpec.
- #match(path) ⇒ Object
- #translate_segment_glob(pattern) ⇒ Object
Constructor Details
#initialize(pattern) ⇒ GitIgnoreSpec
Returns a new instance of GitIgnoreSpec.
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 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
# File 'lib/puppet/vendor/pathspec/lib/pathspec/gitignorespec.rb', line 8 def initialize(pattern) pattern = pattern.strip unless 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? @regex = nil @inclusive = nil # Patterns containing three or more consecutive stars are invalid and # will be ignored. elsif pattern =~ /\*\*\*+/ @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..-1] else @inclusive = true end # Remove leading back-slash escape for escaped hash ('#') or # exclamation mark ('!'). if pattern.start_with?('\\') pattern = pattern[1..-1] end # 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 else # 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. if pattern_segs.length == 1 && pattern_segs[0] != '**' pattern_segs.insert(0, '**') end 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. if pattern_segs[-1].empty? pattern_segs[-1] = '**' end # Handle platforms with backslash separated paths if File::SEPARATOR == '\\' path_sep = '\\\\' else path_sep = '/' 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] if seg == '**' # 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. elsif seg == '*' if need_slash regex.concat(path_sep) end regex.concat("[^#{path_sep}]+") need_slash = true else # Match segment glob pattern. if need_slash regex.concat(path_sep) end regex.concat(translate_segment_glob(seg)) need_slash = true end end regex.concat('$') super(regex) end end |
Instance Attribute Details
#regex ⇒ Object (readonly)
Returns the value of attribute regex.
6 7 8 |
# File 'lib/puppet/vendor/pathspec/lib/pathspec/gitignorespec.rb', line 6 def regex @regex end |
Instance Method Details
#inclusive? ⇒ Boolean
272 273 274 |
# File 'lib/puppet/vendor/pathspec/lib/pathspec/gitignorespec.rb', line 272 def inclusive? @inclusive end |
#match(path) ⇒ Object
143 144 145 |
# File 'lib/puppet/vendor/pathspec/lib/pathspec/gitignorespec.rb', line 143 def match(path) super(path) end |
#translate_segment_glob(pattern) ⇒ Object
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 |
# File 'lib/puppet/vendor/pathspec/lib/pathspec/gitignorespec.rb', line 147 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. if j < pattern.size && pattern[j].chr == '!' j += 1 end # Pass first closing braket if it is at the beginning of the # expression. if j < pattern.size && pattern[j].chr == ']' j += 1 end # Find closing braket. Stop once we reach the end or find it. while j < pattern.size && pattern[j].chr != ']' j += 1 end if j < pattern.size expr = '[' # Braket expression needs to be negated. if pattern[i].chr == '!' 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. elsif pattern[i].chr == '^' 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 |