has_cache_key 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ group :test, :development do
6
+ gem 'rspec'
7
+ gem 'sqlite3'
8
+ end
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2012 YOURNAME
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,41 @@
1
+ HasCacheKey -- automatic cache key management For Rails
2
+ ================================
3
+
4
+ Allows you describe cache keys in the models, and provides automatic expiration
5
+
6
+ Installation
7
+ ------------
8
+
9
+ Add this line to your Gemfile:
10
+
11
+ gem 'has_cache_key'
12
+
13
+ Usage
14
+ -----
15
+
16
+ Define cache_keys in your model
17
+
18
+ class Listing < ActiveRecord::Base
19
+ # Default cache key for the listing view
20
+ has_cache_key :id
21
+ # A cache key for the home page of category in location
22
+ has_cache_key [:location_id, :category_id], name: :category_in_location_home_page
23
+ end
24
+
25
+ To get a cache key in you can use one of the following:
26
+
27
+
28
+ # Default cache key
29
+ @listing.cache_key
30
+
31
+ # A cache key
32
+ @listing.cache_key(:category_in_location_home_page)
33
+
34
+ # A cache key without a listing instance:
35
+ Listing.cache_key(:category_in_location_home_page, category_id: 10, location_id: 10)
36
+
37
+
38
+ Your cache keys will be automatically updates after_save and after_destroy according to the attribute changes.
39
+
40
+
41
+ This project uses MIT-LICENSE.
data/Rakefile ADDED
@@ -0,0 +1,22 @@
1
+ # encoding: UTF-8
2
+ require 'rubygems'
3
+ begin
4
+ require 'bundler/setup'
5
+ rescue LoadError
6
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
7
+ end
8
+
9
+ require 'rake'
10
+ require 'rdoc/task'
11
+ require 'rspec/core/rake_task'
12
+
13
+ RSpec::Core::RakeTask.new(:spec)
14
+ task :default => :spec
15
+
16
+ Rake::RDocTask.new(:rdoc) do |rdoc|
17
+ rdoc.rdoc_dir = 'rdoc'
18
+ rdoc.title = 'Has Cache Key'
19
+ rdoc.options << '--line-numbers' << '--inline-source'
20
+ rdoc.rdoc_files.include('README.rdoc')
21
+ rdoc.rdoc_files.include('lib/**/*.rb')
22
+ end
@@ -0,0 +1,24 @@
1
+ module HasCacheKey
2
+ class CacheKey
3
+ attr_reader :name, :keys
4
+
5
+ # *keys, options
6
+ def initialize(*args)
7
+ options = args.extract_options!
8
+ keys = args
9
+ @keys = args.map { |k| k.is_a?(Symbol) ? k : k.to_sym }.freeze
10
+ @format = options[:format]
11
+ if options[:name]
12
+ @name = options[:name]
13
+ @name = @name.to_sym unless @name.is_a?(Symbol)
14
+ end
15
+ end
16
+
17
+ def format(values = nil)
18
+ return @format if !values
19
+ values = values.symbolize_keys
20
+ fmt = @format
21
+ fmt.is_a?(Proc) ? fmt.call(values) : (fmt % values)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,87 @@
1
+ require 'has_cache_key/possible_values'
2
+ require 'has_cache_key/cache_key'
3
+
4
+ module HasCacheKey
5
+ module ModelExt
6
+ def self.included(base)
7
+ base.extend ClassMethods
8
+ base.after_save :expire_cache_keys
9
+ base.after_destroy :expire_cache_keys
10
+ end
11
+
12
+ def expire_cache_keys
13
+ # First, assemble all keys the listing is involved in, and the values that were affected
14
+ affected_values_by_key = {}
15
+ attr_changes = self.changes
16
+ keys = self.class.cache_keys.map(&:keys)
17
+ keys.flatten!
18
+ keys.uniq!
19
+
20
+ keys.each do |key|
21
+ affected_values_by_key[key] = if attr_changes.has_key?(key)
22
+ # For :updated_at only expire the first value , because the second one never exists
23
+ if key == :updated_at
24
+ [attr_changes[key][0]]
25
+ else
26
+ attr_changes[key]
27
+ end
28
+ else
29
+ [send(key)]
30
+ end
31
+ end
32
+
33
+ self.class.cache_keys.each do |cache_key|
34
+ PossibleValues.new(affected_values_by_key.slice(*cache_key.keys)).each do |interpolations|
35
+ expire_fragment_key(cache_key.format(interpolations))
36
+ end
37
+ end
38
+ end
39
+
40
+ # @returns cache key string for the cache key with the given name
41
+ def cache_key(name = self.class.name.to_s.demodulize.underscore)
42
+ self.class.cache_key(name, attributes)
43
+ end
44
+
45
+ protected
46
+ def expire_fragment_key(key)
47
+ (@controller ||= ActionController::Base.new).expire_fragment(key)
48
+ end
49
+
50
+ module ClassMethods
51
+ def cache_keys
52
+ @cache_keys ||= []
53
+ end
54
+
55
+ def cache_key(name, format_values = nil)
56
+ (@cache_key_by_name ||= {}.with_indifferent_access)[name].format(format_values)
57
+ end
58
+
59
+
60
+ # Adds a cache key association to your model
61
+ # @param [Array|Symbol] composed_of keys that make up the cache key,
62
+ # can be also passed as @option composed_of
63
+ #
64
+ # @option [Symbol] name unique name which is used to refer to the cache key from your application
65
+ # defaults to: name.to_s.demodulize.underscore
66
+ #
67
+ # @example Expire on any update:
68
+ # has_cache_key :product_snippet, composed_of: [:updated_at]
69
+ def has_cache_key(*args)
70
+ if (options = args.extract_options!)
71
+ options[:composed_of] ||= args.flatten
72
+ else
73
+ options = {}
74
+ end
75
+ options[:composed_of] = Array(options[:composed_of])
76
+ options[:name] ||= name.to_s.demodulize.underscore
77
+ options[:format] ||= "#{options[:name]}-#{options[:composed_of].map { |k| "%{#{k}}" }.join('-')}"
78
+ cache_key = HasCacheKey::CacheKey.new(*options[:composed_of], options)
79
+ (@cache_keys ||= []) << cache_key
80
+ (@cache_key_by_name ||= {}.with_indifferent_access)[cache_key.name] = cache_key
81
+ end
82
+ end
83
+ end
84
+ end
85
+
86
+ require 'active_record'
87
+ ActiveRecord::Base.class_eval { include HasCacheKey::ModelExt }
@@ -0,0 +1,45 @@
1
+ module HasCacheKey
2
+ class PossibleValues
3
+ attr_reader :data
4
+ include Enumerable
5
+
6
+ # Yields with {slot_name => possible value} for all possible values
7
+ # It yields exactly this many times:
8
+ # PRODUCT{i in 0..COUNT(slot))}
9
+ # COUNT(VALUES(slot)))
10
+ def each(&block)
11
+ if @results
12
+ @results.each &block
13
+ return self
14
+ end
15
+ @results = []
16
+ cur_v_i = Array.new(@data.keys.length, 0)
17
+ keys = @data.keys.sort_by(&:to_s)
18
+ result = {}
19
+ keys.each do |key|
20
+ result[key] = @data[key][0]
21
+ end
22
+ while true
23
+ r = result.dup
24
+ @results << r
25
+ block.call(r) if block
26
+ keys.each_with_index do |k, k_i|
27
+ v_i = ((cur_v_i[k_i] = (1 + cur_v_i[k_i]) % @data[k].length))
28
+ result[k] = @data[k][v_i]
29
+ if v_i > 0 # Found next ?
30
+ break
31
+ elsif k_i == keys.length - 1 # Last?
32
+ return
33
+ end
34
+ end
35
+ end
36
+ @results
37
+ end
38
+
39
+ # Iterates over possible all possible values of keys
40
+ # data hash of {slot_name => [possible values...]}
41
+ def initialize(data)
42
+ @data = data
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,3 @@
1
+ module HasCacheKey
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,2 @@
1
+ require 'has_cache_key/version'
2
+ require 'has_cache_key/model_ext'
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: has_cache_key
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Gleb Mazovetskiy
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-02-18 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rails
16
+ requirement: &15820680 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '3.2'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *15820680
25
+ description: Allows you to define multiple cache keys on the module, and automatically
26
+ expires them.
27
+ email: glex.spb@gmail.com
28
+ executables: []
29
+ extensions: []
30
+ extra_rdoc_files: []
31
+ files:
32
+ - lib/has_cache_key/possible_values.rb
33
+ - lib/has_cache_key/model_ext.rb
34
+ - lib/has_cache_key/cache_key.rb
35
+ - lib/has_cache_key/version.rb
36
+ - lib/has_cache_key.rb
37
+ - MIT-LICENSE
38
+ - Rakefile
39
+ - Gemfile
40
+ - README.md
41
+ homepage:
42
+ licenses: []
43
+ post_install_message:
44
+ rdoc_options: []
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ none: false
49
+ requirements:
50
+ - - ! '>='
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ! '>='
57
+ - !ruby/object:Gem::Version
58
+ version: '0'
59
+ requirements: []
60
+ rubyforge_project:
61
+ rubygems_version: 1.8.11
62
+ signing_key:
63
+ specification_version: 3
64
+ summary: Automatic Cache Key Management for Rails Models. v0.0.1
65
+ test_files: []