Empact-activerecord-import 0.3.1 → 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +3 -1
- data/VERSION +1 -1
- data/lib/activerecord-import/adapters/abstract_adapter.rb +1 -7
- data/lib/activerecord-import/adapters/mysql_adapter.rb +10 -1
- data/lib/activerecord-import/base.rb +1 -0
- data/lib/activerecord-import/import.rb +20 -8
- data/lib/activerecord-import/synchronize.rb +55 -0
- metadata +23 -72
- data/test/active_record/connection_adapter_test.rb +0 -52
- data/test/adapters/mysql.rb +0 -1
- data/test/adapters/mysql2.rb +0 -1
- data/test/adapters/postgresql.rb +0 -1
- data/test/adapters/sqlite3.rb +0 -1
- data/test/import_test.rb +0 -202
- data/test/models/book.rb +0 -3
- data/test/models/group.rb +0 -3
- data/test/models/topic.rb +0 -7
- data/test/mysql/import_test.rb +0 -6
- data/test/mysql2/import_test.rb +0 -6
- data/test/postgresql/import_test.rb +0 -20
- data/test/schema/generic_schema.rb +0 -98
- data/test/schema/mysql_schema.rb +0 -17
- data/test/schema/version.rb +0 -4
- data/test/support/active_support/test_case_extensions.rb +0 -67
- data/test/support/factories.rb +0 -13
- data/test/support/generate.rb +0 -29
- data/test/support/mysql/assertions.rb +0 -55
- data/test/support/mysql/import_examples.rb +0 -117
- data/test/test_helper.rb +0 -46
data/Rakefile
CHANGED
@@ -41,7 +41,9 @@ ADAPTERS.each do |adapter|
|
|
41
41
|
namespace :test do
|
42
42
|
desc "Runs #{adapter} database tests."
|
43
43
|
Rake::TestTask.new(adapter) do |t|
|
44
|
-
|
44
|
+
# FactoryGirl has an issue with warnings, so turn off, so noisy
|
45
|
+
# t.warning = true
|
46
|
+
t.test_files = FileList["test/adapters/#{adapter}.rb", "test/*_test.rb", "test/active_record/*_test.rb", "test/#{adapter}/**/*_test.rb"]
|
45
47
|
end
|
46
48
|
task adapter
|
47
49
|
end
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.3.
|
1
|
+
0.3.2
|
@@ -3,12 +3,6 @@ module ActiveRecord::Import::AbstractAdapter
|
|
3
3
|
QUERY_OVERHEAD = 8 #This was shown to be true for MySQL, but it's not clear where the overhead is from.
|
4
4
|
|
5
5
|
module ClassMethods
|
6
|
-
# Returns the sum of the sizes of the passed in objects. This should
|
7
|
-
# probably be moved outside this class, but to where?
|
8
|
-
def sum_sizes( *objects ) # :nodoc:
|
9
|
-
objects.inject( 0 ){|sum,o| sum += o.size }
|
10
|
-
end
|
11
|
-
|
12
6
|
def get_insert_value_sets( values, sql_size, max_bytes ) # :nodoc:
|
13
7
|
value_sets = []
|
14
8
|
arr, current_arr_values_size, current_size = [], 0, 0
|
@@ -51,7 +45,7 @@ module ActiveRecord::Import::AbstractAdapter
|
|
51
45
|
sql_size = QUERY_OVERHEAD + base_sql.size + post_sql.size
|
52
46
|
|
53
47
|
# the number of bytes the requested insert statement values will take up
|
54
|
-
values_in_bytes =
|
48
|
+
values_in_bytes = values.sum {|value| value.size }
|
55
49
|
|
56
50
|
# the number of bytes (commas) it will take to comma separate our values
|
57
51
|
comma_separated_bytes = values.size-1
|
@@ -6,7 +6,16 @@ module ActiveRecord::Import::MysqlAdapter
|
|
6
6
|
include ActiveRecord::Import::OnDuplicateKeyUpdateSupport
|
7
7
|
end
|
8
8
|
end
|
9
|
-
|
9
|
+
|
10
|
+
# Returns the maximum number of bytes that the server will allow
|
11
|
+
# in a single packet
|
12
|
+
def max_allowed_packet # :nodoc:
|
13
|
+
result = execute( "SHOW VARIABLES like 'max_allowed_packet';" )
|
14
|
+
# original Mysql gem responds to #fetch_row while Mysql2 responds to #first
|
15
|
+
val = result.respond_to?(:fetch_row) ? result.fetch_row[1] : result.first[1]
|
16
|
+
val.to_i
|
17
|
+
end
|
18
|
+
|
10
19
|
# Returns a generated ON DUPLICATE KEY UPDATE statement given the passed
|
11
20
|
# in +args+.
|
12
21
|
def sql_for_on_duplicate_key_update( table_name, *args ) # :nodoc:
|
@@ -129,6 +129,11 @@ class ActiveRecord::Base
|
|
129
129
|
# BlogPost.import posts, :synchronize=>[ post ]
|
130
130
|
# puts post.author_name # => 'yoda'
|
131
131
|
#
|
132
|
+
# # Example synchronizing unsaved/new instances in memory by using a uniqued imported field
|
133
|
+
# posts = [BlogPost.new(:title => "Foo"), BlogPost.new(:title => "Bar")]
|
134
|
+
# BlogPost.import posts, :synchronize => posts
|
135
|
+
# puts posts.first.new_record? # => false
|
136
|
+
#
|
132
137
|
# == On Duplicate Key Update (MySQL only)
|
133
138
|
#
|
134
139
|
# The :on_duplicate_key_update option can be either an Array or a Hash.
|
@@ -178,6 +183,9 @@ class ActiveRecord::Base
|
|
178
183
|
end
|
179
184
|
# end
|
180
185
|
end
|
186
|
+
# supports empty array
|
187
|
+
elsif args.last.is_a?( Array ) and args.last.empty?
|
188
|
+
return ActiveRecord::Import::Result.new([], 0) if args.last.empty?
|
181
189
|
# supports 2-element array and array
|
182
190
|
elsif args.size == 2 and args.first.is_a?( Array ) and args.last.is_a?( Array )
|
183
191
|
column_names, array_of_attributes = args
|
@@ -209,7 +217,8 @@ class ActiveRecord::Base
|
|
209
217
|
end
|
210
218
|
|
211
219
|
if options[:synchronize]
|
212
|
-
|
220
|
+
sync_keys = options[:synchronize_keys] || [self.primary_key]
|
221
|
+
synchronize( options[:synchronize], sync_keys)
|
213
222
|
end
|
214
223
|
|
215
224
|
return_obj.num_inserts = 0 if return_obj.num_inserts.nil?
|
@@ -236,7 +245,9 @@ class ActiveRecord::Base
|
|
236
245
|
# keep track of the instance and the position it is currently at. if this fails
|
237
246
|
# validation we'll use the index to remove it from the array_of_attributes
|
238
247
|
arr.each_with_index do |hsh,i|
|
239
|
-
instance = new
|
248
|
+
instance = new do |model|
|
249
|
+
hsh.each_pair{ |k,v| model.send("#{k}=", v) }
|
250
|
+
end
|
240
251
|
if not instance.valid?
|
241
252
|
array_of_attributes[ i ] = nil
|
242
253
|
failed_instances << instance
|
@@ -255,9 +266,11 @@ class ActiveRecord::Base
|
|
255
266
|
# information on +column_names+, +array_of_attributes_ and
|
256
267
|
# +options+.
|
257
268
|
def import_without_validations_or_callbacks( column_names, array_of_attributes, options={} )
|
269
|
+
columns = column_names.map { |name| columns_hash[name.to_s] }
|
270
|
+
|
258
271
|
columns_sql = "(#{column_names.map{|name| connection.quote_column_name(name) }.join(',')})"
|
259
272
|
insert_sql = "INSERT #{options[:ignore] ? 'IGNORE ':''}INTO #{quoted_table_name} #{columns_sql} VALUES "
|
260
|
-
values_sql =
|
273
|
+
values_sql = values_sql_for_columns_and_attributes(columns, array_of_attributes)
|
261
274
|
if not supports_import?
|
262
275
|
number_inserted = 0
|
263
276
|
values_sql.each do |values|
|
@@ -280,15 +293,14 @@ class ActiveRecord::Base
|
|
280
293
|
|
281
294
|
# Returns SQL the VALUES for an INSERT statement given the passed in +columns+
|
282
295
|
# and +array_of_attributes+.
|
283
|
-
def
|
284
|
-
columns = column_names.map { |name| columns_hash[name] }
|
285
|
-
|
296
|
+
def values_sql_for_columns_and_attributes(columns, array_of_attributes) # :nodoc:
|
286
297
|
array_of_attributes.map do |arr|
|
287
298
|
my_values = arr.each_with_index.map do |val,j|
|
288
|
-
|
299
|
+
column = columns[j]
|
300
|
+
if !sequence_name.blank? && column.name == primary_key && val.nil?
|
289
301
|
connection.next_value_for_sequence(sequence_name)
|
290
302
|
else
|
291
|
-
|
303
|
+
connection.quote(column.type_cast(val), column)
|
292
304
|
end
|
293
305
|
end
|
294
306
|
"(#{my_values.join(',')})"
|
@@ -0,0 +1,55 @@
|
|
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.find_by_author("Zach")
|
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.find_by_author("Zach")
|
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.find( :all, :conditions=>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
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# See ActiveRecord::ConnectionAdapters::AbstractAdapter.synchronize
|
51
|
+
def synchronize(instances, key=[ActiveRecord::Base.primary_key])
|
52
|
+
self.class.synchronize(instances, key)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
metadata
CHANGED
@@ -1,12 +1,8 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: Empact-activerecord-import
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
prerelease:
|
5
|
-
|
6
|
-
- 0
|
7
|
-
- 3
|
8
|
-
- 1
|
9
|
-
version: 0.3.1
|
4
|
+
prerelease:
|
5
|
+
version: 0.3.2
|
10
6
|
platform: ruby
|
11
7
|
authors:
|
12
8
|
- Zach Dennis
|
@@ -15,8 +11,7 @@ autorequire:
|
|
15
11
|
bindir: bin
|
16
12
|
cert_chain: []
|
17
13
|
|
18
|
-
date:
|
19
|
-
default_executable:
|
14
|
+
date: 2011-05-05 00:00:00 Z
|
20
15
|
dependencies:
|
21
16
|
- !ruby/object:Gem::Dependency
|
22
17
|
name: activerecord
|
@@ -25,44 +20,43 @@ dependencies:
|
|
25
20
|
requirements:
|
26
21
|
- - ~>
|
27
22
|
- !ruby/object:Gem::Version
|
28
|
-
segments:
|
29
|
-
- 3
|
30
|
-
- 0
|
31
|
-
- 0
|
32
23
|
version: 3.0.0
|
33
24
|
type: :runtime
|
34
25
|
prerelease: false
|
35
26
|
version_requirements: *id001
|
36
27
|
- !ruby/object:Gem::Dependency
|
37
|
-
name:
|
28
|
+
name: rake
|
38
29
|
requirement: &id002 !ruby/object:Gem::Requirement
|
39
30
|
none: false
|
40
31
|
requirements:
|
41
32
|
- - ">="
|
42
33
|
- !ruby/object:Gem::Version
|
43
|
-
|
44
|
-
- 1
|
45
|
-
- 4
|
46
|
-
- 0
|
47
|
-
version: 1.4.0
|
34
|
+
version: "0"
|
48
35
|
type: :development
|
49
36
|
prerelease: false
|
50
37
|
version_requirements: *id002
|
51
38
|
- !ruby/object:Gem::Dependency
|
52
|
-
name:
|
39
|
+
name: jeweler
|
53
40
|
requirement: &id003 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 1.4.0
|
46
|
+
type: :development
|
47
|
+
prerelease: false
|
48
|
+
version_requirements: *id003
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: activerecord
|
51
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
54
52
|
none: false
|
55
53
|
requirements:
|
56
54
|
- - ~>
|
57
55
|
- !ruby/object:Gem::Version
|
58
|
-
segments:
|
59
|
-
- 3
|
60
|
-
- 0
|
61
|
-
- 0
|
62
56
|
version: 3.0.0
|
63
57
|
type: :runtime
|
64
58
|
prerelease: false
|
65
|
-
version_requirements: *
|
59
|
+
version_requirements: *id004
|
66
60
|
description: Extraction of the ActiveRecord::Base#import functionality from ar-extensions for Rails 3 and beyond
|
67
61
|
email: ben.woosley@gmail.com
|
68
62
|
executables: []
|
@@ -91,28 +85,7 @@ files:
|
|
91
85
|
- lib/activerecord-import/mysql2.rb
|
92
86
|
- lib/activerecord-import/postgresql.rb
|
93
87
|
- lib/activerecord-import/sqlite3.rb
|
94
|
-
-
|
95
|
-
- test/adapters/mysql.rb
|
96
|
-
- test/adapters/mysql2.rb
|
97
|
-
- test/adapters/postgresql.rb
|
98
|
-
- test/adapters/sqlite3.rb
|
99
|
-
- test/import_test.rb
|
100
|
-
- test/models/book.rb
|
101
|
-
- test/models/group.rb
|
102
|
-
- test/models/topic.rb
|
103
|
-
- test/mysql/import_test.rb
|
104
|
-
- test/mysql2/import_test.rb
|
105
|
-
- test/postgresql/import_test.rb
|
106
|
-
- test/schema/generic_schema.rb
|
107
|
-
- test/schema/mysql_schema.rb
|
108
|
-
- test/schema/version.rb
|
109
|
-
- test/support/active_support/test_case_extensions.rb
|
110
|
-
- test/support/factories.rb
|
111
|
-
- test/support/generate.rb
|
112
|
-
- test/support/mysql/assertions.rb
|
113
|
-
- test/support/mysql/import_examples.rb
|
114
|
-
- test/test_helper.rb
|
115
|
-
has_rdoc: true
|
88
|
+
- lib/activerecord-import/synchronize.rb
|
116
89
|
homepage: http://github.com/Empact/activerecord-import
|
117
90
|
licenses: []
|
118
91
|
|
@@ -126,7 +99,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
126
99
|
requirements:
|
127
100
|
- - ">="
|
128
101
|
- !ruby/object:Gem::Version
|
129
|
-
hash: -
|
102
|
+
hash: -2706600043388041365
|
130
103
|
segments:
|
131
104
|
- 0
|
132
105
|
version: "0"
|
@@ -135,35 +108,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
135
108
|
requirements:
|
136
109
|
- - ">="
|
137
110
|
- !ruby/object:Gem::Version
|
138
|
-
segments:
|
139
|
-
- 0
|
140
111
|
version: "0"
|
141
112
|
requirements: []
|
142
113
|
|
143
114
|
rubyforge_project:
|
144
|
-
rubygems_version: 1.
|
115
|
+
rubygems_version: 1.7.2
|
145
116
|
signing_key:
|
146
117
|
specification_version: 3
|
147
118
|
summary: Bulk-loading extension for ActiveRecord
|
148
|
-
test_files:
|
149
|
-
|
150
|
-
- test/adapters/mysql.rb
|
151
|
-
- test/adapters/mysql2.rb
|
152
|
-
- test/adapters/postgresql.rb
|
153
|
-
- test/adapters/sqlite3.rb
|
154
|
-
- test/import_test.rb
|
155
|
-
- test/models/book.rb
|
156
|
-
- test/models/group.rb
|
157
|
-
- test/models/topic.rb
|
158
|
-
- test/mysql/import_test.rb
|
159
|
-
- test/mysql2/import_test.rb
|
160
|
-
- test/postgresql/import_test.rb
|
161
|
-
- test/schema/generic_schema.rb
|
162
|
-
- test/schema/mysql_schema.rb
|
163
|
-
- test/schema/version.rb
|
164
|
-
- test/support/active_support/test_case_extensions.rb
|
165
|
-
- test/support/factories.rb
|
166
|
-
- test/support/generate.rb
|
167
|
-
- test/support/mysql/assertions.rb
|
168
|
-
- test/support/mysql/import_examples.rb
|
169
|
-
- test/test_helper.rb
|
119
|
+
test_files: []
|
120
|
+
|
@@ -1,52 +0,0 @@
|
|
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
|
data/test/adapters/mysql.rb
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
ENV["ARE_DB"] = "mysql"
|
data/test/adapters/mysql2.rb
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
ENV["ARE_DB"] = "mysql2"
|
data/test/adapters/postgresql.rb
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
ENV["ARE_DB"] = "postgresql"
|
data/test/adapters/sqlite3.rb
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
ENV["ARE_DB"] = "sqlite3"
|
data/test/import_test.rb
DELETED
@@ -1,202 +0,0 @@
|
|
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
|
-
context "with :validation option" do
|
16
|
-
let(:columns) { %w(title author_name) }
|
17
|
-
let(:valid_values) { [[ "LDAP", "Jerry Carter"], ["Rails Recipes", "Chad Fowler"]] }
|
18
|
-
let(:invalid_values) { [[ "The RSpec Book", ""], ["Agile+UX", ""]] }
|
19
|
-
|
20
|
-
context "with validation checks turned off" do
|
21
|
-
it "should import valid data" do
|
22
|
-
assert_difference "Topic.count", +2 do
|
23
|
-
result = Topic.import columns, valid_values, :validate => false
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
it "should import invalid data" do
|
28
|
-
assert_difference "Topic.count", +2 do
|
29
|
-
result = Topic.import columns, invalid_values, :validate => false
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
context "with validation checks turned on" do
|
35
|
-
it "should import valid data" do
|
36
|
-
assert_difference "Topic.count", +2 do
|
37
|
-
result = Topic.import columns, valid_values, :validate => true
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
it "should not import invalid data" do
|
42
|
-
assert_no_difference "Topic.count" do
|
43
|
-
result = Topic.import columns, invalid_values, :validate => true
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
it "should report the failed instances" do
|
48
|
-
results = Topic.import columns, invalid_values, :validate => true
|
49
|
-
assert_equal invalid_values.size, results.failed_instances.size
|
50
|
-
results.failed_instances.each{ |e| assert_kind_of Topic, e }
|
51
|
-
end
|
52
|
-
|
53
|
-
it "should import valid data when mixed with invalid data" do
|
54
|
-
assert_difference "Topic.count", +2 do
|
55
|
-
result = Topic.import columns, valid_values + invalid_values, :validate => true
|
56
|
-
end
|
57
|
-
assert_equal 0, Topic.find_all_by_title(invalid_values.map(&:first)).count
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
context "with an array of unsaved model instances" do
|
63
|
-
let(:topic) { Build(:topic, :title => "The RSpec Book", :author_name => "David Chelimsky")}
|
64
|
-
let(:topics) { Build(9, :topics) }
|
65
|
-
let(:invalid_topics){ Build(7, :invalid_topics)}
|
66
|
-
|
67
|
-
it "should import records based on those model's attributes" do
|
68
|
-
assert_difference "Topic.count", +9 do
|
69
|
-
result = Topic.import topics
|
70
|
-
end
|
71
|
-
|
72
|
-
Topic.import [topic]
|
73
|
-
assert Topic.find_by_title_and_author_name("The RSpec Book", "David Chelimsky")
|
74
|
-
end
|
75
|
-
|
76
|
-
it "should not overwrite existing records" do
|
77
|
-
topic = Generate(:topic, :title => "foobar")
|
78
|
-
assert_no_difference "Topic.count" do
|
79
|
-
begin
|
80
|
-
Topic.transaction do
|
81
|
-
topic.title = "baz"
|
82
|
-
Topic.import [topic]
|
83
|
-
end
|
84
|
-
rescue Exception
|
85
|
-
# PostgreSQL raises PgError due to key constraints
|
86
|
-
# I don't know why ActiveRecord doesn't catch these. *sigh*
|
87
|
-
end
|
88
|
-
end
|
89
|
-
assert_equal "foobar", topic.reload.title
|
90
|
-
end
|
91
|
-
|
92
|
-
context "with validation checks turned on" do
|
93
|
-
it "should import valid models" do
|
94
|
-
assert_difference "Topic.count", +9 do
|
95
|
-
result = Topic.import topics, :validate => true
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
it "should not import invalid models" do
|
100
|
-
assert_no_difference "Topic.count" do
|
101
|
-
result = Topic.import invalid_topics, :validate => true
|
102
|
-
end
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
context "with validation checks turned off" do
|
107
|
-
it "should import invalid models" do
|
108
|
-
assert_difference "Topic.count", +7 do
|
109
|
-
result = Topic.import invalid_topics, :validate => false
|
110
|
-
end
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
context "with an array of columns and an array of unsaved model instances" do
|
116
|
-
let(:topics) { Build(2, :topics) }
|
117
|
-
|
118
|
-
it "should import records populating the supplied columns with the corresponding model instance attributes" do
|
119
|
-
assert_difference "Topic.count", +2 do
|
120
|
-
result = Topic.import [:author_name, :title], topics
|
121
|
-
end
|
122
|
-
|
123
|
-
# imported topics should be findable by their imported attributes
|
124
|
-
assert Topic.find_by_author_name(topics.first.author_name)
|
125
|
-
assert Topic.find_by_author_name(topics.last.author_name)
|
126
|
-
end
|
127
|
-
|
128
|
-
it "should not populate fields for columns not imported" do
|
129
|
-
topics.first.author_email_address = "zach.dennis@gmail.com"
|
130
|
-
assert_difference "Topic.count", +2 do
|
131
|
-
result = Topic.import [:author_name, :title], topics
|
132
|
-
end
|
133
|
-
|
134
|
-
assert !Topic.find_by_author_email_address("zach.dennis@gmail.com")
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
context "ActiveRecord timestamps" do
|
139
|
-
context "when the timestamps columns are present" do
|
140
|
-
setup do
|
141
|
-
Delorean.time_travel_to("5 minutes ago") do
|
142
|
-
assert_difference "Book.count", +1 do
|
143
|
-
result = Book.import [:title, :author_name, :publisher], [["LDAP", "Big Bird", "Del Rey"]]
|
144
|
-
end
|
145
|
-
end
|
146
|
-
@book = Book.last
|
147
|
-
end
|
148
|
-
|
149
|
-
it "should set the created_at column for new records" do
|
150
|
-
assert_equal 5.minutes.ago.strftime("%H:%M"), @book.created_at.strftime("%H:%M")
|
151
|
-
end
|
152
|
-
|
153
|
-
it "should set the created_on column for new records" do
|
154
|
-
assert_equal 5.minutes.ago.strftime("%H:%M"), @book.created_on.strftime("%H:%M")
|
155
|
-
end
|
156
|
-
|
157
|
-
it "should set the updated_at column for new records" do
|
158
|
-
assert_equal 5.minutes.ago.strftime("%H:%M"), @book.updated_at.strftime("%H:%M")
|
159
|
-
end
|
160
|
-
|
161
|
-
it "should set the updated_on column for new records" do
|
162
|
-
assert_equal 5.minutes.ago.strftime("%H:%M"), @book.updated_on.strftime("%H:%M")
|
163
|
-
end
|
164
|
-
end
|
165
|
-
|
166
|
-
context "when a custom time zone is set" do
|
167
|
-
setup do
|
168
|
-
original_timezone = ActiveRecord::Base.default_timezone
|
169
|
-
ActiveRecord::Base.default_timezone = :utc
|
170
|
-
Delorean.time_travel_to("5 minutes ago") do
|
171
|
-
assert_difference "Book.count", +1 do
|
172
|
-
result = Book.import [:title, :author_name, :publisher], [["LDAP", "Big Bird", "Del Rey"]]
|
173
|
-
end
|
174
|
-
end
|
175
|
-
ActiveRecord::Base.default_timezone = original_timezone
|
176
|
-
@book = Book.last
|
177
|
-
end
|
178
|
-
|
179
|
-
it "should set the created_at and created_on timestamps for new records" do
|
180
|
-
assert_equal 5.minutes.ago.utc.strftime("%H:%M"), @book.created_at.strftime("%H:%M")
|
181
|
-
assert_equal 5.minutes.ago.utc.strftime("%H:%M"), @book.created_on.strftime("%H:%M")
|
182
|
-
end
|
183
|
-
|
184
|
-
it "should set the updated_at and updated_on timestamps for new records" do
|
185
|
-
assert_equal 5.minutes.ago.utc.strftime("%H:%M"), @book.updated_at.strftime("%H:%M")
|
186
|
-
assert_equal 5.minutes.ago.utc.strftime("%H:%M"), @book.updated_on.strftime("%H:%M")
|
187
|
-
end
|
188
|
-
end
|
189
|
-
end
|
190
|
-
|
191
|
-
context "importing with database reserved words" do
|
192
|
-
let(:group) { Build(:group, :order => "superx") }
|
193
|
-
|
194
|
-
it "should import just fine" do
|
195
|
-
assert_difference "Group.count", +1 do
|
196
|
-
result = Group.import [group]
|
197
|
-
end
|
198
|
-
assert_equal "superx", Group.first.order
|
199
|
-
end
|
200
|
-
end
|
201
|
-
|
202
|
-
end
|
data/test/models/book.rb
DELETED
data/test/models/group.rb
DELETED
data/test/models/topic.rb
DELETED
@@ -1,7 +0,0 @@
|
|
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
|
data/test/mysql/import_test.rb
DELETED
@@ -1,6 +0,0 @@
|
|
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
|
data/test/mysql2/import_test.rb
DELETED
@@ -1,6 +0,0 @@
|
|
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
|
@@ -1,20 +0,0 @@
|
|
1
|
-
require File.expand_path('../../test_helper', __FILE__)
|
2
|
-
|
3
|
-
describe "#supports_imports?" do
|
4
|
-
it "should support import" do
|
5
|
-
assert ActiveRecord::Base.supports_import?
|
6
|
-
end
|
7
|
-
end
|
8
|
-
|
9
|
-
describe "#import" do
|
10
|
-
it "should import with a single insert" do
|
11
|
-
# see ActiveRecord::ConnectionAdapters::AbstractAdapter test for more specifics
|
12
|
-
assert_difference "Topic.count", +10 do
|
13
|
-
result = Topic.import Build(3, :topics)
|
14
|
-
assert_equal 1, result.num_inserts
|
15
|
-
|
16
|
-
result = Topic.import Build(7, :topics)
|
17
|
-
assert_equal 1, result.num_inserts
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
@@ -1,98 +0,0 @@
|
|
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
|
-
end
|
data/test/schema/mysql_schema.rb
DELETED
@@ -1,17 +0,0 @@
|
|
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
|
data/test/schema/version.rb
DELETED
@@ -1,67 +0,0 @@
|
|
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
|
-
|
data/test/support/factories.rb
DELETED
@@ -1,13 +0,0 @@
|
|
1
|
-
Factory.define :group do |m|
|
2
|
-
m.sequence(:order) { |n| "Order #{n}" }
|
3
|
-
end
|
4
|
-
|
5
|
-
Factory.define :invalid_topic, :class => "Topic" do |m|
|
6
|
-
m.sequence(:title){ |n| "Title #{n}"}
|
7
|
-
m.author_name nil
|
8
|
-
end
|
9
|
-
|
10
|
-
Factory.define :topic do |m|
|
11
|
-
m.sequence(:title){ |n| "Title #{n}"}
|
12
|
-
m.sequence(:author_name){ |n| "Author #{n}"}
|
13
|
-
end
|
data/test/support/generate.rb
DELETED
@@ -1,29 +0,0 @@
|
|
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 << Factory.build(factory.to_s.singularize.to_sym, factory_girl_args) }
|
10
|
-
end
|
11
|
-
else
|
12
|
-
Factory.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 << Factory.create(factory.to_s.singularize.to_sym, factory_girl_args) }
|
24
|
-
end
|
25
|
-
else
|
26
|
-
Factory.create(factory.to_s.singularize.to_sym, factory_girl_args)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
@@ -1,55 +0,0 @@
|
|
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
|
@@ -1,117 +0,0 @@
|
|
1
|
-
def should_support_mysql_import_functionality
|
2
|
-
describe "#import with :on_duplicate_key_update option (mysql specific functionality)" do
|
3
|
-
extend ActiveSupport::TestCase::MySQLAssertions
|
4
|
-
|
5
|
-
asssertion_group(:should_support_on_duplicate_key_update) do
|
6
|
-
should_not_update_fields_not_mentioned
|
7
|
-
should_update_foreign_keys
|
8
|
-
should_not_update_created_at_on_timestamp_columns
|
9
|
-
should_update_updated_at_on_timestamp_columns
|
10
|
-
end
|
11
|
-
|
12
|
-
macro(:perform_import){ raise "supply your own #perform_import in a context below" }
|
13
|
-
macro(:updated_topic){ Topic.find(@topic) }
|
14
|
-
|
15
|
-
context "given columns and values with :validation checks turned off" do
|
16
|
-
let(:columns){ %w( id title author_name author_email_address parent_id ) }
|
17
|
-
let(:values){ [ [ 99, "Book", "John Doe", "john@doe.com", 17 ] ] }
|
18
|
-
let(:updated_values){ [ [ 99, "Book - 2nd Edition", "Author Should Not Change", "johndoe@example.com", 57 ] ] }
|
19
|
-
|
20
|
-
macro(:perform_import) do |*opts|
|
21
|
-
Topic.import columns, updated_values, opts.extract_options!.merge(:on_duplicate_key_update => update_columns, :validate => false)
|
22
|
-
end
|
23
|
-
|
24
|
-
setup do
|
25
|
-
Topic.import columns, values, :validate => false
|
26
|
-
@topic = Topic.find 99
|
27
|
-
end
|
28
|
-
|
29
|
-
context "using string column names" do
|
30
|
-
let(:update_columns){ [ "title", "author_email_address", "parent_id" ] }
|
31
|
-
should_support_on_duplicate_key_update
|
32
|
-
should_update_fields_mentioned
|
33
|
-
end
|
34
|
-
|
35
|
-
context "using symbol column names" do
|
36
|
-
let(:update_columns){ [ :title, :author_email_address, :parent_id ] }
|
37
|
-
should_support_on_duplicate_key_update
|
38
|
-
should_update_fields_mentioned
|
39
|
-
end
|
40
|
-
|
41
|
-
context "using string hash map" do
|
42
|
-
let(:update_columns){ { "title" => "title", "author_email_address" => "author_email_address", "parent_id" => "parent_id" } }
|
43
|
-
should_support_on_duplicate_key_update
|
44
|
-
should_update_fields_mentioned
|
45
|
-
end
|
46
|
-
|
47
|
-
context "using string hash map, but specifying column mismatches" do
|
48
|
-
let(:update_columns){ { "title" => "author_email_address", "author_email_address" => "title", "parent_id" => "parent_id" } }
|
49
|
-
should_support_on_duplicate_key_update
|
50
|
-
should_update_fields_mentioned_with_hash_mappings
|
51
|
-
end
|
52
|
-
|
53
|
-
context "using symbol hash map" do
|
54
|
-
let(:update_columns){ { :title => :title, :author_email_address => :author_email_address, :parent_id => :parent_id } }
|
55
|
-
should_support_on_duplicate_key_update
|
56
|
-
should_update_fields_mentioned
|
57
|
-
end
|
58
|
-
|
59
|
-
context "using symbol hash map, but specifying column mismatches" do
|
60
|
-
let(:update_columns){ { :title => :author_email_address, :author_email_address => :title, :parent_id => :parent_id } }
|
61
|
-
should_support_on_duplicate_key_update
|
62
|
-
should_update_fields_mentioned_with_hash_mappings
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
context "given array of model instances with :validation checks turned off" do
|
67
|
-
macro(:perform_import) do |*opts|
|
68
|
-
@topic.title = "Book - 2nd Edition"
|
69
|
-
@topic.author_name = "Author Should Not Change"
|
70
|
-
@topic.author_email_address = "johndoe@example.com"
|
71
|
-
@topic.parent_id = 57
|
72
|
-
Topic.import [@topic], opts.extract_options!.merge(:on_duplicate_key_update => update_columns, :validate => false)
|
73
|
-
end
|
74
|
-
|
75
|
-
setup do
|
76
|
-
@topic = Generate(:topic, :id => 99, :author_name => "John Doe", :parent_id => 17)
|
77
|
-
end
|
78
|
-
|
79
|
-
context "using string column names" do
|
80
|
-
let(:update_columns){ [ "title", "author_email_address", "parent_id" ] }
|
81
|
-
should_support_on_duplicate_key_update
|
82
|
-
should_update_fields_mentioned
|
83
|
-
end
|
84
|
-
|
85
|
-
context "using symbol column names" do
|
86
|
-
let(:update_columns){ [ :title, :author_email_address, :parent_id ] }
|
87
|
-
should_support_on_duplicate_key_update
|
88
|
-
should_update_fields_mentioned
|
89
|
-
end
|
90
|
-
|
91
|
-
context "using string hash map" do
|
92
|
-
let(:update_columns){ { "title" => "title", "author_email_address" => "author_email_address", "parent_id" => "parent_id" } }
|
93
|
-
should_support_on_duplicate_key_update
|
94
|
-
should_update_fields_mentioned
|
95
|
-
end
|
96
|
-
|
97
|
-
context "using string hash map, but specifying column mismatches" do
|
98
|
-
let(:update_columns){ { "title" => "author_email_address", "author_email_address" => "title", "parent_id" => "parent_id" } }
|
99
|
-
should_support_on_duplicate_key_update
|
100
|
-
should_update_fields_mentioned_with_hash_mappings
|
101
|
-
end
|
102
|
-
|
103
|
-
context "using symbol hash map" do
|
104
|
-
let(:update_columns){ { :title => :title, :author_email_address => :author_email_address, :parent_id => :parent_id } }
|
105
|
-
should_support_on_duplicate_key_update
|
106
|
-
should_update_fields_mentioned
|
107
|
-
end
|
108
|
-
|
109
|
-
context "using symbol hash map, but specifying column mismatches" do
|
110
|
-
let(:update_columns){ { :title => :author_email_address, :author_email_address => :title, :parent_id => :parent_id } }
|
111
|
-
should_support_on_duplicate_key_update
|
112
|
-
should_update_fields_mentioned_with_hash_mappings
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
end
|
117
|
-
end
|
data/test/test_helper.rb
DELETED
@@ -1,46 +0,0 @@
|
|
1
|
-
require 'pathname'
|
2
|
-
test_dir = Pathname.new File.dirname(__FILE__)
|
3
|
-
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
4
|
-
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
5
|
-
|
6
|
-
require "fileutils"
|
7
|
-
require "rubygems"
|
8
|
-
|
9
|
-
ENV["RAILS_ENV"] = "test"
|
10
|
-
|
11
|
-
require "bundler"
|
12
|
-
Bundler.setup
|
13
|
-
|
14
|
-
require "logger"
|
15
|
-
require 'test/unit'
|
16
|
-
require "active_record"
|
17
|
-
require "active_record/fixtures"
|
18
|
-
require "active_support/test_case"
|
19
|
-
|
20
|
-
require "delorean"
|
21
|
-
require "ruby-debug"
|
22
|
-
|
23
|
-
adapter = ENV["ARE_DB"] || "sqlite3"
|
24
|
-
|
25
|
-
FileUtils.mkdir_p 'log'
|
26
|
-
ActiveRecord::Base.logger = Logger.new("log/test.log")
|
27
|
-
ActiveRecord::Base.logger.level = Logger::DEBUG
|
28
|
-
ActiveRecord::Base.configurations["test"] = YAML.load(test_dir.join("database.yml").open)[adapter]
|
29
|
-
|
30
|
-
require "activerecord-import"
|
31
|
-
ActiveRecord::Base.establish_connection "test"
|
32
|
-
|
33
|
-
ActiveSupport::Notifications.subscribe(/active_record.sql/) do |event, _, _, _, hsh|
|
34
|
-
ActiveRecord::Base.logger.info hsh[:sql]
|
35
|
-
end
|
36
|
-
|
37
|
-
require "factory_girl"
|
38
|
-
Dir[File.dirname(__FILE__) + "/support/**/*.rb"].each{ |file| require file }
|
39
|
-
|
40
|
-
# Load base/generic schema
|
41
|
-
require test_dir.join("schema/version")
|
42
|
-
require test_dir.join("schema/generic_schema")
|
43
|
-
adapter_schema = test_dir.join("schema/#{adapter}_schema.rb")
|
44
|
-
require adapter_schema if File.exists?(adapter_schema)
|
45
|
-
|
46
|
-
Dir[File.dirname(__FILE__) + "/models/*.rb"].each{ |file| require file }
|