enumerate_by 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.rdoc CHANGED
@@ -1,5 +1,9 @@
1
1
  == master
2
2
 
3
+ == 0.4.1 / 2009-05-01
4
+
5
+ * Improve #fast_bootstrap speed by 50% by using the connection directly
6
+
3
7
  == 0.4.0 / 2009-04-30
4
8
 
5
9
  * Allow cache to be cleared on a per-enumeration basis
data/Rakefile CHANGED
@@ -5,7 +5,7 @@ require 'rake/contrib/sshpublisher'
5
5
 
6
6
  spec = Gem::Specification.new do |s|
7
7
  s.name = 'enumerate_by'
8
- s.version = '0.4.0'
8
+ s.version = '0.4.1'
9
9
  s.platform = Gem::Platform::RUBY
10
10
  s.summary = 'Adds support for declaring an ActiveRecord class as an enumeration'
11
11
 
data/lib/enumerate_by.rb CHANGED
@@ -261,16 +261,17 @@ module EnumerateBy
261
261
  defaults = attributes.delete(:defaults)
262
262
 
263
263
  # Update with new attributes
264
- record = !existing.include?(attributes[:id]) ? new(attributes) : begin
265
- record = existing[attributes[:id]]
266
- record.attributes = attributes
267
- record
268
- end
264
+ record =
265
+ if record = existing[attributes[:id]]
266
+ attributes.merge!(defaults.delete_if {|attribute, value| record.send("#{attribute}?")}) if defaults
267
+ record.attributes = attributes
268
+ record
269
+ else
270
+ attributes.merge!(defaults) if defaults
271
+ new(attributes)
272
+ end
269
273
  record.id = attributes[:id]
270
274
 
271
- # Only update defaults if they aren't already specified
272
- defaults.each {|attribute, value| record[attribute] = value unless record.send("#{attribute}?")} if defaults
273
-
274
275
  # Force failed saves to stop execution
275
276
  record.save!
276
277
  record
@@ -281,39 +282,63 @@ module EnumerateBy
281
282
  end
282
283
 
283
284
  # Quickly synchronizes the given records with the existing ones. This
284
- # disables certain features of ActiveRecord in order to provide a speed
285
- # boost, including:
285
+ # skips ActiveRecord altogether, interacting directly with the connection
286
+ # instead. As a result, certain features are not available when being
287
+ # bootstrapped, including:
286
288
  # * Callbacks
287
289
  # * Validations
290
+ # * Transactions
288
291
  # * Timestamps
289
292
  # * Dirty attributes
290
293
  #
291
- # This produces a noticeable performance increase when bootstrapping more
294
+ # Also note that records are created directly without creating instances
295
+ # of the model. As a result, all of the attributes for the record must
296
+ # be specified.
297
+ #
298
+ # This produces a significant performance increase when bootstrapping more
292
299
  # than several hundred records.
293
300
  #
294
301
  # See EnumerateBy::Bootstrapped#bootstrap for information about usage.
295
302
  def fast_bootstrap(*records)
296
- features = {:callbacks => %w(create create_or_update valid?), :dirty => %w(write_attribute), :validation => %w(save save!)}
297
- features.each do |feature, methods|
298
- methods.each do |method|
299
- method, punctuation = method.sub(/([?!=])$/, ''), $1
300
- alias_method "#{method}_without_bootstrap#{punctuation}", "#{method}#{punctuation}"
301
- alias_method "#{method}#{punctuation}", "#{method}_without_#{feature}#{punctuation}"
302
- end
303
- end
304
- original_record_timestamps = self.record_timestamps
305
- self.record_timestamps = false
303
+ # Remove records that are no longer being used
304
+ records.flatten!
305
+ delete_all(['id NOT IN (?)', records.map {|record| record[:id]}])
306
306
 
307
- bootstrap(*records)
308
- ensure
309
- features.each do |feature, methods|
310
- methods.each do |method|
311
- method, punctuation = method.sub(/([?!=])$/, ''), $1
312
- alias_method "#{method}_without_#{feature}#{punctuation}", "#{method}#{punctuation}"
313
- alias_method "#{method}#{punctuation}", "#{method}_without_bootstrap#{punctuation}"
307
+ # Find remaining existing records (to be updated)
308
+ quoted_table_name = self.quoted_table_name
309
+ existing = connection.select_all("SELECT * FROM #{quoted_table_name}").inject({}) {|existing, record| existing[record['id'].to_i] = record; existing}
310
+
311
+ records.each do |attributes|
312
+ attributes.stringify_keys!
313
+ if defaults = attributes.delete('defaults')
314
+ defaults.stringify_keys!
315
+ end
316
+
317
+ id = attributes['id']
318
+ if existing_attributes = existing[id]
319
+ # Record exists: Update attributes
320
+ attributes.delete('id')
321
+ attributes.merge!(defaults.delete_if {|attribute, value| !existing_attributes[attribute].nil?}) if defaults
322
+ update_all(attributes, :id => id)
323
+ else
324
+ # Record doesn't exist: create new one
325
+ attributes.merge!(defaults) if defaults
326
+ column_names = []
327
+ values = []
328
+
329
+ attributes.each do |column_name, value|
330
+ column_names << connection.quote_column_name(column_name)
331
+ values << connection.quote(value, columns_hash[column_name])
332
+ end
333
+
334
+ connection.insert(
335
+ "INSERT INTO #{quoted_table_name} (#{column_names * ', '}) VALUES(#{values * ', '})",
336
+ "#{name} Create", primary_key, id, sequence_name
337
+ )
314
338
  end
315
339
  end
316
- self.record_timestamps = original_record_timestamps
340
+
341
+ true
317
342
  end
318
343
  end
319
344
 
@@ -324,27 +324,81 @@ class EnumerationBootstrappedWithDefaultsTest < ActiveRecord::TestCase
324
324
  end
325
325
 
326
326
  class EnumerationFastBootstrappedTest < ActiveRecord::TestCase
327
- def test_should_not_run_validations
328
- assert_raise(ActiveRecord::StatementInvalid) { Color.fast_bootstrap({:id => 1, :name => nil}) }
327
+ def setup
328
+ @result = Color.fast_bootstrap(
329
+ {:id => 1, :name => 'red'},
330
+ {:id => 2, :name => 'green'}
331
+ )
332
+ end
333
+
334
+ def test_should_not_raise_exception_if_id_not_specified
335
+ assert_nothing_raised { Color.fast_bootstrap({:name => 'red'}, {:name => 'green'}) }
336
+ assert_equal 2, Color.count
329
337
  end
330
338
 
331
- def test_should_still_record_timestamps_after_bootstrap
332
- Color.fast_bootstrap({:id => 1, :name => 'red'})
333
- assert Color.record_timestamps
339
+ def test_should_raise_exception_if_query_fails
340
+ assert_raise(ActiveRecord::StatementInvalid) { Color.fast_bootstrap({:id => 1, :name => nil}, {:id => 2, :name => 'green'}) }
334
341
  end
335
342
 
336
- def test_should_still_run_validations_after_bootstrap
337
- Color.fast_bootstrap({:id => 1, :name => 'red'})
343
+ def test_should_flatten_bootstrap_records
344
+ Color.bootstrap(
345
+ [{:id => 1, :name => 'red'}],
346
+ [{:id => 2, :name => 'green'}]
347
+ )
348
+ assert_equal 2, Color.count
349
+ end
350
+
351
+ def test_should_create_records
352
+ assert @result
353
+ assert_not_nil Color.find_by_name('red')
354
+ assert_not_nil Color.find_by_name('green')
355
+ end
356
+ end
357
+
358
+ class EnumeratioFastBootstrappedWithExistingRecordsTest < ActiveRecord::TestCase
359
+ def setup
360
+ @red = create_color(:name => 'RED')
361
+ @green = create_color(:name => 'GREEN')
362
+
363
+ Color.fast_bootstrap(
364
+ {:id => @red.id, :name => 'red'},
365
+ {:id => @green.id, :name => 'green'}
366
+ )
338
367
 
339
- color = Color.new
340
- assert !color.save
341
- assert_raise(ActiveRecord::RecordInvalid) { color.save! }
368
+ @red.reload
369
+ @green.reload
342
370
  end
343
371
 
344
- def test_should_still_track_changed_attributes_after_bootstrap
345
- Color.fast_bootstrap({:id => 1, :name => 'red'})
372
+ def test_should_synchronize_all_attributes
373
+ assert_equal 'red', @red.name
374
+ assert_equal 'green', @green.name
375
+ end
376
+ end
377
+
378
+ class EnumerationFastBootstrappedWithDefaultsTest < ActiveRecord::TestCase
379
+ def setup
380
+ @red = create_color(:name => 'RED', :html => '#f00')
381
+ @green = create_color(:name => 'GREEN')
382
+
383
+ Color.fast_bootstrap(
384
+ {:id => @red.id, :name => 'red', :defaults => {:html => '#ff0000'}},
385
+ {:id => @green.id, :name => 'green', :defaults => {:html => '#00ff00'}}
386
+ )
346
387
 
347
- color = Color.new(:name => 'red')
348
- assert color.changed?
388
+ @red.reload
389
+ @green.reload
390
+ end
391
+
392
+ def test_should_update_all_non_default_attributes
393
+ assert_equal 'red', @red.name
394
+ assert_equal 'green', @green.name
395
+ end
396
+
397
+ def test_should_not_update_default_attributes_if_defined
398
+ assert_equal '#f00', @red.html
399
+ end
400
+
401
+ def test_should_update_default_attributes_if_not_defined
402
+ assert_equal '#00ff00', @green.html
349
403
  end
350
404
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: enumerate_by
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aaron Pfeifer
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-04-30 00:00:00 -04:00
12
+ date: 2009-05-01 00:00:00 -04:00
13
13
  default_executable:
14
14
  dependencies: []
15
15