docter 1.0.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.
@@ -0,0 +1,109 @@
1
+ module Docter
2
+
3
+ class MongrelHandler < Mongrel::HttpHandler
4
+
5
+ attr_reader :collection, :template, :options
6
+
7
+ def initialize(collection, template, options = {})
8
+ @collection, @template, @options = collection, template, options || {}
9
+ end
10
+
11
+ def resources()
12
+ @resources ||= Resources.new
13
+ end
14
+
15
+ def process(request, response)
16
+ # Absolute path to relative path, default index.
17
+ path = request.params[Mongrel::Const::PATH_INFO].sub(/^\//, "")
18
+ path = "index.html" if path.empty?
19
+
20
+ begin
21
+ if file = template.find(path)
22
+ # Files served directly from disk (CSS, images, RDoc, etc).
23
+ since = request.params[Mongrel::Const::HTTP_IF_MODIFIED_SINCE]
24
+ if since && Time.parse(since) >= File.stat(file).mtime
25
+ response.status = 304
26
+ response.finished
27
+ else
28
+ puts "Serving #{path}" if verbose
29
+ response.start(200) do |head,out|
30
+ head[Mongrel::Const::LAST_MODIFIED] = CGI.rfc1123_date(File.stat(file).mtime)
31
+ out.write File.read(file)
32
+ end
33
+ end
34
+ elsif options[:one_page] && path == "index.html"
35
+ puts "Serving #{path}" if verbose
36
+ response.start(200) do |head,out|
37
+ head[Mongrel::Const::CONTENT_TYPE] = "text/html"
38
+ out.write collection.render(template, options)
39
+ end
40
+ elsif page = collection.page(path)
41
+ puts "Serving #{path}" if verbose
42
+ response.start(200) do |head,out|
43
+ head[Mongrel::Const::CONTENT_TYPE] = "text/html"
44
+ out.write collection.render(template, page, options)
45
+ end
46
+ else
47
+ response.start 404 do |head, out|
48
+ head[Mongrel::Const::CONTENT_TYPE] = "text/html"
49
+ out.write "<h1>Did you accidentally rm #{path}, or did you forget to :w it?</h1>"
50
+ end
51
+ end
52
+ rescue Exception=>error
53
+ response.start(500) do |head, out|
54
+ head["Content-Type"] = "text/plain"
55
+ error = ["#{error.class}: #{error}", error.backtrace.join("\n")]
56
+ out.puts *error
57
+ puts *error
58
+ end
59
+ end
60
+ end
61
+
62
+ end
63
+
64
+
65
+ class Server
66
+
67
+ PORT = 3000
68
+
69
+ attr_reader :collection, :template
70
+ attr_accessor :port, :options
71
+
72
+ def initialize(collection, template, *args)
73
+ @collection, @template = collection, template
74
+ @options = Hash === args.last ? args.pop.clone : {}
75
+ args.each { |arg| @options[arg.to_sym] = true }
76
+ @port = @options[:port] || PORT
77
+ end
78
+
79
+ def start(wait = true)
80
+ puts "Starting Mongrel on port #{port}"
81
+ @mongrel = Mongrel::HttpServer.new("0.0.0.0", port, 4)
82
+ @mongrel.register("/", MongrelHandler.new(collection, template, options))
83
+ if wait
84
+ @mongrel.run.join rescue nil
85
+ else
86
+ @mongrel.run
87
+ end
88
+ end
89
+
90
+ def stop()
91
+ puts "Stopping Mongrel"
92
+ @mongrel.stop if @mongrel
93
+ end
94
+
95
+ end
96
+
97
+
98
+ class Collection
99
+
100
+ def serve(template, *args)
101
+ options = Hash === args.last ? args.pop.clone : {}
102
+ options[:port] = args.shift if Integer === args.first
103
+ args.each { |arg| options[arg.to_sym] = true }
104
+ Server.new(self, template, options).start
105
+ end
106
+
107
+ end
108
+
109
+ end
@@ -0,0 +1,183 @@
1
+ module Docter
2
+
3
+ # A template for formatting pages. The template is parsed once and processed using each Page to
4
+ # produce an HTML document. Processing can rely on various attributes of the Scope.
5
+ #
6
+ # A template will require additional files like CSS stylesheets, images, JavaScript, etc.
7
+ # You can associate additional resources with the template using #include and #exclude.
8
+ # These resources are copied to the destination directory when generating output, and
9
+ # served from the integrated Web server.
10
+ class Template < Resource::Base
11
+
12
+ module ContextMethods
13
+
14
+ include HTML
15
+
16
+ def collect_links(content, mark = false)
17
+ @links ||= []
18
+ content.gsub(regexp_element("a")) do |link|
19
+ url = $3 if link =~ regexp_attribute("href")
20
+ if url =~ /^\w+:/
21
+ unless index = @links.index(url)
22
+ index = @links.size
23
+ @links << [inner_text_from(link), url]
24
+ end
25
+ mark ? "#{link}<sup>[#{index + 1}]</sup>" : link
26
+ else
27
+ link
28
+ end
29
+ end
30
+ end
31
+
32
+ def footnote_links(cls = nil)
33
+ list = @links.map { |link| %{<dt>#{link.first}</dt><dd><a href="#{link.last}">#{link.last}</a></dd>} }
34
+ %{<dl class="#{cls}">#{list.join}</dl>}
35
+ end
36
+
37
+ end
38
+
39
+ # Options passed when creating the template.
40
+ attr_reader :options
41
+
42
+ def initialize(*args)
43
+ super
44
+ @sources = FileList[]
45
+ end
46
+
47
+ # :call-seq:
48
+ # render(context) => html
49
+ #
50
+ # Renders this template. The template is processed using a context that provides the template
51
+ # with access to various methods and variables, e.g. the page being rendered, or the ToC.
52
+ #
53
+ # There are two ways to supply a context:
54
+ # * Hash -- Each key becomes a method you can call on the hash to obtain it's value.
55
+ # The hash will include a method called template that returns the template itself.
56
+ # * Object -- Creates a context object that delegates all method calls to this object,
57
+ # and adds the method template that returns the template itself.
58
+ def render(context)
59
+ load
60
+ if Hash === context
61
+ hash = context.merge(:template=>self)
62
+ struct = Struct.new(*hash.keys).new(*hash.values)
63
+ struct.class.send :include, ContextMethods
64
+ @process.call struct
65
+ else
66
+ delegate = Class.new
67
+ context.class.instance_methods.each { |method| delegate.send :define_method, method, &context.method(method) }
68
+ context.class.send :include, ContextMethods
69
+ delegate.send(:define_method, :template) { self }
70
+ @process.call delegate.new
71
+ end
72
+ end
73
+
74
+ # :call-seq:
75
+ # include(*paths) => self
76
+ #
77
+ # Adds files and directories included in the generated output.
78
+ def include(*paths)
79
+ @sources.include *paths.flatten
80
+ self
81
+ end
82
+
83
+ alias :add :include
84
+
85
+ # :call-seq:
86
+ # exclude(*paths) => self
87
+ #
88
+ # Excludes files or directories from the generated output.
89
+ def exclude(*paths)
90
+ @sources.exclude *paths.flatten
91
+ self
92
+ end
93
+
94
+ # :call-seq:
95
+ # find(path) => file
96
+ #
97
+ # Returns the location of a file on disk based on the request path.
98
+ #
99
+ # For example:
100
+ # template.include("html/index.html", "images", "css/*")
101
+ # map.find("index.html") => "html/index.html"
102
+ # map.find("images/logo.png") => "images/logo.png"
103
+ # map.find("fancy.css") => "css/fancy.css"
104
+ def find(path)
105
+ @sources.inject(nil) do |found, file|
106
+ break found if found
107
+ if File.directory?(file)
108
+ base = File.dirname(file) + "/"
109
+ FileList["#{file}/**/*"].find { |file| file.sub(base, "") == path }
110
+ else
111
+ file if File.basename(file) == path
112
+ end
113
+ end
114
+ end
115
+
116
+ # :call-seq:
117
+ # copy_resources(to_dir)
118
+ #
119
+ # Copy resource files to the destination directory.
120
+ def copy_resources(to_dir)
121
+ mkpath to_dir
122
+ @sources.each do |file|
123
+ if File.directory?(file)
124
+ base = File.dirname(file) + "/"
125
+ FileList[File.join(file, "**/*")].each do |file|
126
+ target = File.join(to_dir, file.sub(base, ""))
127
+ mkpath File.dirname(target)
128
+ cp file, target
129
+ end
130
+ else
131
+ cp file, to_dir
132
+ end
133
+ end
134
+ touch to_dir # For Rake dependency management.
135
+ end
136
+
137
+ # :call-seq:
138
+ #
139
+ # Returns a list of dependencies (resource files, the template file, etc). Useful when creating
140
+ # a Rake task based on this template.
141
+ def dependencies()
142
+ @sources.map { |path| File.directory?(path) ? FileList[path, File.join(path, "**/*")] : path }.flatten +
143
+ [@filename].compact
144
+ end
145
+
146
+ protected
147
+
148
+ if defined?(::Haml)
149
+ def create_from_haml(content, options)
150
+ @options = options
151
+ template = Haml::Engine.new(content, :filename=>@filename)
152
+ @process = lambda { |context| template.render(context) }
153
+ end
154
+ else
155
+ def create_from_haml(content, options)
156
+ fail "You need to install HAML first:\n gem install haml"
157
+ end
158
+ end
159
+
160
+ def create_from_erb(content, options)
161
+ @options = options
162
+ template = ERB.new(content)
163
+ @process = lambda { |context| template.result(context.instance_eval { binding }) }
164
+ end
165
+
166
+ end
167
+
168
+
169
+ class << self
170
+
171
+ # :call-seq:
172
+ # template(filename, options?)
173
+ # template(format, content, options?)
174
+ #
175
+ # The first form loads the template from the specified filename. The second creates the template from
176
+ # the content string based on the specified format.
177
+ def template(*args)
178
+ Template.new(*args)
179
+ end
180
+
181
+ end
182
+
183
+ end
@@ -0,0 +1,26 @@
1
+ module Docter
2
+
3
+ SYNTAX_THEME = "eiffel"
4
+ SYNTAX_STYLESHEET = "css/#{SYNTAX_THEME}.css"
5
+ SYNTAX_MAP = { "sh"=>"shell-unix-generic" }
6
+
7
+ filter_for :syntax do |html|
8
+ html.gsub(HTML.regexp_element("pre")) do |pre|
9
+ attributes, code = $2, $3
10
+ if attributes[HTML.regexp_attribute("class")]
11
+ classes = $3.split(/\s+/)
12
+ lang = classes.first
13
+ end
14
+ if lang == "text"
15
+ Uv.parse(CGI.unescapeHTML(code), "xhtml", "plain_text", false, SYNTAX_THEME).
16
+ gsub(URI.regexp) { |uri| uri =~ /^http(s?):\/\// ? %{<a href="#{uri}">#{uri}</a>} : uri }
17
+ elsif lang
18
+ syntax = SYNTAX_MAP[lang] || (Uv.syntaxes.include?(lang) ? lang : "plain_text")
19
+ Uv.parse(CGI.unescapeHTML(code), "xhtml", syntax || "plain_text", classes.include?("lines"), SYNTAX_THEME)
20
+ else
21
+ Uv.parse(CGI.unescapeHTML(code), "xhtml", "plain_text", false, SYNTAX_THEME)
22
+ end
23
+ end
24
+ end
25
+
26
+ end
data/lib/docter.rb ADDED
@@ -0,0 +1,33 @@
1
+ # &:symbol goodness.
2
+ require "facet/symbol/to_proc"
3
+ # blank? on string and nil
4
+ require "facet/string/blank"
5
+ require "facet/nilclass/blank"
6
+ # x.in?(y) is better than y.include?(x)
7
+ require "facet/string/starts_with"
8
+ require "facets/core/kernel/tap"
9
+ require "facet/kernel/__DIR__"
10
+
11
+ module Docter
12
+ VERSION = "1.0.0".freeze
13
+ end
14
+
15
+ $LOAD_PATH.unshift __DIR__
16
+
17
+ require "cgi"
18
+ require "erb"
19
+ # All these Gems are optional.
20
+ ["redcloth", "haml", "mongrel", "uv"].each do |gem|
21
+ begin
22
+ require gem
23
+ rescue LoadError
24
+ end
25
+ end
26
+
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)
33
+ require "docter/ultraviolet.rb" if defined?(Uv)
metadata ADDED
@@ -0,0 +1,99 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.2
3
+ specification_version: 1
4
+ name: docter
5
+ version: !ruby/object:Gem::Version
6
+ version: 1.0.0
7
+ date: 2007-07-03 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:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message:
29
+ authors:
30
+ - Assaf Arkin
31
+ files:
32
+ - lib/docter
33
+ - lib/docter/server.rb
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
+ test_files: []
45
+
46
+ rdoc_options:
47
+ - --title
48
+ - Docter -- We has docs
49
+ - --main
50
+ - README
51
+ - --line-numbers
52
+ - -inline-source
53
+ extra_rdoc_files:
54
+ - README
55
+ - CHANGELOG
56
+ - LICENSE
57
+ executables: []
58
+
59
+ extensions: []
60
+
61
+ requirements: []
62
+
63
+ dependencies:
64
+ - !ruby/object:Gem::Dependency
65
+ name: facets
66
+ version_requirement:
67
+ version_requirements: !ruby/object:Gem::Version::Requirement
68
+ requirements:
69
+ - - ~>
70
+ - !ruby/object:Gem::Version
71
+ version: "1.8"
72
+ version:
73
+ - !ruby/object:Gem::Dependency
74
+ name: RedCloth
75
+ version_requirement:
76
+ version_requirements: !ruby/object:Gem::Version::Requirement
77
+ requirements:
78
+ - - ~>
79
+ - !ruby/object:Gem::Version
80
+ version: "3.0"
81
+ version:
82
+ - !ruby/object:Gem::Dependency
83
+ name: haml
84
+ version_requirement:
85
+ version_requirements: !ruby/object:Gem::Version::Requirement
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ version: "1.5"
90
+ version:
91
+ - !ruby/object:Gem::Dependency
92
+ name: mongrel
93
+ version_requirement:
94
+ version_requirements: !ruby/object:Gem::Version::Requirement
95
+ requirements:
96
+ - - ~>
97
+ - !ruby/object:Gem::Version
98
+ version: "1.0"
99
+ version: