update message bus to support per client filtering

start work on user_tracking_state
fix can_ban? in guardian
expose protected scopes on topic_query we need
move guardian spec to use build as opposed to creating topics / posts / users
start work on user tracking spec
This commit is contained in:
Sam 2013-05-21 16:39:51 +10:00
parent 4ccd89f7df
commit b5eff93a9d
6 changed files with 136 additions and 28 deletions

View File

@ -8,7 +8,7 @@ GIT
GIT GIT
remote: https://github.com/SamSaffron/message_bus remote: https://github.com/SamSaffron/message_bus
revision: e5654728b1c1b97fa5c7bb79fd689b31cec3f01d revision: 031a107bbe6e468caa67ff540485d70230d1c362
specs: specs:
message_bus (0.0.2) message_bus (0.0.2)
eventmachine eventmachine

View File

@ -0,0 +1,41 @@
# this class is used to mirror unread and new status back to end users
# in JavaScript there is a mirror class that is kept in-sync using the mssage bus
# the allows end users to always know which topics have unread posts in them
# and which topics are new
class UserTrackingState
CHANNEL = "/user-tracking"
MessageBus.client_filter(CHANNEL) do |user_id, message|
if user_id
UserTrackingState.new(User.find(user_id)).filter(message)
else
nil
end
end
def self.trigger_change(topic_id, post_number, user_id=nil)
MessageBus.publish(CHANNEL, "CHANGE", user_ids: [user_id].compact)
end
def initialize(user)
@user = user
@query = TopicQuery.new(@user)
end
def new_list
@query
.new_results(limit: false)
.select(topics: [:id, :created_at])
.map{|t| [t.id, t.created_at]}
end
def unread_list
[]
end
def filter(message)
end
end

View File

@ -109,11 +109,9 @@ class Guardian
alias :can_activate? :can_approve? alias :can_activate? :can_approve?
def can_ban?(user) def can_ban?(user)
return false if user.blank? is_staff? && user && !user.staff?
return false unless @user.try(:admin?)
return false if user.admin?
true
end end
alias :can_deactivate? :can_ban? alias :can_deactivate? :can_ban?
def can_clear_flags?(post) def can_clear_flags?(post)

View File

@ -190,6 +190,19 @@ class TopicQuery
create_list(:new_in_category) {|l| l.where(category_id: category.id).by_newest.first(25)} create_list(:new_in_category) {|l| l.where(category_id: category.id).by_newest.first(25)}
end end
def new_results(list_opts={})
default_list(list_opts)
.where("topics.created_at >= :created_at", created_at: @user.treat_as_new_topic_start_date)
.where("tu.last_read_post_number IS NULL")
.where("COALESCE(tu.notification_level, :tracking) >= :tracking", tracking: TopicUser.notification_levels[:tracking])
end
def unread_results(list_opts={})
default_list(list_opts)
.where("tu.last_read_post_number < topics.highest_post_number")
.where("COALESCE(tu.notification_level, :regular) >= :tracking", regular: TopicUser.notification_levels[:regular], tracking: TopicUser.notification_levels[:tracking])
end
protected protected
def create_list(filter, list_opts={}) def create_list(filter, list_opts={})
@ -240,18 +253,6 @@ class TopicQuery
result result
end end
def new_results(list_opts={})
default_list(list_opts)
.where("topics.created_at >= :created_at", created_at: @user.treat_as_new_topic_start_date)
.where("tu.last_read_post_number IS NULL")
.where("COALESCE(tu.notification_level, :tracking) >= :tracking", tracking: TopicUser.notification_levels[:tracking])
end
def unread_results(list_opts={})
default_list(list_opts)
.where("tu.last_read_post_number < topics.highest_post_number")
.where("COALESCE(tu.notification_level, :regular) >= :tracking", regular: TopicUser.notification_levels[:regular], tracking: TopicUser.notification_levels[:tracking])
end
def random_suggested_results_for(topic, count, exclude_topic_ids) def random_suggested_results_for(topic, count, exclude_topic_ids)
results = default_list(unordered: true, per_page: count) results = default_list(unordered: true, per_page: count)

View File

