identity_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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7a3b2c0bfce28e5d4b88e8f9003b82b8b1ce6ac4
4
- data.tar.gz: 1aeba990c3842ddebcc6ffa81e7a95610d8e797b
3
+ metadata.gz: 4486319de757bbba037be1e7687cb18a2fd72dc8
4
+ data.tar.gz: 8869e35fceeca476227e9b8181a573cfe38f1d96
5
5
  SHA512:
6
- metadata.gz: 5cb1a76f43cd10bafd968cc27c3aad201ca5f10ce20e9af90231deb6b663e42f5505497ef63d8eff94f64ad60ddc05e46be79d9ff64a3671a86142d33480248a
7
- data.tar.gz: 976d47284787e9239bf81956fbfc0e518dd2c77915752e13b57c9cd70cf398b1b1d37f5325383ef1f415d76a6ac70ec0549c6d8432fd3b10bdcaee6f0297f345
6
+ metadata.gz: 1a7927f13588a480924f558473f536a94790a165820c4ff606e69456a5374394ae66c1b6eb6e40d39bd76490b0537a1b3bc164855f0174b122cbd1109376580e
7
+ data.tar.gz: 90f5bec88b0edd0e6c915804533eebb2d5f020dcf99ecc03b262eeaec9313d64f6c30aa9420384d8652014642198083d0bf17a8bd507872705c7838e2df827e8
data/CHANGELOG CHANGED
@@ -1,3 +1,9 @@
1
+ 0.0.7
2
+ * Fix: Not implemented error for cache_has_one with embed: false
3
+ * Fix: cache key to change when adding a cache_has_many association with :embed => false
4
+ * Add support for non-integer primary keys
5
+ * Fix: Compatibility rails 4.1 for quote_value, which needs default column.
6
+
1
7
  0.0.6
2
8
  * Fix: bug where previously nil-cached attribute caches weren't expired on record creation
3
9
  * Fix: cache key to not change when adding a non-embedded association.
@@ -4,5 +4,4 @@ source 'https://rubygems.org'
4
4
  gemspec
5
5
 
6
6
  gem 'activerecord', '~> 4.0.1'
7
- gem 'activesupport', '~> 4.0.1'
8
- gem 'dalli'
7
+ gem 'activesupport', '~> 4.0.1'
@@ -18,7 +18,7 @@ Gem::Specification.new do |gem|
18
18
  gem.add_dependency('ar_transaction_changes', '= 0.0.3')
19
19
  gem.add_dependency('activerecord', '>= 3.2', '< 4.1')
20
20
 
21
- gem.add_development_dependency('memcache-client')
21
+ gem.add_development_dependency('memcached_store', '~> 0.11.2')
22
22
  gem.add_development_dependency('rake')
23
23
  gem.add_development_dependency('mocha', '0.14.0')
24
24
 
@@ -29,5 +29,6 @@ Gem::Specification.new do |gem|
29
29
  else
30
30
  gem.add_development_dependency('cityhash', '0.6.0')
31
31
  gem.add_development_dependency('mysql2')
32
+ gem.add_development_dependency('stackprof') if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.1.0")
32
33
  end
33
34
  end
@@ -9,23 +9,18 @@ module IdentityCache
9
9
 
10
10
  def self.denormalized_schema_hash(klass)
11
11
  schema_string = schema_to_string(klass.columns)
12
- if !(associations = embedded_associations(klass)).empty?
13
- embedded_schema = associations.map do |name, options|
14
- "#{name}:(#{denormalized_schema_hash(options[:association_class])})"
15
- end.sort.join(',')
16
- schema_string << "," << embedded_schema
12
+ if klass.include?(IdentityCache)
13
+ klass.send(:all_cached_associations).sort.each do |name, options|
14
+ if options[:embed]
15
+ schema_string << ",#{name}:(#{denormalized_schema_hash(options[:association_class])})"
16
+ elsif options[:cached_ids_name]
17
+ schema_string << ",#{name}:ids"
18
+ end
19
+ end
17
20
  end
18
21
  IdentityCache.memcache_hash(schema_string)
19
22
  end
20
23
 
21
- def self.embedded_associations(klass)
22
- if klass.respond_to?(:all_embedded_associations)
23
- klass.all_embedded_associations
24
- else
25
- {}
26
- end
27
- end
28
-
29
24
  module ClassMethods
30
25
  def rails_cache_key(id)
31
26
  "#{rails_cache_key_prefix}#{id}"
@@ -14,10 +14,6 @@ module IdentityCache
14
14
  base.cache_attributes = []
