conditional_counter_cache 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1a7dd773fc250091226da0a2a5efdedc39b8da51
4
+ data.tar.gz: 1d9d5bcc9e6712100f44ef3894243c77bd67a12c
5
+ SHA512:
6
+ metadata.gz: f6fc68c2c9700a7456d1f3de057010645f390b8ae4a9e608b6d4809ac84128f4d5788faad1d44f101d5b3aa274ad2987d0274c748ad3a6c73e0e3957d4938547
7
+ data.tar.gz: 484034592c1b600f73ef671592ea2211c97da011451a872f4007890812c920c0e7ac2e09f7730b115615be484745752e23bae9810d0d5c88e9d562678cee2071
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in conditional_counter_cache.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Ryo Nakamura
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,22 @@
1
+ # ConditionalCounterCache
2
+ Allows you to customize condition of counter cache.
3
+
4
+ ## Usage
5
+ Customize condition via `:counter_cache` option:
6
+
7
+ ```ruby
8
+ class Tagging < ActiveRecord::Base
9
+ belongs_to :item
10
+ belongs_to :tag, counter_cache: { condition: -> { !tagging.item.private? } }
11
+ end
12
+ ```
13
+
14
+ Other examples:
15
+
16
+ ```ruby
17
+ belongs_to :tag, counter_cache: true
18
+ belongs_to :tag, counter_cache: "items_count"
19
+ belongs_to :tag, counter_cache: { condition: -> { !tagging.item.private? } }
20
+ belongs_to :tag, counter_cache: { column_name: "items_count" }
21
+ belongs_to :tag, counter_cache: { column_name: "items_count", condition: -> { !tagging.item.private? } }
22
+ ```
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'conditional_counter_cache/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "conditional_counter_cache"
8
+ spec.version = ConditionalCounterCache::VERSION
9
+ spec.authors = ["Ryo Nakamura"]
10
+ spec.email = ["r7kamura@gmail.com"]
11
+ spec.summary = "Allows you to customize condition of counter cache."
12
+ spec.homepage = "https://github.com/r7kamura/conditional_counter_cache"
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_dependency "activerecord", ">= 4.0.0", "< 4.2.0"
21
+ spec.add_dependency "activesupport"
22
+ spec.add_development_dependency "bundler", "~> 1.7"
23
+ spec.add_development_dependency "rails"
24
+ spec.add_development_dependency "rake", "~> 10.0"
25
+ spec.add_development_dependency "rspec", "3.0.2"
26
+ spec.add_development_dependency "rspec-rails"
27
+ end
@@ -0,0 +1,4 @@
1
+ ActiveSupport.on_load(:active_record) do
2
+ ActiveRecord::Reflection::AssociationReflection.prepend(ConditionalCounterCache::Reflection)
3
+ ActiveRecord::Associations::Builder::BelongsTo.singleton_class.prepend(ConditionalCounterCache::BelongsTo)
4
+ end
@@ -0,0 +1,52 @@
1
+ module ConditionalCounterCache
2
+ # Overrides ActiveRecord 4.1.4's counter cache implementation.
3
+ module BelongsTo
4
+ def add_counter_cache_methods(mixin)
5
+ return if mixin.method_defined? :belongs_to_counter_cache_after_create
6
+
7
+ mixin.class_eval do
8
+ def belongs_to_counter_cache_after_create(reflection)
9
+ if record = send(reflection.name)
10
+ return unless reflection.has_countable?(self)
11
+ cache_column = reflection.counter_cache_column
12
+ record.class.increment_counter(cache_column, record.id)
13
+ @_after_create_counter_called = true
14
+ end
15
+ end
16
+
17
+ def belongs_to_counter_cache_before_destroy(reflection)
18
+ foreign_key = reflection.foreign_key.to_sym
19
+ unless destroyed_by_association && destroyed_by_association.foreign_key.to_sym == foreign_key
20
+ record = send reflection.name
21
+ if record && !self.destroyed?
22
+ return unless reflection.has_countable?(self)
23
+ cache_column = reflection.counter_cache_column
24
+ record.class.decrement_counter(cache_column, record.id)
25
+ end
26
+ end
27
+ end
28
+
29
+ def belongs_to_counter_cache_after_update(reflection)
30
+ foreign_key = reflection.foreign_key
31
+ cache_column = reflection.counter_cache_column
32
+
33
+ if (@_after_create_counter_called ||= false)
34
+ @_after_create_counter_called = false
35
+ elsif attribute_changed?(foreign_key) && !new_record? && reflection.constructable?
36
+ return unless reflection.has_countable?(self)
37
+ model = reflection.klass
38
+ foreign_key_was = attribute_was foreign_key
39
+ foreign_key = attribute foreign_key
40
+
41
+ if foreign_key && model.respond_to?(:increment_counter)
42
+ model.increment_counter(cache_column, foreign_key)
43
+ end
44
+ if foreign_key_was && model.respond_to?(:decrement_counter)
45
+ model.decrement_counter(cache_column, foreign_key_was)
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,73 @@
1
+ module ConditionalCounterCache
2
+ module Reflection
3
+ # @note Overridden
4
+ def counter_cache_column
5
+ Option.new(options[:counter_cache]).column_name ||
6
+ "#{active_record.name.demodulize.underscore.pluralize}_count"
7
+ end
8
+
9
+ # @return [true, false]
10
+ def has_countable?(record)
11
+ Option.new(options[:counter_cache]).condition.call(record)
12
+ end
13
+
14
+ # Utility wrapper of `option[:counter_cache][:condition]`.
15
+ class Condition
16
+ # @param [Proc, String, Symbol, nil]
17
+ def initialize(value)
18
+ @value = value
19
+ end
20
+
21
+ def call(record)
22
+ case @value
23
+ when Proc
24
+ record.instance_exec(&@value)
25
+ when nil
26
+ true
27
+ else
28
+ record.send(@value)
29
+ end
30
+ end
31
+ end
32
+
33
+ # Utility wrapper of `option[:counter_cache]`.
34
+ class Option
35
+ # @param [Hash, String, true, nil]
36
+ def initialize(value)
37
+ @value = value
38
+ end
39
+
40
+ # @return [String, nil] Specified column name, or nil meaning "use default column name".
41
+ def column_name
42
+ case
43
+ when has_hash_value?
44
+ @value[:column_name]
45
+ when has_true_value?
46
+ nil
47
+ else
48
+ @value.to_s
49
+ end
50
+ end
51
+
52
+ # @return [Condition]
53
+ def condition
54
+ Condition.new(raw_condition)
55
+ end
56
+
57
+ private
58
+
59
+ def has_hash_value?
60
+ @value.is_a?(Hash)
61
+ end
62
+
63
+ def has_true_value?
64
+ @value == true
65
+ end
66
+
67
+ # @return [Proc, String, Symbol, nil]
68
+ def raw_condition
69
+ @value[:condition] if has_hash_value?
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,3 @@
1
+ module ConditionalCounterCache
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,6 @@
1
+ require "active_record"
2
+
3
+ require "conditional_counter_cache/belongs_to"
4
+ require "conditional_counter_cache/reflection"
5
+ require "conditional_counter_cache/version"
6
+ require "conditional_counter_cache/active_record"
metadata ADDED
@@ -0,0 +1,160 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: conditional_counter_cache
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Ryo Nakamura
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-10-07 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: 4.0.0
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: 4.2.0
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: 4.0.0
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: 4.2.0
33
+ - !ruby/object:Gem::Dependency
34
+ name: activesupport
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: bundler
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '1.7'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '1.7'
61
+ - !ruby/object:Gem::Dependency
62
+ name: rails
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ - !ruby/object:Gem::Dependency
76
+ name: rake
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '10.0'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '10.0'
89
+ - !ruby/object:Gem::Dependency
90
+ name: rspec
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - '='
94
+ - !ruby/object:Gem::Version
95
+ version: 3.0.2
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - '='
101
+ - !ruby/object:Gem::Version
102
+ version: 3.0.2
103
+ - !ruby/object:Gem::Dependency
104
+ name: rspec-rails
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ description:
118
+ email:
119
+ - r7kamura@gmail.com
120
+ executables: []
121
+ extensions: []
122
+ extra_rdoc_files: []
123
+ files:
124
+ - ".gitignore"
125
+ - Gemfile
126
+ - LICENSE.txt
127
+ - README.md
128
+ - Rakefile
129
+ - conditional_counter_cache.gemspec
130
+ - lib/conditional_counter_cache.rb
131
+ - lib/conditional_counter_cache/active_record.rb
132
+ - lib/conditional_counter_cache/belongs_to.rb
133
+ - lib/conditional_counter_cache/reflection.rb
134
+ - lib/conditional_counter_cache/version.rb
135
+ homepage: https://github.com/r7kamura/conditional_counter_cache
136
+ licenses:
137
+ - MIT
138
+ metadata: {}
139
+ post_install_message:
140
+ rdoc_options: []
141
+ require_paths:
142
+ - lib
143
+ required_ruby_version: !ruby/object:Gem::Requirement
144
+ requirements:
145
+ - - ">="
146
+ - !ruby/object:Gem::Version
147
+ version: '0'
148
+ required_rubygems_version: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ requirements: []
154
+ rubyforge_project:
155
+ rubygems_version: 2.2.2
156
+ signing_key:
157
+ specification_version: 4
158
+ summary: Allows you to customize condition of counter cache.
159
+ test_files: []
160
+ has_rdoc: