docter 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: