Module: EnumerateBy::Bootstrapped
- Defined in:
- lib/enumerate_by.rb
Instance Method Summary collapse
-
#bootstrap(*records) ⇒ Object
Synchronizes the given records with existing ones.
-
#fast_bootstrap(*records) ⇒ Object
Quickly synchronizes the given records with the existing ones.
Instance Method Details
#bootstrap(*records) ⇒ Object
Synchronizes the given records with existing ones. This ensures that only the correct and most up-to-date records exist in the database. The sync process is as follows:
-
Any existing record that doesn’t match is deleted
-
Existing records with matches are updated based on the given attributes for that record
-
Records that don’t exist are created
To create records that can be referenced elsewhere in the database, an id should always be specified. Otherwise, records may change id each time they are bootstrapped.
Examples
class Color < ActiveRecord::Base
enumerate_by :name
bootstrap(
{:id => 1, :name => 'red'},
{:id => 2, :name => 'blue'},
{:id => 3, :name => 'green'}
)
end
In the above model, the colors table will be synchronized with the 3 records passed into the bootstrap helper. Any existing records that do not match those 3 are deleted. Otherwise, they are either created or updated with the attributes specified.
Defaults
In addition to always synchronizing certain attributes, an additional defaults option can be given to indicate that certain attributes should only be synchronized if they haven’t been modified in the database.
For example,
class Color < ActiveRecord::Base
enumerate_by :name
bootstrap(
{:id => 1, :name => 'red', :defaults => {:html => '#f00'}},
{:id => 2, :name => 'blue', :defaults => {:html => '#0f0'}},
{:id => 3, :name => 'green', :defaults => {:html => '#00f'}}
)
end
In the above model, the name attribute will always be updated on existing records in the database. However, the html attribute will only be synchronized if the attribute is nil in the database. Otherwise, any changes to that column remain there.
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 |
# File 'lib/enumerate_by.rb', line 266 def bootstrap(*records) uncached do # Remove records that are no longer being used records.flatten! ids = records.map {|record| record[:id]}.compact delete_all(ids.any? ? ['id NOT IN (?)', ids] : nil) # Find remaining existing records (to be updated) existing = all.inject({}) {|existing, record| existing[record.id] = record; existing} records.map! do |attributes| attributes.symbolize_keys! defaults = attributes.delete(:defaults) # Update with new attributes record = if record = existing[attributes[:id]] attributes.merge!(defaults.delete_if {|attribute, value| record.send("#{attribute}?")}) if defaults record.attributes = attributes record else attributes.merge!(defaults) if defaults new(attributes) end record.id = attributes[:id] # Force failed saves to stop execution record.save! record end records end end |
#fast_bootstrap(*records) ⇒ Object
Quickly synchronizes the given records with the existing ones. This skips ActiveRecord altogether, interacting directly with the connection instead. As a result, certain features are not available when being bootstrapped, including:
-
Callbacks
-
Validations
-
Transactions
-
Timestamps
-
Dirty attributes
Also note that records are created directly without creating instances of the model. As a result, all of the attributes for the record must be specified.
This produces a significant performance increase when bootstrapping more than several hundred records.
See EnumerateBy::Bootstrapped#bootstrap for information about usage.
319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 |
# File 'lib/enumerate_by.rb', line 319 def fast_bootstrap(*records) # Remove records that are no longer being used records.flatten! ids = records.map {|record| record[:id]}.compact delete_all(ids.any? ? ['id NOT IN (?)', ids] : nil) # Find remaining existing records (to be updated) quoted_table_name = self.quoted_table_name existing = connection.select_all("SELECT * FROM #{quoted_table_name}").inject({}) {|existing, record| existing[record['id'].to_i] = record; existing} records.each do |attributes| attributes.stringify_keys! if defaults = attributes.delete('defaults') defaults.stringify_keys! end id = attributes['id'] if existing_attributes = existing[id] # Record exists: Update attributes attributes.delete('id') attributes.merge!(defaults.delete_if {|attribute, value| !existing_attributes[attribute].nil?}) if defaults update_all(attributes, :id => id) else # Record doesn't exist: create new one attributes.merge!(defaults) if defaults column_names = [] values = [] attributes.each do |column_name, value| column_names << connection.quote_column_name(column_name) values << connection.quote(value, columns_hash[column_name]) end connection.insert( "INSERT INTO #{quoted_table_name} (#{column_names * ', '}) VALUES(#{values * ', '})", "#{name} Create", primary_key, id, sequence_name ) end end true end |