tekeya 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +0 -0
- data/.gitignore +23 -0
- data/.rspec +3 -0
- data/.yardopts +1 -0
- data/Gemfile +39 -0
- data/Guardfile +40 -0
- data/LICENSE +22 -0
- data/README.md +37 -0
- data/Rakefile +13 -0
- data/TODO.todo +8 -0
- data/app/active_record/tekeya/activity.rb +7 -0
- data/app/active_record/tekeya/attachment.rb +5 -0
- data/app/active_record/tekeya/notification.rb +5 -0
- data/app/mongoid/tekeya/activity.rb +9 -0
- data/app/mongoid/tekeya/attachment.rb +6 -0
- data/app/mongoid/tekeya/notification.rb +10 -0
- data/db/migrate/00_create_activities.rb +11 -0
- data/db/migrate/01_create_attachments.rb +13 -0
- data/db/migrate/02_create_notifications.rb +14 -0
- data/lib/tasks/resque_tasks.rake +3 -0
- data/lib/tekeya.rb +81 -0
- data/lib/tekeya/configuration.rb +48 -0
- data/lib/tekeya/entity.rb +432 -0
- data/lib/tekeya/entity/group.rb +22 -0
- data/lib/tekeya/errors/tekeya_error.rb +11 -0
- data/lib/tekeya/errors/tekeya_fatal.rb +11 -0
- data/lib/tekeya/errors/tekeya_non_entity.rb +6 -0
- data/lib/tekeya/errors/tekeya_non_group.rb +6 -0
- data/lib/tekeya/errors/tekeya_relation_already_exists.rb +6 -0
- data/lib/tekeya/errors/tekeya_relation_non_existent.rb +6 -0
- data/lib/tekeya/feed/activity.rb +101 -0
- data/lib/tekeya/feed/activity/feed_item.rb +58 -0
- data/lib/tekeya/feed/activity/resque.rb +56 -0
- data/lib/tekeya/feed/activity/resque/activity_fanout.rb +61 -0
- data/lib/tekeya/feed/activity/resque/delete_activity.rb +43 -0
- data/lib/tekeya/feed/activity/resque/feed_copy.rb +34 -0
- data/lib/tekeya/feed/activity/resque/untrack_feed.rb +34 -0
- data/lib/tekeya/feed/attachable.rb +15 -0
- data/lib/tekeya/feed/attachment.rb +19 -0
- data/lib/tekeya/feed/notification.rb +93 -0
- data/lib/tekeya/railtie.rb +18 -0
- data/lib/tekeya/version.rb +3 -0
- data/spec/fabricators/attachment_fabricator.rb +3 -0
- data/spec/fabricators/group_fabricator.rb +4 -0
- data/spec/fabricators/status_fabricator.rb +3 -0
- data/spec/fabricators/user_fabricator.rb +3 -0
- data/spec/orm/active_record.rb +14 -0
- data/spec/orm/mongoid.rb +6 -0
- data/spec/rails_app/Rakefile +7 -0
- data/spec/rails_app/app/active_record/group.rb +3 -0
- data/spec/rails_app/app/active_record/status.rb +3 -0
- data/spec/rails_app/app/active_record/user.rb +3 -0
- data/spec/rails_app/app/controllers/application_controller.rb +3 -0
- data/spec/rails_app/app/helpers/application_helper.rb +2 -0
- data/spec/rails_app/app/mongoid/group.rb +6 -0
- data/spec/rails_app/app/mongoid/status.rb +6 -0
- data/spec/rails_app/app/mongoid/user.rb +7 -0
- data/spec/rails_app/app/views/layouts/application.html.erb +14 -0
- data/spec/rails_app/config.ru +4 -0
- data/spec/rails_app/config/application.rb +35 -0
- data/spec/rails_app/config/boot.rb +8 -0
- data/spec/rails_app/config/database.yml +21 -0
- data/spec/rails_app/config/environment.rb +5 -0
- data/spec/rails_app/config/environments/development.rb +18 -0
- data/spec/rails_app/config/environments/production.rb +33 -0
- data/spec/rails_app/config/environments/test.rb +33 -0
- data/spec/rails_app/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/rails_app/config/initializers/configure_mongoid.rb +6 -0
- data/spec/rails_app/config/initializers/inflections.rb +15 -0
- data/spec/rails_app/config/initializers/resque.rb +1 -0
- data/spec/rails_app/config/initializers/secret_token.rb +7 -0
- data/spec/rails_app/config/locales/en.yml +5 -0
- data/spec/rails_app/config/routes.rb +58 -0
- data/spec/rails_app/db/migrate/100_create_users.rb +9 -0
- data/spec/rails_app/db/migrate/101_create_groups.rb +11 -0
- data/spec/rails_app/db/migrate/102_create_statuses.rb +9 -0
- data/spec/rails_app/db/seeds.rb +7 -0
- data/spec/rails_app/public/404.html +26 -0
- data/spec/rails_app/public/422.html +26 -0
- data/spec/rails_app/public/500.html +25 -0
- data/spec/rails_app/public/favicon.ico +0 -0
- data/spec/rails_app/public/index.html +241 -0
- data/spec/rails_app/public/robots.txt +5 -0
- data/spec/rails_app/script/rails +6 -0
- data/spec/spec_helper.rb +78 -0
- data/spec/tekeya/activity_spec.rb +170 -0
- data/spec/tekeya/entity_spec.rb +158 -0
- data/spec/tekeya/notification_spec.rb +49 -0
- data/spec/tekeya_helper.rb +7 -0
- data/spec/tekeya_spec.rb +51 -0
- data/tekeya.gemspec +22 -0
- metadata +231 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module Tekeya
|
|
2
|
+
module Entity
|
|
3
|
+
module Group
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
include Entity
|
|
6
|
+
|
|
7
|
+
included do
|
|
8
|
+
belongs_to :owner, polymorphic: true
|
|
9
|
+
|
|
10
|
+
validates_presence_of :owner
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def is_tekeya_group?
|
|
14
|
+
return true
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def members(type = nil)
|
|
18
|
+
tekeya_relations_of(self, :joins, type, true)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
module Tekeya
|
|
2
|
+
module Feed
|
|
3
|
+
module Activity
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
included do
|
|
7
|
+
belongs_to :entity, polymorphic: true, autosave: true
|
|
8
|
+
has_many :attachments, as: :attache, class_name: 'Tekeya::Attachment'
|
|
9
|
+
|
|
10
|
+
before_create :group_activities
|
|
11
|
+
after_create :write_activity_in_redis
|
|
12
|
+
after_destroy :delete_activity_from_redis
|
|
13
|
+
|
|
14
|
+
accepts_nested_attributes_for :attachments
|
|
15
|
+
|
|
16
|
+
validates_presence_of :attachments
|
|
17
|
+
|
|
18
|
+
attr_writer :group_with_recent
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Check if this activity is cached in redis
|
|
22
|
+
#
|
|
23
|
+
# @return [Boolean] true if an aggregate of the activity exists in redis, false otherwise
|
|
24
|
+
def cached_in_redis?
|
|
25
|
+
::Tekeya.redis.scard(activity_key) > 0
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Approximates the timestamp to the nearest 15 minutes for grouping activities
|
|
29
|
+
#
|
|
30
|
+
# @param [Datetime] from_time the time to approximate
|
|
31
|
+
# @return [Integer] the timestamp approximated to the nearest 15 minutes
|
|
32
|
+
def score(from_time = nil)
|
|
33
|
+
if from_time.present?
|
|
34
|
+
stamp = from_time.to_i
|
|
35
|
+
|
|
36
|
+
# floors the timestamp to the nearest 15 minute
|
|
37
|
+
return (stamp.to_f / 15.minutes).floor * 15.minutes
|
|
38
|
+
else
|
|
39
|
+
return current_time_from_proper_timezone.to_i
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Returns an activity key for usage in caching
|
|
44
|
+
#
|
|
45
|
+
# @return [String] the activity key
|
|
46
|
+
def activity_key
|
|
47
|
+
"activity:#{self.id}:#{self.entity_type}:#{self.entity.send(self.entity.entity_primary_key)}:#{self.activity_type}:#{score}"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# @private
|
|
51
|
+
#
|
|
52
|
+
# returns if the activity should be grouped with similar recent activities
|
|
53
|
+
def group_with_recent
|
|
54
|
+
@group_with_recent.nil? ? true : @group_with_recent
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
# @private
|
|
60
|
+
# Writes to the activity's aggregate set (a set of attachments associated with the activity)
|
|
61
|
+
def write_activity_in_redis
|
|
62
|
+
akey = activity_key
|
|
63
|
+
tscore = score
|
|
64
|
+
::Resque.enqueue(::Tekeya::Feed::Activity::Resque::ActivityFanout, self.entity_id, self.entity_type, akey, tscore, self.attachments.map{ |att| att.to_json(root: false, only: [:attachable_id, :attachable_type]) })
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# @private
|
|
68
|
+
# Checks if the activity should be grouped and aborts the creation of a new record
|
|
69
|
+
def group_activities
|
|
70
|
+
if self.group_with_recent
|
|
71
|
+
self.created_at = current_time_from_proper_timezone
|
|
72
|
+
rel = self.class.where(created_at: self.created_at, activity_type: self.activity_type, entity_id: self.entity_id, entity_type: entity_type)
|
|
73
|
+
if rel.count > 0
|
|
74
|
+
activity = rel.first
|
|
75
|
+
activity.attachments << self.attachments
|
|
76
|
+
self.id = activity.id
|
|
77
|
+
self.reload
|
|
78
|
+
return false
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# @private
|
|
84
|
+
# Deletes the activity's aggregate set when its deleted from the DB
|
|
85
|
+
def delete_activity_from_redis
|
|
86
|
+
::Resque.enqueue(::Tekeya::Feed::Activity::Resque::DeleteActivity, self.activity_key)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# @private
|
|
90
|
+
# Override AR's default created_at calculation formula
|
|
91
|
+
def current_time_from_proper_timezone #:nodoc:
|
|
92
|
+
zone = self.class.respond_to?(:default_timezone) ? self.class.default_timezone : :utc
|
|
93
|
+
ctime = zone == :utc ? Time.now.utc : Time.now
|
|
94
|
+
stamp = ctime.to_i
|
|
95
|
+
|
|
96
|
+
# floors the timestamp to the nearest 15 minute
|
|
97
|
+
return Time.at((stamp.to_f / 15.minutes).floor * 15.minutes)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
module Tekeya
|
|
2
|
+
module Feed
|
|
3
|
+
module Activity
|
|
4
|
+
class FeedItem
|
|
5
|
+
attr_reader :activity_id, :activity_type, :attachments, :actor, :timestamp
|
|
6
|
+
|
|
7
|
+
def initialize(activity_id, activity_type, attachments, actor, timestamp)
|
|
8
|
+
@activity_id = activity_id
|
|
9
|
+
@activity_type = activity_type
|
|
10
|
+
@attachments = attachments
|
|
11
|
+
@actor = actor
|
|
12
|
+
@timestamp = timestamp
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Builds a feed item from a redis activity
|
|
16
|
+
#
|
|
17
|
+
# @param [String] key the aggregate key of the activity
|
|
18
|
+
# @param [Tekeya::Entity] act_actor the activty actor; when nil the actor is retrieved from the aggregate key
|
|
19
|
+
# @return [Tekeya::Feed::FeedItem] the feed item
|
|
20
|
+
def self.from_redis(key, act_actor = nil)
|
|
21
|
+
key_components = key.split(':')
|
|
22
|
+
|
|
23
|
+
act_id = key_components[1]
|
|
24
|
+
act_type = key_components[4].to_sym
|
|
25
|
+
act_time = Time.at(key_components[5].to_i)
|
|
26
|
+
|
|
27
|
+
if act_actor.nil?
|
|
28
|
+
actor_class = key_components[2].safe_constantize
|
|
29
|
+
act_actor = actor_class.where(:"#{actor_class.entity_primary_key}" => key_components[3]).first
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
act_attachments = ::Tekeya.redis.smembers(key).map{|act|
|
|
33
|
+
ActiveSupport::JSON.decode(act)
|
|
34
|
+
}.map{|att|
|
|
35
|
+
att['attachable_type'].safe_constantize.find att['attachable_id']
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return self.new(act_id, act_type, act_attachments, act_actor, act_time)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Builds a feed item a DB activity
|
|
42
|
+
#
|
|
43
|
+
# @param [Tekeya::Activity] activity the source activity
|
|
44
|
+
# @param [Tekeya::Entity] act_actor the activty actor; when nil the actor is retrieved from the activity
|
|
45
|
+
# @return [Tekeya::Feed::FeedItem] the feed item
|
|
46
|
+
def self.from_db(activity, act_actor = nil)
|
|
47
|
+
act_id = activity.id.to_s
|
|
48
|
+
act_type = activity.activity_type.to_sym
|
|
49
|
+
act_time = activity.created_at
|
|
50
|
+
act_actor ||= activity.entity
|
|
51
|
+
act_attachments = activity.attachments.map(&:attachable)
|
|
52
|
+
|
|
53
|
+
return self.new(act_id, act_type, act_attachments, act_actor, act_time)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
module Tekeya
|
|
2
|
+
module Feed
|
|
3
|
+
module Activity
|
|
4
|
+
module Resque
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
included do
|
|
8
|
+
MAXTIMESTAMP = 10.days.ago.to_i unless defined?(MAXTIMESTAMP)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
module ClassMethods
|
|
12
|
+
private
|
|
13
|
+
# Writes the activity reference to the feed with the supplied key
|
|
14
|
+
#
|
|
15
|
+
# @param [String] feed_key the key of the feed where the activity will be referenced
|
|
16
|
+
# @param [Integer] score the score of the activity (timestamp) used to order the feed
|
|
17
|
+
# @param [String] activity_key a string containing the key to reference the activity
|
|
18
|
+
def write_to_feed(feed_key, score, activity_key)
|
|
19
|
+
# add the activity to the owner's profile feed
|
|
20
|
+
::Tekeya.redis.zadd(feed_key, score, activity_key)
|
|
21
|
+
# increment the activity counter to keep track of its presence in feeds
|
|
22
|
+
activity_counter_key = "#{activity_key}:counter"
|
|
23
|
+
::Tekeya.redis.incr(activity_counter_key)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Trims the feed according to the MAXTIMESTAMP set and returns the removed keys (for garbage collection)
|
|
27
|
+
#
|
|
28
|
+
# @param [String] feed_key a string containing the key of the feed to be trimed
|
|
29
|
+
def trim_feed(feed_key)
|
|
30
|
+
removed_keys = ::Tekeya.redis.zrevrangebyscore(feed_key, '-inf', MAXTIMESTAMP)
|
|
31
|
+
::Tekeya.redis.zremrangebyscore(feed_key, '-inf', MAXTIMESTAMP)
|
|
32
|
+
|
|
33
|
+
return removed_keys
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Checks if the given keys are referenced in any feed otherwise removes the activity
|
|
37
|
+
#
|
|
38
|
+
# @param [Array] keys an array of activity keys to be removed
|
|
39
|
+
def collect_garbage(keys)
|
|
40
|
+
keys.each do |key|
|
|
41
|
+
activity_counter_key = "#{key}:counter"
|
|
42
|
+
# Check if the key is referenced anywhere
|
|
43
|
+
if ::Tekeya.redis.get(activity_counter_key).to_i <= 0
|
|
44
|
+
# Delete the activity and the counter
|
|
45
|
+
::Tekeya.redis.multi do
|
|
46
|
+
::Tekeya.redis.del(key)
|
|
47
|
+
::Tekeya.redis.del(activity_counter_key)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
module Tekeya
|
|
2
|
+
module Feed
|
|
3
|
+
module Activity
|
|
4
|
+
module Resque
|
|
5
|
+
# A resque worker to perform the activity fanout operation
|
|
6
|
+
class ActivityFanout
|
|
7
|
+
include Tekeya::Feed::Activity::Resque
|
|
8
|
+
|
|
9
|
+
@queue = :activity_queue
|
|
10
|
+
|
|
11
|
+
# @private
|
|
12
|
+
def self.perform(entity_id, entity_type, activity_key, score, attachments)
|
|
13
|
+
# get the entity class
|
|
14
|
+
entity_type = entity_type.safe_constantize
|
|
15
|
+
entity = entity_type.where(entity_type.entity_primary_key.to_sym => entity_id).first
|
|
16
|
+
# we only need the feed keys of the trackers
|
|
17
|
+
entity_trackers_feeds = entity.trackers.map(&:feed_key)
|
|
18
|
+
# keep track of the keys we delete in the trim operation for garbage collection
|
|
19
|
+
removed_keys = []
|
|
20
|
+
|
|
21
|
+
# write the activity to the aggregate set and the owner's feed
|
|
22
|
+
::Tekeya.redis.multi do
|
|
23
|
+
write_aggregate(activity_key, attachments)
|
|
24
|
+
write_to_feed(entity.profile_feed_key, score, activity_key)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# trim the profile feed
|
|
28
|
+
removed_keys += trim_feed(entity.profile_feed_key)
|
|
29
|
+
|
|
30
|
+
# Fanout the activity to the owner's trackers
|
|
31
|
+
entity_trackers_feeds.each do |feed_key|
|
|
32
|
+
# write the activity to the tracker's feed
|
|
33
|
+
::Tekeya.redis.multi do
|
|
34
|
+
write_to_feed(feed_key, score, activity_key)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# trim the tracker's feed
|
|
38
|
+
removed_keys += trim_feed(feed_key)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# cleanup the garbage
|
|
42
|
+
collect_garbage removed_keys
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
# Writes the activity and its' attachments to the aggregate set
|
|
48
|
+
#
|
|
49
|
+
# @param [String] activity_key the key of the activity to be added
|
|
50
|
+
# @param [Array] attachments an array of attachments associated with the activity
|
|
51
|
+
def self.write_aggregate(activity_key, attachments)
|
|
52
|
+
# save the aggregate set
|
|
53
|
+
attachments.each do |attachment|
|
|
54
|
+
::Tekeya.redis.sadd(activity_key, attachment)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module Tekeya
|
|
2
|
+
module Feed
|
|
3
|
+
module Activity
|
|
4
|
+
module Resque
|
|
5
|
+
# A resque worker to copy activities when an entity tracks another
|
|
6
|
+
class DeleteActivity
|
|
7
|
+
include Tekeya::Feed::Activity::Resque
|
|
8
|
+
|
|
9
|
+
@queue = :activity_queue
|
|
10
|
+
|
|
11
|
+
# @private
|
|
12
|
+
def self.perform(activity_aggregate_key)
|
|
13
|
+
# get the activity properties from the key
|
|
14
|
+
key_components = activity_aggregate_key.split(':')
|
|
15
|
+
entity_type = key_components[2].safe_constantize
|
|
16
|
+
entity_id = key_components[3]
|
|
17
|
+
|
|
18
|
+
# get the entity
|
|
19
|
+
entity = entity_type.where(entity_type.entity_primary_key.to_sym => entity_id).first
|
|
20
|
+
# we only need the feed keys of the trackers
|
|
21
|
+
entity_trackers_feeds = entity.trackers.map(&:feed_key)
|
|
22
|
+
entity_trackers_feeds << entity.profile_feed_key
|
|
23
|
+
|
|
24
|
+
# remove the aggregate key from the trackers' feeds and prepare the activity for garbage collection
|
|
25
|
+
::Tekeya.redis.multi do
|
|
26
|
+
entity_trackers_feeds.each do |feed_key|
|
|
27
|
+
if ::Tekeya.redis.zrank(feed_key, activity_aggregate_key)
|
|
28
|
+
# remove the activity aggregate key from the feed
|
|
29
|
+
::Tekeya.redis.zrem(feed_key, activity_aggregate_key)
|
|
30
|
+
# decrement the activity counter
|
|
31
|
+
::Tekeya.redis.decr("#{activity_aggregate_key}:counter")
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# trim the tracker feed and cleanup
|
|
37
|
+
collect_garbage [activity_aggregate_key]
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module Tekeya
|
|
2
|
+
module Feed
|
|
3
|
+
module Activity
|
|
4
|
+
module Resque
|
|
5
|
+
# A resque worker to copy activities when an entity tracks another
|
|
6
|
+
class FeedCopy
|
|
7
|
+
include Tekeya::Feed::Activity::Resque
|
|
8
|
+
|
|
9
|
+
@queue = :activity_queue
|
|
10
|
+
|
|
11
|
+
# @private
|
|
12
|
+
def self.perform(tracked_feed_key, tracker_feed_key)
|
|
13
|
+
# get the keys to the activities so we can increment the counters later
|
|
14
|
+
activity_keys = ::Tekeya.redis.zrange(tracked_feed_key, 0, -1)
|
|
15
|
+
|
|
16
|
+
::Tekeya.redis.multi do
|
|
17
|
+
# copy the latest activities from the tracked entity to the tracker feed
|
|
18
|
+
::Tekeya.redis.zunionstore(tracker_feed_key, [tracker_feed_key, tracked_feed_key])
|
|
19
|
+
|
|
20
|
+
# increment the activity counter
|
|
21
|
+
activity_keys.each do |activity_key|
|
|
22
|
+
activity_counter_key = "#{activity_key}:counter"
|
|
23
|
+
::Tekeya.redis.incr(activity_counter_key)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# trim the tracker feed and cleanup
|
|
28
|
+
collect_garbage trim_feed(tracker_feed_key)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module Tekeya
|
|
2
|
+
module Feed
|
|
3
|
+
module Activity
|
|
4
|
+
module Resque
|
|
5
|
+
# A resque worker to copy activities when an entity tracks another
|
|
6
|
+
class UntrackFeed
|
|
7
|
+
include Tekeya::Feed::Activity::Resque
|
|
8
|
+
|
|
9
|
+
@queue = :activity_queue
|
|
10
|
+
|
|
11
|
+
# @private
|
|
12
|
+
def self.perform(untracked_feed_key, untracker_feed_key)
|
|
13
|
+
# get the keys to the activities so we can decrement the counters later
|
|
14
|
+
activity_keys = ::Tekeya.redis.zrange(untracked_feed_key, 0, -1)
|
|
15
|
+
|
|
16
|
+
::Tekeya.redis.multi do
|
|
17
|
+
# delete the latest activities of the untracked entity from the tracker feed
|
|
18
|
+
::Tekeya.redis.zrem(untracker_feed_key, activity_keys)
|
|
19
|
+
|
|
20
|
+
# increment the activity counter
|
|
21
|
+
activity_keys.each do |activity_key|
|
|
22
|
+
activity_counter_key = "#{activity_key}:counter"
|
|
23
|
+
::Tekeya.redis.decr(activity_counter_key)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# trim the tracker feed and cleanup
|
|
28
|
+
collect_garbage trim_feed(untracker_feed_key)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|