dbf 1.0.5 → 1.0.6

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,3 +1,9 @@
1
+ == 1.0.6
2
+
3
+ * DBF::Table now includes the Enumerable module
4
+ * Return nil for memo values if the memo file is missing
5
+ * Finder conditions now support the original and downcased/underscored column names
6
+
1
7
  == 1.0.5
2
8
 
3
9
  * Strip non-ascii characters from column names
data/Manifest.txt CHANGED
@@ -2,9 +2,8 @@ History.txt
2
2
  Manifest.txt
3
3
  README.txt
4
4
  Rakefile
5
- benchmarks/performance.rb
6
- benchmarks/seek_benchmark.rb
7
5
  bin/dbf
6
+ dbf.gemspec
8
7
  lib/dbf.rb
9
8
  lib/dbf/column.rb
10
9
  lib/dbf/globals.rb
data/README.txt CHANGED
@@ -70,14 +70,6 @@ An example of migrating a DBF book table to ActiveRecord using a migration:
70
70
  end
71
71
  end
72
72
 
73
- == Large databases
74
-
75
- DBF::Table defaults to loading all records into memory. This may not be what
76
- you want, especially if the database is large. To disable this behavior, set
77
- the in_memory option to false during initialization.
78
-
79
- table = DBF::Table.new("old_data.dbf", :in_memory => false)
80
-
81
73
  == Command-line utility
82
74
 
83
75
  A small command-line utility called dbf is installed along with the gem.
@@ -97,7 +89,7 @@ A small command-line utility called dbf is installed along with the gem.
97
89
 
98
90
  (The MIT Licence)
99
91
 
100
- Copyright (c) 2006-2007 Keith Morrison <keithm@infused.org>
92
+ Copyright (c) 2006-2008 Keith Morrison <keithm@infused.org>
101
93
 
102
94
  Permission is hereby granted, free of charge, to any person
103
95
  obtaining a copy of this software and associated documentation
data/Rakefile CHANGED
@@ -2,7 +2,7 @@ require 'hoe'
2
2
  require 'spec/rake/spectask'
3
3
 
4
4
  PKG_NAME = "dbf"
5
- PKG_VERSION = "1.0.5"
5
+ PKG_VERSION = "1.0.6"
6
6
  PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
7
7
 
8
8
  Hoe.new PKG_NAME, PKG_VERSION do |p|
@@ -12,12 +12,12 @@ Hoe.new PKG_NAME, PKG_VERSION do |p|
12
12
  p.summary = "A small fast library for reading dBase, xBase, Clipper and FoxPro database files."
13
13
  p.description = p.paragraphs_of("README.txt", 1..3).join("\n\n")
14
14
  p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
15
- p.url = "http://dbf.rubyforge.org"
15
+ p.url = "http://github.com/infused/dm-dbf/tree/master"
16
16
  p.need_tar = true
17
17
  p.need_zip = true
18
18
  end
19
19
 
20
- task :default => [:spec]
20
+ task :default => :spec
21
21
 
22
22
  desc "Run specs"
23
23
  Spec::Rake::SpecTask.new :spec do |t|
@@ -28,4 +28,9 @@ desc "Run spec docs"
28
28
  Spec::Rake::SpecTask.new :specdoc do |t|
29
29
  t.spec_opts = ["-f specdoc"]
30
30
  t.spec_files = FileList['spec/**/*spec.rb']
31
+ end
32
+
33
+ desc "Generate gemspec"
34
+ task :gemspec do |t|
35
+ `rake debug_gem > dbf.gemspec`
31
36
  end
