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
@@ -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