citeproc 0.0.3 → 0.0.6

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/lib/citeproc.rb CHANGED
@@ -1,10 +1,26 @@
1
1
 
2
2
  require 'multi_json'
3
3
  require 'forwardable'
4
+ require 'observer'
5
+ require 'uri'
4
6
 
7
+ if ENV['DEBUG']
8
+ require 'ruby-debug'
9
+ Debugger.start
10
+ end
5
11
 
6
12
  require 'citeproc/version'
7
13
 
14
+ #
15
+ # CiteProc processes bibliographic data and formats it according to a style
16
+ # defined in CSL (Citation Style Language).
17
+ #
18
+ module CiteProc
19
+
20
+ module Converters; end
21
+
22
+ end
23
+
8
24
  require 'citeproc/compatibility'
9
25
  require 'citeproc/extensions'
10
26
 
@@ -36,3 +52,21 @@ require 'citeproc/processor'
36
52
  require 'citeproc/utilities'
37
53
 
38
54
  CiteProc.extend CiteProc::Utilities
55
+
56
+
57
+ CiteProc::Converters.class_eval do
58
+
59
+ # Define all converters (all classes have been loaded at this point)
60
+ CiteProc.constants.each do |name|
61
+ klass = CiteProc.const_get(name)
62
+
63
+ if klass.instance_of?(Class) && klass.respond_to?(:create)
64
+
65
+ define_method(name) do |obj|
66
+ obj.instance_of?(klass) ? obj : klass.create(obj)
67
+ end
68
+
69
+ end
70
+ end
71
+
72
+ end
@@ -1,30 +1,34 @@
1
1
  require 'multi_json'
2
2
 
3
3
  module CiteProc
4
- module Abbreviate
5
-
6
- attr_reader :abbreviations
7
- attr_accessor :default_namespace
8
-
9
- def abbreviations=(abbreviations)
10
- @abbreviations = case abbreviations
11
- when ::String
12
- MultiJson.decode(abbreviations, :symbolize_keys => true)
13
- when ::Hash
14
- abbreviations.deep_copy
15
- else raise ArgumentError, "failed to set abbreviations from #{abbreviations.inspect}"
16
- end
17
- end
18
-
19
- # call-seq:
20
- # abbreviate(namespace = :default, context, word)
21
- def abbreviate(*arguments)
22
- raise ArgumentError, "wrong number of arguments (#{arguments.length} for 2..3)" unless (2..3).include?(arguments.length)
23
- arguments.unshift(default_namespace || :default) if arguments.length < 3
24
- @abbreviations.deep_fetch(*arguments)
25
- end
26
-
27
- alias abbrev abbreviate
4
+ module Abbreviate
28
5
 
29
- end
6
+ attr_accessor :namespace
7
+
8
+ def abbreviations
9
+ @abbreviations ||= { :default => {} }
10
+ end
11
+
12
+ def abbreviations=(abbreviations)
13
+ @abbreviations = case abbreviations
14
+ when ::String
15
+ MultiJson.decode(abbreviations, :symbolize_keys => true)
16
+ when ::Hash
17
+ abbreviations.deep_copy
18
+ else
19
+ raise ArgumentError, "failed to set abbreviations from #{abbreviations.inspect}"
20
+ end
21
+ end
22
+
23
+ # call-seq:
24
+ # abbreviate(namespace = :default, context, word)
25
+ def abbreviate(*arguments)
26
+ raise ArgumentError, "wrong number of arguments (#{arguments.length} for 2..3)" unless (2..3).include?(arguments.length)
27
+ arguments.unshift(namespace || :default) if arguments.length < 3
28
+ @abbreviations.deep_fetch(*arguments)
29
+ end
30
+
31
+ alias abbrev abbreviate
32
+
33
+ end
30
34
  end
@@ -1,66 +1,106 @@
1
- require 'uri'
2
-
3
1
  module CiteProc
4
- module Asset
5
-
6
- def self.included(base)
7
- base.extend(ClassMethods)
8
- end
9
-
10
- attr_accessor :asset
11
-
12
- alias to_s asset
13
2
 
