Empact-activerecord-import 0.3.1 → 0.3.2
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.
- 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 }
|