This repository has been archived by the owner on Nov 24, 2017. It is now read-only.
/
dependency_resolver.rb
109 lines (94 loc) · 3.18 KB
/
dependency_resolver.rb
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
module Juicer
class DependencyResolver
include Enumerable
attr_reader :files
# Constructor
def initialize(options = {})
@files = []
@options = options
end
#
# Resolve dependencies.
# This method accepts an optional block. The block will receive each
# file in succession. The file is included in the returned collection
# if the block is true for the given file. Without a block every found
# file is returned.
#
def resolve(file, &block)
@files = []
_resolve(file, &block)
end
#
# Yield files recursively. Resolve dependencies first, then call each, or
# any other enumerable methods.
#
def each(&block)
@files.each(&block)
end
#
# Resolves a path relative to another. If the path is absolute (ie it
# starts with a protocol or /) the <tt>:document_root</tt> options has to be
# set as well.
#
def resolve_path(path, reference)
# Absolute URL
if path =~ %r{^(/|[a-z]+:)}
if @options[:document_root].nil?
msg = "Cannot resolve absolute path '#{path}' without document root option"
raise ArgumentError.new(msg)
end
path.sub!(%r{^[a-z]+://[^/]+/}, '')
return File.expand_path(File.join(@options[:document_root], path))
end
File.expand_path(File.join(File.dirname(reference), path))
end
private
def parse(line)
raise NotImplementedError.new
end
def extension
raise NotImplementedError.new
end
def encoded_line(line)
if String.method_defined?(:encode)
line.encode!('UTF-8', 'UTF-8', :invalid => :replace)
else
line
end
end
#
# Carries out the actual work of resolve. resolve resets the internal
# file list and yields control to _resolve for rebuilding the file list.
#
def _resolve(file)
imported_path = nil
IO.foreach(file) do |line|
# Implementing subclasses may throw :done from the parse method when
# the file is exhausted for dependency declaration possibilities.
catch(:done) do
imported_path = parse(line, imported_path)
# If a dependency declaration was found
if imported_path
# Resolves a path relative to the file that imported it
imported_path = resolve_path(imported_path, file)
if File.directory?(imported_path)
imported_files = Dir.glob(File.join(imported_path, "**", "*#{extension}"))
else
imported_files = [imported_path]
end
imported_files.each do |imported_file|
# Only keep processing file if it's not already included.
# Yield to block to allow caller to ignore file
if !@files.include?(imported_file) && (!block_given? || yield(imported_file))
# Check this file for imports before adding it to get order right
_resolve(imported_file) { |f| f != File.expand_path(file) }
end
end
end
end
end
file = File.expand_path(file)
@files << file if !@files.include?(file) && (!block_given? || yield(file))
end
end
end