dbf 2.0.10 → 2.0.11

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,63 +1,63 @@
1
1
  module DBF
2
2
  ENCODINGS = {
3
- "01" => "cp437", # U.S. MSDOS
4
- "02" => "cp850", # International MSDOS
5
- "03" => "cp1252", # Windows ANSI
6
- "08" => "cp865", # Danish OEM
7
- "09" => "cp437", # Dutch OEM
8
- "0a" => "cp850", # Dutch OEM*
9
- "0b" => "cp437", # Finnish OEM
10
- "0d" => "cp437", # French OEM
11
- "0e" => "cp850", # French OEM*
12
- "0f" => "cp437", # German OEM
13
- "10" => "cp850", # German OEM*
14
- "11" => "cp437", # Italian OEM
15
- "12" => "cp850", # Italian OEM*
16
- "13" => "cp932", # Japanese Shift-JIS
17
- "14" => "cp850", # Spanish OEM*
18
- "15" => "cp437", # Swedish OEM
19
- "16" => "cp850", # Swedish OEM*
20
- "17" => "cp865", # Norwegian OEM
21
- "18" => "cp437", # Spanish OEM
22
- "19" => "cp437", # English OEM (Britain)
23
- "1a" => "cp850", # English OEM (Britain)*
24
- "1b" => "cp437", # English OEM (U.S.)
25
- "1c" => "cp863", # French OEM (Canada)
26
- "1d" => "cp850", # French OEM*
27
- "1f" => "cp852", # Czech OEM
28
- "22" => "cp852", # Hungarian OEM
29
- "23" => "cp852", # Polish OEM
30
- "24" => "cp860", # Portuguese OEM
31
- "25" => "cp850", # Portuguese OEM*
32
- "26" => "cp866", # Russian OEM
33
- "37" => "cp850", # English OEM (U.S.)*
34
- "40" => "cp852", # Romanian OEM
35
- "4d" => "cp936", # Chinese GBK (PRC)
36
- "4e" => "cp949", # Korean (ANSI/OEM)
37
- "4f" => "cp950", # Chinese Big5 (Taiwan)
38
- "50" => "cp874", # Thai (ANSI/OEM)
39
- "57" => "cp1252", # ANSI
40
- "58" => "cp1252", # Western European ANSI
41
- "59" => "cp1252", # Spanish ANSI
42
- "64" => "cp852", # Eastern European MSDOS
43
- "65" => "cp866", # Russian MSDOS
44
- "66" => "cp865", # Nordic MSDOS
45
- "67" => "cp861", # Icelandic MSDOS
46
- "6a" => "cp737", # Greek MSDOS (437G)
47
- "6b" => "cp857", # Turkish MSDOS
48
- "6c" => "cp863", # FrenchCanadian MSDOS
49
- "78" => "cp950", # Taiwan Big 5
50
- "79" => "cp949", # Hangul (Wansung)
51
- "7a" => "cp936", # PRC GBK
52
- "7b" => "cp932", # Japanese Shift-JIS
53
- "7c" => "cp874", # Thai Windows/MSDOS
54
- "86" => "cp737", # Greek OEM
55
- "87" => "cp852", # Slovenian OEM
56
- "88" => "cp857", # Turkish OEM
57
- "c8" => "cp1250", # Eastern European Windows
58
- "c9" => "cp1251", # Russian Windows
59
- "ca" => "cp1254", # Turkish Windows
60
- "cb" => "cp1253", # Greek Windows
61
- "cc" => "cp1257", # Baltic Windows
3
+ '01' => 'cp437', # U.S. MS-DOS
4
+ '02' => 'cp850', # International MS-DOS
5
+ '03' => 'cp1252', # Windows ANSI
6
+ '08' => 'cp865', # Danish OEM
7
+ '09' => 'cp437', # Dutch OEM
8
+ '0a' => 'cp850', # Dutch OEM*
9
+ '0b' => 'cp437', # Finnish OEM
10
+ '0d' => 'cp437', # French OEM
11
+ '0e' => 'cp850', # French OEM*
12
+ '0f' => 'cp437', # German OEM
13
+ '10' => 'cp850', # German OEM*
14
+ '11' => 'cp437', # Italian OEM
15
+ '12' => 'cp850', # Italian OEM*
16
+ '13' => 'cp932', # Japanese Shift-JIS
17
+ '14' => 'cp850', # Spanish OEM*
18
+ '15' => 'cp437', # Swedish OEM
19
+ '16' => 'cp850', # Swedish OEM*
20
+ '17' => 'cp865', # Norwegian OEM
21
+ '18' => 'cp437', # Spanish OEM
22
+ '19' => 'cp437', # English OEM (Britain)
23
+ '1a' => 'cp850', # English OEM (Britain)*
24
+ '1b' => 'cp437', # English OEM (U.S.)
25
+ '1c' => 'cp863', # French OEM (Canada)
26
+ '1d' => 'cp850', # French OEM*
27
+ '1f' => 'cp852', # Czech OEM
28
+ '22' => 'cp852', # Hungarian OEM
29
+ '23' => 'cp852', # Polish OEM
30
+ '24' => 'cp860', # Portuguese OEM
31
+ '25' => 'cp850', # Portuguese OEM*
32
+ '26' => 'cp866', # Russian OEM
33
+ '37' => 'cp850', # English OEM (U.S.)*
34
+ '40' => 'cp852', # Romanian OEM
35
+ '4d' => 'cp936', # Chinese GBK (PRC)
36
+ '4e' => 'cp949', # Korean (ANSI/OEM)
37
+ '4f' => 'cp950', # Chinese Big5 (Taiwan)
38
+ '50' => 'cp874', # Thai (ANSI/OEM)
39
+ '57' => 'cp1252', # ANSI
40
+ '58' => 'cp1252', # Western European ANSI
41
+ '59' => 'cp1252', # Spanish ANSI
42
+ '64' => 'cp852', # Eastern European MS-DOS
43
+ '65' => 'cp866', # Russian MS-DOS
44
+ '66' => 'cp865', # Nordic MS-DOS
45
+ '67' => 'cp861', # Icelandic MS-DOS
46
+ '6a' => 'cp737', # Greek MS-DOS (437G)
47
+ '6b' => 'cp857', # Turkish MS-DOS
48
+ '6c' => 'cp863', # French-Canadian MS-DOS
49
+ '78' => 'cp950', # Taiwan Big 5
50
+ '79' => 'cp949', # Hangul (Wansung)
51
+ '7a' => 'cp936', # PRC GBK
52
+ '7b' => 'cp932', # Japanese Shift-JIS
53
+ '7c' => 'cp874', # Thai Windows/MS-DOS
54
+ '86' => 'cp737', # Greek OEM
55
+ '87' => 'cp852', # Slovenian OEM
56
+ '88' => 'cp857', # Turkish OEM
57
+ 'c8' => 'cp1250', # Eastern European Windows
58
+ 'c9' => 'cp1251', # Russian Windows
59
+ 'ca' => 'cp1254', # Turkish Windows
60
+ 'cb' => 'cp1253', # Greek Windows
61
+ 'cc' => 'cp1257', # Baltic Windows
62
62
  }
