Class: Rote::Page
- Inherits:
-
Object
- Object
- Rote::Page
- Defined in:
- lib/rote/page.rb
Overview
A Page object represents an individual page in the final documentation set, bringing together a source template, optional page code (in Ruby) obtained from various sources (see below), and an optional layout template (with it’s own code) to produce rendered output as a String. Specifically, Page provides the following capabilities:
-
Read template files containing ERB code and render them to create output.
-
Locate and evaluate all common and page code in the binding of the Page instance itself.
-
Apply layout to rendered content using multiple render passes.
-
Apply user-supplied filters to the output to allow transformations and additional processing.
In normal use the instantiation and initialization of Pages will be handled internally by Rote. From the user point of view most interaction with Rote from user code takes place via the instance methods of this class.
Template lookup and evaluation
Each Page instance is provided at instantiation with base paths from which it should resolve both template and layout files when required. Usually these paths are supplied by the Rake task configuration. The attributes that provide information on template and layout paths (e.g. template_name, base_layout_name, and so on) give those paths relative to the base_path and layout_path as appropriate.
Common, page and layout code evaluation
Code applied to a given page is found and evaluated in the following order:
-
A block supplied to the <%= section_link ‘extension mappings’, ‘extension mapping’ %> that matched this page (if any).
-
Any COMMON.rb files from the filesystem root down to this directory.
-
This page’s ruby code, basename.rb.
-
In the template itself (via ERB).
When a Page instance is created, Rote looks for these, and if found evaluates them, in order, in the Page instance binding.
Additionally, when layout is used the following evaluation takes place *after rendering the template text* and can be used to make variables available for the layout pass(es), and apply nested layout:
-
This layout’s ruby code, layout_basename.rb.
-
In the layout itself (again, with ERB).
As mentioned, Page instances serve as the context for page code execution - All user-supplied code (COMMON.rb, page and layout code, and ERB in the templates themselves) is executed in the binding of an instance of this class.
Layout
All pages support layout, which allow common template to be applied across several pages. This is handled via multiple render passes, with each layout responsible for including the previously rendered content (via ERB).
Layout templates include the content rendered by the page (or previous layout, see below) render pass using the instance variable @content_for_layout. This should be a familar pattern for those familiar with the Rails framework.
To apply layout to a page, the layout method should be called, passing in the base-name (with extension if different from the page template). When issued from common or page code, multiple calls to this method will override any previous setting. It may be called again from layout code, however, in which case the output of the currently-rendering layout will be passed (via the @content_to_layout instance variable) to the specified layout. In this way, Rote allows layouts to be nested to any level.
Filtering
The page_filter and post_filter methods allow filters to be applied to a page. Filters can be used to support any kind of textual transformation, macro expansion (page filters), or post-render processing (post filters). Rote includes a number of filters as standard, supporting plain-text markup, syntax highlighting, HTMLTidy postprocessing, and more.
See Rote::Filters for details of standard filters and their individual use.
Filters are written in Ruby, and Rote provides base-classes from which filters can be derived with just a few lines of code (See Rote::Filters::TextFilter and Rote::Filters::MacroFilter). Additionally, the page and post filter methods allow text filters to be created from a supplied block.
Rendering
Rendering occurs only once for a given page object, when the render method is first called. Once a page has been rendered, the instance it is frozen to prevent further modification, and the rendered output is cached. Future calls to render will return the cached output.
Instance Attribute Summary collapse
-
#base_path ⇒ Object
readonly
The base path for template resolution.
-
#layout_names ⇒ Object
readonly
:nodoc:.
-
#layout_path ⇒ Object
readonly
The base path for layout resolution.
-
#layout_text ⇒ Object
readonly
The text of the layout to use for this page.
-
#page_filters ⇒ Object
readonly
The array of page filters (applied to this page during the first render pass, before layout is applied).
-
#post_filters ⇒ Object
readonly
The array of post filters (applied to this page output after layout is applied).
-
#template_name ⇒ Object
readonly
The basename from which this page’s template was read, relative to the
base_path. -
#template_text ⇒ Object
readonly
The text of the template to use for this page.
Class Method Summary collapse
-
.page_ruby_filename(template_fn) ⇒ Object
Helper that returns a page-code filename given a template filename.
-
.resolve_common_rubys(dir, arr = []) ⇒ Object
Find all COMMON.rb files from given dir up to FS root.
Instance Method Summary collapse
-
#base_layout_filename ⇒ Object
(also: #layout_filename)
Returns the full filename of the first queued layout.
-
#base_layout_name ⇒ Object
(also: #layout_name)
The filename of the innermost layout, usually specified by the page itself, relative to the
layout_path. -
#initialize(template_name, pages_dir = '.', layout_dir = pages_dir) {|_self| ... } ⇒ Page
constructor
Reads the template, and evaluates the global and page scripts, if available, using the current binding.
-
#layout(basename) ⇒ Object
Sets the page’s base-layout as specified, or applies nested layout if called during a layout render pass.
-
#page_filter(filter = nil, &block) ⇒ Object
Append
filterto this page’s page-filter chain, or create a new Rote::Filters::TextFilter with the supplied block. -
#post_filter(filter = nil, &block) ⇒ Object
Append
filterto this page’s post-filter chain. -
#render ⇒ Object
(also: #to_s)
Render this page’s textile and ERB, and apply layout.
-
#ruby_filename ⇒ Object
Returns the full filename of this Page’s ruby source.
-
#template_filename ⇒ Object
Returns the full filename of this Page’s template.
Constructor Details
#initialize(template_name, pages_dir = '.', layout_dir = pages_dir) {|_self| ... } ⇒ Page
Reads the template, and evaluates the global and page scripts, if available, using the current binding. You may define any instance variables or methods you like in that code for use in the template, as well as accessing the predefined @template and @template_text variables.
If specified, the layout path will be used to find layouts referenced from templates.
If a block is supplied, it is executed before the global / page code. This will be the block supplied by the file-extension mapping.
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 |
# File 'lib/rote/page.rb', line 197 def initialize(template_name, pages_dir = '.', layout_dir = pages_dir) @template_text = nil @template_name = nil @layout_names = [] @content_for_layout = nil @result = nil @layout_defext = File.extname(template_name) @layout_path = layout_dir[STRIP_SLASHES,1] @layout_text = nil @base_path = pages_dir[STRIP_SLASHES,1] @page_filters, @post_filters = [], [] # read in the template. Layout _may_ get configured later in page code # We only add the pages_dir if it's not already there, because it's # easier to pass the whole relative fn from rake... # template_name always needs with no prefix. tfn = template_name read_template(tfn) # Yield to the (extension mapping) block yield self if block_given? # Eval COMMON.rb's eval_common_rubys # get script filenames, and eval them if found tfn = ruby_filename # nil if no file instance_eval(File.read(tfn),tfn) if tfn end |
Instance Attribute Details
#base_path ⇒ Object (readonly)
The base path for template resolution.
169 170 171 |
# File 'lib/rote/page.rb', line 169 def base_path @base_path end |
#layout_names ⇒ Object (readonly)
:nodoc:
159 160 161 |
# File 'lib/rote/page.rb', line 159 def layout_names @layout_names end |
#layout_path ⇒ Object (readonly)
The base path for layout resolution
172 173 174 |
# File 'lib/rote/page.rb', line 172 def layout_path @layout_path end |
#layout_text ⇒ Object (readonly)
The text of the layout to use for this page. This is read in when (if) the page source calls layout(basename).
Deprecated This has no knowledge of nested layout, and operates only on the innermost layout.
153 154 155 |
# File 'lib/rote/page.rb', line 153 def layout_text @layout_text end |
#page_filters ⇒ Object (readonly)
The array of page filters (applied to this page during the first render pass, before layout is applied). You can use page_filter to add new page filters, which gives implicit block => Filters::TextFilter conversion and checks for nil.
178 179 180 |
# File 'lib/rote/page.rb', line 178 def page_filters @page_filters end |
#post_filters ⇒ Object (readonly)
The array of post filters (applied to this page output after layout is applied). You can use post_filter to add new post filters, which gives implicit block => Filters::TextFilter conversion and checks for nil.
184 185 186 |
# File 'lib/rote/page.rb', line 184 def post_filters @post_filters end |
#template_name ⇒ Object (readonly)
The basename from which this page’s template was read, relative to the base_path.
157 158 159 |
# File 'lib/rote/page.rb', line 157 def template_name @template_name end |
#template_text ⇒ Object (readonly)
The text of the template to use for this page.
146 147 148 |
# File 'lib/rote/page.rb', line 146 def template_text @template_text end |
Class Method Details
.page_ruby_filename(template_fn) ⇒ Object
Helper that returns a page-code filename given a template filename. This does not check that the source exists - use the ruby_filename instance method to get the actual filename (if any) of source associated with a given page instance.
122 123 124 125 126 127 128 129 130 131 132 |
# File 'lib/rote/page.rb', line 122 def page_ruby_filename(template_fn) # :nodoc: fn = nil if (template_fn) if (fn = template_fn.dup) =~ FILE_EXT fn[FILE_EXT] = '.rb' else fn << '.rb' unless fn.empty? end end fn end |
.resolve_common_rubys(dir, arr = []) ⇒ Object
Find all COMMON.rb files from given dir up to FS root.
135 136 137 138 139 140 141 142 |
# File 'lib/rote/page.rb', line 135 def resolve_common_rubys(dir, arr = []) # :nodoc: # defer to parent dir first parent = File.(File.join(dir, '..')) resolve_common_rubys(parent,arr) unless parent == dir # at root fn = File.join(dir,'COMMON.rb') arr << fn if (File.exists?(fn) && File.readable?(fn)) arr end |
Instance Method Details
#base_layout_filename ⇒ Object Also known as: layout_filename
Returns the full filename of the first queued layout. This is the innermost layout, usually specified by the page itself.
236 237 238 |
# File 'lib/rote/page.rb', line 236 def base_layout_filename layout_fn(layout_name) end |
#base_layout_name ⇒ Object Also known as: layout_name
The filename of the innermost layout, usually specified by the page itself, relative to the layout_path. This method should not be used from COMMON.rb since its behaviour is undefined until all page code is evaluated and the final base_layout is known.
165 |
# File 'lib/rote/page.rb', line 165 def base_layout_name; layout_names.first; end |
#layout(basename) ⇒ Object
Sets the page’s base-layout as specified, or applies nested layout if called during a layout render pass. The specified basename should be the name of the layout file relative to the layout_path. If the layout has the same extension as the page source template, it may be omitted.
The layout is not read by this method. It, and it’s source, are loaded only at rendering time. This prevents multiple calls by various scoped COMMON code, for example, from making a mess in the Page binding.
This can only be called before the first call to render returns it’s result. After that the Page instance is frozen.
296 297 298 299 300 |
# File 'lib/rote/page.rb', line 296 def layout(basename) if basename self.layout_names << "#{basename}#{@layout_defext if File.extname(basename).empty?}" end end |
#page_filter(filter = nil, &block) ⇒ Object
Append filter to this page’s page-filter chain, or create a new Rote::Filters::TextFilter with the supplied block. This method should be preferred over direct manipulation of the filters array if you are simply building a chain.
252 253 254 255 256 257 258 259 260 |
# File 'lib/rote/page.rb', line 252 def page_filter(filter = nil, &block) if filter page_filters << filter else if block page_filters << Filters::TextFilter.new(&block) end end end |
#post_filter(filter = nil, &block) ⇒ Object
Append filter to this page’s post-filter chain. Behaviour is much the same as append_page_filter.
264 265 266 267 268 269 270 271 272 |
# File 'lib/rote/page.rb', line 264 def post_filter(filter = nil, &block) if filter post_filters << filter else if block post_filters << Filters::TextFilter.new(&block) end end end |
#render ⇒ Object Also known as: to_s
Render this page’s textile and ERB, and apply layout. This is only done once - after that, it’s cached for next time. You can also circumvent rendering by setting @result yourself in your page’s ruby.
277 278 279 |
# File 'lib/rote/page.rb', line 277 def render @result or do_render! # sets up result for next time... end |
#ruby_filename ⇒ Object
Returns the full filename of this Page’s ruby source. If no source is found for this page (not including common source) this returns nil.
243 244 245 246 |
# File 'lib/rote/page.rb', line 243 def ruby_filename fn = Page::page_ruby_filename(template_filename) File.exists?(fn) ? fn : nil end |
#template_filename ⇒ Object
Returns the full filename of this Page’s template. This is obtained by joining the base path with template name.
230 231 232 |
# File 'lib/rote/page.rb', line 230 def template_filename File.join(base_path,template_name) if template_name end |