twrk-socialization 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (138) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/.ruby-version +1 -0
  4. data/.travis.yml +8 -0
  5. data/Appraisals +23 -0
  6. data/CHANGELOG.md +67 -0
  7. data/Gemfile +3 -0
  8. data/Guardfile +12 -0
  9. data/LICENSE +20 -0
  10. data/README.md +298 -0
  11. data/Rakefile +14 -0
  12. data/demo/.gitignore +15 -0
  13. data/demo/Gemfile +35 -0
  14. data/demo/README.rdoc +261 -0
  15. data/demo/Rakefile +7 -0
  16. data/demo/app/assets/images/rails.png +0 -0
  17. data/demo/app/assets/javascripts/application.js +15 -0
  18. data/demo/app/assets/stylesheets/application.css +13 -0
  19. data/demo/app/controllers/application_controller.rb +3 -0
  20. data/demo/app/helpers/application_helper.rb +2 -0
  21. data/demo/app/mailers/.gitkeep +0 -0
  22. data/demo/app/models/.gitkeep +0 -0
  23. data/demo/app/models/celebrity.rb +4 -0
  24. data/demo/app/models/comment.rb +5 -0
  25. data/demo/app/models/follow.rb +3 -0
  26. data/demo/app/models/like.rb +2 -0
  27. data/demo/app/models/mention.rb +2 -0
  28. data/demo/app/models/movie.rb +3 -0
  29. data/demo/app/models/user.rb +7 -0
  30. data/demo/app/views/layouts/application.html.erb +14 -0
  31. data/demo/config/application.rb +59 -0
  32. data/demo/config/boot.rb +6 -0
  33. data/demo/config/database.yml +25 -0
  34. data/demo/config/environment.rb +5 -0
  35. data/demo/config/environments/development.rb +37 -0
  36. data/demo/config/environments/production.rb +67 -0
  37. data/demo/config/environments/test.rb +37 -0
  38. data/demo/config/initializers/backtrace_silencers.rb +7 -0
  39. data/demo/config/initializers/inflections.rb +15 -0
  40. data/demo/config/initializers/mime_types.rb +5 -0
  41. data/demo/config/initializers/secret_token.rb +7 -0
  42. data/demo/config/initializers/session_store.rb +8 -0
  43. data/demo/config/initializers/wrap_parameters.rb +14 -0
  44. data/demo/config/locales/en.yml +5 -0
  45. data/demo/config/routes.rb +58 -0
  46. data/demo/config.ru +4 -0
  47. data/demo/db/migrate/20120115051222_create_users.rb +9 -0
  48. data/demo/db/migrate/20120115051234_create_movies.rb +9 -0
  49. data/demo/db/migrate/20120115051255_create_celebrities.rb +9 -0
  50. data/demo/db/migrate/20120115054646_create_follows.rb +14 -0
  51. data/demo/db/migrate/20120115054647_create_likes.rb +14 -0
  52. data/demo/db/migrate/20120221200644_create_mentions.rb +14 -0
  53. data/demo/db/migrate/20120221202703_create_comments.rb +9 -0
  54. data/demo/db/schema.rb +72 -0
  55. data/demo/db/seeds.rb +17 -0
  56. data/demo/lib/assets/.gitkeep +0 -0
  57. data/demo/lib/tasks/.gitkeep +0 -0
  58. data/demo/public/404.html +26 -0
  59. data/demo/public/422.html +26 -0
  60. data/demo/public/500.html +25 -0
  61. data/demo/public/favicon.ico +0 -0
  62. data/demo/public/index.html +204 -0
  63. data/demo/public/robots.txt +5 -0
  64. data/demo/script/rails +6 -0
  65. data/demo/test/fixtures/.gitkeep +0 -0
  66. data/demo/test/fixtures/celebrities.yml +7 -0
  67. data/demo/test/fixtures/movies.yml +7 -0
  68. data/demo/test/fixtures/users.yml +7 -0
  69. data/demo/test/functional/.gitkeep +0 -0
  70. data/demo/test/integration/.gitkeep +0 -0
  71. data/demo/test/performance/browsing_test.rb +12 -0
  72. data/demo/test/test_helper.rb +13 -0
  73. data/demo/test/unit/.gitkeep +0 -0
  74. data/demo/test/unit/celebrity_test.rb +7 -0
  75. data/demo/test/unit/movie_test.rb +7 -0
  76. data/demo/test/unit/user_test.rb +7 -0
  77. data/demo/vendor/assets/javascripts/.gitkeep +0 -0
  78. data/demo/vendor/assets/stylesheets/.gitkeep +0 -0
  79. data/demo/vendor/plugins/.gitkeep +0 -0
  80. data/init.rb +1 -0
  81. data/lib/generators/socialization/socialization_generator.rb +38 -0
  82. data/lib/generators/socialization/templates/active_record/migration_follows.rb +14 -0
  83. data/lib/generators/socialization/templates/active_record/migration_likes.rb +14 -0
  84. data/lib/generators/socialization/templates/active_record/migration_mentions.rb +14 -0
  85. data/lib/generators/socialization/templates/active_record/model_follow.rb +2 -0
  86. data/lib/generators/socialization/templates/active_record/model_like.rb +2 -0
  87. data/lib/generators/socialization/templates/active_record/model_mention.rb +2 -0
  88. data/lib/generators/socialization/templates/redis/model_follow.rb +2 -0
  89. data/lib/generators/socialization/templates/redis/model_like.rb +2 -0
  90. data/lib/generators/socialization/templates/redis/model_mention.rb +2 -0
  91. data/lib/socialization/actors/follower.rb +88 -0
  92. data/lib/socialization/actors/liker.rb +88 -0
  93. data/lib/socialization/actors/mentioner.rb +88 -0
  94. data/lib/socialization/config/config.rb +39 -0
  95. data/lib/socialization/helpers/acts_as_helpers.rb +39 -0
  96. data/lib/socialization/helpers/string.rb +17 -0
  97. data/lib/socialization/lib/exceptions.rb +3 -0
  98. data/lib/socialization/stores/active_record/follow.rb +125 -0
  99. data/lib/socialization/stores/active_record/like.rb +121 -0
  100. data/lib/socialization/stores/active_record/mention.rb +125 -0
  101. data/lib/socialization/stores/active_record/mixins/base.rb +12 -0
  102. data/lib/socialization/stores/mixins/base.rb +22 -0
  103. data/lib/socialization/stores/mixins/follow.rb +39 -0
  104. data/lib/socialization/stores/mixins/like.rb +40 -0
  105. data/lib/socialization/stores/mixins/mention.rb +40 -0
  106. data/lib/socialization/stores/redis/base.rb +125 -0
  107. data/lib/socialization/stores/redis/config.rb +11 -0
  108. data/lib/socialization/stores/redis/follow.rb +22 -0
  109. data/lib/socialization/stores/redis/like.rb +22 -0
  110. data/lib/socialization/stores/redis/mention.rb +22 -0
  111. data/lib/socialization/stores/redis/mixins/base.rb +8 -0
  112. data/lib/socialization/version.rb +3 -0
  113. data/lib/socialization/victims/followable.rb +50 -0
  114. data/lib/socialization/victims/likeable.rb +51 -0
  115. data/lib/socialization/victims/mentionable.rb +51 -0
  116. data/lib/socialization.rb +20 -0
  117. data/socialization.gemspec +32 -0
  118. data/spec/actors/follower_spec.rb +113 -0
  119. data/spec/actors/liker_spec.rb +105 -0
  120. data/spec/actors/mentioner_spec.rb +105 -0
  121. data/spec/spec_helper.rb +21 -0
  122. data/spec/spec_support/data_stores.rb +232 -0
  123. data/spec/spec_support/matchers.rb +54 -0
  124. data/spec/stores/active_record/follow_store_spec.rb +147 -0
  125. data/spec/stores/active_record/like_store_spec.rb +146 -0
  126. data/spec/stores/active_record/mention_store_spec.rb +148 -0
  127. data/spec/stores/active_record/mixins/base_spec.rb +25 -0
  128. data/spec/stores/redis/base_spec.rb +195 -0
  129. data/spec/stores/redis/config_spec.rb +28 -0
  130. data/spec/stores/redis/follow_store_spec.rb +26 -0
  131. data/spec/stores/redis/like_store_spec.rb +23 -0
  132. data/spec/stores/redis/mention_store_spec.rb +23 -0
  133. data/spec/string_spec.rb +13 -0
  134. data/spec/victims/followable_spec.rb +59 -0
  135. data/spec/victims/likeable_spec.rb +58 -0
  136. data/spec/victims/mentionable_spec.rb +59 -0
  137. data/spec/world_spec.rb +107 -0
  138. 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,11 @@
1
+ module Socialization
2
+ class << self
3
+ def redis
4
+ @redis ||= Redis.new
5
+ end
6
+
7
+ def redis=(redis)
8
+ @redis = redis
9
+ end
10
+ end
11
+ end
@@ -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,8 @@
1
+ module Socialization
2
+ module RedisStores
3
+ module Mixins
4
+ module Base
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,3 @@
1
+ module Socialization
2
+ VERSION = "1.2.0"
3
+ 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