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.
Files changed (54) hide show
  1. data/README.markdown +29 -0
  2. data/Rakefile +16 -0
  3. data/bin/pith +60 -0
  4. data/cucumber.yml +7 -0
  5. data/features/content_for.feature +29 -0
  6. data/features/content_for.feature~ +29 -0
  7. data/features/haml.feature +24 -0
  8. data/features/haml.feature~ +24 -0
  9. data/features/helpers.feature +23 -0
  10. data/features/ignorance.feature +26 -0
  11. data/features/ignorance.feature~ +26 -0
  12. data/features/include.feature +84 -0
  13. data/features/include.feature~ +84 -0
  14. data/features/incremental_rebuild.feature +24 -0
  15. data/features/layouts.feature +43 -0
  16. data/features/layouts.feature~ +43 -0
  17. data/features/linking.feature +79 -0
  18. data/features/linking.feature~ +79 -0
  19. data/features/metadata.feature +20 -0
  20. data/features/metadata.feature~ +20 -0
  21. data/features/step_definitions/build_steps.rb +46 -0
  22. data/features/step_definitions/build_steps.rb~ +29 -0
  23. data/features/support/env.rb +75 -0
  24. data/features/support/env.rb~ +43 -0
  25. data/features/support/rspec_matchers.rb +5 -0
  26. data/features/textile.feature +29 -0
  27. data/features/textile.feature~ +29 -0
  28. data/features/verbatim.feature +32 -0
  29. data/features/verbatim.feature~ +32 -0
  30. data/lib/pith.rb +1 -0
  31. data/lib/pith.rb~ +2 -0
  32. data/lib/pith/console_logger.rb +20 -0
  33. data/lib/pith/console_logger.rb~ +20 -0
  34. data/lib/pith/input.rb +189 -0
  35. data/lib/pith/input.rb~ +95 -0
  36. data/lib/pith/metadata.rb +26 -0
  37. data/lib/pith/metadata.rb~ +26 -0
  38. data/lib/pith/project.rb +68 -0
  39. data/lib/pith/project.rb~ +50 -0
  40. data/lib/pith/render_context.rb +77 -0
  41. data/lib/pith/render_context.rb~ +66 -0
  42. data/lib/pith/server.rb +42 -0
  43. data/lib/pith/server.rb~ +45 -0
  44. data/lib/pith/version.rb +3 -0
  45. data/lib/pith/version.rb~ +3 -0
  46. data/sample/_layouts/standard.haml +7 -0
  47. data/sample/index.html.haml +8 -0
  48. data/sample/stylesheets/app.css.sass +44 -0
  49. data/spec/pith/metadata_spec.rb +46 -0
  50. data/spec/pith/metadata_spec.rb~ +46 -0
  51. data/spec/pith/project_spec.rb +61 -0
  52. data/spec/spec_helper.rb +27 -0
  53. data/spec/spec_helper.rb~ +0 -0
  54. 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,5 @@
1
+ Spec::Matchers.define :contain do |expected|
2
+ match do |actual|
3
+ actual.any? { |x| expected === x }
4
+ end
5
+ 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
+ """
@@ -0,0 +1 @@
1
+ require 'pith/project'
@@ -0,0 +1,2 @@
1
+ require 'pith/console_logger'
2
+ require 'pith/project'
@@ -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
@@ -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
@@ -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