tap 0.9.1 → 0.10.0

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.
Files changed (244) hide show
  1. data/History +37 -30
  2. data/MIT-LICENSE +1 -1
  3. data/README +92 -44
  4. data/bin/tap +62 -75
  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 +6 -45
  13. data/lib/tap/app.rb +126 -500
  14. data/lib/tap/constants.rb +2 -29
  15. data/lib/tap/env.rb +555 -250
  16. data/lib/tap/file_task.rb +60 -103
  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 +16 -12
  21. data/lib/tap/generator/generators/command/templates/command.erb +13 -19
  22. data/lib/tap/generator/generators/config/config_generator.rb +18 -27
  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 +16 -11
  26. data/lib/tap/generator/generators/file_task/templates/file.txt +11 -2
  27. data/lib/tap/generator/generators/file_task/templates/result.yml +6 -0
  28. data/lib/tap/generator/generators/file_task/templates/task.erb +24 -31
  29. data/lib/tap/generator/generators/file_task/templates/test.erb +18 -22
  30. data/lib/tap/generator/generators/root/root_generator.rb +45 -31
  31. data/lib/tap/generator/generators/root/templates/Rakefile +64 -41
  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 +0 -0
  35. data/lib/tap/generator/generators/root/templates/test/tap_test_suite.rb +1 -1
  36. data/lib/tap/generator/generators/root/templates/test/tapfile_test.rb +15 -0
  37. data/lib/tap/generator/generators/task/task_generator.rb +21 -28
  38. data/lib/tap/generator/generators/task/templates/task.erb +13 -23
  39. data/lib/tap/generator/generators/task/templates/test.erb +15 -18
  40. data/lib/tap/generator/manifest.rb +14 -0
  41. data/lib/tap/patches/rake/rake_test_loader.rb +0 -0
  42. data/lib/tap/patches/rake/testtask.rb +0 -0
  43. data/lib/tap/patches/ruby19/backtrace_filter.rb +0 -0
  44. data/lib/tap/patches/ruby19/parsedate.rb +0 -0
  45. data/lib/tap/root.rb +260 -21
  46. data/lib/tap/support/aggregator.rb +11 -11
  47. data/lib/tap/support/assignments.rb +172 -0
  48. data/lib/tap/support/audit.rb +20 -18
  49. data/lib/tap/support/batchable.rb +21 -10
  50. data/lib/tap/support/batchable_class.rb +107 -0
  51. data/lib/tap/support/class_configuration.rb +154 -239
  52. data/lib/tap/support/command_line.rb +97 -102
  53. data/lib/tap/support/comment.rb +270 -0
  54. data/lib/tap/support/configurable.rb +86 -65
  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 +30 -17
  61. data/lib/tap/support/executable_queue.rb +0 -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 +0 -0
  68. data/lib/tap/support/shell_utils.rb +33 -9
  69. data/lib/tap/support/summary.rb +30 -0
  70. data/lib/tap/support/tdoc.rb +339 -134
  71. data/lib/tap/support/tdoc/tdoc_html_generator.rb +0 -0
  72. data/lib/tap/support/tdoc/tdoc_html_template.rb +0 -0
  73. data/lib/tap/support/templater.rb +180 -0
  74. data/lib/tap/support/validation.rb +409 -76
  75. data/lib/tap/support/versions.rb +5 -3
  76. data/lib/tap/task.rb +78 -174
  77. data/lib/tap/tasks/dump.rb +56 -0
  78. data/lib/tap/tasks/rake.rb +93 -0
  79. data/lib/tap/test.rb +3 -3
  80. data/lib/tap/test/env_vars.rb +2 -2
  81. data/lib/tap/test/file_methods.rb +19 -20
  82. data/lib/tap/test/script_methods.rb +144 -0
  83. data/lib/tap/test/subset_methods.rb +1 -1
  84. data/lib/tap/test/tap_methods.rb +28 -62
  85. data/lib/tap/workflow.rb +22 -39
  86. metadata +48 -179
  87. data/Basic Overview +0 -151
  88. data/Command Reference +0 -99
  89. data/Rakefile +0 -127
  90. data/Tutorial +0 -287
  91. data/lib/tap/cmd/console.rb +0 -31
  92. data/lib/tap/cmd/destroy.rb +0 -20
  93. data/lib/tap/cmd/generate.rb +0 -20
  94. data/lib/tap/cmd/run.rb +0 -151
  95. data/lib/tap/dump.rb +0 -57
  96. data/lib/tap/generator.rb +0 -91
  97. data/lib/tap/generator/generators/command/USAGE +0 -6
  98. data/lib/tap/generator/generators/config/USAGE +0 -21
  99. data/lib/tap/generator/generators/config/templates/config.erb +0 -1
  100. data/lib/tap/generator/generators/file_task/USAGE +0 -3
  101. data/lib/tap/generator/generators/file_task/templates/file.yml +0 -3
  102. data/lib/tap/generator/generators/generator/USAGE +0 -0
  103. data/lib/tap/generator/generators/generator/generator_generator.rb +0 -21
  104. data/lib/tap/generator/generators/generator/templates/generator.erb +0 -32
  105. data/lib/tap/generator/generators/generator/templates/usage.erb +0 -1
  106. data/lib/tap/generator/generators/root/USAGE +0 -0
  107. data/lib/tap/generator/generators/root/templates/ReadMe.txt +0 -0
  108. data/lib/tap/generator/generators/root/templates/tap.yml +0 -80
  109. data/lib/tap/generator/generators/task/USAGE +0 -3
  110. data/lib/tap/generator/generators/workflow/USAGE +0 -0
  111. data/lib/tap/generator/generators/workflow/templates/task.erb +0 -16
  112. data/lib/tap/generator/generators/workflow/templates/test.erb +0 -7
  113. data/lib/tap/generator/generators/workflow/workflow_generator.rb +0 -6
  114. data/lib/tap/generator/options.rb +0 -26
  115. data/lib/tap/generator/usage.rb +0 -26
  116. data/lib/tap/support/batchable_methods.rb +0 -34
  117. data/lib/tap/support/command_line_methods.rb +0 -76
  118. data/lib/tap/support/configurable_methods.rb +0 -224
  119. data/lib/tap/support/logger.rb +0 -88
  120. data/lib/tap/support/rake.rb +0 -43
  121. data/lib/tap/support/tdoc/config_attr.rb +0 -362
  122. data/test/app/config/another/task.yml +0 -1
  123. data/test/app/config/batch.yml +0 -2
  124. data/test/app/config/empty.yml +0 -0
  125. data/test/app/config/erb.yml +0 -2
  126. data/test/app/config/some/task.yml +0 -1
  127. data/test/app/config/template.yml +0 -2
  128. data/test/app/config/version-0.1.yml +0 -1
  129. data/test/app/config/version.yml +0 -1
  130. data/test/app/lib/app_test_task.rb +0 -3
  131. data/test/app_test.rb +0 -1849
  132. data/test/env/test_configure/recurse_a.yml +0 -2
  133. data/test/env/test_configure/recurse_b.yml +0 -2
  134. data/test/env/test_configure/tap.yml +0 -23
  135. data/test/env/test_load_env_config/dir/tap.yml +0 -3
  136. data/test/env/test_load_env_config/recurse_a.yml +0 -2
  137. data/test/env/test_load_env_config/recurse_b.yml +0 -2
  138. data/test/env/test_load_env_config/tap.yml +0 -3
  139. data/test/env_test.rb +0 -198
  140. data/test/file_task/config/batch.yml +0 -2
  141. data/test/file_task/config/configured.yml +0 -1
  142. data/test/file_task/old_file_one.txt +0 -0
  143. data/test/file_task/old_file_two.txt +0 -0
  144. data/test/file_task_test.rb +0 -1291
  145. data/test/root/alt_lib/alt_module.rb +0 -4
  146. data/test/root/file.txt +0 -0
  147. data/test/root/glob/one.txt +0 -0
  148. data/test/root/glob/two.txt +0 -0
  149. data/test/root/lib/absolute_alt_filepath.rb +0 -2
  150. data/test/root/lib/alternative_filepath.rb +0 -2
  151. data/test/root/lib/another_module.rb +0 -2
  152. data/test/root/lib/nested/some_module.rb +0 -4
  153. data/test/root/lib/no_module_included.rb +0 -0
  154. data/test/root/lib/some/module.rb +0 -4
  155. data/test/root/lib/some_class.rb +0 -2
  156. data/test/root/lib/some_module.rb +0 -3
  157. data/test/root/load_path/load_path_module.rb +0 -2
  158. data/test/root/load_path/skip_module.rb +0 -2
  159. data/test/root/mtime/older.txt +0 -0
  160. data/test/root/unload/full_path.rb +0 -2
  161. data/test/root/unload/loaded_by_nested.rb +0 -2
  162. data/test/root/unload/nested/nested_load.rb +0 -6
  163. data/test/root/unload/nested/nested_with_ext.rb +0 -4
  164. data/test/root/unload/nested/relative_path.rb +0 -4
  165. data/test/root/unload/older.rb +0 -2
  166. data/test/root/unload/unload_base.rb +0 -9
  167. data/test/root/versions/another.yml +0 -0
  168. data/test/root/versions/file-0.1.2.yml +0 -0
  169. data/test/root/versions/file-0.1.yml +0 -0
  170. data/test/root/versions/file.yml +0 -0
  171. data/test/root_test.rb +0 -718
  172. data/test/support/aggregator_test.rb +0 -99
  173. data/test/support/audit_test.rb +0 -445
  174. data/test/support/batchable_test.rb +0 -74
  175. data/test/support/class_configuration_test.rb +0 -331
  176. data/test/support/command_line_test.rb +0 -58
  177. data/test/support/configurable/config/configured.yml +0 -2
  178. data/test/support/configurable_test.rb +0 -295
  179. data/test/support/executable_queue_test.rb +0 -103
  180. data/test/support/executable_test.rb +0 -38
  181. data/test/support/logger_test.rb +0 -31
  182. data/test/support/rake_test.rb +0 -37
  183. data/test/support/shell_utils_test.rb +0 -24
  184. data/test/support/tdoc_test.rb +0 -370
  185. data/test/support/validation_test.rb +0 -54
  186. data/test/support/versions_test.rb +0 -103
  187. data/test/tap_test_helper.rb +0 -57
  188. data/test/tap_test_suite.rb +0 -7
  189. data/test/task/config/batch.yml +0 -2
  190. data/test/task/config/batched.yml +0 -2
  191. data/test/task/config/configured.yml +0 -1
  192. data/test/task/config/example.yml +0 -1
  193. data/test/task_base_test.rb +0 -24
  194. data/test/task_syntax_test.rb +0 -300
  195. data/test/task_test.rb +0 -320
  196. data/test/test/env_vars_test.rb +0 -48
  197. data/test/test/file_methods/test_assert_files/expected/one.txt +0 -1
  198. data/test/test/file_methods/test_assert_files/expected/two.txt +0 -1
  199. data/test/test/file_methods/test_assert_files/input/one.txt +0 -1
  200. data/test/test/file_methods/test_assert_files/input/two.txt +0 -1
  201. data/test/test/file_methods/test_assert_files_can_have_no_expected_files_if_specified/input/one.txt +0 -1
  202. data/test/test/file_methods/test_assert_files_can_have_no_expected_files_if_specified/input/two.txt +0 -1
  203. data/test/test/file_methods/test_assert_files_fails_for_different_content/expected/one.txt +0 -1
  204. data/test/test/file_methods/test_assert_files_fails_for_different_content/expected/two.txt +0 -1
  205. data/test/test/file_methods/test_assert_files_fails_for_different_content/input/one.txt +0 -1
  206. data/test/test/file_methods/test_assert_files_fails_for_different_content/input/two.txt +0 -1
  207. data/test/test/file_methods/test_assert_files_fails_for_missing_expected_file/expected/one.txt +0 -1
  208. data/test/test/file_methods/test_assert_files_fails_for_missing_expected_file/input/one.txt +0 -1
  209. data/test/test/file_methods/test_assert_files_fails_for_missing_expected_file/input/two.txt +0 -1
  210. data/test/test/file_methods/test_assert_files_fails_for_missing_output_file/expected/one.txt +0 -1
  211. data/test/test/file_methods/test_assert_files_fails_for_missing_output_file/expected/two.txt +0 -1
  212. data/test/test/file_methods/test_assert_files_fails_for_missing_output_file/input/one.txt +0 -1
  213. data/test/test/file_methods/test_assert_files_fails_for_missing_output_file/input/two.txt +0 -1
  214. data/test/test/file_methods/test_assert_files_fails_for_no_expected_files/input/one.txt +0 -1
  215. data/test/test/file_methods/test_assert_files_fails_for_no_expected_files/input/two.txt +0 -1
  216. data/test/test/file_methods/test_method_glob/expected/file.yml +0 -0
  217. data/test/test/file_methods/test_method_glob/expected/file_1.txt +0 -0
  218. data/test/test/file_methods/test_method_glob/expected/file_2.txt +0 -0
  219. data/test/test/file_methods_doc/test_sub/expected/one.txt +0 -1
  220. data/test/test/file_methods_doc/test_sub/expected/two.txt +0 -1
  221. data/test/test/file_methods_doc/test_sub/input/one.txt +0 -1
  222. data/test/test/file_methods_doc/test_sub/input/two.txt +0 -1
  223. data/test/test/file_methods_doc_test.rb +0 -29
  224. data/test/test/file_methods_test.rb +0 -275
  225. data/test/test/subset_methods_test.rb +0 -171
  226. data/test/test/tap_methods/test_assert_files/expected/task/name/a.txt +0 -1
  227. data/test/test/tap_methods/test_assert_files/expected/task/name/b.txt +0 -1
  228. data/test/test/tap_methods/test_assert_files/input/a.txt +0 -1
  229. data/test/test/tap_methods/test_assert_files/input/b.txt +0 -1
  230. data/test/test/tap_methods_test.rb +0 -399
  231. data/test/workflow_test.rb +0 -120
  232. data/vendor/rails_generator.rb +0 -56
  233. data/vendor/rails_generator/base.rb +0 -263
  234. data/vendor/rails_generator/commands.rb +0 -581
  235. data/vendor/rails_generator/generated_attribute.rb +0 -42
  236. data/vendor/rails_generator/lookup.rb +0 -209
  237. data/vendor/rails_generator/manifest.rb +0 -53
  238. data/vendor/rails_generator/options.rb +0 -143
  239. data/vendor/rails_generator/scripts.rb +0 -83
  240. data/vendor/rails_generator/scripts/destroy.rb +0 -7
  241. data/vendor/rails_generator/scripts/generate.rb +0 -7
  242. data/vendor/rails_generator/scripts/update.rb +0 -12
  243. data/vendor/rails_generator/simple_logger.rb +0 -46
  244. data/vendor/rails_generator/spec.rb +0 -44
