Class: LowCardTables::LowCardTable::RowManager
- Inherits:
-
Object
- Object
- LowCardTables::LowCardTable::RowManager
- Defined in:
- lib/low_card_tables/low_card_table/row_manager.rb
Overview
In many ways, the RowManager is the beating heart of low_card_tables
. It is responsible for finding and creating rows in low-card tables, as well as maintaining the unique index across all columns in the table and dealing with any needs from migrations.
Because this class is quite complex, some pieces of functionality have been broken out into other classes. The TableUniqueIndex is responsible for maintaining the unique index across all columns in the table, and the RowCollapser handles the case where rows need to be collapsed (unified) because a column was removed from the low-card table.
Cache Notifications
This class uses the ActiveSupport::Notifications interface to notify anyone who’s interested of cache-related events. In particular, it fires the following events with the following payloads:
- low_card_tables.cache_load
-
{ :low_card_model => <ActiveRecord model class> }
; this is fired when the cache is loaded from the database, whether that’s the first time after startup or after a cache flush. - low_card_tables.cache_flush
-
{ :low_card_model => <ActiveRecord model class>, :reason => <some reason> }
; this is fired when there’s a cache that is flushed. Additional payload depends on the:reason
.
Reasons for low_card_tables.cache_flush
include:
- :manually_requested
-
You called
low_card_flush_cache!
on the low-card model. - :id_not_found
-
You requested a low-card row by ID, and we didn’t find that ID in the cache. We assume that the ID is likely valid and that it’s simply been created since we retrieved the cache from the database, so we flush the cache and try again.
:ids
is present in the payload, mapping to an array of one or more IDs – the ID or IDs that weren’t found in the cache. - :collapse_rows_and_update_referrers
-
The low-card table has been migrated and has had a column removed; we’ve collapsed any now-duplicate rows properly. As such, we need to flush the cache.
- :schema_change
-
We have detected that the schema of the low-card table has changed, and need to flush the cache.
- :creating_rows
-
We’re about to create one or more new rows in the low-card table, because a set of attributes that has never been seen before was asked for. Before we actually go try to create them, we lock the table and flush the cache, so that, in the case where some other process has already created them, we simply pick them up now. Then, after we create them, we flush the cache again to pick up the newly-created rows.
:context
is present in the payload, mapped to either:before_import
or:after_import
(corresponding to the two situations above).:new_rows
is also present in the payload, mapped to an array of one or more Hashes, each of which represents a unique combination of attributes to be created. - :stale
-
By far the most common case – the cache is simply stale based upon the current cache-expiration policy, and needs to be reloaded. The payload will contain
:loaded
, which is the time that the cache was loaded, and:now
, which is the time at which the cache was checked for validity. (:now
will always be very close to, but not after, the current time; any delay is just due to the time it took to receive the notification via ActiveSupport::Notifications.)
Instance Attribute Summary collapse
-
#low_card_model ⇒ Object
readonly
Returns the value of attribute low_card_model.
-
#referring_models ⇒ Object
readonly
Returns the value of attribute referring_models.
Instance Method Summary collapse
-
#all_rows ⇒ Object
Returns all rows in the low-card table.
-
#collapse_rows_and_update_referrers!(low_card_options = { }) ⇒ Object
Iterates through this table, finding duplicate rows and collapsing them.
-
#column_information_reset! ⇒ Object
Tells us that someone called #reset_column_information on the low-card table; we’ll inform all referring models of that fact.
-
#ensure_has_unique_index!(create_if_needed = false) ⇒ Object
If this table already has the correct unique index across all value columns, does nothing.
-
#find_ids_for(hash_hashes_object_or_objects) ⇒ Object
Behaves identically to #find_rows_for, except that it returns IDs instead of rows.
-
#find_or_create_ids_for(hash_hashes_object_or_objects) ⇒ Object
Behaves identically to #find_or_create_rows_for, except that it returns IDs instead of rows.
-
#find_or_create_rows_for(hash_hashes_object_or_objects) ⇒ Object
Given a single Hash specifying values for every column in the low-card table, returns an instance of the low-card table for that combination of values.
-
#find_rows_for(hash_hashes_object_or_objects) ⇒ Object
Given a single Hash specifying values for every column in the low-card table, returns an instance of the low-card table, already existing in the database, for that combination of values.
-
#flush_cache! ⇒ Object
Flushes the cache immediately (assuming we have any cached data at all).
-
#ids_matching(hash_or_hashes = nil, &block) ⇒ Object
Given a single Hash specifying zero or more constraints for low-card rows (i.e., mapping zero or more columns of the low-card table to specific values for those columns), returns a (possibly empty) Array of IDs of low-card rows that match those constraints.
-
#initialize(low_card_model) ⇒ RowManager
constructor
Creates a new instance for the given low-card model.
-
#referred_to_by(referring_model_class) ⇒ Object
Tells us that the low-card model we’re operating on behalf of is referenced by the given
referring_model_class
. -
#remove_unique_index! ⇒ Object
If this table currently has a unique index across all value columns, removes it.
-
#row_for_id(id) ⇒ Object
A synonym for #rows_for_ids.
-
#rows_for_ids(id_or_ids) ⇒ Object
Given a single primary-key ID of a low-card row, returns the row for that ID.
-
#rows_matching(hash_or_hashes = nil, &block) ⇒ Object
Given a single Hash specifying zero or more constraints for low-card rows (i.e., mapping zero or more columns of the low-card table to specific values for those columns), returns a (possibly empty) Array of low-card rows that match those constraints.
-
#value_column_names ⇒ Object
Returns the set of columns on the low-card table that we should consider “value columns” – i.e., those that contain data values, rather than metadata, like the primary key, created_at/updated_at, and so on.
Constructor Details
#initialize(low_card_model) ⇒ RowManager
Creates a new instance for the given low-card model.
58 59 60 61 62 63 64 65 66 |
# File 'lib/low_card_tables/low_card_table/row_manager.rb', line 58 def initialize(low_card_model) unless low_card_model.respond_to?(:is_low_card_table?) && low_card_model.is_low_card_table? raise ArgumentError, "You must supply a low-card AR model class, not: #{low_card_model.inspect}" end @low_card_model = low_card_model @table_unique_index = LowCardTables::LowCardTable::TableUniqueIndex.new(low_card_model) @referring_models = [ ] end |
Instance Attribute Details
#low_card_model ⇒ Object (readonly)
Returns the value of attribute low_card_model.
55 56 57 |
# File 'lib/low_card_tables/low_card_table/row_manager.rb', line 55 def low_card_model @low_card_model end |
#referring_models ⇒ Object (readonly)
Returns the value of attribute referring_models.
68 69 70 |
# File 'lib/low_card_tables/low_card_table/row_manager.rb', line 68 def referring_models @referring_models end |
Instance Method Details
#all_rows ⇒ Object
Returns all rows in the low-card table. This behaves semantically identically to simply calling ActiveRecord’s #all method on the low-card table itself, but it returns the data from cache.
92 93 94 |
# File 'lib/low_card_tables/low_card_table/row_manager.rb', line 92 def all_rows cache.all_rows end |
#collapse_rows_and_update_referrers!(low_card_options = { }) ⇒ Object
Iterates through this table, finding duplicate rows and collapsing them. See RowCollapser for far more information.
197 198 199 200 201 202 203 |
# File 'lib/low_card_tables/low_card_table/row_manager.rb', line 197 def collapse_rows_and_update_referrers!( = { }) collapser = LowCardTables::LowCardTable::RowCollapser.new(@low_card_model, ) collapse_map = collapser.collapse! flush!(:collapse_rows_and_update_referrers) collapse_map end |
#column_information_reset! ⇒ Object
Tells us that someone called #reset_column_information on the low-card table; we’ll inform all referring models of that fact.
86 87 88 |
# File 'lib/low_card_tables/low_card_table/row_manager.rb', line 86 def column_information_reset! @referring_models.each { |m| m._low_card_associations_manager.low_card_column_information_reset!(@low_card_model) } end |
#ensure_has_unique_index!(create_if_needed = false) ⇒ Object
If this table already has the correct unique index across all value columns, does nothing.
If this table does not have the correct unique index, and create_if_needed
is truthy, then creates the index. If this table does not have the correct unique index, and create_if_needed
is falsy, then raises LowCardTables::Errors::LowCardNoUniqueIndexError.
210 211 212 |
# File 'lib/low_card_tables/low_card_table/row_manager.rb', line 210 def ensure_has_unique_index!(create_if_needed = false) @table_unique_index.ensure_present!(create_if_needed) end |
#find_ids_for(hash_hashes_object_or_objects) ⇒ Object
Behaves identically to #find_rows_for, except that it returns IDs instead of rows.
174 175 176 |
# File 'lib/low_card_tables/low_card_table/row_manager.rb', line 174 def find_ids_for(hash_hashes_object_or_objects) row_map_to_id_map(find_rows_for(hash_hashes_object_or_objects)) end |
#find_or_create_ids_for(hash_hashes_object_or_objects) ⇒ Object
Behaves identically to #find_or_create_rows_for, except that it returns IDs instead of rows.
179 180 181 |
# File 'lib/low_card_tables/low_card_table/row_manager.rb', line 179 def find_or_create_ids_for(hash_hashes_object_or_objects) row_map_to_id_map(find_or_create_rows_for(hash_hashes_object_or_objects)) end |
#find_or_create_rows_for(hash_hashes_object_or_objects) ⇒ Object
Given a single Hash specifying values for every column in the low-card table, returns an instance of the low-card table for that combination of values. The row in question will be created if it doesn’t already exist.
Given an array of Hashes, each specifying values for every column in the low-card table, returns a Hash mapping each of those Hashes to an instance of the low-card table for that combination of values. Rows for any missing combinations of values will be created. (Creation is done in bulk, using activerecord_import
, so this method will be fast no matter how many rows need to be created.)
169 170 171 |
# File 'lib/low_card_tables/low_card_table/row_manager.rb', line 169 def find_or_create_rows_for(hash_hashes_object_or_objects) do_find_or_create(hash_hashes_object_or_objects, true) end |
#find_rows_for(hash_hashes_object_or_objects) ⇒ Object
Given a single Hash specifying values for every column in the low-card table, returns an instance of the low-card table, already existing in the database, for that combination of values.
Given an array of Hashes, each specifying values for every column in the low-card table, returns a Hash mapping each of those Hashes to an instance of the low-card table, already existing in the database, for that combination of values.
If you request an instance for a combination of values that doesn’t exist in the table, it will simply be mapped to nil
. Under no circumstances will rows be added to the database.
157 158 159 |
# File 'lib/low_card_tables/low_card_table/row_manager.rb', line 157 def find_rows_for(hash_hashes_object_or_objects) do_find_or_create(hash_hashes_object_or_objects, false) end |
#flush_cache! ⇒ Object
Flushes the cache immediately (assuming we have any cached data at all).
97 98 99 |
# File 'lib/low_card_tables/low_card_table/row_manager.rb', line 97 def flush_cache! flush!(:manually_requested) end |
#ids_matching(hash_or_hashes = nil, &block) ⇒ Object
Given a single Hash specifying zero or more constraints for low-card rows (i.e., mapping zero or more columns of the low-card table to specific values for those columns), returns a (possibly empty) Array of IDs of low-card rows that match those constraints.
Given an array of one or more Hashes, each of which specify zero or more constraints for low-card rows, returns a Hash mapping each of those Hashes to a (possibly empty) Array of IDs of low-card rows that match each Hash.
Given a block (in which case no hashes may be passed), returns an Array of IDs of low-card rows that match the block. The block is passed an instance of the low-card model class, and the return value of the block (truthy or falsy) determines whether the ID of that row is included in the return value or not.
129 130 131 |
# File 'lib/low_card_tables/low_card_table/row_manager.rb', line 129 def ids_matching(hash_or_hashes = nil, &block) do_matching(hash_or_hashes, block, :ids_matching) end |
#referred_to_by(referring_model_class) ⇒ Object
Tells us that the low-card model we’re operating on behalf of is referenced by the given referring_model_class
. This referring_model_class
should be an ActiveRecord class that has declared ‘has_low_card_table’ on this low-card table.
We keep track of this and expose it for a few reasons:
-
If we need to collapse the rows in this low-card table because a column has been removed, we use this list of referring models to know which columns have a foreign key to this table;
-
When someone calls #reset_column_information on the low-card table, we re-compute (and re-install) the set of delegated methods from all models that refer to this low-card table.
80 81 82 |
# File 'lib/low_card_tables/low_card_table/row_manager.rb', line 80 def referred_to_by(referring_model_class) @referring_models |= [ referring_model_class ] end |
#remove_unique_index! ⇒ Object
If this table currently has a unique index across all value columns, removes it.
215 216 217 |
# File 'lib/low_card_tables/low_card_table/row_manager.rb', line 215 def remove_unique_index! @table_unique_index.remove! end |
#row_for_id(id) ⇒ Object
A synonym for #rows_for_ids.
114 115 116 |
# File 'lib/low_card_tables/low_card_table/row_manager.rb', line 114 def row_for_id(id) rows_for_ids(id) end |
#rows_for_ids(id_or_ids) ⇒ Object
Given a single primary-key ID of a low-card row, returns the row for that ID. Given an Array of one or more primary-key IDs, returns a Hash mapping each of those IDs to the corresponding row. Properly flushes the cache and tries again if given an ID that doesn’t exist in cache.
104 105 106 107 108 109 110 111 |
# File 'lib/low_card_tables/low_card_table/row_manager.rb', line 104 def rows_for_ids(id_or_ids) begin cache.rows_for_ids(id_or_ids) rescue LowCardTables::Errors::LowCardIdNotFoundError => lcinfe flush!(:id_not_found, :ids => lcinfe.ids) cache.rows_for_ids(id_or_ids) end end |
#rows_matching(hash_or_hashes = nil, &block) ⇒ Object
Given a single Hash specifying zero or more constraints for low-card rows (i.e., mapping zero or more columns of the low-card table to specific values for those columns), returns a (possibly empty) Array of low-card rows that match those constraints.
Given an array of one or more Hashes, each of which specify zero or more constraints for low-card rows, returns a Hash mapping each of those Hashes to a (possibly empty) Array of low-card rows that match each Hash.
Given a block (in which case no hashes may be passed), returns an Array of low-card rows that match the block. The block is passed an instance of the low-card model class, and the return value of the block (truthy or falsy) determines whether that row is included in the return value or not.
144 145 146 |
# File 'lib/low_card_tables/low_card_table/row_manager.rb', line 144 def rows_matching(hash_or_hashes = nil, &block) do_matching(hash_or_hashes, block, :rows_matching) end |
#value_column_names ⇒ Object
Returns the set of columns on the low-card table that we should consider “value columns” – i.e., those that contain data values, rather than metadata, like the primary key, created_at/updated_at, and so on.
Columns that are excluded:
-
The primary key
-
created_at and updated_at
-
Any additional columns specified using the
:exclude_column_names
option when declaringis_low_card_table
.
191 192 193 |
# File 'lib/low_card_tables/low_card_table/row_manager.rb', line 191 def value_column_names value_columns.map(&:name) end |