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
data/bin/rap CHANGED
@@ -73,16 +73,15 @@ begin
73
73
  <% entries.each do |name, const| %>
74
74
  <% desc = if const.require_path == nil # should be a declaration %>
75
75
  <% manifest = const.constantize.manifest %>
76
- <% next if manifest == nil || manifest.subject.to_s.empty? %>
76
+ <% next if manifest == nil || manifest.empty? %>
77
77
  <% manifest %>
78
78
  <% else %>
79
79
  <% const.document[const.name]['manifest'] %>
80
80
  <% end %>
81
- <% desc = desc.subject.to_s if desc.kind_of?(Tap::Support::Lazydoc::Comment) %>
82
81
  <%= name.ljust(width) %><%= desc.empty? ? '' : ' # ' %><%= desc %>
83
82
  <% end %>}
84
83
 
85
- puts Tap::Support::Lazydoc.usage(__FILE__)
84
+ puts Lazydoc.usage(__FILE__)
86
85
  puts
87
86
  puts "=== tap tasks ==="
88
87
  puts env.summarize(:tasks, rap_template)
data/bin/tap CHANGED
@@ -59,7 +59,7 @@ end
59
59
  begin
60
60
  env.activate
61
61
  env.execute(ARGV) do
62
- puts Tap::Support::Lazydoc.usage(__FILE__)
62
+ puts Lazydoc.usage(__FILE__)
63
63
  puts
64
64
  puts "available commands:"
65
65
  puts env.summarize(:commands)
@@ -7,12 +7,12 @@
7
7
  # handle options
8
8
  #
9
9
 
10
- OptionParser.new do |opts|
10
+ ConfigParser.new do |opts|
11
11
  opts.separator ""
12
12
  opts.separator "options:"
13
13
 
14
14
  opts.on("-h", "--help", "Show this message") do
15
- opts.banner = Tap::Support::Lazydoc.usage(__FILE__)
15
+ puts Lazydoc.usage(__FILE__)
16
16
  puts opts
17
17
  exit
18
18
  end
@@ -4,13 +4,13 @@
4
4
  #
5
5
 
6
6
  options = {}
7
- OptionParser.new do |opts|
7
+ ConfigParser.new do |opts|
8
8
 
9
9
  opts.separator ""
10
10
  opts.separator "options:"
11
11
 
12
12
  opts.on("-h", "--help", "Show this message") do
13
- opts.banner = Tap::Support::Lazydoc.usage(__FILE__)
13
+ puts Lazydoc.usage(__FILE__)
14
14
  puts opts
15
15
  exit
16
16
  end
data/cmd/run.rb CHANGED
@@ -13,24 +13,22 @@ app = Tap::App.instance
13
13
  #
14
14
 
15
15
  dump = false
16
- OptionParser.new do |opts|
16
+ ConfigParser.new do |opts|
17
17
  opts.separator ""
18
18
  opts.separator "configurations:"
19
19
 
20
- Tap::App.configurations.each do |receiver, key, config|
21
- next if receiver == Tap::Root
22
-
23
- opts.on(*config.to_optparse_argv) do |value|
24
- app.send(config.writer, value)
25
- end
20
+ root_keys = Tap::Root.configurations.keys
21
+ Tap::App.configurations.each_pair do |key, config|
22
+ next if root_keys.include?(key)
23
+ opts.define(key, config.default, config.attributes)
26
24
  end
27
25
 
28
26
  opts.separator ""
29
27
  opts.separator "options:"
30
-
28
+
31
29
  opts.on("-h", "--help", "Show this message") do
32
- opts.banner = Tap::Support::Lazydoc.usage(__FILE__)
33
30
  Tap::App.lazydoc.resolve
31
+ puts Lazydoc.usage(__FILE__)
34
32
  puts opts
35
33
  exit
36
34
  end
@@ -11,22 +11,22 @@ env = Tap::Env.instance
11
11
  # handle options
