Merge branch 'master' of bitbucket.org:jamkazam/jam-ruby

This commit is contained in:
Seth Call 2013-04-15 21:12:18 -05:00
commit 2e157bf157
14 changed files with 314 additions and 70 deletions

View File

@ -1,6 +1,8 @@
#ruby=1.9.3
source 'https://rubygems.org'
source 'https://jamjam:blueberryjam@www.jamkazam.com/gems/'
unless ENV["LOCAL_DEV"] == "1"
source 'https://jamjam:blueberryjam@www.jamkazam.com/gems/'
end
# Look for $WORKSPACE, otherwise use "workspace" as dev path.
workspace = ENV["WORKSPACE"] || "~/workspace"

View File

@ -10,8 +10,9 @@ require "will_paginate/active_record"
require "action_mailer"
require "devise"
require "sendgrid"
require "jam_ruby/constants/validation_messages"
require "jam_ruby/constants/limits"
require "jam_ruby/constants/notification_types"
require "jam_ruby/constants/validation_messages"
require "jam_ruby/errors/permission_error"
require "jam_ruby/errors/state_error"
require "jam_ruby/errors/jam_argument_error"
@ -59,6 +60,7 @@ require "jam_ruby/models/user_favorite"
require "jam_ruby/models/search"
require "jam_ruby/models/recording"
require "jam_ruby/models/recorded_track"
require "jam_ruby/models/mix"
include Jampb

View File

@ -0,0 +1,8 @@
module NotificationTypes
FRIEND_UPDATE = "FRIEND_UPDATE"
FRIEND_REQUEST = "FRIEND_REQUEST"
FRIEND_REQUEST_ACCEPTED = "FRIEND_REQUEST_ACCEPTED"
SESSION_INVITATION = "SESSION_INVITATION"
end

View File

@ -111,23 +111,30 @@
return Jampb::ClientMessage.new(:type => ClientMessage::Type::SESSION_INVITATION, :route_to => USER_TARGET_PREFIX + receiver_id, :session_invitation => session_invitation)
end
# create a friend update message
def friend_update(user_id, name, photo_url, online, msg)
friend = Jampb::FriendUpdate.new(:user_id => user_id, :name => name, :photo_url => photo_url, :online => online, :msg => msg)
return Jampb::ClientMessage.new(:type => ClientMessage::Type::FRIEND_UPDATE, :route_to => USER_TARGET_PREFIX + user_id, :friend_update => friend)
end
# create a friend request message
def friend_request(user_id, name, photo_url, friend_id)
friend_request = Jampb::FriendRequest.new(:user_id => user_id, :name => name, :photo_url => photo_url, :friend_id => friend_id)
def friend_request(friend_request_id, user_id, name, photo_url, friend_id, msg, notification_id, created_at)
friend_request = Jampb::FriendRequest.new(:friend_request_id => friend_request_id,
:user_id => user_id, :name => name, :photo_url => photo_url, :friend_id => friend_id, :msg => msg,
:notification_id => notification_id, :created_at => created_at)
return Jampb::ClientMessage.new(:type => ClientMessage::Type::FRIEND_REQUEST, :route_to => USER_TARGET_PREFIX + friend_id, :friend_request => friend_request)
end
# create a friend request acceptance message
def friend_request_accepted(friend_id, name, photo_url, user_id)
friend_request_accepted = Jampb::FriendRequestAccepted.new(:friend_id => friend_id, :name => name, :photo_url => photo_url, :user_id => user_id)
def friend_request_accepted(friend_id, name, photo_url, user_id, msg, notification_id, created_at)
friend_request_accepted = Jampb::FriendRequestAccepted.new(:friend_id => friend_id,
:name => name, :photo_url => photo_url, :user_id => user_id, :msg => msg,
:notification_id => notification_id, :created_at => created_at)
return Jampb::ClientMessage.new(:type => ClientMessage::Type::FRIEND_REQUEST_ACCEPTED, :route_to => USER_TARGET_PREFIX + user_id, :friend_request_accepted => friend_request_accepted)
end
# create a friend update message
def friend_update(user_id, online)
friend = Jampb::FriendUpdate.new(:user_id => user_id, :online => online)
return Jampb::ClientMessage.new(:type => ClientMessage::Type::FRIEND_UPDATE, :route_to => USER_TARGET_PREFIX + user_id, :friend_update => friend)
end
############## P2P CLIENT MESSAGES #################
# send a request to do a ping

View File

@ -7,12 +7,25 @@ module JamRuby
self.primary_key = 'id'
attr_accessible :version, :uri, :sha1, :environment, :product
# ORDER MATTERS HERE- before_save for this method must be declared before mount_uploader: https://github.com/jnicklas/carrierwave/wiki/Known-Issues
before_save :update_uri_attributes
mount_uploader :uri, ArtifactUploader
validate :version, :presence => true
validate :uri, :presence => true
validate :sha1, :presence => false
validate :environment, presence => true
validate :sha1, :presence => true
validate :size, :presence => true
validate :environment, :presence => true
validate :product, :inclusion => {:in => PRODUCTS}
private
def update_uri_attributes
if uri.present? && uri_changed?
self.size = uri.file.size
self.sha1 = Digest::MD5.hexdigest(File.read(uri.current_path))
end
end
end
end

View File

@ -27,7 +27,7 @@ module JamRuby
friend_request.save
# send notification
# Notification.send_friend_request(user_id, friend_id)
Notification.send_friend_request(friend_request.id, user_id, friend_id)
else
ActiveRecord::Base.transaction do
@ -41,7 +41,7 @@ module JamRuby
Friendship.save(friend_request.user_id, friend_request.friend_id)
# send notification
# Notification.send_friend_request_accepted(user_id, friend_id)
Notification.send_friend_request_accepted(friend_request.user_id, friend_request.friend_id)
end
end
end

View File

@ -0,0 +1,57 @@
# FIXME:
# Need to pass in the JSON spec for the mix and put that in the migration.
module JamRuby
class Mix < ActiveRecord::Base
MAX_MIX_TIME = 7200 # 2 hours
self.primary_key = 'id'
belongs_to :recording, :class_name => "JamRuby::Recording", :inverse_of => :mix
def self.schedule(recording_id, user_id, description, spec)
# This would have made it so you couldn't have more than one mix of a recording+owner
#raise unless self.where(:recording_id => recording_id, :owner_id => user_id).size == 0
recording = Recording.find(recording_id)
raise if recording.nil?
raise if recording.owner_id != user_id
mix = Mix.new
mix.recording_id = recording_id
mix.owner_id = user_id
mix.description = description
mix.spec = spec
mix.save
mix
end
def self.next(mix_server)
# First check if there are any mixes started so long ago that we want to re-run them
Mix.where("completed_at IS NULL AND started_at < ?", Time.now - MAX_MIX_TIME).each do |mix|
# FIXME: This should probably throw some kind of log, since it means something went wrong
mix.started_at = nil
mix.mix_server = nil
mix.save
end
mix = Mix.where(:started_at => nil).limit(1).first
return nil if mix.nil?
mix.started_at = Time.now
mix.mix_server = mix_server
mix.save
mix
end
def finish(url)
raise if url.nil?
self.completed_at = Time.now
self.url = url
save
end
end
end

View File

@ -14,6 +14,5 @@ module JamRuby
def description
@description = self.instrument.description
end
end
end

View File

@ -1,6 +1,62 @@
module JamRuby
class Notification < ActiveRecord::Base
self.primary_key = 'id'
default_scope order('created_at DESC')
belongs_to :target_user, :class_name => "JamRuby::User", :foreign_key => "target_user_id"
belongs_to :source_user, :class_name => "JamRuby::User", :foreign_key => "source_user_id"
belongs_to :band, :class_name => "JamRuby::Band", :foreign_key => "band_id"
belongs_to :session, :class_name => "JamRuby::MusicSession", :foreign_key => "session_id"
belongs_to :recording, :class_name => "JamRuby::Recording", :foreign_key => "recording_id"
def index(user_id)
results = Notification.where(:target_user_id => user_id).limit(50)
return results
end
def photo_url
unless self.source_user.nil?
self.source_user.photo_url
end
end
# used for persisted notifications
def formatted_msg
target_user, source_user, band, session, recording, invitation, join_request = nil
unless self.target_user_id.nil?
target_user = User.find(self.target_user_id)
end
unless self.source_user_id.nil?
source_user = User.find(self.source_user_id)
end
unless self.band_id.nil?
band = Band.find(self.band_id)
end
unless self.session_id.nil?
session = MusicSession.find(self.session_id)
end
unless self.recording_id.nil?
recording = Recording.find(self.recording_id)
end
unless self.invitation_id.nil?
invitation = Invitation.find(self.invitation_id)
end
unless self.join_request_id.nil?
join_request = JoinRequest.find(self.join_request_id)
end
return self.class.format_msg(self.description, source_user)
end
# TODO: MAKE ALL METHODS BELOW ASYNC SO THE CLIENT DOESN'T BLOCK ON NOTIFICATION LOGIC
# TODO: ADD TESTS FOR THIS CLASS
@ -9,9 +65,8 @@ module JamRuby
@@mq_router = MQRouter.new
@@message_factory = MessageFactory.new
def index(user_id)
results = Notification.where(:user_id => user_id).limit(50)
return results
def delete_all(session_id)
Notification.delete_all "(session_id = '#{session_id}')"
end
################### HELPERS ###################
@ -41,10 +96,39 @@ module JamRuby
return ids
end
def format_msg(description, user)
case description
when NotificationTypes::FRIEND_UPDATE
return "#{user.name} is now "
when NotificationTypes::FRIEND_REQUEST
return "#{user.name} has sent you a friend request."
when NotificationTypes::FRIEND_REQUEST_ACCEPTED
return "#{user.name} has accepted your friend request."
else
return ""
# when "friend_joined_session"
# when "social_media_friend_joined"
# when "join_request_approved"
# when "join_request_rejected"
# when "session_invitation"
# when "band_invitation"
# when "band_invitation_accepted"
# when "recording_available"
# else
end
end
################### FRIEND UPDATE ###################
def send_friend_update(user_id, online, connection)
user = User.find(user_id)
# (1) create notification
msg = @@message_factory.friend_update(user_id, online)
online_msg = online ? "online." : "offline."
notification_msg = format_msg(NotificationTypes::FRIEND_UPDATE, user) + online_msg
msg = @@message_factory.friend_update(user_id, user.name, user.photo_url, online, notification_msg)
# (2) get all of this user's friends
friend_ids = retrieve_friends(connection, user_id)
@ -54,54 +138,58 @@ module JamRuby
end
################### FRIEND REQUEST ###################
def send_friend_request(user_id, friend_id)
def send_friend_request(friend_request_id, user_id, friend_id)
user = User.find(user_id)
# (1) create notification
msg = @@message_factory.friend_request(user_id, user.name, user.photo_url, friend_id)
# (1) save to database
notification = Notification.new
notification.description = NotificationTypes::FRIEND_REQUEST
notification.source_user_id = user_id
notification.target_user_id = friend_id
notification.friend_request_id = friend_request_id
notification.save
# (2) send notification
# (2) create notification
notification_msg = format_msg(NotificationTypes::FRIEND_REQUEST, user)
msg = @@message_factory.friend_request(friend_request_id, user_id, user.name, user.photo_url, friend_id, notification_msg, notification.id, notification.created_at.to_s)
# (3) send notification
@@mq_router.publish_to_user(friend_id, msg)
# (3) save to database
# notification = Notification.new
# notification.type = "friend_request"
# notification.source_user_id = user_id
# notification.target_user_id = friend_id
# notification.save
end
############### FRIEND REQUEST ACCEPTED ###############
def send_friend_request_accepted(user_id, friend_id)
friend = User.find(friend_id)
# (1) create notification
msg = @@message_factory.friend_request_accepted(friend_id, friend.name, friend.photo_url, user_id)
# (1) save to database
notification = Notification.new
notification.description = NotificationTypes::FRIEND_REQUEST_ACCEPTED
notification.source_user_id = friend_id
notification.target_user_id = user_id
notification.save
# (2) send notification
# (2) create notification
notification_msg = format_msg(NotificationTypes::FRIEND_REQUEST_ACCEPTED, friend)
msg = @@message_factory.friend_request_accepted(friend_id, friend.name, friend.photo_url, user_id, notification_msg, notification.id, notification.created_at.to_s)
# (3) send notification
@@mq_router.publish_to_user(user_id, msg)
# (3) save to database
# notification = Notification.new
# notification.type = "friend_request_accepted"
# notification.source_user_id = friend_id
# notification.target_user_id = user_id
# notification.save
end
################## SESSION INVITATION ##################
def send_session_invitation(receiver_id, invitation_id)
# (1) create notification
# (1) save to database
notification = Notification.new
notification.description = NotificationTypes::SESSION_INVITATION
notification.target_user_id = receiver_id
notification.save
# (2) create notification
msg = @@message_factory.session_invitation(receiver_id, invitation_id)
# (2) send notification
# (3) send notification
@@mq_router.publish_to_user(receiver_id, msg)
# (3) save to database
# notification = Notification.new
# notification.type = "session_invitation"
# notification.target_user_id = receiver_id
end
def send_session_left(music_session, connection, user)
@ -115,31 +203,28 @@ module JamRuby
def send_join_request(music_session, join_request, sender, text)
# (1) create notification
# (1) save to database
# notification = Notification.new
# (2) create notification
msg = @@message_factory.join_request(music_session.id, join_request.id, sender.name, text)
# (2) send notification
# (3) send notification
@@mq_router.server_publish_to_session(music_session, msg)
# (3) save to database
# notification = Notification.new
end
def send_session_joined(connection, user)
# (1) create notification
# (1) save to database
# (2) create notification
msg = @@message_factory.user_joined_music_session(connection.music_session.id, user.id, user.name)
# (2a) send notification to session members
# (3a) send notification to session members
@@mq_router.server_publish_to_session(connection.music_session, msg, sender = {:client_id => connection.client_id})
# TODO: (2b) retrieve all friends and followers of user and send notification to them as well
# (3) save to database
# TODO: (3b) retrieve all friends and followers of user and send notification to them as well
end
# TODO: add methods to delete Notifications based on user id, session id, etc.
end
end
end

View File

@ -7,6 +7,7 @@ module JamRuby
belongs_to :owner, :class_name => "JamRuby::User", :inverse_of => :owned_recordings
belongs_to :band, :class_name => "JamRuby::Band", :inverse_of => :recordings
belongs_to :music_session, :class_name => "JamRuby::MusicSession", :inverse_of => :recording
has_one :mix, :class_name => "JamRuby::Mix", :inverse_of => :recording
has_many :recorded_tracks, :class_name => "JamRuby::RecordedTrack", :foreign_key => :recording_id
@ -35,6 +36,14 @@ module JamRuby
recordings_users.recording_id = recordings.id
}
)
.joins(
%Q{
LEFT OUTER JOIN
mixes
ON
recordings.id = mixes.recording_id
}
)
.order(
%Q{
recordings.created_at DESC

View File

@ -61,6 +61,10 @@ module JamRuby
has_many :favorites, :class_name => "JamRuby::UserFavorite", :foreign_key => "user_id"
has_many :inverse_favorites, :through => :favorites, :class_name => "JamRuby::User"
# notifications
has_many :notifications, :class_name => "JamRuby::Notification", :foreign_key => "target_user_id"
has_many :inverse_notifications, :through => :notifications, :class_name => "JamRuby::User"
# friends
has_many :friendships, :class_name => "JamRuby::Friendship", :foreign_key => "user_id"
has_many :friends, :through => :friendships, :class_name => "JamRuby::User"

View File

@ -11,9 +11,7 @@ class MQRouter
@@log = Logging.logger[MQRouter]
end
def access_music_session(music_session, user)
if music_session.nil?
raise ArgumentError, 'specified session not found'
end
@ -50,7 +48,6 @@ class MQRouter
# sends a message to a client with no checking of permissions (RAW USAGE)
# this method deliberately has no database interactivity/active_record objects
def publish_to_client(client_id, client_msg, sender = {:client_id => ""})
EM.schedule do
sender_client_id = sender[:client_id]
@ -64,7 +61,6 @@ class MQRouter
# sends a message to a session with no checking of permissions (RAW USAGE)
# this method deliberately has no database interactivity/active_record objects
def publish_to_session(music_session_id, client_ids, client_msg, sender = {:client_id => ""})
EM.schedule do
sender_client_id = sender[:client_id]
@ -81,7 +77,6 @@ class MQRouter
# sends a message to a user with no checking of permissions (RAW USAGE)
# this method deliberately has no database interactivity/active_record objects
def publish_to_user(user_id, user_msg)
EM.schedule do
@@log.debug "publishing to user:#{user_id} from server"
# put it on the topic exchange for users
@ -92,7 +87,6 @@ class MQRouter
# sends a message to a list of friends with no checking of permissions (RAW USAGE)
# this method deliberately has no database interactivity/active_record objects
def publish_to_friends(friend_ids, user_msg, from_user_id)
EM.schedule do
friend_ids.each do |friend_id|
@@log.debug "publishing to friend:#{friend_id} from user #{from_user_id}"

View File

@ -1,4 +1,5 @@
require 'spec_helper'
require 'digest/md5'
describe ArtifactUpdate do
@ -23,15 +24,14 @@ describe ArtifactUpdate do
artifact.product = 'JamClient/Win32'
artifact.version = '0.1.1'
artifact.uri = File.open(ARTIFACT_FILE)
artifact.sha1 = 'blahablahblah'
artifact.save!
artifact.environment.should == "public"
artifact.product.should == "JamClient/Win32"
artifact.version.should == "0.1.1"
File.basename(artifact.uri.path).should == ARTIFACT_FILE
artifact.sha1.should == "blahablahblah"
artifact.sha1.should == Digest::MD5.hexdigest(File.read(ARTIFACT_FILE))
artifact.size.should == File.size(ARTIFACT_FILE)
found = ArtifactUpdate.find_by_product_and_version('JamClient/Win32', '0.1.1')
artifact.should == found

View File

@ -0,0 +1,64 @@
require 'spec_helper'
describe Mix do
before do
@user = FactoryGirl.create(:user)
@connection = FactoryGirl.create(:connection, :user => @user)
@instrument = FactoryGirl.create(:instrument, :description => 'a great instrument')
@track = FactoryGirl.create(:track, :connection => @connection, :instrument => @instrument)
@music_session = FactoryGirl.create(:music_session, :creator => @user, :musician_access => true)
@music_session.connections << @connection
@music_session.save
@recording = Recording.start(@music_session.id, @user)
@recording.stop
@mix = Mix.schedule(@recording.id, @user.id, "description", "{}")
end
it "should create a mix for a user's recording properly" do
@mix.recording_id.should == @recording.id
@mix.owner_id.should == @user.id
@mix.description.should == "description"
@mix.spec.should == "{}"
@mix.url.should be_nil
@mix.mix_server.should be_nil
@mix.started_at.should be_nil
@mix.completed_at.should be_nil
end
it "should fail to create a mix if the userid doesn't own the recording" do
@user2 = FactoryGirl.create(:user)
expect { Mix.schedule(@recording.id, @user2.id) }.to raise_error
end
it "should fail if the recording doesn't exist" do
expect { @mix2 = Mix.schedule("bad_recording_id", @user.id) }.to raise_error
end
it "should return a mix when the cron asks for it" do
this_mix = Mix.next("server")
this_mix.id.should == @mix.id
@mix.reload
@mix.started_at.should_not be_nil
@mix.mix_server.should == "server"
@mix.completed_at.should be_nil
end
it "should record when a mix has finished" do
Mix.find(@mix.id).finish("http://blah")
@mix.reload
@mix.completed_at.should_not be_nil
@mix.url.should == "http://blah"
end
it "should re-run a mix if it was started a long time ago" do
this_mix = Mix.next("server")
@mix.reload
@mix.started_at -= 1000000
@mix.save
this_mix = Mix.next("server")
this_mix.id.should == @mix.id
end
end