diff --git a/README.md b/README.md index 65a96cb720d6a5632d7111e4b1318bf2ecc2e1f7..9d14a6b3cb4407a6bfecfb4cbf1c36d7151ed1cf 100644 --- a/README.md +++ b/README.md @@ -255,6 +255,23 @@ For example, this could be rendered as: +#### Multiple citation + +You can cite multiple items in a single citation by referencing all ids +of the items you wish to quote separated by spaces. For example, +`{% cite ruby microscope %}` would produce a cite tag like: + + (Flanagan & Matsumoto 2008; Shaughnessy 2013) + +#### Page numbers and locators + +If you would like to add page numbers to your citation, you can use the +`-l` or `--locator` option. For example, `{% cite ruby -l 23-5 %}` would +produce a citation like `(Matsumoto, 2008, pp. 23-5)`. + +When quoting multiple items (see above) you can add multiple locators after +the list of ids. For example, `{% cite ruby microscope -l 2 -l 24 & 32 %}`. + #### Displaying formatted references If you want to display the full formatted reference entry, you can use the @@ -446,7 +463,7 @@ License Jekyll-Scholar is distributed under the same license as Jekyll. -Copyright (c) 2011-2013 [Sylvester Keil](http://sylvester.keil.or.at/) +Copyright (c) 2011-2014 [Sylvester Keil](http://sylvester.keil.or.at/) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal diff --git a/features/bibtex.feature b/features/bibtex.feature index b41be6dbcc6453e51fc09a8860a9c4781a4e09d4..913802740110d2253812dc553a8a114dff43edae 100644 --- a/features/bibtex.feature +++ b/features/bibtex.feature @@ -204,7 +204,7 @@ Feature: BibTeX """ --- --- - {% bibliography --style mla %} + {% bibliography --style modern-language-association %} """ When I run jekyll Then the _site directory should exist diff --git a/features/citation.feature b/features/citation.feature index 67f8b44ca5eb33b9505393582a809bccbc732256..d6901d05194490af010928be94ae8e48e318412e 100644 --- a/features/citation.feature +++ b/features/citation.feature @@ -111,3 +111,107 @@ Feature: Citations Then the _site directory should exist And the "_site/scholar.html" file should exist And I should see "#a-ruby" in "_site/scholar.html" + + @tags @cite + Scenario: Multiple Citations + Given I have a scholar configuration with: + | key | value | + | source | ./_bibliography | + | bibliography | my_references | + And I have a "_bibliography" directory + And I have a file "_bibliography/my_references.bib": + """ + @book{ruby, + title = {The Ruby Programming Language}, + author = {Flanagan, David and Matsumoto, Yukihiro}, + year = {2008}, + publisher = {O'Reilly Media} + } + + @book{microscope, + title = {Ruby Under a Microscope}, + author = {Pat Shaughnessy}, + year = {2013}, + publisher = {No Starch Press} + } + """ + And I have a page "scholar.html": + """ + --- + --- + {% cite ruby microscope %} + """ + When I run jekyll + Then the _site directory should exist + And the "_site/scholar.html" file should exist + And I should see "Flanagan & Matsumoto, 2008; Shaughnessy, 2013" in "_site/scholar.html" + + @tags @cite @locator + Scenario: Multiple Citations with locators + Given I have a scholar configuration with: + | key | value | + | source | ./_bibliography | + | bibliography | my_references | + And I have a "_bibliography" directory + And I have a file "_bibliography/my_references.bib": + """ + @book{ruby, + title = {The Ruby Programming Language}, + author = {Flanagan, David and Matsumoto, Yukihiro}, + year = {2008}, + publisher = {O'Reilly Media} + } + + @book{microscope, + title = {Ruby Under a Microscope}, + author = {Pat Shaughnessy}, + year = {2013}, + publisher = {No Starch Press} + } + """ + And I have a page "scholar.html": + """ + --- + --- + {% cite ruby microscope -l 2-3 --locator 23 & 42 %} + """ + When I run jekyll + Then the _site directory should exist + And the "_site/scholar.html" file should exist + And I should see "Matsumoto, 2008, pp. 2-3; Shaughnessy, 2013, pp. 23 & 42" in "_site/scholar.html" + + @tags @cite @citation_number + Scenario: Multiple citations using citation numbers + Given I have a scholar configuration with: + | key | value | + | source | ./_bibliography | + | bibliography | my_references | + | style | ieee | + And I have a "_bibliography" directory + And I have a file "_bibliography/my_references.bib": + """ + @book{ruby, + title = {The Ruby Programming Language}, + author = {Flanagan, David and Matsumoto, Yukihiro}, + year = {2008}, + publisher = {O'Reilly Media} + } + + @book{microscope, + title = {Ruby Under a Microscope}, + author = {Pat Shaughnessy}, + year = {2013}, + publisher = {No Starch Press} + } + """ + And I have a page "scholar.html": + """ + --- + --- + {% cite ruby microscope %} + """ + When I run jekyll + Then the _site directory should exist + And the "_site/scholar.html" file should exist + And I should see "\[1\], \[2\]" in "_site/scholar.html" + diff --git a/features/cited_only.feature b/features/cited_only.feature new file mode 100644 index 0000000000000000000000000000000000000000..7faecbf44288f37bfbf14ab50b5a50344e7c43bd --- /dev/null +++ b/features/cited_only.feature @@ -0,0 +1,72 @@ +Feature: Cited-only Bibliographies + As a scholar who likes to blog + I want to cite references on my website + And generate bibliographies for the cited items + + Scenario: Cited-only references from a single bibliography + Given I have a scholar configuration with: + | key | value | + | source | ./_bibliography | + And I have a "_bibliography" directory + And I have a file "_bibliography/references.bib": + """ + @book{ruby, + title = {The Ruby Programming Language}, + author = {Flanagan, David and Matsumoto, Yukihiro}, + year = {2008}, + publisher = {O'Reilly Media} + }, + @book{smalltalk, + title = {Smalltalk Best Practice Patterns}, + author = {Kent Beck}, + year = {1996}, + publisher = {Prentice Hall} + } + + """ + And I have a page "scholar.html": + """ + --- + --- + {% cite smalltalk %} + {% bibliography --cited %} + """ + When I run jekyll + Then the _site directory should exist + And the "_site/scholar.html" file should exist + And I should not see "The Ruby Programming Language" in "_site/scholar.html" + And I should see "Smalltalk Best Practice Patterns" in "_site/scholar.html" + + Scenario: No-cited items result in empty bibliography + Given I have a scholar configuration with: + | key | value | + | source | ./_bibliography | + And I have a "_bibliography" directory + And I have a file "_bibliography/references.bib": + """ + @book{ruby, + title = {The Ruby Programming Language}, + author = {Flanagan, David and Matsumoto, Yukihiro}, + year = {2008}, + publisher = {O'Reilly Media} + }, + @book{smalltalk, + title = {Smalltalk Best Practice Patterns}, + author = {Kent Beck}, + year = {1996}, + publisher = {Prentice Hall} + } + + """ + And I have a page "scholar.html": + """ + --- + --- + {% bibliography --cited %} + """ + When I run jekyll + Then the _site directory should exist + And the "_site/scholar.html" file should exist + And I should not see "The Ruby Programming Language" in "_site/scholar.html" + And I should not see "Smalltalk Best Practice Patterns" in "_site/scholar.html" + diff --git a/features/step_definitions/jekyll_steps.rb b/features/step_definitions/jekyll_steps.rb index 868e4869e00cef0fd9c4eb8cc547152324b57f66..20fef1b57d55bfd8891ea34bb9f027ef3817753a 100644 --- a/features/step_definitions/jekyll_steps.rb +++ b/features/step_definitions/jekyll_steps.rb @@ -44,7 +44,6 @@ Then(/^the (.*) directory should exist$/) do |dir| end Then(/^I should see "(.*)" in "(.*)"$/) do |text, file| - puts File.open(file).readlines.join assert_match Regexp.new(text), File.open(file).readlines.join end diff --git a/features/step_definitions/scholar_steps.rb b/features/step_definitions/scholar_steps.rb index 5bd209e10a12ecc3427376cfd72f42940baad786..7cac358c95f184740c10f43a8814e9ce4b428302 100644 --- a/features/step_definitions/scholar_steps.rb +++ b/features/step_definitions/scholar_steps.rb @@ -1,15 +1,15 @@ -Given /^I have a "([^"]*)" directory/ do |dir| +Given(/^I have a "([^"]*)" directory/) do |dir| FileUtils.mkdir(dir) end -Given /^I have a (?:page|file) "([^"]*)":$/ do |file, string| +Given(/^I have a (?:page|file) "([^"]*)":$/) do |file, string| File.open(file, 'w') do |f| f.write(string) end end -Given /^I have a configuration file with "([^\"]*)" set to:$/ do |key, table| +Given(/^I have a configuration file with "([^\"]*)" set to:$/) do |key, table| File.open('_config.yml', 'w') do |f| f.write("#{key}:\n") table.hashes.each do |row| @@ -18,7 +18,7 @@ Given /^I have a configuration file with "([^\"]*)" set to:$/ do |key, table| end end -Given /^I have a scholar configuration with:$/ do |table| +Given(/^I have a scholar configuration with:$/) do |table| File.open('_config.yml', 'w') do |f| f.write("scholar:\n") table.hashes.each do |row| @@ -28,7 +28,7 @@ Given /^I have a scholar configuration with:$/ do |table| end -Then /^"(.*)" should come before "(.*)" in "(.*)"$/ do |p1, p2, file| +Then(/^"(.*)" should come before "(.*)" in "(.*)"$/) do |p1, p2, file| data = File.open(file).readlines.join('') m1 = data.match(p1) diff --git a/features/support/hooks.rb b/features/support/hooks.rb index d7cc7974af714d367904d12ba9f3f6fea8658819..0ff5a9518ed03ce3b786e3e02fd7c5324fd47df4 100644 --- a/features/support/hooks.rb +++ b/features/support/hooks.rb @@ -1,5 +1,6 @@ Before do - FileUtils.mkdir_p(TEST_DIR) unless File.exist?(TEST_DIR) + FileUtils.rm_rf(TEST_DIR) if File.exist?(TEST_DIR) + FileUtils.mkdir_p(TEST_DIR) Dir.chdir(TEST_DIR) end diff --git a/jekyll-scholar.gemspec b/jekyll-scholar.gemspec index 56fe940ea81ff5042cc8066fe08893a34d47c01f..449bf9935e89b967357263c69266e595f9261492 100644 --- a/jekyll-scholar.gemspec +++ b/jekyll-scholar.gemspec @@ -26,7 +26,8 @@ Gem::Specification.new do |s| s.rubyforge_project = s.name s.add_runtime_dependency('jekyll', '~> 1.0') - s.add_runtime_dependency('citeproc-ruby', '~> 0.0.6') + s.add_runtime_dependency('citeproc-ruby', '~> 1.0') + s.add_runtime_dependency('csl-styles', '~> 1.0') s.add_runtime_dependency('bibtex-ruby', '~> 3.0') s.files = `git ls-files`.split("\n") diff --git a/lib/jekyll/scholar.rb b/lib/jekyll/scholar.rb index 088740d3b8dcbbbbe9f85879aea715a8e924fcc7..63c6e1d0e35790d580ae1f7b7aeb3b35bd16ea6b 100644 --- a/lib/jekyll/scholar.rb +++ b/lib/jekyll/scholar.rb @@ -4,7 +4,8 @@ require 'jekyll' require 'optparse' require 'bibtex' -require 'citeproc' +require 'citeproc/ruby' +require 'csl/styles' require 'jekyll/scholar/version' require 'jekyll/scholar/defaults' diff --git a/lib/jekyll/scholar/converters/bibtex.rb b/lib/jekyll/scholar/converters/bibtex.rb index 803a35c05e0ff53a40bede2dd71a9e9279461315..b458ffb0c5e95fed01cb2ca14a6e8405b4f44717 100644 --- a/lib/jekyll/scholar/converters/bibtex.rb +++ b/lib/jekyll/scholar/converters/bibtex.rb @@ -1,45 +1,46 @@ module Jekyll class Scholar class BibTeXConverter < Converter + include Scholar::Utilities + safe true priority :highest - attr_reader :config - + attr_reader :config + @pattern = (/bib(tex)?$/i).freeze @extension = '.html'.freeze - - class << self - attr_reader :pattern, :extension - end - + + class << self + attr_reader :pattern, :extension + end + def initialize(config = {}) super @config['scholar'] = Scholar.defaults.merge(@config['scholar'] || {}) @markdown = Jekyll::Converters::Markdown.new(config) end - + def matches(extension) - extension =~ BibTeXConverter.pattern - end - + extension =~ BibTeXConverter.pattern + end + def output_ext(extension) - BibTeXConverter.extension - end - + BibTeXConverter.extension + end + def convert(content) - content = BibTeX.parse(content, :strict => true, :include => [:meta_content], :filter => [:latex]).map do |b| + content = BibTeX.parse(content, :strict => true, :include => [:meta_content], :filter => [:latex]).map do |b| if b.respond_to?(:to_citeproc) - CiteProc.process b.to_citeproc, :style => config['style'], - :locale => config['locale'], :format => 'html' + render_bibliography b else - b.is_a?(BibTeX::MetaContent) ? b.to_s : '' + b.is_a?(BibTeX::MetaContent) ? b.to_s : '' end end @markdown.convert(content.join("\n")) end - + end end -end \ No newline at end of file +end diff --git a/lib/jekyll/scholar/tags/bibliography.rb b/lib/jekyll/scholar/tags/bibliography.rb index 401bb5c439364cd6e49b84a566e9f156bd48bef1..dde5302ced4e95211415640e90a3bd525fbaa9a2 100644 --- a/lib/jekyll/scholar/tags/bibliography.rb +++ b/lib/jekyll/scholar/tags/bibliography.rb @@ -38,15 +38,6 @@ module Jekyll end end - private - - def citeproc - @citeproc ||= CiteProc::Processor.new do |p| - p.style = config['style'] - p.format = 'html' - p.locale = config['locale'] - end - end end end diff --git a/lib/jekyll/scholar/tags/cite.rb b/lib/jekyll/scholar/tags/cite.rb index 1f5bab4d39fee0ea772b59c9fc80fa06884d01e0..a210792249dd2092e918f4d926d6bb699a8f1c34 100644 --- a/lib/jekyll/scholar/tags/cite.rb +++ b/lib/jekyll/scholar/tags/cite.rb @@ -10,14 +10,14 @@ module Jekyll super @config = Scholar.defaults.dup - @key, arguments = arguments.strip.split(/\s+/, 2) + @keys, arguments = split_arguments(arguments) optparse(arguments) end def render(context) set_context_to context - cite key + cite keys end end @@ -25,4 +25,4 @@ module Jekyll end end -Liquid::Template.register_tag('cite', Jekyll::Scholar::CiteTag) \ No newline at end of file +Liquid::Template.register_tag('cite', Jekyll::Scholar::CiteTag) diff --git a/lib/jekyll/scholar/tags/cite_details.rb b/lib/jekyll/scholar/tags/cite_details.rb index 718a8481d1f72e7c0d636864c45a3a5a6a0d642b..2cc6e3735d05e65f522156c5d6d01ce6147b1b60 100644 --- a/lib/jekyll/scholar/tags/cite_details.rb +++ b/lib/jekyll/scholar/tags/cite_details.rb @@ -8,14 +8,16 @@ module Jekyll super @config = Scholar.defaults.dup - @key, arguments = arguments.strip.split(/\s+/, 2) + @keys, arguments = split_arguments arguments optparse(arguments) end def render(context) set_context_to context - cite_details key, text + keys.map { |key| + cite_details key, text + }.join("\n") end end diff --git a/lib/jekyll/scholar/tags/quote.rb b/lib/jekyll/scholar/tags/quote.rb index 376d558d17019505ec7caddfff9b72ad18e3b016..1f590840ef24179bcc71ec3bd14fffb4798e3568 100644 --- a/lib/jekyll/scholar/tags/quote.rb +++ b/lib/jekyll/scholar/tags/quote.rb @@ -3,32 +3,32 @@ module Jekyll class QuoteTag < Liquid::Block include Scholar::Utilities - + attr_reader :pages - + def initialize(tag_name, arguments, tokens) super - + @config = Scholar.defaults.dup - @key, arguments = arguments.strip.split(/\s+/, 2) + @keys, arguments = split_arguments arguments end def render(context) set_context_to context - + quote = super.strip.gsub(/\n\n/, '
').gsub(/\n/, '
')
quote = content_tag :p, quote
-
- citation = cite key
-
+
+ citation = cite keys
+
quote << content_tag(:cite, citation)
-
+
content_tag :blockquote, quote
end
-
+
end
-
+
end
end
-Liquid::Template.register_tag('quote', Jekyll::Scholar::QuoteTag)
\ No newline at end of file
+Liquid::Template.register_tag('quote', Jekyll::Scholar::QuoteTag)
diff --git a/lib/jekyll/scholar/tags/reference.rb b/lib/jekyll/scholar/tags/reference.rb
index 5a325061a8893b90ff8615b14481fdc5f2e9cff6..d5e346179ff42094e6196b0097a7cbd8e1c9211e 100644
--- a/lib/jekyll/scholar/tags/reference.rb
+++ b/lib/jekyll/scholar/tags/reference.rb
@@ -8,16 +8,16 @@ module Jekyll
super
@config = Scholar.defaults.dup
- @key, arguments = arguments.strip.split(/\s+/, 2)
+ @keys, arguments = split_arguments arguments
optparse(arguments)
end
def render(context)
set_context_to context
- reference_tag bibliography[key]
- rescue
- "(#{key})"
+ keys.map { |key|
+ reference_tag bibliography[key]
+ }.join("\n")
end
end
diff --git a/lib/jekyll/scholar/utilities.rb b/lib/jekyll/scholar/utilities.rb
index 09d668b7dfb416263b81dd0db77b2ed56cb1a85a..2732bed00be564de64a446e99a32c60943ff16c9 100644
--- a/lib/jekyll/scholar/utilities.rb
+++ b/lib/jekyll/scholar/utilities.rb
@@ -1,13 +1,31 @@
module Jekyll
class Scholar
+ # Load styles into static memory.
+ # They should be thread safe as long as they are
+ # treated as being read-only.
+ STYLES = Hash.new do |h, k|
+ h[k.to_s] = CSL::Style.load k
+ end
+
+
# Utility methods used by several Scholar plugins. The methods in this
# module may depend on the presence of #config, #bibtex_files, and
# #site readers
module Utilities
attr_reader :config, :site, :query,
- :context, :prefix, :key, :text
+ :context, :prefix, :keys, :text
+
+ def split_arguments(arguments)
+
+ tokens = arguments.strip.split(/\s+/)
+
+ args = tokens.take_while { |a| !a.start_with?('-') }
+ opts = (tokens - args).join(' ')
+
+ [args, opts]
+ end
def optparse(arguments)
return if arguments.nil? || arguments.empty?
@@ -34,6 +52,10 @@ module Jekyll
@text = text
end
+ opts.on('-l', '--locator LOCATOR') do |locator|
+ locators << locator
+ end
+
opts.on('-s', '--style STYLE') do |style|
@style = style
end
@@ -43,11 +65,15 @@ module Jekyll
end
end
- argv = arguments.split(/(\B-[cfqptTs]|\B--(?:cited|file|query|prefix|text|style|template|))/)
+ argv = arguments.split(/(\B-[cfqptTsl]|\B--(?:cited|file|query|prefix|text|style|template|locator|))/)
parser.parse argv.map(&:strip).reject(&:empty?)
end
+ def locators
+ @locators ||= []
+ end
+
def bibtex_files
@bibtex_files ||= [config['bibliography']]
end
@@ -145,12 +171,11 @@ module Jekyll
p
end
- def reference_tag(entry)
+ def reference_tag(entry, index = nil)
return missing_reference unless entry
entry = entry.convert(*bibtex_filters) unless bibtex_filters.empty?
- reference = CiteProc.process entry.to_citeproc,
- :style => style, :locale => config['locale'], :format => 'html'
+ reference = render_bibliography entry, index
content_tag reference_tagname, reference,
:id => [prefix, entry.key].compact.join('-')
@@ -192,7 +217,7 @@ module Jekyll
liquid_template.render({
'entry' => liquidify(entry),
- 'reference' => reference_tag(entry),
+ 'reference' => reference_tag(entry, index),
'key' => entry.key,
'type' => entry.type,
'link' => repository_link_for(entry),
@@ -253,23 +278,55 @@ module Jekyll
config['details_dir']
end
- def cite(key)
- context['cited'] ||= []
- context['cited'] << key
+ def renderer
+ @renderer ||= CiteProc::Ruby::Renderer.new :format => 'html',
+ :style => style, :locale => config['locale']
+ end
- if bibliography.key?(key)
- entry = bibliography[key]
- entry = entry.convert(*bibtex_filters) unless bibtex_filters.empty?
+ def render_citation(items)
+ renderer.render items.zip(locators).map { |entry, locator|
+ cited_keys << entry.key
- citation = CiteProc.process entry.to_citeproc, :style => style,
- :locale => config['locale'], :format => 'html', :mode => :citation
+ item = citation_item_for entry, citation_number
+ item.locator = locator
- link_to "##{[prefix, entry.key].compact.join('-')}", citation.join
- else
- missing_reference
+ item
+ }, STYLES[style].citation
+ end
+
+ def render_bibliography(entry, index = nil)
+ renderer.render citation_item_for(entry, index),
+ STYLES[style].bibliography
+ end
+
+ def citation_item_for(entry, citation_number = nil)
+ CiteProc::CitationItem.new id: entry.id do |c|
+ c.data = CiteProc::Item.new entry.to_citeproc
+ c.data[:'citation-number'] = citation_number
end
- rescue
- "(#{key})"
+ end
+
+ def cited_keys
+ context['cited'] ||= []
+ end
+
+ def citation_number
+ number = context['citation_number'] || 1
+ context['citation_number'] = number.succ
+ number
+ end
+
+ def cite(keys)
+ items = keys.map do |key|
+ if bibliography.key?(key)
+ entry = bibliography[key]
+ entry = entry.convert(*bibtex_filters) unless bibtex_filters.empty?
+ else
+ return missing_reference
+ end
+ end
+
+ link_to "##{[prefix, keys[0]].compact.join('-')}", render_citation(items)
end
def cite_details(key, text)
diff --git a/lib/jekyll/scholar/version.rb b/lib/jekyll/scholar/version.rb
index c4e65441347020118905a0923c71ed8d1f9f67d2..9edc5371bff13f77b8fa7adad036c69670e80137 100644
--- a/lib/jekyll/scholar/version.rb
+++ b/lib/jekyll/scholar/version.rb
@@ -1,5 +1,5 @@
module Jekyll
class Scholar
- VERSION = '2.0.0'.freeze
+ VERSION = '3.0.0'.freeze
end
end