Skip to content

Commit 24d3148

Browse files
committed
Merge pull request #1 from jekyll/archives
Migrate from alfredxing/jekyll-archives
2 parents affa3f3 + 58ffaff commit 24d3148

17 files changed

+529
-2
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
*.gem
2+
Gemfile.lock
3+
test/destination
4+
.bundle

Gemfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
source 'https://rubygems.org'
2+
gemspec

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2014 Alfred Xing
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,27 @@
1-
# jekyll-archives
1+
# Jekyll Archives
22

3-
Archive pages for your Jekyll tags and categories.
3+
Automatically generate post archives by dates, tags, and categories.
4+
5+
[![Gem Version](https://badge.fury.io/rb/jekyll-archives.png)](http://badge.fury.io/rb/jekyll-archives)
6+
7+
## Usage
8+
9+
1. Add `gem 'jekyll-archives'` to your site's Gemfile
10+
2. Add the following to your site's `_config.yml`:
11+
12+
```yml
13+
gems:
14+
- jekyll-archives
15+
```
16+
17+
## Configuration
18+
`jekyll-archives` is configurable from your site's `_config.yml` file:
19+
20+
```yml
21+
jekyll-archives:
22+
layout: archive # The layout to use for archive pages.
23+
permalinks:
24+
year: '/archive/:name' # The permalink to use for year-based archives.
25+
tag: '/tag/:name' # The permalink to use for tag archives.
26+
category: '/category/:name' # The permalink to use for category archives.
27+
```

Rakefile

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# From jekyll/jekyll-mentions
2+
3+
require 'rubygems'
4+
require 'bundler'
5+
6+
begin
7+
Bundler.setup(:default, :development, :test)
8+
rescue Bundler::BundlerError => e
9+
$stderr.puts e.message
10+
$stderr.puts "Run `bundle install` to install missing gems"
11+
exit e.status_code
12+
end
13+
14+
require 'rake'
15+
require 'rake/testtask'
16+
17+
Rake::TestTask.new(:test) do |test|
18+
test.libs << 'lib' << 'test'
19+
test.pattern = 'test/**/test_*.rb'
20+
test.verbose = true
21+
end
22+
23+
task :default => 'test'

jekyll-archives.gemspec

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
Gem::Specification.new do |s|
2+
s.name = "jekyll-archives"
3+
s.summary = "Post archives for Jekyll."
4+
s.description = "Automatically generate post archives by dates, tags, and categories."
5+
s.version = "0.1.0"
6+
s.authors = ["Alfred Xing"]
7+
8+
s.homepage = "https://github.com/jekyll/jekyll-archives"
9+
s.licenses = ["MIT"]
10+
s.files = ["lib/jekyll-archives.rb", "lib/jekyll-archives/archive.rb"]
11+
12+
s.add_dependency "jekyll", '~> 2.0'
13+
14+
s.add_development_dependency 'rake'
15+
s.add_development_dependency 'rdoc'
16+
s.add_development_dependency 'shoulda'
17+
s.add_development_dependency 'minitest'
18+
end

lib/jekyll-archives.rb

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
require 'jekyll'
2+
3+
module Jekyll
4+
# Internal requires
5+
autoload :Archive, 'jekyll-archives/archive'
6+
7+
class Archives < Jekyll::Generator
8+
safe true
9+
10+
DEFAULTS = {
11+
'layout' => 'archive',
12+
'permalinks' => {
13+
'year' => '/:year/',
14+
'month' => '/:year/:month/',
15+
'day' => '/:year/:month/:day/',
16+
'tag' => '/tag/:name/',
17+
'category' => '/category/:name/'
18+
}
19+
}
20+
21+
def initialize(config = nil)
22+
if config['jekyll-archives'].nil?
23+
@config = DEFAULTS
24+
else
25+
@config = Utils.deep_merge_hashes(DEFAULTS, config['jekyll-archives'])
26+
end
27+
end
28+
29+
def generate(site)
30+
@site = site
31+
@posts = site.posts
32+
@archives = []
33+
34+
@site.config['jekyll-archives'] = @config
35+
36+
read
37+
render
38+
write
39+
40+
@site.keep_files ||= []
41+
@archives.each do |archive|
42+
@site.keep_files << archive.relative_path
43+
end
44+
@site.config["archives"] = @archives
45+
end
46+
47+
# Read archive data from posts
48+
def read
49+
tags.each do |name, posts|
50+
@archives << Archive.new(@site, name, "tag", posts)
51+
end
52+
categories.each do |name, posts|
53+
@archives << Archive.new(@site, name, "category", posts)
54+
end
55+
years.each do |year, posts|
56+
@archives << Archive.new(@site, { :year => year }, "year", posts)
57+
months(posts).each do |month, posts|
58+
@archives << Archive.new(@site, { :year => year, :month => month }, "month", posts)
59+
days(posts).each do |day, posts|
60+
@archives << Archive.new(@site, { :year => year, :month => month, :day => day }, "day", posts)
61+
end
62+
end
63+
end
64+
end
65+
66+
# Renders the archives into the layouts
67+
def render
68+
payload = @site.site_payload
69+
@archives.each do |archive|
70+
archive.render(@site.layouts, payload)
71+
end
72+
end
73+
74+
# Write archives to their destination
75+
def write
76+
@archives.each do |archive|
77+
archive.write(@site.dest)
78+
end
79+
end
80+
81+
# Construct a Hash of Posts indexed by the specified Post attribute.
82+
#
83+
# post_attr - The String name of the Post attribute.
84+
#
85+
# Examples
86+
#
87+
# post_attr_hash('categories')
88+
# # => { 'tech' => [<Post A>, <Post B>],
89+
# # 'ruby' => [<Post B>] }
90+
#
91+
# Returns the Hash: { attr => posts } where
92+
# attr - One of the values for the requested attribute.
93+
# posts - The Array of Posts with the given attr value.
94+
#
95+
# Taken from jekyll/jekyll (Copyright (c) 2014 Tom Preston-Werner under the MIT).
96+
def post_attr_hash(post_attr)
97+
# Build a hash map based on the specified post attribute ( post attr =>
98+
# array of posts ) then sort each array in reverse order.
99+
hash = Hash.new { |h, key| h[key] = [] }
100+
@posts.each { |p| p.send(post_attr.to_sym).each { |t| hash[t] << p } }
101+
hash.values.each { |posts| posts.sort!.reverse! }
102+
hash
103+
end
104+
105+
def tags
106+
post_attr_hash('tags')
107+
end
108+
109+
def categories
110+
post_attr_hash('categories')
111+
end
112+
113+
# Custom `post_attr_hash` method for years
114+
def years
115+
hash = Hash.new { |h, key| h[key] = [] }
116+
@posts.each { |p| hash[p.date.strftime("%Y")] << p }
117+
hash.values.each { |posts| posts.sort!.reverse! }
118+
hash
119+
end
120+
121+
def months(year_posts)
122+
hash = Hash.new { |h, key| h[key] = [] }
123+
year_posts.each { |p| hash[p.date.strftime("%m")] << p }
124+
hash.values.each { |posts| posts.sort!.reverse! }
125+
hash
126+
end
127+
128+
def days(month_posts)
129+
hash = Hash.new { |h, key| h[key] = [] }
130+
month_posts.each { |p| hash[p.date.strftime("%d")] << p }
131+
hash.values.each { |posts| posts.sort!.reverse! }
132+
hash
133+
end
134+
end
135+
end

lib/jekyll-archives/archive.rb

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
module Jekyll
2+
class Archive
3+
include Convertible
4+
5+
attr_accessor :posts, :type, :name, :slug
6+
attr_accessor :data, :content, :output
7+
attr_accessor :path, :ext
8+
attr_accessor :site
9+
10+
# Attributes for Liquid templates
11+
ATTRIBUTES_FOR_LIQUID = %w[
12+
posts
13+
type
14+
name
15+
]
16+
17+
# Initialize a new Archive page
18+
#
19+
# site - The Site object.
20+
# name - The name of the tag/category or a Hash of the year/month/day in case of date.
21+
# e.g. { :year => 2014, :month => 08 } or "my-category" or "my-tag".
22+
# type - The type of archive. Can be one of "year", "month", "day", "category", or "tag"
23+
# posts - The array of posts that belong in this archive.
24+
def initialize(site, name, type, posts)
25+
@site = site
26+
@posts = posts
27+
@type = type
28+
@name = name
29+
30+
# Generate slug if tag or category (taken from jekyll/jekyll/features/support/env.rb)
31+
if name.is_a? String
32+
@slug = name.split(" ").map { |w|
33+
w.downcase.gsub(/[^\w]/, '')
34+
}.join("-")
35+
end
36+
37+
# Use ".html" for file extension and url for path
38+
@ext = ".html"
39+
@path = url
40+
41+
@data = {
42+
"layout" => site.config['jekyll-archives']['layout']
43+
}
44+
@content = ""
45+
end
46+
47+
# The template of the permalink.
48+
#
49+
# Returns the template String.
50+
def template
51+
site.config['jekyll-archives']['permalinks'][type]
52+
end
53+
54+
# Returns a hash of URL placeholder names (as symbols) mapping to the
55+
# desired placeholder replacements. For details see "url.rb".
56+
def url_placeholders
57+
if @name.is_a? Hash
58+
@name.merge({ :type => @type })
59+
else
60+
{ :name => @slug, :type => @type }
61+
end
62+
end
63+
64+
# The generated relative url of this page. e.g. /about.html.
65+
#
66+
# Returns the String url.
67+
def url
68+
@url ||= URL.new({
69+
:template => template,
70+
:placeholders => url_placeholders,
71+
:permalink => nil
72+
}).to_s
73+
rescue ArgumentError
74+
raise ArgumentError.new "Template \"#{template}\" provided is invalid."
75+
end
76+
77+
# Add any necessary layouts to this post
78+
#
79+
# layouts - The Hash of {"name" => "layout"}.
80+
# site_payload - The site payload Hash.
81+
#
82+
# Returns nothing.
83+
def render(layouts, site_payload)
84+
payload = Utils.deep_merge_hashes({
85+
"page" => to_liquid
86+
}, site_payload)
87+
88+
do_layout(payload, layouts)
89+
end
90+
91+
# Convert this Convertible's data to a Hash suitable for use by Liquid.
92+
#
93+
# Returns the Hash representation of this Convertible.
94+
def to_liquid(attrs = nil)
95+
further_data = Hash[(attrs || self.class::ATTRIBUTES_FOR_LIQUID).map { |attribute|
96+
[attribute, send(attribute)]
97+
}]
98+
99+
Utils.deep_merge_hashes(data, further_data)
100+
end
101+
102+
# Obtain destination path.
103+
#
104+
# dest - The String path to the destination dir.
105+
#
106+
# Returns the destination file path String.
107+
def destination(dest)
108+
@dest ||= dest
109+
path = Jekyll.sanitized_path(dest, URL.unescape_path(url))
110+
path = File.join(path, "index.html") if url =~ /\/$/
111+
path
112+
end
113+
114+
# Obtain the write path relative to the destination directory
115+
#
116+
# Returns the destination relative path String.
117+
def relative_path
118+
path = Pathname.new(destination(@dest)).relative_path_from Pathname.new(@dest)
119+
path
120+
end
121+
122+
# Returns the object as a debug String.
123+
def inspect
124+
"#<Jekyll:Archive @type=#{@type.to_s} @name=#{@name}>"
125+
end
126+
127+
# Returns the Boolean of whether this Page is HTML or not.
128+
def html?
129+
true
130+
end
131+
end
132+
end

script/cibuild

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#! /bin/sh
2+
3+
bundle exec rake test

0 commit comments

Comments
 (0)