root 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,126 @@
1
+ require 'lazydoc'
2
+ require 'root/manifest'
3
+ require 'root/constant'
4
+ require 'root/utils'
5
+
6
+ class Root
7
+
8
+ # :startdoc:::-
9
+ #
10
+ # ConstantManifest builds a manifest of Constant entries using Lazydoc.
11
+ #
12
+ # Lazydoc can quickly scan files for constant attributes, and thereby
13
+ # identify constants based upon a flag like the '::manifest' attribute used
14
+ # to identify task classes. ConstantManifest registers paths that will be
15
+ # scanned for a specific resource, and lazily builds the references to load
16
+ # them as necessary.
17
+ #
18
+ # :startdoc:::+
19
+ class ConstantManifest < Manifest
20
+
21
+ # The attribute identifying constants in a file
22
+ attr_reader :const_attr
23
+
24
+ # An array of registered (root, [paths]) pairs
25
+ # that will be searched for const_attr
26
+ attr_reader :search_paths
27
+
28
+ # The current index of search_paths
29
+ attr_reader :search_path_index
30
+
31
+ # The current index of paths
32
+ attr_reader :path_index
33
+
34
+ # Initializes a new ConstantManifest that will identify constants
35
+ # using the specified constant attribute.
36
+ def initialize(const_attr)
37
+ @const_attr = const_attr
38
+ @search_paths = []
39
+ @search_path_index = 0
40
+ @path_index = 0
41
+ super([])
42
+ end
43
+
44
+ # Registers the files matching pattern under dir. Returns self.
45
+ def register(dir, pattern)
46
+ paths = Dir.glob(File.join(dir, pattern)).select {|file| File.file?(file) }
47
+ search_paths << [dir, paths.sort]
48
+ self
49
+ end
50
+
51
+ # Searches all paths for entries and adds them to self. Returns self.
52
+ def build
53
+ each {|entry| } unless built?
54
+ self
55
+ end
56
+
57
+ # True if there are no more paths to search
58
+ # (ie search_path_index == search_paths.length)
59
+ def built?
60
+ search_path_index == search_paths.length
61
+ end
62
+
63
+ # Sets search_path_index and path_index to zero and clears entries.
64
+ # Returns self.
65
+ def reset
66
+ @search_paths.each {|path| Lazydoc[path].resolved = false }
67
+ @entries.clear
68
+ @search_path_index = 0
69
+ @path_index = 0
70
+ super
71
+ end
72
+
73
+ # Yields each Constant entry to the block. Unless built?, each
74
+ # lazily iterates over search_paths to look for new entries.
75
+ def each
76
+ entries.each do |entry|
77
+ yield(entry)
78
+ end
79
+
80
+ search_paths[search_path_index, search_paths.length - search_path_index].each do |(path_root, paths)|
81
+ paths[path_index, paths.length - path_index].each do |path|
82
+ new_entries = resolve(path_root, path)
83
+ entries.concat(new_entries)
84
+
85
+ @path_index += 1
86
+ new_entries.each {|entry| yield(entry) }
87
+ end
88
+
89
+ @search_path_index += 1
90
+ @path_index = 0
91
+ end unless built?
92
+ end
93
+
94
+ protected
95
+
96
+ def minikey(const) # :nodoc:
97
+ const.path
98
+ end
99
+
100
+ # Scans path for constants having const_attr, and initializes Constant
101
+ # objects for each. If the document has no default_const_name set,
102
+ # resolve will set the default_const_name based on the relative
103
+ # filepath from path_root to path.
104
+ def resolve(path_root, path) # :nodoc:
105
+ entries = []
106
+ document = nil
107
+
108
+ Lazydoc::Document.scan(File.read(path), const_attr) do |const_name, key, value|
109
+ if document == nil
110
+ relative_path = Utils.relative_filepath(path_root, path).chomp(File.extname(path))
111
+ document = Lazydoc.register_file(path, relative_path.camelize)
112
+ end
113
+
114
+ const_name = document.default_const_name if const_name.empty?
115
+ comment = Lazydoc::Subject.new(nil, document)
116
+ comment.value = value
117
+
118
+ document[const_name][key] = comment
119
+ entries << Constant.new(const_name, path)
120
+ end
121
+
122
+ entries
123
+ end
124
+
125
+ end
126
+ end
@@ -0,0 +1,40 @@
1
+ require 'rubygems'
2
+
3
+ class Root
4
+
5
+ # Methods for working with {RubyGems}[http://www.rubygems.org/].
6
+ module Gems
7
+ module_function
8
+
9
+ # Returns the gemspec for the specified gem. A gem version
10
+ # can be specified in the name, like 'gem >= 1.2'. The gem
11
+ # will be activated using +gem+ if necessary.
12
+ def gemspec(gem_name)
13
+ return gem_name if gem_name.kind_of?(Gem::Specification)
14
+
15
+ # figure the version of the gem, by default >= 0.0.0
16
+ gem_name.to_s =~ /^([^<=>]*)(.*)$/
17
+ name, version = $1.strip, $2
18
+ version = ">= 0.0.0" if version.empty?
19
+
20
+ return nil if name.empty?
21
+
22
+ # load the gem and get the spec
23
+ gem(name, version)
24
+ Gem.loaded_specs[name]
25
+ end
26
+
27
+ # Selects gem specs for which the block returns true. If
28
+ # latest is specified, only the latest version of each
29
+ # gem will be passed to the block.
30
+ def select_gems(latest=true)
31
+ index = latest ?
32
+ Gem.source_index.latest_specs :
33
+ Gem.source_index.gems.collect {|(name, spec)| spec }
34
+
35
+ index.select do |spec|
36
+ yield(spec)
37
+ end.sort
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,36 @@
1
+ class Root
2
+ # Generates an Intern module for the specified method_name.
3
+ #
4
+ # An Intern module:
5
+ #
6
+ # - adds an accessor for <method_name>_block
7
+ # - overrides <method_name> to call the block
8
+ #
9
+ def self.Intern(method_name)
10
+ mod = INTERN_MODULES[method_name.to_sym]
11
+ return mod unless mod == nil
12
+
13
+ mod = INTERN_MODULES[method_name.to_sym] = Module.new
14
+ mod.module_eval %Q{
15
+ attr_accessor :#{method_name}_block
16
+
17
+ def #{method_name}(*inputs)
18
+ raise "no #{method_name} block set" unless #{method_name}_block
19
+ inputs.unshift(self)
20
+
21
+ arity = #{method_name}_block.arity
22
+ n = inputs.length
23
+ unless n == arity || (arity < 0 && (-1-n) <= arity)
24
+ raise ArgumentError.new("wrong number of arguments (\#{n} for \#{arity})")
25
+ end
26
+
27
+ #{method_name}_block.call(*inputs)
28
+ end
29
+ }
30
+ mod
31
+ end
32
+
33
+ # An array of already-declared intern modules,
34
+ # keyed by method_name.
35
+ INTERN_MODULES = {}
36
+ end
@@ -0,0 +1,168 @@
1
+ require 'root/minimap'
2
+ require 'root/intern'
3
+
4
+ class Root
5
+
6
+ # Stores an array of paths and makes them available for lookup by minipath.
7
+ # Manifests may be bound to a Tap::Env, allowing searches across a full
8
+ # environment (including nested environments).
9
+ #
10
+ # Manifest has a number of hooks used by subclasses like ConstantManifest
11
+ # to lazily add entries as needed.
12
+ class Manifest
13
+ class << self
14
+
15
+ # Interns a new manifest, overriding the minikey method with the block
16
+ # (the minikey method converts entries to the path used during minimap
17
+ # and minimatch lookup, see Minimap).
18
+ def intern(*args, &block)
19
+ instance = new(*args)
20
+ if block_given?
21
+ instance.extend Intern(:minikey)
22
+ instance.minikey_block = block
23
+ end
24
+ instance
25
+ end
26
+ end
27
+
28
+ include Enumerable
29
+ include Minimap
30
+
31
+ # Matches a compound manifest search key. After the match, if the key is
32
+ # compound then:
33
+ #
34
+ # $1:: env_key
35
+ # $4:: key
36
+ #
37
+ # If the key is not compound, $4 is nil and $1 is the key.
38
+ SEARCH_REGEXP = /^(([A-z]:)?.*?)(:(.*))?$/
39
+
40
+ # An array entries in self.
41
+ attr_reader :entries
42
+
43
+ # The bound Env, or nil.
44
+ attr_reader :env
45
+
46
+ # The reader on Env accessing manifests of the same type as this value.
47
+ # Reader is set during bind.
48
+ attr_reader :reader
49
+
50
+ # Initializes a new, unbound Manifest.
51
+ def initialize(entries=[])
52
+ @entries = entries
53
+ @env = nil
54
+ @reader = nil
55
+ end
56
+
57
+ # Binds self to an env and reader. The manifests returned by env.reader
58
+ # will be used during traversal methods like search. Raises an error if
59
+ # env does not respond to reader; returns self.
60
+ def bind(env, reader)
61
+ if env == nil
62
+ raise ArgumentError, "env may not be nil"
63
+ end
64
+
65
+ unless env.respond_to?(reader)
66
+ raise ArgumentError, "env does not respond to #{reader}"
67
+ end
68
+
69
+ @env = env
70
+ @reader = reader
71
+ self
72
+ end
73
+
74
+ # Unbinds self from env. Returns self.
75
+ def unbind
76
+ @env = nil
77
+ @reader = nil
78
+ self
79
+ end
80
+
81
+ # True if the env and reader have been set.
82
+ def bound?
83
+ @env != nil && @reader != nil
84
+ end
85
+
86
+ # A hook for dynamically building entries. By default build simply
87
+ # returns self
88
+ def build
89
+ self
90
+ end
91
+
92
+ # A hook to flag when self is built. By default built? returns true.
93
+ def built?
94
+ true
95
+ end
96
+
97
+ # A hook to reset a build. By default reset simply returns self.
98
+ def reset
99
+ self
100
+ end
101
+
102
+ # True if entries are empty.
103
+ def empty?
104
+ entries.empty?
105
+ end
106
+
107
+ # Iterates over each entry entry in self.
108
+ def each
109
+ entries.each {|entry| yield(entry) }
110
+ end
111
+
112
+ # Alias for Minimap#minimatch.
113
+ def [](key)
114
+ minimatch(key)
115
+ end
116
+
117
+ # Search across env.each for the first entry minimatching key. A single
118
+ # env can be specified by using a compound key like 'env_key:key'.
119
+ # Returns nil if no matching entry is found.
120
+ #
121
+ # Search raises an error unless bound?
122
+ def search(key)
123
+ raise "cannot search unless bound" unless bound?
124
+
125
+ key =~ SEARCH_REGEXP
126
+ envs = if $4 != nil
127
+ # compound key, match for env
128
+ key = $4
129
+ [env.minimatch($1)].compact
130
+ else
131
+ # not a compound key, search all
132
+ # envs by iterating env itself
133
+ env
134
+ end
135
+
136
+ # traverse envs looking for the first
137
+ # manifest entry matching key
138
+ envs.each do |env|
139
+ if result = env.send(reader).minimatch(key)
140
+ return result
141
+ end
142
+ end
143
+
144
+ nil
145
+ end
146
+
147
+ def inspect(traverse=true)
148
+ if traverse && bound?
149
+ lines = []
150
+ env.each do |env|
151
+ manifest = env.send(reader).build
152
+ next if manifest.empty?
153
+
154
+ lines << "== #{env.root.root}"
155
+ manifest.minimap.each do |mini, value|
156
+ lines << " #{mini}: #{value.inspect}"
157
+ end
158
+ end
159
+ return lines.join("\n")
160
+ end
161
+
162
+ lines = minimap.collect do |mini, value|
163
+ " #{mini}: #{value.inspect}"
164
+ end
165
+ "#{self.class}:#{object_id} (#{bound? ? env.root.root : ''})\n#{lines.join("\n")}"
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,88 @@
1
+ require 'root/utils'
2
+
3
+ class Root
4
+
5
+ # Minimap adds minimization and search methods to an array of paths (see
6
+ # Utils.minimize and Utils.minimal_match?).
7
+ #
8
+ # paths = %w{
9
+ # path/to/file-0.1.0.txt
10
+ # path/to/file-0.2.0.txt
11
+ # path/to/another_file.txt
12
+ # }
13
+ # paths.extend Root::Minimap
14
+ #
15
+ # paths.minimatch('file') # => 'path/to/file-0.1.0.txt'
16
+ # paths.minimatch('file-0.2.0') # => 'path/to/file-0.2.0.txt'
17
+ # paths.minimatch('another_file') # => 'path/to/another_file.txt'
18
+ #
19
+ # More generally, Minimap may extend any object responding to each.
20
+ # Override the minikey method to convert objects into paths.
21
+ #
22
+ # class ConstantMap < Array
23
+ # include Root::Minimap
24
+ #
25
+ # def minikey(const)
26
+ # const.underscore
27
+ # end
28
+ # end
29
+ #
30
+ # constants = ConstantMap[Root::Minimap, Root]
31
+ # constants.minimatch('root') # => Root
32
+ # constants.minimatch('minimap') # => Root::Minimap
33
+ #
34
+ module Minimap
35
+
36
+ # Provides a minimized map of the entries using keys provided minikey.
37
+ #
38
+ # paths = %w{
39
+ # path/to/file-0.1.0.txt
40
+ # path/to/file-0.2.0.txt
41
+ # path/to/another_file.txt
42
+ # }.extend Root::Minimap
43
+ #
44
+ # paths.minimap
45
+ # # => [
46
+ # # ['file-0.1.0', 'path/to/file-0.1.0.txt'],
47
+ # # ['file-0.2.0', 'path/to/file-0.2.0.txt'],
48
+ # # ['another_file','path/to/another_file.txt']]
49
+ #
50
+ def minimap
51
+ hash = {}
52
+ map = []
53
+ each {|entry| map << (hash[minikey(entry)] = [entry]) }
54
+ Utils.minimize(hash.keys) do |key, mini_key|
55
+ hash[key].unshift mini_key
56
+ end
57
+
58
+ map
59
+ end
60
+
61
+ # Returns the first entry whose minikey mini-matches the input, or nil if
62
+ # no such entry exists.
63
+ #
64
+ # paths = %w{
65
+ # path/to/file-0.1.0.txt
66
+ # path/to/file-0.2.0.txt
67
+ # path/to/another_file.txt
68
+ # }.extend Root::Minimap
69
+ #
70
+ # paths.minimatch('file-0.2.0') # => 'path/to/file-0.2.0.txt'
71
+ # paths.minimatch('file-0.3.0') # => nil
72
+ #
73
+ def minimatch(key)
74
+ each do |entry|
75
+ return entry if Utils.minimal_match?(minikey(entry), key)
76
+ end
77
+ nil
78
+ end
79
+
80
+ protected
81
+
82
+ # A hook to convert a non-path entry into a path for minimap and
83
+ # minimatch. Returns the entry by default.
84
+ def minikey(entry)
85
+ entry
86
+ end
87
+ end
88
+ end