bibtex-ruby 1.2.1 → 1.3.0

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.

Potentially problematic release.


This version of bibtex-ruby might be problematic. Click here for more details.

Files changed (67) hide show
  1. data/Gemfile +6 -1
  2. data/Gemfile.lock +48 -5
  3. data/History.txt +16 -1
  4. data/Manifest +43 -19
  5. data/README.md +178 -167
  6. data/Rakefile +26 -5
  7. data/auto.watchr +6 -0
  8. data/bibtex-ruby.gemspec +8 -5
  9. data/examples/bib2html.rb +28 -18
  10. data/features/bibtex.feature +96 -0
  11. data/features/entries.feature +67 -0
  12. data/features/issues/slash_keys.feature +21 -0
  13. data/features/names.feature +72 -0
  14. data/features/preambles.feature +27 -0
  15. data/features/query.feature +56 -0
  16. data/features/replacement.feature +68 -0
  17. data/features/step_definitions/bibtex_steps.rb +74 -0
  18. data/features/step_definitions/name_steps.rb +13 -0
  19. data/features/strings.feature +52 -0
  20. data/features/support/env.rb +7 -0
  21. data/lib/bibtex.rb +5 -1
  22. data/lib/bibtex/bibliography.rb +218 -95
  23. data/lib/bibtex/bibtex.y +18 -15
  24. data/lib/bibtex/elements.rb +130 -136
  25. data/lib/bibtex/entry.rb +133 -69
  26. data/lib/bibtex/extensions.rb +0 -35
  27. data/lib/bibtex/lexer.rb +9 -9
  28. data/lib/bibtex/name_parser.output +464 -0
  29. data/lib/bibtex/name_parser.rb +490 -0
  30. data/lib/bibtex/names.rb +162 -0
  31. data/lib/bibtex/names.y +196 -0
  32. data/lib/bibtex/parser.output +5 -5
  33. data/lib/bibtex/parser.rb +19 -16
  34. data/lib/bibtex/replaceable.rb +52 -0
  35. data/lib/bibtex/utilities.rb +23 -5
  36. data/lib/bibtex/value.rb +201 -0
  37. data/lib/bibtex/version.rb +1 -1
  38. data/test/benchmark.rb +52 -0
  39. data/test/bibtex/test_bibliography.rb +141 -0
  40. data/test/bibtex/test_elements.rb +40 -0
  41. data/test/bibtex/test_entry.rb +99 -0
  42. data/test/bibtex/test_names.rb +23 -0
  43. data/test/bibtex/test_parser.rb +79 -0
  44. data/test/bibtex/test_string.rb +83 -0
  45. data/test/bibtex/test_utilities.rb +34 -0
  46. data/test/bibtex/test_value.rb +70 -0
  47. data/test/{bib/10_bibdesk.bib → fixtures/bibdesk.bib} +1 -1
  48. data/test/{bib/05_comment.bib → fixtures/comment.bib} +0 -0
  49. data/test/{bib/08_decoret.bib → fixtures/decoret.bib} +0 -0
  50. data/test/{bib/00_empty.bib → fixtures/empty.bib} +0 -0
  51. data/test/{bib/07_entry.bib → fixtures/entry.bib} +0 -0
  52. data/test/{bib/09_errors.bib → fixtures/errors.bib} +0 -0
  53. data/test/{bib/01_no_bibtex.bib → fixtures/no_bibtex.bib} +0 -0
  54. data/test/{bib/06_preamble.bib → fixtures/preamble.bib} +1 -1
  55. data/test/{bib/11_roundtrip.bib → fixtures/roundtrip.bib} +1 -1
  56. data/test/helper.rb +17 -2
  57. data/test/test_bibtex.rb +87 -93
  58. data/test/test_export.rb +18 -22
  59. metadata +85 -30
  60. data/test/bib/02_string.bib +0 -1
  61. data/test/bib/03_string.bib +0 -25
  62. data/test/bib/04_string_replacement.bib +0 -16
  63. data/test/test_comment.rb +0 -21
  64. data/test/test_entry.rb +0 -98
  65. data/test/test_preamble.rb +0 -39
  66. data/test/test_string.rb +0 -97
  67. data/test/test_utilities.rb +0 -36
