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 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
@@ -1,5 +1,8 @@
1
+ services:
2
+ - memcached
3
+ - redis-server
1
4
  bundler_args: --without development
2
- script: bundle exec rake spec
5
+ script: bundle exec rspec
3
6
  language: ruby
4
7
  rvm:
5
8
  - 1.8.7
data/Gemfile CHANGED
@@ -1,30 +1,28 @@
1
- source "http://rubygems.org"
2
- # Add dependencies required to use your gem here.
3
- # Example:
4
- # gem "activesupport", ">= 2.3.5"
1
+ source :rubygems
5
2
 
6
- # Add dependencies to develop your gem here.
7
- # Include everything needed to run rake, tests, features, etc.
3
+ gem 'bundler'
4
+ gem 'rake'
8
5
 
9
- gem 'sequel', '~> 3.34'
10
- gem 'msgpack', '~> 0.4.6'
11
- gem 'hashr', '~> 0.0.19'
6
+ gemspec
12
7
 
13
8
  group :development, :test do
14
- gem "bundler", ">= 1.0.0"
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 "rspec", "~> 2"
17
- gem "yard", "~> 0.7"
18
- gem "rdoc", "~> 3.12"
19
- gem "jeweler", "~> 1.8.3"
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 "guard", '~> 1.0.0'
23
- gem "guard-rspec", '~> 0.7'
24
- gem "growl"
25
-
26
- gem "sqlite3"
27
- gem "memcache"
28
- gem "redis"
29
- gem "queencheck"
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
@@ -0,0 +1,2 @@
1
+ memcached: memcached
2
+ redis: redis-server
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 true if cache client implemented keys method
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,15 @@
1
+ module Sequel::Plugins
2
+ module Cacheable
3
+ class MemcacheDriver < Driver
4
+ def del(key)
5
+ @store.delete(key)
6
+
7
+ return nil
8
+ end
9
+
10
+ def expire(key, time)
11
+ @store.set(key, @store.get(key), time)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,6 @@
1
+ module Sequel::Plugins
2
+ module Cacheable
3
+ class RedisDriver < Driver
4
+ end
5
+ end
6
+ 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
@@ -0,0 +1,7 @@
1
+ module Sequel
2
+ module Plugins
3
+ module Cacheable
4
+ VERSION = '1.0.0'
5
+ end
6
+ end
7
+ end