pith 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +29 -0
- data/Rakefile +16 -0
- data/bin/pith +60 -0
- data/cucumber.yml +7 -0
- data/features/content_for.feature +29 -0
- data/features/content_for.feature~ +29 -0
- data/features/haml.feature +24 -0
- data/features/haml.feature~ +24 -0
- data/features/helpers.feature +23 -0
- data/features/ignorance.feature +26 -0
- data/features/ignorance.feature~ +26 -0
- data/features/include.feature +84 -0
- data/features/include.feature~ +84 -0
- data/features/incremental_rebuild.feature +24 -0
- data/features/layouts.feature +43 -0
- data/features/layouts.feature~ +43 -0
- data/features/linking.feature +79 -0
- data/features/linking.feature~ +79 -0
- data/features/metadata.feature +20 -0
- data/features/metadata.feature~ +20 -0
- data/features/step_definitions/build_steps.rb +46 -0
- data/features/step_definitions/build_steps.rb~ +29 -0
- data/features/support/env.rb +75 -0
- data/features/support/env.rb~ +43 -0
- data/features/support/rspec_matchers.rb +5 -0
- data/features/textile.feature +29 -0
- data/features/textile.feature~ +29 -0
- data/features/verbatim.feature +32 -0
- data/features/verbatim.feature~ +32 -0
- data/lib/pith.rb +1 -0
- data/lib/pith.rb~ +2 -0
- data/lib/pith/console_logger.rb +20 -0
- data/lib/pith/console_logger.rb~ +20 -0
- data/lib/pith/input.rb +189 -0
- data/lib/pith/input.rb~ +95 -0
- data/lib/pith/metadata.rb +26 -0
- data/lib/pith/metadata.rb~ +26 -0
- data/lib/pith/project.rb +68 -0
- data/lib/pith/project.rb~ +50 -0
- data/lib/pith/render_context.rb +77 -0
- data/lib/pith/render_context.rb~ +66 -0
- data/lib/pith/server.rb +42 -0
- data/lib/pith/server.rb~ +45 -0
- data/lib/pith/version.rb +3 -0
- data/lib/pith/version.rb~ +3 -0
- data/sample/_layouts/standard.haml +7 -0
- data/sample/index.html.haml +8 -0
- data/sample/stylesheets/app.css.sass +44 -0
- data/spec/pith/metadata_spec.rb +46 -0
- data/spec/pith/metadata_spec.rb~ +46 -0
- data/spec/pith/project_spec.rb +61 -0
- data/spec/spec_helper.rb +27 -0
- data/spec/spec_helper.rb~ +0 -0
- metadata +166 -0
@@ -0,0 +1,43 @@
|
|
1
|
+
require "pathname"
|
2
|
+
|
3
|
+
class DirHash
|
4
|
+
|
5
|
+
def initialize(dir)
|
6
|
+
@dir = Pathname(dir)
|
7
|
+
end
|
8
|
+
|
9
|
+
def [](file_name)
|
10
|
+
file_path = @dir + file_name
|
11
|
+
file_path.read if file_path.exist?
|
12
|
+
end
|
13
|
+
|
14
|
+
def []=(file_name, content)
|
15
|
+
file_path = @dir + file_name
|
16
|
+
file_path.parent.mkpath
|
17
|
+
file_path.open("w") do |io|
|
18
|
+
io << content
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
$project_dir = Pathname(__FILE__).expand_path.parent.parent.parent
|
25
|
+
$tmp_dir = $project_dir + "tmp"
|
26
|
+
|
27
|
+
$input_dir = $tmp_dir + "input"
|
28
|
+
|
29
|
+
$output_dir = $tmp_dir + "output"
|
30
|
+
$output_dir.mkpath
|
31
|
+
|
32
|
+
$: << ($project_dir + "lib").to_s
|
33
|
+
require "pith"
|
34
|
+
|
35
|
+
Before do
|
36
|
+
[$input_dir, $output_dir].each do |dir|
|
37
|
+
dir.rmtree if dir.exist?
|
38
|
+
dir.mkpath
|
39
|
+
end
|
40
|
+
@project = Pith::Project.new(:input_dir => $input_dir, :output_dir => $output_dir)
|
41
|
+
@inputs = DirHash.new($input_dir)
|
42
|
+
@outputs = DirHash.new($output_dir)
|
43
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
Feature: support for Textile markup
|
2
|
+
|
3
|
+
I want to use Textile fragments in pages
|
4
|
+
So that I can express content succintly
|
5
|
+
|
6
|
+
Scenario: Haml page with embedded Textile
|
7
|
+
|
8
|
+
Given input file "page.html.haml" contains
|
9
|
+
"""
|
10
|
+
:textile
|
11
|
+
|
12
|
+
A paragraph.
|
13
|
+
|
14
|
+
* one
|
15
|
+
* two
|
16
|
+
* three
|
17
|
+
"""
|
18
|
+
|
19
|
+
When I build the site
|
20
|
+
|
21
|
+
Then output file "page.html" should contain
|
22
|
+
"""
|
23
|
+
<p>A paragraph.</p>
|
24
|
+
<ul>
|
25
|
+
<li>one</li>
|
26
|
+
<li>two</li>
|
27
|
+
<li>three</li>
|
28
|
+
</ul>
|
29
|
+
"""
|
@@ -0,0 +1,29 @@
|
|
1
|
+
Feature: support for Textile markup
|
2
|
+
|
3
|
+
I want to use Textile fragments in pages
|
4
|
+
So that I can express content succintly
|
5
|
+
|
6
|
+
Scenario: Haml page with embedded Textile
|
7
|
+
|
8
|
+
Given input file "page.html.haml" contains
|
9
|
+
"""
|
10
|
+
:textile
|
11
|
+
|
12
|
+
A paragraph.
|
13
|
+
|
14
|
+
* one
|
15
|
+
* two
|
16
|
+
* three
|
17
|
+
"""
|
18
|
+
|
19
|
+
When I build the site
|
20
|
+
|
21
|
+
Then output file "page.html" should contain
|
22
|
+
"""
|
23
|
+
<p>A paragraph.</p>
|
24
|
+
<ul>
|
25
|
+
<li>one</li>
|
26
|
+
<li>two</li>
|
27
|
+
<li>three</li>
|
28
|
+
</ul>
|
29
|
+
"""
|
@@ -0,0 +1,32 @@
|
|
1
|
+
Feature: unrecognised input files are copied into output intact
|
2
|
+
|
3
|
+
I want any unrecognised file to be copied into the site verbatim
|
4
|
+
So that I can mix dynamic and static content
|
5
|
+
|
6
|
+
Scenario: text file
|
7
|
+
|
8
|
+
Given input file "verbatim.txt" contains
|
9
|
+
"""
|
10
|
+
Blah de blah
|
11
|
+
"""
|
12
|
+
|
13
|
+
When I build the site
|
14
|
+
|
15
|
+
Then output file "verbatim.txt" should contain
|
16
|
+
"""
|
17
|
+
Blah de blah
|
18
|
+
"""
|
19
|
+
|
20
|
+
Scenario: file in subdirectory
|
21
|
+
|
22
|
+
Given input file "blah/verbatim.txt" contains
|
23
|
+
"""
|
24
|
+
Blah de blah
|
25
|
+
"""
|
26
|
+
|
27
|
+
When I build the site
|
28
|
+
|
29
|
+
Then output file "blah/verbatim.txt" should contain
|
30
|
+
"""
|
31
|
+
Blah de blah
|
32
|
+
"""
|
@@ -0,0 +1,32 @@
|
|
1
|
+
Feature: unrecognised input files are copied into output intact
|
2
|
+
|
3
|
+
I want any unrecognised file to be copied into the site verbatim
|
4
|
+
So that I can mix dynamic and static content
|
5
|
+
|
6
|
+
Scenario: text file
|
7
|
+
|
8
|
+
Given input file "verbatim.txt" contains
|
9
|
+
"""
|
10
|
+
Blah de blah
|
11
|
+
"""
|
12
|
+
|
13
|
+
When I build the site
|
14
|
+
|
15
|
+
Then output file "verbatim.txt" should contain
|
16
|
+
"""
|
17
|
+
Blah de blah
|
18
|
+
"""
|
19
|
+
|
20
|
+
Scenario: file in subdirectory
|
21
|
+
|
22
|
+
Given input file "blah/verbatim.txt" contains
|
23
|
+
"""
|
24
|
+
Blah de blah
|
25
|
+
"""
|
26
|
+
|
27
|
+
When I build the site
|
28
|
+
|
29
|
+
Then output file "blah/verbatim.txt" should contain
|
30
|
+
"""
|
31
|
+
Blah de blah
|
32
|
+
"""
|
data/lib/pith.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'pith/project'
|
data/lib/pith.rb~
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
module Pith
|
2
|
+
|
3
|
+
class ConsoleLogger
|
4
|
+
|
5
|
+
def initialize(io = STDOUT)
|
6
|
+
@io = io
|
7
|
+
end
|
8
|
+
|
9
|
+
def info(message, &block)
|
10
|
+
message ||= block.call
|
11
|
+
@io.puts(message)
|
12
|
+
end
|
13
|
+
|
14
|
+
def write(message)
|
15
|
+
@io.puts(message)
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Pith
|
2
|
+
|
3
|
+
class ConsoleLogger
|
4
|
+
|
5
|
+
def initialize(io = STDOUT)
|
6
|
+
@io = io
|
7
|
+
end
|
8
|
+
|
9
|
+
def info(message, &block)
|
10
|
+
message ||= block.call
|
11
|
+
@io.puts(message)
|
12
|
+
end
|
13
|
+
|
14
|
+
def write(message)
|
15
|
+
@io.puts(message)
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
data/lib/pith/input.rb
ADDED
@@ -0,0 +1,189 @@
|
|
1
|
+
require "pith/render_context"
|
2
|
+
require "pith/metadata"
|
3
|
+
require "fileutils"
|
4
|
+
|
5
|
+
module Pith
|
6
|
+
module Input
|
7
|
+
|
8
|
+
class << self
|
9
|
+
|
10
|
+
def new(project, path)
|
11
|
+
if path.to_s =~ /^(.*)\.(.*)$/ && RenderContext.can_render?($2)
|
12
|
+
Template.new(project, path)
|
13
|
+
else
|
14
|
+
Verbatim.new(project, path)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
class Abstract
|
21
|
+
|
22
|
+
def initialize(project, path)
|
23
|
+
@project = project
|
24
|
+
@path = path
|
25
|
+
end
|
26
|
+
|
27
|
+
attr_reader :project, :path
|
28
|
+
|
29
|
+
# Public: Get the file-system location of this input.
|
30
|
+
#
|
31
|
+
# Returns a fully-qualified Pathname.
|
32
|
+
#
|
33
|
+
def full_path
|
34
|
+
project.input_dir + path
|
35
|
+
end
|
36
|
+
|
37
|
+
# Public: Get YAML metadata declared in the header of of a template.
|
38
|
+
#
|
39
|
+
# The first line of the template must end with "---". Any preceding characters
|
40
|
+
# are considered to be a comment prefix, and are stripped from the following
|
41
|
+
# lines. The metadata block ends when the comment block ends, or a line ending
|
42
|
+
# with "..." is encountered.
|
43
|
+
#
|
44
|
+
# Examples
|
45
|
+
#
|
46
|
+
# Given input starting with:
|
47
|
+
#
|
48
|
+
# -# ---
|
49
|
+
# -# published: 2008-09-15
|
50
|
+
# -# ...
|
51
|
+
#
|
52
|
+
# input.meta
|
53
|
+
# #=> { "published" => "2008-09-15" }
|
54
|
+
#
|
55
|
+
# Returns a Hash.
|
56
|
+
#
|
57
|
+
def meta
|
58
|
+
if @metadata.nil?
|
59
|
+
full_path.open do |io|
|
60
|
+
@metadata = Pith::Metadata.extract_from(io).freeze
|
61
|
+
end
|
62
|
+
end
|
63
|
+
@metadata
|
64
|
+
end
|
65
|
+
|
66
|
+
# Public: Generate a corresponding output file.
|
67
|
+
#
|
68
|
+
# Returns the output Pathname.
|
69
|
+
# Returns nil if no output was generated.
|
70
|
+
#
|
71
|
+
def build
|
72
|
+
generate_output unless ignorable? || uptodate?
|
73
|
+
end
|
74
|
+
|
75
|
+
# Public: Resolve a reference relative to this input.
|
76
|
+
#
|
77
|
+
# href - a String referencing another asset
|
78
|
+
#
|
79
|
+
# An href starting with "/" is resolved relative to the project root;
|
80
|
+
# anything else is resolved relative to this input.
|
81
|
+
#
|
82
|
+
# Returns a fully-qualified Pathname of the asset.
|
83
|
+
#
|
84
|
+
def relative_path(href)
|
85
|
+
href = href.to_str
|
86
|
+
if href[0,1] == "/"
|
87
|
+
Pathname(href[1..-1])
|
88
|
+
else
|
89
|
+
path.parent + href
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Resolve a reference relative to this input.
|
94
|
+
#
|
95
|
+
# href - a String referencing another asset
|
96
|
+
#
|
97
|
+
# Returns the referenced Input.
|
98
|
+
#
|
99
|
+
def relative_input(href)
|
100
|
+
project.input(relative_path(href))
|
101
|
+
end
|
102
|
+
|
103
|
+
# Consider whether this input can be ignored.
|
104
|
+
#
|
105
|
+
# Returns true if it can.
|
106
|
+
#
|
107
|
+
def ignorable?
|
108
|
+
path.to_s.split("/").any? { |component| component.to_s[0,1] == "_" }
|
109
|
+
end
|
110
|
+
|
111
|
+
protected
|
112
|
+
|
113
|
+
def logger
|
114
|
+
project.logger
|
115
|
+
end
|
116
|
+
|
117
|
+
def trace(strategy, output_path = nil)
|
118
|
+
output_path ||= "X"
|
119
|
+
logger.info("%-36s%-14s%s" % [path, "--(#{strategy})-->", output_path])
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
class Template < Abstract
|
125
|
+
|
126
|
+
def initialize(project, path)
|
127
|
+
super(project, path)
|
128
|
+
path.to_s =~ /^(.*)\.(.*)$/
|
129
|
+
@output_path = $1
|
130
|
+
@type = $2
|
131
|
+
end
|
132
|
+
|
133
|
+
attr_reader :output_path, :type
|
134
|
+
|
135
|
+
def full_output_path
|
136
|
+
project.output_dir + output_path
|
137
|
+
end
|
138
|
+
|
139
|
+
def uptodate?
|
140
|
+
return false if all_input_paths.nil?
|
141
|
+
FileUtils.uptodate?(full_output_path, all_input_paths)
|
142
|
+
end
|
143
|
+
|
144
|
+
# Render this input using Tilt
|
145
|
+
#
|
146
|
+
def generate_output
|
147
|
+
trace(type, output_path)
|
148
|
+
full_output_path.parent.mkpath
|
149
|
+
render_context = RenderContext.new(project)
|
150
|
+
full_output_path.open("w") do |out|
|
151
|
+
out.puts(render_context.render(self))
|
152
|
+
end
|
153
|
+
remember_dependencies(render_context.rendered_inputs)
|
154
|
+
end
|
155
|
+
|
156
|
+
private
|
157
|
+
|
158
|
+
def remember_dependencies(rendered_inputs)
|
159
|
+
@all_input_paths = rendered_inputs.map { |input| input.full_path }
|
160
|
+
end
|
161
|
+
|
162
|
+
def all_input_paths
|
163
|
+
@all_input_paths
|
164
|
+
end
|
165
|
+
|
166
|
+
end
|
167
|
+
|
168
|
+
class Verbatim < Abstract
|
169
|
+
|
170
|
+
def full_output_path
|
171
|
+
project.output_dir + path
|
172
|
+
end
|
173
|
+
|
174
|
+
def uptodate?
|
175
|
+
FileUtils.uptodate?(full_output_path, [full_path])
|
176
|
+
end
|
177
|
+
|
178
|
+
# Copy this input verbatim into the output directory
|
179
|
+
#
|
180
|
+
def generate_output
|
181
|
+
trace("copy", path)
|
182
|
+
full_output_path.parent.mkpath
|
183
|
+
FileUtils.copy(full_path, full_output_path)
|
184
|
+
end
|
185
|
+
|
186
|
+
end
|
187
|
+
|
188
|
+
end
|
189
|
+
end
|
data/lib/pith/input.rb~
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
require "tilt"
|
2
|
+
require "pith/output_context"
|
3
|
+
require "pith/metadata"
|
4
|
+
|
5
|
+
module Pith
|
6
|
+
|
7
|
+
class Input
|
8
|
+
|
9
|
+
def initialize(project, path)
|
10
|
+
@project = project
|
11
|
+
@path = path
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_reader :project, :path
|
15
|
+
|
16
|
+
def build
|
17
|
+
ignore || evaluate_as_tilt_template || copy_verbatim
|
18
|
+
end
|
19
|
+
|
20
|
+
def relative_input(name)
|
21
|
+
resolved_path = relative_path(name)
|
22
|
+
input = project.inputs.find do |input|
|
23
|
+
input.path == resolved_path
|
24
|
+
end
|
25
|
+
raise %{can't locate "#{resolved_path}"} if input.nil?
|
26
|
+
input
|
27
|
+
end
|
28
|
+
|
29
|
+
def relative_path(name)
|
30
|
+
name = name.to_str
|
31
|
+
if name[0,1] == "/"
|
32
|
+
Pathname(name[1..-1])
|
33
|
+
else
|
34
|
+
path.parent + name
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def render(context, locals = {}, &block)
|
39
|
+
Tilt.new(full_path).render(context, locals, &block)
|
40
|
+
end
|
41
|
+
|
42
|
+
def meta
|
43
|
+
if @metadata.nil?
|
44
|
+
full_path.open do |io|
|
45
|
+
@metadata = Pith::Metadata.extract_from(io).freeze
|
46
|
+
end
|
47
|
+
end
|
48
|
+
@metadata
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def logger
|
54
|
+
project.logger
|
55
|
+
end
|
56
|
+
|
57
|
+
def trace(strategy, output_path = nil)
|
58
|
+
output_path ||= "X"
|
59
|
+
logger.info("%-36s%-14s%s" % [path, "--(#{strategy})-->", output_path])
|
60
|
+
end
|
61
|
+
|
62
|
+
def full_path
|
63
|
+
project.input_dir + path
|
64
|
+
end
|
65
|
+
|
66
|
+
def ignore
|
67
|
+
path.to_s.split("/").any? { |component| component.to_s[0,1] == "_" }
|
68
|
+
end
|
69
|
+
alias :ignorable? :ignore
|
70
|
+
|
71
|
+
def evaluate_as_tilt_template
|
72
|
+
if path.to_s =~ /^(.*)\.(.*)$/ && Tilt.registered?($2)
|
73
|
+
output_path = Pathname($1); ext = $2
|
74
|
+
trace(ext, output_path)
|
75
|
+
output_file = project.output_dir + output_path
|
76
|
+
output_file.parent.mkpath
|
77
|
+
output_file.open("w") do |out|
|
78
|
+
output = render(RenderContext.new(self))
|
79
|
+
out.puts(output)
|
80
|
+
end
|
81
|
+
output_file
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def copy_verbatim
|
86
|
+
trace("copy", path)
|
87
|
+
output_path = project.output_dir + path
|
88
|
+
output_path.parent.mkpath
|
89
|
+
FileUtils.copy(full_path, output_path)
|
90
|
+
output_path
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|