kasket 3.1.5 → 3.2.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.
@@ -1,49 +0,0 @@
1
- require File.expand_path("helper", File.dirname(__FILE__))
2
-
3
- class CacheExpiryTest < ActiveSupport::TestCase
4
- fixtures :blogs, :posts
5
-
6
- context "a cached object" do
7
- setup do
8
- post = Post.first
9
- @post = Post.find(post.id)
10
-
11
- assert(Kasket.cache.read(@post.kasket_key))
12
- end
13
-
14
- should "be removed from cache when deleted" do
15
- @post.destroy
16
- assert_nil(Kasket.cache.read(@post.kasket_key))
17
- end
18
-
19
- should "clear all indices for instance when deleted" do
20
- Kasket.cache.expects(:delete).with(Post.kasket_key_prefix + "id=#{@post.id}")
21
- Kasket.cache.expects(:delete).with(Post.kasket_key_prefix + "title='#{@post.title}'")
22
- Kasket.cache.expects(:delete).with(Post.kasket_key_prefix + "title='#{@post.title}'/first")
23
- Kasket.cache.expects(:delete).with(Post.kasket_key_prefix + "blog_id=#{@post.blog_id}/id=#{@post.id}")
24
- Kasket.cache.expects(:delete).never
25
-
26
- @post.destroy
27
- end
28
-
29
- should "be removed from cache when updated" do
30
- @post.title = "new_title"
31
- @post.save
32
- assert_nil(Kasket.cache.read(@post.kasket_key))
33
- end
34
-
35
- should "clear all indices for instance when updated" do
36
- Kasket.cache.expects(:delete).with(Post.kasket_key_prefix + "id=#{@post.id}")
37
- Kasket.cache.expects(:delete).with(Post.kasket_key_prefix + "title='#{@post.title}'")
38
- Kasket.cache.expects(:delete).with(Post.kasket_key_prefix + "title='#{@post.title}'/first")
39
- Kasket.cache.expects(:delete).with(Post.kasket_key_prefix + "title='new_title'")
40
- Kasket.cache.expects(:delete).with(Post.kasket_key_prefix + "title='new_title'/first")
41
- Kasket.cache.expects(:delete).with(Post.kasket_key_prefix + "blog_id=#{@post.blog_id}/id=#{@post.id}")
42
- Kasket.cache.expects(:delete).never
43
-
44
- @post.title = "new_title"
45
- @post.save
46
- end
47
-
48
- end
49
- end
@@ -1,58 +0,0 @@
1
- require File.expand_path("helper", File.dirname(__FILE__))
2
-
3
- class CacheableTest < ActiveSupport::TestCase
4
- context "#store_in_kasket" do
5
- should "only cache object that are kasket_cacheable?" do
6
- post = Post.send(:instantiate, { 'id' => 1, 'title' => 'Hello' })
7
-
8
- post.expects(:kasket_cacheable?).returns(true)
9
- Kasket.cache.expects(:write).once
10
- post.store_in_kasket
11
-
12
- post.expects(:kasket_cacheable?).returns(false)
13
- Kasket.cache.expects(:write).never
14
- post.store_in_kasket
15
- end
16
- end
17
-
18
- context "caching of results of find" do
19
- setup do
20
- @post_database_result = { 'id' => 1, 'title' => 'Hello' }
21
- @post_records = [Post.send(:instantiate, @post_database_result)]
22
- Post.stubs(:find_by_sql_without_kasket).returns(@post_records)
23
-
24
- @comment_database_result = [{ 'id' => 1, 'body' => 'Hello' }, { 'id' => 2, 'body' => 'World' }]
25
- @comment_records = @comment_database_result.map {|r| Comment.send(:instantiate, r)}
26
- Comment.stubs(:find_by_sql_without_kasket).returns(@comment_records)
27
- end
28
-
29
- context "with just one result" do
30
- should "write result in cache if it is kasket_cacheable?" do
31
- Post.any_instance.expects(:kasket_cacheable?).returns(true)
32
- Kasket.cache.expects(:write).once
33
- Post.find(1)
34
- end
35
-
36
- should "not write result in cache if it is not kasket_cacheable?" do
37
- Post.any_instance.expects(:kasket_cacheable?).returns(false)
38
- Kasket.cache.expects(:write).never
39
- Post.find(1)
40
- end
41
- end
42
-
43
- context "with several results" do
44
- should "write result in cache if all results are kasket_cacheable?" do
45
- Comment.any_instance.stubs(:kasket_cacheable?).returns(true)
46
- Kasket.cache.expects(:write).times(@comment_records.size + 1)
47
- Comment.all(:conditions => {:post_id => 1})
48
- end
49
-
50
- should "not write result in cache if any of them are not kasket_cacheable?" do
51
- @comment_records[0].expects(:kasket_cacheable?).returns(true)
52
- @comment_records[1].expects(:kasket_cacheable?).returns(false)
53
- Kasket.cache.expects(:write).never
54
- Comment.all(:conditions => {:post_id => 1})
55
- end
56
- end
57
- end
58
- end
@@ -1,44 +0,0 @@
1
- require File.expand_path("helper", File.dirname(__FILE__))
2
- require "digest/md5"
3
-
4
- class ConfigurationMixinTest < ActiveSupport::TestCase
5
- context "Generating cache keys" do
6
- should "not choke on empty numeric attributes" do
7
- expected_cache_key = "#{Post.kasket_key_prefix}blog_id=null"
8
- query_attributes = [ [:blog_id, ''] ]
9
-
10
- assert_equal expected_cache_key, Post.kasket_key_for(query_attributes)
11
- end
12
-
13
- should "not fail on unknown columns" do
14
- expected_cache_key = "#{Post.kasket_key_prefix}does_not_exist=111"
15
- query_attributes = [ [:does_not_exist, '111'] ]
16
-
17
- assert_equal expected_cache_key, Post.kasket_key_for(query_attributes)
18
- end
19
-
20
- should "not generate keys longer that 255" do
21
- very_large_number = (1..999).to_a.join
22
- query_attributes = [ [:blog_id, very_large_number] ]
23
-
24
- assert(Post.kasket_key_for(query_attributes).size < 255)
25
- end
26
-
27
- should "not generate keys with spaces" do
28
- query_attributes = [ [:title, 'this key has speces'] ]
29
-
30
- assert(!(Post.kasket_key_for(query_attributes) =~ /\s/))
31
- end
32
-
33
- should "downcase string attributes" do
34
- query_attributes = [ [:title, 'ThIs'] ]
35
- expected_cache_key = "#{Post.kasket_key_prefix}title='this'"
36
-
37
- assert_equal expected_cache_key, Post.kasket_key_for(query_attributes)
38
- end
39
-
40
- should "build correct prefix" do
41
- assert_equal "kasket-#{Kasket::Version::PROTOCOL}/R#{ActiveRecord::VERSION::MAJOR}#{ActiveRecord::VERSION::MINOR}/posts/version=#{POST_VERSION}/", Post.kasket_key_prefix
42
- end
43
- end
44
- end
@@ -1,7 +0,0 @@
1
- test:
2
- adapter: mysql
3
- encoding: utf8
4
- database: kasket_test
5
- username: root
6
- password:
7
- host: 127.0.0.1
@@ -1,16 +0,0 @@
1
- require File.expand_path("helper", File.dirname(__FILE__))
2
-
3
- class DirtyTest < ActiveSupport::TestCase
4
- fixtures :blogs, :posts
5
-
6
- should "clear the indices when a dirty method is called" do
7
- post = Post.first
8
-
9
- Post.cache { pots = Post.find(post.id) }
10
- assert(Kasket.cache.read(post.kasket_key))
11
-
12
- post.make_dirty!
13
-
14
- assert_nil(Kasket.cache.read(post.kasket_key))
15
- end
16
- end
@@ -1,61 +0,0 @@
1
- require File.expand_path("helper", File.dirname(__FILE__))
2
-
3
- class FindOneTest < ActiveSupport::TestCase
4
- fixtures :blogs, :posts
5
-
6
- should "cache find(id) calls" do
7
- post = Post.first
8
- Kasket.cache.write(post.kasket_key, nil)
9
- assert_equal(post, Post.find(post.id))
10
- assert(Kasket.cache.read(post.kasket_key))
11
- Post.connection.expects(:select_all).never
12
- assert_equal(post, Post.find(post.id))
13
- end
14
-
15
- should "only cache on indexed attributes" do
16
- Kasket.cache.expects(:read).twice
17
- Post.find_by_id(1)
18
- Post.find_by_id(1, :conditions => {:blog_id => 2})
19
-
20
- Kasket.cache.expects(:read).never
21
- Post.first :conditions => {:blog_id => 2}
22
- end
23
-
24
- should "not use cache when using the :select option" do
25
- post = Post.first
26
- assert_nil(Kasket.cache.read(post.kasket_key))
27
-
28
- Post.find(post.id, :select => 'title')
29
- assert_nil(Kasket.cache.read(post.kasket_key))
30
-
31
- Post.find(post.id)
32
- assert(Kasket.cache.read(post.kasket_key))
33
-
34
- Kasket.cache.expects(:read)
35
- Post.find(post.id, :select => nil)
36
-
37
- Kasket.cache.expects(:read).never
38
- Post.find(post.id, :select => 'title')
39
- end
40
-
41
- should "respect scope" do
42
- post = Post.find(Post.first.id)
43
- other_blog = Blog.first(:conditions => "id != #{post.blog_id}")
44
-
45
- assert(Kasket.cache.read(post.kasket_key))
46
-
47
- assert_raise(ActiveRecord::RecordNotFound) do
48
- other_blog.posts.find(post.id)
49
- end
50
- end
51
-
52
- should "use same scope when finding on has_many" do
53
- post = Blog.first.posts.first
54
- blog = Blog.first
55
- Rails.cache.clear
56
-
57
- post = blog.posts.find_by_id(post.id)
58
- key = post.kasket_key.sub(%r{(/id=#{post.id})}, "/blog_id=#{Blog.first.id}\\1")
59
- assert(Kasket.cache.read(key))
60
- end
61
- end
@@ -1,55 +0,0 @@
1
- require File.expand_path("helper", File.dirname(__FILE__))
2
-
3
- class FindSomeTest < ActiveSupport::TestCase
4
- fixtures :blogs, :posts
5
-
6
- def setup
7
- @post1 = Post.first
8
- @post2 = Post.last
9
- Post.find(@post1.id, @post2.id)
10
- assert Kasket.cache.read(@post1.kasket_key)
11
- assert Kasket.cache.read(@post2.kasket_key)
12
- end
13
-
14
- should "use cache for find(id, id) calls" do
15
- Post.connection.expects(:select_all).never
16
- Post.find(@post1.id, @post2.id)
17
- end
18
-
19
- should "cache when found using find(id, id) calls" do
20
- Kasket.cache.delete(@post1.kasket_key)
21
- Kasket.cache.delete(@post2.kasket_key)
22
-
23
- Post.find(@post1.id, @post2.id)
24
-
25
- assert Kasket.cache.read(@post1.kasket_key)
26
- assert Kasket.cache.read(@post2.kasket_key)
27
- end
28
-
29
- should "only lookup the records that are not in the cache" do
30
- Kasket.cache.delete(@post2.kasket_key)
31
-
32
- # has to lookup post2 via db
33
- Post.expects(:find_by_sql_without_kasket).returns([@post2])
34
- found_posts = Post.find(@post1.id, @post2.id)
35
- assert_equal [@post1, @post2].map(&:id).sort, found_posts.map(&:id).sort
36
-
37
- # now all are cached
38
- Post.expects(:find_by_sql_without_kasket).never
39
- found_posts = Post.find(@post1.id, @post2.id)
40
- assert_equal [@post1, @post2].map(&:id).sort, found_posts.map(&:id).sort
41
- end
42
-
43
- context "unfound" do
44
- should "ignore unfound when using find_all_by_id" do
45
- found_posts = Post.find_all_by_id([@post1.id, 1231232])
46
- assert_equal [@post1.id], found_posts.map(&:id)
47
- end
48
-
49
- should "not ignore unfound when using find" do
50
- assert_raise ActiveRecord::RecordNotFound do
51
- Post.find(@post1.id, 1231232)
52
- end
53
- end
54
- end
55
- end
@@ -1,7 +0,0 @@
1
- mick:
2
- name: Mick Staugaard
3
- metadata:
4
- sex: male
5
-
6
- eric:
7
- name: Eric Chapweske
@@ -1,5 +0,0 @@
1
- a_blog:
2
- name: My Blog
3
-
4
- other_blog:
5
- name: Some Other Blog
@@ -1,16 +0,0 @@
1
- few_comments_1:
2
- id: 1
3
- post_id: 2
4
- body: what ever body 1
5
-
6
- few_comments_2:
7
- id: 2
8
- post_id: 2
9
- body: what ever body 2
10
-
11
- <% (1..10).each do |i| %>
12
- many_comments_<%= i %>:
13
- post: has_many_comments
14
- body: what ever body <%= i %>
15
- <% end %>
16
-
@@ -1,24 +0,0 @@
1
- no_comments:
2
- id: 1
3
- blog: a_blog
4
- title: no_comments
5
- author: mick
6
- created_at: 2013-10-14 15:30:00
7
-
8
- has_two_comments:
9
- id: 2
10
- blog: a_blog
11
- title: few_comments
12
- author: mick
13
-
14
- on_other_blog:
15
- id: 3
16
- blog: other_blog
17
- title: no_comments
18
- author: eric
19
-
20
- has_many_comments:
21
- id: 4
22
- blog: a_blog
23
- title: many_comments
24
- author: eric
@@ -1,84 +0,0 @@
1
- require 'rubygems'
2
-
3
- require 'bundler'
4
- Bundler.setup
5
- # shoulda-matchers dependency:
6
- require 'active_support/core_ext/module/delegation'
7
- Bundler.require(:default, :development)
8
-
9
- if defined?(Debugger)
10
- ::Debugger.start
11
- ::Debugger.settings[:autoeval] = true if ::Debugger.respond_to?(:settings)
12
- end
13
-
14
- require 'test/unit'
15
- require 'mocha'
16
- require 'active_record'
17
- require "logger"
18
-
19
- raise "Must configure #time_zone_aware_attributes prior to models" if defined?(Post)
20
- ENV['TZ'] = 'utc'
21
- ActiveRecord::Base.time_zone_aware_attributes = true
22
- ActiveRecord::Base.logger = Logger.new(StringIO.new)
23
-
24
- require 'active_record/fixtures'
25
-
26
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
27
- $LOAD_PATH.unshift(File.dirname(__FILE__))
28
- require 'kasket'
29
-
30
- Kasket.setup
31
-
32
- class ActiveSupport::TestCase
33
- include ActiveRecord::TestFixtures
34
-
35
- def create_fixtures(*table_names)
36
- if block_given?
37
- Fixtures.create_fixtures(Test::Unit::TestCase.fixture_path, table_names) { yield }
38
- else
39
- Fixtures.create_fixtures(Test::Unit::TestCase.fixture_path, table_names)
40
- end
41
- end
42
-
43
- self.use_transactional_fixtures = true
44
-
45
- self.use_instantiated_fixtures = false
46
-
47
- setup :clear_cache
48
- def clear_cache
49
- Kasket.cache.clear
50
- end
51
-
52
- def arel?
53
- self.class.arel?
54
- end
55
-
56
- def self.arel?
57
- ActiveRecord::VERSION::MAJOR >= 3 && ActiveRecord::VERSION::MINOR >= 1
58
- end
59
- end
60
-
61
- ActiveSupport::TestCase.fixture_path = File.dirname(__FILE__) + "/fixtures/"
62
- $LOAD_PATH.unshift(ActiveSupport::TestCase.fixture_path)
63
-
64
- class ActiveSupport::TestCase
65
- fixtures :all
66
- end
67
-
68
- module Rails
69
- module_function
70
- CACHE = ActiveSupport::Cache::MemoryStore.new
71
- LOGGER = Logger.new(STDOUT)
72
-
73
- def cache
74
- CACHE
75
- end
76
-
77
- def logger
78
- LOGGER
79
- end
80
- end
81
-
82
- require 'test_models'
83
- POST_VERSION = Post.column_names.join.sum
84
- COMMENT_VERSION = Comment.column_names.join.sum
@@ -1,153 +0,0 @@
1
- require File.expand_path("helper", File.dirname(__FILE__))
2
- require 'kasket/query_parser'
3
-
4
- class ParserTest < ActiveSupport::TestCase
5
- def parse(options)
6
- scope = Post
7
- if arel?
8
- options.each do |k,v|
9
- scope = case k
10
- when :conditions then scope.where(v)
11
- else
12
- scope.send(k, v)
13
- end
14
- end
15
- scope.to_kasket_query
16
- elsif ActiveRecord::VERSION::MAJOR == 3 && ActiveRecord::VERSION::MINOR == 0
17
- @parser.parse(scope.scoped(options).to_sql)
18
- else
19
- sql = scope.send(:construct_finder_sql, options)
20
- @parser.parse(sql)
21
- end
22
- end
23
-
24
- context "Parsing" do
25
- setup do
26
- @parser = Kasket::QueryParser.new(Post)
27
- end
28
-
29
- should "not support conditions with number as column (e.g. 0 = 1)" do
30
- assert !parse(:conditions => "0 = 1")
31
- end
32
-
33
- should "not support conditions with number as column and parans (e.g. 0 = 1)" do
34
- assert !parse(:conditions => "(0 = 1)")
35
- end
36
-
37
- should "not support :order" do
38
- assert !parse(:conditions => "id = 1", :order => "xxx")
39
- end
40
-
41
- should 'not support IN queries in combination with other conditions' do
42
- assert !parse(:conditions => {:id => [1,2,3], :is_active => true})
43
- end
44
-
45
- should "extract conditions" do
46
- kasket_query = parse(:conditions => {:title => 'red', :blog_id => 1})
47
- assert_equal [[:blog_id, "1"], [:title, "red"]], kasket_query[:attributes]
48
- end
49
-
50
- should "extract conditions with parans that do not surround" do
51
- kasket_query = parse(:conditions => "(title = 'red') AND (blog_id = 1)")
52
- if ActiveRecord::VERSION::STRING > "3.1.0"
53
- assert !kasket_query
54
- else
55
- assert_equal [[:blog_id, "1"], [:title, "red"]], kasket_query[:attributes]
56
- end
57
- end
58
-
59
- should "extract required index" do
60
- assert_equal [:blog_id, :title], parse(:conditions => {:title => 'red', :blog_id => 1})[:index]
61
- end
62
-
63
- should "only support queries against its model's table" do
64
- assert !parse(:conditions => {'blogs.id' => 2}, :from => 'apples')
65
- end
66
-
67
- should "support cachable queries" do
68
- assert parse(:conditions => {:id => 1})
69
- assert parse(:conditions => {:id => 1}, :limit => 1)
70
- end
71
-
72
- should "support IN queries on id" do
73
- assert_equal [[:id, ['1', '2', '3']]], parse(:conditions => {:id => [1,2,3]})[:attributes]
74
- end
75
-
76
- should "not support IN queries on other attributes" do
77
- assert !parse(:conditions => {:hest => [1,2,3]})
78
- end
79
-
80
- should "support vaguely formatted queries" do
81
- assert @parser.parse('SELECT * FROM "posts" WHERE (title = red AND blog_id = big)')
82
- end
83
-
84
- context "extract options" do
85
- should "provide the limit" do
86
- assert_equal nil, parse(:conditions => {:id => 2})[:limit]
87
- assert_equal 1, parse(:conditions => {:id => 2}, :limit => 1)[:limit]
88
- end
89
- end
90
-
91
- context "unsupported queries" do
92
- should "include advanced limits" do
93
- assert !parse(:conditions => {:title => 'red', :blog_id => 1}, :limit => 2)
94
- end
95
-
96
- should "include joins" do
97
- assert !parse(:conditions => {:title => 'test', 'apple.tree_id' => 'posts.id'}, :from => ['posts', 'apple'])
98
- assert !parse(:conditions => {:title => 'test'}, :joins => :comments)
99
- end
100
-
101
- should "include specific selects" do
102
- assert !parse(:conditions => {:title => 'red'}, :select => :id)
103
- end
104
-
105
- should "include offset" do
106
- assert !parse(:conditions => {:title => 'red'}, :limit => 1, :offset => 2)
107
- end
108
-
109
- should "include order" do
110
- assert !parse(:conditions => {:title => 'red'}, :order => :title)
111
- end
112
-
113
- should "include the OR operator" do
114
- assert !parse(:conditions => "title = 'red' OR blog_id = 1")
115
- end
116
- end
117
-
118
- context "key generation" do
119
- should "include the table name and version" do
120
- kasket_query = parse(:conditions => {:id => 1})
121
- assert_match(/^kasket-#{Kasket::Version::PROTOCOL}\/R#{ActiveRecord::VERSION::MAJOR}#{ActiveRecord::VERSION::MINOR}\/posts\/version=#{POST_VERSION}\//, kasket_query[:key])
122
- end
123
-
124
- should "include all indexed attributes" do
125
- kasket_query = parse(:conditions => {:id => 1})
126
- assert_match(/id=1$/, kasket_query[:key])
127
-
128
- kasket_query = parse(:conditions => {:id => 1, :blog_id => 2})
129
- assert_match(/blog_id=2\/id=1$/, kasket_query[:key])
130
-
131
- kasket_query = parse(:conditions => {:id => 1, :title => 'title'})
132
- assert_match(/id=1\/title='title'$/, kasket_query[:key])
133
- end
134
-
135
- should "generate multiple keys on IN queries" do
136
- keys = parse(:conditions => {:id => [1,2]})[:key]
137
- assert_instance_of(Array, keys)
138
- assert_match(/id=1$/, keys[0])
139
- assert_match(/id=2$/, keys[1])
140
- end
141
-
142
- context "when limit 1" do
143
- should "add /first to the key if the index does not include id" do
144
- assert_match(/title='a'\/first$/, parse(:conditions => {:title => 'a'}, :limit => 1)[:key])
145
- end
146
-
147
- should "not add /first to the key when the index includes id" do
148
- assert_match(/id=1$/, parse(:conditions => {:id => 1}, :limit => 1)[:key])
149
- end
150
- end
151
- end
152
- end
153
- end