activerecord-import-uuid 0.1

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 (98) hide show
  1. data/.gitignore +32 -0
  2. data/.rubocop.yml +49 -0
  3. data/.rubocop_todo.yml +36 -0
  4. data/.travis.yml +52 -0
  5. data/Brewfile +3 -0
  6. data/CHANGELOG.md +87 -0
  7. data/Gemfile +54 -0
  8. data/LICENSE +56 -0
  9. data/README.markdown +101 -0
  10. data/Rakefile +66 -0
  11. data/activerecord-import.gemspec +23 -0
  12. data/benchmarks/README +32 -0
  13. data/benchmarks/benchmark.rb +67 -0
  14. data/benchmarks/lib/base.rb +138 -0
  15. data/benchmarks/lib/cli_parser.rb +106 -0
  16. data/benchmarks/lib/float.rb +15 -0
  17. data/benchmarks/lib/mysql2_benchmark.rb +19 -0
  18. data/benchmarks/lib/output_to_csv.rb +19 -0
  19. data/benchmarks/lib/output_to_html.rb +64 -0
  20. data/benchmarks/models/test_innodb.rb +3 -0
  21. data/benchmarks/models/test_memory.rb +3 -0
  22. data/benchmarks/models/test_myisam.rb +3 -0
  23. data/benchmarks/schema/mysql_schema.rb +16 -0
  24. data/gemfiles/3.2.gemfile +3 -0
  25. data/gemfiles/4.0.gemfile +3 -0
  26. data/gemfiles/4.1.gemfile +3 -0
  27. data/gemfiles/4.2.gemfile +7 -0
  28. data/gemfiles/5.0.gemfile +3 -0
  29. data/lib/activerecord-import.rb +19 -0
  30. data/lib/activerecord-import/active_record/adapters/abstract_adapter.rb +9 -0
  31. data/lib/activerecord-import/active_record/adapters/jdbcmysql_adapter.rb +6 -0
  32. data/lib/activerecord-import/active_record/adapters/jdbcpostgresql_adapter.rb +6 -0
  33. data/lib/activerecord-import/active_record/adapters/mysql2_adapter.rb +6 -0
  34. data/lib/activerecord-import/active_record/adapters/postgresql_adapter.rb +6 -0
  35. data/lib/activerecord-import/active_record/adapters/seamless_database_pool_adapter.rb +7 -0
  36. data/lib/activerecord-import/active_record/adapters/sqlite3_adapter.rb +6 -0
  37. data/lib/activerecord-import/adapters/abstract_adapter.rb +78 -0
  38. data/lib/activerecord-import/adapters/em_mysql2_adapter.rb +5 -0
  39. data/lib/activerecord-import/adapters/mysql2_adapter.rb +5 -0
  40. data/lib/activerecord-import/adapters/mysql_adapter.rb +114 -0
  41. data/lib/activerecord-import/adapters/postgresql_adapter.rb +144 -0
  42. data/lib/activerecord-import/adapters/sqlite3_adapter.rb +51 -0
  43. data/lib/activerecord-import/base.rb +38 -0
  44. data/lib/activerecord-import/import.rb +660 -0
  45. data/lib/activerecord-import/mysql2.rb +7 -0
  46. data/lib/activerecord-import/postgresql.rb +7 -0
  47. data/lib/activerecord-import/sqlite3.rb +7 -0
  48. data/lib/activerecord-import/synchronize.rb +66 -0
  49. data/lib/activerecord-import/value_sets_parser.rb +55 -0
  50. data/lib/activerecord-import/version.rb +5 -0
  51. data/test/adapters/jdbcmysql.rb +1 -0
  52. data/test/adapters/jdbcpostgresql.rb +1 -0
  53. data/test/adapters/mysql2.rb +1 -0
  54. data/test/adapters/mysql2_makara.rb +1 -0
  55. data/test/adapters/mysql2spatial.rb +1 -0
  56. data/test/adapters/postgis.rb +1 -0
  57. data/test/adapters/postgresql.rb +1 -0
  58. data/test/adapters/postgresql_makara.rb +1 -0
  59. data/test/adapters/seamless_database_pool.rb +1 -0
  60. data/test/adapters/spatialite.rb +1 -0
  61. data/test/adapters/sqlite3.rb +1 -0
  62. data/test/database.yml.sample +52 -0
  63. data/test/import_test.rb +574 -0
  64. data/test/jdbcmysql/import_test.rb +6 -0
  65. data/test/jdbcpostgresql/import_test.rb +5 -0
  66. data/test/models/book.rb +7 -0
  67. data/test/models/chapter.rb +4 -0
  68. data/test/models/discount.rb +3 -0
  69. data/test/models/end_note.rb +4 -0
  70. data/test/models/group.rb +3 -0
  71. data/test/models/promotion.rb +3 -0
  72. data/test/models/question.rb +3 -0
  73. data/test/models/rule.rb +3 -0
  74. data/test/models/topic.rb +9 -0
  75. data/test/models/widget.rb +24 -0
  76. data/test/mysql2/import_test.rb +5 -0
  77. data/test/mysql2_makara/import_test.rb +6 -0
  78. data/test/mysqlspatial2/import_test.rb +6 -0
  79. data/test/postgis/import_test.rb +4 -0
  80. data/test/postgresql/import_test.rb +8 -0
  81. data/test/schema/generic_schema.rb +144 -0
  82. data/test/schema/mysql_schema.rb +16 -0
  83. data/test/schema/version.rb +10 -0
  84. data/test/sqlite3/import_test.rb +52 -0
  85. data/test/support/active_support/test_case_extensions.rb +70 -0
  86. data/test/support/assertions.rb +73 -0
  87. data/test/support/factories.rb +57 -0
  88. data/test/support/generate.rb +29 -0
  89. data/test/support/mysql/import_examples.rb +85 -0
  90. data/test/support/postgresql/import_examples.rb +242 -0
  91. data/test/support/shared_examples/on_duplicate_key_update.rb +103 -0
  92. data/test/support/shared_examples/recursive_import.rb +122 -0
  93. data/test/synchronize_test.rb +33 -0
  94. data/test/test_helper.rb +59 -0
  95. data/test/travis/database.yml +62 -0
  96. data/test/value_sets_bytes_parser_test.rb +93 -0
  97. data/test/value_sets_records_parser_test.rb +32 -0
  98. metadata +225 -0
