Commit cb1c0abb by Rocky Duan

feed finally works; not sure if bug caused by custom id or autosave

parent 71fe495b
......@@ -4,16 +4,20 @@ require 'bundler'
Bundler.setup
Bundler.require
Dir[File.dirname(__FILE__) + '/models/*.rb'].each {|file| require file}
env_index = ARGV.index("-e")
env_arg = ARGV[env_index + 1] if env_index
env = env_arg || ENV["SINATRA_ENV"] || "development"
module CommentService
class << self; attr_accessor :config; end
end
CommentService.config = YAML.load_file("config/application.yml")
Mongoid.load!("config/mongoid.yml")
Mongoid.logger.level = Logger::INFO
config = YAML.load_file("config/application.yml")
Dir[File.dirname(__FILE__) + '/models/*.rb'].each {|file| require file}
# DELETE /api/v1/commentables/:commentable_type/:commentable_id
......@@ -71,7 +75,7 @@ end
post '/api/v1/commentables/:commentable_type/:commentable_id/comment_threads' do |commentable_type, commentable_id|
commentable = Commentable.find_or_create_by(commentable_type: commentable_type, commentable_id: commentable_id)
comment_thread = commentable.comment_threads.new(params.slice(*%w[title body course_id]))
comment_thread.author = User.find_or_create_by(id: params["user_id"])
comment_thread.author = User.find_or_create_by(external_id: params["user_id"])
comment_thread.save!
comment_thread.to_hash.to_json
end
......@@ -99,7 +103,7 @@ end
post '/api/v1/comment_threads/:comment_thread_id/comments' do |comment_thread_id|
comment_thread = CommentThread.find(comment_thread_id)
comment = comment_thread.comments.new(params.slice(*%w[body course_id]))
comment.author = User.find_or_create_by(id: params["user_id"])
comment.author = User.find_or_create_by(external_id: params["user_id"])
comment.save!
comment.to_hash.to_json
end
......@@ -137,7 +141,7 @@ end
post '/api/v1/comments/:comment_id' do |comment_id|
comment = Comment.find(comment_id)
sub_comment = comment.children.new(params.slice(*%w[body course_id]))
sub_comment.author = User.find_or_create_by(id: params["user_id"])
sub_comment.author = User.find_or_create_by(external_id: params["user_id"])
sub_comment.save!
sub_comment.to_hash.to_json
end
......@@ -156,7 +160,7 @@ end
put '/api/v1/votes/comments/:comment_id/users/:user_id' do |comment_id, user_id|
comment = Comment.find(comment_id)
user = User.find_or_create_by(id: user_id)
user = User.find_or_create_by(external_id: user_id)
user.vote(comment, params["value"].intern)
Comment.find(comment_id).to_hash.to_json
end
......@@ -166,7 +170,7 @@ end
delete '/api/v1/votes/comments/:comment_id/users/:user_id' do |comment_id, user_id|
comment = Comment.find(comment_id)
user = User.find_or_create_by(id: user_id)
user = User.find_or_create_by(external_id: user_id)
user.unvote(comment)
Comment.find(comment_id).to_hash.to_json
end
......@@ -176,7 +180,7 @@ end
put '/api/v1/votes/comment_threads/:comment_thread_id/users/:user_id' do |comment_thread_id, user_id|
comment_thread = CommentThread.find(comment_thread_id)
user = User.find_or_create_by(id: user_id)
user = User.find_or_create_by(external_id: user_id)
user.vote(comment_thread, params["value"].intern)
CommentThread.find(comment_thread_id).to_hash.to_json
end
......@@ -186,7 +190,7 @@ end
delete '/api/v1/votes/comment_threads/:comment_thread_id/users/:user_id' do |comment_thread_id, user_id|
comment_thread = CommentThread.find(comment_thread_id)
user = User.find_or_create_by(id: user_id)
user = User.find_or_create_by(external_id: user_id)
user.unvote(comment_thread)
CommentThread.find(comment_thread_id).to_hash.to_json
end
......@@ -195,16 +199,17 @@ end
# get all subscribed feeds for the user
get '/api/v1/users/:user_id/feeds' do |user_id|
user = User.find_or_create_by(id: user_id)
user.feeds.map(&:to_hash).to_json
user = User.find_or_create_by(external_id: user_id)
puts user.inspect
user.subscribed_feeds.map(&:to_hash).to_json
end
# POST /api/v1/users/:user_id/follow
# follow user
post '/api/v1/users/:user_id/follow' do |user_id|
user = User.find_or_create_by(id: user_id)
followed_user = User.find_or_create_by(id: params[:user_id])
user = User.find_or_create_by(external_id: user_id)
followed_user = User.find_or_create_by(external_id: params[:user_id])
user.follow(followed_user)
user.to_hash.to_json
end
......@@ -213,8 +218,8 @@ end
# unfollow user
post '/api/v1/users/:user_id/unfollow' do |user_id|
user = User.find_or_create_by(id: user_id)
followed_user = User.find_or_create_by(id: params[:user_id])
user = User.find_or_create_by(external_id: user_id)
followed_user = User.find_or_create_by(external_id: params[:user_id])
user.unfollow(followed_user)
user.to_hash.to_json
end
......@@ -223,7 +228,7 @@ end
# watch a commentable
post '/api/v1/users/:user_id/watch/commentable' do |user_id|
user = User.find_or_create_by(id: user_id)
user = User.find_or_create_by(external_id: user_id)
commentable = Commentable.find_or_create_by(commentable_type: params[:commentable_type],
commentable_id: parasm[:commentable_id])
user.watch_commentable(commentable)
......@@ -234,7 +239,7 @@ end
# unwatch a commentable
post '/api/v1/users/:user_id/unwatch/commentable' do |user_id|
user = User.find_or_create_by(id: user_id)
user = User.find_or_create_by(external_id: user_id)
commentable = Commentable.find_or_create_by(commentable_type: params[:commentable_type],
commentable_id: parasm[:commentable_id])
user.unwatch_commentable(commentable)
......@@ -245,7 +250,7 @@ end
# watch a comment thread
post '/api/v1/users/:user_id/watch/comment_thread' do |user_id|
user = User.find_or_create_by(id: user_id)
user = User.find_or_create_by(external_id: user_id)
comment_thread = CommentThread.find(params[:comment_thread_id])
user.watch_comment_thread(comment_thread)
user.to_hash.to_json
......@@ -255,7 +260,7 @@ end
# unwatch a comment thread
post '/api/v1/users/:user_id/unwatch/comment_thread' do |user_id|
user = User.find_or_create_by(id: user_id)
user = User.find_or_create_by(external_id: user_id)
comment_thread = CommentThread.find(params[:comment_thread_id])
user.unwatch_comment_thread(comment_thread)
user.to_hash.to_json
......
level_limit: 3
send_notifications_to_author: false
auto_watch_comment_threads: true
allow_anonymity: true
......@@ -10,17 +10,17 @@ class Comment
field :course_id, type: String
field :endorsed, type: Boolean, default: false
belongs_to :author, class_name: "User", index: true, autosave: true
belongs_to :comment_thread, index: true, autosave: true
belongs_to :author, class_name: "User", index: true
belongs_to :comment_thread, index: true
attr_accessible :body, :course_id, :endorsed
validates_presence_of :body
validates_presence_of :course_id # do we really need this?
#validates_presence_of :author # allow anonymity?
validates_presence_of :author if not CommentService.config["allow_anonymity"]
before_destroy :delete_descendants # TODO async
after_create :generate_feeds
after_create :handle_after_create
def self.hash_tree(nodes)
nodes.map{|node, sub_nodes| node.to_hash.merge("children" => hash_tree(sub_nodes).compact)}
......@@ -52,7 +52,9 @@ class Comment
end
end
private
def generate_feeds
if get_comment_thread.watchers or (author.followers if author)
feed = Feed.new(
feed_type: "post_reply",
info: {
......@@ -63,10 +65,23 @@ class Comment
)
feed.actor = author
feed.target = self
feed.subscribers << (get_comment_thread.watchers + author.followers).uniq_by(&:id).delete(author) # doesn't send notification to author
feed.subscribers << (get_comment_thread.watchers + author.followers).uniq_by(&:id)
feed.subscribers.delete(author) if not CommentService.config["send_notifications_to_author"]
feed.save!
end
end
def auto_watch_comment_thread
if CommentService.config["auto_watch_comment_threads"] and author
author.watch_comment_thread(get_comment_thread)
end
end
def handle_after_create
generate_feeds
#auto_watch_comment_thread
end
handle_asynchronously :generate_feeds
#handle_asynchronously :handle_after_create
end
......@@ -9,23 +9,23 @@ class CommentThread
field :body, type: String
field :course_id, type: String, index: true
belongs_to :author, class_name: "User", inverse_of: :comment_threads, index: true, autosave: true
belongs_to :commentable, 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
has_many :comments, dependent: :destroy # Use destroy to envoke callback on the top-level comments TODO async
has_and_belongs_to_many :watchers, class_name: "User", inverse_of: :watched_comment_threads, autosave: true
has_and_belongs_to_many :watchers, class_name: "User", inverse_of: :watched_comment_threads#, autosave: true
attr_accessible :title, :body, :course_id
validates_presence_of :title
validates_presence_of :body
validates_presence_of :course_id # do we really need this?
#validates_presence_of :author #allow anonymity?
validates_presence_of :author if not CommentService.config["allow_anonymity"]
after_create :generate_feeds
after_create :handle_after_create
def to_hash(params={})
doc = as_document.slice(*%w[title body course_id _id]).
merge("user_id" => author.id).
merge("user_id" => (author.id if author)).
merge("votes" => votes.slice(*%w[count up_count down_count point]))
if params[:recursive]
doc = doc.merge("children" => comments.map{|c| c.to_hash(recursive: true)})
......@@ -33,7 +33,9 @@ class CommentThread
doc
end
private
def generate_feeds
if watchers or (author.followers if author)
feed = Feed.new(
feed_type: "post_topic",
info: {
......@@ -45,9 +47,22 @@ class CommentThread
)
feed.actor = author
feed.target = self
feed.subscribers << (commentable.watchers + author.followers).uniq_by(&:id).delete(author) # doesn't send notification to author
feed.subscribers << (commentable.watchers + (author.followers if author).to_a).uniq_by(&:id)
feed.subscribers.delete(author) if not CommentService.config["send_notifications_to_author"]
feed.save!
end
end
def auto_watch_comment_thread
if CommentService.config["auto_watch_comment_threads"] and author
author.watch_comment_thread(self)
end
end
def handle_after_create
generate_feeds
auto_watch_comment_thread
end
handle_asynchronously :generate_feeds
#handle_asynchronously :handle_after_create
end
......@@ -5,7 +5,7 @@ class Commentable
field :commentable_id, type: String
has_many :comment_threads, dependent: :destroy
has_and_belongs_to_many :watchers, class_name: "User", inverse_of: :watched_commentables, autosave: true
has_and_belongs_to_many :watchers, class_name: "User", inverse_of: :watched_commentables#, autosave: true
attr_accessible :commentable_type, :commentable_id
......
......@@ -5,8 +5,8 @@ class Feed
field :feed_type, type: String
field :info, type: Hash
belongs_to :actor, class_name: "User", inverse_of: :activities, index: true, autosave: true
belongs_to :target, inverse_of: :activities, polymorphic: true, autosave: true
belongs_to :actor, class_name: "User", inverse_of: :activities, index: true
belongs_to :target, inverse_of: :activities, polymorphic: true
attr_accessible :feed_type, :info
......
......@@ -2,17 +2,20 @@ class User
include Mongoid::Document
include Mongo::Voter
identity type: String
field :external_id, type: String, index: true
has_many :comments
has_many :comment_threads, inverse_of: :author
has_many :activities, class_name: "Feed", inverse_of: :actor
has_and_belongs_to_many :subscribed_feeds, class_name: "Feed", inverse_of: :subscribers, autosave: true
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, autosave: true
has_and_belongs_to_many :followers, class_name: "User", inverse_of: :followings
has_and_belongs_to_many :followings, class_name: "User", inverse_of: :followers
validates_presence_of :external_id
validates_uniqueness_of :external_id
def to_hash(params={})
as_document.slice(*%w[_id])
as_document.slice(*%w[_id external_id])
end
def follow(user)
......@@ -37,11 +40,13 @@ class User
def watch_#{class_single}(watching_object)
if not watched_#{class_plural}.include? watching_object
watched_#{class_plural} << watching_object
save!
end
end
def unwatch_#{class_single}(watching_object)
watched_#{class_plural}.delete(watching_object)
save!
end
END
end
......
......@@ -18,7 +18,7 @@ def init_without_feeds
commentable = Commentable.new(commentable_type: "questions", commentable_id: "1")
commentable.save!
user = User.create!(id: "1")
user = User.create!(external_id: "1")
comment_thread = commentable.comment_threads.new(title: "I can't solve this problem", body: "can anyone help me?", course_id: "1")
comment_thread.author = user
......@@ -58,7 +58,7 @@ def init_without_feeds
comment1.author = user
comment1.save!
users = (2..10).map{|id| User.find_or_create_by(id: id.to_s)}
users = (2..10).map{|id| User.find_or_create_by(external_id: id.to_s)}
Comment.all.each do |c|
user.vote(c, :up) # make the first user always vote up for convenience
......@@ -69,6 +69,7 @@ def init_without_feeds
user.vote(c, :up) # make the first user always vote up for convenience
users.each {|user| user.vote(c, [:up, :down].sample)}
end
end
def init_with_feeds
......@@ -78,10 +79,11 @@ def init_with_feeds
User.delete_all
Feed.delete_all
user1 = User.create!(id: "1")
user2 = User.create!(id: "2")
user1 = User.create!(external_id: "1")
user2 = User.create!(external_id: "2")
user1.followers << user2
user1.save!
commentable = Commentable.new(commentable_type: "questions", commentable_id: "1")
commentable.watchers << [user1, user2]
......@@ -89,26 +91,34 @@ def init_with_feeds
comment_thread = commentable.comment_threads.new(title: "I can't solve this problem", body: "can anyone help me?", course_id: "1")
comment_thread.author = user1
comment_thread.watchers << user1
comment_thread.watchers << user2
comment_thread.save!
comment = comment_thread.comments.new(body: "this problem is so easy", course_id: "1")
comment.author = user1
comment.author = user2
comment.save!
comment1 = comment.children.new(body: "not for me!", course_id: "1")
comment1.author = user1
comment1.save!
comment2 = comment1.children.new(body: "not for me neither!", course_id: "1")
comment2.author = user1
comment2.author = user2
comment2.save!
comment_thread = commentable.comment_threads.new(title: "This problem is wrong", body: "it is unsolvable", course_id: "2")
comment_thread.author = user
comment_thread.author = user1
comment_thread.save!
user1.save!
user2.save!
pp Feed.all.to_a
puts User.first.inspect
puts User.first.subscribed_feeds.inspect
end
describe "app" do
=begin
describe "commentables" do
before(:each) { init_without_feeds }
describe "DELETE /api/v1/commentables/:commentable_type/:commentable_id" do
......@@ -156,7 +166,6 @@ describe "app" do
end
end
end
describe "comment threads" do
before(:each) { init_without_feeds }
describe "GET /api/v1/comment_threads/:comment_thread_id" do
......@@ -331,12 +340,31 @@ describe "app" do
end
end
end
=end
describe "feeds" do
#before(:each) { init_with_feeds }
before(:each) { init_with_feeds }
describe "GET /api/v1/users/:user_id/feeds" do
it "get all subscribed feeds for the user" do
user = User.where(external_id: "1").first
get "/api/v1/users/#{user.external_id}/feeds"
last_response.should be_ok
feeds = parse last_response.body
pp feeds
so_easy = Comment.all.select{|c| c.body == "this problem is so easy"}.first
not_for_me_neither = Comment.all.select{|c| c.body == "not for me neither!"}.first
feed_so_easy = feeds.select{|f| f["feed_type"] == "post_reply" and f["info"]["comment_id"] == so_easy.id.to_s}.first
feed_so_easy.should_not be_nil
feed_not_for_me_neither = feeds.select{|f| f["feed_type"] == "post_reply" and f["info"]["comment_id"] == not_for_me_neither.id.to_s}.first
feed_not_for_me_neither.should_not be_nil
feeds.each do |feed|
if feed["feed_type"] == "post_reply"
feed["info"]["comment_body"] = Comment.find(feed["info"]["comment_id"]).body
end
end
pp feeds
end
end
=begin
describe "POST /api/v1/users/:user_id/follow" do
it "follow user" do
......@@ -367,5 +395,6 @@ describe "app" do
end
end
=end
end
end
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