@@ -0,0 +1,89 @@
1
+ module Tap
2
+ module Support
3
+ class Manifest
4
+
5
+ class << self
6
+ def glob_method(name)
7
+ "manifest_glob_#{name}".to_sym
8
+ end
9
+
10
+ def map_method(name)
11
+ "manifest_map_#{name}".to_sym
12
+ end
13
+ end
14
+
15
+ DEFAULT_MAP_METHOD = :manifest_map
16
+
17
+ attr_reader :entries
18
+ attr_reader :map_method
19
+ attr_reader :paths
20
+ attr_reader :path_index
21
+
22
+ def initialize(name, source)
23
+ @entries = []
24
+
25
+ @map_method = Manifest.map_method(name)
26
+ @map_method = DEFAULT_MAP_METHOD if !source.respond_to?(@map_method)
27
+
28
+ @paths = source.send(Manifest.glob_method(name)).uniq
29
+ @path_index = 0
30
+ end
31
+
32
+ def complete?
33
+ @path_index == paths.length
34
+ end
35
+
36
+ def each_path
37
+ return(false) if complete?
38
+
39
+ n_to_skip = @path_index
40
+ paths.each do |context, path|
41
+ if n_to_skip > 0
42
+ n_to_skip -= 1
43
+ next
44
+ end
45
+
46
+ @path_index += 1
47
+ yield(context, path)
48
+ end
49
+
50
+ true
51
+ end
52
+
53
+ # Checks that the manifest does not already assign key a conflicting path,
54
+ # then adds the (key, path) pair to manifest.
55
+ def store(entry)
56
+ existing_key, existing_path = entries.find {|(key, path)| key == entry[0] }
57
+
58
+ if existing_key && existing_path != entry[1]
59
+ raise ManifestConflict, "multiple paths for key '#{existing_key}': ['#{existing_path}', '#{entry[1]}']"
60
+ end
61
+
62
+ entries << entry
63
+ end
64
+
65
+ def keys
66
+ entries.collect {|(key, value)| key }
67
+ end
68
+
69
+ def values
70
+ entries.collect {|(key, value)| value }
71
+ end
72
+
73
+ def mini_map
74
+ return [] if entries.empty?
75
+
76
+ hash = {}
77
+ Root.minimize(keys) do |path, mini_path|
78
+ hash[path] = mini_path
79
+ end
80
+
81
+ entries.collect {|path, value| [hash[path], value] }
82
+ end
83
+
84
+ # Raised when multiple paths are assigned to the same manifest key.
85
+ class ManifestConflict < StandardError
86
+ end
87
+ end
88
+ end
89
+ end
File without changes
@@ -1,8 +1,21 @@
1
- require 'tempfile'
1
+ autoload(:Tempfile, 'tempfile')
2
2
 
3
3
  module Tap
4
4
  module Support
5
5
  # Provides several shell utility methods for calling programs.
6
+ #
7
+ # == Windows
8
+ # A couple warnings when running shell commands in the MSDOS prompt on Windows.
9
+ # MSDOS has command line length limits specific to the version of Windows being
10
+ # run (from http://www.ss64.com/nt/cmd.html):
11
+ #
12
+ # Windows NT:: 256 characters
13
+ # Windows 2000:: 2046 characters
14
+ # Windows XP:: 8190 characters
15
+ #
16
+ # No word on more recent versions of Windows. Commands longer than these limits
17
+ # fail, usually with something like: 'the input line is too long'
18
+ #
6
19
  module ShellUtils
7
20
 
8
21
  module_function
@@ -25,21 +38,32 @@ module Tap
25
38
  # Runs the system command +cmd+ using sh, redirecting the output to the
26
39
  # specified file path. Uses the redirection command:
27
40
  #
28
- # "> \"#{path}\" 2>&1 #{cmd}"
29
- #
30
- # This redirection has been tested on Windows, OS X, and Fedora. See
31
- # http://www.robvanderwoude.com/redirection.html for pointers on
32
- # redirection. The website notes that this style of redirection SHOULD
41
+ # "> \"#{path}\" 2>&1 #{cmd}"
42
+ #
43
+ # This redirection has been tested on Windows, OS X, and Fedora. See
44
+ # http://www.robvanderwoude.com/redirection.html for pointers on
45
+ # redirection. The website notes that this style of redirection SHOULD
33
46
  # NOT be used with commands that contain other redirections.
