Class: Zenweb::Page
- Inherits:
-
Object
- Object
- Zenweb::Page
- Includes:
- Rake::DSL
- Defined in:
- lib/zenweb/page.rb,
lib/zenweb/plugins/erb.rb,
lib/zenweb/plugins/less.rb,
lib/zenweb/plugins/disqus.rb,
lib/zenweb/plugins/google.rb,
lib/zenweb/plugins/markdown.rb
Overview
Page represents pretty much any type of file that goes on your website or is needed by other pages to build your website. Each page can have a YAML header that contains configuration data or variables used in the page.
Defined Under Namespace
Modules: MarkdownHelpers
Constant Summary collapse
- KRAMDOWN_CONFIG =
:nodoc:
{ # :nodoc: :toc_levels => '2..4', :entity_output => :symbolic, :coderay_wrap => :div, :coderay_line_numbers => :table, :coderay_tab_width => 4, :coderay_css => :class, }
Instance Attribute Summary collapse
-
#parent ⇒ Object
The parent page of this page.
-
#path ⇒ Object
readonly
The path to this source file.
-
#site ⇒ Object
readonly
The shared site instance.
-
#subpages ⇒ Object
readonly
The pages directly below this page.
Class Method Summary collapse
-
.renderers_re ⇒ Object
Returns a regexp that will match file extensions for all known renderer types.
Instance Method Summary collapse
-
#[](k) ⇒ Object
Helper method to access the config value named
k. -
#all_subpages ⇒ Object
All pages below this page, recursively.
- #analytics ⇒ Object
-
#body ⇒ Object
Returns the actual content of the file minus the optional YAML header.
-
#breadcrumbs ⇒ Object
Returns an array of all parent pages of this page, including self.
- #change_frequency ⇒ Object
-
#clean_url ⇒ Object
Return the url as users normally enter them (ie, no index.html).
-
#config ⇒ Object
Returns the closest Config instance for this file.
-
#content ⇒ Object
Returns the entire (raw) content of the file.
-
#date ⇒ Object
Returns either:.
-
#date_from_path ⇒ Object
:nodoc:.
- #date_str ⇒ Object
-
#dated? ⇒ Boolean
Returns true if this page has a date (via config or within the path).
-
#dated_path? ⇒ Boolean
Is this a dated page? (ie, does it have YYYY-MM-DD in the path?).
-
#depends_on(deps) ⇒ Object
Wires up additional dependencies for this Page.
-
#disqus(shortname) ⇒ Object
Returns a javascript blob to add a disqus comments block to the page.
-
#disqus_counts(shortname) ⇒ Object
Returns a javascript blob to convert properly formatted links to disqus comment counts.
-
#erb(content, source, binding = TOPLEVEL_BINDING) ⇒ Object
Render erb in
contentforsourcewith +binding. - #extend_md ⇒ Object
-
#filetype(name = self.path) ⇒ Object
Returns the extension (without the ‘.’) of
name, defaulting toself.path. -
#filetypes ⇒ Object
Returns an array of extensions (in reverse order) of this page that match known renderers.
-
#format_date(s) ⇒ Object
Format a date string
susing the config valuedate_fmtor YYYY/MM/DD. - #gauges_analytics ⇒ Object
-
#generate ⇒ Object
Render and write the result to #url_path.
-
#google_ad(slot, width = 468, height = 60) ⇒ Object
Returns a javascript blob to add a google ad to the page.
- #google_analytics ⇒ Object
-
#html? ⇒ Boolean
Returns true if this is an html page.
-
#include(name, page) ⇒ Object
Render a named file from
_includes. -
#index? ⇒ Boolean
Returns true if this page is an index page.
-
#initialize(site, path, config = nil) ⇒ Page
constructor
:nodoc:.
-
#inspect ⇒ Object
(also: #to_s)
:nodoc:.
-
#layout ⇒ Object
Return a layout Page named in the config key
layout. -
#link_html(title = self.title) ⇒ Object
Convenience function to create an html link for this page.
-
#markdown(content, no_line_numbers = false) ⇒ Object
Render markdown content.
-
#meta(key, name = key, label = "name") ⇒ Object
Stupid helper method to make declaring header meta lines cleaner.
-
#method_missing(msg, *args) ⇒ Object
Access a config variable and only warn if it isn’t accessible.
-
#parent_url(url = self.url) ⇒ Object
Returns the parent url of a particular url (or self).
-
#render(page = self, content = nil) ⇒ Object
Render this page as a whole.
-
#render_erb(page, content) ⇒ Object
Render a page’s erb and return the result.
-
#render_less(page, content) ⇒ Object
Render less source to css.
-
#render_md(page, content) ⇒ Object
Render markdown page content using kramdown.
-
#run_js_script(url) ⇒ Object
TODO: move this and others to plugins/html_toys.rb (or something).
- #stale? ⇒ Boolean
-
#subrender(page = self, content = nil) ⇒ Object
Render a Page instance based on its filetypes.
-
#url ⇒ Object
Return the url for this page.
-
#url_dir ⇒ Object
The directory portion of the url.
-
#url_path ⇒ Object
The real file path for the generated file.
-
#wire ⇒ Object
Wire up this page to the rest of the rake dependencies.
Constructor Details
#initialize(site, path, config = nil) ⇒ Page
:nodoc:
51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/zenweb/page.rb', line 51 def initialize site, path, config = nil # :nodoc: # TODO: make sure that creating page /a.html strips leading / from path @site, @path = site, path @config = config if config self.filetypes.each do |type| send "extend_#{type}" if self.respond_to? "extend_#{type}" end @subpages = [] end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(msg, *args) ⇒ Object
Access a config variable and only warn if it isn’t accessible. If msg starts with render, go ahead and pass that up to the default method_missing.
311 312 313 314 315 316 317 318 |
# File 'lib/zenweb/page.rb', line 311 def method_missing msg, *args # :nodoc: case msg.to_s when /=|^render_|^to_a(?:ry)?$/ then # to_a/ry for 1.9 only. :( super else self[msg] end end |
Instance Attribute Details
#parent ⇒ Object
The parent page of this page. Can be nil.
35 36 37 |
# File 'lib/zenweb/page.rb', line 35 def parent @parent end |
#path ⇒ Object (readonly)
The path to this source file.
25 26 27 |
# File 'lib/zenweb/page.rb', line 25 def path @path end |
#site ⇒ Object (readonly)
The shared site instance.
20 21 22 |
# File 'lib/zenweb/page.rb', line 20 def site @site end |
#subpages ⇒ Object (readonly)
The pages directly below this page. Can be empty.
30 31 32 |
# File 'lib/zenweb/page.rb', line 30 def subpages @subpages end |
Class Method Details
.renderers_re ⇒ Object
Returns a regexp that will match file extensions for all known renderer types.
41 42 43 44 45 46 47 48 49 |
# File 'lib/zenweb/page.rb', line 41 def self.renderers_re @renderers_re ||= begin ext = instance_methods.grep(/^render_/).map { |s| s.to_s.sub(/render_/, '') } /(?:\.(#{ext.join "|"}))+$/ end end |
Instance Method Details
#[](k) ⇒ Object
Helper method to access the config value named k.
66 67 68 |
# File 'lib/zenweb/page.rb', line 66 def [] k config[k] or warn("#{self.inspect} does not define #{k.inspect}") end |
#all_subpages ⇒ Object
All pages below this page, recursively.
73 74 75 |
# File 'lib/zenweb/page.rb', line 73 def all_subpages subpages.map { |p| [p, p.all_subpages] } end |
#analytics ⇒ Object
64 65 66 |
# File 'lib/zenweb/plugins/google.rb', line 64 def analytics [google_analytics, gauges_analytics].compact.join "\n\n" end |
#body ⇒ Object
Returns the actual content of the file minus the optional YAML header.
80 81 82 83 84 85 86 |
# File 'lib/zenweb/page.rb', line 80 def body # TODO: add a test for something with --- without a yaml header. @body ||= begin _, body = Zenweb::Config.split path body.strip end end |
#breadcrumbs ⇒ Object
Returns an array of all parent pages of this page, including self.
99 100 101 102 103 104 105 106 107 108 |
# File 'lib/zenweb/page.rb', line 99 def pages = [self] loop do parent = pages.first.parent break unless parent and parent != pages.first pages.unshift parent end pages.pop # take self back off pages end |
#change_frequency ⇒ Object
173 174 175 176 177 178 179 180 181 182 183 184 185 186 |
# File 'lib/zenweb/page.rb', line 173 def change_frequency days_old = (Time.now - self.date).to_i / 86400 case days_old when 0...14 then "daily" when 14...56 then "weekly" when 56...365 then "monthly" else "yearly" end end |
#clean_url ⇒ Object
Return the url as users normally enter them (ie, no index.html).
113 114 115 |
# File 'lib/zenweb/page.rb', line 113 def clean_url url.sub(/\/index.html$/, '/') end |
#config ⇒ Object
Returns the closest Config instance for this file. That could be the YAML prefix in the file or it could be a _config.yml file in the file’s directory or above.
122 123 124 125 126 127 128 |
# File 'lib/zenweb/page.rb', line 122 def config unless defined? @config then @config = Config.new site, path @config = @config.parent unless content.start_with? "---" end @config end |
#content ⇒ Object
Returns the entire (raw) content of the file.
133 134 135 136 |
# File 'lib/zenweb/page.rb', line 133 def content # TODO: this has the same drawbacks as Config.split @content ||= File.read path end |
#date ⇒ Object
Returns either:
+ The value of the date config value + The date embedded in the filename itself (eg: 2012-01-02-blah.html). + The last modified timestamp of the file itself.
145 146 147 |
# File 'lib/zenweb/page.rb', line 145 def date config['date'] || date_from_path || File.stat(path).mtime end |
#date_from_path ⇒ Object
:nodoc:
149 150 151 152 |
# File 'lib/zenweb/page.rb', line 149 def date_from_path # :nodoc: date = path[/\d\d\d\d-\d\d-\d\d/] Time.local(*date.split(/-/).map(&:to_i)) if date end |
#date_str ⇒ Object
154 155 156 157 |
# File 'lib/zenweb/page.rb', line 154 def date_str fmt ||= self.config["date_fmt"] || "%Y-%m" # REFACTOR: yuck self.date.strftime fmt end |
#dated? ⇒ Boolean
Returns true if this page has a date (via config or within the path).
162 163 164 |
# File 'lib/zenweb/page.rb', line 162 def dated? config['date'] || date_from_path end |
#dated_path? ⇒ Boolean
Is this a dated page? (ie, does it have YYYY-MM-DD in the path?)
169 170 171 |
# File 'lib/zenweb/page.rb', line 169 def dated_path? path[/\d\d\d\d-\d\d-\d\d/] end |
#depends_on(deps) ⇒ Object
Wires up additional dependencies for this Page. from_deps may be a Hash (eg site.pages), an Array (eg. site.categories.blog), or a single page.
200 201 202 203 204 205 206 207 208 209 |
# File 'lib/zenweb/page.rb', line 200 def depends_on deps if String === deps then file self.path => deps else deps = deps.values if Hash === deps deps = Array(deps) file self.url_path => deps.map(&:url_path) - [self.url_path] end end |
#disqus(shortname) ⇒ Object
Returns a javascript blob to add a disqus comments block to the page.
6 7 8 9 |
# File 'lib/zenweb/plugins/disqus.rb', line 6 def disqus shortname '<div id="disqus_thread"></div>' + run_js_script("http://#{shortname}.disqus.com/embed.js") end |
#disqus_counts(shortname) ⇒ Object
Returns a javascript blob to convert properly formatted links to disqus comment counts.
15 16 17 |
# File 'lib/zenweb/plugins/disqus.rb', line 15 def disqus_counts shortname run_js_script "http://#{shortname}.disqus.com/count.js" end |
#erb(content, source, binding = TOPLEVEL_BINDING) ⇒ Object
Render erb in content for source with +binding.
Personally, I find erb’s delimiters a bit annoying, so for now, I’ve added additional gsub’s to the content to make it a bit more palatable.
{{ ... }} becomes <%= ... %>
{% ... %} becomes <% ... %>
Unfortunately, those are the delimiters from liquid, so if someone goes and makes a liquid plugin it could clash. But why you’d have liquid and erb on the same file is beyond me… so it prolly won’t become a legitimate issue.
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
# File 'lib/zenweb/plugins/erb.rb', line 24 def erb content, source, binding = TOPLEVEL_BINDING require 'erb' extend ERB::Util unless defined? @erb then content = content. gsub(/\{\{/, "<%="). gsub(/\}\}/, "%>"). gsub(/\{%/, "<%"). gsub(/%\}/, "%>"). gsub(/\\([{}%])/, '\1') @erb = ERB.new(content, nil, "-") end @erb.filename = source.inspect @erb.result binding end |
#extend_md ⇒ Object
21 22 23 |
# File 'lib/zenweb/plugins/markdown.rb', line 21 def extend_md extend Zenweb::Page::MarkdownHelpers end |
#filetype(name = self.path) ⇒ Object
Returns the extension (without the ‘.’) of name, defaulting to self.path.
215 216 217 |
# File 'lib/zenweb/page.rb', line 215 def filetype name = self.path File.extname(name)[1..-1] end |
#filetypes ⇒ Object
Returns an array of extensions (in reverse order) of this page that match known renderers. For example:
Given renderer methods render_erb and render_md, the file “index.html.md.erb” would return %w[erb md], but the file “index.html” would return [].
Additional renderers can be added via Site.load_plugins.
229 230 231 232 233 |
# File 'lib/zenweb/page.rb', line 229 def filetypes @filetypes ||= path[self.class.renderers_re].split(/\./)[1..-1].reverse rescue [] end |
#format_date(s) ⇒ Object
Format a date string s using the config value date_fmt or YYYY/MM/DD.
238 239 240 241 |
# File 'lib/zenweb/page.rb', line 238 def format_date s fmt = self.config["date_fmt"] || "%Y/%m/%d" Time.local(*s.split(/-/).map(&:to_i)).strftime(fmt) end |
#gauges_analytics ⇒ Object
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
# File 'lib/zenweb/plugins/google.rb', line 44 def gauges_analytics if site.config["gauges_id"] then " <script type=\"text/javascript\">\n var _gauges = _gauges || [];\n (function() {\n var t = document.createElement('script');\n t.type = 'text/javascript';\n t.async = true;\n t.id = 'gauges-tracker';\n t.setAttribute('data-site-id', '\#{site.gauges_id}');\n t.src = '//secure.gaug.es/track.js';\n var s = document.getElementsByTagName('script')[0];\n s.parentNode.insertBefore(t, s);\n })();\n </script>\n EOM\n end\nend\n".gsub(/^ {8}/, '') |
#generate ⇒ Object
Render and write the result to #url_path.
246 247 248 249 250 251 252 253 254 |
# File 'lib/zenweb/page.rb', line 246 def generate warn "Rendering #{url_path}" content = self.render open url_path, "w" do |f| f.puts content end end |
#google_ad(slot, width = 468, height = 60) ⇒ Object
Returns a javascript blob to add a google ad to the page. You need to provide the configuration param “google_ad_client” to your site config for this to work.
7 8 9 10 11 12 13 14 15 16 17 18 19 |
# File 'lib/zenweb/plugins/google.rb', line 7 def google_ad slot, width = 468, height = 60 " <script><!--\n google_ad_client = \"\#{self[\"google_ad_client\"]}\";\n google_ad_slot = \"\#{slot}\";\n google_ad_width = \#{width};\n google_ad_height = \#{height};\n //-->\n </script>\n <script src=\"http://pagead2.googlesyndication.com/pagead/show_ads.js\">\n </script>\n EOM\nend\n".gsub(/^ {6}/, '') |
#google_analytics ⇒ Object
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
# File 'lib/zenweb/plugins/google.rb', line 21 def google_analytics if site.config["google_ua"] then " <script type=\"text/javascript\">\n var _gaq = _gaq || [];\n _gaq.push(['_setAccount', '\#{site.google_ua}']);\n _gaq.push(['_trackPageview']);\n\n (function() {\n var ga = document.createElement('script');\n ga.type = 'text/javascript';\n ga.async = true;\n ga.src = ('https:' == document.location.protocol ?\n 'https://ssl' : 'http://www') +\n '.google-analytics.com/ga.js';\n (document.getElementsByTagName('head')[0] ||\n document.getElementsByTagName('body')[0]).appendChild(ga);\n })();\n </script>\n EOM\n end\nend\n".gsub(/^ {8}/, '') |
#html? ⇒ Boolean
Returns true if this is an html page.
191 192 193 |
# File 'lib/zenweb/page.rb', line 191 def html? path =~ /\.html/ end |
#include(name, page) ⇒ Object
Render a named file from _includes.
category: XXX
261 262 263 264 |
# File 'lib/zenweb/page.rb', line 261 def include name, page incl = Page.new(site, File.join("_includes", name)) incl.subrender page end |
#index? ⇒ Boolean
Returns true if this page is an index page.
269 270 271 |
# File 'lib/zenweb/page.rb', line 269 def index? url.end_with? "index.html" end |
#inspect ⇒ Object Also known as: to_s
:nodoc:
273 274 275 |
# File 'lib/zenweb/page.rb', line 273 def inspect # :nodoc: "Page[#{path.inspect}]" end |
#layout ⇒ Object
Return a layout Page named in the config key layout.
TODO: expand
284 285 286 287 288 289 |
# File 'lib/zenweb/page.rb', line 284 def layout unless defined? @layout then @layout = site.layout self.config["layout"] end @layout end |
#link_html(title = self.title) ⇒ Object
Convenience function to create an html link for this page.
294 295 296 |
# File 'lib/zenweb/page.rb', line 294 def link_html title = self.title %(<a href="#{clean_url}">#{title}</a>) end |
#markdown(content, no_line_numbers = false) ⇒ Object
Render markdown content.
I cheated and added some additional gsubs. I prefer ““‘ lang” so that works now.
31 32 33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/zenweb/plugins/markdown.rb', line 31 def markdown content, no_line_numbers = false require "kramdown" require "coderay/zenweb_extensions" content = content. gsub(/^\`\`\` *(\w+)/) { "~~~ #$1" }. gsub(/^\`\`\`/, '~~~') config = KRAMDOWN_CONFIG.dup config[:coderay_line_numbers] = nil if no_line_numbers Kramdown::Document.new(content, config).to_html end |
#meta(key, name = key, label = "name") ⇒ Object
Stupid helper method to make declaring header meta lines cleaner
301 302 303 304 |
# File 'lib/zenweb/page.rb', line 301 def key, name=key, label="name" val = self.config[key] %(<meta #{label}="#{name}" content="#{val}">) if val end |
#parent_url(url = self.url) ⇒ Object
Returns the parent url of a particular url (or self).
91 92 93 94 |
# File 'lib/zenweb/page.rb', line 91 def parent_url url = self.url url = File.dirname url if File.basename(url) == "index.html" File.join File.dirname(url), "index.html" end |
#render(page = self, content = nil) ⇒ Object
Render this page as a whole. This includes rendering the page’s content into a layout if one has been specified via config.
324 325 326 327 328 329 330 331 |
# File 'lib/zenweb/page.rb', line 324 def render page = self, content = nil content = subrender page, content layout = self.layout # TODO: make nullpage to avoid 'if layout' tests content = layout.render page, content if layout content end |
#render_erb(page, content) ⇒ Object
Render a page’s erb and return the result
5 6 7 |
# File 'lib/zenweb/plugins/erb.rb', line 5 def render_erb page, content erb body, self, binding end |
#render_less(page, content) ⇒ Object
Render less source to css.
5 6 7 8 9 |
# File 'lib/zenweb/plugins/less.rb', line 5 def render_less page, content require "less" Less::Engine.new(content || body).to_css end |
#render_md(page, content) ⇒ Object
Render markdown page content using kramdown.
16 17 18 19 |
# File 'lib/zenweb/plugins/markdown.rb', line 16 def render_md page, content no_line_numbers = page.config["no_line_numbers"] markdown(content || self.body, no_line_numbers) end |
#run_js_script(url) ⇒ Object
TODO: move this and others to plugins/html_toys.rb (or something)
351 352 353 354 355 356 357 358 359 360 361 362 363 |
# File 'lib/zenweb/page.rb', line 351 def run_js_script url " <script type=\"text/javascript\">\n (function() {\n var s = document.createElement('script');\n s.type = 'text/javascript';\n s.async = true;\n s.src = '\#{url}';\n (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(s);\n })();\n </script>\n".gsub(/^ {6}/, '') end |
#stale? ⇒ Boolean
333 334 335 |
# File 'lib/zenweb/page.rb', line 333 def stale? file(url_path).needed? end |
#subrender(page = self, content = nil) ⇒ Object
Render a Page instance based on its filetypes. For example, index.html.md.erb will essentially call:
render_md(render_erb(content))
343 344 345 346 347 |
# File 'lib/zenweb/page.rb', line 343 def subrender page = self, content = nil self.filetypes.inject(content) { |cont, type| send "render_#{type}", page, cont } || self.body end |
#url ⇒ Object
Return the url for this page. The url is based entirely on its location in the file-system.
TODO: expand
371 372 373 374 375 376 |
# File 'lib/zenweb/page.rb', line 371 def url @url ||= self.path. sub(/^/, '/'). sub(/(\d\d\d\d)-(\d\d)-(\d\d)-/) { |s| "#{format_date s}/" }. gsub(self.class.renderers_re, '') end |
#url_dir ⇒ Object
The directory portion of the url.
381 382 383 |
# File 'lib/zenweb/page.rb', line 381 def url_dir File.dirname url_path end |
#url_path ⇒ Object
The real file path for the generated file.
388 389 390 |
# File 'lib/zenweb/page.rb', line 388 def url_path @url_path ||= File.join(".site", self.url) end |
#wire ⇒ Object
Wire up this page to the rest of the rake dependencies. If you have extra dependencies for this file (ie, an index page that links to many other pages) you can add them by creating a rake task named :extra_wirings and using #depends_on. Eg:
task :extra_wirings do |x|
site = $website
page = site.pages
page["sitemap.xml.erb"]. depends_on site.html_pages
page["atom.xml.erb"]. depends_on site.pages_by_date.first(30)
page["blog/index.html.erb"].depends_on site.categories.blog
end
407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 |
# File 'lib/zenweb/page.rb', line 407 def wire @wired ||= false # HACK return if @wired @wired = true file self.path conf = self.config conf = conf.parent if self.path == conf.path file self.path => conf.path if conf.path conf.wire if self.layout then file self.path => self.layout.path self.layout.wire end file url_path => all_subpages.flatten.map(&:url_path) if url =~ /index.html/ unless url_dir =~ %r%/_% then directory url_dir file url_path => url_dir file url_path => path do self.generate end task :site => url_path end end |