diff options
-rw-r--r-- | app/controllers/application_controller.rb | 34 | ||||
-rw-r--r-- | app/controllers/pages_controller.rb | 4 | ||||
-rw-r--r-- | app/helpers/application_helper.rb | 8 | ||||
-rw-r--r-- | app/helpers/blog_helper.rb | 4 | ||||
-rw-r--r-- | app/helpers/navigation_helper.rb | 37 | ||||
-rw-r--r-- | app/views/layouts/_topnav.html.haml | 8 | ||||
-rw-r--r-- | app/views/layouts/application.html.haml | 2 | ||||
-rw-r--r-- | config/initializers/libraries.rb | 9 | ||||
-rw-r--r-- | lib/config.rb | 9 | ||||
-rw-r--r-- | lib/menu.rb | 133 | ||||
-rw-r--r-- | lib/site.rb | 93 | ||||
-rw-r--r-- | lib/site_configuration.rb | 50 | ||||
-rw-r--r-- | lib/site_mount_point.rb | 59 | ||||
-rw-r--r-- | lib/static_page.rb | 149 | ||||
-rw-r--r-- | lib/static_page_array.rb | 32 | ||||
-rw-r--r-- | site.rb | 11 |
16 files changed, 473 insertions, 169 deletions
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index da6cf3e..b2c5762 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -3,6 +3,10 @@ class ApplicationController < ActionController::Base protect_from_forgery before_filter :set_locale, :initialize_pages + class << self + attr_accessor :current_site # a class instance variable + end + if Rails.env.production? rescue_from Exception, :with => :render_500 rescue_from ActionController::RoutingError, :with => :render_404 @@ -81,25 +85,31 @@ class ApplicationController < ActionController::Base helper_method :page_body ## - ## INITIALIZATION + ## SITE ## # - # run every time in development mode, run once in production mode + # if we ever make this code support multiple sites, this should depend on the request's domain # - def initialize_pages - run_once(:initialize_pages, :unless => Rails.env.development?) do - StaticPage.load(PAGE_DIRECTORY) - Menu.load(PAGE_DIRECTORY + '/menu.txt') - end + def site + self.class.current_site ||= Site.new end + helper_method :site - def run_once(name, options={}) - key_name = "run_once_#{name}" - if !Thread.current[key_name] || options[:unless] - yield + def initialize_pages + if Rails.env.development? + site.load_pages + else + site.reload_pages_if_needed end - Thread.current[key_name] = true end + #def run_once(name, options={}) + # key_name = "run_once_#{name}" + # if !Thread.current[key_name] || options[:unless] + # yield + # end + # Thread.current[key_name] = true + #end + end diff --git a/app/controllers/pages_controller.rb b/app/controllers/pages_controller.rb index d4fa09a..36c733b 100644 --- a/app/controllers/pages_controller.rb +++ b/app/controllers/pages_controller.rb @@ -7,7 +7,7 @@ class PagesController < ApplicationController rescue_from PageNotFound, :with => :render_404 def show - @page = StaticPage.find(params[:page]) + @page = site.find_pages(params[:page]) if @page respond_to do |format| format.atom { render_atom_feed(@page) } @@ -21,7 +21,7 @@ class PagesController < ApplicationController protected def choose_layout - if @page && @page.props.layout + if @page && @page.props && @page.props.layout @page.props.layout else 'application' diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index b5bb657..2933bd1 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -33,7 +33,7 @@ module ApplicationHelper if name.starts_with?('#') || name.starts_with?('http') path = name else - page = StaticPage.find(name) + page = site.find_page(name) if page label ||= page.title path = page_path(page) @@ -69,7 +69,11 @@ module ApplicationHelper def page_title if @page - @page.props.title || @page.title + if @page.props + @page.props.title || @page.title + else + @page.title + end else "" end diff --git a/app/helpers/blog_helper.rb b/app/helpers/blog_helper.rb index 42e6f9c..2fc6b85 100644 --- a/app/helpers/blog_helper.rb +++ b/app/helpers/blog_helper.rb @@ -1,9 +1,9 @@ module BlogHelper def recent_blog_summaries(path) - root = StaticPage.find(path) + root = site.find_page(path) if root - pages = root.all_children.order_by(:posted_at, :direction => :desc).limit(PAGINATION_SIZE) + pages = root.all_children.order_by(:posted_at, :direction => :desc).limit(site.pagination_size) haml do pages.each do |page| haml render(:partial => 'layouts/blog/summary', :locals => {:page => page}) diff --git a/app/helpers/navigation_helper.rb b/app/helpers/navigation_helper.rb index 8582391..a43eecd 100644 --- a/app/helpers/navigation_helper.rb +++ b/app/helpers/navigation_helper.rb @@ -1,18 +1,21 @@ module NavigationHelper def has_side_column? - second_level_children_count = Menu.menu.submenu(current_page_path.first).try(:size) + if root_page? + return false + end + second_level_children_count = site.menu.submenu(current_page_path.first).try(:size) if second_level_children_count.nil? false else - second_level_children_count > 1 + second_level_children_count >= 1 end end def top_level_navigation_links haml do first = 'first' - Menu.menu.each do |item| + site.menu.each do |item| active = current_page_path.first == item.name ? 'active' : '' haml 'li.tab', :class => first do haml 'a.tab', I18n.t('pages.' + item.name), :href => menu_item_path(item), :class => active @@ -23,7 +26,7 @@ module NavigationHelper end def side_column_navigation_links - if menu = Menu.menu.submenu(current_page_path.first) + if menu = site.menu.submenu(current_page_path.first) haml do haml 'ul.nav.nav-tabs.nav-stacked' do display_menu(menu, 1) @@ -33,7 +36,7 @@ module NavigationHelper end def act_as(page) - page = StaticPage.find(page) + page = site.find_page(page) @current_page_path = page.path render_page(page) end @@ -54,7 +57,17 @@ module NavigationHelper end def path_active(page_path, menu_item) - array_starts_with(page_path, menu_item.path) ? 'active' : '' + active = '' + if menu_item.path == page_path + active = 'active' + elsif menu_item.path_prefix_of?(page_path) + if menu_item.leaf_for_path?(page_path) + active = 'active' + else + active = 'semi-active' + end + end + active end def current_page_path @@ -70,15 +83,11 @@ module NavigationHelper end # - # returns true if first part of array_long contains array_short + # the usage of 'home' as the default root page is hardcoded right now in the routes. + # this should be changed in the future. # - def array_starts_with(array_long, array_short) - array_short.length.times do |i| - if array_short[i] != array_long[i] - return false - end - end - return true + def root_page? + @page && @page.path == ['home'] end end diff --git a/app/views/layouts/_topnav.html.haml b/app/views/layouts/_topnav.html.haml deleted file mode 100644 index d0d553e..0000000 --- a/app/views/layouts/_topnav.html.haml +++ /dev/null @@ -1,8 +0,0 @@ -%ul#topnav - = top_level_navigation_links - %li.tab.first - %a.tab{:href => '#'} Home - %li.tab - %a.tab.active{:href => '#'} Technology - %li.tab - %a.tab About Us
\ No newline at end of file diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index fe0888a..3d28f38 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -1,7 +1,7 @@ !!! 5 %html{:dir=>'ltr'} %head - %title #{SITE_TITLE} - #{page_title} + %title #{site.title} - #{page_title} = stylesheet_link_tag "application", :media => "all" = javascript_include_tag "application" = csrf_meta_tags diff --git a/config/initializers/libraries.rb b/config/initializers/libraries.rb index f018ff9..ae0b4f0 100644 --- a/config/initializers/libraries.rb +++ b/config/initializers/libraries.rb @@ -1,5 +1,6 @@ -require 'config' -require 'static_page' -require 'menu' -require 'http_accept_language' +#require 'site_configuration' +#require 'site' +#require 'static_page' +#require 'menu' +#require 'http_accept_language' require 'core_extensions'
\ No newline at end of file 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 @@ -0,0 +1,11 @@ +# +# site configuration +# + +pages 'pages', :path => '/' + +pages '../leap_doc', :path => '/docs' + +@title = "LEAP" + +@pagination_size = 20 |