63
63
  end
@@ -15,7 +15,7 @@ module DBF
15
15
  end
16
16
 
17
17
  def unpack_header
18
- data.unpack("H2 x3 V v2 x17H2")
18
+ data.unpack('H2 x3 V v2 x17H2')
19
19
  end
20
20
  end
21
21
  end
@@ -9,13 +9,13 @@ module DBF
9
9
  end
10
10
 
11
11
  def initialize(data, version)
12
- @data, @version = data, version
12
+ @data = data
13
+ @version = version
13
14
  end
14
15
 
15
16
  def get(start_block)
16
- if start_block > 0
17
- build_memo start_block
18
- end
17
+ return nil unless start_block > 0
18
+ build_memo start_block
19
19
  end
20
20
 
21
21
  def close
@@ -28,15 +28,15 @@ module DBF
28
28
 
29
29
  private
30
30
 
31
- def offset(start_block) #nodoc
31
+ def offset(start_block) # nodoc
32
32
  start_block * block_size
33
33
  end
34
34
 
35
- def content_size(memo_size) #nodoc
35
+ def content_size(memo_size) # nodoc
36
36
  (memo_size - block_size) + BLOCK_HEADER_SIZE
37
37
  end
38
38
 
39
- def block_content_size #nodoc
39
+ def block_content_size # nodoc
40
40
  @block_content_size ||= block_size - BLOCK_HEADER_SIZE
41
41
  end
42
42
 
@@ -1,15 +1,16 @@
1
1
  module DBF
2
2
  module Memo
3
3
  class Dbase3 < Base
4
- def build_memo(start_block) #nodoc
4
+ def build_memo(start_block) # nodoc
5
5
  @data.seek offset(start_block)
6
- memo_string = ""
7
- begin
6
+ memo_string = ''
7
+ loop do
8
8
  block = @data.read(BLOCK_SIZE).gsub(/(\000|\032)/, '')
9
9
  memo_string << block
10
- end until block.size < BLOCK_SIZE
10
+ break if block.size < BLOCK_SIZE
11
+ end
11
12
  memo_string
12
13
  end
13
14
  end
14
15
  end
15
- end
16
+ end
@@ -1,9 +1,9 @@
1
1
  module DBF
2
2
  module Memo
3
3
  class Dbase4 < Base
4
- def build_memo(start_block) #nodoc
4
+ def build_memo(start_block) # nodoc
5
5
  @data.seek offset(start_block)
6
- @data.read(@data.read(BLOCK_HEADER_SIZE).unpack("x4L").first)
6
+ @data.read(@data.read(BLOCK_HEADER_SIZE).unpack('x4L').first)
7
7
  end
8
8
  end
9
9
  end
@@ -3,10 +3,10 @@ module DBF
3
3
  class Foxpro < Base
4
4
  FPT_HEADER_SIZE = 512
5
5
 
6
- def build_memo(start_block) #nodoc
6
+ def build_memo(start_block) # nodoc
7
7
  @data.seek offset(start_block)
8
8
 
9
- memo_type, memo_size, memo_string = @data.read(block_size).unpack("NNa*")
9
+ memo_type, memo_size, memo_string = @data.read(block_size).unpack('NNa*')
10
10
  return nil unless memo_type == 1 && memo_size > 0
11
11
 
12
12
  if memo_size > block_content_size
@@ -22,7 +22,7 @@ module DBF
22
22
 
23
23
  private
24
24
 
25
- def block_size #nodoc
25
+ def block_size # nodoc
26
26
  @block_size ||= begin
27
27
  @data.rewind
28
28
  @data.read(FPT_HEADER_SIZE).unpack('x6n').first || 0
@@ -30,4 +30,4 @@ module DBF
30
30
  end
31
31
  end
32
32
  end
33
- end
33
+ end
@@ -9,7 +9,9 @@ module DBF
9
9
  # @memo [DBF::Memo]
10
10
  def initialize(data, columns, version, memo)
11
11
  @data = StringIO.new(data)
12
- @columns, @version, @memo = columns, version, memo
12
+ @columns = columns
13
+ @version = version
14
+ @memo = memo
13
15
  end
14
16
 
15
17
  # Equality
@@ -24,7 +26,7 @@ module DBF
24
26
  #
25
27
  # @return [Array]
26
28
  def to_a
27
- @columns.map {|column| attributes[column.name]}
29
+ @columns.map { |column| attributes[column.name] }
28
30
  end
29
31
 
30
32
  # Do all search parameters match?
@@ -32,17 +34,17 @@ module DBF
32
34
  # @param [Hash] options
33
35
  # @return [Boolean]
34
36
  def match?(options)
35
- options.all? {|key, value| self[key] == value}
37
+ options.all? { |key, value| self[key] == value }
36
38
  end
37
39
 
38
40
  # Reads attributes by column name
39
41
  #
40
42
  # @param [String, Symbol] key
41
- def [](key)
42
- key = key.to_s
43
- if attributes.has_key?(key)
43
+ def [](name)
44
+ key = name.to_s
45
+ if attributes.key?(key)
44
46
  attributes[key]
45
- elsif index = underscored_column_names.index(key)
47
+ elsif (index = underscored_column_names.index(key))
46
48
  attributes[@columns[index].name]
47
49
  end
48
50
  end
@@ -51,7 +53,8 @@ module DBF
51
53
  #
52
54
  # @return [Hash]
53
55
  def attributes
54
- @attributes ||= Hash[@columns.map {|column| [column.name, init_attribute(column)]}]
56
+ @attributes ||=
57
+ Hash[@columns.map { |column| [column.name, init_attribute(column)] }]
55
58
  end
56
59
 
57
60
  # Overrides standard Object.respond_to? to return true if a
