pith 0.0.2
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 -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
|