From 627f0488e5bd3c31359fc9e78acffbfea4a86a8b Mon Sep 17 00:00:00 2001 From: elijah Date: Fri, 24 Aug 2012 21:12:36 -0700 Subject: committed website v. 0.1.0 --- lib/assets/.gitkeep | 0 lib/config.rb | 9 +++ lib/http_accept_language.rb | 9 +++ lib/http_accept_language/parser.rb | 104 ++++++++++++++++++++++++ lib/menu.rb | 78 ++++++++++++++++++ lib/property_set.rb | 158 +++++++++++++++++++++++++++++++++++++ lib/static_page.rb | 156 ++++++++++++++++++++++++++++++++++++ lib/tasks/.gitkeep | 0 8 files changed, 514 insertions(+) create mode 100644 lib/assets/.gitkeep create mode 100644 lib/config.rb create mode 100644 lib/http_accept_language.rb create mode 100644 lib/http_accept_language/parser.rb create mode 100644 lib/menu.rb create mode 100644 lib/property_set.rb create mode 100644 lib/static_page.rb create mode 100644 lib/tasks/.gitkeep (limited to 'lib') diff --git a/lib/assets/.gitkeep b/lib/assets/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/lib/config.rb b/lib/config.rb new file mode 100644 index 0000000..a579631 --- /dev/null +++ b/lib/config.rb @@ -0,0 +1,9 @@ +# +# Here go any application configuration +# + +SITE_TITLE = "LEAP" + +PAGE_DIRECTORY = File.expand_path('../../app/views/pages', __FILE__) + +PAGINATION_SIZE = 20 diff --git a/lib/http_accept_language.rb b/lib/http_accept_language.rb new file mode 100644 index 0000000..87c6ecc --- /dev/null +++ b/lib/http_accept_language.rb @@ -0,0 +1,9 @@ +require 'http_accept_language/parser' + +module HttpAcceptLanguage + + def self.compatible_language_from(header, languages) + Parser.new(header).compatible_language_from(languages) + end + +end diff --git a/lib/http_accept_language/parser.rb b/lib/http_accept_language/parser.rb new file mode 100644 index 0000000..0aad1a2 --- /dev/null +++ b/lib/http_accept_language/parser.rb @@ -0,0 +1,104 @@ +module HttpAcceptLanguage + + class Parser + + attr_accessor :header + + def initialize(header) + @header = header + end + + # Returns a sorted array based on user preference in HTTP_ACCEPT_LANGUAGE. + # Browsers send this HTTP header, so don't think this is holy. + # + # Example: + # + # request.user_preferred_languages + # # => [ 'nl-NL', 'nl-BE', 'nl', 'en-US', 'en' ] + # + def user_preferred_languages + @user_preferred_languages ||= header.gsub(/\s+/, '').split(/,/).collect do |l| + l += ';q=1.0' unless l =~ /;q=\d+\.\d+$/ + l.split(';q=') + end.sort do |x,y| + raise "Not correctly formatted" unless x.first =~ /^[a-z\-0-9]+$/i + y.last.to_f <=> x.last.to_f + end.collect do |l| + l.first.downcase.gsub(/-[a-z0-9]+$/i) { |x| x.upcase } + end + rescue # Just rescue anything if the browser messed up badly. + [] + end + + # Sets the user languages preference, overiding the browser + # + def user_preferred_languages=(languages) + @user_preferred_languages = languages + end + + # Finds the locale specifically requested by the browser. + # + # Example: + # + # request.preferred_language_from I18n.available_locales + # # => 'nl' + # + def preferred_language_from(array) + (user_preferred_languages & array.collect { |i| i.to_s }).first + end + + # Returns the first of the user_preferred_languages that is compatible + # with the available locales. Ignores region. + # + # Example: + # + # request.compatible_language_from I18n.available_locales + # + def compatible_language_from(available_languages) + user_preferred_languages.map do |x| #en-US + available_languages.find do |y| # en + y = y.to_s + x == y || x.split('-', 2).first == y.split('-', 2).first + end + end.compact.first + end + + # Returns a supplied list of available locals without any extra application info + # that may be attached to the locale for storage in the application. + # + # Example: + # [ja_JP-x1, en-US-x4, en_UK-x5, fr-FR-x3] => [ja-JP, en-US, en-UK, fr-FR] + # + def sanitize_available_locales(available_languages) + available_languages.map do |avail| + split_locale = avail.split(/[_-]/) + + split_locale.map do |e| + e unless e.match(/x|[0-9*]/) + end.compact.join("-") + end + end + + # Returns the first of the user preferred languages that is + # also found in available languages. Finds best fit by matching on + # primary language first and secondarily on region. If no matching region is + # found, return the first language in the group matching that primary language. + # + # Example: + # + # request.language_region_compatible(available_languages) + # + def language_region_compatible_from(available_languages) + available_languages = sanitize_available_locales(available_languages) + user_preferred_languages.map do |x| #en-US + lang_group = available_languages.select do |y| # en + y = y.to_s + x.split('-', 2).first == y.split('-', 2).first + end + lang_group.find{|l| l == x} || lang_group.first #en-US, en-UK + end.compact.first + end + + end + +end diff --git a/lib/menu.rb b/lib/menu.rb new file mode 100644 index 0000000..71690a4 --- /dev/null +++ b/lib/menu.rb @@ -0,0 +1,78 @@ +class Menu < Array + attr_accessor :parent + + # + # class methods + # + + # + # load the menu.txt file, recursively, and build the in-memory menu array + # + def self.load(menu_file_path) + @menu = Menu.new + File.open(menu_file_path) do |file| + append_menu_item(@menu, file) + end + end + + def self.menu + @menu || Menu.new + end + + def self.create(elements=[], parent=nil) + menu = Menu[elements] + menu.parent = parent + return menu + end + + # + # public methods + # + + def submenu(item_name=nil) + if item_name + submenu = detect {|item| item[0] == item_name} || Menu.new + else + submenu = self + end + return submenu[1..-1] # strip of first element + end + + def name + first + end + + def path + @path ||= begin + if parent == nil + [] + else + parent.path + [name] + end + end + end + + private + + def self.append_menu_item(menu, file) + begin + item = file.readline + rescue EOFError + # do nothing + else + if item !~ /^\s*#/ + depth = item.scan(" ").size + sub_menu = sub_menu_for_depth(menu, depth) + sub_menu << Menu.create(item.strip, sub_menu) + end + append_menu_item(menu, file) + end + end + + def self.sub_menu_for_depth(menu, depth) + sub_menu = menu + depth.times { sub_menu = sub_menu[-1] } + sub_menu + end + +end \ No newline at end of file diff --git a/lib/property_set.rb b/lib/property_set.rb new file mode 100644 index 0000000..c315a13 --- /dev/null +++ b/lib/property_set.rb @@ -0,0 +1,158 @@ +# +# holds the set or properties defined for each static page. +# property sets are organized by locale. +# +# e.g. +# +# setting the property (in en.haml): +# +# - @title = 'hi' +# +# getting the property +# +# page.props.title +# + +require 'i18n' +require 'time' +require 'rubygems' +require 'haml' +require 'RedCloth' + +class PropertySet + + DEFAULT_LOCALE = 'en' + + # + # a simple class to pass through all member variables as attributes. + # when the template for a page is evaluated, all the member variabled defined in that template + # are loaded as member variables of the AttrObject instance. + # + class AttrObject + def initialize(property_set, locale) + @_ps = property_set + @_locale = locale + #code.gsub!(/^((?!^\-\ \@).)*$/, '') # remove any lines not starting with '- @' + #code.gsub!(/^- /m, '') # remove '-' + #instance_eval(code) + #instance_variables.grep(/_at$/).each do |time_variable| + # instance_variable_set(time_variable, Time.parse(instance_variable_get(time_variable))) + #end + end + def method_missing(method) + get(method) + end + def textile(str) + RedCloth.new(str).to_html + end + def get(var_name) + value = instance_variable_get("@#{var_name}") + if value.nil? + if @_locale != DEFAULT_LOCALE + # try value from default locale + @_ps.get_var(var_name) + else + # try inherited value + @_ps.get_inherited_var(var_name, @_locale) + end + else + value + end + end + def set(var_name, value) + instance_variable_set("@#{var_name}", value) + end + end + + def initialize(page=nil) + @page = page + @locales = {} + end + + + # + # maybe something like this in the future: + # + #contr = PagesController.new() + #contr.response = ActionController::Response.new() + #scope = ActionView::Base.new(["#{RAILS_ROOT}/app/views/pages","#{RAILS_ROOT}/app/views"], {}, contr) + #scope.template_format = 'html' + #Haml::Engine.new(value, :format => :html5).render(scope) + # + + # + # evaluate the template_string, and load the variables defined into an AttrObject. + # + def eval(locale, template_string) + attrs = AttrObject.new(self, locale) + body = nil + begin + body = Haml::Engine.new(template_string, :format => :html5).render(attrs) # template is evaluated in scope of attrs + rescue Exception => exc + # eat exceptions + end + attrs.instance_variable_set('@body', body) + attrs.instance_variables.grep(/_at$/).each do |time_variable| + attrs.instance_variable_set(time_variable, Time.parse(attrs.instance_variable_get(time_variable))) + end + @locales[locale] = attrs + end + + # + # allows property_set.propname shortcut, assumes default locale + # + def method_missing(method) + get_var(method) + end + + def locale(l) + @locales[l.to_s] || @locales[DEFAULT_LOCALE] + end + + def get_var(var_name, locale=I18n.locale) + attrs = locale(locale) + if attrs + attrs.get(var_name) + else + nil + end + end + + # + # tries to get the value of an inherited variable + # + def get_inherited_var(var_name, locale=I18n.locale) + if @page && @page.parent && @page.parent.props + @page.parent.props.get_var(var_name, locale) + end + end +end + +# +# a simple little test. +# +if ARGV.grep('--test').any? + text_en = " +- @title = 'hi' +- @author = 'you' +- @created_at = 'Sun Aug 12 18:32:20 PDT 2012' +- ignored = 1 +this is the body" + + text_es = " +- @title = 'hola' +- @author = 'tu' +- @heading = textile 'h1. hi' +" + + ps = PropertySet.new + ps.eval('en', text_en) + ps.eval('es', text_es) + + p ps.title == 'hi' + p ps.locale(:es).title == 'hola' + p ps.locale(:es).created_at == Time.parse('Sun Aug 12 18:32:20 PDT 2012') + p ps.ignored == nil + p ps.locale(:es).heading == "

