jamesgolick-webby 0.9.5
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +219 -0
- data/README.rdoc +92 -0
- data/Rakefile +62 -0
- data/bin/webby +41 -0
- data/bin/webby-gen +41 -0
- data/examples/blog/Sitefile +7 -0
- data/examples/blog/tasks/blog.rake +72 -0
- data/examples/blog/templates/atom_feed.erb +40 -0
- data/examples/blog/templates/blog/month.erb +22 -0
- data/examples/blog/templates/blog/post.erb +16 -0
- data/examples/blog/templates/blog/year.erb +22 -0
- data/examples/presentation/Sitefile +10 -0
- data/examples/presentation/content/css/uv/twilight.css +137 -0
- data/examples/presentation/content/presentation/_sample_code.txt +10 -0
- data/examples/presentation/content/presentation/index.txt +63 -0
- data/examples/presentation/content/presentation/s5/blank.gif +0 -0
- data/examples/presentation/content/presentation/s5/bodybg.gif +0 -0
- data/examples/presentation/content/presentation/s5/framing.css +23 -0
- data/examples/presentation/content/presentation/s5/iepngfix.htc +42 -0
- data/examples/presentation/content/presentation/s5/opera.css +7 -0
- data/examples/presentation/content/presentation/s5/outline.css +15 -0
- data/examples/presentation/content/presentation/s5/pretty.css +86 -0
- data/examples/presentation/content/presentation/s5/print.css +25 -0
- data/examples/presentation/content/presentation/s5/s5-core.css +9 -0
- data/examples/presentation/content/presentation/s5/slides.css +3 -0
- data/examples/presentation/content/presentation/s5/slides.js +553 -0
- data/examples/presentation/layouts/presentation.txt +43 -0
- data/examples/presentation/templates/_code_partial.erb +13 -0
- data/examples/presentation/templates/presentation.erb +40 -0
- data/examples/tumblog/Sitefile +9 -0
- data/examples/tumblog/content/css/tumblog.css +308 -0
- data/examples/tumblog/content/images/tumblog/permalink.gif +0 -0
- data/examples/tumblog/content/images/tumblog/rss.gif +0 -0
- data/examples/tumblog/content/tumblog/200806/the-noble-chicken/index.txt +12 -0
- data/examples/tumblog/content/tumblog/200807/historical-perspectives-on-the-classic-chicken-joke/index.txt +12 -0
- data/examples/tumblog/content/tumblog/200807/mad-city-chickens/index.txt +10 -0
- data/examples/tumblog/content/tumblog/200807/the-wisdom-of-the-dutch/index.txt +11 -0
- data/examples/tumblog/content/tumblog/200807/up-a-tree/index.txt +13 -0
- data/examples/tumblog/content/tumblog/index.txt +37 -0
- data/examples/tumblog/content/tumblog/rss.txt +37 -0
- data/examples/tumblog/layouts/tumblog/default.txt +44 -0
- data/examples/tumblog/layouts/tumblog/post.txt +15 -0
- data/examples/tumblog/lib/tumblog_helper.rb +32 -0
- data/examples/tumblog/tasks/tumblog.rake +30 -0
- data/examples/tumblog/templates/atom_feed.erb +40 -0
- data/examples/tumblog/templates/tumblog/conversation.erb +12 -0
- data/examples/tumblog/templates/tumblog/link.erb +10 -0
- data/examples/tumblog/templates/tumblog/photo.erb +13 -0
- data/examples/tumblog/templates/tumblog/post.erb +12 -0
- data/examples/tumblog/templates/tumblog/quote.erb +11 -0
- data/examples/webby/Sitefile +19 -0
- data/examples/webby/content/communicate/index.txt +28 -0
- data/examples/webby/content/css/background.gif +0 -0
- data/examples/webby/content/css/blueprint/print.css +76 -0
- data/examples/webby/content/css/blueprint/screen.css +696 -0
- data/examples/webby/content/css/coderay.css +96 -0
- data/examples/webby/content/css/site.css +196 -0
- data/examples/webby/content/css/uv/twilight.css +137 -0
- data/examples/webby/content/index.txt +37 -0
- data/examples/webby/content/learn/index.txt +28 -0
- data/examples/webby/content/reference/index.txt +204 -0
- data/examples/webby/content/release-notes/index.txt +21 -0
- data/examples/webby/content/release-notes/rel-0-9-0/index.txt +74 -0
- data/examples/webby/content/release-notes/rel-0-9-1/index.txt +93 -0
- data/examples/webby/content/release-notes/rel-0-9-2/index.txt +14 -0
- data/examples/webby/content/release-notes/rel-0-9-3/index.txt +47 -0
- data/examples/webby/content/robots.txt +6 -0
- data/examples/webby/content/script/jquery.corner.js +152 -0
- data/examples/webby/content/script/jquery.js +31 -0
- data/examples/webby/content/sitemap.txt +31 -0
- data/examples/webby/content/tips_and_tricks/index.txt +97 -0
- data/examples/webby/content/tutorial/index.txt +135 -0
- data/examples/webby/content/user-manual/index.txt +419 -0
- data/examples/webby/layouts/default.txt +49 -0
- data/examples/webby/templates/page.erb +10 -0
- data/examples/website/Sitefile +7 -0
- data/examples/website/content/css/blueprint/ie.css +26 -0
- data/examples/website/content/css/blueprint/plugins/buttons/icons/cross.png +0 -0
- data/examples/website/content/css/blueprint/plugins/buttons/icons/key.png +0 -0
- data/examples/website/content/css/blueprint/plugins/buttons/icons/tick.png +0 -0
- data/examples/website/content/css/blueprint/plugins/buttons/readme.txt +32 -0
- data/examples/website/content/css/blueprint/plugins/buttons/screen.css +97 -0
- data/examples/website/content/css/blueprint/plugins/fancy-type/readme.txt +14 -0
- data/examples/website/content/css/blueprint/plugins/fancy-type/screen.css +71 -0
- data/examples/website/content/css/blueprint/plugins/link-icons/icons/doc.png +0 -0
- data/examples/website/content/css/blueprint/plugins/link-icons/icons/email.png +0 -0
- data/examples/website/content/css/blueprint/plugins/link-icons/icons/external.png +0 -0
- data/examples/website/content/css/blueprint/plugins/link-icons/icons/feed.png +0 -0
- data/examples/website/content/css/blueprint/plugins/link-icons/icons/im.png +0 -0
- data/examples/website/content/css/blueprint/plugins/link-icons/icons/pdf.png +0 -0
- data/examples/website/content/css/blueprint/plugins/link-icons/icons/visited.png +0 -0
- data/examples/website/content/css/blueprint/plugins/link-icons/icons/xls.png +0 -0
- data/examples/website/content/css/blueprint/plugins/link-icons/readme.txt +18 -0
- data/examples/website/content/css/blueprint/plugins/link-icons/screen.css +40 -0
- data/examples/website/content/css/blueprint/plugins/rtl/readme.txt +10 -0
- data/examples/website/content/css/blueprint/plugins/rtl/screen.css +109 -0
- data/examples/website/content/css/blueprint/print.css +30 -0
- data/examples/website/content/css/blueprint/screen.css +251 -0
- data/examples/website/content/css/blueprint/src/forms.css +49 -0
- data/examples/website/content/css/blueprint/src/grid.css +212 -0
- data/examples/website/content/css/blueprint/src/grid.png +0 -0
- data/examples/website/content/css/blueprint/src/ie.css +59 -0
- data/examples/website/content/css/blueprint/src/print.css +85 -0
- data/examples/website/content/css/blueprint/src/reset.css +38 -0
- data/examples/website/content/css/blueprint/src/typography.css +105 -0
- data/examples/website/content/css/coderay.css +111 -0
- data/examples/website/content/css/site.css +67 -0
- data/examples/website/content/index.txt +19 -0
- data/examples/website/layouts/default.txt +61 -0
- data/examples/website/lib/breadcrumbs.rb +28 -0
- data/examples/website/templates/_partial.erb +10 -0
- data/examples/website/templates/page.erb +18 -0
- data/lib/webby.rb +233 -0
- data/lib/webby/apps.rb +12 -0
- data/lib/webby/apps/generator.rb +276 -0
- data/lib/webby/apps/main.rb +258 -0
- data/lib/webby/auto_builder.rb +157 -0
- data/lib/webby/builder.rb +172 -0
- data/lib/webby/core_ext/enumerable.rb +11 -0
- data/lib/webby/core_ext/hash.rb +28 -0
- data/lib/webby/core_ext/kernel.rb +26 -0
- data/lib/webby/core_ext/string.rb +163 -0
- data/lib/webby/core_ext/time.rb +9 -0
- data/lib/webby/filters.rb +85 -0
- data/lib/webby/filters/basepath.rb +97 -0
- data/lib/webby/filters/erb.rb +9 -0
- data/lib/webby/filters/haml.rb +18 -0
- data/lib/webby/filters/markdown.rb +16 -0
- data/lib/webby/filters/maruku.rb +16 -0
- data/lib/webby/filters/outline.rb +309 -0
- data/lib/webby/filters/sass.rb +17 -0
- data/lib/webby/filters/slides.rb +56 -0
- data/lib/webby/filters/textile.rb +16 -0
- data/lib/webby/filters/tidy.rb +76 -0
- data/lib/webby/filters/wiki_words.rb +14 -0
- data/lib/webby/helpers.rb +30 -0
- data/lib/webby/helpers/capture_helper.rb +141 -0
- data/lib/webby/helpers/coderay_helper.rb +69 -0
- data/lib/webby/helpers/graphviz_helper.rb +136 -0
- data/lib/webby/helpers/tag_helper.rb +65 -0
- data/lib/webby/helpers/tex_img_helper.rb +133 -0
- data/lib/webby/helpers/ultraviolet_helper.rb +63 -0
- data/lib/webby/helpers/url_helper.rb +241 -0
- data/lib/webby/journal.rb +126 -0
- data/lib/webby/link_validator.rb +160 -0
- data/lib/webby/renderer.rb +390 -0
- data/lib/webby/resources.rb +137 -0
- data/lib/webby/resources/db.rb +251 -0
- data/lib/webby/resources/layout.rb +54 -0
- data/lib/webby/resources/meta_file.rb +211 -0
- data/lib/webby/resources/page.rb +81 -0
- data/lib/webby/resources/partial.rb +85 -0
- data/lib/webby/resources/resource.rb +201 -0
- data/lib/webby/resources/static.rb +36 -0
- data/lib/webby/stelan/mktemp.rb +135 -0
- data/lib/webby/stelan/paginator.rb +165 -0
- data/lib/webby/tasks/build.rake +27 -0
- data/lib/webby/tasks/create.rake +25 -0
- data/lib/webby/tasks/deploy.rake +22 -0
- data/lib/webby/tasks/growl.rake +16 -0
- data/lib/webby/tasks/validate.rake +19 -0
- data/spec/core_ext/hash_spec.rb +47 -0
- data/spec/core_ext/string_spec.rb +110 -0
- data/spec/core_ext/time_spec.rb +19 -0
- data/spec/data/hooligans/bad_meta_data_1.txt +34 -0
- data/spec/data/hooligans/bad_meta_data_2.txt +34 -0
- data/spec/data/html/anchor.html +11 -0
- data/spec/data/html/external.html +10 -0
- data/spec/data/html/invalid-relative.html +10 -0
- data/spec/data/html/relative-anchor.html +10 -0
- data/spec/data/html/relative-invalid-anchor.html +10 -0
- data/spec/data/html/relative.html +10 -0
- data/spec/data/outline/basic.out +81 -0
- data/spec/data/outline/basic.txt +25 -0
- data/spec/data/outline/no_clobber.out +86 -0
- data/spec/data/outline/numbering.out +81 -0
- data/spec/data/outline/numbering_only.out +21 -0
- data/spec/data/outline/toc_range_1.out +66 -0
- data/spec/data/outline/toc_range_2.out +55 -0
- data/spec/data/outline/toc_style.out +81 -0
- data/spec/data/site/Sitefile +9 -0
- data/spec/data/site/content/_partial.txt +10 -0
- data/spec/data/site/content/css/coderay.css +111 -0
- data/spec/data/site/content/css/site.css +67 -0
- data/spec/data/site/content/css/tumblog.css +308 -0
- data/spec/data/site/content/images/tumblog/permalink.gif +0 -0
- data/spec/data/site/content/images/tumblog/rss.gif +0 -0
- data/spec/data/site/content/index.txt +19 -0
- data/spec/data/site/content/photos.txt +21 -0
- data/spec/data/site/content/tumblog/200806/the-noble-chicken/index.txt +12 -0
- data/spec/data/site/content/tumblog/200807/historical-perspectives-on-the-classic-chicken-joke/index.txt +12 -0
- data/spec/data/site/content/tumblog/200807/mad-city-chickens/index.txt +10 -0
- data/spec/data/site/content/tumblog/200807/the-wisdom-of-the-dutch/index.txt +11 -0
- data/spec/data/site/content/tumblog/200807/up-a-tree/index.txt +13 -0
- data/spec/data/site/content/tumblog/index.txt +37 -0
- data/spec/data/site/content/tumblog/rss.txt +37 -0
- data/spec/data/site/layouts/default.txt +58 -0
- data/spec/data/site/layouts/tumblog/default.txt +44 -0
- data/spec/data/site/layouts/tumblog/post.txt +15 -0
- data/spec/data/site/lib/breadcrumbs.rb +28 -0
- data/spec/data/site/lib/tumblog_helper.rb +32 -0
- data/spec/data/site/tasks/tumblog.rake +30 -0
- data/spec/data/site/templates/_partial.erb +10 -0
- data/spec/data/site/templates/atom_feed.erb +40 -0
- data/spec/data/site/templates/page.erb +18 -0
- data/spec/data/site/templates/presentation.erb +40 -0
- data/spec/data/site/templates/tumblog/conversation.erb +12 -0
- data/spec/data/site/templates/tumblog/link.erb +10 -0
- data/spec/data/site/templates/tumblog/photo.erb +13 -0
- data/spec/data/site/templates/tumblog/post.erb +12 -0
- data/spec/data/site/templates/tumblog/quote.erb +11 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +58 -0
- data/spec/webby/apps/generator_spec.rb +117 -0
- data/spec/webby/apps/main_spec.rb +88 -0
- data/spec/webby/filters/basepath_spec.rb +167 -0
- data/spec/webby/filters/maruku_spec.rb +31 -0
- data/spec/webby/filters/outline_spec.rb +92 -0
- data/spec/webby/filters/textile_spec.rb +31 -0
- data/spec/webby/helpers/capture_helper_spec.rb +56 -0
- data/spec/webby/link_validator_spec.rb +154 -0
- data/spec/webby/renderer_spec.rb +139 -0
- data/spec/webby/resources/db_spec.rb +250 -0
- data/spec/webby/resources/layout_spec.rb +83 -0
- data/spec/webby/resources/meta_file_spec.rb +171 -0
- data/spec/webby/resources/page_spec.rb +111 -0
- data/spec/webby/resources/partial_spec.rb +58 -0
- data/spec/webby/resources/resource_spec.rb +219 -0
- data/spec/webby/resources/static_spec.rb +49 -0
- data/spec/webby/resources_spec.rb +69 -0
- data/tasks/mswin32.rake +38 -0
- data/tasks/website.rake +37 -0
- metadata +435 -0
@@ -0,0 +1,160 @@
|
|
1
|
+
require 'hpricot'
|
2
|
+
require 'open-uri'
|
3
|
+
|
4
|
+
module Webby
|
5
|
+
|
6
|
+
# The Webby LinkValidator class is used to validate the hyperlinks of all
|
7
|
+
# the HTML files in the output directory. By default, only links to other
|
8
|
+
# pages in the output directory are checked. However, setting the
|
9
|
+
# :external flag to +true+ will cause hyperlinks to external web sites to
|
10
|
+
# be validated as well.
|
11
|
+
#
|
12
|
+
class LinkValidator
|
13
|
+
|
14
|
+
# A lazy man's method that will instantiate a new link validator and run
|
15
|
+
# the validations.
|
16
|
+
#
|
17
|
+
def self.validate( opts = {} )
|
18
|
+
new(opts).validate
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_accessor :validate_externals
|
22
|
+
|
23
|
+
# call-seq:
|
24
|
+
# LinkValidator.new( opts = {} )
|
25
|
+
#
|
26
|
+
# Creates a new LinkValidator object. The only supported option is the
|
27
|
+
# :external flag. When set to +true+, the link validator will also check
|
28
|
+
# out links to external websites. This is done by opening a connection to
|
29
|
+
# the remote site and pulling down the page specified in the hyperlink.
|
30
|
+
# Use with caution.
|
31
|
+
#
|
32
|
+
def initialize( opts = {} )
|
33
|
+
@log = Logging::Logger[self]
|
34
|
+
|
35
|
+
glob = ::File.join(::Webby.site.output_dir, '**', '*.html')
|
36
|
+
@files = Dir.glob(glob).sort
|
37
|
+
@attr_rgxp = %r/\[@(\w+)\]$/o
|
38
|
+
|
39
|
+
@validate_externals = opts.getopt(:external, false)
|
40
|
+
@valid_uris = ::Webby.site.valid_uris.flatten
|
41
|
+
@invalid_uris = []
|
42
|
+
end
|
43
|
+
|
44
|
+
# Iterate over all the HTML files in the output directory and validate the
|
45
|
+
# hyperlinks.
|
46
|
+
#
|
47
|
+
def validate
|
48
|
+
@files.each {|fn| check_file fn}
|
49
|
+
end
|
50
|
+
|
51
|
+
# Check the given file (identified by its filename {fn for short here}) by
|
52
|
+
# iterating through all the configured xpaths and validating that those
|
53
|
+
# hyperlinks ae valid.
|
54
|
+
#
|
55
|
+
def check_file( fn )
|
56
|
+
@log.info "validating #{fn}"
|
57
|
+
|
58
|
+
dir = ::File.dirname(fn)
|
59
|
+
doc = Hpricot(::File.read(fn))
|
60
|
+
|
61
|
+
::Webby.site.xpaths.each do |xpath|
|
62
|
+
@attr_name = nil
|
63
|
+
|
64
|
+
doc.search(xpath).each do |element|
|
65
|
+
attr_name ||= @attr_rgxp.match(xpath)[1]
|
66
|
+
uri = URI.parse(element.get_attribute(attr_name))
|
67
|
+
validate_uri(uri, dir, doc)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
return nil # side effect of original implementation
|
71
|
+
end
|
72
|
+
|
73
|
+
# Validate the the page the _uri_ refers to actually exists. The directory
|
74
|
+
# of the current page being processed is needed in order to resolve
|
75
|
+
# relative paths.
|
76
|
+
#
|
77
|
+
# If the _uri_ is a relative path, then the output directory is searched
|
78
|
+
# for the appropriate page. If the _uri_ is an absolute path, then the
|
79
|
+
# remote server is contacted and the page requested from the server. This
|
80
|
+
# will only take place if the LinkValidator was created with the :external
|
81
|
+
# flag set to true.
|
82
|
+
#
|
83
|
+
def validate_uri( uri, dir, doc )
|
84
|
+
# do not retry external uris that have already been validated
|
85
|
+
return if @valid_uris.include? uri.to_s
|
86
|
+
|
87
|
+
return validate_relative_uri(uri, dir, doc) if uri.relative?
|
88
|
+
return validate_external_uri(uri, dir, doc) if uri.respond_to? :open
|
89
|
+
|
90
|
+
# otherwise, post a warning that the URI could not be validated
|
91
|
+
@log.warn "could not validate URI '#{uri.to_s}'"
|
92
|
+
end
|
93
|
+
|
94
|
+
# Validate that the anchor fragment of the URI exists in the given
|
95
|
+
# document. The document is an Hpricot document object.
|
96
|
+
#
|
97
|
+
# Returns +true+ if the anchor exists in the document and +false+ if it
|
98
|
+
# does not.
|
99
|
+
#
|
100
|
+
def validate_anchor( uri, doc )
|
101
|
+
return false if uri.fragment.nil?
|
102
|
+
|
103
|
+
anchor = '#' + uri.fragment
|
104
|
+
if doc.at(anchor).nil?
|
105
|
+
@log.error "invalid URI '#{uri.to_s}'"
|
106
|
+
false
|
107
|
+
else true end
|
108
|
+
end
|
109
|
+
|
110
|
+
# Validate that the file pointed to by the relative URI exists in the output
|
111
|
+
# directory. If the URI has an anchor, validate that the anchor exists as
|
112
|
+
# well.
|
113
|
+
#
|
114
|
+
def validate_relative_uri( uri, dir, doc )
|
115
|
+
return validate_anchor(uri, doc) if uri.path.empty?
|
116
|
+
|
117
|
+
path = if uri.path =~ %r/^\//
|
118
|
+
::File.join(::Webby.site.output_dir, uri.path)
|
119
|
+
else
|
120
|
+
::File.join(dir, uri.path)
|
121
|
+
end
|
122
|
+
path = ::File.join(path, 'index.html') if ::File.extname(path).empty?
|
123
|
+
|
124
|
+
uri_str = path.dup
|
125
|
+
(uri_str << '#' << uri.fragment) if uri.fragment
|
126
|
+
return if @valid_uris.include? uri_str
|
127
|
+
|
128
|
+
if test ?f, path
|
129
|
+
valid = if uri.fragment
|
130
|
+
validate_anchor(uri, Hpricot(::File.read(path)))
|
131
|
+
else true end
|
132
|
+
@valid_uris << uri_str if valid
|
133
|
+
else
|
134
|
+
@log.error "invalid URI '#{uri.to_s}'"
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# Validate that an external URI can be opened
|
139
|
+
#
|
140
|
+
def validate_external_uri( uri, dir, doc )
|
141
|
+
return unless @validate_externals
|
142
|
+
|
143
|
+
if @invalid_uris.include? uri.to_s
|
144
|
+
@log.error "could not open URI '#{uri.to_s}'"
|
145
|
+
return
|
146
|
+
end
|
147
|
+
|
148
|
+
begin
|
149
|
+
uri.open {|_| nil}
|
150
|
+
@valid_uris << uri.to_s
|
151
|
+
rescue Exception
|
152
|
+
@log.error "could not open URI '#{uri.to_s}'"
|
153
|
+
@invalid_uris << uri.to_s
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
end # class LinkValidator
|
158
|
+
end # module Webby
|
159
|
+
|
160
|
+
# EOF
|
@@ -0,0 +1,390 @@
|
|
1
|
+
# Equivalent to a header guard in C/C++
|
2
|
+
# Used to prevent the spec helper from being loaded more than once
|
3
|
+
unless defined? ::Webby::Renderer
|
4
|
+
|
5
|
+
require 'erb'
|
6
|
+
|
7
|
+
module Webby
|
8
|
+
|
9
|
+
# The Webby::Renderer is used to _filter_ and _layout_ the text found in the
|
10
|
+
# resource page files in the content directory.
|
11
|
+
#
|
12
|
+
# A page is filtered based on the settings of the 'filter' option in the
|
13
|
+
# page's meta-data information. For example, if 'textile' is specified as
|
14
|
+
# a filter, then the page will be run through the RedCloth markup filter.
|
15
|
+
# More than one filter can be used on a page; they will be run in the
|
16
|
+
# order specified in the meta-data.
|
17
|
+
#
|
18
|
+
# A page is rendered into a layout specified by the 'layout' option in the
|
19
|
+
# page's meta-data information.
|
20
|
+
#
|
21
|
+
class Renderer
|
22
|
+
include ERB::Util
|
23
|
+
|
24
|
+
# :stopdoc:
|
25
|
+
@@stack = []
|
26
|
+
# :startdoc:
|
27
|
+
|
28
|
+
# call-seq:
|
29
|
+
# Renderer.write( page )
|
30
|
+
#
|
31
|
+
# Render the given _page_ and write the resulting output to the page's
|
32
|
+
# destination. If the _page_ uses pagination, then multiple destination
|
33
|
+
# files will be created -- one for each paginated data set in the page.
|
34
|
+
#
|
35
|
+
def self.write( page )
|
36
|
+
renderer = self.new(page)
|
37
|
+
|
38
|
+
loop {
|
39
|
+
dest = page.destination
|
40
|
+
FileUtils.mkdir_p ::File.dirname(dest)
|
41
|
+
journal.create_or_update(page)
|
42
|
+
|
43
|
+
text = renderer._layout_page
|
44
|
+
unless text.nil?
|
45
|
+
::File.open(dest, 'w') {|fd| fd.write(text)}
|
46
|
+
end
|
47
|
+
|
48
|
+
break unless renderer._next_page
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
attr_reader :logger
|
53
|
+
|
54
|
+
# call-seq:
|
55
|
+
# Renderer.new( page )
|
56
|
+
#
|
57
|
+
# Create a new renderer for the given _page_. The renderer will apply the
|
58
|
+
# desired filters to the _page_ (from the page's meta-data) and then
|
59
|
+
# render the filtered page into the desired layout.
|
60
|
+
#
|
61
|
+
def initialize( page )
|
62
|
+
unless page.instance_of? Resources::Page
|
63
|
+
raise ArgumentError,
|
64
|
+
"only page resources can be rendered '#{page.path}'"
|
65
|
+
end
|
66
|
+
|
67
|
+
@page = page
|
68
|
+
@pages = Resources.pages
|
69
|
+
@partials = Resources.partials
|
70
|
+
@content = nil
|
71
|
+
@config = ::Webby.site
|
72
|
+
|
73
|
+
@_bindings = []
|
74
|
+
@_content_for = {}
|
75
|
+
@logger = Logging::Logger[self]
|
76
|
+
end
|
77
|
+
|
78
|
+
# call-seq:
|
79
|
+
# render( resource = nil, opts = {} ) => string
|
80
|
+
#
|
81
|
+
# Render the given resource (a page or a partial) and return the results
|
82
|
+
# as a string. If a resource is not given, then the options hash should
|
83
|
+
# contain the name of a partial to render (:partial => 'name').
|
84
|
+
#
|
85
|
+
# When a partial name is given, the partial is found by looking in the
|
86
|
+
# directory of the current page being rendered. Otherwise, the full path
|
87
|
+
# to the partial can be given.
|
88
|
+
#
|
89
|
+
# If a :guard option is given as true, then the resulting string will be
|
90
|
+
# protected from processing by subsequent filters. Currently this only
|
91
|
+
# protects against the textile filter.
|
92
|
+
#
|
93
|
+
# When rendering partials, local variables can be passed to the partial by
|
94
|
+
# setting them in hash passed as the :locals option.
|
95
|
+
#
|
96
|
+
# ==== Options
|
97
|
+
# :partial<String>::
|
98
|
+
# The partial to render
|
99
|
+
# :locals<Hash>::
|
100
|
+
# Locals values to define when rendering a partial
|
101
|
+
# :guard<Boolean>::
|
102
|
+
# Prevents the resulting string from being processed by subsequent
|
103
|
+
# filters (only textile for now)
|
104
|
+
#
|
105
|
+
# ==== Returns
|
106
|
+
# A string that is the rendered page or partial.
|
107
|
+
#
|
108
|
+
# ==== Examples
|
109
|
+
# # render the partial "foo" using the given local variables
|
110
|
+
# render( :partial => "foo", :locals => {:bar => "value for bar"} )
|
111
|
+
#
|
112
|
+
# # find another page and render it into this page and protect the
|
113
|
+
# # resulting contents from further filters
|
114
|
+
# page = @pages.find( :title => "Chicken Coop" )
|
115
|
+
# render( page, :guard => true )
|
116
|
+
#
|
117
|
+
# # find a partial and render it using the given local variables
|
118
|
+
# partial = @partials.find( :filename => "foo", :in_directory => "/path" )
|
119
|
+
# render( partial, :locals => {:baz => "baztastic"} )
|
120
|
+
#
|
121
|
+
def render( *args )
|
122
|
+
opts = Hash === args.last ? args.pop : {}
|
123
|
+
resource = args.first
|
124
|
+
resource = _find_partial(opts[:partial]) if resource.nil?
|
125
|
+
|
126
|
+
str = case resource
|
127
|
+
when Resources::Page
|
128
|
+
::Webby::Renderer.new(resource)._render_page
|
129
|
+
when Resources::Partial
|
130
|
+
_render_partial(resource, opts)
|
131
|
+
when Resources::Static
|
132
|
+
resource._read
|
133
|
+
else
|
134
|
+
raise ::Webby::Error, "expecting a page or a partial but got '#{resource.class.name}'"
|
135
|
+
end
|
136
|
+
|
137
|
+
str = _guard(str) if opts[:guard]
|
138
|
+
str
|
139
|
+
end
|
140
|
+
|
141
|
+
# call-seq:
|
142
|
+
# render_page => string
|
143
|
+
#
|
144
|
+
# This method is being deprecated. It is being made internal to the
|
145
|
+
# framework and really shouldn't be used anymore.
|
146
|
+
#
|
147
|
+
def render_page
|
148
|
+
Webby.deprecated "render_page", "this method is being made internal to the framework"
|
149
|
+
_render_page
|
150
|
+
end
|
151
|
+
|
152
|
+
# call-seq:
|
153
|
+
# render_partial( partial, :locals => {} ) => string
|
154
|
+
#
|
155
|
+
# This method is being deprecated. Please use the +render+ method instead.
|
156
|
+
#
|
157
|
+
def render_partial( part, opts = {} )
|
158
|
+
Webby.deprecated "render_partial", "it is being replaced by the Renderer#render() method"
|
159
|
+
opts[:partial] = part
|
160
|
+
render opts
|
161
|
+
end
|
162
|
+
|
163
|
+
# call-seq:
|
164
|
+
# paginate( items, per_page ) {|item| block}
|
165
|
+
#
|
166
|
+
# Iterate the given _block_ for each item selected from the _items_ array
|
167
|
+
# using the given number of items _per_page_. The first time the page is
|
168
|
+
# rendered, the items passed to the block are selected using the range
|
169
|
+
# (0...per_page). The next rendering selects (per_page...2*per_page). This
|
170
|
+
# continues until all _items_ have been paginated.
|
171
|
+
#
|
172
|
+
# Calling this method creates a <code>@pager</code> object that can be
|
173
|
+
# accessed from the page. The <code>@pager</code> contains information
|
174
|
+
# about the next page, the current page number, the previous page, and the
|
175
|
+
# number of items in the current page.
|
176
|
+
#
|
177
|
+
def paginate( items, count, &block )
|
178
|
+
@pager ||= Paginator.new(items.length, count, @page) do |offset, per_page|
|
179
|
+
items[offset,per_page]
|
180
|
+
end.first
|
181
|
+
|
182
|
+
@pager.each(&block)
|
183
|
+
end
|
184
|
+
|
185
|
+
# call-seq:
|
186
|
+
# get_binding => binding
|
187
|
+
#
|
188
|
+
# Returns the current binding for the renderer.
|
189
|
+
#
|
190
|
+
def get_binding
|
191
|
+
@_bindings.last
|
192
|
+
end
|
193
|
+
|
194
|
+
# call-seq:
|
195
|
+
# _render_page => string
|
196
|
+
#
|
197
|
+
# Apply the desired filters to the page. The filters to apply are
|
198
|
+
# determined from the page's meta-data.
|
199
|
+
#
|
200
|
+
def _render_page
|
201
|
+
_track_rendering(@page.path) {
|
202
|
+
Filters.process(self, @page, @page._read)
|
203
|
+
}
|
204
|
+
end
|
205
|
+
|
206
|
+
# call-seq:
|
207
|
+
# _render_partial( partial, :locals => {} ) => string
|
208
|
+
#
|
209
|
+
# Render the given _partial_ into the current page. The :locals are a hash
|
210
|
+
# of key / value pairs that will be set as local variables in the scope of
|
211
|
+
# the partial when it is rendered.
|
212
|
+
#
|
213
|
+
def _render_partial( part, opts = {} )
|
214
|
+
_track_rendering(part.path) {
|
215
|
+
_configure_locals(opts[:locals])
|
216
|
+
Filters.process(self, part, part._read)
|
217
|
+
}
|
218
|
+
end
|
219
|
+
|
220
|
+
# call-seq:
|
221
|
+
# _layout_page => string
|
222
|
+
#
|
223
|
+
# Apply the desired filters to the page and then render the filtered page
|
224
|
+
# into the desired layout. The filters to apply to the page are determined
|
225
|
+
# from the page's meta-data. The layout to use is also determined from the
|
226
|
+
# page's meta-data.
|
227
|
+
#
|
228
|
+
def _layout_page
|
229
|
+
@content = _render_page
|
230
|
+
|
231
|
+
_track_rendering(@page.path) {
|
232
|
+
_render_layout_for(@page)
|
233
|
+
}
|
234
|
+
raise ::Webby::Error, "rendering stack corrupted" unless @@stack.empty?
|
235
|
+
|
236
|
+
@content
|
237
|
+
rescue ::Webby::Error => err
|
238
|
+
logger.error "while rendering page '#{@page.path}'"
|
239
|
+
logger.error err.message
|
240
|
+
return nil
|
241
|
+
rescue => err
|
242
|
+
logger.error "while rendering page '#{@page.path}'"
|
243
|
+
logger.fatal err
|
244
|
+
exit 1
|
245
|
+
ensure
|
246
|
+
@content = nil
|
247
|
+
@@stack.clear
|
248
|
+
end
|
249
|
+
|
250
|
+
# call-seq:
|
251
|
+
# _render_layout_for( resource )
|
252
|
+
#
|
253
|
+
# Render the layout for the given resource. If the resource does not have
|
254
|
+
# a layout, then this method returns immediately.
|
255
|
+
#
|
256
|
+
def _render_layout_for( res )
|
257
|
+
return unless res.layout
|
258
|
+
lyt = Resources.find_layout(res.layout)
|
259
|
+
return if lyt.nil?
|
260
|
+
|
261
|
+
_track_rendering(lyt.path) {
|
262
|
+
@content = Filters.process(self, lyt, lyt._read)
|
263
|
+
_render_layout_for(lyt)
|
264
|
+
}
|
265
|
+
end
|
266
|
+
|
267
|
+
# call-seq:
|
268
|
+
# _next_page => true or false
|
269
|
+
#
|
270
|
+
# Returns +true+ if there is a next page to render. Returns +false+ if
|
271
|
+
# there is no next page or if pagination has not been configured for the
|
272
|
+
# current page.
|
273
|
+
#
|
274
|
+
def _next_page
|
275
|
+
return false unless defined? @pager and @pager
|
276
|
+
|
277
|
+
# go to the next page; break out if there is no next page
|
278
|
+
if @pager.next?
|
279
|
+
@pager = @pager.next
|
280
|
+
@_content_for.clear
|
281
|
+
@_bindings.clear
|
282
|
+
else
|
283
|
+
@pager.pager.reset
|
284
|
+
@pager = nil
|
285
|
+
return false
|
286
|
+
end
|
287
|
+
|
288
|
+
true
|
289
|
+
end
|
290
|
+
|
291
|
+
# call-seq:
|
292
|
+
# _track_rendering( path ) {block}
|
293
|
+
#
|
294
|
+
# Keep track of the page rendering for the given _path_. The _block_ is
|
295
|
+
# where the the page will be rendered.
|
296
|
+
#
|
297
|
+
# This method keeps a stack of the current pages being rendeered. It looks
|
298
|
+
# for duplicates in the stack -- an indication of a rendering loop. When a
|
299
|
+
# rendering loop is detected, an error is raised.
|
300
|
+
#
|
301
|
+
# This method returns whatever is returned from the _block_.
|
302
|
+
#
|
303
|
+
def _track_rendering( path )
|
304
|
+
loop_error = @@stack.include? path
|
305
|
+
@@stack << path
|
306
|
+
@_bindings << _binding
|
307
|
+
|
308
|
+
if loop_error
|
309
|
+
msg = "rendering loop detected for '#{path}'\n"
|
310
|
+
msg << " current rendering stack\n\t"
|
311
|
+
msg << @@stack.join("\n\t")
|
312
|
+
raise ::Webby::Error, msg
|
313
|
+
end
|
314
|
+
|
315
|
+
yield
|
316
|
+
ensure
|
317
|
+
@@stack.pop if path == @@stack.last
|
318
|
+
@_bindings.pop
|
319
|
+
end
|
320
|
+
|
321
|
+
# call-seq:
|
322
|
+
# _configure_locals( locals )
|
323
|
+
#
|
324
|
+
# Configure local variables in the scope of the current binding returned
|
325
|
+
# by the +get_binding+ method. The _locals_ should be given as a hash of
|
326
|
+
# name / value pairs.
|
327
|
+
#
|
328
|
+
def _configure_locals( locals )
|
329
|
+
return if locals.nil?
|
330
|
+
|
331
|
+
locals.each do |k,v|
|
332
|
+
Thread.current[:value] = v
|
333
|
+
definition = "#{k} = Thread.current[:value]"
|
334
|
+
eval(definition, get_binding)
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
# Attempts to locate a partial by name. If only the partial name is given,
|
339
|
+
# then the current directory of the page being rendered is searched for
|
340
|
+
# the partial. If a full path is given, then the partial is searched for
|
341
|
+
# in that directory.
|
342
|
+
#
|
343
|
+
# Raies a Webby::Error if the partial could not be found.
|
344
|
+
#
|
345
|
+
def _find_partial( part )
|
346
|
+
case part
|
347
|
+
when String
|
348
|
+
part_dir = ::File.dirname(part)
|
349
|
+
part_dir = @page.dir if part_dir == '.'
|
350
|
+
|
351
|
+
part_fn = ::File.basename(part)
|
352
|
+
part_fn = '_' + part_fn unless part_fn =~ %r/^_/
|
353
|
+
|
354
|
+
p = Resources.partials.find(
|
355
|
+
:filename => part_fn, :in_directory => part_dir ) rescue nil
|
356
|
+
raise ::Webby::Error, "could not find partial '#{part}'" if p.nil?
|
357
|
+
p
|
358
|
+
when ::Webby::Resources::Partial
|
359
|
+
part
|
360
|
+
else raise ::Webby::Error, "expecting a partial or a partial name" end
|
361
|
+
end
|
362
|
+
|
363
|
+
# This method will put filter guards around the given input string. This
|
364
|
+
# will protect the string from being processed by any remaining filters
|
365
|
+
# (specifically the textile filter).
|
366
|
+
#
|
367
|
+
# The string is returned unchanged if there are no remaining filters to
|
368
|
+
# guard against.
|
369
|
+
#
|
370
|
+
def _guard( str )
|
371
|
+
return str unless @_cursor
|
372
|
+
|
373
|
+
if @_cursor.remaining_filters.include? 'textile'
|
374
|
+
str = "<notextile>\n%s\n</notextile>" % str
|
375
|
+
end
|
376
|
+
str
|
377
|
+
end
|
378
|
+
|
379
|
+
# Returns the binding in the scope of this Renderer object.
|
380
|
+
#
|
381
|
+
def _binding() binding end
|
382
|
+
|
383
|
+
end # class Renderer
|
384
|
+
end # module Webby
|
385
|
+
|
386
|
+
Webby.require_all_libs_relative_to(__FILE__, 'stelan')
|
387
|
+
|
388
|
+
end # unless defined?
|
389
|
+
|
390
|
+
# EOF
|