counter_culture 0.1.4 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -7,6 +7,163 @@ Turbo-charged counter caches for your Rails app. Huge improvements over the Rail
7
7
  * Supports dynamic column names, making it possible to split up the counter cache for different types of objects
8
8
  * Executes counter updates after the commit, avoiding [deadlocks](http://mina.naguib.ca/blog/2010/11/22/postgresql-foreign-key-deadlocks.html)
9
9
 
10
+ ## Installation
11
+
12
+ Add counter_culture to your Gemfile:
13
+
14
+ ```ruby
15
+ gem 'counter_culture', '~> 0.1.4'
16
+ ```
17
+
18
+ Then run ```bundle update ```
19
+
20
+ ## Database Schema
21
+
22
+ You will need to manually create the necessary columns for all counter caches. Use code like the following in your migration:
23
+ ```ruby
24
+ add_column :categories, :products_count, :integer, :null => false, :default => 0
25
+ ```
26
+ It is important to make the column ```NOT NULL``` and set a default of zero for this gem to work correctly.
27
+
28
+ If you are adding counter caches to existing data, you must [manually populate their values](#manually-populating-counter-cache-values).
29
+
30
+ ## Usage
31
+
32
+ ### Simple counter-cache
33
+
34
+ ```ruby
35
+ class Product < ActiveRecord::Base
36
+ belongs_to :category
37
+ counter_culture :category
38
+ end
39
+
40
+ class Category < ActiveRecord::Base
41
+ has_many :products
42
+ end
43
+ ```
44
+
45
+ Now, the ```Category``` model will keep an up-to-date counter-cache in the ```products_count``` column of the ```categories``` table.
46
+
47
+ ### Multi-level counter-cache
48
+
49
+ ```ruby
50
+ class Product < ActiveRecord::Base
51
+ belongs_to :sub_category
52
+ counter_culture [:sub_category, :category]
53
+ end
54
+
55
+ class SubCategory < ActiveRecord::Base
56
+ has_many :products
57
+ belongs_to :category
58
+ end
59
+
60
+ class Category < ActiveRecord::Base
61
+ has_many :sub_categories
62
+ end
63
+ ```
64
+
65
+ Now, the ```Category``` model will keep an up-to-date counter-cache in the ```products_count``` column of the ```categories``` table. This will work with any number of levels.
66
+
67
+ ### Customizing the column name
68
+
69
+ ```ruby
70
+ class Product < ActiveRecord::Base
71
+ belongs_to :category
72
+ counter_culture :category, :column_name => "products_counter_cache"
73
+ end
74
+
75
+ class Category < ActiveRecord::Base
76
+ has_many :products
77
+ end
78
+ ```
79
+
80
+ Now, the ```Category``` model will keep an up-to-date counter-cache in the ```products_counter_cache``` column of the ```categories``` table. This will also work with multi-level counter caches.
81
+
82
+ ### Dynamic column name
83
+
84
+ ```ruby
85
+ class Product < ActiveRecord::Base
86
+ belongs_to :category
87
+ counter_culture :category, :column_name => Proc.new {|model| "#{model.product_type}_count" }
88
+ # attribute product_type may be one of ['awesome', 'sucky']
89
+ end
90
+
91
+ class Category < ActiveRecord::Base
92
+ has_many :products
93
+ end
94
+ ```
95
+
96
+ Now, the ```Category``` model will keep two up-to-date counter-caches in the ```awesome_count``` and ```sucky_count``` columns of the ```categories``` table. Products with type ```'awesome'``` will affect only the ```awesome_count```, while products with type ```'sucky'``` will affect only the ```sucky_count```. This will also work with multi-level counter caches.
97
+
98
+ ### Dynamically over-writing affected foreign keys
99
+
100
+ ```ruby
101
+ class Product < ActiveRecord::Base
102
+ belongs_to :category
103
+ counter_culture :category, :foreign_key_values =>
104
+ Proc.new {|category_id| [category_id, Category.find_by_id(category_id).try(:parent_category).try(:id)] }
105
+ end
106
+
107
+ class Category < ActiveRecord::Base
108
+ belongs_to :parent_category, :class_name => 'Category', :foreign_key => 'parent_id'
109
+ has_many :children, :class_name => 'Category', :foreign_key => 'parent_id'
110
+
111
+ has_many :products
112
+ end
113
+ ```
114
+
115
+ Now, the ```Category``` model will keep an up-to-date counter-cache in the ```products_count``` column of the ```categories``` table. Each product will affect the counts of both its immediate category and that category's parent. This will work with any number of levels.
116
+
117
+ ### Manually populating counter cache values
118
+
119
+ You will sometimes want to populate counter-cache values from primary data. This is required when adding counter-caches to existing data. It is also recommended to run this regularly (at BestVendor, we run it once a week) to catch any incorrect values in the counter caches.
120
+
121
+ ```ruby
122
+ Product.counter_culture_fix_counts
123
+ # will automatically fix counts for all counter caches defined on Product
124
+
125
+ Product.counter_culture_fix_counts :except => :category
126
+ # will automatically fix counts for all counter caches defined on Product, except for the :category relation
127
+
128
+ Product.counter_culture_fix_counts :only => :category
129
+ # will automatically fix counts only on the :category relation on Product
130
+
131
+ # :except and :only also accept arrays
132
+ ```
133
+
134
+ ```counter_culture_fix_counts``` returns an array of hashes of all incorrect values for debugging purposes. The hashes have the following format:
135
+
136
+ ```ruby
137
+ { :entity => which model the count was fixed on,
138
+ :id => the id of the model that had the incorrect count,
139
+ :what => which column contained the incorrect count,
140
+ :wrong => the previously saved, incorrect count,
141
+ :right => the newly fixed, correct count }
142
+ ```
143
+
144
+ ```counter_culture_fix_counts``` is optimized to minimize the number of queries and runs very quickly.
145
+
146
+ #### Handling dynamic column names
147
+
148
+ Manually populating counter caches with dynammic column names requires additional configuration:
149
+
150
+ ```ruby
151
+ class Product < ActiveRecord::Base
152
+ belongs_to :category
153
+ counter_culture :category,
154
+ :column_name => Proc.new {|model| "#{model.product_type}_count" },
155
+ :column_names => {
156
+ ["products.product_type = ?", 'awesome'] => 'awesome_count',
157
+ ["products.product_type = ?", 'sucky'] => 'sucky_count'
158
+ }
159
+ # attribute product_type may be one of ['awesome', 'sucky']
160
+ end
161
+ ```
162
+
163
+ #### Handling over-written, dynamic foreign keys
164
+
165
+ Manually populating counter caches with dynamicall over-written foreign keys (```:foreign_key_values``` option) is not supported. You will have to write code to handle this case yourself.
166
+
10
167
  ## Contributing to counter_culture
11
168
 
12
169
  * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.4
1
+ 0.1.5
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "counter_culture"
8
- s.version = "0.1.4"
8
+ s.version = "0.1.5"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Magnus von Koeller"]
12
- s.date = "2012-05-31"
12
+ s.date = "2012-06-01"
13
13
  s.description = "counter_culture provides turbo-charged counter caches that are kept up-to-date not just on create and destroy, that support multiple levels of indirection through relationships, allow dynamic column names and that avoid deadlocks by updating in the after_commit callback."
14
14
  s.email = "magnus@vonkoeller.de"
15
15
  s.extra_rdoc_files = [
@@ -29,6 +29,7 @@ Gem::Specification.new do |s|
29
29
  "counter_culture.gemspec",
30
30
  "lib/counter_culture.rb",
31
31
  "spec/counter_culture_spec.rb",
32
+ "spec/models/category.rb",
32
33
  "spec/models/company.rb",
33
34
  "spec/models/industry.rb",
34
35
  "spec/models/product.rb",
@@ -59,6 +59,15 @@ module CounterCulture
59
59
  next if options[:exclude] && options[:exclude].include?(hash[:relation])
60
60
  next if options[:only] && !options[:only].include?(hash[:relation])
61
61
 
62
+ if options[:skip_unsupported]
63
+ next if (hash[:foreign_key_values] || (hash[:counter_cache_name].is_a?(Proc) && !hash[:column_names]))
64
+ else
65
+ raise "Fixing counter caches is not supported when using :foreign_key_values; you may skip this relation with :skip_unsupported => true" if hash[:foreign_key_values]
66
+ raise "Must provide :column_names option for relation #{hash[:relation].inspect} when :column_name is a Proc; you may skip this relation with :skip_unsupported => true" if hash[:counter_cache_name].is_a?(Proc) && !hash[:column_names]
67
+ end
68
+
69
+ # if we're provided a custom set of column names with conditions, use them; just use the
70
+ # column name otherwise
62
71
  # which class does this relation ultimately point to? that's where we have to start
63
72
  klass = relation_klass(hash[:relation])
64
73
 
@@ -66,11 +75,6 @@ module CounterCulture
66
75
  query = klass.select("#{klass.table_name}.id, COUNT(#{self.table_name}.id) AS count")
67
76
  query = query.group("#{klass.table_name}.id")
68
77
 
69
- raise "Fixing counter caches is not supported when using :foreign_key_values" if hash[:foreign_key_values]
70
-
71
- # if we're provided a custom set of column names with conditions, use them; just use the
72
- # column name otherwise
73
- raise "Must provide :column_names option for relation #{hash[:relation].inspect} when :counter_cache_name is a Proc" if hash[:counter_cache_name].is_a?(Proc) && !hash[:column_names]
74
78
  column_names = hash[:column_names] || {nil => hash[:counter_cache_name]}
75
79
  raise ":column_names must be a Hash of conditions and column names" unless column_names.is_a?(Hash)
76
80
 
@@ -5,6 +5,7 @@ require 'models/industry'
5
5
  require 'models/product'
6
6
  require 'models/review'
7
7
  require 'models/user'
8
+ require 'models/category'
8
9
 
9
10
  describe "CounterCulture" do
10
11
  it "increments counter cache on create" do
@@ -229,4 +230,505 @@ describe "CounterCulture" do
229
230
  product2.rexiews_count.should == 1
230
231
  end
231
232
 
233
+ it "increments third-level counter cache on create" do
234
+ industry = Industry.create
235
+ company = Company.create :industry_id => industry.id
236
+ user = User.create :company_id => company.id
237
+ product = Product.create
238
+
239
+ industry.reviews_count.should == 0
240
+ company.reviews_count.should == 0
241
+ user.reviews_count.should == 0
242
+ product.reviews_count.should == 0
243
+
244
+ review = Review.create :user_id => user.id, :product_id => product.id
245
+
246
+ industry.reload
247
+ company.reload
248
+ user.reload
249
+ product.reload
250
+
251
+ industry.reviews_count.should == 1
252
+ company.reviews_count.should == 1
253
+ user.reviews_count.should == 1
254
+ product.reviews_count.should == 1
255
+ end
256
+
257
+ it "decrements third-level counter cache on destroy" do
258
+ industry = Industry.create
259
+ company = Company.create :industry_id => industry.id
260
+ user = User.create :company_id => company.id
261
+ product = Product.create
262
+
263
+ industry.reviews_count.should == 0
264
+ company.reviews_count.should == 0
265
+ user.reviews_count.should == 0
266
+ product.reviews_count.should == 0
267
+
268
+ review = Review.create :user_id => user.id, :product_id => product.id
269
+
270
+ industry.reload
271
+ company.reload
272
+ user.reload
273
+ product.reload
274
+
275
+ industry.reviews_count.should == 1
276
+ company.reviews_count.should == 1
277
+ user.reviews_count.should == 1
278
+ product.reviews_count.should == 1
279
+
280
+ review.destroy
281
+
282
+ industry.reload
283
+ company.reload
284
+ user.reload
285
+ product.reload
286
+
287
+ industry.reviews_count.should == 0
288
+ company.reviews_count.should == 0
289
+ user.reviews_count.should == 0
290
+ product.reviews_count.should == 0
291
+ end
292
+
293
+ it "updates third-level counter cache on update" do
294
+ industry1 = Industry.create
295
+ industry2 = Industry.create
296
+ company1 = Company.create :industry_id => industry1.id
297
+ company2 = Company.create :industry_id => industry2.id
298
+ user1 = User.create :company_id => company1.id
299
+ user2 = User.create :company_id => company2.id
300
+ product = Product.create
301
+
302
+ industry1.reviews_count.should == 0
303
+ industry2.reviews_count.should == 0
304
+ company1.reviews_count.should == 0
305
+ company2.reviews_count.should == 0
306
+ user1.reviews_count.should == 0
307
+ user2.reviews_count.should == 0
308
+
309
+ review = Review.create :user_id => user1.id, :product_id => product.id
310
+
311
+ industry1.reload
312
+ industry2.reload
313
+ company1.reload
314
+ company2.reload
315
+ user1.reload
316
+ user2.reload
317
+
318
+ industry1.reviews_count.should == 1
319
+ industry2.reviews_count.should == 0
320
+ company1.reviews_count.should == 1
321
+ company2.reviews_count.should == 0
322
+ user1.reviews_count.should == 1
323
+ user2.reviews_count.should == 0
324
+
325
+ review.user = user2
326
+ review.save!
327
+
328
+ industry1.reload
329
+ industry2.reload
330
+ company1.reload
331
+ company2.reload
332
+ user1.reload
333
+ user2.reload
334
+
335
+ industry1.reviews_count.should == 0
336
+ industry2.reviews_count.should == 1
337
+ company1.reviews_count.should == 0
338
+ company2.reviews_count.should == 1
339
+ user1.reviews_count.should == 0
340
+ user2.reviews_count.should == 1
341
+ end
342
+
343
+ it "increments third-level custom counter cache on create" do
344
+ industry = Industry.create
345
+ company = Company.create :industry_id => industry.id
346
+ user = User.create :company_id => company.id
347
+ product = Product.create
348
+
349
+ industry.rexiews_count.should == 0
350
+
351
+ review = Review.create :user_id => user.id, :product_id => product.id
352
+
353
+ industry.reload
354
+
355
+ industry.rexiews_count.should == 1
356
+ end
357
+
358
+ it "decrements third-level custom counter cache on destroy" do
359
+ industry = Industry.create
360
+ company = Company.create :industry_id => industry.id
361
+ user = User.create :company_id => company.id
362
+ product = Product.create
363
+
364
+ industry.rexiews_count.should == 0
365
+
366
+ review = Review.create :user_id => user.id, :product_id => product.id
367
+
368
+ industry.reload
369
+ industry.rexiews_count.should == 1
370
+
371
+ review.destroy
372
+
373
+ industry.reload
374
+ industry.rexiews_count.should == 0
375
+ end
376
+
377
+ it "updates third-level custom counter cache on update" do
378
+ industry1 = Industry.create
379
+ industry2 = Industry.create
380
+ company1 = Company.create :industry_id => industry1.id
381
+ company2 = Company.create :industry_id => industry2.id
382
+ user1 = User.create :company_id => company1.id
383
+ user2 = User.create :company_id => company2.id
384
+ product = Product.create
385
+
386
+ industry1.rexiews_count.should == 0
387
+ industry2.rexiews_count.should == 0
388
+
389
+ review = Review.create :user_id => user1.id, :product_id => product.id
390
+
391
+ industry1.reload
392
+ industry1.rexiews_count.should == 1
393
+ industry2.reload
394
+ industry2.rexiews_count.should == 0
395
+
396
+ review.user = user2
397
+ review.save!
398
+
399
+ industry1.reload
400
+ industry1.rexiews_count.should == 0
401
+ industry2.reload
402
+ industry2.rexiews_count.should == 1
403
+ end
404
+
405
+ it "increments dynamic counter cache on create" do
406
+ user = User.create
407
+ product = Product.create
408
+
409
+ user.using_count.should == 0
410
+ user.tried_count.should == 0
411
+
412
+ review_using = Review.create :user_id => user.id, :product_id => product.id, :review_type => 'using'
413
+
414
+ user.reload
415
+
416
+ user.using_count.should == 1
417
+ user.tried_count.should == 0
418
+
419
+ review_tried = Review.create :user_id => user.id, :product_id => product.id, :review_type => 'tried'
420
+
421
+ user.reload
422
+
423
+ user.using_count.should == 1
424
+ user.tried_count.should == 1
425
+ end
426
+
427
+ it "decrements dynamic counter cache on destroy" do
428
+ user = User.create
429
+ product = Product.create
430
+
431
+ user.using_count.should == 0
432
+ user.tried_count.should == 0
433
+
434
+ review_using = Review.create :user_id => user.id, :product_id => product.id, :review_type => 'using'
435
+
436
+ user.reload
437
+
438
+ user.using_count.should == 1
439
+ user.tried_count.should == 0
440
+
441
+ review_tried = Review.create :user_id => user.id, :product_id => product.id, :review_type => 'tried'
442
+
443
+ user.reload
444
+
445
+ user.using_count.should == 1
446
+ user.tried_count.should == 1
447
+
448
+ review_tried.destroy
449
+
450
+ user.reload
451
+
452
+ user.using_count.should == 1
453
+ user.tried_count.should == 0
454
+
455
+ review_using.destroy
456
+
457
+ user.reload
458
+
459
+ user.using_count.should == 0
460
+ user.tried_count.should == 0
461
+ end
462
+
463
+ it "increments third-level dynamic counter cache on create" do
464
+ industry = Industry.create
465
+ company = Company.create :industry_id => industry.id
466
+ user = User.create :company_id => company.id
467
+ product = Product.create
468
+
469
+ industry.using_count.should == 0
470
+ industry.tried_count.should == 0
471
+
472
+ review_using = Review.create :user_id => user.id, :product_id => product.id, :review_type => 'using'
473
+
474
+ industry.reload
475
+
476
+ industry.using_count.should == 1
477
+ industry.tried_count.should == 0
478
+
479
+ review_tried = Review.create :user_id => user.id, :product_id => product.id, :review_type => 'tried'
480
+
481
+ industry.reload
482
+
483
+ industry.using_count.should == 1
484
+ industry.tried_count.should == 1
485
+ end
486
+
487
+ it "decrements third-level custom counter cache on destroy" do
488
+ industry = Industry.create
489
+ company = Company.create :industry_id => industry.id
490
+ user = User.create :company_id => company.id
491
+ product = Product.create
492
+
493
+ industry.using_count.should == 0
494
+ industry.tried_count.should == 0
495
+
496
+ review_using = Review.create :user_id => user.id, :product_id => product.id, :review_type => 'using'
497
+
498
+ industry.reload
499
+
500
+ industry.using_count.should == 1
501
+ industry.tried_count.should == 0
502
+
503
+ review_tried = Review.create :user_id => user.id, :product_id => product.id, :review_type => 'tried'
504
+
505
+ industry.reload
506
+
507
+ industry.using_count.should == 1
508
+ industry.tried_count.should == 1
509
+
510
+ review_tried.destroy
511
+
512
+ industry.reload
513
+
514
+ industry.using_count.should == 1
515
+ industry.tried_count.should == 0
516
+
517
+ review_using.destroy
518
+
519
+ industry.reload
520
+
521
+ industry.using_count.should == 0
522
+ industry.tried_count.should == 0
523
+ end
524
+
525
+ it "updates third-level custom counter cache on update" do
526
+ industry1 = Industry.create
527
+ industry2 = Industry.create
528
+ company1 = Company.create :industry_id => industry1.id
529
+ company2 = Company.create :industry_id => industry2.id
530
+ user1 = User.create :company_id => company1.id
531
+ user2 = User.create :company_id => company2.id
532
+ product = Product.create
533
+
534
+ industry1.using_count.should == 0
535
+ industry1.tried_count.should == 0
536
+ industry2.using_count.should == 0
537
+ industry2.tried_count.should == 0
538
+
539
+ review_using = Review.create :user_id => user1.id, :product_id => product.id, :review_type => 'using'
540
+
541
+ industry1.reload
542
+ industry2.reload
543
+
544
+ industry1.using_count.should == 1
545
+ industry1.tried_count.should == 0
546
+ industry2.using_count.should == 0
547
+ industry2.tried_count.should == 0
548
+
549
+ review_tried = Review.create :user_id => user1.id, :product_id => product.id, :review_type => 'tried'
550
+
551
+ industry1.reload
552
+ industry2.reload
553
+
554
+ industry1.using_count.should == 1
555
+ industry1.tried_count.should == 1
556
+ industry2.using_count.should == 0
557
+ industry2.tried_count.should == 0
558
+
559
+ review_tried.user = user2
560
+ review_tried.save!
561
+
562
+ industry1.reload
563
+ industry2.reload
564
+
565
+ industry1.using_count.should == 1
566
+ industry1.tried_count.should == 0
567
+ industry2.using_count.should == 0
568
+ industry2.tried_count.should == 1
569
+
570
+ review_using.user = user2
571
+ review_using.save!
572
+
573
+ industry1.reload
574
+ industry2.reload
575
+
576
+ industry1.using_count.should == 0
577
+ industry1.tried_count.should == 0
578
+ industry2.using_count.should == 1
579
+ industry2.tried_count.should == 1
580
+ end
581
+
582
+ it "should overwrite foreign-key values on create" do
583
+ 3.times { Category.create }
584
+ Category.all {|category| category.products_count.should == 0 }
585
+
586
+ product = Product.create :category_id => Category.first.id
587
+ Category.all {|category| category.products_count.should == 1 }
588
+ end
589
+
590
+ it "should overwrite foreign-key values on destroy" do
591
+ 3.times { Category.create }
592
+ Category.all {|category| category.products_count.should == 0 }
593
+
594
+ product = Product.create :category_id => Category.first.id
595
+ Category.all {|category| category.products_count.should == 1 }
596
+
597
+ product.destroy
598
+ Category.all {|category| category.products_count.should == 0 }
599
+ end
600
+
601
+ it "should overwrite foreign-key values on destroy" do
602
+ 3.times { Category.create }
603
+ Category.all {|category| category.products_count.should == 0 }
604
+
605
+ product = Product.create :category_id => Category.first.id
606
+ Category.all {|category| category.products_count.should == 1 }
607
+
608
+ product.category = nil
609
+ product.save!
610
+ Category.all {|category| category.products_count.should == 0 }
611
+ end
612
+
613
+
614
+ it "should fix a simple counter cache correctly" do
615
+ user = User.create
616
+ product = Product.create
617
+
618
+ user.reviews_count.should == 0
619
+ product.reviews_count.should == 0
620
+
621
+ review = Review.create :user_id => user.id, :product_id => product.id
622
+
623
+ user.reload
624
+ product.reload
625
+
626
+ user.reviews_count.should == 1
627
+ product.reviews_count.should == 1
628
+
629
+ user.reviews_count = 0
630
+ product.reviews_count = 2
631
+ user.save!
632
+ product.save!
633
+
634
+ fixed = Review.counter_culture_fix_counts :skip_unsupported => true
635
+ fixed.length.should == 2
636
+
637
+ user.reload
638
+ product.reload
639
+
640
+ user.reviews_count.should == 1
641
+ product.reviews_count.should == 1
642
+ end
643
+
644
+ it "should fix a second-level counter cache correctly" do
645
+ company = Company.create
646
+ user = User.create :company_id => company.id
647
+ product = Product.create
648
+
649
+ company.reviews_count.should == 0
650
+ user.reviews_count.should == 0
651
+ product.reviews_count.should == 0
652
+
653
+ review = Review.create :user_id => user.id, :product_id => product.id
654
+
655
+ company.reload
656
+ user.reload
657
+ product.reload
658
+
659
+ company.reviews_count.should == 1
660
+ user.reviews_count.should == 1
661
+ product.reviews_count.should == 1
662
+
663
+ company.reviews_count = 2
664
+ user.reviews_count = 3
665
+ product.reviews_count = 4
666
+ company.save!
667
+ user.save!
668
+ product.save!
669
+
670
+ Review.counter_culture_fix_counts :skip_unsupported => true
671
+ company.reload
672
+ user.reload
673
+ product.reload
674
+
675
+ company.reviews_count.should == 1
676
+ user.reviews_count.should == 1
677
+ product.reviews_count.should == 1
678
+ end
679
+
680
+ it "should fix a custom counter cache correctly" do
681
+ user = User.create
682
+ product = Product.create
683
+
684
+ product.rexiews_count.should == 0
685
+
686
+ review = Review.create :user_id => user.id, :product_id => product.id
687
+
688
+ product.reload
689
+
690
+ product.rexiews_count.should == 1
691
+
692
+ product.rexiews_count = 2
693
+ product.save!
694
+
695
+ Review.counter_culture_fix_counts :skip_unsupported => true
696
+
697
+ product.reload
698
+ product.rexiews_count.should == 1
699
+ end
700
+
701
+ it "should fix a dynamic counter cache correctly" do
702
+ user = User.create
703
+ product = Product.create
704
+
705
+ user.using_count.should == 0
706
+ user.tried_count.should == 0
707
+
708
+ review_using = Review.create :user_id => user.id, :product_id => product.id, :review_type => 'using'
709
+
710
+ user.reload
711
+
712
+ user.using_count.should == 1
713
+ user.tried_count.should == 0
714
+
715
+ review_tried = Review.create :user_id => user.id, :product_id => product.id, :review_type => 'tried'
716
+
717
+ user.reload
718
+
719
+ user.using_count.should == 1
720
+ user.tried_count.should == 1
721
+
722
+ user.using_count = 2
723
+ user.tried_count = 3
724
+ user.save!
725
+
726
+ Review.counter_culture_fix_counts :skip_unsupported => true
727
+
728
+ user.reload
729
+
730
+ user.using_count.should == 1
731
+ user.tried_count.should == 1
732
+ end
733
+
232
734
  end
@@ -0,0 +1,3 @@
1
+ class Category < ActiveRecord::Base
2
+ has_many :products
3
+ end
@@ -1,2 +1,5 @@
1
1
  class Product < ActiveRecord::Base
2
+ belongs_to :category
3
+
4
+ counter_culture :category, :foreign_key_values => Proc.new {|foreign_key_value| Category.pluck(:id) }
2
5
  end
@@ -5,10 +5,9 @@ class Review < ActiveRecord::Base
5
5
  counter_culture :product
6
6
  counter_culture :product, :column_name => 'rexiews_count'
7
7
  counter_culture :user
8
- counter_culture :user, :column_name => Proc.new { |model| "#{model.type}_count" }
8
+ counter_culture :user, :column_name => Proc.new { |model| "#{model.review_type}_count" }, :column_names => {"reviews.review_type = 'using'" => 'using_count', "reviews.review_type = 'tried'" => 'tried_count'}
9
9
  counter_culture [:user, :company]
10
- counter_culture [:user, :company], :column_name => Proc.new { |model| "#{model.type}_count" }
11
10
  counter_culture [:user, :company, :industry]
12
11
  counter_culture [:user, :company, :industry], :column_name => 'rexiews_count'
13
- counter_culture [:user, :company, :industry], :column_name => Proc.new { |model| "#{model.type}_count" }
12
+ counter_culture [:user, :company, :industry], :column_name => Proc.new { |model| "#{model.review_type}_count" }
14
13
  end
data/spec/schema.rb CHANGED
@@ -33,10 +33,11 @@ ActiveRecord::Schema.define(:version => 20120522160158) do
33
33
  t.string "name"
34
34
  t.integer "reviews_count", :default => 0, :null => false
35
35
  t.integer "rexiews_count", :default => 0, :null => false
36
+ t.integer "category_id"
36
37
  end
37
38
 
38
39
  create_table "reviews", :force => true do |t|
39
- t.string "type", :default => "using", :null => false
40
+ t.string "review_type", :default => "using", :null => false
40
41
  t.integer "user_id"
41
42
  t.integer "product_id"
42
43
  end
@@ -49,4 +50,9 @@ ActiveRecord::Schema.define(:version => 20120522160158) do
49
50
  t.integer "tried_count", :default => 0, :null => false
50
51
  end
51
52
 
53
+ create_table "categories", :force => true do |t|
54
+ t.string "name"
55
+ t.integer "products_count", :default => 0, :null => false
56
+ end
57
+
52
58
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: counter_culture
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.1.5
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-05-31 00:00:00.000000000 Z
12
+ date: 2012-06-01 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
@@ -146,6 +146,7 @@ files:
146
146
  - counter_culture.gemspec
147
147
  - lib/counter_culture.rb
148
148
  - spec/counter_culture_spec.rb
149
+ - spec/models/category.rb
149
150
  - spec/models/company.rb
150
151
  - spec/models/industry.rb
151
152
  - spec/models/product.rb
@@ -216,7 +217,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
216
217
  version: '0'
217
218
  segments:
218
219
  - 0
219
- hash: -1949241983326778340
220
+ hash: -1900291701591297555
220
221
  required_rubygems_version: !ruby/object:Gem::Requirement
221
222
  none: false
222
223
  requirements: