benoit 0.2.2 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/.gitignore +1 -2
- data/.rspec +1 -1
- data/Assetfile +19 -4
- data/Gemfile.lock +29 -5
- data/Guardfile +28 -0
- data/Rakefile +2 -0
- data/benoit.gemspec +5 -0
- data/bin/benoit +25 -7
- data/lib/benoit/cadenza.rb +2 -0
- data/lib/benoit/cadenza/blocks.rb +18 -0
- data/lib/benoit/compiler_error.rb +5 -1
- data/lib/benoit/filters/base_filter.rb +4 -5
- data/lib/benoit/filters/cadenza_filter.rb +19 -16
- data/lib/benoit/filters/markdown_filter.rb +36 -4
- data/lib/benoit/logger.rb +2 -2
- data/lib/benoit/pipeline_project.rb +23 -23
- data/lib/benoit/server.rb +47 -0
- data/lib/benoit/server/remote.rb +29 -0
- data/lib/benoit/utils/finds_layouts_for_template.rb +28 -30
- data/lib/benoit/version.rb +1 -1
- data/spec/features/build_command.feature +1 -1
- data/spec/features/frontmatter_metadata.feature +3 -21
- data/spec/features/javascript_files.feature +1 -0
- data/spec/features/markdown_files.feature +41 -0
- data/spec/features/output_filters.feature +2 -7
- data/spec/features/page_layouts.feature +4 -28
- data/spec/features/pagination.feature +1 -0
- data/spec/lib/filters/base_filter_spec.rb +4 -33
- data/spec/lib/filters/markdown_filter_spec.rb +31 -8
- data/spec/lib/filters/sass_filter_spec.rb +2 -2
- data/spec/lib/site_context_spec.rb +2 -2
- data/spec/steps/staticly_steps.rb +1 -1
- data/spec/support/spec_helpers/memory_file_wrapper.rb +2 -6
- data/vendor/rake-pipeline/GETTING_STARTED.md +11 -11
- data/vendor/rake-pipeline/bin/rakep +1 -1
- data/vendor/rake-pipeline/lib/rake-pipeline.rb +2 -49
- data/vendor/rake-pipeline/lib/rake-pipeline/cli.rb +0 -1
- data/vendor/rake-pipeline/lib/rake-pipeline/file_wrapper.rb +2 -9
- data/vendor/rake-pipeline/lib/rake-pipeline/filter.rb +1 -19
- data/vendor/rake-pipeline/lib/rake-pipeline/manifest.rb +4 -0
- data/vendor/rake-pipeline/lib/rake-pipeline/middleware.rb +1 -2
- data/vendor/rake-pipeline/lib/rake-pipeline/project.rb +3 -2
- data/vendor/rake-pipeline/rake-pipeline.gemspec +1 -1
- data/vendor/rake-pipeline/spec/cli_spec.rb +0 -2
- data/vendor/rake-pipeline/spec/concat_filter_spec.rb +4 -4
- data/vendor/rake-pipeline/spec/filter_spec.rb +0 -35
- data/vendor/rake-pipeline/spec/gsub_filter_spec.rb +5 -5
- data/vendor/rake-pipeline/spec/ordering_concat_filter_spec.rb +4 -4
- data/vendor/rake-pipeline/spec/rake_acceptance_spec.rb +18 -0
- data/vendor/rake-pipeline/spec/support/spec_helpers/filters.rb +1 -1
- data/vendor/rake-pipeline/spec/support/spec_helpers/memory_file_wrapper.rb +2 -6
- metadata +77 -2
data/lib/benoit/logger.rb
CHANGED
@@ -16,7 +16,7 @@ module Benoit
|
|
16
16
|
message = <<-EOM
|
17
17
|
Benoit encountered an error.
|
18
18
|
|
19
|
-
#{ex
|
19
|
+
#{ex}
|
20
20
|
#{sub_message}
|
21
21
|
|
22
22
|
#{ENV.map { |k,v| "#{k}=#{v}" }.join("\n")}
|
@@ -33,7 +33,7 @@ module Benoit
|
|
33
33
|
if Benoit.config.output_mode == :app
|
34
34
|
$stderr.puts({ line_no: nil, message: ex.message, path: __FILE__ }.to_json)
|
35
35
|
else
|
36
|
-
$stderr.puts ex.
|
36
|
+
$stderr.puts ex.to_s
|
37
37
|
$stderr.puts ex.backtrace
|
38
38
|
end
|
39
39
|
end
|
@@ -24,36 +24,36 @@ module Benoit
|
|
24
24
|
if Benoit.config.clean_before_build?
|
25
25
|
Benoit::Cleaner.run
|
26
26
|
end
|
27
|
-
project.pipelines.each do |pipeline|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
27
|
+
# project.pipelines.each do |pipeline|
|
28
|
+
# pipeline.register_invocation_hook :after_task, BuildNotifiers::FileBuiltNotifier
|
29
|
+
# pipeline.register_invocation_hook :before_filter, BuildNotifiers::ProgressNotifier
|
30
|
+
# pipeline.register_invocation_hook :filters_ready, ->(pipeline){
|
31
|
+
# require 'ostruct'
|
32
|
+
# paths_map = {}
|
33
|
+
# # pipeline.output_files.each do |output|
|
34
|
+
# # input = output.original_inputs.first
|
35
35
|
|
36
|
-
|
36
|
+
# # wrapper = OpenStruct.new(path: output.path, read: input.read, fullpath: input.fullpath)
|
37
37
|
|
38
|
-
|
38
|
+
# # paths_map[input.path] = output.path
|
39
39
|
|
40
|
-
|
40
|
+
# # PageMetadata::Store.current[wrapper]
|
41
41
|
|
42
|
-
|
42
|
+
# # end
|
43
43
|
|
44
|
-
|
44
|
+
# current_site = CurrentSite.load
|
45
45
|
|
46
|
-
|
46
|
+
# current_site.paths_map = paths_map
|
47
47
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
end
|
48
|
+
# # Load ALL filters (including filters within filters)
|
49
|
+
# filters = recursively_load_filters_from_pipeline(pipeline)
|
50
|
+
# filters.each do |filter|
|
51
|
+
# if filter.respond_to? :current_site=
|
52
|
+
# filter.current_site = current_site
|
53
|
+
# end
|
54
|
+
# end
|
55
|
+
# }
|
56
|
+
# end
|
57
57
|
project.invoke
|
58
58
|
end
|
59
59
|
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'rake-pipeline/middleware'
|
2
|
+
require 'rack/server'
|
3
|
+
|
4
|
+
|
5
|
+
module Benoit
|
6
|
+
class Server < Rack::Server
|
7
|
+
|
8
|
+
require_relative 'server/remote'
|
9
|
+
|
10
|
+
DEFAULT_APP = proc { [404, { "Content-Type" => "text/plain" }, ["not found"]] }
|
11
|
+
|
12
|
+
def initialize(project, options={})
|
13
|
+
@project = project
|
14
|
+
super options
|
15
|
+
end
|
16
|
+
|
17
|
+
def app
|
18
|
+
project = @project.project
|
19
|
+
Rack::Builder.new do
|
20
|
+
use Rack::ShowExceptions
|
21
|
+
use CustomExceptionHandler
|
22
|
+
map "/_remote" do
|
23
|
+
run Benoit::Server::Remote.new(project)
|
24
|
+
end
|
25
|
+
run Rake::Pipeline::Middleware.new DEFAULT_APP, project
|
26
|
+
end.to_app
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
class CustomExceptionHandler
|
32
|
+
|
33
|
+
def initialize(app)
|
34
|
+
@app = app
|
35
|
+
end
|
36
|
+
|
37
|
+
def call(env)
|
38
|
+
@app.call(env)
|
39
|
+
rescue StandardError => ex
|
40
|
+
Benoit::Cleaner.run
|
41
|
+
Logger.error "#{ex.message} (#{ex.class.name})"
|
42
|
+
Logger.error ex.backtrace.join("\n")
|
43
|
+
raise ex
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Benoit
|
2
|
+
class Server
|
3
|
+
class Remote
|
4
|
+
|
5
|
+
def initialize(project)
|
6
|
+
@project = project
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(env)
|
10
|
+
code = 200
|
11
|
+
|
12
|
+
case env['PATH_INFO']
|
13
|
+
when '/clean'
|
14
|
+
Benoit::Cleaner.run
|
15
|
+
body = "cleaned"
|
16
|
+
when '/build'
|
17
|
+
@project.invoke
|
18
|
+
body = "built"
|
19
|
+
end
|
20
|
+
|
21
|
+
code = 200 if body
|
22
|
+
body || (body, code = ["not found", 404])
|
23
|
+
|
24
|
+
[code, {"Content-Type" => "application/json"}, {response: body}.to_json]
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -16,33 +16,32 @@ module Benoit::Utils
|
|
16
16
|
class FindsLayoutsForTemplate
|
17
17
|
|
18
18
|
|
19
|
-
FrontMatterLookupStrategy = ->(input) {
|
20
|
-
input =
|
21
|
-
if input.respond_to? :final_output and input.final_output
|
22
|
-
input.final_output
|
23
|
-
else
|
24
|
-
input
|
25
|
-
end
|
26
|
-
metadata = Benoit::PageMetadata::Store.current[input]
|
27
|
-
return unless metadata
|
28
|
-
metadata["layout"] || metadata["template"]
|
29
|
-
}
|
30
|
-
|
31
|
-
CadenzaInheritanceLookupStrategy = ->(input) {
|
32
|
-
extends_pattern = /\{% extends "([\.\w_-]+)" %\}/
|
33
|
-
return unless File.exist?(input.path)
|
34
|
-
match_data = File.read(input.path).match(extends_pattern)
|
35
|
-
return unless match_data
|
36
|
-
match_data.captures[0]
|
37
|
-
}
|
38
|
-
|
39
19
|
attr_reader :root, :template_path, :load_paths, :input
|
40
20
|
|
21
|
+
def self.first_layout(content, options={})
|
22
|
+
FindsLayoutsForTemplate.new(content, options).lookup_layout
|
23
|
+
end
|
24
|
+
|
41
25
|
def initialize(input, options)
|
42
|
-
|
43
|
-
@root = options.delete(:root)
|
44
|
-
@template_path = input
|
26
|
+
if input.respond_to? :path
|
45
27
|
@input = input
|
28
|
+
@template_path = input
|
29
|
+
else
|
30
|
+
@content = input
|
31
|
+
end
|
32
|
+
@load_paths = options.delete(:load_paths) || [""]
|
33
|
+
@root = options.delete(:root)
|
34
|
+
end
|
35
|
+
|
36
|
+
def match_extends_line(content)
|
37
|
+
extends_pattern = /\{% extends "([\.\w-]+)" %\}/
|
38
|
+
match_data = content.match(extends_pattern)
|
39
|
+
return unless match_data
|
40
|
+
match_data.captures[0]
|
41
|
+
end
|
42
|
+
|
43
|
+
def lookup_layout
|
44
|
+
match_extends_line(@content)
|
46
45
|
end
|
47
46
|
|
48
47
|
def lookup_layouts
|
@@ -53,24 +52,23 @@ module Benoit::Utils
|
|
53
52
|
|
54
53
|
def recursively_lookup_layouts_for_file(input, template_list=[])
|
55
54
|
|
56
|
-
parent_template =
|
55
|
+
parent_template = match_file input, root
|
57
56
|
|
58
57
|
if parent_template
|
59
58
|
normalized_path = NormalizesPathToTemplate(parent_template, load_paths)
|
60
59
|
template_list << normalized_path
|
61
|
-
input = Rake::Pipeline::FileWrapper.new(Dir.pwd, normalized_path)
|
62
60
|
# TODO: Not a fan of Dir.pwd here, but it will always work. Is there a better way to get the correct input root?
|
61
|
+
input = Rake::Pipeline::FileWrapper.new(Dir.pwd, normalized_path)
|
63
62
|
recursively_lookup_layouts_for_file(input, template_list)
|
64
63
|
end
|
65
64
|
|
66
65
|
template_list
|
67
66
|
end
|
68
67
|
|
69
|
-
def
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
end
|
68
|
+
def match_file(input, root)
|
69
|
+
return unless File.exist?(input.path)
|
70
|
+
content = File.read(input.path)
|
71
|
+
match_extends_line(content)
|
74
72
|
end
|
75
73
|
|
76
74
|
end
|
data/lib/benoit/version.rb
CHANGED
@@ -7,7 +7,7 @@ Feature: benoit build
|
|
7
7
|
When I build the site
|
8
8
|
Then a directory named "_build" should exist
|
9
9
|
|
10
|
-
Scenario: Defaults cache path to "
|
10
|
+
Scenario: Defaults cache path to "/tmp/.benoit/tmpcache/{site}"
|
11
11
|
When I build the site
|
12
12
|
Then a cache directory should exist for that site
|
13
13
|
|
@@ -1,5 +1,6 @@
|
|
1
1
|
Feature: Site Context
|
2
2
|
|
3
|
+
@backlog
|
3
4
|
Scenario: Groups posts by type
|
4
5
|
Given a site named "subpages"
|
5
6
|
And a file named "index.html" with content:
|
@@ -29,6 +30,7 @@ Feature: Site Context
|
|
29
30
|
Scenario: Group arbitrary page types by type
|
30
31
|
|
31
32
|
|
33
|
+
@backlog
|
32
34
|
Scenario: Loads content from markdown files
|
33
35
|
Given a site named "test"
|
34
36
|
And a file named "index.html" with content:
|
@@ -53,6 +55,7 @@ Feature: Site Context
|
|
53
55
|
This is the content
|
54
56
|
"""
|
55
57
|
|
58
|
+
@backlog
|
56
59
|
Scenario: Parses field named 'date' as a date object for reliable sorting
|
57
60
|
Given a site
|
58
61
|
And a file containing metadata:
|
@@ -76,24 +79,3 @@ Feature: Site Context
|
|
76
79
|
2012-12-12
|
77
80
|
2022-04-13
|
78
81
|
"""
|
79
|
-
|
80
|
-
Scenario: Metadata contains rendered Markdown
|
81
|
-
Given a site
|
82
|
-
And a file with an extension of ".markdown" with content:
|
83
|
-
"""
|
84
|
-
---
|
85
|
-
type: post
|
86
|
-
---
|
87
|
-
|
88
|
-
[a link](http://www.example.com)
|
89
|
-
"""
|
90
|
-
And a file named "index.html" with content:
|
91
|
-
"""
|
92
|
-
{% for post in site.posts %}{{ post.content }}
|
93
|
-
{% endfor %}
|
94
|
-
"""
|
95
|
-
When I build the site
|
96
|
-
Then the output file "index.html" should have content:
|
97
|
-
"""
|
98
|
-
<a href="http://www.example.com">a link</a>
|
99
|
-
"""
|
@@ -0,0 +1,41 @@
|
|
1
|
+
Feature: Markdown Files
|
2
|
+
|
3
|
+
Benoit converts Markdown files to HTML (Markdown files are those that end in ".markdown", ".md", ".mkdown" or ".mdown"). It uses the RedCarpet engine for rendering Markdown. Currently, there is no way to configure Markdown settings ([want this? Let me know!][feedback])
|
4
|
+
|
5
|
+
If you use a layout for your site, you can use the regular {% extends ... %} tag and {% block ... %} tags just like you do in HTML files. If you need to use more template features than just the layouts, then you will need to use HTML rather than Markdown.
|
6
|
+
|
7
|
+
Scenario: Converts Markdown to HTML
|
8
|
+
Given a file named "cadenza.markdown" with content:
|
9
|
+
"""
|
10
|
+
[blah](http://www.google.com)
|
11
|
+
"""
|
12
|
+
When I build the site
|
13
|
+
Then the output file "cadenza.html" should only have content:
|
14
|
+
"""
|
15
|
+
<p><a href="http://www.google.com">blah</a></p>
|
16
|
+
|
17
|
+
"""
|
18
|
+
|
19
|
+
Scenario: Properly wraps Markdown files in specified layout
|
20
|
+
Given a file named "cadenza.markdown" with content:
|
21
|
+
"""
|
22
|
+
{% extends "_layout.html" %}
|
23
|
+
{% block content %}
|
24
|
+
[this is my content](http://www.google.com)
|
25
|
+
{% endblock %}
|
26
|
+
"""
|
27
|
+
And a file named "_layouts/_layout.html" with content:
|
28
|
+
"""
|
29
|
+
<section id="main">
|
30
|
+
{% block content %}{% endblock %}
|
31
|
+
</section>
|
32
|
+
"""
|
33
|
+
When I run `benoit build`
|
34
|
+
Then the output file "cadenza.html" should have content:
|
35
|
+
"""
|
36
|
+
<section id="main">
|
37
|
+
|
38
|
+
<a href="http://www.google.com">this is my content</a>
|
39
|
+
|
40
|
+
</section>
|
41
|
+
"""
|
@@ -4,14 +4,9 @@ Feature: format_like Output Filter
|
|
4
4
|
Given a site
|
5
5
|
|
6
6
|
Scenario: Formats simple dates according to passed example
|
7
|
-
Given a file
|
7
|
+
Given a file with an extension of ".html" with content:
|
8
8
|
"""
|
9
|
-
|
10
|
-
type: post
|
11
|
-
"""
|
12
|
-
And that file has content:
|
13
|
-
"""
|
14
|
-
{{page.date|format_like:"March 23, 2012"}}
|
9
|
+
{{"2012-12-23"|format_like:"March 23, 2012"}}
|
15
10
|
"""
|
16
11
|
When I build the site
|
17
12
|
Then the output file should have content:
|
@@ -25,36 +25,12 @@ Feature: Page Layouts
|
|
25
25
|
</section>
|
26
26
|
"""
|
27
27
|
|
28
|
-
|
29
|
-
Given a file with an extension of ".html" containing metadata:
|
30
|
-
"""
|
31
|
-
layout: _layout
|
32
|
-
"""
|
33
|
-
And that file has content:
|
34
|
-
"""
|
35
|
-
{% block content %}CONTENT{% endblock %}
|
36
|
-
"""
|
37
|
-
And a layout named "_layout" with content:
|
38
|
-
"""
|
39
|
-
!!!
|
40
|
-
{% block content %}{% endblock %}
|
41
|
-
!!!
|
42
|
-
"""
|
43
|
-
When I build the site
|
44
|
-
Then the output file should have content:
|
45
|
-
"""
|
46
|
-
!!!
|
47
|
-
CONTENT
|
48
|
-
!!!
|
49
|
-
"""
|
50
|
-
|
28
|
+
@backlog
|
51
29
|
Scenario: Doesn't require layouts prefixed with underscore
|
52
|
-
Given a file with an extension of ".html"
|
53
|
-
"""
|
54
|
-
layout: layout
|
55
|
-
"""
|
56
|
-
And that file has content:
|
30
|
+
Given a file with an extension of ".html" with content:
|
57
31
|
"""
|
32
|
+
{% extends "layout.html" %}
|
33
|
+
|
58
34
|
{% block content %}CONTENT{% endblock %}
|
59
35
|
"""
|
60
36
|
And a layout named "_layout" with content:
|
@@ -14,11 +14,11 @@ describe Benoit::Filters::BaseFilter do
|
|
14
14
|
let(:html_input) { "<html><head></head><body><h1>TESTING</h1></body></html>" }
|
15
15
|
|
16
16
|
def input_file(name, content)
|
17
|
-
MemoryFileWrapper.new("/path/to/input", name, "UTF-8",
|
17
|
+
MemoryFileWrapper.new("/path/to/input", name, "UTF-8", content)
|
18
18
|
end
|
19
19
|
|
20
20
|
def output_file(name)
|
21
|
-
MemoryFileWrapper.new("/path/to/output", name, "UTF-8"
|
21
|
+
MemoryFileWrapper.new("/path/to/output", name, "UTF-8")
|
22
22
|
end
|
23
23
|
|
24
24
|
def setup_filter(filter, input_filename='index.html')
|
@@ -28,25 +28,14 @@ describe Benoit::Filters::BaseFilter do
|
|
28
28
|
filter.input_files = [input_file(input_filename, html_input)]
|
29
29
|
filter.output_root= "/path/to/output"
|
30
30
|
filter.rake_application = Rake::Application.new
|
31
|
-
filter.current_site = {}
|
32
31
|
filter
|
33
32
|
end
|
34
33
|
|
35
|
-
def setup_final_outputs(filter)
|
36
|
-
filter.input_files.each_with_index do |input,i|
|
37
|
-
output = filter.output_files[i]
|
38
|
-
filter.current_site.merge!(input.path => { "content" => input.read })
|
39
|
-
filter.current_site.merge!(output.path => { "content" => input.read })
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
34
|
it "generates output directly from the input" do
|
44
35
|
filter = setup_filter basic_filter.new
|
45
36
|
|
46
37
|
expect(filter.output_files).to eq([output_file("index.html")])
|
47
38
|
|
48
|
-
setup_final_outputs(filter)
|
49
|
-
|
50
39
|
tasks = filter.generate_rake_tasks
|
51
40
|
tasks.each(&:invoke)
|
52
41
|
|
@@ -74,7 +63,7 @@ describe Benoit::Filters::BaseFilter do
|
|
74
63
|
|
75
64
|
let(:path_writer) do
|
76
65
|
Class.new(described_class) do
|
77
|
-
build_output do |
|
66
|
+
build_output do |input|
|
78
67
|
input.path
|
79
68
|
end
|
80
69
|
end
|
@@ -83,8 +72,6 @@ describe Benoit::Filters::BaseFilter do
|
|
83
72
|
it "gets output from calling builder" do
|
84
73
|
filter = setup_filter builder_filter.new
|
85
74
|
|
86
|
-
setup_final_outputs(filter)
|
87
|
-
|
88
75
|
tasks = filter.generate_rake_tasks
|
89
76
|
tasks.each(&:invoke)
|
90
77
|
|
@@ -92,26 +79,10 @@ describe Benoit::Filters::BaseFilter do
|
|
92
79
|
expect(file.body).to eq("blah")
|
93
80
|
end
|
94
81
|
|
95
|
-
it "passes
|
96
|
-
filter = setup_filter page_filter.new
|
97
|
-
|
98
|
-
setup_final_outputs(filter)
|
99
|
-
|
100
|
-
page = filter.current_site[filter.output_files.first.path]
|
101
|
-
|
102
|
-
tasks = filter.generate_rake_tasks
|
103
|
-
tasks.each(&:invoke)
|
104
|
-
|
105
|
-
file = MemoryFileWrapper.files["/path/to/output/index.html"]
|
106
|
-
expect(file.body).to eq(page["content"])
|
107
|
-
end
|
108
|
-
|
109
|
-
it "passes input to builder when needed" do
|
82
|
+
it "passes input to builder" do
|
110
83
|
filter = setup_filter path_writer.new
|
111
84
|
input = filter.input_files.first
|
112
85
|
|
113
|
-
setup_final_outputs(filter)
|
114
|
-
|
115
86
|
tasks = filter.generate_rake_tasks
|
116
87
|
tasks.each(&:invoke)
|
117
88
|
|