activerecord_cache 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +5 -0
  3. data/Gemfile.lock +92 -0
  4. data/Rakefile +31 -1
  5. data/activerecord_cache.gemspec +1 -1
  6. data/lib/activerecord_cache.rb +1 -0
  7. data/lib/activerecord_cache/base.rb +2 -2
  8. data/lib/activerecord_cache/finder_methods.rb +3 -26
  9. data/lib/activerecord_cache/railtie.rb +4 -1
  10. data/lib/activerecord_cache/relation.rb +4 -6
  11. data/lib/activerecord_cache/version.rb +1 -1
  12. data/test/activerecord_cache_test.rb +167 -0
  13. data/test/dummy/README.rdoc +261 -0
  14. data/test/dummy/Rakefile +7 -0
  15. data/test/dummy/app/assets/javascripts/application.js +15 -0
  16. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  17. data/test/dummy/app/controllers/application_controller.rb +3 -0
  18. data/test/dummy/app/helpers/application_helper.rb +2 -0
  19. data/test/dummy/app/mailers/.gitkeep +0 -0
  20. data/test/dummy/app/models/.gitkeep +0 -0
  21. data/test/dummy/app/models/associated_record.rb +3 -0
  22. data/test/dummy/app/models/cached_record.rb +3 -0
  23. data/test/dummy/app/models/non_primary_associated.rb +3 -0
  24. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  25. data/test/dummy/config.ru +4 -0
  26. data/test/dummy/config/application.rb +55 -0
  27. data/test/dummy/config/boot.rb +10 -0
  28. data/test/dummy/config/database.yml +25 -0
  29. data/test/dummy/config/environment.rb +5 -0
  30. data/test/dummy/config/environments/development.rb +39 -0
  31. data/test/dummy/config/environments/production.rb +70 -0
  32. data/test/dummy/config/environments/test.rb +33 -0
  33. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  34. data/test/dummy/config/initializers/inflections.rb +15 -0
  35. data/test/dummy/config/initializers/mime_types.rb +5 -0
  36. data/test/dummy/config/initializers/secret_token.rb +7 -0
  37. data/test/dummy/config/initializers/session_store.rb +8 -0
  38. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  39. data/test/dummy/config/locales/en.yml +5 -0
  40. data/test/dummy/config/routes.rb +58 -0
  41. data/test/dummy/db/development.sqlite3 +0 -0
  42. data/test/dummy/db/migrate/20140715044034_create_cached_records.rb +9 -0
  43. data/test/dummy/db/migrate/20140715052133_create_associated_records.rb +10 -0
  44. data/test/dummy/db/migrate/20140715053153_create_non_primary_associateds.rb +9 -0
  45. data/test/dummy/db/schema.rb +36 -0
  46. data/test/dummy/db/test.sqlite3 +0 -0
  47. data/test/dummy/lib/assets/.gitkeep +0 -0
  48. data/test/dummy/log/.gitkeep +0 -0
  49. data/test/dummy/public/404.html +26 -0
  50. data/test/dummy/public/422.html +26 -0
  51. data/test/dummy/public/500.html +25 -0
  52. data/test/dummy/public/favicon.ico +0 -0
  53. data/test/dummy/script/rails +6 -0
  54. data/test/dummy/test/fixtures/associated_records.yml +9 -0
  55. data/test/dummy/test/fixtures/cached_records.yml +7 -0
  56. data/test/dummy/test/fixtures/non_primary_associateds.yml +9 -0
  57. data/test/dummy/test/unit/associated_record_test.rb +7 -0
  58. data/test/dummy/test/unit/cached_record_test.rb +7 -0
  59. data/test/dummy/test/unit/non_primary_associated_test.rb +7 -0
  60. data/test/test_helper.rb +15 -0
  61. metadata +61 -27
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: cc8aa7320c445aea2920adb2282e87eaf140c084
4
+ data.tar.gz: ef5d398cf49741e94e3b1073ba619e0c810559b7
5
+ SHA512:
6
+ metadata.gz: b57793fdef8d3b740e866bab4595401201f211e881b91e198c77c809ed99770b39563a059805745d374da2f2a5552efd6b3ddcf9384ed0d0a981cc70095fc66a
7
+ data.tar.gz: ccb1921116861244af6285ffaa82106f28279d8e7ac328521d30bad004a031ef877fd7a96266ec399ea8e20d492ea97fe70092bb4e54b451c0e096afd8ebb85d
data/Gemfile CHANGED
@@ -2,3 +2,8 @@ source "http://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in activerecord_cache.gemspec
4
4
  gemspec
