activerecord-import 0.12.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +49 -0
  3. data/.rubocop_todo.yml +36 -0
  4. data/.travis.yml +31 -7
  5. data/CHANGELOG.md +19 -0
  6. data/Gemfile +5 -2
  7. data/README.markdown +6 -1
  8. data/Rakefile +5 -2
  9. data/activerecord-import.gemspec +1 -1
  10. data/benchmarks/benchmark.rb +67 -68
  11. data/benchmarks/lib/base.rb +136 -137
  12. data/benchmarks/lib/cli_parser.rb +106 -107
  13. data/benchmarks/lib/mysql2_benchmark.rb +19 -21
  14. data/benchmarks/lib/output_to_csv.rb +2 -1
  15. data/benchmarks/lib/output_to_html.rb +8 -13
  16. data/benchmarks/schema/mysql_schema.rb +8 -8
  17. data/gemfiles/4.0.gemfile +1 -1
  18. data/gemfiles/4.1.gemfile +1 -1
  19. data/gemfiles/4.2.gemfile +1 -1
  20. data/gemfiles/5.0.gemfile +1 -1
  21. data/lib/activerecord-import.rb +2 -0
  22. data/lib/activerecord-import/active_record/adapters/sqlite3_adapter.rb +0 -1
  23. data/lib/activerecord-import/adapters/abstract_adapter.rb +9 -9
  24. data/lib/activerecord-import/adapters/mysql_adapter.rb +17 -17
  25. data/lib/activerecord-import/adapters/postgresql_adapter.rb +20 -22
  26. data/lib/activerecord-import/adapters/sqlite3_adapter.rb +9 -9
  27. data/lib/activerecord-import/base.rb +3 -3
  28. data/lib/activerecord-import/import.rb +152 -131
  29. data/lib/activerecord-import/synchronize.rb +20 -20
  30. data/lib/activerecord-import/value_sets_parser.rb +7 -6
  31. data/lib/activerecord-import/version.rb +1 -1
  32. data/test/adapters/mysql2spatial.rb +1 -1
  33. data/test/adapters/postgis.rb +1 -1
  34. data/test/adapters/postgresql.rb +1 -1
  35. data/test/adapters/spatialite.rb +1 -1
  36. data/test/adapters/sqlite3.rb +1 -1
  37. data/test/import_test.rb +121 -70
  38. data/test/models/book.rb +5 -6
  39. data/test/models/chapter.rb +2 -2
  40. data/test/models/discount.rb +3 -0
  41. data/test/models/end_note.rb +2 -2
  42. data/test/models/promotion.rb +1 -1
  43. data/test/models/question.rb +1 -1
  44. data/test/models/rule.rb +2 -2
  45. data/test/models/topic.rb +3 -3
  46. data/test/models/widget.rb +1 -1
  47. data/test/postgis/import_test.rb +1 -1
  48. data/test/schema/generic_schema.rb +100 -96
  49. data/test/schema/mysql_schema.rb +5 -7
  50. data/test/sqlite3/import_test.rb +0 -2
  51. data/test/support/active_support/test_case_extensions.rb +12 -15
  52. data/test/support/assertions.rb +1 -1
  53. data/test/support/factories.rb +15 -16
  54. data/test/support/generate.rb +4 -4
  55. data/test/support/mysql/import_examples.rb +21 -21
  56. data/test/support/postgresql/import_examples.rb +83 -55
  57. data/test/support/shared_examples/on_duplicate_key_update.rb +23 -23
  58. data/test/synchronize_test.rb +2 -2
  59. data/test/test_helper.rb +6 -8
  60. data/test/value_sets_bytes_parser_test.rb +14 -17
  61. data/test/value_sets_records_parser_test.rb +6 -6
  62. metadata +7 -4
  63. data/test/travis/build.sh +0 -34
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9f8e8fa0974d52a903ef20a78f196030a515687d
4
- data.tar.gz: 0ccc98cac5b73a78e9e9ffe57c2b74504c2b9e81
3
+ metadata.gz: 1f525ceb164067e5f2c49042b1a8f3312eab016a
4
+ data.tar.gz: c9817adb82be93259525dc52a85b42bf7829d585
5
5
  SHA512:
