bonsai 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +5 -0
- data/.gitignore +23 -0
- data/.kick +26 -0
- data/LICENSE +20 -0
- data/README.md +65 -0
- data/Rakefile +64 -0
- data/VERSION +1 -0
- data/benchmark/associations.rb +51 -0
- data/bin/bonsai +69 -0
- data/bonsai.gemspec +142 -0
- data/lib/bonsai.rb +60 -0
- data/lib/bonsai/console.rb +8 -0
- data/lib/bonsai/exporter.rb +103 -0
- data/lib/bonsai/generate.rb +24 -0
- data/lib/bonsai/navigation.rb +7 -0
- data/lib/bonsai/page.rb +192 -0
- data/lib/bonsai/template.rb +31 -0
- data/lib/bonsai/templates/content/index/default.yml +3 -0
- data/lib/bonsai/templates/public/.htaccess +28 -0
- data/lib/bonsai/templates/public/docs/css/base.less +1 -0
- data/lib/bonsai/templates/templates/default.mustache +14 -0
- data/lib/bonsai/webserver.rb +19 -0
- data/spec/bonsai/console_spec.rb +12 -0
- data/spec/bonsai/exporter_spec.rb +142 -0
- data/spec/bonsai/generate_spec.rb +40 -0
- data/spec/bonsai/navigation_spec.rb +28 -0
- data/spec/bonsai/page_spec.rb +225 -0
- data/spec/bonsai/template_spec.rb +19 -0
- data/spec/bonsai_spec.rb +21 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/support/broken/content/broken_page/demo-template.yml +3 -0
- data/spec/support/content/1.about-us/1.contact/1.child/a_file_asset.txt +0 -0
- data/spec/support/content/1.about-us/1.contact/1.child/demo-template.yml +1 -0
- data/spec/support/content/1.about-us/1.contact/demo-template.yml +7 -0
- data/spec/support/content/1.about-us/1.contact/images/image001.jpg +0 -0
- data/spec/support/content/1.about-us/1.contact/magic/image001.jpg +0 -0
- data/spec/support/content/1.about-us/1.contact/magic/image002.jpg +0 -0
- data/spec/support/content/1.about-us/demo-template.yml +1 -0
- data/spec/support/content/1.about-us/history/a_file_asset.txt +0 -0
- data/spec/support/content/1.about-us/history/child/a_file_asset.txt +0 -0
- data/spec/support/content/1.about-us/history/child/demo-template.yml +1 -0
- data/spec/support/content/1.about-us/history/demo-template.yml +1 -0
- data/spec/support/content/1.about-us/history/image001.jpg +0 -0
- data/spec/support/content/1.about-us/history/images/image001.jpg +0 -0
- data/spec/support/content/1.about-us/history/magic/image001.jpg +0 -0
- data/spec/support/content/1.about-us/history/magic/image002.jpg +0 -0
- data/spec/support/content/10.many-pages/demo-template.yml +1 -0
- data/spec/support/content/2.products/1.product-a/demo-template.yml +1 -0
- data/spec/support/content/2.products/2.product-b/demo-template.yml +1 -0
- data/spec/support/content/2.products/demo-template.yml +1 -0
- data/spec/support/content/index/demo-template.yml +1 -0
- data/spec/support/content/legals/terms-and-conditions/demo-template.yml +1 -0
- data/spec/support/public/.htaccess +31 -0
- data/spec/support/public/js/script.js +19 -0
- data/spec/support/public/stylesheets/base.less +2 -0
- data/spec/support/public/stylesheets/broken.less +2 -0
- data/spec/support/templates/demo-template.mustache +19 -0
- data/spec/support/templates/partials/inserted.mustache +1 -0
- data/vendor/yui-compressor/yuicompressor-2.4.2.jar +0 -0
- metadata +201 -0
    
        data/lib/bonsai.rb
    ADDED
    
    | @@ -0,0 +1,60 @@ | |
| 1 | 
            +
            require 'rubygems'
         | 
| 2 | 
            +
            require 'fileutils'
         | 
| 3 | 
            +
            require 'logger'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            $LOAD_PATH << "#{File.dirname(__FILE__)}/bonsai"
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            module Bonsai
         | 
| 8 | 
            +
              @@root_dir = nil
         | 
| 9 | 
            +
              @@config = { :enable_logging => true }
         | 
| 10 | 
            +
             | 
| 11 | 
            +
              class << self
         | 
| 12 | 
            +
                def root_dir
         | 
| 13 | 
            +
                  @@root_dir || Dir.pwd
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
              
         | 
| 16 | 
            +
                def root_dir=(path)
         | 
| 17 | 
            +
                  unless is_a_bonsai?(path)
         | 
| 18 | 
            +
                    log "no bonsai site found - are you in the right directory?" 
         | 
| 19 | 
            +
                    exit 0
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
                  
         | 
| 22 | 
            +
                  @@root_dir = path
         | 
| 23 | 
            +
                  
         | 
| 24 | 
            +
                  Exporter.path = "#{path}/output"
         | 
| 25 | 
            +
                  Page.path = "#{path}/content"
         | 
| 26 | 
            +
                  Template.path = "#{path}/templates"
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
                
         | 
| 29 | 
            +
                def log(message)
         | 
| 30 | 
            +
                  puts message if @@config[:enable_logging]
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
              
         | 
| 33 | 
            +
                def config
         | 
| 34 | 
            +
                  @@config 
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
              
         | 
| 37 | 
            +
                def configure(&block)
         | 
| 38 | 
            +
                  yield @@config
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
                
         | 
| 41 | 
            +
                def version
         | 
| 42 | 
            +
                  File.read("#{File.dirname(__FILE__)}/../VERSION")
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
                
         | 
| 45 | 
            +
                private
         | 
| 46 | 
            +
                def is_a_bonsai?(path)
         | 
| 47 | 
            +
                  File.directory?("#{path}/content") && File.directory?("#{path}/public") && File.directory?("#{path}/templates")
         | 
| 48 | 
            +
                end
         | 
| 49 | 
            +
              end
         | 
| 50 | 
            +
              
         | 
| 51 | 
            +
              autoload :Page,               "page"
         | 
| 52 | 
            +
              autoload :Console,            "console"
         | 
| 53 | 
            +
              autoload :Exporter,           "exporter"
         | 
| 54 | 
            +
              autoload :Template,           "template"
         | 
| 55 | 
            +
              autoload :Generate,           "generate"
         | 
| 56 | 
            +
              autoload :Navigation,         "navigation"
         | 
| 57 | 
            +
              autoload :PagePresenter,      "page_presenter"
         | 
| 58 | 
            +
              autoload :StaticPassThrough,  "webserver"
         | 
| 59 | 
            +
              autoload :DevelopmentServer,  "webserver"
         | 
| 60 | 
            +
            end
         | 
| @@ -0,0 +1,103 @@ | |
| 1 | 
            +
            require 'fileutils'
         | 
| 2 | 
            +
            require 'less'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module Bonsai
         | 
| 5 | 
            +
              class Exporter
         | 
| 6 | 
            +
                @@path = "output"
         | 
| 7 | 
            +
                
         | 
| 8 | 
            +
                class << self
         | 
| 9 | 
            +
                  def path; @@path; end
         | 
| 10 | 
            +
                  def path=(path); @@path = path; end
         | 
| 11 | 
            +
                  
         | 
| 12 | 
            +
                  def process!
         | 
| 13 | 
            +
                    setup
         | 
| 14 | 
            +
                    copy_public
         | 
| 15 | 
            +
                    copy_assets
         | 
| 16 | 
            +
                    cleanup
         | 
| 17 | 
            +
                  end
         | 
| 18 | 
            +
                  
         | 
| 19 | 
            +
                  def publish!
         | 
| 20 | 
            +
                    teardown
         | 
| 21 | 
            +
                    setup
         | 
| 22 | 
            +
                    copy_assets
         | 
| 23 | 
            +
                    copy_public
         | 
| 24 | 
            +
                    compress_assets
         | 
| 25 | 
            +
                    write_index
         | 
| 26 | 
            +
                    write_pages
         | 
| 27 | 
            +
                    cleanup
         | 
| 28 | 
            +
                  end
         | 
| 29 | 
            +
                  
         | 
| 30 | 
            +
                  protected 
         | 
| 31 | 
            +
                  def teardown
         | 
| 32 | 
            +
                    FileUtils.rm_rf path
         | 
| 33 | 
            +
                  end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                  def setup
         | 
| 36 | 
            +
                    FileUtils.mkdir_p path
         | 
| 37 | 
            +
                  end
         | 
| 38 | 
            +
                  
         | 
| 39 | 
            +
                  def cleanup
         | 
| 40 | 
            +
                    remove_less_from_public
         | 
| 41 | 
            +
                  end
         | 
| 42 | 
            +
                  
         | 
| 43 | 
            +
                  def write_index
         | 
| 44 | 
            +
                    Bonsai.log "Writing index"
         | 
| 45 | 
            +
                    File.open("#{path}/index.html", "w") {|file| file.write(Page.find("index").render)}
         | 
| 46 | 
            +
                  end
         | 
| 47 | 
            +
                  
         | 
| 48 | 
            +
                  def write_pages
         | 
| 49 | 
            +
                    Bonsai.log "Writing pages"
         | 
| 50 | 
            +
                    Page.all.each do |page|
         | 
| 51 | 
            +
                      Bonsai.log "\t Writing page - #{page.permalink}"
         | 
| 52 | 
            +
                      FileUtils.mkdir_p("#{path}#{page.permalink}")
         | 
| 53 | 
            +
                      File.open("#{path}#{page.write_path}", "w"){|file| file.write(page.render) }
         | 
| 54 | 
            +
                    end
         | 
| 55 | 
            +
                  end
         | 
| 56 | 
            +
                  
         | 
| 57 | 
            +
                  def copy_assets
         | 
| 58 | 
            +
                    Bonsai.log "Copying page assets"
         | 
| 59 | 
            +
                    Page.all.each do |page|
         | 
| 60 | 
            +
                      page.assets.each do |asset|      
         | 
| 61 | 
            +
                        # Create the path to the asset by the export path of the page + File.dirname(asset permalink)
         | 
| 62 | 
            +
                        FileUtils.mkdir_p "#{path}#{File.dirname(asset[:path])}"
         | 
| 63 | 
            +
                        
         | 
| 64 | 
            +
                        # Copy the the asset from its disk path to File.dirname(asset permalink)
         | 
| 65 | 
            +
                        FileUtils.cp asset[:disk_path], "#{path}#{asset[:path]}"            
         | 
| 66 | 
            +
                      end
         | 
| 67 | 
            +
                    end
         | 
| 68 | 
            +
                  end
         | 
| 69 | 
            +
                  
         | 
| 70 | 
            +
                  def copy_public
         | 
| 71 | 
            +
                    generate_css_from_less
         | 
| 72 | 
            +
                    
         | 
| 73 | 
            +
                    Bonsai.log "Copying public files"
         | 
| 74 | 
            +
                    # Using system call because fileutils is inadequate
         | 
| 75 | 
            +
                    system("cp -fR '#{Bonsai.root_dir}/public/' '#{path}'")
         | 
| 76 | 
            +
                  end
         | 
| 77 | 
            +
                  
         | 
| 78 | 
            +
                  def compress_assets
         | 
| 79 | 
            +
                    yui_compressor = File.expand_path("#{File.dirname(__FILE__)}/../../vendor/yui-compressor/yuicompressor-2.4.2.jar")
         | 
| 80 | 
            +
                    
         | 
| 81 | 
            +
                    Bonsai.log "Compressing javascript and stylesheets"
         | 
| 82 | 
            +
                    Dir["#{path}/**/*.{js,css}"].each do |asset|
         | 
| 83 | 
            +
                      system "java -jar #{yui_compressor} #{File.expand_path(asset)} -o #{File.expand_path(asset)}"
         | 
| 84 | 
            +
                    end
         | 
| 85 | 
            +
                  end
         | 
| 86 | 
            +
                  
         | 
| 87 | 
            +
                  def generate_css_from_less
         | 
| 88 | 
            +
                    Dir["#{Bonsai.root_dir}/public/**/*.less"].each do |lessfile|
         | 
| 89 | 
            +
                      css = File.open(lessfile) {|f| Less::Engine.new(f) }.to_css
         | 
| 90 | 
            +
                      path = "#{File.dirname(lessfile)}/#{File.basename(lessfile, ".less")}.css"
         | 
