Permalink
Browse files

Site#generate now finds all paths

  • Loading branch information...
1 parent f313775 commit 66e098ed9755807920633322c9a5c5edf8164c59 @ddemaree committed Mar 11, 2012
Showing with 137 additions and 32 deletions.
  1. +40 −9 lib/soundwave.rb
  2. +16 −20 lib/soundwave/page.rb
  3. +7 −0 lib/soundwave/server.rb
  4. +6 −3 soundwave.gemspec
  5. +11 −0 spec.watchr
  6. +2 −0 spec/page_spec.rb
  7. +55 −0 spec/site_spec.rb
View
@@ -18,6 +18,10 @@
# several popular JSON parsing and decoding libraries.
require 'multi_json'
+# Next, load in some bits of Soundwave stored in other files:
+require 'soundwave/page'
+
+
#### Public Interface
module Soundwave
@@ -39,23 +43,50 @@ def initialize(source="./")
# The aptly named `generate` takes a `destination` path for the generated web pages.
def generate(destination)
- destination = Pathname(destination)
- # TODO: Expand to handle all files, not just Mustache.
- #
- # First, we find all of the `.mustache` files in the project and iterate over that list.
- # Files whose basenames start with an underscore (_) are skipped.
+ destination = Pathname(destination).expand_path
+ # First, we find all of the renderable files in the project and iterate over that list.
#
# Next, we create a [Page](./soundwave/page.html) object for each file and write it to
# the destination path.
- Dir[source.join("**","*.mustache")].each do |path|
- next if File.basename(path).to_s =~ /^_/
-
+ find_paths.each do |path|
page = Page.new(self, path)
page.write(destination.join(page.output_path))
end
end
- private
+ # `find_paths` returns a filtered list of all the pages/assets in the site.
+ def find_paths(dir="")
+ base = File.join(@source, dir)
+ entries = Dir.chdir(base) { filter_entries(Dir["*"]) }
+ paths = []
+
+ entries.each do |entry|
+ absolute_path = File.join(base, entry)
+ relative_path = File.join(dir, entry)
+
+ if File.directory?(absolute_path)
+ paths.concat find_paths(relative_path)
+ else
+ paths << absolute_path
+ end
+ end
+ paths
+ end
+
+ # `filter_entries` is a utility method for filtering out files/directories that should not be published:
+ #
+ # * Files starting with _ or #
+ # * Swap files ending in a tilde (~)
+ # * Symbolic links
+ def filter_entries(entries)
+ entries = entries.reject do |e|
+ unless ['.htaccess'].include?(e)
+ ['_', '#'].include?(e[0..0]) ||
+ e[-1..-1] == '~' ||
+ File.symlink?(e)
+ end
+ end
+ end
def data_trail
@_data_path ||= Hike::Trail.new(data_dir).tap do |t|
View
@@ -1,12 +1,21 @@
+#### Prerequisites
+
+# We need to require the `soundwave` lib, for the Site class
require 'soundwave'
-require 'tilt'
+
+# Template engines: Mustache for basic HTML pages, Tilt for more complex views/assets
require 'mustache'
+require 'tilt'
+
+#### Soundwave::Mustache
+# We need to subclass Mustache in order to implement our own partial-finding behavior.
module Soundwave
class Mustache < ::Mustache
attr_reader :page
- def initialize(page=nil)
+ # `Soundwave::Mustache` takes a `Page`, for its template source and partial path
+ def initialize(page)
@page = page
end
@@ -39,6 +48,8 @@ def partial(name)
end
end
+ #### Soundwave::Page
+
class Page
attr_reader :site, :path
attr_accessor :data
@@ -55,13 +66,6 @@ def initialize(site, path)
@data = {}
end
- # def template
- # if engine_extension
- # options = app.template_options[engine_extension] || nil
- # @template ||= Tilt.new(path, options, :outvar => '@_out_buf')
- # end
- # end
-
def basename
@basename ||= File.basename(path.to_s)
end
@@ -71,7 +75,6 @@ def extensions
end
def format_extension
- # If you don't want HTML, use multiple extensions
if extensions.length == 1 && DEFAULT_FORMATS[extensions[0]]
DEFAULT_FORMATS[extensions[0]]
elsif engine_extension
@@ -81,27 +84,19 @@ def format_extension
end
end
- # By convention, files that are to be processed should be named in Rails/Sprockets-like format, e.g. name.format.engine.
+ # By convention, files that are to be processed should be named in Rails/Sprockets-like format, e.g. `name.format.engine`.
def engine_extension
extensions.select { |e| ENGINES.include?(e) }[-1]
end
+ # This is used by `Soundwave::Server` to identify the appropriate content type for Rack serving.
def content_type
@content_type ||= begin
type = Rack::Mime.mime_type(format_extension)
type[/^text/] ? "#{type}; charset=utf-8" : type
end
end
- # def render(env, locals = {}, &block)
- # if template
- # template.render(app.context_for(self, env), locals, &block)
- # else
- # File.read(path)
- # end
- # end
-
-
def relative_path
@path.relative_path_from(site.source).to_s
end
@@ -138,6 +133,7 @@ def render
def write(destination)
destination = Pathname(destination)
puts "#{relative_path} => #{destination.relative_path_from(site.source)}"
+ FileUtils.mkdir_p(destination.dirname)
File.open(destination, "w") { |f| f.write(self.render) }
end
View
@@ -1,7 +1,14 @@
+#### Prerequisites
+
+# Require Soundwave's Site and Page objects.
require "soundwave"
require "soundwave/page"
+
+# Obviously we will need Rack.
require "rack"
+# `Soundwave::Server` provides a Rack app that will dynamically read, render, and output pages from a Soundwave site.
+
module Soundwave
class Server
View
@@ -22,11 +22,14 @@ Gem::Specification.new do |gem|
gem.add_runtime_dependency "hike"
gem.add_runtime_dependency "tilt"
+ gem.add_development_dependency "bundler"
+ gem.add_development_dependency "rspec", "~> 2.8.0"
+ gem.add_development_dependency "rake"
+ gem.add_development_dependency "fakefs"
+ gem.add_development_dependency "watchr"
gem.add_development_dependency "rocco"
gem.add_development_dependency "redcarpet", "~> 1.7.0"
- gem.add_development_dependency "rack"
gem.add_development_dependency "annotations"
- gem.add_development_dependency "bundler"
- gem.add_development_dependency "rspec", "~> 2.8.0"
+ gem.add_development_dependency "rack"
gem.add_development_dependency "sass", "~> 3.1.0"
end
View
@@ -0,0 +1,11 @@
+watch( 'lib/(.*)\.rb' ) do |md|
+ if md[0] == "lib/soundwave.rb"
+ system("bundle exec rake spec")
+ elsif File.exists?(spec_path = "spec/#{md[1]}.rb")
+ system "bundle exec rspec #{spec_path}"
+ end
+end
+
+watch( 'spec/(spec_helper|.*\_spec).rb') do |md|
+ system "bundle exec rspec #{md[0]}"
+end
View
@@ -5,6 +5,8 @@
let(:root_path) { Pathname(File.expand_path("../fixtures/site", __FILE__)) }
let(:site) { Soundwave::Site.new(root_path) }
+
+ # TODO: Write spec for #write()
describe "Mustache template" do
subject { Soundwave::Page.new(site, root_path.join("index.mustache")) }
View
@@ -0,0 +1,55 @@
+require "spec_helper"
+require "fakefs/safe"
+
+describe Soundwave::Site do
+
+ let(:root_path) { Pathname(File.expand_path("../fixtures/site", __FILE__)) }
+ let(:site) { Soundwave::Site.new(root_path) }
+
+ before do
+ Dir.chdir(root_path)
+ end
+
+ before(:all) { FileUtils.rm_rf("./_site") }
+ after(:all) { FileUtils.rm_rf("./_site") }
+
+ describe "generate" do
+ it "builds the web site" do
+ site.generate("./_site")
+ Dir["./_site/*"].should == ["./_site/index.html", "./_site/site.js", "./_site/styles.css"]
+ end
+ end
+
+ describe "find_paths" do
+ it "indexes pages and static files" do
+ paths = site.find_paths
+ paths.should have(3).items
+ paths.sort.should == ["index.mustache", "site.js", "styles.css.scss"].map { |p| File.expand_path("./#{p}") }
+ end
+ end
+
+ describe "filter_entries" do
+ it "filters out _files" do
+ site.filter_entries(["_file", "a"]).should == ["a"]
+ end
+ it "preserves .dotfiles" do
+ site.filter_entries([".rspec", "a"]).should == [".rspec", "a"]
+ end
+ it "filters out #files" do
+ site.filter_entries(["#wtf", "a"]).should == ["a"]
+ end
+ it "filters out files~" do
+ site.filter_entries(["vimblows~", "~tildesrule", "a"]).should == ["~tildesrule","a"]
+ end
+ it "filters out files listed in #exclude" do
+ pending
+ old_exclude = site.exclude
+ site.stub!(:exclude).and_return(old_exclude + ["Guardfile"])
+ site.filter_entries(["Guardfile", "a"]).should == ["a"]
+ end
+ it "does not filter out .htaccess" do
+ site.filter_entries([".htaccess", "a"]).should == [".htaccess", "a"]
+ end
+ end
+
+end

0 comments on commit 66e098e

Please sign in to comment.