bahuvrihi-tap 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. data/History +69 -0
  2. data/MIT-LICENSE +21 -0
  3. data/README +119 -0
  4. data/bin/tap +114 -0
  5. data/cmd/console.rb +42 -0
  6. data/cmd/destroy.rb +16 -0
  7. data/cmd/generate.rb +16 -0
  8. data/cmd/run.rb +126 -0
  9. data/doc/Class Reference +362 -0
  10. data/doc/Command Reference +153 -0
  11. data/doc/Tutorial +237 -0
  12. data/lib/tap.rb +32 -0
  13. data/lib/tap/app.rb +720 -0
  14. data/lib/tap/constants.rb +8 -0
  15. data/lib/tap/env.rb +640 -0
  16. data/lib/tap/file_task.rb +547 -0
  17. data/lib/tap/generator/base.rb +109 -0
  18. data/lib/tap/generator/destroy.rb +37 -0
  19. data/lib/tap/generator/generate.rb +61 -0
  20. data/lib/tap/generator/generators/command/command_generator.rb +21 -0
  21. data/lib/tap/generator/generators/command/templates/command.erb +32 -0
  22. data/lib/tap/generator/generators/config/config_generator.rb +26 -0
  23. data/lib/tap/generator/generators/config/templates/doc.erb +12 -0
  24. data/lib/tap/generator/generators/config/templates/nodoc.erb +8 -0
  25. data/lib/tap/generator/generators/file_task/file_task_generator.rb +27 -0
  26. data/lib/tap/generator/generators/file_task/templates/file.txt +11 -0
  27. data/lib/tap/generator/generators/file_task/templates/result.yml +6 -0
  28. data/lib/tap/generator/generators/file_task/templates/task.erb +33 -0
  29. data/lib/tap/generator/generators/file_task/templates/test.erb +29 -0
  30. data/lib/tap/generator/generators/root/root_generator.rb +55 -0
  31. data/lib/tap/generator/generators/root/templates/Rakefile +86 -0
  32. data/lib/tap/generator/generators/root/templates/gemspec +27 -0
  33. data/lib/tap/generator/generators/root/templates/tapfile +8 -0
  34. data/lib/tap/generator/generators/root/templates/test/tap_test_helper.rb +3 -0
  35. data/lib/tap/generator/generators/root/templates/test/tap_test_suite.rb +5 -0
  36. data/lib/tap/generator/generators/root/templates/test/tapfile_test.rb +15 -0
  37. data/lib/tap/generator/generators/task/task_generator.rb +27 -0
  38. data/lib/tap/generator/generators/task/templates/task.erb +14 -0
  39. data/lib/tap/generator/generators/task/templates/test.erb +21 -0
  40. data/lib/tap/generator/manifest.rb +14 -0
  41. data/lib/tap/patches/rake/rake_test_loader.rb +8 -0
  42. data/lib/tap/patches/rake/testtask.rb +55 -0
  43. data/lib/tap/patches/ruby19/backtrace_filter.rb +51 -0
  44. data/lib/tap/patches/ruby19/parsedate.rb +16 -0
  45. data/lib/tap/root.rb +581 -0
  46. data/lib/tap/support/aggregator.rb +55 -0
  47. data/lib/tap/support/assignments.rb +172 -0
  48. data/lib/tap/support/audit.rb +418 -0
  49. data/lib/tap/support/batchable.rb +47 -0
  50. data/lib/tap/support/batchable_class.rb +107 -0
  51. data/lib/tap/support/class_configuration.rb +194 -0
  52. data/lib/tap/support/command_line.rb +98 -0
  53. data/lib/tap/support/comment.rb +270 -0
  54. data/lib/tap/support/configurable.rb +114 -0
  55. data/lib/tap/support/configurable_class.rb +296 -0
  56. data/lib/tap/support/configuration.rb +122 -0
  57. data/lib/tap/support/constant.rb +70 -0
  58. data/lib/tap/support/constant_utils.rb +127 -0
  59. data/lib/tap/support/declarations.rb +111 -0
  60. data/lib/tap/support/executable.rb +111 -0
  61. data/lib/tap/support/executable_queue.rb +82 -0
  62. data/lib/tap/support/framework.rb +71 -0
  63. data/lib/tap/support/framework_class.rb +199 -0
  64. data/lib/tap/support/instance_configuration.rb +147 -0
  65. data/lib/tap/support/lazydoc.rb +428 -0
  66. data/lib/tap/support/manifest.rb +89 -0
  67. data/lib/tap/support/run_error.rb +39 -0
  68. data/lib/tap/support/shell_utils.rb +71 -0
  69. data/lib/tap/support/summary.rb +30 -0
  70. data/lib/tap/support/tdoc.rb +404 -0
  71. data/lib/tap/support/tdoc/tdoc_html_generator.rb +38 -0
  72. data/lib/tap/support/tdoc/tdoc_html_template.rb +42 -0
  73. data/lib/tap/support/templater.rb +180 -0
  74. data/lib/tap/support/validation.rb +410 -0
  75. data/lib/tap/support/versions.rb +97 -0
  76. data/lib/tap/task.rb +259 -0
  77. data/lib/tap/tasks/dump.rb +56 -0
  78. data/lib/tap/tasks/rake.rb +93 -0
  79. data/lib/tap/test.rb +37 -0
  80. data/lib/tap/test/env_vars.rb +29 -0
  81. data/lib/tap/test/file_methods.rb +377 -0
  82. data/lib/tap/test/script_methods.rb +144 -0
  83. data/lib/tap/test/subset_methods.rb +420 -0
  84. data/lib/tap/test/tap_methods.rb +237 -0
  85. data/lib/tap/workflow.rb +187 -0
  86. metadata +145 -0
