Class: Godo::Finder

Inherits:
Object
  • Object
show all
Defined in:
lib/finder.rb

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(roots, ignores, max_depth = 0) ⇒ Finder

Create a Finder which will search under the given root folders, ignoring folders that match any of the specified ‘ignore’ patterns. Searches only max_depth folders below the root paths, searches unlimited depth if not specified.



20
21
22
23
24
# File 'lib/finder.rb', line 20

def initialize( roots, ignores, max_depth = 0 )
  @roots = roots.map { |root| File.expand_path( root ) }
  @ignores = ignores.map { |ignore| Regexp.compile( "/#{ignore}" ) }
  @max_depth = max_depth.to_i
end

Class Method Details

.find(query, options) ⇒ Object

Find, if possible, a single project path matching the query string.



9
10
11
12
13
# File 'lib/finder.rb', line 9

def self.find( query, options )
  let( Finder.new( options["projects"], options["ignores"], options["max_depth"] ) ) do |finder|
    finder.find( Regexp.escape( query ) )
  end
end

Instance Method Details

#base_match(paths) ⇒ Object



110
111
112
113
114
# File 'lib/finder.rb', line 110

def base_match( paths )
  # Is the first path a prefix for all subsequent-paths
  path_match = Regexp.compile( "^#{paths.first}" )
  paths[1..-1].all? { |path| path.match( path_match ) }
end

#excluded?(path) ⇒ Boolean

Returns:

  • (Boolean)


95
96
97
98
# File 'lib/finder.rb', line 95

def excluded?( path )
  ignore = !!@ignores.detect { |ignore| ignore.match( path ) }
  ignore
end

#filtered?(path) ⇒ Boolean

Returns:

  • (Boolean)


91
92
93
# File 'lib/finder.rb', line 91

def filtered?( path )
  !File.directory?( path ) || excluded?( path )
end

#find(query) ⇒ Object

Search for a folder matching the query.

1) If a single folder is found it is returned.

2) If multiple folders are found then an attempt is made to strip any folders that are not an exact match for the query string.

For example with the query ‘reeplay’ returning the following paths

Paths:

/root/reeplay.it
/root/reeplay.it/reeplay
/root/reeplay.it/reeplay/app

The first path would be stripped because none of it’s components are an exact match for the query term.

3) If there are still multiple folders then the remaining folders are checked to see if they contain the first folder as a base path. If so the first path is returned.

In this case because the second of the two remaining paths has the first as a base path, the first path would be returned.



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
# File 'lib/finder.rb', line 50

def find( query )
  matches = []
  @roots.each do |root|
    # puts "Searching for #{query} in #{root}"
    Find.find( root ) do |path|
      if @max_depth > 0
        # limit to @max_depth
        partial_path = path.gsub(root, '')
        Find.prune if partial_path =~ /\A\.+\Z/
        depth = partial_path.split('/').length
        Find.prune if depth > @max_depth + 1
      end

      if filtered?( path )
        Find.prune
      elsif matches?( path, query )
        matches << path
      end
    end
  end
  
  if matches.size > 1
    matches = strip_inexact_matches( query, matches )
    if matches.size > 1
      if base_match( matches )
        matches[0,1]
      else
        matches
      end
    else
      matches
    end
  else
    matches
  end
end

#matches?(path, query) ⇒ Boolean

Returns:

  • (Boolean)


87
88
89
# File 'lib/finder.rb', line 87

def matches?( path, query )
  path.match( query )
end

#strip_inexact_matches(query, paths) ⇒ Object



100
101
102
103
104
105
106
107
108
# File 'lib/finder.rb', line 100

def strip_inexact_matches( query, paths )
  # If any of the paths have the query as a complete path component
  # then strip any paths that don't
  if paths.any? { |path| path.split( File::SEPARATOR ).any? { |component| query == component } }
    paths.select { |path| path.split( File::SEPARATOR ).any? { |component| query == component } }
  else
    paths
  end
end