soundwave 0.0.1 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -1,28 +1,10 @@
1
1
  #!/usr/bin/env rake
2
2
  require "bundler/gem_tasks"
3
- require 'rspec/core/rake_task'
3
+ # require 'rspec/core/rake_task'
4
+ # RSpec::Core::RakeTask.new(:spec)
4
5
 
5
- RSpec::Core::RakeTask.new(:spec)
6
+ require 'annotations/rake_task'
7
+ Annotations::RakeTask.new(:notes)
6
8
 
7
- task :default => :spec
9
+ task :default => [:notes]
8
10
 
9
- require './vendor/source_annotation_extractor'
10
-
11
- desc "Enumerate all annotations (use notes:optimize, :fixme, :todo for focus)"
12
- task :notes do
13
- SourceAnnotationExtractor.enumerate "OPTIMIZE|FIXME|TODO", :tag => true
14
- end
15
-
16
- namespace :notes do
17
- ["OPTIMIZE", "FIXME", "TODO"].each do |annotation|
18
- # desc "Enumerate all #{annotation} annotations"
19
- task annotation.downcase.intern do
20
- SourceAnnotationExtractor.enumerate annotation
21
- end
22
- end
23
-
24
- desc "Enumerate a custom annotation, specify with ANNOTATION=CUSTOM"
25
- task :custom do
26
- SourceAnnotationExtractor.enumerate ENV['ANNOTATION']
27
- end
28
- end
@@ -1,96 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'optparse'
4
- require 'soundwave'
5
-
6
- help = <<HELP
7
- Soundwave is a static site generator.
8
-
9
- soundwave #=> Builds a Soundwave site in the current directory
10
- soundwave init #=> Set up a new Soundwave site in the current directory
11
-
12
- HELP
13
-
14
- if ARGV.first == "init"
15
- ARGV.shift
16
- puts "soundwave init not yet implemented"
17
- exit(0)
18
- else
19
- # puts "Soundwave #{Soundwave::VERSION}"
20
- end
21
-
22
- options = {}
23
-
24
- opts = OptionParser.new do |opts|
25
- opts.summary_width = 24
26
- opts.banner = help
27
-
28
- # opts.on("-r", "--require LIBRARY", "Require the LIBRARY before doing anything") do |lib|
29
- # require lib
30
- # end
31
-
32
- # opts.on("-I DIRECTORY", "--include=DIRECTORY", "Adds the directory to the Sprockets load path") do |directory|
33
- # environment.append_path directory
34
- # end
35
-
36
- opts.on("-e", "--exclude [file]", "Skip files/directories named FILE when generating the site") do |file|
37
- Soundwave.config.exclude << file
38
- end
39
-
40
- opts.on("--version", "Display current version") do
41
- puts "Soundwave " + Soundwave::VERSION
42
- exit 0
43
- end
44
-
45
- opts.on("--watch", "Automatically regenerate when files are changed") do
46
- options["watch"] = true
47
- end
48
- end
49
-
50
- opts.parse!
51
-
52
- # TODO: Move into Soundwave proper and rename
53
- if File.exists?("./_soundwave.rb")
54
- require "./_soundwave.rb"
55
- end
56
-
57
- environment = Soundwave::Environment.new
58
-
59
- if options["watch"]
60
- require 'fssm'
61
- monitor = FSSM::Monitor.new #(:directories => true)
62
-
63
- entries = Dir.chdir(environment.root_dir.expand_path) { Dir.glob("*") }
64
- entries = environment.filter_entries(entries)
65
- entries.map! { |e| environment.root_dir.join(e) }
66
-
67
- directories = entries.select { |e| e.directory? }
68
- indiv_files = entries - directories
69
-
70
- # Also monitor the data and includes directories
71
- directories.push environment.data_dir
72
- directories.push environment.root_dir.join("_includes")
73
-
74
- directories.each do |dir_path|
75
- monitor.path(dir_path) do
76
- update { |base_dir, relative_path|
77
- environment.generate_site
78
- }
79
- end
80
- end
81
-
82
- indiv_files.each do |file_path|
83
- monitor.file(file_path) do
84
- update { |full_path, relative_dir|
85
- environment.generate_site
86
- }
87
- end
88
- end
89
-
90
- environment.generate_site
91
- puts "Now watching for changes in #{environment.root_dir}..."
92
- monitor.run
93
- else
94
- environment.generate_site
95
- exit(0)
96
- end
3
+ require 'soundwave/cli'
4
+ Soundwave::CLI.run
@@ -1,17 +1,112 @@
1
- require "soundwave/version"
2
- require "active_support/configurable"
3
- require "digest/md5"
1
+ require 'hike'
2
+ require 'mustache'
3
+ require 'multi_json'
4
4
 
5
5
  module Soundwave
6
- include ActiveSupport::Configurable
6
+ class Mustache < ::Mustache
7
+ attr_reader :page
7
8
 
