rcache 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.swp
19
+ dump.rdb
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rcache.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 novikov
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,48 @@
1
+ # Rcache
2
+
3
+ Caches activerecord query results to memory and redis
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'rcache'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install rcache
18
+
19
+ In your initializers:
20
+
21
+ Rcache.configure do |config|
22
+ config.redis = <your connect to redis>
23
+ end
24
+
25
+ Optional settings:
26
+
27
+ 1. expires_in (default 60)
28
+ 2. log_cached_queries (default true)
29
+ 3. key_prefix (default rcache::)
30
+
31
+
32
+ ## Usage
33
+
34
+ Cache find result for 60 seconds:
35
+
36
+ Client.rcache.find(2)
37
+
38
+ Cache where results with includes for 10 seconds and not show cached queries in log:
39
+
40
+ Event.limit(2).includes(:thumbnails, :type => :variable).rcache(:expires_in => 10.seconds, :log_cached_queries => false)
41
+
42
+ ## Contributing
43
+
44
+ 1. Fork it
45
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
46
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
47
+ 4. Push to the branch (`git push origin my-new-feature`)
48
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,28 @@
1
+ module ActiveRecord
2
+ module Associations
3
+ class Preloader
4
+ class Association #:nodoc:
5
+ def build_scope
6
+ scope = klass.scoped
7
+
8
+ scope = scope.where(process_conditions(options[:conditions]))
9
+ scope = scope.where(process_conditions(preload_options[:conditions]))
10
+
11
+ scope = scope.select(preload_options[:select] || options[:select] || table[Arel.star])
12
+ scope = scope.includes(preload_options[:include] || options[:include])
13
+
14
+ if options[:as]
15
+ scope = scope.where(
16
+ klass.table_name => {
17
+ reflection.type => model.base_class.sti_name
18
+ }
19
+ )
20
+ end
21
+ scope = scope.rcache(preload_options[:rcache_value]) if preload_options[:rcache_value]
22
+
23
+ scope
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,12 @@
1
+ module ActiveRecord
2
+ module Associations
3
+ class Preloader #:nodoc:
4
+ def preload_hash(association)
5
+ association.each do |parent, child|
6
+ Preloader.new(records, parent, options).run
7
+ Preloader.new(records.map { |record| record.send(parent) }.flatten, child, options.select { |k,v| k == :rcache_value }).run
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,50 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters # :nodoc:
3
+ module QueryCache
4
+ attr_accessor :rcache_value
5
+
6
+ def select_all(arel, name = nil, binds = [])
7
+ if @rcache_value && !locked?(arel) && (@rcache_value[:expires_in] || Rcache.expires_in).to_i > 0
8
+ sql = to_sql(arel, binds)
9
+ redis_cache_sql(sql, binds) { super(sql, name, binds) }
10
+ elsif @query_cache_enabled && !locked?(arel)
11
+ sql = to_sql(arel, binds)
12
+ cache_sql(sql, binds) { super(sql, name, binds) }
13
+ else
14
+ super
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def redis_cache_sql(sql, binds)
21
+ [:redis, :expires_in, :log_cached_queries, :key_prefix].each do |attr|
22
+ instance_variable_set("@#{attr}", @rcache_value.has_key?(attr) ? @rcache_value[attr] : Rcache.send(attr))
23
+ end
24
+
25
+ result =
26
+ # return from memory
27
+ if @query_cache[sql].key?(binds)
28
+ ActiveSupport::Notifications.instrument("sql.active_record", :sql => sql, :binds => binds, :name => "CACHE", :connection_id => object_id) if @log_cached_queries
29
+ @query_cache[sql][binds]
30
+ # write to memory from redis and return
31
+ elsif res = (JSON.parse(@redis.get(redis_cache_key(sql, binds, @key_prefix))) rescue nil)
32
+ ActiveSupport::Notifications.instrument("sql.active_record", :sql => sql, :binds => binds, :name => "REDIS", :connection_id => object_id) if @log_cached_queries
33
+ @query_cache[sql][binds] = res
34
+ # write to memory and redis from db and return
35
+ else
36
+ res = yield
37
+ @query_cache[sql][binds] = res
38
+ @redis.setex(redis_cache_key(sql, binds, @key_prefix), @expires_in, res.to_json)
39
+ res
40
+ end
41
+
42
+ result.collect { |row| row.dup }
43
+ end
44
+
45
+ def redis_cache_key(sql, binds, key_prefix)
46
+ key_prefix + Digest::MD5.hexdigest(sql + binds.to_s)
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,8 @@
1
+ module ActiveRecord
2
+ module QueryMethods
3
+ def rcache(opts = {})
4
+ connection.rcache_value = opts
5
+ self
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,5 @@
1
+ module ActiveRecord
2
+ module Querying
3
+ delegate :rcache, :to => :scoped
4
+ end
5
+ end
@@ -0,0 +1,37 @@
1
+ module ActiveRecord
2
+ class Relation
3
+ def exec_queries
4
+ return @records if loaded?
5
+
6
+ default_scoped = with_default_scope
7
+
8
+ if default_scoped.equal?(self)
9
+ @records = if @readonly_value.nil? && !@klass.locking_enabled?
10
+ eager_loading? ? find_with_associations : @klass.find_by_sql(arel, @bind_values)
11
+ else
12
+ IdentityMap.without do
13
+ eager_loading? ? find_with_associations : @klass.find_by_sql(arel, @bind_values)
14
+ end
15
+ end
16
+
17
+ preload = @preload_values
18
+ preload += @includes_values unless eager_loading?
19
+ preload.each do |associations|
20
+ # this line distincts only
21
+ ActiveRecord::Associations::Preloader.new(@records, associations, :rcache_value => @klass.connection.rcache_value).run
22
+ end
23
+
24
+ # @readonly_value is true only if set explicitly. @implicit_readonly is true if there
25
+ # are JOINS and no explicit SELECT.
26
+ readonly = @readonly_value.nil? ? @implicit_readonly : @readonly_value
27
+ @records.each { |record| record.readonly! } if readonly
28
+ else
29
+ @records = default_scoped.to_a
30
+ end
31
+
32
+ @loaded = true
33
+ @records
34
+ end
35
+ private :exec_queries
36
+ end
37
+ end
@@ -0,0 +1,3 @@
1
+ module Rcache
2
+ VERSION = "0.0.1"
3
+ end
data/lib/rcache.rb ADDED
@@ -0,0 +1,23 @@
1
+ require "rcache/version"
2
+ require "json"
3
+ require "digest/md5"
4
+ require "rcache/query_cache"
5
+ require "rcache/query_methods"
6
+ require "rcache/relation"
7
+ require "rcache/association"
8
+ require "rcache/querying"
9
+ require "rcache/preloader"
10
+
11
+ module Rcache
12
+ class << self
13
+ attr_accessor :redis, :expires_in, :log_cached_queries, :key_prefix
14
+
15
+ def configure
16
+ yield self
17
+ end
18
+ end
19
+
20
+ self.expires_in = 60
21
+ self.log_cached_queries = true
22
+ self.key_prefix = 'rcache::'
23
+ end
data/rcache.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'rcache/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "rcache"
8
+ spec.version = Rcache::VERSION
9
+ spec.authors = ["novikov"]
10
+ spec.email = ["novikov@inventos.ru"]
11
+ spec.description = %q{ActiveRecord redis query cache}
12
+ spec.summary = %q{ActiveRecord redis query cache}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_runtime_dependency "json"
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.3"
24
+ spec.add_development_dependency "rake"
25
+ end
metadata ADDED
@@ -0,0 +1,114 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rcache
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - novikov
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-12-14 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: json
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: bundler
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: '1.3'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: '1.3'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rake
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: ActiveRecord redis query cache
63
+ email:
64
+ - novikov@inventos.ru
65
+ executables: []
66
+ extensions: []
67
+ extra_rdoc_files: []
68
+ files:
69
+ - .gitignore
70
+ - Gemfile
71
+ - LICENSE.txt
72
+ - README.md
73
+ - Rakefile
74
+ - lib/rcache.rb
75
+ - lib/rcache/association.rb
76
+ - lib/rcache/preloader.rb
77
+ - lib/rcache/query_cache.rb
78
+ - lib/rcache/query_methods.rb
79
+ - lib/rcache/querying.rb
80
+ - lib/rcache/relation.rb
81
+ - lib/rcache/version.rb
82
+ - rcache.gemspec
83
+ homepage: ''
84
+ licenses:
85
+ - MIT
86
+ post_install_message:
87
+ rdoc_options: []
88
+ require_paths:
89
+ - lib
90
+ required_ruby_version: !ruby/object:Gem::Requirement
91
+ none: false
92
+ requirements:
93
+ - - ! '>='
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ segments:
97
+ - 0
98
+ hash: -3051535261146459295
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
100
+ none: false
101
+ requirements:
102
+ - - ! '>='
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ segments:
106
+ - 0
107
+ hash: -3051535261146459295
108
+ requirements: []
109
+ rubyforge_project:
110
+ rubygems_version: 1.8.24
111
+ signing_key:
112
+ specification_version: 3
113
+ summary: ActiveRecord redis query cache
114
+ test_files: []