activerecord-redundancy 0.1.0 → 0.2.0
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 +4 -4
- data/lib/redundancy.rb +28 -54
- data/lib/redundancy/update_base.rb +88 -0
- data/lib/redundancy/update_column.rb +19 -0
- data/lib/redundancy/update_method.rb +19 -0
- data/lib/redundancy/update_prev_column.rb +19 -0
- data/lib/redundancy/update_prev_method.rb +25 -0
- data/lib/redundancy/utils.rb +102 -0
- data/lib/redundancy/version.rb +1 -1
- data/test/{belongs_to_has_one_association_test.rb → cache_column/belongs_to_has_one_association_test.rb} +5 -3
- data/test/{has_many_belongs_to_association_test.rb → cache_column/has_many_belongs_to_association_test.rb} +4 -2
- data/test/{has_one_belongs_to_association_test.rb → cache_column/has_one_belongs_to_association_test.rb} +5 -1
- data/test/{options_test.rb → cache_column/options_test.rb} +0 -0
- data/test/cache_method/has_many_belongs_to_association_test.rb +70 -0
- data/test/fixtures/posts.yml +2 -0
- data/test/fixtures/users.yml +2 -0
- data/test/support/environment.rb +3 -0
- data/test/support/models/account.rb +2 -2
- data/test/support/models/post.rb +5 -2
- data/test/support/models/user.rb +9 -1
- data/test/test_helper.rb +2 -0
- metadata +33 -12
- data/lib/redundancy/cache_column.rb +0 -59
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: faa2836ac314098d90878058bf77c1260a99decc
|
4
|
+
data.tar.gz: b6494ce4bbfdaf61a9eb799ad9daf418eeb76483
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 078c06ecc0f44628b258e97c6ca3290f0ce209ad450e5f84eb2f419e62c8c387e93fb236727e77179e35880c34b9499733e5de3e4a1ece02b954e98a37df0d02
|
7
|
+
data.tar.gz: 2f0cb3ee7453469962e52b95104450a40cae0308616447f775237dea18bf875d6a51cb923a25982d0e9a16e31c608c3af5283d62d110309f25cab0367028c4db
|
data/lib/redundancy.rb
CHANGED
@@ -1,73 +1,47 @@
|
|
1
|
-
require 'redundancy/
|
1
|
+
require 'redundancy/utils'
|
2
2
|
|
3
3
|
module Redundancy
|
4
4
|
extend ActiveSupport::Concern
|
5
5
|
|
6
6
|
included do
|
7
|
-
before_save :
|
7
|
+
before_save :update_redundacies_before_save
|
8
|
+
after_save :update_redundacies_after_save
|
8
9
|
end
|
9
10
|
|
10
11
|
private
|
11
12
|
|
12
|
-
def
|
13
|
-
self.class.
|
14
|
-
|
13
|
+
def update_redundacies_before_save
|
14
|
+
self.class.redundacies.each do |redundancy|
|
15
|
+
redundancy.before_save(self)
|
15
16
|
end
|
16
17
|
end
|
17
18
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
raise ArgumentError, "Unknown association :#{association}" unless reflection
|
24
|
-
raise ArgumentError, "BelongsTo or HasOne reflection needed" unless
|
25
|
-
[:has_one, :belongs_to].include? reflection.macro
|
26
|
-
|
27
|
-
inverse_associations = options[:inverse_of]
|
28
|
-
inverse_associations ||= [model_name.plural, model_name.singular].map(&:to_sym)
|
29
|
-
|
30
|
-
inverse_association = Array.wrap(inverse_associations).find do |inverse_association|
|
31
|
-
reflection.klass.reflect_on_association(inverse_association)
|
32
|
-
end
|
33
|
-
|
34
|
-
raise ArgumentError, "Could not find the inverse association for #{association} (#{inverse_associations.inspect} in #{reflection.klass})" unless inverse_association
|
35
|
-
|
36
|
-
foreign_key = reflection.foreign_key
|
37
|
-
cache_column = options[:cache_column] || :"#{association}_#{attribute}"
|
38
|
-
|
39
|
-
local_klass = self
|
40
|
-
remote_klass = reflection.klass
|
41
|
-
|
42
|
-
case reflection.macro
|
43
|
-
when :belongs_to
|
44
|
-
local_klass.cache_columns << CacheColumn.new({
|
45
|
-
source: { association: association, attribute: attribute },
|
46
|
-
dist: { association: nil, attribute: cache_column },
|
47
|
-
change_if: foreign_key, klass: local_klass
|
48
|
-
})
|
49
|
-
|
50
|
-
when :has_one
|
51
|
-
remote_klass.cache_columns << CacheColumn.new({
|
52
|
-
source: { association: nil, attribute: attribute },
|
53
|
-
dist: { association: inverse_association, attribute: cache_column },
|
54
|
-
change_if: foreign_key, nil_unless: foreign_key, klass: remote_klass,
|
55
|
-
set_prev_nil: { klass: local_klass, attribute: foreign_key }
|
56
|
-
})
|
57
|
-
|
58
|
-
end
|
19
|
+
def update_redundacies_after_save
|
20
|
+
self.class.redundacies.each do |redundancy|
|
21
|
+
redundancy.after_save(self)
|
22
|
+
end
|
23
|
+
end
|
59
24
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
25
|
+
def update_redundacies
|
26
|
+
self.class.redundacies.each do |redundancy|
|
27
|
+
redundancy.before_save(self)
|
28
|
+
redundancy.after_save(self)
|
29
|
+
end
|
30
|
+
end
|
65
31
|
|
32
|
+
module ClassMethods
|
33
|
+
def cache_column association, attribute, options = {}
|
34
|
+
options.assert_valid_keys(:cache_column, :inverse_of)
|
35
|
+
Utils.cache_column self, association, attribute, options
|
36
|
+
end
|
66
37
|
|
38
|
+
def cache_method association, attribute, options = {}
|
39
|
+
options.assert_valid_keys(:cache_column, :inverse_of)
|
40
|
+
Utils.cache_method self, association, attribute, options
|
67
41
|
end
|
68
42
|
|
69
|
-
def
|
70
|
-
@
|
43
|
+
def redundacies
|
44
|
+
@redundacies ||= []
|
71
45
|
end
|
72
46
|
|
73
47
|
end
|
@@ -77,4 +51,4 @@ end
|
|
77
51
|
# include in AR
|
78
52
|
ActiveSupport.on_load(:active_record) do
|
79
53
|
ActiveRecord::Base.send(:include, Redundancy)
|
80
|
-
end
|
54
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module Redundancy
|
2
|
+
|
3
|
+
class UpdateBase
|
4
|
+
attr_reader :options
|
5
|
+
attr_reader :source, :dest, :klass
|
6
|
+
attr_reader :change_if, :update, :target, :value
|
7
|
+
|
8
|
+
def initialize options
|
9
|
+
@options = options
|
10
|
+
@source, @dest = options[:source], options[:dest]
|
11
|
+
@klass = options[:klass]
|
12
|
+
|
13
|
+
@change_if = options[:change_if]
|
14
|
+
@update = options[:update] || false
|
15
|
+
end
|
16
|
+
|
17
|
+
def before_save record
|
18
|
+
end
|
19
|
+
|
20
|
+
def after_save record
|
21
|
+
end
|
22
|
+
|
23
|
+
def get_target_from_association record
|
24
|
+
@target = dest[:association] ? record.send(dest[:association]) : record
|
25
|
+
end
|
26
|
+
|
27
|
+
def get_target_from_prev_id record
|
28
|
+
prev_id = record.send(:attribute_was, dest[:prev_id])
|
29
|
+
return unless prev_id
|
30
|
+
@target = dest[:klass].where(id: prev_id)
|
31
|
+
end
|
32
|
+
|
33
|
+
def get_target_from_relation_first_record
|
34
|
+
@target = @target.first if @target.kind_of? ActiveRecord::Relation
|
35
|
+
end
|
36
|
+
|
37
|
+
def get_value_from_association record
|
38
|
+
@value = source[:association] ? record.send(source[:association]) : record
|
39
|
+
@value = value && source[:attribute] && value.send(source[:attribute])
|
40
|
+
@value = nil if source[:nil_unless] && !record.send(source[:nil_unless])
|
41
|
+
@value
|
42
|
+
end
|
43
|
+
|
44
|
+
def get_value_from_source record
|
45
|
+
@value = source
|
46
|
+
end
|
47
|
+
|
48
|
+
def get_value_from_target record
|
49
|
+
@value = target && source[:attribute] && target.send(source[:attribute])
|
50
|
+
end
|
51
|
+
|
52
|
+
def raise_if_class_mismatch record
|
53
|
+
raise ArgumentError, "record class mismatch, expected #{klass}, got #{record.class}" unless record.kind_of? klass
|
54
|
+
end
|
55
|
+
|
56
|
+
def update_target record
|
57
|
+
case target
|
58
|
+
when ActiveRecord::Base
|
59
|
+
return if target.send(:read_attribute, dest[:attribute]) == value
|
60
|
+
log "#{ update ? "update" : "write" } #{target.class}(#{target.id})##{dest[:attribute]} with #{value.inspect}"
|
61
|
+
log "#{change_if}: #{record.send(change_if).inspect}, #{dest[:association]||"self"}.id: #{target.id}" if change_if
|
62
|
+
if update
|
63
|
+
target.send(:update_attribute, dest[:attribute], value)
|
64
|
+
else
|
65
|
+
target.send(:write_attribute, dest[:attribute], value)
|
66
|
+
end
|
67
|
+
when ActiveRecord::Relation
|
68
|
+
log "update #{target.class}##{dest[:attribute]} with #{value.inspect}"
|
69
|
+
target.send(:update_all, dest[:attribute] => value)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def force_update_target record
|
74
|
+
@update = true
|
75
|
+
update_target record
|
76
|
+
end
|
77
|
+
|
78
|
+
def need_update? record
|
79
|
+
!change_if || record.send(:attribute_changed?, change_if)
|
80
|
+
end
|
81
|
+
|
82
|
+
def log *message
|
83
|
+
# puts *message
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'redundancy/update_base'
|
2
|
+
|
3
|
+
module Redundancy
|
4
|
+
|
5
|
+
class UpdateColumn < UpdateBase
|
6
|
+
|
7
|
+
def before_save record
|
8
|
+
raise_if_class_mismatch record
|
9
|
+
return unless need_update? record
|
10
|
+
|
11
|
+
get_target_from_association record
|
12
|
+
get_value_from_association record
|
13
|
+
|
14
|
+
update_target record
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'redundancy/update_base'
|
2
|
+
|
3
|
+
module Redundancy
|
4
|
+
|
5
|
+
class UpdateMethod < UpdateBase
|
6
|
+
|
7
|
+
def after_save record
|
8
|
+
raise_if_class_mismatch record
|
9
|
+
return unless need_update? record
|
10
|
+
|
11
|
+
get_target_from_association record
|
12
|
+
get_value_from_target record
|
13
|
+
|
14
|
+
force_update_target record
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'redundancy/update_base'
|
2
|
+
|
3
|
+
module Redundancy
|
4
|
+
|
5
|
+
class UpdatePrevColumn < UpdateBase
|
6
|
+
|
7
|
+
def before_save record
|
8
|
+
raise_if_class_mismatch record
|
9
|
+
return unless need_update? record
|
10
|
+
|
11
|
+
get_target_from_prev_id record
|
12
|
+
get_value_from_source record
|
13
|
+
|
14
|
+
update_target record
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'redundancy/update_base'
|
2
|
+
|
3
|
+
module Redundancy
|
4
|
+
|
5
|
+
class UpdatePrevMethod < UpdateBase
|
6
|
+
|
7
|
+
def before_save record
|
8
|
+
raise_if_class_mismatch record
|
9
|
+
return unless need_update? record
|
10
|
+
|
11
|
+
get_target_from_prev_id record
|
12
|
+
end
|
13
|
+
|
14
|
+
def after_save record
|
15
|
+
return unless need_update? record
|
16
|
+
return unless target
|
17
|
+
|
18
|
+
get_target_from_relation_first_record
|
19
|
+
get_value_from_target record
|
20
|
+
force_update_target record
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'redundancy/update_column'
|
2
|
+
require 'redundancy/update_prev_column'
|
3
|
+
require 'redundancy/update_method'
|
4
|
+
require 'redundancy/update_prev_method'
|
5
|
+
|
6
|
+
module Redundancy
|
7
|
+
|
8
|
+
module Utils
|
9
|
+
|
10
|
+
def self.cache_column klass, association, attribute, options
|
11
|
+
local_klass = klass
|
12
|
+
|
13
|
+
reflection = get_reflection local_klass, association
|
14
|
+
raise ArgumentError, "BelongsTo or HasOne reflection required" unless
|
15
|
+
[:has_one, :belongs_to].include? reflection.macro
|
16
|
+
|
17
|
+
foreign_key = reflection.foreign_key
|
18
|
+
remote_klass = reflection.klass
|
19
|
+
|
20
|
+
inverse_association = get_inverse_association local_klass, remote_klass, options
|
21
|
+
|
22
|
+
cache_column = options[:cache_column] || :"#{association}_#{attribute}"
|
23
|
+
|
24
|
+
case reflection.macro
|
25
|
+
when :belongs_to
|
26
|
+
local_klass.redundacies << UpdateColumn.new(
|
27
|
+
source: { association: association, attribute: attribute },
|
28
|
+
dest: { association: nil, attribute: cache_column },
|
29
|
+
change_if: foreign_key, klass: local_klass
|
30
|
+
)
|
31
|
+
|
32
|
+
when :has_one
|
33
|
+
remote_klass.redundacies << UpdateColumn.new(
|
34
|
+
source: { association: nil, attribute: attribute, nil_unless: foreign_key },
|
35
|
+
dest: { association: inverse_association, attribute: cache_column },
|
36
|
+
change_if: foreign_key, klass: remote_klass
|
37
|
+
)
|
38
|
+
|
39
|
+
remote_klass.redundacies << UpdatePrevColumn.new(
|
40
|
+
source: options[:default],
|
41
|
+
dest: { klass: local_klass, prev_id: foreign_key, attribute: cache_column },
|
42
|
+
change_if: foreign_key, klass: remote_klass
|
43
|
+
)
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
remote_klass.redundacies << UpdateColumn.new(
|
48
|
+
source: { association: nil, attribute: attribute },
|
49
|
+
dest: { association: inverse_association, attribute: cache_column },
|
50
|
+
change_if: attribute, klass: remote_klass, update: true
|
51
|
+
)
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.cache_method klass, association, attribute, options
|
55
|
+
local_klass = klass
|
56
|
+
|
57
|
+
reflection = get_reflection local_klass, association
|
58
|
+
raise ArgumentError, "BelongsTo reflection required" unless
|
59
|
+
[:belongs_to].include? reflection.macro
|
60
|
+
|
61
|
+
foreign_key = reflection.foreign_key
|
62
|
+
remote_klass = reflection.klass
|
63
|
+
|
64
|
+
cache_method = options[:cache_method] || :"raw_#{attribute}"
|
65
|
+
|
66
|
+
local_klass.redundacies << UpdateMethod.new(
|
67
|
+
source: { attribute: cache_method },
|
68
|
+
dest: { association: association, attribute: attribute },
|
69
|
+
change_if: options[:change_if], klass: local_klass
|
70
|
+
)
|
71
|
+
|
72
|
+
local_klass.redundacies << UpdatePrevMethod.new(
|
73
|
+
source: { attribute: cache_method },
|
74
|
+
dest: { klass: remote_klass, prev_id: foreign_key, attribute: attribute },
|
75
|
+
change_if: foreign_key, klass: local_klass
|
76
|
+
)
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.get_reflection klass, association
|
81
|
+
reflection = klass.reflect_on_association(association)
|
82
|
+
raise ArgumentError, "Unknown association :#{association}" unless reflection
|
83
|
+
reflection
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.get_inverse_association klass, reflection_klass, options
|
87
|
+
model_name = klass.model_name
|
88
|
+
inverse_associations = options[:inverse_of]
|
89
|
+
inverse_associations ||= [model_name.plural, model_name.singular].map(&:to_sym)
|
90
|
+
|
91
|
+
inverse_association = Array.wrap(inverse_associations).find do |inverse_association|
|
92
|
+
reflection_klass.reflect_on_association(inverse_association)
|
93
|
+
end
|
94
|
+
|
95
|
+
raise ArgumentError, "Could not find the inverse association for #{association} (#{inverse_associations.inspect} in #{reflection_klass})" unless inverse_association
|
96
|
+
inverse_association
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
data/lib/redundancy/version.rb
CHANGED
@@ -21,6 +21,7 @@ class BelongsToHasOneAssociationTest < ActiveSupport::TestCase
|
|
21
21
|
|
22
22
|
account.update_attribute(:user, user)
|
23
23
|
assert_equal user.name, account.user_name
|
24
|
+
assert_equal user.name, account.reload.user_name
|
24
25
|
end
|
25
26
|
|
26
27
|
test "should update account.user_name when update account.user with nil" do
|
@@ -30,6 +31,7 @@ class BelongsToHasOneAssociationTest < ActiveSupport::TestCase
|
|
30
31
|
|
31
32
|
account.update_attribute(:user, nil)
|
32
33
|
assert_equal nil, account.user_name
|
34
|
+
assert_equal nil, account.reload.user_name
|
33
35
|
end
|
34
36
|
|
35
37
|
test "should update account.user_name when update account.user with other user" do
|
@@ -42,7 +44,8 @@ class BelongsToHasOneAssociationTest < ActiveSupport::TestCase
|
|
42
44
|
|
43
45
|
account.update_attribute(:user, other_user)
|
44
46
|
assert_equal other_user.name, account.user_name
|
45
|
-
assert_equal
|
47
|
+
assert_equal other_user.name, account.reload.user_name
|
48
|
+
assert_equal nil, other_account.user
|
46
49
|
assert_equal nil, other_account.reload.user_name
|
47
50
|
end
|
48
51
|
|
@@ -53,8 +56,7 @@ class BelongsToHasOneAssociationTest < ActiveSupport::TestCase
|
|
53
56
|
|
54
57
|
user.update_attribute(:name, "Other Name")
|
55
58
|
assert_equal user.name, user.account.user_name
|
56
|
-
|
57
59
|
assert_equal user.name, account.reload.user_name
|
58
60
|
end
|
59
61
|
|
60
|
-
end
|
62
|
+
end
|
@@ -3,6 +3,7 @@ require 'test_helper'
|
|
3
3
|
class HasManyBelongsToAssociationTest < ActiveSupport::TestCase
|
4
4
|
|
5
5
|
# has_many:belongs_to association
|
6
|
+
# cache_column
|
6
7
|
test "should update post.user_name when create post" do
|
7
8
|
user = users(:one)
|
8
9
|
post = Post.create(title: 'title', content: 'content', user: user)
|
@@ -21,6 +22,7 @@ class HasManyBelongsToAssociationTest < ActiveSupport::TestCase
|
|
21
22
|
|
22
23
|
post.update_attribute(:user, user)
|
23
24
|
assert_equal user.name, post.user_name
|
25
|
+
assert_equal user.name, post.reload.user_name
|
24
26
|
end
|
25
27
|
|
26
28
|
test "should update post.user_name when update post.user with nil" do
|
@@ -30,6 +32,7 @@ class HasManyBelongsToAssociationTest < ActiveSupport::TestCase
|
|
30
32
|
|
31
33
|
post.update_attribute(:user, nil)
|
32
34
|
assert_equal nil, post.user_name
|
35
|
+
assert_equal nil, post.reload.user_name
|
33
36
|
end
|
34
37
|
|
35
38
|
test "should update post.user_name when update post.user with other user" do
|
@@ -42,6 +45,7 @@ class HasManyBelongsToAssociationTest < ActiveSupport::TestCase
|
|
42
45
|
|
43
46
|
post.update_attribute(:user, other_user)
|
44
47
|
assert_equal other_user.name, post.user_name
|
48
|
+
assert_equal other_user.name, post.reload.user_name
|
45
49
|
end
|
46
50
|
|
47
51
|
test "should update post.user_name when update user.name" do
|
@@ -53,8 +57,6 @@ class HasManyBelongsToAssociationTest < ActiveSupport::TestCase
|
|
53
57
|
user.posts.each do |post|
|
54
58
|
assert_equal user.name, post.user_name
|
55
59
|
end
|
56
|
-
|
57
60
|
assert_equal user.name, post.reload.user_name
|
58
61
|
end
|
59
|
-
|
60
62
|
end
|
@@ -21,6 +21,7 @@ class HasOneBelongsToAssociationTest < ActiveSupport::TestCase
|
|
21
21
|
|
22
22
|
user.update_attribute(:account, account)
|
23
23
|
assert_equal account.email, user.account_email
|
24
|
+
assert_equal account.email, user.reload.account_email
|
24
25
|
end
|
25
26
|
|
26
27
|
test "should update user.account_email when update user.account with nil" do
|
@@ -30,6 +31,7 @@ class HasOneBelongsToAssociationTest < ActiveSupport::TestCase
|
|
30
31
|
|
31
32
|
user.update_attribute(:account, nil)
|
32
33
|
assert_equal nil, user.account_email
|
34
|
+
assert_equal nil, user.reload.account_email
|
33
35
|
end
|
34
36
|
|
35
37
|
test "should update user.account_email when update user.account with other account" do
|
@@ -42,8 +44,10 @@ class HasOneBelongsToAssociationTest < ActiveSupport::TestCase
|
|
42
44
|
|
43
45
|
user.update_attribute(:account, other_account)
|
44
46
|
assert_equal other_account.email, user.account_email
|
47
|
+
assert_equal other_account.email, user.reload.account_email
|
45
48
|
assert_equal other_account, other_user.account
|
46
49
|
assert_equal other_account.email, other_user.account_email
|
50
|
+
assert_equal other_account.email, other_user.reload.account_email
|
47
51
|
end
|
48
52
|
|
49
53
|
test "should update user.account_email when update account.email" do
|
@@ -57,4 +61,4 @@ class HasOneBelongsToAssociationTest < ActiveSupport::TestCase
|
|
57
61
|
assert_equal account.email, user.reload.account_email
|
58
62
|
end
|
59
63
|
|
60
|
-
end
|
64
|
+
end
|
File without changes
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class HasManyBelongsToAssociationTest < ActiveSupport::TestCase
|
4
|
+
|
5
|
+
# has_many:belongs_to association
|
6
|
+
# cache_method
|
7
|
+
test "should update user.posts_count when create post" do
|
8
|
+
user = users(:one)
|
9
|
+
post = Post.create(title: 'title', content: 'content', user: user)
|
10
|
+
assert_equal user.raw_posts_count, user.posts_count
|
11
|
+
end
|
12
|
+
|
13
|
+
test "should update user.posts_star when create post" do
|
14
|
+
user = users(:one)
|
15
|
+
post = Post.create(title: 'title', content: 'content', user: user, star: 5)
|
16
|
+
assert_equal user.raw_posts_star, user.posts_star
|
17
|
+
end
|
18
|
+
|
19
|
+
test "should update user.posts_star when create post without star" do
|
20
|
+
user = users(:one)
|
21
|
+
post = Post.create(title: 'title', content: 'content', user: user)
|
22
|
+
assert_equal user.raw_posts_star, user.posts_star
|
23
|
+
end
|
24
|
+
|
25
|
+
test "should update nothing when create post without user" do
|
26
|
+
post = Post.create(title: 'title', content: 'content')
|
27
|
+
end
|
28
|
+
|
29
|
+
test "should update user.posts_star when update post.user" do
|
30
|
+
user = users(:one)
|
31
|
+
post = posts(:two)
|
32
|
+
assert_equal user.raw_posts_star, user.posts_star
|
33
|
+
|
34
|
+
post.update_attribute(:user, user)
|
35
|
+
assert_equal user.raw_posts_star, user.reload.posts_star
|
36
|
+
end
|
37
|
+
|
38
|
+
test "should update user.posts_star when update post.user with nil" do
|
39
|
+
user = users(:one)
|
40
|
+
post = posts(:one)
|
41
|
+
assert_equal user.raw_posts_star, user.posts_star
|
42
|
+
|
43
|
+
post.update_attribute(:user, nil)
|
44
|
+
assert_equal user.raw_posts_star, user.reload.posts_star
|
45
|
+
end
|
46
|
+
|
47
|
+
test "should update user.posts_star when update post.user with other user" do
|
48
|
+
user = users(:one)
|
49
|
+
other_user = users(:two)
|
50
|
+
post = posts(:one)
|
51
|
+
other_post = posts(:two)
|
52
|
+
assert_equal user.raw_posts_star, user.posts_star
|
53
|
+
assert_equal other_user.raw_posts_star, other_user.posts_star
|
54
|
+
|
55
|
+
post.update_attribute(:user, other_user)
|
56
|
+
assert_equal user.raw_posts_star, user.reload.posts_star
|
57
|
+
assert_equal other_user.raw_posts_star, other_user.reload.posts_star
|
58
|
+
end
|
59
|
+
|
60
|
+
test "should update user.posts_star when update post.star" do
|
61
|
+
user = users(:one)
|
62
|
+
post = posts(:one)
|
63
|
+
assert_equal user.raw_posts_star, user.posts_star
|
64
|
+
|
65
|
+
post.update_attribute(:star, 5)
|
66
|
+
assert_equal user.raw_posts_star, post.user.posts_star
|
67
|
+
assert_equal user.raw_posts_star, user.reload.posts_star
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
data/test/fixtures/posts.yml
CHANGED
data/test/fixtures/users.yml
CHANGED
data/test/support/environment.rb
CHANGED
@@ -22,6 +22,7 @@ ActiveRecord::Schema.define do
|
|
22
22
|
t.integer "user_id"
|
23
23
|
t.string "user_name"
|
24
24
|
t.string "username"
|
25
|
+
t.integer "star"
|
25
26
|
t.string "title"
|
26
27
|
t.text "content"
|
27
28
|
t.datetime "created_at"
|
@@ -33,6 +34,8 @@ ActiveRecord::Schema.define do
|
|
33
34
|
create_table "users", force: true do |t|
|
34
35
|
t.integer "account_id"
|
35
36
|
t.string "account_email"
|
37
|
+
t.integer "posts_count"
|
38
|
+
t.integer "posts_star"
|
36
39
|
t.string "name"
|
37
40
|
t.datetime "created_at"
|
38
41
|
t.datetime "updated_at"
|
data/test/support/models/post.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
class Post < ActiveRecord::Base
|
2
2
|
belongs_to :user
|
3
3
|
|
4
|
-
|
5
|
-
|
4
|
+
cache_column :user, :name
|
5
|
+
cache_column :user, :name, cache_column: :username
|
6
|
+
|
7
|
+
cache_method :user, :posts_count
|
8
|
+
cache_method :user, :posts_star
|
6
9
|
end
|
data/test/support/models/user.rb
CHANGED
data/test/test_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord-redundancy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Theo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-12-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -58,6 +58,20 @@ dependencies:
|
|
58
58
|
- - ">="
|
59
59
|
- !ruby/object:Gem::Version
|
60
60
|
version: '0'
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: pry-stack_explorer
|
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'
|
61
75
|
- !ruby/object:Gem::Dependency
|
62
76
|
name: rails
|
63
77
|
requirement: !ruby/object:Gem::Requirement
|
@@ -83,15 +97,21 @@ files:
|
|
83
97
|
- Rakefile
|
84
98
|
- lib/activerecord-redundancy.rb
|
85
99
|
- lib/redundancy.rb
|
86
|
-
- lib/redundancy/
|
100
|
+
- lib/redundancy/update_base.rb
|
101
|
+
- lib/redundancy/update_column.rb
|
102
|
+
- lib/redundancy/update_method.rb
|
103
|
+
- lib/redundancy/update_prev_column.rb
|
104
|
+
- lib/redundancy/update_prev_method.rb
|
105
|
+
- lib/redundancy/utils.rb
|
87
106
|
- lib/redundancy/version.rb
|
88
|
-
- test/belongs_to_has_one_association_test.rb
|
107
|
+
- test/cache_column/belongs_to_has_one_association_test.rb
|
108
|
+
- test/cache_column/has_many_belongs_to_association_test.rb
|
109
|
+
- test/cache_column/has_one_belongs_to_association_test.rb
|
110
|
+
- test/cache_column/options_test.rb
|
111
|
+
- test/cache_method/has_many_belongs_to_association_test.rb
|
89
112
|
- test/fixtures/accounts.yml
|
90
113
|
- test/fixtures/posts.yml
|
91
114
|
- test/fixtures/users.yml
|
92
|
-
- test/has_many_belongs_to_association_test.rb
|
93
|
-
- test/has_one_belongs_to_association_test.rb
|
94
|
-
- test/options_test.rb
|
95
115
|
- test/support/environment.rb
|
96
116
|
- test/support/models/account.rb
|
97
117
|
- test/support/models/post.rb
|
@@ -117,18 +137,19 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
117
137
|
version: '0'
|
118
138
|
requirements: []
|
119
139
|
rubyforge_project:
|
120
|
-
rubygems_version: 2.
|
140
|
+
rubygems_version: 2.4.6
|
121
141
|
signing_key:
|
122
142
|
specification_version: 4
|
123
143
|
summary: Redundancy for better performance, non painful
|
124
144
|
test_files:
|
125
|
-
- test/belongs_to_has_one_association_test.rb
|
145
|
+
- test/cache_column/belongs_to_has_one_association_test.rb
|
146
|
+
- test/cache_column/has_many_belongs_to_association_test.rb
|
147
|
+
- test/cache_column/has_one_belongs_to_association_test.rb
|
148
|
+
- test/cache_column/options_test.rb
|
149
|
+
- test/cache_method/has_many_belongs_to_association_test.rb
|
126
150
|
- test/fixtures/accounts.yml
|
127
151
|
- test/fixtures/posts.yml
|
128
152
|
- test/fixtures/users.yml
|
129
|
-
- test/has_many_belongs_to_association_test.rb
|
130
|
-
- test/has_one_belongs_to_association_test.rb
|
131
|
-
- test/options_test.rb
|
132
153
|
- test/support/environment.rb
|
133
154
|
- test/support/models/account.rb
|
134
155
|
- test/support/models/post.rb
|
@@ -1,59 +0,0 @@
|
|
1
|
-
module Redundancy
|
2
|
-
|
3
|
-
class CacheColumn
|
4
|
-
attr_reader :options
|
5
|
-
attr_reader :source, :dist, :klass
|
6
|
-
attr_reader :change_if, :nil_unless, :update, :set_prev_nil
|
7
|
-
|
8
|
-
def initialize options
|
9
|
-
@options = options
|
10
|
-
@source, @dist = options[:source], options[:dist]
|
11
|
-
@klass = options[:klass]
|
12
|
-
|
13
|
-
@change_if = options[:change_if]
|
14
|
-
@nil_unless = options[:nil_unless]
|
15
|
-
@update = options[:update] || false
|
16
|
-
@set_prev_nil = options[:set_prev_nil]
|
17
|
-
end
|
18
|
-
|
19
|
-
def update_record record
|
20
|
-
raise ArgumentError, "record class mismatch, expected #{klass}, got #{record.class}" unless record.kind_of? klass
|
21
|
-
return unless need_update?(record)
|
22
|
-
|
23
|
-
src = source[:association] ? record.send(source[:association]) : record
|
24
|
-
src = src && source[:attribute] && src.send(source[:attribute])
|
25
|
-
src = nil if nil_unless && !record.send(nil_unless)
|
26
|
-
|
27
|
-
dst = dist[:association] ? record.send(dist[:association]) : record
|
28
|
-
|
29
|
-
set_prev_nil[:klass].where(id: record.send(:attribute_was, set_prev_nil[:attribute]))
|
30
|
-
.update_all(dist[:attribute] => nil) if set_prev_nil
|
31
|
-
|
32
|
-
case dst
|
33
|
-
when ActiveRecord::Base
|
34
|
-
return if dst.send(:read_attribute, dist[:attribute]) == src
|
35
|
-
log "#{ update ? "update" : "write" } #{dst.class}(#{dst.id})##{dist[:attribute]} with #{src.inspect}"
|
36
|
-
log "#{change_if}: #{record.send(change_if).inspect}, #{dist[:association]||"self"}.id: #{dst.id}"
|
37
|
-
if update
|
38
|
-
dst.send(:update_attribute, dist[:attribute], src)
|
39
|
-
else
|
40
|
-
dst.send(:write_attribute, dist[:attribute], src)
|
41
|
-
end
|
42
|
-
when ActiveRecord::Relation
|
43
|
-
log "update #{dst.class}##{dist[:attribute]} with #{src.inspect}"
|
44
|
-
dst.send(:update_all, dist[:attribute] => src)
|
45
|
-
end
|
46
|
-
|
47
|
-
end
|
48
|
-
|
49
|
-
def need_update? record
|
50
|
-
record.send(:attribute_changed?, change_if)
|
51
|
-
end
|
52
|
-
|
53
|
-
def log *message
|
54
|
-
# puts *message
|
55
|
-
end
|
56
|
-
|
57
|
-
end
|
58
|
-
|
59
|
-
end
|