counter_cache_with_conditions 0.9.2
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 +7 -0
- data/.gitignore +8 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +22 -0
- data/README.md +36 -0
- data/Rakefile +14 -0
- data/counter_cache_with_conditions.gemspec +19 -0
- data/lib/counter_cache_with_conditions.rb +3 -0
- data/lib/counter_cache_with_conditions/active_record.rb +100 -0
- data/test/active_record_counter_cache_test.rb +73 -0
- data/test/counter_cache_with_hash_conditions_test.rb +23 -0
- data/test/counter_cache_with_lambda_conditions_test.rb +25 -0
- data/test/database.yml +5 -0
- data/test/schema.rb +15 -0
- data/test/test_helper.rb +17 -0
- data/test/unread_messages_count_tests.rb +239 -0
- metadata +81 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9c4e95d3320ffd49431b25befe2d0aad3ef178b9
|
4
|
+
data.tar.gz: a6e2b5e5b8ff04731cdc1f18297517da4198c52e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d3eb25c1cc41a0cde506c5b8b35360a21101c733a2363010df54b188550c95447d335f637facbc6e9210f65d0b652ca1cabbe3fc6999c2f4781237b2ce426a16
|
7
|
+
data.tar.gz: 5b6f62df86215a04dbfe85d5f6c93e08fb5b6163b965aca7c4fc192994c98852eb69e268580285a4e5a981b7b9735c1ff9eb4856951d1cec6c8ef9c76b944023
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Sergey Kojin
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# Counter Cache With Conditions
|
2
|
+
|
3
|
+
Replacement for ActiveRecord belongs_to :counter_cache with ability to specify conditions.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'counter_cache_with_conditions'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install counter_cache_with_conditions
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
When additional counter cache need
|
22
|
+
|
23
|
+
belongs_to :folder, :counter_cache => true
|
24
|
+
counter_cache_with_conditions :folder, :unread_messages_count, :unread => true
|
25
|
+
|
26
|
+
Or as replacement for rails build in solution.
|
27
|
+
|
28
|
+
belongs_to :folder
|
29
|
+
counter_cache_with_conditions :folder, :messages_count, {}
|
30
|
+
counter_cache_with_conditions :folder, :unread_messages_count, :unread => true
|
31
|
+
counter_cache_with_conditions :folder, :unread_messages_count, [:read, :source], lambda{|read, source| read == false && source == 'message'}
|
32
|
+
counter_cache_with_conditions :folder, :published_events_count, [:published_at], lambda{|published_at| published_at != nil }
|
33
|
+
|
34
|
+
|
35
|
+
|
36
|
+
Copyright (c) 2010 Sergey Kojin, released under the MIT license
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
|
3
|
+
require "bundler/gem_tasks"
|
4
|
+
require "rake/testtask"
|
5
|
+
|
6
|
+
desc "Default: run unit tests."
|
7
|
+
task :default => :test
|
8
|
+
|
9
|
+
desc "Test the acts_as_archival plugin."
|
10
|
+
Rake::TestTask.new(:test) do |t|
|
11
|
+
t.libs << "test"
|
12
|
+
t.pattern = "test/**/*_test.rb"
|
13
|
+
t.verbose = true
|
14
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = "counter_cache_with_conditions"
|
5
|
+
s.version = "0.9.2"
|
6
|
+
s.authors = ["Sergey Kojin"]
|
7
|
+
s.email = ["sergey.kojin@gmail.com"]
|
8
|
+
s.description = %q{Replacement for ActiveRecord belongs_to :counter_cache with ability to specify conditions.}
|
9
|
+
s.summary = %q{ActiveRecord counter_cache with conditions.}
|
10
|
+
s.homepage = "https://github.com/skojin/counter_cache_with_conditions"
|
11
|
+
s.license = "MIT"
|
12
|
+
|
13
|
+
s.files = `git ls-files`.split($/)
|
14
|
+
s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
15
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
|
18
|
+
s.add_dependency "activerecord", '>= 3.2'
|
19
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
module CounterCacheWithConditions
|
2
|
+
module ActiveRecord
|
3
|
+
|
4
|
+
# @param association_name extended belongs_to association name (like :user)
|
5
|
+
# @param counter_name name of counter cache column
|
6
|
+
# @param conditions hash with equal conditions, like {:read => false, :source => 'message'}, no nesting
|
7
|
+
# @param conditions array with checked attributes names, in this case block is required.
|
8
|
+
# usage looks like [:read, :source], lambda{|read, source| read == false && source == 'message'}
|
9
|
+
# @param block proc object that take list of arguments specified in conditions parameter, should compare them and return boolean
|
10
|
+
def counter_cache_with_conditions(association_name, counter_name, conditions, block = nil)
|
11
|
+
unless ancestors.include? InstanceMethods
|
12
|
+
include InstanceMethods
|
13
|
+
after_create :counter_cache_with_conditions_after_create
|
14
|
+
before_update :counter_cache_with_conditions_before_update
|
15
|
+
before_destroy :counter_cache_with_conditions_before_destroy
|
16
|
+
|
17
|
+
cattr_accessor :counter_cache_with_conditions_options
|
18
|
+
self.counter_cache_with_conditions_options = []
|
19
|
+
end
|
20
|
+
|
21
|
+
# TODO make readonly
|
22
|
+
ref = reflect_on_association(association_name)
|
23
|
+
ref.klass.send(:attr_readonly, counter_name.to_sym) if ref.klass.respond_to?(:attr_readonly)
|
24
|
+
conditions = [conditions, block] if conditions.is_a? Array
|
25
|
+
self.counter_cache_with_conditions_options << [ref.klass, ref.foreign_key, counter_name, conditions]
|
26
|
+
end
|
27
|
+
|
28
|
+
module InstanceMethods
|
29
|
+
private
|
30
|
+
def counter_cache_with_conditions_after_create
|
31
|
+
self.counter_cache_with_conditions_options.each do |klass, foreign_key, counter_name, conditions|
|
32
|
+
if counter_conditions_match?(conditions)
|
33
|
+
association_id = send(foreign_key)
|
34
|
+
klass.increment_counter(counter_name, association_id) if association_id
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def counter_cache_with_conditions_before_update
|
40
|
+
self.counter_cache_with_conditions_options.each do |klass, foreign_key, counter_name, conditions|
|
41
|
+
match_before = counter_conditions_without_changes_match?(conditions)
|
42
|
+
match_now = counter_conditions_match?(conditions)
|
43
|
+
if match_now && !match_before
|
44
|
+
ccwc_update_counter_on(klass, foreign_key, counter_name, 1, 0)
|
45
|
+
elsif !match_now && match_before && send("#{foreign_key}_changed?")
|
46
|
+
# decrement only old, if association changed and condition broken
|
47
|
+
ccwc_update_counter_on(klass, foreign_key, counter_name, 0, -1)
|
48
|
+
elsif !match_now && match_before
|
49
|
+
ccwc_update_counter_on(klass, foreign_key, counter_name, -1, -1)
|
50
|
+
elsif match_now && send("#{foreign_key}_changed?")
|
51
|
+
# if just association changed, decrement old, increment new
|
52
|
+
ccwc_update_counter_on(klass, foreign_key, counter_name, 1, -1)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def counter_cache_with_conditions_before_destroy
|
58
|
+
self.counter_cache_with_conditions_options.each do |klass, foreign_key, counter_name, conditions|
|
59
|
+
if counter_conditions_without_changes_match?(conditions)
|
60
|
+
association_was = attribute_was(foreign_key.to_s)
|
61
|
+
klass.decrement_counter(counter_name, association_was) if association_was
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
def counter_conditions_match?(conditions)
|
68
|
+
if conditions.is_a? Array
|
69
|
+
attr_names, block = conditions
|
70
|
+
block.call(*attr_names.map { |attr| send(attr) })
|
71
|
+
else
|
72
|
+
conditions.all? { |attr, value| send(attr) == value }
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def counter_conditions_without_changes_match?(conditions)
|
77
|
+
if conditions.is_a? Array
|
78
|
+
attr_names, block = conditions
|
79
|
+
block.call(*attr_names.map { |attr| attribute_was(attr.to_s) })
|
80
|
+
else
|
81
|
+
conditions.all? { |attr, value| attribute_was(attr.to_s) == value }
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# e.g. increment counter on association, and decrement it on old association if association was changed, and vice versa
|
86
|
+
# @param value (+1, -1)
|
87
|
+
# @param value_was value for old association (if association was changed)
|
88
|
+
def ccwc_update_counter_on(klass, foreign_key, counter_name, value, value_was = 0)
|
89
|
+
association_id = send(foreign_key)
|
90
|
+
klass.update_counters(association_id, counter_name => value) if association_id
|
91
|
+
if value_was != 0
|
92
|
+
association_was = attribute_was(foreign_key.to_s)
|
93
|
+
klass.update_counters(association_was, counter_name => value_was) if association_was && association_was != association_id
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
|
+
|
3
|
+
class ActiveRecordCounterCacheTest < Test::Unit::TestCase
|
4
|
+
load_schema
|
5
|
+
|
6
|
+
class Folder < ActiveRecord::Base
|
7
|
+
has_many :messages
|
8
|
+
end
|
9
|
+
|
10
|
+
class Message < ActiveRecord::Base
|
11
|
+
# uncomment this, and comment out lines below, too see active record counter cache error
|
12
|
+
#belongs_to :folder, :counter_cache => true
|
13
|
+
|
14
|
+
belongs_to :folder
|
15
|
+
counter_cache_with_conditions :folder, :messages_count, {}
|
16
|
+
end
|
17
|
+
|
18
|
+
def teardown
|
19
|
+
Message.delete_all
|
20
|
+
Folder.delete_all
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_schema_has_loaded_correctly
|
24
|
+
assert_equal [], Folder.all
|
25
|
+
assert_equal [], Message.all
|
26
|
+
end
|
27
|
+
|
28
|
+
# default rails counter tests
|
29
|
+
def test_default_counter_cache_should_increment_on_create
|
30
|
+
f, _ = build_fixture
|
31
|
+
assert_equal 1, f.reload.messages_count
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_default_counter_cache_should_decrement_on_destroy
|
35
|
+
f, m = build_fixture
|
36
|
+
assert_equal 1, f.reload.messages_count
|
37
|
+
m.destroy
|
38
|
+
assert_equal 0, f.reload.messages_count
|
39
|
+
end
|
40
|
+
|
41
|
+
# active record counter cache fail on this
|
42
|
+
def test_default_counter_cache_not_update_counters_when_change_association_but_not_save
|
43
|
+
f1, m = build_fixture
|
44
|
+
assert_equal 1, f1.reload.messages_count
|
45
|
+
f2 = Folder.create
|
46
|
+
assert_equal 0, f2.reload.messages_count
|
47
|
+
m.folder = f2
|
48
|
+
assert_equal f1, m.reload.folder, "folder not changed"
|
49
|
+
assert_equal 1, f1.reload.messages_count
|
50
|
+
assert_equal 0, f2.reload.messages_count
|
51
|
+
end
|
52
|
+
|
53
|
+
# active record counter cache fail on this
|
54
|
+
def fail_test_default_counter_cache_update_counters_when_change_association_via_id
|
55
|
+
f1, m = build_fixture
|
56
|
+
assert_equal 1, f1.reload.messages_count
|
57
|
+
f2 = Folder.create
|
58
|
+
assert_equal 0, f2.reload.messages_count
|
59
|
+
m.folder_id = f2.id
|
60
|
+
m.save
|
61
|
+
assert_equal 0, f1.reload.messages_count
|
62
|
+
assert_equal 1, f2.reload.messages_count
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
def build_fixture(attributes = {})
|
67
|
+
f = Folder.create
|
68
|
+
m = f.messages.create attributes
|
69
|
+
[f, m]
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
|
4
|
+
class CounterCacheWithHashConditionsTest < Test::Unit::TestCase
|
5
|
+
load_schema
|
6
|
+
class Folder < ActiveRecord::Base
|
7
|
+
has_many :messages
|
8
|
+
end
|
9
|
+
|
10
|
+
class Message < ActiveRecord::Base
|
11
|
+
belongs_to :folder, :counter_cache => true
|
12
|
+
counter_cache_with_conditions :folder, :unread_messages_count, :unread => true
|
13
|
+
end
|
14
|
+
|
15
|
+
def teardown
|
16
|
+
Message.delete_all
|
17
|
+
Folder.delete_all
|
18
|
+
end
|
19
|
+
|
20
|
+
eval IO.read(File.dirname(__FILE__) + '/unread_messages_count_tests.rb')
|
21
|
+
|
22
|
+
end
|
23
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
require File.dirname(__FILE__) + '/unread_messages_count_tests'
|
3
|
+
|
4
|
+
class CounterCacheWithLambdaConditionsTest < Test::Unit::TestCase
|
5
|
+
load_schema
|
6
|
+
|
7
|
+
class Folder < ActiveRecord::Base
|
8
|
+
has_many :messages
|
9
|
+
end
|
10
|
+
|
11
|
+
class Message < ActiveRecord::Base
|
12
|
+
belongs_to :folder, :counter_cache => true
|
13
|
+
counter_cache_with_conditions :folder, :unread_messages_count, [:unread], lambda{|unread| unread == true}
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
def teardown
|
18
|
+
Message.delete_all
|
19
|
+
Folder.delete_all
|
20
|
+
end
|
21
|
+
|
22
|
+
eval IO.read(File.dirname(__FILE__) + '/unread_messages_count_tests.rb')
|
23
|
+
|
24
|
+
end
|
25
|
+
|
data/test/database.yml
ADDED
data/test/schema.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
ActiveRecord::Schema.define do
|
2
|
+
|
3
|
+
create_table "folders", :force => true do |t|
|
4
|
+
t.integer "messages_count", :null => false, :default => 0
|
5
|
+
t.integer "unread_messages_count", :null => false, :default => 0
|
6
|
+
t.datetime "created_at"
|
7
|
+
t.datetime "updated_at"
|
8
|
+
end
|
9
|
+
|
10
|
+
create_table "messages", :force => true do |t|
|
11
|
+
t.belongs_to :folder
|
12
|
+
t.boolean "unread", :null => false, :default => true
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
2
|
+
require "bundler/setup"
|
3
|
+
require "minitest/autorun"
|
4
|
+
|
5
|
+
require "active_record"
|
6
|
+
|
7
|
+
require 'test/unit'
|
8
|
+
require File.dirname(__FILE__) + '/../init.rb'
|
9
|
+
# ::ActiveRecord::Base.extend(ActiveRecord)
|
10
|
+
|
11
|
+
def load_schema
|
12
|
+
conf = YAML::load(File.open(File.dirname(__FILE__) + '/database.yml'))
|
13
|
+
# ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log")
|
14
|
+
|
15
|
+
ActiveRecord::Base.establish_connection(conf['sqlite3'])
|
16
|
+
load(File.dirname(__FILE__) + "/schema.rb")
|
17
|
+
end
|
@@ -0,0 +1,239 @@
|
|
1
|
+
# default rails counter tests
|
2
|
+
def test_default_counter_cache_should_increment_on_create
|
3
|
+
f, _ = build_fixture
|
4
|
+
assert_equal 1, f.reload.messages_count
|
5
|
+
end
|
6
|
+
|
7
|
+
def test_default_counter_cache_should_decrement_on_destroy
|
8
|
+
f, m = build_fixture
|
9
|
+
assert_equal 1, f.reload.messages_count
|
10
|
+
m.destroy
|
11
|
+
assert_equal 0, f.reload.messages_count
|
12
|
+
end
|
13
|
+
|
14
|
+
# custom counter tests
|
15
|
+
|
16
|
+
def test_should_increment_counter_on_create_when_condition_match
|
17
|
+
f, _ = build_fixture(:unread => true)
|
18
|
+
assert_equal 1, f.reload.unread_messages_count
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_should_not_increment_counter_on_create_when_condition_not_match
|
22
|
+
f, _ = build_fixture(:unread => false)
|
23
|
+
assert_equal 0, f.reload.unread_messages_count
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_should_decrement_counter_on_destroy_when_condition_match
|
27
|
+
f, m = build_fixture(:unread => true)
|
28
|
+
m.destroy
|
29
|
+
assert_equal 0, f.reload.unread_messages_count
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_should_not_decrement_counter_on_destroy_when_condition_not_match
|
33
|
+
f, m = build_fixture(:unread => false)
|
34
|
+
m.destroy
|
35
|
+
assert_equal 0, f.reload.unread_messages_count
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_should_decrement_counter_when_unread_unset_just_before_destroy
|
39
|
+
f, m = build_fixture(:unread => true)
|
40
|
+
m.unread = false
|
41
|
+
m.destroy
|
42
|
+
assert_equal 0, f.reload.unread_messages_count
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_should_not_decrement_counter_when_unread_set_just_before_destroy
|
46
|
+
f, m = build_fixture(:unread => false)
|
47
|
+
m.unread = true
|
48
|
+
m.destroy
|
49
|
+
assert_equal 0, f.reload.unread_messages_count
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_should_increment_counter_when_unread_set_on_update
|
53
|
+
f, m = build_fixture(:unread => false)
|
54
|
+
assert_equal 0, f.reload.unread_messages_count
|
55
|
+
m.update_attribute :unread, true
|
56
|
+
assert_equal 1, f.reload.unread_messages_count
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_should_decrement_counter_when_unread_unset_on_update
|
60
|
+
f, m = build_fixture(:unread => true)
|
61
|
+
m.unread = false
|
62
|
+
m.save!
|
63
|
+
assert_equal 0, f.reload.unread_messages_count
|
64
|
+
|
65
|
+
f, m = build_fixture(:unread => true)
|
66
|
+
m.unread = true
|
67
|
+
m.save!
|
68
|
+
assert_equal 1, f.reload.unread_messages_count
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_should_not_change_counter_when_unread_set_to_same_value_on_update
|
72
|
+
f, m = build_fixture(:unread => true)
|
73
|
+
assert_equal 1, f.reload.unread_messages_count
|
74
|
+
m.unread = true
|
75
|
+
m.save!
|
76
|
+
assert_equal 1, f.reload.unread_messages_count
|
77
|
+
end
|
78
|
+
|
79
|
+
# ---------- when association nillified ------------
|
80
|
+
|
81
|
+
def test_should_change_counter_when_unset_folder_for_unread
|
82
|
+
f, m = build_fixture(:unread => true)
|
83
|
+
m.folder = nil
|
84
|
+
m.save!
|
85
|
+
assert_equal 0, f.reload.unread_messages_count
|
86
|
+
end
|
87
|
+
def test_should_change_counter_when_unset_folder_id_for_unread
|
88
|
+
f, m = build_fixture(:unread => true)
|
89
|
+
m.folder_id = nil
|
90
|
+
m.save!
|
91
|
+
assert_equal 0, f.reload.unread_messages_count
|
92
|
+
end
|
93
|
+
def test_should_change_counter_when_unset_folder_and_unset_unread
|
94
|
+
f, m = build_fixture(:unread => true)
|
95
|
+
m.folder = nil
|
96
|
+
m.unread = false
|
97
|
+
m.save!
|
98
|
+
assert_equal 0, f.reload.unread_messages_count
|
99
|
+
end
|
100
|
+
|
101
|
+
def test_should_not_change_counter_when_unset_folder_id_for_read
|
102
|
+
f, m = build_fixture(:unread => false)
|
103
|
+
m.folder_id = nil
|
104
|
+
m.save!
|
105
|
+
assert_equal 0, f.reload.unread_messages_count
|
106
|
+
end
|
107
|
+
|
108
|
+
def test_should_not_change_counter_when_unset_folder_set_unread
|
109
|
+
f, m = build_fixture(:unread => false)
|
110
|
+
m.folder = nil
|
111
|
+
m.unread = true
|
112
|
+
m.save!
|
113
|
+
assert_equal 0, f.reload.unread_messages_count
|
114
|
+
end
|
115
|
+
|
116
|
+
# ---------- when association changed ------------
|
117
|
+
|
118
|
+
def test_should_not_change_counters_when_change_folder_for_read
|
119
|
+
f, m = build_fixture(:unread => false)
|
120
|
+
f2 = Folder.create!
|
121
|
+
m.folder = f2
|
122
|
+
m.save!
|
123
|
+
assert_equal 0, f.reload.unread_messages_count
|
124
|
+
assert_equal 0, f2.reload.unread_messages_count
|
125
|
+
end
|
126
|
+
|
127
|
+
def test_should_change_counter_when_change_folder_for_unread
|
128
|
+
f, m = build_fixture(:unread => true)
|
129
|
+
f2 = Folder.create!
|
130
|
+
m.folder = f2
|
131
|
+
m.save!
|
132
|
+
assert_equal 0, f.reload.unread_messages_count
|
133
|
+
assert_equal 1, f2.reload.unread_messages_count
|
134
|
+
end
|
135
|
+
|
136
|
+
# this test is important, it fail in process
|
137
|
+
def test_should_change_counter_on_old_and_skip_counter_on_new_when_change_folder_for_unread_with_status_change
|
138
|
+
f, m = build_fixture(:unread => true)
|
139
|
+
f2 = Folder.create!
|
140
|
+
m.folder = f2
|
141
|
+
m.unread = false
|
142
|
+
m.save!
|
143
|
+
assert_equal 0, f.reload.unread_messages_count
|
144
|
+
assert_equal 0, f2.reload.unread_messages_count
|
145
|
+
end
|
146
|
+
|
147
|
+
def test_should_ignore_counter_on_old_and_icrement_counter_on_new_when_change_folder_for_read_with_status_change
|
148
|
+
f, m = build_fixture(:unread => false)
|
149
|
+
f2 = Folder.create!
|
150
|
+
m.folder = f2
|
151
|
+
m.unread = true
|
152
|
+
m.save!
|
153
|
+
assert_equal 0, f.reload.unread_messages_count
|
154
|
+
assert_equal 1, f2.reload.unread_messages_count
|
155
|
+
end
|
156
|
+
|
157
|
+
# ---------- when association was nil ------------
|
158
|
+
|
159
|
+
def test_should_icrement_counter_when_assing_folder_for_unread
|
160
|
+
m = Message.create!(:unread => true)
|
161
|
+
f = Folder.create!
|
162
|
+
m.folder = f
|
163
|
+
m.save!
|
164
|
+
assert_equal 1, f.reload.unread_messages_count
|
165
|
+
end
|
166
|
+
|
167
|
+
def test_should_ignore_counter_when_assing_folder_for_read
|
168
|
+
m = Message.create!(:unread => false)
|
169
|
+
f = Folder.create!
|
170
|
+
m.folder = f
|
171
|
+
m.save!
|
172
|
+
assert_equal 0, f.reload.unread_messages_count
|
173
|
+
end
|
174
|
+
|
175
|
+
def test_should_ignore_counter_when_assing_folder_for_unread_and_mark_as_read
|
176
|
+
m = Message.create!(:unread => true)
|
177
|
+
f = Folder.create!
|
178
|
+
m.folder = f
|
179
|
+
m.unread = false
|
180
|
+
m.save!
|
181
|
+
assert_equal 0, f.reload.unread_messages_count
|
182
|
+
end
|
183
|
+
|
184
|
+
def test_should_increment_counter_when_assing_folder_for_read_and_mark_as_unread
|
185
|
+
m = Message.create!(:unread => false)
|
186
|
+
f = Folder.create!
|
187
|
+
m.folder = f
|
188
|
+
m.unread = true
|
189
|
+
m.save!
|
190
|
+
assert_equal 1, f.reload.unread_messages_count
|
191
|
+
end
|
192
|
+
|
193
|
+
|
194
|
+
# ---------- when association changed before destroy ------------
|
195
|
+
|
196
|
+
def test_should_decrement_counter_when_association_unset_just_before_destroy_when_match
|
197
|
+
f, m = build_fixture(:unread => true)
|
198
|
+
m.folder = nil
|
199
|
+
m.destroy
|
200
|
+
assert_equal 0, f.reload.unread_messages_count
|
201
|
+
|
202
|
+
# just to be sure, dublicate test with attribute change
|
203
|
+
f, m = build_fixture(:unread => true)
|
204
|
+
m.folder = nil
|
205
|
+
m.unread = false
|
206
|
+
m.destroy
|
207
|
+
assert_equal 0, f.reload.unread_messages_count
|
208
|
+
end
|
209
|
+
|
210
|
+
def test_should_not_decrement_counter_when_association_unset_just_before_destroy_when_not_match
|
211
|
+
f, m = build_fixture(:unread => false)
|
212
|
+
m.folder = nil
|
213
|
+
m.unread = true
|
214
|
+
m.destroy
|
215
|
+
assert_equal 0, f.reload.unread_messages_count
|
216
|
+
|
217
|
+
# just to be sure, dublicate test with attribute change
|
218
|
+
f, m = build_fixture(:unread => false)
|
219
|
+
m.folder = nil
|
220
|
+
m.unread = true
|
221
|
+
m.destroy
|
222
|
+
assert_equal 0, f.reload.unread_messages_count
|
223
|
+
end
|
224
|
+
|
225
|
+
def test_should_decrement_counter_when_association_changed_just_before_destroy_when_match
|
226
|
+
f, m = build_fixture(:unread => true)
|
227
|
+
f2 = Folder.create!
|
228
|
+
m.folder = f2
|
229
|
+
m.destroy
|
230
|
+
assert_equal 0, f.reload.unread_messages_count
|
231
|
+
assert_equal 0, f2.reload.unread_messages_count
|
232
|
+
end
|
233
|
+
|
234
|
+
private
|
235
|
+
def build_fixture(attributes = {})
|
236
|
+
f = Folder.create
|
237
|
+
m = f.messages.create attributes
|
238
|
+
[f, m]
|
239
|
+
end
|
metadata
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: counter_cache_with_conditions
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.9.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Sergey Kojin
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-03-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activerecord
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.2'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3.2'
|
27
|
+
description: Replacement for ActiveRecord belongs_to :counter_cache with ability to
|
28
|
+
specify conditions.
|
29
|
+
email:
|
30
|
+
- sergey.kojin@gmail.com
|
31
|
+
executables: []
|
32
|
+
extensions: []
|
33
|
+
extra_rdoc_files: []
|
34
|
+
files:
|
35
|
+
- .gitignore
|
36
|
+
- Gemfile
|
37
|
+
- LICENSE.txt
|
38
|
+
- README.md
|
39
|
+
- Rakefile
|
40
|
+
- counter_cache_with_conditions.gemspec
|
41
|
+
- lib/counter_cache_with_conditions.rb
|
42
|
+
- lib/counter_cache_with_conditions/active_record.rb
|
43
|
+
- test/active_record_counter_cache_test.rb
|
44
|
+
- test/counter_cache_with_hash_conditions_test.rb
|
45
|
+
- test/counter_cache_with_lambda_conditions_test.rb
|
46
|
+
- test/database.yml
|
47
|
+
- test/schema.rb
|
48
|
+
- test/test_helper.rb
|
49
|
+
- test/unread_messages_count_tests.rb
|
50
|
+
homepage: https://github.com/skojin/counter_cache_with_conditions
|
51
|
+
licenses:
|
52
|
+
- MIT
|
53
|
+
metadata: {}
|
54
|
+
post_install_message:
|
55
|
+
rdoc_options: []
|
56
|
+
require_paths:
|
57
|
+
- lib
|
58
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - '>='
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
68
|
+
requirements: []
|
69
|
+
rubyforge_project:
|
70
|
+
rubygems_version: 2.2.1
|
71
|
+
signing_key:
|
72
|
+
specification_version: 4
|
73
|
+
summary: ActiveRecord counter_cache with conditions.
|
74
|
+
test_files:
|
75
|
+
- test/active_record_counter_cache_test.rb
|
76
|
+
- test/counter_cache_with_hash_conditions_test.rb
|
77
|
+
- test/counter_cache_with_lambda_conditions_test.rb
|
78
|
+
- test/database.yml
|
79
|
+
- test/schema.rb
|
80
|
+
- test/test_helper.rb
|
81
|
+
- test/unread_messages_count_tests.rb
|