bahuvrihi-tap 0.11.2 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. data/bin/rap +2 -3
  2. data/bin/tap +1 -1
  3. data/cmd/console.rb +2 -2
  4. data/cmd/manifest.rb +2 -2
  5. data/cmd/run.rb +7 -9
  6. data/cmd/server.rb +5 -5
  7. data/doc/Class Reference +17 -20
  8. data/doc/Tutorial +5 -7
  9. data/lib/tap.rb +2 -0
  10. data/lib/tap/app.rb +21 -31
  11. data/lib/tap/constants.rb +2 -2
  12. data/lib/tap/declarations.rb +85 -97
  13. data/lib/tap/declarations/declaration_task.rb +58 -0
  14. data/lib/tap/declarations/description.rb +24 -0
  15. data/lib/tap/env.rb +20 -16
  16. data/lib/tap/exe.rb +2 -2
  17. data/lib/tap/file_task.rb +224 -410
  18. data/lib/tap/generator/arguments.rb +9 -0
  19. data/lib/tap/generator/base.rb +105 -28
  20. data/lib/tap/generator/destroy.rb +29 -12
  21. data/lib/tap/generator/generate.rb +55 -39
  22. data/lib/tap/generator/generators/command/templates/command.erb +3 -3
  23. data/lib/tap/generator/generators/config/config_generator.rb +34 -3
  24. data/lib/tap/generator/generators/root/root_generator.rb +6 -9
  25. data/lib/tap/generator/generators/root/templates/Rakefile +4 -4
  26. data/lib/tap/generator/generators/task/templates/test.erb +1 -1
  27. data/lib/tap/root.rb +211 -156
  28. data/lib/tap/support/aggregator.rb +6 -9
  29. data/lib/tap/support/audit.rb +278 -357
  30. data/lib/tap/support/constant_manifest.rb +24 -21
  31. data/lib/tap/support/dependency.rb +1 -1
  32. data/lib/tap/support/executable.rb +26 -48
  33. data/lib/tap/support/join.rb +44 -19
  34. data/lib/tap/support/joins/sync_merge.rb +3 -5
  35. data/lib/tap/support/parser.rb +1 -1
  36. data/lib/tap/task.rb +195 -150
  37. data/lib/tap/tasks/dump.rb +2 -2
  38. data/lib/tap/test/extensions.rb +11 -13
  39. data/lib/tap/test/file_test.rb +71 -129
  40. data/lib/tap/test/file_test_class.rb +4 -1
  41. data/lib/tap/test/tap_test.rb +26 -154
  42. metadata +15 -22
  43. data/lib/tap/patches/optparse/summarize.rb +0 -62
  44. data/lib/tap/support/assignments.rb +0 -173
  45. data/lib/tap/support/class_configuration.rb +0 -182
  46. data/lib/tap/support/configurable.rb +0 -113
  47. data/lib/tap/support/configurable_class.rb +0 -271
  48. data/lib/tap/support/configuration.rb +0 -170
  49. data/lib/tap/support/instance_configuration.rb +0 -173
  50. data/lib/tap/support/lazydoc.rb +0 -386
  51. data/lib/tap/support/lazydoc/attributes.rb +0 -48
  52. data/lib/tap/support/lazydoc/comment.rb +0 -503
  53. data/lib/tap/support/lazydoc/config.rb +0 -17
  54. data/lib/tap/support/lazydoc/definition.rb +0 -36
  55. data/lib/tap/support/lazydoc/document.rb +0 -152
  56. data/lib/tap/support/lazydoc/method.rb +0 -24
  57. data/lib/tap/support/tdoc.rb +0 -409
  58. data/lib/tap/support/tdoc/tdoc_html_generator.rb +0 -38
  59. data/lib/tap/support/tdoc/tdoc_html_template.rb +0 -42
  60. data/lib/tap/support/validation.rb +0 -479
@@ -0,0 +1,9 @@
1
+ module Tap
2
+ module Generator
3
+ class Arguments < Lazydoc::Arguments
4
+ def arguments(shift_manifest_arg=true)
5
+ shift_manifest_arg ? @arguments[1..-1] : @arguments
6
+ end
7
+ end
8
+ end
9
+ end
@@ -1,33 +1,95 @@
1
1
  require 'tap/generator/manifest'
2
+ require 'tap/generator/arguments'
2
3
 
3
4
  module Tap
4
- module Generator
5
+ module Generator
6
+
7
+ # :startdoc:::-
8
+ # Base provides the basic structure of a generator and custom generators
9
+ # inherit from it. Base is patterned after the {Ruby on Rails}[http://rubyonrails.org/]
10
+ # generators, but obviously takes on all the advantages of Tasks.
11
+ #
12
+ # === Usage
13
+ #
14
+ # Tap generators define a manifest method that defines what files and
15
+ # directories are created by the generator. Then, at execution time,
16
+ # a mixin with the appropriate funtion (ie Generate or Destory) is
17
+ # overlaid to figure out how to roll those actions forward or backwards.
18
+ #
19
+ # Unlike typical tasks, generators must be named like '<Name>Generator' and
20
+ # are identified using the ::generator flag rather than ::manifest. These
21
+ # requirements make generators available to the generate/destroy commands
22
+ # and not run.
23
+ #
24
+ # Typically, generators live in a directory structure like this:
25
+ #
26
+ # sample
27
+ # |- sample_generator.rb
28
+ # `- templates
29
+ # `- template_file.erb
30
+ #
31
+ # And take the form:
32
+ #
33
+ # [sample/sample_generator.rb]
34
+ # require 'tap/generator/base'
35
+ #
36
+ # # SampleGenerator::generator generates a directory, and two files
37
+ # #
38
+ # # An extended description of the
39
+ # # generator goes here...
40
+ # #
41
+ # class SampleGenerator < Tap::Generator::Base
42
+ #
43
+ # config :key, 'value' # a sample config
44
+ #
45
+ # def manifest(m, *args)
46
+ # # make a directory
47
+ # m.directory('path/to/dir')
48
+ #
49
+ # # make a file
50
+ # m.file('path/to/file.txt') do |file|
51
+ # file << "some content"
52
+ # end
53
+ #
54
+ # # template a file using config
55
+ # m.template('path/to/result.txt', 'template_file.erb', config.to_hash)
56
+ # end
57
+ # end
58
+ #
59
+ # As with any task, generators can have configurations and take arguments
60
+ # specified by manifest (minus the 'm' argument which is standard).
61
+ # Creating directories and files is straightforward, as above. The
62
+ # template function a target file using the source file in the
63
+ # 'templates' directory; any attributes specified by the last argument will
64
+ # be available in the erb template.
65
+ # :startdoc:::+
5
66
  class Base < Tap::Task
6
- class << self
7
- def lazydoc(resolve=true)
8
- lazydoc = super(false)
9
- lazydoc[self.to_s]['args'] ||= lazydoc.register_method(:manifest, Task::Args)
10
- super
11
- end
12
- end
13
-
14
- Constant = Tap::Support::Constant
15
-
16
- lazy_attr :manifest, :generator
67
+ lazy_attr :manifest, 'generator'
68
+ lazy_attr :args, :manifest
69
+ lazy_register :manifest, Arguments
17
70
 
18
71
  config :pretend, false, &c.flag # Run but rollback any changes.
19
72
  config :force, false, &c.flag # Overwrite files that already exist.
20
73
  config :skip, false, &c.flag # Skip files that already exist.
21
74
 
22
- attr_accessor :file_task, :template_dir, :target_dir
75
+ # The generator-specific templates directory. By default:
76
+ # 'path/to/name/templates' for 'path/to/name/name_generator.rb'
77
+ attr_accessor :template_dir
23
78
 