15
15
  base.cache_indexes = []
16
16
  base.primary_cache_index_enabled = true
17
-
18
- base.private_class_method :build_normalized_has_many_cache, :build_denormalized_association_cache,
19
- :add_parent_expiry_hook, :identity_cache_multiple_value_dynamic_fetcher,
20
- :identity_cache_single_value_dynamic_fetcher, :identity_cache_sql_conditions
21
17
  end
22
18
 
23
19
  module ClassMethods
@@ -134,7 +130,7 @@ module IdentityCache
134
130
  # necessary if the name is not the lowercase pluralization of the
135
131
  # parent object's class)
136
132
  def cache_has_one(association, options = {})
137
- options[:embed] ||= true
133
+ options[:embed] = true unless options.has_key?(:embed)
138
134
  options[:inverse_name] ||= self.name.underscore.to_sym
139
135
  raise InverseAssociationError unless self.reflect_on_association(association)
140
136
  self.cached_has_ones[association] = options
@@ -182,12 +178,14 @@ module IdentityCache
182
178
  self.primary_cache_index_enabled = false
183
179
  end
184
180
 
181
+ private
182
+
185
183
  def identity_cache_single_value_dynamic_fetcher(fields, values) # :nodoc:
186
184
  sql_on_miss = "SELECT #{quoted_primary_key} FROM #{quoted_table_name} WHERE #{identity_cache_sql_conditions(fields, values)} LIMIT 1"
187
185
  cache_key = rails_cache_index_key_for_fields_and_values(fields, values)
188
186
  id = IdentityCache.fetch(cache_key) { connection.select_value(sql_on_miss) }
189
187
  unless id.nil?
190
- record = fetch_by_id(id.to_i)
188
+ record = fetch_by_id(id)
191
189
  IdentityCache.cache.delete(cache_key) unless record
192
190
  end
193
191
 
@@ -292,7 +290,7 @@ module IdentityCache
292
290
  end
293
291
 
294
292
  def identity_cache_sql_conditions(fields, values)
295
- fields.each_with_index.collect { |f, i| "#{connection.quote_column_name(f)} = #{quote_value(values[i])}" }.join(" AND ")
293
+ fields.each_with_index.collect { |f, i| "#{connection.quote_column_name(f)} = #{quote_value(values[i],nil)}" }.join(" AND ")
296
294
  end
297
295
  end
298
296
  end
@@ -5,16 +5,16 @@ module IdentityCache
5
5
 
6
6
  if new_parent && new_parent.respond_to?(:expire_primary_index, true)
7
7
  if should_expire_identity_cache_parent?(foreign_key, only_on_foreign_key_change)
8
- new_parent.expire_primary_index
9
- new_parent.expire_parent_cache if new_parent.respond_to?(:expire_parent_cache)
8
+ new_parent.send(:expire_primary_index)
9
+ new_parent.send(:expire_parent_cache) if new_parent.respond_to?(:expire_parent_cache, true)
10
10
  end
11
11
  end
12
12
 
13
13
  if transaction_changed_attributes[foreign_key].present?
14
14
  begin
15
15
  old_parent = parent_class.find(transaction_changed_attributes[foreign_key])
16
- old_parent.expire_primary_index if old_parent.respond_to?(:expire_primary_index)
17
- old_parent.expire_parent_cache if old_parent.respond_to?(:expire_parent_cache)
16
+ old_parent.send(:expire_primary_index) if old_parent.respond_to?(:expire_primary_index, true)
17
+ old_parent.send(:expire_parent_cache) if old_parent.respond_to?(:expire_parent_cache, true)
18
18
  rescue ActiveRecord::RecordNotFound => e
19
19
  # suppress errors finding the old parent if its been destroyed since it will have expired itself in that case
20
20
  end
@@ -3,16 +3,6 @@ module IdentityCache
3
3
  extend ActiveSupport::Concern
4
4
 
5
5
  included do |base|
6
- base.private_class_method :require_if_necessary
7
- base.private_class_method :coder_from_record
8
- base.private_class_method :record_from_coder
9
- base.private_class_method :set_embedded_association
10
- base.private_class_method :get_embedded_association
11
- base.private_class_method :add_cached_associations_to_coder
12
- base.instance_eval(<<-CODE, __FILE__, __LINE__ + 1)
13
- private :expire_cache, :was_new_record?, :fetch_denormalized_cached_association,
14
- :populate_denormalized_cached_association
15
- CODE
16
6
  base.after_commit :expire_cache
17
7
  base.after_touch :expire_cache
18
8
  end
@@ -66,7 +56,7 @@ module IdentityCache
66
56
  coders_by_key = IdentityCache.fetch_multi(*cache_keys) do |unresolved_keys|
67
57
  ids = unresolved_keys.map {|key| key_to_id_map[key] }
68
58
  records = find_batch(ids, options)
69
- records.compact.each(&:populate_association_caches)
59
+ records.compact.each{ |record| record.send(:populate_association_caches) }
70
60
  records.map {|record| coder_from_record(record) }
71
61
  end
72
62
 
@@ -81,6 +71,8 @@ module IdentityCache
81
71
  end
82
72
  end
83
73
 
74
+ private
75
+
84
76
  def record_from_coder(coder) #:nodoc:
85
77
  if coder.present? && coder.has_key?(:class)
86
78
  record = coder[:class].allocate
@@ -122,7 +114,7 @@ module IdentityCache
122
114
  else
123
115
  record_from_coder(coder_or_array)
124
116
  end
125
- variable_name = record.class.all_embedded_associations[association_name][:records_variable_name]
117
+ variable_name = record.class.send(:all_embedded_associations)[association_name][:records_variable_name]
126
118
  record.instance_variable_set(:"@#{variable_name}", IdentityCache.map_cached_nil_for(value))
127
119
  end
128
120
 
@@ -147,8 +139,8 @@ module IdentityCache
147
139
  end
148
140
 
149
141
  def add_cached_associations_to_coder(record, coder)
150
- if record.class.respond_to?(:all_embedded_associations) && record.class.all_embedded_associations.present?
151
- coder[:associations] = record.class.all_embedded_associations.each_with_object({}) do |(name, options), hash|
142
+ if record.class.respond_to?(:all_embedded_associations, true) && record.class.send(:all_embedded_associations).present?
143
+ coder[:associations] = record.class.send(:all_embedded_associations).each_with_object({}) do |(name, options), hash|
152
144
  hash[name] = IdentityCache.map_cached_nil_for(get_embedded_association(record, name, options))
153
145
  end
154
146
  end
@@ -179,7 +171,7 @@ module IdentityCache
179
171
 
180
172
  def resolve_cache_miss(id)
181
173
  object = self.includes(cache_fetch_includes).where(id: id).try(:first)
182
- object.try(:populate_association_caches)
174
+ object.send(:populate_association_caches) if object
183
175
  object
184
176
  end
185
177
 
@@ -208,8 +200,8 @@ module IdentityCache
208
200
 
209
201
  child_includes = additions.delete(child_association)
210
202
 
211
- if child_class.respond_to?(:cache_fetch_includes)
212
- child_includes = child_class.cache_fetch_includes(child_includes)
203
+ if child_class.respond_to?(:cache_fetch_includes, true)
204
+ child_includes = child_class.send(:cache_fetch_includes, child_includes)
213
205
  end
214
206
 
215
207
  if child_includes.blank?
@@ -295,8 +287,8 @@ module IdentityCache
295
287
  raise ArgumentError.new("Unknown cached association #{association} listed for prefetching")
296
288
  end
297
289
 
298
- if details && details[:association_class].respond_to?(:prefetch_associations)
299
- details[:association_class].prefetch_associations(sub_associations, next_level_records)
290
+ if details && details[:association_class].respond_to?(:prefetch_associations, true)
291
+ details[:association_class].send(:prefetch_associations, sub_associations, next_level_records)
300
292
  end
301
293
  end
302
294
  end
@@ -322,13 +314,15 @@ module IdentityCache
322
314
  end
323
315
  end
324
316
 
317
+ private
318
+
325
319
  def populate_association_caches # :nodoc:
326
- self.class.all_cached_associations_needing_population.each do |cached_association, options|
320
+ self.class.send(:all_cached_associations_needing_population).each do |cached_association, options|
327
321
  send(options[:population_method_name])
328
322
  reflection = options[:embed] && self.class.reflect_on_association(cached_association)
329
323
  if reflection && reflection.klass.respond_to?(:cached_has_manys)
330
324
  child_objects = Array.wrap(send(options[:cached_accessor_name]))
331
- child_objects.each(&:populate_association_caches)
325
+ child_objects.each{ |child| child.send(:populate_association_caches) }
332
326
  end
333
327
  end
334
328
  end
@@ -1,4 +1,4 @@
1
1
  module IdentityCache
2
- VERSION = "0.0.6"
2
+ VERSION = "0.0.7"
3
3
  CACHE_VERSION = 3
4
4
  end
@@ -3,7 +3,8 @@ require 'active_record'
3
3
  require 'active_support/core_ext'
4
4
  require 'active_support/cache'
5
5
  require 'identity_cache'
6
- require 'memcache'
6
+ require 'memcached_store'
7
+ require 'active_support/cache/memcached_store'
7
8
 
8
9
  if ENV['BOXEN_HOME'].present?
9
10
  $memcached_port = 21211
@@ -29,17 +30,9 @@ end
29
30
 
30
31
  def create_database(count)
31
32
  DatabaseConnection.setup
32
- a = CacheRunner.new(count)
33
33
 
34
- a.setup_models
35
-
36
- DatabaseConnection.setup
37
- # set up associations
38
- Item.cache_has_one :associated
39
- Item.cache_has_many :associated_records, :embed => true
40
- Item.cache_has_many :normalized_associated_records, :embed => false
41
- Item.cache_index :title, :unique => :true
42
- AssociatedRecord.cache_has_many :deeply_associated_records, :embed => true
34
+ helper = Object.new.extend(ActiveRecordObjects)
35
+ helper.setup_models
43
36
 
44
37
  return if database_ready(count)
45
38
  puts "Database not ready for performance testing, generating records"
@@ -60,6 +53,14 @@ def create_database(count)
60
53
  a.save
61
54
  end
62
55
  end
56
+ ensure
57
+ helper.teardown_models
58
+ end
59
+
60
+ def setup_embedded_associations
61
+ Item.cache_has_one :associated
62
+ Item.cache_has_many :associated_records, :embed => true
63
+ AssociatedRecord.cache_has_many :deeply_associated_records, :embed => true
63
64
  end
64
65
 
65
66
  class CacheRunner
@@ -71,81 +72,92 @@ class CacheRunner
71
72
  end
72
73
 
73
74
  def prepare
75
+ setup_models
76
+ end
77
+
78
+ def cleanup
79
+ teardown_models
74
80
  end
75
81
  end
76
82
 
83
+ CACHE_RUNNERS = []
84
+
77
85
  class FindRunner < CacheRunner
78
86
  def run
79
87
  (1..@count).each do |i|
80
- ::Item.find(i)
88
+ ::Item.find(i, :include => [:associated, {:associated_records => :deeply_associated_records}])
81
89
  end
82
90
  end
83
91
  end
92
+ CACHE_RUNNERS << FindRunner
84
93
 
85
94
  module MissRunner
86
95
  def prepare
96
+ super
87
97
  IdentityCache.cache.clear
88
98
  end
89
99
  end
90
100
 
91
- class FetchMissRunner < CacheRunner
92
- include MissRunner
93
-
94
- def run
95
- (1..@count).each do |i|
96
- rec = ::Item.fetch(i)
97
- rec.fetch_associated
98
- rec.fetch_associated_records
99
- end
101
+ module HitRunner
102
+ def prepare
103
+ super
104
+ run
100
105
  end
101
106
  end
102
107
 
103
- class DoubleFetchMissRunner < CacheRunner
104
- include MissRunner
108
+ class EmbedRunner < CacheRunner
109
+ def setup_models
110
+ super
111
+ Item.cache_has_one :associated
112
+ Item.cache_has_many :associated_records, :embed => true
113
+ AssociatedRecord.cache_has_many :deeply_associated_records, :embed => true
114
+ end
105
115
 
106
116
  def run
107
117
  (1..@count).each do |i|
108
118
  rec = ::Item.fetch(i)
109
119
  rec.fetch_associated
110
120
  rec.fetch_associated_records
111
- rec.fetch_normalized_associated_records
112
121
  end
113
122
  end
114
123
  end
115
124
 
116
- module HitRunner
117
- def prepare
118
- IdentityCache.cache.clear
119
- (1..@count).each do |i|
120
- rec = ::Item.fetch(i)
121
- rec.fetch_normalized_associated_records
122
- end
123
- end
125
+ class FetchEmbedMissRunner < EmbedRunner
126
+ include MissRunner
124
127
  end
128
+ CACHE_RUNNERS << FetchEmbedMissRunner
125
129
 
126
- class FetchHitRunner < CacheRunner
130
+ class FetchEmbedHitRunner < EmbedRunner
127
131
  include HitRunner
132
+ end
133
+ CACHE_RUNNERS << FetchEmbedHitRunner
134
+
135
+
136
+ class NormalizedRunner < CacheRunner
137
+ def setup_models
138
+ super
139
+ Item.cache_has_one :associated # :embed => false isn't supported
140
+ Item.cache_has_many :associated_records, :embed => false
141
+ AssociatedRecord.cache_has_many :deeply_associated_records, :embed => false
142
+ end
128
143
 