6
- metadata.gz: 1562542547afcea6d646e3a5fcf2aabd806471f0000b2434d6fd979a60c2bc93b90e115e09e2b67df00b35a2244c67d0f2554e9480f0c5c6bb6f3dbdebd2e8be
7
- data.tar.gz: 45f2552a368c3b9c70bf7abec385462f34a66e423a65db1e99536b8f3b383c02d21af4cb5b5a313c3769aa59a919bd4db34ddb6927ced52fae88272afaeb28de
6
+ metadata.gz: 974e9a0491e43cd9a9a22e167f1b9dede5bcdececbb3d480c2b4edf2e293e50c0cf3e85f39b4d10f624c541a0651db5de2f85bad0d5ca7142c4de28017e1809f
7
+ data.tar.gz: 4e31a0c10174ba285b08d47abbac06c2089c62488c5f3775d24c6b993b4c7bd465d051e5238e6df511da6ad8b152f47f5827b41cf316d5e8713dc9bd2c64e9bc
@@ -0,0 +1,49 @@
1
+ inherit_from: .rubocop_todo.yml
2
+
3
+ Lint/EndAlignment:
4
+ AlignWith: variable
5
+
6
+ Metrics/AbcSize:
7
+ Enabled: false
8
+
9
+ Metrics/ClassLength:
10
+ Enabled: false
11
+
12
+ Metrics/CyclomaticComplexity:
13
+ Enabled: false
14
+
15
+ Metrics/LineLength:
16
+ Enabled: false
17
+
18
+ Metrics/MethodLength:
19
+ Enabled: false
20
+
21
+ Metrics/ModuleLength:
22
+ Enabled: false
23
+
24
+ Metrics/PerceivedComplexity:
25
+ Enabled: false
26
+
27
+ Style/AlignParameters:
28
+ EnforcedStyle: with_fixed_indentation
29
+
30
+ Style/ClassAndModuleChildren:
31
+ Enabled: false
32
+
33
+ Style/Documentation:
34
+ Enabled: false
35
+
36
+ Style/ElseAlignment:
37
+ Enabled: false
38
+
39
+ Style/SpaceInsideParens:
40
+ Enabled: false
41
+
42
+ Style/SpecialGlobalVars:
43
+ Enabled: false
44
+
45
+ Style/StringLiterals:
46
+ Enabled: false
47
+
48
+ Style/TrailingCommaInLiteral:
49
+ Enabled: false
@@ -0,0 +1,36 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2016-03-17 18:14:55 -0700 using RuboCop version 0.38.0.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 2
10
+ Lint/HandleExceptions:
11
+ Exclude:
12
+ - 'lib/activerecord-import/base.rb'
13
+ - 'test/import_test.rb'
14
+
15
+ # Offense count: 2
16
+ Lint/RescueException:
17
+ Exclude:
18
+ - 'benchmarks/lib/cli_parser.rb'
19
+ - 'test/import_test.rb'
20
+
21
+ # Offense count: 4
22
+ # Cop supports --auto-correct.
23
+ # Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods.
24
+ Lint/UnusedMethodArgument:
25
+ Exclude:
26
+ - 'lib/activerecord-import/adapters/postgresql_adapter.rb'
27
+ - 'lib/activerecord-import/import.rb'
28
+
29
+ # Offense count: 2
30
+ # Cop supports --auto-correct.
31
+ # Configuration parameters: Keywords.
32
+ # Keywords: TODO, FIXME, OPTIMIZE, HACK, REVIEW
33
+ Style/CommentAnnotation:
34
+ Exclude:
35
+ - 'benchmarks/lib/cli_parser.rb'
36
+ - 'lib/activerecord-import/import.rb'
@@ -1,20 +1,44 @@
1
1
  language: ruby