34
47
  def redirect_sh(cmd, path, &block) # :yields: ok, status
35
48
  sh( "> \"#{path}\" 2>&1 #{cmd}", &block)
36
49
  end
37
50
 
38
51
  # Runs the system command +cmd+ and returns the output as a string.
39
- def capture_sh(cmd, quiet=false, &block) # :yields: ok, status
40
- tempfile = Tempfile.new('shell_utils')
52
+ def capture_sh(cmd, quiet=false, &block) # :yields: ok, status, tempfile_path
53
+ tempfile = Tempfile.new('shell_utils')
41
54
  tempfile.close
42
- redirect_sh(cmd, tempfile.path, &block)
55
+ redirect_sh(cmd, tempfile.path) do |ok, status|
56
+ if block_given?
57
+ yield(ok, $?, tempfile.path)
58
+ else
59
+ ok or raise %Q{Command failed with status (#{$?.exitstatus}): [#{cmd}]
60
+ -------------- command output -------------------
61
+ #{File.read(tempfile.path)}
62
+ -------------------------------------------------
63
+ }
64
+ end
65
+ end
66
+
43
67
  quiet == true ? "" : File.read(tempfile.path)
44
68
  end
45
69
  end
@@ -0,0 +1,30 @@
1
+ module Tap
2
+ module Support
3
+ class Summary
4
+ def initialize
5
+ @map = []
6
+ @width = 10
7
+ end
8
+
9
+ def add(env_key, env, map)
10
+ unless map.empty?
11
+ @map << [env_key, env, map]
12
+ map.each {|(key, path)| @width = key.length if @width < key.length }
13
+ end
14
+ end
15
+
16
+ def lines
17
+ lines = []
18
+ @map.each do |(env_lookup, env, map)|
19
+ lines << "#{env_lookup}:" if @map.length > 1
20
+ map.each do |(key, path)|
21
+ desc = block_given? ? (yield(path) || '') : ''
22
+ desc = " # #{desc}" unless desc.empty?
23
+ lines << (" %-#{@width}s%s" % [key, desc])
24
+ end
25
+ end
26
+ lines
27
+ end
28
+ end
29
+ end
30
+ end
@@ -1,23 +1,66 @@
1
- require 'tap/support/tdoc/config_attr'
2
- require 'singleton'
1
+ # RDoc creates a namespace conflict with IRB within 'rdoc/parsers/parse_rb'
2
+ # In that file, RubyToken and RubyLex get defined in the Object namespace,
3
+ # which will conflict with prior definitions from, for instance, IRB.
4
+ #
5
+ # This code redefines the RDoc RubyToken and RubyLex within the RDoc
6
+ # namespace. RDoc is not affected because all includes and uses of
7
+ # RubyToken and RubyLex are set when RDoc is loaded. The single exception
8
+ # I know of are several calls to class methods of RubyLex (ex RubyLex.debug?).
9
+ # These calls will be routed to the existing RubyLex.
10
+ #
11
+ # Uses of the existing RubyToken and RubyLex (as by irb) should be
12
+ # unaffected as the constants are reset after RDoc loads.
13
+ #
14
+ if Object.const_defined?(:RubyToken) || Object.const_defined?(:RubyLex)
15
+ class Object
16
+ old_ruby_token = const_defined?(:RubyToken) ? remove_const(:RubyToken) : nil
17
+ old_ruby_lex = const_defined?(:RubyLex) ? remove_const(:RubyLex) : nil
18
+
19
+ require 'rdoc/rdoc'
20
+
21
+ # if by chance rdoc has ALREADY been loaded then requiring
22
+ # rdoc will not reset RubyToken and RubyLex... in this case
23
+ # the old constants are what you want.
24
+ new_ruby_token = const_defined?(:RubyToken) ? remove_const(:RubyToken) : old_ruby_token
25
+ new_ruby_lex = const_defined?(:RubyLex) ? remove_const(:RubyLex) : old_ruby_lex
26
+
27
+ RDoc.const_set(:RubyToken, new_ruby_token)
28
+ RDoc.const_set(:RubyLex, new_ruby_lex)
29
+
30
+ const_set(:RubyToken, old_ruby_token) unless old_ruby_token == nil
31
+ const_set(:RubyLex, old_ruby_lex) unless old_ruby_lex == nil
32
+ end
33
+ else
34
+ require 'rdoc/rdoc'
35
+
36
+ if Object.const_defined?(:RubyToken) && !RDoc.const_defined?(:RubyToken)
37
+ class Object
38
+ RDoc.const_set(:RubyToken, remove_const(:RubyToken))
39
+ end
40
+ end
41
+
42
+ if Object.const_defined?(:RubyLex) && !RDoc.const_defined?(:RubyLex)
43
+ class Object
44
+ RDoc.const_set(:RubyLex, remove_const(:RubyLex))
45
+ RDoc::RubyLex.const_set(:RubyLex, RDoc::RubyLex)
46
+ end
47
+ end
48
+ end
3
49
 
4
50
  module Tap
5
51
  module Support
6
-
7
- # == Overview
8
- # TDoc hooks into and extends RDoc to make task documentation available for command line
9
- # applications as well as for inclusion in RDoc html. In particular, TDoc makes available
10
- # documentation for Task configurations, when they are present. TDoc provides an extension
11
- # to the standard RDoc HTMLGenerator and template.
52
+
53
+ # TDoc hooks into and extends RDoc to make configuration documentation available as
54
+ # attributes. TDoc provides an extension to the standard RDoc HTMLGenerator and template.
12
55
  #
13
56
  # === Usage
14
- # To generate task documentation with configuration information, TDoc must be loaded and
15
- # the appropriate flags passed to rdoc . Essentially what you want is:
57
+ # To generate task documentation with configuration information, TDoc must be loaded and
58
+ # the appropriate flags passed to rdoc . Essentially what you want is:
16
59
  #
17
60
  # % rdoc --fmt tdoc --template tap/support/tdoc/tdoc_html_template [file_names....]
18
61
  #
19
- # Unfortunately, there is no way to load or require a file into the rdoc utility directly; the
20
- # above code causes an 'Invalid output formatter' error. However, TDoc is easy to utilize
62
+ # Unfortunately, there is no way to load or require a file into the rdoc utility directly; the
63
+ # above code causes an 'Invalid output formatter' error. However, TDoc is easy to utilize
21
64
  # from a Rake::RDocTask:
22
65
  #
23
66
  # require 'rake'
@@ -26,9 +69,9 @@ module Tap
26
69
  # desc 'Generate documentation.'
27
70
  # Rake::RDocTask.new(:rdoc) do |rdoc|
28
71
  # require 'tap/support/tdoc'
29
- # rdoc.template = 'tap/support/tdoc/tdoc_html_template'
72
+ # rdoc.template = 'tap/support/tdoc/tdoc_html_template'
30
73
  # rdoc.options << '--fmt' << 'tdoc'
31
- #
74
+ #
32
75
  # # specify whatever else you need
33
76
  # # rdoc.rdoc_files.include(...)
34
77
  # end
@@ -37,163 +80,325 @@ module Tap
37
80
  #
38
81
  # % rake rdoc
39
82
  #
40
- # TDoc may also be utilized programatically, but you should be aware that RDoc in Ruby
41
- # can raise errors and/or cause namespace conflicts (see below).
42
- #
43
83
  # === Implementation
44
- # RDoc is a beast to utilize in a non-standard way. One way to make RDoc parse unexpected
45
- # flags like 'config_accessor' or the 'c' config specifier is to use the '--accessor' option
46
- # (see 'rdoc --help' or the RDoc documentation for more details).
84
+ # RDoc is a beast to utilize in a non-standard way. One way to make RDoc parse unexpected
85
+ # flags like 'config' or 'config_attr' is to use the '--accessor' option (see 'rdoc --help' or
86
+ # the RDoc documentation for more details).
47
87
  #
48
88
  # TDoc hooks into the '--accessor' parsing process to pull out configuration attributes and
49
- # format them into their own Configuration section on an RDoc html page. When 'tdoc' is
89
+ # format them into their own Configuration section on an RDoc html page. When 'tdoc' is
50
90
  # specified as an rdoc option, TDoc in effect sets accessor flags for all the standard Task
51
- # configuration methods, and then extends the RDoc::RubyParser handle these specially.
91
+ # configuration methods, and then extends the RDoc::RubyParser handle these specially.
52
92
  #
53
- # If 'tdoc' is not specified as the rdoc format, TDoc does not affect the RDoc output.
54
- # Similarly, the configuration attributes will not appear in the output unless you specify a
93
+ # If tdoc is not specified as the rdoc format, TDoc does not affect the RDoc output.
94
+ # Similarly, the configuration attributes will not appear in the output unless you specify a
55
95
  # template that utilizes them.
56
96
  #
57
97
  # === Namespace conflicts
58
98
  # RDoc creates a namespace conflict with other libraries that define RubyToken and RubyLex
59
- # in the Object namespace (the prime example being IRB). TDoc checks for such a conflict
60
- # and redfines the RDoc RubyToken and RubyLex within the RDoc namespace. Essentially:
61
- #
62
- # RubyToken => RDoc::RubyToken
63
- # RubyLex => RDoc::RubyLex
64
- #
65
- # The redefinition should not affect the existing RubyToken and RubyLex constants, but if
66
- # you directly use the RDoc versions after loading TDoc, you should be aware that they must
67
- # be accessed through the new constants. Unfortunatley the trick is not seamless. The RDoc
68
- # RubyLex makes a few calls to the RubyLex class method 'debug?'... these will be issued to
69
- # the existing RubyLex method and not RDoc::RubyLex.debug?
70
- #
71
- # In addition, because of the RubyLex calls, the RDoc::RubyLex cannot be fully hidden when
72
- # TDoc is loaded before the conflicting RubyLex; you cannot load TDoc before loading IRB
73
- # without raising warnings. I hope to submit a patch for RDoc to stop this nonsense in the
74
- # future.
75
- #
76
- # On the plus side, you can now access/use RDoc within irb by requiring 'tap/support/tdoc'.
77
- #
78
- class TDoc
79
- include Singleton
80
-
81
- attr_accessor :stats, :options, :documented_files, :load_paths
82
-
83
- def initialize
84
- reinitialize
85
- end
86
-
87
- def reinitialize(argv=['--fmt', 'tdoc', '--quiet'])
88
- @documented_files = []
89
- @tl = RDoc::TopLevel::reset
90
- @stats = RDoc::Stats.new
91
- @options = Options.instance
92
- @options.parse(argv, RDoc::RDoc::GENERATORS)
93
- @load_paths = ($: + Dependencies.load_paths + Dependencies.load_once_paths).uniq
94
- end
95
-
96
- class << self
99
+ # in the Object namespace (the prime example being IRB). TDoc checks for such a conflict
100
+ # and redfines the RDoc versions of RubyToken and RubyLex within the RDoc namespace.
101
+ # Essentially:
102
+ #
103
+ # original constant redefined constant
104
+ # RubyToken RDoc::RubyToken
105
+ # RubyLex RDoc::RubyLex
106
+ #
107
+ # The redefinition should not affect the existing (non RDoc) RubyToken and RubyLex constants,
108
+ # but if you directly use the RDoc versions after loading TDoc, you should be aware that they must
109
+ # be accessed through the new constants. Unfortunatley the trick is not seamless. The RDoc
110
+ # RubyLex makes a few calls to the RubyLex class method 'debug?'... these will be issued to
111
+ # the existing (non RDoc) RubyLex method and not the redefined RDoc::RubyLex.debug?
112
+ #
113
+ # In addition, because of the RubyLex calls, the RDoc::RubyLex cannot be fully hidden when
114
+ # TDoc is loaded before the conflicting RubyLex; you cannot load TDoc before loading IRB
115
+ # without raising warnings.
116
+ #
117
+ # Luckily all these troubles can be avoided very easily by not loading TDoc or RDoc when
118
+ # you're in irb. On the plus side, going against what I just said, you can now access/use
119
+ # RDoc within irb by requiring <tt>'tap/support/tdoc'</tt>.
120
+ #
121
+ #--
122
+ # Note that tap-0.10.0 heavily refactored TDoc functionality out of the old TDoc and into
123
+ # Lazydoc, and changed the declaration syntax for configurations. These changes also
124
+ # affected the implementation of TDoc. Mostly the changes are hacks to get the old
125
+ # system to work in the new system... as hacky as the old TDoc was, now this TDoc is hacky
126
+ # AND may have cruft. Until it breaks completely, I leave it as is... ugly and hard to fathom.
127
+ #
128
+ module TDoc
129
+
130
+ # Encasulates information about the configuration. Designed to be utilized
131
+ # by the TDocHTMLGenerator as similarly as possible to standard attributes.
132
+ class ConfigAttr < RDoc::Attr
133
+ # Contains the actual declaration for the config attribute. ex: "c [:key, 'value'] # comment"
134
+ attr_accessor :config_declaration, :default
97
135
 
98
- def search_for_files(path_suffix)
99
- # modified from 'activesupport/dependencies'
100
- path_suffix = path_suffix + '.rb' unless path_suffix.ends_with? '.rb'
101
-
102
- files = instance.load_paths.collect do |root|
103
- path = File.join(root, path_suffix)
104
- File.file?(path) ? path : nil
105
- end.compact
136
+ def initialize(*args)
137
+ @comment = nil # suppress a warning in Ruby 1.9
138
+ super
106
139
  end
107
140
 
108
- def search_for_source_files(klass)
109
- source_files = []
110
- # searches back for all configurable source files, so that
111
- # inherited configs can be documented.
112
- while klass.include?(Tap::Support::Configurable)
113
- source_files.concat(search_for_files(klass.to_s.underscore))
114
- klass = klass.superclass
141
+ alias original_comment comment
142
+
143
+ def desc
144
+ case text.to_s
145
+ when /^#--(.*)/ then $1.strip
146
+ when /^#(.*)/ then $1.strip
147
+ else
148
+ nil
115
149
  end
116
-
117
- source_files.uniq
118
150
  end
119
151
 
120
- def document(*filepaths)
121
- filepaths.each do |filepath|
122
- next if filepath == nil || instance.documented_files.include?(filepath) || !File.exists?(filepath)
152
+ # The description for the config. Comment is formed from the standard
153
+ # attribute comment and the text following the attribute, which is slightly
154
+ # different than normal:
155
+ #
156
+ # # standard comment
157
+ # attr_accessor :attribute
158
+ #
159
+ # # standard comment
160
+ # config_accessor :config # ...added to standard comment
161
+ #
162
+ # c [:key, 'value'] # hence you can comment inline like this.
163
+ #
164
+ # The comments for each of these will be:
165
+ # attribute:: standard comment
166
+ # config:: standard comment ...added to standard comment
167
+ # key:: hence you can comment inline like this.
168
+ #
169
+ def comment(add_default=true)
170
+ # this would include the trailing comment...
171
+ # text_comment = text.to_s.sub(/^#--.*/m, '')
172
+ #original_comment.to_s + text_comment + (default && add_default ? " (#{default})" : "")
173
+ comment = original_comment.to_s.strip
174
+ comment = desc.to_s if comment.empty?
175
+ comment + (default && add_default ? " (<tt>#{default}</tt>)" : "")
176
+ end
177
+ end
178
+
179
+ module CodeObjectAccess # :nodoc:
180
+ def comment_sections(section_regexp=//, normalize_comments=false)
181
+ res = {}
182
+
183
+ section = nil
184
+ lines = []
185
+ comment_lines = comment.split(/\r?\n/)
186
+ comment_lines << nil
187
+ comment_lines.each do |line|
188
+ case line
189
+ when nil, /^\s*#\s*=+(.*)/
190
+ next_section = (line == nil ? nil : $1.to_s.strip)
191
+
192
+ if section =~ section_regexp
193
+ lines << "" unless normalize_comments
194
+ res[section] = lines.join("\n") unless section == nil
195
+ end
123
196
 
124
- tl = RDoc::TopLevel.new(filepath)
125
- parser = RDoc::RubyParser.new(tl, filepath, File.read(filepath), instance.options, instance.stats)
126
- parser.scan
127
- instance.documented_files << filepath
197
+ section = next_section
198
+ lines = []
199
+ else
200
+ if normalize_comments
201
+ line =~ /^\s*#\s?(.*)/
202
+ line = $1.to_s
203
+ end
204
+
205
+ lines << line
206
+ end
128
207
  end
208
+
209
+ res
129
210
  end
130
-
211
+ end
212
+
213
+ module ClassModuleAccess # :nodoc:
131
214
  def find_class_or_module_named(name)
132
- RDoc::TopLevel.all_classes_and_modules.each do |c|
133
- res = c.find_class_or_module_named(name)
215
+ return self if full_name == name
216
+ (@classes.values + @modules.values).each do |c|
217
+ res = c.find_class_or_module_named(name)
134
218
  return res if res
135
219
  end
136
220
  nil
137
221
  end
222
+
223
+ def configurations
224
+ @attributes.select do |attribute|
225
+ attribute.kind_of?(TDoc::ConfigAttr)
226
+ end
227
+ end
228
+
229
+ def find_configuration_named(name)
230
+ @attributes.each do |attribute|
231
+ next unless attribute.kind_of?(TDoc::ConfigAttr)
232
+ return attribute if attribute.name == name
233
+ end
234
+ nil
235
+ end
236
+ end
237
+
238
+ # Overrides the new method automatically extend the new object with
239
+ # ConfigParser. Intended to be used like:
240
+ # RDoc::RubyParser.extend InitializeConfigParser
241
+ module InitializeConfigParser # :nodoc:
242
+ def new(*args)
243
+ parser = super
244
+ parser.extend ConfigParser
245
+ #parser.config_mode = 'config_accessor'
246
+ parser
247
+ end
248
+ end
138
249
 
139
- def [](klass)
140
- name = klass.to_s
141
- res = find_class_or_module_named(name)
250
+ # Provides methods extending an RDoc::RubyParser such that the parser will produce
251
+ # TDoc::ConfigAttr instances in the place of RDoc::Attr instances during attribute
252
+ # parsing.
253
+ module ConfigParser # :nodoc:
254
+ include RDoc::RubyToken
255
+ include TokenStream
256
+
257
+ CONFIG_ACCESSORS = ['config', 'config_attr']
142
258
 
143
- # If no result was found, try to document a sourcefile
144
- # from the standard filepath and search again
145
- if res == nil
146
- source_files = klass.respond_to?(:source_files) ? klass.source_files : []
147
- source_files = search_for_source_files(klass) if source_files.empty?
148
-
149
- unless source_files.empty?
150
- document(*source_files)
151
- res = find_class_or_module_named(name)
152
- end
259
+ # Gets tokens until the next TkNL
260
+ def get_tk_to_nl
261
+ tokens = []
262
+ while !(tk = get_tk).kind_of?(TkNL)
263
+ tokens.push tk
153
264
  end
154
-
155
- res
265
+ unget_tk(tk)
266
+ tokens
156
267
  end
157
268
 
158
- def usage(program_file, sections=[], keep_section_headers=false)
159
- comment = []
160
- File.open(program_file) do |file|
161
- while line = file.gets
162
- case line
163
- when /^\s*$/
164
- # skip leading blank lines
165
- comment.empty? ? next : break
166
- when /^\s*# ?(.*)/m
167
- comment << $1
168
- end
169
- end
170
- end
269
+ # Works like the original parse_attr_accessor, except that the arg
270
+ # name is parsed from the config syntax and added attribute will
271
+ # be a TDoc::ConfigAttr. For example:
272
+ #
273
+ # class TaskDoc < Tap::Task
274
+ # config [:key, 'value'] # comment
275
+ # end
276
+ #
277
+ # produces an attribute named :key in the current config_rw mode.
278
+ #
279
+ # (see 'rdoc/parsers/parse_rb' line 2509)
280
+ def parse_config(context, single, tk, comment)
281
+ tks = get_tk_to_nl
171
282
 
172
- unless sections.empty?
173
- sections_hash = {}
174
- current_section = nil
175
- comment.each do |line|
176
- case line
177
- when /^s*=+(.*)/
178
- current_section = []
179
- current_section << line if keep_section_headers
180
- sections_hash[$1.strip] = current_section
283
+ key_tk = nil
284
+ value_tk = nil
285
+
286
+ tks.each do |token|
287
+ next if token.kind_of?(TkSPACE)
288
+
289
+ if key_tk == nil
290
+ case token
291
+ when TkSYMBOL then key_tk = token
292
+ when TkLPAREN then next
293
+ else break
294
+ end
295
+ else
296
+ case token
297
+ when TkCOMMA then value_tk = token
181
298
  else
182
- current_section << line unless current_section.nil?
299
+ value_tk = token if value_tk.kind_of?(TkCOMMA)
300
+ break
183
301
  end
184
302
  end
185
-
186
- comment = []
187
- sections.each do |section|
188
- next unless sections_hash.has_key?(section)
189
- comment.concat(sections_hash[section])
190
- end
191
303
  end
304
+
305
+ text = ""
306
+ if tks.last.kind_of?(TkCOMMENT)
307
+ text = tks.last.text.chomp("\n").chomp("\r")
308
+ unget_tk(tks.last)
309
+
310
+ # If nodoc is given, don't document
311
+
312
+ tmp = RDoc::CodeObject.new
313
+ read_documentation_modifiers(tmp, RDoc::ATTR_MODIFIERS)
314
+ text = nil unless tmp.document_self
315
+ end
316
+
317
+ tks.reverse_each {|token| unget_tk(token) }
318
+ return if key_tk == nil || text == nil
192
319
 
193
- comment.join('')
320
+ arg = key_tk.text[1..-1]
321
+ default = nil
322
+ if value_tk
323
+ if text =~ /(.*):no_default:(.*)/
324
+ text = $1 + $2
325
+ else
326
+ default = value_tk.text
327
+ end
328
+ end
329
+ att = TDoc::ConfigAttr.new(text, arg, "RW", comment)
330
+ att.config_declaration = get_tkread
331
+ att.default = default
332
+
333
+ context.add_attribute(att)
334
+ end
335
+
336
+ # Overrides the standard parse_attr_accessor method to hook in parsing
337
+ # of the config accessors. If the input token is not named as one of the
338
+ # CONFIG_ACCESSORS, it will be processed normally.
339
+ def parse_attr_accessor(context, single, tk, comment)
340
+ case tk.name
341
+ when 'config', 'config_attr'
342
+ parse_config(context, single, tk, comment)
343
+ else
344
+ super
345
+ end
194
346
  end
195
347
  end
196
348
  end
197
349
  end
198
350
  end
199
351
 
352
+ # Register the TDoc generator (in case you want to actually use it).
353
+ # method echos RDoc generator registration (see 'rdoc/rdoc' line 76)
354
+ Generator = Struct.new(:file_name, :class_name, :key)
355
+ RDoc::RDoc::GENERATORS['tdoc'] = Generator.new(
356
+ "tap/support/tdoc/tdoc_html_generator.rb",
357
+ "TDocHTMLGenerator".intern,
358
+ "tdoc")
359
+
360
+ # Add the extended accessors to context classes.
361
+ module RDoc # :nodoc:
362
+ class CodeObject # :nodoc:
363
+ include Tap::Support::TDoc::CodeObjectAccess
364
+ end
365
+
366
+ class ClassModule # :nodoc:
367
+ include Tap::Support::TDoc::ClassModuleAccess
368
+ end
369
+ end
370
+
371
+ # Override methods in Options to in effect incorporate the accessor
372
+ # flags for TDoc parsing. (see 'rdoc/options') Raise an error if an
373
+ # accessor flag has already been specified.
374
+ class Options # :nodoc:
375
+ alias tdoc_original_parse parse
376
+
377
+ def parse(argv, generators)
378
+ tdoc_original_parse(argv, generators)
379
+ return unless @generator_name == 'tdoc'
380
+
381
+ accessors = Tap::Support::TDoc::ConfigParser::CONFIG_ACCESSORS
382
+
383
+ # check the config_accessor_flags for accessor conflicts
384
+ extra_accessor_flags.each_pair do |accessor, flag|
385
+ if accessors.include?(accessor)
386
+ raise OptionList.error("tdoc format already handles the accessor '#{accessor}'")
387
+ end
388
+ end
389
+
390
+ # extra_accessors will be nil if no extra accessors were
391
+ # specifed, otherwise it'll be a regexp like /^(...)$/
392
+ # the string subset assumes
393
+ # regexp.to_s # => /(?-mix:^(...)$)/
394
+ @extra_accessors ||= /^()$/
395
+ current_accessors_str = @extra_accessors.to_s[9..-4]
396
+
397
+ # echos the Regexp production code in rdoc/options.rb
398
+ # (see the parse method, line 501)
399
+ re = '^(' + current_accessors_str + accessors.map{|a| Regexp.quote(a)}.join('|') + ')$'
400
+ @extra_accessors = Regexp.new(re)
401
+
402
+ RDoc::RubyParser.extend Tap::Support::TDoc::InitializeConfigParser
403
+ end
404
+ end