129
144
  def run
130
145
  (1..@count).each do |i|
131
146
  rec = ::Item.fetch(i)
132
- # these should all be no cost
133
147
  rec.fetch_associated
134
- rec.fetch_associated_records
148
+ associated_records = rec.fetch_associated_records
149
+ # FIXME: Only fetch_multi has :includes support, so use what it uses internally
150
+ AssociatedRecord.send(:prefetch_associations, :deeply_associated_records, associated_records)
135
151
  end
136
152
  end
137
153
  end
138
154
 
139
- class DoubleFetchHitRunner < CacheRunner
140
- include HitRunner
155
+ class FetchNormalizedMissRunner < NormalizedRunner
156
+ include MissRunner
157
+ end
158
+ CACHE_RUNNERS << FetchNormalizedMissRunner
141
159
 
142
- def run
143
- (1..@count).each do |i|
144
- rec = ::Item.fetch(i)
145
- # these should all be no cost
146
- rec.fetch_associated
147
- rec.fetch_associated_records
148
- rec.fetch_normalized_associated_records
149
- end
150
- end
160
+ class FetchNormalizedHitRunner < NormalizedRunner
161
+ include HitRunner
151
162
  end
163
+ CACHE_RUNNERS << FetchNormalizedHitRunner
@@ -3,29 +3,41 @@ require 'benchmark'
3
3
 
4
4
  require_relative 'cache_runner'
5
5
 
6
- RUNS = 4000
6
+ RUNS = 400
7
7
 
8
8
  class ARCreator
9
9
  include ActiveRecordObjects
10
10
  end
11
11
 
12
- def run(obj, bench)
13
- bench.report("#{obj.class.name}:") do
14
- obj.prepare
12
+ def run(obj)
13
+ obj.prepare
14
+ GC.start
15
+ Benchmark.measure do
15
16
  obj.run
16
17
  end
18
+ ensure
19
+ obj.cleanup
17
20
  end
18
21
 
19
- create_database(RUNS)
22
+ def benchmark(runners, label_width=0)
23
+ IdentityCache.cache.clear
24
+ runners.each do |runner|
25
+ print "#{runner.name}: ".ljust(label_width)
26
+ puts run(runner.new(RUNS))
27
+ end
28
+ end
20
29
 
21
- Benchmark.bmbm do |x|
22
- run(FindRunner.new(RUNS), x)
30
+ def bmbm(runners)
31
+ label_width = runners.map{ |r| r.name.size }.max + 2
32
+ width = label_width + Benchmark::CAPTION.size
23
33
 
24
- run(FetchMissRunner.new(RUNS), x)
34
+ puts 'Rehearsal: '.ljust(width, '-')
35
+ benchmark(runners, label_width)
36
+ puts '-' * width
25
37
 
26
- run(FetchHitRunner.new(RUNS), x)
38
+ benchmark(runners, label_width)
39
+ end
27
40
 
28
- run(DoubleFetchHitRunner.new(RUNS), x)
41
+ create_database(RUNS)
29
42
 
30
- run(DoubleFetchMissRunner.new(RUNS), x)
31
- end
43
+ bmbm(CACHE_RUNNERS)
@@ -1,30 +1,25 @@
1
1
  require 'rubygems'
2
2
  require 'benchmark'
3
- require 'ruby-prof'
3
+ require 'stackprof'
4
4
 
5
5
  require_relative 'cache_runner'
6
6
 
7
7
  RUNS = 1000
8
- RubyProf.measure_mode = RubyProf::CPU_TIME
9
8
 
10
9
  def run(obj)
10
+ puts "#{obj.class.name}:"
11
11
  obj.prepare
12
- RubyProf.start
13
- obj.run
14
- result = RubyProf.stop
15
- puts "Results for #{obj.class.name}:"
16
- printer = RubyProf::FlatPrinter.new(result)
17
- printer.print(STDOUT)
12
+ data = StackProf.run(mode: :cpu) do
13
+ obj.run
14
+ end
15
+ StackProf::Report.new(data).print_text(false, 20)
16
+ puts
17
+ ensure
18
+ obj.cleanup
18
19
  end
19
20
 
20
21
  create_database(RUNS)
21
22
 
