class Amber::StaticPage
Constants
- FORBIDDEN_PAGE_CHARS_RE
INSTANCE METHODS
- LOCALES_GLOB
- LOCALES_RE
e.g. en, de, pt
- LOCALE_FILE_MATCH_GLOB
- LOCALE_FILE_MATCH_RE
e.g. en.haml or es.md or index.pt.text
- PAGE_SUFFIXES_GLOB
- PAGE_SUFFIXES_RE
e.g. haml, md, text
- SIMPLE_FILE_MATCH_RE
- SIMPLE_VAR_MATCH_RE
- VAR_FILE_MATCH_GLOB
- VAR_FILE_MATCH_RE
- VAR_SUFFIXES_GLOB
- VAR_SUFFIXES_RE
e.g. json, yaml
Attributes
Public Class Methods
# File lib/amber/static_page.rb, line 30 def initialize(parent, name, file_path=nil, path_prefix="/") @valid = true @children = PageArray.new # array of StaticPages @nav_title = {} # key is locale @title = {} # key is locale @name, @suffix = parse_source_file_name(name) # set @parent & @path if parent @parent = parent @config = @parent.config @parent.add_child(self) @path = [@parent.path, @name].flatten.compact else @path = (path_prefix||"").split('/') end if @name =~ FORBIDDEN_PAGE_CHARS_RE Amber.logger.error "Illegal page name #{@name} at path /#{self.path.join('/')} -- must not have symbols, uppercase, or periods." @valid = false end # set the @file_path if file_path @file_path = file_path elsif @parent && @parent.file_path @file_path = File.join(@parent.file_path, @name) else raise 'file path must be specified or in parent' end @simple_page = !File.directory?(@file_path) # eval the property headers, if any @props = load_properties() end
Recursively decends the directory tree, yielding pages and directories it encounters.
yield has two arguments:
(1) StaticPage
instance, or nil. (2) Directory path string if #1 is nil. Path is relative.
Directory paths are dirs in the tree that don't contain any pages.
# File lib/amber/static_page/filesystem.rb, line 26 def self.scan_directory_tree(parent_page, absolute_dir_path, relative_dir_path, &block) Dir.foreach(absolute_dir_path).each do |child_path| next if child_path =~ /^\./ abs_path = File.join(absolute_dir_path, child_path) rel_path = File.join(relative_dir_path, child_path) if parent_page && is_directory_page?(abs_path) child_page = StaticPage.new(parent_page, child_path) if child_page.valid? yield child_page, nil scan_directory_tree(child_page, abs_path, rel_path, &block) end elsif parent_page && is_simple_page?(abs_path) child_page = StaticPage.new(parent_page, child_path) if child_page.valid? yield child_page, nil end elsif File.directory?(abs_path) yield nil, rel_path scan_directory_tree(nil, abs_path, rel_path, &block) end end end
Private Class Methods
# File lib/amber/static_page/filesystem.rb, line 141 def self.is_directory_page?(absolute_path) if File.directory?(absolute_path) Dir.glob(absolute_path + '/' + LOCALE_FILE_MATCH_GLOB).each do |file| return true end end return false end
returns true if the name of a file could be a 'simple' static page that is not a directory.
rules:
-
we include files that end in appriopriate suffixes
-
we exclude file names that are locales.
-
we exclude partials
# File lib/amber/static_page/filesystem.rb, line 136 def self.is_simple_page?(absolute_path) name = File.basename(absolute_path) name =~ /\.#{PAGE_SUFFIXES_RE}$/ && name !~ LOCALE_FILE_MATCH_RE && name !~ /^_/ end
Public Instance Methods
# File lib/amber/static_page.rb, line 68 def add_child(page) @children << page end
returns an array of normalized aliases based on the :alias property defined for a page.
aliases are defined with a leading slash for absolute paths, or without a slash for relative paths. this method converts this to a format that amber uses (all absolute, with no leading slash, as an array instead of a string).
# File lib/amber/static_page.rb, line 144 def aliases(locale=I18n.default_locale) @aliases ||= begin aliases_hash = Hash.new([]) @props.locales.each do |l| aliases = @props.prop_without_inheritance(l, :alias) aliases_hash[l] = begin if aliases.nil? [] else [aliases].flatten.collect {|alias_path| if alias_path =~ /^\// alias_path.sub(/^\//, '').split('/') elsif @parent @parent.path + [alias_path] else alias_path.split('/') end } end end end aliases_hash end @aliases[locale] end
# File lib/amber/static_page.rb, line 72 def all_children PageArray.new(child_tree.flatten.compact) end
returns a child matching name
, if any.
# File lib/amber/static_page.rb, line 107 def child(name) children.detect {|child| child.name == name} end
full filesystem path name of the source content file e.g. /home/user/mysite/pages/about-us/contact/en.md
# File lib/amber/static_page/filesystem.rb, line 87 def content_file(locale) content_files[locale] || content_files[I18n.default_locale] || content_files.values.first end
# File lib/amber/static_page/filesystem.rb, line 91 def content_file_exists?(locale) !!content_files[locale] end
full filesystem path name of the destination rendered file e.g. /home/user/mysite/public/about-us/contact/index.en.html
# File lib/amber/static_page/filesystem.rb, line 99 def destination_file(dest_dir, locale) File.join(dest_dir, *@path, "index.#{locale}.html") end
returns title iff explicitly set.
# File lib/amber/static_page.rb, line 95 def explicit_title(locale) @props.prop_without_inheritance(locale, :title) || @props.prop_without_inheritance(I18n.default_locale, :title) end
# File lib/amber/static_page.rb, line 100 def id self.name end
# File lib/amber/static_page.rb, line 76 def inspect "<'#{@path.join('/')}' #{children.inspect}>" end
creates symlinks for aliases to this page. called by Page#render_to_file and Site#render_short_path_aliases
# File lib/amber/static_page/render.rb, line 48 def link_page_aliases(dest_dir, alias_paths, locale=I18n.default_locale) alias_paths.each do |alias_path| alias_file_path = File.join(dest_dir, alias_path) #if locale != I18n.default_locale # alias_file_path += ".#{locale}" #end alias_file_path = Pathname.new(alias_file_path) page_file_path = Pathname.new(File.join(dest_dir, *@path)) symlink(page_file_path, alias_file_path) end end
Returns array of locale symbols for all locales with properties set Note: there might be a content for a locale that does not show up in this array, if the content file does not set any properties.
# File lib/amber/static_page.rb, line 128 def locales @props.locales end
# File lib/amber/static_page.rb, line 132 def path_str self.path.join('/') end
# File lib/amber/static_page.rb, line 111 def prop(*args) @props.prop(*args) end
render a static copy
dest_dir - e.g. amber_root/public/
# File lib/amber/static_page/render.rb, line 34 def render_to_file(dest_dir, options={}) render_content_files(dest_dir, options) render_assets(dest_dir) @props.locales.each do |locale| if aliases(locale).any? link_page_aliases(dest_dir, aliases(locale), locale) end end end
# File lib/amber/static_page/filesystem.rb, line 49 def scan_directory_tree(&block) StaticPage.scan_directory_tree(self, self.file_path, File.join(self.path), &block) end
# File lib/amber/static_page.rb, line 80 def title(locale=I18n.locale) @title[locale] ||= begin @props.prop_with_fallback(locale, [:title, :nav_title]) || @name end end
# File lib/amber/static_page.rb, line 119 def var(name, locale=I18n.locale) (vars[locale] || vars[I18n.default_locale] || {})[name.to_s] end
# File lib/amber/static_page.rb, line 115 def vars @vars ||= load_variables end
Protected Instance Methods
# File lib/amber/static_page.rb, line 172 def child_tree [self, children.collect{|child| child.child_tree}] end
Private Instance Methods
returns an array of files in the folder that corresponds to this page that are not other pages. in other words, the assets in this folder
file paths are relative to @file_path
# File lib/amber/static_page/filesystem.rb, line 207 def asset_files if @simple_page [] else Dir.foreach(@file_path).collect { |file| is_asset = \ file && file !~ /\.#{PAGE_SUFFIXES_RE}$/ && file !~ /^#{VAR_FILE_MATCH_RE}$/ && !File.directory?(File.join(@file_path, file)) file if is_asset }.compact end end
# File lib/amber/static_page/filesystem.rb, line 263 def cleanup_properties(props, locale) if props.prop(locale, :alias) props.set_prop(locale, :alias, [props.prop(locale, :alias)].flatten) end end
returns the files that compose the content for this page, a different file for each locale (or no locale)
returns a hash like so:
{ :en => '/path/to/page/en.haml', :es => '/path/to/page/index.es.md' }
Or this, if page is simple:
{
:en => '/path/to/page.haml'
}
# File lib/amber/static_page/filesystem.rb, line 181 def content_files @content_files ||= begin if @simple_page directory = File.dirname(@file_path) regexp = SIMPLE_FILE_MATCH_RE.call(@name) else directory = @file_path regexp = LOCALE_FILE_MATCH_RE end hsh = {} Dir.foreach(directory) do |file| if file && match = regexp.match(file) locale = match['locale'] || I18n.default_locale hsh[locale.to_sym] = File.join(directory, file) end end hsh end end
scans the source content files for property headers in the form:
@variable = 'x' - @variable = 'x'
(with or without leading hypen works)
This text is extracted and evaluated as ruby to set properties.
The first paragraph is loaded into the property “excerpt”.
# File lib/amber/static_page/filesystem.rb, line 242 def load_properties props = PageProperties.new(self) content_files.each do |locale, content_file| if type_from_path(content_file) == :haml props.eval(File.read(content_file, :encoding => 'UTF-8'), locale) else headers, excerpt = parse_headers(content_file) props.eval(headers, locale) if !excerpt.empty? props.set_prop(locale, "excerpt", excerpt) end props.set_prop(locale, "content_type", type_from_path(content_file)) end cleanup_properties(props, locale) end unless props.prop_without_inheritance(I18n.default_locale, :name) props.set_prop(I18n.default_locale, :name, self.name) end return props end
# File lib/amber/static_page/filesystem.rb, line 349 def load_variables vars = {} variable_files.each do |locale, var_file| begin if var_file =~ /\.ya?ml$/ vars[locale] = YAML.load_file(var_file) elsif var_file =~ /\.json$/ vars[locale] = JSON.parse(File.read(var_file)) end rescue StandardError => exc Amber.logger.error('ERROR: could not load file #{var_file}: ' + exc.to_s) end end return vars end
parses a content_file
's property headers and tries to extract the first paragraph.
# File lib/amber/static_page/filesystem.rb, line 273 def parse_headers(content_file) headers = [] para1 = [] para2 = [] file_type = type_from_path(content_file) File.open(content_file, :encoding => 'UTF-8') do |f| while (line = f.gets) =~ /^(- |)@\w/ if line !~ /^-/ line = '- ' + line end headers << line end # eat empty lines while line = f.gets break unless line =~ /^\s*$/ end # grab first two paragraphs para1 << line while line = f.gets break if line =~ /^\s*$/ para1 << line end while line = f.gets break if line =~ /^\s*$/ para2 << line end end headers = headers.join para1 = para1.join para2 = para2.join excerpt = "" # pick the first non-heading paragraph. # this is stupid, and chokes on nested headings. # but is also cheap and fast :) if file_type == :textile if para1 =~ /^h[1-5]\. / excerpt = para2 else excerpt = para1 end elsif file_type == :markdown if para1 =~ /^#+ / || para1 =~ /^(===+|---+)\s*$/m excerpt = para2 else excerpt = para1 end end return [headers, excerpt] end
returns [name, suffix] called on new page initialization
# File lib/amber/static_page/filesystem.rb, line 154 def parse_source_file_name(name) matches = name.match(/^(?<name>.*?)(\.#{LOCALES_RE})?(\.#{PAGE_SUFFIXES_RE})$/) if matches [matches['name'], matches['suffix']] else [name, nil] end end
# File lib/amber/static_page/render.rb, line 109 def realpath(pathname) dir = pathname.dirname if dir.directory? || dir.symlink? dir.realpath + pathname.basename else pathname end end
called only by render_to_file
# File lib/amber/static_page/render.rb, line 82 def render_assets(dest_dir) asset_files.each do |asset_file| src_file = File.join(@file_path, asset_file) dst_file = File.join(dest_dir, *@path, asset_file) Render::Asset.render(src_file, dst_file) end end
called only by render_to_file
# File lib/amber/static_page/render.rb, line 119 def render_content_files(dest_dir, options) view = Render::View.new(self, @config) @config.locales.each do |file_locale| content_file = content_file(file_locale) next unless content_file dest = destination_file(dest_dir, file_locale) unless Dir.exist?(File.dirname(dest)) FileUtils.mkdir_p(File.dirname(dest)) end if options[:force] || !File.exist?(dest) || File.mtime(content_file) > File.mtime(dest) File.open(dest, 'w') do |f| layout = @props.layout || 'default' f.write view.render({page: self, layout: layout}, {locale: file_locale}) end end end end
create a symlink. arguments must be of type Pathname.
# File lib/amber/static_page/render.rb, line 93 def symlink(from_path, to_path) to_path = realpath(to_path) target = from_path.relative_path_from(to_path).to_s.sub(/^\.\.\//, '') if !to_path.dirname.directory? Amber.logger.warn { "On page `#{@file_path}`, the parent directories for alias name `#{to_path}` don't exist. Skipping alias." } return end if to_path.exist? && to_path.symlink? File.unlink(to_path) end if !to_path.exist? Amber.logger.debug { "Symlink #{to_path} => #{target}" } FileUtils.ln_s(target, to_path) end end
# File lib/amber/static_page/filesystem.rb, line 365 def type_from_path(path) case File.extname(path) when ".text", ".textile" :textile when ".md", ".markdown" :markdown when ".haml" :haml when ".html" :html when ".erb" :erb else :unknown end end
VARIABLES Variables are associated with a page, but unlike properties they are not inheritable. Variables are defined in a separate file.
# File lib/amber/static_page/filesystem.rb, line 331 def variable_files if @simple_page directory = File.dirname(@file_path) regexp = SIMPLE_VAR_MATCH_RE.call(@name) else directory = @file_path regexp = VAR_FILE_MATCH_RE end hsh = {} Dir.foreach(directory) do |file| if file && match = regexp.match(file) locale = match['locale'] || I18n.default_locale hsh[locale.to_sym] = File.join(directory, file) end end hsh end