pith 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. data/README.markdown +10 -3
  2. data/Rakefile +13 -3
  3. data/cucumber.yml +1 -6
  4. data/features/helpers.feature~ +23 -0
  5. data/features/ignorance.feature +12 -1
  6. data/features/ignorance.feature~ +12 -1
  7. data/features/incremental_rebuild.feature~ +24 -0
  8. data/features/layouts.feature +22 -2
  9. data/features/layouts.feature~ +20 -0
  10. data/features/metadata.feature +1 -1
  11. data/features/{linking.feature → relative_linking.feature} +32 -2
  12. data/features/relative_linking.feature~ +109 -0
  13. data/features/step_definitions/build_steps.rb +20 -16
  14. data/features/step_definitions/build_steps.rb~ +32 -11
  15. data/features/support/env.rb +6 -6
  16. data/features/support/env.rb~ +36 -4
  17. data/features/support/rspec_matchers.rb~ +5 -0
  18. data/lib/pith/input/abstract.rb +120 -0
  19. data/lib/pith/input/abstract.rb~ +120 -0
  20. data/lib/pith/input/template.rb +56 -0
  21. data/lib/pith/input/template.rb~ +46 -0
  22. data/lib/pith/input/verbatim.rb +31 -0
  23. data/lib/pith/input/verbatim.rb~ +31 -0
  24. data/lib/pith/input.rb +5 -176
  25. data/lib/pith/input.rb~ +10 -85
  26. data/lib/pith/project.rb +14 -7
  27. data/lib/pith/project.rb~ +36 -11
  28. data/lib/pith/render_context.rb +34 -13
  29. data/lib/pith/render_context.rb~ +54 -17
  30. data/lib/pith/server.rb +0 -1
  31. data/lib/pith/server.rb~ +13 -17
  32. data/lib/pith/version.rb +1 -1
  33. data/lib/pith/version.rb~ +1 -1
  34. data/lib/pith.rb~ +0 -1
  35. data/sample/_out/index.html +14 -0
  36. data/sample/_out/stylesheets/app.css +38 -0
  37. data/sample/index.html.haml +1 -2
  38. data/spec/pith/input/abstract_spec.rb +31 -0
  39. data/spec/pith/input/abstract_spec.rb~ +31 -0
  40. data/spec/pith/metadata_spec.rb +2 -2
  41. data/spec/pith/project_spec.rb +21 -8
  42. data/spec/pith/project_spec.rb~ +74 -0
  43. data/spec/spec_helper.rb~ +27 -0
  44. metadata +76 -6
data/lib/pith/project.rb CHANGED
@@ -19,17 +19,16 @@ module Pith
19
19
  Pathname.glob(input_dir + "**/*").map do |input_file|
20
20
  next if input_file.directory?
21
21
  path = input_file.relative_path_from(input_dir)
22
- input(path)
22
+ input_cache[path]
23
23
  end.compact
24
24
  end
25
25
 
26
26
  def input(path)
27
- @input_cache ||= Hash.new do |h, path|
28
- h[path] = Input.new(self, path)
27
+ path = Pathname(path)
28
+ inputs.each do |input|
29
+ return input if input.path == path || input.output_path == path
29
30
  end
30
- input = @input_cache[path]
31
- raise %{can't locate "#{path}"} unless input.full_path.file?
32
- input
31
+ raise ReferenceError, "Can't find #{path.inspect}"
33
32
  end
34
33
 
35
34
  def build
@@ -62,7 +61,15 @@ module Pith
62
61
  eval(config_file.read, binding, config_file)
63
62
  end
64
63
  end
65
-
64
+
65
+ def input_cache
66
+ @input_cache ||= Hash.new do |h, path|
67
+ h[path] = Input.new(self, path)
68
+ end
69
+ end
70
+
66
71
  end
67
72
 
73
+ class ReferenceError < StandardError; end
74
+
68
75
  end
data/lib/pith/project.rb~ CHANGED
@@ -17,34 +17,59 @@ module Pith
17
17
 
18
18
  def inputs
19
19
  Pathname.glob(input_dir + "**/*").map do |input_file|
20
- unless input_file.directory?
21
- path = input_file.relative_path_from(input_dir)
22
- Input.new(self, path)
23
- end
20
+ next if input_file.directory?
21
+ path = input_file.relative_path_from(input_dir)
22
+ input_cache[path]
24
23
  end.compact
25
24
  end
26
25
 
27
- def input(name)
28
- input_dir + name
26
+ def input(path)
27
+ path = Pathname(path)
28
+ inputs.each do |input|
29
+ return input if input.path == path || input.output_path == path
30
+ end
31
+ raise ReferenceError, "Can't find #{path.inspect}"
29
32
  end
30
33
 
31
34
  def build
35
+ load_config
32
36
  inputs.each do |input|
33
37
  input.build
34
38
  end
35
39
  end
36
-
37
- def serve
38
- require "pith/server"
39
- Pith::Server.run(self)
40
- end
41
40
 
42
41
  def logger
43
42
  @logger ||= Logger.new(nil)
44
43
  end
45
44
 
46
45
  attr_writer :logger
46
+
47
+ def helpers(&block)
48
+ helper_module.module_eval(&block)
49
+ end
50
+
51
+ def helper_module
52
+ @helper_module ||= Module.new
53
+ end
54
+
55
+ private
47
56
 
57
+ def load_config
58
+ config_file = input_dir + "_pith/config.rb"
59
+ project = self
60
+ if config_file.exist?
61
+ eval(config_file.read, binding, config_file)
62
+ end
63
+ end
64
+
65
+ def input_cache
66
+ @input_cache ||= Hash.new do |h, path|
67
+ h[path] = Input.new(self, path)
68
+ end
69
+ end
70
+
48
71
  end
49
72
 
73
+ class ReferenceError < StandardError; end
74
+
50
75
  end
@@ -8,15 +8,14 @@ module Pith
8
8
 
9
9
  include Tilt::CompileSite
10
10
 
11
- def self.can_render?(extension)
12
- Tilt.registered?(extension)
13
- end
14
-
15
11
  def initialize(project)
12
+ @project = project
16
13
  @input_stack = []
17
14
  @rendered_inputs = Set.new
18
15
  self.extend(project.helper_module)
19
16
  end
17
+
18
+ attr_reader :project
20
19
 
21
20
  def initial_input
22
21
  @input_stack.first
@@ -29,21 +28,25 @@ module Pith
29
28
  def render(input, locals = {}, &block)
30
29
  @rendered_inputs << input
31
30
  with_input(input) do
32
- Tilt.new(input.full_path).render(self, locals, &block)
31
+ result = input.render(self, locals, &block)
32
+ layout_ref = current_input.meta["layout"]
33
+ result = render_ref(layout_ref) { result } if layout_ref
34
+ result
33
35
  end
34
36
  end
35
37
 
36
38
  attr_reader :rendered_inputs
37
39
 
38
- def include(name, locals = {}, &block)
39
- included_input = current_input.relative_input(name)
40
+ def include(template_ref, locals = {}, &block)
40
41
  content_block = if block_given?
41
42
  content = capture_haml(&block)
42
43
  proc { content }
43
44
  end
44
- render(included_input, locals, &content_block)
45
+ render_ref(template_ref, locals, &content_block)
45
46
  end
46
47
 
48
+ alias :inside :include
49
+
47
50
  def content_for
48
51
  @content_for_hash ||= Hash.new { "" }
49
52
  end
@@ -52,16 +55,29 @@ module Pith
52
55
  initial_input.meta || {}
53
56
  end
54
57
 
55
- def href(name)
56
- target_path = current_input.relative_path(name)
57
- target_path.relative_path_from(initial_input.path.parent)
58
+ def href(target_ref)
59
+ relative_path_to(resolve_path(target_ref))
58
60
  end
59
61
 
60
- def link(target, label)
61
- %{<a href="#{href(target)}">#{label}</a>}
62
+ def link(target_ref, label = nil)
63
+ target_path = resolve_path(target_ref)
64
+ label ||= begin
65
+ project.input(target_path).title
66
+ rescue Pith::ReferenceError
67
+ "???"
68
+ end
69
+ %{<a href="#{relative_path_to(target_path)}">#{label}</a>}
62
70
  end
63
71
 
64
72
  private
73
+
74
+ def relative_path_to(target_path)
75
+ target_path.relative_path_from(initial_input.path.parent)
76
+ end
77
+
78
+ def resolve_path(ref)
79
+ current_input.resolve_path(ref)
80
+ end
65
81
 
66
82
  def with_input(input)
67
83
  @input_stack.push(input)
@@ -71,6 +87,11 @@ module Pith
71
87
  @input_stack.pop
72
88
  end
73
89
  end
90
+
91
+ def render_ref(template_ref, locals = {}, &block)
92
+ template = project.input(resolve_path(template_ref))
93
+ render(template, locals, &block)
94
+ end
74
95
 
75
96
  end
76
97
 
@@ -1,3 +1,4 @@
1
+ require "set"
1
2
  require "pathname"
2
3
  require "tilt"
3
4
 
@@ -7,10 +8,19 @@ module Pith
7
8
 
8
9
  include Tilt::CompileSite
9
10
 
10
- def initialize(input)
11
- @input_stack = [input]
11
+ def self.can_render?(extension)
12
+ Tilt.registered?(extension)
12
13
  end
13
14
 
15
+ def initialize(project)
16
+ @project = project
17
+ @input_stack = []
18
+ @rendered_inputs = Set.new
19
+ self.extend(project.helper_module)
20
+ end
21
+
22
+ attr_reader :project
23
+
14
24
  def initial_input
15
25
  @input_stack.first
16
26
  end
@@ -19,19 +29,28 @@ module Pith
19
29
  @input_stack.last
20
30
  end
21
31
 
22
- def include(name, locals = {}, &block)
23
- including_input = current_input
24
- included_input = including_input.relative_input(name)
25
- with_input(included_input) do
26
- content_block = if block_given?
27
- content = with_input(including_input) do
28
- capture_haml(&block)
29
- end
30
- proc { content }
32
+ def render_input(input, locals = {}, &block)
33
+ @rendered_inputs << input
34
+ with_input(input) do
35
+ rendered = Tilt.new(input.file).render(self, locals, &block)
36
+ layout_ref = current_input.meta["layout"]
37
+ if layout_ref
38
+ render_ref(layout_ref) { rendered }
39
+ else
40
+ rendered
31
41
  end
32
- current_input.render(self, locals, &content_block)
33
42
  end
34
43
  end
44
+
45
+ attr_reader :rendered_inputs
46
+
47
+ def include(template_ref, locals = {}, &block)
48
+ content_block = if block_given?
49
+ content = capture_haml(&block)
50
+ proc { content }
51
+ end
52
+ render_ref(template_ref, locals, &content_block)
53
+ end
35
54
 
36
55
  def content_for
37
56
  @content_for_hash ||= Hash.new { "" }
@@ -41,16 +60,29 @@ module Pith
41
60
  initial_input.meta || {}
42
61
  end
43
62
 
44
- def href(name)
45
- target_path = current_input.relative_path(name)
46
- target_path.relative_path_from(initial_input.path.parent)
63
+ def href(target_ref)
64
+ relative_path_to(resolve_path(target_ref))
47
65
  end
48
66
 
49
- def link(target, label)
50
- %{<a href="#{href(target)}">#{label}</a>}
67
+ def link(target_ref, label = nil)
68
+ target_path = resolve_path(target_ref)
69
+ label ||= begin
70
+ project.input(target_path).title
71
+ rescue Pith::ReferenceError
72
+ "???"
73
+ end
74
+ %{<a href="#{relative_path_to(target_path)}">#{label}</a>}
51
75
  end
52
76
 
53
77
  private
78
+
79
+ def relative_path_to(target_path)
80
+ target_path.relative_path_from(initial_input.path.parent)
81
+ end
82
+
83
+ def resolve_path(ref)
84
+ current_input.resolve_path(ref)
85
+ end
54
86
 
55
87
  def with_input(input)
56
88
  @input_stack.push(input)
@@ -60,6 +92,11 @@ module Pith
60
92
  @input_stack.pop
61
93
  end
62
94
  end
95
+
96
+ def render_ref(template_ref, locals = {}, &block)
97
+ template = project.input(resolve_path(template_ref))
98
+ render_input(template, locals, &block)
99
+ end
63
100
 
64
101
  end
65
102
 
data/lib/pith/server.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  require "rack"
2
2
  require "adsf/rack"
3
- require "logger"
4
3
 
5
4
  module Pith
6
5
 
data/lib/pith/server.rb~ CHANGED
@@ -1,31 +1,27 @@
1
1
  require "rack"
2
2
  require "adsf/rack"
3
- require "logger"
4
3
 
5
4
  module Pith
6
5
 
7
6
  module Server
8
7
 
9
- class << self
10
-
11
- def new(project)
12
- Rack::Builder.new do
13
- use Rack::CommonLogger, project.logger
14
- use Rack::ShowExceptions
15
- use Rack::Lint
16
- use Pith::Server::AutoBuild, project
17
- use Adsf::Rack::IndexFileFinder, :root => project.output_dir
18
- run Rack::File.new(project.output_dir)
19
- end
20
- end
21
-
22
- def run(project)
23
- app = new(project)
24
- Rack::Handler.get("thin").run(app, :Port => 4321)
8
+ def new(project)
9
+ Rack::Builder.new do
10
+ use Rack::CommonLogger, project.logger
11
+ use Rack::ShowExceptions
12
+ use Rack::Lint
13
+ use Pith::Server::AutoBuild, project
14
+ use Adsf::Rack::IndexFileFinder, :root => project.output_dir
15
+ run Rack::File.new(project.output_dir)
25
16
  end
17
+ end
26
18
 
19
+ def run(project, options = {})
20
+ Rack::Handler.get("thin").run(new(project), options)
27
21
  end
28
22
 
23
+ extend self
24
+
29
25
  class AutoBuild
30
26
 
31
27
  def initialize(app, project)
data/lib/pith/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Pith
2
- VERSION = "0.0.2".freeze
2
+ VERSION = "0.0.3".freeze
3
3
  end
data/lib/pith/version.rb~ CHANGED
@@ -1,3 +1,3 @@
1
1
  module Pith
2
- VERSION = "0.0.2a".freeze
2
+ VERSION = "0.0.2".freeze
3
3
  end
data/lib/pith.rb~ CHANGED
@@ -1,2 +1 @@
1
- require 'pith/console_logger'
2
1
  require 'pith/project'
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2
+ <html>
3
+ <head>
4
+ <link href='stylesheets/app.css' rel='stylesheet' type='text/css' />
5
+ </head>
6
+ <body>
7
+ <ol class='tabs'>
8
+ <li><a href="index.html">Introduction</a></li>
9
+ </ol>
10
+ <div class='box'>
11
+ <p>This is my site. Do you like it?</p>
12
+ </div>
13
+ </body>
14
+ </html>
@@ -0,0 +1,38 @@
1
+ .sidebar {
2
+ width: 19%;
3
+ float: right; }
4
+
5
+ .main {
6
+ width: 79%; }
7
+
8
+ .box {
9
+ -moz-border-radius: 0.5em;
10
+ -webkit-border-radius: 0.5em;
11
+ border: 2px solid grey;
12
+ padding: 0.5em; }
13
+ .box .heading {
14
+ font-size: 120%;
15
+ border-bottom: 1px solid #aaaaaa;
16
+ font-weight: bold;
17
+ margin-bottom: 0.5em; }
18
+
19
+ ol.tabs {
20
+ position: relative;
21
+ padding: 0;
22
+ margin: 0 0 -2px 1em;
23
+ list-style-type: none; }
24
+ ol.tabs li {
25
+ display: inline-block;
26
+ border: 2px solid grey;
27
+ -moz-border-radius-topleft: 0.5em;
28
+ -webkit-border-top-left-radius: 0.5em;
29
+ -moz-border-radius-topright: 0.5em;
30
+ -webkit-border-top-right-radius: 0.5em;
31
+ background: #cccccc;
32
+ padding: 0.5em; }
33
+ ol.tabs li.current {
34
+ border-bottom: 2px solid white;
35
+ background: white; }
36
+ ol.tabs li a {
37
+ color: inherit;
38
+ text-decoration: none; }
@@ -1,8 +1,7 @@
1
- = include "_layouts/standard.haml" do
1
+ = inside "_layouts/standard.haml" do
2
2
 
3
3
  %ol.tabs
4
4
  %li= link "index.html", "Introduction"
5
5
 
6
6
  .box
7
7
  %p This is my site. Do you like it?
8
-
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+ require "pith/input/abstract"
3
+ require "pith/project"
4
+
5
+ describe Pith::Input::Abstract do
6
+
7
+ before do
8
+ $input_dir.mkpath
9
+ @project = Pith::Project.new(:input_dir => $input_dir)
10
+ @input_file = $input_dir + "some_page.html.haml"
11
+ @input_file.touch
12
+ end
13
+
14
+ describe "#title" do
15
+
16
+ it "is based on last component of filename" do
17
+ @project.input("some_page.html").title.should == "Some page"
18
+ end
19
+
20
+ it "can be over-ridden in metadata" do
21
+ @input_file.open("w") do |i|
22
+ i.puts "-# ---"
23
+ i.puts "-# title: Blah blah"
24
+ i.puts "-# ..."
25
+ end
26
+ @project.input("some_page.html").title.should == "Blah blah"
27
+ end
28
+
29
+ end
30
+
31
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+ require "pith/input/abstract"
3
+ require "pith/project"
4
+
5
+ describe Pith::Input::Abstract do
6
+
7
+ before do
8
+ $input_dir.mkpath
9
+ @project = Pith::Project.new(:input_dir => $input_dir)
10
+ @input_file = $input_dir + "some_page.html.haml"
11
+ @input_file.touch
12
+ end
13
+
14
+ describe "#title" do
15
+
16
+ it "is based on last component of filename" do
17
+ @project.input("some_page.html").title.should == "Some page"
18
+ end
19
+
20
+ it "can be over-ridden in metadata" do
21
+ @input_file.open("w") do |i|
22
+ i.puts "-# ---"
23
+ i.puts "-# title: Blah blah"
24
+ i.puts "-# ..."
25
+ end
26
+ @project.input("some_page.html").title.should == "Blah blah"
27
+ end
28
+
29
+ end
30
+
31
+ end
@@ -9,7 +9,7 @@ describe Pith::Metadata do
9
9
  Pith::Metadata.extract_from(StringIO.new(@input))
10
10
  end
11
11
 
12
- describe "with input containing no YAML metadata" do
12
+ describe "when input contains no YAML metadata" do
13
13
 
14
14
  before do
15
15
  @input = "%p Blah blah"
@@ -21,7 +21,7 @@ describe Pith::Metadata do
21
21
 
22
22
  end
23
23
 
24
- describe "with input containing YAML metadata" do
24
+ describe "when input contains YAML metadata" do
25
25
 
26
26
  before do
27
27
  @input = <<-HAML.gsub(/^ +/, '')
@@ -13,14 +13,14 @@ describe Pith::Project do
13
13
  describe "(with a non-template input path)" do
14
14
 
15
15
  before do
16
- @input_path = $input_dir + "input.txt"
17
- @input_path.touch
16
+ @input_file = $input_dir + "input.txt"
17
+ @input_file.touch
18
18
  end
19
19
 
20
20
  it "constructs an Verbatim object" do
21
21
  @input = @project.input("input.txt")
22
22
  @input.should be_kind_of(Pith::Input::Verbatim)
23
- @input.full_path.should == @input_path
23
+ @input.file.should == @input_file
24
24
  end
25
25
 
26
26
  it "returns the same Input output every time" do
@@ -34,14 +34,27 @@ describe Pith::Project do
34
34
  describe "(with a template input path)" do
35
35
 
36
36
  before do
37
- @input_path = $input_dir + "input.haml"
38
- @input_path.touch
37
+ @input_file = $input_dir + "input.html.haml"
38
+ @input_file.touch
39
39
  end
40
40
 
41
41
  it "constructs an Template object" do
42
- @input = @project.input("input.haml")
42
+ @input = @project.input("input.html.haml")
43
43
  @input.should be_kind_of(Pith::Input::Template)
44
- @input.full_path.should == @input_path
44
+ @input.file.should == @input_file
45
+ end
46
+
47
+ end
48
+
49
+ describe "(with a template ouput path)" do
50
+
51
+ before do
52
+ @input_file = $input_dir + "input.html.haml"
53
+ @input_file.touch
54
+ end
55
+
56
+ it "can also be used to locate the Template" do
57
+ @project.input("input.html").should == @project.input("input.html.haml")
45
58
  end
46
59
 
47
60
  end
@@ -51,7 +64,7 @@ describe Pith::Project do
51
64
  it "complains" do
52
65
  lambda do
53
66
  @project.input("bogus.path")
54
- end.should raise_error
67
+ end.should raise_error(Pith::ReferenceError)
55
68
  end
56
69
 
57
70
  end
@@ -0,0 +1,74 @@
1
+ require 'spec_helper'
2
+ require 'pith/project'
3
+
4
+ describe Pith::Project do
5
+
6
+ before do
7
+ $input_dir.mkpath
8
+ @project = Pith::Project.new(:input_dir => $input_dir)
9
+ end
10
+
11
+ describe "#input" do
12
+
13
+ describe "(with a non-template input path)" do
14
+
15
+ before do
16
+ @input_file = $input_dir + "input.txt"
17
+ @input_file.touch
18
+ end
19
+
20
+ it "constructs an Verbatim object" do
21
+ @input = @project.input("input.txt")
22
+ @input.should be_kind_of(Pith::Input::Verbatim)
23
+ @input.file.should == @input_file
24
+ end
25
+
26
+ it "returns the same Input output every time" do
27
+ first_time = @project.input("input.txt")
28
+ second_time = @project.input("input.txt")
29
+ second_time.should equal(first_time)
30
+ end
31
+
32
+ end
33
+
34
+ describe "(with a template input path)" do
35
+
36
+ before do
37
+ @input_file = $input_dir + "input.html.haml"
38
+ @input_file.touch
39
+ end
40
+
41
+ it "constructs an Template object" do
42
+ @input = @project.input("input.html.haml")
43
+ @input.should be_kind_of(Pith::Input::Template)
44
+ @input.file.should == @input_file
45
+ end
46
+
47
+ end
48
+
49
+ describe "(with a template ouput path)" do
50
+
51
+ before do
52
+ @input_file = $input_dir + "input.html.haml"
53
+ @input_file.touch
54
+ end
55
+
56
+ it "can also be used to locate the Template" do
57
+ @project.input("input.html").should == @project.input("input.html.haml")
58
+ end
59
+
60
+ end
61
+
62
+ describe "(with an invalid input path)" do
63
+
64
+ it "complains" do
65
+ lambda do
66
+ @project.input("bogus.path")
67
+ end.should raise_error(Pith::ReferenceError)
68
+ end
69
+
70
+ end
71
+
72
+ end
73
+
74
+ end