activerecord-import-0.10.0/0000755000004100000410000000000012557352107015611 5ustar www-datawww-dataactiverecord-import-0.10.0/Rakefile0000644000004100000410000000267112557352107017264 0ustar www-datawww-datarequire "bundler" Bundler.setup require 'rake' require 'rake/testtask' namespace :display do task :notice do puts puts "To run tests you must supply the adapter, see rake -T for more information." puts end end task :default => ["display:notice"] ADAPTERS = %w(mysql mysql2 em_mysql2 jdbcmysql jdbcpostgresql postgresql sqlite3 seamless_database_pool mysqlspatial mysql2spatial spatialite postgis) ADAPTERS.each do |adapter| namespace :test do desc "Runs #{adapter} database tests." Rake::TestTask.new(adapter) do |t| # FactoryGirl has an issue with warnings, so turn off, so noisy # t.warning = true t.test_files = FileList["test/adapters/#{adapter}.rb", "test/*_test.rb", "test/active_record/*_test.rb", "test/#{adapter}/**/*_test.rb"] end task adapter end end begin require 'rcov/rcovtask' adapter = ENV['ARE_DB'] Rcov::RcovTask.new do |test| test.libs << 'test' test.pattern = ["test/adapters/#{adapter}.rb", "test/*_test.rb", "test/#{adapter}/**/*_test.rb"] test.verbose = true end rescue LoadError task :rcov do abort "RCov is not available. In order to run rcov, you must: sudo gem install rcov" end end require 'rdoc/task' Rake::RDocTask.new do |rdoc| version = File.exist?('VERSION') ? File.read('VERSION') : "" rdoc.rdoc_dir = 'rdoc' rdoc.title = "activerecord-import #{version}" rdoc.rdoc_files.include('README*') rdoc.rdoc_files.include('lib/**/*.rb') end activerecord-import-0.10.0/activerecord-import.gemspec0000644000004100000410000000170112557352107023137 0ustar www-datawww-data# -*- encoding: utf-8 -*- require File.expand_path('../lib/activerecord-import/version', __FILE__) Gem::Specification.new do |gem| gem.authors = ["Zach Dennis"] gem.email = ["zach.dennis@gmail.com"] gem.summary = "Bulk-loading extension for ActiveRecord" gem.description = "Extraction of the ActiveRecord::Base#import functionality from ar-extensions for Rails 3 and beyond" gem.homepage = "http://github.com/zdennis/activerecord-import" gem.license = "Ruby" gem.files = `git ls-files`.split($\) gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) } gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) gem.name = "activerecord-import" gem.require_paths = ["lib"] gem.version = ActiveRecord::Import::VERSION gem.required_ruby_version = ">= 1.9.2" gem.add_runtime_dependency "activerecord", ">= 3.0" gem.add_development_dependency "rake" end activerecord-import-0.10.0/Gemfile0000644000004100000410000000152612557352107017110 0ustar www-datawww-datasource 'https://rubygems.org' gemspec # Database Adapters platforms :ruby do gem "em-synchrony", "1.0.3" gem "mysql2", "~> 0.3.0" gem "pg", "~> 0.9" gem "sqlite3-ruby", "~> 1.3.1" gem "seamless_database_pool", "~> 1.0.13" end platforms :jruby do gem "jdbc-mysql" gem "jdbc-postgres" gem "activerecord-jdbcmysql-adapter" gem "activerecord-jdbcpostgresql-adapter" end # Support libs gem "factory_girl", "~> 4.2.0" gem "timecop" gem "chronic" # Debugging platforms :jruby do gem "ruby-debug-base", "= 0.10.4" end platforms :jruby, :mri_18 do gem "ruby-debug", "= 0.10.4" end platforms :mri_19 do gem "debugger" end version = ENV['AR_VERSION'] || "3.2" if version > "4.0" gem "minitest" end eval_gemfile File.expand_path("../gemfiles/#{version}.gemfile", __FILE__) activerecord-import-0.10.0/Brewfile0000644000004100000410000000005412557352107017272 0ustar www-datawww-databrew "mysql" brew "postgresql" brew "sqlite"activerecord-import-0.10.0/.travis.yml0000644000004100000410000000072412557352107017725 0ustar www-datawww-datalanguage: ruby cache: bundler rvm: - 2.0.0 gemfile: - Gemfile bundler_args: -j2 before_script: - mysql -e 'create database activerecord_import_test;' - psql -c 'create database activerecord_import_test;' -U postgres - psql -U postgres -c "create extension postgis" - cp test/travis/database.yml test/database.yml # https://github.com/discourse/discourse/blob/master/.travis.yml - export RUBY_GC_MALLOC_LIMIT=50000000 script: - test/travis/build.sh activerecord-import-0.10.0/lib/0000755000004100000410000000000012557352107016357 5ustar www-datawww-dataactiverecord-import-0.10.0/lib/activerecord-import/0000755000004100000410000000000012557352107022341 5ustar www-datawww-dataactiverecord-import-0.10.0/lib/activerecord-import/mysql2.rb0000644000004100000410000000046212557352107024117 0ustar www-datawww-datawarn <<-MSG [DEPRECATION] loading activerecord-import via 'require "activerecord-import/"' is deprecated. Update to autorequire using 'require "activerecord-import"'. See http://github.com/zdennis/activerecord-import/wiki/Requiring for more information MSG require "activerecord-import" activerecord-import-0.10.0/lib/activerecord-import/mysql.rb0000644000004100000410000000046212557352107024035 0ustar www-datawww-datawarn <<-MSG [DEPRECATION] loading activerecord-import via 'require "activerecord-import/"' is deprecated. Update to autorequire using 'require "activerecord-import"'. See http://github.com/zdennis/activerecord-import/wiki/Requiring for more information MSG require "activerecord-import" activerecord-import-0.10.0/lib/activerecord-import/adapters/0000755000004100000410000000000012557352107024144 5ustar www-datawww-dataactiverecord-import-0.10.0/lib/activerecord-import/adapters/postgresql_adapter.rb0000644000004100000410000000151312557352107030374 0ustar www-datawww-datamodule ActiveRecord::Import::PostgreSQLAdapter include ActiveRecord::Import::ImportSupport def insert_many( sql, values, *args ) # :nodoc: number_of_inserts = 1 base_sql,post_sql = if sql.is_a?( String ) [ sql, '' ] elsif sql.is_a?( Array ) [ sql.shift, sql.join( ' ' ) ] end sql2insert = base_sql + values.join( ',' ) + post_sql ids = select_values( sql2insert, *args ) [number_of_inserts,ids] end def next_value_for_sequence(sequence_name) %{nextval('#{sequence_name}')} end def post_sql_statements( table_name, options ) # :nodoc: unless options[:primary_key].blank? super(table_name, options) << (" RETURNING #{options[:primary_key]}") else super(table_name, options) end end def support_setting_primary_key_of_imported_objects? true end end activerecord-import-0.10.0/lib/activerecord-import/adapters/mysql2_adapter.rb0000644000004100000410000000022212557352107027414 0ustar www-datawww-datarequire "activerecord-import/adapters/mysql_adapter" module ActiveRecord::Import::Mysql2Adapter include ActiveRecord::Import::MysqlAdapter end activerecord-import-0.10.0/lib/activerecord-import/adapters/sqlite3_adapter.rb0000644000004100000410000000245712557352107027565 0ustar www-datawww-datamodule ActiveRecord::Import::SQLite3Adapter include ActiveRecord::Import::ImportSupport MIN_VERSION_FOR_IMPORT = "3.7.11" SQLITE_LIMIT_COMPOUND_SELECT = 500 # Override our conformance to ActiveRecord::Import::ImportSupport interface # to ensure that we only support import in supported version of SQLite. # Which INSERT statements with multiple value sets was introduced in 3.7.11. def supports_import?(current_version=self.sqlite_version) if current_version >= MIN_VERSION_FOR_IMPORT true else false end end # +sql+ can be a single string or an array. If it is an array all # elements that are in position >= 1 will be appended to the final SQL. def insert_many(sql, values, *args) # :nodoc: number_of_inserts = 0 base_sql,post_sql = if sql.is_a?( String ) [ sql, '' ] elsif sql.is_a?( Array ) [ sql.shift, sql.join( ' ' ) ] end value_sets = ::ActiveRecord::Import::ValueSetsRecordsParser.parse(values, :max_records => SQLITE_LIMIT_COMPOUND_SELECT) value_sets.each do |values| number_of_inserts += 1 sql2insert = base_sql + values.join( ',' ) + post_sql insert( sql2insert, *args ) end [number_of_inserts,[]] end def next_value_for_sequence(sequence_name) %{nextval('#{sequence_name}')} end end activerecord-import-0.10.0/lib/activerecord-import/adapters/em_mysql2_adapter.rb0000644000004100000410000000022412557352107030077 0ustar www-datawww-datarequire "activerecord-import/adapters/mysql_adapter" module ActiveRecord::Import::EMMysql2Adapter include ActiveRecord::Import::MysqlAdapter end activerecord-import-0.10.0/lib/activerecord-import/adapters/abstract_adapter.rb0000644000004100000410000000352512557352107030001 0ustar www-datawww-datamodule ActiveRecord::Import::AbstractAdapter module InstanceMethods def next_value_for_sequence(sequence_name) %{#{sequence_name}.nextval} end def insert_many( sql, values, *args ) # :nodoc: number_of_inserts = 1 base_sql,post_sql = if sql.is_a?( String ) [ sql, '' ] elsif sql.is_a?( Array ) [ sql.shift, sql.join( ' ' ) ] end sql2insert = base_sql + values.join( ',' ) + post_sql insert( sql2insert, *args ) [number_of_inserts,[]] end def pre_sql_statements(options) sql = [] sql << options[:pre_sql] if options[:pre_sql] sql << options[:command] if options[:command] sql << "IGNORE" if options[:ignore] #add keywords like IGNORE or DELAYED if options[:keywords].is_a?(Array) sql.concat(options[:keywords]) elsif options[:keywords] sql << options[:keywords].to_s end sql end # Synchronizes the passed in ActiveRecord instances with the records in # the database by calling +reload+ on each instance. def after_import_synchronize( instances ) instances.each { |e| e.reload } end # Returns an array of post SQL statements given the passed in options. def post_sql_statements( table_name, options ) # :nodoc: post_sql_statements = [] if options[:on_duplicate_key_update] post_sql_statements << sql_for_on_duplicate_key_update( table_name, options[:on_duplicate_key_update] ) end #custom user post_sql post_sql_statements << options[:post_sql] if options[:post_sql] #with rollup post_sql_statements << rollup_sql if options[:rollup] post_sql_statements end # Returns the maximum number of bytes that the server will allow # in a single packet def max_allowed_packet NO_MAX_PACKET end end end activerecord-import-0.10.0/lib/activerecord-import/adapters/mysql_adapter.rb0000644000004100000410000000666712557352107027355 0ustar www-datawww-datamodule ActiveRecord::Import::MysqlAdapter include ActiveRecord::Import::ImportSupport include ActiveRecord::Import::OnDuplicateKeyUpdateSupport NO_MAX_PACKET = 0 QUERY_OVERHEAD = 8 #This was shown to be true for MySQL, but it's not clear where the overhead is from. # +sql+ can be a single string or an array. If it is an array all # elements that are in position >= 1 will be appended to the final SQL. def insert_many( sql, values, *args ) # :nodoc: # the number of inserts default number_of_inserts = 0 base_sql,post_sql = if sql.is_a?( String ) [ sql, '' ] elsif sql.is_a?( Array ) [ sql.shift, sql.join( ' ' ) ] end sql_size = QUERY_OVERHEAD + base_sql.size + post_sql.size # the number of bytes the requested insert statement values will take up values_in_bytes = values.sum {|value| value.bytesize } # the number of bytes (commas) it will take to comma separate our values comma_separated_bytes = values.size-1 # the total number of bytes required if this statement is one statement total_bytes = sql_size + values_in_bytes + comma_separated_bytes max = max_allowed_packet # if we can insert it all as one statement if NO_MAX_PACKET == max or total_bytes < max number_of_inserts += 1 sql2insert = base_sql + values.join( ',' ) + post_sql insert( sql2insert, *args ) else value_sets = ::ActiveRecord::Import::ValueSetsBytesParser.parse(values, :reserved_bytes => sql_size, :max_bytes => max) value_sets.each do |values| number_of_inserts += 1 sql2insert = base_sql + values.join( ',' ) + post_sql insert( sql2insert, *args ) end end [number_of_inserts,[]] end # Returns the maximum number of bytes that the server will allow # in a single packet def max_allowed_packet # :nodoc: @max_allowed_packet ||= begin result = execute( "SHOW VARIABLES like 'max_allowed_packet';" ) # original Mysql gem responds to #fetch_row while Mysql2 responds to #first val = result.respond_to?(:fetch_row) ? result.fetch_row[1] : result.first[1] val.to_i end end # Returns a generated ON DUPLICATE KEY UPDATE statement given the passed # in +args+. def sql_for_on_duplicate_key_update( table_name, *args ) # :nodoc: sql = ' ON DUPLICATE KEY UPDATE ' arg = args.first if arg.is_a?( Array ) sql << sql_for_on_duplicate_key_update_as_array( table_name, arg ) elsif arg.is_a?( Hash ) sql << sql_for_on_duplicate_key_update_as_hash( table_name, arg ) elsif arg.is_a?( String ) sql << arg else raise ArgumentError.new( "Expected Array or Hash" ) end sql end def sql_for_on_duplicate_key_update_as_array( table_name, arr ) # :nodoc: results = arr.map do |column| qc = quote_column_name( column ) "#{table_name}.#{qc}=VALUES(#{qc})" end results.join( ',' ) end def sql_for_on_duplicate_key_update_as_hash( table_name, hsh ) # :nodoc: sql = ' ON DUPLICATE KEY UPDATE ' results = hsh.map do |column1, column2| qc1 = quote_column_name( column1 ) qc2 = quote_column_name( column2 ) "#{table_name}.#{qc1}=VALUES( #{qc2} )" end results.join( ',') end #return true if the statement is a duplicate key record error def duplicate_key_update_error?(exception)# :nodoc: exception.is_a?(ActiveRecord::StatementInvalid) && exception.to_s.include?('Duplicate entry') end end activerecord-import-0.10.0/lib/activerecord-import/postgresql.rb0000644000004100000410000000046212557352107025073 0ustar www-datawww-datawarn <<-MSG [DEPRECATION] loading activerecord-import via 'require "activerecord-import/"' is deprecated. Update to autorequire using 'require "activerecord-import"'. See http://github.com/zdennis/activerecord-import/wiki/Requiring for more information MSG require "activerecord-import" activerecord-import-0.10.0/lib/activerecord-import/sqlite3.rb0000644000004100000410000000046212557352107024254 0ustar www-datawww-datawarn <<-MSG [DEPRECATION] loading activerecord-import via 'require "activerecord-import/"' is deprecated. Update to autorequire using 'require "activerecord-import"'. See http://github.com/zdennis/activerecord-import/wiki/Requiring for more information MSG require "activerecord-import" activerecord-import-0.10.0/lib/activerecord-import/synchronize.rb0000644000004100000410000000520612557352107025244 0ustar www-datawww-datamodule ActiveRecord # :nodoc: class Base # :nodoc: # Synchronizes the passed in ActiveRecord instances with data # from the database. This is like calling reload on an individual # ActiveRecord instance but it is intended for use on multiple instances. # # This uses one query for all instance updates and then updates existing # instances rather sending one query for each instance # # == Examples # # Synchronizing existing models by matching on the primary key field # posts = Post.where(author: "Zach").first # <.. out of system changes occur to change author name from Zach to Zachary..> # Post.synchronize posts # posts.first.author # => "Zachary" instead of Zach # # # Synchronizing using custom key fields # posts = Post.where(author: "Zach").first # <.. out of system changes occur to change the address of author 'Zach' to 1245 Foo Ln ..> # Post.synchronize posts, [:name] # queries on the :name column and not the :id column # posts.first.address # => "1245 Foo Ln" instead of whatever it was # def self.synchronize(instances, keys=[self.primary_key]) return if instances.empty? conditions = {} order = "" key_values = keys.map { |key| instances.map(&"#{key}".to_sym) } keys.zip(key_values).each { |key, values| conditions[key] = values } order = keys.map{ |key| "#{key} ASC" }.join(",") klass = instances.first.class fresh_instances = klass.where(conditions).order(order) instances.each do |instance| matched_instance = fresh_instances.detect do |fresh_instance| keys.all?{ |key| fresh_instance.send(key) == instance.send(key) } end if matched_instance instance.clear_aggregation_cache instance.clear_association_cache instance.instance_variable_set :@attributes, matched_instance.instance_variable_get(:@attributes) if instance.respond_to?(:clear_changes_information) instance.clear_changes_information # Rails 4.1 and higher else instance.changed_attributes.clear # Rails 3.1, 3.2 end # Since the instance now accurately reflects the record in # the database, ensure that instance.persisted? is true. instance.instance_variable_set '@new_record', false instance.instance_variable_set '@destroyed', false end end end # See ActiveRecord::ConnectionAdapters::AbstractAdapter.synchronize def synchronize(instances, key=[ActiveRecord::Base.primary_key]) self.class.synchronize(instances, key) end end end activerecord-import-0.10.0/lib/activerecord-import/active_record/0000755000004100000410000000000012557352107025152 5ustar www-datawww-dataactiverecord-import-0.10.0/lib/activerecord-import/active_record/adapters/0000755000004100000410000000000012557352107026755 5ustar www-datawww-dataactiverecord-import-0.10.0/lib/activerecord-import/active_record/adapters/postgresql_adapter.rb0000644000004100000410000000035212557352107033205 0ustar www-datawww-datarequire "active_record/connection_adapters/postgresql_adapter" require "activerecord-import/adapters/postgresql_adapter" class ActiveRecord::ConnectionAdapters::PostgreSQLAdapter include ActiveRecord::Import::PostgreSQLAdapter end activerecord-import-0.10.0/lib/activerecord-import/active_record/adapters/jdbcpostgresql_adapter.rb0000644000004100000410000000035212557352107034030 0ustar www-datawww-datarequire "active_record/connection_adapters/postgresql_adapter" require "activerecord-import/adapters/postgresql_adapter" class ActiveRecord::ConnectionAdapters::PostgreSQLAdapter include ActiveRecord::Import::PostgreSQLAdapter end activerecord-import-0.10.0/lib/activerecord-import/active_record/adapters/jdbcmysql_adapter.rb0000644000004100000410000000032612557352107032773 0ustar www-datawww-datarequire "active_record/connection_adapters/mysql_adapter" require "activerecord-import/adapters/mysql_adapter" class ActiveRecord::ConnectionAdapters::MysqlAdapter include ActiveRecord::Import::MysqlAdapter end activerecord-import-0.10.0/lib/activerecord-import/active_record/adapters/mysql2_adapter.rb0000644000004100000410000000033212557352107032227 0ustar www-datawww-datarequire "active_record/connection_adapters/mysql2_adapter" require "activerecord-import/adapters/mysql2_adapter" class ActiveRecord::ConnectionAdapters::Mysql2Adapter include ActiveRecord::Import::Mysql2Adapter end activerecord-import-0.10.0/lib/activerecord-import/active_record/adapters/sqlite3_adapter.rb0000644000004100000410000000033712557352107032371 0ustar www-datawww-datarequire "active_record/connection_adapters/sqlite3_adapter" require "activerecord-import/adapters/sqlite3_adapter" class ActiveRecord::ConnectionAdapters::SQLite3Adapter include ActiveRecord::Import::SQLite3Adapter end activerecord-import-0.10.0/lib/activerecord-import/active_record/adapters/em_mysql2_adapter.rb0000644000004100000410000000037712557352107032721 0ustar www-datawww-datarequire "em-synchrony" require "em-synchrony/mysql2" require "em-synchrony/activerecord" require "activerecord-import/adapters/em_mysql2_adapter" class ActiveRecord::ConnectionAdapters::EMMysql2Adapter include ActiveRecord::Import::EMMysql2Adapter end activerecord-import-0.10.0/lib/activerecord-import/active_record/adapters/abstract_adapter.rb0000644000004100000410000000037012557352107032605 0ustar www-datawww-datarequire "activerecord-import/adapters/abstract_adapter" module ActiveRecord # :nodoc: module ConnectionAdapters # :nodoc: class AbstractAdapter # :nodoc: include ActiveRecord::Import::AbstractAdapter::InstanceMethods end end end activerecord-import-0.10.0/lib/activerecord-import/active_record/adapters/mysql_adapter.rb0000644000004100000410000000032612557352107032150 0ustar www-datawww-datarequire "active_record/connection_adapters/mysql_adapter" require "activerecord-import/adapters/mysql_adapter" class ActiveRecord::ConnectionAdapters::MysqlAdapter include ActiveRecord::Import::MysqlAdapter end ././@LongLink0000000000000000000000000000015400000000000011565 Lustar rootrootactiverecord-import-0.10.0/lib/activerecord-import/active_record/adapters/seamless_database_pool_adapter.rbactiverecord-import-0.10.0/lib/activerecord-import/active_record/adapters/seamless_database_pool_ada0000644000004100000410000000042712557352107034201 0ustar www-datawww-datarequire "seamless_database_pool" require "active_record/connection_adapters/seamless_database_pool_adapter" require "activerecord-import/adapters/mysql_adapter" class ActiveRecord::ConnectionAdapters::SeamlessDatabasePoolAdapter include ActiveRecord::Import::MysqlAdapter end activerecord-import-0.10.0/lib/activerecord-import/import.rb0000644000004100000410000005245112557352107024207 0ustar www-datawww-datarequire "ostruct" module ActiveRecord::Import::ConnectionAdapters ; end module ActiveRecord::Import #:nodoc: class Result < Struct.new(:failed_instances, :num_inserts, :ids) end module ImportSupport #:nodoc: def supports_import? #:nodoc: true end end module OnDuplicateKeyUpdateSupport #:nodoc: def supports_on_duplicate_key_update? #:nodoc: true end end class MissingColumnError < StandardError def initialize(name, index) super "Missing column for value <#{name}> at index #{index}" end end end class ActiveRecord::Associations::CollectionProxy def import(*args, &block) @association.import(*args, &block) end end class ActiveRecord::Associations::CollectionAssociation def import(*args, &block) unless owner.persisted? raise ActiveRecord::RecordNotSaved, "You cannot call import unless the parent is saved" end options = args.last.is_a?(Hash) ? args.pop : {} model_klass = self.reflection.klass symbolized_foreign_key = self.reflection.foreign_key.to_sym symbolized_column_names = model_klass.column_names.map(&:to_sym) owner_primary_key = self.owner.class.primary_key owner_primary_key_value = self.owner.send(owner_primary_key) # assume array of model objects if args.last.is_a?( Array ) and args.last.first.is_a? ActiveRecord::Base if args.length == 2 models = args.last column_names = args.first else models = args.first column_names = symbolized_column_names end if !symbolized_column_names.include?(symbolized_foreign_key) column_names << symbolized_foreign_key end models.each do |m| m.send "#{symbolized_foreign_key}=", owner_primary_key_value end return model_klass.import column_names, models, options # supports empty array elsif args.last.is_a?( Array ) and args.last.empty? return ActiveRecord::Import::Result.new([], 0, []) if args.last.empty? # supports 2-element array and array elsif args.size == 2 and args.first.is_a?( Array ) and args.last.is_a?( Array ) column_names, array_of_attributes = args symbolized_column_names = column_names.map(&:to_s) if !symbolized_column_names.include?(symbolized_foreign_key) column_names << symbolized_foreign_key array_of_attributes.each { |attrs| attrs << owner_primary_key_value } else index = symbolized_column_names.index(symbolized_foreign_key) array_of_attributes.each { |attrs| attrs[index] = owner_primary_key_value } end return model_klass.import column_names, array_of_attributes, options else raise ArgumentError.new( "Invalid arguments!" ) end end end class ActiveRecord::Base class << self # use tz as set in ActiveRecord::Base tproc = lambda do ActiveRecord::Base.default_timezone == :utc ? Time.now.utc : Time.now end AREXT_RAILS_COLUMNS = { :create => { "created_on" => tproc , "created_at" => tproc }, :update => { "updated_on" => tproc , "updated_at" => tproc } } AREXT_RAILS_COLUMN_NAMES = AREXT_RAILS_COLUMNS[:create].keys + AREXT_RAILS_COLUMNS[:update].keys # Returns true if the current database connection adapter # supports import functionality, otherwise returns false. def supports_import?(*args) connection.respond_to?(:supports_import?) && connection.supports_import?(*args) end # Returns true if the current database connection adapter # supports on duplicate key update functionality, otherwise # returns false. def supports_on_duplicate_key_update? connection.respond_to?(:supports_on_duplicate_key_update?) && connection.supports_on_duplicate_key_update? end # returns true if the current database connection adapter # supports setting the primary key of bulk imported models, otherwise # returns false def support_setting_primary_key_of_imported_objects? connection.respond_to?(:support_setting_primary_key_of_imported_objects?) && connection.support_setting_primary_key_of_imported_objects? end # Imports a collection of values to the database. # # This is more efficient than using ActiveRecord::Base#create or # ActiveRecord::Base#save multiple times. This method works well if # you want to create more than one record at a time and do not care # about having ActiveRecord objects returned for each record # inserted. # # This can be used with or without validations. It does not utilize # the ActiveRecord::Callbacks during creation/modification while # performing the import. # # == Usage # Model.import array_of_models # Model.import column_names, array_of_values # Model.import column_names, array_of_values, options # # ==== Model.import array_of_models # # With this form you can call _import_ passing in an array of model # objects that you want updated. # # ==== Model.import column_names, array_of_values # # The first parameter +column_names+ is an array of symbols or # strings which specify the columns that you want to update. # # The second parameter, +array_of_values+, is an array of # arrays. Each subarray is a single set of values for a new # record. The order of values in each subarray should match up to # the order of the +column_names+. # # ==== Model.import column_names, array_of_values, options # # The first two parameters are the same as the above form. The third # parameter, +options+, is a hash. This is optional. Please see # below for what +options+ are available. # # == Options # * +validate+ - true|false, tells import whether or not to use \ # ActiveRecord validations. Validations are enforced by default. # * +on_duplicate_key_update+ - an Array or Hash, tells import to \ # use MySQL's ON DUPLICATE KEY UPDATE ability. See On Duplicate\ # Key Update below. # * +synchronize+ - an array of ActiveRecord instances for the model # that you are currently importing data into. This synchronizes # existing model instances in memory with updates from the import. # * +timestamps+ - true|false, tells import to not add timestamps \ # (if false) even if record timestamps is disabled in ActiveRecord::Base # * +recursive - true|false, tells import to import all autosave association # if the adapter supports setting the primary keys of the newly imported # objects. # # == Examples # class BlogPost < ActiveRecord::Base ; end # # # Example using array of model objects # posts = [ BlogPost.new :author_name=>'Zach Dennis', :title=>'AREXT', # BlogPost.new :author_name=>'Zach Dennis', :title=>'AREXT2', # BlogPost.new :author_name=>'Zach Dennis', :title=>'AREXT3' ] # BlogPost.import posts # # # Example using column_names and array_of_values # columns = [ :author_name, :title ] # values = [ [ 'zdennis', 'test post' ], [ 'jdoe', 'another test post' ] ] # BlogPost.import columns, values # # # Example using column_names, array_of_value and options # columns = [ :author_name, :title ] # values = [ [ 'zdennis', 'test post' ], [ 'jdoe', 'another test post' ] ] # BlogPost.import( columns, values, :validate => false ) # # # Example synchronizing existing instances in memory # post = BlogPost.where(author_name: 'zdennis').first # puts post.author_name # => 'zdennis' # columns = [ :author_name, :title ] # values = [ [ 'yoda', 'test post' ] ] # BlogPost.import posts, :synchronize=>[ post ] # puts post.author_name # => 'yoda' # # # Example synchronizing unsaved/new instances in memory by using a uniqued imported field # posts = [BlogPost.new(:title => "Foo"), BlogPost.new(:title => "Bar")] # BlogPost.import posts, :synchronize => posts, :synchronize_keys => [:title] # puts posts.first.persisted? # => true # # == On Duplicate Key Update (MySQL only) # # The :on_duplicate_key_update option can be either an Array or a Hash. # # ==== Using an Array # # The :on_duplicate_key_update option can be an array of column # names. The column names are the only fields that are updated if # a duplicate record is found. Below is an example: # # BlogPost.import columns, values, :on_duplicate_key_update=>[ :date_modified, :content, :author ] # # ==== Using A Hash # # The :on_duplicate_key_update option can be a hash of column name # to model attribute name mappings. This gives you finer grained # control over what fields are updated with what attributes on your # model. Below is an example: # # BlogPost.import columns, attributes, :on_duplicate_key_update=>{ :title => :title } # # = Returns # This returns an object which responds to +failed_instances+ and +num_inserts+. # * failed_instances - an array of objects that fails validation and were not committed to the database. An empty array if no validation is performed. # * num_inserts - the number of insert statements it took to import the data # * ids - the priamry keys of the imported ids, if the adpater supports it, otherwise and empty array. def import(*args) if args.first.is_a?( Array ) and args.first.first.is_a? ActiveRecord::Base options = {} options.merge!( args.pop ) if args.last.is_a?(Hash) models = args.first import_helper(models, options) else import_helper(*args) end end def import_helper( *args ) options = { :validate=>true, :timestamps=>true, :primary_key=>primary_key } options.merge!( args.pop ) if args.last.is_a? Hash is_validating = options[:validate] is_validating = true unless options[:validate_with_context].nil? # assume array of model objects if args.last.is_a?( Array ) and args.last.first.is_a? ActiveRecord::Base if args.length == 2 models = args.last column_names = args.first else models = args.first column_names = self.column_names.dup end array_of_attributes = models.map do |model| # this next line breaks sqlite.so with a segmentation fault # if model.new_record? || options[:on_duplicate_key_update] column_names.map do |name| model.read_attribute_before_type_cast(name.to_s) end # end end # supports empty array elsif args.last.is_a?( Array ) and args.last.empty? return ActiveRecord::Import::Result.new([], 0, []) if args.last.empty? # supports 2-element array and array elsif args.size == 2 and args.first.is_a?( Array ) and args.last.is_a?( Array ) column_names, array_of_attributes = args else raise ArgumentError.new( "Invalid arguments!" ) end # dup the passed in array so we don't modify it unintentionally array_of_attributes = array_of_attributes.dup # Force the primary key col into the insert if it's not # on the list and we are using a sequence and stuff a nil # value for it into each row so the sequencer will fire later if !column_names.include?(primary_key) && connection.prefetch_primary_key? && sequence_name column_names << primary_key array_of_attributes.each { |a| a << nil } end # record timestamps unless disabled in ActiveRecord::Base if record_timestamps && options.delete( :timestamps ) add_special_rails_stamps column_names, array_of_attributes, options end return_obj = if is_validating import_with_validations( column_names, array_of_attributes, options ) else (num_inserts, ids) = import_without_validations_or_callbacks( column_names, array_of_attributes, options ) ActiveRecord::Import::Result.new([], num_inserts, ids) end if options[:synchronize] sync_keys = options[:synchronize_keys] || [self.primary_key] synchronize( options[:synchronize], sync_keys) end return_obj.num_inserts = 0 if return_obj.num_inserts.nil? # if we have ids, then set the id on the models and mark the models as clean. if support_setting_primary_key_of_imported_objects? set_ids_and_mark_clean(models, return_obj) # if there are auto-save associations on the models we imported that are new, import them as well if options[:recursive] import_associations(models, options) end end return_obj end # TODO import_from_table needs to be implemented. def import_from_table( options ) # :nodoc: end # Imports the passed in +column_names+ and +array_of_attributes+ # given the passed in +options+ Hash with validations. Returns an # object with the methods +failed_instances+ and +num_inserts+. # +failed_instances+ is an array of instances that failed validations. # +num_inserts+ is the number of inserts it took to import the data. See # ActiveRecord::Base.import for more information on # +column_names+, +array_of_attributes+ and +options+. def import_with_validations( column_names, array_of_attributes, options={} ) failed_instances = [] # create instances for each of our column/value sets arr = validations_array_for_column_names_and_attributes( column_names, array_of_attributes ) # keep track of the instance and the position it is currently at. if this fails # validation we'll use the index to remove it from the array_of_attributes arr.each_with_index do |hsh,i| instance = new do |model| hsh.each_pair{ |k,v| model.send("#{k}=", v) } end if not instance.valid?(options[:validate_with_context]) array_of_attributes[ i ] = nil failed_instances << instance end end array_of_attributes.compact! (num_inserts, ids) = if array_of_attributes.empty? || options[:all_or_none] && failed_instances.any? [0,[]] else import_without_validations_or_callbacks( column_names, array_of_attributes, options ) end ActiveRecord::Import::Result.new(failed_instances, num_inserts, ids) end # Imports the passed in +column_names+ and +array_of_attributes+ # given the passed in +options+ Hash. This will return the number # of insert operations it took to create these records without # validations or callbacks. See ActiveRecord::Base.import for more # information on +column_names+, +array_of_attributes_ and # +options+. def import_without_validations_or_callbacks( column_names, array_of_attributes, options={} ) column_names = column_names.map(&:to_sym) scope_columns, scope_values = scope_attributes.to_a.transpose unless scope_columns.blank? scope_columns.zip(scope_values).each do |name, value| next if column_names.include?(name.to_sym) column_names << name.to_sym array_of_attributes.each { |attrs| attrs << value } end end columns = column_names.each_with_index.map do |name, i| column = columns_hash[name.to_s] raise ActiveRecord::Import::MissingColumnError.new(name.to_s, i) if column.nil? column end columns_sql = "(#{column_names.map{|name| connection.quote_column_name(name) }.join(',')})" insert_sql = "INSERT #{options[:ignore] ? 'IGNORE ':''}INTO #{quoted_table_name} #{columns_sql} VALUES " values_sql = values_sql_for_columns_and_attributes(columns, array_of_attributes) ids = [] if not supports_import? number_inserted = 0 values_sql.each do |values| connection.execute(insert_sql + values) number_inserted += 1 end else # generate the sql post_sql_statements = connection.post_sql_statements( quoted_table_name, options ) # perform the inserts (number_inserted,ids) = connection.insert_many( [ insert_sql, post_sql_statements ].flatten, values_sql, "#{self.class.name} Create Many Without Validations Or Callbacks" ) end [number_inserted, ids] end private def set_ids_and_mark_clean(models, import_result) unless models.nil? import_result.ids.each_with_index do |id, index| models[index].id = id.to_i models[index].instance_variable_get(:@changed_attributes).clear # mark the model as saved end end end def import_associations(models, options) # now, for all the dirty associations, collect them into a new set of models, then recurse. # notes: # does not handle associations that reference themselves # assumes that the only associations to be saved are marked with :autosave # should probably take a hash to associations to follow. associated_objects_by_class={} models.each {|model| find_associated_objects_for_import(associated_objects_by_class, model) } associated_objects_by_class.each_pair do |class_name, associations| associations.each_pair do |association_name, associated_records| associated_records.first.class.import(associated_records, options) unless associated_records.empty? end end end # We are eventually going to call Class.import so we build up a hash # of class => objects to import. def find_associated_objects_for_import(associated_objects_by_class, model) associated_objects_by_class[model.class.name]||={} model.class.reflect_on_all_autosave_associations.each do |association_reflection| associated_objects_by_class[model.class.name][association_reflection.name]||=[] association = model.association(association_reflection.name) association.loaded! changed_objects = association.select {|a| a.new_record? || a.changed?} changed_objects.each do |child| child.send("#{association_reflection.foreign_key}=", model.id) end associated_objects_by_class[model.class.name][association_reflection.name].concat changed_objects end associated_objects_by_class end # Returns SQL the VALUES for an INSERT statement given the passed in +columns+ # and +array_of_attributes+. def values_sql_for_columns_and_attributes(columns, array_of_attributes) # :nodoc: # connection gets called a *lot* in this high intensity loop. # Reuse the same one w/in the loop, otherwise it would keep being re-retreived (= lots of time for large imports) connection_memo = connection array_of_attributes.map do |arr| my_values = arr.each_with_index.map do |val,j| column = columns[j] # be sure to query sequence_name *last*, only if cheaper tests fail, because it's costly if val.nil? && column.name == primary_key && !sequence_name.blank? connection_memo.next_value_for_sequence(sequence_name) elsif column if column.respond_to?(:type_cast_from_user) # Rails 4.2 and higher connection_memo.quote(column.type_cast_from_user(val), column) else connection_memo.quote(column.type_cast(val), column) # Rails 3.1, 3.2, and 4.1 end end end "(#{my_values.join(',')})" end end def add_special_rails_stamps( column_names, array_of_attributes, options ) AREXT_RAILS_COLUMNS[:create].each_pair do |key, blk| if self.column_names.include?(key) value = blk.call if index=column_names.index(key) || index=column_names.index(key.to_sym) # replace every instance of the array of attributes with our value array_of_attributes.each{ |arr| arr[index] = value if arr[index].nil? } else column_names << key array_of_attributes.each { |arr| arr << value } end end end AREXT_RAILS_COLUMNS[:update].each_pair do |key, blk| if self.column_names.include?(key) value = blk.call if index=column_names.index(key) || index=column_names.index(key.to_sym) # replace every instance of the array of attributes with our value array_of_attributes.each{ |arr| arr[index] = value } else column_names << key array_of_attributes.each { |arr| arr << value } end if supports_on_duplicate_key_update? if options[:on_duplicate_key_update] options[:on_duplicate_key_update] << key.to_sym if options[:on_duplicate_key_update].is_a?(Array) && !options[:on_duplicate_key_update].include?(key.to_sym) options[:on_duplicate_key_update][key.to_sym] = key.to_sym if options[:on_duplicate_key_update].is_a?(Hash) else options[:on_duplicate_key_update] = [ key.to_sym ] end end end end end # Returns an Array of Hashes for the passed in +column_names+ and +array_of_attributes+. def validations_array_for_column_names_and_attributes( column_names, array_of_attributes ) # :nodoc: array_of_attributes.map do |attributes| Hash[attributes.each_with_index.map {|attr, c| [column_names[c], attr] }] end end end end activerecord-import-0.10.0/lib/activerecord-import/value_sets_parser.rb0000644000004100000410000000247512557352107026424 0ustar www-datawww-datamodule ActiveRecord::Import class ValueSetsBytesParser attr_reader :reserved_bytes, :max_bytes, :values def self.parse(values, options) new(values, options).parse end def initialize(values, options) @values = values @reserved_bytes = options[:reserved_bytes] @max_bytes = options[:max_bytes] end def parse value_sets = [] arr, current_arr_values_size, current_size = [], 0, 0 values.each_with_index do |val,i| comma_bytes = arr.size bytes_thus_far = reserved_bytes + current_size + val.bytesize + comma_bytes if bytes_thus_far <= max_bytes current_size += val.bytesize arr << val else value_sets << arr arr = [ val ] current_size = val.bytesize end # if we're on the last iteration push whatever we have in arr to value_sets value_sets << arr if i == (values.size-1) end [ *value_sets ] end end class ValueSetsRecordsParser attr_reader :max_records, :values def self.parse(values, options) new(values, options).parse end def initialize(values, options) @values = values @max_records = options[:max_records] end def parse @values.in_groups_of(max_records, with_fill=false) end end end activerecord-import-0.10.0/lib/activerecord-import/version.rb0000644000004100000410000000010512557352107024347 0ustar www-datawww-datamodule ActiveRecord module Import VERSION = "0.10.0" end end activerecord-import-0.10.0/lib/activerecord-import/base.rb0000644000004100000410000000210112557352107023572 0ustar www-datawww-datarequire "pathname" require "active_record" require "active_record/version" module ActiveRecord::Import AdapterPath = "activerecord-import/active_record/adapters" def self.base_adapter(adapter) case adapter when 'mysqlspatial' then 'mysql' when 'mysql2spatial' then 'mysql2' when 'spatialite' then 'sqlite3' when 'postgis' then 'postgresql' else adapter end end # Loads the import functionality for a specific database adapter def self.require_adapter(adapter) require File.join(AdapterPath,"/abstract_adapter") begin require File.join(AdapterPath,"/#{base_adapter(adapter)}_adapter") rescue LoadError # fallback end end # Loads the import functionality for the passed in ActiveRecord connection def self.load_from_connection_pool(connection_pool) require_adapter connection_pool.spec.config[:adapter] end end require 'activerecord-import/import' require 'activerecord-import/active_record/adapters/abstract_adapter' require 'activerecord-import/synchronize' require 'activerecord-import/value_sets_parser' activerecord-import-0.10.0/lib/activerecord-import/em_mysql2.rb0000644000004100000410000000046212557352107024600 0ustar www-datawww-datawarn <<-MSG [DEPRECATION] loading activerecord-import via 'require "activerecord-import/"' is deprecated. Update to autorequire using 'require "activerecord-import"'. See http://github.com/zdennis/activerecord-import/wiki/Requiring for more information MSG require "activerecord-import" activerecord-import-0.10.0/lib/activerecord-import.rb0000644000004100000410000000121512557352107022665 0ustar www-datawww-dataclass ActiveRecord::Base class << self def establish_connection_with_activerecord_import(*args) establish_connection_without_activerecord_import(*args) ActiveSupport.run_load_hooks(:active_record_connection_established, connection_pool) end alias_method_chain :establish_connection, :activerecord_import end end ActiveSupport.on_load(:active_record_connection_established) do |connection_pool| if !ActiveRecord.const_defined?(:Import, false) || !ActiveRecord::Import.respond_to?(:load_from_connection_pool) require "activerecord-import/base" end ActiveRecord::Import.load_from_connection_pool connection_pool end activerecord-import-0.10.0/README.markdown0000644000004100000410000001016512557352107020315 0ustar www-datawww-data# activerecord-import activerecord-import is a library for bulk inserting data using ActiveRecord. One of its major features is following activerecord associations and generating the minimal number of SQL insert statements required, avoiding the N+1 insert problem. An example probably explains it best. Say you had a schema like this: Publishers have Books Books have Reviews and you wanted to bulk insert 100 new publishers with 10K books and 3 reviews per book. This library will follow the associations down and generate only 3 SQL insert statements - one for the publishers, one for the books, and one for the reviews. In contrast, the standard ActiveRecord save would generate 100 insert statements for the publishers, then it would visit each publisher and save all the books: 100 * 10,000 = 1,000,000 SQL insert statements and then the reviews: 100 * 10,000 * 3 = 3M SQL insert statements, That would be about 4M SQL insert statements vs 3, which results in vastly improved performance. In our case, it converted an 18 hour batch process to <2 hrs. ### Rails 4.0 Use activerecord-import 0.4.0 or higher. ### Rails 3.1.x up to, but not including 4.0 Use the latest in the activerecord-import 0.3.x series. ### Rails 3.0.x up to, but not including 3.1 Use activerecord-import 0.2.11. As of activerecord-import 0.3.0 we are relying on functionality that was introduced in Rails 3.1. Since Rails 3.0.x is no longer a supported version of Rails we have decided to drop support as well. ### For More Information For more information on activerecord-import please see its wiki: https://github.com/zdennis/activerecord-import/wiki ## Additional Adapters Additional adapters can be provided by gems external to activerecord-import by providing an adapter that matches the naming convention setup by activerecord-import (and subsequently activerecord) for dynamically loading adapters. This involves also providing a folder on the load path that follows the activerecord-import naming convention to allow activerecord-import to dynamically load the file. When `ActiveRecord::Import.require_adapter("fake_name")` is called the require will be: ```ruby require 'activerecord-import/active_record/adapters/fake_name_adapter' ``` This allows an external gem to dyanmically add an adapter without the need to add any file/code to the core activerecord-import gem. ### Load Path Setup To understand how rubygems loads code you can reference the following: http://guides.rubygems.org/patterns/#loading_code And an example of how active_record dynamically load adapters: https://github.com/rails/rails/blob/master/activerecord/lib/active_record/connection_adapters/connection_specification.rb In summary, when a gem is loaded rubygems adds the `lib` folder of the gem to the global load path `$LOAD_PATH` so that all `require` lookups will not propegate through all of the folders on the load path. When a `require` is issued each folder on the `$LOAD_PATH` is checked for the file and/or folder referenced. This allows a gem (like activerecord-import) to define push the activerecord-import folder (or namespace) on the `$LOAD_PATH` and any adapters provided by activerecord-import will be found by rubygems when the require is issued. If `fake_name` adapter is needed by a gem (potentially called `activerecord-import-fake_name`) then the folder structure should look as follows: ```bash activerecord-import-fake_name/ |-- activerecord-import-fake_name.gemspec |-- lib | |-- activerecord-import-fake_name | | |-- version.rb | |-- activerecord-import | | |-- active_record | | | |-- adapters | | | |-- fake_name_adapter.rb |--activerecord-import-fake_name.rb ``` When rubygems pushes the `lib` folder onto the load path a `require` will now find `activerecord-import/active_record/adapters/fake_name_adapter` as it runs through the lookup process for a ruby file under that path in `$LOAD_PATH` # License This is licensed under the ruby license. # Author Zach Dennis (zach.dennis@gmail.com) # Contributors * Blythe Dunham * Gabe da Silveira * Henry Work * James Herdman * Marcus Crafter * Thibaud Guillaume-Gentil * Mark Van Holstyn * Victor Costan activerecord-import-0.10.0/gemfiles/0000755000004100000410000000000012557352107017404 5ustar www-datawww-dataactiverecord-import-0.10.0/gemfiles/4.0.gemfile0000644000004100000410000000013312557352107021234 0ustar www-datawww-dataplatforms :ruby do gem 'mysql', '~> 2.9' gem 'activerecord', '~> 4.0.0.rc2' end activerecord-import-0.10.0/gemfiles/4.2.gemfile0000644000004100000410000000012512557352107021237 0ustar www-datawww-dataplatforms :ruby do gem 'mysql', '~> 2.9' gem 'activerecord', '~> 4.2' end activerecord-import-0.10.0/gemfiles/3.1.gemfile0000644000004100000410000000013112557352107021232 0ustar www-datawww-dataplatforms :ruby do gem 'mysql', '>= 2.8.1' gem 'activerecord', '~> 3.1.0' end activerecord-import-0.10.0/gemfiles/4.1.gemfile0000644000004100000410000000012512557352107021236 0ustar www-datawww-dataplatforms :ruby do gem 'mysql', '~> 2.9' gem 'activerecord', '~> 4.1' end activerecord-import-0.10.0/gemfiles/3.2.gemfile0000644000004100000410000000013112557352107021233 0ustar www-datawww-dataplatforms :ruby do gem 'mysql', '>= 2.8.1' gem 'activerecord', '~> 3.2.0' end activerecord-import-0.10.0/metadata.yml0000644000004100000410000001456012557352107020122 0ustar www-datawww-data--- !ruby/object:Gem::Specification name: activerecord-import version: !ruby/object:Gem::Version version: 0.10.0 platform: ruby authors: - Zach Dennis autorequire: bindir: bin cert_chain: [] date: 2015-07-08 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: activerecord requirement: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '3.0' type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '3.0' - !ruby/object:Gem::Dependency name: rake requirement: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' description: Extraction of the ActiveRecord::Base#import functionality from ar-extensions for Rails 3 and beyond email: - zach.dennis@gmail.com executables: [] extensions: [] extra_rdoc_files: [] files: - ".gitignore" - ".travis.yml" - Brewfile - Gemfile - LICENSE - README.markdown - Rakefile - activerecord-import.gemspec - benchmarks/README - benchmarks/benchmark.rb - benchmarks/boot.rb - benchmarks/lib/base.rb - benchmarks/lib/cli_parser.rb - benchmarks/lib/float.rb - benchmarks/lib/mysql_benchmark.rb - benchmarks/lib/output_to_csv.rb - benchmarks/lib/output_to_html.rb - benchmarks/models/test_innodb.rb - benchmarks/models/test_memory.rb - benchmarks/models/test_myisam.rb - benchmarks/schema/mysql_schema.rb - gemfiles/3.1.gemfile - gemfiles/3.2.gemfile - gemfiles/4.0.gemfile - gemfiles/4.1.gemfile - gemfiles/4.2.gemfile - lib/activerecord-import.rb - lib/activerecord-import/active_record/adapters/abstract_adapter.rb - lib/activerecord-import/active_record/adapters/em_mysql2_adapter.rb - lib/activerecord-import/active_record/adapters/jdbcmysql_adapter.rb - lib/activerecord-import/active_record/adapters/jdbcpostgresql_adapter.rb - lib/activerecord-import/active_record/adapters/mysql2_adapter.rb - lib/activerecord-import/active_record/adapters/mysql_adapter.rb - lib/activerecord-import/active_record/adapters/postgresql_adapter.rb - lib/activerecord-import/active_record/adapters/seamless_database_pool_adapter.rb - lib/activerecord-import/active_record/adapters/sqlite3_adapter.rb - lib/activerecord-import/adapters/abstract_adapter.rb - lib/activerecord-import/adapters/em_mysql2_adapter.rb - lib/activerecord-import/adapters/mysql2_adapter.rb - lib/activerecord-import/adapters/mysql_adapter.rb - lib/activerecord-import/adapters/postgresql_adapter.rb - lib/activerecord-import/adapters/sqlite3_adapter.rb - lib/activerecord-import/base.rb - lib/activerecord-import/em_mysql2.rb - lib/activerecord-import/import.rb - lib/activerecord-import/mysql.rb - lib/activerecord-import/mysql2.rb - lib/activerecord-import/postgresql.rb - lib/activerecord-import/sqlite3.rb - lib/activerecord-import/synchronize.rb - lib/activerecord-import/value_sets_parser.rb - lib/activerecord-import/version.rb - test/adapters/em_mysql2.rb - test/adapters/jdbcmysql.rb - test/adapters/jdbcpostgresql.rb - test/adapters/mysql.rb - test/adapters/mysql2.rb - test/adapters/mysql2spatial.rb - test/adapters/mysqlspatial.rb - test/adapters/postgis.rb - test/adapters/postgresql.rb - test/adapters/seamless_database_pool.rb - test/adapters/spatialite.rb - test/adapters/sqlite3.rb - test/database.yml.sample - test/em_mysql2/import_test.rb - test/import_test.rb - test/jdbcmysql/import_test.rb - test/jdbcpostgresql/import_test.rb - test/models/book.rb - test/models/chapter.rb - test/models/end_note.rb - test/models/group.rb - test/models/topic.rb - test/models/widget.rb - test/mysql/import_test.rb - test/mysql2/import_test.rb - test/mysqlspatial/import_test.rb - test/mysqlspatial2/import_test.rb - test/postgis/import_test.rb - test/postgresql/import_test.rb - test/schema/generic_schema.rb - test/schema/mysql_schema.rb - test/schema/version.rb - test/sqlite3/import_test.rb - test/support/active_support/test_case_extensions.rb - test/support/factories.rb - test/support/generate.rb - test/support/mysql/assertions.rb - test/support/mysql/import_examples.rb - test/support/postgresql/import_examples.rb - test/synchronize_test.rb - test/test_helper.rb - test/travis/build.sh - test/travis/database.yml - test/value_sets_bytes_parser_test.rb - test/value_sets_records_parser_test.rb homepage: http://github.com/zdennis/activerecord-import licenses: - Ruby metadata: {} post_install_message: rdoc_options: [] require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: 1.9.2 required_rubygems_version: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' requirements: [] rubyforge_project: rubygems_version: 2.4.3 signing_key: specification_version: 4 summary: Bulk-loading extension for ActiveRecord test_files: - test/adapters/em_mysql2.rb - test/adapters/jdbcmysql.rb - test/adapters/jdbcpostgresql.rb - test/adapters/mysql.rb - test/adapters/mysql2.rb - test/adapters/mysql2spatial.rb - test/adapters/mysqlspatial.rb - test/adapters/postgis.rb - test/adapters/postgresql.rb - test/adapters/seamless_database_pool.rb - test/adapters/spatialite.rb - test/adapters/sqlite3.rb - test/database.yml.sample - test/em_mysql2/import_test.rb - test/import_test.rb - test/jdbcmysql/import_test.rb - test/jdbcpostgresql/import_test.rb - test/models/book.rb - test/models/chapter.rb - test/models/end_note.rb - test/models/group.rb - test/models/topic.rb - test/models/widget.rb - test/mysql/import_test.rb - test/mysql2/import_test.rb - test/mysqlspatial/import_test.rb - test/mysqlspatial2/import_test.rb - test/postgis/import_test.rb - test/postgresql/import_test.rb - test/schema/generic_schema.rb - test/schema/mysql_schema.rb - test/schema/version.rb - test/sqlite3/import_test.rb - test/support/active_support/test_case_extensions.rb - test/support/factories.rb - test/support/generate.rb - test/support/mysql/assertions.rb - test/support/mysql/import_examples.rb - test/support/postgresql/import_examples.rb - test/synchronize_test.rb - test/test_helper.rb - test/travis/build.sh - test/travis/database.yml - test/value_sets_bytes_parser_test.rb - test/value_sets_records_parser_test.rb has_rdoc: activerecord-import-0.10.0/test/0000755000004100000410000000000012557352107016570 5ustar www-datawww-dataactiverecord-import-0.10.0/test/import_test.rb0000644000004100000410000003666112557352107021502 0ustar www-datawww-datarequire File.expand_path('../test_helper', __FILE__) describe "#import" do it "should return the number of inserts performed" do # see ActiveRecord::ConnectionAdapters::AbstractAdapter test for more specifics assert_difference "Topic.count", +10 do result = Topic.import Build(3, :topics) assert result.num_inserts > 0 result = Topic.import Build(7, :topics) assert result.num_inserts > 0 end end it "should not produce an error when importing empty arrays" do assert_nothing_raised do Topic.import [] Topic.import %w(title author_name), [] end end describe "with non-default ActiveRecord models" do context "that have a non-standard primary key (that is no sequence)" do it "should import models successfully" do assert_difference "Widget.count", +3 do Widget.import Build(3, :widgets) end end end end context "with :validation option" do let(:columns) { %w(title author_name) } let(:valid_values) { [[ "LDAP", "Jerry Carter"], ["Rails Recipes", "Chad Fowler"]] } let(:valid_values_with_context) { [[ 1111, "Jerry Carter"], [2222, "Chad Fowler"]] } let(:invalid_values) { [[ "The RSpec Book", ""], ["Agile+UX", ""]] } context "with validation checks turned off" do it "should import valid data" do assert_difference "Topic.count", +2 do result = Topic.import columns, valid_values, :validate => false end end it "should import invalid data" do assert_difference "Topic.count", +2 do result = Topic.import columns, invalid_values, :validate => false end end it 'should raise a specific error if a column does not exist' do assert_raises ActiveRecord::Import::MissingColumnError do Topic.import ['foo'], [['bar']], :validate => false end end end context "with validation checks turned on" do it "should import valid data" do assert_difference "Topic.count", +2 do result = Topic.import columns, valid_values, :validate => true end end it "should import valid data with on option" do assert_difference "Topic.count", +2 do result = Topic.import columns, valid_values_with_context, :validate_with_context => :context_test end end it "should not import invalid data" do assert_no_difference "Topic.count" do result = Topic.import columns, invalid_values, :validate => true end end it "should import invalid data with on option" do assert_no_difference "Topic.count" do result = Topic.import columns, valid_values, :validate_with_context => :context_test end end it "should report the failed instances" do results = Topic.import columns, invalid_values, :validate => true assert_equal invalid_values.size, results.failed_instances.size results.failed_instances.each{ |e| assert_kind_of Topic, e } end it "should import valid data when mixed with invalid data" do assert_difference "Topic.count", +2 do result = Topic.import columns, valid_values + invalid_values, :validate => true end assert_equal 0, Topic.where(title: invalid_values.map(&:first)).count end end end context "with :all_or_none option" do let(:columns) { %w(title author_name) } let(:valid_values) { [[ "LDAP", "Jerry Carter"], ["Rails Recipes", "Chad Fowler"]] } let(:invalid_values) { [[ "The RSpec Book", ""], ["Agile+UX", ""]] } let(:mixed_values) { valid_values + invalid_values } context "with validation checks turned on" do it "should import valid data" do assert_difference "Topic.count", +2 do result = Topic.import columns, valid_values, :all_or_none => true end end it "should not import invalid data" do assert_no_difference "Topic.count" do result = Topic.import columns, invalid_values, :all_or_none => true end end it "should not import valid data when mixed with invalid data" do assert_no_difference "Topic.count" do result = Topic.import columns, mixed_values, :all_or_none => true end end it "should report the failed instances" do results = Topic.import columns, mixed_values, :all_or_none => true assert_equal invalid_values.size, results.failed_instances.size results.failed_instances.each { |e| assert_kind_of Topic, e } end it "should report the zero inserts" do results = Topic.import columns, mixed_values, :all_or_none => true assert_equal 0, results.num_inserts end end end context "with :synchronize option" do context "synchronizing on new records" do let(:new_topics) { Build(3, :topics) } it "doesn't reload any data (doesn't work)" do Topic.import new_topics, :synchronize => new_topics assert new_topics.all?(&:new_record?), "No record should have been reloaded" end end context "synchronizing on new records with explicit conditions" do let(:new_topics) { Build(3, :topics) } it "reloads data for existing in-memory instances" do Topic.import(new_topics, :synchronize => new_topics, :synchronize_keys => [:title] ) assert new_topics.all?(&:persisted?), "Records should have been reloaded" end end context "synchronizing on destroyed records with explicit conditions" do let(:new_topics) { Generate(3, :topics) } it "reloads data for existing in-memory instances" do new_topics.each &:destroy Topic.import(new_topics, :synchronize => new_topics, :synchronize_keys => [:title] ) assert new_topics.all?(&:persisted?), "Records should have been reloaded" end end end context "with an array of unsaved model instances" do let(:topic) { Build(:topic, :title => "The RSpec Book", :author_name => "David Chelimsky")} let(:topics) { Build(9, :topics) } let(:invalid_topics){ Build(7, :invalid_topics)} it "should import records based on those model's attributes" do assert_difference "Topic.count", +9 do result = Topic.import topics end Topic.import [topic] assert Topic.where(title: "The RSpec Book", author_name: "David Chelimsky").first end it "should not overwrite existing records" do topic = Generate(:topic, :title => "foobar") assert_no_difference "Topic.count" do begin Topic.transaction do topic.title = "baz" Topic.import [topic] end rescue Exception # PostgreSQL raises PgError due to key constraints # I don't know why ActiveRecord doesn't catch these. *sigh* end end assert_equal "foobar", topic.reload.title end context "with validation checks turned on" do it "should import valid models" do assert_difference "Topic.count", +9 do result = Topic.import topics, :validate => true end end it "should not import invalid models" do assert_no_difference "Topic.count" do result = Topic.import invalid_topics, :validate => true end end end context "with validation checks turned off" do it "should import invalid models" do assert_difference "Topic.count", +7 do result = Topic.import invalid_topics, :validate => false end end end end context "with an array of columns and an array of unsaved model instances" do let(:topics) { Build(2, :topics) } it "should import records populating the supplied columns with the corresponding model instance attributes" do assert_difference "Topic.count", +2 do result = Topic.import [:author_name, :title], topics end # imported topics should be findable by their imported attributes assert Topic.where(author_name: topics.first.author_name).first assert Topic.where(author_name: topics.last.author_name).first end it "should not populate fields for columns not imported" do topics.first.author_email_address = "zach.dennis@gmail.com" assert_difference "Topic.count", +2 do result = Topic.import [:author_name, :title], topics end assert !Topic.where(author_email_address: "zach.dennis@gmail.com").first end end context "with an array of columns and an array of values" do it "should import ids when specified" do Topic.import [:id, :author_name, :title], [[99, "Bob Jones", "Topic 99"]] assert_equal 99, Topic.last.id end end context "ActiveRecord timestamps" do context "when the timestamps columns are present" do setup do @existing_book = Book.create(title: "Fell", author_name: "Curry", publisher: "Bayer", created_at: 2.years.ago.utc, created_on: 2.years.ago.utc) ActiveRecord::Base.default_timezone = :utc Timecop.freeze Chronic.parse("5 minutes ago") do assert_difference "Book.count", +2 do result = Book.import ["title", "author_name", "publisher", "created_at", "created_on"], [["LDAP", "Big Bird", "Del Rey", nil, nil], [@existing_book.title, @existing_book.author_name, @existing_book.publisher, @existing_book.created_at, @existing_book.created_on]] end end @new_book, @existing_book = Book.last 2 end it "should set the created_at column for new records" do assert_equal 5.minutes.ago.utc.strftime("%H:%M"), @new_book.created_at.strftime("%H:%M") end it "should set the created_on column for new records" do assert_equal 5.minutes.ago.utc.strftime("%H:%M"), @new_book.created_on.strftime("%H:%M") end it "should not set the created_at column for existing records" do assert_equal 2.years.ago.utc.strftime("%Y:%d"), @existing_book.created_at.strftime("%Y:%d") end it "should not set the created_on column for existing records" do assert_equal 2.years.ago.utc.strftime("%Y:%d"), @existing_book.created_on.strftime("%Y:%d") end it "should set the updated_at column for new records" do assert_equal 5.minutes.ago.utc.strftime("%H:%M"), @new_book.updated_at.strftime("%H:%M") end it "should set the updated_on column for new records" do assert_equal 5.minutes.ago.utc.strftime("%H:%M"), @new_book.updated_on.strftime("%H:%M") end end context "when a custom time zone is set" do let(:time){ Chronic.parse("5 minutes ago") } setup do Timecop.freeze(time) do assert_difference "Book.count", +1 do result = Book.import [:title, :author_name, :publisher], [["LDAP", "Big Bird", "Del Rey"]] end end @book = Book.last end it "should set the created_at and created_on timestamps for new records" do assert_in_delta time.to_i, @book.created_at.to_i, 1.second assert_in_delta time.to_i, @book.created_on.to_i, 1.second end it "should set the updated_at and updated_on timestamps for new records" do assert_in_delta time.to_i, @book.updated_at.to_i, 1.second assert_in_delta time.to_i, @book.updated_on.to_i, 1.second end end end context "importing with database reserved words" do let(:group) { Build(:group, :order => "superx") } it "should import just fine" do assert_difference "Group.count", +1 do result = Group.import [group] end assert_equal "superx", Group.first.order end end context "importing a datetime field" do it "should import a date with YYYY/MM/DD format just fine" do Topic.import [:author_name, :title, :last_read], [["Bob Jones", "Topic 2", "2010/05/14"]] assert_equal "2010/05/14".to_date, Topic.last.last_read.to_date end end context "importing through an association scope" do [ true, false ].each do |b| context "when validation is " + (b ? "enabled" : "disabled") do it "should automatically set the foreign key column" do books = [[ "David Chelimsky", "The RSpec Book" ], [ "Chad Fowler", "Rails Recipes" ]] topic = FactoryGirl.create :topic topic.books.import [ :author_name, :title ], books, :validate => b assert_equal 2, topic.books.count assert topic.books.all? { |b| b.topic_id == topic.id } end end end it "works importing models" do topic = FactoryGirl.create :topic books = [ Book.new(:author_name => "Author #1", :title => "Book #1"), Book.new(:author_name => "Author #2", :title => "Book #2"), ] topic.books.import books assert_equal 2, topic.books.count assert topic.books.detect { |b| b.title == "Book #1" && b.author_name == "Author #1" } assert topic.books.detect { |b| b.title == "Book #2" && b.author_name == "Author #2" } end it "works importing array of columns and values" do topic = FactoryGirl.create :topic books = [ Book.new(:author_name => "Foo", :title => "Baz"), Book.new(:author_name => "Foo2", :title => "Baz2"), ] topic.books.import [:author_name, :title], [["Author #1", "Book #1"], ["Author #2", "Book #2"]] assert_equal 2, topic.books.count assert topic.books.detect { |b| b.title == "Book #1" && b.author_name == "Author #1" } assert topic.books.detect { |b| b.title == "Book #2" && b.author_name == "Author #2" } end end context 'When importing models with Enum fields' do it 'should be able to import enum fields' do Book.delete_all if Book.count > 0 books = [ Book.new(:author_name => "Foo", :title => "Baz", status: 0), Book.new(:author_name => "Foo2", :title => "Baz2", status: 1), ] Book.import books assert_equal 2, Book.count assert_equal 0, Book.first.read_attribute('status') assert_equal 1, Book.last.read_attribute('status') end if ENV['AR_VERSION'].to_i > 4.1 it 'should be able to import enum fields by name' do Book.delete_all if Book.count > 0 books = [ Book.new(:author_name => "Foo", :title => "Baz", status: :draft), Book.new(:author_name => "Foo2", :title => "Baz2", status: :published), ] Book.import books assert_equal 2, Book.count assert_equal 0, Book.first.read_attribute('status') assert_equal 1, Book.last.read_attribute('status') end end end describe "importing when model has default_scope" do it "doesn't import the default scope values" do assert_difference "Widget.unscoped.count", +2 do Widget.import [:w_id], [[1], [2]] end default_scope_value = Widget.scope_attributes[:active] assert_not_equal default_scope_value, Widget.unscoped.find_by_w_id(1) assert_not_equal default_scope_value, Widget.unscoped.find_by_w_id(2) end it "imports columns that are a part of the default scope using the value specified" do assert_difference "Widget.unscoped.count", +2 do Widget.import [:w_id, :active], [[1, true], [2, false]] end assert_not_equal true, Widget.unscoped.find_by_w_id(1) assert_not_equal false, Widget.unscoped.find_by_w_id(2) end end describe "importing serialized fields" do it "imports values for serialized fields" do assert_difference "Widget.unscoped.count", +1 do Widget.import [:w_id, :data], [[1, {:a => :b}]] end assert_equal({:a => :b}, Widget.find_by_w_id(1).data) end end end activerecord-import-0.10.0/test/em_mysql2/0000755000004100000410000000000012557352107020500 5ustar www-datawww-dataactiverecord-import-0.10.0/test/em_mysql2/import_test.rb0000644000004100000410000000043212557352107023375 0ustar www-datawww-datarequire File.expand_path(File.dirname(__FILE__) + '/../test_helper') require File.expand_path(File.dirname(__FILE__) + '/../support/mysql/assertions') require File.expand_path(File.dirname(__FILE__) + '/../support/mysql/import_examples') should_support_mysql_import_functionality activerecord-import-0.10.0/test/adapters/0000755000004100000410000000000012557352107020373 5ustar www-datawww-dataactiverecord-import-0.10.0/test/adapters/mysql2.rb0000644000004100000410000000003112557352107022141 0ustar www-datawww-dataENV["ARE_DB"] = "mysql2" activerecord-import-0.10.0/test/adapters/postgis.rb0000644000004100000410000000003112557352107022402 0ustar www-datawww-dataENV["ARE_DB"] = "postgis"activerecord-import-0.10.0/test/adapters/mysql.rb0000644000004100000410000000002712557352107022064 0ustar www-datawww-dataENV["ARE_DB"] = "mysql"activerecord-import-0.10.0/test/adapters/postgresql.rb0000644000004100000410000000003412557352107023120 0ustar www-datawww-dataENV["ARE_DB"] = "postgresql"activerecord-import-0.10.0/test/adapters/sqlite3.rb0000644000004100000410000000003112557352107022276 0ustar www-datawww-dataENV["ARE_DB"] = "sqlite3"activerecord-import-0.10.0/test/adapters/mysql2spatial.rb0000644000004100000410000000003712557352107023525 0ustar www-datawww-dataENV["ARE_DB"] = "mysql2spatial"activerecord-import-0.10.0/test/adapters/mysqlspatial.rb0000644000004100000410000000003612557352107023442 0ustar www-datawww-dataENV["ARE_DB"] = "mysqlspatial"activerecord-import-0.10.0/test/adapters/spatialite.rb0000644000004100000410000000003412557352107023054 0ustar www-datawww-dataENV["ARE_DB"] = "spatialite"activerecord-import-0.10.0/test/adapters/jdbcpostgresql.rb0000644000004100000410000000004112557352107023741 0ustar www-datawww-dataENV["ARE_DB"] = "jdbcpostgresql" activerecord-import-0.10.0/test/adapters/jdbcmysql.rb0000644000004100000410000000003412557352107022705 0ustar www-datawww-dataENV["ARE_DB"] = "jdbcmysql" activerecord-import-0.10.0/test/adapters/seamless_database_pool.rb0000644000004100000410000000005112557352107025405 0ustar www-datawww-dataENV["ARE_DB"] = "seamless_database_pool" activerecord-import-0.10.0/test/adapters/em_mysql2.rb0000644000004100000410000000003412557352107022625 0ustar www-datawww-dataENV["ARE_DB"] = "em_mysql2" activerecord-import-0.10.0/test/schema/0000755000004100000410000000000012557352107020030 5ustar www-datawww-dataactiverecord-import-0.10.0/test/schema/generic_schema.rb0000644000004100000410000000674112557352107023321 0ustar www-datawww-dataActiveRecord::Schema.define do create_table :schema_info, :force=>true do |t| t.column :version, :integer, :unique=>true end SchemaInfo.create :version=>SchemaInfo::VERSION create_table :group, :force => true do |t| t.column :order, :string t.timestamps end create_table :topics, :force=>true do |t| t.column :title, :string, :null => false t.column :author_name, :string t.column :author_email_address, :string t.column :written_on, :datetime t.column :bonus_time, :time t.column :last_read, :datetime t.column :content, :text t.column :approved, :boolean, :default=>'1' t.column :replies_count, :integer t.column :parent_id, :integer t.column :type, :string t.column :created_at, :datetime t.column :created_on, :datetime t.column :updated_at, :datetime t.column :updated_on, :datetime end create_table :projects, :force=>true do |t| t.column :name, :string t.column :type, :string end create_table :developers, :force=>true do |t| t.column :name, :string t.column :salary, :integer, :default=>'70000' t.column :created_at, :datetime t.column :team_id, :integer t.column :updated_at, :datetime end create_table :addresses, :force=>true do |t| t.column :address, :string t.column :city, :string t.column :state, :string t.column :zip, :string t.column :developer_id, :integer end create_table :teams, :force=>true do |t| t.column :name, :string end create_table :books, :force=>true do |t| t.column :title, :string, :null=>false t.column :publisher, :string, :null=>false, :default => 'Default Publisher' t.column :author_name, :string, :null=>false t.column :created_at, :datetime t.column :created_on, :datetime t.column :updated_at, :datetime t.column :updated_on, :datetime t.column :publish_date, :date t.column :topic_id, :integer t.column :for_sale, :boolean, :default => true t.column :status, :integer end create_table :chapters, :force => true do |t| t.column :title, :string t.column :book_id, :integer, :null => false t.column :created_at, :datetime t.column :updated_at, :datetime end create_table :end_notes, :force => true do |t| t.column :note, :string t.column :book_id, :integer, :null => false t.column :created_at, :datetime t.column :updated_at, :datetime end create_table :languages, :force=>true do |t| t.column :name, :string t.column :developer_id, :integer end create_table :shopping_carts, :force=>true do |t| t.column :name, :string, :null => true t.column :created_at, :datetime t.column :updated_at, :datetime end create_table :cart_items, :force => true do |t| t.column :shopping_cart_id, :string, :null => false t.column :book_id, :string, :null => false t.column :copies, :integer, :default => 1 t.column :created_at, :datetime t.column :updated_at, :datetime end add_index :cart_items, [:shopping_cart_id, :book_id], :unique => true, :name => 'uk_shopping_cart_books' create_table :animals, :force => true do |t| t.column :name, :string, :null => false t.column :size, :string, :default => nil t.column :created_at, :datetime t.column :updated_at, :datetime end add_index :animals, [:name], :unique => true, :name => 'uk_animals' create_table :widgets, :id => false, :force => true do |t| t.integer :w_id t.boolean :active, :default => false t.text :data end end activerecord-import-0.10.0/test/schema/version.rb0000644000004100000410000000042112557352107022037 0ustar www-datawww-dataclass SchemaInfo < ActiveRecord::Base if respond_to?(:table_name=) self.table_name = 'schema_info' else # this is becoming deprecated in ActiveRecord but not all adapters supported it # at this time set_table_name 'schema_info' end VERSION = 12 end activerecord-import-0.10.0/test/schema/mysql_schema.rb0000644000004100000410000000122412557352107023041 0ustar www-datawww-dataActiveRecord::Schema.define do create_table :books, :options=>'ENGINE=MyISAM', :force=>true do |t| t.column :title, :string, :null=>false t.column :publisher, :string, :null=>false, :default => 'Default Publisher' t.column :author_name, :string, :null=>false t.column :created_at, :datetime t.column :created_on, :datetime t.column :updated_at, :datetime t.column :updated_on, :datetime t.column :publish_date, :date t.column :topic_id, :integer t.column :for_sale, :boolean, :default => true t.column :status, :integer end execute "ALTER TABLE books ADD FULLTEXT( `title`, `publisher`, `author_name` )" end activerecord-import-0.10.0/test/synchronize_test.rb0000644000004100000410000000302612557352107022530 0ustar www-datawww-datarequire File.expand_path('../test_helper', __FILE__) describe ".synchronize" do let(:topics){ Generate(3, :topics) } let(:titles){ %w(one two three) } setup do # update records outside of ActiveRecord knowing about it Topic.connection.execute( "UPDATE #{Topic.table_name} SET title='#{titles[0]}_haha' WHERE id=#{topics[0].id}", "Updating record 1 without ActiveRecord" ) Topic.connection.execute( "UPDATE #{Topic.table_name} SET title='#{titles[1]}_haha' WHERE id=#{topics[1].id}", "Updating record 2 without ActiveRecord" ) Topic.connection.execute( "UPDATE #{Topic.table_name} SET title='#{titles[2]}_haha' WHERE id=#{topics[2].id}", "Updating record 3 without ActiveRecord" ) end it "reloads data for the specified records" do Topic.synchronize topics actual_titles = topics.map(&:title) assert_equal "#{titles[0]}_haha", actual_titles[0], "the first record was not correctly updated" assert_equal "#{titles[1]}_haha", actual_titles[1], "the second record was not correctly updated" assert_equal "#{titles[2]}_haha", actual_titles[2], "the third record was not correctly updated" end it "the synchronized records aren't dirty" do # Update the in memory records so they're dirty topics.each { |topic| topic.title = 'dirty title' } Topic.synchronize topics assert_equal false, topics[0].changed?, "the first record was dirty" assert_equal false, topics[1].changed?, "the second record was dirty" assert_equal false, topics[2].changed?, "the third record was dirty" end end activerecord-import-0.10.0/test/jdbcmysql/0000755000004100000410000000000012557352107020560 5ustar www-datawww-dataactiverecord-import-0.10.0/test/jdbcmysql/import_test.rb0000644000004100000410000000043112557352107023454 0ustar www-datawww-datarequire File.expand_path(File.dirname(__FILE__) + '/../test_helper') require File.expand_path(File.dirname(__FILE__) + '/../support/mysql/assertions') require File.expand_path(File.dirname(__FILE__) + '/../support/mysql/import_examples') should_support_mysql_import_functionalityactiverecord-import-0.10.0/test/mysqlspatial2/0000755000004100000410000000000012557352107021375 5ustar www-datawww-dataactiverecord-import-0.10.0/test/mysqlspatial2/import_test.rb0000644000004100000410000000043112557352107024271 0ustar www-datawww-datarequire File.expand_path(File.dirname(__FILE__) + '/../test_helper') require File.expand_path(File.dirname(__FILE__) + '/../support/mysql/assertions') require File.expand_path(File.dirname(__FILE__) + '/../support/mysql/import_examples') should_support_mysql_import_functionalityactiverecord-import-0.10.0/test/value_sets_bytes_parser_test.rb0000644000004100000410000000740112557352107025112 0ustar www-datawww-datarequire File.expand_path(File.dirname(__FILE__) + '/test_helper') require 'activerecord-import/value_sets_parser' describe ActiveRecord::Import::ValueSetsBytesParser do context "#parse - computing insert value sets" do let(:parser){ ActiveRecord::Import::ValueSetsBytesParser } let(:base_sql){ "INSERT INTO atable (a,b,c)" } let(:values){ [ "(1,2,3)", "(2,3,4)", "(3,4,5)" ] } context "when the max allowed bytes is 33 and the base SQL is 26 bytes" do it "should return 3 value sets when given 3 value sets of 7 bytes a piece" do value_sets = parser.parse values, :reserved_bytes => base_sql.size, :max_bytes => 33 assert_equal 3, value_sets.size end end context "when the max allowed bytes is 40 and the base SQL is 26 bytes" do it "should return 3 value sets when given 3 value sets of 7 bytes a piece" do value_sets = parser.parse values, :reserved_bytes => base_sql.size, :max_bytes => 40 assert_equal 3, value_sets.size end end context "when the max allowed bytes is 41 and the base SQL is 26 bytes" do it "should return 2 value sets when given 2 value sets of 7 bytes a piece" do value_sets = parser.parse values, :reserved_bytes => base_sql.size, :max_bytes => 41 assert_equal 2, value_sets.size end end context "when the max allowed bytes is 48 and the base SQL is 26 bytes" do it "should return 2 value sets when given 2 value sets of 7 bytes a piece" do value_sets = parser.parse values, :reserved_bytes => base_sql.size, :max_bytes => 48 assert_equal 2, value_sets.size end end context "when the max allowed bytes is 49 and the base SQL is 26 bytes" do it "should return 1 value sets when given 1 value sets of 7 bytes a piece" do value_sets = parser.parse values, :reserved_bytes => base_sql.size, :max_bytes => 49 assert_equal 1, value_sets.size end end context "when the max allowed bytes is 999999 and the base SQL is 26 bytes" do it "should return 1 value sets when given 1 value sets of 7 bytes a piece" do value_sets = parser.parse values, :reserved_bytes => base_sql.size, :max_bytes => 999999 assert_equal 1, value_sets.size end end it "should properly build insert value set based on max packet allowed" do values = [ "('1','2','3')", "('4','5','6')", "('7','8','9')" ] values_size_in_bytes = values.sum {|value| value.bytesize } base_sql_size_in_bytes = 15 max_bytes = 30 value_sets = parser.parse values, reserved_bytes: base_sql_size_in_bytes, max_bytes: max_bytes assert_equal 3, value_sets.size, 'Three value sets were expected!' # Each element in the value_sets array must be an array value_sets.each_with_index { |e,i| assert_kind_of Array, e, "Element #{i} was expected to be an Array!" } # Each element in the values array should have a 1:1 correlation to the elements # in the returned value_sets arrays assert_equal values[0], value_sets[0].first assert_equal values[1], value_sets[1].first assert_equal values[2], value_sets[2].first end context "data contains multi-byte chars" do it "should properly build insert value set based on max packet allowed" do # each accented e should be 2 bytes, so each entry is 6 bytes instead of 5 values = [ "('é')", "('é')" ] base_sql_size_in_bytes = 15 max_bytes = 26 values_size_in_bytes = values.sum {|value| value.bytesize } value_sets = parser.parse values, reserved_bytes: base_sql_size_in_bytes, max_bytes: max_bytes assert_equal 2, value_sets.size, 'Two value sets were expected!' end end end end activerecord-import-0.10.0/test/database.yml.sample0000644000004100000410000000142112557352107022335 0ustar www-datawww-datacommon: &common username: root password: encoding: utf8 host: localhost database: activerecord_import_test mysql: &mysql <<: *common adapter: mysql mysql2: &mysql2 <<: *common adapter: mysql2 mysqlspatial: <<: *mysql mysql2spatial: <<: *mysql2 em_mysql2: <<: *common adapter: em_mysql2 pool: 5 seamless_database_pool: <<: *common adapter: seamless_database_pool pool_adapter: mysql2 master: host: localhost postgresql: &postgresql <<: *common username: postgres adapter: postgresql min_messages: warning postgis: <<: *postgresql oracle: <<: *common adapter: oracle min_messages: debug sqlite: adapter: sqlite dbfile: test.db sqlite3: &sqlite3 adapter: sqlite3 database: test.db spatialite: <<: *sqlite3 activerecord-import-0.10.0/test/mysql/0000755000004100000410000000000012557352107017735 5ustar www-datawww-dataactiverecord-import-0.10.0/test/mysql/import_test.rb0000644000004100000410000000043112557352107022631 0ustar www-datawww-datarequire File.expand_path(File.dirname(__FILE__) + '/../test_helper') require File.expand_path(File.dirname(__FILE__) + '/../support/mysql/assertions') require File.expand_path(File.dirname(__FILE__) + '/../support/mysql/import_examples') should_support_mysql_import_functionalityactiverecord-import-0.10.0/test/test_helper.rb0000644000004100000410000000306112557352107021433 0ustar www-datawww-datarequire 'pathname' test_dir = Pathname.new File.dirname(__FILE__) $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) $LOAD_PATH.unshift(File.dirname(__FILE__)) require "fileutils" ENV["RAILS_ENV"] = "test" require "bundler" Bundler.setup require "active_record" require "active_record/fixtures" require "active_support/test_case" if ActiveSupport::VERSION::STRING < "4.1" require 'test/unit' else require 'active_support/testing/autorun' end require 'timecop' require 'chronic' require "ruby-debug" if RUBY_VERSION.to_f < 1.9 adapter = ENV["ARE_DB"] || "sqlite3" FileUtils.mkdir_p 'log' ActiveRecord::Base.logger = Logger.new("log/test.log") ActiveRecord::Base.logger.level = Logger::DEBUG ActiveRecord::Base.configurations["test"] = YAML.load_file(test_dir.join("database.yml"))[adapter] ActiveRecord::Base.default_timezone = :utc require "activerecord-import" ActiveRecord::Base.establish_connection "test" ActiveSupport::Notifications.subscribe(/active_record.sql/) do |event, _, _, _, hsh| ActiveRecord::Base.logger.info hsh[:sql] end require "factory_girl" Dir[File.dirname(__FILE__) + "/support/**/*.rb"].each{ |file| require file } # Load base/generic schema require test_dir.join("schema/version") require test_dir.join("schema/generic_schema") adapter_schema = test_dir.join("schema/#{adapter}_schema.rb") require adapter_schema if File.exists?(adapter_schema) Dir[File.dirname(__FILE__) + "/models/*.rb"].each{ |file| require file } # Prevent this deprecation warning from breaking the tests. Rake::FileList.send(:remove_method, :import) activerecord-import-0.10.0/test/postgis/0000755000004100000410000000000012557352107020260 5ustar www-datawww-dataactiverecord-import-0.10.0/test/postgis/import_test.rb0000644000004100000410000000032012557352107023151 0ustar www-datawww-datarequire File.expand_path(File.dirname(__FILE__) + '/../test_helper') require File.expand_path(File.dirname(__FILE__) + '/../support/postgresql/import_examples') should_support_postgresql_import_functionalityactiverecord-import-0.10.0/test/mysqlspatial/0000755000004100000410000000000012557352107021313 5ustar www-datawww-dataactiverecord-import-0.10.0/test/mysqlspatial/import_test.rb0000644000004100000410000000043112557352107024207 0ustar www-datawww-datarequire File.expand_path(File.dirname(__FILE__) + '/../test_helper') require File.expand_path(File.dirname(__FILE__) + '/../support/mysql/assertions') require File.expand_path(File.dirname(__FILE__) + '/../support/mysql/import_examples') should_support_mysql_import_functionalityactiverecord-import-0.10.0/test/sqlite3/0000755000004100000410000000000012557352107020154 5ustar www-datawww-dataactiverecord-import-0.10.0/test/sqlite3/import_test.rb0000644000004100000410000000501712557352107023055 0ustar www-datawww-datarequire File.expand_path(File.dirname(__FILE__) + '/../test_helper') describe "#supports_imports?" do context "and SQLite is 3.7.11 or higher" do it "supports import" do version = ActiveRecord::ConnectionAdapters::SQLite3Adapter::Version.new("3.7.11") assert ActiveRecord::Base.supports_import?(version) version = ActiveRecord::ConnectionAdapters::SQLite3Adapter::Version.new("3.7.12") assert ActiveRecord::Base.supports_import?(version) end end context "and SQLite less than 3.7.11" do it "doesn't support import" do version = ActiveRecord::ConnectionAdapters::SQLite3Adapter::Version.new("3.7.10") assert !ActiveRecord::Base.supports_import?(version) end end end describe "#import" do it "imports with a single insert on SQLite 3.7.11 or higher" do assert_difference "Topic.count", +507 do result = Topic.import Build(7, :topics) assert_equal 1, result.num_inserts, "Failed to issue a single INSERT statement. Make sure you have a supported version of SQLite3 (3.7.11 or higher) installed" assert_equal 7, Topic.count, "Failed to insert all records. Make sure you have a supported version of SQLite3 (3.7.11 or higher) installed" result = Topic.import Build(500, :topics) assert_equal 1, result.num_inserts, "Failed to issue a single INSERT statement. Make sure you have a supported version of SQLite3 (3.7.11 or higher) installed" assert_equal 507, Topic.count, "Failed to insert all records. Make sure you have a supported version of SQLite3 (3.7.11 or higher) installed" end end it "imports with a two inserts on SQLite 3.7.11 or higher" do assert_difference "Topic.count", +501 do result = Topic.import Build(501, :topics) assert_equal 2, result.num_inserts, "Failed to issue a two INSERT statements. Make sure you have a supported version of SQLite3 (3.7.11 or higher) installed" assert_equal 501, Topic.count, "Failed to insert all records. Make sure you have a supported version of SQLite3 (3.7.11 or higher) installed" end end it "imports with a five inserts on SQLite 3.7.11 or higher" do assert_difference "Topic.count", +2500 do result = Topic.import Build(2500, :topics) assert_equal 5, result.num_inserts, "Failed to issue a two INSERT statements. Make sure you have a supported version of SQLite3 (3.7.11 or higher) installed" assert_equal 2500, Topic.count, "Failed to insert all records. Make sure you have a supported version of SQLite3 (3.7.11 or higher) installed" end end end activerecord-import-0.10.0/test/mysql2/0000755000004100000410000000000012557352107020017 5ustar www-datawww-dataactiverecord-import-0.10.0/test/mysql2/import_test.rb0000644000004100000410000000043112557352107022713 0ustar www-datawww-datarequire File.expand_path(File.dirname(__FILE__) + '/../test_helper') require File.expand_path(File.dirname(__FILE__) + '/../support/mysql/assertions') require File.expand_path(File.dirname(__FILE__) + '/../support/mysql/import_examples') should_support_mysql_import_functionalityactiverecord-import-0.10.0/test/value_sets_records_parser_test.rb0000644000004100000410000000214312557352107025423 0ustar www-datawww-datarequire File.expand_path(File.dirname(__FILE__) + '/test_helper') require 'activerecord-import/value_sets_parser' describe "ActiveRecord::Import::ValueSetsRecordsParser" do context "#parse - computing insert value sets" do let(:parser){ ActiveRecord::Import::ValueSetsRecordsParser } let(:base_sql){ "INSERT INTO atable (a,b,c)" } let(:values){ [ "(1,2,3)", "(2,3,4)", "(3,4,5)" ] } context "when the max number of records is 1" do it "should return 3 value sets when given 3 values sets" do value_sets = parser.parse values, :max_records => 1 assert_equal 3, value_sets.size end end context "when the max number of records is 2" do it "should return 2 value sets when given 3 values sets" do value_sets = parser.parse values, :max_records => 2 assert_equal 2, value_sets.size end end context "when the max number of records is 3" do it "should return 1 value sets when given 3 values sets" do value_sets = parser.parse values, :max_records => 3 assert_equal 1, value_sets.size end end end end activerecord-import-0.10.0/test/jdbcpostgresql/0000755000004100000410000000000012557352107021616 5ustar www-datawww-dataactiverecord-import-0.10.0/test/jdbcpostgresql/import_test.rb0000644000004100000410000000045212557352107024515 0ustar www-datawww-datarequire File.expand_path(File.dirname(__FILE__) + '/../test_helper') #require File.expand_path(File.dirname(__FILE__) + '/../support/postgresql/assertions') require File.expand_path(File.dirname(__FILE__) + '/../support/postgresql/import_examples') should_support_postgresql_import_functionality activerecord-import-0.10.0/test/models/0000755000004100000410000000000012557352107020053 5ustar www-datawww-dataactiverecord-import-0.10.0/test/models/chapter.rb0000644000004100000410000000017012557352107022024 0ustar www-datawww-dataclass Chapter < ActiveRecord::Base belongs_to :book, :inverse_of=>:chapters validates :title, :presence => true end activerecord-import-0.10.0/test/models/widget.rb0000644000004100000410000000021112557352107021655 0ustar www-datawww-dataclass Widget < ActiveRecord::Base self.primary_key = :w_id default_scope lambda { where(active: true) } serialize :data, Hash endactiverecord-import-0.10.0/test/models/topic.rb0000644000004100000410000000061412557352107021517 0ustar www-datawww-dataclass Topic < ActiveRecord::Base validates_presence_of :author_name validates :title, numericality: { only_integer: true }, on: :context_test has_many :books, :autosave=>true, :inverse_of=>:topic belongs_to :parent, :class_name => "Topic" composed_of :description, :mapping => [ %w(title title), %w(author_name author_name)], :allow_nil => true, :class_name => "TopicDescription" end activerecord-import-0.10.0/test/models/book.rb0000644000004100000410000000043112557352107021330 0ustar www-datawww-dataclass Book < ActiveRecord::Base belongs_to :topic, :inverse_of=>:books has_many :chapters, :autosave => true, :inverse_of => :book has_many :end_notes, :autosave => true, :inverse_of => :book if ENV['AR_VERSION'].to_i >= 4.1 enum status: [:draft, :published] end end activerecord-import-0.10.0/test/models/group.rb0000644000004100000410000000010112557352107021524 0ustar www-datawww-dataclass Group < ActiveRecord::Base self.table_name = 'group' end activerecord-import-0.10.0/test/models/end_note.rb0000644000004100000410000000017012557352107022171 0ustar www-datawww-dataclass EndNote < ActiveRecord::Base belongs_to :book, :inverse_of=>:end_notes validates :note, :presence => true end activerecord-import-0.10.0/test/postgresql/0000755000004100000410000000000012557352107020773 5ustar www-datawww-dataactiverecord-import-0.10.0/test/postgresql/import_test.rb0000644000004100000410000000032112557352107023665 0ustar www-datawww-datarequire File.expand_path(File.dirname(__FILE__) + '/../test_helper') require File.expand_path(File.dirname(__FILE__) + '/../support/postgresql/import_examples') should_support_postgresql_import_functionality activerecord-import-0.10.0/test/support/0000755000004100000410000000000012557352107020304 5ustar www-datawww-dataactiverecord-import-0.10.0/test/support/generate.rb0000644000004100000410000000150212557352107022421 0ustar www-datawww-dataclass ActiveSupport::TestCase def Build(*args) n = args.shift if args.first.is_a?(Numeric) factory = args.shift factory_girl_args = args.shift || {} if n Array.new.tap do |collection| n.times.each { collection << FactoryGirl.build(factory.to_s.singularize.to_sym, factory_girl_args) } end else FactoryGirl.build(factory.to_s.singularize.to_sym, factory_girl_args) end end def Generate(*args) n = args.shift if args.first.is_a?(Numeric) factory = args.shift factory_girl_args = args.shift || {} if n Array.new.tap do |collection| n.times.each { collection << FactoryGirl.create(factory.to_s.singularize.to_sym, factory_girl_args) } end else FactoryGirl.create(factory.to_s.singularize.to_sym, factory_girl_args) end end end activerecord-import-0.10.0/test/support/mysql/0000755000004100000410000000000012557352107021451 5ustar www-datawww-dataactiverecord-import-0.10.0/test/support/mysql/import_examples.rb0000644000004100000410000001402712557352107025212 0ustar www-datawww-data# encoding: UTF-8 def should_support_mysql_import_functionality # Forcefully disable strict mode for this session. ActiveRecord::Base.connection.execute "set sql_mode=''" describe "#import with :on_duplicate_key_update option (mysql specific functionality)" do extend ActiveSupport::TestCase::MySQLAssertions asssertion_group(:should_support_on_duplicate_key_update) do should_not_update_fields_not_mentioned should_update_foreign_keys should_not_update_created_at_on_timestamp_columns should_update_updated_at_on_timestamp_columns end macro(:perform_import){ raise "supply your own #perform_import in a context below" } macro(:updated_topic){ Topic.find(@topic.id) } context "given columns and values with :validation checks turned off" do let(:columns){ %w( id title author_name author_email_address parent_id ) } let(:values){ [ [ 99, "Book", "John Doe", "john@doe.com", 17 ] ] } let(:updated_values){ [ [ 99, "Book - 2nd Edition", "Author Should Not Change", "johndoe@example.com", 57 ] ] } macro(:perform_import) do |*opts| Topic.import columns, updated_values, opts.extract_options!.merge(:on_duplicate_key_update => update_columns, :validate => false) end setup do Topic.import columns, values, :validate => false @topic = Topic.find 99 end context "using string column names" do let(:update_columns){ [ "title", "author_email_address", "parent_id" ] } should_support_on_duplicate_key_update should_update_fields_mentioned end context "using symbol column names" do let(:update_columns){ [ :title, :author_email_address, :parent_id ] } should_support_on_duplicate_key_update should_update_fields_mentioned end context "using string hash map" do let(:update_columns){ { "title" => "title", "author_email_address" => "author_email_address", "parent_id" => "parent_id" } } should_support_on_duplicate_key_update should_update_fields_mentioned end context "using string hash map, but specifying column mismatches" do let(:update_columns){ { "title" => "author_email_address", "author_email_address" => "title", "parent_id" => "parent_id" } } should_support_on_duplicate_key_update should_update_fields_mentioned_with_hash_mappings end context "using symbol hash map" do let(:update_columns){ { :title => :title, :author_email_address => :author_email_address, :parent_id => :parent_id } } should_support_on_duplicate_key_update should_update_fields_mentioned end context "using symbol hash map, but specifying column mismatches" do let(:update_columns){ { :title => :author_email_address, :author_email_address => :title, :parent_id => :parent_id } } should_support_on_duplicate_key_update should_update_fields_mentioned_with_hash_mappings end end context "given array of model instances with :validation checks turned off" do macro(:perform_import) do |*opts| @topic.title = "Book - 2nd Edition" @topic.author_name = "Author Should Not Change" @topic.author_email_address = "johndoe@example.com" @topic.parent_id = 57 Topic.import [@topic], opts.extract_options!.merge(:on_duplicate_key_update => update_columns, :validate => false) end setup do @topic = Generate(:topic, :id => 99, :author_name => "John Doe", :parent_id => 17) end context "using string column names" do let(:update_columns){ [ "title", "author_email_address", "parent_id" ] } should_support_on_duplicate_key_update should_update_fields_mentioned end context "using symbol column names" do let(:update_columns){ [ :title, :author_email_address, :parent_id ] } should_support_on_duplicate_key_update should_update_fields_mentioned end context "using string hash map" do let(:update_columns){ { "title" => "title", "author_email_address" => "author_email_address", "parent_id" => "parent_id" } } should_support_on_duplicate_key_update should_update_fields_mentioned end context "using string hash map, but specifying column mismatches" do let(:update_columns){ { "title" => "author_email_address", "author_email_address" => "title", "parent_id" => "parent_id" } } should_support_on_duplicate_key_update should_update_fields_mentioned_with_hash_mappings end context "using symbol hash map" do let(:update_columns){ { :title => :title, :author_email_address => :author_email_address, :parent_id => :parent_id } } should_support_on_duplicate_key_update should_update_fields_mentioned end context "using symbol hash map, but specifying column mismatches" do let(:update_columns){ { :title => :author_email_address, :author_email_address => :title, :parent_id => :parent_id } } should_support_on_duplicate_key_update should_update_fields_mentioned_with_hash_mappings end end end describe "#import with :synchronization option" do let(:topics){ Array.new } let(:values){ [ [topics.first.id, "Jerry Carter"], [topics.last.id, "Chad Fowler"] ]} let(:columns){ %W(id author_name) } setup do topics << Topic.create!(:title=>"LDAP", :author_name=>"Big Bird") topics << Topic.create!(:title=>"Rails Recipes", :author_name=>"Elmo") end it "synchronizes passed in ActiveRecord model instances with the data just imported" do columns2update = [ 'author_name' ] expected_count = Topic.count Topic.import( columns, values, :validate=>false, :on_duplicate_key_update=>columns2update, :synchronize=>topics ) assert_equal expected_count, Topic.count, "no new records should have been created!" assert_equal "Jerry Carter", topics.first.author_name, "wrong author!" assert_equal "Chad Fowler", topics.last.author_name, "wrong author!" end end end activerecord-import-0.10.0/test/support/mysql/assertions.rb0000644000004100000410000000415312557352107024173 0ustar www-datawww-dataclass ActiveSupport::TestCase module MySQLAssertions def self.extended(klass) klass.instance_eval do assertion(:should_not_update_created_at_on_timestamp_columns) do Timecop.freeze Chronic.parse("5 minutes from now") do perform_import assert_in_delta @topic.created_at.to_i, updated_topic.created_at.to_i, 1 assert_in_delta @topic.created_on.to_i, updated_topic.created_on.to_i, 1 end end assertion(:should_update_updated_at_on_timestamp_columns) do time = Chronic.parse("5 minutes from now") Timecop.freeze time do perform_import assert_in_delta time.to_i, updated_topic.updated_at.to_i, 1 assert_in_delta time.to_i, updated_topic.updated_on.to_i, 1 end end assertion(:should_not_update_timestamps) do Timecop.freeze Chronic.parse("5 minutes from now") do perform_import :timestamps => false assert_in_delta @topic.created_at.to_i, updated_topic.created_at.to_i, 1 assert_in_delta @topic.created_on.to_i, updated_topic.created_on.to_i, 1 assert_in_delta @topic.updated_at.to_i, updated_topic.updated_at.to_i, 1 assert_in_delta @topic.updated_on.to_i, updated_topic.updated_on.to_i, 1 end end assertion(:should_not_update_fields_not_mentioned) do assert_equal "John Doe", updated_topic.author_name end assertion(:should_update_fields_mentioned) do perform_import assert_equal "Book - 2nd Edition", updated_topic.title assert_equal "johndoe@example.com", updated_topic.author_email_address end assertion(:should_update_fields_mentioned_with_hash_mappings) do perform_import assert_equal "johndoe@example.com", updated_topic.title assert_equal "Book - 2nd Edition", updated_topic.author_email_address end assertion(:should_update_foreign_keys) do perform_import assert_equal 57, updated_topic.parent_id end end end end end activerecord-import-0.10.0/test/support/factories.rb0000644000004100000410000000167312557352107022617 0ustar www-datawww-dataFactoryGirl.define do sequence(:book_title) {|n| "Book #{n}"} sequence(:chapter_title) {|n| "Chapter #{n}"} sequence(:end_note) {|n| "Endnote #{n}"} factory :group do sequence(:order) { |n| "Order #{n}" } end factory :invalid_topic, :class => "Topic" do sequence(:title){ |n| "Title #{n}"} author_name nil end factory :topic do sequence(:title){ |n| "Title #{n}"} sequence(:author_name){ |n| "Author #{n}"} end factory :widget do sequence(:w_id){ |n| n} end factory :topic_with_book, :parent=>:topic do |m| after(:build) do |topic| 2.times do book = topic.books.build(:title=>FactoryGirl.generate(:book_title), :author_name=>'Stephen King') 3.times do book.chapters.build(:title => FactoryGirl.generate(:chapter_title)) end 4.times do book.end_notes.build(:note => FactoryGirl.generate(:end_note)) end end end end end activerecord-import-0.10.0/test/support/postgresql/0000755000004100000410000000000012557352107022507 5ustar www-datawww-dataactiverecord-import-0.10.0/test/support/postgresql/import_examples.rb0000644000004100000410000000707612557352107026256 0ustar www-datawww-data# encoding: UTF-8 def should_support_postgresql_import_functionality describe "#supports_imports?" do it "should support import" do assert ActiveRecord::Base.supports_import? end end describe "#import" do it "should import with a single insert" do # see ActiveRecord::ConnectionAdapters::AbstractAdapter test for more specifics assert_difference "Topic.count", +10 do result = Topic.import Build(3, :topics) assert_equal 1, result.num_inserts result = Topic.import Build(7, :topics) assert_equal 1, result.num_inserts end end describe "importing objects with associations" do let(:new_topics) { Build(num_topics, :topic_with_book) } let(:new_topics_with_invalid_chapter) { chapter = new_topics.first.books.first.chapters.first chapter.title = nil new_topics } let(:num_topics) {3} let(:num_books) {6} let(:num_chapters) {18} let(:num_endnotes) {24} it 'imports top level' do assert_difference "Topic.count", +num_topics do Topic.import new_topics, :recursive => true new_topics.each do |topic| assert_not_nil topic.id end end end it 'imports first level associations' do assert_difference "Book.count", +num_books do Topic.import new_topics, :recursive => true new_topics.each do |topic| topic.books.each do |book| assert_equal topic.id, book.topic_id end end end end [{:recursive => false}, {}].each do |import_options| it "skips recursion for #{import_options.to_s}" do assert_difference "Book.count", 0 do Topic.import new_topics, import_options end end end it 'imports deeper nested associations' do assert_difference "Chapter.count", +num_chapters do assert_difference "EndNote.count", +num_endnotes do Topic.import new_topics, :recursive => true new_topics.each do |topic| topic.books.each do |book| book.chapters.each do |chapter| assert_equal book.id, chapter.book_id end book.end_notes.each do |endnote| assert_equal book.id, endnote.book_id end end end end end end it "skips validation of the associations if requested" do assert_difference "Chapter.count", +num_chapters do Topic.import new_topics_with_invalid_chapter, :validate => false, :recursive => true end end # These models dont validate associated. So we expect that books and topics get inserted, but not chapters # Putting a transaction around everything wouldn't work, so if you want your chapters to prevent topics from # being created, you would need to have validates_associated in your models and insert with validation describe "all_or_none" do [Book, Topic, EndNote].each do |type| it "creates #{type.to_s}" do assert_difference "#{type.to_s}.count", send("num_#{type.to_s.downcase}s") do Topic.import new_topics_with_invalid_chapter, :all_or_none => true, :recursive => true end end end it "doesn't create chapters" do assert_difference "Chapter.count", 0 do Topic.import new_topics_with_invalid_chapter, :all_or_none => true, :recursive => true end end end end end end activerecord-import-0.10.0/test/support/active_support/0000755000004100000410000000000012557352107023353 5ustar www-datawww-dataactiverecord-import-0.10.0/test/support/active_support/test_case_extensions.rb0000644000004100000410000000312112557352107030126 0ustar www-datawww-dataclass ActiveSupport::TestCase include ActiveRecord::TestFixtures self.use_transactional_fixtures = true class << self def assertion(name, &block) mc = class << self ; self ; end mc.class_eval do define_method(name) do it(name, &block) end end end def asssertion_group(name, &block) mc = class << self ; self ; end mc.class_eval do define_method(name, &block) end end def macro(name, &block) class_eval do define_method(name, &block) end end def describe(description, toplevel=nil, &blk) text = toplevel ? description : "#{name} #{description}" klass = Class.new(self) klass.class_eval <<-RUBY_EVAL def self.name "#{text}" end RUBY_EVAL # do not inherit test methods from the superclass klass.class_eval do instance_methods.grep(/^test.+/) do |method| undef_method method end end klass.instance_eval &blk end alias_method :context, :describe def let(name, &blk) define_method(name) do instance_variable_name = "@__let_#{name}" return instance_variable_get(instance_variable_name) if instance_variable_defined?(instance_variable_name) instance_variable_set(instance_variable_name, instance_eval(&blk)) end end def it(description, &blk) define_method("test_#{name}_#{description}", &blk) end end end def describe(description, &blk) ActiveSupport::TestCase.describe(description, true, &blk) end activerecord-import-0.10.0/test/travis/0000755000004100000410000000000012557352107020100 5ustar www-datawww-dataactiverecord-import-0.10.0/test/travis/build.sh0000755000004100000410000000233112557352107021535 0ustar www-datawww-data#!/bin/bash set -e set +x function run { echo "Running: AR_VERSION=$AR_VERSION $@" $@ } for activerecord_version in "3.1" "3.2" "4.1" "4.2" ; do export AR_VERSION=$activerecord_version bundle update activerecord run run bundle exec rake test:em_mysql2 # Run tests for em_mysql2 run bundle exec rake test:mysql # Run tests for mysql run bundle exec rake test:mysql2 # Run tests for mysql2 run bundle exec rake test:mysql2spatial # Run tests for mysql2spatial run bundle exec rake test:mysqlspatial # Run tests for mysqlspatial run bundle exec rake test:postgis # Run tests for postgis run bundle exec rake test:postgresql # Run tests for postgresql run bundle exec rake test:seamless_database_pool # Run tests for seamless_database_pool run bundle exec rake test:spatialite # Run tests for spatialite # so far the version installed in travis seems < 3.7.11 so we cannot test sqlite3 on it # run bundle exec rake test:sqlite3 #jruby #bundle exec rake test:jdbcmysql # Run tests for jdbcmysql #bundle exec rake test:jdbcpostgresql # Run tests for jdbcpostgresql done activerecord-import-0.10.0/test/travis/database.yml0000644000004100000410000000142412557352107022370 0ustar www-datawww-datacommon: &common username: root password: encoding: utf8 host: localhost database: activerecord_import_test mysql: &mysql <<: *common adapter: mysql mysql2: &mysql2 <<: *common adapter: mysql2 mysqlspatial: <<: *mysql mysql2spatial: <<: *mysql2 em_mysql2: <<: *common adapter: em_mysql2 pool: 5 seamless_database_pool: <<: *common adapter: seamless_database_pool pool_adapter: mysql2 master: host: localhost postgresql: &postgresql <<: *common username: postgres adapter: postgresql min_messages: warning postgis: <<: *postgresql oracle: <<: *common adapter: oracle min_messages: debug sqlite: adapter: sqlite dbfile: test.db sqlite3: &sqlite3 adapter: sqlite3 database: ":memory:" spatialite: <<: *sqlite3 activerecord-import-0.10.0/.gitignore0000644000004100000410000000035612557352107017605 0ustar www-datawww-data## MAC OS .DS_Store ## TEXTMATE *.tmproj tmtags ## EMACS *~ \#* .\#* ## VIM *.swp ## PROJECT::GENERAL coverage rdoc pkg *.gem *.lock ## PROJECT::SPECIFIC log/*.log test.db test/database.yml .ruby-* .bundle/ .redcar/ .rvmrc docsite/ activerecord-import-0.10.0/LICENSE0000644000004100000410000000471012557352107016620 0ustar www-datawww-dataRuby is copyrighted free software by Yukihiro Matsumoto . You can redistribute it and/or modify it under either the terms of the 2-clause BSDL (see the file BSDL), or the conditions below: 1. You may make and give away verbatim copies of the source form of the software without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may modify your copy of the software in any way, provided that you do at least ONE of the following: a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or by allowing the author to include your modifications in the software. b) use the modified software only within your corporation or organization. c) give non-standard binaries non-standard names, with instructions on where to get the original software distribution. d) make other distribution arrangements with the author. 3. You may distribute the software in object code or binary form, provided that you do at least ONE of the following: a) distribute the binaries and library files of the software, together with instructions (in the manual page or equivalent) on where to get the original distribution. b) accompany the distribution with the machine-readable source of the software. c) give non-standard binaries non-standard names, with instructions on where to get the original software distribution. d) make other distribution arrangements with the author. 4. You may modify and include the part of the software into any other software (possibly commercial). But some files in the distribution are not written by the author, so that they are not under these terms. For the list of those files and their copying conditions, see the file LEGAL. 5. The scripts and library files supplied as input to or produced as output from the software do not automatically fall under the copyright of the software, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this software. 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. activerecord-import-0.10.0/benchmarks/0000755000004100000410000000000012557352107017726 5ustar www-datawww-dataactiverecord-import-0.10.0/benchmarks/schema/0000755000004100000410000000000012557352107021166 5ustar www-datawww-dataactiverecord-import-0.10.0/benchmarks/schema/mysql_schema.rb0000644000004100000410000000101412557352107024174 0ustar www-datawww-dataActiveRecord::Schema.define do create_table :test_myisam, :options=>'ENGINE=MyISAM', :force=>true do |t| t.column :my_name, :string, :null=>false t.column :description, :string end create_table :test_innodb, :options=>'ENGINE=InnoDb', :force=>true do |t| t.column :my_name, :string, :null=>false t.column :description, :string end create_table :test_memory, :options=>'ENGINE=Memory', :force=>true do |t| t.column :my_name, :string, :null=>false t.column :description, :string end endactiverecord-import-0.10.0/benchmarks/README0000644000004100000410000000217312557352107020611 0ustar www-datawww-dataTo run the benchmarks, from within the benchmarks run: ruby benchmark.rb [options] The following options are supported: --adapter [String] The database adapter to use. IE: mysql, postgresql, oracle --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. --num [Integer] The number of objects to benchmark. (Required!) --table-type [String] The table type to test. This can be used multiple times. By default it is all table types. --to-csv [String] Print results in a CSV file format --to-html [String] Print results in HTML format (String filename must be supplied) See "ruby benchmark.rb -h" for the complete listing of options. EXAMPLES -------- To output to html format: ruby benchmark.rb --adapter=mysql --to-html=results.html To output to csv format: ruby benchmark.rb --adapter=mysql --to-csv=results.csv LIMITATIONS ----------- Currently MySQL is the only supported adapter to benchmark. AUTHOR ------ Zach Dennis zach.dennis@gmail.com http://www.continuousthinking.com activerecord-import-0.10.0/benchmarks/boot.rb0000644000004100000410000000076412557352107021225 0ustar www-datawww-databegin ; require 'rubygems' ; rescue LoadError ; end require 'active_record' # ActiveRecord loads the Benchmark library automatically require 'active_record/version' require 'fastercsv' require 'fileutils' require 'logger' # Files are loaded alphabetically. If this is a problem then manually specify the files # that need to be loaded here. Dir[ File.join( File.dirname( __FILE__ ), 'lib', '*.rb' ) ].sort.each{ |f| require f } ActiveRecord::Base.logger = Logger.new STDOUT activerecord-import-0.10.0/benchmarks/lib/0000755000004100000410000000000012557352107020474 5ustar www-datawww-dataactiverecord-import-0.10.0/benchmarks/lib/cli_parser.rb0000644000004100000410000000617012557352107023150 0ustar www-datawww-datarequire 'optparse' require 'ostruct' # # == PARAMETERS # * a - database adapter. ie: mysql, postgresql, oracle, etc. # * n - number of objects to test with. ie: 1, 100, 1000, etc. # * t - the table types to test. ie: myisam, innodb, memory, temporary, etc. # module BenchmarkOptionParser BANNER = "Usage: ruby #{$0} [options]\nSee ruby #{$0} -h for more options." def self.print_banner puts BANNER end def self.print_banner! print_banner exit end def self.print_options( options ) puts "Benchmarking the following options:" puts " Database adapter: #{options.adapter}" puts " Number of objects: #{options.number_of_objects}" puts " Table types:" print_valid_table_types( options, :prefix=>" " ) end # TODO IMPLEMENT THIS def self.print_valid_table_types( options, hsh={:prefix=>''} ) if options.table_types.keys.size > 0 options.table_types.keys.sort.each{ |type| puts hsh[:prefix].to_s + type.to_s } else puts 'No table types defined.' end end def self.parse( args ) options = OpenStruct.new( :table_types => {}, :delete_on_finish => true, :number_of_objects => [], :outputs => [] ) opts = OptionParser.new do |opts| opts.banner = BANNER # parse the database adapter opts.on( "a", "--adapter [String]", "The database adapter to use. IE: mysql, postgresql, oracle" ) do |arg| options.adapter = arg end # parse do_not_delete flag opts.on( "d", "--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." ) do |arg| options.delete_on_finish = false end # parse the number of row objects to test opts.on( "n", "--num [Integer]", "The number of objects to benchmark." ) do |arg| options.number_of_objects << arg.to_i end # parse the table types to test opts.on( "t", "--table-type [String]", "The table type to test. This can be used multiple times." ) do |arg| if arg =~ /^all$/ options.table_types['all'] = options.benchmark_all_types = true else options.table_types[arg] = true end end # print results in CSV format opts.on( "--to-csv [String]", "Print results in a CSV file format" ) do |filename| options.outputs << OpenStruct.new( :format=>'csv', :filename=>filename) end # print results in HTML format opts.on( "--to-html [String]", "Print results in HTML format" ) do |filename| options.outputs << OpenStruct.new( :format=>'html', :filename=>filename ) end end #end opt.parse! begin opts.parse!( args ) if options.table_types.size == 0 options.table_types['all'] = options.benchmark_all_types = true end rescue Exception => ex print_banner! end print_options( options ) options end end activerecord-import-0.10.0/benchmarks/lib/output_to_csv.rb0000644000004100000410000000071512557352107023741 0ustar www-datawww-datarequire 'fastercsv' module OutputToCSV def self.output_results( filename, results ) FasterCSV.open( filename, 'w' ) do |csv| # Iterate over each result set, which contains many results results.each do |result_set| columns, times = [], [] result_set.each do |result| columns << result.description times << result.tms.real end csv << columns csv << times end end end end activerecord-import-0.10.0/benchmarks/lib/float.rb0000644000004100000410000000043212557352107022125 0ustar www-datawww-data# Taken from http://www.programmingishard.com/posts/show/128 # Posted by rbates class Float def round_to(x) (self * 10**x).round.to_f / 10**x end def ceil_to(x) (self * 10**x).ceil.to_f / 10**x end def floor_to(x) (self * 10**x).floor.to_f / 10**x end end activerecord-import-0.10.0/benchmarks/lib/output_to_html.rb0000644000004100000410000000325412557352107024113 0ustar www-datawww-datarequire 'erb' module OutputToHTML TEMPLATE_HEADER =<<"EOT"
All times are rounded to the nearest thousandth for display purposes. Speedups next to each time are computed before any rounding occurs. Also, all speedup calculations are computed by comparing a given time against the very first column (which is always the default ActiveRecord::Base.create method.
EOT TEMPLATE =<<"EOT" <% columns.each do |col| %> <% end %> <% times.each do |time| %> <% end %>
<%= col %>
<%= time %>
 
EOT def self.output_results( filename, results ) html = '' results.each do |result_set| columns, times = [], [] result_set.each do |result| columns << result.description if result.failed times << "failed" else time = result.tms.real.round_to( 3 ) speedup = ( result_set.first.tms.real / result.tms.real ).round if result == result_set.first times << "#{time}" else times << "#{time} (#{speedup}x speedup)" end end end template = ERB.new( TEMPLATE, 0, "%<>") html << template.result( binding ) end File.open( filename, 'w' ){ |file| file.write( TEMPLATE_HEADER + html ) } end end activerecord-import-0.10.0/benchmarks/lib/base.rb0000644000004100000410000001126312557352107021736 0ustar www-datawww-dataclass BenchmarkBase attr_reader :results # The main benchmark method dispatcher. This dispatches the benchmarks # to actual benchmark_xxxx methods. # # == PARAMETERS # * table_types - an array of table types to benchmark # * num - the number of record insertions to test def benchmark( table_types, num ) array_of_cols_and_vals = build_array_of_cols_and_vals( num ) table_types.each do |table_type| self.send( "benchmark_#{table_type}", array_of_cols_and_vals ) end end # Returns an OpenStruct which contains two attritues, +description+ and +tms+ after performing an # actual benchmark. # # == PARAMETERS # * description - the description of the block that is getting benchmarked # * blk - the block of code to benchmark # # == RETURNS # An OpenStruct object with the following attributes: # * description - the description of the benchmark ran # * tms - a Benchmark::Tms containing the results of the benchmark def bm( description, &blk ) tms = nil puts "Benchmarking #{description}" Benchmark.bm { |x| tms = x.report { blk.call } } delete_all failed = false OpenStruct.new :description=>description, :tms=>tms, :failed=>failed end # Given a model class (ie: Topic), and an array of columns and value sets # this will perform all of the benchmarks necessary for this library. # # == PARAMETERS # * model_clazz - the model class to benchmark (ie: Topic) # * array_of_cols_and_vals - an array of column identifiers and value sets # # == RETURNS # returns true def bm_model( model_clazz, array_of_cols_and_vals ) puts puts "------ Benchmarking #{model_clazz.name} -------" cols,vals = array_of_cols_and_vals num_inserts = vals.size # add a new result group for this particular benchmark group = [] @results << group description = "#{model_clazz.name}.create (#{num_inserts} records)" group << bm( description ) { vals.each do |values| model_clazz.create create_hash_for_cols_and_vals( cols, values ) end } description = "#{model_clazz.name}.import(column, values) for #{num_inserts} records with validations" group << bm( description ) { model_clazz.import cols, vals, :validate=>true } description = "#{model_clazz.name}.import(columns, values) for #{num_inserts} records without validations" group << bm( description ) { model_clazz.import cols, vals, :validate=>false } models = [] array_of_attrs = [] vals.each do |arr| array_of_attrs << (attrs={}) arr.each_with_index { |value, i| attrs[cols[i]] = value } end array_of_attrs.each{ |attrs| models << model_clazz.new(attrs) } description = "#{model_clazz.name}.import(models) for #{num_inserts} records with validations" group << bm( description ) { model_clazz.import models, :validate=>true } description = "#{model_clazz.name}.import(models) for #{num_inserts} records without validations" group << bm( description ) { model_clazz.import models, :validate=>false } true end # Returns a two element array composing of an array of columns and an array of # value sets given the passed +num+. # # === What is a value set? # A value set is an array of arrays. Each child array represents an array of value sets # for a given row of data. # # For example, say we wanted to represent an insertion of two records: # column_names = [ 'id', 'name', 'description' ] # record1 = [ 1, 'John Doe', 'A plumber' ] # record2 = [ 2, 'John Smith', 'A painter' ] # value_set [ record1, record2 ] # # == PARAMETER # * num - the number of records to create def build_array_of_cols_and_vals( num ) cols = [ :my_name, :description ] value_sets = [] num.times { |i| value_sets << [ "My Name #{i}", "My Description #{i}" ] } [ cols, value_sets ] end # Returns a hash of column identifier to value mappings giving the passed in # value array. # # Example: # cols = [ 'id', 'name', 'description' ] # values = [ 1, 'John Doe', 'A plumber' ] # hsh = create_hash_for_cols_and_vals( cols, values ) # # hsh => { 'id'=>1, 'name'=>'John Doe', 'description'=>'A plumber' } def create_hash_for_cols_and_vals( cols, vals ) h = {} cols.zip( vals ){ |col,val| h[col] = val } h end # Deletes all records from all ActiveRecord subclasses def delete_all ActiveRecord::Base.send( :subclasses ).each do |subclass| subclass.delete_all if subclass.respond_to? :delete_all end end def initialize # :nodoc: @results = [] end end activerecord-import-0.10.0/benchmarks/lib/mysql_benchmark.rb0000644000004100000410000000121112557352107024173 0ustar www-datawww-dataclass MysqlBenchmark < BenchmarkBase def benchmark_all( array_of_cols_and_vals ) methods = self.methods.find_all { |m| m =~ /benchmark_/ } methods.delete_if{ |m| m =~ /benchmark_(all|model)/ } methods.each { |method| self.send( method, array_of_cols_and_vals ) } end def benchmark_myisam( array_of_cols_and_vals ) bm_model( TestMyISAM, array_of_cols_and_vals ) end def benchmark_innodb( array_of_cols_and_vals ) bm_model( TestInnoDb, array_of_cols_and_vals ) end def benchmark_memory( array_of_cols_and_vals ) bm_model( TestMemory, array_of_cols_and_vals ) end end activerecord-import-0.10.0/benchmarks/benchmark.rb0000644000004100000410000000371112557352107022207 0ustar www-datawww-datarequire "pathname" this_dir = Pathname.new File.dirname(__FILE__) require this_dir.join('boot') # Parse the options passed in via the command line options = BenchmarkOptionParser.parse( ARGV ) # The support directory where we use to load our connections and models for the # benchmarks. SUPPORT_DIR = this_dir.join('../test') # Load the database adapter adapter = options.adapter # load the library LIB_DIR = this_dir.join("../lib") require LIB_DIR.join("activerecord-import/#{adapter}") ActiveRecord::Base.logger = Logger.new("log/test.log") ActiveRecord::Base.logger.level = Logger::DEBUG ActiveRecord::Base.configurations["test"] = YAML.load(SUPPORT_DIR.join("database.yml").open)[adapter] ActiveRecord::Base.establish_connection "test" ActiveSupport::Notifications.subscribe(/active_record.sql/) do |event, _, _, _, hsh| ActiveRecord::Base.logger.info hsh[:sql] end adapter_schema = SUPPORT_DIR.join("schema/#{adapter}_schema.rb") require adapter_schema if File.exists?(adapter_schema) Dir[this_dir.join("models/*.rb")].each{ |file| require file } # Load databse specific benchmarks require File.join( File.dirname( __FILE__ ), 'lib', "#{adapter}_benchmark" ) # TODO implement method/table-type selection table_types = nil if options.benchmark_all_types table_types = [ "all" ] else table_types = options.table_types.keys end puts letter = options.adapter[0].chr clazz_str = letter.upcase + options.adapter[1..-1].downcase clazz = Object.const_get( clazz_str + "Benchmark" ) benchmarks = [] options.number_of_objects.each do |num| benchmarks << (benchmark = clazz.new) benchmark.send( "benchmark", table_types, num ) end options.outputs.each do |output| format = output.format.downcase output_module = Object.const_get( "OutputTo#{format.upcase}" ) benchmarks.each do |benchmark| output_module.output_results( output.filename, benchmark.results ) end end puts puts "Done with benchmark!" activerecord-import-0.10.0/benchmarks/models/0000755000004100000410000000000012557352107021211 5ustar www-datawww-dataactiverecord-import-0.10.0/benchmarks/models/test_memory.rb0000644000004100000410000000011112557352107024076 0ustar www-datawww-dataclass TestMemory < ActiveRecord::Base set_table_name 'test_memory' end activerecord-import-0.10.0/benchmarks/models/test_innodb.rb0000644000004100000410000000011112557352107024037 0ustar www-datawww-dataclass TestInnoDb < ActiveRecord::Base set_table_name 'test_innodb' end activerecord-import-0.10.0/benchmarks/models/test_myisam.rb0000644000004100000410000000011112557352107024065 0ustar www-datawww-dataclass TestMyISAM < ActiveRecord::Base set_table_name 'test_myisam' end