Class: Actor
- Inherits:
-
ActiveRecord::Base
- Object
- ActiveRecord::Base
- Actor
- Defined in:
- app/models/actor.rb
Overview
An Actor represents a social entity. This includes 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 o group information, contact data, etc.
Actor subtypes
An actor subtype is called a Subject. SocialStream provides two actor subtypes, User and Group, but the application developer can define as many actor subtypes as required. Actor subtypes are added to config/initializers/social_stream.rb
Class Method Summary collapse
- .find_by_webfinger!(link) ⇒ Object
-
.normalize(a) ⇒ Object
Get actor from object, if possible.
-
.normalize_id(a) ⇒ Object
Get actor’s id from an object, if possible.
Instance Method Summary collapse
- #activity_relations(subject, options = {}) ⇒ Object
-
#activity_relations?(*args) ⇒ Boolean
Are there any activity_relations present?.
- #avatar! ⇒ Object
-
#can_comment?(activity) ⇒ Boolean
Is this Actor allowed to create a comment on activity?.
-
#cheesecake_json ⇒ Object
JSON compatible with SocialCheesecake.
-
#comment_relations(activity) ⇒ Object
Are there any relations that allow this actor to create a comment on activity?.
-
#common_contacts_count(subject) ⇒ Object
Count the contacts in common between this Actor and subject.
-
#contact_actors(options = {}) ⇒ Object
All the Actors this one has ties with:.
- #contact_subjects(options = {}) ⇒ Object
-
#contact_to(subject) ⇒ Object
Return a contact to subject.
-
#contact_to!(subject) ⇒ Object
Return a contact to subject.
- #ego_contact ⇒ Object
-
#egocentric_ties ⇒ Object
The ties sent by this actor, plus the second grade ties.
-
#liked_by(subject) ⇒ Object
:nodoc:.
-
#liked_by?(subject) ⇒ Boolean
Does subject like this Actor?.
-
#likes ⇒ Object
The ‘like’ qualifications emmited to this actor.
- #logo ⇒ Object
-
#mailboxer_email(object) ⇒ Object
Returning the email address of the model if an email should be sent for this object (Message or Notification).
-
#new_like(subject) ⇒ Object
Build a new activity where subject like this.
-
#pending_contacts ⇒ Object
Build a new Contact from each that has not inverse.
- #pending_contacts? ⇒ Boolean
- #pending_contacts_count ⇒ Object
-
#relation_custom(name) ⇒ Object
A given relation defined and managed by this actor.
- #relation_customs ⇒ Object
-
#relation_public ⇒ Object
The Relation::Public for this Actor.
-
#relation_reject ⇒ Object
The Relation::Reject for this Actor.
-
#represented_by?(subject) ⇒ Boolean
Can this actor be represented by subject.
- #sent_active_contact_ids ⇒ Object
-
#subject ⇒ Object
The subject instance for this actor.
- #suggestions(size = 1) ⇒ Contact
-
#ties_to(subject) ⇒ Object
Set of ties sent by this actor received by subject.
-
#ties_to?(subject) ⇒ Boolean
Is there any Tie sent by this actor and received by subject.
-
#to_param ⇒ Object
Use slug as parameter.
-
#wall(type, options = {}) ⇒ Object
The set of activities in the wall of this Actor.
Class Method Details
.find_by_webfinger!(link) ⇒ Object
146 147 148 149 150 |
# File 'app/models/actor.rb', line 146 def find_by_webfinger!(link) link =~ /(acct:)?(.*)@/ find_by_slug! $2 end |
.normalize(a) ⇒ Object
Get actor from object, if possible
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
# File 'app/models/actor.rb', line 129 def normalize(a) case a when Actor a when Integer Actor.find a when Array a.map{ |e| Actor.normalize(e) } else begin a.actor rescue raise "Unable to normalize actor #{ a.inspect }" end end end |
Instance Method Details
#activity_relations(subject, options = {}) ⇒ Object
367 368 369 370 371 372 373 |
# File 'app/models/actor.rb', line 367 def activity_relations(subject, = {}) if Actor.normalize(subject) == self return relation_customs + Array.wrap(relation_public) else Array.new end end |
#activity_relations?(*args) ⇒ Boolean
Are there any activity_relations present?
376 377 378 |
# File 'app/models/actor.rb', line 376 def activity_relations?(*args) activity_relations(*args).any? end |
#avatar! ⇒ Object
467 468 469 |
# File 'app/models/actor.rb', line 467 def avatar! avatar || avatars.build 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
383 384 385 386 387 |
# File 'app/models/actor.rb', line 383 def can_comment?(activity) return true comment_relations(activity).any? end |
#cheesecake_json ⇒ Object
JSON compatible with SocialCheesecake
503 504 505 506 507 508 509 510 |
# File 'app/models/actor.rb', line 503 def cheesecake_json { :sectors => relation_customs.includes(:ties => :contact).map { |r| r.to_cheesecake_hash } }.to_json end |
#comment_relations(activity) ⇒ Object
Are there any relations that allow this actor to create a comment on activity?
390 391 392 393 |
# File 'app/models/actor.rb', line 390 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
412 413 414 |
# File 'app/models/actor.rb', line 412 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 notactor.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 relationsactor.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
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 |
# File 'app/models/actor.rb', line 230 def contact_actors( = {}) subject_types = Array([: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 [:load_subjects].nil? || [: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 [:direction].blank? rcv_opts = .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) [:direction] = :sent end case [: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 [:include_self].blank? as = as.where("actors.id != ?", self.id) end if [:relations].present? as = as.merge(Tie.([:relations])) else as = as.merge(Relation.where(:type => ['Relation::Custom', 'Relation::Public'])) end as end |
#contact_subjects(options = {}) ⇒ Object
289 290 291 292 293 294 295 296 297 |
# File 'app/models/actor.rb', line 289 def contact_subjects( = {}) as = contact_actors() if block_given? as = yield(as) end as.map(&:subject) end |
#contact_to(subject) ⇒ Object
Return a contact to subject.
300 301 302 |
# File 'app/models/actor.rb', line 300 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
305 306 307 308 |
# File 'app/models/actor.rb', line 305 def contact_to!(subject) contact_to(subject) || sent_contacts.create!(:receiver => Actor.normalize(subject)) end |
#ego_contact ⇒ Object
311 312 313 |
# File 'app/models/actor.rb', line 311 def ego_contact contact_to!(self) end |
#egocentric_ties ⇒ Object
The ties sent by this actor, plus the second grade ties
344 345 346 347 |
# File 'app/models/actor.rb', line 344 def egocentric_ties @egocentric_ties ||= load_egocentric_ties end |
#liked_by(subject) ⇒ Object
:nodoc:
477 478 479 |
# File 'app/models/actor.rb', line 477 def liked_by(subject) #:nodoc: likes.joins(:contact).merge(Contact.sent_by(subject)) end |
#liked_by?(subject) ⇒ Boolean
Does subject like this Actor?
482 483 484 |
# File 'app/models/actor.rb', line 482 def liked_by?(subject) liked_by(subject).present? end |
#likes ⇒ Object
The ‘like’ qualifications emmited to this actor
472 473 474 475 |
# File 'app/models/actor.rb', line 472 def likes Activity.joins(:activity_verb).where('activity_verbs.name' => "like"). joins(:activity_objects).where('activity_objects.id' => activity_object_id) end |
#logo ⇒ Object
463 464 465 |
# File 'app/models/actor.rb', line 463 def logo avatar!.logo 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.
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 |
# File 'app/models/actor.rb', line 158 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 relation = group.relation_customs.sort.first receivers = group.contact_actors(:direction => :sent, :relations => relation) emails = Array.new 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 return emails end end |
#new_like(subject) ⇒ Object
Build a new activity where subject like this
487 488 489 490 491 492 493 494 495 |
# File 'app/models/actor.rb', line 487 def new_like(subject) a = Activity.new :verb => "like", :contact => subject.contact_to!(self), :relation_ids => Array(subject.relation_public.id) a.activity_objects << activity_object a end |
#pending_contacts ⇒ Object
Build a new Contact from each that has not inverse
404 405 406 407 408 409 |
# File 'app/models/actor.rb', line 404 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
399 400 401 |
# File 'app/models/actor.rb', line 399 def pending_contacts? pending_contacts_count > 0 end |
#pending_contacts_count ⇒ Object
395 396 397 |
# File 'app/models/actor.rb', line 395 def pending_contacts_count received_contacts.not_reflexive.pending.count end |
#relation_custom(name) ⇒ Object
A given relation defined and managed by this actor
196 197 198 |
# File 'app/models/actor.rb', line 196 def relation_custom(name) relation_customs.find_by_name(name) end |
#relation_customs ⇒ Object
191 192 193 |
# File 'app/models/actor.rb', line 191 def relation_customs relations.where(:type => 'Relation::Custom') end |
#relation_public ⇒ Object
The Relation::Public for this Actor
201 202 203 |
# File 'app/models/actor.rb', line 201 def relation_public Relation::Public.of(self) end |
#relation_reject ⇒ Object
The Relation::Reject for this Actor
206 207 208 |
# File 'app/models/actor.rb', line 206 def relation_reject Relation::Reject.of(self) end |
#represented_by?(subject) ⇒ Boolean
Can this actor be represented by subject. Does she has permissions for it?
351 352 353 354 355 356 357 358 359 360 |
# File 'app/models/actor.rb', line 351 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 |
#sent_active_contact_ids ⇒ Object
315 316 317 318 |
# File 'app/models/actor.rb', line 315 def sent_active_contact_ids @sent_active_contact_ids ||= load_sent_active_contact_ids end |
#subject ⇒ Object
The subject instance for this actor
186 187 188 |
# File 'app/models/actor.rb', line 186 def subject subtype_instance end |
#suggestions(size = 1) ⇒ Contact
323 324 325 326 327 328 329 330 331 |
# File 'app/models/actor.rb', line 323 def suggestions(size = 1) candidates = Actor.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
334 335 336 |
# File 'app/models/actor.rb', line 334 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
339 340 341 |
# File 'app/models/actor.rb', line 339 def ties_to?(subject) ties_to(subject).present? end |
#to_param ⇒ Object
Use slug as parameter
498 499 500 |
# File 'app/models/actor.rb', line 498 def to_param slug end |
#wall(type, options = {}) ⇒ Object
The set of activities in the wall of this Actor.
There are two types of walls:
- home
-
includes all the activities from this Actor and their followed actors
See {Permission permissions} for more information on the following support
- profile
-
The set of activities in the wall profile of this Actor, it includes only the activities from the ties of this actor that can be read by the subject
Options:
- :for
-
the subject that is accessing the wall
- :relation
-
show only activities that are attached at this relation level. For example, the wall for members of the group.
429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 |
# File 'app/models/actor.rb', line 429 def wall(type, = {}) args = {} args[:type] = type args[:owner] = self # Preserve this options [ :for, :object_type ].each do |opt| args[opt] = [opt] end if type == :home args[:followed] = Actor.followed_by(self).map(&:id) end # TODO: this is not scalling for sure. We must use a fact table in the future args[:relation_ids] = case type when :home # The relations from followings that can be read Relation.allow(self, 'read', 'activity').map(&:id) when :profile # FIXME: options[:relation] # # The relations that can be read by options[:for] [:for].present? ? Relation.allow([:for], 'read', 'activity').map(&:id) : [] else raise "Unknown type of wall: #{ type }" end Activity.wall args end |