Collections
This commit is contained in:
parent
445443cffc
commit
a95dbb6367
|
@ -55,3 +55,5 @@ CaseIndentation:
|
||||||
IndentWhenRelativeTo: end
|
IndentWhenRelativeTo: end
|
||||||
TrivialAccessors:
|
TrivialAccessors:
|
||||||
ExactNameMatch: true
|
ExactNameMatch: true
|
||||||
|
PerceivedComplexity:
|
||||||
|
Enabled: false
|
145
middleman-core/features/collections.feature
Normal file
145
middleman-core/features/collections.feature
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
Feature: Collections
|
||||||
|
Scenario: Lazy query
|
||||||
|
Given a fixture app "collections-app"
|
||||||
|
And a file named "config.rb" with:
|
||||||
|
"""
|
||||||
|
articles1 = collection :articles1, resources.select { |r|
|
||||||
|
matcher = ::Middleman::Util::UriTemplates.uri_template('blog1/{year}-{month}-{day}-{title}.html')
|
||||||
|
::Middleman::Util::UriTemplates.extract_params(matcher, ::Middleman::Util.normalize_path(r.url))
|
||||||
|
}
|
||||||
|
|
||||||
|
everything = resources.select do |r|
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_tags(resource)
|
||||||
|
if resource.data.tags.is_a? String
|
||||||
|
resource.data.tags.split(',').map(&:strip)
|
||||||
|
else
|
||||||
|
resource.data.tags
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def group_lookup(resource, sum)
|
||||||
|
results = Array(get_tags(resource)).map(&:to_s).map(&:to_sym)
|
||||||
|
|
||||||
|
results.each do |k|
|
||||||
|
sum[k] ||= []
|
||||||
|
sum[k] << resource
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
tags = everything
|
||||||
|
.select { |resource| resource.data.tags }
|
||||||
|
.each_with_object({}, &method(:group_lookup))
|
||||||
|
|
||||||
|
class Wrapper
|
||||||
|
attr_reader :stuff
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@stuff = Set.new
|
||||||
|
end
|
||||||
|
|
||||||
|
def <<((k, v))
|
||||||
|
@stuff << k
|
||||||
|
self
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
collection :wrapped, tags.reduce(Wrapper.new, :<<)
|
||||||
|
|
||||||
|
set :tags, tags # Expose to templates
|
||||||
|
|
||||||
|
collection :first_tag, tags.keys.sort.first
|
||||||
|
"""
|
||||||
|
And a file named "source/index.html.erb" with:
|
||||||
|
"""
|
||||||
|
<% collection(:articles1).each do |article| %>
|
||||||
|
Article1: <%= article.data.title %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
Tag Count: <%= collection(:wrapped).stuff.length %>
|
||||||
|
|
||||||
|
<% config[:tags].value.each do |k, items| %>
|
||||||
|
Tag: <%= k %> (<%= items.length %>)
|
||||||
|
<% items.each do |article| %>
|
||||||
|
Article (<%= k %>): <%= article.data.title %>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
First Tag: <%= collection(:first_tag) %>
|
||||||
|
"""
|
||||||
|
Given the Server is running at "collections-app"
|
||||||
|
When I go to "index.html"
|
||||||
|
Then I should see 'Article1: Blog1 Newer Article'
|
||||||
|
And I should see 'Article1: Blog1 Another Article'
|
||||||
|
And I should see 'Tag: foo (4)'
|
||||||
|
And I should see 'Article (foo): Blog1 Newer Article'
|
||||||
|
And I should see 'Article (foo): Blog1 Another Article'
|
||||||
|
And I should see 'Article (foo): Blog2 Newer Article'
|
||||||
|
And I should see 'Article (foo): Blog2 Another Article'
|
||||||
|
And I should see 'Tag: bar (2)'
|
||||||
|
And I should see 'Article (bar): Blog1 Newer Article'
|
||||||
|
And I should see 'Article (bar): Blog2 Newer Article'
|
||||||
|
And I should see 'Tag: 120 (1)'
|
||||||
|
And I should see 'Article (120): Blog1 Another Article'
|
||||||
|
And I should see 'First Tag: 120'
|
||||||
|
And I should see 'Tag Count: 3'
|
||||||
|
|
||||||
|
Scenario: Collected resources update with file changes
|
||||||
|
Given a fixture app "collections-app"
|
||||||
|
And a file named "config.rb" with:
|
||||||
|
"""
|
||||||
|
collection :articles, resources.select { |r|
|
||||||
|
matcher = ::Middleman::Util::UriTemplates.uri_template('blog2/{year}-{month}-{day}-{title}.html')
|
||||||
|
::Middleman::Util::UriTemplates.extract_params(matcher, ::Middleman::Util.normalize_path(r.url))
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
And a file named "source/index.html.erb" with:
|
||||||
|
"""
|
||||||
|
<% collection(:articles).each do |article| %>
|
||||||
|
Article: <%= article.data.title || article.source_file[:relative_path] %>
|
||||||
|
<% end %>
|
||||||
|
"""
|
||||||
|
Given the Server is running at "collections-app"
|
||||||
|
When I go to "index.html"
|
||||||
|
Then I should not see "Article: index.html.erb"
|
||||||
|
Then I should see 'Article: Blog2 Newer Article'
|
||||||
|
And I should see 'Article: Blog2 Another Article'
|
||||||
|
|
||||||
|
And the file "source/blog2/2011-01-02-another-article.html.markdown" has the contents
|
||||||
|
"""
|
||||||
|
---
|
||||||
|
title: "Blog3 Another Article"
|
||||||
|
date: 2011-01-02
|
||||||
|
tags:
|
||||||
|
- foo
|
||||||
|
---
|
||||||
|
|
||||||
|
Another Article Content
|
||||||
|
|
||||||
|
"""
|
||||||
|
When I go to "index.html"
|
||||||
|
Then I should see "Article: Blog2 Newer Article"
|
||||||
|
And I should not see "Article: Blog2 Another Article"
|
||||||
|
And I should see 'Article: Blog3 Another Article'
|
||||||
|
|
||||||
|
And the file "source/blog2/2011-01-01-new-article.html.markdown" is removed
|
||||||
|
When I go to "index.html"
|
||||||
|
Then I should not see "Article: Blog2 Newer Article"
|
||||||
|
And I should see 'Article: Blog3 Another Article'
|
||||||
|
|
||||||
|
And the file "source/blog2/2014-01-02-yet-another-article.html.markdown" has the contents
|
||||||
|
"""
|
||||||
|
---
|
||||||
|
title: "Blog2 Yet Another Article"
|
||||||
|
date: 2011-01-02
|
||||||
|
tags:
|
||||||
|
- foo
|
||||||
|
---
|
||||||
|
|
||||||
|
Yet Another Article Content
|
||||||
|
"""
|
||||||
|
When I go to "index.html"
|
||||||
|
And I should see 'Article: Blog3 Another Article'
|
||||||
|
And I should see 'Article: Blog2 Yet Another Article'
|
|
@ -1,8 +1,9 @@
|
||||||
Feature: Console
|
Feature: Console
|
||||||
|
|
||||||
Scenario: Enter and exit the console
|
Scenario: Enter and exit the console
|
||||||
Given I run `middleman console` interactively
|
Given a fixture app "large-build-app"
|
||||||
When I type "puts 'Hello from the console.'"
|
When I run `middleman console` interactively
|
||||||
|
And I type "puts 'Hello from the console.'"
|
||||||
And I type "exit"
|
And I type "exit"
|
||||||
Then it should pass with:
|
Then it should pass with:
|
||||||
"""
|
"""
|
||||||
|
|
204
middleman-core/features/paginate.feature
Normal file
204
middleman-core/features/paginate.feature
Normal file
|
@ -0,0 +1,204 @@
|
||||||
|
Feature: Pagination
|
||||||
|
Scenario: Basic configuration
|
||||||
|
Given a fixture app "paginate-app"
|
||||||
|
And a file named "config.rb" with:
|
||||||
|
"""
|
||||||
|
articles = resources.select { |r|
|
||||||
|
matcher = ::Middleman::Util::UriTemplates.uri_template('blog/2011-{remaining}')
|
||||||
|
::Middleman::Util::UriTemplates.extract_params(matcher, ::Middleman::Util.normalize_path(r.url))
|
||||||
|
}
|
||||||
|
|
||||||
|
articles.sort { |a, b|
|
||||||
|
b.data.date <=> a.data.date
|
||||||
|
}.per_page(5) do |items, num, meta, is_last|
|
||||||
|
page_path = num == 1 ? '/2011/index.html' : "/2011/page/#{num}.html"
|
||||||
|
|
||||||
|
prev_page = case num
|
||||||
|
when 1
|
||||||
|
nil
|
||||||
|
when 2
|
||||||
|
'/2011/index.html'
|
||||||
|
when 3
|
||||||
|
"/2011/page/#{num-1}.html"
|
||||||
|
end
|
||||||
|
|
||||||
|
next_page = is_last ? nil : "/2011/page/#{num+1}.html"
|
||||||
|
|
||||||
|
proxy page_path, "/archive/2011/index.html", locals: {
|
||||||
|
items: items,
|
||||||
|
pagination: meta,
|
||||||
|
prev_page: prev_page,
|
||||||
|
next_page: next_page
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_tags(resource)
|
||||||
|
if resource.data.tags.is_a? String
|
||||||
|
resource.data.tags.split(',').map(&:strip)
|
||||||
|
else
|
||||||
|
resource.data.tags
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def group_lookup(resource, sum)
|
||||||
|
results = Array(get_tags(resource)).map(&:to_s).map(&:to_sym)
|
||||||
|
|
||||||
|
results.each do |k|
|
||||||
|
sum[k] ||= []
|
||||||
|
sum[k] << resource
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
tags = articles
|
||||||
|
.select { |resource| resource.data.tags }
|
||||||
|
.each_with_object({}, &method(:group_lookup))
|
||||||
|
|
||||||
|
tags.each do |k, articles_in_tag|
|
||||||
|
articles_in_tag.sort { |a, b|
|
||||||
|
b.data.date <=> a.data.date
|
||||||
|
}.per_page(2).each do |items, num, meta, is_last|
|
||||||
|
page_path = num == 1 ? "/tags/#{k}.html" : "/tags/#{k}/page/#{num}.html"
|
||||||
|
|
||||||
|
prev_page = case num
|
||||||
|
when 1
|
||||||
|
nil
|
||||||
|
when 2
|
||||||
|
"/tags/#{k}.html"
|
||||||
|
when 3
|
||||||
|
"/tags/#{k}/page/#{num-1}.html"
|
||||||
|
end
|
||||||
|
|
||||||
|
next_page = is_last ? nil : "/tags/#{k}/page/#{num+1}.html"
|
||||||
|
|
||||||
|
proxy page_path, "/archive/2011/index.html", locals: {
|
||||||
|
items: items,
|
||||||
|
pagination: meta,
|
||||||
|
prev_page: prev_page,
|
||||||
|
next_page: next_page
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
"""
|
||||||
|
And the Server is running
|
||||||
|
When I go to "/2011/index.html"
|
||||||
|
Then I should see "Paginate: true"
|
||||||
|
Then I should see "Article Count: 5"
|
||||||
|
Then I should see "Page Num: 1"
|
||||||
|
Then I should see "Num Pages: 2"
|
||||||
|
Then I should see "Per Page: 5"
|
||||||
|
Then I should see "Page Start: 1"
|
||||||
|
Then I should see "Page End: 5"
|
||||||
|
Then I should see "Next Page: '/2011/page/2.html'"
|
||||||
|
Then I should see "Prev Page: ''"
|
||||||
|
Then I should not see "/blog/2011-01-01-test-article.html"
|
||||||
|
Then I should not see "/blog/2011-01-02-test-article.html"
|
||||||
|
Then I should see "/blog/2011-01-03-test-article.html"
|
||||||
|
Then I should see "/blog/2011-01-04-test-article.html"
|
||||||
|
Then I should see "/blog/2011-01-05-test-article.html"
|
||||||
|
Then I should see "/blog/2011-02-01-test-article.html"
|
||||||
|
Then I should see "/blog/2011-02-02-test-article.html"
|
||||||
|
|
||||||
|
When I go to "/2011/page/2.html"
|
||||||
|
Then I should see "Article Count: 2"
|
||||||
|
Then I should see "Page Num: 2"
|
||||||
|
Then I should see "Page Start: 6"
|
||||||
|
Then I should see "Page End: 7"
|
||||||
|
Then I should see "Next Page: ''"
|
||||||
|
Then I should see "Prev Page: '/2011/'"
|
||||||
|
Then I should see "/2011-01-01-test-article.html"
|
||||||
|
Then I should see "/2011-01-02-test-article.html"
|
||||||
|
Then I should not see "/2011-01-03-test-article.html"
|
||||||
|
Then I should not see "/2011-01-04-test-article.html"
|
||||||
|
Then I should not see "/2011-01-05-test-article.html"
|
||||||
|
Then I should not see "/2011-02-01-test-article.html"
|
||||||
|
Then I should not see "/2011-02-02-test-article.html"
|
||||||
|
|
||||||
|
When I go to "/tags/bar.html"
|
||||||
|
Then I should see "Paginate: true"
|
||||||
|
Then I should see "Article Count: 2"
|
||||||
|
Then I should see "Page Num: 1"
|
||||||
|
Then I should see "Num Pages: 3"
|
||||||
|
Then I should see "Per Page: 2"
|
||||||
|
Then I should see "Page Start: 1"
|
||||||
|
Then I should see "Page End: 2"
|
||||||
|
Then I should see "Next Page: '/tags/bar/page/2.html'"
|
||||||
|
Then I should see "Prev Page: ''"
|
||||||
|
Then I should see "/2011-02-02-test-article.html"
|
||||||
|
Then I should see "/2011-02-01-test-article.html"
|
||||||
|
Then I should not see "/2011-02-05-test-article.html"
|
||||||
|
Then I should not see "/2011-01-04-test-article.html"
|
||||||
|
Then I should not see "/2011-01-03-test-article.html"
|
||||||
|
|
||||||
|
Scenario: Custom pager method
|
||||||
|
Given a fixture app "paginate-app"
|
||||||
|
And a file named "config.rb" with:
|
||||||
|
"""
|
||||||
|
def items_per_page(all_items)
|
||||||
|
[
|
||||||
|
all_items.shift(2),
|
||||||
|
all_items
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
articles = resources.select { |r|
|
||||||
|
matcher = ::Middleman::Util::UriTemplates.uri_template('blog/2011-{remaining}')
|
||||||
|
::Middleman::Util::UriTemplates.extract_params(matcher, ::Middleman::Util.normalize_path(r.url))
|
||||||
|
}
|
||||||
|
|
||||||
|
articles.sort { |a, b|
|
||||||
|
b.data.date <=> a.data.date
|
||||||
|
}.per_page(method(:items_per_page).to_proc).each do |items, num, meta, is_last|
|
||||||
|
page_path = num == 1 ? '/2011/index.html' : "/2011/page/#{num}.html"
|
||||||
|
|
||||||
|
prev_page = case num
|
||||||
|
when 1
|
||||||
|
nil
|
||||||
|
when 2
|
||||||
|
'/2011/index.html'
|
||||||
|
when 3
|
||||||
|
"/2011/page/#{num-1}.html"
|
||||||
|
end
|
||||||
|
|
||||||
|
next_page = is_last ? nil : "/2011/page/#{num+1}.html"
|
||||||
|
|
||||||
|
proxy page_path, "/archive/2011/index.html", locals: {
|
||||||
|
items: items,
|
||||||
|
pagination: meta,
|
||||||
|
prev_page: prev_page,
|
||||||
|
next_page: next_page
|
||||||
|
}
|
||||||
|
end
|
||||||
|
"""
|
||||||
|
And the Server is running
|
||||||
|
When I go to "/2011/index.html"
|
||||||
|
Then I should see "Paginate: true"
|
||||||
|
Then I should see "Article Count: 2"
|
||||||
|
Then I should see "Page Num: 1"
|
||||||
|
Then I should see "Num Pages: 2"
|
||||||
|
Then I should see "Per Page: 2"
|
||||||
|
Then I should see "Page Start: 1"
|
||||||
|
Then I should see "Page End: 2"
|
||||||
|
Then I should see "Next Page: '/2011/page/2.html'"
|
||||||
|
Then I should see "Prev Page: ''"
|
||||||
|
Then I should not see "/blog/2011-01-01-test-article.html"
|
||||||
|
Then I should not see "/blog/2011-01-02-test-article.html"
|
||||||
|
Then I should not see "/blog/2011-01-03-test-article.html"
|
||||||
|
Then I should not see "/blog/2011-01-04-test-article.html"
|
||||||
|
Then I should not see "/blog/2011-01-05-test-article.html"
|
||||||
|
Then I should see "/blog/2011-02-01-test-article.html"
|
||||||
|
Then I should see "/blog/2011-02-02-test-article.html"
|
||||||
|
|
||||||
|
When I go to "/2011/page/2.html"
|
||||||
|
Then I should see "Article Count: 5"
|
||||||
|
Then I should see "Page Num: 2"
|
||||||
|
Then I should see "Page Start: 3"
|
||||||
|
Then I should see "Page End: 7"
|
||||||
|
Then I should see "Next Page: ''"
|
||||||
|
Then I should see "Prev Page: '/2011/'"
|
||||||
|
Then I should see "/2011-01-01-test-article.html"
|
||||||
|
Then I should see "/2011-01-02-test-article.html"
|
||||||
|
Then I should see "/2011-01-03-test-article.html"
|
||||||
|
Then I should see "/2011-01-04-test-article.html"
|
||||||
|
Then I should see "/2011-01-05-test-article.html"
|
||||||
|
Then I should not see "/2011-02-01-test-article.html"
|
||||||
|
Then I should not see "/2011-02-02-test-article.html"
|
16
middleman-core/fixtures/collections-app/config.rb
Normal file
16
middleman-core/fixtures/collections-app/config.rb
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
collection :articles,
|
||||||
|
where: proc { |resource|
|
||||||
|
uri_match resource.url, 'blog/{year}-{month}-{day}-{title}.html'
|
||||||
|
}
|
||||||
|
|
||||||
|
collection :tags,
|
||||||
|
where: proc { |resource|
|
||||||
|
resource.data.tags
|
||||||
|
},
|
||||||
|
group_by: proc { |resource|
|
||||||
|
if resource.data.tags.is_a? String
|
||||||
|
resource.data.tags.split(',').map(&:strip)
|
||||||
|
else
|
||||||
|
resource.data.tags
|
||||||
|
end
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
---
|
||||||
|
title: "Blog1 Newer Article"
|
||||||
|
date: 2011-01-01
|
||||||
|
tags: foo, bar
|
||||||
|
---
|
||||||
|
|
||||||
|
Newer Article Content
|
|
@ -0,0 +1,9 @@
|
||||||
|
---
|
||||||
|
title: "Blog1 Another Article"
|
||||||
|
date: 2011-01-02
|
||||||
|
tags:
|
||||||
|
- foo
|
||||||
|
- 120
|
||||||
|
---
|
||||||
|
|
||||||
|
Another Article Content
|
|
@ -0,0 +1,7 @@
|
||||||
|
---
|
||||||
|
title: "Blog2 Newer Article"
|
||||||
|
date: 2011-01-01
|
||||||
|
tags: foo, bar
|
||||||
|
---
|
||||||
|
|
||||||
|
Newer Article Content
|
|
@ -0,0 +1,8 @@
|
||||||
|
---
|
||||||
|
title: "Blog2 Another Article"
|
||||||
|
date: 2011-01-02
|
||||||
|
tags:
|
||||||
|
- foo
|
||||||
|
---
|
||||||
|
|
||||||
|
Another Article Content
|
|
@ -0,0 +1,26 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<!-- ARTICLES -->
|
||||||
|
<% collected.articles.each do |article| %>
|
||||||
|
<li>
|
||||||
|
<a href="<%= article.url %>">Article: <%= article.data.title %></a>
|
||||||
|
<time><%= article.data.date.strftime('%b %e') %></time>
|
||||||
|
</li>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<!-- TAGS -->
|
||||||
|
<% collected[:tags].each do |k, items| %>
|
||||||
|
<li>
|
||||||
|
<%= k %>
|
||||||
|
<% items.each do |article| %>
|
||||||
|
<%= article.data.title %>
|
||||||
|
<% end %>
|
||||||
|
</li>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
0
middleman-core/fixtures/paginate-app/config.rb
Normal file
0
middleman-core/fixtures/paginate-app/config.rb
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
Year: '<%#= year %>'
|
||||||
|
|
||||||
|
Paginate: <%= !!pagination %>
|
||||||
|
Article Count: <%= items.length %>
|
||||||
|
<% if pagination %>
|
||||||
|
Page Num: <%= pagination.page_number %>
|
||||||
|
Num Pages: <%= pagination.num_pages %>
|
||||||
|
Per Page: <%= pagination.per_page %>
|
||||||
|
Page Start: <%= pagination.page_start %>
|
||||||
|
Page End: <%= pagination.page_end %>
|
||||||
|
Next Page: '<%= sitemap.find_resource_by_destination_path(next_page).url if next_page %>'
|
||||||
|
Prev Page: '<%= sitemap.find_resource_by_destination_path(prev_page).url if prev_page %>'
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<% items.each do |article| %>
|
||||||
|
<article>
|
||||||
|
<%= article.data.title %>
|
||||||
|
<%= article.url %>
|
||||||
|
</article>
|
||||||
|
<% end %>
|
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
title: "Test Article"
|
||||||
|
date: 2011-01-01
|
||||||
|
tags: foo
|
||||||
|
---
|
||||||
|
Test Article Content
|
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
title: "Test Article"
|
||||||
|
date: 2011-01-02
|
||||||
|
tags: foo
|
||||||
|
---
|
||||||
|
Test Article Content
|
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
title: "Test Article"
|
||||||
|
date: 2011-01-03
|
||||||
|
tags: bar
|
||||||
|
---
|
||||||
|
Test Article Content
|
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
title: "Test Article"
|
||||||
|
date: 2011-01-04
|
||||||
|
tags: bar
|
||||||
|
---
|
||||||
|
Test Article Content
|
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
title: "Test Article"
|
||||||
|
date: 2011-01-05
|
||||||
|
tags: bar
|
||||||
|
---
|
||||||
|
Test Article Content
|
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
title: "Test Article"
|
||||||
|
date: 2011-02-01
|
||||||
|
tags: bar
|
||||||
|
---
|
||||||
|
Test Article Content
|
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
title: "Test Article"
|
||||||
|
date: 2011-02-02
|
||||||
|
tags: bar
|
||||||
|
---
|
||||||
|
Test Article Content
|
15
middleman-core/fixtures/paginate-app/source/index.html.erb
Normal file
15
middleman-core/fixtures/paginate-app/source/index.html.erb
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
Paginate: <%= paginate %>
|
||||||
|
Article Count: <%= page_articles.size %>
|
||||||
|
<% if paginate %>
|
||||||
|
Page Num: <%= page_number %>
|
||||||
|
Num Pages: <%= num_pages %>
|
||||||
|
Per Page: <%= per_page %>
|
||||||
|
Page Start: <%= page_start %>
|
||||||
|
Page End: <%= page_end %>
|
||||||
|
Next Page: '<%= next_page.url if next_page %>'
|
||||||
|
Prev Page: '<%= prev_page.url if prev_page %>'
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<% page_articles.each do |article| %>
|
||||||
|
<li><a href="<%= article.url %>"><%= article.title %></a> <time><%= article.date.strftime('%b %e') %></time></li>
|
||||||
|
<% end %>
|
23
middleman-core/fixtures/paginate-app/source/tag.html.erb
Normal file
23
middleman-core/fixtures/paginate-app/source/tag.html.erb
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
---
|
||||||
|
pageable: true
|
||||||
|
per_page: 2
|
||||||
|
---
|
||||||
|
Tag: <%= tagname %>
|
||||||
|
|
||||||
|
Paginate: <%= paginate %>
|
||||||
|
Article Count: <%= page_articles.size %>
|
||||||
|
<% if paginate %>
|
||||||
|
Page Num: <%= page_number %>
|
||||||
|
Num Pages: <%= num_pages %>
|
||||||
|
Per Page: <%= per_page %>
|
||||||
|
Page Start: <%= page_start %>
|
||||||
|
Page End: <%= page_end %>
|
||||||
|
Next Page: '<%= next_page.url if next_page %>'
|
||||||
|
Prev Page: '<%= prev_page.url if prev_page %>'
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<% if page_articles %>
|
||||||
|
<% page_articles.each do |article| %>
|
||||||
|
<li><a href="<%= article.url %>"><%= article.title %></a> <time><%= article.date.strftime('%b %e') %></time></li>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
|
@ -59,6 +59,21 @@ if ENV['TEST'] || ENV['CONTRACTS'] == 'true'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# class MethodDefined
|
||||||
|
# def self.[](val)
|
||||||
|
# @lookup ||= {}
|
||||||
|
# @lookup[val] ||= new(val)
|
||||||
|
# end
|
||||||
|
|
||||||
|
# def initialize(val)
|
||||||
|
# @val = val
|
||||||
|
# end
|
||||||
|
|
||||||
|
# def valid?(val)
|
||||||
|
# val.method_defined? @val
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
|
||||||
ResourceList = Contracts::ArrayOf[IsA['Middleman::Sitemap::Resource']]
|
ResourceList = Contracts::ArrayOf[IsA['Middleman::Sitemap::Resource']]
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
@ -141,6 +156,9 @@ else
|
||||||
|
|
||||||
class Frozen < Callable
|
class Frozen < Callable
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# class MethodDefined < Callable
|
||||||
|
# end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,11 @@ Middleman::Extensions.register :routing, auto_activate: :before_configuration do
|
||||||
Middleman::CoreExtensions::Routing
|
Middleman::CoreExtensions::Routing
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Middleman::Extensions.register :collections, auto_activate: :before_configuration do
|
||||||
|
require 'middleman-core/core_extensions/collections'
|
||||||
|
Middleman::CoreExtensions::Collections::CollectionsExtension
|
||||||
|
end
|
||||||
|
|
||||||
###
|
###
|
||||||
# Setup Optional Extensions
|
# Setup Optional Extensions
|
||||||
###
|
###
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
require 'middleman-core/core_extensions/collections/pagination'
|
||||||
|
require 'middleman-core/core_extensions/collections/step_context'
|
||||||
|
require 'middleman-core/core_extensions/collections/lazy_root'
|
||||||
|
require 'middleman-core/core_extensions/collections/lazy_step'
|
||||||
|
|
||||||
|
# Super "class-y" injection of array helpers
|
||||||
|
class Array
|
||||||
|
include Middleman::Pagination::ArrayHelpers
|
||||||
|
end
|
||||||
|
|
||||||
|
module Middleman
|
||||||
|
module CoreExtensions
|
||||||
|
module Collections
|
||||||
|
class CollectionsExtension < Extension
|
||||||
|
# This should run after most other sitemap manipulators so that it
|
||||||
|
# gets a chance to modify any new resources that get added.
|
||||||
|
self.resource_list_manipulator_priority = 110
|
||||||
|
|
||||||
|
attr_accessor :root_collector, :leaves
|
||||||
|
|
||||||
|
def initialize(app, options_hash={}, &block)
|
||||||
|
super
|
||||||
|
|
||||||
|
@leaves = Set.new
|
||||||
|
@collectors_by_name = {}
|
||||||
|
@values_by_name = {}
|
||||||
|
|
||||||
|
@root_collector = LazyCollectorRoot.new(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
Contract None => Any
|
||||||
|
def before_configuration
|
||||||
|
@leaves.clear
|
||||||
|
|
||||||
|
app.add_to_config_context :resources, &method(:root_collector)
|
||||||
|
app.add_to_config_context :collection, &method(:register_collector)
|
||||||
|
end
|
||||||
|
|
||||||
|
Contract Symbol, LazyCollectorStep => Any
|
||||||
|
def register_collector(label, endpoint)
|
||||||
|
@collectors_by_name[label] = endpoint
|
||||||
|
end
|
||||||
|
|
||||||
|
Contract Symbol => Any
|
||||||
|
def collector_value(label)
|
||||||
|
@values_by_name[label]
|
||||||
|
end
|
||||||
|
|
||||||
|
Contract ResourceList => ResourceList
|
||||||
|
def manipulate_resource_list(resources)
|
||||||
|
@root_collector.realize!(resources)
|
||||||
|
|
||||||
|
ctx = StepContext.new
|
||||||
|
leaves = @leaves.dup
|
||||||
|
|
||||||
|
@collectors_by_name.each do |k, v|
|
||||||
|
@values_by_name[k] = v.value(ctx)
|
||||||
|
leaves.delete v
|
||||||
|
end
|
||||||
|
|
||||||
|
# Execute code paths
|
||||||
|
leaves.each do |v|
|
||||||
|
v.value(ctx)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Inject descriptors
|
||||||
|
resources + ctx.descriptors.map { |d| d.to_resource(app) }
|
||||||
|
end
|
||||||
|
|
||||||
|
helpers do
|
||||||
|
def collection(label)
|
||||||
|
extensions[:collections].collector_value(label)
|
||||||
|
end
|
||||||
|
|
||||||
|
def pagination
|
||||||
|
current_resource.data.pagination
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,30 @@
|
||||||
|
require 'middleman-core/core_extensions/collections/lazy_step'
|
||||||
|
|
||||||
|
module Middleman
|
||||||
|
module CoreExtensions
|
||||||
|
module Collections
|
||||||
|
class LazyCollectorRoot < BasicObject
|
||||||
|
def initialize(parent)
|
||||||
|
@data = nil
|
||||||
|
@parent = parent
|
||||||
|
end
|
||||||
|
|
||||||
|
def realize!(data)
|
||||||
|
@data = data
|
||||||
|
end
|
||||||
|
|
||||||
|
def value(_ctx=nil)
|
||||||
|
@data
|
||||||
|
end
|
||||||
|
|
||||||
|
def leaves
|
||||||
|
@parent.leaves
|
||||||
|
end
|
||||||
|
|
||||||
|
def method_missing(name, *args, &block)
|
||||||
|
LazyCollectorStep.new(name, args, block, self)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,48 @@
|
||||||
|
module Middleman
|
||||||
|
module CoreExtensions
|
||||||
|
module Collections
|
||||||
|
class LazyCollectorStep < BasicObject
|
||||||
|
DELEGATE = [:hash, :eql?]
|
||||||
|
|
||||||
|
def initialize(name, args, block, parent=nil)
|
||||||
|
@name = name
|
||||||
|
@args = args
|
||||||
|
@block = block
|
||||||
|
|
||||||
|
@parent = parent
|
||||||
|
@result = nil
|
||||||
|
|
||||||
|
leaves << self
|
||||||
|
end
|
||||||
|
|
||||||
|
def leaves
|
||||||
|
@parent.leaves
|
||||||
|
end
|
||||||
|
|
||||||
|
def value(ctx=nil)
|
||||||
|
data = @parent.value(ctx)
|
||||||
|
|
||||||
|
original_block = @block
|
||||||
|
|
||||||
|
b = if ctx
|
||||||
|
::Proc.new do |*args|
|
||||||
|
ctx.instance_exec(*args, &original_block)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
original_block
|
||||||
|
end if original_block
|
||||||
|
|
||||||
|
data.send(@name, *@args.deep_dup, &b)
|
||||||
|
end
|
||||||
|
|
||||||
|
def method_missing(name, *args, &block)
|
||||||
|
return ::Kernel.send(name, *args, &block) if DELEGATE.include? name
|
||||||
|
|
||||||
|
leaves.delete self
|
||||||
|
|
||||||
|
LazyCollectorStep.new(name, args, block, self)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,59 @@
|
||||||
|
require 'active_support/core_ext/object/deep_dup'
|
||||||
|
require 'middleman-core/util'
|
||||||
|
|
||||||
|
module Middleman
|
||||||
|
module Pagination
|
||||||
|
module ArrayHelpers
|
||||||
|
def per_page(per_page)
|
||||||
|
return enum_for(:per_page, per_page) unless block_given?
|
||||||
|
|
||||||
|
parts = if per_page.respond_to? :call
|
||||||
|
per_page.call(dup)
|
||||||
|
else
|
||||||
|
each_slice(per_page).reduce([]) do |sum, items|
|
||||||
|
sum << items
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
num_pages = parts.length
|
||||||
|
collection = self
|
||||||
|
|
||||||
|
current_start_i = 0
|
||||||
|
parts.each_with_index do |items, i|
|
||||||
|
num = i + 1
|
||||||
|
|
||||||
|
meta = ::Middleman::Pagination.page_locals(
|
||||||
|
num,
|
||||||
|
num_pages,
|
||||||
|
collection,
|
||||||
|
items,
|
||||||
|
current_start_i
|
||||||
|
)
|
||||||
|
|
||||||
|
yield items, num, meta, num >= num_pages
|
||||||
|
|
||||||
|
current_start_i += items.length
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.page_locals(page_num, num_pages, collection, items, page_start)
|
||||||
|
per_page = items.length
|
||||||
|
|
||||||
|
# Index into collection of the last item of this page
|
||||||
|
page_end = (page_start + per_page) - 1
|
||||||
|
|
||||||
|
::Middleman::Util.recursively_enhance(page_number: page_num,
|
||||||
|
num_pages: num_pages,
|
||||||
|
per_page: per_page,
|
||||||
|
|
||||||
|
# The range of item numbers on this page
|
||||||
|
# (1-based, for showing "Items X to Y of Z")
|
||||||
|
page_start: page_start + 1,
|
||||||
|
page_end: [page_end + 1, collection.length].min,
|
||||||
|
|
||||||
|
# Use "collection" in templates.
|
||||||
|
collection: collection)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,26 @@
|
||||||
|
module Middleman
|
||||||
|
module CoreExtensions
|
||||||
|
module Collections
|
||||||
|
class StepContext
|
||||||
|
def self.add_to_context(name, &func)
|
||||||
|
send(:define_method, :"_internal_#{name}", &func)
|
||||||
|
end
|
||||||
|
|
||||||
|
attr_reader :descriptors
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@descriptors = []
|
||||||
|
end
|
||||||
|
|
||||||
|
def method_missing(name, *args, &block)
|
||||||
|
internal = :"_internal_#{name}"
|
||||||
|
if respond_to?(internal)
|
||||||
|
@descriptors << send(internal, *args, &block)
|
||||||
|
else
|
||||||
|
super
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -12,7 +12,7 @@ module Middleman::CoreExtensions
|
||||||
end
|
end
|
||||||
|
|
||||||
def after_configuration
|
def after_configuration
|
||||||
app.use ::Rack::ShowExceptions if app.config[:show_exceptions]
|
app.use ::Rack::ShowExceptions if !app.build? && app.config[:show_exceptions]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
require 'middleman-core/sitemap/resource'
|
require 'middleman-core/sitemap/resource'
|
||||||
|
require 'middleman-core/core_extensions/collections/step_context'
|
||||||
|
|
||||||
module Middleman
|
module Middleman
|
||||||
module Sitemap
|
module Sitemap
|
||||||
|
@ -13,6 +14,13 @@ module Middleman
|
||||||
@app.define_singleton_method(:proxy, &method(:create_proxy))
|
@app.define_singleton_method(:proxy, &method(:create_proxy))
|
||||||
|
|
||||||
@proxy_configs = Set.new
|
@proxy_configs = Set.new
|
||||||
|
@post_config = false
|
||||||
|
end
|
||||||
|
|
||||||
|
def after_configuration
|
||||||
|
@post_config = true
|
||||||
|
|
||||||
|
::Middleman::CoreExtensions::Collections::StepContext.add_to_context(:proxy, &method(:create_anonymous_proxy))
|
||||||
end
|
end
|
||||||
|
|
||||||
# Setup a proxy from a path to a target
|
# Setup a proxy from a path to a target
|
||||||
|
@ -27,71 +35,49 @@ module Middleman
|
||||||
Contract String, String, Maybe[Hash] => Any
|
Contract String, String, Maybe[Hash] => Any
|
||||||
def create_proxy(path, target, opts={})
|
def create_proxy(path, target, opts={})
|
||||||
options = opts.dup
|
options = opts.dup
|
||||||
|
|
||||||
@app.ignore(target) if options.delete(:ignore)
|
@app.ignore(target) if options.delete(:ignore)
|
||||||
|
|
||||||
metadata = {
|
@proxy_configs << create_anonymous_proxy(path, target, options)
|
||||||
options: options,
|
|
||||||
locals: options.delete(:locals) || {},
|
|
||||||
page: options.delete(:data) || {}
|
|
||||||
}
|
|
||||||
|
|
||||||
@proxy_configs << ProxyConfiguration.new(path: path, target: target, metadata: metadata)
|
|
||||||
|
|
||||||
@app.sitemap.rebuild_resource_list!(:added_proxy)
|
@app.sitemap.rebuild_resource_list!(:added_proxy)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Setup a proxy from a path to a target
|
||||||
|
# @param [String] path The new, proxied path to create
|
||||||
|
# @param [String] target The existing path that should be proxied to. This must be a real resource, not another proxy.
|
||||||
|
# @option opts [Boolean] ignore Ignore the target from the sitemap (so only the new, proxy resource ends up in the output)
|
||||||
|
# @option opts [Symbol, Boolean, String] layout The layout name to use (e.g. `:article`) or `false` to disable layout.
|
||||||
|
# @option opts [Boolean] directory_indexes Whether or not the `:directory_indexes` extension applies to these paths.
|
||||||
|
# @option opts [Hash] locals Local variables for the template. These will be available when the template renders.
|
||||||
|
# @option opts [Hash] data Extra metadata to add to the page. This is the same as frontmatter, though frontmatter will take precedence over metadata defined here. Available via {Resource#data}.
|
||||||
|
# @return [void]
|
||||||
|
def create_anonymous_proxy(path, target, options={})
|
||||||
|
ProxyDescriptor.new(
|
||||||
|
::Middleman::Util.normalize_path(path),
|
||||||
|
::Middleman::Util.normalize_path(target),
|
||||||
|
options
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
# Update the main sitemap resource list
|
# Update the main sitemap resource list
|
||||||
# @return Array<Middleman::Sitemap::Resource>
|
# @return Array<Middleman::Sitemap::Resource>
|
||||||
Contract ResourceList => ResourceList
|
Contract ResourceList => ResourceList
|
||||||
def manipulate_resource_list(resources)
|
def manipulate_resource_list(resources)
|
||||||
resources + @proxy_configs.map do |config|
|
resources + @proxy_configs.map { |c| c.to_resource(@app) }
|
||||||
p = ProxyResource.new(
|
end
|
||||||
@app.sitemap,
|
end
|
||||||
config.path,
|
|
||||||
config.target
|
ProxyDescriptor = Struct.new(:path, :target, :metadata) do
|
||||||
|
def to_resource(app)
|
||||||
|
ProxyResource.new(app.sitemap, path, target).tap do |p|
|
||||||
|
md = metadata.dup
|
||||||
|
p.add_metadata(
|
||||||
|
locals: md.delete(:locals) || {},
|
||||||
|
page: md.delete(:data) || {},
|
||||||
|
options: md
|
||||||
)
|
)
|
||||||
|
|
||||||
p.add_metadata(config.metadata)
|
|
||||||
p
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Configuration for a proxy instance
|
|
||||||
class ProxyConfiguration
|
|
||||||
# The path that this proxy will appear at in the sitemap
|
|
||||||
attr_reader :path
|
|
||||||
def path=(p)
|
|
||||||
@path = ::Middleman::Util.normalize_path(p)
|
|
||||||
end
|
|
||||||
|
|
||||||
# The existing sitemap path that this will proxy to
|
|
||||||
attr_reader :target
|
|
||||||
def target=(t)
|
|
||||||
@target = ::Middleman::Util.normalize_path(t)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Additional metadata like locals to apply to the proxy
|
|
||||||
attr_accessor :metadata
|
|
||||||
|
|
||||||
# Create a new proxy configuration from hash options
|
|
||||||
def initialize(options={})
|
|
||||||
options.each do |key, value|
|
|
||||||
send "#{key}=", value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Two configurations are equal if they reference the same path
|
|
||||||
def eql?(other)
|
|
||||||
other.path == path
|
|
||||||
end
|
|
||||||
|
|
||||||
# Two configurations are equal if they reference the same path
|
|
||||||
def hash
|
|
||||||
path.hash
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
class ProxyResource < ::Middleman::Sitemap::Resource
|
class ProxyResource < ::Middleman::Sitemap::Resource
|
||||||
|
|
|
@ -165,7 +165,11 @@ module Middleman
|
||||||
# Ignore based on the source path (without template extensions)
|
# Ignore based on the source path (without template extensions)
|
||||||
return true if @app.sitemap.ignored?(path)
|
return true if @app.sitemap.ignored?(path)
|
||||||
# This allows files to be ignored by their source file name (with template extensions)
|
# This allows files to be ignored by their source file name (with template extensions)
|
||||||
!self.is_a?(ProxyResource) && @app.sitemap.ignored?(source_file[:relative_path].to_s)
|
if !self.is_a?(ProxyResource) && source_file && @app.sitemap.ignored?(source_file[:relative_path].to_s)
|
||||||
|
true
|
||||||
|
else
|
||||||
|
false
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# The preferred MIME content type for this resource based on extension or metadata
|
# The preferred MIME content type for this resource based on extension or metadata
|
||||||
|
@ -174,6 +178,31 @@ module Middleman
|
||||||
def content_type
|
def content_type
|
||||||
options[:content_type] || ::Rack::Mime.mime_type(ext, nil)
|
options[:content_type] || ::Rack::Mime.mime_type(ext, nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
"#<Middleman::Sitemap::Resource path=#{@path}>"
|
||||||
|
end
|
||||||
|
alias_method :inspect, :to_s # Ruby 2.0 calls inspect for NoMethodError instead of to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
class StringResource < Resource
|
||||||
|
def initialize(store, path, contents=nil, &block)
|
||||||
|
@request_path = path
|
||||||
|
@contents = block_given? ? block : contents
|
||||||
|
super(store, path)
|
||||||
|
end
|
||||||
|
|
||||||
|
def template?
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def render(*)
|
||||||
|
@contents.respond_to?(:call) ? @contents.call : @contents
|
||||||
|
end
|
||||||
|
|
||||||
|
def binary?
|
||||||
|
false
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -49,11 +49,15 @@ module Middleman
|
||||||
# @return [Middleman::Application]
|
# @return [Middleman::Application]
|
||||||
attr_reader :app
|
attr_reader :app
|
||||||
|
|
||||||
|
attr_reader :update_count
|
||||||
|
|
||||||
# Initialize with parent app
|
# Initialize with parent app
|
||||||
# @param [Middleman::Application] app
|
# @param [Middleman::Application] app
|
||||||
def initialize(app)
|
def initialize(app)
|
||||||
@app = app
|
@app = app
|
||||||
@resources = []
|
@resources = []
|
||||||
|
@update_count = 0
|
||||||
|
|
||||||
# TODO: Should this be a set or hash?
|
# TODO: Should this be a set or hash?
|
||||||
@resource_list_manipulators = []
|
@resource_list_manipulators = []
|
||||||
@needs_sitemap_rebuild = true
|
@needs_sitemap_rebuild = true
|
||||||
|
@ -187,6 +191,7 @@ module Middleman
|
||||||
end
|
end
|
||||||
|
|
||||||
invalidate_resources_not_ignored_cache!
|
invalidate_resources_not_ignored_cache!
|
||||||
|
@update_count += 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -172,7 +172,7 @@ module Middleman
|
||||||
.lazy
|
.lazy
|
||||||
.select { |d| d.type == type }
|
.select { |d| d.type == type }
|
||||||
.map { |d| d.find(path, glob) }
|
.map { |d| d.find(path, glob) }
|
||||||
.reject { |d| d.nil? }
|
.reject(&:nil?)
|
||||||
.first
|
.first
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -102,7 +102,7 @@ module Middleman
|
||||||
|
|
||||||
partial_file = locate_partial(name)
|
partial_file = locate_partial(name)
|
||||||
|
|
||||||
return "" unless partial_file
|
return '' unless partial_file
|
||||||
raise ::Middleman::TemplateRenderer::TemplateNotFound, "Could not locate partial: #{name}" unless partial_file
|
raise ::Middleman::TemplateRenderer::TemplateNotFound, "Could not locate partial: #{name}" unless partial_file
|
||||||
|
|
||||||
source_path = sitemap.file_to_path(partial_file)
|
source_path = sitemap.file_to_path(partial_file)
|
||||||
|
|
|
@ -14,6 +14,11 @@ require 'rack/mime'
|
||||||
# DbC
|
# DbC
|
||||||
require 'middleman-core/contracts'
|
require 'middleman-core/contracts'
|
||||||
|
|
||||||
|
# For URI templating
|
||||||
|
require 'addressable/template'
|
||||||
|
require 'active_support/inflector'
|
||||||
|
require 'active_support/inflector/transliterate'
|
||||||
|
|
||||||
module Middleman
|
module Middleman
|
||||||
module Util
|
module Util
|
||||||
include Contracts
|
include Contracts
|
||||||
|
@ -384,5 +389,93 @@ module Middleman
|
||||||
resource_url
|
resource_url
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Handy methods for dealing with URI templates. Mix into whatever class.
|
||||||
|
module UriTemplates
|
||||||
|
module_function
|
||||||
|
|
||||||
|
# Given a URI template string, make an Addressable::Template
|
||||||
|
# This supports the legacy middleman-blog/Sinatra style :colon
|
||||||
|
# URI templates as well as RFC6570 templates.
|
||||||
|
#
|
||||||
|
# @param [String] tmpl_src URI template source
|
||||||
|
# @return [Addressable::Template] a URI template
|
||||||
|
def uri_template(tmpl_src)
|
||||||
|
# Support the RFC6470 templates directly if people use them
|
||||||
|
if tmpl_src.include?(':')
|
||||||
|
tmpl_src = tmpl_src.gsub(/:([A-Za-z0-9]+)/, '{\1}')
|
||||||
|
end
|
||||||
|
|
||||||
|
Addressable::Template.new ::Middleman::Util.normalize_path(tmpl_src)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Apply a URI template with the given data, producing a normalized
|
||||||
|
# Middleman path.
|
||||||
|
#
|
||||||
|
# @param [Addressable::Template] template
|
||||||
|
# @param [Hash] data
|
||||||
|
# @return [String] normalized path
|
||||||
|
def apply_uri_template(template, data)
|
||||||
|
::Middleman::Util.normalize_path Addressable::URI.unencode(template.expand(data)).to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
# Use a template to extract parameters from a path, and validate some special (date)
|
||||||
|
# keys. Returns nil if the special keys don't match.
|
||||||
|
#
|
||||||
|
# @param [Addressable::Template] template
|
||||||
|
# @param [String] path
|
||||||
|
def extract_params(template, path)
|
||||||
|
template.extract(path, BlogTemplateProcessor)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Parameterize a string preserving any multibyte characters
|
||||||
|
def safe_parameterize(str)
|
||||||
|
sep = '-'
|
||||||
|
|
||||||
|
# Reimplementation of http://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-parameterize that preserves un-transliterate-able multibyte chars.
|
||||||
|
parameterized_string = ActiveSupport::Inflector.transliterate(str.to_s).downcase
|
||||||
|
parameterized_string.gsub!(/[^a-z0-9\-_\?]+/, sep)
|
||||||
|
|
||||||
|
parameterized_string.chars.to_a.each_with_index do |char, i|
|
||||||
|
next unless char == '?' && str[i].bytes.count != 1
|
||||||
|
parameterized_string[i] = str[i]
|
||||||
|
end
|
||||||
|
|
||||||
|
re_sep = Regexp.escape(sep)
|
||||||
|
# No more than one of the separator in a row.
|
||||||
|
parameterized_string.gsub!(/#{re_sep}{2,}/, sep)
|
||||||
|
# Remove leading/trailing separator.
|
||||||
|
parameterized_string.gsub!(/^#{re_sep}|#{re_sep}$/, '')
|
||||||
|
|
||||||
|
parameterized_string
|
||||||
|
end
|
||||||
|
|
||||||
|
# Convert a date into a hash of components to strings
|
||||||
|
# suitable for using in a URL template.
|
||||||
|
# @param [DateTime] date
|
||||||
|
# @return [Hash] parameters
|
||||||
|
def date_to_params(date)
|
||||||
|
{
|
||||||
|
year: date.year.to_s,
|
||||||
|
month: date.month.to_s.rjust(2, '0'),
|
||||||
|
day: date.day.to_s.rjust(2, '0')
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# A special template processor that validates date fields
|
||||||
|
# and has an extra-permissive default regex.
|
||||||
|
#
|
||||||
|
# See https://github.com/sporkmonger/addressable/blob/master/lib/addressable/template.rb#L279
|
||||||
|
class BlogTemplateProcessor
|
||||||
|
def self.match(name)
|
||||||
|
case name
|
||||||
|
when 'year' then '\d{4}'
|
||||||
|
when 'month' then '\d{2}'
|
||||||
|
when 'day' then '\d{2}'
|
||||||
|
else '.*?'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -29,6 +29,7 @@ Gem::Specification.new do |s|
|
||||||
# Helpers
|
# Helpers
|
||||||
s.add_dependency('activesupport', ['~> 4.1.0'])
|
s.add_dependency('activesupport', ['~> 4.1.0'])
|
||||||
s.add_dependency('padrino-helpers', ['~> 0.12.3'])
|
s.add_dependency('padrino-helpers', ['~> 0.12.3'])
|
||||||
|
s.add_dependency("addressable", ["~> 2.3.5"])
|
||||||
|
|
||||||
# Watcher
|
# Watcher
|
||||||
s.add_dependency('listen', ['>= 2.7.9', '< 3.0'])
|
s.add_dependency('listen', ['>= 2.7.9', '< 3.0'])
|
||||||
|
|
Loading…
Reference in a new issue