activerecord_cache 0.0.6 → 0.0.7

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.
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