Merge branch 'participation_refactor'
This commit is contained in:
commit
f8ec80b62d
25 changed files with 526 additions and 374 deletions
|
|
@ -103,7 +103,7 @@ class AccountDeleter
|
||||||
end
|
end
|
||||||
|
|
||||||
def normal_ar_person_associates_to_delete
|
def normal_ar_person_associates_to_delete
|
||||||
[:posts, :photos, :mentions]
|
[:posts, :photos, :mentions, :participations]
|
||||||
end
|
end
|
||||||
|
|
||||||
def ignored_or_special_ar_person_associations
|
def ignored_or_special_ar_person_associations
|
||||||
|
|
|
||||||
|
|
@ -84,4 +84,19 @@ class Comment < ActiveRecord::Base
|
||||||
def parent= parent
|
def parent= parent
|
||||||
self.post = parent
|
self.post = parent
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class Generator < Federated::Generator
|
||||||
|
def self.federated_class
|
||||||
|
Comment
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(person, target, text)
|
||||||
|
@text = text
|
||||||
|
super(person, target)
|
||||||
|
end
|
||||||
|
|
||||||
|
def relayable_options
|
||||||
|
{:post => @target, :text => @text}
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,26 @@
|
||||||
# licensed under the Affero General Public License version 3 or later. See
|
# licensed under the Affero General Public License version 3 or later. See
|
||||||
# the COPYRIGHT file.
|
# the COPYRIGHT file.
|
||||||
|
|
||||||
class Like < ActiveRecord::Base
|
class Like < Federated::Relayable
|
||||||
include ROXML
|
class Generator < Federated::Generator
|
||||||
|
def self.federated_class
|
||||||
|
Like
|
||||||
|
end
|
||||||
|
|
||||||
include Diaspora::Webhooks
|
def relayable_options
|
||||||
include Diaspora::Guid
|
{:target => @target, :positive => true}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
xml_attr :target_type
|
after_create do
|
||||||
include Diaspora::Relayable
|
self.parent.update_likes_counter
|
||||||
|
end
|
||||||
|
|
||||||
|
after_destroy do
|
||||||
|
self.parent.update_likes_counter
|
||||||
|
end
|
||||||
|
|
||||||
|
xml_attr :positive
|
||||||
|
|
||||||
# NOTE API V1 to be extracted
|
# NOTE API V1 to be extracted
|
||||||
acts_as_api
|
acts_as_api
|
||||||
|
|
@ -20,43 +32,6 @@ class Like < ActiveRecord::Base
|
||||||
t.add :created_at
|
t.add :created_at
|
||||||
end
|
end
|
||||||
|
|
||||||
xml_attr :positive
|
|
||||||
xml_attr :diaspora_handle
|
|
||||||
|
|
||||||
belongs_to :target, :polymorphic => true
|
|
||||||
belongs_to :author, :class_name => 'Person'
|
|
||||||
|
|
||||||
validates_uniqueness_of :target_id, :scope => [:target_type, :author_id]
|
|
||||||
validates :parent, :presence => true #should be in relayable (pending on fixing Message)
|
|
||||||
|
|
||||||
after_create do
|
|
||||||
self.parent.update_likes_counter
|
|
||||||
end
|
|
||||||
|
|
||||||
after_destroy do
|
|
||||||
self.parent.update_likes_counter
|
|
||||||
end
|
|
||||||
|
|
||||||
def diaspora_handle
|
|
||||||
self.author.diaspora_handle
|
|
||||||
end
|
|
||||||
|
|
||||||
def diaspora_handle= nh
|
|
||||||
self.author = Webfinger.new(nh).fetch
|
|
||||||
end
|
|
||||||
|
|
||||||
def parent_class
|
|
||||||
self.target_type.constantize
|
|
||||||
end
|
|
||||||
|
|
||||||
def parent
|
|
||||||
self.target
|
|
||||||
end
|
|
||||||
|
|
||||||
def parent= parent
|
|
||||||
self.target = parent
|
|
||||||
end
|
|
||||||
|
|
||||||
def notification_type(user, person)
|
def notification_type(user, person)
|
||||||
#TODO(dan) need to have a notification for likes on comments, until then, return nil
|
#TODO(dan) need to have a notification for likes on comments, until then, return nil
|
||||||
return nil if self.target_type == "Comment"
|
return nil if self.target_type == "Comment"
|
||||||
|
|
|
||||||
11
app/models/participation.rb
Normal file
11
app/models/participation.rb
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
class Participation < Federated::Relayable
|
||||||
|
class Generator < Federated::Generator
|
||||||
|
def self.federated_class
|
||||||
|
Participation
|
||||||
|
end
|
||||||
|
|
||||||
|
def relayable_options
|
||||||
|
{:target => @target}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -44,6 +44,7 @@ class Person < ActiveRecord::Base
|
||||||
has_many :posts, :foreign_key => :author_id, :dependent => :destroy # This person's own posts
|
has_many :posts, :foreign_key => :author_id, :dependent => :destroy # This person's own posts
|
||||||
has_many :photos, :foreign_key => :author_id, :dependent => :destroy # This person's own photos
|
has_many :photos, :foreign_key => :author_id, :dependent => :destroy # This person's own photos
|
||||||
has_many :comments, :foreign_key => :author_id, :dependent => :destroy # This person's own comments
|
has_many :comments, :foreign_key => :author_id, :dependent => :destroy # This person's own comments
|
||||||
|
has_many :participations, :foreign_key => :author_id, :dependent => :destroy
|
||||||
|
|
||||||
belongs_to :owner, :class_name => 'User'
|
belongs_to :owner, :class_name => 'User'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@ class Post < ActiveRecord::Base
|
||||||
include Diaspora::Commentable
|
include Diaspora::Commentable
|
||||||
include Diaspora::Shareable
|
include Diaspora::Shareable
|
||||||
|
|
||||||
|
has_many :participations, :dependent => :delete_all, :as => :target
|
||||||
|
|
||||||
attr_accessor :user_like
|
attr_accessor :user_like
|
||||||
|
|
||||||
# NOTE API V1 to be extracted
|
# NOTE API V1 to be extracted
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,17 @@
|
||||||
# licensed under the Affero General Public License version 3 or later. See
|
# licensed under the Affero General Public License version 3 or later. See
|
||||||
# the COPYRIGHT file.
|
# the COPYRIGHT file.
|
||||||
|
|
||||||
require File.join(Rails.root, 'lib/diaspora/user')
|
|
||||||
require File.join(Rails.root, 'lib/salmon/salmon')
|
require File.join(Rails.root, 'lib/salmon/salmon')
|
||||||
require File.join(Rails.root, 'lib/postzord/dispatcher')
|
require File.join(Rails.root, 'lib/postzord/dispatcher')
|
||||||
require 'rest-client'
|
require 'rest-client'
|
||||||
|
|
||||||
class User < ActiveRecord::Base
|
class User < ActiveRecord::Base
|
||||||
include Diaspora::UserModules
|
|
||||||
include Encryptor::Private
|
include Encryptor::Private
|
||||||
|
|
||||||
|
include Connecting
|
||||||
|
include Querying
|
||||||
|
include SocialActions
|
||||||
|
|
||||||
devise :invitable, :database_authenticatable, :registerable,
|
devise :invitable, :database_authenticatable, :registerable,
|
||||||
:recoverable, :rememberable, :trackable, :validatable,
|
:recoverable, :rememberable, :trackable, :validatable,
|
||||||
:timeoutable, :token_authenticatable, :lockable,
|
:timeoutable, :token_authenticatable, :lockable,
|
||||||
|
|
@ -33,7 +35,7 @@ class User < ActiveRecord::Base
|
||||||
serialize :hidden_shareables, Hash
|
serialize :hidden_shareables, Hash
|
||||||
|
|
||||||
has_one :person, :foreign_key => :owner_id
|
has_one :person, :foreign_key => :owner_id
|
||||||
delegate :public_key, :posts, :photos, :owns?, :diaspora_handle, :name, :public_url, :profile, :first_name, :last_name, :to => :person
|
delegate :public_key, :posts, :photos, :owns?, :diaspora_handle, :name, :public_url, :profile, :first_name, :last_name, :participations, :to => :person
|
||||||
|
|
||||||
has_many :invitations_from_me, :class_name => 'Invitation', :foreign_key => :sender_id
|
has_many :invitations_from_me, :class_name => 'Invitation', :foreign_key => :sender_id
|
||||||
has_many :invitations_to_me, :class_name => 'Invitation', :foreign_key => :recipient_id
|
has_many :invitations_to_me, :class_name => 'Invitation', :foreign_key => :recipient_id
|
||||||
|
|
@ -284,43 +286,6 @@ class User < ActiveRecord::Base
|
||||||
Salmon::EncryptedSlap.create_by_user_and_activity(self, post.to_diaspora_xml)
|
Salmon::EncryptedSlap.create_by_user_and_activity(self, post.to_diaspora_xml)
|
||||||
end
|
end
|
||||||
|
|
||||||
def comment!(post, text, opts={})
|
|
||||||
comment = build_comment(opts.merge!(:post => post, :text => text))
|
|
||||||
if comment.save
|
|
||||||
dispatch_post(comment)
|
|
||||||
comment
|
|
||||||
else
|
|
||||||
false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def like!(target, opts={})
|
|
||||||
like = build_like(opts.merge!(:target => target, :positive => true))
|
|
||||||
if like.save
|
|
||||||
dispatch_post(like)
|
|
||||||
like
|
|
||||||
else
|
|
||||||
false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def build_relayable(model, options = {})
|
|
||||||
r = model.new(options.merge(:author_id => self.person.id))
|
|
||||||
r.set_guid
|
|
||||||
r.initialize_signatures
|
|
||||||
r
|
|
||||||
end
|
|
||||||
|
|
||||||
######## Commenting ########
|
|
||||||
def build_comment(options = {})
|
|
||||||
build_relayable(Comment, options)
|
|
||||||
end
|
|
||||||
|
|
||||||
######## Liking ########
|
|
||||||
def build_like(options = {})
|
|
||||||
build_relayable(Like, options)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Check whether the user has liked a post.
|
# Check whether the user has liked a post.
|
||||||
# @param [Post] post
|
# @param [Post] post
|
||||||
def liked?(target)
|
def liked?(target)
|
||||||
|
|
|
||||||
70
app/models/user/connecting.rb
Normal file
70
app/models/user/connecting.rb
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
# Copyright (c) 2010-2011, Diaspora Inc. This file is
|
||||||
|
# licensed under the Affero General Public License version 3 or later. See
|
||||||
|
# the COPYRIGHT file.
|
||||||
|
|
||||||
|
module User::Connecting
|
||||||
|
# This will create a contact on the side of the sharer and the sharee.
|
||||||
|
# @param [Person] person The person to start sharing with.
|
||||||
|
# @param [Aspect] aspect The aspect to add them to.
|
||||||
|
# @return [Contact] The newly made contact for the passed in person.
|
||||||
|
def share_with(person, aspect)
|
||||||
|
contact = self.contacts.find_or_initialize_by_person_id(person.id)
|
||||||
|
return false unless contact.valid?
|
||||||
|
|
||||||
|
unless contact.receiving?
|
||||||
|
contact.dispatch_request
|
||||||
|
contact.receiving = true
|
||||||
|
end
|
||||||
|
|
||||||
|
contact.aspects << aspect
|
||||||
|
contact.save
|
||||||
|
|
||||||
|
if notification = Notification.where(:target_id => person.id).first
|
||||||
|
notification.update_attributes(:unread=>false)
|
||||||
|
end
|
||||||
|
|
||||||
|
register_share_visibilities(contact)
|
||||||
|
contact
|
||||||
|
end
|
||||||
|
|
||||||
|
# This puts the last 100 public posts by the passed in contact into the user's stream.
|
||||||
|
# @param [Contact] contact
|
||||||
|
# @return [void]
|
||||||
|
def register_share_visibilities(contact)
|
||||||
|
#should have select here, but proven hard to test
|
||||||
|
posts = Post.where(:author_id => contact.person_id, :public => true).limit(100)
|
||||||
|
p = posts.map do |post|
|
||||||
|
ShareVisibility.new(:contact_id => contact.id, :shareable_id => post.id, :shareable_type => 'Post')
|
||||||
|
end
|
||||||
|
ShareVisibility.import(p) unless posts.empty?
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def remove_contact(contact, opts={:force => false})
|
||||||
|
posts = contact.posts.all
|
||||||
|
|
||||||
|
if !contact.mutual? || opts[:force]
|
||||||
|
contact.destroy
|
||||||
|
else
|
||||||
|
contact.update_attributes(:receiving => false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def disconnect(bad_contact, opts={})
|
||||||
|
person = bad_contact.person
|
||||||
|
Rails.logger.info("event=disconnect user=#{diaspora_handle} target=#{person.diaspora_handle}")
|
||||||
|
retraction = Retraction.for(self)
|
||||||
|
retraction.subscribers = [person]#HAX
|
||||||
|
Postzord::Dispatcher.build(self, retraction).post
|
||||||
|
|
||||||
|
AspectMembership.where(:contact_id => bad_contact.id).delete_all
|
||||||
|
remove_contact(bad_contact, opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
def disconnected_by(person)
|
||||||
|
Rails.logger.info("event=disconnected_by user=#{diaspora_handle} target=#{person.diaspora_handle}")
|
||||||
|
if contact = self.contact_for(person)
|
||||||
|
remove_contact(contact)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
164
app/models/user/querying.rb
Normal file
164
app/models/user/querying.rb
Normal file
|
|
@ -0,0 +1,164 @@
|
||||||
|
# Copyright (c) 2010-2011, Diaspora Inc. This file is
|
||||||
|
# licensed under the Affero General Public License version 3 or later. See
|
||||||
|
# the COPYRIGHT file.
|
||||||
|
|
||||||
|
require File.join(Rails.root, 'lib', 'evil_query')
|
||||||
|
|
||||||
|
|
||||||
|
#TODO: THIS FILE SHOULD NOT EXIST, EVIL SQL SHOULD BE ENCAPSULATED IN EvilQueries,
|
||||||
|
#throwing all of this stuff in user violates demeter like WHOA
|
||||||
|
module User::Querying
|
||||||
|
def find_visible_shareable_by_id(klass, id, opts={} )
|
||||||
|
key = (opts.delete(:key) || :id)
|
||||||
|
::EvilQuery::VisibleShareableById.new(self, klass, key, id, opts).post!
|
||||||
|
end
|
||||||
|
|
||||||
|
def visible_shareables(klass, opts={})
|
||||||
|
opts = prep_opts(klass, opts)
|
||||||
|
shareable_ids = visible_shareable_ids(klass, opts)
|
||||||
|
klass.where(:id => shareable_ids).select('DISTINCT '+klass.to_s.tableize+'.*').limit(opts[:limit]).order(opts[:order_with_table])
|
||||||
|
end
|
||||||
|
|
||||||
|
def visible_shareable_ids(klass, opts={})
|
||||||
|
opts = prep_opts(klass, opts)
|
||||||
|
visible_ids_from_sql(klass, opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
# @return [Array<Integer>]
|
||||||
|
def visible_ids_from_sql(klass, opts={})
|
||||||
|
opts = prep_opts(klass, opts)
|
||||||
|
opts[:klass] = klass
|
||||||
|
opts[:by_members_of] ||= self.aspect_ids
|
||||||
|
|
||||||
|
post_ids = klass.connection.select_values(visible_shareable_sql(klass, opts)).map { |id| id.to_i }
|
||||||
|
post_ids += klass.connection.select_values(construct_public_followings_sql(opts).to_sql).map {|id| id.to_i }
|
||||||
|
end
|
||||||
|
|
||||||
|
def visible_shareable_sql(klass, opts={})
|
||||||
|
table = klass.table_name
|
||||||
|
opts = prep_opts(klass, opts)
|
||||||
|
opts[:klass] = klass
|
||||||
|
|
||||||
|
shareable_from_others = construct_shareable_from_others_query(opts)
|
||||||
|
shareable_from_self = construct_shareable_from_self_query(opts)
|
||||||
|
|
||||||
|
"(#{shareable_from_others.to_sql} LIMIT #{opts[:limit]}) UNION ALL (#{shareable_from_self.to_sql} LIMIT #{opts[:limit]}) ORDER BY #{opts[:order]} LIMIT #{opts[:limit]}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def ugly_select_clause(query, opts)
|
||||||
|
klass = opts[:klass]
|
||||||
|
select_clause ='DISTINCT %s.id, %s.updated_at AS updated_at, %s.created_at AS created_at' % [klass.table_name, klass.table_name, klass.table_name]
|
||||||
|
query.select(select_clause).order(opts[:order_with_table]).where(klass.arel_table[opts[:order_field]].lt(opts[:max_time]))
|
||||||
|
end
|
||||||
|
|
||||||
|
def construct_shareable_from_others_query(opts)
|
||||||
|
conditions = {
|
||||||
|
:pending => false,
|
||||||
|
:share_visibilities => {:hidden => opts[:hidden]},
|
||||||
|
:contacts => {:user_id => self.id, :receiving => true}
|
||||||
|
}
|
||||||
|
|
||||||
|
conditions[:type] = opts[:type] if opts.has_key?(:type)
|
||||||
|
|
||||||
|
query = opts[:klass].joins(:contacts).where(conditions)
|
||||||
|
|
||||||
|
if opts[:by_members_of]
|
||||||
|
query = query.joins(:contacts => :aspect_memberships).where(
|
||||||
|
:aspect_memberships => {:aspect_id => opts[:by_members_of]})
|
||||||
|
end
|
||||||
|
|
||||||
|
ugly_select_clause(query, opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
def construct_public_followings_sql(opts)
|
||||||
|
aspects = Aspect.where(:id => opts[:by_members_of])
|
||||||
|
person_ids = Person.connection.select_values(people_in_aspects(aspects).select("people.id").to_sql)
|
||||||
|
|
||||||
|
query = opts[:klass].where(:author_id => person_ids, :public => true, :pending => false)
|
||||||
|
|
||||||
|
unless(opts[:klass] == Photo)
|
||||||
|
query = query.where(:type => opts[:type])
|
||||||
|
end
|
||||||
|
|
||||||
|
ugly_select_clause(query, opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
def construct_shareable_from_self_query(opts)
|
||||||
|
conditions = {:pending => false }
|
||||||
|
conditions[:type] = opts[:type] if opts.has_key?(:type)
|
||||||
|
query = self.person.send(opts[:klass].to_s.tableize).where(conditions)
|
||||||
|
|
||||||
|
if opts[:by_members_of]
|
||||||
|
query = query.joins(:aspect_visibilities).where(:aspect_visibilities => {:aspect_id => opts[:by_members_of]})
|
||||||
|
end
|
||||||
|
|
||||||
|
ugly_select_clause(query, opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
def contact_for(person)
|
||||||
|
return nil unless person
|
||||||
|
contact_for_person_id(person.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def aspects_with_shareable(base_class_name_or_class, shareable_id)
|
||||||
|
base_class_name = base_class_name_or_class
|
||||||
|
base_class_name = base_class_name_or_class.base_class.to_s if base_class_name_or_class.is_a?(Class)
|
||||||
|
self.aspects.joins(:aspect_visibilities).where(:aspect_visibilities => {:shareable_id => shareable_id, :shareable_type => base_class_name})
|
||||||
|
end
|
||||||
|
|
||||||
|
def contact_for_person_id(person_id)
|
||||||
|
Contact.where(:user_id => self.id, :person_id => person_id).includes(:person => :profile).first
|
||||||
|
end
|
||||||
|
|
||||||
|
# @param [Person] person
|
||||||
|
# @return [Boolean] whether person is a contact of this user
|
||||||
|
def has_contact_for?(person)
|
||||||
|
Contact.exists?(:user_id => self.id, :person_id => person.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def people_in_aspects(requested_aspects, opts={})
|
||||||
|
allowed_aspects = self.aspects & requested_aspects
|
||||||
|
aspect_ids = allowed_aspects.map(&:id)
|
||||||
|
|
||||||
|
people = Person.in_aspects(aspect_ids)
|
||||||
|
|
||||||
|
if opts[:type] == 'remote'
|
||||||
|
people = people.where(:owner_id => nil)
|
||||||
|
elsif opts[:type] == 'local'
|
||||||
|
people = people.where('people.owner_id IS NOT NULL')
|
||||||
|
end
|
||||||
|
people
|
||||||
|
end
|
||||||
|
|
||||||
|
def aspects_with_person person
|
||||||
|
contact_for(person).aspects
|
||||||
|
end
|
||||||
|
|
||||||
|
def posts_from(person)
|
||||||
|
::EvilQuery::ShareablesFromPerson.new(self, Post, person).make_relation!
|
||||||
|
end
|
||||||
|
|
||||||
|
def photos_from(person)
|
||||||
|
::EvilQuery::ShareablesFromPerson.new(self, Photo, person).make_relation!
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
# @return [Hash]
|
||||||
|
def prep_opts(klass, opts)
|
||||||
|
defaults = {
|
||||||
|
:order => 'created_at DESC',
|
||||||
|
:limit => 15,
|
||||||
|
:hidden => false
|
||||||
|
}
|
||||||
|
defaults[:type] = Stream::Base::TYPES_OF_POST_IN_STREAM if klass == Post
|
||||||
|
opts = defaults.merge(opts)
|
||||||
|
|
||||||
|
opts[:order_field] = opts[:order].split.first.to_sym
|
||||||
|
opts[:order_with_table] = klass.table_name + '.' + opts[:order]
|
||||||
|
|
||||||
|
opts[:max_time] = Time.at(opts[:max_time]) if opts[:max_time].is_a?(Integer)
|
||||||
|
opts[:max_time] ||= Time.now + 1
|
||||||
|
opts
|
||||||
|
end
|
||||||
|
end
|
||||||
19
app/models/user/social_actions.rb
Normal file
19
app/models/user/social_actions.rb
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
module User::SocialActions
|
||||||
|
def comment!(target, text, opts={})
|
||||||
|
participations.where(:target_id => target).first || participate!(target)
|
||||||
|
Comment::Generator.new(self, target, text).create!(opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
def participate!(target, opts={})
|
||||||
|
Participation::Generator.new(self, target).create!(opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
def like!(target, opts={})
|
||||||
|
participations.where(:target_id => target).first || participate!(target)
|
||||||
|
Like::Generator.new(self, target).create!(opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
def build_comment(options={})
|
||||||
|
Comment::Generator.new(self, options.delete(:post), options.delete(:text)).build(options)
|
||||||
|
end
|
||||||
|
end
|
||||||
17
db/migrate/20120208231253_create_participations.rb
Normal file
17
db/migrate/20120208231253_create_participations.rb
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
class CreateParticipations < ActiveRecord::Migration
|
||||||
|
def self.up
|
||||||
|
create_table "participations", :force => true do |t|
|
||||||
|
t.string "guid"
|
||||||
|
t.integer "target_id"
|
||||||
|
t.string "target_type", :limit => 60, :null => false
|
||||||
|
t.integer "author_id"
|
||||||
|
t.text "author_signature"
|
||||||
|
t.text "parent_author_signature"
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.down
|
||||||
|
drop_table :participations
|
||||||
|
end
|
||||||
|
end
|
||||||
13
db/schema.rb
13
db/schema.rb
|
|
@ -11,7 +11,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended to check this file into your version control system.
|
# It's strongly recommended to check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(:version => 20120203220932) do
|
ActiveRecord::Schema.define(:version => 20120208231253) do
|
||||||
|
|
||||||
create_table "account_deletions", :force => true do |t|
|
create_table "account_deletions", :force => true do |t|
|
||||||
t.string "diaspora_handle"
|
t.string "diaspora_handle"
|
||||||
|
|
@ -244,6 +244,17 @@ ActiveRecord::Schema.define(:version => 20120203220932) do
|
||||||
add_index "oauth_clients", ["name"], :name => "index_oauth_clients_on_name", :unique => true
|
add_index "oauth_clients", ["name"], :name => "index_oauth_clients_on_name", :unique => true
|
||||||
add_index "oauth_clients", ["nonce"], :name => "index_oauth_clients_on_nonce", :unique => true
|
add_index "oauth_clients", ["nonce"], :name => "index_oauth_clients_on_nonce", :unique => true
|
||||||
|
|
||||||
|
create_table "participations", :force => true do |t|
|
||||||
|
t.string "guid"
|
||||||
|
t.integer "target_id"
|
||||||
|
t.string "target_type", :limit => 60, :null => false
|
||||||
|
t.integer "author_id"
|
||||||
|
t.text "author_signature"
|
||||||
|
t.text "parent_author_signature"
|
||||||
|
t.datetime "created_at"
|
||||||
|
t.datetime "updated_at"
|
||||||
|
end
|
||||||
|
|
||||||
create_table "people", :force => true do |t|
|
create_table "people", :force => true do |t|
|
||||||
t.string "guid", :null => false
|
t.string "guid", :null => false
|
||||||
t.text "url", :null => false
|
t.text "url", :null => false
|
||||||
|
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
require File.join(Rails.root, 'lib/diaspora/user/connecting')
|
|
||||||
require File.join(Rails.root, 'lib/diaspora/user/querying')
|
|
||||||
|
|
||||||
module Diaspora
|
|
||||||
module UserModules
|
|
||||||
include Connecting
|
|
||||||
include Querying
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,74 +0,0 @@
|
||||||
# Copyright (c) 2010-2011, Diaspora Inc. This file is
|
|
||||||
# licensed under the Affero General Public License version 3 or later. See
|
|
||||||
# the COPYRIGHT file.
|
|
||||||
|
|
||||||
module Diaspora
|
|
||||||
module UserModules
|
|
||||||
module Connecting
|
|
||||||
# This will create a contact on the side of the sharer and the sharee.
|
|
||||||
# @param [Person] person The person to start sharing with.
|
|
||||||
# @param [Aspect] aspect The aspect to add them to.
|
|
||||||
# @return [Contact] The newly made contact for the passed in person.
|
|
||||||
def share_with(person, aspect)
|
|
||||||
contact = self.contacts.find_or_initialize_by_person_id(person.id)
|
|
||||||
return false unless contact.valid?
|
|
||||||
|
|
||||||
unless contact.receiving?
|
|
||||||
contact.dispatch_request
|
|
||||||
contact.receiving = true
|
|
||||||
end
|
|
||||||
|
|
||||||
contact.aspects << aspect
|
|
||||||
contact.save
|
|
||||||
|
|
||||||
if notification = Notification.where(:target_id => person.id).first
|
|
||||||
notification.update_attributes(:unread=>false)
|
|
||||||
end
|
|
||||||
|
|
||||||
register_share_visibilities(contact)
|
|
||||||
contact
|
|
||||||
end
|
|
||||||
|
|
||||||
# This puts the last 100 public posts by the passed in contact into the user's stream.
|
|
||||||
# @param [Contact] contact
|
|
||||||
# @return [void]
|
|
||||||
def register_share_visibilities(contact)
|
|
||||||
#should have select here, but proven hard to test
|
|
||||||
posts = Post.where(:author_id => contact.person_id, :public => true).limit(100)
|
|
||||||
p = posts.map do |post|
|
|
||||||
ShareVisibility.new(:contact_id => contact.id, :shareable_id => post.id, :shareable_type => 'Post')
|
|
||||||
end
|
|
||||||
ShareVisibility.import(p) unless posts.empty?
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
|
|
||||||
def remove_contact(contact, opts={:force => false})
|
|
||||||
posts = contact.posts.all
|
|
||||||
|
|
||||||
if !contact.mutual? || opts[:force]
|
|
||||||
contact.destroy
|
|
||||||
else
|
|
||||||
contact.update_attributes(:receiving => false)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def disconnect(bad_contact, opts={})
|
|
||||||
person = bad_contact.person
|
|
||||||
Rails.logger.info("event=disconnect user=#{diaspora_handle} target=#{person.diaspora_handle}")
|
|
||||||
retraction = Retraction.for(self)
|
|
||||||
retraction.subscribers = [person]#HAX
|
|
||||||
Postzord::Dispatcher.build(self, retraction).post
|
|
||||||
|
|
||||||
AspectMembership.where(:contact_id => bad_contact.id).delete_all
|
|
||||||
remove_contact(bad_contact, opts)
|
|
||||||
end
|
|
||||||
|
|
||||||
def disconnected_by(person)
|
|
||||||
Rails.logger.info("event=disconnected_by user=#{diaspora_handle} target=#{person.diaspora_handle}")
|
|
||||||
if contact = self.contact_for(person)
|
|
||||||
remove_contact(contact)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,169 +0,0 @@
|
||||||
# Copyright (c) 2010-2011, Diaspora Inc. This file is
|
|
||||||
# licensed under the Affero General Public License version 3 or later. See
|
|
||||||
# the COPYRIGHT file.
|
|
||||||
|
|
||||||
require File.join(Rails.root, 'lib', 'evil_query')
|
|
||||||
|
|
||||||
|
|
||||||
#TODO: THIS FILE SHOULD NOT EXIST, EVIL SQL SHOULD BE ENCAPSULATED IN EvilQueries,
|
|
||||||
#throwing all of this stuff in user violates demeter like WHOA
|
|
||||||
|
|
||||||
module Diaspora
|
|
||||||
module UserModules
|
|
||||||
module Querying
|
|
||||||
def find_visible_shareable_by_id(klass, id, opts={} )
|
|
||||||
key = (opts.delete(:key) || :id)
|
|
||||||
::EvilQuery::VisibleShareableById.new(self, klass, key, id, opts).post!
|
|
||||||
end
|
|
||||||
|
|
||||||
def visible_shareables(klass, opts={})
|
|
||||||
opts = prep_opts(klass, opts)
|
|
||||||
shareable_ids = visible_shareable_ids(klass, opts)
|
|
||||||
klass.where(:id => shareable_ids).select('DISTINCT '+klass.to_s.tableize+'.*').limit(opts[:limit]).order(opts[:order_with_table])
|
|
||||||
end
|
|
||||||
|
|
||||||
def visible_shareable_ids(klass, opts={})
|
|
||||||
opts = prep_opts(klass, opts)
|
|
||||||
visible_ids_from_sql(klass, opts)
|
|
||||||
end
|
|
||||||
|
|
||||||
# @return [Array<Integer>]
|
|
||||||
def visible_ids_from_sql(klass, opts={})
|
|
||||||
opts = prep_opts(klass, opts)
|
|
||||||
opts[:klass] = klass
|
|
||||||
opts[:by_members_of] ||= self.aspect_ids
|
|
||||||
|
|
||||||
post_ids = klass.connection.select_values(visible_shareable_sql(klass, opts)).map { |id| id.to_i }
|
|
||||||
post_ids += klass.connection.select_values(construct_public_followings_sql(opts).to_sql).map {|id| id.to_i }
|
|
||||||
end
|
|
||||||
|
|
||||||
def visible_shareable_sql(klass, opts={})
|
|
||||||
table = klass.table_name
|
|
||||||
opts = prep_opts(klass, opts)
|
|
||||||
opts[:klass] = klass
|
|
||||||
|
|
||||||
shareable_from_others = construct_shareable_from_others_query(opts)
|
|
||||||
shareable_from_self = construct_shareable_from_self_query(opts)
|
|
||||||
|
|
||||||
"(#{shareable_from_others.to_sql} LIMIT #{opts[:limit]}) UNION ALL (#{shareable_from_self.to_sql} LIMIT #{opts[:limit]}) ORDER BY #{opts[:order]} LIMIT #{opts[:limit]}"
|
|
||||||
end
|
|
||||||
|
|
||||||
def ugly_select_clause(query, opts)
|
|
||||||
klass = opts[:klass]
|
|
||||||
select_clause ='DISTINCT %s.id, %s.updated_at AS updated_at, %s.created_at AS created_at' % [klass.table_name, klass.table_name, klass.table_name]
|
|
||||||
query.select(select_clause).order(opts[:order_with_table]).where(klass.arel_table[opts[:order_field]].lt(opts[:max_time]))
|
|
||||||
end
|
|
||||||
|
|
||||||
def construct_shareable_from_others_query(opts)
|
|
||||||
conditions = {
|
|
||||||
:pending => false,
|
|
||||||
:share_visibilities => {:hidden => opts[:hidden]},
|
|
||||||
:contacts => {:user_id => self.id, :receiving => true}
|
|
||||||
}
|
|
||||||
|
|
||||||
conditions[:type] = opts[:type] if opts.has_key?(:type)
|
|
||||||
|
|
||||||
query = opts[:klass].joins(:contacts).where(conditions)
|
|
||||||
|
|
||||||
if opts[:by_members_of]
|
|
||||||
query = query.joins(:contacts => :aspect_memberships).where(
|
|
||||||
:aspect_memberships => {:aspect_id => opts[:by_members_of]})
|
|
||||||
end
|
|
||||||
|
|
||||||
ugly_select_clause(query, opts)
|
|
||||||
end
|
|
||||||
|
|
||||||
def construct_public_followings_sql(opts)
|
|
||||||
aspects = Aspect.where(:id => opts[:by_members_of])
|
|
||||||
person_ids = Person.connection.select_values(people_in_aspects(aspects).select("people.id").to_sql)
|
|
||||||
|
|
||||||
query = opts[:klass].where(:author_id => person_ids, :public => true, :pending => false)
|
|
||||||
|
|
||||||
unless(opts[:klass] == Photo)
|
|
||||||
query = query.where(:type => opts[:type])
|
|
||||||
end
|
|
||||||
|
|
||||||
ugly_select_clause(query, opts)
|
|
||||||
end
|
|
||||||
|
|
||||||
def construct_shareable_from_self_query(opts)
|
|
||||||
conditions = {:pending => false }
|
|
||||||
conditions[:type] = opts[:type] if opts.has_key?(:type)
|
|
||||||
query = self.person.send(opts[:klass].to_s.tableize).where(conditions)
|
|
||||||
|
|
||||||
if opts[:by_members_of]
|
|
||||||
query = query.joins(:aspect_visibilities).where(:aspect_visibilities => {:aspect_id => opts[:by_members_of]})
|
|
||||||
end
|
|
||||||
|
|
||||||
ugly_select_clause(query, opts)
|
|
||||||
end
|
|
||||||
|
|
||||||
def contact_for(person)
|
|
||||||
return nil unless person
|
|
||||||
contact_for_person_id(person.id)
|
|
||||||
end
|
|
||||||
|
|
||||||
def aspects_with_shareable(base_class_name_or_class, shareable_id)
|
|
||||||
base_class_name = base_class_name_or_class
|
|
||||||
base_class_name = base_class_name_or_class.base_class.to_s if base_class_name_or_class.is_a?(Class)
|
|
||||||
self.aspects.joins(:aspect_visibilities).where(:aspect_visibilities => {:shareable_id => shareable_id, :shareable_type => base_class_name})
|
|
||||||
end
|
|
||||||
|
|
||||||
def contact_for_person_id(person_id)
|
|
||||||
Contact.where(:user_id => self.id, :person_id => person_id).includes(:person => :profile).first
|
|
||||||
end
|
|
||||||
|
|
||||||
# @param [Person] person
|
|
||||||
# @return [Boolean] whether person is a contact of this user
|
|
||||||
def has_contact_for?(person)
|
|
||||||
Contact.exists?(:user_id => self.id, :person_id => person.id)
|
|
||||||
end
|
|
||||||
|
|
||||||
def people_in_aspects(requested_aspects, opts={})
|
|
||||||
allowed_aspects = self.aspects & requested_aspects
|
|
||||||
aspect_ids = allowed_aspects.map(&:id)
|
|
||||||
|
|
||||||
people = Person.in_aspects(aspect_ids)
|
|
||||||
|
|
||||||
if opts[:type] == 'remote'
|
|
||||||
people = people.where(:owner_id => nil)
|
|
||||||
elsif opts[:type] == 'local'
|
|
||||||
people = people.where('people.owner_id IS NOT NULL')
|
|
||||||
end
|
|
||||||
people
|
|
||||||
end
|
|
||||||
|
|
||||||
def aspects_with_person person
|
|
||||||
contact_for(person).aspects
|
|
||||||
end
|
|
||||||
|
|
||||||
def posts_from(person)
|
|
||||||
::EvilQuery::ShareablesFromPerson.new(self, Post, person).make_relation!
|
|
||||||
end
|
|
||||||
|
|
||||||
def photos_from(person)
|
|
||||||
::EvilQuery::ShareablesFromPerson.new(self, Photo, person).make_relation!
|
|
||||||
end
|
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
# @return [Hash]
|
|
||||||
def prep_opts(klass, opts)
|
|
||||||
defaults = {
|
|
||||||
:order => 'created_at DESC',
|
|
||||||
:limit => 15,
|
|
||||||
:hidden => false
|
|
||||||
}
|
|
||||||
defaults[:type] = Stream::Base::TYPES_OF_POST_IN_STREAM if klass == Post
|
|
||||||
opts = defaults.merge(opts)
|
|
||||||
|
|
||||||
opts[:order_field] = opts[:order].split.first.to_sym
|
|
||||||
opts[:order_with_table] = klass.table_name + '.' + opts[:order]
|
|
||||||
|
|
||||||
opts[:max_time] = Time.at(opts[:max_time]) if opts[:max_time].is_a?(Integer)
|
|
||||||
opts[:max_time] ||= Time.now + 1
|
|
||||||
opts
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -17,9 +17,7 @@ module EvilQuery
|
||||||
end
|
end
|
||||||
|
|
||||||
def posts
|
def posts
|
||||||
liked_post_ids = fetch_ids!(LikedPosts.new(@user).posts, "posts.id")
|
Post.joins(:participations).where(:participations => {:author_id => @user.person.id}).order("posts.interacted_at DESC")
|
||||||
commented_post_ids = fetch_ids!(CommentedPosts.new(@user).posts, "posts.id")
|
|
||||||
Post.where(:id => liked_post_ids + commented_post_ids).order("posts.interacted_at DESC")
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
32
lib/federated/generator.rb
Normal file
32
lib/federated/generator.rb
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
module Federated
|
||||||
|
class Generator
|
||||||
|
def initialize(user, target)
|
||||||
|
@user = user
|
||||||
|
@target = target
|
||||||
|
end
|
||||||
|
|
||||||
|
def create!(options={})
|
||||||
|
relayable = build(options)
|
||||||
|
if relayable.save
|
||||||
|
Postzord::Dispatcher.defer_build_and_post(@user, relayable)
|
||||||
|
relayable
|
||||||
|
else
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def build(options={})
|
||||||
|
options.merge!(relayable_options)
|
||||||
|
relayable = self.class.federated_class.new(options.merge(:author_id => @user.person.id))
|
||||||
|
relayable.set_guid
|
||||||
|
relayable.initialize_signatures
|
||||||
|
relayable
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def relayable_options
|
||||||
|
{}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
44
lib/federated/relayable.rb
Normal file
44
lib/federated/relayable.rb
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
module Federated
|
||||||
|
class Relayable < ActiveRecord::Base
|
||||||
|
self.abstract_class = true
|
||||||
|
|
||||||
|
#crazy ordering issues - DEATH TO ROXML
|
||||||
|
include ROXML
|
||||||
|
|
||||||
|
include Diaspora::Webhooks
|
||||||
|
include Diaspora::Guid
|
||||||
|
|
||||||
|
#seriously, don't try to move this shit around until you have killed ROXML
|
||||||
|
xml_attr :target_type
|
||||||
|
include Diaspora::Relayable
|
||||||
|
|
||||||
|
xml_attr :diaspora_handle
|
||||||
|
|
||||||
|
belongs_to :target, :polymorphic => true
|
||||||
|
belongs_to :author, :class_name => 'Person'
|
||||||
|
#end crazy ordering issues
|
||||||
|
|
||||||
|
validates_uniqueness_of :target_id, :scope => [:target_type, :author_id]
|
||||||
|
validates :parent, :presence => true #should be in relayable (pending on fixing Message)
|
||||||
|
|
||||||
|
def diaspora_handle
|
||||||
|
self.author.diaspora_handle
|
||||||
|
end
|
||||||
|
|
||||||
|
def diaspora_handle=(nh)
|
||||||
|
self.author = Webfinger.new(nh).fetch
|
||||||
|
end
|
||||||
|
|
||||||
|
def parent_class
|
||||||
|
self.target_type.constantize
|
||||||
|
end
|
||||||
|
|
||||||
|
def parent
|
||||||
|
self.target
|
||||||
|
end
|
||||||
|
|
||||||
|
def parent= parent
|
||||||
|
self.target = parent
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -6,7 +6,7 @@ describe EvilQuery::Participation do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "includes posts liked by the user" do
|
it "includes posts liked by the user" do
|
||||||
Factory(:like, :target => @status_message, :author => alice.person)
|
alice.like!(@status_message)
|
||||||
EvilQuery::Participation.new(alice).posts.should include(@status_message)
|
EvilQuery::Participation.new(alice).posts.should include(@status_message)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -35,7 +35,7 @@ describe EvilQuery::Participation do
|
||||||
alice.comment!(@status_messageB, "party")
|
alice.comment!(@status_messageB, "party")
|
||||||
Timecop.travel time += 1.month
|
Timecop.travel time += 1.month
|
||||||
|
|
||||||
Factory(:like, :target => @status_messageA, :author => alice.person)
|
alice.like!(@status_messageA)
|
||||||
Timecop.travel time += 1.month
|
Timecop.travel time += 1.month
|
||||||
|
|
||||||
alice.comment!(@photoC, "party")
|
alice.comment!(@photoC, "party")
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,7 @@ require File.join(Rails.root, "spec", "shared_behaviors", "relayable")
|
||||||
|
|
||||||
describe Like do
|
describe Like do
|
||||||
before do
|
before do
|
||||||
bobs_aspect = bob.aspects.first
|
@status = bob.post(:status_message, :text => "hello", :to => bob.aspects.first.id)
|
||||||
@status = bob.post(:status_message, :text => "hello", :to => bobs_aspect.id)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'has a valid factory' do
|
it 'has a valid factory' do
|
||||||
|
|
@ -91,8 +90,7 @@ describe Like do
|
||||||
@object_on_remote_parent = @local_luke.like!(@remote_parent)
|
@object_on_remote_parent = @local_luke.like!(@remote_parent)
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:build_object) { alice.build_like(:target => @status, :positive => true) }
|
let(:build_object) { Like::Generator.new(alice, @status).build }
|
||||||
it_should_behave_like 'it is relayable'
|
it_should_behave_like 'it is relayable'
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
23
spec/models/participation_spec.rb
Normal file
23
spec/models/participation_spec.rb
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
require "spec_helper"
|
||||||
|
|
||||||
|
describe Participation do
|
||||||
|
describe 'it is relayable' do
|
||||||
|
before do
|
||||||
|
@status = bob.post(:status_message, :text => "hello", :to => bob.aspects.first.id)
|
||||||
|
|
||||||
|
@local_luke, @local_leia, @remote_raphael = set_up_friends
|
||||||
|
@remote_parent = Factory(:status_message, :author => @remote_raphael)
|
||||||
|
@local_parent = @local_luke.post :status_message, :text => "foobar", :to => @local_luke.aspects.first
|
||||||
|
|
||||||
|
@object_by_parent_author = @local_luke.participate!(@local_parent)
|
||||||
|
@object_by_recipient = @local_leia.participate!(@local_parent)
|
||||||
|
@dup_object_by_parent_author = @object_by_parent_author.dup
|
||||||
|
|
||||||
|
@object_on_remote_parent = @local_luke.participate!(@remote_parent)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:build_object) { Participation::Generator.new(alice, @status).build }
|
||||||
|
|
||||||
|
it_should_behave_like 'it is relayable'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
describe Diaspora::UserModules::Connecting do
|
describe User::Connecting do
|
||||||
|
|
||||||
let(:aspect) { alice.aspects.first }
|
let(:aspect) { alice.aspects.first }
|
||||||
let(:aspect1) { alice.aspects.create(:name => 'other') }
|
let(:aspect1) { alice.aspects.create(:name => 'other') }
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,7 @@
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
describe User do
|
describe User::Querying do
|
||||||
|
|
||||||
before do
|
before do
|
||||||
@alices_aspect = alice.aspects.where(:name => "generic").first
|
@alices_aspect = alice.aspects.where(:name => "generic").first
|
||||||
@eves_aspect = eve.aspects.where(:name => "generic").first
|
@eves_aspect = eve.aspects.where(:name => "generic").first
|
||||||
|
|
|
||||||
85
spec/models/user/social_actions_spec.rb
Normal file
85
spec/models/user/social_actions_spec.rb
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
require "spec_helper"
|
||||||
|
|
||||||
|
describe User::SocialActions do
|
||||||
|
before do
|
||||||
|
@bobs_aspect = bob.aspects.where(:name => "generic").first
|
||||||
|
@status = bob.post(:status_message, :text => "hello", :to => @bobs_aspect.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'User#comment!' do
|
||||||
|
it "sets the comment text" do
|
||||||
|
alice.comment!(@status, "unicorn_mountain").text.should == "unicorn_mountain"
|
||||||
|
end
|
||||||
|
|
||||||
|
it "creates a partcipation" do
|
||||||
|
lambda{ alice.comment!(@status, "bro") }.should change(Participation, :count).by(1)
|
||||||
|
alice.participations.last.target.should == @status
|
||||||
|
end
|
||||||
|
|
||||||
|
it "creates the like" do
|
||||||
|
lambda{ alice.comment!(@status, "bro") }.should change(Comment, :count).by(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "federates" do
|
||||||
|
Participation::Generator.any_instance.stub(:create!)
|
||||||
|
Postzord::Dispatcher.should_receive(:defer_build_and_post)
|
||||||
|
alice.comment!(@status, "omg")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'User#like!' do
|
||||||
|
it "creates a partcipation" do
|
||||||
|
lambda{ alice.like!(@status) }.should change(Participation, :count).by(1)
|
||||||
|
alice.participations.last.target.should == @status
|
||||||
|
end
|
||||||
|
|
||||||
|
it "creates the like" do
|
||||||
|
lambda{ alice.like!(@status) }.should change(Like, :count).by(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "federates" do
|
||||||
|
#participation and like
|
||||||
|
Participation::Generator.any_instance.stub(:create!)
|
||||||
|
Postzord::Dispatcher.should_receive(:defer_build_and_post)
|
||||||
|
alice.like!(@status)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'User#like!' do
|
||||||
|
before do
|
||||||
|
@bobs_aspect = bob.aspects.where(:name => "generic").first
|
||||||
|
@status = bob.post(:status_message, :text => "hello", :to => @bobs_aspect.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "creates a partcipation" do
|
||||||
|
lambda{ alice.like!(@status) }.should change(Participation, :count).by(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "creates the like" do
|
||||||
|
lambda{ alice.like!(@status) }.should change(Like, :count).by(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "federates" do
|
||||||
|
#participation and like
|
||||||
|
Postzord::Dispatcher.should_receive(:defer_build_and_post).twice
|
||||||
|
alice.like!(@status)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should be able to like on one's own status" do
|
||||||
|
like = alice.like!(@status)
|
||||||
|
@status.reload.likes.first.should == like
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should be able to like on a contact's status" do
|
||||||
|
like = bob.like!(@status)
|
||||||
|
@status.reload.likes.first.should == like
|
||||||
|
end
|
||||||
|
|
||||||
|
it "does not allow multiple likes" do
|
||||||
|
alice.like!(@status)
|
||||||
|
lambda {
|
||||||
|
alice.like!(@status)
|
||||||
|
}.should_not change(@status, :likes)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
describe User do
|
describe User do
|
||||||
|
|
||||||
describe "private key" do
|
describe "private key" do
|
||||||
it 'has a key' do
|
it 'has a key' do
|
||||||
alice.encryption_key.should_not be nil
|
alice.encryption_key.should_not be nil
|
||||||
|
|
@ -688,30 +687,6 @@ describe User do
|
||||||
@like2 = bob.like!(@message)
|
@like2 = bob.like!(@message)
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'User#like' do
|
|
||||||
before do
|
|
||||||
@status = bob.post(:status_message, :text => "hello", :to => @bobs_aspect.id)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should be able to like on one's own status" do
|
|
||||||
like = alice.like!(@status)
|
|
||||||
@status.reload.likes.first.should == like
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should be able to like on a contact's status" do
|
|
||||||
like = bob.like!(@status)
|
|
||||||
@status.reload.likes.first.should == like
|
|
||||||
end
|
|
||||||
|
|
||||||
it "does not allow multiple likes" do
|
|
||||||
alice.like!(@status)
|
|
||||||
|
|
||||||
lambda {
|
|
||||||
alice.like!(@status)
|
|
||||||
}.should_not change(@status, :likes)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#like_for' do
|
describe '#like_for' do
|
||||||
it 'returns the correct like' do
|
it 'returns the correct like' do
|
||||||
alice.like_for(@message).should == @like
|
alice.like_for(@message).should == @like
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue