Module: Cfruby::FileFind
- Defined in:
- lib/libcfruby/filefind.rb
Defined Under Namespace
Classes: FileExistError, FileFindError, FileMatch
Class Method Summary collapse
-
.find(basedir, options = {}, &block) ⇒ Object
Find files that conform to a set of options within a given base directory and yield them to a given block.
Class Method Details
.find(basedir, options = {}, &block) ⇒ Object
Find files that conform to a set of options within a given base directory and yield them to a given block. In addition to all of the options available to FileMatch the following options may be given:
:depth
-
the maximum depth to recurse into the base directory
:recursive
-
recurse (default to a depth => 200)
:nonrecursive
-
don’t recurse at all (equivilant to :depth => 0)
:followsymlinks
-
if true symlinks are expanded and returned as their realpath, otherwise they are returned as is (default false)
:returnorder
-
if unset files are returned in traversal order, if set to ‘delete’ files are returned first followed by directories
:filematch
-
normally internal use only, allows passing in of a FileMatch object (all other file matching options ignored)
Returns the number of files found
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 |
# File 'lib/libcfruby/filefind.rb', line 316 def FileFind.find(basedir, = {}, &block) filematch = nil matched = 0 if([:filematch] != nil) filematch = [:filematch] else # set real options based on helper options if(([:nonrecursive] or [:recursive]) and [:depth]) raise(ArgumentError, ":depth argument is not meaningful with :recursive or :nonrecursive") end if([:nonrecursive]) [:depth] = 0 elsif([:recursive]) [:depth] = 200 if [:depth] == nil .delete(:recursive) end # set basic options if they weren't passed in if([:glob] == nil) Cfruby.controller.inform('debug', "No glob given for search, using '*'") [:glob] = '*' end if([:depth] == nil) Cfruby.controller.inform('debug', "No depth given for search, using 0") [:depth] = 0 end # get a filematch object filematch = FileMatch.new() end # add the filematch back to options so we can pass it back to # ourselves recursively [:filematch] = filematch # set some options to local variables to prevent a lookup on every file maxdepth = [:depth] # set the file return order deleteorder = false if([:returnorder] == 'delete') deleteorder = true end # allow people to pass in lists if(basedir.respond_to?(:each) and !basedir.kind_of?(String) and !basedir.kind_of?(Pathname)) basedir.each() { |subdir| find(subdir, ) { |filename| matched += 1 yield(filename) } } return(matched) else # attempt to expand with a glob and expand_path dirs = Dir.glob(File.(basedir.to_s)) if(dirs.length > 1) find(dirs, ) { |filename| matched += 1 yield(filename) } return(matched) elsif(dirs.length == 0) # if we didn't get anything out of the glob expansion, then the file doesn't really exist raise(FileExistError, "\"#{basedir}\" does not exist") else basedir = dirs[0] end end # convert basedir into an absolute pathname if(!basedir.kind_of?(Pathname)) basedir = Pathname.new(basedir) end if(!basedir.symlink?() and !basedir.exist?()) raise(FileExistError, "#{basedir.to_s} does not exist") end directories = Array.new() visited = Hash.new() # if the given basedir is a symlink, only follow it if :followsymlink == true if(basedir.symlink? and ![:followsymlinks]) if(filematch.matches?(basedir.)) matched += 1 yield(basedir.) end else basedir = basedir.realpath directories << Array.[](basedir, 0) end yielddirs = Array.new() while(directories.length > 0) currentdir = directories.pop() # skip this directory if we have already been there if(visited[currentdir[0]] != nil) next else visited[currentdir[0]] = true end currentdepth = currentdir[1] currentdir = currentdir[0] # add the directory to the yielddirs list if we need to, or yield it # depending on :returnorder if(filematch.matches?(currentdir)) if(deleteorder) yielddirs << currentdir else matched += 1 yield(currentdir) end end # yield files at this level and recurse if needed. We use a form of pseudo recursion to avoid # exhausting the stack depth. Directories that match the find are added to a special yielddirs # list and are yielded to the caller after the entire recursion has finished. This ensures that # things like recursive delete work properly. if(currentdepth < maxdepth) Dir.glob("#{currentdir}/*", File::FNM_DOTMATCH) { |filename| filename = FileFind.(Pathname.new(filename), ) # skip if it escapes our basedir if(filename.relative_path_from(basedir).to_s() =~ /(^|\/)\.\.(\/|$)/) next end # if it is a directory add it to the list for recursion if(filename.directory?()) directories.push(Array.[](filename, currentdepth+1)) elsif(filematch.matches?(filename)) Cfruby.controller.inform('debug', "found #{filename}") matched += 1 yield(filename) end } end end while(yielddirs.length > 0) matched += 1 yield(yielddirs.pop) end return(matched) end |