fedora-migrator /vendor/gems/facets-2.4.5/lib/more/facets/filelist.rb

Language Ruby Lines 498
MD5 Hash 0a2104d81b44a8f581e066333da5277c Estimated Cost $6,038 (why?)
Repository https://bitbucket.org/mediashelf/fedora-migrator View Raw File
  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
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
142
143
144
145
146
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
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
# = FileList
#
# A FileList is essentially an array with helper methods
# to make file manipulation easier.
#
# FileLists are lazy.  When given a list of glob patterns for
# possible files to be included in the file list, instead of
# searching the file structures to find the files, a FileList holds
# the pattern for latter use.
#
# This allows us to define a number of FileList to match any number of
# files, but only search out the actual files when then FileList
# itself is actually used.  The key is that the first time an
# element of the FileList/Array is requested, the pending patterns
# are resolved into a real list of file names.
#
#   fl = FileList.new
#   fl.include('./**/*')
#   fl.exclude('./*~')
#
# == History
#
#   FileList was ported from Jim Weirich's Rake.
#
# == Authors
#
# * Jim Weirich
#
# == Todo
#
# * Should the exclusions really be the default?
#   Maybe have #exclude_typical instead.
#
# == Copying
#
# Copyright (C) 2002 Jim Weirich
#
# General Public License (GPL)
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

