activerecord-import-rails4 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +31 -0
  3. data/Appraisals +9 -0
  4. data/Gemfile +25 -0
  5. data/README.markdown +24 -0
  6. data/Rakefile +52 -0
  7. data/activerecord-import-rails4.gemspec +24 -0
  8. data/benchmarks/README +32 -0
  9. data/benchmarks/benchmark.rb +64 -0
  10. data/benchmarks/boot.rb +18 -0
  11. data/benchmarks/lib/base.rb +137 -0
  12. data/benchmarks/lib/cli_parser.rb +103 -0
  13. data/benchmarks/lib/float.rb +15 -0
  14. data/benchmarks/lib/mysql_benchmark.rb +22 -0
  15. data/benchmarks/lib/output_to_csv.rb +18 -0
  16. data/benchmarks/lib/output_to_html.rb +69 -0
  17. data/benchmarks/models/test_innodb.rb +3 -0
  18. data/benchmarks/models/test_memory.rb +3 -0
  19. data/benchmarks/models/test_myisam.rb +3 -0
  20. data/benchmarks/schema/mysql_schema.rb +16 -0
  21. data/gemfiles/rails3.gemfile +18 -0
  22. data/gemfiles/rails4.gemfile +18 -0
  23. data/lib/activerecord-import-rails4.rb +16 -0
  24. data/lib/activerecord-import-rails4/active_record/adapters/abstract_adapter.rb +10 -0
  25. data/lib/activerecord-import-rails4/active_record/adapters/jdbcmysql_adapter.rb +6 -0
  26. data/lib/activerecord-import-rails4/active_record/adapters/mysql2_adapter.rb +6 -0
  27. data/lib/activerecord-import-rails4/active_record/adapters/mysql_adapter.rb +6 -0
  28. data/lib/activerecord-import-rails4/active_record/adapters/postgresql_adapter.rb +7 -0
  29. data/lib/activerecord-import-rails4/active_record/adapters/seamless_database_pool_adapter.rb +7 -0
  30. data/lib/activerecord-import-rails4/active_record/adapters/sqlite3_adapter.rb +7 -0
  31. data/lib/activerecord-import-rails4/adapters/abstract_adapter.rb +119 -0
  32. data/lib/activerecord-import-rails4/adapters/mysql2_adapter.rb +5 -0
  33. data/lib/activerecord-import-rails4/adapters/mysql_adapter.rb +55 -0
  34. data/lib/activerecord-import-rails4/adapters/postgresql_adapter.rb +7 -0
  35. data/lib/activerecord-import-rails4/adapters/sqlite3_adapter.rb +5 -0
  36. data/lib/activerecord-import-rails4/base.rb +34 -0
  37. data/lib/activerecord-import-rails4/import.rb +387 -0
  38. data/lib/activerecord-import-rails4/mysql.rb +8 -0
  39. data/lib/activerecord-import-rails4/mysql2.rb +8 -0
  40. data/lib/activerecord-import-rails4/postgresql.rb +8 -0
  41. data/lib/activerecord-import-rails4/sqlite3.rb +8 -0
  42. data/lib/activerecord-import-rails4/synchronize.rb +60 -0
  43. data/lib/activerecord-import-rails4/version.rb +5 -0
  44. data/test/active_record/connection_adapter_test.rb +62 -0
  45. data/test/adapters/jdbcmysql.rb +1 -0
  46. data/test/adapters/mysql.rb +1 -0
  47. data/test/adapters/mysql2.rb +1 -0
  48. data/test/adapters/mysql2spatial.rb +1 -0
  49. data/test/adapters/mysqlspatial.rb +1 -0
  50. data/test/adapters/postgis.rb +1 -0
  51. data/test/adapters/postgresql.rb +1 -0
  52. data/test/adapters/seamless_database_pool.rb +1 -0
  53. data/test/adapters/spatialite.rb +1 -0
  54. data/test/adapters/sqlite3.rb +1 -0
  55. data/test/import_test.rb +321 -0
  56. data/test/jdbcmysql/import_test.rb +6 -0
  57. data/test/models/book.rb +3 -0
  58. data/test/models/group.rb +3 -0
  59. data/test/models/topic.rb +7 -0
  60. data/test/models/widget.rb +3 -0
  61. data/test/mysql/import_test.rb +6 -0
  62. data/test/mysql2/import_test.rb +6 -0
  63. data/test/mysqlspatial/import_test.rb +6 -0
  64. data/test/mysqlspatial2/import_test.rb +6 -0
  65. data/test/postgis/import_test.rb +4 -0
  66. data/test/postgresql/import_test.rb +4 -0
  67. data/test/schema/generic_schema.rb +102 -0
  68. data/test/schema/mysql_schema.rb +17 -0
  69. data/test/schema/version.rb +10 -0
  70. data/test/support/active_support/test_case_extensions.rb +67 -0
  71. data/test/support/factories.rb +19 -0
  72. data/test/support/generate.rb +29 -0
  73. data/test/support/mysql/assertions.rb +55 -0
  74. data/test/support/mysql/import_examples.rb +190 -0
  75. data/test/support/postgresql/import_examples.rb +21 -0
  76. data/test/synchronize_test.rb +22 -0
  77. data/test/test_helper.rb +48 -0
  78. metadata +197 -0
