Skip to content
Browse files

Add mentions timeline

  • Loading branch information...
1 parent 5522e95 commit 284682b7aa518f38ea7912da74a2d2c38f695449 @felixclack committed
Showing with 81 additions and 39 deletions.
  1. +1 −0 README.md
  2. +2 −0 lib/timeline/actor.rb
  3. +56 −30 lib/timeline/track.rb
  4. +1 −1 lib/timeline/version.rb
  5. +1 −1 spec/spec_helper.rb
  6. +20 −7 spec/track_spec.rb
View
1 README.md
@@ -33,6 +33,7 @@ You can specify these options explicity...
on: :update,
actor: :author,
target: :post,
+ object: [:body]
followers: :post_participants
delegate :participants, to: :post, prefix: true
View
2 lib/timeline/actor.rb
@@ -23,6 +23,8 @@ def timeline_options(options)
defaults.merge!(list_name: "global:activity")
when :posts
defaults.merge!(list_name: "user:id:#{self.id}:posts")
+ when :mentions
+ defaults.merge!(list_name: "user:id:#{self.id}:mentions")
end
end
end
View
86 lib/timeline/track.rb
@@ -12,10 +12,10 @@ def track(name, options={})
@target = options.delete :target
@followers = options.delete :followers
@followers ||= :followers
- @extra_fields = options.delete :extra_fields
+ @mentionable = options.delete :mentionable
method_name = "track_#{@name}_after_#{@callback}".to_sym
- define_activity_method method_name, actor: @actor, object: @object, target: @target, followers: @followers, verb: name, extra_fields: @extra_fields
+ define_activity_method method_name, actor: @actor, object: @object, target: @target, followers: @followers, verb: name, mentionable: @mentionable
send "after_#{@callback}".to_sym, method_name, if: options.delete(:if)
end
@@ -23,11 +23,14 @@ def track(name, options={})
private
def define_activity_method(method_name, options={})
define_method method_name do
- actor = send(options[:actor])
- object = !options[:object].nil? ? send(options[:object].to_sym) : self
- target = !options[:target].nil? ? send(options[:target].to_sym) : nil
- followers = actor.send(options[:followers].to_sym)
- add_activity activity(verb: options[:verb], actor: actor, object: object, target: target, extra_fields: options[:extra_fields]), followers
+ @actor = send(options[:actor])
+ @fields_for = {}
+ @object = set_object(options[:object])
+ @target = !options[:target].nil? ? send(options[:target].to_sym) : nil
+ @extra_fields ||= nil
+ @followers = @actor.send(options[:followers].to_sym)
+ @mentionable = options[:mentionable]
+ add_activity activity(verb: options[:verb])
end
end
end
@@ -36,46 +39,52 @@ def define_activity_method(method_name, options={})
def activity(options={})
{
verb: options[:verb],
- actor: options_for(options[:actor]),
- object: options_for(options[:object]),
- target: options_for(options[:target]),
+ actor: options_for(@actor),
+ object: options_for(@object),
+ target: options_for(@target),
created_at: Time.now
- }.merge(add_extra_fields(options[:extra_fields]))
+ }
end
- def add_activity(activity_item, followers)
+ def add_activity(activity_item)
redis_add "global:activity", activity_item
add_activity_to_user(activity_item[:actor][:id], activity_item)
add_activity_by_user(activity_item[:actor][:id], activity_item)
- add_activity_to_followers(followers, activity_item) if followers.any?
- end
-
- def add_activity_to_user(user_id, activity_item)
- redis_add "user:id:#{user_id}:activity", activity_item
+ add_mentions(activity_item)
+ add_activity_to_followers(activity_item) if @followers.any?
end
def add_activity_by_user(user_id, activity_item)
redis_add "user:id:#{user_id}:posts", activity_item
end
- def add_activity_to_followers(followers, activity_item)
- followers.each { |follower| add_activity_to_user(follower.id, activity_item) }
+ def add_activity_to_user(user_id, activity_item)
+ redis_add "user:id:#{user_id}:activity", activity_item
+ end
+
+ def add_activity_to_followers(activity_item)
+ @followers.each { |follower| add_activity_to_user(follower.id, activity_item) }
end
- def add_extra_fields(extra_fields)
- if !extra_fields.nil? and extra_fields.any?
- extras = {}
- extra_fields.each do |value|
- extras[value] = send(value)
+ def add_mentions(activity_item)
+ return unless @mentionable and @object.send(@mentionable)
+ @object.send(@mentionable).scan(/@\w+/).each do |mention|
+ if user = @actor.class.find_by_username(mention[1..-1])
+ add_mention_to_user(user.id, activity_item)
end
- extras
- else
- {}
end
end
- def redis_add(list, activity_item)
- Timeline.redis.lpush list, Timeline.encode(activity_item)
+ def add_mention_to_user(user_id, activity_item)
+ redis_add "user:id:#{user_id}:mentions", activity_item
+ end
+
+ def extra_fields_for(object)
+ return {} unless @fields_for.has_key?(object.class.to_s.downcase.to_sym)
+ @fields_for[object.class.to_s.downcase.to_sym].inject({}) do |sum, method|
+ sum[method.to_sym] = @object.send(method.to_sym)
+ sum
+ end
end
def options_for(target)
@@ -84,9 +93,26 @@ def options_for(target)
id: target.id,
class: target.class.to_s,
display_name: target.to_s
- }
+ }.merge(extra_fields_for(target))
else
nil
end
end
+
+ def redis_add(list, activity_item)
+ Timeline.redis.lpush list, Timeline.encode(activity_item)
+ end
+
+ def set_object(object)
+ case
+ when object.is_a?(Symbol)
+ send(object)
+ when object.is_a?(Array)
+ @fields_for[self.class.to_s.downcase.to_sym] = object
+ self
+ else
+ self
+ end
+ end
+
end
View
2 lib/timeline/version.rb
@@ -1,3 +1,3 @@
module Timeline
- VERSION = "0.1.5"
+ VERSION = "0.1.6"
end
View
2 spec/spec_helper.rb
@@ -1,4 +1,4 @@
-require File.expand_path(File.join(File.dirname(__FILE__), %w[.. lib timeline]))
+require File.expand_path(File.join(File.dirname(__FILE__), %w[.. lib redis-timeline]))
dir = File.dirname(File.expand_path(__FILE__))
RSpec.configure do |config|
View
27 spec/track_spec.rb
@@ -34,14 +34,15 @@ class Comment
extend ActiveModel::Callbacks
define_model_callbacks :create
- attr_accessor :id, :creator_id
+ attr_accessor :id, :creator_id, :body
include Timeline::Track
- track :new_comment, extra_fields: [:post_name, :post_id]
+ track :new_comment, object: [:post_name, :post_id, :body], mentionable: :body
def initialize(options={})
@creator_id = options.delete :creator_id
+ @body = options.delete :body
end
def save
@@ -68,22 +69,28 @@ def to_s
class User
include Timeline::Actor
- attr_accessor :id, :to_param
+ attr_accessor :id, :to_param, :username
def initialize(options={})
@id = options.delete :id
+ @username = options.delete :username
end
class << self
def find user_id
User.new(id: user_id)
end
+
+ def find_by_username username
+ User.new(username: username)
+ end
end
end
describe Timeline::Track do
- let(:creator) { User.new(id: 1) }
+ let(:creator) { User.new(id: 1, username: "first_user") }
let(:post) { Post.new(creator_id: creator.id, name: "New post") }
+ let(:comment) { Comment.new(creator_id: creator.id, id: 1) }
describe "included in an ActiveModel-compliant class" do
it "tracks on create by default" do
@@ -116,11 +123,17 @@ def find user_id
end
describe "with extra_fields" do
- let(:comment) { Comment.new(creator_id: creator.id, id: 1) }
-
it "stores the extra fields in the timeline" do
comment.save
- creator.timeline.first.should respond_to :post_id
+ creator.timeline.first.object.should respond_to :post_id
+ end
+ end
+
+ describe "tracking mentions" do
+ it "adds to a user's mentions timeline" do
+ User.stub(:find_by_username).and_return(creator)
+ Comment.new(creator_id: creator.id, body: "@first_user should see this").save
+ creator.timeline(:mentions).first.object.body.should == "@first_user should see this"
end
end
end

0 comments on commit 284682b

Please sign in to comment.
Something went wrong with that request. Please try again.