Commit 91420166 authored by Sylvester Keil's avatar Sylvester Keil

Merge pull request #121 from hendrikvanantwerpen/master

Improve sorting and support grouping for bibliographies
parents bf45383a f3e4dc80
......@@ -63,6 +63,9 @@ default configuration is as follows:
sort_by: none
order: ascending
group_by: none
group_order: ascending
source: ./_bibliography
bibliography: references.bib
bibliography_template: "{{reference}}"
......@@ -100,6 +103,33 @@ are embedded in the Bibtex fields. This option provides a way to circumvent
the problem that the double braces functionality of BibTex is accidentally
parsed by Liquid, while it was intended to keep the exact capitalization style.
The `sort_by` and `order` options specify if and how bibliography
entries are sorted. Entries can be sorted on multiple fields, by using
a list of keys, e.g. `sort_by: year,month`. Ordering can be specified
per sort level, e.g. `order: descending,ascending` will sort the years
descending, but per year the months are ascending. If there are more
sort keys than order directives, the last order entry is used for the
remaining keys.
The `group_by` and `group_order` options specify how bibliography
items are grouped. Grouping can be multi-level as well,
e.g. `group_by: type, year` groups entries per publication type, and
within those groups per year. Ordering for groups is specified in the
same way as the sort order. Publication types -- specified with group
key `type`, can be ordered by adding `type_order` to the
configuration. For example, `type_order: article,techreport` lists
journal articles before technical reports. Types not mentioned in
`type_order` are considered smaller than types that are
mentioned. Types can be merge in one group using the `type_aliases`
setting. By default `phdthesis` and `mastersthesis` are grouped as
`thesis`. By using, for example, `type_aliases: { inproceeding =>
article}`, journal and conference articles appear in a single
group. The display names for entry types are specified with
`type_names`. Names for common types are provided, but they can be
extended or overridden. For example, the default name for `article` is
*Journal Articles*, but it can be changed to *Papers* using
`type_name: { article => 'Papers' }`.
### Bibliographies
Once you have loaded Jekyll-Scholar, all files with the extension `.bib` or
......@@ -187,7 +217,8 @@ use the `--max` option:
{% bibliography --max 5 %}
This would generate a bibliography containing only the first 5 entries
of your bibliography (after query filters and sort options have been applied).
of your bibliography (after query filters and sort options have been
applied). Limiting entries is disabled if grouping is active.
### Bibliography Template
......
Feature: Grouping BibTeX Bibliographies
As a scholar who likes to blog
I want to group my bibliographies according to configurable parameters
@tags @grouping
Scenario: Group By Year
Given I have a scholar configuration with:
| key | value |
| group_by | year |
And I have a "_bibliography" directory
And I have a file "_bibliography/references.bib":
"""
@book{ruby1,
title = {The Ruby Programming Language},
author = {Flanagan, David and Matsumoto, Yukihiro},
year = {2008},
publisher = {O'Reilly Media}
}
@book{ruby2,
title = {The Ruby Programming Language},
author = {Flanagan, David and Matsumoto, Yukihiro},
year = {2007},
publisher = {O'Reilly Media}
}
"""
And I have a page "scholar.html":
"""
---
---
{% bibliography -f references %}
"""
When I run jekyll
Then the _site directory should exist
And the "_site/scholar.html" file should exist
Then I should see "<h2 class=\"bibliography\">2007</h2>" in "_site/scholar.html"
And I should see "<h2 class=\"bibliography\">2008</h2>" in "_site/scholar.html"
@tags @grouping
Scenario: Group Order
Given I have a scholar configuration with:
| key | value |
| group_by | year |
| group_order | ascending |
And I have a "_bibliography" directory
And I have a file "_bibliography/references.bib":
"""
@book{ruby1,
title = {The Ruby Programming Language},
author = {Flanagan, David and Matsumoto, Yukihiro},
year = {2008},
publisher = {O'Reilly Media}
}
@book{ruby2,
title = {The Ruby Programming Language},
author = {Flanagan, David and Matsumoto, Yukihiro},
year = {2007},
publisher = {O'Reilly Media}
}
"""
And I have a page "scholar.html":
"""
---
---
{% bibliography -f references %}
"""
When I run jekyll
Then the _site directory should exist
And the "_site/scholar.html" file should exist
Then "<h2 class=\"bibliography\">2007</h2>" should come before "<h2 class=\"bibliography\">2008</h2>" in "_site/scholar.html"
@tags @grouping
Scenario: Reverse Group Order
Given I have a scholar configuration with:
| key | value |
| group_by | year |
| group_order | descending |
And I have a "_bibliography" directory
And I have a file "_bibliography/references.bib":
"""
@book{ruby1,
title = {The Ruby Programming Language},
author = {Flanagan, David and Matsumoto, Yukihiro},
year = {2008},
publisher = {O'Reilly Media}
}
@book{ruby2,
title = {The Ruby Programming Language},
author = {Flanagan, David and Matsumoto, Yukihiro},
year = {2007},
publisher = {O'Reilly Media}
}
"""
And I have a page "scholar.html":
"""
---
---
{% bibliography -f references %}
"""
When I run jekyll
Then the _site directory should exist
And the "_site/scholar.html" file should exist
Then "<h2 class=\"bibliography\">2008</h2>" should come before "<h2 class=\"bibliography\">2007</h2>" in "_site/scholar.html"
@tags @grouping
Scenario: Multi-level Group Order
Given I have a scholar configuration with:
| key | value |
| group_by | year,month |
| group_order | descending,ascending |
And I have a "_bibliography" directory
And I have a file "_bibliography/references.bib":
"""
@book{ruby1,
title = {November 08},
year = {2008},
month = {nov}
}
@book{ruby2,
title = {March 08},
year = {2008},
month = {mar}
}
@book{ruby3,
title = {June 07},
year = {2007},
month = {jun}
}
"""
And I have a page "scholar.html":
"""
---
---
{% bibliography -f references %}
"""
When I run jekyll
Then the _site directory should exist
And the "_site/scholar.html" file should exist
Then "March" should come before "November" in "_site/scholar.html"
And "November" should come before "June" in "_site/scholar.html"
@tags @grouping
Scenario: Group by Type
Given I have a scholar configuration with:
| key | value |
| group_by | type |
And I have a "_bibliography" directory
And I have a file "_bibliography/references.bib":
"""
@book{ruby1,
title = {Book 1},
}
@article{ruby2,
title = {Article 1},
}
@book{ruby3,
title = {Book 2},
}
@article{ruby4,
title = {Article 2},
}
"""
And I have a page "scholar.html":
"""
---
---
{% bibliography -f references %}
"""
When I run jekyll
Then the _site directory should exist
And the "_site/scholar.html" file should exist
Then "Journal Articles" should come before "Article 1" in "_site/scholar.html"
And "Journal Articles" should come before "Article 2" in "_site/scholar.html"
Then "Books" should come before "Book 1" in "_site/scholar.html"
And "Books" should come before "Book 2" in "_site/scholar.html"
@tags @grouping
Scenario: Type Order
Given I have a scholar configuration with:
| key | value |
| group_by | type |
| type_order | [article,book] |
And I have a "_bibliography" directory
And I have a file "_bibliography/references.bib":
"""
@book{ruby1,
title = {Book 1},
}
@article{ruby2,
title = {Article 1},
}
@book{ruby3,
title = {Book 2},
}
@techreport{ruby4,
title = {Book 2},
}
@article{ruby5,
title = {Article 2},
}
"""
And I have a page "scholar.html":
"""
---
---
{% bibliography -f references %}
"""
When I run jekyll
Then the _site directory should exist
And the "_site/scholar.html" file should exist
Then "Journal Articles" should come before "Books" in "_site/scholar.html"
And "Books" should come before "Technical Reports" in "_site/scholar.html"
@tags @grouping
Scenario: Type Names
Given I have a scholar configuration with:
| key | value |
| group_by | type |
| type_names | { article: 'Long Papers' } |
And I have a "_bibliography" directory
And I have a file "_bibliography/references.bib":
"""
@article{ruby1,
title = {Article},
}
@book{ruby2,
title = {Book},
}
"""
And I have a page "scholar.html":
"""
---
---
{% bibliography -f references %}
"""
When I run jekyll
Then the _site directory should exist
And the "_site/scholar.html" file should exist
Then I should see "Long Papers" in "_site/scholar.html"
And I should not see "Journal Articles" in "_site/scholar.html"
And I should see "Books" in "_site/scholar.html"
@tags @grouping
Scenario: Type Aliases
Given I have a scholar configuration with:
| key | value |
| group_by | type |
| type_aliases | { phdthesis: phdthesis } |
And I have a "_bibliography" directory
And I have a file "_bibliography/references.bib":
"""
@mastersthesis{ruby1,
title = {MSc Thesis},
}
@phdthesis{ruby2,
title = {PhD Thesis},
}
"""
And I have a page "scholar.html":
"""
---
---
{% bibliography -f references %}
"""
When I run jekyll
Then the _site directory should exist
And the "_site/scholar.html" file should exist
Then I should see "PhD Theses" in "_site/scholar.html"
And I should not see "Master's Theses" in "_site/scholar.html"
@tags @grouping
Scenario: Month Names
Given I have a scholar configuration with:
| key | value |
| group_by | month |
| month_names | [Januar,Februar,März,April,Mai,Juni,Juli,August,September,Oktober,November,Dezember] |
And I have a "_bibliography" directory
And I have a file "_bibliography/references.bib":
"""
@book{ruby1,
title = {The Ruby Programming Language},
author = {Flanagan, David and Matsumoto, Yukihiro},
year = {2008},
month = jan
}
@book{ruby2,
title = {The Ruby Programming Language},
author = {Flanagan, David and Matsumoto, Yukihiro},
year = {2008},
month = dec
}
"""
And I have a page "scholar.html":
"""
---
---
{% bibliography -f references %}
"""
When I run jekyll
Then the _site directory should exist
And the "_site/scholar.html" file should exist
Then I should see "Januar" in "_site/scholar.html"
And I should not see "January" in "_site/scholar.html"
And I should see "Dezember" in "_site/scholar.html"
And I should not see "December" in "_site/scholar.html"
\ No newline at end of file
......@@ -262,3 +262,40 @@ Feature: Sorting BibTeX Bibliographies
And the "_site/scholar.html" file should exist
Then "August 07" should come before "March 08" in "_site/scholar.html"
And "March 08" should come before "December 08" in "_site/scholar.html"
@tags @sorting
Scenario: Multi-level Sort Order
Given I have a scholar configuration with:
| key | value |
| sort_by | year, month |
| order | descending, ascending |
And I have a "_bibliography" directory
And I have a file "_bibliography/references.bib":
"""
@book{ruby1,
title = {August 07},
year = {2007},
month = aug
}
@book{ruby2,
title = {March 08},
year = {2008},
month = mar
}
@book{ruby3,
title = {December 08},
year = {2008},
month = dec
}
"""
And I have a page "scholar.html":
"""
---
---
{% bibliography %}
"""
When I run jekyll
Then the _site directory should exist
And the "_site/scholar.html" file should exist
Then "March 08" should come before "December 08" in "_site/scholar.html"
And "December 08" should come before "August 07" in "_site/scholar.html"
......@@ -6,6 +6,9 @@ module Jekyll
'sort_by' => 'none',
'order' => 'ascending',
'group_by' => 'none',
'group_order' => 'ascending',
'bibliography_group_tag' => 'h2,h3,h4,h5',
'bibliography_list_tag' => 'ol',
'bibliography_item_tag' => 'li',
......@@ -33,8 +36,28 @@ module Jekyll
'details_link_class' => 'details',
'query' => '@*'
'query' => '@*',
'type_names' => {
'article' => 'Journal Articles',
'book' => 'Books',
'incollection' => 'Book Chapters',
'inproceedings' => 'Conference Articles',
'thesis' => 'Theses',
'mastersthesis' => 'Master\'s Theses',
'phdthesis' => 'PhD Theses',
'manual' => 'Manuals',
'techreport' => 'Technical Reports',
'misc' => 'Miscellaneous',
'unpublished' => 'Unpublished',
},
'type_aliases' => {
'phdthesis' => 'thesis',
'mastersthesis' => 'thesis',
},
'type_order' => [],
'month_names' => nil,
}.freeze
class << self
......
......@@ -42,8 +42,45 @@ module Jekyll
cited_keys.clear
end
if group?
groups = group(items)
bibliography = render_groups(groups)
else
items = items[offset..max] if limit_entries?
bibliography = render_items(items)
end
bibliography
end
def render_groups(groups)
def group_renderer(groupsOrItems,keys,order,tags)
if keys.count == 0
renderer(force = true)
render_items(groupsOrItems)
else
groupsOrItems
.sort do |e1,e2|
if (order.first || group_order.last) =~ /^(desc|reverse)/i
group_compare(keys.first,e2[0],e1[0])
else
group_compare(keys.first,e1[0],e2[0])
end
end
.map do |e|
bibhead = content_tag(tags.first || group_tags.last,
group_name(keys.first, e[0]),
:class => config['bibliography_class'])
bibentries = group_renderer(e[1], keys.drop(1), order.drop(1), tags.drop(1))
bibhead + "\n" + bibentries
end
.join("\n")
end
end
group_renderer(groups,group_keys,group_order,group_tags)
end
def render_items(items)
bibliography = items.each_with_index.map { |entry, index|
reference = bibliography_tag(entry, index + 1)
......@@ -56,8 +93,8 @@ module Jekyll
}.join("\n")
content_tag config['bibliography_list_tag'], bibliography, :class => config['bibliography_class']
end
end
end
......
module Jekyll
class Scholar
require 'date'
# Load styles into static memory.
# They should be thread safe as long as they are
......@@ -156,11 +157,20 @@ module Jekyll
def sort(unsorted)
return unsorted if skip_sort?
sorted = unsorted.sort_by do |e|
e.values_at(*sort_keys).map { |v| v.nil? ? BibTeX::Value.new : v }
sorted = unsorted.sort do |e1, e2|
sort_keys
.map.with_index do |key, idx|
v1 = e1[key].nil? ? BibTeX::Value.new : e1[key]
v2 = e2[key].nil? ? BibTeX::Value.new : e2[key]
if (sort_order[idx] || sort_order.last) =~ /^(desc|reverse)/i
v2 <=> v1
else
v1 <=> v2
end
end
.find { |c| c != 0 } || 0
end
sorted.reverse! if config['order'] =~ /^(desc|reverse)/i
sorted
end
......@@ -173,6 +183,124 @@ module Jekyll
.map { |key| key == 'month' ? 'month_numeric' : key }
end
def sort_order
return @sort_order unless @sort_order.nil?
@sort_order = Array(config['order'])
.map { |key| key.to_s.split(/\s*,\s*/) }
.flatten
end
def group?
config['group_by'] != 'none'
end
def group(ungrouped)
def grouper(items,keys,order)
groups = items
.group_by do |item|
group_value(keys.first,item)
end
if keys.count == 1
groups
else
groups.merge(groups) do |key,items|
grouper(items,keys.drop(1),order.drop(1))
end
end
end
grouper(ungrouped,group_keys,group_order)
end
def group_keys
return @group_keys unless @group_keys.nil?
@group_keys = Array(config['group_by'])
.map { |key| key.to_s.split(/\s*,\s*/) }
.flatten
.map { |key| key == 'month' ? 'month_numeric' : key }
end
def group_order
return @group_order unless @group_order.nil?
@group_order = Array(config['group_order'])
.map { |key| key.to_s.split(/\s*,\s*/) }
.flatten
end
def group_compare(key,v1,v2)
case key
when 'type'
o1 = type_order.find_index(v1)
o2 = type_order.find_index(v2)
if o1.nil? && o2.nil?
0
elsif o1.nil?
1
elsif o2.nil?
-1
else
o1 <=> o2
end
else
v1 <=> v2
end
end
def group_value(key,item)
case key
when 'type'
type_aliases[item.type.to_s] || item.type.to_s
else
value = item[key]
if value.numeric?
value.to_i
elsif value.date?
value.to_date
else
value.to_s
end
end
end
def group_tags
return @group_tags unless @group_tags.nil?
@group_tags = Array(config['bibliography_group_tag'])
.map { |key| key.to_s.split(/\s*,\s*/) }
.flatten
end
def group_name(key,value)
case key
when 'type'
type_names[value] || value.to_s
when 'month_numeric'
month_names[value] || "(unknown)"
else
value.to_s
end
end
def type_order
@type_order ||= config['type_order']
end
def type_aliases
@type_aliases ||= Scholar.defaults['type_aliases'].merge(config['type_aliases'])
end
def type_names
@type_names ||= Scholar.defaults['type_names'].merge(config['type_names'])
end
def month_names
return @month_names unless @month_names.nil?
@month_names = config['month_names'].nil? ? Date::MONTHNAMES : config['month_names'].unshift(nil)
end
def suppress_author?
!!@suppress_author
end
......@@ -399,8 +527,10 @@ module Jekyll
config['details_dir']
end
def renderer
@renderer ||= CiteProc::Ruby::Renderer.new :format => 'html',
def renderer(force = false)
return @renderer if @renderer && !force
@renderer = CiteProc::Ruby::Renderer.new :format => 'html',
:style => style, :locale => config['locale']
end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment