citeproc 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/Gemfile CHANGED
@@ -1,2 +1,11 @@
1
1
  source :rubygems
2
2
  gemspec
3
+
4
+ group :debug do
5
+ gem 'ruby-debug', :platforms => [:ruby_18, :jruby]
6
+ gem 'ruby-debug19', :require => 'ruby-debug', :platforms => [:ruby_19]
7
+ end
8
+
9
+ group :optional do
10
+ gem 'chronic', '~>0.6'
11
+ end
@@ -11,7 +11,7 @@ Signal.trap('INT' ) { abort("\n") } # Ctrl-C
11
11
 
12
12
  def rspec (*paths)
13
13
  paths = paths.reject { |p| !File.exists?(p) }
14
- run "bundle exec rspec #{ paths.empty? ? 'spec' : paths.join(' ') }"
14
+ run "bundle exec rspec --tty -c #{ paths.empty? ? 'spec' : paths.join(' ') }"
15
15
  end
16
16
 
17
17
  def run (cmd)
@@ -1,9 +1,20 @@
1
- require 'rubygems'
1
+
2
+ require 'multi_json'
3
+ require 'forwardable'
4
+
2
5
 
3
6
  require 'citeproc/version'
7
+
8
+ require 'citeproc/compatibility'
4
9
  require 'citeproc/extensions'
10
+
5
11
  require 'citeproc/errors'
12
+
6
13
  require 'citeproc/abbreviate'
14
+ require 'citeproc/attributes'
15
+ require 'citeproc/variable'
16
+ require 'citeproc/date'
17
+
7
18
  require 'citeproc/engine'
8
19
  require 'citeproc/processor'
9
20
  require 'citeproc/utilities'
