pith 0.0.9 → 0.0.10
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/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
|