Module: IdentityCache::QueryAPI::ClassMethods

Defined in:
lib/identity_cache/query_api.rb

Instance Method Summary collapse

Instance Method Details

#exists_with_identity_cache?(id) ⇒ Boolean

Similar to ActiveRecord::Base#exists? will return true if the id can be found in the cache or in the DB.

Returns:

  • (Boolean)

Raises:

  • (NotImplementedError)


12
13
14
15
# File 'lib/identity_cache/query_api.rb', line 12

def exists_with_identity_cache?(id)
  raise NotImplementedError, "exists_with_identity_cache? needs the primary index enabled" unless primary_cache_index_enabled
  !!fetch_by_id(id)
end

#fetch(id, options = {}) ⇒ Object

Default fetcher added to the model on inclusion, it behaves like ActiveRecord::Base.find, will raise ActiveRecord::RecordNotFound exception if id is not in the cache or the db.



44
45
46
# File 'lib/identity_cache/query_api.rb', line 44

def fetch(id, options={})
  fetch_by_id(id, options) or raise(ActiveRecord::RecordNotFound, "Couldn't find #{self.name} with ID=#{id}")
end

#fetch_by_id(id, options = {}) ⇒ Object

Default fetcher added to the model on inclusion, it behaves like ActiveRecord::Base.where(id: id).first

Raises:

  • (NotImplementedError)


19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/identity_cache/query_api.rb', line 19

def fetch_by_id(id, options={})
  ensure_base_model
  raise_if_scoped
  raise NotImplementedError, "fetching needs the primary index enabled" unless primary_cache_index_enabled
  return unless id
  record = if IdentityCache.should_use_cache?
    require_if_necessary do
      object = nil
      coder = IdentityCache.fetch(rails_cache_key(id)){ coder_from_record(object = resolve_cache_miss(id)) }
      object ||= record_from_coder(coder)
      if object && object.id.to_s != id.to_s
        IdentityCache.logger.error "[IDC id mismatch] fetch_by_id_requested=#{id} fetch_by_id_got=#{object.id} for #{object.inspect[(0..100)]}"
      end
      object
    end
  else
    resolve_cache_miss(id)
  end
  prefetch_associations(options[:includes], [record]) if record && options[:includes]
  record
end

#fetch_multi(*ids) ⇒ Object

Default fetcher added to the model on inclusion, if behaves like ActiveRecord::Base.find_all_by_id

Raises:

  • (NotImplementedError)


50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/identity_cache/query_api.rb', line 50

def fetch_multi(*ids)
  ensure_base_model
  raise_if_scoped
  raise NotImplementedError, "fetching needs the primary index enabled" unless primary_cache_index_enabled
  options = ids.extract_options!
  ids.flatten!(1)
  records = if IdentityCache.should_use_cache?
    require_if_necessary do
      cache_keys = ids.map {|id| rails_cache_key(id) }
      key_to_id_map = Hash[ cache_keys.zip(ids) ]
      key_to_record_map = {}

      coders_by_key = IdentityCache.fetch_multi(cache_keys) do |unresolved_keys|
        ids = unresolved_keys.map {|key| key_to_id_map[key] }
        records = find_batch(ids)
        key_to_record_map = records.compact.index_by{ |record| rails_cache_key(record.id) }
        records.map {|record| coder_from_record(record) }
      end

      cache_keys.map{ |key| key_to_record_map[key] || record_from_coder(coders_by_key[key]) }
    end
  else
    find_batch(ids)
  end
  records.compact!
  prefetch_associations(options[:includes], records) if options[:includes]
  records
end

#prefetch_associations(associations, records) ⇒ Object



79
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
# File 'lib/identity_cache/query_api.rb', line 79

def prefetch_associations(associations, records)
  records = records.to_a
  return if records.empty?
  unless IdentityCache.should_use_cache?
    ActiveRecord::Associations::Preloader.new.preload(records, associations)
    return
  end

  case associations
  when nil
    # do nothing
  when Symbol
    prefetch_one_association(associations, records)
  when Array
    associations.each do |association|
      prefetch_associations(association, records)
    end
  when Hash
    associations.each do |association, sub_associations|
      next_level_records = prefetch_one_association(association, records)

      associated_class = reflect_on_association(association).klass
      if associated_class.respond_to?(:prefetch_associations)
        associated_class.prefetch_associations(sub_associations, next_level_records)
      end
    end
  else
    raise TypeError, "Invalid associations class #{associations.class}"
  end
  nil
end