twrk-socialization 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.ruby-version +1 -0
- data/.travis.yml +8 -0
- data/Appraisals +23 -0
- data/CHANGELOG.md +67 -0
- data/Gemfile +3 -0
- data/Guardfile +12 -0
- data/LICENSE +20 -0
- data/README.md +298 -0
- data/Rakefile +14 -0
- data/demo/.gitignore +15 -0
- data/demo/Gemfile +35 -0
- data/demo/README.rdoc +261 -0
- data/demo/Rakefile +7 -0
- data/demo/app/assets/images/rails.png +0 -0
- data/demo/app/assets/javascripts/application.js +15 -0
- data/demo/app/assets/stylesheets/application.css +13 -0
- data/demo/app/controllers/application_controller.rb +3 -0
- data/demo/app/helpers/application_helper.rb +2 -0
- data/demo/app/mailers/.gitkeep +0 -0
- data/demo/app/models/.gitkeep +0 -0
- data/demo/app/models/celebrity.rb +4 -0
- data/demo/app/models/comment.rb +5 -0
- data/demo/app/models/follow.rb +3 -0
- data/demo/app/models/like.rb +2 -0
- data/demo/app/models/mention.rb +2 -0
- data/demo/app/models/movie.rb +3 -0
- data/demo/app/models/user.rb +7 -0
- data/demo/app/views/layouts/application.html.erb +14 -0
- data/demo/config/application.rb +59 -0
- data/demo/config/boot.rb +6 -0
- data/demo/config/database.yml +25 -0
- data/demo/config/environment.rb +5 -0
- data/demo/config/environments/development.rb +37 -0
- data/demo/config/environments/production.rb +67 -0
- data/demo/config/environments/test.rb +37 -0
- data/demo/config/initializers/backtrace_silencers.rb +7 -0
- data/demo/config/initializers/inflections.rb +15 -0
- data/demo/config/initializers/mime_types.rb +5 -0
- data/demo/config/initializers/secret_token.rb +7 -0
- data/demo/config/initializers/session_store.rb +8 -0
- data/demo/config/initializers/wrap_parameters.rb +14 -0
- data/demo/config/locales/en.yml +5 -0
- data/demo/config/routes.rb +58 -0
- data/demo/config.ru +4 -0
- data/demo/db/migrate/20120115051222_create_users.rb +9 -0
- data/demo/db/migrate/20120115051234_create_movies.rb +9 -0
- data/demo/db/migrate/20120115051255_create_celebrities.rb +9 -0
- data/demo/db/migrate/20120115054646_create_follows.rb +14 -0
- data/demo/db/migrate/20120115054647_create_likes.rb +14 -0
- data/demo/db/migrate/20120221200644_create_mentions.rb +14 -0
- data/demo/db/migrate/20120221202703_create_comments.rb +9 -0
- data/demo/db/schema.rb +72 -0
- data/demo/db/seeds.rb +17 -0
- data/demo/lib/assets/.gitkeep +0 -0
- data/demo/lib/tasks/.gitkeep +0 -0
- data/demo/public/404.html +26 -0
- data/demo/public/422.html +26 -0
- data/demo/public/500.html +25 -0
- data/demo/public/favicon.ico +0 -0
- data/demo/public/index.html +204 -0
- data/demo/public/robots.txt +5 -0
- data/demo/script/rails +6 -0
- data/demo/test/fixtures/.gitkeep +0 -0
- data/demo/test/fixtures/celebrities.yml +7 -0
- data/demo/test/fixtures/movies.yml +7 -0
- data/demo/test/fixtures/users.yml +7 -0
- data/demo/test/functional/.gitkeep +0 -0
- data/demo/test/integration/.gitkeep +0 -0
- data/demo/test/performance/browsing_test.rb +12 -0
- data/demo/test/test_helper.rb +13 -0
- data/demo/test/unit/.gitkeep +0 -0
- data/demo/test/unit/celebrity_test.rb +7 -0
- data/demo/test/unit/movie_test.rb +7 -0
- data/demo/test/unit/user_test.rb +7 -0
- data/demo/vendor/assets/javascripts/.gitkeep +0 -0
- data/demo/vendor/assets/stylesheets/.gitkeep +0 -0
- data/demo/vendor/plugins/.gitkeep +0 -0
- data/init.rb +1 -0
- data/lib/generators/socialization/socialization_generator.rb +38 -0
- data/lib/generators/socialization/templates/active_record/migration_follows.rb +14 -0
- data/lib/generators/socialization/templates/active_record/migration_likes.rb +14 -0
- data/lib/generators/socialization/templates/active_record/migration_mentions.rb +14 -0
- data/lib/generators/socialization/templates/active_record/model_follow.rb +2 -0
- data/lib/generators/socialization/templates/active_record/model_like.rb +2 -0
- data/lib/generators/socialization/templates/active_record/model_mention.rb +2 -0
- data/lib/generators/socialization/templates/redis/model_follow.rb +2 -0
- data/lib/generators/socialization/templates/redis/model_like.rb +2 -0
- data/lib/generators/socialization/templates/redis/model_mention.rb +2 -0
- data/lib/socialization/actors/follower.rb +88 -0
- data/lib/socialization/actors/liker.rb +88 -0
- data/lib/socialization/actors/mentioner.rb +88 -0
- data/lib/socialization/config/config.rb +39 -0
- data/lib/socialization/helpers/acts_as_helpers.rb +39 -0
- data/lib/socialization/helpers/string.rb +17 -0
- data/lib/socialization/lib/exceptions.rb +3 -0
- data/lib/socialization/stores/active_record/follow.rb +125 -0
- data/lib/socialization/stores/active_record/like.rb +121 -0
- data/lib/socialization/stores/active_record/mention.rb +125 -0
- data/lib/socialization/stores/active_record/mixins/base.rb +12 -0
- data/lib/socialization/stores/mixins/base.rb +22 -0
- data/lib/socialization/stores/mixins/follow.rb +39 -0
- data/lib/socialization/stores/mixins/like.rb +40 -0
- data/lib/socialization/stores/mixins/mention.rb +40 -0
- data/lib/socialization/stores/redis/base.rb +125 -0
- data/lib/socialization/stores/redis/config.rb +11 -0
- data/lib/socialization/stores/redis/follow.rb +22 -0
- data/lib/socialization/stores/redis/like.rb +22 -0
- data/lib/socialization/stores/redis/mention.rb +22 -0
- data/lib/socialization/stores/redis/mixins/base.rb +8 -0
- data/lib/socialization/version.rb +3 -0
- data/lib/socialization/victims/followable.rb +50 -0
- data/lib/socialization/victims/likeable.rb +51 -0
- data/lib/socialization/victims/mentionable.rb +51 -0
- data/lib/socialization.rb +20 -0
- data/socialization.gemspec +32 -0
- data/spec/actors/follower_spec.rb +113 -0
- data/spec/actors/liker_spec.rb +105 -0
- data/spec/actors/mentioner_spec.rb +105 -0
- data/spec/spec_helper.rb +21 -0
- data/spec/spec_support/data_stores.rb +232 -0
- data/spec/spec_support/matchers.rb +54 -0
- data/spec/stores/active_record/follow_store_spec.rb +147 -0
- data/spec/stores/active_record/like_store_spec.rb +146 -0
- data/spec/stores/active_record/mention_store_spec.rb +148 -0
- data/spec/stores/active_record/mixins/base_spec.rb +25 -0
- data/spec/stores/redis/base_spec.rb +195 -0
- data/spec/stores/redis/config_spec.rb +28 -0
- data/spec/stores/redis/follow_store_spec.rb +26 -0
- data/spec/stores/redis/like_store_spec.rb +23 -0
- data/spec/stores/redis/mention_store_spec.rb +23 -0
- data/spec/string_spec.rb +13 -0
- data/spec/victims/followable_spec.rb +59 -0
- data/spec/victims/likeable_spec.rb +58 -0
- data/spec/victims/mentionable_spec.rb +59 -0
- data/spec/world_spec.rb +107 -0
- metadata +322 -0
@@ -0,0 +1,121 @@
|
|
1
|
+
module Socialization
|
2
|
+
module ActiveRecordStores
|
3
|
+
class Like < ActiveRecord::Base
|
4
|
+
extend Socialization::Stores::Mixins::Base
|
5
|
+
extend Socialization::Stores::Mixins::Like
|
6
|
+
extend Socialization::ActiveRecordStores::Mixins::Base
|
7
|
+
|
8
|
+
belongs_to :liker, :polymorphic => true
|
9
|
+
belongs_to :likeable, :polymorphic => true
|
10
|
+
|
11
|
+
scope :liked_by, lambda { |liker| where(
|
12
|
+
:liker_type => liker.class.name.classify,
|
13
|
+
:liker_id => liker.id)
|
14
|
+
}
|
15
|
+
|
16
|
+
scope :liking, lambda { |likeable| where(
|
17
|
+
:likeable_id => likeable.id)
|
18
|
+
}
|
19
|
+
|
20
|
+
class << self
|
21
|
+
def like!(liker, likeable)
|
22
|
+
unless likes?(liker, likeable)
|
23
|
+
self.create! do |like|
|
24
|
+
like.liker = liker
|
25
|
+
like.likeable = likeable
|
26
|
+
end
|
27
|
+
update_counter(liker, likees_count: +1)
|
28
|
+
update_counter(likeable, likers_count: +1)
|
29
|
+
call_after_create_hooks(liker, likeable)
|
30
|
+
true
|
31
|
+
else
|
32
|
+
false
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def unlike!(liker, likeable)
|
37
|
+
if likes?(liker, likeable)
|
38
|
+
like_for(liker, likeable).destroy_all
|
39
|
+
update_counter(liker, likees_count: -1)
|
40
|
+
update_counter(likeable, likers_count: -1)
|
41
|
+
call_after_destroy_hooks(liker, likeable)
|
42
|
+
true
|
43
|
+
else
|
44
|
+
false
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def likes?(liker, likeable)
|
49
|
+
!like_for(liker, likeable).empty?
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns an ActiveRecord::Relation of all the likers of a certain type that are liking likeable
|
53
|
+
def likers_relation(likeable, klass, opts = {})
|
54
|
+
rel = klass.where(:id =>
|
55
|
+
self.select(:liker_id).
|
56
|
+
where(:liker_type => klass.name.classify).
|
57
|
+
where(:likeable_id => likeable.id)
|
58
|
+
)
|
59
|
+
|
60
|
+
if opts[:pluck]
|
61
|
+
rel.pluck(opts[:pluck])
|
62
|
+
else
|
63
|
+
rel
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Returns all the likers of a certain type that are liking likeable
|
68
|
+
def likers(likeable, klass, opts = {})
|
69
|
+
rel = likers_relation(likeable, klass, opts)
|
70
|
+
if rel.is_a?(ActiveRecord::Relation)
|
71
|
+
rel.to_a
|
72
|
+
else
|
73
|
+
rel
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Returns an ActiveRecord::Relation of all the likeables of a certain type that are liked by liker
|
78
|
+
def likeables_relation(liker, klass, opts = {})
|
79
|
+
rel = klass.where(:id =>
|
80
|
+
self.select(:likeable_id).
|
81
|
+
where(:liker_type => liker.class.to_s).
|
82
|
+
where(:liker_id => liker.id)
|
83
|
+
)
|
84
|
+
|
85
|
+
if opts[:pluck]
|
86
|
+
rel.pluck(opts[:pluck])
|
87
|
+
else
|
88
|
+
rel
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Returns all the likeables of a certain type that are liked by liker
|
93
|
+
def likeables(liker, klass, opts = {})
|
94
|
+
rel = likeables_relation(liker, klass, opts)
|
95
|
+
if rel.is_a?(ActiveRecord::Relation)
|
96
|
+
rel.to_a
|
97
|
+
else
|
98
|
+
rel
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Remove all the likers for likeable
|
103
|
+
def remove_likers(likeable)
|
104
|
+
self.where(:likeable_id => likeable.id).destroy_all
|
105
|
+
end
|
106
|
+
|
107
|
+
# Remove all the likeables for liker
|
108
|
+
def remove_likeables(liker)
|
109
|
+
self.where(:liker_type => liker.class.name.classify).
|
110
|
+
where(:liker_id => liker.id).destroy_all
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
def like_for(liker, likeable)
|
115
|
+
liked_by(liker).liking( likeable)
|
116
|
+
end
|
117
|
+
end # class << self
|
118
|
+
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
module Socialization
|
2
|
+
module ActiveRecordStores
|
3
|
+
class Mention < ActiveRecord::Base
|
4
|
+
extend Socialization::Stores::Mixins::Base
|
5
|
+
extend Socialization::Stores::Mixins::Mention
|
6
|
+
extend Socialization::ActiveRecordStores::Mixins::Base
|
7
|
+
|
8
|
+
belongs_to :mentioner, :polymorphic => true
|
9
|
+
belongs_to :mentionable, :polymorphic => true
|
10
|
+
|
11
|
+
scope :mentioned_by, lambda { |mentioner| where(
|
12
|
+
:mentioner_type => mentioner.class.table_name.classify,
|
13
|
+
:mentioner_id => mentioner.id)
|
14
|
+
}
|
15
|
+
|
16
|
+
scope :mentioning, lambda { |mentionable| where(
|
17
|
+
:mentionable_type => mentionable.class.table_name.classify,
|
18
|
+
:mentionable_id => mentionable.id)
|
19
|
+
}
|
20
|
+
|
21
|
+
class << self
|
22
|
+
def mention!(mentioner, mentionable)
|
23
|
+
unless mentions?(mentioner, mentionable)
|
24
|
+
self.create! do |mention|
|
25
|
+
mention.mentioner = mentioner
|
26
|
+
mention.mentionable = mentionable
|
27
|
+
end
|
28
|
+
update_counter(mentioner, mentionees_count: +1)
|
29
|
+
update_counter(mentionable, mentioners_count: +1)
|
30
|
+
call_after_create_hooks(mentioner, mentionable)
|
31
|
+
true
|
32
|
+
else
|
33
|
+
false
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def unmention!(mentioner, mentionable)
|
38
|
+
if mentions?(mentioner, mentionable)
|
39
|
+
mention_for(mentioner, mentionable).destroy_all
|
40
|
+
update_counter(mentioner, mentionees_count: -1)
|
41
|
+
update_counter(mentionable, mentioners_count: -1)
|
42
|
+
call_after_destroy_hooks(mentioner, mentionable)
|
43
|
+
true
|
44
|
+
else
|
45
|
+
false
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def mentions?(mentioner, mentionable)
|
50
|
+
!mention_for(mentioner, mentionable).empty?
|
51
|
+
end
|
52
|
+
|
53
|
+
# Returns an ActiveRecord::Relation of all the mentioners of a certain type that are mentioning mentionable
|
54
|
+
def mentioners_relation(mentionable, klass, opts = {})
|
55
|
+
rel = klass.where(:id =>
|
56
|
+
self.select(:mentioner_id).
|
57
|
+
where(:mentioner_type => klass.table_name.classify).
|
58
|
+
where(:mentionable_type => mentionable.class.to_s).
|
59
|
+
where(:mentionable_id => mentionable.id)
|
60
|
+
)
|
61
|
+
|
62
|
+
if opts[:pluck]
|
63
|
+
rel.pluck(opts[:pluck])
|
64
|
+
else
|
65
|
+
rel
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Returns all the mentioners of a certain type that are mentioning mentionable
|
70
|
+
def mentioners(mentionable, klass, opts = {})
|
71
|
+
rel = mentioners_relation(mentionable, klass, opts)
|
72
|
+
if rel.is_a?(ActiveRecord::Relation)
|
73
|
+
rel.to_a
|
74
|
+
else
|
75
|
+
rel
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Returns an ActiveRecord::Relation of all the mentionables of a certain type that are mentioned by mentioner
|
80
|
+
def mentionables_relation(mentioner, klass, opts = {})
|
81
|
+
rel = klass.where(:id =>
|
82
|
+
self.select(:mentionable_id).
|
83
|
+
where(:mentionable_type => klass.table_name.classify).
|
84
|
+
where(:mentioner_type => mentioner.class.to_s).
|
85
|
+
where(:mentioner_id => mentioner.id)
|
86
|
+
)
|
87
|
+
|
88
|
+
if opts[:pluck]
|
89
|
+
rel.pluck(opts[:pluck])
|
90
|
+
else
|
91
|
+
rel
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Returns all the mentionables of a certain type that are mentioned by mentioner
|
96
|
+
def mentionables(mentioner, klass, opts = {})
|
97
|
+
rel = mentionables_relation(mentioner, klass, opts)
|
98
|
+
if rel.is_a?(ActiveRecord::Relation)
|
99
|
+
rel.to_a
|
100
|
+
else
|
101
|
+
rel
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Remove all the mentioners for mentionable
|
106
|
+
def remove_mentioners(mentionable)
|
107
|
+
self.where(:mentionable_type => mentionable.class.name.classify).
|
108
|
+
where(:mentionable_id => mentionable.id).destroy_all
|
109
|
+
end
|
110
|
+
|
111
|
+
# Remove all the mentionables for mentioner
|
112
|
+
def remove_mentionables(mentioner)
|
113
|
+
self.where(:mentioner_type => mentioner.class.name.classify).
|
114
|
+
where(:mentioner_id => mentioner.id).destroy_all
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
def mention_for(mentioner, mentionable)
|
119
|
+
mentioned_by(mentioner).mentioning(mentionable)
|
120
|
+
end
|
121
|
+
end # class << self
|
122
|
+
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Socialization
|
2
|
+
module ActiveRecordStores
|
3
|
+
module Mixins
|
4
|
+
module Base
|
5
|
+
def update_counter(model, counter)
|
6
|
+
column_name, _ = counter.first
|
7
|
+
model.class.update_counters model.id, counter if model.respond_to?(column_name)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Socialization
|
2
|
+
module Stores
|
3
|
+
module Mixins
|
4
|
+
module Base
|
5
|
+
def touch_dependents(actor, victim)
|
6
|
+
actor.touch if touch_actor?(actor)
|
7
|
+
victim.touch if touch_victim?(victim)
|
8
|
+
end
|
9
|
+
|
10
|
+
def touch_actor?(actor)
|
11
|
+
return false unless actor.respond_to?(:touch)
|
12
|
+
touch == :all || touch.to_s =~ /er$/i
|
13
|
+
end
|
14
|
+
|
15
|
+
def touch_victim?(victim)
|
16
|
+
return false unless victim.respond_to?(:touch)
|
17
|
+
touch == :all || touch.to_s =~ /able$/i
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Socialization
|
2
|
+
module Stores
|
3
|
+
module Mixins
|
4
|
+
module Follow
|
5
|
+
|
6
|
+
public
|
7
|
+
def touch(what = nil)
|
8
|
+
if what.nil?
|
9
|
+
@touch || false
|
10
|
+
else
|
11
|
+
raise Socialization::ArgumentError unless [:all, :follower, :followable, false, nil].include?(what)
|
12
|
+
@touch = what
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def after_follow(method)
|
17
|
+
raise Socialization::ArgumentError unless method.is_a?(Symbol) || method.nil?
|
18
|
+
@after_create_hook = method
|
19
|
+
end
|
20
|
+
|
21
|
+
def after_unfollow(method)
|
22
|
+
raise Socialization::ArgumentError unless method.is_a?(Symbol) || method.nil?
|
23
|
+
@after_destroy_hook = method
|
24
|
+
end
|
25
|
+
|
26
|
+
protected
|
27
|
+
def call_after_create_hooks(follower, followable)
|
28
|
+
self.send(@after_create_hook, follower, followable) if @after_create_hook
|
29
|
+
touch_dependents(follower, followable)
|
30
|
+
end
|
31
|
+
|
32
|
+
def call_after_destroy_hooks(follower, followable)
|
33
|
+
self.send(@after_destroy_hook, follower, followable) if @after_destroy_hook
|
34
|
+
touch_dependents(follower, followable)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Socialization
|
2
|
+
module Stores
|
3
|
+
module Mixins
|
4
|
+
module Like
|
5
|
+
|
6
|
+
public
|
7
|
+
def touch(what = nil)
|
8
|
+
if what.nil?
|
9
|
+
@touch || false
|
10
|
+
else
|
11
|
+
raise Socialization::ArgumentError unless [:all, :liker, :likeable, false, nil].include?(what)
|
12
|
+
@touch = what
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def after_like(method)
|
17
|
+
raise Socialization::ArgumentError unless method.is_a?(Symbol) || method.nil?
|
18
|
+
@after_create_hook = method
|
19
|
+
end
|
20
|
+
|
21
|
+
def after_unlike(method)
|
22
|
+
raise Socialization::ArgumentError unless method.is_a?(Symbol) || method.nil?
|
23
|
+
@after_destroy_hook = method
|
24
|
+
end
|
25
|
+
|
26
|
+
protected
|
27
|
+
def call_after_create_hooks(liker, likeable)
|
28
|
+
self.send(@after_create_hook, liker, likeable) if @after_create_hook
|
29
|
+
touch_dependents(liker, likeable)
|
30
|
+
end
|
31
|
+
|
32
|
+
def call_after_destroy_hooks(liker, likeable)
|
33
|
+
self.send(@after_destroy_hook, liker, likeable) if @after_destroy_hook
|
34
|
+
touch_dependents(liker, likeable)
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Socialization
|
2
|
+
module Stores
|
3
|
+
module Mixins
|
4
|
+
module Mention
|
5
|
+
|
6
|
+
public
|
7
|
+
def touch(what = nil)
|
8
|
+
if what.nil?
|
9
|
+
@touch || false
|
10
|
+
else
|
11
|
+
raise Socialization::ArgumentError unless [:all, :mentioner, :mentionable, false, nil].include?(what)
|
12
|
+
@touch = what
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def after_mention(method)
|
17
|
+
raise Socialization::ArgumentError unless method.is_a?(Symbol) || method.nil?
|
18
|
+
@after_create_hook = method
|
19
|
+
end
|
20
|
+
|
21
|
+
def after_unmention(method)
|
22
|
+
raise Socialization::ArgumentError unless method.is_a?(Symbol) || method.nil?
|
23
|
+
@after_destroy_hook = method
|
24
|
+
end
|
25
|
+
|
26
|
+
protected
|
27
|
+
def call_after_create_hooks(mentioner, mentionable)
|
28
|
+
self.send(@after_create_hook, mentioner, mentionable) if @after_create_hook
|
29
|
+
touch_dependents(mentioner, mentionable)
|
30
|
+
end
|
31
|
+
|
32
|
+
def call_after_destroy_hooks(mentioner, mentionable)
|
33
|
+
self.send(@after_destroy_hook, mentioner, mentionable) if @after_destroy_hook
|
34
|
+
touch_dependents(mentioner, mentionable)
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
module Socialization
|
2
|
+
module RedisStores
|
3
|
+
class Base
|
4
|
+
|
5
|
+
class << self
|
6
|
+
protected
|
7
|
+
def actors(victim, klass, options = {})
|
8
|
+
if options[:pluck]
|
9
|
+
Socialization.redis.smembers(generate_forward_key(victim)).inject([]) do |result, element|
|
10
|
+
result << element.match(/\:(\d+)$/)[1] if element.match(/^#{klass}\:/)
|
11
|
+
result
|
12
|
+
end
|
13
|
+
else
|
14
|
+
actors_relation(victim, klass, options).to_a
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def actors_relation(victim, klass, options = {})
|
19
|
+
ids = actors(victim, klass, :pluck => :id)
|
20
|
+
klass.where("#{klass.table_name}.id IN (?)", ids)
|
21
|
+
end
|
22
|
+
|
23
|
+
def victims_relation(actor, klass, options = {})
|
24
|
+
ids = victims(actor, klass, :pluck => :id)
|
25
|
+
klass.where("#{klass.table_name}.id IN (?)", ids)
|
26
|
+
end
|
27
|
+
|
28
|
+
def victims(actor, klass, options = {})
|
29
|
+
if options[:pluck]
|
30
|
+
Socialization.redis.smembers(generate_backward_key(actor)).inject([]) do |result, element|
|
31
|
+
result << element.match(/\:(\d+)$/)[1] if element.match(/^#{klass}\:/)
|
32
|
+
result
|
33
|
+
end
|
34
|
+
else
|
35
|
+
victims_relation(actor, klass, options).to_a
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def relation!(actor, victim, options = {})
|
40
|
+
unless options[:skip_check] || relation?(actor, victim)
|
41
|
+
Socialization.redis.sadd generate_forward_key(victim), generate_redis_value(actor)
|
42
|
+
Socialization.redis.sadd generate_backward_key(actor), generate_redis_value(victim)
|
43
|
+
call_after_create_hooks(actor, victim)
|
44
|
+
true
|
45
|
+
else
|
46
|
+
false
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def unrelation!(actor, victim, options = {})
|
51
|
+
if options[:skip_check] || relation?(actor, victim)
|
52
|
+
Socialization.redis.srem generate_forward_key(victim), generate_redis_value(actor)
|
53
|
+
Socialization.redis.srem generate_backward_key(actor), generate_redis_value(victim)
|
54
|
+
call_after_destroy_hooks(actor, victim)
|
55
|
+
true
|
56
|
+
else
|
57
|
+
false
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def relation?(actor, victim)
|
62
|
+
Socialization.redis.sismember generate_forward_key(victim), generate_redis_value(actor)
|
63
|
+
end
|
64
|
+
|
65
|
+
def remove_actor_relations(victim)
|
66
|
+
forward_key = generate_forward_key(victim)
|
67
|
+
actors = Socialization.redis.smembers forward_key
|
68
|
+
Socialization.redis.del forward_key
|
69
|
+
actors.each do |actor|
|
70
|
+
Socialization.redis.srem generate_backward_key(actor), generate_redis_value(victim)
|
71
|
+
end
|
72
|
+
true
|
73
|
+
end
|
74
|
+
|
75
|
+
def remove_victim_relations(actor)
|
76
|
+
backward_key = generate_backward_key(actor)
|
77
|
+
victims = Socialization.redis.smembers backward_key
|
78
|
+
Socialization.redis.del backward_key
|
79
|
+
victims.each do |victim|
|
80
|
+
Socialization.redis.srem generate_forward_key(victim), generate_redis_value(actor)
|
81
|
+
end
|
82
|
+
true
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
private
|
87
|
+
def key_type_to_type_names(klass)
|
88
|
+
if klass.name.match(/Follow$/)
|
89
|
+
['follower', 'followable']
|
90
|
+
elsif klass.name.match(/Like$/)
|
91
|
+
['liker', 'likeable']
|
92
|
+
elsif klass.name.match(/Mention$/)
|
93
|
+
['mentioner', 'mentionable']
|
94
|
+
else
|
95
|
+
raise Socialization::ArgumentError.new("Can't find matching type for #{klass}.")
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def generate_forward_key(victim)
|
100
|
+
keys = key_type_to_type_names(self)
|
101
|
+
if victim.is_a?(String)
|
102
|
+
"#{keys[0].pluralize.capitalize}:#{victim}"
|
103
|
+
else
|
104
|
+
"#{keys[0].pluralize.capitalize}:#{victim.class}:#{victim.id}"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def generate_backward_key(actor)
|
109
|
+
keys = key_type_to_type_names(self)
|
110
|
+
if actor.is_a?(String)
|
111
|
+
"#{keys[1].pluralize.capitalize}:#{actor}"
|
112
|
+
else
|
113
|
+
"#{keys[1].pluralize.capitalize}:#{actor.class}:#{actor.id}"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def generate_redis_value(obj)
|
118
|
+
"#{obj.class.name}:#{obj.id}"
|
119
|
+
end
|
120
|
+
|
121
|
+
end # class << self
|
122
|
+
|
123
|
+
end # Base
|
124
|
+
end # RedisStores
|
125
|
+
end # Socialization
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Socialization
|
2
|
+
module RedisStores
|
3
|
+
class Follow < Socialization::RedisStores::Base
|
4
|
+
extend Socialization::Stores::Mixins::Base
|
5
|
+
extend Socialization::Stores::Mixins::Follow
|
6
|
+
extend Socialization::RedisStores::Mixins::Base
|
7
|
+
|
8
|
+
class << self
|
9
|
+
alias_method :follow!, :relation!; public :follow!
|
10
|
+
alias_method :unfollow!, :unrelation!; public :unfollow!
|
11
|
+
alias_method :follows?, :relation?; public :follows?
|
12
|
+
alias_method :followers_relation, :actors_relation; public :followers_relation
|
13
|
+
alias_method :followers, :actors; public :followers
|
14
|
+
alias_method :followables_relation, :victims_relation; public :followables_relation
|
15
|
+
alias_method :followables, :victims; public :followables
|
16
|
+
alias_method :remove_followers, :remove_actor_relations; public :remove_followers
|
17
|
+
alias_method :remove_followables, :remove_victim_relations; public :remove_followables
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Socialization
|
2
|
+
module RedisStores
|
3
|
+
class Like < Socialization::RedisStores::Base
|
4
|
+
extend Socialization::Stores::Mixins::Base
|
5
|
+
extend Socialization::Stores::Mixins::Like
|
6
|
+
extend Socialization::RedisStores::Mixins::Base
|
7
|
+
|
8
|
+
class << self
|
9
|
+
alias_method :like!, :relation!; public :like!
|
10
|
+
alias_method :unlike!, :unrelation!; public :unlike!
|
11
|
+
alias_method :likes?, :relation?; public :likes?
|
12
|
+
alias_method :likers_relation, :actors_relation; public :likers_relation
|
13
|
+
alias_method :likers, :actors; public :likers
|
14
|
+
alias_method :likeables_relation, :victims_relation; public :likeables_relation
|
15
|
+
alias_method :likeables, :victims; public :likeables
|
16
|
+
alias_method :remove_likers, :remove_actor_relations; public :remove_likers
|
17
|
+
alias_method :remove_likeables, :remove_victim_relations; public :remove_likeables
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Socialization
|
2
|
+
module RedisStores
|
3
|
+
class Mention < Socialization::RedisStores::Base
|
4
|
+
extend Socialization::Stores::Mixins::Base
|
5
|
+
extend Socialization::Stores::Mixins::Mention
|
6
|
+
extend Socialization::RedisStores::Mixins::Base
|
7
|
+
|
8
|
+
class << self
|
9
|
+
alias_method :mention!, :relation!; public :mention!
|
10
|
+
alias_method :unmention!, :unrelation!; public :unmention!
|
11
|
+
alias_method :mentions?, :relation?; public :mentions?
|
12
|
+
alias_method :mentioners_relation, :actors_relation; public :mentioners_relation
|
13
|
+
alias_method :mentioners, :actors; public :mentioners
|
14
|
+
alias_method :mentionables_relation, :victims_relation; public :mentionables_relation
|
15
|
+
alias_method :mentionables, :victims; public :mentionables
|
16
|
+
alias_method :remove_mentioners, :remove_actor_relations; public :remove_mentioners
|
17
|
+
alias_method :remove_mentionables, :remove_victim_relations; public :remove_mentionables
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
class Base
|
3
|
+
def is_followable?
|
4
|
+
false
|
5
|
+
end
|
6
|
+
alias followable? is_followable?
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
module Socialization
|
11
|
+
module Followable
|
12
|
+
extend ActiveSupport::Concern
|
13
|
+
|
14
|
+
included do
|
15
|
+
after_destroy { Socialization.follow_model.remove_followers(self) }
|
16
|
+
|
17
|
+
# Specifies if self can be followed.
|
18
|
+
#
|
19
|
+
# @return [Boolean]
|
20
|
+
def is_followable?
|
21
|
+
true
|
22
|
+
end
|
23
|
+
alias followable? is_followable?
|
24
|
+
|
25
|
+
# Specifies if self is followed by a {Follower} object.
|
26
|
+
#
|
27
|
+
# @return [Boolean]
|
28
|
+
def followed_by?(follower)
|
29
|
+
raise Socialization::ArgumentError, "#{follower} is not follower!" unless follower.respond_to?(:is_follower?) && follower.is_follower?
|
30
|
+
Socialization.follow_model.follows?(follower, self)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns an array of {Follower}s following self.
|
34
|
+
#
|
35
|
+
# @param [Class] klass the {Follower} class to be included. e.g. `User`
|
36
|
+
# @return [Array<Follower, Numeric>] An array of Follower objects or IDs
|
37
|
+
def followers(klass, opts = {})
|
38
|
+
Socialization.follow_model.followers(self, klass, opts)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Returns a scope of the {Follower}s following self.
|
42
|
+
#
|
43
|
+
# @param [Class] klass the {Follower} class to be included in the scope. e.g. `User`
|
44
|
+
# @return ActiveRecord::Relation
|
45
|
+
def followers_relation(klass, opts = {})
|
46
|
+
Socialization.follow_model.followers_relation(self, klass, opts)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|