lib/streama/activity.rb
module Streama
module Activity
extend ActiveSupport::Concern
included do
include Mongoid::Document
include Mongoid::Timestamps
field :verb, :type => Symbol
field :actor
field :object
field :target_object
field :receivers, :type => Array
index({ 'actor._id' => 1, 'actor._type' => 1 })
index({ 'object._id' => 1, 'object._type' => 1 })
index({ 'target_object._id' => 1, 'target_object._type' => 1 })
index({ 'receivers.id' => 1, 'receivers.type' => 1 })
validates_presence_of :actor, :verb
before_save :assign_data
end
module ClassMethods
# Defines a new activity type and registers a definition
#
# @param [ String ] name The name of the activity
#
# @example Define a new activity
# activity(:enquiry) do
# actor :user, :cache => [:full_name]
# object :enquiry, :cache => [:subject]
# target_object :listing, :cache => [:title]
# end
#
# @return [Definition] Returns the registered definition
def activity(name, &block)
definition = Streama::DefinitionDSL.new(name)
definition.instance_eval(&block)
Streama::Definition.register(definition)
end
# Publishes an activity using an activity name and data
#
# @param [ String ] verb The verb of the activity
# @param [ Hash ] data The data to initialize the activity with.
#
# @return [Streama::Activity] An Activity instance with data
def publish(verb, data)
receivers = data.delete(:receivers)
new({:verb => verb}.merge(data)).publish(:receivers => receivers)
end
def stream_for(actor, options={})
query = {:receivers => {'$elemMatch' => {:id => actor.id, :type => actor.class.to_s}}}
query.merge!({:verb.in => [*options[:type]]}) if options[:type]
self.where(query).desc(:created_at)
end
def stream_of(actor, options={})
query = {'actor.id' => actor.id, 'actor.type' => actor.class.to_s}
query.merge!({:verb.in => [*options[:type]]}) if options[:type]
self.where(query).desc(:created_at)
end
end
# Publishes the activity to the receivers
#
# @param [ Hash ] options The options to publish with.
#
def publish(options = {})
actor = load_instance(:actor)
self.receivers = (options[:receivers] || actor.followers).map { |r| { :id => r.id, :type => r.class.to_s } }
self.save
self
end
# Returns an instance of an actor, object or target
#
# @param [ Symbol ] type The data type (actor, object, target) to return an instance for.
#
# @return [Mongoid::Document] document A mongoid document instance
def load_instance(type)
(data = self.read_attribute(type)).is_a?(Hash) ? data['type'].to_s.camelcase.constantize.find(data['id']) : data
end
def refresh_data
assign_data
save(:validates_presence_of => false)
end
protected
def assign_data
[:actor, :object, :target_object].each do |type|
next unless object = load_instance(type)
class_sym = object.class.name.underscore.to_sym
raise Errors::InvalidData.new(class_sym) unless definition.send(type).has_key?(class_sym)
hash = {'id' => object.id, 'type' => object.class.name}
if fields = definition.send(type)[class_sym].try(:[],:cache)
fields.each do |field|
raise Errors::InvalidField.new(field) unless object.respond_to?(field)
hash[field.to_s] = object.send(field)
end
end
write_attribute(type, hash)
end
end
def definition
@definition ||= Streama::Definition.find(verb)
end
end
end