diff --git a/app/assets/javascripts/discourse/controllers/list_controller.js b/app/assets/javascripts/discourse/controllers/list_controller.js
index 8282101fa73a6..b69e37ede0c16 100644
--- a/app/assets/javascripts/discourse/controllers/list_controller.js
+++ b/app/assets/javascripts/discourse/controllers/list_controller.js
@@ -35,7 +35,7 @@ Discourse.ListController = Discourse.Controller.extend({
var listController = this;
this.set('loading', true);
- var trackingState = Discourse.get('currentUser.userTrackingState');
+ var trackingState = Discourse.TopicTrackingState.current();
if (filterMode === 'categories') {
return Discourse.CategoryList.list(filterMode).then(function(items) {
diff --git a/app/assets/javascripts/discourse/controllers/list_topics_controller.js b/app/assets/javascripts/discourse/controllers/list_topics_controller.js
index 3cf53a7de375f..90425fe1a35cc 100644
--- a/app/assets/javascripts/discourse/controllers/list_topics_controller.js
+++ b/app/assets/javascripts/discourse/controllers/list_topics_controller.js
@@ -55,7 +55,7 @@ Discourse.ListTopicsController = Discourse.ObjectController.extend({
// Show newly inserted topics
showInserted: function(e) {
- var tracker = Discourse.get('currentUser.userTrackingState');
+ var tracker = Discourse.TopicTrackingState.current();
// Move inserted into topics
this.get('content').loadBefore(tracker.get('newIncoming'));
diff --git a/app/assets/javascripts/discourse/models/nav_item.js b/app/assets/javascripts/discourse/models/nav_item.js
index fa92786089d38..4b1e9e28e3b53 100644
--- a/app/assets/javascripts/discourse/models/nav_item.js
+++ b/app/assets/javascripts/discourse/models/nav_item.js
@@ -10,7 +10,10 @@ var validNavNames = ['latest', 'hot', 'categories', 'category', 'favorited', 'un
var validAnon = ['latest', 'hot', 'categories', 'category'];
Discourse.NavItem = Discourse.Model.extend({
- userTrackingStateBinding: Ember.Binding.oneWay('Discourse.currentUser.userTrackingState.messageCount'),
+ topicTrackingState: function(){
+ return Discourse.TopicTrackingState.current();
+ }.property(),
+
categoryName: function() {
var split = this.get('name').split('/');
return split[0] === 'category' ? split[1] : null;
@@ -25,11 +28,11 @@ Discourse.NavItem = Discourse.Model.extend({
}.property('name'),
count: function() {
- var state = Discourse.get('currentUser.userTrackingState');
+ var state = this.get('topicTrackingState');
if (state) {
return state.lookupCount(this.get('name'));
}
- }.property('userTrackingState')
+ }.property('topicTrackingState.messageCount')
});
Discourse.NavItem.reopenClass({
diff --git a/app/assets/javascripts/discourse/models/user_tracking_state.js b/app/assets/javascripts/discourse/models/topic_tracking_state.js
similarity index 79%
rename from app/assets/javascripts/discourse/models/user_tracking_state.js
rename to app/assets/javascripts/discourse/models/topic_tracking_state.js
index a0297ad20626e..f0420844b3058 100644
--- a/app/assets/javascripts/discourse/models/user_tracking_state.js
+++ b/app/assets/javascripts/discourse/models/topic_tracking_state.js
@@ -1,4 +1,4 @@
-Discourse.UserTrackingState = Discourse.Model.extend({
+Discourse.TopicTrackingState = Discourse.Model.extend({
messageCount: 0,
init: function(){
@@ -17,7 +17,7 @@ Discourse.UserTrackingState = Discourse.Model.extend({
tracker.removeTopic(data.topic_id);
}
- if (data.message_type === "new_topic") {
+ if (data.message_type === "new_topic" || data.message_type === "unread") {
tracker.states["t" + data.topic_id] = data.payload;
tracker.notify(data);
}
@@ -26,7 +26,10 @@ Discourse.UserTrackingState = Discourse.Model.extend({
};
Discourse.MessageBus.subscribe("/new", process);
- Discourse.MessageBus.subscribe("/unread/" + Discourse.currentUser.id, process);
+ var currentUser = Discourse.User.current();
+ if(currentUser) {
+ Discourse.MessageBus.subscribe("/unread/" + currentUser.id, process);
+ }
},
notify: function(data){
@@ -62,6 +65,8 @@ Discourse.UserTrackingState = Discourse.Model.extend({
sync: function(list, filter){
var tracker = this;
+ if(!list || !list.topics || !list.topics.length) { return; }
+
if(filter === "new" && !list.more_topics_url){
// scrub all new rows and reload from list
$.each(this.states, function(){
@@ -88,8 +93,10 @@ Discourse.UserTrackingState = Discourse.Model.extend({
if(topic.unseen) {
row.last_read_post_number = null;
} else {
- row.last_read_post_number = topic.last_read_post_number;
+ // subtle issue here
+ row.last_read_post_number = topic.last_read_post_number || topic.highest_post_number;
}
+
row.highest_post_number = topic.highest_post_number;
if (topic.category) {
row.category_name = topic.category.name;
@@ -97,6 +104,8 @@ Discourse.UserTrackingState = Discourse.Model.extend({
if (row.last_read_post_number === null || row.highest_post_number > row.last_read_post_number) {
tracker.states["t" + topic.id] = row;
+ } else {
+ delete tracker.states["t" + topic.id];
}
});
@@ -151,18 +160,28 @@ Discourse.UserTrackingState = Discourse.Model.extend({
// not exposed
var states = this.states;
- data.each(function(row){
- states["t" + row.topic_id] = row;
- });
+ if(data) {
+ data.each(function(row){
+ states["t" + row.topic_id] = row;
+ });
+ }
}
});
-Discourse.UserTrackingState.reopenClass({
+Discourse.TopicTrackingState.reopenClass({
createFromStates: function(data){
- var instance = Discourse.UserTrackingState.create();
+ var instance = Discourse.TopicTrackingState.create();
instance.loadStates(data);
instance.establishChannels();
return instance;
+ },
+ current: function(){
+ if (!this.tracker) {
+ var data = PreloadStore.get('topicTrackingStates');
+ this.tracker = this.createFromStates(data);
+ PreloadStore.remove('topicTrackingStates');
+ }
+ return this.tracker;
}
});
diff --git a/app/assets/javascripts/discourse/routes/application_route.js b/app/assets/javascripts/discourse/routes/application_route.js
deleted file mode 100644
index 337f3df6ec688..0000000000000
--- a/app/assets/javascripts/discourse/routes/application_route.js
+++ /dev/null
@@ -1,24 +0,0 @@
-/**
- The base Application route
-
- @class ApplicationRoute
- @extends Discourse.Route
- @namespace Discourse
- @module Discourse
-**/
-Discourse.ApplicationRoute = Discourse.Route.extend({
- setupController: function(controller) {
- Discourse.set('site', Discourse.Site.create(PreloadStore.get('site')));
- var currentUser = PreloadStore.get('currentUser');
- if (currentUser) {
- var states = currentUser.user_tracking_states;
- currentUser.user_tracking_states = null;
-
- Discourse.set('currentUser', Discourse.User.create(currentUser));
- Discourse.set('currentUser.userTrackingState', Discourse.UserTrackingState.createFromStates(states));
- }
- // make sure we delete preloaded data
- PreloadStore.remove('site');
- PreloadStore.remove('currentUser');
- }
-});
diff --git a/app/assets/javascripts/discourse/templates/list/topics.js.handlebars b/app/assets/javascripts/discourse/templates/list/topics.js.handlebars
index 7ad5eaa802e99..6ecb51f44c20c 100644
--- a/app/assets/javascripts/discourse/templates/list/topics.js.handlebars
+++ b/app/assets/javascripts/discourse/templates/list/topics.js.handlebars
@@ -28,12 +28,12 @@
- {{#if Discourse.currentUser.userTrackingState.hasIncoming}}
+ {{#if view.topicTrackingState.hasIncoming}}
- {{countI18n new_topics_inserted countBinding="Discourse.currentUser.userTrackingState.incomingCount"}}
+ {{countI18n new_topics_inserted countBinding="view.topicTrackingState.incomingCount"}}
{{i18n show_new_topics}}
|
diff --git a/app/assets/javascripts/discourse/views/list/list_topics_view.js b/app/assets/javascripts/discourse/views/list/list_topics_view.js
index 676bab9f00adc..f48d3d82451f0 100644
--- a/app/assets/javascripts/discourse/views/list/list_topics_view.js
+++ b/app/assets/javascripts/discourse/views/list/list_topics_view.js
@@ -14,6 +14,9 @@ Discourse.ListTopicsView = Discourse.View.extend(Discourse.Scrolling, {
listBinding: 'controller.model',
loadedMore: false,
currentTopicId: null,
+ topicTrackingState: function() {
+ return Discourse.TopicTrackingState.current();
+ }.property(),
willDestroyElement: function() {
this.unbindScrolling();
@@ -42,8 +45,11 @@ Discourse.ListTopicsView = Discourse.View.extend(Discourse.Scrolling, {
},
showTable: function() {
- return this.get('list.topics').length > 0 || Discourse.get('currentUser.userTrackingState.hasIncoming');
- }.property('list.topics','Discourse.currentUser.userTrackingState.hasIncoming'),
+ var topics = this.get('list.topics');
+ if(topics) {
+ return this.get('list.topics').length > 0 || this.get('topicTrackingState.hasIncoming');
+ }
+ }.property('list.topics','topicTrackingState.hasIncoming'),
loadMore: function() {
var listTopicsView = this;
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 638f605484a2f..37d53f3c55cd6 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -107,6 +107,9 @@ def preload_json
if current_user.present?
store_preloaded("currentUser", MultiJson.dump(CurrentUserSerializer.new(current_user, root: false)))
+
+ serializer = ActiveModel::ArraySerializer.new(TopicTrackingState.report([current_user.id]), each_serializer: TopicTrackingStateSerializer)
+ store_preloaded("topicTrackingStates", MultiJson.dump(serializer))
end
store_preloaded("siteSettings", SiteSetting.client_settings_json)
end
diff --git a/app/models/category.rb b/app/models/category.rb
index bdeacedb57d6a..971d5d0fb5879 100644
--- a/app/models/category.rb
+++ b/app/models/category.rb
@@ -133,6 +133,12 @@ def allow(group)
end
end
+ def secure_group_ids
+ if self.secure
+ groups.pluck("groups.id")
+ end
+ end
+
end
# == Schema Information
diff --git a/app/models/user_tracking_state.rb b/app/models/topic_tracking_state.rb
similarity index 67%
rename from app/models/user_tracking_state.rb
rename to app/models/topic_tracking_state.rb
index 50c8f8fce5602..8239f717a6cef 100644
--- a/app/models/user_tracking_state.rb
+++ b/app/models/topic_tracking_state.rb
@@ -3,7 +3,7 @@
# the allows end users to always know which topics have unread posts in them
# and which topics are new
-class UserTrackingState
+class TopicTrackingState
include ActiveModel::SerializerSupport
@@ -11,16 +11,53 @@ class UserTrackingState
attr_accessor :user_id, :topic_id, :highest_post_number, :last_read_post_number, :created_at, :category_name
- MessageBus.client_filter(CHANNEL) do |user_id, message|
- if user_id
- UserTrackingState.new(User.find(user_id)).filter(message)
- else
- nil
+ def self.publish_new(topic)
+
+ message = {
+ topic_id: topic.id,
+ message_type: "new_topic",
+ payload: {
+ last_read_post_number: nil,
+ highest_post_number: 1,
+ created_at: topic.created_at,
+ topic_id: topic.id
+ }
+ }
+
+ group_ids = topic.category && topic.category.secure_group_ids
+
+ MessageBus.publish("/new", message.as_json, group_ids: group_ids)
+ publish_read(topic.id, 1, topic.user_id)
+ end
+
+ def self.publish_unread(post)
+ # TODO at high scale we are going to have to defer this,
+ # perhaps cut down to users that are around in the last 7 days as well
+ #
+ group_ids = post.topic.category && post.topic.category.secure_group_ids
+
+ TopicUser
+ .tracking(post.topic_id)
+ .select([:user_id,:last_read_post_number])
+ .each do |tu|
+
+ message = {
+ topic_id: post.topic_id,
+ message_type: "unread",
+ payload: {
+ last_read_post_number: tu.last_read_post_number,
+ highest_post_number: post.post_number,
+ created_at: post.created_at,
+ topic_id: post.topic_id
+ }
+ }
+
+ MessageBus.publish("/unread/#{tu.user_id}", message.as_json, group_ids: group_ids)
+
end
end
- def self.trigger_change(topic_id, post_number, user_id=nil)
- MessageBus.publish(CHANNEL, "CHANGE", user_ids: [user_id].compact)
+ def self.publish_read(topic_id, highest_post_number, user_id)
end
def self.treat_as_new_topic_clause
@@ -76,7 +113,7 @@ def self.report(user_ids, topic_id = nil)
end
SqlBuilder.new(sql)
- .map_exec(UserTrackingState, user_ids: user_ids, topic_id: topic_id)
+ .map_exec(TopicTrackingState, user_ids: user_ids, topic_id: topic_id)
end
diff --git a/app/models/topic_user.rb b/app/models/topic_user.rb
index e0d98d8be420c..e32a13bdf6bfe 100644
--- a/app/models/topic_user.rb
+++ b/app/models/topic_user.rb
@@ -5,6 +5,12 @@ class TopicUser < ActiveRecord::Base
scope :starred_since, lambda { |sinceDaysAgo| where('starred_at > ?', sinceDaysAgo.days.ago) }
scope :by_date_starred, group('date(starred_at)').order('date(starred_at)')
+ scope :tracking, lambda { |topic_id|
+ where(topic_id: topic_id)
+ .where("COALESCE(topic_users.notification_level, :regular) >= :tracking",
+ regular: TopicUser.notification_levels[:regular], tracking: TopicUser.notification_levels[:tracking])
+ }
+
# Class methods
class << self
diff --git a/app/models/user.rb b/app/models/user.rb
index 02f84a31ef3aa..7fde363025142 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -576,9 +576,6 @@ def flag_linked_posts_as_spam
end
end
- def user_tracking_states
- UserTrackingState.report([self.id])
- end
protected
diff --git a/app/serializers/current_user_serializer.rb b/app/serializers/current_user_serializer.rb
index dd4747116f99f..d34826f0bc9e7 100644
--- a/app/serializers/current_user_serializer.rb
+++ b/app/serializers/current_user_serializer.rb
@@ -14,10 +14,6 @@ class CurrentUserSerializer < BasicUserSerializer
:external_links_in_new_tab,
:trust_level
- has_many :user_tracking_states, serializer: UserTrackingStateSerializer, embed: :objects
-
- # we probably want to move this into site, but that json is cached so hanging it off current user seems okish
-
def include_site_flagged_posts_count?
object.staff?
end
diff --git a/app/serializers/user_tracking_state_serializer.rb b/app/serializers/topic_tracking_state_serializer.rb
similarity index 63%
rename from app/serializers/user_tracking_state_serializer.rb
rename to app/serializers/topic_tracking_state_serializer.rb
index 1abd7c08b8e15..b4df77a025051 100644
--- a/app/serializers/user_tracking_state_serializer.rb
+++ b/app/serializers/topic_tracking_state_serializer.rb
@@ -1,3 +1,3 @@
-class UserTrackingStateSerializer < ApplicationSerializer
+class TopicTrackingStateSerializer < ApplicationSerializer
attributes :topic_id, :highest_post_number, :last_read_post_number, :created_at, :category_name
end
diff --git a/lib/post_creator.rb b/lib/post_creator.rb
index 6962ef1c36bac..115285488bdfb 100644
--- a/lib/post_creator.rb
+++ b/lib/post_creator.rb
@@ -123,7 +123,6 @@ def create
@user.last_posted_at = post.created_at
@user.save!
-
if post.post_number > 1
MessageBus.publish("/topic/#{post.topic_id}",{
id: post.id,
@@ -142,12 +141,14 @@ def create
post.save_reply_relationships
end
- # We need to enqueue jobs after the transaction. Otherwise they might begin before the data has
- # been comitted.
- topic_id = @opts[:topic_id] || topic.try(:id)
- Jobs.enqueue(:feature_topic_users, topic_id: topic.id) if topic_id.present?
- if post
+ if post && !post.errors.present?
+
+ # We need to enqueue jobs after the transaction. Otherwise they might begin before the data has
+ # been comitted.
+ topic_id = @opts[:topic_id] || topic.try(:id)
+ Jobs.enqueue(:feature_topic_users, topic_id: topic.id) if topic_id.present?
post.trigger_post_process
+ after_post_create(post)
after_topic_create(topic) if new_topic
end
@@ -164,7 +165,13 @@ def self.create(user, opts)
def secure_group_ids(topic)
@secure_group_ids ||= if topic.category && topic.category.secure?
- topic.category.groups.select("groups.id").map{|g| g.id}
+ topic.category.secure_group_ids
+ end
+ end
+
+ def after_post_create(post)
+ if post.post_number > 1
+ TopicTrackingState.publish_unread(post)
end
end
@@ -177,21 +184,8 @@ def after_topic_create(topic)
topic.posters = topic.posters_summary
topic.posts_count = 1
- topic_json = TopicListItemSerializer.new(topic).as_json
-
- message = {
- topic_id: topic.id,
- message_type: "new_topic",
- payload: {
- last_read_post_number: nil,
- topic_id: topic.id
- }
- }
-
- group_ids = secure_group_ids(topic)
- MessageBus.publish("/new", message.as_json, group_ids: group_ids)
- # TODO post creator should get an unread
+ TopicTrackingState.publish_new(topic)
end
def create_topic
diff --git a/script/alice.txt b/script/alice.txt
new file mode 100644
index 0000000000000..5f3f07427df20
--- /dev/null
+++ b/script/alice.txt
@@ -0,0 +1,122 @@
+Alice was beginning to get very tired of sitting by her sister on the
+bank, and of having nothing to do: once or twice she had peeped into the
+book her sister was reading, but it had no pictures or conversations in
+it, 'and what is the use of a book,' thought Alice 'without pictures or
+conversation?'
+
+So she was considering in her own mind (as well as she could, for the
+hot day made her feel very sleepy and stupid), whether the pleasure
+of making a daisy-chain would be worth the trouble of getting up and
+picking the daisies, when suddenly a White Rabbit with pink eyes ran
+close by her.
+
+There was nothing so VERY remarkable in that; nor did Alice think it so
+VERY much out of the way to hear the Rabbit say to itself, 'Oh dear!
+Oh dear! I shall be late!' (when she thought it over afterwards, it
+occurred to her that she ought to have wondered at this, but at the time
+it all seemed quite natural); but when the Rabbit actually TOOK A WATCH
+OUT OF ITS WAISTCOAT-POCKET, and looked at it, and then hurried on,
+Alice started to her feet, for it flashed across her mind that she had
+never before seen a rabbit with either a waistcoat-pocket, or a watch
+to take out of it, and burning with curiosity, she ran across the field
+after it, and fortunately was just in time to see it pop down a large
+rabbit-hole under the hedge.
+
+In another moment down went Alice after it, never once considering how
+in the world she was to get out again.
+
+The rabbit-hole went straight on like a tunnel for some way, and then
+dipped suddenly down, so suddenly that Alice had not a moment to think
+about stopping herself before she found herself falling down a very deep
+well.
+
+Either the well was very deep, or she fell very slowly, for she had
+plenty of time as she went down to look about her and to wonder what was
+going to happen next. First, she tried to look down and make out what
+she was coming to, but it was too dark to see anything; then she
+looked at the sides of the well, and noticed that they were filled with
+cupboards and book-shelves; here and there she saw maps and pictures
+hung upon pegs. She took down a jar from one of the shelves as
+she passed; it was labelled 'ORANGE MARMALADE', but to her great
+disappointment it was empty: she did not like to drop the jar for fear
+of killing somebody, so managed to put it into one of the cupboards as
+she fell past it.
+
+'Well!' thought Alice to herself, 'after such a fall as this, I shall
+think nothing of tumbling down stairs! How brave they'll all think me at
+home! Why, I wouldn't say anything about it, even if I fell off the top
+of the house!' (Which was very likely true.)
+
+Down, down, down. Would the fall NEVER come to an end! 'I wonder how
+many miles I've fallen by this time?' she said aloud. 'I must be getting
+somewhere near the centre of the earth. Let me see: that would be four
+thousand miles down, I think--' (for, you see, Alice had learnt several
+things of this sort in her lessons in the schoolroom, and though this
+was not a VERY good opportunity for showing off her knowledge, as there
+was no one to listen to her, still it was good practice to say it over)
+'--yes, that's about the right distance--but then I wonder what Latitude
+or Longitude I've got to?' (Alice had no idea what Latitude was, or
+Longitude either, but thought they were nice grand words to say.)
+
+Presently she began again. 'I wonder if I shall fall right THROUGH the
+earth! How funny it'll seem to come out among the people that walk with
+their heads downward! The Antipathies, I think--' (she was rather glad
+there WAS no one listening, this time, as it didn't sound at all the
+right word) '--but I shall have to ask them what the name of the country
+is, you know. Please, Ma'am, is this New Zealand or Australia?' (and
+she tried to curtsey as she spoke--fancy CURTSEYING as you're falling
+through the air! Do you think you could manage it?) 'And what an
+ignorant little girl she'll think me for asking! No, it'll never do to
+ask: perhaps I shall see it written up somewhere.'
+
+Down, down, down. There was nothing else to do, so Alice soon began
+talking again. 'Dinah'll miss me very much to-night, I should think!'
+(Dinah was the cat.) 'I hope they'll remember her saucer of milk at
+tea-time. Dinah my dear! I wish you were down here with me! There are no
+mice in the air, I'm afraid, but you might catch a bat, and that's very
+like a mouse, you know. But do cats eat bats, I wonder?' And here Alice
+began to get rather sleepy, and went on saying to herself, in a dreamy
+sort of way, 'Do cats eat bats? Do cats eat bats?' and sometimes, 'Do
+bats eat cats?' for, you see, as she couldn't answer either question,
+it didn't much matter which way she put it. She felt that she was dozing
+off, and had just begun to dream that she was walking hand in hand with
+Dinah, and saying to her very earnestly, 'Now, Dinah, tell me the truth:
+did you ever eat a bat?' when suddenly, thump! thump! down she came upon
+a heap of sticks and dry leaves, and the fall was over.
+
+Alice was not a bit hurt, and she jumped up on to her feet in a moment:
+she looked up, but it was all dark overhead; before her was another
+long passage, and the White Rabbit was still in sight, hurrying down it.
+There was not a moment to be lost: away went Alice like the wind, and
+was just in time to hear it say, as it turned a corner, 'Oh my ears
+and whiskers, how late it's getting!' She was close behind it when she
+turned the corner, but the Rabbit was no longer to be seen: she found
+herself in a long, low hall, which was lit up by a row of lamps hanging
+from the roof.
+
+There were doors all round the hall, but they were all locked; and when
+Alice had been all the way down one side and up the other, trying every
+door, she walked sadly down the middle, wondering how she was ever to
+get out again.
+
+Suddenly she came upon a little three-legged table, all made of solid
+glass; there was nothing on it except a tiny golden key, and Alice's
+first thought was that it might belong to one of the doors of the hall;
+but, alas! either the locks were too large, or the key was too small,
+but at any rate it would not open any of them. However, on the second
+time round, she came upon a low curtain she had not noticed before, and
+behind it was a little door about fifteen inches high: she tried the
+little golden key in the lock, and to her great delight it fitted!
+
+Alice opened the door and found that it led into a small passage, not
+much larger than a rat-hole: she knelt down and looked along the passage
+into the loveliest garden you ever saw. How she longed to get out of
+that dark hall, and wander about among those beds of bright flowers and
+those cool fountains, but she could not even get her head through the
+doorway; 'and even if my head would go through,' thought poor Alice, 'it
+would be of very little use without my shoulders. Oh, how I wish I could
+shut up like a telescope! I think I could, if I only know how to begin.'
+For, you see, so many out-of-the-way things had happened lately,
+that Alice had begun to think that very few things indeed were really
+impossible.
+
diff --git a/script/user_simulator.rb b/script/user_simulator.rb
new file mode 100644
index 0000000000000..0005a6ebaec0e
--- /dev/null
+++ b/script/user_simulator.rb
@@ -0,0 +1,60 @@
+# used during local testing, simulates a user active on the site.
+#
+# by default 1 new topic every 30 sec, 1 reply to last topic every 30 secs
+
+require 'optparse'
+require 'gabbler'
+
+user_id = nil
+
+def sentence
+ @gabbler ||= Gabbler.new.tap do |gabbler|
+ story = File.read(File.dirname(__FILE__) + "/alice.txt")
+ gabbler.learn(story)
+ end
+
+ sentence = ""
+ until sentence.length > 800 do
+ sentence << @gabbler.sentence
+ sentence << "\n"
+ end
+ sentence
+end
+
+OptionParser.new do |opts|
+ opts.banner = "Usage: ruby user_simulator.rb [options]"
+ opts.on("-u", "--user NUMBER", "user id") do |u|
+ user_id = u.to_i
+ end
+end.parse!
+
+unless user_id
+ puts "user must be specified"
+ exit
+end
+
+require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
+
+unless Rails.env.development?
+ puts "Bad idea to run a script that inserts random posts in any non development environment"
+ exit
+end
+
+
+user = User.find(user_id)
+last_topics = Topic.order('id desc').limit(10).pluck(:id)
+
+puts "Simulating activity for user id #{user.id}: #{user.name}"
+
+
+while true
+ # puts "Creating a random topi"
+
+ # category = Category.where(secure: false).order('random()').first
+ # PostCreator.create(user, raw: sentence, title: sentence[0..50].strip, category: category.name)
+
+ puts "creating random reply"
+ PostCreator.create(user, raw: sentence, topic_id: last_topics.sample)
+
+ sleep 3
+end
diff --git a/spec/models/user_tracking_state_spec.rb b/spec/models/topic_tracking_state_spec.rb
similarity index 65%
rename from spec/models/user_tracking_state_spec.rb
rename to spec/models/topic_tracking_state_spec.rb
index cb5dd6872cc38..af9348736f71d 100644
--- a/spec/models/user_tracking_state_spec.rb
+++ b/spec/models/topic_tracking_state_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe UserTrackingState do
+describe TopicTrackingState do
let(:user) do
Fabricate(:user)
@@ -10,13 +10,18 @@
Fabricate(:post)
end
+ it "can correctly publish unread" do
+ # TODO setup stuff and look at messages
+ TopicTrackingState.publish_unread(post)
+ end
+
it "correctly gets the tracking state" do
- report = UserTrackingState.report([user.id])
+ report = TopicTrackingState.report([user.id])
report.length.should == 0
new_post = post
- report = UserTrackingState.report([user.id])
+ report = TopicTrackingState.report([user.id])
report.length.should == 1
row = report[0]
@@ -27,15 +32,15 @@
row.user_id.should == user.id
# lets not leak out random users
- UserTrackingState.report([post.user_id]).should be_empty
+ TopicTrackingState.report([post.user_id]).should be_empty
# lets not return anything if we scope on non-existing topic
- UserTrackingState.report([user.id], post.topic_id + 1).should be_empty
+ TopicTrackingState.report([user.id], post.topic_id + 1).should be_empty
# when we reply the poster should have an unread row
Fabricate(:post, user: user, topic: post.topic)
- report = UserTrackingState.report([post.user_id, user.id])
+ report = TopicTrackingState.report([post.user_id, user.id])
report.length.should == 1
row = report[0]
@@ -51,6 +56,6 @@
post.topic.category_id = category.id
post.topic.save
- UserTrackingState.report([post.user_id, user.id]).count.should == 0
+ TopicTrackingState.report([post.user_id, user.id]).count.should == 0
end
end
diff --git a/spec/models/topic_user_spec.rb b/spec/models/topic_user_spec.rb
index 0a67ea6d3efb8..927d45b0593a6 100644
--- a/spec/models/topic_user_spec.rb
+++ b/spec/models/topic_user_spec.rb
@@ -216,6 +216,14 @@
end
+ it "can scope by tracking" do
+ TopicUser.create!(user_id: 1, topic_id: 1, notification_level: TopicUser.notification_levels[:tracking])
+ TopicUser.create!(user_id: 2, topic_id: 1, notification_level: TopicUser.notification_levels[:watching])
+ TopicUser.create!(user_id: 3, topic_id: 1, notification_level: TopicUser.notification_levels[:regular])
+
+ TopicUser.tracking(1).count.should == 2
+ TopicUser.tracking(10).count.should == 0
+ end
it "is able to self heal" do
p1 = Fabricate(:post)