data/Rakefile ADDED
@@ -0,0 +1,66 @@
1
+ require "bundler"
2
+ Bundler.setup
3
+
4
+ require 'rake'
5
+ require 'rake/testtask'
6
+
7
+ namespace :display do
8
+ task :notice do
9
+ puts
10
+ puts "To run tests you must supply the adapter, see rake -T for more information."
11
+ puts
12
+ end
13
+ end
14
+ task default: ["display:notice"]
15
+
16
+ ADAPTERS = %w(
17
+ mysql2
18
+ mysql2_makara
19
+ mysql2spatial
20
+ jdbcmysql
21
+ jdbcpostgresql
22
+ postgresql
23
+ postgresql_makara
24
+ postgis
25
+ sqlite3
26
+ spatialite
27
+ seamless_database_pool
28
+ ).freeze
29
+ ADAPTERS.each do |adapter|
30
+ namespace :test do
31
+ desc "Runs #{adapter} database tests."
32
+ Rake::TestTask.new(adapter) do |t|
33
+ # FactoryGirl has an issue with warnings, so turn off, so noisy
34
+ # t.warning = true
35
+ t.test_files = FileList["test/adapters/#{adapter}.rb", "test/*_test.rb", "test/active_record/*_test.rb", "test/#{adapter}/**/*_test.rb"]
36
+ end
37
+ task adapter
38
+ end
39
+ end
40
+
41
+ begin
42
+ require 'rcov/rcovtask'
43
+ adapter = ENV['ARE_DB']
44
+ Rcov::RcovTask.new do |test|
45
+ test.libs << 'test'
46
+ test.pattern = ["test/adapters/#{adapter}.rb", "test/*_test.rb", "test/#{adapter}/**/*_test.rb"]
47
+ test.verbose = true
48
+ end
49
+ rescue LoadError
50
+ task :rcov do
51
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install rcov"
52
+ end
53
+ end
54
+
55
+ require 'rdoc/task'
56
+ Rake::RDocTask.new do |rdoc|
57
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
58
+
59
+ rdoc.rdoc_dir = 'rdoc'
60
+ rdoc.title = "activerecord-import #{version}"
61
+ rdoc.rdoc_files.include('README*')
62
+ rdoc.rdoc_files.include('lib/**/*.rb')
63
+ end
64
+
65
+ require 'rubocop/rake_task'
66
+ RuboCop::RakeTask.new
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/activerecord-import/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Zach Dennis", "Shiv Bharthur"]
6
+ gem.email = ["zach.dennis@gmail.com", "shiv.bharthur@gmail.com"]
7
+ gem.summary = "Bulk-loading extension for ActiveRecord"
8
+ gem.description = "Extraction of the ActiveRecord::Base#import functionality from ar-extensions for Rails 3 and beyond"
9
+ gem.homepage = "http://github.com/bharthur/activerecord-import"
10
+ gem.license = "Ruby"
11
+
12
+ gem.files = `git ls-files`.split($\)
13
+ gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
14
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
15
+ gem.name = "activerecord-import-uuid"
16
+ gem.require_paths = ["lib"]
17
+ gem.version = ActiveRecord::Import::VERSION
18
+
19
+ gem.required_ruby_version = ">= 1.9.2"
20
+
21
+ gem.add_runtime_dependency "activerecord", ">= 3.2"
22
+ gem.add_development_dependency "rake"
23
+ end
data/benchmarks/README ADDED
@@ -0,0 +1,32 @@
1
+ To run the benchmarks, from within the benchmarks run:
2
+ ruby benchmark.rb [options]
3
+
4
+ The following options are supported:
5
+ --adapter [String] The database adapter to use. IE: mysql, postgresql, oracle
6
+
7
+ --do-not-delete By default all records in the benchmark tables will be deleted at the end of the benchmark. This flag indicates not to delete the benchmark data.
8
+ --num [Integer] The number of objects to benchmark. (Required!)
9
+ --table-type [String] The table type to test. This can be used multiple times. By default it is all table types.
10
+ --to-csv [String] Print results in a CSV file format
11
+ --to-html [String] Print results in HTML format (String filename must be supplied)
12
+
13
+ See "ruby benchmark.rb -h" for the complete listing of options.
14
+
15
+ EXAMPLES
16
+ --------
17
+ To output to html format:
18
+ ruby benchmark.rb --adapter=mysql --to-html=results.html
19
+
20
+ To output to csv format:
21
+ ruby benchmark.rb --adapter=mysql --to-csv=results.csv
22
+
23
+ LIMITATIONS
24
+ -----------
25
+ Currently MySQL is the only supported adapter to benchmark.
26
+
27
+ AUTHOR
28
+ ------
29
+ Zach Dennis
30
+ zach.dennis@gmail.com
31
+ http://www.continuousthinking.com
32
+
@@ -0,0 +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 |_, _, _, _, 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!"
@@ -0,0 +1,138 @@
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
+ if subclass.table_exists? && subclass.respond_to?(:delete_all)
130
+ subclass.delete_all
131
+ end
132
+ end
133
+ end
134
+
135
+ def initialize # :nodoc:
136
+ @results = []
137
+ end
138
+ end
@@ -0,0 +1,106 @@
1
+ require 'optparse'
2
+ require 'ostruct'
3
+
4
+ #
5
+ # == PARAMETERS
6
+ # * a - database adapter. ie: mysql, postgresql, oracle, etc.
7
+ # * n - number of objects to test with. ie: 1, 100, 1000, etc.
8
+ # * t - the table types to test. ie: myisam, innodb, memory, temporary, etc.
9
+ #
10
+ module BenchmarkOptionParser
11
+ BANNER = "Usage: ruby #{$0} [options]\nSee ruby #{$0} -h for more options.".freeze
12
+
13
+ def self.print_banner
14
+ puts BANNER
15
+ end
16
+
17
+ def self.print_banner!
18
+ print_banner
19
+ exit
20
+ end
21
+
22
+ def self.print_options( options )
23
+ puts "Benchmarking the following options:"
24
+ puts " Database adapter: #{options.adapter}"
25
+ puts " Number of objects: #{options.number_of_objects}"
26
+ puts " Table types:"
27
+ print_valid_table_types( options, prefix: " " )
28
+ end
29
+
30
+ # TODO IMPLEMENT THIS
31
+ def self.print_valid_table_types( options, hsh = { prefix: '' } )
32
+ if !options.table_types.keys.empty?
33
+ options.table_types.keys.sort.each { |type| puts hsh[:prefix].to_s + type.to_s }
34
+ else
35
+ puts 'No table types defined.'
36
+ end
37
+ end
38
+
39
+ def self.parse( args )
40
+ options = OpenStruct.new(
41
+ adapter: 'mysql2',
42
+ table_types: {},
43
+ delete_on_finish: true,
44
+ number_of_objects: [],
45
+ outputs: [] )
46
+
47
+ opt_parser = OptionParser.new do |opts|
48
+ opts.banner = BANNER
49
+
50
+ # parse the database adapter
51
+ opts.on( "a", "--adapter [String]",
52
+ "The database adapter to use. IE: mysql, postgresql, oracle" ) do |arg|
53
+ options.adapter = arg
54
+ end
55
+
56
+ # parse do_not_delete flag
57
+ opts.on( "d", "--do-not-delete",
58
+ "By default all records in the benchmark tables will be deleted at the end of the benchmark. " \
59
+ "This flag indicates not to delete the benchmark data." ) do |_|
60
+ options.delete_on_finish = false
61
+ end
62
+
63
+ # parse the number of row objects to test
64
+ opts.on( "n", "--num [Integer]",
65
+ "The number of objects to benchmark." ) do |arg|
66
+ options.number_of_objects << arg.to_i
67
+ end
68
+
69
+ # parse the table types to test
70
+ opts.on( "t", "--table-type [String]",
71
+ "The table type to test. This can be used multiple times." ) do |arg|
72
+ if arg =~ /^all$/
73
+ options.table_types['all'] = options.benchmark_all_types = true
74
+ else
75
+ options.table_types[arg] = true
76
+ end
77
+ end
78
+
79
+ # print results in CSV format
80
+ opts.on( "--to-csv [String]", "Print results in a CSV file format" ) do |filename|
81
+ options.outputs << OpenStruct.new( format: 'csv', filename: filename)
82
+ end
83
+
84
+ # print results in HTML format
85
+ opts.on( "--to-html [String]", "Print results in HTML format" ) do |filename|
86
+ options.outputs << OpenStruct.new( format: 'html', filename: filename )
87
+ end
88
+ end # end opt.parse!
89
+
90
+ begin
91
+ opt_parser.parse!( args )
92
+ if options.table_types.empty?
93
+ options.table_types['all'] = options.benchmark_all_types = true
94
+ end
95
+ rescue Exception
96
+ print_banner!
97
+ end
98
+
99
+ options.number_of_objects = [1000] if options.number_of_objects.empty?
100
+ options.outputs = [OpenStruct.new( format: 'html', filename: 'benchmark.html')] if options.outputs.empty?
101
+
102
+ print_options( options )
103
+
104
+ options
105
+ end
106
+ end
@@ -0,0 +1,15 @@
1
+ # Taken from http://www.programmingishard.com/posts/show/128
2
+ # Posted by rbates
3
+ class Float
4
+ def round_to(x)
5
+ (self * 10**x).round.to_f / 10**x
6
+ end
7
+
8
+ def ceil_to(x)
9
+ (self * 10**x).ceil.to_f / 10**x
10
+ end
11
+
12
+ def floor_to(x)
13
+ (self * 10**x).floor.to_f / 10**x
14
+ end
15
+ end