2
2
  cache: bundler
3
3
  rvm:
4
- - 2.2.3
4
+ - 2.2.4
5
5
 
6
- gemfile:
7
- - Gemfile
6
+ env:
7
+ global:
8
+ # https://github.com/discourse/discourse/blob/master/.travis.yml
9
+ - RUBY_GC_MALLOC_LIMIT=50000000
10
+ matrix:
11
+ - AR_VERSION=3.1
12
+ - AR_VERSION=3.2
13
+ - AR_VERSION=4.0
14
+ - AR_VERSION=4.1
15
+ - AR_VERSION=4.2
16
+ - AR_VERSION=5.0
8
17
 
9
- bundler_args: -j2
18
+ matrix:
19
+ fast_finish: true
10
20
 
11
21
  before_script:
12
22
  - mysql -e 'create database activerecord_import_test;'
13
23
  - psql -c 'create database activerecord_import_test;' -U postgres
14
24
  - psql -U postgres -c "create extension postgis"
15
25
  - cp test/travis/database.yml test/database.yml
16
- # https://github.com/discourse/discourse/blob/master/.travis.yml
17
- - export RUBY_GC_MALLOC_LIMIT=50000000
26
+
27
+ addons:
28
+ apt:
29
+ sources:
30
+ - travis-ci/sqlite3
31
+ packages:
32
+ - sqlite3
18
33
 
19
34
  script:
20
- - test/travis/build.sh
35
+ - bundle exec rake test:mysql2
36
+ - bundle exec rake test:mysql2spatial
37
+ - bundle exec rake test:postgis
38
+ - bundle exec rake test:postgresql
39
+ - bundle exec rake test:seamless_database_pool
40
+ - bundle exec rake test:spatialite
41
+ - bundle exec rake test:sqlite3
42
+ - bundle exec rubocop
43
+
44
+ sudo: false
@@ -1,3 +1,22 @@
1
+ ## Changes in 0.13.0
2
+
3
+ ### New Features
4
+
5
+ * Addition of :batch_size option to control the number of rows to insert per INSERT statement. The default is the total number of records being inserted so there is a single INSERT statement. Thanks to @jkowens via \#245
6
+
7
+ * Addition `import!` which will raise an exception if a validation occurs. It will fail fast. Thanks to @jkowens via \#246
8
+
9
+ ### Fixes
10
+
11
+ * Fixing issue with recursive import when utilizing the `:on_duplicate_key_update` option. The `on_duplicate_key_update` only applies to parent models at this time. Thanks to @yuri-karpovich for reporting and @jkowens for fixing via \#249
12
+
13
+ ### Misc
14
+
15
+ * Refactoring of fetching and assigning attributes. Thanks to @jkownes via \#259
16
+ * Lots of code cleanup and addition of Rubocop linter. Thanks to @sferik via \#256 and \#250
17
+ * Resolving errors with the test suite when running against ActiveRecord 4.0 and 4.1. Thanks to @jkowens via \#262
18
+ * Cleaning up the TravisCI settings and packages. Thanks to @sferik via \#258 and \#251
19
+
1
20
  ## Changes in 0.12.0
2
21
 
3
22
  ### New Features
data/Gemfile CHANGED
@@ -2,6 +2,10 @@ source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
4
 
5
+ group :development, :test do
6
+ gem 'rubocop', '~> 0.38.0'
7
+ end
8
+
5
9
  # Database Adapters
6
10
  platforms :ruby do
7
11
  gem "mysql2", "~> 0.3.0"
@@ -22,7 +26,6 @@ gem "factory_girl", "~> 4.2.0"
22
26
  gem "timecop"
23
27
  gem "chronic"
24
28
 
25
-
26
29
  # Debugging
27
30
  platforms :jruby do
28
31
  gem "ruby-debug-base", "= 0.10.4"
