staugaard-magic_cache_keys 0.1.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/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :patch: 0
3
+ :major: 0
4
+ :minor: 1
@@ -0,0 +1,58 @@
1
+ ActiveRecord::Base.valid_keys_for_has_many_association << :cache_key
2
+
3
+ module ActiveRecord
4
+ module CacheKeyCaching
5
+ module AssociationCollectionExtension
6
+ def cache_key
7
+ options = {:conditions => sanitize_sql({@reflection.primary_key_name => @owner.id})}
8
+ options[:conditions] << " AND (#{conditions})" if conditions
9
+
10
+ if @reflection.options[:order]
11
+ options[:order] = @reflection.options[:order]
12
+ end
13
+ construct_find_options!(options)
14
+ merge_options_from_reflection!(options)
15
+
16
+ key = @reflection.klass.cache_key(options)
17
+
18
+ new_records_count = @target.select { |r| r.new_record? }.size
19
+
20
+ if new_records_count > 0
21
+ key + "/#{new_records_count}new"
22
+ else
23
+ key.dup
24
+ end
25
+ end
26
+ end
27
+
28
+ end
29
+
30
+ class Base
31
+ def cache_key_with_associations(*include_associations)
32
+ parts = [cache_key_without_associations]
33
+ include_associations.sort! {|a1, a2| a1.to_s <=> a2.to_s}
34
+ parts += include_associations.map {|a| self.send(a).cache_key }
35
+ parts.join('/')
36
+ end
37
+ alias_method_chain :cache_key, :associations
38
+
39
+ class << self
40
+ def cache_key(options = {})
41
+ order = options.delete(:order) || scope(:find, :order)
42
+ opts = {:select => "MD5(CONCAT(GROUP_CONCAT(CONV(id,10,36)#{ ' ORDER BY ' + order unless order.blank?}), MAX(updated_at))) as cached_key"}.reverse_merge(options)
43
+
44
+ connection.execute('SET group_concat_max_len = 1048576')
45
+ "#{model_name.cache_key}/#{connection.select_value(construct_finder_sql(opts)) || 'empty'}"
46
+ end
47
+
48
+ def has_many_with_cache_key(association_id, options = {}, &extension)
49
+ options[:extend] ||= []
50
+ options[:extend] = [*options[:extend]]
51
+ options[:extend] << ActiveRecord::CacheKeyCaching::AssociationCollectionExtension
52
+
53
+ has_many_without_cache_key(association_id, options, &extension)
54
+ end
55
+ alias_method_chain :has_many, :cache_key
56
+ end
57
+ end
58
+ end
data/test/database.yml ADDED
@@ -0,0 +1,7 @@
1
+ test:
2
+ adapter: mysql
3
+ encoding: utf8
4
+ database: magic_cache_keys_test
5
+ username: root
6
+ password:
7
+ socket: /tmp/mysql.sock
@@ -0,0 +1,2 @@
1
+ a_blog:
2
+ name: My Blog
@@ -0,0 +1,14 @@
1
+ few_comments_1:
2
+ post: has_two_comments
3
+ body: what ever body 1
4
+
5
+ few_comments_2:
6
+ post: has_two_comments
7
+ body: what ever body 2
8
+
9
+ <% (1..10000).each do |i| %>
10
+ many_comments_<%= i %>:
11
+ post: has_many_comments
12
+ body: what ever body <%= i %>
13
+ <% end %>
14
+
@@ -0,0 +1,11 @@
1
+ no_comments:
2
+ blog: a_blog
3
+ title: This post has no comments
4
+
5
+ has_two_comments:
6
+ blog: a_blog
7
+ title: This post has a few comments
8
+
9
+ has_many_comments:
10
+ blog: a_blog
11
+ title: A shit load of comments on this one
@@ -0,0 +1,115 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class Comment < ActiveRecord::Base
4
+ belongs_to :post
5
+ end
6
+
7
+ class Post < ActiveRecord::Base
8
+ belongs_to :blog
9
+ has_many :comments, :cache_key => true
10
+ named_scope :orderd1, {:order => 'title DESC'}
11
+ named_scope :orderd2, {:order => 'title ASC'}
12
+ end
13
+
14
+ class Blog < ActiveRecord::Base
15
+ has_many :posts
16
+ has_many :posts_orderd1, :class_name => 'Post', :order => 'title DESC'
17
+ has_many :posts_orderd2, :class_name => 'Post', :order => 'title ASC'
18
+ has_many :posts_conditioned, :class_name => 'Post', :conditions => {:title => 'This post has a few comments'}
19
+ end
20
+
21
+ class MagicCacheKeysTest < ActiveSupport::TestCase
22
+ fixtures :blogs, :posts, :comments
23
+
24
+ test "Base class generates a collection cache key" do
25
+ assert_not_nil(Comment.cache_key)
26
+ assert_not_nil(Post.cache_key)
27
+ end
28
+
29
+ test "Base class generates a new cache keys when you add an item" do
30
+ key1 = Comment.cache_key.to_s
31
+ Comment.create(:post_id => posts(:has_many_comments).id, :body => 'yay another comment')
32
+ key2 = Comment.cache_key.to_s
33
+
34
+ assert_not_equal(key1, key2)
35
+ end
36
+
37
+ test "Base class generates a new cache keys when you remove an item" do
38
+ key1 = Comment.cache_key.to_s
39
+ assert_equal(Comment.delete_all(:body => 'what ever body 5000'), 1)
40
+ key2 = Comment.cache_key.to_s
41
+
42
+ assert_not_equal(key1, key2)
43
+ end
44
+
45
+ test "has_many associations generates a cache key" do
46
+ assert_not_nil(blogs(:a_blog).posts.cache_key)
47
+ end
48
+
49
+ test "generates different cache keys on different ordering" do
50
+ key1 = Comment.cache_key(:order => 'id DESC')
51
+ key2 = Comment.cache_key(:order => 'id ASC')
52
+
53
+ assert_not_equal(key1, key2)
54
+
55
+ key1 = blogs(:a_blog).posts_orderd1.cache_key
56
+ key2 = blogs(:a_blog).posts_orderd2.cache_key
57
+
58
+ assert_not_equal(key1, key2)
59
+ end
60
+
61
+ test "generated different cache key when conditioned" do
62
+ key1 = Comment.cache_key
63
+ key2 = Comment.cache_key(:conditions => {:body => comments(:few_comments_1).body})
64
+
65
+ assert_not_equal(key1, key2)
66
+
67
+ key1 = blogs(:a_blog).posts.cache_key
68
+ key2 = blogs(:a_blog).posts_conditioned.cache_key
69
+
70
+ assert_not_equal(key1, key2)
71
+ end
72
+
73
+ test "generated the correct keys for names scopes" do
74
+ key1 = Post.orderd1.cache_key
75
+ key2 = Post.orderd2.cache_key
76
+
77
+ assert_not_equal(key1, key2)
78
+
79
+ key1 = Post.orderd1.cache_key
80
+ key2 = Post.cache_key(:order => 'title DESC')
81
+
82
+ assert_equal(key1, key2)
83
+
84
+ key1 = Post.orderd2.cache_key
85
+ key2 = Post.cache_key(:order => 'title ASC')
86
+
87
+ assert_equal(key1, key2)
88
+ end
89
+
90
+ test "includes info about new records" do
91
+ key1 = blogs(:a_blog).posts.cache_key
92
+ blogs(:a_blog).posts.build(:title => 'new blog post')
93
+ key2 = blogs(:a_blog).posts.cache_key
94
+
95
+ assert_not_equal(key1, key2)
96
+ end
97
+
98
+ test "includes association cache key when asked to" do
99
+ key1 = blogs(:a_blog).cache_key
100
+ key2 = blogs(:a_blog).cache_key(:posts)
101
+ assert_not_equal(key1, key2)
102
+ end
103
+
104
+ test "includes multiple association cache keys when asked to" do
105
+ key1 = blogs(:a_blog).cache_key(:posts)
106
+ key2 = blogs(:a_blog).cache_key(:posts, :posts_orderd1)
107
+ assert_not_equal(key1, key2)
108
+ end
109
+
110
+ test "the order of the association cache keys should not matter" do
111
+ key1 = blogs(:a_blog).cache_key(:posts_orderd1, :posts)
112
+ key2 = blogs(:a_blog).cache_key(:posts, :posts_orderd1)
113
+ assert_equal(key1, key2)
114
+ end
115
+ end
data/test/schema.rb ADDED
@@ -0,0 +1,35 @@
1
+ # This file is auto-generated from the current state of the database. Instead of editing this file,
2
+ # please use the migrations feature of Active Record to incrementally modify your database, and
3
+ # then regenerate this schema definition.
4
+ #
5
+ # Note that this schema.rb definition is the authoritative source for your database schema. If you need
6
+ # to create the application database on another system, you should be using db:schema:load, not running
7
+ # all the migrations from scratch. The latter is a flawed and unsustainable approach (the more migrations
8
+ # you'll amass, the slower it'll run and the greater likelihood for issues).
9
+ #
10
+ # It's strongly recommended to check this file into your version control system.
11
+
12
+ ActiveRecord::Schema.define(:version => 1) do
13
+
14
+ create_table "comments", :force => true do |t|
15
+ t.text "body"
16
+ t.integer "post_id"
17
+ t.datetime "created_at"
18
+ t.datetime "updated_at"
19
+ end
20
+
21
+ create_table "posts", :force => true do |t|
22
+ t.string "title"
23
+ t.integer "blog_id"
24
+ t.string "comments_cache_key"
25
+ t.datetime "created_at"
26
+ t.datetime "updated_at"
27
+ end
28
+
29
+ create_table "blogs", :force => true do |t|
30
+ t.string "name"
31
+ t.datetime "created_at"
32
+ t.datetime "updated_at"
33
+ end
34
+
35
+ end
@@ -0,0 +1,34 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'active_record'
4
+ require 'active_record/fixtures'
5
+
6
+ ActiveRecord::Base.configurations = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
7
+ ActiveRecord::Base.establish_connection('test')
8
+ ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log")
9
+
10
+ load(File.dirname(__FILE__) + "/schema.rb")
11
+
12
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
13
+ require 'magic_cache_keys'
14
+
15
+ Test::Unit::TestCase.fixture_path = File.dirname(__FILE__) + "/fixtures/"
16
+ $LOAD_PATH.unshift(Test::Unit::TestCase.fixture_path)
17
+
18
+ class Test::Unit::TestCase #:nodoc:
19
+ def create_fixtures(*table_names)
20
+ if block_given?
21
+ Fixtures.create_fixtures(Test::Unit::TestCase.fixture_path, table_names) { yield }
22
+ else
23
+ Fixtures.create_fixtures(Test::Unit::TestCase.fixture_path, table_names)
24
+ end
25
+ end
26
+
27
+ # Turn off transactional fixtures if you're working with MyISAM tables in MySQL
28
+ self.use_transactional_fixtures = true
29
+
30
+ # Instantiated fixtures are slow, but give you @david where you otherwise would need people(:david)
31
+ self.use_instantiated_fixtures = false
32
+
33
+ # Add more helper methods to be used by all tests here...
34
+ end
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: staugaard-magic_cache_keys
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Mick Staugaard
8
+ - Morten Primdahl
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2009-02-13 00:00:00 -08:00
14
+ default_executable:
15
+ dependencies: []
16
+
17
+ description: An extension of ActiveRecord adding database side generated cache keys for collections
18
+ email: mick@staugaard.com
19
+ executables: []
20
+
21
+ extensions: []
22
+
23
+ extra_rdoc_files: []
24
+
25
+ files:
26
+ - VERSION.yml
27
+ - lib/magic_cache_keys.rb
28
+ - test/database.yml
29
+ - test/debug.log
30
+ - test/fixtures
31
+ - test/fixtures/blogs.yml
32
+ - test/fixtures/comments.yml
33
+ - test/fixtures/posts.yml
34
+ - test/magic_cache_keys_test.rb
35
+ - test/schema.rb
36
+ - test/test_helper.rb
37
+ has_rdoc: true
38
+ homepage: http://github.com/staugaard/magic_cache_keys
39
+ post_install_message:
40
+ rdoc_options:
41
+ - --inline-source
42
+ - --charset=UTF-8
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: "0"
50
+ version:
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: "0"
56
+ version:
57
+ requirements: []
58
+
59
+ rubyforge_project:
60
+ rubygems_version: 1.2.0
61
+ signing_key:
62
+ specification_version: 2
63
+ summary: An extension of ActiveRecord adding database side generated cache keys for collections
64
+ test_files: []
65
+