8
- self.configure do |config|
9
- config.exclude = ["bin", "Gemfile", "Gemfile.lock"]
10
- config.static_extensions = %w(.jpg .jpeg .png .gif)
9
+ def initialize(page=nil)
10
+ @page = page
11
+ end
12
+
13
+ def site
14
+ @page.site
15
+ end
16
+
17
+ def template
18
+ @page.path.read
19
+ end
20
+
21
+ def partial_path(name)
22
+ name = name.to_s
23
+ dirname = File.dirname(name)
24
+ basename = File.basename(name)
25
+ partialname = "_#{basename}"
26
+ File.join(dirname, partialname)
27
+ end
28
+
29
+ def partial(name)
30
+ @paths ||= [@page.path.dirname, site.source.join("includes"), Dir.pwd].map(&:to_s).uniq
31
+ @trail ||= Hike::Trail.new(@page.site.source).tap do |t|
32
+ t.append_extension ".mustache"
33
+ t.append_paths *@paths
34
+ end
35
+
36
+ if path = @trail.find(partial_path(name))
37
+ File.read(path)
38
+ end
39
+ end
11
40
  end
12
41
 
13
- config_accessor :static_extensions
14
- end
42
+ class Site
43
+ attr_accessor :source, :destination
44
+
45
+ def initialize(source="./", destination="./_site")
46
+ @source = Pathname(source).expand_path
47
+ end
48
+
49
+ def generate(destination)
50
+ destination = Pathname(destination)
51
+ Dir[source.join("**","*.mustache")].each do |path|
52
+ next if File.basename(path).to_s =~ /^_/
53
+ page = Page.new(self, path)
54
+ page.write(destination.join(page.output_path))
55
+ end
56
+ end
57
+
58
+ def data_trail
59
+ @_data_path ||= Hike::Trail.new(source.join("_data")).tap do |t|
60
+ t.append_path "."
61
+ t.append_extension ".json"
62
+ t.append_extension ".yml", ".yaml"
63
+ end
64
+ end
65
+ end
15
66
 
16
- # Load all submodules
17
- Dir[File.expand_path("../soundwave/*.rb", __FILE__)].each { |f| require f }
67
+ class Page
68
+ attr_reader :site, :path
69
+
70
+ def initialize(site, path)
71
+ @site = site
72
+ @path = Pathname(path).expand_path
73
+ end
74
+
75
+ def relative_path
76
+ @path.relative_path_from(site.source).to_s
77
+ end
78
+
79
+ def output_path
80
+ relative_path.sub('.mustache','.html')
81
+ end
82
+
83
+ def base_path
84
+ relative_path.sub(".mustache",'')
85
+ end
86
+
87
+ def render
88
+ Soundwave::Mustache.new(self).render(@path.read, self.read_data)
89
+ end
90
+
91
+ def write(destination)
92
+ destination = Pathname(destination)
93
+ puts "#{relative_path} => #{destination.relative_path_from(site.source)}"
94
+ File.open(destination, "w") { |f| f.write(self.render) }
95
+ end
96
+
97
+ def read_data
98
+ if data_file = site.data_trail.find(base_path)
99
+ case File.extname(data_file)
100
+ when ".yml"
101
+ data = YAML.load(File.read(data_file))
102
+ when ".json"
103
+ data = MultiJson.decode(File.read(data_file))
104
+ else
105
+ data = {}
106
+ end
107
+ else
108
+ data = {}
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,67 @@
1
+ module Soundwave
2
+ module CLI
3
+ extend self
4
+
5
+ def run
6
+ require 'optparse'
7
+
8
+ help = <<HELP
9
+ Soundwave processes Mustache templates and YAML/JSON data into static web pages.
10
+
11
+ Usage:
12
+ - soundwave [options]
13
+ Processes all .mustache files in the current directory.
14
+
15
+ - soundwave [options] SOURCE [DESTINATION ...]
16
+ Processes all .mustache files in SOURCE, and outputs them to DESTINATION.
17
+ If DESTINATION is empty, defaults to SOURCE/_site.
18
+
19
+ - soundwave [options] FILENAME [DESTINATION ...]
20
+ Renders the Mustache template at FILENAME and writes it to DESTINATION
21
+ if given, or to STDOUT.
22
+
23
+ Options:
24
+ HELP
25
+
26
+ options = {}
27
+ opts = OptionParser.new do |opts|
28
+ opts.banner = help
29
+
30
+ opts.on("--version", "Display current version") do
31
+ puts "Soundwave " + Soundwave::VERSION
32
+ exit 0
33
+ end
34
+
35
+ opts.on("-i DIRECTORY", "--includes-dir=DIRECTORY", "Adds DIRECTORY to includes path") do |directory|
36
+ options[:include_dirs] ||= []
37
+ options[:include_dirs] << directory
38
+ end
39
+
40
+ opts.on("-d DIRECTORY", "--data-dir=DIRECTORY", "Adds DIRECTORY to data path") do |directory|
41
+ options[:data_dirs] ||= []
42
+ options[:data_dirs] << directory
43
+ end
44
+ end
45
+
46
+ opts.parse!
47
+
48
+ source = ARGV.shift || "./"
49
+
50
+ if File.directory?(source)
51
+ puts "Generating website in #{source}"
52
+ site = Site.new(source)
53
+ destination = ARGV.shift || site.source.join("_site")
54
+ site.generate(destination)
55
+ else
56
+ site = Site.new("./")
57
+ page = Page.new(site, source)
58
+ if destination = ARGV.shift
59
+ destination = site.source.join(destination)
60
+ page.write(destination)
61
+ else
62
+ puts page.render
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,23 @@
1
+ require 'rake'
2
+ require 'rake/tasklib'
3
+ require 'soundwave'
4
+
5
+ module Soundwave
6
+ class RakeTask < ::Rake::TaskLib
7
+ attr_accessor :name, :site, :destination
8
+
9
+ def initialize(name=:pages, source="./", destination="./_site")
10
+ @name = name
11
+ @site = Site.new(source)
12
+ @destination = Pathname(destination).expand_path
13
+ define
14
+ end
15
+
16
+ def define
17
+ desc "Build pages in #{@site.source}"
18
+ task(name) do
19
+ @site.generate(@destination)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -1,3 +1,3 @@
1
1
  module Soundwave
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -4,8 +4,8 @@ require File.expand_path('../lib/soundwave/version', __FILE__)
4
4
  Gem::Specification.new do |gem|
5
5
  gem.authors = ["David Demaree"]
6
6
  gem.email = ["ddemaree@gmail.com"]
7
- gem.description = %q{A simple static website generator}
8
- gem.summary = %q{A simple static website generator based on Tilt and Mustache}
7
+ gem.description = %q{Processes Mustache templates and YAML/JSON data into static web pages}
8
+ gem.summary = %q{A simple static website generator based on Mustache}
9
9
  gem.homepage = "http://github.com/ddemaree/soundwave"
10
10
 
11
11
  gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
@@ -19,9 +19,9 @@ Gem::Specification.new do |gem|
19
19
 
20
20
  gem.add_runtime_dependency "activesupport", ">= 3.1.0"
21
21
  gem.add_runtime_dependency "mustache"
22
- gem.add_runtime_dependency "tilt"
23
- gem.add_runtime_dependency "fssm"
22
+ gem.add_runtime_dependency "hike"
24
23
 
24
+ gem.add_development_dependency "annotations"
25
25
  gem.add_development_dependency "bundler"
26
26
  gem.add_development_dependency "rspec", "~> 2.8.0"
27
27
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: soundwave
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-02-17 00:00:00.000000000 Z
12
+ date: 2012-03-10 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
16
- requirement: &70334812682320 !ruby/object:Gem::Requirement
16
+ requirement: &70323690370480 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 3.1.0
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70334812682320
24
+ version_requirements: *70323690370480
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: mustache
27
- requirement: &70334812681460 !ruby/object:Gem::Requirement
27
+ requirement: &70323690370060 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70334812681460
35
+ version_requirements: *70323690370060
36
36
  - !ruby/object:Gem::Dependency
37
- name: tilt
38
- requirement: &70334812680460 !ruby/object:Gem::Requirement
37
+ name: hike
38
+ requirement: &70323690369600 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,21 +43,21 @@ dependencies:
43
43
  version: '0'
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *70334812680460
46
+ version_requirements: *70323690369600
47
47
  - !ruby/object:Gem::Dependency
48
- name: fssm
49
- requirement: &70334812679880 !ruby/object:Gem::Requirement
48
+ name: annotations
49
+ requirement: &70323690369180 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
- type: :runtime
55
+ type: :development
56
56
  prerelease: false
57
- version_requirements: *70334812679880
57
+ version_requirements: *70323690369180
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: bundler
60
- requirement: &70334812679220 !ruby/object:Gem::Requirement
60
+ requirement: &70323690368760 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: '0'
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *70334812679220
68
+ version_requirements: *70323690368760
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rspec
71
- requirement: &70334812678060 !ruby/object:Gem::Requirement
71
+ requirement: &70323690368260 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ~>
@@ -76,8 +76,8 @@ dependencies:
76
76
  version: 2.8.0
77
77
  type: :development
78
78
  prerelease: false
79
- version_requirements: *70334812678060
80
- description: A simple static website generator
79
+ version_requirements: *70323690368260
80
+ description: Processes Mustache templates and YAML/JSON data into static web pages
81
81
  email:
82
82
  - ddemaree@gmail.com
83
83
  executables:
@@ -85,35 +85,14 @@ executables:
85
85
  extensions: []
86
86
  extra_rdoc_files: []
87
87
  files:
88
- - .gitignore
89
- - .rspec
90
88
  - Gemfile
91
- - LICENSE
92
- - README.md
93
89
  - Rakefile
94
90
  - bin/soundwave
95
91
  - lib/soundwave.rb
96
- - lib/soundwave/document.rb
97
- - lib/soundwave/environment.rb
98
- - lib/soundwave/file_attributes.rb
99
- - lib/soundwave/mustache_template.rb
100
- - lib/soundwave/rendered_page.rb
101
- - lib/soundwave/static_file.rb
102
- - lib/soundwave/utils.rb
92
+ - lib/soundwave/cli.rb
93
+ - lib/soundwave/rake.rb
103
94
  - lib/soundwave/version.rb