@@ -0,0 +1,6 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+
3
+ require File.expand_path(File.dirname(__FILE__) + '/../support/mysql/assertions')
4
+ require File.expand_path(File.dirname(__FILE__) + '/../support/mysql/import_examples')
5
+
6
+ should_support_mysql_import_functionality
@@ -0,0 +1,3 @@
1
+ class Book < ActiveRecord::Base
2
+ belongs_to :topic
3
+ end
@@ -0,0 +1,3 @@
1
+ class Group < ActiveRecord::Base
2
+ self.table_name = 'group'
3
+ end
@@ -0,0 +1,7 @@
1
+ class Topic < ActiveRecord::Base
2
+ validates_presence_of :author_name
3
+ has_many :books
4
+ belongs_to :parent, :class_name => "Topic"
5
+
6
+ composed_of :description, :mapping => [ %w(title title), %w(author_name author_name)], :allow_nil => true, :class_name => "TopicDescription"
7
+ end
@@ -0,0 +1,3 @@
1
+ class Widget < ActiveRecord::Base
2
+ self.primary_key = :w_id
3
+ end
@@ -0,0 +1,6 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+
3
+ require File.expand_path(File.dirname(__FILE__) + '/../support/mysql/assertions')
4
+ require File.expand_path(File.dirname(__FILE__) + '/../support/mysql/import_examples')
5
+
6
+ should_support_mysql_import_functionality
@@ -0,0 +1,6 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+
3
+ require File.expand_path(File.dirname(__FILE__) + '/../support/mysql/assertions')
4
+ require File.expand_path(File.dirname(__FILE__) + '/../support/mysql/import_examples')
5
+
6
+ should_support_mysql_import_functionality
@@ -0,0 +1,6 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+
3
+ require File.expand_path(File.dirname(__FILE__) + '/../support/mysql/assertions')
4
+ require File.expand_path(File.dirname(__FILE__) + '/../support/mysql/import_examples')
5
+
6
+ should_support_mysql_import_functionality
@@ -0,0 +1,6 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+
3
+ require File.expand_path(File.dirname(__FILE__) + '/../support/mysql/assertions')
4
+ require File.expand_path(File.dirname(__FILE__) + '/../support/mysql/import_examples')
5
+
6
+ should_support_mysql_import_functionality
@@ -0,0 +1,4 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+ require File.expand_path(File.dirname(__FILE__) + '/../support/postgresql/import_examples')
3
+
4
+ should_support_postgresql_import_functionality
@@ -0,0 +1,4 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+ require File.expand_path(File.dirname(__FILE__) + '/../support/postgresql/import_examples')
3
+
4
+ should_support_postgresql_import_functionality
@@ -0,0 +1,102 @@
1
+ ActiveRecord::Schema.define do
2
+
3
+ create_table :schema_info, :force=>true do |t|
4
+ t.column :version, :integer, :unique=>true
5
+ end
6
+ SchemaInfo.create :version=>SchemaInfo::VERSION
7
+
8
+ create_table :group, :force => true do |t|
9
+ t.column :order, :string
10
+ t.timestamps
11
+ end
12
+
13
+ create_table :topics, :force=>true do |t|
14
+ t.column :title, :string, :null => false
15
+ t.column :author_name, :string
16
+ t.column :author_email_address, :string
17
+ t.column :written_on, :datetime
18
+ t.column :bonus_time, :time
19
+ t.column :last_read, :datetime
20
+ t.column :content, :text
21
+ t.column :approved, :boolean, :default=>'1'
22
+ t.column :replies_count, :integer
23
+ t.column :parent_id, :integer
24
+ t.column :type, :string
25
+ t.column :created_at, :datetime
26
+ t.column :created_on, :datetime
27
+ t.column :updated_at, :datetime
28
+ t.column :updated_on, :datetime
29
+ end
30
+
31
+ create_table :projects, :force=>true do |t|
32
+ t.column :name, :string
33
+ t.column :type, :string
34
+ end
35
+
36
+ create_table :developers, :force=>true do |t|
37
+ t.column :name, :string
38
+ t.column :salary, :integer, :default=>'70000'
39
+ t.column :created_at, :datetime
40
+ t.column :team_id, :integer
41
+ t.column :updated_at, :datetime
42
+ end
43
+
44
+ create_table :addresses, :force=>true do |t|
45
+ t.column :address, :string
46
+ t.column :city, :string
47
+ t.column :state, :string
48
+ t.column :zip, :string
49
+ t.column :developer_id, :integer
50
+ end
51
+
52
+ create_table :teams, :force=>true do |t|
53
+ t.column :name, :string
54
+ end
55
+
56
+ create_table :books, :force=>true do |t|
57
+ t.column :title, :string, :null=>false
58
+ t.column :publisher, :string, :null=>false, :default => 'Default Publisher'
59
+ t.column :author_name, :string, :null=>false
60
+ t.column :created_at, :datetime
61
+ t.column :created_on, :datetime
62
+ t.column :updated_at, :datetime
63
+ t.column :updated_on, :datetime
64
+ t.column :publish_date, :date
65
+ t.column :topic_id, :integer
66
+ t.column :for_sale, :boolean, :default => true
67
+ end
68
+
69
+ create_table :languages, :force=>true do |t|
70
+ t.column :name, :string
71
+ t.column :developer_id, :integer
72
+ end
73
+
74
+ create_table :shopping_carts, :force=>true do |t|
75
+ t.column :name, :string, :null => true
76
+ t.column :created_at, :datetime
77
+ t.column :updated_at, :datetime
78
+ end
79
+
80
+ create_table :cart_items, :force => true do |t|
81
+ t.column :shopping_cart_id, :string, :null => false
82
+ t.column :book_id, :string, :null => false
83
+ t.column :copies, :integer, :default => 1
84
+ t.column :created_at, :datetime
85
+ t.column :updated_at, :datetime
86
+ end
87
+
88
+ add_index :cart_items, [:shopping_cart_id, :book_id], :unique => true, :name => 'uk_shopping_cart_books'
89
+
90
+ create_table :animals, :force => true do |t|
91
+ t.column :name, :string, :null => false
92
+ t.column :size, :string, :default => nil
93
+ t.column :created_at, :datetime
94
+ t.column :updated_at, :datetime
95
+ end
96
+
97
+ add_index :animals, [:name], :unique => true, :name => 'uk_animals'
98
+
99
+ create_table :widgets, :id => false, :force => true do |t|
100
+ t.integer :w_id
101
+ end
102
+ end
@@ -0,0 +1,17 @@
1
+ ActiveRecord::Schema.define do
2
+
3
+ create_table :books, :options=>'ENGINE=MyISAM', :force=>true do |t|
4
+ t.column :title, :string, :null=>false
5
+ t.column :publisher, :string, :null=>false, :default => 'Default Publisher'
6
+ t.column :author_name, :string, :null=>false
7
+ t.column :created_at, :datetime
8
+ t.column :created_on, :datetime
9
+ t.column :updated_at, :datetime
10
+ t.column :updated_on, :datetime
11
+ t.column :publish_date, :date
12
+ t.column :topic_id, :integer
13
+ t.column :for_sale, :boolean, :default => true
14
+ end
15
+ execute "ALTER TABLE books ADD FULLTEXT( `title`, `publisher`, `author_name` )"
16
+
17
+ end
@@ -0,0 +1,10 @@
1
+ class SchemaInfo < ActiveRecord::Base
2
+ if respond_to?(:table_name=)
3
+ self.table_name = 'schema_info'
4
+ else
5
+ # this is becoming deprecated in ActiveRecord but not all adapters supported it
6
+ # at this time
7
+ set_table_name 'schema_info'
8
+ end
9
+ VERSION = 12
10
+ end
@@ -0,0 +1,67 @@
1
+ class ActiveSupport::TestCase
2
+ include ActiveRecord::TestFixtures
3
+ self.use_transactional_fixtures = true
4
+
5
+ class << self
6
+ def assertion(name, &block)
7
+ mc = class << self ; self ; end
8
+ mc.class_eval do
9
+ define_method(name) do
10
+ it(name, &block)
11
+ end
12
+ end
13
+ end
14
+
15
+ def asssertion_group(name, &block)
16
+ mc = class << self ; self ; end
17
+ mc.class_eval do
18
+ define_method(name, &block)
19
+ end
20
+ end
21
+
22
+ def macro(name, &block)
23
+ class_eval do
24
+ define_method(name, &block)
25
+ end
26
+ end
27
+
28
+ def describe(description, toplevel=nil, &blk)
29
+ text = toplevel ? description : "#{name} #{description}"
30
+ klass = Class.new(self)
31
+
32
+ klass.class_eval <<-RUBY_EVAL
33
+ def self.name
34
+ "#{text}"
35
+ end
36
+ RUBY_EVAL
37
+
38
+ # do not inherit test methods from the superclass
39
+ klass.class_eval do
40
+ instance_methods.grep(/^test.+/) do |method|
41
+ undef_method method
42
+ end
43
+ end
44
+
45
+ klass.instance_eval &blk
46
+ end
47
+ alias_method :context, :describe
48
+
49
+ def let(name, &blk)
50
+ values = {}
51
+ define_method(name) do
52
+ return values[name] if values.has_key?(name)
53
+ values[name] = instance_eval(&blk)
54
+ end
55
+ end
56
+
57
+ def it(description, &blk)
58
+ define_method("test: #{name} #{description}", &blk)
59
+ end
60
+ end
61
+
62
+ end
63
+
64
+ def describe(description, &blk)
65
+ ActiveSupport::TestCase.describe(description, true, &blk)
66
+ end
67
+
@@ -0,0 +1,19 @@
1
+ FactoryGirl.define do
2
+ factory :group do
3
+ sequence(:order) { |n| "Order #{n}" }
4
+ end
5
+
6
+ factory :invalid_topic, :class => "Topic" do
7
+ sequence(:title){ |n| "Title #{n}"}
8
+ author_name nil
9
+ end
10
+
11
+ factory :topic do
12
+ sequence(:title){ |n| "Title #{n}"}
13
+ sequence(:author_name){ |n| "Author #{n}"}
14
+ end
15
+
16
+ factory :widget do
17
+ sequence(:w_id){ |n| n}
18
+ end
19
+ end
@@ -0,0 +1,29 @@
1
+ class ActiveSupport::TestCase
2
+ def Build(*args)
3
+ n = args.shift if args.first.is_a?(Numeric)
4
+ factory = args.shift
5
+ factory_girl_args = args.shift || {}
6
+
7
+ if n
8
+ Array.new.tap do |collection|
9
+ n.times.each { collection << FactoryGirl.build(factory.to_s.singularize.to_sym, factory_girl_args) }
10
+ end
11
+ else
12
+ FactoryGirl.build(factory.to_s.singularize.to_sym, factory_girl_args)
13
+ end
14
+ end
15
+
16
+ def Generate(*args)
17
+ n = args.shift if args.first.is_a?(Numeric)
18
+ factory = args.shift
19
+ factory_girl_args = args.shift || {}
20
+
21
+ if n
22
+ Array.new.tap do |collection|
23
+ n.times.each { collection << FactoryGirl.create(factory.to_s.singularize.to_sym, factory_girl_args) }
24
+ end
25
+ else
26
+ FactoryGirl.create(factory.to_s.singularize.to_sym, factory_girl_args)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,55 @@
1
+ class ActiveSupport::TestCase
2
+ module MySQLAssertions
3
+ def self.extended(klass)
4
+ klass.instance_eval do
5
+ assertion(:should_not_update_created_at_on_timestamp_columns) do
6
+ Delorean.time_travel_to("5 minutes from now") do
7
+ perform_import
8
+ assert_equal @topic.created_at.to_i, updated_topic.created_at.to_i
9
+ assert_equal @topic.created_on.to_i, updated_topic.created_on.to_i
10
+ end
11
+ end
12
+
13
+ assertion(:should_update_updated_at_on_timestamp_columns) do
14
+ time = Chronic.parse("5 minutes from now")
15
+ Delorean.time_travel_to(time) do
16
+ perform_import
17
+ assert_equal time.to_i, updated_topic.updated_at.to_i
18
+ assert_equal time.to_i, updated_topic.updated_on.to_i
19
+ end
20
+ end
21
+
22
+ assertion(:should_not_update_timestamps) do
23
+ Delorean.time_travel_to("5 minutes from now") do
24
+ perform_import :timestamps => false
25
+ assert_equal @topic.created_at.to_i, updated_topic.created_at.to_i
26
+ assert_equal @topic.created_on.to_i, updated_topic.created_on.to_i
27
+ assert_equal @topic.updated_at.to_i, updated_topic.updated_at.to_i
28
+ assert_equal @topic.updated_on.to_i, updated_topic.updated_on.to_i
29
+ end
30
+ end
31
+
32
+ assertion(:should_not_update_fields_not_mentioned) do
33
+ assert_equal "John Doe", updated_topic.author_name
34
+ end
35
+
36
+ assertion(:should_update_fields_mentioned) do
37
+ perform_import
38
+ assert_equal "Book - 2nd Edition", updated_topic.title
39
+ assert_equal "johndoe@example.com", updated_topic.author_email_address
40
+ end
41
+
42
+ assertion(:should_update_fields_mentioned_with_hash_mappings) do
43
+ perform_import
44
+ assert_equal "johndoe@example.com", updated_topic.title
45
+ assert_equal "Book - 2nd Edition", updated_topic.author_email_address
46
+ end
47
+
48
+ assertion(:should_update_foreign_keys) do
49
+ perform_import
50
+ assert_equal 57, updated_topic.parent_id
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,190 @@
1
+ # encoding: UTF-8
2
+ def should_support_mysql_import_functionality
3
+
4
+ describe "building insert value sets" do
5
+ it "should properly build insert value set based on max packet allowed" do
6
+ values = [
7
+ "('1','2','3')",
8
+ "('4','5','6')",
9
+ "('7','8','9')" ]
10
+
11
+ adapter = ActiveRecord::Base.connection.class
12
+ values_size_in_bytes = values.sum {|value| value.bytesize }
13
+ base_sql_size_in_bytes = 15
14
+ max_bytes = 30
15
+
16
+ value_sets = adapter.get_insert_value_sets( values, base_sql_size_in_bytes, max_bytes )
17
+ assert_equal 3, value_sets.size, 'Three value sets were expected!'
18
+
19
+ # Each element in the value_sets array must be an array
20
+ value_sets.each_with_index { |e,i|
21
+ assert_kind_of Array, e, "Element #{i} was expected to be an Array!" }
22
+
23
+ # Each element in the values array should have a 1:1 correlation to the elements
24
+ # in the returned value_sets arrays
25
+ assert_equal values[0], value_sets[0].first
26
+ assert_equal values[1], value_sets[1].first
27
+ assert_equal values[2], value_sets[2].first
28
+ end
29
+
30
+ context "data contains multi-byte chars" do
31
+ it "should properly build insert value set based on max packet allowed" do
32
+ # each accented e should be 2 bytes, so each entry is 6 bytes instead of 5
33
+ values = [
34
+ "('é')",
35
+ "('é')" ]
36
+
37
+ adapter = ActiveRecord::Base.connection.class
38
+ base_sql_size_in_bytes = 15
39
+ max_bytes = 26
40
+
41
+ values_size_in_bytes = values.sum {|value| value.bytesize }
42
+ value_sets = adapter.get_insert_value_sets( values, base_sql_size_in_bytes, max_bytes )
43
+
44
+ assert_equal 2, value_sets.size, 'Two value sets were expected!'
45
+ end
46
+ end
47
+ end
48
+
49
+ describe "#import with :on_duplicate_key_update option (mysql specific functionality)" do
50
+ extend ActiveSupport::TestCase::MySQLAssertions
51
+
52
+ asssertion_group(:should_support_on_duplicate_key_update) do
53
+ should_not_update_fields_not_mentioned
54
+ should_update_foreign_keys
55
+ should_not_update_created_at_on_timestamp_columns
56
+ should_update_updated_at_on_timestamp_columns
57
+ end
58
+
59
+ macro(:perform_import){ raise "supply your own #perform_import in a context below" }
60
+ macro(:updated_topic){ Topic.find(@topic) }
61
+
62
+ context "given columns and values with :validation checks turned off" do
63
+ let(:columns){ %w( id title author_name author_email_address parent_id ) }
64
+ let(:values){ [ [ 99, "Book", "John Doe", "john@doe.com", 17 ] ] }
65
+ let(:updated_values){ [ [ 99, "Book - 2nd Edition", "Author Should Not Change", "johndoe@example.com", 57 ] ] }
66
+
67
+ macro(:perform_import) do |*opts|
68
+ Topic.import columns, updated_values, opts.extract_options!.merge(:on_duplicate_key_update => update_columns, :validate => false)
69
+ end
70
+
71
+ setup do
72
+ Topic.import columns, values, :validate => false
73
+ @topic = Topic.find 99
74
+ end
75
+
76
+ context "using string column names" do
77
+ let(:update_columns){ [ "title", "author_email_address", "parent_id" ] }
78
+ should_support_on_duplicate_key_update
79
+ should_update_fields_mentioned
80
+ end
81
+
82
+ context "using symbol column names" do
83
+ let(:update_columns){ [ :title, :author_email_address, :parent_id ] }
84
+ should_support_on_duplicate_key_update
85
+ should_update_fields_mentioned
86
+ end
87
+
88
+ context "using string hash map" do
89
+ let(:update_columns){ { "title" => "title", "author_email_address" => "author_email_address", "parent_id" => "parent_id" } }
90
+ should_support_on_duplicate_key_update
91
+ should_update_fields_mentioned
92
+ end
93
+
94
+ context "using string hash map, but specifying column mismatches" do
95
+ let(:update_columns){ { "title" => "author_email_address", "author_email_address" => "title", "parent_id" => "parent_id" } }
96
+ should_support_on_duplicate_key_update
97
+ should_update_fields_mentioned_with_hash_mappings
98
+ end
99
+
100
+ context "using symbol hash map" do
101
+ let(:update_columns){ { :title => :title, :author_email_address => :author_email_address, :parent_id => :parent_id } }
102
+ should_support_on_duplicate_key_update
103
+ should_update_fields_mentioned
104
+ end
105
+
106
+ context "using symbol hash map, but specifying column mismatches" do
107
+ let(:update_columns){ { :title => :author_email_address, :author_email_address => :title, :parent_id => :parent_id } }
108
+ should_support_on_duplicate_key_update
109
+ should_update_fields_mentioned_with_hash_mappings
110
+ end
111
+ end
112
+
113
+ context "given array of model instances with :validation checks turned off" do
114
+ macro(:perform_import) do |*opts|
115
+ @topic.title = "Book - 2nd Edition"
116
+ @topic.author_name = "Author Should Not Change"
117
+ @topic.author_email_address = "johndoe@example.com"
118
+ @topic.parent_id = 57
119
+ Topic.import [@topic], opts.extract_options!.merge(:on_duplicate_key_update => update_columns, :validate => false)
120
+ end
121
+
122
+ setup do
123
+ @topic = Generate(:topic, :id => 99, :author_name => "John Doe", :parent_id => 17)
124
+ end
125
+
126
+ context "using string column names" do
127
+ let(:update_columns){ [ "title", "author_email_address", "parent_id" ] }
128
+ should_support_on_duplicate_key_update
129
+ should_update_fields_mentioned
130
+ end
131
+
132
+ context "using symbol column names" do
133
+ let(:update_columns){ [ :title, :author_email_address, :parent_id ] }
134
+ should_support_on_duplicate_key_update
135
+ should_update_fields_mentioned
136
+ end
137
+
138
+ context "using string hash map" do
139
+ let(:update_columns){ { "title" => "title", "author_email_address" => "author_email_address", "parent_id" => "parent_id" } }
140
+ should_support_on_duplicate_key_update
141
+ should_update_fields_mentioned
142
+ end
143
+
144
+ context "using string hash map, but specifying column mismatches" do
145
+ let(:update_columns){ { "title" => "author_email_address", "author_email_address" => "title", "parent_id" => "parent_id" } }
146
+ should_support_on_duplicate_key_update
147
+ should_update_fields_mentioned_with_hash_mappings
148
+ end
149
+
150
+ context "using symbol hash map" do
151
+ let(:update_columns){ { :title => :title, :author_email_address => :author_email_address, :parent_id => :parent_id } }
152
+ should_support_on_duplicate_key_update
153
+ should_update_fields_mentioned
154
+ end
155
+
156
+ context "using symbol hash map, but specifying column mismatches" do
157
+ let(:update_columns){ { :title => :author_email_address, :author_email_address => :title, :parent_id => :parent_id } }
158
+ should_support_on_duplicate_key_update
159
+ should_update_fields_mentioned_with_hash_mappings
160
+ end
161
+ end
162
+
163
+ end
164
+
165
+ describe "#import with :synchronization option" do
166
+ let(:topics){ Array.new }
167
+ let(:values){ [ [topics.first.id, "Jerry Carter"], [topics.last.id, "Chad Fowler"] ]}
168
+ let(:columns){ %W(id author_name) }
169
+
170
+ setup do
171
+ topics << Topic.create!(:title=>"LDAP", :author_name=>"Big Bird")
172
+ topics << Topic.create!(:title=>"Rails Recipes", :author_name=>"Elmo")
173
+ end
174
+
175
+ it "synchronizes passed in ActiveRecord model instances with the data just imported" do
176
+ columns2update = [ 'author_name' ]
177
+
178
+ expected_count = Topic.count
179
+ Topic.import( columns, values,
180
+ :validate=>false,
181
+ :on_duplicate_key_update=>columns2update,
182
+ :synchronize=>topics )
183
+
184
+ assert_equal expected_count, Topic.count, "no new records should have been created!"
185
+ assert_equal "Jerry Carter", topics.first.author_name, "wrong author!"
186
+ assert_equal "Chad Fowler", topics.last.author_name, "wrong author!"
187
+ end
188
+ end
189
+
190
+ end