activerecord-import 0.11.0 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +24 -0
  3. data/Gemfile +4 -2
  4. data/README.markdown +8 -7
  5. data/Rakefile +1 -1
  6. data/benchmarks/benchmark.rb +2 -1
  7. data/benchmarks/lib/cli_parser.rb +1 -1
  8. data/benchmarks/lib/{mysql_benchmark.rb → mysql2_benchmark.rb} +6 -7
  9. data/gemfiles/3.1.gemfile +0 -2
  10. data/gemfiles/3.2.gemfile +0 -2
  11. data/gemfiles/4.0.gemfile +0 -2
  12. data/gemfiles/4.1.gemfile +0 -2
  13. data/gemfiles/4.2.gemfile +0 -2
  14. data/gemfiles/5.0.gemfile +0 -2
  15. data/lib/activerecord-import/adapters/abstract_adapter.rb +11 -2
  16. data/lib/activerecord-import/adapters/mysql_adapter.rb +14 -2
  17. data/lib/activerecord-import/adapters/postgresql_adapter.rb +105 -1
  18. data/lib/activerecord-import/base.rb +0 -1
  19. data/lib/activerecord-import/import.rb +92 -21
  20. data/lib/activerecord-import/version.rb +1 -1
  21. data/test/database.yml.sample +0 -12
  22. data/test/import_test.rb +1 -1
  23. data/test/jdbcmysql/import_test.rb +2 -2
  24. data/test/jdbcpostgresql/import_test.rb +0 -1
  25. data/test/models/book.rb +4 -4
  26. data/test/models/promotion.rb +3 -0
  27. data/test/models/question.rb +3 -0
  28. data/test/models/rule.rb +3 -0
  29. data/test/models/topic.rb +1 -1
  30. data/test/mysql2/import_test.rb +2 -3
  31. data/test/mysqlspatial2/import_test.rb +2 -2
  32. data/test/postgresql/import_test.rb +4 -0
  33. data/test/schema/generic_schema.rb +19 -2
  34. data/test/support/{mysql/assertions.rb → assertions.rb} +12 -3
  35. data/test/support/factories.rb +14 -0
  36. data/test/support/mysql/import_examples.rb +28 -119
  37. data/test/support/postgresql/import_examples.rb +156 -1
  38. data/test/support/shared_examples/on_duplicate_key_update.rb +92 -0
  39. data/test/test_helper.rb +5 -1
  40. data/test/travis/build.sh +12 -8
  41. data/test/travis/database.yml +0 -12
  42. metadata +14 -23
  43. data/lib/activerecord-import/active_record/adapters/em_mysql2_adapter.rb +0 -8
  44. data/lib/activerecord-import/active_record/adapters/mysql_adapter.rb +0 -6
  45. data/lib/activerecord-import/em_mysql2.rb +0 -7
  46. data/lib/activerecord-import/mysql.rb +0 -7
  47. data/test/adapters/em_mysql2.rb +0 -1
  48. data/test/adapters/mysql.rb +0 -1
  49. data/test/adapters/mysqlspatial.rb +0 -1
  50. data/test/em_mysql2/import_test.rb +0 -6
  51. data/test/mysql/import_test.rb +0 -6
  52. data/test/mysqlspatial/import_test.rb +0 -6
  53. data/test/support/em-synchrony_extensions.rb +0 -13
@@ -1,5 +1,5 @@
1
1
  module ActiveRecord
2
2
  module Import
3
- VERSION = "0.11.0"
3
+ VERSION = "0.12.0"
4
4
  end
5
5
  end
@@ -5,25 +5,13 @@ common: &common
5
5
  host: localhost
6
6
  database: activerecord_import_test
7
7
 
8
- mysql: &mysql
9
- <<: *common
10
- adapter: mysql
11
-
12
8
  mysql2: &mysql2
13
9
  <<: *common
14
10
  adapter: mysql2
15
11
 
16
- mysqlspatial:
17
- <<: *mysql
18
-
19
12
  mysql2spatial:
20
13
  <<: *mysql2
21
14
 
22
- em_mysql2:
23
- <<: *common
24
- adapter: em_mysql2
25
- pool: 5
26
-
27
15
  seamless_database_pool:
28
16
  <<: *common
29
17
  adapter: seamless_database_pool
@@ -404,7 +404,7 @@ describe "#import" do
404
404
  end
405
405
  end
406
406
 
407
- if ENV['AR_VERSION'].to_i > 4.1
407
+ if ENV['AR_VERSION'].to_f > 4.1
408
408
  it 'should be able to import enum fields by name' do
409
409
  Book.delete_all if Book.count > 0
410
410
  books = [
@@ -1,6 +1,6 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
2
 
3
- require File.expand_path(File.dirname(__FILE__) + '/../support/mysql/assertions')
3
+ require File.expand_path(File.dirname(__FILE__) + '/../support/assertions')
4
4
  require File.expand_path(File.dirname(__FILE__) + '/../support/mysql/import_examples')
5
5
 
6
- should_support_mysql_import_functionality
6
+ should_support_mysql_import_functionality
@@ -1,6 +1,5 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
2
 
3
- #require File.expand_path(File.dirname(__FILE__) + '/../support/postgresql/assertions')
4
3
  require File.expand_path(File.dirname(__FILE__) + '/../support/postgresql/import_examples')
5
4
 
6
5
  should_support_postgresql_import_functionality
@@ -1,8 +1,8 @@
1
1
  class Book < ActiveRecord::Base
2
- belongs_to :topic, :inverse_of=>:books
3
- has_many :chapters, :autosave => true, :inverse_of => :book
4
- has_many :end_notes, :autosave => true, :inverse_of => :book
5
- if ENV['AR_VERSION'].to_i >= 4.1
2
+ belongs_to :topic, :inverse_of => :books
3
+ has_many :chapters, :inverse_of => :book
4
+ has_many :end_notes, :inverse_of => :book
5
+ if ENV['AR_VERSION'].to_f >= 4.1
6
6
  enum status: [:draft, :published]
7
7
  end
8
8
  end
@@ -0,0 +1,3 @@
1
+ class Promotion < ActiveRecord::Base
2
+ self.primary_key = :promotion_id
3
+ end
@@ -0,0 +1,3 @@
1
+ class Question < ActiveRecord::Base
2
+ has_one :rule
3
+ end
@@ -0,0 +1,3 @@
1
+ class Rule < ActiveRecord::Base
2
+ belongs_to :question
3
+ end
@@ -2,7 +2,7 @@ class Topic < ActiveRecord::Base
2
2
  validates_presence_of :author_name
3
3
  validates :title, numericality: { only_integer: true }, on: :context_test
4
4
 
5
- has_many :books, :autosave=>true, :inverse_of=>:topic
5
+ has_many :books, :inverse_of => :topic
6
6
  belongs_to :parent, :class_name => "Topic"
7
7
 
8
8
  composed_of :description, :mapping => [ %w(title title), %w(author_name author_name)], :allow_nil => true, :class_name => "TopicDescription"
@@ -1,6 +1,5 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
-
3
- require File.expand_path(File.dirname(__FILE__) + '/../support/mysql/assertions')
2
+ require File.expand_path(File.dirname(__FILE__) + '/../support/assertions')
4
3
  require File.expand_path(File.dirname(__FILE__) + '/../support/mysql/import_examples')
5
4
 
6
- should_support_mysql_import_functionality
5
+ should_support_mysql_import_functionality
@@ -1,6 +1,6 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
2
 
3
- require File.expand_path(File.dirname(__FILE__) + '/../support/mysql/assertions')
3
+ require File.expand_path(File.dirname(__FILE__) + '/../support/assertions')
4
4
  require File.expand_path(File.dirname(__FILE__) + '/../support/mysql/import_examples')
5
5
 
6
- should_support_mysql_import_functionality
6
+ should_support_mysql_import_functionality
@@ -2,3 +2,7 @@ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
2
  require File.expand_path(File.dirname(__FILE__) + '/../support/postgresql/import_examples')
3
3
 
4
4
  should_support_postgresql_import_functionality
5
+
6
+ if ActiveRecord::Base.connection.supports_on_duplicate_key_update?
7
+ should_support_postgresql_upsert_functionality
8
+ end
@@ -7,7 +7,7 @@ ActiveRecord::Schema.define do
7
7
 
8
8
  create_table :group, :force => true do |t|
9
9
  t.column :order, :string
10
- t.timestamps
10
+ t.timestamps null: true
11
11
  end
12
12
 
13
13
  create_table :topics, :force=>true do |t|
@@ -64,7 +64,7 @@ ActiveRecord::Schema.define do
64
64
  t.column :publish_date, :date
65
65
  t.column :topic_id, :integer
66
66
  t.column :for_sale, :boolean, :default => true
67
- t.column :status, :integer
67
+ t.column :status, :integer, :default => 0
68
68
  end
69
69
 
70
70
  create_table :chapters, :force => true do |t|
@@ -118,4 +118,21 @@ ActiveRecord::Schema.define do
118
118
  t.text :data
119
119
  t.text :json_data
120
120
  end
121
+
122
+ create_table "promotions", primary_key: "promotion_id", force: :cascade do |t|
123
+ t.string :code
124
+ t.string :description
125
+ t.decimal :discount
126
+ end
127
+
128
+ add_index :promotions, [:code], :unique => true, :name => 'uk_code'
129
+
130
+ create_table :rules, force: true do |t|
131
+ t.column :condition_text, :string
132
+ t.column :question_id, :integer
133
+ end
134
+
135
+ create_table :questions, force: true do |t|
136
+ t.column :body, :string
137
+ end
121
138
  end
@@ -1,5 +1,5 @@
1
1
  class ActiveSupport::TestCase
2
- module MySQLAssertions
2
+ module ImportAssertions
3
3
  def self.extended(klass)
4
4
  klass.instance_eval do
5
5
  assertion(:should_not_update_created_at_on_timestamp_columns) do
@@ -19,6 +19,15 @@ class ActiveSupport::TestCase
19
19
  end
20
20
  end
21
21
 
22
+ assertion(:should_not_update_updated_at_on_timestamp_columns) do
23
+ time = Chronic.parse("5 minutes from now")
24
+ Timecop.freeze time do
25
+ perform_import
26
+ assert_in_delta @topic.updated_at.to_i, updated_topic.updated_at.to_i, 1
27
+ assert_in_delta @topic.updated_on.to_i, updated_topic.updated_on.to_i, 1
28
+ end
29
+ end
30
+
22
31
  assertion(:should_not_update_timestamps) do
23
32
  Timecop.freeze Chronic.parse("5 minutes from now") do
24
33
  perform_import :timestamps => false
@@ -40,10 +49,10 @@ class ActiveSupport::TestCase
40
49
  end
41
50
 
42
51
  assertion(:should_raise_update_fields_mentioned) do
43
- assert_raise ActiveRecord::RecordNotUnique do
52
+ assert_raise ActiveRecord::RecordNotUnique do
44
53
  perform_import
45
54
  end
46
-
55
+
47
56
  assert_equal "Book", updated_topic.title
48
57
  assert_equal "john@doe.com", updated_topic.author_email_address
49
58
  end
@@ -21,6 +21,20 @@ FactoryGirl.define do
21
21
  sequence(:w_id){ |n| n}
22
22
  end
23
23
 
24
+ factory :question do
25
+ sequence(:body) { |n| "Text #{n}"}
26
+
27
+ trait :with_rule do
28
+ after(:build) do |question|
29
+ question.build_rule(FactoryGirl.attributes_for(:rule))
30
+ end
31
+ end
32
+ end
33
+
34
+ factory :rule do
35
+ sequence(:condition_text){ |n| "q_#{n}_#{n}"}
36
+ end
37
+
24
38
  factory :topic_with_book, :parent=>:topic do |m|
25
39
  after(:build) do |topic|
26
40
  2.times do
@@ -3,29 +3,22 @@ def should_support_mysql_import_functionality
3
3
  # Forcefully disable strict mode for this session.
4
4
  ActiveRecord::Base.connection.execute "set sql_mode='STRICT_ALL_TABLES'"
5
5
 
6
- describe "#import with :on_duplicate_key_update option (mysql specific functionality)" do
7
- extend ActiveSupport::TestCase::MySQLAssertions
8
-
9
- asssertion_group(:should_support_on_duplicate_key_update) do
10
- should_not_update_fields_not_mentioned
11
- should_update_foreign_keys
12
- should_not_update_created_at_on_timestamp_columns
13
- should_update_updated_at_on_timestamp_columns
14
- end
6
+ should_support_basic_on_duplicate_key_update
15
7
 
16
- macro(:perform_import){ raise "supply your own #perform_import in a context below" }
17
- macro(:updated_topic){ Topic.find(@topic.id) }
8
+ describe "#import" do
9
+ context "with :on_duplicate_key_update and validation checks turned off" do
10
+ extend ActiveSupport::TestCase::ImportAssertions
18
11
 
19
- describe "argument safety" do
20
- it "should not modify the passed in :on_duplicate_key_update columns array" do
21
- assert_nothing_raised do
22
- columns = %w(title author_name).freeze
23
- Topic.import columns, [["foo", "bar"]], :on_duplicate_key_update => columns
24
- end
12
+ asssertion_group(:should_support_on_duplicate_key_update) do
13
+ should_not_update_fields_not_mentioned
14
+ should_update_foreign_keys
15
+ should_not_update_created_at_on_timestamp_columns
16
+ should_update_updated_at_on_timestamp_columns
25
17
  end
26
- end
27
18
 
28
- context "given columns and values with :validation checks turned off" do
19
+ macro(:perform_import){ raise "supply your own #perform_import in a context below" }
20
+ macro(:updated_topic){ Topic.find(@topic.id) }
21
+
29
22
  let(:columns){ %w( id title author_name author_email_address parent_id ) }
30
23
  let(:values){ [ [ 99, "Book", "John Doe", "john@doe.com", 17 ] ] }
31
24
  let(:updated_values){ [ [ 99, "Book - 2nd Edition", "Author Should Not Change", "johndoe@example.com", 57 ] ] }
@@ -39,18 +32,6 @@ def should_support_mysql_import_functionality
39
32
  @topic = Topic.find 99
40
33
  end
41
34
 
42
- context "using string column names" do
43
- let(:update_columns){ [ "title", "author_email_address", "parent_id" ] }
44
- should_support_on_duplicate_key_update
45
- should_update_fields_mentioned
46
- end
47
-
48
- context "using symbol column names" do
49
- let(:update_columns){ [ :title, :author_email_address, :parent_id ] }
50
- should_support_on_duplicate_key_update
51
- should_update_fields_mentioned
52
- end
53
-
54
35
  context "using string hash map" do
55
36
  let(:update_columns){ { "title" => "title", "author_email_address" => "author_email_address", "parent_id" => "parent_id" } }
56
37
  should_support_on_duplicate_key_update
@@ -76,101 +57,29 @@ def should_support_mysql_import_functionality
76
57
  end
77
58
  end
78
59
 
79
- context "given array of model instances with :validation checks turned off" do
80
- macro(:perform_import) do |*opts|
81
- @topic.title = "Book - 2nd Edition"
82
- @topic.author_name = "Author Should Not Change"
83
- @topic.author_email_address = "johndoe@example.com"
84
- @topic.parent_id = 57
85
- Topic.import [@topic], opts.extract_options!.merge(:on_duplicate_key_update => update_columns, :validate => false)
86
- end
60
+ context "with :synchronization option" do
61
+ let(:topics){ Array.new }
62
+ let(:values){ [ [topics.first.id, "Jerry Carter", "title1"], [topics.last.id, "Chad Fowler", "title2"] ]}
63
+ let(:columns){ %W(id author_name title) }
87
64
 
88
65
  setup do
89
- @topic = Generate(:topic, :id => 99, :author_name => "John Doe", :parent_id => 17)
90
- end
91
-
92
- context "using string column names" do
93
- let(:update_columns){ [ "title", "author_email_address", "parent_id" ] }
94
- should_support_on_duplicate_key_update
95
- should_update_fields_mentioned
96
- end
97
-
98
- context "using symbol column names" do
99
- let(:update_columns){ [ :title, :author_email_address, :parent_id ] }
100
- should_support_on_duplicate_key_update
101
- should_update_fields_mentioned
102
- end
103
-
104
- context "using string hash map" do
105
- let(:update_columns){ { "title" => "title", "author_email_address" => "author_email_address", "parent_id" => "parent_id" } }
106
- should_support_on_duplicate_key_update
107
- should_update_fields_mentioned
108
- end
109
-
110
- context "using string hash map, but specifying column mismatches" do
111
- let(:update_columns){ { "title" => "author_email_address", "author_email_address" => "title", "parent_id" => "parent_id" } }
112
- should_support_on_duplicate_key_update
113
- should_update_fields_mentioned_with_hash_mappings
66
+ topics << Topic.create!(:title=>"LDAP", :author_name=>"Big Bird")
67
+ topics << Topic.create!(:title=>"Rails Recipes", :author_name=>"Elmo")
114
68
  end
115
69
 
116
- context "using symbol hash map" do
117
- let(:update_columns){ { :title => :title, :author_email_address => :author_email_address, :parent_id => :parent_id } }
118
- should_support_on_duplicate_key_update
119
- should_update_fields_mentioned
120
- end
121
-
122
- context "using symbol hash map, but specifying column mismatches" do
123
- let(:update_columns){ { :title => :author_email_address, :author_email_address => :title, :parent_id => :parent_id } }
124
- should_support_on_duplicate_key_update
125
- should_update_fields_mentioned_with_hash_mappings
126
- end
127
- end
128
-
129
- context "given array of model instances with :on_duplicate_key_update turned off" do
130
- let(:columns){ %w( id title author_name author_email_address parent_id ) }
131
- let(:values){ [ [ 100, "Book", "John Doe", "john@doe.com", 17 ] ] }
132
- let(:updated_values){ [ [ 100, "Book - 2nd Edition", "This should raise an exception", "john@nogo.com", 57 ] ] }
70
+ it "synchronizes passed in ActiveRecord model instances with the data just imported" do
71
+ columns2update = [ 'author_name' ]
133
72
 
134
- macro(:perform_import) do |*opts|
135
- # `:on_duplicate_key_update => false` is the tested feature
136
- Topic.import columns, updated_values, opts.extract_options!.merge(:on_duplicate_key_update => false, :validate => false)
137
- end
73
+ expected_count = Topic.count
74
+ Topic.import( columns, values,
75
+ :validate=>false,
76
+ :on_duplicate_key_update=>columns2update,
77
+ :synchronize=>topics )
138
78
 
139
- setup do
140
- Topic.import columns, values, :validate => false
141
- @topic = Topic.find 100
142
- end
143
-
144
- context "using string column names" do
145
- let(:update_columns){ [ "title", "author_email_address", "parent_id" ] }
146
- should_raise_update_fields_mentioned
79
+ assert_equal expected_count, Topic.count, "no new records should have been created!"
80
+ assert_equal "Jerry Carter", topics.first.author_name, "wrong author!"
81
+ assert_equal "Chad Fowler", topics.last.author_name, "wrong author!"
147
82
  end
148
83
  end
149
84
  end
150
-
151
- describe "#import with :synchronization option" do
152
- let(:topics){ Array.new }
153
- let(:values){ [ [topics.first.id, "Jerry Carter", "title1"], [topics.last.id, "Chad Fowler", "title2"] ]}
154
- let(:columns){ %W(id author_name title) }
155
-
156
- setup do
157
- topics << Topic.create!(:title=>"LDAP", :author_name=>"Big Bird")
158
- topics << Topic.create!(:title=>"Rails Recipes", :author_name=>"Elmo")
159
- end
160
-
161
- it "synchronizes passed in ActiveRecord model instances with the data just imported" do
162
- columns2update = [ 'author_name' ]
163
-
164
- expected_count = Topic.count
165
- Topic.import( columns, values,
166
- :validate=>false,
167
- :on_duplicate_key_update=>columns2update,
168
- :synchronize=>topics )
169
-
170
- assert_equal expected_count, Topic.count, "no new records should have been created!"
171
- assert_equal "Jerry Carter", topics.first.author_name, "wrong author!"
172
- assert_equal "Chad Fowler", topics.last.author_name, "wrong author!"
173
- end
174
- end
175
-
176
85
  end
@@ -19,7 +19,6 @@ def should_support_postgresql_import_functionality
19
19
  end
20
20
 
21
21
  describe "importing objects with associations" do
22
-
23
22
  let(:new_topics) { Build(num_topics, :topic_with_book) }
24
23
  let(:new_topics_with_invalid_chapter) {
25
24
  chapter = new_topics.first.books.first.chapters.first
@@ -31,6 +30,8 @@ def should_support_postgresql_import_functionality
31
30
  let(:num_chapters) {18}
32
31
  let(:num_endnotes) {24}
33
32
 
33
+ let(:new_question_with_rule) { FactoryGirl.build :question, :with_rule }
34
+
34
35
  it 'imports top level' do
35
36
  assert_difference "Topic.count", +num_topics do
36
37
  Topic.import new_topics, :recursive => true
@@ -83,6 +84,12 @@ def should_support_postgresql_import_functionality
83
84
  end
84
85
  end
85
86
 
87
+ it 'imports has_one associations' do
88
+ assert_difference 'Rule.count' do
89
+ Question.import [new_question_with_rule], recursive: true
90
+ end
91
+ end
92
+
86
93
  # These models dont validate associated. So we expect that books and topics get inserted, but not chapters
87
94
  # Putting a transaction around everything wouldn't work, so if you want your chapters to prevent topics from
88
95
  # being created, you would need to have validates_associated in your models and insert with validation
@@ -127,3 +134,151 @@ def should_support_postgresql_import_functionality
127
134
  end
128
135
  end
129
136
  end
137
+
138
+ def should_support_postgresql_upsert_functionality
139
+ should_support_basic_on_duplicate_key_update
140
+
141
+ describe "#import" do
142
+ extend ActiveSupport::TestCase::ImportAssertions
143
+
144
+ macro(:perform_import){ raise "supply your own #perform_import in a context below" }
145
+ macro(:updated_topic){ Topic.find(@topic.id) }
146
+
147
+ context "with :on_duplicate_key_ignore and validation checks turned off" do
148
+ let(:columns){ %w( id title author_name author_email_address parent_id ) }
149
+ let(:values){ [ [ 99, "Book", "John Doe", "john@doe.com", 17 ] ] }
150
+ let(:updated_values){ [ [ 99, "Book - 2nd Edition", "Author Should Not Change", "johndoe@example.com", 57 ] ] }
151
+
152
+ macro(:perform_import) do |*opts|
153
+ Topic.import columns, updated_values, opts.extract_options!.merge(:on_duplicate_key_ignore => value, :validate => false)
154
+ end
155
+
156
+ setup do
157
+ Topic.import columns, values, :validate => false
158
+ @topic = Topic.find 99
159
+ end
160
+
161
+ context "using true" do
162
+ let(:value){ true }
163
+ should_not_update_updated_at_on_timestamp_columns
164
+ end
165
+
166
+ context "using hash with :conflict_target" do
167
+ let(:value){ { conflict_target: :id } }
168
+ should_not_update_updated_at_on_timestamp_columns
169
+ end
170
+
171
+ context "using hash with :constraint_target" do
172
+ let(:value){ { constraint_name: :topics_pkey } }
173
+ should_not_update_updated_at_on_timestamp_columns
174
+ end
175
+ end
176
+
177
+ context "with :on_duplicate_key_update and validation checks turned off" do
178
+ asssertion_group(:should_support_on_duplicate_key_update) do
179
+ should_not_update_fields_not_mentioned
180
+ should_update_foreign_keys
181
+ should_not_update_created_at_on_timestamp_columns
182
+ should_update_updated_at_on_timestamp_columns
183
+ end
184
+
185
+ context "using a hash" do
186
+ context "with :columns a hash" do
187
+ let(:columns){ %w( id title author_name author_email_address parent_id ) }
188
+ let(:values){ [ [ 99, "Book", "John Doe", "john@doe.com", 17 ] ] }
189
+ let(:updated_values){ [ [ 99, "Book - 2nd Edition", "Author Should Not Change", "johndoe@example.com", 57 ] ] }
190
+
191
+ macro(:perform_import) do |*opts|
192
+ Topic.import columns, updated_values, opts.extract_options!.merge(:on_duplicate_key_update => { :conflict_target => :id, :columns => update_columns }, :validate => false)
193
+ end
194
+
195
+ setup do
196
+ Topic.import columns, values, :validate => false
197
+ @topic = Topic.find 99
198
+ end
199
+
200
+ context "using string hash map" do
201
+ let(:update_columns){ { "title" => "title", "author_email_address" => "author_email_address", "parent_id" => "parent_id" } }
202
+ should_support_on_duplicate_key_update
203
+ should_update_fields_mentioned
204
+ end
205
+
206
+ context "using string hash map, but specifying column mismatches" do
207
+ let(:update_columns){ { "title" => "author_email_address", "author_email_address" => "title", "parent_id" => "parent_id" } }
208
+ should_support_on_duplicate_key_update
209
+ should_update_fields_mentioned_with_hash_mappings
210
+ end
211
+
212
+ context "using symbol hash map" do
213
+ let(:update_columns){ { :title => :title, :author_email_address => :author_email_address, :parent_id => :parent_id } }
214
+ should_support_on_duplicate_key_update
215
+ should_update_fields_mentioned
216
+ end
217
+
218
+ context "using symbol hash map, but specifying column mismatches" do
219
+ let(:update_columns){ { :title => :author_email_address, :author_email_address => :title, :parent_id => :parent_id } }
220
+ should_support_on_duplicate_key_update
221
+ should_update_fields_mentioned_with_hash_mappings
222
+ end
223
+ end
224
+
225
+ context "with :constraint_name" do
226
+ let(:columns){ %w( id title author_name author_email_address parent_id ) }
227
+ let(:values){ [ [ 100, "Book", "John Doe", "john@doe.com", 17 ] ] }
228
+ let(:updated_values){ [ [ 100, "Book - 2nd Edition", "Author Should Not Change", "johndoe@example.com", 57 ] ] }
229
+
230
+ macro(:perform_import) do |*opts|
231
+ Topic.import columns, updated_values, opts.extract_options!.merge(:on_duplicate_key_update => { :constraint_name => :topics_pkey, :columns => update_columns }, :validate => false)
232
+ end
233
+
234
+ setup do
235
+ Topic.import columns, values, :validate => false
236
+ @topic = Topic.find 100
237
+ end
238
+
239
+ let(:update_columns){ [ :title, :author_email_address, :parent_id ] }
240
+ should_support_on_duplicate_key_update
241
+ should_update_fields_mentioned
242
+ end
243
+
244
+ context "with no :conflict_target or :constraint_name" do
245
+ let(:columns){ %w( id title author_name author_email_address parent_id ) }
246
+ let(:values){ [ [ 100, "Book", "John Doe", "john@doe.com", 17 ] ] }
247
+ let(:updated_values){ [ [ 100, "Book - 2nd Edition", "Author Should Not Change", "johndoe@example.com", 57 ] ] }
248
+
249
+ macro(:perform_import) do |*opts|
250
+ Topic.import columns, updated_values, opts.extract_options!.merge(:on_duplicate_key_update => { :columns => update_columns }, :validate => false)
251
+ end
252
+
253
+ setup do
254
+ Topic.import columns, values, :validate => false
255
+ @topic = Topic.find 100
256
+ end
257
+
258
+ context "default to the primary key" do
259
+ let(:update_columns){ [ :title, :author_email_address, :parent_id ] }
260
+ should_support_on_duplicate_key_update
261
+ should_update_fields_mentioned
262
+ end
263
+ end
264
+
265
+ context "with no :columns" do
266
+ let(:columns){ %w( id title author_name author_email_address ) }
267
+ let(:values){ [ [ 100, "Book", "John Doe", "john@doe.com" ] ] }
268
+ let(:updated_values){ [ [ 100, "Title Should Not Change", "Author Should Not Change", "john@nogo.com" ] ] }
269
+
270
+ macro(:perform_import) do |*opts|
271
+ Topic.import columns, updated_values, opts.extract_options!.merge(:on_duplicate_key_update => { :conflict_target => :id }, :validate => false)
272
+ end
273
+
274
+ setup do
275
+ Topic.import columns, values, :validate => false
276
+ @topic = Topic.find 100
277
+ end
278
+
279
+ should_update_updated_at_on_timestamp_columns
280
+ end
281
+ end
282
+ end
283
+ end
284
+ end