bibtex-ruby 1.2.1 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.

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
@@ -0,0 +1,52 @@
1
+ #--
2
+ # BibTeX-Ruby
3
+ # Copyright (C) 2010 Sylvester Keil <sylvester.keil.or.at>
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ #++
18
+
19
+ module BibTeX
20
+
21
+ #
22
+ # The Replaceable module provides methods that expose a Value attribute
23
+ # and the ability to join or replace the contained BibTeX symbols.
24
+ #
25
+ module Replaceable
26
+ extend Forwardable
27
+
28
+ attr_reader :value
29
+
30
+ def value=(value)
31
+ @value = Value.new(value)
32
+ end
33
+
34
+ def replace(*arguments)
35
+ @value.replace(*arguments)
36
+ self
37
+ end
38
+
39
+ def join
40
+ @value.join
41
+ self
42
+ end
43
+
44
+ def <<(value)
45
+ @value << value
46
+ self
47
+ end
48
+
49
+ alias :v :value
50
+
51
+ end
52
+ end
@@ -19,20 +19,38 @@
19
19
  module BibTeX
20
20
 
21
21
  class << self
22
- # Opens a BibTeX file and returns a corresponding +Bibliography+ object.
23
- def open(file, options={})
24
- Bibliography.open(file, options)
22
+
23
+ # Opens a BibTeX file or URI and returns a corresponding +Bibliography+
24
+ # object or, if a block is given, yields the Bibliography to the block,
25
+ # ensuring that the file is saved.
26
+ def open(file, options = {}, &block)
27
+ Bibliography.open(file, options, &block)
25
28
  end
26
29
 
27
30
  # Parses the given string and returns a corresponding +Bibliography+ object.
28
- def parse(string, options={})
29
- BibTeX::Parser.new(options).parse(string)
31
+ # Delegates to BibTeX.open if the string is a filename or URI.
32
+ def parse(string, options = {}, &block)
33
+ if File.exists?(string) || string =~ /^[a-z]+:\/\//i
34
+ open(string, options, &block)
35
+ else
36
+ Bibliography.parse(string, options)
37
+ end
30
38
  end
31
39
 
32
40
  # Returns true if the given file is a valid BibTeX bibliography.
33
41
  def valid?(file)
34
42
  Bibliography.open(file).valid?
35
43
  end
44
+
45
+ # Parses the given string as a BibTeX name value and returns a Names object.
46
+ def names(string)
47
+ Names.parse(string)
48
+ end
49
+
50
+ alias :name :names
51
+ alias :parse_name :names
52
+ alias :parse_names :names
53
+
36
54
  end
37
55
 
38
56
  end
