hflr 0.10.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,40 @@
1
+
2
+ if test(?e, PROJ.test.file) or not PROJ.test.files.to_a.empty?
3
+ require 'rake/testtask'
4
+
5
+ namespace :test do
6
+
7
+ Rake::TestTask.new(:run) do |t|
8
+ t.libs = PROJ.libs
9
+ t.test_files = if test(?f, PROJ.test.file) then [PROJ.test.file]
10
+ else PROJ.test.files end
11
+ t.ruby_opts += PROJ.ruby_opts
12
+ t.ruby_opts += PROJ.test.opts
13
+ end
14
+
15
+ if HAVE_RCOV
16
+ desc 'Run rcov on the unit tests'
17
+ task :rcov => :clobber_rcov do
18
+ opts = PROJ.rcov.opts.dup << '-o' << PROJ.rcov.dir
19
+ opts = opts.join(' ')
20
+ files = if test(?f, PROJ.test.file) then [PROJ.test.file]
21
+ else PROJ.test.files end
22
+ files = files.join(' ')
23
+ sh "#{RCOV} #{files} #{opts}"
24
+ end
25
+
26
+ task :clobber_rcov do
27
+ rm_r 'coverage' rescue nil
28
+ end
29
+ end
30
+
31
+ end # namespace :test
32
+
33
+ desc 'Alias to test:run'
34
+ task :test => 'test:run'
35
+
36
+ task :clobber => 'test:clobber_rcov' if HAVE_RCOV
37
+
38
+ end
39
+
40
+ # EOF
@@ -0,0 +1,36 @@
1
+ if HAVE_ZENTEST
2
+
3
+ # --------------------------------------------------------------------------
4
+ if test(?e, PROJ.test.file) or not PROJ.test.files.to_a.empty?
5
+ require 'autotest'
6
+
7
+ namespace :test do
8
+ task :autotest do
9
+ Autotest.run
10
+ end
11
+ end
12
+
13
+ desc "Run the autotest loop"
14
+ task :autotest => 'test:autotest'
15
+
16
+ end # if test
17
+
18
+ # --------------------------------------------------------------------------
19
+ if HAVE_SPEC_RAKE_SPECTASK and not PROJ.spec.files.to_a.empty?
20
+ require 'autotest/rspec'
21
+
22
+ namespace :spec do
23
+ task :autotest do
24
+ load '.autotest' if test(?f, '.autotest')
25
+ Autotest::Rspec.run
26
+ end
27
+ end
28
+
29
+ desc "Run the autotest loop"
30
+ task :autotest => 'spec:autotest'
31
+
32
+ end # if rspec
33
+
34
+ end # if HAVE_ZENTEST
35
+
36
+ # EOF
@@ -0,0 +1,5 @@
1
+ CJoe Smith 55455025.53
2
+ O0005233110-10-2008
3
+ CJane Smith 55404015.25
4
+ O0054933310-11-2008
5
+ O0075789110-12-2008
@@ -0,0 +1,2 @@
1
+ Joe Smith 55455025.53
2
+ Jane Smith 55404015.25
@@ -0,0 +1,48 @@
1
+ require '../lib/fl_record_file'
2
+
3
+
4
+ # Read a file with only one record type (no record type markers)
5
+
6
+ # metadata for customer file
7
+ Column = Struct.new(:name,:start,:len)
8
+ columns = {:customer=>[
9
+ Column.new("name",1,25),
10
+ Column.new("zip",26,5),
11
+ Column.new("balance",31,5)]}
12
+
13
+ customer_file = FixedLengthRecordFile.new(File.new("customers.dat"), :customer, columns, 1, [:line_number])
14
+
15
+ customer_file.each do |record|
16
+ puts "Customer #{customer_file.line_number.to_s} #{record.name} #{record.zip} "
17
+ end
18
+
19
+
20
+ # You can get the values by attribute name like a hash
21
+ def show(record)
22
+ print record.members.map{|m| m.to_s + ": " + record[m].to_s}.join(", ") + "\n"
23
+ end
24
+
25
+ # metadata for customer_orders file
26
+ layouts = {:customer=>[
27
+ Column.new("name",1,25),
28
+ Column.new("zip",26,5),
29
+ Column.new("balance",31,5)],
30
+ :order=>[
31
+ Column.new("order_num",1,8),
32
+ Column.new("date",9,10),]}
33
+
34
+
35
+ customer_orders_file = FixedLengthRecordFile.new(
36
+ File.new("customer_orders.dat"),
37
+ {"C"=>:customer,"O"=>:order},# Use these characters as record type markers
38
+ layouts,
39
+ 0, # shift parsed string 0 columns to the left of the indicated start column
40
+ {:customer=>[:line_number,:record_type],:order=>[:line_number,:record_type]}) # Add these columns to the indicated record types post read
41
+
42
+
43
+ customer_orders_file.each do |record|
44
+ show record
45
+ end
46
+
47
+
48
+
@@ -0,0 +1,212 @@
1
+
2
+
3
+
4
+ class FixedLengthRecordFileTest < Test::Unit::TestCase
5
+
6
+
7
+
8
+ def setup
9
+ var_type = Struct.new(:name,:start,:len)
10
+
11
+ # Split up your metadata by record type.
12
+
13
+ @layouts =
14
+ {:household=>[var_type.new(:rectypeh,1,1), var_type.new(:phone,2,1), var_type.new("mortgage",3,1)],
15
+ :person=>[var_type.new(:rectypep,1,1), var_type.new("age",2,3), var_type.new("sex",5,1), var_type.new("marst",6,1)]}
16
+
17
+ # Give the values used in the data for each record type
18
+ @record_types ={"H"=>:household,"P"=>:person}
19
+ end
20
+
21
+ def teardown
22
+ end
23
+
24
+
25
+ def test_initialize
26
+ sample_data_path = File.dirname(__FILE__)
27
+ fwf = FixedLengthRecordFile.new(
28
+ File.new("#{sample_data_path}/sample.dat"),
29
+ @record_types, # Record types to read from the file, all others will be ignored
30
+ @layouts,# metadata for all record types
31
+ 1, # column 0 starts at logical location 1
32
+ {:household=>[:people],:person=>[:household_id,:pserial]} # extra columns by record type
33
+ )
34
+
35
+ # Extra columns + record_type accessors should have been created
36
+ hh_struct = fwf.record_template[:household].record_structure.new
37
+ assert hh_struct.respond_to?(:record_type),"household record should have record_type method"
38
+ p_struct = fwf.record_template[:person].record_structure.new
39
+ assert p_struct.respond_to?(:household_id),"Should have household_id as an extra column"
40
+ assert p_struct.respond_to?(:record_type),"Should have record_type method"
41
+
42
+
43
+ fwf = FixedLengthRecordFile.new(
44
+ File.new("#{sample_data_path}/sample.dat"),
45
+ @record_types, # Record types to read from the file, all others will be ignored
46
+ @layouts,# metadata for all record types
47
+ 1 )
48
+
49
+ # Should still have added the record_type method but none of the others
50
+ hh_struct = fwf.record_template[:household].record_structure.new
51
+ assert hh_struct.respond_to?(:record_type),"Should have record_type method"
52
+ assert !hh_struct.respond_to?(:people)
53
+ end
54
+
55
+ def test_get_record_type
56
+ sample_data_path = File.dirname(__FILE__)
57
+ fwf = FixedLengthRecordFile.new(
58
+ File.new("#{sample_data_path}/sample.dat"),
59
+ @record_types, # Record types to read from the file, all others will be ignored
60
+ @layouts,# metadata for all record types
61
+ 1, # column 0 starts at logical location 1
62
+ {:household=>[:people],:person=>[:household_id,:pserial]} # extra columns by record type
63
+ )
64
+ assert_nil fwf.get_record_type(nil)
65
+ assert_equal :household,fwf.get_record_type("H123")
66
+ assert_equal :person,fwf.get_record_type("P1234")
67
+ assert_equal nil, fwf.get_record_type("C123")
68
+
69
+ end
70
+
71
+
72
+
73
+
74
+ def test_build_record
75
+ sample_data_path = File.dirname(__FILE__)
76
+ fwf = FixedLengthRecordFile.new(
77
+ File.new("#{sample_data_path}/sample.dat"),
78
+ @record_types, # Record types to read from the file, all others will be ignored
79
+ @layouts,# metadata for all record types
80
+ 1, # column 0 starts at logical location 1
81
+ {:household=>[:people],:person=>[:household_id,:pserial]} # extra columns by record type
82
+ )
83
+
84
+ assert_equal nil, fwf.build_record(nil)
85
+ rec = fwf.build_record("H012345666665555444333")
86
+ assert_equal :household,rec[:record_type]
87
+
88
+ assert_raise RuntimeError do
89
+ fwf.build_record("c23abbbc")
90
+ end
91
+
92
+ end
93
+
94
+
95
+ def test_each
96
+ sample_data_path = File.dirname(__FILE__)
97
+ fwf = FixedLengthRecordFile.new(
98
+ File.new("#{sample_data_path}/sample.dat"),
99
+ @record_types,
100
+ @layouts,
101
+ 1,
102
+ {:household=>[:record_type,:people],:person=>[:household_id,:pserial,:record_type]})
103
+
104
+ records = []
105
+
106
+ fwf.each do |record|
107
+ records << record
108
+ end
109
+ assert records.first.respond_to?(:record_type)
110
+ assert_equal :person, records.last.record_type
111
+ assert_equal :household,records[0].record_type
112
+ assert_equal :person, records[1].record_type
113
+
114
+ end
115
+
116
+ def test_next_record
117
+ sample_data_path = File.dirname(__FILE__)
118
+ fwf = FixedLengthRecordFile.new(
119
+ File.new("#{sample_data_path}/sample.dat"), # data is in this file
120
+ @record_types, # Records of different types have these labels
121
+ @layouts, # metadata for creating record structs
122
+ 1, # All metadata starting column locations are to be shifted 1 left
123
+ {:household=>[:people],:person=>[:household_id,:pserial]}) # Extra columns not to come from metadata
124
+
125
+ records = []
126
+ while rec = fwf.next_record do
127
+ records << rec
128
+ end
129
+
130
+ assert_equal :household, records.first.record_type
131
+
132
+ assert records.first.respond_to?(:record_type)
133
+
134
+ # The last record is a person type and should not have a 'people' accessor
135
+ assert !records.last.respond_to?(:people)
136
+
137
+ # Should have added these accessors from the extra_columns argument above
138
+ assert records.first.respond_to?(:people)
139
+ assert records.last.respond_to?(:household_id)
140
+
141
+ assert_equal :household,records[0].record_type
142
+ assert_equal :person, records[1].record_type
143
+
144
+ end
145
+
146
+ def test_open
147
+ record = Struct.new(:rectypeh,:phone,:mortgage,:record_type)
148
+
149
+ sample_data_path = File.dirname(__FILE__)
150
+ FixedLengthRecordFile.open(
151
+ "#{sample_data_path}/sample_out.dat", # data is in this file
152
+ "w",# open file for writing
153
+ @record_types, # Records of different types have these labels
154
+ @layouts, # metadata for creating record structs
155
+ 1) do |fwf|# All metadata starting column locations are to be shifted 1 left
156
+ assert_equal FixedLengthRecordFile, fwf.class
157
+ fwf << record.new("H",1,2,:household)
158
+ fwf << ["H",1,3,:household]
159
+ end
160
+
161
+ assert File.exists?("#{sample_data_path}/sample.dat") # data is in this file
162
+
163
+ var = Struct.new(:name,:start,:len)
164
+ l = {:customer=>[var.new("name",1,25),var.new("age",26,3)]}
165
+ fwf = FixedLengthRecordFile.new(File.new("sample2_out.dat","w"),
166
+ :customer, l,1)
167
+
168
+ fwf << ["joe",25,:customer]
169
+ fwf.close
170
+
171
+ end
172
+
173
+
174
+ def test_line_type
175
+ sample_data_path = File.dirname(__FILE__)
176
+ fwf = FixedLengthRecordFile.new(
177
+ File.new("#{sample_data_path}/sample.dat"),
178
+ @record_types, # Record types to read from the file, all others will be ignored
179
+ @layouts,# metadata for all record types
180
+ 1, # column 0 starts at logical location 1
181
+ {:household=>[:people],:person=>[:household_id,:pserial]} # extra columns by record type
182
+ )
183
+
184
+ assert_equal :unknown, fwf.line_type(nil)
185
+ assert_equal :household,fwf.line_type("H123")
186
+ assert_equal :person,fwf.line_type("P123")
187
+ assert_equal :unknown, fwf.line_type("C123")
188
+ end
189
+
190
+ def test_get_next_known_line_type
191
+ sample_data_path = File.dirname(__FILE__)
192
+ fwf = FixedLengthRecordFile.new(
193
+ File.new("#{sample_data_path}/sample_activities.dat"),
194
+ @record_types, # Record types to read from the file, all others will be ignored
195
+ @layouts,# metadata for all record types
196
+ 1, # column 0 starts at logical location 1
197
+ {:household=>[:people],:person=>[:household_id,:pserial]} # extra columns by record type
198
+ )
199
+ # By reading the sample_activities file with only the household and person record types know
200
+ # we should get the activity and who records to be skipped.
201
+ while rec=fwf.get_next_known_line_type
202
+
203
+ unless rec.strip.empty?
204
+
205
+ assert ["P","H"].include?(rec[0..0])
206
+
207
+ end
208
+ end
209
+
210
+ end
211
+
212
+ end
@@ -0,0 +1,174 @@
1
+
2
+
3
+
4
+ class RecordTemplateTest < Test::Unit::TestCase
5
+
6
+
7
+ Var = Struct.new(:name,:start,:len)
8
+
9
+ # Some real world metadata. This is only layout for the starting portion of the ATUS / CPS household record
10
+ # which actually extends to beyond column 117
11
+ AtusHH =[[:RECTYPEH, 1, 1,],
12
+ [:CASEID, 2, 14],
13
+ [:AGEYCHILD, 16, 3],
14
+ [:SERIAL, 19, 7],
15
+ [:HH_NUMADULTS, 26, 2],
16
+ [:FAMBUS_RESP, 28, 2],
17
+ [:FAMBUS_OTHER, 30, 2],
18
+ [:FAMBUS_SPOUSE, 32, 2],
19
+ [:FAMBUS, 34, 2],
20
+ [:HH_CHILD, 36, 2],
21
+ [:HH_NUMKIDS, 38, 2],
22
+ [:HH_SIZE, 40, 3],
23
+ [:HH_NUMEMPLD, 43, 3],
24
+ [:FAMINCOME, 46, 3]]
25
+
26
+
27
+
28
+ def setup
29
+
30
+ hh_vars = AtusHH.map{|v| Var.new(v[0],v[1],v[2])}
31
+
32
+ @vars = {:household=>hh_vars,
33
+ :person=>[Var.new("age",2,3),Var.new("sex",5,1)],
34
+ :activity=>[Var.new("where",1,5),Var.new("activity",6,5)],
35
+ :who=>[Var.new("relatew",1,2)]}
36
+ @record_types = {"H"=>:household,"P"=>:person,"A"=>:activity,"W"=>:who}
37
+ @record_type_symbols = @record_types.invert
38
+ end
39
+
40
+ def test_create
41
+ record_layouts = @vars # variables by record type
42
+ templates = HFLR::RecordTemplate.create(record_layouts, @record_type_symbols, 1)
43
+ assert_equal @vars.keys, templates.keys
44
+
45
+ household_field_pattern = templates[:household].field_pattern
46
+ person_field_pattern =templates[:person].field_pattern
47
+ activity_field_pattern = templates[:activity].field_pattern
48
+ who_field_pattern = templates[:who].field_pattern
49
+
50
+
51
+ assert household_field_pattern.is_a?(String)
52
+ assert person_field_pattern.size>2,"field pattern should have at least one variable"
53
+ assert activity_field_pattern.size>2,"field pattern for activity should have at least one variable"
54
+ assert who_field_pattern.size>2,"field pattern for who should have at least one variable"
55
+
56
+
57
+ household_record_struct = templates[:household].record_structure.new
58
+ assert household_record_struct.respond_to?(:HH_SIZE)
59
+
60
+ end
61
+
62
+ def test_create_template_class
63
+ template = HFLR::RecordTemplate.create_template_class(:person,@record_type_symbols[:person], @vars[:person],1,{})
64
+ assert template.respond_to?(:field_pattern)
65
+ assert template.respond_to?(:record_structure)
66
+ record_structure = template.record_structure
67
+ assert record_structure.new.is_a?(Struct)
68
+ assert record_structure.new.respond_to?(:record_type)
69
+
70
+ end
71
+
72
+
73
+ def test_get_pattern
74
+ household_layout = @vars[:household]
75
+
76
+ pattern = HFLR::RecordTemplate.get_pattern(household_layout)
77
+ assert_equal "@1A1",pattern[0..3]
78
+
79
+ # Adjust the location ('@') leftward (the metadata refers to the 0th column as column 1.)
80
+ pattern = HFLR::RecordTemplate.get_pattern(household_layout,1)
81
+ assert_equal "@0A1",pattern[0..3]
82
+
83
+ vars_in_pattern = pattern.scan("A").size
84
+ assert_equal household_layout.size, vars_in_pattern
85
+
86
+ end
87
+
88
+ def test_build_record
89
+ templates = HFLR::RecordTemplate.create(@vars,@record_type_symbols,1)
90
+ hh_str = "1200501010500069980000001020000000000000020009960200009999999999999999991330299902305030201034300000000037110550997797000000000007700100000000200411000000000"
91
+
92
+ # from a string to a record struct
93
+ household_rec = templates[:household].build_record(hh_str)
94
+
95
+ assert household_rec.is_a?(Struct)
96
+ assert household_rec.values.size> @vars[:household].size,"Should be values for the extra columns"
97
+
98
+ # Check a few things...
99
+ assert_equal 1,household_rec[:SERIAL].to_i
100
+
101
+
102
+ assert_equal 1,household_rec.SERIAL.to_i
103
+
104
+ assert_equal "1",household_rec[0]
105
+ assert_equal "1",household_rec.RECTYPEH
106
+ assert_equal "1",household_rec[:RECTYPEH]
107
+
108
+
109
+
110
+ end
111
+
112
+ def test_build_line
113
+ templates = HFLR::RecordTemplate.create(@vars,@record_type_symbols,1)
114
+
115
+ hh_str = "H200501010500069980000001020000000000000020009960200009999999999999999991330299902305030201034300000000037110550997797000000000007700100000000200411000000000"
116
+
117
+ # from a string to a record struct
118
+ household_rec = templates[:household].build_record(hh_str)
119
+ assert_equal "002",hh_str[39..41]
120
+ assert_equal "002",household_rec.HH_SIZE
121
+
122
+
123
+ # back to a string
124
+ new_hh_str = templates[:household].build_line(household_rec)
125
+
126
+
127
+ assert_equal "002",new_hh_str[39..41]
128
+
129
+ # Some of the values in hh_str won't be in new_hh_str because not all data
130
+ # in hh_str is mapped by household_layout, but the mapped variables should have
131
+ # the same values.
132
+
133
+ @vars[:household].each do |v|
134
+ format_str = "@#{(v.start-1).to_s}a#{v.len.to_s}"
135
+ orig_data = hh_str.unpack(format_str)
136
+ new_data = new_hh_str.unpack(format_str)
137
+
138
+
139
+ assert_equal new_data, orig_data,"Comparing #{v.name} #{format_str}"
140
+ end
141
+ end
142
+
143
+
144
+ def test_format_fields
145
+ templates = HFLR::RecordTemplate.create(@vars,@record_type_symbols,1)
146
+
147
+ formatted_fields = templates[:who].send(:format_fields,[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15])
148
+
149
+ widths = @vars[:who].map{|v| v.len}
150
+ formatted_fields.size.times do |i|
151
+ assert_equal formatted_fields[i].size, widths[i],"Width of #{@vars[:who][i].name} should have been #{widths[i].to_s}"
152
+ end
153
+
154
+ end
155
+
156
+ def test_write_format
157
+ templates = HFLR::RecordTemplate.create(@vars,@record_type_symbols,1)
158
+
159
+ assert_equal "abc",templates[:activity].send(:right_format,"abc",3)
160
+ assert_equal "abc ",templates[:activity].send(:right_format,"abc",6)
161
+ assert_equal "3",templates[:activity].send(:right_format,3,1)
162
+ assert_equal "005",templates[:activity].send(:right_format,5,3)
163
+ assert_equal "ZZZ",templates[:activity].send(:right_format,-999998,3)
164
+ end
165
+
166
+
167
+
168
+ end
169
+
170
+
171
+
172
+
173
+
174
+