24
- def initialize(config={}, name=nil, app=App.instance)
25
- super(config, name, app)
26
-
27
- @file_task = Tap::FileTask.new
79
+ # The IO used to pull prompt inputs (default: $stdin)
80
+ attr_accessor :prompt_in
81
+
82
+ # The IO used to prompt users for input (default: $stdout)
83
+ attr_accessor :prompt_out
84
+
85
+ def initialize(*args)
86
+ super
87
+ @prompt_in = $stdin
88
+ @prompt_out = $stdout
28
89
  @template_dir = File.dirname(self.class.source_file) + '/templates'
29
90
  end
30
91
 
92
+ # Builds the manifest, then executes the actions of the manifest.
31
93
  def process(*argv)
32
94
  actions = []
33
95
  manifest(Manifest.new(actions), *argv)
@@ -35,38 +97,45 @@ module Tap
35
97
  iterate(actions) do |action, args, block|
36
98
  send(action, *args, &block)
37
99
  end
38
-
39
- @target_dir = nil
40
- file_task.added_files
41
- end
42
-
43
- def log_relative(action, path)
44
- log(action, app.relative_filepath(Dir.pwd, path))
45
100
  end
46
101
 
102
+ # Overridden in subclasses to add actions to the input Manifest.
103
+ # Any arguments passed to process will be passed to manifest
104
+ # unchanged.
47
105
  def manifest(m, *argv)
48
106
  raise NotImplementedError
49
107
  end
50
108
 
51
- def iterate(action)
109
+ # Peforms each of the input actions. Overridden by one of the
110
+ # action mixins (ex Generate or Destory).
111
+ def iterate(actions)
52
112
  raise NotImplementedError
53
113
  end
54
-
114
+
115
+ # Peforms a directory action (ex generate or destroy). Must be
116
+ # overridden by one of the action mixins (ex Generate or Destroy).
55
117
  def directory(target, options={})
56
118
  raise NotImplementedError
57
119
  end
58
120
 
121
+ # Peforms a file action (ex generate or destroy). Must be
122
+ # overridden by one of the action mixins (ex Generate or Destroy).
59
123
  def file(target, options={})
60
124
  raise NotImplementedError
61
125
  end
62
126
 
127
+ # Makes (or destroys) the root and each of the targets, relative
128
+ # to root. Options are passed onto directory.
63
129
  def directories(root, targets, options={})
64
- directory(root)
130
+ directory(root, options)
65
131
  targets.each do |target|
66
132
  directory(File.join(root, target), options)
67
133
  end
68
134
  end
69
135
 
136
+ # Makes (or destroys) the target by templating the source using
137
+ # the specified attributes. Source is expanded relative to
138
+ # template_dir. Options are passed onto file.
70
139
  def template(target, source, attributes={}, options={})
71
140
  template_path = File.expand_path(source, template_dir)
72
141
  templater = Support::Templater.new(File.read(template_path), attributes)
@@ -76,13 +145,21 @@ module Tap
76
145
  end
77
146
  end
78
147
 
148
+ # Yields each source file under template_dir to the block, with
149
+ # a target path of the source relative to template_dir.
79
150
  def template_files
80
151
  Dir.glob(template_dir + "/**/*").sort.each do |source|
152
+ next unless File.file?(source)
153
+
81
154
  target = Tap::Root.relative_filepath(template_dir, source)
82
155
  yield(source, target)
83
156
  end
84
157
  end
85
-
158
+
159
+ # Logs the action with the relative filepath from Dir.pwd to path.
160
+ def log_relative(action, path)
161
+ log(action, Root.relative_filepath(Dir.pwd, path))
162
+ end
86
163
  end
87
164
  end
88
165
  end
@@ -1,34 +1,51 @@
1
1
  module Tap
2
2
  module Generator
3
+
4
+ # A mixin defining how to run manifest actions in reverse.
3
5
  module Destroy
6
+
7
+ # Iterates over the actions in reverse.
4
8
  def iterate(actions)
5
9
  actions.reverse_each {|action| yield(action) }
6
10
  end
7
11
 
