slither 0.99.4 → 0.99.5

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc DELETED
@@ -1,100 +0,0 @@
1
- == slither
2
- by Ryan Wood
3
- http://ryanwood.com
4
-
5
- == DESCRIPTION:
6
-
7
- A simple, clean DSL for describing, writing, and parsing fixed-width text files.
8
-
9
- == FEATURES:
10
-
11
- * Easy DSL syntax
12
- * Can parse and format fixed width files
13
- * Templated sections for reuse
14
-
15
- == SYNOPSIS:
16
-
17
- # Create a Slither::Defintion to describe a file format
18
- Slither.define :simple do |d|
19
-
20
- # This is a template section that can be reused in other sections
21
- d.template :boundary do |t|
22
- t.column :record_type, 4
23
- t.column :company_id, 12
24
- end
25
-
26
- # Create a header section
27
- d.header, :align => :left do |header|
28
- # The trap tells Slither which lines should fall into this section
29
- header.trap { |line| line[0,4] == 'HEAD' }
30
- # Use the boundary template for the columns
31
- header.template :boundary
32
- end
33
-
34
- d.body do |body|
35
- body.trap { |line| line[0,4] =~ /[^(HEAD|FOOT)]/ }
36
- body.column :id, 10, :type => :integer
37
- body.column :name, 10, :align => :left
38
- body.spacer 3
39
- body.column :state, 2
40
- end
41
-
42
- d.footer do |footer|
43
- footer.trap { |line| line[0,4] == 'FOOT' }
44
- footer.template :boundary
45
- footer.column :record_count, 10
46
- end
47
- end
48
-
49
- Supported types are: string, integer, date, float, money, and money_with_implied_decimal.
50
-
51
- Then either feed it a nested struct with data values to create the file in the defined format:
52
-
53
- test_data = {
54
- :body => [
55
- { :id => 12, :name => "Ryan", :state => 'SC' },
56
- { :id => 23, :name => "Joe", :state => 'VA' },
57
- { :id => 42, :name => "Tommy", :state => 'FL' },
58
- ],
59
- :header => { :record_type => 'HEAD', :company_id => 'ABC' },
60
- :footer => { :record_type => 'FOOT', :company_id => 'ABC' }
61
- }
62
-
63
- # Generates the file as a string
64
- puts Slither.generate(:simple, test_data)
65
-
66
- # Writes the file
67
- Slither.write('outfile.txt', :simple, test_data)
68
-
69
- or parse files already in that format into a nested hash:
70
-
71
- parsed_data = Slither.parse('infile.txt', :test).inspect
72
-
73
- == INSTALL:
74
-
75
- sudo gem install slither
76
-
77
- == LICENSE:
78
-
79
- (The MIT License)
80
-
81
- Copyright (c) 2008
82
-
83
- Permission is hereby granted, free of charge, to any person obtaining
84
- a copy of this software and associated documentation files (the
85
- 'Software'), to deal in the Software without restriction, including
86
- without limitation the rights to use, copy, modify, merge, publish,
87
- distribute, sublicense, and/or sell copies of the Software, and to
88
- permit persons to whom the Software is furnished to do so, subject to
89
- the following conditions:
90
-
91
- The above copyright notice and this permission notice shall be
92
- included in all copies or substantial portions of the Software.
93
-
94
- THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
95
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
96
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
97
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
98
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
99
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
100
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -1,49 +0,0 @@
1
- class Slither
2
-
3
- VERSION = '0.99.0'
4
-
5
- class DuplicateColumnNameError < StandardError; end
6
- class RequiredSectionNotFoundError < StandardError; end
7
- class RequiredSectionEmptyError < StandardError; end
8
- class FormattedStringExceedsLengthError < StandardError; end
9
- class ColumnMismatchError < StandardError; end
10
-
11
-
12
- def self.define(name, options = {}, &block)
13
- definition = Definition.new(options)
14
- yield(definition)
15
- definitions[name] = definition
16
- definition
17
- end
18
-
19
- def self.generate(definition_name, data)
20
- definition = definition(definition_name)
21
- raise ArgumentError, "Definition name '#{name}' was not found." unless definition
22
- generator = Generator.new(definition)
23
- generator.generate(data)
24
- end
25
-
26
- def self.write(filename, definition_name, data)
27
- File.open(filename, 'w') do |f|
28
- f.write generate(definition_name, data)
29
- end
30
- end
31
-
32
- def self.parse(filename, definition_name)
33
- raise ArgumentError, "File #{filename} does not exist." unless File.exists?(filename)
34
- definition = definition(definition_name)
35
- raise ArgumentError, "Definition name '#{definition_name}' was not found." unless definition
36
- parser = Parser.new(definition, filename)
37
- parser.parse
38
- end
39
-
40
- private
41
-
42
- def self.definitions
43
- @@definitions ||= {}
44
- end
45
-
46
- def self.definition(name)
47
- definitions[name]
48
- end
49
- end
data/slither.gemspec DELETED
Binary file
data/spec/column_spec.rb DELETED
@@ -1,224 +0,0 @@
1
- require File.join(File.dirname(__FILE__), 'spec_helper')
2
-
3
- describe Slither::Column do
4
- before(:each) do
5
- @name = :id
6
- @length = 5
7
- @column = Slither::Column.new(@name, @length)
8
- end
9
-
10
- describe "when being created" do
11
- it "should have a name" do
12
- @column.name.should == @name
13
- end
14
-
15
- it "should have a length" do
16
- @column.length.should == @length
17
- end
18
-
19
- it "should have a default padding" do
20
- @column.padding.should == :space
21
- end
22
-
23
- it "should have a default alignment" do
24
- @column.alignment.should == :right
25
- end
26
-
27
- it "should return a proper formatter" do
28
- @column.send(:formatter).should == "%5s"
29
- end
30
- end
31
-
32
- describe "when specifying an alignment" do
33
- before(:each) do
34
- @column = Slither::Column.new(@name, @length, :align => :left)
35
- end
36
-
37
- it "should only accept :right or :left for an alignment" do
38
- lambda{ Slither::Column.new(@name, @length, :align => :bogus) }.should raise_error(ArgumentError, "Option :align only accepts :right (default) or :left")
39
- end
40
-
41
- it "should override the default alignment" do
42
- @column.alignment.should == :left
43
- end
44
- end
45
-
46
- describe "when specifying padding" do
47
- before(:each) do
48
- @column = Slither::Column.new(@name, @length, :padding => :zero)
49
- end
50
-
51
- it "should accept only :space or :zero" do
52
- lambda{ Slither::Column.new(@name, @length, :padding => :bogus) }.should raise_error(ArgumentError, "Option :padding only accepts :space (default) or :zero")
53
- end
54
-
55
- it "should override the default padding" do
56
- @column.padding.should == :zero
57
- end
58
- end
59
-
60
- it "should return the proper unpack value for a string" do
61
- @column.send(:unpacker).should == 'A5'
62
- end
63
-
64
- describe "when parsing a value from a file" do
65
- it "should default to a string" do
66
- @column.parse(' name ').should == 'name'
67
- @column.parse(' 234').should == '234'
68
- @column.parse('000000234').should == '000000234'
69
- @column.parse('12.34').should == '12.34'
70
- end
71
-
72
- it "should support the integer type" do
73
- @column = Slither::Column.new(:amount, 10, :type=> :integer)
74
- @column.parse('234 ').should == 234
75
- @column.parse(' 234').should == 234
76
- @column.parse('00000234').should == 234
77
- @column.parse('Ryan ').should == 0
78
- @column.parse('00023.45').should == 23
79
- end
80
-
81
- it "should support the float type" do
82
- @column = Slither::Column.new(:amount, 10, :type=> :float)
83
- @column.parse(' 234.45').should == 234.45
84
- @column.parse('234.5600').should == 234.56
85
- @column.parse(' 234').should == 234.0
86
- @column.parse('00000234').should == 234.0
87
- @column.parse('Ryan ').should == 0
88
- @column.parse('00023.45').should == 23.45
89
- end
90
-
91
- it "should support the money_with_implied_decimal type" do
92
- @column = Slither::Column.new(:amount, 10, :type=> :money_with_implied_decimal)
93
- @column.parse(' 23445').should == 234.45
94
- end
95
-
96
- it "should support the date type" do
97
- @column = Slither::Column.new(:date, 10, :type => :date)
98
- dt = @column.parse('2009-08-22')
99
- dt.should be_a(Date)
100
- dt.to_s.should == '2009-08-22'
101
- end
102
-
103
- it "should use the format option with date type if available" do
104
- @column = Slither::Column.new(:date, 10, :type => :date, :format => "%m%d%Y")
105
- dt = @column.parse('08222009')
106
- dt.should be_a(Date)
107
- dt.to_s.should == '2009-08-22'
108
- end
109
- end
110
-
111
- describe "when applying formatting options" do
112
- it "should return a proper formatter" do
113
- @column = Slither::Column.new(@name, @length, :align => :left)
114
- @column.send(:formatter).should == "%-5s"
115
- end
116
-
117
- it "should respect a right alignment" do
118
- @column = Slither::Column.new(@name, @length, :align => :right)
119
- @column.format(25).should == ' 25'
120
- end
121
-
122
- it "should respect a left alignment" do
123
- @column = Slither::Column.new(@name, @length, :align => :left)
124
- @column.format(25).should == '25 '
125
- end
126
-
127
- it "should respect padding with spaces" do
128
- @column = Slither::Column.new(@name, @length, :padding => :space)
129
- @column.format(25).should == ' 25'
130
- end
131
-
132
- it "should respect padding with zeros with integer types" do
133
- @column = Slither::Column.new(@name, @length, :type => :integer, :padding => :zero)
134
- @column.format(25).should == '00025'
135
- end
136
-
137
- describe "that is a float type" do
138
- it "should respect padding with zeros aligned right" do
139
- @column = Slither::Column.new(@name, @length, :type => :float, :padding => :zero, :align => :right)
140
- @column.format(4.45).should == '04.45'
141
- end
142
-
143
- it "should respect padding with zeros aligned left" do
144
- @column = Slither::Column.new(@name, @length, :type => :float, :padding => :zero, :align => :left)
145
- @column.format(4.45).should == '4.450'
146
- end
147
- end
148
- end
149
-
150
- describe "when formatting values for a file" do
151
- it "should default to a string" do
152
- @column = Slither::Column.new(:name, 10)
153
- @column.format('Bill').should == ' Bill'
154
- end
155
-
156
- describe "whose size is too long" do
157
- it "should raise an error if truncate is false" do
158
- @value = "XX" * @length
159
- lambda { @column.format(@value) }.should raise_error(
160
- Slither::FormattedStringExceedsLengthError,
161
- "The formatted value '#{@value}' in column '#{@name}' exceeds the allowed length of #{@length} chararacters."
162
- )
163
- end
164
-
165
- it "should truncate from the left if truncate is true and aligned left" do
166
- @column = Slither::Column.new(@name, @length, :truncate => true, :align => :left)
167
- @column.format("This is too long").should == "This "
168
- end
169
-
170
- it "should truncate from the right if truncate is true and aligned right" do
171
- @column = Slither::Column.new(@name, @length, :truncate => true, :align => :right)
172
- @column.format("This is too long").should == " long"
173
- end
174
- end
175
-
176
- it "should support the integer type" do
177
- @column = Slither::Column.new(:amount, 10, :type => :integer)
178
- @column.format(234).should == ' 234'
179
- @column.format('234').should == ' 234'
180
- end
181
-
182
- it "should support the float type" do
183
- @column = Slither::Column.new(:amount, 10, :type => :float)
184
- @column.format(234.45).should == ' 234.45'
185
- @column.format('234.4500').should == ' 234.45'
186
- @column.format('3').should == ' 3.0'
187
- end
188
-
189
- it "should support the float type with a format" do
190
- @column = Slither::Column.new(:amount, 10, :type => :float, :format => "%.3f")
191
- @column.format(234.45).should == ' 234.450'
192
- @column.format('234.4500').should == ' 234.450'
193
- @column.format('3').should == ' 3.000'
194
- end
195
-
196
- it "should support the float type with a format, alignment and padding" do
197
- @column = Slither::Column.new(:amount, 10, :type => :float, :format => "%.2f", :align => :left, :padding => :zero)
198
- @column.format(234.45).should == '234.450000'
199
- @column = Slither::Column.new(:amount, 10, :type => :float, :format => "%.2f", :align => :right, :padding => :zero)
200
- @column.format('234.400').should == '0000234.40'
201
- @column = Slither::Column.new(:amount, 10, :type => :float, :format => "%.4f", :align => :left, :padding => :space)
202
- @column.format('3').should == '3.0000 '
203
- end
204
-
205
- it "should support the money_with_implied_decimal type" do
206
- @column = Slither::Column.new(:amount, 10, :type=> :money_with_implied_decimal)
207
- @column.format(234.450).should == " 23445"
208
- @column.format(12.34).should == " 1234"
209
- end
210
-
211
- it "should support the date type" do
212
- dt = Date.new(2009, 8, 22)
213
- @column = Slither::Column.new(:date, 10, :type => :date)
214
- @column.format(dt).should == '2009-08-22'
215
- end
216
-
217
- it "should support the date type with a :format" do
218
- dt = Date.new(2009, 8, 22)
219
- @column = Slither::Column.new(:date, 8, :type => :date, :format => "%m%d%Y")
220
- @column.format(dt).should == '08222009'
221
- end
222
- end
223
-
224
- end
@@ -1,85 +0,0 @@
1
- require File.join(File.dirname(__FILE__), 'spec_helper')
2
-
3
- describe Slither::Definition do
4
- before(:each) do
5
- end
6
-
7
- describe "when specifying alignment" do
8
- it "should have an alignment option" do
9
- d = Slither::Definition.new :align => :right
10
- d.options[:align].should == :right
11
- end
12
-
13
- it "should default to being right aligned" do
14
- d = Slither::Definition.new
15
- d.options[:align].should == :right
16
- end
17
-
18
- it "should override the default if :align is passed to the section" do
19
- section = mock('section', :null_object => true)
20
- Slither::Section.should_receive(:new).with('name', {:align => :left}).and_return(section)
21
- d = Slither::Definition.new
22
- d.options[:align].should == :right
23
- d.section('name', :align => :left) {}
24
- end
25
- end
26
-
27
- describe "when creating a section" do
28
- before(:each) do
29
- @d = Slither::Definition.new
30
- @section = mock('section', :null_object => true)
31
- end
32
-
33
- it "should create and yield a new section object" do
34
- yielded = nil
35
- @d.section :header do |section|
36
- yielded = section
37
- end
38
- yielded.should be_a(Slither::Section)
39
- @d.sections.first.should == yielded
40
- end
41
-
42
- it "should magically build a section from an unknown method" do
43
- Slither::Section.should_receive(:new).with(:header, anything()).and_return(@section)
44
- @d.header {}
45
- end
46
-
47
- it "should not create duplicate section names" do
48
- lambda { @d.section(:header) {} }.should_not raise_error(ArgumentError)
49
- lambda { @d.section(:header) {} }.should raise_error(ArgumentError, "Reserved or duplicate section name: 'header'")
50
- end
51
-
52
- it "should throw an error if a reserved section name is used" do
53
- lambda { @d.section(:spacer) {} }.should raise_error(ArgumentError, "Reserved or duplicate section name: 'spacer'")
54
- end
55
- end
56
-
57
- describe "when creating a template" do
58
- before(:each) do
59
- @d = Slither::Definition.new
60
- @section = mock('section', :null_object => true)
61
- end
62
-
63
- it "should create a new section" do
64
- Slither::Section.should_receive(:new).with(:row, anything()).and_return(@section)
65
- @d.template(:row) {}
66
- end
67
-
68
- it "should yield the new section" do
69
- Slither::Section.should_receive(:new).with(:row, anything()).and_return(@section)
70
- yielded = nil
71
- @d.template :row do |section|
72
- yielded = section
73
- end
74
- yielded.should == @section
75
- end
76
-
77
- it "add a section to the templates collection" do
78
- @d.should have(0).templates
79
- @d.template :row do |t|
80
- t.column :id, 3
81
- end
82
- @d.should have(1).templates
83
- end
84
- end
85
- end
@@ -1,42 +0,0 @@
1
- require File.join(File.dirname(__FILE__), 'spec_helper')
2
-
3
- describe Slither::Generator do
4
- before(:each) do
5
- @definition = Slither.define :test do |d|
6
- d.header do |h|
7
- h.trap { |line| line[0,4] == 'HEAD' }
8
- h.column :type, 4
9
- h.column :file_id, 10
10
- end
11
- d.body do |b|
12
- b.trap { |line| line[0,4] =~ /[^(HEAD|FOOT)]/ }
13
- b.column :first, 10
14
- b.column :last, 10
15
- end
16
- d.footer do |f|
17
- f.trap { |line| line[0,4] == 'FOOT' }
18
- f.column :type, 4
19
- f.column :file_id, 10
20
- end
21
- end
22
- @data = {
23
- :header => [ {:type => "HEAD", :file_id => "1" }],
24
- :body => [
25
- {:first => "Paul", :last => "Hewson" },
26
- {:first => "Dave", :last => "Evans" }
27
- ],
28
- :footer => [ {:type => "FOOT", :file_id => "1" }]
29
- }
30
- @generator = Slither::Generator.new(@definition)
31
- end
32
-
33
- it "should raise an error if there is no data for a required section" do
34
- @data.delete :header
35
- lambda { @generator.generate(@data) }.should raise_error(Slither::RequiredSectionEmptyError, "Required section 'header' was empty.")
36
- end
37
-
38
- it "should generate a string" do
39
- expected = "HEAD 1\n Paul Hewson\n Dave Evans\nFOOT 1"
40
- @generator.generate(@data).should == expected
41
- end
42
- end
data/spec/parser_spec.rb DELETED
@@ -1,74 +0,0 @@
1
- require File.join(File.dirname(__FILE__), 'spec_helper')
2
-
3
- describe Slither::Parser do
4
- before(:each) do
5
- @definition = mock('definition', :sections => [])
6
- @file = mock("file", :gets => nil)
7
- @file_name = 'test.txt'
8
- @parser = Slither::Parser.new(@definition, @file_name)
9
- end
10
-
11
- it "should open and yield the source file" do
12
- File.should_receive(:open).with(@file_name, 'r').and_yield(@file)
13
- @parser.parse
14
- end
15
-
16
- describe "when parsing sections" do
17
- before(:each) do
18
- @definition = Slither.define :test do |d|
19
- d.header do |h|
20
- h.trap { |line| line[0,4] == 'HEAD' }
21
- h.column :type, 4
22
- h.column :file_id, 10
23
- end
24
- d.body do |b|
25
- b.trap { |line| line[0,4] =~ /[^(HEAD|FOOT)]/ }
26
- b.column :first, 10
27
- b.column :last, 10
28
- end
29
- d.footer do |f|
30
- f.trap { |line| line[0,4] == 'FOOT' }
31
- f.column :type, 4
32
- f.column :file_id, 10
33
- end
34
- end
35
- File.should_receive(:open).with(@file_name, 'r').and_yield(@file)
36
- @parser = Slither::Parser.new(@definition, @file_name)
37
- end
38
-
39
- it "should add lines to the proper sections" do
40
- @file.should_receive(:gets).exactly(4).times.and_return(
41
- 'HEAD 1',
42
- ' Paul Hewson',
43
- ' Dave Evans',
44
- 'FOOT 1',
45
- nil
46
- )
47
- expected = {
48
- :header => [ {:type => "HEAD", :file_id => "1" }],
49
- :body => [
50
- {:first => "Paul", :last => "Hewson" },
51
- {:first => "Dave", :last => "Evans" }
52
- ],
53
- :footer => [ {:type => "FOOT", :file_id => "1" }]
54
- }
55
- result = @parser.parse
56
- result.should == expected
57
- end
58
-
59
- it "should allow optional sections to be skipped" do
60
- @definition.sections[0].optional = true
61
- @definition.sections[2].optional = true
62
- @file.should_receive(:gets).twice.and_return(' Paul Hewson', nil)
63
- expected = { :body => [ {:first => "Paul", :last => "Hewson" } ] }
64
- @parser.parse.should == expected
65
- end
66
-
67
- it "should raise an error if a required section is not found" do
68
- @file.should_receive(:gets).twice.and_return(' Ryan Wood', nil)
69
- lambda { @parser.parse }.should raise_error(Slither::RequiredSectionNotFoundError, "Required section 'header' was not found.")
70
- end
71
-
72
- # it "raise an error if a section limit is over run"
73
- end
74
- end