5
+
6
+ group :test do
7
+ gem 'sqlite3'
8
+ gem 'bourne'
9
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,92 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ activerecord_cache (0.0.7)
5
+ rails (~> 4.0.0)
6
+
7
+ GEM
8
+ remote: http://rubygems.org/
9
+ specs:
10
+ actionmailer (4.0.8)
11
+ actionpack (= 4.0.8)
12
+ mail (~> 2.5.4)
13
+ actionpack (4.0.8)
14
+ activesupport (= 4.0.8)
15
+ builder (~> 3.1.0)
16
+ erubis (~> 2.7.0)
17
+ rack (~> 1.5.2)
18
+ rack-test (~> 0.6.2)
19
+ activemodel (4.0.8)
20
+ activesupport (= 4.0.8)
21
+ builder (~> 3.1.0)
22
+ activerecord (4.0.8)
23
+ activemodel (= 4.0.8)
24
+ activerecord-deprecated_finders (~> 1.0.2)
25
+ activesupport (= 4.0.8)
26
+ arel (~> 4.0.0)
27
+ activerecord-deprecated_finders (1.0.3)
28
+ activesupport (4.0.8)
29
+ i18n (~> 0.6, >= 0.6.9)
30
+ minitest (~> 4.2)
31
+ multi_json (~> 1.3)
32
+ thread_safe (~> 0.1)
33
+ tzinfo (~> 0.3.37)
34
+ arel (4.0.2)
35
+ bourne (1.5.0)
36
+ mocha (>= 0.13.2, < 0.15)
37
+ builder (3.1.4)
38
+ erubis (2.7.0)
39
+ hike (1.2.3)
40
+ i18n (0.6.11)
41
+ mail (2.5.4)
42
+ mime-types (~> 1.16)
43
+ treetop (~> 1.4.8)
44
+ metaclass (0.0.4)
45
+ mime-types (1.25.1)
46
+ minitest (4.7.5)
47
+ mocha (0.14.0)
48
+ metaclass (~> 0.0.1)
49
+ multi_json (1.10.1)
50
+ polyglot (0.3.5)
51
+ rack (1.5.2)
52
+ rack-test (0.6.2)
53
+ rack (>= 1.0)
54
+ rails (4.0.8)
55
+ actionmailer (= 4.0.8)
56
+ actionpack (= 4.0.8)
57
+ activerecord (= 4.0.8)
58
+ activesupport (= 4.0.8)
59
+ bundler (>= 1.3.0, < 2.0)
60
+ railties (= 4.0.8)
61
+ sprockets-rails (~> 2.0)
62
+ railties (4.0.8)
63
+ actionpack (= 4.0.8)
64
+ activesupport (= 4.0.8)
65
+ rake (>= 0.8.7)
66
+ thor (>= 0.18.1, < 2.0)
67
+ rake (10.3.2)
68
+ sprockets (2.12.1)
69
+ hike (~> 1.2)
70
+ multi_json (~> 1.0)
71
+ rack (~> 1.0)
72
+ tilt (~> 1.1, != 1.3.0)
73
+ sprockets-rails (2.1.3)
74
+ actionpack (>= 3.0)
75
+ activesupport (>= 3.0)
76
+ sprockets (~> 2.8)
77
+ sqlite3 (1.3.9)
78
+ thor (0.19.1)
79
+ thread_safe (0.3.4)
80
+ tilt (1.4.1)
81
+ treetop (1.4.15)
82
+ polyglot
83
+ polyglot (>= 0.3.1)
84
+ tzinfo (0.3.40)
85
+
86
+ PLATFORMS
87
+ ruby
88
+
89
+ DEPENDENCIES
90
+ activerecord_cache!
91
+ bourne
92
+ sqlite3
data/Rakefile CHANGED
@@ -1,2 +1,32 @@
1
- require 'bundler'
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'TestPlugin'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+
18
+
19
+
2
20
  Bundler::GemHelper.install_tasks