# = FileList
#
# A FileList is essentially an array with helper methods
# to make file manipulation easier.
#
# FileLists are lazy.  When given a list of glob patterns for
# possible files to be included in the file list, instead of
# searching the file structures to find the files, a FileList holds
# the pattern for latter use.
#
# This allows us to define a number of FileList to match any number of
# files, but only search out the actual files when then FileList
# itself is actually used.  The key is that the first time an
# element of the FileList/Array is requested, the pending patterns
# are resolved into a real list of file names.
#
#   fl = FileList.new
#   fl.include('./**/*')
#   fl.exclude('./*~')
#
class FileList

  # TODO: Add glob options.
  #attr :glob_options

  #include Cloneable
  def clone
    sibling = self.class.new
    instance_variables.each do |ivar|
      value = self.instance_variable_get(ivar)
      sibling.instance_variable_set(ivar, value.rake_dup)
    end
    sibling
  end
  alias_method :dup, :clone

  # == Method Delegation
  #
  # The lazy evaluation magic of FileLists happens by implementing
  # all the array specific methods to call +resolve+ before
  # delegating the heavy lifting to an embedded array object
  # (@items).
  #
  # In addition, there are two kinds of delegation calls.  The
  # regular kind delegates to the @items array and returns the
  # result directly.  Well, almost directly.  It checks if the
  # returned value is the @items object itself, and if so will
  # return the FileList object instead.
  #
  # The second kind of delegation call is used in methods that
  # normally return a new Array object.  We want to capture the
  # return value of these methods and wrap them in a new FileList
  # object.  We enumerate these methods in the +SPECIAL_RETURN+ list
  # below.

  # List of array methods (that are not in +Object+) that need to be
  # delegated.
  ARRAY_METHODS = Array.instance_methods - Object.instance_methods

  # List of additional methods that must be delegated.
  MUST_DEFINE = %w[to_a inspect]

  # List of methods that should not be delegated here (we define
  # special versions of them explicitly below).
  MUST_NOT_DEFINE = %w[to_a to_ary partition *]

  # List of delegated methods that return new array values which
  # need wrapping.
  SPECIAL_RETURN = %w[
    map collect sort sort_by select find_all reject grep
    compact flatten uniq values_at
    + - & |
  ]

  DELEGATING_METHODS = (ARRAY_METHODS + MUST_DEFINE - MUST_NOT_DEFINE).sort.uniq

  # Now do the delegation.
  DELEGATING_METHODS.each_with_index do |sym, i|
    if SPECIAL_RETURN.include?(sym)
      ln = __LINE__+1
      class_eval %{
        def #{sym}(*args, &block)
          resolve if @pending
          result = @items.send(:#{sym}, *args, &block)
          FileList.new.import(result)
        end
      }, __FILE__, ln
    else
      ln = __LINE__+1
      class_eval %{
        def #{sym}(*args, &block)
          resolve if @pending
          result = @items.send(:#{sym}, *args, &block)
          result.object_id == @items.object_id ? self : result
        end
      }, __FILE__, ln
    end
  end

  # Create a file list from the globbable patterns given.  If you
  # wish to perform multiple includes or excludes at object build
  # time, use the "yield self" pattern.
  #
  # Example:
  #   file_list = FileList.new['lib/**/*.rb', 'test/test*.rb']
  #
  #   pkg_files = FileList.new['lib/**/*'] do |fl|
  #     fl.exclude(/\bCVS\b/)
  #   end
  #
  def initialize(*patterns)
    @pending_add = []
    @pending = false
    @exclude_patterns = DEFAULT_IGNORE_PATTERNS.dup
    @exclude_re = nil
    @items = []
    patterns.each { |pattern| include(pattern) }
    yield self if block_given?
  end

  # Add file names defined by glob patterns to the file list.  If an
  # array is given, add each element of the array.
  #
  # Example:
  #   file_list.include("*.java", "*.cfg")
  #   file_list.include %w( math.c lib.h *.o )
  #
  def include(*filenames)
    # TODO: check for pending
    filenames.each do |fn|
      if fn.respond_to? :to_ary
        include(*fn.to_ary)
      else
        @pending_add << fn
      end
    end
    @pending = true
    self
  end
  alias :add :include

  # Register a list of file name patterns that should be excluded
  # from the list.  Patterns may be regular expressions, glob
  # patterns or regular strings.
  #
  # Note that glob patterns are expanded against the file system.
  # If a file is explicitly added to a file list, but does not exist
  # in the file system, then an glob pattern in the exclude list
  # will not exclude the file.
  #
  # Examples:
  #   FileList['a.c', 'b.c'].exclude("a.c") => ['b.c']
  #   FileList['a.c', 'b.c'].exclude(/^a/)  => ['b.c']
  #
  # If "a.c" is a file, then ...
  #   FileList['a.c', 'b.c'].exclude("a.*") => ['b.c']
  #
  # If "a.c" is not a file, then ...
  #   FileList['a.c', 'b.c'].exclude("a.*") => ['a.c', 'b.c']
  #
  def exclude(*patterns)
    patterns.each do |pat| @exclude_patterns << pat end
    if ! @pending
      calculate_exclude_regexp
      reject! { |fn| fn =~ @exclude_re }
    end
    self
  end

  # Clear all the exclude patterns so that we exclude nothing.
  def clear_exclude
    @exclude_patterns = []
    calculate_exclude_regexp if ! @pending
  end

  # Define equality.
  def ==(array)
    to_ary == array
  end

  # Return the internal array object.
  def to_a
    resolve
    @items
  end

  # Return the internal array object.
  def to_ary
    resolve
    @items
  end

  # Redefine * to return either a string or a new file list.
  def *(other)
    result = @items * other
    case result
    when Array
      FileList.new.import(result)
    else
      result
    end
  end

  # Resolve all the pending adds now.
  def resolve
    if @pending
      @pending = false
      @pending_add.each do |fn| resolve_add(fn) end
      @pending_add = []
      resolve_exclude
    end
    self
  end

  def calculate_exclude_regexp
    ignores = []
    @exclude_patterns.each do |pat|
      case pat
      when Regexp
        ignores << pat
      when /[*.]/
        Dir[pat].each do |p| ignores << p end
      else
        ignores << Regexp.quote(pat)
      end
    end
    if ignores.empty?
      @exclude_re = /^$/
    else
      re_str = ignores.collect { |p| "(" + p.to_s + ")" }.join("|")
      @exclude_re = Regexp.new(re_str)
    end
  end

  def resolve_add(fn)
    case fn
    when Array
      fn.each { |f| self.resolve_add(f) }
    when %r{[*?]}
      add_matching(fn)
    else
      self << fn
    end
  end

  def resolve_exclude
    @exclude_patterns.each do |pat|
      case pat
      when Regexp
        reject! { |fn| fn =~ pat }
      when /[*.]/
        reject_list = Dir[pat]
        reject! { |fn| reject_list.include?(fn) }
      else
        reject! { |fn| fn == pat }
      end
    end
    self
  end

  # Return a new FileList with the results of running +sub+ against
  # each element of the oringal list.
  #
  # Example:
  #   FileList['a.c', 'b.c'].sub(/\.c$/, '.o')  => ['a.o', 'b.o']
  #
  def sub(pat, rep)
    inject(FileList.new) { |res, fn| res << fn.sub(pat,rep) }
  end

  # Return a new FileList with the results of running +gsub+ against
  # each element of the original list.
  #
  # Example:
  #   FileList['lib/test/file', 'x/y'].gsub(/\//, "\\")
  #      => ['lib\\test\\file', 'x\\y']
  #
  def gsub(pat, rep)
    inject(FileList.new) { |res, fn| res << fn.gsub(pat,rep) }
  end

  # Same as +sub+ except that the oringal file list is modified.
  def sub!(pat, rep)
    each_with_index { |fn, i| self[i] = fn.sub(pat,rep) }
    self
  end

  # Same as +gsub+ except that the original file list is modified.
  def gsub!(pat, rep)
    each_with_index { |fn, i| self[i] = fn.gsub(pat,rep) }
    self
  end

  # Return a new array with <tt>String#ext</tt> method applied to
  # each member of the array.
  #
  # This method is a shortcut for:
  #
  #    array.collect { |item| item.ext(newext) }
  #
  # +ext+ is a user added method for the Array class.
  def ext(newext='')
    collect { |fn| fn.ext(newext) }
  end

  # Grep each of the files in the filelist using the given pattern.
  # If a block is given, call the block on each matching line,
  # passing the file name, line number, and the matching line of
  # text.  If no block is given, a standard emac style
  # file:linenumber:line message will be printed to standard out.
  def egrep(pattern)
    each do |fn|
      open(fn) do |inf|
        count = 0

        inf.each do |line|
          count += 1
          if pattern.match(line)
            if block_given?
              yield fn, count, line
            else
              puts "#{fn}:#{count}:#{line}"
            end
          end
        end

      end
    end
  end

  # FileList version of partition.  Needed because the nested arrays
  # should be FileLists in this version.
  def partition(&block) # :nodoc:
    resolve
    result = @items.partition(&block)
    [
      FileList.new.import(result[0]),
      FileList.new.import(result[1]),
    ]
  end

  # Convert a FileList to a string by joining all elements with a space.
  def to_s
    resolve if @pending
    self.join(' ')
  end

  # Add matching glob patterns.
  def add_matching(pattern)
    Dir[pattern].each do |fn|
      self << fn unless exclude?(fn)
    end
  end
  private :add_matching

  # Should the given file name be excluded?
  def exclude?(fn)
    calculate_exclude_regexp unless @exclude_re
    fn =~ @exclude_re
  end

  DEFAULT_IGNORE_PATTERNS = [
    /(^|[\/\\])CVS([\/\\]|$)/,
    /(^|[\/\\])\.svn([\/\\]|$)/,
    /(^|[\/\\])_darcs([\/\\]|$)/,
    /\.bak$/,
    /~$/,
    /(^|[\/\\])core$/
  ]
  @exclude_patterns = DEFAULT_IGNORE_PATTERNS.dup

  def import(array)
    @items = array
    self
  end

  class << self
    # Create a new file list including the files listed. Similar to:
    #
    #   FileList.new(*args)
    def [](*args)
      new(*args)
    end

    # Set the ignore patterns back to the default value.  The
    # default patterns will ignore files
    # * containing "CVS" in the file path
    # * containing ".svn" in the file path
    # * containing "_darcs" in the file path
    # * ending with ".bak"
    # * ending with "~"
    # * named "core"
    #
    # Note that file names beginning with "." are automatically
    # ignored by Ruby's glob patterns and are not specifically
    # listed in the ignore patterns.
    def select_default_ignore_patterns
      @exclude_patterns = DEFAULT_IGNORE_PATTERNS.dup
    end

    # Clear the ignore patterns.
    def clear_ignore_patterns
      @exclude_patterns = [ /^$/ ]
    end
  end

end # FileList



# = TEST
#
# TODO This test needs a mock File class.

# FileList can't be tested without a FIXTURE setup.
# So don't bother running it directly from here, but
# transfer it over to the test directory.
# Need a  better solution, is there a way to fake the
# filesystem?

=begin #no test

  require 'test/unit'

  class TC_FileList < Test::Unit::TestCase

    def test_filelist
      Dir.chdir File.join( $TESTDIR, 'filelist' ) do
        fl = FileList.new
        fl.include('*')
        assert_equal( ['testfile.txt', 'testfile2.txt'], fl.to_a )
        fl.exclude('*2.txt')
        assert_equal( ['testfile.txt'], fl.to_a )
      end
    end

  end

=end
Back to Top