@@ -1,6 +1,6 @@
1
1
  #--
2
2
  # BibTeX-Ruby
3
- # Copyright (C) 2010 Sylvester Keil <http://sylvester.keil.or.at>
3
+ # Copyright (C) 2010-2011 Sylvester Keil <http://sylvester.keil.or.at>
4
4
  #
5
5
  # This program is free software: you can redistribute it and/or modify
6
6
  # it under the terms of the GNU General Public License as published by
@@ -23,21 +23,21 @@
23
23
 
24
24
  class BibTeX::Parser
25
25
 
26
- token AT COMMA COMMENT CONTENT ERROR EQ LBRACE META_COMMENT
26
+ token AT COMMA COMMENT CONTENT ERROR EQ LBRACE META_CONTENT
27
27
  NAME NUMBER PREAMBLE RBRACE SHARP STRING STRING_LITERAL
28
28
 
29
29
  expect 0
30
30
 
31
31
  rule
32
32
 
33
- bibliography : /* empty */ { result = Bibliography.new }
33
+ bibliography : /* empty */ { result = BibTeX::Bibliography.new }
34
34
  | objects { result = val[0] }
35
35
 
36
- objects : object { result = Bibliography.new << val[0] }
36
+ objects : object { result = BibTeX::Bibliography.new << val[0] }
37
37
  | objects object { result << val[1] }
38
38
 
39
39
  object : AT at_object { result = val[1] }
40
- | META_COMMENT { result = BibTeX::MetaComment.new(val[0]) }
40
+ | META_CONTENT { result = BibTeX::MetaContent.new(val[0]) }
41
41
  | ERROR { result = BibTeX::Error.new(val[0]) }
42
42
 
43
43
  at_object : comment { result = val[0] }
@@ -89,32 +89,35 @@ require 'bibtex/lexer'
89
89
 
90
90
  attr_reader :lexer
91
91
 
92
- def initialize(options={})
93
- @options = options
94
- @options[:include] ||= [:errors]
95
- @lexer = Lexer.new(options)
92
+ def initialize(options = {})
93
+ self.options.merge!(options)
94
+ @lexer = Lexer.new(@options)
96
95
  end
97
96
 
97
+ def options
98
+ @options ||= { :include => [:errors], :debug => ENV['DEBUG'] == true }
99
+ end
100
+
98
101
  def parse(input)
99
- @yydebug = self.debug?
102
+ @yydebug = debug?
100
103
 
101
- self.lexer.src = input
102
- self.lexer.analyse
104
+ @lexer.src = input
105
+ @lexer.analyse
103
106
 
104
107
  do_parse
105
108
  end
106
109
 
107
110
  def next_token
108
- token = self.lexer.next_token
111
+ token = @lexer.next_token
109
112
  if token[0] == :ERROR
110
- self.include_errors? ? token : next_token
113
+ include_errors? ? token : next_token
111
114
  else
112
115
  [token[0],token[1][0]]
113
116
  end
114
117
  end
115
118
 
116
119
  def debug?
117
- @options[:debug] == true || ENV['DEBUG'] == true
120
+ @options[:debug] == true
118
121
  end
119
122
 
120
123
  def include_errors?
@@ -1,6 +1,6 @@
1
1
  #--
2
2
  # BibTeX-Ruby
3
- # Copyright (C) 2010 Sylvester Keil <sylvester.keil.or.at>
3
+ # Copyright (C) 2010-2011 Sylvester Keil <sylvester.keil.or.at>
4
4
  #
5
5
  # This program is free software: you can redistribute it and/or modify
6
6
  # it under the terms of the GNU General Public License as published by
@@ -24,45 +24,100 @@ module BibTeX
24
24
  class Element
25
25
  include Comparable
26
26
 
27
+ attr_writer :id
27
28
  attr_reader :bibliography
28
29
 
29
30
  # Returns an array of BibTeX elements.
30
- def self.parse(string, options={})
31
- BibTeX::Parser.new(options).parse(string).to_a
31
+ def self.parse(string, options = {})
32
+ BibTeX::Parser.new(options).parse(string).data
32
33
  end
33
34
 
34
- def initialize
35
- @bibliography = nil
36
- end
37
-
38
35
  # Returns a string containing the object's content.
39
- def content
40
- ""
36
+ def content(options = {})
37
+ ''
41
38
  end
42
39
 
43
- # Returns a string representation of the object.
44
- def to_s
45
- self.content
46
- end
40
+ # Invokes BibTeX string replacement on this element.
41
+ def replace(*arguments); self; end
42
+
43
+ # Invokes BibTeX string joining on this element.
44
+ def join; self; end
45
+
46
+ # Returns the element's id.
47
+ def id; @id ||= object_id.to_s.intern; end
48
+
49
+ # Returns the BibTeX type (if applicable) or the normalized class name.
50
+ def type
51
+ self.class.name.split(/::/).last.gsub(/([[:lower:]])([[:upper:]])/) { "#{$1}_#{$2}" }.downcase.intern
52
+ end
53
+
54
+ def has_type?(type)
55
+ self.type == type.intern || defined?(type) == 'constant' && is_a?(type)
56
+ end
57
+
58
+ [:entry, :book, :article, :collection, :string, :preamble, :comment]. each do |type|
59
+ method_id = "#{type}?"
60
+ unless method_defined?(method_id)
61
+ define_method(method_id) { has_type?(type) }
62
+ alias_method("is_#{method_id}", method_id)
63
+ end
64
+ end
65
+
66
+ # Returns true if the element matches the given query.
67
+ def matches?(query)
68
+ return true if query.nil? || query.respond_to?(:empty?) && query.empty?
69
+
70
+ case query
71
+ when Element
72
+ self == query
73
+ when Symbol
74
+ id == query
75
+ when Regexp
76
+ to_s.match(query)
77
+ when /^\/(.+)\/$/
78
+ to_s.match(Regexp.new($1))
79
+ when /@(\w+)(?:\[([^\]]*)\])?/
80
+ query.scan(/@(\w+)(?:\[([^\]]*)\])?/).any? do |type, condition|
81
+ has_type?(type) && ( condition.nil? || meets?(condition.split(/,\s*/)) )
82
+ end
83
+ else
84
+ id == query.to_sym
85
+ end
86
+ end
87
+
88
+ alias :=== :matches?
89
+ alias :match? :matches?
90
+
91
+ # Returns true if the element meets all of the given conditions.
92
+ def meets?(*conditions)
93
+ conditions.flatten.all? do |condition|
94
+ property, value = condition.split(/\s*=\s*/)
95
+ property.nil? || send(property).to_s == value
96
+ end
97
+ end
98
+
99
+ alias :meet? :meets?
100
+
101
+ alias :to_s :content
47
102
 
48
- def to_hash
49
- { self.class.name.downcase => content }
103
+ def to_hash(options = {})
104
+ { type => content }
50
105
  end
51
106
 
52
- def to_yaml
107
+ def to_yaml(options = {})
53
108
  require 'yaml'
54
- self.to_hash.to_yaml
109
+ to_hash.to_yaml
55
110
  end
56
111
 
57
- def to_json
112
+ def to_json(options = {})
58
113
  require 'json'
59
- self.to_hash.to_json
114
+ to_hash.to_json
60
115
  end
61
116
 
62
- def to_xml
117
+ def to_xml(options = {})
63
118
  require 'rexml/document'
64
- xml = REXML::Element.new(self.class.name.downcase)
65
- xml.text = self.content
119
+ xml = REXML::Element.new(type)
120
+ xml.text = content
66
121
  xml
67
122
  end
68
123
 
@@ -79,7 +134,7 @@ module BibTeX
79
134
  end
80
135
 
81
136
  def <=>(other)
82
- (type_comparison = self.class.name <=> other.class.name) == 0 ? self.to_s <=> other.to_s : type_comparison
137
+ [type, to_s] <=> [other.type, other.to_s]
83
138
  end
84
139
 
85
140
  end
@@ -96,105 +151,70 @@ module BibTeX
96
151
  # of regular entries.
97
152
  #
98
153
  class String < Element
154
+ include Replaceable
155
+
99
156
  attr_reader :key
100
157
 
101
158
  # Creates a new instance.
