citeproc 0.0.2 → 0.0.3

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/.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