Module: IdentityCache
- Defined in:
- lib/identity_cache.rb,
lib/belongs_to_caching.rb,
lib/memoized_cache_proxy.rb,
lib/identity_cache/version.rb
Defined Under Namespace
Modules: BelongsToCaching, ClassMethods Classes: AlreadyIncludedError, InverseAssociationError, MemoizedCacheProxy
Constant Summary collapse
- CACHED_NIL =
:idc_cached_nil- VERSION =
"0.0.2"
Class Attribute Summary collapse
-
.cache ⇒ Object
readonly
Returns the value of attribute cache.
-
.logger ⇒ Object
Returns the value of attribute logger.
-
.readonly ⇒ Object
Returns the value of attribute readonly.
Class Method Summary collapse
-
.cache_backend=(cache_adaptor) ⇒ Object
Sets the cache adaptor IdentityCache will be using.
-
.fetch(key, &block) ⇒ Object
Cache retrieval and miss resolver primitive; given a key it will try to retrieve the associated value from the cache otherwise it will return the value of the execution of the block.
-
.fetch_multi(*keys, &block) ⇒ Object
Same as
fetch, except that it will try a collection of keys, using the multiget operation of the cache adaptor. -
.included(base) ⇒ Object
:nodoc:.
- .map_cached_nil_for(value) ⇒ Object
-
.memcache_hash(key) ⇒ Object
:nodoc:.
- .schema_to_string(columns) ⇒ Object
-
.should_cache? ⇒ Boolean
:nodoc:.
- .unmap_cached_nil_for(value) ⇒ Object
Instance Method Summary collapse
-
#attribute_cache_key_for_attribute_and_previous_values(attribute, fields) ⇒ Object
:nodoc:.
-
#expire_attribute_indexes ⇒ Object
:nodoc:.
-
#expire_cache ⇒ Object
:nodoc:.
-
#expire_primary_index ⇒ Object
:nodoc:.
-
#expire_secondary_indexes ⇒ Object
:nodoc:.
-
#fetch_denormalized_cached_association(ivar_name, association_name) ⇒ Object
:nodoc:.
-
#old_values_for_fields(fields) ⇒ Object
:nodoc:.
-
#populate_association_caches ⇒ Object
:nodoc:.
-
#populate_denormalized_cached_association(ivar_name, association_name) ⇒ Object
:nodoc:.
-
#primary_cache_index_key ⇒ Object
:nodoc:.
-
#secondary_cache_index_key_for_current_values(fields) ⇒ Object
:nodoc:.
-
#secondary_cache_index_key_for_previous_values(fields) ⇒ Object
:nodoc:.
-
#was_new_record? ⇒ Boolean
:nodoc:.
Class Attribute Details
.cache ⇒ Object (readonly)
Returns the value of attribute cache.
13 14 15 |
# File 'lib/identity_cache.rb', line 13 def cache @cache end |
.logger ⇒ Object
Returns the value of attribute logger.
12 13 14 |
# File 'lib/identity_cache.rb', line 12 def logger @logger end |
.readonly ⇒ Object
Returns the value of attribute readonly.
12 13 14 |
# File 'lib/identity_cache.rb', line 12 def readonly @readonly end |
Class Method Details
.cache_backend=(cache_adaptor) ⇒ Object
Sets the cache adaptor IdentityCache will be using
Parameters
cache_adaptor - A ActiveSupport::Cache::Store
21 22 23 |
# File 'lib/identity_cache.rb', line 21 def cache_backend=(cache_adaptor) cache.memcache = cache_adaptor end |
.fetch(key, &block) ⇒ Object
Cache retrieval and miss resolver primitive; given a key it will try to retrieve the associated value from the cache otherwise it will return the value of the execution of the block.
Parameters
key A cache key string
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
# File 'lib/identity_cache.rb', line 44 def fetch(key, &block) result = cache.read(key) if should_cache? if result.nil? if block_given? ActiveRecord::Base.connection.with_master do result = yield end result = map_cached_nil_for(result) if should_cache? cache.write(key, result) end end logger.debug "[IdentityCache] cache miss for #{key}" else logger.debug "[IdentityCache] cache hit for #{key}" end unmap_cached_nil_for(result) end |
.fetch_multi(*keys, &block) ⇒ Object
Same as fetch, except that it will try a collection of keys, using the multiget operation of the cache adaptor
Parameters
keys A collection of key strings
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
# File 'lib/identity_cache.rb', line 80 def fetch_multi(*keys, &block) return {} if keys.size == 0 result = {} result = cache.read_multi(*keys) if should_cache? missed_keys = keys - result.select {|key, value| value.present? }.keys if missed_keys.size > 0 if block_given? replacement_results = nil ActiveRecord::Base.connection.with_master do replacement_results = yield missed_keys end missed_keys.zip(replacement_results) do |(key, replacement_result)| if should_cache? replacement_result = map_cached_nil_for(replacement_result ) cache.write(key, replacement_result) logger.debug "[IdentityCache] cache miss for #{key} (multi)" end result[key] = replacement_result end end else result.keys.each do |key| logger.debug "[IdentityCache] cache hit for #{key} (multi)" end end result.keys.each do |key| result[key] = unmap_cached_nil_for(result[key]) end result end |
.included(base) ⇒ Object
:nodoc:
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
# File 'lib/identity_cache.rb', line 119 def included(base) #:nodoc: raise AlreadyIncludedError if base.respond_to? :cache_indexes unless ActiveRecord::Base.connection.respond_to?(:with_master) ActiveRecord::Base.connection.class.class_eval(ruby = <<-CODE, __FILE__, __LINE__) def with_master yield end CODE end base.send(:include, ArTransactionChanges) unless base.include?(ArTransactionChanges) base.send(:include, IdentityCache::BelongsToCaching) base.after_commit :expire_cache base.after_touch :expire_cache base.class_attribute :cache_indexes base.class_attribute :cache_attributes base.class_attribute :cached_has_manys base.class_attribute :cached_has_ones base.class_attribute :embedded_schema_hashes base.send(:extend, ClassMethods) base.cached_has_manys = {} base.cached_has_ones = {} base. = {} base.cache_attributes = [] base.cache_indexes = [] base.private_class_method :require_if_necessary, :build_normalized_has_many_cache, :build_denormalized_association_cache, :add_parent_expiry_hook, :identity_cache_multiple_value_dynamic_fetcher, :identity_cache_single_value_dynamic_fetcher base.instance_eval(ruby = <<-CODE, __FILE__, __LINE__) private :expire_cache, :was_new_record?, :fetch_denormalized_cached_association, :populate_denormalized_cached_association CODE end |
.map_cached_nil_for(value) ⇒ Object
66 67 68 |
# File 'lib/identity_cache.rb', line 66 def map_cached_nil_for(value) value.nil? ? IdentityCache::CACHED_NIL : value end |
.memcache_hash(key) ⇒ Object
:nodoc:
156 157 158 |
# File 'lib/identity_cache.rb', line 156 def memcache_hash(key) #:nodoc: CityHash.hash64(key) end |
.schema_to_string(columns) ⇒ Object
115 116 117 |
# File 'lib/identity_cache.rb', line 115 def schema_to_string(columns) columns.sort_by(&:name).map {|c| "#{c.name}:#{c.type}"} * "," end |
.should_cache? ⇒ Boolean
:nodoc:
33 34 35 |
# File 'lib/identity_cache.rb', line 33 def should_cache? # :nodoc: !readonly && ActiveRecord::Base.connection.open_transactions == 0 end |
.unmap_cached_nil_for(value) ⇒ Object
71 72 73 |
# File 'lib/identity_cache.rb', line 71 def unmap_cached_nil_for(value) value == IdentityCache::CACHED_NIL ? nil : value end |
Instance Method Details
#attribute_cache_key_for_attribute_and_previous_values(attribute, fields) ⇒ Object
:nodoc:
674 675 676 |
# File 'lib/identity_cache.rb', line 674 def attribute_cache_key_for_attribute_and_previous_values(attribute, fields) # :nodoc: self.class.rails_cache_key_for_attribute_and_fields_and_values(attribute, fields, old_values_for_fields(fields)) end |
#expire_attribute_indexes ⇒ Object
:nodoc:
719 720 721 722 723 |
# File 'lib/identity_cache.rb', line 719 def expire_attribute_indexes # :nodoc: cache_attributes.try(:each) do |(attribute, fields)| IdentityCache.cache.delete(attribute_cache_key_for_attribute_and_previous_values(attribute, fields)) unless was_new_record? end end |
#expire_cache ⇒ Object
:nodoc:
725 726 727 728 729 730 |
# File 'lib/identity_cache.rb', line 725 def expire_cache # :nodoc: expire_primary_index expire_secondary_indexes expire_attribute_indexes true end |
#expire_primary_index ⇒ Object
:nodoc:
691 692 693 694 695 696 697 698 699 700 701 |
# File 'lib/identity_cache.rb', line 691 def expire_primary_index # :nodoc: extra_keys = if respond_to? :updated_at old_updated_at = old_values_for_fields([:updated_at]).first "expiring_last_updated_at=#{old_updated_at}" else "" end IdentityCache.logger.debug "[IdentityCache] expiring=#{self.class.name} expiring_id=#{id} #{extra_keys}" IdentityCache.cache.delete(primary_cache_index_key) end |
#expire_secondary_indexes ⇒ Object
:nodoc:
703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 |
# File 'lib/identity_cache.rb', line 703 def expire_secondary_indexes # :nodoc: cache_indexes.try(:each) do |fields| if self.destroyed? IdentityCache.cache.delete(secondary_cache_index_key_for_previous_values(fields)) else new_cache_index_key = secondary_cache_index_key_for_current_values(fields) IdentityCache.cache.delete(new_cache_index_key) if !was_new_record? old_cache_index_key = secondary_cache_index_key_for_previous_values(fields) IdentityCache.cache.delete(old_cache_index_key) unless old_cache_index_key == new_cache_index_key end end end end |
#fetch_denormalized_cached_association(ivar_name, association_name) ⇒ Object
:nodoc:
628 629 630 631 632 633 634 635 636 |
# File 'lib/identity_cache.rb', line 628 def fetch_denormalized_cached_association(ivar_name, association_name) # :nodoc: ivar_full_name = :"@#{ivar_name}" if IdentityCache.should_cache? populate_denormalized_cached_association(ivar_name, association_name) IdentityCache.unmap_cached_nil_for(instance_variable_get(ivar_full_name)) else send(association_name.to_sym) end end |
#old_values_for_fields(fields) ⇒ Object
:nodoc:
678 679 680 681 682 683 684 685 686 687 688 689 |
# File 'lib/identity_cache.rb', line 678 def old_values_for_fields(fields) # :nodoc: fields.map do |field| field_string = field.to_s if destroyed? && transaction_changed_attributes.has_key?(field_string) transaction_changed_attributes[field_string] elsif persisted? && transaction_changed_attributes.has_key?(field_string) transaction_changed_attributes[field_string] else self.send(field) end end end |
#populate_association_caches ⇒ Object
:nodoc:
616 617 618 619 620 621 622 623 624 625 626 |
# File 'lib/identity_cache.rb', line 616 def populate_association_caches # :nodoc: self.class.all_cached_associations_needing_population.each do |cached_association, | send([:population_method_name]) reflection = [:embed] && self.class.reflect_on_association(cached_association) if reflection && reflection.klass.respond_to?(:cached_has_manys) child_objects = Array.wrap(send([:cached_accessor_name])) child_objects.each(&:populate_association_caches) end end self.clear_association_cache if self.respond_to?(:clear_association_cache) end |
#populate_denormalized_cached_association(ivar_name, association_name) ⇒ Object
:nodoc:
638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 |
# File 'lib/identity_cache.rb', line 638 def populate_denormalized_cached_association(ivar_name, association_name) # :nodoc: ivar_full_name = :"@#{ivar_name}" schema_hash_ivar = :"@#{ivar_name}_schema_hash" reflection = association(association_name) current_schema_hash = self.class.[association_name] ||= begin IdentityCache.memcache_hash(IdentityCache.schema_to_string(reflection.klass.columns)) end saved_schema_hash = instance_variable_get(schema_hash_ivar) if saved_schema_hash == current_schema_hash value = instance_variable_get(ivar_full_name) return value unless value.nil? end reflection.load_target unless reflection.loaded? loaded_association = send(association_name) instance_variable_set(schema_hash_ivar, current_schema_hash) instance_variable_set(ivar_full_name, IdentityCache.map_cached_nil_for(loaded_association)) end |
#primary_cache_index_key ⇒ Object
:nodoc:
662 663 664 |
# File 'lib/identity_cache.rb', line 662 def primary_cache_index_key # :nodoc: self.class.rails_cache_key(id) end |
#secondary_cache_index_key_for_current_values(fields) ⇒ Object
:nodoc:
666 667 668 |
# File 'lib/identity_cache.rb', line 666 def secondary_cache_index_key_for_current_values(fields) # :nodoc: self.class.rails_cache_index_key_for_fields_and_values(fields, fields.collect {|field| self.send(field)}) end |
#secondary_cache_index_key_for_previous_values(fields) ⇒ Object
:nodoc:
670 671 672 |
# File 'lib/identity_cache.rb', line 670 def secondary_cache_index_key_for_previous_values(fields) # :nodoc: self.class.rails_cache_index_key_for_fields_and_values(fields, old_values_for_fields(fields)) end |
#was_new_record? ⇒ Boolean
:nodoc:
732 733 734 |
# File 'lib/identity_cache.rb', line 732 def was_new_record? # :nodoc: !destroyed? && transaction_changed_attributes.has_key?('id') && transaction_changed_attributes['id'].nil? end |