activr 1.0.0
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.
- checksums.yaml +7 -0
- data/LICENSE +20 -0
- data/README.md +764 -0
- data/Rakefile +50 -0
- data/lib/activr.rb +206 -0
- data/lib/activr/activity.rb +446 -0
- data/lib/activr/async.rb +92 -0
- data/lib/activr/async/resque.rb +58 -0
- data/lib/activr/configuration.rb +40 -0
- data/lib/activr/dispatcher.rb +70 -0
- data/lib/activr/entity.rb +103 -0
- data/lib/activr/entity/model_mixin.rb +117 -0
- data/lib/activr/rails_ctx.rb +37 -0
- data/lib/activr/railtie.rb +73 -0
- data/lib/activr/railties/activr.rake +14 -0
- data/lib/activr/registry.rb +268 -0
- data/lib/activr/storage.rb +404 -0
- data/lib/activr/storage/mongo_driver.rb +645 -0
- data/lib/activr/timeline.rb +441 -0
- data/lib/activr/timeline/entry.rb +165 -0
- data/lib/activr/timeline/route.rb +161 -0
- data/lib/activr/utils.rb +74 -0
- data/lib/activr/version.rb +6 -0
- data/lib/generators/activr/activity_generator.rb +91 -0
- data/lib/generators/activr/templates/activity.rb +28 -0
- data/lib/generators/activr/templates/timeline.rb +42 -0
- data/lib/generators/activr/timeline_generator.rb +24 -0
- metadata +197 -0
@@ -0,0 +1,645 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'moped'
|
5
|
+
rescue LoadError
|
6
|
+
begin
|
7
|
+
require 'mongo'
|
8
|
+
rescue LoadError
|
9
|
+
raise "[activr] Can't find any suitable mongodb driver: please install 'mongo' or 'moped' gem"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
#
|
14
|
+
# Generic Mongodb driver
|
15
|
+
#
|
16
|
+
# This is main interface with the underlying MongobDB driver, which can be either the official `mongo` driver or `moped`, the `mongoid` driver.
|
17
|
+
#
|
18
|
+
class Activr::Storage::MongoDriver
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
# check settings
|
22
|
+
raise "Missing setting :uri in config: #{self.config.inspect}" if self.config[:uri].blank?
|
23
|
+
|
24
|
+
@collections = { }
|
25
|
+
|
26
|
+
@kind = if defined?(::Moped)
|
27
|
+
if defined?(::Moped::BSON)
|
28
|
+
# moped < 2.0.0
|
29
|
+
:moped_1
|
30
|
+
else
|
31
|
+
# moped driver
|
32
|
+
:moped
|
33
|
+
end
|
34
|
+
elsif defined?(::Mongo::MongoClient)
|
35
|
+
# mongo ruby driver < 2.0.0
|
36
|
+
:mongo
|
37
|
+
elsif defined?(::Mongo::Client)
|
38
|
+
raise "Sorry, mongo gem >= 2.0 is not supported yet"
|
39
|
+
else
|
40
|
+
raise "Can't find any suitable mongodb driver: please install 'mongo' or 'moped' gem"
|
41
|
+
end
|
42
|
+
|
43
|
+
# Activr.logger.info("Using mongodb driver: #{@kind}")
|
44
|
+
|
45
|
+
if @kind == :mongo
|
46
|
+
uri = URI.parse(self.config[:uri])
|
47
|
+
|
48
|
+
@db_name = uri.path[1..-1]
|
49
|
+
raise "Missing database name in setting uri: #{config[:uri]}" if @db_name.blank?
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# MongoDB config
|
54
|
+
#
|
55
|
+
# @api private
|
56
|
+
#
|
57
|
+
# @return [hash] Config
|
58
|
+
def config
|
59
|
+
Activr.config.mongodb
|
60
|
+
end
|
61
|
+
|
62
|
+
# Mongodb connection/session
|
63
|
+
#
|
64
|
+
# @api private
|
65
|
+
#
|
66
|
+
# @return [Mongo::MongoClient, Mongo::MongoReplicaSetClient, Moped::Session] Connection handler
|
67
|
+
def conn
|
68
|
+
@conn ||= begin
|
69
|
+
case @kind
|
70
|
+
when :moped_1, :moped
|
71
|
+
::Moped::Session.connect(self.config[:uri])
|
72
|
+
when :mongo
|
73
|
+
::Mongo::MongoClient.from_uri(self.config[:uri])
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Mongodb collection
|
79
|
+
#
|
80
|
+
# @api private
|
81
|
+
#
|
82
|
+
# @param col_name [String] Collection name
|
83
|
+
# @return [Mongo::Collection, Moped::Collection] Collection handler
|
84
|
+
def collection(col_name)
|
85
|
+
case @kind
|
86
|
+
when :moped_1, :moped
|
87
|
+
self.conn[col_name]
|
88
|
+
when :mongo
|
89
|
+
self.conn.db(@db_name).collection(col_name)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Insert a document into given collection
|
94
|
+
#
|
95
|
+
# @api private
|
96
|
+
#
|
97
|
+
# @param col [Mongo::Collection, Moped::Collection] Collection handler
|
98
|
+
# @param doc [Hash] Document hash to insert
|
99
|
+
# @return [BSON::ObjectId, Moped::BSON::ObjectId] Inserted document id
|
100
|
+
def insert(col, doc)
|
101
|
+
case @kind
|
102
|
+
when :moped_1, :moped
|
103
|
+
doc_id = doc[:_id] || doc['_id']
|
104
|
+
if doc_id.nil?
|
105
|
+
doc_id = case @kind
|
106
|
+
when :moped_1
|
107
|
+
# Moped < 2.0.0 uses a custom BSON implementation
|
108
|
+
::Moped::BSON::ObjectId.new
|
109
|
+
when :moped
|
110
|
+
# Moped >= 2.0.0 uses bson gem
|
111
|
+
::BSON::ObjectId.new
|
112
|
+
end
|
113
|
+
|
114
|
+
doc['_id'] = doc_id
|
115
|
+
end
|
116
|
+
|
117
|
+
col.insert(doc)
|
118
|
+
|
119
|
+
doc_id
|
120
|
+
when :mongo
|
121
|
+
col.insert(doc)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# Find a document by id
|
126
|
+
#
|
127
|
+
# @api private
|
128
|
+
#
|
129
|
+
# @param col [Mongo::Collection, Moped::Collection] Collection handler
|
130
|
+
# @param selector [Hash] Selector hash
|
131
|
+
# @return [Hash, OrderedHash, Nil] Document
|
132
|
+
def find_one(col, selector)
|
133
|
+
case @kind
|
134
|
+
when :moped_1, :moped
|
135
|
+
col.find(selector).one
|
136
|
+
when :mongo
|
137
|
+
col.find_one(selector)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# Find documents in given collection
|
142
|
+
#
|
143
|
+
# @api private
|
144
|
+
#
|
145
|
+
# @param col [Mongo::Collection, Moped::Collection] Collection handler
|
146
|
+
# @param selector [Hash] Selector hash
|
147
|
+
# @param limit [Integer] Maximum number of documents to find
|
148
|
+
# @param skip [Integer] Number of documents to skip
|
149
|
+
# @param sort_field [Symbol,String] The field to use to sort documents in descending order
|
150
|
+
# @return [Enumerable] An enumerable on found documents
|
151
|
+
def find(col, selector, limit, skip, sort_field = nil)
|
152
|
+
case @kind
|
153
|
+
when :moped_1, :moped
|
154
|
+
result = col.find(selector).skip(skip).limit(limit)
|
155
|
+
result.sort(sort_field => -1) if sort_field
|
156
|
+
result
|
157
|
+
when :mongo
|
158
|
+
# compute options hash
|
159
|
+
options = {
|
160
|
+
:limit => limit,
|
161
|
+
:skip => skip,
|
162
|
+
}
|
163
|
+
|
164
|
+
options[:sort] = [ sort_field, ::Mongo::DESCENDING ] if sort_field
|
165
|
+
|
166
|
+
options[:batch_size] = 100 if (limit > 100)
|
167
|
+
|
168
|
+
col.find(selector, options)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
# Count documents in given collection
|
173
|
+
#
|
174
|
+
# @api private
|
175
|
+
#
|
176
|
+
# @param col [Mongo::Collection, Moped::Collection] Collection handler
|
177
|
+
# @param selector [Hash] Selector hash
|
178
|
+
# @return [Integer] Number of documents in collections that satisfy given selector
|
179
|
+
def count(col, selector)
|
180
|
+
case @kind
|
181
|
+
when :moped_1, :moped, :mongo
|
182
|
+
col.find(selector).count()
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
# Delete documents in given collection
|
187
|
+
#
|
188
|
+
# @api private
|
189
|
+
#
|
190
|
+
# @param col [Mongo::Collection, Moped::Collection] Collection handler
|
191
|
+
# @param selector [Hash] Selector hash
|
192
|
+
def delete(col, selector)
|
193
|
+
case @kind
|
194
|
+
when :moped_1, :moped
|
195
|
+
col.find(selector).remove_all
|
196
|
+
when :mongo
|
197
|
+
col.remove(selector)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
# Add index to given collection
|
202
|
+
#
|
203
|
+
# @api private
|
204
|
+
#
|
205
|
+
# @param col [Mongo::Collection, Moped::Collection] Collection handler
|
206
|
+
# @param index_spec [Array] Array of [ {String}, {Integer} ] tuplets with {String} being a field to index and {Integer} the order (`-1` of DESC and `1` for ASC)
|
207
|
+
# @param options [Hash] Options hash
|
208
|
+
# @option options [Boolean] :background Background indexing ? (default: `true`)
|
209
|
+
# @option options [Boolean] :sparse Is it a sparse index ? (default: `false`)
|
210
|
+
# @return [String] Index created
|
211
|
+
def add_index(col, index_spec, options = { })
|
212
|
+
options = {
|
213
|
+
:background => true,
|
214
|
+
:sparse => false,
|
215
|
+
}.merge(options)
|
216
|
+
|
217
|
+
case @kind
|
218
|
+
when :moped_1, :moped
|
219
|
+
index_spec = index_spec.inject(ActiveSupport::OrderedHash.new) do |memo, field_spec|
|
220
|
+
memo[field_spec[0]] = field_spec[1]
|
221
|
+
memo
|
222
|
+
end
|
223
|
+
|
224
|
+
col.indexes.create(index_spec, options)
|
225
|
+
|
226
|
+
index_spec
|
227
|
+
|
228
|
+
when :mongo
|
229
|
+
col.create_index(index_spec, options)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
# Get all indexes for given collection
|
234
|
+
#
|
235
|
+
# @api private
|
236
|
+
#
|
237
|
+
# @param col [Mongo::Collection, Moped::Collection] Collection handler
|
238
|
+
# @return [Array<String>] Indexes names
|
239
|
+
def indexes(col)
|
240
|
+
result = [ ]
|
241
|
+
|
242
|
+
case @kind
|
243
|
+
when :moped_1, :moped
|
244
|
+
col.indexes.each do |index_spec|
|
245
|
+
result << index_spec["name"]
|
246
|
+
end
|
247
|
+
|
248
|
+
when :mongo
|
249
|
+
result = col.index_information.keys
|
250
|
+
end
|
251
|
+
|
252
|
+
result
|
253
|
+
end
|
254
|
+
|
255
|
+
# Drop all indexes for given collection
|
256
|
+
#
|
257
|
+
# @api private
|
258
|
+
#
|
259
|
+
# @param col [Mongo::Collection, Moped::Collection] Collection handler
|
260
|
+
def drop_indexes(col)
|
261
|
+
case @kind
|
262
|
+
when :moped_1, :moped
|
263
|
+
col.indexes.drop
|
264
|
+
|
265
|
+
when :mongo
|
266
|
+
col.drop_indexes
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
# Get handler for `activities` collection
|
271
|
+
#
|
272
|
+
# @api private
|
273
|
+
#
|
274
|
+
# @return [Mongo::Collection, Moped::Collection] Collection handler
|
275
|
+
def activity_collection
|
276
|
+
@activity_collection ||= begin
|
277
|
+
col_name = self.config[:activities_col]
|
278
|
+
if col_name.nil?
|
279
|
+
col_name = "activities"
|
280
|
+
col_name = "#{self.config[:col_prefix]}_#{col_name}" unless self.config[:col_prefix].blank?
|
281
|
+
end
|
282
|
+
|
283
|
+
self.collection(col_name)
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
# Get handler for a `<kind>_timelines` collection
|
288
|
+
#
|
289
|
+
# @api private
|
290
|
+
#
|
291
|
+
# @param kind [String] Timeline kind
|
292
|
+
# @return [Mongo::Collection, Moped::Collection] Collection handler
|
293
|
+
def timeline_collection(kind)
|
294
|
+
@timeline_collection ||= { }
|
295
|
+
@timeline_collection[kind] ||= begin
|
296
|
+
col_name = self.config[:timelines_col]
|
297
|
+
if col_name.nil?
|
298
|
+
col_name = "#{kind}_timelines"
|
299
|
+
col_name = "#{self.config[:col_prefix]}_#{col_name}" unless self.config[:col_prefix].blank?
|
300
|
+
end
|
301
|
+
|
302
|
+
self.collection(col_name)
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
|
307
|
+
#
|
308
|
+
# Main interface with the Storage
|
309
|
+
#
|
310
|
+
|
311
|
+
# (see Activr::Storage#valid_id?)
|
312
|
+
def valid_id?(doc_id)
|
313
|
+
case @kind
|
314
|
+
when :moped_1
|
315
|
+
doc_id.is_a?(String) || doc_id.is_a?(::Moped::BSON::ObjectId)
|
316
|
+
when :mongo, :moped
|
317
|
+
doc_id.is_a?(String) || doc_id.is_a?(::BSON::ObjectId)
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
# Is it a serialized document id (ie. with format { '$oid' => ... })
|
322
|
+
#
|
323
|
+
# @return [true,false]
|
324
|
+
def serialized_id?(doc_id)
|
325
|
+
doc_id.is_a?(Hash) && !doc_id['$oid'].blank?
|
326
|
+
end
|
327
|
+
|
328
|
+
# Unserialize a document id
|
329
|
+
#
|
330
|
+
# @param doc_id [String,Hash] Document id
|
331
|
+
# @return [BSON::ObjectId,Moped::BSON::ObjectId] Unserialized document id
|
332
|
+
def unserialize_id(doc_id)
|
333
|
+
# get string representation
|
334
|
+
doc_id = self.serialized_id?(doc_id) ? doc_id['$oid'] : doc_id
|
335
|
+
|
336
|
+
if @kind == :moped_1
|
337
|
+
# Moped < 2.0.0 uses a custom BSON implementation
|
338
|
+
if doc_id.is_a?(::Moped::BSON::ObjectId)
|
339
|
+
doc_id
|
340
|
+
else
|
341
|
+
::Moped::BSON::ObjectId(doc_id)
|
342
|
+
end
|
343
|
+
else
|
344
|
+
if doc_id.is_a?(::BSON::ObjectId)
|
345
|
+
doc_id
|
346
|
+
else
|
347
|
+
::BSON::ObjectId.from_string(doc_id)
|
348
|
+
end
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
|
353
|
+
#
|
354
|
+
# Activities
|
355
|
+
#
|
356
|
+
|
357
|
+
# Insert an activity document
|
358
|
+
#
|
359
|
+
# @api private
|
360
|
+
#
|
361
|
+
# @param activity_hash [Hash] Activity document to insert
|
362
|
+
# @return [BSON::ObjectId, Moped::BSON::ObjectId] Inserted activity id
|
363
|
+
def insert_activity(activity_hash)
|
364
|
+
self.insert(self.activity_collection, activity_hash)
|
365
|
+
end
|
366
|
+
|
367
|
+
# Find an activity document
|
368
|
+
#
|
369
|
+
# @api private
|
370
|
+
#
|
371
|
+
# @param activity_id [BSON::ObjectId, Moped::BSON::ObjectId] The activity id
|
372
|
+
# @return [Hash, OrderedHash, Nil] Activity document
|
373
|
+
def find_activity(activity_id)
|
374
|
+
self.find_one(self.activity_collection, { '_id' => activity_id })
|
375
|
+
end
|
376
|
+
|
377
|
+
# Compute selector for querying `activities` collection
|
378
|
+
#
|
379
|
+
# @api private
|
380
|
+
#
|
381
|
+
# @param options [Hash] Options when querying `activities` collection
|
382
|
+
# @return [Hash] The computed selector
|
383
|
+
def activities_selector(options)
|
384
|
+
result = { }
|
385
|
+
|
386
|
+
# compute selector
|
387
|
+
if options[:before]
|
388
|
+
result['at'] ||= { }
|
389
|
+
result['at']["$lt"] = options[:before]
|
390
|
+
end
|
391
|
+
|
392
|
+
if options[:after]
|
393
|
+
result['at'] ||= { }
|
394
|
+
result['at']["$gt"] = options[:after]
|
395
|
+
end
|
396
|
+
|
397
|
+
(options[:entities] || { }).each do |name, value|
|
398
|
+
result[name.to_s] = value
|
399
|
+
end
|
400
|
+
|
401
|
+
if !options[:only].blank?
|
402
|
+
result['kind'] ||= { }
|
403
|
+
result['kind']['$in'] = options[:only].map(&:kind)
|
404
|
+
end
|
405
|
+
|
406
|
+
if !options[:except].blank?
|
407
|
+
result['kind'] ||= { }
|
408
|
+
result['kind']['$nin'] = options[:except].map(&:kind)
|
409
|
+
end
|
410
|
+
|
411
|
+
result
|
412
|
+
end
|
413
|
+
|
414
|
+
# (see Storage#find_activities)
|
415
|
+
#
|
416
|
+
# @api private
|
417
|
+
def find_activities(limit, options = { })
|
418
|
+
selector = options[:mongo_selector] || self.activities_selector(options)
|
419
|
+
|
420
|
+
self.find(self.activity_collection, selector, limit, options[:skip], 'at')
|
421
|
+
end
|
422
|
+
|
423
|
+
# (see Storage#count_activities)
|
424
|
+
#
|
425
|
+
# @api private
|
426
|
+
def count_activities(options = { })
|
427
|
+
selector = options[:mongo_selector] || self.activities_selector(options)
|
428
|
+
|
429
|
+
self.count(self.activity_collection, selector)
|
430
|
+
end
|
431
|
+
|
432
|
+
# (see Storage#delete_activities)
|
433
|
+
#
|
434
|
+
# @api private
|
435
|
+
def delete_activities(options = { })
|
436
|
+
selector = options[:mongo_selector] || self.activities_selector(options)
|
437
|
+
|
438
|
+
self.delete(self.activity_collection, selector)
|
439
|
+
end
|
440
|
+
|
441
|
+
# Add index for activities
|
442
|
+
#
|
443
|
+
# @api private
|
444
|
+
#
|
445
|
+
# @param index [String,Array<String>] Field or array of fields
|
446
|
+
# @param options [Hash] Options hash
|
447
|
+
# @option options (see Activr::Storage::MongoDriver#add_index)
|
448
|
+
# @return [String] Index created
|
449
|
+
def add_activity_index(index, options = { })
|
450
|
+
index = index.is_a?(Array) ? index : [ index ]
|
451
|
+
index_spec = index.map{ |field| [ field, 1 ] }
|
452
|
+
|
453
|
+
self.add_index(self.activity_collection, index_spec, options)
|
454
|
+
end
|
455
|
+
|
456
|
+
|
457
|
+
#
|
458
|
+
# Timeline entries
|
459
|
+
#
|
460
|
+
|
461
|
+
# Insert a timeline entry document
|
462
|
+
#
|
463
|
+
# @api private
|
464
|
+
#
|
465
|
+
# @param timeline_kind [String] Timeline kind
|
466
|
+
# @param timeline_entry_hash [Hash] Timeline entry document to insert
|
467
|
+
def insert_timeline_entry(timeline_kind, timeline_entry_hash)
|
468
|
+
self.insert(self.timeline_collection(timeline_kind), timeline_entry_hash)
|
469
|
+
end
|
470
|
+
|
471
|
+
# Find a timeline entry document
|
472
|
+
#
|
473
|
+
# @api private
|
474
|
+
#
|
475
|
+
# @param timeline_kind [String] Timeline kind
|
476
|
+
# @param tl_entry_id [BSON::ObjectId, Moped::BSON::ObjectId] Timeline entry document id
|
477
|
+
# @return [Hash, OrderedHash, Nil] Timeline entry document
|
478
|
+
def find_timeline_entry(timeline_kind, tl_entry_id)
|
479
|
+
self.find_one(self.timeline_collection(timeline_kind), { '_id' => tl_entry_id })
|
480
|
+
end
|
481
|
+
|
482
|
+
# Compute selector for querying a `*_timelines` collection
|
483
|
+
#
|
484
|
+
# @api private
|
485
|
+
#
|
486
|
+
# @param timeline_kind [String] Timeline kind
|
487
|
+
# @param recipient_id [String, BSON::ObjectId, Moped::BSON::ObjectId] Recipient id
|
488
|
+
# @param options (see Storage#find_timeline)
|
489
|
+
# @return [Hash] The computed selector
|
490
|
+
def timeline_selector(timeline_kind, recipient_id, options = { })
|
491
|
+
result = { }
|
492
|
+
|
493
|
+
# compute selector
|
494
|
+
result['rcpt'] = recipient_id unless recipient_id.nil?
|
495
|
+
|
496
|
+
if options[:before]
|
497
|
+
result['activity.at'] = { "$lt" => options[:before] }
|
498
|
+
end
|
499
|
+
|
500
|
+
(options[:entities] || { }).each do |name, value|
|
501
|
+
result["activity.#{name}"] = value
|
502
|
+
end
|
503
|
+
|
504
|
+
if !options[:only].blank?
|
505
|
+
result['$or'] = options[:only].map do |route|
|
506
|
+
{ 'routing' => route.routing_kind, 'activity.kind' => route.activity_class.kind }
|
507
|
+
end
|
508
|
+
end
|
509
|
+
|
510
|
+
result
|
511
|
+
end
|
512
|
+
|
513
|
+
# Find several timeline entry documents
|
514
|
+
#
|
515
|
+
# @api private
|
516
|
+
#
|
517
|
+
# @param timeline_kind [String] Timeline kind
|
518
|
+
# @param recipient_id [String, BSON::ObjectId, Moped::BSON::ObjectId] Recipient id
|
519
|
+
# @param limit [Integer] Max number of entries to find
|
520
|
+
# @param options (see Storage#find_timeline)
|
521
|
+
# @return [Array<Hash>] An array of timeline entry documents
|
522
|
+
def find_timeline_entries(timeline_kind, recipient_id, limit, options = { })
|
523
|
+
selector = options[:mongo_selector] || self.timeline_selector(timeline_kind, recipient_id, options)
|
524
|
+
|
525
|
+
self.find(self.timeline_collection(timeline_kind), selector, limit, options[:skip], 'activity.at')
|
526
|
+
end
|
527
|
+
|
528
|
+
# Count number of timeline entry documents
|
529
|
+
#
|
530
|
+
# @api private
|
531
|
+
#
|
532
|
+
# @param timeline_kind [String] Timeline kind
|
533
|
+
# @param recipient_id [String, BSON::ObjectId, Moped::BSON::ObjectId] Recipient id
|
534
|
+
# @param options (see Storage#count_timeline)
|
535
|
+
# @return [Integer] Number of documents in given timeline
|
536
|
+
def count_timeline_entries(timeline_kind, recipient_id, options = { })
|
537
|
+
selector = options[:mongo_selector] || self.timeline_selector(timeline_kind, recipient_id, options)
|
538
|
+
|
539
|
+
self.count(self.timeline_collection(timeline_kind), selector)
|
540
|
+
end
|
541
|
+
|
542
|
+
# Delete timeline entry documents
|
543
|
+
#
|
544
|
+
# @api private
|
545
|
+
#
|
546
|
+
# WARNING: If recipient_id is `nil` then documents are deleted for ALL recipients
|
547
|
+
#
|
548
|
+
# @param timeline_kind [String] Timeline kind
|
549
|
+
# @param recipient_id [String, BSON::ObjectId, Moped::BSON::ObjectId, nil] Recipient id
|
550
|
+
# @param options (see Storage#delete_timeline)
|
551
|
+
def delete_timeline_entries(timeline_kind, recipient_id, options = { })
|
552
|
+
selector = options[:mongo_selector] || self.timeline_selector(timeline_kind, recipient_id, options)
|
553
|
+
|
554
|
+
# "end of the world" check
|
555
|
+
raise "Deleting everything is not the solution" if selector.blank?
|
556
|
+
|
557
|
+
self.delete(self.timeline_collection(timeline_kind), selector)
|
558
|
+
end
|
559
|
+
|
560
|
+
# Add index for timeline entries
|
561
|
+
#
|
562
|
+
# @api private
|
563
|
+
#
|
564
|
+
# @param timeline_kind [String] Timeline kind
|
565
|
+
# @param index [String,Array<String>] Field or array of fields
|
566
|
+
# @param options [Hash] Options hash
|
567
|
+
# @option options (see Activr::Storage::MongoDriver#add_index)
|
568
|
+
# @return [String] Index created
|
569
|
+
def add_timeline_index(timeline_kind, index, options = { })
|
570
|
+
index = index.is_a?(Array) ? index : [ index ]
|
571
|
+
index_spec = index.map{ |field| [ field, 1 ] }
|
572
|
+
|
573
|
+
self.add_index(self.timeline_collection(timeline_kind), index_spec, options)
|
574
|
+
end
|
575
|
+
|
576
|
+
|
577
|
+
#
|
578
|
+
# Indexes
|
579
|
+
#
|
580
|
+
|
581
|
+
# (see Storage#create_indexes)
|
582
|
+
#
|
583
|
+
# @api private
|
584
|
+
def create_indexes
|
585
|
+
# Create indexes on 'activities' collection for models that includes Activr::Entity::ModelMixin
|
586
|
+
#
|
587
|
+
# eg: activities
|
588
|
+
# [['actor', Mongo::ASCENDING], ['at', Mongo::ASCENDING]]
|
589
|
+
# [['album', Mongo::ASCENDING], ['at', Mongo::ASCENDING]]
|
590
|
+
# [['picture', Mongo::ASCENDING], ['at', Mongo::ASCENDING]]
|
591
|
+
Activr.registry.models.each do |model_class|
|
592
|
+
if !model_class.activr_entity_settings[:feed_index]
|
593
|
+
# @todo Output a warning to remove the index if it exists
|
594
|
+
else
|
595
|
+
fields = [ model_class.activr_entity_feed_actual_name.to_s, 'at' ]
|
596
|
+
|
597
|
+
index_name = self.add_activity_index(fields)
|
598
|
+
yield("activity / #{index_name}") if block_given?
|
599
|
+
end
|
600
|
+
end
|
601
|
+
|
602
|
+
# Create indexes on '*_timelines' collections for defined timeline classes
|
603
|
+
#
|
604
|
+
# eg: user_news_feed_timelines
|
605
|
+
# [['rcpt', Mongo::ASCENDING], ['activity.at', Mongo::ASCENDING]]
|
606
|
+
Activr.registry.timelines.each do |timeline_kind, timeline_class|
|
607
|
+
fields = [ 'rcpt', 'activity.at' ]
|
608
|
+
|
609
|
+
index_name = self.add_timeline_index(timeline_kind, fields)
|
610
|
+
yield("#{timeline_kind} timeline / #{index_name}") if block_given?
|
611
|
+
end
|
612
|
+
|
613
|
+
# Create sparse indexes to remove activities and timeline entries when entity is deleted
|
614
|
+
#
|
615
|
+
# eg: activities
|
616
|
+
# [['actor', Mongo::ASCENDING]], :sparse => true
|
617
|
+
#
|
618
|
+
# eg: user_news_feed_timelines
|
619
|
+
# [['activity.actor', Mongo::ASCENDING]], :sparse => true
|
620
|
+
# [['activity.album', Mongo::ASCENDING]], :sparse => true
|
621
|
+
# [['activity.picture', Mongo::ASCENDING]], :sparse => true
|
622
|
+
Activr.registry.models.each do |model_class|
|
623
|
+
if model_class.activr_entity_settings[:deletable]
|
624
|
+
# create sparse index on `activities`
|
625
|
+
Activr.registry.activity_entities_for_model(model_class).each do |entity_name|
|
626
|
+
# if entity activity feed is enabled and this is the entity name used to fetch that feed then we can use the existing index...
|
627
|
+
if !model_class.activr_entity_settings[:feed_index] || (entity_name != model_class.activr_entity_feed_actual_name)
|
628
|
+
# ... else we create an index
|
629
|
+
index_name = self.add_activity_index(entity_name.to_s, :sparse => true)
|
630
|
+
yield("activity / #{index_name}") if block_given?
|
631
|
+
end
|
632
|
+
end
|
633
|
+
|
634
|
+
# create sparse index on timeline classes where that entity can be present
|
635
|
+
Activr.registry.timeline_entities_for_model(model_class).each do |timeline_class, entities|
|
636
|
+
entities.each do |entity_name|
|
637
|
+
index_name = self.add_timeline_index(timeline_class.kind, "activity.#{entity_name}", :sparse => true)
|
638
|
+
yield("#{timeline_class.kind} timeline / #{index_name}") if block_given?
|
639
|
+
end
|
640
|
+
end
|
641
|
+
end
|
642
|
+
end
|
643
|
+
end
|
644
|
+
|
645
|
+
end # class Storage::MongoDriver
|