@@ -42,7 +45,7 @@ end
42
45
 
43
46
  version = ENV['AR_VERSION'] || "4.2"
44
47
 
45
- if version > "4.0"
48
+ if version >= "4.0"
46
49
  gem "minitest"
47
50
  else
48
51
  gem "test-unit"
@@ -21,6 +21,10 @@ and then the reviews:
21
21
  That would be about 4M SQL insert statements vs 3, which results in vastly improved performance. In our case, it converted
22
22
  an 18 hour batch process to <2 hrs.
23
23
 
24
+ ### Rails 5.0
25
+
26
+ Use activerecord-import 0.11.0 or higher.
27
+
24
28
  ### Rails 4.0
25
29
 
26
30
  Use activerecord-import 0.4.0 or higher.
@@ -85,7 +89,8 @@ Zach Dennis (zach.dennis@gmail.com)
85
89
 
86
90
  # Contributors
87
91
 
88
- * Jordan Owens
92
+ * Jordan Owens (@jkowens)
93
+ * Erik Michaels-Ober (@sferik)
89
94
  * Blythe Dunham
90
95
  * Gabe da Silveira
91
96
  * Henry Work
data/Rakefile CHANGED
@@ -11,9 +11,9 @@ namespace :display do
11
11
  puts
12
12
  end
13
13
  end
14
- task :default => ["display:notice"]
14
+ task default: ["display:notice"]
15
15
 
16
- ADAPTERS = %w(mysql2 jdbcmysql jdbcpostgresql postgresql sqlite3 seamless_database_pool mysql2spatial spatialite postgis)
16
+ ADAPTERS = %w(mysql2 jdbcmysql jdbcpostgresql postgresql sqlite3 seamless_database_pool mysql2spatial spatialite postgis).freeze
17
17
  ADAPTERS.each do |adapter|
18
18
  namespace :test do
19
19
  desc "Runs #{adapter} database tests."
@@ -49,3 +49,6 @@ Rake::RDocTask.new do |rdoc|
49
49
  rdoc.rdoc_files.include('README*')
50
50
  rdoc.rdoc_files.include('lib/**/*.rb')
51
51
  end
52
+
53
+ require 'rubocop/rake_task'
54
+ RuboCop::RakeTask.new
@@ -10,7 +10,7 @@ Gem::Specification.new do |gem|
10
10
  gem.license = "Ruby"
11
11
 
12
12
  gem.files = `git ls-files`.split($\)
13
- gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
14
14
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
15
15
  gem.name = "activerecord-import"
16
16
  gem.require_paths = ["lib"]
