activerecord-import 0.3.1 → 0.4.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.
- checksums.yaml +7 -0
- data/.gitignore +31 -0
- data/Brewfile +3 -0
- data/Gemfile +39 -0
- data/Rakefile +1 -24
- data/activerecord-import.gemspec +22 -0
- data/benchmarks/README +32 -0
- data/benchmarks/benchmark.rb +64 -0
- data/benchmarks/boot.rb +18 -0
- data/benchmarks/lib/base.rb +137 -0
- data/benchmarks/lib/cli_parser.rb +103 -0
- data/benchmarks/lib/float.rb +15 -0
- data/benchmarks/lib/mysql_benchmark.rb +22 -0
- data/benchmarks/lib/output_to_csv.rb +18 -0
- data/benchmarks/lib/output_to_html.rb +69 -0
- data/benchmarks/models/test_innodb.rb +3 -0
- data/benchmarks/models/test_memory.rb +3 -0
- data/benchmarks/models/test_myisam.rb +3 -0
- data/benchmarks/schema/mysql_schema.rb +16 -0
- data/gemfiles/3.1.gemfile +4 -0
- data/gemfiles/3.2.gemfile +4 -0
- data/gemfiles/4.0.gemfile +4 -0
- data/lib/activerecord-import/active_record/adapters/abstract_adapter.rb +0 -1
- data/lib/activerecord-import/active_record/adapters/em_mysql2_adapter.rb +8 -0
- data/lib/activerecord-import/adapters/abstract_adapter.rb +5 -58
- data/lib/activerecord-import/adapters/em_mysql2_adapter.rb +5 -0
- data/lib/activerecord-import/adapters/mysql_adapter.rb +50 -3
- data/lib/activerecord-import/adapters/sqlite3_adapter.rb +28 -3
- data/lib/activerecord-import/base.rb +3 -2
- data/lib/activerecord-import/em_mysql2.rb +7 -0
- data/lib/activerecord-import/import.rb +29 -29
- data/lib/activerecord-import/synchronize.rb +11 -10
- data/lib/activerecord-import/value_sets_parser.rb +54 -0
- data/lib/activerecord-import/version.rb +5 -0
- data/test/adapters/em_mysql2.rb +1 -0
- data/test/adapters/jdbcmysql.rb +1 -0
- data/test/adapters/mysql.rb +1 -0
- data/test/adapters/mysql2.rb +1 -0
- data/test/adapters/mysql2spatial.rb +1 -0
- data/test/adapters/mysqlspatial.rb +1 -0
- data/test/adapters/postgis.rb +1 -0
- data/test/adapters/postgresql.rb +1 -0
- data/test/adapters/seamless_database_pool.rb +1 -0
- data/test/adapters/spatialite.rb +1 -0
- data/test/adapters/sqlite3.rb +1 -0
- data/test/database.yml.sample +57 -0
- data/test/em_mysql2/import_test.rb +6 -0
- data/test/import_test.rb +350 -0
- data/test/jdbcmysql/import_test.rb +6 -0
- data/test/models/book.rb +3 -0
- data/test/models/group.rb +3 -0
- data/test/models/topic.rb +7 -0
- data/test/models/widget.rb +7 -0
- data/test/mysql/import_test.rb +6 -0
- data/test/mysql2/import_test.rb +6 -0
- data/test/mysqlspatial/import_test.rb +6 -0
- data/test/mysqlspatial2/import_test.rb +6 -0
- data/test/postgis/import_test.rb +4 -0
- data/test/postgresql/import_test.rb +4 -0
- data/test/schema/generic_schema.rb +104 -0
- data/test/schema/mysql_schema.rb +17 -0
- data/test/schema/version.rb +10 -0
- data/test/sqlite3/import_test.rb +52 -0
- data/test/support/active_support/test_case_extensions.rb +67 -0
- data/test/support/factories.rb +19 -0
- data/test/support/generate.rb +29 -0
- data/test/support/mysql/assertions.rb +55 -0
- data/test/support/mysql/import_examples.rb +147 -0
- data/test/support/postgresql/import_examples.rb +21 -0
- data/test/synchronize_test.rb +22 -0
- data/test/test_helper.rb +48 -0
- data/test/value_sets_bytes_parser_test.rb +96 -0
- data/test/value_sets_records_parser_test.rb +32 -0
- metadata +120 -58
- data/VERSION +0 -1
@@ -1,22 +1,22 @@
|
|
1
1
|
module ActiveRecord # :nodoc:
|
2
2
|
class Base # :nodoc:
|
3
|
-
|
3
|
+
|
4
4
|
# Synchronizes the passed in ActiveRecord instances with data
|
5
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
|
-
#
|
6
|
+
# ActiveRecord instance but it is intended for use on multiple instances.
|
7
|
+
#
|
8
8
|
# This uses one query for all instance updates and then updates existing
|
9
9
|
# instances rather sending one query for each instance
|
10
10
|
#
|
11
11
|
# == Examples
|
12
12
|
# # Synchronizing existing models by matching on the primary key field
|
13
|
-
# posts = Post.
|
13
|
+
# posts = Post.where(author: "Zach").first
|
14
14
|
# <.. out of system changes occur to change author name from Zach to Zachary..>
|
15
15
|
# Post.synchronize posts
|
16
16
|
# posts.first.author # => "Zachary" instead of Zach
|
17
|
-
#
|
17
|
+
#
|
18
18
|
# # Synchronizing using custom key fields
|
19
|
-
# posts = Post.
|
19
|
+
# posts = Post.where(author: "Zach").first
|
20
20
|
# <.. out of system changes occur to change the address of author 'Zach' to 1245 Foo Ln ..>
|
21
21
|
# Post.synchronize posts, [:name] # queries on the :name column and not the :id column
|
22
22
|
# posts.first.address # => "1245 Foo Ln" instead of whatever it was
|
@@ -26,23 +26,24 @@ module ActiveRecord # :nodoc:
|
|
26
26
|
|
27
27
|
conditions = {}
|
28
28
|
order = ""
|
29
|
-
|
29
|
+
|
30
30
|
key_values = keys.map { |key| instances.map(&"#{key}".to_sym) }
|
31
31
|
keys.zip(key_values).each { |key, values| conditions[key] = values }
|
32
32
|
order = keys.map{ |key| "#{key} ASC" }.join(",")
|
33
|
-
|
33
|
+
|
34
34
|
klass = instances.first.class
|
35
35
|
|
36
|
-
fresh_instances = klass.
|
36
|
+
fresh_instances = klass.where(conditions).order(order)
|
37
37
|
instances.each do |instance|
|
38
38
|
matched_instance = fresh_instances.detect do |fresh_instance|
|
39
39
|
keys.all?{ |key| fresh_instance.send(key) == instance.send(key) }
|
40
40
|
end
|
41
|
-
|
41
|
+
|
42
42
|
if matched_instance
|
43
43
|
instance.clear_aggregation_cache
|
44
44
|
instance.clear_association_cache
|
45
45
|
instance.instance_variable_set '@attributes', matched_instance.attributes
|
46
|
+
instance.instance_variable_set '@attributes_cache', {}
|
46
47
|
# Since the instance now accurately reflects the record in
|
47
48
|
# the database, ensure that instance.persisted? is true.
|
48
49
|
instance.instance_variable_set '@new_record', false
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module ActiveRecord::Import
|
2
|
+
class ValuesSetsBytesParser
|
3
|
+
attr_reader :reserved_bytes, :max_bytes, :values
|
4
|
+
|
5
|
+
def self.parse(values, options)
|
6
|
+
new(values, options).parse
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(values, options)
|
10
|
+
@values = values
|
11
|
+
@reserved_bytes = options[:reserved_bytes]
|
12
|
+
@max_bytes = options[:max_bytes]
|
13
|
+
end
|
14
|
+
|
15
|
+
def parse
|
16
|
+
value_sets = []
|
17
|
+
arr, current_arr_values_size, current_size = [], 0, 0
|
18
|
+
values.each_with_index do |val,i|
|
19
|
+
comma_bytes = arr.size
|
20
|
+
bytes_thus_far = reserved_bytes + current_size + val.bytesize + comma_bytes
|
21
|
+
if bytes_thus_far <= max_bytes
|
22
|
+
current_size += val.bytesize
|
23
|
+
arr << val
|
24
|
+
else
|
25
|
+
value_sets << arr
|
26
|
+
arr = [ val ]
|
27
|
+
current_size = val.bytesize
|
28
|
+
end
|
29
|
+
|
30
|
+
# if we're on the last iteration push whatever we have in arr to value_sets
|
31
|
+
value_sets << arr if i == (values.size-1)
|
32
|
+
end
|
33
|
+
|
34
|
+
[ *value_sets ]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class ValueSetsRecordsParser
|
39
|
+
attr_reader :max_records, :values
|
40
|
+
|
41
|
+
def self.parse(values, options)
|
42
|
+
new(values, options).parse
|
43
|
+
end
|
44
|
+
|
45
|
+
def initialize(values, options)
|
46
|
+
@values = values
|
47
|
+
@max_records = options[:max_records]
|
48
|
+
end
|
49
|
+
|
50
|
+
def parse
|
51
|
+
@values.in_groups_of(max_records, with_fill=false)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
ENV["ARE_DB"] = "em_mysql2"
|
@@ -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,57 @@
|
|
1
|
+
common: &common
|
2
|
+
username: root
|
3
|
+
password:
|
4
|
+
encoding: utf8
|
5
|
+
host: localhost
|
6
|
+
database: activerecord_import_test
|
7
|
+
|
8
|
+
mysql: &mysql
|
9
|
+
<<: *common
|
10
|
+
adapter: mysql
|
11
|
+
|
12
|
+
mysql2: &mysql2
|
13
|
+
<<: *common
|
14
|
+
adapter: mysql2
|
15
|
+
|
16
|
+
mysqlspatial:
|
17
|
+
<<: *mysql
|
18
|
+
|
19
|
+
mysql2spatial:
|
20
|
+
<<: *mysql2
|
21
|
+
|
22
|
+
em_mysql2:
|
23
|
+
<<: *common
|
24
|
+
adapter: em_mysql2
|
25
|
+
pool: 5
|
26
|
+
|
27
|
+
seamless_database_pool:
|
28
|
+
<<: *common
|
29
|
+
adapter: seamless_database_pool
|
30
|
+
pool_adapter: mysql2
|
31
|
+
master:
|
32
|
+
host: localhost
|
33
|
+
|
34
|
+
postgresql: &postgresql
|
35
|
+
<<: *common
|
36
|
+
username: postgres
|
37
|
+
adapter: postgresql
|
38
|
+
min_messages: warning
|
39
|
+
|
40
|
+
postgis:
|
41
|
+
<<: *postgresql
|
42
|
+
|
43
|
+
oracle:
|
44
|
+
<<: *common
|
45
|
+
adapter: oracle
|
46
|
+
min_messages: debug
|
47
|
+
|
48
|
+
sqlite:
|
49
|
+
adapter: sqlite
|
50
|
+
dbfile: test.db
|
51
|
+
|
52
|
+
sqlite3: &sqlite3
|
53
|
+
adapter: sqlite3
|
54
|
+
database: test.db
|
55
|
+
|
56
|
+
spatialite:
|
57
|
+
<<: *sqlite3
|
@@ -0,0 +1,6 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
|
2
|
+
|
3
|
+
require File.expand_path(File.dirname(__FILE__) + '/../support/mysql/assertions')
|
4
|
+
require File.expand_path(File.dirname(__FILE__) + '/../support/mysql/import_examples')
|
5
|
+
|
6
|
+
should_support_mysql_import_functionality
|
data/test/import_test.rb
ADDED
@@ -0,0 +1,350 @@
|
|
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
|
+
|
322
|
+
describe "importing when model has default_scope" do
|
323
|
+
it "doesn't import the default scope values" do
|
324
|
+
assert_difference "Widget.unscoped.count", +2 do
|
325
|
+
Widget.import [:w_id], [[1], [2]]
|
326
|
+
end
|
327
|
+
default_scope_value = Widget.scope_attributes[:active]
|
328
|
+
assert_not_equal default_scope_value, Widget.unscoped.find_by_w_id(1)
|
329
|
+
assert_not_equal default_scope_value, Widget.unscoped.find_by_w_id(2)
|
330
|
+
end
|
331
|
+
|
332
|
+
it "imports columns that are a part of the default scope using the value specified" do
|
333
|
+
assert_difference "Widget.unscoped.count", +2 do
|
334
|
+
Widget.import [:w_id, :active], [[1, true], [2, false]]
|
335
|
+
end
|
336
|
+
assert_not_equal true, Widget.unscoped.find_by_w_id(1)
|
337
|
+
assert_not_equal false, Widget.unscoped.find_by_w_id(2)
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
describe "importing serialized fields" do
|
342
|
+
it "imports values for serialized fields" do
|
343
|
+
assert_difference "Widget.unscoped.count", +1 do
|
344
|
+
Widget.import [:w_id, :data], [[1, {:a => :b}]]
|
345
|
+
end
|
346
|
+
assert_equal({:a => :b}, Widget.find_by_w_id(1).data)
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
end
|