inochi 1.1.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (104) hide show
  1. data/CREDITS +17 -1
  2. data/bin/inochi +19 -539
  3. data/lib/inochi.rb +1 -41
  4. data/lib/inochi/engine.rb +101 -0
  5. data/lib/inochi/generate.rb +52 -0
  6. data/lib/inochi/inochi.rb +109 -0
  7. data/lib/inochi/tasks/ann.rake +230 -0
  8. data/lib/inochi/tasks/api.rake +28 -0
  9. data/lib/inochi/tasks/gem.rake +46 -0
  10. data/lib/inochi/tasks/init.rake +38 -0
  11. data/lib/inochi/tasks/man.rake +78 -0
  12. data/lib/inochi/tasks/project.rake +25 -0
  13. data/lib/inochi/tasks/pub.rake +130 -0
  14. data/lib/inochi/templates/CREDITS.rbs +18 -0
  15. data/lib/inochi/templates/EXAMPLES.rbs +24 -0
  16. data/lib/inochi/templates/FURTHER.rbs +17 -0
  17. data/lib/inochi/templates/HACKING.rbs +92 -0
  18. data/lib/inochi/templates/HISTORY.rbs +55 -0
  19. data/lib/inochi/templates/LICENSE.rbs +15 -0
  20. data/lib/inochi/templates/MANUAL.rbs +27 -0
  21. data/lib/inochi/templates/README.rbs +60 -0
  22. data/lib/inochi/templates/USAGE.rbs +23 -0
  23. data/lib/inochi/templates/command.rbs +23 -0
  24. data/lib/inochi/templates/inochi.opts.rbs +43 -0
  25. data/lib/inochi/templates/inochi.rb.rbs +102 -0
  26. data/lib/inochi/templates/library.rbs +7 -0
  27. data/lib/inochi/templates/library_test.rb.rbs +3 -0
  28. data/lib/inochi/templates/test_helper.rb.rbs +3 -0
  29. data/lib/inochi/templates/test_runner.rbs +25 -0
  30. data/{doc → logo}/inochi.png +0 -0
  31. data/man.html +959 -0
  32. data/man/man1/inochi.1.gz +0 -0
  33. metadata +129 -198
  34. data/doc/README +0 -6
  35. data/doc/api/apple-touch-icon.png +0 -0
  36. data/doc/api/classes/Array.html +0 -370
  37. data/doc/api/classes/File.html +0 -110
  38. data/doc/api/classes/Inochi.html +0 -1477
  39. data/doc/api/classes/Inochi/Manual.html +0 -157
  40. data/doc/api/classes/Inochi/Phrases.html +0 -331
  41. data/doc/api/classes/Inochi/Version.html +0 -190
  42. data/doc/api/classes/TempDir.html +0 -164
  43. data/doc/api/created.rid +0 -1
  44. data/doc/api/css/main.css +0 -263
  45. data/doc/api/css/panel.css +0 -383
  46. data/doc/api/css/reset.css +0 -53
  47. data/doc/api/favicon.ico +0 -0
  48. data/doc/api/files/CREDITS.html +0 -61
  49. data/doc/api/files/LICENSE.html +0 -76
  50. data/doc/api/files/lib/inochi/book_rb.html +0 -106
  51. data/doc/api/files/lib/inochi/init_rb.html +0 -66
  52. data/doc/api/files/lib/inochi/main_rb.html +0 -52
  53. data/doc/api/files/lib/inochi/rake_rb.html +0 -52
  54. data/doc/api/files/lib/inochi/test/bacon_rb.html +0 -67
  55. data/doc/api/files/lib/inochi/test/context_rb.html +0 -69
  56. data/doc/api/files/lib/inochi/test/dfect_rb.html +0 -67
  57. data/doc/api/files/lib/inochi/test/matchy_rb.html +0 -69
  58. data/doc/api/files/lib/inochi/test/minitest_rb.html +0 -71
  59. data/doc/api/files/lib/inochi/test/mocha_rb.html +0 -67
  60. data/doc/api/files/lib/inochi/test/rspec_rb.html +0 -67
  61. data/doc/api/files/lib/inochi/test/shoulda_rb.html +0 -67
  62. data/doc/api/files/lib/inochi/test/test_spec_rb.html +0 -67
  63. data/doc/api/files/lib/inochi/test/test_unit_rb.html +0 -67
  64. data/doc/api/files/lib/inochi/util/combo_rb.html +0 -59
  65. data/doc/api/files/lib/inochi/util/tempdir_rb.html +0 -68
  66. data/doc/api/files/lib/inochi/util_rb.html +0 -59
  67. data/doc/api/files/lib/inochi_rb.html +0 -76
  68. data/doc/api/i/arrows.png +0 -0
  69. data/doc/api/i/results_bg.png +0 -0
  70. data/doc/api/i/tree_bg.png +0 -0
  71. data/doc/api/index.html +0 -14
  72. data/doc/api/js/jquery-1.3.2.min.js +0 -19
  73. data/doc/api/js/jquery-effect.js +0 -593
  74. data/doc/api/js/main.js +0 -22
  75. data/doc/api/js/searchdoc.js +0 -628
  76. data/doc/api/panel/index.html +0 -71
  77. data/doc/api/panel/search_index.js +0 -1
  78. data/doc/api/panel/tree.js +0 -1
  79. data/doc/history.erb +0 -268
  80. data/doc/index.erb +0 -11
  81. data/doc/index.html +0 -3179
  82. data/doc/inochi.svg +0 -405
  83. data/doc/intro.erb +0 -87
  84. data/doc/setup.erb +0 -105
  85. data/doc/usage.erb +0 -641
  86. data/lib/inochi/book.rb +0 -91
  87. data/lib/inochi/init.rb +0 -256
  88. data/lib/inochi/main.rb +0 -85
  89. data/lib/inochi/rake.rb +0 -902
  90. data/lib/inochi/test/bacon.rb +0 -3
  91. data/lib/inochi/test/context.rb +0 -4
  92. data/lib/inochi/test/dfect.rb +0 -3
  93. data/lib/inochi/test/matchy.rb +0 -4
  94. data/lib/inochi/test/minitest.rb +0 -7
  95. data/lib/inochi/test/mocha.rb +0 -3
  96. data/lib/inochi/test/rspec.rb +0 -3
  97. data/lib/inochi/test/shoulda.rb +0 -3
  98. data/lib/inochi/test/test_spec.rb +0 -3
  99. data/lib/inochi/test/test_unit.rb +0 -3
  100. data/lib/inochi/util.rb +0 -99
  101. data/lib/inochi/util/combo.rb +0 -191
  102. data/lib/inochi/util/tempdir.rb +0 -29
  103. data/rakefile +0 -12
  104. data/test/inochi.rb +0 -111
