docter 1.0.1 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +6 -2
- data/LICENSE +0 -26
- data/Rakefile +45 -60
- data/lib/docter/collection.rb +40 -35
- data/lib/docter/common.rb +78 -35
- data/lib/docter/page.rb +38 -38
- data/lib/docter/server.rb +13 -13
- data/lib/docter/template.rb +19 -19
- data/lib/docter/ultraviolet.rb +11 -11
- data/lib/docter.rb +16 -16
- metadata +74 -64
data/CHANGELOG
CHANGED
@@ -1,7 +1,11 @@
|
|
1
|
-
1.0
|
1
|
+
1.1.0 (2007-01-03)
|
2
|
+
* Changed: Now using YAML for nested ToC.
|
3
|
+
* Fixed: Sleek upload with changelog for each release courtesy of Anatol Pomozov.
|
4
|
+
|
5
|
+
1.0.1 (2007-06-06)
|
2
6
|
* Added: renumber_footnotes to template for handling footnote numbering when creating a single page.
|
3
7
|
* Changed: Ultraviolet no longer used as default syntax highlighting, must require separately.
|
4
8
|
* Changed: footnote_links is now list_links and the new method eliminates duplicates (based on URL), sorts alphabetically (the text component) and capitalizes the text description.
|
5
9
|
|
6
|
-
1.0.0 (
|
10
|
+
1.0.0 (2007-06-03)
|
7
11
|
* First release of working code.
|
data/LICENSE
CHANGED
@@ -174,29 +174,3 @@
|
|
174
174
|
of your accepting any such warranty or additional liability.
|
175
175
|
|
176
176
|
END OF TERMS AND CONDITIONS
|
177
|
-
|
178
|
-
APPENDIX: How to apply the Apache License to your work.
|
179
|
-
|
180
|
-
To apply the Apache License to your work, attach the following
|
181
|
-
boilerplate notice, with the fields enclosed by brackets "[]"
|
182
|
-
replaced with your own identifying information. (Don't include
|
183
|
-
the brackets!) The text should be enclosed in the appropriate
|
184
|
-
comment syntax for the file format. We also recommend that a
|
185
|
-
file or class name and description of purpose be included on the
|
186
|
-
same "printed page" as the copyright notice for easier
|
187
|
-
identification within third-party archives.
|
188
|
-
|
189
|
-
Copyright 2006-2007, Intalio Inc.
|
190
|
-
|
191
|
-
Licensed under the Apache License, Version 2.0 (the "License");
|
192
|
-
you may not use this file except in compliance with the License.
|
193
|
-
You may obtain a copy of the License at
|
194
|
-
|
195
|
-
http://www.apache.org/licenses/LICENSE-2.0
|
196
|
-
|
197
|
-
Unless required by applicable law or agreed to in writing, software
|
198
|
-
distributed under the License is distributed on an "AS IS" BASIS,
|
199
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
200
|
-
See the License for the specific language governing permissions and
|
201
|
-
limitations under the License.
|
202
|
-
|
data/Rakefile
CHANGED
@@ -1,48 +1,32 @@
|
|
1
|
-
require
|
1
|
+
require 'rubygems'
|
2
2
|
Gem::manage_gems
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require "lib/docter"
|
3
|
+
require 'rake/gempackagetask'
|
4
|
+
require 'rake/rdoctask'
|
5
|
+
require 'rubyforge'
|
7
6
|
|
8
7
|
|
9
8
|
# Gem specification comes first, other tasks rely on it.
|
10
9
|
spec = Gem::Specification.new do |spec|
|
11
|
-
spec.name =
|
12
|
-
spec.version = File.read(__FILE__.pathmap(
|
13
|
-
spec.author =
|
14
|
-
spec.email =
|
15
|
-
spec.homepage =
|
16
|
-
spec.summary =
|
17
|
-
spec.files = FileList[
|
18
|
-
spec.require_path =
|
19
|
-
spec.autorequire = "docter.rb"
|
10
|
+
spec.name = 'docter'
|
11
|
+
spec.version = File.read(__FILE__.pathmap('%d/lib/docter.rb')).scan(/VERSION\s*=\s*(['"])(.*)\1/)[0][1]
|
12
|
+
spec.author = 'Apache Buildr'
|
13
|
+
spec.email = 'buildr-user@incubator.apache.org'
|
14
|
+
spec.homepage = 'http://incubator.apache.org/buildr/'
|
15
|
+
spec.summary = 'We has docs'
|
16
|
+
spec.files = FileList['lib/**/*', 'CHANGELOG', 'README', 'LICENSE', 'Rakefile', 'html/**/*'].collect
|
17
|
+
spec.require_path = 'lib'
|
20
18
|
spec.has_rdoc = true
|
21
|
-
spec.extra_rdoc_files = [
|
22
|
-
spec.rdoc_options <<
|
23
|
-
|
24
|
-
|
19
|
+
spec.extra_rdoc_files = ['README', 'CHANGELOG', 'LICENSE']
|
20
|
+
spec.rdoc_options << '--title' << "Docter -- #{spec.summary}" <<
|
21
|
+
'--main' << 'README' << '--line-numbers' << '--inline-source' << '-p' <<
|
22
|
+
'--webcvs' << 'http://svn.apache.org/repos/asf/incubator/buildr/docter/trunk/'
|
23
|
+
spec.rubyforge_project = 'buildr'
|
25
24
|
|
26
25
|
# Tested against these dependencies.
|
27
|
-
spec.add_dependency
|
28
|
-
spec.add_dependency
|
29
|
-
spec.add_dependency
|
30
|
-
spec.add_dependency
|
31
|
-
end
|
32
|
-
|
33
|
-
|
34
|
-
# Testing is everything.
|
35
|
-
desc "Run test cases"
|
36
|
-
Spec::Rake::SpecTask.new(:test) do |task|
|
37
|
-
task.spec_files = FileList["test/**/*.rb"]
|
38
|
-
task.spec_opts = [ "--format", "specdoc", "--color", "--diff" ]
|
39
|
-
end
|
40
|
-
|
41
|
-
desc "Run test cases with rcov"
|
42
|
-
Spec::Rake::SpecTask.new(:rcov) do |task|
|
43
|
-
task.spec_files = FileList["test/**/*.rb"]
|
44
|
-
task.spec_opts = [ "--format", "specdoc", "--color", "--diff" ]
|
45
|
-
task.rcov = true
|
26
|
+
spec.add_dependency 'facets', '~> 2.2'
|
27
|
+
spec.add_dependency 'RedCloth', '~> 3.0'
|
28
|
+
spec.add_dependency 'haml', '~> 1.7'
|
29
|
+
spec.add_dependency 'mongrel', '~> 1.1'
|
46
30
|
end
|
47
31
|
|
48
32
|
|
@@ -52,66 +36,67 @@ Rake::GemPackageTask.new(spec) do |pkg|
|
|
52
36
|
pkg.need_zip = true
|
53
37
|
end
|
54
38
|
|
55
|
-
desc
|
39
|
+
desc 'Install the package locally'
|
56
40
|
task :install=>:package do |task|
|
57
|
-
system
|
41
|
+
system 'gem', 'install', "pkg/#{spec.name}-#{spec.version}.gem"
|
58
42
|
end
|
59
43
|
|
60
|
-
desc
|
44
|
+
desc 'Uninstall previously installed packaged'
|
61
45
|
task :uninstall do |task|
|
62
|
-
system
|
46
|
+
system 'gem', 'uninstall', spec.name, '-v', spec.version.to_s
|
63
47
|
end
|
64
48
|
|
65
49
|
|
66
|
-
desc
|
50
|
+
desc 'Generate RDoc documentation'
|
67
51
|
rdoc = Rake::RDocTask.new(:rdoc) do |rdoc|
|
68
|
-
rdoc.rdoc_dir =
|
52
|
+
rdoc.rdoc_dir = 'rdoc'
|
69
53
|
rdoc.title = spec.name
|
70
54
|
rdoc.options = spec.rdoc_options
|
71
|
-
rdoc.rdoc_files.include(
|
55
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
72
56
|
rdoc.rdoc_files.include spec.extra_rdoc_files
|
73
57
|
end
|
74
58
|
|
75
|
-
task(
|
59
|
+
task('clobber') { rm_rf [rdoc.rdoc_dir.to_s] }
|
76
60
|
|
77
61
|
|
78
62
|
# Commit to SVN, upload and do the release cycle.
|
79
63
|
namespace :svn do
|
80
64
|
task :clean? do |task|
|
81
65
|
status = `svn status`.reject { |line| line =~ /\s(pkg|html)$/ }
|
82
|
-
fail "Cannot release unless all local changes are in SVN:\n#{status}" unless status.empty?
|
66
|
+
#fail "Cannot release unless all local changes are in SVN:\n#{status}" unless status.empty?
|
83
67
|
end
|
84
68
|
|
85
69
|
task :tag do |task|
|
86
70
|
cur_url = `svn info`.scan(/URL: (.*)/)[0][0]
|
87
71
|
new_url = cur_url.sub(/trunk$/, "tags/#{spec.version.to_s}")
|
88
|
-
system
|
89
|
-
system
|
72
|
+
system 'svn', 'remove', new_url, '-m', 'Removing old copy' rescue nil
|
73
|
+
system 'svn', 'copy', cur_url, new_url, '-m', "Release #{spec.version.to_s}"
|
90
74
|
end
|
91
75
|
end
|
92
76
|
|
93
77
|
namespace :upload do
|
94
|
-
task :packages=>[
|
78
|
+
task :packages=>['rake:package'] do |task|
|
95
79
|
# Read the changes for this release.
|
96
|
-
pattern = /(^(\d+\.\d+(?:\.\d+)?)\s+\(\d
|
97
|
-
changelog = File.read(__FILE__.pathmap(
|
80
|
+
pattern = /(^(\d+\.\d+(?:\.\d+)?)\s+\(\d{4}-\d{2}-\d{2}\)\s*((:?^[^\n]+\n)*))/
|
81
|
+
changelog = File.read(__FILE__.pathmap('%d/CHANGELOG'))
|
98
82
|
changes = changelog.scan(pattern).inject({}) { |hash, set| hash[set[1]] = set[2] ; hash }
|
99
83
|
current = changes[spec.version.to_s]
|
100
84
|
if !current && spec.version.to_s =~ /\.0$/
|
101
|
-
current = changes[spec.version.to_s.split(
|
85
|
+
current = changes[spec.version.to_s.split('.')[0..-2].join('.')]
|
102
86
|
end
|
103
87
|
fail "No changeset found for version #{spec.version}" unless current
|
104
88
|
|
105
89
|
puts "Uploading #{spec.name} #{spec.version}"
|
106
|
-
files =
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
90
|
+
files = %w( gem tgz zip ).map { |ext| "pkg/#{spec.name}-#{spec.version}.#{ext}" }
|
91
|
+
rubyforge = RubyForge.new
|
92
|
+
rubyforge.login
|
93
|
+
File.open('.changes', 'w'){|f| f.write(current)}
|
94
|
+
rubyforge.userconfig.merge!('release_changes' => '.changes', 'preformatted' => true)
|
95
|
+
rubyforge.add_release spec.rubyforge_project.downcase, spec.name.downcase, spec.version, *files
|
96
|
+
rm '.changes'
|
112
97
|
puts "Release #{spec.version} uploaded"
|
113
98
|
end
|
114
99
|
end
|
115
100
|
|
116
|
-
desc
|
117
|
-
task :release=>[
|
101
|
+
desc 'Upload release to RubyForge including docs, tag SVN'
|
102
|
+
task :release=>[ 'clobber', 'svn:clean?', 'upload:packages' ]
|
data/lib/docter/collection.rb
CHANGED
@@ -14,18 +14,22 @@ module Docter
|
|
14
14
|
@collection.page(path)
|
15
15
|
end
|
16
16
|
|
17
|
-
def toc
|
17
|
+
def toc
|
18
18
|
load
|
19
19
|
@toc
|
20
20
|
end
|
21
21
|
|
22
|
-
def title
|
22
|
+
def title
|
23
23
|
load
|
24
24
|
@title
|
25
25
|
end
|
26
26
|
|
27
27
|
protected
|
28
28
|
|
29
|
+
def create_from_yaml(yaml, options)
|
30
|
+
@toc = ToC.from_yaml(yaml, @collection)
|
31
|
+
end
|
32
|
+
|
29
33
|
if defined?(::RedCloth)
|
30
34
|
def create_from_textile(text, options)
|
31
35
|
html = RedCloth.new(erb_this(text, binding), [:no_span_caps]).to_html(:textile)
|
@@ -45,27 +49,27 @@ module Docter
|
|
45
49
|
|
46
50
|
def create_from_html(html, options)
|
47
51
|
@toc = ToC.new.tap do |toc|
|
48
|
-
html.scan(regexp_element(
|
49
|
-
list.scan(regexp_element(
|
50
|
-
if item[regexp_element(
|
52
|
+
html.scan(regexp_element('ol')).each do |tag, attrs, list|
|
53
|
+
list.scan(regexp_element('li')).each do |tag, attrs, item|
|
54
|
+
if item[regexp_element('a')]
|
51
55
|
attrs, title = $2, $3
|
52
|
-
url = $3 if attrs =~ regexp_attribute(
|
56
|
+
url = $3 if attrs =~ regexp_attribute('href')
|
53
57
|
if page = page(url)
|
54
58
|
toc.add page.toc_entry
|
55
59
|
else
|
56
|
-
toc.add
|
60
|
+
toc.add inner_text_from(title), url
|
57
61
|
end
|
58
62
|
else
|
59
|
-
url, title =
|
60
|
-
if page = page(item.strip.gsub(/\s+/,
|
63
|
+
url, title = '#', inner_text_from(item)
|
64
|
+
if page = page(item.strip.gsub(/\s+/, ' ').downcase + '.html')
|
61
65
|
toc.add page.toc_entry
|
62
66
|
else
|
63
|
-
toc.add
|
67
|
+
toc.add inner_text_from(title), url
|
64
68
|
end
|
65
69
|
end
|
66
70
|
end
|
67
71
|
end
|
68
|
-
@title = inner_text_from($3) if html =~ regexp_element(
|
72
|
+
@title = inner_text_from($3) if html =~ regexp_element('h1')
|
69
73
|
end
|
70
74
|
end
|
71
75
|
|
@@ -80,18 +84,18 @@ module Docter
|
|
80
84
|
end
|
81
85
|
|
82
86
|
# The collection title (HTML encoded).
|
83
|
-
def title
|
87
|
+
def title
|
84
88
|
@title || if @toc_resource
|
85
89
|
@toc_resource.reload if @toc_resource.modified?
|
86
90
|
@toc_resource.title
|
87
|
-
end ||
|
91
|
+
end || ''
|
88
92
|
end
|
89
93
|
|
90
94
|
# :call-seq:
|
91
|
-
# toc
|
95
|
+
# toc => ToC
|
92
96
|
#
|
93
97
|
# Returns the collection's ToC.
|
94
|
-
def toc
|
98
|
+
def toc
|
95
99
|
if @toc_resource
|
96
100
|
@toc_resource.reload if @toc_resource.modified?
|
97
101
|
@toc_resource.toc
|
@@ -152,19 +156,19 @@ module Docter
|
|
152
156
|
# Returns a page based on its path.
|
153
157
|
#
|
154
158
|
# For example:
|
155
|
-
# collection.include(
|
156
|
-
# collection.page(
|
157
|
-
# Will return a page generated from
|
159
|
+
# collection.include('doc/pages')
|
160
|
+
# collection.page('index.html')
|
161
|
+
# Will return a page generated from 'doc/pages/index.textile'.
|
158
162
|
def page(path)
|
159
163
|
pages.find { |page| page.path == path }
|
160
164
|
end
|
161
165
|
|
162
166
|
# :call-seq:
|
163
|
-
# pages
|
167
|
+
# pages => Pages
|
164
168
|
#
|
165
169
|
# Returns all the pages in this collection/
|
166
|
-
def pages
|
167
|
-
@pages = @sources.map { |path| File.directory?(path) ? FileList[File.join(path,
|
170
|
+
def pages
|
171
|
+
@pages = @sources.map { |path| File.directory?(path) ? FileList[File.join(path, '**/*')] : path }.flatten.
|
168
172
|
inject(@pages || {}) { |pages, file| pages[file] ||= Page.new(file, :collection=>self) ; pages }
|
169
173
|
@pages.values
|
170
174
|
end
|
@@ -172,27 +176,28 @@ module Docter
|
|
172
176
|
def prev(page)
|
173
177
|
pages = toc.map { |entry| page(entry.url) }.compact
|
174
178
|
index = pages.index(page)
|
175
|
-
pages[index - 1] if index > 0
|
179
|
+
pages[index - 1] if index && index > 0
|
176
180
|
end
|
177
181
|
|
178
182
|
def next(page)
|
179
183
|
pages = toc.map { |entry| page(entry.url) }.compact
|
180
|
-
pages
|
184
|
+
index = pages.index(page)
|
185
|
+
pages[index + 1] if index
|
181
186
|
end
|
182
187
|
|
183
188
|
# :call-seq:
|
184
189
|
# file(path) => filename
|
185
190
|
#
|
186
191
|
# Returns the full path based on the relative path. For example:
|
187
|
-
# collection.include(
|
188
|
-
# collection.find(
|
189
|
-
# collection.find(
|
192
|
+
# collection.include('pages', 'LICENSE')
|
193
|
+
# collection.find('index.textile') => 'pages/index.textile'
|
194
|
+
# collection.find('LICENSE') => 'LICENSE'
|
190
195
|
def file(path)
|
191
196
|
@sources.inject(nil) do |found, file|
|
192
197
|
break found if found
|
193
198
|
if File.directory?(file)
|
194
|
-
base = file +
|
195
|
-
FileList[File.join(file,
|
199
|
+
base = file + '/'
|
200
|
+
FileList[File.join(file, '**/*')].find { |file| file.sub(base, '') == path }
|
196
201
|
else
|
197
202
|
file if File.basename(file) == path
|
198
203
|
end
|
@@ -224,8 +229,8 @@ module Docter
|
|
224
229
|
html = template.render(options.merge(:collection=>self, :pages=>pages, :one_page=>true))
|
225
230
|
|
226
231
|
url_map = pages.inject({}) { |map, page| map[page.path] = "##{page.id}" ; map }
|
227
|
-
html.gsub(regexp_element(
|
228
|
-
link.gsub(regexp_attribute(
|
232
|
+
html.gsub(regexp_element('a')) do |link|
|
233
|
+
link.gsub(regexp_attribute('href')) do |href|
|
229
234
|
url = $3
|
230
235
|
url = url_map[url] || url.gsub(/^(.*?)#(.*?)$/) { |path| url_map[$1] ? "##{$2}" : path }
|
231
236
|
%{href="#{url}"}
|
@@ -256,10 +261,10 @@ module Docter
|
|
256
261
|
template.copy_resources to_dir
|
257
262
|
elsif args.first == :one_page
|
258
263
|
mkpath to_dir
|
259
|
-
File.join(to_dir,
|
264
|
+
File.join(to_dir, 'index.html').tap do |filename|
|
260
265
|
puts "Generating #{filename}" if verbose
|
261
266
|
html = render(template, options)
|
262
|
-
File.open(filename,
|
267
|
+
File.open(filename, 'w') { |file| file.write html }
|
263
268
|
end
|
264
269
|
template.copy_resources to_dir
|
265
270
|
else
|
@@ -268,7 +273,7 @@ module Docter
|
|
268
273
|
File.join(to_dir, page.path).tap do |filename|
|
269
274
|
puts "Generating #{filename}" if verbose
|
270
275
|
html = render(template, page, options)
|
271
|
-
File.open(filename,
|
276
|
+
File.open(filename, 'w') { |file| file.write html }
|
272
277
|
end
|
273
278
|
end
|
274
279
|
end
|
@@ -278,8 +283,8 @@ module Docter
|
|
278
283
|
#
|
279
284
|
# Returns a list of dependencies (resource files, the template file, etc). Useful when creating
|
280
285
|
# a Rake task based on this template.
|
281
|
-
def dependencies
|
282
|
-
files = @sources.map { |path| File.directory?(path) ? FileList[path, File.join(path,
|
286
|
+
def dependencies #:nodoc:
|
287
|
+
files = @sources.map { |path| File.directory?(path) ? FileList[path, File.join(path, '**/*')] : path }.flatten +
|
283
288
|
(@toc_resource ? [@toc_resource.filename] : [])
|
284
289
|
end
|
285
290
|
|
data/lib/docter/common.rb
CHANGED
@@ -10,11 +10,11 @@ module Docter
|
|
10
10
|
# Returns the inner text from some HTML text, effectively stripping the tags, normalizing whitespaces
|
11
11
|
# and stripping leading/trailing spaces.
|
12
12
|
def inner_text_from(html)
|
13
|
-
html.gsub(/<(\w*).*?>(.*?)<\/\1\s*>/m, "\\2").strip.gsub(/\s+/m,
|
13
|
+
html.gsub(/<(\w*).*?>(.*?)<\/\1\s*>/m, "\\2").strip.gsub(/\s+/m, ' ')
|
14
14
|
end
|
15
15
|
|
16
16
|
def regexp_element(name)
|
17
|
-
Regexp.new("<(#{name})
|
17
|
+
Regexp.new("<(#{name})(?:>|\s+(.*?)>)(.*?)<\\/\\1\\s*>", Regexp::MULTILINE + Regexp::IGNORECASE)
|
18
18
|
end
|
19
19
|
|
20
20
|
def regexp_attribute(name)
|
@@ -34,8 +34,8 @@ module Docter
|
|
34
34
|
module Resource
|
35
35
|
|
36
36
|
# Maps various filename extensions to the appropriate format. You only need to use this when the filename
|
37
|
-
# extension is not the same as the format, e.g. map
|
38
|
-
EXTENSIONS = {
|
37
|
+
# extension is not the same as the format, e.g. map '.txt' to :plain, but not necessary to map '.textile'.
|
38
|
+
EXTENSIONS = { ''=>:plain, '.txt'=>:plain, '.text'=>:plain, '.thtml'=>:textile, '.mhtml'=>:markdown }
|
39
39
|
|
40
40
|
class << self
|
41
41
|
|
@@ -58,35 +58,35 @@ module Docter
|
|
58
58
|
attr_reader :filename
|
59
59
|
|
60
60
|
# :call-seq:
|
61
|
-
# modified
|
61
|
+
# modified => time
|
62
62
|
#
|
63
63
|
# Returns the date/time this resource was last modified. If the resource comes from a file,
|
64
64
|
# the timestamp of the file, otherwise the when the resource was created.
|
65
|
-
def modified
|
65
|
+
def modified
|
66
66
|
@filename ? File.stat(@filename).mtime : @modified
|
67
67
|
end
|
68
68
|
|
69
69
|
# :call-seq:
|
70
|
-
# modified?
|
70
|
+
# modified? => boolean
|
71
71
|
#
|
72
72
|
# Returns true if the resource was modified since it was lase (re)loaded. Only applies to resources
|
73
73
|
# created from a file, all other resources return false.
|
74
|
-
def modified?
|
74
|
+
def modified?
|
75
75
|
@filename ? File.stat(@filename).mtime > @modified : false
|
76
76
|
end
|
77
77
|
|
78
78
|
# :call-seq:
|
79
|
-
# reload
|
79
|
+
# reload
|
80
80
|
#
|
81
81
|
# Reloads the resource. Only applies to resources created from a file, otherwise acts like load.
|
82
82
|
# You can safely call it for all resources, for example:
|
83
83
|
# page.reload if page.modified?
|
84
|
-
def reload
|
84
|
+
def reload
|
85
85
|
@loaded = false if @filename
|
86
86
|
load
|
87
87
|
end
|
88
88
|
|
89
|
-
def to_s
|
89
|
+
def to_s #:nodoc:
|
90
90
|
@filename || super
|
91
91
|
end
|
92
92
|
|
@@ -98,7 +98,7 @@ module Docter
|
|
98
98
|
case args.first
|
99
99
|
when String
|
100
100
|
@filename = args.shift
|
101
|
-
raise ArgumentError,
|
101
|
+
raise ArgumentError, 'Expecting file name and options, found too may arguments.' unless args.empty?
|
102
102
|
# We'll load the file later, but we need to known the mtime in case someone calls modified?/reload first.
|
103
103
|
@modified = File.stat(@filename).mtime
|
104
104
|
@load_using = lambda do
|
@@ -112,28 +112,28 @@ module Docter
|
|
112
112
|
when Symbol
|
113
113
|
@modified = Time.now # Best guess
|
114
114
|
format, content = args.shift, args.shift
|
115
|
-
raise ArgumentError,
|
115
|
+
raise ArgumentError, 'Expecting format (as symbol) followed by content (string), found too many arguments.' unless args.empty?
|
116
116
|
@load_using = lambda { create format, content, options }
|
117
117
|
else
|
118
118
|
if args.empty? && block
|
119
119
|
@modified = Time.now # Best guess
|
120
120
|
@load_using = lambda { block.call options }
|
121
121
|
else
|
122
|
-
raise ArgumentError,
|
122
|
+
raise ArgumentError, 'Expecting file name, or (format, content), not sure what to do with these arguments.'
|
123
123
|
end
|
124
124
|
end
|
125
125
|
end
|
126
126
|
|
127
127
|
# :call-seq:
|
128
|
-
# load
|
128
|
+
# load
|
129
129
|
#
|
130
130
|
# Loads the resource. Call this method before anything that depends on the content of the resource,
|
131
131
|
# for example:
|
132
|
-
# def title
|
132
|
+
# def title
|
133
133
|
# load
|
134
134
|
# @title # Created by load
|
135
135
|
# end
|
136
|
-
def load
|
136
|
+
def load
|
137
137
|
unless @loaded
|
138
138
|
@load_using.call
|
139
139
|
@loaded = true
|
@@ -184,7 +184,7 @@ module Docter
|
|
184
184
|
#
|
185
185
|
# The second form creates this resource from content in the specified format. This one you cannot reload.
|
186
186
|
# For example:
|
187
|
-
# Page.new(:plain,
|
187
|
+
# Page.new(:plain, 'HAI')
|
188
188
|
#
|
189
189
|
# The third form creates this resource by calling the block with the supplied options.
|
190
190
|
def initialize(*args, &block)
|
@@ -206,23 +206,36 @@ module Docter
|
|
206
206
|
# Use #to_html to transform to an HTML ordered list.
|
207
207
|
class ToC
|
208
208
|
|
209
|
+
class << self
|
210
|
+
|
211
|
+
# Loads the ToC from a YAML file.
|
212
|
+
def from_yaml(yaml, collection = nil)
|
213
|
+
new.send(:initialize_from, YAML.load(yaml), collection)
|
214
|
+
end
|
215
|
+
|
216
|
+
end
|
217
|
+
|
209
218
|
include Enumerable
|
210
219
|
|
220
|
+
# The title of this entry (optional)
|
221
|
+
attr_reader :title
|
222
|
+
|
211
223
|
# Array of entries.
|
212
224
|
attr_reader :entries
|
213
225
|
|
214
226
|
# Create new ToC with no entries.
|
215
|
-
def initialize()
|
227
|
+
def initialize(title = nil)
|
228
|
+
@title = title
|
216
229
|
@entries = []
|
217
230
|
end
|
218
231
|
|
219
|
-
ARRAY_METHODS = [
|
220
|
-
(Enumerable.instance_methods + ARRAY_METHODS - [
|
232
|
+
ARRAY_METHODS = ['each', 'first', 'last', 'size', 'empty?', 'include?', 'index', '[]']
|
233
|
+
(Enumerable.instance_methods + ARRAY_METHODS - ['entries']).each do |method|
|
221
234
|
class_eval "def #{method}(*args, &block) ; entries.send(:#{method}, *args, &block) ; end", __FILE__, __LINE__
|
222
235
|
end
|
223
236
|
|
224
237
|
# :call-seq:
|
225
|
-
# add(
|
238
|
+
# add(title, url?) => entry
|
226
239
|
# add(entry) => entry
|
227
240
|
#
|
228
241
|
# Adds (and returns) a new entry. The first form creates an entry with a link (must be a valid URL,
|
@@ -231,7 +244,7 @@ module Docter
|
|
231
244
|
def add(*args)
|
232
245
|
if ToCEntry === args.first
|
233
246
|
entry = args.shift
|
234
|
-
raise ArgumentError,
|
247
|
+
raise ArgumentError, 'Can only accept a ToCEntry argument.' unless args.empty?
|
235
248
|
else
|
236
249
|
entry = ToCEntry.new(*args)
|
237
250
|
end
|
@@ -251,9 +264,9 @@ module Docter
|
|
251
264
|
#
|
252
265
|
# The +options+ argument can take the form of a Hash, list of symbols or both. Symbols are
|
253
266
|
# treated as +true+ for example:
|
254
|
-
# to_html(:nested, :class=>
|
267
|
+
# to_html(:nested, :class=>'toc')
|
255
268
|
# Is the same as:
|
256
|
-
# to_html(:nested=>true, :class=>
|
269
|
+
# to_html(:nested=>true, :class=>'toc')
|
257
270
|
def to_html(*args)
|
258
271
|
options = Hash === args.last ? args.pop.clone : {}
|
259
272
|
args.each { |arg| options[arg.to_sym] = true }
|
@@ -261,6 +274,32 @@ module Docter
|
|
261
274
|
%{<ol #{cls}>#{map { |entry| entry.to_html(options) }}</ol>}
|
262
275
|
end
|
263
276
|
|
277
|
+
protected
|
278
|
+
|
279
|
+
def initialize_from(entry, collection = nil) #:nodoc:
|
280
|
+
case entry
|
281
|
+
when Array
|
282
|
+
entry.each { |item| initialize_from(item, collection) }
|
283
|
+
when Hash
|
284
|
+
entry.each do |title, entry|
|
285
|
+
if String === entry
|
286
|
+
if collection && page = collection.page(entry)
|
287
|
+
add page.toc_entry
|
288
|
+
else
|
289
|
+
add title, entry
|
290
|
+
end
|
291
|
+
else
|
292
|
+
add ToCEntry.new(title).initialize_from(entry, collection)
|
293
|
+
end
|
294
|
+
end
|
295
|
+
when String
|
296
|
+
add entry
|
297
|
+
else
|
298
|
+
fail "Don't know how to populate ToC from #{entry.inspect}"
|
299
|
+
end
|
300
|
+
self
|
301
|
+
end
|
302
|
+
|
264
303
|
end
|
265
304
|
|
266
305
|
|
@@ -270,17 +309,14 @@ module Docter
|
|
270
309
|
# The URL for this entry.
|
271
310
|
attr_reader :url
|
272
311
|
|
273
|
-
# The title of this entry.
|
274
|
-
attr_reader :title
|
275
|
-
|
276
312
|
# :call-seq:
|
277
|
-
# new(
|
313
|
+
# new(title, url)
|
278
314
|
#
|
279
315
|
# URL links to the ToC entry, and must be a valid URL (use CGI.escape is necessary). The title must
|
280
316
|
# be HTML-encoded (use CGI.escapeHTML if necessary).
|
281
|
-
def initialize(
|
282
|
-
super
|
283
|
-
@url
|
317
|
+
def initialize(title, url = nil)
|
318
|
+
super title
|
319
|
+
@url = url
|
284
320
|
end
|
285
321
|
|
286
322
|
# :call-seq:
|
@@ -292,10 +328,17 @@ module Docter
|
|
292
328
|
options = Hash === args.last ? args.pop.clone : {}
|
293
329
|
args.each { |arg| options[arg.to_sym] = true }
|
294
330
|
if options[:nested] && !empty?
|
295
|
-
|
296
|
-
super(options.merge(:nested=>options[:nested] - 1)) :
|
331
|
+
if Integer === options[:nested]
|
332
|
+
nested = super(options.merge(:nested=>options[:nested] - 1)) if options[:nested] > 0
|
333
|
+
else
|
334
|
+
nested = super(options)
|
335
|
+
end
|
336
|
+
end
|
337
|
+
if url
|
338
|
+
%{<li><a href="#{url}">#{title}</a>#{nested}</li>}
|
339
|
+
else
|
340
|
+
%{<li>#{title}#{nested}</li>}
|
297
341
|
end
|
298
|
-
%{<li><a href="#{url}">#{title}</a>#{nested}</li>}
|
299
342
|
end
|
300
343
|
|
301
344
|
end
|
data/lib/docter/page.rb
CHANGED
@@ -34,7 +34,7 @@ module Docter
|
|
34
34
|
# TITLE and H1 are used, they must match.
|
35
35
|
#
|
36
36
|
# If none of these options are available (e.g. for :plain) the title comes from the filename, treating
|
37
|
-
# underscore as space and capitalizing first letter, e.g. change_log.txt becomes
|
37
|
+
# underscore as space and capitalizing first letter, e.g. change_log.txt becomes 'Change Log'.
|
38
38
|
#
|
39
39
|
# *ToC* The table of contents is constructed from H2 and H3 headers. H2 headers provide the top-level sections,
|
40
40
|
# and H3 headers are nested inside H2 headers.
|
@@ -43,7 +43,7 @@ module Docter
|
|
43
43
|
# one is created using the header title, for example:
|
44
44
|
# h2. Getting Started
|
45
45
|
# becomes:
|
46
|
-
# <h2 id=
|
46
|
+
# <h2 id='getting_started'>Getting Started</h2>
|
47
47
|
# You can rely on these IDs to link inside the page and across pages.
|
48
48
|
#
|
49
49
|
# *Filters* Runs the default chain of filters, or those specified by the :filters option. See Filter
|
@@ -59,25 +59,25 @@ module Docter
|
|
59
59
|
@page = page
|
60
60
|
end
|
61
61
|
|
62
|
-
def title
|
62
|
+
def title
|
63
63
|
@page.title
|
64
64
|
end
|
65
65
|
|
66
|
-
def url
|
66
|
+
def url
|
67
67
|
@page.path
|
68
68
|
end
|
69
69
|
|
70
|
-
def entries
|
70
|
+
def entries
|
71
71
|
@page.toc.entries
|
72
72
|
end
|
73
73
|
|
74
74
|
end
|
75
75
|
|
76
76
|
# :call-seq:
|
77
|
-
# title
|
77
|
+
# title => string
|
78
78
|
#
|
79
79
|
# Returns the page title.
|
80
|
-
def title
|
80
|
+
def title
|
81
81
|
load
|
82
82
|
@title
|
83
83
|
end
|
@@ -87,51 +87,51 @@ module Docter
|
|
87
87
|
end
|
88
88
|
|
89
89
|
# :call-seq:
|
90
|
-
# content
|
90
|
+
# content => string
|
91
91
|
#
|
92
92
|
# Returns the page content (HTML).
|
93
|
-
def content
|
93
|
+
def content
|
94
94
|
load
|
95
95
|
Filter.process(@content)
|
96
96
|
end
|
97
97
|
|
98
98
|
# :call-seq:
|
99
|
-
# toc
|
99
|
+
# toc => ToC
|
100
100
|
#
|
101
101
|
# Returns the table of contents.
|
102
|
-
def toc
|
102
|
+
def toc
|
103
103
|
load
|
104
104
|
@toc
|
105
105
|
end
|
106
106
|
|
107
107
|
# :call-seq:
|
108
|
-
# path
|
108
|
+
# path => filename
|
109
109
|
#
|
110
110
|
# Returns the path for this page. You can use this to link to the page from any other page.
|
111
111
|
#
|
112
|
-
# For example, if the page name is
|
113
|
-
def path
|
114
|
-
@path ||= File.basename(@filename).downcase.ext(
|
112
|
+
# For example, if the page name is 'intro.textile' the path will be 'intro.html'.
|
113
|
+
def path
|
114
|
+
@path ||= File.basename(@filename).downcase.ext('.html')
|
115
115
|
end
|
116
116
|
|
117
117
|
# :call-seq;
|
118
|
-
# id
|
118
|
+
# id => string
|
119
119
|
#
|
120
120
|
# Returns fragment identifier for this page.
|
121
|
-
def id
|
122
|
-
@id ||= title.gsub(/\s+/,
|
121
|
+
def id
|
122
|
+
@id ||= title.gsub(/\s+/, '_').downcase
|
123
123
|
end
|
124
124
|
|
125
|
-
def entries
|
125
|
+
def entries #:nodoc:
|
126
126
|
toc.entries
|
127
127
|
end
|
128
128
|
|
129
129
|
# :call-seq:
|
130
|
-
# toc_entry
|
130
|
+
# toc_entry => ToCEntry
|
131
131
|
#
|
132
132
|
# Returns a ToC entry for this page. Uses the +one_page+ argument to determine whether to return
|
133
133
|
# a link to #path of the fragment #id.
|
134
|
-
def toc_entry
|
134
|
+
def toc_entry
|
135
135
|
@toc_entry ||= ToCEntryForPage.new(self)
|
136
136
|
end
|
137
137
|
|
@@ -142,7 +142,7 @@ module Docter
|
|
142
142
|
end
|
143
143
|
|
144
144
|
def create_from_plain(text, options)
|
145
|
-
parse(%{<pre class=
|
145
|
+
parse(%{<pre class='text'>#{CGI.escapeHTML(text)}</pre>}, options)
|
146
146
|
end
|
147
147
|
|
148
148
|
def create_from_textile(textile, options)
|
@@ -166,7 +166,7 @@ module Docter
|
|
166
166
|
# Process {{{ ... }}} code sections into pre tags.
|
167
167
|
text = text.gsub(/^\{\{\{([^\n]*)\n(.*?)\n\}\}\}/m) do
|
168
168
|
code, spec = $2, $1.scan(/^!(.*?)$/).to_s.strip
|
169
|
-
%{<notextile><pre class=
|
169
|
+
%{<notextile><pre class='#{spec.split(',').join(' ')}'>#{CGI.escapeHTML(code)}</pre></notextile>}
|
170
170
|
end
|
171
171
|
# Create the HTML.
|
172
172
|
RedCloth.new(text, [:no_span_caps]).to_html(format)
|
@@ -185,32 +185,32 @@ module Docter
|
|
185
185
|
def parse(html, options)
|
186
186
|
# Get the body (in most cases it's just the page). Make sure when we wreck havoc on the HTML,
|
187
187
|
# we're not changing any content passed to us.
|
188
|
-
body = html[regexp_element(
|
188
|
+
body = html[regexp_element('body')] ? $2 : html.clone
|
189
189
|
|
190
190
|
# The correct structure is to use H1 for the document title (but TITLE element will also work).
|
191
191
|
# If both are used, they must both match. Two or more H1 is a sign you're using H1 instead of H2.
|
192
|
-
title = html.scan(regexp_element(
|
193
|
-
raise ArgumentError,
|
192
|
+
title = html.scan(regexp_element('title|h1')).map{ |parts| inner_text_from(parts.last) }.uniq
|
193
|
+
raise ArgumentError, 'A page can only have one title, you can use the H1 element (preferred) or TITLE element, or both if they\'re the same. If you want to title sections, please use the H2 element' if title.size > 1
|
194
194
|
# Lacking that, we need to derive the title somehow.
|
195
|
-
title = title.first || options[:title] || (filename && filename.pathmap(
|
195
|
+
title = title.first || options[:title] || (filename && filename.pathmap('%n').gsub('_', ' ').capitalize) || 'Untitled'
|
196
196
|
# Get rid of H1 header.
|
197
|
-
body.gsub!(regexp_element(
|
197
|
+
body.gsub!(regexp_element('h1'), '')
|
198
198
|
|
199
199
|
# Catalog all the major sections, based on the H2/H3 headers.
|
200
200
|
toc = ToC.new
|
201
|
-
body.gsub!(regexp_element(
|
201
|
+
body.gsub!(regexp_element('h[23]')) do |header|
|
202
202
|
tag, attributes, text = $1.downcase, $2.to_s, inner_text_from($3)
|
203
203
|
# Make sure all H2/H3 headers have a usable ID, create once if necessary.
|
204
|
-
id = CGI.unescape($3) if attributes[regexp_attribute(
|
204
|
+
id = CGI.unescape($3) if attributes[regexp_attribute('id')]
|
205
205
|
if id.blank?
|
206
|
-
id = CGI.unescapeHTML(text.downcase.gsub(
|
207
|
-
header = %{<#{tag} #{attributes} id=
|
206
|
+
id = CGI.unescapeHTML(text.downcase.gsub(' ', '_'))
|
207
|
+
header = %{<#{tag} #{attributes} id='#{id}'>#{text}</#{tag}>}
|
208
208
|
end
|
209
|
-
if tag ==
|
210
|
-
toc.add "##{id}"
|
209
|
+
if tag == 'h2'
|
210
|
+
toc.add text, "##{id}"
|
211
211
|
else
|
212
|
-
fail ArgumentError,
|
213
|
-
toc.last.add "##{id}"
|
212
|
+
fail ArgumentError, 'H3 section found without any H2 section.' unless toc.last
|
213
|
+
toc.last.add text, "##{id}"
|
214
214
|
end
|
215
215
|
header
|
216
216
|
end
|
@@ -228,10 +228,10 @@ module Docter
|
|
228
228
|
class << self
|
229
229
|
|
230
230
|
# :call-seq:
|
231
|
-
# list
|
231
|
+
# list => names
|
232
232
|
#
|
233
233
|
# Return the names of all defined filters.
|
234
|
-
def list
|
234
|
+
def list
|
235
235
|
@filters.keys
|
236
236
|
end
|
237
237
|
|
data/lib/docter/server.rb
CHANGED
@@ -8,14 +8,14 @@ module Docter
|
|
8
8
|
@collection, @template, @options = collection, template, options || {}
|
9
9
|
end
|
10
10
|
|
11
|
-
def resources
|
11
|
+
def resources
|
12
12
|
@resources ||= Resources.new
|
13
13
|
end
|
14
14
|
|
15
15
|
def process(request, response)
|
16
16
|
# Absolute path to relative path, default index.
|
17
|
-
path = request.params[Mongrel::Const::PATH_INFO].sub(/^\//,
|
18
|
-
path =
|
17
|
+
path = request.params[Mongrel::Const::PATH_INFO].sub(/^\//, '')
|
18
|
+
path = 'index.html' if path.empty?
|
19
19
|
|
20
20
|
begin
|
21
21
|
if file = template.find(path)
|
@@ -27,32 +27,32 @@ module Docter
|
|
27
27
|
else
|
28
28
|
puts "Serving #{path}" if verbose
|
29
29
|
response.start(200) do |head,out|
|
30
|
-
head[
|
30
|
+
head['Last-Modified'] = CGI.rfc1123_date(File.stat(file).mtime)
|
31
31
|
out.write File.read(file)
|
32
32
|
end
|
33
33
|
end
|
34
|
-
elsif options[:one_page] && path ==
|
34
|
+
elsif options[:one_page] && path == 'index.html'
|
35
35
|
puts "Serving #{path}" if verbose
|
36
36
|
response.start(200) do |head,out|
|
37
|
-
head[
|
37
|
+
head['Content-Type'] = 'text/html'
|
38
38
|
out.write collection.render(template, options)
|
39
39
|
end
|
40
40
|
elsif page = collection.page(path)
|
41
41
|
puts "Serving #{path}" if verbose
|
42
42
|
response.start(200) do |head,out|
|
43
|
-
head[
|
43
|
+
head['Content-Type'] = 'text/html'
|
44
44
|
out.write collection.render(template, page, options)
|
45
45
|
end
|
46
46
|
else
|
47
47
|
response.start 404 do |head, out|
|
48
|
-
head[
|
48
|
+
head['Content-Type'] = 'text/html'
|
49
49
|
out.write "<h1>Did you accidentally rm #{path}, or did you forget to :w it?</h1>"
|
50
50
|
end
|
51
51
|
end
|
52
52
|
rescue Exception=>error
|
53
53
|
response.start(500) do |head, out|
|
54
|
-
head[
|
55
|
-
error = ["#{error.class}: #{error}", error.backtrace.join(
|
54
|
+
head['Content-Type'] = 'text/plain'
|
55
|
+
error = ["#{error.class}: #{error}", error.backtrace.join('\n')]
|
56
56
|
out.puts *error
|
57
57
|
puts *error
|
58
58
|
end
|
@@ -78,7 +78,7 @@ module Docter
|
|
78
78
|
|
79
79
|
def start(wait = true)
|
80
80
|
puts "Starting Mongrel on port #{port}"
|
81
|
-
@mongrel = Mongrel::HttpServer.new(
|
81
|
+
@mongrel = Mongrel::HttpServer.new('0.0.0.0', port, 4)
|
82
82
|
@mongrel.register("/", MongrelHandler.new(collection, template, options))
|
83
83
|
if wait
|
84
84
|
@mongrel.run.join rescue nil
|
@@ -87,8 +87,8 @@ module Docter
|
|
87
87
|
end
|
88
88
|
end
|
89
89
|
|
90
|
-
def stop
|
91
|
-
puts
|
90
|
+
def stop
|
91
|
+
puts 'Stopping Mongrel'
|
92
92
|
@mongrel.stop if @mongrel
|
93
93
|
end
|
94
94
|
|
data/lib/docter/template.rb
CHANGED
@@ -15,8 +15,8 @@ module Docter
|
|
15
15
|
|
16
16
|
def collect_links(content, mark = false)
|
17
17
|
@links ||= []
|
18
|
-
content.gsub(regexp_element(
|
19
|
-
url = $3 if link =~ regexp_attribute(
|
18
|
+
content.gsub(regexp_element('a')) do |link|
|
19
|
+
url = $3 if link =~ regexp_attribute('href')
|
20
20
|
if url =~ /^\w+:/
|
21
21
|
unless index = @links.index(url)
|
22
22
|
index = @links.size
|
@@ -33,20 +33,20 @@ module Docter
|
|
33
33
|
# Remove duplicate links (same URL), sort by text and convert into DT/DD pairs.
|
34
34
|
links = @links.inject({}) { |hash, link| hash[link.first] ||= link.last ; hash }.
|
35
35
|
sort { |a,b| a.last <=> b.last }.
|
36
|
-
map { |url, text| %{<dt>#{text.capitalize}</dt><dd><a href=
|
37
|
-
%{<dl class=
|
36
|
+
map { |url, text| %{<dt>#{text.capitalize}</dt><dd><a href='#{url}'>#{url}</a></dd>} }
|
37
|
+
%{<dl class='#{cls}'>#{links.join}</dl>}
|
38
38
|
end
|
39
39
|
|
40
40
|
def renumber_footnotes(html)
|
41
41
|
@footnote ||= 0
|
42
|
-
html.gsub(/<a href="#fn(\d+)">\1<\/a>/) {
|
42
|
+
html.gsub(/<a href=['"]#fn(\d+)['"]>\1<\/a>/) {
|
43
43
|
# Renumber footnote references starting from the last footnote number.
|
44
44
|
fn = $1.to_i + @footnote
|
45
|
-
%{<a href=
|
46
|
-
}.gsub(/<p id="fn(\d+)"(.*?)><sup>\1<\/sup>(.*?)<\/p>/m) {
|
45
|
+
%{<a href='#fn#{fn}'>#{fn}</a>}
|
46
|
+
}.gsub(/<p id=['"]fn(\d+)['"](.*?)><sup>\1<\/sup>(.*?)<\/p>/m) {
|
47
47
|
# Renumber footnotes the same way, and update the last footnote number.
|
48
48
|
@footnote += 1
|
49
|
-
%{<p id=
|
49
|
+
%{<p id='fn#{@footnote}'#{$2}><sup>#{@footnote}</sup>#{$3}</p>}
|
50
50
|
}
|
51
51
|
end
|
52
52
|
|
@@ -113,16 +113,16 @@ module Docter
|
|
113
113
|
# Returns the location of a file on disk based on the request path.
|
114
114
|
#
|
115
115
|
# For example:
|
116
|
-
# template.include(
|
117
|
-
# map.find(
|
118
|
-
# map.find(
|
119
|
-
# map.find(
|
116
|
+
# template.include('html/index.html', 'images', 'css/*')
|
117
|
+
# map.find('index.html') => 'html/index.html'
|
118
|
+
# map.find('images/logo.png') => 'images/logo.png'
|
119
|
+
# map.find('fancy.css') => 'css/fancy.css'
|
120
120
|
def find(path)
|
121
121
|
@sources.inject(nil) do |found, file|
|
122
122
|
break found if found
|
123
123
|
if File.directory?(file)
|
124
|
-
base = File.dirname(file) +
|
125
|
-
FileList["#{file}/**/*"].find { |file| file.sub(base,
|
124
|
+
base = File.dirname(file) + '/'
|
125
|
+
FileList["#{file}/**/*"].find { |file| file.sub(base, '') == path }
|
126
126
|
else
|
127
127
|
file if File.basename(file) == path
|
128
128
|
end
|
@@ -137,9 +137,9 @@ module Docter
|
|
137
137
|
mkpath to_dir
|
138
138
|
@sources.each do |file|
|
139
139
|
if File.directory?(file)
|
140
|
-
base = File.dirname(file) +
|
141
|
-
FileList[File.join(file,
|
142
|
-
target = File.join(to_dir, file.sub(base,
|
140
|
+
base = File.dirname(file) + '/'
|
141
|
+
FileList[File.join(file, '**/*')].each do |file|
|
142
|
+
target = File.join(to_dir, file.sub(base, ''))
|
143
143
|
mkpath File.dirname(target)
|
144
144
|
cp file, target
|
145
145
|
end
|
@@ -154,8 +154,8 @@ module Docter
|
|
154
154
|
#
|
155
155
|
# Returns a list of dependencies (resource files, the template file, etc). Useful when creating
|
156
156
|
# a Rake task based on this template.
|
157
|
-
def dependencies
|
158
|
-
@sources.map { |path| File.directory?(path) ? FileList[path, File.join(path,
|
157
|
+
def dependencies
|
158
|
+
@sources.map { |path| File.directory?(path) ? FileList[path, File.join(path, '**/*')] : path }.flatten +
|
159
159
|
[@filename].compact
|
160
160
|
end
|
161
161
|
|
data/lib/docter/ultraviolet.rb
CHANGED
@@ -1,26 +1,26 @@
|
|
1
|
-
require
|
1
|
+
require 'uv' # gem install ultraviolet
|
2
2
|
|
3
3
|
module Docter
|
4
4
|
|
5
|
-
SYNTAX_THEME =
|
5
|
+
SYNTAX_THEME = 'eiffel'
|
6
6
|
SYNTAX_STYLESHEET = "css/#{SYNTAX_THEME}.css"
|
7
|
-
SYNTAX_MAP = {
|
7
|
+
SYNTAX_MAP = { 'sh'=>'shell-unix-generic' }
|
8
8
|
|
9
9
|
filter_for :syntax do |html|
|
10
|
-
html.gsub(HTML.regexp_element(
|
10
|
+
html.gsub(HTML.regexp_element('pre')) do |pre|
|
11
11
|
attributes, code = $2, $3
|
12
|
-
if attributes[HTML.regexp_attribute(
|
12
|
+
if attributes[HTML.regexp_attribute('class')]
|
13
13
|
classes = $3.split(/\s+/)
|
14
14
|
lang = classes.first
|
15
15
|
end
|
16
|
-
if lang ==
|
17
|
-
Uv.parse(CGI.unescapeHTML(code),
|
18
|
-
gsub(URI.regexp) { |uri| uri =~ /^http(s?):\/\// ? %{<a href=
|
16
|
+
if lang == 'text'
|
17
|
+
Uv.parse(CGI.unescapeHTML(code), 'xhtml', 'plain_text', false, SYNTAX_THEME).
|
18
|
+
gsub(URI.regexp) { |uri| uri =~ /^http(s?):\/\// ? %{<a href='#{uri}'>#{uri}</a>} : uri }
|
19
19
|
elsif lang
|
20
|
-
syntax = SYNTAX_MAP[lang] || (Uv.syntaxes.include?(lang) ? lang :
|
21
|
-
Uv.parse(CGI.unescapeHTML(code),
|
20
|
+
syntax = SYNTAX_MAP[lang] || (Uv.syntaxes.include?(lang) ? lang : 'plain_text')
|
21
|
+
Uv.parse(CGI.unescapeHTML(code), 'xhtml', syntax || 'plain_text', classes.include?('lines'), SYNTAX_THEME)
|
22
22
|
else
|
23
|
-
Uv.parse(CGI.unescapeHTML(code),
|
23
|
+
Uv.parse(CGI.unescapeHTML(code), 'xhtml', 'plain_text', false, SYNTAX_THEME)
|
24
24
|
end
|
25
25
|
end
|
26
26
|
end
|
data/lib/docter.rb
CHANGED
@@ -1,32 +1,32 @@
|
|
1
1
|
# &:symbol goodness.
|
2
|
-
require
|
2
|
+
require 'facet/symbol/to_proc'
|
3
3
|
# blank? on string and nil
|
4
|
-
require
|
5
|
-
require
|
4
|
+
require 'facet/string/blank'
|
5
|
+
require 'facet/nilclass/blank'
|
6
6
|
# x.in?(y) is better than y.include?(x)
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
7
|
+
require 'facet/string/starts_with'
|
8
|
+
require 'facets/core/kernel/tap'
|
9
|
+
require 'facet/kernel/__DIR__'
|
10
10
|
|
11
11
|
module Docter
|
12
|
-
VERSION =
|
12
|
+
VERSION = '1.1.0'.freeze
|
13
13
|
end
|
14
14
|
|
15
15
|
$LOAD_PATH.unshift __DIR__
|
16
16
|
|
17
|
-
require
|
18
|
-
require
|
17
|
+
require 'cgi'
|
18
|
+
require 'erb'
|
19
19
|
# All these Gems are optional.
|
20
|
-
[
|
20
|
+
['redcloth', 'haml', 'mongrel', 'uv'].each do |gem|
|
21
21
|
begin
|
22
22
|
require gem
|
23
23
|
rescue LoadError
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
-
require
|
28
|
-
require
|
29
|
-
require
|
30
|
-
require
|
31
|
-
require
|
32
|
-
require
|
27
|
+
require 'docter/common.rb'
|
28
|
+
require 'docter/page.rb'
|
29
|
+
require 'docter/template.rb'
|
30
|
+
require 'docter/collection.rb'
|
31
|
+
require 'docter/server.rb' if defined?(Mongrel)
|
32
|
+
require 'docter/rake.rb' if defined?(Rake)
|
metadata
CHANGED
@@ -1,80 +1,30 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
|
-
rubygems_version: 0.9.2
|
3
|
-
specification_version: 1
|
4
2
|
name: docter
|
5
3
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 1.0
|
7
|
-
date: 2007-07-06 00:00:00 -07:00
|
8
|
-
summary: We has docs
|
9
|
-
require_paths:
|
10
|
-
- lib
|
11
|
-
email: arkin@intalio.com
|
12
|
-
homepage: http://docter.rubyforge.org
|
13
|
-
rubyforge_project: buildr
|
14
|
-
description:
|
15
|
-
autorequire: docter.rb
|
16
|
-
default_executable:
|
17
|
-
bindir: bin
|
18
|
-
has_rdoc: true
|
19
|
-
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
-
requirements:
|
21
|
-
- - ">"
|
22
|
-
- !ruby/object:Gem::Version
|
23
|
-
version: 0.0.0
|
24
|
-
version:
|
4
|
+
version: 1.1.0
|
25
5
|
platform: ruby
|
26
|
-
signing_key:
|
27
|
-
cert_chain:
|
28
|
-
post_install_message:
|
29
6
|
authors:
|
30
|
-
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
- lib/docter/template.rb
|
35
|
-
- lib/docter/collection.rb
|
36
|
-
- lib/docter/rake.rb
|
37
|
-
- lib/docter/ultraviolet.rb
|
38
|
-
- lib/docter/page.rb
|
39
|
-
- lib/docter/common.rb
|
40
|
-
- lib/docter.rb
|
41
|
-
- CHANGELOG
|
42
|
-
- README
|
43
|
-
- LICENSE
|
44
|
-
- Rakefile
|
45
|
-
test_files: []
|
46
|
-
|
47
|
-
rdoc_options:
|
48
|
-
- --title
|
49
|
-
- Docter -- We has docs
|
50
|
-
- --main
|
51
|
-
- README
|
52
|
-
- --line-numbers
|
53
|
-
- -inline-source
|
54
|
-
extra_rdoc_files:
|
55
|
-
- README
|
56
|
-
- CHANGELOG
|
57
|
-
- LICENSE
|
58
|
-
executables: []
|
59
|
-
|
60
|
-
extensions: []
|
61
|
-
|
62
|
-
requirements: []
|
7
|
+
- Apache Buildr
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
63
11
|
|
12
|
+
date: 2008-01-03 00:00:00 -08:00
|
13
|
+
default_executable:
|
64
14
|
dependencies:
|
65
15
|
- !ruby/object:Gem::Dependency
|
66
16
|
name: facets
|
67
17
|
version_requirement:
|
68
|
-
version_requirements: !ruby/object:Gem::
|
18
|
+
version_requirements: !ruby/object:Gem::Requirement
|
69
19
|
requirements:
|
70
20
|
- - ~>
|
71
21
|
- !ruby/object:Gem::Version
|
72
|
-
version: "
|
22
|
+
version: "2.2"
|
73
23
|
version:
|
74
24
|
- !ruby/object:Gem::Dependency
|
75
25
|
name: RedCloth
|
76
26
|
version_requirement:
|
77
|
-
version_requirements: !ruby/object:Gem::
|
27
|
+
version_requirements: !ruby/object:Gem::Requirement
|
78
28
|
requirements:
|
79
29
|
- - ~>
|
80
30
|
- !ruby/object:Gem::Version
|
@@ -83,18 +33,78 @@ dependencies:
|
|
83
33
|
- !ruby/object:Gem::Dependency
|
84
34
|
name: haml
|
85
35
|
version_requirement:
|
86
|
-
version_requirements: !ruby/object:Gem::
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
87
37
|
requirements:
|
88
38
|
- - ~>
|
89
39
|
- !ruby/object:Gem::Version
|
90
|
-
version: "1.
|
40
|
+
version: "1.7"
|
91
41
|
version:
|
92
42
|
- !ruby/object:Gem::Dependency
|
93
43
|
name: mongrel
|
94
44
|
version_requirement:
|
95
|
-
version_requirements: !ruby/object:Gem::
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
96
46
|
requirements:
|
97
47
|
- - ~>
|
98
48
|
- !ruby/object:Gem::Version
|
99
|
-
version: "1.
|
49
|
+
version: "1.1"
|
100
50
|
version:
|
51
|
+
description:
|
52
|
+
email: buildr-user@incubator.apache.org
|
53
|
+
executables: []
|
54
|
+
|
55
|
+
extensions: []
|
56
|
+
|
57
|
+
extra_rdoc_files:
|
58
|
+
- README
|
59
|
+
- CHANGELOG
|
60
|
+
- LICENSE
|
61
|
+
files:
|
62
|
+
- lib/docter
|
63
|
+
- lib/docter/server.rb
|
64
|
+
- lib/docter/template.rb
|
65
|
+
- lib/docter/collection.rb
|
66
|
+
- lib/docter/rake.rb
|
67
|
+
- lib/docter/ultraviolet.rb
|
68
|
+
- lib/docter/page.rb
|
69
|
+
- lib/docter/common.rb
|
70
|
+
- lib/docter.rb
|
71
|
+
- CHANGELOG
|
72
|
+
- README
|
73
|
+
- LICENSE
|
74
|
+
- Rakefile
|
75
|
+
has_rdoc: true
|
76
|
+
homepage: http://incubator.apache.org/buildr/
|
77
|
+
post_install_message:
|
78
|
+
rdoc_options:
|
79
|
+
- --title
|
80
|
+
- Docter -- We has docs
|
81
|
+
- --main
|
82
|
+
- README
|
83
|
+
- --line-numbers
|
84
|
+
- --inline-source
|
85
|
+
- -p
|
86
|
+
- --webcvs
|
87
|
+
- http://svn.apache.org/repos/asf/incubator/buildr/docter/trunk/
|
88
|
+
require_paths:
|
89
|
+
- lib
|
90
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: "0"
|
95
|
+
version:
|
96
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
97
|
+
requirements:
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: "0"
|
101
|
+
version:
|
102
|
+
requirements: []
|
103
|
+
|
104
|
+
rubyforge_project: buildr
|
105
|
+
rubygems_version: 1.0.1
|
106
|
+
signing_key:
|
107
|
+
specification_version: 2
|
108
|
+
summary: We has docs
|
109
|
+
test_files: []
|
110
|
+
|