summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorelijah <elijah@riseup.net>2013-02-20 00:39:58 -0800
committerelijah <elijah@riseup.net>2013-02-20 00:39:58 -0800
commit10038e58efe3aa3c1725e2b5b0a0b7b2ce060df7 (patch)
tree53413a9cd679c6d22b405c1035f9c41546677c9a /lib
parent9c4e765c8fe972a4a9f49c3de7991b3925ddb97c (diff)
added support for pandoc and page properties in static markup.
Diffstat (limited to 'lib')
-rw-r--r--lib/menu.rb4
-rw-r--r--lib/property_set.rb23
-rw-r--r--lib/static_page.rb274
-rw-r--r--lib/template.html9
4 files changed, 279 insertions, 31 deletions
diff --git a/lib/menu.rb b/lib/menu.rb
index 4da8441..b586151 100644
--- a/lib/menu.rb
+++ b/lib/menu.rb
@@ -50,6 +50,10 @@ class Menu
end
end
+ def path_str
+ @path_str ||= path.join('/')
+ end
+
def each(&block)
children.each(&block)
end
diff --git a/lib/property_set.rb b/lib/property_set.rb
index 64982c9..05aa46a 100644
--- a/lib/property_set.rb
+++ b/lib/property_set.rb
@@ -11,6 +11,7 @@
# getting the property
#
# page.props.title
+# page.props.locale('en').title
#
require 'i18n'
@@ -39,15 +40,17 @@ class PropertySet
def textile(str)
RedCloth.new(str).to_html
end
- def get(var_name)
+ def get(var_name, inheritance=true)
value = instance_variable_get("@#{var_name}")
if value.nil?
if @_locale != DEFAULT_LOCALE
# try value from default locale
@_ps.get_var(var_name)
- else
+ elsif inheritance
# try inherited value
@_ps.get_inherited_var(var_name, @_locale)
+ else
+ nil
end
else
value
@@ -66,7 +69,9 @@ class PropertySet
#
# evaluate the template_string, and load the variables defined into an AttrObject.
#
- def eval(locale, template_string)
+ def eval(template_string, locale)
+ locale ||= DEFAULT_LOCALE
+
# render to the template to get the instance variables
attrs = AttrObject.new(self, locale)
begin
@@ -136,6 +141,18 @@ class PropertySet
end
#
+ # like get_var, but forbits inheritance
+ #
+ def get_var_without_inheritance(var_name, locale=I18n.locale)
+ attrs = locale(locale)
+ if attrs
+ attrs.get(var_name, false)
+ else
+ nil
+ end
+ end
+
+ #
# tries to get the value of an inherited variable
#
def get_inherited_var(var_name, locale=I18n.locale)
diff --git a/lib/static_page.rb b/lib/static_page.rb
index eb32319..b4bc40d 100644
--- a/lib/static_page.rb
+++ b/lib/static_page.rb
@@ -1,4 +1,4 @@
-#
+ #
# class StaticPage
#
# represents a static website page.
@@ -8,9 +8,11 @@
require 'property_set'
require 'i18n'
require 'pathname'
+require 'RedCloth'
+require 'pandoc-ruby'
class StaticPage
- attr_accessor :path, :children, :name, :file_path, :props, :parent, :mount_point
+ attr_accessor :path, :children, :name, :file_path, :props, :parent, :mount_point, :locales
##
## CLASS METHODS
@@ -31,7 +33,8 @@ class StaticPage
end
#
- # loads a directory, creating StaticPages from the directory structure
+ # loads a directory, creating StaticPages from the directory structure,
+ # yielding each StaticPage as it is created.
#
def scan(&block)
Dir.chdir(file_path) do
@@ -41,7 +44,7 @@ class StaticPage
yield child
child.scan(&block)
elsif is_simple_page?(child_name)
- child = StaticPage.new(self, file_without_suffix(child_name))
+ child = StaticPage.new(self, child_name)
yield child
end
end
@@ -54,7 +57,17 @@ class StaticPage
def initialize(parent, name, file_path=nil)
@children = []
- @name = name
+
+ # set the @name and @suffix
+ @suffix = File.extname(name)
+ if @suffix.chars.any?
+ @name = file_without_suffix(name)
+ else
+ @name = name
+ @suffix = nil
+ end
+
+ # set @parent & @path
if parent
@parent = parent
@mount_point = @parent.mount_point
@@ -63,6 +76,8 @@ class StaticPage
else
@path = []
end
+
+ # set the @file_path
if file_path
@file_path = file_path
elsif @parent && @parent.file_path
@@ -70,8 +85,13 @@ class StaticPage
else
raise 'file path must be specified or in parent'
end
+
+ # discover supported locales
@simple_page = !File.directory?(@file_path)
- @props = load_properties
+ #@locales = find_locales()
+
+ # eval the property headers, if any
+ @props = load_properties()
end
def add_child(page)
@@ -100,15 +120,69 @@ class StaticPage
end
end
+ #
+ # e.g. pages/about-us/contact/en
+ #
+ def absolute_template_path(locale=I18n.locale)
+ if @simple_page
+ "#{@mount_point.directory}/#{@path.join('/')}"
+ else
+ "#{@mount_point.directory}/#{@path.join('/')}/#{locale}"
+ end
+ end
+
+ #
+ # e.g. pages/about-us/contact/en.haml
+ #
+ #def source_path(locale=I18n.locale)
+ # if @simple_page
+ #
+ # else
+ #
+ # end
+ #end
+
def inspect
"<'#{@path.join('/')}' #{children.inspect}>"
end
- def title
+ #
+ # title is tricky
+ #
+ # * for nav_title, default to title, then try to inherit.
+ # * for title, default to nav_title, then try to inherit.
+ #
+ def title(locale=I18n.locale)
+ title = props.get_var_without_inheritance(:title, locale)
+ title ||= props.get_var_without_inheritance(:nav_title, locale)
+ title ||= props.get_var(:title, locale)
+ title ||= props.get_var(:nav_title, locale)
+ title ||= I18n.t('pages.'+@name, :raise => false, :default => "Untitled", :locale => locale)
+ return title
+ end
+
+ def nav_title(locale=I18n.locale)
+ return_value = I18n.t('pages.'+@name, :raise => false, :default => "Untitled", :locale => locale)
+ if return_value == "Untitled"
+ property_title = props.get_var_without_inheritance(:nav_title, locale)
+ property_title ||= props.get_var_without_inheritance(:title, locale)
+ property_title ||= props.get_var(:nav_title, locale)
+ property_title ||= props.get_var(:title, locale)
+ return_value = property_title || return_value
+ end
+ return_value
+ end
+
+ def render_to_string(renderer)
begin
- I18n.t!('pages.' + @name, :raise => true)
- rescue I18n::MissingTranslationData
- props.title
+ render_locale(renderer, I18n.locale)
+ rescue ActionView::MissingTemplate, MissingTemplate => exc
+ begin
+ render_locale(renderer, DEFAULT_LOCALE)
+ rescue
+ Rails.logger.error "ERROR: could not file template path #{self.template_path}"
+ raise exc
+ end
end
end
@@ -124,30 +198,49 @@ class StaticPage
private
- #def self.relative_to_rails_view_root(absolute_path)
- # if Rails.root
- # absolute = Pathname.new(absolute_path)
- # rails_view_root = Pathname.new(Rails.root + 'app/views')
- # absolute.relative_path_from(rails_view_root).to_s
- # end
- #end
+ ##
+ ## PROPERTIES
+ ##
+ #
+ # 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.
+ #
def load_properties
props = PropertySet.new(self)
- Dir.glob(file_path + '/*.haml') do |content_file_path|
- locale = File.basename(content_file_path).sub(File.extname(content_file_path),'')
- #variable_header = ""
- #File.open(content_file_path) do |f|
- # while (line = f.gets) =~ /^- @/
- # variable_header << line
- # end
- #end
- props.eval(locale, File.read(content_file_path))
+ content_files.each do |content_file, locale|
+ if File.extname(content_file) == '.haml'
+ props.eval(File.read(content_file), locale)
+ else
+ headers = []
+ File.open(content_file) do |f|
+ while (line = f.gets) =~ /^(- |)@\w/
+ if line !~ /^-/
+ line = '- ' + line
+ end
+ headers << line
+ end
+ end
+ props.eval(headers.join("\n"), locale)
+ end
end
return props
end
- SUFFIXES = '(haml|md)'
+ ##
+ ## CONTENT FILES
+ ##
+
+ SUFFIXES = '(haml|md|markdown|txt|textile|rst|latex|pandoc|html)'
+
+ # e.g. en.haml or es.md
+ LOCALE_FILE_MATCH = /^(#{AVAILABLE_LANGUAGES.join('|')})\.#{SUFFIXES}$/
#
# returns true if the name of a file could be a 'simple' static page
@@ -158,12 +251,137 @@ class StaticPage
# * we exclude file names that are locales.
#
def is_simple_page?(name)
- name =~ /\.#{SUFFIXES}$/ && name !~ /^(#{AVAILABLE_LANGUAGES.join('|')})\.#{SUFFIXES}$/
+ name =~ /\.#{SUFFIXES}$/ && name !~ LOCALE_FILE_MATCH
end
def file_without_suffix(name)
name.sub(/^(.*?)\.#{SUFFIXES}$/, "\\1")
end
+
+ #
+ # returns an array like so:
+ #
+ # [
+ # ['/path/to/page/en.haml', 'en']
+ # ['/path/to/page/es.haml', 'es']
+ # ]
+ #
+ # Or this, if page is simple:
+ #
+ # [
+ # ['/path/to/page.haml', nil]
+ # ]
+ #
+ #
+ def content_files
+ if @simple_page
+ [[@file_path + @suffix,nil]]
+ elsif File.directory?(@file_path)
+ Dir.foreach(@file_path).collect { |file|
+ if file && file =~ LOCALE_FILE_MATCH
+ [File.join(@file_path, file), $1]
+ end
+ }.compact
+ end
+ end
+
+ #def self.relative_to_rails_view_root(absolute_path)
+ # if Rails.root
+ # absolute = Pathname.new(absolute_path)
+ # rails_view_root = Pathname.new(Rails.root + 'app/views')
+ # absolute.relative_path_from(rails_view_root).to_s
+ # end
+ #end
+
+ ##
+ ## RENDERING
+ ##
+
+ PROPERTY_HEADER = /^\s*(^(|- )@\w[^\n]*?\n)*/m
+
+ class MissingTemplate < StandardError
+ end
+
+ def render_locale(renderer, locale)
+ if is_haml_template?(locale)
+ renderer.render_to_string(:template => self.template_path(locale), :layout => false).html_safe
+ else
+ render_static_locale(locale).html_safe
+ end
+ end
+
+ def render_static_locale(locale)
+ content_files.each do |content_file, file_locale|
+ if file_locale.nil? || locale == file_locale
+ return render_content_file(content_file, locale)
+ end
+ end
+ raise MissingTemplate.new(template_path(locale))
+ end
+
+ #
+ # todo: maybe use RDiscount for markdown instead?
+ #
+ def render_content_file(content_file, locale)
+ content = File.read(content_file).sub(PROPERTY_HEADER, '')
+ suffix = File.extname(content_file)
+ if PANDOC_FORMATS[suffix]
+ render_pandoc(content, suffix, locale)
+ elsif REDCLOTH_FORMATS[suffix]
+ render_redcloth(content, suffix, locale)
+ else
+ "sorry, i don't understand how to render #{suffix}"
+ end
+ end
+
+ def is_haml_template?(locale)
+ @suffix == '.haml' || File.exists?(self.absolute_template_path(locale) + '.haml')
+ end
+
+ PANDOC_FORMATS = {
+ '.md' => :markdown,
+ '.markdown' => :markdown,
+ #'.txt' => :textile,
+ #'.textile' => :textile,
+ '.rst' => :rst,
+ '.latex' => :latex,
+ '.pandoc' => :pandoc,
+ }
+
+ def render_pandoc(string, suffix, locale)
+ args = [string, {:from => PANDOC_FORMATS[suffix], :to => :html5}, "smart"]
+ if props.locale(locale).toc != false
+ args << "table_of_contents"
+ args << {"template" => "'#{File.dirname(__FILE__) + '/template.html'}'"}
+ end
+ unless (title = explicit_title(locale)).nil?
+ args << {"variable" => "title:'#{title}'"}
+ end
+ renderer = PandocRuby.new(*args)
+ renderer.convert
+ end
+
+ #
+ # pandoc can do textile, but it does it HORRIBLY
+ #
+ REDCLOTH_FORMATS = {
+ '.txt' => :textile,
+ '.textile' => :textile,
+ }
+
+ def render_redcloth(string, suffix, locale)
+ unless (title = explicit_title(locale)).nil?
+ string = "h1. #{title}\n\n" + string
+ end
+ RedCloth.new(string).to_html
+ end
+
+ #
+ # returns title iff explicitly set.
+ #
+ def explicit_title(locale)
+ props.get_var_without_inheritance(:title, locale)
+ end
end
diff --git a/lib/template.html b/lib/template.html
new file mode 100644
index 0000000..379a602
--- /dev/null
+++ b/lib/template.html
@@ -0,0 +1,9 @@
+$if(title)$
+<h1 class="first">$title$</h1>
+$endif$
+$if(toc)$
+<div id="$idprefix$TOC">
+$toc$
+</div>
+$endif$
+$body$ \ No newline at end of file