sequel-cacheable 0.0.7 → 1.0.0
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/.gitignore +53 -0
- data/.pryrc +17 -0
- data/.rbenv-version +1 -0
- data/.travis.yml +4 -1
- data/Gemfile +21 -23
- data/Procfile +2 -0
- data/README.rdoc +4 -3
- data/Rakefile +1 -36
- data/lib/sequel-cacheable/class_methods.rb +74 -0
- data/lib/sequel-cacheable/dataset_methods.rb +53 -0
- data/lib/sequel-cacheable/driver/dalli.rb +19 -0
- data/lib/sequel-cacheable/driver/memcache.rb +15 -0
- data/lib/sequel-cacheable/driver/redis.rb +6 -0
- data/lib/sequel-cacheable/driver.rb +56 -0
- data/lib/sequel-cacheable/instance_methods.rb +66 -0
- data/lib/sequel-cacheable/packer.rb +34 -0
- data/lib/sequel-cacheable/version.rb +7 -0
- data/lib/sequel-cacheable.rb +18 -211
- data/sequel-cacheable.gemspec +18 -94
- data/spec/lib/sequel-cacheable/driver/dalli_driver_spec.rb +8 -0
- data/spec/lib/sequel-cacheable/driver/memcache_driver_spec.rb +7 -0
- data/spec/lib/sequel-cacheable/driver/redis_driver_spec.rb +8 -0
- data/spec/lib/sequel-cacheable/driver_spec.rb +35 -0
- data/spec/lib/sequel-cacheable_spec.rb +1 -243
- data/spec/models/dalli_spec.rb +5 -0
- data/spec/models/memcache_spec.rb +5 -0
- data/spec/models/redis_spec.rb +5 -0
- data/spec/shared/cacheable.rb +161 -0
- data/spec/shared/driver.rb +102 -0
- data/spec/spec_helper.rb +25 -28
- data/spec/support/models/dalli.rb +5 -0
- data/spec/support/models/memcache.rb +5 -0
- data/spec/support/models/redis.rb +5 -0
- metadata +57 -241
- data/Gemfile.lock +0 -66
- data/VERSION +0 -1
data/.gitignore
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
# rcov generated
|
2
|
+
coverage
|
3
|
+
coverage.data
|
4
|
+
|
5
|
+
# rdoc generated
|
6
|
+
rdoc
|
7
|
+
|
8
|
+
# yard generated
|
9
|
+
doc
|
10
|
+
.yardoc
|
11
|
+
|
12
|
+
# bundler
|
13
|
+
.bundle
|
14
|
+
|
15
|
+
# jeweler generated
|
16
|
+
pkg
|
17
|
+
|
18
|
+
Gemfile.lock
|
19
|
+
dump.rdb
|
20
|
+
tmp
|
21
|
+
|
22
|
+
# Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore:
|
23
|
+
#
|
24
|
+
# * Create a file at ~/.gitignore
|
25
|
+
# * Include files you want ignored
|
26
|
+
# * Run: git config --global core.excludesfile ~/.gitignore
|
27
|
+
#
|
28
|
+
# After doing this, these files will be ignored in all your git projects,
|
29
|
+
# saving you from having to 'pollute' every project you touch with them
|
30
|
+
#
|
31
|
+
# Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line)
|
32
|
+
#
|
33
|
+
# For MacOS:
|
34
|
+
#
|
35
|
+
#.DS_Store
|
36
|
+
|
37
|
+
# For TextMate
|
38
|
+
#*.tmproj
|
39
|
+
#tmtags
|
40
|
+
|
41
|
+
# For emacs:
|
42
|
+
#*~
|
43
|
+
#\#*
|
44
|
+
#.\#*
|
45
|
+
|
46
|
+
# For vim:
|
47
|
+
#*.swp
|
48
|
+
|
49
|
+
# For redcar:
|
50
|
+
#.redcar
|
51
|
+
|
52
|
+
# For rubinius:
|
53
|
+
#*.rbc
|
data/.pryrc
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
Bundler.require(:default, :development)
|
4
|
+
|
5
|
+
$: << "#{Dir.pwd}/lib"
|
6
|
+
require 'sequel-cacheable'
|
7
|
+
|
8
|
+
DB = Sequel.sqlite
|
9
|
+
DB.create_table(:mock) do
|
10
|
+
primary_key :id, :auto_increment => true
|
11
|
+
String :name
|
12
|
+
end
|
13
|
+
|
14
|
+
require 'memcache/null_server'
|
15
|
+
class Mock < Sequel::Model(:mock)
|
16
|
+
plugin :cacheable, Redis.new(server: 'localhost:6379'), :query_cache => true
|
17
|
+
end
|
data/.rbenv-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.9.3-p327
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
@@ -1,30 +1,28 @@
|
|
1
|
-
source
|
2
|
-
# Add dependencies required to use your gem here.
|
3
|
-
# Example:
|
4
|
-
# gem "activesupport", ">= 2.3.5"
|
1
|
+
source :rubygems
|
5
2
|
|
6
|
-
|
7
|
-
|
3
|
+
gem 'bundler'
|
4
|
+
gem 'rake'
|
8
5
|
|
9
|
-
|
10
|
-
gem 'msgpack', '~> 0.4.6'
|
11
|
-
gem 'hashr', '~> 0.0.19'
|
6
|
+
gemspec
|
12
7
|
|
13
8
|
group :development, :test do
|
14
|
-
gem
|
9
|
+
gem 'rspec', "~> 2.12.0"
|
10
|
+
gem 'yard', "~> 0.8.3"
|
11
|
+
gem 'rdoc', "~> 3.12"
|
12
|
+
gem 'simplecov', "~> 0.7.1", :require => false
|
15
13
|
|
16
|
-
gem
|
17
|
-
gem
|
18
|
-
gem
|
19
|
-
gem
|
20
|
-
gem 'simplecov', "~> 0.6.1", :require => false
|
14
|
+
gem 'guard', '~> 1.6.1'
|
15
|
+
gem 'guard-rspec', '~> 2.3.3'
|
16
|
+
gem 'terminal-notifier'
|
17
|
+
gem 'listen', '~> 0.7.0'
|
21
18
|
|
22
|
-
gem
|
23
|
-
gem
|
24
|
-
gem
|
25
|
-
|
26
|
-
gem
|
27
|
-
gem
|
28
|
-
gem
|
29
|
-
gem
|
19
|
+
gem 'sqlite3'
|
20
|
+
gem 'dalli'
|
21
|
+
gem 'memcache'
|
22
|
+
gem 'hiredis'
|
23
|
+
gem 'redis', :require => ['redis', 'redis/connection/hiredis']
|
24
|
+
gem 'pry'
|
25
|
+
gem 'pry-doc'
|
26
|
+
gem 'forgery'
|
27
|
+
gem 'tapp'
|
30
28
|
end
|
data/Procfile
ADDED
data/README.rdoc
CHANGED
@@ -23,15 +23,16 @@ Caching!
|
|
23
23
|
tested cache_client:
|
24
24
|
|
25
25
|
* Redis[https://rubygems.org/gems/redis]
|
26
|
+
* Hiredis[https://rubygems.org/gems/hiredis]
|
26
27
|
* Memcached[https://rubygems.org/gems/memcache]
|
28
|
+
* Dalli[https://rubygems.org/gems/dalli]
|
27
29
|
|
28
30
|
options:
|
29
31
|
|
30
32
|
{
|
31
33
|
:ttl => 3600, # time to live. default 3600
|
32
|
-
:ignore_exception => false, # through exception. default false
|
33
34
|
:pack_lib => MessagePack, # cache packing library. This Object should be have pack / unpack method.
|
34
|
-
:query_cache => false # enable query cache. default
|
35
|
+
:query_cache => false # enable query cache. default false
|
35
36
|
}
|
36
37
|
|
37
38
|
== Cache Timing
|
@@ -48,7 +49,7 @@ options:
|
|
48
49
|
* clear all query caches of model
|
49
50
|
|
50
51
|
== Contributing to sequel-cacheable
|
51
|
-
|
52
|
+
|
52
53
|
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
53
54
|
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
54
55
|
* Fork the project.
|
data/Rakefile
CHANGED
@@ -1,37 +1,2 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
|
3
|
-
require 'rubygems'
|
4
|
-
require 'bundler'
|
5
|
-
begin
|
6
|
-
Bundler.setup(:default, :development)
|
7
|
-
rescue Bundler::BundlerError => e
|
8
|
-
$stderr.puts e.message
|
9
|
-
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
-
exit e.status_code
|
11
|
-
end
|
12
|
-
require 'rake'
|
13
|
-
|
14
|
-
require 'jeweler'
|
15
|
-
Jeweler::Tasks.new do |gem|
|
16
|
-
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
17
|
-
gem.name = "sequel-cacheable"
|
18
|
-
gem.homepage = "http://github.com/rosylilly/sequel-cacheable"
|
19
|
-
gem.license = "MIT"
|
20
|
-
gem.summary = %Q{This plug-in caching mechanism to implement the Model of the Sequel}
|
21
|
-
gem.description = %Q{This plug-in caching mechanism to implement the Model of the Sequel}
|
22
|
-
gem.email = "rosylilly@aduca.org"
|
23
|
-
gem.authors = ["Sho Kusano <rosylilly>"]
|
24
|
-
# dependencies defined in Gemfile
|
25
|
-
end
|
26
|
-
Jeweler::RubygemsDotOrgTasks.new
|
27
|
-
|
28
|
-
require 'rspec/core'
|
29
|
-
require 'rspec/core/rake_task'
|
30
|
-
RSpec::Core::RakeTask.new(:spec) do |spec|
|
31
|
-
spec.pattern = FileList['spec/**/*_spec.rb']
|
32
|
-
end
|
33
|
-
|
34
|
-
task :default => :spec
|
35
|
-
|
36
|
-
require 'yard'
|
37
|
-
YARD::Rake::YardocTask.new
|
2
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
module Sequel::Plugins
|
4
|
+
module Cacheable
|
5
|
+
module ClassMethods
|
6
|
+
attr_reader :cache_driver, :cache_options, :caches
|
7
|
+
|
8
|
+
def inherited(subclass)
|
9
|
+
super
|
10
|
+
end
|
11
|
+
|
12
|
+
def primary_key_lookup(id)
|
13
|
+
cache_fetch(id.to_s) do
|
14
|
+
super
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def restore_by_cache(hash)
|
19
|
+
return nil if hash.nil?
|
20
|
+
|
21
|
+
return hash.map{|hs| restore_by_cache(hs) } if hash.is_a?(Array)
|
22
|
+
|
23
|
+
return hash if hash.kind_of?(Sequel::Model)
|
24
|
+
|
25
|
+
hash.keys.each do | key |
|
26
|
+
value = hash.delete(key)
|
27
|
+
key = key.to_sym rescue key
|
28
|
+
case db_schema[key][:type]
|
29
|
+
when :date
|
30
|
+
value = Date.new(*value)
|
31
|
+
when :time
|
32
|
+
value = Sequel::SQLTime.at(value[0], value[1])
|
33
|
+
when :datetime
|
34
|
+
value = Time.at(value[0], value[1])
|
35
|
+
when :decimal
|
36
|
+
value = BigDecimal.new(value)
|
37
|
+
when :integer
|
38
|
+
value = value.to_i
|
39
|
+
end
|
40
|
+
hash[key] = value
|
41
|
+
end
|
42
|
+
|
43
|
+
return new(hash, true)
|
44
|
+
end
|
45
|
+
|
46
|
+
def cache_key(key)
|
47
|
+
"#{self.name}:#{key}"
|
48
|
+
end
|
49
|
+
|
50
|
+
def cache_set(key, value, ttl = @cache_options[:ttl])
|
51
|
+
@caches[key.match(/\AQuery:/) ? :query : :instance] << key
|
52
|
+
cache_driver.set(cache_key(key), value, ttl)
|
53
|
+
end
|
54
|
+
|
55
|
+
def cache_get(key)
|
56
|
+
restore_by_cache(cache_driver.get(cache_key(key)))
|
57
|
+
end
|
58
|
+
|
59
|
+
def cache_fetch(key, ttl = @cache_options[:ttl], &block)
|
60
|
+
@caches[key.match(/\AQuery:/) ? :query : :instance] << key
|
61
|
+
cache_driver.fetch(cache_key(key), ttl, &block)
|
62
|
+
end
|
63
|
+
|
64
|
+
def cache_del(key)
|
65
|
+
@caches[key.match(/\AQuery:/) ? :query : :instance].delete(key)
|
66
|
+
cache_driver.del(cache_key(key))
|
67
|
+
end
|
68
|
+
|
69
|
+
def cache_clear(type)
|
70
|
+
@caches[type].dup.each {|key| cache_del(key) }
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
module Sequel::Plugins
|
4
|
+
module Cacheable
|
5
|
+
module DatasetMethods
|
6
|
+
def all(*args, &block)
|
7
|
+
if(
|
8
|
+
model &&
|
9
|
+
model.respond_to?(:cache_fetch) &&
|
10
|
+
model.cache_options[:query_cache] &&
|
11
|
+
@row_proc.kind_of?(Class) &&
|
12
|
+
@row_proc.included_modules.include?(Sequel::Model::InstanceMethods)
|
13
|
+
)
|
14
|
+
@row_proc.cache_fetch(cache_key) do
|
15
|
+
super(*args, &block)
|
16
|
+
end
|
17
|
+
else
|
18
|
+
super(*args, &block)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def first(*args)
|
23
|
+
if(
|
24
|
+
model &&
|
25
|
+
model.respond_to?(:cache_fetch) &&
|
26
|
+
model.cache_options[:query_cache] &&
|
27
|
+
@row_proc.kind_of?(Class) &&
|
28
|
+
@row_proc.included_modules.include?(Sequel::Model::InstanceMethods)
|
29
|
+
)
|
30
|
+
@row_proc.cache_fetch("#{cache_key}:first") do
|
31
|
+
super(*args)
|
32
|
+
end
|
33
|
+
else
|
34
|
+
super(*args)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def with_pk(pk)
|
39
|
+
if pk.is_a(Integer)
|
40
|
+
model.cache_fetch(pk.to_s) do
|
41
|
+
super(pk)
|
42
|
+
end
|
43
|
+
else
|
44
|
+
super(pk)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def cache_key
|
49
|
+
"Query:#{select_sql.gsub(/ +/, '_')}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Sequel::Plugins
|
2
|
+
module Cacheable
|
3
|
+
class DalliDriver < Driver
|
4
|
+
def del(key)
|
5
|
+
@store.delete(key)
|
6
|
+
|
7
|
+
return nil
|
8
|
+
end
|
9
|
+
|
10
|
+
def expire(key, time)
|
11
|
+
if time > 0
|
12
|
+
@store.touch(key, time)
|
13
|
+
else
|
14
|
+
@store.delete(key)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Sequel::Plugins
|
2
|
+
module Cacheable
|
3
|
+
DRIVERS = %w(memcache dalli redis).freeze
|
4
|
+
|
5
|
+
class Driver
|
6
|
+
def self.factory(*args)
|
7
|
+
case args[0].class.name
|
8
|
+
when 'Memcache'
|
9
|
+
MemcacheDriver.new(*args)
|
10
|
+
when 'Dalli::Client'
|
11
|
+
DalliDriver.new(*args)
|
12
|
+
when 'Redis'
|
13
|
+
RedisDriver.new(*args)
|
14
|
+
else
|
15
|
+
Driver.new(*args)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(store, pack_lib = MessagePackPacker)
|
20
|
+
@store = store
|
21
|
+
@packer = pack_lib
|
22
|
+
end
|
23
|
+
|
24
|
+
def get(key)
|
25
|
+
val = @store.get(key)
|
26
|
+
|
27
|
+
return val.nil? || val.empty? ? nil : @packer.unpack(val)
|
28
|
+
end
|
29
|
+
|
30
|
+
def set(key, val, expire = nil)
|
31
|
+
@store.set(key, @packer.pack(val))
|
32
|
+
expire(key, expire) unless expire.nil?
|
33
|
+
|
34
|
+
return val
|
35
|
+
end
|
36
|
+
|
37
|
+
def del(key)
|
38
|
+
@store.del(key)
|
39
|
+
|
40
|
+
return nil
|
41
|
+
end
|
42
|
+
|
43
|
+
def expire(key, time)
|
44
|
+
@store.expire(key, time)
|
45
|
+
end
|
46
|
+
|
47
|
+
def fetch(key, *args, &block)
|
48
|
+
get(key) || set(key, block.call(*args))
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
Sequel::Plugins::Cacheable::DRIVERS.each do |driver|
|
55
|
+
require "sequel-cacheable/driver/#{driver}"
|
56
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
module Sequel::Plugins
|
4
|
+
module Cacheable
|
5
|
+
module InstanceMethods
|
6
|
+
def after_initialize
|
7
|
+
super
|
8
|
+
cache! unless id.nil?
|
9
|
+
end
|
10
|
+
|
11
|
+
def after_save
|
12
|
+
super
|
13
|
+
recache!
|
14
|
+
end
|
15
|
+
|
16
|
+
def delete(*args)
|
17
|
+
uncache!
|
18
|
+
super
|
19
|
+
end
|
20
|
+
|
21
|
+
def destory(*args)
|
22
|
+
uncache!
|
23
|
+
super(*args)
|
24
|
+
end
|
25
|
+
|
26
|
+
def cache!
|
27
|
+
model.cache_set(cache_key, self)
|
28
|
+
end
|
29
|
+
|
30
|
+
def uncache!
|
31
|
+
model.cache_del(cache_key)
|
32
|
+
model.cache_clear(:query)
|
33
|
+
end
|
34
|
+
|
35
|
+
def recache!
|
36
|
+
uncache!
|
37
|
+
cache!
|
38
|
+
end
|
39
|
+
|
40
|
+
def cache_key
|
41
|
+
"#{self.id.to_s}"
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_msgpack(*args)
|
45
|
+
msgpack_hash.to_msgpack
|
46
|
+
end
|
47
|
+
|
48
|
+
def msgpack_hash
|
49
|
+
hash = {}
|
50
|
+
@values.each_pair do | key, value |
|
51
|
+
case value
|
52
|
+
when Date
|
53
|
+
value = [value.year, value.mon, value.mday, value.start]
|
54
|
+
when Sequel::SQLTime, Time
|
55
|
+
value = [value.to_i, value.usec]
|
56
|
+
when BigDecimal, Bignum
|
57
|
+
value = value.to_s
|
58
|
+
end
|
59
|
+
hash[key] = value
|
60
|
+
end
|
61
|
+
hash
|
62
|
+
end
|
63
|
+
private :msgpack_hash
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'msgpack'
|
2
|
+
|
3
|
+
module Sequel::Plugins
|
4
|
+
module Cacheable
|
5
|
+
class Packer
|
6
|
+
def self.factory(lib)
|
7
|
+
case lib
|
8
|
+
when MessagePack
|
9
|
+
MessagePackPacker.new
|
10
|
+
else
|
11
|
+
Packer.new
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def pack(obj)
|
16
|
+
Marshal.dump(obj)
|
17
|
+
end
|
18
|
+
|
19
|
+
def unpack(string)
|
20
|
+
Marshal.load(string)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class MessagePackPacker < Packer
|
25
|
+
def pack(obj)
|
26
|
+
obj.to_msgpack
|
27
|
+
end
|
28
|
+
|
29
|
+
def unpack(string)
|
30
|
+
MessagePack.unpack(string)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|