citeproc 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/.rspec CHANGED
@@ -1,2 +1,3 @@
1
1
  --color
2
+ --require spec_helper
2
3
 
data/README.md CHANGED
@@ -1,2 +1,4 @@
1
1
  CiteProc
2
2
  ========
3
+
4
+ The CiteProc gem is a Ruby abstraction layer for the JSON API used by citeproc-js. To process citations you also require a dedicate processor engine available as a separate gem: these will include citeproc-js, citeproc-ruby and eventually citeproc-hs. As work on these packages is still in progress, please be aware of the following caveats: the 0.0.2 release of citeproc-ruby is still a standalone package and will conflict with this release of citeproc; support will be added in future versions. The citeproc-js gem currently works only in JRuby (using an embedded version of the Rhino JavaScript interpreter) but is going to support other versions of Ruby using different JavaScript runtimes.
data/auto.watchr CHANGED
@@ -18,3 +18,5 @@ def run (cmd)
18
18
  puts cmd
19
19
  system cmd
20
20
  end
21
+
22
+ rspec 'spec'
data/lib/citeproc.rb CHANGED
@@ -12,8 +12,24 @@ require 'citeproc/errors'
12
12
 
13
13
  require 'citeproc/abbreviate'
14
14
  require 'citeproc/attributes'
15
+
15
16
  require 'citeproc/variable'
16
17
  require 'citeproc/date'
18
+ require 'citeproc/names'
19
+
20
+ CiteProc::Variable.class_eval do
21
+ @factories = Hash.new { |h,k| h.fetch(k.to_sym, CiteProc::Variable) }.merge(
22
+ Hash[*@types.map { |n,k|
23
+ [n, CiteProc.const_get(k.to_s.capitalize)]
24
+ }.flatten]
25
+ ).freeze
26
+ end
27
+
28
+ require 'citeproc/item'
29
+ require 'citeproc/citation_data'
30
+ require 'citeproc/selector'
31
+ require 'citeproc/bibliography'
32
+ require 'citeproc/assets'
17
33
 
18
34
  require 'citeproc/engine'
19
35
  require 'citeproc/processor'
@@ -0,0 +1,66 @@
1
+ require 'uri'
2
+
3
+ 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
+
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
36
+
37
+ def read(name)
38
+ io = open(name, 'r:UTF-8')
39
+ io.read
40
+ ensure
41
+ io.close
42
+ end
43
+
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
+ end
@@ -1,9 +1,16 @@
1
1
 
2
2
  module CiteProc
3
3
 
4
+ # TODO refactor using a Struct instead of a hash. This will have to convert
5
+ # the CiteProc/CSL names which are no proper method names.
6
+
7
+
4
8
  module Attributes
5
9
  extend Forwardable
6
10
 
11
+
12
+ FALSE_PATTERN = (/^(false|no|never)$/i).freeze
13
+
7
14
  def self.included(base)
8
15
  base.extend(ClassMethods)
9
16
  end
@@ -12,37 +19,105 @@ module CiteProc
12
19
  @attributes ||= {}
13
20
  end
14
21
 
22
+ def_delegators :attributes, :length, :empty?
23
+
24
+ def [](key)
25
+ attributes[filter_key(key)]
26
+ end
27
+
28
+ def []=(key, value)
29
+ attributes[filter_key(key)] = filter_value(value)
30
+ end
31
+
32
+ def filter_key(key)
33
+ key.to_sym
34
+ end
35
+
36
+ def filter_value(value, key = nil)
37
+ value.respond_to?(:deep_copy) ? value.deep_copy : value.dup
38
+ rescue
39
+ value
40
+ end
41
+
15
42
  def merge(other)
16
43
  return self if other.nil?
17
44
 
18
45
  case other
19
46
  when String, /^\s*\{/
20
- other = MulitJson.encode(other, :symbolize_keys => true)
47
+ other = MulitJson.decode(other, :symbolize_keys => true)
21
48
  when Hash
22
- other = other.deep_copy
23
- else
49
+ # do nothing
50
+ when Attributes
24
51
  other = other.to_hash
52
+ else
53
+ raise ParseError, "failed to merge attributes and #{other.inspect}"
25
54
  end
26
55
 
27
- other.each_pair { |k,v| attributes[k.to_sym] = v }
56
+ other.each_pair do |key, value|
57
+ attributes[filter_key(key)] = filter_value(value, key)
58
+ end
28
59
 
29
60
  self
30
61
  end
31
62
 
32
63
  alias update merge
33
64
 
34
- def reverse_merge(other)
35
- other.merge(self)
36
- end
65
+ def reverse_merge(other)
66
+ fail "not implemented yet"
67
+ end
68
+
69
+ def to_hash
70
+ attributes.deep_copy
71
+ end
72
+
73
+ def to_citeproc
74
+ Hash[*attributes.map { |k,v|
75
+ [k.to_s, v.respond_to?(:to_citeproc) ? v.to_citeproc : v.to_s]
76
+ }.flatten(1)]
77
+ end
78
+
79
+ def to_json
80
+ MultiJson.encode(to_citeproc)
81
+ end
37
82
 
38
- alias to_hash attributes
83
+ # Don't expose internals to public API
84
+ private :filter_key, :filter_value
85
+
86
+ # initialize_copy should be able to access attributes
87
+ protected :attributes
88
+
39
89
 
90
+
91
+ # def eql?(other)
92
+ # case
93
+ # when equal?(other)
94
+ # true
95
+ # when self.class != other.class, length != other.length
96
+ # false
97
+ # else
98
+ # other.attributes.each_pair do |key, value|
99
+ # return false unless attributes[key].eql?(value)
100
+ # end
101
+ #
102
+ # true
103
+ # end
104
+ # end
105
+ #
106
+ # def hash
107
+ # end
108
+
40
109
  module ClassMethods
41
110
 
42
- def create(parameters)
43
- new.merge(parameters)
44
- end
45
-
111
+ def create(parameters)
112
+ create!(parameters)
113
+ rescue
114
+ nil
115
+ end
116
+
117
+ def create!(parameters)
118
+ new.merge(parameters)
119
+ end
120
+
46
121
  def attr_predicates(*arguments)
47
122
  arguments.flatten.each do |field|
48
123
  field, default = *(field.is_a?(Hash) ? field.to_a.flatten : [field]).map(&:to_s)
@@ -50,14 +125,14 @@ module CiteProc
50
125
  end
51
126
  end
52
127
 
53
- def attr_fields
128
+ def attr_fields(*arguments)
54
129
  arguments.flatten.each do |field|
55
130
  attr_field(*(field.is_a?(Hash) ? field.to_a.flatten : [field]).map(&:to_s))
56
131
  end
57
132
  end
58
133
 
59
134
  def attr_field(field, default = nil, predicate = false)
60
- method_id = field.downcase.gsub(/[-\s]+/, '_')
135
+ method_id = field.to_s.downcase.gsub(/[-\s]+/, '_')
61
136
 
62
137
  unless instance_methods.include?(method_id)
63
138
  if default
@@ -77,11 +152,12 @@ module CiteProc
77
152
  attributes[field.to_sym] = value
78
153
  end
79
154
  end
80
-
155
+
81
156
  predicate_id = [method_id, '?'].join
82
157
  if predicate && !instance_methods.include?(predicate_id)
83
158
  define_method(predicate_id) do
84
- ![nil, false, '', [], 'false', 'no', 'never'].include?(attributes[field.to_sym])
159
+ v = attributes[field.to_sym]
160
+ !(v.nil? || (v.respond_to?(:empty?) && v.empty?) || v =~ FALSE_PATTERN)
85
161
  end
86
162
 
87
163
  has_predicate = ['has_', predicate_id].join
@@ -0,0 +1,180 @@
1
+
2
+ module CiteProc
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
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
85
+
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
108
+
109
+ attr_reader :options, :errors, :references
110
+
111
+ attr_accessor :preamble, :postamble
112
+
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
130
+
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
150
+
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
+
180
+ end