@@ -69,8 +72,8 @@ module DBF
69
72
 
70
73
  private
71
74
 
72
- def method_missing(method, *args) #nodoc
73
- if index = underscored_column_names.index(method.to_s)
75
+ def method_missing(method, *args) # nodoc
76
+ if (index = underscored_column_names.index(method.to_s))
74
77
  attributes[@columns[index].name]
75
78
  else
76
79
  super
@@ -78,15 +81,15 @@ module DBF
78
81
  end
79
82
 
80
83
  def underscored_column_names # nodoc
81
- @underscored_column_names ||= @columns.map {|column| column.underscored_name}
84
+ @underscored_column_names ||= @columns.map(&:underscored_name)
82
85
  end
83
86
 
84
- def init_attribute(column) #nodoc
87
+ def init_attribute(column) # nodoc
85
88
  value = column.memo? ? memo(column) : unpack_data(column)
86
89
  column.type_cast(value)
87
90
  end
88
91
 
89
- def memo(column) #nodoc
92
+ def memo(column) # nodoc
90
93
  if @memo
91
94
  @memo.get(memo_start_block(column))
92
95
  else
@@ -96,15 +99,14 @@ module DBF
96
99
  end
97
100
  end
98
101
 
99
- def memo_start_block(column) #nodoc
102
+ def memo_start_block(column) # nodoc
100
103
  format = 'V' if %w(30 31).include?(@version)
101
104
  unpack_data(column, format).to_i
102
105
  end
103
106
 
104
- def unpack_data(column, format=nil) #nodoc
107
+ def unpack_data(column, format = nil) # nodoc
105
108
  format ||= "a#{column.length}"
106
109
  @data.read(column.length).unpack(format).first
107
110
  end
108
-
109
111
  end
110
112
  end
@@ -1,4 +1,5 @@
1
1
  module DBF
2
+ # The Schema module is mixin for the Table class
2
3
  module Schema
3
4
  # Generate an ActiveRecord::Schema
4
5
  #
@@ -11,33 +11,33 @@ module DBF
11
11
  DBF_HEADER_SIZE = 32
12
12
 
13
13
  VERSIONS = {
14
- "02" => "FoxBase",
15
- "03" => "dBase III without memo file",
16
- "04" => "dBase IV without memo file",
17
- "05" => "dBase V without memo file",
18
- "07" => "Visual Objects 1.x",
19
- "30" => "Visual FoxPro",
20
- "31" => "Visual FoxPro with AutoIncrement field",
21
- "43" => "dBASE IV SQL table files, no memo",
22
- "63" => "dBASE IV SQL system files, no memo",
23
- "7b" => "dBase IV with memo file",
24
- "83" => "dBase III with memo file",
25
- "87" => "Visual Objects 1.x with memo file",
26
- "8b" => "dBase IV with memo file",
27
- "8e" => "dBase IV with SQL table",
28
- "cb" => "dBASE IV SQL table files, with memo",
29
- "f5" => "FoxPro with memo file",
30
- "fb" => "FoxPro without memo file"
14
+ '02' => 'FoxBase',
15
+ '03' => 'dBase III without memo file',
16
+ '04' => 'dBase IV without memo file',
17
+ '05' => 'dBase V without memo file',
18
+ '07' => 'Visual Objects 1.x',
19
+ '30' => 'Visual FoxPro',
20
+ '31' => 'Visual FoxPro with AutoIncrement field',
21
+ '43' => 'dBASE IV SQL table files, no memo',
22
+ '63' => 'dBASE IV SQL system files, no memo',
23
+ '7b' => 'dBase IV with memo file',
24
+ '83' => 'dBase III with memo file',
25
+ '87' => 'Visual Objects 1.x with memo file',
26
+ '8b' => 'dBase IV with memo file',
27
+ '8e' => 'dBase IV with SQL table',
28
+ 'cb' => 'dBASE IV SQL table files, with memo',
29
+ 'f5' => 'FoxPro with memo file',
30
+ 'fb' => 'FoxPro without memo file'
31
31
  }
32
32
 
