rbibtex 0.0.2 → 0.1.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.
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