From 6dd58fe87512d4ff2eb1f881b8b69edd73b0cb80 Mon Sep 17 00:00:00 2001 From: Raphael Date: Tue, 9 Nov 2010 13:56:10 -0800 Subject: [PATCH] Specs pass again, invitations moved to Invitation model, refactor possibly complete --- app/controllers/invitations_controller.rb | 6 +- app/controllers/users_controller.rb | 5 - app/models/invitation.rb | 47 +++++++ app/models/user.rb | 77 ++--------- app/views/devise/mailer/invitation.html.haml | 15 ++- lib/diaspora/user/friending.rb | 5 +- spec/models/invitation_spec.rb | 113 +++++++++++++++- spec/models/user/invite_spec.rb | 134 ++++++++----------- 8 files changed, 239 insertions(+), 163 deletions(-) diff --git a/app/controllers/invitations_controller.rb b/app/controllers/invitations_controller.rb index 8c20ac2ba..7ee7e8f92 100644 --- a/app/controllers/invitations_controller.rb +++ b/app/controllers/invitations_controller.rb @@ -30,8 +30,8 @@ class InvitationsController < Devise::InvitationsController def update begin - user = User.find_by_invitation_token(params["user"]["invitation_token"]) - user.accept_invitation!(params["user"]) + user = User.find_by_invitation_token(params[:user][:invitation_token]) + user.accept_invitation!(params[:user]) rescue MongoMapper::DocumentNotValid => e user = nil flash[:error] = e.message @@ -47,7 +47,7 @@ class InvitationsController < Devise::InvitationsController protected def check_token - if User.find_by_invitation_token(params['invitation_token']).nil? + if User.find_by_invitation_token(params[:invitation_token]).nil? flash[:error] = I18n.t 'invitations.check_token.not_found' redirect_to root_url end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 76f192ffd..8262aac97 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -94,9 +94,4 @@ class UsersController < ApplicationController tar_path = PhotoMover::move_photos(current_user) send_data( File.open(tar_path).read, :filename => "#{current_user.id}.tar" ) end - - def invite - User.invite!(:email => params[:email]) - end - end diff --git a/app/models/invitation.rb b/app/models/invitation.rb index c1855624c..2cd29fa22 100644 --- a/app/models/invitation.rb +++ b/app/models/invitation.rb @@ -8,7 +8,54 @@ class Invitation belongs_to :from, :class => User belongs_to :to, :class => User belongs_to :into, :class => Aspect + key :message, String validates_presence_of :from, :to, :into + def self.invite(opts = {}) + existing_user = User.find_by_email(opts[:email]) + if existing_user + if opts[:from].contact_for(opts[:from].person) + raise "You are already friends with this person" + elsif not existing_user.invited? + opts[:from].send_friend_request_to(existing_user.person, opts[:into]) + return + elsif Invitation.first(:from_id => opts[:from].id, :to_id => existing_user.id) + raise "You already invited this person" + end + end + + invited_user = create_invitee(:email => opts[:email]) + if invited_user.persisted? + Invitation.create!(:from => opts[:from], + :to => invited_user, + :into => opts[:into], + :message => opts[:message]) + + opts[:from].invites -= 1 + opts[:from].save! + invited_user + else + false + end + end + + def self.create_invitee(opts = {}) + invitable = User.find_or_initialize_with_error_by(:email, opts[:email]) + + if invitable.new_record? + invitable.errors.clear if invitable.email.try(:match, Devise.email_regexp) + else + invitable.errors.add(:email, :taken) unless invitable.invited? + end + + invitable.invite! if invitable.errors.empty? + invitable + end + + def to_request! + request = from.send_friend_request_to(to.person, into) + destroy if request + request + end end diff --git a/app/models/user.rb b/app/models/user.rb index fb090238e..642d1366c 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -22,13 +22,10 @@ class User key :invites, Integer, :default => 5 key :invitation_token, String key :invitation_sent_at, DateTime - key :inviter_ids, Array, :typecast => 'ObjectId' key :pending_request_ids, Array, :typecast => 'ObjectId' key :visible_post_ids, Array, :typecast => 'ObjectId' key :visible_person_ids, Array, :typecast => 'ObjectId' - key :invite_messages, Hash - key :getting_started, Boolean, :default => true key :language, String @@ -47,7 +44,8 @@ class User one :person, :class_name => 'Person', :foreign_key => :owner_id - many :inviters, :in => :inviter_ids, :class_name => 'User' + many :invitations_from_me, :class => Invitation, :foreign_key => :from_id + many :invitations_to_me, :class => Invitation, :foreign_key => :to_id many :friends, :class_name => 'Contact', :foreign_key => :user_id many :visible_people, :in => :visible_person_ids, :class_name => 'Person' # One of these needs to go many :pending_requests, :in => :pending_request_ids, :class_name => 'Request' @@ -87,7 +85,7 @@ class User key :email, String def method_missing(method, *args) - self.person.send(method, *args) + self.person.send(method, *args) if self.person end ######### Aspects ###################### @@ -322,7 +320,6 @@ class User ###Invitations############ def invite_user(opts = {}) if self.invites > 0 - aspect_id = opts.delete(:aspect_id) if aspect_id == nil raise "Must invite into aspect" @@ -331,83 +328,29 @@ class User if !(aspect_object) raise "Must invite to your aspect" else - u = User.find_by_email(opts[:email]) - if u.nil? - elsif contact_for(u.person) - raise "You are already friends with this person" - elsif not u.invited? - self.send_friend_request_to(u.person, aspect_object) - return - elsif u.invited? && u.inviters.include?(self) - raise "You already invited this person" - end + Invitation.invite(:email => opts[:email], + :from => self, + :into => aspect_object, + :message => opts[:invite_message]) + end - request = Request.instantiate( - :to => "http://local_request.example.com", - :from => self.person, - :into => aspect_id - ) - - invited_user = User.invite!(:email => opts[:email], :request => request, :inviter => self, :invite_message => opts[:invite_message]) - - self.invites = self.invites - 1 - self.pending_requests << request - request.save - self.save! - invited_user else raise "You have no invites" end end - def self.invite!(attributes={}) - inviter = attributes.delete(:inviter) - request = attributes.delete(:request) - - invitable = find_or_initialize_with_error_by(:email, attributes.delete(:email)) - invitable.attributes = attributes - if invitable.inviters.include?(inviter) - raise "You already invited this person" - else - invitable.pending_requests << Request.create( - :from => inviter.person) - - invitable.inviters << inviter - message = attributes.delete(:invite_message) - if message - invitable.invite_messages[inviter.id.to_s] = message - end - end - - if invitable.new_record? - invitable.errors.clear if invitable.email.try(:match, Devise.email_regexp) - else - invitable.errors.add(:email, :taken) unless invitable.invited? - end - - invitable.invite! if invitable.errors.empty? - invitable - end - def accept_invitation!(opts = {}) if self.invited? - self.setup(opts) self.invitation_token = nil self.password = opts[:password] self.password_confirmation = opts[:password_confirmation] - self.person.save! - self.invitation_token = nil - friend_inviters self.save! - self - end - end + invitations_to_me.each{|invitation| invitation.to_request!} - def friend_inviters - inviters.each do |inviter| + self end end diff --git a/app/views/devise/mailer/invitation.html.haml b/app/views/devise/mailer/invitation.html.haml index 17f0ed9df..4dd82e773 100644 --- a/app/views/devise/mailer/invitation.html.haml +++ b/app/views/devise/mailer/invitation.html.haml @@ -45,14 +45,19 @@ %header = image_tag '/images/diaspora_white.png' #container + - @invs = @resource.invitations_to_me %p Hello #{@resource.email}! %p - #{(@resource.inviters.count == 1)? ( @resource.inviters.first.real_name + " (#{@resource.inviters.first.diaspora_handle})" + " has") : (@resource.inviters.map{|inv| inv.real_name + " (#{inv.diaspora_handle})"}.join(",") + " have")} invited you to join Diaspora at #{root_url}, you can accept it through the link below. - - @resource.inviters.each do |inv| - - if @resource.invite_messages[inv.id.to_s] - = "#{inv.real_name}:" - = "\"#{@resource.invite_messages[inv.id.to_s]}\"" + - if @invs.count == 1 + = @invs.first.real_name + " (#{@invs.first.diaspora_handle})" + " has" + - else + = (@invs.map{|inv| inv.from.real_name + " (#{inv.from.diaspora_handle})"}.join(",") + " have") + = "invited you to join Diaspora at #{root_url}, you can accept it through the link below." + - @invs.each do |inv| + - if inv.message + = "#{inv.from.real_name}:" + = "\"#{inv.message}\"" %p %p= link_to 'Accept invitation', accept_invitation_url(@resource, :invitation_token => @resource.invitation_token), :class => "large_text" %p.small diff --git a/lib/diaspora/user/friending.rb b/lib/diaspora/user/friending.rb index ca985b267..f131edfc1 100644 --- a/lib/diaspora/user/friending.rb +++ b/lib/diaspora/user/friending.rb @@ -67,9 +67,12 @@ module Diaspora #pp friend_request.person activate_friend(friend_request.from, destination_aspect) Rails.logger.info("#{self.real_name}'s friend request has been accepted") - + friend_request.destroy + + pending_requests.delete(original_request) original_request.destroy + self.save Request.send_request_accepted(self, friend_request.from, destination_aspect) #this is a new friend request diff --git a/spec/models/invitation_spec.rb b/spec/models/invitation_spec.rb index 8570e3dec..af49df951 100644 --- a/spec/models/invitation_spec.rb +++ b/spec/models/invitation_spec.rb @@ -6,8 +6,11 @@ require 'spec_helper' describe Invitation do let(:user) {make_user} - let(:aspect) {user.aspects.create(:name => "Invitees")} + let!(:aspect) {user.aspects.create(:name => "Invitees")} let(:user2) {make_user} + before do + @email = 'maggie@example.com' + end describe 'validations' do before do aspect @@ -32,5 +35,113 @@ describe Invitation do @invitation.should_not be_valid end end + + it 'has a message' do + @invitation = Invitation.new(:from => user, :to => user2, :into => aspect) + @invitation.message = "!" + @invitation.message.should == "!" + end + + describe '.invite' do + it 'creates an invitation' do + lambda { + Invitation.invite(:email => @email, :from => user, :into => aspect) + }.should change(Invitation, :count).by(1) + end + it 'associates the invitation with the inviter' do + lambda { + Invitation.invite(:email => @email, :from => user, :into => aspect) + }.should change{user.reload.invitations_from_me.count}.by(1) + end + it 'associates the invitation with the invitee' do + new_user = Invitation.invite(:email => @email, :from => user, :into => aspect) + new_user.invitations_to_me.count.should == 1 + end + it 'creates a user' do + lambda { + Invitation.invite(:from => user, :email => @email, :into => aspect) + }.should change(User, :count).by(1) + end + it 'returns the new user' do + new_user = Invitation.invite(:from => user, :email => @email, :into => aspect) + new_user.is_a?(User).should be_true + new_user.email.should == @email + end + it 'adds the inviter to the invited_user' do + new_user = Invitation.invite(:from => user, :email => @email, :into => aspect) + new_user.invitations_to_me.first.from.should == user + end + + it 'adds an optional message' do + message = "How've you been?" + new_user = Invitation.invite(:from => user, :email => @email, :into => aspect, :message => message) + new_user.invitations_to_me.first.message.should == message + end + + it 'sends a friend request to a user with that email into the aspect' do + user2 + user.should_receive(:send_friend_request_to){ |a, b| + a.should == user2.person + b.should == aspect + } + Invitation.invite(:from => user, :email => user2.email, :into => aspect) + end + end + + describe '.create_invitee' do + it 'creates a user' do + lambda { + Invitation.create_invitee(:email => @email) + }.should change(User, :count).by(1) + end + it 'sends email to the invited user' do + lambda { + Invitation.create_invitee(:email => @email) + }.should change{Devise.mailer.deliveries.size}.by(1) + end + end + + describe '#to_request!' do + before do + @new_user = Invitation.invite(:from => user, :email => @email, :into => aspect) + acceptance_params = {:invitation_token => "abc", + :username => "user", + :password => "secret", + :password_confirmation => "secret", + :person => {:profile => {:first_name => "Bob", + :last_name => "Smith"}}} + @new_user.setup(acceptance_params) + @new_user.person.save + @new_user.save + @invitation = @new_user.invitations_to_me.first + end + it 'destroys the invitation' do + lambda { + @invitation.to_request! + }.should change(Invitation, :count).by(-1) + end + it 'creates a request, and sends it to the new user' do + lambda { + @invitation.to_request! + }.should change(Request, :count).by(2) + end + describe 'return values' do + before do + @request = @invitation.to_request! + end + it 'returns the sent request' do + @request.is_a?(Request).should be_true + end + it 'sets the receiving user' do + @request.to.should == @new_user.person + end + it 'sets the sending user' do + @request.from.should == user.person + end + it 'sets the aspect' do + @request.into.should == aspect + end + end + end end diff --git a/spec/models/user/invite_spec.rb b/spec/models/user/invite_spec.rb index 1a926201e..256fe0b48 100644 --- a/spec/models/user/invite_spec.rb +++ b/spec/models/user/invite_spec.rb @@ -9,58 +9,35 @@ describe User do let(:aspect) {inviter.aspects.create(:name => "awesome")} let(:another_user) {make_user} let(:wrong_aspect) {another_user.aspects.create(:name => "super")} - let(:inviter_with_3_invites) {Factory.create :user, :invites => 3} + let(:inviter_with_3_invites) { new_user = make_user; new_user.invites = 3; new_user.save; new_user;} let(:aspect2) {inviter_with_3_invites.aspects.create(:name => "Jersey Girls")} context "creating invites" do it 'requires an apect' do - proc{inviter.invite_user(:email => "maggie@example.com")}.should raise_error /Must invite into aspect/ + proc{ + inviter.invite_user(:email => "maggie@example.com") + }.should raise_error /Must invite into aspect/ end it 'requires your aspect' do - proc{inviter.invite_user(:email => "maggie@example.com", :aspect_id => wrong_aspect.id)}.should raise_error /Must invite to your aspect/ + proc{ + inviter.invite_user(:email => "maggie@example.com", :aspect_id => wrong_aspect.id) + }.should raise_error /Must invite to your aspect/ end - it 'creates a user' do - inviter - lambda { - inviter.invite_user(:email => "joe@example.com", :aspect_id => aspect.id ) - }.should change(User, :count).by(1) + it 'calls Invitation.invite' do + Invitation.should_receive(:invite) + inviter.invite_user(:email => @email, :aspect_id => aspect.id) + end + + it 'has an invitation' do + inviter.invite_user(:email => "joe@example.com", :aspect_id => aspect.id).invitations_to_me.count.should == 1 end it 'creates it with an email' do inviter.invite_user(:email => "joe@example.com", :aspect_id => aspect.id).email.should == "joe@example.com" end - it 'sends email to the invited user' do - ::Devise.mailer.should_receive(:invitation).once - inviter.invite_user(:email => "ian@example.com", :aspect_id => aspect.id) - end - - it 'adds the inviter to the invited_user' do - invited_user = inviter.invite_user(:email => "marcy@example.com", :aspect_id => aspect.id) - invited_user.reload - invited_user.inviters.include?(inviter).should be_true - end - - it 'adds an optional message' do - invited_user = inviter.invite_user(:email => "marcy@example.com", :invite_message => "How've you been?",:aspect_id => aspect.id) - invited_user.reload - invited_user.invite_messages[inviter.id.to_s].should == "How've you been?" - end - - - it 'adds a pending request to the invited user' do - invited_user = inviter.invite_user(:email => "marcy@example.com", :aspect_id => aspect.id) - invited_user.reload - invited_user.pending_requests.find_by_callback_url(inviter.receive_url).should_not be_nil - end - - it 'adds a pending request to the inviter' do - inviter.invite_user(:email => "marcy@example.com", :aspect_id => aspect.id) - inviter.reload - inviter.pending_requests.find_by_callback_url(inviter.receive_url).nil?.should == false - end it 'throws if you try to add someone you"re friends with' do friend_users(inviter, aspect, another_user, wrong_aspect) @@ -68,13 +45,6 @@ describe User do proc{inviter.invite_user(:email => another_user.email, :aspect_id => aspect.id)}.should raise_error /already friends/ end - it 'sends a friend request to a user with that email into the aspect' do - inviter.should_receive(:send_friend_request_to){ |a, b| - a.should == another_user.person - b.should == aspect - } - inviter.invite_user(:email => another_user.email, :aspect_id => aspect.id) - end end context "limit on invites" do @@ -93,53 +63,55 @@ describe User do end - context "the acceptance of an invitation" do - let!(:invited_user1) { create_user_with_invitation("abc", :email => "email@example.com", :inviter => inviter)} - let!(:invited_user2) { inviter.invite_user(:email => "jane@example.com", :aspect_id => aspect.id) } - - it "should create the person with the passed in params" do - person_count = Person.count - u = invited_user1.accept_invitation!(:invitation_token => "abc", + describe "#accept_invitation!" do + let(:invited_user) {@invited_user_pre.accept_invitation!(:invitation_token => "abc", :username => "user", :password => "secret", :password_confirmation => "secret", :person => {:profile => {:first_name => "Bob", - :last_name => "Smith"}} ) - Person.count.should be person_count + 1 - u.person.profile.first_name.should == "Bob" + :last_name => "Smith"}} )} + + before do + @invited_user_pre = Invitation.invite(:from => inviter, :email => 'invitee@example.org', :into => aspect).reload + @person_count = Person.count end - it 'should auto accept the request for the sender into the right aspect' do - u = invited_user2.accept_invitation!(:invitation_token => invited_user2.invitation_token, - :username => "user", - :password => "secret", - :password_confirmation => "secret", - :person => {:profile => {:first_name => "Bob", - :last_name => "Smith"}} ) + context 'after invitation acceptance' do + before do + invited_user.reload + end + it 'destroys the invitations' do + invited_user.invitations_to_me.count.should == 0 + end + it "should create the person with the passed in params" do + Person.count.should == @person_count + 1 + invited_user.person.profile.first_name.should == "Bob" + end - u.pending_requests.count.should == 1 + it 'resolves incoming invitations into friend requests' do + invited_user.reload.pending_requests.count.should == 1 + inviter.reload.pending_requests.count.should == 1 + end - received_request = u.pending_requests.first + context 'after request acceptance' do + before do + invited_user.accept_and_respond(invited_user.pending_requests.first.id, + invited_user.aspects.create( + :name => 'first aspect!').id) + invited_user.reload + inviter.reload + end + it 'successfully makes invited_user friends with inviter' do + invited_user.contact_for(inviter.person).should_not be_nil + invited_user.pending_requests.count.should == 0 + end - aspect2 = u.aspects.create(:name => "dudes") - - reversed_request = u.accept_friend_request(received_request.id, aspect2.id) - u.reload - - inviter.receive_salmon(u.salmon(reversed_request).xml_for(inviter.person)) - inviter.reload.contact_for(u.person).should_not be_nil + it 'successfully makes inviter friends with invited_user' do + inviter.contact_for(invited_user.person).should_not be_nil + inviter.pending_requests.size.should == 0 + end + end end end end -def create_user_with_invitation(invitation_token, attributes={}) - inviter = attributes.delete(:inviter) - user = User.new({:password => nil, :password_confirmation => nil}.update(attributes)) - #user.skip_confirmation! - user.email = attributes[:email] - user.invitation_token = invitation_token - user.invitation_sent_at = Time.now.utc - user.inviters << inviter - user.save(:validate => false) - user -end