Class: Actor

Inherits:
ActiveRecord::Base
  • Object
show all
Includes:
SocialStream::Models::Object
Defined in:
app/models/actor.rb

Overview

An Actor represents a social entity. This means individuals, but also groups, departments, organizations even nations or states.

Actors are the nodes of a social network. Two actors are linked by Ties. The type of a Tie is a Relation. Each actor can define and customize their relations own Relations.

Every Actor has an Avatar, a Profile with personal or group information, contact data, etc.

Actors perform actions (like, suscribe, etc.) on activity objects (posts, commments, pictures, events..)

Actor subtypes

An actor subtype is called a Subject. SocialStream::Base provides two actor subtypes, User and Group, but the application developer can define as many actor subtypes as required. Besides including the SocialStream::Models::Subject module, Actor subtypes must added to config/initializers/social_stream.rb

Instance Method Summary collapse

Instance Method Details

#action_to(activity_object) ⇒ Object

Return the ActivityAction model to an ActivityObject



391
392
393
# File 'app/models/actor.rb', line 391

def action_to(activity_object)
  sent_actions.received_by(activity_object).first
end

#action_to!(activity_object) ⇒ Object

Return the ActivityAction model to an ActivityObject. Create it if it does not exist



396
397
398
399
# File 'app/models/actor.rb', line 396

def action_to!(activity_object)
  action_to(activity_object) ||
    sent_actions.create!(:activity_object => ActivityObject.normalize(activity_object))
end

#activity_relation_idsObject

The ids of the default Relations for sharing an Activity owned by this Actor



479
480
481
# File 'app/models/actor.rb', line 479

def activity_relation_ids
  activity_relations.map(&:id)
end

#activity_relationsObject

The default Relations for sharing an Activity owned by this Actor

Activities are shared with all the contacts by default.

You can change this behaviour with a decorator, overwriting this method. For example, if you want the activities shared publicly by default, create a decorator in app/decorators/actor_decorator.rb with

Actor.class_eval do
  def activity_relations
    [ Relation::Public.instance ]
  end
end


472
473
474
475
# File 'app/models/actor.rb', line 472

def activity_relations
  relations.
    allowing('read', 'activity')
end

#activity_relations_for(subject, options = {}) ⇒ Object

This method returns all the Relation that subject can choose to broadcast an Activity in this Actor‘s wall

See Activity on how they can be shared with multiple Audience, which corresponds to a Relation.



487
488
489
490
491
492
493
# File 'app/models/actor.rb', line 487

def activity_relations_for(subject, options = {})
  if Actor.normalize(subject) == self
    return relation_customs + Array.wrap(Relation::Public.instance)
  else
    Array.new
  end
end

#activity_relations_for?(subject, options = {}) ⇒ Boolean

Are #activity_relations available for subject?

Returns:

  • (Boolean)


496
497
498
# File 'app/models/actor.rb', line 496

def activity_relations_for?(subject, options = {})
  activity_relations(subject, options).any?
end

#allow?(subject, action, object = nil) ⇒ Boolean

Does this Actor allow subject to perform action on object?

Returns:

  • (Boolean)


386
387
388
# File 'app/models/actor.rb', line 386

def allow?(subject, action, object = nil)
  ties_to(subject).with_permissions(action, object).any?
end

#as_json(options) ⇒ Object



541
542
543
544
545
546
547
548
549
550
551
# File 'app/models/actor.rb', line 541

def as_json(options)
  {
    id: id,
    slug: slug,
    name: name,
    url: options[:helper].polymorphic_url(subject),
    image: {
      url: options[:helper].root_url + .url(:small)
    }
  }
end

#as_object_typeObject

FIXME SocialStream::ActivityStreams::Supertype should take precedence over SocialStream::ActivityStreams::Subtype



166
167
168
# File 'app/models/actor.rb', line 166

def as_object_type
  subtype_instance.as_object_type
end

#can_comment?(activity) ⇒ Boolean

Is this Actor allowed to create a comment on activity?