hi

" + p ps.body == "this is the body\n" +end diff --git a/lib/static_page.rb b/lib/static_page.rb new file mode 100644 index 0000000..12d4948 --- /dev/null +++ b/lib/static_page.rb @@ -0,0 +1,156 @@ +require 'lib/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, locale=I18n.locale) + array = sort do |a,b| + 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 + end + end + + attr_accessor :path, :children, :name, :props, :parent + + def self.find(filter) + if filter =~ /\// + path = filter.split('/').map{|segment| segment.gsub(/[^0-9a-z_-]/, '')} + page = @@pages[path.join('/')] + if page + return page + else + return @@pages[path.last] + end + else + @@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 + end + end + + def initialize(parent, name) + @children = [] + @name = name + if parent + @parent = parent + @parent.add_child(self) + @path = [@parent.path, @name].flatten.compact + @props = load_properties(file_path) + else + @path = [] + end + end + + def add_child(page) + @children << page + end + + def all_children + PageArray.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 + + # + # e.g. pages/about-us/contact/en + # + def template_path(locale=I18n.locale) + "#{@@relative_root_directory}/#{@path.join('/')}/#{locale}" + end + + def inspect + "<'#{@path.join('/')}' #{children.inspect}>" + end + + def title + begin + I18n.t!('pages.' + @name, :raise => true) + rescue I18n::MissingTranslationData + props.title + end + end + + protected + + def child_tree + [self, children.collect{|child| child.child_tree}] + end + + 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 load_properties(file_path) + 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 + # end + #end + props.eval(locale, File.read(content_file_path)) + end + return props + end + +end + + diff --git a/lib/tasks/.gitkeep b/lib/tasks/.gitkeep new file mode 100644 index 0000000..e69de29 -- cgit v1.2.3