# -*- coding: utf-8 -*- require 'spec_helper' module CiteProc module Ruby describe "Renderer#render_names" do let(:renderer) { Renderer.new } let(:node) { CSL::Style::Names.new } let(:item) { i = CiteProc::CitationItem.new(:id => 'ID-1') i.data = CiteProc::Item.new(:id => 'ID-1') i } let(:poe) { people(:poe) } let(:philosophers) { CiteProc::Names.new('Plato and Socrates and Aristotle') } describe 'given an empty node' do it 'returns an empty string for an empty item' do expect(renderer.render(item, node)).to eq('') end it 'returns an empty string for an item with variables' do item.data.edition = 'foo' expect(renderer.render_names(item, node)).to eq('') end end describe 'given a single name for the variable' do before do item.data.author = poe node[:variable] = 'author' end it 'formats it in long form' do expect(renderer.render_names(item, node)).to eq('Edgar Allen Poe') end it 'supports nested name node options' do node << CSL::Style::Name.new(:form => 'short') expect(renderer.render_names(item, node)).to eq('Poe') end it 'supports nested label node' do node << CSL::Style::Label.new(:prefix => ' [', :suffix => ']') expect(renderer.render_names(item, node)).to eq('Edgar Allen Poe [author]') end end describe 'given multiple names for a single variable' do before do item.data.editor = philosophers node[:variable] = 'editor' end it 'formats them as a list' do expect(renderer.render_names(item, node)).to eq('Plato, Socrates, Aristotle') end it 'supports nested name node options' do node << CSL::Style::Name.new(:and => 'symbol') expect(renderer.render_names(item, node)).to eq('Plato, Socrates, & Aristotle') end it 'supports nested label node' do node << CSL::Style::Label.new(:prefix => ' (', :suffix => ')') expect(renderer.render_names(item, node)).to eq('Plato, Socrates, Aristotle (editors)') end end describe 'given multiple variables' do before do node[:variable] = 'author editor' node[:delimiter] = '; ' end it 'renders all matching lists combinded using default delimiter' do expect(renderer.render_names(item, node)).to eq('') item.data.author = poe expect(renderer.render_names(item, node)).to eq('Edgar Allen Poe') item.data.editor = philosophers expect(renderer.render_names(item, node)).to eq('Edgar Allen Poe; Plato, Socrates, Aristotle') end it 'keeps the variable order' do item.data.author = poe item.data.editor = philosophers node[:variable] = 'editor author' expect(renderer.render_names(item, node)).to eq('Plato, Socrates, Aristotle; Edgar Allen Poe') end it 'supports labels' do item.data.author = poe item.data.editor = philosophers node << CSL::Style::Label.new(:prefix => ' (', :suffix => ')') expect(renderer.render_names(item, node)).to eq('Edgar Allen Poe (author); Plato, Socrates, Aristotle (editors)') end it 'resolves the editor translator special case' do renderer.format = :text node[:variable] = 'translator author editor' item.data.editor = 'Patrick F. Quinn and G. R. Thompson' item.data.author = poe node.name = { :and => 'symbol', :form => 'short' } node.label = { :prefix => ' (', :suffix => ')' } expect(renderer.render_names(item, node)).to eq('Poe (author); Quinn & Thompson (editors)') item.data.translator = poe expect(renderer.render_names(item, node)).to eq('Poe (translator); Poe (author); Quinn & Thompson (editors)') item.data.translator = 'Patrick F. Quinn and G. R. Thompson' expect(renderer.render(item, node)).to eq('Quinn & Thompson (editors & translators); Poe (author)') end end end describe "Renderer#render_name" do let(:renderer) { Renderer.new } let(:node) { CSL::Style::Name.new } let(:poe) { people(:poe) } let(:philosophers) { CiteProc::Names.new('Plato and Socrates and Aristotle') } describe 'given an empty node' do it 'returns an empty string given no names' do expect(renderer.render_name(CiteProc::Names.new, node)).to eq('') end it 'formats the given name in long form' do expect(renderer.render_name(poe, node)).to eq('Edgar Allen Poe') end it 'formats multiple names delimitted by commas' do expect(renderer.render_name(philosophers, node)).to eq('Plato, Socrates, Aristotle') end end describe 'given a node with and "and" attribute' do it 'inserts a delimited connector' do node[:and] = 'symbol' expect(renderer.render_name(philosophers, node)).to eq('Plato, Socrates, & Aristotle') node[:and] = 'text' expect(renderer.render_name(philosophers, node)).to eq('Plato, Socrates, and Aristotle') end end describe 'given a node with delimier-precedes-last' do it 'inserts final delimiter only for three or more names when set to "contextual"' do node.delimiter_contextually_precedes_last! expect(renderer.render_name(philosophers, node)).to eq('Plato, Socrates, Aristotle') node[:and] = 'text' expect(renderer.render_name(philosophers, node)).to eq('Plato, Socrates, and Aristotle') expect(renderer.render_name(philosophers.take(2), node)).to eq('Plato and Socrates') node[:and] = nil expect(renderer.render_name(philosophers.take(2), node)).to eq('Plato, Socrates') end it 'inserts final delimiter when set to "always"' do node.delimiter_always_precedes_last! expect(renderer.render_name(philosophers, node)).to eq('Plato, Socrates, Aristotle') node[:and] = 'text' expect(renderer.render_name(philosophers, node)).to eq('Plato, Socrates, and Aristotle') expect(renderer.render_name(philosophers.take(2), node)).to eq('Plato, and Socrates') node[:and] = nil expect(renderer.render_name(philosophers.take(2), node)).to eq('Plato, Socrates') end it 'never inserts final delimiter when set to "never" (unless there is no "and")' do node.delimiter_never_precedes_last! expect(renderer.render_name(philosophers, node)).to eq('Plato, Socrates, Aristotle') expect(renderer.render_name(philosophers.take(2), node)).to eq('Plato, Socrates') node[:and] = 'text' expect(renderer.render_name(philosophers, node)).to eq('Plato, Socrates and Aristotle') expect(renderer.render_name(philosophers.take(2), node)).to eq('Plato and Socrates') end it 'supports only-after-inverted-name rule' do names = CiteProc::Names.new('Doe, J. and Smith, S. and Williams, T.') node.delimiter_precedes_last_after_inverted_name! # always delimit when there is no and! expect(renderer.render_name(names, node)).to eq('J. Doe, S. Smith, T. Williams') expect(renderer.render_name(names.take(2), node)).to eq('J. Doe, S. Smith') node[:and] = 'text' expect(renderer.render_name(names, node)).to eq('J. Doe, S. Smith and T. Williams') expect(renderer.render_name(names.take(2), node)).to eq('J. Doe and S. Smith') node[:'name-as-sort-order'] = 'first' expect(renderer.render_name(names, node)).to eq('Doe, J., S. Smith and T. Williams') expect(renderer.render_name(names.take(2), node)).to eq('Doe, J., and S. Smith') node[:'name-as-sort-order'] = 'all' expect(renderer.render_name(names, node)).to eq('Doe, J., Smith, S., and Williams, T.') expect(renderer.render_name(names.take(2), node)).to eq('Doe, J., and Smith, S.') end end describe 'truncation of name lists' do let(:names) { CiteProc::Names.new('Doe, J. and Smith, S. and Williams, T.') } it 'supports et-al formatting via an et-al node' do node[:'et-al-min'] = 3 node[:'et-al-use-first'] = 2 others = CSL::Style::EtAl.new(:prefix => '!!') allow(node).to receive(:et_al).and_return(others) expect(renderer.render_name(names, node)).to eq('J. Doe, S. Smith, !!et al.') others[:term] = 'and others' expect(renderer.render_name(names, node)).to eq('J. Doe, S. Smith, !!and others') end it 'supports et-al-use-last' do node[:'et-al-min'] = 3 node[:'et-al-use-first'] = 2 node[:'et-al-use-last'] = true # truncated list must be at least two names short! expect(renderer.render_name(names, node)).to eq('J. Doe, S. Smith, et al.') node[:'et-al-use-first'] = 1 expect(renderer.render_name(names, node)).to eq('J. Doe, … T. Williams') end describe 'with default delimiter settings' do it 'truncates the list if it matches or exceeds et-al-min' do node[:'et-al-min'] = 3 node[:'et-al-use-first'] = 2 expect(renderer.render_name(names, node)).to eq('J. Doe, S. Smith, et al.') node[:'et-al-use-first'] = 1 expect(renderer.render_name(names, node)).to eq('J. Doe et al.') end it 'does not truncate the list if it is less than et-al-min' do node[:'et-al-min'] = 4 node[:'et-al-use-first'] = 2 expect(renderer.render_name(names, node)).to eq('J. Doe, S. Smith, T. Williams') end end describe 'with delimiter-precedes-et-al set' do it 'inserts delimiter only for two or more names when set to "contextual" or nil' do node.truncate_when! 3 node.truncate_at! 2 # default behaviour should match contextual! expect(renderer.render_name(philosophers, node)).to eq('Plato, Socrates, et al.') node.truncate_at! 1 expect(renderer.render_name(philosophers, node)).to eq('Plato et al.') # set contextual explicitly node.delimiter_contextually_precedes_et_al! expect(renderer.render_name(philosophers, node)).to eq('Plato et al.') node.truncate_at! 2 expect(renderer.render_name(philosophers, node)).to eq('Plato, Socrates, et al.') end it 'inserts delimiter when set to "always"' do node.truncate_when! 3 node.truncate_at! 2 node.delimiter_always_precedes_et_al! expect(renderer.render_name(philosophers, node)).to eq('Plato, Socrates, et al.') node.truncate_at! 1 expect(renderer.render_name(philosophers, node)).to eq('Plato, et al.') end it 'never inserts delimiter when set to "never"' do node.truncate_when! 3 node.truncate_at! 2 node.delimiter_never_precedes_et_al! expect(renderer.render_name(philosophers, node)).to eq('Plato, Socrates et al.') node.truncate_at! 1 expect(renderer.render_name(philosophers, node)).to eq('Plato et al.') end it 'supports only-after-inverted-name rule' do node.truncate_when! 3 node.truncate_at! 2 node.delimiter_precedes_et_al_after_inverted_name! expect(renderer.render_name(names, node)).to eq('J. Doe, S. Smith et al.') node[:'name-as-sort-order'] = 'first' expect(renderer.render_name(names, node)).to eq('Doe, J., S. Smith et al.') node.truncate_at! 1 expect(renderer.render_name(names, node)).to eq('Doe, J., et al.') node[:'name-as-sort-order'] = 'all' expect(renderer.render_name(names, node)).to eq('Doe, J., et al.') node.truncate_at! 2 expect(renderer.render_name(names, node)).to eq('Doe, J., Smith, S., et al.') end end end describe 'name-part formatting' do let(:part) { CSL::Style::NamePart.new(:'text-case' => 'uppercase') } before { node.parts << part } it 'supports family name formatting' do part[:name] = 'family' expect(renderer.render_name(poe, node)).to eq('Edgar Allen POE') end it 'family part includes non-demoted particles' do part[:name] = 'family' expect(renderer.render_name(people(:la_fontaine), node)).to eq('Jean de LA FONTAINE') expect(renderer.render_name(people(:humboldt), node)).to eq('Alexander von HUMBOLDT') expect(renderer.render_name(people(:van_gogh), node)).to eq('Vincent VAN GOGH') end it 'family part affixes includes name suffix for non-inverted names' do part.merge! :name => 'family', :prefix => '(', :suffix => ')' la_fontaine = people(:la_fontaine) la_fontaine[0].suffix = 'Jr.' expect(renderer.render_name(la_fontaine, node)).to eq('Jean de (LA FONTAINE Jr.)') end it 'supports given name formatting' do part[:name] = 'given' expect(renderer.render_name(poe, node)).to eq('EDGAR ALLEN Poe') end it 'given part includes particles' do part[:name] = 'given' expect(renderer.render_name(people(:la_fontaine), node)).to eq('JEAN DE La Fontaine') expect(renderer.render_name(people(:humboldt), node)).to eq('ALEXANDER VON Humboldt') expect(renderer.render_name(people(:van_gogh), node)).to eq('VINCENT van Gogh') end it 'given part affixes enclose demoted particles' do part.merge! :name => 'given', :prefix => '(', :suffix => ')' la_fontaine = people(:la_fontaine) expect(renderer.render_name(la_fontaine, node)).to eq('(JEAN DE) La Fontaine') node.all_names_as_sort_order! expect(renderer.render_name(la_fontaine, node)).to eq('La Fontaine, (JEAN DE)') la_fontaine[0].always_demote_particle! expect(renderer.render_name(la_fontaine, node)).to eq('Fontaine, (JEAN DE La)') end it 'does not alter the passed-in name object' do part[:name] = 'family' expect(renderer.render_name(poe, node)).to eq('Edgar Allen POE') expect(poe.to_s).to eq('Edgar Allen Poe') end end end end end