21
+
22
+ require 'rake/testtask'
23
+
24
+ Rake::TestTask.new(:test) do |t|
25
+ t.libs << 'lib'
26
+ t.libs << 'test'
27
+ t.pattern = 'test/**/*_test.rb'
28
+ t.verbose = false
29
+ end
30
+
31
+
32
+ task default: :test
@@ -17,5 +17,5 @@ Gem::Specification.new do |s|
17
17
 
18
18
  s.licenses = ['MIT']
19
19
 
20
- s.add_dependency 'rails', ['>= 3.1.3', '<= 3.3.0']
20
+ s.add_dependency 'rails', ['~> 4.0.0']
21
21
  end
@@ -6,5 +6,6 @@ require File.join(File.dirname(__FILE__), 'activerecord_cache/finder_methods')
6
6
  require File.join(File.dirname(__FILE__), 'activerecord_cache/belongs_to_association')
7
7
  require File.join(File.dirname(__FILE__), 'activerecord_cache/belongs_to_preloader')
8
8
  require File.join(File.dirname(__FILE__), 'activerecord_cache/relation')
9
+ require File.join(File.dirname(__FILE__), 'activerecord_cache/errors')
9
10
 
10
11
  require 'activerecord_cache/railtie.rb' if defined?(Rails)
@@ -14,7 +14,7 @@ module ActiveRecordCache
14
14
  def cache_key(id_or_record)
15
15
  unless use_activerecord_cache
16
16
  message = "ActiveRecord cache is not enabled for #{self.name}"
17
- raise ActiveRecordCache::CachingNotEnabled, message
17
+ raise ActiveRecordCache::CacheNotEnabled, message
18
18
  end
19
19
 
20
20
  if id_or_record.is_a?(ActiveRecord::Base)
@@ -53,7 +53,7 @@ module ActiveRecordCache
53
53
  end
54
54
 
55
55
  if cache_misses.present?
56
- records += where(primary_key => cache_misses).all
56
+ records += where(primary_key => cache_misses).load
57
57
  end
58
58
 
59
59
  records
@@ -3,39 +3,18 @@ module ActiveRecordCache
3
3
  extend ActiveSupport::Concern
4
4
 
5
5
  included do
6
- alias_method_chain :find_by_attributes, :caching
7
6
  alias_method_chain :find_some, :caching
8
7
  alias_method_chain :find_one, :caching
9
8
  end
10
9
 
11
10
  protected
12
11
 
13
- def find_by_attributes_with_caching(match, attributes, *args)
14
- unless cached_lookup_allowed? && attributes == Array(@klass.primary_key) && (match.finder == :all || !args.first.is_a?(Array))
15
- return find_by_attributes_without_caching(match, attributes, *args)
16
- end
17
-
18
- param = args.first
19
-
20
- results = case match.finder
21
- when :all then @klass.find_some_through_cache(Array(param))
22
- when :first then @klass.find_through_cache(param)
23
- when :last then @klass.find_through_cache(param)
24
- end
25
-
26
- results
27
- end
28
-
29
12
  def find_some_with_caching(ids)
30
13
  return find_some_without_caching(ids) unless cached_lookup_allowed?
31
14
 
32
15
  results = find_some_through_cache(ids)
33
16
 