104
95
  - soundwave.gemspec
105
- - spec/document_spec.rb
106
- - spec/environment_spec.rb
107
- - spec/file_attributes_spec.rb
108
- - spec/fixtures/site/_data/about.yml
109
- - spec/fixtures/site/_data/index.yml
110
- - spec/fixtures/site/about.mustache.erb
111
- - spec/fixtures/site/css/site.css.scss
112
- - spec/fixtures/site/index.mustache
113
- - spec/rendered_page_spec.rb
114
- - spec/soundwave_spec.rb
115
- - spec/spec_helper.rb
116
- - vendor/source_annotation_extractor.rb
117
96
  homepage: http://github.com/ddemaree/soundwave
118
97
  licenses: []
119
98
  post_install_message:
@@ -137,16 +116,5 @@ rubyforge_project:
137
116
  rubygems_version: 1.8.11
138
117
  signing_key:
139
118
  specification_version: 3
140
- summary: A simple static website generator based on Tilt and Mustache
141
- test_files:
142
- - spec/document_spec.rb
143
- - spec/environment_spec.rb
144
- - spec/file_attributes_spec.rb
145
- - spec/fixtures/site/_data/about.yml
146
- - spec/fixtures/site/_data/index.yml
147
- - spec/fixtures/site/about.mustache.erb
148
- - spec/fixtures/site/css/site.css.scss
149
- - spec/fixtures/site/index.mustache
150
- - spec/rendered_page_spec.rb
151
- - spec/soundwave_spec.rb
152
- - spec/spec_helper.rb
119
+ summary: A simple static website generator based on Mustache
120
+ test_files: []
data/.gitignore DELETED
@@ -1,17 +0,0 @@
1
- *.gem
2
- *.rbc
3
- .bundle
4
- .config
5
- .yardoc
6
- Gemfile.lock
7
- InstalledFiles
8
- _yardoc
9
- coverage
10
- doc/
11
- lib/bundler/man
12
- pkg
13
- rdoc
14
- spec/reports
15
- test/tmp
16
- test/version_tmp
17
- tmp
data/.rspec DELETED
@@ -1,2 +0,0 @@
1
- --color
2
- --format progress
data/LICENSE DELETED
@@ -1,22 +0,0 @@
1
- Copyright (c) 2012 David Demaree
2
-
3
- MIT License
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining
6
- a copy of this software and associated documentation files (the
7
- "Software"), to deal in the Software without restriction, including
8
- without limitation the rights to use, copy, modify, merge, publish,
9
- distribute, sublicense, and/or sell copies of the Software, and to
10
- permit persons to whom the Software is furnished to do so, subject to
11
- the following conditions:
12
-
13
- The above copyright notice and this permission notice shall be
14
- included in all copies or substantial portions of the Software.
15
-
16
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md DELETED
@@ -1,31 +0,0 @@
1
- # Soundwave
2
-
3
- Soundwave is a simple static file generator. Its primary use is for building static web sites, and in that sense it's similar to Tom Preston-Werner's Jekyll. But Soundwave can also bootstrap web application projects, or basically do anything that involves processing data or copying a directory structure lots and lots of times.
4
-
5
- ## Installation
6
-
7
- TODO: This isn't really a Rails plugin, so you wouldn't necessarily put it in a Gemfile.
8
-
9
- Add this line to your application's Gemfile:
10
-
11
- gem 'soundwave'
12
-
13
- And then execute:
14
-
15
- $ bundle
16
-
17
- Or install it yourself as:
18
-
19
- $ gem install soundwave
20
-
21
- ## Usage
22
-
23
- TODO: Write usage instructions here
24
-
25
- ## Contributing
26
-
27
- 1. Fork it
28
- 2. Create your feature branch (`git checkout -b my-new-feature`)
29
- 3. Commit your changes (`git commit -am 'Added some feature'`)
30
- 4. Push to the branch (`git push origin my-new-feature`)
31
- 5. Create new Pull Request
@@ -1,34 +0,0 @@
1
- module Soundwave
2
- class Document
3
-
4
- attr_reader :mtime, :pathname, :logical_path
5
-
6
- def initialize(env, logical_path, absolute_path=nil)
7
- @env = env
8
- @logical_path = logical_path
9
- @pathname = Pathname(absolute_path || env.root_dir.join(logical_path))
10
- refresh
11
- end
12
-
13
- def output_path
14
- @env.output_dir.join(@logical_path)
15
- end
16
-
17
- def changed?
18
- @mtime != @pathname.stat.mtime
19
- true
20
- end
21
-
22
- def refresh
23
- @mtime = @pathname.stat.mtime
24
- end
25
-
26
- def file_attributes
27
- @env.attributes_for(self.pathname)
28
- end
29
-
30
- def write
31
- # Stub: override this in a subclass
32
- end
33
- end
34
- end
@@ -1,102 +0,0 @@
1
- require 'active_support/callbacks'
2
- require 'tilt'
3
-
4
- module Soundwave
5
- class Environment
6
- include ActiveSupport::Callbacks
7
- define_callbacks :read
8
- define_callbacks :generate
9
-
10
- attr_accessor :root_dir, :output_dir, :exclude, :data_dir
11
-
12
- def initialize(root="./")
13
- @root_dir = Pathname(root).expand_path
14
- @exclude = Soundwave.config.exclude
15
- @output_dir ||= @root_dir.join("_site")
16
- @data_dir ||= @root_dir.join("_data")
17
-
18
- register_engine ".mustache", Soundwave::MustacheTemplate
19
- register_engine ".scss", Tilt::ScssTemplate
20
- register_engine ".erb", Tilt::ERBTemplate
21
- end
22
-
23
- def site_data
24
- # TODO: Implement something for getting global/site-level data from _data/_site.(yml|json)
25
- {}
26
- end
27
-
28
- def pages
29
- @pages ||= {}
30
- end
31
-
32
- def engines(ext)
33
- @engines[ext]
34
- end
35
-
36
- def register_engine(ext, engine)
37
- @engines ||= {}
38
- @engines[ext] = engine
39
- end
40
-
41
- def attributes_for(pathname)
42
- FileAttributes.new(self, pathname)
43
- end
44
-
45
- def build_page(logical_path, pathname)
46
- attrs = attributes_for(pathname)
47
-
48
- if attrs.engines.any?
49
- RenderedPage.new(self, logical_path, pathname)
50
- else
51
- StaticFile.new(self, logical_path, pathname)
52
- end
53
- end
54
-
55
- def read_directories(dir='')
56
- base = File.join(self.root_dir, dir)
57
- entries = Dir.chdir(base) { filter_entries(Dir['*']) }
58
- entries.each do |entry|
59
- absolute_path = File.join(base, entry)
60
- relative_path = File.join(dir, entry)
61
- logical_path = absolute_path.sub(self.root_dir.to_s, "")
62
-
63
- if File.directory?(absolute_path)
64
- read_directories(relative_path)
65
- else
66
- pathname = Pathname(absolute_path).expand_path
67
- attrs = attributes_for(pathname)
68
- logical_path = attrs.logical_path
69
- pages[logical_path] = build_page(logical_path, pathname)
70
- end
71
- end
72
- end
73
-
74
- def generate
75
- run_callbacks(:read) do
76
- read_directories
77
- end
78
- run_callbacks(:generate) do
79
- @pages.each do |logical_path, page|
80
- puts "#{logical_path} => #{page.output_path}"
81
- page.write
82
- end
83
- end
84
- end
85
-
86
- # Deprecated: Alias to #generate for the two extant projects that use this code
87
- alias_method :generate_site, :generate
88
-
89
- # Exclude .dotfiles, _underscores, #hashes, ~tildes, paths in @exclude
90
- # and symlinks, EXCEPT for .htaccess
91
- def filter_entries(entries)
92
- entries = entries.reject do |e|
93
- unless ['.htaccess'].include?(e)
94
- ['.', '_', '#'].include?(e[0..0]) ||
95
- e[-1..-1] == '~' ||
96
- self.exclude.include?(e) ||
97
- File.symlink?(e)
98
- end
99
- end
100
- end
101
- end
102
- end
@@ -1,56 +0,0 @@
1
- require 'pathname'
2
-
3
- module Soundwave
4
- class FileAttributes
5
- attr_reader :environment, :pathname
6
-
7
- def initialize(environment, pathname)
8
- @environment = environment
9
- @pathname = Pathname(pathname)
10
- end
11
-
12
- def logical_path
13
- if root_path = pathname.expand_path.to_s[environment.root_dir.to_s]
14
- path = pathname.to_s.sub("#{root_path}/", '')
15
- path = pathname.expand_path.relative_path_from(Pathname.new(root_path)).to_s
16
- path = engine_extensions.inject(path) { |p, ext| p.sub(ext, '') }
17
- path = "#{path}#{engine_format_extension}" unless format_extension
18
- path
19
- else
20
- raise "File outside paths"
21
- end
22
- end
23
-
24
- def extensions
25
- @extensions ||= @pathname.basename.to_s.scan(/\.[^.]+/)
26
- end
27
-
28
- def format_extension
29
- extensions.reverse.detect { |ext|
30
- # TODO: Environment may need a mime types registry
31
- !@environment.engines(ext)
32
- }
33
- end
34
-
35
- def engine_extensions
36
- exts = extensions
37
-
38
- if offset = extensions.index(format_extension)
39
- exts = extensions[offset+1..-1]
40
- end
41
-
42
- exts.select { |ext| @environment.engines(ext) }
43
- end
44
-
45
- def engines
46
- engine_extensions.map { |ext| @environment.engines(ext) }
47
- end
48
-
49
- private
50
-
51
- def engine_format_extension
52
- # TODO: Engines should provide a default extension, this should be engines.first.default_extension
53
- ".html"
54
- end
55
- end
56
- end
@@ -1,32 +0,0 @@
1
- require 'mustache'
2
- require 'pathname'
3
-
4
- module Soundwave
5
- # Custom wrapper for Mustache, to enforce Soundwave conventions w/r/t
6
- # template and partial naming and provide a Tilt-like interface.
7
- class MustacheTemplate < ::Mustache
8
-
9
- # Public: Initializes a new MustacheTemplate
10
- #
11
- # pathname - Pathname for the template file
12
- #
13
- # Returns a MustacheTemplate object.
14
- def initialize(pathname)
15
- @pathname = Pathname(pathname)
16
- end
17
-
18
- # Public: Reads the template file from disk and returns its
19
- # contents as a String.
20
- def template
21
- @pathname.read
22
- end
23
-
24
- # Public: Reads the partial template with the given name and
25
- # returns its contents.
26
- def partial(name)
27
- # TODO: Make this a teeny bit more robust?
28
- name = '_includes/' + name.to_s
29
- super
30
- end
31
- end
32
- end
@@ -1,69 +0,0 @@
1
- require 'mustache'
2
-
3
- module Soundwave
4
- class RenderedPage < Document
5
-
6
- attr_accessor :data
7
- attr_reader :output
8
-
9
- def initialize(env, logical_path, pathname)
10
- super
11
- @data = {}
12
- read_data # is this necessary?
13
- end
14
-
15
- def output_path
16
- @env.output_dir.join(logical_path)
17
- end
18
-
19
- def mustache
20
- MustacheTemplate.new(@pathname)
21
- end
22
-
23
- def render
24
- read_data
25
- result = pathname.read
26
- processors.each do |processor|
27
- template = processor.new(pathname.to_s) { result }
28
- result = template.render(@data)
29
- end
30
-
31
- @output ||= result
32
- result
33
- end
34
- alias_method :to_s, :render
35
-
36
- def write
37
- if changed?
38
- FileUtils.mkdir_p(output_path.dirname)
39
- File.open(output_path, "w") { |f| f.write(self.render()) }
40
- end
41
- end
42
-
43
- protected
44
-
45
- def processors
46
- file_attributes.engines
47
- end
48
-
49
- def read_data
50
- # Get site data
51
- @data = @env.site_data || {}
52
-
53
- basepath = file_attributes.logical_path.sub(/\..+/, '')
54
- data_file = @env.data_dir.join(basepath + ".yml")
55
-
56
- if File.exists?(data_file)
57
- page_data = YAML.load_file(data_file)
58
- else
59
- page_data = {}
60
- end
61
-
62
- @data.merge!(page_data)
63
-
64
- # TODO: YAML frontmatter?
65
-
66
- @data
67
- end
68
- end
69
- end
@@ -1,10 +0,0 @@
1
- module Soundwave
2
- class StaticFile < Soundwave::Document
3
- def write
4
- if changed?
5
- FileUtils.mkdir_p(output_path.dirname.to_s)
6
- FileUtils.cp(@pathname, output_path)
7
- end
8
- end
9
- end
10
- end
@@ -1,22 +0,0 @@
1
- module Soundwave
2
- module Utils
3
-
4
- # Prepends a leading "." to an extension if its missing.
5
- #
6
- # normalize_extension("js")
7
- # # => ".js"
8
- #
9
- # normalize_extension(".css")
10
- # # => ".css"
11
- #
12
- def self.normalize_extension(extension)
13
- extension = extension.to_s
14
- if extension[/^\./]
15
- extension
16
- else
17
- ".#{extension}"
18
- end
19
- end
20
-
21
- end
22
- end
@@ -1,40 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Soundwave::Document do
4
-
5
- let(:root_dir) { File.expand_path("../fixtures/site", __FILE__) }
6
- let(:environment) { Soundwave::Environment.new(root_dir) }
7
- let(:pathname) { Pathname(File.join(root_dir, "index.mustache")) }
8
- let(:document) { Soundwave::Document.new(environment, "index.html", pathname) }
9
-
10
- describe "change tracking" do
11
- it "stores the file's mtime at initialization" do
12
- document.mtime.should == pathname.stat.mtime
13
- end
14
- describe "changed?" do
15
- it "compares the current mtime with the cached value" do
16
- document
17
- sleep 1
18
- FileUtils.touch pathname
19
- document.should be_changed
20
- end
21
- end
22
- describe "refresh" do
23
- it "updates the stored mtime" do
24
- document
25
- sleep 1
26
- FileUtils.touch pathname
27
- document.refresh
28
- document.should_not be_changed
29
- end
30
- end
31
- end
32
-
33
- describe "output path" do
34
- it "is joined on the env's output dir" do
35
- document = Soundwave::Document.new(environment, "index.html", pathname)
36
- document.output_path.to_s.should == File.join(root_dir, "_site", "index.html")
37
- end
38
- end
39
-
40
- end
@@ -1,54 +0,0 @@
1
- require "spec_helper"
2
-
3
- describe Soundwave::Environment do
4
- let(:environment) { Soundwave::Environment.new }
5
-
6
- before do
7
- FileUtils.cd File.expand_path("../fixtures/site", __FILE__)
8
- end
9
-
10
- describe "initialization" do
11
- it "creates an environment for the current directory" do
12
- env = Soundwave::Environment.new
13
- env.root_dir.expand_path.to_s.should == Dir.pwd
14
- end
15
- it "allows the root directory to be set" do
16
- env = Soundwave::Environment.new("/tmp/soundwave")
17
- env.root_dir.expand_path.to_s.should == "/tmp/soundwave"
18
- end
19
- end
20
-
21
- # Indexes the content of the site and stores it as @paths
22
- describe "read_directories" do
23
- it "indexes pages and static files" do
24
- environment.read_directories
25
- pages = environment.instance_variable_get("@pages")
26
- pages.keys.should have(3).items
27
- pages.keys.sort.should == ["about.html", "css/site.css", "index.html"]
28
- end
29
- end
30
-
31
- describe "filter_entries" do
32
- it "filters out _files" do
33
- environment.filter_entries(["_file", "a"]).should == ["a"]
34
- end
35
- it "filters out .files" do
36
- environment.filter_entries([".rspec", "a"]).should == ["a"]
37
- end
38
- it "filters out #files" do
39
- environment.filter_entries(["#wtf", "a"]).should == ["a"]
40
- end
41
- it "filters out files~" do
42
- environment.filter_entries(["vimblows~", "~tildesrule", "a"]).should == ["~tildesrule","a"]
43
- end
44
- it "filters out files listed in #exclude" do
45
- old_exclude = environment.exclude
46
- environment.stub!(:exclude).and_return(old_exclude + ["Guardfile"])
47
- environment.filter_entries(["Guardfile", "a"]).should == ["a"]
48
- end
49
- it "does not filter out .htaccess" do
50
- environment.filter_entries([".htaccess", "a"]).should == [".htaccess", "a"]
51
- end
52
- end
53
-
54
- end
@@ -1,41 +0,0 @@
1
- require "spec_helper"
2
-
3
- describe Soundwave::FileAttributes do
4
- let(:root_dir) { File.expand_path("../fixtures/site", __FILE__) }
5
- let(:environment) { Soundwave::Environment.new(root_dir) }
6
-
7
- def attributes_for(pathname)
8
- environment.attributes_for(pathname)
9
- end
10
-
11
- before do
12
- FileUtils.cd(root_dir)
13
- end
14
-
15
- describe "logical paths" do
16
- it "are relative to the env's root_dir" do
17
- attributes_for("about/index.html").logical_path.should == "about/index.html"
18
- end
19
- it "strip engine extensions" do
20
- attributes_for("about.html.mustache").logical_path.should == "about.html"
21
- end
22
- it "append a default format extension if necessary" do
23
- attributes_for("about.mustache").logical_path.should == "about.html"
24
- end
25
- end
26
-
27
- describe "extensions" do
28
- it "gets an array of all extensions" do
29
- attributes_for("index.html.mustache").extensions.should == [".html", ".mustache"]
30
- end
31
- it "gets the format extension" do
32
- attributes_for("index.html.mustache").format_extension.should == ".html"
33
- end
34
- it "gets the engine extension(s)" do
35
- attributes_for("index.html.mustache").engine_extensions.should == [".mustache"]
36
- end
37
- it "allows pathnames with just an engine extension" do
38
- attributes_for("about.scss").engine_extensions.should == [".scss"]
39
- end
40
- end
41
- end
@@ -1 +0,0 @@
1
- page_title: "About this site"
@@ -1 +0,0 @@
1
- page_title: "Hello World!"
@@ -1 +0,0 @@
1
- <%= "Title: {{ page_title }}" %>
File without changes
@@ -1,6 +0,0 @@
1
- <html>
2
- <head><title>{{page_title}}</title></head>
3
- <body>
4
- <h1>{{page_title}}</h1>
5
- </body>
6
- </html>
@@ -1,49 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Soundwave::RenderedPage do
4
- let(:root_dir) { Pathname(File.expand_path("../fixtures/site", __FILE__)) }
5
- let(:environment) { Soundwave::Environment.new(root_dir) }
6
-
7
- let(:pathname) { root_dir.join("index.mustache") }
8
- let(:data_pathname) { root_dir.join("_data", "index.yml") }
9
- let(:page) { Soundwave::RenderedPage.new(environment, "index.html", pathname) }
10
-
11
- before do
12
- FileUtils.cd(root_dir)
13
- end
14
-
15
- describe "with data" do
16
- it "reads YAML data file on initialization" do
17
- page.data.should == {"page_title" => "Hello World!"}
18
- end
19
- it "sets mtime to data file's mtime if it is later" do
20
- pending "Need to add dependency tracking to RenderedPage"
21
- FileUtils.touch(pathname)
22
- sleep 1
23
- FileUtils.touch(data_pathname)
24
-
25
- page.mtime.should == data_pathname.stat.mtime
26
- end
27
- end
28
-
29
- describe "rendering" do
30
- it "renders content with data" do
31
- output = page.render
32
- output.should == <<-HTML
33
- <html>
34
- <head><title>Hello World!</title></head>
35
- <body>
36
- <h1>Hello World!</h1>
37
- </body>
38
- </html>
39
- HTML
40
- end
41
- it "can chain multiple engines" do
42
- page = Soundwave::RenderedPage.new(environment, "index.html", root_dir.join("about.mustache.erb"))
43
- page.data = {"page_title" => "This is page title"}
44
- output = page.render
45
- output.should == "Title: About this site"
46
- end
47
- end
48
-
49
- end
@@ -1,4 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Soundwave do
4
- end
@@ -1,16 +0,0 @@
1
- # This file was generated by the `rspec --init` command. Conventionally, all
2
- # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
- # Require this file using `require "spec_helper.rb"` to ensure that it is only
4
- # loaded once.
5
- #
6
- # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
-
8
- # require 'bundler'
9
- # Bundler.setup
10
- require 'soundwave'
11
-
12
- RSpec.configure do |config|
13
- config.treat_symbols_as_metadata_keys_with_true_values = true
14
- config.run_all_when_everything_filtered = true
15
- config.filter_run :focus
16
- end
@@ -1,102 +0,0 @@
1
- # Implements the logic behind the rake tasks for annotations like
2
- #
3
- # rake notes
4
- # rake notes:optimize
5
- #
6
- # and friends. See <tt>rake -T notes</tt> and <tt>railties/lib/tasks/annotations.rake</tt>.
7
- #
8
- # Annotation objects are triplets <tt>:line</tt>, <tt>:tag</tt>, <tt>:text</tt> that
9
- # represent the line where the annotation lives, its tag, and its text. Note
10
- # the filename is not stored.
11
- #
12
- # Annotations are looked for in comments and modulus whitespace they have to
13
- # start with the tag optionally followed by a colon. Everything up to the end
14
- # of the line (or closing ERB comment tag) is considered to be their text.
15
- class SourceAnnotationExtractor
16
- class Annotation < Struct.new(:line, :tag, :text)
17
-
18
- # Returns a representation of the annotation that looks like this:
19
- #
20
- # [126] [TODO] This algorithm is simple and clearly correct, make it faster.
21
- #
22
- # If +options+ has a flag <tt>:tag</tt> the tag is shown as in the example above.
23
- # Otherwise the string contains just line and text.
24
- def to_s(options={})
25
- s = "[%3d] " % line
26
- s << "[#{tag}] " if options[:tag]
27
- s << text
28
- end
29
- end
30
-
31
- # Prints all annotations with tag +tag+ under the root directories +app+, +lib+,
32
- # and +test+ (recursively). Only filenames with extension +.builder+, +.rb+,
33
- # +.rxml+, +.rhtml+, or +.erb+ are taken into account. The +options+
34
- # hash is passed to each annotation's +to_s+.
35
- #
36
- # This class method is the single entry point for the rake tasks.
37
- def self.enumerate(tag, options={})
38
- extractor = new(tag)
39
- extractor.display(extractor.find, options)
40
- end
41
-
42
- attr_reader :tag
43
-
44
- def initialize(tag)
45
- @tag = tag
46
- end
47
-
48
- # Returns a hash that maps filenames under +dirs+ (recursively) to arrays
49
- # with their annotations. Only files with annotations are included, and only
50
- # those with extension +.builder+, +.rb+, +.rxml+, +.rhtml+, and +.erb+
51
- # are taken into account.
52
- def find(dirs=%w(app lib test))
53
- dirs.inject({}) { |h, dir| h.update(find_in(dir)) }
54
- end
55
-
56
- # Returns a hash that maps filenames under +dir+ (recursively) to arrays
57
- # with their annotations. Only files with annotations are included, and only
58
- # those with extension +.builder+, +.rb+, +.rxml+, +.rhtml+, and +.erb+
59
- # are taken into account.
60
- def find_in(dir)
61
- results = {}
62
-
63
- Dir.glob("#{dir}/*") do |item|
64
- next if File.basename(item)[0] == ?.
65
-
66
- if File.directory?(item)
67
- results.update(find_in(item))
68
- elsif item =~ /\.(builder|(r(?:b|xml|js)))$/
69
- results.update(extract_annotations_from(item, /#\s*(#{tag}):?\s*(.*)$/))
70
- elsif item =~ /\.(rhtml|erb)$/
71
- results.update(extract_annotations_from(item, /<%\s*#\s*(#{tag}):?\s*(.*?)\s*%>/))
72
- end
73
- end
74
-
75
- results
76
- end
77
-
78
- # If +file+ is the filename of a file that contains annotations this method returns
79
- # a hash with a single entry that maps +file+ to an array of its annotations.
80
- # Otherwise it returns an empty hash.
81
- def extract_annotations_from(file, pattern)
82
- lineno = 0
83
- result = File.readlines(file).inject([]) do |list, line|
84
- lineno += 1
85
- next list unless line =~ pattern
86
- list << Annotation.new(lineno, $1, $2)
87
- end
88
- result.empty? ? {} : { file => result }
89
- end
90
-
91
- # Prints the mapping from filenames to annotations in +results+ ordered by filename.
92
- # The +options+ hash is passed to each annotation's +to_s+.
93
- def display(results, options={})
94
- results.keys.sort.each do |file|
95
- puts "#{file}:"
96
- results[file].each do |note|
97
- puts " * #{note.to_s(options)}"
98
- end
99
- puts
100
- end
101
- end
102
- end