@@ -0,0 +1,95 @@
1
+
2
+ module CiteProc
3
+
4
+ module Attributes
5
+ extend Forwardable
6
+
7
+ def self.included(base)
8
+ base.extend(ClassMethods)
9
+ end
10
+
11
+ def attributes
12
+ @attributes ||= {}
13
+ end
14
+
15
+ def merge(other)
16
+ return self if other.nil?
17
+
18
+ case other
19
+ when String, /^\s*\{/
20
+ other = MulitJson.encode(other, :symbolize_keys => true)
21
+ when Hash
22
+ other = other.deep_copy
23
+ else
24
+ other = other.to_hash
25
+ end
26
+
27
+ other.each_pair { |k,v| attributes[k.to_sym] = v }
28
+
29
+ self
30
+ end
31
+
32
+ alias update merge
33
+
34
+ def reverse_merge(other)
35
+ other.merge(self)
36
+ end
37
+
38
+ alias to_hash attributes
39
+
40
+ module ClassMethods
41
+
42
+ def create(parameters)
43
+ new.merge(parameters)
44
+ end
45
+
46
+ def attr_predicates(*arguments)
47
+ arguments.flatten.each do |field|
48
+ field, default = *(field.is_a?(Hash) ? field.to_a.flatten : [field]).map(&:to_s)
49
+ attr_field(field, default, true)
50
+ end
51
+ end
52
+
53
+ def attr_fields
54
+ arguments.flatten.each do |field|
55
+ attr_field(*(field.is_a?(Hash) ? field.to_a.flatten : [field]).map(&:to_s))
56
+ end
57
+ end
58
+
59
+ def attr_field(field, default = nil, predicate = false)
60
+ method_id = field.downcase.gsub(/[-\s]+/, '_')
61
+
62
+ unless instance_methods.include?(method_id)
63
+ if default
64
+ define_method(method_id) do
65
+ attributes[field.to_sym]
66
+ end
67
+ else
68
+ define_method(method_id) do
69
+ attributes[field.to_sym] ||= default
70
+ end
71
+ end
72
+ end
73
+
74
+ writer_id = [method_id,'='].join
75
+ unless instance_methods.include?(writer_id)
76
+ define_method(writer_id) do |value|
77
+ attributes[field.to_sym] = value
78
+ end
79
+ end
80
+
81
+ predicate_id = [method_id, '?'].join
82
+ if predicate && !instance_methods.include?(predicate_id)
83
+ define_method(predicate_id) do
84
+ ![nil, false, '', [], 'false', 'no', 'never'].include?(attributes[field.to_sym])
85
+ end
86
+
87
+ has_predicate = ['has_', predicate_id].join
88
+ alias_method(has_predicate, predicate_id) unless instance_methods.include?(has_predicate)
89
+ end
90
+ end
91
+
92
+ end
93
+
94
+ end
95
+ end
@@ -0,0 +1,10 @@
1
+ unless Symbol.is_a?(Comparable)
2
+ class Symbol
3
+ include Comparable
4
+
5
+ def <=>(other)
6
+ return nil unless other.respond_to?(:to_s)
7
+ to_s <=> other.to_s
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,166 @@
1
+
2
+ require 'date'
3
+
4
+ begin
5
+ require 'chronic'
6
+ rescue LoadError => e
7
+ # warn 'failed to load chronic gem'
8
+ end
9
+
10
+ module CiteProc
11
+
12
+ class Date < Variable
13
+
14
+ include Attributes
15
+
16
+ alias attributes value
17
+ alias to_hash value
18
+
19
+ @parser = Object.const_defined?(:Chronic) ? ::Chronic : ::Date
20
+
21
+ class << self
22
+
23
+ def parse(date_string)
24
+ new(@parser.parse(date_string))
25
+ rescue => e
26
+ raise ArgumentError.new("failed to parse date from #{date_string.inspect}", e)
27
+ end
28
+
29
+ def today
30
+ new(::Date.today)
31
+ end
32
+
33
+ alias now today
34
+
35
+ end
36
+
37
+
38
+ def initialize(attributes = ::Date.today)
39
+ super
40
+ end
41
+
42
+ def initialize_copy(other)
43
+ @value = other.value.deep_copy
44
+ end
45
+
46
+ def parse(attributes)
47
+ case
48
+ when attributes.is_a?(Date)
49
+ @value = attributes.dup
50
+
51
+ when attributes.is_a?(Numeric)
52
+ @value = { :'date-parts' => [[attributes.to_i]] }
53
+
54
+ when attributes.is_a?(Hash)
55
+ attributes = attributes.symbolize_keys
56
+ if attributes.has_key?(:raw)
57
+ @value = Date.parse(attributes.delete(:raw)).value
58
+ @value.merge!(attributes)
59
+ else
60
+ @value = attributes.deep_copy
61
+ end
62
+ to_i!
63
+
64
+ when attributes.respond_to?(:strftime)
65
+ @value = { :'date-parts' => [attributes.strftime('%Y-%m-%d').split(/-/).map(&:to_i)] }
66
+
67
+ when attributes.is_a?(Array)
68
+ @value = { :'date-parts' => attributes[0].is_a?(Array) ? attributes : [attributes] }
69
+ to_i!
70
+
71
+ when attributes.respond_to?(:to_s)
72
+ @value = Date.parse(attributes.to_s).value
73
+
74
+ else
75
+ raise ArgumentError, "failed to parse date from #{attributes.inspect}"
76
+ end
77
+ end
78
+
79
+ attr_predicates :circa, :season, :literal, :'date-parts'
80
+
81
+ def date_parts
82
+ @value[:'date-parts'] ||= [[]]
83
+ end
84
+
85
+ alias parts date_parts
86
+ alias parts= date_parts=
87
+
88
+ %w{ year month day }.each_with_index do |m,i|
89
+ define_method(m) { parts[0][i] }
90
+ define_method("#{m}=") { |v| parts[0][i] = v.to_i }
91
+ end
92
+
93
+ def start_date
94
+ ::Date.new(*parts[0])
95
+ end
96
+
97
+ def start_date=(date)
98
+ parts[0] = date.strftime('%Y-%m-%d').split(/-/).map(&:to_i)
99
+ end
100
+
101
+ def end_date=(date)
102
+ parts[1] = date.nil? ? [0,0,0] : date.strftime('%Y-%m-%d').split(/-/).map(&:to_i)
103
+ end
104
+
105
+ # Returns a Ruby date object for this instance, or Range object if this
106
+ # instance is closed range
107
+ def to_ruby
108
+ closed_range? ? start_date ... end_date : start_date
109
+ end
110
+
111
+ def end_date
112
+ closed_range? ? ::Date.new(*parts[1]) : nil
113
+ end
114
+
115
+ def has_end_date?
116
+ parts[1] && !parts[1].empty?
117
+ end
118
+
119
+ # Returns true if this date is a range
120
+ alias range? has_end_date?
121
+
122
+ def open_range?
123
+ range? && parts[1].uniq == [0]
124
+ end
125
+
126
+ def closed_range?
127
+ range? && !open_range?
128
+ end
129
+
130
+ alias uncertain? circa?
131
+
132
+ # Marks the date as uncertain
133
+ def uncertain!; @value[:'circa'] = true; end
134
+
135
+ # Marks the date as a certain date
136
+ def certain!; @value[:'circa'] = false; end
137
+
138
+ def certain?; !uncertain?; end
139
+
140
+ def numeric?; false; end
141
+
142
+ def bc?; year && year < 0; end
143
+ def ad?; !bc? && year < 1000; end
144
+
145
+ def to_s
146
+ literal? ? literal : @value.inspect
147
+ end
148
+
149
+ def sort_order
150
+ "%04d%02d%02d-%04d%02d%02d" % ((parts[0] + [0,0,0])[0,3] + ((parts[1] || []) + [0,0,0])[0,3])
151
+ end
152
+
153
+ def <=>(other)
154
+ return nil unless other.is_a?(Date)
155
+ [year, sort_order] <=> [other.year, other.sort_order]
156
+ end
157
+
158
+ private
159
+
160
+ def to_i!
161
+ parts.each { |p| p.map!(&:to_i) }
162
+ end
163
+
164
+ end
165
+
166
+ end
@@ -10,9 +10,11 @@ module CiteProc
10
10
  @subclasses ||= []
11
11
 
12
12
  class << self
13
-
13
+
14
14
  attr_reader :subclasses, :type, :version
15
15
 
16
+ attr_writer :default
17
+
16
18
  private :new
17
19
 
18
20
  def inherited(subclass)
@@ -21,6 +23,10 @@ module CiteProc
21
23
  @subclasses = subclasses.sort_by { |engine| -1 * engine.priority }
22
24
  end
23
25
 
26
+ def default
27
+ @default ||= autodetect or warn 'no citeproc engine found'
28
+ end
29
+
24
30
  # Returns the engine class for the given name or nil. If no suitable
25
31
  # class is found and a block is given, executes the block and returns
26
32
  # the result. The list of available engines will be passed to the block.
@@ -99,20 +105,26 @@ module CiteProc
99
105
  raise NotImplementedByEngine
100
106
  end
101
107
 
108
+ alias process_citation_cluster process
109
+
102
110
  def append
103
111
  raise NotImplementedByEngine
104
112
  end
105
113
 
106
114
  alias append_citation_cluster append
107
-
108
- def process_citation_cluster
115
+
116
+ def preview
109
117
  raise NotImplementedByEngine
110
118
  end
111
-
112
- def make_bibliography
119
+
120
+ alias preview_citation_cluster preview
121
+
122
+ def bibliography
113
123
  raise NotImplementedByEngine
114
124
  end
115
125
 
126
+ alias make_bibliography bibliography
127
+
116
128
  def update_items
117
129
  raise NotImplementedByEngine
118
130
  end
@@ -8,10 +8,10 @@ module CiteProc
8
8
  end
9
9
  end
10
10
 
11
- class EngineError < Error
12
- end
11
+ class EngineError < Error; end
13
12
 
14
- class NotImplementedByEngine < EngineError
15
- end
13
+ class NotImplementedByEngine < EngineError; end
14
+
15
+ class ArgumentError < Error; end
16
16
 
17
17
  end
@@ -7,7 +7,7 @@ module CiteProc
7
7
  Hash[*map { |k,v| [
8
8
  k.is_a?(Symbol) ? k : k.respond_to?(:deep_copy) ? k.deep_copy : k.clone,
9
9
  v.is_a?(Symbol) ? v : v.respond_to?(:deep_copy) ? v.deep_copy : v.clone
10
- ]}.flatten]
10
+ ]}.flatten(1)]
11
11
  end
