jorahood-ar-extensions 0.9.2.3
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/ChangeLog +145 -0
- data/README +167 -0
- data/Rakefile +79 -0
- data/config/database.yml +7 -0
- data/config/database.yml.template +7 -0
- data/config/mysql.schema +72 -0
- data/config/postgresql.schema +39 -0
- data/db/migrate/generic_schema.rb +96 -0
- data/db/migrate/mysql_schema.rb +31 -0
- data/db/migrate/oracle_schema.rb +5 -0
- data/db/migrate/version.rb +4 -0
- data/init.rb +31 -0
- data/lib/ar-extensions/create_and_update.rb +509 -0
- data/lib/ar-extensions/csv.rb +309 -0
- data/lib/ar-extensions/delete.rb +143 -0
- data/lib/ar-extensions/extensions.rb +506 -0
- data/lib/ar-extensions/finder_options.rb +275 -0
- data/lib/ar-extensions/finders.rb +94 -0
- data/lib/ar-extensions/foreign_keys.rb +70 -0
- data/lib/ar-extensions/fulltext.rb +62 -0
- data/lib/ar-extensions/import.rb +352 -0
- data/lib/ar-extensions/insert_select.rb +178 -0
- data/lib/ar-extensions/synchronize.rb +30 -0
- data/lib/ar-extensions/temporary_table.rb +124 -0
- data/lib/ar-extensions/union.rb +204 -0
- data/lib/ar-extensions/version.rb +9 -0
- data/tests/connections/native_mysql/connection.rb +16 -0
- data/tests/connections/native_oracle/connection.rb +16 -0
- data/tests/connections/native_postgresql/connection.rb +19 -0
- data/tests/connections/native_sqlite/connection.rb +14 -0
- data/tests/connections/native_sqlite3/connection.rb +14 -0
- data/tests/fixtures/addresses.yml +25 -0
- data/tests/fixtures/books.yml +46 -0
- data/tests/fixtures/developers.yml +20 -0
- data/tests/fixtures/unit/active_record_base_finders/addresses.yml +25 -0
- data/tests/fixtures/unit/active_record_base_finders/books.yml +64 -0
- data/tests/fixtures/unit/active_record_base_finders/developers.yml +20 -0
- data/tests/fixtures/unit/synchronize/books.yml +16 -0
- data/tests/fixtures/unit/to_csv_headers/addresses.yml +8 -0
- data/tests/fixtures/unit/to_csv_headers/developers.yml +6 -0
- data/tests/fixtures/unit/to_csv_with_common_options/addresses.yml +40 -0
- data/tests/fixtures/unit/to_csv_with_common_options/developers.yml +13 -0
- data/tests/fixtures/unit/to_csv_with_common_options/languages.yml +29 -0
- data/tests/fixtures/unit/to_csv_with_common_options/teams.yml +3 -0
- data/tests/fixtures/unit/to_csv_with_default_options/developers.yml +7 -0
- data/tests/models/address.rb +4 -0
- data/tests/models/animal.rb +2 -0
- data/tests/models/book.rb +3 -0
- data/tests/models/cart_item.rb +4 -0
- data/tests/models/developer.rb +8 -0
- data/tests/models/group.rb +3 -0
- data/tests/models/language.rb +5 -0
- data/tests/models/mysql/book.rb +3 -0
- data/tests/models/mysql/test_innodb.rb +3 -0
- data/tests/models/mysql/test_memory.rb +3 -0
- data/tests/models/mysql/test_myisam.rb +3 -0
- data/tests/models/project.rb +2 -0
- data/tests/models/shopping_cart.rb +4 -0
- data/tests/models/team.rb +4 -0
- data/tests/models/topic.rb +13 -0
- data/tests/mysql/test_create_and_update.rb +290 -0
- data/tests/mysql/test_delete.rb +142 -0
- data/tests/mysql/test_finder_options.rb +121 -0
- data/tests/mysql/test_finders.rb +29 -0
- data/tests/mysql/test_import.rb +354 -0
- data/tests/mysql/test_insert_select.rb +173 -0
- data/tests/mysql/test_mysql_adapter.rb +45 -0
- data/tests/mysql/test_union.rb +81 -0
- data/tests/oracle/test_adapter.rb +14 -0
- data/tests/postgresql/test_adapter.rb +14 -0
- metadata +147 -0
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
require File.expand_path( File.join( File.dirname( __FILE__ ), '../test_helper') )
|
|
2
|
+
|
|
3
|
+
class CreateAndUpdateTest < TestCaseSuperClass
|
|
4
|
+
if ActiveRecord::Base.connection.class.name =~ /sqlite/i
|
|
5
|
+
self.use_transactional_fixtures = false
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def setup
|
|
9
|
+
super
|
|
10
|
+
Animal.delete_all
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
#Input Validation Tests
|
|
14
|
+
def test_replace_should_replace_existing_record_with_new_data
|
|
15
|
+
create_animal
|
|
16
|
+
assert_nil(@animal.size)
|
|
17
|
+
|
|
18
|
+
new_animal = Animal.new(:name => 'giraffe', :size => 'big')
|
|
19
|
+
assert_raise(ActiveRecord::StatementInvalid,
|
|
20
|
+
"Should not be able to save duplicate.") { new_animal.save }
|
|
21
|
+
|
|
22
|
+
new_animal.replace
|
|
23
|
+
|
|
24
|
+
validate_animal(:size => 'big', :name => 'giraffe')
|
|
25
|
+
assert(@animal.updated_at < @new_animal.updated_at)
|
|
26
|
+
assert(@animal.created_at < @new_animal.created_at)
|
|
27
|
+
assert(@new_animal.id > @animal.id)
|
|
28
|
+
|
|
29
|
+
assert_nil(Animal.find_by_id(@original_animal_id))
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
#Input Validation Tests
|
|
33
|
+
def test_create_ignore_without_duplicate_columns_should_throw_exception_for_missing_duplicate_columns
|
|
34
|
+
assert_raise(ArgumentError) {
|
|
35
|
+
test_create({:method => 'create'}, :ignore => true, :reload => true)
|
|
36
|
+
}
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def test_save_with_invalid_option_should_throw_exception
|
|
40
|
+
assert_raise(ArgumentError) {
|
|
41
|
+
test_save({:method => 'create'}, :ignore => true, :reload => true, :unknown_argument => true)
|
|
42
|
+
}
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
#Create tests
|
|
46
|
+
def test_create_ignore_should_ignore_existing_data
|
|
47
|
+
test_create({:method => 'create'}, :ignore => true)
|
|
48
|
+
validate_animal(:name => 'giraffe',
|
|
49
|
+
:updated_at => @animal.updated_at,
|
|
50
|
+
:created_at => @animal.created_at)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def test_create_ignore_with_reload_should_ignore_existing_data_then_reload
|
|
54
|
+
create_animal
|
|
55
|
+
|
|
56
|
+
@new_animal = Animal.create!({:name => 'giraffe', :size => 'little'}, :ignore => true)
|
|
57
|
+
assert(@new_animal.stale_record?)
|
|
58
|
+
assert_equal('little', @new_animal.size)
|
|
59
|
+
|
|
60
|
+
@new_animal.reload_duplicate :duplicate_columns => [:name]
|
|
61
|
+
assert(!@new_animal.stale_record?)
|
|
62
|
+
assert_nil(@new_animal.size)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def test_create_bang_ignore_should_ignore_existing_data
|
|
66
|
+
test_create({:method => 'create!'}, :ignore => true)
|
|
67
|
+
validate_animal(:name => 'giraffe',
|
|
68
|
+
:updated_at => @animal.updated_at,
|
|
69
|
+
:created_at => @animal.created_at)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def test_create_duplicate_should_update_duplicate_data
|
|
73
|
+
test_create({:method =>'create', :size => 'big'}, :on_duplicate_key_update => [:updated_at, :size])
|
|
74
|
+
validate_animal(:name => 'giraffe',
|
|
75
|
+
:size => 'big',
|
|
76
|
+
:created_at => @animal.created_at)
|
|
77
|
+
assert(@animal.updated_at < @new_animal.updated_at)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def test_create_bang_duplicate_should_update_duplicate_data
|
|
81
|
+
test_create({:method =>'create!', :size => 'big'}, :on_duplicate_key_update => [:updated_at, :size])
|
|
82
|
+
validate_animal(:name => 'giraffe',
|
|
83
|
+
:size => 'big',
|
|
84
|
+
:created_at => @animal.created_at)
|
|
85
|
+
assert(@animal.updated_at < @new_animal.updated_at)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
#Save insert tests
|
|
89
|
+
def test_save_ignore_should_ignore_existing_data
|
|
90
|
+
test_save({:method => 'save'}, :ignore => true)
|
|
91
|
+
validate_animal(:name => 'giraffe',
|
|
92
|
+
:updated_at => @animal.updated_at,
|
|
93
|
+
:created_at => @animal.created_at)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def test_save_bang_ignore_should_ignore_existing_data_and_reload
|
|
97
|
+
test_save({:method => 'save!'},
|
|
98
|
+
:ignore => true, :reload => true, :duplicate_columns => [:name])
|
|
99
|
+
validate_animal(:name => 'giraffe',
|
|
100
|
+
:updated_at => @animal.updated_at,
|
|
101
|
+
:created_at => @animal.created_at)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def test_save_duplicate_should_update_duplicate_data_and_reload
|
|
106
|
+
test_save({:method =>'save', :size => 'big'},
|
|
107
|
+
:on_duplicate_key_update => [:updated_at, :size],
|
|
108
|
+
:reload => true, :duplicate_columns => [:name])
|
|
109
|
+
|
|
110
|
+
validate_animal(:name => 'giraffe',
|
|
111
|
+
:size => 'big',
|
|
112
|
+
:created_at => @animal.created_at)
|
|
113
|
+
|
|
114
|
+
assert(@animal.updated_at < @new_animal.updated_at)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def test_save_bang_duplicate_should_update_duplicate_data
|
|
118
|
+
test_save({:method => 'save!', :size => 'big'},
|
|
119
|
+
:on_duplicate_key_update => [:updated_at, :size])
|
|
120
|
+
|
|
121
|
+
validate_animal(:name => 'giraffe',
|
|
122
|
+
:size => 'big',
|
|
123
|
+
:created_at => @animal.created_at)
|
|
124
|
+
|
|
125
|
+
assert(@animal.updated_at < @new_animal.updated_at)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def test_save_existing_record_should_just_save_without_reload
|
|
129
|
+
test_save_existing_record(:ignore => true)
|
|
130
|
+
assert_equal(@bear.name, 'giraffe')
|
|
131
|
+
assert_equal(@bear.size, 'huge')
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def test_save_existing_record_should_just_save_with_reload
|
|
135
|
+
test_save_existing_record(:ignore => true, :reload => true, :duplicate_columns => [:name])
|
|
136
|
+
assert_equal(@bear.name, 'giraffe')
|
|
137
|
+
assert_nil(@bear.size)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def test_save_existing_record_should_update_duplicate
|
|
141
|
+
test_save_existing_record(:on_duplicate_key_update => [:updated_at, :size], :reload => true, :duplicate_columns => [:name])
|
|
142
|
+
assert_equal(@bear.name, 'giraffe')
|
|
143
|
+
assert_equal(@bear.size, 'huge')
|
|
144
|
+
|
|
145
|
+
#ensure record was modified in database
|
|
146
|
+
@bear = Animal.find_by_name('giraffe')
|
|
147
|
+
assert_equal(@bear.name, 'giraffe')
|
|
148
|
+
assert_equal(@bear.size, 'huge')
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
#RELOAD tests
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def test_create_ignore_with_reload_should_ignore_existing_data_then_reload
|
|
155
|
+
create_animal
|
|
156
|
+
|
|
157
|
+
@new_animal = Animal.create!({:name => 'giraffe', :size => 'little'}, :ignore => true)
|
|
158
|
+
assert(@new_animal.stale_record?)
|
|
159
|
+
assert_equal('little', @new_animal.size)
|
|
160
|
+
|
|
161
|
+
@new_animal.reload_duplicate :duplicate_columns => [:name]
|
|
162
|
+
assert(!@new_animal.stale_record?)
|
|
163
|
+
assert_nil(@new_animal.size)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def test_save_should_update_existing_data_then_reload_later
|
|
167
|
+
create_animal
|
|
168
|
+
|
|
169
|
+
@new_animal = Animal.new :name => 'giraffe', :size => 'little'
|
|
170
|
+
@new_animal.save!(:on_duplicate_key_update => [:updated_at])
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
assert(@new_animal.stale_record?)
|
|
174
|
+
assert_equal(0, @new_animal.id)
|
|
175
|
+
assert_equal('little', @new_animal.size)
|
|
176
|
+
assert(@animal.updated_at < @new_animal.updated_at)
|
|
177
|
+
new_updated = @new_animal.updated_at
|
|
178
|
+
|
|
179
|
+
@new_animal.reload_duplicate :duplicate_columns => [:name]
|
|
180
|
+
|
|
181
|
+
assert(!@new_animal.stale_record?)
|
|
182
|
+
assert_nil(@new_animal.size)
|
|
183
|
+
assert_equal(new_updated.to_s, @new_animal.updated_at.to_s)
|
|
184
|
+
assert_equal(@animal.id, @new_animal.id)
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def test_update_should_delete_duplicate_record_on_reload
|
|
188
|
+
create_animal
|
|
189
|
+
|
|
190
|
+
@new_animal = Animal.create! :name => 'bear', :size => 'little'
|
|
191
|
+
@new_animal.name = 'giraffe'
|
|
192
|
+
assert(@new_animal.id > @animal.id)
|
|
193
|
+
|
|
194
|
+
@new_animal.reload_duplicate :force => true, :duplicate_columns => [:name]
|
|
195
|
+
assert(!@new_animal.stale_record?)
|
|
196
|
+
assert_nil(@new_animal.size)
|
|
197
|
+
assert_equal(@animal.updated_at.to_s, @new_animal.updated_at.to_s)
|
|
198
|
+
assert_equal(@animal.id, @new_animal.id)
|
|
199
|
+
assert_nil(Animal.find_by_name('bear'))
|
|
200
|
+
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
protected
|
|
204
|
+
|
|
205
|
+
def test_create(options, ex_options)
|
|
206
|
+
method = options.delete(:method)
|
|
207
|
+
create_animal
|
|
208
|
+
assert_raises(ActiveRecord::StatementInvalid,
|
|
209
|
+
"Should not be able to create duplicate.") { Animal.send(method, {:name => 'giraffe'}) }
|
|
210
|
+
@new_animal = Animal.send(method, {:name => 'giraffe'}.merge(options), ex_options)
|
|
211
|
+
assert(@new_animal)
|
|
212
|
+
validate_state_state(ex_options)
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def test_save(options, ex_options)
|
|
216
|
+
method = options.delete(:method)
|
|
217
|
+
create_animal
|
|
218
|
+
@new_animal = Animal.new({:name => 'giraffe'}.merge(options))
|
|
219
|
+
assert_raise(ActiveRecord::StatementInvalid,
|
|
220
|
+
"Should not be able to save duplicate.") { @new_animal.send(method) }
|
|
221
|
+
assert(@new_animal.send(method, ex_options))
|
|
222
|
+
validate_state_state(ex_options)
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def validate_state_state(ex_options)
|
|
226
|
+
if ex_options[:reload]
|
|
227
|
+
assert_equal(@new_animal.created_at.to_s, @animal.created_at.to_s)
|
|
228
|
+
assert(!@new_animal.stale_record?)
|
|
229
|
+
assert_equal(@original_animal_id, @new_animal.id)
|
|
230
|
+
else
|
|
231
|
+
assert_equal(0, @new_animal.id)
|
|
232
|
+
assert(@new_animal.created_at > @animal.created_at)
|
|
233
|
+
assert(@new_animal.stale_record?)
|
|
234
|
+
assert(Animal.find_by_name('giraffe'))
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def test_save_existing_record(save_options)
|
|
239
|
+
@giraffe = create_animal
|
|
240
|
+
|
|
241
|
+
@bear = create_animal(:name => 'bear')
|
|
242
|
+
@bear.name = 'giraffe'
|
|
243
|
+
@bear.size = 'huge'
|
|
244
|
+
|
|
245
|
+
@original_bear_id = @bear.id
|
|
246
|
+
@original_giraffe_id = @giraffe.id
|
|
247
|
+
|
|
248
|
+
assert_raises(ActiveRecord::StatementInvalid,
|
|
249
|
+
"Should not be able to create duplicate."){ @bear.save }
|
|
250
|
+
|
|
251
|
+
@bear.save(save_options)
|
|
252
|
+
assert(Animal.find_by_name('giraffe'))
|
|
253
|
+
|
|
254
|
+
if save_options[:reload] || (save_options[:on_duplicate_key_update] && save_options[:duplicate_columns])
|
|
255
|
+
assert_equal(@original_giraffe_id, @bear.id)
|
|
256
|
+
assert(!@bear.stale_record?)
|
|
257
|
+
assert_nil(Animal.find_by_name('bear'))
|
|
258
|
+
else
|
|
259
|
+
assert_equal(@original_bear_id, @bear.id)
|
|
260
|
+
assert(@bear.stale_record?)
|
|
261
|
+
assert(Animal.find_by_name('bear'))
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
def create_animal(options={})
|
|
266
|
+
Animal.create!(options.reverse_merge(:name => 'giraffe'))
|
|
267
|
+
|
|
268
|
+
#back set the date so we can compare the new timestamp
|
|
269
|
+
reset_time = Time.now - 1.day
|
|
270
|
+
Animal.update_all(['updated_at = ?, created_at = ?', reset_time, reset_time])
|
|
271
|
+
|
|
272
|
+
@animal = Animal.find_by_name options[:name] || 'giraffe'
|
|
273
|
+
@original_animal_id = @animal.id
|
|
274
|
+
@animal
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
def validate_animal(fields)
|
|
278
|
+
@new_animal = Animal.find_by_name fields[:name]
|
|
279
|
+
assert(@new_animal)
|
|
280
|
+
|
|
281
|
+
assert(@new_animal.updated_at)
|
|
282
|
+
assert(@new_animal.created_at)
|
|
283
|
+
|
|
284
|
+
fields.each do|field, exp_val|
|
|
285
|
+
assert_equal(exp_val.to_s, @new_animal.send(field).to_s,
|
|
286
|
+
"Expecting #{exp_val} for #{field} but got #{@new_animal.send(field)}")
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
end
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
require File.expand_path( File.join( File.dirname( __FILE__ ), '../test_helper') )
|
|
2
|
+
|
|
3
|
+
{:execute => [/^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/],
|
|
4
|
+
:insert => [],
|
|
5
|
+
:delete => []
|
|
6
|
+
}.each do |method, ignore_list|
|
|
7
|
+
|
|
8
|
+
ActiveRecord::Base.connection.class.class_eval <<-END_SRC
|
|
9
|
+
cattr_accessor :#{method}_count
|
|
10
|
+
# Array of regexes of queries that are not counted against query_count
|
|
11
|
+
@@ignore_#{method}_list = #{ignore_list.inspect}
|
|
12
|
+
alias_method :#{method}_without_query_counting, :#{method}
|
|
13
|
+
def #{method}_with_query_counting(sql, name = nil, &block)
|
|
14
|
+
self.#{method}_count += 1 unless @@ignore_#{method}_list.any? { |r| sql =~ r }
|
|
15
|
+
#{method}_without_query_counting(sql, name, &block)
|
|
16
|
+
end
|
|
17
|
+
END_SRC
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
class DeleteTestCaseSuperClass < TestCaseSuperClass
|
|
21
|
+
protected
|
|
22
|
+
|
|
23
|
+
def assert_query(method, num = 1)
|
|
24
|
+
ActiveRecord::Base.connection.class.class_eval do
|
|
25
|
+
self.send "#{method}_count=", 0
|
|
26
|
+
alias_method method, "#{method}_with_query_counting"
|
|
27
|
+
end
|
|
28
|
+
yield
|
|
29
|
+
ensure
|
|
30
|
+
ActiveRecord::Base.connection.class.class_eval do
|
|
31
|
+
alias_method method, "#{method}_without_query_counting"
|
|
32
|
+
end
|
|
33
|
+
count = ActiveRecord::Base.connection.send "#{method}_count"
|
|
34
|
+
assert_equal num, count, "#{count} instead of #{num} #{method} queries were executed."
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class DeleteTest < DeleteTestCaseSuperClass
|
|
41
|
+
|
|
42
|
+
def setup
|
|
43
|
+
super
|
|
44
|
+
insert_books
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def test_delete_limit_should_only_delete_three_records
|
|
48
|
+
count = Book.count(:all, :conditions => ['author_name = ?', 'giraffe'])
|
|
49
|
+
assert_equal 50, count
|
|
50
|
+
|
|
51
|
+
deleted = Book.delete_all(['author_name = ?', 'giraffe'], :limit => 24)
|
|
52
|
+
|
|
53
|
+
assert_equal 24, deleted
|
|
54
|
+
|
|
55
|
+
count = Book.count(:all, :conditions => ['author_name = ?', 'giraffe'])
|
|
56
|
+
assert_equal 26, count
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def test_delete_limit_should_delete_less_than_limit
|
|
60
|
+
count = Book.count(:all, :conditions => ['author_name = ?', 'giraffe'])
|
|
61
|
+
assert_equal 50, count
|
|
62
|
+
|
|
63
|
+
deleted = Book.delete_all(['author_name = ?', 'giraffe'], :limit => 55)
|
|
64
|
+
assert_equal(50, deleted)
|
|
65
|
+
|
|
66
|
+
count = Book.count(:all, :conditions => ['author_name = ?', 'giraffe'])
|
|
67
|
+
assert_equal 0, count
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def test_delete_batch_should_execute_6_deletes
|
|
71
|
+
assert_query(:delete, 6){
|
|
72
|
+
assert_equal(50, Book.delete_all(nil, :batch => 10))
|
|
73
|
+
}
|
|
74
|
+
assert_equal 0, Book.count
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def test_delete_batch_with_limit_should_delete_until_limit_reached
|
|
78
|
+
assert_query(:delete, 4){
|
|
79
|
+
assert_equal(32, Book.delete_all('id > 0', :batch => 10, :limit => 32))
|
|
80
|
+
}
|
|
81
|
+
assert_equal 18, Book.count
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def test_delete_batch_default_should_delete_all
|
|
86
|
+
assert_query(:delete, 1){
|
|
87
|
+
assert_equal(50, Book.delete_all(nil, :batch => true))
|
|
88
|
+
}
|
|
89
|
+
assert_equal 0, Book.count
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def test_delete_batch_bigger_than_limit_should_delete_limit
|
|
94
|
+
assert_query(:delete, 1){
|
|
95
|
+
assert_equal(12, Book.delete_all(nil, :batch => 20, :limit => 12))
|
|
96
|
+
}
|
|
97
|
+
assert_equal 38, Book.count
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def test_delete_duplicates_should_leave_one_by_author_with_low_id
|
|
102
|
+
min_id = Book.minimum :id
|
|
103
|
+
|
|
104
|
+
Book.delete_duplicates(:fields => [:author_name])
|
|
105
|
+
assert_equal 0, Book.count(:all, :group => :author_name, :having => 'count(*) > 1' ).length
|
|
106
|
+
|
|
107
|
+
assert_equal(min_id, Book.find_by_author_name('giraffe').id)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def test_delete_duplicates_should_delete_all_but_one_with_publisher_one
|
|
112
|
+
|
|
113
|
+
min_id = Book.minimum :id
|
|
114
|
+
|
|
115
|
+
Book.delete_duplicates(:fields => [:author_name], :conditions => ['c1.publisher = ? and c2.publisher = ?', 'Pub1', 'Pub1'])
|
|
116
|
+
|
|
117
|
+
books = Book.find_all_by_author_name('giraffe', :order => :id)
|
|
118
|
+
assert_equal 41, books.length
|
|
119
|
+
|
|
120
|
+
assert_equal min_id, books.first.id
|
|
121
|
+
|
|
122
|
+
assert_equal 1, Book.find_all_by_author_name_and_publisher('giraffe', 'Pub1').length
|
|
123
|
+
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def test_delete_duplicates_should_delete_all_but_five
|
|
127
|
+
Book.delete_duplicates(:fields => [:author_name, :publisher])
|
|
128
|
+
assert_equal(5, Book.count)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
protected
|
|
134
|
+
def insert_books
|
|
135
|
+
Book.delete_all
|
|
136
|
+
1.upto(50){|count|
|
|
137
|
+
Book.create!(:author_name => 'giraffe', :title => "Title#{count}", :publisher => "Pub#{count % 5}")
|
|
138
|
+
}
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
end
|
|
142
|
+
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
require File.expand_path( File.join( File.dirname( __FILE__ ), '..', 'test_helper' ))
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class FinderOptionsTest < TestCaseSuperClass
|
|
5
|
+
include ActiveRecord::ConnectionAdapters
|
|
6
|
+
self.fixture_path = File.join( File.dirname( __FILE__ ), 'fixtures/unit/active_record_base_finders' )
|
|
7
|
+
self.fixtures 'books'
|
|
8
|
+
|
|
9
|
+
def setup
|
|
10
|
+
@connection = ActiveRecord::Base.connection
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def teardown
|
|
14
|
+
Topic.delete_all
|
|
15
|
+
Book.delete_all
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def test_find_with_having_should_add_having_option_to_sql
|
|
19
|
+
create_books_and_topics
|
|
20
|
+
|
|
21
|
+
#test having without associations
|
|
22
|
+
books = Book.find(:all, :select => 'count(*) as count_all, topic_id', :group => :topic_id, :having => 'count(*) > 1')
|
|
23
|
+
assert_equal 2, books.size
|
|
24
|
+
|
|
25
|
+
#test having with associations
|
|
26
|
+
books = Book.find(:all,
|
|
27
|
+
:include => :topic,
|
|
28
|
+
:conditions => " topics.id is not null", #the conditions forces eager loading in Rails 2.2
|
|
29
|
+
:select => 'count(*) as count_all, topic_id',
|
|
30
|
+
:group => :topic_id,
|
|
31
|
+
:having => 'count(*) > 1')
|
|
32
|
+
|
|
33
|
+
assert_equal 2, books.size
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def test_finder_sql_to_string_should_select_from_one_table
|
|
37
|
+
book_sql = Book.finder_sql_to_string(:select => 'topic_id', :include => :topic)
|
|
38
|
+
|
|
39
|
+
# current generated output looks like this now:
|
|
40
|
+
# SELECT topic_id FROM `books`
|
|
41
|
+
assert(/^SELECT\s/.match(book_sql))
|
|
42
|
+
assert_nil(/\sJOIN\s/.match(book_sql))
|
|
43
|
+
(Book.column_names - ['topic_id']).each do |col|
|
|
44
|
+
assert_nil(book_sql.match(Regexp.new("^SELECT .*[`\s]#{col}.*FROM")))
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
assert(/^SELECT\s.*topic_id.*FROM/.match(book_sql))
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def test_finder_sql_to_string_w_forced_eager_load_should_generate_sql_with_joined_table
|
|
51
|
+
book_sql = Book.finder_sql_to_string(:select => 'topic_id',
|
|
52
|
+
:include => :topic,
|
|
53
|
+
:force_eager_load => true)
|
|
54
|
+
assert(/^SELECT\s/.match(book_sql))
|
|
55
|
+
assert(/\sJOIN\s/.match(book_sql))
|
|
56
|
+
|
|
57
|
+
#assert that each column was included in the sql
|
|
58
|
+
Book.column_names.each do |col|
|
|
59
|
+
assert(book_sql.match(Regexp.new("^SELECT .*[`\s\.]#{col}.*FROM")))
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# current generated output looks like this now:
|
|
63
|
+
# SELECT `books`.`id` AS t0_r0, `books`.`title` AS t0_r1, `books`.`publisher` AS t0_r2, `books`.`author_name` AS t0_r3, `books`.`created_at` AS t0_r4, `books`.`created_on` AS t0_r5, `books`.`updated_at` AS t0_r6, `books`.`updated_on` AS t0_r7, `books`.`topic_id` AS t0_r8, `books`.`for_sale` AS t0_r9, `topics`.`id` AS t1_r0, `topics`.`title` AS t1_r1, `topics`.`author_name` AS t1_r2, `topics`.`author_email_address` AS t1_r3, `topics`.`written_on` AS t1_r4, `topics`.`bonus_time` AS t1_r5, `topics`.`last_read` AS t1_r6, `topics`.`content` AS t1_r7, `topics`.`approved` AS t1_r8, `topics`.`replies_count` AS t1_r9, `topics`.`parent_id` AS t1_r10, `topics`.`type` AS t1_r11, `topics`.`created_at` AS t1_r12, `topics`.`updated_at` AS t1_r13 FROM `books` LEFT OUTER JOIN `topics` ON `topics`.id = `books`.topic_id
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def test_finder_sql_to_string_should_generate_sql_with_joined_table
|
|
67
|
+
conditions = 'topics.id is not null'
|
|
68
|
+
book_sql = Book.finder_sql_to_string(:select => 'topic_id', :include => :topic, :conditions => conditions)
|
|
69
|
+
|
|
70
|
+
assert(/^SELECT\s/.match(book_sql))
|
|
71
|
+
assert(/\sJOIN\s/.match(book_sql))
|
|
72
|
+
assert(book_sql.include?(conditions))
|
|
73
|
+
|
|
74
|
+
#assert that each column was included in the sql
|
|
75
|
+
Book.column_names.each do |col|
|
|
76
|
+
assert(book_sql.match(Regexp.new("^SELECT .*[`\s\.]#{col}.*FROM")))
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# current generated output looks like this now:
|
|
80
|
+
#"SELECT `books`.`id` AS t0_r0, `books`.`title` AS t0_r1, `books`.`publisher` AS t0_r2, `books`.`author_name` AS t0_r3, `books`.`created_at` AS t0_r4, `books`.`created_on` AS t0_r5, `books`.`updated_at` AS t0_r6, `books`.`updated_on` AS t0_r7, `books`.`topic_id` AS t0_r8, `books`.`for_sale` AS t0_r9, `topics`.`id` AS t1_r0, `topics`.`title` AS t1_r1, `topics`.`author_name` AS t1_r2, `topics`.`author_email_address` AS t1_r3, `topics`.`written_on` AS t1_r4, `topics`.`bonus_time` AS t1_r5, `topics`.`last_read` AS t1_r6, `topics`.`content` AS t1_r7, `topics`.`approved` AS t1_r8, `topics`.`replies_count` AS t1_r9, `topics`.`parent_id` AS t1_r10, `topics`.`type` AS t1_r11, `topics`.`created_at` AS t1_r12, `topics`.`updated_at` AS t1_r13 FROM `books` LEFT OUTER JOIN `topics` ON `topics`.id = `books`.topic_id WHERE (topics.id is not null)"
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def test_pre_sql_should_ensure_pre_sql_option_is_added_to_beginning_of_sql
|
|
84
|
+
book_sql = Book.finder_sql_to_string(:select => 'topic_id', :pre_sql => "/* BLAH */")
|
|
85
|
+
assert(/^\/\* BLAH \*\/\sSELECT/.match(book_sql))
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def test_pre_sql_should_ensure_pre_sql_option_is_added_to_beginning_of_sql_with_eager_loading
|
|
89
|
+
book_sql = Book.finder_sql_to_string(:select => 'topic_id', :pre_sql => "/* BLAH */", :include => :topic, :conditions => 'topics.id is not null')
|
|
90
|
+
assert(/^\/\* BLAH \*\/\sSELECT/.match(book_sql))
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def test_pre_sql_should_ensure_post_sql_option_is_added_to_end_of_sql
|
|
94
|
+
book_sql = Book.finder_sql_to_string(:select => 'topic_id', :post_sql => "/* BLAH */")
|
|
95
|
+
assert(/\s\/\* BLAH \*\/$/.match(book_sql))
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def test_pre_sql_should_ensure_post_sql_option_is_added_to_end_of_sql_with_eager_loading
|
|
99
|
+
book_sql = Book.finder_sql_to_string(:select => 'topic_id', :post_sql => "/* BLAH */", :include => :topic, :conditions => 'topics.id is not null')
|
|
100
|
+
assert(/\s\/\* BLAH \*\/$/.match(book_sql))
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
protected
|
|
104
|
+
|
|
105
|
+
def create_books_and_topics
|
|
106
|
+
Book.destroy_all
|
|
107
|
+
Topic.destroy_all
|
|
108
|
+
|
|
109
|
+
topics = [Topic.create!(:title => 'My Topic', :author_name => 'Giraffe'),
|
|
110
|
+
Topic.create!(:title => 'Other Topic', :author_name => 'Giraffe'),
|
|
111
|
+
Topic.create!(:title => 'Last Topic', :author_name => 'Giraffe')]
|
|
112
|
+
|
|
113
|
+
Book.create!(:title => 'Title A', :topic_id => topics[0].to_param, :author_name => 'Giraffe')
|
|
114
|
+
Book.create!(:title => 'Title B', :topic_id => topics[0].to_param, :author_name => 'Giraffe')
|
|
115
|
+
Book.create!(:title => 'Title C', :topic_id => topics[0].to_param, :author_name => 'Giraffe')
|
|
116
|
+
Book.create!(:title => 'Title D', :topic_id => topics[1].to_param, :author_name => 'Giraffe')
|
|
117
|
+
Book.create!(:title => 'Title E', :topic_id => topics[1].to_param, :author_name => 'Giraffe')
|
|
118
|
+
Book.create!(:title => 'Title F', :topic_id => topics[2].to_param, :author_name => 'Giraffe')
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
end
|