22
- run(FindRunner.new(RUNS))
23
-
24
- run(FetchMissRunner.new(RUNS))
25
-
26
- run(FetchHitRunner.new(RUNS))
27
-
28
- run(DoubleFetchHitRunner.new(RUNS))
29
-
30
- run(DoubleFetchMissRunner.new(RUNS))
23
+ CACHE_RUNNERS.each do |runner|
24
+ run(runner.new(RUNS))
25
+ end
@@ -7,28 +7,28 @@ class CacheFetchIncludesTest < IdentityCache::TestCase
7
7
 
8
8
  def test_cached_embedded_has_manys_are_included_in_includes
9
9
  Item.send(:cache_has_many, :associated_records, :embed => true)
10
- assert_equal [:associated_records], Item.cache_fetch_includes
10
+ assert_equal [:associated_records], Item.send(:cache_fetch_includes)
11
11
  end
12
12
 
13
13
  def test_cached_nonembedded_has_manys_are_included_in_includes
14
14
  Item.send(:cache_has_many, :associated_records, :embed => false)
15
- assert_equal [], Item.cache_fetch_includes
15
+ assert_equal [], Item.send(:cache_fetch_includes)
16
16
  end
17
17
 
18
18
  def test_cached_has_ones_are_included_in_includes
19
19
  Item.send(:cache_has_one, :associated)
20
- assert_equal [:associated], Item.cache_fetch_includes
20
+ assert_equal [:associated], Item.send(:cache_fetch_includes)
21
21
  end
22
22
 
23
23
  def test_cached_nonembedded_belongs_tos_are_not_included_in_includes
24
24
  Item.send(:cache_belongs_to, :item)
25
- assert_equal [], Item.cache_fetch_includes
25
+ assert_equal [], Item.send(:cache_fetch_includes)
26
26
  end
27
27
 
28
28
  def test_cached_child_associations_are_included_in_includes
29
29
  Item.send(:cache_has_many, :associated_records, :embed => true)
30
30
  AssociatedRecord.send(:cache_has_many, :deeply_associated_records, :embed => true)
31
- assert_equal [{:associated_records => [:deeply_associated_records]}], Item.cache_fetch_includes
31
+ assert_equal [{:associated_records => [:deeply_associated_records]}], Item.send(:cache_fetch_includes)
32
32
  end
33
33
 
34
34
  def test_multiple_cached_associations_and_child_associations_are_included_in_includes
@@ -40,16 +40,16 @@ class CacheFetchIncludesTest < IdentityCache::TestCase
40
40
  {:associated_records => [:deeply_associated_records]},
41
41
  :polymorphic_records,
42
42
  {:associated => [:deeply_associated_records]}
43
- ], Item.cache_fetch_includes
43
+ ], Item.send(:cache_fetch_includes)
44
44
  end
45
45
 
46
46
  def test_empty_additions_for_top_level_associations_makes_no_difference
47
47
  Item.send(:cache_has_many, :associated_records, :embed => true)
48
- assert_equal [:associated_records], Item.cache_fetch_includes({})
48
+ assert_equal [:associated_records], Item.send(:cache_fetch_includes, {})
49
49
  end
50
50
 
51
51
  def test_top_level_additions_are_included_in_includes
52
- assert_equal [{:associated_records => []}], Item.cache_fetch_includes({:associated_records => []})
52
+ assert_equal [{:associated_records => []}], Item.send(:cache_fetch_includes, {:associated_records => []})
53
53
  end
54
54
 
55
55
  def test_top_level_additions_alongside_top_level_cached_associations_are_included_in_includes
@@ -57,21 +57,21 @@ class CacheFetchIncludesTest < IdentityCache::TestCase
57
57
  assert_equal [
58
58
  :associated_records,
59
59
  {:polymorphic_records => []}
60
- ], Item.cache_fetch_includes({:polymorphic_records => []})
60
+ ], Item.send(:cache_fetch_includes, {:polymorphic_records => []})
61
61
  end
62
62
 
63
63
  def test_child_level_additions_for_top_level_cached_associations_are_included_in_includes
64
64
  Item.send(:cache_has_many, :associated_records, :embed => true)
65
65
  assert_equal [
66
66
  {:associated_records => [{:deeply_associated_records => []}]}
67
- ], Item.cache_fetch_includes({:associated_records => :deeply_associated_records})
67
+ ], Item.send(:cache_fetch_includes, {:associated_records => :deeply_associated_records})
68
68
  end
69
69
 
70
70
  def test_array_child_level_additions_for_top_level_cached_associations_are_included_in_includes
71
71
  Item.send(:cache_has_many, :associated_records, :embed => true)
72
72
  assert_equal [
73
73
  {:associated_records => [{:deeply_associated_records => []}]}
74
- ], Item.cache_fetch_includes({:associated_records => [:deeply_associated_records]})
74
+ ], Item.send(:cache_fetch_includes, {:associated_records => [:deeply_associated_records]})
75
75
  end
76
76
 
77
77
  def test_array_child_level_additions_for_child_level_cached_associations_are_included_in_includes
@@ -82,7 +82,7 @@ class CacheFetchIncludesTest < IdentityCache::TestCase
82
82
  :deeply_associated_records,
83
83
  {:record => []}
84
84
  ]}
85
- ], Item.cache_fetch_includes({:associated_records => [:record]})
85
+ ], Item.send(:cache_fetch_includes, {:associated_records => [:record]})
86
86
  end
87
87
 
88
88
  end
@@ -28,7 +28,7 @@ class DenormalizedHasOneTest < IdentityCache::TestCase
28
28
  @record.associated = nil
29
29
  @record.save!
30
30
  @record.reload
31
- @record.populate_association_caches
31
+ @record.send(:populate_association_caches)
32
32
  Item.expects(:resolve_cache_miss).with(@record.id).once.returns(@record)
33
33
 
34
34
  IdentityCache.cache.expects(:read).with(@record.secondary_cache_index_key_for_current_values([:title]))
@@ -51,7 +51,7 @@ class DenormalizedHasOneTest < IdentityCache::TestCase
51
51
  end
52
52
 
53
53
  def test_on_cache_hit_record_should_come_back_with_cached_association
54
- @record.populate_association_caches
54
+ @record.send(:populate_association_caches)
55
55
  Item.expects(:resolve_cache_miss).with(1).once.returns(@record)
56
56
  Item.fetch_by_title('foo')
57
57
 
@@ -67,7 +67,7 @@ class DenormalizedHasOneTest < IdentityCache::TestCase
67
67
  @record.save!
68
68
  @record.reload
69
69
 
70
- @record.populate_association_caches
70
+ @record.send(:populate_association_caches)
71
71
  Item.expects(:resolve_cache_miss).with(1).once.returns(@record)
72
72
  Item.fetch_by_title('foo')
73
73
 
@@ -125,7 +125,7 @@ class FetchMultiTest < IdentityCache::TestCase
125
125
  Item.expects(:where).returns(mock_relation)
126
126
  mock_relation.expects(:includes).returns(stub(:to_a => [@bob, @joe, @fred]))
127
127
 
128
- Item.find_batch([@bob, @joe, @fred].map(&:id).map(&:to_s))
128
+ Item.send(:find_batch, [@bob, @joe, @fred].map(&:id).map(&:to_s))
129
129
  end
130
130
 
131
131
  def test_fetch_multi_doesnt_freeze_keys
@@ -203,7 +203,7 @@ class FetchMultiWithBatchedAssociationsTest < IdentityCache::TestCase
203
203
  Item.expects(:where).returns(mock_relation)
204
204
  mock_relation.expects(:includes).returns(stub(:to_a => [@bob, @joe, @fred]))
205
205
 
206
- Item.find_batch([@bob, @joe, @fred].map(&:id).map(&:to_s))
206
+ Item.send(:find_batch, [@bob, @joe, @fred].map(&:id).map(&:to_s))
207
207
  end
208
208
 
209
209
  private
@@ -2,7 +2,7 @@ require 'logger'
2
2
 
3
3
  module Rails
4
4
 
5
- class Cache < ActiveSupport::Cache::MemCacheStore
5
+ class Cache < ActiveSupport::Cache::MemcachedStore
6
6
  end
7
7
 
8
8
  def self.cache
@@ -0,0 +1,9 @@
1
+ require "test_helper"
2
+
3
+ class NormalizedHasOneTest < IdentityCache::TestCase
4
+ def test_not_implemented_error
5
+ assert_raises(NotImplementedError) do
6
+ Item.cache_has_one :associated, :embed => false
7
+ end
8
+ end
9
+ end
@@ -23,7 +23,7 @@ class RecursiveDenormalizedHasManyTest < IdentityCache::TestCase
23
23
  end
24
24
 
25
25
  def test_cache_fetch_includes
26
- assert_equal [{:associated_records => [:deeply_associated_records]}, :associated => [:deeply_associated_records]], Item.cache_fetch_includes
26
+ assert_equal [{:associated_records => [:deeply_associated_records]}, :associated => [:deeply_associated_records]], Item.send(:cache_fetch_includes)
27
27
  end