33
33
  FOXPRO_VERSIONS = {
34
- "30" => "Visual FoxPro",
35
- "31" => "Visual FoxPro with AutoIncrement field",
36
- "f5" => "FoxPro with memo file",
37
- "fb" => "FoxPro without memo file"
34
+ '30' => 'Visual FoxPro',
35
+ '31' => 'Visual FoxPro with AutoIncrement field',
36
+ 'f5' => 'FoxPro with memo file',
37
+ 'fb' => 'FoxPro without memo file'
38
38
  }
39
39
 
40
- attr_reader :header
40
+ attr_reader :header
41
41
  attr_accessor :encoding # Source encoding (for ex. :cp1251)
42
42
 
43
43
  # Opens a DBF::Table
@@ -62,16 +62,14 @@ module DBF
62
62
  # @param [optional String, StringIO] memo Path to the memo file or a StringIO object
63
63
  # @param [optional String, Encoding] encoding Name of the encoding or an Encoding object
64
64
  def initialize(data, memo = nil, encoding = nil)
65
- begin
66
- @data = open_data(data)
67
- @data.rewind
68
- @header = Header.new(@data.read(DBF_HEADER_SIZE), supports_encoding?)
69
- @encoding = encoding || header.encoding
70
- @memo = open_memo(data, memo)
71
- yield self if block_given?
72
- rescue Errno::ENOENT => error
73
- raise DBF::FileNotFoundError.new("file not found: #{data}")
74
- end
65
+ @data = open_data(data)
66
+ @data.rewind
67
+ @header = Header.new(@data.read(DBF_HEADER_SIZE), supports_encoding?)
68
+ @encoding = encoding || header.encoding
69
+ @memo = open_memo(data, memo)
70
+ yield self if block_given?
71
+ rescue Errno::ENOENT
72
+ raise DBF::FileNotFoundError, "file not found: #{data}"
75
73
  end
76
74
 
77
75
  # @return [TrueClass, FalseClass]
@@ -106,7 +104,7 @@ module DBF
106
104
  #
107
105
  # @yield [nil, DBF::Record]
108
106
  def each
109
- header.record_count.times {|i| yield record(i)}
107
+ header.record_count.times { |i| yield record(i) }
110
108
  end
111
109
 
112
110
  # Retrieve a record by index number.
@@ -117,9 +115,8 @@ module DBF
117
115
  # @return [DBF::Record, NilClass]
118
116
  def record(index)
119
117
  seek_to_record(index)
120
- if !deleted_record?
121
- DBF::Record.new(@data.read(header.record_length), columns, version, @memo)
122
- end
118
+ return nil if deleted_record?
119
+ DBF::Record.new(@data.read(header.record_length), columns, version, @memo)
123
120
  end
124
121
 
125
122
  alias_method :row, :record
@@ -171,10 +168,11 @@ module DBF
171
168
  # table.find :first, :first_name => "Keith"
172
169
  #
173
170
  # The <b>command</b> may be a record index, :all, or :first.
174
- # <b>options</b> is optional and, if specified, should be a hash where the keys correspond
175
- # to column names in the database. The values will be matched exactly with the value
176
- # in the database. If you specify more than one key, all values must match in order
177
- # for the record to be returned. The equivalent SQL would be "WHERE key1 = 'value1'
171
+ # <b>options</b> is optional and, if specified, should be a hash where the
172
+ # keys correspond to column names in the database. The values will be
173
+ # matched exactly with the value in the database. If you specify more
174
+ # than one key, all values must match in order for the record to be
175
+ # returned. The equivalent SQL would be "WHERE key1 = 'value1'
178
176
  # AND key2 = 'value2'".
179
177
  #
180
178
  # @param [Fixnum, Symbol] command
@@ -185,7 +183,7 @@ module DBF
185
183
  when Fixnum
186
184
  record(command)
187
185
  when Array
188
- command.map {|i| record(i)}
186
+ command.map { |i| record(i) }
189
187
  when :all
190
188
  find_all(options, &block)
191
189
  when :first
@@ -204,7 +202,7 @@ module DBF
204
202
  #
205
203
  # @return [String]
206
204
  def column_names
207
- columns.map { |column| column.name }
205
+ columns.map(&:name)
208
206
  end
209
207
 
210
208
  # Is string encoding supported?
@@ -219,7 +217,7 @@ module DBF
219
217
  ''.respond_to?(:encoding)
220
218
  end
221
219
 
222
- def supports_iconv? #nodoc
220
+ def supports_iconv? # nodoc
223
221
  require 'iconv'
224
222
  true
225
223
  rescue
@@ -228,10 +226,10 @@ module DBF
228
226
 
229
227
  private
230
228
 
231
- def build_columns #nodoc
229
+ def build_columns # nodoc
232
230
  columns = []
233
231
  @data.seek(DBF_HEADER_SIZE)
234
- while !["\0", "\r"].include?(first_byte = @data.read(1))
232
+ until ["\0", "\r"].include?(first_byte = @data.read(1))
235
233
  column_data = first_byte + @data.read(DBF_HEADER_SIZE - 1)
236
234
  name, type, length, decimal = column_data.unpack('a10 x a x4 C2')
237
235
  columns << column_class.new(self, name, type, length, decimal)
@@ -239,55 +237,43 @@ module DBF
239
237
  columns
240
238
  end
241
239
 
242
-
243
- def foxpro? #nodoc
240
+ def foxpro? # nodoc
244
241
  FOXPRO_VERSIONS.keys.include? version
245
242
  end
246
243
 
247
- def column_class #nodoc
244
+ def column_class # nodoc
248
245
  @column_class ||= foxpro? ? Column::Foxpro : Column::Dbase
249
246
  end
250
247
 
251
- def memo_class #nodoc
248
+ def memo_class # nodoc
252
249
  @memo_class ||= if foxpro?
253
250
  Memo::Foxpro
254
251
  else
255
- if version == "83"
256
- Memo::Dbase3
257
- else
258
- Memo::Dbase4
259
- end
252
+ version == '83' ? Memo::Dbase3 : Memo::Dbase4
260
253
  end
261
254
  end
262
255
 
263
- def column_count #nodoc
264
- @column_count ||= ((header.header_length - DBF_HEADER_SIZE + 1) / DBF_HEADER_SIZE).to_i
265
- end
266
-
267
- def open_data(data) #nodoc
256
+ def open_data(data) # nodoc
268
257
  data.is_a?(StringIO) ? data : File.open(data, 'rb')
269
258
  end
270
259
 
271
- def open_memo(data, memo = nil) #nodoc
272
- if memo.is_a? StringIO
273
- memo_class.new(memo, version)
274
- elsif memo
275
- memo_class.open(memo, version)
260
+ def open_memo(data, memo = nil) # nodoc
261
+ if memo
262
+ meth = memo.is_a?(StringIO) ? :new : :open
263
+ memo_class.send(meth, memo, version)
276
264
  elsif !data.is_a? StringIO
277
- files = Dir.glob(memo_search_path(data))
265
+ files = Dir.glob(memo_search_path data)
278
266
  files.any? ? memo_class.open(files.first, version) : nil
279
- else
280
- nil
281
267
  end
282
268
  end
283
269
 
284
- def memo_search_path(io) #nodoc
270
+ def memo_search_path(io) # nodoc
285
271
  dirname = File.dirname(io)
286
272
  basename = File.basename(io, '.*')
287
273
  "#{dirname}/#{basename}*.{fpt,FPT,dbt,DBT}"
288
274
  end
289
275
 
290
- def find_all(options) #nodoc
276
+ def find_all(options) # nodoc
291
277
  map do |record|
292
278
  if record && record.match?(options)
293
279
  yield record if block_given?
@@ -296,25 +282,24 @@ module DBF
296
282
  end.compact
297
283
  end
298
284
 
299
- def find_first(options) #nodoc
300
- detect {|record| record && record.match?(options)}
285
+ def find_first(options) # nodoc
286
+ detect { |record| record && record.match?(options) }
301
287
  end
302
288
 
303
- def deleted_record? #nodoc
289
+ def deleted_record? # nodoc
304
290
  @data.read(1).unpack('a') == ['*']
305
291
  end
306
292
 
307
- def seek(offset) #nodoc
293
+ def seek(offset) # nodoc
308
294
  @data.seek header.header_length + offset
309
295
  end
310
296
 
311
- def seek_to_record(index) #nodoc
297
+ def seek_to_record(index) # nodoc
312
298
  seek(index * header.record_length)
313
299
  end
314
300
 
315
- def csv_class #nodoc
301
+ def csv_class # nodoc
316
302
  @csv_class ||= CSV.const_defined?(:Reader) ? FCSV : CSV
317
303
  end
318
304
  end
319
-
320
305
  end