@@ -1,68 +1,67 @@
1
- require 'pathname'
2
- require "fileutils"
3
- require "active_record"
4
-
5
- benchmark_dir = File.dirname(__FILE__)
6
-
7
- $LOAD_PATH.unshift('.')
8
-
9
- # Get the gem into the load path
10
- $LOAD_PATH.unshift(File.join(benchmark_dir, '..', 'lib'))
11
-
12
- # Load the benchmark files
13
- Dir[File.join( benchmark_dir, 'lib', '*.rb' ) ].sort.each{ |f| require f }
14
-
15
- # Parse the options passed in via the command line
16
- options = BenchmarkOptionParser.parse( ARGV )
17
-
18
- FileUtils.mkdir_p 'log'
19
- ActiveRecord::Base.configurations["test"] = YAML.load_file(File.join(benchmark_dir, "../test/database.yml"))[options.adapter]
20
- ActiveRecord::Base.logger = Logger.new("log/test.log")
21
- ActiveRecord::Base.logger.level = Logger::DEBUG
22
- ActiveRecord::Base.default_timezone = :utc
23
-
24
- require "activerecord-import"
25
- ActiveRecord::Base.establish_connection(:test)
26
-
27
- ActiveSupport::Notifications.subscribe(/active_record.sql/) do |event, _, _, _, hsh|
28
- ActiveRecord::Base.logger.info hsh[:sql]
29
- end
30
-
31
- # Load base/generic schema
32
- require File.join(benchmark_dir, "../test/schema/version")
33
- require File.join(benchmark_dir, "../test/schema/generic_schema")
34
- adapter_schema = File.join(benchmark_dir, "schema/#{options.adapter}_schema.rb")
35
- require adapter_schema if File.exists?(adapter_schema)
36
-
37
- Dir[File.dirname(__FILE__) + "/models/*.rb"].each{ |file| require file }
38
-
39
-
40
- require File.join( benchmark_dir, 'lib', "#{options.adapter}_benchmark" )
41
-
42
- table_types = nil
43
- if options.benchmark_all_types
44
- table_types = [ "all" ]
45
- else
46
- table_types = options.table_types.keys
47
- end
48
-
49
- letter = options.adapter[0].chr
50
- clazz_str = letter.upcase + options.adapter[1..-1].downcase
51
- clazz = Object.const_get( clazz_str + "Benchmark" )
52
-
53
- benchmarks = []
54
- options.number_of_objects.each do |num|
55
- benchmarks << (benchmark = clazz.new)
56
- benchmark.send( "benchmark", table_types, num )
57
- end
58
-
59
- options.outputs.each do |output|
60
- format = output.format.downcase
61
- output_module = Object.const_get( "OutputTo#{format.upcase}" )
62
- benchmarks.each do |benchmark|
63
- output_module.output_results( output.filename, benchmark.results )
64
- end
65
- end
66
-
67
- puts
68
- puts "Done with benchmark!"
1
+ require 'pathname'
2
+ require "fileutils"
3
+ require "active_record"
4
+
5
+ benchmark_dir = File.dirname(__FILE__)
6
+
7
+ $LOAD_PATH.unshift('.')
8
+
9
+ # Get the gem into the load path
10
+ $LOAD_PATH.unshift(File.join(benchmark_dir, '..', 'lib'))
11
+
12
+ # Load the benchmark files
13
+ Dir[File.join( benchmark_dir, 'lib', '*.rb' )].sort.each { |f| require f }
14
+
15
+ # Parse the options passed in via the command line
16
+ options = BenchmarkOptionParser.parse( ARGV )
17
+
18
+ FileUtils.mkdir_p 'log'
19
+ ActiveRecord::Base.configurations["test"] = YAML.load_file(File.join(benchmark_dir, "../test/database.yml"))[options.adapter]
20
+ ActiveRecord::Base.logger = Logger.new("log/test.log")
21
+ ActiveRecord::Base.logger.level = Logger::DEBUG
22
+ ActiveRecord::Base.default_timezone = :utc
23
+
24
+ require "activerecord-import"
25
+ ActiveRecord::Base.establish_connection(:test)
26
+
27
+ ActiveSupport::Notifications.subscribe(/active_record.sql/) do |_, _, _, _, hsh|
28
+ ActiveRecord::Base.logger.info hsh[:sql]
29
+ end
30
+
31
+ # Load base/generic schema
32
+ require File.join(benchmark_dir, "../test/schema/version")
33
+ require File.join(benchmark_dir, "../test/schema/generic_schema")
34
+ adapter_schema = File.join(benchmark_dir, "schema/#{options.adapter}_schema.rb")
35
+ require adapter_schema if File.exist?(adapter_schema)
36
+
37
+ Dir[File.dirname(__FILE__) + "/models/*.rb"].each { |file| require file }
38
+
39
+ require File.join( benchmark_dir, 'lib', "#{options.adapter}_benchmark" )
40
+
41
+ table_types = nil
42
+ table_types = if options.benchmark_all_types
43
+ ["all"]
44
+ else
45
+ options.table_types.keys
46
+ end
47
+
48
+ letter = options.adapter[0].chr
49
+ clazz_str = letter.upcase + options.adapter[1..-1].downcase
50
+ clazz = Object.const_get( clazz_str + "Benchmark" )
51
+
52
+ benchmarks = []
53
+ options.number_of_objects.each do |num|
54
+ benchmarks << (benchmark = clazz.new)
55
+ benchmark.send( "benchmark", table_types, num )
56
+ end
57
+
58
+ options.outputs.each do |output|
59
+ format = output.format.downcase
60
+ output_module = Object.const_get( "OutputTo#{format.upcase}" )
61
+ benchmarks.each do |benchmark|
62
+ output_module.output_results( output.filename, benchmark.results )
63
+ end
64
+ end
65
+
66
+ puts
67
+ puts "Done with benchmark!"
@@ -1,137 +1,136 @@
1
- class BenchmarkBase
2
-
3
- attr_reader :results
4
-
5
- # The main benchmark method dispatcher. This dispatches the benchmarks
6
- # to actual benchmark_xxxx methods.
7
- #
8
- # == PARAMETERS
9
- # * table_types - an array of table types to benchmark
10
- # * num - the number of record insertions to test
11
- def benchmark( table_types, num )
12
- array_of_cols_and_vals = build_array_of_cols_and_vals( num )
13
- table_types.each do |table_type|
14
- self.send( "benchmark_#{table_type}", array_of_cols_and_vals )
15
- end
16
- end
17
-
18
- # Returns an OpenStruct which contains two attritues, +description+ and +tms+ after performing an
19
- # actual benchmark.
20
- #
21
- # == PARAMETERS
22
- # * description - the description of the block that is getting benchmarked
23
- # * blk - the block of code to benchmark
24
- #
25
- # == RETURNS
26
- # An OpenStruct object with the following attributes:
27
- # * description - the description of the benchmark ran
28
- # * tms - a Benchmark::Tms containing the results of the benchmark
29
- def bm( description, &blk )
30
- tms = nil
31
- puts "Benchmarking #{description}"
32
-
33
- Benchmark.bm { |x| tms = x.report { blk.call } }
34
- delete_all
35
- failed = false
36
-
37
- OpenStruct.new :description=>description, :tms=>tms, :failed=>failed
38
- end
39
-
40
- # Given a model class (ie: Topic), and an array of columns and value sets
41
- # this will perform all of the benchmarks necessary for this library.
42
- #
43
- # == PARAMETERS
44
- # * model_clazz - the model class to benchmark (ie: Topic)
45
- # * array_of_cols_and_vals - an array of column identifiers and value sets
46
- #
47
- # == RETURNS
48
- # returns true
49
- def bm_model( model_clazz, array_of_cols_and_vals )
50
- puts
51
- puts "------ Benchmarking #{model_clazz.name} -------"
52
-
53
- cols,vals = array_of_cols_and_vals
54
- num_inserts = vals.size
55
-
56
- # add a new result group for this particular benchmark
57
- group = []
58
- @results << group
59
-
60
- description = "#{model_clazz.name}.create (#{num_inserts} records)"
61
- group << bm( description ) {
62
- vals.each do |values|
63
- model_clazz.create create_hash_for_cols_and_vals( cols, values )
64
- end }
65
-
66
- description = "#{model_clazz.name}.import(column, values) for #{num_inserts} records with validations"
67
- group << bm( description ) { model_clazz.import cols, vals, :validate=>true }
68
-
69
- description = "#{model_clazz.name}.import(columns, values) for #{num_inserts} records without validations"
70
- group << bm( description ) { model_clazz.import cols, vals, :validate=>false }
71
-
72
- models = []
73
- array_of_attrs = []
74
-
75
- vals.each do |arr|
76
- array_of_attrs << (attrs={})
77
- arr.each_with_index { |value, i| attrs[cols[i]] = value }
78
- end
79
- array_of_attrs.each{ |attrs| models << model_clazz.new(attrs) }
80
-
81
- description = "#{model_clazz.name}.import(models) for #{num_inserts} records with validations"
82
- group << bm( description ) { model_clazz.import models, :validate=>true }
83
-
84
- description = "#{model_clazz.name}.import(models) for #{num_inserts} records without validations"
85
- group << bm( description ) { model_clazz.import models, :validate=>false }
86
-
87
- true
88
- end
89
-
90
- # Returns a two element array composing of an array of columns and an array of
91
- # value sets given the passed +num+.
92
- #
93
- # === What is a value set?
94
- # A value set is an array of arrays. Each child array represents an array of value sets
95
- # for a given row of data.
96
- #
97
- # For example, say we wanted to represent an insertion of two records:
98
- # column_names = [ 'id', 'name', 'description' ]
99
- # record1 = [ 1, 'John Doe', 'A plumber' ]
100
- # record2 = [ 2, 'John Smith', 'A painter' ]
101
- # value_set [ record1, record2 ]
102
- #
103
- # == PARAMETER
104
- # * num - the number of records to create
105
- def build_array_of_cols_and_vals( num )
106
- cols = [ :my_name, :description ]
107
- value_sets = []
108
- num.times { |i| value_sets << [ "My Name #{i}", "My Description #{i}" ] }
109
- [ cols, value_sets ]
110
- end
111
-
112
- # Returns a hash of column identifier to value mappings giving the passed in
113
- # value array.
114
- #
115
- # Example:
116
- # cols = [ 'id', 'name', 'description' ]
117
- # values = [ 1, 'John Doe', 'A plumber' ]
118
- # hsh = create_hash_for_cols_and_vals( cols, values )
119
- # # hsh => { 'id'=>1, 'name'=>'John Doe', 'description'=>'A plumber' }
120
- def create_hash_for_cols_and_vals( cols, vals )
121
- h = {}
122
- cols.zip( vals ){ |col,val| h[col] = val }
123
- h
124
- end
125
-
126
- # Deletes all records from all ActiveRecord subclasses
127
- def delete_all
128
- ActiveRecord::Base.send( :subclasses ).each do |subclass|
129
- subclass.delete_all if subclass.respond_to? :delete_all
130
- end
131
- end
132
-
133
- def initialize # :nodoc:
134
- @results = []
135
- end
136
-
137
- end
1
+ class BenchmarkBase
2
+ attr_reader :results
3
+
4
+ # The main benchmark method dispatcher. This dispatches the benchmarks
5
+ # to actual benchmark_xxxx methods.
6
+ #
7
+ # == PARAMETERS
8
+ # * table_types - an array of table types to benchmark
9
+ # * num - the number of record insertions to test
10
+ def benchmark( table_types, num )
11
+ array_of_cols_and_vals = build_array_of_cols_and_vals( num )
12
+ table_types.each do |table_type|
13
+ send( "benchmark_#{table_type}", array_of_cols_and_vals )
14
+ end
15
+ end
16
+
17
+ # Returns an OpenStruct which contains two attritues, +description+ and +tms+ after performing an
18
+ # actual benchmark.
19
+ #
20
+ # == PARAMETERS
21
+ # * description - the description of the block that is getting benchmarked
22
+ # * blk - the block of code to benchmark
23
+ #
24
+ # == RETURNS
25
+ # An OpenStruct object with the following attributes:
26
+ # * description - the description of the benchmark ran
27
+ # * tms - a Benchmark::Tms containing the results of the benchmark
28
+ def bm( description )
29
+ tms = nil
30
+ puts "Benchmarking #{description}"
31
+
32
+ Benchmark.bm { |x| tms = x.report { yield } }
33
+ delete_all
34
+ failed = false
35
+
36
+ OpenStruct.new description: description, tms: tms, failed: failed
37
+ end
38
+
39
+ # Given a model class (ie: Topic), and an array of columns and value sets
40
+ # this will perform all of the benchmarks necessary for this library.
41
+ #
42
+ # == PARAMETERS
43
+ # * model_clazz - the model class to benchmark (ie: Topic)
44
+ # * array_of_cols_and_vals - an array of column identifiers and value sets
45
+ #
46
+ # == RETURNS
47
+ # returns true
48
+ def bm_model( model_clazz, array_of_cols_and_vals )
49
+ puts
50
+ puts "------ Benchmarking #{model_clazz.name} -------"
51
+
52
+ cols, vals = array_of_cols_and_vals
53
+ num_inserts = vals.size
54
+
55
+ # add a new result group for this particular benchmark
56
+ group = []
57
+ @results << group
58
+
59
+ description = "#{model_clazz.name}.create (#{num_inserts} records)"
60
+ group << bm( description ) do
61
+ vals.each do |values|
62
+ model_clazz.create create_hash_for_cols_and_vals( cols, values )
63
+ end
64
+ end
65
+
66
+ description = "#{model_clazz.name}.import(column, values) for #{num_inserts} records with validations"
67
+ group << bm( description ) { model_clazz.import cols, vals, validate: true }
68
+
69
+ description = "#{model_clazz.name}.import(columns, values) for #{num_inserts} records without validations"
70
+ group << bm( description ) { model_clazz.import cols, vals, validate: false }
71
+
72
+ models = []
73
+ array_of_attrs = []
74
+
75
+ vals.each do |arr|
76
+ array_of_attrs << (attrs = {})
77
+ arr.each_with_index { |value, i| attrs[cols[i]] = value }
78
+ end
79
+ array_of_attrs.each { |attrs| models << model_clazz.new(attrs) }
80
+
81
+ description = "#{model_clazz.name}.import(models) for #{num_inserts} records with validations"
82
+ group << bm( description ) { model_clazz.import models, validate: true }
83
+
84
+ description = "#{model_clazz.name}.import(models) for #{num_inserts} records without validations"
85
+ group << bm( description ) { model_clazz.import models, validate: false }
86
+
87
+ true
88
+ end
89
+
90
+ # Returns a two element array composing of an array of columns and an array of
91
+ # value sets given the passed +num+.
92
+ #
93
+ # === What is a value set?
94
+ # A value set is an array of arrays. Each child array represents an array of value sets
95
+ # for a given row of data.
96
+ #
97
+ # For example, say we wanted to represent an insertion of two records:
98
+ # column_names = [ 'id', 'name', 'description' ]
99
+ # record1 = [ 1, 'John Doe', 'A plumber' ]
100
+ # record2 = [ 2, 'John Smith', 'A painter' ]
101
+ # value_set [ record1, record2 ]
102
+ #
103
+ # == PARAMETER
104
+ # * num - the number of records to create
105
+ def build_array_of_cols_and_vals( num )
106
+ cols = [:my_name, :description]
107
+ value_sets = []
108
+ num.times { |i| value_sets << ["My Name #{i}", "My Description #{i}"] }
109
+ [cols, value_sets]
110
+ end
111
+
112
+ # Returns a hash of column identifier to value mappings giving the passed in
113
+ # value array.
114
+ #
115
+ # Example:
116
+ # cols = [ 'id', 'name', 'description' ]
117
+ # values = [ 1, 'John Doe', 'A plumber' ]
118
+ # hsh = create_hash_for_cols_and_vals( cols, values )
119
+ # # hsh => { 'id'=>1, 'name'=>'John Doe', 'description'=>'A plumber' }
120
+ def create_hash_for_cols_and_vals( cols, vals )
121
+ h = {}
122
+ cols.zip( vals ) { |col, val| h[col] = val }
123
+ h
124
+ end
125
+
126
+ # Deletes all records from all ActiveRecord subclasses
127
+ def delete_all
128
+ ActiveRecord::Base.send( :subclasses ).each do |subclass|
129
+ subclass.delete_all if subclass.respond_to? :delete_all
130
+ end
131
+ end
132
+
133
+ def initialize # :nodoc:
134
+ @results = []
135
+ end
136
+ end