12
+ # Removes the target directory if it exists. Missing, non-directory and
13
+ # non-empty targets are simply logged and not removed. When pretend is
14
+ # true, removal is logged but does not actually happen.
15
+ #
16
+ # No options currently affect the behavior of this method.
8
17
  def directory(target, options={})
9
- target = File.expand_path(target, target_dir)
18
+ target = File.expand_path(target)
10
19
 
11
20
  case
12
21
  when !File.exists?(target)
13
22
  log_relative :missing, target
14
- when !file_task.dir_empty?(target)
23
+ when !File.directory?(target)
24
+ log_relative 'not a directory', target
25
+ when !Root.empty?(target)
15
26
  log_relative 'not empty', target
16
27
  else
17
28
  log_relative :rm, target
18
- file_task.added_files << File.expand_path(target)
19
- file_task.rmdir(target) unless pretend
29
+ FileUtils.rmdir(target) unless pretend
20
30
  end
21
31
  end
22
32
 
33
+ # Removes the target file if it exists. Missing and non-file and targets
34
+ # are simply logged and not removed. When pretend is true, removal is
35
+ # logged but does not actually happen.
36
+ #
37
+ # No options currently affect the behavior of this method.
23
38
  def file(target, options={})
24
- target = File.expand_path(target, target_dir)
25
-
26
- if File.exists?(target)
27
- log_relative :rm, target
28
- file_task.added_files << File.expand_path(target)
29
- file_task.rm(target) unless pretend
30
- else
31
- log_relative :missing, target
39
+ target = File.expand_path(target)
40
+
41
+ case
42
+ when File.file?(target)
43
+ log_relative :rm, target
44
+ FileUtils.rm(target) unless pretend
45
+ when File.directory?(target)
46
+ log_relative 'not a file', target
47
+ else
48
+ log_relative :missing, target
32
49
  end
33
50
  end
34
51
 
@@ -1,66 +1,82 @@
1
1
  module Tap
2
- module Generator
2
+ module Generator
3
+
4
+ # A mixin defining how to run manifest actions.
3
5
  module Generate
6
+
7
+ # Iterates over the actions in order.
4
8
  def iterate(actions)
5
9
  actions.each {|action| yield(action) }
6
10
  end
7
-
11
+
12
+ # Creates the target directory if it doesn't exist. When pretend is
13
+ # true, creation is logged but does not actually happen.
14
+ #
15
+ # No options currently affect the behavior of this method.
8
16
  def directory(target, options={})
9
- target = File.expand_path(target, target_dir)
17
+ target = File.expand_path(target)
10
18
 
11
- if File.exists?(target)
19
+ case
20
+ when File.exists?(target)
12
21
  log_relative :exists, target
13
22
  else
14
23
  log_relative :create, target
15
- file_task.mkdir(target) unless pretend
24
+ FileUtils.mkdir_p(target) unless pretend
16
25
  end
17
26
  end
18
-
19
- def file(target, options={})
20
- source_file = Tempfile.new('generate')
21
- yield(source_file) if block_given?
22
- source_file.close
23
-
24
- source = source_file.path
25
- target = File.expand_path(target, target_dir)
26
-
27
- copy_file = case
28
- when !File.exists?(target)
29
- log_relative :create, target
30
- true
31
- when FileUtils.cmp(source, target)
32
- log_relative :exists, target
33
- false
34
- when force_file_collision?(target)
35
- log_relative :force, target
36
- true
37
- else
38
- log_relative :skip, target
39
- false
40
- end
41
-
42
- if copy_file && !pretend
43
- file_task.prepare(target)
44
- FileUtils.mv(source, target)
27
+
28
+ # Creates the target file; content may be added to the file by providing
29
+ # block. If the target file already exists, the new and existing content
30
+ # is compared and the user will be prompted for how to handle collisions.
31
+ # All activity is logged. When pretend is true, creation is logged but
32
+ # does not actually happen.
33
+ #
34
+ # No options currently affect the behavior of this method.
35
+ def file(target, options={})
36
+ source_file = Tempfile.new('generate')
37
+ yield(source_file) if block_given?
38
+ source_file.close
39
+
40
+ source = source_file.path
41
+ target = File.expand_path(target)
42
+
43
+ copy_file = true
44
+ msg = case
45
+ when !File.exists?(target)
46
+ :create
47
+ when FileUtils.cmp(source, target)
48
+ :exists
49
+ when force_file_collision?(target)
50
+ :force
51
+ else
52
+ copy_file = false
53
+ :skip
54
+ end
55
+
56
+ log_relative msg, target
57
+ if copy_file && !pretend
58
+ dir = File.dirname(target)
59
+ FileUtils.mkdir_p(dir) unless File.exists?(dir)
60
+ FileUtils.mv(source, target, :force => true)
45
61
  end