14
- def inspect
15
- to_s.inspect
16
- end
17
-
18
- module ClassMethods
19
-
20
- attr_accessor :root, :extension, :prefix
21
-
22
- def load(asset)
23
- instance = new
24
- case
25
- when File.exists?(asset)
26
- instance.asset = read(asset)
27
- when File.exists?(File.join(root.to_s, extend_name(asset)))
28
- instance.asset = read(File.join(root.to_s, extend_name(asset)))
29
- else
30
- instance.asset = asset
31
- end
32
- instance
33
- end
34
-
35
- private
3
+ module Asset
4
+
5
+ def self.included(base)
6
+ base.extend(ClassMethods)
7
+ end
8
+
9
+ attr_reader :asset, :location
10
+
11
+ def open?
12
+ !asset.nil?
13
+ end
14
+
15
+ def open(input)
16
+ case
17
+ when input.respond_to?(:read)
18
+ @location = nil
19
+ @asset = input.read
20
+ when input.to_s =~ /^\s*</
21
+ @location = nil
22
+ @asset = input.to_s.dup
23
+ else
24
+ case
25
+ when File.exists?(input)
26
+ @location = input
27
+ when File.exists?(self.class.extend_name(input))
28
+ @location = self.class.extend_name(input)
29
+ when File.exists?(self.class.extend_path(input))
30
+ @location = self.class.extend_path(input)
31
+ else
32
+ @location = input
33
+ end
34
+
35
+ Kernel.open(@location, 'r:UTF-8') do |io|
36
+ @asset = io.read
37
+ end
38
+ end
39
+
40
+ self
41
+ rescue => e
42
+ puts e.backtrace.join("\n")
43
+ raise ArgumentError, "failed to open asset #{input.inspect}: #{e.message}"
44
+ end
45
+
46
+ def name
47
+ File.basename(location, self.class.extension).sub(Regexp.new("^#{self.class.prefix}"), '')
48
+ end
49
+
50
+ alias to_s asset
51
+
52
+ def inspect
53
+ "#<CiteProc::#{self.class.name} #{name}>"
54
+ end
55
+
56
+ module ClassMethods
57
+
58
+ attr_accessor :root, :extension, :prefix
59
+
60
+ def open(path_or_name)
61
+ new.open(path_or_name)
62
+ end
63
+
64
+ def extend_path(input)
65
+ File.join(root.to_s, extend_name(input))
66
+ end
67
+
68
+ def extend_name(input)
69
+ name = File.extname(input).empty? ? [input, extension].compact.join : input.to_s.dup
70
+ name = name.start_with?(prefix.to_s) ? name : [prefix, name].join
71
+ name
72
+ end
73
+
74
+ end
75
+
76
+ end
77
+
78
+ class Style
79
+
80
+ include Asset
81
+
82
+ @root = '/usr/local/share/citation-style-language/styles'.freeze
83
+ @extension = '.csl'.freeze
84
+
85
+ end
36
86
 
37
- def read(name)
38
- io = open(name, 'r:UTF-8')
39
- io.read
40
- ensure
41
- io.close
42
- end
87
+ class Locale
88
+
89
+ include Asset
90
+
91
+ @root = '/usr/local/share/citation-style-language/locales'.freeze
92
+ @extension = '.xml'.freeze
93
+ @prefix = 'locales-'.freeze
94
+
95
+
96
+ def language
97
+ name.split('-')[0]
98
+ end
99
+
100
+ def region
101
+ name.split('-')[1]
102
+ end
103
+
104
+ end
43
105
 
44
- def extend_name(file)
45
- file = File.extname(file).empty? ? [file, extension].compact.join : file
46
- file = file.start_with?(prefix.to_s) ? file : [prefix,file].join
47
- file
48
- end
49
- end
50
-
51
- end
52
-
53
- class Style
54
- include Asset
55
- @root = '/usr/local/share/citation-style-language/styles'.freeze
56
- @extension = '.csl'.freeze
57
- end
58
-
59
- class Locale
60
- include Asset
61
- @root = '/usr/local/share/citation-style-language/locales'.freeze
62
- @extension = '.xml'.freeze
63
- @prefix = 'locales-'
64
- end
65
-
66
106
  end
@@ -1,180 +1,199 @@
1
1
 
2
2
  module CiteProc
3
3
 
4
- # = Bibliography
5
- #
6
- # Typically a Bibliography is returned by the Processor. It contains a list
7
- # of references and formatting options.
8
- #
9
- # A Bibliography can be created from (and exported to) CiteProc JSON
10
- # bibliographies; these consist of a two-element list, composed of a
11
- # JavaScript array containing certain formatting parameters, and a list of
12
- # strings representing bibliography entries.
13
- #
14
- # == Bibliography Formatting Options
15
- #
16
- # :offset => 0
17
- # Some citation styles apply a label (either a number or an alphanumeric
18
- # code) to each bibliography entry, and use this label to cite
19
- # bibliography items in the main text. In the bibliography, the labels
20
- # may either be hung in the margin, or they may be set flush to the
21
- # margin, with the citations indented by a uniform amount to the right.
22
- # In the latter case, the amount of indentation needed depends on the
23
- # maximum width of any label. The :offset option gives the maximum
24
- # number of characters that appear in any label used in the
25
- # bibliography. The client that controls the final rendering of the
26
- # bibliography string should use this value to calculate and apply a
27
- # suitable indentation length.
28
- #
29
- # :entry_spacing => 0
30
- # An integer representing the spacing between entries in the
31
- # bibliography.
32
- #
33
- # :line_spacing => 0
34
- # An integer representing the spacing between the lines within each
35
- # bibliography entry.
36
- #
37
- # :indent => 0
38
- # The number of em-spaces to apply in hanging indents within the
39
- # bibliography.
40
- #
41
- # :align => false
42
- # When the second-field-align CSL option is set, this returns either
43
- # "flush" or "margin". The calling application should align text in
44
- # bibliography output as described in the CSL specification. Where
45
- # second-field-align is not set, this return value is set to false.
46
- #
47
- class Bibliography
4
+ # = Bibliography
5
+ #
6
+ # Typically a Bibliography is returned by the Processor. It contains a list
7
+ # of references and formatting options.
8
+ #
9
+ # A Bibliography can be created from (and exported to) CiteProc JSON
10
+ # bibliographies; these consist of a two-element list, composed of a
11
+ # JavaScript array containing certain formatting parameters, and a list of
12
+ # strings representing bibliography entries.
13
+ #
14
+ # == Bibliography Formatting Options
15
+ #
16
+ # :offset => 0
17
+ # Some citation styles apply a label (either a number or an alphanumeric
18
+ # code) to each bibliography entry, and use this label to cite
19
+ # bibliography items in the main text. In the bibliography, the labels
20
+ # may either be hung in the margin, or they may be set flush to the
21
+ # margin, with the citations indented by a uniform amount to the right.
22
+ # In the latter case, the amount of indentation needed depends on the
23
+ # maximum width of any label. The :offset option gives the maximum
24
+ # number of characters that appear in any label used in the
25
+ # bibliography. The client that controls the final rendering of the
26
+ # bibliography string should use this value to calculate and apply a
27
+ # suitable indentation length.
28
+ #
29
+ # :entry_spacing => 0
30
+ # An integer representing the spacing between entries in the
31
+ # bibliography.
32
+ #
33
+ # :line_spacing => 0
34
+ # An integer representing the spacing between the lines within each
35
+ # bibliography entry.
36
+ #
37
+ # :indent => 0
38
+ # The number of em-spaces to apply in hanging indents within the
39
+ # bibliography.
40
+ #
41
+ # :align => false
42
+ # When the second-field-align CSL option is set, this returns either
43
+ # "flush" or "margin". The calling application should align text in
44
+ # bibliography output as described in the CSL specification. Where
45
+ # second-field-align is not set, this return value is set to false.
46
+ #
47
+ class Bibliography
48
48
 
49
- @defaults = {
50
- :offset => 0,
51
- :entry_spacing => 0,
52
- :line_spacing => 0,
53
- :indent => 0,
54
- :align => false
55
- }.freeze
56
-
57
-
58
- # citeproc-js and csl attributes are often inconsistent or difficult
59
- # to use as symbols/method names, so we're using different names at the
60
- # cost of making conversion to and from the json format more difficult
61
-
62
- @cp2rb = {
63
- 'maxoffset' => :offset,
64
- 'entryspacing' => :entry_spacing,
65
- 'linespacing' => :line_spacing,
66
- 'hangingindent' => :indent,
67
- 'second-field-align' => :align
68
- }
69
-
70
- @rb2cp = @cp2rb.invert.freeze
71
- @cp2rb.freeze
72
-
73
- class << self
74
-
75
- attr_reader :defaults, :cp2rb, :rb2cp
76
-
77
- # Create a new Bibliography from the passed-in string or array.
78
- def parse(input)
79
- case
80
- when input.is_a?(String)
81
- parse(MultiJson.decode(input))
82
-
83
- when input.is_a?(Array) && input.length == 2
84
- options, references = input
49
+ @defaults = {
50
+ :offset => 0,
51
+ :entry_spacing => 0,
52
+ :line_spacing => 0,
53
+ :indent => 0,
54
+ :align => false
55
+ }.freeze
56
+
57
+
58
+ # citeproc-js and csl attributes are often inconsistent or difficult
59
+ # to use as symbols/method names, so we're using different names at the
60
+ # cost of making conversion to and from the json format more difficult
61
+
62
+ @cp2rb = {
63
+ 'maxoffset' => :offset,
64
+ 'entryspacing' => :entry_spacing,
65
+ 'linespacing' => :line_spacing,
66
+ 'hangingindent' => :indent,
67
+ 'second-field-align' => :align
68
+ }
69
+
70
+ @rb2cp = @cp2rb.invert.freeze
71
+ @cp2rb.freeze
72
+
73
+ class << self
74
+
75
+ attr_reader :defaults, :cp2rb, :rb2cp
76
+
77
+ # Create a new Bibliography from the passed-in string or array, or nil
78
+ # if the input cannot be parsed.
79
+ def create(input)
80
+ create!(input)
81
+ rescue
82
+ nil
83
+ end
84
+
85
+ # Create a new Bibliography from the passed-in string or array. Raises
86
+ # ParseError if the input cannot be parsed.
87
+ def create!(input)
88
+ case
89
+ when input.is_a?(String)
90
+ create!(MultiJson.decode(input))
91
+
92
+ when input.is_a?(Array) && input.length == 2
93
+ options, references = input
85
94
 
86
- new do |b|
87
- b.concat(references)
88
- b.errors.concat(options.fetch('bibliography_errors', []))
89
-
90
- b.preamble, b.postamble = options['bibstart'], options['bibend']
91
-
92
- (options.keys & cp2rb.keys).each do |k|
93
- b.options[cp2rb[k]] = options[k]
94
- end
95
- end
96
-
97
- else
98
- raise ParseError, "failed to create Bibliography from #{input.inspect}"
99
- end
100
- end
101
-
102
- end
103
-
104
- include Comparable
105
- include Enumerable
106
-
107
- extend Forwardable
95
+ new do |b|
96
+ b.concat(references)
97
+ b.errors.concat(options.fetch('bibliography_errors', []))
98
+
99
+ b.prefix, b.suffix = options['bibstart'], options['bibend']
100
+
101
+ (options.keys & cp2rb.keys).each do |k|
102
+ b.options[cp2rb[k]] = options[k]
103
+ end
104
+ end
105
+
106
+ else
107
+ raise ParseError, "failed to create Bibliography from #{input.inspect}"
108
+ end
109
+ end
110
+
111
+ end
112
+
113
+ include Comparable
114
+ include Enumerable
115
+
116
+ extend Forwardable
108
117
 
109
- attr_reader :options, :errors, :references
118
+ attr_reader :options, :errors, :references
110
119
 
111
- attr_accessor :preamble, :postamble
120
+ attr_accessor :prefix, :suffix
112
121
 
113
- alias before preamble
114
- alias after postamble
115
-
116
- def_delegators :@references, :[], :[]=, :<<, :push, :unshift, :pop,
117
- :concat, :include?, :index, :length, :empty?
118
-
119
- def initialize(options = {})
120
- @options = Bibliography.defaults.merge(options)
121
- @errors, @references = [], []
122
-
123
- yield self if block_given?
124
- end
125
-
126
- def initialize_copy(other)
127
- @options = other.options.dup
128
- @errors, @references = other.errors.dup, other.references.dup
129
- end
122
+ # Bibliographies quack sorta like an Array
123
+ def_delegators :@references, :length, :empty?, :[], :include?, :index
130
124
 
131
- def has_errors?
132
- !errors.empty?
133
- end
134
-
135
- alias errors? has_errors?
136
-
137
- def each
138
- if block_given?
139
- references.each(&Proc.new)
140
- self
141
- else
142
- to_enum
143
- end
144
- end
145
-
146
- def <=>(other)
147
- return nil unless other.respond_to?(:references)
148
- references <=> other.references
149
- end
125
+ # Some delegators should return self
126
+ [:push, :<<, :unshift, :concat].each do |m|
127
+ define_method(m) do |*arguments, &block|
128
+ references.send(m, *arguments, &block)
129
+ self
130
+ end
131
+ end
132
+
133
+
134
+ def initialize(options = {})
135
+ @options = Bibliography.defaults.merge(options)
136
+ @errors, @references = [], []
137
+
138
+ yield self if block_given?
139
+ end
140
+
141
+ def initialize_copy(other)
142
+ @options = other.options.dup
143
+ @errors, @references = other.errors.dup, other.references.dup
144
+ end
150
145
 
151
- def citeproc_options
152
- Hash[*options.map { |k,v|
153
- [Bibliography.rb2cp[k] || k.to_s, v]
154
- }.flatten]
155
- end
156
-
157
- def to_citeproc
158
- [
159
- citeproc_options.merge({
160
- 'bibstart' => preamble,
161
- 'bibend' => postamble,
162
- 'bibliography_errors' => errors
163
- }),
164
-
165
- references
166
-
167
- ]
168
- end
169
-
170
- def to_json
171
- MultiJson.encode(to_citeproc)
172
- end
173
-
174
- def inspect
175
- "#<CiteProc::Bibliography @references=[#{references.length}], @errors=[#{errors.length}]>"
176
- end
177
-
178
- end
179
-
146
+ def has_errors?
147
+ !errors.empty?
148
+ end
149
+
150
+ alias errors? has_errors?
151
+
152
+ def join(connector = "\n")
153
+ [prefix, references.join(connector), suffix].join
154
+ end
155
+
156
+ def each
157
+ if block_given?
158
+ references.each(&Proc.new)
159
+ self
160
+ else
161
+ to_enum
162
+ end
163
+ end
164
+
165
+ def <=>(other)
166
+ return nil unless other.respond_to?(:references)
167
+ references <=> other.references
168
+ end
169
+
170
+ def citeproc_options
171
+ Hash[*options.map { |k,v|
172
+ [Bibliography.rb2cp[k] || k.to_s, v]
173
+ }.flatten]
174
+ end
175
+
176
+ def to_citeproc
177
+ [
178
+ citeproc_options.merge({
179
+ 'bibstart' => prefix,
180
+ 'bibend' => suffix,
181
+ 'bibliography_errors' => errors
182
+ }),
183
+
184
+ references
185
+
186
+ ]
187
+ end
188
+
189
+ def to_json
190
+ MultiJson.encode(to_citeproc)
191
+ end
192
+
193
+ def inspect
194
+ "#<CiteProc::Bibliography @references=[#{references.length}], @errors=[#{errors.length}]>"
195
+ end
196
+
197
+ end
198
+
180
199
  end