34
- if results.size != ids.size
35
- error = "Couldn't find all #{@klass.name.pluralize} with IDs "
36
- error << "(#{ids.join(", ")}) (found #{results.size} results, but was looking for #{ids.size})"
37
- raise ActiveRecord::RecordNotFound, error
38
- end
17
+ raise_record_not_found_exception!(ids, result.size, ids.size) unless results.size == ids.size
39
18
 
40
19
  results
41
20
  end
@@ -43,11 +22,9 @@ module ActiveRecordCache
43
22
  def find_one_with_caching(id)
44
23
  return find_one_without_caching(id) unless cached_lookup_allowed?
45
24
 
46
- record = @klass.find_through_cache(id)
25
+ record = find_through_cache(id)
47
26
 
48
- unless record
49
- raise ActiveRecord::RecordNotFound, "Couldn't find #{@klass.name} with #{primary_key}=#{id}"
50
- end
27
+ raise_record_not_found_exception!(id, 0, 1) unless record
51
28
 
52
29
  record
53
30
  end
@@ -6,10 +6,13 @@ class ActiveRecordCache::Railtie < Rails::Railtie
6
6
  initializer "activerecord_cache.initialize" do
7
7
  ActiveSupport.on_load(:active_record) do
8
8
  ActiveRecord::Base.send(:include, ActiveRecordCache::Base)
9
- ActiveRecord::FinderMethods.send(:include, ActiveRecordCache::FinderMethods)
10
9
  ActiveRecord::Associations::BelongsToAssociation.send(:include, ActiveRecordCache::BelongsToAssociation)
11
10
  ActiveRecord::Associations::Preloader::BelongsTo.send(:include, ActiveRecordCache::BelongsToPreloader)
12
11
  ActiveRecord::Relation.send(:include, ActiveRecordCache::Relation)
12
+
13
+ # FinderMethods only gets included in ActiveRecord::Relation, so extend it there.
14
+ # This gets around some headaches caused by extending modules already included in other classes.
15
+ ActiveRecord::Relation.send(:include, ActiveRecordCache::FinderMethods)
13
16
  end
14
17
  end
15
18
 
@@ -3,14 +3,12 @@ module ActiveRecordCache
3
3
  extend ActiveSupport::Concern
4
4
 
5
5
  included do
6
- alias_method_chain :to_a, :caching
6
+ alias_method_chain :exec_queries, :caching
7
7
  end
8
8
 
9
- def to_a_with_caching
10
- previously_loaded = loaded?
11
-
12
- records = to_a_without_caching
13
- records.each(&:write_to_cache) if @klass.use_activerecord_cache && !previously_loaded && select_values.empty?
9
+ def exec_queries_with_caching
10
+ records = exec_queries_without_caching
11
+ records.each(&:write_to_cache) if @klass.use_activerecord_cache && select_values.empty?
14
12
 
15
13
  records
16
14
  end
@@ -2,7 +2,7 @@ module ActiveRecordCache
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0
4
4
  MINOR = 0
5
- TINY = 6
5
+ TINY = 7
6
6
  PRE = nil
7
7
 
8
8
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
@@ -0,0 +1,167 @@
1
+ require 'test_helper'
2
+
3
+ class ActiveRecordCacheTest < ActiveSupport::TestCase
4
+
5
+ setup do
6
+ Rails.cache.clear
7
+ end
8
+
9
+
10
+
11
+ # Loading records should persist to cache
12
+ test "loading records using find should persist them to the cache" do
13
+ Rails.cache.expects(:write).twice
14
+ CachedRecord.find(1, 2)
15
+ end
16
+
17
+ test "loading all records should persist them to the cache" do
18
+ Rails.cache.expects(:write).twice
19
+ CachedRecord.all.load
20
+ end
21
+
22
+ test "loading records using a where should persist them to the cache" do
23
+ Rails.cache.expects(:write).once
24
+ CachedRecord.where(:name => 'John').load
25
+ end
26
+
27
+
28
+
29
+ # Finders should use the cache
30
+ test "#find(id) should use the cache" do
31
+ cache_records(1)
32
+
33
+ assert_query_count 0 do
34
+ CachedRecord.find(1)
35
+ end
36
+ end
37
+
38
+ test "#find(ids) should use the cache" do
39
+ cache_records(1,2)
40
+
41
+ assert_query_count 0 do
42
+ CachedRecord.find(1, 2)
43
+ end
44
+ end
45
+
46
+ test "#find(ids) with some cached records should query for additional records" do
47
+ cache_records(1)
48
+
49
+ assert_query_count 1 do
50
+ CachedRecord.find(1, 2)
51
+ end
52
+ end
53
+
54
+
55
+
56
+ # Complex queries should skip the cache
57
+ test "#where(:id => id, :other => criteria) should NOT use the cache" do
58
+ cache_records(1)
59
+
60
+ records = CachedRecord.where(:id => 1, :name => 'Bogus').load
61
+
62
+ assert records.empty?, 'should not find cached record'
63
+ end
64
+
65
+ test "#select(:id) should NOT use the cache" do
66
+ cache_records(1)
67
+
68
+ Rails.cache.expects(:write).never
69
+ CachedRecord.select(:id).load
70
+ end
71
+
72
+
73
+
74
+ # belongs_to associations should use the cache
75
+ test "accessing cached associations should use the cache" do
76
+ cache_records(1)
77
+ associated = AssociatedRecord.find(1)
78
+
79
+ assert_query_count 0 do
80
+ associated.cached_record
81
+ end
82
+ end
83
+
84
+ test "accessing associations should persist records to the cache" do
85
+ associated = AssociatedRecord.find(1)
86
+
87
+ Rails.cache.expects(:write).once
88
+ associated.cached_record
89
+ end
90
+
91
+ test "associations that do not reference the primary key should NOT use the cache" do
92
+ cache_records(1, 2)
93
+ non_primary = NonPrimaryAssociated.find(1)
94
+
95
+ assert_query_count 1 do
96
+ non_primary.cached_record
97
+ end
98
+ end
99
+
100
+
101
+
102
+ # Preloading associations should use the cache
103
+ test "preloading associations should use the cache" do
104
+ cache_records(1)
105
+
106
+ assert_query_count 1 do
107
+ AssociatedRecord.includes(:cached_record).find(1)
108
+ end
109
+ end
110
+
111
+ test "preloading associations should persist records to the cache" do
112
+ Rails.cache.expects(:write).once
113
+ AssociatedRecord.includes(:cached_record).find(1)
114
+ end
115
+
116
+ test "preloading associations that do not reference the primary key should NOT use the cache" do
117
+ cache_records(1, 2)
118
+
119
+ assert_query_count 2 do
120
+ NonPrimaryAssociated.includes(:cached_record).find(1)
121
+ end
122
+ end
123
+
124
+
125
+
126
+ # Saving and destroying
127
+ test "saving a record should write it to the cache" do
128
+ Rails.cache.expects(:write).once
129
+ CachedRecord.create!({})
130
+ end
131
+
132
+ test "destroying a record should remove it from the cache" do
133
+ Rails.cache.expects(:delete).once
134
+ CachedRecord.find(1).destroy
135
+ end
136
+
137
+
138
+
139
+ private
140
+
141
+ def assert_query_count(expected_count, &block)
142
+ count = count_queries do
143
+ yield block
144
+ end
145
+
146
+ assert_equal expected_count, count, "expected #{expected_count} queries"
147
+ end
148
+
149
+ def count_queries(&block)
150
+ count = 0
151
+
152
+ counter = ->(name, started, finished, unique_id, payload) {
153
+ unless payload[:name].in? %w[ CACHE SCHEMA ]
154
+ count += 1
155
+ end
156
+ }
157
+
158
+ ActiveSupport::Notifications.subscribed(counter, "sql.active_record", &block)
159
+
160
+ count
161
+ end
162
+
163
+ def cache_records(*records)
164
+ CachedRecord.find(Array(records).flatten)
165
+ end
166
+
167
+ end