12
12
  #
13
13
  options = {:Port => 8080}
14
- OptionParser.new do |opts|
14
+ ConfigParser.new do |opts|
15
15
 
16
16
  opts.separator ""
17
17
  opts.separator "options:"
18
18
 
19
19
  opts.on("-h", "--help", "Show this message") do
20
- opts.banner = Tap::Support::TDoc.usage(__FILE__)
20
+ puts Lazydoc.usage(__FILE__)
21
21
  puts opts
22
22
  exit
23
23
  end
24
24
 
25
- opts.on("-p", "--port PORT", Integer, "Specifies the port (default #{options[:Port]})") do |value|
26
- options[:Port] = value
25
+ opts.on("-p", "--port PORT", "Specifies the port (default 8080)") do |value|
26
+ options[:Port] = value.to_i
27
27
  end
28
28
 
29
- opts.on("-d", "--development", Integer, "Specifies development mode") do
29
+ opts.on("-d", "--development", "Specifies development mode") do
30
30
  env.config[:development] = true
31
31
  end
32
32
 
@@ -18,16 +18,14 @@ Executable extends objects allowing them to be used in workflows, enqued, and ru
18
18
 
19
19
  Tasks are constructed such that <tt>process</tt> is the executable method (actually execute_with_callbacks is used to wrap process with before/after execute callbacks, but effectively this is the case). Hence <tt>process</tt> is the standard method overridden in Task subclasses.
20
20
 
21
- ==== Tap::Support::Configurable
21
+ ==== {Configurable}[http://tap.rubyforge.org/configurable/]
22
22
 
23
23
  http://tap.rubyforge.org/images/Configurable.png
24
24
 
25
- Configurable allows declaration of class configurations. Configurable classes have a <tt>configurations</tt> method accessing a {ClassConfiguration}[link:classes/Tap/Support/ClassConfiguration.html] which holds all declared configs, their default values, and metadata for transforming configurations into command line options (for example).
26
-
27
- Instances of a Configurable class have a <tt>config</tt> method accessing a {InstanceConfiguration}[link:classes/Tap/Support/InstanceConfiguration.html] object. The instance configuration acts like a forwarding hash; read and write operations for declared configs get forwarded to class methods while undeclared configs are stored directly. The writer for a config may be defined through a block provided during config declaration. For instance:
28
-
25
+ Tap uses the {Configurable}[http://tap.rubyforge.org/configurable/] module to declare class configurations and make them available in for use in contexts like the command line. Configurations essentially consist of a reader, writer, and default value, but they may additionally be accessed through a hash-like config object. For instance:
26
+
29
27
  class ConfigClass
30
- include Tap::Support::Configurable
28
+ include Configurable
31
29
 
32
30
  config :key, 'value' do |input|
33
31
  input.upcase
@@ -52,7 +50,7 @@ Is basically the same as:
52
50
  end
53
51
  end
54
52
 
55
- As you can see here:
53
+ And as you can see here:
56
54
 
57
55
  c = ConfigClass.new
58
56
  c.key # => 'VALUE'
@@ -65,12 +63,12 @@ As you can see here:
65
63
 
66
64
  This setup is both fast and convenient.
67
65
 
68
- ==== Tap::Support::Validation
66
+ ==== {Configurable::Validation}[http://tap.rubyforge.org/configurable/classes/Configurable/Validation.html]
69
67
 
70
- When configurations are set from the command line, the writer method will inevitably receive a string, whereas configurations set within code can receive any type of object. The {Validation}[link:classes/Tap/Support/Validation.html] module provides standard blocks for validating and transforming inputs, accessible through the <tt>c</tt> method (ex: <tt>c.integer</tt> or <tt>c.regexp</tt>). These blocks (generally) load string inputs as YAML and validate that the result is the correct class; non-string inputs are simply validated.
68
+ When configurations are set from the command line, the writer method inevitably receives a string, even though a non-string input may be desired. The {Validation}[http://tap.rubyforge.org/configurable/classes/Configurable/Validation.html] module provides standard blocks for validating and transforming inputs, accessible through the <tt>c</tt> method (ex: <tt>c.integer</tt> or <tt>c.regexp</tt>). These blocks (generally) load string inputs as YAML and validate that the result is the correct class; non-string inputs are simply validated.
71
69
 
72
70
  class ValidatingClass
73
- include Tap::Support::Configurable
71
+ include Configurable
74
72
 
75
73
  config :int, 1, &c.integer # assures the input is an integer
76
74
  config :int_or_nil, 1, &c.integer_or_nil # integer or nil only
@@ -89,9 +87,9 @@ When configurations are set from the command line, the writer method will inevit
89
87
 
90
88
  Validation blocks sometimes imply metadata. For instance <tt>c.flag</tt> makes a config into a flag on the command line.
91
89
 
92
- ==== Tap::Support::Lazydoc
90
+ ==== {Lazydoc}[http://tap.rubyforge.org/lazydoc]
93
91
 
94
- Ah lazydoc. Lazydoc fits into the space between live code and code documentation. Lazydoc can scan a file (code or not) and pull documentation into the object space where it can be utilized. Lazydoc uses a key-value syntax like this:
92
+ Ah lazydoc. {Lazydoc}[http://tap.rubyforge.org/lazydoc] fits into the space between live code and code documentation. Lazydoc can scan a file (code or not) and pull documentation into the object space where it can be utilized. Lazydoc uses a key-value syntax like this:
95
93
 
96
94
  # ::key value
97
95
 
@@ -112,10 +110,10 @@ Lazydoc parses a constant name, the key, the value, and any comment following th
112
110
 
113
111
  require 'tap'
114
112
 
115
- lazydoc = Tap::Support::Lazydoc[__FILE__]
113
+ lazydoc = Lazydoc[__FILE__]
116
114
  lazydoc.resolve
117
115
 
118
- lazydoc['Name::Space']['key'].to_s # => "This documentation gets parsed."
116
+ lazydoc['Name::Space']['key'].comment # => "This documentation gets parsed."
119
117
  lazydoc['Name::Space']['another'].value # => "another value"
120
118
 
121
119
  Furthermore, Lazydoc can register specific lines for documentation. These lines are parsed to echo what happens in RDoc.
@@ -128,7 +126,7 @@ Furthermore, Lazydoc can register specific lines for documentation. These lines
128
126
 
129
127
  require 'tap'
130
128
 
131
- lazydoc = Tap::Support::Lazydoc[__FILE__]
129
+ lazydoc = Lazydoc[__FILE__]
132
130
  code_comment = lazydoc.register(2)
133
131
  lazydoc.resolve
134
132
 
@@ -146,7 +144,7 @@ When no constant name is specified for a Lazydoc key, Env uses a constant based
146
144
  # If more than one task is defined in this file, or if Sample::Task
147
145
  # is not defined by loading this file, Tap will run into trouble.
148
146
 
149
- However, the best practice is to include the namespace explicitly.
147
+ However, the best practice is to include the namespace explicitly. See the {Lazydoc}[http://tap.rubyforge.org/lazydoc] documentation for more information.
150
148
 
151
149
  === Tap::Task
152
150
 
@@ -205,7 +203,7 @@ Tracking the evolution of a result through a workflow can get complex; Tap audit
205
203
  t3.name = 'trois'
206
204
 
207
205
  app._results(t2).collect do |_result|
208
- _result._to_s
206
+ _result.dump
209
207
  end.join("---\n")
210
208
  # =>
211
209
  # o-[] "input"
@@ -222,7 +220,7 @@ Tracking the evolution of a result through a workflow can get complex; Tap audit
222
220
 
223
221
  http://tap.rubyforge.org/images/Root.png
224
222
 
225
- A Root represents the base of a directory structure. Roots allow you to alias directories and ease working with filepaths, basically allowing you to develop code for a conceptual directory structure that can be defined later.
223
+ A Root represents the base of a directory structure. Roots allow you to alias relative paths, basically allowing you to develop code for a conceptual directory structure that can be defined later.
226
224
 
227
225
  root = Tap::Root.new '/path/to/root'
228
226
  root.root # => '/path/to/root'
@@ -248,8 +246,7 @@ Tap tracks inputs as they are modified by various tasks, again through Executabl
248
246
  Auditing is largely invisible except in <tt>on_complete</tt> blocks. <tt>on_complete</tt> blocks receive the audited results so that this information can be used, as needed, to make decisions.
249
247
 
250
248
  Task.new.on_complete do |_result| # _result is an Audit instance
251
- _result._current # the current value
252
- _result._original # the original value
249
+ _result.value # the current value
253
250
  end
254
251
 
255
252
  To help indicate when a result is actually a result and when it is an audit, Tap uses a convention whereby a leading underscore signals auditing is involved.
@@ -98,8 +98,6 @@ Here is a corresponding class:
98
98
 
99
99
  Simple enough; the name corresponds to the class, configurations (and dependencies, although they aren't show) are written out individually, and the block corresponds to process. There are a few differences, especially relating to process, but for the moment lets gloss over them and see how Goodnight works.
100
100
 
101
- Goodnight.configurations.to_hash # => {:message => 'goodnight'}
102
-
103
101
  goodnight = Goodnight.new
104
102
  goodnight.message # => 'goodnight'
105
103
  goodnight.process('moon') # => 'goodnight moon'
@@ -108,7 +106,7 @@ Simple enough; the name corresponds to the class, configurations (and dependenci
108
106
  hello.message # => 'hello'
109
107
  hello.process('world') # => 'hello world'
110
108
 
111
- Totally straightforward. Goodnight stores the default configurations, each instance has accessors to the configurations, and the defaults may be overridden during initialization, or later. Class definitions allow validation/transformation blocks to be specified for configurations. These blocks process inputs (ex the string inputs from the command line), quite literally defining the writer for a configuration accessor. A set of standard blocks are available through +c+, an alias for the Tap::Support::Validation module.
109
+ Totally straightforward. Goodnight stores the default configurations, each instance has accessors to the configurations, and the defaults may be overridden during initialization, or later. Class definitions allow validation/transformation blocks to be specified for configurations. These blocks process inputs (ex the string inputs from the command line), quite literally defining the writer for a configuration accessor. A set of standard blocks are available through +c+, an alias for the {Configurable::Validation}[http://tap.rubyforge.org/configurable/classes/Configurable/Validation.html] module.
112
110
 
113
111
  [lib/goodnight.rb]
114
112
 
@@ -166,9 +164,9 @@ Now from the command line:
166
164
  --name NAME Specify a name
167
165
  --use FILE Loads inputs from file
168
166
 
169
- Take a quick look at the documentation. Class definitions can also map documentation and, in some cases, metadata to the command line; the configurations now have comments and reverse is a switch! Rich mapping allows tasks to act as an script interface, not unlike {OptionParser}[http://www.ruby-doc.org/stdlib/libdoc/optparse/rdoc/classes/OptionParser.html] (surprise, tap uses OptionParser).
167
+ Take a quick look at the documentation. Class definitions map documentation and, in some cases, metadata to the command line; the configurations now have comments and reverse is a switch! Rich mapping allows tasks to act as an script interface, not unlike {OptionParser}[http://www.ruby-doc.org/stdlib/libdoc/optparse/rdoc/classes/OptionParser.html] (check out the {Configurable}[http://tap.rubyforge.org/configurable/] gem and specifically {ConfigParser}[http://tap.rubyforge.org/configurable/classes/ConfigParser.html] for more details).
170
168
 
171
- For example this is a stand-alone goodnight script:
169
+ This is a stand-alone goodnight script:
172
170
 
173
171
  [goodnight]
174
172
 
@@ -208,7 +206,7 @@ Tap comes with two executables, rap and tap. The tap executable is more verbose
208
206
  % cd sample
209
207
  % tap generate task goodnight
210
208
 
211
- Take a look at the task files an you find something like this:
209
+ Take a look at the task files and you find something like this:
212
210
 
213
211
  [lib/goodnight.rb]
214
212
 
@@ -247,7 +245,7 @@ Take a look at the task files an you find something like this:
247
245
  app.run
248
246
 
249
247
  assert_equal ["goodnight moon"], app.results(task)
250
- assert_audit_equal ExpAudit[[nil, "moon"], [task, "goodnight moon"]], app._results(task)[0]
248
+ assert_audit_equal [[nil, "moon"], [task, "goodnight moon"]], app._results(task)[0]
251
249
  end
252
250
  end
253
251
 
data/lib/tap.rb CHANGED
@@ -2,6 +2,8 @@ autoload(:YAML, 'yaml') # expensive to load
2
2
 
3
3
  $:.unshift File.expand_path(File.dirname(__FILE__))
4
4
 
5
+ require 'rubygems'
6
+ require 'configurable'
5
7
  require 'tap/constants'
6
8
 
7
9
  # require in order...
@@ -106,9 +106,8 @@ module Tap
106
106
  # add_five = Tap::Task.intern({}, 'add_five') {|task, input| input += 5 }
107
107
  #
108
108
  # add_one.on_complete do |_result|
109
- # # _result is the audit; use the _current method
110
- # # to get the current value in the audit trail
111
- # current_value = _result._current
109
+ # # _result is the audit
110
+ # current_value = _result.value
112
111
  #
113
112
  # if current_value < 3
114
113
  # add_one.enq(_result)
@@ -128,33 +127,23 @@ module Tap
128
127
  # through a different series of tasks. With auditing you can see how each
129
128
  # input came to the final value of 8:
130
129
  #
131
- # # app.results returns the actual result values
132
- # # app._results returns the audits for these values
133
- # app._results(add_five).each do |_result|
134
- # puts "How #{_result._original} became #{_result._current}:"
135
- # puts _result._to_s
136
- # puts
137
- # end
138
- #
139
- # Prints:
140
- #
141
- # How 2 became 8:
142
- # o-[] 2
143
- # o-[add_one] 3
144
- # o-[add_five] 8
145
- #
146
- # How 1 became 8:
147
- # o-[] 1
148
- # o-[add_one] 2
149
- # o-[add_one] 3
150
- # o-[add_five] 8
151
- #
152
- # How 0 became 8:
153
- # o-[] 0
154
- # o-[add_one] 1
155
- # o-[add_one] 2
156
- # o-[add_one] 3
157
- # o-[add_five] 8
130
+ # "\n" + Tap::Support::Audit.dump(app._results(add_five), "")
131
+ # # => %Q{
132
+ # # o-[] 2
133
+ # # o-[add_one] 3
134
+ # # o-[add_five] 8
135
+ # #
136
+ # # o-[] 1
137
+ # # o-[add_one] 2
138
+ # # o-[add_one] 3
139
+ # # o-[add_five] 8
140
+ # #
141
+ # # o-[] 0
142
+ # # o-[add_one] 1
143
+ # # o-[add_one] 2
144
+ # # o-[add_one] 3
145
+ # # o-[add_five] 8
146
+ # # }
158
147
  #
159
148
  # See Tap::Support::Audit for more details.
160
149
  class App < Root
@@ -378,9 +367,10 @@ module Tap
378
367
  # app.results(t1, t0) # => ["1.1", "0.0"]
379
368
  #
380
369
  def results(*tasks)
381
- _results(tasks).collect {|_result| _result._current}
370
+ _results(tasks).collect {|_result| _result.value }
382
371
  end
383
372
 
373
+ # Returns a string like: "#<Tap::App:#{object_id} root: #{root} >"
384
374
  def inspect
385
375
  "#<#{self.class.to_s}:#{object_id} root: #{root} >"
386
376
  end
@@ -1,7 +1,7 @@
1
1
  module Tap
2
2
  MAJOR = 0
3
- MINOR = 11
4
- TINY = 2
3
+ MINOR = 12
4
+ TINY = 0
5
5
 
6
6
  VERSION="#{MAJOR}.#{MINOR}.#{TINY}"
7
7
  WEBSITE="http://tap.rubyforge.org"
@@ -1,5 +1,6 @@
1
1
  require File.dirname(__FILE__) + "/../tap"
2
- autoload(:OpenStruct, 'ostruct')
2
+ require "tap/declarations/description"
3
+ require "tap/declarations/declaration_task"
3
4
 
4
5
  module Tap
5
6
  #--
@@ -8,38 +9,9 @@ module Tap
8
9
  # the top level (for main/Object) while extend should be used
9
10
  # in all other cases.
10
11
  module Declarations
11
- Lazydoc = Tap::Support::Lazydoc
12
12
  include Tap::Support::ShellUtils
13
13
 
14
- module Lazydoc
15
- class Declaration < Comment
16
- attr_accessor :desc
17
-
18
- def resolve(lines)
19
- super
20
-
21
- @subject = case
22
- when content.empty? || content[0][0].to_s !~ /^::desc(.*)/
23
- desc.to_s
24
- else
25
- content[0].shift
26
- $1.strip
27
- end
28
-
29
- self
30
- end
31
- end
32
- end
33
-
34
- module Rakish
35
- def new(*args)
36
- @instance ||= super
37
- @instance.app.dependencies.register(@instance)
38
- @instance
39
- end
40
- end
41
-
42
- def self.extended(base)
14
+ def self.extended(base) # :nodoc:
43
15
  declaration_base = base.to_s
44
16
  case declaration_base
45
17
  when "Object", "Tap", "main"
@@ -50,58 +22,51 @@ module Tap
50
22
  base.instance_variable_set(:@current_desc, nil)
51
23
  end
52
24
 
25
+ # The Tap::Env for Dir.pwd
53
26
  def self.env
54
27
  @env ||= Tap::Env.instance_for(Dir.pwd)
55
28
  end
56
29
 
30
+ # The environment in which declared task classes are registered.
31
+ # By default Declarations.env
57
32
  def declaration_env
58
33
  @declaration_env ||= Declarations.env
59
34
  end
60
35
 
61
36
  attr_writer :declaration_base
62
37
 
38
+ # The base constant for all task declarations, prepended to the task name.
63
39
  def declaration_base
64
40
  @declaration_base ||= ''
65
41
  end
66
42
 
67
43
  attr_writer :current_desc
68
44
 
45
+ # Tracks the current description, which will be used to
46
+ # document the next task declaration.
69
47
  def current_desc
70
48
  @current_desc ||= nil
71
49
  end
72
50
 
51
+ # Declares a task with a rake-like syntax
73
52
  def task(*args, &block)
74
- const_name, configs, dependencies, arg_names = resolve_args(args)
75
- task_class = declare(const_name, configs, dependencies) do |*inputs|
76
- # collect inputs to make a rakish-args object
77
- args = {}
78
- arg_names.each do |arg_name|
79
- break if inputs.empty?
80
- args[arg_name] = inputs.shift
81
- end
82
- args = OpenStruct.new(args)
83
-
84
- # execute each block assciated with this task
85
- self.class::BLOCKS.each do |task_block|
86
- case task_block.arity
87
- when 0 then task_block.call()
88
- when 1 then task_block.call(self)
89
- else task_block.call(self, args)
90
- end
91
- end
92
-
93
- nil
94
- end
95
- register_doc(task_class, arg_names)
53
+ task_name, configs, needs, arg_names = resolve_args(args)
54
+ task_class = declare(task_name, configs, needs)
55
+
56
+ # set the arg_names for the subclass
57
+ task_class.arg_names = arg_names
58
+
59
+ # register the current_desc
60
+ register_doc(task_class)
96
61
 
97
62
  # add the block to the task
98
- unless task_class.const_defined?(:BLOCKS)
99
- task_class.const_set(:BLOCKS, [])
100
- end
101
- task_class::BLOCKS << block unless block == nil
63
+ task_class.blocks << block if block
102
64
  task_class.instance
103
65
  end
104
66
 
67
+ # Appends name to the declaration base for the duration of the block.
68
+ # This has the effect of nesting any task declarations within the
69
+ # Name module or class.
105
70
  def namespace(name, &block)
106
71
  current_base = declaration_base
107
72
  @declaration_base = File.join(current_base, name.to_s.underscore)
@@ -109,43 +74,60 @@ module Tap
109
74
  @declaration_base = current_base
110
75
  end
111
76
 
77
+ # Sets the current description for use by the next task declaration.
112
78
  def desc(str)
113
79
  self.current_desc = str
114
80
  end
115
81
 
116
82
  protected
117
83
 
118
- # Resolve the arguments for a task/rule. Returns an array of
84
+ # A helper to resolve the arguments for a task; returns the array
119
85
  # [task_name, configs, needs, arg_names].
120
86
  #
121
87
  # Adapted from Rake 0.8.3
122
88
  # Changes:
123
89
  # - no :needs support for the trailing Hash (which is now config)
124
- def resolve_args(args)
90
+ def resolve_args(args) # :nodoc:
125
91
  task_name = args.shift
126
92
  arg_names = args
127
93
  configs = {}
128
94
  needs = []
129
95
 
96
+ # resolve hash task_names, for the syntax:
97
+ # task :name => [dependencies]
130
98
  if task_name.is_a?(Hash)
131
99
  hash = task_name
132
- task_name = hash.keys[0]
133
- needs = hash[task_name]
100
+ case hash.length
101
+ when 0
102
+ task_name = nil
103
+ when 1
104
+ task_name = hash.keys[0]
105
+ needs = hash[task_name]
106
+ else
107
+ raise ArgumentError, "multiple task names specified: #{hash.keys.inspect}"
108
+ end
109
+ end
110
+
111
+ # ensure a task name is specified
112
+ if task_name == nil
113
+ raise ArgumentError, "no task name specified" if args.empty?
134
114
  end
135
115
 
116
+ # pop off configurations, if present, using the syntax:
117
+ # task :name, :one, :two, {configs...}
136
118
  if arg_names.last.is_a?(Hash)
137
119
  configs = arg_names.pop
138
120
  end
139
121
 
140
122
  needs = needs.respond_to?(:to_ary) ? needs.to_ary : [needs]
141
123
  needs = needs.compact.collect do |need|
124
+
142
125
  unless need.kind_of?(Class)
126
+ # lookup or declare non-class dependencies
143
127
  name = normalize_name(need).camelize
144
- need = Support::Constant.constantize(name) do |base, constants|
145
- declare(name)
146
- end
128
+ need = Support::Constant.constantize(name) {|base, constants| declare(name) }
147
129
  end
148
-
130
+
149
131
  unless need.ancestors.include?(Tap::Task)
150
132
  raise ArgumentError, "not a task: #{need}"
151
133
  end
@@ -156,45 +138,57 @@ module Tap
156
138
  [normalize_name(task_name), configs, needs, arg_names]
157
139
  end
158
140
 
159
- def normalize_name(name)
160
- name.to_s.underscore.tr(":", "/")
141
+ # helper to translate rake-style names to tap-style names, ie
142
+ #
143
+ # normalize_name('nested:name') # => "nested/name"
144
+ # normalize_name(:symbol) # => "symbol"
145
+ #
146
+ def normalize_name(name) # :nodoc:
147
+ name.to_s.tr(":", "/")
161
148
  end
162
149
 
163
- def declare(name, configs={}, dependencies=[], &block)
164
- const_name = File.join(declaration_base, name).camelize
150
+ # Looks up or creates the DeclarationTask subclass specified by name
151
+ # (nested within declaration_base), and adds the configs and dependencies.
152
+ # Declare also registers the subclass in the declaration_env tasks
153
+ # manifest.
154
+ #
155
+ # Configurations are always validated using the yaml transformation block
156
+ # (see {Configurable::Validation.yaml}[http://tap.rubyforge.org/configurable/classes/Configurable/Validation.html]).
157
+ #
158
+ def declare(name, configs={}, dependencies=[])
159
+ # assemble the constant name
160
+ const_name = File.join(declaration_base, name.to_s).camelize
165
161
 
166
- # generate the subclass
162
+ # lookup or generate the subclass
167
163
  subclass = Support::Constant.constantize(const_name) do |base, constants|
168
164
  constants.each do |const|
169
- # nesting Tasks into Tasks is required for
170
- # namespaces with the same name as a task
171
- base = base.const_set(const, Class.new(Tap::Task))
165
+ # nesting Task classes into other Task classes
166
+ # is required for namespaces with the same name
167
+ # as a task
168
+ base = base.const_set(const, Class.new(DeclarationTask))
172
169
  end
173
170
  base
174
171
  end
175
-
176
- subclass.extend Rakish
177
172
 
173
+ # check a correct class was found
174
+ unless subclass.ancestors.include?(DeclarationTask)
175
+ raise "not a DeclarationTask: #{subclass}"
176
+ end
177
+
178
+ # append configuration (note that specifying a desc
179
+ # prevents lazydoc registration of these lines)
180
+ convert_to_yaml = Configurable::Validation.yaml
178
181
  configs.each_pair do |key, value|
179
- subclass.send(:config, key, value)
182
+ subclass.send(:config, key, value, :desc => "", &convert_to_yaml)
180
183
  end
181
184
 
185
+ # add dependencies
182
186
  dependencies.each do |dependency|
183
187
  dependency_name = File.basename(dependency.default_name)
184
188
  subclass.send(:depends_on, dependency_name, dependency)
185
189
  end
186
190
 
187
- if block_given?
188
- subclass.send(:undef_method, :process) if subclass.method_defined?(:process)
189
- subclass.send(:define_method, :process, &block)
190
- end
191
-
192
- # update any dependencies in instance
193
- subclass.dependencies.each do |dependency|
194
- subclass.instance.depends_on(dependency.instance)
195
- end
196
-
197
- # register the subclass in the manifest
191
+ # register the subclass in the manifest, if necessary
198
192
  manifest = declaration_env.tasks
199
193
  const_name = subclass.to_s
200
194
  unless manifest.entries.any? {|const| const.name == const_name }
@@ -204,23 +198,17 @@ module Tap
204
198
  subclass
205
199
  end
206
200
 
207
- def register_doc(task_class, arg_names)
208
-
201
+ # a helper to register the current_desc as the task_class.manifest
202
+ def register_doc(task_class) # :nodoc:
209
203
  # register documentation
210
204
  caller[1] =~ Lazydoc::CALLER_REGEXP
211
205
  task_class.source_file = File.expand_path($1)
212
- manifest = task_class.lazydoc(false).register($3.to_i - 1, Lazydoc::Declaration)
206
+
207
+ manifest = task_class.lazydoc.register($3.to_i - 1, Description)
213
208
  manifest.desc = current_desc
214
209
  task_class.manifest = manifest
215
210
 
216
211
  self.current_desc = nil
217
-
218
- if arg_names
219
- comment = Lazydoc::Comment.new
220
- comment.subject = arg_names.collect {|name| name.to_s.upcase }.join(' ')
221
- task_class.args = comment
222
- end
223
-
224
212
  task_class
225
213
  end
226
214
  end