Module: GenCache

Defined in:
lib/gen_cache.rb,
lib/gen_cache/keys.rb,
lib/gen_cache/caches.rb,
lib/gen_cache/expiry.rb,
lib/gen_cache/cache_io/parsing.rb,
lib/gen_cache/cache_io/fetching.rb,
lib/gen_cache/cache_io/formatting.rb,
lib/gen_cache/cache_types/key_cache.rb,
lib/gen_cache/cache_types/method_cache.rb,
lib/gen_cache/cache_types/attribute_cache.rb,
lib/gen_cache/cache_types/association_cache.rb,
lib/gen_cache/cache_types/class_method_cache.rb

Defined Under Namespace

Modules: AssocationCache, AttributeCache, Caches, ClassMethodCache, ClassMethods, KeyCache, MethodCache

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.all_class_method_keys(klass) ⇒ Object



44
45
46
# File 'lib/gen_cache/keys.rb', line 44

def self.all_class_method_keys(klass)
  klass.cached_class_methods.map { |c_method| class_method_key(klass, c_method) }
end

.association_key(instance, association) ⇒ Object

> “users/5821759535148822589/64/12126514016877773284/association”



66
67
68
69
# File 'lib/gen_cache/keys.rb', line 66

def self.association_key(instance, association)
  { type: :association,
    key: [instance_prefix(instance), association].join("/") }
end

.attribute_key(klass, attribute, args, options = {}) ⇒ Object

> “users/5821759535148822589/attribute/value”

> “users/5821759535148822589/all/attribute/value”



27
28
29
30
31
32
33
34
35
36
# File 'lib/gen_cache/keys.rb', line 27

def self.attribute_key(klass, attribute, args, options={})
  att_args = [attribute, symbolize_args([args])].join("/")
  unless options[:all]
    { type: :object,
      key: [model_prefix(klass), att_args].join("/") }
  else
    { type: :object,
      key: [model_prefix(klass), "all", att_args].join("/") }
  end
end

.class_method_key(klass, method) ⇒ Object

> “users/5821759535148822589/method”



39
40
41
42
# File 'lib/gen_cache/keys.rb', line 39

def self.class_method_key(klass, method)
  { type: :method, 
    key: [model_prefix(klass), method].join("/") }
end

.coder_from_record(record) ⇒ Object



24
25
26
27
28
29
30
# File 'lib/gen_cache/cache_io/formatting.rb', line 24

def self.coder_from_record(record)
  unless record.nil?
    coder = { :class => record.class }
    record.encode_with(coder)
    coder
  end
end

.data_parse(result) ⇒ Object

DATA PARSING ##



59
60
61
62
63
64
65
# File 'lib/gen_cache/cache_io/parsing.rb', line 59

def self.data_parse(result)
  if detect_coder(result)
    object_parse(result)
  else
    result
  end
end

.detect_coder(data) ⇒ Object

METHOD PARSING ##

METHOD STORE FORMATTING

{ args.to_string.to_symbol => answer }



36
37
38
39
# File 'lib/gen_cache/cache_io/parsing.rb', line 36

def self.detect_coder(data)
  (data.is_a?(Hash) && hash_inspect(data)) ||
  (data.is_a?(Array) && data[0].is_a?(Hash) && hash_inspect(data[0]))
end

.detect_object(data) ⇒ Object



50
51
52
53
# File 'lib/gen_cache/cache_io/formatting.rb', line 50

def self.detect_object(data)
  data.is_a?(ActiveRecord::Base) || 
  (data.is_a?(Array) && data[0].is_a?(ActiveRecord::Base))
end

.escape_punctuation(string) ⇒ Object



46
47
48
# File 'lib/gen_cache/cache_io/formatting.rb', line 46

def self.escape_punctuation(string)
  string.sub(/\?\Z/, '_query').sub(/!\Z/, '_bang')
end

.expire(object) ⇒ Object

Manual expiry initiated by an object after commit only has to worry about key_cache, attribute_cache, and class_method_cache



9
10
11
12
13
# File 'lib/gen_cache/expiry.rb', line 9

def self.expire(object)
  expire_instance_key(object)
  expire_class_method_keys(object)
  expire_attribute_keys(object)
end

.expire_attribute_keys(object) ⇒ Object

cached attributes are stored like: { attribute => [value1, value2] }



30
31
32
33
34
35
36
# File 'lib/gen_cache/expiry.rb', line 30

def self.expire_attribute_keys(object)
  object.class.cached_indices.map do |index, values|
    values.map { |v| attribute_key(object.class, index, v) }
  end.flatten.each do |attribute_key|
    Rails.cache.delete(attribute_key[:key])
  end
end

.expire_class_method_keys(object) ⇒ Object



