mack-caching 0.8.1 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/gems/cachetastic-2.0.0/bin/cachetastic_drb_server +115 -0
- data/lib/gems/cachetastic-2.0.0/lib/cachetastic/adapters/base.rb +78 -0
- data/lib/gems/cachetastic-2.0.0/lib/cachetastic/adapters/drb.rb +51 -0
- data/lib/gems/cachetastic-2.0.0/lib/cachetastic/adapters/file.rb +49 -0
- data/lib/gems/cachetastic-2.0.0/lib/cachetastic/adapters/file_base.rb +86 -0
- data/lib/gems/cachetastic-2.0.0/lib/cachetastic/adapters/html_file.rb +68 -0
- data/lib/gems/cachetastic-2.0.0/lib/cachetastic/adapters/local_memory.rb +68 -0
- data/lib/gems/cachetastic-2.0.0/lib/cachetastic/adapters/memcache.rb +114 -0
- data/lib/gems/cachetastic-2.0.0/lib/cachetastic/adapters/store_object.rb +28 -0
- data/lib/gems/cachetastic-2.0.0/lib/cachetastic/cacheable.rb +196 -0
- data/lib/gems/cachetastic-2.0.0/lib/cachetastic/caches/base.rb +238 -0
- data/lib/gems/cachetastic-2.0.0/lib/cachetastic/caches/mack_session_cache.rb +3 -0
- data/lib/gems/cachetastic-2.0.0/lib/cachetastic/caches/page_cache.rb +6 -0
- data/lib/gems/cachetastic-2.0.0/lib/cachetastic/caches/rails_session_cache.rb +12 -0
- data/lib/gems/cachetastic-2.0.0/lib/cachetastic/connection.rb +24 -0
- data/lib/gems/cachetastic-2.0.0/lib/cachetastic/errors/unsupported_adapter.rb +7 -0
- data/lib/gems/cachetastic-2.0.0/lib/cachetastic/logger.rb +49 -0
- data/lib/gems/cachetastic-2.0.0/lib/cachetastic/rails_extensions/active_record_base.rb +24 -0
- data/lib/gems/cachetastic-2.0.0/lib/cachetastic/rails_extensions/cgi_session_store.rb +59 -0
- data/lib/gems/cachetastic-2.0.0/lib/cachetastic/ruby_extensions/object.rb +8 -0
- data/lib/gems/cachetastic-2.0.0/lib/cachetastic.rb +70 -0
- data/lib/gems/memcache-client-1.5.0/lib/memcache.rb +805 -0
- data/lib/gems/memcache-client-1.5.0/lib/memcache_util.rb +90 -0
- data/lib/gems.rb +13 -0
- data/lib/mack-caching.rb +2 -1
- data/lib/mack-caching_tasks.rb +2 -0
- metadata +51 -16
@@ -0,0 +1,115 @@
|
|
1
|
+
#!/usr/local/bin/ruby
|
2
|
+
require 'thread'
|
3
|
+
require 'drb'
|
4
|
+
require 'singleton'
|
5
|
+
require 'optparse'
|
6
|
+
require 'optparse/time'
|
7
|
+
require 'ostruct'
|
8
|
+
|
9
|
+
# require the necessary gems:
|
10
|
+
require 'rubygems'
|
11
|
+
gem 'mack-facets'
|
12
|
+
require 'mack-facets'
|
13
|
+
gem 'configatron'
|
14
|
+
require 'configatron'
|
15
|
+
gem 'cachetastic'
|
16
|
+
require 'cachetastic'
|
17
|
+
|
18
|
+
$SAFE = 1
|
19
|
+
|
20
|
+
SERVER_OPTIONS = OpenStruct.new
|
21
|
+
SERVER_OPTIONS.host = "127.0.0.1"
|
22
|
+
SERVER_OPTIONS.port = "61676"
|
23
|
+
SERVER_OPTIONS.config_file = nil
|
24
|
+
SERVER_OPTIONS.verbose = false
|
25
|
+
SERVER_OPTIONS.really_verbose = false
|
26
|
+
|
27
|
+
opts = OptionParser.new do |opts|
|
28
|
+
|
29
|
+
opts.on("-h [HOST]") do |v|
|
30
|
+
SERVER_OPTIONS.host = v
|
31
|
+
end
|
32
|
+
|
33
|
+
opts.on("-p [PORT]", Integer) do |v|
|
34
|
+
SERVER_OPTIONS.port = v
|
35
|
+
end
|
36
|
+
|
37
|
+
opts.on("-c [CONFIG_FILE]") do |v|
|
38
|
+
SERVER_OPTIONS.config_file = v
|
39
|
+
end
|
40
|
+
|
41
|
+
opts.on("-v") do |v|
|
42
|
+
SERVER_OPTIONS.verbose = true
|
43
|
+
end
|
44
|
+
|
45
|
+
opts.on("-rv") do |v|
|
46
|
+
SERVER_OPTIONS.verbose = true
|
47
|
+
SERVER_OPTIONS.really_verbose = true
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
opts.parse!(ARGV)
|
53
|
+
|
54
|
+
if SERVER_OPTIONS.config_file
|
55
|
+
configatron.configure_from_yaml(SERVER_OPTIONS.config_file)
|
56
|
+
end
|
57
|
+
|
58
|
+
module Cachetastic
|
59
|
+
module Drb
|
60
|
+
class Server
|
61
|
+
include Singleton
|
62
|
+
|
63
|
+
def initialize
|
64
|
+
DRb.start_service("druby://#{SERVER_OPTIONS.host}:#{SERVER_OPTIONS.port}", self)
|
65
|
+
puts "Cachetastic::Drb::Server listening for connection..."
|
66
|
+
DRb.thread.join
|
67
|
+
end # initialize
|
68
|
+
|
69
|
+
def get(ns, key)
|
70
|
+
puts "get: #{ns}.#{key}" if SERVER_OPTIONS.verbose
|
71
|
+
puts cache_for_namespace(ns).inspect if SERVER_OPTIONS.really_verbose
|
72
|
+
value = cache_for_namespace(ns).get(key)
|
73
|
+
puts "\tvalue.class: #{value.class}" if SERVER_OPTIONS.verbose
|
74
|
+
value
|
75
|
+
end
|
76
|
+
|
77
|
+
def set(ns, key, value, expiry = 0)
|
78
|
+
puts "set: #{ns}.#{key}: #{value}" if SERVER_OPTIONS.verbose
|
79
|
+
cache_for_namespace(ns).set(key, value)
|
80
|
+
puts cache_for_namespace(ns).inspect if SERVER_OPTIONS.really_verbose
|
81
|
+
value
|
82
|
+
end
|
83
|
+
|
84
|
+
def delete(ns, key, delay = 0)
|
85
|
+
puts "delete: #{ns}.#{key}" if SERVER_OPTIONS.verbose
|
86
|
+
cache_for_namespace(ns).delete(key, delay = 0)
|
87
|
+
puts cache_for_namespace(ns).inspect if SERVER_OPTIONS.really_verbose
|
88
|
+
end
|
89
|
+
|
90
|
+
def expire_all(ns)
|
91
|
+
puts "expire_all: #{ns}" if SERVER_OPTIONS.verbose
|
92
|
+
instance_variable_set("@store_for_#{ns}", Cachetastic::Adapters::LocalMemory.new("store_for_#{ns}"))
|
93
|
+
end
|
94
|
+
|
95
|
+
def ping
|
96
|
+
true
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
def cache_for_namespace(ns)
|
101
|
+
# puts "@store_for_#{ns}"
|
102
|
+
c = instance_variable_get("@store_for_#{ns}")
|
103
|
+
if c.nil?
|
104
|
+
instance_variable_set("@store_for_#{ns}", Cachetastic::Adapters::LocalMemory.new("store_for_#{ns}"))
|
105
|
+
c = instance_variable_get("@store_for_#{ns}")
|
106
|
+
end
|
107
|
+
c
|
108
|
+
end
|
109
|
+
|
110
|
+
end # Server
|
111
|
+
end # Drb
|
112
|
+
end # Cachetastic
|
113
|
+
|
114
|
+
# let's start `er up!
|
115
|
+
Cachetastic::Drb::Server.instance
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# This class is the interface used to develop adapters for stores.
|
2
|
+
# Stores are where the data is actually held. These could be local memory,
|
3
|
+
# memcached, a database, the file system, etc...
|
4
|
+
# If you implement this API, then you should be able to plug in different
|
5
|
+
# stores for your caches without having to change any of your code.
|
6
|
+
#
|
7
|
+
# === Methods that need to be implemented:
|
8
|
+
# * setup - used to setup the implementation of the adapter.
|
9
|
+
# * set(key, object, expiry) - sets an object into the store using the given key and the expiry time.
|
10
|
+
# * get(key) - returns an object from the store for a given key.
|
11
|
+
# * delete(key, delay) - deletes an object from the store for a given key. If the store supports it, a delay can be used.
|
12
|
+
# * expire_all - expires all objects in the store for a given cache.
|
13
|
+
# * stats - returns statistics for the store.
|
14
|
+
# * valid? - used to test whether or not the store is still valid. If this returns false a new instance of the adapter is created by Cachetastic::Connection
|
15
|
+
class Cachetastic::Adapters::Base
|
16
|
+
|
17
|
+
# attr_reader :all_options
|
18
|
+
# attr_reader :store_options
|
19
|
+
# attr_reader :servers
|
20
|
+
attr_reader :name
|
21
|
+
# attr_reader :logging
|
22
|
+
attr_reader :logger
|
23
|
+
|
24
|
+
def initialize(name)
|
25
|
+
@name = name
|
26
|
+
@logger = Cachetastic::Logger.new(configuration.retrieve(:logger, ::Logger.new(STDOUT)))
|
27
|
+
setup
|
28
|
+
if self.debug?
|
29
|
+
self.logger.debug(self.name, :self, self.inspect)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
needs_method :setup
|
34
|
+
needs_method :set
|
35
|
+
needs_method :get
|
36
|
+
needs_method :delete
|
37
|
+
needs_method :expire_all
|
38
|
+
needs_method :stats
|
39
|
+
needs_method :valid?
|
40
|
+
|
41
|
+
# Returns true/or falsed based on whether or not the debug setting is set to true in the
|
42
|
+
# configuration file. If the config setting is set, then false is returned.
|
43
|
+
def debug?
|
44
|
+
ivar_cache(:debug) do
|
45
|
+
configuration.retrieve(:debug, false)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def stats
|
50
|
+
cache_name = self.name.to_s.camelize
|
51
|
+
adapter_type = self.class.to_s.gsub('Cachetastic::Adapters::', '')
|
52
|
+
s = "Cache: #{cache_name}\nStore Type: #{adapter_type}\n"
|
53
|
+
if self.servers
|
54
|
+
servers = self.servers.join(',')
|
55
|
+
s += "Servers: #{servers}"
|
56
|
+
end
|
57
|
+
puts s
|
58
|
+
end
|
59
|
+
|
60
|
+
def configuration
|
61
|
+
Cachetastic::Adapters::Base.configuration(self.name)
|
62
|
+
end
|
63
|
+
|
64
|
+
class << self
|
65
|
+
# Returns either the options
|
66
|
+
# Options need to be specified in configatrion as the methodized name of the cache with
|
67
|
+
# _options attached at the end.
|
68
|
+
# Examples:
|
69
|
+
# Cachetastic::Caches::PageCache # => cachetastic_caches_page_cache_options
|
70
|
+
# MyAwesomeCache # => my_awesome_cache_options
|
71
|
+
def configuration(name)
|
72
|
+
name = "#{name}_options"
|
73
|
+
conf = configatron.retrieve(name, configatron.cachetastic_default_options)
|
74
|
+
conf
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# This adapter uses Cachetastic::Drb::Server as it's backing.
|
2
|
+
# The configuration for this should look something like this:
|
3
|
+
# my_awesome_cache_options:
|
4
|
+
# debug: false
|
5
|
+
# adapter: drb
|
6
|
+
# store_options:
|
7
|
+
# host: druby://127.0.0.1:61676
|
8
|
+
class Cachetastic::Adapters::Drb < Cachetastic::Adapters::Base
|
9
|
+
|
10
|
+
attr_accessor :drb_store
|
11
|
+
|
12
|
+
def valid?
|
13
|
+
begin
|
14
|
+
return self.drb_store.ping
|
15
|
+
rescue Exception => e
|
16
|
+
return false
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def setup
|
21
|
+
# self.all_options["marshall_method"] = "ruby"
|
22
|
+
self.drb_store = DRbObject.new_with_uri(configuration.servers)
|
23
|
+
end
|
24
|
+
|
25
|
+
def expire_all
|
26
|
+
self.drb_store.expire_all(self.name)
|
27
|
+
end
|
28
|
+
|
29
|
+
# See Cachetastic::Adapters::Base
|
30
|
+
def get(key)
|
31
|
+
Cachetastic::Caches::Base.unmarshall(self.drb_store.get(self.name, key))
|
32
|
+
end
|
33
|
+
|
34
|
+
def set(key, value, expiry = 0)
|
35
|
+
self.drb_store.set(self.name, key, Cachetastic::Caches::Base.marshall(value), expiry)
|
36
|
+
end
|
37
|
+
|
38
|
+
def delete(key, delay = 0)
|
39
|
+
self.drb_store.delete(self.name, key)
|
40
|
+
end
|
41
|
+
|
42
|
+
def stats
|
43
|
+
super
|
44
|
+
begin
|
45
|
+
self.drb_store.stats if self.drb_store.respond_to? 'stats'
|
46
|
+
rescue Exception => e
|
47
|
+
puts "Calling stats on the DRb store raised this exception: #{e.message}"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# This adapter uses the file system as it's backing.
|
2
|
+
# The configuration for this should look something like this:
|
3
|
+
# my_awesome_cache_options:
|
4
|
+
# debug: false
|
5
|
+
# adapter: file
|
6
|
+
# marshall_method: none
|
7
|
+
# default_expiry: <%= 24.hours %>
|
8
|
+
# store_options:
|
9
|
+
# dir: /usr/local/caches/
|
10
|
+
# logging:
|
11
|
+
# logger_1:
|
12
|
+
# type: file
|
13
|
+
# file: log/file_store_cache.log
|
14
|
+
class Cachetastic::Adapters::File < Cachetastic::Adapters::FileBase
|
15
|
+
|
16
|
+
def get(key)
|
17
|
+
full_path = full_path_from_dir(get_key_directoy(key, false))
|
18
|
+
return nil unless File.exists?(full_path)
|
19
|
+
so = YAML::load(File.open(full_path).read)
|
20
|
+
if so
|
21
|
+
if so.invalid?
|
22
|
+
self.delete(key)
|
23
|
+
return nil
|
24
|
+
end
|
25
|
+
if so.value.is_a?(YAML::Object)
|
26
|
+
require so.value.class.underscore
|
27
|
+
so = YAML::load(File.open(full_path).read)
|
28
|
+
end
|
29
|
+
return so.value
|
30
|
+
end
|
31
|
+
return nil
|
32
|
+
end
|
33
|
+
|
34
|
+
def set(key, value, expiry = 0)
|
35
|
+
so = Cachetastic::Adapters::StoreObject.new(key.to_s, value, expiry)
|
36
|
+
File.open(full_path_from_key(key), "w") do |f|
|
37
|
+
f.puts YAML.dump(so)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
protected
|
42
|
+
def store_file_name
|
43
|
+
return STORE_FILE_NAME
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
STORE_FILE_NAME = "cache.yml"
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'digest/md5'
|
2
|
+
require 'base64'
|
3
|
+
class Cachetastic::Adapters::FileBase < Cachetastic::Adapters::Base
|
4
|
+
|
5
|
+
attr_reader :directory
|
6
|
+
attr_reader :hashed_keys
|
7
|
+
|
8
|
+
def setup
|
9
|
+
@directory = File.join(self.configuration.store_options.dir, self.name.to_s)
|
10
|
+
FileUtils.mkdir_p(self.directory, :verbose => self.debug?)
|
11
|
+
@hashed_keys = {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def valid?
|
15
|
+
File.exists?(self.directory)
|
16
|
+
end
|
17
|
+
|
18
|
+
def stats
|
19
|
+
super
|
20
|
+
num_files = num_directories = file_size = 0
|
21
|
+
everything = Dir.glob("#{self.directory}/**/*")
|
22
|
+
everything.reject{|x| x =~ /^\./}.each do |entry|
|
23
|
+
if ::File.directory?(entry)
|
24
|
+
num_directories += 1
|
25
|
+
else
|
26
|
+
file_size += ::File.size(entry)
|
27
|
+
num_files += 1
|
28
|
+
end
|
29
|
+
end
|
30
|
+
puts "Number of Files: #{num_files}\nNumber of Directories: #{num_directories}\nTotal Size on Disk: #{file_size/1024.to_f} KB\n\n"
|
31
|
+
end
|
32
|
+
|
33
|
+
def delete(key, delay = 0)
|
34
|
+
if delay <= 0
|
35
|
+
FileUtils.rm_rf(get_key_directoy(key), :verbose => self.debug?)
|
36
|
+
else
|
37
|
+
so = self.get(key)
|
38
|
+
if so
|
39
|
+
self.set(so.key, so.value, delay)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def expire_all
|
45
|
+
FileUtils.rm_rf(self.directory, :verbose => self.debug?)
|
46
|
+
setup
|
47
|
+
end
|
48
|
+
|
49
|
+
protected
|
50
|
+
def store_file_name
|
51
|
+
return "cachetastic.data"
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
def directory_from_key(key)
|
56
|
+
hkey = Base64.encode64(Digest::MD5.digest(key))
|
57
|
+
hkey.gsub!("==\n", "")
|
58
|
+
i = 0
|
59
|
+
path = ""
|
60
|
+
until i >= hkey.length do
|
61
|
+
path = File.join(path, hkey[i..i+2])
|
62
|
+
i += 3
|
63
|
+
end
|
64
|
+
path
|
65
|
+
end
|
66
|
+
|
67
|
+
def full_path_from_key(key)
|
68
|
+
full_path_from_dir(get_key_directoy(key))
|
69
|
+
end
|
70
|
+
|
71
|
+
def full_path_from_dir(dir)
|
72
|
+
File.join(dir, store_file_name)
|
73
|
+
end
|
74
|
+
|
75
|
+
def get_key_directoy(key, mkdir = true)
|
76
|
+
hkey = self.hashed_keys[key.to_sym]
|
77
|
+
if hkey.nil?
|
78
|
+
path = File.join(self.directory, directory_from_key(key))
|
79
|
+
self.hashed_keys[key.to_sym] = path
|
80
|
+
hkey = path
|
81
|
+
end
|
82
|
+
FileUtils.mkdir_p(hkey, :verbose => self.debug?) if mkdir
|
83
|
+
hkey
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# This adapter uses the file system as it's backing.
|
2
|
+
# Caches are stored in files called index.html.
|
3
|
+
# At the beginning of the html the following html comments are pre-pended:
|
4
|
+
# <!-- cachetastic: expires_at: Fri Feb 01 20:51:45 -0500 2008 -->
|
5
|
+
# <!-- cachetastic: key: /channels/1-arts-humanities -->
|
6
|
+
# Obviously the expires_at date will be the ACTUAL expire date,
|
7
|
+
# same goes for the key.
|
8
|
+
# The configuration for this should look something like this:
|
9
|
+
# my_awesome_cache_options:
|
10
|
+
# debug: false
|
11
|
+
# adapter: html_file
|
12
|
+
# marshall_method: none
|
13
|
+
# default_expiry: <%= 24.hours %>
|
14
|
+
# store_options:
|
15
|
+
# dir: /usr/local/caches/
|
16
|
+
# logging:
|
17
|
+
# logger_1:
|
18
|
+
# type: file
|
19
|
+
# file: log/file_store_cache.log
|
20
|
+
require 'time'
|
21
|
+
class Cachetastic::Adapters::HtmlFile < Cachetastic::Adapters::FileBase
|
22
|
+
|
23
|
+
def get(key)
|
24
|
+
full_path = full_path_from_dir(get_key_directoy(key, false))
|
25
|
+
return nil unless File.exists?(full_path)
|
26
|
+
so = html_to_store_object(File.open(full_path).read)
|
27
|
+
if so
|
28
|
+
if so.invalid?
|
29
|
+
self.delete(key)
|
30
|
+
return nil
|
31
|
+
end
|
32
|
+
return so.value
|
33
|
+
end
|
34
|
+
return nil
|
35
|
+
end
|
36
|
+
|
37
|
+
def set(key, value, expiry = 0)
|
38
|
+
so = Cachetastic::Adapters::StoreObject.new(key.to_s, value, expiry)
|
39
|
+
File.open(full_path_from_key(key), "w") do |f|
|
40
|
+
f.puts store_object_to_html(so)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
protected
|
45
|
+
def store_file_name
|
46
|
+
return STORE_FILE_NAME
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
def html_to_store_object(html)
|
51
|
+
key = html.match(/<!-- cachetastic: key: (.*) -->/).captures.first
|
52
|
+
expires_at = html.match(/<!-- cachetastic: expires_at: (.*) -->/).captures.first
|
53
|
+
html.gsub!(/<!-- cachetastic: key: .* -->/, '')
|
54
|
+
html.gsub!(/<!-- cachetastic: expires_at: .* -->/, '')
|
55
|
+
so = Cachetastic::Adapters::StoreObject.new(key, html, 0)
|
56
|
+
so.expires_at = Time.parse(expires_at)
|
57
|
+
so
|
58
|
+
end
|
59
|
+
|
60
|
+
def store_object_to_html(so)
|
61
|
+
x = so.value
|
62
|
+
x << "\n<!-- cachetastic: expires_at: #{so.expires_at} -->"
|
63
|
+
x << "\n<!-- cachetastic: key: #{so.key} -->"
|
64
|
+
end
|
65
|
+
|
66
|
+
STORE_FILE_NAME = "index.html"
|
67
|
+
|
68
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# An adapter/store that keeps objects in local memory. This is great for development/testing,
|
2
|
+
# but probably shouldn't be put into production.
|
3
|
+
# It's also a very good example of how to write a adapter.
|
4
|
+
class Cachetastic::Adapters::LocalMemory < Cachetastic::Adapters::Base
|
5
|
+
|
6
|
+
attr_accessor :local_store
|
7
|
+
|
8
|
+
def valid?
|
9
|
+
true
|
10
|
+
end
|
11
|
+
|
12
|
+
def setup
|
13
|
+
self.local_store = {}
|
14
|
+
end
|
15
|
+
|
16
|
+
def expire_all
|
17
|
+
self.local_store = {}
|
18
|
+
end
|
19
|
+
|
20
|
+
# See Cachetastic::Adapters::Base
|
21
|
+
def get(key)
|
22
|
+
so = self.local_store[key.to_s]
|
23
|
+
if so
|
24
|
+
if so.invalid?
|
25
|
+
self.delete(key)
|
26
|
+
return nil
|
27
|
+
end
|
28
|
+
return so.value
|
29
|
+
end
|
30
|
+
return nil
|
31
|
+
end
|
32
|
+
|
33
|
+
def set(key, value, expiry = 0)
|
34
|
+
self.local_store[key.to_s] = Cachetastic::Adapters::StoreObject.new(key.to_s, value, expiry)
|
35
|
+
end
|
36
|
+
|
37
|
+
def delete(key, delay = 0)
|
38
|
+
if delay <= 0
|
39
|
+
self.local_store.delete(key.to_s)
|
40
|
+
else
|
41
|
+
so = self.get(key)
|
42
|
+
if so
|
43
|
+
self.set(so.key, so.value, delay)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def stats
|
49
|
+
super
|
50
|
+
num_keys = self.local_store.size
|
51
|
+
s = "Number of Entries: #{num_keys}\n"
|
52
|
+
if num_keys > 0
|
53
|
+
expiries = []
|
54
|
+
keys = []
|
55
|
+
self.local_store.each do |key,value|
|
56
|
+
keys << key
|
57
|
+
expiries << value.expires_at
|
58
|
+
end
|
59
|
+
expiries.sort! {|x, y| x <=> y}
|
60
|
+
oldest_expiry = expiries.first
|
61
|
+
newest_expiry = expiries.last
|
62
|
+
s += "Oldest Entry: #{oldest_expiry}\nNewest Entry: #{newest_expiry}\nKeys: #{keys.inspect}\n"
|
63
|
+
end
|
64
|
+
puts s + "\n"
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# This adapter uses Memcache as it's backing.
|
2
|
+
# The configuration for this should look something like this:
|
3
|
+
# my_awesome_cache_options:
|
4
|
+
# debug: false
|
5
|
+
# adapter: memcache
|
6
|
+
# marshall_method: none
|
7
|
+
# default_expiry: <%= 24.hours %>
|
8
|
+
# store_options:
|
9
|
+
# c_threshold: 10_000
|
10
|
+
# compression: true
|
11
|
+
# debug: false
|
12
|
+
# readonly: false
|
13
|
+
# urlencode: false
|
14
|
+
# logging:
|
15
|
+
# logger_1:
|
16
|
+
# type: file
|
17
|
+
# file: log/memcached.log
|
18
|
+
# servers:
|
19
|
+
# - 127.0.0.1:11211
|
20
|
+
class Cachetastic::Adapters::Memcache < Cachetastic::Adapters::Base
|
21
|
+
|
22
|
+
def setup
|
23
|
+
self.conn = MemCache.new(configuration.servers, configuration.store_options.to_hash.merge({:namespace => self.namespace}))
|
24
|
+
self.version = self.get_version(self.name)
|
25
|
+
end
|
26
|
+
|
27
|
+
def set(key, value, expiry = 0, raw = false)
|
28
|
+
self.conn.set(key, value, expiry, raw)
|
29
|
+
end
|
30
|
+
|
31
|
+
def delete(key, delay = 0)
|
32
|
+
self.conn.delete(key, delay)
|
33
|
+
end
|
34
|
+
|
35
|
+
def get(key, raw = false)
|
36
|
+
self.conn.get(key, raw)
|
37
|
+
end
|
38
|
+
|
39
|
+
def expire_all
|
40
|
+
self.increment_version(self.name)
|
41
|
+
end
|
42
|
+
|
43
|
+
def inspect
|
44
|
+
self.conn.inspect + " <version: #{self.version}> #{self.conn.stats.inspect}"
|
45
|
+
end
|
46
|
+
|
47
|
+
def valid?
|
48
|
+
begin
|
49
|
+
return (self.conn.active? && self.version == self.get_version(self.name))
|
50
|
+
rescue Exception => e
|
51
|
+
puts e.message
|
52
|
+
puts e.backtrace.join("\n")
|
53
|
+
return false
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def stats
|
58
|
+
super
|
59
|
+
begin
|
60
|
+
puts "Memcache stats for all caches:"
|
61
|
+
memc = self.conn
|
62
|
+
puts Kernel.pp_to_s(memc.stats)
|
63
|
+
paths = `sh -c 'echo $PATH'`
|
64
|
+
paths = paths.split(':')
|
65
|
+
memcached_tool_found = false
|
66
|
+
paths.each do |path|
|
67
|
+
cmd_path = File.expand_path(File.join(path,'memcached_tool'))
|
68
|
+
if File.exists?(cmd_path)
|
69
|
+
memcached_tool_found = true
|
70
|
+
break
|
71
|
+
end
|
72
|
+
end
|
73
|
+
if memcached_tool_found
|
74
|
+
configuration.memcache_servers.each do |server|
|
75
|
+
puts `memcached_tool #{server}`
|
76
|
+
end
|
77
|
+
end
|
78
|
+
rescue
|
79
|
+
end
|
80
|
+
puts ""
|
81
|
+
end
|
82
|
+
|
83
|
+
protected
|
84
|
+
attr_accessor :conn
|
85
|
+
attr_accessor :version
|
86
|
+
|
87
|
+
def namespace
|
88
|
+
v = self.get_version(self.name)
|
89
|
+
return "#{name}.#{v}"
|
90
|
+
end
|
91
|
+
|
92
|
+
def ns_versions
|
93
|
+
ivar_cache do
|
94
|
+
ns_conn = MemCache.new(configuration.servers, configuration.store_options.to_hash.merge({:namespace => :namespace_versions}))
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def increment_version(name)
|
99
|
+
name = name.to_s
|
100
|
+
v = get_version(name)
|
101
|
+
self.ns_versions.set(name, v + 1)
|
102
|
+
end
|
103
|
+
|
104
|
+
def get_version(name)
|
105
|
+
name = name.to_s
|
106
|
+
v = self.ns_versions.get(name)
|
107
|
+
if v.nil?
|
108
|
+
self.ns_versions.set(name, 1)
|
109
|
+
v = 1
|
110
|
+
end
|
111
|
+
v
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class Cachetastic::Adapters::StoreObject #:nodoc:#
|
2
|
+
attr_accessor :key
|
3
|
+
attr_accessor :value
|
4
|
+
attr_accessor :expires_at
|
5
|
+
|
6
|
+
def initialize(key, value, expiry)
|
7
|
+
self.key = key
|
8
|
+
self.value = value
|
9
|
+
begin
|
10
|
+
self.expires_at = (Time.now + (expiry == 0 ? (31536000) : expiry)) # 31536000 = one year
|
11
|
+
rescue RangeError => e
|
12
|
+
self.expires_at = Time.at(expiry)
|
13
|
+
end
|
14
|
+
# puts "now: #{Time.now}"
|
15
|
+
# puts "expiry: #{expiry}"
|
16
|
+
# puts "expires_at: #{self.expires_at}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def size
|
20
|
+
return self.value.size if self.value.respond_to?(:size)
|
21
|
+
-1
|
22
|
+
end
|
23
|
+
|
24
|
+
def invalid?
|
25
|
+
Time.now >= self.expires_at
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|