We are allowing comments from everyone signed in by now

Returns:

  • (Boolean)


503
504
505
506
507
# File 'app/models/actor.rb', line 503

def can_comment?(activity)
  return true

  comment_relations(activity).any?
end

#comment_relations(activity) ⇒ Object

Are there any relations that allow this actor to create a comment on activity?



510
511
512
513
# File 'app/models/actor.rb', line 510

def comment_relations(activity)
  activity.relations.select{ |r| r.is_a?(Relation::Public) } |
    Relation.allow(self, 'create', 'activity', :in => activity.relations)
end

#common_contacts_count(subject) ⇒ Object

Count the contacts in common between this Actor and subject



532
533
534
# File 'app/models/actor.rb', line 532

def common_contacts_count(subject)
  (sent_active_contact_ids & subject.sent_active_contact_ids).size
end

#contact_actors(options = {}) ⇒ Object

All the Actors this one has ties with:

actor.contact_actors #=> array of actors that sent and receive ties from actor

There are several options available to refine the query:

type

Filter by the class of the contacts (User, Group, etc.)

actor.contact_actors(:type => :user) #=> array of user actors. Exclude groups, etc.
direction

:sent leaves only the actors this one has ties to. :received gets the actors sending ties to this actor, whether this actor added them or not

actor.contact_actors(:direction => :sent) #=> all the receivers of ties from actor
relations

Restrict to ties made up with relations. In the case of both directions, only relations belonging to Actor are considered. It defaults to actor’s custom relations

actor.contact_actors(:relations => [2]) #=> actors tied with relation #2
include_self

False by default, do not include this actor even they have ties with themselves.

load_subjects

True by default, make the queries for eager loading of Subject



272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
# File 'app/models/actor.rb', line 272

def contact_actors(options = {})
  subject_types   = Array(options[:type] || self.class.subtypes)
  subject_classes = subject_types.map{ |s| s.to_s.classify }
  
  as = Actor.select('actors.*').
             # PostgreSQL requires that all the columns must be included in the GROUP BY
             group((Actor.columns.map(&:name).map{ |c| "actors.#{ c }" } + [ "contacts.created_at" ]).join(", ")).
             where('actors.subject_type' => subject_classes)

  if options[:load_subjects].nil? || options[:load_subjects]
    as = as.includes(subject_types)
  end
  
  # A blank :direction means reciprocate contacts, there must be ties in both directions
  #
  # This is achieved by getting the id of all the contacts that are sending ties
  # Then, we filter the sent contacts query to only those contacts
  if options[:direction].blank?
    rcv_opts = options.dup
    rcv_opts[:direction] = :received
    rcv_opts[:load_subjects] = false

    # Get the id of actors that are sending to this one
    sender_ids = contact_actors(rcv_opts).map(&:id)

    # Filter the sent query with these ids
    as = as.where(:id => sender_ids)

    options[:direction] = :sent
  end
  
  case options[:direction]
  when :sent
    as = as.joins(:received_ties => :relation).merge(Contact.sent_by(self))
  when :received
    as = as.joins(:sent_ties => :relation).merge(Contact.received_by(self))
  else
    raise "How do you get here?!"
  end
  
  if options[:include_self].blank?
    as = as.where("actors.id != ?", self.id)
  end
  
  if options[:relations].present?
    as = as.merge(Tie.related_by(options[:relations]))
  else
    as = as.merge(Relation.positive)
  end
  
  as
end

#contact_subjects(options = {}) ⇒ Object

All the subjects that send or receive at least one Tie to this Actor

When passing a block, it will be evaluated for building the actors query, allowing to add options before the mapping to subjects

See #contact_actors for options



331
332
333
334
335
336
337
338
339
# File 'app/models/actor.rb', line 331

def contact_subjects(options = {})
  as = contact_actors(options)
  
  if block_given?
    as = yield(as)
  end
  
  as.map(&:subject)
end

#contact_to(subject) ⇒ Object

Return a contact to subject.



347
348
349
# File 'app/models/actor.rb', line 347

def contact_to(subject)
  sent_contacts.received_by(subject).first
end

#contact_to!(subject) ⇒ Object

Return a contact to subject. Create it if it does not exist



352
353
354
355
# File 'app/models/actor.rb', line 352

def contact_to!(subject)
  contact_to(subject) ||
    sent_contacts.create!(receiver_id: Actor.normalize_id(subject))
end

#egocentric_tiesObject

The ties sent by this actor, plus the second grade ties



440
441
442
443
# File 'app/models/actor.rb', line 440

def egocentric_ties
  @egocentric_ties ||=
    load_egocentric_ties
end

#following_actor_and_self_idsObject

An array with the ids of Actors followed by this Actor plus the id from this Actor



381
382
383
# File 'app/models/actor.rb', line 381

def following_actor_and_self_ids
  following_actor_ids + [ id ]
end

#following_actor_idsObject

An array with the ids of Actors followed by this Actor



372
373
374
375
376
377
# File 'app/models/actor.rb', line 372

def following_actor_ids
  following_actor_objects.
    includes(:actor).
    map(&:actor).
    map(&:id)
end

#following_actor_objectsObject

The ActivityObjects followed by this Actor that are Actors



366
367
368
369
# File 'app/models/actor.rb', line 366

def following_actor_objects
  followings.
    where('activity_objects.object_type' => "Actor")
end

#mailboxer_email(object) ⇒ Object

Returning the email address of the model if an email should be sent for this object (Message or Notification). If the actor is a Group and has no email address, an array with the email of the highest rank members will be returned isntead.

If no mail has to be sent, return nil.



175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'app/models/actor.rb', line 175

def mailboxer_email(object)
  #If actor has disabled the emails, return nil.
  return nil if !notify_by_email
  #If actor has enabled the emails and has email
  return "#{name} <#{email}>" if email.present?
  #If actor is a Group, has enabled emails but no mail we return the highest_rank ones.
  if (group = self.subject).is_a? Group
    emails = Array.new
    group.relation_notifys.each do |relation|
      receivers = group.contact_actors(:direction => :sent, :relations => relation)
      receivers.each do |receiver|
        next unless Actor.normalize(receiver).subject_type.eql?("User")

        receiver_emails = receiver.mailboxer_email(object)
        case receiver_emails
        when String
          emails << receiver_emails
        when Array
          receiver_emails.each do |receiver_email|
            emails << receiver_email
          end
        end
      end
    end
  return emails
  end
end

#notification_settingsObject



553
554
555
556
# File 'app/models/actor.rb', line 553

def notification_settings
  self.update_attribute(:notification_settings, SocialStream.default_notification_settings) unless super
  super
end

#options_for_contact_selectObject

Return an object of choices suitable for the contact add button or modal



238
239
240
# File 'app/models/actor.rb', line 238

def options_for_contact_select
  relations_for_select.map{ |r| [ r.name, r.id ] }
end

#options_for_contact_select_simple?Boolean

Options for contact select are simple

Returns:

  • (Boolean)


243
244
245
# File 'app/models/actor.rb', line 243

def options_for_contact_select_simple?
  relations_list.count == 1
end

#options_for_contact_select_typeObject

Returns the type for contact select, :simple or :multiple



248
249
250
# File 'app/models/actor.rb', line 248

def options_for_contact_select_type
  options_for_contact_select_simple? ? :simple : :multiple
end

#pending_contactsObject

Build a new Contact from each that has not inverse



524
525
526
527
528
529
# File 'app/models/actor.rb', line 524

def pending_contacts
  received_contacts.pending.includes(:inverse).all.map do |c|
    c.inverse ||
      c.receiver.contact_to!(c.sender)
  end
end

#pending_contacts?Boolean

Returns:

  • (Boolean)


519
520
521
# File 'app/models/actor.rb', line 519

def pending_contacts?
  pending_contacts_count > 0
end

#pending_contacts_countObject



515
516
517
# File 'app/models/actor.rb', line 515

def pending_contacts_count
  received_contacts.not_reflexive.pending.count
end

#positive_sent_contact_actorsObject

The actors this one has established positive ties with



342
343
344
# File 'app/models/actor.rb', line 342

def positive_sent_contact_actors
  sent_contacts.joins(ties: :relation).merge(Relation.positive)
end

#relation_custom(name) ⇒ Object

A given relation defined and managed by this actor



214
215
216
# File 'app/models/actor.rb', line 214

def relation_custom(name)
  relation_customs.find_by_name(name)
end

#relation_customsObject

All the relations defined by this Actor



209
210
211
# File 'app/models/actor.rb', line 209

def relation_customs
  relations.where(:type => 'Relation::Custom')
end

#relation_notifysObject

All relations with the ‘notify’ permission



219
220
221
# File 'app/models/actor.rb', line 219

def relation_notifys
  relations.joins(:relation_permissions => :permission).where('permissions.action' => 'notify')
end

#relations_for_selectObject

The relations offered in the “Add contact” button when subjects add new contacts



233
234
235
# File 'app/models/actor.rb', line 233

def relations_for_select
  relations_list
end

#relations_listObject

The relations that will appear in privacy forms

They usually include Relation::Custom but may also include other system-defined relations that are not editable but appear in add contact buttons



227
228
229
# File 'app/models/actor.rb', line 227

def relations_list
  Relation.system_list(subject) + relation_customs
end

#represented_by?(subject) ⇒ Boolean

Can this actor be represented by subject. Does she has permissions for it?

Returns:

  • (Boolean)


447
448
449
450
451
452
453
454
455
456
# File 'app/models/actor.rb', line 447

def represented_by?(subject)
  return false if subject.blank?

  self.class.normalize(subject) == self ||
    sent_ties.
      merge(Contact.received_by(subject)).
      joins(:relation => :permissions).
      merge(Permission.represent).
      any?
end

#self_contactObject Also known as: ego_contact

The Contact of this Actor to self (totally close!)



358
359
360
# File 'app/models/actor.rb', line 358

def self_contact
  contact_to!(self)
end

#sent_active_contact_countObject



401
402
403
# File 'app/models/actor.rb', line 401

def sent_active_contact_count
  sent_contacts.active.count
end

#sent_active_contact_idsObject



405
406
407
408
# File 'app/models/actor.rb', line 405

def sent_active_contact_ids
  @sent_active_contact_ids ||=
    load_sent_active_contact_ids
end

#subjectObject

The subject instance for this actor



204
205
206
# File 'app/models/actor.rb', line 204

def subject
  subtype_instance
end

#suggestions(size = 1) ⇒ Contact

By now, it returns a suggested Contact to another Actor without any current Tie

Suggested actor types can be configured in SocialStream.suggestion_models in config/initializers/social_stream.rb

Returns:



416
417
418
419
420
421
422
423
424
425
426
427
# File 'app/models/actor.rb', line 416

def suggestions(size = 1)
  candidates =
    Actor.
      where(subject_type: SocialStream.suggested_models.map{ |m| m.to_s.classify }).
      where(Actor.arel_table[:id].not_in(sent_active_contact_ids + [id]))

  size.times.map {
    candidates.delete_at rand(candidates.size)
  }.compact.map { |a|
    contact_to! a
  }
end

#ties_to(subject) ⇒ Object

Set of ties sent by this actor received by subject



430
431
432
# File 'app/models/actor.rb', line 430

def ties_to(subject)
  sent_ties.merge(Contact.received_by(subject))
end

#ties_to?(subject) ⇒ Boolean

Is there any Tie sent by this actor and received by subject

Returns:

  • (Boolean)


435
436
437
# File 'app/models/actor.rb', line 435

def ties_to?(subject)
  ties_to(subject).present?
end

#to_paramObject

Use slug as parameter



537
538
539
# File 'app/models/actor.rb', line 537

def to_param
  slug
end