Module: Cachely::Mechanics

Defined in:
lib/cachely/mechanics.rb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.connect(opts = {}) ⇒ Boolean

Connects to the Redis store you want to use for a cache.

Returns:

  • success or not



9
10
11
12
13
14
15
# File 'lib/cachely/mechanics.rb', line 9

def self.connect(opts = {})
  @redis ||= Redis.new(
    :host => opts[:host], 
    :port => opts[:port],
    :password => opts[:password],
    :driver => opts[:driver])
end

.flush_all_keysString

Flush the Redis store of all keys.

Returns:

  • success or not, normally “OK”



20
21
22
# File 'lib/cachely/mechanics.rb', line 20

def self.flush_all_keys
  redis.flushdb
end

.get(obj, method, *args) ⇒ Object

Gets a cached response to a method.

Returns:

  • The original response of the method back, whatever it may be.



66
67
68
69
70
71
72
# File 'lib/cachely/mechanics.rb', line 66

def self.get(obj, method, *args)
  result = redis.get(redis_key(obj, method, *args))
  #return an array, bc if the result stored was nil, it looks the same as if
  #we got no result back(which we would return nil) so we differentiate by putting
  #our return value always in an array. Easy to check.
  result.nil? ? nil : [map_s_to_param(result)]
end

.map_array_to_s(p) ⇒ String

Map array to s, calls recursively on elements then to json

Returns:

  • Outgoing redis string



219
220
221
222
223
# File 'lib/cachely/mechanics.rb', line 219

def self.map_array_to_s(p)  
  p.map do |part|
    map_param_to_s(part)
  end.to_json
end

.map_hash_to_s(p) ⇒ String

Maps hash to s, basically does recursive call to map_param_to_s to get everythign inside and then to_jsons.

Returns:

  • Outgoing redis string



208
209
210
211
212
213
# File 'lib/cachely/mechanics.rb', line 208

def self.map_hash_to_s(p)
  p.inject(Hash.new) do |hash, entry|
    hash[map_param_to_s(entry[0])] = map_param_to_s(entry[1])
    hash
  end.to_json
end

.map_param_to_s(p) ⇒ String

Turns any parameter into a string. Current supported types are primitives, orm models That respond to id and have an updated_at field, and objects that have a to_json method.

Returns:

  • The redis coded string.



169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/cachely/mechanics.rb', line 169