@@ -1,41 +1 @@
1
- #--
2
- # Copyright protects this work.
3
- # See LICENSE file for details.
4
- #++
5
-
6
- require 'rubygems'
7
-
8
- module Inochi
9
- libs = File.dirname(__FILE__)
10
- $LOAD_PATH << libs unless $LOAD_PATH.include? libs
11
- end
12
-
13
- require 'inochi/init'
14
- require 'inochi/main'
15
- require 'inochi/rake'
16
- require 'inochi/book'
17
- require 'inochi/util'
18
-
19
- Inochi.init :Inochi,
20
- :version => '1.1.1',
21
- :release => '2009-10-03',
22
- :website => 'http://snk.tuxfamily.org/lib/inochi/',
23
- :tagline => 'Gives life to RubyGems-based software',
24
- :require => {
25
- 'trollop' => '~> 1', # for parsing command-line
26
- 'launchy' => ['~> 0', '>= 0.3.3'], # for launching a browser
27
- },
28
- :develop => {
29
- 'rake' => ['~> 0', '>= 0.8.4'],
30
- 'rubyforge' => '~> 2', # for publishing gems
31
- 'mechanize' => '~> 0', # for web automation
32
- 'voloko-sdoc' => ['~> 0', '>= 0.2.10'], # for API docs
33
- 'addressable' => '~> 2', # for parsing URIs properly
34
- 'erbook' => '~> 7', # for generating user manual
35
- 'babelfish' => '~> 0', # for language translation
36
- 'relevance-rcov' => nil, # for code coverage statistics
37
- 'flay' => nil, # for code quality analysis
38
- 'reek' => nil, # for code quality analysis
39
- 'roodi' => nil, # for code quality analysis
40
- 'ZenTest' => '~> 4', # for the `multiruby` tool
41
- }
1
+ require 'inochi/inochi'
@@ -0,0 +1,101 @@
1
+ require 'yaml'
2
+ require 'time'
3
+ require 'rake'
4
+ require 'rake/clean'
5
+ require 'shellwords'
6
+
7
+ module Inochi
8
+ class Engine
9
+
10
+ require 'inochi/generate'
11
+ include Inochi::Generate
12
+
13
+ def run
14
+ register_rake_tasks
15
+ run_rake_tasks
16
+ end
17
+
18
+ ##
19
+ # Renders the given RBS (ruby string) file
20
+ # and/or template inside the given binding.
21
+ #
22
+ def self.render_rbs binding, filename, template = File.read(filename)
23
+ here = "TEMPORARY_HERE_DOC#{object_id}TEMPORARY_HERE_DOC"
24
+ eval("<<#{here}\n#{template}\n#{here}", binding, filename).chomp
25
+ end
26
+
27
+ private
28
+
29
+ def run_rake_tasks
30
+ task :default do
31
+ Rake.application.options.show_task_pattern = //
32
+ Rake.application.display_tasks_and_comments
33
+ end
34
+
35
+ Rake.application.init 'inochi'
36
+ Rake.application.top_level
37
+ end
38
+
39
+ def register_rake_tasks
40
+ Dir[File.dirname(__FILE__) + '/tasks/*.rake'].each do |file|
41
+ instance_eval File.read(file), file
42
+ end
43
+ end
44
+
45
+ TEMPLATE_DIR = File.join(File.dirname(__FILE__), 'templates')
46
+
47
+ ##
48
+ # Renders the given RBS template file (found in the
49
+ # TEMPLATE_DIR directory) to the given output file.
50
+ #
51
+ def create_from_rbs binding, output_file, template_file = File.basename(output_file)
52
+ actual_template_file = "#{TEMPLATE_DIR}/#{template_file}.rbs"
53
+ create output_file, self.class.render_rbs(binding, actual_template_file)
54
+ end
55
+
56
+ ##
57
+ # Writes the given contents to the file at the given
58
+ # path. If the given path already exists, then a
59
+ # backup is created before invoking the merging tool.
60
+ #
61
+ def create path, body, merger = ENV[:merger]
62
+ generate path, body do |*files|
63
+ system "#{merger} #{Shellwords.join files}" if merger
64
+ end
65
+ end
66
+
67
+ ##
68
+ # Returns the name of the main program executable, which
69
+ # is the same as the project name fully in lowercase.
70
+ #
71
+ def self.calc_package_name library_name
72
+ camel_to_snake_case(library_name).downcase
73
+ end
74
+
75
+ ##
76
+ # Calculates the name of the project module from the given project name.
77
+ #
78
+ def self.calc_library_name project_name
79
+ name = project_name.to_s.gsub(/\W+/, '_').squeeze('_').gsub(/^_|_$/, '')
80
+ (name[0,1].upcase + name[1..-1]).to_sym
81
+ end
82
+
83
+ ##
84
+ # Transforms the given input from CamelCase to snake_case.
85
+ #
86
+ def self.camel_to_snake_case input
87
+ input = input.to_s.dup
88
+
89
+ # handle camel case like FooBar => Foo_Bar
90
+ while input.gsub!(/([a-z]+)([A-Z])(\w+)/) { $1 + '_' + $2 + $3 }
91
+ end
92
+
93
+ # handle abbreviations like XMLParser => XML_Parser
94
+ while input.gsub!(/([A-Z]+)([A-Z])([a-z]+)/) { $1 + '_' + $2 + $3 }
95
+ end
96
+
97
+ input
98
+ end
99
+
100
+ end
101
+ end
@@ -0,0 +1,52 @@
1
+ require 'fileutils'
2
+ require 'digest/sha1'
3
+
4
+ module Inochi
5
+ module Generate
6
+ extend self
7
+
8
+ ##
9
+ # Notify the user about some action being performed.
10
+ #
11
+ def notify action, message
12
+ printf "%16s %s\n", action, message
13
+ end
14
+
15
+ ##
16
+ # Writes the given contents to the file at the given
17
+ # path. If the given path already exists, then a
18
+ # backup is created before invoking the given block.
19
+ #
20
+ def generate path, content # :yields: old_file, new_file, output_file
21
+ if File.exist? path
22
+ old_digest = Digest::SHA1.digest(File.read(path))
23
+ new_digest = Digest::SHA1.digest(content)
24
+
25
+ if old_digest == new_digest
26
+ notify :skip, path
27
+ else
28
+ notify :update, path
29
+ cur, old, new = path, "#{path}.old", "#{path}.new"
30
+
31
+ FileUtils.cp cur, old, :preserve => true
32
+ File.write new, content
33
+
34
+ yield old, new, cur if block_given?
35
+ end
36
+ else
37
+ notify :create, path
38
+ FileUtils.mkdir_p File.dirname(path)
39
+ File.write path, content
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ unless File.respond_to? :write
46
+ ##
47
+ # Writes the given content to the given file.
48
+ #
49
+ def File.write path, content
50
+ open(path, 'wb') {|f| f.write content }
51
+ end
52
+ end
@@ -0,0 +1,109 @@
1
+ module Inochi
2
+
3
+ ##
4
+ # Official name of this project.
5
+ #
6
+ PROJECT = 'Inochi'
7
+
8
+ ##
9
+ # Short single-line description of this project.
10
+ #
11
+ TAGLINE = 'Gives life to Ruby projects'
12
+
13
+ ##
14
+ # Address of this project's official home page.
15
+ #
16
+ WEBSITE = 'http://snk.tuxfamily.org/lib/inochi/'
17
+
18
+ ##
19
+ # Number of this release of this project.
20
+ #
21
+ VERSION = '2.0.0'
22
+
23
+ ##
24
+ # Date of this release of this project.
25
+ #
26
+ RELDATE = '2010-04-24'
27
+
28
+ ##
29
+ # Description of this release of this project.
30
+ #
31
+ def self.inspect
32
+ "#{PROJECT} #{VERSION} (#{RELDATE})"
33
+ end
34
+
35
+ ##
36
+ # Location of this release of this project.
37
+ #
38
+ INSTDIR = File.expand_path('../../..', __FILE__)
39
+
40
+ ##
41
+ # RubyGems required by this project during runtime.
42
+ #
43
+ # @example
44
+ #
45
+ # RUNTIME = {
46
+ # # this project needs exactly version 1.2.3 of the "an_example" gem
47
+ # "an_example" => [ "1.2.3" ],
48
+ #
49
+ # # this project needs at least version 1.2 (but not
50
+ # # version 1.2.4 or newer) of the "another_example" gem
51
+ # "another_example" => [ ">= 1.2" , "< 1.2.4" ],
52
+ #
53
+ # # this project needs any version of the "yet_another_example" gem
54
+ # "yet_another_example" => [],
55
+ # }
56
+ #
57
+ RUNTIME = {
58
+ 'highline' => [ '>= 1.5' , '< 2' ], # for echoless password entry
59
+ 'mechanize' => [ '~> 1' ], # for publishing announcements
60
+ 'nokogiri' => [ '>= 1.4' , '< 2' ], # for parsing HTML and XML
61
+ 'rake' => [ '>= 0.8.4' , '< 1' ], # for Inochi::Engine
62
+ 'ronn' => [ '>= 0.5' , '< 1' ], # for making UNIX man pages
63
+ 'yard' => [ ], # for making API documentation
64
+ }
65
+
66
+ ##
67
+ # RubyGems required by this project during development.
68
+ #
69
+ # @example
70
+ #
71
+ # DEVTIME = {
72
+ # # this project needs exactly version 1.2.3 of the "an_example" gem
73
+ # "an_example" => [ "1.2.3" ],
74
+ #
75
+ # # this project needs at least version 1.2 (but not
76
+ # # version 1.2.4 or newer) of the "another_example" gem
77
+ # "another_example" => [ ">= 1.2" , "< 1.2.4" ],
78
+ #
79
+ # # this project needs any version of the "yet_another_example" gem
80
+ # "yet_another_example" => [],
81
+ # }
82
+ #
83
+ DEVTIME = {
84
+ 'dfect' => [ '>= 1.1.0', '< 2' ], # for unit testing
85
+ }
86
+
87
+ ##
88
+ # Loads the correct version (as defined by the {RUNTIME} or {DEVTIME}
89
+ # constant in this module) of the given gem or the gem that contains
90
+ # the given library.
91
+ #
92
+ def self.require gem_name_or_library
93
+ # prepare the correct version of the gem for loading
94
+ if respond_to? :gem
95
+ gem_name = gem_name_or_library.to_s.sub(%r{/.*$}, '')
96
+ if gem_version = RUNTIME[gem_name] || DEVTIME[gem_name]
97
+ begin
98
+ gem gem_name, *gem_version
99
+ rescue LoadError => error
100
+ warn "#{self.inspect}: #{error}"
101
+ end
102
+ end
103
+ end
104
+
105
+ # do the loading
106
+ super
107
+ end
108
+
109
+ end
@@ -0,0 +1,230 @@
1
+ desc 'Build all release announcements.'
2
+ task :ann => %w[ ann:html ann:text ann:feed ]
3
+
4
+ # it has long been a tradition to use an "[ANN]" prefix
5
+ # when announcing things on the ruby-talk mailing list
6
+ @ann_subject_prefix = '[ANN] '
7
+
8
+ task :@ann_subject do
9
+ unless @ann_subject
10
+ Rake::Task[:@project].invoke
11
+ @ann_subject = @ann_subject_prefix +
12
+ @project_module::PROJECT + ' ' + @project_module::VERSION
13
+ end
14
+ end
15
+
16
+ # fetch project description from manual
17
+ task :@ann_nfo_html_nodes do
18
+ unless @ann_nfo_html_nodes
19
+ begin
20
+ head, body = fetch_nodes_between('h2#ABOUT', 'h1,h2,h3,h4,h5,h6')
21
+ rescue => error
22
+ error.message.insert 0,
23
+ "The manual lacks a <H2> ABOUT heading.\n"
24
+ raise error
25
+ end
26
+
27
+ @ann_nfo_html_nodes = body
28
+ end
29
+ end
30
+
31
+ task :@ann_nfo_text do
32
+ unless @ann_nfo_text
33
+ Rake::Task[:@ann_nfo_html_nodes].invoke
34
+ @ann_nfo_text = nodes_inner_text(@ann_nfo_html_nodes)
35
+ end
36
+ end
37
+
38
+ # fetch release notes from manual
39
+ task :@ann_rel_html_body_nodes do
40
+ unless @ann_rel_html_body_nodes
41
+ begin
42
+ head, body = fetch_nodes_between('h2#VERSIONS ~ h3', 'h1,h2,h3')
43
+ rescue => error
44
+ error.message.insert 0,
45
+ "The manual lacks a <H3> heading under a <H2> VERSIONS heading.\n"
46
+ raise error
47
+ end
48
+
49
+ @ann_rel_html_title_node = head
50
+ @ann_rel_html_body_nodes = body
51
+ end
52
+ end
53
+
54
+ # fetch authors list from manual
55
+ task :@project_authors_html_nodes do
56
+ unless @project_authors_html_nodes
57
+ begin
58
+ head, body = fetch_nodes_between('h2#AUTHORS,h2#CREDITS',
59
+ 'h1,h2,h3,h4,h5,h6')
60
+ rescue => error
61
+ error.message.insert 0,
62
+ "The manual lacks content under a <H2> AUTHORS or CREDITS heading.\n"
63
+ raise error
64
+ end
65
+
66
+ @project_authors_html_nodes = body
67
+ end
68
+ end
69
+
70
+ task :@project_authors_text do
71
+ unless @project_authors_text
72
+ Rake::Task[:@project_authors_html_nodes].invoke
73
+ @project_authors_text = nodes_inner_text(@project_authors_html_nodes)
74
+ end
75
+ end
76
+
77
+ # build release announcement
78
+ task :@ann_html do
79
+ unless @ann_html
80
+ Rake::Task[:@ann_nfo_html_nodes].invoke
81
+ Rake::Task[:@ann_rel_html_body_nodes].invoke
82
+
83
+ @ann_html = %{
84
+ <center>
85
+ <h1>#{@project_module::PROJECT}</h1>
86
+ <h2>#{@project_module::TAGLINE}</h2>
87
+ <p>#{@project_module::WEBSITE}</p>
88
+ </center>
89
+ #{@ann_nfo_html_nodes.join}
90
+ #{@ann_rel_html_body_nodes.join}
91
+ }.strip
92
+
93
+ @ann_html = resolve_html_links(@ann_html)
94
+ end
95
+ end
96
+
97
+ task :@ann_text do
98
+ unless @ann_text
99
+ Rake::Task[:@ann_html].invoke
100
+ @ann_text = convert_html_to_text(@ann_html)
101
+ end
102
+ end
103
+
104
+ #-----------------------------------------------------------------------------
105
+ # HTML
106
+ #-----------------------------------------------------------------------------
107
+
108
+ @ann_html_dst = 'ann.html'
109
+
110
+ desc 'Build HTML announcement.'
111
+ task 'ann:html' => @ann_html_dst
112
+
113
+ file @ann_html_dst => @man_src do
114
+ Rake::Task[:@ann_html].invoke
115
+ File.write @ann_html_dst, @ann_html
116
+ end
117
+
118
+ CLOBBER.include @ann_html_dst
119
+
120
+ #-----------------------------------------------------------------------------
121
+ # plain text
122
+ #-----------------------------------------------------------------------------
123
+
124
+ @ann_text_dst = 'ann.txt'
125
+
126
+ desc 'Build plain text announcement.'
127
+ task 'ann:text' => @ann_text_dst
128
+
129
+ file @ann_text_dst => @man_src do
130
+ Rake::Task[:@ann_text].invoke
131
+ File.write @ann_text_dst, @ann_text
132
+ end
133
+
134
+ CLOBBER.include @ann_text_dst
135
+
136
+ #-----------------------------------------------------------------------------
137
+ # RSS feed
138
+ #-----------------------------------------------------------------------------
139
+
140
+ @ann_feed_dst = 'ann.xml'
141
+
142
+ desc 'Build RSS feed announcement.'
143
+ task 'ann:feed' => @ann_feed_dst
144
+
145
+ file @ann_feed_dst => @man_src do
146
+ Rake::Task[:@project].invoke
147
+ Rake::Task[:@ann_nfo_html_nodes].invoke
148
+ Rake::Task[:@ann_rel_html_body_nodes].invoke
149
+
150
+ require 'rss/maker'
151
+ rss = RSS::Maker.make('2.0') do |feed|
152
+ feed.channel.title = @ann_subject_prefix + @project_module::PROJECT
153
+ feed.channel.link = @project_module::WEBSITE
154
+ feed.channel.description = @ann_nfo_html_nodes.join
155
+
156
+ item = feed.items.new_item
157
+ item.link = @project_module::WEBSITE
158
+ require 'time'
159
+ item.date = Time.parse(@project_module::RELDATE)
160
+ item.title = @ann_rel_html_title_node.inner_text
161
+ item.description = @ann_rel_html_body_nodes.join
162
+ end
163
+
164
+ File.write @ann_feed_dst, rss
165
+ end
166
+
167
+ CLOBBER.include @ann_feed_dst
168
+
169
+ #-----------------------------------------------------------------------------
170
+ # helper logic
171
+ #-----------------------------------------------------------------------------
172
+
173
+ def nodes_inner_text nodes
174
+ nodes.map {|n| n.inner_text }.join(' ').gsub(/\n/, ' ').squeeze(' ').strip
175
+ end
176
+
177
+ ##
178
+ # Fetches all nodes between those matching the given head and tail selectors.
179
+ #
180
+ def fetch_nodes_between head_selector, tail_selector
181
+ Rake::Task[:@man_html_dom].invoke
182
+
183
+ head = @man_html_dom.at(head_selector)
184
+ body = []
185
+
186
+ tail = head
187
+ while tail = tail.next_sibling and not tail.matches? tail_selector
188
+ body << tail
189
+ end
190
+
191
+ [head, body, tail]
192
+ end
193
+
194
+ ##
195
+ # Converts the given HTML into plain text. we do this using
196
+ # lynx because (1) it outputs a list of all hyperlinks used
197
+ # in the HTML document and (2) it runs on all major platforms
198
+ #
199
+ def convert_html_to_text html
200
+ # lynx's -dump option requires a .html file
201
+ require 'tempfile'
202
+ tmp_file = Tempfile.new($$).path + '.html'
203
+
204
+ begin
205
+ File.write tmp_file, html
206
+
207
+ `lynx -dump #{tmp_file} -width 70`.
208
+ #
209
+ # improve readability of list items
210
+ # by adding a blank line between them
211
+ #
212
+ gsub(/(\r?\n)( +\* \S)/, '\1\1\2')
213
+ ensure
214
+ File.delete tmp_file
215
+ end
216
+ end
217
+
218
+ ##
219
+ # Converts relative URLs in the given HTML into
220
+ # absolute URLs bound to the given base URL.
221
+ #
222
+ # http://en.wikipedia.org/wiki/URI_scheme#Generic_syntax
223
+ #
224
+ def resolve_html_links html, base_url = nil
225
+ Rake::Task[:@project].invoke
226
+ base_url ||= @project_module::WEBSITE
227
+
228
+ require 'cgi'
229
+ "<base href='#{CGI.escapeHTML base_url}'/> #{html}"
230
+ end