28
28
 
29
29
  def test_uncached_record_from_the_db_will_use_normal_association_for_deeply_associated_records
@@ -94,4 +94,14 @@ class SchemaChangeTest < IdentityCache::TestCase
94
94
  Item.expects(:resolve_cache_miss).returns(@record)
95
95
  record = Item.fetch(@record.id)
96
96
  end
97
+
98
+ def test_add_non_embedded_cache_has_many
99
+ record = Item.fetch(@record.id)
100
+
101
+ Item.cache_has_many :polymorphic_records, :inverse_name => :owner, :embed => false
102
+ read_new_schema
103
+
104
+ Item.expects(:resolve_cache_miss).returns(@record)
105
+ record = Item.fetch(@record.id)
106
+ end
97
107
  end
@@ -1,6 +1,8 @@
1
1
  require 'minitest/autorun'
2
2
  require 'mocha/setup'
3
3
  require 'active_record'
4
+ require 'memcached_store'
5
+ require 'active_support/cache/memcached_store'
4
6
  require 'helpers/cache'
5
7
  require 'helpers/database_connection'
6
8
  require 'helpers/active_record_objects'
@@ -19,7 +21,7 @@ DatabaseConnection.setup
19
21
  ActiveSupport::Cache::Store.instrument = true
20
22
 
21
23
  # This patches AR::MemcacheStore to notify AS::Notifications upon read_multis like the rest of rails does
22
- class ActiveSupport::Cache::MemCacheStore
24
+ class ActiveSupport::Cache::MemcachedStore
23
25
  def read_multi_with_instrumentation(*args, &block)
24
26
  instrument("read_multi", "MULTI", {:keys => args}) do
25
27
  read_multi_without_instrumentation(*args, &block)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: identity_cache
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Camilo Lopez
@@ -13,7 +13,7 @@ authors:
13
13
  autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
- date: 2014-02-21 00:00:00.000000000 Z
16
+ date: 2014-03-05 00:00:00.000000000 Z
17
17
  dependencies:
18
18
  - !ruby/object:Gem::Dependency
19
19
  name: ar_transaction_changes
@@ -50,19 +50,19 @@ dependencies:
50
50
  - !ruby/object:Gem::Version
51
51
  version: '4.1'
52
52
  - !ruby/object:Gem::Dependency
53
- name: memcache-client
53
+ name: memcached_store
54
54
  requirement: !ruby/object:Gem::Requirement
55
55
  requirements:
56
- - - ">="
56
+ - - "~>"
57
57
  - !ruby/object:Gem::Version
58
- version: '0'
58
+ version: 0.11.2
59
59
  type: :development
60
60
  prerelease: false
61
61
  version_requirements: !ruby/object:Gem::Requirement
62
62
  requirements:
63
- - - ">="
63
+ - - "~>"
64
64
  - !ruby/object:Gem::Version
65
- version: '0'
65
+ version: 0.11.2
66
66
  - !ruby/object:Gem::Dependency
67
67
  name: rake
68
68
  requirement: !ruby/object:Gem::Requirement
@@ -119,6 +119,20 @@ dependencies:
119
119
  - - ">="
120
120
  - !ruby/object:Gem::Version
121
121
  version: '0'
122
+ - !ruby/object:Gem::Dependency
123
+ name: stackprof
124
+ requirement: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
129
+ type: :development
130
+ prerelease: false
131
+ version_requirements: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - ">="
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
122
136
  description: Opt in read through ActiveRecord caching.
123
137
  email:
124
138
  - harry.brundage@shopify.com
@@ -168,6 +182,7 @@ files:
168
182
  - test/memoized_cache_proxy_test.rb
169
183
  - test/normalized_belongs_to_test.rb
170
184
  - test/normalized_has_many_test.rb
185
+ - test/normalized_has_one_test.rb
171
186
  - test/recursive_denormalized_has_many_test.rb
172
187
  - test/save_test.rb
173
188
  - test/schema_change_test.rb
@@ -219,8 +234,10 @@ test_files:
219
234
  - test/memoized_cache_proxy_test.rb
220
235
  - test/normalized_belongs_to_test.rb
221
236
  - test/normalized_has_many_test.rb
237
+ - test/normalized_has_one_test.rb
222
238
  - test/recursive_denormalized_has_many_test.rb
223
239
  - test/save_test.rb
224
240
  - test/schema_change_test.rb
225
241
  - test/serialization_format_change_test.rb
226
242
  - test/test_helper.rb
243
+ has_rdoc: