Class: CounterCulture::Counter
- Inherits:
-
Object
- Object
- CounterCulture::Counter
- Defined in:
- lib/counter_culture/counter.rb
Constant Summary collapse
- CONFIG_OPTIONS =
[ :column_names, :counter_cache_name, :delta_column, :foreign_key_values, :touch, :delta_magnitude, :execute_after_commit ]
Instance Method Summary collapse
-
#change_counter_cache(obj, options) ⇒ Object
increments or decrements a counter cache.
-
#counter_cache_name_for(obj) ⇒ Object
Gets the name of the counter cache for a specific object.
-
#counter_delta_magnitude_for(obj) ⇒ Object
Gets the delta magnitude of the counter cache for a specific object.
-
#first_level_relation_foreign_key ⇒ Object
gets the foreign key name of the relation.
-
#foreign_key_value(obj, relation, was = false) ⇒ Object
gets the value of the foreign key on the given relation.
-
#full_primary_key(klass) ⇒ Object
the string to pass to order() in order to sort by primary key.
-
#initialize(model, relation, options) ⇒ Counter
constructor
A new instance of Counter.
- #previous_model(obj) ⇒ Object
-
#relation_foreign_key(relation) ⇒ Object
gets the foreign key name of the given relation.
-
#relation_klass(relation) ⇒ Object
gets the class of the given relation.
-
#relation_primary_key(relation) ⇒ Object
gets the primary key name of the given relation.
-
#relation_reflect(relation) ⇒ Object
gets the reflect object on the given relation.
Constructor Details
#initialize(model, relation, options) ⇒ Counter
Returns a new instance of Counter.
7 8 9 10 11 12 13 14 15 16 17 18 |
# File 'lib/counter_culture/counter.rb', line 7 def initialize(model, relation, ) @model = model @relation = relation.is_a?(Enumerable) ? relation : [relation] @counter_cache_name = .fetch(:column_name, "#{model.name.tableize}_count") @column_names = [:column_names] @delta_column = [:delta_column] @foreign_key_values = [:foreign_key_values] @touch = .fetch(:touch, false) @delta_magnitude = [:delta_magnitude] || 1 @execute_after_commit = .fetch(:execute_after_commit, false) end |
Instance Method Details
#change_counter_cache(obj, options) ⇒ Object
increments or decrements a counter cache
options:
:increment => true to increment, false to decrement
:relation => which relation to increment the count on,
:counter_cache_name => the column name of the counter cache
:counter_column => overrides :counter_cache_name
:delta_column => override the default count delta (1) with the value of this column in the counted record
:was => whether to get the current value or the old value of the
first part of the relation
:execute_after_commit => execute the column update outside of the transaction to avoid deadlocks
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/counter_culture/counter.rb', line 31 def change_counter_cache(obj, ) change_counter_column = .fetch(:counter_column) { counter_cache_name_for(obj) } # default to the current foreign key value id_to_change = foreign_key_value(obj, relation, [:was]) # allow overwriting of foreign key value by the caller id_to_change = foreign_key_values.call(id_to_change) if foreign_key_values if id_to_change && change_counter_column delta_magnitude = if delta_column delta_attr_name = [:was] ? "#{delta_column}_was" : delta_column obj.send(delta_attr_name) || 0 else counter_delta_magnitude_for(obj) end execute_change_counter_cache(obj, ) do # increment or decrement? operator = [:increment] ? '+' : '-' # we don't use Rails' update_counters because we support changing the timestamp quoted_column = model.connection.quote_column_name(change_counter_column) updates = [] # this updates the actual counter updates << "#{quoted_column} = COALESCE(#{quoted_column}, 0) #{operator} #{delta_magnitude}" # and here we update the timestamp, if so desired if touch current_time = obj.send(:current_time_from_proper_timezone) = obj.send(:timestamp_attributes_for_update_in_model) << touch if touch != true .each do || updates << "#{} = '#{current_time.to_formatted_s(:db)}'" end end klass = relation_klass(relation) klass.where(relation_primary_key(relation) => id_to_change).update_all updates.join(', ') end end end |
#counter_cache_name_for(obj) ⇒ Object
Gets the name of the counter cache for a specific object
obj: object to calculate the counter cache name for cache_name_finder: object used to calculate the cache name
87 88 89 90 91 92 93 94 95 96 |
# File 'lib/counter_culture/counter.rb', line 87 def counter_cache_name_for(obj) # figure out what the column name is if counter_cache_name.is_a?(Proc) # dynamic column name -- call the Proc counter_cache_name.call(obj) else # static column name counter_cache_name end end |
#counter_delta_magnitude_for(obj) ⇒ Object
Gets the delta magnitude of the counter cache for a specific object
obj: object to calculate the counter cache name for
75 76 77 78 79 80 81 |
# File 'lib/counter_culture/counter.rb', line 75 def counter_delta_magnitude_for(obj) if delta_magnitude.is_a?(Proc) delta_magnitude.call(obj) else delta_magnitude end end |
#first_level_relation_foreign_key ⇒ Object
gets the foreign key name of the relation. will look at the first level only – i.e., if passed an array will consider only its first element
relation: a symbol or array of symbols; specifies the relation
that has the counter cache column
176 177 178 179 |
# File 'lib/counter_culture/counter.rb', line 176 def first_level_relation_foreign_key first_relation = relation.first if relation.is_a?(Enumerable) relation_reflect(first_relation).foreign_key end |
#foreign_key_value(obj, relation, was = false) ⇒ Object
gets the value of the foreign key on the given relation
relation: a symbol or array of symbols; specifies the relation
that has the counter cache column
was: whether to get the current or past value from ActiveRecord;
pass true to get the past value, false or nothing to get the
current value
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
# File 'lib/counter_culture/counter.rb', line 110 def foreign_key_value(obj, relation, was = false) relation = relation.is_a?(Enumerable) ? relation.dup : [relation] first_relation = relation.first if was first = relation.shift foreign_key_value = obj.send("#{relation_foreign_key(first)}_was") klass = relation_klass(first) value = klass.where("#{klass.table_name}.#{relation_primary_key(first)} = ?", foreign_key_value).first if foreign_key_value else value = obj end while !value.nil? && relation.size > 0 value = value.send(relation.shift) end return value.try(relation_primary_key(first_relation).to_sym) end |
#full_primary_key(klass) ⇒ Object
the string to pass to order() in order to sort by primary key
99 100 101 |
# File 'lib/counter_culture/counter.rb', line 99 def full_primary_key(klass) "#{klass.quoted_table_name}.#{klass.quoted_primary_key}" end |
#previous_model(obj) ⇒ Object
181 182 183 184 185 186 187 188 189 |
# File 'lib/counter_culture/counter.rb', line 181 def previous_model(obj) prev = obj.dup obj.changed_attributes.each do |key, value| prev.send("#{key}=", value) end prev end |
#relation_foreign_key(relation) ⇒ Object
gets the foreign key name of the given relation
relation: a symbol or array of symbols; specifies the relation
that has the counter cache column
158 159 160 |
# File 'lib/counter_culture/counter.rb', line 158 def relation_foreign_key(relation) relation_reflect(relation).foreign_key end |
#relation_klass(relation) ⇒ Object
gets the class of the given relation
relation: a symbol or array of symbols; specifies the relation
that has the counter cache column
150 151 152 |
# File 'lib/counter_culture/counter.rb', line 150 def relation_klass(relation) relation_reflect(relation).klass end |
#relation_primary_key(relation) ⇒ Object
gets the primary key name of the given relation
relation: a symbol or array of symbols; specifies the relation
that has the counter cache column
166 167 168 |
# File 'lib/counter_culture/counter.rb', line 166 def relation_primary_key(relation) relation_reflect(relation).association_primary_key end |
#relation_reflect(relation) ⇒ Object
gets the reflect object on the given relation
relation: a symbol or array of symbols; specifies the relation
that has the counter cache column
131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/counter_culture/counter.rb', line 131 def relation_reflect(relation) relation = relation.is_a?(Enumerable) ? relation.dup : [relation] # go from one relation to the next until we hit the last reflect object klass = model while relation.size > 0 cur_relation = relation.shift reflect = klass.reflect_on_association(cur_relation) raise "No relation #{cur_relation} on #{klass.name}" if reflect.nil? klass = reflect.klass end return reflect end |