workbook 0.4.2 → 0.4.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -1
- data/.yardoc/checksums +22 -21
- data/.yardoc/object_types +0 -0
- data/.yardoc/objects/root.dat +0 -0
- data/Gemfile.lock +2 -2
- data/doc/RubyXL.html +1 -1
- data/doc/RubyXL/Cell.html +1 -1
- data/doc/RubyXL/Workbook.html +31 -31
- data/doc/Workbook.html +4 -4
- data/doc/Workbook/Book.html +5 -5
- data/doc/Workbook/Cell.html +202 -62
- data/doc/Workbook/Format.html +32 -32
- data/doc/Workbook/Modules.html +1 -1
- data/doc/Workbook/Modules/RawObjectsStorage.html +5 -5
- data/doc/Workbook/Modules/TableDiffSort.html +14 -14
- data/doc/Workbook/Modules/TypeParser.html +7 -7
- data/doc/Workbook/NilValue.html +434 -0
- data/doc/Workbook/Readers.html +3 -3
- data/doc/Workbook/Readers/CsvReader.html +6 -6
- data/doc/Workbook/Readers/OdsReader.html +562 -60
- data/doc/Workbook/Readers/TxtReader.html +2 -2
- data/doc/Workbook/Readers/XlsReader.html +14 -14
- data/doc/Workbook/Readers/XlsShared.html +67 -67
- data/doc/Workbook/Readers/XlsxReader.html +7 -7
- data/doc/Workbook/Row.html +243 -5
- data/doc/Workbook/Sheet.html +2 -2
- data/doc/Workbook/Table.html +669 -58
- data/doc/Workbook/Template.html +5 -5
- data/doc/Workbook/Writers.html +1 -1
- data/doc/Workbook/Writers/CsvTableWriter.html +1 -1
- data/doc/Workbook/Writers/HtmlWriter.html +27 -13
- data/doc/Workbook/Writers/XlsWriter.html +2 -2
- data/doc/_index.html +23 -8
- data/doc/class_list.html +1 -1
- data/doc/file.README.html +16 -12
- data/doc/index.html +16 -12
- data/doc/method_list.html +232 -72
- data/doc/top-level-namespace.html +1 -1
- data/lib/workbook/book.rb +25 -25
- data/lib/workbook/cell.rb +33 -33
- data/lib/workbook/format.rb +10 -10
- data/lib/workbook/modules/raw_objects_storage.rb +19 -19
- data/lib/workbook/modules/table_diff_sort.rb +22 -22
- data/lib/workbook/modules/type_parser.rb +18 -18
- data/lib/workbook/nil_value.rb +6 -6
- data/lib/workbook/readers/csv_reader.rb +8 -8
- data/lib/workbook/readers/ods_reader.rb +92 -46
- data/lib/workbook/readers/txt_reader.rb +2 -2
- data/lib/workbook/readers/xls_reader.rb +19 -19
- data/lib/workbook/readers/xls_shared.rb +70 -70
- data/lib/workbook/readers/xlsx_reader.rb +42 -42
- data/lib/workbook/row.rb +59 -29
- data/lib/workbook/sheet.rb +8 -8
- data/lib/workbook/table.rb +52 -19
- data/lib/workbook/template.rb +10 -10
- data/lib/workbook/version.rb +1 -1
- data/lib/workbook/writers/csv_table_writer.rb +1 -1
- data/lib/workbook/writers/html_writer.rb +6 -6
- data/lib/workbook/writers/xls_writer.rb +12 -12
- data/rbeautify.rb +232 -0
- data/test/artifacts/book_with_colspans.ods +0 -0
- data/test/artifacts/book_with_tabs_and_colours.ods +0 -0
- data/test/test_book.rb +10 -10
- data/test/test_cell.rb +14 -14
- data/test/test_format.rb +11 -11
- data/test/test_functional.rb +3 -3
- data/test/test_modules_table_diff_sort.rb +24 -24
- data/test/test_modules_type_parser.rb +27 -27
- data/test/test_readers_csv_reader.rb +11 -11
- data/test/test_readers_ods_reader.rb +22 -15
- data/test/test_readers_txt_reader.rb +13 -13
- data/test/test_readers_xls_reader.rb +11 -11
- data/test/test_readers_xlsx_reader.rb +5 -5
- data/test/test_row.rb +96 -26
- data/test/test_sheet.rb +9 -9
- data/test/test_table.rb +42 -26
- data/test/test_template.rb +3 -3
- data/test/test_writers_html_writer.rb +6 -4
- data/test/test_writers_xls_writer.rb +7 -7
- metadata +5 -1
data/lib/workbook/sheet.rb
CHANGED
@@ -4,7 +4,7 @@ module Workbook
|
|
4
4
|
# A Sheet is a container of tables
|
5
5
|
attr_accessor :book
|
6
6
|
attr_accessor :name
|
7
|
-
|
7
|
+
|
8
8
|
# Initialize a new sheet
|
9
9
|
#
|
10
10
|
# @param [Workbook::Table, Array<Array>] table The first table of this sheet
|
@@ -20,14 +20,14 @@ module Workbook
|
|
20
20
|
self.book = book
|
21
21
|
return self
|
22
22
|
end
|
23
|
-
|
23
|
+
|
24
24
|
# Returns true if the first table of this sheet contains anything
|
25
25
|
#
|
26
26
|
# @return [Boolean]
|
27
27
|
def has_contents?
|
28
28
|
table.has_contents?
|
29
29
|
end
|
30
|
-
|
30
|
+
|
31
31
|
# Returns the first table of this sheet
|
32
32
|
#
|
33
33
|
# @return [Workbook::Table] the first table of this sheet
|
@@ -46,14 +46,14 @@ module Workbook
|
|
46
46
|
return @book
|
47
47
|
end
|
48
48
|
end
|
49
|
-
|
49
|
+
|
50
50
|
# Removes all lines from this table
|
51
51
|
#
|
52
52
|
# @return [Workbook::Table] (self)
|
53
53
|
def delete_all
|
54
54
|
self.delete_if{|b| true}
|
55
55
|
end
|
56
|
-
|
56
|
+
|
57
57
|
# clones itself *and* the tables it contains
|
58
58
|
#
|
59
59
|
# @return [Workbook::Sheet] The cloned sheet
|
@@ -64,15 +64,15 @@ module Workbook
|
|
64
64
|
s.each{|t| c << t.clone}
|
65
65
|
return c
|
66
66
|
end
|
67
|
-
|
67
|
+
|
68
68
|
# Create or open the existing table at an index value
|
69
|
-
#
|
69
|
+
#
|
70
70
|
# @param [Integer] index the index of the table
|
71
71
|
def create_or_open_table_at index
|
72
72
|
t = self[index]
|
73
73
|
t = self[index] = Workbook::Table.new if t == nil
|
74
74
|
t.sheet = self
|
75
|
-
t
|
75
|
+
t
|
76
76
|
end
|
77
77
|
end
|
78
78
|
end
|
data/lib/workbook/table.rb
CHANGED
@@ -3,7 +3,7 @@ require 'workbook/modules/table_diff_sort'
|
|
3
3
|
require 'workbook/writers/csv_table_writer'
|
4
4
|
|
5
5
|
|
6
|
-
module Workbook
|
6
|
+
module Workbook
|
7
7
|
# A table is a container of rows and keeps track of the sheet it belongs to and which row is its header. Additionally suport for CSV writing and diffing with another table is included.
|
8
8
|
class Table < Array
|
9
9
|
include Workbook::Modules::TableDiffSort
|
@@ -11,7 +11,7 @@ module Workbook
|
|
11
11
|
attr_accessor :sheet
|
12
12
|
attr_accessor :name
|
13
13
|
attr_accessor :header
|
14
|
-
|
14
|
+
|
15
15
|
def initialize row_cel_values=[], sheet=nil, options={}
|
16
16
|
row_cel_values = [] if row_cel_values == nil
|
17
17
|
row_cel_values.each do |r|
|
@@ -24,8 +24,8 @@ module Workbook
|
|
24
24
|
self.sheet = sheet
|
25
25
|
# Column data is considered as a 'row' with 'cells' that contain 'formatting'
|
26
26
|
end
|
27
|
-
|
28
|
-
# Returns the header of this table (typically the first row, but can be a different row).
|
27
|
+
|
28
|
+
# Returns the header of this table (typically the first row, but can be a different row).
|
29
29
|
# The header row is also used for finding values in a aribrary row.
|
30
30
|
#
|
31
31
|
# @return [Workbook::Row] The header
|
@@ -38,33 +38,39 @@ module Workbook
|
|
38
38
|
first
|
39
39
|
end
|
40
40
|
end
|
41
|
-
|
41
|
+
|
42
42
|
# Generates a new row, with optionally predefined cell-values, that is already connected to this table.
|
43
43
|
def new_row cell_values=[]
|
44
44
|
r = Workbook::Row.new(cell_values,self)
|
45
45
|
return r
|
46
46
|
end
|
47
|
-
|
47
|
+
|
48
48
|
def create_or_open_row_at index
|
49
49
|
r = self[index]
|
50
50
|
if r == nil
|
51
51
|
r = Workbook::Row.new
|
52
52
|
r.table=(self)
|
53
53
|
end
|
54
|
-
r
|
55
|
-
end
|
56
|
-
|
54
|
+
r
|
55
|
+
end
|
56
|
+
|
57
57
|
def remove_empty_lines!
|
58
58
|
self.delete_if{|r| r.nil? or r.compact.empty?}
|
59
59
|
self
|
60
60
|
end
|
61
61
|
|
62
|
+
# Add row
|
63
|
+
# @param [Workbook::Table, Array] row to add
|
62
64
|
def push(row)
|
65
|
+
row = Workbook::Row.new(row) if row.class == Array
|
63
66
|
super(row)
|
64
67
|
row.set_table(self)
|
65
68
|
end
|
66
|
-
|
69
|
+
|
70
|
+
# Add row
|
71
|
+
# @param [Workbook::Table, Array] row to add
|
67
72
|
def <<(row)
|
73
|
+
row = Workbook::Row.new(row) if row.class == Array
|
68
74
|
super(row)
|
69
75
|
row.set_table(self)
|
70
76
|
end
|
@@ -72,16 +78,16 @@ module Workbook
|
|
72
78
|
def has_contents?
|
73
79
|
self.clone.remove_empty_lines!.count != 0
|
74
80
|
end
|
75
|
-
|
81
|
+
|
76
82
|
# Returns true if the row exists in this table
|
77
83
|
#
|
78
84
|
# @param [Workbook::Row] row to test for
|
79
|
-
# @return [Boolean] whether the row exist in this table
|
85
|
+
# @return [Boolean] whether the row exist in this table
|
80
86
|
def contains_row? row
|
81
87
|
raise ArgumentError, "table should be a Workbook::Row (you passed a #{t.class})" unless row.is_a?(Workbook::Row)
|
82
88
|
self.collect{|r| r.object_id}.include? row.object_id
|
83
89
|
end
|
84
|
-
|
90
|
+
|
85
91
|
# Returns the sheet this table belongs to, creates a new sheet if none exists
|
86
92
|
#
|
87
93
|
# @return [Workbook::Sheet] The sheet this table belongs to
|
@@ -93,14 +99,14 @@ module Workbook
|
|
93
99
|
return @sheet
|
94
100
|
end
|
95
101
|
end
|
96
|
-
|
102
|
+
|
97
103
|
# Removes all lines from this table
|
98
104
|
#
|
99
105
|
# @return [Workbook::Table] (self)
|
100
106
|
def delete_all
|
101
107
|
self.delete_if{|b| true}
|
102
108
|
end
|
103
|
-
|
109
|
+
|
104
110
|
# clones itself *and* the rows it contains
|
105
111
|
#
|
106
112
|
# @return [Workbook::Table] The cloned table
|
@@ -113,7 +119,7 @@ module Workbook
|
|
113
119
|
c.header = c[header_row_index] if header_row_index
|
114
120
|
return c
|
115
121
|
end
|
116
|
-
|
122
|
+
|
117
123
|
# Overrides normal Array's []-function with support for symbols that identify a column based on the header-values
|
118
124
|
#
|
119
125
|
# @example Lookup using fixnum or header value encoded as symbol
|
@@ -128,24 +134,51 @@ module Workbook
|
|
128
134
|
cell_index = alpha_index_to_number_index(match[1])
|
129
135
|
row_index = match[2].to_i - 1
|
130
136
|
return self[row_index][cell_index]
|
131
|
-
else
|
137
|
+
else
|
132
138
|
if index_or_string
|
133
139
|
return to_a[index_or_string]
|
134
140
|
end
|
135
141
|
end
|
136
142
|
end
|
137
|
-
|
143
|
+
|
138
144
|
# Helps to convert from e.g. "AA" to 26
|
139
145
|
# @param [String] string that typically identifies a column
|
140
146
|
# @return [Integer]
|
141
147
|
def alpha_index_to_number_index string
|
142
148
|
string.upcase!
|
143
149
|
sum = 0
|
144
|
-
string.chars.each_with_index do | char, char_index|
|
150
|
+
string.chars.each_with_index do | char, char_index|
|
145
151
|
sum = sum * 26 + char.unpack('U')[0]-64
|
146
152
|
end
|
147
153
|
return sum-1
|
148
154
|
end
|
149
155
|
|
156
|
+
# remove all the trailing empty-rows (returning a trimmed clone)
|
157
|
+
#
|
158
|
+
# @param [Integer] desired_row_length of the rows
|
159
|
+
# @return [Workbook::Row] a trimmed clone of the array
|
160
|
+
def trim(desired_row_length=nil)
|
161
|
+
self.clone.trim!(desired_row_length)
|
162
|
+
end
|
163
|
+
|
164
|
+
# remove all the trailing empty-rows (returning a trimmed self)
|
165
|
+
#
|
166
|
+
# @param [Integer] desired_row_length of the new row
|
167
|
+
# @return [Workbook::Row] self
|
168
|
+
def trim!(desired_row_length=nil)
|
169
|
+
max_length = self.collect{|a| a.trim.length }.max
|
170
|
+
self_count = self.count-1
|
171
|
+
self.count.times do |index|
|
172
|
+
index = self_count - index
|
173
|
+
if self[index].trim.empty?
|
174
|
+
self.delete_at(index)
|
175
|
+
else
|
176
|
+
break
|
177
|
+
end
|
178
|
+
end
|
179
|
+
self.each{|a| a.trim!(max_length)}
|
180
|
+
self
|
181
|
+
end
|
182
|
+
|
150
183
|
end
|
151
184
|
end
|
data/lib/workbook/template.rb
CHANGED
@@ -5,18 +5,18 @@ module Workbook
|
|
5
5
|
# Workbook::Template is a container for different Workbook::Format's and the storage of raw template data that isn't really supported by Workbook, but should survive a typical read/write cyclus.
|
6
6
|
class Template
|
7
7
|
include Workbook::Modules::RawObjectsStorage
|
8
|
-
|
8
|
+
|
9
9
|
# Initialize Workbook::Template
|
10
|
-
def initialize
|
10
|
+
def initialize
|
11
11
|
@formats = {}
|
12
12
|
@has_header = true
|
13
13
|
end
|
14
|
-
|
14
|
+
|
15
15
|
# Whether the template has a predefined header (headers are used )
|
16
16
|
def has_header?
|
17
17
|
@has_header
|
18
18
|
end
|
19
|
-
|
19
|
+
|
20
20
|
# Add a Workbook::Format to the template
|
21
21
|
# @param [Workbook::Format] format (of a cell) to add to the template
|
22
22
|
def add_format format
|
@@ -24,15 +24,15 @@ module Workbook
|
|
24
24
|
@formats[format.name]=format
|
25
25
|
else
|
26
26
|
raise ArgumentError, "format should be a Workboot::Format"
|
27
|
-
end
|
27
|
+
end
|
28
28
|
end
|
29
|
-
|
29
|
+
|
30
30
|
# Return the list of associated formats
|
31
31
|
# @return [Hash] A keyed-hash of named formats
|
32
32
|
def formats
|
33
33
|
@formats
|
34
34
|
end
|
35
|
-
|
35
|
+
|
36
36
|
# Create or find a format by name
|
37
37
|
# @return [Workbook::Format] The new or found format
|
38
38
|
# @param [String] name of the format (e.g. whatever you want, in diff names such as 'destroyed', 'updated' and 'created' are being used)
|
@@ -41,8 +41,8 @@ module Workbook
|
|
41
41
|
fs = @formats[name]
|
42
42
|
fs = @formats[name] = {} if fs.nil?
|
43
43
|
f = fs[variant]
|
44
|
-
if f.nil?
|
45
|
-
f = Workbook::Format.new
|
44
|
+
if f.nil?
|
45
|
+
f = Workbook::Format.new
|
46
46
|
if variant != :default and fs[:default]
|
47
47
|
f = fs[:default].clone
|
48
48
|
end
|
@@ -50,5 +50,5 @@ module Workbook
|
|
50
50
|
end
|
51
51
|
return @formats[name][variant]
|
52
52
|
end
|
53
|
-
|
53
|
+
end
|
54
54
|
end
|
data/lib/workbook/version.rb
CHANGED
@@ -6,15 +6,15 @@ module Workbook
|
|
6
6
|
module HtmlWriter
|
7
7
|
|
8
8
|
# Generates an Spreadsheet (from the spreadsheet gem) in order to build an XlS
|
9
|
-
#
|
9
|
+
#
|
10
10
|
# @param [Hash] options A hash with options
|
11
11
|
# @return [Spreadsheet] A Spreadsheet object, ready for writing or more lower level operations
|
12
12
|
def to_html options={}
|
13
13
|
options = {:style_with_inline_css=>false}.merge(options)
|
14
|
-
builder = Nokogiri::
|
14
|
+
builder = Nokogiri::XML::Builder.new do |doc|
|
15
15
|
doc.html {
|
16
16
|
doc.body {
|
17
|
-
self.each{|sheet|
|
17
|
+
self.each{|sheet|
|
18
18
|
doc.h1 {
|
19
19
|
doc.text sheet.name
|
20
20
|
}
|
@@ -45,7 +45,7 @@ module Workbook
|
|
45
45
|
}
|
46
46
|
}
|
47
47
|
end
|
48
|
-
return builder.
|
48
|
+
return builder.doc.to_xhtml
|
49
49
|
end
|
50
50
|
|
51
51
|
|
@@ -53,11 +53,11 @@ module Workbook
|
|
53
53
|
# Write the current workbook to Microsoft Excel format (using the spreadsheet gem)
|
54
54
|
#
|
55
55
|
# @param [String] filename
|
56
|
-
# @param [Hash] options see #to_xls
|
56
|
+
# @param [Hash] options see #to_xls
|
57
57
|
def write_to_html filename="#{title}.html", options={}
|
58
58
|
File.open(filename, 'w') {|f| f.write(to_html(options)) }
|
59
59
|
return filename
|
60
60
|
end
|
61
61
|
end
|
62
62
|
end
|
63
|
-
end
|
63
|
+
end
|
@@ -6,7 +6,7 @@ module Workbook
|
|
6
6
|
module XlsWriter
|
7
7
|
|
8
8
|
# Generates an Spreadsheet (from the spreadsheet gem) in order to build an XlS
|
9
|
-
#
|
9
|
+
#
|
10
10
|
# @param [Hash] options A hash with options (unused so far)
|
11
11
|
# @return [Spreadsheet] A Spreadsheet object, ready for writing or more lower level operations
|
12
12
|
def to_xls options={}
|
@@ -30,9 +30,9 @@ module Workbook
|
|
30
30
|
end
|
31
31
|
book
|
32
32
|
end
|
33
|
-
|
33
|
+
|
34
34
|
# Generates an Spreadsheet (from the spreadsheet gem) in order to build an XlS
|
35
|
-
#
|
35
|
+
#
|
36
36
|
# @param [Workbook::Format, Hash] f A Workbook::Format or hash with format-options (:font_weight, :rotation, :background_color, :number_format, :text_direction, :color, :font_family)
|
37
37
|
# @return [Spreadsheet::Format] A Spreadsheet format-object, ready for writing or more lower level operations
|
38
38
|
def format_to_xls_format f
|
@@ -43,7 +43,7 @@ module Workbook
|
|
43
43
|
xlsfmt = f.return_raw_for Spreadsheet::Format
|
44
44
|
unless xlsfmt
|
45
45
|
xlsfmt=Spreadsheet::Format.new :weight=>f[:font_weight]
|
46
|
-
xlsfmt.rotation = f[:rotation] if f[:rotation]
|
46
|
+
xlsfmt.rotation = f[:rotation] if f[:rotation]
|
47
47
|
xlsfmt.pattern_fg_color = html_color_to_xls_color(f[:background_color]) if html_color_to_xls_color(f[:background_color])
|
48
48
|
xlsfmt.pattern = 1 if html_color_to_xls_color(f[:background_color])
|
49
49
|
xlsfmt.number_format = strftime_to_ms_format(f[:number_format]) if f[:number_format]
|
@@ -55,7 +55,7 @@ module Workbook
|
|
55
55
|
end
|
56
56
|
return xlsfmt
|
57
57
|
end
|
58
|
-
|
58
|
+
|
59
59
|
# Attempt to convert html-hex color value to xls color number
|
60
60
|
#
|
61
61
|
# @param [String] hex color
|
@@ -66,26 +66,26 @@ module Workbook
|
|
66
66
|
end
|
67
67
|
return nil
|
68
68
|
end
|
69
|
-
|
69
|
+
|
70
70
|
# Converts standard (ruby/C++/unix/...) strftime formatting to MS's formatting
|
71
|
-
#
|
71
|
+
#
|
72
72
|
# @param [String, nil] numberformat (nil returns nil)
|
73
73
|
# @return [String, nil]
|
74
74
|
def strftime_to_ms_format numberformat
|
75
75
|
return nil if numberformat.nil?
|
76
76
|
return numberformat.gsub('%Y','yyyy').gsub('%A','dddd').gsub('%B','mmmm').gsub('%a','ddd').gsub('%b','mmm').gsub('%y','yy').gsub('%d','dd').gsub('%m','mm').gsub('%y','y').gsub('%y','%%y').gsub('%e','d')
|
77
77
|
end
|
78
|
-
|
78
|
+
|
79
79
|
# Write the current workbook to Microsoft Excel format (using the spreadsheet gem)
|
80
80
|
#
|
81
81
|
# @param [String] filename
|
82
|
-
# @param [Hash] options see #to_xls
|
82
|
+
# @param [Hash] options see #to_xls
|
83
83
|
def write_to_xls filename="#{title}.xls", options={}
|
84
84
|
if to_xls(options).write(filename)
|
85
85
|
return filename
|
86
86
|
end
|
87
87
|
end
|
88
|
-
|
88
|
+
|
89
89
|
def xls_sheet a
|
90
90
|
if xls_template.worksheet(a)
|
91
91
|
return xls_template.worksheet(a)
|
@@ -94,11 +94,11 @@ module Workbook
|
|
94
94
|
self.xls_sheet a
|
95
95
|
end
|
96
96
|
end
|
97
|
-
|
97
|
+
|
98
98
|
def xls_template
|
99
99
|
return template.raws[Spreadsheet::Excel::Workbook] ? template.raws[Spreadsheet::Excel::Workbook] : template.raws[Spreadsheet::Workbook]
|
100
100
|
end
|
101
|
-
|
101
|
+
|
102
102
|
def init_spreadsheet_template
|
103
103
|
if self.xls_template.is_a? Spreadsheet::Workbook
|
104
104
|
return self.xls_template
|
data/rbeautify.rb
ADDED
@@ -0,0 +1,232 @@
|
|
1
|
+
#!/usr/bin/ruby -w
|
2
|
+
|
3
|
+
|
4
|
+
=begin
|
5
|
+
/***************************************************************************
|
6
|
+
* Copyright (C) 2008, Paul Lutus *
|
7
|
+
* *
|
8
|
+
* This program is free software; you can redistribute it and/or modify *
|
9
|
+
* it under the terms of the GNU General Public License as published by *
|
10
|
+
* the Free Software Foundation; either version 2 of the License, or *
|
11
|
+
* (at your option) any later version. *
|
12
|
+
* *
|
13
|
+
* This program is distributed in the hope that it will be useful, *
|
14
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
15
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
16
|
+
* GNU General Public License for more details. *
|
17
|
+
* *
|
18
|
+
* You should have received a copy of the GNU General Public License *
|
19
|
+
* along with this program; if not, write to the *
|
20
|
+
* Free Software Foundation, Inc., *
|
21
|
+
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
22
|
+
***************************************************************************/
|
23
|
+
=end
|
24
|
+
|
25
|
+
PVERSION = "Version 2.9, 10/24/2008"
|
26
|
+
|
27
|
+
module RBeautify
|
28
|
+
|
29
|
+
# user-customizable values
|
30
|
+
|
31
|
+
RBeautify::TabStr = " "
|
32
|
+
RBeautify::TabSize = 2
|
33
|
+
|
34
|
+
# indent regexp tests
|
35
|
+
|
36
|
+
IndentExp = [
|
37
|
+
/^module\b/,
|
38
|
+
/^class\b/,
|
39
|
+
/^if\b/,
|
40
|
+
/(=\s*|^)until\b/,
|
41
|
+
/(=\s*|^)for\b/,
|
42
|
+
/^unless\b/,
|
43
|
+
/(=\s*|^)while\b/,
|
44
|
+
/(=\s*|^)begin\b/,
|
45
|
+
/(^| )case\b/,
|
46
|
+
/\bthen\b/,
|
47
|
+
/^rescue\b/,
|
48
|
+
/^def\b/,
|
49
|
+
/\bdo\b/,
|
50
|
+
/^else\b/,
|
51
|
+
/^elsif\b/,
|
52
|
+
/^ensure\b/,
|
53
|
+
/\bwhen\b/,
|
54
|
+
/\{[^\}]*$/,
|
55
|
+
/\[[^\]]*$/
|
56
|
+
]
|
57
|
+
|
58
|
+
# outdent regexp tests
|
59
|
+
|
60
|
+
OutdentExp = [
|
61
|
+
/^rescue\b/,
|
62
|
+
/^ensure\b/,
|
63
|
+
/^elsif\b/,
|
64
|
+
/^end\b/,
|
65
|
+
/^else\b/,
|
66
|
+
/\bwhen\b/,
|
67
|
+
/^[^\{]*\}/,
|
68
|
+
/^[^\[]*\]/
|
69
|
+
]
|
70
|
+
|
71
|
+
def RBeautify.rb_make_tab(tab)
|
72
|
+
return (tab < 0)?"":TabStr * TabSize * tab
|
73
|
+
end
|
74
|
+
|
75
|
+
def RBeautify.rb_add_line(line,tab)
|
76
|
+
line.strip!
|
77
|
+
line = rb_make_tab(tab) + line if line.length > 0
|
78
|
+
return line
|
79
|
+
end
|
80
|
+
|
81
|
+
def RBeautify.beautify_string(source, path = "")
|
82
|
+
comment_block = false
|
83
|
+
in_here_doc = false
|
84
|
+
here_doc_term = ""
|
85
|
+
program_end = false
|
86
|
+
multiLine_array = []
|
87
|
+
multiLine_str = ""
|
88
|
+
tab = 0
|
89
|
+
output = []
|
90
|
+
source.each_line do |line|
|
91
|
+
line.chomp!
|
92
|
+
if(!program_end)
|
93
|
+
# detect program end mark
|
94
|
+
if(line =~ /^__END__$/)
|
95
|
+
program_end = true
|
96
|
+
else
|
97
|
+
# combine continuing lines
|
98
|
+
if(!(line =~ /^\s*#/) && line =~ /[^\\]\\\s*$/)
|
99
|
+
multiLine_array.push line
|
100
|
+
multiLine_str += line.sub(/^(.*)\\\s*$/,"\\1")
|
101
|
+
next
|
102
|
+
end
|
103
|
+
|
104
|
+
# add final line
|
105
|
+
if(multiLine_str.length > 0)
|
106
|
+
multiLine_array.push line
|
107
|
+
multiLine_str += line.sub(/^(.*)\\\s*$/,"\\1")
|
108
|
+
end
|
109
|
+
|
110
|
+
tline = ((multiLine_str.length > 0)?multiLine_str:line).strip
|
111
|
+
if(tline =~ /^=begin/)
|
112
|
+
comment_block = true
|
113
|
+
end
|
114
|
+
if(in_here_doc)
|
115
|
+
in_here_doc = false if tline =~ %r{\s*#{here_doc_term}\s*}
|
116
|
+
else # not in here_doc
|
117
|
+
if tline =~ %r{=\s*<<}
|
118
|
+
here_doc_term = tline.sub(%r{.*=\s*<<-?\s*([\w]+).*},"\\1")
|
119
|
+
in_here_doc = here_doc_term.size > 0
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
if(comment_block || program_end || in_here_doc)
|
125
|
+
# add the line unchanged
|
126
|
+
output << line
|
127
|
+
else
|
128
|
+
comment_line = (tline =~ /^#/)
|
129
|
+
if(!comment_line)
|
130
|
+
# throw out sequences that will
|
131
|
+
# only sow confusion
|
132
|
+
while tline.gsub!(/\{[^\{]*?\}/,"")
|
133
|
+
end
|
134
|
+
while tline.gsub!(/\[[^\[]*?\]/,"")
|
135
|
+
end
|
136
|
+
while tline.gsub!(/'.*?'/,"")
|
137
|
+
end
|
138
|
+
while tline.gsub!(/".*?"/,"")
|
139
|
+
end
|
140
|
+
while tline.gsub!(/\`.*?\`/,"")
|
141
|
+
end
|
142
|
+
while tline.gsub!(/\([^\(]*?\)/,"")
|
143
|
+
end
|
144
|
+
while tline.gsub!(/\/.*?\//,"")
|
145
|
+
end
|
146
|
+
while tline.gsub!(/%r(.).*?\1/,"")
|
147
|
+
end
|
148
|
+
# delete end-of-line comments
|
149
|
+
tline.sub!(/#[^\"]+$/,"")
|
150
|
+
# convert quotes
|
151
|
+
tline.gsub!(/\\\"/,"'")
|
152
|
+
OutdentExp.each do |re|
|
153
|
+
if(tline =~ re)
|
154
|
+
tab -= 1
|
155
|
+
break
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
if (multiLine_array.length > 0)
|
160
|
+
multiLine_array.each do |ml|
|
161
|
+
output << rb_add_line(ml,tab)
|
162
|
+
end
|
163
|
+
multiLine_array.clear
|
164
|
+
multiLine_str = ""
|
165
|
+
else
|
166
|
+
output << rb_add_line(line,tab)
|
167
|
+
end
|
168
|
+
if(!comment_line)
|
169
|
+
IndentExp.each do |re|
|
170
|
+
if(tline =~ re && !(tline =~ /\s+end\s*$/))
|
171
|
+
tab += 1
|
172
|
+
break
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
if(tline =~ /^=end/)
|
178
|
+
comment_block = false
|
179
|
+
end
|
180
|
+
end
|
181
|
+
error = (tab != 0)
|
182
|
+
STDERR.puts "#{path}: Error: indent/outdent mismatch: #{tab}." if error
|
183
|
+
return output.join("\n") + "\n",error
|
184
|
+
end # beautify_string
|
185
|
+
|
186
|
+
def RBeautify.beautify_file(path, check)
|
187
|
+
error = false
|
188
|
+
if(path == '-') # stdin source
|
189
|
+
source = STDIN.read
|
190
|
+
dest,error = beautify_string(source,"stdin")
|
191
|
+
print dest
|
192
|
+
else # named file source
|
193
|
+
source = File.read(path)
|
194
|
+
dest,error = beautify_string(source,path)
|
195
|
+
if(source != dest && !error)
|
196
|
+
if check
|
197
|
+
puts "#{path} needs formatting"
|
198
|
+
error = "formatting"
|
199
|
+
else
|
200
|
+
# make a backup copy
|
201
|
+
File.open(path + "~","w") { |f| f.write(source) }
|
202
|
+
# overwrite the original
|
203
|
+
File.open(path,"w") { |f| f.write(dest) }
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
return error
|
208
|
+
end # beautify_file
|
209
|
+
|
210
|
+
def RBeautify.main
|
211
|
+
error = false
|
212
|
+
check = false
|
213
|
+
if(!ARGV[0])
|
214
|
+
STDERR.puts "usage: Ruby filenames or \"-\" for stdin."
|
215
|
+
exit 0
|
216
|
+
end
|
217
|
+
if ARGV[0] == '--check'
|
218
|
+
check = true
|
219
|
+
ARGV.slice!(0)
|
220
|
+
end
|
221
|
+
ARGV.each do |argument|
|
222
|
+
error = (beautify_file(argument, check))?true:error
|
223
|
+
end
|
224
|
+
error = (error)?1:0
|
225
|
+
exit error
|
226
|
+
end # main
|
227
|
+
end # module RBeautify
|
228
|
+
|
229
|
+
# if launched as a standalone program, not loaded as a module
|
230
|
+
if __FILE__ == $0
|
231
|
+
RBeautify.main
|
232
|
+
end
|