summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorelijah <elijah@riseup.net>2013-02-12 21:33:39 -0800
committerelijah <elijah@riseup.net>2013-02-12 21:33:39 -0800
commit71ec0edea3f87fb69222dbb6fe025c2211402ca2 (patch)
tree5490323e8686df14165ccf5f37fa2d691fc99b88 /lib
parent00c785b728c5d97335b87e3eb7d10b9ad0c46d35 (diff)
added capacity for pulling static pages from multiple directory source trees.
Diffstat (limited to 'lib')
-rw-r--r--lib/config.rb9
-rw-r--r--lib/menu.rb133
-rw-r--r--lib/site.rb93
-rw-r--r--lib/site_configuration.rb50
-rw-r--r--lib/site_mount_point.rb59
-rw-r--r--lib/static_page.rb149
-rw-r--r--lib/static_page_array.rb32
7 files changed, 401 insertions, 124 deletions
diff --git a/lib/config.rb b/lib/config.rb
deleted file mode 100644
index a579631..0000000
--- a/lib/config.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-#
-# Here go any application configuration
-#
-
-SITE_TITLE = "LEAP"
-
-PAGE_DIRECTORY = File.expand_path('../../app/views/pages', __FILE__)
-
-PAGINATION_SIZE = 20
diff --git a/lib/menu.rb b/lib/menu.rb
index 71690a4..c156e07 100644
--- a/lib/menu.rb
+++ b/lib/menu.rb
@@ -1,47 +1,45 @@
-class Menu < Array
- attr_accessor :parent
+#
+# A navigation menu class
+#
- #
- # class methods
- #
+class Menu
+ attr_accessor :parent
+ attr_accessor :children
+ attr_accessor :name
#
- # load the menu.txt file, recursively, and build the in-memory menu array
+ # load the menu.txt file and build the in-memory menu array
#
- def self.load(menu_file_path)
- @menu = Menu.new
+ def load(menu_file_path)
File.open(menu_file_path) do |file|
- append_menu_item(@menu, file)
+ parse_menu(file)
end
end
- def self.menu
- @menu || Menu.new
+ def initialize(name, parent=nil)
+ self.name = name
+ self.parent = parent
+ self.children = []
end
- def self.create(elements=[], parent=nil)
- menu = Menu[elements]
- menu.parent = parent
- return menu
- end
+ ##
+ ## public methods
+ ##
#
- # public methods
+ # returns the menu under the item that matches item_name.
#
-
def submenu(item_name=nil)
if item_name
- submenu = detect {|item| item[0] == item_name} || Menu.new
+ self.children.detect {|child| child.name == item_name} || Menu.new
else
- submenu = self
+ self.children
end
- return submenu[1..-1] # strip of first element
- end
-
- def name
- first
end
+ #
+ # returns path from root to this leaf as an array
+ #
def path
@path ||= begin
if parent == nil
@@ -52,27 +50,86 @@ class Menu < Array
end
end
+ def each(&block)
+ children.each(&block)
+ end
+
+ def size
+ children.size
+ end
+
+ #
+ # returns true if menu's path starts with +path_prefix+
+ #
+ def path_starts_with?(path_prefix)
+ array_starts_with?(path, path_prefix)
+ end
+
+ def path_prefix_of?(full_path)
+ array_starts_with?(full_path, path)
+ end
+
+ #
+ # returns true if this menu item is the terminus menu item for path.
+ # (meaning that there are no children that match more path segments)
+ #
+ def leaf_for_path?(path)
+ return false unless path_prefix_of?(path)
+ next_path_segment = (path - self.path).first
+ return false if next_path_segment.nil?
+ return !children.detect {|i| i.name == next_path_segment}
+ end
+
+ def inspect(indent=0)
+ lines = []
+ lines << ' '*indent + '- ' + self.name
+ self.children.each do |child|
+ lines << child.inspect(indent+1)
+ end
+ lines.join("\n")
+ end
+
+ #
+ # private & protected methods
+ #
+
+ protected
+
+ def add_child(name)
+ self.children << Menu.new(name, self)
+ end
+
private
- def self.append_menu_item(menu, file)
- begin
+ def array_starts_with?(big_array, small_array)
+ small_array.length.times do |i|
+ if small_array[i] != big_array[i]
+ return false
+ end
+ end
+ return true
+ end
+
+
+ def parse_menu(file)
+ while true
item = file.readline
- rescue EOFError
- # do nothing
- else
- if item !~ /^\s*#/
+ if item.strip.chars.any? && item !~ /^\s*#/
depth = item.scan(" ").size
- sub_menu = sub_menu_for_depth(menu, depth)
- sub_menu << Menu.create(item.strip, sub_menu)
+ last_menu_at_depth(depth).add_child(item.strip)
end
- append_menu_item(menu, file)
end
+ rescue EOFError
+ # done loading
end
- def self.sub_menu_for_depth(menu, depth)
- sub_menu = menu
- depth.times { sub_menu = sub_menu[-1] }
- sub_menu
+ #
+ # returns the last list of children at the specified depth
+ #
+ def last_menu_at_depth(depth)
+ menu = self
+ depth.times { menu = menu.children.last }
+ menu
end
end \ No newline at end of file
diff --git a/lib/site.rb b/lib/site.rb
new file mode 100644
index 0000000..a3e8ac3
--- /dev/null
+++ b/lib/site.rb
@@ -0,0 +1,93 @@
+
+class Site
+ extend Forwardable
+
+ attr_accessor :pages
+ attr_accessor :page_list
+ attr_accessor :root
+ attr_accessor :menu
+
+ def_delegators :@config, :title, :pagination_size
+
+ def initialize
+ @config = SiteConfiguration.load("#{Rails.root}/site.rb")
+ end
+
+ def load_pages
+ @root = nil
+ @pages = {}
+ @page_list = StaticPageArray.new
+ @menu = Menu.new('root')
+ @config.mount_points.each do |mp|
+ add_mount_point(mp)
+ mp.reset_timestamp
+ end
+ end
+
+ def reload_pages_if_needed
+ if @pages.nil? || @config.pages_changed?
+ puts "Reloading pages ................."
+ load_pages
+ end
+ end
+
+ #def menu
+ # @menu ||= Menu.new
+ #end
+
+ def find_pages(filter)
+ StaticPage.find(self, filter)
+ end
+
+ def find_page(filter)
+ find_pages(filter)
+ end
+
+ def all_pages
+ @page_list
+ end
+
+ private
+
+ def add_mount_point(mp)
+ # create base_page
+ base_page = begin
+ if mp.path == '/'
+ @root = StaticPage.new(nil, 'root', mp.directory)
+ else
+ name = File.basename(mp.path)
+ page = StaticPage.new(find_parent(mp.path), name, File.join(mp.directory, name))
+ add_page(page)
+ page
+ end
+ end
+ base_page.mount_point = mp
+
+ # load menu and locals
+ menu.load(mp.menu_file) if mp.menu_file
+ I18n.load_path += Dir[File.join(mp.locales_dir, '/*.{rb,yml,yaml}')] if mp.locales_dir
+
+ # add the full directory tree
+ base_page.scan do |page|
+ add_page(page)
+ end
+ end
+
+ def add_page(page)
+ @pages[page.name] = page
+ @pages[page.path.join('/')] = page
+ @page_list << page
+ end
+
+ def find_parent(path)
+ so_far = []
+ path.split('/').compact.each do |path_segment|
+ so_far << path_segment
+ if page = @pages[so_far.join('/')]
+ return page
+ end
+ end
+ return @root
+ end
+
+end
diff --git a/lib/site_configuration.rb b/lib/site_configuration.rb
new file mode 100644
index 0000000..62ad854
--- /dev/null
+++ b/lib/site_configuration.rb
@@ -0,0 +1,50 @@
+#
+# A class for a site's configuration.
+# Site configuration file is eval'ed in the context of an instance of SiteConfiguration
+#
+
+require 'pathname'
+
+class SiteConfiguration
+
+ attr_accessor :title
+ attr_accessor :pagination_size
+ attr_accessor :mount_points
+ attr_reader :file_path
+
+ ##
+ ## CLASS METHODS
+ ##
+
+ def self.load(config_file)
+ SiteConfiguration.new(config_file)
+ end
+
+ ##
+ ## INSTANCE METHODS
+ ##
+
+ #
+ # accepts a file_path to a configuration file.
+ #
+ def initialize(file_path)
+ @file_path = file_path
+ @site_title = "untitled"
+ @pagination_size = 20
+ @mount_points = []
+ self.eval
+ end
+
+ def pages(directory_source, options={})
+ @mount_points << SiteMountPoint.new(self, directory_source, options)
+ end
+
+ def pages_changed?
+ @mount_points.detect {|mp| mp.changed?}
+ end
+
+ def eval
+ self.instance_eval(File.read(@file_path), @file_path)
+ end
+
+end
diff --git a/lib/site_mount_point.rb b/lib/site_mount_point.rb
new file mode 100644
index 0000000..0b8224a
--- /dev/null
+++ b/lib/site_mount_point.rb
@@ -0,0 +1,59 @@
+#
+# A site can have many 'mount points' -- places in the site tree where different directories are inserted.
+#
+# At a minimum, every site needs a mount point for '/'
+#
+class SiteMountPoint
+
+ attr_accessor :directory
+ attr_accessor :relative_directory
+ attr_accessor :path
+ attr_accessor :options
+ attr_accessor :menu_file
+ attr_accessor :locales_dir
+ attr_accessor :timestamp
+
+ def initialize(site_config, directory_source, options={})
+ if directory_source.starts_with?('/')
+ @directory = directory_source
+ else
+ @directory = File.expand_path(directory_source, File.dirname(site_config.file_path))
+ end
+
+ @path = options[:path]
+ @relative_directory = relative_dir_path(@directory)
+ @menu_file = file_path('menu.txt')
+ @locales_dir = file_path('locales')
+ @options = options
+ reset_timestamp
+ end
+
+ def changed?
+ File.mtime(@directory) > @timestamp
+ end
+
+ def reset_timestamp
+ @timestamp = File.mtime(@directory)
+ end
+
+ private
+
+ def file_path(file)
+ path = File.join(@directory, file)
+ if File.exists?(path)
+ path
+ else
+ nil
+ end
+ end
+
+ #
+ # returns path relative to app/views
+ #
+ def relative_dir_path(directory)
+ if Rails.root
+ Pathname.new(directory).relative_path_from(Pathname.new(Rails.root + 'app/views')).to_s
+ end
+ end
+
+end \ No newline at end of file
diff --git a/lib/static_page.rb b/lib/static_page.rb
index 1c5336e..eb32319 100644
--- a/lib/static_page.rb
+++ b/lib/static_page.rb
@@ -1,72 +1,50 @@
+#
+# class StaticPage
+#
+# represents a static website page.
+#
+#
+
require 'property_set'
require 'i18n'
require 'pathname'
class StaticPage
-
- class PageArray < Array
- def limit(num)
- PageArray.new(self[0..(num-1)])
- end
- def order_by(attr, options={})
- locale = options[:locale] || I18n.locale
- direction = options[:direction] || :asc
- array = sort do |a,b|
- if direction == :desc
- a, b = b, a
- end
- a_prop = a.props.locale(locale).send(attr)
- b_prop = b.props.locale(locale).send(attr)
- if a_prop.nil? && b_prop.nil?
- 0
- elsif a_prop.nil?
- 1
- elsif b_prop.nil?
- -1
- else
- a_prop <=> b_prop
- end
- end
- array.delete_if do |page|
- page.props.locale(locale).send(attr).nil?
- end
- return PageArray.new.replace array
- end
- end
-
- attr_accessor :path, :children, :name, :props, :parent
+ attr_accessor :path, :children, :name, :file_path, :props, :parent, :mount_point
##
## CLASS METHODS
##
- def self.find(filter)
+ def self.find(site, filter)
if filter =~ /\//
path = filter.split('/').map{|segment| segment.gsub(/[^0-9a-z_-]/, '')}
- page = @@pages[path.join('/')]
+ page = site.pages[path.join('/')]
if page
return page
else
- return @@pages[path.last]
+ return site.pages[path.last]
end
else
- @@pages[filter]
+ site.pages[filter]
end
end
- def self.all
- @@pages_array
- end
-
- def self.load(directory)
- @@pages = {}
- @@page_array = PageArray.new
- @@root_directory = directory
- @@relative_root_directory = relative_to_rails_view_root(directory)
- scan_directory(directory) do |page|
- @@pages[page.name] ||= page
- @@pages[page.path.join('/')] = page
- @@page_array << page
+ #
+ # loads a directory, creating StaticPages from the directory structure
+ #
+ def scan(&block)
+ Dir.chdir(file_path) do
+ Dir.glob("*").each do |child_name|
+ if File.directory?(child_name)
+ child = StaticPage.new(self, child_name)
+ yield child
+ child.scan(&block)
+ elsif is_simple_page?(child_name)
+ child = StaticPage.new(self, file_without_suffix(child_name))
+ yield child
+ end
+ end
end
end
@@ -74,17 +52,26 @@ class StaticPage
## INSTANCE METHODS
##
- def initialize(parent, name)
+ def initialize(parent, name, file_path=nil)
@children = []
@name = name
if parent
@parent = parent
+ @mount_point = @parent.mount_point
@parent.add_child(self)
@path = [@parent.path, @name].flatten.compact
- @props = load_properties(file_path)
else
@path = []
end
+ 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)
+ @props = load_properties
end
def add_child(page)
@@ -92,21 +79,25 @@ class StaticPage
end
def all_children
- PageArray.new(child_tree.flatten.compact)
+ StaticPageArray.new(child_tree.flatten.compact)
end
#
# e.g. /home/user/dev/leap-public-site/app/views/pages/about-us/contact
#
- def file_path
- "#{@@root_directory}/#{@path.join('/')}"
- end
+ #def file_path
+ # "#{@mount_point.directory}/#{@path.join('/')}"
+ #end
#
# e.g. pages/about-us/contact/en
#
def template_path(locale=I18n.locale)
- "#{@@relative_root_directory}/#{@path.join('/')}/#{locale}"
+ if @simple_page
+ "#{@mount_point.relative_directory}/#{@path.join('/')}"
+ else
+ "#{@mount_point.relative_directory}/#{@path.join('/')}/#{locale}"
+ end
end
def inspect
@@ -133,32 +124,19 @@ class StaticPage
private
- def self.scan_directory(directory, parent=nil, &block)
- parent ||= StaticPage.new(nil, 'root')
- Dir.chdir directory do
- Dir.glob("*").each do |child_dir|
- next unless File.directory?(child_dir)
- page = StaticPage.new(parent, child_dir)
- yield page
- scan_directory(child_dir, page, &block)
- end
- 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
- 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
-
- def load_properties(file_path)
+ 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 = ""
- #p content_file_path
#File.open(content_file_path) do |f|
# while (line = f.gets) =~ /^- @/
# variable_header << line
@@ -169,6 +147,23 @@ class StaticPage
return props
end
+ SUFFIXES = '(haml|md)'
+
+ #
+ # returns true if the name of a file could be a 'simple' static page
+ # with only one translation.
+ #
+ # rules:
+ # * we include files that end in appriopriate suffixes
+ # * we exclude file names that are locales.
+ #
+ def is_simple_page?(name)
+ name =~ /\.#{SUFFIXES}$/ && name !~ /^(#{AVAILABLE_LANGUAGES.join('|')})\.#{SUFFIXES}$/
+ end
+
+ def file_without_suffix(name)
+ name.sub(/^(.*?)\.#{SUFFIXES}$/, "\\1")
+ end
end
diff --git a/lib/static_page_array.rb b/lib/static_page_array.rb
new file mode 100644
index 0000000..33b11e0
--- /dev/null
+++ b/lib/static_page_array.rb
@@ -0,0 +1,32 @@
+#
+# Array of StaticPages
+#
+class StaticPageArray < Array
+ def limit(num)
+ StaticPageArray.new(self[0..(num-1)])
+ end
+ def order_by(attr, options={})
+ locale = options[:locale] || I18n.locale
+ direction = options[:direction] || :asc
+ array = sort do |a,b|
+ if direction == :desc
+ a, b = b, a
+ end
+ a_prop = a.props.locale(locale).send(attr)
+ b_prop = b.props.locale(locale).send(attr)
+ if a_prop.nil? && b_prop.nil?
+ 0
+ elsif a_prop.nil?
+ 1
+ elsif b_prop.nil?
+ -1
+ else
+ a_prop <=> b_prop
+ end
+ end
+ array.delete_if do |page|
+ page.props.locale(locale).send(attr).nil?
+ end
+ return StaticPageArray.new.replace array
+ end
+end \ No newline at end of file