102
- def initialize(key=nil,value=nil)
103
- self.key = key.to_sym unless key.nil?
104
- self.value = value unless value.nil?
159
+ def initialize(key = nil, value = nil)
160
+ @key, @value = key.to_sym, Value.new(value)
161
+ yield self if block_given?
105
162
  end
106
163
 
107
- # Sets the string's key (i.e., the name of the constant)
164
+ # Sets the string's key (i.e., the symbol identifying the constant).
108
165
  def key=(key)
109
- raise(ArgumentError, "BibTeX::String key must be of type Symbol; was: #{key.class.name}.") unless key.is_a?(Symbol)
110
- @key = key
166
+ raise(ArgumentError, "keys must be convertible to Symbol; was: #{type.class.name}.") unless type.respond_to?(:to_sym)
167
+
168
+ unless @bibliography.nil?
169
+ @bibliography.strings.delete(@key)
170
+ @bibliography.strings[key.to_sym] = self
171
+ end
172
+
173
+ @key = key.to_sym
111
174
  end
112
175
 
113
- # Sets the string's value (i.e., the string literal defined by the constant)
114
- def value=(value)
115
- raise(ArgumentError, "BibTeX::String value must be of type Array, Symbol, or String; was: #{value.class.name}.") unless [Array,::String,Symbol].map { |k| value.is_a?(k) }.inject { |sum,n| sum || n }
116
- @value = Extensions.string_replacement(value.is_a?(Array) ? value : [value])
117
- end
118
-
119
- def value
120
- @value.to_s(:quotes => %w(" "))
176
+ # Retuns the string's value if parameter matches the key; nil otherwise.
177
+ def [](key)
178
+ @key == key ? @value : nil
121
179
  end
122
180
 
123
- # Replaces all constants in this string's value which are defined in +hash+.
124
- # Returns the new value (the @string object itself remains unchanged).
125
- #
126
- # call-seq:
127
- # s.to_s
128
- # => "@string{ foobar = foo # "bar"}"
129
- # s.replace({:foo => 'bar'})
130
- # => "\"bar\" # \"bar\""
131
- # s.to_s
132
- # => "@string{ foobar = foo # "bar"}"
133
- def replace(hash)
134
- @value.replace_strings(hash)
135
- end
136
-
137
- # Replaces all constants in this string's value which are defined in +hsh+.
138
- # Returns the new value (the @string object itself is changed as well).
139
- #
140
- # call-seq:
141
- # s.to_s
142
- # => "@string{ foobar = foo # "bar"}"
143
- # s.replace({:foo => 'bar'})
144
- # => ["bar","bar"]
145
- # s.to_s
146
- # => "@string{ foobar = "bar" # "bar"}"
147
- def replace!(hash)
148
- @value = @value.replace_strings(hash)
149
- @bibliography.strings[@key] = @value unless @bibliography.nil?
150
- end
151
-
152
- def join!
153
- @value = @value.join_strings
154
- @bibliography.strings[@key] = @value unless @bibliography.nil?
155
- end
156
-
157
- # Adds either a string constant or literal to the current value. The
158
- # values will be concatenated using the `#' symbol.
159
- def <<(value)
160
- raise(ArgumentError, "BibTeX::String value can contain only instances of Symbol or String; was: #{value.class.name}.") unless [::String,Symbol].map { |k| value.is_a?(k) }.inject { |sum,n| sum || n }
161
- @value << value
162
- end
163
181
 
164
182
  # Called when the element was added to a bibliography.
165
183
  def added_to_bibliography(bibliography)
166
- super(bibliography)
167
- bibliography.strings[@key] = @value
184
+ super
185
+ bibliography.strings[@key] = self
168
186
  self
169
187
  end
170
188
 
171
189
  # Called when the element was removed from a bibliography.
172
190
  def removed_from_bibliography(bibliography)
173
- super(bibliography)
191
+ super
174
192
  bibliography.strings[@key] = nil
175
193
  self
176
194
  end
177
195
 
178
196
  # Returns a string representation of the @string's content.
179
197
  def content
180
- [@key.to_s, value].join(' = ')
198
+ "#@key = #{@value.to_s(:quotes => '"')}"
181
199
  end
182
200
 
183
201
  # Returns a string representation of the @string object.
184
- def to_s
202
+ def to_s(options = {})
185
203
  "@string{ #{content} }"
186
204
  end
187
205
 
188
- def to_hash
189
- { 'string' => { @key.to_s => @value.to_s(:quotes => %w(" ")) } }
206
+ def to_hash(options = {})
207
+ { :string => { @key => @value.to_s(:quotes => '"') } }
190
208
  end
191
209
 
192
- def to_xml
193
- xml = REXML::Element.new('string')
194
- key = REXML::Element.new('key')
195
- val = REXML::Element.new('value')
210
+ def to_xml(options = {})
211
+ require 'rexml/document'
212
+
213
+ xml = REXML::Element.new(:string)
214
+ key = REXML::Element.new(:key)
215
+ val = REXML::Element.new(:value)
196
216
  key.text = @key.to_s
197
- val.text = @value.to_s(:quotes => %w(" "))
217
+ val.text = @value.to_s(:quotes => '"')
198
218
  xml
199
219
  end
200
220
  end
@@ -206,79 +226,53 @@ module BibTeX
206
226
  # a single constant, or a concatenation of string literals and
207
227
  # constants.
208
228
  class Preamble < Element
209
-
210
- # Creates a new instance.
211
- def initialize(value=[])
212
- self.value = value
213
- end
214
-
215
- def value=(value)
216
- raise(ArgumentError, "BibTeX::Preamble value must be of type Array, Symbol, or String; was: #{value.class.name}.") unless [Array,::String,Symbol].map { |k| value.is_a?(k) }.inject { |sum,n| sum || n }
217
- @value = Extensions.string_replacement(value.is_a?(Array) ? value : [value])
218
- end
229
+ include Replaceable
219
230
 
220
- def replace(hash)
221
- @value.replace_strings(hash)
222
- end
223
-
224
- def replace!(hash)
225
- @value = @value.replace_strings(hash)
231
+ # Creates a new instance.
232
+ def initialize(value = '')
233
+ @value = Value.new(value)
226
234
  end
227
-
228
- def join!
229
- @value = @value.join_strings
230
- end
231
-
232
- def value
233
- content
234
- end
235
235
 
236
236
  # Returns a string representation of the @preamble's content.
237
237
  def content
238
- @value.to_s(:quotes => %w(" "))
238
+ @value.to_s(:quotes => '"')
239
239
  end
240
240
 
241
241
  # Returns a string representation of the @preamble object
242
- def to_s
243
- "@preamble{ #{ content } }"
242
+ def to_s(options = {})
243
+ "@preamble{ #{content} }"
244
244
  end
245
-
246
245
  end
247
246
 
248
247
  # Represents a @comment object.
249
248
  class Comment < Element
250
-
251
- def initialize(content='')
252
- self.content = content
253
- end
254
-
255
- def content=(content)
256
- raise(ArgumentError, "BibTeX::#{self.class.name} content must be of type String; was: #{content.class.name}.") unless content.is_a?(::String)
249
+ attr_accessor :content
250
+
251
+ def initialize(content = '')
257
252
  @content = content
258
253
  end
259
254
 
260
- def content
261
- @content
255
+ def to_s(options = {})
256
+ "@comment{ #@content }"
262
257
  end
263
-
264
- def to_s
265
- ['@comment{ ',content,'}'].join
266
- end
267
-
268
258
  end
269
259
 
270
260
  # Represents text in a `.bib' file, but outside of an
271
261
  # actual BibTeX object; typically, such text is treated
272
262
  # as a comment and is ignored by the parser.
273
263
  # BibTeX-Ruby offers this class to allows for
274
- # post-processing of this type of `meta' comment. If you
264
+ # post-processing of this type of `meta' content. If you
275
265
  # want the parser to include +MetaComment+ objects, you
276
- # need to add +:meta_comments+ to the parser's +:include+
266
+ # need to add +:meta_content+ to the parser's +:include+
277
267
  # option.
278
- class MetaComment < Comment
279
- def to_s
280
- @content
268
+ class MetaContent < Element
269
+ attr_accessor :content
270
+
271
+ def initialize(content = '')
272
+ @content = content
281
273
  end
274
+
275
+ def to_s(options = {}); @content; end
282
276
  end
283
277
 
284
278
  end