12
12
  end
13
13
 
@@ -23,6 +23,20 @@ module CiteProc
23
23
 
24
24
  end
25
25
 
26
+ # shamelessly copied from active_support
27
+ module SymbolizeKeys
28
+ def symbolize_keys
29
+ inject({}) do |options, (key, value)|
30
+ options[(key.to_sym rescue key) || key] = value
31
+ options
32
+ end
33
+ end
34
+
35
+ def symbolize_keys!
36
+ replace(symbolize_keys)
37
+ end
38
+ end
39
+
26
40
  module AliasMethods
27
41
  private
28
42
  def alias_methods(*arguments)
@@ -37,6 +51,7 @@ end
37
51
  class Hash
38
52
  include CiteProc::Extensions::DeepCopy
39
53
  include CiteProc::Extensions::DeepFetch
54
+ include CiteProc::Extensions::SymbolizeKeys unless Hash.instance_methods.include?(:symbolize_keys)
40
55
  end
41
56
 
42
57
  # module Kernel
@@ -2,6 +2,8 @@
2
2
  module CiteProc
3
3
  class Processor
4
4
 
5
+ extend Forwardable
6
+
5
7
  @defaults ||= {
6
8
  :locale => 'en-US',
7
9
  :style => 'chicago-author-date',
@@ -14,11 +16,14 @@ module CiteProc
14
16
  end
15
17
 
16
18
  attr_reader :options, :engine, :items
19
+
20
+ def_delegators :@engine, :process, :append, :preview, :bibliography, :style, :style=, :abbreviate, :abbreviations, :abbreviations=
17
21
 
18
22
  def initialize(options = {})
19
23
  @options = Processor.defaults.merge(options)
20
- @engine = Engine.autodetect(@options).new :processor => self
24
+ @engine = Engine.autodetect(@options).new(:processor => self)
25
+ @items = {}
21
26
  end
22
27
 
23
28
  end
24
- end
29
+ end
@@ -0,0 +1,121 @@
1
+
2
+ module CiteProc
3
+
4
+ #
5
+ # A CiteProc Variable represents the content of a text, numeric, date, or
6
+ # name variable.
7
+ #
8
+ class Variable
9
+
10
+ extend Forwardable
11
+
12
+ include Comparable
13
+
14
+ @fields = Hash.new { |h,k| h.fetch(k.to_sym, nil) }.merge({
15
+ :date => %w{ accessed container event-date issued original-date },
16
+
17
+ :names => %w{
18
+ author editor translator recipient interviewer publisher composer
19
+ original-publisher original-author container-author collection-editor },
20
+
21
+ :text => %w{
22
+ id abstract annote archive archive-location archive-place authority
23
+ call-number chapter-number citation-label citation-number collection-title
24
+ container-title DOI edition event event-place first-reference-note-number
25
+ genre ISBN issue jurisdiction keyword locator medium note number
26
+ number-of-pages number-of-volumes original-publisher original-publisher-place
27
+ original-title page page-first publisher publisher-place references
28
+ section status title URL version volume year-suffix }
29
+ })
30
+
31
+ @fields.each_value { |v| v.map!(&:to_sym) }
32
+
33
+ @types = Hash.new { |h,k| h.fetch(k.to_sym, nil) }.merge(
34
+ Hash[*@fields.keys.map { |k| @fields[k].map { |n| [n,k] } }.flatten]
35
+ ).freeze
36
+
37
+ @fields[:name] = @fields[:names]
38
+ @fields[:dates] = @fields[:date]
39
+
40
+ @fields[:all] = @fields[:any] =
41
+ [:date,:names,:text].reduce([]) { |s,a| s.concat(@fields[a]) }.sort
42
+
43
+ @fields.freeze
44
+
45
+ class << self
46
+ attr_reader :fields, :types
47
+
48
+ def parse(*args)
49
+ end
50
+ end
51
+
52
+ def initialize(attributes = nil)
53
+ parse(attributes)
54
+ yield self if block_given?
55
+ end
56
+
57
+ def initialize_copy(other)
58
+ @value = other.value.dup
59
+ end
60
+
61
+ attr_accessor :value
62
+
63
+ def_delegators :@value, :to_s, :strip!, :upcase!, :downcase!, :sub!, :gsub!, :chop!, :chomp!, :rstrip!
64
+
65
+ def_delegators :to_s, :empty?, :=~, :===, :match, :intern, :to_sym, :end_with?, :start_with?, :include?, :upcase, :downcase, :reverse, :chop, :chomp, :rstrip, :gsub, :sub, :size, :strip, :succ, :to_c, :to_r, :to_str, :split, :each_byte, :each_char, :each_line
66
+
67
+ def parse(attributes)
68
+ case
69
+ when attributes.is_a?(Hash)
70
+ attributes.each_key do |key|
71
+ writer = "#{key}="
72
+ send(writer, attributes[key]) if respond_to?(writer)
73
+ end
74
+ when attributes.respond_to?(:to_s)
75
+ @value = attributes.to_s.dup
76
+ end
77
+ end
78
+
79
+ def type
80
+ @type ||= self.class.name.split(/::/)[-1].downcase.to_sym
81
+ end
82
+
83
+ def numeric?
84
+ self =~ /\d/ ? to_i : false
85
+ end
86
+
87
+ # @returns (first) numeric data contained in the variable's value
88
+ def to_i
89
+ to_s =~ /(-?\d+)/ && $1.to_i || 0
90
+ end
91
+
92
+ def to_f
93
+ to_s =~ /(-?\d[\d,\.]*)/ && $1.tr(',','.').to_f || 0.0
94
+ end
95
+
96
+ def strip_markup
97
+ gsub(/<[^>]*>/, '')
98
+ end
99
+
100
+ def strip_markup!
101
+ gsub!(/<[^>]*>/, '')
102
+ end
103
+
104
+ def <=>(other)
105
+ case
106
+ when other.respond_to?(:strip_markup)
107
+ strip_markup <=> other.strip_markup
108
+ when other && other.respond_to?(:to_s)
109
+ to_s <=> other.to_s
110
+ else
111
+ nil
112
+ end
113
+ end
114
+
115
+ def to_json
116
+ MultiJson.encode(@value)
117
+ end
118
+ end
119
+
120
+ class Text < Variable; end
121
+ end
@@ -1,3 +1,3 @@
1
1
  module CiteProc
2
- VERSION = '0.0.1'.freeze
2
+ VERSION = '0.0.2'.freeze
3
3
  end
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  module CiteProc
4
- describe 'Abbreviate' do
4
+ describe Abbreviate do
5
5
  before { Object.class_eval { include Abbreviate } }
6
6
 
7
7
  let(:subject) { Object.new }
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+
3
+ module CiteProc
4
+ describe Attributes do
5
+
6
+ before(:each) { Object.instance_eval { include Attributes } }
7
+
8
+ let(:instance) { o = Object.new }
9
+ let(:other) { o = Object.new; o.attributes[:foo] = 'bar'; o }
10
+
11
+ it { should_not be_nil }
12
+
13
+ describe '.attr_fields' do
14
+
15
+ # before(:all) { class Object; attr_fields :value, %w[ is-numeric punctuation-mode ]; end }
16
+
17
+ it 'generates setters for attr_field values' do
18
+ # pending
19
+ # lambda { Object.new.is_numeric }.should_not raise_error
20
+ end
21
+
22
+ it 'generates no other setters' do
23
+ lambda { Object.new.some_other_value }.should raise_error
24
+ end
25
+ end
26
+
27
+ describe '#merge' do
28
+
29
+ it 'merges non-existent values from other object' do
30
+ Object.new.merge(other).attributes[:foo].should == 'bar'
31
+ end
32
+
33
+ # it 'does not overwrite existing values when merging other object' do
34
+ # instance.merge(other)['foo'].should == 'bar'
35
+ # end
36
+
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,103 @@
1
+ require 'spec_helper'
2
+
3
+ module CiteProc
4
+ describe Date do
5
+
6
+ it { should_not be nil }
7
+
8
+ it { should_not be_numeric }
9
+
10
+ describe '.new' do
11
+
12
+ end
13
+
14
+ describe '.create' do
15
+ it 'should accept parameters and return a new instance' do
16
+ Date.create('date-parts' => [[2001, 1]]).year.should == 2001
17
+ end
18
+ end
19
+
20
+ describe 'literal dates' do
21
+ it 'is not literal by default' do
22
+ Date.new.should_not be_literal
23
+ end
24
+
25
+ it 'is literal if it contains only a literal field' do
26
+ Date.create(:literal => 'foo').should be_literal
27
+ end
28
+
29
+ it 'is literal if it contains a literal field' do
30
+ Date.create('date-parts' => [[2000]], :literal => 'foo').should be_literal
31
+ end
32
+ end
33
+
34
+ describe 'uncertain dates' do
35
+ it 'are uncertain' do
36
+ Date.new({ 'date-parts' => [[-225]], 'circa' => '1' }).should be_uncertain
37
+ Date.new { |d| d.parts = [[-225]]; d.uncertain! }.should be_uncertain
38
+ end
39
+ end
40
+
41
+ describe 'sorting' do
42
+
43
+ let(:ad2k) { Date.create('date-parts' => [[2000]])}
44
+ let(:may) { Date.create('date-parts' => [[2000, 5]])}
45
+ let(:first_of_may) { Date.create('date-parts' => [[2000, 5, 1]])}
46
+
47
+ let(:bc100) { Date.create('date-parts' => [[-100]]) }
48
+ let(:bc50) { Date.create('date-parts' => [[-50]]) }
49
+ let(:ad50) { Date.create('date-parts' => [[50]]) }
50
+ let(:ad100) { Date.create('date-parts' => [[100]]) }
51
+
52
+ it 'dates with more date-parts will come after those with fewer parts' do
53
+ (ad2k < may && may < first_of_may).should be true
54
+ end
55
+
56
+ it 'negative years are sorted inversely' do
57
+ [ad50, bc100, bc50, ad100].sort.map(&:year).should == [-100, -50, 50, 100]
58
+ end
59
+ end
60
+
61
+
62
+ describe '#display' do
63
+ it 'returns an empty string by default' do
64
+ Date.new({}).to_s == ''
65
+ end
66
+ end
67
+
68
+ describe '#to_json' do
69
+ it 'supports simple parts' do
70
+ Date.new(%w{2000 1 15}).to_json.should == '{"date-parts":[[2000,1,15]]}'
71
+ end
72
+
73
+ it 'supports integers and strings parts' do
74
+ Date.new(['2000', '1', '15']).to_json.should == '{"date-parts":[[2000,1,15]]}'
75
+ Date.new([2000, 1, 15]).to_json.should == '{"date-parts":[[2000,1,15]]}'
76
+ Date.new(['2000', 1, '15']).to_json.should == '{"date-parts":[[2000,1,15]]}'
77
+ end
78
+
79
+ it 'supports negative years' do
80
+ Date.new(-200).to_json.should == '{"date-parts":[[-200]]}'
81
+ end
82
+
83
+ it 'supports seasons' do
84
+ Date.create({:season => '1', 'date-parts' => [[1950]]}).to_json.should == '{"date-parts":[[1950]],"season":"1"}'
85
+ Date.create({:season => 'Trinity', 'date-parts' => [[1975]]}).to_json.should == '{"date-parts":[[1975]],"season":"Trinity"}'
86
+ end
87
+
88
+ it 'supports string literals' do
89
+ Date.new(:literal => '13th century').to_json.should == '{"literal":"13th century"}'
90
+ end
91
+
92
+ it 'supports raw strings' do
93
+ Date.new(:raw => '23 May 1955').to_json.should == '{"date-parts":[[1955,5,23]]}'
94
+ end
95
+
96
+ it 'supports open and closed ranges' do
97
+ Date.new([[2000,11],[2000,12]]).to_json.should == '{"date-parts":[[2000,11],[2000,12]]}'
98
+ Date.new([[2000,11],[0,0]]).to_json.should == '{"date-parts":[[2000,11],[0,0]]}'
99
+ end
100
+ end
101
+
102
+ end
103
+ end
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  module CiteProc
4
- describe 'Engine' do
4
+ describe Engine do
5
5
 
6
6
  it 'cannot be instantiated' do
7
7
  lambda { Engine.new }.should raise_error(NoMethodError)
@@ -22,6 +22,15 @@ describe Hash do
22
22
  hash.deep_copy[:a][:b].should_not equal(hash[:a][:b])
23
23
  hash.deep_copy[:a][:b][:c].should == hash[:a][:b][:c]
24
24
  end
25
+
26
+ context 'when given nested arrays' do
27
+ let(:hash) {{:a => [[1,2]]}}
28
+ it 'it returns a deep copy' do
29
+ hash.deep_copy[:a].should == hash[:a]
30
+ hash.deep_copy[:a].should_not equal(hash[:a])
31
+ end
32
+
33
+ end
25
34
  end
26
35
 
27
36
  describe '#deep_fetch' do
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  module CiteProc
4
- describe 'Processor' do
4
+ describe Processor do
5
5
  before { Class.new(Engine) }
6
6
 
7
7
  let(:subject) { Processor.new }
@@ -0,0 +1,168 @@
1
+ require 'spec_helper'
2
+
3
+ module CiteProc
4
+ describe Variable do
5
+
6
+ describe '.new' do
7
+ it { should be_an_instance_of(Variable) }
8
+
9
+ it 'is empty by default' do
10
+ Variable.new.should be_empty
11
+ end
12
+
13
+ it 'equals an empty string (==) by default' do
14
+ Variable.new.should == ''
15
+ end
16
+
17
+ it 'matches an empty pattern by default' do
18
+ Variable.new.should =~ /^$/
19
+ end
20
+
21
+ it 'accepts a string value' do
22
+ Variable.new('test').should == 'test'
23
+ end
24
+
25
+ it 'accepts a numeric value' do
26
+ Variable.new(23).should == '23'
27
+ Variable.new(23.1).should == '23.1'
28
+ end
29
+
30
+ it 'accepts an attributes hash' do
31
+ Variable.new(:value => 'test').should == 'test'
32
+ end
33
+
34
+ it 'supports self yielding block' do
35
+ Variable.new { |v| v.value = 'test' }.should == 'test'
36
+ end
37
+ end
38
+
39
+ describe '.fields' do
40
+ it 'contains :names fields' do
41
+ Variable.fields[:names].should_not be_empty
42
+ Variable.fields[:name].should equal Variable.fields[:names]
43
+ end
44
+
45
+ it 'contains :date fields' do
46
+ Variable.fields[:date].should_not be_empty
47
+ Variable.fields[:dates].should equal Variable.fields[:date]
48
+ end
49
+
50
+ it 'contains :text fields' do
51
+ Variable.fields[:text].should_not be_empty
52
+ end
53
+
54
+ it 'accepts either string or symbol input' do
55
+ Variable.fields[:names].should equal Variable.fields['names']
56
+ end
57
+ end
58
+
59
+ describe '.types' do
60
+ it 'given a field name returns the corresponding type' do
61
+ Variable.types[:author].should == :names
62
+ Variable.types[:issued].should == :date
63
+ Variable.types[:abstract].should == :text
64
+ end
65
+
66
+ it 'accepts either string or symbol input' do
67
+ Variable.types.should have_key(:author)
68
+ Variable.types['author'].should equal Variable.types[:author]
69
+ end
70
+ end
71
+
72
+ describe '#to_s' do
73
+ it 'displays the value' do
74
+ Variable.new('test').to_s.should == 'test'
75
+ end
76
+ end
77
+
78
+ describe '#to_i' do
79
+ it 'returns zero by default' do
80
+ Variable.new.to_i.should == 0
81
+ end
82
+
83
+ context 'when the value is numeric' do
84
+ %w{ -23 -1 0 1 23 }.each do |value|
85
+ it "returns the integer value (#{value})" do
86
+ Variable.new(value).to_i.should equal(value.to_i)
87
+ end
88
+ end
89
+
90
+ it 'returns only the first numeric value if there are several' do
91
+ Variable.new('testing 1, 2, 3...').to_i.should == 1
92
+ end
93
+ end
94
+ end
95
+
96
+ describe '#to_f' do
97
+ it 'returns zero by default' do
98
+ Variable.new.to_f.should == 0.0
99
+ end
100
+
101
+ context 'when the value is numeric' do
102
+ %w{ -23.2 -1.45 0.2 1.733 23 }.each do |value|
103
+ it "returns the integer value (#{value})" do
104
+ Variable.new(value).to_f.should == value.to_f
105
+ end
106
+ end
107
+
108
+ it 'returns only the first numeric value if there are several' do
109
+ Variable.new('testing 1, 2, 3...').to_f.should == 1.0
110
+ end
111
+
112
+ it 'works with dot and comma separators' do
113
+ Variable.new('1,23').to_f.should == Variable.new('1.23').to_f
114
+ end
115
+ end
116
+ end
117
+
118
+ describe '#numeric?' do
119
+ it 'returns false by default' do
120
+ Variable.new.should_not be_numeric
121
+ end
122
+
123
+ context 'variable contains a number' do
124
+ it 'returns true (string initialized)' do
125
+ Variable.new('23').should be_numeric
126
+ Variable.new('foo 23').should be_numeric
127
+ end
128
+ it 'returns true (integer initialized)' do
129
+ Variable.new(23).should be_numeric
130
+ end
131
+ end
132
+ context 'variable does not contain a number' do
133
+ it 'returns false for strings' do
134
+ Variable.new('test').should_not be_numeric
135
+ end
136
+ end
137
+ end
138
+
139
+ describe '#strip_markup' do
140
+ let(:greeting) { '<h1>hello<b> world</b></h1>' }
141
+ it 'returns a string stripped of html tags' do
142
+ Variable.new(greeting).strip_markup.should == 'hello world'
143
+ end
144
+ it 'does not alter the value itself' do
145
+ v = Variable.new(greeting)
146
+ v.strip_markup
147
+ v.should == greeting
148
+ end
149
+ end
150
+
151
+ describe '#strip_markup!' do
152
+ let(:greeting) { '<h1>hello<b> world</b></h1>' }
153
+ it 'strips of html tags' do
154
+ v = Variable.new(greeting)
155
+ v.strip_markup!
156
+ v.should == 'hello world'
157
+ end
158
+ end
159
+
160
+
161
+ describe 'sorting' do
162
+ end
163
+
164
+ describe '#to_json' do
165
+ end
166
+
167
+ end
168
+ end
metadata CHANGED
@@ -1,126 +1,140 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: citeproc
3
- version: !ruby/object:Gem::Version
4
- version: 0.0.1
3
+ version: !ruby/object:Gem::Version
5
4
  prerelease:
5
+ version: 0.0.2
6
6
  platform: ruby
7
- authors:
8
- - Sylvester Keil
7
+ authors:
8
+ - Sylvester Keil
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-08-03 00:00:00.000000000 +02:00
12
+
13
+ date: 2011-08-08 00:00:00 +02:00
13
14
  default_executable:
14
- dependencies:
15
- - !ruby/object:Gem::Dependency
16
- name: multi_json
17
- requirement: &2157053340 !ruby/object:Gem::Requirement
18
- none: false
19
- requirements:
20
- - - ~>
21
- - !ruby/object:Gem::Version
22
- version: '1.0'
23
- type: :runtime
24
- prerelease: false
25
- version_requirements: *2157053340
26
- - !ruby/object:Gem::Dependency
27
- name: cucumber
28
- requirement: &2157052700 !ruby/object:Gem::Requirement
29
- none: false
30
- requirements:
31
- - - ! '>='
32
- - !ruby/object:Gem::Version
33
- version: 1.0.2
34
- type: :development
35
- prerelease: false
36
- version_requirements: *2157052700
37
- - !ruby/object:Gem::Dependency
38
- name: rspec
39
- requirement: &2157052100 !ruby/object:Gem::Requirement
40
- none: false
41
- requirements:
42
- - - ! '>='
43
- - !ruby/object:Gem::Version
44
- version: 2.6.0
45
- type: :development
46
- prerelease: false
47
- version_requirements: *2157052100
48
- - !ruby/object:Gem::Dependency
49
- name: watchr
50
- requirement: &2157051560 !ruby/object:Gem::Requirement
51
- none: false
52
- requirements:
53
- - - ! '>='
54
- - !ruby/object:Gem::Version
55
- version: '0.7'
56
- type: :development
57
- prerelease: false
58
- version_requirements: *2157051560
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: multi_json
18
+ prerelease: false
19
+ requirement: &id001 !ruby/object:Gem::Requirement
20
+ none: false
21
+ requirements:
22
+ - - ~>
23
+ - !ruby/object:Gem::Version
24
+ version: "1.0"
25
+ type: :runtime
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: cucumber
29
+ prerelease: false
30
+ requirement: &id002 !ruby/object:Gem::Requirement
31
+ none: false
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: 1.0.2
36
+ type: :development
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: rspec
40
+ prerelease: false
41
+ requirement: &id003 !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 2.6.0
47
+ type: :development
48
+ version_requirements: *id003
49
+ - !ruby/object:Gem::Dependency
50
+ name: watchr
51
+ prerelease: false
52
+ requirement: &id004 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: "0.7"
58
+ type: :development
59
+ version_requirements: *id004
59
60
  description: A a cite processor for Citation Style Language (CSL) styles.
60
61
  email: http://sylvester.keil.or.at
61
62
  executables: []
63
+
62
64
  extensions: []
63
- extra_rdoc_files:
64
- - README.md
65
- files:
66
- - .gitignore
67
- - .rspec
68
- - Gemfile
69
- - LICENSE
70
- - README.md
71
- - auto.watchr
72
- - citeproc.gemspec
73
- - lib/citeproc.rb
74
- - lib/citeproc/abbreviate.rb
75
- - lib/citeproc/engine.rb
76
- - lib/citeproc/errors.rb
77
- - lib/citeproc/extensions.rb
78
- - lib/citeproc/processor.rb
79
- - lib/citeproc/utilities.rb
80
- - lib/citeproc/version.rb
81
- - spec/citeproc/abbreviate_spec.rb
82
- - spec/citeproc/engine_spec.rb
83
- - spec/citeproc/extensions_spec.rb
84
- - spec/citeproc/processor_spec.rb
85
- - spec/citeproc/utilities_spec.rb
86
- - spec/spec_helper.rb
65
+
66
+ extra_rdoc_files:
67
+ - README.md
68
+ files:
69
+ - .gitignore
70
+ - .rspec
71
+ - Gemfile
72
+ - LICENSE
73
+ - README.md
74
+ - auto.watchr
75
+ - citeproc.gemspec
76
+ - lib/citeproc.rb
77
+ - lib/citeproc/abbreviate.rb
78
+ - lib/citeproc/attributes.rb
79
+ - lib/citeproc/compatibility.rb
80
+ - lib/citeproc/date.rb
81
+ - lib/citeproc/engine.rb
82
+ - lib/citeproc/errors.rb
83
+ - lib/citeproc/extensions.rb
84
+ - lib/citeproc/processor.rb
85
+ - lib/citeproc/utilities.rb
86
+ - lib/citeproc/variable.rb
87
+ - lib/citeproc/version.rb
88
+ - spec/citeproc/abbreviate_spec.rb
89
+ - spec/citeproc/attributes_spec.rb
90
+ - spec/citeproc/date_spec.rb
91
+ - spec/citeproc/engine_spec.rb
92
+ - spec/citeproc/extensions_spec.rb
93
+ - spec/citeproc/processor_spec.rb
94
+ - spec/citeproc/utilities_spec.rb
95
+ - spec/citeproc/variable_spec.rb
96
+ - spec/spec_helper.rb
87
97
  has_rdoc: true
88
98
  homepage: http://inukshuk.github.com/citeproc
89
- licenses:
90
- - FreeBSD
99
+ licenses:
100
+ - FreeBSD
91
101
  post_install_message:
92
- rdoc_options:
93
- - --line-numbers
94
- - --inline-source
95
- - --title
96
- - ! '"CiteProc"'
97
- - --main
98
- - README.md
99
- - --webcvs=http://github.com/inukshuk/citeproc/tree/master/
100
- require_paths:
101
- - lib
102
- required_ruby_version: !ruby/object:Gem::Requirement
102
+ rdoc_options:
103
+ - --line-numbers
104
+ - --inline-source
105
+ - --title
106
+ - "\"CiteProc\""
107
+ - --main
108
+ - README.md
109
+ - --webcvs=http://github.com/inukshuk/citeproc/tree/master/
110
+ require_paths:
111
+ - lib
112
+ required_ruby_version: !ruby/object:Gem::Requirement
103
113
  none: false
104
- requirements:
105
- - - ! '>='
106
- - !ruby/object:Gem::Version
107
- version: '0'
108
- required_rubygems_version: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: "0"
118
+ required_rubygems_version: !ruby/object:Gem::Requirement
109
119
  none: false
110
- requirements:
111
- - - ! '>='
112
- - !ruby/object:Gem::Version
113
- version: '0'
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: "0"
114
124
  requirements: []
125
+
115
126
  rubyforge_project:
116
- rubygems_version: 1.6.2
127
+ rubygems_version: 1.5.1
117
128
  signing_key:
118
129
  specification_version: 3
119
130
  summary: A cite processor interface.
120
- test_files:
121
- - spec/citeproc/abbreviate_spec.rb
122
- - spec/citeproc/engine_spec.rb
123
- - spec/citeproc/extensions_spec.rb
124
- - spec/citeproc/processor_spec.rb
125
- - spec/citeproc/utilities_spec.rb
126
- - spec/spec_helper.rb
131
+ test_files:
132
+ - spec/citeproc/abbreviate_spec.rb
133
+ - spec/citeproc/attributes_spec.rb
134
+ - spec/citeproc/date_spec.rb
135
+ - spec/citeproc/engine_spec.rb
136
+ - spec/citeproc/extensions_spec.rb
137
+ - spec/citeproc/processor_spec.rb
138
+ - spec/citeproc/utilities_spec.rb
139
+ - spec/citeproc/variable_spec.rb
140
+ - spec/spec_helper.rb