activerecord-import 0.11.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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