@@ -0,0 +1,201 @@
1
+ #--
2
+ # BibTeX-Ruby
3
+ # Copyright (C) 2010-2011 Sylvester Keil <sylvester.keil.or.at>
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ #++
18
+
19
+ require 'forwardable'
20
+
21
+ module BibTeX
22
+
23
+ #
24
+ # A BibTeX Value is something very much like a string. In BibTeX files it
25
+ # can appear on the right hand side of @string or @entry field assignments
26
+ # or as @preamble contents. In the example below [VALUE] indicates possible
27
+ # occurences of values in BibTeX:
28
+ #
29
+ # @preamble{ "foo" [VALUE] }
30
+ # @string{ foo = "foo" [VALUE] }
31
+ # @book{id,
32
+ # author = {John Doe} [VALUE],
33
+ # title = foo # "bar" [VALUE]
34
+ # }
35
+ #
36
+ # All Values have in common that they can be simple strings in curly braces
37
+ # or double quotes or complex BibTeX string-concatenations (using the '#'
38
+ # symbol).
39
+ #
40
+ # Generally, Values try to behave as much as normal Ruby strings as possible;
41
+ # If you do not require any of the advanced BibTeX functionality (string
42
+ # replacement or concatentaion) you can simply convert them to strings using
43
+ # +to_s+. Note that BibTeX Names are special instances of Values which
44
+ # currently do not support string concatenation or replacement.
45
+ #
46
+ class Value
47
+ extend Forwardable
48
+ include Comparable
49
+
50
+ attr_reader :tokens
51
+ alias :to_a :tokens
52
+
53
+ def_delegators :to_s, :empty?, :=~, :match, :intern, :to_sym, :to_i, :to_f, :end_with?, :start_with?, :include?, :upcase, :downcase, :reverse, :chop, :chomp, :rstrip, :gsub, :sub, :size, :strip, :succ, :to_c, :to_r, :to_str, :split, :each_byte, :each_char, :each_line
54
+ def_delegators :@tokens, :[], :length
55
+
56
+ def initialize(*arguments)
57
+ @tokens = []
58
+ arguments.flatten.compact.each do |argument|
59
+ add(argument)
60
+ end
61
+ end
62
+
63
+ def initialize_copy(other)
64
+ @tokens = other.tokens.dup
65
+ end
66
+
67
+ def add(argument)
68
+ case argument
69
+ when Value
70
+ @tokens += argument.tokens.dup
71
+ when ::String
72
+ @tokens << argument
73
+ when Symbol
74
+ @tokens << argument
75
+ else
76
+ raise(ArgumentError, "Failed to create Value from argument #{ argument.inspect }; expected String, Symbol or Value instance.")
77
+ end
78
+ self
79
+ end
80
+
81
+ alias :<< :add
82
+ alias :push :add
83
+
84
+ [:strip!, :upcase!, :downcase!, :sub!, :gsub!, :chop!, :chomp!, :rstrip!].each do |method_id|
85
+ define_method(method_id) do |*arguments, &block|
86
+ @tokens.each do |part|
87
+ part.send(method_id, *arguments, &block)
88
+ end
89
+ self
90
+ end
91
+ end
92
+
93
+ def replace(*arguments)
94
+ return self unless has_symbol?
95
+ arguments.flatten.each do |argument|
96
+ case argument
97
+ when ::String # simulates Ruby's String#replace
98
+ @tokens = [argument]
99
+ when String
100
+ @tokens = @tokens.map { |v| argument.key == v ? argument.value.tokens : v }.flatten
101
+ when Hash
102
+ @tokens = @tokens.map { |v| argument[v] || v }
103
+ end
104
+ end
105
+ self
106
+ end
107
+
108
+
109
+ # Returns the Value instance with all consecutive String tokens joined.
110
+ #
111
+ # call-seq:
112
+ # Value.new('foo', 'bar').join #=> <'foobar'>
113
+ # Value.new(:foo, 'bar').join #=> <:foo, 'bar'>
114
+ #
115
+ def join
116
+ @tokens = @tokens.inject([]) do |a,b|
117
+ a[-1].is_a?(::String) && b.is_a?(::String) ? a[-1] += b : a << b; a
118
+ end
119
+ self
120
+ end
121
+
122
+ # Returns a the Value as a string. @see #value; the only difference is
123
+ # that single symbols are returned as String, too.
124
+ # If the Value is atomic and the option :quotes is given, the string
125
+ # will be quoted using the quote symbols specified.
126
+ #
127
+ # call-seq:
128
+ # Value.new('foo').to_s #=> "foo"
129
+ # Value.new(:foo).to_s #=> "foo"
130
+ # Value.new('foo').to_s(:quotes => '"') #=> "\"foo\""
131
+ # Value.new('foo').to_s(:quotes => ['"','"']) #=> "\"foo\""
132
+ # Value.new('foo').to_s(:quotes => ['{','}']) #=> "{foo}"
133
+ # Value.new(:foo, 'bar').to_s #=> "foo # \"bar\""
134
+ # Value.new('foo', 'bar').to_s #=> "\"foo\" # \"bar\""
135
+ #
136
+ def to_s(options = {})
137
+ return value.to_s unless options.has_key?(:quotes) && atomic?
138
+ *q = options[:quotes]
139
+ [q[0], value, q[-1]].compact.join
140
+ end
141
+
142
+ # Returns the Value as a string or, if it consists of a single symbol, as
143
+ # a Symbol instance. If the Value contains multiple tokens, they will be
144
+ # joined by a '#', additionally, all string tokens will be turned into
145
+ # string literals (i.e., delimitted by quotes).
146
+ def value
147
+ atomic? ? @tokens[0] : @tokens.map { |v| v.is_a?(::String) ? v.inspect : v }.join(' # ')
148
+ end
149
+
150
+ alias :v :value
151
+
152
+ def inspect
153
+ '<' + @tokens.map(&:inspect).join(', ') + '>'
154
+ end
155
+
156
+ # Returns true if the Value is empty or consists of a single token.
157
+ def atomic?
158
+ @tokens.length < 2
159
+ end
160
+
161
+ # Returns true if the Value looks like a BibTeX name value.
162
+ def name?
163
+ end
164
+
165
+ alias :names? :name?
166
+ alias :is_name? :name?
167
+
168
+ def to_name
169
+ Names.parse(to_s)
170
+ end
171
+
172
+ alias :to_names :to_name
173
+
174
+
175
+
176
+ # Returns true if the Value's content is numeric.
177
+ def numeric?
178
+ to_s =~ /^\s*[+-]?\d+[\/\.]?\d*\s*$/
179
+ end
180
+
181
+ alias :is_numeric? :numeric?
182
+
183
+ # Returns true if the Value contains at least one symbol.
184
+ def symbol?
185
+ @tokens.detect { |v| v.is_a?(Symbol) }
186
+ end
187
+
188
+ alias :has_symbol? :symbol?
189
+
190
+ # Returns all symbols contained in the Value.
191
+ def symbols
192
+ @tokens.select { |v| v.is_a?(Symbol) }
193
+ end
194
+
195
+ def <=>(other)
196
+ to_s <=> other.to_s
197
+ end
198
+
199
+ end
200
+
201
+ end
@@ -18,6 +18,6 @@
18
18
 
