citeproc 0.0.3 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
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