activerecord-import 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +31 -0
  3. data/Brewfile +3 -0
  4. data/Gemfile +39 -0
  5. data/Rakefile +1 -24
  6. data/activerecord-import.gemspec +22 -0
  7. data/benchmarks/README +32 -0
  8. data/benchmarks/benchmark.rb +64 -0
  9. data/benchmarks/boot.rb +18 -0
  10. data/benchmarks/lib/base.rb +137 -0
  11. data/benchmarks/lib/cli_parser.rb +103 -0
  12. data/benchmarks/lib/float.rb +15 -0
  13. data/benchmarks/lib/mysql_benchmark.rb +22 -0
  14. data/benchmarks/lib/output_to_csv.rb +18 -0
  15. data/benchmarks/lib/output_to_html.rb +69 -0
  16. data/benchmarks/models/test_innodb.rb +3 -0
  17. data/benchmarks/models/test_memory.rb +3 -0
  18. data/benchmarks/models/test_myisam.rb +3 -0
  19. data/benchmarks/schema/mysql_schema.rb +16 -0
  20. data/gemfiles/3.1.gemfile +4 -0
  21. data/gemfiles/3.2.gemfile +4 -0
  22. data/gemfiles/4.0.gemfile +4 -0
  23. data/lib/activerecord-import/active_record/adapters/abstract_adapter.rb +0 -1
  24. data/lib/activerecord-import/active_record/adapters/em_mysql2_adapter.rb +8 -0
  25. data/lib/activerecord-import/adapters/abstract_adapter.rb +5 -58
  26. data/lib/activerecord-import/adapters/em_mysql2_adapter.rb +5 -0
  27. data/lib/activerecord-import/adapters/mysql_adapter.rb +50 -3
  28. data/lib/activerecord-import/adapters/sqlite3_adapter.rb +28 -3
  29. data/lib/activerecord-import/base.rb +3 -2
  30. data/lib/activerecord-import/em_mysql2.rb +7 -0
  31. data/lib/activerecord-import/import.rb +29 -29
  32. data/lib/activerecord-import/synchronize.rb +11 -10
  33. data/lib/activerecord-import/value_sets_parser.rb +54 -0
  34. data/lib/activerecord-import/version.rb +5 -0
  35. data/test/adapters/em_mysql2.rb +1 -0
  36. data/test/adapters/jdbcmysql.rb +1 -0
  37. data/test/adapters/mysql.rb +1 -0
  38. data/test/adapters/mysql2.rb +1 -0
  39. data/test/adapters/mysql2spatial.rb +1 -0
  40. data/test/adapters/mysqlspatial.rb +1 -0
  41. data/test/adapters/postgis.rb +1 -0
  42. data/test/adapters/postgresql.rb +1 -0
  43. data/test/adapters/seamless_database_pool.rb +1 -0
  44. data/test/adapters/spatialite.rb +1 -0
  45. data/test/adapters/sqlite3.rb +1 -0
  46. data/test/database.yml.sample +57 -0
  47. data/test/em_mysql2/import_test.rb +6 -0
  48. data/test/import_test.rb +350 -0
  49. data/test/jdbcmysql/import_test.rb +6 -0
  50. data/test/models/book.rb +3 -0
  51. data/test/models/group.rb +3 -0
  52. data/test/models/topic.rb +7 -0
  53. data/test/models/widget.rb +7 -0
  54. data/test/mysql/import_test.rb +6 -0
  55. data/test/mysql2/import_test.rb +6 -0
  56. data/test/mysqlspatial/import_test.rb +6 -0
  57. data/test/mysqlspatial2/import_test.rb +6 -0
  58. data/test/postgis/import_test.rb +4 -0
  59. data/test/postgresql/import_test.rb +4 -0
  60. data/test/schema/generic_schema.rb +104 -0
  61. data/test/schema/mysql_schema.rb +17 -0
  62. data/test/schema/version.rb +10 -0
  63. data/test/sqlite3/import_test.rb +52 -0
  64. data/test/support/active_support/test_case_extensions.rb +67 -0
  65. data/test/support/factories.rb +19 -0
  66. data/test/support/generate.rb +29 -0
  67. data/test/support/mysql/assertions.rb +55 -0
  68. data/test/support/mysql/import_examples.rb +147 -0
  69. data/test/support/postgresql/import_examples.rb +21 -0
  70. data/test/synchronize_test.rb +22 -0
  71. data/test/test_helper.rb +48 -0
  72. data/test/value_sets_bytes_parser_test.rb +96 -0
  73. data/test/value_sets_records_parser_test.rb +32 -0
  74. metadata +120 -58
  75. data/VERSION +0 -1
@@ -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,7 @@
1
+ class Widget < ActiveRecord::Base
2
+ self.primary_key = :w_id
3
+
4
+ default_scope lambda { where(active: true) }
5
+
6
+ serialize :data, Hash
7
+ 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,104 @@
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
+ t.boolean :active, :default => false
102
+ t.text :data
103
+ end
104
+ 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,52 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+
3
+ describe "#supports_imports?" do
4
+ context "and SQLite is 3.7.11 or higher" do
5
+ it "supports import" do
6
+ version = ActiveRecord::ConnectionAdapters::SQLite3Adapter::Version.new("3.7.11")
7
+ assert ActiveRecord::Base.supports_import?(version)
8
+
9
+ version = ActiveRecord::ConnectionAdapters::SQLite3Adapter::Version.new("3.7.12")
10
+ assert ActiveRecord::Base.supports_import?(version)
11
+ end
12
+ end
13
+
14
+ context "and SQLite less than 3.7.11" do
15
+ it "doesn't support import" do
16
+ version = ActiveRecord::ConnectionAdapters::SQLite3Adapter::Version.new("3.7.10")
17
+ assert !ActiveRecord::Base.supports_import?(version)
18
+ end
19
+ end
20
+ end
21
+
22
+ describe "#import" do
23
+ it "imports with a single insert on SQLite 3.7.11 or higher" do
24
+ assert_difference "Topic.count", +507 do
25
+ result = Topic.import Build(7, :topics)
26
+ 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"
27
+ 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"
28
+
29
+ result = Topic.import Build(500, :topics)
30
+ 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"
31
+ 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"
32
+ end
33
+ end
34
+
35
+ it "imports with a two inserts on SQLite 3.7.11 or higher" do
36
+ assert_difference "Topic.count", +501 do
37
+ result = Topic.import Build(501, :topics)
38
+ 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"
39
+ 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"
40
+ end
41
+ end
42
+
43
+ it "imports with a five inserts on SQLite 3.7.11 or higher" do
44
+ assert_difference "Topic.count", +2500 do
45
+ result = Topic.import Build(2500, :topics)
46
+ 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"
47
+ 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"
48
+ end
49
+ end
50
+
51
+ end
52
+
@@ -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,147 @@
1
+ # encoding: UTF-8
2
+ def should_support_mysql_import_functionality
3
+ # Forcefully disable strict mode for this session.
4
+ ActiveRecord::Base.connection.execute "set sql_mode=''"
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
15
+
16
+ macro(:perform_import){ raise "supply your own #perform_import in a context below" }
17
+ macro(:updated_topic){ Topic.find(@topic) }
18
+
19
+ context "given columns and values with :validation checks turned off" do
20
+ let(:columns){ %w( id title author_name author_email_address parent_id ) }
21
+ let(:values){ [ [ 99, "Book", "John Doe", "john@doe.com", 17 ] ] }
22
+ let(:updated_values){ [ [ 99, "Book - 2nd Edition", "Author Should Not Change", "johndoe@example.com", 57 ] ] }
23
+
24
+ macro(:perform_import) do |*opts|
25
+ Topic.import columns, updated_values, opts.extract_options!.merge(:on_duplicate_key_update => update_columns, :validate => false)
26
+ end
27
+
28
+ setup do
29
+ Topic.import columns, values, :validate => false
30
+ @topic = Topic.find 99
31
+ end
32
+
33
+ context "using string column names" do
34
+ let(:update_columns){ [ "title", "author_email_address", "parent_id" ] }
35
+ should_support_on_duplicate_key_update
36
+ should_update_fields_mentioned
37
+ end
38
+
39
+ context "using symbol column names" do
40
+ let(:update_columns){ [ :title, :author_email_address, :parent_id ] }
41
+ should_support_on_duplicate_key_update
42
+ should_update_fields_mentioned
43
+ end
44
+
45
+ context "using string hash map" do
46
+ let(:update_columns){ { "title" => "title", "author_email_address" => "author_email_address", "parent_id" => "parent_id" } }
47
+ should_support_on_duplicate_key_update
48
+ should_update_fields_mentioned
49
+ end
50
+
51
+ context "using string hash map, but specifying column mismatches" do
52
+ let(:update_columns){ { "title" => "author_email_address", "author_email_address" => "title", "parent_id" => "parent_id" } }
53
+ should_support_on_duplicate_key_update
54
+ should_update_fields_mentioned_with_hash_mappings
55
+ end
56
+
57
+ context "using symbol hash map" do
58
+ let(:update_columns){ { :title => :title, :author_email_address => :author_email_address, :parent_id => :parent_id } }
59
+ should_support_on_duplicate_key_update
60
+ should_update_fields_mentioned
61
+ end
62
+
63
+ context "using symbol hash map, but specifying column mismatches" do
64
+ let(:update_columns){ { :title => :author_email_address, :author_email_address => :title, :parent_id => :parent_id } }
65
+ should_support_on_duplicate_key_update
66
+ should_update_fields_mentioned_with_hash_mappings
67
+ end
68
+ end
69
+
70
+ context "given array of model instances with :validation checks turned off" do
71
+ macro(:perform_import) do |*opts|
72
+ @topic.title = "Book - 2nd Edition"
73
+ @topic.author_name = "Author Should Not Change"
74
+ @topic.author_email_address = "johndoe@example.com"
75
+ @topic.parent_id = 57
76
+ Topic.import [@topic], opts.extract_options!.merge(:on_duplicate_key_update => update_columns, :validate => false)
77
+ end
78
+
79
+ setup do
80
+ @topic = Generate(:topic, :id => 99, :author_name => "John Doe", :parent_id => 17)
81
+ end
82
+
83
+ context "using string column names" do
84
+ let(:update_columns){ [ "title", "author_email_address", "parent_id" ] }
85
+ should_support_on_duplicate_key_update
86
+ should_update_fields_mentioned
87
+ end
88
+
89
+ context "using symbol column names" do
90
+ let(:update_columns){ [ :title, :author_email_address, :parent_id ] }
91
+ should_support_on_duplicate_key_update
92
+ should_update_fields_mentioned
93
+ end
94
+
95
+ context "using string hash map" do
96
+ let(:update_columns){ { "title" => "title", "author_email_address" => "author_email_address", "parent_id" => "parent_id" } }
97
+ should_support_on_duplicate_key_update
98
+ should_update_fields_mentioned
99
+ end
100
+
101
+ context "using string hash map, but specifying column mismatches" do
102
+ let(:update_columns){ { "title" => "author_email_address", "author_email_address" => "title", "parent_id" => "parent_id" } }
103
+ should_support_on_duplicate_key_update
104
+ should_update_fields_mentioned_with_hash_mappings
105
+ end
106
+
107
+ context "using symbol hash map" do
108
+ let(:update_columns){ { :title => :title, :author_email_address => :author_email_address, :parent_id => :parent_id } }
109
+ should_support_on_duplicate_key_update
110
+ should_update_fields_mentioned
111
+ end
112
+
113
+ context "using symbol hash map, but specifying column mismatches" do
114
+ let(:update_columns){ { :title => :author_email_address, :author_email_address => :title, :parent_id => :parent_id } }
115
+ should_support_on_duplicate_key_update
116
+ should_update_fields_mentioned_with_hash_mappings
117
+ end
118
+ end
119
+
120
+ end
121
+
122
+ describe "#import with :synchronization option" do
123
+ let(:topics){ Array.new }
124
+ let(:values){ [ [topics.first.id, "Jerry Carter"], [topics.last.id, "Chad Fowler"] ]}
125
+ let(:columns){ %W(id author_name) }
126
+
127
+ setup do
128
+ topics << Topic.create!(:title=>"LDAP", :author_name=>"Big Bird")
129
+ topics << Topic.create!(:title=>"Rails Recipes", :author_name=>"Elmo")
130
+ end
131
+
132
+ it "synchronizes passed in ActiveRecord model instances with the data just imported" do
133
+ columns2update = [ 'author_name' ]
134
+
135
+ expected_count = Topic.count
136
+ Topic.import( columns, values,
137
+ :validate=>false,
138
+ :on_duplicate_key_update=>columns2update,
139
+ :synchronize=>topics )
140
+
141
+ assert_equal expected_count, Topic.count, "no new records should have been created!"
142
+ assert_equal "Jerry Carter", topics.first.author_name, "wrong author!"
143
+ assert_equal "Chad Fowler", topics.last.author_name, "wrong author!"
144
+ end
145
+ end
146
+
147
+ end