Commit 0ce290be by Rocky Duan

single subscription model working

parent f60b0f0f
...@@ -28,6 +28,7 @@ namespace :test do ...@@ -28,6 +28,7 @@ namespace :test do
Commentable.delete_all Commentable.delete_all
User.delete_all User.delete_all
Notification.delete_all Notification.delete_all
Subscription.delete_all
commentable = Commentable.create!(commentable_type: "questions", commentable_id: "1") commentable = Commentable.create!(commentable_type: "questions", commentable_id: "1")
...@@ -68,6 +69,7 @@ namespace :db do ...@@ -68,6 +69,7 @@ namespace :db do
User.create_indexes User.create_indexes
Commentable.create_indexes Commentable.create_indexes
Notification.create_indexes Notification.create_indexes
Subscription.create_indexes
Delayed::Backend::Mongoid::Job.create_indexes Delayed::Backend::Mongoid::Job.create_indexes
puts "finished" puts "finished"
end end
...@@ -78,6 +80,8 @@ namespace :db do ...@@ -78,6 +80,8 @@ namespace :db do
Comment.delete_all Comment.delete_all
CommentThread.delete_all CommentThread.delete_all
User.delete_all User.delete_all
Notification.delete_all
Subscription.delete_all
beginning_time = Time.now beginning_time = Time.now
...@@ -86,7 +90,7 @@ namespace :db do ...@@ -86,7 +90,7 @@ namespace :db do
users = (1..10).map {|id| User.find_or_create_by(external_id: id.to_s)} users = (1..10).map {|id| User.find_or_create_by(external_id: id.to_s)}
10.times do 10.times do
users.sample.follow(users.sample) users.sample.subscribe(users.sample)
end end
def generate_comments(commentable_type, commentable_id, level_limit, users) def generate_comments(commentable_type, commentable_id, level_limit, users)
......
...@@ -115,28 +115,28 @@ end ...@@ -115,28 +115,28 @@ end
post '/api/v1/users/:user_id/subscriptions' do |user_id| post '/api/v1/users/:user_id/subscriptions' do |user_id|
user = User.find_or_create_by(external_id: user_id) user = User.find_or_create_by(external_id: user_id)
case params["subscribed_type"] source = case params["subscribed_type"]
when "user" when "user"
user.follow(User.find_or_create_by(external_id: params["subscribed_id"])) User.find_or_create_by(external_id: params["subscribed_id"])
when "thread" when "thread"
user.subscribe_comment_thread(CommentThread.find(params["subscribed_id"])) CommentThread.find(params["subscribed_id"])
else else
user.subscribe_commentable(Commentable.find_or_create_by(commentable_type: params["subscribed_type"], commentable_id: params["subscribed_id"])) Commentable.find_or_create_by(commentable_type: params["subscribed_type"], commentable_id: params["subscribed_id"])
end end
user.reload.to_hash.to_json user.subscribe(source).to_hash.to_json
end end
delete '/api/v1/users/:user_id/subscriptions' do |user_id| delete '/api/v1/users/:user_id/subscriptions' do |user_id|
user = User.find_or_create_by(external_id: user_id) user = User.find_or_create_by(external_id: user_id)
case params["subscribed_type"] source = case params["subscribed_type"]
when "user" when "user"
user.unfollow(User.find_or_create_by(external_id: params["subscribed_id"])) User.find_or_create_by(external_id: params["subscribed_id"])
when "thread" when "thread"
user.unsubscribe_comment_thread(CommentThread.find(params["subscribed_id"])) CommentThread.find(params["subscribed_id"])
else else
user.unsubscribe_commentable(Commentable.find_or_create_by(commentable_type: params["subscribed_type"], commentable_id: params["subscribed_id"])) Commentable.find_or_create_by(commentable_type: params["subscribed_type"], commentable_id: params["subscribed_id"])
end end
user.reload.to_hash.to_json user.unsubscribe(source).to_hash.to_json
end end
if env.to_s == "development" if env.to_s == "development"
...@@ -146,6 +146,7 @@ if env.to_s == "development" ...@@ -146,6 +146,7 @@ if env.to_s == "development"
Commentable.delete_all Commentable.delete_all
User.delete_all User.delete_all
Notification.delete_all Notification.delete_all
Subscription.delete_all
{}.to_json {}.to_json
end end
end end
......
...@@ -73,7 +73,7 @@ private ...@@ -73,7 +73,7 @@ private
def auto_subscribe_comment_thread def auto_subscribe_comment_thread
if CommentService.config["auto_subscribe_comment_threads"] and author if CommentService.config["auto_subscribe_comment_threads"] and author
author.subscribe_comment_thread(get_comment_thread) author.subscribe(get_comment_thread)
end end
end end
......
...@@ -12,7 +12,8 @@ class CommentThread ...@@ -12,7 +12,8 @@ class CommentThread
belongs_to :author, class_name: "User", inverse_of: :comment_threads, index: true, autosave: true belongs_to :author, class_name: "User", inverse_of: :comment_threads, index: true, autosave: true
belongs_to :commentable, index: true, autosave: true belongs_to :commentable, index: true, autosave: true
has_many :comments, dependent: :destroy, autosave: true# Use destroy to envoke callback on the top-level comments TODO async has_many :comments, dependent: :destroy, autosave: true# Use destroy to envoke callback on the top-level comments TODO async
has_and_belongs_to_many :subscribers, class_name: "User", inverse_of: :subscribed_comment_threads, autosave: true #has_many :subscriptions, as: :source
#has_and_belongs_to_many :subscribers, class_name: "User", inverse_of: :subscribed_comment_threads, autosave: true
attr_accessible :title, :body, :course_id attr_accessible :title, :body, :course_id
...@@ -22,7 +23,15 @@ class CommentThread ...@@ -22,7 +23,15 @@ class CommentThread
validates_presence_of :author if not CommentService.config["allow_anonymity"] validates_presence_of :author if not CommentService.config["allow_anonymity"]
after_create :handle_after_create after_create :handle_after_create
def subscriptions
Subscription.where(source_id: self.id, source_type: self.class)
end
def subscribers
subscriptions.map{|s| User.find(s.subscriber_id)}
end
def to_hash(params={}) def to_hash(params={})
doc = as_document.slice(*%w[title body course_id _id]). doc = as_document.slice(*%w[title body course_id _id]).
merge("user_id" => (author.id if author)). merge("user_id" => (author.id if author)).
...@@ -47,7 +56,7 @@ private ...@@ -47,7 +56,7 @@ private
) )
notification.actor = author notification.actor = author
notification.target = self notification.target = self
notification.receivers << (commentable.subscribers + (author.followers if author).to_a).uniq_by(&:id) notification.receivers << (commentable.subscribers + author.followers).uniq_by(&:id)
notification.receivers.delete(author) if not CommentService.config["send_notifications_to_author"] and author notification.receivers.delete(author) if not CommentService.config["send_notifications_to_author"] and author
notification.save! notification.save!
end end
...@@ -55,7 +64,7 @@ private ...@@ -55,7 +64,7 @@ private
def auto_subscribe_comment_thread def auto_subscribe_comment_thread
if CommentService.config["auto_subscribe_comment_threads"] and author if CommentService.config["auto_subscribe_comment_threads"] and author
author.subscribe_comment_thread(self) author.subscribe(self)
end end
end end
......
...@@ -5,7 +5,7 @@ class Commentable ...@@ -5,7 +5,7 @@ class Commentable
field :commentable_id, type: String field :commentable_id, type: String
has_many :comment_threads, dependent: :destroy has_many :comment_threads, dependent: :destroy
has_and_belongs_to_many :subscribers, class_name: "User", inverse_of: :subscribed_commentables, autosave: true has_many :subscriptions, as: :source
attr_accessible :commentable_type, :commentable_id attr_accessible :commentable_type, :commentable_id
...@@ -15,6 +15,14 @@ class Commentable ...@@ -15,6 +15,14 @@ class Commentable
index [[:commentable_type, Mongo::ASCENDING], [:commentable_id, Mongo::ASCENDING]] index [[:commentable_type, Mongo::ASCENDING], [:commentable_id, Mongo::ASCENDING]]
def subscriptions
Subscription.where(source_id: self.id, source_type: self.class)
end
def subscribers
subscriptions.map{|s| User.find(s.subscriber_id)}
end
def to_hash(params={}) def to_hash(params={})
as_document.slice(*%w[_id commentable_type commentable_id]) as_document.slice(*%w[_id commentable_type commentable_id])
end end
......
class Subscription
include Mongoid::Document
include Mongoid::Timestamps
field :subscriber_id, type: String
field :source_id, type: String
field :source_type, type: String
index [[:subscriber_id, Mongo::ASCENDING], [:source_id, Mongo::ASCENDING], [:source_type, Mongo::ASCENDING]]
index [[:source_id, Mongo::ASCENDING], [:source_type, Mongo::ASCENDING]]
index :subscriber_id
def to_hash
as_document
end
end
...@@ -8,8 +8,6 @@ class User ...@@ -8,8 +8,6 @@ class User
has_many :comment_threads, inverse_of: :author has_many :comment_threads, inverse_of: :author
has_many :activities, class_name: "Notification", inverse_of: :actor has_many :activities, class_name: "Notification", inverse_of: :actor
has_and_belongs_to_many :notifications, inverse_of: :receivers has_and_belongs_to_many :notifications, inverse_of: :receivers
has_and_belongs_to_many :followers, class_name: "User", inverse_of: :followings, autosave: true
has_and_belongs_to_many :followings, class_name: "User", inverse_of: :followers
validates_presence_of :external_id validates_presence_of :external_id
validates_uniqueness_of :external_id validates_uniqueness_of :external_id
...@@ -18,38 +16,26 @@ class User ...@@ -18,38 +16,26 @@ class User
as_document.slice(*%w[_id external_id]) as_document.slice(*%w[_id external_id])
end end
def follow(user) def subscriptions
if id != user.id and not followings.include? user Subscription.where(subscriber_id: self.id)
followings << user
end
end end
def unfollow(user) def follower_subscriptions
followings.delete(user) Subscription.where(source_id: self.id, source_type: self.class)
end end
def self.subscribing(class_plural_sym) def followers
class_plural = class_plural_sym.to_s follower_subscriptions.map{|s| User.find(s.subscriber_id)}
class_single = class_plural.singularize
class_name = class_single.camelize
subscribed_symbol = "subscribed_#{class_plural}".intern
has_and_belongs_to_many subscribed_symbol, class_name: class_name, inverse_of: :subscribers
self.class_eval <<-END
def subscribe_#{class_single}(subscribing_object)
if not subscribed_#{class_plural}.include? subscribing_object
subscribed_#{class_plural} << subscribing_object
end
end
def unsubscribe_#{class_single}(subscribing_object)
subscribed_#{class_plural}.delete(subscribing_object)
end
END
end end
subscribing :comment_threads def subscribe(source)
subscribing :commentables Subscription.find_or_create_by(subscriber_id: self._id.to_s, source_id: source._id.to_s, source_type: source.class.to_s)
end
def unsubscribe(source)
subscription = Subscription.where(subscriber_id: self._id.to_s, source_id: source._id.to_s, source_type: source.class.to_s).first
subscription.destroy
subscription
end
end end
...@@ -14,6 +14,7 @@ def init_without_subscriptions ...@@ -14,6 +14,7 @@ def init_without_subscriptions
Commentable.delete_all Commentable.delete_all
User.delete_all User.delete_all
Notification.delete_all Notification.delete_all
Subscription.delete_all
commentable = Commentable.new(commentable_type: "questions", commentable_id: "1") commentable = Commentable.new(commentable_type: "questions", commentable_id: "1")
commentable.save! commentable.save!
...@@ -78,19 +79,21 @@ def init_with_subscriptions ...@@ -78,19 +79,21 @@ def init_with_subscriptions
Commentable.delete_all Commentable.delete_all
User.delete_all User.delete_all
Notification.delete_all Notification.delete_all
Subscription.delete_all
user1 = User.create!(external_id: "1") user1 = User.create!(external_id: "1")
user2 = User.create!(external_id: "2") user2 = User.create!(external_id: "2")
user2.followings << user1 user2.subscribe(user1)
commentable = Commentable.new(commentable_type: "questions", commentable_id: "1") commentable = Commentable.new(commentable_type: "questions", commentable_id: "1")
commentable.subscribers << [user1, user2] user1.subscribe(commentable)
user2.subscribe(commentable)
commentable.save! commentable.save!
thread = commentable.comment_threads.new(title: "I can't solve this problem", body: "can anyone help me?", course_id: "1") thread = commentable.comment_threads.new(title: "I can't solve this problem", body: "can anyone help me?", course_id: "1")
thread.author = user1 thread.author = user1
thread.subscribers << user2 user2.subscribe(thread)
thread.save! thread.save!
comment = thread.comments.new(body: "this problem is so easy", course_id: "1") comment = thread.comments.new(body: "this problem is so easy", course_id: "1")
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment