cache_money_millionaire 0.0.1
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 +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +65 -0
- data/Rakefile +1 -0
- data/cache_money_millionaire.gemspec +23 -0
- data/lib/cache_money_millionaire/config.rb +30 -0
- data/lib/cache_money_millionaire/rails/active_record_patch.rb +62 -0
- data/lib/cache_money_millionaire/rails/cache_key_patch.rb +30 -0
- data/lib/cache_money_millionaire/rails/engine.rb +6 -0
- data/lib/cache_money_millionaire/version.rb +3 -0
- data/lib/cache_money_millionaire.rb +20 -0
- metadata +90 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 John Koht
|
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,65 @@
|
|
1
|
+
# CacheMoneyMillionaire
|
2
|
+
|
3
|
+
Rails ActiveRecord Query Caching like a Millionaire
|
4
|
+
|
5
|
+
CacheMoneyMillionaire is a simple Ruby gem that extends Rail's basic find, save and touch methods to create, set and update cache keys for ActiveRecord models.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
gem 'cache_money_millionaire'
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install cache_money_millionaire
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
In order to get started, make sure you add the initializer:
|
24
|
+
|
25
|
+
# config/initializers/cache_money_millionaire.rb
|
26
|
+
CacheMoneyMillionaire.configure do |config|
|
27
|
+
config.cacheable = true
|
28
|
+
config.expires_in = 30.days
|
29
|
+
end
|
30
|
+
|
31
|
+
Caching will only occur if the config.cacheable AND your Rails application is set to perform_caching. Please note that development environments have caching turned off, so if you want to test locally, make sure you update your development.rb.
|
32
|
+
|
33
|
+
That's it! Everything else is automatic!
|
34
|
+
|
35
|
+
### Skip Caching
|
36
|
+
|
37
|
+
If you want, you can ignore the cached version when fetching a model, to do so, simple do:
|
38
|
+
|
39
|
+
# Get a fresh copy of the record by bypassing the cached version
|
40
|
+
Post.find(3, cache: false)
|
41
|
+
|
42
|
+
### ActiveRecord Collection and Array Helper
|
43
|
+
|
44
|
+
There are two active record patches for the ActiveRecord Collections and Array. These allow you to call .cache_key on a collection or an array and receive an MD5 hash for caching:
|
45
|
+
|
46
|
+
# Cache key for all posts
|
47
|
+
Post.published.all.cache_key
|
48
|
+
# => "ba6a3c1d35e7c7d1b4acdcd5330f8891"
|
49
|
+
|
50
|
+
# Cache key for paginated post collection
|
51
|
+
Post.published.page(params[:page]).per(20)
|
52
|
+
# => "b7ff80c902b673c03b5f195dcf3e492e"
|
53
|
+
|
54
|
+
# Cache key for Arrays
|
55
|
+
tags = Post.find(3).tags
|
56
|
+
tags.cache_key
|
57
|
+
# => "9d72ecf5ab79e30cfe67fe5ee0b03aa9"
|
58
|
+
|
59
|
+
## Contributing
|
60
|
+
|
61
|
+
1. Fork it
|
62
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
63
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
64
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
65
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'cache_money_millionaire/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "cache_money_millionaire"
|
8
|
+
spec.version = CacheMoneyMillionaire::VERSION
|
9
|
+
spec.authors = ["John Koht"]
|
10
|
+
spec.email = ["john@kohactive.com"]
|
11
|
+
spec.description = "Rails ActiveRecord Query Caching like a Millionaire"
|
12
|
+
spec.summary = "Rails ActiveRecord Query Caching like a Millionaire"
|
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_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module CacheMoneyMillionaire
|
2
|
+
module Config
|
3
|
+
|
4
|
+
VALID_OPTION_KEYS = [
|
5
|
+
:cacheable,
|
6
|
+
:expires_in
|
7
|
+
]
|
8
|
+
|
9
|
+
attr_accessor *VALID_OPTION_KEYS
|
10
|
+
|
11
|
+
def configure
|
12
|
+
yield self
|
13
|
+
self
|
14
|
+
end
|
15
|
+
|
16
|
+
def options
|
17
|
+
options = {}
|
18
|
+
VALID_OPTION_KEYS.each{ |pname| options[pname] = send(pname) }
|
19
|
+
options
|
20
|
+
end
|
21
|
+
|
22
|
+
# Is the application cacheable? We'll only perform caching if the perform_caching
|
23
|
+
# setting is true
|
24
|
+
def cacheable?
|
25
|
+
Rails.application.config.action_controller.perform_caching and options[:cacheable]
|
26
|
+
#true
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
class Base
|
3
|
+
|
4
|
+
# Include the CacheMoneyMillionaire module
|
5
|
+
extend CacheMoneyMillionaire
|
6
|
+
|
7
|
+
|
8
|
+
# Extend the find method for ActiveRecord
|
9
|
+
# This method will create or fetch a record by a cache key. If the record exists in cache
|
10
|
+
# then we fetch it, otherwise, we'll create a cache key for it and store it in memory
|
11
|
+
# To ignore cache, pass cache: false as a parameter
|
12
|
+
|
13
|
+
def self.find *args
|
14
|
+
# extract the options and see if the :cache: option was passed, and set the skip_cache
|
15
|
+
# variable based on it's validity. Then we'll remove from the arguments and pass
|
16
|
+
# the original arguments back into our hash
|
17
|
+
options = args.extract_options!
|
18
|
+
cache = options.delete(:cache) || true
|
19
|
+
args.push(options)
|
20
|
+
|
21
|
+
# If it's cacheable and cache isn't set to false, then let's CacheMoneyMillionaire this bitch!
|
22
|
+
# We set the cache key to the object class name and id, i.e. Post 3, User 109, etc.
|
23
|
+
if CacheMoneyMillionaire.cacheable? and cache
|
24
|
+
model = Rails.cache.fetch (Digest::MD5.hexdigest "#{self} #{args[0]}"), expires_in: CacheMoneyMillionaire.options[:expires_in] do
|
25
|
+
super *args
|
26
|
+
end
|
27
|
+
else
|
28
|
+
# if no caching, then let's just ignore and call typical .find method
|
29
|
+
super *args
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
# Extend the save method for ActiveRecord
|
35
|
+
# When an object is saved, let's update the cache key (the one we created above) so we can
|
36
|
+
# invalidate our stale cache
|
37
|
+
def save *args
|
38
|
+
self.rewrite_cache
|
39
|
+
super
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
# Extend the touch method for ActiveRecord
|
44
|
+
# Similar to the save method extention, we'll just hijack the touch method and update the
|
45
|
+
# cache key value with our updated data
|
46
|
+
def touch
|
47
|
+
super
|
48
|
+
self.rewrite_cache
|
49
|
+
end
|
50
|
+
|
51
|
+
# rewrite the cache for this model
|
52
|
+
def rewrite_cache
|
53
|
+
if CacheMoneyMillionaire.cacheable?
|
54
|
+
cache_key = Digest::MD5.hexdigest "#{self.class.name} #{self.id}"
|
55
|
+
Rails.cache.write cache_key, self
|
56
|
+
else
|
57
|
+
false
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# CacheKey Patch
|
2
|
+
# Add cache_key methods to Arrays and ActiveRecord collections which allows us to
|
3
|
+
# easily cache sets of data without any problems.
|
4
|
+
|
5
|
+
# example: Posts.all.cache_key
|
6
|
+
# example: Posts.published.page(params[:page]).per(20).cache_key
|
7
|
+
|
8
|
+
# This is great for HTTP caching in the controller, here's a quick example:
|
9
|
+
# @posts = Post.scoped.published.page(params[:page]).per(10)
|
10
|
+
# if stale?(etag: @posts.cache_key, last_modified: @posts.maximum(:updated_at), public: true)
|
11
|
+
# ...
|
12
|
+
# end
|
13
|
+
|
14
|
+
|
15
|
+
# Extend Array to include a cache_key method
|
16
|
+
class Array
|
17
|
+
def cache_key
|
18
|
+
Digest::MD5.hexdigest "#{self.count}-#{self.inspect}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
# Extend ActiveRecord::Base Collections to include a cache_key method
|
24
|
+
module ActiveRecord
|
25
|
+
class Base
|
26
|
+
def self.cache_key
|
27
|
+
Digest::MD5.hexdigest "#{scoped.maximum(:updated_at).try(:to_i)}-#{scoped.count}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require "cache_money_millionaire/version"
|
2
|
+
require "cache_money_millionaire/config"
|
3
|
+
require "cache_money_millionaire/rails/active_record_patch"
|
4
|
+
require "cache_money_millionaire/rails/cache_key_patch"
|
5
|
+
|
6
|
+
module CacheMoneyMillionaire
|
7
|
+
|
8
|
+
extend Config
|
9
|
+
|
10
|
+
class << self
|
11
|
+
def initialize attrs={}
|
12
|
+
puts "initialize..."
|
13
|
+
attrs = CacheMoneyMillionaire.options.merge(attrs)
|
14
|
+
Config::VALID_OPTION_KEYS.each do |key|
|
15
|
+
instance_variable_set("@#{key}".to_sym, attrs[key])
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
metadata
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cache_money_millionaire
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- John Koht
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-03-27 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.3'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '1.3'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rake
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
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: '0'
|
46
|
+
description: Rails ActiveRecord Query Caching like a Millionaire
|
47
|
+
email:
|
48
|
+
- john@kohactive.com
|
49
|
+
executables: []
|
50
|
+
extensions: []
|
51
|
+
extra_rdoc_files: []
|
52
|
+
files:
|
53
|
+
- .gitignore
|
54
|
+
- Gemfile
|
55
|
+
- LICENSE.txt
|
56
|
+
- README.md
|
57
|
+
- Rakefile
|
58
|
+
- cache_money_millionaire.gemspec
|
59
|
+
- lib/cache_money_millionaire.rb
|
60
|
+
- lib/cache_money_millionaire/config.rb
|
61
|
+
- lib/cache_money_millionaire/rails/active_record_patch.rb
|
62
|
+
- lib/cache_money_millionaire/rails/cache_key_patch.rb
|
63
|
+
- lib/cache_money_millionaire/rails/engine.rb
|
64
|
+
- lib/cache_money_millionaire/version.rb
|
65
|
+
homepage: ''
|
66
|
+
licenses:
|
67
|
+
- MIT
|
68
|
+
post_install_message:
|
69
|
+
rdoc_options: []
|
70
|
+
require_paths:
|
71
|
+
- lib
|
72
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
|
+
none: false
|
80
|
+
requirements:
|
81
|
+
- - ! '>='
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
requirements: []
|
85
|
+
rubyforge_project:
|
86
|
+
rubygems_version: 1.8.25
|
87
|
+
signing_key:
|
88
|
+
specification_version: 3
|
89
|
+
summary: Rails ActiveRecord Query Caching like a Millionaire
|
90
|
+
test_files: []
|