def self.map_param_to_s(p)
  if(p.is_a?(Hash)) 
    return map_hash_to_s(p)
  elsif(p.is_a?(Array))
    return map_array_to_s(p)
  elsif(p.respond_to?("to_json"))
    translated = nil

    if p.is_a?(String)
      translated = "instance|"+p.class.to_s+"|"+p
    elsif p.is_a?(Symbol)
      translated = "instance|"+p.class.to_s+"|"+p.to_s
    elsif p.nil?
      translated = "instance|NilClass|nil"
    elsif p.is_a?(TrueClass)
      translated = "instance|TrueClass|true"
    elsif p.is_a?(FalseClass)
      translated = "instance|FalseClass|false"
    elsif p.is_a?(Fixnum)
      translated = "instance|Fixnum|" + p.to_s
    elsif p.is_a?(Float)
      translated = "instance|Float|" + p.to_s
    elsif p.is_a?(ActiveRecord::Base)
      #don't want { "dummy_model" = > {:attributes => 1}}
      #want {:attributes => 1}
      translated = "instance|#{p.class.to_s}|#{JSON.parse(p.to_json)[p.class.to_s.underscore].to_json}"
    else 
      translated = (p.to_s.match(/^#</) ? "instance|#{p.class}" : "class|#{p.to_s}") + "|"+ p.to_json
    end
    
    return translated
  end
end

.map_s_to_obj(s) ⇒ Object

Turns String into an actual object

Returns:

  • The respawned object



133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/cachely/mechanics.rb', line 133

def self.map_s_to_obj(s)
  class_or_instance = s.split("|").first
  type = s.split("|")[1]
  data = s.gsub(/^#{class_or_instance}\|#{type}\|/,'')

  case type
  when "TrueClass"
    return true
  when "FalseClass"
    return false
  when "Symbol"
    return data.to_sym
  when "Fixnum"
    return data.to_i
  when "Float"
    return data.to_f
  when "String"
    return data
  when "NilClass"
    return nil
  else
    class_or_instance == "instance" ? obj = Object.const_get(type).new : obj = Object.const_get(type)

    JSON.parse(data).each do |key, value|
      obj.send(key+"=",value) if obj.respond_to?(key+"=")
    end

    return obj
  end
end

.map_s_to_param(s) ⇒ Object

Turns any string into a parameter. Current supported types are primitives, orm models That respond to id and have an updated_at field, and objects that have a to_json method.

Returns:

  • The respawned object



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/cachely/mechanics.rb', line 104

def self.map_s_to_param(s)
  begin
    respawned_hash = JSON.parse(s)
    
    if respawned_hash.is_a?(Array)
      respawned_hash.map! do |piece|
        map_s_to_param(piece)
      end
    elsif respawned_hash.is_a?(Hash)
      respawned_hash = respawned_hash.inject(Hash.new) do |new_hash, entry|
        new_hash[map_s_to_param(entry[0])] = map_s_to_param(entry[1])
        new_hash
      end
    end
    
    return respawned_hash
  rescue JSON::ParserError => e
    #The only times the string isn't json is if it is a primitive, or an ORM object
    #This exception happens then, we catch it, and check orm signature.
    
    return map_s_to_obj(s)
  end
  
end

.redisRedis

Grab current redis instance. Has ability to reconnect for you if it fails.

Returns:

  • , instance of Redis client.



27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/cachely/mechanics.rb', line 27

def self.redis
  @tries = 0 unless @tries
  begin
    @redis.get("test") #tests to see if we're still authed.
    @tries = 0 #means we can reset our trymeter.
  rescue Exception => e
    @tries+=1 if @tries
    if @tries<5
      connect
    end
  end
  return @redis  
end

.redis_key(context, method, *args) ⇒ String

Converts method name and arguments into a coherent key. Creates a hash and to_jsons it And that becomes the redis key. Spiffy, I know.

Returns:

  • The proper redis key to be used in storage.



91
92
93
94
95
96
97
# File 'lib/cachely/mechanics.rb', line 91

def self.redis_key(context, method, *args)
  map_param_to_s({
    :context => context,
    :method => method,
    :args => args
  })
end

.setup_method(klazz, name, time_to_expire_in_s, is_class_method = false) ⇒ Object

Defines the method using my hacky eval script. So far, this is the only way I’ve found that allows me to define variable argument length definitions. If you have a better idea, please refactor it!

Returns:

  • nil



47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/cachely/mechanics.rb', line 47

def self.setup_method(klazz, name, time_to_expire_in_s, is_class_method = false) 
  context = (is_class_method ? klazz : klazz.new)
  args_str = context.method("#{name.to_s}_old".to_sym).parameters.map { |k| k.last}.join(',')
  args_to_use_in_def = args_str.empty? ? "" : "," + args_str
  eval("klazz.define_#{is_class_method ? "singleton_" : ""}method(:#{name}) do #{args_str.empty? ? "" : "|#{args_str}|"}; " +
    "result = Cachely::Mechanics.get(context,:#{name}#{args_to_use_in_def});" +
    "return result.first if result.is_a?(Array);" + 
    "result = context.send(:#{"#{name.to_s}_old"}#{args_to_use_in_def});" + 
    "Cachely::Mechanics.store(context,:#{"#{name.to_s}"}, result#{time_to_expire_in_s.nil? ? ",nil": ",#{time_to_expire_in_s}"}#{args_to_use_in_def});" +
    "return result;" + 
    "end"
  )
end

.store(obj, method, result, time_to_exp_in_sec, *args) ⇒ String

Stores the result in it’s proper cached location on redis by getting the redis key and parsing The result into a storable string, mostly made up of recursive json.

Returns:

  • Should be “Ok” or something similar.



81
82
83
84
# File 'lib/cachely/mechanics.rb', line 81

def self.store(obj, method, result, time_to_exp_in_sec, *args) 
  redis.set(redis_key(obj, method, *args), map_param_to_s(result))
  redis.expire(redis_key(obj, method, *args), time_to_exp_in_sec) if time_to_exp_in_sec
end

Instance Method Details

#context([Object, Symbol, Args], methodnamesymbol) ⇒ String

Converts method name and arguments into a coherent key. Creates a hash and to_jsons it And that becomes the redis key. Spiffy, I know.

Returns:

  • The proper redis key to be used in storage.



91
92
93
94
95
96
97
# File 'lib/cachely/mechanics.rb', line 91

def self.redis_key(context, method, *args)
  map_param_to_s({
    :context => context,
    :method => method,
    :args => args
  })
end

#obj([Symbol]) ⇒ String

Stores the result in it’s proper cached location on redis by getting the redis key and parsing The result into a storable string, mostly made up of recursive json.

Returns:

  • Should be “Ok” or something similar.



66
67
68
69
70
71
72
# File 'lib/cachely/mechanics.rb', line 66

def self.get(obj, method, *args)
  result = redis.get(redis_key(obj, method, *args))
  #return an array, bc if the result stored was nil, it looks the same as if
  #we got no result back(which we would return nil) so we differentiate by putting
  #our return value always in an array. Easy to check.
  result.nil? ? nil : [map_s_to_param(result)]
end