@@ -0,0 +1,8 @@
1
+ module Tap
2
+ MAJOR = 0
3
+ MINOR = 10
4
+ TINY = 0
5
+
6
+ VERSION="#{MAJOR}.#{MINOR}.#{TINY}"
7
+ WEBSITE="http://tap.rubyforge.org"
8
+ end
data/lib/tap/env.rb ADDED
@@ -0,0 +1,640 @@
1
+ require 'tap/root'
2
+ require 'tap/support/constant'
3
+ require 'tap/support/summary'
4
+ require 'tap/support/manifest'
5
+
6
+ module Tap
7
+
8
+ #--
9
+ # Note that gems and env_paths reset envs -- custom modifications to envs will be lost
10
+ # whenever these configs are reset.
11
+ class Env
12
+ include Support::Configurable
13
+ include Enumerable
14
+
15
+ @@instance = nil
16
+ @@instances = {}
17
+ @@manifests = {}
18
+
19
+ class << self
20
+ # Returns the active instance of Env.
21
+ def instance
22
+ @@instance
23
+ end
24
+
25
+ # A hash of (path, Env instance) pairs, generated by Env#instantiate. Used
26
+ # to prevent infinite loops of Env dependencies by assigning a single Env
27
+ # to a given path.
28
+ def instances
29
+ @@instances
30
+ end
31
+
32
+ # Creates a new Env for the specified path and adds it to Env#instances, or
33
+ # returns the existing instance for the path. Paths can point to an env config
34
+ # file, or to a directory. If a directory is provided, instantiate treats
35
+ # path as the DEFAULT_CONFIG_FILE in that directory. All paths are expanded.
36
+ #
37
+ # e1 = Env.instantiate("./path/to/config.yml")
38
+ # e2 = Env.instantiate("./path/to/dir")
39
+ #
40
+ # Env.instances
41
+ # # => {
42
+ # # File.expand_path("./path/to/config.yml") => e1,
43
+ # # File.expand_path("./path/to/dir/#{Tap::Env::DEFAULT_CONFIG_FILE}") => e2 }
44
+ #
45
+ # The Env is initialized using configurations read from the env config file using
46
+ # load_config, and a Root initialized to the config file directory. An instance
47
+ # will be initialized regardless of whether the config file or directory exists.
48
+ def instantiate(path_or_root, default_config={}, logger=nil)
49
+ path = path_or_root.kind_of?(Root) ? path_or_root.root : path_or_root
50
+ path = pathify(path)
51
+
52
+ begin
53
+ root = path_or_root.kind_of?(Root) ? path_or_root : Root.new(File.dirname(path))
54
+ config = default_config.merge(load_config(path))
55
+
56
+ # note the assignment of env to instances MUST occur before
57
+ # reconfigure to prevent infinite looping
58
+ (instances[path] = Env.new({}, root, logger)).reconfigure(config) do |unhandled_configs|
59
+ yield(unhandled_configs) if block_given?
60
+ end
61
+ rescue(Exception)
62
+ raise Env::ConfigError.new($!, path)
63
+ end
64
+ end
65
+
66
+ def pathify(path)
67
+ if File.directory?(path) || (!File.exists?(path) && File.extname(path) == "")
68
+ path = File.join(path, DEFAULT_CONFIG_FILE)
69
+ end
70
+ File.expand_path(path)
71
+ end
72
+
73
+ def instance_for(path)
74
+ path = pathify(path)
75
+ instances.has_key?(path) ? instances[path] : instantiate(path)
76
+ end
77
+
78
+ # Returns the gemspec for the specified gem. A gem version
79
+ # can be specified in the name, like 'gem >= 1.2'. The gem
80
+ # will be activated using +gem+ if necessary.
81
+ def gemspec(gem_name)
82
+ return gem_name if gem_name.kind_of?(Gem::Specification)
83
+
84
+ # figure the version of the gem, by default >= 0.0.0
85
+ gem_name.to_s =~ /^([^<=>]*)(.*)$/
86
+ name, version = $1.strip, $2
87
+ version = ">= 0.0.0" if version.empty?
88
+
89
+ return nil if name.empty?
90
+
91
+ # load the gem and get the spec
92
+ gem(name, version)
93
+ Gem.loaded_specs[name]
94
+ end
95
+
96
+ # Returns the gem name for all installed gems with a DEFAULT_CONFIG_FILE.
97
+ # If latest==true, then only the names for the most current gem specs
98
+ # will be returned.
99
+ def known_gems(latest=true)
100
+ index = latest ?
101
+ Gem.source_index.latest_specs :
102
+ Gem.source_index.gems.collect {|(name, spec)| spec }
103
+
104
+ index.select do |spec|
105
+ File.exists?(File.join(spec.full_gem_path, DEFAULT_CONFIG_FILE)) ||
106
+ File.exists?(File.join(spec.full_gem_path, DEFAULT_TASK_FILE))
107
+ end.sort
108
+ end
109
+
110
+ protected
111
+
112
+ # Defines a config that raises an error if set when the
113
+ # instance is active. static_config MUST take a block
114
+ # and raises an error if a block is not given.
115
+ def static_config(key, value=nil, &block)
116
+ raise ArgumentError.new("active config requires block") unless block_given?
117
+
118
+ instance_variable = "@#{key}".to_sym
119
+ config_attr(key, value) do |input|
120
+ check_configurable
121
+ instance_variable_set(instance_variable, block.call(input))
122
+ end
123
+ end
124
+
125
+ # Defines a config that collects the input into a unique,
126
+ # compact array where each member has been resolved using
127
+ # root[]. In short, ['lib', nil, 'lib', 'alt] becomes
128
+ # [root['lib'], root['alt']].
129
+ #
130
+ # Single and nil arguments are allowed; they are arrayified
131
+ # and handled as above. Path configs raise an error if
132
+ # modified when the instance is active.
133
+ def path_config(key, value=[])
134
+ instance_variable = "@#{key}".to_sym
135
+ config_attr(key, value) do |input|
136
+ check_configurable
137
+ instance_variable_set(instance_variable, [*input].compact.collect {|path| root[path]}.uniq)
138
+ end
139
+ end
140
+
141
+ #--
142
+ # To manifest simply requires an glob_<name> method which
143
+ # yields each (key, path) pair for the manifested object in
144
+ # a predictable order.
145
+ #
146
+ #--
147
+ # Alternate implementation would create the manifest for each individual
148
+ # env, then merge the manifests. On the plus side, each env would then
149
+ # carry it's own slice of the manifest without having to recalculate.
150
+ # On the down side, the merging would have to occur in some separate
151
+ # method that cannot be defined here.
152
+ def manifest(name, paths_key, pattern, &block)
153
+ return manifest(name, paths_key, pattern) do |context, path|
154
+ [[path.chomp(File.extname(path)), path]]
155
+ end unless block_given?
156
+
157
+ glob_method = Support::Manifest.glob_method(name)
158
+ module_eval %Q{
159
+ def #{glob_method}
160
+ paths = []
161
+ self.#{paths_key}.each do |manifest_path|
162
+ root.glob(manifest_path, "#{pattern}").each do |path|
163
+ next if File.directory?(path)
164
+ paths << [manifest_path, path]
165
+ end
166
+ end
167
+ paths.sort_by {|mp, p| File.basename(p)}
168
+ end
169
+ }
170
+
171
+ map_method = Support::Manifest.map_method(name)
172
+ define_method(map_method, &block)
173
+
174
+ protected glob_method, map_method
175
+ end
176
+ end
177
+
178
+ # The global config file path
179
+ GLOBAL_CONFIG_FILE = File.join(Gem.user_home, ".tap.yml")
180
+
181
+ # The default config file path
182
+ DEFAULT_CONFIG_FILE = "tap.yml"
183
+
184
+ # The default task file path
185
+ DEFAULT_TASK_FILE = "tapfile.rb"
186
+
187
+ # The Root directory structure for self.
188
+ attr_reader :root
189
+
190
+ # Gets or sets the logger for self
191
+ attr_accessor :logger
192
+
193
+ # A hash of the manifests for self.
194
+ attr_reader :manifests
195
+
196
+ # Specify gems to load as nested Envs. Gems may be specified
197
+ # by name and/or version, like 'gemname >= 1.2'; by default the
198
+ # latest version of the gem is selected.
199
+ #
200
+ # Gems are immediately loaded (via gem) through this method.
201
+ #--
202
+ # Note that the gems are resolved to gemspecs using Env.gemspec,
203
+ # so self.gems returns an array of gemspecs.
204
+ config_attr :gems, [] do |input|
205
+ check_configurable
206
+ @gems = [*input].compact.collect do |gem_name|
207
+ spec = Env.gemspec(gem_name)
208
+
209
+ case spec
210
+ when nil then log(:warn, "unknown gem: #{gem_name}", Logger::WARN)
211
+ else Env.instance_for(spec.full_gem_path)
212
+ end
213
+
214
+ spec
215
+ end.uniq
216
+ reset_envs
217
+ end
218
+
219
+ # Specify configuration files to load as nested Envs.
220
+ config_attr :env_paths, [] do |input|
221
+ check_configurable
222
+ @env_paths = [*input].compact.collect do |path|
223
+ Env.instance_for(root[path]).env_path
224
+ end.uniq
225
+ reset_envs
226
+ end
227
+
228
+ # Designate load paths. If use_dependencies == true, then
229
+ # load_paths will be used for automatic loading of modules
230
+ # through the active_support Dependencies module.
231
+ path_config :load_paths, ["lib"]
232
+
233
+ # Designate paths for discovering and executing commands.
234
+ path_config :command_paths, ["cmd"]
235
+
236
+ # Designate paths for discovering generators.
237
+ path_config :generator_paths, ["lib"]
238
+
239
+ manifest(:tasks, :load_paths, "**/*.rb") do |load_path, path|
240
+ next unless document = Support::Lazydoc.scan_doc(path, 'manifest')
241
+
242
+ document.const_names.collect do |const_name|
243
+ if const_name.empty?
244
+ key = root.relative_filepath(load_path, path).chomp('.rb')
245
+ [key, Support::Constant.new(key.camelize, path)]
246
+ else
247
+ [const_name.underscore, Support::Constant.new(const_name, path)]
248
+ end
249
+ end
250
+ end
251
+
252
+ manifest(:commands, :command_paths, "**/*.rb")
253
+
254
+ manifest(:generators, :generator_paths, '**/*_generator.rb') do |load_path, path|
255
+ dirname = File.dirname(path)
256
+ next unless "#{File.basename(dirname)}_generator.rb" == File.basename(path)
257
+
258
+ next unless document = Support::Lazydoc.scan_doc(path, 'generator')
259
+ document.const_names.collect do |const_name|
260
+ if const_name.empty?
261
+ key = root.relative_filepath(load_path, dirname)
262
+ [key, Support::Constant.new((key + '_generator').camelize, path)]
263
+ else
264
+ [const_name.underscore, Support::Constant.new(const_name, path)]
265
+ end
266
+ end
267
+ end
268
+
269
+ def initialize(config={}, root=Tap::Root.new, logger=nil)
270
+ @root = root
271
+ @logger = logger
272
+ @envs = []
273
+ @active = false
274
+ @manifests = {}
275
+ @manifested = []
276
+
277
+ # initialize these for reset_env
278
+ @gems = []
279
+ @env_paths = []
280
+
281
+ initialize_config(config)
282
+ end
283
+
284
+ # Sets envs removing duplicates and instances of self.
285
+ def envs=(envs)
286
+ @envs = envs.uniq.delete_if {|e| e == self }
287
+ @envs.freeze
288
+ @flat_envs = nil
289
+ end
290
+
291
+ # An array of nested Envs, by default comprised of the
292
+ # env_path + gem environments (in that order). These
293
+ # nested Envs are activated/deactivated with self.
294
+ #
295
+ # Returns a flattened array of the unique nested envs
296
+ # when flat == true.
297
+ def envs(flat=false)
298
+ flat ? (@flat_envs ||= self.flatten_envs.freeze) : @envs
299
+ end
300
+
301
+ # Unshifts env onto envs, removing duplicates.
302
+ # Self cannot be unshifted onto self.
303
+ def unshift(env)
304
+ unless env == self || envs[0] == env
305
+ self.envs = envs.dup.unshift(env)
306
+ end
307
+ envs
308
+ end
309
+
310
+ # Pushes env onto envs, removing duplicates.
311
+ # Self cannot be pushed onto self.
312
+ def push(env)
313
+ unless env == self || envs[-1] == env
314
+ envs = self.envs.reject {|e| e == env }
315
+ self.envs = envs.push(env)
316
+ end
317
+ envs
318
+ end
319
+
320
+ # Passes each nested env to the block in order, starting with self.
321
+ def each
322
+ envs(true).each {|e| yield(e) }
323
+ end
324
+
325
+ # Passes each nested env to the block in reverse order, ending with self.
326
+ def reverse_each
327
+ envs(true).reverse_each {|e| yield(e) }
328
+ end
329
+
330
+ # Returns the total number of unique envs nested in self (including self).
331
+ def count
332
+ envs(true).length
333
+ end
334
+
335
+ # Returns a list of arrays that receive load_paths on activate,
336
+ # by default [$LOAD_PATH]. If use_dependencies == true, then
337
+ # Dependencies.load_paths will also be included.
338
+ def load_path_targets
339
+ [$LOAD_PATH]
340
+ end
341
+
342
+ # Processes and resets the input configurations for both root
343
+ # and self. Reconfiguration consists of the following steps:
344
+ #
345
+ # * partition overrides into env, root, and other configs
346
+ # * reconfigure root with the root configs
347
+ # * reconfigure self with the env configs
348
+ # * yield other configs to the block (if given)
349
+ #
350
+ # Reconfigure will always yields to the block, even if there
351
+ # are no non-root, non-env configurations. Unspecified
352
+ # configurations are NOT reconfigured. (Note this means
353
+ # that existing path configurations like load_paths will
354
+ # not automatically be reset using reconfigured root.)
355
+ def reconfigure(overrides={})
356
+ check_configurable
357
+
358
+ # partiton config into its parts
359
+ env_configs = {}
360
+ root_configs = {}
361
+ other_configs = {}
362
+
363
+ env_configurations = self.class.configurations
364
+ root_configurations = root.class.configurations
365
+ overrides.each_pair do |key, value|
366
+ key = key.to_sym
367
+
368
+ partition = case
369
+ when env_configurations.key?(key) then env_configs
370
+ when root_configurations.key?(key) then root_configs
371
+ else other_configs
372
+ end
373
+
374
+ partition[key] = value
375
+ end
376
+
377
+ # reconfigure root so it can resolve path_configs
378
+ root.reconfigure(root_configs)
379
+
380
+ # reconfigure self
381
+ super(env_configs)
382
+
383
+ # handle other configs
384
+ case
385
+ when block_given?
386
+ yield(other_configs)
387
+ when !other_configs.empty?
388
+ log(:warn, "ignoring non-env configs: #{other_configs.keys.join(',')}", Logger::DEBUG)
389
+ end
390
+
391
+ self
392
+ end
393
+
394
+ # Returns the path for self in Env.instances.
395
+ def env_path
396
+ Env.instances.each_pair {|path, env| return path if env == self }
397
+ nil
398
+ end
399
+
400
+ # Logs the action and message at the input level (default INFO).
401
+ # Logging is suppressed if no logger is set.
402
+ def log(action, msg="", level=Logger::INFO)
403
+ logger.add(level, msg, action.to_s) if logger
404
+ end
405
+
406
+ # Activates self by unshifting load_paths for self to the load_path_targets.
407
+ # Once active, self can be referenced from Env.instance and the current
408
+ # configurations are frozen. Env.instance is deactivated, if set, before
409
+ # self is activated. Returns true if activate succeeded, or false if self
410
+ # is already active.
411
+ def activate
412
+ return false if active?
413
+
414
+ @active = true
415
+ @@instance = self unless @@instance
416
+
417
+ # freeze array configs like load_paths
418
+ config.each_pair do |key, value|
419
+ case value
420
+ when Array then value.freeze
421
+ end
422
+ end
423
+
424
+ # activate nested envs
425
+ envs.reverse_each do |env|
426
+ env.activate
427
+ end
428
+
429
+ # add load paths to load_path_targets
430
+ load_path_targets.each do |target|
431
+ load_paths.reverse_each do |path|
432
+ target.unshift(path)
433
+ end
434
+
435
+ target.uniq!
436
+ end
437
+
438
+ true
439
+ end
440
+
441
+ # Deactivates self by clearing manifests and deleting load_paths for self
442
+ # from the load_path_targets. Env.instance will no longer reference self
443
+ # and the configurations are unfrozen (using duplication).
444
+ #
445
+ # Returns true if deactivate succeeded, or false if self is not active.
446
+ def deactivate
447
+ return false unless active?
448
+
449
+ # remove load paths from load_path_targets
450
+ load_path_targets.each do |target|
451
+ load_paths.each do |path|
452
+ target.delete(path)
453
+ end
454
+ end
455
+
456
+ # unfreeze array configs by duplicating
457
+ self.config.class_config.each_pair do |key, value|
458
+ value = send(key)
459
+ case value
460
+ when Array then instance_variable_set("@#{key}", value.dup)
461
+ end
462
+ end
463
+
464
+ @active = false
465
+ @manifests.clear
466
+ @@instance = nil if @@instance == self
467
+
468
+ # dectivate nested envs
469
+ envs.reverse_each do |env|
470
+ env.deactivate
471
+ end
472
+
473
+ true
474
+ end
475
+
476
+ # Return true if self has been activated.
477
+ def active?
478
+ @active
479
+ end
480
+
481
+ # Cycles through all items yielded by the iterate_<name> method and
482
+ # adds each to the manifests[name] hash. Freezes the hash when complete.
483
+ # Simply returns the manifests[name] hash if frozen.
484
+ def manifest(name)
485
+ manifest = manifests[name] ||= Support::Manifest.new(name, self)
486
+
487
+ manifest.entries.each do |key, path|
488
+ yield(key, path)
489
+ end if block_given?
490
+
491
+ manifest.each_path do |context, path|
492
+ next unless keys = send(manifest.map_method, context, path)
493
+
494
+ keys.each {|entry| manifest.store(entry) }
495
+ keys.each {|key, value| yield(key, value) } if block_given?
496
+ end unless manifest.complete?
497
+
498
+ manifest
499
+ end
500
+
501
+ def find(name, pattern)
502
+ manifest(name) do |key, path|
503
+ return path if Root.minimal_match?(key, pattern)
504
+ end
505
+ nil
506
+ end
507
+
508
+ def search(name, pattern)
509
+ return find(name, pattern) if name == :envs
510
+
511
+ envs = case pattern
512
+ when /^(.*):([^:]+)$/
513
+ env_pattern = $1
514
+ pattern = $2
515
+ find(:envs, env_pattern) or raise(ArgumentError, "could not find env: #{env_pattern}")
516
+ else manifest(:envs).values
517
+ end
518
+
519
+ envs.each do |env|
520
+ if result = env.find(name, pattern)
521
+ return result
522
+ end
523
+ end
524
+
525
+ nil
526
+ end
527
+
528
+ def summary(name)
529
+ summary = Support::Summary.new
530
+ manifest(:envs).mini_map.each do |(key, env)|
531
+ summary.add(key, env, env.manifest(name).mini_map)
532
+ end
533
+ summary
534
+ end
535
+
536
+ def summarize(name, &block)
537
+ lines = summary(name).lines(&block)
538
+ lines << "=== no #{name} found" if lines.empty?
539
+ lines.join("\n")
540
+ end
541
+
542
+ def inspect(brief=false)
543
+ brief ? "#<#{self.class}:#{object_id} root='#{root.root}'>" : super()
544
+ end
545
+
546
+ def to_s
547
+ inspect(true)
548
+ end
549
+
550
+ #--
551
+ # Under construction
552
+ #++
553
+
554
+ def handle_error(err)
555
+ case
556
+ when $DEBUG
557
+ puts err.message
558
+ puts
559
+ puts err.backtrace
560
+ else puts err.message
561
+ end
562
+ end
563
+
564
+ protected
565
+
566
+ # Iterates over each nested env, yielding the root path and env.
567
+ # This is the manifest method for envs.
568
+ def manifest_glob_envs
569
+ collect {|env| [env.root.root, env] }.sort_by {|root, env| File.basename(root) }
570
+ end
571
+
572
+ def manifest_map(context, path)
573
+ [[context, path]]
574
+ end
575
+
576
+ alias default_manifest_glob_tasks manifest_glob_tasks
577
+
578
+ def manifest_glob_tasks
579
+ paths = default_manifest_glob_tasks
580
+
581
+ # very odd behaviors --
582
+ # * OS X is case-insensitive, apparently. Tapfile.rb and tapfile.rb are the same.
583
+ # * require 'tapfile' does not work
584
+ # * require 'tapfile.rb' works
585
+ # * load 'tapfile' works
586
+ #
587
+ root.glob(:root, DEFAULT_TASK_FILE).each do |path|
588
+ next if File.directory?(path)
589
+ paths.unshift [root.root, path]
590
+ end
591
+ paths
592
+ end
593
+
594
+ # Raises an error if self is already active (and hence, configurations
595
+ # should not be modified)
596
+ def check_configurable
597
+ raise "path configurations are disabled when active" if active?
598
+ end
599
+
600
+ # Resets envs using the current env_paths and gems.
601
+ def reset_envs
602
+ self.envs = env_paths.collect do |path|
603
+ Env.instance_for(path)
604
+ end + gems.collect do |spec|
605
+ Env.instance_for(spec.full_gem_path)
606
+ end
607
+ end
608
+
609
+ # Recursively iterates through envs collecting all envs into
610
+ # the target. The result is a unique array of all nested
611
+ # envs, in order, beginning with self.
612
+ def flatten_envs(target=[])
613
+ unless target.include?(self)
614
+ target << self
615
+ envs.each do |env|
616
+ env.flatten_envs(target)
617
+ end
618
+ end
619
+
620
+ target
621
+ end
622
+
623
+ # Raised when there is a Env-level configuration error.
624
+ class ConfigError < StandardError
625
+ attr_reader :original_error, :env_path
626
+
627
+ def initialize(original_error, env_path)
628
+ @original_error = original_error
629
+ @env_path = env_path
630
+ super()
631
+ end
632
+
633
+ def message
634
+ "Configuration error: #{original_error.message}\n" +
635
+ ($DEBUG ? "#{original_error.backtrace}\n" : "") +
636
+ "Check '#{env_path}' configurations"
637
+ end
638
+ end
639
+ end
640
+ end