activerecord-import-rails4 0.5.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 (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,8 @@
1
+ warn <<-MSG
2
+ [DEPRECATION] loading activerecord-import via 'require "activerecord-import/<adapter-name>"'
3
+ is deprecated. Update to autorequire using 'require "activerecord-import"'. See
4
+ http://github.com/zdennis/activerecord-import/wiki/Requiring for more information
5
+ MSG
6
+
7
+ require File.expand_path(File.join(File.dirname(__FILE__), "/../activerecord-import-rails4"))
8
+
@@ -0,0 +1,8 @@
1
+ warn <<-MSG
2
+ [DEPRECATION] loading activerecord-import via 'require "activerecord-import/<adapter-name>"'
3
+ is deprecated. Update to autorequire using 'require "activerecord-import"'. See
4
+ http://github.com/zdennis/activerecord-import/wiki/Requiring for more information
5
+ MSG
6
+
7
+ require File.expand_path(File.join(File.dirname(__FILE__), "/../activerecord-import-rails4"))
8
+
@@ -0,0 +1,60 @@
1
+ module ActiveRecord # :nodoc:
2
+ class Base # :nodoc:
3
+
4
+ # Synchronizes the passed in ActiveRecord instances with data
5
+ # from the database. This is like calling reload on an individual
6
+ # ActiveRecord instance but it is intended for use on multiple instances.
7
+ #
8
+ # This uses one query for all instance updates and then updates existing
9
+ # instances rather sending one query for each instance
10
+ #
11
+ # == Examples
12
+ # # Synchronizing existing models by matching on the primary key field
13
+ # posts = Post.where(author: "Zach").first
14
+ # <.. out of system changes occur to change author name from Zach to Zachary..>
15
+ # Post.synchronize posts
16
+ # posts.first.author # => "Zachary" instead of Zach
17
+ #
18
+ # # Synchronizing using custom key fields
19
+ # posts = Post.where(author: "Zach").first
20
+ # <.. out of system changes occur to change the address of author 'Zach' to 1245 Foo Ln ..>
21
+ # Post.synchronize posts, [:name] # queries on the :name column and not the :id column
22
+ # posts.first.address # => "1245 Foo Ln" instead of whatever it was
23
+ #
24
+ def self.synchronize(instances, keys=[self.primary_key])
25
+ return if instances.empty?
26
+
27
+ conditions = {}
28
+ order = ""
29
+
30
+ key_values = keys.map { |key| instances.map(&"#{key}".to_sym) }
31
+ keys.zip(key_values).each { |key, values| conditions[key] = values }
32
+ order = keys.map{ |key| "#{key} ASC" }.join(",")
33
+
34
+ klass = instances.first.class
35
+
36
+ fresh_instances = klass.where(conditions).order(order)
37
+ instances.each do |instance|
38
+ matched_instance = fresh_instances.detect do |fresh_instance|
39
+ keys.all?{ |key| fresh_instance.send(key) == instance.send(key) }
40
+ end
41
+
42
+ if matched_instance
43
+ instance.clear_aggregation_cache
44
+ instance.clear_association_cache
45
+ instance.instance_variable_set '@attributes', matched_instance.attributes
46
+ instance.instance_variable_set '@attributes_cache', {}
47
+ # Since the instance now accurately reflects the record in
48
+ # the database, ensure that instance.persisted? is true.
49
+ instance.instance_variable_set '@new_record', false
50
+ instance.instance_variable_set '@destroyed', false
51
+ end
52
+ end
53
+ end
54
+
55
+ # See ActiveRecord::ConnectionAdapters::AbstractAdapter.synchronize
56
+ def synchronize(instances, key=[ActiveRecord::Base.primary_key])
57
+ self.class.synchronize(instances, key)
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,5 @@
1
+ module ActiveRecord
2
+ module Import
3
+ VERSION = "0.5.0"
4
+ end
5
+ end
@@ -0,0 +1,62 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+
3
+ describe "ActiveRecord::ConnectionAdapter::AbstractAdapter" do
4
+ context "#get_insert_value_sets - computing insert value sets" do
5
+ let(:adapter){ ActiveRecord::ConnectionAdapters::AbstractAdapter }
6
+ let(:base_sql){ "INSERT INTO atable (a,b,c)" }
7
+ let(:values){ [ "(1,2,3)", "(2,3,4)", "(3,4,5)" ] }
8
+
9
+ context "when the max allowed bytes is 33 and the base SQL is 26 bytes" do
10
+ it "should return 3 value sets when given 3 value sets of 7 bytes a piece" do
11
+ value_sets = adapter.get_insert_value_sets values, base_sql.size, max_allowed_bytes = 33
12
+ assert_equal 3, value_sets.size
13
+ end
14
+ end
15
+
16
+ context "when the max allowed bytes is 40 and the base SQL is 26 bytes" do
17
+ it "should return 3 value sets when given 3 value sets of 7 bytes a piece" do
18
+ value_sets = adapter.get_insert_value_sets values, base_sql.size, max_allowed_bytes = 40
19
+ assert_equal 3, value_sets.size
20
+ end
21
+ end
22
+
23
+ context "when the max allowed bytes is 41 and the base SQL is 26 bytes" do
24
+ it "should return 2 value sets when given 2 value sets of 7 bytes a piece" do
25
+ value_sets = adapter.get_insert_value_sets values, base_sql.size, max_allowed_bytes = 41
26
+ assert_equal 2, value_sets.size
27
+ end
28
+ end
29
+
30
+ context "when the max allowed bytes is 48 and the base SQL is 26 bytes" do
31
+ it "should return 2 value sets when given 2 value sets of 7 bytes a piece" do
32
+ value_sets = adapter.get_insert_value_sets values, base_sql.size, max_allowed_bytes = 48
33
+ assert_equal 2, value_sets.size
34
+ end
35
+ end
36
+
37
+ context "when the max allowed bytes is 49 and the base SQL is 26 bytes" do
38
+ it "should return 1 value sets when given 1 value sets of 7 bytes a piece" do
39
+ value_sets = adapter.get_insert_value_sets values, base_sql.size, max_allowed_bytes = 49
40
+ assert_equal 1, value_sets.size
41
+ end
42
+ end
43
+
44
+ context "when the max allowed bytes is 999999 and the base SQL is 26 bytes" do
45
+ it "should return 1 value sets when given 1 value sets of 7 bytes a piece" do
46
+ value_sets = adapter.get_insert_value_sets values, base_sql.size, max_allowed_bytes = 999999
47
+ assert_equal 1, value_sets.size
48
+ end
49
+ end
50
+ end
51
+
52
+ end
53
+
54
+ describe "ActiveRecord::Import DB-specific adapter class" do
55
+ context "when ActiveRecord::Import is in use" do
56
+ it "should appear in the AR connection adapter class's ancestors" do
57
+ connection = ActiveRecord::Base.connection
58
+ import_class_name = 'ActiveRecord::Import::' + connection.class.name.demodulize
59
+ assert_includes connection.class.ancestors, import_class_name.constantize
60
+ end
61
+ end
62
+ end
@@ -0,0 +1 @@
1
+ ENV["ARE_DB"] = "jdbcmysql"
@@ -0,0 +1 @@
1
+ ENV["ARE_DB"] = "mysql"
@@ -0,0 +1 @@
1
+ ENV["ARE_DB"] = "mysql2"
@@ -0,0 +1 @@
1
+ ENV["ARE_DB"] = "mysql2spatial"
@@ -0,0 +1 @@
1
+ ENV["ARE_DB"] = "mysqlspatial"
@@ -0,0 +1 @@
1
+ ENV["ARE_DB"] = "postgis"
@@ -0,0 +1 @@
1
+ ENV["ARE_DB"] = "postgresql"
@@ -0,0 +1 @@
1
+ ENV["ARE_DB"] = "seamless_database_pool"
@@ -0,0 +1 @@
1
+ ENV["ARE_DB"] = "spatialite"
@@ -0,0 +1 @@
1
+ ENV["ARE_DB"] = "sqlite3"
@@ -0,0 +1,321 @@
1
+ require File.expand_path('../test_helper', __FILE__)
2
+
3
+ describe "#import" do
4
+ it "should return the number of inserts performed" do
5
+ # see ActiveRecord::ConnectionAdapters::AbstractAdapter test for more specifics
6
+ assert_difference "Topic.count", +10 do
7
+ result = Topic.import Build(3, :topics)
8
+ assert result.num_inserts > 0
9
+
10
+ result = Topic.import Build(7, :topics)
11
+ assert result.num_inserts > 0
12
+ end
13
+ end
14
+
15
+ it "should not produce an error when importing empty arrays" do
16
+ assert_nothing_raised do
17
+ Topic.import []
18
+ Topic.import %w(title author_name), []
19
+ end
20
+ end
21
+
22
+ describe "with non-default ActiveRecord models" do
23
+ context "that have a non-standard primary key (that is no sequence)" do
24
+ it "should import models successfully" do
25
+ assert_difference "Widget.count", +3 do
26
+ Widget.import Build(3, :widgets)
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ context "with :validation option" do
33
+ let(:columns) { %w(title author_name) }
34
+ let(:valid_values) { [[ "LDAP", "Jerry Carter"], ["Rails Recipes", "Chad Fowler"]] }
35
+ let(:invalid_values) { [[ "The RSpec Book", ""], ["Agile+UX", ""]] }
36
+
37
+ context "with validation checks turned off" do
38
+ it "should import valid data" do
39
+ assert_difference "Topic.count", +2 do
40
+ result = Topic.import columns, valid_values, :validate => false
41
+ end
42
+ end
43
+
44
+ it "should import invalid data" do
45
+ assert_difference "Topic.count", +2 do
46
+ result = Topic.import columns, invalid_values, :validate => false
47
+ end
48
+ end
49
+
50
+ it 'should raise a specific error if a column does not exist' do
51
+ assert_raises ActiveRecord::Import::MissingColumnError do
52
+ Topic.import ['foo'], [['bar']], :validate => false
53
+ end
54
+ end
55
+ end
56
+
57
+ context "with validation checks turned on" do
58
+ it "should import valid data" do
59
+ assert_difference "Topic.count", +2 do
60
+ result = Topic.import columns, valid_values, :validate => true
61
+ end
62
+ end
63
+
64
+ it "should not import invalid data" do
65
+ assert_no_difference "Topic.count" do
66
+ result = Topic.import columns, invalid_values, :validate => true
67
+ end
68
+ end
69
+
70
+ it "should report the failed instances" do
71
+ results = Topic.import columns, invalid_values, :validate => true
72
+ assert_equal invalid_values.size, results.failed_instances.size
73
+ results.failed_instances.each{ |e| assert_kind_of Topic, e }
74
+ end
75
+
76
+ it "should import valid data when mixed with invalid data" do
77
+ assert_difference "Topic.count", +2 do
78
+ result = Topic.import columns, valid_values + invalid_values, :validate => true
79
+ end
80
+ assert_equal 0, Topic.where(title: invalid_values.map(&:first)).count
81
+ end
82
+ end
83
+ end
84
+
85
+ context "with :all_or_none option" do
86
+ let(:columns) { %w(title author_name) }
87
+ let(:valid_values) { [[ "LDAP", "Jerry Carter"], ["Rails Recipes", "Chad Fowler"]] }
88
+ let(:invalid_values) { [[ "The RSpec Book", ""], ["Agile+UX", ""]] }
89
+ let(:mixed_values) { valid_values + invalid_values }
90
+
91
+ context "with validation checks turned on" do
92
+ it "should import valid data" do
93
+ assert_difference "Topic.count", +2 do
94
+ result = Topic.import columns, valid_values, :all_or_none => true
95
+ end
96
+ end
97
+
98
+ it "should not import invalid data" do
99
+ assert_no_difference "Topic.count" do
100
+ result = Topic.import columns, invalid_values, :all_or_none => true
101
+ end
102
+ end
103
+
104
+ it "should not import valid data when mixed with invalid data" do
105
+ assert_no_difference "Topic.count" do
106
+ result = Topic.import columns, mixed_values, :all_or_none => true
107
+ end
108
+ end
109
+
110
+ it "should report the failed instances" do
111
+ results = Topic.import columns, mixed_values, :all_or_none => true
112
+ assert_equal invalid_values.size, results.failed_instances.size
113
+ results.failed_instances.each { |e| assert_kind_of Topic, e }
114
+ end
115
+
116
+ it "should report the zero inserts" do
117
+ results = Topic.import columns, mixed_values, :all_or_none => true
118
+ assert_equal 0, results.num_inserts
119
+ end
120
+ end
121
+ end
122
+
123
+ context "with :synchronize option" do
124
+ context "synchronizing on new records" do
125
+ let(:new_topics) { Build(3, :topics) }
126
+
127
+ it "doesn't reload any data (doesn't work)" do
128
+ Topic.import new_topics, :synchronize => new_topics
129
+ assert new_topics.all?(&:new_record?), "No record should have been reloaded"
130
+ end
131
+ end
132
+
133
+ context "synchronizing on new records with explicit conditions" do
134
+ let(:new_topics) { Build(3, :topics) }
135
+
136
+ it "reloads data for existing in-memory instances" do
137
+ Topic.import(new_topics, :synchronize => new_topics, :synchronize_keys => [:title] )
138
+ assert new_topics.all?(&:persisted?), "Records should have been reloaded"
139
+ end
140
+ end
141
+
142
+ context "synchronizing on destroyed records with explicit conditions" do
143
+ let(:new_topics) { Generate(3, :topics) }
144
+
145
+ it "reloads data for existing in-memory instances" do
146
+ new_topics.each &:destroy
147
+ Topic.import(new_topics, :synchronize => new_topics, :synchronize_keys => [:title] )
148
+ assert new_topics.all?(&:persisted?), "Records should have been reloaded"
149
+ end
150
+ end
151
+ end
152
+
153
+ context "with an array of unsaved model instances" do
154
+ let(:topic) { Build(:topic, :title => "The RSpec Book", :author_name => "David Chelimsky")}
155
+ let(:topics) { Build(9, :topics) }
156
+ let(:invalid_topics){ Build(7, :invalid_topics)}
157
+
158
+ it "should import records based on those model's attributes" do
159
+ assert_difference "Topic.count", +9 do
160
+ result = Topic.import topics
161
+ end
162
+
163
+ Topic.import [topic]
164
+ assert Topic.where(title: "The RSpec Book", author_name: "David Chelimsky").first
165
+ end
166
+
167
+ it "should not overwrite existing records" do
168
+ topic = Generate(:topic, :title => "foobar")
169
+ assert_no_difference "Topic.count" do
170
+ begin
171
+ Topic.transaction do
172
+ topic.title = "baz"
173
+ Topic.import [topic]
174
+ end
175
+ rescue Exception
176
+ # PostgreSQL raises PgError due to key constraints
177
+ # I don't know why ActiveRecord doesn't catch these. *sigh*
178
+ end
179
+ end
180
+ assert_equal "foobar", topic.reload.title
181
+ end
182
+
183
+ context "with validation checks turned on" do
184
+ it "should import valid models" do
185
+ assert_difference "Topic.count", +9 do
186
+ result = Topic.import topics, :validate => true
187
+ end
188
+ end
189
+
190
+ it "should not import invalid models" do
191
+ assert_no_difference "Topic.count" do
192
+ result = Topic.import invalid_topics, :validate => true
193
+ end
194
+ end
195
+ end
196
+
197
+ context "with validation checks turned off" do
198
+ it "should import invalid models" do
199
+ assert_difference "Topic.count", +7 do
200
+ result = Topic.import invalid_topics, :validate => false
201
+ end
202
+ end
203
+ end
204
+ end
205
+
206
+ context "with an array of columns and an array of unsaved model instances" do
207
+ let(:topics) { Build(2, :topics) }
208
+
209
+ it "should import records populating the supplied columns with the corresponding model instance attributes" do
210
+ assert_difference "Topic.count", +2 do
211
+ result = Topic.import [:author_name, :title], topics
212
+ end
213
+
214
+ # imported topics should be findable by their imported attributes
215
+ assert Topic.where(author_name: topics.first.author_name).first
216
+ assert Topic.where(author_name: topics.last.author_name).first
217
+ end
218
+
219
+ it "should not populate fields for columns not imported" do
220
+ topics.first.author_email_address = "zach.dennis@gmail.com"
221
+ assert_difference "Topic.count", +2 do
222
+ result = Topic.import [:author_name, :title], topics
223
+ end
224
+
225
+ assert !Topic.where(author_email_address: "zach.dennis@gmail.com").first
226
+ end
227
+ end
228
+
229
+ context "with an array of columns and an array of values" do
230
+ it "should import ids when specified" do
231
+ Topic.import [:id, :author_name, :title], [[99, "Bob Jones", "Topic 99"]]
232
+ assert_equal 99, Topic.last.id
233
+ end
234
+ end
235
+
236
+ context "ActiveRecord timestamps" do
237
+ context "when the timestamps columns are present" do
238
+ setup do
239
+ ActiveRecord::Base.default_timezone = :utc
240
+ Delorean.time_travel_to("5 minutes ago") do
241
+ assert_difference "Book.count", +1 do
242
+ result = Book.import [:title, :author_name, :publisher], [["LDAP", "Big Bird", "Del Rey"]]
243
+ end
244
+ end
245
+ @book = Book.last
246
+ end
247
+
248
+ it "should set the created_at column for new records" do
249
+ assert_equal 5.minutes.ago.utc.strftime("%H:%M"), @book.created_at.strftime("%H:%M")
250
+ end
251
+
252
+ it "should set the created_on column for new records" do
253
+ assert_equal 5.minutes.ago.utc.strftime("%H:%M"), @book.created_on.strftime("%H:%M")
254
+ end
255
+
256
+ it "should set the updated_at column for new records" do
257
+ assert_equal 5.minutes.ago.utc.strftime("%H:%M"), @book.updated_at.strftime("%H:%M")
258
+ end
259
+
260
+ it "should set the updated_on column for new records" do
261
+ assert_equal 5.minutes.ago.utc.strftime("%H:%M"), @book.updated_on.strftime("%H:%M")
262
+ end
263
+ end
264
+
265
+ context "when a custom time zone is set" do
266
+ setup do
267
+ original_timezone = ActiveRecord::Base.default_timezone
268
+ ActiveRecord::Base.default_timezone = :utc
269
+ Delorean.time_travel_to("5 minutes ago") do
270
+ assert_difference "Book.count", +1 do
271
+ result = Book.import [:title, :author_name, :publisher], [["LDAP", "Big Bird", "Del Rey"]]
272
+ end
273
+ end
274
+ ActiveRecord::Base.default_timezone = original_timezone
275
+ @book = Book.last
276
+ end
277
+
278
+ it "should set the created_at and created_on timestamps for new records" do
279
+ assert_equal 5.minutes.ago.utc.strftime("%H:%M"), @book.created_at.strftime("%H:%M")
280
+ assert_equal 5.minutes.ago.utc.strftime("%H:%M"), @book.created_on.strftime("%H:%M")
281
+ end
282
+
283
+ it "should set the updated_at and updated_on timestamps for new records" do
284
+ assert_equal 5.minutes.ago.utc.strftime("%H:%M"), @book.updated_at.strftime("%H:%M")
285
+ assert_equal 5.minutes.ago.utc.strftime("%H:%M"), @book.updated_on.strftime("%H:%M")
286
+ end
287
+ end
288
+ end
289
+
290
+ context "importing with database reserved words" do
291
+ let(:group) { Build(:group, :order => "superx") }
292
+
293
+ it "should import just fine" do
294
+ assert_difference "Group.count", +1 do
295
+ result = Group.import [group]
296
+ end
297
+ assert_equal "superx", Group.first.order
298
+ end
299
+ end
300
+
301
+ context "importing a datetime field" do
302
+ it "should import a date with YYYY/MM/DD format just fine" do
303
+ Topic.import [:author_name, :title, :last_read], [["Bob Jones", "Topic 2", "2010/05/14"]]
304
+ assert_equal "2010/05/14".to_date, Topic.last.last_read.to_date
305
+ end
306
+ end
307
+
308
+ context "importing through an association scope" do
309
+ [ true, false ].each do |b|
310
+ context "when validation is " + (b ? "enabled" : "disabled") do
311
+ it "should automatically set the foreign key column" do
312
+ books = [[ "David Chelimsky", "The RSpec Book" ], [ "Chad Fowler", "Rails Recipes" ]]
313
+ topic = FactoryGirl.create :topic
314
+ topic.books.import [ :author_name, :title ], books, :validate => b
315
+ assert_equal 2, topic.books.count
316
+ assert topic.books.all? { |b| b.topic_id == topic.id }
317
+ end
318
+ end
319
+ end
320
+ end
321
+ end