pith 0.0.9 → 0.0.10
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +29 -18
- data/features/cleanup.feature +14 -0
- data/features/step_definitions/build_steps.rb +4 -0
- data/features/step_definitions/build_steps.rb~ +12 -0
- data/features/support/env.rb +9 -1
- data/features/support/env.rb~ +9 -1
- data/lib/pith/console_logger.rb~ +0 -4
- data/lib/pith/input/abstract.rb +1 -1
- data/lib/pith/input/base.rb~ +74 -0
- data/lib/pith/input/resource.rb +2 -0
- data/lib/pith/input/resource.rb~ +41 -0
- data/lib/pith/input/template.rb +2 -0
- data/lib/pith/input/template.rb~ +9 -3
- data/lib/pith/input.rb +2 -0
- data/lib/pith/input.rb~ +6 -4
- data/lib/pith/pathname_ext.rb +4 -2
- data/lib/pith/pathname_ext.rb~ +16 -0
- data/lib/pith/plugins/publication/input.rb~ +50 -0
- data/lib/pith/plugins/publication/project.rb +2 -0
- data/lib/pith/plugins/publication/project.rb~ +14 -0
- data/lib/pith/plugins/publication.rb~ +2 -0
- data/lib/pith/project.rb +21 -6
- data/lib/pith/project.rb~ +43 -11
- data/lib/pith/reference_error.rb~ +5 -0
- data/lib/pith/render_context.rb~ +36 -25
- data/lib/pith/server.rb~ +17 -12
- data/lib/pith/version.rb +1 -1
- data/lib/pith/version.rb~ +1 -1
- data/lib/pith/watcher.rb~ +32 -0
- data/spec/pith/input/template_spec.rb~ +70 -0
- data/spec/pith/plugins/publication_spec.rb~ +39 -0
- data/spec/pith/project_spec.rb~ +28 -6
- data/spec/spec_helper.rb~ +0 -10
- metadata +18 -4
data/README.markdown
CHANGED
@@ -30,7 +30,7 @@ Create an input directory for your site (wherever you want), and pop some files
|
|
30
30
|
logo.png
|
31
31
|
index.html.haml
|
32
32
|
|
33
|
-
The only requirement is the existance of a
|
33
|
+
The only requirement is the existance of a subdirectory called "`_pith`". Pith checks that it's present, to prevent you accidently treating your entire home directory as website input.
|
34
34
|
|
35
35
|
Next, use the `pith build` command to convert your inputs into a functioning website.
|
36
36
|
|
@@ -39,7 +39,16 @@ Next, use the `pith build` command to convert your inputs into a functioning web
|
|
39
39
|
--(copy)--> images/logo.png
|
40
40
|
--(haml)--> index.html
|
41
41
|
|
42
|
-
|
42
|
+
By default, output is generated into an subdirectory called "`_out`", inside the input directory ... but the default can be easily overridden, e.g.
|
43
|
+
|
44
|
+
$ pith -i SITE -o OUTPUT build
|
45
|
+
|
46
|
+
Templates
|
47
|
+
---------
|
48
|
+
|
49
|
+
Files in the input directory are considered to be "templates" if the file name ends with a template format extension recognised as such by [Tilt][tilt], e.g. "`.haml`", or "`.textile`". These will be evaluated dynamically. Pith strips the format extension off the file name when generating output.
|
50
|
+
|
51
|
+
Formats supported by Tilt include:
|
43
52
|
|
44
53
|
- [Markdown](http://daringfireball.net/projects/markdown/) (`markdown`, `md`)
|
45
54
|
- [Textile](http://redcloth.org/hobix.com/textile/) (`textile`)
|
@@ -48,7 +57,12 @@ Any input file with an extension recognised by [Tilt][tilt] is considered to be
|
|
48
57
|
- [Sass][sass] (`sass`)
|
49
58
|
- [CoffeeScript](http://jashkenas.github.com/coffee-script/) (`coffee`)
|
50
59
|
|
51
|
-
|
60
|
+
Any non-template input files (we call them "resources") are just copied verbatim into the output directory.
|
61
|
+
|
62
|
+
Ignored files
|
63
|
+
-------------
|
64
|
+
|
65
|
+
Files or directories beginning with an underscore are ignored; that is, we don't generate corresponding output files. They can still be used as "layout" or "partial" templates though; see below.
|
52
66
|
|
53
67
|
Page metadata
|
54
68
|
-------------
|
@@ -69,18 +83,16 @@ Metadata provided in the header can be referenced by template content, via the "
|
|
69
83
|
|
70
84
|
This is especially useful in "layout" templates (see below).
|
71
85
|
|
72
|
-
Partials
|
73
|
-
|
86
|
+
Partials and Layouts
|
87
|
+
--------------------
|
74
88
|
|
75
89
|
Templates can include other templates, e.g.
|
76
90
|
|
77
|
-
= include
|
78
|
-
|
79
|
-
Note the leading underscore ("_"). Any input file (or directory) beginning with an underscore is ignored when generating outputs.
|
91
|
+
= include "_header.haml"
|
80
92
|
|
81
93
|
When including, you can pass local variables, e.g.
|
82
94
|
|
83
|
-
= include
|
95
|
+
= include "_list.haml", :items => [1,2,3]
|
84
96
|
|
85
97
|
which can be accessed in the included template:
|
86
98
|
|
@@ -88,15 +100,12 @@ which can be accessed in the included template:
|
|
88
100
|
- items.each do |i|
|
89
101
|
%li= i
|
90
102
|
|
91
|
-
|
92
|
-
-------
|
93
|
-
|
94
|
-
Layout templates are a bit like partials, except that they take a block, e.g.
|
103
|
+
In Haml templates, you can also pass a block, e.g.
|
95
104
|
|
96
|
-
=
|
105
|
+
= include "_mylayout.haml" do
|
97
106
|
%p Some content
|
98
107
|
|
99
|
-
|
108
|
+
and access it in the template using "`yield`":
|
100
109
|
|
101
110
|
!!!
|
102
111
|
%html
|
@@ -106,14 +115,16 @@ Use "`yield`" to embed the block's content within the layout:
|
|
106
115
|
.main
|
107
116
|
= yield
|
108
117
|
|
109
|
-
|
118
|
+
This way, any template can be used as a "layout".
|
119
|
+
|
120
|
+
Layouts can also be applied by using a "`layout`" entry in the page header, e.g.
|
110
121
|
|
111
122
|
---
|
112
123
|
layout: "/_mylayout.haml"
|
113
124
|
...
|
114
125
|
|
115
126
|
Some content
|
116
|
-
|
127
|
+
|
117
128
|
Relative links
|
118
129
|
--------------
|
119
130
|
|
@@ -123,7 +134,7 @@ It's sensible to use relative URIs when linking to other pages (and resources) i
|
|
123
134
|
|
124
135
|
%img{:src => href("/images/logo.png")}
|
125
136
|
|
126
|
-
Any path beginning with a slash ("/") is resolved relative to the root of the site; anything else is
|
137
|
+
Any path beginning with a slash ("/") is resolved relative to the root of the site; anything else is resolved relative to the current input-file (even if that happens to be a layout or partial). Either way, "`href`" always returns a relative link.
|
127
138
|
|
128
139
|
There's also a "`link`" function, for even easier hyper-linking:
|
129
140
|
|
@@ -0,0 +1,14 @@
|
|
1
|
+
Feature: automatic cleanup
|
2
|
+
|
3
|
+
I want old outputs removed from the output directory
|
4
|
+
So that they don't hang around
|
5
|
+
|
6
|
+
Scenario: output without matching input
|
7
|
+
|
8
|
+
Given input file "blah.txt" exists
|
9
|
+
|
10
|
+
When I build the site
|
11
|
+
And I remove input file "blah.txt"
|
12
|
+
And I rebuild the site
|
13
|
+
|
14
|
+
Then output file "blah.txt" should not exist
|
@@ -30,6 +30,10 @@ When /^I change input file "([^\"]*)" to contain$/ do |path, content|
|
|
30
30
|
@inputs[path] = content
|
31
31
|
end
|
32
32
|
|
33
|
+
When /^I remove input file "([^\"]*)"$/ do |path|
|
34
|
+
@inputs[path] = nil
|
35
|
+
end
|
36
|
+
|
33
37
|
When /^I (?:re)?build the site$/ do
|
34
38
|
@project.logger.clear
|
35
39
|
@project.build
|
@@ -14,6 +14,14 @@ Given "the site is up-to-date" do
|
|
14
14
|
When "I build the site"
|
15
15
|
end
|
16
16
|
|
17
|
+
Given /^the "([^\"]*)" flag is enabled$/ do |flag|
|
18
|
+
@project.send("#{flag}=", true)
|
19
|
+
end
|
20
|
+
|
21
|
+
Given /^the "([^\"]*)" flag is disabled$/ do |flag|
|
22
|
+
@project.send("#{flag}=", false)
|
23
|
+
end
|
24
|
+
|
17
25
|
When /^I change input file "([^\"]*)" to contain "([^\"]*)"$/ do |path, content|
|
18
26
|
@inputs[path] = content
|
19
27
|
end
|
@@ -22,6 +30,10 @@ When /^I change input file "([^\"]*)" to contain$/ do |path, content|
|
|
22
30
|
@inputs[path] = content
|
23
31
|
end
|
24
32
|
|
33
|
+
When /^I remove input file "([^\"]*)"$/ do |path|
|
34
|
+
@inputs[path] = nil
|
35
|
+
end
|
36
|
+
|
25
37
|
When /^I (?:re)?build the site$/ do
|
26
38
|
@project.logger.clear
|
27
39
|
@project.build
|
data/features/support/env.rb
CHANGED
@@ -12,7 +12,15 @@ class DirHash
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def []=(file, content)
|
15
|
-
|
15
|
+
if content.nil?
|
16
|
+
remove(file)
|
17
|
+
else
|
18
|
+
write(file, content)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def remove(file)
|
23
|
+
(@dir + file).unlink
|
16
24
|
end
|
17
25
|
|
18
26
|
def write(file, content, options = {})
|
data/features/support/env.rb~
CHANGED
@@ -12,7 +12,15 @@ class DirHash
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def []=(file, content)
|
15
|
-
|
15
|
+
if content.nil?
|
16
|
+
remove(file)
|
17
|
+
else
|
18
|
+
write(file, content)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def remove(file)
|
23
|
+
(@dir + file).unlink
|
16
24
|
end
|
17
25
|
|
18
26
|
def write(file, content, options = {})
|
data/lib/pith/console_logger.rb~
CHANGED
data/lib/pith/input/abstract.rb
CHANGED
@@ -0,0 +1,74 @@
|
|
1
|
+
require "pathname"
|
2
|
+
|
3
|
+
module Pith
|
4
|
+
module Input
|
5
|
+
|
6
|
+
class Abstract
|
7
|
+
|
8
|
+
def initialize(project, path)
|
9
|
+
@project = project
|
10
|
+
@path = path
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_reader :project, :path
|
14
|
+
|
15
|
+
# Public: Get the file-system location of this input.
|
16
|
+
#
|
17
|
+
# Returns a fully-qualified Pathname.
|
18
|
+
#
|
19
|
+
def file
|
20
|
+
project.input_dir + path
|
21
|
+
end
|
22
|
+
|
23
|
+
# Public: Get the file-system location of the corresponding output file.
|
24
|
+
#
|
25
|
+
# Returns a fully-qualified Pathname.
|
26
|
+
#
|
27
|
+
def output_file
|
28
|
+
project.output_dir + output_path
|
29
|
+
end
|
30
|
+
|
31
|
+
# Public: Generate an output file.
|
32
|
+
#
|
33
|
+
def build
|
34
|
+
return false if ignorable? || uptodate?
|
35
|
+
logger.info("%-16s%s" % ["--(#{type})-->", output_path])
|
36
|
+
generate_output
|
37
|
+
end
|
38
|
+
|
39
|
+
# Public: Resolve a reference relative to this input.
|
40
|
+
#
|
41
|
+
# ref - a String referencing another asset
|
42
|
+
#
|
43
|
+
# A ref starting with "/" is resolved relative to the project root;
|
44
|
+
# anything else is resolved relative to this input.
|
45
|
+
#
|
46
|
+
# Returns a fully-qualified Pathname of the asset.
|
47
|
+
#
|
48
|
+
def resolve_path(ref)
|
49
|
+
ref = ref.to_str
|
50
|
+
if ref[0,1] == "/"
|
51
|
+
Pathname(ref[1..-1])
|
52
|
+
else
|
53
|
+
path.parent + ref
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Consider whether this input can be ignored.
|
58
|
+
#
|
59
|
+
# Returns true if it can.
|
60
|
+
#
|
61
|
+
def ignorable?
|
62
|
+
path.to_s.split("/").any? { |component| component.to_s[0,1] == "_" }
|
63
|
+
end
|
64
|
+
|
65
|
+
protected
|
66
|
+
|
67
|
+
def logger
|
68
|
+
project.logger
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
data/lib/pith/input/resource.rb
CHANGED
@@ -0,0 +1,41 @@
|
|
1
|
+
require "fileutils"
|
2
|
+
require "pith/input/abstract"
|
3
|
+
|
4
|
+
module Pith
|
5
|
+
module Input
|
6
|
+
|
7
|
+
class Resource < Abstract
|
8
|
+
|
9
|
+
def output_path
|
10
|
+
path
|
11
|
+
end
|
12
|
+
|
13
|
+
def type
|
14
|
+
"copy"
|
15
|
+
end
|
16
|
+
|
17
|
+
def uptodate?
|
18
|
+
FileUtils.uptodate?(output_file, [file])
|
19
|
+
end
|
20
|
+
|
21
|
+
# Copy this input verbatim into the output directory
|
22
|
+
#
|
23
|
+
def generate_output
|
24
|
+
output_file.parent.mkpath
|
25
|
+
FileUtils.copy(file, output_file)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Render this input, for inclusion within templates
|
29
|
+
#
|
30
|
+
def render(context, locals = {})
|
31
|
+
file.read
|
32
|
+
end
|
33
|
+
|
34
|
+
def meta
|
35
|
+
{}
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
data/lib/pith/input/template.rb
CHANGED
data/lib/pith/input/template.rb~
CHANGED
@@ -10,14 +10,16 @@ module Pith
|
|
10
10
|
|
11
11
|
class Template < Abstract
|
12
12
|
|
13
|
-
|
13
|
+
def self.can_handle?(path)
|
14
|
+
path.to_str =~ /\.([^.]+)$/ && Tilt.registered?($1)
|
15
|
+
end
|
14
16
|
|
15
17
|
def initialize(project, path)
|
18
|
+
raise(ArgumentError, "#{path} is not a template") unless Template.can_handle?(path)
|
16
19
|
super(project, path)
|
17
|
-
path.
|
20
|
+
path.to_str =~ /^(.+)\.(.+)$/ || raise("huh?")
|
18
21
|
@output_path = Pathname($1)
|
19
22
|
@type = $2
|
20
|
-
raise(UnrecognisedType, @type) unless Tilt.registered?(@type)
|
21
23
|
load
|
22
24
|
end
|
23
25
|
|
@@ -88,6 +90,10 @@ module Pith
|
|
88
90
|
end
|
89
91
|
|
90
92
|
private
|
93
|
+
|
94
|
+
def default_title
|
95
|
+
path.basename.to_s.sub(/\..*/, '').tr('_-', ' ').capitalize
|
96
|
+
end
|
91
97
|
|
92
98
|
# Read input file, extracting YAML meta-data header, and template content.
|
93
99
|
#
|
data/lib/pith/input.rb
CHANGED
data/lib/pith/input.rb~
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require "pith/input/template"
|
2
|
-
require "pith/input/
|
2
|
+
require "pith/input/resource"
|
3
3
|
|
4
4
|
module Pith
|
5
5
|
module Input
|
@@ -7,9 +7,11 @@ module Pith
|
|
7
7
|
class << self
|
8
8
|
|
9
9
|
def new(project, path)
|
10
|
-
Template.
|
11
|
-
|
12
|
-
|
10
|
+
if Template.can_handle?(path)
|
11
|
+
Template.new(project, path)
|
12
|
+
else
|
13
|
+
Resource.new(project, path)
|
14
|
+
end
|
13
15
|
end
|
14
16
|
|
15
17
|
end
|
data/lib/pith/pathname_ext.rb
CHANGED
@@ -7,8 +7,10 @@ class Pathname
|
|
7
7
|
utime(mtime, mtime) if mtime
|
8
8
|
end
|
9
9
|
|
10
|
-
def
|
11
|
-
Pathname.glob(self + pattern, File::FNM_DOTMATCH)
|
10
|
+
def all_files(pattern = "**/*")
|
11
|
+
Pathname.glob(self + pattern, File::FNM_DOTMATCH).select do |path|
|
12
|
+
path.file?
|
13
|
+
end
|
12
14
|
end
|
13
15
|
|
14
16
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require "pathname"
|
2
|
+
|
3
|
+
class Pathname
|
4
|
+
|
5
|
+
def touch(mtime = nil)
|
6
|
+
FileUtils.touch(self.to_s)
|
7
|
+
utime(mtime, mtime) if mtime
|
8
|
+
end
|
9
|
+
|
10
|
+
def all_files(pattern = "**/*")
|
11
|
+
Pathname.glob(self + pattern, File::FNM_DOTMATCH).select do |path|
|
12
|
+
path.file?
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require "pith/input"
|
2
|
+
require "time"
|
3
|
+
|
4
|
+
module Pith
|
5
|
+
module Plugins
|
6
|
+
module Publication
|
7
|
+
|
8
|
+
module TemplateMethods
|
9
|
+
|
10
|
+
def published?
|
11
|
+
!published_at.nil?
|
12
|
+
end
|
13
|
+
|
14
|
+
def published_at
|
15
|
+
parse_date(meta["published"])
|
16
|
+
end
|
17
|
+
|
18
|
+
def updated_at
|
19
|
+
parse_date(meta["updated"]) || published_at
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def parse_date(date_string)
|
25
|
+
Time.parse(date_string) if date_string
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
module Pith
|
35
|
+
module Input
|
36
|
+
|
37
|
+
class Abstract
|
38
|
+
|
39
|
+
def published?
|
40
|
+
false
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
class Template
|
46
|
+
include Pith::Plugins::Publication::TemplateMethods
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
data/lib/pith/project.rb
CHANGED
@@ -37,8 +37,7 @@ module Pith
|
|
37
37
|
# call #refresh to discard the cached data.
|
38
38
|
#
|
39
39
|
def inputs
|
40
|
-
@inputs ||= input_dir.
|
41
|
-
next if input_file.directory?
|
40
|
+
@inputs ||= input_dir.all_files.map do |input_file|
|
42
41
|
path = input_file.relative_path_from(input_dir)
|
43
42
|
find_or_create_input(path)
|
44
43
|
end.compact
|
@@ -64,9 +63,8 @@ module Pith
|
|
64
63
|
def build
|
65
64
|
refresh
|
66
65
|
load_config
|
67
|
-
|
68
|
-
|
69
|
-
end
|
66
|
+
remove_old_outputs
|
67
|
+
generate_outputs
|
70
68
|
output_dir.touch
|
71
69
|
end
|
72
70
|
|
@@ -97,7 +95,7 @@ module Pith
|
|
97
95
|
|
98
96
|
def config_files
|
99
97
|
@config_files ||= begin
|
100
|
-
input_dir.
|
98
|
+
input_dir.all_files("_pith/**")
|
101
99
|
end.to_set
|
102
100
|
end
|
103
101
|
|
@@ -111,6 +109,23 @@ module Pith
|
|
111
109
|
end
|
112
110
|
end
|
113
111
|
|
112
|
+
def remove_old_outputs
|
113
|
+
valid_output_paths = inputs.map { |i| i.output_path }
|
114
|
+
output_dir.all_files.each do |output_file|
|
115
|
+
output_path = output_file.relative_path_from(output_dir)
|
116
|
+
unless valid_output_paths.member?(output_path)
|
117
|
+
logger.info("removing #{output_path}")
|
118
|
+
FileUtils.rm(output_file)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def generate_outputs
|
124
|
+
inputs.each do |input|
|
125
|
+
input.build
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
114
129
|
def input_cache
|
115
130
|
@input_cache ||= Hash.new do |h, cache_key|
|
116
131
|
h[cache_key] = Input.new(self, cache_key.first)
|
data/lib/pith/project.rb~
CHANGED
@@ -1,6 +1,7 @@
|
|
1
|
-
require "pathname"
|
2
1
|
require "logger"
|
3
2
|
require "pith/input"
|
3
|
+
require "pith/pathname_ext"
|
4
|
+
require "pith/reference_error"
|
4
5
|
require "tilt"
|
5
6
|
|
6
7
|
module Pith
|
@@ -13,8 +14,21 @@ module Pith
|
|
13
14
|
end
|
14
15
|
end
|
15
16
|
|
16
|
-
|
17
|
+
attr_reader :input_dir
|
18
|
+
|
19
|
+
def input_dir=(dir)
|
20
|
+
@input_dir = Pathname(dir)
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_reader :output_dir
|
24
|
+
|
25
|
+
def output_dir=(dir)
|
26
|
+
@output_dir = Pathname(dir)
|
27
|
+
end
|
17
28
|
|
29
|
+
attr_accessor :assume_content_negotiation
|
30
|
+
attr_accessor :assume_directory_index
|
31
|
+
|
18
32
|
# Public: get inputs
|
19
33
|
#
|
20
34
|
# Returns Pith::Input objects representing the files in the input_dir.
|
@@ -23,8 +37,7 @@ module Pith
|
|
23
37
|
# call #refresh to discard the cached data.
|
24
38
|
#
|
25
39
|
def inputs
|
26
|
-
@inputs ||=
|
27
|
-
next if input_file.directory?
|
40
|
+
@inputs ||= input_dir.all_files.map do |input_file|
|
28
41
|
path = input_file.relative_path_from(input_dir)
|
29
42
|
find_or_create_input(path)
|
30
43
|
end.compact
|
@@ -42,7 +55,7 @@ module Pith
|
|
42
55
|
inputs.each do |input|
|
43
56
|
return input if input.path == path || input.output_path == path
|
44
57
|
end
|
45
|
-
|
58
|
+
nil
|
46
59
|
end
|
47
60
|
|
48
61
|
# Public: build the project, generating output files.
|
@@ -50,9 +63,9 @@ module Pith
|
|
50
63
|
def build
|
51
64
|
refresh
|
52
65
|
load_config
|
53
|
-
|
54
|
-
|
55
|
-
|
66
|
+
remove_old_outputs
|
67
|
+
generate_outputs
|
68
|
+
output_dir.touch
|
56
69
|
end
|
57
70
|
|
58
71
|
# Public: discard cached data that is out-of-sync with the file-system.
|
@@ -62,6 +75,10 @@ module Pith
|
|
62
75
|
@config_files = nil
|
63
76
|
end
|
64
77
|
|
78
|
+
def last_built_at
|
79
|
+
output_dir.mtime
|
80
|
+
end
|
81
|
+
|
65
82
|
def logger
|
66
83
|
@logger ||= Logger.new(nil)
|
67
84
|
end
|
@@ -78,7 +95,7 @@ module Pith
|
|
78
95
|
|
79
96
|
def config_files
|
80
97
|
@config_files ||= begin
|
81
|
-
|
98
|
+
input_dir.all_files("_pith/**")
|
82
99
|
end.to_set
|
83
100
|
end
|
84
101
|
|
@@ -92,6 +109,23 @@ module Pith
|
|
92
109
|
end
|
93
110
|
end
|
94
111
|
|
112
|
+
def remove_old_outputs
|
113
|
+
valid_output_paths = inputs.map { |i| i.output_path }
|
114
|
+
output_dir.all_files.each do |output_file|
|
115
|
+
output_path = output_file.relative_path_from(output_dir)
|
116
|
+
unless valid_output_paths.member?(output_path)
|
117
|
+
logger.info("removing #{output_path}")
|
118
|
+
FileUtils.rm(output_file)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def generate_outputs
|
124
|
+
inputs.each do |input|
|
125
|
+
input.build
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
95
129
|
def input_cache
|
96
130
|
@input_cache ||= Hash.new do |h, cache_key|
|
97
131
|
h[cache_key] = Input.new(self, cache_key.first)
|
@@ -105,7 +139,5 @@ module Pith
|
|
105
139
|
end
|
106
140
|
|
107
141
|
end
|
108
|
-
|
109
|
-
class ReferenceError < StandardError; end
|
110
142
|
|
111
143
|
end
|
data/lib/pith/render_context.rb~
CHANGED
@@ -1,6 +1,7 @@
|
|
1
|
-
require "set"
|
2
|
-
require "pathname"
|
3
1
|
require "ostruct"
|
2
|
+
require "pathname"
|
3
|
+
require "pith/reference_error"
|
4
|
+
require "set"
|
4
5
|
require "tilt"
|
5
6
|
|
6
7
|
module Pith
|
@@ -18,7 +19,7 @@ module Pith
|
|
18
19
|
|
19
20
|
attr_reader :project
|
20
21
|
|
21
|
-
def
|
22
|
+
def page
|
22
23
|
@input_stack.first
|
23
24
|
end
|
24
25
|
|
@@ -29,7 +30,7 @@ module Pith
|
|
29
30
|
def render(input, locals = {}, &block)
|
30
31
|
with_input(input) do
|
31
32
|
result = input.render(self, locals, &block)
|
32
|
-
layout_ref =
|
33
|
+
layout_ref = input.meta["layout"]
|
33
34
|
result = render_ref(layout_ref) { result } if layout_ref
|
34
35
|
result
|
35
36
|
end
|
@@ -51,42 +52,53 @@ module Pith
|
|
51
52
|
@content_for_hash ||= Hash.new { "" }
|
52
53
|
end
|
53
54
|
|
54
|
-
def
|
55
|
-
|
55
|
+
def relative_url_to(target_path)
|
56
|
+
url = target_path.relative_path_from(page.path.parent).to_str
|
57
|
+
url = url.sub(/index\.html$/, "") if project.assume_directory_index
|
58
|
+
url = url.sub(/\.html$/, "") if project.assume_content_negotiation
|
59
|
+
url = "./" if url.empty?
|
60
|
+
Pathname(url)
|
56
61
|
end
|
57
62
|
|
58
63
|
def href(target_ref)
|
59
|
-
|
64
|
+
relative_url_to(resolve_reference(target_ref))
|
60
65
|
end
|
61
66
|
|
62
67
|
def link(target_ref, label = nil)
|
63
|
-
target_path =
|
64
|
-
label ||= begin
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
+
target_path = resolve_reference(target_ref)
|
69
|
+
label ||= begin
|
70
|
+
target_input = input(target_path)
|
71
|
+
record_dependency_on(target_input.file)
|
72
|
+
target_input.title
|
73
|
+
rescue ReferenceError
|
74
|
+
"???"
|
68
75
|
end
|
69
|
-
|
76
|
+
url = relative_url_to(target_path)
|
77
|
+
%{<a href="#{url}">#{label}</a>}
|
70
78
|
end
|
71
|
-
|
72
|
-
private
|
73
79
|
|
74
|
-
def
|
75
|
-
|
80
|
+
def record_dependency_on(file)
|
81
|
+
@dependencies << file
|
76
82
|
end
|
77
83
|
|
78
|
-
|
79
|
-
|
84
|
+
private
|
85
|
+
|
86
|
+
def resolve_reference(ref)
|
87
|
+
if ref.respond_to?(:output_path)
|
88
|
+
ref.output_path
|
89
|
+
else
|
90
|
+
current_input.resolve_path(ref)
|
91
|
+
end
|
80
92
|
end
|
81
93
|
|
82
|
-
def
|
94
|
+
def input(path)
|
83
95
|
input = project.input(path)
|
84
|
-
|
96
|
+
raise(ReferenceError, %{Can't find "#{path}"}) if input.nil?
|
85
97
|
input
|
86
98
|
end
|
87
99
|
|
88
100
|
def with_input(input)
|
89
|
-
|
101
|
+
record_dependency_on(input.file)
|
90
102
|
@input_stack.push(input)
|
91
103
|
begin
|
92
104
|
yield
|
@@ -96,9 +108,8 @@ module Pith
|
|
96
108
|
end
|
97
109
|
|
98
110
|
def render_ref(template_ref, locals = {}, &block)
|
99
|
-
|
100
|
-
|
101
|
-
render(template, locals, &block)
|
111
|
+
template_input = input(resolve_reference(template_ref))
|
112
|
+
render(template_input, locals, &block)
|
102
113
|
end
|
103
114
|
|
104
115
|
end
|
data/lib/pith/server.rb~
CHANGED
@@ -7,31 +7,36 @@ module Pith
|
|
7
7
|
|
8
8
|
def new(project)
|
9
9
|
Rack::Builder.new do
|
10
|
-
use Rack::CommonLogger
|
10
|
+
use Rack::CommonLogger
|
11
11
|
use Rack::ShowExceptions
|
12
12
|
use Rack::Lint
|
13
|
-
use Pith::Server::AutoBuild, project
|
14
13
|
use Adsf::Rack::IndexFileFinder, :root => project.output_dir
|
14
|
+
use Pith::Server::DefaultToHtml, project.output_dir
|
15
15
|
run Rack::Directory.new(project.output_dir)
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
-
def run(project, options = {})
|
20
|
-
Rack::Handler.get("thin").run(new(project), options)
|
21
|
-
end
|
22
|
-
|
23
19
|
extend self
|
24
|
-
|
25
|
-
class AutoBuild
|
26
20
|
|
27
|
-
|
28
|
-
|
29
|
-
|
21
|
+
class DefaultToHtml
|
22
|
+
|
23
|
+
def initialize(app, root)
|
24
|
+
@app = app
|
25
|
+
@root = root
|
30
26
|
end
|
31
27
|
|
32
28
|
def call(env)
|
33
|
-
|
29
|
+
|
30
|
+
path_info = ::Rack::Utils.unescape(env["PATH_INFO"])
|
31
|
+
file = "#{@root}#{path_info}"
|
32
|
+
unless File.exist?(file)
|
33
|
+
if File.exist?("#{file}.html")
|
34
|
+
env["PATH_INFO"] += ".html"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
34
38
|
@app.call(env)
|
39
|
+
|
35
40
|
end
|
36
41
|
|
37
42
|
end
|
data/lib/pith/version.rb
CHANGED
data/lib/pith/version.rb~
CHANGED
@@ -0,0 +1,32 @@
|
|
1
|
+
module Pith
|
2
|
+
|
3
|
+
class Watcher
|
4
|
+
|
5
|
+
DEFAULT_INTERVAL = 2
|
6
|
+
|
7
|
+
def initialize(project, options = {})
|
8
|
+
@project = project
|
9
|
+
@interval = DEFAULT_INTERVAL
|
10
|
+
options.each do |k,v|
|
11
|
+
send("#{k}=", v)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_accessor :project
|
16
|
+
attr_accessor :interval
|
17
|
+
|
18
|
+
def call
|
19
|
+
loop do
|
20
|
+
begin
|
21
|
+
project.build
|
22
|
+
rescue Exception => e
|
23
|
+
$stderr.puts "ERROR: #{e}"
|
24
|
+
e.backtrace.each { |line| $stderr.puts line }
|
25
|
+
end
|
26
|
+
sleep(interval)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require "pith/input/abstract"
|
3
|
+
require "pith/project"
|
4
|
+
|
5
|
+
describe Pith::Input::Template do
|
6
|
+
|
7
|
+
before do
|
8
|
+
$input_dir.mkpath
|
9
|
+
@project = Pith::Project.new(:input_dir => $input_dir)
|
10
|
+
end
|
11
|
+
|
12
|
+
def make_input(path)
|
13
|
+
input_file = $input_dir + path
|
14
|
+
input_file.parent.mkpath
|
15
|
+
input_file.open("w") do |io|
|
16
|
+
yield io if block_given?
|
17
|
+
end
|
18
|
+
@project.input(path)
|
19
|
+
end
|
20
|
+
|
21
|
+
describe ".can_handle?" do
|
22
|
+
|
23
|
+
it "returns true for template paths" do
|
24
|
+
Pith::Input::Template.can_handle?("xyz.html.haml").should be_true
|
25
|
+
Pith::Input::Template.can_handle?("xyz.html.md").should be_true
|
26
|
+
end
|
27
|
+
|
28
|
+
it "handles directories" do
|
29
|
+
Pith::Input::Template.can_handle?("dir/xyz.haml").should be_true
|
30
|
+
end
|
31
|
+
|
32
|
+
it "accepts Pathname objects" do
|
33
|
+
Pith::Input::Template.can_handle?(Pathname("xyz.html.haml")).should be_true
|
34
|
+
end
|
35
|
+
|
36
|
+
it "returns false for non-template paths" do
|
37
|
+
Pith::Input::Template.can_handle?("foo.html").should be_false
|
38
|
+
Pith::Input::Template.can_handle?("foo").should be_false
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
describe "#title" do
|
44
|
+
|
45
|
+
it "is based on last component of filename" do
|
46
|
+
@input = make_input("dir/some_page.html.haml")
|
47
|
+
@input.title.should == "Some page"
|
48
|
+
end
|
49
|
+
|
50
|
+
it "can be over-ridden in metadata" do
|
51
|
+
@input = make_input("dir/some_page.html.haml") do |i|
|
52
|
+
i.puts "---"
|
53
|
+
i.puts "title: Blah blah"
|
54
|
+
i.puts "..."
|
55
|
+
end
|
56
|
+
@input.title.should == "Blah blah"
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "#output_path" do
|
62
|
+
|
63
|
+
it "excludes the template-type extension" do
|
64
|
+
@input = make_input("dir/some_page.html.haml")
|
65
|
+
@input.output_path.should == Pathname("dir/some_page.html")
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require "pith/plugins/publication"
|
3
|
+
|
4
|
+
describe Pith::Plugins::Publication::TemplateMethods do
|
5
|
+
|
6
|
+
before do
|
7
|
+
@template = OpenStruct.new(:meta => {})
|
8
|
+
@template.extend(Pith::Plugins::Publication::TemplateMethods)
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "#published_at" do
|
12
|
+
|
13
|
+
it "defaults to nil" do
|
14
|
+
@template.published_at.should be_nil
|
15
|
+
end
|
16
|
+
|
17
|
+
it "is derived by parsing the 'published' meta-field" do
|
18
|
+
@template.meta["published"] = "25 Dec 1999 22:30"
|
19
|
+
@template.published_at.should == Time.local(1999, 12, 25, 22, 30)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "#updated_at" do
|
25
|
+
|
26
|
+
it "defaults to #published_at" do
|
27
|
+
@template.meta["published"] = "25 Dec 1999 22:30"
|
28
|
+
@template.updated_at.should == Time.local(1999, 12, 25, 22, 30)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "can be overridden with an 'updated' meta-field" do
|
32
|
+
@template.meta["published"] = "25 Dec 1999 22:30"
|
33
|
+
@template.meta["published"] = "1 Jan 2000 03:00"
|
34
|
+
@template.updated_at.should == Time.local(2000, 1, 1, 3, 0)
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
data/spec/pith/project_spec.rb~
CHANGED
@@ -17,9 +17,9 @@ describe Pith::Project do
|
|
17
17
|
@input_file.touch
|
18
18
|
end
|
19
19
|
|
20
|
-
it "constructs an
|
20
|
+
it "constructs an Resource object" do
|
21
21
|
@input = @project.input("input.txt")
|
22
|
-
@input.should be_kind_of(Pith::Input::
|
22
|
+
@input.should be_kind_of(Pith::Input::Resource)
|
23
23
|
@input.file.should == @input_file
|
24
24
|
end
|
25
25
|
|
@@ -55,10 +55,8 @@ describe Pith::Project do
|
|
55
55
|
|
56
56
|
describe "(with an invalid input path)" do
|
57
57
|
|
58
|
-
it "
|
59
|
-
|
60
|
-
@project.input("bogus.path")
|
61
|
-
end.should raise_error(Pith::ReferenceError)
|
58
|
+
it "returns nil" do
|
59
|
+
@project.input("bogus.path").should be_nil
|
62
60
|
end
|
63
61
|
|
64
62
|
end
|
@@ -112,4 +110,28 @@ describe Pith::Project do
|
|
112
110
|
|
113
111
|
end
|
114
112
|
|
113
|
+
describe "when an input file is removed" do
|
114
|
+
|
115
|
+
before do
|
116
|
+
@input_file = $input_dir + "input.html.haml"
|
117
|
+
@input_file.touch(Time.now - 10)
|
118
|
+
end
|
119
|
+
|
120
|
+
describe "a second call to #input" do
|
121
|
+
it "returns nil" do
|
122
|
+
|
123
|
+
first_time = @project.input("input.html.haml")
|
124
|
+
first_time.should_not be_nil
|
125
|
+
|
126
|
+
FileUtils.rm(@input_file)
|
127
|
+
|
128
|
+
@project.refresh
|
129
|
+
second_time = @project.input("input.html.haml")
|
130
|
+
second_time.should be_nil
|
131
|
+
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
|
115
137
|
end
|
data/spec/spec_helper.rb~
CHANGED
@@ -1,16 +1,6 @@
|
|
1
1
|
require "rubygems"
|
2
2
|
|
3
3
|
require "fileutils"
|
4
|
-
require "pathname"
|
5
|
-
|
6
|
-
class Pathname
|
7
|
-
|
8
|
-
def touch(mtime = nil)
|
9
|
-
FileUtils.touch(self.to_s)
|
10
|
-
utime(mtime, mtime) if mtime
|
11
|
-
end
|
12
|
-
|
13
|
-
end
|
14
4
|
|
15
5
|
$project_dir = Pathname(__FILE__).expand_path.parent.parent
|
16
6
|
$tmp_dir = $project_dir + "tmp"
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pith
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 11
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 10
|
10
|
+
version: 0.0.10
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Mike Williams
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-
|
18
|
+
date: 2010-10-01 00:00:00 +10:00
|
19
19
|
default_executable: pith
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -112,7 +112,9 @@ files:
|
|
112
112
|
- lib/pith/console_logger.rb~
|
113
113
|
- lib/pith/input/abstract.rb
|
114
114
|
- lib/pith/input/abstract.rb~
|
115
|
+
- lib/pith/input/base.rb~
|
115
116
|
- lib/pith/input/resource.rb
|
117
|
+
- lib/pith/input/resource.rb~
|
116
118
|
- lib/pith/input/template.rb
|
117
119
|
- lib/pith/input/template.rb~
|
118
120
|
- lib/pith/input/verbatim.rb~
|
@@ -120,12 +122,17 @@ files:
|
|
120
122
|
- lib/pith/input.rb~
|
121
123
|
- lib/pith/metadata.rb~
|
122
124
|
- lib/pith/pathname_ext.rb
|
125
|
+
- lib/pith/pathname_ext.rb~
|
123
126
|
- lib/pith/plugins/publication/input.rb
|
127
|
+
- lib/pith/plugins/publication/input.rb~
|
124
128
|
- lib/pith/plugins/publication/project.rb
|
129
|
+
- lib/pith/plugins/publication/project.rb~
|
125
130
|
- lib/pith/plugins/publication.rb
|
131
|
+
- lib/pith/plugins/publication.rb~
|
126
132
|
- lib/pith/project.rb
|
127
133
|
- lib/pith/project.rb~
|
128
134
|
- lib/pith/reference_error.rb
|
135
|
+
- lib/pith/reference_error.rb~
|
129
136
|
- lib/pith/render_context.rb
|
130
137
|
- lib/pith/render_context.rb~
|
131
138
|
- lib/pith/server.rb
|
@@ -133,6 +140,7 @@ files:
|
|
133
140
|
- lib/pith/version.rb
|
134
141
|
- lib/pith/version.rb~
|
135
142
|
- lib/pith/watcher.rb
|
143
|
+
- lib/pith/watcher.rb~
|
136
144
|
- lib/pith.rb
|
137
145
|
- lib/pith.rb~
|
138
146
|
- sample/_layouts/standard.haml
|
@@ -146,12 +154,15 @@ files:
|
|
146
154
|
- Rakefile
|
147
155
|
- spec/pith/input/abstract_spec.rb~
|
148
156
|
- spec/pith/input/template_spec.rb
|
157
|
+
- spec/pith/input/template_spec.rb~
|
149
158
|
- spec/pith/metadata_spec.rb~
|
150
159
|
- spec/pith/plugins/publication_spec.rb
|
160
|
+
- spec/pith/plugins/publication_spec.rb~
|
151
161
|
- spec/pith/project_spec.rb
|
152
162
|
- spec/pith/project_spec.rb~
|
153
163
|
- spec/spec_helper.rb
|
154
164
|
- spec/spec_helper.rb~
|
165
|
+
- features/cleanup.feature
|
155
166
|
- features/content_for.feature
|
156
167
|
- features/content_for.feature~
|
157
168
|
- features/haml.feature
|
@@ -222,12 +233,15 @@ test_files:
|
|
222
233
|
- Rakefile
|
223
234
|
- spec/pith/input/abstract_spec.rb~
|
224
235
|
- spec/pith/input/template_spec.rb
|
236
|
+
- spec/pith/input/template_spec.rb~
|
225
237
|
- spec/pith/metadata_spec.rb~
|
226
238
|
- spec/pith/plugins/publication_spec.rb
|
239
|
+
- spec/pith/plugins/publication_spec.rb~
|
227
240
|
- spec/pith/project_spec.rb
|
228
241
|
- spec/pith/project_spec.rb~
|
229
242
|
- spec/spec_helper.rb
|
230
243
|
- spec/spec_helper.rb~
|
244
|
+
- features/cleanup.feature
|
231
245
|
- features/content_for.feature
|
232
246
|
- features/content_for.feature~
|
233
247
|
- features/haml.feature
|