20
21
22
23
24
25
26
# File 'lib/gen_cache/expiry.rb', line 20

def self.expire_class_method_keys(object)
  object.class.cached_class_methods.map do |class_method|
    key = class_method_key(object.class, class_method)
  end.each do |method_key|
    Rails.cache.delete(method_key[:key])
  end
end

.expire_instance_key(object) ⇒ Object



15
16
17
18
# File 'lib/gen_cache/expiry.rb', line 15

def self.expire_instance_key(object)
  key = instance_key(object.class, object.id)
  Rails.cache.delete(key[:key])
end

.fetch(key_blob, options = {}, &block) ⇒ Object



3
4
5
6
7
8
9
# File 'lib/gen_cache/cache_io/fetching.rb', line 3

def self.fetch(key_blob, options={}, &block)
  unless key_blob.is_a?(Array)
    single_fetch(key_blob, options) { yield if block_given? }
  else
    multiple_fetch(key_blob) { yield if block_given? }
  end
end

.format_data(result) ⇒ Object



67
68
69
70
71
72
73
# File 'lib/gen_cache/cache_io/formatting.rb', line 67

def self.format_data(result)
  if detect_object(result)
    format_object(result)
  else
    result
  end
end

.format_method(result) ⇒ Object



55
56
57
58
59
60
61
62
63
64
65
# File 'lib/gen_cache/cache_io/formatting.rb', line 55

def self.format_method(result)
  if detect_object(result)
    format_object(result)
  elsif result.is_a?(Hash)
    result.each do |arg_key, value|
      result[arg_key] = format_data(value)
    end
  else
    format_data(result)
  end
end

.format_object(object) ⇒ Object

OBJECT FORMATTING ##



16
17
18
19
20
21
22
# File 'lib/gen_cache/cache_io/formatting.rb', line 16

def self.format_object(object)
  if object.is_a?(Array)
    object.map { |obj| coder_from_record(obj) }
  else
    coder_from_record(object)
  end
end

.format_with_key(result, key_type) ⇒ Object



3
4
5
6
7
8
9
10
11
12
# File 'lib/gen_cache/cache_io/formatting.rb', line 3

def self.format_with_key(result, key_type)
  return if result.nil?
  if key_type == :association
    result
  elsif key_type == :object
    formatted_result = format_object(result)
  else
    formatted_result = format_method(result)
  end
end

.hash_inspect(hash) ⇒ Object



41
42
43
# File 'lib/gen_cache/cache_io/parsing.rb', line 41

def self.hash_inspect(hash)
  hash.has_key?(:class) && hash.has_key?('attributes')
end

.included(base) ⇒ Object



12
13
14
15
16
17
18
19
20
21
22
23
24
# File 'lib/gen_cache.rb', line 12

def self.included(base)
  base.extend(GenCache::Caches)
  base.extend(GenCache::ClassMethods)

  base.class_eval do
    class_attribute   :cached_key,
                      :cached_indices,
                      :cached_methods,
                      :cached_class_methods,
                      :cached_associations
    after_commit :expire_all
  end
end

.instance_key(klass, id) ⇒ Object

> “users/5821759535148822589/64”



20
21
22
23
# File 'lib/gen_cache/keys.rb', line 20

def self.instance_key(klass, id)
  {type: :object,
   key: [model_prefix(klass), id].join("/") }
end

.instance_prefix(instance) ⇒ Object

HASH generated from ATTRIBUTES to indicate INSTANCE GENERATIONS

> “users/5821759535148822589/64/12126514016877773284”



52
53
54
55
56
57
# File 'lib/gen_cache/keys.rb', line 52

def self.instance_prefix(instance)
  atts = instance.attributes
  att_string = atts.sort.map { |k, v| [k,v].join(":") }.join(",")
  generation = CityHash.hash64(att_string)
  [model_prefix(instance.class), instance.id, generation].join("/")
end

.method_key(instance, method) ⇒ Object

> “users/5821759535148822589/64/12126514016877773284/method”



60
61
62
63
# File 'lib/gen_cache/keys.rb', line 60

def self.method_key(instance, method)
  { type: :method,
    key: [instance_prefix(instance), method].join("/") }
end

.method_parse(result) ⇒ Object



45
46
47
48
49
50
51
52
53
54
55
# File 'lib/gen_cache/cache_io/parsing.rb', line 45

def self.method_parse(result)
  if detect_coder(result)
    object_parse(result)
  elsif result.is_a?(Hash)
    result.each do |k,v|
      result[k] = data_parse(v)
    end
  else
    data_parse(result)
  end
end

.model_prefix(klass) ⇒ Object

HASH generated from SCHEMA to indicate MODEL GENERATIONS

> “users/5821759535148822589”



11
12
13
14
15
16
17
# File 'lib/gen_cache/keys.rb', line 11

def self.model_prefix(klass)
  columns = klass.try(:columns)
  return if columns.nil?
  schema_string = columns.sort_by(&:name).map{|c| "#{c.name}:#{c.type}"}.join(',')
  generation = CityHash.hash64(schema_string)
  [klass.name.tableize, generation].join("/")
end

.multiple_fetch(key_blobs, &block) ⇒ Object



32
33
34
35
36
37
38
39
40
41
# File 'lib/gen_cache/cache_io/fetching.rb', line 32

def self.multiple_fetch(key_blobs, &block)
  results = read_multi_from_cache(key_blobs)
  if results.nil?
    if block_given?
      results = yield
      write_multi_to_cache(results)
    end
  end
  results.values
end

.object_parse(result) ⇒ Object

OBJECT PARSING ##



16
17
18
19
20
21
22
# File 'lib/gen_cache/cache_io/parsing.rb', line 16

def self.object_parse(result)
  if result.is_a?(Array)
    result.map {|obj| record_from_coder(obj)}
  else
    record_from_coder(result)
  end
end

.parse_with_key(result, key_type) ⇒ Object



3
4
5
6
7
8
9
10
11
12
# File 'lib/gen_cache/cache_io/parsing.rb', line 3

def self.parse_with_key(result, key_type)
  return if result.nil?
  if key_type == :association
    result
  elsif key_type == :object
    object_parse(result)
  else
    method_parse(result)
  end
end

.read_from_cache(key_blob) ⇒ Object

READING FROM THE CACHE



47
48
49
50
51
# File 'lib/gen_cache/cache_io/fetching.rb', line 47

def self.read_from_cache(key_blob)
  result = Rails.cache.read key_blob[:key]
  return result if result.nil?
  parse_with_key(result, key_blob[:type])
end

.read_multi_from_cache(key_blobs) ⇒ Object



53
54
55
56
57
58
59
60
61
62
# File 'lib/gen_cache/cache_io/fetching.rb', line 53

def self.read_multi_from_cache(key_blobs)
  keys = key_blobs.map { |blob| blob[:key] }
  results = Rails.cache.read_multi(*keys)
  return nil if results.values.all?(&:nil?)
  results.each do |key, value|
    type = key_blobs.select {|kb| kb.has_value?(key) }.first[:type]
    results[key] = parse_with_key(value, type)
  end
  results
end

.record_from_coder(coder) ⇒ Object



24
25
26
27
28
# File 'lib/gen_cache/cache_io/parsing.rb', line 24

def self.record_from_coder(coder)
  record = coder[:class].allocate
  record.init_with(coder)
  record
end

.single_fetch(key_blob, options, &block) ⇒ Object



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/gen_cache/cache_io/fetching.rb', line 11

def self.single_fetch(key_blob, options, &block)
  result = read_from_cache(key_blob)
  method_args = symbolize_args(options[:args])
  should_write = false

  if block_given?
    if method_args != :no_args && (result.nil? || result[method_args].nil?)
      result ||= {}
      result[method_args] = yield
      should_write = true
    elsif method_args == :no_args && result.nil?
      result = yield
      should_write = true
    end
  end

  write_to_cache(key_blob, result) if should_write
  
  result = (method_args == :no_args) ? result : result[method_args]
end

.symbolize_args(args) ⇒ Object

METHOD FORMATTING ##



33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/gen_cache/cache_io/formatting.rb', line 33

def self.symbolize_args(args)
  return :no_args if args.nil? || args.empty?
  args.map do |arg|
    if arg.is_a?(Hash)
      arg.map {|k,v| "#{k}:#{v}"}.join(",")
    elsif arg.is_a?(Array)
      arg.join(",")
    else
      arg.to_s.split(" ").join("_")
    end
  end.join("+").to_sym
end

.write_multi_to_cache(keys_and_results) ⇒ Object

WRITING TO THE CACHE



68
69
70
71
72
# File 'lib/gen_cache/cache_io/fetching.rb', line 68

def self.write_multi_to_cache(keys_and_results)
  keys_and_results.each do |key, result|
    write_to_cache(key, result)
  end
end

.write_to_cache(key_blob, result) ⇒ Object



74
75
76
77
# File 'lib/gen_cache/cache_io/fetching.rb', line 74

def self.write_to_cache(key_blob, result)
  formatted_result = format_with_key(result, key_blob[:type])
  Rails.cache.write key_blob[:key], formatted_result
end

Instance Method Details

#expire_allObject



3
4
5
# File 'lib/gen_cache/expiry.rb', line 3

def expire_all
  GenCache.expire(self)
end