data/dbf.gemspec ADDED
@@ -0,0 +1,35 @@
1
+ (in /Users/keithm/projects/dbf)
2
+ Gem::Specification.new do |s|
3
+ s.name = %q{dbf}
4
+ s.version = "1.0.6"
5
+
6
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
7
+ s.authors = ["Keith Morrison"]
8
+ s.date = %q{2008-06-30}
9
+ s.default_executable = %q{dbf}
10
+ s.description = %q{DBF is a small fast library for reading dBase, xBase, Clipper and FoxPro database files. It is written completely in Ruby and has no external dependencies. Copyright (c) 2006-2007 Keith Morrison <keithm@infused.org, www.infused.org> * Official project page: http://rubyforge.org/projects/dbf * API Documentation: http://dbf.rubyforge.org/docs * To report bugs: http://www.rubyforge.org/tracker/?group_id=2009 * Questions: Email keithm@infused.org and put DBF somewhere in the subject line}
11
+ s.email = %q{keithm@infused.org}
12
+ s.executables = ["dbf"]
13
+ s.extra_rdoc_files = ["History.txt", "Manifest.txt", "README.txt", "spec/fixtures/dbase_83_schema.txt"]
14
+ s.files = ["History.txt", "Manifest.txt", "README.txt", "Rakefile", "bin/dbf", "dbf.gemspec", "lib/dbf.rb", "lib/dbf/column.rb", "lib/dbf/globals.rb", "lib/dbf/record.rb", "lib/dbf/table.rb", "spec/fixtures/dbase_03.dbf", "spec/fixtures/dbase_30.dbf", "spec/fixtures/dbase_30.fpt", "spec/fixtures/dbase_83.dbf", "spec/fixtures/dbase_83.dbt", "spec/fixtures/dbase_83_schema.txt", "spec/fixtures/dbase_8b.dbf", "spec/fixtures/dbase_8b.dbt", "spec/fixtures/dbase_f5.dbf", "spec/fixtures/dbase_f5.fpt", "spec/functional/dbf_shared.rb", "spec/functional/format_03_spec.rb", "spec/functional/format_30_spec.rb", "spec/functional/format_83_spec.rb", "spec/functional/format_8b_spec.rb", "spec/functional/format_f5_spec.rb", "spec/spec_helper.rb", "spec/unit/column_spec.rb", "spec/unit/record_spec.rb", "spec/unit/table_spec.rb"]
15
+ s.has_rdoc = true
16
+ s.homepage = %q{http://github.com/infused/dm-dbf/tree/master}
17
+ s.rdoc_options = ["--main", "README.txt"]
18
+ s.require_paths = ["lib"]
19
+ s.rubyforge_project = %q{dbf}
20
+ s.rubygems_version = %q{1.2.0}
21
+ s.summary = %q{A small fast library for reading dBase, xBase, Clipper and FoxPro database files.}
22
+
23
+ if s.respond_to? :specification_version then
24
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
25
+ s.specification_version = 2
26
+
27
+ if current_version >= 3 then
28
+ s.add_runtime_dependency(%q<hoe>, [">= 1.6.0"])
29
+ else
30
+ s.add_dependency(%q<hoe>, [">= 1.6.0"])
31
+ end
32
+ else
33
+ s.add_dependency(%q<hoe>, [">= 1.6.0"])
34
+ end
35
+ end
data/lib/dbf/column.rb CHANGED
@@ -2,6 +2,8 @@ module DBF
2
2
  class ColumnLengthError < DBFError; end
3
3
 
4
4
  class Column
5
+ include Helpers
6
+
5
7
  attr_reader :name, :type, :length, :decimal
6
8
 
7
9
  def initialize(name, type, length, decimal)
@@ -10,8 +12,7 @@ module DBF
10
12
  end
11
13
 
12
14
  def schema_definition
13
- "\"#{underscore(name)}\", " +
14
- case type
15
+ data_type = case type
15
16
  when "N" # number
16
17
  if decimal > 0
17
18
  ":float"
@@ -26,19 +27,12 @@ module DBF
26
27
  ":text"
27
28
  else
28
29
  ":string, :limit => #{length}"
29
- end +
30
- "\n"
30
+ end
31
+
32
+ "\"#{underscore(name)}\", #{data_type}\n"
31
33
  end
32
34
 
33
35
  private
34
-
35
- def underscore(camel_cased_word)
36
- camel_cased_word.to_s.gsub(/::/, '/').
37
- gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
38
- gsub(/([a-z\d])([A-Z])/,'\1_\2').
39
- tr("-", "_").
40
- downcase
41
- end
42
36
 
43
37
  def strip_non_ascii_chars(s)
44
38
  clean = ''
data/lib/dbf/globals.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  module DBF
2
2
  DBF_HEADER_SIZE = 32
3
3
  FPT_HEADER_SIZE = 512
4
- FPT_BLOCK_HEADER_SIZE = 8
4
+ BLOCK_HEADER_SIZE = 8
5
5
  DATE_REGEXP = /([\d]{4})([\d]{2})([\d]{2})/
6
6
  VERSION_DESCRIPTIONS = {
7
7
  "02" => "FoxBase",
@@ -20,9 +20,17 @@ module DBF
20
20
 
21
21
  MS_PER_SECOND = 1000
22
22
  MS_PER_MINUTE = MS_PER_SECOND * 60
23
- MS_PER_HOUR = MS_PER_MINUTE * 60
23
+ MS_PER_HOUR = MS_PER_MINUTE * 60
24
24
 
25
25
  class DBFError < StandardError; end
26
- class InvalidColumnName < DBFError; end
27
- class InvalidColumnLength < DBFError; end
26
+
27
+ module Helpers
28
+ def underscore(camel_cased_word)
29
+ camel_cased_word.to_s.gsub(/::/, '/').
30
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
31
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
32
+ tr("-", "_").
33
+ downcase
34
+ end
35
+ end
28
36
  end
data/lib/dbf/record.rb CHANGED
@@ -1,33 +1,34 @@
1
1
  module DBF
2
2
  class Record
3
- attr_reader :attributes
3
+ include Helpers
4
4
 
5
- @@accessors_defined = false
5
+ attr_reader :attributes
6
6
 
7
7
  def initialize(table)
8
8
  @table, @data, @memo = table, table.data, table.memo
9
- @attributes = {}
10
9
  initialize_values(table.columns)
11
10
  define_accessors
12
- self
11
+ end
12
+
13
+ def ==(other)
14
+ other.respond_to?(:attributes) && other.attributes == attributes
13
15
  end
14
16
 
15
17
  private
16
18
 
17
19
  def define_accessors
18
- return if @@accessors_defined
19
20
  @table.columns.each do |column|
20
21
  underscored_column_name = underscore(column.name)
21
- if @table.options[:accessors] && !respond_to?(underscored_column_name)
22
+ unless respond_to?(underscored_column_name)
22
23
  self.class.send :define_method, underscored_column_name do
23
24
  @attributes[column.name]
24
25
  end
25
- @@accessors_defined = true
26
26
  end
27
27
  end
28
28
  end
29
29
 
30
30
  def initialize_values(columns)
31
+ @attributes = {}
31
32
  columns.each do |column|
32
33
  @attributes[column.name] = case column.type
33
34
  when 'N' # number
@@ -54,11 +55,13 @@ module DBF
54
55
  else
55
56
  unpack_string(column).strip
56
57
  end
58
+ @attributes[underscore(column.name)] = @attributes[column.name]
59
+ @attributes
57
60
  end
58
61
  end
59
62
 
60
63
  def unpack_column(column)
61
- @data.read(column.length).unpack("a#{column.length}")
64
+ @data.read(column.length).to_s.unpack("a#{column.length}")
62
65
  end
63
66
 
64
67
  def unpack_string(column)
@@ -78,42 +81,53 @@ module DBF
78
81
  end
79
82
 
80
83
  def read_memo(start_block)
81
- return nil if start_block <= 0 || @table.memo_block_size.nil?
82
- @memo.seek(start_block * @table.memo_block_size)
83
- if @table.memo_file_format == :fpt
84
- memo_type, memo_size, memo_string = @memo.read(@table.memo_block_size).unpack("NNa56")
84
+ return nil if !@table.has_memo_file? || start_block < 1
85
+
86
+ @table.memo_file_format == :fpt ? build_fpt_memo(start_block) : build_dbt_memo(start_block)
87
+ end
88
+
89
+ def build_fpt_memo(start_block)
90
+ @memo.seek(start_block * memo_block_size)
85
91
 
86
- # skip the memo if it isn't text
87
- return nil unless memo_type == 1
88
-
89
- memo_block_content_size = @table.memo_block_size - FPT_BLOCK_HEADER_SIZE
90
- if memo_size > memo_block_content_size
91
- memo_string << @memo.read(memo_size - @table.memo_block_size + FPT_BLOCK_HEADER_SIZE)
92
- elsif memo_size > 0 and memo_size < memo_block_content_size
93
- memo_string = memo_string[0, memo_size]
94
- end
92
+ memo_type, memo_size, memo_string = @memo.read(memo_block_size).unpack("NNa56")
93
+ return nil unless memo_type == 1 and memo_size > 0
94
+
95
+ if memo_size > memo_block_content_size
96
+ memo_string << @memo.read(memo_content_size(memo_size))
95
97
  else
96
- case @table.version
97
- when "83" # dbase iii
98
- memo_string = ""
99
- loop do
100
- memo_string << block = @memo.read(512)
101
- break if block.strip.size < 512
102
- end
103
- when "8b" # dbase iv
104
- memo_type, memo_size = @memo.read(8).unpack("LL")
105
- memo_string = @memo.read(memo_size)
98
+ memo_string = memo_string[0, memo_size]
99
+ end
100
+ memo_string
101
+ end
102
+
103
+ def build_dbt_memo(start_block)
104
+ @memo.seek(start_block * memo_block_size)
105
+
106
+ case @table.version
107
+ when "83" # dbase iii
108
+ memo_string = ""
109
+ loop do
110
+ memo_string << block = @memo.read(memo_block_size)
111
+ break if block.rstrip.size < memo_block_size
106
112
  end
113
+ when "8b" # dbase iv
114
+ memo_type, memo_size = @memo.read(BLOCK_HEADER_SIZE).unpack("LL")
115
+ memo_string = @memo.read(memo_size)
107
116
  end
108
117
  memo_string
109
118
  end
110
119
 
111
- def underscore(camel_cased_word)
112
- camel_cased_word.to_s.gsub(/::/, '/').
113
- gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
114
- gsub(/([a-z\d])([A-Z])/,'\1_\2').
115
- tr("-", "_").
116
- downcase
120
+ def memo_block_size
121
+ @memo_block_size ||= @table.memo_block_size
117
122
  end
123
+
124
+ def memo_block_content_size
125
+ memo_block_size - BLOCK_HEADER_SIZE
126
+ end
127
+
128
+ def memo_content_size(memo_size)
129
+ (memo_size - memo_block_size) + BLOCK_HEADER_SIZE
130
+ end
131
+
118
132
  end
119
133
  end
data/lib/dbf/table.rb CHANGED
@@ -1,40 +1,25 @@
1
1
  module DBF
2
2
 
3
3
  class Table
4
- # The total number of columns (columns)
5
- attr_reader :column_count
6
-
7
- # An array of DBF::Column records
8
- attr_reader :columns
9
-
10
- # Internal dBase version number
11
- attr_reader :version
12
-
13
- # Last updated datetime
14
- attr_reader :last_updated
15
-
16
- # Either :fpt or :dpt
17
- attr_reader :memo_file_format
18
-
19
- # The block size for memo records
20
- attr_reader :memo_block_size
21
-
22
- # The options that were used when initializing DBF::Table. This is a Hash.
23
- attr_reader :options
24
-
25
- attr_reader :data
26
- attr_reader :memo
27
-
28
- # Initialize a new DBF::Reader.
4
+ include Enumerable
5
+
6
+ attr_reader :column_count # The total number of columns (columns)
7
+ attr_reader :columns # An array of DBF::Column
8
+ attr_reader :version # Internal dBase version number
9
+ attr_reader :last_updated # Last updated datetime
10
+ attr_reader :memo_file_format # :fpt or :dpt
11
+ attr_reader :memo_block_size # The block size for memo records
12
+ attr_reader :options # The options hash that was used to initialize the table
13
+ attr_reader :data # DBF file handle
14
+ attr_reader :memo # Memo file handle
15
+
16
+ # Initializes a new DBF::Reader
29
17
  # Example:
30
18
  # reader = DBF::Reader.new 'data.dbf'
31
19
  def initialize(filename, options = {})
32
- @options = {:in_memory => true, :accessors => true}.merge(options)
33
-
34
- @in_memory = @options[:in_memory]
35
- @accessors = @options[:accessors]
36
20
  @data = File.open(filename, 'rb')
37
21
  @memo = open_memo(filename)
22
+ @options = options
38
23
  reload!
39
24
  end
40
25
 
@@ -57,8 +42,8 @@ module DBF
57
42
  @db_index.size
58
43
  end
59
44
 
60
- # Returns an instance of DBF::Column for <b>column_name</b>. <b>column_name</b>
61
- # can be a symbol or a string.
45
+ # Returns an instance of DBF::Column for <b>column_name</b>. The <b>column_name</b>
46
+ # can be a specified as either a symbol or string.
62
47
  def column(column_name)
63
48
  @columns.detect {|f| f.name == column_name.to_s}
64
49
  end
@@ -66,22 +51,28 @@ module DBF
66
51
  # An array of all the records contained in the database file. Each record is an instance
67
52
  # of DBF::Record (or nil if the record is marked for deletion).
68
53
  def records
69
- if options[:in_memory]
70
- @records ||= get_all_records_from_file
71
- else
72
- get_all_records_from_file
54
+ self.to_a
55
+ end
56
+
57
+ alias_method :rows, :records
58
+
59
+ def each
60
+ 0.upto(@record_count - 1) do |n|
61
+ seek_to_record(n)
62
+ unless deleted_record?
63
+ yield DBF::Record.new(self)
64
+ end
73
65
  end
74
66
  end
75
67
 
76
- alias_method :rows, :records
68
+ # def get_record_from_file(index)
69
+ # seek_to_record(@db_index[index])
70
+ # Record.new(self)
71
+ # end
77
72
 
78
73
  # Returns a DBF::Record (or nil if the record has been marked for deletion) for the record at <tt>index</tt>.
79
74
  def record(index)
80
- if options[:in_memory]
81
- records[index]
82
- else
83
- get_record_from_file(index)
84
- end
75
+ records[index]
85
76
  end
86
77
 
87
78
  # Find records using a simple ActiveRecord-like syntax.
@@ -158,21 +149,38 @@ module DBF
158
149
  end
159
150
  end
160
151
 
152
+ # Returns the record at <tt>index</tt> by seeking to the record in the
153
+ # physical database file. See the documentation for the records method for
154
+ # information on how these two methods differ.
155
+ def get_record_from_file(index)
156
+ seek_to_record(@db_index[index])
157
+ Record.new(self)
158
+ end
159
+
161
160
  private
162
161
 
163
162
  def open_memo(file)
164
- %w(fpt FPT dbt DBT).each do |extension|
165
- filename = file.sub(/#{File.extname(file)[1..-1]}$/, extension)
163
+ %w(fpt FPT dbt DBT).each do |extname|
164
+ filename = replace_extname(file, extname)
166
165
  if File.exists?(filename)
167
- @memo_file_format = extension.downcase.to_sym
166
+ @memo_file_format = extname.downcase.to_sym
168
167
  return File.open(filename, 'rb')
169
168
  end
170
169
  end
171
170
  nil
172
171
  end
172
+
173
+ def replace_extname(filename, extension)
174
+ filename.sub(/#{File.extname(filename)[1..-1]}$/, extension)
175
+ end
173
176
 
174
177
  def deleted_record?
175
- @data.read(1).unpack('a') == ['*']
178
+ if @data.read(1).unpack('a') == ['*']
179
+ @data.rewind
180
+ true
181
+ else
182
+ false
183
+ end
176
184
  end
177
185
 
178
186
  def get_header_info
@@ -189,7 +197,7 @@ module DBF
189
197
  @columns << Column.new(name.strip, type, length, decimal)
190
198
  end
191
199
  end
192
- # Reset the column count
200
+ # Reset the column count in case any were skipped
193
201
  @column_count = @columns.size
194
202
 
195
203
  @columns
@@ -199,6 +207,7 @@ module DBF
199
207
  @memo.rewind
200
208
  if @memo_file_format == :fpt
201
209
  @memo_next_available_block, @memo_block_size = @memo.read(FPT_HEADER_SIZE).unpack('N x2 n')
210
+ @memo_block_size = 0 if @memo_block_size.nil?
202
211
  else
203
212
  @memo_block_size = 512
204
213
  @memo_next_available_block = File.size(@memo.path) / @memo_block_size
@@ -213,23 +222,6 @@ module DBF
213
222
  seek(index * @record_length)
214
223
  end
215
224
 
216
- # Returns the record at <tt>index</tt> by seeking to the record in the
217
- # physical database file. See the documentation for the records method for
218
- # information on how these two methods differ.
219
- def get_record_from_file(index)
220
- seek_to_record(@db_index[index])
221
- deleted_record? ? nil : Record.new(self)
222
- end
223
-
224
- def get_all_records_from_file
225
- all_records = []
226
- 0.upto(@record_count - 1) do |n|
227
- seek_to_record(n)
228
- all_records << DBF::Record.new(self) unless deleted_record?
229
- end
230
- all_records
231
- end
232
-
233
225
  def build_db_index
234
226
  @db_index = []
235
227
  @deleted_records = []
File without changes
File without changes
File without changes
@@ -36,11 +36,9 @@ describe DBF, :shared => true do
36
36
  end
37
37
 
38
38
  specify "column read accessors should return the attribute after typecast" do
39
- if @table.options[:accessors]
40
- @table.columns do |column|
41
- record = table.records.first
42
- record.send(column.name).should == record[column.name]
43
- end
39
+ @table.columns do |column|
40
+ record = table.records.first
41
+ record.send(column.name).should == record[column.name]
44
42
  end
45
43
  end
46
44
 
data/spec/spec_helper.rb CHANGED
@@ -6,5 +6,5 @@ require "dbf"
6
6
  DB_PATH = File.dirname(__FILE__) + '/fixtures' unless defined?(DB_PATH)
7
7
 
8
8
  Spec::Runner.configure do |config|
9
- config.mock_with :mocha
9
+
10
10
  end
@@ -1,34 +1,71 @@
1
1
  require File.dirname(__FILE__) + "/../spec_helper"
2
2
 
3
- describe DBF::Column, "when initialized" do
3
+ describe DBF::Column do
4
4
 
5
- before(:each) do
6
- @column = DBF::Column.new "ColumnName", "N", 1, 0
7
- end
5
+ context "when initialized" do
6
+ before do
7
+ @column = DBF::Column.new "ColumnName", "N", 1, 0
8
+ end
9
+
10
+ it "should set the #name accessor" do
11
+ @column.name.should == "ColumnName"
12
+ end
8
13
 
9
- it "should set the #name accessor" do
10
- @column.name.should == "ColumnName"
11
- end
14
+ it "should set the #type accessor" do
15
+ @column.type.should == "N"
16
+ end
12
17
 
13
- it "should set the #type accessor" do
14
- @column.type.should == "N"
15
- end
18
+ it "should set the #length accessor" do
19
+ @column.length.should == 1
20
+ end
16
21
 
17
- it "should set the #length accessor" do
18
- @column.length.should == 1
19
- end
22
+ it "should set the #decimal accessor" do
23
+ @column.decimal.should == 0
24
+ end
20
25
 
21
- it "should set the #decimal accessor" do
22
- @column.decimal.should == 0
26
+ it "should raise an error if length is greater than 0" do
27
+ lambda { column = DBF::Column.new "ColumnName", "N", -1, 0 }.should raise_error(DBF::ColumnLengthError)
28
+ end
29
+
23
30
  end
24
31
 
25
- it "should raise an error if length is greater than 0" do
26
- lambda { column = DBF::Column.new "ColumnName", "N", -1, 0 }.should raise_error(DBF::ColumnLengthError)
32
+ context "#schema_definition" do
33
+ it "should define an integer column if type is (N)umber with 9 decimals" do
34
+ column = DBF::Column.new "ColumnName", "N", 1, 0
35
+ column.schema_definition.should == "\"column_name\", :integer\n"
36
+ end
37
+
38
+ it "should define a float colmn if type is (N)umber with more than 0 decimals" do
39
+ column = DBF::Column.new "ColumnName", "N", 1, 2
40
+ column.schema_definition.should == "\"column_name\", :float\n"
41
+ end
42
+
43
+ it "should define a datetime column if type is (D)ate" do
44
+ column = DBF::Column.new "ColumnName", "D", 8, 0
45
+ column.schema_definition.should == "\"column_name\", :datetime\n"
46
+ end
47
+
48
+ it "should define a boolean column if type is (L)ogical" do
49
+ column = DBF::Column.new "ColumnName", "L", 1, 0
50
+ column.schema_definition.should == "\"column_name\", :boolean\n"
51
+ end
52
+
53
+ it "should define a text column if type is (M)emo" do
54
+ column = DBF::Column.new "ColumnName", "M", 1, 0
55
+ column.schema_definition.should == "\"column_name\", :text\n"
56
+ end
57
+
58
+ it "should define a string column with length for any other data types" do
59
+ column = DBF::Column.new "ColumnName", "X", 20, 0
60
+ column.schema_definition.should == "\"column_name\", :string, :limit => 20\n"
61
+ end
27
62
  end
28
63
 
29
- it "should strip non-ascii characters from the name" do
30
- column = DBF::Column.new "Col\200umn\0Name\037", "N", 1, 0
31
- column.name.should == "ColumnName"
64
+ context "#strip_non_ascii_chars" do
65
+ it "should strip characters below decimal 32 and above decimal 128" do
66
+ column = DBF::Column.new "ColumnName", "N", 1, 0
67
+ column.send(:strip_non_ascii_chars, "--\x1F-\x68\x65\x6C\x6C\x6F world-\x80--").should == "---hello world---"
68
+ end
32
69
  end
33
70
 
34
71
  end
@@ -1,56 +1,100 @@
1
1
  require File.dirname(__FILE__) + "/../spec_helper"
2
2
 
3
- describe DBF::Record, "when initialized" do
4
-
5
- def standalone_record(binary_data)
6
- table = mock
7
- table.stubs(:data)
8
- table.data.stubs(:read).returns(binary_data)
9
- table.stubs(:memo).returns(nil)
10
- table.stubs(:columns).returns([])
11
- DBF::Record.new(table)
12
- end
13
-
14
- it "should typecast number columns with decimals == 0 to Integer" do
15
- table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
16
- table.column("ID").type.should == "N"
17
- table.column("ID").decimal.should == 0
18
- table.records.all? {|record| record.attributes['ID'].should be_kind_of(Integer)}
3
+ describe DBF::Record do
4
+
5
+ def example_record(data = '')
6
+ DBF::Record.new(mock_table(data))
19
7
  end
20
8
 
21
- it "should typecast number columns with decimals > 0 to Float" do
22
- table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
23
- table.column("ID").type.should == "N"
24
- table.column("COST").decimal.should == 2
25
- table.records.all? {|record| record.attributes['COST'].should be_kind_of(Float)}
9
+ def mock_table(data = '')
10
+ table = mock('table')
11
+ table.stub!(:memo_block_size).and_return(8)
12
+ table.stub!(:memo).and_return(nil)
13
+ table.stub!(:columns).and_return([])
14
+ table.stub!(:data)
15
+ table.stub!(:has_memo_file?).and_return(true)
16
+ table.data.stub!(:read).and_return(data)
17
+ table
26
18
  end
27
19
 
28
- it "should typecast memo columns to String" do
29
- table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
30
- table.column("DESC").type.should == "M"
31
- table.records.all? {|record| record.attributes['DESC'].should be_kind_of(String)}
20
+ context "when initialized" do
21
+ it "should typecast number columns with decimals == 0 to Integer" do
22
+ table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
23
+ table.column("ID").type.should == "N"
24
+ table.column("ID").decimal.should == 0
25
+ table.records.all? {|record| record.attributes['ID'].should be_kind_of(Integer)}
26
+ end
27
+
28
+ it "should typecast number columns with decimals > 0 to Float" do
29
+ table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
30
+ table.column("ID").type.should == "N"
31
+ table.column("COST").decimal.should == 2
32
+ table.records.all? {|record| record.attributes['COST'].should be_kind_of(Float)}
33
+ end
34
+
35
+ it "should typecast memo columns to String" do
36
+ table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
37
+ table.column("DESC").type.should == "M"
38
+ table.records.all? {|record| record.attributes['DESC'].should be_kind_of(String)}
39
+ end
40
+
41
+ it "should typecast logical columns to True or False" do
42
+ table = DBF::Table.new "#{DB_PATH}/dbase_30.dbf"
43
+ table.column("WEBINCLUDE").type.should == "L"
44
+ table.records.all? {|record| record.attributes["WEBINCLUDE"].should satisfy {|v| v == true || v == false}}
45
+ end
46
+
47
+ it "should typecast datetime columns to DateTime" do
48
+ record = example_record("Nl%\000\300Z\252\003")
49
+ column = mock('column', :length => 8)
50
+
51
+ record.instance_eval {unpack_datetime(column)}.to_s.should == "2002-10-10T17:04:56+00:00"
52
+ end
53
+
54
+ it "should typecast integers to Fixnum" do
55
+ record = example_record("\017\020\000\000")
56
+ column = mock('column', :length => 4)
57
+
58
+ record.instance_eval {unpack_integer(column)}.should == 4111
59
+ end
32
60
  end
33
61
 
34
- it "should typecast logical columns to True or False" do
35
- table = DBF::Table.new "#{DB_PATH}/dbase_30.dbf"
36
- table.column("WEBINCLUDE").type.should == "L"
37
- table.records.all? {|record| record.attributes["WEBINCLUDE"].should satisfy {|v| v == true || v == false}}
62
+ describe '#memo_block_content_size' do
63
+ it "should equal the difference between the table's memo_block_size and 8" do
64
+ table = mock_table
65
+ table.should_receive(:memo_block_size).and_return(128)
66
+ record = DBF::Record.new(table)
67
+
68
+ record.send(:memo_block_content_size).should == 120
69
+ end
38
70
  end
39
71
 
40
- it "should typecast datetime columns to DateTime" do
41
- binary_data = "Nl%\000\300Z\252\003"
42
- record = standalone_record(binary_data)
43
- column = stub(:length => 8)
44
-
45
- record.instance_eval {unpack_datetime(column)}.to_s.should == "2002-10-10T17:04:56+00:00"
72
+ describe '#memo_content_size' do
73
+ it "should equal 8 plus the difference between memo_size and the table's memo_block_size" do
74
+ record = example_record
75
+ record.should_receive(:memo_block_size).and_return(8)
76
+
77
+ record.send(:memo_content_size, 1024).should == 1024
78
+ end
46
79
  end
47
80
 
48
- it "should typecast integers to Fixnum" do
49
- binary_data = "\017\020\000\000"
50
- record = standalone_record(binary_data)
51
- column = stub(:length => 4)
81
+ describe '#read_memo' do
82
+ it 'should return nil if start_block is less than 1' do
83
+ table = mock_table
84
+ record = DBF::Record.new(table)
85
+
86
+ record.send(:read_memo, 0).should be_nil
87
+ record.send(:read_memo, -1).should be_nil
88
+ end
89
+
90
+ it 'should return nil if memo file is missing' do
91
+ table = mock_table
92
+ table.should_receive(:has_memo_file?).and_return(false)
93
+ record = DBF::Record.new(table)
52
94
 
53
- record.instance_eval {unpack_integer(column)}.should == 4111
95
+ record.send(:read_memo, 5).should be_nil
96
+ end
97
+
54
98
  end
55
99
 
56
100
  end
@@ -1,167 +1,161 @@
1
1
  require File.dirname(__FILE__) + "/../spec_helper"
2
2
 
3
- describe DBF::Table, "when initialized" do
4
-
5
- before(:all) do
6
- @table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
7
- end
8
-
9
- it "should load the data file" do
10
- @table.data.should be_kind_of(File)
11
- end
12
-
13
- it "should locate load the memo file" do
14
- @table.has_memo_file?.should be_true
15
- @table.instance_eval("@memo").should be_kind_of(File)
16
- end
17
-
18
- it "should determine the memo file format" do
19
- @table.memo_file_format.should == :dbt
20
- end
21
-
22
- it "should determine the correct memo block size" do
23
- @table.memo_block_size.should == 512
24
- end
25
-
26
- it "should default to loading all records into memory" do
27
- @table.options[:in_memory].should be_true
28
- end
29
-
30
- it "should determine the number of columns in each record" do
31
- @table.columns.size.should == 15
32
- end
33
-
34
- it "should determine the number of records in the database" do
35
- @table.record_count.should == 67
36
- end
37
-
38
- it "should determine the database version" do
39
- @table.version.should == "83"
40
- end
41
-
42
- it "should set the in_memory option" do
43
- table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf", :in_memory => false
44
- table.options[:in_memory].should be_false
45
- end
3
+ describe DBF::Table do
46
4
 
47
- end
5
+ context "when initialized" do
6
+ before do
7
+ @table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
8
+ end
48
9
 
49
- describe DBF::Table, "when the in_memory flag is true" do
50
-
51
- before(:each) do
52
- @table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
53
- end
54
-
55
- it "should build the records array from disk only on the first request" do
56
- @table.expects(:get_all_records_from_file).at_most_once.returns([])
57
- 3.times { @table.records }
58
- end
59
-
60
- it "should read from the records array when using the record() method" do
61
- @table.expects(:get_all_records_from_file).at_most_once.returns([])
62
- @table.expects(:get_record_from_file).never
63
- @table.expects(:records).times(2).returns([])
64
- @table.record(1)
65
- @table.record(10)
66
- end
67
-
68
- end
10
+ it "should load the data file" do
11
+ @table.data.should be_kind_of(File)
12
+ end
69
13
 
70
- describe DBF::Table, "when the in_memory flag is false" do
71
-
72
- it "should read the records from disk on every request" do
73
- table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf", :in_memory => false
74
- table.expects(:get_all_records_from_file).times(3).returns([])
75
- 3.times { table.records }
76
- end
77
- end
14
+ it "should locate load the memo file" do
15
+ @table.has_memo_file?.should be_true
16
+ @table.instance_eval("@memo").should be_kind_of(File)
17
+ end
78
18
 
79
- describe DBF::Table, "schema" do
80
-
81
- it "should match test schema " do
82
- table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
83
- control_schema = File.read(File.dirname(__FILE__) + '/../fixtures/dbase_83_schema.txt')
84
-
85
- table.schema.should == control_schema
86
- end
87
-
88
- end
19
+ it "should determine the memo file format" do
20
+ @table.memo_file_format.should == :dbt
21
+ end
89
22
 
90
- describe DBF::Table, "find(index)" do
91
-
92
- before(:all) do
93
- @table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
94
- end
95
-
96
- it "should return the correct record" do
97
- @table.find(5).should == @table.record(5)
98
- end
99
-
100
- end
23
+ it "should determine the correct memo block size" do
24
+ @table.memo_block_size.should == 512
25
+ end
101
26
 
102
- describe DBF::Table, "find(:all)" do
103
-
104
- before(:all) do
105
- @table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
106
- end
107
-
108
- it "should return all records if options are empty" do
109
- @table.find(:all).should == @table.records
110
- end
111
-
112
- it "should return matching records when used with options" do
113
- @table.find(:all, "WEIGHT" => 0.0).should == @table.records.select {|r| r.attributes["WEIGHT"] == 0.0}
114
- end
115
-
116
- it "with multiple options should search for all search terms as if using AND" do
117
- @table.find(:all, "ID" => 30, "IMAGE" => "graphics/00000001/TBC01.jpg").should == []
118
- end
119
- end
27
+ it "should determine the number of columns in each record" do
28
+ @table.columns.size.should == 15
29
+ end
30
+
31
+ it "should determine the number of records in the database" do
32
+ @table.record_count.should == 67
33
+ end
34
+
35
+ it "should determine the database version" do
36
+ @table.version.should == "83"
37
+ end
120
38
 
121
- describe DBF::Table, "find(:first)" do
122
-
123
- before(:all) do
124
- @table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
125
39
  end
126
40
 
127
- it "should return the first record if options are empty" do
128
- @table.find(:first).should == @table.records.first
41
+ describe "#find" do
42
+ describe "with index" do
43
+ it "should return the correct record" do
44
+ table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
45
+ table.find(5).should == table.record(5)
46
+ end
47
+ end
48
+
49
+ describe "with :all" do
50
+ before do
51
+ @table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
52
+ end
53
+
54
+ it "should return all records if options are empty" do
55
+ @table.find(:all).should == @table.records
56
+ end
57
+
58
+ it "should return matching records when used with options" do
59
+ @table.find(:all, "WEIGHT" => 0.0).should == @table.select {|r| r.attributes["WEIGHT"] == 0.0}
60
+ end
61
+
62
+ it "with multiple options should search for all search terms as if using AND" do
63
+ @table.find(:all, "ID" => 30, "IMAGE" => "graphics/00000001/TBC01.jpg").should == []
64
+ end
65
+
66
+ it "should match original column names" do
67
+ @table.find(:all, "WEIGHT" => 0.0).should_not be_empty
68
+ end
69
+
70
+ it "should match symbolized column names" do
71
+ @table.find(:all, :WEIGHT => 0.0).should_not be_empty
72
+ end
73
+
74
+ it "should match downcased column names" do
75
+ @table.find(:all, "weight" => 0.0).should_not be_empty
76
+ end
77
+
78
+ it "should match symbolized downcased column names" do
79
+ @table.find(:all, :weight => 0.0).should_not be_empty
80
+ end
81
+ end
82
+
83
+ describe "with :first" do
84
+ before do
85
+ @table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
86
+ end
87
+
88
+ it "should return the first record if options are empty" do
89
+ @table.find(:first).should == @table.records.first
90
+ end
91
+
92
+ it "should return the first matching record when used with options" do
93
+ @table.find(:first, "CODE" => "C").should == @table.record(5)
94
+ end
95
+
96
+ it "with multiple options should search for all search terms as if using AND" do
97
+ @table.find(:first, "ID" => 30, "IMAGE" => "graphics/00000001/TBC01.jpg").should be_nil
98
+ end
99
+ end
129
100
  end
130
101
 
131
- it "should return the first matching record when used with options" do
132
- @table.find(:first, "CODE" => "C").should == @table.record(5)
102
+ describe "#reload" do
103
+ # TODO
133
104
  end
134
105
 
135
- it "with multiple options should search for all search terms as if using AND" do
136
- @table.find(:first, "ID" => 30, "IMAGE" => "graphics/00000001/TBC01.jpg").should be_nil
137
- end
138
- end
139
-
140
- describe DBF::Table do
106
+ describe "#column" do
107
+ it "should accept a string or symbol as input" do
108
+ table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
109
+ table.column(:IMAGE).should be_kind_of(DBF::Column)
110
+ table.column("IMAGE").should be_kind_of(DBF::Column)
111
+ end
112
+
113
+ it "should return a DBF::Field object when the column_name exists" do
114
+ table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
115
+ table.column(:IMAGE).should be_kind_of(DBF::Column)
116
+ end
141
117
 
142
- before(:each) do
143
- @table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
118
+ it "should return nil when the column_name does not exist" do
119
+ table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
120
+ table.column(:NOTANIMAGE).should be_nil
121
+ end
144
122
  end
145
123
 
146
- it "should reload all data when sent #reload!" do
147
- @table.records
148
- @table.instance_eval("@records").should be_kind_of(Array)
149
- @table.reload!
150
- @table.instance_eval("@records").should be_nil
124
+ describe "#schema" do
125
+ it "should match the test schema fixture" do
126
+ table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
127
+ control_schema = File.read(File.dirname(__FILE__) + '/../fixtures/dbase_83_schema.txt')
128
+
129
+ table.schema.should == control_schema
130
+ end
151
131
  end
152
132
 
153
- it "should return a DBF::Field object when sent #column with a valid column_name given as a string or symbol" do
154
- @table.column("IMAGE").should be_kind_of(DBF::Column)
155
- @table.column(:IMAGE).should be_kind_of(DBF::Column)
133
+ describe "#version_description" do
134
+ it "should return a text description of the database type" do
135
+ table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
136
+ table.version_description.should == "dBase III with memo file"
137
+ end
156
138
  end
157
139
 
158
- it "should return nil when sent #column with an invalid column_name given as a string or symbol" do
159
- @table.column("NOTANIMAGE").should be_nil
160
- @table.column(:NOTANIMAGE).should be_nil
140
+ describe '#replace_extname' do
141
+ it 'should replace the extname' do
142
+ table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
143
+ table.send(:replace_extname, "dbase_83.dbf", 'fpt').should == 'dbase_83.fpt'
144
+ end
161
145
  end
162
146
 
163
- it "should return a text description of the database type when sent #version_description" do
164
- @table.version_description.should == "dBase III with memo file"
147
+ describe '#each' do
148
+ it 'should enumerate all records' do
149
+ table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
150
+ records = []
151
+ table.each do |record|
152
+ records << record
153
+ end
154
+
155
+ records.map! { |r| r.attributes }
156
+ records.should == table.records.map {|r| r.attributes}
157
+ end
158
+
165
159
  end
166
160
 
167
161
  end
metadata CHANGED
@@ -1,41 +1,45 @@
1
1
  --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.9.4
3
- specification_version: 1
4
2
  name: dbf
5
3
  version: !ruby/object:Gem::Version
6
- version: 1.0.5
7
- date: 2007-11-27 00:00:00 -08:00
8
- summary: A small fast library for reading dBase, xBase, Clipper and FoxPro database files.
9
- require_paths:
10
- - lib
11
- email: keithm@infused.org
12
- homepage: http://dbf.rubyforge.org
13
- rubyforge_project: dbf
14
- description: "DBF is a small fast library for reading dBase, xBase, Clipper and FoxPro database files. It is written completely in Ruby and has no external dependencies. Copyright (c) 2006-2007 Keith Morrison <keithm@infused.org, www.infused.org> * Official project page: http://rubyforge.org/projects/dbf * API Documentation: http://dbf.rubyforge.org/docs * To report bugs: http://www.rubyforge.org/tracker/?group_id=2009 * Questions: Email keithm@infused.org and put DBF somewhere in the subject line"
15
- autorequire:
16
- default_executable:
17
- bindir: bin
18
- has_rdoc: true
19
- required_ruby_version: !ruby/object:Gem::Version::Requirement
20
- requirements:
21
- - - ">"
22
- - !ruby/object:Gem::Version
23
- version: 0.0.0
24
- version:
4
+ version: 1.0.6
25
5
  platform: ruby
26
- signing_key:
27
- cert_chain:
28
- post_install_message:
29
6
  authors:
30
7
  - Keith Morrison
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-08-08 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: hoe
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.7.0
24
+ version:
25
+ description: "DBF is a small fast library for reading dBase, xBase, Clipper and FoxPro database files. It is written completely in Ruby and has no external dependencies. Copyright (c) 2006-2007 Keith Morrison <keithm@infused.org, www.infused.org> * Official project page: http://rubyforge.org/projects/dbf * API Documentation: http://dbf.rubyforge.org/docs * To report bugs: http://www.rubyforge.org/tracker/?group_id=2009 * Questions: Email keithm@infused.org and put DBF somewhere in the subject line"
26
+ email: keithm@infused.org
27
+ executables:
28
+ - dbf
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - History.txt
33
+ - Manifest.txt
34
+ - README.txt
35
+ - spec/fixtures/dbase_83_schema.txt
31
36
  files:
32
37
  - History.txt
33
38
  - Manifest.txt
34
39
  - README.txt
35
40
  - Rakefile
36
- - benchmarks/performance.rb
37
- - benchmarks/seek_benchmark.rb
38
41
  - bin/dbf
42
+ - dbf.gemspec
39
43
  - lib/dbf.rb
40
44
  - lib/dbf/column.rb
41
45
  - lib/dbf/globals.rb
@@ -61,29 +65,32 @@ files:
61
65
  - spec/unit/column_spec.rb
62
66
  - spec/unit/record_spec.rb
63
67
  - spec/unit/table_spec.rb
64
- test_files: []
65
-
68
+ has_rdoc: true
69
+ homepage: http://github.com/infused/dm-dbf/tree/master
70
+ post_install_message:
66
71
  rdoc_options:
67
72
  - --main
68
73
  - README.txt
69
- extra_rdoc_files:
70
- - History.txt
71
- - Manifest.txt
72
- - README.txt
73
- - spec/fixtures/dbase_83_schema.txt
74
- executables:
75
- - dbf
76
- extensions: []
77
-
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: "0"
81
+ version:
82
+ required_rubygems_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: "0"
87
+ version:
78
88
  requirements: []
79
89
 
80
- dependencies:
81
- - !ruby/object:Gem::Dependency
82
- name: hoe
83
- version_requirement:
84
- version_requirements: !ruby/object:Gem::Version::Requirement
85
- requirements:
86
- - - ">="
87
- - !ruby/object:Gem::Version
88
- version: 1.3.0
89
- version:
90
+ rubyforge_project: dbf
91
+ rubygems_version: 1.2.0
92
+ signing_key:
93
+ specification_version: 2
94
+ summary: A small fast library for reading dBase, xBase, Clipper and FoxPro database files.
95
+ test_files: []
96
+
@@ -1,14 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- $:.unshift(File.dirname(__FILE__) + "/../lib/")
4
- require 'dbf'
5
- require 'profiler'
6
-
7
- dbf = DBF::Reader.new(File.join(File.dirname(__FILE__),'databases', 'foxpro.dbf'))
8
-
9
- Profiler__::start_profile
10
-
11
- dbf.records
12
-
13
- Profiler__::stop_profile
14
- Profiler__::print_profile($stdout)
@@ -1,18 +0,0 @@
1
- #!/usr/bin/env ruby
2
- require 'benchmark'
3
- $:.unshift(File.dirname(__FILE__) + "/../lib/")
4
- require 'dbf'
5
-
6
- puts
7
- puts "Runs 5000 random row seeks first using the I/O based record(n) method and then using"
8
- puts "using the array of records."
9
- puts
10
-
11
- iterations = 5000
12
- Benchmark.bm(20) do |x|
13
- @dbf = DBF::Reader.new(File.join(File.dirname(__FILE__), '..', 'test', 'databases', 'foxpro.dbf'))
14
- max = @dbf.record_count + 1
15
-
16
- x.report("I/O based record(n)") { iterations.times { @dbf.record(rand(max)) } }
17
- x.report("array of records[n]") { iterations.times { @dbf.records[rand(max)] } }
18
- end