fixed_width 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/COPYING +10 -0
- data/HISTORY +12 -0
- data/README.markdown +149 -0
- data/Rakefile +44 -0
- data/TODO +7 -0
- data/VERSION +1 -0
- data/examples/readme_example.rb +62 -0
- data/fixed_width.gemspec +74 -0
- data/lib/fixed_width/column.rb +85 -0
- data/lib/fixed_width/core_ext/symbol.rb +17 -0
- data/lib/fixed_width/definition.rb +31 -0
- data/lib/fixed_width/fixed_width.rb +84 -0
- data/lib/fixed_width/generator.rb +20 -0
- data/lib/fixed_width/parser.rb +47 -0
- data/lib/fixed_width/section.rb +91 -0
- data/lib/fixed_width.rb +9 -0
- data/spec/column_spec.rb +209 -0
- data/spec/definition_spec.rb +81 -0
- data/spec/fixed_width_spec.rb +81 -0
- data/spec/generator_spec.rb +48 -0
- data/spec/parser_spec.rb +110 -0
- data/spec/section_spec.rb +176 -0
- data/spec/spec_helper.rb +3 -0
- metadata +97 -0
data/COPYING
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
Copyright (c) 2010, Topspin Media Inc.
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
5
|
+
|
6
|
+
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
7
|
+
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
8
|
+
* Neither the name of the Topspin Media Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
9
|
+
|
10
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/HISTORY
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
v0.1.1 (2010-05-29)
|
2
|
+
=========================
|
3
|
+
* column grouping (parsing and writing)
|
4
|
+
* :singular section option
|
5
|
+
* :nil_blank column option
|
6
|
+
* some rdoc for FixedWidth
|
7
|
+
* idiomatic syntax cleanup
|
8
|
+
|
9
|
+
v0.1.0 (2010-05-19)
|
10
|
+
=========================
|
11
|
+
* non-release (hence untagged)
|
12
|
+
* Forked from ryanwood/slither, renamed.
|
data/README.markdown
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
DESCRIPTION:
|
2
|
+
============
|
3
|
+
|
4
|
+
A simple, clean DSL for describing, writing, and parsing fixed-width text files.
|
5
|
+
|
6
|
+
FEATURES:
|
7
|
+
=========
|
8
|
+
|
9
|
+
* Easy DSL syntax
|
10
|
+
* Can parse and format fixed width files
|
11
|
+
* Templated sections for reuse
|
12
|
+
|
13
|
+
SYNOPSIS:
|
14
|
+
=========
|
15
|
+
|
16
|
+
##Creating a definition (Quick 'n Dirty)
|
17
|
+
|
18
|
+
Hopefully this will cover 90% of use cases.
|
19
|
+
|
20
|
+
# Create a FixedWidth::Defintion to describe a file format
|
21
|
+
FixedWidth.define :simple do |d|
|
22
|
+
# This is a template section that can be reused in other sections
|
23
|
+
d.template :boundary do |t|
|
24
|
+
t.column :record_type, 4
|
25
|
+
t.column :company_id, 12
|
26
|
+
end
|
27
|
+
|
28
|
+
# Create a section named :header
|
29
|
+
d.header(:align => :left) do |header|
|
30
|
+
# The trap tells FixedWidth which lines should fall into this section
|
31
|
+
header.trap { |line| line[0,4] == 'HEAD' }
|
32
|
+
# Use the boundary template for the columns
|
33
|
+
header.template :boundary
|
34
|
+
end
|
35
|
+
|
36
|
+
d.body do |body|
|
37
|
+
body.trap { |line| line[0,4] =~ /[^(HEAD|FOOT)]/ }
|
38
|
+
body.column :id, 10, :parser => :to_i
|
39
|
+
body.column :first, 10, :align => :left, :group => :name
|
40
|
+
body.column :last, 10, :align => :left, :group => :name
|
41
|
+
body.spacer 3
|
42
|
+
body.column :city, 20 , :group => :address
|
43
|
+
body.column :state, 2 , :group => :address
|
44
|
+
body.column :country, 3, :group => :address
|
45
|
+
end
|
46
|
+
|
47
|
+
d.footer do |footer|
|
48
|
+
footer.trap { |line| line[0,4] == 'FOOT' }
|
49
|
+
footer.template :boundary
|
50
|
+
footer.column :record_count, 10, :parser => :to_i
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
This definition would output a parsed file something like this:
|
55
|
+
|
56
|
+
{
|
57
|
+
:body => [
|
58
|
+
{ :id => 12,
|
59
|
+
:name => { :first => "Ryan", :last => "Wood" },
|
60
|
+
:address => { :city => "Foo", :state => 'SC', :country => "USA" }
|
61
|
+
},
|
62
|
+
{ :id => 13,
|
63
|
+
:name => { :first => "Jo", :last => "Schmo" },
|
64
|
+
:address => { :city => "Bar", :state => "CA", :country => "USA" }
|
65
|
+
}
|
66
|
+
],
|
67
|
+
:header => [{ :record_type => 'HEAD', :company_id => 'ABC' }],
|
68
|
+
:footer => [{ :record_type => 'FOOT', :company_id => 'ABC', :record_count => 2 }]
|
69
|
+
}
|
70
|
+
|
71
|
+
##Sections
|
72
|
+
###Declaring a section
|
73
|
+
|
74
|
+
Sections can have any name, however duplicates are not allowed. (A `DuplicateSectionNameError` will be thrown.) We use the standard `method_missing` trick. So if you see any unusual behavior, that's probably the first spot to look.
|
75
|
+
|
76
|
+
FixedWidth.define :simple do |d|
|
77
|
+
d.a_section_name do |s|
|
78
|
+
...
|
79
|
+
end
|
80
|
+
d.another_section_name do |s|
|
81
|
+
...
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
### Section options:
|
86
|
+
|
87
|
+
* `:singular` (default `false`) indicates that the section will only have a single record, and that it should not be returned nested in an array.
|
88
|
+
|
89
|
+
* `:optional` (default `false`) indicates that the section is optional. (An otherwise-specified section will raise a `RequiredSectionNotFoundError` if the trap block doesn't match the row after the last one of the previous section.)
|
90
|
+
|
91
|
+
##Columns
|
92
|
+
###Declaring a column
|
93
|
+
|
94
|
+
Columns can have any name, except for `:spacer` which is reserved. Also, duplicate column names within groupings are not allowed, and a column cannot share the same name as a group. (A `DuplicateColumnNameError` will be thrown for a duplicate column name within a grouping. A `DuplicateGroupNameError` will be thrown if you try to declare a column with the same name as an existing group or vice versa.) Again, basic `method_missing` trickery here, so be warned. You can declare columns either with the `method_missing` thing or by calling `Section#column`.
|
95
|
+
|
96
|
+
FixedWidth.define :simple do |d|
|
97
|
+
d.a_section_name do |s|
|
98
|
+
s.a_column_name 12
|
99
|
+
s.column :another_column_name, 14
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
###Column Options:
|
104
|
+
|
105
|
+
* `:align` can be set to `:left` or `:right`, to indicate which side the values should be/are justified to. By default, all columns are aligned `:right`.
|
106
|
+
|
107
|
+
* `:group` can be set to a `Symbol` indicating the name of the nested hash which the value should be parsed to when reading/the name of the nested hash the value should be extracted from when writing.
|
108
|
+
|
109
|
+
* `:parser` and `:formatter` options are symbols (to be proc-ified) or procs. By default, parsing and formatting assume that we're expecting/writing right-aligned strings, padded with spaces.
|
110
|
+
|
111
|
+
* `:nil_blank` set to true will cause whitespace-only fields to be parsed to nil, regardless of `:parser`.
|
112
|
+
|
113
|
+
* `:padding` can be set to a single character that will be used to pad formatted values, when writing fixed-width files.
|
114
|
+
|
115
|
+
* `:truncate` can be set to true to truncate any value that exceeds the `length` property of a column. If unset or set to `false`, a `FixedWidth::FormattedStringExceedsLengthError` exception will be thrown.
|
116
|
+
|
117
|
+
##Writing out fixed-width records
|
118
|
+
|
119
|
+
Then either feed it a nested struct with data values to create the file in the defined format:
|
120
|
+
|
121
|
+
test_data = {
|
122
|
+
:body => [
|
123
|
+
{ :id => 12,
|
124
|
+
:name => { :first => "Ryan", :last => "Wood" },
|
125
|
+
:address => { :city => "Foo", :state => 'SC', :country => "USA" }
|
126
|
+
},
|
127
|
+
{ :id => 13,
|
128
|
+
:name => { :first => "Jo", :last => "Schmo" },
|
129
|
+
:address => { :city => "Bar", :state => "CA", :country => "USA" }
|
130
|
+
}
|
131
|
+
],
|
132
|
+
:header => [{ :record_type => 'HEAD', :company_id => 'ABC' }],
|
133
|
+
:footer => [{ :record_type => 'FOOT', :company_id => 'ABC', :record_count => 2 }]
|
134
|
+
}
|
135
|
+
|
136
|
+
# Generates the file as a string
|
137
|
+
puts FixedWidth.generate(:simple, test_data)
|
138
|
+
|
139
|
+
# Writes the file
|
140
|
+
FixedWidth.write(file_instance, :simple, test_data)
|
141
|
+
|
142
|
+
Or parse files already in that format into a nested hash:
|
143
|
+
|
144
|
+
parsed_data = FixedWidth.parse(file_instance, :test).inspect
|
145
|
+
|
146
|
+
INSTALL:
|
147
|
+
========
|
148
|
+
|
149
|
+
sudo gem install fixed_width
|
data/Rakefile
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
task :default => :spec
|
2
|
+
task :test => :spec
|
3
|
+
|
4
|
+
desc "Build a gem"
|
5
|
+
task :gem => [ :gemspec, :build ]
|
6
|
+
|
7
|
+
desc "Run specs"
|
8
|
+
task :spec do
|
9
|
+
exec "spec -fn -b -c spec/"
|
10
|
+
end
|
11
|
+
|
12
|
+
begin
|
13
|
+
require 'jeweler'
|
14
|
+
Jeweler::Tasks.new do |gemspec|
|
15
|
+
gemspec.name = "fixed_width"
|
16
|
+
gemspec.summary = "A gem that provides a DSL for parsing and writing files of fixed-width records."
|
17
|
+
gemspec.description = <<END
|
18
|
+
Shamelessly forked from ryanwood/slither [http://github.com/ryanwood/slither].
|
19
|
+
|
20
|
+
Renamed the gem to be a little clearer as to its purpose. Hate that 'nokogiri' nonsense.
|
21
|
+
END
|
22
|
+
gemspec.email = "timon.karnezos@gmail.com"
|
23
|
+
gemspec.homepage = "http://github.com/timonk/fixed_width"
|
24
|
+
gemspec.authors = ["Timon Karnezos"]
|
25
|
+
end
|
26
|
+
rescue LoadError
|
27
|
+
warn "Jeweler not available. Install it with:"
|
28
|
+
warn "gem install jeweler"
|
29
|
+
end
|
30
|
+
|
31
|
+
require 'rake/rdoctask'
|
32
|
+
Rake::RDocTask.new do |rdoc|
|
33
|
+
if File.exist?('VERSION')
|
34
|
+
version = File.read('VERSION')
|
35
|
+
else
|
36
|
+
version = ""
|
37
|
+
end
|
38
|
+
|
39
|
+
rdoc.rdoc_dir = 'rdoc'
|
40
|
+
rdoc.title = "rprince #{version}"
|
41
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
42
|
+
rdoc.rdoc_files.include('README*')
|
43
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
44
|
+
end
|
data/TODO
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
* Add :limit option on sections
|
2
|
+
* Add :validation option for columns
|
3
|
+
* Add a validate_file() method to parse a file and run all validation tests (implies validation implemented)
|
4
|
+
* Better Documentation
|
5
|
+
* Alternate Section Flow (other than linear), i.e. repeatable sections (think batch)
|
6
|
+
* Add batched generation, so we don't turn into memory hogs
|
7
|
+
* Add batched parsing, so we don't turn into memory hogs
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.2.0
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
require File.join(File.dirname(__FILE__), "..", "lib", "fixed_width")
|
3
|
+
|
4
|
+
# Create a FixedWidth::Defintion to describe a file format
|
5
|
+
FixedWidth.define :simple do |d|
|
6
|
+
# This is a template section that can be reused in other sections
|
7
|
+
d.template :boundary do |t|
|
8
|
+
t.column :record_type, 4
|
9
|
+
t.column :company_id, 12
|
10
|
+
end
|
11
|
+
|
12
|
+
# Create a header section
|
13
|
+
d.header(:align => :left) do |header|
|
14
|
+
# The trap tells FixedWidth which lines should fall into this section
|
15
|
+
header.trap { |line| line[0,4] == 'HEAD' }
|
16
|
+
# Use the boundary template for the columns
|
17
|
+
header.template :boundary
|
18
|
+
end
|
19
|
+
|
20
|
+
d.body do |body|
|
21
|
+
body.trap { |line| line[0,4] =~ /[^(HEAD|FOOT)]/ }
|
22
|
+
body.column :id, 10, :parser => :to_i
|
23
|
+
body.column :first, 10, :align => :left, :group => :name
|
24
|
+
body.column :last, 10, :align => :left, :group => :name
|
25
|
+
body.spacer 3
|
26
|
+
body.column :city, 20 , :group => :address
|
27
|
+
body.column :state, 2 , :group => :address
|
28
|
+
body.column :country, 3, :group => :address
|
29
|
+
end
|
30
|
+
|
31
|
+
d.footer do |footer|
|
32
|
+
footer.trap { |line| line[0,4] == 'FOOT' }
|
33
|
+
footer.template :boundary
|
34
|
+
footer.column :record_count, 10, :parser => :to_i
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
test_data = {
|
39
|
+
:body => [
|
40
|
+
{ :id => 12,
|
41
|
+
:name => { :first => "Ryan", :last => "Wood" },
|
42
|
+
:address => { :city => "Foo", :state => 'SC', :country => "USA" }
|
43
|
+
},
|
44
|
+
{ :id => 13,
|
45
|
+
:name => { :first => "Jo", :last => "Schmo" },
|
46
|
+
:address => { :city => "Bar", :state => "CA", :country => "USA" }
|
47
|
+
}
|
48
|
+
],
|
49
|
+
:header => [{ :record_type => 'HEAD', :company_id => 'ABC' }],
|
50
|
+
:footer => [{ :record_type => 'FOOT', :company_id => 'ABC', :record_count => 2 }]
|
51
|
+
}
|
52
|
+
|
53
|
+
# Generates the file as a string
|
54
|
+
generated = FixedWidth.generate(:simple, test_data)
|
55
|
+
|
56
|
+
sio = StringIO.new
|
57
|
+
sio.write(generated)
|
58
|
+
sio.rewind
|
59
|
+
|
60
|
+
parsed = FixedWidth.parse(sio, :simple)
|
61
|
+
|
62
|
+
puts parsed == test_data
|
data/fixed_width.gemspec
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{fixed_width}
|
8
|
+
s.version = "0.2.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Timon Karnezos"]
|
12
|
+
s.date = %q{2010-05-31}
|
13
|
+
s.description = %q{Shamelessly forked from ryanwood/slither [http://github.com/ryanwood/slither].
|
14
|
+
|
15
|
+
Renamed the gem to be a little clearer as to its purpose. Hate that 'nokogiri' nonsense.
|
16
|
+
}
|
17
|
+
s.email = %q{timon.karnezos@gmail.com}
|
18
|
+
s.extra_rdoc_files = [
|
19
|
+
"README.markdown",
|
20
|
+
"TODO"
|
21
|
+
]
|
22
|
+
s.files = [
|
23
|
+
".gitignore",
|
24
|
+
"COPYING",
|
25
|
+
"HISTORY",
|
26
|
+
"README.markdown",
|
27
|
+
"Rakefile",
|
28
|
+
"TODO",
|
29
|
+
"VERSION",
|
30
|
+
"examples/readme_example.rb",
|
31
|
+
"fixed_width.gemspec",
|
32
|
+
"lib/fixed_width.rb",
|
33
|
+
"lib/fixed_width/column.rb",
|
34
|
+
"lib/fixed_width/core_ext/symbol.rb",
|
35
|
+
"lib/fixed_width/definition.rb",
|
36
|
+
"lib/fixed_width/fixed_width.rb",
|
37
|
+
"lib/fixed_width/generator.rb",
|
38
|
+
"lib/fixed_width/parser.rb",
|
39
|
+
"lib/fixed_width/section.rb",
|
40
|
+
"spec/column_spec.rb",
|
41
|
+
"spec/definition_spec.rb",
|
42
|
+
"spec/fixed_width_spec.rb",
|
43
|
+
"spec/generator_spec.rb",
|
44
|
+
"spec/parser_spec.rb",
|
45
|
+
"spec/section_spec.rb",
|
46
|
+
"spec/spec_helper.rb"
|
47
|
+
]
|
48
|
+
s.homepage = %q{http://github.com/timonk/fixed_width}
|
49
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
50
|
+
s.require_paths = ["lib"]
|
51
|
+
s.rubygems_version = %q{1.3.6}
|
52
|
+
s.summary = %q{A gem that provides a DSL for parsing and writing files of fixed-width records.}
|
53
|
+
s.test_files = [
|
54
|
+
"spec/column_spec.rb",
|
55
|
+
"spec/definition_spec.rb",
|
56
|
+
"spec/fixed_width_spec.rb",
|
57
|
+
"spec/generator_spec.rb",
|
58
|
+
"spec/parser_spec.rb",
|
59
|
+
"spec/section_spec.rb",
|
60
|
+
"spec/spec_helper.rb",
|
61
|
+
"examples/readme_example.rb"
|
62
|
+
]
|
63
|
+
|
64
|
+
if s.respond_to? :specification_version then
|
65
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
66
|
+
s.specification_version = 3
|
67
|
+
|
68
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
69
|
+
else
|
70
|
+
end
|
71
|
+
else
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
@@ -0,0 +1,85 @@
|
|
1
|
+
class FixedWidth
|
2
|
+
class Column
|
3
|
+
DEFAULT_PADDING = ' '
|
4
|
+
DEFAULT_ALIGNMENT = :right
|
5
|
+
DEFAULT_TRUNCATE = false
|
6
|
+
DEFAULT_FORMATTER = :to_s
|
7
|
+
|
8
|
+
attr_reader :name, :length, :alignment, :padding, :truncate, :group, :unpacker
|
9
|
+
|
10
|
+
def initialize(name, length, options={})
|
11
|
+
assert_valid_options(options)
|
12
|
+
@name = name
|
13
|
+
@length = length
|
14
|
+
@alignment = options[:align] || DEFAULT_ALIGNMENT
|
15
|
+
@padding = options[:padding] || DEFAULT_PADDING
|
16
|
+
@truncate = options[:truncate] || DEFAULT_TRUNCATE
|
17
|
+
|
18
|
+
@group = options[:group]
|
19
|
+
|
20
|
+
@unpacker = "A#{@length}"
|
21
|
+
|
22
|
+
@parser = options[:parser]
|
23
|
+
@parser ||= case @alignment
|
24
|
+
when :right then :lstrip
|
25
|
+
when :left then :rstrip
|
26
|
+
end
|
27
|
+
@parser = @parser.to_proc if @parser.is_a?(Symbol)
|
28
|
+
|
29
|
+
@formatter = options[:formatter]
|
30
|
+
@formatter ||= DEFAULT_FORMATTER
|
31
|
+
@formatter = @formatter.to_proc if @formatter.is_a?(Symbol)
|
32
|
+
|
33
|
+
@nil_blank = options[:nil_blank]
|
34
|
+
end
|
35
|
+
|
36
|
+
def parse(value)
|
37
|
+
if @nil_blank && blank?(value)
|
38
|
+
return nil
|
39
|
+
else
|
40
|
+
@parser.call(value)
|
41
|
+
end
|
42
|
+
rescue
|
43
|
+
raise ParserError.new("The value '#{value}' could not be parsed: #{$!}")
|
44
|
+
end
|
45
|
+
|
46
|
+
def format(value)
|
47
|
+
pad(
|
48
|
+
validate_size(
|
49
|
+
@formatter.call(value)
|
50
|
+
)
|
51
|
+
)
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
BLANK_REGEX = /^\s*$/
|
56
|
+
def blank?(value)
|
57
|
+
value =~ BLANK_REGEX
|
58
|
+
end
|
59
|
+
|
60
|
+
def pad(value)
|
61
|
+
case @alignment
|
62
|
+
when :left
|
63
|
+
value.ljust(@length, @padding)
|
64
|
+
when :right
|
65
|
+
value.rjust(@length, @padding)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def assert_valid_options(options)
|
70
|
+
unless options[:align].nil? || [:left, :right].include?(options[:align])
|
71
|
+
raise ArgumentError.new("Option :align only accepts :right (default) or :left")
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def validate_size(result)
|
76
|
+
return result if result.length <= @length
|
77
|
+
raise FixedWidth::FormattedStringExceedsLengthError.new(
|
78
|
+
"The formatted value '#{result}' in column '#{@name}' exceeds the allowed length of #{@length} chararacters.") unless @truncate
|
79
|
+
case @alignment
|
80
|
+
when :right then result[-@length,@length]
|
81
|
+
when :left then result[0,@length]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|