| 91 | 
            +
                      
         | 
| 92 | 
            +
                      File.open(path, "w") {|file| file.write(css) }
         | 
| 93 | 
            +
                    end
         | 
| 94 | 
            +
                  rescue Less::SyntaxError => exception
         | 
| 95 | 
            +
                    Bonsai.log "LessCSS Syntax error\n\n#{exception.message}"
         | 
| 96 | 
            +
                  end
         | 
| 97 | 
            +
                  
         | 
| 98 | 
            +
                  def remove_less_from_public
         | 
| 99 | 
            +
                    Dir["#{path}/**/*.less"].each{|f| FileUtils.rm(f) }
         | 
| 100 | 
            +
                  end
         | 
| 101 | 
            +
                end
         | 
| 102 | 
            +
              end
         | 
| 103 | 
            +
            end
         | 
| @@ -0,0 +1,24 @@ | |
| 1 | 
            +
            module Bonsai
         | 
| 2 | 
            +
              class Generate  
         | 
| 3 | 
            +
                def initialize(path)
         | 
| 4 | 
            +
                  @path = path
         | 
| 5 | 
            +
                
         | 
| 6 | 
            +
                  Bonsai.log "Planting your bonsai '#{path}'"
         | 
| 7 | 
            +
                  copy_templates
         | 
| 8 | 
            +
                  create_directories
         | 
| 9 | 
            +
                end
         | 
| 10 | 
            +
              
         | 
| 11 | 
            +
                private  
         | 
| 12 | 
            +
                def create_directories
         | 
| 13 | 
            +
                  %w(content content/index public/docs/css public/docs/js).each do |dir|
         | 
| 14 | 
            +
                    Bonsai.log "\tcreate\t#{dir}"
         | 
| 15 | 
            +
                    FileUtils.mkdir_p(File.join(@path, dir))
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
              
         | 
| 19 | 
            +
                def copy_templates
         | 
| 20 | 
            +
                  # Using system call because fileutils is inadequate
         | 