19
19
  module BibTeX
20
20
  module Version
21
- STRING = '1.2.1'.freeze
21
+ STRING = '1.3.0'.freeze
22
22
  end
23
23
  end
@@ -0,0 +1,52 @@
1
+ require File.expand_path('../../lib/bibtex.rb', __FILE__)
2
+ require 'profile'
3
+
4
+ module BibTeX
5
+ module Benchmark
6
+ def self.open
7
+ BibTeX.parse <<-END
8
+ @book{rails,
9
+ Address = {Raleigh, North Carolina},
10
+ Author = {Ruby, Sam, and Thomas, Dave, and Hansson Heinemeier, David},
11
+ Booktitle = {Agile Web Development with Rails},
12
+ Date-Added = {2010-08-05 10:01:36 +0200},
13
+ Date-Modified = {2010-08-05 10:06:46 +0200},
14
+ Edition = {third},
15
+ Isbn = {978-1-9343561-6-6},
16
+ Keywords = {ruby, rails},
17
+ Publisher = {The Pragmatic Bookshelf},
18
+ Series = {The Facets of Ruby},
19
+ Title = {Agile Web Development with Rails},
20
+ Year = {2009}
21
+ }
22
+
23
+ @book{dragon,
24
+ Address = {Boston},
25
+ Author = {Aho, Alfred V., and Lam, Monica S., and Ullman, Jeffrey D.},
26
+ Booktitle = {Compilers: Principles, Techniques, and Tools},
27
+ Date-Added = {2010-08-05 09:57:15 +0200},
28
+ Date-Modified = {2010-08-05 10:06:32 +0200},
29
+ Edition = {second},
30
+ Keywords = {compiler, lex, yacc},
31
+ Publisher = {Addison Wesley},
32
+ Title = {Compilers: Principles, Techniques, and Tools},
33
+ Year = {2007}
34
+ }
35
+
36
+ @book{pickaxe,
37
+ Address = {Raleigh, North Carolina},
38
+ Author = {Thomas, Dave, and Fowler, Chad, and Hunt, Andy},
39
+ Date-Added = {2010-08-05 09:54:07 +0200},
40
+ Date-Modified = {2010-08-05 10:07:01 +0200},
41
+ Keywords = {ruby},
42
+ Publisher = {The Pragmatic Bookshelf},
43
+ Series = {The Facets of Ruby},
44
+ Title = {Programming Ruby 1.9: The Pragmatic Programmer's Guide},
45
+ Year = {2009}
46
+ }
47
+ END
48
+ end
49
+ end
50
+ end
51
+
52
+ BibTeX::Benchmark.open
@@ -0,0 +1,141 @@
1
+ require 'helper'
2
+
3
+ module BibTeX
4
+
5
+ class BibliographyTest < MiniTest::Spec
6
+
7
+ context 'when newly created' do
8
+ should 'not be nil' do
9
+ assert Bibliography.new
10
+ end
11
+ should 'be empty' do
12
+ assert Bibliography.new.empty?
13
+ end
14
+ end
15
+
16
+ context '#open' do
17
+ should 'accept a block and save the file after execution' do
18
+ tmp = Tempfile.new('bibtex')
19
+ tmp.close
20
+ b = BibTeX.open(Test.fixtures(:bibdesk)).save_to(tmp.path)
21
+
22
+ BibTeX.open(tmp.path) do |bib|
23
+ bib.delete(:rails)
24
+ end
25
+
26
+ assert_equal b.length - 1, BibTeX.open(tmp.path).length
27
+ end
28
+ end
29
+
30
+ context 'given a populated biliography' do
31
+ setup do
32
+ @bib = BibTeX.parse <<-END
33
+ @book{rails,
34
+ address = {Raleigh, North Carolina},
35
+ author = {Ruby, Sam, and Thomas, Dave, and Hansson Heinemeier, David},
36
+ booktitle = {Agile Web Development with Rails},
37
+ edition = {third},
38
+ keywords = {ruby, rails},
39
+ publisher = {The Pragmatic Bookshelf},
40
+ series = {The Facets of Ruby},
41
+ title = {Agile Web Development with Rails},
42
+ year = {2009}
43
+ }
44
+ @book{flanagan2008,
45
+ title={{The ruby programming language}},
46
+ author={Flanagan, D. and Matsumoto, Y.},
47
+ keywords = {ruby},
48
+ year={2008},
49
+ publisher={O'Reilly}
50
+ }
51
+ @article{segaran2007,
52
+ title={{Programming collective intelligence}},
53
+ author={Segaran, T.},
54
+ year={2007},
55
+ publisher={O'Reilly}
56
+ }
57
+ END
58
+ end
59
+
60
+ should 'support access by index' do
61
+ assert_equal 'ruby', @bib[1].keywords
62
+ end
63
+
64
+ should 'support access by range' do
65
+ assert_equal %w{2008 2007}, @bib[1..2].map(&:year)
66
+ end
67
+
68
+ should 'support access by index and offset' do
69
+ assert_equal %w{2008 2007}, @bib[1,2].map(&:year)
70
+ end
71
+
72
+ should 'support queries by symbol key' do
73
+ refute_nil @bib[:rails]
74
+ assert_nil @bib[:ruby]
75
+ end
76
+
77
+ should 'support queries by symbol key and selector' do
78
+ assert_equal 1, @bib.q(:all, :rails).length
79
+ refute_nil @bib.q(:first, :rails)
80
+ assert_nil @bib.q(:first, :railss)
81
+ end
82
+
83
+ should 'support queries by string key' do
84
+ assert_equal 1, @bib['rails'].length
85
+ assert_equal 0, @bib['ruby'].length
86
+ end
87
+
88
+ should 'support queries by type string' do
89
+ assert_equal 2, @bib['@book'].length
90
+ assert_equal 1, @bib['@article'].length
91
+ assert_equal 0, @bib['@collection'].length
92
+ end
93
+
94
+ should 'support queries by type string and selector' do
95
+ assert_equal 2, @bib.q(:all, '@book').length
96
+ refute_nil @bib.q(:first, '@book')
97
+ assert_equal 1, @bib.q(:all, '@article').length
98
+ refute_nil @bib.q(:first, '@article')
99
+ assert_equal 0, @bib.q(:all, '@collection').length
100
+ assert_nil @bib.q(:first, '@collection')
101
+ end
102
+
103
+
104
+ should 'support queries by pattern' do
105
+ assert_equal 0, @bib[/reilly/].length
106
+ assert_equal 2, @bib[/reilly/i].length
107
+ end
108
+
109
+ should 'support queries by type string and conditions' do
110
+ assert_equal 1, @bib['@book[keywords=ruby]'].length
111
+ end
112
+
113
+ should 'support queries by bibtex element' do
114
+ entry = Entry.parse(<<-END).first
115
+ @article{segaran2007,
116
+ title = {{Programming collective intelligence}},
117
+ author = {Segaran, T.},
118
+ year = {2007},
119
+ publisher = {O'Reilly}
120
+ }
121
+ END
122
+ assert_equal 1, @bib[entry].length
123
+ entry.year = '2006'
124
+ assert_equal 0, @bib[entry].length
125
+ end
126
+
127
+ should 'support query and additional block' do
128
+ assert_equal 1, @bib.q('@book') { |e| e.keywords.split(/,/).length > 1 }.length
129
+ end
130
+
131
+ should 'support saving the bibliography to a file' do
132
+ tmp = Tempfile.new('bibtex')
133
+ tmp.close
134
+ @bib.save_to(tmp.path)
135
+ assert_equal @bib, BibTeX.open(tmp.path)
136
+ end
137
+
138
+ end
139
+
140
+ end
141
+ end