middleman-blog 3.0.0 → 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +18 -0
- data/Gemfile +1 -0
- data/features/article_dirs.feature +7 -0
- data/features/blog_sources.feature +5 -0
- data/features/calendar-and-tag.feature +56 -0
- data/features/calendar.feature +71 -12
- data/features/future_date.feature +36 -0
- data/features/layouts.feature +21 -0
- data/features/no_date.feature +9 -0
- data/features/paginate.feature +157 -0
- data/features/published.feature +33 -0
- data/features/support/time_steps.rb +24 -0
- data/features/tags.feature +15 -2
- data/fixtures/article-dirs-app/config.rb +3 -0
- data/fixtures/article-dirs-app/source/blog/2011-01-01-new-article.html.markdown +6 -0
- data/fixtures/article-dirs-app/source/blog/2011-01-01-new-article/example.txt +1 -0
- data/fixtures/article-dirs-app/source/index.html.erb +9 -0
- data/fixtures/article-dirs-app/source/layout.erb +13 -0
- data/fixtures/calendar-and-tag-app/config-directory-indexes.rb +10 -0
- data/fixtures/calendar-and-tag-app/config.rb +7 -0
- data/fixtures/calendar-and-tag-app/source/archive.html.erb +17 -0
- data/fixtures/calendar-and-tag-app/source/blog/2011-01-01-new-article.html.markdown +7 -0
- data/fixtures/calendar-and-tag-app/source/blog/2011-01-02-another-article.html.markdown +8 -0
- data/fixtures/calendar-and-tag-app/source/index.html.erb +7 -0
- data/fixtures/calendar-and-tag-app/source/layout.erb +15 -0
- data/fixtures/calendar-app/config-directory-indexes.rb +10 -0
- data/fixtures/calendar-app/config.rb +3 -1
- data/fixtures/calendar-app/source/calendar.html.erb +4 -4
- data/fixtures/calendar-app/source/index.html.erb +4 -0
- data/fixtures/filename-date-app/config.rb +3 -1
- data/fixtures/filename-date-app/source/layout.erb +1 -1
- data/fixtures/future-date-app/config.rb +5 -0
- data/fixtures/future-date-app/source/_article_template.erb +1 -0
- data/fixtures/future-date-app/source/blog/2011-01-01-new-article.html.markdown +6 -0
- data/fixtures/future-date-app/source/blog/2012-06-19-future-article.html.markdown +6 -0
- data/fixtures/future-date-app/source/blog/2012-06-19-future-article/example.txt +1 -0
- data/fixtures/future-date-app/source/index.html.erb +9 -0
- data/fixtures/future-date-app/source/layout.erb +13 -0
- data/fixtures/layouts-app/config.rb +5 -0
- data/fixtures/layouts-app/source/2011/01/01/first-article.html.markdown +7 -0
- data/fixtures/layouts-app/source/2011/01/01/second-article.html.markdown +7 -0
- data/fixtures/layouts-app/source/2011/01/01/third-article.html.markdown +7 -0
- data/fixtures/layouts-app/source/2011/01/02/article-in-normal-layout.markdown +6 -0
- data/fixtures/layouts-app/source/2011/01/03/article-without-layout.markdown +7 -0
- data/fixtures/layouts-app/source/layouts/first.html.erb +2 -0
- data/fixtures/layouts-app/source/layouts/layout.erb +9 -0
- data/fixtures/layouts-app/source/layouts/second.html.erb +2 -0
- data/fixtures/layouts-app/source/layouts/third.html.erb +2 -0
- data/fixtures/no-date-app/config.rb +3 -0
- data/fixtures/no-date-app/source/_article_template.erb +1 -0
- data/fixtures/no-date-app/source/blog/new-article.html.markdown +6 -0
- data/fixtures/no-date-app/source/index.html.erb +9 -0
- data/fixtures/no-date-app/source/layout.erb +13 -0
- data/fixtures/no-day-app/config.rb +3 -0
- data/fixtures/no-day-app/source/2012-08-testing.html.markdown +6 -0
- data/fixtures/no-day-app/source/layout.erb +13 -0
- data/fixtures/paginate-app/config-directory-indexes.rb +11 -0
- data/fixtures/paginate-app/config-paginate-off.rb +8 -0
- data/fixtures/paginate-app/config.rb +8 -0
- data/fixtures/paginate-app/source/blog/2011-01-01-test-article.html.markdown +6 -0
- data/fixtures/paginate-app/source/blog/2011-01-02-test-article.html.markdown +6 -0
- data/fixtures/paginate-app/source/blog/2011-01-03-test-article.html.markdown +6 -0
- data/fixtures/paginate-app/source/blog/2011-01-04-test-article.html.markdown +6 -0
- data/fixtures/paginate-app/source/blog/2011-01-05-test-article.html.markdown +6 -0
- data/fixtures/paginate-app/source/blog/2011-02-01-test-article.html.markdown +6 -0
- data/fixtures/paginate-app/source/blog/2011-02-02-test-article.html.markdown +6 -0
- data/fixtures/paginate-app/source/calendar.html.erb +28 -0
- data/fixtures/paginate-app/source/index.html.erb +19 -0
- data/fixtures/paginate-app/source/layout.erb +15 -0
- data/fixtures/paginate-app/source/tag.html.erb +23 -0
- data/fixtures/published-app/config.rb +3 -0
- data/fixtures/published-app/source/_article_template.erb +1 -0
- data/fixtures/published-app/source/blog/2011-01-01-new-article.html.markdown +6 -0
- data/fixtures/published-app/source/blog/2012-06-19-draft-article.html.markdown +7 -0
- data/fixtures/published-app/source/blog/2012-06-19-draft-article/example.txt +1 -0
- data/fixtures/published-app/source/index.html.erb +9 -0
- data/fixtures/published-app/source/layout.erb +13 -0
- data/fixtures/tags-app/config-directory-indexes.rb +9 -0
- data/fixtures/tags-app/source/index.html.erb +2 -0
- data/fixtures/tags-app/source/tag.html.erb +3 -3
- data/lib/middleman-blog/blog_article.rb +59 -21
- data/lib/middleman-blog/blog_data.rb +59 -7
- data/lib/middleman-blog/calendar_pages.rb +50 -3
- data/lib/middleman-blog/extension.rb +37 -8
- data/lib/middleman-blog/paginator.rb +127 -0
- data/lib/middleman-blog/tag_pages.rb +20 -1
- data/lib/middleman-blog/template/config.tt +8 -1
- data/lib/middleman-blog/template/source/calendar.html.erb +27 -9
- data/lib/middleman-blog/template/source/index.html.erb +21 -7
- data/lib/middleman-blog/template/source/tag.html.erb +22 -4
- data/lib/middleman-blog/version.rb +1 -1
- data/middleman-blog.gemspec +3 -2
- metadata +211 -55
@@ -0,0 +1,11 @@
|
|
1
|
+
activate :blog do |blog|
|
2
|
+
blog.sources = "blog/:year-:month-:day-:title.html"
|
3
|
+
blog.permalink = "blog/:year-:month-:day-:title.html"
|
4
|
+
blog.calendar_template = 'calendar.html'
|
5
|
+
blog.tag_template = 'tag.html'
|
6
|
+
blog.paginate = true
|
7
|
+
blog.per_page = 5
|
8
|
+
end
|
9
|
+
|
10
|
+
require "middleman-more"
|
11
|
+
activate :directory_indexes
|
@@ -0,0 +1,28 @@
|
|
1
|
+
---
|
2
|
+
pageable: true
|
3
|
+
---
|
4
|
+
Year: '<%= year %>'
|
5
|
+
Month: '<%= month if defined?(month) %>'
|
6
|
+
Day: '<%= day if defined?(day) %>'
|
7
|
+
|
8
|
+
Paginate: <%= paginate %>
|
9
|
+
Article Count: <%= page_articles.size %>
|
10
|
+
<% if paginate %>
|
11
|
+
Page Num: <%= page_number %>
|
12
|
+
Num Pages: <%= num_pages %>
|
13
|
+
Per Page: <%= per_page %>
|
14
|
+
Page Start: <%= page_start %>
|
15
|
+
Page End: <%= page_end %>
|
16
|
+
Next Page: '<%= next_page.url if next_page %>'
|
17
|
+
Prev Page: '<%= prev_page.url if prev_page %>'
|
18
|
+
<% end %>
|
19
|
+
|
20
|
+
<% page_articles.each_with_index do |article, i| %>
|
21
|
+
<article class="<%= (i == 0) ? 'first' : '' %>">
|
22
|
+
<h1><a href="<%= article.url %>"><%= article.title %></a> <span><%= article.date.strftime('%b %e %Y') %></span></h1>
|
23
|
+
|
24
|
+
<%= article.summary %>
|
25
|
+
|
26
|
+
<div class="more"><a href="<%= article.url %>">read on »</a></div>
|
27
|
+
</article>
|
28
|
+
<% end %>
|
@@ -0,0 +1,19 @@
|
|
1
|
+
---
|
2
|
+
pageable: true
|
3
|
+
---
|
4
|
+
|
5
|
+
Paginate: <%= paginate %>
|
6
|
+
Article Count: <%= page_articles.size %>
|
7
|
+
<% if paginate %>
|
8
|
+
Page Num: <%= page_number %>
|
9
|
+
Num Pages: <%= num_pages %>
|
10
|
+
Per Page: <%= per_page %>
|
11
|
+
Page Start: <%= page_start %>
|
12
|
+
Page End: <%= page_end %>
|
13
|
+
Next Page: '<%= next_page.url if next_page %>'
|
14
|
+
Prev Page: '<%= prev_page.url if prev_page %>'
|
15
|
+
<% end %>
|
16
|
+
|
17
|
+
<% page_articles.each do |article| %>
|
18
|
+
<li><a href="<%= article.url %>"><%= article.title %></a> <time><%= article.date.strftime('%b %e') %></time></li>
|
19
|
+
<% end %>
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<!doctype html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
</head>
|
5
|
+
<body>
|
6
|
+
<% if is_blog_article? %>
|
7
|
+
<%= yield %>
|
8
|
+
Url: <%= current_article.url %>
|
9
|
+
Previous: <%= current_article.previous_article.try(:url) %>
|
10
|
+
Next: <%= current_article.next_article.try(:url) %>
|
11
|
+
<% else %>
|
12
|
+
<%= yield %>
|
13
|
+
<% end %>
|
14
|
+
</body>
|
15
|
+
</html>
|
@@ -0,0 +1,23 @@
|
|
1
|
+
---
|
2
|
+
pageable: true
|
3
|
+
per_page: 2
|
4
|
+
---
|
5
|
+
Tag: <%= tagname %>
|
6
|
+
|
7
|
+
Paginate: <%= paginate %>
|
8
|
+
Article Count: <%= page_articles.size %>
|
9
|
+
<% if paginate %>
|
10
|
+
Page Num: <%= page_number %>
|
11
|
+
Num Pages: <%= num_pages %>
|
12
|
+
Per Page: <%= per_page %>
|
13
|
+
Page Start: <%= page_start %>
|
14
|
+
Page End: <%= page_end %>
|
15
|
+
Next Page: '<%= next_page.url if next_page %>'
|
16
|
+
Prev Page: '<%= prev_page.url if prev_page %>'
|
17
|
+
<% end %>
|
18
|
+
|
19
|
+
<% if page_articles %>
|
20
|
+
<% page_articles.each do |article| %>
|
21
|
+
<li><a href="<%= article.url %>"><%= article.title %></a> <time><%= article.date.strftime('%b %e') %></time></li>
|
22
|
+
<% end %>
|
23
|
+
<% end %>
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= current_article.url %>
|
@@ -0,0 +1 @@
|
|
1
|
+
Example Text
|
@@ -0,0 +1,9 @@
|
|
1
|
+
<% blog.articles[0...5].each_with_index do |article, i| %>
|
2
|
+
<article class="<%= (i == 0) ? 'first' : '' %>">
|
3
|
+
<h1><a href="<%= article.url %>"><%= article.title %></a> <span><%= article.date.strftime('%b %e %Y') %></span></h1>
|
4
|
+
|
5
|
+
<%= article.summary %>
|
6
|
+
|
7
|
+
<div class="more"><a href="<%= article.url %>">read on »</a></div>
|
8
|
+
</article>
|
9
|
+
<% end %>
|
@@ -1,7 +1,7 @@
|
|
1
|
-
Tag: <%=
|
1
|
+
Tag: <%= tagname %>
|
2
2
|
|
3
|
-
<% if
|
4
|
-
<%
|
3
|
+
<% if articles %>
|
4
|
+
<% articles[0...12].each do |article| %>
|
5
5
|
<li><a href="<%= article.url %>"><%= article.title %></a> <time><%= article.date.strftime('%b %e') %></time></li>
|
6
6
|
<% end %>
|
7
7
|
<% end %>
|
@@ -1,17 +1,22 @@
|
|
1
|
-
require '
|
1
|
+
require 'active_support/core_ext/time/calculations'
|
2
|
+
|
3
|
+
# Default to UTC
|
4
|
+
Time.zone = "UTC" if Time.zone.nil?
|
2
5
|
|
3
6
|
module Middleman
|
4
7
|
module Blog
|
5
8
|
# A module that adds blog-article methods to Resources.
|
6
9
|
module BlogArticle
|
7
|
-
# The "slug" of the article that shows up in its URL.
|
8
|
-
# @return [String]
|
9
|
-
attr_accessor :slug
|
10
|
-
|
11
10
|
# Render this resource
|
12
11
|
# @return [String]
|
13
12
|
def render(opts={}, locs={}, &block)
|
14
|
-
|
13
|
+
if opts[:layout].nil?
|
14
|
+
if metadata[:options] && !metadata[:options][:layout].nil?
|
15
|
+
opts[:layout] = metadata[:options][:layout]
|
16
|
+
end
|
17
|
+
opts[:layout] = app.blog.options.layout if opts[:layout].nil?
|
18
|
+
opts[:layout] = opts[:layout].to_s if opts[:layout].is_a? Symbol
|
19
|
+
end
|
15
20
|
|
16
21
|
content = super(opts, locs, &block)
|
17
22
|
|
@@ -30,6 +35,12 @@ module Middleman
|
|
30
35
|
data["title"]
|
31
36
|
end
|
32
37
|
|
38
|
+
# Whether or not this article has been published
|
39
|
+
# @return [Boolean]
|
40
|
+
def published?
|
41
|
+
data["published"] != false and date <= Time.current
|
42
|
+
end
|
43
|
+
|
33
44
|
# The body of this article, in HTML. This is for
|
34
45
|
# things like RSS feeds or lists of articles - individual
|
35
46
|
# articles will automatically be rendered from their
|
@@ -46,12 +57,26 @@ module Middleman
|
|
46
57
|
# @return [String]
|
47
58
|
def summary
|
48
59
|
@_summary ||= begin
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
60
|
+
source = app.template_data_for_file(source_file).dup
|
61
|
+
|
62
|
+
summary_source = if app.blog.options.summary_generator
|
63
|
+
app.blog.options.summary_generator.call(self, source)
|
64
|
+
else
|
65
|
+
default_summary_generator(source)
|
66
|
+
end
|
67
|
+
|
68
|
+
md = metadata.dup
|
69
|
+
locs = md[:locals]
|
70
|
+
opts = md[:options].merge({:template_body => summary_source})
|
71
|
+
app.render_individual_file(source_file, locs, opts)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def default_summary_generator(source)
|
76
|
+
if source =~ app.blog.options.summary_separator
|
77
|
+
source.split(app.blog.options.summary_separator).first
|
78
|
+
else
|
79
|
+
source.match(/(.{1,#{app.blog.options.summary_length}}.*?)(\n|\Z)/m).to_s
|
55
80
|
end
|
56
81
|
end
|
57
82
|
|
@@ -67,22 +92,31 @@ module Middleman
|
|
67
92
|
end
|
68
93
|
end
|
69
94
|
|
95
|
+
# Retrieve a section of the source path
|
96
|
+
# @param [String] The part of the path, e.g. "year", "month", "day", "title"
|
97
|
+
# @return [String]
|
98
|
+
def path_part(part)
|
99
|
+
@_path_parts ||= app.blog.path_matcher.match(path).captures
|
100
|
+
|
101
|
+
@_path_parts[app.blog.matcher_indexes[part]]
|
102
|
+
end
|
103
|
+
|
70
104
|
# Attempt to figure out the date of the post. The date should be
|
71
105
|
# present in the source path, but users may also provide a date
|
72
106
|
# in the frontmatter in order to provide a time of day for sorting
|
73
107
|
# reasons.
|
74
108
|
#
|
75
|
-
# @return [
|
109
|
+
# @return [TimeWithZone]
|
76
110
|
def date
|
77
111
|
return @_date if @_date
|
78
112
|
|
79
113
|
frontmatter_date = data["date"]
|
80
114
|
|
81
115
|
# First get the date from frontmatter
|
82
|
-
if frontmatter_date.is_a?
|
83
|
-
@_date =
|
116
|
+
if frontmatter_date.is_a? Time
|
117
|
+
@_date = frontmatter_date.in_time_zone
|
84
118
|
else
|
85
|
-
@_date = frontmatter_date
|
119
|
+
@_date = Time.zone.parse(frontmatter_date.to_s)
|
86
120
|
end
|
87
121
|
|
88
122
|
# Next figure out the date from the filename
|
@@ -90,13 +124,11 @@ module Middleman
|
|
90
124
|
app.blog.options.sources.include?(":month") &&
|
91
125
|
app.blog.options.sources.include?(":day")
|
92
126
|
|
93
|
-
|
94
|
-
|
95
|
-
filename_date = Date.new(date_parts[0].to_i, date_parts[1].to_i, date_parts[2].to_i)
|
127
|
+
filename_date = Time.zone.local(path_part("year").to_i, path_part("month").to_i, path_part("day").to_i)
|
96
128
|
if @_date
|
97
|
-
raise "The date in #{path}'s filename doesn't match the date in its frontmatter" unless @_date.to_date == filename_date
|
129
|
+
raise "The date in #{path}'s filename doesn't match the date in its frontmatter" unless @_date.to_date == filename_date.to_date
|
98
130
|
else
|
99
|
-
@_date = filename_date.
|
131
|
+
@_date = filename_date.to_time.in_time_zone
|
100
132
|
end
|
101
133
|
end
|
102
134
|
|
@@ -105,6 +137,12 @@ module Middleman
|
|
105
137
|
@_date
|
106
138
|
end
|
107
139
|
|
140
|
+
# The "slug" of the article that shows up in its URL.
|
141
|
+
# @return [String]
|
142
|
+
def slug
|
143
|
+
@_slug ||= path_part("title")
|
144
|
+
end
|
145
|
+
|
108
146
|
# The previous (chronologically earlier) article before this one
|
109
147
|
# or nil if this is the first article.
|
110
148
|
# @return [Middleman::Sitemap::Resource]
|
@@ -8,6 +8,10 @@ module Middleman
|
|
8
8
|
# @return [Regex]
|
9
9
|
attr_reader :path_matcher
|
10
10
|
|
11
|
+
# A hash of indexes into the path_matcher captures
|
12
|
+
# @return [Hash]
|
13
|
+
attr_reader :matcher_indexes
|
14
|
+
|
11
15
|
# The configured options for this blog
|
12
16
|
# @return [Thor::CoreExt::HashWithIndifferentAccess]
|
13
17
|
attr_reader :options
|
@@ -25,17 +29,27 @@ module Middleman
|
|
25
29
|
sub(":year", "(\\d{4})").
|
26
30
|
sub(":month", "(\\d{2})").
|
27
31
|
sub(":day", "(\\d{2})").
|
28
|
-
sub(":title", "(
|
32
|
+
sub(":title", "([^/]+)")
|
33
|
+
|
34
|
+
subdir_matcher = matcher.sub(/\\\.[^.]+$/, "(/.*)$")
|
29
35
|
|
30
36
|
@path_matcher = /^#{matcher}/
|
37
|
+
@subdir_matcher = /^#{subdir_matcher}/
|
38
|
+
|
39
|
+
# Build a hash of part name to capture index, e.g. {"year" => 0}
|
40
|
+
@matcher_indexes = {}
|
41
|
+
options.sources.scan(/:year|:month|:day|:title/).
|
42
|
+
each_with_index do |key, i|
|
43
|
+
@matcher_indexes[key[1..-1]] = i
|
44
|
+
end
|
45
|
+
# The path always appears at the end.
|
46
|
+
@matcher_indexes["path"] = @matcher_indexes.size
|
31
47
|
end
|
32
48
|
|
33
|
-
# A list of all blog articles, sorted by date
|
49
|
+
# A list of all blog articles, sorted by descending date
|
34
50
|
# @return [Array<Middleman::Sitemap::Resource>]
|
35
51
|
def articles
|
36
|
-
@_articles.
|
37
|
-
b.date <=> a.date
|
38
|
-
end
|
52
|
+
@_articles.sort_by(&:date).reverse
|
39
53
|
end
|
40
54
|
|
41
55
|
# The BlogArticle for the given path, or nil if one doesn't exist.
|
@@ -61,6 +75,10 @@ module Middleman
|
|
61
75
|
end
|
62
76
|
end
|
63
77
|
|
78
|
+
tags.each do |tag, articles|
|
79
|
+
tags[tag] = articles.sort_by(&:date).reverse
|
80
|
+
end
|
81
|
+
|
64
82
|
tags
|
65
83
|
end
|
66
84
|
|
@@ -69,12 +87,15 @@ module Middleman
|
|
69
87
|
# @return [void]
|
70
88
|
def manipulate_resource_list(resources)
|
71
89
|
@_articles = []
|
90
|
+
used_resources = []
|
72
91
|
|
73
92
|
resources.each do |resource|
|
74
93
|
if resource.path =~ path_matcher
|
75
94
|
resource.extend BlogArticle
|
76
|
-
|
77
|
-
|
95
|
+
|
96
|
+
# Skip articles that are not published (in non-development environments)
|
97
|
+
next unless @app.environment == :development || resource.published?
|
98
|
+
|
78
99
|
# compute output path:
|
79
100
|
# substitute date parts to path pattern
|
80
101
|
resource.destination_path = options.permalink.
|
@@ -86,8 +107,39 @@ module Middleman
|
|
86
107
|
resource.destination_path = Middleman::Util.normalize_path(resource.destination_path)
|
87
108
|
|
88
109
|
@_articles << resource
|
110
|
+
|
111
|
+
elsif resource.path =~ @subdir_matcher
|
112
|
+
match = $~.captures
|
113
|
+
|
114
|
+
article_path = options.sources.
|
115
|
+
sub(':year', match[@matcher_indexes["year"]]).
|
116
|
+
sub(':month', match[@matcher_indexes["month"]]).
|
117
|
+
sub(':day', match[@matcher_indexes["day"]]).
|
118
|
+
sub(':title', match[@matcher_indexes["title"]])
|
119
|
+
|
120
|
+
article = @app.sitemap.find_resource_by_path(article_path)
|
121
|
+
raise "Article for #{resource.path} not found" if article.nil?
|
122
|
+
article.extend BlogArticle
|
123
|
+
|
124
|
+
# Skip files that belong to articles that are not published (in non-development environments)
|
125
|
+
next unless @app.environment == :development || article.published?
|
126
|
+
|
127
|
+
# The subdir path is the article path with the index file name
|
128
|
+
# or file extension stripped off.
|
129
|
+
resource.destination_path = options.permalink.
|
130
|
+
sub(':year', article.date.year.to_s).
|
131
|
+
sub(':month', article.date.month.to_s.rjust(2,'0')).
|
132
|
+
sub(':day', article.date.day.to_s.rjust(2,'0')).
|
133
|
+
sub(':title', article.slug).
|
134
|
+
sub(/(\/#{@app.index_file}$)|(\.[^.]+$)|(\/$)/, match[@matcher_indexes["path"]])
|
135
|
+
|
136
|
+
resource.destination_path = Middleman::Util.normalize_path(resource.destination_path)
|
89
137
|
end
|
138
|
+
|
139
|
+
used_resources << resource
|
90
140
|
end
|
141
|
+
|
142
|
+
used_resources
|
91
143
|
end
|
92
144
|
end
|
93
145
|
end
|