| 21 | 
            +
                  system("cp -fR '#{File.expand_path("#{File.dirname(__FILE__)}/templates")}' '#{@path}'")
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
              end
         | 
| 24 | 
            +
            end
         | 
    
        data/lib/bonsai/page.rb
    ADDED
    
    | @@ -0,0 +1,192 @@ | |
| 1 | 
            +
            require 'yaml'
         | 
| 2 | 
            +
            require 'rdiscount'
         | 
| 3 | 
            +
            require 'tilt'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module Bonsai
         | 
| 6 | 
            +
              class Page
         | 
| 7 | 
            +
                class NotFound < StandardError; end;
         | 
| 8 | 
            +
                class PropertyNotFound < StandardError; end
         | 
| 9 | 
            +
                @@pages = {}
         | 
| 10 | 
            +
                
         | 
| 11 | 
            +
                class << self    
         | 
| 12 | 
            +
                  def path; @@path; end
         | 
| 13 | 
            +
                  def path=(path); @@path = path; end
         | 
| 14 | 
            +
                  
         | 
| 15 | 
            +
                  def all(dir_path = path, pattern = "*/**")
         | 
| 16 | 
            +
                    Dir["#{dir_path}/#{pattern}/*.yml"].map {|p| Page.new p }
         | 
| 17 | 
            +
                  end
         | 
| 18 | 
            +
                  
         | 
| 19 | 
            +
                  def find(permalink)        
         | 
| 20 | 
            +
                    @@pages[permalink] ||= find!(permalink)
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
                  
         | 
| 23 | 
            +
                  private
         | 
| 24 | 
            +
                  def find!(permalink)
         | 
| 25 | 
            +
                    search_path = permalink.gsub("/", "/*")
         | 
| 26 | 
            +
                    disk_path = Dir["#{path}/*#{search_path}/*.yml"]
         | 
| 27 | 
            +
                    if disk_path.any?
         | 
| 28 | 
            +
                      return new disk_path.first
         | 
| 29 | 
            +
                    else
         | 
| 30 | 
            +
                      raise NotFound, "page '#{permalink}' not found at '#{path}'"
         | 
| 31 | 
            +
                    end
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
                
         | 
| 35 | 
            +
                attr_reader :disk_path
         | 
| 36 | 
            +
                
         | 
| 37 | 
            +
                def initialize(path)
         | 
| 38 | 
            +
                  @disk_path = path
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
                
         | 
| 41 | 
            +
                def slug
         | 
| 42 | 
            +
                  permalink.split('/').pop
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
                
         | 
| 45 | 
            +
                def name
         | 
| 46 | 
            +
                  slug.gsub(/\W/, " ").gsub(/\d\./, '').gsub(/^\w/){$&.upcase}
         | 
| 47 | 
            +
                end
         | 
| 48 | 
            +
                
         | 
| 49 | 
            +
                def permalink
         | 
| 50 | 
            +
                  web_path(directory)
         | 
| 51 | 
            +
                end
         | 
| 52 | 
            +
                
         | 
| 53 | 
            +
                def write_path
         | 
| 54 | 
            +
                  "#{permalink}/index.html"
         | 
| 55 | 
            +
                end
         | 
| 56 | 
            +
                
         | 
| 57 | 
            +
                def template
         | 
| 58 | 
            +
                  Template.find(template_name)
         | 
| 59 | 
            +
                end
         | 
| 60 | 
            +
                
         | 
| 61 | 
            +
                # This method is used for the exporter to copy assets
         | 
| 62 | 
            +
                def assets
         | 
| 63 | 
            +
                  Dir["#{directory}/**/*"].select{|p| !File.directory?(p) && !File.basename(p).include?("yml") }.map do |a|
         | 
| 64 | 
            +
                    {
         | 
| 65 | 
            +
                      :name       => File.basename(a),
         | 
| 66 | 
            +
                      :path       => web_path(a),
         | 
| 67 | 
            +
                      :disk_path  => a
         | 
| 68 | 
            +
                    }
         | 
| 69 | 
            +
                  end
         | 
| 70 | 
            +
                end
         | 
| 71 | 
            +
                
         | 
| 72 | 
            +
                def floating?
         | 
| 73 | 
            +
                  !!(File.dirname(disk_path) =~ /\/[a-zA-z][\w-]+$/)
         | 
| 74 | 
            +
                end
         | 
| 75 | 
            +
                
         | 
| 76 | 
            +
                def parent
         | 
| 77 | 
            +
                  id = permalink[/^\/(.+)\/[^\/]*$/, 1]
         | 
| 78 | 
            +
                  return nil if id.nil?
         | 
| 79 | 
            +
                  
         | 
| 80 | 
            +
                  parent = Page.find(id)
         | 
| 81 | 
            +
                  return nil if parent == self
         | 
| 82 | 
            +
                  
         | 
| 83 | 
            +
                  parent
         | 
| 84 | 
            +
                rescue NotFound
         | 
| 85 | 
            +
                  nil
         | 
| 86 | 
            +
                end
         | 
| 87 | 
            +
                
         | 
| 88 | 
            +
                def siblings
         | 
| 89 | 
            +
                  self.class.all(File.dirname(disk_path[/(.+)\/[^\/]*$/, 1]), "*").delete_if{|p| p == self}
         | 
| 90 | 
            +
                end
         | 
| 91 | 
            +
                
         | 
| 92 | 
            +
                def children
         | 
| 93 | 
            +
                  self.class.all(File.dirname(disk_path), "*").delete_if{|p| p.floating? }
         | 
| 94 | 
            +
                end
         | 
| 95 | 
            +
                
         | 
| 96 | 
            +
                def ancestors
         | 
| 97 | 
            +
                  ancestors = []
         | 
| 98 | 
            +
                  # Remove leading slash
         | 
| 99 | 
            +
                  page_ref = permalink.gsub(/^\//, '')
         | 
| 100 | 
            +
                  
         | 
| 101 | 
            +
                  # Find pages up the permalink tree if possible
         | 
| 102 | 
            +
                  while(page_ref) do
         | 
| 103 | 
            +
                    page_ref = page_ref[/(.+)\/[^\/]*$/, 1]
         | 
| 104 | 
            +
                    ancestors << self.class.find(page_ref) rescue nil
         | 
| 105 | 
            +
                  end
         | 
| 106 | 
            +
                  
         | 
| 107 | 
            +
                  ancestors.compact.reverse
         | 
| 108 | 
            +
                end
         | 
| 109 | 
            +
                
         | 
| 110 | 
            +
                def ==(other)
         | 
| 111 | 
            +
                  self.permalink == other.permalink
         | 
| 112 | 
            +
                end
         | 
| 113 | 
            +
                
         | 
| 114 | 
            +
                def render
         | 
| 115 | 
            +
                  Tilt.new(template.path, :path => template.class.path).render(self, to_hash)
         | 
| 116 | 
            +
                rescue => stack
         | 
| 117 | 
            +
                  raise "Issue rendering #{permalink}\n\n#{stack}"
         | 
| 118 | 
            +
                end
         | 
| 119 | 
            +
                
         | 
| 120 | 
            +
                def content
         | 
| 121 | 
            +
                  YAML::load(File.read(@disk_path)) || {}
         | 
| 122 | 
            +
                rescue ArgumentError
         | 
| 123 | 
            +
                  Bonsai.log "Page '#{permalink}' has badly formatted content"
         | 
| 124 | 
            +
                end
         | 
| 125 | 
            +
                
         | 
| 126 | 
            +
                # This hash is available to all templates, it will map common properties, 
         | 
| 127 | 
            +
                # content file results, as well as any "magic" hashes for file 
         | 
| 128 | 
            +
                # system contents
         | 
| 129 | 
            +
                def to_hash
         | 
| 130 | 
            +
                  {
         | 
| 131 | 
            +
                    :slug         => slug, 
         | 
| 132 | 
            +
                    :permalink    => permalink, 
         | 
| 133 | 
            +
                    :name         => name, 
         | 
| 134 | 
            +
                    :children     => children,
         | 
| 135 | 
            +
                    :siblings     => siblings,
         | 
| 136 | 
            +
                    :parent       => parent, 
         | 
| 137 | 
            +
                    :ancestors    => ancestors,
         | 
| 138 | 
            +
                    :navigation   => Bonsai::Navigation.tree
         | 
| 139 | 
            +
                  }.merge(formatted_content).merge(disk_assets)
         | 
| 140 | 
            +
                end
         | 
| 141 | 
            +
                
         | 
| 142 | 
            +
                private
         | 
| 143 | 
            +
                def formatted_content
         | 
| 144 | 
            +
                  formatted_content = content
         | 
| 145 | 
            +
                  
         | 
| 146 | 
            +
                  formatted_content.each do |k,v|
         | 
| 147 | 
            +
                    if v.is_a?(String) and v =~ /\n/
         | 
| 148 | 
            +
                      formatted_content[k] = RDiscount.new(v, :smart).to_html
         | 
| 149 | 
            +
                    end
         | 
| 150 | 
            +
                  end
         | 
| 151 | 
            +
                  
         | 
| 152 | 
            +
                  formatted_content
         | 
| 153 | 
            +
                end
         | 
| 154 | 
            +
                
         | 
| 155 | 
            +
                # Creates methods for each sub-folder within the page's folder
         | 
| 156 | 
            +
                # that isn't a sub-page (a page object)
         | 
| 157 | 
            +
                def disk_assets
         | 
| 158 | 
            +
                  assets = {}
         | 
| 159 | 
            +
                  Dir["#{File.dirname(disk_path)}/**"].select{|p| File.directory?(p)}.reject {|p|
         | 
| 160 | 
            +
                    Dir.entries(p).any?{|e| e.include? "yml"}
         | 
| 161 | 
            +
                  }.each{|asset_path| assets.merge!(map_to_disk(asset_path)) }
         | 
| 162 | 
            +
             | 
| 163 | 
            +
                  assets
         | 
| 164 | 
            +
                end
         | 
| 165 | 
            +
                
         | 
| 166 | 
            +
                def map_to_disk(path)
         | 
| 167 | 
            +
                  name = File.basename(path)
         | 
| 168 | 
            +
                  
         | 
| 169 | 
            +
                  {
         | 
| 170 | 
            +
                    name.to_sym => Dir["#{path}/*"].map do |file|
         | 
| 171 | 
            +
                      {
         | 
| 172 | 
            +
                        :name       => File.basename(file),
         | 
| 173 | 
            +
                        :path       => web_path(file),
         | 
| 174 | 
            +
                        :disk_path  => file
         | 
| 175 | 
            +
                      }  
         | 
| 176 | 
            +
                    end
         | 
| 177 | 
            +
                  }
         | 
| 178 | 
            +
                end
         | 
| 179 | 
            +
                
         | 
| 180 | 
            +
                def directory
         | 
| 181 | 
            +
                  @disk_path.split("/")[0..-2].join("/")
         | 
| 182 | 
            +
                end
         | 
| 183 | 
            +
                
         | 
| 184 | 
            +
                def template_name
         | 
| 185 | 
            +
                  File.basename(@disk_path, '.*')
         | 
| 186 | 
            +
                end
         | 
| 187 | 
            +
                
         | 
| 188 | 
            +
                def web_path(path)
         | 
| 189 | 
            +
                  path.gsub(self.class.path, '').gsub(/\/\d+\./, '/')
         | 
| 190 | 
            +
                end
         | 
| 191 | 
            +
              end
         | 
| 192 | 
            +
            end
         | 
| @@ -0,0 +1,31 @@ | |
| 1 | 
            +
            module Bonsai
         | 
| 2 | 
            +
              class Template
         | 
| 3 | 
            +
                @@path = "templates"
         | 
| 4 | 
            +
                
         | 
| 5 | 
            +
                class NotFound < StandardError; end
         | 
| 6 | 
            +
                
         | 
| 7 | 
            +
                class << self
         | 
| 8 | 
            +
                  def path; @@path; end
         | 
| 9 | 
            +
                  
         | 
| 10 | 
            +
                  def path=(path)
         | 
| 11 | 
            +
                    @@path = path
         | 
| 12 | 
            +
                  end
         | 
| 13 | 
            +
                  
         | 
| 14 | 
            +
                  def find(name)
         | 
| 15 | 
            +
                    disk_path = Dir["#{path}/#{name}.*"]
         | 
| 16 | 
            +
                    
         | 
| 17 | 
            +
                    if disk_path.any? 
         | 
| 18 | 
            +
                      new disk_path.first
         | 
| 19 | 
            +
                    else
         | 
| 20 | 
            +
                      raise NotFound, "template '#{name}' not found at #{path}"
         | 
| 21 | 
            +
                    end
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
                
         | 
| 25 | 
            +
                attr_reader :path
         | 
| 26 | 
            +
                
         | 
| 27 | 
            +
                def initialize(path)
         | 
| 28 | 
            +
                  @path = path
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
              end
         | 
| 31 | 
            +
            end
         | 
| @@ -0,0 +1,28 @@ | |
| 1 | 
            +
            DirectoryIndex index.html
         | 
| 2 | 
            +
            FileETag All
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            # Compress all static assets
         | 
| 5 | 
            +
            <IfModule mod_deflate.c>
         | 
| 6 | 
            +
            	# compress content with type html, text, and css
         | 
| 7 | 
            +
              AddOutputFilterByType DEFLATE text/css text/html text/javascript application/javascript application/x-javascript text/js text/plain text/xml
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            	<IfModule mod_headers.c>
         | 
| 10 | 
            +
            		# properly handle requests coming from behind proxies
         | 
| 11 | 
            +
            		Header append Vary User-Agent
         | 
| 12 | 
            +
            	</IfModule>
         | 
| 13 | 
            +
            </IfModule>
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            # Cache, aggressively 
         | 
| 16 | 
            +
            <IfModule mod_expires.c>
         | 
| 17 | 
            +
              ExpiresActive On
         | 
| 18 | 
            +
              ExpiresDefault "access plus 10 days"
         | 
| 19 | 
            +
             | 
| 20 | 
            +
            	<FilesMatch "\.(eot|ttf|otf)$">
         | 
| 21 | 
            +
            		ExpiresDefault "access plus 10 years"
         | 
| 22 | 
            +
            	</filesMatch>
         | 
| 23 | 
            +
            </IfModule>
         | 
| 24 | 
            +
             | 
| 25 | 
            +
            # Mime-types
         | 
| 26 | 
            +
            AddType application/vnd.ms-fontobject .eot
         | 
| 27 | 
            +
            AddType font/ttf .ttf
         | 
| 28 | 
            +
            AddType font/otf .otf
         |