@ -4,14 +4,14 @@ require_dependency 'post_destroyer'
describe Guardian do describe Guardian do
let(:user) { Fabricate(:user) } let(:user) { build(:user) }
let(:moderator) { Fabricate(:moderator) } let(:moderator) { build(:moderator) }
let(:admin) { Fabricate(:admin) } let(:admin) { build(:admin) }
let(:another_admin) { Fabricate(:another_admin) } let(:another_admin) { build(:another_admin) }
let(:coding_horror) { Fabricate(:coding_horror) } let(:coding_horror) { build(:coding_horror) }
let(:topic) { Fabricate(:topic, user: user) } let(:topic) { build(:topic, user: user) }
let(:post) { Fabricate(:post, topic: topic, user: topic.user) } let(:post) { build(:post, topic: topic, user: topic.user) }
it 'can be created without a user (not logged in)' do it 'can be created without a user (not logged in)' do
lambda { Guardian.new }.should_not raise_error lambda { Guardian.new }.should_not raise_error
@ -22,8 +22,8 @@ describe Guardian do
end end
describe 'post_can_act?' do describe 'post_can_act?' do
let(:post) { Fabricate(:post) } let(:post) { build(:post) }
let(:user) { Fabricate(:user) } let(:user) { build(:user) }
it "returns false when the user is nil" do it "returns false when the user is nil" do
Guardian.new(nil).post_can_act?(post, :like).should be_false Guardian.new(nil).post_can_act?(post, :like).should be_false
@ -220,6 +220,9 @@ describe Guardian do
describe 'a Post' do describe 'a Post' do
it 'correctly handles post visibility' do it 'correctly handles post visibility' do
post = Fabricate(:post)
topic = post.topic
Guardian.new(user).can_see?(post).should be_true Guardian.new(user).can_see?(post).should be_true
post.trash! post.trash!
@ -613,6 +616,7 @@ describe Guardian do
end end
it "returns false when trying to delete your own post that has already been deleted" do it "returns false when trying to delete your own post that has already been deleted" do
post = Fabricate(:post)
PostDestroyer.new(user, post).destroy PostDestroyer.new(user, post).destroy
post.reload post.reload
Guardian.new(user).can_delete?(post).should be_false Guardian.new(user).can_delete?(post).should be_false
@ -642,7 +646,7 @@ describe Guardian do
context 'a Category' do context 'a Category' do
let(:category) { Fabricate(:category, user: moderator) } let(:category) { build(:category, user: moderator) }
it 'returns false when not logged in' do it 'returns false when not logged in' do
Guardian.new.can_delete?(category).should be_false Guardian.new.can_delete?(category).should be_false
@ -667,8 +671,33 @@ describe Guardian do
end end
context 'can_ban?' do
it 'returns false when a user tries to ban another user' do
Guardian.new(user).can_ban?(coding_horror).should be_false
end
it 'returns true when an admin tries to ban another user' do
Guardian.new(admin).can_ban?(coding_horror).should be_true
end
it 'returns true when a moderator tries to ban another user' do
Guardian.new(moderator).can_ban?(coding_horror).should be_true
end
it 'returns false when staff tries to ban staff' do
Guardian.new(admin).can_ban?(moderator).should be_false
end
end
context 'a PostAction' do context 'a PostAction' do
let(:post_action) { PostAction.create(user_id: user.id, post_id: post.id, post_action_type_id: 1)} let(:post_action) {
user.id = 1
post.id = 1
a = PostAction.new(user_id: user.id, post_id: post.id, post_action_type_id: 1)
a.created_at = 1.minute.ago
a
}
it 'returns false when not logged in' do it 'returns false when not logged in' do
Guardian.new.can_delete?(post_action).should be_false Guardian.new.can_delete?(post_action).should be_false
@ -732,6 +761,8 @@ describe Guardian do
end end
it "allows an admin to grant a regular user access" do it "allows an admin to grant a regular user access" do
admin.id = 1
user.id = 2
Guardian.new(admin).can_grant_admin?(user).should be_true Guardian.new(admin).can_grant_admin?(user).should be_true
end end
end end
@ -750,6 +781,9 @@ describe Guardian do
end end
it "allows an admin to revoke another admin's access" do it "allows an admin to revoke another admin's access" do
admin.id = 1
another_admin.id = 2
Guardian.new(admin).can_revoke_admin?(another_admin).should be_true Guardian.new(admin).can_revoke_admin?(another_admin).should be_true
end end
end end

View File

@ -0,0 +1,34 @@
require 'spec_helper'
describe UserTrackingState do
let(:user) do
Fabricate(:user)
end
let(:post) do
Fabricate(:post)
end
let(:state) do
UserTrackingState.new(user)
end
it "correctly gets the list of new topics" do
state.new_list.should == []
state.unread_list.should == []
new_post = post
new_list = state.new_list
new_list.length.should == 1
new_list[0][0].should == post.topic.id
new_list[0][1].should be_within(1.second).of(post.topic.created_at)
state.unread_list.should == []
# read it
end
end