# A {Tie} is a link between two {Actor Actors} ({Contact}) with a {Relation}. # # The first {Actor} is the sender of the {Tie}. The second {Actor} # is the receiver of the {Tie}. # # = Tie strengh # # Because each {Tie} belongs to a {Relation} and {Relation Relations} have strength # hierarchies, {Tie Ties} also have them. A {Tie} is stronger than other if its # {Relation} is stronger than the other's. For example, if _Alice_ has a _friend_ {Tie} # with _Bob_, and an _acquaintance_ {Tie} with _Charlie_, given that _friend_ {Relation} # is stronger than _acquaintance_, the {Tie} with _Bob_ is stronger than the {Tie} with # _Charlie_. # # = Authorization # When an {Actor} establishes a {Tie} with other, she is granting a set of # {Permission Permissions} to them (posting to her wall, reading her posts, etc..) # The set of {Permission Permissions} granted are associated with the {Relation} of # the {Tie}. # # Usually, stronger ties (and relations) have more permissions than weaker ones. # # = Scopes # There are several scopes defined: # # sent_by(actor):: ties whose sender is actor # received_by(actor):: ties whose receiver is actor # sent_or_received_by(actor):: the union of the former # related_by(relation):: ties with this relation. Accepts relation, relation_name, # integer, array class Tie < ActiveRecord::Base belongs_to :contact, :counter_cache => true has_one :sender, :through => :contact has_one :receiver, :through => :contact belongs_to :relation scope :recent, order("ties.created_at DESC") scope :sent_by, lambda { |a| joins(:contact).merge(Contact.sent_by(a)) } scope :received_by, lambda { |a| joins(:contact).merge(Contact.received_by(a)) } scope :sent_or_received_by, lambda { |a| joins(:contact).merge(Contact.sent_or_received_by(a)) } scope :related_by, lambda { |r| if r.present? where(:relation_id => Relation.normalize_id(r)) end } scope :with_permissions, lambda { |action, object| joins(:relation => :permissions). where('permissions.action' => action). where('permissions.object' => object) } validates_presence_of :contact_id, :relation_id validate :relation_belongs_to_sender after_create :create_activity after_create :increment_follower_count after_destroy :decrement_follower_count def relation_name relation.try(:name) end def sender_subject sender.subject end def receiver_subject receiver.subject end private # before_create callback # # Create contact activity if this is the first tie def create_activity return if contact.reload.ties_count != 1 || relation.is_a?(Relation::Public) Activity.create! :contact => contact, :relation_ids => contact.relation_ids, :activity_verb => ActivityVerb[contact.verb] end # after_create callback # # Increment the {Actor}'s follower_count def increment_follower_count return if contact.reflexive? || !relation.permissions.include?(Permission.follow.first) # Because we allow several ties from the same sender to the same receiver, # we check the receiver does not already have a follower tie from this sender return if Tie.sent_by(sender). received_by(receiver). with_permissions('follow', nil). where("ties.id != ?", id). present? receiver.increment!(:follower_count) end # after_destroy callback # # Decrement the {Actor}'s follower_count def decrement_follower_count return if contact.reflexive? || !relation.permissions.include?(Permission.follow.first) # Because we allow several ties from the same sender to the same receiver, # we check the receiver does not still have a follower tie from this sender return if Tie.sent_by(sender). received_by(receiver). with_permissions('follow', nil). present? receiver.decrement!(:follower_count) end def relation_belongs_to_sender errors.add(:relation, "Relation must belong to sender") unless contact.sender_id == relation.actor_id end end