root 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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