46
- end
47
-
48
- protected
62
+ end
63
+
64
+ protected
49
65
 
50
66
  # Ask the user interactively whether to force collision.
51
67
  def force_file_collision?(target)
52
68
  return false if skip
53
69
  return true if force
54
70
 
55
- $stdout.print "overwrite #{target}? [Ynaiq] "
56
- $stdout.flush
57
- case $stdin.gets
71
+ prompt_out.print "overwrite #{target}? [Ynaiq] "
72
+ prompt_out.flush
73
+ case prompt_in.gets
58
74
  when /a/i
59
75
  self.force = true
60
76
  when /i/i
61
77
  self.skip = true
62
78
  when /q/i
63
- $stdout.puts "aborting #{name}"
79
+ prompt_out.puts "aborting #{name}"
64
80
  raise SystemExit
65
81
  when /n/i then false
66
82
  when /y/i then true
@@ -11,12 +11,12 @@ app = Tap::App.instance
11
11
  # handle options
12
12
  #
13
13
 
14
- OptionParser.new do |opts|
14
+ ConfigParser.new do |opts|
15
15
  opts.separator ""
16
16
  opts.separator "options:"
17
17
 
18
18
  opts.on("-h", "--help", "Show this message") do
19
- opts.banner = Tap::Support::Lazydoc.usage(__FILE__)
19
+ puts Lazydoc.usage(__FILE__)
20
20
  puts opts
21
21
  exit
22
22
  end
@@ -29,4 +29,4 @@ end.parse!(ARGV)
29
29
  #
30
30
 
31
31
  puts "Received: #{ARGV.join(', ')}"
32
- puts app.info
32
+ puts app.info
@@ -17,10 +17,41 @@ module Tap::Generator::Generators
17
17
  task_class = const.constantize or raise "unknown task: #{name}"
18
18
 
19
19
  m.directory app['config']
20
- m.file app.filepath('config', config_name + '.yml') do |file|
21
- task_class.configurations.inspect((doc ? :doc : :nodoc), file)
22
- end
20
+ dump(m, app.filepath('config', config_name), task_class.configurations)
21
+ end
22
+
23
+ def template
24
+ path = File.expand_path(doc ? 'doc.erb' : 'nodoc.erb', template_dir)
25
+ File.read(path)
23
26
  end
24
27
 
28
+ def dump(m, path, configurations, &block)
29
+ non_nested_configurations = configurations.to_a.sort_by do |(key, config)|
30
+ config.attributes[:declaration_order] || 0
31
+ end.collect do |(key, config)|
32
+ default = config.default(false)
33
+
34
+ if default.kind_of?(Configurable::DelegateHash)
35
+ # nest nested configs
36
+ dump(m, File.join(path, key), default, &block)
37
+ nil
38
+ else
39
+ # duplicate config so that any changes to it
40
+ # during templation will not propogate back
41
+ # into configurations
42
+ [key, config.dup]
43
+ end
44
+ end.compact
45
+
46
+ if block_given?
47
+ yield(non_nested_configurations)
48
+ end
49
+
50
+ m.file "#{path}.yml" do |file|
51
+ templater = Tap::Support::Templater.new(template)
52
+ templater.configurations = non_nested_configurations
53
+ file << templater.build
54
+ end
55
+ end
25
56
  end
26
57
  end