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
remote: https://github.com/SamSaffron/message_bus
revision: e5654728b1c1b97fa5c7bb79fd689b31cec3f01d
revision: 031a107bbe6e468caa67ff540485d70230d1c362
specs:
message_bus (0.0.2)
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?
def can_ban?(user)
return false if user.blank?
return false unless @user.try(:admin?)
return false if user.admin?
true
is_staff? && user && !user.staff?
end
alias :can_deactivate? :can_ban?
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)}
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
def create_list(filter, list_opts={})
@ -240,18 +253,6 @@ class TopicQuery
result
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)
results = default_list(unordered: true, per_page: count)

View File

@ -4,14 +4,14 @@ require_dependency 'post_destroyer'
describe Guardian do
let(:user) { Fabricate(:user) }
let(:moderator) { Fabricate(:moderator) }
let(:admin) { Fabricate(:admin) }
let(:another_admin) { Fabricate(:another_admin) }
let(:coding_horror) { Fabricate(:coding_horror) }
let(:user) { build(:user) }
let(:moderator) { build(:moderator) }
let(:admin) { build(:admin) }
let(:another_admin) { build(:another_admin) }
let(:coding_horror) { build(:coding_horror) }
let(:topic) { Fabricate(:topic, user: user) }
let(:post) { Fabricate(:post, topic: topic, user: topic.user) }
let(:topic) { build(:topic, user: user) }
let(:post) { build(:post, topic: topic, user: topic.user) }
it 'can be created without a user (not logged in)' do
lambda { Guardian.new }.should_not raise_error
@ -22,8 +22,8 @@ describe Guardian do
end
describe 'post_can_act?' do
let(:post) { Fabricate(:post) }
let(:user) { Fabricate(:user) }
let(:post) { build(:post) }
let(:user) { build(:user) }
it "returns false when the user is nil" do
Guardian.new(nil).post_can_act?(post, :like).should be_false
@ -220,6 +220,9 @@ describe Guardian do
describe 'a Post' do
it 'correctly handles post visibility' do
post = Fabricate(:post)
topic = post.topic
Guardian.new(user).can_see?(post).should be_true
post.trash!
@ -613,6 +616,7 @@ describe Guardian do
end
it "returns false when trying to delete your own post that has already been deleted" do
post = Fabricate(:post)
PostDestroyer.new(user, post).destroy
post.reload
Guardian.new(user).can_delete?(post).should be_false
@ -642,7 +646,7 @@ describe Guardian 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
Guardian.new.can_delete?(category).should be_false
@ -667,8 +671,33 @@ describe Guardian do
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
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
Guardian.new.can_delete?(post_action).should be_false
@ -732,6 +761,8 @@ describe Guardian do
end
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
end
end
@ -750,6 +781,9 @@ describe Guardian do
end
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
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