-
Notifications
You must be signed in to change notification settings - Fork 36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP] Create Update Feed #117
base: master
Are you sure you want to change the base?
Changes from 15 commits
29a28ca
5e49de5
4c90eb3
7b195b2
535cc1e
094f399
a9e44c1
212aece
2c8b3f2
267b290
29738fc
2391d72
2a4e099
e20ee34
6040002
962eeda
ab657c7
a9206c5
19c4ef4
8517657
d5707fa
11e7091
1840b30
fac47c3
cecbecf
20f9167
6bd7539
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
|
||
import Flow from require "lapis.flow" | ||
|
||
import Followings, Notifications from require "models" | ||
import Events, Followings, Notifications, TimelineEvents, Users from require "models" | ||
|
||
import assert_error from require "lapis.application" | ||
|
||
|
@@ -12,7 +12,8 @@ class FollowingsFlow extends Flow | |
super ... | ||
assert_error @current_user, "must be logged in" | ||
|
||
follow_object: (object, type) => | ||
follow_object: (object, type) => | ||
|
||
f = Followings\create { | ||
source_user_id: @current_user.id | ||
:object | ||
|
@@ -25,6 +26,9 @@ class FollowingsFlow extends Flow | |
Notifications\notify_for target_user, object, | ||
type, @current_user | ||
|
||
event = Events\create(@current_user, object, Events.event_types.subscription) | ||
TimelineEvents\deliver(@current_user, event) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. are you sure you're delivering the event to the right person? If I follow someone then I would expect an event to show up on their timeline? Additionally, we already have notifications for follows, do we want to also have timeline events? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It depends, maybe not sending the event to the followed timeline is better (and save some processing). Yeah, for followings, I guess the notifications that are present are enough. Do you an idea about what kind of stuff show be added to a timeline ? |
||
|
||
f | ||
|
||
unfollow_object: (object, type) => | ||
|
@@ -35,6 +39,13 @@ class FollowingsFlow extends Flow | |
type: Followings.types\for_db type | ||
} | ||
|
||
event = Events\find { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. so when you unfollow something you'd like all the events cleaned from your timeline (eg. they pushed 10 new updates to their module) Given that, you'll need to use the logic to do this is semi-complicated, so maybe it could be a method on a model, or in a timeline flow |
||
source_user_id: @current_user.id | ||
object_object_id: object.id | ||
object_object_type: Events\object_type_for_object object | ||
event_type: Events.event_types.subscription | ||
} | ||
|
||
return unless following | ||
|
||
if object.get_user | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
db = require "lapis.db" | ||
|
||
import Model, enum from require "lapis.db.model" | ||
import safe_insert from require "helpers.models" | ||
|
||
class Events extends Model | ||
@timestamp: true | ||
|
||
@event_types: enum { | ||
subscription: 1 | ||
bookmark: 2 | ||
update: 3 | ||
} | ||
|
||
@relations: { | ||
{"source_user", belongs_to: "Users"} | ||
{"object", polymorphic_belongs_to: { | ||
[1]: {"module", "Modules"} | ||
[2]: {"user", "Users"} | ||
}} | ||
} | ||
|
||
@create: (user, object, event_type) => | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's better to use an opts argument for all model creation, with assertions on required fields. I know there are some models in the codebase that use this style, but I've been updating them as I see them There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||
assert user, "missing event's user" | ||
assert object, "missing event's object" | ||
assert event_type, "missing event_type, events must have a type" | ||
|
||
opts = { | ||
:event_type | ||
source_user_id: user.id | ||
object_object_id: object.id | ||
object_object_type: @@object_type_for_object object | ||
} | ||
|
||
event = safe_insert @, opts | ||
|
||
return event | ||
|
||
delete: () => | ||
db.delete @@table_name!, { id: @id } |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
db = require "lapis.db" | ||
import Model from require "lapis.db.model" | ||
import Events, Followings, Users from require "models" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I prefer to avoid putting module requires on the top level, and instead in the methods, to prevent any issues with circular dependencies in the future There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||
|
||
class TimelineEvents extends Model | ||
@primary_key: { "user_id", "event_id" } | ||
|
||
@create: (user, event) => | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's better to use an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||
super { | ||
user_id: user.id | ||
event_id: event.id | ||
} | ||
|
||
@delete: (user, event) => | ||
db.delete @table_name!, { user_id: user.id, event_id: event.id } | ||
|
||
@deliver: (user, event) => | ||
switch event.event_type | ||
when Events.event_types.update | ||
followers = Followings\select "where object_id = ?", event.object_object_id | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||
|
||
for users in *followers | ||
follower_user = Users\find users.source_user_id | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. preload the user, use the relation There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||
@@create(follower_user, event) | ||
else | ||
@@create(user, event) | ||
|
||
if Events\model_for_object_type(event.object_object_type) == Users | ||
@@create(Users\find(event.object_object_id), event) | ||
|
||
@user_timeline: (user) => | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you probably want to either paginate this, or limit to something reasonable like 50 for the time being There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm limiting by 50 but I plan to add pagination soon. |
||
@@select "where user_id = ?", user.id |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,13 +6,13 @@ import request_as from require "spec.helpers" | |
factory = require "spec.factory" | ||
|
||
|
||
import Modules, Versions, Followings, Users, Notifications, NotificationObjects from require "models" | ||
import Modules, Versions, Events, Followings, Users, Notifications, NotificationObjects from require "spec.models" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||
|
||
describe "applications.modules", -> | ||
use_test_server! | ||
|
||
before_each -> | ||
truncate_tables Modules, Versions, Followings, Users, Notifications, NotificationObjects | ||
truncate_tables Events, Modules, Versions, Followings, Users, Notifications, NotificationObjects | ||
|
||
it "follows module", -> | ||
current_user = factory.Users! | ||
|
@@ -21,8 +21,13 @@ describe "applications.modules", -> | |
assert.same 302, status | ||
|
||
followings = Followings\select! | ||
events = Events\select! | ||
user_timeline = current_user\timeline! | ||
|
||
assert.same 1, #followings | ||
assert.same 1, #events | ||
assert.same 1, #user_timeline | ||
|
||
following = unpack followings | ||
|
||
assert.same current_user.id, following.source_user_id | ||
|
@@ -44,7 +49,12 @@ describe "applications.modules", -> | |
assert.same 302, status | ||
|
||
followings = Followings\select! | ||
events = Events\select! | ||
user_timeline = current_user\timeline! | ||
|
||
assert.same 0, #followings | ||
assert.same 0, #events | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This fails because of the comment mentioned above in flows/followings.moon There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Apparently "only" in tests the event created in |
||
assert.same 0, #user_timeline | ||
|
||
current_user\refresh! | ||
mod\refresh! | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import use_test_env from require "lapis.spec" | ||
|
||
factory = require "spec.factory" | ||
|
||
import | ||
Modules | ||
Users from require "spec.models" | ||
|
||
import Events, TimelineEvents from require "models" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. convert this to require from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed. |
||
|
||
describe "models.events", -> | ||
use_test_env! | ||
|
||
it "creates an event of user following user", -> | ||
user = factory.Users! | ||
followed_user = factory.Users! | ||
|
||
event = Events\create(user, followed_user, Events.event_types.subscription) | ||
user_timeline = user\timeline! | ||
|
||
assert.same user.id, event.source_user_id | ||
assert.same followed_user.id, event.object_object_id | ||
assert.same event.event_type, Events.event_types.subscription | ||
|
||
assert.same, #user_timeline, 1 | ||
|
||
it "creates an event of user following a module", -> | ||
user = factory.Users! | ||
module = factory.Modules! | ||
|
||
event = Events\create(user, module, Events.event_types.subscription) | ||
user_timeline = user\timeline! | ||
|
||
assert.same user.id, event.source_user_id | ||
assert.same module.id, event.object_object_id | ||
assert.same event.event_type, Events.event_types.subscription | ||
|
||
assert.same, #user_timeline, 1 | ||
|
||
it "creates an event of user starring a module", -> | ||
user = factory.Users! | ||
module = factory.Modules! | ||
|
||
event = Events\create(user, module, Events.event_types.bookmark) | ||
user_timeline = user\timeline! | ||
|
||
assert.same user.id, event.source_user_id | ||
assert.same module.id, event.object_object_id | ||
assert.same event.event_type, Events.event_types.bookmark | ||
|
||
assert.same, #user_timeline, 1 | ||
|
||
it "deletes an event", -> | ||
user = factory.Users! | ||
module = factory.Modules! | ||
|
||
event = Events\create(user, module, Events.event_types.bookmark) | ||
event_id = event.id | ||
|
||
event\delete! | ||
|
||
assert.same nil, Events\find user.id, event_id | ||
assert.same 0, #user\timeline! |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import Events, Modules, Users from require "models" | ||
|
||
class TimelineEvents extends require "widgets.base" | ||
@needs: { | ||
"modules" | ||
} | ||
|
||
inner_content: => | ||
ul -> | ||
for event in *@current_user\timeline! | ||
row_event = Events\find(event.event_id) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. both of these will cause the n+1 queries issue: http://leafo.net/guides/postgresql-preloading.html#avoiding-n-and-1-queries the solution in this case is to use the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm currently preloading it in |
||
user = Users\find(row_event.source_user_id) | ||
|
||
|
||
message = switch row_event.event_type | ||
when Events.event_types.subscription | ||
" followed " | ||
when Events.event_type.bookmark | ||
" starred " | ||
when Events.event_type.update | ||
" delivered a new version of " | ||
else | ||
"" | ||
li -> | ||
span class: "author", -> | ||
a href: @url_for("user_profile", user: user.slug), user\name_for_display! | ||
text message | ||
|
||
switch Events\model_for_object_type(row_event.object_object_type) | ||
when Modules | ||
mod = Modules\find row_event.object_object_id | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. needs to be preloaded There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Those are broken because the nested preload of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||
a { | ||
class: "title", | ||
href: @url_for("module", user: user.slug, module: mod.name) | ||
}, mod\name_for_display! | ||
when Users | ||
usr = Users\find row_event.object_object_id | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. needs to be preloaded There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||
a { | ||
class: "title", | ||
href: @url_for("user_profile", user: usr.slug) | ||
}, usr\name_for_display! | ||
else | ||
"" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
indentation looks messed up
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed.