rbibtex 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,3 +1,21 @@
1
+ 0.1.0
2
+ * ! Incompatible API change. Removed entry.properties property. The data can
3
+ now be accessed as described below. !
4
+ * ! More incompatible changes. Renamed File to BibTeXFile, though I'm not yet
5
+ satisfied with the name. More changes can be expected. !
6
+ * Now parsing and interpreting strings. See BibTeXFile#interpolate.
7
+ * Moved common transformation logic into the library
8
+ * Made entry directly accessible like a hash. That allows to sort also by key
9
+ and type
10
+ * Made entry access case / type insensitive.
11
+ (I.e. entry["Title"] = entry["title"] = entry[:TITLE])
12
+ * Made entry accessible by method also. (I.e. entry[:Title] = entry.title
13
+ * Made entry use Enumerable for all the goodness of it. Each iterates about
14
+ all properties except key and type.
15
+ * Allow second line to be indented
16
+ * Added some documentation
17
+ * Added some unit tests (fixing a few layout bugs on the way. Should have done TDD).
18
+
1
19
  0.0.2
2
20
  * Simplified Grammar a bit
3
21
  * Now parsing @comment correctly
data/README CHANGED
@@ -53,12 +53,14 @@ Brian
53
53
  * The binaries are only quick hacks for my specific applications right now,
54
54
  they should be rewritten and extended.
55
55
  * I should transfer functionality from rbib2bib into a library usable by all
56
- programs.
56
+ programs. (Partially done)
57
57
  * The full bibtex format is not parsed, I only allow for a specific subset
58
- right now.
59
- * Escaping is not implemented.
58
+ right now. (May be done, I'm not quite sure what is the exact standard)
59
+ * Add automatic abbreviation creation as a fun quiz.
60
60
  * Create the documentation after having racc'ed the .y files, then cleanup
61
61
  again before packaging.
62
+ * Move string handling code into module, such that we no longer trample around
63
+ in the standard library.
62
64
 
63
65
  ## Authors
64
66
  This was implemented by Brian Schroeder.
data/bin/rbib2bib CHANGED
@@ -1,147 +1,117 @@
1
1
  #! /home/bschroed//bin/ruby
2
-
3
- # Some string handling routines for wordwrapping and tabifying.
4
- class String
5
- def wrap(width = 78, tabwidth = 8)
6
- raise "Invalid width" if width < 0
7
- self.untabify(tabwidth).
8
- gsub(/.{1,#{width}}(?:\s|\Z)/) { ($& + 5.chr).gsub(/\n\005/,"\n").gsub(/\005/,"\n") }.
9
- gsub(/\s*\Z/m, "")
10
- end
11
-
12
- def indent_hanging(indent = 2)
13
- self.gsub(/\n/, "\n#{" " * indent}")
14
- end
15
-
16
- def indent(indent = 2)
17
- self.gsub(/^/, " " * indent)
18
- end
19
-
20
-
21
- def untabify(tab_width = 8)
22
- self.gsub(/\t/, " "*tab_width)
2
+ #
3
+ # Pretty print a bibtex file.
4
+ #
5
+ # (c) 2005 Brian Schroeder
6
+ #
7
+ # http://ruby.brian-schroeder.de/rbibtex/
8
+ #
9
+
10
+ require "rbibtex"
11
+ require "rbibtex/transform"
12
+ require 'optparse'
13
+
14
+ include BibTeX
15
+ # Define Interface
16
+ class B2BOptions < OptionParser
17
+ VERSION = "0.0.3"
18
+
19
+ attr_reader :wrap, :wrap_width, :comment, :interpolate
20
+
21
+ def initialize
22
+ super()
23
+
24
+ # Defaults
25
+ @comment = true
26
+ @wrap = true
27
+ @wrap_width = 78
28
+
29
+ # Parser Actions
30
+ self.banner = "Usage: #{ __FILE__ } [-o OUTPUT] [OPTIONS] INPUT [INPUT_1 ...]"
31
+ self.separator ""
32
+ self.separator "Formatting"
33
+ self.on("-C", "--no-comment",
34
+ "Suppress generation of 'Created by' comment.") { @comment = false }
35
+ self.on("-w", "--wrap [WIDTH]", Integer,
36
+ "Reformat and wrap text (the default).") { | w | @wrap_width = w || @wrap_width; @wrap = true }
37
+ self.on("-W", "--no-wrap",
38
+ "Do not reformat and wrap text.") { @wrap = false }
39
+ self.on("-S", "--interpolate-strings",
40
+ "Replace abbreviations by their full string.") { @interpolate = true }
23
41
  end
24
42
 
25
- def deindent_hanging(indent = [self[/^\s*/].untabify.length, self[/\n(\s*)/, 1].to_s.untabify.length].max)
26
- self.untabify.gsub(/^\s{0,#{indent}}/, "")
43
+ def parse!(*args)
44
+ super
27
45
  end
28
46
 
29
- def deindent(indent = self[/^\s*/].untabify.length)
30
- self.untabify.gsub(/^\s{0,#{indent}}/, "")
31
- end
32
-
33
- def unwrap
34
- self.split(/\n\n+/).map { | paragraph |
35
- paragraph.gsub(/((?:\n|^)\S[^\n]*)\n(?=\S)/, "\\1 ")
36
- }.join("\n\n")
37
- end
47
+ include Options::ApplicationOptions
48
+ include Options::SortOptions
49
+ include Options::SortFieldsOptions
38
50
  end
39
51
 
40
- module Bib2Bib
41
- VERSION = "0.0.2"
42
-
43
- require 'optparse'
44
-
45
- class Options < OptionParser
46
- attr_reader :wrap, :sort_by, :top_fields, :bottom_fields, :output, :help, :show_version, :comment
47
-
48
- def initialize
49
- super()
50
-
51
- # Defaults
52
- @top_fields = [:author, :title, :year]
53
- @bottom_fields = [:note, :pdf, :ps, :www]
54
- @comment = true
55
- @output = STDOUT
56
-
57
- # Parser Actions
58
- self.banner = "Usage: #{ __FILE__ } [-o OUTPUT] [OPTIONS] INPUT [INPUT_1 ...]"
59
- self.on("-o FILENAME", "--output FILENAME", String,
60
- "Set the output filename. Writes to stdout if nothing is given") { | v | @output = File.open(v, 'w') }
61
- self.on("-s", "--sort-by", "--sort_by Field_1,Field2,...", Array,
62
- "Specify fields that shall be used for sorting of the file") { | v | @sort_by = v.map { | e | e.downcase.to_sym } }
63
- self.on("-t", "--top_fields Field_1,Field2,...", Array,
64
- "Specify a list of fields that are written first in the output") { | v | @top_fields = v.map { | e | e.downcase.to_sym }
65
- @bottom_fields -= @top_fields }
66
- self.on("-b", "--bottom_fields Field_1,Field2,...", Array,
67
- "Specify a list of fields that are written last in the output") { | v | @bottom_fields = v.map { | e | e.downcase.to_sym }
68
- @top_fields -= @bottom_fields }
69
- self.on("-C", "--no-comment",
70
- "Suppress generation of 'Created by' comment") { @comment = false }
71
- self.on("-?", "--help",
72
- "Show this help") { @help = true }
73
- self.on("-v", "--version",
74
- "Output version number and exit") { @show_version = true }
75
- end
76
-
77
- def parse!(*args)
78
- super
79
- raise "You can't specify an empty list of fields for sorting" if @sort_by and @sort_by.empty?
80
- end
81
- end
82
-
83
- options = Options.new
84
- begin
85
- options.parse!(ARGV)
86
- rescue => e
87
- puts "Invalid Commandline Arguments"
88
- puts e
89
- puts options
90
- exit
91
- end
92
-
93
- if options.show_version
94
- puts VERSION
95
- exit
96
- end
97
-
98
- if options.help
99
- puts options
100
- exit
101
- end
52
+ # Parse the options
53
+ options = B2BOptions.new
54
+ begin
55
+ options.parse!(ARGV)
56
+ rescue => e
57
+ puts "Invalid Commandline Arguments"
58
+ puts e
59
+ puts options
60
+ exit
61
+ end
102
62
 
103
- require "rbibtex"
63
+ file = BibTeXFile.parse( ARGF.read )
64
+ file.normalize!
65
+ file.interpolate! if options.interpolate
66
+ O = options.output
104
67
 
68
+ if @comment
69
+ O.puts "@comment { Pretty Printed using rbib2bib }"
70
+ end
105
71
 
106
- entries = BibTeX::Parser.new.parse( ARGF.read )
107
- O = options.output
72
+ # Calculate maximum key length
73
+ max_key_length = file.entries.inject(0) { | r, entry |
74
+ entry.inject(r) { | r, (k, _) |
75
+ [r, k.to_s.length].max
76
+ }
77
+ }
108
78
 
109
- if @comment
110
- O.puts "@comment { Pretty Printed using rbib2bib }"
111
- end
79
+ def extract_properties(properties, fields)
80
+ fields.map { | k | [k, properties.delete(k)] }.select { | (_, v) | v }
81
+ end
112
82
 
113
- # Calculate maximum key length
114
- max_key_length = entries.entries.inject(0) { | r, entry |
115
- entry.properties.inject(r) { | r, (k, _) |
116
- [r, k.to_s.length].max
117
- }
118
- }
83
+ def rewrap(text, indent, width)
84
+ text.to_str.deindent_hanging.unwrap.wrap(width - indent).indent_hanging(indent)
85
+ end
119
86
 
120
- def self.extract_properties(properties, fields)
121
- fields.map { | k | [k, properties.delete(k)] }.select { | (_, v) | v }
122
- end
87
+ if options.sort_by
88
+ file.sort_by(*options.sort_by)
89
+ end
123
90
 
124
- elements = entries.elements.select { | e | e.is_a?(BibTeX::Entry) or e.is_a?(BibTeX::Comment) }
125
- if options.sort_by
126
- elements = elements.sort_by { | e | e.is_a?(BibTeX::Comment) ? [] : options.sort_by.map { | k | e.properties[k] || "" } }
127
- end
128
- puts elements.map { | entry |
91
+ reformat = if options.wrap
92
+ lambda do | text, indent | rewrap(text, indent, options.wrap_width).remove_trailing_whitespace end
93
+ else
94
+ lambda do | text, indent | text.remove_trailing_whitespace end
95
+ end
96
+
97
+ puts file.elements.map { | element |
98
+
99
+ if element.is_a?Comment
100
+ "@comment { #{reformat[element, 11]} }"
101
+ elsif element.is_a?Abbreviation
102
+ %(@string{#{element.key} = #{reformat[element.string.to_bib, max_key_length + 6]}})
103
+ else
104
+ properties = element.inject({}) { | r, (k, v) | r[k] = v; r }
105
+ top = extract_properties(properties, options.top_fields)
106
+ bottom = extract_properties(properties, options.bottom_fields)
107
+ middle = properties.to_a.map { | (k, v) | [k.to_s, v] }.sort
129
108
 
130
- if entry.is_a?BibTeX::Comment
131
- "@comment { #{entry.to_s.deindent_hanging.unwrap.wrap(78 - 11).indent_hanging(11)} }"
132
- else
133
- properties = entry.properties.dup
134
- top = extract_properties(properties, options.top_fields)
135
- bottom = extract_properties(properties, options.bottom_fields)
136
- middle = properties.to_a.map { | (k, v) | [k.to_s, v] }.sort
137
-
138
- "@#{entry.type}{#{entry.key},\n" +
139
- (
140
- (top + middle + bottom).map { | (k, v) |
141
- %( #{k}).ljust(max_key_length+2) + " = {#{v.deindent_hanging.unwrap.wrap(78-(max_key_length+6)).indent_hanging(max_key_length+6)}}"
142
- }.join(",\n")
143
- ) + "\n}"
144
- end
145
- }.join("\n\n\n")
109
+ "@#{element.type}{#{element.key},\n" +
110
+ (
111
+ (top + middle + bottom).map { | (k, v) |
112
+ %( #{Entry::CAPITALIZATION[k]}).ljust(max_key_length+2) + " = " + reformat[v.to_bib, max_key_length + 6]
113
+ }.join(",\n")
114
+ ) + "\n}"
115
+ end
116
+ }.join("\n\n\n")
146
117
 
147
- end
data/bin/rbib2html CHANGED
@@ -1,13 +1,61 @@
1
1
  #! /home/bschroed//bin/ruby
2
2
 
3
- $:.unshift("~/lib/ruby")
4
-
5
3
  require "rbibtex"
4
+ require "rbibtex/transform"
5
+ require 'optparse'
6
+
7
+ include BibTeX
8
+ # Define Interface
9
+ class B2HTMLOptions < OptionParser
10
+ VERSION = "0.0.3"
11
+
12
+ attr_reader :wrap, :wrap_width, :comment, :interpolate
6
13
 
7
- parser = BibTeX::Parser.new
8
- file = parser.parse( ARGF.read )
14
+ def initialize
15
+ super()
9
16
 
10
- puts <<HTML_HEADER
17
+ # Defaults
18
+ @comment = true
19
+ @wrap = true
20
+ @wrap_width = 78
21
+
22
+ # Parser Actions
23
+ self.banner = "Usage: #{ __FILE__ } [-o OUTPUT] [OPTIONS] INPUT [INPUT_1 ...]"
24
+ self.separator ""
25
+ self.separator "Formatting"
26
+ self.on("-C", "--no-comment",
27
+ "Suppress generation of 'Created by' comment.") { @comment = false }
28
+ end
29
+
30
+ def parse!(*args)
31
+ super
32
+ end
33
+
34
+ include Options::ApplicationOptions
35
+ include Options::SortOptions
36
+ end
37
+
38
+ # Parse the options
39
+ options = B2HTMLOptions.new
40
+ begin
41
+ options.parse!(ARGV)
42
+ rescue => e
43
+ puts "Invalid Commandline Arguments"
44
+ puts e
45
+ puts options
46
+ exit
47
+ end
48
+
49
+ file = BibTeXFile.parse( ARGF.read )
50
+ file.normalize!
51
+ file.interpolate!
52
+ O = options.output
53
+
54
+ if options.sort_by
55
+ file.sort_by(*options.sort_by)
56
+ end
57
+
58
+ O.puts <<HTML_HEADER
11
59
  <html>
12
60
  <head>
13
61
  <style type="text/css">
@@ -19,33 +67,38 @@ puts <<HTML_HEADER
19
67
  </style>
20
68
  </head>
21
69
  <body>
70
+ <h1>Bibliography created from #{ARGV.inspect}</h1>
22
71
  HTML_HEADER
23
72
 
73
+
24
74
  first = [:author, :title, :year]
25
75
  last = [:note]
26
76
  links = [:pdf, :ps]
27
77
 
28
- file.each do | entry |
78
+ file.entries.each do | entry |
29
79
 
30
- p = entry.properties.dup
80
+ p = entry.inject({}) { | r, (k, v) | r[k] = v; r }
31
81
  f = first.map { | k | [k, p.delete(k)] }
32
82
  l = last.map { | k | [k, p.delete(k)] }
33
83
  a = links.map { | k | [k, p.delete(k)] }
34
84
  p = p.to_a.map{ | (k, v) | [k.to_s, v] }.sort
35
85
 
36
- puts %(<div class="entry">)
86
+ O.puts %(<div class="entry">)
37
87
  (f + p).each do | (k, v) |
38
- puts %(<span class="#{k}">#{v}</span>)
88
+ O.puts %(<span class="#{k}">#{v}</span>)
39
89
  end
40
- puts %(<span class="links")
90
+ O.puts %(<span class="links")
41
91
  a.each do | k, v |
42
- puts %(<a href="#{v}">#{k}</a>)
92
+ O.puts %(<a href="#{v}">#{k}</a>)
43
93
  end
44
- puts %(</span)
94
+ O.puts %(</span)
45
95
  (l).each do | (k, v) |
46
- puts %(<span class="#{k}">#{v}</span>)
96
+ O.puts %(<span class="#{k}">#{v}</span>)
47
97
  end
48
- puts %(</div>)
98
+ O.puts %(</div>)
49
99
  end
50
100
 
51
- puts "</body></html>"
101
+ if @comment
102
+ O.puts %(<div class="comment">Pretty Printed using rbib2bib</div>)
103
+ end
104
+ O.puts "</body></html>"
data/bin/rbib2txt CHANGED
@@ -1,41 +1,111 @@
1
1
  #! /home/bschroed//bin/ruby
2
-
3
- $:.unshift("~/lib/ruby")
2
+ #
3
+ # Convert a bibtex file to plain text
4
+ #
5
+ # (c) 2005 Brian Schroeder
6
+ #
7
+ # http://ruby.brian-schroeder.de/rbibtex/
8
+ #
4
9
 
5
10
  require "rbibtex"
11
+ require "rbibtex/transform"
12
+ require 'optparse'
6
13
 
7
- parser = BibTeX::Parser.new
8
- file = parser.parse( ARGF.read )
14
+ include BibTeX
15
+ # Define Interface
16
+ class B2TXTOptions < OptionParser
17
+ VERSION = "0.0.3"
18
+
19
+ attr_reader :wrap, :wrap_width, :comment, :interpolate
9
20
 
10
- first = [:author, :title, :year]
11
- last = [:note]
12
- links = [:pdf, :ps]
21
+ def initialize
22
+ super()
13
23
 
14
- file.each do | entry |
15
-
16
- p = entry.properties.dup
17
- f = first.map { | k | [k, p.delete(k)] }
18
- l = last.map { | k | [k, p.delete(k)] }
19
- a = links.map { | k | [k, p.delete(k)] }
20
- p = p.to_a.map{ | (k, v) | [k.to_s, v] }.sort
21
-
22
- puts
23
- (f + p).each do | (k, v) |
24
- next unless v
25
- print %( #{k}: ).ljust(20)
26
- puts v.gsub(/\n\s*/, "\n" + (" " * 20))
27
- end
28
- (l).each do | (k, v) |
29
- next unless v
30
- print %( #{k}: ).ljust(20)
31
- puts v.gsub(/\n\s*/, "\n" + (" " * 20))
24
+ # Defaults
25
+ @comment = true
26
+ @wrap = true
27
+ @wrap_width = 78
28
+
29
+ # Parser Actions
30
+ self.banner = "Usage: #{ __FILE__ } [-o OUTPUT] [OPTIONS] INPUT [INPUT_1 ...]"
31
+ self.separator ""
32
+ self.separator "Formatting"
33
+ self.on("-C", "--no-comment",
34
+ "Suppress generation of 'Created by' comment.") { @comment = false }
35
+ self.on("-w", "--wrap [WIDTH]", Integer,
36
+ "Reformat and wrap text (the default).") { | w | @wrap_width = w || @wrap_width; @wrap = true }
37
+ self.on("-W", "--no-wrap",
38
+ "Do not reformat and wrap text.") { @wrap = false }
32
39
  end
33
- a.each do | k, v |
34
- next unless v
35
- print %( #{k}: ).ljust(20)
36
- puts v.gsub(/\n\s*/, "\n" + (" " * 20))
40
+
41
+ def parse!(*args)
42
+ super
37
43
  end
38
- puts
39
- puts "-" * 80
44
+
45
+ include Options::ApplicationOptions
46
+ include Options::SortOptions
47
+ include Options::SortFieldsOptions
48
+ end
49
+
50
+ # Parse the options
51
+ options = B2TXTOptions.new
52
+ begin
53
+ options.parse!(ARGV)
54
+ rescue => e
55
+ puts "Invalid Commandline Arguments"
56
+ puts e
57
+ puts options
58
+ exit
40
59
  end
41
60
 
61
+ file = BibTeXFile.parse( ARGF.read )
62
+ file.normalize!
63
+ file.interpolate!
64
+ O = options.output
65
+
66
+ if @comment
67
+ O.puts "# Pretty Printed using rbib2bib"
68
+ end
69
+
70
+ # Calculate maximum key length
71
+ max_key_length = file.entries.inject(0) { | r, entry |
72
+ entry.inject(r) { | r, (k, _) |
73
+ [r, k.to_s.length].max
74
+ }
75
+ }
76
+
77
+ def extract_properties(properties, fields)
78
+ fields.map { | k | [k, properties.delete(k)] }.select { | (_, v) | v }
79
+ end
80
+
81
+ def rewrap(text, indent, width)
82
+ text.to_str.deindent_hanging.unwrap.wrap(width - indent).indent_hanging(indent)
83
+ end
84
+
85
+ if options.sort_by
86
+ file.sort_by(*options.sort_by)
87
+ end
88
+
89
+ reformat = if options.wrap
90
+ lambda do | text, indent | rewrap(text, indent, options.wrap_width).remove_trailing_whitespace end
91
+ else
92
+ lambda do | text, indent | text.remove_trailing_whitespace end
93
+ end
94
+
95
+ puts file.elements.map { | element |
96
+
97
+ if element.is_a?Comment
98
+ reformat[element, 11].gsub(/^/, "# ")
99
+ elsif element.is_a?Abbreviation
100
+ else
101
+ properties = element.inject({}) { | r, (k, v) | r[k] = v; r }
102
+ top = extract_properties(properties, options.top_fields)
103
+ bottom = extract_properties(properties, options.bottom_fields)
104
+ middle = properties.to_a.map { | (k, v) | [k.to_s, v] }.sort
105
+
106
+ (top + middle + bottom).map { | (k, v) |
107
+ %(#{Entry::CAPITALIZATION[k]}:).ljust(max_key_length) + " " + reformat[v.to_s, max_key_length + 2]
108
+ }.join("\n") + "\n" + \
109
+ (%(Type:).ljust(max_key_length) + " " + reformat[element.type, max_key_length + 2])
110
+ end
111
+ }.join("\n\n" + ("-" * options.wrap_width) + "\n\n")
@@ -1,5 +1,5 @@
1
1
  # pre-setup.rb
2
2
 
3
3
  # process grammer file
4
- system "racc rbibtex.y"
4
+ system "racc -g rbibtex.y"
5
5