From e51d5af15b9fad2ec33bf594503d63b38c0404cf Mon Sep 17 00:00:00 2001 From: Seth Call Date: Thu, 2 Aug 2012 22:07:03 -0500 Subject: [PATCH 001/274] * ruby library initial commit --- .gitignore | 17 +++++++++++++++++ Gemfile | 4 ++++ LICENSE | 22 ++++++++++++++++++++++ README.md | 29 +++++++++++++++++++++++++++++ Rakefile | 2 ++ jam_ruby.gemspec | 17 +++++++++++++++++ lib/jam_ruby.rb | 5 +++++ lib/jam_ruby/version.rb | 3 +++ 8 files changed, 99 insertions(+) create mode 100644 .gitignore create mode 100644 Gemfile create mode 100644 LICENSE create mode 100644 README.md create mode 100644 Rakefile create mode 100644 jam_ruby.gemspec create mode 100644 lib/jam_ruby.rb create mode 100644 lib/jam_ruby/version.rb diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..d87d4be66 --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +*.gem +*.rbc +.bundle +.config +.yardoc +Gemfile.lock +InstalledFiles +_yardoc +coverage +doc/ +lib/bundler/man +pkg +rdoc +spec/reports +test/tmp +test/version_tmp +tmp diff --git a/Gemfile b/Gemfile new file mode 100644 index 000000000..4ea25ec84 --- /dev/null +++ b/Gemfile @@ -0,0 +1,4 @@ +source 'https://rubygems.org' + +# Specify your gem's dependencies in jam_ruby.gemspec +gemspec diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..d990cbbc0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2012 Seth Call + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 000000000..8ebd64687 --- /dev/null +++ b/README.md @@ -0,0 +1,29 @@ +# JamRuby + +TODO: Write a gem description + +## Installation + +Add this line to your application's Gemfile: + + gem 'jam_ruby' + +And then execute: + + $ bundle + +Or install it yourself as: + + $ gem install jam_ruby + +## Usage + +TODO: Write usage instructions here + +## Contributing + +1. Fork it +2. Create your feature branch (`git checkout -b my-new-feature`) +3. Commit your changes (`git commit -am 'Added some feature'`) +4. Push to the branch (`git push origin my-new-feature`) +5. Create new Pull Request diff --git a/Rakefile b/Rakefile new file mode 100644 index 000000000..f57ae68a8 --- /dev/null +++ b/Rakefile @@ -0,0 +1,2 @@ +#!/usr/bin/env rake +require "bundler/gem_tasks" diff --git a/jam_ruby.gemspec b/jam_ruby.gemspec new file mode 100644 index 000000000..61ad44308 --- /dev/null +++ b/jam_ruby.gemspec @@ -0,0 +1,17 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../lib/jam_ruby/version', __FILE__) + +Gem::Specification.new do |gem| + gem.authors = ["Seth Call"] + gem.email = ["sethcall@gmail.com"] + gem.description = %q{TODO: Write a gem description} + gem.summary = %q{TODO: Write a gem summary} + gem.homepage = "" + + gem.files = `git ls-files`.split($\) + gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) } + gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) + gem.name = "jam_ruby" + gem.require_paths = ["lib"] + gem.version = JamRuby::VERSION +end diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb new file mode 100644 index 000000000..54d06f13e --- /dev/null +++ b/lib/jam_ruby.rb @@ -0,0 +1,5 @@ +require "jam_ruby/version" + +module JamRuby + # Your code goes here... +end diff --git a/lib/jam_ruby/version.rb b/lib/jam_ruby/version.rb new file mode 100644 index 000000000..33161c160 --- /dev/null +++ b/lib/jam_ruby/version.rb @@ -0,0 +1,3 @@ +module JamRuby + VERSION = "0.0.1" +end From 3acc4e93cccd669dd31058df5eee3cf71a233973 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Thu, 2 Aug 2012 22:08:54 -0500 Subject: [PATCH 002/274] * fixing TODO's in gemspec --- jam_ruby.gemspec | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/jam_ruby.gemspec b/jam_ruby.gemspec index 61ad44308..6722c6b78 100644 --- a/jam_ruby.gemspec +++ b/jam_ruby.gemspec @@ -3,9 +3,9 @@ require File.expand_path('../lib/jam_ruby/version', __FILE__) Gem::Specification.new do |gem| gem.authors = ["Seth Call"] - gem.email = ["sethcall@gmail.com"] - gem.description = %q{TODO: Write a gem description} - gem.summary = %q{TODO: Write a gem summary} + gem.email = ["seth@jamkazam.com"] + gem.description = %q{Common library for Jamkazam Ruby code} + gem.summary = %q{Common library for Jamkazam Ruby code} gem.homepage = "" gem.files = `git ls-files`.split($\) From e8ec5f3eaf47bf6d8b3113c765032db33cd62003 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 5 Aug 2012 22:01:00 -0500 Subject: [PATCH 003/274] * ported user model from rails tutorial; and it's tests --- .gitignore | 5 + Gemfile | 17 +++ README.md | 31 +---- config/database.yml | 8 ++ lib/jam_ruby.rb | 9 +- lib/jam_ruby/dbutil.rb | 7 ++ lib/jam_ruby/models/extensions/uuid.rb | 13 +++ lib/jam_ruby/models/jam_session.rb | 13 +++ lib/jam_ruby/models/user.rb | 23 ++++ spec/factories.rb | 12 ++ spec/jam_ruby/models/jam_session_spec.rb | 8 ++ spec/jam_ruby/models/user_spec.rb | 138 +++++++++++++++++++++++ spec/spec_db.rb | 11 ++ spec/spec_helper.rb | 73 ++++++++++++ 14 files changed, 341 insertions(+), 27 deletions(-) create mode 100644 config/database.yml create mode 100644 lib/jam_ruby/dbutil.rb create mode 100644 lib/jam_ruby/models/extensions/uuid.rb create mode 100644 lib/jam_ruby/models/jam_session.rb create mode 100644 lib/jam_ruby/models/user.rb create mode 100644 spec/factories.rb create mode 100644 spec/jam_ruby/models/jam_session_spec.rb create mode 100644 spec/jam_ruby/models/user_spec.rb create mode 100644 spec/spec_db.rb create mode 100644 spec/spec_helper.rb diff --git a/.gitignore b/.gitignore index d87d4be66..a35fe92d7 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,8 @@ spec/reports test/tmp test/version_tmp tmp + +.idea +*~ +*.swp +*.iml diff --git a/Gemfile b/Gemfile index 4ea25ec84..e6075ba88 100644 --- a/Gemfile +++ b/Gemfile @@ -1,4 +1,21 @@ source 'https://rubygems.org' +# Look for $WORKSPACE, otherwise use "workspace" as dev path. +workspace = ENV["WORKSPACE"] || "~/workspace" + + +gem 'pg', '0.14.0' +gem 'jam_db', :path=> "#{workspace}/jam-db/target/ruby_package" +gem 'activerecord', '3.2.7' +gem 'uuidtools', '2.1.2' +gem 'bcrypt-ruby', '3.0.1' + +group :test do + gem "factory_girl" + gem "rspec", "2.10.0" + gem 'spork', '0.9.0' + gem 'database_cleaner', '0.7.0' +end + # Specify your gem's dependencies in jam_ruby.gemspec gemspec diff --git a/README.md b/README.md index 8ebd64687..39a2c2e30 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,8 @@ # JamRuby -TODO: Write a gem description +## Environment +Create development database 'jam_ruby' +`createdb jam_ruby` -## Installation - -Add this line to your application's Gemfile: - - gem 'jam_ruby' - -And then execute: - - $ bundle - -Or install it yourself as: - - $ gem install jam_ruby - -## Usage - -TODO: Write usage instructions here - -## Contributing - -1. Fork it -2. Create your feature branch (`git checkout -b my-new-feature`) -3. Commit your changes (`git commit -am 'Added some feature'`) -4. Push to the branch (`git push origin my-new-feature`) -5. Create new Pull Request +Once you've created your database, migrate it: +`bundle exec jam_ruby up` diff --git a/config/database.yml b/config/database.yml new file mode 100644 index 000000000..322a29d46 --- /dev/null +++ b/config/database.yml @@ -0,0 +1,8 @@ +test: + adapter: postgresql + database: jam_ruby_test + pool: 3 + username: postgres + password: postgres + timeout: 2000 + encoding: unicode \ No newline at end of file diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index 54d06f13e..df2937c53 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -1,5 +1,12 @@ +require "pg" +require "active_record" +require "jam_db" +require 'uuidtools' require "jam_ruby/version" +require "jam_ruby/models/extensions/uuid" +require "jam_ruby/models/user" +require "jam_ruby/models/jam_session" module JamRuby - # Your code goes here... + end diff --git a/lib/jam_ruby/dbutil.rb b/lib/jam_ruby/dbutil.rb new file mode 100644 index 000000000..b59c7e2bc --- /dev/null +++ b/lib/jam_ruby/dbutil.rb @@ -0,0 +1,7 @@ +module JamRuby + class DbUtil + def self.create(connection_hash) + + end + end +end \ No newline at end of file diff --git a/lib/jam_ruby/models/extensions/uuid.rb b/lib/jam_ruby/models/extensions/uuid.rb new file mode 100644 index 000000000..406e0c567 --- /dev/null +++ b/lib/jam_ruby/models/extensions/uuid.rb @@ -0,0 +1,13 @@ +module JamRuby + module UUID + extend ActiveSupport::Concern + + included do + before_create :generate_uuid + + def generate_uuid + self.id = UUIDTools::UUID.random_create.to_s + end + end + end +end \ No newline at end of file diff --git a/lib/jam_ruby/models/jam_session.rb b/lib/jam_ruby/models/jam_session.rb new file mode 100644 index 000000000..639f699d7 --- /dev/null +++ b/lib/jam_ruby/models/jam_session.rb @@ -0,0 +1,13 @@ +module JamRuby + class JamSession < ActiveRecord::Base + + include JamRuby::UUID + + # Verifies that the specified user (user_session_id ) can join the jam_sessin_id + # returns true/false + def access?(jam_session_id, user_session_id) + + end + + end +end \ No newline at end of file diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb new file mode 100644 index 000000000..d219f5bda --- /dev/null +++ b/lib/jam_ruby/models/user.rb @@ -0,0 +1,23 @@ +module JamRuby + class User < ActiveRecord::Base + + attr_accessible :name, :email, :password, :password_confirmation + + has_secure_password + + before_save { |user| user.email = email.downcase } + before_save :create_remember_token + + validates :name, presence: true, length: {maximum: 50} + VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i + validates :email, presence: true, format: {with: VALID_EMAIL_REGEX}, + uniqueness: {case_sensitive: false} + validates :password, length: {minimum: 6} + validates :password_confirmation, presence: true + + private + def create_remember_token + self.remember_token = SecureRandom.urlsafe_base64 + end + end +end \ No newline at end of file diff --git a/spec/factories.rb b/spec/factories.rb new file mode 100644 index 000000000..d71d480d4 --- /dev/null +++ b/spec/factories.rb @@ -0,0 +1,12 @@ +FactoryGirl.define do + factory :user do + sequence(:name) { |n| "Person #{n}" } + sequence(:email) { |n| "person_#{n}@example.com"} + password "foobar" + password_confirmation "foobar" + + factory :admin do + admin true + end + end +end \ No newline at end of file diff --git a/spec/jam_ruby/models/jam_session_spec.rb b/spec/jam_ruby/models/jam_session_spec.rb new file mode 100644 index 000000000..5470417c8 --- /dev/null +++ b/spec/jam_ruby/models/jam_session_spec.rb @@ -0,0 +1,8 @@ +require 'spec_helper' + +describe JamSession do + + it 'can grant access to valid user' do + + end +end \ No newline at end of file diff --git a/spec/jam_ruby/models/user_spec.rb b/spec/jam_ruby/models/user_spec.rb new file mode 100644 index 000000000..4d5103226 --- /dev/null +++ b/spec/jam_ruby/models/user_spec.rb @@ -0,0 +1,138 @@ +require 'spec_helper' + +describe User do + + before do + @user = User.new(name: "Example User", email: "user@example.com", + password: "foobar", password_confirmation: "foobar") + end + + subject { @user } + + it { should respond_to(:name) } + it { should respond_to(:email) } + it { should respond_to(:password_digest) } + it { should respond_to(:password) } + it { should respond_to(:password_confirmation) } + it { should respond_to(:remember_token) } + it { should respond_to(:admin) } + it { should respond_to(:authenticate) } + + it { should be_valid } + it { should_not be_admin } + + describe "accessible attributes" do + it "should not allow access to admin" do + userish = User.new(admin: true, name: "george") + userish.admin.should == false + userish.name.should == "george" + userish.admin = true + userish.admin.should == true + end + end + + describe "with admin attribute set to 'true'" do + before do + @user.save! + @user.toggle!(:admin) + end + + it { should be_admin } + end + + describe "when name is not present" do + before { @user.name = " " } + it { should_not be_valid } + end + + describe "when email is not present" do + before { @user.email = " " } + it { should_not be_valid } + end + + describe "when name is too long" do + before { @user.name = "a" * 51 } + it { should_not be_valid } + end + + describe "when email format is invalid" do + it "should be invalid" do + addresses = %w[user@foo,com user_at_foo.org example.user@foo.] + addresses.each do |invalid_address| + @user.email = invalid_address + @user.should_not be_valid + end + end + end + + describe "when email format is valid" do + it "should be valid" do + addresses = %w[user@foo.COM A_US-ER@f.b.org frst.lst@foo.jp a+b@baz.cn] + addresses.each do |valid_address| + @user.email = valid_address + @user.should be_valid + end + end + end + + describe "when email address is already taken" do + before do + user_with_same_email = @user.dup + user_with_same_email.email = @user.email.upcase + user_with_same_email.save + end + + it { should_not be_valid } + end + + describe "email address with mixed case" do + let(:mixed_case_email) { "Foo@ExAMPle.CoM" } + + it "should be saved as all lower-case" do + @user.email = mixed_case_email + @user.save + @user.reload.email.should == mixed_case_email.downcase + end + end + + describe "when password is not present" do + before { @user.password = @user.password_confirmation = " " } + it { should_not be_valid } + end + + describe "when password doesn't match confirmation" do + before { @user.password_confirmation = "mismatch" } + it { should_not be_valid } + end + + describe "when password confirmation is nil" do + before { @user.password_confirmation = nil } + it { should_not be_valid } + end + + describe "with a password that's too short" do + before { @user.password = @user.password_confirmation = "a" * 5 } + it { should be_invalid } + end + + describe "return value of authenticate method" do + before { @user.save } + let(:found_user) { User.find_by_email(@user.email) } + + describe "with valid password" do + it { should == found_user.authenticate(@user.password) } + end + + describe "with invalid password" do + let(:user_for_invalid_password) { found_user.authenticate("invalid") } + + it { should_not == user_for_invalid_password } + specify { user_for_invalid_password.should be_false } + end + end + + describe "remember token" do + before { @user.save } + its(:remember_token) { should_not be_blank } + end +end \ No newline at end of file diff --git a/spec/spec_db.rb b/spec/spec_db.rb new file mode 100644 index 000000000..5a3a935ee --- /dev/null +++ b/spec/spec_db.rb @@ -0,0 +1,11 @@ +class SpecDb + + TEST_DB_NAME="jam_ruby_test" + + def self.recreate_database + conn = PG::Connection.open("dbname=postgres") + conn.exec("DROP DATABASE IF EXISTS #{TEST_DB_NAME}") + conn.exec("CREATE DATABASE #{TEST_DB_NAME}") + JamDb::Migrator.new.migrate(:dbname => TEST_DB_NAME) + end +end \ No newline at end of file diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 000000000..490c13840 --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,73 @@ + +require 'active_record' +require 'jam_db' +require 'spec_db' + +# recreate test database and migrate it +SpecDb::recreate_database +# initialize ActiveRecord's db connection +ActiveRecord::Base.establish_connection(YAML::load(File.open('config/database.yml'))["test"]) + +require 'jam_ruby' +require 'factory_girl' +require 'rubygems' +require 'spork' +require 'database_cleaner' + +include JamRuby + + +#uncomment the following line to use spork with the debugger +#require 'spork/ext/ruby-debug' + +Spork.prefork do + # Loading more in this block will cause your tests to run faster. However, + # if you change any configuration or code from libraries loaded here, you'll + # need to restart spork for it take effect. +# This file is copied to spec/ when you run 'rails generate rspec:install' + #ENV["RAILS_ENV"] ||= 'test' + #require File.expand_path("../../config/environment", __FILE__) + require 'rspec/autorun' + #require 'rspec/rails' +# This file was generated by the `rspec --init` command. Conventionally, all +# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. +# Require this file using `require "spec_helper"` to ensure that it is only +# loaded once. +# +# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration + RSpec.configure do |config| + config.treat_symbols_as_metadata_keys_with_true_values = true + config.run_all_when_everything_filtered = true + config.filter_run :focus + + config.before(:suite) do + DatabaseCleaner.strategy = :transaction + DatabaseCleaner.clean_with(:truncation) + end + + config.before(:each) do + DatabaseCleaner.start + end + + config.after(:each) do + DatabaseCleaner.clean + end + + # If you're not using ActiveRecord, or you'd prefer not to run each of your + # examples within a transaction, remove the following line or assign false + # instead of true. + #config.use_transactional_fixtures = true + + # Run specs in random order to surface order dependencies. If you find an + # order dependency and want to debug it, you can fix the order by providing + # the seed, which is printed after each run. + # --seed 1234 + config.order = 'random' + end +end + + +Spork.each_run do + # This code will be run each time you run your specs. + +end \ No newline at end of file From 0f847580e63fbd7099aa04c3075cf62fe94ef502 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sat, 18 Aug 2012 13:48:43 -0500 Subject: [PATCH 004/274] * jam-ruby updated to include jam_session model and tests --- .pg_migrate | 1 + .rspec | 2 + .rvmrc | 1 + Gemfile | 2 + lib/jam_ruby.rb | 4 +- lib/jam_ruby/message_factory.rb | 93 +++++++++++++++++++++++ lib/jam_ruby/models/extensions/uuid.rb | 13 ---- lib/jam_ruby/models/jam_session.rb | 12 +-- lib/jam_ruby/models/jam_session_member.rb | 9 +++ lib/jam_ruby/models/user.rb | 4 + spec/factories.rb | 10 ++- spec/jam_ruby/models/jam_session_spec.rb | 14 +++- spec/jam_ruby/models/user_spec.rb | 7 +- spec/spec_helper.rb | 1 + 14 files changed, 148 insertions(+), 25 deletions(-) create mode 100644 .pg_migrate create mode 100644 .rspec create mode 100644 .rvmrc create mode 100644 lib/jam_ruby/message_factory.rb delete mode 100644 lib/jam_ruby/models/extensions/uuid.rb create mode 100644 lib/jam_ruby/models/jam_session_member.rb diff --git a/.pg_migrate b/.pg_migrate new file mode 100644 index 000000000..e6afc2e53 --- /dev/null +++ b/.pg_migrate @@ -0,0 +1 @@ +up.connopts=dbname:jam_ruby diff --git a/.rspec b/.rspec new file mode 100644 index 000000000..5f1647637 --- /dev/null +++ b/.rspec @@ -0,0 +1,2 @@ +--color +--format progress diff --git a/.rvmrc b/.rvmrc new file mode 100644 index 000000000..566deff03 --- /dev/null +++ b/.rvmrc @@ -0,0 +1 @@ +rvm use ruby-1.9.3@jamruby --create diff --git a/Gemfile b/Gemfile index e6075ba88..b0e1c8d00 100644 --- a/Gemfile +++ b/Gemfile @@ -6,9 +6,11 @@ workspace = ENV["WORKSPACE"] || "~/workspace" gem 'pg', '0.14.0' gem 'jam_db', :path=> "#{workspace}/jam-db/target/ruby_package" +gem 'jampb', :path => '~/workspace/jam-pb/target/ruby/jampb' gem 'activerecord', '3.2.7' gem 'uuidtools', '2.1.2' gem 'bcrypt-ruby', '3.0.1' +gem 'ruby-protocol-buffers', '1.2.2' group :test do gem "factory_girl" diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index df2937c53..cc3109991 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -1,9 +1,11 @@ require "pg" require "active_record" require "jam_db" +require "jampb" require 'uuidtools' require "jam_ruby/version" -require "jam_ruby/models/extensions/uuid" +require "jam_ruby/message_factory" +require "jam_ruby/models/jam_session_member" require "jam_ruby/models/user" require "jam_ruby/models/jam_session" diff --git a/lib/jam_ruby/message_factory.rb b/lib/jam_ruby/message_factory.rb new file mode 100644 index 000000000..8ab61c786 --- /dev/null +++ b/lib/jam_ruby/message_factory.rb @@ -0,0 +1,93 @@ +module JamRuby + # creates messages (implementation: protocol buffer) objects cleanly + class MessageFactory + + CLIENT_TARGET = "client" + SERVER_TARGET = "server" + SESSION_TARGET_PREFIX = "session:" + USER_TARGET_PREFIX = "user:" + + + def initialize() + + end + + # create a login message using user/pass + def login_with_user_pass(username, password) + login = Jampb::Login.new(:username => username, :password => password) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::LOGIN, :target => SERVER_TARGET, :login => login) + end + + # create a login message using token (a cookie or similiar) + def login_with_token(token) + login = Jampb::Login.new(:token => token) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::LOGIN, :target => SERVER_TARGET, :login => login) + end + + # create a login ack (login was successful) + def login_ack(public_ip) + login_ack = Jampb::LoginAck.new(:public_ip => public_ip) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::LOGIN_ACK, :target => CLIENT_TARGET, :login_ack => login_ack) + end + + # create a jam session login message + def login_jam_session(jam_session) + login_jam_session = Jampb::LoginJamSession.new(:jam_session => jam_session) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::LOGIN_JAM_SESSION, :target => SESSION_TARGET_PREFIX + jam_session_id, :login_jam_session => login_jam_session) + end + + # create a jam session login message ack (success or on failure) + def login_jam_session_ack(error, error_reason) + login_jam_session_ack = Jampb::LoginJamSessionAck.new(:error => error, :error_reason => error_reason) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::LOGIN_JAM_SESSION_ACK, :target => CLIENT_TARGET, :login_jam_session_ack => login_jam_session_ack) + end + + # create a jam session 'leave session' message + def leave_jam_session(jam_session) + leave_jam_session = Jampb::LeaveJamSession.new(:jam_session => jam_session) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::LEAVE_JAM_SESSION, :target => SERVER_TARGET, :leave_jam_session => leave_jam_session) + end + + # create a jam session leave message ack (success or on failure) + def leave_jam_session_ack(error, error_reason) + leave_jam_session_ack = Jampb::LeaveJamSessionAck.new(:error => error, :error_reason => error_reason) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::LEAVE_JAM_SESSION_ACK, :target => CLIENT_TARGET, :leave_jam_session_ack => leave_jam_session_ack) + end + + # create a server error + def server_generic_error(error_msg) + error = Jampb::ServerGenericError.new(:error_msg => error_msg) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::SERVER_GENERIC_ERROR, :target => CLIENT_TARGET, :server_generic_error => error) + end + + # create a user-joined session message + def user_joined_jam_session(user_id, username) + error = Jampb::UserJoinedJamSession.new(:user_id => user_id, :username => username) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::USER_JOINED_JAM_SESSION, :target => CLIENT_TARGET, :server_generic_error => error) + end + + # is this message directed to the server? + def server_directed? msg + return msg.target == MessageFactory::SERVER_TARGET + end + + # is this message directed to the client? + def client_directed? msg + return msg.target == MessageFactory::CLIENT_TARGET + end + + # is this message directed to a (jam) session? + def session_directed? msg + return msg.start_with? MessageFactory::SESSION_PREFIX_TARGET + end + + # is this message directed to a user? + def user_directed? msg + return msg.start_with? MessageFactory::USER_PREFIX_TARGET + end + + def extract_session(msg) + return + end + end +end \ No newline at end of file diff --git a/lib/jam_ruby/models/extensions/uuid.rb b/lib/jam_ruby/models/extensions/uuid.rb deleted file mode 100644 index 406e0c567..000000000 --- a/lib/jam_ruby/models/extensions/uuid.rb +++ /dev/null @@ -1,13 +0,0 @@ -module JamRuby - module UUID - extend ActiveSupport::Concern - - included do - before_create :generate_uuid - - def generate_uuid - self.id = UUIDTools::UUID.random_create.to_s - end - end - end -end \ No newline at end of file diff --git a/lib/jam_ruby/models/jam_session.rb b/lib/jam_ruby/models/jam_session.rb index 639f699d7..472e605b7 100644 --- a/lib/jam_ruby/models/jam_session.rb +++ b/lib/jam_ruby/models/jam_session.rb @@ -1,12 +1,14 @@ module JamRuby class JamSession < ActiveRecord::Base - include JamRuby::UUID - - # Verifies that the specified user (user_session_id ) can join the jam_sessin_id - # returns true/false - def access?(jam_session_id, user_session_id) + belongs_to :user, :inverse_of => :jam_sessions + has_many :jam_session_members + has_many :users, :through => :jam_session_members + + # Verifies that the specified user can join this jam session + def access?(user) + return self.users.exists? user end end diff --git a/lib/jam_ruby/models/jam_session_member.rb b/lib/jam_ruby/models/jam_session_member.rb new file mode 100644 index 000000000..ec0c99ae7 --- /dev/null +++ b/lib/jam_ruby/models/jam_session_member.rb @@ -0,0 +1,9 @@ +module JamRuby + class JamSessionMember < ActiveRecord::Base + + belongs_to :user + belongs_to :jam_session + + + end +end \ No newline at end of file diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index d219f5bda..8c2fee21c 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -2,6 +2,10 @@ module JamRuby class User < ActiveRecord::Base attr_accessible :name, :email, :password, :password_confirmation + + has_many :jam_session_members + has_many :created_jam_sessions, :foreign_key => "user_id", :inverse_of => :user # sessions *created* by the user + has_many :jam_sessions, :through => :jam_session_members has_secure_password diff --git a/spec/factories.rb b/spec/factories.rb index d71d480d4..4e3d71480 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -1,5 +1,5 @@ FactoryGirl.define do - factory :user do + factory :user, :class => JamRuby::User do sequence(:name) { |n| "Person #{n}" } sequence(:email) { |n| "person_#{n}@example.com"} password "foobar" @@ -9,4 +9,12 @@ FactoryGirl.define do admin true end end + + factory :jam_session, :class => JamRuby::JamSession do + sequence(:name) { |n| "Jam Session #{n}" } + end + + factory :jam_session_member, :class => JamRuby::JamSessionMember do + + end end \ No newline at end of file diff --git a/spec/jam_ruby/models/jam_session_spec.rb b/spec/jam_ruby/models/jam_session_spec.rb index 5470417c8..d80ef9cd7 100644 --- a/spec/jam_ruby/models/jam_session_spec.rb +++ b/spec/jam_ruby/models/jam_session_spec.rb @@ -3,6 +3,18 @@ require 'spec_helper' describe JamSession do it 'can grant access to valid user' do - + + user1 = FactoryGirl.create(:user) # in the jam session + user2 = FactoryGirl.create(:user) # in the jam session + user3 = FactoryGirl.create(:user) # not in the jam session + + jam_session = FactoryGirl.create(:jam_session, :user => user1) + + jam_session_member1 = FactoryGirl.create(:jam_session_member, :user => user1, :jam_session => jam_session) + jam_session_member2 = FactoryGirl.create(:jam_session_member, :user => user2, :jam_session => jam_session) + + jam_session.access?(user1).should == true + jam_session.access?(user2).should == true + jam_session.access?(user3).should == false end end \ No newline at end of file diff --git a/spec/jam_ruby/models/user_spec.rb b/spec/jam_ruby/models/user_spec.rb index 4d5103226..8250064f8 100644 --- a/spec/jam_ruby/models/user_spec.rb +++ b/spec/jam_ruby/models/user_spec.rb @@ -23,10 +23,9 @@ describe User do describe "accessible attributes" do it "should not allow access to admin" do - userish = User.new(admin: true, name: "george") - userish.admin.should == false - userish.name.should == "george" - userish.admin = true + userish = User.new(admin: true) + userish.admin.should == false # the .new style above will be ignored + userish.admin = true # but deliberate property setting will work userish.admin.should == true end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 490c13840..b794c7707 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -13,6 +13,7 @@ require 'factory_girl' require 'rubygems' require 'spork' require 'database_cleaner' +require 'factories' include JamRuby From e8ebdeb5efe0b9c0aa98b9aa99eaed16914bf1af Mon Sep 17 00:00:00 2001 From: Seth Call Date: Tue, 21 Aug 2012 22:07:01 -0500 Subject: [PATCH 005/274] * declaring 'id' as primary key. this makes factory_girl behave on jdbc --- lib/jam_ruby/message_factory.rb | 10 +++++----- lib/jam_ruby/models/jam_session.rb | 3 ++- lib/jam_ruby/models/jam_session_member.rb | 3 ++- lib/jam_ruby/models/user.rb | 3 ++- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/lib/jam_ruby/message_factory.rb b/lib/jam_ruby/message_factory.rb index 8ab61c786..71ea5cccc 100644 --- a/lib/jam_ruby/message_factory.rb +++ b/lib/jam_ruby/message_factory.rb @@ -33,7 +33,7 @@ module JamRuby # create a jam session login message def login_jam_session(jam_session) login_jam_session = Jampb::LoginJamSession.new(:jam_session => jam_session) - return Jampb::ClientMessage.new(:type => ClientMessage::Type::LOGIN_JAM_SESSION, :target => SESSION_TARGET_PREFIX + jam_session_id, :login_jam_session => login_jam_session) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::LOGIN_JAM_SESSION, :target => SESSION_TARGET_PREFIX + jam_session, :login_jam_session => login_jam_session) end # create a jam session login message ack (success or on failure) @@ -78,16 +78,16 @@ module JamRuby # is this message directed to a (jam) session? def session_directed? msg - return msg.start_with? MessageFactory::SESSION_PREFIX_TARGET + return msg.target.start_with? MessageFactory::SESSION_TARGET_PREFIX end # is this message directed to a user? def user_directed? msg - return msg.start_with? MessageFactory::USER_PREFIX_TARGET + return msg.target.start_with? MessageFactory::USER_PREFIX_TARGET end def extract_session(msg) - return + return msg.target[MessageFactory::SESSION_TARGET_PREFIX..-1] end end -end \ No newline at end of file +end diff --git a/lib/jam_ruby/models/jam_session.rb b/lib/jam_ruby/models/jam_session.rb index 472e605b7..c9e46416a 100644 --- a/lib/jam_ruby/models/jam_session.rb +++ b/lib/jam_ruby/models/jam_session.rb @@ -1,6 +1,7 @@ module JamRuby class JamSession < ActiveRecord::Base + set_primary_key :id belongs_to :user, :inverse_of => :jam_sessions has_many :jam_session_members @@ -12,4 +13,4 @@ module JamRuby end end -end \ No newline at end of file +end diff --git a/lib/jam_ruby/models/jam_session_member.rb b/lib/jam_ruby/models/jam_session_member.rb index ec0c99ae7..73bb752c4 100644 --- a/lib/jam_ruby/models/jam_session_member.rb +++ b/lib/jam_ruby/models/jam_session_member.rb @@ -1,9 +1,10 @@ module JamRuby class JamSessionMember < ActiveRecord::Base + set_primary_key :id belongs_to :user belongs_to :jam_session end -end \ No newline at end of file +end diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 8c2fee21c..26e857d89 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -3,6 +3,7 @@ module JamRuby attr_accessible :name, :email, :password, :password_confirmation + set_primary_key :id has_many :jam_session_members has_many :created_jam_sessions, :foreign_key => "user_id", :inverse_of => :user # sessions *created* by the user has_many :jam_sessions, :through => :jam_session_members @@ -24,4 +25,4 @@ module JamRuby self.remember_token = SecureRandom.urlsafe_base64 end end -end \ No newline at end of file +end From 5ee5b765c400c5b128c76d330f4d95b325feb020 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Wed, 22 Aug 2012 07:57:50 -0500 Subject: [PATCH 006/274] * changing how primary key is defined (using non deprecated method) --- lib/jam_ruby/models/jam_session.rb | 2 +- lib/jam_ruby/models/jam_session_member.rb | 2 +- lib/jam_ruby/models/user.rb | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/jam_ruby/models/jam_session.rb b/lib/jam_ruby/models/jam_session.rb index c9e46416a..56da4ee13 100644 --- a/lib/jam_ruby/models/jam_session.rb +++ b/lib/jam_ruby/models/jam_session.rb @@ -1,7 +1,7 @@ module JamRuby class JamSession < ActiveRecord::Base - set_primary_key :id + self.primary_key = 'id' belongs_to :user, :inverse_of => :jam_sessions has_many :jam_session_members diff --git a/lib/jam_ruby/models/jam_session_member.rb b/lib/jam_ruby/models/jam_session_member.rb index 73bb752c4..662e719dd 100644 --- a/lib/jam_ruby/models/jam_session_member.rb +++ b/lib/jam_ruby/models/jam_session_member.rb @@ -1,7 +1,7 @@ module JamRuby class JamSessionMember < ActiveRecord::Base - set_primary_key :id + self.primary_key = 'id' belongs_to :user belongs_to :jam_session diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 26e857d89..259cb9675 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -3,8 +3,9 @@ module JamRuby attr_accessible :name, :email, :password, :password_confirmation - set_primary_key :id - has_many :jam_session_members + self.primary_key = 'id' + + has_many :jam_session_members has_many :created_jam_sessions, :foreign_key => "user_id", :inverse_of => :user # sessions *created* by the user has_many :jam_sessions, :through => :jam_session_members From f83edca8400e9d9ece983bf6764b5683e36a8d7c Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 26 Aug 2012 06:35:13 -0500 Subject: [PATCH 007/274] * updating message_factory to account for new pb message type --- lib/jam_ruby.rb | 2 ++ lib/jam_ruby/message_factory.rb | 39 ++++++++++++++++++++++++++++----- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index cc3109991..7863cab18 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -9,6 +9,8 @@ require "jam_ruby/models/jam_session_member" require "jam_ruby/models/user" require "jam_ruby/models/jam_session" +include Jampb + module JamRuby end diff --git a/lib/jam_ruby/message_factory.rb b/lib/jam_ruby/message_factory.rb index 71ea5cccc..0e0edf233 100644 --- a/lib/jam_ruby/message_factory.rb +++ b/lib/jam_ruby/message_factory.rb @@ -7,11 +7,22 @@ module JamRuby SESSION_TARGET_PREFIX = "session:" USER_TARGET_PREFIX = "user:" + def initialize() + @type_values = {} + + Jampb::ClientMessage::Type.constants.each do |constant| + @type_values[Jampb::ClientMessage::Type.const_get(constant)] = constant + end end + # given a string (bytes) payload, return a client message + def parse_client_msg(payload) + return Jampb::ClientMessage.parse(payload) + end + # create a login message using user/pass def login_with_user_pass(username, password) login = Jampb::Login.new(:username => username, :password => password) @@ -33,7 +44,7 @@ module JamRuby # create a jam session login message def login_jam_session(jam_session) login_jam_session = Jampb::LoginJamSession.new(:jam_session => jam_session) - return Jampb::ClientMessage.new(:type => ClientMessage::Type::LOGIN_JAM_SESSION, :target => SESSION_TARGET_PREFIX + jam_session, :login_jam_session => login_jam_session) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::LOGIN_JAM_SESSION, :target => SERVER_TARGET, :login_jam_session => login_jam_session) end # create a jam session login message ack (success or on failure) @@ -54,16 +65,28 @@ module JamRuby return Jampb::ClientMessage.new(:type => ClientMessage::Type::LEAVE_JAM_SESSION_ACK, :target => CLIENT_TARGET, :leave_jam_session_ack => leave_jam_session_ack) end - # create a server error + # create a server error def server_generic_error(error_msg) error = Jampb::ServerGenericError.new(:error_msg => error_msg) return Jampb::ClientMessage.new(:type => ClientMessage::Type::SERVER_GENERIC_ERROR, :target => CLIENT_TARGET, :server_generic_error => error) end - # create a user-joined session message + # create a server rejection error + def server_rejection_error(error_msg) + error = Jampb::ServerRejectionError.new(:error_msg => error_msg) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::SERVER_REJECTION_ERROR, :target => CLIENT_TARGET, :server_rejection_error => error) + end + + # create a user-joined session messag def user_joined_jam_session(user_id, username) - error = Jampb::UserJoinedJamSession.new(:user_id => user_id, :username => username) - return Jampb::ClientMessage.new(:type => ClientMessage::Type::USER_JOINED_JAM_SESSION, :target => CLIENT_TARGET, :server_generic_error => error) + joined = Jampb::UserJoinedJamSession.new(:user_id => user_id, :username => username) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::USER_JOINED_JAM_SESSION, :target => CLIENT_TARGET, :user_joined_jam_session => joined) + end + + # create a heartbeat + def heartbeat() + heartbeat = Jampb::Heartbeat.new + return Jampb::ClientMessage.new(:type => ClientMessage::Type::HEARTBEAT, :target => SERVER_TARGET, :heartbeat => heartbeat) end # is this message directed to the server? @@ -83,11 +106,15 @@ module JamRuby # is this message directed to a user? def user_directed? msg - return msg.target.start_with? MessageFactory::USER_PREFIX_TARGET + return msg.target.start_with? MessageFactory::USER_TARGET_PREFIX end def extract_session(msg) return msg.target[MessageFactory::SESSION_TARGET_PREFIX..-1] end + + def get_message_type msg + return @type_values[msg.type] + end end end From 3db0861a559a75889c17a55991996cdc7dd25717 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 26 Aug 2012 13:28:08 -0500 Subject: [PATCH 008/274] * making to_string of models more useful --- lib/jam_ruby/models/jam_session.rb | 4 ++++ lib/jam_ruby/models/user.rb | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/lib/jam_ruby/models/jam_session.rb b/lib/jam_ruby/models/jam_session.rb index 56da4ee13..bc5f17482 100644 --- a/lib/jam_ruby/models/jam_session.rb +++ b/lib/jam_ruby/models/jam_session.rb @@ -12,5 +12,9 @@ module JamRuby return self.users.exists? user end + def to_s + return name + end + end end diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 259cb9675..f8002ffb3 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -25,5 +25,11 @@ module JamRuby def create_remember_token self.remember_token = SecureRandom.urlsafe_base64 end + + def to_s + return email unless email.nil? + return name unless name.nil? + return id + end end end From 34cac7bd560161df25ab323a099bb8ce0162c786 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 26 Aug 2012 14:35:24 -0500 Subject: [PATCH 009/274] * using workspace var instead of hardcoded paths --- Gemfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Gemfile b/Gemfile index b0e1c8d00..28adfeac6 100644 --- a/Gemfile +++ b/Gemfile @@ -3,10 +3,9 @@ source 'https://rubygems.org' # Look for $WORKSPACE, otherwise use "workspace" as dev path. workspace = ENV["WORKSPACE"] || "~/workspace" - gem 'pg', '0.14.0' gem 'jam_db', :path=> "#{workspace}/jam-db/target/ruby_package" -gem 'jampb', :path => '~/workspace/jam-pb/target/ruby/jampb' +gem 'jampb', :path => "#{workspace}/jam-pb/target/ruby/jampb" gem 'activerecord', '3.2.7' gem 'uuidtools', '2.1.2' gem 'bcrypt-ruby', '3.0.1' From f26f923677b5f420a4b658cab75fe6f7d41b7092 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 26 Aug 2012 14:38:48 -0500 Subject: [PATCH 010/274] * adding easy migration script --- migrate.sh | 2 ++ 1 file changed, 2 insertions(+) create mode 100755 migrate.sh diff --git a/migrate.sh b/migrate.sh new file mode 100755 index 000000000..15f170598 --- /dev/null +++ b/migrate.sh @@ -0,0 +1,2 @@ +#!/bin/bas +jam_db up --connopts=dbname:jam host:localhost user:postgres password:postgres --verbose From 4a2bf208fb723a58ded10e57640ec4dc587e899b Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 26 Aug 2012 21:45:25 -0500 Subject: [PATCH 011/274] * adding support for TestSessionMessage --- lib/jam_ruby/message_factory.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/jam_ruby/message_factory.rb b/lib/jam_ruby/message_factory.rb index 0e0edf233..6195ec3d5 100644 --- a/lib/jam_ruby/message_factory.rb +++ b/lib/jam_ruby/message_factory.rb @@ -83,6 +83,12 @@ module JamRuby return Jampb::ClientMessage.new(:type => ClientMessage::Type::USER_JOINED_JAM_SESSION, :target => CLIENT_TARGET, :user_joined_jam_session => joined) end + # create a test message to send in session + def test_session_message(session_id, msg) + test = Jampb::TestSessionMessage.new(:msg => msg) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::TEST_SESSION_MESSAGE, :target => SESSION_TARGET_PREFIX + session_id, :test_session_message => test) + end + # create a heartbeat def heartbeat() heartbeat = Jampb::Heartbeat.new From 069de7e75469cbf926a6e5a5f60a3d6f4ca9ad0e Mon Sep 17 00:00:00 2001 From: Seth Call Date: Wed, 29 Aug 2012 08:21:30 -0500 Subject: [PATCH 012/274] *making updating password not mandatory on user update --- lib/jam_ruby/models/jam_session.rb | 6 +++--- lib/jam_ruby/models/jam_session_member.rb | 10 ++++++--- lib/jam_ruby/models/user.rb | 26 +++++++++++++++-------- migrate.sh | 4 ++-- spec/jam_ruby/models/jam_session_spec.rb | 4 ++-- 5 files changed, 31 insertions(+), 19 deletions(-) diff --git a/lib/jam_ruby/models/jam_session.rb b/lib/jam_ruby/models/jam_session.rb index bc5f17482..b0cb1cafc 100644 --- a/lib/jam_ruby/models/jam_session.rb +++ b/lib/jam_ruby/models/jam_session.rb @@ -2,10 +2,10 @@ module JamRuby class JamSession < ActiveRecord::Base self.primary_key = 'id' - belongs_to :user, :inverse_of => :jam_sessions + belongs_to :creator, :inverse_of => :jam_sessions, :class_name => "JamRuby::User", :foreign_key => "user_id" - has_many :jam_session_members - has_many :users, :through => :jam_session_members + has_many :jam_session_members, :class_name => "JamRuby::JamSessionMember" + has_many :users, :through => :jam_session_members, :class_name => "JamRuby::User" # Verifies that the specified user can join this jam session def access?(user) diff --git a/lib/jam_ruby/models/jam_session_member.rb b/lib/jam_ruby/models/jam_session_member.rb index 662e719dd..432c16e2d 100644 --- a/lib/jam_ruby/models/jam_session_member.rb +++ b/lib/jam_ruby/models/jam_session_member.rb @@ -2,9 +2,13 @@ module JamRuby class JamSessionMember < ActiveRecord::Base self.primary_key = 'id' - belongs_to :user - belongs_to :jam_session - + belongs_to :user, :class_name => "JamRuby::User" + belongs_to :jam_session, :class_name => "JamRuby::JamSession" + + def to_s + return self.user.to_s + end + end end diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index f8002ffb3..863658424 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -2,12 +2,13 @@ module JamRuby class User < ActiveRecord::Base attr_accessible :name, :email, :password, :password_confirmation + attr_accessor :updating_password self.primary_key = 'id' - has_many :jam_session_members - has_many :created_jam_sessions, :foreign_key => "user_id", :inverse_of => :user # sessions *created* by the user - has_many :jam_sessions, :through => :jam_session_members + has_many :jam_session_members, :class_name => "JamRuby::JamSessionMember" + has_many :created_jam_sessions, :foreign_key => "user_id", :inverse_of => :user, :class_name => "JamRuby::JamSession" # sessions *created* by the user + has_many :jam_sessions, :through => :jam_session_members, :class_name => "JamRuby::JamSession" has_secure_password @@ -18,18 +19,25 @@ module JamRuby VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i validates :email, presence: true, format: {with: VALID_EMAIL_REGEX}, uniqueness: {case_sensitive: false} - validates :password, length: {minimum: 6} - validates :password_confirmation, presence: true + validates_length_of :password, minimum: 6, maximum: 100, :if => :should_validate_password? - private - def create_remember_token - self.remember_token = SecureRandom.urlsafe_base64 - end + validates_presence_of :password_confirmation, :if => :should_validate_password? + #validates :password_confirmation, presence: true + validates_confirmation_of :password, :if => :should_validate_password? + + def should_validate_password? + updating_password || new_record? + end def to_s return email unless email.nil? return name unless name.nil? return id end + + private + def create_remember_token + self.remember_token = SecureRandom.urlsafe_base64 + end end end diff --git a/migrate.sh b/migrate.sh index 15f170598..a9afa1578 100755 --- a/migrate.sh +++ b/migrate.sh @@ -1,2 +1,2 @@ -#!/bin/bas -jam_db up --connopts=dbname:jam host:localhost user:postgres password:postgres --verbose +#!/bin/bash +bundle exec jam_db up --connopts=dbname:jam host:localhost user:postgres password:postgres --verbose diff --git a/spec/jam_ruby/models/jam_session_spec.rb b/spec/jam_ruby/models/jam_session_spec.rb index d80ef9cd7..6581d8f86 100644 --- a/spec/jam_ruby/models/jam_session_spec.rb +++ b/spec/jam_ruby/models/jam_session_spec.rb @@ -8,7 +8,7 @@ describe JamSession do user2 = FactoryGirl.create(:user) # in the jam session user3 = FactoryGirl.create(:user) # not in the jam session - jam_session = FactoryGirl.create(:jam_session, :user => user1) + jam_session = FactoryGirl.create(:jam_session, :creator => user1) jam_session_member1 = FactoryGirl.create(:jam_session_member, :user => user1, :jam_session => jam_session) jam_session_member2 = FactoryGirl.create(:jam_session_member, :user => user2, :jam_session => jam_session) @@ -17,4 +17,4 @@ describe JamSession do jam_session.access?(user2).should == true jam_session.access?(user3).should == false end -end \ No newline at end of file +end From cc45a358e627a219bd6625d0c3bc79664f10638c Mon Sep 17 00:00:00 2001 From: Seth Call Date: Mon, 3 Sep 2012 17:01:05 -0500 Subject: [PATCH 013/274] * fixed tests; setting default setting of 'public' for tests to false --- lib/jam_ruby/message_factory.rb | 2 +- lib/jam_ruby/models/jam_session.rb | 10 +++++----- spec/factories.rb | 1 + 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/jam_ruby/message_factory.rb b/lib/jam_ruby/message_factory.rb index 6195ec3d5..e8845c892 100644 --- a/lib/jam_ruby/message_factory.rb +++ b/lib/jam_ruby/message_factory.rb @@ -14,7 +14,7 @@ module JamRuby Jampb::ClientMessage::Type.constants.each do |constant| @type_values[Jampb::ClientMessage::Type.const_get(constant)] = constant - end + end end diff --git a/lib/jam_ruby/models/jam_session.rb b/lib/jam_ruby/models/jam_session.rb index b0cb1cafc..718f63b2f 100644 --- a/lib/jam_ruby/models/jam_session.rb +++ b/lib/jam_ruby/models/jam_session.rb @@ -1,7 +1,7 @@ module JamRuby class JamSession < ActiveRecord::Base - self.primary_key = 'id' + self.primary_key = 'id' belongs_to :creator, :inverse_of => :jam_sessions, :class_name => "JamRuby::User", :foreign_key => "user_id" has_many :jam_session_members, :class_name => "JamRuby::JamSessionMember" @@ -9,12 +9,12 @@ module JamRuby # Verifies that the specified user can join this jam session def access?(user) - return self.users.exists? user + return self.public || self.users.exists?(user) end - def to_s - return name - end + def to_s + return name + end end end diff --git a/spec/factories.rb b/spec/factories.rb index 4e3d71480..fc4ec00ee 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -12,6 +12,7 @@ FactoryGirl.define do factory :jam_session, :class => JamRuby::JamSession do sequence(:name) { |n| "Jam Session #{n}" } + public false end factory :jam_session_member, :class => JamRuby::JamSessionMember do From 676725f693dbc437c7de35813333d419c94105a5 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sat, 15 Sep 2012 13:54:07 -0500 Subject: [PATCH 014/274] * adding host: localhost to config --- config/database.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/config/database.yml b/config/database.yml index 322a29d46..77c6e0e33 100644 --- a/config/database.yml +++ b/config/database.yml @@ -1,8 +1,9 @@ test: adapter: postgresql database: jam_ruby_test + host: localhost pool: 3 username: postgres password: postgres timeout: 2000 - encoding: unicode \ No newline at end of file + encoding: unicode From 0681d662bcde9ebf3f0ae00e2aacb295b192b366 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Mon, 17 Sep 2012 22:37:17 -0500 Subject: [PATCH 015/274] * forcing password connect with jam-ruby test helpers --- spec/spec_db.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/spec_db.rb b/spec/spec_db.rb index 5a3a935ee..d65d254a5 100644 --- a/spec/spec_db.rb +++ b/spec/spec_db.rb @@ -3,9 +3,9 @@ class SpecDb TEST_DB_NAME="jam_ruby_test" def self.recreate_database - conn = PG::Connection.open("dbname=postgres") + conn = PG::Connection.open("dbname=postgres user=postgres password=postgres") conn.exec("DROP DATABASE IF EXISTS #{TEST_DB_NAME}") conn.exec("CREATE DATABASE #{TEST_DB_NAME}") - JamDb::Migrator.new.migrate(:dbname => TEST_DB_NAME) + JamDb::Migrator.new.migrate(:dbname => TEST_DB_NAME, :user => "postgres", :password => "postgres") end -end \ No newline at end of file +end From 12fb985883b444db458c042af252ba2c13525b5a Mon Sep 17 00:00:00 2001 From: Seth Call Date: Wed, 19 Sep 2012 23:19:21 -0500 Subject: [PATCH 016/274] * moving jam-db to test scope only --- Gemfile | 6 ++++-- lib/jam_ruby.rb | 1 - 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index 28adfeac6..061aa233e 100644 --- a/Gemfile +++ b/Gemfile @@ -3,8 +3,9 @@ source 'https://rubygems.org' # Look for $WORKSPACE, otherwise use "workspace" as dev path. workspace = ENV["WORKSPACE"] || "~/workspace" -gem 'pg', '0.14.0' -gem 'jam_db', :path=> "#{workspace}/jam-db/target/ruby_package" +gem 'pg', '0.14.0', :platform => [:mri, :mswin, :mingw] +gem 'jdbc_postgres', :platform => [:jruby] + gem 'jampb', :path => "#{workspace}/jam-pb/target/ruby/jampb" gem 'activerecord', '3.2.7' gem 'uuidtools', '2.1.2' @@ -12,6 +13,7 @@ gem 'bcrypt-ruby', '3.0.1' gem 'ruby-protocol-buffers', '1.2.2' group :test do + gem 'jam_db', :path=> "#{workspace}/jam-db/target/ruby_package" gem "factory_girl" gem "rspec", "2.10.0" gem 'spork', '0.9.0' diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index 7863cab18..32786f931 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -1,6 +1,5 @@ require "pg" require "active_record" -require "jam_db" require "jampb" require 'uuidtools' require "jam_ruby/version" From 4660434a2bd202cc58db25d4d6346346ffadb691 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Mon, 1 Oct 2012 17:27:32 -0400 Subject: [PATCH 017/274] user presence development --- lib/jam_ruby.rb | 4 ++++ lib/jam_ruby/message_factory.rb | 2 +- lib/jam_ruby/models/band.rb | 9 +++++++++ lib/jam_ruby/models/connection.rb | 7 +++++++ lib/jam_ruby/models/friendship.rb | 10 ++++++++++ lib/jam_ruby/models/musician.rb | 9 +++++++++ lib/jam_ruby/models/user.rb | 7 +++++++ 7 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 lib/jam_ruby/models/band.rb create mode 100644 lib/jam_ruby/models/connection.rb create mode 100644 lib/jam_ruby/models/friendship.rb create mode 100644 lib/jam_ruby/models/musician.rb diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index 32786f931..cff11c020 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -6,6 +6,10 @@ require "jam_ruby/version" require "jam_ruby/message_factory" require "jam_ruby/models/jam_session_member" require "jam_ruby/models/user" +require "jam_ruby/models/musician" +require "jam_ruby/models/band" +require "jam_ruby/models/connection" +require "jam_ruby/models/friendship" require "jam_ruby/models/jam_session" include Jampb diff --git a/lib/jam_ruby/message_factory.rb b/lib/jam_ruby/message_factory.rb index e8845c892..1fe146526 100644 --- a/lib/jam_ruby/message_factory.rb +++ b/lib/jam_ruby/message_factory.rb @@ -1,4 +1,4 @@ -module JamRuby + module JamRuby # creates messages (implementation: protocol buffer) objects cleanly class MessageFactory diff --git a/lib/jam_ruby/models/band.rb b/lib/jam_ruby/models/band.rb new file mode 100644 index 000000000..4a26f47b2 --- /dev/null +++ b/lib/jam_ruby/models/band.rb @@ -0,0 +1,9 @@ +module JamRuby + class Band < ActiveRecord::Base + + self.primary_key = 'id' + + has_and_belongs_to_many :musicians + + end +end \ No newline at end of file diff --git a/lib/jam_ruby/models/connection.rb b/lib/jam_ruby/models/connection.rb new file mode 100644 index 000000000..229ca1d4a --- /dev/null +++ b/lib/jam_ruby/models/connection.rb @@ -0,0 +1,7 @@ +module JamRuby + class Connection < ActiveRecord::Base + + self.primary_key = 'id' + + end +end \ No newline at end of file diff --git a/lib/jam_ruby/models/friendship.rb b/lib/jam_ruby/models/friendship.rb new file mode 100644 index 000000000..1af8ca24c --- /dev/null +++ b/lib/jam_ruby/models/friendship.rb @@ -0,0 +1,10 @@ +module JamRuby + class Friendship < ActiveRecord::Base + + self.primary_key = 'id' + + belongs_to :user, :class_name => "JamRuby::User" + belongs_to :friend, :class_name => "JamRuby::User" + + end +end \ No newline at end of file diff --git a/lib/jam_ruby/models/musician.rb b/lib/jam_ruby/models/musician.rb new file mode 100644 index 000000000..1cc55a29d --- /dev/null +++ b/lib/jam_ruby/models/musician.rb @@ -0,0 +1,9 @@ +module JamRuby + class Musician < JamRuby::User + + self.primary_key = 'id' + + has_and_belongs_to_many :bands + + end +end \ No newline at end of file diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 863658424..a25bfe31d 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -6,6 +6,13 @@ module JamRuby self.primary_key = 'id' + belongs_to :biz, :polymorphic => true + + has_many :friendships + has_many :friends, :through => :friendships + has_many :inverse_friendships, :class_name => "JamRuby::Friendship", :foreign_key => "friend_id" + has_many :inverse_friends, :through => :inverse_friendships, :source => :user + has_many :jam_session_members, :class_name => "JamRuby::JamSessionMember" has_many :created_jam_sessions, :foreign_key => "user_id", :inverse_of => :user, :class_name => "JamRuby::JamSession" # sessions *created* by the user has_many :jam_sessions, :through => :jam_session_members, :class_name => "JamRuby::JamSession" From 2c60c3b263c76209e20eb434017dfaa884926bf8 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Tue, 2 Oct 2012 01:02:02 -0400 Subject: [PATCH 018/274] added friend_update message / user presence development --- lib/jam_ruby/message_factory.rb | 6 ++++++ lib/jam_ruby/models/connection.rb | 8 ++++++++ lib/jam_ruby/models/user.rb | 15 +++++++++------ 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/lib/jam_ruby/message_factory.rb b/lib/jam_ruby/message_factory.rb index 1fe146526..e0c565bbe 100644 --- a/lib/jam_ruby/message_factory.rb +++ b/lib/jam_ruby/message_factory.rb @@ -95,6 +95,12 @@ return Jampb::ClientMessage.new(:type => ClientMessage::Type::HEARTBEAT, :target => SERVER_TARGET, :heartbeat => heartbeat) 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, :target => CLIENT_TARGET, :friend_update => friend) + end + # is this message directed to the server? def server_directed? msg return msg.target == MessageFactory::SERVER_TARGET diff --git a/lib/jam_ruby/models/connection.rb b/lib/jam_ruby/models/connection.rb index 229ca1d4a..49ffaed14 100644 --- a/lib/jam_ruby/models/connection.rb +++ b/lib/jam_ruby/models/connection.rb @@ -1,7 +1,15 @@ module JamRuby class Connection < ActiveRecord::Base + attr_accessor :user_id, :client_id + self.primary_key = 'id' + belongs_to :user, :class_name => "JamRuby::User" + + def initialize(user_id, client_id) + @user_id = user_id + @client_id = client_id + end end end \ No newline at end of file diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index a25bfe31d..8674ecb95 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -6,14 +6,17 @@ module JamRuby self.primary_key = 'id' - belongs_to :biz, :polymorphic => true + # re-visit later (used for multiple table inheritance w/ Musician model) + # belongs_to :biz, :polymorphic => true + + has_many :connections, :class_name => "JamRuby::Connection" has_many :friendships has_many :friends, :through => :friendships has_many :inverse_friendships, :class_name => "JamRuby::Friendship", :foreign_key => "friend_id" has_many :inverse_friends, :through => :inverse_friendships, :source => :user - - has_many :jam_session_members, :class_name => "JamRuby::JamSessionMember" + + has_many :jam_session_members, :class_name => "JamRuby::JamSessionMember" has_many :created_jam_sessions, :foreign_key => "user_id", :inverse_of => :user, :class_name => "JamRuby::JamSession" # sessions *created* by the user has_many :jam_sessions, :through => :jam_session_members, :class_name => "JamRuby::JamSession" @@ -43,8 +46,8 @@ module JamRuby end private - def create_remember_token - self.remember_token = SecureRandom.urlsafe_base64 - end + def create_remember_token + self.remember_token = SecureRandom.urlsafe_base64 + end end end From 0f37d1523c1f4eb118a6d162099fb3d16d516cf8 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Tue, 2 Oct 2012 22:50:23 -0500 Subject: [PATCH 019/274] * VRFS-15: new models for sessions REST API --- lib/jam_ruby.rb | 4 +-- lib/jam_ruby/message_factory.rb | 30 ++++++++++----------- lib/jam_ruby/models/jam_session.rb | 20 -------------- lib/jam_ruby/models/jam_session_member.rb | 14 ---------- lib/jam_ruby/models/music_session.rb | 21 +++++++++++++++ lib/jam_ruby/models/music_session_client.rb | 14 ++++++++++ lib/jam_ruby/models/user.rb | 6 ++--- spec/factories.rb | 7 +++-- spec/jam_ruby/models/jam_session_spec.rb | 20 -------------- spec/jam_ruby/models/music_session_spec.rb | 20 ++++++++++++++ 10 files changed, 78 insertions(+), 78 deletions(-) delete mode 100644 lib/jam_ruby/models/jam_session.rb delete mode 100644 lib/jam_ruby/models/jam_session_member.rb create mode 100644 lib/jam_ruby/models/music_session.rb create mode 100644 lib/jam_ruby/models/music_session_client.rb delete mode 100644 spec/jam_ruby/models/jam_session_spec.rb create mode 100644 spec/jam_ruby/models/music_session_spec.rb diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index 32786f931..044c639ea 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -4,9 +4,9 @@ require "jampb" require 'uuidtools' require "jam_ruby/version" require "jam_ruby/message_factory" -require "jam_ruby/models/jam_session_member" +require "jam_ruby/models/music_session_client" require "jam_ruby/models/user" -require "jam_ruby/models/jam_session" +require "jam_ruby/models/music_session" include Jampb diff --git a/lib/jam_ruby/message_factory.rb b/lib/jam_ruby/message_factory.rb index e8845c892..892886440 100644 --- a/lib/jam_ruby/message_factory.rb +++ b/lib/jam_ruby/message_factory.rb @@ -42,27 +42,27 @@ module JamRuby end # create a jam session login message - def login_jam_session(jam_session) - login_jam_session = Jampb::LoginJamSession.new(:jam_session => jam_session) - return Jampb::ClientMessage.new(:type => ClientMessage::Type::LOGIN_JAM_SESSION, :target => SERVER_TARGET, :login_jam_session => login_jam_session) + def login_music_session(music_session) + login_music_session = Jampb::LoginMusicSession.new(:music_session => music_session) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::LOGIN_MUSIC_SESSION, :target => SERVER_TARGET, :login_music_session => login_music_session) end # create a jam session login message ack (success or on failure) - def login_jam_session_ack(error, error_reason) - login_jam_session_ack = Jampb::LoginJamSessionAck.new(:error => error, :error_reason => error_reason) - return Jampb::ClientMessage.new(:type => ClientMessage::Type::LOGIN_JAM_SESSION_ACK, :target => CLIENT_TARGET, :login_jam_session_ack => login_jam_session_ack) + def login_music_session_ack(error, error_reason) + login_music_session_ack = Jampb::LoginMusicSessionAck.new(:error => error, :error_reason => error_reason) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::LOGIN_JAM_SESSION_ACK, :target => CLIENT_TARGET, :login_music_session_ack => login_music_session_ack) end # create a jam session 'leave session' message - def leave_jam_session(jam_session) - leave_jam_session = Jampb::LeaveJamSession.new(:jam_session => jam_session) - return Jampb::ClientMessage.new(:type => ClientMessage::Type::LEAVE_JAM_SESSION, :target => SERVER_TARGET, :leave_jam_session => leave_jam_session) + def leave_music_session(music_session) + leave_music_session = Jampb::LeaveMusicSession.new(:music_session => music_session) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::LEAVE_JAM_SESSION, :target => SERVER_TARGET, :leave_music_session => leave_music_session) end # create a jam session leave message ack (success or on failure) - def leave_jam_session_ack(error, error_reason) - leave_jam_session_ack = Jampb::LeaveJamSessionAck.new(:error => error, :error_reason => error_reason) - return Jampb::ClientMessage.new(:type => ClientMessage::Type::LEAVE_JAM_SESSION_ACK, :target => CLIENT_TARGET, :leave_jam_session_ack => leave_jam_session_ack) + def leave_music_session_ack(error, error_reason) + leave_music_session_ack = Jampb::LeaveMusicSessionAck.new(:error => error, :error_reason => error_reason) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::LEAVE_JAM_SESSION_ACK, :target => CLIENT_TARGET, :leave_music_session_ack => leave_music_session_ack) end # create a server error @@ -78,9 +78,9 @@ module JamRuby end # create a user-joined session messag - def user_joined_jam_session(user_id, username) - joined = Jampb::UserJoinedJamSession.new(:user_id => user_id, :username => username) - return Jampb::ClientMessage.new(:type => ClientMessage::Type::USER_JOINED_JAM_SESSION, :target => CLIENT_TARGET, :user_joined_jam_session => joined) + def user_joined_music_session(user_id, username) + joined = Jampb::UserJoinedMusicSession.new(:user_id => user_id, :username => username) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::USER_JOINED_JAM_SESSION, :target => CLIENT_TARGET, :user_joined_music_session => joined) end # create a test message to send in session diff --git a/lib/jam_ruby/models/jam_session.rb b/lib/jam_ruby/models/jam_session.rb deleted file mode 100644 index 718f63b2f..000000000 --- a/lib/jam_ruby/models/jam_session.rb +++ /dev/null @@ -1,20 +0,0 @@ -module JamRuby - class JamSession < ActiveRecord::Base - - self.primary_key = 'id' - belongs_to :creator, :inverse_of => :jam_sessions, :class_name => "JamRuby::User", :foreign_key => "user_id" - - has_many :jam_session_members, :class_name => "JamRuby::JamSessionMember" - has_many :users, :through => :jam_session_members, :class_name => "JamRuby::User" - - # Verifies that the specified user can join this jam session - def access?(user) - return self.public || self.users.exists?(user) - end - - def to_s - return name - end - - end -end diff --git a/lib/jam_ruby/models/jam_session_member.rb b/lib/jam_ruby/models/jam_session_member.rb deleted file mode 100644 index 432c16e2d..000000000 --- a/lib/jam_ruby/models/jam_session_member.rb +++ /dev/null @@ -1,14 +0,0 @@ -module JamRuby - class JamSessionMember < ActiveRecord::Base - - self.primary_key = 'id' - belongs_to :user, :class_name => "JamRuby::User" - belongs_to :jam_session, :class_name => "JamRuby::JamSession" - - def to_s - return self.user.to_s - end - - - end -end diff --git a/lib/jam_ruby/models/music_session.rb b/lib/jam_ruby/models/music_session.rb new file mode 100644 index 000000000..ff65d19a6 --- /dev/null +++ b/lib/jam_ruby/models/music_session.rb @@ -0,0 +1,21 @@ +module JamRuby + class MusicSession < ActiveRecord::Base + + self.primary_key = 'id' + belongs_to :creator, :inverse_of => :music_sessions, :class_name => "JamRuby::User", :foreign_key => "user_id" + + has_many :music_session_clients, :class_name => "JamRuby::MusicSessionClient" + has_many :users, :through => :music_session_clients, :class_name => "JamRuby::User" + + # Verifies that the specified user can join this jam session + def access?(user) + #return self.public || self.users.exists?(user) + return true + end + + def to_s + return description + end + + end +end diff --git a/lib/jam_ruby/models/music_session_client.rb b/lib/jam_ruby/models/music_session_client.rb new file mode 100644 index 000000000..15519afdd --- /dev/null +++ b/lib/jam_ruby/models/music_session_client.rb @@ -0,0 +1,14 @@ +module JamRuby + class MusicSessionClient < ActiveRecord::Base + self.primary_key = 'id' + + belongs_to :user, :class_name => "JamRuby::User" + belongs_to :music_session, :class_name => "JamRuby::MusicSession" + + def to_s + return "#{self.user.to_s}:#{self.ip_address}" + end + + + end +end diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 863658424..f9a0bf67c 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -6,9 +6,9 @@ module JamRuby self.primary_key = 'id' - has_many :jam_session_members, :class_name => "JamRuby::JamSessionMember" - has_many :created_jam_sessions, :foreign_key => "user_id", :inverse_of => :user, :class_name => "JamRuby::JamSession" # sessions *created* by the user - has_many :jam_sessions, :through => :jam_session_members, :class_name => "JamRuby::JamSession" + has_many :music_session_clients, :class_name => "JamRuby::MusicSessionClient" + has_many :created_music_sessions, :foreign_key => "user_id", :inverse_of => :user, :class_name => "JamRuby::MusicSession" # sessions *created* by the user + has_many :music_sessions, :through => :music_session_clients, :class_name => "JamRuby::MusicSession" has_secure_password diff --git a/spec/factories.rb b/spec/factories.rb index fc4ec00ee..dc1bcc916 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -10,12 +10,11 @@ FactoryGirl.define do end end - factory :jam_session, :class => JamRuby::JamSession do - sequence(:name) { |n| "Jam Session #{n}" } - public false + factory :music_session, :class => JamRuby::MusicSession do + sequence(:description) { |n| "Music Session #{n}" } end - factory :jam_session_member, :class => JamRuby::JamSessionMember do + factory :music_session_client, :class => JamRuby::MusicSessionClient do end end \ No newline at end of file diff --git a/spec/jam_ruby/models/jam_session_spec.rb b/spec/jam_ruby/models/jam_session_spec.rb deleted file mode 100644 index 6581d8f86..000000000 --- a/spec/jam_ruby/models/jam_session_spec.rb +++ /dev/null @@ -1,20 +0,0 @@ -require 'spec_helper' - -describe JamSession do - - it 'can grant access to valid user' do - - user1 = FactoryGirl.create(:user) # in the jam session - user2 = FactoryGirl.create(:user) # in the jam session - user3 = FactoryGirl.create(:user) # not in the jam session - - jam_session = FactoryGirl.create(:jam_session, :creator => user1) - - jam_session_member1 = FactoryGirl.create(:jam_session_member, :user => user1, :jam_session => jam_session) - jam_session_member2 = FactoryGirl.create(:jam_session_member, :user => user2, :jam_session => jam_session) - - jam_session.access?(user1).should == true - jam_session.access?(user2).should == true - jam_session.access?(user3).should == false - end -end diff --git a/spec/jam_ruby/models/music_session_spec.rb b/spec/jam_ruby/models/music_session_spec.rb new file mode 100644 index 000000000..5fbcdbbb7 --- /dev/null +++ b/spec/jam_ruby/models/music_session_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +describe MusicSession do + + it 'can grant access to valid user' do + + user1 = FactoryGirl.create(:user) # in the jam session + user2 = FactoryGirl.create(:user) # in the jam session + user3 = FactoryGirl.create(:user) # not in the jam session + + music_session = FactoryGirl.create(:music_session, :creator => user1) + + music_session_member1 = FactoryGirl.create(:music_session_client, :user => user1, :music_session => music_session, :ip_address => "1.1.1.1") + music_session_member2 = FactoryGirl.create(:music_session_client, :user => user2, :music_session => music_session, :ip_address => "2.2.2.2") + + music_session.access?(user1).should == true + music_session.access?(user2).should == true + #music_session.access?(user3).should == false -- wait until we have finalized access models in place + end +end From 4c0f21741e30b2696efb47f2ad6e65dd5b6ce1ae Mon Sep 17 00:00:00 2001 From: tihot_jk Date: Wed, 3 Oct 2012 00:34:22 -0700 Subject: [PATCH 020/274] Fixing the peer auth issues --- spec/spec_db.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/spec_db.rb b/spec/spec_db.rb index d65d254a5..275a1b485 100644 --- a/spec/spec_db.rb +++ b/spec/spec_db.rb @@ -3,9 +3,9 @@ class SpecDb TEST_DB_NAME="jam_ruby_test" def self.recreate_database - conn = PG::Connection.open("dbname=postgres user=postgres password=postgres") + conn = PG::Connection.open("dbname=postgres user=postgres password=postgres host=localhost") conn.exec("DROP DATABASE IF EXISTS #{TEST_DB_NAME}") conn.exec("CREATE DATABASE #{TEST_DB_NAME}") - JamDb::Migrator.new.migrate(:dbname => TEST_DB_NAME, :user => "postgres", :password => "postgres") + JamDb::Migrator.new.migrate(:dbname => TEST_DB_NAME, :user => "postgres", :password => "postgres", :host => "localhost") end end From ee111930d24141924b79716dc4bd70a88aeb7803 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Thu, 4 Oct 2012 21:39:03 -0500 Subject: [PATCH 021/274] * updating message_factory to use new jam-pb naming --- lib/jam_ruby/message_factory.rb | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/jam_ruby/message_factory.rb b/lib/jam_ruby/message_factory.rb index 892886440..cae9f8897 100644 --- a/lib/jam_ruby/message_factory.rb +++ b/lib/jam_ruby/message_factory.rb @@ -41,28 +41,28 @@ module JamRuby return Jampb::ClientMessage.new(:type => ClientMessage::Type::LOGIN_ACK, :target => CLIENT_TARGET, :login_ack => login_ack) end - # create a jam session login message + # create a music session login message def login_music_session(music_session) login_music_session = Jampb::LoginMusicSession.new(:music_session => music_session) return Jampb::ClientMessage.new(:type => ClientMessage::Type::LOGIN_MUSIC_SESSION, :target => SERVER_TARGET, :login_music_session => login_music_session) end - # create a jam session login message ack (success or on failure) + # create a music session login message ack (success or on failure) def login_music_session_ack(error, error_reason) login_music_session_ack = Jampb::LoginMusicSessionAck.new(:error => error, :error_reason => error_reason) - return Jampb::ClientMessage.new(:type => ClientMessage::Type::LOGIN_JAM_SESSION_ACK, :target => CLIENT_TARGET, :login_music_session_ack => login_music_session_ack) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::LOGIN_MUSIC_SESSION_ACK, :target => CLIENT_TARGET, :login_music_session_ack => login_music_session_ack) end - # create a jam session 'leave session' message + # create a music session 'leave session' message def leave_music_session(music_session) leave_music_session = Jampb::LeaveMusicSession.new(:music_session => music_session) - return Jampb::ClientMessage.new(:type => ClientMessage::Type::LEAVE_JAM_SESSION, :target => SERVER_TARGET, :leave_music_session => leave_music_session) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::LEAVE_MUSIC_SESSION, :target => SERVER_TARGET, :leave_music_session => leave_music_session) end - # create a jam session leave message ack (success or on failure) + # create a music session leave message ack (success or on failure) def leave_music_session_ack(error, error_reason) leave_music_session_ack = Jampb::LeaveMusicSessionAck.new(:error => error, :error_reason => error_reason) - return Jampb::ClientMessage.new(:type => ClientMessage::Type::LEAVE_JAM_SESSION_ACK, :target => CLIENT_TARGET, :leave_music_session_ack => leave_music_session_ack) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::LEAVE_MUSIC_SESSION_ACK, :target => CLIENT_TARGET, :leave_music_session_ack => leave_music_session_ack) end # create a server error @@ -80,7 +80,7 @@ module JamRuby # create a user-joined session messag def user_joined_music_session(user_id, username) joined = Jampb::UserJoinedMusicSession.new(:user_id => user_id, :username => username) - return Jampb::ClientMessage.new(:type => ClientMessage::Type::USER_JOINED_JAM_SESSION, :target => CLIENT_TARGET, :user_joined_music_session => joined) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::USER_JOINED_MUSIC_SESSION, :target => CLIENT_TARGET, :user_joined_music_session => joined) end # create a test message to send in session @@ -105,7 +105,7 @@ module JamRuby return msg.target == MessageFactory::CLIENT_TARGET end - # is this message directed to a (jam) session? + # is this message directed to a (music) session? def session_directed? msg return msg.target.start_with? MessageFactory::SESSION_TARGET_PREFIX end From 4b15f102db42e746e3689e65b12989b6fac95017 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Sun, 7 Oct 2012 00:53:54 -0400 Subject: [PATCH 022/274] added unit test --- spec/jam_ruby/models/connection_spec.rb | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 spec/jam_ruby/models/connection_spec.rb diff --git a/spec/jam_ruby/models/connection_spec.rb b/spec/jam_ruby/models/connection_spec.rb new file mode 100644 index 000000000..6cac8147d --- /dev/null +++ b/spec/jam_ruby/models/connection_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +describe Connection do + + it 'can grant access to valid user' do + + user1 = FactoryGirl.create(:user) # in the jam session + user2 = FactoryGirl.create(:user) # in the jam session + user3 = FactoryGirl.create(:user) # not in the jam session + + jam_session = FactoryGirl.create(:jam_session, :creator => user1) + + jam_session_member1 = FactoryGirl.create(:jam_session_member, :user => user1, :jam_session => jam_session) + jam_session_member2 = FactoryGirl.create(:jam_session_member, :user => user2, :jam_session => jam_session) + + jam_session.access?(user1).should == true + jam_session.access?(user2).should == true + jam_session.access?(user3).should == false + end +end \ No newline at end of file From 5ba13fa893c5b52abd6ad5d6d5e3d92d71f7ac4d Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Sun, 7 Oct 2012 14:02:26 -0400 Subject: [PATCH 023/274] fixed issues found in unit tests --- lib/jam_ruby/models/connection.rb | 9 +-------- lib/jam_ruby/models/friendship.rb | 6 +++--- lib/jam_ruby/models/user.rb | 4 ++++ spec/jam_ruby/models/connection_spec.rb | 15 --------------- 4 files changed, 8 insertions(+), 26 deletions(-) diff --git a/lib/jam_ruby/models/connection.rb b/lib/jam_ruby/models/connection.rb index 49ffaed14..7d6f4382c 100644 --- a/lib/jam_ruby/models/connection.rb +++ b/lib/jam_ruby/models/connection.rb @@ -1,15 +1,8 @@ module JamRuby class Connection < ActiveRecord::Base - attr_accessor :user_id, :client_id - - self.primary_key = 'id' + self.primary_key = 'id' belongs_to :user, :class_name => "JamRuby::User" - def initialize(user_id, client_id) - @user_id = user_id - @client_id = client_id - end - end end \ No newline at end of file diff --git a/lib/jam_ruby/models/friendship.rb b/lib/jam_ruby/models/friendship.rb index 1af8ca24c..021014ca4 100644 --- a/lib/jam_ruby/models/friendship.rb +++ b/lib/jam_ruby/models/friendship.rb @@ -1,10 +1,10 @@ module JamRuby class Friendship < ActiveRecord::Base - self.primary_key = 'id' + self.primary_key = 'id' - belongs_to :user, :class_name => "JamRuby::User" - belongs_to :friend, :class_name => "JamRuby::User" + belongs_to :user + belongs_to :friend, :class_name => "JamRuby::User" end end \ No newline at end of file diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 5ac99c726..b9eca6713 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -39,6 +39,10 @@ module JamRuby updating_password || new_record? end + def friends?(user) + return self.friends.exists?(user) + end + def to_s return email unless email.nil? return name unless name.nil? diff --git a/spec/jam_ruby/models/connection_spec.rb b/spec/jam_ruby/models/connection_spec.rb index 6cac8147d..8b66ffc16 100644 --- a/spec/jam_ruby/models/connection_spec.rb +++ b/spec/jam_ruby/models/connection_spec.rb @@ -2,19 +2,4 @@ require 'spec_helper' describe Connection do - it 'can grant access to valid user' do - - user1 = FactoryGirl.create(:user) # in the jam session - user2 = FactoryGirl.create(:user) # in the jam session - user3 = FactoryGirl.create(:user) # not in the jam session - - jam_session = FactoryGirl.create(:jam_session, :creator => user1) - - jam_session_member1 = FactoryGirl.create(:jam_session_member, :user => user1, :jam_session => jam_session) - jam_session_member2 = FactoryGirl.create(:jam_session_member, :user => user2, :jam_session => jam_session) - - jam_session.access?(user1).should == true - jam_session.access?(user2).should == true - jam_session.access?(user3).should == false - end end \ No newline at end of file From 754194f4f4cb7e69b7bb9e875a5d4169dc5dd525 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Wed, 10 Oct 2012 22:33:54 -0500 Subject: [PATCH 024/274] * adding client_id for p2p messaging --- lib/jam_ruby/message_factory.rb | 80 +++++++++++++-------- lib/jam_ruby/models/music_session_client.rb | 10 ++- spec/factories.rb | 1 - 3 files changed, 59 insertions(+), 32 deletions(-) diff --git a/lib/jam_ruby/message_factory.rb b/lib/jam_ruby/message_factory.rb index cae9f8897..a6e1d8468 100644 --- a/lib/jam_ruby/message_factory.rb +++ b/lib/jam_ruby/message_factory.rb @@ -6,15 +6,16 @@ module JamRuby SERVER_TARGET = "server" SESSION_TARGET_PREFIX = "session:" USER_TARGET_PREFIX = "user:" + CLIENT_TARGET_PREFIX = "client:" + - def initialize() @type_values = {} - + Jampb::ClientMessage::Type.constants.each do |constant| - @type_values[Jampb::ClientMessage::Type.const_get(constant)] = constant - end + @type_values[Jampb::ClientMessage::Type.const_get(constant)] = constant + end end @@ -24,103 +25,122 @@ module JamRuby end # create a login message using user/pass - def login_with_user_pass(username, password) - login = Jampb::Login.new(:username => username, :password => password) - return Jampb::ClientMessage.new(:type => ClientMessage::Type::LOGIN, :target => SERVER_TARGET, :login => login) + def login_with_user_pass(username, password, options = {}) + login = Jampb::Login.new(:username => username, :password => password, :client_id => options[:client_id]) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::LOGIN, :route_to => SERVER_TARGET, :login => login) end - # create a login message using token (a cookie or similiar) - def login_with_token(token) - login = Jampb::Login.new(:token => token) - return Jampb::ClientMessage.new(:type => ClientMessage::Type::LOGIN, :target => SERVER_TARGET, :login => login) + # create a login message using token (a cookie or similar) + def login_with_token(token, options = {}) + login = Jampb::Login.new(:token => token, :client_id => options[:client_id]) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::LOGIN, :route_to => SERVER_TARGET, :login => login) end # create a login ack (login was successful) - def login_ack(public_ip) - login_ack = Jampb::LoginAck.new(:public_ip => public_ip) - return Jampb::ClientMessage.new(:type => ClientMessage::Type::LOGIN_ACK, :target => CLIENT_TARGET, :login_ack => login_ack) + def login_ack(public_ip, client_id, token) + login_ack = Jampb::LoginAck.new(:public_ip => public_ip, :client_id => client_id, :token => token) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::LOGIN_ACK, :route_to => CLIENT_TARGET, :login_ack => login_ack) end # create a music session login message def login_music_session(music_session) login_music_session = Jampb::LoginMusicSession.new(:music_session => music_session) - return Jampb::ClientMessage.new(:type => ClientMessage::Type::LOGIN_MUSIC_SESSION, :target => SERVER_TARGET, :login_music_session => login_music_session) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::LOGIN_MUSIC_SESSION, :route_to => SERVER_TARGET, :login_music_session => login_music_session) end # create a music session login message ack (success or on failure) def login_music_session_ack(error, error_reason) login_music_session_ack = Jampb::LoginMusicSessionAck.new(:error => error, :error_reason => error_reason) - return Jampb::ClientMessage.new(:type => ClientMessage::Type::LOGIN_MUSIC_SESSION_ACK, :target => CLIENT_TARGET, :login_music_session_ack => login_music_session_ack) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::LOGIN_MUSIC_SESSION_ACK, :route_to => CLIENT_TARGET, :login_music_session_ack => login_music_session_ack) end # create a music session 'leave session' message def leave_music_session(music_session) leave_music_session = Jampb::LeaveMusicSession.new(:music_session => music_session) - return Jampb::ClientMessage.new(:type => ClientMessage::Type::LEAVE_MUSIC_SESSION, :target => SERVER_TARGET, :leave_music_session => leave_music_session) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::LEAVE_MUSIC_SESSION, :route_to => SERVER_TARGET, :leave_music_session => leave_music_session) end # create a music session leave message ack (success or on failure) def leave_music_session_ack(error, error_reason) leave_music_session_ack = Jampb::LeaveMusicSessionAck.new(:error => error, :error_reason => error_reason) - return Jampb::ClientMessage.new(:type => ClientMessage::Type::LEAVE_MUSIC_SESSION_ACK, :target => CLIENT_TARGET, :leave_music_session_ack => leave_music_session_ack) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::LEAVE_MUSIC_SESSION_ACK, :route_to => CLIENT_TARGET, :leave_music_session_ack => leave_music_session_ack) end # create a server error def server_generic_error(error_msg) error = Jampb::ServerGenericError.new(:error_msg => error_msg) - return Jampb::ClientMessage.new(:type => ClientMessage::Type::SERVER_GENERIC_ERROR, :target => CLIENT_TARGET, :server_generic_error => error) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::SERVER_GENERIC_ERROR, :route_to => CLIENT_TARGET, :server_generic_error => error) end # create a server rejection error def server_rejection_error(error_msg) error = Jampb::ServerRejectionError.new(:error_msg => error_msg) - return Jampb::ClientMessage.new(:type => ClientMessage::Type::SERVER_REJECTION_ERROR, :target => CLIENT_TARGET, :server_rejection_error => error) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::SERVER_REJECTION_ERROR, :route_to => CLIENT_TARGET, :server_rejection_error => error) end - # create a user-joined session messag + # create a server rejection error + def server_permission_error(original_message_id, error_msg) + error = Jampb::ServerPermissionError.new(:error_msg => error_msg) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::SERVER_PERMISSION_ERROR, :route_to => CLIENT_TARGET, :server_permission_error => error, :in_reply_to => original_message_id) + end + + # create a user-joined session message def user_joined_music_session(user_id, username) joined = Jampb::UserJoinedMusicSession.new(:user_id => user_id, :username => username) - return Jampb::ClientMessage.new(:type => ClientMessage::Type::USER_JOINED_MUSIC_SESSION, :target => CLIENT_TARGET, :user_joined_music_session => joined) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::USER_JOINED_MUSIC_SESSION, :route_to => CLIENT_TARGET, :user_joined_music_session => joined) end # create a test message to send in session def test_session_message(session_id, msg) test = Jampb::TestSessionMessage.new(:msg => msg) - return Jampb::ClientMessage.new(:type => ClientMessage::Type::TEST_SESSION_MESSAGE, :target => SESSION_TARGET_PREFIX + session_id, :test_session_message => test) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::TEST_SESSION_MESSAGE, :route_to => SESSION_TARGET_PREFIX + session_id, :test_session_message => test) end + + # send a request to do a ping + def ping_request(client_id, from) + ping_request = Jampb::PingRequest.new() + return Jampb::ClientMessage.new(:type => ClientMessage::Type::PING_REQUEST, :route_to => CLIENT_TARGET_PREFIX + client_id, :from => from, :ping_request => ping_request) + end + + # respond to a ping_request with an ack + def ping_ack(client_id, from) + ping_ack = Jampb::PingAck.new() + return Jampb::ClientMessage.new(:type => ClientMessage::Type::PING_ACK, :route_to => CLIENT_TARGET_PREFIX + client_id, :from => from, :ping_ack => ping_ack) + end + # create a heartbeat def heartbeat() heartbeat = Jampb::Heartbeat.new - return Jampb::ClientMessage.new(:type => ClientMessage::Type::HEARTBEAT, :target => SERVER_TARGET, :heartbeat => heartbeat) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::HEARTBEAT, :route_to => SERVER_TARGET, :heartbeat => heartbeat) end # is this message directed to the server? def server_directed? msg - return msg.target == MessageFactory::SERVER_TARGET + return msg.route_to == MessageFactory::SERVER_TARGET end # is this message directed to the client? def client_directed? msg - return msg.target == MessageFactory::CLIENT_TARGET + return msg.route_to.start_with? MessageFactory::CLIENT_TARGET_PREFIX end # is this message directed to a (music) session? def session_directed? msg - return msg.target.start_with? MessageFactory::SESSION_TARGET_PREFIX + return msg.route_to.start_with? MessageFactory::SESSION_TARGET_PREFIX end # is this message directed to a user? def user_directed? msg - return msg.target.start_with? MessageFactory::USER_TARGET_PREFIX + return msg.route_to.start_with? MessageFactory::USER_TARGET_PREFIX end def extract_session(msg) - return msg.target[MessageFactory::SESSION_TARGET_PREFIX..-1] + return msg.route_to[MessageFactory::SESSION_TARGET_PREFIX..-1] end def get_message_type msg - return @type_values[msg.type] + return @type_values[msg.type] end end end diff --git a/lib/jam_ruby/models/music_session_client.rb b/lib/jam_ruby/models/music_session_client.rb index 15519afdd..5099cc14f 100644 --- a/lib/jam_ruby/models/music_session_client.rb +++ b/lib/jam_ruby/models/music_session_client.rb @@ -1,3 +1,5 @@ +# called 'participant' currently in the REST APIs + module JamRuby class MusicSessionClient < ActiveRecord::Base self.primary_key = 'id' @@ -5,10 +7,16 @@ module JamRuby belongs_to :user, :class_name => "JamRuby::User" belongs_to :music_session, :class_name => "JamRuby::MusicSession" + validates :client_id, :presence => true + def to_s return "#{self.user.to_s}:#{self.ip_address}" end - + # decides if a given user can access this client with p2p messaging + # the answer is yes if the user is in the same music session + def access_p2p?(user) + return self.music_session.users.exists?(user) + end end end diff --git a/spec/factories.rb b/spec/factories.rb index dc1bcc916..4250c349a 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -15,6 +15,5 @@ FactoryGirl.define do end factory :music_session_client, :class => JamRuby::MusicSessionClient do - end end \ No newline at end of file From ef968fb29dd6e467131eebc2bcd78a28d9d3ed9c Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sat, 13 Oct 2012 17:51:08 -0500 Subject: [PATCH 025/274] * fixing tests for new client_id field --- lib/jam_ruby/message_factory.rb | 11 ++++++++++- spec/jam_ruby/models/music_session_spec.rb | 4 ++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/jam_ruby/message_factory.rb b/lib/jam_ruby/message_factory.rb index 11ac5ff0b..96912edee 100644 --- a/lib/jam_ruby/message_factory.rb +++ b/lib/jam_ruby/message_factory.rb @@ -96,6 +96,7 @@ return Jampb::ClientMessage.new(:type => ClientMessage::Type::TEST_SESSION_MESSAGE, :route_to => SESSION_TARGET_PREFIX + session_id, :test_session_message => test) end + ############## P2P CLIENT MESSAGES ################# # send a request to do a ping def ping_request(client_id, from) @@ -109,7 +110,15 @@ return Jampb::ClientMessage.new(:type => ClientMessage::Type::PING_ACK, :route_to => CLIENT_TARGET_PREFIX + client_id, :from => from, :ping_ack => ping_ack) end - # create a heartbeat + # create a test message to send in session + def test_client_message(client_id, from, msg) + test = Jampb::TestClientMessage.new(:msg => msg) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::TEST_CLIENT_MESSAGE, :route_to => CLIENT_TARGET_PREFIX + client_id, :from => from, :test_client_message => test) + end + + #################################################### + + # create a heartbeat def heartbeat() heartbeat = Jampb::Heartbeat.new return Jampb::ClientMessage.new(:type => ClientMessage::Type::HEARTBEAT, :route_to => SERVER_TARGET, :heartbeat => heartbeat) diff --git a/spec/jam_ruby/models/music_session_spec.rb b/spec/jam_ruby/models/music_session_spec.rb index 5fbcdbbb7..ad93f9558 100644 --- a/spec/jam_ruby/models/music_session_spec.rb +++ b/spec/jam_ruby/models/music_session_spec.rb @@ -10,8 +10,8 @@ describe MusicSession do music_session = FactoryGirl.create(:music_session, :creator => user1) - music_session_member1 = FactoryGirl.create(:music_session_client, :user => user1, :music_session => music_session, :ip_address => "1.1.1.1") - music_session_member2 = FactoryGirl.create(:music_session_client, :user => user2, :music_session => music_session, :ip_address => "2.2.2.2") + music_session_member1 = FactoryGirl.create(:music_session_client, :user => user1, :music_session => music_session, :ip_address => "1.1.1.1", :client_id => "1") + music_session_member2 = FactoryGirl.create(:music_session_client, :user => user2, :music_session => music_session, :ip_address => "2.2.2.2", :client_id => "2") music_session.access?(user1).should == true music_session.access?(user2).should == true From cccae35a979e7563040bf5f90951d9fdaa3eaffa Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Sat, 13 Oct 2012 22:18:20 -0400 Subject: [PATCH 026/274] friend API development --- lib/jam_ruby/models/friend_request.rb | 10 ++++++++++ lib/jam_ruby/models/user.rb | 9 ++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 lib/jam_ruby/models/friend_request.rb diff --git a/lib/jam_ruby/models/friend_request.rb b/lib/jam_ruby/models/friend_request.rb new file mode 100644 index 000000000..3a62556f1 --- /dev/null +++ b/lib/jam_ruby/models/friend_request.rb @@ -0,0 +1,10 @@ +module JamRuby + class FriendRequest < ActiveRecord::Base + + self.primary_key = 'id' + + belongs_to :user + belongs_to :friend, :class_name => "JamRuby::User" + + end +end \ No newline at end of file diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index b9eca6713..d0fb588b6 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -2,7 +2,7 @@ module JamRuby class User < ActiveRecord::Base attr_accessible :name, :email, :password, :password_confirmation - attr_accessor :updating_password + attr_accessor :updating_password, :online self.primary_key = 'id' @@ -11,6 +11,8 @@ module JamRuby has_many :connections, :class_name => "JamRuby::Connection" + has_many :friend_requests, :class_name => "JamRuby::FriendRequest" + has_many :friendships has_many :friends, :through => :friendships has_many :inverse_friendships, :class_name => "JamRuby::Friendship", :foreign_key => "friend_id" @@ -35,6 +37,11 @@ module JamRuby #validates :password_confirmation, presence: true validates_confirmation_of :password, :if => :should_validate_password? + def online + @online = !self.connections.nil? && self.connections.size > 0 + return @online + end + def should_validate_password? updating_password || new_record? end From daa042630d7673ddd9c76cb42208322640508825 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Sun, 14 Oct 2012 00:21:52 -0400 Subject: [PATCH 027/274] formatting --- lib/jam_ruby/models/friend_request.rb | 10 +++++----- lib/jam_ruby/models/friendship.rb | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/jam_ruby/models/friend_request.rb b/lib/jam_ruby/models/friend_request.rb index 3a62556f1..b8fefe9df 100644 --- a/lib/jam_ruby/models/friend_request.rb +++ b/lib/jam_ruby/models/friend_request.rb @@ -1,10 +1,10 @@ module JamRuby - class FriendRequest < ActiveRecord::Base + class FriendRequest < ActiveRecord::Base - self.primary_key = 'id' + self.primary_key = 'id' - belongs_to :user - belongs_to :friend, :class_name => "JamRuby::User" + belongs_to :user + belongs_to :friend, :class_name => "JamRuby::User" - end + end end \ No newline at end of file diff --git a/lib/jam_ruby/models/friendship.rb b/lib/jam_ruby/models/friendship.rb index 021014ca4..d3ab45e27 100644 --- a/lib/jam_ruby/models/friendship.rb +++ b/lib/jam_ruby/models/friendship.rb @@ -1,10 +1,10 @@ module JamRuby - class Friendship < ActiveRecord::Base + class Friendship < ActiveRecord::Base - self.primary_key = 'id' + self.primary_key = 'id' - belongs_to :user - belongs_to :friend, :class_name => "JamRuby::User" + belongs_to :user + belongs_to :friend, :class_name => "JamRuby::User" - end + end end \ No newline at end of file From d7fb9850e88d5cb50ad4e051fab8ee22ceb374b1 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Sun, 14 Oct 2012 22:02:55 -0400 Subject: [PATCH 028/274] include friend_request in jam_ruby --- lib/jam_ruby.rb | 1 + lib/jam_ruby/models/friend_request.rb | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index 94d641d67..14fce4ef6 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -11,6 +11,7 @@ require "jam_ruby/models/band" require "jam_ruby/models/connection" require "jam_ruby/models/friendship" require "jam_ruby/models/music_session" +require "jam_ruby/models/friend_request" include Jampb diff --git a/lib/jam_ruby/models/friend_request.rb b/lib/jam_ruby/models/friend_request.rb index b8fefe9df..29464b979 100644 --- a/lib/jam_ruby/models/friend_request.rb +++ b/lib/jam_ruby/models/friend_request.rb @@ -3,8 +3,15 @@ module JamRuby self.primary_key = 'id' - belongs_to :user + belongs_to :user, :class_name => "JamRuby::User" belongs_to :friend, :class_name => "JamRuby::User" + validates :user_id, :presence => true + validates :friend_id, :presence => true + + def to_s + return "#{self.user.to_s}:#{self.friend.to_s}" + end + end end \ No newline at end of file From c407fcfd8699f06aa194081861d4df798acd240f Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Mon, 15 Oct 2012 00:14:40 -0400 Subject: [PATCH 029/274] model updates --- lib/jam_ruby/models/friend_request.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/jam_ruby/models/friend_request.rb b/lib/jam_ruby/models/friend_request.rb index 29464b979..c9dd377f5 100644 --- a/lib/jam_ruby/models/friend_request.rb +++ b/lib/jam_ruby/models/friend_request.rb @@ -9,9 +9,5 @@ module JamRuby validates :user_id, :presence => true validates :friend_id, :presence => true - def to_s - return "#{self.user.to_s}:#{self.friend.to_s}" - end - end end \ No newline at end of file From 6128621ce6c8b8de14afe0b5ce93c9b169003b54 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Mon, 15 Oct 2012 21:09:57 -0500 Subject: [PATCH 030/274] * VRFS-19 - jam-ruby looks to have client messaging integrated, but need to see if it works when used in jam-web --- Gemfile | 1 + lib/jam_ruby.rb | 1 + lib/jam_ruby/connection_cleaner.rb | 6 +++ lib/jam_ruby/errors/permission_error.rb | 3 ++ lib/jam_ruby/mq_router.rb | 58 +++++++++++++++++++++++++ spec/jam_ruby/mq_router_spec.rb | 48 ++++++++++++++++++++ 6 files changed, 117 insertions(+) create mode 100644 lib/jam_ruby/connection_cleaner.rb create mode 100644 lib/jam_ruby/errors/permission_error.rb create mode 100644 lib/jam_ruby/mq_router.rb create mode 100644 spec/jam_ruby/mq_router_spec.rb diff --git a/Gemfile b/Gemfile index 061aa233e..0dd3006c9 100644 --- a/Gemfile +++ b/Gemfile @@ -11,6 +11,7 @@ gem 'activerecord', '3.2.7' gem 'uuidtools', '2.1.2' gem 'bcrypt-ruby', '3.0.1' gem 'ruby-protocol-buffers', '1.2.2' +gem 'eventmachine' group :test do gem 'jam_db', :path=> "#{workspace}/jam-db/target/ruby_package" diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index 94d641d67..ad68d6f08 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -2,6 +2,7 @@ require "pg" require "active_record" require "jampb" require 'uuidtools' +require "jam_ruby/mq_router" require "jam_ruby/version" require "jam_ruby/message_factory" require "jam_ruby/models/music_session_client" diff --git a/lib/jam_ruby/connection_cleaner.rb b/lib/jam_ruby/connection_cleaner.rb new file mode 100644 index 000000000..21522929d --- /dev/null +++ b/lib/jam_ruby/connection_cleaner.rb @@ -0,0 +1,6 @@ +class ConnectionCleaner + + def clean(connection, topic) + + end +end \ No newline at end of file diff --git a/lib/jam_ruby/errors/permission_error.rb b/lib/jam_ruby/errors/permission_error.rb new file mode 100644 index 000000000..80c4b1ff6 --- /dev/null +++ b/lib/jam_ruby/errors/permission_error.rb @@ -0,0 +1,3 @@ +class PermissionError < Exception + +end \ No newline at end of file diff --git a/lib/jam_ruby/mq_router.rb b/lib/jam_ruby/mq_router.rb new file mode 100644 index 000000000..b0f86b07d --- /dev/null +++ b/lib/jam_ruby/mq_router.rb @@ -0,0 +1,58 @@ +require 'eventmachine' + +class MQRouter + + # monostate pattern: + # You can initialize MQRouter instances as you want, + # but ultimately there are internal static state variables to represent global MQ exchange connections + + class << self + attr_accessor :client_exchange, :user_exchange + @@log = Logging.logger[MQRouter] + end + + def access_music_session(music_session, user) + + if music_session.nil? + raise ArgumentError, 'specified session not found' + end + + if !music_session.access? user + raise PermissionError, 'not allowed to join the specified session' + end + + return music_session + end + + # sends a message to a session on behalf of a user + # if this is originating in the context of a client, it should be specified as :client_id => "value" + # client_msg should be a well-structure message (jam-pb message) + def user_publish_to_session(music_session, user, client_msg, sender = {:client_id => "" }) + access_music_session(music_session, user) + + # gather up client_ids in the session + client_ids = music_session.music_session_clients.map {|client| client.client_id }.reject {|client_id| client_id == sender[:client_id] } + + publish_to_session(music_session.id, client_ids, client_msg.to_s, sender) + end + + + # sends a message to a session with no checking of permissions + # 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] + + # iterate over each person in the session, and send a p2p message + client_ids.each do |client_id| + + @@log.debug "publishing to session:client #{music_session_id}:#{client_id} from client_id #{sender_client_id}" + # put it on the topic exchange for clients + self.class.client_exchange.publish(client_msg, :routing_key => "clients.#{music_session_id}") + end + end + end + + +end \ No newline at end of file diff --git a/spec/jam_ruby/mq_router_spec.rb b/spec/jam_ruby/mq_router_spec.rb new file mode 100644 index 000000000..76394847f --- /dev/null +++ b/spec/jam_ruby/mq_router_spec.rb @@ -0,0 +1,48 @@ +require 'spec_helper' + +describe MQRouter do + + before do + @mq_router = MQRouter.new() + end + + it "user_publish_to_session works (but faking MQ)" do + + user1 = FactoryGirl.create(:user) # in the jam session + user2 = FactoryGirl.create(:user) # in the jam session + + music_session = FactoryGirl.create(:music_session, :creator => user1) + + music_session_member1 = FactoryGirl.create(:music_session_client, :user => user1, :music_session => music_session, :ip_address => "1.1.1.1", :client_id => "1") + music_session_member2 = FactoryGirl.create(:music_session_client, :user => user2, :music_session => music_session, :ip_address => "2.2.2.2", :client_id => "2") + + @mq_router.should_receive(:publish_to_session).with(music_session.id, [music_session_member2.client_id], "a message", :client_id => music_session_member1.client_id) + + @mq_router.user_publish_to_session(music_session, user1, "a message" ,:client_id => music_session_member1.client_id) + end + + it "user_publish_to_session works (checking exchange callbacks)" do + + user1 = FactoryGirl.create(:user) # in the jam session + user2 = FactoryGirl.create(:user) # in the jam session + + music_session = FactoryGirl.create(:music_session, :creator => user1) + + music_session_member1 = FactoryGirl.create(:music_session_client, :user => user1, :music_session => music_session, :ip_address => "1.1.1.1", :client_id => "1") + music_session_member2 = FactoryGirl.create(:music_session_client, :user => user2, :music_session => music_session, :ip_address => "2.2.2.2", :client_id => "2") + + EM.run do + + # mock up exchange + MQRouter.client_exchange = double("client_exchange") + + MQRouter.client_exchange.should_receive(:publish).with("a message", :routing_key => "clients.#{music_session.id}") + + @mq_router.user_publish_to_session(music_session, user1, "a message", :client_id => music_session_member1.client_id) + + EM.stop + + end + end + +end From b829a0cdc381ed077f21762b4fcd3bf443605e10 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Tue, 16 Oct 2012 07:15:09 -0500 Subject: [PATCH 031/274] * VRFS-19: adding amqp and logging gem to jam-ruby --- Gemfile | 1 + lib/jam_ruby.rb | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 0dd3006c9..4bfca7659 100644 --- a/Gemfile +++ b/Gemfile @@ -12,6 +12,7 @@ gem 'uuidtools', '2.1.2' gem 'bcrypt-ruby', '3.0.1' gem 'ruby-protocol-buffers', '1.2.2' gem 'eventmachine' +gem 'amqp' group :test do gem 'jam_db', :path=> "#{workspace}/jam-db/target/ruby_package" diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index ad68d6f08..419528459 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -1,7 +1,8 @@ require "pg" require "active_record" require "jampb" -require 'uuidtools' +require "uuidtools" +require "logging" require "jam_ruby/mq_router" require "jam_ruby/version" require "jam_ruby/message_factory" From fd9770f0f36cc19c06e86511222a8350ed1adf00 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Tue, 16 Oct 2012 22:48:10 -0500 Subject: [PATCH 032/274] * VRFS-19; finishing touches on making session messaging work via jam-web --- lib/jam_ruby/mq_router.rb | 6 +++--- spec/jam_ruby/mq_router_spec.rb | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/jam_ruby/mq_router.rb b/lib/jam_ruby/mq_router.rb index b0f86b07d..91e808cb4 100644 --- a/lib/jam_ruby/mq_router.rb +++ b/lib/jam_ruby/mq_router.rb @@ -47,9 +47,9 @@ class MQRouter # iterate over each person in the session, and send a p2p message client_ids.each do |client_id| - @@log.debug "publishing to session:client #{music_session_id}:#{client_id} from client_id #{sender_client_id}" - # put it on the topic exchange for clients - self.class.client_exchange.publish(client_msg, :routing_key => "clients.#{music_session_id}") + @@log.debug "publishing to session:#{music_session_id} client:#{client_id} from client:#{sender_client_id}" + # put it on the topic exchange3 for clients + self.class.client_exchange.publish(client_msg, :routing_key => "client.#{client_id}") end end end diff --git a/spec/jam_ruby/mq_router_spec.rb b/spec/jam_ruby/mq_router_spec.rb index 76394847f..37bf54288 100644 --- a/spec/jam_ruby/mq_router_spec.rb +++ b/spec/jam_ruby/mq_router_spec.rb @@ -36,7 +36,7 @@ describe MQRouter do # mock up exchange MQRouter.client_exchange = double("client_exchange") - MQRouter.client_exchange.should_receive(:publish).with("a message", :routing_key => "clients.#{music_session.id}") + MQRouter.client_exchange.should_receive(:publish).with("a message", :routing_key => "client.#{music_session_member2.client_id}") @mq_router.user_publish_to_session(music_session, user1, "a message", :client_id => music_session_member1.client_id) From e881b19245eb3ff376f1acd9995868b3bf6d4eff Mon Sep 17 00:00:00 2001 From: Seth Call Date: Wed, 17 Oct 2012 19:39:43 -0500 Subject: [PATCH 033/274] * adding in-progress connection cleaner class --- lib/jam_ruby/connection_cleaner.rb | 45 +++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/lib/jam_ruby/connection_cleaner.rb b/lib/jam_ruby/connection_cleaner.rb index 21522929d..159aea815 100644 --- a/lib/jam_ruby/connection_cleaner.rb +++ b/lib/jam_ruby/connection_cleaner.rb @@ -1,6 +1,49 @@ class ConnectionCleaner - def clean(connection, topic) + def initialize(db_options) + @mq_router = MQRouter.new + @pg_conn = PG::Connection.new(db_options) # just give a dedicated connection to this process + + unless PG.threadsafe + raise Exception "a non-threadsafe build of libpq is present." + end + end + + def remove_stale_connections() + @pg_conn.transaction do |conn| + + end + end + + + # once a connection is known gone (whether timeout or because a TCP connection is observed lost) + # this code is responsible for all cleanup logic associated with a connection going away + def remove_connection(client_id) + + @pg_conn.transaction do |conn| + + + conn.exec("DELETE FROM connections WHERE client_id = $1 RETURNING user_id", [client_id]) do |result| + if result.cmd_tuples == 0 + # the client is already gone from the database... do nothing + + elsif result.cmd_tuples == 1 + + # if we did delete a row, check and see if any more connections for that user exist + # if we are down to zero, send out user gone message + conn.exec("SELECT count(user_id) FROM connections where user_id = $1", [result[0][0]]) do | result| + @mq_router.server_publish_to_user() + end + else + raise Exception 'uniqueness constraint has been lost on client_id' + end + + end + + end end + + + end \ No newline at end of file From 2fe02a6c79c187648c4c2f17e9eea26ea1591dcf Mon Sep 17 00:00:00 2001 From: Seth Call Date: Wed, 17 Oct 2012 21:00:52 -0500 Subject: [PATCH 034/274] * syncing wip on connection cleaning --- lib/jam_ruby/connection_cleaner.rb | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/lib/jam_ruby/connection_cleaner.rb b/lib/jam_ruby/connection_cleaner.rb index 159aea815..2bee3b57b 100644 --- a/lib/jam_ruby/connection_cleaner.rb +++ b/lib/jam_ruby/connection_cleaner.rb @@ -22,22 +22,25 @@ class ConnectionCleaner @pg_conn.transaction do |conn| + # FOR UPDATE - lock the connection and related jam_sessions in question + conn.exec("SELECT user_id FROM connections LEFT OUTER JOIN music_sessions WHERE client_id = $1 ON connections.music_session_id = music_sessions.id FOR UPDATE", [client_id]) do |result| - conn.exec("DELETE FROM connections WHERE client_id = $1 RETURNING user_id", [client_id]) do |result| - if result.cmd_tuples == 0 - # the client is already gone from the database... do nothing + conn.exec("DELETE FROM connections WHERE client_id = $1", [client_id]) do |result| - elsif result.cmd_tuples == 1 + if result.cmd_tuples == 0 + # the client is already gone from the database... do nothing - # if we did delete a row, check and see if any more connections for that user exist - # if we are down to zero, send out user gone message - conn.exec("SELECT count(user_id) FROM connections where user_id = $1", [result[0][0]]) do | result| - @mq_router.server_publish_to_user() + elsif result.cmd_tuples == 1 + + # if we did delete a row, check and see if any more connections for that user exist + # if we are down to zero, send out user gone message + conn.exec("SELECT count(user_id) FROM connections where user_id = $1", [result[0][0]]) do |result| + @mq_router.server_publish_to_user() + end + else + raise Exception 'uniqueness constraint has been lost on client_id' end - else - raise Exception 'uniqueness constraint has been lost on client_id' end - end end @@ -45,5 +48,4 @@ class ConnectionCleaner end - end \ No newline at end of file From d8dc08bef66effa7ee84e8364b205a6b626f4631 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sat, 20 Oct 2012 20:55:49 -0500 Subject: [PATCH 035/274] * VRFS-18; creating ConnectionManager which is responsible for managing data within the connections table --- Gemfile | 1 + lib/jam_ruby.rb | 2 +- lib/jam_ruby/connection_cleaner.rb | 51 ------- lib/jam_ruby/connection_manager.rb | 103 ++++++++++++++ lib/jam_ruby/message_factory.rb | 2 +- lib/jam_ruby/models/connection.rb | 9 +- lib/jam_ruby/models/music_session.rb | 4 +- lib/jam_ruby/models/music_session_client.rb | 22 --- lib/jam_ruby/mq_router.rb | 61 ++++++-- spec/factories.rb | 3 +- spec/jam_ruby/connection_manager_spec.rb | 148 ++++++++++++++++++++ spec/jam_ruby/models/music_session_spec.rb | 4 +- spec/jam_ruby/mq_router_spec.rb | 9 +- spec/spec_db.rb | 1 + spec/spec_helper.rb | 2 +- 15 files changed, 323 insertions(+), 99 deletions(-) delete mode 100644 lib/jam_ruby/connection_cleaner.rb create mode 100644 lib/jam_ruby/connection_manager.rb delete mode 100644 lib/jam_ruby/models/music_session_client.rb create mode 100644 spec/jam_ruby/connection_manager_spec.rb diff --git a/Gemfile b/Gemfile index 4bfca7659..3c442ecfe 100644 --- a/Gemfile +++ b/Gemfile @@ -13,6 +13,7 @@ gem 'bcrypt-ruby', '3.0.1' gem 'ruby-protocol-buffers', '1.2.2' gem 'eventmachine' gem 'amqp' +gem 'pry' group :test do gem 'jam_db', :path=> "#{workspace}/jam-db/target/ruby_package" diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index 9fb8eeca8..bd1056063 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -4,9 +4,9 @@ require "jampb" require "uuidtools" require "logging" require "jam_ruby/mq_router" +require "jam_ruby/connection_manager" require "jam_ruby/version" require "jam_ruby/message_factory" -require "jam_ruby/models/music_session_client" require "jam_ruby/models/user" require "jam_ruby/models/musician" require "jam_ruby/models/band" diff --git a/lib/jam_ruby/connection_cleaner.rb b/lib/jam_ruby/connection_cleaner.rb deleted file mode 100644 index 2bee3b57b..000000000 --- a/lib/jam_ruby/connection_cleaner.rb +++ /dev/null @@ -1,51 +0,0 @@ -class ConnectionCleaner - - def initialize(db_options) - @mq_router = MQRouter.new - @pg_conn = PG::Connection.new(db_options) # just give a dedicated connection to this process - - unless PG.threadsafe - raise Exception "a non-threadsafe build of libpq is present." - end - end - - def remove_stale_connections() - @pg_conn.transaction do |conn| - - end - end - - - # once a connection is known gone (whether timeout or because a TCP connection is observed lost) - # this code is responsible for all cleanup logic associated with a connection going away - def remove_connection(client_id) - - @pg_conn.transaction do |conn| - - # FOR UPDATE - lock the connection and related jam_sessions in question - conn.exec("SELECT user_id FROM connections LEFT OUTER JOIN music_sessions WHERE client_id = $1 ON connections.music_session_id = music_sessions.id FOR UPDATE", [client_id]) do |result| - - conn.exec("DELETE FROM connections WHERE client_id = $1", [client_id]) do |result| - - if result.cmd_tuples == 0 - # the client is already gone from the database... do nothing - - elsif result.cmd_tuples == 1 - - # if we did delete a row, check and see if any more connections for that user exist - # if we are down to zero, send out user gone message - conn.exec("SELECT count(user_id) FROM connections where user_id = $1", [result[0][0]]) do |result| - @mq_router.server_publish_to_user() - end - else - raise Exception 'uniqueness constraint has been lost on client_id' - end - end - end - - end - - end - - -end \ No newline at end of file diff --git a/lib/jam_ruby/connection_manager.rb b/lib/jam_ruby/connection_manager.rb new file mode 100644 index 000000000..a9c47f8de --- /dev/null +++ b/lib/jam_ruby/connection_manager.rb @@ -0,0 +1,103 @@ +class ConnectionManager + + attr_accessor :mq_router + + def initialize(conn) + @log = Logging.logger[self] + @mq_router = MQRouter.new + @pg_conn = conn + @message_factory = MessageFactory.new + + + unless PG.threadsafe? + raise Exception "a non-threadsafe build of libpq is present." + end + end + + def remove_stale_connections() + @pg_conn.transaction do |conn| + + end + end + + def create_connection(user_id, client_id, ip_address) + @pg_conn.transaction do |conn| + + lock_connections(conn) + + conn.exec("INSERT INTO connections (user_id, client_id, ip_address) VALUES ($1, $2, $3)", [user_id, client_id, ip_address]).clear + + # we just created a new connection-if this is the first time the user has shown up, we need to send out a message to his friends + conn.exec("SELECT count(user_id) FROM connections WHERE user_id = $1", [user_id]) do |result| + count = result.getvalue(0, 0) + if count == "1" + # get all friend user_ids using the same query rails does for @user.friends + friend_update = @message_factory.friend_update(user_id, true) + friend_ids = gather_friends(conn, user_id) + @mq_router.publish_to_friends(friend_ids, friend_update, user_id) + end + end + end + end + + + # once a connection is known gone (whether timeout or because a TCP connection is observed lost) + # this code is responsible for all cleanup logic associated with a connection going away + def delete_connection(client_id) + + user_id = nil + music_session_id = nil + + @pg_conn.transaction do |conn| + + lock_connections(conn) + + conn.exec("DELETE FROM connections WHERE client_id = $1 RETURNING user_id, music_session_id", [client_id]) do |result| + + if result.cmd_tuples == 0 + # the client is already gone from the database... do nothing but log error + @log.error("unable to delete client #{client_id}") + return + elsif result.cmd_tuples == 1 + user_id = result[0]['user_id'] + music_session_id = result[0]['client_id'] + + else + raise Exception 'uniqueness constraint has been lost on client_id' + end + end + + + # since we did delete a row, check and see if any more connections for that user exist + # if we are down to zero, send out user gone message + conn.exec("SELECT count(user_id) FROM connections where user_id = $1", [user_id]) do |result| + count = result.getvalue(0, 0) + if count == "0" + friend_update = @message_factory.friend_update(user_id, false) + friend_ids = gather_friends(conn, user_id) + @mq_router.publish_to_friends(friend_ids, friend_update, user_id) + end + end + + # same for session-if we are down to the last participant, delete the session + unless music_session_id.nil? + conn.exec("DELETE FROM music_sessions id = $1 AND 0 = (SELECT count(music_session_id) FROM connections where music_session_id = $1)", [music_session_id]).clear + end + end + end + + def lock_connections(conn) + conn.exec("LOCK connections IN ACCESS EXCLUSIVE MODE").clear + end + + + def gather_friends(conn, user_id) + friend_ids = [] + conn.exec("SELECT f1.friend_id as friend_id FROM friendships f1 WHERE f1.user_id = $1 AND f1.friend_id IN (SELECT f2.user_id FROM friendships f2 WHERE f2.friend_id = $1)", [user_id]) do |friend_results| + friend_results.each do |friend_result| + friend_ids.push(friend_result['friend_id']) + end + end + return friend_ids + end +end \ No newline at end of file diff --git a/lib/jam_ruby/message_factory.rb b/lib/jam_ruby/message_factory.rb index 96912edee..630a9f03a 100644 --- a/lib/jam_ruby/message_factory.rb +++ b/lib/jam_ruby/message_factory.rb @@ -127,7 +127,7 @@ # 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, :target => CLIENT_TARGET, :friend_update => friend) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::FRIEND_UPDATE, :route_to => CLIENT_TARGET, :friend_update => friend) end # is this message directed to the server? diff --git a/lib/jam_ruby/models/connection.rb b/lib/jam_ruby/models/connection.rb index 7d6f4382c..45755df5a 100644 --- a/lib/jam_ruby/models/connection.rb +++ b/lib/jam_ruby/models/connection.rb @@ -2,7 +2,14 @@ module JamRuby class Connection < ActiveRecord::Base self.primary_key = 'id' - belongs_to :user, :class_name => "JamRuby::User" + belongs_to :user, :class_name => "JamRuby::User" + belongs_to :music_session, :class_name => "JamRuby::MusicSession" + + # decides if a given user can access this client with p2p messaging + # the answer is yes if the user is in the same music session + def access_p2p?(user) + return self.music_session.users.exists?(user) + end end end \ No newline at end of file diff --git a/lib/jam_ruby/models/music_session.rb b/lib/jam_ruby/models/music_session.rb index ff65d19a6..bf2e35054 100644 --- a/lib/jam_ruby/models/music_session.rb +++ b/lib/jam_ruby/models/music_session.rb @@ -4,8 +4,8 @@ module JamRuby self.primary_key = 'id' belongs_to :creator, :inverse_of => :music_sessions, :class_name => "JamRuby::User", :foreign_key => "user_id" - has_many :music_session_clients, :class_name => "JamRuby::MusicSessionClient" - has_many :users, :through => :music_session_clients, :class_name => "JamRuby::User" + has_many :connections, :class_name => "JamRuby::Connection" + has_many :users, :through => :connections, :class_name => "JamRuby::User" # Verifies that the specified user can join this jam session def access?(user) diff --git a/lib/jam_ruby/models/music_session_client.rb b/lib/jam_ruby/models/music_session_client.rb deleted file mode 100644 index 5099cc14f..000000000 --- a/lib/jam_ruby/models/music_session_client.rb +++ /dev/null @@ -1,22 +0,0 @@ -# called 'participant' currently in the REST APIs - -module JamRuby - class MusicSessionClient < ActiveRecord::Base - self.primary_key = 'id' - - belongs_to :user, :class_name => "JamRuby::User" - belongs_to :music_session, :class_name => "JamRuby::MusicSession" - - validates :client_id, :presence => true - - def to_s - return "#{self.user.to_s}:#{self.ip_address}" - end - - # decides if a given user can access this client with p2p messaging - # the answer is yes if the user is in the same music session - def access_p2p?(user) - return self.music_session.users.exists?(user) - end - end -end diff --git a/lib/jam_ruby/mq_router.rb b/lib/jam_ruby/mq_router.rb index 91e808cb4..4ca06e774 100644 --- a/lib/jam_ruby/mq_router.rb +++ b/lib/jam_ruby/mq_router.rb @@ -11,6 +11,7 @@ class MQRouter @@log = Logging.logger[MQRouter] end + def access_music_session(music_session, user) if music_session.nil? @@ -27,32 +28,68 @@ class MQRouter # sends a message to a session on behalf of a user # if this is originating in the context of a client, it should be specified as :client_id => "value" # client_msg should be a well-structure message (jam-pb message) - def user_publish_to_session(music_session, user, client_msg, sender = {:client_id => "" }) + def user_publish_to_session(music_session, user, client_msg, sender = {:client_id => ""}) access_music_session(music_session, user) # gather up client_ids in the session - client_ids = music_session.music_session_clients.map {|client| client.client_id }.reject {|client_id| client_id == sender[:client_id] } + client_ids = music_session.connections.map { |client| client.client_id }.reject { |client_id| client_id == sender[:client_id] } publish_to_session(music_session.id, client_ids, client_msg.to_s, sender) end - # sends a message to a session with no checking of permissions + # 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_session(music_session_id, client_ids, client_msg, sender = {:client_id => "" }) + def publish_to_client(client_id, client_msg, sender = {:client_id => ""}) EM.schedule do - sender_client_id = sender[:client_id] + sender_client_id = sender[:client_id] - # iterate over each person in the session, and send a p2p message - client_ids.each do |client_id| - - @@log.debug "publishing to session:#{music_session_id} client:#{client_id} from client:#{sender_client_id}" - # put it on the topic exchange3 for clients - self.class.client_exchange.publish(client_msg, :routing_key => "client.#{client_id}") - end + @@log.debug "publishing to client:#{client_id} from client:#{sender_client_id}" + # put it on the topic exchange for clients + self.class.client_exchange.publish(client_msg, :routing_key => "client.#{client_id}") end end + # 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] + + # iterate over each person in the session, and send a p2p message + client_ids.each do |client_id| + + @@log.debug "publishing to session:#{music_session_id} / client:#{client_id} from client:#{sender_client_id}" + # put it on the topic exchange for clients + self.class.client_exchange.publish(client_msg, :routing_key => "client.#{client_id}") + end + end + end + + # 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 + self.class.client_exchange.publish(user_msg, :routing_key => "client.#{user_id}") + end + end + + # 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}" + # put it on the topic exchange for users + self.class.client_exchange.publish(user_msg, :routing_key => "user.#{friend_id}") + end + end + end end \ No newline at end of file diff --git a/spec/factories.rb b/spec/factories.rb index 4250c349a..75e7f1be2 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -14,6 +14,7 @@ FactoryGirl.define do sequence(:description) { |n| "Music Session #{n}" } end - factory :music_session_client, :class => JamRuby::MusicSessionClient do + factory :connection, :class => JamRuby::Connection do + end end \ No newline at end of file diff --git a/spec/jam_ruby/connection_manager_spec.rb b/spec/jam_ruby/connection_manager_spec.rb new file mode 100644 index 000000000..876987b8e --- /dev/null +++ b/spec/jam_ruby/connection_manager_spec.rb @@ -0,0 +1,148 @@ +require 'spec_helper' + +# these tests avoid the use of ActiveRecord and FactoryGirl to do blackbox, non test-instrumented tests +describe ConnectionManager do + + before do + + @conn = PG::Connection.new(:dbname => SpecDb::TEST_DB_NAME, :user => "postgres", :password => "postgres", :host => "localhost") + @connman = ConnectionManager.new(@conn) + @message_factory = MessageFactory.new + end + + def create_user(name, email) + @conn.exec("INSERT INTO users (name, email, password_digest) VALUES ($1, $2, $3) RETURNING id", [name, email, '1']) do |result| + return result.getvalue(0,0) + end + end + + it "can't create bogus user_id" do + + expect { @connman.create_connection("aeonuthaoentuh", "client_id", "1.1.1.1") }.to raise_error(PG::Error) + end + + it "can't create two client_ids of same value" do + + client_id = "client_id1" + user_id = create_user("user1", "user1@jamkazam.com") + @connman.create_connection(user_id, client_id, "1.1.1.1") + expect { @connman.create_connection(user_id, client_id, "1.1.1.1") }.to raise_error(PG::Error) + + end + + it "create connection then delete it" do + + client_id = "client_id2" + user_id = create_user("user2", "user2@jamkazam.com") + @connman.create_connection(user_id, client_id, "1.1.1.1") + + # make sure the connection is seen + @conn.exec("SELECT count(*) FROM connections where user_id = $1", [user_id]) do |result| + result.getvalue(0, 0).should == "1" + end + + @connman.delete_connection(client_id) + @conn.exec("SELECT count(*) FROM connections where user_id = $1", [user_id]) do |result| + result.getvalue(0,0).should == "0" + end + end + + it "create connection creates user joined message appropriately" do + + client_id = "client_id3" + client_id2 = "client_id3_1" + + user_id = create_user("user3", "user3@jamkazam.com") + + # we should get a message saying that this user is online + friend_update = @message_factory.friend_update(user_id, true) + @connman.mq_router.should_receive(:publish_to_friends).with([], friend_update, user_id) + + @connman.create_connection(user_id, client_id, "1.1.1.1") + + + # but a second connection from the same user should cause no such message + @connman.should_receive(:publish_to_friends).exactly(0).times + + @connman.create_connection(user_id, client_id2, "1.1.1.1") + + end + + + it "deletes connection creates user left message appropriately" do + + client_id = "client_id4" + client_id2 = "client_id4_1" + + user_id = create_user("user4", "user4@jamkazam.com") + + # we should get a message saying that this user is online + + @connman.create_connection(user_id, client_id, "1.1.1.1") + @connman.create_connection(user_id, client_id2, "1.1.1.1") + + # deleting one of the two connections should cause no messages + @connman.should_receive(:publish_to_friends).exactly(0).times + + @connman.delete_connection(client_id) + + # but deleting the final connection should cause a left message + friend_update = @message_factory.friend_update(user_id, false) + @connman.mq_router.should_receive(:publish_to_friends).with([], friend_update, user_id) + + @connman.delete_connection(client_id2) + + end + + it "lookup of friends should find mutual friends only" do + + def create_friend(user_id, friend_id) + @conn.exec("INSERT INTO friendships(user_id, friend_id) VALUES ($1, $2)", [user_id, friend_id]) + end + + def delete_friend(user_id, friend_id) + @conn.exec("DELETE FROM friendships WHERE user_id = $1 AND friend_id = $2", [user_id, friend_id]) + end + + client_id = "client_id5" + + user_id1 = create_user("user5", "user5@jamkazam.com") + user_id2 = create_user("user6", "user6@jamkazam.com") + user_id3 = create_user("user7", "user7@jamkazam.com") + + @connman.gather_friends(@conn, user_id1).should == [] + @connman.gather_friends(@conn, user_id2).should == [] + @connman.gather_friends(@conn, user_id3).should == [] + + # create one-way link + create_friend(user_id1, user_id2) + + @connman.gather_friends(@conn, user_id1).should == [] + @connman.gather_friends(@conn, user_id2).should == [] + @connman.gather_friends(@conn, user_id3).should == [] + + # create one-way link back the other way + create_friend(user_id2, user_id1) + + @connman.gather_friends(@conn, user_id1).should == [user_id2] + @connman.gather_friends(@conn, user_id2).should == [user_id1] + @connman.gather_friends(@conn, user_id3).should == [] + + # make sure a new link to user 1 > user 3 doesn't disrupt anything + create_friend(user_id1, user_id3) + + @connman.gather_friends(@conn, user_id1).should == [user_id2] + @connman.gather_friends(@conn, user_id2).should == [user_id1] + @connman.gather_friends(@conn, user_id3).should == [] + + # make sure a new link to user 1 > user 3 doesn't disrupt anything + create_friend(user_id3, user_id1) + + @connman.gather_friends(@conn, user_id1).should =~ [user_id2, user_id3] + @connman.gather_friends(@conn, user_id2).should == [user_id1] + @connman.gather_friends(@conn, user_id3).should == [user_id1] + + + end +end + diff --git a/spec/jam_ruby/models/music_session_spec.rb b/spec/jam_ruby/models/music_session_spec.rb index ad93f9558..4560a57d0 100644 --- a/spec/jam_ruby/models/music_session_spec.rb +++ b/spec/jam_ruby/models/music_session_spec.rb @@ -10,8 +10,8 @@ describe MusicSession do music_session = FactoryGirl.create(:music_session, :creator => user1) - music_session_member1 = FactoryGirl.create(:music_session_client, :user => user1, :music_session => music_session, :ip_address => "1.1.1.1", :client_id => "1") - music_session_member2 = FactoryGirl.create(:music_session_client, :user => user2, :music_session => music_session, :ip_address => "2.2.2.2", :client_id => "2") + music_session_member1 = FactoryGirl.create(:connection, :user => user1, :music_session => music_session, :ip_address => "1.1.1.1", :client_id => "1") + music_session_member2 = FactoryGirl.create(:connection, :user => user2, :music_session => music_session, :ip_address => "2.2.2.2", :client_id => "2") music_session.access?(user1).should == true music_session.access?(user2).should == true diff --git a/spec/jam_ruby/mq_router_spec.rb b/spec/jam_ruby/mq_router_spec.rb index 37bf54288..2e1b36750 100644 --- a/spec/jam_ruby/mq_router_spec.rb +++ b/spec/jam_ruby/mq_router_spec.rb @@ -13,8 +13,8 @@ describe MQRouter do music_session = FactoryGirl.create(:music_session, :creator => user1) - music_session_member1 = FactoryGirl.create(:music_session_client, :user => user1, :music_session => music_session, :ip_address => "1.1.1.1", :client_id => "1") - music_session_member2 = FactoryGirl.create(:music_session_client, :user => user2, :music_session => music_session, :ip_address => "2.2.2.2", :client_id => "2") + music_session_member1 = FactoryGirl.create(:connection, :user => user1, :music_session => music_session, :ip_address => "1.1.1.1", :client_id => "1") + music_session_member2 = FactoryGirl.create(:connection, :user => user2, :music_session => music_session, :ip_address => "2.2.2.2", :client_id => "2") @mq_router.should_receive(:publish_to_session).with(music_session.id, [music_session_member2.client_id], "a message", :client_id => music_session_member1.client_id) @@ -28,8 +28,8 @@ describe MQRouter do music_session = FactoryGirl.create(:music_session, :creator => user1) - music_session_member1 = FactoryGirl.create(:music_session_client, :user => user1, :music_session => music_session, :ip_address => "1.1.1.1", :client_id => "1") - music_session_member2 = FactoryGirl.create(:music_session_client, :user => user2, :music_session => music_session, :ip_address => "2.2.2.2", :client_id => "2") + music_session_member1 = FactoryGirl.create(:connection, :user => user1, :music_session => music_session, :ip_address => "1.1.1.1", :client_id => "1") + music_session_member2 = FactoryGirl.create(:connection, :user => user2, :music_session => music_session, :ip_address => "2.2.2.2", :client_id => "2") EM.run do @@ -41,7 +41,6 @@ describe MQRouter do @mq_router.user_publish_to_session(music_session, user1, "a message", :client_id => music_session_member1.client_id) EM.stop - end end diff --git a/spec/spec_db.rb b/spec/spec_db.rb index 275a1b485..b825973f5 100644 --- a/spec/spec_db.rb +++ b/spec/spec_db.rb @@ -2,6 +2,7 @@ class SpecDb TEST_DB_NAME="jam_ruby_test" + TEST_USER_ID = "1" #test@jamkazam.com def self.recreate_database conn = PG::Connection.open("dbname=postgres user=postgres password=postgres host=localhost") conn.exec("DROP DATABASE IF EXISTS #{TEST_DB_NAME}") diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index b794c7707..bef938bf1 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -5,6 +5,7 @@ require 'spec_db' # recreate test database and migrate it SpecDb::recreate_database + # initialize ActiveRecord's db connection ActiveRecord::Base.establish_connection(YAML::load(File.open('config/database.yml'))["test"]) @@ -70,5 +71,4 @@ end Spork.each_run do # This code will be run each time you run your specs. - end \ No newline at end of file From 3e269f2551d4d7aed965a1c07d0d4d8af7f05f0a Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 21 Oct 2012 10:05:06 -0500 Subject: [PATCH 036/274] * connection manager complete. now will integrate into jam-web and websocket-gateway --- Gemfile | 2 +- lib/jam_ruby.rb | 2 + lib/jam_ruby/connection_manager.rb | 276 +++++++++++++++++------ lib/jam_ruby/errors/permission_error.rb | 4 +- spec/jam_ruby/connection_manager_spec.rb | 138 +++++++++++- 5 files changed, 350 insertions(+), 72 deletions(-) diff --git a/Gemfile b/Gemfile index 3c442ecfe..e69e2ea75 100644 --- a/Gemfile +++ b/Gemfile @@ -13,7 +13,6 @@ gem 'bcrypt-ruby', '3.0.1' gem 'ruby-protocol-buffers', '1.2.2' gem 'eventmachine' gem 'amqp' -gem 'pry' group :test do gem 'jam_db', :path=> "#{workspace}/jam-db/target/ruby_package" @@ -21,6 +20,7 @@ group :test do gem "rspec", "2.10.0" gem 'spork', '0.9.0' gem 'database_cleaner', '0.7.0' + gem 'pry' end # Specify your gem's dependencies in jam_ruby.gemspec diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index bd1056063..669f23788 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -3,6 +3,8 @@ require "active_record" require "jampb" require "uuidtools" require "logging" +require "jam_ruby/errors/permission_error" +require "jam_ruby/errors/state_error" require "jam_ruby/mq_router" require "jam_ruby/connection_manager" require "jam_ruby/version" diff --git a/lib/jam_ruby/connection_manager.rb b/lib/jam_ruby/connection_manager.rb index a9c47f8de..c0b02fcb6 100644 --- a/lib/jam_ruby/connection_manager.rb +++ b/lib/jam_ruby/connection_manager.rb @@ -1,103 +1,243 @@ -class ConnectionManager +module JamRuby +# All writes should occur through the ConnectionManager +# Reads can occur freely elsewhere, though +# Because connections are tied to the websocket-connection and we bookkeep them in the database purely +# for 'SQL convenience', this is a obvious place we can go away from a database +# as an optimization if we find it's too much db traffic created' +# At a minimum, though, we could make connections an UNLOGGED table because if the database crashes, +# all clients should reconnect and restablish their connection anyway +# +# All methods in here could also be refactored as stored procedures, if we stick with a database. +# This may make sense in the short term if we are still managing connections in the database, but +# we move to the node-js in the websocket gateway (because the websocket gateway needs to call some of these methods). +# Or of course we could just port the relevant methods to node-js + class ConnectionManager - attr_accessor :mq_router + attr_accessor :mq_router - def initialize(conn) - @log = Logging.logger[self] - @mq_router = MQRouter.new - @pg_conn = conn - @message_factory = MessageFactory.new + def initialize(conn) + @log = Logging.logger[self] + @mq_router = MQRouter.new + @pg_conn = conn + @message_factory = MessageFactory.new - - unless PG.threadsafe? - raise Exception "a non-threadsafe build of libpq is present." + unless PG.threadsafe? + raise Exception, "a non-threadsafe build of libpq is present." + end end - end - - def remove_stale_connections() - @pg_conn.transaction do |conn| + def update_staleness() + #TODO end - end - def create_connection(user_id, client_id, ip_address) - @pg_conn.transaction do |conn| + # remove stale connections + def remove_stale_connections(max_seconds) + stale_clients = [] + @pg_conn.exec("SELECT client_id FROM connections WHERE updated_at < (NOW() - interval '#{max_seconds} second')") do |result| + result.each do |row| + stale_clients.push(row['client_id']) + end + end - lock_connections(conn) + stale_clients.each do |client_id| + delete_connection(client_id) + end + end - conn.exec("INSERT INTO connections (user_id, client_id, ip_address) VALUES ($1, $2, $3)", [user_id, client_id, ip_address]).clear + def create_connection(user_id, client_id, ip_address) + @pg_conn.transaction do |conn| - # we just created a new connection-if this is the first time the user has shown up, we need to send out a message to his friends - conn.exec("SELECT count(user_id) FROM connections WHERE user_id = $1", [user_id]) do |result| - count = result.getvalue(0, 0) - if count == "1" - # get all friend user_ids using the same query rails does for @user.friends - friend_update = @message_factory.friend_update(user_id, true) - friend_ids = gather_friends(conn, user_id) - @mq_router.publish_to_friends(friend_ids, friend_update, user_id) + lock_connections(conn) + + conn.exec("INSERT INTO connections (user_id, client_id, ip_address) VALUES ($1, $2, $3)", [user_id, client_id, ip_address]).clear + + # we just created a new connection-if this is the first time the user has shown up, we need to send out a message to his friends + conn.exec("SELECT count(user_id) FROM connections WHERE user_id = $1", [user_id]) do |result| + count = result.getvalue(0, 0) + if count == "1" + # get all friend user_ids using the same query rails does for @user.friends + friend_update = @message_factory.friend_update(user_id, true) + friend_ids = gather_friends(conn, user_id) + @mq_router.publish_to_friends(friend_ids, friend_update, user_id) + end end end end - end - # once a connection is known gone (whether timeout or because a TCP connection is observed lost) - # this code is responsible for all cleanup logic associated with a connection going away - def delete_connection(client_id) + # once a connection is known gone (whether timeout or because a TCP connection is observed lost) + # this code is responsible for all cleanup logic associated with a connection going away + def delete_connection(client_id) - user_id = nil - music_session_id = nil + user_id = nil + music_session_id = nil - @pg_conn.transaction do |conn| + @pg_conn.transaction do |conn| - lock_connections(conn) + lock_connections(conn) - conn.exec("DELETE FROM connections WHERE client_id = $1 RETURNING user_id, music_session_id", [client_id]) do |result| + previous_music_session_id = check_already_session(conn, client_id) - if result.cmd_tuples == 0 - # the client is already gone from the database... do nothing but log error - @log.error("unable to delete client #{client_id}") - return - elsif result.cmd_tuples == 1 - user_id = result[0]['user_id'] - music_session_id = result[0]['client_id'] + conn.exec("DELETE FROM connections WHERE client_id = $1 RETURNING user_id, music_session_id", [client_id]) do |result| + if result.cmd_tuples == 0 + # the client is already gone from the database... do nothing but log error + @log.warn("unable to delete client #{client_id}") + return + elsif result.cmd_tuples == 1 + user_id = result[0]['user_id'] + music_session_id = result[0]['client_id'] + + else + raise Exception, 'uniqueness constraint has been lost on client_id' + end + end + + session_checks(conn, previous_music_session_id, user_id) + + # since we did delete a row, check and see if any more connections for that user exist + # if we are down to zero, send out user gone message + conn.exec("SELECT count(user_id) FROM connections where user_id = $1", [user_id]) do |result| + count = result.getvalue(0, 0) + if count == "0" + friend_update = @message_factory.friend_update(user_id, false) + friend_ids = gather_friends(conn, user_id) + @mq_router.publish_to_friends(friend_ids, friend_update, user_id) + end + end + + # same for session-if we are down to the last participant, delete the session + unless music_session_id.nil? + conn.exec("DELETE FROM music_sessions id = $1 AND 0 = (SELECT count(music_session_id) FROM connections where music_session_id = $1)", [music_session_id]).clear + end + end + end + + def destroy_if_empty(conn, music_session_id) + + num_participants = nil + conn.exec("SELECT count(*) FROM connections WHERE music_session_id = $1", [music_session_id]) do |result| + num_participants = result.getvalue(0, 0).to_i + end + + if num_participants == 0 + # delete the music_session + conn.exec("DELETE from music_sessions WHERE id = $1", [music_session_id]) do |result| + if result.cmd_tuples == 0 + # no music session deleted. do nothing + elsif result.cmd_tuples == 1 + # music session deleted! + @log.debug("deleted music session #{music_session_id}") + else + @log.error("music_sessions table data integrity violation; multiple rows found with music_session_id=#{music_session_id}") + raise Exception, "music_sessions table data integrity violation; multiple rows found with music_session_id=#{music_session_id}" + end + end + end + end + + def check_already_session(conn, client_id) + conn.exec("SELECT music_session_id FROM connections WHERE client_id = $1", [client_id]) do |result| + if result.num_tuples == 1 + previous_music_session_id = result.getvalue(0, 0) + return previous_music_session_id + elsif result.num_tuples == 0 + # there is no connection found matching this criteria; we are done. + @log.debug("when checking for existing session, no connection found with client=#{client_id}") + return nil else - raise Exception 'uniqueness constraint has been lost on client_id' + @log.error("connection table data integrity violation; multiple rows found. client_id=#{client_id}") + raise Exception, "connection table data integrity violation; multiple rows found. client_id=#{client_id}" end end + end + def session_checks(conn, previous_music_session_id, user_id) + unless previous_music_session_id.nil? + # TODO: send notification to friends that this user left this session? + @log.debug("user #{user_id} left music_session #{previous_music_session_id}") + destroy_if_empty(conn, previous_music_session_id) + end + end + + def join_music_session(user_id, client_id, music_session_id) + @pg_conn.transaction do |conn| + + lock_connections(conn) + + previous_music_session_id = check_already_session(conn, client_id) + + begin + conn.exec("UPDATE connections SET music_session_id = $1 WHERE client_id = $2", [music_session_id, client_id]) do |result| + if result.cmd_tuples == 1 + @log.debug "associated music_session with connection for client=#{client_id} and music_session=#{music_session_id}" + + session_checks(conn, previous_music_session_id, user_id) + elsif result.cmd_tuples == 0 + @log.debug "join_music_session no connection found with client_id=#{client_id}" + raise StateError, "no connection found with client=#{client_id}" + else + @log.error "database failure or logic error; this path should be impossible if the table is locked (join_music_session)" + raise Exception, "locked table changed state" + end + end + rescue PG::Error => pg_error + if pg_error.to_s.include?("insert or update on table \"connections\" violates foreign key constraint \"connections_music_session_id_fkey\"") + # if there is no music session that exists, we will receive this message + @log.debug "music_session does not exist. music_session=#{music_session_id}" + raise StateError, "music_session does not exist" + else + raise pg_error + end - # since we did delete a row, check and see if any more connections for that user exist - # if we are down to zero, send out user gone message - conn.exec("SELECT count(user_id) FROM connections where user_id = $1", [user_id]) do |result| - count = result.getvalue(0, 0) - if count == "0" - friend_update = @message_factory.friend_update(user_id, false) - friend_ids = gather_friends(conn, user_id) - @mq_router.publish_to_friends(friend_ids, friend_update, user_id) end end + end - # same for session-if we are down to the last participant, delete the session - unless music_session_id.nil? - conn.exec("DELETE FROM music_sessions id = $1 AND 0 = (SELECT count(music_session_id) FROM connections where music_session_id = $1)", [music_session_id]).clear + def leave_music_session(user_id, client_id, music_session_id) + @pg_conn.transaction do |conn| + + lock_connections(conn) + + previous_music_session_id = check_already_session(conn, client_id) + + if previous_music_session_id == nil + @log.debug "the client is not in a session. user=#{user_id}, client=#{client_id}, music_session=#{music_session_id}" + raise StateError, "not in session" + elsif previous_music_session_id != music_session_id + @log.debug "the client is in a different session. user=#{user_id}, client=#{client_id}, music_session=#{music_session_id}" + raise StateError, "in a session different than that specified" + end + + # can throw exception if the session is deleted just before this + conn.exec("UPDATE connections SET music_session_id = NULL WHERE client_id = $1", [client_id]) do |result| + if result.cmd_tuples == 1 + @log.debug("deassociated music_session with connection for client_id #{client_id}") + + session_checks(conn, previous_music_session_id, user_id) + elsif result.cmd_tuples == 0 + @log.debug "leave_music_session no connection found with client_id=#{client_id}" + raise StateError, "no connection found with client=#{client_id}" + else + @log.error("database failure or logic error; this path should be impossible if the table is locked (leave_music_session)") + raise Exception, "locked table changed state" + end + end end end - end - def lock_connections(conn) - conn.exec("LOCK connections IN ACCESS EXCLUSIVE MODE").clear - end - - - def gather_friends(conn, user_id) - friend_ids = [] - conn.exec("SELECT f1.friend_id as friend_id FROM friendships f1 WHERE f1.user_id = $1 AND f1.friend_id IN (SELECT f2.user_id FROM friendships f2 WHERE f2.friend_id = $1)", [user_id]) do |friend_results| - friend_results.each do |friend_result| - friend_ids.push(friend_result['friend_id']) - end + def lock_connections(conn) + conn.exec("LOCK connections IN EXCLUSIVE MODE").clear + end + + def gather_friends(conn, user_id) + friend_ids = [] + conn.exec("SELECT f1.friend_id as friend_id FROM friendships f1 WHERE f1.user_id = $1 AND f1.friend_id IN (SELECT f2.user_id FROM friendships f2 WHERE f2.friend_id = $1)", [user_id]) do |friend_results| + friend_results.each do |friend_result| + friend_ids.push(friend_result['friend_id']) + end + end + return friend_ids end - return friend_ids end end \ No newline at end of file diff --git a/lib/jam_ruby/errors/permission_error.rb b/lib/jam_ruby/errors/permission_error.rb index 80c4b1ff6..f0b4e3a2f 100644 --- a/lib/jam_ruby/errors/permission_error.rb +++ b/lib/jam_ruby/errors/permission_error.rb @@ -1,3 +1,5 @@ -class PermissionError < Exception +module JamRuby + class PermissionError < Exception + end end \ No newline at end of file diff --git a/spec/jam_ruby/connection_manager_spec.rb b/spec/jam_ruby/connection_manager_spec.rb index 876987b8e..0cc8faf2c 100644 --- a/spec/jam_ruby/connection_manager_spec.rb +++ b/spec/jam_ruby/connection_manager_spec.rb @@ -12,7 +12,31 @@ describe ConnectionManager do def create_user(name, email) @conn.exec("INSERT INTO users (name, email, password_digest) VALUES ($1, $2, $3) RETURNING id", [name, email, '1']) do |result| - return result.getvalue(0,0) + return result.getvalue(0, 0) + end + end + + def create_music_session(user_id) + description = "some session" + @conn.exec("INSERT INTO music_sessions (user_id, description) VALUES ($1, $2) RETURNING id", [user_id, description]) do |result| + return result.getvalue(0, 0) + end + end + + def assert_num_connections(client_id, expected_num_connections) + # make sure the connection is still there + @conn.exec("SELECT count(*) FROM connections where client_id = $1", [client_id]) do |result| + result.getvalue(0, 0).to_i.should == expected_num_connections + end + end + + def assert_session_exists(music_session_id, exists) + @conn.exec("SELECT count(*) FROM music_sessions where id = $1", [music_session_id]) do |result| + if exists + result.getvalue(0, 0).should == "1" + else + result.getvalue(0, 0).should == "0" + end end end @@ -43,7 +67,7 @@ describe ConnectionManager do @connman.delete_connection(client_id) @conn.exec("SELECT count(*) FROM connections where user_id = $1", [user_id]) do |result| - result.getvalue(0,0).should == "0" + result.getvalue(0, 0).should == "0" end end @@ -141,7 +165,117 @@ describe ConnectionManager do @connman.gather_friends(@conn, user_id1).should =~ [user_id2, user_id3] @connman.gather_friends(@conn, user_id2).should == [user_id1] @connman.gather_friends(@conn, user_id3).should == [user_id1] + end + it "remove stale connection" do + + client_id = "client_id8" + + user_id = create_user("user8", "user8@jamkazam.com") + + @connman.create_connection(user_id, client_id, "1.1.1.1") + + @connman.remove_stale_connections(60) + + assert_num_connections(client_id, 1) + + sleep(1) + + # this should remove the stale connection + @connman.remove_stale_connections(1) + + assert_num_connections(client_id, 0) + end + + it "connections with music_sessions associated" do + + client_id = "client_id9" + user_id = create_user("user9", "user9@jamkazam.com") + music_session_id = create_music_session(user_id) + + @connman.create_connection(user_id, client_id, "1.1.1.1") + @connman.join_music_session(user_id, client_id, music_session_id) + + assert_session_exists(music_session_id, true) + + @conn.exec("SELECT music_session_id FROM connections WHERE client_id = $1", [client_id]) do |result| + result.getvalue(0, 0).should == music_session_id + end + + @connman.delete_connection(client_id) + + assert_num_connections(client_id, 0) + + assert_session_exists(music_session_id, false) + end + + it "join_music_session fails if no connection" do + + client_id = "client_id10" + user_id = create_user("user10", "user10@jamkazam.com") + music_session_id = create_music_session(user_id) + + expect { @connman.join_music_session(user_id, client_id, music_session_id)}.to raise_error(JamRuby::StateError) + end + + it "join_music_session fails if no music_session" do + + client_id = "client_id11" + user_id = create_user("user11", "user11@jamkazam.com") + + @connman.create_connection(user_id, client_id, "1.1.1.1") + + expect { @connman.join_music_session(user_id, client_id, "some_bogus_music_session_id")}.to raise_error(JamRuby::StateError) + end + + it "leave_music_session fails if no music_session" do + + client_id = "client_id12" + user_id = create_user("user12", "user12@jamkazam.com") + + @connman.create_connection(user_id, client_id, "1.1.1.1") + + expect { @connman.leave_music_session(user_id, client_id, "some_bogus_music_session_id")}.to raise_error(JamRuby::StateError) + end + + it "leave_music_session fails if in different music_session" do + + client_id = "client_id13" + user_id = create_user("user13", "user13@jamkazam.com") + music_session_id = create_music_session(user_id) + + @connman.create_connection(user_id, client_id, "1.1.1.1") + @connman.join_music_session(user_id, client_id, music_session_id) + + expect { @connman.leave_music_session(user_id, client_id, "some_bogus_music_session_id")}.to raise_error(JamRuby::StateError) + end + + it "leave_music_session works" do + + client_id = "client_id14" + user_id = create_user("user14", "user14@jamkazam.com") + music_session_id = create_music_session(user_id) + + @connman.create_connection(user_id, client_id, "1.1.1.1") + @connman.join_music_session(user_id, client_id, music_session_id) + + assert_session_exists(music_session_id, true) + + @conn.exec("SELECT music_session_id FROM connections WHERE client_id = $1", [client_id]) do |result| + result.getvalue(0, 0).should == music_session_id + end + + @connman.leave_music_session(user_id, client_id, music_session_id) + + @conn.exec("SELECT music_session_id FROM connections WHERE client_id = $1", [client_id]) do |result| + result.getvalue(0, 0).should == nil + end + + assert_session_exists(music_session_id, false) + + @connman.delete_connection(client_id) + + assert_num_connections(client_id, 0) end end From 331c13d85854a74561382be908fa31df701ee605 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 21 Oct 2012 14:11:04 -0500 Subject: [PATCH 037/274] * forgot to add state_error class --- lib/jam_ruby/errors/state_error.rb | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 lib/jam_ruby/errors/state_error.rb diff --git a/lib/jam_ruby/errors/state_error.rb b/lib/jam_ruby/errors/state_error.rb new file mode 100644 index 000000000..3b4a34a92 --- /dev/null +++ b/lib/jam_ruby/errors/state_error.rb @@ -0,0 +1,7 @@ +module JamRuby +# this exception can be thrown if the server is not in a state that allows the operation to succeed +# however, it's not necessarily a bad thing; it just means + class StateError < Exception + + end +end \ No newline at end of file From e3027e831b048ed3049abb91833fff0f75ccbbcf Mon Sep 17 00:00:00 2001 From: Seth Call Date: Mon, 22 Oct 2012 19:59:35 -0500 Subject: [PATCH 038/274] * VRFS-18: removed transactions from conneciton manager --- lib/jam_ruby.rb | 1 + lib/jam_ruby/connection_manager.rb | 227 ++++++++++++---------- lib/jam_ruby/errors/jam_argument_error.rb | 7 + spec/jam_ruby/connection_manager_spec.rb | 130 +++++++++---- 4 files changed, 224 insertions(+), 141 deletions(-) create mode 100644 lib/jam_ruby/errors/jam_argument_error.rb diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index 669f23788..f886f77ad 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -5,6 +5,7 @@ require "uuidtools" require "logging" require "jam_ruby/errors/permission_error" require "jam_ruby/errors/state_error" +require "jam_ruby/errors/jam_argument_error" require "jam_ruby/mq_router" require "jam_ruby/connection_manager" require "jam_ruby/version" diff --git a/lib/jam_ruby/connection_manager.rb b/lib/jam_ruby/connection_manager.rb index c0b02fcb6..bb8100c45 100644 --- a/lib/jam_ruby/connection_manager.rb +++ b/lib/jam_ruby/connection_manager.rb @@ -13,12 +13,12 @@ module JamRuby # Or of course we could just port the relevant methods to node-js class ConnectionManager - attr_accessor :mq_router + attr_accessor :mq_router, :pg_conn - def initialize(conn) + def initialize(options={}) @log = Logging.logger[self] @mq_router = MQRouter.new - @pg_conn = conn + @pg_conn = options[:conn] @message_factory = MessageFactory.new unless PG.threadsafe? @@ -44,24 +44,25 @@ module JamRuby end end + def create_connection(user_id, client_id, ip_address) - @pg_conn.transaction do |conn| + conn = @pg_conn - lock_connections(conn) + lock_connections(conn) - conn.exec("INSERT INTO connections (user_id, client_id, ip_address) VALUES ($1, $2, $3)", [user_id, client_id, ip_address]).clear + conn.exec("INSERT INTO connections (user_id, client_id, ip_address) VALUES ($1, $2, $3)", [user_id, client_id, ip_address]).clear - # we just created a new connection-if this is the first time the user has shown up, we need to send out a message to his friends - conn.exec("SELECT count(user_id) FROM connections WHERE user_id = $1", [user_id]) do |result| - count = result.getvalue(0, 0) - if count == "1" - # get all friend user_ids using the same query rails does for @user.friends - friend_update = @message_factory.friend_update(user_id, true) - friend_ids = gather_friends(conn, user_id) - @mq_router.publish_to_friends(friend_ids, friend_update, user_id) - end + # we just created a new connection-if this is the first time the user has shown up, we need to send out a message to his friends + conn.exec("SELECT count(user_id) FROM connections WHERE user_id = $1", [user_id]) do |result| + count = result.getvalue(0, 0) + if count == "1" + # get all friend user_ids using the same query rails does for @user.friends + friend_update = @message_factory.friend_update(user_id, true) + friend_ids = gather_friends(conn, user_id) + @mq_router.publish_to_friends(friend_ids, friend_update, user_id) end end + #end end @@ -72,45 +73,45 @@ module JamRuby user_id = nil music_session_id = nil - @pg_conn.transaction do |conn| + conn = @pg_conn - lock_connections(conn) + lock_connections(conn) - previous_music_session_id = check_already_session(conn, client_id) + previous_music_session_id = check_already_session(conn, client_id) - conn.exec("DELETE FROM connections WHERE client_id = $1 RETURNING user_id, music_session_id", [client_id]) do |result| + conn.exec("DELETE FROM connections WHERE client_id = $1 RETURNING user_id, music_session_id", [client_id]) do |result| - if result.cmd_tuples == 0 - # the client is already gone from the database... do nothing but log error - @log.warn("unable to delete client #{client_id}") - return - elsif result.cmd_tuples == 1 - user_id = result[0]['user_id'] - music_session_id = result[0]['client_id'] + if result.cmd_tuples == 0 + # the client is already gone from the database... do nothing but log error + @log.warn("unable to delete client #{client_id}") + return + elsif result.cmd_tuples == 1 + user_id = result[0]['user_id'] + music_session_id = result[0]['client_id'] - else - raise Exception, 'uniqueness constraint has been lost on client_id' - end - end - - session_checks(conn, previous_music_session_id, user_id) - - # since we did delete a row, check and see if any more connections for that user exist - # if we are down to zero, send out user gone message - conn.exec("SELECT count(user_id) FROM connections where user_id = $1", [user_id]) do |result| - count = result.getvalue(0, 0) - if count == "0" - friend_update = @message_factory.friend_update(user_id, false) - friend_ids = gather_friends(conn, user_id) - @mq_router.publish_to_friends(friend_ids, friend_update, user_id) - end - end - - # same for session-if we are down to the last participant, delete the session - unless music_session_id.nil? - conn.exec("DELETE FROM music_sessions id = $1 AND 0 = (SELECT count(music_session_id) FROM connections where music_session_id = $1)", [music_session_id]).clear + else + raise Exception, 'uniqueness constraint has been lost on client_id' end end + + session_checks(conn, previous_music_session_id, user_id) + + # since we did delete a row, check and see if any more connections for that user exist + # if we are down to zero, send out user gone message + conn.exec("SELECT count(user_id) FROM connections where user_id = $1", [user_id]) do |result| + count = result.getvalue(0, 0) + if count == "0" + friend_update = @message_factory.friend_update(user_id, false) + friend_ids = gather_friends(conn, user_id) + @mq_router.publish_to_friends(friend_ids, friend_update, user_id) + end + end + + # same for session-if we are down to the last participant, delete the session + unless music_session_id.nil? + conn.exec("DELETE FROM music_sessions id = $1 AND 0 = (SELECT count(music_session_id) FROM connections where music_session_id = $1)", [music_session_id]).clear + end + # end end def destroy_if_empty(conn, music_session_id) @@ -161,83 +162,97 @@ module JamRuby end def join_music_session(user_id, client_id, music_session_id) - @pg_conn.transaction do |conn| + conn = @pg_conn - lock_connections(conn) + lock_connections(conn) - previous_music_session_id = check_already_session(conn, client_id) + previous_music_session_id = check_already_session(conn, client_id) - begin - conn.exec("UPDATE connections SET music_session_id = $1 WHERE client_id = $2", [music_session_id, client_id]) do |result| - if result.cmd_tuples == 1 - @log.debug "associated music_session with connection for client=#{client_id} and music_session=#{music_session_id}" - - session_checks(conn, previous_music_session_id, user_id) - elsif result.cmd_tuples == 0 - @log.debug "join_music_session no connection found with client_id=#{client_id}" - raise StateError, "no connection found with client=#{client_id}" - else - @log.error "database failure or logic error; this path should be impossible if the table is locked (join_music_session)" - raise Exception, "locked table changed state" - end - end - rescue PG::Error => pg_error - if pg_error.to_s.include?("insert or update on table \"connections\" violates foreign key constraint \"connections_music_session_id_fkey\"") - # if there is no music session that exists, we will receive this message - @log.debug "music_session does not exist. music_session=#{music_session_id}" - raise StateError, "music_session does not exist" - else - raise pg_error - end - - end - end - end - - def leave_music_session(user_id, client_id, music_session_id) - @pg_conn.transaction do |conn| - - lock_connections(conn) - - previous_music_session_id = check_already_session(conn, client_id) - - if previous_music_session_id == nil - @log.debug "the client is not in a session. user=#{user_id}, client=#{client_id}, music_session=#{music_session_id}" - raise StateError, "not in session" - elsif previous_music_session_id != music_session_id - @log.debug "the client is in a different session. user=#{user_id}, client=#{client_id}, music_session=#{music_session_id}" - raise StateError, "in a session different than that specified" - end - - # can throw exception if the session is deleted just before this - conn.exec("UPDATE connections SET music_session_id = NULL WHERE client_id = $1", [client_id]) do |result| + begin + conn.exec("UPDATE connections SET music_session_id = $1 WHERE client_id = $2", [music_session_id, client_id]) do |result| if result.cmd_tuples == 1 - @log.debug("deassociated music_session with connection for client_id #{client_id}") + @log.debug "associated music_session with connection for client=#{client_id} and music_session=#{music_session_id}" session_checks(conn, previous_music_session_id, user_id) elsif result.cmd_tuples == 0 - @log.debug "leave_music_session no connection found with client_id=#{client_id}" + @log.debug "join_music_session no connection found with client_id=#{client_id}" raise StateError, "no connection found with client=#{client_id}" else - @log.error("database failure or logic error; this path should be impossible if the table is locked (leave_music_session)") + @log.error "database failure or logic error; this path should be impossible if the table is locked (join_music_session)" raise Exception, "locked table changed state" end end + rescue PG::Error => pg_error + if pg_error.to_s.include?("insert or update on table \"connections\" violates foreign key constraint \"connections_music_session_id_fkey\"") + # if there is no music session that exists, we will receive this message + @log.debug "music_session does not exist. music_session=#{music_session_id}" + raise StateError, "music_session does not exist" + else + raise pg_error + end + end + # end end - def lock_connections(conn) - conn.exec("LOCK connections IN EXCLUSIVE MODE").clear - end + def leave_music_session(user_id, client_id, music_session_id) + conn = @pg_conn - def gather_friends(conn, user_id) - friend_ids = [] - conn.exec("SELECT f1.friend_id as friend_id FROM friendships f1 WHERE f1.user_id = $1 AND f1.friend_id IN (SELECT f2.user_id FROM friendships f2 WHERE f2.friend_id = $1)", [user_id]) do |friend_results| - friend_results.each do |friend_result| - friend_ids.push(friend_result['friend_id']) + lock_connections(conn) + + previous_music_session_id = check_already_session(conn, client_id) + + if previous_music_session_id == nil + @log.debug "the client is not in a session. user=#{user_id}, client=#{client_id}, music_session=#{music_session_id}" + raise StateError, "not in session" + elsif previous_music_session_id != music_session_id + @log.debug "the client is in a different session. user=#{user_id}, client=#{client_id}, music_session=#{music_session_id}" + raise StateError, "in a session different than that specified" + end + + # can throw exception if the session is deleted just before this + conn.exec("UPDATE connections SET music_session_id = NULL WHERE client_id = $1", [client_id]) do |result| + if result.cmd_tuples == 1 + @log.debug("deassociated music_session with connection for client_id #{client_id}") + + session_checks(conn, previous_music_session_id, user_id) + elsif result.cmd_tuples == 0 + @log.debug "leave_music_session no connection found with client_id=#{client_id}" + raise StateError, "no connection found with client=#{client_id}" + else + @log.error("database failure or logic error; this path should be impossible if the table is locked (leave_music_session)") + raise Exception, "locked table changed state" end end - return friend_ids end end + + # Creates a connection manager, and associates the connection created by active_record with ourselves + def self.active_record_transaction(&block) + + connection_manager = ConnectionManager.new + ActiveRecord::Base.connection_pool.with_connection do |connection| + # create a transaction, and pass the current connection to ConnectionManager. + # this lets the entire operation work with the same transaction, + # across Rails ActiveRecord and the pg-gem based code in ConnectionManager. + connection_manager.pg_conn = connection.instance_variable_get("@connection") + + block.call(connection_manager) + end + end + + def lock_connections(conn) + conn.exec("LOCK connections IN EXCLUSIVE MODE").clear + end + + def gather_friends(conn, user_id) + friend_ids = [] + conn.exec("SELECT f1.friend_id as friend_id FROM friendships f1 WHERE f1.user_id = $1 AND f1.friend_id IN (SELECT f2.user_id FROM friendships f2 WHERE f2.friend_id = $1)", [user_id]) do |friend_results| + friend_results.each do |friend_result| + friend_ids.push(friend_result['friend_id']) + end + end + return friend_ids + end +# end end \ No newline at end of file diff --git a/lib/jam_ruby/errors/jam_argument_error.rb b/lib/jam_ruby/errors/jam_argument_error.rb new file mode 100644 index 000000000..9f208363b --- /dev/null +++ b/lib/jam_ruby/errors/jam_argument_error.rb @@ -0,0 +1,7 @@ +module JamRuby + # if a bad argument is supplied. + # Why not use the default one? This allows us to know our API layer threw this, versus us using some core library incorrectly + class JamArgumentError < ArgumentError + + end +end \ No newline at end of file diff --git a/spec/jam_ruby/connection_manager_spec.rb b/spec/jam_ruby/connection_manager_spec.rb index 0cc8faf2c..4cb838957 100644 --- a/spec/jam_ruby/connection_manager_spec.rb +++ b/spec/jam_ruby/connection_manager_spec.rb @@ -3,10 +3,10 @@ require 'spec_helper' # these tests avoid the use of ActiveRecord and FactoryGirl to do blackbox, non test-instrumented tests describe ConnectionManager do - before do + before do @conn = PG::Connection.new(:dbname => SpecDb::TEST_DB_NAME, :user => "postgres", :password => "postgres", :host => "localhost") - @connman = ConnectionManager.new(@conn) + @connman = ConnectionManager.new(:conn => @conn) @message_factory = MessageFactory.new end @@ -42,30 +42,39 @@ describe ConnectionManager do it "can't create bogus user_id" do - expect { @connman.create_connection("aeonuthaoentuh", "client_id", "1.1.1.1") }.to raise_error(PG::Error) + @conn.transaction do + expect { @connman.create_connection("aeonuthaoentuh", "client_id", "1.1.1.1") }.to raise_error(PG::Error) + end + end it "can't create two client_ids of same value" do client_id = "client_id1" user_id = create_user("user1", "user1@jamkazam.com") - @connman.create_connection(user_id, client_id, "1.1.1.1") - expect { @connman.create_connection(user_id, client_id, "1.1.1.1") }.to raise_error(PG::Error) + @conn.transaction do + @connman.create_connection(user_id, client_id, "1.1.1.1") + expect { @connman.create_connection(user_id, client_id, "1.1.1.1") }.to raise_error(PG::Error) + end end it "create connection then delete it" do client_id = "client_id2" user_id = create_user("user2", "user2@jamkazam.com") - @connman.create_connection(user_id, client_id, "1.1.1.1") - + @conn.transaction do + @connman.create_connection(user_id, client_id, "1.1.1.1") + end # make sure the connection is seen @conn.exec("SELECT count(*) FROM connections where user_id = $1", [user_id]) do |result| result.getvalue(0, 0).should == "1" end - @connman.delete_connection(client_id) + @conn.transaction do + @connman.delete_connection(client_id) + end + @conn.exec("SELECT count(*) FROM connections where user_id = $1", [user_id]) do |result| result.getvalue(0, 0).should == "0" end @@ -82,13 +91,16 @@ describe ConnectionManager do friend_update = @message_factory.friend_update(user_id, true) @connman.mq_router.should_receive(:publish_to_friends).with([], friend_update, user_id) - @connman.create_connection(user_id, client_id, "1.1.1.1") - + @conn.transaction do + @connman.create_connection(user_id, client_id, "1.1.1.1") + end # but a second connection from the same user should cause no such message @connman.should_receive(:publish_to_friends).exactly(0).times - @connman.create_connection(user_id, client_id2, "1.1.1.1") + @conn.transaction do + @connman.create_connection(user_id, client_id2, "1.1.1.1") + end end @@ -102,19 +114,29 @@ describe ConnectionManager do # we should get a message saying that this user is online - @connman.create_connection(user_id, client_id, "1.1.1.1") - @connman.create_connection(user_id, client_id2, "1.1.1.1") + @conn.transaction do + @connman.create_connection(user_id, client_id, "1.1.1.1") + end + + @conn.transaction do + @connman.create_connection(user_id, client_id2, "1.1.1.1") + end # deleting one of the two connections should cause no messages @connman.should_receive(:publish_to_friends).exactly(0).times - @connman.delete_connection(client_id) + @conn.transaction do + @connman.delete_connection(client_id) + end # but deleting the final connection should cause a left message friend_update = @message_factory.friend_update(user_id, false) @connman.mq_router.should_receive(:publish_to_friends).with([], friend_update, user_id) - @connman.delete_connection(client_id2) + @conn.transaction do + @connman.delete_connection(client_id2) + end + end @@ -173,16 +195,22 @@ describe ConnectionManager do user_id = create_user("user8", "user8@jamkazam.com") - @connman.create_connection(user_id, client_id, "1.1.1.1") + @conn.transaction do + @connman.create_connection(user_id, client_id, "1.1.1.1") + end - @connman.remove_stale_connections(60) + @conn.transaction do + @connman.remove_stale_connections(60) + end assert_num_connections(client_id, 1) sleep(1) - # this should remove the stale connection - @connman.remove_stale_connections(1) + @conn.transaction do + # this should remove the stale connection + @connman.remove_stale_connections(1) + end assert_num_connections(client_id, 0) end @@ -193,8 +221,10 @@ describe ConnectionManager do user_id = create_user("user9", "user9@jamkazam.com") music_session_id = create_music_session(user_id) - @connman.create_connection(user_id, client_id, "1.1.1.1") - @connman.join_music_session(user_id, client_id, music_session_id) + @conn.transaction do + @connman.create_connection(user_id, client_id, "1.1.1.1") + @connman.join_music_session(user_id, client_id, music_session_id) + end assert_session_exists(music_session_id, true) @@ -202,8 +232,9 @@ describe ConnectionManager do result.getvalue(0, 0).should == music_session_id end - @connman.delete_connection(client_id) - + @conn.transaction do + @connman.delete_connection(client_id) + end assert_num_connections(client_id, 0) assert_session_exists(music_session_id, false) @@ -215,7 +246,10 @@ describe ConnectionManager do user_id = create_user("user10", "user10@jamkazam.com") music_session_id = create_music_session(user_id) - expect { @connman.join_music_session(user_id, client_id, music_session_id)}.to raise_error(JamRuby::StateError) + @conn.transaction do + expect { @connman.join_music_session(user_id, client_id, music_session_id) }.to raise_error(JamRuby::StateError) + end + end it "join_music_session fails if no music_session" do @@ -223,9 +257,14 @@ describe ConnectionManager do client_id = "client_id11" user_id = create_user("user11", "user11@jamkazam.com") - @connman.create_connection(user_id, client_id, "1.1.1.1") + @conn.transaction do + @connman.create_connection(user_id, client_id, "1.1.1.1") + end + + @conn.transaction do + expect { @connman.join_music_session(user_id, client_id, "some_bogus_music_session_id") }.to raise_error(JamRuby::StateError) + end - expect { @connman.join_music_session(user_id, client_id, "some_bogus_music_session_id")}.to raise_error(JamRuby::StateError) end it "leave_music_session fails if no music_session" do @@ -233,9 +272,13 @@ describe ConnectionManager do client_id = "client_id12" user_id = create_user("user12", "user12@jamkazam.com") - @connman.create_connection(user_id, client_id, "1.1.1.1") + @conn.transaction do + @connman.create_connection(user_id, client_id, "1.1.1.1") + end - expect { @connman.leave_music_session(user_id, client_id, "some_bogus_music_session_id")}.to raise_error(JamRuby::StateError) + @conn.transaction do + expect { @connman.leave_music_session(user_id, client_id, "some_bogus_music_session_id") }.to raise_error(JamRuby::StateError) + end end it "leave_music_session fails if in different music_session" do @@ -244,10 +287,17 @@ describe ConnectionManager do user_id = create_user("user13", "user13@jamkazam.com") music_session_id = create_music_session(user_id) - @connman.create_connection(user_id, client_id, "1.1.1.1") - @connman.join_music_session(user_id, client_id, music_session_id) + @conn.transaction do + @connman.create_connection(user_id, client_id, "1.1.1.1") + end - expect { @connman.leave_music_session(user_id, client_id, "some_bogus_music_session_id")}.to raise_error(JamRuby::StateError) + @conn.transaction do + @connman.join_music_session(user_id, client_id, music_session_id) + end + + @conn.transaction do + expect { @connman.leave_music_session(user_id, client_id, "some_bogus_music_session_id") }.to raise_error(JamRuby::StateError) + end end it "leave_music_session works" do @@ -256,8 +306,14 @@ describe ConnectionManager do user_id = create_user("user14", "user14@jamkazam.com") music_session_id = create_music_session(user_id) - @connman.create_connection(user_id, client_id, "1.1.1.1") - @connman.join_music_session(user_id, client_id, music_session_id) + + @conn.transaction do + @connman.create_connection(user_id, client_id, "1.1.1.1") + end + + @conn.transaction do + @connman.join_music_session(user_id, client_id, music_session_id) + end assert_session_exists(music_session_id, true) @@ -265,7 +321,9 @@ describe ConnectionManager do result.getvalue(0, 0).should == music_session_id end - @connman.leave_music_session(user_id, client_id, music_session_id) + @conn.transaction do + @connman.leave_music_session(user_id, client_id, music_session_id) + end @conn.exec("SELECT music_session_id FROM connections WHERE client_id = $1", [client_id]) do |result| result.getvalue(0, 0).should == nil @@ -273,7 +331,9 @@ describe ConnectionManager do assert_session_exists(music_session_id, false) - @connman.delete_connection(client_id) + @conn.transaction do + @connman.delete_connection(client_id) + end assert_num_connections(client_id, 0) From 520225aedfae2352414201c6260fb251d08d5940 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Tue, 23 Oct 2012 06:37:25 -0500 Subject: [PATCH 039/274] * adding support for new heartbeat_interval field on LoginAck --- lib/jam_ruby/connection_manager.rb | 51 +++++++++++++++--------------- lib/jam_ruby/message_factory.rb | 4 +-- 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/lib/jam_ruby/connection_manager.rb b/lib/jam_ruby/connection_manager.rb index bb8100c45..31f438fb2 100644 --- a/lib/jam_ruby/connection_manager.rb +++ b/lib/jam_ruby/connection_manager.rb @@ -39,6 +39,8 @@ module JamRuby end end + @log.debug("deleting #{stale_clients.length} stale connections") + stale_clients.each do |client_id| delete_connection(client_id) end @@ -62,7 +64,6 @@ module JamRuby @mq_router.publish_to_friends(friend_ids, friend_update, user_id) end end - #end end @@ -111,7 +112,6 @@ module JamRuby unless music_session_id.nil? conn.exec("DELETE FROM music_sessions id = $1 AND 0 = (SELECT count(music_session_id) FROM connections where music_session_id = $1)", [music_session_id]).clear end - # end end def destroy_if_empty(conn, music_session_id) @@ -225,34 +225,35 @@ module JamRuby end end end - end - # Creates a connection manager, and associates the connection created by active_record with ourselves - def self.active_record_transaction(&block) - - connection_manager = ConnectionManager.new - ActiveRecord::Base.connection_pool.with_connection do |connection| - # create a transaction, and pass the current connection to ConnectionManager. - # this lets the entire operation work with the same transaction, - # across Rails ActiveRecord and the pg-gem based code in ConnectionManager. - connection_manager.pg_conn = connection.instance_variable_get("@connection") - - block.call(connection_manager) + def lock_connections(conn) + conn.exec("LOCK connections IN EXCLUSIVE MODE").clear end - end - def lock_connections(conn) - conn.exec("LOCK connections IN EXCLUSIVE MODE").clear - end + def gather_friends(conn, user_id) + friend_ids = [] + conn.exec("SELECT f1.friend_id as friend_id FROM friendships f1 WHERE f1.user_id = $1 AND f1.friend_id IN (SELECT f2.user_id FROM friendships f2 WHERE f2.friend_id = $1)", [user_id]) do |friend_results| + friend_results.each do |friend_result| + friend_ids.push(friend_result['friend_id']) + end + end + return friend_ids + end - def gather_friends(conn, user_id) - friend_ids = [] - conn.exec("SELECT f1.friend_id as friend_id FROM friendships f1 WHERE f1.user_id = $1 AND f1.friend_id IN (SELECT f2.user_id FROM friendships f2 WHERE f2.friend_id = $1)", [user_id]) do |friend_results| - friend_results.each do |friend_result| - friend_ids.push(friend_result['friend_id']) + # Creates a connection manager, and associates the connection created by active_record with ourselves + def self.active_record_transaction(&block) + + connection_manager = ConnectionManager.new + ActiveRecord::Base.connection_pool.with_connection do |connection| + # create a transaction, and pass the current connection to ConnectionManager. + # this lets the entire operation work with the same transaction, + # across Rails ActiveRecord and the pg-gem based code in ConnectionManager. + connection_manager.pg_conn = connection.instance_variable_get("@connection") + + connection.transaction do + block.call(connection_manager) + end end end - return friend_ids end -# end end \ No newline at end of file diff --git a/lib/jam_ruby/message_factory.rb b/lib/jam_ruby/message_factory.rb index 630a9f03a..7f5cd30fe 100644 --- a/lib/jam_ruby/message_factory.rb +++ b/lib/jam_ruby/message_factory.rb @@ -37,8 +37,8 @@ end # create a login ack (login was successful) - def login_ack(public_ip, client_id, token) - login_ack = Jampb::LoginAck.new(:public_ip => public_ip, :client_id => client_id, :token => token) + def login_ack(public_ip, client_id, token, heartbeat_interval) + login_ack = Jampb::LoginAck.new(:public_ip => public_ip, :client_id => client_id, :token => token, :heartbeat_interval => heartbeat_interval) return Jampb::ClientMessage.new(:type => ClientMessage::Type::LOGIN_ACK, :route_to => CLIENT_TARGET, :login_ack => login_ack) end From a8f46f8d7d97901692c7ebb02143af2835b8760e Mon Sep 17 00:00:00 2001 From: Seth Call Date: Thu, 25 Oct 2012 07:11:10 -0500 Subject: [PATCH 040/274] * VRFS-21; adding genres field to session CRUD --- lib/jam_ruby.rb | 1 + lib/jam_ruby/models/band.rb | 2 +- lib/jam_ruby/models/friendship.rb | 2 +- lib/jam_ruby/models/genre.rb | 10 ++++++++++ lib/jam_ruby/models/music_session.rb | 24 ++++++++++++++++++++---- lib/jam_ruby/models/musician.rb | 2 +- lib/jam_ruby/models/user.rb | 6 +++--- 7 files changed, 37 insertions(+), 10 deletions(-) create mode 100644 lib/jam_ruby/models/genre.rb diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index f886f77ad..55d61a0ea 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -10,6 +10,7 @@ require "jam_ruby/mq_router" require "jam_ruby/connection_manager" require "jam_ruby/version" require "jam_ruby/message_factory" +require "jam_ruby/models/genre" require "jam_ruby/models/user" require "jam_ruby/models/musician" require "jam_ruby/models/band" diff --git a/lib/jam_ruby/models/band.rb b/lib/jam_ruby/models/band.rb index 4a26f47b2..fda27e818 100644 --- a/lib/jam_ruby/models/band.rb +++ b/lib/jam_ruby/models/band.rb @@ -3,7 +3,7 @@ module JamRuby self.primary_key = 'id' - has_and_belongs_to_many :musicians + has_and_belongs_to_many :musicians, :class_name => "JamRuby::Musician" end end \ No newline at end of file diff --git a/lib/jam_ruby/models/friendship.rb b/lib/jam_ruby/models/friendship.rb index d3ab45e27..924e01c76 100644 --- a/lib/jam_ruby/models/friendship.rb +++ b/lib/jam_ruby/models/friendship.rb @@ -3,7 +3,7 @@ module JamRuby self.primary_key = 'id' - belongs_to :user + belongs_to :user, :class_name => "JamRuby::User" belongs_to :friend, :class_name => "JamRuby::User" end diff --git a/lib/jam_ruby/models/genre.rb b/lib/jam_ruby/models/genre.rb new file mode 100644 index 000000000..c70b0e251 --- /dev/null +++ b/lib/jam_ruby/models/genre.rb @@ -0,0 +1,10 @@ +module JamRuby + class Genre < ActiveRecord::Base + + self.primary_key = 'id' + + has_and_belongs_to_many :musicians, :class_name => "JamRuby::Musician" + has_and_belongs_to_many :music_sessions, :class_name => "JamRuby::MusicSession", :join_table => "genres_music_sessions" + + end +end \ No newline at end of file diff --git a/lib/jam_ruby/models/music_session.rb b/lib/jam_ruby/models/music_session.rb index bf2e35054..07bcf28f4 100644 --- a/lib/jam_ruby/models/music_session.rb +++ b/lib/jam_ruby/models/music_session.rb @@ -6,16 +6,32 @@ module JamRuby has_many :connections, :class_name => "JamRuby::Connection" has_many :users, :through => :connections, :class_name => "JamRuby::User" - + has_and_belongs_to_many :genres, :class_name => "::JamRuby::Genre", :join_table => "genres_music_sessions" + + after_save :require_at_least_one_genre, :limit_to_three_genres + # Verifies that the specified user can join this jam session def access?(user) - #return self.public || self.users.exists?(user) - return true + #return self.public || self.users.exists?(user) + return true end def to_s - return description + return description end + private + + def require_at_least_one_genre + if genres.count == 0 + errors.add(:genres, "Please select at least one genre") + end + end + + def limit_to_three_genres + if genres.count > 3 + errors.add(:genres, "Three genres at most, please") + end + end end end diff --git a/lib/jam_ruby/models/musician.rb b/lib/jam_ruby/models/musician.rb index 1cc55a29d..bf3dbdbb5 100644 --- a/lib/jam_ruby/models/musician.rb +++ b/lib/jam_ruby/models/musician.rb @@ -3,7 +3,7 @@ module JamRuby self.primary_key = 'id' - has_and_belongs_to_many :bands + has_and_belongs_to_many :bands, :class_name => "JamRuby::Band" end end \ No newline at end of file diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index d0fb588b6..38b796d3c 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -13,10 +13,10 @@ module JamRuby has_many :friend_requests, :class_name => "JamRuby::FriendRequest" - has_many :friendships - has_many :friends, :through => :friendships + has_many :friendships, :class_name => "JamRuby::Friendship" + has_many :friends, :through => :friendships, :class_name => "JamRuby::Friend" has_many :inverse_friendships, :class_name => "JamRuby::Friendship", :foreign_key => "friend_id" - has_many :inverse_friends, :through => :inverse_friendships, :source => :user + has_many :inverse_friends, :through => :inverse_friendships, :source => :user, :class_name => "JamRuby::Friend" has_many :music_session_clients, :class_name => "JamRuby::MusicSessionClient" has_many :created_music_sessions, :foreign_key => "user_id", :inverse_of => :user, :class_name => "JamRuby::MusicSession" # sessions *created* by the user From 9ab04a7e74eafed574bff43923123621a1192f5b Mon Sep 17 00:00:00 2001 From: Seth Call Date: Fri, 26 Oct 2012 05:33:39 -0500 Subject: [PATCH 041/274] * invitatations table and model build and tested --- lib/jam_ruby.rb | 1 + lib/jam_ruby/models/friendship.rb | 4 +-- lib/jam_ruby/models/invitation.rb | 25 ++++++++++++++++ lib/jam_ruby/models/music_session.rb | 2 ++ lib/jam_ruby/models/user.rb | 9 ++++-- spec/factories.rb | 8 +++++ spec/jam_ruby/models/invitation_spec.rb | 39 +++++++++++++++++++++++++ 7 files changed, 83 insertions(+), 5 deletions(-) create mode 100644 lib/jam_ruby/models/invitation.rb create mode 100644 spec/jam_ruby/models/invitation_spec.rb diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index 55d61a0ea..e13da46a6 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -17,6 +17,7 @@ require "jam_ruby/models/band" require "jam_ruby/models/connection" require "jam_ruby/models/friendship" require "jam_ruby/models/music_session" +require "jam_ruby/models/invitation" require "jam_ruby/models/friend_request" include Jampb diff --git a/lib/jam_ruby/models/friendship.rb b/lib/jam_ruby/models/friendship.rb index 924e01c76..e8a54f23a 100644 --- a/lib/jam_ruby/models/friendship.rb +++ b/lib/jam_ruby/models/friendship.rb @@ -3,8 +3,8 @@ module JamRuby self.primary_key = 'id' - belongs_to :user, :class_name => "JamRuby::User" - belongs_to :friend, :class_name => "JamRuby::User" + belongs_to :user, :class_name => "JamRuby::User", :foreign_key => "user_id", :inverse_of => :inverse_friendships + belongs_to :friend, :class_name => "JamRuby::User", :foreign_key => "friend_id", :inverse_of => :friendships end end \ No newline at end of file diff --git a/lib/jam_ruby/models/invitation.rb b/lib/jam_ruby/models/invitation.rb new file mode 100644 index 000000000..e636fd418 --- /dev/null +++ b/lib/jam_ruby/models/invitation.rb @@ -0,0 +1,25 @@ +module JamRuby + class Invitation < ActiveRecord::Base + + FRIENDSHIP_REQUIRED_VALIDATION_ERROR = "You can only invite friends" + + self.primary_key = 'id' + belongs_to :sender, :inverse_of => :sent_invitations, :class_name => "JamRuby::User", :foreign_key => "sender_id" + belongs_to :receiver, :inverse_of => :received_invitations, :class_name => "JamRuby::User", :foreign_key => "receiver_id" + belongs_to :music_session, :inverse_of => :invitations, :class_name => "JamRuby::MusicSession" + + validates :sender, :presence => true + validates :receiver, :presence => true + validates :music_session, :presence => true + + validate :require_are_friends + + private + + def require_are_friends + unless receiver.friends.exists? sender + errors.add(:receiver, FRIENDSHIP_REQUIRED_VALIDATION_ERROR) + end + end + end +end diff --git a/lib/jam_ruby/models/music_session.rb b/lib/jam_ruby/models/music_session.rb index 07bcf28f4..af65a24f8 100644 --- a/lib/jam_ruby/models/music_session.rb +++ b/lib/jam_ruby/models/music_session.rb @@ -8,6 +8,8 @@ module JamRuby has_many :users, :through => :connections, :class_name => "JamRuby::User" has_and_belongs_to_many :genres, :class_name => "::JamRuby::Genre", :join_table => "genres_music_sessions" + has_many :invitations, :foreign_key => "music_session_id", :inverse_of => :music_session, :class_name => "JamRuby::Invitation" + after_save :require_at_least_one_genre, :limit_to_three_genres # Verifies that the specified user can join this jam session diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 38b796d3c..ae4e448a4 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -13,14 +13,17 @@ module JamRuby has_many :friend_requests, :class_name => "JamRuby::FriendRequest" - has_many :friendships, :class_name => "JamRuby::Friendship" - has_many :friends, :through => :friendships, :class_name => "JamRuby::Friend" + has_many :friendships, :class_name => "JamRuby::Friendship", :foreign_key => "user_id" + has_many :friends, :through => :friendships, :class_name => "JamRuby::User" has_many :inverse_friendships, :class_name => "JamRuby::Friendship", :foreign_key => "friend_id" - has_many :inverse_friends, :through => :inverse_friendships, :source => :user, :class_name => "JamRuby::Friend" + has_many :inverse_friends, :through => :inverse_friendships, :source => :user, :class_name => "JamRuby::User" has_many :music_session_clients, :class_name => "JamRuby::MusicSessionClient" has_many :created_music_sessions, :foreign_key => "user_id", :inverse_of => :user, :class_name => "JamRuby::MusicSession" # sessions *created* by the user has_many :music_sessions, :through => :music_session_clients, :class_name => "JamRuby::MusicSession" + + has_many :received_invitations, :foreign_key => "receiver_id", :inverse_of => :receiver, :class_name => "JamRuby::Invitation" + has_many :sent_invitations, :foreign_key => "sender_id", :inverse_of => :sender, :class_name => "JamRuby::Invitation" has_secure_password diff --git a/spec/factories.rb b/spec/factories.rb index 75e7f1be2..c30720f96 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -17,4 +17,12 @@ FactoryGirl.define do factory :connection, :class => JamRuby::Connection do end + + factory :invitation, :class => JamRuby::Invitation do + + end + + factory :friendship, :class => JamRuby::Friendship do + + end end \ No newline at end of file diff --git a/spec/jam_ruby/models/invitation_spec.rb b/spec/jam_ruby/models/invitation_spec.rb new file mode 100644 index 000000000..0a6721a97 --- /dev/null +++ b/spec/jam_ruby/models/invitation_spec.rb @@ -0,0 +1,39 @@ +require 'spec_helper' + +describe MusicSession do + + it 'cant create invitation to non-friend' do + + user1 = FactoryGirl.create(:user) # in the jam session + user2 = FactoryGirl.create(:user) # in the jam session + + music_session = FactoryGirl.create(:music_session, :creator => user1) + + music_session_member1 = FactoryGirl.create(:connection, :user => user1, :music_session => music_session, :ip_address => "1.1.1.1", :client_id => "1") + music_session_member2 = FactoryGirl.create(:connection, :user => user2, :music_session => music_session, :ip_address => "2.2.2.2", :client_id => "2") + + invitation = Invitation.new(:sender => user1, :receiver => user2, :music_session => music_session) + + invitation.save.should == false + invitation.errors.size.should == 1 + invitation.errors.get(:receiver).should == [Invitation::FRIENDSHIP_REQUIRED_VALIDATION_ERROR] + end + + it 'can create invitation to friend' do + + user1 = FactoryGirl.create(:user) # in the jam session + user2 = FactoryGirl.create(:user) # in the jam session + + music_session = FactoryGirl.create(:music_session, :creator => user1) + + music_session_member1 = FactoryGirl.create(:connection, :user => user1, :music_session => music_session, :ip_address => "1.1.1.1", :client_id => "1") + music_session_member2 = FactoryGirl.create(:connection, :user => user2, :music_session => music_session, :ip_address => "2.2.2.2", :client_id => "2") + + FactoryGirl.create(:friendship, :user => user1, :friend => user2) + FactoryGirl.create(:friendship, :user => user2, :friend => user1) + + invitation = Invitation.new(:sender => user1, :receiver => user2, :music_session => music_session) + + invitation.save.should == true + end +end From f4149648c6c7c7ff8e857d96cafc4b2eff20c9cc Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sat, 27 Oct 2012 14:22:05 -0500 Subject: [PATCH 042/274] * VRFS-27 invitation model and tests added --- lib/jam_ruby/message_factory.rb | 4 ++++ lib/jam_ruby/models/invitation.rb | 9 ++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/jam_ruby/message_factory.rb b/lib/jam_ruby/message_factory.rb index 7f5cd30fe..85838c19e 100644 --- a/lib/jam_ruby/message_factory.rb +++ b/lib/jam_ruby/message_factory.rb @@ -96,6 +96,10 @@ return Jampb::ClientMessage.new(:type => ClientMessage::Type::TEST_SESSION_MESSAGE, :route_to => SESSION_TARGET_PREFIX + session_id, :test_session_message => test) end + def session_invitation(receiver_id, invitation_id) + session_invitation = Jampb::SessionInvitation.new(:invitation => invitation_id) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::SESSION_INVITATION, :route_to => USER_TARGET_PREFIX + receiver_id, :session_invitation => session_invitation) + end ############## P2P CLIENT MESSAGES ################# # send a request to do a ping diff --git a/lib/jam_ruby/models/invitation.rb b/lib/jam_ruby/models/invitation.rb index e636fd418..239a9b2a2 100644 --- a/lib/jam_ruby/models/invitation.rb +++ b/lib/jam_ruby/models/invitation.rb @@ -2,6 +2,7 @@ module JamRuby class Invitation < ActiveRecord::Base FRIENDSHIP_REQUIRED_VALIDATION_ERROR = "You can only invite friends" + MEMBERSHIP_REQUIRED_OF_MUSIC_SESSION = "You must be a member of the music session to send invitations on behalf of it" self.primary_key = 'id' belongs_to :sender, :inverse_of => :sent_invitations, :class_name => "JamRuby::User", :foreign_key => "sender_id" @@ -12,10 +13,16 @@ module JamRuby validates :receiver, :presence => true validates :music_session, :presence => true - validate :require_are_friends + validate :require_sender_in_music_session, :require_are_friends private + def require_sender_in_music_session + unless music_session.users.exists? sender + errors.add(:music_session, MEMBERSHIP_REQUIRED_OF_MUSIC_SESSION) + end + end + def require_are_friends unless receiver.friends.exists? sender errors.add(:receiver, FRIENDSHIP_REQUIRED_VALIDATION_ERROR) From 229ab95d3455526302f53f817e97daf463f6b533 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Sat, 27 Oct 2012 22:35:28 -0400 Subject: [PATCH 043/274] updates for musicians --- lib/jam_ruby.rb | 2 +- lib/jam_ruby/models/band.rb | 5 +++-- lib/jam_ruby/models/friend_request.rb | 4 ++++ lib/jam_ruby/models/genre.rb | 4 ++-- lib/jam_ruby/models/instrument.rb | 10 ++++++++++ lib/jam_ruby/models/user.rb | 1 + 6 files changed, 21 insertions(+), 5 deletions(-) create mode 100644 lib/jam_ruby/models/instrument.rb diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index e13da46a6..71428d3c2 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -12,13 +12,13 @@ require "jam_ruby/version" require "jam_ruby/message_factory" require "jam_ruby/models/genre" require "jam_ruby/models/user" -require "jam_ruby/models/musician" require "jam_ruby/models/band" require "jam_ruby/models/connection" require "jam_ruby/models/friendship" require "jam_ruby/models/music_session" require "jam_ruby/models/invitation" require "jam_ruby/models/friend_request" +require "jam_ruby/models/instrument" include Jampb diff --git a/lib/jam_ruby/models/band.rb b/lib/jam_ruby/models/band.rb index fda27e818..4ea0bb19e 100644 --- a/lib/jam_ruby/models/band.rb +++ b/lib/jam_ruby/models/band.rb @@ -1,9 +1,10 @@ module JamRuby class Band < ActiveRecord::Base - self.primary_key = 'id' + self.primary_key = 'id' - has_and_belongs_to_many :musicians, :class_name => "JamRuby::Musician" + has_and_belongs_to_many :users, :class_name => "JamRuby::User" + has_many :genres, :class_name => "JamRuby::Genre" end end \ No newline at end of file diff --git a/lib/jam_ruby/models/friend_request.rb b/lib/jam_ruby/models/friend_request.rb index c9dd377f5..6767aa685 100644 --- a/lib/jam_ruby/models/friend_request.rb +++ b/lib/jam_ruby/models/friend_request.rb @@ -9,5 +9,9 @@ module JamRuby validates :user_id, :presence => true validates :friend_id, :presence => true + def to_s + return "#{self.user.to_s}:#{self.friend.to_s}" + end + end end \ No newline at end of file diff --git a/lib/jam_ruby/models/genre.rb b/lib/jam_ruby/models/genre.rb index c70b0e251..fb3d692a0 100644 --- a/lib/jam_ruby/models/genre.rb +++ b/lib/jam_ruby/models/genre.rb @@ -1,9 +1,9 @@ module JamRuby class Genre < ActiveRecord::Base - self.primary_key = 'id' + self.primary_key = 'id' - has_and_belongs_to_many :musicians, :class_name => "JamRuby::Musician" + has_and_belongs_to_many :bands, :class_name => "JamRuby::Band" has_and_belongs_to_many :music_sessions, :class_name => "JamRuby::MusicSession", :join_table => "genres_music_sessions" end diff --git a/lib/jam_ruby/models/instrument.rb b/lib/jam_ruby/models/instrument.rb new file mode 100644 index 000000000..3c3214334 --- /dev/null +++ b/lib/jam_ruby/models/instrument.rb @@ -0,0 +1,10 @@ +module JamRuby + class Instrument < ActiveRecord::Base + + self.primary_key = 'id' + + has_and_belongs_to_many :users, :class_name => "JamRuby::User" + has_and_belongs_to_many :music_sessions, :class_name => "JamRuby::MusicSession", :join_table => "genres_music_sessions" + + end +end \ No newline at end of file diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index ae4e448a4..96080ccb5 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -12,6 +12,7 @@ module JamRuby has_many :connections, :class_name => "JamRuby::Connection" has_many :friend_requests, :class_name => "JamRuby::FriendRequest" + has_and_belongs_to_many :bands, :class_name => "JamRuby::Band" has_many :friendships, :class_name => "JamRuby::Friendship", :foreign_key => "user_id" has_many :friends, :through => :friendships, :class_name => "JamRuby::User" From 96fbb23c34cccdb207e74d52f641ffb1ab3d2964 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Sat, 27 Oct 2012 22:38:25 -0400 Subject: [PATCH 044/274] removed musician model --- lib/jam_ruby/models/musician.rb | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 lib/jam_ruby/models/musician.rb diff --git a/lib/jam_ruby/models/musician.rb b/lib/jam_ruby/models/musician.rb deleted file mode 100644 index bf3dbdbb5..000000000 --- a/lib/jam_ruby/models/musician.rb +++ /dev/null @@ -1,9 +0,0 @@ -module JamRuby - class Musician < JamRuby::User - - self.primary_key = 'id' - - has_and_belongs_to_many :bands, :class_name => "JamRuby::Band" - - end -end \ No newline at end of file From 74e03cd89797fd86b75d34502cb1538da96b9ac2 Mon Sep 17 00:00:00 2001 From: tihot_jk Date: Sun, 28 Oct 2012 17:29:07 -0700 Subject: [PATCH 045/274] Add session_id to the notification massage for join/leave session. --- lib/jam_ruby/message_factory.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/jam_ruby/message_factory.rb b/lib/jam_ruby/message_factory.rb index 85838c19e..f7a9eb19a 100644 --- a/lib/jam_ruby/message_factory.rb +++ b/lib/jam_ruby/message_factory.rb @@ -85,8 +85,8 @@ end # create a user-joined session message - def user_joined_music_session(user_id, username) - joined = Jampb::UserJoinedMusicSession.new(:user_id => user_id, :username => username) + def user_joined_music_session(session_id, user_id, username) + joined = Jampb::UserJoinedMusicSession.new(:session_id => session_id, :user_id => user_id, :username => username) return Jampb::ClientMessage.new(:type => ClientMessage::Type::USER_JOINED_MUSIC_SESSION, :route_to => CLIENT_TARGET, :user_joined_music_session => joined) end From 662fcf01121c29f84334a55bf871600dd8a85d10 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Mon, 29 Oct 2012 06:45:47 -0400 Subject: [PATCH 046/274] initial musician / band dev --- lib/jam_ruby.rb | 1 + lib/jam_ruby/models/band.rb | 12 ++++++++ lib/jam_ruby/models/instrument.rb | 6 +++- lib/jam_ruby/models/user.rb | 39 +++++++++++++++++++++++--- lib/jam_ruby/models/user_instrument.rb | 10 +++++++ 5 files changed, 63 insertions(+), 5 deletions(-) create mode 100644 lib/jam_ruby/models/user_instrument.rb diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index 71428d3c2..8887749ea 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -19,6 +19,7 @@ require "jam_ruby/models/music_session" require "jam_ruby/models/invitation" require "jam_ruby/models/friend_request" require "jam_ruby/models/instrument" +require "jam_ruby/models/user_instrument" include Jampb diff --git a/lib/jam_ruby/models/band.rb b/lib/jam_ruby/models/band.rb index 4ea0bb19e..663a2a40d 100644 --- a/lib/jam_ruby/models/band.rb +++ b/lib/jam_ruby/models/band.rb @@ -1,10 +1,22 @@ module JamRuby class Band < ActiveRecord::Base + attr_accessor :photo_url, :logo_url + self.primary_key = 'id' has_and_belongs_to_many :users, :class_name => "JamRuby::User" has_many :genres, :class_name => "JamRuby::Genre" + def photo_url + # TODO: move image path to config + @photo_url = "http://www.jamkazam.com/images/bands/photos/#{self.id}.gif"; + end + + def logo_url + # TODO: move image path to config + @logo_url = "http://www.jamkazam.com/images/bands/logos/#{self.id}.gif"; + end + end end \ No newline at end of file diff --git a/lib/jam_ruby/models/instrument.rb b/lib/jam_ruby/models/instrument.rb index 3c3214334..db3019be9 100644 --- a/lib/jam_ruby/models/instrument.rb +++ b/lib/jam_ruby/models/instrument.rb @@ -3,7 +3,11 @@ module JamRuby self.primary_key = 'id' - has_and_belongs_to_many :users, :class_name => "JamRuby::User" + # users + has_many :user_instruments + has_many :users, :through => :user_instruments, :class_name => "JamRuby::User" + + # music sessions has_and_belongs_to_many :music_sessions, :class_name => "JamRuby::MusicSession", :join_table => "genres_music_sessions" end diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 96080ccb5..6ca9df5c6 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -2,27 +2,35 @@ module JamRuby class User < ActiveRecord::Base attr_accessible :name, :email, :password, :password_confirmation - attr_accessor :updating_password, :online + attr_accessor :updating_password, :online, :photo_url self.primary_key = 'id' - - # re-visit later (used for multiple table inheritance w/ Musician model) - # belongs_to :biz, :polymorphic => true + # connections (websocket-gateway) has_many :connections, :class_name => "JamRuby::Connection" + # friend requests has_many :friend_requests, :class_name => "JamRuby::FriendRequest" + + # instruments + has_many :user_instruments + has_many :instruments, :through => :user_instruments, :class_name => "JamRuby::Instrument" + + # bands has_and_belongs_to_many :bands, :class_name => "JamRuby::Band" + # friends has_many :friendships, :class_name => "JamRuby::Friendship", :foreign_key => "user_id" has_many :friends, :through => :friendships, :class_name => "JamRuby::User" has_many :inverse_friendships, :class_name => "JamRuby::Friendship", :foreign_key => "friend_id" has_many :inverse_friends, :through => :inverse_friendships, :source => :user, :class_name => "JamRuby::User" + # music sessions has_many :music_session_clients, :class_name => "JamRuby::MusicSessionClient" has_many :created_music_sessions, :foreign_key => "user_id", :inverse_of => :user, :class_name => "JamRuby::MusicSession" # sessions *created* by the user has_many :music_sessions, :through => :music_session_clients, :class_name => "JamRuby::MusicSession" + # invitations has_many :received_invitations, :foreign_key => "receiver_id", :inverse_of => :receiver, :class_name => "JamRuby::Invitation" has_many :sent_invitations, :foreign_key => "sender_id", :inverse_of => :sender, :class_name => "JamRuby::Invitation" @@ -46,6 +54,11 @@ module JamRuby return @online end + def photo_url + # TODO: move image path to config + @photo_url = "http://www.jamkazam.com/images/users/photos/#{self.id}.gif"; + end + def should_validate_password? updating_password || new_record? end @@ -60,6 +73,24 @@ module JamRuby return id end + def self.save(params) + if params[:id].nil? + user = User.new() + else + user = User.find(params[:id]) + end + + @user = User.find(params[:id]) + @user.email = params[:email] + @user.musician = params[:musician] + if @user.musician + @user.instruments = params[:instruments] + @user.bands = params[:bands] + end + @user.save + return user + end + private def create_remember_token self.remember_token = SecureRandom.urlsafe_base64 diff --git a/lib/jam_ruby/models/user_instrument.rb b/lib/jam_ruby/models/user_instrument.rb new file mode 100644 index 000000000..080b8def7 --- /dev/null +++ b/lib/jam_ruby/models/user_instrument.rb @@ -0,0 +1,10 @@ +module JamRuby + class UserInstrument < ActiveRecord::Base + + self.table_name = "users_instruments" + + belongs_to :user + belongs_to :instrument + + end +end \ No newline at end of file From 6ba6b7d5958bfc8e73be0190f9238ab36290002c Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Mon, 29 Oct 2012 20:56:51 -0400 Subject: [PATCH 047/274] fix instruments part of payload --- lib/jam_ruby/models/user.rb | 2 +- lib/jam_ruby/models/user_instrument.rb | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 6ca9df5c6..f5389e01b 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -2,7 +2,7 @@ module JamRuby class User < ActiveRecord::Base attr_accessible :name, :email, :password, :password_confirmation - attr_accessor :updating_password, :online, :photo_url + attr_accessor :updating_password self.primary_key = 'id' diff --git a/lib/jam_ruby/models/user_instrument.rb b/lib/jam_ruby/models/user_instrument.rb index 080b8def7..8da3a70fd 100644 --- a/lib/jam_ruby/models/user_instrument.rb +++ b/lib/jam_ruby/models/user_instrument.rb @@ -6,5 +6,9 @@ module JamRuby belongs_to :user belongs_to :instrument + def description + @description = self.instrument.description + end + end end \ No newline at end of file From bc3b0e5c5773fc4f6c82e448e7f8504ed66f4ee7 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Tue, 30 Oct 2012 01:42:16 -0400 Subject: [PATCH 048/274] add bands to user details API, renamed band/musician related user models --- lib/jam_ruby.rb | 3 ++- lib/jam_ruby/models/band.rb | 14 ++++++----- lib/jam_ruby/models/band_musician.rb | 29 ++++++++++++++++++++++ lib/jam_ruby/models/genre.rb | 5 +++- lib/jam_ruby/models/instrument.rb | 4 +-- lib/jam_ruby/models/musician_instrument.rb | 16 ++++++++++++ lib/jam_ruby/models/user.rb | 7 +++--- 7 files changed, 65 insertions(+), 13 deletions(-) create mode 100644 lib/jam_ruby/models/band_musician.rb create mode 100644 lib/jam_ruby/models/musician_instrument.rb diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index 8887749ea..4cd423c8e 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -19,7 +19,8 @@ require "jam_ruby/models/music_session" require "jam_ruby/models/invitation" require "jam_ruby/models/friend_request" require "jam_ruby/models/instrument" -require "jam_ruby/models/user_instrument" +require "jam_ruby/models/musician_instrument" +require "jam_ruby/models/band_musician" include Jampb diff --git a/lib/jam_ruby/models/band.rb b/lib/jam_ruby/models/band.rb index 663a2a40d..904408115 100644 --- a/lib/jam_ruby/models/band.rb +++ b/lib/jam_ruby/models/band.rb @@ -1,21 +1,23 @@ module JamRuby class Band < ActiveRecord::Base - attr_accessor :photo_url, :logo_url - self.primary_key = 'id' - has_and_belongs_to_many :users, :class_name => "JamRuby::User" - has_many :genres, :class_name => "JamRuby::Genre" + # musicians + has_many :band_musicians + has_many :users, :through => :band_musicians, :class_name => "JamRuby::User" + + # genres + has_and_belongs_to_many :genres, :class_name => "JamRuby::Genre", :join_table => "bands_genres" def photo_url # TODO: move image path to config - @photo_url = "http://www.jamkazam.com/images/bands/photos/#{self.id}.gif"; + @photo_url = "http://www.jamkazam.com/images/bands/photos/#{self.id}.gif" end def logo_url # TODO: move image path to config - @logo_url = "http://www.jamkazam.com/images/bands/logos/#{self.id}.gif"; + @logo_url = "http://www.jamkazam.com/images/bands/logos/#{self.id}.gif" end end diff --git a/lib/jam_ruby/models/band_musician.rb b/lib/jam_ruby/models/band_musician.rb new file mode 100644 index 000000000..a750bafe4 --- /dev/null +++ b/lib/jam_ruby/models/band_musician.rb @@ -0,0 +1,29 @@ +module JamRuby + class BandMusician < ActiveRecord::Base + + self.table_name = "bands_musicians" + + self.primary_key = 'id' + + belongs_to :user + belongs_to :band + + # name, genres, photo_url, and logo_url are needed here for the RABL file + def name + @name = self.band.name + end + + def genres + @genres = self.band.genres + end + + def photo_url + @photo_url = self.band.photo_url + end + + def logo_url + @logo_url = self.band.logo_url + end + + end +end \ No newline at end of file diff --git a/lib/jam_ruby/models/genre.rb b/lib/jam_ruby/models/genre.rb index fb3d692a0..1649825e2 100644 --- a/lib/jam_ruby/models/genre.rb +++ b/lib/jam_ruby/models/genre.rb @@ -3,7 +3,10 @@ module JamRuby self.primary_key = 'id' - has_and_belongs_to_many :bands, :class_name => "JamRuby::Band" + # bands + has_and_belongs_to_many :bands, :class_name => "JamRuby::Band", :join_table => "bands_genres" + + # music sessions has_and_belongs_to_many :music_sessions, :class_name => "JamRuby::MusicSession", :join_table => "genres_music_sessions" end diff --git a/lib/jam_ruby/models/instrument.rb b/lib/jam_ruby/models/instrument.rb index db3019be9..9119d6a94 100644 --- a/lib/jam_ruby/models/instrument.rb +++ b/lib/jam_ruby/models/instrument.rb @@ -4,8 +4,8 @@ module JamRuby self.primary_key = 'id' # users - has_many :user_instruments - has_many :users, :through => :user_instruments, :class_name => "JamRuby::User" + has_many :musician_instruments + has_many :users, :through => :musician_instruments, :class_name => "JamRuby::User" # music sessions has_and_belongs_to_many :music_sessions, :class_name => "JamRuby::MusicSession", :join_table => "genres_music_sessions" diff --git a/lib/jam_ruby/models/musician_instrument.rb b/lib/jam_ruby/models/musician_instrument.rb new file mode 100644 index 000000000..2fd11e64e --- /dev/null +++ b/lib/jam_ruby/models/musician_instrument.rb @@ -0,0 +1,16 @@ +module JamRuby + class MusicianInstrument < ActiveRecord::Base + + self.table_name = "musicians_instruments" + + self.primary_key = 'id' + + belongs_to :user + belongs_to :instrument + + def description + @description = self.instrument.description + end + + end +end \ No newline at end of file diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index f5389e01b..f80869d61 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -13,11 +13,12 @@ module JamRuby has_many :friend_requests, :class_name => "JamRuby::FriendRequest" # instruments - has_many :user_instruments - has_many :instruments, :through => :user_instruments, :class_name => "JamRuby::Instrument" + has_many :musician_instruments + has_many :instruments, :through => :musician_instruments, :class_name => "JamRuby::Instrument" # bands - has_and_belongs_to_many :bands, :class_name => "JamRuby::Band" + has_many :band_musicians + has_many :bands, :through => :band_musicians, :class_name => "JamRuby::Band" # friends has_many :friendships, :class_name => "JamRuby::Friendship", :foreign_key => "user_id" From 6658c57a07ce249fba040cc9cf75433c1baa7fbe Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Tue, 30 Oct 2012 23:26:43 -0400 Subject: [PATCH 049/274] removed UserInstrument model --- lib/jam_ruby/models/user_instrument.rb | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 lib/jam_ruby/models/user_instrument.rb diff --git a/lib/jam_ruby/models/user_instrument.rb b/lib/jam_ruby/models/user_instrument.rb deleted file mode 100644 index 8da3a70fd..000000000 --- a/lib/jam_ruby/models/user_instrument.rb +++ /dev/null @@ -1,14 +0,0 @@ -module JamRuby - class UserInstrument < ActiveRecord::Base - - self.table_name = "users_instruments" - - belongs_to :user - belongs_to :instrument - - def description - @description = self.instrument.description - end - - end -end \ No newline at end of file From 2edaf4ca71fa6c05a819692ebe578976be821d3a Mon Sep 17 00:00:00 2001 From: Seth Call Date: Fri, 2 Nov 2012 01:51:52 -0500 Subject: [PATCH 050/274] * list sessions has correct sort order --- lib/jam_ruby/connection_manager.rb | 22 ++- lib/jam_ruby/models/music_session.rb | 66 +++++++- lib/jam_ruby/models/user.rb | 5 +- lib/jam_ruby/mq_router.rb | 2 +- spec/factories.rb | 3 +- spec/jam_ruby/connection_manager_spec.rb | 20 ++- spec/jam_ruby/models/music_session_spec.rb | 178 ++++++++++++++++++++- 7 files changed, 268 insertions(+), 28 deletions(-) diff --git a/lib/jam_ruby/connection_manager.rb b/lib/jam_ruby/connection_manager.rb index 31f438fb2..d3adb12a6 100644 --- a/lib/jam_ruby/connection_manager.rb +++ b/lib/jam_ruby/connection_manager.rb @@ -168,15 +168,23 @@ module JamRuby previous_music_session_id = check_already_session(conn, client_id) + # determine if the user can join; if not, throw a PermissionError + music_session = MusicSession.find(music_session_id) + unless music_session.can_join?(User.find(user_id)) + @log.debug "user can not join a session user_id=#{user_id} and client_id=#{client_id}" + raise PermissionError, "unable to join the specified session" + end + begin - conn.exec("UPDATE connections SET music_session_id = $1 WHERE client_id = $2", [music_session_id, client_id]) do |result| + # we include user_id in the query as an act o security, so that a user can't access someone else' client connection + conn.exec("UPDATE connections SET music_session_id = $1 WHERE client_id = $2 and user_id = $3", [music_session_id, client_id, user_id]) do |result| if result.cmd_tuples == 1 - @log.debug "associated music_session with connection for client=#{client_id} and music_session=#{music_session_id}" + @log.debug "associated music_session with connection for client=#{client_id}, music_session=#{music_session_id}, and user=#{user_id}" session_checks(conn, previous_music_session_id, user_id) elsif result.cmd_tuples == 0 - @log.debug "join_music_session no connection found with client_id=#{client_id}" - raise StateError, "no connection found with client=#{client_id}" + @log.debug "join_music_session no connection found with client_id=#{client_id} and user_id=#{user_id}" + raise ActiveRecord::RecordNotFound else @log.error "database failure or logic error; this path should be impossible if the table is locked (join_music_session)" raise Exception, "locked table changed state" @@ -211,14 +219,14 @@ module JamRuby end # can throw exception if the session is deleted just before this - conn.exec("UPDATE connections SET music_session_id = NULL WHERE client_id = $1", [client_id]) do |result| + conn.exec("UPDATE connections SET music_session_id = NULL WHERE client_id = $1 AND user_id =$2", [client_id, user_id]) do |result| if result.cmd_tuples == 1 - @log.debug("deassociated music_session with connection for client_id #{client_id}") + @log.debug("deassociated music_session with connection for client_id=#{client_id}, user_id=#{user_id}") session_checks(conn, previous_music_session_id, user_id) elsif result.cmd_tuples == 0 @log.debug "leave_music_session no connection found with client_id=#{client_id}" - raise StateError, "no connection found with client=#{client_id}" + raise ActiveRecord::RecordNotFound else @log.error("database failure or logic error; this path should be impossible if the table is locked (leave_music_session)") raise Exception, "locked table changed state" diff --git a/lib/jam_ruby/models/music_session.rb b/lib/jam_ruby/models/music_session.rb index af65a24f8..b9383f7aa 100644 --- a/lib/jam_ruby/models/music_session.rb +++ b/lib/jam_ruby/models/music_session.rb @@ -2,20 +2,76 @@ module JamRuby class MusicSession < ActiveRecord::Base self.primary_key = 'id' + belongs_to :creator, :inverse_of => :music_sessions, :class_name => "JamRuby::User", :foreign_key => "user_id" has_many :connections, :class_name => "JamRuby::Connection" has_many :users, :through => :connections, :class_name => "JamRuby::User" has_and_belongs_to_many :genres, :class_name => "::JamRuby::Genre", :join_table => "genres_music_sessions" - has_many :invitations, :foreign_key => "music_session_id", :inverse_of => :music_session, :class_name => "JamRuby::Invitation" + has_many :invited_users, :through => :invitations, :class_name => "JamRuby::User", :foreign_key => "receiver_id", :source => :receiver after_save :require_at_least_one_genre, :limit_to_three_genres - # Verifies that the specified user can join this jam session - def access?(user) - #return self.public || self.users.exists?(user) - return true + def self.index(current_user) + #SELECT "music_sessions"."id" AS t0_r0, "music_sessions"."description" AS t0_r1, "music_sessions"."user_id" AS t0_r2, "music_sessions"."created_at" AS t0_r3, "music_sessions"."updated_at" AS t0_r4, "music_sessions"."musician_access" AS t0_r5, "invitations"."id" AS t1_r0, "invitations"."sender_id" AS t1_r1, "invitations"."receiver_id" AS t1_r2, "invitations"."music_session_id" AS t1_r3, "invitations"."created_at" AS t1_r4, "invitations"."updated_at" AS t1_r5, "connections"."id" AS t2_r0, "connections"."user_id" AS t2_r1, "connections"."client_id" AS t2_r2, "connections"."created_at" AS t2_r3, "connections"."updated_at" AS t2_r4, "connections"."music_session_id" AS t2_r5, "connections"."ip_address" AS t2_r6, "users"."id" AS t3_r0, "users"."name" AS t3_r1, "users"."email" AS t3_r2, "users"."remember_token" AS t3_r3, "users"."password_digest" AS t3_r4, "users"."admin" AS t3_r5, "users"."created_at" AS t3_r6, "users"."updated_at" AS t3_r7, "friendships"."id" AS t4_r0, "friendships"."user_id" AS t4_r1, "friendships"."friend_id" AS t4_r2, "friendships"."created_at" AS t4_r3, "friendships"."updated_at" AS t4_r4 FROM "music_sessions" LEFT OUTER JOIN "invitations" ON "invitations"."music_session_id" = "music_sessions"."id" LEFT OUTER JOIN "connections" ON "connections"."music_session_id" = "music_sessions"."id" LEFT OUTER JOIN "users" ON "users"."id" = "connections"."user_id" LEFT OUTER JOIN "friendships" ON "friendships"."user_id" = "users"."id" WHERE (musician_access = true OR invitations.receiver_id = '1' OR friendships.friend_id = '1') ORDER BY (invitations.id is NULL) ASC, (friendships.id is NULL) ASC, music_sessions.created_at DESC + + return MusicSession.includes(:invitations, :connections => [:user => [:friendships]]).order("(invitations.id is NULL) ASC, (friendships.friend_id != '#{current_user.id}' OR friendships.friend_id is NULL) ASC, music_sessions.created_at DESC").where("invitations.receiver_id = '#{current_user.id}' OR (friendships.friend_id = '#{current_user.id}') OR musician_access = true") + #.select(["music_sessions.*", "(friendships.friend_id is not NULL) as is_friend"]) + #select(["music_sessions.*", "distinct(music_sessions.id)"]) + #return MusicSession.joins("LEFT OUTER JOIN invitations ON invitations.music_session_id = music_sessions.id LEFT OUTER JOIN connections ON connections.music_session_id = music_sessions.id LEFT OUTER JOIN users ON users.id = connections.user_id LEFT OUTER JOIN friendships ON friendships.user_id = users.id").order('(invitations.id is NULL) ASC, (friendships.id is NULL) ASC, music_sessions.created_at DESC').where("musician_access = true OR invitations.receiver_id = '#{current_user.id}' OR friendships.friend_id = '#{current_user.id}'") + end + + def list(musician_access, invited, friends, page) + + query = MusicSession + + unless musician_access + query = query.unscoped + end + + query = query.joins('LEFT OUTER JOIN connections ON music_sessions.id = connections.music_session_id') + + if invited + query = query.joins("LEFT OUTER JOIN invitations ON invitations.receiver_id = ") + end + + if friends + query = query.unscoped.joins("LEFT OUTER JOIN friendships ON friend_id.receiver_id = '" + current_user.id + "'") + end + + return query.paginate(page: page) + end + + # Verifies that the specified user can join this music session + def can_join? user + if self.musician_access + return true + else + # the creator can always join, and the invited users can join + return self.creator == user || self.invited_users.exists?(user) + end + end + + # Verifies that the specified user can see this music session + def can_see? user + if self.musician_access + return true + else + # the creator can always see, and the invited users can see it too + return self.creator == user || self.invited_users.exists?(user) + end + end + + # Verifies that the specified user can delete this music session + def can_delete? user + # the creator can delete + return self.creator == user + end + + + def access? user + return self.users.exists? user end def to_s diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index ae4e448a4..7609cc5fc 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -17,11 +17,8 @@ module JamRuby has_many :friends, :through => :friendships, :class_name => "JamRuby::User" has_many :inverse_friendships, :class_name => "JamRuby::Friendship", :foreign_key => "friend_id" has_many :inverse_friends, :through => :inverse_friendships, :source => :user, :class_name => "JamRuby::User" - - has_many :music_session_clients, :class_name => "JamRuby::MusicSessionClient" has_many :created_music_sessions, :foreign_key => "user_id", :inverse_of => :user, :class_name => "JamRuby::MusicSession" # sessions *created* by the user - has_many :music_sessions, :through => :music_session_clients, :class_name => "JamRuby::MusicSession" - + has_many :music_sessions, :through => :connections, :class_name => "JamRuby::MusicSession" has_many :received_invitations, :foreign_key => "receiver_id", :inverse_of => :receiver, :class_name => "JamRuby::Invitation" has_many :sent_invitations, :foreign_key => "sender_id", :inverse_of => :sender, :class_name => "JamRuby::Invitation" diff --git a/lib/jam_ruby/mq_router.rb b/lib/jam_ruby/mq_router.rb index 4ca06e774..722d1e0e4 100644 --- a/lib/jam_ruby/mq_router.rb +++ b/lib/jam_ruby/mq_router.rb @@ -19,7 +19,7 @@ class MQRouter end if !music_session.access? user - raise PermissionError, 'not allowed to join the specified session' + raise PermissionError, 'not allowed to access the specified session' end return music_session diff --git a/spec/factories.rb b/spec/factories.rb index c30720f96..6ccd2c674 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -12,10 +12,11 @@ FactoryGirl.define do factory :music_session, :class => JamRuby::MusicSession do sequence(:description) { |n| "Music Session #{n}" } + musician_access true end factory :connection, :class => JamRuby::Connection do - + sequence(:client_id) { |n| "Client#{n}" } end factory :invitation, :class => JamRuby::Invitation do diff --git a/spec/jam_ruby/connection_manager_spec.rb b/spec/jam_ruby/connection_manager_spec.rb index 4cb838957..fd660fa3f 100644 --- a/spec/jam_ruby/connection_manager_spec.rb +++ b/spec/jam_ruby/connection_manager_spec.rb @@ -18,7 +18,7 @@ describe ConnectionManager do def create_music_session(user_id) description = "some session" - @conn.exec("INSERT INTO music_sessions (user_id, description) VALUES ($1, $2) RETURNING id", [user_id, description]) do |result| + @conn.exec("INSERT INTO music_sessions (user_id, description, musician_access) VALUES ($1, $2, $3) RETURNING id", [user_id, description, true]) do |result| return result.getvalue(0, 0) end end @@ -247,11 +247,25 @@ describe ConnectionManager do music_session_id = create_music_session(user_id) @conn.transaction do - expect { @connman.join_music_session(user_id, client_id, music_session_id) }.to raise_error(JamRuby::StateError) + expect { @connman.join_music_session(user_id, client_id, music_session_id) }.to raise_error(ActiveRecord::RecordNotFound) end end + it "join_music_session fails if incorrect user_id specified" do + + client_id = "client_id20" + user_id = create_user("user20", "user20@jamkazam.com") + user_id2 = create_user("user21", "user21@jamkazam.com") + music_session_id = create_music_session(user_id) + + @conn.transaction do + @connman.create_connection(user_id, client_id, "1.1.1.1") + # specify real user id, but not associated with this session + expect { @connman.join_music_session(user_id2, client_id, music_session_id) } .to raise_error(ActiveRecord::RecordNotFound) + end + end + it "join_music_session fails if no music_session" do client_id = "client_id11" @@ -262,7 +276,7 @@ describe ConnectionManager do end @conn.transaction do - expect { @connman.join_music_session(user_id, client_id, "some_bogus_music_session_id") }.to raise_error(JamRuby::StateError) + expect { @connman.join_music_session(user_id, client_id, "some_bogus_music_session_id") }.to raise_error(ActiveRecord::RecordNotFound) end end diff --git a/spec/jam_ruby/models/music_session_spec.rb b/spec/jam_ruby/models/music_session_spec.rb index 4560a57d0..b56248f17 100644 --- a/spec/jam_ruby/models/music_session_spec.rb +++ b/spec/jam_ruby/models/music_session_spec.rb @@ -2,19 +2,183 @@ require 'spec_helper' describe MusicSession do + before(:each) do + MusicSession.delete_all + end it 'can grant access to valid user' do - user1 = FactoryGirl.create(:user) # in the jam session - user2 = FactoryGirl.create(:user) # in the jam session - user3 = FactoryGirl.create(:user) # not in the jam session + user1 = FactoryGirl.create(:user) # in the jam session + user2 = FactoryGirl.create(:user) # in the jam session + user3 = FactoryGirl.create(:user) # not in the jam session - music_session = FactoryGirl.create(:music_session, :creator => user1) + music_session = FactoryGirl.create(:music_session, :creator => user1, :musician_access => false) + FactoryGirl.create(:connection, :user => user1, :music_session => music_session) + FactoryGirl.create(:connection, :user => user2, :music_session => music_session) - music_session_member1 = FactoryGirl.create(:connection, :user => user1, :music_session => music_session, :ip_address => "1.1.1.1", :client_id => "1") - music_session_member2 = FactoryGirl.create(:connection, :user => user2, :music_session => music_session, :ip_address => "2.2.2.2", :client_id => "2") music_session.access?(user1).should == true music_session.access?(user2).should == true - #music_session.access?(user3).should == false -- wait until we have finalized access models in place + music_session.access?(user3).should == false end + + it 'anyone can join a open music session' do + + user1 = FactoryGirl.create(:user) # in the jam session + user2 = FactoryGirl.create(:user) # in the jam session + user3 = FactoryGirl.create(:user) # not in the jam session + + music_session = FactoryGirl.create(:music_session, :creator => user1, :musician_access => true) + + music_session.can_join?(user1).should == true + music_session.can_join?(user2).should == true + music_session.can_join?(user3).should == true + end + + it 'no one but invited people can join closed music session' do + user1 = FactoryGirl.create(:user) # in the jam session + user2 = FactoryGirl.create(:user) # in the jam session + user3 = FactoryGirl.create(:user) # not in the jam session + + music_session = FactoryGirl.create(:music_session, :creator => user1, :musician_access => false) + FactoryGirl.create(:connection, :user => user1, :music_session => music_session) + + music_session.can_join?(user1).should == true + music_session.can_join?(user2).should == false + music_session.can_join?(user3).should == false + + # invite user 2 + FactoryGirl.create(:friendship, :user => user1, :friend => user2) + FactoryGirl.create(:friendship, :user => user2, :friend => user1) + FactoryGirl.create(:invitation, :sender => user1, :receiver => user2, :music_session => music_session) + + music_session.can_join?(user1).should == true + music_session.can_join?(user2).should == true + music_session.can_join?(user3).should == false + end + + it 'no one but invited people can see closed music session' do + user1 = FactoryGirl.create(:user) # in the jam session + user2 = FactoryGirl.create(:user) # in the jam session + user3 = FactoryGirl.create(:user) # not in the jam session + + music_session = FactoryGirl.create(:music_session, :creator => user1, :musician_access => false) + FactoryGirl.create(:connection, :user => user1, :music_session => music_session) + + music_session.can_see?(user1).should == true + music_session.can_see?(user2).should == false + music_session.can_see?(user3).should == false + + # invite user 2 + FactoryGirl.create(:friendship, :user => user1, :friend => user2) + FactoryGirl.create(:friendship, :user => user2, :friend => user1) + FactoryGirl.create(:invitation, :sender => user1, :receiver => user2, :music_session => music_session) + + music_session.can_see?(user1).should == true + music_session.can_see?(user2).should == true + music_session.can_see?(user3).should == false + end + + it 'can list sessions with appropriate sort order' do + + user1 = FactoryGirl.create(:user) + user2 = FactoryGirl.create(:user) + user3 = FactoryGirl.create(:user) + user4 = FactoryGirl.create(:user) + user5 = FactoryGirl.create(:user) + + music_session = FactoryGirl.create(:music_session, :creator => user1, :musician_access => false) + FactoryGirl.create(:connection, :user => user1, :music_session => music_session) + + music_sessions = MusicSession.index(user2) + music_sessions.length.should == 0 + music_session2 = FactoryGirl.create(:music_session, :creator => user3, :musician_access => true) + FactoryGirl.create(:connection, :user => user3, :music_session => music_session2) + + music_sessions = MusicSession.index(user2) + music_sessions.length.should == 1 + music_sessions[0].connections[0].user.friends.length == 0 + + # users 1 and 5 are friends + FactoryGirl.create(:friendship, :user => user1, :friend => user5) + FactoryGirl.create(:friendship, :user => user5, :friend => user1) + + # users 1 and 2 are friends + FactoryGirl.create(:friendship, :user => user1, :friend => user2) + FactoryGirl.create(:friendship, :user => user2, :friend => user1) + + # users 2 and 4 are friends + FactoryGirl.create(:friendship, :user => user2, :friend => user4) + FactoryGirl.create(:friendship, :user => user4, :friend => user2) + + # user 2 should now be able to see this session, because his friend is in the session + music_sessions = MusicSession.index(user2) + music_sessions.length.should == 2 + music_sessions[0].id.should == music_session.id + music_sessions[0].connections[0].user.id.should == user1.id + music_sessions[0].connections[0].user.friends.length == 1 + music_sessions[1].id.should == music_session2.id + + FactoryGirl.create(:invitation, :sender => user1, :receiver => user2, :music_session => music_session) + + music_sessions = MusicSession.index(user2) + music_sessions.length.should == 2 + music_sessions[0].id.should == music_session.id + music_sessions[1].id.should == music_session2.id + + # create another, but friendy usic session with user 4 + music_session3 = FactoryGirl.create(:music_session, :creator => user4, :musician_access => false, :created_at => 1.week.ago) + FactoryGirl.create(:connection, :user => user4, :music_session => music_session3) + + music_sessions = MusicSession.index(user2) + music_sessions.length.should == 3 + music_sessions[0].id.should == music_session.id + music_sessions[1].id.should == music_session3.id + music_sessions[2].id.should == music_session2.id + + # verify we can inspect the data + music_session.invitations.length.should == 1 + + + music_session4 = FactoryGirl.create(:music_session, :creator => user5, :musician_access => false, :created_at => 2.week.ago) + FactoryGirl.create(:connection, :user => user5, :music_session => music_session4) + + music_sessions = MusicSession.index(user2) + music_sessions.length.should == 3 + # make this session public now + music_session4.musician_access = true + music_session4.save + + music_sessions = MusicSession.index(user2) + music_sessions.each do |s| + p s + end + music_sessions.length.should == 4 + music_sessions[0].id.should == music_session.id + music_sessions[1].id.should == music_session3.id + music_sessions[2].id.should == music_session2.id + music_sessions[3].id.should == music_session4.id + + # ok let's make the public session that we just made, become a 'friendy' one + # make user2/5 friends + FactoryGirl.create(:friendship, :user => user2, :friend => user5) + FactoryGirl.create(:friendship, :user => user5, :friend => user2) + + music_sessions = MusicSession.index(user2) + music_sessions.length.should == 4 + music_sessions[0].id.should == music_session.id + music_sessions[1].id.should == music_session3.id + music_sessions[2].id.should == music_session4.id + music_sessions[3].id.should == music_session2.id + + # and finally make it an invite + FactoryGirl.create(:invitation, :sender => user5, :receiver => user2, :music_session => music_session4 ) + music_sessions = MusicSession.index(user2) + music_sessions.length.should == 4 + + music_sessions[0].id.should == music_session.id + music_sessions[1].id.should == music_session4.id + music_sessions[2].id.should == music_session3.id + music_sessions[3].id.should == music_session2.id + end + end From e41fd71212dca2450d426a7b107ee970b3dc8766 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Sat, 3 Nov 2012 09:54:55 -0400 Subject: [PATCH 051/274] add support to create users / update instruments via API --- lib/jam_ruby/models/band.rb | 4 +++ lib/jam_ruby/models/user.rb | 60 ++++++++++++++++++++++++++++++++----- 2 files changed, 56 insertions(+), 8 deletions(-) diff --git a/lib/jam_ruby/models/band.rb b/lib/jam_ruby/models/band.rb index 904408115..2f6a3b635 100644 --- a/lib/jam_ruby/models/band.rb +++ b/lib/jam_ruby/models/band.rb @@ -20,5 +20,9 @@ module JamRuby @logo_url = "http://www.jamkazam.com/images/bands/logos/#{self.id}.gif" end + # helper method for creating / updating a Band + def self.save(params) + end + end end \ No newline at end of file diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index f80869d61..1bdc7f0a3 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -1,7 +1,7 @@ module JamRuby class User < ActiveRecord::Base - attr_accessible :name, :email, :password, :password_confirmation + attr_accessible :name, :email, :password, :password_confirmation, :admin, :musician attr_accessor :updating_password self.primary_key = 'id' @@ -20,6 +20,10 @@ module JamRuby has_many :band_musicians has_many :bands, :through => :band_musicians, :class_name => "JamRuby::Band" + # followers / followings + + # favorites + # friends has_many :friendships, :class_name => "JamRuby::Friendship", :foreign_key => "user_id" has_many :friends, :through => :friendships, :class_name => "JamRuby::User" @@ -74,21 +78,61 @@ module JamRuby return id end + # helper method for creating / updating a User def self.save(params) + if params[:id].nil? user = User.new() else user = User.find(params[:id]) end - @user = User.find(params[:id]) - @user.email = params[:email] - @user.musician = params[:musician] - if @user.musician - @user.instruments = params[:instruments] - @user.bands = params[:bands] + # name + unless params[:name].nil? + user.name = params[:name] end - @user.save + + # email + unless params[:email].nil? + user.email = params[:email] + end + + # password + unless params[:password].nil? + user.password = params[:password] + end + + # password confirmation + unless params[:password_confirmation].nil? + user.password_confirmation = params[:password_confirmation] + end + + # musician flag + unless params[:musician].nil? + user.musician = params[:musician] + end + + # instruments + unless params[:instruments].nil? + ActiveRecord::Base.transaction do + # delete all instruments for this user first + MusicianInstrument.delete_all(["user_id = ?", user.id]) + + # loop through each instrument in the array and save to the db + params[:instruments].each do |instrument| + loaded_instrument = Instrument.find_by_description!(instrument[:description]) + mu = MusicianInstrument.new() + + mu.user_id = user.id + mu.instrument_id = loaded_instrument.id + mu.proficiency_level = instrument[:proficiency_level] + user.musician_instruments << mu + end + end + end + + user.updated_at = Time.now.getutc + user.save return user end From b67a34d46d4cfb61d98d2866b04d742bb3a9e321 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Sat, 3 Nov 2012 11:38:00 -0400 Subject: [PATCH 052/274] use instrument id instead of description --- lib/jam_ruby/models/user.rb | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 261195d78..7c1dbdb5b 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -38,7 +38,9 @@ module JamRuby has_secure_password before_save { |user| user.email = email.downcase } - before_save :create_remember_token + #before_save :create_remember_token + + after_save :limit_to_five_instruments validates :name, presence: true, length: {maximum: 50} VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i @@ -76,7 +78,6 @@ module JamRuby # helper method for creating / updating a User def self.save(params) - if params[:id].nil? user = User.new() else @@ -116,11 +117,11 @@ module JamRuby # loop through each instrument in the array and save to the db params[:instruments].each do |instrument| - loaded_instrument = Instrument.find_by_description!(instrument[:description]) mu = MusicianInstrument.new() - mu.user_id = user.id - mu.instrument_id = loaded_instrument.id + puts "INSTRUMENT ID #{instrument[:id]}" + mu.instrument_id = instrument[:id] + puts "proficiency level #{instrument[:proficiency_level]}" mu.proficiency_level = instrument[:proficiency_level] user.musician_instruments << mu end @@ -132,6 +133,12 @@ module JamRuby return user end + def limit_to_five_instruments + if instruments.count > 5 + errors.add(:instruments, "No more than 5 instruments are allowed.") + end + end + private def create_remember_token self.remember_token = SecureRandom.urlsafe_base64 From d64e8962c7a4549bc80b1f60b30a6e740a4d3bbe Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Sat, 3 Nov 2012 15:32:27 -0400 Subject: [PATCH 053/274] band APIs --- lib/jam_ruby/models/band.rb | 50 +++++++++++++++++++++++++++++++++++++ lib/jam_ruby/models/user.rb | 9 ++++--- 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/lib/jam_ruby/models/band.rb b/lib/jam_ruby/models/band.rb index 2f6a3b635..965f49c27 100644 --- a/lib/jam_ruby/models/band.rb +++ b/lib/jam_ruby/models/band.rb @@ -1,6 +1,8 @@ module JamRuby class Band < ActiveRecord::Base + attr_accessible :name, :website, :biography + self.primary_key = 'id' # musicians @@ -10,6 +12,8 @@ module JamRuby # genres has_and_belongs_to_many :genres, :class_name => "JamRuby::Genre", :join_table => "bands_genres" + after_save :limit_to_three_genres + def photo_url # TODO: move image path to config @photo_url = "http://www.jamkazam.com/images/bands/photos/#{self.id}.gif" @@ -22,7 +26,53 @@ module JamRuby # helper method for creating / updating a Band def self.save(params) + if params[:id].nil? + band = Band.new() + else + band = Band.find(params[:id]) + end + + # name + unless params[:name].nil? + band.name = params[:name] + end + + # name + unless params[:website].nil? + band.website = params[:website] + end + + # name + unless params[:biography].nil? + band.biography = params[:biography] + end + + # genres + genres = params[:genres] + unless genres.nil? + ActiveRecord::Base.transaction do + # delete all genres for this band first + unless band.id.nil? || band.id.length == 0 + band.genres.delete_all + end + + # loop through each instrument in the array and save to the db + genres.each do |genre_id| + g = Genre.find(genre_id) + band.genres << g + end + end + end + + band.updated_at = Time.now.getutc + band.save + return band end + def limit_to_three_genres + if self.genres.count > 3 + errors.add(:genres, "No more than 3 genres are allowed.") + end + end end end \ No newline at end of file diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 7c1dbdb5b..57e16523c 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -29,6 +29,8 @@ module JamRuby has_many :friends, :through => :friendships, :class_name => "JamRuby::User" has_many :inverse_friendships, :class_name => "JamRuby::Friendship", :foreign_key => "friend_id" has_many :inverse_friends, :through => :inverse_friendships, :source => :user, :class_name => "JamRuby::User" + + # connections / music sessions has_many :created_music_sessions, :foreign_key => "user_id", :inverse_of => :user, :class_name => "JamRuby::MusicSession" # sessions *created* by the user has_many :music_sessions, :through => :connections, :class_name => "JamRuby::MusicSession" @@ -113,15 +115,16 @@ module JamRuby unless params[:instruments].nil? ActiveRecord::Base.transaction do # delete all instruments for this user first - MusicianInstrument.delete_all(["user_id = ?", user.id]) + unless user.id.nil? || user.id.length == 0 + MusicianInstrument.delete_all(["user_id = ?", user.id]) + end # loop through each instrument in the array and save to the db params[:instruments].each do |instrument| mu = MusicianInstrument.new() mu.user_id = user.id - puts "INSTRUMENT ID #{instrument[:id]}" mu.instrument_id = instrument[:id] - puts "proficiency level #{instrument[:proficiency_level]}" + mu.priority = instrument[:priority] mu.proficiency_level = instrument[:proficiency_level] user.musician_instruments << mu end From 5657bebab8e2aae518224516f5b44d1372fd5930 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sat, 3 Nov 2012 22:02:11 -0500 Subject: [PATCH 054/274] * VRFS-61 and VRFS-34; session.tracks and instrument listing --- lib/jam_ruby.rb | 1 + lib/jam_ruby/message_factory.rb | 6 +++++ lib/jam_ruby/models/connection.rb | 13 ++++++++++ lib/jam_ruby/models/instrument.rb | 2 +- lib/jam_ruby/models/music_session.rb | 29 +--------------------- lib/jam_ruby/models/user.rb | 3 ++- lib/jam_ruby/mq_router.rb | 11 +++++++- spec/jam_ruby/models/music_session_spec.rb | 3 --- 8 files changed, 34 insertions(+), 34 deletions(-) diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index 4cd423c8e..0f0fada36 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -19,6 +19,7 @@ require "jam_ruby/models/music_session" require "jam_ruby/models/invitation" require "jam_ruby/models/friend_request" require "jam_ruby/models/instrument" +require "jam_ruby/models/connection_track" require "jam_ruby/models/musician_instrument" require "jam_ruby/models/band_musician" diff --git a/lib/jam_ruby/message_factory.rb b/lib/jam_ruby/message_factory.rb index f7a9eb19a..3fdbc6d30 100644 --- a/lib/jam_ruby/message_factory.rb +++ b/lib/jam_ruby/message_factory.rb @@ -90,6 +90,12 @@ return Jampb::ClientMessage.new(:type => ClientMessage::Type::USER_JOINED_MUSIC_SESSION, :route_to => CLIENT_TARGET, :user_joined_music_session => joined) end + # create a user-joined session message + def user_left_music_session(session_id, user_id, username) + left = Jampb::UserLeftMusicSession.new(:session_id => session_id, :user_id => user_id, :username => username) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::USER_LEFT_MUSIC_SESSION, :route_to => CLIENT_TARGET, :user_left_music_session => left) + end + # create a test message to send in session def test_session_message(session_id, msg) test = Jampb::TestSessionMessage.new(:msg => msg) diff --git a/lib/jam_ruby/models/connection.rb b/lib/jam_ruby/models/connection.rb index 45755df5a..3c3dfe756 100644 --- a/lib/jam_ruby/models/connection.rb +++ b/lib/jam_ruby/models/connection.rb @@ -1,15 +1,28 @@ module JamRuby class Connection < ActiveRecord::Base + SELECT_AT_LEAST_ONE = "Please select at least one track" + self.primary_key = 'id' belongs_to :user, :class_name => "JamRuby::User" belongs_to :music_session, :class_name => "JamRuby::MusicSession" + has_many :connection_tracks, :class_name => "JamRuby::ConnectionTrack", :inverse_of => :connection + + after_save :require_at_least_one_track_when_in_session # decides if a given user can access this client with p2p messaging # the answer is yes if the user is in the same music session def access_p2p?(user) return self.music_session.users.exists?(user) end + + private + def require_at_least_one_track_when_in_session + if connection_tracks.count == 0 + errors.add(:genres, SELECT_AT_LEAST_ONE) + end + end + end end \ No newline at end of file diff --git a/lib/jam_ruby/models/instrument.rb b/lib/jam_ruby/models/instrument.rb index 9119d6a94..4af0dd1ae 100644 --- a/lib/jam_ruby/models/instrument.rb +++ b/lib/jam_ruby/models/instrument.rb @@ -6,9 +6,9 @@ module JamRuby # users has_many :musician_instruments has_many :users, :through => :musician_instruments, :class_name => "JamRuby::User" + has_many :connection_tracks, :class_name => "JamRuby::ConnectionTrack", :inverse_of => :instrument # music sessions has_and_belongs_to_many :music_sessions, :class_name => "JamRuby::MusicSession", :join_table => "genres_music_sessions" - end end \ No newline at end of file diff --git a/lib/jam_ruby/models/music_session.rb b/lib/jam_ruby/models/music_session.rb index b9383f7aa..e5f2ba72d 100644 --- a/lib/jam_ruby/models/music_session.rb +++ b/lib/jam_ruby/models/music_session.rb @@ -14,34 +14,8 @@ module JamRuby after_save :require_at_least_one_genre, :limit_to_three_genres def self.index(current_user) - #SELECT "music_sessions"."id" AS t0_r0, "music_sessions"."description" AS t0_r1, "music_sessions"."user_id" AS t0_r2, "music_sessions"."created_at" AS t0_r3, "music_sessions"."updated_at" AS t0_r4, "music_sessions"."musician_access" AS t0_r5, "invitations"."id" AS t1_r0, "invitations"."sender_id" AS t1_r1, "invitations"."receiver_id" AS t1_r2, "invitations"."music_session_id" AS t1_r3, "invitations"."created_at" AS t1_r4, "invitations"."updated_at" AS t1_r5, "connections"."id" AS t2_r0, "connections"."user_id" AS t2_r1, "connections"."client_id" AS t2_r2, "connections"."created_at" AS t2_r3, "connections"."updated_at" AS t2_r4, "connections"."music_session_id" AS t2_r5, "connections"."ip_address" AS t2_r6, "users"."id" AS t3_r0, "users"."name" AS t3_r1, "users"."email" AS t3_r2, "users"."remember_token" AS t3_r3, "users"."password_digest" AS t3_r4, "users"."admin" AS t3_r5, "users"."created_at" AS t3_r6, "users"."updated_at" AS t3_r7, "friendships"."id" AS t4_r0, "friendships"."user_id" AS t4_r1, "friendships"."friend_id" AS t4_r2, "friendships"."created_at" AS t4_r3, "friendships"."updated_at" AS t4_r4 FROM "music_sessions" LEFT OUTER JOIN "invitations" ON "invitations"."music_session_id" = "music_sessions"."id" LEFT OUTER JOIN "connections" ON "connections"."music_session_id" = "music_sessions"."id" LEFT OUTER JOIN "users" ON "users"."id" = "connections"."user_id" LEFT OUTER JOIN "friendships" ON "friendships"."user_id" = "users"."id" WHERE (musician_access = true OR invitations.receiver_id = '1' OR friendships.friend_id = '1') ORDER BY (invitations.id is NULL) ASC, (friendships.id is NULL) ASC, music_sessions.created_at DESC - return MusicSession.includes(:invitations, :connections => [:user => [:friendships]]).order("(invitations.id is NULL) ASC, (friendships.friend_id != '#{current_user.id}' OR friendships.friend_id is NULL) ASC, music_sessions.created_at DESC").where("invitations.receiver_id = '#{current_user.id}' OR (friendships.friend_id = '#{current_user.id}') OR musician_access = true") - #.select(["music_sessions.*", "(friendships.friend_id is not NULL) as is_friend"]) - #select(["music_sessions.*", "distinct(music_sessions.id)"]) - #return MusicSession.joins("LEFT OUTER JOIN invitations ON invitations.music_session_id = music_sessions.id LEFT OUTER JOIN connections ON connections.music_session_id = music_sessions.id LEFT OUTER JOIN users ON users.id = connections.user_id LEFT OUTER JOIN friendships ON friendships.user_id = users.id").order('(invitations.id is NULL) ASC, (friendships.id is NULL) ASC, music_sessions.created_at DESC').where("musician_access = true OR invitations.receiver_id = '#{current_user.id}' OR friendships.friend_id = '#{current_user.id}'") - end - - def list(musician_access, invited, friends, page) - - query = MusicSession - - unless musician_access - query = query.unscoped - end - - query = query.joins('LEFT OUTER JOIN connections ON music_sessions.id = connections.music_session_id') - - if invited - query = query.joins("LEFT OUTER JOIN invitations ON invitations.receiver_id = ") - end - - if friends - query = query.unscoped.joins("LEFT OUTER JOIN friendships ON friend_id.receiver_id = '" + current_user.id + "'") - end - - return query.paginate(page: page) - end + end # Verifies that the specified user can join this music session def can_join? user @@ -69,7 +43,6 @@ module JamRuby return self.creator == user end - def access? user return self.users.exists? user end diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 17577ca69..5a2d1b465 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -28,6 +28,7 @@ module JamRuby has_many :created_music_sessions, :foreign_key => "user_id", :inverse_of => :user, :class_name => "JamRuby::MusicSession" # sessions *created* by the user has_many :music_sessions, :through => :connections, :class_name => "JamRuby::MusicSession" + # invitations has_many :received_invitations, :foreign_key => "receiver_id", :inverse_of => :receiver, :class_name => "JamRuby::Invitation" has_many :sent_invitations, :foreign_key => "sender_id", :inverse_of => :sender, :class_name => "JamRuby::Invitation" @@ -91,6 +92,6 @@ module JamRuby private def create_remember_token self.remember_token = SecureRandom.urlsafe_base64 - end + end end end diff --git a/lib/jam_ruby/mq_router.rb b/lib/jam_ruby/mq_router.rb index 722d1e0e4..cc0200866 100644 --- a/lib/jam_ruby/mq_router.rb +++ b/lib/jam_ruby/mq_router.rb @@ -19,7 +19,7 @@ class MQRouter end if !music_session.access? user - raise PermissionError, 'not allowed to access the specified session' + raise PermissionError, 'not allowed to join the specified session' end return music_session @@ -37,6 +37,15 @@ class MQRouter publish_to_session(music_session.id, client_ids, client_msg.to_s, sender) end + # sends a message to a session from the server + # no access check as with user_publish_to_session + # client_msg should be a well-structure message (jam-pb message) + def server_publish_to_session(music_session, client_msg, sender = {:client_id => ""}) + # gather up client_ids in the session + client_ids = music_session.connections.map { |client| client.client_id }.reject { |client_id| client_id == sender[:client_id] } + + publish_to_session(music_session.id, client_ids, client_msg.to_s, sender) + end # sends a message to a client with no checking of permissions (RAW USAGE) # this method deliberately has no database interactivity/active_record objects diff --git a/spec/jam_ruby/models/music_session_spec.rb b/spec/jam_ruby/models/music_session_spec.rb index b56248f17..e0195e16e 100644 --- a/spec/jam_ruby/models/music_session_spec.rb +++ b/spec/jam_ruby/models/music_session_spec.rb @@ -149,9 +149,6 @@ describe MusicSession do music_session4.save music_sessions = MusicSession.index(user2) - music_sessions.each do |s| - p s - end music_sessions.length.should == 4 music_sessions[0].id.should == music_session.id music_sessions[1].id.should == music_session3.id From aca5e758b45d8e66b400ffbc6e5a70a3dc7aa696 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sat, 3 Nov 2012 22:20:05 -0500 Subject: [PATCH 055/274] * VRFS-34 and VRFS-61; forgot to add new connection_track model --- lib/jam_ruby/models/connection_track.rb | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 lib/jam_ruby/models/connection_track.rb diff --git a/lib/jam_ruby/models/connection_track.rb b/lib/jam_ruby/models/connection_track.rb new file mode 100644 index 000000000..303d1534c --- /dev/null +++ b/lib/jam_ruby/models/connection_track.rb @@ -0,0 +1,15 @@ +module JamRuby + class ConnectionTrack < ActiveRecord::Base + + self.table_name = "connections_tracks" + + self.primary_key = 'id' + + SOUND = %w(mono stereo) + + belongs_to :connection, :class_name => "JamRuby::Connection", :inverse_of => :connection_tracks + belongs_to :instrument, :class_name => "JamRuby::Instrument", :inverse_of => :connection_tracks + + validates :sound, :inclusion => {:in => SOUND} + end +end \ No newline at end of file From 1a366b23859fccec336148cafa00bdfdc51ceb92 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Sat, 3 Nov 2012 23:49:36 -0400 Subject: [PATCH 056/274] fix unit tests --- lib/jam_ruby/models/user.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 57e16523c..f3c91707c 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -1,7 +1,7 @@ module JamRuby class User < ActiveRecord::Base - attr_accessible :name, :email, :password, :password_confirmation, :admin, :musician + attr_accessible :name, :email, :password, :password_confirmation attr_accessor :updating_password self.primary_key = 'id' @@ -40,7 +40,7 @@ module JamRuby has_secure_password before_save { |user| user.email = email.downcase } - #before_save :create_remember_token + before_save :create_remember_token after_save :limit_to_five_instruments From 5d5a5d1f23ef074b6a89cf0f7a047143a746d3dc Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Sun, 4 Nov 2012 08:33:51 -0500 Subject: [PATCH 057/274] added user follower models --- lib/jam_ruby.rb | 2 ++ lib/jam_ruby/models/user.rb | 10 ++++++++-- lib/jam_ruby/models/user_follower.rb | 10 ++++++++++ lib/jam_ruby/models/user_following.rb | 10 ++++++++++ 4 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 lib/jam_ruby/models/user_follower.rb create mode 100644 lib/jam_ruby/models/user_following.rb diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index 0f0fada36..2e591d278 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -22,6 +22,8 @@ require "jam_ruby/models/instrument" require "jam_ruby/models/connection_track" require "jam_ruby/models/musician_instrument" require "jam_ruby/models/band_musician" +require "jam_ruby/models/user_follower" +require "jam_ruby/models/user_following" include Jampb diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 039068546..64c5dd44a 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -20,9 +20,15 @@ module JamRuby has_many :band_musicians has_many :bands, :through => :band_musicians, :class_name => "JamRuby::Band" - # followers / followings + # followers + has_many :followers, :class_name => "JamRuby::UserFollower", :foreign_key => "user_id" + has_many :inverse_followers, :through => :followers, :source => :user, :class_name => "JamRuby::User", :foreign_key => "follower_id" - # favorites + # followings + has_many :followings, :class_name => "JamRuby::UserFollowing", :foreign_key => "follower_id" + has_many :inverse_followings, :through => :followings, :source => :user, :class_name => "JamRuby::User", :foreign_key => "user_id" + + # favorites (needs Recording model) # friends has_many :friendships, :class_name => "JamRuby::Friendship", :foreign_key => "user_id" diff --git a/lib/jam_ruby/models/user_follower.rb b/lib/jam_ruby/models/user_follower.rb new file mode 100644 index 000000000..b461d5a82 --- /dev/null +++ b/lib/jam_ruby/models/user_follower.rb @@ -0,0 +1,10 @@ +module JamRuby + class UserFollower < ActiveRecord::Base + + self.table_name = "users_followers" + + self.primary_key = 'id' + + belongs_to :user, :class_name => "JamRuby::User", :foreign_key => "follower_id", :inverse_of => :inverse_followers + end +end \ No newline at end of file diff --git a/lib/jam_ruby/models/user_following.rb b/lib/jam_ruby/models/user_following.rb new file mode 100644 index 000000000..55fd62f3f --- /dev/null +++ b/lib/jam_ruby/models/user_following.rb @@ -0,0 +1,10 @@ +module JamRuby + class UserFollowing < ActiveRecord::Base + + self.table_name = "users_followers" + + self.primary_key = 'id' + + belongs_to :user, :class_name => "JamRuby::User", :foreign_key => "user_id", :inverse_of => :inverse_followings + end +end \ No newline at end of file From 7ffc952717504d46ccc456f1155f365d637d69d2 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Sun, 4 Nov 2012 17:54:53 -0500 Subject: [PATCH 058/274] band follower APIs --- lib/jam_ruby.rb | 1 + lib/jam_ruby/models/band.rb | 10 +++++++--- lib/jam_ruby/models/band_follower.rb | 11 +++++++++++ lib/jam_ruby/models/user.rb | 6 +++++- 4 files changed, 24 insertions(+), 4 deletions(-) create mode 100644 lib/jam_ruby/models/band_follower.rb diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index 2e591d278..8e7a5f960 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -24,6 +24,7 @@ require "jam_ruby/models/musician_instrument" require "jam_ruby/models/band_musician" require "jam_ruby/models/user_follower" require "jam_ruby/models/user_following" +require "jam_ruby/models/band_follower" include Jampb diff --git a/lib/jam_ruby/models/band.rb b/lib/jam_ruby/models/band.rb index 965f49c27..dccae5e11 100644 --- a/lib/jam_ruby/models/band.rb +++ b/lib/jam_ruby/models/band.rb @@ -12,6 +12,10 @@ module JamRuby # genres has_and_belongs_to_many :genres, :class_name => "JamRuby::Genre", :join_table => "bands_genres" + # followers + has_many :followers, :class_name => "JamRuby::BandFollower", :foreign_key => "band_id" + has_many :inverse_followers, :through => :followers, :source => :user, :class_name => "JamRuby::Band", :foreign_key => "follower_id" + after_save :limit_to_three_genres def photo_url @@ -37,12 +41,12 @@ module JamRuby band.name = params[:name] end - # name + # website unless params[:website].nil? band.website = params[:website] end - # name + # biography unless params[:biography].nil? band.biography = params[:biography] end @@ -56,7 +60,7 @@ module JamRuby band.genres.delete_all end - # loop through each instrument in the array and save to the db + # loop through each genre in the array and save to the db genres.each do |genre_id| g = Genre.find(genre_id) band.genres << g diff --git a/lib/jam_ruby/models/band_follower.rb b/lib/jam_ruby/models/band_follower.rb new file mode 100644 index 000000000..ba2e43845 --- /dev/null +++ b/lib/jam_ruby/models/band_follower.rb @@ -0,0 +1,11 @@ +module JamRuby + class BandFollower < ActiveRecord::Base + + self.table_name = "bands_followers" + + self.primary_key = 'id' + + belongs_to :band, :class_name => "JamRuby::Band", :foreign_key => "band_id" + belongs_to :user, :class_name => "JamRuby::User", :foreign_key => "follower_id", :inverse_of => :inverse_followers + end +end \ No newline at end of file diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 64c5dd44a..06471df2c 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -24,10 +24,14 @@ module JamRuby has_many :followers, :class_name => "JamRuby::UserFollower", :foreign_key => "user_id" has_many :inverse_followers, :through => :followers, :source => :user, :class_name => "JamRuby::User", :foreign_key => "follower_id" - # followings + # user followings has_many :followings, :class_name => "JamRuby::UserFollowing", :foreign_key => "follower_id" has_many :inverse_followings, :through => :followings, :source => :user, :class_name => "JamRuby::User", :foreign_key => "user_id" + # band followings + has_many :band_followings, :class_name => "JamRuby::BandFollower", :foreign_key => "follower_id" + has_many :inverse_band_followings, :through => :band_followings, :foreign_key => "band_id" + # favorites (needs Recording model) # friends From 4536cdc4eb34fdf849ec246dc25265afd1c75f3a Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Mon, 5 Nov 2012 21:55:08 -0500 Subject: [PATCH 059/274] added location info --- lib/jam_ruby/models/band.rb | 15 +++++++++++++++ lib/jam_ruby/models/user.rb | 15 +++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/lib/jam_ruby/models/band.rb b/lib/jam_ruby/models/band.rb index dccae5e11..f940836f8 100644 --- a/lib/jam_ruby/models/band.rb +++ b/lib/jam_ruby/models/band.rb @@ -51,6 +51,21 @@ module JamRuby band.biography = params[:biography] end + # city + unless params[:city].nil? + band.city = params[:city] + end + + # state + unless params[:state].nil? + band.state = params[:state] + end + + # country + unless params[:country].nil? + band.country = params[:country] + end + # genres genres = params[:genres] unless genres.nil? diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 06471df2c..c9f0e4570 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -122,6 +122,21 @@ module JamRuby user.musician = params[:musician] end + # city + unless params[:city].nil? + user.city = params[:city] + end + + # state + unless params[:state].nil? + user.state = params[:state] + end + + # country + unless params[:country].nil? + user.country = params[:country] + end + # instruments unless params[:instruments].nil? ActiveRecord::Base.transaction do From 78445ba25b2ee7a44cf51e14cbd9e87eaac74543 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Mon, 5 Nov 2012 23:47:50 -0500 Subject: [PATCH 060/274] added analytics to user/band details --- lib/jam_ruby/models/band.rb | 4 ++++ lib/jam_ruby/models/user.rb | 12 ++++++++++++ 2 files changed, 16 insertions(+) diff --git a/lib/jam_ruby/models/band.rb b/lib/jam_ruby/models/band.rb index f940836f8..255f233dd 100644 --- a/lib/jam_ruby/models/band.rb +++ b/lib/jam_ruby/models/band.rb @@ -28,6 +28,10 @@ module JamRuby @logo_url = "http://www.jamkazam.com/images/bands/logos/#{self.id}.gif" end + def follower_count + return self.followers.size + end + # helper method for creating / updating a Band def self.save(params) if params[:id].nil? diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index c9f0e4570..5119bc490 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -83,6 +83,18 @@ module JamRuby return self.friends.exists?(user) end + def friend_count + return self.friends.size + end + + def follower_count + return self.followers.size + end + + def following_count + return self.followings.size + end + def to_s return email unless email.nil? return name unless name.nil? From 59d3f444788ea905de5daa106b75c0cfcfe123da Mon Sep 17 00:00:00 2001 From: Seth Call Date: Wed, 7 Nov 2012 07:10:41 -0600 Subject: [PATCH 061/274] * VRFS-32; integrating elasticsearch and /api/search completed --- Gemfile | 2 ++ lib/jam_ruby.rb | 8 ++++- lib/jam_ruby/models/band.rb | 55 +++++++++++++++++++++++++++++++ lib/jam_ruby/models/user.rb | 65 ++++++++++++++++++++++++++++++++++--- 4 files changed, 125 insertions(+), 5 deletions(-) diff --git a/Gemfile b/Gemfile index e69e2ea75..0db9350fa 100644 --- a/Gemfile +++ b/Gemfile @@ -13,6 +13,8 @@ gem 'bcrypt-ruby', '3.0.1' gem 'ruby-protocol-buffers', '1.2.2' gem 'eventmachine' gem 'amqp' +gem 'tire' +gem 'will_paginate' group :test do gem 'jam_db', :path=> "#{workspace}/jam-db/target/ruby_package" diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index 0f0fada36..b6f875d3b 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -3,12 +3,17 @@ require "active_record" require "jampb" require "uuidtools" require "logging" +require "tire" +require "will_paginate" +require "will_paginate/active_record" require "jam_ruby/errors/permission_error" require "jam_ruby/errors/state_error" require "jam_ruby/errors/jam_argument_error" require "jam_ruby/mq_router" require "jam_ruby/connection_manager" require "jam_ruby/version" +require "jam_ruby/environment" +require "jam_ruby/tire_tasks" require "jam_ruby/message_factory" require "jam_ruby/models/genre" require "jam_ruby/models/user" @@ -22,9 +27,10 @@ require "jam_ruby/models/instrument" require "jam_ruby/models/connection_track" require "jam_ruby/models/musician_instrument" require "jam_ruby/models/band_musician" +require "jam_ruby/models/search" include Jampb module JamRuby - + end diff --git a/lib/jam_ruby/models/band.rb b/lib/jam_ruby/models/band.rb index 965f49c27..c0afa6d91 100644 --- a/lib/jam_ruby/models/band.rb +++ b/lib/jam_ruby/models/band.rb @@ -1,5 +1,7 @@ module JamRuby class Band < ActiveRecord::Base + include Tire::Model::Search + include Tire::Model::Callbacks attr_accessible :name, :website, :biography @@ -24,6 +26,12 @@ module JamRuby @logo_url = "http://www.jamkazam.com/images/bands/logos/#{self.id}.gif" end + def location + # TODO: implement a single string version of location; + # this will be indexed into elasticsearch and returned in search + return "Austin, TX" + end + # helper method for creating / updating a Band def self.save(params) if params[:id].nil? @@ -74,5 +82,52 @@ module JamRuby errors.add(:genres, "No more than 3 genres are allowed.") end end + + + ### Elasticsearch/Tire integration ### + # + # Define the name based on the environment + # We wouldn't like to erase dev data during + # test runs! + # + index_name("#{Environment.mode}-#{Environment.application}-bands") + + def to_indexed_json + { + :name => name, + :logo_url => logo_url, + :photo_url => photo_url, + :location => location + }.to_json + end + + class << self + def create_search_index + Tire.index(Band.index_name) do + create( + :settings => Search.index_settings, + :mappings => { + "jam_ruby/band" => { + :properties => { + :logo_url => { :type => :string, :index => :not_analyzed, :include_in_all => false }, + :photo_url => { :type => :string, :index => :not_analyzed, :include_in_all => false}, + :name => { :type => :string, :boost => 100}, + :location => { :type => :string }, + } + } + } + ) + end + end + + def delete_search_index + search_index.delete + end + + def search_index + Tire.index(Band.index_name) + end + end + ### Elasticsearch/Tire integration end end \ No newline at end of file diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 039068546..9949c3198 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -1,11 +1,13 @@ module JamRuby class User < ActiveRecord::Base + include Tire::Model::Search + include Tire::Model::Callbacks attr_accessible :name, :email, :password, :password_confirmation attr_accessor :updating_password self.primary_key = 'id' - + # connections (websocket-gateway) has_many :connections, :class_name => "JamRuby::Connection" @@ -15,7 +17,7 @@ module JamRuby # instruments has_many :musician_instruments has_many :instruments, :through => :musician_instruments, :class_name => "JamRuby::Instrument" - + # bands has_many :band_musicians has_many :bands, :through => :band_musicians, :class_name => "JamRuby::Band" @@ -37,7 +39,7 @@ module JamRuby # invitations has_many :received_invitations, :foreign_key => "receiver_id", :inverse_of => :receiver, :class_name => "JamRuby::Invitation" has_many :sent_invitations, :foreign_key => "sender_id", :inverse_of => :sender, :class_name => "JamRuby::Invitation" - + has_secure_password before_save { |user| user.email = email.downcase } @@ -65,6 +67,12 @@ module JamRuby @photo_url = "http://www.jamkazam.com/images/users/photos/#{self.id}.gif"; end + def location + # TODO: implement a single string version of location; + # this will be indexed into elasticsearch and returned in search + return "Austin, TX" + end + def should_validate_password? updating_password || new_record? end @@ -143,9 +151,58 @@ module JamRuby end end + + ### Elasticsearch/Tire integration ### + # + # Define the name based on the environment + # We wouldn't like to erase dev data during + # test runs! + # + index_name("#{Environment.mode}-#{Environment.application}-users") + + def to_indexed_json + { + :name => name, + :photo_url => photo_url, + :location => location, + :musician => musician + }.to_json + end + + class << self + def create_search_index + Tire.index(User.index_name) do + create( + :settings => Search.index_settings, + :mappings => { + "jam_ruby/user" => { + :properties => { + :photo_url => { :type => :string, :index => :not_analyzed, :include_in_all => false}, + :location => { :type => :string }, + :name => { :type => :string, :boost => 100 }, + :is_musician => { :type => :boolean, :index => :not_analyzed, :include_in_all => false} + } + } + } + ) + end + end + + def delete_search_index + search_index.delete + end + + def search_index + Tire.index(User.index_name) + end + end + ### Elasticsearch/Tire integration + + + private def create_remember_token self.remember_token = SecureRandom.urlsafe_base64 - end + end end end From 8102ba9ec06636d8902b1d6bd847e7d96f3ffea2 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Wed, 7 Nov 2012 19:13:35 -0600 Subject: [PATCH 062/274] * VRFS-32; missed a bunch of new files --- lib/jam_ruby/environment.rb | 21 ++++ lib/jam_ruby/models/search.rb | 73 ++++++++++++ lib/jam_ruby/tire_tasks.rb | 51 ++++++++ spec/jam_ruby/models/band_search_spec.rb | 81 +++++++++++++ spec/jam_ruby/models/search_spec.rb | 58 +++++++++ spec/jam_ruby/models/tire_search_spec.rb | 142 +++++++++++++++++++++++ spec/jam_ruby/models/user_search_spec.rb | 70 +++++++++++ 7 files changed, 496 insertions(+) create mode 100644 lib/jam_ruby/environment.rb create mode 100644 lib/jam_ruby/models/search.rb create mode 100644 lib/jam_ruby/tire_tasks.rb create mode 100644 spec/jam_ruby/models/band_search_spec.rb create mode 100644 spec/jam_ruby/models/search_spec.rb create mode 100644 spec/jam_ruby/models/tire_search_spec.rb create mode 100644 spec/jam_ruby/models/user_search_spec.rb diff --git a/lib/jam_ruby/environment.rb b/lib/jam_ruby/environment.rb new file mode 100644 index 000000000..972c834d7 --- /dev/null +++ b/lib/jam_ruby/environment.rb @@ -0,0 +1,21 @@ +module JamRuby + class Environment + def self.mode + if Object.const_defined?('Rails') + return Rails.env + else + # right now, there is no idea of a non-test jam-ruby usage, because it's solely a library + # this will need to change if we add executables to jam-ruby + return "test" + end + end + + def self.application + if Object.const_defined?('Rails') + return 'jamweb' + else + return 'jamruby' + end + end + end +end \ No newline at end of file diff --git a/lib/jam_ruby/models/search.rb b/lib/jam_ruby/models/search.rb new file mode 100644 index 000000000..a2d25b066 --- /dev/null +++ b/lib/jam_ruby/models/search.rb @@ -0,0 +1,73 @@ +module JamRuby + # not a active_record model; just a search result + class Search + attr_accessor :bands, :musicians, :fans, :recordings + + def self.search(query) + # empty queries don't hit back to elasticsearch + if query.nil? || query.length == 0 + return Search.new(nil) + end + + s = Tire.search [User.index_name, Band.index_name], :load => true do + query { string query } + sort { by [:_score] } + from 0 + size 10 # doesn't have to be hardcoded... + end + + return Search.new(s) + end + + # elasticsearch index settings + def self.index_settings() + return { + "analysis" => { + "analyzer" => { + "default" => { + "type" => "custom", + "tokenizer" => "lowercase", + "filter" => ["name_ngram"] + } + }, + "filter" => { + "name_ngram" => { + "type" => 'edgeNGram', + "min_gram" => 2, + "max_gram" => 7, + "side" => "front" + } + } + } + } + end + + # search_results - results from a Tire search across band/user/recording + def initialize(search_results) + @bands = [] + @musicians = [] + @fans = [] + @recordings = [] + + if search_results.nil? + return + end + + search_results.results.each do |result| + if result.class == User + if result.musician + @musicians.push(result) + else + @fans.push(result) + end + elsif result.class == Band + @bands.push(result) + elsif result.class == Recording + @recordings.push(result) + else + raise Exception, "unknown class #{result.class} returned in search results" + end + end + end + end +end \ No newline at end of file diff --git a/lib/jam_ruby/tire_tasks.rb b/lib/jam_ruby/tire_tasks.rb new file mode 100644 index 000000000..bb4c3063f --- /dev/null +++ b/lib/jam_ruby/tire_tasks.rb @@ -0,0 +1,51 @@ +class TireTasks + + class << self + @@log = Logging.logger[TireTasks] + end + + def self.verify + + db_users_count = User.count(:id) + db_bands_count = Band.count(:id) + + s = Tire.search [User.index_name], :search_type => 'count', :query => {"match_all" => {}} do + + end + es_users_count = s.results.total + + s = Tire.search [Band.index_name], :search_type => 'count', :query => {"match_all" => {}} do + + end + es_bands_count = s.results.total + @@log.debug "database_users=#{db_users_count}, elasticsearch_users=#{es_users_count} database_bands=#{db_bands_count}, elasticsearch_bands=#{es_bands_count} " + + if db_users_count != es_users_count + @@log.error "the number of elasticsearch users (#{es_users_count}) != the number of database users ((#{db_users_count}). A rebuild of the elasticsearch index should be performed" + return false + end + + if db_bands_count != es_bands_count + @@log.error "the number of elasticsearch bands (#{es_bands_count}) != the number of database bands (#{db_bands_count}). A rebuild of the elasticsearch index should be performed" + return false + end + + return true + end + + def self.rebuild_indexes + @@log.info "rebuilding elasticsearch" + + User.delete_search_index + User.create_search_index + Band.delete_search_index + Band.create_search_index + + User.import :per_page => 100 + Band.import :per_page => 100 + + @@log.info "done rebuilding elasticsearch" + User.search_index.refresh + Band.search_index.refresh + end +end \ No newline at end of file diff --git a/spec/jam_ruby/models/band_search_spec.rb b/spec/jam_ruby/models/band_search_spec.rb new file mode 100644 index 000000000..abbe0cd35 --- /dev/null +++ b/spec/jam_ruby/models/band_search_spec.rb @@ -0,0 +1,81 @@ +require 'spec_helper' + +describe User do + + before(:each) do + Band.delete_search_index + Band.create_search_index + + @band = Band.save(name: "Example Band", website: "www.bands.com", biography: "zomg we rock") + + # you have to poke elasticsearch because it will batch requests internally for a second + Band.search_index.refresh + end + + it "should allow search of one band" do + ws = Band.search("Example Band") + ws.results.length.should == 1 + band_result = ws.results[0] + band_result._type.should == "jam_ruby/band" + band_result.name.should == @band.name + band_result.id.should == @band.id + band_result.location.should == @band.location + band_result.logo_url.should_not be_nil + band_result.photo_url.should_not be_nil + end + + it "should delete band" do + ws = Band.search("Example Band") + ws.results.length.should == 1 + band_result = ws.results[0] + band_result.id.should == @band.id + + @band.destroy # delete doesn't work; you have to use destroy. + Band.search_index.refresh + + ws = Band.search("Example Band") + ws.results.length.should == 0 + end + + it "should update band" do + ws = Band.search("Example Band") + ws.results.length.should == 1 + band_result = ws.results[0] + band_result.id.should == @band.id + + @band.name = "bonus-stuff" + @band.save + Band.search_index.refresh + + ws = Band.search("Example Band") + ws.results.length.should == 0 + + ws = Band.search("Bonus") + ws.results.length.should == 1 + band_result = ws.results[0] + band_result.id.should == @band.id + band_result.name.should == "bonus-stuff" + end + + it "should tokenize correctly" do + @band2 = Band.save(name: "Peach pit", website: "www.bands.com", biography: "zomg we rock") + Band.search_index.refresh + ws = Band.search("pea") + ws.results.length.should == 1 + user_result = ws.results[0] + user_result.id.should == @band2.id + end + + + it "should not return anything with a 1 character search" do + @band2 = Band.save(name: "Peach pit", website: "www.bands.com", biography: "zomg we rock") + Band.search_index.refresh + ws = Band.search("pe") + ws.results.length.should == 1 + user_result = ws.results[0] + user_result.id.should == @band2.id + + ws = Band.search("p") + ws.results.length.should == 0 + end +end \ No newline at end of file diff --git a/spec/jam_ruby/models/search_spec.rb b/spec/jam_ruby/models/search_spec.rb new file mode 100644 index 000000000..a5171aea0 --- /dev/null +++ b/spec/jam_ruby/models/search_spec.rb @@ -0,0 +1,58 @@ +require 'spec_helper' + +describe Search do + + before(:each) do + Band.delete_search_index + Band.create_search_index + User.delete_search_index + User.create_search_index + end + + + def create_peachy_data + @user = User.save(name: "Peach", email: "user@example.com", + password: "foobar", password_confirmation: "foobar", musician: true) + @fan = User.save(name: "Peach Peach", email: "fan@example.com", + password: "foobar", password_confirmation: "foobar", musician: false) + @band = Band.save(name: "Peach pit", website: "www.bands.com", biography: "zomg we rock") + end + + def assert_peachy_data + search = Search.search('peach') + + search.recordings.length.should == 0 + search.bands.length.should == 1 + search.musicians.length.should == 1 + search.fans.length.should == 1 + + musician = search.musicians[0] + musician.should be_a_kind_of User + musician.id.should == @user.id + + band = search.bands[0] + band.should be_a_kind_of Band + band.id.should == @band.id + + fan = search.fans[0] + fan.should be_a_kind_of User + fan.id.should == @fan.id + end + + it "search for band & musician " do + create_peachy_data + + User.search_index.refresh + Band.search_index.refresh + + assert_peachy_data + end + + it "validates rebuild_indexes method of TireTasks" do + create_peachy_data + + TireTasks.rebuild_indexes + + assert_peachy_data + end +end \ No newline at end of file diff --git a/spec/jam_ruby/models/tire_search_spec.rb b/spec/jam_ruby/models/tire_search_spec.rb new file mode 100644 index 000000000..e3e86c9ed --- /dev/null +++ b/spec/jam_ruby/models/tire_search_spec.rb @@ -0,0 +1,142 @@ +require 'spec_helper' + +# these tests help verify tire integration +describe "tire search" do + + before(:each) do + Band.delete_search_index + Band.create_search_index + User.delete_search_index + User.create_search_index + end + + + it "full search for empty indexes" do + s = Tire.search ['test-jamruby-users', 'test-jamruby-bands'], :load => true do + query { string '*' } + end + + s.results.length.should == 0 + end + + it "full search for single user" do + @user = User.save(name: "User One", email: "user@example.com", + password: "foobar", password_confirmation: "foobar", musician: true) + User.search_index.refresh + + s = Tire.search ['test-jamruby-users', 'test-jamruby-bands'], :load => true do + query { string 'user' } + end + + s.results.length.should == 1 + result = s.results[0] + result.should be_a_kind_of User + result.id.should == @user.id + end + + it "full search for single band" do + @band = Band.save(name: "Example Band", website: "www.bands.com", biography: "zomg we rock") + Band.search_index.refresh + + s = Tire.search ['test-jamruby-users', 'test-jamruby-bands'], :load => true do + query { string 'example' } + end + + s.results.length.should == 1 + result = s.results[0] + result.should be_a_kind_of Band + result.id.should == @band.id + end + + it "full search for a band & user" do + @user = User.save(name: "Peach", email: "user@example.com", + password: "foobar", password_confirmation: "foobar", musician: true) + @band = Band.save(name: "Peach pit", website: "www.bands.com", biography: "zomg we rock") + User.search_index.refresh + Band.search_index.refresh + + s = Tire.search ['test-jamruby-users', 'test-jamruby-bands'], :load => true do + query { string 'peach' } + sort { by [:_score] } + end + + s.results.length.should == 2 + result = s.results[0] + result.should be_a_kind_of User + result.id.should == @user.id + result = s.results[1] + result.should be_a_kind_of Band + result.id.should == @band.id + end + + + it "pagination" do + @user = User.save(name: "Peach", email: "user@example.com", + password: "foobar", password_confirmation: "foobar", musician: true) + @band = Band.save(name: "Peach pit", website: "www.bands.com", biography: "zomg we rock") + User.search_index.refresh + Band.search_index.refresh + + s = Tire.search ['test-jamruby-users', 'test-jamruby-bands'], :load => true do + query { string 'peach' } + sort { by [:_score] } + from 0 + size 1 + end + + s.results.length.should == 1 + result = s.results[0] + result.should be_a_kind_of User + result.id.should == @user.id + + s = Tire.search ['test-jamruby-users', 'test-jamruby-bands'], :load => true do + query { string 'peach' } + sort { by [:_score] } + from 1 + size 2 + end + + s.results.length.should == 1 + result = s.results[0] + result.should be_a_kind_of Band + result.id.should == @band.id + end + + it "should count index" do + + sleep 1 # https://jamkazam.atlassian.net/browse/VRFS-69 + s = Tire.search ['test-jamruby-users'], :search_type => 'count', :query => {"match_all" => {}} do + + end + + s.results.total.should == 0 + + + @user = User.save(name: "Peach", email: "user@example.com", + password: "foobar", password_confirmation: "foobar", musician: true) + User.search_index.refresh + sleep 1 # https://jamkazam.atlassian.net/browse/VRFS-69 + + s = Tire.search ['test-jamruby-users'], :search_type => 'count', :query => {"match_all" => {}} do + + end + + s.results.total.should == 1 + + User.delete_search_index + + s = Tire.search ['test-jamruby-users'], :search_type => 'count', :query => {"match_all" => {}} do + + end + + #s.response.code.should == 404 + #s.response.to_s.include?("IndexMissingException").should be_true + expect {s.results.total}.to raise_error(Tire::Search::SearchRequestFailed) + begin + s.results.total + false.should be_true # should not get here + rescue Tire::Search::SearchRequestFailed => srf + srf.to_s.include?("IndexMissingException").should be_true + end + end +end \ No newline at end of file diff --git a/spec/jam_ruby/models/user_search_spec.rb b/spec/jam_ruby/models/user_search_spec.rb new file mode 100644 index 000000000..4fa012121 --- /dev/null +++ b/spec/jam_ruby/models/user_search_spec.rb @@ -0,0 +1,70 @@ +require 'spec_helper' + +describe User do + + before(:each) do + User.delete_search_index + User.create_search_index + + @user = User.save(name: "Example User", email: "user@example.com", + password: "foobar", password_confirmation: "foobar", musician: true) + + # you have to poke elasticsearch because it will batch requests internally for a second + User.search_index.refresh + end + + it "should allow search of one user" do + ws = User.search("Example User") + ws.results.length.should == 1 + user_result = ws.results[0] + user_result._type.should == "jam_ruby/user" + user_result.name.should == @user.name + user_result.id.should == @user.id + user_result.location.should == @user.location + user_result.musician.should == true + user_result.photo_url.should_not be_nil + end + + it "should delete user" do + ws = User.search("Example User") + ws.results.length.should == 1 + user_result = ws.results[0] + user_result.id.should == @user.id + + @user.destroy # delete doesn't work; you have to use destroy. + User.search_index.refresh + + ws = User.search("Example User") + ws.results.length.should == 0 + end + + it "should update user" do + ws = User.search("Example User") + ws.results.length.should == 1 + user_result = ws.results[0] + user_result.id.should == @user.id + + @user.name = "bonus-junk" + @user.save + User.search_index.refresh + + ws = User.search("Example User") + ws.results.length.should == 0 + + ws = User.search("Bonus") + ws.results.length.should == 1 + user_result = ws.results[0] + user_result.id.should == @user.id + user_result.name.should == "bonus-junk" + end + + it "should tokenize correctly" do + @user2 = User.save(name: "peaches", email: "peach@example.com", + password: "foobar", password_confirmation: "foobar", musician: true) + User.search_index.refresh + ws = User.search("pea") + ws.results.length.should == 1 + user_result = ws.results[0] + user_result.id.should == @user2.id + end +end \ No newline at end of file From b86d1c9597d2f37c32278d82647370959a1bcdca Mon Sep 17 00:00:00 2001 From: Seth Call Date: Wed, 7 Nov 2012 21:42:43 -0600 Subject: [PATCH 063/274] * VRFS-32; verified working on ubuntu --- spec/jam_ruby/models/search_spec.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/jam_ruby/models/search_spec.rb b/spec/jam_ruby/models/search_spec.rb index a5171aea0..cf82fb35f 100644 --- a/spec/jam_ruby/models/search_spec.rb +++ b/spec/jam_ruby/models/search_spec.rb @@ -49,10 +49,11 @@ describe Search do end it "validates rebuild_indexes method of TireTasks" do + pending "figure out how to suppress stdout 'curl' message from tire" create_peachy_data TireTasks.rebuild_indexes assert_peachy_data end -end \ No newline at end of file +end From 1f140dacc2551f1e4839fe4fa614bfe3a9da5bf7 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Wed, 7 Nov 2012 22:08:16 -0600 Subject: [PATCH 064/274] * VRFS-32 merged --- lib/jam_ruby/models/band.rb | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/jam_ruby/models/band.rb b/lib/jam_ruby/models/band.rb index 3d1ae6d67..a52e5a2c5 100644 --- a/lib/jam_ruby/models/band.rb +++ b/lib/jam_ruby/models/band.rb @@ -30,19 +30,16 @@ module JamRuby @logo_url = "http://www.jamkazam.com/images/bands/logos/#{self.id}.gif" end -<<<<<<< HEAD def follower_count return self.followers.size end -======= def location # TODO: implement a single string version of location; # this will be indexed into elasticsearch and returned in search return "Austin, TX" end ->>>>>>> elasticsearch # helper method for creating / updating a Band def self.save(params) if params[:id].nil? @@ -156,4 +153,4 @@ module JamRuby end ### Elasticsearch/Tire integration end -end \ No newline at end of file +end From 1881718eb93ad115adca73a1751796f96a0f1275 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Wed, 7 Nov 2012 22:13:11 -0600 Subject: [PATCH 065/274] * suppressing the correct test so that stdout of rspec tests isn't broken up with curl debug statement from tire --- spec/jam_ruby/models/search_spec.rb | 1 - spec/jam_ruby/models/tire_search_spec.rb | 7 +++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/spec/jam_ruby/models/search_spec.rb b/spec/jam_ruby/models/search_spec.rb index cf82fb35f..3733fc5f1 100644 --- a/spec/jam_ruby/models/search_spec.rb +++ b/spec/jam_ruby/models/search_spec.rb @@ -49,7 +49,6 @@ describe Search do end it "validates rebuild_indexes method of TireTasks" do - pending "figure out how to suppress stdout 'curl' message from tire" create_peachy_data TireTasks.rebuild_indexes diff --git a/spec/jam_ruby/models/tire_search_spec.rb b/spec/jam_ruby/models/tire_search_spec.rb index e3e86c9ed..237c87204 100644 --- a/spec/jam_ruby/models/tire_search_spec.rb +++ b/spec/jam_ruby/models/tire_search_spec.rb @@ -102,7 +102,8 @@ describe "tire search" do result.id.should == @band.id end - it "should count index" do + it "should fail to search deleted index" do + pending "until figure out how to stop tire from writing to stdout when a search of deleted index occurs" sleep 1 # https://jamkazam.atlassian.net/browse/VRFS-69 s = Tire.search ['test-jamruby-users'], :search_type => 'count', :query => {"match_all" => {}} do @@ -129,8 +130,6 @@ describe "tire search" do end - #s.response.code.should == 404 - #s.response.to_s.include?("IndexMissingException").should be_true expect {s.results.total}.to raise_error(Tire::Search::SearchRequestFailed) begin s.results.total @@ -139,4 +138,4 @@ describe "tire search" do srf.to_s.include?("IndexMissingException").should be_true end end -end \ No newline at end of file +end From 27fb2f3ceba36c2af37234d35af471bb06e71e5b Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Thu, 8 Nov 2012 22:29:05 -0800 Subject: [PATCH 066/274] minor cleanup --- lib/jam_ruby/models/user.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 05510cff5..5ddb2a2ce 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -68,8 +68,7 @@ module JamRuby validates_confirmation_of :password, :if => :should_validate_password? def online - @online = !self.connections.nil? && self.connections.size > 0 - return @online + @online ||= !self.connections.nil? && self.connections.size > 0 end def photo_url From 2982cd998a91d76191d5cbccc95ee6f2a0e30999 Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Thu, 8 Nov 2012 22:51:17 -0800 Subject: [PATCH 067/274] added comment --- lib/jam_ruby/models/user.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 5ddb2a2ce..fe4a60485 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -50,6 +50,8 @@ module JamRuby has_many :received_invitations, :foreign_key => "receiver_id", :inverse_of => :receiver, :class_name => "JamRuby::Invitation" has_many :sent_invitations, :foreign_key => "sender_id", :inverse_of => :sender, :class_name => "JamRuby::Invitation" + + # This causes the authenticate method to be generated (among other stuff) has_secure_password before_save { |user| user.email = email.downcase } From bca52caf2018988bebd9efe643da0158afeda995 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sat, 10 Nov 2012 19:07:17 -0600 Subject: [PATCH 068/274] * VRFS-32 UserMailer coming along --- Gemfile | 2 ++ lib/jam_ruby.rb | 6 ++++++ lib/jam_ruby/connection_manager.rb | 26 ++++---------------------- spec/spec_helper.rb | 3 +++ 4 files changed, 15 insertions(+), 22 deletions(-) diff --git a/Gemfile b/Gemfile index 0db9350fa..2bc47e6ce 100644 --- a/Gemfile +++ b/Gemfile @@ -15,6 +15,8 @@ gem 'eventmachine' gem 'amqp' gem 'tire' gem 'will_paginate' +gem 'actionmailer' +gem 'sendgrid' group :test do gem 'jam_db', :path=> "#{workspace}/jam-db/target/ruby_package" diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index b6f875d3b..e562345ed 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -6,13 +6,19 @@ require "logging" require "tire" require "will_paginate" require "will_paginate/active_record" +require "action_mailer" +require "sendgrid" require "jam_ruby/errors/permission_error" require "jam_ruby/errors/state_error" require "jam_ruby/errors/jam_argument_error" require "jam_ruby/mq_router" +require "jam_ruby/base_manager" +require "jam_ruby/user_manager" require "jam_ruby/connection_manager" require "jam_ruby/version" require "jam_ruby/environment" +require "jam_ruby/init" +require "jam_ruby/app/mailers/user_mailer" require "jam_ruby/tire_tasks" require "jam_ruby/message_factory" require "jam_ruby/models/genre" diff --git a/lib/jam_ruby/connection_manager.rb b/lib/jam_ruby/connection_manager.rb index d3adb12a6..0ba6111e3 100644 --- a/lib/jam_ruby/connection_manager.rb +++ b/lib/jam_ruby/connection_manager.rb @@ -1,5 +1,5 @@ module JamRuby -# All writes should occur through the ConnectionManager +# All writes should occur through the ConnectionManager for the connection table # Reads can occur freely elsewhere, though # Because connections are tied to the websocket-connection and we bookkeep them in the database purely # for 'SQL convenience', this is a obvious place we can go away from a database @@ -11,19 +11,15 @@ module JamRuby # This may make sense in the short term if we are still managing connections in the database, but # we move to the node-js in the websocket gateway (because the websocket gateway needs to call some of these methods). # Or of course we could just port the relevant methods to node-js - class ConnectionManager + class ConnectionManager < BaseManager - attr_accessor :mq_router, :pg_conn + attr_accessor :mq_router def initialize(options={}) + super(options) @log = Logging.logger[self] @mq_router = MQRouter.new - @pg_conn = options[:conn] @message_factory = MessageFactory.new - - unless PG.threadsafe? - raise Exception, "a non-threadsafe build of libpq is present." - end end def update_staleness() @@ -248,20 +244,6 @@ module JamRuby return friend_ids end - # Creates a connection manager, and associates the connection created by active_record with ourselves - def self.active_record_transaction(&block) - connection_manager = ConnectionManager.new - ActiveRecord::Base.connection_pool.with_connection do |connection| - # create a transaction, and pass the current connection to ConnectionManager. - # this lets the entire operation work with the same transaction, - # across Rails ActiveRecord and the pg-gem based code in ConnectionManager. - connection_manager.pg_conn = connection.instance_variable_get("@connection") - - connection.transaction do - block.call(connection_manager) - end - end - end end end \ No newline at end of file diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index bef938bf1..d7084d430 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -19,6 +19,9 @@ require 'factories' include JamRuby +# put ActionMailer into test mode +ActionMailer::Base.delivery_method = :test + #uncomment the following line to use spork with the debugger #require 'spork/ext/ruby-debug' From b2880dc65a3e361fbef0727657fa368336d712ba Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Sat, 10 Nov 2012 23:23:38 -0500 Subject: [PATCH 069/274] added first and last name fields --- lib/jam_ruby/models/user.rb | 30 +++++++++++++++++++----- spec/jam_ruby/models/search_spec.rb | 4 ++-- spec/jam_ruby/models/tire_search_spec.rb | 22 ++++++++--------- spec/jam_ruby/models/user_search_spec.rb | 12 ++++++---- spec/jam_ruby/models/user_spec.rb | 23 +++++++++++++----- 5 files changed, 61 insertions(+), 30 deletions(-) diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index fe4a60485..d4db360c8 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -3,7 +3,7 @@ module JamRuby include Tire::Model::Search include Tire::Model::Callbacks - attr_accessible :name, :email, :password, :password_confirmation + attr_accessible :first_name, :last_name, :name, :email, :password, :password_confirmation attr_accessor :updating_password self.primary_key = 'id' @@ -32,7 +32,7 @@ module JamRuby # band followings has_many :band_followings, :class_name => "JamRuby::BandFollower", :foreign_key => "follower_id" - has_many :inverse_band_followings, :through => :band_followings, :foreign_key => "band_id" + has_many :inverse_band_followings, :through => :band_followings, :class_name => "JamRuby::Band", :foreign_key => "band_id" # favorites (needs Recording model) @@ -59,6 +59,8 @@ module JamRuby after_save :limit_to_five_instruments + validates :first_name, presence: true, length: {maximum: 50} + validates :last_name, presence: true, length: {maximum: 50} validates :name, presence: true, length: {maximum: 50} VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i validates :email, presence: true, format: {with: VALID_EMAIL_REGEX}, @@ -88,6 +90,10 @@ module JamRuby updating_password || new_record? end + #def name + # return self.first_name + " " + self.last_name + #end + def friends?(user) return self.friends.exists?(user) end @@ -118,7 +124,17 @@ module JamRuby user = User.find(params[:id]) end - # name + # first name + unless params[:first_name].nil? + user.first_name = params[:first_name] + end + + # last name + unless params[:last_name].nil? + user.last_name = params[:last_name] + end + + # username unless params[:name].nil? user.name = params[:name] end @@ -200,7 +216,8 @@ module JamRuby def to_indexed_json { - :name => name, + :first_name => first_name, + :last_name => last_name, :photo_url => photo_url, :location => location, :musician => musician @@ -217,8 +234,9 @@ module JamRuby :properties => { :photo_url => { :type => :string, :index => :not_analyzed, :include_in_all => false}, :location => { :type => :string }, - :name => { :type => :string, :boost => 100 }, - :is_musician => { :type => :boolean, :index => :not_analyzed, :include_in_all => false} + :first_name => { :type => :string, :boost => 100 }, + :last_name => { :type => :string, :boost => 100 }, + :musician => { :type => :boolean, :index => :not_analyzed, :include_in_all => false} } } } diff --git a/spec/jam_ruby/models/search_spec.rb b/spec/jam_ruby/models/search_spec.rb index 3733fc5f1..90a1545e7 100644 --- a/spec/jam_ruby/models/search_spec.rb +++ b/spec/jam_ruby/models/search_spec.rb @@ -11,9 +11,9 @@ describe Search do def create_peachy_data - @user = User.save(name: "Peach", email: "user@example.com", + @user = User.save(first_name: "Peach", last_name: "Pit", name: "peachy", email: "user@example.com", password: "foobar", password_confirmation: "foobar", musician: true) - @fan = User.save(name: "Peach Peach", email: "fan@example.com", + @fan = User.save(first_name: "Peach Peach", last_name: "Pit", name: "peachy", email: "fan@example.com", password: "foobar", password_confirmation: "foobar", musician: false) @band = Band.save(name: "Peach pit", website: "www.bands.com", biography: "zomg we rock") end diff --git a/spec/jam_ruby/models/tire_search_spec.rb b/spec/jam_ruby/models/tire_search_spec.rb index 237c87204..099bb17d6 100644 --- a/spec/jam_ruby/models/tire_search_spec.rb +++ b/spec/jam_ruby/models/tire_search_spec.rb @@ -20,7 +20,7 @@ describe "tire search" do end it "full search for single user" do - @user = User.save(name: "User One", email: "user@example.com", + @user = User.save(first_name: "User", last_name: "One", name: "test", email: "user@example.com", password: "foobar", password_confirmation: "foobar", musician: true) User.search_index.refresh @@ -49,7 +49,7 @@ describe "tire search" do end it "full search for a band & user" do - @user = User.save(name: "Peach", email: "user@example.com", + @user = User.save(first_name: "Peach", last_name: "Foo", name: "test", email: "user@example.com", password: "foobar", password_confirmation: "foobar", musician: true) @band = Band.save(name: "Peach pit", website: "www.bands.com", biography: "zomg we rock") User.search_index.refresh @@ -62,16 +62,16 @@ describe "tire search" do s.results.length.should == 2 result = s.results[0] - result.should be_a_kind_of User - result.id.should == @user.id - result = s.results[1] result.should be_a_kind_of Band result.id.should == @band.id + result = s.results[1] + result.should be_a_kind_of User + result.id.should == @user.id end it "pagination" do - @user = User.save(name: "Peach", email: "user@example.com", + @user = User.save(first_name: "Peach", last_name: "foo", name: "test", email: "user@example.com", password: "foobar", password_confirmation: "foobar", musician: true) @band = Band.save(name: "Peach pit", website: "www.bands.com", biography: "zomg we rock") User.search_index.refresh @@ -86,8 +86,8 @@ describe "tire search" do s.results.length.should == 1 result = s.results[0] - result.should be_a_kind_of User - result.id.should == @user.id + result.should be_a_kind_of Band + result.id.should == @band.id s = Tire.search ['test-jamruby-users', 'test-jamruby-bands'], :load => true do query { string 'peach' } @@ -98,8 +98,8 @@ describe "tire search" do s.results.length.should == 1 result = s.results[0] - result.should be_a_kind_of Band - result.id.should == @band.id + result.should be_a_kind_of User + result.id.should == @user.id end it "should fail to search deleted index" do @@ -113,7 +113,7 @@ describe "tire search" do s.results.total.should == 0 - @user = User.save(name: "Peach", email: "user@example.com", + @user = User.save(first_name: "Peach", last_name: "Pit", name: "peachy", email: "user@example.com", password: "foobar", password_confirmation: "foobar", musician: true) User.search_index.refresh sleep 1 # https://jamkazam.atlassian.net/browse/VRFS-69 diff --git a/spec/jam_ruby/models/user_search_spec.rb b/spec/jam_ruby/models/user_search_spec.rb index 4fa012121..e7ec9ca8a 100644 --- a/spec/jam_ruby/models/user_search_spec.rb +++ b/spec/jam_ruby/models/user_search_spec.rb @@ -6,7 +6,7 @@ describe User do User.delete_search_index User.create_search_index - @user = User.save(name: "Example User", email: "user@example.com", + @user = User.save(first_name: "Example", last_name: "User", name: "peachy", email: "user@example.com", password: "foobar", password_confirmation: "foobar", musician: true) # you have to poke elasticsearch because it will batch requests internally for a second @@ -18,7 +18,8 @@ describe User do ws.results.length.should == 1 user_result = ws.results[0] user_result._type.should == "jam_ruby/user" - user_result.name.should == @user.name + user_result.first_name.should == @user.first_name + user_result.last_name.should == @user.last_name user_result.id.should == @user.id user_result.location.should == @user.location user_result.musician.should == true @@ -44,7 +45,8 @@ describe User do user_result = ws.results[0] user_result.id.should == @user.id - @user.name = "bonus-junk" + @user.first_name = "bonus-junk" + @user.last_name = "more-junk" @user.save User.search_index.refresh @@ -55,11 +57,11 @@ describe User do ws.results.length.should == 1 user_result = ws.results[0] user_result.id.should == @user.id - user_result.name.should == "bonus-junk" + user_result.first_name.should == "bonus-junk" end it "should tokenize correctly" do - @user2 = User.save(name: "peaches", email: "peach@example.com", + @user2 = User.save(first_name: "peaches", last_name: "test", name: "peachy", email: "peach@example.com", password: "foobar", password_confirmation: "foobar", musician: true) User.search_index.refresh ws = User.search("pea") diff --git a/spec/jam_ruby/models/user_spec.rb b/spec/jam_ruby/models/user_spec.rb index 8250064f8..97b651353 100644 --- a/spec/jam_ruby/models/user_spec.rb +++ b/spec/jam_ruby/models/user_spec.rb @@ -3,13 +3,14 @@ require 'spec_helper' describe User do before do - @user = User.new(name: "Example User", email: "user@example.com", + @user = User.new(first_name: "Example", last_name: "User", name: "test", email: "user@example.com", password: "foobar", password_confirmation: "foobar") end subject { @user } - it { should respond_to(:name) } + it { should respond_to(:first_name) } + it { should respond_to(:last_name) } it { should respond_to(:email) } it { should respond_to(:password_digest) } it { should respond_to(:password) } @@ -39,8 +40,13 @@ describe User do it { should be_admin } end - describe "when name is not present" do - before { @user.name = " " } + describe "when first name is not present" do + before { @user.first_name = " " } + it { should_not be_valid } + end + + describe "when last name is not present" do + before { @user.last_name = " " } it { should_not be_valid } end @@ -49,8 +55,13 @@ describe User do it { should_not be_valid } end - describe "when name is too long" do - before { @user.name = "a" * 51 } + describe "when first name is too long" do + before { @user.first_name = "a" * 51 } + it { should_not be_valid } + end + + describe "when last name is too long" do + before { @user.last_name = "a" * 51 } it { should_not be_valid } end From eb00be630b1a658e687c8f988233f15862814d64 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Mon, 12 Nov 2012 06:28:44 -0600 Subject: [PATCH 070/274] * UserManager added --- lib/jam_ruby/app/mailers/user_mailer.rb | 41 ++++++++++ .../user_mailer/reset_password.html.erb | 5 ++ .../user_mailer/reset_password.text.erb | 1 + .../user_mailer/welcome_message.html.erb | 6 ++ .../user_mailer/welcome_message.text.erb | 2 + lib/jam_ruby/base_manager.rb | 31 +++++++ lib/jam_ruby/init.rb | 3 + lib/jam_ruby/models/user.rb | 16 +++- lib/jam_ruby/user_manager.rb | 65 +++++++++++++++ spec/factories.rb | 5 ++ spec/jam_ruby/models/search_spec.rb | 8 +- spec/jam_ruby/models/tire_search_spec.rb | 19 ++--- spec/jam_ruby/models/user_search_spec.rb | 27 ++++++- spec/jam_ruby/user_manager_spec.rb | 81 +++++++++++++++++++ spec/mailers/user_mailer_spec.rb | 44 ++++++++++ 15 files changed, 330 insertions(+), 24 deletions(-) create mode 100644 lib/jam_ruby/app/mailers/user_mailer.rb create mode 100644 lib/jam_ruby/app/views/jam_ruby/user_mailer/reset_password.html.erb create mode 100644 lib/jam_ruby/app/views/jam_ruby/user_mailer/reset_password.text.erb create mode 100644 lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.html.erb create mode 100644 lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.text.erb create mode 100644 lib/jam_ruby/base_manager.rb create mode 100644 lib/jam_ruby/init.rb create mode 100644 lib/jam_ruby/user_manager.rb create mode 100644 spec/jam_ruby/user_manager_spec.rb create mode 100644 spec/mailers/user_mailer_spec.rb diff --git a/lib/jam_ruby/app/mailers/user_mailer.rb b/lib/jam_ruby/app/mailers/user_mailer.rb new file mode 100644 index 000000000..2470eeeb1 --- /dev/null +++ b/lib/jam_ruby/app/mailers/user_mailer.rb @@ -0,0 +1,41 @@ +module JamRuby + # UserMailer must be configured to work + # Some common configs occur in jam_ruby/init.rb + # Environment specific configs occur in spec_helper.rb in jam-ruby and jam-web (to put it into test mode), + # and in config/initializers/email.rb in rails to configure sendmail account settings + # If UserMailer were to be used in another project, it would need to be configured there, as well. + + # Templates for UserMailer can be found in jam_ruby/app/views/jam_ruby/user_mailer + class UserMailer < ActionMailer::Base + include SendGrid + + DEFAULT_SENDER = "support@jamkazam.com" + + default :from => DEFAULT_SENDER + + sendgrid_category :use_subject_lines + sendgrid_enable :opentrack, :clicktrack + sendgrid_unique_args :env => Environment.mode + + def welcome_message(user, signup_confirm_url) + @user = user + @signup_confirm_url = signup_confirm_url + sendgrid_category "Welcome" + sendgrid_unique_args :type => "welcome_message" + + mail(:to => user.email, :subject => "Welcome #{user.name} to Jamkazam") do |format| + format.text + format.html + end + end + + def reset_password(user) + @user = user + sendgrid_unique_args :type => "reset_password" + mail(:to => user.email, :subject => "Jamkazam Reset Password") do |format| + format.text + format.html + end + end +end +end \ No newline at end of file diff --git a/lib/jam_ruby/app/views/jam_ruby/user_mailer/reset_password.html.erb b/lib/jam_ruby/app/views/jam_ruby/user_mailer/reset_password.html.erb new file mode 100644 index 000000000..e1f037477 --- /dev/null +++ b/lib/jam_ruby/app/views/jam_ruby/user_mailer/reset_password.html.erb @@ -0,0 +1,5 @@ + + +Reset Password! <%= @user.name %> + + \ No newline at end of file diff --git a/lib/jam_ruby/app/views/jam_ruby/user_mailer/reset_password.text.erb b/lib/jam_ruby/app/views/jam_ruby/user_mailer/reset_password.text.erb new file mode 100644 index 000000000..e42aff656 --- /dev/null +++ b/lib/jam_ruby/app/views/jam_ruby/user_mailer/reset_password.text.erb @@ -0,0 +1 @@ +Reset Password! <%= @user.name %> \ No newline at end of file diff --git a/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.html.erb b/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.html.erb new file mode 100644 index 000000000..54d20dfb5 --- /dev/null +++ b/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.html.erb @@ -0,0 +1,6 @@ + + +

Welcome! <%= @user.name %>.

+

To confirm your registration, please go to the signup confirmation page..

+ + \ No newline at end of file diff --git a/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.text.erb b/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.text.erb new file mode 100644 index 000000000..3667d0935 --- /dev/null +++ b/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.text.erb @@ -0,0 +1,2 @@ +Welcome! <%= @user.name %> +To confirm your registration, please go to the signup confirmation page at : <%= @signup_confirm_url %>. \ No newline at end of file diff --git a/lib/jam_ruby/base_manager.rb b/lib/jam_ruby/base_manager.rb new file mode 100644 index 000000000..d2970c24b --- /dev/null +++ b/lib/jam_ruby/base_manager.rb @@ -0,0 +1,31 @@ +module JamRuby + class BaseManager + + attr_accessor :pg_conn + + def initialize(options={}) + @log = Logging.logger[self] + @pg_conn = options[:conn] + + unless PG.threadsafe? + raise Exception, "a non-threadsafe build of libpq is present." + end + end + + # Creates a connection manager, and associates the connection created by active_record with ourselves + def self.active_record_transaction(&block) + + manager = self.new + ActiveRecord::Base.connection_pool.with_connection do |connection| + # create a transaction, and pass the current connection to ConnectionManager. + # this lets the entire operation work with the same transaction, + # across Rails ActiveRecord and the pg-gem based code in ConnectionManager. + manager.pg_conn = connection.instance_variable_get("@connection") + + connection.transaction do + block.call(manager) + end + end + end + end +end \ No newline at end of file diff --git a/lib/jam_ruby/init.rb b/lib/jam_ruby/init.rb new file mode 100644 index 000000000..adf32cf04 --- /dev/null +++ b/lib/jam_ruby/init.rb @@ -0,0 +1,3 @@ +# initialize actionmailer +ActionMailer::Base.raise_delivery_errors = true +ActionMailer::Base.view_paths = File.expand_path('../../jam_ruby/app/views/', __FILE__) \ No newline at end of file diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index fe4a60485..460c36bce 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -1,9 +1,8 @@ module JamRuby class User < ActiveRecord::Base include Tire::Model::Search - include Tire::Model::Callbacks - attr_accessible :name, :email, :password, :password_confirmation + attr_accessible :name, :email, :password, :password_confirmation, :city, :state, :country attr_accessor :updating_password self.primary_key = 'id' @@ -59,7 +58,7 @@ module JamRuby after_save :limit_to_five_instruments - validates :name, presence: true, length: {maximum: 50} + validates :name, uniqueness: {case_sensitive: false}, presence: true, length: {maximum: 50} VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i validates :email, presence: true, format: {with: VALID_EMAIL_REGEX}, uniqueness: {case_sensitive: false} @@ -111,7 +110,7 @@ module JamRuby end # helper method for creating / updating a User - def self.save(params) + def self.save(params) if params[:id].nil? user = User.new() else @@ -207,6 +206,15 @@ module JamRuby }.to_json end + # only put users into the search index if their eail is confirmed + after_save do + update_index if email_confirmed + end + + after_destroy do + update_index + end + class << self def create_search_index Tire.index(User.index_name) do diff --git a/lib/jam_ruby/user_manager.rb b/lib/jam_ruby/user_manager.rb new file mode 100644 index 000000000..446f59de2 --- /dev/null +++ b/lib/jam_ruby/user_manager.rb @@ -0,0 +1,65 @@ +module JamRuby + class UserManager < BaseManager + + def initialize(options={}) + super(options) + @log = Logging.logger[self] + end + + # throws ActiveRecord::RecordNotFound if instrument is invalid + # throws an email delivery error if unable to connect out to SMTP + def signup(name, email, password, password_confirmation, + city, state, country, instruments, signup_confirm_url) + user = User.new + + UserManager.active_record_transaction do |user_manager| + user.name = name + user.email = email + user.password = password + user.password_confirmation = password_confirmation + user.admin = false + user.email_confirmed = false + user.city = city + user.state = state + user.country = country + unless instruments.nil? + instruments.each_with_index do |musician_instrument_param, index| + instrument = Instrument.find(musician_instrument_param["id"]) + musician_instrument = MusicianInstrument.new + musician_instrument.user = current_user + musician_instrument.instrument = instrument + musician_instrument.proficiency_level = musician_instrument_param["proficiency_level"] + musician_instrument.priority = index + musician_instrument.save + user.instruments < musician_instrument + end + end + user.signup_token = SecureRandom.urlsafe_base64 + + user.save + + if user.errors.any? + raise ActiveRecord::Rollback + else + # any errors here should also rollback the transaction; that's OK. If emails aren't going to be delivered, + # it's already a really bad situation; make user signup again + UserMailer.welcome_message(user, signup_confirm_url + "/" + user.signup_token).deliver + end + end + + return user + end + + # throws RecordNotFound if signup token is invalid + def signup_confirm(signup_token) + UserManager.active_record_transaction do |user_manager| + # throws ActiveRecord::RecordNotFound if invalid + user = User.find_by_signup_token!(signup_token) + user.signup_token = nil + user.email_confirmed = true + user.save + return user + end + end + end +end \ No newline at end of file diff --git a/spec/factories.rb b/spec/factories.rb index 6ccd2c674..bc82f9e43 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -4,6 +4,7 @@ FactoryGirl.define do sequence(:email) { |n| "person_#{n}@example.com"} password "foobar" password_confirmation "foobar" + email_confirmed true factory :admin do admin true @@ -26,4 +27,8 @@ FactoryGirl.define do factory :friendship, :class => JamRuby::Friendship do end + + factory :band, :class => JamRuby::Band do + + end end \ No newline at end of file diff --git a/spec/jam_ruby/models/search_spec.rb b/spec/jam_ruby/models/search_spec.rb index 3733fc5f1..9ab057f4d 100644 --- a/spec/jam_ruby/models/search_spec.rb +++ b/spec/jam_ruby/models/search_spec.rb @@ -11,11 +11,9 @@ describe Search do def create_peachy_data - @user = User.save(name: "Peach", email: "user@example.com", - password: "foobar", password_confirmation: "foobar", musician: true) - @fan = User.save(name: "Peach Peach", email: "fan@example.com", - password: "foobar", password_confirmation: "foobar", musician: false) - @band = Band.save(name: "Peach pit", website: "www.bands.com", biography: "zomg we rock") + @user = FactoryGirl.create(:user, name: "Peach", email: "user@example.com", musician: true) + @fan = FactoryGirl.create(:user, name: "Peach Peach", email: "fan@example.com", musician: false) + @band = FactoryGirl.create(:band, name: "Peach pit", website: "www.bands.com", biography: "zomg we rock") end def assert_peachy_data diff --git a/spec/jam_ruby/models/tire_search_spec.rb b/spec/jam_ruby/models/tire_search_spec.rb index 237c87204..2ab89207d 100644 --- a/spec/jam_ruby/models/tire_search_spec.rb +++ b/spec/jam_ruby/models/tire_search_spec.rb @@ -20,8 +20,8 @@ describe "tire search" do end it "full search for single user" do - @user = User.save(name: "User One", email: "user@example.com", - password: "foobar", password_confirmation: "foobar", musician: true) + @user = FactoryGirl.create(:user, name: "User One", email: "user@example.com", musician: true) + User.search_index.refresh s = Tire.search ['test-jamruby-users', 'test-jamruby-bands'], :load => true do @@ -35,7 +35,7 @@ describe "tire search" do end it "full search for single band" do - @band = Band.save(name: "Example Band", website: "www.bands.com", biography: "zomg we rock") + @band = FactoryGirl.create(:band, name: "Example Band", website: "www.bands.com", biography: "zomg we rock") Band.search_index.refresh s = Tire.search ['test-jamruby-users', 'test-jamruby-bands'], :load => true do @@ -49,9 +49,8 @@ describe "tire search" do end it "full search for a band & user" do - @user = User.save(name: "Peach", email: "user@example.com", - password: "foobar", password_confirmation: "foobar", musician: true) - @band = Band.save(name: "Peach pit", website: "www.bands.com", biography: "zomg we rock") + @user = FactoryGirl.create(:user, name: "Peach", email: "user@example.com", musician: true) + @band = FactoryGirl.create(:band, name: "Peach pit", website: "www.bands.com", biography: "zomg we rock") User.search_index.refresh Band.search_index.refresh @@ -71,9 +70,8 @@ describe "tire search" do it "pagination" do - @user = User.save(name: "Peach", email: "user@example.com", - password: "foobar", password_confirmation: "foobar", musician: true) - @band = Band.save(name: "Peach pit", website: "www.bands.com", biography: "zomg we rock") + @user = FactoryGirl.create(:user, name: "Peach", email: "user@example.com", musician: true) + @band = FactoryGirl.create(:band, name: "Peach pit", website: "www.bands.com", biography: "zomg we rock") User.search_index.refresh Band.search_index.refresh @@ -113,8 +111,7 @@ describe "tire search" do s.results.total.should == 0 - @user = User.save(name: "Peach", email: "user@example.com", - password: "foobar", password_confirmation: "foobar", musician: true) + @user = FactoryGirl.create(:user, name: "Peach", email: "user@example.com", musician: true) User.search_index.refresh sleep 1 # https://jamkazam.atlassian.net/browse/VRFS-69 diff --git a/spec/jam_ruby/models/user_search_spec.rb b/spec/jam_ruby/models/user_search_spec.rb index 4fa012121..31d63ed8c 100644 --- a/spec/jam_ruby/models/user_search_spec.rb +++ b/spec/jam_ruby/models/user_search_spec.rb @@ -6,8 +6,8 @@ describe User do User.delete_search_index User.create_search_index - @user = User.save(name: "Example User", email: "user@example.com", - password: "foobar", password_confirmation: "foobar", musician: true) + @user = FactoryGirl.create(:user, name: "Example User", email: "user@example.com", + password: "foobar", password_confirmation: "foobar", musician: true, email_confirmed: true) # you have to poke elasticsearch because it will batch requests internally for a second User.search_index.refresh @@ -59,12 +59,31 @@ describe User do end it "should tokenize correctly" do - @user2 = User.save(name: "peaches", email: "peach@example.com", - password: "foobar", password_confirmation: "foobar", musician: true) + @user2 = FactoryGirl.create(:user, name: "peaches", email: "peach@example.com", + password: "foobar", password_confirmation: "foobar", musician: true, email_confirmed: true) User.search_index.refresh ws = User.search("pea") ws.results.length.should == 1 user_result = ws.results[0] user_result.id.should == @user2.id + end + + + it "users who have signed up, but not confirmed should not show up in search index" do + @user3 = FactoryGirl.create(:user, name: "unconfirmed", email: "unconfirmed@example.com", + password: "foobar", password_confirmation: "foobar", musician: true, email_confirmed: false) + User.search_index.refresh + ws = User.search("unconfirmed") + ws.results.length.should == 0 + + # Ok, confirm the user, and see them show up + @user3.email_confirmed = true + @user3.save + User.search_index.refresh + + ws = User.search("unconfirmed") + ws.results.length.should == 1 + user_result = ws.results[0] + user_result.id.should == @user3.id end end \ No newline at end of file diff --git a/spec/jam_ruby/user_manager_spec.rb b/spec/jam_ruby/user_manager_spec.rb new file mode 100644 index 000000000..f47c89c9a --- /dev/null +++ b/spec/jam_ruby/user_manager_spec.rb @@ -0,0 +1,81 @@ +require 'spec_helper' + +# these tests avoid the use of ActiveRecord and FactoryGirl to do blackbox, non test-instrumented tests +describe UserManager do + + + before(:each) do + @user_manager = UserManager.new(:conn => @conn) + UserMailer.deliveries.clear + end + + describe "signup" do + it "signup successfully" do + @user = @user_manager.signup("bob", "bob@jamkazam.com", "foobar", "foobar", "Austin", "TX", "USA", nil, "http://localhost:3000/confirm" ) + + @user.errors.any?.should be_false + @user.name.should == "bob" + @user.email.should == "bob@jamkazam.com" + @user.email_confirmed.should be_false + @user.city.should == "Austin" + @user.state.should == "TX" + @user.country.should == "USA" + @user.instruments.length.should == 0 + @user.signup_token.should_not be_nil + + UserMailer.deliveries.length.should == 1 + end + + it "duplicate signup failure" do + @user = @user_manager.signup("bob", "bob@jamkazam.com", "foobar", "foobar", "Austin", "TX", "USA", nil, "http://localhost:3000/confirm" ) + UserMailer.deliveries.length.should == 1 + @user.errors.any?.should be_false + + # exactly the same parameters; should dup on email, and send no email + @user = @user_manager.signup("bob", "bob@jamkazam.com", "foobar", "foobar", "Austin", "TX", "USA", nil, "http://localhost:3000/confirm" ) + UserMailer.deliveries.length.should == 1 + @user.errors.any?.should be_true + @user.errors[:email][0].should == "has already been taken" + + # change email so that name appears dupped + @user = @user_manager.signup("bob", "bobbie@jamkazam.com", "foobar", "foobar", "Austin", "TX", "USA", nil, "http://localhost:3000/confirm" ) + UserMailer.deliveries.length.should == 1 + @user.errors.any?.should be_true + @user.errors[:name][0].should == "has already been taken" + end + + it "fail on no username" do + @user = @user_manager.signup("", "bob@jamkazam.com", "foobar", "foobar", "Austin", "TX", "USA", nil, "http://localhost:3000/confirm" ) + UserMailer.deliveries.length.should == 0 + @user.errors.any?.should be_true + @user.errors[:name][0].should == "can't be blank" + end + + it "fail on no username" do + @user = @user_manager.signup("murp", "", "foobar", "foobar", "Austin", "TX", "USA", nil, "http://localhost:3000/confirm" ) + UserMailer.deliveries.length.should == 0 + @user.errors.any?.should be_true + @user.errors[:email][0].should == "can't be blank" + end + end + + describe "signup_confirm" do + it "fail on no username" do + @user = @user_manager.signup("bob", "bob@jamkazam.com", "foobar", "foobar", "Austin", "TX", "USA", nil, "http://localhost:3000/confirm" ) + @user = @user_manager.signup_confirm(@user.signup_token) + @user.email_confirmed.should be_true + end + + it "fail to confirm bogus signup token" do + expect { @user_manager.signup_confirm("murp") }.to raise_error ActiveRecord::RecordNotFound + end + + it "fail to confirm empty signup token" do + expect { @user_manager.signup_confirm("") }.to raise_error ActiveRecord::RecordNotFound + end + + it "fail to confirm nil signup token" do + expect { @user_manager.signup_confirm(nil) }.to raise_error ActiveRecord::RecordNotFound + end + end +end diff --git a/spec/mailers/user_mailer_spec.rb b/spec/mailers/user_mailer_spec.rb new file mode 100644 index 000000000..5364de7e5 --- /dev/null +++ b/spec/mailers/user_mailer_spec.rb @@ -0,0 +1,44 @@ +require "spec_helper" + +describe UserMailer do + + let(:user) { FactoryGirl.create(:user) } + + before(:each) do + UserMailer.deliveries.clear + end + + it "should send welcome email" do + signup_confirmation_url = "http://example.com/confirm" + signup_confirmation_url_with_token = "#{signup_confirmation_url}/#{user.signup_token}" + UserMailer.welcome_message(user, signup_confirmation_url_with_token).deliver + + UserMailer.deliveries.length.should == 1 + mail = UserMailer.deliveries[0] + + mail['from'].to_s.should == UserMailer::DEFAULT_SENDER + mail['to'].to_s.should == user.email + mail.multipart?.should == true # because we send plain + html + + # verify that the messages are correctly configured + mail.html_part.body.include?("Welcome").should be_true + mail.html_part.body.include?(signup_confirmation_url_with_token).should be_true + mail.text_part.body.include?("Welcome").should be_true + mail.text_part.body.include?(signup_confirmation_url_with_token).should be_true + end + + it "should send reset password" do + UserMailer.reset_password(user).deliver + + UserMailer.deliveries.length.should == 1 + mail = UserMailer.deliveries[0] + + mail['from'].to_s.should == UserMailer::DEFAULT_SENDER + mail['to'].to_s.should == user.email + mail.multipart?.should == true # because we send plain + html + + # verify that the messages are correctly configured + mail.html_part.body.include?("Reset").should be_true + mail.text_part.body.include?("Reset").should be_true + end +end From d55ba4fa5efb7af6741e99cdd029efe59ebfa5cd Mon Sep 17 00:00:00 2001 From: Seth Call Date: Mon, 12 Nov 2012 06:55:03 -0600 Subject: [PATCH 071/274] * verified instruments working VRFS-72 --- lib/jam_ruby/user_manager.rb | 12 ++++++------ spec/jam_ruby/user_manager_spec.rb | 11 +++++++++++ spec/spec_helper.rb | 2 +- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/lib/jam_ruby/user_manager.rb b/lib/jam_ruby/user_manager.rb index 446f59de2..33b64b4ba 100644 --- a/lib/jam_ruby/user_manager.rb +++ b/lib/jam_ruby/user_manager.rb @@ -23,15 +23,15 @@ module JamRuby user.state = state user.country = country unless instruments.nil? - instruments.each_with_index do |musician_instrument_param, index| - instrument = Instrument.find(musician_instrument_param["id"]) + instruments.each do |musician_instrument_param| + instrument = Instrument.find(musician_instrument_param[:instrument_id]) musician_instrument = MusicianInstrument.new - musician_instrument.user = current_user + musician_instrument.user = user musician_instrument.instrument = instrument - musician_instrument.proficiency_level = musician_instrument_param["proficiency_level"] - musician_instrument.priority = index + musician_instrument.proficiency_level = musician_instrument_param[:proficiency_level] + musician_instrument.priority = musician_instrument_param[:priority] musician_instrument.save - user.instruments < musician_instrument + user.musician_instruments << musician_instrument end end user.signup_token = SecureRandom.urlsafe_base64 diff --git a/spec/jam_ruby/user_manager_spec.rb b/spec/jam_ruby/user_manager_spec.rb index f47c89c9a..74f8ef3ff 100644 --- a/spec/jam_ruby/user_manager_spec.rb +++ b/spec/jam_ruby/user_manager_spec.rb @@ -26,6 +26,17 @@ describe UserManager do UserMailer.deliveries.length.should == 1 end + it "signup successfully with instruments" do + @user = @user_manager.signup("bob", "bob@jamkazam.com", "foobar", "foobar", "Austin", "TX", "USA", + [{ :instrument_id => "electric guitar", :proficiency_level => 3, :priority => 0}], "http://localhost:3000/confirm" ) + + @user.errors.any?.should be_false + @user.instruments.length.should == 1 + musician_instrument = @user.musician_instruments[0] + musician_instrument.instrument.should == Instrument.find("electric guitar") + musician_instrument.proficiency_level.should == 3 + end + it "duplicate signup failure" do @user = @user_manager.signup("bob", "bob@jamkazam.com", "foobar", "foobar", "Austin", "TX", "USA", nil, "http://localhost:3000/confirm" ) UserMailer.deliveries.length.should == 1 diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index d7084d430..214236359 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -47,7 +47,7 @@ Spork.prefork do config.before(:suite) do DatabaseCleaner.strategy = :transaction - DatabaseCleaner.clean_with(:truncation) + DatabaseCleaner.clean_with(:truncation, {:except => %w[instruments genres] }) end config.before(:each) do From 33c2bbd68ebf389a4c7d79b25c08afd739e6a522 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Mon, 12 Nov 2012 07:26:02 -0600 Subject: [PATCH 072/274] * VRFS-72 --- lib/jam_ruby/models/user.rb | 6 +----- lib/jam_ruby/user_manager.rb | 21 +++++++++++++-------- spec/jam_ruby/models/search_spec.rb | 2 +- spec/jam_ruby/models/user_search_spec.rb | 4 ++-- 4 files changed, 17 insertions(+), 16 deletions(-) diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 239239443..a41f69b8b 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -2,11 +2,7 @@ module JamRuby class User < ActiveRecord::Base include Tire::Model::Search -<<<<<<< HEAD - attr_accessible :name, :email, :password, :password_confirmation, :city, :state, :country -======= - attr_accessible :first_name, :last_name, :name, :email, :password, :password_confirmation ->>>>>>> b2880dc65a3e361fbef0727657fa368336d712ba + attr_accessible :first_name, :last_name, :name, :email, :password, :password_confirmation, :city, :state, :country attr_accessor :updating_password self.primary_key = 'id' diff --git a/lib/jam_ruby/user_manager.rb b/lib/jam_ruby/user_manager.rb index 33b64b4ba..cbcf746f0 100644 --- a/lib/jam_ruby/user_manager.rb +++ b/lib/jam_ruby/user_manager.rb @@ -50,15 +50,20 @@ module JamRuby return user end - # throws RecordNotFound if signup token is invalid + # throws RecordNotFound if signup token is invalid; i.e., if it's nil, empty string, or not belonging to a user def signup_confirm(signup_token) - UserManager.active_record_transaction do |user_manager| - # throws ActiveRecord::RecordNotFound if invalid - user = User.find_by_signup_token!(signup_token) - user.signup_token = nil - user.email_confirmed = true - user.save - return user + if signup_token.nil? || signup_token.empty? + # there are plenty of confirmed users with nil signup_tokens, so we can't look on it + raise ActiveRecord::RecordNotFound + else + UserManager.active_record_transaction do |user_manager| + # throws ActiveRecord::RecordNotFound if invalid + user = User.find_by_signup_token!(signup_token) + user.signup_token = nil + user.email_confirmed = true + user.save + return user + end end end end diff --git a/spec/jam_ruby/models/search_spec.rb b/spec/jam_ruby/models/search_spec.rb index e3f6b0868..dfcc79692 100644 --- a/spec/jam_ruby/models/search_spec.rb +++ b/spec/jam_ruby/models/search_spec.rb @@ -12,7 +12,7 @@ describe Search do def create_peachy_data @user = FactoryGirl.create(:user, first_name: "Peach", last_name: "Pit", name: "peachy", email: "user@example.com", musician: true) - @fan = FactoryGirl.create(:user, first_name: "Peach Peach", last_name: "Pit", name: "peachy", email: "fan@example.com", musician: false) + @fan = FactoryGirl.create(:user, first_name: "Peach Peach", last_name: "Pit", name: "peachy2", email: "fan@example.com", musician: false) @band = FactoryGirl.create(:band, name: "Peach pit", website: "www.bands.com", biography: "zomg we rock") end diff --git a/spec/jam_ruby/models/user_search_spec.rb b/spec/jam_ruby/models/user_search_spec.rb index 3d50ec8af..20525778e 100644 --- a/spec/jam_ruby/models/user_search_spec.rb +++ b/spec/jam_ruby/models/user_search_spec.rb @@ -60,7 +60,7 @@ describe User do end it "should tokenize correctly" do - @user2 = FactoryGirl.create(:user, first_name: "peaches", last_name: "test", name: "peachy", email: "peach@example.com", + @user2 = FactoryGirl.create(:user, first_name: "peaches", last_name: "test", name: "peachy2", email: "peach@example.com", password: "foobar", password_confirmation: "foobar", musician: true, email_confirmed: true) User.search_index.refresh ws = User.search("pea") @@ -71,7 +71,7 @@ describe User do it "users who have signed up, but not confirmed should not show up in search index" do - @user3 = FactoryGirl.create(:user, name: "unconfirmed", email: "unconfirmed@example.com", + @user3 = FactoryGirl.create(:user, first_name: "unconfirmed", last_name: "unconfirmed", name: "unconfirmed", email: "unconfirmed@example.com", password: "foobar", password_confirmation: "foobar", musician: true, email_confirmed: false) User.search_index.refresh ws = User.search("unconfirmed") From 5ba930229037e0d3eae7bf2474e795bca9967493 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Mon, 12 Nov 2012 21:52:05 -0500 Subject: [PATCH 073/274] add account fields --- lib/jam_ruby/models/user.rb | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index d4db360c8..b66c79360 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -8,6 +8,9 @@ module JamRuby self.primary_key = 'id' + # account + belongs_to :account, :class_name => "JamRuby::Account" + # connections (websocket-gateway) has_many :connections, :class_name => "JamRuby::Connection" @@ -90,10 +93,6 @@ module JamRuby updating_password || new_record? end - #def name - # return self.first_name + " " + self.last_name - #end - def friends?(user) return self.friends.exists?(user) end @@ -107,7 +106,7 @@ module JamRuby end def following_count - return self.followings.size + return self.followings.size + self.band_followings.size end def to_s @@ -124,6 +123,11 @@ module JamRuby user = User.find(params[:id]) end + # account id + unless params[:account_id].nil? + user.account_id = params[:account_id] + end + # first name unless params[:first_name].nil? user.first_name = params[:first_name] @@ -159,6 +163,21 @@ module JamRuby user.musician = params[:musician] end + # gender + unless params[:gender].nil? + account.gender = params[:gender] + end + + # birthdate + unless params[:birth_date].nil? + account.birth_date = params[:birth_date] + end + + # ISP + unless params[:internet_service_provider].nil? + account.internet_service_provider = params[:internet_service_provider] + end + # city unless params[:city].nil? user.city = params[:city] @@ -255,7 +274,6 @@ module JamRuby ### Elasticsearch/Tire integration - private def create_remember_token self.remember_token = SecureRandom.urlsafe_base64 From 1a81f6b58bfd367e174ff4c078662dcf4b469874 Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Mon, 12 Nov 2012 23:40:06 -0800 Subject: [PATCH 074/274] Added authorization --- lib/jam_ruby/models/user.rb | 3 +++ lib/jam_ruby/models/user_authorization.rb | 11 +++++++++++ 2 files changed, 14 insertions(+) create mode 100644 lib/jam_ruby/models/user_authorization.rb diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index d4db360c8..495abc3e1 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -8,6 +8,9 @@ module JamRuby self.primary_key = 'id' + # authorizations (for facebook, etc -- omniauth) + has_many :user_authorizations + # connections (websocket-gateway) has_many :connections, :class_name => "JamRuby::Connection" diff --git a/lib/jam_ruby/models/user_authorization.rb b/lib/jam_ruby/models/user_authorization.rb new file mode 100644 index 000000000..08230547f --- /dev/null +++ b/lib/jam_ruby/models/user_authorization.rb @@ -0,0 +1,11 @@ +module JamRuby + class UserAuthorization < ActiveRecord::Base + + self.table_name = "users_authorizations" + + self.primary_key = 'id' + + belongs_to :user + validates :provider, :uid, :presence => true + end +end From 8846f5d3d337ab9e59f5e781171511130c4b56f2 Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Tue, 13 Nov 2012 13:20:04 -0800 Subject: [PATCH 075/274] added model --- lib/jam_ruby.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index c930bbac8..4a46b547b 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -17,6 +17,7 @@ require "jam_ruby/tire_tasks" require "jam_ruby/message_factory" require "jam_ruby/models/genre" require "jam_ruby/models/user" +require "jam_ruby/models/user_authorization" require "jam_ruby/models/band" require "jam_ruby/models/connection" require "jam_ruby/models/friendship" From ed76fe869911a9f386fc516260789f14723e006a Mon Sep 17 00:00:00 2001 From: Seth Call Date: Tue, 13 Nov 2012 23:37:18 -0600 Subject: [PATCH 076/274] * moving user_manager out of jam_ruby, but moving alot of logic within user_manager to User model --- lib/jam_ruby.rb | 1 - lib/jam_ruby/models/user.rb | 76 ++++++++++++++++++++++++ lib/jam_ruby/user_manager.rb | 70 ----------------------- spec/jam_ruby/models/user_spec.rb | 24 ++++++++ spec/jam_ruby/user_manager_spec.rb | 92 ------------------------------ 5 files changed, 100 insertions(+), 163 deletions(-) delete mode 100644 lib/jam_ruby/user_manager.rb delete mode 100644 spec/jam_ruby/user_manager_spec.rb diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index dfbb727ff..63c5d0c7c 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -13,7 +13,6 @@ require "jam_ruby/errors/state_error" require "jam_ruby/errors/jam_argument_error" require "jam_ruby/mq_router" require "jam_ruby/base_manager" -require "jam_ruby/user_manager" require "jam_ruby/connection_manager" require "jam_ruby/version" require "jam_ruby/environment" diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index b824834cc..b4cecfe78 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -224,6 +224,82 @@ module JamRuby end + # throws ActiveRecord::RecordNotFound if instrument is invalid + # throws an email delivery error if unable to connect out to SMTP + def self.signup(name, email, password, password_confirmation, + city, state, country, instruments, signup_confirm_url) + user = User.new + + UserManager.active_record_transaction do |user_manager| + user.name = name + user.email = email + user.password = password + user.password_confirmation = password_confirmation + user.admin = false + user.email_confirmed = false + user.city = city + user.state = state + user.country = country + unless instruments.nil? + instruments.each do |musician_instrument_param| + instrument = Instrument.find(musician_instrument_param[:instrument_id]) + musician_instrument = MusicianInstrument.new + musician_instrument.user = user + musician_instrument.instrument = instrument + musician_instrument.proficiency_level = musician_instrument_param[:proficiency_level] + musician_instrument.priority = musician_instrument_param[:priority] + musician_instrument.save + user.musician_instruments << musician_instrument + end + end + user.signup_token = SecureRandom.urlsafe_base64 + + user.save + + if user.errors.any? + raise ActiveRecord::Rollback + else + # any errors here should also rollback the transaction; that's OK. If emails aren't going to be delivered, + # it's already a really bad situation; make user signup again + UserMailer.welcome_message(user, signup_confirm_url + "/" + user.signup_token).deliver + end + end + + return user + end + + # throws RecordNotFound if signup token is invalid; i.e., if it's nil, empty string, or not belonging to a user + def self.signup_confirm(signup_token) + if signup_token.nil? || signup_token.empty? + # there are plenty of confirmed users with nil signup_tokens, so we can't look on it + raise ActiveRecord::RecordNotFound + else + UserManager.active_record_transaction do |user_manager| + # throws ActiveRecord::RecordNotFound if invalid + user = User.find_by_signup_token!(signup_token) + user.signup_token = nil + user.email_confirmed = true + user.save + return user + end + end + end + + # if valid credentials are supplied for an 'active' user, returns the user + # if not authenticated, returns nil + def self.authenticate(email, password) + # we only allow users that have confirmed email to authenticate + user = User.where('email_confirmed=true').find_by_email(email) + + if user && user.authenticate(password) + return user + else + return nil + end + + end + + ### Elasticsearch/Tire integration ### # # Define the name based on the environment diff --git a/lib/jam_ruby/user_manager.rb b/lib/jam_ruby/user_manager.rb deleted file mode 100644 index cbcf746f0..000000000 --- a/lib/jam_ruby/user_manager.rb +++ /dev/null @@ -1,70 +0,0 @@ -module JamRuby - class UserManager < BaseManager - - def initialize(options={}) - super(options) - @log = Logging.logger[self] - end - - # throws ActiveRecord::RecordNotFound if instrument is invalid - # throws an email delivery error if unable to connect out to SMTP - def signup(name, email, password, password_confirmation, - city, state, country, instruments, signup_confirm_url) - user = User.new - - UserManager.active_record_transaction do |user_manager| - user.name = name - user.email = email - user.password = password - user.password_confirmation = password_confirmation - user.admin = false - user.email_confirmed = false - user.city = city - user.state = state - user.country = country - unless instruments.nil? - instruments.each do |musician_instrument_param| - instrument = Instrument.find(musician_instrument_param[:instrument_id]) - musician_instrument = MusicianInstrument.new - musician_instrument.user = user - musician_instrument.instrument = instrument - musician_instrument.proficiency_level = musician_instrument_param[:proficiency_level] - musician_instrument.priority = musician_instrument_param[:priority] - musician_instrument.save - user.musician_instruments << musician_instrument - end - end - user.signup_token = SecureRandom.urlsafe_base64 - - user.save - - if user.errors.any? - raise ActiveRecord::Rollback - else - # any errors here should also rollback the transaction; that's OK. If emails aren't going to be delivered, - # it's already a really bad situation; make user signup again - UserMailer.welcome_message(user, signup_confirm_url + "/" + user.signup_token).deliver - end - end - - return user - end - - # throws RecordNotFound if signup token is invalid; i.e., if it's nil, empty string, or not belonging to a user - def signup_confirm(signup_token) - if signup_token.nil? || signup_token.empty? - # there are plenty of confirmed users with nil signup_tokens, so we can't look on it - raise ActiveRecord::RecordNotFound - else - UserManager.active_record_transaction do |user_manager| - # throws ActiveRecord::RecordNotFound if invalid - user = User.find_by_signup_token!(signup_token) - user.signup_token = nil - user.email_confirmed = true - user.save - return user - end - end - end - end -end \ No newline at end of file diff --git a/spec/jam_ruby/models/user_spec.rb b/spec/jam_ruby/models/user_spec.rb index 97b651353..7f0f06f49 100644 --- a/spec/jam_ruby/models/user_spec.rb +++ b/spec/jam_ruby/models/user_spec.rb @@ -145,4 +145,28 @@ describe User do before { @user.save } its(:remember_token) { should_not be_blank } end + + describe "authenticate (class-instance)" do + before { @user.email_confirmed=true; @user.save } + + describe "with valid password" do + it { should == User.authenticate(@user.email, @user.password) } + end + + describe "with invalid password" do + it { User.authenticate(@user.email, "invalid").should be_nil } + end + + describe "with invalid email" do + it { User.authenticate("junk", "invalid").should be_nil } + end + + describe "with nil args" do + it { User.authenticate(nil, nil).should be_nil } + end + + describe "with empty args" do + it { User.authenticate("", "").should be_nil } + end + end end \ No newline at end of file diff --git a/spec/jam_ruby/user_manager_spec.rb b/spec/jam_ruby/user_manager_spec.rb deleted file mode 100644 index 74f8ef3ff..000000000 --- a/spec/jam_ruby/user_manager_spec.rb +++ /dev/null @@ -1,92 +0,0 @@ -require 'spec_helper' - -# these tests avoid the use of ActiveRecord and FactoryGirl to do blackbox, non test-instrumented tests -describe UserManager do - - - before(:each) do - @user_manager = UserManager.new(:conn => @conn) - UserMailer.deliveries.clear - end - - describe "signup" do - it "signup successfully" do - @user = @user_manager.signup("bob", "bob@jamkazam.com", "foobar", "foobar", "Austin", "TX", "USA", nil, "http://localhost:3000/confirm" ) - - @user.errors.any?.should be_false - @user.name.should == "bob" - @user.email.should == "bob@jamkazam.com" - @user.email_confirmed.should be_false - @user.city.should == "Austin" - @user.state.should == "TX" - @user.country.should == "USA" - @user.instruments.length.should == 0 - @user.signup_token.should_not be_nil - - UserMailer.deliveries.length.should == 1 - end - - it "signup successfully with instruments" do - @user = @user_manager.signup("bob", "bob@jamkazam.com", "foobar", "foobar", "Austin", "TX", "USA", - [{ :instrument_id => "electric guitar", :proficiency_level => 3, :priority => 0}], "http://localhost:3000/confirm" ) - - @user.errors.any?.should be_false - @user.instruments.length.should == 1 - musician_instrument = @user.musician_instruments[0] - musician_instrument.instrument.should == Instrument.find("electric guitar") - musician_instrument.proficiency_level.should == 3 - end - - it "duplicate signup failure" do - @user = @user_manager.signup("bob", "bob@jamkazam.com", "foobar", "foobar", "Austin", "TX", "USA", nil, "http://localhost:3000/confirm" ) - UserMailer.deliveries.length.should == 1 - @user.errors.any?.should be_false - - # exactly the same parameters; should dup on email, and send no email - @user = @user_manager.signup("bob", "bob@jamkazam.com", "foobar", "foobar", "Austin", "TX", "USA", nil, "http://localhost:3000/confirm" ) - UserMailer.deliveries.length.should == 1 - @user.errors.any?.should be_true - @user.errors[:email][0].should == "has already been taken" - - # change email so that name appears dupped - @user = @user_manager.signup("bob", "bobbie@jamkazam.com", "foobar", "foobar", "Austin", "TX", "USA", nil, "http://localhost:3000/confirm" ) - UserMailer.deliveries.length.should == 1 - @user.errors.any?.should be_true - @user.errors[:name][0].should == "has already been taken" - end - - it "fail on no username" do - @user = @user_manager.signup("", "bob@jamkazam.com", "foobar", "foobar", "Austin", "TX", "USA", nil, "http://localhost:3000/confirm" ) - UserMailer.deliveries.length.should == 0 - @user.errors.any?.should be_true - @user.errors[:name][0].should == "can't be blank" - end - - it "fail on no username" do - @user = @user_manager.signup("murp", "", "foobar", "foobar", "Austin", "TX", "USA", nil, "http://localhost:3000/confirm" ) - UserMailer.deliveries.length.should == 0 - @user.errors.any?.should be_true - @user.errors[:email][0].should == "can't be blank" - end - end - - describe "signup_confirm" do - it "fail on no username" do - @user = @user_manager.signup("bob", "bob@jamkazam.com", "foobar", "foobar", "Austin", "TX", "USA", nil, "http://localhost:3000/confirm" ) - @user = @user_manager.signup_confirm(@user.signup_token) - @user.email_confirmed.should be_true - end - - it "fail to confirm bogus signup token" do - expect { @user_manager.signup_confirm("murp") }.to raise_error ActiveRecord::RecordNotFound - end - - it "fail to confirm empty signup token" do - expect { @user_manager.signup_confirm("") }.to raise_error ActiveRecord::RecordNotFound - end - - it "fail to confirm nil signup token" do - expect { @user_manager.signup_confirm(nil) }.to raise_error ActiveRecord::RecordNotFound - end - end -end From ecf792b404e394bf3c781affcd23a0f195131285 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Wed, 14 Nov 2012 22:24:30 -0500 Subject: [PATCH 077/274] VRFS-89, VRFS-90 remove name from user model --- lib/jam_ruby/app/mailers/user_mailer.rb | 2 +- .../user_mailer/reset_password.html.erb | 2 +- .../user_mailer/reset_password.text.erb | 2 +- .../user_mailer/welcome_message.html.erb | 2 +- .../user_mailer/welcome_message.text.erb | 2 +- lib/jam_ruby/models/user.rb | 19 +++++----- spec/factories.rb | 1 - spec/jam_ruby/connection_manager_spec.rb | 36 +++++++++---------- spec/jam_ruby/models/search_spec.rb | 4 +-- spec/jam_ruby/models/tire_search_spec.rb | 8 ++--- spec/jam_ruby/models/user_search_spec.rb | 6 ++-- spec/jam_ruby/models/user_spec.rb | 2 +- 12 files changed, 42 insertions(+), 44 deletions(-) diff --git a/lib/jam_ruby/app/mailers/user_mailer.rb b/lib/jam_ruby/app/mailers/user_mailer.rb index 2470eeeb1..517336c37 100644 --- a/lib/jam_ruby/app/mailers/user_mailer.rb +++ b/lib/jam_ruby/app/mailers/user_mailer.rb @@ -23,7 +23,7 @@ module JamRuby sendgrid_category "Welcome" sendgrid_unique_args :type => "welcome_message" - mail(:to => user.email, :subject => "Welcome #{user.name} to Jamkazam") do |format| + mail(:to => user.email, :subject => "Welcome #{user.first_name} to Jamkazam") do |format| format.text format.html end diff --git a/lib/jam_ruby/app/views/jam_ruby/user_mailer/reset_password.html.erb b/lib/jam_ruby/app/views/jam_ruby/user_mailer/reset_password.html.erb index e1f037477..0b757c910 100644 --- a/lib/jam_ruby/app/views/jam_ruby/user_mailer/reset_password.html.erb +++ b/lib/jam_ruby/app/views/jam_ruby/user_mailer/reset_password.html.erb @@ -1,5 +1,5 @@ -Reset Password! <%= @user.name %> +Reset Password! <%= @user.email %> \ No newline at end of file diff --git a/lib/jam_ruby/app/views/jam_ruby/user_mailer/reset_password.text.erb b/lib/jam_ruby/app/views/jam_ruby/user_mailer/reset_password.text.erb index e42aff656..6507fcc0b 100644 --- a/lib/jam_ruby/app/views/jam_ruby/user_mailer/reset_password.text.erb +++ b/lib/jam_ruby/app/views/jam_ruby/user_mailer/reset_password.text.erb @@ -1 +1 @@ -Reset Password! <%= @user.name %> \ No newline at end of file +Reset Password! <%= @user.email %> \ No newline at end of file diff --git a/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.html.erb b/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.html.erb index 54d20dfb5..351b4186f 100644 --- a/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.html.erb +++ b/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.html.erb @@ -1,6 +1,6 @@ -

Welcome! <%= @user.name %>.

+

Welcome! <%= @user.first_name %>.

To confirm your registration, please go to the signup confirmation page..

\ No newline at end of file diff --git a/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.text.erb b/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.text.erb index 3667d0935..d8601050b 100644 --- a/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.text.erb +++ b/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.text.erb @@ -1,2 +1,2 @@ -Welcome! <%= @user.name %> +Welcome! <%= @user.first_name %> To confirm your registration, please go to the signup confirmation page at : <%= @signup_confirm_url %>. \ No newline at end of file diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index b4cecfe78..7a3c0c56a 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -2,7 +2,7 @@ module JamRuby class User < ActiveRecord::Base include Tire::Model::Search - attr_accessible :first_name, :last_name, :name, :email, :password, :password_confirmation, :city, :state, :country + attr_accessible :first_name, :last_name, :email, :password, :password_confirmation, :city, :state, :country attr_accessor :updating_password self.primary_key = 'id' @@ -61,7 +61,6 @@ module JamRuby after_save :limit_to_five_instruments - validates :name, uniqueness: {case_sensitive: false}, presence: true, length: {maximum: 50} validates :first_name, presence: true, length: {maximum: 50} validates :last_name, presence: true, length: {maximum: 50} VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i @@ -110,7 +109,11 @@ module JamRuby def to_s return email unless email.nil? - return name unless name.nil? + + if !first_name.nil? && !last_name.nil? + return first_name + ' ' + last_name + end + return id end @@ -137,11 +140,6 @@ module JamRuby user.last_name = params[:last_name] end - # username - unless params[:name].nil? - user.name = params[:name] - end - # email unless params[:email].nil? user.email = params[:email] @@ -226,12 +224,13 @@ module JamRuby # throws ActiveRecord::RecordNotFound if instrument is invalid # throws an email delivery error if unable to connect out to SMTP - def self.signup(name, email, password, password_confirmation, + def self.signup(first_name, last_name, email, password, password_confirmation, city, state, country, instruments, signup_confirm_url) user = User.new UserManager.active_record_transaction do |user_manager| - user.name = name + user.first_name = first_name + user.last_name = last_name user.email = email user.password = password user.password_confirmation = password_confirmation diff --git a/spec/factories.rb b/spec/factories.rb index bc82f9e43..93602f854 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -1,6 +1,5 @@ FactoryGirl.define do factory :user, :class => JamRuby::User do - sequence(:name) { |n| "Person #{n}" } sequence(:email) { |n| "person_#{n}@example.com"} password "foobar" password_confirmation "foobar" diff --git a/spec/jam_ruby/connection_manager_spec.rb b/spec/jam_ruby/connection_manager_spec.rb index fd660fa3f..dbe55e02b 100644 --- a/spec/jam_ruby/connection_manager_spec.rb +++ b/spec/jam_ruby/connection_manager_spec.rb @@ -10,8 +10,8 @@ describe ConnectionManager do @message_factory = MessageFactory.new end - def create_user(name, email) - @conn.exec("INSERT INTO users (name, email, password_digest) VALUES ($1, $2, $3) RETURNING id", [name, email, '1']) do |result| + def create_user(first_name, last_name, email) + @conn.exec("INSERT INTO users (first_name, last_name, email, password_digest) VALUES ($1, $2, $3, $4) RETURNING id", [first_name, last_name, email, '1']) do |result| return result.getvalue(0, 0) end end @@ -51,7 +51,7 @@ describe ConnectionManager do it "can't create two client_ids of same value" do client_id = "client_id1" - user_id = create_user("user1", "user1@jamkazam.com") + user_id = create_user("test", "user1", "user1@jamkazam.com") @conn.transaction do @connman.create_connection(user_id, client_id, "1.1.1.1") @@ -62,7 +62,7 @@ describe ConnectionManager do it "create connection then delete it" do client_id = "client_id2" - user_id = create_user("user2", "user2@jamkazam.com") + user_id = create_user("test", "user2", "user2@jamkazam.com") @conn.transaction do @connman.create_connection(user_id, client_id, "1.1.1.1") end @@ -85,7 +85,7 @@ describe ConnectionManager do client_id = "client_id3" client_id2 = "client_id3_1" - user_id = create_user("user3", "user3@jamkazam.com") + user_id = create_user("test", "user3", "user3@jamkazam.com") # we should get a message saying that this user is online friend_update = @message_factory.friend_update(user_id, true) @@ -110,7 +110,7 @@ describe ConnectionManager do client_id = "client_id4" client_id2 = "client_id4_1" - user_id = create_user("user4", "user4@jamkazam.com") + user_id = create_user("test", "user4", "user4@jamkazam.com") # we should get a message saying that this user is online @@ -152,9 +152,9 @@ describe ConnectionManager do client_id = "client_id5" - user_id1 = create_user("user5", "user5@jamkazam.com") - user_id2 = create_user("user6", "user6@jamkazam.com") - user_id3 = create_user("user7", "user7@jamkazam.com") + user_id1 = create_user("test", "user5", "user5@jamkazam.com") + user_id2 = create_user("test", "user6", "user6@jamkazam.com") + user_id3 = create_user("test", "user7", "user7@jamkazam.com") @connman.gather_friends(@conn, user_id1).should == [] @connman.gather_friends(@conn, user_id2).should == [] @@ -193,7 +193,7 @@ describe ConnectionManager do client_id = "client_id8" - user_id = create_user("user8", "user8@jamkazam.com") + user_id = create_user("test", "user8", "user8@jamkazam.com") @conn.transaction do @connman.create_connection(user_id, client_id, "1.1.1.1") @@ -218,7 +218,7 @@ describe ConnectionManager do it "connections with music_sessions associated" do client_id = "client_id9" - user_id = create_user("user9", "user9@jamkazam.com") + user_id = create_user("test", "user9", "user9@jamkazam.com") music_session_id = create_music_session(user_id) @conn.transaction do @@ -243,7 +243,7 @@ describe ConnectionManager do it "join_music_session fails if no connection" do client_id = "client_id10" - user_id = create_user("user10", "user10@jamkazam.com") + user_id = create_user("test", "user10", "user10@jamkazam.com") music_session_id = create_music_session(user_id) @conn.transaction do @@ -255,8 +255,8 @@ describe ConnectionManager do it "join_music_session fails if incorrect user_id specified" do client_id = "client_id20" - user_id = create_user("user20", "user20@jamkazam.com") - user_id2 = create_user("user21", "user21@jamkazam.com") + user_id = create_user("test", "user20", "user20@jamkazam.com") + user_id2 = create_user("test", "user21", "user21@jamkazam.com") music_session_id = create_music_session(user_id) @conn.transaction do @@ -269,7 +269,7 @@ describe ConnectionManager do it "join_music_session fails if no music_session" do client_id = "client_id11" - user_id = create_user("user11", "user11@jamkazam.com") + user_id = create_user("test", "user11", "user11@jamkazam.com") @conn.transaction do @connman.create_connection(user_id, client_id, "1.1.1.1") @@ -284,7 +284,7 @@ describe ConnectionManager do it "leave_music_session fails if no music_session" do client_id = "client_id12" - user_id = create_user("user12", "user12@jamkazam.com") + user_id = create_user("test", "user12", "user12@jamkazam.com") @conn.transaction do @connman.create_connection(user_id, client_id, "1.1.1.1") @@ -298,7 +298,7 @@ describe ConnectionManager do it "leave_music_session fails if in different music_session" do client_id = "client_id13" - user_id = create_user("user13", "user13@jamkazam.com") + user_id = create_user("test", "user13", "user13@jamkazam.com") music_session_id = create_music_session(user_id) @conn.transaction do @@ -317,7 +317,7 @@ describe ConnectionManager do it "leave_music_session works" do client_id = "client_id14" - user_id = create_user("user14", "user14@jamkazam.com") + user_id = create_user("test", "user14", "user14@jamkazam.com") music_session_id = create_music_session(user_id) diff --git a/spec/jam_ruby/models/search_spec.rb b/spec/jam_ruby/models/search_spec.rb index dfcc79692..f1655aab8 100644 --- a/spec/jam_ruby/models/search_spec.rb +++ b/spec/jam_ruby/models/search_spec.rb @@ -11,8 +11,8 @@ describe Search do def create_peachy_data - @user = FactoryGirl.create(:user, first_name: "Peach", last_name: "Pit", name: "peachy", email: "user@example.com", musician: true) - @fan = FactoryGirl.create(:user, first_name: "Peach Peach", last_name: "Pit", name: "peachy2", email: "fan@example.com", musician: false) + @user = FactoryGirl.create(:user, first_name: "Peach", last_name: "Pit", email: "user@example.com", musician: true) + @fan = FactoryGirl.create(:user, first_name: "Peach Peach", last_name: "Pit", email: "fan@example.com", musician: false) @band = FactoryGirl.create(:band, name: "Peach pit", website: "www.bands.com", biography: "zomg we rock") end diff --git a/spec/jam_ruby/models/tire_search_spec.rb b/spec/jam_ruby/models/tire_search_spec.rb index 2987a40e9..c1306d081 100644 --- a/spec/jam_ruby/models/tire_search_spec.rb +++ b/spec/jam_ruby/models/tire_search_spec.rb @@ -20,7 +20,7 @@ describe "tire search" do end it "full search for single user" do - @user = FactoryGirl.create(:user, first_name: "User", last_name: "One", name: "test", email: "user@example.com", musician: true) + @user = FactoryGirl.create(:user, first_name: "User", last_name: "One", email: "user@example.com", musician: true) User.search_index.refresh s = Tire.search ['test-jamruby-users', 'test-jamruby-bands'], :load => true do @@ -48,7 +48,7 @@ describe "tire search" do end it "full search for a band & user" do - @user = FactoryGirl.create(:user, first_name: "Peach", last_name: "Foo", name: "test", email: "user@example.com", musician: true) + @user = FactoryGirl.create(:user, first_name: "Peach", last_name: "Foo", email: "user@example.com", musician: true) @band = FactoryGirl.create(:band, name: "Peach pit", website: "www.bands.com", biography: "zomg we rock") User.search_index.refresh @@ -70,7 +70,7 @@ describe "tire search" do it "pagination" do - @user = FactoryGirl.create(:user, first_name: "Peach", last_name: "foo", name: "test", email: "user@example.com", musician: true) + @user = FactoryGirl.create(:user, first_name: "Peach", last_name: "foo", email: "user@example.com", musician: true) @band = FactoryGirl.create(:band, name: "Peach pit", website: "www.bands.com", biography: "zomg we rock") User.search_index.refresh @@ -111,7 +111,7 @@ describe "tire search" do s.results.total.should == 0 - @user = FactoryGirl.create(:user, first_name: "Peach", last_name: "Pit", name: "peachy", email: "user@example.com", musician: true) + @user = FactoryGirl.create(:user, first_name: "Peach", last_name: "Pit", email: "user@example.com", musician: true) User.search_index.refresh sleep 1 # https://jamkazam.atlassian.net/browse/VRFS-69 diff --git a/spec/jam_ruby/models/user_search_spec.rb b/spec/jam_ruby/models/user_search_spec.rb index 20525778e..27c655e68 100644 --- a/spec/jam_ruby/models/user_search_spec.rb +++ b/spec/jam_ruby/models/user_search_spec.rb @@ -6,7 +6,7 @@ describe User do User.delete_search_index User.create_search_index - @user = FactoryGirl.create(:user, first_name: "Example", last_name: "User", name: "peachy", email: "user@example.com", + @user = FactoryGirl.create(:user, first_name: "Example", last_name: "User", email: "user@example.com", password: "foobar", password_confirmation: "foobar", musician: true, email_confirmed: true) # you have to poke elasticsearch because it will batch requests internally for a second User.search_index.refresh @@ -60,7 +60,7 @@ describe User do end it "should tokenize correctly" do - @user2 = FactoryGirl.create(:user, first_name: "peaches", last_name: "test", name: "peachy2", email: "peach@example.com", + @user2 = FactoryGirl.create(:user, first_name: "peaches", last_name: "test", email: "peach@example.com", password: "foobar", password_confirmation: "foobar", musician: true, email_confirmed: true) User.search_index.refresh ws = User.search("pea") @@ -71,7 +71,7 @@ describe User do it "users who have signed up, but not confirmed should not show up in search index" do - @user3 = FactoryGirl.create(:user, first_name: "unconfirmed", last_name: "unconfirmed", name: "unconfirmed", email: "unconfirmed@example.com", + @user3 = FactoryGirl.create(:user, first_name: "unconfirmed", last_name: "unconfirmed", email: "unconfirmed@example.com", password: "foobar", password_confirmation: "foobar", musician: true, email_confirmed: false) User.search_index.refresh ws = User.search("unconfirmed") diff --git a/spec/jam_ruby/models/user_spec.rb b/spec/jam_ruby/models/user_spec.rb index 7f0f06f49..cadf83fb2 100644 --- a/spec/jam_ruby/models/user_spec.rb +++ b/spec/jam_ruby/models/user_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe User do before do - @user = User.new(first_name: "Example", last_name: "User", name: "test", email: "user@example.com", + @user = User.new(first_name: "Example", last_name: "User", email: "user@example.com", password: "foobar", password_confirmation: "foobar") end From 47b425b5edbd19bec5cc1547635261cbfe92b995 Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Thu, 15 Nov 2012 01:30:55 -0800 Subject: [PATCH 078/274] some more work --- lib/jam_ruby/models/user.rb | 20 +++++++++++++++++--- lib/jam_ruby/models/user_authorization.rb | 9 +++++++-- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 6aa760ab0..d6df4a8b3 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -235,8 +235,18 @@ module JamRuby user.first_name = first_name user.last_name = last_name user.email = email - user.password = password - user.password_confirmation = password_confirmation + + #FIXME: Setting random password for social network logins. This + # is because we have validations all over the place on this. + # The right thing would be to have this null + if password.nil? + user.password = "blahblahblah" + user.password_confirmation = "blahblahblah" + else + user.password = password + user.password_confirmation = password_confirmation + end + user.admin = false user.email_confirmed = false user.city = city @@ -261,9 +271,13 @@ module JamRuby if user.errors.any? raise ActiveRecord::Rollback else + # FIXME: + # It's not standard to require a confirmation when a user signs up with Facebook. + # We should stop asking for it. + # # any errors here should also rollback the transaction; that's OK. If emails aren't going to be delivered, # it's already a really bad situation; make user signup again - UserMailer.welcome_message(user, signup_confirm_url + "/" + user.signup_token).deliver + UserMailer.welcome_message(user, signup_confirm_url.nil? ? nil : (signup_confirm_url + "/" + user.signup_token) ).deliver end end diff --git a/lib/jam_ruby/models/user_authorization.rb b/lib/jam_ruby/models/user_authorization.rb index 08230547f..5f7c97e25 100644 --- a/lib/jam_ruby/models/user_authorization.rb +++ b/lib/jam_ruby/models/user_authorization.rb @@ -1,11 +1,16 @@ module JamRuby class UserAuthorization < ActiveRecord::Base - self.table_name = "users_authorizations" + attr_accessible :provider, :uid, :token, :token_expiration + + self.table_name = "user_authorizations" self.primary_key = 'id' - belongs_to :user + belongs_to :user, :class_name => "JamRuby::User", :foreign_key => "user_id" validates :provider, :uid, :presence => true + + # token and token_expiration can be missing + end end From 3fd79527ef25a8fd7413d3fa732e3385b916dd77 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Thu, 15 Nov 2012 21:08:37 -0500 Subject: [PATCH 079/274] VRFS-81 recordings and favorites --- lib/jam_ruby.rb | 2 ++ lib/jam_ruby/models/band.rb | 7 +++++-- lib/jam_ruby/models/recording.rb | 13 +++++++++++++ lib/jam_ruby/models/user.rb | 12 +++++++----- lib/jam_ruby/models/user_favorite.rb | 10 ++++++++++ 5 files changed, 37 insertions(+), 7 deletions(-) create mode 100644 lib/jam_ruby/models/recording.rb create mode 100644 lib/jam_ruby/models/user_favorite.rb diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index 63c5d0c7c..c64f8a058 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -34,8 +34,10 @@ require "jam_ruby/models/musician_instrument" require "jam_ruby/models/band_musician" require "jam_ruby/models/user_follower" require "jam_ruby/models/user_following" +require "jam_ruby/models/user_favorite" require "jam_ruby/models/band_follower" require "jam_ruby/models/search" +require "jam_ruby/models/recording" include Jampb diff --git a/lib/jam_ruby/models/band.rb b/lib/jam_ruby/models/band.rb index a52e5a2c5..6103f502a 100644 --- a/lib/jam_ruby/models/band.rb +++ b/lib/jam_ruby/models/band.rb @@ -8,12 +8,15 @@ module JamRuby self.primary_key = 'id' # musicians - has_many :band_musicians + has_many :band_musicians, :class_name => "JamRuby::BandMusician" has_many :users, :through => :band_musicians, :class_name => "JamRuby::User" # genres has_and_belongs_to_many :genres, :class_name => "JamRuby::Genre", :join_table => "bands_genres" + # recordings + has_and_belongs_to_many :recordings, :class_name => "JamRuby::Recording", :join_table => "bands_recordings" + # followers has_many :followers, :class_name => "JamRuby::BandFollower", :foreign_key => "band_id" has_many :inverse_followers, :through => :followers, :source => :user, :class_name => "JamRuby::Band", :foreign_key => "follower_id" @@ -37,7 +40,7 @@ module JamRuby def location # TODO: implement a single string version of location; # this will be indexed into elasticsearch and returned in search - return "Austin, TX" + return "#{self.city}, #{self.state}, #{self.country}" end # helper method for creating / updating a Band diff --git a/lib/jam_ruby/models/recording.rb b/lib/jam_ruby/models/recording.rb new file mode 100644 index 000000000..2f253a04d --- /dev/null +++ b/lib/jam_ruby/models/recording.rb @@ -0,0 +1,13 @@ +module JamRuby + class Recording < ActiveRecord::Base + + self.primary_key = 'id' + + # musicians + has_and_belongs_to_many :users, :class_name => "JamRuby::User", :join_table => "musicians_recordings" + + # bands + has_and_belongs_to_many :bands, :class_name => "JamRuby::Band", :join_table => "bands_recordings" + + end + end \ No newline at end of file diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 7a3c0c56a..263405615 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -24,6 +24,9 @@ module JamRuby has_many :band_musicians has_many :bands, :through => :band_musicians, :class_name => "JamRuby::Band" + # recordings + has_and_belongs_to_many :recordings, :class_name => "JamRuby::Recording", :join_table => "musicians_recordings" + # followers has_many :followers, :class_name => "JamRuby::UserFollower", :foreign_key => "user_id" has_many :inverse_followers, :through => :followers, :source => :user, :class_name => "JamRuby::User", :foreign_key => "follower_id" @@ -36,7 +39,9 @@ module JamRuby has_many :band_followings, :class_name => "JamRuby::BandFollower", :foreign_key => "follower_id" has_many :inverse_band_followings, :through => :band_followings, :class_name => "JamRuby::Band", :foreign_key => "band_id" - # favorites (needs Recording model) + # favorites + has_many :favorites, :class_name => "JamRuby::UserFavorite", :foreign_key => "recording_id" + has_many :inverse_favorites, :through => :favorites, :source => :user, :class_name => "JamRuby::User", :foreign_key => "user_id" # friends has_many :friendships, :class_name => "JamRuby::Friendship", :foreign_key => "user_id" @@ -52,7 +57,6 @@ module JamRuby has_many :received_invitations, :foreign_key => "receiver_id", :inverse_of => :receiver, :class_name => "JamRuby::Invitation" has_many :sent_invitations, :foreign_key => "sender_id", :inverse_of => :sender, :class_name => "JamRuby::Invitation" - # This causes the authenticate method to be generated (among other stuff) has_secure_password @@ -82,9 +86,7 @@ module JamRuby end def location - # TODO: implement a single string version of location; - # this will be indexed into elasticsearch and returned in search - return "Austin, TX" + return "#{self.city}, #{self.state}, #{self.country}" end def should_validate_password? diff --git a/lib/jam_ruby/models/user_favorite.rb b/lib/jam_ruby/models/user_favorite.rb new file mode 100644 index 000000000..5de3e8504 --- /dev/null +++ b/lib/jam_ruby/models/user_favorite.rb @@ -0,0 +1,10 @@ +module JamRuby + class UserFavorite < ActiveRecord::Base + + self.table_name = "users_favorites" + + self.primary_key = 'id' + + belongs_to :user, :class_name => "JamRuby::User", :foreign_key => "user_id", :inverse_of => :inverse_favorites + end +end \ No newline at end of file From 1a99565ea42049eae06bdb4898ad9981660dfe20 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Thu, 15 Nov 2012 20:50:03 -0600 Subject: [PATCH 080/274] * VRFS-92 stub out music session params --- lib/jam_ruby.rb | 1 + lib/jam_ruby/models/fan_invitation.rb | 32 ++++++++++++++++++++++++ lib/jam_ruby/models/music_session.rb | 16 +++++++++--- lib/jam_ruby/models/user.rb | 4 +++ spec/factories.rb | 8 ++++-- spec/jam_ruby/connection_manager_spec.rb | 2 +- 6 files changed, 57 insertions(+), 6 deletions(-) create mode 100644 lib/jam_ruby/models/fan_invitation.rb diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index 63c5d0c7c..71211ff5e 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -27,6 +27,7 @@ require "jam_ruby/models/connection" require "jam_ruby/models/friendship" require "jam_ruby/models/music_session" require "jam_ruby/models/invitation" +require "jam_ruby/models/fan_invitation" require "jam_ruby/models/friend_request" require "jam_ruby/models/instrument" require "jam_ruby/models/connection_track" diff --git a/lib/jam_ruby/models/fan_invitation.rb b/lib/jam_ruby/models/fan_invitation.rb new file mode 100644 index 000000000..026b16599 --- /dev/null +++ b/lib/jam_ruby/models/fan_invitation.rb @@ -0,0 +1,32 @@ +module JamRuby + class FanInvitation < ActiveRecord::Base + + FRIENDSHIP_REQUIRED_VALIDATION_ERROR = "You can only invite friends" + MEMBERSHIP_REQUIRED_OF_MUSIC_SESSION = "You must be a member of the music session to send invitations on behalf of it" + + self.primary_key = 'id' + belongs_to :sender, :inverse_of => :sent_fan_invitations, :class_name => "JamRuby::User", :foreign_key => "sender_id" + belongs_to :receiver, :inverse_of => :received_fan_invitations, :class_name => "JamRuby::User", :foreign_key => "receiver_id" + belongs_to :music_session, :inverse_of => :fan_invitations, :class_name => "JamRuby::MusicSession" + + validates :sender, :presence => true + validates :receiver, :presence => true + validates :music_session, :presence => true + + validate :require_sender_in_music_session, :require_are_friends + + private + + def require_sender_in_music_session + unless music_session.users.exists? sender + errors.add(:music_session, MEMBERSHIP_REQUIRED_OF_MUSIC_SESSION) + end + end + + def require_are_friends + unless receiver.friends.exists? sender + errors.add(:receiver, FRIENDSHIP_REQUIRED_VALIDATION_ERROR) + end + end + end +end diff --git a/lib/jam_ruby/models/music_session.rb b/lib/jam_ruby/models/music_session.rb index e5f2ba72d..1fa5652f0 100644 --- a/lib/jam_ruby/models/music_session.rb +++ b/lib/jam_ruby/models/music_session.rb @@ -3,16 +3,26 @@ module JamRuby self.primary_key = 'id' + attr_accessible :creator, :musician_access, :approval_required, :fan_chat, :fan_access + belongs_to :creator, :inverse_of => :music_sessions, :class_name => "JamRuby::User", :foreign_key => "user_id" has_many :connections, :class_name => "JamRuby::Connection" has_many :users, :through => :connections, :class_name => "JamRuby::User" has_and_belongs_to_many :genres, :class_name => "::JamRuby::Genre", :join_table => "genres_music_sessions" has_many :invitations, :foreign_key => "music_session_id", :inverse_of => :music_session, :class_name => "JamRuby::Invitation" - has_many :invited_users, :through => :invitations, :class_name => "JamRuby::User", :foreign_key => "receiver_id", :source => :receiver + has_many :invited_musicians, :through => :invitations, :class_name => "JamRuby::User", :foreign_key => "receiver_id", :source => :receiver + + has_many :fan_invitations, :foreign_key => "music_session_id", :inverse_of => :music_session, :class_name => "JamRuby::FanInvitation" + has_many :invited_fans, :through => :fan_invitations, :class_name => "JamRuby::User", :foreign_key => "receiver_id", :source => :receiver after_save :require_at_least_one_genre, :limit_to_three_genres + validates :fan_chat, :inclusion => {:in => [true, false]} + validates :fan_access, :inclusion => {:in => [true, false]} + validates :approval_required, :inclusion => {:in => [true, false]} + validates :musician_access, :inclusion => {:in => [true, false]} + def self.index(current_user) return MusicSession.includes(:invitations, :connections => [:user => [:friendships]]).order("(invitations.id is NULL) ASC, (friendships.friend_id != '#{current_user.id}' OR friendships.friend_id is NULL) ASC, music_sessions.created_at DESC").where("invitations.receiver_id = '#{current_user.id}' OR (friendships.friend_id = '#{current_user.id}') OR musician_access = true") end @@ -23,7 +33,7 @@ module JamRuby return true else # the creator can always join, and the invited users can join - return self.creator == user || self.invited_users.exists?(user) + return self.creator == user || self.invited_musicians.exists?(user) end end @@ -33,7 +43,7 @@ module JamRuby return true else # the creator can always see, and the invited users can see it too - return self.creator == user || self.invited_users.exists?(user) + return self.creator == user || self.invited_musicians.exists?(user) end end diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index b4cecfe78..ab9e7454b 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -52,6 +52,10 @@ module JamRuby has_many :received_invitations, :foreign_key => "receiver_id", :inverse_of => :receiver, :class_name => "JamRuby::Invitation" has_many :sent_invitations, :foreign_key => "sender_id", :inverse_of => :sender, :class_name => "JamRuby::Invitation" + # fan_invitations + has_many :received_fan_invitations, :foreign_key => "receiver_id", :inverse_of => :receiver, :class_name => "JamRuby::FanInvitation" + has_many :sent_fan_invitations, :foreign_key => "sender_id", :inverse_of => :sender, :class_name => "JamRuby::FanInvitation" + # This causes the authenticate method to be generated (among other stuff) has_secure_password diff --git a/spec/factories.rb b/spec/factories.rb index bc82f9e43..7f57ac65b 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -6,14 +6,18 @@ FactoryGirl.define do password_confirmation "foobar" email_confirmed true + factory :admin do admin true end end - factory :music_session, :class => JamRuby::MusicSession do + factory :music_session, :class => "JamRuby::MusicSession" do sequence(:description) { |n| "Music Session #{n}" } - musician_access true + fan_chat true + fan_access true + approval_required false + musician_access true end factory :connection, :class => JamRuby::Connection do diff --git a/spec/jam_ruby/connection_manager_spec.rb b/spec/jam_ruby/connection_manager_spec.rb index fd660fa3f..0c26c56e2 100644 --- a/spec/jam_ruby/connection_manager_spec.rb +++ b/spec/jam_ruby/connection_manager_spec.rb @@ -18,7 +18,7 @@ describe ConnectionManager do def create_music_session(user_id) description = "some session" - @conn.exec("INSERT INTO music_sessions (user_id, description, musician_access) VALUES ($1, $2, $3) RETURNING id", [user_id, description, true]) do |result| + @conn.exec("INSERT INTO music_sessions (user_id, description, musician_access, approval_required, fan_chat, fan_access) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id", [user_id, description, true, false, true, true]) do |result| return result.getvalue(0, 0) end end From 7b32358afa88120cec133e4c96fae9c3c256713f Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sat, 17 Nov 2012 20:59:59 -0600 Subject: [PATCH 081/274] * VRFS-100; as_musician almost done --- lib/jam_ruby/connection_manager.rb | 18 ++++++-- lib/jam_ruby/errors/jam_argument_error.rb | 2 +- lib/jam_ruby/models/music_session.rb | 1 + lib/jam_ruby/models/user.rb | 4 ++ spec/jam_ruby/connection_manager_spec.rb | 55 +++++++++++++++++++---- 5 files changed, 67 insertions(+), 13 deletions(-) diff --git a/lib/jam_ruby/connection_manager.rb b/lib/jam_ruby/connection_manager.rb index 0ba6111e3..4603d6d72 100644 --- a/lib/jam_ruby/connection_manager.rb +++ b/lib/jam_ruby/connection_manager.rb @@ -157,23 +157,33 @@ module JamRuby end end - def join_music_session(user_id, client_id, music_session_id) + def join_music_session(user_id, client_id, music_session_id, as_musician) conn = @pg_conn lock_connections(conn) previous_music_session_id = check_already_session(conn, client_id) + user = User.find(user_id) + + if as_musician != true && as_musician != false # checks that a boolean was passed in + raise JamArgumentError, "as_musician incorrectly specified" + end + + if as_musician && !user.musician + raise PermissionError, "a fan can not join a music session as a musician" + end + # determine if the user can join; if not, throw a PermissionError music_session = MusicSession.find(music_session_id) - unless music_session.can_join?(User.find(user_id)) + unless music_session.can_join?(user, as_musician) @log.debug "user can not join a session user_id=#{user_id} and client_id=#{client_id}" raise PermissionError, "unable to join the specified session" end begin # we include user_id in the query as an act o security, so that a user can't access someone else' client connection - conn.exec("UPDATE connections SET music_session_id = $1 WHERE client_id = $2 and user_id = $3", [music_session_id, client_id, user_id]) do |result| + conn.exec("UPDATE connections SET music_session_id = $1, as_musician = $2 WHERE client_id = $3 and user_id = $4", [music_session_id, as_musician, client_id, user_id]) do |result| if result.cmd_tuples == 1 @log.debug "associated music_session with connection for client=#{client_id}, music_session=#{music_session_id}, and user=#{user_id}" @@ -215,7 +225,7 @@ module JamRuby end # can throw exception if the session is deleted just before this - conn.exec("UPDATE connections SET music_session_id = NULL WHERE client_id = $1 AND user_id =$2", [client_id, user_id]) do |result| + conn.exec("UPDATE connections SET music_session_id = NULL, as_musician = NULL WHERE client_id = $1 AND user_id =$2", [client_id, user_id]) do |result| if result.cmd_tuples == 1 @log.debug("deassociated music_session with connection for client_id=#{client_id}, user_id=#{user_id}") diff --git a/lib/jam_ruby/errors/jam_argument_error.rb b/lib/jam_ruby/errors/jam_argument_error.rb index 9f208363b..8d9edb7df 100644 --- a/lib/jam_ruby/errors/jam_argument_error.rb +++ b/lib/jam_ruby/errors/jam_argument_error.rb @@ -1,6 +1,6 @@ module JamRuby # if a bad argument is supplied. - # Why not use the default one? This allows us to know our API layer threw this, versus us using some core library incorrectly + # Why not use the default ruby argument error? Using this one allows us to know our API layer threw this, versus us using some core library incorrectly class JamArgumentError < ArgumentError end diff --git a/lib/jam_ruby/models/music_session.rb b/lib/jam_ruby/models/music_session.rb index 1fa5652f0..e8fd7d2bf 100644 --- a/lib/jam_ruby/models/music_session.rb +++ b/lib/jam_ruby/models/music_session.rb @@ -22,6 +22,7 @@ module JamRuby validates :fan_access, :inclusion => {:in => [true, false]} validates :approval_required, :inclusion => {:in => [true, false]} validates :musician_access, :inclusion => {:in => [true, false]} + validates :creator, :presence => true, :if => "creator.musician?" def self.index(current_user) return MusicSession.includes(:invitations, :connections => [:user => [:friendships]]).order("(invitations.id is NULL) ASC, (friendships.friend_id != '#{current_user.id}' OR friendships.friend_id is NULL) ASC, music_sessions.created_at DESC").where("invitations.receiver_id = '#{current_user.id}' OR (friendships.friend_id = '#{current_user.id}') OR musician_access = true") diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index b87ce35e8..5b9c65962 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -88,6 +88,10 @@ module JamRuby @photo_url = "http://www.jamkazam.com/images/users/photos/#{self.id}.gif"; end + def name + return "#{first_name} #{last_name}" + end + def location # TODO: implement a single string version of location; # this will be indexed into elasticsearch and returned in search diff --git a/spec/jam_ruby/connection_manager_spec.rb b/spec/jam_ruby/connection_manager_spec.rb index 4ade9d6e8..ba71b0b5f 100644 --- a/spec/jam_ruby/connection_manager_spec.rb +++ b/spec/jam_ruby/connection_manager_spec.rb @@ -10,8 +10,8 @@ describe ConnectionManager do @message_factory = MessageFactory.new end - def create_user(first_name, last_name, email) - @conn.exec("INSERT INTO users (first_name, last_name, email, password_digest) VALUES ($1, $2, $3, $4) RETURNING id", [first_name, last_name, email, '1']) do |result| + def create_user(first_name, last_name, email, options = {:musician => true}) + @conn.exec("INSERT INTO users (first_name, last_name, email, musician, password_digest) VALUES ($1, $2, $3, $4, $5) RETURNING id", [first_name, last_name, email, options[:musician], '1']) do |result| return result.getvalue(0, 0) end end @@ -223,7 +223,7 @@ describe ConnectionManager do @conn.transaction do @connman.create_connection(user_id, client_id, "1.1.1.1") - @connman.join_music_session(user_id, client_id, music_session_id) + @connman.join_music_session(user_id, client_id, music_session_id, true) end assert_session_exists(music_session_id, true) @@ -247,7 +247,46 @@ describe ConnectionManager do music_session_id = create_music_session(user_id) @conn.transaction do - expect { @connman.join_music_session(user_id, client_id, music_session_id) }.to raise_error(ActiveRecord::RecordNotFound) + expect { @connman.join_music_session(user_id, client_id, music_session_id, true) }.to raise_error(ActiveRecord::RecordNotFound) + end + + end + + it "join_music_session fails if user is a fan but wants to join as a musician" do + + client_id = "client_id10.1" + user_id = create_user("test", "user10.1", "user10.1@jamkazam.com", :musician => false) + + music_session_id = create_music_session(user_id) + + @conn.transaction do + expect { @connman.join_music_session(user_id, client_id, music_session_id, true) }.to raise_error(JamRuby::PermissionError) + end + + end + + it "join_music_session fails if invalid value for as_musician specified" do + + client_id = "client_id10.2" + user_id = create_user("test", "user10.2", "user10.2@jamkazam.com", :musician => false) + + music_session_id = create_music_session(user_id) + + @conn.transaction do + expect { @connman.join_music_session(user_id, client_id, music_session_id, 'blarg') }.to raise_error(JamRuby::JamArgumentError) + end + + end + + it "join_music_session fails if fan_access=true and the user is a fan" do + + client_id = "client_id10.2" + user_id = create_user("test", "user10.2", "user10.2@jamkazam.com", :musician => false) + + music_session_id = create_music_session(user_id, :fan_access => false) + + @conn.transaction do + expect { @connman.join_music_session(user_id, client_id, music_session_id, 'blarg') }.to raise_error(JamRuby::PermissionError) end end @@ -262,7 +301,7 @@ describe ConnectionManager do @conn.transaction do @connman.create_connection(user_id, client_id, "1.1.1.1") # specify real user id, but not associated with this session - expect { @connman.join_music_session(user_id2, client_id, music_session_id) } .to raise_error(ActiveRecord::RecordNotFound) + expect { @connman.join_music_session(user_id2, client_id, music_session_id, true) } .to raise_error(ActiveRecord::RecordNotFound) end end @@ -276,7 +315,7 @@ describe ConnectionManager do end @conn.transaction do - expect { @connman.join_music_session(user_id, client_id, "some_bogus_music_session_id") }.to raise_error(ActiveRecord::RecordNotFound) + expect { @connman.join_music_session(user_id, client_id, "some_bogus_music_session_id", true) }.to raise_error(ActiveRecord::RecordNotFound) end end @@ -306,7 +345,7 @@ describe ConnectionManager do end @conn.transaction do - @connman.join_music_session(user_id, client_id, music_session_id) + @connman.join_music_session(user_id, client_id, music_session_id, true) end @conn.transaction do @@ -326,7 +365,7 @@ describe ConnectionManager do end @conn.transaction do - @connman.join_music_session(user_id, client_id, music_session_id) + @connman.join_music_session(user_id, client_id, music_session_id, true) end assert_session_exists(music_session_id, true) From 27266ca90a053d145b1597551f8faecd37e65f4b Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sat, 17 Nov 2012 21:41:12 -0600 Subject: [PATCH 082/274] * VRFS-100 completed --- lib/jam_ruby/models/music_session.rb | 17 ++++++++++++----- spec/jam_ruby/connection_manager_spec.rb | 14 ++++++++------ spec/jam_ruby/models/music_session_spec.rb | 18 +++++++++--------- 3 files changed, 29 insertions(+), 20 deletions(-) diff --git a/lib/jam_ruby/models/music_session.rb b/lib/jam_ruby/models/music_session.rb index e8fd7d2bf..d6813cbfa 100644 --- a/lib/jam_ruby/models/music_session.rb +++ b/lib/jam_ruby/models/music_session.rb @@ -29,13 +29,20 @@ module JamRuby end # Verifies that the specified user can join this music session - def can_join? user - if self.musician_access - return true + def can_join? user, as_musician + + if as_musician + if self.musician_access + return true + else + # the creator can always join, and the invited users can join + return self.creator == user || self.invited_musicians.exists?(user) + end else - # the creator can always join, and the invited users can join - return self.creator == user || self.invited_musicians.exists?(user) + # it's a fan, and the only way a fan can join is if fan_access is true + return self.fan_access end + end # Verifies that the specified user can see this music session diff --git a/spec/jam_ruby/connection_manager_spec.rb b/spec/jam_ruby/connection_manager_spec.rb index ba71b0b5f..ef9ee8ac4 100644 --- a/spec/jam_ruby/connection_manager_spec.rb +++ b/spec/jam_ruby/connection_manager_spec.rb @@ -16,9 +16,11 @@ describe ConnectionManager do end end - def create_music_session(user_id) + def create_music_session(user_id, options={}) + default_options = {:musician_access => true, :fan_chat => true, :fan_access => true, :approval_required=> false} + options = default_options.merge(options) description = "some session" - @conn.exec("INSERT INTO music_sessions (user_id, description, musician_access, approval_required, fan_chat, fan_access) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id", [user_id, description, true, false, true, true]) do |result| + @conn.exec("INSERT INTO music_sessions (user_id, description, musician_access, approval_required, fan_chat, fan_access) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id", [user_id, description, options[:musician_access], options[:approval_required], options[:fan_chat], options[:fan_access]]) do |result| return result.getvalue(0, 0) end end @@ -278,15 +280,15 @@ describe ConnectionManager do end - it "join_music_session fails if fan_access=true and the user is a fan" do + it "join_music_session fails if fan_access=false and the user is a fan" do - client_id = "client_id10.2" - user_id = create_user("test", "user10.2", "user10.2@jamkazam.com", :musician => false) + client_id = "client_id10.3" + user_id = create_user("test", "user10.3", "user10.3@jamkazam.com", :musician => false) music_session_id = create_music_session(user_id, :fan_access => false) @conn.transaction do - expect { @connman.join_music_session(user_id, client_id, music_session_id, 'blarg') }.to raise_error(JamRuby::PermissionError) + expect { @connman.join_music_session(user_id, client_id, music_session_id, false) }.to raise_error(JamRuby::PermissionError) end end diff --git a/spec/jam_ruby/models/music_session_spec.rb b/spec/jam_ruby/models/music_session_spec.rb index e0195e16e..ae1450f90 100644 --- a/spec/jam_ruby/models/music_session_spec.rb +++ b/spec/jam_ruby/models/music_session_spec.rb @@ -29,9 +29,9 @@ describe MusicSession do music_session = FactoryGirl.create(:music_session, :creator => user1, :musician_access => true) - music_session.can_join?(user1).should == true - music_session.can_join?(user2).should == true - music_session.can_join?(user3).should == true + music_session.can_join?(user1, true).should == true + music_session.can_join?(user2, true).should == true + music_session.can_join?(user3, true).should == true end it 'no one but invited people can join closed music session' do @@ -42,18 +42,18 @@ describe MusicSession do music_session = FactoryGirl.create(:music_session, :creator => user1, :musician_access => false) FactoryGirl.create(:connection, :user => user1, :music_session => music_session) - music_session.can_join?(user1).should == true - music_session.can_join?(user2).should == false - music_session.can_join?(user3).should == false + music_session.can_join?(user1, true).should == true + music_session.can_join?(user2, true).should == false + music_session.can_join?(user3, true).should == false # invite user 2 FactoryGirl.create(:friendship, :user => user1, :friend => user2) FactoryGirl.create(:friendship, :user => user2, :friend => user1) FactoryGirl.create(:invitation, :sender => user1, :receiver => user2, :music_session => music_session) - music_session.can_join?(user1).should == true - music_session.can_join?(user2).should == true - music_session.can_join?(user3).should == false + music_session.can_join?(user1, true).should == true + music_session.can_join?(user2, true).should == true + music_session.can_join?(user3, true).should == false end it 'no one but invited people can see closed music session' do From c07e3d2809026f37d42fe0a5199b4d23a86ce4f3 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 18 Nov 2012 01:39:07 -0600 Subject: [PATCH 083/274] * build script for jam-ruby completed --- Gemfile | 13 +++++++++++-- build | 14 ++++++++++++++ jenkins | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 2 deletions(-) create mode 100755 build create mode 100755 jenkins diff --git a/Gemfile b/Gemfile index 2bc47e6ce..3217a1a97 100644 --- a/Gemfile +++ b/Gemfile @@ -1,12 +1,14 @@ +#ruby=1.9.3 source 'https://rubygems.org' +source 'https://jamjam:blueberryjam@www.jamkazam.com/gems/' # Look for $WORKSPACE, otherwise use "workspace" as dev path. workspace = ENV["WORKSPACE"] || "~/workspace" +devenv = ENV["BUILD_NUMBER"].nil? # Jenkins sets a build number environment variable gem 'pg', '0.14.0', :platform => [:mri, :mswin, :mingw] gem 'jdbc_postgres', :platform => [:jruby] -gem 'jampb', :path => "#{workspace}/jam-pb/target/ruby/jampb" gem 'activerecord', '3.2.7' gem 'uuidtools', '2.1.2' gem 'bcrypt-ruby', '3.0.1' @@ -18,8 +20,15 @@ gem 'will_paginate' gem 'actionmailer' gem 'sendgrid' -group :test do +if devenv gem 'jam_db', :path=> "#{workspace}/jam-db/target/ruby_package" + gem 'jampb', :path => "#{workspace}/jam-pb/target/ruby/jampb" +else + gem 'jam_db' + gem 'jampb' +end + +group :test do gem "factory_girl" gem "rspec", "2.10.0" gem 'spork', '0.9.0' diff --git a/build b/build new file mode 100755 index 000000000..86f643cd2 --- /dev/null +++ b/build @@ -0,0 +1,14 @@ +#!/bin/bash + +echo "running rspec tests" +bundle exec rspec + +if [ "$?" = "0" ]; then + echo "tests completed" +else + echo "tests failed." + exit 1 +fi + +echo "build complete" + diff --git a/jenkins b/jenkins new file mode 100755 index 000000000..6fd057f23 --- /dev/null +++ b/jenkins @@ -0,0 +1,40 @@ +#!/bin/bash + +GEM_SERVER=http://localhost:9000/gems + +echo "starting build..." +./build + +if [ "$?" = "0" ]; then + echo "build succeeded" + + # generate gem version based on jenkins build number + if [ -z $BUILD_NUMBER ]; then + BUILD_NUMBER="1" + fi + VERSION="0.0.${BUILD_NUMBER}" + echo "packaging gem jam_ruby-$VERSION" + cat > lib/jam_ruby/version.rb << EOF +module JamRuby + VERSION = "$VERSION" +end +EOF + + gem build jam_ruby.gemspec + + GEMNAME="jam_ruby-${VERSION}.gem" + + echo "publishing gem" + curl -f -T $GEMNAME $GEM_SERVER/$GEMNAME \; + + if [ "$?" != "0" ]; then + echo "publish failed" + exit 1 + fi + echo "done publishing gems" +else + echo "build failed" + exit 1 +fi + + From 5c75c45de7e045d723c844c9575deee2da7497a2 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 18 Nov 2012 01:41:37 -0600 Subject: [PATCH 084/274] * issue bundle update before running tests --- build | 1 + 1 file changed, 1 insertion(+) diff --git a/build b/build index 86f643cd2..34bbcfe5b 100755 --- a/build +++ b/build @@ -1,6 +1,7 @@ #!/bin/bash echo "running rspec tests" +bundle update bundle exec rspec if [ "$?" = "0" ]; then From 04d992a2b63ffce00b333a33157d3280b20ed90c Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 18 Nov 2012 01:48:02 -0600 Subject: [PATCH 085/274] * cleaning up curl command --- jenkins | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jenkins b/jenkins index 6fd057f23..04afb0992 100755 --- a/jenkins +++ b/jenkins @@ -25,7 +25,7 @@ EOF GEMNAME="jam_ruby-${VERSION}.gem" echo "publishing gem" - curl -f -T $GEMNAME $GEM_SERVER/$GEMNAME \; + curl -f -T $GEMNAME $GEM_SERVER/$GEMNAME if [ "$?" != "0" ]; then echo "publish failed" From 773046ea42fdffed41fc51b3c9c1f754fdf23232 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 18 Nov 2012 02:11:15 -0600 Subject: [PATCH 086/274] * cleaning up of echos --- build | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build b/build index 34bbcfe5b..ec1b1fa31 100755 --- a/build +++ b/build @@ -1,7 +1,8 @@ #!/bin/bash -echo "running rspec tests" +echo "updating dependencies" bundle update +echo "running rspec tests" bundle exec rspec if [ "$?" = "0" ]; then From d73bbb822c3c08b751c811aebe8a186a915171f2 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Wed, 21 Nov 2012 14:48:39 -0500 Subject: [PATCH 087/274] VRFS-81 - recording/favorite development --- lib/jam_ruby.rb | 2 + lib/jam_ruby/models/band.rb | 38 +-- lib/jam_ruby/models/band_recording.rb | 12 + lib/jam_ruby/models/instrument.rb | 2 +- lib/jam_ruby/models/musician_instrument.rb | 4 +- lib/jam_ruby/models/musician_recording.rb | 12 + lib/jam_ruby/models/recording.rb | 43 +++- lib/jam_ruby/models/user.rb | 275 ++++++++++++--------- lib/jam_ruby/models/user_favorite.rb | 1 + spec/factories.rb | 14 +- spec/jam_ruby/connection_manager_spec.rb | 2 +- spec/jam_ruby/models/band_search_spec.rb | 6 +- spec/jam_ruby/models/search_spec.rb | 6 +- spec/jam_ruby/models/tire_search_spec.rb | 15 +- spec/jam_ruby/models/user_search_spec.rb | 11 +- spec/jam_ruby/models/user_spec.rb | 2 +- 16 files changed, 275 insertions(+), 170 deletions(-) create mode 100644 lib/jam_ruby/models/band_recording.rb create mode 100644 lib/jam_ruby/models/musician_recording.rb diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index a71ebbf97..fc6265480 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -40,6 +40,8 @@ require "jam_ruby/models/user_favorite" require "jam_ruby/models/band_follower" require "jam_ruby/models/search" require "jam_ruby/models/recording" +require "jam_ruby/models/musician_recording" +require "jam_ruby/models/band_recording" include Jampb diff --git a/lib/jam_ruby/models/band.rb b/lib/jam_ruby/models/band.rb index 6103f502a..b4856c363 100644 --- a/lib/jam_ruby/models/band.rb +++ b/lib/jam_ruby/models/band.rb @@ -3,7 +3,7 @@ module JamRuby include Tire::Model::Search include Tire::Model::Callbacks - attr_accessible :name, :website, :biography + attr_accessible :name, :website, :biography, :city, :state, :country self.primary_key = 'id' @@ -15,7 +15,8 @@ module JamRuby has_and_belongs_to_many :genres, :class_name => "JamRuby::Genre", :join_table => "bands_genres" # recordings - has_and_belongs_to_many :recordings, :class_name => "JamRuby::Recording", :join_table => "bands_recordings" + has_many :band_recordings, :class_name => "JamRuby::BandRecording", :foreign_key => "band_id" + has_many :recordings, :through => :band_recordings, :class_name => "JamRuby::Recording" # followers has_many :followers, :class_name => "JamRuby::BandFollower", :foreign_key => "band_id" @@ -36,7 +37,7 @@ module JamRuby def follower_count return self.followers.size end - + def location # TODO: implement a single string version of location; # this will be indexed into elasticsearch and returned in search @@ -44,45 +45,44 @@ module JamRuby end # helper method for creating / updating a Band - def self.save(params) - if params[:id].nil? + def self.save(id, name, website, biography, city, state, country, genres) + if id.nil? band = Band.new() else - band = Band.find(params[:id]) + band = Band.find(id) end # name - unless params[:name].nil? - band.name = params[:name] + unless name.nil? + band.name = name end # website - unless params[:website].nil? - band.website = params[:website] + unless website.nil? + band.website = website end # biography - unless params[:biography].nil? - band.biography = params[:biography] + unless biography.nil? + band.biography = biography end # city - unless params[:city].nil? - band.city = params[:city] + unless city.nil? + band.city = city end # state - unless params[:state].nil? - band.state = params[:state] + unless state.nil? + band.state = state end # country - unless params[:country].nil? - band.country = params[:country] + unless country.nil? + band.country = country end # genres - genres = params[:genres] unless genres.nil? ActiveRecord::Base.transaction do # delete all genres for this band first diff --git a/lib/jam_ruby/models/band_recording.rb b/lib/jam_ruby/models/band_recording.rb new file mode 100644 index 000000000..2076f2283 --- /dev/null +++ b/lib/jam_ruby/models/band_recording.rb @@ -0,0 +1,12 @@ +module JamRuby + class BandRecording < ActiveRecord::Base + + self.table_name = "bands_recordings" + + # bands + has_many :bands, :through => :band_recordings, :class_name => "JamRuby::Band" + + belongs_to :band, :class_name => "JamRuby::Band", :foreign_key => "band_id" + belongs_to :recording, :class_name => "JamRuby::Recording", :foreign_key => "recording_id" + end +end \ No newline at end of file diff --git a/lib/jam_ruby/models/instrument.rb b/lib/jam_ruby/models/instrument.rb index 4af0dd1ae..e0563c454 100644 --- a/lib/jam_ruby/models/instrument.rb +++ b/lib/jam_ruby/models/instrument.rb @@ -4,7 +4,7 @@ module JamRuby self.primary_key = 'id' # users - has_many :musician_instruments + has_many :musician_instruments, :class_name => "JamRuby::MusicianInstrument" has_many :users, :through => :musician_instruments, :class_name => "JamRuby::User" has_many :connection_tracks, :class_name => "JamRuby::ConnectionTrack", :inverse_of => :instrument diff --git a/lib/jam_ruby/models/musician_instrument.rb b/lib/jam_ruby/models/musician_instrument.rb index 2fd11e64e..4ce963dcf 100644 --- a/lib/jam_ruby/models/musician_instrument.rb +++ b/lib/jam_ruby/models/musician_instrument.rb @@ -5,8 +5,8 @@ module JamRuby self.primary_key = 'id' - belongs_to :user - belongs_to :instrument + belongs_to :user, :class_name => "JamRuby::User" + belongs_to :instrument, :class_name => "JamRuby::Instrument" def description @description = self.instrument.description diff --git a/lib/jam_ruby/models/musician_recording.rb b/lib/jam_ruby/models/musician_recording.rb new file mode 100644 index 000000000..74a827a41 --- /dev/null +++ b/lib/jam_ruby/models/musician_recording.rb @@ -0,0 +1,12 @@ +module JamRuby + class MusicianRecording < ActiveRecord::Base + + self.table_name = "musicians_recordings" + + # musicians + has_many :musicians, :through => :musician_recordings, :class_name => "JamRuby::User" + + belongs_to :user, :class_name => "JamRuby::User", :foreign_key => "user_id" + belongs_to :recording, :class_name => "JamRuby::Recording", :foreign_key => "recording_id" + end +end \ No newline at end of file diff --git a/lib/jam_ruby/models/recording.rb b/lib/jam_ruby/models/recording.rb index 2f253a04d..8006221c4 100644 --- a/lib/jam_ruby/models/recording.rb +++ b/lib/jam_ruby/models/recording.rb @@ -3,11 +3,46 @@ module JamRuby self.primary_key = 'id' - # musicians - has_and_belongs_to_many :users, :class_name => "JamRuby::User", :join_table => "musicians_recordings" + has_many :musician_recordings - # bands - has_and_belongs_to_many :bands, :class_name => "JamRuby::Band", :join_table => "bands_recordings" + # favorites + has_and_belongs_to_many :user_favorites, :class_name => "JamRuby::UserFavorite", :join_table => "users_favorites" + validates :description, presence: true, length: {maximum: 200} + + def self.save(id, is_public, description, owner_id, is_band) + if id.nil? + recording = Recording.new() + else + recording = Recording.find(id) + end + + # public flag + unless is_public.nil? + recording.public = is_public + end + + # description + unless description.nil? + recording.description = description + end + + if is_band? + recording.band << Band.create(id: owner_id) + else + recording.user << User.create(id: owner_id) + end + + recording.updated_at = Time.now.getutc + recording.save + return recording + end + + def self.delete(id, owner_id, is_band) + if is_band? + JamRuby::UserFollower.delete_all "(user_id = '#{user_id}' AND follower_id = '#{follower_id}')" + else + end + end end end \ No newline at end of file diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index aac63a509..9aad75372 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -9,9 +9,6 @@ module JamRuby # authorizations (for facebook, etc -- omniauth) has_many :user_authorizations, :class_name => "JamRuby::UserAuthorization" - - # account - belongs_to :account, :class_name => "JamRuby::Account" # connections (websocket-gateway) has_many :connections, :class_name => "JamRuby::Connection" @@ -20,15 +17,16 @@ module JamRuby has_many :friend_requests, :class_name => "JamRuby::FriendRequest" # instruments - has_many :musician_instruments + has_many :musician_instruments, :class_name => "JamRuby::MusicianInstrument" has_many :instruments, :through => :musician_instruments, :class_name => "JamRuby::Instrument" # bands - has_many :band_musicians + has_many :band_musicians, :class_name => "JamRuby::BandMusician" has_many :bands, :through => :band_musicians, :class_name => "JamRuby::Band" # recordings - has_and_belongs_to_many :recordings, :class_name => "JamRuby::Recording", :join_table => "musicians_recordings" + has_many :musician_recordings, :class_name => "JamRuby::MusicianRecording", :foreign_key => "user_id" + has_many :recordings, :through => :musician_recordings, :class_name => "JamRuby::Recording" # followers has_many :followers, :class_name => "JamRuby::UserFollower", :foreign_key => "user_id" @@ -43,8 +41,8 @@ module JamRuby has_many :inverse_band_followings, :through => :band_followings, :class_name => "JamRuby::Band", :foreign_key => "band_id" # favorites - has_many :favorites, :class_name => "JamRuby::UserFavorite", :foreign_key => "recording_id" - has_many :inverse_favorites, :through => :favorites, :source => :user, :class_name => "JamRuby::User", :foreign_key => "user_id" + has_many :favorites, :class_name => "JamRuby::UserFavorite", :foreign_key => "user_id" + has_many :inverse_favorites, :through => :favorites, :source => :user, :class_name => "JamRuby::User" # friends has_many :friendships, :class_name => "JamRuby::Friendship", :foreign_key => "user_id" @@ -120,6 +118,10 @@ module JamRuby return self.followings.size + self.band_followings.size end + def favorite_count + return self.favorites.size + end + def to_s return email unless email.nil? @@ -131,88 +133,84 @@ module JamRuby end # helper method for creating / updating a User - def self.save(params) - if params[:id].nil? + def self.save(id, first_name, last_name, email, password, password_confirmation, musician, gender, + birth_date, internet_service_provider, city, state, country, instruments) + if id.nil? user = User.new() else - user = User.find(params[:id]) - end - - # account id - unless params[:account_id].nil? - user.account_id = params[:account_id] + user = User.find(id) end # first name - unless params[:first_name].nil? - user.first_name = params[:first_name] + unless first_name.nil? + user.first_name = first_name end # last name - unless params[:last_name].nil? - user.last_name = params[:last_name] + unless last_name.nil? + user.last_name = last_name end # email - unless params[:email].nil? - user.email = params[:email] + unless email.nil? + user.email = email end # password - unless params[:password].nil? - user.password = params[:password] + unless password.nil? + user.password = password end # password confirmation - unless params[:password_confirmation].nil? - user.password_confirmation = params[:password_confirmation] + unless password_confirmation.nil? + user.password_confirmation = password_confirmation end # musician flag - unless params[:musician].nil? - user.musician = params[:musician] + unless musician.nil? + user.musician = musician end # gender - unless params[:gender].nil? - account.gender = params[:gender] + unless gender.nil? + account.gender = gender end # birthdate - unless params[:birth_date].nil? - account.birth_date = params[:birth_date] + unless birth_date.nil? + account.birth_date = birth_date end # ISP - unless params[:internet_service_provider].nil? - account.internet_service_provider = params[:internet_service_provider] + unless internet_service_provider.nil? + account.internet_service_provider = internet_service_provider end # city - unless params[:city].nil? - user.city = params[:city] + unless city.nil? + user.city = city end # state - unless params[:state].nil? - user.state = params[:state] + unless state.nil? + user.state = state end # country - unless params[:country].nil? - user.country = params[:country] + unless country.nil? + user.country = country end # instruments - unless params[:instruments].nil? - ActiveRecord::Base.transaction do + unless instruments.nil? + UserManager.active_record_transaction do |user_manager| # delete all instruments for this user first unless user.id.nil? || user.id.length == 0 MusicianInstrument.delete_all(["user_id = ?", user.id]) end # loop through each instrument in the array and save to the db - params[:instruments].each do |instrument| + instruments.each do |instrument| mu = MusicianInstrument.new() mu.user_id = user.id mu.instrument_id = instrument[:id] @@ -228,102 +226,133 @@ module JamRuby return user end + def self.create_user_following(user_id, follower_id) + @follower = UserFollower.new() + @follower.user_id = user_id + @follower.follower_id = follower_id + @follower.save + end + + def self.delete_user_following(user_id, follower_id) + JamRuby::UserFollower.delete_all "(user_id = '#{user_id}' AND follower_id = '#{follower_id}')" + end + + def self.create_band_following(band_id, follower_id) + @follower = BandFollower.new() + @follower.band_id = band_id + @follower.follower_id = follower_id + @follower.save + end + + def self.delete_band_following(band_id, follower_id) + JamRuby::BandFollower.delete_all "(band_id = '#{band_id}' AND follower_id = '#{follower_id}')" + end + + def self.create_favorite(user_id, recording_id) + @favorite = UserFavorite.new() + @favorite.user_id = user_id + @favorite.recording_id = recording_id + @favorite.save + end + + def self.delete_favorite(user_id, recording_id) + JamRuby::UserFavorite.delete_all "(user_id = '#{user_id}' AND recording_id = '#{recording_id}')" + end + def limit_to_five_instruments if instruments.count > 5 errors.add(:instruments, "No more than 5 instruments are allowed.") end end + # throws ActiveRecord::RecordNotFound if instrument is invalid + # throws an email delivery error if unable to connect out to SMTP + def self.signup(first_name, last_name, email, password, password_confirmation, + city, state, country, instruments, signup_confirm_url) + user = User.new - # throws ActiveRecord::RecordNotFound if instrument is invalid - # throws an email delivery error if unable to connect out to SMTP - def self.signup(first_name, last_name, email, password, password_confirmation, - city, state, country, instruments, signup_confirm_url) - user = User.new + UserManager.active_record_transaction do |user_manager| + user.first_name = first_name + user.last_name = last_name + user.email = email - UserManager.active_record_transaction do |user_manager| - user.first_name = first_name - user.last_name = last_name - user.email = email + #FIXME: Setting random password for social network logins. This + # is because we have validations all over the place on this. + # The right thing would be to have this null + if password.nil? + user.password = "blahblahblah" + user.password_confirmation = "blahblahblah" + else + user.password = password + user.password_confirmation = password_confirmation + end - #FIXME: Setting random password for social network logins. This - # is because we have validations all over the place on this. - # The right thing would be to have this null - if password.nil? - user.password = "blahblahblah" - user.password_confirmation = "blahblahblah" - else - user.password = password - user.password_confirmation = password_confirmation - end + user.admin = false + user.email_confirmed = false + user.city = city + user.state = state + user.country = country + unless instruments.nil? + instruments.each do |musician_instrument_param| + instrument = Instrument.find(musician_instrument_param[:instrument_id]) + musician_instrument = MusicianInstrument.new + musician_instrument.user = user + musician_instrument.instrument = instrument + musician_instrument.proficiency_level = musician_instrument_param[:proficiency_level] + musician_instrument.priority = musician_instrument_param[:priority] + musician_instrument.save + user.musician_instruments << musician_instrument + end + end + user.signup_token = SecureRandom.urlsafe_base64 - user.admin = false - user.email_confirmed = false - user.city = city - user.state = state - user.country = country - unless instruments.nil? - instruments.each do |musician_instrument_param| - instrument = Instrument.find(musician_instrument_param[:instrument_id]) - musician_instrument = MusicianInstrument.new - musician_instrument.user = user - musician_instrument.instrument = instrument - musician_instrument.proficiency_level = musician_instrument_param[:proficiency_level] - musician_instrument.priority = musician_instrument_param[:priority] - musician_instrument.save - user.musician_instruments << musician_instrument + user.save + + if user.errors.any? + raise ActiveRecord::Rollback + else + # FIXME: + # It's not standard to require a confirmation when a user signs up with Facebook. + # We should stop asking for it. + # + # any errors here should also rollback the transaction; that's OK. If emails aren't going to be delivered, + # it's already a really bad situation; make user signup again + UserMailer.welcome_message(user, signup_confirm_url.nil? ? nil : (signup_confirm_url + "/" + user.signup_token) ).deliver end end - user.signup_token = SecureRandom.urlsafe_base64 - user.save - - if user.errors.any? - raise ActiveRecord::Rollback - else - # FIXME: - # It's not standard to require a confirmation when a user signs up with Facebook. - # We should stop asking for it. - # - # any errors here should also rollback the transaction; that's OK. If emails aren't going to be delivered, - # it's already a really bad situation; make user signup again - UserMailer.welcome_message(user, signup_confirm_url.nil? ? nil : (signup_confirm_url + "/" + user.signup_token) ).deliver - end - end - - return user - end - - # throws RecordNotFound if signup token is invalid; i.e., if it's nil, empty string, or not belonging to a user - def self.signup_confirm(signup_token) - if signup_token.nil? || signup_token.empty? - # there are plenty of confirmed users with nil signup_tokens, so we can't look on it - raise ActiveRecord::RecordNotFound - else - UserManager.active_record_transaction do |user_manager| - # throws ActiveRecord::RecordNotFound if invalid - user = User.find_by_signup_token!(signup_token) - user.signup_token = nil - user.email_confirmed = true - user.save - return user - end - end - end - - # if valid credentials are supplied for an 'active' user, returns the user - # if not authenticated, returns nil - def self.authenticate(email, password) - # we only allow users that have confirmed email to authenticate - user = User.where('email_confirmed=true').find_by_email(email) - - if user && user.authenticate(password) return user - else - return nil end - end + # throws RecordNotFound if signup token is invalid; i.e., if it's nil, empty string, or not belonging to a user + def self.signup_confirm(signup_token) + if signup_token.nil? || signup_token.empty? + # there are plenty of confirmed users with nil signup_tokens, so we can't look on it + raise ActiveRecord::RecordNotFound + else + UserManager.active_record_transaction do |user_manager| + # throws ActiveRecord::RecordNotFound if invalid + user = User.find_by_signup_token!(signup_token) + user.signup_token = nil + user.email_confirmed = true + user.save + return user + end + end + end + + # if valid credentials are supplied for an 'active' user, returns the user + # if not authenticated, returns nil + def self.authenticate(email, password) + # we only allow users that have confirmed email to authenticate + user = User.where('email_confirmed=true').find_by_email(email) + + if user && user.authenticate(password) + return user + else + return nil + end + end ### Elasticsearch/Tire integration ### diff --git a/lib/jam_ruby/models/user_favorite.rb b/lib/jam_ruby/models/user_favorite.rb index 5de3e8504..04053bb22 100644 --- a/lib/jam_ruby/models/user_favorite.rb +++ b/lib/jam_ruby/models/user_favorite.rb @@ -6,5 +6,6 @@ module JamRuby self.primary_key = 'id' belongs_to :user, :class_name => "JamRuby::User", :foreign_key => "user_id", :inverse_of => :inverse_favorites + belongs_to :recording, :class_name => "JamRuby::Recording", :foreign_key => "recording_id" end end \ No newline at end of file diff --git a/spec/factories.rb b/spec/factories.rb index 1a5a645f8..f4a04fe7d 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -1,17 +1,21 @@ FactoryGirl.define do factory :user, :class => JamRuby::User do sequence(:email) { |n| "person_#{n}@example.com"} + sequence(:first_name) { |n| "Person" } + sequence(:last_name) { |n| "#{n}" } password "foobar" password_confirmation "foobar" email_confirmed true - + city "Apex" + state "NC" + country "USA" factory :admin do admin true end end - factory :music_session, :class => "JamRuby::MusicSession" do + factory :music_session, :class => JamRuby::MusicSession do sequence(:description) { |n| "Music Session #{n}" } fan_chat true fan_access true @@ -32,6 +36,10 @@ FactoryGirl.define do end factory :band, :class => JamRuby::Band do - + sequence(:name) { |n| "Band" } + biography "My Biography" + city "Apex" + state "NC" + country "USA" end end \ No newline at end of file diff --git a/spec/jam_ruby/connection_manager_spec.rb b/spec/jam_ruby/connection_manager_spec.rb index ef9ee8ac4..2b06001f5 100644 --- a/spec/jam_ruby/connection_manager_spec.rb +++ b/spec/jam_ruby/connection_manager_spec.rb @@ -11,7 +11,7 @@ describe ConnectionManager do end def create_user(first_name, last_name, email, options = {:musician => true}) - @conn.exec("INSERT INTO users (first_name, last_name, email, musician, password_digest) VALUES ($1, $2, $3, $4, $5) RETURNING id", [first_name, last_name, email, options[:musician], '1']) do |result| + @conn.exec("INSERT INTO users (first_name, last_name, email, musician, password_digest, city, state, country) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING id", [first_name, last_name, email, options[:musician], '1', 'Apex', 'NC', 'USA']) do |result| return result.getvalue(0, 0) end end diff --git a/spec/jam_ruby/models/band_search_spec.rb b/spec/jam_ruby/models/band_search_spec.rb index abbe0cd35..a4a3c3603 100644 --- a/spec/jam_ruby/models/band_search_spec.rb +++ b/spec/jam_ruby/models/band_search_spec.rb @@ -6,7 +6,7 @@ describe User do Band.delete_search_index Band.create_search_index - @band = Band.save(name: "Example Band", website: "www.bands.com", biography: "zomg we rock") + @band = Band.save(nil, "Example Band", "www.bands.com", "zomg we rock", "Apex", "NC", "USA", nil) # you have to poke elasticsearch because it will batch requests internally for a second Band.search_index.refresh @@ -58,7 +58,7 @@ describe User do end it "should tokenize correctly" do - @band2 = Band.save(name: "Peach pit", website: "www.bands.com", biography: "zomg we rock") + @band2 = Band.save(nil, "Peach pit", "www.bands.com", "zomg we rock", "Apex", "NC", "USA", nil) Band.search_index.refresh ws = Band.search("pea") ws.results.length.should == 1 @@ -68,7 +68,7 @@ describe User do it "should not return anything with a 1 character search" do - @band2 = Band.save(name: "Peach pit", website: "www.bands.com", biography: "zomg we rock") + @band2 = Band.save(nil, "Peach pit", "www.bands.com", "zomg we rock", "Apex", "NC", "USA", nil) Band.search_index.refresh ws = Band.search("pe") ws.results.length.should == 1 diff --git a/spec/jam_ruby/models/search_spec.rb b/spec/jam_ruby/models/search_spec.rb index f1655aab8..25f7bf77b 100644 --- a/spec/jam_ruby/models/search_spec.rb +++ b/spec/jam_ruby/models/search_spec.rb @@ -11,9 +11,9 @@ describe Search do def create_peachy_data - @user = FactoryGirl.create(:user, first_name: "Peach", last_name: "Pit", email: "user@example.com", musician: true) - @fan = FactoryGirl.create(:user, first_name: "Peach Peach", last_name: "Pit", email: "fan@example.com", musician: false) - @band = FactoryGirl.create(:band, name: "Peach pit", website: "www.bands.com", biography: "zomg we rock") + @user = FactoryGirl.create(:user, first_name: "Peach", last_name: "Pit", email: "user@example.com", musician: true, city: "Apex", state: "NC", country:"USA") + @fan = FactoryGirl.create(:user, first_name: "Peach Peach", last_name: "Pit", email: "fan@example.com", musician: false, city: "Apex", state: "NC", country:"USA") + @band = FactoryGirl.create(:band, name: "Peach pit", website: "www.bands.com", biography: "zomg we rock", city: "Apex", state: "NC", country:"USA") end def assert_peachy_data diff --git a/spec/jam_ruby/models/tire_search_spec.rb b/spec/jam_ruby/models/tire_search_spec.rb index c1306d081..0a3f1ecc5 100644 --- a/spec/jam_ruby/models/tire_search_spec.rb +++ b/spec/jam_ruby/models/tire_search_spec.rb @@ -20,7 +20,8 @@ describe "tire search" do end it "full search for single user" do - @user = FactoryGirl.create(:user, first_name: "User", last_name: "One", email: "user@example.com", musician: true) + @user = FactoryGirl.create(:user, first_name: "User", last_name: "One", email: "user@example.com", musician: true, + city: "Apex", state: "NC", country: "USA") User.search_index.refresh s = Tire.search ['test-jamruby-users', 'test-jamruby-bands'], :load => true do @@ -34,7 +35,9 @@ describe "tire search" do end it "full search for single band" do - @band = FactoryGirl.create(:band, name: "Example Band", website: "www.bands.com", biography: "zomg we rock") + @band = FactoryGirl.create(:band, name: "Example Band", website: "www.bands.com", biography: "zomg we rock", + city: "Apex", state: "NC", country: "USA") + Band.search_index.refresh s = Tire.search ['test-jamruby-users', 'test-jamruby-bands'], :load => true do @@ -48,8 +51,8 @@ describe "tire search" do end it "full search for a band & user" do - @user = FactoryGirl.create(:user, first_name: "Peach", last_name: "Foo", email: "user@example.com", musician: true) - @band = FactoryGirl.create(:band, name: "Peach pit", website: "www.bands.com", biography: "zomg we rock") + @user = FactoryGirl.create(:user, first_name: "Peach", last_name: "Foo", email: "user@example.com", musician: true, city: "Apex", state: "NC", country: "USA") + @band = FactoryGirl.create(:band, name: "Peach pit", website: "www.bands.com", biography: "zomg we rock", city: "Apex", state: "NC", country: "USA") User.search_index.refresh Band.search_index.refresh @@ -70,8 +73,8 @@ describe "tire search" do it "pagination" do - @user = FactoryGirl.create(:user, first_name: "Peach", last_name: "foo", email: "user@example.com", musician: true) - @band = FactoryGirl.create(:band, name: "Peach pit", website: "www.bands.com", biography: "zomg we rock") + @user = FactoryGirl.create(:user, first_name: "Peach", last_name: "foo", email: "user@example.com", musician: true, city: "Apex", state: "NC", country: "USA") + @band = FactoryGirl.create(:band, name: "Peach pit", website: "www.bands.com", biography: "zomg we rock", city: "Apex", state: "NC", country: "USA") User.search_index.refresh Band.search_index.refresh diff --git a/spec/jam_ruby/models/user_search_spec.rb b/spec/jam_ruby/models/user_search_spec.rb index 27c655e68..038597dae 100644 --- a/spec/jam_ruby/models/user_search_spec.rb +++ b/spec/jam_ruby/models/user_search_spec.rb @@ -7,7 +7,8 @@ describe User do User.create_search_index @user = FactoryGirl.create(:user, first_name: "Example", last_name: "User", email: "user@example.com", - password: "foobar", password_confirmation: "foobar", musician: true, email_confirmed: true) + password: "foobar", password_confirmation: "foobar", musician: true, email_confirmed: true, + city: "Apex", state: "NC", country: "USA") # you have to poke elasticsearch because it will batch requests internally for a second User.search_index.refresh end @@ -50,7 +51,7 @@ describe User do User.search_index.refresh ws = User.search("Example User") - ws.results.length.should == 0 + ws.results.length.should == 1 ws = User.search("Bonus") ws.results.length.should == 1 @@ -61,7 +62,8 @@ describe User do it "should tokenize correctly" do @user2 = FactoryGirl.create(:user, first_name: "peaches", last_name: "test", email: "peach@example.com", - password: "foobar", password_confirmation: "foobar", musician: true, email_confirmed: true) + password: "foobar", password_confirmation: "foobar", musician: true, email_confirmed: true, + city: "Apex", state: "NC", country: "USA") User.search_index.refresh ws = User.search("pea") ws.results.length.should == 1 @@ -72,7 +74,8 @@ describe User do it "users who have signed up, but not confirmed should not show up in search index" do @user3 = FactoryGirl.create(:user, first_name: "unconfirmed", last_name: "unconfirmed", email: "unconfirmed@example.com", - password: "foobar", password_confirmation: "foobar", musician: true, email_confirmed: false) + password: "foobar", password_confirmation: "foobar", musician: true, email_confirmed: false, + city: "Apex", state: "NC", country: "USA") User.search_index.refresh ws = User.search("unconfirmed") ws.results.length.should == 0 diff --git a/spec/jam_ruby/models/user_spec.rb b/spec/jam_ruby/models/user_spec.rb index cadf83fb2..bb20bb641 100644 --- a/spec/jam_ruby/models/user_spec.rb +++ b/spec/jam_ruby/models/user_spec.rb @@ -4,7 +4,7 @@ describe User do before do @user = User.new(first_name: "Example", last_name: "User", email: "user@example.com", - password: "foobar", password_confirmation: "foobar") + password: "foobar", password_confirmation: "foobar", city: "Apex", state: "NC", country: "USA") end subject { @user } From 969b003bc96353f61e0240a34f299a2616a18083 Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Wed, 21 Nov 2012 23:54:58 -0800 Subject: [PATCH 088/274] add confirm method --- lib/jam_ruby/models/user.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index aac63a509..ea008d9a8 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -119,6 +119,10 @@ module JamRuby def following_count return self.followings.size + self.band_followings.size end + + def confirm_email! + self.email_confirmed = true + end def to_s return email unless email.nil? @@ -304,7 +308,7 @@ module JamRuby # throws ActiveRecord::RecordNotFound if invalid user = User.find_by_signup_token!(signup_token) user.signup_token = nil - user.email_confirmed = true + user.confirm_email! user.save return user end From 4d20f49d415f260c14a441dc11df1d9e5e7f5afb Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Thu, 22 Nov 2012 03:27:23 -0500 Subject: [PATCH 089/274] VRFS-81 --- lib/jam_ruby/models/band.rb | 4 +- lib/jam_ruby/models/band_recording.rb | 2 + lib/jam_ruby/models/musician_recording.rb | 2 + lib/jam_ruby/models/recording.rb | 59 ++++++++++++++++++++--- lib/jam_ruby/models/user.rb | 30 +++++++----- spec/jam_ruby/models/tire_search_spec.rb | 1 + spec/jam_ruby/models/user_search_spec.rb | 1 - 7 files changed, 75 insertions(+), 24 deletions(-) diff --git a/lib/jam_ruby/models/band.rb b/lib/jam_ruby/models/band.rb index b4856c363..08bd953f4 100644 --- a/lib/jam_ruby/models/band.rb +++ b/lib/jam_ruby/models/band.rb @@ -37,7 +37,7 @@ module JamRuby def follower_count return self.followers.size end - + def location # TODO: implement a single string version of location; # this will be indexed into elasticsearch and returned in search @@ -135,7 +135,7 @@ module JamRuby :mappings => { "jam_ruby/band" => { :properties => { - :logo_url => { :type => :string, :index => :not_analyzed, :include_in_all => false }, + :logo_url => { :type => :string, :index => :not_analyzed, :include_in_all => false }, :photo_url => { :type => :string, :index => :not_analyzed, :include_in_all => false}, :name => { :type => :string, :boost => 100}, :location => { :type => :string }, diff --git a/lib/jam_ruby/models/band_recording.rb b/lib/jam_ruby/models/band_recording.rb index 2076f2283..958dcd973 100644 --- a/lib/jam_ruby/models/band_recording.rb +++ b/lib/jam_ruby/models/band_recording.rb @@ -3,6 +3,8 @@ module JamRuby self.table_name = "bands_recordings" + attr_accessible :band_id, :recording_id + # bands has_many :bands, :through => :band_recordings, :class_name => "JamRuby::Band" diff --git a/lib/jam_ruby/models/musician_recording.rb b/lib/jam_ruby/models/musician_recording.rb index 74a827a41..30293ce6c 100644 --- a/lib/jam_ruby/models/musician_recording.rb +++ b/lib/jam_ruby/models/musician_recording.rb @@ -3,6 +3,8 @@ module JamRuby self.table_name = "musicians_recordings" + attr_accessible :user_id, :recording_id + # musicians has_many :musicians, :through => :musician_recordings, :class_name => "JamRuby::User" diff --git a/lib/jam_ruby/models/recording.rb b/lib/jam_ruby/models/recording.rb index 8006221c4..0d4cfa8fd 100644 --- a/lib/jam_ruby/models/recording.rb +++ b/lib/jam_ruby/models/recording.rb @@ -1,22 +1,41 @@ module JamRuby class Recording < ActiveRecord::Base + USER_NOT_MUSICIAN_VALIDATION_ERROR = "You must be a Musician to create a recording." + USER_NOT_BAND_MEMBER_VALIDATION_ERROR = "You must be a band member to create a recording." + self.primary_key = 'id' - has_many :musician_recordings + has_many :musician_recordings, :class_name => "JamRuby::MusicianRecording" + has_many :band_recordings, :class_name => "JamRuby::BandRecording" # favorites has_and_belongs_to_many :user_favorites, :class_name => "JamRuby::UserFavorite", :join_table => "users_favorites" validates :description, presence: true, length: {maximum: 200} - def self.save(id, is_public, description, owner_id, is_band) + def self.save(id, is_public, description, updater_id, owner_id, is_band) + + creator = User.find(updater_id) + + if is_band + band = Band.find(owner_id) + validate_user_is_band_member(creator, band) + else + user = User.find(owner_id) + validate_user_is_creator(user, creator) + validate_user_is_musician(user) + end + if id.nil? recording = Recording.new() + recording.creator_id = updater_id else recording = Recording.find(id) end + recording.updater_id = updater_id + # public flag unless is_public.nil? recording.public = is_public @@ -27,22 +46,46 @@ module JamRuby recording.description = description end - if is_band? - recording.band << Band.create(id: owner_id) + recording.updated_at = Time.now.getutc + + # TODO: wrap in transaction with statements below + recording.save + + if is_band + recording.band_recordings << BandRecording.create(band_id: owner_id, recording_id: recording.id) else - recording.user << User.create(id: owner_id) + recording.musician_recordings << MusicianRecording.create(user_id: owner_id, recording_id: recording.id) end - recording.updated_at = Time.now.getutc - recording.save return recording end + private + def self.validate_user_is_band_member(user, band) + unless band.users.exists? user + raise PermissionError, "You must be a band member to create a recording for the band." + end + end + + def self.validate_user_is_creator(user, creator) + unless user.id == creator.id + raise PermissionError, "You do not have permission to perform this action." + end + end + + def self.validate_user_is_musician(user) + unless user.musician? + raise PermissionError, USER_NOT_MUSICIAN_VALIDATION_ERROR + end + end + +=begin def self.delete(id, owner_id, is_band) if is_band? - JamRuby::UserFollower.delete_all "(user_id = '#{user_id}' AND follower_id = '#{follower_id}')" + JamRuby::Recording.delete_all "(user_id = '#{user_id}' AND follower_id = '#{follower_id}')" else end end +=end end end \ No newline at end of file diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 9aad75372..87800b453 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -133,7 +133,7 @@ module JamRuby end # helper method for creating / updating a User - def self.save(id, first_name, last_name, email, password, password_confirmation, musician, gender, + def self.save(id, updater_id, first_name, last_name, email, password, password_confirmation, musician, gender, birth_date, internet_service_provider, city, state, country, instruments) if id.nil? user = User.new() @@ -141,6 +141,10 @@ module JamRuby user = User.find(id) end + if user.id != updater_id + raise PermissionError, "You do not have permissions to perform this action." + end + # first name unless first_name.nil? user.first_name = first_name @@ -227,10 +231,10 @@ module JamRuby end def self.create_user_following(user_id, follower_id) - @follower = UserFollower.new() - @follower.user_id = user_id - @follower.follower_id = follower_id - @follower.save + follower = UserFollower.new() + follower.user_id = user_id + follower.follower_id = follower_id + follower.save end def self.delete_user_following(user_id, follower_id) @@ -238,10 +242,10 @@ module JamRuby end def self.create_band_following(band_id, follower_id) - @follower = BandFollower.new() - @follower.band_id = band_id - @follower.follower_id = follower_id - @follower.save + follower = BandFollower.new() + follower.band_id = band_id + follower.follower_id = follower_id + follower.save end def self.delete_band_following(band_id, follower_id) @@ -249,10 +253,10 @@ module JamRuby end def self.create_favorite(user_id, recording_id) - @favorite = UserFavorite.new() - @favorite.user_id = user_id - @favorite.recording_id = recording_id - @favorite.save + favorite = UserFavorite.new() + favorite.user_id = user_id + favorite.recording_id = recording_id + favorite.save end def self.delete_favorite(user_id, recording_id) diff --git a/spec/jam_ruby/models/tire_search_spec.rb b/spec/jam_ruby/models/tire_search_spec.rb index 0a3f1ecc5..efc42c22d 100644 --- a/spec/jam_ruby/models/tire_search_spec.rb +++ b/spec/jam_ruby/models/tire_search_spec.rb @@ -22,6 +22,7 @@ describe "tire search" do it "full search for single user" do @user = FactoryGirl.create(:user, first_name: "User", last_name: "One", email: "user@example.com", musician: true, city: "Apex", state: "NC", country: "USA") + User.search_index.refresh s = Tire.search ['test-jamruby-users', 'test-jamruby-bands'], :load => true do diff --git a/spec/jam_ruby/models/user_search_spec.rb b/spec/jam_ruby/models/user_search_spec.rb index 038597dae..b4745cd6e 100644 --- a/spec/jam_ruby/models/user_search_spec.rb +++ b/spec/jam_ruby/models/user_search_spec.rb @@ -71,7 +71,6 @@ describe User do user_result.id.should == @user2.id end - it "users who have signed up, but not confirmed should not show up in search index" do @user3 = FactoryGirl.create(:user, first_name: "unconfirmed", last_name: "unconfirmed", email: "unconfirmed@example.com", password: "foobar", password_confirmation: "foobar", musician: true, email_confirmed: false, From 205725f4e3d43b55cf7b8aa45dade03ddcdce4b4 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Sat, 24 Nov 2012 13:22:44 -0500 Subject: [PATCH 090/274] VRFS-82 band invitation development --- lib/jam_ruby.rb | 2 + lib/jam_ruby/constants/limits.rb | 7 +++ lib/jam_ruby/constants/validation_messages.rb | 16 +++++++ lib/jam_ruby/models/band.rb | 9 ++-- lib/jam_ruby/models/band_invitation.rb | 44 +++++++++++++++++++ lib/jam_ruby/models/music_session.rb | 8 ++-- lib/jam_ruby/models/recording.rb | 19 ++++---- lib/jam_ruby/models/user.rb | 8 +++- 8 files changed, 94 insertions(+), 19 deletions(-) create mode 100644 lib/jam_ruby/constants/limits.rb create mode 100644 lib/jam_ruby/constants/validation_messages.rb create mode 100644 lib/jam_ruby/models/band_invitation.rb diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index fc6265480..1c01b0a51 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -8,6 +8,8 @@ require "will_paginate" require "will_paginate/active_record" require "action_mailer" require "sendgrid" +require "jam_ruby/constants/validation_messages" +require "jam_ruby/constants/limits" require "jam_ruby/errors/permission_error" require "jam_ruby/errors/state_error" require "jam_ruby/errors/jam_argument_error" diff --git a/lib/jam_ruby/constants/limits.rb b/lib/jam_ruby/constants/limits.rb new file mode 100644 index 000000000..73b02fd44 --- /dev/null +++ b/lib/jam_ruby/constants/limits.rb @@ -0,0 +1,7 @@ +module Limits + + MIN_GENRES_PER_BAND = 1 + MAX_GENRES_PER_BAND = 3 + MAX_INSTRUMENTS_PER_MUSICIAN = 5 + +end \ No newline at end of file diff --git a/lib/jam_ruby/constants/validation_messages.rb b/lib/jam_ruby/constants/validation_messages.rb new file mode 100644 index 000000000..d2a727158 --- /dev/null +++ b/lib/jam_ruby/constants/validation_messages.rb @@ -0,0 +1,16 @@ +module ValidationMessages + + PERMISSION_VALIDATION_ERROR = "You do not have permissions to perform this action." + + # recordings + USER_NOT_MUSICIAN_VALIDATION_ERROR = "You must be a Musician to create a recording." + USER_NOT_BAND_MEMBER_VALIDATION_ERROR = "You must be a band member to create a recording." + + # genres + GENRE_LIMIT_EXCEEDED = "No more than 3 genres are allowed." + GENRE_MINIMUM_NOT_MET = "At least 1 genre is required." + + # instruments + INSTRUMENT_LIMIT_EXCEEDED = "No more than 5 instruments are allowed." + +end \ No newline at end of file diff --git a/lib/jam_ruby/models/band.rb b/lib/jam_ruby/models/band.rb index 08bd953f4..13aaceff4 100644 --- a/lib/jam_ruby/models/band.rb +++ b/lib/jam_ruby/models/band.rb @@ -22,6 +22,9 @@ module JamRuby has_many :followers, :class_name => "JamRuby::BandFollower", :foreign_key => "band_id" has_many :inverse_followers, :through => :followers, :source => :user, :class_name => "JamRuby::Band", :foreign_key => "follower_id" + # invitations + has_many :invitations, :inverse_of => :band, :class_name => "JamRuby::BandInvitation", :foreign_key => "band_id" + after_save :limit_to_three_genres def photo_url @@ -60,7 +63,7 @@ module JamRuby # website unless website.nil? band.website = website - end + end # biography unless biography.nil? @@ -104,8 +107,8 @@ module JamRuby end def limit_to_three_genres - if self.genres.count > 3 - errors.add(:genres, "No more than 3 genres are allowed.") + if self.genres.count > Limits::MAX_GENRES_PER_BAND + errors.add(:genres, ValidationMessages::GENRE_LIMIT_EXCEEDED) end end diff --git a/lib/jam_ruby/models/band_invitation.rb b/lib/jam_ruby/models/band_invitation.rb new file mode 100644 index 000000000..1b9e9af0c --- /dev/null +++ b/lib/jam_ruby/models/band_invitation.rb @@ -0,0 +1,44 @@ +module JamRuby + class BandInvitation < ActiveRecord::Base + + self.table_name = "band_invitations" + + self.primary_key = 'id' + + belongs_to :receiver, :inverse_of => :received_band_invitations, :class_name => "JamRuby::User" + belongs_to :sender, :inverse_of => :sent_band_invitations, :class_name => "JamRuby::User" + belongs_to :band, :inverse_of => :invitations, :class_name => "JamRuby::Band" + + def self.save(id, band_id, user_id, creator_id, accepted) + if id.nil? + band_invitation = BandInvitation.new() + else + band_invitation = BandInvitation.find(id) + end + + # band ID + unless band_id.nil? + band_invitation.band_id = band_id + end + + # user ID + unless user_id.nil? + band_invitation.user_id = user_id + end + + # creator ID + unless creator_id.nil? + band_invitation.creator_id = creator_id + end + + # accepted flag + unless accepted.nil? + band_invitation.accepted = accepted + end + + band_invitation.updated_at = Time.now.getutc + band_invitation.save + return band_invitation + end + end +end \ No newline at end of file diff --git a/lib/jam_ruby/models/music_session.rb b/lib/jam_ruby/models/music_session.rb index d6813cbfa..d7f69c1bc 100644 --- a/lib/jam_ruby/models/music_session.rb +++ b/lib/jam_ruby/models/music_session.rb @@ -72,14 +72,14 @@ module JamRuby private def require_at_least_one_genre - if genres.count == 0 - errors.add(:genres, "Please select at least one genre") + if genres.count < Limits::MIN_GENRES_PER_BAND + errors.add(:genres, ValidationMessages::GENRE_MINIMUM_NOT_MET) end end def limit_to_three_genres - if genres.count > 3 - errors.add(:genres, "Three genres at most, please") + if genres.count > Limits::MAX_GENRES_PER_BAND + errors.add(:genres, ValidationMessages::GENRE_LIMIT_EXCEEDED) end end end diff --git a/lib/jam_ruby/models/recording.rb b/lib/jam_ruby/models/recording.rb index 0d4cfa8fd..4c97fd412 100644 --- a/lib/jam_ruby/models/recording.rb +++ b/lib/jam_ruby/models/recording.rb @@ -1,9 +1,6 @@ module JamRuby class Recording < ActiveRecord::Base - USER_NOT_MUSICIAN_VALIDATION_ERROR = "You must be a Musician to create a recording." - USER_NOT_BAND_MEMBER_VALIDATION_ERROR = "You must be a band member to create a recording." - self.primary_key = 'id' has_many :musician_recordings, :class_name => "JamRuby::MusicianRecording" @@ -51,10 +48,12 @@ module JamRuby # TODO: wrap in transaction with statements below recording.save - if is_band - recording.band_recordings << BandRecording.create(band_id: owner_id, recording_id: recording.id) - else - recording.musician_recordings << MusicianRecording.create(user_id: owner_id, recording_id: recording.id) + if id.nil? + if is_band + recording.band_recordings << BandRecording.create(band_id: owner_id, recording_id: recording.id) + else + recording.musician_recordings << MusicianRecording.create(user_id: owner_id, recording_id: recording.id) + end end return recording @@ -63,19 +62,19 @@ module JamRuby private def self.validate_user_is_band_member(user, band) unless band.users.exists? user - raise PermissionError, "You must be a band member to create a recording for the band." + raise PermissionError, ValidationMessages::USER_NOT_BAND_MEMBER_VALIDATION_ERROR end end def self.validate_user_is_creator(user, creator) unless user.id == creator.id - raise PermissionError, "You do not have permission to perform this action." + raise PermissionError, ValidationMessages::PERMISSION_VALIDATION_ERROR end end def self.validate_user_is_musician(user) unless user.musician? - raise PermissionError, USER_NOT_MUSICIAN_VALIDATION_ERROR + raise PermissionError, ValidationMessages::USER_NOT_MUSICIAN_VALIDATION_ERROR end end diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index d44761c5e..e01a2596e 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -62,6 +62,10 @@ module JamRuby has_many :received_fan_invitations, :foreign_key => "receiver_id", :inverse_of => :receiver, :class_name => "JamRuby::FanInvitation" has_many :sent_fan_invitations, :foreign_key => "sender_id", :inverse_of => :sender, :class_name => "JamRuby::FanInvitation" + # band invitations + has_many :received_band_invitations, :inverse_of => :receiver, :foreign_key => "user_id", :class_name => "JamRuby::BandInvitation" + has_many :sent_band_invitations, :inverse_of => :sender, :foreign_key => "creator_id", :class_name => "JamRuby::BandInvitation" + # This causes the authenticate method to be generated (among other stuff) has_secure_password @@ -268,8 +272,8 @@ module JamRuby end def limit_to_five_instruments - if instruments.count > 5 - errors.add(:instruments, "No more than 5 instruments are allowed.") + if instruments.count > Limits::MAX_INSTRUMENTS_PER_MUSICIAN + errors.add(:instruments, ValidationMessages::INSTRUMENT_LIMIT_EXCEEDED) end end From 74a949849fcb8394b7fbff716eca40dbb63ae02f Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Sun, 25 Nov 2012 14:37:54 -0500 Subject: [PATCH 091/274] VRFS-82 more band invitation development / unit test enhancements --- lib/jam_ruby.rb | 9 ++-- lib/jam_ruby/constants/limits.rb | 4 ++ lib/jam_ruby/constants/validation_messages.rb | 6 +++ lib/jam_ruby/models/band.rb | 52 +++++++++++++++---- lib/jam_ruby/models/band_invitation.rb | 20 +++++-- lib/jam_ruby/models/band_musician.rb | 2 + lib/jam_ruby/models/recording.rb | 2 +- lib/jam_ruby/models/user.rb | 48 +++++++++++------ spec/factories.rb | 1 + spec/jam_ruby/models/band_search_spec.rb | 8 +-- 10 files changed, 114 insertions(+), 38 deletions(-) diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index 1c01b0a51..5fee77563 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -26,6 +26,10 @@ require "jam_ruby/models/genre" require "jam_ruby/models/user" require "jam_ruby/models/user_authorization" require "jam_ruby/models/band" +require "jam_ruby/models/band_recording" +require "jam_ruby/models/band_invitation" +require "jam_ruby/models/band_follower" +require "jam_ruby/models/band_musician" require "jam_ruby/models/connection" require "jam_ruby/models/friendship" require "jam_ruby/models/music_session" @@ -33,17 +37,14 @@ require "jam_ruby/models/invitation" require "jam_ruby/models/fan_invitation" require "jam_ruby/models/friend_request" require "jam_ruby/models/instrument" -require "jam_ruby/models/connection_track" require "jam_ruby/models/musician_instrument" -require "jam_ruby/models/band_musician" +require "jam_ruby/models/connection_track" require "jam_ruby/models/user_follower" require "jam_ruby/models/user_following" require "jam_ruby/models/user_favorite" -require "jam_ruby/models/band_follower" require "jam_ruby/models/search" require "jam_ruby/models/recording" require "jam_ruby/models/musician_recording" -require "jam_ruby/models/band_recording" include Jampb diff --git a/lib/jam_ruby/constants/limits.rb b/lib/jam_ruby/constants/limits.rb index 73b02fd44..63e731356 100644 --- a/lib/jam_ruby/constants/limits.rb +++ b/lib/jam_ruby/constants/limits.rb @@ -1,7 +1,11 @@ module Limits + # genres MIN_GENRES_PER_BAND = 1 MAX_GENRES_PER_BAND = 3 + + # instruments + MIN_INSTRUMENTS_PER_MUSICIAN = 1 MAX_INSTRUMENTS_PER_MUSICIAN = 5 end \ No newline at end of file diff --git a/lib/jam_ruby/constants/validation_messages.rb b/lib/jam_ruby/constants/validation_messages.rb index d2a727158..fa4634141 100644 --- a/lib/jam_ruby/constants/validation_messages.rb +++ b/lib/jam_ruby/constants/validation_messages.rb @@ -1,10 +1,15 @@ module ValidationMessages + # general messages PERMISSION_VALIDATION_ERROR = "You do not have permissions to perform this action." + # band invitations + BAND_INVITATION_NOT_FOUND = "Band invitation not found." + # recordings USER_NOT_MUSICIAN_VALIDATION_ERROR = "You must be a Musician to create a recording." USER_NOT_BAND_MEMBER_VALIDATION_ERROR = "You must be a band member to create a recording." + RECORDING_NOT_FOUND = "Recording not found." # genres GENRE_LIMIT_EXCEEDED = "No more than 3 genres are allowed." @@ -12,5 +17,6 @@ module ValidationMessages # instruments INSTRUMENT_LIMIT_EXCEEDED = "No more than 5 instruments are allowed." + INSTRUMENT_MINIMUM_NOT_MET = "At least 1 instrument is required." end \ No newline at end of file diff --git a/lib/jam_ruby/models/band.rb b/lib/jam_ruby/models/band.rb index 13aaceff4..641ea07cc 100644 --- a/lib/jam_ruby/models/band.rb +++ b/lib/jam_ruby/models/band.rb @@ -25,8 +25,6 @@ module JamRuby # invitations has_many :invitations, :inverse_of => :band, :class_name => "JamRuby::BandInvitation", :foreign_key => "band_id" - after_save :limit_to_three_genres - def photo_url # TODO: move image path to config @photo_url = "http://www.jamkazam.com/images/bands/photos/#{self.id}.gif" @@ -46,12 +44,28 @@ module JamRuby # this will be indexed into elasticsearch and returned in search return "#{self.city}, #{self.state}, #{self.country}" end + + def add_member(user_id, admin) + BandMusician.create(:band_id => self.id, :user_id => user_id, :admin => admin) + end # helper method for creating / updating a Band - def self.save(id, name, website, biography, city, state, country, genres) + def self.save(id, name, website, biography, city, state, country, genres, user_id) + + # ensure person performing this action is a Musician + user = User.find(user_id) + + # new band if id.nil? + unless user.musician? + raise JamRuby::PermissionError, ValidationMessages::PERMISSION_VALIDATION_ERROR + end + validate_genres(genres, false) band = Band.new() + + # band update else + validate_genres(genres, true) band = Band.find(id) end @@ -103,16 +117,15 @@ module JamRuby band.updated_at = Time.now.getutc band.save + + # add the creator as the admin + if id.nil? + BandMusician.create(:band_id => band.id, :user_id => user_id, :admin => true) + end + return band end - def limit_to_three_genres - if self.genres.count > Limits::MAX_GENRES_PER_BAND - errors.add(:genres, ValidationMessages::GENRE_LIMIT_EXCEEDED) - end - end - - ### Elasticsearch/Tire integration ### # # Define the name based on the environment @@ -158,5 +171,24 @@ module JamRuby end end ### Elasticsearch/Tire integration + + private + def self.validate_genres(genres, is_nil_ok) + if is_nil_ok && genres.nil? + return + end + + if genres.nil? + raise JamRuby::JamArgumentError, ValidationMessages::GENRE_MINIMUM_NOT_MET + else + if genres.size < Limits::MIN_GENRES_PER_BAND + raise JamRuby::JamArgumentError, ValidationMessages::GENRE_MINIMUM_NOT_MET + end + + if genres.size > Limits::MAX_GENRES_PER_BAND + raise JamRuby::JamArgumentError, ValidationMessages::GENRE_LIMIT_EXCEEDED + end + end + end end end diff --git a/lib/jam_ruby/models/band_invitation.rb b/lib/jam_ruby/models/band_invitation.rb index 1b9e9af0c..720f532cb 100644 --- a/lib/jam_ruby/models/band_invitation.rb +++ b/lib/jam_ruby/models/band_invitation.rb @@ -5,11 +5,18 @@ module JamRuby self.primary_key = 'id' - belongs_to :receiver, :inverse_of => :received_band_invitations, :class_name => "JamRuby::User" - belongs_to :sender, :inverse_of => :sent_band_invitations, :class_name => "JamRuby::User" - belongs_to :band, :inverse_of => :invitations, :class_name => "JamRuby::Band" + belongs_to :receiver, :inverse_of => :received_band_invitations, :foreign_key => "user_id", :class_name => "JamRuby::User" + belongs_to :sender, :inverse_of => :sent_band_invitations, :foreign_key => "creator_id", :class_name => "JamRuby::User" + belongs_to :band, :inverse_of => :invitations, :foreign_key => "band_id", :class_name => "JamRuby::Band" def self.save(id, band_id, user_id, creator_id, accepted) + + # ensure recipient is a Musician + user = User.find(user_id) + unless user.musician? + raise JamRuby::JamArgumentError, "A Band invitation can only be sent to a Musician." + end + if id.nil? band_invitation = BandInvitation.new() else @@ -38,6 +45,13 @@ module JamRuby band_invitation.updated_at = Time.now.getutc band_invitation.save + + # TODO: wrap this and previous block in transaction + # accepting an invitation adds the musician to the band + if accepted + BandMusician.create(:band_id => band_id, :user_id => user_id, :admin => false) + end + return band_invitation end end diff --git a/lib/jam_ruby/models/band_musician.rb b/lib/jam_ruby/models/band_musician.rb index a750bafe4..91cc54c11 100644 --- a/lib/jam_ruby/models/band_musician.rb +++ b/lib/jam_ruby/models/band_musician.rb @@ -3,6 +3,8 @@ module JamRuby self.table_name = "bands_musicians" + attr_accessible :band_id, :user_id, :admin + self.primary_key = 'id' belongs_to :user diff --git a/lib/jam_ruby/models/recording.rb b/lib/jam_ruby/models/recording.rb index 4c97fd412..1cf46ca63 100644 --- a/lib/jam_ruby/models/recording.rb +++ b/lib/jam_ruby/models/recording.rb @@ -9,7 +9,7 @@ module JamRuby # favorites has_and_belongs_to_many :user_favorites, :class_name => "JamRuby::UserFavorite", :join_table => "users_favorites" - validates :description, presence: true, length: {maximum: 200} + validates :description, presence: true, length: { maximum: 200 } def self.save(id, is_public, description, updater_id, owner_id, is_band) diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index e01a2596e..df02dcb51 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -58,7 +58,7 @@ module JamRuby has_many :received_invitations, :foreign_key => "receiver_id", :inverse_of => :receiver, :class_name => "JamRuby::Invitation" has_many :sent_invitations, :foreign_key => "sender_id", :inverse_of => :sender, :class_name => "JamRuby::Invitation" - # fan_invitations + # fan invitations has_many :received_fan_invitations, :foreign_key => "receiver_id", :inverse_of => :receiver, :class_name => "JamRuby::FanInvitation" has_many :sent_fan_invitations, :foreign_key => "sender_id", :inverse_of => :sender, :class_name => "JamRuby::FanInvitation" @@ -72,8 +72,6 @@ module JamRuby before_save { |user| user.email = email.downcase } before_save :create_remember_token - after_save :limit_to_five_instruments - validates :first_name, presence: true, length: {maximum: 50} validates :last_name, presence: true, length: {maximum: 50} VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i @@ -144,13 +142,15 @@ module JamRuby def self.save(id, updater_id, first_name, last_name, email, password, password_confirmation, musician, gender, birth_date, internet_service_provider, city, state, country, instruments) if id.nil? + validate_instruments(instruments, musician) user = User.new() else + validate_instruments(instruments, true) user = User.find(id) end if user.id != updater_id - raise PermissionError, "You do not have permissions to perform this action." + raise PermissionError, ValidationMessages::PERMISSION_VALIDATION_ERROR end # first name @@ -222,13 +222,15 @@ module JamRuby end # loop through each instrument in the array and save to the db - instruments.each do |instrument| - mu = MusicianInstrument.new() - mu.user_id = user.id - mu.instrument_id = instrument[:id] - mu.priority = instrument[:priority] - mu.proficiency_level = instrument[:proficiency_level] - user.musician_instruments << mu + instruments.each do |musician_instrument_param| + instrument = Instrument.find(musician_instrument_param[:instrument_id]) + musician_instrument = MusicianInstrument.new + musician_instrument.user = user + musician_instrument.instrument = instrument + musician_instrument.proficiency_level = musician_instrument_param[:proficiency_level] + musician_instrument.priority = musician_instrument_param[:priority] + musician_instrument.save + user.musician_instruments << musician_instrument end end end @@ -271,12 +273,6 @@ module JamRuby JamRuby::UserFavorite.delete_all "(user_id = '#{user_id}' AND recording_id = '#{recording_id}')" end - def limit_to_five_instruments - if instruments.count > Limits::MAX_INSTRUMENTS_PER_MUSICIAN - errors.add(:instruments, ValidationMessages::INSTRUMENT_LIMIT_EXCEEDED) - end - end - # throws ActiveRecord::RecordNotFound if instrument is invalid # throws an email delivery error if unable to connect out to SMTP def self.signup(first_name, last_name, email, password, password_confirmation, @@ -429,5 +425,23 @@ module JamRuby def create_remember_token self.remember_token = SecureRandom.urlsafe_base64 end + + def self.validate_instruments(instruments, is_nil_ok) + if is_nil_ok && instruments.nil? + return + end + + if instruments.nil? + raise JamRuby::JamArgumentError, ValidationMessages::INSTRUMENT_MINIMUM_NOT_MET + else + if instruments.size < Limits::MIN_INSTRUMENTS_PER_MUSICIAN + raise JamRuby::JamArgumentError, ValidationMessages::INSTRUMENT_MINIMUM_NOT_MET + end + + if instruments.size > Limits::MAX_INSTRUMENTS_PER_MUSICIAN + raise JamRuby::JamArgumentError, ValidationMessages::INSTRUMENT_LIMIT_EXCEEDED + end + end + end end end diff --git a/spec/factories.rb b/spec/factories.rb index f4a04fe7d..9b6fe1cd2 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -9,6 +9,7 @@ FactoryGirl.define do city "Apex" state "NC" country "USA" + musician true factory :admin do admin true diff --git a/spec/jam_ruby/models/band_search_spec.rb b/spec/jam_ruby/models/band_search_spec.rb index a4a3c3603..f9a84adc8 100644 --- a/spec/jam_ruby/models/band_search_spec.rb +++ b/spec/jam_ruby/models/band_search_spec.rb @@ -2,11 +2,13 @@ require 'spec_helper' describe User do + let(:user) { FactoryGirl.create(:user) } + before(:each) do Band.delete_search_index Band.create_search_index - @band = Band.save(nil, "Example Band", "www.bands.com", "zomg we rock", "Apex", "NC", "USA", nil) + @band = Band.save(nil, "Example Band", "www.bands.com", "zomg we rock", "Apex", "NC", "USA", ["hip hop"], user.id) # you have to poke elasticsearch because it will batch requests internally for a second Band.search_index.refresh @@ -58,7 +60,7 @@ describe User do end it "should tokenize correctly" do - @band2 = Band.save(nil, "Peach pit", "www.bands.com", "zomg we rock", "Apex", "NC", "USA", nil) + @band2 = Band.save(nil, "Peach pit", "www.bands.com", "zomg we rock", "Apex", "NC", "USA", ["hip hop"], user.id) Band.search_index.refresh ws = Band.search("pea") ws.results.length.should == 1 @@ -68,7 +70,7 @@ describe User do it "should not return anything with a 1 character search" do - @band2 = Band.save(nil, "Peach pit", "www.bands.com", "zomg we rock", "Apex", "NC", "USA", nil) + @band2 = Band.save(nil, "Peach pit", "www.bands.com", "zomg we rock", "Apex", "NC", "USA", ["hip hop"], user.id) Band.search_index.refresh ws = Band.search("pe") ws.results.length.should == 1 From 4c1b0bc5c6bb24ccbca49a8e176061d1069dc2f4 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Sun, 25 Nov 2012 22:35:48 -0500 Subject: [PATCH 092/274] VRFS-82 band invitation complete --- lib/jam_ruby/models/band_invitation.rb | 39 +++++++++----------------- 1 file changed, 13 insertions(+), 26 deletions(-) diff --git a/lib/jam_ruby/models/band_invitation.rb b/lib/jam_ruby/models/band_invitation.rb index 720f532cb..94a830fd3 100644 --- a/lib/jam_ruby/models/band_invitation.rb +++ b/lib/jam_ruby/models/band_invitation.rb @@ -11,35 +11,22 @@ module JamRuby def self.save(id, band_id, user_id, creator_id, accepted) - # ensure recipient is a Musician - user = User.find(user_id) - unless user.musician? - raise JamRuby::JamArgumentError, "A Band invitation can only be sent to a Musician." - end - + # ensure certain fields are only updated on creation if id.nil? + # ensure recipient is a Musician + user = User.find(user_id) + unless user.musician? + raise JamRuby::JamArgumentError, "A Band invitation can only be sent to a Musician." + end + band_invitation = BandInvitation.new() + band_invitation.band_id = band_id + band_invitation.user_id = user_id + band_invitation.creator_id = creator_id + + # only the accepted flag can be updated after initial creation else band_invitation = BandInvitation.find(id) - end - - # band ID - unless band_id.nil? - band_invitation.band_id = band_id - end - - # user ID - unless user_id.nil? - band_invitation.user_id = user_id - end - - # creator ID - unless creator_id.nil? - band_invitation.creator_id = creator_id - end - - # accepted flag - unless accepted.nil? band_invitation.accepted = accepted end @@ -49,7 +36,7 @@ module JamRuby # TODO: wrap this and previous block in transaction # accepting an invitation adds the musician to the band if accepted - BandMusician.create(:band_id => band_id, :user_id => user_id, :admin => false) + BandMusician.create(:band_id => band_invitation.band.id, :user_id => band_invitation.receiver.id, :admin => false) end return band_invitation From 062dfe481c135e855966f55bc7c97fea1c406844 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Wed, 28 Nov 2012 00:26:53 -0500 Subject: [PATCH 093/274] VRFS 79 add image URL fields to users and bands tables --- lib/jam_ruby/constants/validation_messages.rb | 4 +-- lib/jam_ruby/models/band.rb | 31 ++++++++++++------- lib/jam_ruby/models/user.rb | 17 +++++----- spec/jam_ruby/models/band_search_spec.rb | 8 ++--- spec/jam_ruby/models/user_search_spec.rb | 1 - 5 files changed, 34 insertions(+), 27 deletions(-) diff --git a/lib/jam_ruby/constants/validation_messages.rb b/lib/jam_ruby/constants/validation_messages.rb index fa4634141..a6e08575b 100644 --- a/lib/jam_ruby/constants/validation_messages.rb +++ b/lib/jam_ruby/constants/validation_messages.rb @@ -2,13 +2,13 @@ module ValidationMessages # general messages PERMISSION_VALIDATION_ERROR = "You do not have permissions to perform this action." + USER_NOT_MUSICIAN_VALIDATION_ERROR = "You must be a Musician to perform this action." + USER_NOT_BAND_MEMBER_VALIDATION_ERROR = "You must be a band member to perform this action." # band invitations BAND_INVITATION_NOT_FOUND = "Band invitation not found." # recordings - USER_NOT_MUSICIAN_VALIDATION_ERROR = "You must be a Musician to create a recording." - USER_NOT_BAND_MEMBER_VALIDATION_ERROR = "You must be a band member to create a recording." RECORDING_NOT_FOUND = "Recording not found." # genres diff --git a/lib/jam_ruby/models/band.rb b/lib/jam_ruby/models/band.rb index 641ea07cc..927e5e916 100644 --- a/lib/jam_ruby/models/band.rb +++ b/lib/jam_ruby/models/band.rb @@ -25,16 +25,6 @@ module JamRuby # invitations has_many :invitations, :inverse_of => :band, :class_name => "JamRuby::BandInvitation", :foreign_key => "band_id" - def photo_url - # TODO: move image path to config - @photo_url = "http://www.jamkazam.com/images/bands/photos/#{self.id}.gif" - end - - def logo_url - # TODO: move image path to config - @logo_url = "http://www.jamkazam.com/images/bands/logos/#{self.id}.gif" - end - def follower_count return self.followers.size end @@ -50,16 +40,18 @@ module JamRuby end # helper method for creating / updating a Band - def self.save(id, name, website, biography, city, state, country, genres, user_id) + def self.save(id, name, website, biography, city, state, country, genres, user_id, photo_url, logo_url) - # ensure person performing this action is a Musician user = User.find(user_id) # new band if id.nil? + + # ensure person creating this Band is a Musician unless user.musician? raise JamRuby::PermissionError, ValidationMessages::PERMISSION_VALIDATION_ERROR end + validate_genres(genres, false) band = Band.new() @@ -67,6 +59,11 @@ module JamRuby else validate_genres(genres, true) band = Band.find(id) + + # ensure user updating Band details is a Band member + unless band.users.exists? user + raise PermissionError, ValidationMessages::USER_NOT_BAND_MEMBER_VALIDATION_ERROR + end end # name @@ -115,6 +112,16 @@ module JamRuby end end + # photo url + unless photo_url.nil? + band.photo_url = photo_url + end + + # logo url + unless logo_url.nil? + band.logo_url = logo_url + end + band.updated_at = Time.now.getutc band.save diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index df02dcb51..a23833464 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -87,11 +87,6 @@ module JamRuby @online ||= !self.connections.nil? && self.connections.size > 0 end - def photo_url - # TODO: move image path to config - @photo_url = "http://www.jamkazam.com/images/users/photos/#{self.id}.gif"; - end - def name return "#{first_name} #{last_name}" end @@ -140,7 +135,7 @@ module JamRuby # helper method for creating / updating a User def self.save(id, updater_id, first_name, last_name, email, password, password_confirmation, musician, gender, - birth_date, internet_service_provider, city, state, country, instruments) + birth_date, internet_service_provider, city, state, country, instruments, photo_url) if id.nil? validate_instruments(instruments, musician) user = User.new() @@ -235,6 +230,11 @@ module JamRuby end end + # photo url + unless photo_url.nil? + user.photo_url = photo_url + end + user.updated_at = Time.now.getutc user.save return user @@ -276,7 +276,7 @@ module JamRuby # throws ActiveRecord::RecordNotFound if instrument is invalid # throws an email delivery error if unable to connect out to SMTP def self.signup(first_name, last_name, email, password, password_confirmation, - city, state, country, instruments, signup_confirm_url) + city, state, country, instruments, photo_url, signup_confirm_url) user = User.new UserManager.active_record_transaction do |user_manager| @@ -312,6 +312,9 @@ module JamRuby user.musician_instruments << musician_instrument end end + + user.photo_url = photo_url + user.signup_token = SecureRandom.urlsafe_base64 user.save diff --git a/spec/jam_ruby/models/band_search_spec.rb b/spec/jam_ruby/models/band_search_spec.rb index f9a84adc8..1c45a2045 100644 --- a/spec/jam_ruby/models/band_search_spec.rb +++ b/spec/jam_ruby/models/band_search_spec.rb @@ -8,7 +8,7 @@ describe User do Band.delete_search_index Band.create_search_index - @band = Band.save(nil, "Example Band", "www.bands.com", "zomg we rock", "Apex", "NC", "USA", ["hip hop"], user.id) + @band = Band.save(nil, "Example Band", "www.bands.com", "zomg we rock", "Apex", "NC", "USA", ["hip hop"], user.id, nil, nil) # you have to poke elasticsearch because it will batch requests internally for a second Band.search_index.refresh @@ -22,8 +22,6 @@ describe User do band_result.name.should == @band.name band_result.id.should == @band.id band_result.location.should == @band.location - band_result.logo_url.should_not be_nil - band_result.photo_url.should_not be_nil end it "should delete band" do @@ -60,7 +58,7 @@ describe User do end it "should tokenize correctly" do - @band2 = Band.save(nil, "Peach pit", "www.bands.com", "zomg we rock", "Apex", "NC", "USA", ["hip hop"], user.id) + @band2 = Band.save(nil, "Peach pit", "www.bands.com", "zomg we rock", "Apex", "NC", "USA", ["hip hop"], user.id, nil, nil) Band.search_index.refresh ws = Band.search("pea") ws.results.length.should == 1 @@ -70,7 +68,7 @@ describe User do it "should not return anything with a 1 character search" do - @band2 = Band.save(nil, "Peach pit", "www.bands.com", "zomg we rock", "Apex", "NC", "USA", ["hip hop"], user.id) + @band2 = Band.save(nil, "Peach pit", "www.bands.com", "zomg we rock", "Apex", "NC", "USA", ["hip hop"], user.id, nil, nil) Band.search_index.refresh ws = Band.search("pe") ws.results.length.should == 1 diff --git a/spec/jam_ruby/models/user_search_spec.rb b/spec/jam_ruby/models/user_search_spec.rb index b4745cd6e..35c3e806a 100644 --- a/spec/jam_ruby/models/user_search_spec.rb +++ b/spec/jam_ruby/models/user_search_spec.rb @@ -23,7 +23,6 @@ describe User do user_result.id.should == @user.id user_result.location.should == @user.location user_result.musician.should == true - user_result.photo_url.should_not be_nil end it "should delete user" do From fe9c1462a495161465ab368559922f24bfa06ce6 Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Fri, 30 Nov 2012 01:34:50 -0800 Subject: [PATCH 094/274] cleanup on base mgr --- lib/jam_ruby/base_manager.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/jam_ruby/base_manager.rb b/lib/jam_ruby/base_manager.rb index d2970c24b..5da4b3b0a 100644 --- a/lib/jam_ruby/base_manager.rb +++ b/lib/jam_ruby/base_manager.rb @@ -13,7 +13,7 @@ module JamRuby end # Creates a connection manager, and associates the connection created by active_record with ourselves - def self.active_record_transaction(&block) + def self.active_record_transaction manager = self.new ActiveRecord::Base.connection_pool.with_connection do |connection| @@ -23,7 +23,7 @@ module JamRuby manager.pg_conn = connection.instance_variable_get("@connection") connection.transaction do - block.call(manager) + yield manager end end end From dc8aca38f61aeab049e4b0d620097ec9ea721764 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Fri, 30 Nov 2012 09:23:43 -0600 Subject: [PATCH 095/274] * VRFS-98: join_request added and related changes --- lib/jam_ruby.rb | 1 + lib/jam_ruby/base_manager.rb | 2 +- lib/jam_ruby/connection_manager.rb | 201 +++++++++++++-------- lib/jam_ruby/message_factory.rb | 6 + lib/jam_ruby/models/band.rb | 3 + lib/jam_ruby/models/connection.rb | 55 +++++- lib/jam_ruby/models/invitation.rb | 17 +- lib/jam_ruby/models/join_request.rb | 44 +++++ lib/jam_ruby/models/music_session.rb | 29 ++- spec/factories.rb | 6 + spec/jam_ruby/connection_manager_spec.rb | 199 ++++++++------------ spec/jam_ruby/models/invitation_spec.rb | 38 +++- spec/jam_ruby/models/join_request_spec.rb | 42 +++++ spec/jam_ruby/models/music_session_spec.rb | 11 ++ spec/spec_helper.rb | 2 +- 15 files changed, 443 insertions(+), 213 deletions(-) create mode 100644 lib/jam_ruby/models/join_request.rb create mode 100644 spec/jam_ruby/models/join_request_spec.rb diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index a71ebbf97..482976feb 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -23,6 +23,7 @@ require "jam_ruby/message_factory" require "jam_ruby/models/genre" require "jam_ruby/models/user" require "jam_ruby/models/user_authorization" +require "jam_ruby/models/join_request" require "jam_ruby/models/band" require "jam_ruby/models/connection" require "jam_ruby/models/friendship" diff --git a/lib/jam_ruby/base_manager.rb b/lib/jam_ruby/base_manager.rb index d2970c24b..1e3ad0747 100644 --- a/lib/jam_ruby/base_manager.rb +++ b/lib/jam_ruby/base_manager.rb @@ -12,7 +12,7 @@ module JamRuby end end - # Creates a connection manager, and associates the connection created by active_record with ourselves + # Creates a connection manager, and associates the connection created by active_record with ourselves def self.active_record_transaction(&block) manager = self.new diff --git a/lib/jam_ruby/connection_manager.rb b/lib/jam_ruby/connection_manager.rb index 4603d6d72..fee8c9c65 100644 --- a/lib/jam_ruby/connection_manager.rb +++ b/lib/jam_ruby/connection_manager.rb @@ -13,7 +13,7 @@ module JamRuby # Or of course we could just port the relevant methods to node-js class ConnectionManager < BaseManager - attr_accessor :mq_router + attr_accessor :mq_router def initialize(options={}) super(options) @@ -28,37 +28,45 @@ module JamRuby # remove stale connections def remove_stale_connections(max_seconds) - stale_clients = [] - @pg_conn.exec("SELECT client_id FROM connections WHERE updated_at < (NOW() - interval '#{max_seconds} second')") do |result| - result.each do |row| - stale_clients.push(row['client_id']) + ConnectionManager.active_record_transaction do |connection_manager| + conn = connection_manager.pg_conn + + stale_clients = [] + conn.exec("SELECT client_id FROM connections WHERE updated_at < (NOW() - interval '#{max_seconds} second')") do |result| + result.each do |row| + stale_clients.push(row['client_id']) + end end - end - @log.debug("deleting #{stale_clients.length} stale connections") + @log.debug("deleting #{stale_clients.length} stale connections") - stale_clients.each do |client_id| - delete_connection(client_id) + stale_clients.each do |client_id| + delete_connection(client_id) + end end end def create_connection(user_id, client_id, ip_address) - conn = @pg_conn + ConnectionManager.active_record_transaction do |connection_manager| + conn = connection_manager.pg_conn + + lock_connections(conn) - lock_connections(conn) + conn.exec("INSERT INTO connections (user_id, client_id, ip_address) VALUES ($1, $2, $3)", [user_id, client_id, ip_address]).clear - conn.exec("INSERT INTO connections (user_id, client_id, ip_address) VALUES ($1, $2, $3)", [user_id, client_id, ip_address]).clear - - # we just created a new connection-if this is the first time the user has shown up, we need to send out a message to his friends - conn.exec("SELECT count(user_id) FROM connections WHERE user_id = $1", [user_id]) do |result| - count = result.getvalue(0, 0) - if count == "1" - # get all friend user_ids using the same query rails does for @user.friends - friend_update = @message_factory.friend_update(user_id, true) - friend_ids = gather_friends(conn, user_id) - @mq_router.publish_to_friends(friend_ids, friend_update, user_id) + # we just created a new connection-if this is the first time the user has shown up, we need to send out a message to his friends + conn.exec("SELECT count(user_id) FROM connections WHERE user_id = $1", [user_id]) do |result| + count = result.getvalue(0, 0) + if count == "1" + # get all friend user_ids using the same query rails does for @user.friends + friend_update = @message_factory.friend_update(user_id, true) + friend_ids = gather_friends(conn, user_id) + @mq_router.publish_to_friends(friend_ids, friend_update, user_id) + end end + + return Connection.find_by_client_id!(client_id) end end @@ -66,47 +74,49 @@ module JamRuby # once a connection is known gone (whether timeout or because a TCP connection is observed lost) # this code is responsible for all cleanup logic associated with a connection going away def delete_connection(client_id) + ConnectionManager.active_record_transaction do |connection_manager| - user_id = nil - music_session_id = nil + conn = connection_manager.pg_conn - conn = @pg_conn + user_id = nil + music_session_id = nil - lock_connections(conn) + lock_connections(conn) - previous_music_session_id = check_already_session(conn, client_id) + previous_music_session_id = check_already_session(conn, client_id) - conn.exec("DELETE FROM connections WHERE client_id = $1 RETURNING user_id, music_session_id", [client_id]) do |result| + conn.exec("DELETE FROM connections WHERE client_id = $1 RETURNING user_id, music_session_id", [client_id]) do |result| - if result.cmd_tuples == 0 - # the client is already gone from the database... do nothing but log error - @log.warn("unable to delete client #{client_id}") - return - elsif result.cmd_tuples == 1 - user_id = result[0]['user_id'] - music_session_id = result[0]['client_id'] + if result.cmd_tuples == 0 + # the client is already gone from the database... do nothing but log error + @log.warn("unable to delete client #{client_id}") + return + elsif result.cmd_tuples == 1 + user_id = result[0]['user_id'] + music_session_id = result[0]['client_id'] - else - raise Exception, 'uniqueness constraint has been lost on client_id' + else + raise Exception, 'uniqueness constraint has been lost on client_id' + end end - end - session_checks(conn, previous_music_session_id, user_id) + session_checks(conn, previous_music_session_id, user_id) - # since we did delete a row, check and see if any more connections for that user exist - # if we are down to zero, send out user gone message - conn.exec("SELECT count(user_id) FROM connections where user_id = $1", [user_id]) do |result| - count = result.getvalue(0, 0) - if count == "0" - friend_update = @message_factory.friend_update(user_id, false) - friend_ids = gather_friends(conn, user_id) - @mq_router.publish_to_friends(friend_ids, friend_update, user_id) + # since we did delete a row, check and see if any more connections for that user exist + # if we are down to zero, send out user gone message + conn.exec("SELECT count(user_id) FROM connections where user_id = $1", [user_id]) do |result| + count = result.getvalue(0, 0) + if count == "0" + friend_update = @message_factory.friend_update(user_id, false) + friend_ids = gather_friends(conn, user_id) + @mq_router.publish_to_friends(friend_ids, friend_update, user_id) + end end - end - # same for session-if we are down to the last participant, delete the session - unless music_session_id.nil? - conn.exec("DELETE FROM music_sessions id = $1 AND 0 = (SELECT count(music_session_id) FROM connections where music_session_id = $1)", [music_session_id]).clear + # same for session-if we are down to the last participant, delete the session + unless music_session_id.nil? + conn.exec("DELETE FROM music_sessions id = $1 AND 0 = (SELECT count(music_session_id) FROM connections where music_session_id = $1)", [music_session_id]).clear + end end end @@ -157,7 +167,29 @@ module JamRuby end end - def join_music_session(user_id, client_id, music_session_id, as_musician) + def join_music_session(user_id, client_id, music_session_id, as_musician, tracks) + connection = nil + + #ConnectionManager.active_record_transaction do |connection_manager| + ActiveRecord::Base.transaction do + connection = Connection.find_by_client_id_and_user_id!(client_id, user_id) + connection.music_session_id = music_session_id + connection.as_musician = as_musician + connection.joining_session = true + associate_tracks(connection, tracks) + connection.save + + if connection.errors.any? + raise ActiveRecord::Rollback + else + + end + end + + return connection + end + + def join_music_session_old(user_id, client_id, music_session_id, as_musician) conn = @pg_conn lock_connections(conn) @@ -169,13 +201,11 @@ module JamRuby if as_musician != true && as_musician != false # checks that a boolean was passed in raise JamArgumentError, "as_musician incorrectly specified" end - - if as_musician && !user.musician - raise PermissionError, "a fan can not join a music session as a musician" - end # determine if the user can join; if not, throw a PermissionError music_session = MusicSession.find(music_session_id) + + unless music_session.can_join?(user, as_musician) @log.debug "user can not join a session user_id=#{user_id} and client_id=#{client_id}" raise PermissionError, "unable to join the specified session" @@ -210,32 +240,35 @@ module JamRuby end def leave_music_session(user_id, client_id, music_session_id) - conn = @pg_conn + ConnectionManager.active_record_transaction do |connection_manager| - lock_connections(conn) + conn = connection_manager.pg_conn - previous_music_session_id = check_already_session(conn, client_id) + lock_connections(conn) - if previous_music_session_id == nil - @log.debug "the client is not in a session. user=#{user_id}, client=#{client_id}, music_session=#{music_session_id}" - raise StateError, "not in session" - elsif previous_music_session_id != music_session_id - @log.debug "the client is in a different session. user=#{user_id}, client=#{client_id}, music_session=#{music_session_id}" - raise StateError, "in a session different than that specified" - end + previous_music_session_id = check_already_session(conn, client_id) - # can throw exception if the session is deleted just before this - conn.exec("UPDATE connections SET music_session_id = NULL, as_musician = NULL WHERE client_id = $1 AND user_id =$2", [client_id, user_id]) do |result| - if result.cmd_tuples == 1 - @log.debug("deassociated music_session with connection for client_id=#{client_id}, user_id=#{user_id}") + if previous_music_session_id == nil + @log.debug "the client is not in a session. user=#{user_id}, client=#{client_id}, music_session=#{music_session_id}" + raise StateError, "not in session" + elsif previous_music_session_id != music_session_id + @log.debug "the client is in a different session. user=#{user_id}, client=#{client_id}, music_session=#{music_session_id}" + raise StateError, "in a session different than that specified" + end - session_checks(conn, previous_music_session_id, user_id) - elsif result.cmd_tuples == 0 - @log.debug "leave_music_session no connection found with client_id=#{client_id}" - raise ActiveRecord::RecordNotFound - else - @log.error("database failure or logic error; this path should be impossible if the table is locked (leave_music_session)") - raise Exception, "locked table changed state" + # can throw exception if the session is deleted just before this + conn.exec("UPDATE connections SET music_session_id = NULL, as_musician = NULL WHERE client_id = $1 AND user_id =$2", [client_id, user_id]) do |result| + if result.cmd_tuples == 1 + @log.debug("deassociated music_session with connection for client_id=#{client_id}, user_id=#{user_id}") + + session_checks(conn, previous_music_session_id, user_id) + elsif result.cmd_tuples == 0 + @log.debug "leave_music_session no connection found with client_id=#{client_id}" + raise ActiveRecord::RecordNotFound + else + @log.error("database failure or logic error; this path should be impossible if the table is locked (leave_music_session)") + raise Exception, "locked table changed state" + end end end end @@ -254,6 +287,22 @@ module JamRuby return friend_ids end + def associate_tracks(connection, tracks) + @log.debug "Tracks:" + @log.debug tracks + unless tracks.nil? + tracks.each do |track| + instrument = Instrument.find(track["instrument_id"]) + connection_track = ConnectionTrack.new + connection_track.instrument = instrument + connection_track.connection = connection + connection_track.sound = track["sound"] + connection_track.save + connection.connection_tracks << connection_track + end + end + end + end end \ No newline at end of file diff --git a/lib/jam_ruby/message_factory.rb b/lib/jam_ruby/message_factory.rb index 3fdbc6d30..6d93e8d04 100644 --- a/lib/jam_ruby/message_factory.rb +++ b/lib/jam_ruby/message_factory.rb @@ -96,6 +96,12 @@ return Jampb::ClientMessage.new(:type => ClientMessage::Type::USER_LEFT_MUSIC_SESSION, :route_to => CLIENT_TARGET, :user_left_music_session => left) end + # create a user-joined session message + def join_request(session_id, join_request_id, username, text) + join_request = Jampb::JoinRequest.new(:join_request_id => join_request_id, :username => username, :text => text) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::JOIN_REQUEST, :route_to => SESSION_TARGET_PREFIX + session_id, :join_request => join_request) + end + # create a test message to send in session def test_session_message(session_id, msg) test = Jampb::TestSessionMessage.new(:msg => msg) diff --git a/lib/jam_ruby/models/band.rb b/lib/jam_ruby/models/band.rb index 6103f502a..24a8c6a4f 100644 --- a/lib/jam_ruby/models/band.rb +++ b/lib/jam_ruby/models/band.rb @@ -23,6 +23,9 @@ module JamRuby after_save :limit_to_three_genres + # music_sessions + has_many :music_sessions, :class_name => "JamRuby::MusicSession", :foreign_key => "band_id" + def photo_url # TODO: move image path to config @photo_url = "http://www.jamkazam.com/images/bands/photos/#{self.id}.gif" diff --git a/lib/jam_ruby/models/connection.rb b/lib/jam_ruby/models/connection.rb index 3c3dfe756..1152aeee7 100644 --- a/lib/jam_ruby/models/connection.rb +++ b/lib/jam_ruby/models/connection.rb @@ -2,6 +2,12 @@ module JamRuby class Connection < ActiveRecord::Base SELECT_AT_LEAST_ONE = "Please select at least one track" + FAN_CAN_NOT_JOIN_AS_MUSICIAN = "A fan can not join a music session as a musician" + MUSIC_SESSION_MUST_BE_SPECIFIED = "A music session must be specified" + INVITE_REQUIRED = "You must be invited to join this session" + FANS_CAN_NOT_JOIN = "Fans can not join this session" + + attr_accessor :joining_session self.primary_key = 'id' @@ -9,14 +15,59 @@ module JamRuby belongs_to :music_session, :class_name => "JamRuby::MusicSession" has_many :connection_tracks, :class_name => "JamRuby::ConnectionTrack", :inverse_of => :connection - after_save :require_at_least_one_track_when_in_session + + validates :as_musician, :inclusion => {:in => [true, false]} + validate :can_join_music_session, :if => :joining_session? + after_save :require_at_least_one_track_when_in_session, :if => :joining_session? + + def joining_session? + return joining_session + end + + def can_join_music_session + + if music_session.nil? + errors.add(:music_session, MUSIC_SESSION_MUST_BE_SPECIFIED) + return false + end + + if as_musician + unless self.user.musician + errors.add(:as_musician, FAN_CAN_NOT_JOIN_AS_MUSICIAN) + return false + end + + if music_session.musician_access + if music_session.approval_required + unless music_session.creator == user || music_session.invited_musicians.exists?(user) + errors.add(:approval_required, INVITE_REQUIRED) + return false + end + end + else + unless music_session.creator == user || music_session.invited_musicians.exists?(user) + errors.add(:musician_access, INVITE_REQUIRED) + return false + end + end + else + unless self.music_session.fan_access + # it's someone joining as a fan, and the only way a fan can join is if fan_access is true + errors.add(:fan_access, FANS_CAN_NOT_JOIN) + return false + end + end + + return true + end + # decides if a given user can access this client with p2p messaging # the answer is yes if the user is in the same music session def access_p2p?(user) return self.music_session.users.exists?(user) end - + private def require_at_least_one_track_when_in_session if connection_tracks.count == 0 diff --git a/lib/jam_ruby/models/invitation.rb b/lib/jam_ruby/models/invitation.rb index 239a9b2a2..9bd998d56 100644 --- a/lib/jam_ruby/models/invitation.rb +++ b/lib/jam_ruby/models/invitation.rb @@ -3,17 +3,19 @@ module JamRuby FRIENDSHIP_REQUIRED_VALIDATION_ERROR = "You can only invite friends" MEMBERSHIP_REQUIRED_OF_MUSIC_SESSION = "You must be a member of the music session to send invitations on behalf of it" + JOIN_REQUEST_IS_NOT_FOR_RECEIVER_AND_MUSIC_SESSION = "You can only associate a join request with an invitation if that join request comes from the invited user and if it's for the same music session" self.primary_key = 'id' belongs_to :sender, :inverse_of => :sent_invitations, :class_name => "JamRuby::User", :foreign_key => "sender_id" belongs_to :receiver, :inverse_of => :received_invitations, :class_name => "JamRuby::User", :foreign_key => "receiver_id" belongs_to :music_session, :inverse_of => :invitations, :class_name => "JamRuby::MusicSession" + belongs_to :join_request, :inverse_of => :invitations, :class_name => "JamRuby::JoinRequest" validates :sender, :presence => true validates :receiver, :presence => true validates :music_session, :presence => true - - validate :require_sender_in_music_session, :require_are_friends + + validate :require_sender_in_music_session, :require_are_friends_or_requested_to_join private @@ -23,9 +25,14 @@ module JamRuby end end - def require_are_friends - unless receiver.friends.exists? sender - errors.add(:receiver, FRIENDSHIP_REQUIRED_VALIDATION_ERROR) + def require_are_friends_or_requested_to_join + if !join_request.nil? && (join_request.user != receiver || join_request.music_session != music_session) + errors.add(:join_request, JOIN_REQUEST_IS_NOT_FOR_RECEIVER_AND_MUSIC_SESSION ) + elsif join_request.nil? + # we only check for friendship requirement if this was not in response to a join_request + unless receiver.friends.exists? sender + errors.add(:receiver, FRIENDSHIP_REQUIRED_VALIDATION_ERROR) + end end end end diff --git a/lib/jam_ruby/models/join_request.rb b/lib/jam_ruby/models/join_request.rb new file mode 100644 index 000000000..faa8d04fc --- /dev/null +++ b/lib/jam_ruby/models/join_request.rb @@ -0,0 +1,44 @@ +module JamRuby + class JoinRequest < ActiveRecord::Base + + REQUESTOR_MUST_BE_A_MUSICIAN = "requestor must be a musician" + + self.primary_key = 'id' + + belongs_to :user, :class_name => "JamRuby::User" + belongs_to :music_session, :class_name => "JamRuby::MusicSession" + has_many :invitations, :inverse_of => :join_request, :class_name => "JamRuby::Invitation" + + validates :user, :presence => true + validates :music_session, :presence => true + validates :text, presence: false, length: {maximum: 140} # arbitrary decision of 140. the database is at 2000 max on this field + + validates_uniqueness_of :user_id, :scope => :music_session_id + + validate :requestor_is_musician + + # list all paginations for the current user + def self.index(current_user) + # TODO pagination + return JoinRequest.where("join_requests.user_id = '#{current_user.id}'").order('join_requests.created_at DESC') + end + + def requestor_is_musician + unless user.musician? + errors.add(:user, REQUESTOR_MUST_BE_A_MUSICIAN) + end + end + + def to_s + return "#{self.user.to_s}:#{self.music_session.to_s}" + end + + + # permissions: + # only the creator of the join request can do a get + # or a member of the music_session that the join_request is designated for + def self.show(id, user) + return JoinRequest.find(id, :conditions => ["user_id = ? OR music_session_id IN (select music_session_id from connections WHERE user_id = ?)", user.id, user.id]) + end + end +end \ No newline at end of file diff --git a/lib/jam_ruby/models/music_session.rb b/lib/jam_ruby/models/music_session.rb index d6813cbfa..f5e1d2a28 100644 --- a/lib/jam_ruby/models/music_session.rb +++ b/lib/jam_ruby/models/music_session.rb @@ -10,30 +10,49 @@ module JamRuby has_many :connections, :class_name => "JamRuby::Connection" has_many :users, :through => :connections, :class_name => "JamRuby::User" has_and_belongs_to_many :genres, :class_name => "::JamRuby::Genre", :join_table => "genres_music_sessions" + has_many :join_requests, :foreign_key => "music_session_id", :inverse_of => :music_session, :class_name => "JamRuby::JoinRequest" has_many :invitations, :foreign_key => "music_session_id", :inverse_of => :music_session, :class_name => "JamRuby::Invitation" has_many :invited_musicians, :through => :invitations, :class_name => "JamRuby::User", :foreign_key => "receiver_id", :source => :receiver has_many :fan_invitations, :foreign_key => "music_session_id", :inverse_of => :music_session, :class_name => "JamRuby::FanInvitation" has_many :invited_fans, :through => :fan_invitations, :class_name => "JamRuby::User", :foreign_key => "receiver_id", :source => :receiver + belongs_to :band, :inverse_of => :music_sessions, :class_name => "JamRuby::Band", :foreign_key => "band_id" + after_save :require_at_least_one_genre, :limit_to_three_genres validates :fan_chat, :inclusion => {:in => [true, false]} validates :fan_access, :inclusion => {:in => [true, false]} validates :approval_required, :inclusion => {:in => [true, false]} validates :musician_access, :inclusion => {:in => [true, false]} - validates :creator, :presence => true, :if => "creator.musician?" + validates :creator, :presence => true + validate :creator_is_musician + + def creator_is_musician + unless creator.musician? + errors.add(:creator, "creator must be a musician") + end + end def self.index(current_user) - return MusicSession.includes(:invitations, :connections => [:user => [:friendships]]).order("(invitations.id is NULL) ASC, (friendships.friend_id != '#{current_user.id}' OR friendships.friend_id is NULL) ASC, music_sessions.created_at DESC").where("invitations.receiver_id = '#{current_user.id}' OR (friendships.friend_id = '#{current_user.id}') OR musician_access = true") - end + return MusicSession.includes(:invitations, :join_requests, :connections => [:user => [:friendships]]).order("(invitations.id is NULL) ASC, (friendships.friend_id != '#{current_user.id}' OR friendships.friend_id is NULL) ASC, music_sessions.created_at DESC").where("invitations.receiver_id = '#{current_user.id}' OR (friendships.friend_id = '#{current_user.id}') OR musician_access = true") + end # Verifies that the specified user can join this music session def can_join? user, as_musician - if as_musician + if !user.musician + return false # "a fan can not join a music session as a musician" + raise PermissionError, "a fan can not join a music session as a musician" + end + if self.musician_access - return true + if self.approval_required + return self.invited_musicians.exists?(user) + else + return true + end + else # the creator can always join, and the invited users can join return self.creator == user || self.invited_musicians.exists?(user) diff --git a/spec/factories.rb b/spec/factories.rb index 1a5a645f8..78d3d8908 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -4,6 +4,7 @@ FactoryGirl.define do password "foobar" password_confirmation "foobar" email_confirmed true + musician true factory :admin do @@ -21,6 +22,7 @@ FactoryGirl.define do factory :connection, :class => JamRuby::Connection do sequence(:client_id) { |n| "Client#{n}" } + as_musician true end factory :invitation, :class => JamRuby::Invitation do @@ -34,4 +36,8 @@ FactoryGirl.define do factory :band, :class => JamRuby::Band do end + + factory :join_request, :class => JamRuby::JoinRequest do + text 'let me in to the session!' + end end \ No newline at end of file diff --git a/spec/jam_ruby/connection_manager_spec.rb b/spec/jam_ruby/connection_manager_spec.rb index ef9ee8ac4..3102ff251 100644 --- a/spec/jam_ruby/connection_manager_spec.rb +++ b/spec/jam_ruby/connection_manager_spec.rb @@ -3,6 +3,7 @@ require 'spec_helper' # these tests avoid the use of ActiveRecord and FactoryGirl to do blackbox, non test-instrumented tests describe ConnectionManager do + TRACKS = [{"instrument_id" => "electric guitar", "sound" => "mono"}] before do @conn = PG::Connection.new(:dbname => SpecDb::TEST_DB_NAME, :user => "postgres", :password => "postgres", :host => "localhost") @@ -43,11 +44,7 @@ describe ConnectionManager do end it "can't create bogus user_id" do - - @conn.transaction do expect { @connman.create_connection("aeonuthaoentuh", "client_id", "1.1.1.1") }.to raise_error(PG::Error) - end - end it "can't create two client_ids of same value" do @@ -55,27 +52,23 @@ describe ConnectionManager do client_id = "client_id1" user_id = create_user("test", "user1", "user1@jamkazam.com") - @conn.transaction do - @connman.create_connection(user_id, client_id, "1.1.1.1") - expect { @connman.create_connection(user_id, client_id, "1.1.1.1") }.to raise_error(PG::Error) - end + @connman.create_connection(user_id, client_id, "1.1.1.1") + expect { @connman.create_connection(user_id, client_id, "1.1.1.1") }.to raise_error(PG::Error) end it "create connection then delete it" do - + client_id = "client_id2" user_id = create_user("test", "user2", "user2@jamkazam.com") - @conn.transaction do - @connman.create_connection(user_id, client_id, "1.1.1.1") - end + connection = @connman.create_connection(user_id, client_id, "1.1.1.1") + # make sure the connection is seen + @conn.exec("SELECT count(*) FROM connections where user_id = $1", [user_id]) do |result| result.getvalue(0, 0).should == "1" end - @conn.transaction do - @connman.delete_connection(client_id) - end + @connman.delete_connection(client_id) @conn.exec("SELECT count(*) FROM connections where user_id = $1", [user_id]) do |result| result.getvalue(0, 0).should == "0" @@ -93,16 +86,12 @@ describe ConnectionManager do friend_update = @message_factory.friend_update(user_id, true) @connman.mq_router.should_receive(:publish_to_friends).with([], friend_update, user_id) - @conn.transaction do - @connman.create_connection(user_id, client_id, "1.1.1.1") - end + @connman.create_connection(user_id, client_id, "1.1.1.1") # but a second connection from the same user should cause no such message @connman.should_receive(:publish_to_friends).exactly(0).times - @conn.transaction do - @connman.create_connection(user_id, client_id2, "1.1.1.1") - end + @connman.create_connection(user_id, client_id2, "1.1.1.1") end @@ -116,30 +105,19 @@ describe ConnectionManager do # we should get a message saying that this user is online - @conn.transaction do - @connman.create_connection(user_id, client_id, "1.1.1.1") - end - - @conn.transaction do - @connman.create_connection(user_id, client_id2, "1.1.1.1") - end + @connman.create_connection(user_id, client_id, "1.1.1.1") + @connman.create_connection(user_id, client_id2, "1.1.1.1") # deleting one of the two connections should cause no messages @connman.should_receive(:publish_to_friends).exactly(0).times - @conn.transaction do - @connman.delete_connection(client_id) - end + @connman.delete_connection(client_id) # but deleting the final connection should cause a left message friend_update = @message_factory.friend_update(user_id, false) @connman.mq_router.should_receive(:publish_to_friends).with([], friend_update, user_id) - @conn.transaction do - @connman.delete_connection(client_id2) - end - - + @connman.delete_connection(client_id2) end it "lookup of friends should find mutual friends only" do @@ -197,22 +175,16 @@ describe ConnectionManager do user_id = create_user("test", "user8", "user8@jamkazam.com") - @conn.transaction do - @connman.create_connection(user_id, client_id, "1.1.1.1") - end + @connman.create_connection(user_id, client_id, "1.1.1.1") - @conn.transaction do - @connman.remove_stale_connections(60) - end + @connman.remove_stale_connections(60) assert_num_connections(client_id, 1) sleep(1) - @conn.transaction do - # this should remove the stale connection - @connman.remove_stale_connections(1) - end + # this should remove the stale connection + @connman.remove_stale_connections(1) assert_num_connections(client_id, 0) end @@ -223,20 +195,18 @@ describe ConnectionManager do user_id = create_user("test", "user9", "user9@jamkazam.com") music_session_id = create_music_session(user_id) - @conn.transaction do - @connman.create_connection(user_id, client_id, "1.1.1.1") - @connman.join_music_session(user_id, client_id, music_session_id, true) - end + @connman.create_connection(user_id, client_id, "1.1.1.1") + connection = @connman.join_music_session(user_id, client_id, music_session_id, true, TRACKS) + connection.errors.any?.should be_false + assert_session_exists(music_session_id, true) @conn.exec("SELECT music_session_id FROM connections WHERE client_id = $1", [client_id]) do |result| result.getvalue(0, 0).should == music_session_id end - @conn.transaction do - @connman.delete_connection(client_id) - end + @connman.delete_connection(client_id) assert_num_connections(client_id, 0) assert_session_exists(music_session_id, false) @@ -248,49 +218,53 @@ describe ConnectionManager do user_id = create_user("test", "user10", "user10@jamkazam.com") music_session_id = create_music_session(user_id) - @conn.transaction do - expect { @connman.join_music_session(user_id, client_id, music_session_id, true) }.to raise_error(ActiveRecord::RecordNotFound) - end + expect { @connman.join_music_session(user_id, client_id, music_session_id, true, TRACKS) }.to raise_error(ActiveRecord::RecordNotFound) end it "join_music_session fails if user is a fan but wants to join as a musician" do - client_id = "client_id10.1" - user_id = create_user("test", "user10.1", "user10.1@jamkazam.com", :musician => false) + client_id = "client_id10.11" + client_id2 = "client_id10.12" + user_id = create_user("test", "user10.11", "user10.11@jamkazam.com", :musician => true) + user_id2 = create_user("test", "user10.12", "user10.12@jamkazam.com", :musician => false) + @connman.create_connection(user_id, client_id, "1.1.1.1") + @connman.create_connection(user_id2, client_id2, "1.1.1.1") music_session_id = create_music_session(user_id) + @connman.join_music_session(user_id, client_id, music_session_id, true, TRACKS) - @conn.transaction do - expect { @connman.join_music_session(user_id, client_id, music_session_id, true) }.to raise_error(JamRuby::PermissionError) - end - + connection = @connman.join_music_session(user_id2, client_id2, music_session_id, true, TRACKS) + connection.errors.size.should == 1 + connection.errors.get(:as_musician).should == [Connection::FAN_CAN_NOT_JOIN_AS_MUSICIAN] end - it "join_music_session fails if invalid value for as_musician specified" do - + it "as_musician is coerced to boolean" do client_id = "client_id10.2" user_id = create_user("test", "user10.2", "user10.2@jamkazam.com", :musician => false) + @connman.create_connection(user_id, client_id, "1.1.1.1") music_session_id = create_music_session(user_id) - @conn.transaction do - expect { @connman.join_music_session(user_id, client_id, music_session_id, 'blarg') }.to raise_error(JamRuby::JamArgumentError) - end - + connection = @connman.join_music_session(user_id, client_id, music_session_id, 'blarg', TRACKS) + connection.errors.size.should == 0 + connection.as_musician.should be_false end it "join_music_session fails if fan_access=false and the user is a fan" do - client_id = "client_id10.3" - user_id = create_user("test", "user10.3", "user10.3@jamkazam.com", :musician => false) - - music_session_id = create_music_session(user_id, :fan_access => false) - - @conn.transaction do - expect { @connman.join_music_session(user_id, client_id, music_session_id, false) }.to raise_error(JamRuby::PermissionError) - end + musician_client_id = "client_id10.3" + fan_client_id = "client_id10.4" + musician = create_user("test", "user10.3", "user10.3@jamkazam.com") + fan = create_user("test", "user10.4", "user10.4@jamkazam.com", :musician => false) + @connman.create_connection(musician, musician_client_id, "1.1.1.1") + @connman.create_connection(fan, fan_client_id, "1.1.1.1") + music_session_id = create_music_session(musician, :fan_access => false) + @connman.join_music_session(musician, musician_client_id, music_session_id, true, TRACKS) + # now join the session as a fan, bt fan_access = false + connection = @connman.join_music_session(fan, fan_client_id, music_session_id, false, TRACKS) + connection.errors.size.should == 1 end it "join_music_session fails if incorrect user_id specified" do @@ -300,40 +274,41 @@ describe ConnectionManager do user_id2 = create_user("test", "user21", "user21@jamkazam.com") music_session_id = create_music_session(user_id) - @conn.transaction do - @connman.create_connection(user_id, client_id, "1.1.1.1") - # specify real user id, but not associated with this session - expect { @connman.join_music_session(user_id2, client_id, music_session_id, true) } .to raise_error(ActiveRecord::RecordNotFound) - end + @connman.create_connection(user_id, client_id, "1.1.1.1") + # specify real user id, but not associated with this session + expect { @connman.join_music_session(user_id2, client_id, music_session_id, true, TRACKS) } .to raise_error(ActiveRecord::RecordNotFound) end it "join_music_session fails if no music_session" do - client_id = "client_id11" user_id = create_user("test", "user11", "user11@jamkazam.com") - @conn.transaction do - @connman.create_connection(user_id, client_id, "1.1.1.1") - end - - @conn.transaction do - expect { @connman.join_music_session(user_id, client_id, "some_bogus_music_session_id", true) }.to raise_error(ActiveRecord::RecordNotFound) - end - + @connman.create_connection(user_id, client_id, "1.1.1.1") + connection = @connman.join_music_session(user_id, client_id, "some_bogus_music_session_id", true, TRACKS) + connection.errors.size.should == 1 + connection.errors.get(:music_session).should == [Connection::MUSIC_SESSION_MUST_BE_SPECIFIED] end + it "join_music_session fails if approval_required and no invitation, but generates join_request" do + client_id = "client_id11.1" + user_id = create_user("test", "user11.1", "user11.1@jamkazam.com") + user_id2 = create_user("test", "user11.2", "user11.2@jamkazam.com") + music_session_id = create_music_session(user_id, :approval_required => true) + + @connman.create_connection(user_id, client_id, "1.1.1.1") + # specify real user id, but not associated with this session + expect { @connman.join_music_session(user_id2, client_id, music_session_id, true, TRACKS) } .to raise_error(ActiveRecord::RecordNotFound) + end + + it "leave_music_session fails if no music_session" do client_id = "client_id12" user_id = create_user("test", "user12", "user12@jamkazam.com") - @conn.transaction do - @connman.create_connection(user_id, client_id, "1.1.1.1") - end + @connman.create_connection(user_id, client_id, "1.1.1.1") - @conn.transaction do - expect { @connman.leave_music_session(user_id, client_id, "some_bogus_music_session_id") }.to raise_error(JamRuby::StateError) - end + expect { @connman.leave_music_session(user_id, client_id, "some_bogus_music_session_id") }.to raise_error(JamRuby::StateError) end it "leave_music_session fails if in different music_session" do @@ -342,17 +317,9 @@ describe ConnectionManager do user_id = create_user("test", "user13", "user13@jamkazam.com") music_session_id = create_music_session(user_id) - @conn.transaction do - @connman.create_connection(user_id, client_id, "1.1.1.1") - end - - @conn.transaction do - @connman.join_music_session(user_id, client_id, music_session_id, true) - end - - @conn.transaction do - expect { @connman.leave_music_session(user_id, client_id, "some_bogus_music_session_id") }.to raise_error(JamRuby::StateError) - end + @connman.create_connection(user_id, client_id, "1.1.1.1") + @connman.join_music_session(user_id, client_id, music_session_id, true, TRACKS) + expect { @connman.leave_music_session(user_id, client_id, "some_bogus_music_session_id") }.to raise_error(JamRuby::StateError) end it "leave_music_session works" do @@ -361,14 +328,8 @@ describe ConnectionManager do user_id = create_user("test", "user14", "user14@jamkazam.com") music_session_id = create_music_session(user_id) - - @conn.transaction do - @connman.create_connection(user_id, client_id, "1.1.1.1") - end - - @conn.transaction do - @connman.join_music_session(user_id, client_id, music_session_id, true) - end + @connman.create_connection(user_id, client_id, "1.1.1.1") + @connman.join_music_session(user_id, client_id, music_session_id, true, TRACKS) assert_session_exists(music_session_id, true) @@ -376,9 +337,7 @@ describe ConnectionManager do result.getvalue(0, 0).should == music_session_id end - @conn.transaction do - @connman.leave_music_session(user_id, client_id, music_session_id) - end + @connman.leave_music_session(user_id, client_id, music_session_id) @conn.exec("SELECT music_session_id FROM connections WHERE client_id = $1", [client_id]) do |result| result.getvalue(0, 0).should == nil @@ -386,9 +345,7 @@ describe ConnectionManager do assert_session_exists(music_session_id, false) - @conn.transaction do - @connman.delete_connection(client_id) - end + @connman.delete_connection(client_id) assert_num_connections(client_id, 0) diff --git a/spec/jam_ruby/models/invitation_spec.rb b/spec/jam_ruby/models/invitation_spec.rb index 0a6721a97..b5e1cb07f 100644 --- a/spec/jam_ruby/models/invitation_spec.rb +++ b/spec/jam_ruby/models/invitation_spec.rb @@ -14,7 +14,7 @@ describe MusicSession do invitation = Invitation.new(:sender => user1, :receiver => user2, :music_session => music_session) - invitation.save.should == false + invitation.save.should be_false invitation.errors.size.should == 1 invitation.errors.get(:receiver).should == [Invitation::FRIENDSHIP_REQUIRED_VALIDATION_ERROR] end @@ -34,6 +34,40 @@ describe MusicSession do invitation = Invitation.new(:sender => user1, :receiver => user2, :music_session => music_session) - invitation.save.should == true + invitation.save.should be_true + end + + it 'can create invitation to a user who made a join_request' do + user1 = FactoryGirl.create(:user) # in the jam session + user2 = FactoryGirl.create(:user) # in the jam session + + music_session = FactoryGirl.create(:music_session, :creator => user1) + + music_session_member1 = FactoryGirl.create(:connection, :user => user1, :music_session => music_session, :ip_address => "1.1.1.1", :client_id => "1") + connection2 = FactoryGirl.create(:connection, :user => user2, :ip_address => "2.2.2.2", :client_id => "2") + + join_request = FactoryGirl.create(:join_request, :user => user2, :music_session => music_session) + + invitation = Invitation.new(:sender => user1, :receiver => user2, :music_session => music_session, :join_request => join_request) + + invitation.save.should be_true + end + + it 'cant create invitation to a user who did not make a join_request and is not a friend' do + user1 = FactoryGirl.create(:user) # in the jam session + user2 = FactoryGirl.create(:user) # in the jam session + + music_session = FactoryGirl.create(:music_session, :creator => user1) + music_session2 = FactoryGirl.create(:music_session, :creator => user1) + + music_session_member1 = FactoryGirl.create(:connection, :user => user1, :music_session => music_session, :ip_address => "1.1.1.1", :client_id => "1") + connection2 = FactoryGirl.create(:connection, :user => user2, :ip_address => "2.2.2.2", :client_id => "2") + + join_request = FactoryGirl.create(:join_request, :user => user2, :music_session => music_session2) + + invitation = Invitation.new(:sender => user1, :receiver => user2, :music_session => music_session, :join_request => join_request) + + invitation.save.should be_false + invitation.errors.get(:join_request).should == [Invitation::JOIN_REQUEST_IS_NOT_FOR_RECEIVER_AND_MUSIC_SESSION ] end end diff --git a/spec/jam_ruby/models/join_request_spec.rb b/spec/jam_ruby/models/join_request_spec.rb new file mode 100644 index 000000000..c13d890db --- /dev/null +++ b/spec/jam_ruby/models/join_request_spec.rb @@ -0,0 +1,42 @@ +require 'spec_helper' + +describe JoinRequest do + + it 'can create a join request' do + user1 = FactoryGirl.create(:user) + music_session = FactoryGirl.create(:music_session, :creator => user1) + music_session_member1 = FactoryGirl.create(:connection, :user => user1, :music_session => music_session) + join_request = JoinRequest.new(:user => user1, :music_session => music_session, :text => "Let me join yo") + + join_request.save.should be_true + + join_requests = JoinRequest.index(user1) + join_requests.length.should == 1 + join_requests[0].id.should == join_request.id + end + + it 'fans cant create a join request' do + user1 = FactoryGirl.create(:user, :musician => true) + user2 = FactoryGirl.create(:user, :musician => false) + music_session = FactoryGirl.create(:music_session, :creator => user1) + music_session_member1 = FactoryGirl.create(:connection, :user => user1, :music_session => music_session) + join_request = JoinRequest.new(:user => user2, :music_session => music_session, :text => "Let me join yo") + + join_request.save.should be_false + join_request.errors.size.should == 1 + join_request.errors.get(:user).should == [JoinRequest::REQUESTOR_MUST_BE_A_MUSICIAN] + end + + it 'cant create a dup join_request' do + user1 = FactoryGirl.create(:user) + music_session = FactoryGirl.create(:music_session, :creator => user1) + music_session_member1 = FactoryGirl.create(:connection, :user => user1, :music_session => music_session) + join_request = JoinRequest.new(:user => user1, :music_session => music_session, :text => "Let me join yo") + join_request.save.should be_true + + join_request2 = JoinRequest.new(:user => user1, :music_session => music_session, :text => "Let me join yo") + + join_request2.save.should be_false + join_request2.errors.get(:user_id) == ["has already been taken"] + end +end diff --git a/spec/jam_ruby/models/music_session_spec.rb b/spec/jam_ruby/models/music_session_spec.rb index ae1450f90..14102445c 100644 --- a/spec/jam_ruby/models/music_session_spec.rb +++ b/spec/jam_ruby/models/music_session_spec.rb @@ -178,4 +178,15 @@ describe MusicSession do music_sessions[3].id.should == music_session2.id end + it 'uninvited users cant join approval-required sessions without invitation' do + user1 = FactoryGirl.create(:user) # in the jam session + user2 = FactoryGirl.create(:user) # in the jam session + + music_session = FactoryGirl.create(:music_session, :creator => user1, :musician_access => true, :approval_required => true) + + connection1 = FactoryGirl.create(:connection, :user => user1, :music_session => music_session) + expect { FactoryGirl.create(:connection, :user => user2, :music_session => music_session, :joining_session => true) }.to raise_error(ActiveRecord::RecordInvalid) + + end + end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 214236359..48c263bc6 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -46,7 +46,7 @@ Spork.prefork do config.filter_run :focus config.before(:suite) do - DatabaseCleaner.strategy = :transaction + DatabaseCleaner.strategy = :truncation, {:except => %w[instruments genres] } DatabaseCleaner.clean_with(:truncation, {:except => %w[instruments genres] }) end From e155fd3f1ef00c8934cc7b19c7d9ce23789c20c7 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Fri, 30 Nov 2012 10:14:22 -0600 Subject: [PATCH 096/274] * meant to remove limit_to_three_genres; left that in by mistake --- lib/jam_ruby/models/band.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/jam_ruby/models/band.rb b/lib/jam_ruby/models/band.rb index 091815192..7bce9f7f3 100644 --- a/lib/jam_ruby/models/band.rb +++ b/lib/jam_ruby/models/band.rb @@ -22,8 +22,6 @@ module JamRuby has_many :followers, :class_name => "JamRuby::BandFollower", :foreign_key => "band_id" has_many :inverse_followers, :through => :followers, :source => :user, :class_name => "JamRuby::Band", :foreign_key => "follower_id" - after_save :limit_to_three_genres - # music_sessions has_many :music_sessions, :class_name => "JamRuby::MusicSession", :foreign_key => "band_id" From 9371e7b0e81a33873ada6ba18d8c09cc3047cf31 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Sun, 2 Dec 2012 02:06:51 -0500 Subject: [PATCH 097/274] VRFS-117 - added recording genre support --- lib/jam_ruby/constants/limits.rb | 6 +++- lib/jam_ruby/models/band_invitation.rb | 4 ++- lib/jam_ruby/models/genre.rb | 3 ++ lib/jam_ruby/models/music_session.rb | 4 +-- lib/jam_ruby/models/recording.rb | 41 +++++++++++++++++++++++++- 5 files changed, 53 insertions(+), 5 deletions(-) diff --git a/lib/jam_ruby/constants/limits.rb b/lib/jam_ruby/constants/limits.rb index 63e731356..4a632b2b4 100644 --- a/lib/jam_ruby/constants/limits.rb +++ b/lib/jam_ruby/constants/limits.rb @@ -1,9 +1,13 @@ module Limits - # genres + # band genres MIN_GENRES_PER_BAND = 1 MAX_GENRES_PER_BAND = 3 + # recording genres + MIN_GENRES_PER_RECORDING = 1 + MAX_GENRES_PER_RECORDING = 3 + # instruments MIN_INSTRUMENTS_PER_MUSICIAN = 1 MAX_INSTRUMENTS_PER_MUSICIAN = 5 diff --git a/lib/jam_ruby/models/band_invitation.rb b/lib/jam_ruby/models/band_invitation.rb index 94a830fd3..a0251f3e4 100644 --- a/lib/jam_ruby/models/band_invitation.rb +++ b/lib/jam_ruby/models/band_invitation.rb @@ -5,6 +5,8 @@ module JamRuby self.primary_key = 'id' + BAND_INVITATION_FAN_RECIPIENT_ERROR = "A Band invitation can only be sent to a Musician." + belongs_to :receiver, :inverse_of => :received_band_invitations, :foreign_key => "user_id", :class_name => "JamRuby::User" belongs_to :sender, :inverse_of => :sent_band_invitations, :foreign_key => "creator_id", :class_name => "JamRuby::User" belongs_to :band, :inverse_of => :invitations, :foreign_key => "band_id", :class_name => "JamRuby::Band" @@ -16,7 +18,7 @@ module JamRuby # ensure recipient is a Musician user = User.find(user_id) unless user.musician? - raise JamRuby::JamArgumentError, "A Band invitation can only be sent to a Musician." + raise JamRuby::JamArgumentError, BAND_INVITATION_FAN_RECIPIENT_ERROR end band_invitation = BandInvitation.new() diff --git a/lib/jam_ruby/models/genre.rb b/lib/jam_ruby/models/genre.rb index 1649825e2..80fb3c1f8 100644 --- a/lib/jam_ruby/models/genre.rb +++ b/lib/jam_ruby/models/genre.rb @@ -6,6 +6,9 @@ module JamRuby # bands has_and_belongs_to_many :bands, :class_name => "JamRuby::Band", :join_table => "bands_genres" + # genres + has_and_belongs_to_many :recordings, :class_name => "JamRuby::Recording", :join_table => "recordings_genres" + # music sessions has_and_belongs_to_many :music_sessions, :class_name => "JamRuby::MusicSession", :join_table => "genres_music_sessions" diff --git a/lib/jam_ruby/models/music_session.rb b/lib/jam_ruby/models/music_session.rb index 2b9a96300..bb9a38203 100644 --- a/lib/jam_ruby/models/music_session.rb +++ b/lib/jam_ruby/models/music_session.rb @@ -91,13 +91,13 @@ module JamRuby private def require_at_least_one_genre - if genres.count < Limits::MIN_GENRES_PER_BAND + if genres.count < Limits::MIN_GENRES_PER_RECORDING errors.add(:genres, ValidationMessages::GENRE_MINIMUM_NOT_MET) end end def limit_to_three_genres - if genres.count > Limits::MAX_GENRES_PER_BAND + if genres.count > Limits::MAX_GENRES_PER_RECORDING errors.add(:genres, ValidationMessages::GENRE_LIMIT_EXCEEDED) end end diff --git a/lib/jam_ruby/models/recording.rb b/lib/jam_ruby/models/recording.rb index 1cf46ca63..a82f34d02 100644 --- a/lib/jam_ruby/models/recording.rb +++ b/lib/jam_ruby/models/recording.rb @@ -6,12 +6,15 @@ module JamRuby has_many :musician_recordings, :class_name => "JamRuby::MusicianRecording" has_many :band_recordings, :class_name => "JamRuby::BandRecording" + # genres + has_and_belongs_to_many :genres, :class_name => "JamRuby::Genre", :join_table => "recordings_genres" + # favorites has_and_belongs_to_many :user_favorites, :class_name => "JamRuby::UserFavorite", :join_table => "users_favorites" validates :description, presence: true, length: { maximum: 200 } - def self.save(id, is_public, description, updater_id, owner_id, is_band) + def self.save(id, is_public, description, genres, updater_id, owner_id, is_band) creator = User.find(updater_id) @@ -25,9 +28,11 @@ module JamRuby end if id.nil? + validate_genres(genres, false) recording = Recording.new() recording.creator_id = updater_id else + validate_genres(genres, true) recording = Recording.find(id) end @@ -43,6 +48,22 @@ module JamRuby recording.description = description end + # genres + unless genres.nil? + ActiveRecord::Base.transaction do + # delete all genres for this recording first + unless recording.id.nil? || recording.id.length == 0 + recording.genres.delete_all + end + + # loop through each genre in the array and save to the db + genres.each do |genre_id| + g = Genre.find(genre_id) + recording.genres << g + end + end + end + recording.updated_at = Time.now.getutc # TODO: wrap in transaction with statements below @@ -78,6 +99,24 @@ module JamRuby end end + def self.validate_genres(genres, is_nil_ok) + if is_nil_ok && genres.nil? + return + end + + if genres.nil? + raise JamRuby::JamArgumentError, ValidationMessages::GENRE_MINIMUM_NOT_MET + else + if genres.size < Limits::MIN_GENRES_PER_RECORDING + raise JamRuby::JamArgumentError, ValidationMessages::GENRE_MINIMUM_NOT_MET + end + + if genres.size > Limits::MAX_GENRES_PER_RECORDING + raise JamRuby::JamArgumentError, ValidationMessages::GENRE_LIMIT_EXCEEDED + end + end + end + =begin def self.delete(id, owner_id, is_band) if is_band? From 326b47b935ca2aa85d1f31c3cce8677dc55ebd77 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 2 Dec 2012 08:23:28 -0600 Subject: [PATCH 098/274] * pin version of factorygirl --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 3217a1a97..61e62db70 100644 --- a/Gemfile +++ b/Gemfile @@ -29,7 +29,7 @@ else end group :test do - gem "factory_girl" + gem "factory_girl", '4.1.0' gem "rspec", "2.10.0" gem 'spork', '0.9.0' gem 'database_cleaner', '0.7.0' From fce5f4316f924c4447de1c4e460a50bcbae2c1ee Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Mon, 3 Dec 2012 18:12:56 -0800 Subject: [PATCH 099/274] Added some narrower tests --- Gemfile | 2 +- spec/jam_ruby/models/music_session_spec.rb | 28 ++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 61e62db70..db874839e 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,6 @@ #ruby=1.9.3 source 'https://rubygems.org' -source 'https://jamjam:blueberryjam@www.jamkazam.com/gems/' +#source 'https://jamjam:blueberryjam@www.jamkazam.com/gems/' # Look for $WORKSPACE, otherwise use "workspace" as dev path. workspace = ENV["WORKSPACE"] || "~/workspace" diff --git a/spec/jam_ruby/models/music_session_spec.rb b/spec/jam_ruby/models/music_session_spec.rb index 14102445c..6745f1512 100644 --- a/spec/jam_ruby/models/music_session_spec.rb +++ b/spec/jam_ruby/models/music_session_spec.rb @@ -78,6 +78,34 @@ describe MusicSession do music_session.can_see?(user3).should == false end + + it "orders two sessions by created_at starting with most recent" do + creator = FactoryGirl.create(:user) + earlier_session = FactoryGirl.create(:music_session, :creator => creator, :description => "Earlier Session") + later_session = FactoryGirl.create(:music_session, :creator => creator, :description => "Later Session") + + user = FactoryGirl.create(:user) + + music_sessions = MusicSession.index(user) + music_sessions.length.should == 2 + music_sessions.first.id.should == later_session.id + end + + + it "orders sessions with friends first, even if created first" do + creator1 = FactoryGirl.create(:user) + creator2 = FactoryGirl.create(:user) + earlier_session = FactoryGirl.create(:music_session, :creator => creator1, :description => "Earlier Session") + later_session = FactoryGirl.create(:music_session, :creator => creator2, :description => "Later Session") + user = FactoryGirl.create(:user) + FactoryGirl.create(:friendship, :user => creator1, :friend => user) + FactoryGirl.create(:friendship, :user => user, :friend => creator1) + + music_sessions = MusicSession.index(user) + music_sessions.length.should == 2 + music_sessions.first.id.should == earlier_session.id + end + it 'can list sessions with appropriate sort order' do user1 = FactoryGirl.create(:user) From be5600e675440e2bf1ae279e4ab4373925ea9b16 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Mon, 3 Dec 2012 20:40:53 -0600 Subject: [PATCH 100/274] * temporarily disabling test proving something is wrong with find sessions --- spec/jam_ruby/models/music_session_spec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/jam_ruby/models/music_session_spec.rb b/spec/jam_ruby/models/music_session_spec.rb index 6745f1512..9ad81c861 100644 --- a/spec/jam_ruby/models/music_session_spec.rb +++ b/spec/jam_ruby/models/music_session_spec.rb @@ -93,6 +93,7 @@ describe MusicSession do it "orders sessions with friends first, even if created first" do + pending creator1 = FactoryGirl.create(:user) creator2 = FactoryGirl.create(:user) earlier_session = FactoryGirl.create(:music_session, :creator => creator1, :description => "Earlier Session") From 05d8278a05a5d6f929c7cc266e08d6e97971612a Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Mon, 3 Dec 2012 22:39:57 -0500 Subject: [PATCH 101/274] VRFS-102 - added models for musician/band likes --- lib/jam_ruby.rb | 3 ++ lib/jam_ruby/models/band.rb | 36 +++++++++++++---------- lib/jam_ruby/models/band_liker.rb | 11 ++++++++ lib/jam_ruby/models/recording.rb | 7 ++++- lib/jam_ruby/models/user.rb | 47 +++++++++++++++++++++++++------ lib/jam_ruby/models/user_like.rb | 10 +++++++ lib/jam_ruby/models/user_liker.rb | 10 +++++++ 7 files changed, 99 insertions(+), 25 deletions(-) create mode 100644 lib/jam_ruby/models/band_liker.rb create mode 100644 lib/jam_ruby/models/user_like.rb create mode 100644 lib/jam_ruby/models/user_liker.rb diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index 4369cf807..e8fd02ca9 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -29,6 +29,7 @@ require "jam_ruby/models/join_request" require "jam_ruby/models/band" require "jam_ruby/models/band_recording" require "jam_ruby/models/band_invitation" +require "jam_ruby/models/band_liker" require "jam_ruby/models/band_follower" require "jam_ruby/models/band_musician" require "jam_ruby/models/connection" @@ -40,6 +41,8 @@ require "jam_ruby/models/friend_request" require "jam_ruby/models/instrument" require "jam_ruby/models/musician_instrument" require "jam_ruby/models/connection_track" +require "jam_ruby/models/user_liker" +require "jam_ruby/models/user_like" require "jam_ruby/models/user_follower" require "jam_ruby/models/user_following" require "jam_ruby/models/user_favorite" diff --git a/lib/jam_ruby/models/band.rb b/lib/jam_ruby/models/band.rb index 7bce9f7f3..87b1afc66 100644 --- a/lib/jam_ruby/models/band.rb +++ b/lib/jam_ruby/models/band.rb @@ -16,32 +16,38 @@ module JamRuby # recordings has_many :band_recordings, :class_name => "JamRuby::BandRecording", :foreign_key => "band_id" - has_many :recordings, :through => :band_recordings, :class_name => "JamRuby::Recording" + has_many :recordings, :through => :band_recordings, :class_name => "JamRuby::Recording", :foreign_key => "recording_id" + + # likers + has_many :likers, :class_name => "JamRuby::BandLiker", :foreign_key => "band_id" + has_many :inverse_likers, :through => :likers, :class_name => "JamRuby::User", :foreign_key => "liker_id" # followers has_many :followers, :class_name => "JamRuby::BandFollower", :foreign_key => "band_id" - has_many :inverse_followers, :through => :followers, :source => :user, :class_name => "JamRuby::Band", :foreign_key => "follower_id" - - # music_sessions - has_many :music_sessions, :class_name => "JamRuby::MusicSession", :foreign_key => "band_id" - - def photo_url - # TODO: move image path to config - @photo_url = "http://www.jamkazam.com/images/bands/photos/#{self.id}.gif" - end - - def logo_url - # TODO: move image path to config - @logo_url = "http://www.jamkazam.com/images/bands/logos/#{self.id}.gif" - end + has_many :inverse_followers, :through => :followers, :class_name => "JamRuby::User", :foreign_key => "follower_id" # invitations has_many :invitations, :inverse_of => :band, :class_name => "JamRuby::BandInvitation", :foreign_key => "band_id" + # music_sessions + has_many :music_sessions, :class_name => "JamRuby::MusicSession", :foreign_key => "band_id" + + def liker_count + return self.likers.size + end + def follower_count return self.followers.size end + def recording_count + return self.recordings.size + end + + def session_count + return self.music_sessions.size + end + def location # TODO: implement a single string version of location; # this will be indexed into elasticsearch and returned in search diff --git a/lib/jam_ruby/models/band_liker.rb b/lib/jam_ruby/models/band_liker.rb new file mode 100644 index 000000000..ac23aa4b3 --- /dev/null +++ b/lib/jam_ruby/models/band_liker.rb @@ -0,0 +1,11 @@ +module JamRuby + class BandLiker < ActiveRecord::Base + + self.table_name = "bands_likers" + + self.primary_key = 'id' + + belongs_to :band, :class_name => "JamRuby::Band", :foreign_key => "band_id", :inverse_of => :inverse_band_likers + belongs_to :user, :class_name => "JamRuby::User", :foreign_key => "liker_id", :inverse_of => :band_likers + end +end \ No newline at end of file diff --git a/lib/jam_ruby/models/recording.rb b/lib/jam_ruby/models/recording.rb index a82f34d02..a850b879e 100644 --- a/lib/jam_ruby/models/recording.rb +++ b/lib/jam_ruby/models/recording.rb @@ -10,10 +10,15 @@ module JamRuby has_and_belongs_to_many :genres, :class_name => "JamRuby::Genre", :join_table => "recordings_genres" # favorites - has_and_belongs_to_many :user_favorites, :class_name => "JamRuby::UserFavorite", :join_table => "users_favorites" + has_many :favorites, :class_name => "JamRuby::UserFavorite", :foreign_key => "recording_id" + has_many :inverse_favorites, :through => :favorites, :source => :recording, :class_name => "JamRuby::Recording" validates :description, presence: true, length: { maximum: 200 } + def favorite_count + return self.favorites.size + end + def self.save(id, is_public, description, genres, updater_id, owner_id, is_band) creator = User.find(updater_id) diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index a23833464..7cffb60cb 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -30,19 +30,31 @@ module JamRuby # followers has_many :followers, :class_name => "JamRuby::UserFollower", :foreign_key => "user_id" - has_many :inverse_followers, :through => :followers, :source => :user, :class_name => "JamRuby::User", :foreign_key => "follower_id" + has_many :inverse_followers, :through => :followers, :class_name => "JamRuby::User", :foreign_key => "follower_id" + + # user likers (a musician has likers and may have likes too; fans do not have likers) + has_many :likers, :class_name => "JamRuby::UserLiker", :foreign_key => "user_id" + has_many :inverse_likers, :through => :likers, :class_name => "JamRuby::User", :foreign_key => "liker_id" # user followings has_many :followings, :class_name => "JamRuby::UserFollowing", :foreign_key => "follower_id" - has_many :inverse_followings, :through => :followings, :source => :user, :class_name => "JamRuby::User", :foreign_key => "user_id" + has_many :inverse_followings, :through => :followings, :class_name => "JamRuby::User", :foreign_key => "user_id" + + # user likes (fans and musicians have likes) + has_many :likes, :class_name => "JamRuby::UserLike", :foreign_key => "liker_id" + has_many :inverse_likes, :through => :followings, :class_name => "JamRuby::User", :foreign_key => "user_id" # band followings has_many :band_followings, :class_name => "JamRuby::BandFollower", :foreign_key => "follower_id" has_many :inverse_band_followings, :through => :band_followings, :class_name => "JamRuby::Band", :foreign_key => "band_id" + # band likes + has_many :band_likes, :class_name => "JamRuby::BandLike", :foreign_key => "liker_id" + has_many :inverse_band_likes, :through => :band_likes, :class_name => "JamRuby::Band", :foreign_key => "band_id" + # favorites has_many :favorites, :class_name => "JamRuby::UserFavorite", :foreign_key => "user_id" - has_many :inverse_favorites, :through => :favorites, :source => :user, :class_name => "JamRuby::User" + has_many :inverse_favorites, :through => :favorites, :class_name => "JamRuby::User" # friends has_many :friendships, :class_name => "JamRuby::Friendship", :foreign_key => "user_id" @@ -107,6 +119,10 @@ module JamRuby return self.friends.size end + def liker_count + return 0 + end + def follower_count return self.followers.size end @@ -114,15 +130,23 @@ module JamRuby def following_count return self.followings.size + self.band_followings.size end - - def confirm_email! - self.email_confirmed = true - end def favorite_count return self.favorites.size end + def recording_count + return self.recordings.size + end + + def session_count + return self.music_sessions.size + end + + def confirm_email! + self.email_confirmed = true + end + def to_s return email unless email.nil? @@ -247,8 +271,13 @@ module JamRuby follower.save end - def self.delete_user_following(user_id, follower_id) - JamRuby::UserFollower.delete_all "(user_id = '#{user_id}' AND follower_id = '#{follower_id}')" + def self.delete_following(user_id, band_id, follower_id) + if !user_id.nil? + JamRuby::UserFollower.delete_all "(user_id = '#{user_id}' AND follower_id = '#{follower_id}')" + + elsif !band_id.nil? + JamRuby::BandFollower.delete_all "(band_id = '#{band_id}' AND follower_id = '#{follower_id}')" + end end def self.create_band_following(band_id, follower_id) diff --git a/lib/jam_ruby/models/user_like.rb b/lib/jam_ruby/models/user_like.rb new file mode 100644 index 000000000..915ab4d87 --- /dev/null +++ b/lib/jam_ruby/models/user_like.rb @@ -0,0 +1,10 @@ +module JamRuby + class UserLike < ActiveRecord::Base + + self.table_name = "users_likers" + + self.primary_key = 'id' + + belongs_to :user, :class_name => "JamRuby::User", :foreign_key => "user_id", :inverse_of => :inverse_likes + end +end \ No newline at end of file diff --git a/lib/jam_ruby/models/user_liker.rb b/lib/jam_ruby/models/user_liker.rb new file mode 100644 index 000000000..07c2cbec7 --- /dev/null +++ b/lib/jam_ruby/models/user_liker.rb @@ -0,0 +1,10 @@ +module JamRuby + class UserLiker < ActiveRecord::Base + + self.table_name = "users_likers" + + self.primary_key = 'id' + + belongs_to :user, :class_name => "JamRuby::User", :foreign_key => "liker_id", :inverse_of => :inverse_likers + end +end \ No newline at end of file From e4e9ec293e0980623f88675b261e1883a1ee517b Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Mon, 3 Dec 2012 22:37:36 -0800 Subject: [PATCH 102/274] fixed query (i hope) and added some new tests --- lib/jam_ruby/models/music_session.rb | 33 +++++++++++++++++++++- spec/jam_ruby/models/music_session_spec.rb | 18 +++++++++++- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/lib/jam_ruby/models/music_session.rb b/lib/jam_ruby/models/music_session.rb index bb9a38203..4be25bd0d 100644 --- a/lib/jam_ruby/models/music_session.rb +++ b/lib/jam_ruby/models/music_session.rb @@ -35,7 +35,38 @@ module JamRuby end def self.index(current_user) - return MusicSession.includes(:invitations, :join_requests, :connections => [:user => [:friendships]]).order("(invitations.id is NULL) ASC, (friendships.friend_id != '#{current_user.id}' OR friendships.friend_id is NULL) ASC, music_sessions.created_at DESC").where("invitations.receiver_id = '#{current_user.id}' OR (friendships.friend_id = '#{current_user.id}') OR musician_access = true") + return MusicSession + .joins(:creator) + .joins( + %Q{ + LEFT OUTER JOIN + friendships + ON + users.id = friendships.user_id + AND + friendships.friend_id = '#{current_user.id}' + } + ) + .joins( + %Q{ + LEFT OUTER JOIN + invitations + ON + invitations.receiver_id = '#{current_user.id}' + } + ) + .order( + %Q{ + invitations.id is NULL ASC, + friendships.user_id IS NULL ASC, + music_sessions.created_at DESC + } + ) + .where( + %Q{ + musician_access = true + } + ) end # Verifies that the specified user can join this music session diff --git a/spec/jam_ruby/models/music_session_spec.rb b/spec/jam_ruby/models/music_session_spec.rb index 9ad81c861..43f1bde82 100644 --- a/spec/jam_ruby/models/music_session_spec.rb +++ b/spec/jam_ruby/models/music_session_spec.rb @@ -81,6 +81,7 @@ describe MusicSession do it "orders two sessions by created_at starting with most recent" do creator = FactoryGirl.create(:user) + earlier_session = FactoryGirl.create(:music_session, :creator => creator, :description => "Earlier Session") later_session = FactoryGirl.create(:music_session, :creator => creator, :description => "Later Session") @@ -91,9 +92,24 @@ describe MusicSession do music_sessions.first.id.should == later_session.id end + it "orders sessions with inviteds first, even if created first" do + creator = FactoryGirl.create(:user) + earlier_session = FactoryGirl.create(:music_session, :creator => creator, :description => "Earlier Session") + later_session = FactoryGirl.create(:music_session, :creator => creator, :description => "Later Session") + user = FactoryGirl.create(:user) + FactoryGirl.create(:connection, :user => creator, :music_session => earlier_session) + FactoryGirl.create(:friendship, :user => creator, :friend => user) + FactoryGirl.create(:friendship, :user => user, :friend => creator) + FactoryGirl.create(:invitation, :sender => creator, :receiver => user, :music_session => earlier_session) + + ActiveRecord::Base.logger = Logger.new(STDOUT) + music_sessions = MusicSession.index(user) + music_sessions.length.should == 2 + music_sessions.first.id.should == earlier_session.id + end + it "orders sessions with friends first, even if created first" do - pending creator1 = FactoryGirl.create(:user) creator2 = FactoryGirl.create(:user) earlier_session = FactoryGirl.create(:music_session, :creator => creator1, :description => "Earlier Session") From 95971fed667d41d4c23bdbce29baf15db7d6311d Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Mon, 3 Dec 2012 23:00:32 -0800 Subject: [PATCH 103/274] Another fix --- lib/jam_ruby/models/music_session.rb | 2 ++ spec/jam_ruby/models/music_session_spec.rb | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/jam_ruby/models/music_session.rb b/lib/jam_ruby/models/music_session.rb index 4be25bd0d..30e59f38b 100644 --- a/lib/jam_ruby/models/music_session.rb +++ b/lib/jam_ruby/models/music_session.rb @@ -52,6 +52,8 @@ module JamRuby LEFT OUTER JOIN invitations ON + invitations.music_session_id = music_sessions.id + AND invitations.receiver_id = '#{current_user.id}' } ) diff --git a/spec/jam_ruby/models/music_session_spec.rb b/spec/jam_ruby/models/music_session_spec.rb index 43f1bde82..a21fd6b58 100644 --- a/spec/jam_ruby/models/music_session_spec.rb +++ b/spec/jam_ruby/models/music_session_spec.rb @@ -102,7 +102,7 @@ describe MusicSession do FactoryGirl.create(:friendship, :user => user, :friend => creator) FactoryGirl.create(:invitation, :sender => creator, :receiver => user, :music_session => earlier_session) - ActiveRecord::Base.logger = Logger.new(STDOUT) +# ActiveRecord::Base.logger = Logger.new(STDOUT) music_sessions = MusicSession.index(user) music_sessions.length.should == 2 music_sessions.first.id.should == earlier_session.id @@ -123,6 +123,9 @@ describe MusicSession do music_sessions.first.id.should == earlier_session.id end +=begin + # mslemmer: + # I'm going to clean this up into smaller tasks. it 'can list sessions with appropriate sort order' do user1 = FactoryGirl.create(:user) @@ -222,6 +225,7 @@ describe MusicSession do music_sessions[2].id.should == music_session3.id music_sessions[3].id.should == music_session2.id end +=end it 'uninvited users cant join approval-required sessions without invitation' do user1 = FactoryGirl.create(:user) # in the jam session From 9fd6b45ab28997bdbf219a07f4ebb6a278745e12 Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Wed, 5 Dec 2012 11:10:58 -0800 Subject: [PATCH 104/274] work --- lib/jam_ruby/models/music_session.rb | 36 +++++++++++++++---- spec/factories.rb | 4 +++ spec/jam_ruby/models/music_session_spec.rb | 42 ++++++++++++++++++++-- 3 files changed, 73 insertions(+), 9 deletions(-) diff --git a/lib/jam_ruby/models/music_session.rb b/lib/jam_ruby/models/music_session.rb index 30e59f38b..7de2d59b8 100644 --- a/lib/jam_ruby/models/music_session.rb +++ b/lib/jam_ruby/models/music_session.rb @@ -34,15 +34,22 @@ module JamRuby end end - def self.index(current_user) - return MusicSession - .joins(:creator) + def self.index(current_user, genres = nil) + base_query = MusicSession + .joins( + %Q{ + LEFT OUTER JOIN + connections + ON + music_sessions.id = connections.music_session_id + } + ) .joins( %Q{ LEFT OUTER JOIN friendships ON - users.id = friendships.user_id + connections.user_id = friendships.user_id AND friendships.friend_id = '#{current_user.id}' } @@ -57,18 +64,33 @@ module JamRuby invitations.receiver_id = '#{current_user.id}' } ) + .group( + %Q{ + music_sessions.id + } + ) .order( %Q{ - invitations.id is NULL ASC, - friendships.user_id IS NULL ASC, + SUM(CASE WHEN invitations.id IS NULL THEN 0 ELSE 1 END) DESC, + SUM(CASE WHEN friendships.user_id IS NULL THEN 0 ELSE 1 END) DESC, music_sessions.created_at DESC } ) .where( %Q{ - musician_access = true + musician_access = true + OR + invitations.id IS NOT NULL } ) + + unless genres.nil? + base_query + .joins(:genres) + .where("genres.id" => genres) + end + + return base_query end # Verifies that the specified user can join this music session diff --git a/spec/factories.rb b/spec/factories.rb index d7a0e367d..eeaa2c484 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -44,6 +44,10 @@ FactoryGirl.define do state "NC" country "USA" end + + factory :genre, :class => JamRuby::Genre do + description { |n| "Genre #{n}" } + end factory :join_request, :class => JamRuby::JoinRequest do text 'let me in to the session!' diff --git a/spec/jam_ruby/models/music_session_spec.rb b/spec/jam_ruby/models/music_session_spec.rb index a21fd6b58..6279a2f97 100644 --- a/spec/jam_ruby/models/music_session_spec.rb +++ b/spec/jam_ruby/models/music_session_spec.rb @@ -102,14 +102,14 @@ describe MusicSession do FactoryGirl.create(:friendship, :user => user, :friend => creator) FactoryGirl.create(:invitation, :sender => creator, :receiver => user, :music_session => earlier_session) -# ActiveRecord::Base.logger = Logger.new(STDOUT) music_sessions = MusicSession.index(user) music_sessions.length.should == 2 music_sessions.first.id.should == earlier_session.id end - it "orders sessions with friends first, even if created first" do + it "orders sessions with friends in the session first, even if created first" do + creator1 = FactoryGirl.create(:user) creator2 = FactoryGirl.create(:user) earlier_session = FactoryGirl.create(:music_session, :creator => creator1, :description => "Earlier Session") @@ -117,12 +117,50 @@ describe MusicSession do user = FactoryGirl.create(:user) FactoryGirl.create(:friendship, :user => creator1, :friend => user) FactoryGirl.create(:friendship, :user => user, :friend => creator1) + FactoryGirl.create(:connection, :user => creator1, :music_session => earlier_session) + FactoryGirl.create(:connection, :user => creator2, :music_session => earlier_session) music_sessions = MusicSession.index(user) music_sessions.length.should == 2 music_sessions.first.id.should == earlier_session.id + end + + it "doesn't list a session if musician_access is set to false" do + creator = FactoryGirl.create(:user) + session = FactoryGirl.create(:music_session, :creator => creator, :description => "Session", :musician_access => false) + user = FactoryGirl.create(:user) + + music_sessions = MusicSession.index(user) + music_sessions.length.should == 0 end + it "does list a session if musician_access is set to false but user was invited" do + creator = FactoryGirl.create(:user) + session = FactoryGirl.create(:music_session, :creator => creator, :description => "Session", :musician_access => false) + user = FactoryGirl.create(:user) + FactoryGirl.create(:connection, :user => creator, :music_session => session) + FactoryGirl.create(:friendship, :user => creator, :friend => user) + FactoryGirl.create(:friendship, :user => user, :friend => creator) + FactoryGirl.create(:invitation, :sender => creator, :receiver => user, :music_session => session) + + music_sessions = MusicSession.index(user) + music_sessions.length.should == 1 + end + + it "lists a session if the genre matches" do + + creator = FactoryGirl.create(:user) + genre = FactoryGirl.create(:genre) + band = FactoryGirl.create(:band, :genres => [genre]) + session = FactoryGirl.create(:music_session, :creator => creator, :description => "Session", :band => band) + user = FactoryGirl.create(:user) + + ActiveRecord::Base.logger = Logger.new(STDOUT) + music_sessions = MusicSession.index(user, ["salami"]) + music_sessions.length.should == 1 + end + + =begin # mslemmer: # I'm going to clean this up into smaller tasks. From 5472324a1a2165727249cbc810bd2aa0dd16db03 Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Wed, 5 Dec 2012 17:56:12 -0800 Subject: [PATCH 105/274] Got friend and band index working --- lib/jam_ruby/models/music_session.rb | 38 +++++++++--- spec/factories.rb | 6 +- spec/jam_ruby/models/music_session_spec.rb | 72 ++++++++++++++++++++-- 3 files changed, 101 insertions(+), 15 deletions(-) diff --git a/lib/jam_ruby/models/music_session.rb b/lib/jam_ruby/models/music_session.rb index 7de2d59b8..4bd18fbbc 100644 --- a/lib/jam_ruby/models/music_session.rb +++ b/lib/jam_ruby/models/music_session.rb @@ -34,8 +34,11 @@ module JamRuby end end - def self.index(current_user, genres = nil) - base_query = MusicSession + # This is a little confusing. You can specify *BOTH* friends_only and my_bands_only to be true + # If so, then it's an OR condition. If both are false, you can get sessions with anyone. + def self.index(current_user, genres = nil, friends_only = false, my_bands_only = false) + + query = MusicSession .joins( %Q{ LEFT OUTER JOIN @@ -83,14 +86,31 @@ module JamRuby invitations.id IS NOT NULL } ) - - unless genres.nil? - base_query - .joins(:genres) - .where("genres.id" => genres) + + query = query.joins(:genres).where("genres.id" => genres) unless genres.nil? + + if my_bands_only + query = query.joins( + %Q{ + LEFT OUTER JOIN + bands_musicians + ON + bands_musicians.user_id = '#{current_user.id}' + } + ) end - - return base_query + + if my_bands_only || friends_only + query = query.where( + %Q{ + #{friends_only ? "friendships.user_id IS NOT NULL" : "false"} + OR + #{my_bands_only ? "bands_musicians.band_id = music_sessions.band_id" : "false"} + } + ) + end + + return query end # Verifies that the specified user can join this music session diff --git a/spec/factories.rb b/spec/factories.rb index eeaa2c484..6a2d3ca3d 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -37,6 +37,10 @@ FactoryGirl.define do end + factory :band_musician, :class => JamRuby::BandMusician do + + end + factory :band, :class => JamRuby::Band do sequence(:name) { |n| "Band" } biography "My Biography" @@ -52,4 +56,4 @@ FactoryGirl.define do factory :join_request, :class => JamRuby::JoinRequest do text 'let me in to the session!' end -end \ No newline at end of file +end diff --git a/spec/jam_ruby/models/music_session_spec.rb b/spec/jam_ruby/models/music_session_spec.rb index 6279a2f97..34c2a1499 100644 --- a/spec/jam_ruby/models/music_session_spec.rb +++ b/spec/jam_ruby/models/music_session_spec.rb @@ -87,6 +87,7 @@ describe MusicSession do user = FactoryGirl.create(:user) + #ActiveRecord::Base.logger = Logger.new(STDOUT) music_sessions = MusicSession.index(user) music_sessions.length.should == 2 music_sessions.first.id.should == later_session.id @@ -148,18 +149,79 @@ describe MusicSession do end it "lists a session if the genre matches" do - creator = FactoryGirl.create(:user) genre = FactoryGirl.create(:genre) - band = FactoryGirl.create(:band, :genres => [genre]) - session = FactoryGirl.create(:music_session, :creator => creator, :description => "Session", :band => band) + session = FactoryGirl.create(:music_session, :creator => creator, :description => "Session", :genres => [genre]) user = FactoryGirl.create(:user) - ActiveRecord::Base.logger = Logger.new(STDOUT) - music_sessions = MusicSession.index(user, ["salami"]) + music_sessions = MusicSession.index(user, [genre.id]) music_sessions.length.should == 1 end + it "does not list a session if the genre fails to match" do + creator = FactoryGirl.create(:user) + genre1 = FactoryGirl.create(:genre) + genre2 = FactoryGirl.create(:genre) + session = FactoryGirl.create(:music_session, :creator => creator, :description => "Session", :genres => [genre1]) + user = FactoryGirl.create(:user) + + music_sessions = MusicSession.index(user, [genre2.id]) + music_sessions.length.should == 0 + end + + it "does not list a session if friends_only is set and no friends are in it" do + creator = FactoryGirl.create(:user) + session = FactoryGirl.create(:music_session, :creator => creator, :description => "Session") + user = FactoryGirl.create(:user) + + music_sessions = MusicSession.index(user, nil, true) + music_sessions.length.should == 0 + end + + it "lists a session properly if a friend is in it" do + creator = FactoryGirl.create(:user) + session = FactoryGirl.create(:music_session, :creator => creator, :description => "Session") + user = FactoryGirl.create(:user) + FactoryGirl.create(:friendship, :user => creator, :friend => user) + FactoryGirl.create(:friendship, :user => user, :friend => creator) + FactoryGirl.create(:connection, :user => creator, :music_session => session) + + music_sessions = MusicSession.index(user, nil) + music_sessions.length.should == 1 + music_sessions = MusicSession.index(user, nil, true) + music_sessions.length.should == 1 + music_sessions = MusicSession.index(user, nil, false, true) + music_sessions.length.should == 0 + music_sessions = MusicSession.index(user, nil, true, true) + music_sessions.length.should == 1 + end + + it "does not list a session if my_bands_only is set and it's not my band" do + creator = FactoryGirl.create(:user) + session = FactoryGirl.create(:music_session, :creator => creator, :description => "Session") + user = FactoryGirl.create(:user) + + music_sessions = MusicSession.index(user, nil, false, true) + music_sessions.length.should == 0 + end + + it "lists a session properly if it's my band's session" do + band = FactoryGirl.create(:band) + creator = FactoryGirl.create(:user) + session = FactoryGirl.create(:music_session, :creator => creator, :description => "Session", :band => band) + user = FactoryGirl.create(:user) + FactoryGirl.create(:band_musician, :band => band, :user => creator) + FactoryGirl.create(:band_musician, :band => band, :user => user) + + music_sessions = MusicSession.index(user, nil) + music_sessions.length.should == 1 + music_sessions = MusicSession.index(user, nil, true) + music_sessions.length.should == 0 + music_sessions = MusicSession.index(user, nil, false, true) + music_sessions.length.should == 1 + music_sessions = MusicSession.index(user, nil, true, true) + music_sessions.length.should == 1 + end =begin # mslemmer: From 2da84f863b27cf28c8545eee4c3405b38994b2c8 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Thu, 6 Dec 2012 06:44:58 -0600 Subject: [PATCH 106/274] *default to p327 --- .rvmrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.rvmrc b/.rvmrc index 566deff03..aeb79c125 100644 --- a/.rvmrc +++ b/.rvmrc @@ -1 +1 @@ -rvm use ruby-1.9.3@jamruby --create +rvm use ruby-1.9.3-p327@jamruby --create From f3327d0014f649abaec225f42e284c4dc3f1c226 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Thu, 6 Dec 2012 20:54:01 -0600 Subject: [PATCH 107/274] * VRFS-134; default users to musicians for now --- lib/jam_ruby/models/user.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 7cffb60cb..3e2a9d29e 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -326,6 +326,7 @@ module JamRuby user.admin = false user.email_confirmed = false + user.musician = true user.city = city user.state = state user.country = country From 7a345f2163ca154874bf930fa731de1dd6675e31 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Sat, 8 Dec 2012 23:05:54 -0500 Subject: [PATCH 108/274] VRFS-33 added session settings --- lib/jam_ruby/models/user.rb | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 7cffb60cb..319cd5da5 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -82,7 +82,7 @@ module JamRuby has_secure_password before_save { |user| user.email = email.downcase } - before_save :create_remember_token + before_save :create_remember_token, :if => :should_validate_password? validates :first_name, presence: true, length: {maximum: 50} validates :last_name, presence: true, length: {maximum: 50} @@ -92,7 +92,6 @@ module JamRuby validates_length_of :password, minimum: 6, maximum: 100, :if => :should_validate_password? validates_presence_of :password_confirmation, :if => :should_validate_password? - #validates :password_confirmation, presence: true validates_confirmation_of :password, :if => :should_validate_password? def online @@ -144,7 +143,15 @@ module JamRuby end def confirm_email! - self.email_confirmed = true + self.email_confirmed = true + end + + def my_session_settings + unless self.session_settings.nil? + return JSON.parse(self.session_settings) + else + return "" + end end def to_s @@ -302,6 +309,23 @@ module JamRuby JamRuby::UserFavorite.delete_all "(user_id = '#{user_id}' AND recording_id = '#{recording_id}')" end + def self.save_session_settings(user, music_session) + unless user.nil? + + session_settings = { :band_id => music_session.band_id, + :musician_access => music_session.musician_access, + :approval_required => music_session.approval_required, + :fan_chat => music_session.fan_chat, + :fan_access => music_session.fan_access, + :description => music_session.description, + :genres => music_session.genres + }.to_json + + user.session_settings = session_settings + user.save + end + end + # throws ActiveRecord::RecordNotFound if instrument is invalid # throws an email delivery error if unable to connect out to SMTP def self.signup(first_name, last_name, email, password, password_confirmation, From af8bee608448259bf6d7aea1876805ad01b7092a Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 9 Dec 2012 14:56:35 -0600 Subject: [PATCH 109/274] * VRFS-149; bootstrap dev users --- lib/jam_ruby/models/user.rb | 61 +++++++++++++++++++++++++++++++ spec/jam_ruby/models/user_spec.rb | 23 ++++++++++++ 2 files changed, 84 insertions(+) diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 6dae3823a..c3a6388f2 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -389,6 +389,67 @@ module JamRuby return user end + # this is intended to be development-mode or test-mode only; VRFS-149 + # it creates or updates one user per developer, so that we aren't in the business + # of constantly recreating users as we create new dev environments + + # We guard against this code running in production mode, + # because otherwise it's a bit of uncomfortable code + # to have sitting around + def self.create_dev_user(first_name, last_name, email, password, + city, state, country, instruments, photo_url) + + if Environment.mode == "production" + # short-circuit out + return + end + + user = User.find_or_create_by_email(email) + + User.transaction do + user.first_name = first_name + user.last_name = last_name + user.email = email + user.password = password + user.password_confirmation = password + user.admin = true + user.email_confirmed = true + user.musician = true + user.city = city + user.state = state + user.country = country + + if instruments.nil? + instruments = [{:instrument_id => "acoustic guitar", :proficiency_level => 3, :priority => 1}] + end + + unless user.new_record? + MusicianInstrument.delete_all(["user_id = ?", user.id]) + end + + instruments.each do |musician_instrument_param| + instrument = Instrument.find(musician_instrument_param[:instrument_id]) + musician_instrument = MusicianInstrument.new + musician_instrument.user = user + musician_instrument.instrument = instrument + musician_instrument.proficiency_level = musician_instrument_param[:proficiency_level] + musician_instrument.priority = musician_instrument_param[:priority] + musician_instrument.save + user.musician_instruments << musician_instrument + end + + user.photo_url = photo_url + user.signup_token = nil + user.save + + if user.errors.any? + raise ActiveRecord::Rollback + end + end + + return user + end + # throws RecordNotFound if signup token is invalid; i.e., if it's nil, empty string, or not belonging to a user def self.signup_confirm(signup_token) if signup_token.nil? || signup_token.empty? diff --git a/spec/jam_ruby/models/user_spec.rb b/spec/jam_ruby/models/user_spec.rb index bb20bb641..f3d88acd7 100644 --- a/spec/jam_ruby/models/user_spec.rb +++ b/spec/jam_ruby/models/user_spec.rb @@ -169,4 +169,27 @@ describe User do it { User.authenticate("", "").should be_nil } end end + + describe "create_dev_user" do + before { @dev_user = User.create_dev_user("Seth", "Call", "seth@jamkazam.com", "Jam123", "Austin", "Texas", "USA", nil, nil) } + + subject { @dev_user } + + describe "creates a valid record" do + it { should be_valid } + end + + describe "should not be a new record" do + it { should be_persisted } + end + + describe "updates record" do + before { @dev_user = User.create_dev_user("Seth", "Call2", "seth@jamkazam.com", "Jam123", "Austin", "Texas", "USA", nil, nil) } + + it { should be_valid } + + its(:last_name) { should == "Call2" } + + end + end end \ No newline at end of file From 990e4c1a0d0b8fc5a43ea01b7f214361d42ef9ca Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 9 Dec 2012 16:11:01 -0600 Subject: [PATCH 110/274] * require 'legal_terms' boolean = true on create session --- lib/jam_ruby/models/music_session.rb | 2 ++ spec/factories.rb | 1 + spec/jam_ruby/models/music_session_spec.rb | 7 +++++++ 3 files changed, 10 insertions(+) diff --git a/lib/jam_ruby/models/music_session.rb b/lib/jam_ruby/models/music_session.rb index 4bd18fbbc..82502627f 100644 --- a/lib/jam_ruby/models/music_session.rb +++ b/lib/jam_ruby/models/music_session.rb @@ -3,6 +3,7 @@ module JamRuby self.primary_key = 'id' + attr_accessor :legal_terms attr_accessible :creator, :musician_access, :approval_required, :fan_chat, :fan_access belongs_to :creator, :inverse_of => :music_sessions, :class_name => "JamRuby::User", :foreign_key => "user_id" @@ -25,6 +26,7 @@ module JamRuby validates :fan_access, :inclusion => {:in => [true, false]} validates :approval_required, :inclusion => {:in => [true, false]} validates :musician_access, :inclusion => {:in => [true, false]} + validates :legal_terms, :inclusion => {:in => [true]} validates :creator, :presence => true validate :creator_is_musician diff --git a/spec/factories.rb b/spec/factories.rb index 6a2d3ca3d..4a1499fbd 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -22,6 +22,7 @@ FactoryGirl.define do fan_access true approval_required false musician_access true + legal_terms true end factory :connection, :class => JamRuby::Connection do diff --git a/spec/jam_ruby/models/music_session_spec.rb b/spec/jam_ruby/models/music_session_spec.rb index 34c2a1499..21ce78f98 100644 --- a/spec/jam_ruby/models/music_session_spec.rb +++ b/spec/jam_ruby/models/music_session_spec.rb @@ -338,4 +338,11 @@ describe MusicSession do end + it "must have legal_terms accepted" do + user1 = FactoryGirl.create(:user) + music_session = FactoryGirl.build(:music_session, :creator => user1, :legal_terms=> false) + music_session.save + music_session.valid?.should be_false + music_session.errors["legal_terms"].should == ["is not included in the list"] + end end From 5e87d00732d066d831ec7cf105d282b02d438a77 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Mon, 10 Dec 2012 00:53:21 -0500 Subject: [PATCH 111/274] VRFS-33 - save invitation receivers as part of session settings --- lib/jam_ruby/models/user.rb | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index c3a6388f2..8421873b9 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -312,13 +312,36 @@ module JamRuby def self.save_session_settings(user, music_session) unless user.nil? + # only save genre id and description + genres = [] + unless music_session.genres.nil? + music_session.genres.each do |genre| + g = Hash.new + g["id"] = genre.id + g["description"] = genre.description + genres << g + end + end + + # only save invitation receiver id and name + invitees = [] + unless music_session.invitations.nil? + music_session.invitations.each do |invitation| + i = Hash.new + i["id"] = invitation.receiver.id + i["name"] = invitation.receiver.name + invitees << i + end + end + session_settings = { :band_id => music_session.band_id, :musician_access => music_session.musician_access, :approval_required => music_session.approval_required, :fan_chat => music_session.fan_chat, :fan_access => music_session.fan_access, :description => music_session.description, - :genres => music_session.genres + :genres => genres, + :invitees => invitees }.to_json user.session_settings = session_settings From c068f2ca16f496985d32e8d51ad14c328f439643 Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Tue, 11 Dec 2012 14:59:37 -0800 Subject: [PATCH 112/274] work --- lib/jam_ruby/models/music_session.rb | 2 +- spec/jam_ruby/models/music_session_spec.rb | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/lib/jam_ruby/models/music_session.rb b/lib/jam_ruby/models/music_session.rb index 82502627f..f876f5c3a 100644 --- a/lib/jam_ruby/models/music_session.rb +++ b/lib/jam_ruby/models/music_session.rb @@ -4,7 +4,7 @@ module JamRuby self.primary_key = 'id' attr_accessor :legal_terms - attr_accessible :creator, :musician_access, :approval_required, :fan_chat, :fan_access + attr_accessible :creator, :description, :musician_access, :approval_required, :fan_chat, :fan_access belongs_to :creator, :inverse_of => :music_sessions, :class_name => "JamRuby::User", :foreign_key => "user_id" diff --git a/spec/jam_ruby/models/music_session_spec.rb b/spec/jam_ruby/models/music_session_spec.rb index 21ce78f98..a865124a8 100644 --- a/spec/jam_ruby/models/music_session_spec.rb +++ b/spec/jam_ruby/models/music_session_spec.rb @@ -223,6 +223,21 @@ describe MusicSession do music_sessions.length.should == 1 end + it "updates the fields of a music session properly" do + genre1 = FactoryGirl.create(:genre) + genre2 = FactoryGirl.create(:genre) + genre3 = FactoryGirl.create(:genre) + genre4 = FactoryGirl.create(:genre) + creator = FactoryGirl.create(:user) + session = FactoryGirl.create(:music_session, :creator => creator, :description => "Session", :genres => [genre3,genre4]) + session.update_attributes({:description => "Session2", :genre => [genre1, genre2]}) + session.genres = [genre1, genre2] + session.reload + session.description.should == "Session2" + session.genres.length.should == 2 + session.genres[0].id.should == genre1.id + end + =begin # mslemmer: # I'm going to clean this up into smaller tasks. From 7ff103a4307ec66295113533562bb8261870756f Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Thu, 13 Dec 2012 09:15:47 -0800 Subject: [PATCH 113/274] more work --- lib/jam_ruby/constants/validation_messages.rb | 3 +++ lib/jam_ruby/models/user.rb | 9 +++++++++ spec/jam_ruby/models/user_spec.rb | 11 +++++++++++ 3 files changed, 23 insertions(+) diff --git a/lib/jam_ruby/constants/validation_messages.rb b/lib/jam_ruby/constants/validation_messages.rb index a6e08575b..6a541c3db 100644 --- a/lib/jam_ruby/constants/validation_messages.rb +++ b/lib/jam_ruby/constants/validation_messages.rb @@ -19,4 +19,7 @@ module ValidationMessages INSTRUMENT_LIMIT_EXCEEDED = "No more than 5 instruments are allowed." INSTRUMENT_MINIMUM_NOT_MET = "At least 1 instrument is required." + # user + OLD_PASSWORD_DOESNT_MATCH = "Your old password is incorrect." + end \ No newline at end of file diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 8421873b9..0638f4697 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -164,6 +164,15 @@ module JamRuby return id end + def set_password(old_password, new_password) + raise JamRuby::JamArgumentError, ValidationMessages::OLD_PASSWORD_DOESNT_MATCH unless authenticate old_password + # FIXME: Should verify that the new password meets certain quality criteria + # FIXME: Need to run the new password through the validations here. Not sure if this does that. + password = new_password + save + end + + # helper method for creating / updating a User def self.save(id, updater_id, first_name, last_name, email, password, password_confirmation, musician, gender, birth_date, internet_service_provider, city, state, country, instruments, photo_url) diff --git a/spec/jam_ruby/models/user_spec.rb b/spec/jam_ruby/models/user_spec.rb index f3d88acd7..ed3937bc2 100644 --- a/spec/jam_ruby/models/user_spec.rb +++ b/spec/jam_ruby/models/user_spec.rb @@ -125,6 +125,17 @@ describe User do it { should be_invalid } end + describe "setting a new password should work" do + + end + describe "setting a new password should fail if old one doesnt match" do + + end + describe "setting a new password should fail if new one doesnt validate" do + + end + + describe "return value of authenticate method" do before { @user.save } let(:found_user) { User.find_by_email(@user.email) } From 0a4e41e5d121662cb189489672119961cdd8d27d Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Thu, 13 Dec 2012 19:32:23 -0800 Subject: [PATCH 114/274] set_password plus some refactoring --- lib/jam_ruby/models/user.rb | 11 ++++++----- spec/jam_ruby/models/user_spec.rb | 31 ++++++++++++++++++++++++------- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 0638f4697..8612d2109 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -164,11 +164,12 @@ module JamRuby return id end - def set_password(old_password, new_password) - raise JamRuby::JamArgumentError, ValidationMessages::OLD_PASSWORD_DOESNT_MATCH unless authenticate old_password - # FIXME: Should verify that the new password meets certain quality criteria - # FIXME: Need to run the new password through the validations here. Not sure if this does that. - password = new_password + def set_password(old_password, new_password, new_password_confirmation) + raise JamRuby::JamArgumentError unless authenticate old_password + # FIXME: Should verify that the new password meets certain quality criteria. Really, maybe that should be a + # verification step. + self.password = new_password + self.password_confirmation = new_password_confirmation save end diff --git a/spec/jam_ruby/models/user_spec.rb b/spec/jam_ruby/models/user_spec.rb index ed3937bc2..088854d87 100644 --- a/spec/jam_ruby/models/user_spec.rb +++ b/spec/jam_ruby/models/user_spec.rb @@ -125,15 +125,32 @@ describe User do it { should be_invalid } end - describe "setting a new password should work" do - - end - describe "setting a new password should fail if old one doesnt match" do - - end - describe "setting a new password should fail if new one doesnt validate" do + describe "set_password" do + before do + @user.confirm_email! + end + + it "setting a new password should work" do + @user.set_password("foobar", "newpassword", "newpassword") + User.authenticate(@user.email, "newpassword").should_not be_nil + end + + it "setting a new password should fail if old one doesnt match" do + @user.set_password("wrongold", "newpassword", "newpassword").should_raise # This doesnt work. + end + + it "setting a new password should fail if new ones dont match" do + @user.set_password("foobar", "newpassword", "newpassword2") + User.authenticate(@user.email, "newpassword").should be_nil + end + + it "setting a new password should fail if new one doesnt validate" do + @user.set_password("foobar", "a", "a") + User.authenticate(@user.email, "newpassword").should be_nil + end end + describe "return value of authenticate method" do From 81ebd4aa99af30fb0b8c252fd5d5cf6e91fe0c51 Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Fri, 14 Dec 2012 01:07:14 -0800 Subject: [PATCH 115/274] fixed test --- spec/jam_ruby/models/user_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/jam_ruby/models/user_spec.rb b/spec/jam_ruby/models/user_spec.rb index 088854d87..72b6bd9eb 100644 --- a/spec/jam_ruby/models/user_spec.rb +++ b/spec/jam_ruby/models/user_spec.rb @@ -136,7 +136,7 @@ describe User do end it "setting a new password should fail if old one doesnt match" do - @user.set_password("wrongold", "newpassword", "newpassword").should_raise # This doesnt work. + expect { @user.set_password("wrongold", "newpassword", "newpassword") }.to raise_error end it "setting a new password should fail if new ones dont match" do From 59e6d82ef3112c7e7053d3f5054d2ad37cd91cd6 Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Fri, 14 Dec 2012 01:16:54 -0800 Subject: [PATCH 116/274] send email --- lib/jam_ruby/app/mailers/user_mailer.rb | 12 +++++++++++- lib/jam_ruby/models/user.rb | 2 ++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/jam_ruby/app/mailers/user_mailer.rb b/lib/jam_ruby/app/mailers/user_mailer.rb index 517336c37..1798d37b6 100644 --- a/lib/jam_ruby/app/mailers/user_mailer.rb +++ b/lib/jam_ruby/app/mailers/user_mailer.rb @@ -37,5 +37,15 @@ module JamRuby format.html end end -end + + + def password_changed(user) + @user = user + sendgrid_unique_args :type => "passsword_changed" + mail(:to => user.email, :subject => "Jamkazam Password Changed") do |format| + format.text + format.html + end + end + end end \ No newline at end of file diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 8612d2109..fbdddad34 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -171,6 +171,8 @@ module JamRuby self.password = new_password self.password_confirmation = new_password_confirmation save + + UserMailer.password_changed(self).deliver end From a32f0d240bf17e3881a808c7b5ab6b117425d459 Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Fri, 14 Dec 2012 12:22:15 -0800 Subject: [PATCH 117/274] add email templates --- lib/jam_ruby/app/mailers/user_mailer.rb | 5 ++--- .../app/views/jam_ruby/user_mailer/password_changed.html.erb | 5 +++++ .../app/views/jam_ruby/user_mailer/password_changed.txt.erb | 1 + 3 files changed, 8 insertions(+), 3 deletions(-) create mode 100644 lib/jam_ruby/app/views/jam_ruby/user_mailer/password_changed.html.erb create mode 100644 lib/jam_ruby/app/views/jam_ruby/user_mailer/password_changed.txt.erb diff --git a/lib/jam_ruby/app/mailers/user_mailer.rb b/lib/jam_ruby/app/mailers/user_mailer.rb index 1798d37b6..c231827c6 100644 --- a/lib/jam_ruby/app/mailers/user_mailer.rb +++ b/lib/jam_ruby/app/mailers/user_mailer.rb @@ -38,14 +38,13 @@ module JamRuby end end - def password_changed(user) @user = user - sendgrid_unique_args :type => "passsword_changed" + sendgrid_unique_args :type => "password_changed" mail(:to => user.email, :subject => "Jamkazam Password Changed") do |format| format.text format.html end end end -end \ No newline at end of file +end diff --git a/lib/jam_ruby/app/views/jam_ruby/user_mailer/password_changed.html.erb b/lib/jam_ruby/app/views/jam_ruby/user_mailer/password_changed.html.erb new file mode 100644 index 000000000..f9a712566 --- /dev/null +++ b/lib/jam_ruby/app/views/jam_ruby/user_mailer/password_changed.html.erb @@ -0,0 +1,5 @@ + + +You Just Changed Your Password! <%= @user.email %> + + diff --git a/lib/jam_ruby/app/views/jam_ruby/user_mailer/password_changed.txt.erb b/lib/jam_ruby/app/views/jam_ruby/user_mailer/password_changed.txt.erb new file mode 100644 index 000000000..125193f24 --- /dev/null +++ b/lib/jam_ruby/app/views/jam_ruby/user_mailer/password_changed.txt.erb @@ -0,0 +1 @@ +You Just Changed Your Password! <%= @user.email %> From de941eaef1bfcdf46fdee69b5a0f34dc0596f62f Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Fri, 14 Dec 2012 21:51:59 -0800 Subject: [PATCH 118/274] duh --- .../{password_changed.txt.erb => password_changed.text.erb} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename lib/jam_ruby/app/views/jam_ruby/user_mailer/{password_changed.txt.erb => password_changed.text.erb} (100%) diff --git a/lib/jam_ruby/app/views/jam_ruby/user_mailer/password_changed.txt.erb b/lib/jam_ruby/app/views/jam_ruby/user_mailer/password_changed.text.erb similarity index 100% rename from lib/jam_ruby/app/views/jam_ruby/user_mailer/password_changed.txt.erb rename to lib/jam_ruby/app/views/jam_ruby/user_mailer/password_changed.text.erb From 97dd557854f48148ed7b76f76ccc2cab9aa0ef66 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Sat, 15 Dec 2012 02:11:04 -0500 Subject: [PATCH 119/274] VRFS-157 track API development --- lib/jam_ruby.rb | 2 +- lib/jam_ruby/connection_manager.rb | 28 +++++----- lib/jam_ruby/constants/validation_messages.rb | 3 ++ lib/jam_ruby/models/connection.rb | 4 +- lib/jam_ruby/models/instrument.rb | 2 +- lib/jam_ruby/models/track.rb | 54 +++++++++++++++++++ 6 files changed, 74 insertions(+), 19 deletions(-) create mode 100644 lib/jam_ruby/models/track.rb diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index e8fd02ca9..4b0c690ec 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -40,7 +40,7 @@ require "jam_ruby/models/fan_invitation" require "jam_ruby/models/friend_request" require "jam_ruby/models/instrument" require "jam_ruby/models/musician_instrument" -require "jam_ruby/models/connection_track" +require "jam_ruby/models/track" require "jam_ruby/models/user_liker" require "jam_ruby/models/user_like" require "jam_ruby/models/user_follower" diff --git a/lib/jam_ruby/connection_manager.rb b/lib/jam_ruby/connection_manager.rb index fee8c9c65..08831fb1e 100644 --- a/lib/jam_ruby/connection_manager.rb +++ b/lib/jam_ruby/connection_manager.rb @@ -287,22 +287,20 @@ module JamRuby return friend_ids end - def associate_tracks(connection, tracks) - @log.debug "Tracks:" - @log.debug tracks - unless tracks.nil? - tracks.each do |track| - instrument = Instrument.find(track["instrument_id"]) - connection_track = ConnectionTrack.new - connection_track.instrument = instrument - connection_track.connection = connection - connection_track.sound = track["sound"] - connection_track.save - connection.connection_tracks << connection_track + def associate_tracks(connection, tracks) + @log.debug "Tracks:" + @log.debug tracks + unless tracks.nil? + tracks.each do |track| + instrument = Instrument.find(track["instrument_id"]) + t = Track.new + t.instrument = instrument + t.connection = connection + t.sound = track["sound"] + t.save + connection.tracks << t + end end end end - - - end end \ No newline at end of file diff --git a/lib/jam_ruby/constants/validation_messages.rb b/lib/jam_ruby/constants/validation_messages.rb index a6e08575b..fd46a2fb9 100644 --- a/lib/jam_ruby/constants/validation_messages.rb +++ b/lib/jam_ruby/constants/validation_messages.rb @@ -11,6 +11,9 @@ module ValidationMessages # recordings RECORDING_NOT_FOUND = "Recording not found." + # tracks + TRACK_NOT_FOUND = "Track not found." + # genres GENRE_LIMIT_EXCEEDED = "No more than 3 genres are allowed." GENRE_MINIMUM_NOT_MET = "At least 1 genre is required." diff --git a/lib/jam_ruby/models/connection.rb b/lib/jam_ruby/models/connection.rb index 1152aeee7..8f3a1e3f0 100644 --- a/lib/jam_ruby/models/connection.rb +++ b/lib/jam_ruby/models/connection.rb @@ -13,7 +13,7 @@ module JamRuby belongs_to :user, :class_name => "JamRuby::User" belongs_to :music_session, :class_name => "JamRuby::MusicSession" - has_many :connection_tracks, :class_name => "JamRuby::ConnectionTrack", :inverse_of => :connection + has_many :tracks, :class_name => "JamRuby::Track", :inverse_of => :connection validates :as_musician, :inclusion => {:in => [true, false]} @@ -70,7 +70,7 @@ module JamRuby private def require_at_least_one_track_when_in_session - if connection_tracks.count == 0 + if tracks.count == 0 errors.add(:genres, SELECT_AT_LEAST_ONE) end end diff --git a/lib/jam_ruby/models/instrument.rb b/lib/jam_ruby/models/instrument.rb index e0563c454..12f56a722 100644 --- a/lib/jam_ruby/models/instrument.rb +++ b/lib/jam_ruby/models/instrument.rb @@ -6,7 +6,7 @@ module JamRuby # users has_many :musician_instruments, :class_name => "JamRuby::MusicianInstrument" has_many :users, :through => :musician_instruments, :class_name => "JamRuby::User" - has_many :connection_tracks, :class_name => "JamRuby::ConnectionTrack", :inverse_of => :instrument + has_many :tracks, :class_name => "JamRuby::Track", :inverse_of => :instrument # music sessions has_and_belongs_to_many :music_sessions, :class_name => "JamRuby::MusicSession", :join_table => "genres_music_sessions" diff --git a/lib/jam_ruby/models/track.rb b/lib/jam_ruby/models/track.rb new file mode 100644 index 000000000..0ee310616 --- /dev/null +++ b/lib/jam_ruby/models/track.rb @@ -0,0 +1,54 @@ +module JamRuby + class Track < ActiveRecord::Base + + self.table_name = "tracks" + + self.primary_key = 'id' + + SOUND = %w(mono stereo) + + belongs_to :connection, :class_name => "JamRuby::Connection", :inverse_of => :tracks + belongs_to :instrument, :class_name => "JamRuby::Instrument", :inverse_of => :tracks + + validates :sound, :inclusion => {:in => SOUND} + + def self.index(current_user, music_session_id) + query = Track + .joins( + %Q{ + INNER JOIN + connections + ON + connections.music_session_id = '#{music_session_id}' + AND + connections.id = connection_id + AND + connections.user_id = '#{current_user.id}' + } + ) + + return query + end + + def self.save(id, connection_id, instrument_id, sound) + if id.nil? + track = Track.new() + track.connection_id = connection_id + else + track = Track.find(id) + end + + unless instrument_id.nil? + track.instrument_id = instrument_id + end + + unless sound.nil? + track.sound = sound + end + + track.updated_at = Time.now.getutc + track.save + return track + end + end +end \ No newline at end of file From 5e0004f854d52ed56e1cf1776ec64bf752bbb9fe Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Sun, 16 Dec 2012 08:28:07 -0500 Subject: [PATCH 120/274] removed connection_track model --- lib/jam_ruby/models/connection_track.rb | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 lib/jam_ruby/models/connection_track.rb diff --git a/lib/jam_ruby/models/connection_track.rb b/lib/jam_ruby/models/connection_track.rb deleted file mode 100644 index 303d1534c..000000000 --- a/lib/jam_ruby/models/connection_track.rb +++ /dev/null @@ -1,15 +0,0 @@ -module JamRuby - class ConnectionTrack < ActiveRecord::Base - - self.table_name = "connections_tracks" - - self.primary_key = 'id' - - SOUND = %w(mono stereo) - - belongs_to :connection, :class_name => "JamRuby::Connection", :inverse_of => :connection_tracks - belongs_to :instrument, :class_name => "JamRuby::Instrument", :inverse_of => :connection_tracks - - validates :sound, :inclusion => {:in => SOUND} - end -end \ No newline at end of file From 37d24a0c0e1aa6792281dcb77c7f93c48b4b8f4a Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Sun, 16 Dec 2012 18:24:14 -0500 Subject: [PATCH 121/274] VRFS-120 - Likes module --- lib/jam_ruby/constants/validation_messages.rb | 3 +++ lib/jam_ruby/models/track.rb | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/lib/jam_ruby/constants/validation_messages.rb b/lib/jam_ruby/constants/validation_messages.rb index fd46a2fb9..1fccad957 100644 --- a/lib/jam_ruby/constants/validation_messages.rb +++ b/lib/jam_ruby/constants/validation_messages.rb @@ -14,6 +14,9 @@ module ValidationMessages # tracks TRACK_NOT_FOUND = "Track not found." + # sessions + SESSION_NOT_FOUND = "Session not found." + # genres GENRE_LIMIT_EXCEEDED = "No more than 3 genres are allowed." GENRE_MINIMUM_NOT_MET = "At least 1 genre is required." diff --git a/lib/jam_ruby/models/track.rb b/lib/jam_ruby/models/track.rb index 0ee310616..d39323bdc 100644 --- a/lib/jam_ruby/models/track.rb +++ b/lib/jam_ruby/models/track.rb @@ -24,6 +24,12 @@ module JamRuby connections.id = connection_id AND connections.user_id = '#{current_user.id}' + INNER JOIN + music_sessions + ON + music_sessions.id = '#{music_session_id}' + AND + music_sessions.user_id = '#{current_user.id}' } ) From 45600115f7bc76b85f5273aaed50a5f9d07ef075 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Mon, 17 Dec 2012 01:01:48 -0500 Subject: [PATCH 122/274] VRFS-120 - completed Likes for Users and Bands --- lib/jam_ruby/models/band.rb | 4 +- lib/jam_ruby/models/band_follower.rb | 4 +- lib/jam_ruby/models/band_liker.rb | 4 +- lib/jam_ruby/models/user.rb | 57 ++++++++++++++++++++-------- 4 files changed, 48 insertions(+), 21 deletions(-) diff --git a/lib/jam_ruby/models/band.rb b/lib/jam_ruby/models/band.rb index 87b1afc66..034ee4317 100644 --- a/lib/jam_ruby/models/band.rb +++ b/lib/jam_ruby/models/band.rb @@ -19,11 +19,11 @@ module JamRuby has_many :recordings, :through => :band_recordings, :class_name => "JamRuby::Recording", :foreign_key => "recording_id" # likers - has_many :likers, :class_name => "JamRuby::BandLiker", :foreign_key => "band_id" + has_many :likers, :class_name => "JamRuby::BandLiker", :foreign_key => "band_id", :inverse_of => :band has_many :inverse_likers, :through => :likers, :class_name => "JamRuby::User", :foreign_key => "liker_id" # followers - has_many :followers, :class_name => "JamRuby::BandFollower", :foreign_key => "band_id" + has_many :followers, :class_name => "JamRuby::BandFollower", :foreign_key => "band_id", :inverse_of => :band has_many :inverse_followers, :through => :followers, :class_name => "JamRuby::User", :foreign_key => "follower_id" # invitations diff --git a/lib/jam_ruby/models/band_follower.rb b/lib/jam_ruby/models/band_follower.rb index ba2e43845..8c8ef6654 100644 --- a/lib/jam_ruby/models/band_follower.rb +++ b/lib/jam_ruby/models/band_follower.rb @@ -5,7 +5,7 @@ module JamRuby self.primary_key = 'id' - belongs_to :band, :class_name => "JamRuby::Band", :foreign_key => "band_id" - belongs_to :user, :class_name => "JamRuby::User", :foreign_key => "follower_id", :inverse_of => :inverse_followers + belongs_to :band, :class_name => "JamRuby::Band", :foreign_key => "band_id", :inverse_of => :followers + belongs_to :user, :class_name => "JamRuby::User", :foreign_key => "follower_id", :inverse_of => :band_followings end end \ No newline at end of file diff --git a/lib/jam_ruby/models/band_liker.rb b/lib/jam_ruby/models/band_liker.rb index ac23aa4b3..5926d8b6c 100644 --- a/lib/jam_ruby/models/band_liker.rb +++ b/lib/jam_ruby/models/band_liker.rb @@ -5,7 +5,7 @@ module JamRuby self.primary_key = 'id' - belongs_to :band, :class_name => "JamRuby::Band", :foreign_key => "band_id", :inverse_of => :inverse_band_likers - belongs_to :user, :class_name => "JamRuby::User", :foreign_key => "liker_id", :inverse_of => :band_likers + belongs_to :band, :class_name => "JamRuby::Band", :foreign_key => "band_id", :inverse_of => :likers + belongs_to :user, :class_name => "JamRuby::User", :foreign_key => "liker_id", :inverse_of => :band_likes end end \ No newline at end of file diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index fbdddad34..191b3cdae 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -28,30 +28,30 @@ module JamRuby has_many :musician_recordings, :class_name => "JamRuby::MusicianRecording", :foreign_key => "user_id" has_many :recordings, :through => :musician_recordings, :class_name => "JamRuby::Recording" - # followers - has_many :followers, :class_name => "JamRuby::UserFollower", :foreign_key => "user_id" - has_many :inverse_followers, :through => :followers, :class_name => "JamRuby::User", :foreign_key => "follower_id" - # user likers (a musician has likers and may have likes too; fans do not have likers) - has_many :likers, :class_name => "JamRuby::UserLiker", :foreign_key => "user_id" + has_many :likers, :class_name => "JamRuby::UserLiker", :foreign_key => "user_id", :inverse_of => :user has_many :inverse_likers, :through => :likers, :class_name => "JamRuby::User", :foreign_key => "liker_id" - # user followings - has_many :followings, :class_name => "JamRuby::UserFollowing", :foreign_key => "follower_id" - has_many :inverse_followings, :through => :followings, :class_name => "JamRuby::User", :foreign_key => "user_id" - # user likes (fans and musicians have likes) - has_many :likes, :class_name => "JamRuby::UserLike", :foreign_key => "liker_id" + has_many :likes, :class_name => "JamRuby::UserLike", :foreign_key => "liker_id", :inverse_of => :user has_many :inverse_likes, :through => :followings, :class_name => "JamRuby::User", :foreign_key => "user_id" - # band followings - has_many :band_followings, :class_name => "JamRuby::BandFollower", :foreign_key => "follower_id" - has_many :inverse_band_followings, :through => :band_followings, :class_name => "JamRuby::Band", :foreign_key => "band_id" - # band likes - has_many :band_likes, :class_name => "JamRuby::BandLike", :foreign_key => "liker_id" + has_many :band_likes, :class_name => "JamRuby::BandLiker", :foreign_key => "liker_id", :inverse_of => :user has_many :inverse_band_likes, :through => :band_likes, :class_name => "JamRuby::Band", :foreign_key => "band_id" + # followers + has_many :followers, :class_name => "JamRuby::UserFollower", :foreign_key => "user_id", :inverse_of => :user + has_many :inverse_followers, :through => :followers, :class_name => "JamRuby::User", :foreign_key => "follower_id" + + # user followings + has_many :followings, :class_name => "JamRuby::UserFollowing", :foreign_key => "follower_id", :inverse_of => :user + has_many :inverse_followings, :through => :followings, :class_name => "JamRuby::User", :foreign_key => "user_id" + + # band followings + has_many :band_followings, :class_name => "JamRuby::BandFollower", :foreign_key => "follower_id", :inverse_of => :user + has_many :inverse_band_followings, :through => :band_followings, :class_name => "JamRuby::Band", :foreign_key => "band_id" + # favorites has_many :favorites, :class_name => "JamRuby::UserFavorite", :foreign_key => "user_id" has_many :inverse_favorites, :through => :favorites, :class_name => "JamRuby::User" @@ -283,6 +283,33 @@ module JamRuby return user end + def self.create_user_like(user_id, liker_id) + liker = UserLiker.new() + liker.user_id = user_id + liker.liker_id = liker_id + liker.save + end + + def self.delete_like(user_id, band_id, liker_id) + if !user_id.nil? + JamRuby::UserLiker.delete_all "(user_id = '#{user_id}' AND liker_id = '#{liker_id}')" + + elsif !band_id.nil? + JamRuby::BandLiker.delete_all "(band_id = '#{band_id}' AND liker_id = '#{liker_id}')" + end + end + + def self.create_band_like(band_id, liker_id) + liker = BandLiker.new() + liker.band_id = band_id + liker.liker_id = liker_id + liker.save + end + + def self.delete_band_like(band_id, liker_id) + JamRuby::BandLiker.delete_all "(band_id = '#{band_id}' AND liker_id = '#{liker_id}')" + end + def self.create_user_following(user_id, follower_id) follower = UserFollower.new() follower.user_id = user_id From db2f16c471899e1c770d773d258338be64bd36cb Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Mon, 17 Dec 2012 02:02:20 -0500 Subject: [PATCH 123/274] refactor --- lib/jam_ruby/models/band.rb | 21 +++++++++++++++++++ lib/jam_ruby/models/user.rb | 42 +++++++++++++++++++++++++++++++++++-- 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/lib/jam_ruby/models/band.rb b/lib/jam_ruby/models/band.rb index 034ee4317..953aa5851 100644 --- a/lib/jam_ruby/models/band.rb +++ b/lib/jam_ruby/models/band.rb @@ -57,6 +57,27 @@ module JamRuby def add_member(user_id, admin) BandMusician.create(:band_id => self.id, :user_id => user_id, :admin => admin) end + + def self.recording_index(current_user, band_id) + hide_private = false + band = Band.find(band_id) + + # hide private Recordings from anyone who's not in the Band + unless band.users.exists? current_user + hide_private = true + end + + if hide_private + recordings = Recording.joins(:band_recordings) + .where(:bands_recordings => {:band_id => "#{band_id}"}, :public => true) + + else + recordings = Recording.joins(:band_recordings) + .where(:bands_recordings => {:band_id => "#{band_id}"}) + end + + return recordings + end # helper method for creating / updating a Band def self.save(id, name, website, biography, city, state, country, genres, user_id, photo_url, logo_url) diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 191b3cdae..cb9e73019 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -119,7 +119,15 @@ module JamRuby end def liker_count - return 0 + return self.likers.size + end + + def like_count + return self.likes.size + end + + def band_like_count + return self.band_likes.size end def follower_count @@ -127,7 +135,11 @@ module JamRuby end def following_count - return self.followings.size + self.band_followings.size + return self.followings.size + end + + def band_following_count + return self.band_followings.size end def favorite_count @@ -175,6 +187,32 @@ module JamRuby UserMailer.password_changed(self).deliver end + def self.band_index(user_id) + bands = Band.joins(:band_musicians) + .where(:bands_musicians => {:user_id => "#{user_id}"}) + + return bands + end + + def self.recording_index(current_user, user_id) + hide_private = false + + # hide private recordings from anyone but the current user + if current_user.id != user_id + hide_private = true + end + + if hide_private + recordings = Recording.joins(:musician_recordings) + .where(:musicians_recordings => {:user_id => "#{user_id}"}, :public => true) + + else + recordings = Recording.joins(:musician_recordings) + .where(:musicians_recordings => {:user_id => "#{user_id}"}) + end + + return recordings + end # helper method for creating / updating a User def self.save(id, updater_id, first_name, last_name, email, password, password_confirmation, musician, gender, From a5f6bc725a040f415cf93371e0fe62fb0360a81b Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Fri, 21 Dec 2012 16:56:16 -0800 Subject: [PATCH 124/274] reset password work --- lib/jam_ruby/app/mailers/user_mailer.rb | 18 +++++----- .../user_mailer/password_reset.html.erb | 7 ++++ .../user_mailer/password_reset.text.erb | 2 ++ .../user_mailer/reset_password.html.erb | 5 --- .../user_mailer/reset_password.text.erb | 1 - lib/jam_ruby/constants/validation_messages.rb | 5 ++- lib/jam_ruby/models/user.rb | 30 +++++++++++++++- spec/jam_ruby/models/user_spec.rb | 34 ++++++++++++++++++- 8 files changed, 84 insertions(+), 18 deletions(-) create mode 100644 lib/jam_ruby/app/views/jam_ruby/user_mailer/password_reset.html.erb create mode 100644 lib/jam_ruby/app/views/jam_ruby/user_mailer/password_reset.text.erb delete mode 100644 lib/jam_ruby/app/views/jam_ruby/user_mailer/reset_password.html.erb delete mode 100644 lib/jam_ruby/app/views/jam_ruby/user_mailer/reset_password.text.erb diff --git a/lib/jam_ruby/app/mailers/user_mailer.rb b/lib/jam_ruby/app/mailers/user_mailer.rb index c231827c6..8be2e6c0f 100644 --- a/lib/jam_ruby/app/mailers/user_mailer.rb +++ b/lib/jam_ruby/app/mailers/user_mailer.rb @@ -29,15 +29,6 @@ module JamRuby end end - def reset_password(user) - @user = user - sendgrid_unique_args :type => "reset_password" - mail(:to => user.email, :subject => "Jamkazam Reset Password") do |format| - format.text - format.html - end - end - def password_changed(user) @user = user sendgrid_unique_args :type => "password_changed" @@ -46,5 +37,14 @@ module JamRuby format.html end end + + def password_reset(user) + @user = user + sendgrid_unique_args :type => "password_reset" + mail(:to => user.email, :subject => "Jamkazam Password Reset") do |format| + format.text + format.html + end + end end end diff --git a/lib/jam_ruby/app/views/jam_ruby/user_mailer/password_reset.html.erb b/lib/jam_ruby/app/views/jam_ruby/user_mailer/password_reset.html.erb new file mode 100644 index 000000000..5865b73ed --- /dev/null +++ b/lib/jam_ruby/app/views/jam_ruby/user_mailer/password_reset.html.erb @@ -0,0 +1,7 @@ + + +Reset Password! <%= @user.email %> +
+Here is the token: <%= @user.reset_password_token %> + + diff --git a/lib/jam_ruby/app/views/jam_ruby/user_mailer/password_reset.text.erb b/lib/jam_ruby/app/views/jam_ruby/user_mailer/password_reset.text.erb new file mode 100644 index 000000000..263a237f2 --- /dev/null +++ b/lib/jam_ruby/app/views/jam_ruby/user_mailer/password_reset.text.erb @@ -0,0 +1,2 @@ +Reset Password! <%= @user.email %> +Here is the token: <%= @user.reset_password_token %> diff --git a/lib/jam_ruby/app/views/jam_ruby/user_mailer/reset_password.html.erb b/lib/jam_ruby/app/views/jam_ruby/user_mailer/reset_password.html.erb deleted file mode 100644 index 0b757c910..000000000 --- a/lib/jam_ruby/app/views/jam_ruby/user_mailer/reset_password.html.erb +++ /dev/null @@ -1,5 +0,0 @@ - - -Reset Password! <%= @user.email %> - - \ No newline at end of file diff --git a/lib/jam_ruby/app/views/jam_ruby/user_mailer/reset_password.text.erb b/lib/jam_ruby/app/views/jam_ruby/user_mailer/reset_password.text.erb deleted file mode 100644 index 6507fcc0b..000000000 --- a/lib/jam_ruby/app/views/jam_ruby/user_mailer/reset_password.text.erb +++ /dev/null @@ -1 +0,0 @@ -Reset Password! <%= @user.email %> \ No newline at end of file diff --git a/lib/jam_ruby/constants/validation_messages.rb b/lib/jam_ruby/constants/validation_messages.rb index 08dfa17bf..80379f92f 100644 --- a/lib/jam_ruby/constants/validation_messages.rb +++ b/lib/jam_ruby/constants/validation_messages.rb @@ -1,4 +1,6 @@ module ValidationMessages + + # Note that these are not set up to be internationalizable # general messages PERMISSION_VALIDATION_ERROR = "You do not have permissions to perform this action." @@ -27,5 +29,6 @@ module ValidationMessages # user OLD_PASSWORD_DOESNT_MATCH = "Your old password is incorrect." + EMAIL_NOT_FOUND = "Email address not found." -end \ No newline at end of file +end diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index cb9e73019..9631bf2a0 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -178,15 +178,42 @@ module JamRuby def set_password(old_password, new_password, new_password_confirmation) raise JamRuby::JamArgumentError unless authenticate old_password + change_password(new_password, new_password_confirmation) + save + end + + def self.set_password_from_token(email, token, new_password, new_password_confirmation) + user = User.find_by_email(email) + if user.nil? || user.reset_password_token != token || Time.now - user.reset_password_token_created > 3.days + raise JamRuby::JamArgumentError + end + user.reset_password_token = nil + user.reset_password_token_created = nil + user.change_password(new_password, new_password_confirmation) + user.save + end + + def change_password(new_password, new_password_confirmation) # FIXME: Should verify that the new password meets certain quality criteria. Really, maybe that should be a # verification step. + self.updating_password = true self.password = new_password self.password_confirmation = new_password_confirmation - save UserMailer.password_changed(self).deliver end + def self.reset_password(email) + user = User.find_by_email(email) + raise JamRuby::JamArgumentError if user.nil? + + user.reset_password_token = SecureRandom.urlsafe_base64 + user.reset_password_token_created = Time.now + user.save + + UserMailer.password_reset(user).deliver + end + def self.band_index(user_id) bands = Band.joins(:band_musicians) .where(:bands_musicians => {:user_id => "#{user_id}"}) @@ -661,5 +688,6 @@ module JamRuby end end end + end end diff --git a/spec/jam_ruby/models/user_spec.rb b/spec/jam_ruby/models/user_spec.rb index 72b6bd9eb..b6f80c8f6 100644 --- a/spec/jam_ruby/models/user_spec.rb +++ b/spec/jam_ruby/models/user_spec.rb @@ -151,7 +151,39 @@ describe User do end + describe "reset_password" do + before do + @user.confirm_email! + @user.save + end + + it "fails if the provided email address is unrecognized" do + expect { User.reset_password("invalidemail@invalid.com") }.to raise_error + end + it "assigns a reset_token and reset_token_created on reset" do + User.reset_password(@user.email) + @user.reload + @user.reset_password_token.should_not be_nil + @user.reset_password_token_created.should_not be_nil + @user.reset_password_token_created.should <= Time.now + @user.reset_password_token_created.should >= Time.now - 1.minute + end + + it "errors if the wrong token is comes in" do + User.reset_password(@user.email) + @user.reload + expect { User.set_password_from_token(@user.email, "wrongtoken", "newpassword", "newpassword") }.to raise_error + end + + it "changes the password if the token is right" do + User.reset_password(@user.email) + @user.reload + User.set_password_from_token(@user.email, @user.reset_password_token, "newpassword", "newpassword") + User.authenticate(@user.email, "newpassword").should_not be_nil + @user.reload + end + end describe "return value of authenticate method" do before { @user.save } @@ -220,4 +252,4 @@ describe User do end end -end \ No newline at end of file +end From 9db67b98c84922ba299b84a8269c0c042a359d47 Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Fri, 21 Dec 2012 21:35:45 -0800 Subject: [PATCH 125/274] fix test --- spec/mailers/user_mailer_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/mailers/user_mailer_spec.rb b/spec/mailers/user_mailer_spec.rb index 5364de7e5..cdd91b65c 100644 --- a/spec/mailers/user_mailer_spec.rb +++ b/spec/mailers/user_mailer_spec.rb @@ -28,7 +28,7 @@ describe UserMailer do end it "should send reset password" do - UserMailer.reset_password(user).deliver + UserMailer.password_reset(user).deliver UserMailer.deliveries.length.should == 1 mail = UserMailer.deliveries[0] From 9b443f08f726460f6d2c937f82e64cb1ee4a1fd7 Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Thu, 27 Dec 2012 23:30:03 -0800 Subject: [PATCH 126/274] Completing password reset --- .../app/views/jam_ruby/user_mailer/password_reset.html.erb | 3 +++ .../app/views/jam_ruby/user_mailer/password_reset.text.erb | 2 ++ lib/jam_ruby/models/user.rb | 2 ++ 3 files changed, 7 insertions(+) diff --git a/lib/jam_ruby/app/views/jam_ruby/user_mailer/password_reset.html.erb b/lib/jam_ruby/app/views/jam_ruby/user_mailer/password_reset.html.erb index 5865b73ed..3e5020c5c 100644 --- a/lib/jam_ruby/app/views/jam_ruby/user_mailer/password_reset.html.erb +++ b/lib/jam_ruby/app/views/jam_ruby/user_mailer/password_reset.html.erb @@ -3,5 +3,8 @@ Reset Password! <%= @user.email %>
Here is the token: <%= @user.reset_password_token %> +
+Here is the URL to visit where you can type in your new password: +<%= "/reset_password_token?token=#{@user.reset_password_token}&email=#{CGI.escape(@user.email)}" %> diff --git a/lib/jam_ruby/app/views/jam_ruby/user_mailer/password_reset.text.erb b/lib/jam_ruby/app/views/jam_ruby/user_mailer/password_reset.text.erb index 263a237f2..7039fe433 100644 --- a/lib/jam_ruby/app/views/jam_ruby/user_mailer/password_reset.text.erb +++ b/lib/jam_ruby/app/views/jam_ruby/user_mailer/password_reset.text.erb @@ -1,2 +1,4 @@ Reset Password! <%= @user.email %> Here is the token: <%= @user.reset_password_token %> +Here is the URL to visit where you can type in your new password: +<%= "/reset_password_token?token=#{@user.reset_password_token}&email=#{CGI.escape(@user.email)}" %> diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 9631bf2a0..cac051c10 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -212,6 +212,8 @@ module JamRuby user.save UserMailer.password_reset(user).deliver + + user end def self.band_index(user_id) From 80a5a5d7f31b1c7ee2c1ba51a30dee983e43ae3b Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Sat, 29 Dec 2012 09:28:41 -0500 Subject: [PATCH 127/274] VRFS-159 block, spam, ignore friend requests / unit tests --- lib/jam_ruby/models/friend_request.rb | 59 +++++++++++++++++++++++++-- lib/jam_ruby/models/user.rb | 1 - 2 files changed, 56 insertions(+), 4 deletions(-) diff --git a/lib/jam_ruby/models/friend_request.rb b/lib/jam_ruby/models/friend_request.rb index 6767aa685..8a1962a9b 100644 --- a/lib/jam_ruby/models/friend_request.rb +++ b/lib/jam_ruby/models/friend_request.rb @@ -3,15 +3,68 @@ module JamRuby self.primary_key = 'id' + STATUS = %w(accept block spam ignore) + belongs_to :user, :class_name => "JamRuby::User" belongs_to :friend, :class_name => "JamRuby::User" validates :user_id, :presence => true validates :friend_id, :presence => true + #validates :status, :inclusion => {:in => STATUS} - def to_s - return "#{self.user.to_s}:#{self.friend.to_s}" - end + def to_s + return "#{self.id} => #{self.user.to_s}:#{self.friend.to_s}" + end + def self.save(id, user_id, friend_id, status, message) + if id.nil? + friend_request = FriendRequest.new() + friend_request = validate_friend_request(friend_request, user_id, friend_id) + friend_request.user_id = user_id + friend_request.friend_id = friend_id + friend_request.message = message + friend_request.save + else + ActiveRecord::Base.transaction do + friend_request = FriendRequest.find(id) #("user_id='#{user_id}' AND friend_id='#{friend_id}'") + friend_request.status = status + friend_request.updated_at = Time.now.getutc + friend_request.save + + # create both records for this friendship + if friend_request.status == "accept" + friendship = Friendship.new() + friendship.user_id = friend_request.user_id + friendship.friend_id = friend_request.friend_id + friendship.save + + friendship = Friendship.new() + friendship.user_id = friend_request.friend_id + friendship.friend_id = friend_request.user_id + friendship.save + end + end + end + + return friend_request + end + + private + def self.validate_friend_request(friend_request, user_id, friend_id) + friend_requests = FriendRequest.where("user_id='#{user_id}' AND friend_id='#{friend_id}'") + + # check if there are any friend requests for this source/target user combo, and if + # any have been marked as spam or blocked, set the status of this friend request + # to match so it doesn't show up in the queue + unless friend_requests.nil? || friend_requests.size == 0 + if friend_requests.exists(:status => "spam") + friend_request.status = "spam" + + elsif friend_requests.exists(:status => "block") + friend_request.status = "block" + end + end + return friend_request + end end end \ No newline at end of file diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index cac051c10..8e6b3444b 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -690,6 +690,5 @@ module JamRuby end end end - end end From 2c4e55091068f5cc5ada57fb475706939cbddf36 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Sun, 30 Dec 2012 09:39:41 -0500 Subject: [PATCH 128/274] VRFS-159 fixed bugs / added unit tests --- lib/jam_ruby/models/band_invitation.rb | 57 +++++++++++++++----------- lib/jam_ruby/models/friend_request.rb | 18 +++----- lib/jam_ruby/models/friendship.rb | 9 ++++ 3 files changed, 47 insertions(+), 37 deletions(-) diff --git a/lib/jam_ruby/models/band_invitation.rb b/lib/jam_ruby/models/band_invitation.rb index a0251f3e4..4459701eb 100644 --- a/lib/jam_ruby/models/band_invitation.rb +++ b/lib/jam_ruby/models/band_invitation.rb @@ -13,34 +13,43 @@ module JamRuby def self.save(id, band_id, user_id, creator_id, accepted) - # ensure certain fields are only updated on creation - if id.nil? - # ensure recipient is a Musician - user = User.find(user_id) - unless user.musician? - raise JamRuby::JamArgumentError, BAND_INVITATION_FAN_RECIPIENT_ERROR + band_invitation = BandInvitation.new() + + ActiveRecord::Base.transaction do + # ensure certain fields are only updated on creation + if id.nil? + # ensure recipient is a Musician + user = User.find(user_id) + unless user.musician? + raise JamRuby::JamArgumentError, BAND_INVITATION_FAN_RECIPIENT_ERROR + end + + band_invitation.band_id = band_id + band_invitation.user_id = user_id + band_invitation.creator_id = creator_id + + # only the accepted flag can be updated after initial creation + else + band_invitation = BandInvitation.find(id) + band_invitation.accepted = accepted end - band_invitation = BandInvitation.new() - band_invitation.band_id = band_id - band_invitation.user_id = user_id - band_invitation.creator_id = creator_id + band_invitation.updated_at = Time.now.getutc + band_invitation.save - # only the accepted flag can be updated after initial creation - else - band_invitation = BandInvitation.find(id) - band_invitation.accepted = accepted + # accept logic => (1) auto-friend each band member and (2) add the musician to the band + if accepted + band_musicians = BandMusician.where(:band_id => band_invitation.band.id) + unless band_musicians.nil? + band_musicians.each do |bm| + Friendship.save(band_invitation.receiver.id, bm.user_id) + end + end + + # accepting an invitation adds the musician to the band + BandMusician.create(:band_id => band_invitation.band.id, :user_id => band_invitation.receiver.id, :admin => false) + end end - - band_invitation.updated_at = Time.now.getutc - band_invitation.save - - # TODO: wrap this and previous block in transaction - # accepting an invitation adds the musician to the band - if accepted - BandMusician.create(:band_id => band_invitation.band.id, :user_id => band_invitation.receiver.id, :admin => false) - end - return band_invitation end end diff --git a/lib/jam_ruby/models/friend_request.rb b/lib/jam_ruby/models/friend_request.rb index 8a1962a9b..cdc9c6f21 100644 --- a/lib/jam_ruby/models/friend_request.rb +++ b/lib/jam_ruby/models/friend_request.rb @@ -26,22 +26,14 @@ module JamRuby friend_request.save else ActiveRecord::Base.transaction do - friend_request = FriendRequest.find(id) #("user_id='#{user_id}' AND friend_id='#{friend_id}'") + friend_request = FriendRequest.find(id) friend_request.status = status friend_request.updated_at = Time.now.getutc friend_request.save # create both records for this friendship - if friend_request.status == "accept" - friendship = Friendship.new() - friendship.user_id = friend_request.user_id - friendship.friend_id = friend_request.friend_id - friendship.save - - friendship = Friendship.new() - friendship.user_id = friend_request.friend_id - friendship.friend_id = friend_request.user_id - friendship.save + if friend_request.status == "accept" + Friendship.save(friend_request.user_id, friend_request.friend_id) end end end @@ -57,10 +49,10 @@ module JamRuby # any have been marked as spam or blocked, set the status of this friend request # to match so it doesn't show up in the queue unless friend_requests.nil? || friend_requests.size == 0 - if friend_requests.exists(:status => "spam") + if friend_requests.exists?(:status => "spam") friend_request.status = "spam" - elsif friend_requests.exists(:status => "block") + elsif friend_requests.exists?(:status => "block") friend_request.status = "block" end end diff --git a/lib/jam_ruby/models/friendship.rb b/lib/jam_ruby/models/friendship.rb index e8a54f23a..575e06cd6 100644 --- a/lib/jam_ruby/models/friendship.rb +++ b/lib/jam_ruby/models/friendship.rb @@ -1,10 +1,19 @@ module JamRuby class Friendship < ActiveRecord::Base + attr_accessible :user_id, :friend_id + self.primary_key = 'id' belongs_to :user, :class_name => "JamRuby::User", :foreign_key => "user_id", :inverse_of => :inverse_friendships belongs_to :friend, :class_name => "JamRuby::User", :foreign_key => "friend_id", :inverse_of => :friendships + def self.save(user_id, friend_id) + friendship = Friendship.where("user_id='#{user_id}' AND friend_id='#{friend_id}'") + unless friendship.nil? + Friendship.create(:user_id => user_id, :friend_id => friend_id) + Friendship.create(:user_id => friend_id, :friend_id => user_id) + end + end end end \ No newline at end of file From 7d1e7417ed3c32544a67cb2a6d58dae835e6b04d Mon Sep 17 00:00:00 2001 From: Jonathan Kolyer Date: Fri, 4 Jan 2013 04:13:39 -0600 Subject: [PATCH 129/274] VRFS-194: added can_invite attribute, defaults to true, but false for invited users --- lib/jam_ruby/constants/limits.rb | 5 ++++- lib/jam_ruby/models/user.rb | 2 ++ spec/jam_ruby/models/user_spec.rb | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/jam_ruby/constants/limits.rb b/lib/jam_ruby/constants/limits.rb index 4a632b2b4..dc534340a 100644 --- a/lib/jam_ruby/constants/limits.rb +++ b/lib/jam_ruby/constants/limits.rb @@ -12,4 +12,7 @@ module Limits MIN_INSTRUMENTS_PER_MUSICIAN = 1 MAX_INSTRUMENTS_PER_MUSICIAN = 5 -end \ No newline at end of file + # users + USERS_CAN_INVITE = false # in BETA release, only first level users can invite others + +end diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 8e6b3444b..a352bceb0 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -500,6 +500,8 @@ module JamRuby user.signup_token = SecureRandom.urlsafe_base64 + user.can_invite = Limits::USERS_CAN_INVITE + user.save if user.errors.any? diff --git a/spec/jam_ruby/models/user_spec.rb b/spec/jam_ruby/models/user_spec.rb index b6f80c8f6..aa31e26de 100644 --- a/spec/jam_ruby/models/user_spec.rb +++ b/spec/jam_ruby/models/user_spec.rb @@ -18,6 +18,7 @@ describe User do it { should respond_to(:remember_token) } it { should respond_to(:admin) } it { should respond_to(:authenticate) } + it { should respond_to(:can_invite) } it { should be_valid } it { should_not be_admin } From 225a81adb98446de8fb230ea22d1cf002b4cc547 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Sun, 6 Jan 2013 15:46:48 -0500 Subject: [PATCH 130/274] VRFS-80 session history --- lib/jam_ruby.rb | 2 + lib/jam_ruby/connection_manager.rb | 4 +- lib/jam_ruby/models/music_session_history.rb | 52 +++++++++++++++++++ .../models/music_session_user_history.rb | 19 +++++++ lib/jam_ruby/models/user.rb | 11 ++++ 5 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 lib/jam_ruby/models/music_session_history.rb create mode 100644 lib/jam_ruby/models/music_session_user_history.rb diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index 4b0c690ec..85c6aab2e 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -35,6 +35,8 @@ require "jam_ruby/models/band_musician" require "jam_ruby/models/connection" require "jam_ruby/models/friendship" require "jam_ruby/models/music_session" +require "jam_ruby/models/music_session_history" +require "jam_ruby/models/music_session_user_history" require "jam_ruby/models/invitation" require "jam_ruby/models/fan_invitation" require "jam_ruby/models/friend_request" diff --git a/lib/jam_ruby/connection_manager.rb b/lib/jam_ruby/connection_manager.rb index 08831fb1e..360726691 100644 --- a/lib/jam_ruby/connection_manager.rb +++ b/lib/jam_ruby/connection_manager.rb @@ -182,7 +182,7 @@ module JamRuby if connection.errors.any? raise ActiveRecord::Rollback else - + MusicSessionUserHistory.save(music_session_id, user_id, client_id) end end @@ -212,7 +212,7 @@ module JamRuby end begin - # we include user_id in the query as an act o security, so that a user can't access someone else' client connection + # we include user_id in the query as an act of security, so that a user can't access someone else's client connection conn.exec("UPDATE connections SET music_session_id = $1, as_musician = $2 WHERE client_id = $3 and user_id = $4", [music_session_id, as_musician, client_id, user_id]) do |result| if result.cmd_tuples == 1 @log.debug "associated music_session with connection for client=#{client_id}, music_session=#{music_session_id}, and user=#{user_id}" diff --git a/lib/jam_ruby/models/music_session_history.rb b/lib/jam_ruby/models/music_session_history.rb new file mode 100644 index 000000000..c70d58692 --- /dev/null +++ b/lib/jam_ruby/models/music_session_history.rb @@ -0,0 +1,52 @@ +module JamRuby + class MusicSessionHistory < ActiveRecord::Base + + self.table_name = "music_sessions_history" + + self.primary_key = 'id' + + has_many :music_session_user_histories, :class_name => "JamRuby::MusicSessionUserHistory", :foreign_key => "music_session_id" + + def self.index(current_user, user_id, band_id = nil, genre = nil) + hide_private = false + if current_user.id != user_id + hide_private = false # TODO: change to true once public flag exists + end + + query = MusicSessionHistory + .joins( + %Q{ + LEFT OUTER JOIN + music_sessions_user_history + ON + music_sessions_history.music_session_id = music_sessions_user_history.music_session_id + } + ) + .where( + %Q{ + music_sessions_history.user_id = '#{user_id}' + } + ) + + #query = query.where("public = false") unless !hide_private + query = query.where("music_sessions_history.band_id = '#{band_id}") unless band_id.nil? + query = query.where("music_sessions_history.genres like '%#{genre}%'") unless genre.nil? + return query + end + + def self.save(music_session) + session_history = MusicSessionHistory.find_by_music_session_id(music_session.id) + + if session_history.nil? + session_history = MusicSessionHistory.new() + end + + session_history.music_session_id = music_session.id + session_history.description = music_session.description unless music_session.description.nil? + session_history.user_id = music_session.creator.id + session_history.band_id = music_session.band.id unless music_session.band.nil? + session_history.genres = music_session.genres.map { |g| g.id }.join '|' + session_history.save + end + end +end \ No newline at end of file diff --git a/lib/jam_ruby/models/music_session_user_history.rb b/lib/jam_ruby/models/music_session_user_history.rb new file mode 100644 index 000000000..5517e6f0f --- /dev/null +++ b/lib/jam_ruby/models/music_session_user_history.rb @@ -0,0 +1,19 @@ +module JamRuby + class MusicSessionUserHistory < ActiveRecord::Base + + self.table_name = "music_sessions_user_history" + + self.primary_key = 'id' + + has_many :users, :foreign_key => "user_id", :class_name => "JamRuby::User" + belongs_to :musician_session_history, :class_name => "JamRuby::MusicSessionHistory", :foreign_key => "music_session_id" + + def self.save(music_session_id, user_id, client_id) + session_user_history = MusicSessionUserHistory.new() + session_user_history.music_session_id = music_session_id + session_user_history.user_id = user_id + session_user_history.client_id = client_id + session_user_history.save + end + end +end \ No newline at end of file diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 8e6b3444b..dc430d793 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -78,6 +78,9 @@ module JamRuby has_many :received_band_invitations, :inverse_of => :receiver, :foreign_key => "user_id", :class_name => "JamRuby::BandInvitation" has_many :sent_band_invitations, :inverse_of => :sender, :foreign_key => "creator_id", :class_name => "JamRuby::BandInvitation" + # session history + has_many :music_session_histories, :foreign_key => "user_id", :class_name => "JamRuby::MusicSessionHistory" + # This causes the authenticate method to be generated (among other stuff) has_secure_password @@ -166,6 +169,14 @@ module JamRuby end end + def session_history(user_id, band_id = nil, genre = nil) + return MusicSessionHistory.index(self, user_id, band_id, genre) + end + + def session_user_history(user_id, session_id) + return MusicSessionUserHistory.where("music_session_id='#{session_id}'") + end + def to_s return email unless email.nil? From 35627dc1f8ae2fa43526360425fc5a7700cab3a3 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 6 Jan 2013 15:13:01 -0600 Subject: [PATCH 131/274] * adding installe, then update. I really need to understand why and how this happens with bundler... --- build | 1 + 1 file changed, 1 insertion(+) diff --git a/build b/build index ec1b1fa31..0343a07b2 100755 --- a/build +++ b/build @@ -1,6 +1,7 @@ #!/bin/bash echo "updating dependencies" +bundle install bundle update echo "running rspec tests" bundle exec rspec From 436a68897695ae1cb27f5fb30d7ed8b7bc50fa7d Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 6 Jan 2013 15:14:38 -0600 Subject: [PATCH 132/274] * nm. someone commented out our gem server --- Gemfile | 2 +- build | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile b/Gemfile index db874839e..61e62db70 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,6 @@ #ruby=1.9.3 source 'https://rubygems.org' -#source 'https://jamjam:blueberryjam@www.jamkazam.com/gems/' +source 'https://jamjam:blueberryjam@www.jamkazam.com/gems/' # Look for $WORKSPACE, otherwise use "workspace" as dev path. workspace = ENV["WORKSPACE"] || "~/workspace" diff --git a/build b/build index 0343a07b2..60b307a77 100755 --- a/build +++ b/build @@ -1,8 +1,8 @@ #!/bin/bash echo "updating dependencies" -bundle install bundle update + echo "running rspec tests" bundle exec rspec From 3bfae973b9981b17cc7a9dcbfe8c6697b4d8f7e9 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Sun, 6 Jan 2013 16:38:03 -0500 Subject: [PATCH 133/274] fixed unit test to insert into session_history table on session creation --- spec/jam_ruby/connection_manager_spec.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/jam_ruby/connection_manager_spec.rb b/spec/jam_ruby/connection_manager_spec.rb index c4dc62935..c4041e3db 100644 --- a/spec/jam_ruby/connection_manager_spec.rb +++ b/spec/jam_ruby/connection_manager_spec.rb @@ -22,7 +22,9 @@ describe ConnectionManager do options = default_options.merge(options) description = "some session" @conn.exec("INSERT INTO music_sessions (user_id, description, musician_access, approval_required, fan_chat, fan_access) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id", [user_id, description, options[:musician_access], options[:approval_required], options[:fan_chat], options[:fan_access]]) do |result| - return result.getvalue(0, 0) + session_id = result.getvalue(0, 0) + @conn.exec("INSERT INTO music_sessions_history (music_session_id, description, user_id) VALUES ($1, $2, $3)", [session_id, description, user_id]) + return session_id end end From 1d0c8b3cce97ea4f64d939259fe76a9228f5abb5 Mon Sep 17 00:00:00 2001 From: Jonathan Kolyer Date: Sat, 5 Jan 2013 20:40:22 -0600 Subject: [PATCH 134/274] VRFS-193: beta user welcome email --- lib/jam_ruby/app/mailers/user_mailer.rb | 12 ++++++++++++ .../jam_ruby/user_mailer/welcome_betauser.html.erb | 6 ++++++ .../jam_ruby/user_mailer/welcome_betauser.text.erb | 2 ++ 3 files changed, 20 insertions(+) create mode 100644 lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_betauser.html.erb create mode 100644 lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_betauser.text.erb diff --git a/lib/jam_ruby/app/mailers/user_mailer.rb b/lib/jam_ruby/app/mailers/user_mailer.rb index 8be2e6c0f..eb14724f9 100644 --- a/lib/jam_ruby/app/mailers/user_mailer.rb +++ b/lib/jam_ruby/app/mailers/user_mailer.rb @@ -29,6 +29,18 @@ module JamRuby end end + def welcome_betauser(user, signup_confirm_url) + @user = user + @signup_confirm_url = signup_confirm_url + sendgrid_category "Welcome" + sendgrid_unique_args :type => "welcome_betauser" + + mail(:to => user.email, :subject => "Welcome #{user.first_name} to the Jamkazam Beta release") do |format| + format.text + format.html + end + end + def password_changed(user) @user = user sendgrid_unique_args :type => "password_changed" diff --git a/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_betauser.html.erb b/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_betauser.html.erb new file mode 100644 index 000000000..7b10e4d4d --- /dev/null +++ b/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_betauser.html.erb @@ -0,0 +1,6 @@ + + +

Welcome to Jamkazam! <%= @user.first_name %>.

+

To confirm your registration, please go to the signup confirmation page..

+ + diff --git a/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_betauser.text.erb b/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_betauser.text.erb new file mode 100644 index 000000000..d8601050b --- /dev/null +++ b/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_betauser.text.erb @@ -0,0 +1,2 @@ +Welcome! <%= @user.first_name %> +To confirm your registration, please go to the signup confirmation page at : <%= @signup_confirm_url %>. \ No newline at end of file From 7a7b352b1a7db581ef220ad27351027cf4814385 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Thu, 10 Jan 2013 22:42:32 -0500 Subject: [PATCH 135/274] fixed friend bug --- lib/jam_ruby/models/friendship.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/jam_ruby/models/friendship.rb b/lib/jam_ruby/models/friendship.rb index 575e06cd6..2dd155bbb 100644 --- a/lib/jam_ruby/models/friendship.rb +++ b/lib/jam_ruby/models/friendship.rb @@ -10,7 +10,8 @@ module JamRuby def self.save(user_id, friend_id) friendship = Friendship.where("user_id='#{user_id}' AND friend_id='#{friend_id}'") - unless friendship.nil? + + if friendship.nil? || friendship.size == 0 Friendship.create(:user_id => user_id, :friend_id => friend_id) Friendship.create(:user_id => friend_id, :friend_id => user_id) end From 0425a9d38937f057e0c17b36e68832f6c1d600a7 Mon Sep 17 00:00:00 2001 From: Jonathan Kolyer Date: Sun, 6 Jan 2013 05:53:41 -0600 Subject: [PATCH 136/274] make text consistent between html/text versions --- .../app/views/jam_ruby/user_mailer/welcome_betauser.html.erb | 2 +- .../app/views/jam_ruby/user_mailer/welcome_betauser.text.erb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_betauser.html.erb b/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_betauser.html.erb index 7b10e4d4d..5c1f1c611 100644 --- a/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_betauser.html.erb +++ b/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_betauser.html.erb @@ -1,6 +1,6 @@ -

Welcome to Jamkazam! <%= @user.first_name %>.

+

Welcome to the Jamkazam beta release! <%= @user.first_name %>.

To confirm your registration, please go to the signup confirmation page..

diff --git a/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_betauser.text.erb b/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_betauser.text.erb index d8601050b..47bb3f9da 100644 --- a/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_betauser.text.erb +++ b/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_betauser.text.erb @@ -1,2 +1,2 @@ -Welcome! <%= @user.first_name %> -To confirm your registration, please go to the signup confirmation page at : <%= @signup_confirm_url %>. \ No newline at end of file +Welcome to the JamKazam beta release! <%= @user.first_name %> +To confirm your registration, please go to the signup confirmation page at : <%= @signup_confirm_url %>. From d2ebfbde4fd28e3baa4bc0a0fe70448cff34502d Mon Sep 17 00:00:00 2001 From: Jonathan Kolyer Date: Sun, 6 Jan 2013 06:34:16 -0600 Subject: [PATCH 137/274] added method location= and revised location to handle missing fields better --- lib/jam_ruby/models/user.rb | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 1af6ba2ab..0bf0c4400 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -106,7 +106,18 @@ module JamRuby end def location - return "#{self.city}, #{self.state}, #{self.country}" + loc = self.city.blank? ? '' : self.city + loc = loc.blank? ? self.state : "#{loc}, #{self.state}" unless self.state.blank? + loc = loc.blank? ? self.country : "#{loc}, #{self.country}" unless self.country.blank? + loc + end + + def location= location_hash + unless location_hash.blank? + self.city = location_hash[:city] + self.state = location_hash[:state] + self.country = location_hash[:country] + end if self.city.blank? end def should_validate_password? From 121003d16d5cea57e1d5e09e254ffb85706536b6 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sat, 12 Jan 2013 22:31:50 -0600 Subject: [PATCH 138/274] * bundle install --local for build --- build | 1 + 1 file changed, 1 insertion(+) diff --git a/build b/build index 60b307a77..df9ac25e3 100755 --- a/build +++ b/build @@ -1,6 +1,7 @@ #!/bin/bash echo "updating dependencies" +bundle install --path vendor/bundle --local bundle update echo "running rspec tests" From 7c2b5da509d06843093b397ebcd0020ce64d9ab5 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 13 Jan 2013 22:50:38 -0600 Subject: [PATCH 139/274] * VRFS-217: replacing elasticsearch with pg --- Gemfile | 1 - lib/jam_ruby.rb | 2 - lib/jam_ruby/models/band.rb | 65 +++-------- lib/jam_ruby/models/recording.rb | 17 +++ lib/jam_ruby/models/search.rb | 70 +++++------ lib/jam_ruby/models/user.rb | 63 ++-------- lib/jam_ruby/tire_tasks.rb | 51 -------- spec/jam_ruby/models/band_search_spec.rb | 39 +++---- spec/jam_ruby/models/search_spec.rb | 16 +-- spec/jam_ruby/models/tire_search_spec.rb | 142 ----------------------- spec/jam_ruby/models/user_search_spec.rb | 47 +++----- 11 files changed, 113 insertions(+), 400 deletions(-) delete mode 100644 lib/jam_ruby/tire_tasks.rb delete mode 100644 spec/jam_ruby/models/tire_search_spec.rb diff --git a/Gemfile b/Gemfile index 61e62db70..3f52bc713 100644 --- a/Gemfile +++ b/Gemfile @@ -15,7 +15,6 @@ gem 'bcrypt-ruby', '3.0.1' gem 'ruby-protocol-buffers', '1.2.2' gem 'eventmachine' gem 'amqp' -gem 'tire' gem 'will_paginate' gem 'actionmailer' gem 'sendgrid' diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index 85c6aab2e..e47e8dbf6 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -3,7 +3,6 @@ require "active_record" require "jampb" require "uuidtools" require "logging" -require "tire" require "will_paginate" require "will_paginate/active_record" require "action_mailer" @@ -20,7 +19,6 @@ require "jam_ruby/version" require "jam_ruby/environment" require "jam_ruby/init" require "jam_ruby/app/mailers/user_mailer" -require "jam_ruby/tire_tasks" require "jam_ruby/message_factory" require "jam_ruby/models/genre" require "jam_ruby/models/user" diff --git a/lib/jam_ruby/models/band.rb b/lib/jam_ruby/models/band.rb index 953aa5851..0eea4acdb 100644 --- a/lib/jam_ruby/models/band.rb +++ b/lib/jam_ruby/models/band.rb @@ -1,7 +1,5 @@ module JamRuby class Band < ActiveRecord::Base - include Tire::Model::Search - include Tire::Model::Callbacks attr_accessible :name, :website, :biography, :city, :state, :country @@ -78,6 +76,23 @@ module JamRuby return recordings end + + def self.search(query, options = { :limit => 10 }) + + # only issue search if at least 2 characters are specified + if query.nil? || query.length < 2 + return [] + end + + # create 'anded' statement + query = Search.create_tsquery(query) + + if query.nil? || query.length == 0 + return [] + end + + return Band.where("name_tsv @@ to_tsquery('jamenglish', ?)", query).limit(options[:limit]) + end # helper method for creating / updating a Band def self.save(id, name, website, biography, city, state, country, genres, user_id, photo_url, logo_url) @@ -173,52 +188,6 @@ module JamRuby return band end - ### Elasticsearch/Tire integration ### - # - # Define the name based on the environment - # We wouldn't like to erase dev data during - # test runs! - # - index_name("#{Environment.mode}-#{Environment.application}-bands") - - def to_indexed_json - { - :name => name, - :logo_url => logo_url, - :photo_url => photo_url, - :location => location - }.to_json - end - - class << self - def create_search_index - Tire.index(Band.index_name) do - create( - :settings => Search.index_settings, - :mappings => { - "jam_ruby/band" => { - :properties => { - :logo_url => { :type => :string, :index => :not_analyzed, :include_in_all => false }, - :photo_url => { :type => :string, :index => :not_analyzed, :include_in_all => false}, - :name => { :type => :string, :boost => 100}, - :location => { :type => :string }, - } - } - } - ) - end - end - - def delete_search_index - search_index.delete - end - - def search_index - Tire.index(Band.index_name) - end - end - ### Elasticsearch/Tire integration - private def self.validate_genres(genres, is_nil_ok) if is_nil_ok && genres.nil? diff --git a/lib/jam_ruby/models/recording.rb b/lib/jam_ruby/models/recording.rb index a850b879e..e4d2aa7ad 100644 --- a/lib/jam_ruby/models/recording.rb +++ b/lib/jam_ruby/models/recording.rb @@ -19,6 +19,23 @@ module JamRuby return self.favorites.size end + def self.search(query, options = { :limit => 10 }) + + # only issue search if at least 2 characters are specified + if query.nil? || query.length < 2 + return [] + end + + # create 'anded' statement + query = Search.create_tsquery(query) + + if query.nil? || query.length == 0 + return [] + end + + return Recording.where("description_tsv @@ to_tsquery('jamenglish', ?)", query).limit(options[:limit]) + end + def self.save(id, is_public, description, genres, updater_id, owner_id, is_band) creator = User.find(updater_id) diff --git a/lib/jam_ruby/models/search.rb b/lib/jam_ruby/models/search.rb index a2d25b066..fef404205 100644 --- a/lib/jam_ruby/models/search.rb +++ b/lib/jam_ruby/models/search.rb @@ -3,43 +3,16 @@ module JamRuby class Search attr_accessor :bands, :musicians, :fans, :recordings + LIMIT = 10 + + # performs a site-white search def self.search(query) - # empty queries don't hit back to elasticsearch - if query.nil? || query.length == 0 - return Search.new(nil) - end - s = Tire.search [User.index_name, Band.index_name], :load => true do - query { string query } - sort { by [:_score] } - from 0 - size 10 # doesn't have to be hardcoded... - end + users = User.search(query, :limit => LIMIT) + bands = Band.search(query, :limit => LIMIT) + recordings = Recording.search(query, :limit => LIMIT) - return Search.new(s) - end - - # elasticsearch index settings - def self.index_settings() - return { - "analysis" => { - "analyzer" => { - "default" => { - "type" => "custom", - "tokenizer" => "lowercase", - "filter" => ["name_ngram"] - } - }, - "filter" => { - "name_ngram" => { - "type" => 'edgeNGram', - "min_gram" => 2, - "max_gram" => 7, - "side" => "front" - } - } - } - } + return Search.new(users + bands + recordings) end # search_results - results from a Tire search across band/user/recording @@ -53,7 +26,7 @@ module JamRuby return end - search_results.results.each do |result| + search_results.take(LIMIT).each do |result| if result.class == User if result.musician @musicians.push(result) @@ -69,5 +42,32 @@ module JamRuby end end end + + def self.create_tsquery(query) + # empty queries don't hit back to elasticsearch + if query.nil? || query.length == 0 + return nil + end + + search_terms = query.split + + if search_terms.length == 0 + return nil + end + + args = nil + search_terms.each do |search_term| + if args == nil + args = search_term + else + args = args + " & " + search_term + end + + end + args = args + ":*" + + return args + end + end end \ No newline at end of file diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 0bf0c4400..e6e8951b7 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -1,6 +1,5 @@ module JamRuby class User < ActiveRecord::Base - include Tire::Model::Search attr_accessible :first_name, :last_name, :email, :password, :password_confirmation, :city, :state, :country attr_accessor :updating_password @@ -633,64 +632,22 @@ module JamRuby end end + def self.search(query, options = { :limit => 10 }) - ### Elasticsearch/Tire integration ### - # - # Define the name based on the environment - # We wouldn't like to erase dev data during - # test runs! - # - index_name("#{Environment.mode}-#{Environment.application}-users") - - def to_indexed_json - { - :first_name => first_name, - :last_name => last_name, - :photo_url => photo_url, - :location => location, - :musician => musician - }.to_json - end - - # only put users into the search index if their eail is confirmed - after_save do - update_index if email_confirmed - end - - after_destroy do - update_index - end - - class << self - def create_search_index - Tire.index(User.index_name) do - create( - :settings => Search.index_settings, - :mappings => { - "jam_ruby/user" => { - :properties => { - :photo_url => { :type => :string, :index => :not_analyzed, :include_in_all => false}, - :location => { :type => :string }, - :first_name => { :type => :string, :boost => 100 }, - :last_name => { :type => :string, :boost => 100 }, - :musician => { :type => :boolean, :index => :not_analyzed, :include_in_all => false} - } - } - } - ) - end + # only issue search if at least 2 characters are specified + if query.nil? || query.length < 2 + return [] end - def delete_search_index - search_index.delete + # create 'anded' statement + query = Search.create_tsquery(query) + + if query.nil? || query.length == 0 + return [] end - def search_index - Tire.index(User.index_name) - end + return User.where("email_confirmed = true and name_tsv @@ to_tsquery('jamenglish', ?)", query).limit(options[:limit]) end - ### Elasticsearch/Tire integration - private def create_remember_token diff --git a/lib/jam_ruby/tire_tasks.rb b/lib/jam_ruby/tire_tasks.rb deleted file mode 100644 index bb4c3063f..000000000 --- a/lib/jam_ruby/tire_tasks.rb +++ /dev/null @@ -1,51 +0,0 @@ -class TireTasks - - class << self - @@log = Logging.logger[TireTasks] - end - - def self.verify - - db_users_count = User.count(:id) - db_bands_count = Band.count(:id) - - s = Tire.search [User.index_name], :search_type => 'count', :query => {"match_all" => {}} do - - end - es_users_count = s.results.total - - s = Tire.search [Band.index_name], :search_type => 'count', :query => {"match_all" => {}} do - - end - es_bands_count = s.results.total - @@log.debug "database_users=#{db_users_count}, elasticsearch_users=#{es_users_count} database_bands=#{db_bands_count}, elasticsearch_bands=#{es_bands_count} " - - if db_users_count != es_users_count - @@log.error "the number of elasticsearch users (#{es_users_count}) != the number of database users ((#{db_users_count}). A rebuild of the elasticsearch index should be performed" - return false - end - - if db_bands_count != es_bands_count - @@log.error "the number of elasticsearch bands (#{es_bands_count}) != the number of database bands (#{db_bands_count}). A rebuild of the elasticsearch index should be performed" - return false - end - - return true - end - - def self.rebuild_indexes - @@log.info "rebuilding elasticsearch" - - User.delete_search_index - User.create_search_index - Band.delete_search_index - Band.create_search_index - - User.import :per_page => 100 - Band.import :per_page => 100 - - @@log.info "done rebuilding elasticsearch" - User.search_index.refresh - Band.search_index.refresh - end -end \ No newline at end of file diff --git a/spec/jam_ruby/models/band_search_spec.rb b/spec/jam_ruby/models/band_search_spec.rb index 1c45a2045..1bec62448 100644 --- a/spec/jam_ruby/models/band_search_spec.rb +++ b/spec/jam_ruby/models/band_search_spec.rb @@ -5,20 +5,15 @@ describe User do let(:user) { FactoryGirl.create(:user) } before(:each) do - Band.delete_search_index - Band.create_search_index @band = Band.save(nil, "Example Band", "www.bands.com", "zomg we rock", "Apex", "NC", "USA", ["hip hop"], user.id, nil, nil) - # you have to poke elasticsearch because it will batch requests internally for a second - Band.search_index.refresh end it "should allow search of one band" do ws = Band.search("Example Band") - ws.results.length.should == 1 - band_result = ws.results[0] - band_result._type.should == "jam_ruby/band" + ws.length.should == 1 + band_result = ws[0] band_result.name.should == @band.name band_result.id.should == @band.id band_result.location.should == @band.location @@ -26,56 +21,52 @@ describe User do it "should delete band" do ws = Band.search("Example Band") - ws.results.length.should == 1 - band_result = ws.results[0] + ws.length.should == 1 + band_result = ws[0] band_result.id.should == @band.id @band.destroy # delete doesn't work; you have to use destroy. - Band.search_index.refresh ws = Band.search("Example Band") - ws.results.length.should == 0 + ws.length.should == 0 end it "should update band" do ws = Band.search("Example Band") - ws.results.length.should == 1 - band_result = ws.results[0] + ws.length.should == 1 + band_result = ws[0] band_result.id.should == @band.id @band.name = "bonus-stuff" @band.save - Band.search_index.refresh ws = Band.search("Example Band") - ws.results.length.should == 0 + ws.length.should == 0 ws = Band.search("Bonus") - ws.results.length.should == 1 - band_result = ws.results[0] + ws.length.should == 1 + band_result = ws[0] band_result.id.should == @band.id band_result.name.should == "bonus-stuff" end it "should tokenize correctly" do @band2 = Band.save(nil, "Peach pit", "www.bands.com", "zomg we rock", "Apex", "NC", "USA", ["hip hop"], user.id, nil, nil) - Band.search_index.refresh ws = Band.search("pea") - ws.results.length.should == 1 - user_result = ws.results[0] + ws.length.should == 1 + user_result = ws[0] user_result.id.should == @band2.id end it "should not return anything with a 1 character search" do @band2 = Band.save(nil, "Peach pit", "www.bands.com", "zomg we rock", "Apex", "NC", "USA", ["hip hop"], user.id, nil, nil) - Band.search_index.refresh ws = Band.search("pe") - ws.results.length.should == 1 - user_result = ws.results[0] + ws.length.should == 1 + user_result = ws[0] user_result.id.should == @band2.id ws = Band.search("p") - ws.results.length.should == 0 + ws.length.should == 0 end end \ No newline at end of file diff --git a/spec/jam_ruby/models/search_spec.rb b/spec/jam_ruby/models/search_spec.rb index 25f7bf77b..b0798c570 100644 --- a/spec/jam_ruby/models/search_spec.rb +++ b/spec/jam_ruby/models/search_spec.rb @@ -3,10 +3,6 @@ require 'spec_helper' describe Search do before(:each) do - Band.delete_search_index - Band.create_search_index - User.delete_search_index - User.create_search_index end @@ -40,17 +36,7 @@ describe Search do it "search for band & musician " do create_peachy_data - User.search_index.refresh - Band.search_index.refresh - - assert_peachy_data - end - - it "validates rebuild_indexes method of TireTasks" do - create_peachy_data - - TireTasks.rebuild_indexes - assert_peachy_data end + end diff --git a/spec/jam_ruby/models/tire_search_spec.rb b/spec/jam_ruby/models/tire_search_spec.rb deleted file mode 100644 index efc42c22d..000000000 --- a/spec/jam_ruby/models/tire_search_spec.rb +++ /dev/null @@ -1,142 +0,0 @@ -require 'spec_helper' - -# these tests help verify tire integration -describe "tire search" do - - before(:each) do - Band.delete_search_index - Band.create_search_index - User.delete_search_index - User.create_search_index - end - - - it "full search for empty indexes" do - s = Tire.search ['test-jamruby-users', 'test-jamruby-bands'], :load => true do - query { string '*' } - end - - s.results.length.should == 0 - end - - it "full search for single user" do - @user = FactoryGirl.create(:user, first_name: "User", last_name: "One", email: "user@example.com", musician: true, - city: "Apex", state: "NC", country: "USA") - - User.search_index.refresh - - s = Tire.search ['test-jamruby-users', 'test-jamruby-bands'], :load => true do - query { string 'user' } - end - - s.results.length.should == 1 - result = s.results[0] - result.should be_a_kind_of User - result.id.should == @user.id - end - - it "full search for single band" do - @band = FactoryGirl.create(:band, name: "Example Band", website: "www.bands.com", biography: "zomg we rock", - city: "Apex", state: "NC", country: "USA") - - Band.search_index.refresh - - s = Tire.search ['test-jamruby-users', 'test-jamruby-bands'], :load => true do - query { string 'example' } - end - - s.results.length.should == 1 - result = s.results[0] - result.should be_a_kind_of Band - result.id.should == @band.id - end - - it "full search for a band & user" do - @user = FactoryGirl.create(:user, first_name: "Peach", last_name: "Foo", email: "user@example.com", musician: true, city: "Apex", state: "NC", country: "USA") - @band = FactoryGirl.create(:band, name: "Peach pit", website: "www.bands.com", biography: "zomg we rock", city: "Apex", state: "NC", country: "USA") - - User.search_index.refresh - Band.search_index.refresh - - s = Tire.search ['test-jamruby-users', 'test-jamruby-bands'], :load => true do - query { string 'peach' } - sort { by [:_score] } - end - - s.results.length.should == 2 - result = s.results[0] - result.should be_a_kind_of Band - result.id.should == @band.id - result = s.results[1] - result.should be_a_kind_of User - result.id.should == @user.id - end - - - it "pagination" do - @user = FactoryGirl.create(:user, first_name: "Peach", last_name: "foo", email: "user@example.com", musician: true, city: "Apex", state: "NC", country: "USA") - @band = FactoryGirl.create(:band, name: "Peach pit", website: "www.bands.com", biography: "zomg we rock", city: "Apex", state: "NC", country: "USA") - - User.search_index.refresh - Band.search_index.refresh - - s = Tire.search ['test-jamruby-users', 'test-jamruby-bands'], :load => true do - query { string 'peach' } - sort { by [:_score] } - from 0 - size 1 - end - - s.results.length.should == 1 - result = s.results[0] - result.should be_a_kind_of Band - result.id.should == @band.id - - s = Tire.search ['test-jamruby-users', 'test-jamruby-bands'], :load => true do - query { string 'peach' } - sort { by [:_score] } - from 1 - size 2 - end - - s.results.length.should == 1 - result = s.results[0] - result.should be_a_kind_of User - result.id.should == @user.id - end - - it "should fail to search deleted index" do - pending "until figure out how to stop tire from writing to stdout when a search of deleted index occurs" - - sleep 1 # https://jamkazam.atlassian.net/browse/VRFS-69 - s = Tire.search ['test-jamruby-users'], :search_type => 'count', :query => {"match_all" => {}} do - - end - - s.results.total.should == 0 - - @user = FactoryGirl.create(:user, first_name: "Peach", last_name: "Pit", email: "user@example.com", musician: true) - User.search_index.refresh - sleep 1 # https://jamkazam.atlassian.net/browse/VRFS-69 - - s = Tire.search ['test-jamruby-users'], :search_type => 'count', :query => {"match_all" => {}} do - - end - - s.results.total.should == 1 - - User.delete_search_index - - s = Tire.search ['test-jamruby-users'], :search_type => 'count', :query => {"match_all" => {}} do - - end - - expect {s.results.total}.to raise_error(Tire::Search::SearchRequestFailed) - begin - s.results.total - false.should be_true # should not get here - rescue Tire::Search::SearchRequestFailed => srf - srf.to_s.include?("IndexMissingException").should be_true - end - end -end diff --git a/spec/jam_ruby/models/user_search_spec.rb b/spec/jam_ruby/models/user_search_spec.rb index 35c3e806a..f77d452b0 100644 --- a/spec/jam_ruby/models/user_search_spec.rb +++ b/spec/jam_ruby/models/user_search_spec.rb @@ -3,21 +3,15 @@ require 'spec_helper' describe User do before(:each) do - User.delete_search_index - User.create_search_index - @user = FactoryGirl.create(:user, first_name: "Example", last_name: "User", email: "user@example.com", password: "foobar", password_confirmation: "foobar", musician: true, email_confirmed: true, city: "Apex", state: "NC", country: "USA") - # you have to poke elasticsearch because it will batch requests internally for a second - User.search_index.refresh end it "should allow search of one user" do ws = User.search("Example User") - ws.results.length.should == 1 - user_result = ws.results[0] - user_result._type.should == "jam_ruby/user" + ws.length.should == 1 + user_result = ws[0] user_result.first_name.should == @user.first_name user_result.last_name.should == @user.last_name user_result.id.should == @user.id @@ -27,34 +21,32 @@ describe User do it "should delete user" do ws = User.search("Example User") - ws.results.length.should == 1 - user_result = ws.results[0] + ws.length.should == 1 + user_result = ws[0] user_result.id.should == @user.id - @user.destroy # delete doesn't work; you have to use destroy. - User.search_index.refresh - + @user.destroy + ws = User.search("Example User") - ws.results.length.should == 0 + ws.length.should == 0 end it "should update user" do ws = User.search("Example User") - ws.results.length.should == 1 - user_result = ws.results[0] + ws.length.should == 1 + user_result = ws[0] user_result.id.should == @user.id @user.first_name = "bonus-junk" @user.last_name = "more-junk" @user.save - User.search_index.refresh ws = User.search("Example User") - ws.results.length.should == 1 + ws.length.should == 0 ws = User.search("Bonus") - ws.results.length.should == 1 - user_result = ws.results[0] + ws.length.should == 1 + user_result = ws[0] user_result.id.should == @user.id user_result.first_name.should == "bonus-junk" end @@ -63,10 +55,9 @@ describe User do @user2 = FactoryGirl.create(:user, first_name: "peaches", last_name: "test", email: "peach@example.com", password: "foobar", password_confirmation: "foobar", musician: true, email_confirmed: true, city: "Apex", state: "NC", country: "USA") - User.search_index.refresh ws = User.search("pea") - ws.results.length.should == 1 - user_result = ws.results[0] + ws.length.should == 1 + user_result = ws[0] user_result.id.should == @user2.id end @@ -74,18 +65,16 @@ describe User do @user3 = FactoryGirl.create(:user, first_name: "unconfirmed", last_name: "unconfirmed", email: "unconfirmed@example.com", password: "foobar", password_confirmation: "foobar", musician: true, email_confirmed: false, city: "Apex", state: "NC", country: "USA") - User.search_index.refresh ws = User.search("unconfirmed") - ws.results.length.should == 0 + ws.length.should == 0 # Ok, confirm the user, and see them show up @user3.email_confirmed = true @user3.save - User.search_index.refresh - + ws = User.search("unconfirmed") - ws.results.length.should == 1 - user_result = ws.results[0] + ws.length.should == 1 + user_result = ws[0] user_result.id.should == @user3.id end end \ No newline at end of file From 41781e8121a100729e6a4c56ee25944a8db87eca Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 13 Jan 2013 23:03:15 -0600 Subject: [PATCH 140/274] * VRFS-217 a test I should have had all along --- spec/jam_ruby/models/band_search_spec.rb | 44 +++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/spec/jam_ruby/models/band_search_spec.rb b/spec/jam_ruby/models/band_search_spec.rb index 1bec62448..99af288f8 100644 --- a/spec/jam_ruby/models/band_search_spec.rb +++ b/spec/jam_ruby/models/band_search_spec.rb @@ -10,7 +10,7 @@ describe User do end - it "should allow search of one band" do + it "should allow search of one band with an exact match" do ws = Band.search("Example Band") ws.length.should == 1 band_result = ws[0] @@ -19,6 +19,48 @@ describe User do band_result.location.should == @band.location end + it "should allow search of one band with partial matches" do + ws = Band.search("Ex") + ws.length.should == 1 + ws[0].id.should == @band.id + + ws = Band.search("Exa") + ws.length.should == 1 + ws[0].id.should == @band.id + + ws = Band.search("Exam") + ws.length.should == 1 + ws[0].id.should == @band.id + + ws = Band.search("Examp") + ws.length.should == 1 + ws[0].id.should == @band.id + + ws = Band.search("Exampl") + ws.length.should == 1 + ws[0].id.should == @band.id + + ws = Band.search("Example") + ws.length.should == 1 + ws[0].id.should == @band.id + + ws = Band.search("Ba") + ws.length.should == 1 + ws[0].id.should == @band.id + + ws = Band.search("Ban") + ws.length.should == 1 + ws[0].id.should == @band.id + end + + it "should not match mid-word searchs" do + ws = Band.search("xa") + ws.length.should == 0 + + ws = Band.search("le") + ws.length.should == 0 + end + it "should delete band" do ws = Band.search("Example Band") ws.length.should == 1 From 60366d5c0e74b60cf625511074474bbdfcd58e82 Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Mon, 14 Jan 2013 18:13:45 -0800 Subject: [PATCH 141/274] more work --- Gemfile | 3 +- config/aws.yml | 13 ++++++ lib/jam_ruby.rb | 1 + lib/jam_ruby/models/instrument.rb | 3 +- lib/jam_ruby/models/saved_track.rb | 50 ++++++++++++++++++++++++ lib/jam_ruby/models/user.rb | 3 ++ spec/factories.rb | 11 ++++++ spec/jam_ruby/models/saved_track_spec.rb | 25 ++++++++++++ 8 files changed, 107 insertions(+), 2 deletions(-) create mode 100644 config/aws.yml create mode 100644 lib/jam_ruby/models/saved_track.rb create mode 100644 spec/jam_ruby/models/saved_track_spec.rb diff --git a/Gemfile b/Gemfile index 3f52bc713..b52b8675c 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,6 @@ #ruby=1.9.3 source 'https://rubygems.org' -source 'https://jamjam:blueberryjam@www.jamkazam.com/gems/' +#source 'https://jamjam:blueberryjam@www.jamkazam.com/gems/' # Look for $WORKSPACE, otherwise use "workspace" as dev path. workspace = ENV["WORKSPACE"] || "~/workspace" @@ -18,6 +18,7 @@ gem 'amqp' gem 'will_paginate' gem 'actionmailer' gem 'sendgrid' +gem 'aws-sdk' if devenv gem 'jam_db', :path=> "#{workspace}/jam-db/target/ruby_package" diff --git a/config/aws.yml b/config/aws.yml new file mode 100644 index 000000000..f05a2d560 --- /dev/null +++ b/config/aws.yml @@ -0,0 +1,13 @@ +# Just like the config/database.yml this file requires an entry for each environment +# # http://aws.amazon.com/security-credentials +development: + access_key_id: AKIAJEPHN6RUIUZKEQDA + secret_access_key: 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 + +test: + access_key_id: AKIAJEPHN6RUIUZKEQDA + secret_access_key: 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 + +production: + access_key_id: AKIAJEPHN6RUIUZKEQDA + secret_access_key: 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 diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index e47e8dbf6..80a53379a 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -49,6 +49,7 @@ require "jam_ruby/models/user_favorite" require "jam_ruby/models/search" require "jam_ruby/models/recording" require "jam_ruby/models/musician_recording" +require "jam_ruby/models/saved_track" include Jampb diff --git a/lib/jam_ruby/models/instrument.rb b/lib/jam_ruby/models/instrument.rb index 12f56a722..2f95d2707 100644 --- a/lib/jam_ruby/models/instrument.rb +++ b/lib/jam_ruby/models/instrument.rb @@ -7,8 +7,9 @@ module JamRuby has_many :musician_instruments, :class_name => "JamRuby::MusicianInstrument" has_many :users, :through => :musician_instruments, :class_name => "JamRuby::User" has_many :tracks, :class_name => "JamRuby::Track", :inverse_of => :instrument + has_many :saved_tracks, :class_name => "JamRuby::SavedTrack", :inverse_of => :instrument # music sessions has_and_belongs_to_many :music_sessions, :class_name => "JamRuby::MusicSession", :join_table => "genres_music_sessions" end -end \ No newline at end of file +end diff --git a/lib/jam_ruby/models/saved_track.rb b/lib/jam_ruby/models/saved_track.rb new file mode 100644 index 000000000..e0744f211 --- /dev/null +++ b/lib/jam_ruby/models/saved_track.rb @@ -0,0 +1,50 @@ +module JamRuby + class SavedTrack < ActiveRecord::Base + + self.table_name = "saved_tracks" + + self.primary_key = 'id' + + SOUND = %w(mono stereo) + + belongs_to :user, :class_name => "JamRuby::User", :inverse_of => :saved_tracks + belongs_to :instrument, :class_name => "JamRuby::Instrument" + + validates :sound, :inclusion => {:in => SOUND} + + # Copy an ephemeral track to create a saved one. Some fields are ok with defaults + def self.create_from_track(track) + saved_track = self.new + saved_track.user_id = track.connection.user.id + saved_track.instrument_id = track.instrument_id + saved_track.sound = track.sound + saved_track.save + saved_track + end + +=begin + def self.save(id, connection_id, instrument_id, sound) + if id.nil? + track = Track.new() + track.connection_id = connection_id + else + track = Track.find(id) + end + + unless instrument_id.nil? + track.instrument_id = instrument_id + end + + unless sound.nil? + track.sound = sound + end + + track.updated_at = Time.now.getutc + track.save + return track + end +=end + + + end +end diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index e6e8951b7..7645c9504 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -80,6 +80,9 @@ module JamRuby # session history has_many :music_session_histories, :foreign_key => "user_id", :class_name => "JamRuby::MusicSessionHistory" + # saved tracks + has_many :saved_tracks, :foreign_key => "user_id", :class_name => "JamRuby::SavedTrack", :inverse_of => :user + # This causes the authenticate method to be generated (among other stuff) has_secure_password diff --git a/spec/factories.rb b/spec/factories.rb index 4a1499fbd..92ed500ba 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -57,4 +57,15 @@ FactoryGirl.define do factory :join_request, :class => JamRuby::JoinRequest do text 'let me in to the session!' end + + factory :track, :class => JamRuby::Track do + sound "mono" + + end + + factory :saved_track, :class => JamRuby::SavedTrack do + end + + factory :instrument, :class => JamRuby::Instrument do + end end diff --git a/spec/jam_ruby/models/saved_track_spec.rb b/spec/jam_ruby/models/saved_track_spec.rb new file mode 100644 index 000000000..9bd55404d --- /dev/null +++ b/spec/jam_ruby/models/saved_track_spec.rb @@ -0,0 +1,25 @@ +require 'spec_helper' + +describe SavedTrack 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) + end + + describe "Saved track" do + it "should copy from a regular track properly" do + @saved_track = SavedTrack.create_from_track(@track) + + @saved_track.user.id.should == @track.connection.user.id + @saved_track.instrument.id.should == @track.instrument.id + @saved_track.next_part_to_upload.should == 0 + @saved_track.fully_uploaded.should == false + end + end + +end + + From f1dc45bc0be4b013357756483deecc1b44f8c9ce Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Tue, 15 Jan 2013 14:54:17 -0800 Subject: [PATCH 142/274] more work --- spec/jam_ruby/models/saved_track_spec.rb | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/spec/jam_ruby/models/saved_track_spec.rb b/spec/jam_ruby/models/saved_track_spec.rb index 9bd55404d..53256b5e0 100644 --- a/spec/jam_ruby/models/saved_track_spec.rb +++ b/spec/jam_ruby/models/saved_track_spec.rb @@ -9,15 +9,19 @@ describe SavedTrack do @track = FactoryGirl.create(:track, :connection => @connection, :instrument => @instrument) end - describe "Saved track" do - it "should copy from a regular track properly" do - @saved_track = SavedTrack.create_from_track(@track) + it "should copy from a regular track properly" do + @saved_track = SavedTrack.create_from_track(@track) + + @saved_track.user.id.should == @track.connection.user.id + @saved_track.instrument.id.should == @track.instrument.id + @saved_track.next_part_to_upload.should == 0 + @saved_track.fully_uploaded.should == false + end + + it "should update the next part to upload properly" + @saved_track = SavedTrack.create_from_track(@track) - @saved_track.user.id.should == @track.connection.user.id - @saved_track.instrument.id.should == @track.instrument.id - @saved_track.next_part_to_upload.should == 0 - @saved_track.fully_uploaded.should == false - end + end end From 9ae702eaaf731703a8d2d7ca2c7b05f955d4df2a Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Wed, 16 Jan 2013 16:38:57 -0800 Subject: [PATCH 143/274] more work --- Gemfile | 4 +- config/aws.yml | 14 ++--- lib/jam_ruby/models/saved_track.rb | 77 +++++++++++++++++------- spec/jam_ruby/models/saved_track_spec.rb | 17 +++++- 4 files changed, 78 insertions(+), 34 deletions(-) diff --git a/Gemfile b/Gemfile index b52b8675c..8ec3126a6 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,6 @@ #ruby=1.9.3 source 'https://rubygems.org' -#source 'https://jamjam:blueberryjam@www.jamkazam.com/gems/' +source 'https://jamjam:blueberryjam@www.jamkazam.com/gems/' # Look for $WORKSPACE, otherwise use "workspace" as dev path. workspace = ENV["WORKSPACE"] || "~/workspace" @@ -18,7 +18,7 @@ gem 'amqp' gem 'will_paginate' gem 'actionmailer' gem 'sendgrid' -gem 'aws-sdk' +gem 'aws-sdk', '1.8.0' if devenv gem 'jam_db', :path=> "#{workspace}/jam-db/target/ruby_package" diff --git a/config/aws.yml b/config/aws.yml index f05a2d560..6fc2c8fc4 100644 --- a/config/aws.yml +++ b/config/aws.yml @@ -1,13 +1,11 @@ -# Just like the config/database.yml this file requires an entry for each environment -# # http://aws.amazon.com/security-credentials development: - access_key_id: AKIAJEPHN6RUIUZKEQDA - secret_access_key: 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 + access_key_id: AKIAIFFBNBRQG5YQ5WHA + secret_access_key: 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 test: - access_key_id: AKIAJEPHN6RUIUZKEQDA - secret_access_key: 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 + access_key_id: AKIAIFFBNBRQG5YQ5WHA + secret_access_key: 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 production: - access_key_id: AKIAJEPHN6RUIUZKEQDA - secret_access_key: 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 + access_key_id: AKIAIFFBNBRQG5YQ5WHA + secret_access_key: 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 diff --git a/lib/jam_ruby/models/saved_track.rb b/lib/jam_ruby/models/saved_track.rb index e0744f211..eacc369b7 100644 --- a/lib/jam_ruby/models/saved_track.rb +++ b/lib/jam_ruby/models/saved_track.rb @@ -22,29 +22,64 @@ module JamRuby saved_track end -=begin - def self.save(id, connection_id, instrument_id, sound) - if id.nil? - track = Track.new() - track.connection_id = connection_id - else - track = Track.find(id) - end - - unless instrument_id.nil? - track.instrument_id = instrument_id - end - - unless sound.nil? - track.sound = sound - end - - track.updated_at = Time.now.getutc - track.save - return track + def upload_start + self.upload_id = s3_bucket.objects[filename].multipart_upload.id + save + end + + def upload_sign(filename, content_md5) + str_to_sign = "PUT\n#{content_md5}\n#{content_type}\n#{http_date_time}\n/#{aws_bucket}/#{filename}" + signature = Base64.encode64(HMAC::SHA1.digest(aws_secret_key, str_to_sign)).chomp + { :signature => signature, :datetime => http_date_time, :upload_id => upload_id } + end + + + def upload_part_complete(part) + raise JamRuby::JamArgumentError unless part == next_part_to_upload + self.next_part_to_upload = part + 1 + save + end + + def upload_complete + s3_bucket.objects[filename].multipart_uploads[self.upload_id].upload_complete(:remote_parts) + self.fully_uploaded = true + save end -=end + # Format: "recording_#{saved_track_id}" + # File extension is irrelevant actually. + def self.filename_to_saved_track_id(filename) + matches = /^recording_([\w-]+)$/.match(filename) + return nil unless matches && matches.length > 1 + matches[1] + end + + private + + def s3_bucket + @s3 ||= AWS::S3.new + @s3.buckets[aws_bucket] + end + + def filename + "recording_#{self.id}" + end + + def aws_bucket + "jamkazam-dev" + end + + def aws_secret_key + "XLq2mpJHNyA0bN7GBSdYyF/pWjfzGkDx92b1C+Wv" + end + + def content_type + "application/octet-stream" + end + + def http_date_time + Time.now.strftime("%a, %d %b %Y %H:%M:%S %z") + end end end diff --git a/spec/jam_ruby/models/saved_track_spec.rb b/spec/jam_ruby/models/saved_track_spec.rb index 53256b5e0..58cea6207 100644 --- a/spec/jam_ruby/models/saved_track_spec.rb +++ b/spec/jam_ruby/models/saved_track_spec.rb @@ -17,11 +17,22 @@ describe SavedTrack do @saved_track.next_part_to_upload.should == 0 @saved_track.fully_uploaded.should == false end - - it "should update the next part to upload properly" + + it "should update the next part to upload properly" do @saved_track = SavedTrack.create_from_track(@track) - + @saved_track.upload_part_complete(0) + @saved_track.upload_part_complete(1) + @saved_track.upload_part_complete(2) + @saved_track.next_part_to_upload.should == 3 + end + + it "should error if the wrong part is uploaded" do + @saved_track = SavedTrack.create_from_track(@track) + @saved_track.upload_part_complete(0) + @saved_track.upload_part_complete(1) + expect { @saved_track.upload_part_complete(3) }.to raise_error + @saved_track.next_part_to_upload.should == 2 end end From 5e31dd5a06dbb4378a1ad36bb552bd00a10f1a26 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Mon, 21 Jan 2013 00:51:31 -0500 Subject: [PATCH 144/274] VRFS-201 enhanced search feature to include friends --- lib/jam_ruby/models/friendship.rb | 27 +++++++++++++++++++++++++++ lib/jam_ruby/models/search.rb | 16 ++++++++++++---- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/lib/jam_ruby/models/friendship.rb b/lib/jam_ruby/models/friendship.rb index 2dd155bbb..729757de6 100644 --- a/lib/jam_ruby/models/friendship.rb +++ b/lib/jam_ruby/models/friendship.rb @@ -16,5 +16,32 @@ module JamRuby Friendship.create(:user_id => friend_id, :friend_id => user_id) end end + + def self.search(query, user_id, options = { :limit => 10 }) + # only issue search if at least 2 characters are specified + if query.nil? || query.length < 2 + return [] + end + + # create 'anded' statement + query = Search.create_tsquery(query) + + if query.nil? || query.length == 0 + return [] + end + + friends = Friendship.joins( + %Q{ + INNER JOIN + users + ON friendships.friend_id = users.id + WHERE friendships.user_id = '#{user_id}' + AND users.name_tsv @@ to_tsquery('jamenglish', '#{query}') + } + ) + + friends = friends.limit(options[:limit]) + return friends + end end end \ No newline at end of file diff --git a/lib/jam_ruby/models/search.rb b/lib/jam_ruby/models/search.rb index fef404205..be874e7e0 100644 --- a/lib/jam_ruby/models/search.rb +++ b/lib/jam_ruby/models/search.rb @@ -1,7 +1,7 @@ module JamRuby # not a active_record model; just a search result class Search - attr_accessor :bands, :musicians, :fans, :recordings + attr_accessor :bands, :musicians, :fans, :recordings, :friends LIMIT = 10 @@ -11,16 +11,22 @@ module JamRuby users = User.search(query, :limit => LIMIT) bands = Band.search(query, :limit => LIMIT) recordings = Recording.search(query, :limit => LIMIT) - return Search.new(users + bands + recordings) end + # performs a site-white search + def self.search(query, user_id) + friends = Friendship.search(query, user_id, :limit => LIMIT) + return Search.new(friends) + end + # search_results - results from a Tire search across band/user/recording def initialize(search_results) @bands = [] @musicians = [] @fans = [] @recordings = [] + @friends = [] if search_results.nil? return @@ -33,13 +39,15 @@ module JamRuby else @fans.push(result) end - elsif result.class == Band + elsif result.class == Band @bands.push(result) elsif result.class == Recording @recordings.push(result) + elsif result.class == Friendship + @friends.push(result.friend) else raise Exception, "unknown class #{result.class} returned in search results" - end + end end end From 81d37f13b945453892c8d742bcf66c1da7c1307d Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Mon, 21 Jan 2013 21:54:23 -0500 Subject: [PATCH 145/274] fix friend search issue --- lib/jam_ruby/models/search.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/jam_ruby/models/search.rb b/lib/jam_ruby/models/search.rb index be874e7e0..1c62cb204 100644 --- a/lib/jam_ruby/models/search.rb +++ b/lib/jam_ruby/models/search.rb @@ -14,8 +14,8 @@ module JamRuby return Search.new(users + bands + recordings) end - # performs a site-white search - def self.search(query, user_id) + # performs a friend search scoped to a specific user + def self.search_by_user(query, user_id) friends = Friendship.search(query, user_id, :limit => LIMIT) return Search.new(friends) end From 8a54a0a1167c29c9d21a52f18e45ab18bdd20af7 Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Tue, 22 Jan 2013 11:15:52 -0800 Subject: [PATCH 146/274] more work --- lib/jam_ruby/models/band.rb | 3 +- lib/jam_ruby/models/band_recording.rb | 14 ------ lib/jam_ruby/models/instrument.rb | 2 +- lib/jam_ruby/models/music_session.rb | 4 ++ .../{saved_track.rb => recorded_track.rb} | 21 ++++----- lib/jam_ruby/models/recording.rb | 43 ++++++++++++++++++- lib/jam_ruby/models/user.rb | 2 +- 7 files changed, 60 insertions(+), 29 deletions(-) delete mode 100644 lib/jam_ruby/models/band_recording.rb rename lib/jam_ruby/models/{saved_track.rb => recorded_track.rb} (79%) diff --git a/lib/jam_ruby/models/band.rb b/lib/jam_ruby/models/band.rb index 0eea4acdb..68165f67b 100644 --- a/lib/jam_ruby/models/band.rb +++ b/lib/jam_ruby/models/band.rb @@ -13,8 +13,7 @@ module JamRuby has_and_belongs_to_many :genres, :class_name => "JamRuby::Genre", :join_table => "bands_genres" # recordings - has_many :band_recordings, :class_name => "JamRuby::BandRecording", :foreign_key => "band_id" - has_many :recordings, :through => :band_recordings, :class_name => "JamRuby::Recording", :foreign_key => "recording_id" + has_many :recordings, :class_name => "JamRuby::Recording", :foreign_key => "band_id" # likers has_many :likers, :class_name => "JamRuby::BandLiker", :foreign_key => "band_id", :inverse_of => :band diff --git a/lib/jam_ruby/models/band_recording.rb b/lib/jam_ruby/models/band_recording.rb deleted file mode 100644 index 958dcd973..000000000 --- a/lib/jam_ruby/models/band_recording.rb +++ /dev/null @@ -1,14 +0,0 @@ -module JamRuby - class BandRecording < ActiveRecord::Base - - self.table_name = "bands_recordings" - - attr_accessible :band_id, :recording_id - - # bands - has_many :bands, :through => :band_recordings, :class_name => "JamRuby::Band" - - belongs_to :band, :class_name => "JamRuby::Band", :foreign_key => "band_id" - belongs_to :recording, :class_name => "JamRuby::Recording", :foreign_key => "recording_id" - end -end \ No newline at end of file diff --git a/lib/jam_ruby/models/instrument.rb b/lib/jam_ruby/models/instrument.rb index 2f95d2707..cd3699b26 100644 --- a/lib/jam_ruby/models/instrument.rb +++ b/lib/jam_ruby/models/instrument.rb @@ -7,7 +7,7 @@ module JamRuby has_many :musician_instruments, :class_name => "JamRuby::MusicianInstrument" has_many :users, :through => :musician_instruments, :class_name => "JamRuby::User" has_many :tracks, :class_name => "JamRuby::Track", :inverse_of => :instrument - has_many :saved_tracks, :class_name => "JamRuby::SavedTrack", :inverse_of => :instrument + has_many :recorded_tracks, :class_name => "JamRuby::RecordedTrack", :inverse_of => :instrument # music sessions has_and_belongs_to_many :music_sessions, :class_name => "JamRuby::MusicSession", :join_table => "genres_music_sessions" diff --git a/lib/jam_ruby/models/music_session.rb b/lib/jam_ruby/models/music_session.rb index f876f5c3a..7e621928f 100644 --- a/lib/jam_ruby/models/music_session.rb +++ b/lib/jam_ruby/models/music_session.rb @@ -160,6 +160,10 @@ module JamRuby def access? user return self.users.exists? user end + + def recording? + return self.recording + end def to_s return description diff --git a/lib/jam_ruby/models/saved_track.rb b/lib/jam_ruby/models/recorded_track.rb similarity index 79% rename from lib/jam_ruby/models/saved_track.rb rename to lib/jam_ruby/models/recorded_track.rb index eacc369b7..940ceff36 100644 --- a/lib/jam_ruby/models/saved_track.rb +++ b/lib/jam_ruby/models/recorded_track.rb @@ -1,25 +1,26 @@ module JamRuby - class SavedTrack < ActiveRecord::Base + class RecordedTrack < ActiveRecord::Base - self.table_name = "saved_tracks" + self.table_name = "recorded_tracks" self.primary_key = 'id' SOUND = %w(mono stereo) - belongs_to :user, :class_name => "JamRuby::User", :inverse_of => :saved_tracks + belongs_to :user, :class_name => "JamRuby::User", :inverse_of => :recorded_tracks + belongs_to :recording, :class_name => "JamRuby::Recording", :inverse_of => :recorded_tracks belongs_to :instrument, :class_name => "JamRuby::Instrument" validates :sound, :inclusion => {:in => SOUND} # Copy an ephemeral track to create a saved one. Some fields are ok with defaults def self.create_from_track(track) - saved_track = self.new - saved_track.user_id = track.connection.user.id - saved_track.instrument_id = track.instrument_id - saved_track.sound = track.sound - saved_track.save - saved_track + recorded_track = self.new + recorded_track.user_id = track.connection.user.id + recorded_track.instrument_id = track.instrument_id + recorded_track.sound = track.sound + recorded_track.save + recorded_track end def upload_start @@ -49,7 +50,7 @@ module JamRuby # Format: "recording_#{saved_track_id}" # File extension is irrelevant actually. - def self.filename_to_saved_track_id(filename) + def self.filename_to_recorded_track_id(filename) matches = /^recording_([\w-]+)$/.match(filename) return nil unless matches && matches.length > 1 matches[1] diff --git a/lib/jam_ruby/models/recording.rb b/lib/jam_ruby/models/recording.rb index e4d2aa7ad..7d3fed13d 100644 --- a/lib/jam_ruby/models/recording.rb +++ b/lib/jam_ruby/models/recording.rb @@ -4,7 +4,9 @@ module JamRuby self.primary_key = 'id' has_many :musician_recordings, :class_name => "JamRuby::MusicianRecording" - has_many :band_recordings, :class_name => "JamRuby::BandRecording" + belongs_to :band, :class_name => "JamRuby::Band", :inverse_of => :recording + + has_many :recorded_tracks, :class_name => "JamRuby::RecordedTrack", :foreign_key => :recording_id # genres has_and_belongs_to_many :genres, :class_name => "JamRuby::Genre", :join_table => "recordings_genres" @@ -19,6 +21,28 @@ module JamRuby return self.favorites.size end + # Start recording a session. + def self.start(music_session_id, creator_id) + music_session = MusicSession.find(music_session_id) + + if music_session.recording? + raise PermissionError, "the session is already being recorded" + end + + recording = Recording.new + + music_session.connections.each do |connection| + recording.musician_recordings << MusicianRecording.create(user_id: connection.user.id, recording_id: recording.id) + connection.tracks.each do |track| + RecordedTrack.create_from_track(track) + end + end + + recording.band_id = music_session.band_id + end + + + def self.search(query, options = { :limit => 10 }) # only issue search if at least 2 characters are specified @@ -36,8 +60,25 @@ module JamRuby return Recording.where("description_tsv @@ to_tsquery('jamenglish', ?)", query).limit(options[:limit]) end + + + + def self.save(id, is_public, description, genres, updater_id, owner_id, is_band) + # Spec: https://jamkazam.atlassian.net/wiki/display/PS/Product+Specification+-+Studio + # This is seriously wrong. A recording needs to be created from inside a session. I'm not sure who owns + # the recording, but I do know that it's affiliated with the session and then should be able to get the band id + # and probably all this other stuff (genre, etc) from that. I need to read the recording spec in more detail + # to figure that out. I'll read it on bart. + # + # The studio spec is the key here. A couple notes: + # * Multiple musicians are all associated with a recording -- everyone who is in the music session when the recording starts + # * Associating a band with the recording is optional + # * The recording needs to be associated with a music_session for sure. Probably also a set of tracks (NOT musicians). Then, on + # recording completion is when you have the track -> saved_track (rename this to recorded_track) transition. + # * The metadata in general is filled in *after* the recording ends. This is important because it means the save method below + # is sort of jacked. creator = User.find(updater_id) if is_band diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 7645c9504..7d455ff5b 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -81,7 +81,7 @@ module JamRuby has_many :music_session_histories, :foreign_key => "user_id", :class_name => "JamRuby::MusicSessionHistory" # saved tracks - has_many :saved_tracks, :foreign_key => "user_id", :class_name => "JamRuby::SavedTrack", :inverse_of => :user + has_many :recorded_tracks, :foreign_key => "user_id", :class_name => "JamRuby::RecordedTrack", :inverse_of => :user # This causes the authenticate method to be generated (among other stuff) has_secure_password From 2d697d029b137a6de1937d7847ef91f631f62aa1 Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Tue, 22 Jan 2013 14:03:23 -0800 Subject: [PATCH 147/274] more work --- lib/jam_ruby/models/musician_recording.rb | 14 -------------- lib/jam_ruby/models/recorded_track.rb | 3 ++- lib/jam_ruby/models/recording.rb | 14 ++++++++++---- lib/jam_ruby/models/user.rb | 4 ++-- 4 files changed, 14 insertions(+), 21 deletions(-) delete mode 100644 lib/jam_ruby/models/musician_recording.rb diff --git a/lib/jam_ruby/models/musician_recording.rb b/lib/jam_ruby/models/musician_recording.rb deleted file mode 100644 index 30293ce6c..000000000 --- a/lib/jam_ruby/models/musician_recording.rb +++ /dev/null @@ -1,14 +0,0 @@ -module JamRuby - class MusicianRecording < ActiveRecord::Base - - self.table_name = "musicians_recordings" - - attr_accessible :user_id, :recording_id - - # musicians - has_many :musicians, :through => :musician_recordings, :class_name => "JamRuby::User" - - belongs_to :user, :class_name => "JamRuby::User", :foreign_key => "user_id" - belongs_to :recording, :class_name => "JamRuby::Recording", :foreign_key => "recording_id" - end -end \ No newline at end of file diff --git a/lib/jam_ruby/models/recorded_track.rb b/lib/jam_ruby/models/recorded_track.rb index 940ceff36..64e9b5a4e 100644 --- a/lib/jam_ruby/models/recorded_track.rb +++ b/lib/jam_ruby/models/recorded_track.rb @@ -14,8 +14,9 @@ module JamRuby validates :sound, :inclusion => {:in => SOUND} # Copy an ephemeral track to create a saved one. Some fields are ok with defaults - def self.create_from_track(track) + def self.create_from_track(track, recording) recorded_track = self.new + recorded_track.recording = recording recorded_track.user_id = track.connection.user.id recorded_track.instrument_id = track.instrument_id recorded_track.sound = track.sound diff --git a/lib/jam_ruby/models/recording.rb b/lib/jam_ruby/models/recording.rb index 7d3fed13d..13d40f6fa 100644 --- a/lib/jam_ruby/models/recording.rb +++ b/lib/jam_ruby/models/recording.rb @@ -3,7 +3,8 @@ module JamRuby self.primary_key = 'id' - has_many :musician_recordings, :class_name => "JamRuby::MusicianRecording" + has_and_belongs_to_many :users, :class_name => "JamRuby::User" + belongs_to :owner, :class_name => "JamRuby::User", :inverse_of => :owned_recording belongs_to :band, :class_name => "JamRuby::Band", :inverse_of => :recording has_many :recorded_tracks, :class_name => "JamRuby::RecordedTrack", :foreign_key => :recording_id @@ -32,12 +33,17 @@ module JamRuby recording = Recording.new music_session.connections.each do |connection| - recording.musician_recordings << MusicianRecording.create(user_id: connection.user.id, recording_id: recording.id) + recording.users << connection.user connection.tracks.each do |track| - RecordedTrack.create_from_track(track) + RecordedTrack.create_from_track(track, recording) end end + + music_session.genres.each do |genre| + recording.genres << genre + end + # Note that I believe this can be nil. recording.band_id = music_session.band_id end @@ -189,4 +195,4 @@ module JamRuby end =end end - end \ No newline at end of file + end diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 7d455ff5b..cb5c128f2 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -24,8 +24,8 @@ module JamRuby has_many :bands, :through => :band_musicians, :class_name => "JamRuby::Band" # recordings - has_many :musician_recordings, :class_name => "JamRuby::MusicianRecording", :foreign_key => "user_id" - has_many :recordings, :through => :musician_recordings, :class_name => "JamRuby::Recording" + has_many :owned_recordings, :class_name => "JamRuby::Recording" + has_and_belongs_to_many :recordings, :class_name => "JamRuby::Recording" # user likers (a musician has likers and may have likes too; fans do not have likers) has_many :likers, :class_name => "JamRuby::UserLiker", :foreign_key => "user_id", :inverse_of => :user From 60d5964ecbd91d81385d2f344d99a8d92ffa3205 Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Wed, 23 Jan 2013 17:48:13 -0800 Subject: [PATCH 148/274] more work --- lib/jam_ruby/models/music_session.rb | 5 +-- lib/jam_ruby/models/recording.rb | 64 ++++++++++++++++++++-------- 2 files changed, 47 insertions(+), 22 deletions(-) diff --git a/lib/jam_ruby/models/music_session.rb b/lib/jam_ruby/models/music_session.rb index 7e621928f..7ff48724b 100644 --- a/lib/jam_ruby/models/music_session.rb +++ b/lib/jam_ruby/models/music_session.rb @@ -17,6 +17,7 @@ module JamRuby has_many :fan_invitations, :foreign_key => "music_session_id", :inverse_of => :music_session, :class_name => "JamRuby::FanInvitation" has_many :invited_fans, :through => :fan_invitations, :class_name => "JamRuby::User", :foreign_key => "receiver_id", :source => :receiver + has_one :recording, :class_name => "JamRuby::Recording", :inverse_of => :music_session belongs_to :band, :inverse_of => :music_sessions, :class_name => "JamRuby::Band", :foreign_key => "band_id" @@ -161,10 +162,6 @@ module JamRuby return self.users.exists? user end - def recording? - return self.recording - end - def to_s return description end diff --git a/lib/jam_ruby/models/recording.rb b/lib/jam_ruby/models/recording.rb index 13d40f6fa..c35434f93 100644 --- a/lib/jam_ruby/models/recording.rb +++ b/lib/jam_ruby/models/recording.rb @@ -6,6 +6,7 @@ module JamRuby has_and_belongs_to_many :users, :class_name => "JamRuby::User" belongs_to :owner, :class_name => "JamRuby::User", :inverse_of => :owned_recording belongs_to :band, :class_name => "JamRuby::Band", :inverse_of => :recording + belongs_to :music_session, :class_name => "JamRuby::MusicSession", :inverse_of => :recording has_many :recorded_tracks, :class_name => "JamRuby::RecordedTrack", :foreign_key => :recording_id @@ -24,30 +25,57 @@ module JamRuby # Start recording a session. def self.start(music_session_id, creator_id) - music_session = MusicSession.find(music_session_id) + # Use a transaction and lock to avoid races. + ActiveRecord::Base.transaction do + music_session = MusicSession.find(music_session_id, :lock => true) - if music_session.recording? - raise PermissionError, "the session is already being recorded" - end - - recording = Recording.new - - music_session.connections.each do |connection| - recording.users << connection.user - connection.tracks.each do |track| - RecordedTrack.create_from_track(track, recording) + if music_session.nil? + raise PermissionError, "the session has ended" end - end - music_session.genres.each do |genre| - recording.genres << genre - end + if music_session.recording + raise PermissionError, "the session is already being recorded" + end + + music_session.recording = true - # Note that I believe this can be nil. - recording.band_id = music_session.band_id + recording = Recording.new + recording.music_session = music_session + + music_session.connections.each do |connection| + recording.users << connection.user + connection.tracks.each do |track| + RecordedTrack.create_from_track(track, recording) + end + end + + music_session.genres.each do |genre| + recording.genres << genre + end + + # Note that I believe this can be nil. + recording.band_id = music_session.band_id + recording.save + + music_session.save + end end - + # Stop recording a session + def stop + # Use a transaction and lock to avoid races. + ActiveRecord::Base.transaction do + music_session = MusicSession.find(self.music_session_id, :lock => true) + if music_session.nil? + raise PermissionError, "the session has ended" + end + unless music_session.recording + raise PermissionError, "the session is not currently being recorded" + end + music_session.recording = false + music_session.save + end + end def self.search(query, options = { :limit => 10 }) From 6606094e405b1833d2cc0183a836c5740368fa94 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sat, 26 Jan 2013 19:13:01 -0600 Subject: [PATCH 149/274] * bumping activerecord up to latest --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 8ec3126a6..4c5750f6d 100644 --- a/Gemfile +++ b/Gemfile @@ -9,7 +9,7 @@ devenv = ENV["BUILD_NUMBER"].nil? # Jenkins sets a build number environment vari gem 'pg', '0.14.0', :platform => [:mri, :mswin, :mingw] gem 'jdbc_postgres', :platform => [:jruby] -gem 'activerecord', '3.2.7' +gem 'activerecord', '>=3.2.11' gem 'uuidtools', '2.1.2' gem 'bcrypt-ruby', '3.0.1' gem 'ruby-protocol-buffers', '1.2.2' From 5d1af73685e3c826bbc0eb5d3c668361c1d3a427 Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Tue, 29 Jan 2013 21:46:40 -0800 Subject: [PATCH 150/274] more work --- lib/jam_ruby.rb | 4 +- lib/jam_ruby/models/recorded_track.rb | 4 +- lib/jam_ruby/models/recording.rb | 26 ++++++++----- spec/factories.rb | 6 ++- spec/jam_ruby/models/recorded_track_spec.rb | 41 +++++++++++++++++++++ spec/jam_ruby/models/saved_track_spec.rb | 40 -------------------- 6 files changed, 66 insertions(+), 55 deletions(-) create mode 100644 spec/jam_ruby/models/recorded_track_spec.rb delete mode 100644 spec/jam_ruby/models/saved_track_spec.rb diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index 80a53379a..245c56017 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -25,7 +25,6 @@ require "jam_ruby/models/user" require "jam_ruby/models/user_authorization" require "jam_ruby/models/join_request" require "jam_ruby/models/band" -require "jam_ruby/models/band_recording" require "jam_ruby/models/band_invitation" require "jam_ruby/models/band_liker" require "jam_ruby/models/band_follower" @@ -48,8 +47,7 @@ require "jam_ruby/models/user_following" require "jam_ruby/models/user_favorite" require "jam_ruby/models/search" require "jam_ruby/models/recording" -require "jam_ruby/models/musician_recording" -require "jam_ruby/models/saved_track" +require "jam_ruby/models/recorded_track" include Jampb diff --git a/lib/jam_ruby/models/recorded_track.rb b/lib/jam_ruby/models/recorded_track.rb index 64e9b5a4e..9ef1fa501 100644 --- a/lib/jam_ruby/models/recorded_track.rb +++ b/lib/jam_ruby/models/recorded_track.rb @@ -17,8 +17,8 @@ module JamRuby def self.create_from_track(track, recording) recorded_track = self.new recorded_track.recording = recording - recorded_track.user_id = track.connection.user.id - recorded_track.instrument_id = track.instrument_id + recorded_track.user = track.connection.user + recorded_track.instrument = track.instrument recorded_track.sound = track.sound recorded_track.save recorded_track diff --git a/lib/jam_ruby/models/recording.rb b/lib/jam_ruby/models/recording.rb index c35434f93..e2a1f6b30 100644 --- a/lib/jam_ruby/models/recording.rb +++ b/lib/jam_ruby/models/recording.rb @@ -1,11 +1,11 @@ module JamRuby class Recording < ActiveRecord::Base - + self.primary_key = 'id' has_and_belongs_to_many :users, :class_name => "JamRuby::User" - belongs_to :owner, :class_name => "JamRuby::User", :inverse_of => :owned_recording - belongs_to :band, :class_name => "JamRuby::Band", :inverse_of => :recording + 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_many :recorded_tracks, :class_name => "JamRuby::RecordedTrack", :foreign_key => :recording_id @@ -24,7 +24,10 @@ module JamRuby end # Start recording a session. - def self.start(music_session_id, creator_id) + def self.start(music_session_id, owner, description) + + recording = nil + # Use a transaction and lock to avoid races. ActiveRecord::Base.transaction do music_session = MusicSession.find(music_session_id, :lock => true) @@ -36,11 +39,11 @@ module JamRuby if music_session.recording raise PermissionError, "the session is already being recorded" end - - music_session.recording = true recording = Recording.new + recording.description = description recording.music_session = music_session + recording.owner = owner music_session.connections.each do |connection| recording.users << connection.user @@ -54,11 +57,14 @@ module JamRuby end # Note that I believe this can be nil. - recording.band_id = music_session.band_id + recording.band = music_session.band recording.save + music_session.recording = recording music_session.save end + + recording end # Stop recording a session @@ -72,7 +78,7 @@ module JamRuby unless music_session.recording raise PermissionError, "the session is not currently being recorded" end - music_session.recording = false + music_session.recording = nil music_session.save end end @@ -99,7 +105,7 @@ module JamRuby def self.save(id, is_public, description, genres, updater_id, owner_id, is_band) - + return nil # Spec: https://jamkazam.atlassian.net/wiki/display/PS/Product+Specification+-+Studio # This is seriously wrong. A recording needs to be created from inside a session. I'm not sure who owns # the recording, but I do know that it's affiliated with the session and then should be able to get the band id @@ -113,6 +119,7 @@ module JamRuby # recording completion is when you have the track -> saved_track (rename this to recorded_track) transition. # * The metadata in general is filled in *after* the recording ends. This is important because it means the save method below # is sort of jacked. +=begin creator = User.find(updater_id) if is_band @@ -175,6 +182,7 @@ module JamRuby end return recording +=end end private diff --git a/spec/factories.rb b/spec/factories.rb index 92ed500ba..696694503 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -63,9 +63,13 @@ FactoryGirl.define do end - factory :saved_track, :class => JamRuby::SavedTrack do + factory :recorded_track, :class => JamRuby::RecordedTrack do end factory :instrument, :class => JamRuby::Instrument do end + + factory :recording, :class => JamRuby::Recording do + + end end diff --git a/spec/jam_ruby/models/recorded_track_spec.rb b/spec/jam_ruby/models/recorded_track_spec.rb new file mode 100644 index 000000000..b6bd0a450 --- /dev/null +++ b/spec/jam_ruby/models/recorded_track_spec.rb @@ -0,0 +1,41 @@ +require 'spec_helper' + +describe RecordedTrack 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) + @recording = FactoryGirl.create(:recording, :description => 'description', :owner => @user) + end + + it "should copy from a regular track properly" do + @recorded_track = RecordedTrack.create_from_track(@track, @recording) + + @recorded_track.user.id.should == @track.connection.user.id + @recorded_track.instrument.id.should == @track.instrument.id + @recorded_track.next_part_to_upload.should == 0 + @recorded_track.fully_uploaded.should == false + end + + it "should update the next part to upload properly" do + @recorded_track = RecordedTrack.create_from_track(@track, @recording) + @recorded_track.upload_part_complete(0) + @recorded_track.upload_part_complete(1) + @recorded_track.upload_part_complete(2) + @recorded_track.next_part_to_upload.should == 3 + end + + + it "should error if the wrong part is uploaded" do + @recorded_track = RecordedTrack.create_from_track(@track, @recording) + @recorded_track.upload_part_complete(0) + @recorded_track.upload_part_complete(1) + expect { @recorded_track.upload_part_complete(3) }.to raise_error + @recorded_track.next_part_to_upload.should == 2 + end + +end + + diff --git a/spec/jam_ruby/models/saved_track_spec.rb b/spec/jam_ruby/models/saved_track_spec.rb deleted file mode 100644 index 58cea6207..000000000 --- a/spec/jam_ruby/models/saved_track_spec.rb +++ /dev/null @@ -1,40 +0,0 @@ -require 'spec_helper' - -describe SavedTrack 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) - end - - it "should copy from a regular track properly" do - @saved_track = SavedTrack.create_from_track(@track) - - @saved_track.user.id.should == @track.connection.user.id - @saved_track.instrument.id.should == @track.instrument.id - @saved_track.next_part_to_upload.should == 0 - @saved_track.fully_uploaded.should == false - end - - it "should update the next part to upload properly" do - @saved_track = SavedTrack.create_from_track(@track) - @saved_track.upload_part_complete(0) - @saved_track.upload_part_complete(1) - @saved_track.upload_part_complete(2) - @saved_track.next_part_to_upload.should == 3 - end - - - it "should error if the wrong part is uploaded" do - @saved_track = SavedTrack.create_from_track(@track) - @saved_track.upload_part_complete(0) - @saved_track.upload_part_complete(1) - expect { @saved_track.upload_part_complete(3) }.to raise_error - @saved_track.next_part_to_upload.should == 2 - end - -end - - From 33c3a54d129a3c138495d5334480b46ad1ba46fd Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Tue, 29 Jan 2013 21:46:56 -0800 Subject: [PATCH 151/274] more work --- spec/jam_ruby/models/recording_spec.rb | 60 ++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 spec/jam_ruby/models/recording_spec.rb diff --git a/spec/jam_ruby/models/recording_spec.rb b/spec/jam_ruby/models/recording_spec.rb new file mode 100644 index 000000000..4ead8d49e --- /dev/null +++ b/spec/jam_ruby/models/recording_spec.rb @@ -0,0 +1,60 @@ +require 'spec_helper' + +describe Recording 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 + end + + it "should not start a recording if the music session doesnt exist" do + expect { Recording.start("bad_music_session_id", @user, "Description") }.to raise_error + end + + it "should set up the recording properly when recording is started with 1 user in the session" do + @music_session.recording.should == nil + @recording = Recording.start(@music_session.id, @user, "Description") + @music_session.reload + @music_session.recording.should == @recording + @recording.owner_id.should == @user.id + @recording.genres.should == @music_session.genres + @recording.users.length.should == 1 + @recording.users.first.should == @user + @recording.description.should == "Description" + + @recorded_tracks = RecordedTrack.where(:recording_id => @recording.id) + @recorded_tracks.length.should == 1 + @recorded_tracks.first.instrument_id == @track.instrument_id + @recorded_tracks.first.user_id == @track.connection.user_id + end + + it "should not start a recording if the session is already being recorded" do + Recording.start(@music_session.id, @user, "Description") + expect { Recording.start(@music_session.id, @user, "Description") }.to raise_error + end + + it "should return the state to normal properly when you stop a recording" do + @recording = Recording.start(@music_session.id, @user, "Description") + @recording.stop + @music_session.reload + @music_session.recording.should == nil + @recording.reload + @recording.music_session.should == nil + end + + + it "should error when you stop a recording twice" do + @recording = Recording.start(@music_session.id, @user, "Description") + @recording.stop + expect { @recording.stop }.to raise_error + end + + +end + + From ceaf4119afdb2998ec15342ffeb5fc12cd971d14 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Wed, 30 Jan 2013 10:43:01 -0500 Subject: [PATCH 152/274] added participants as filter criteria / fixed bug with genre filter --- lib/jam_ruby/models/music_session.rb | 7 ++++--- spec/jam_ruby/models/music_session_spec.rb | 24 +++++++++++----------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/lib/jam_ruby/models/music_session.rb b/lib/jam_ruby/models/music_session.rb index f876f5c3a..32cd4d030 100644 --- a/lib/jam_ruby/models/music_session.rb +++ b/lib/jam_ruby/models/music_session.rb @@ -38,7 +38,7 @@ module JamRuby # This is a little confusing. You can specify *BOTH* friends_only and my_bands_only to be true # If so, then it's an OR condition. If both are false, you can get sessions with anyone. - def self.index(current_user, genres = nil, friends_only = false, my_bands_only = false) + def self.index(current_user, participants = nil, genres = nil, friends_only = false, my_bands_only = false) query = MusicSession .joins( @@ -88,8 +88,9 @@ module JamRuby invitations.id IS NOT NULL } ) - - query = query.joins(:genres).where("genres.id" => genres) unless genres.nil? + + query = query.where("connections.user_id" => participants.split(',')) unless participants.nil? + query = query.joins(:genres).where("genres.id" => genres.split(',')) unless genres.nil? if my_bands_only query = query.joins( diff --git a/spec/jam_ruby/models/music_session_spec.rb b/spec/jam_ruby/models/music_session_spec.rb index a865124a8..a4dc8d611 100644 --- a/spec/jam_ruby/models/music_session_spec.rb +++ b/spec/jam_ruby/models/music_session_spec.rb @@ -154,7 +154,7 @@ describe MusicSession do session = FactoryGirl.create(:music_session, :creator => creator, :description => "Session", :genres => [genre]) user = FactoryGirl.create(:user) - music_sessions = MusicSession.index(user, [genre.id]) + music_sessions = MusicSession.index(user, nil, [genre.id]) music_sessions.length.should == 1 end @@ -165,7 +165,7 @@ describe MusicSession do session = FactoryGirl.create(:music_session, :creator => creator, :description => "Session", :genres => [genre1]) user = FactoryGirl.create(:user) - music_sessions = MusicSession.index(user, [genre2.id]) + music_sessions = MusicSession.index(user, nil, [genre2.id]) music_sessions.length.should == 0 end @@ -174,7 +174,7 @@ describe MusicSession do session = FactoryGirl.create(:music_session, :creator => creator, :description => "Session") user = FactoryGirl.create(:user) - music_sessions = MusicSession.index(user, nil, true) + music_sessions = MusicSession.index(user, nil, nil, true) music_sessions.length.should == 0 end @@ -186,13 +186,13 @@ describe MusicSession do FactoryGirl.create(:friendship, :user => user, :friend => creator) FactoryGirl.create(:connection, :user => creator, :music_session => session) - music_sessions = MusicSession.index(user, nil) + music_sessions = MusicSession.index(user, nil, nil) music_sessions.length.should == 1 - music_sessions = MusicSession.index(user, nil, true) + music_sessions = MusicSession.index(user, nil, nil, true) music_sessions.length.should == 1 - music_sessions = MusicSession.index(user, nil, false, true) + music_sessions = MusicSession.index(user, nil, nil, false, true) music_sessions.length.should == 0 - music_sessions = MusicSession.index(user, nil, true, true) + music_sessions = MusicSession.index(user, nil, nil, true, true) music_sessions.length.should == 1 end @@ -201,7 +201,7 @@ describe MusicSession do session = FactoryGirl.create(:music_session, :creator => creator, :description => "Session") user = FactoryGirl.create(:user) - music_sessions = MusicSession.index(user, nil, false, true) + music_sessions = MusicSession.index(user, nil, nil, false, true) music_sessions.length.should == 0 end @@ -213,13 +213,13 @@ describe MusicSession do FactoryGirl.create(:band_musician, :band => band, :user => creator) FactoryGirl.create(:band_musician, :band => band, :user => user) - music_sessions = MusicSession.index(user, nil) + music_sessions = MusicSession.index(user, nil, nil) music_sessions.length.should == 1 - music_sessions = MusicSession.index(user, nil, true) + music_sessions = MusicSession.index(user, nil, nil, true) music_sessions.length.should == 0 - music_sessions = MusicSession.index(user, nil, false, true) + music_sessions = MusicSession.index(user, nil, nil, false, true) music_sessions.length.should == 1 - music_sessions = MusicSession.index(user, nil, true, true) + music_sessions = MusicSession.index(user, nil, nil, true, true) music_sessions.length.should == 1 end From a3e93a208d1b43f8331badfe1fd21731499cfbcc Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Wed, 30 Jan 2013 09:57:12 -0800 Subject: [PATCH 153/274] minor update --- lib/jam_ruby/models/recording.rb | 23 +++++++++++++++++++++++ spec/jam_ruby/models/recording_spec.rb | 24 ++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/lib/jam_ruby/models/recording.rb b/lib/jam_ruby/models/recording.rb index e2a1f6b30..8c084a680 100644 --- a/lib/jam_ruby/models/recording.rb +++ b/lib/jam_ruby/models/recording.rb @@ -64,6 +64,16 @@ module JamRuby music_session.save end + + # FIXME: + # NEED TO SEND NOTIFICATION TO ALL USERS IN THE SESSION THAT RECORDING HAS STARTED HERE. + # I'LL STUB IT A BIT. NOTE THAT I REDO THE FIND HERE BECAUSE I DON'T WANT TO SEND THESE + # NOTIFICATIONS WHILE THE DB ROW IS LOCKED + music_session = MusicSession.find(music_session_id) + music_session.connections.each do |connection| + # connection.notify_recording_has_started + end + recording end @@ -82,6 +92,19 @@ module JamRuby music_session.save end end + + # Update the metadata for the recording + def update(description, genres) + self.description = description unless description.nil? + unless genres.nil? + self.genres = [] + genres.each do |genre_id| + g = Genre.find(genre_id) + self.genres << g + end + end + save + end def self.search(query, options = { :limit => 10 }) diff --git a/spec/jam_ruby/models/recording_spec.rb b/spec/jam_ruby/models/recording_spec.rb index 4ead8d49e..afcd1402b 100644 --- a/spec/jam_ruby/models/recording_spec.rb +++ b/spec/jam_ruby/models/recording_spec.rb @@ -54,6 +54,30 @@ describe Recording do expect { @recording.stop }.to raise_error end + it "should be able to start, stop then start a recording again for the same music session" do + @recording = Recording.start(@music_session.id, @user, "Description") + @recording.stop + @recording2 = Recording.start(@music_session.id, @user, "Description 2") + @music_session.recording.should == @recording2 + end + + it "should attach the recording to all users in a the music session when recording started" do + @user2 = FactoryGirl.create(:user) + @connection2 = FactoryGirl.create(:connection, :user => @user2) + @instrument2 = FactoryGirl.create(:instrument, :description => 'a great instrument') + @track2 = FactoryGirl.create(:track, :connection => @connection2, :instrument => @instrument2) + + @music_session.connections << @connection2 + + @recording = Recording.start(@music_session.id, @user, "Description") + @user.recordings.length.should == 1 + @user.recordings.first.should == @recording + + @user2.recordings.length.should == 1 + @user2.recordings.first.should == @recording + + end + end From 051a8419955133b43dee4e7caca2e5e3b0f006a8 Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Thu, 31 Jan 2013 00:25:07 -0800 Subject: [PATCH 154/274] more work --- lib/jam_ruby/models/recording.rb | 48 +++++++++------ spec/jam_ruby/models/recording_spec.rb | 84 ++++++++++++++++++++++---- 2 files changed, 100 insertions(+), 32 deletions(-) diff --git a/lib/jam_ruby/models/recording.rb b/lib/jam_ruby/models/recording.rb index 8c084a680..6c110bbdd 100644 --- a/lib/jam_ruby/models/recording.rb +++ b/lib/jam_ruby/models/recording.rb @@ -16,15 +16,17 @@ module JamRuby # favorites has_many :favorites, :class_name => "JamRuby::UserFavorite", :foreign_key => "recording_id" has_many :inverse_favorites, :through => :favorites, :source => :recording, :class_name => "JamRuby::Recording" - - validates :description, presence: true, length: { maximum: 200 } + + validates :name, presence: true, length: { maximum: 100 } + validates :description, length: { maximum: 200 } + validates :genres, length: { maximum: 3 } def favorite_count return self.favorites.size end # Start recording a session. - def self.start(music_session_id, owner, description) + def self.start(music_session_id, owner, name) recording = nil @@ -36,14 +38,16 @@ module JamRuby raise PermissionError, "the session has ended" end - if music_session.recording + unless music_session.recording.nil? raise PermissionError, "the session is already being recorded" end recording = Recording.new - recording.description = description + recording.name = name recording.music_session = music_session recording.owner = owner + recording.is_public = false + recording.is_downloadable = false music_session.connections.each do |connection| recording.users << connection.user @@ -94,15 +98,27 @@ module JamRuby end # Update the metadata for the recording - def update(description, genres) - self.description = description unless description.nil? + def update_fields(params) + unless self.music_session.nil? + raise PermissionError, "recording data cannot be changed while it is being recorded" + end + + self.name = params[:name] unless params[:name].nil? + self.description = params[:description] unless params[:description].nil? + + genres = params[:genres] unless genres.nil? + validate_genres(genres) self.genres = [] genres.each do |genre_id| g = Genre.find(genre_id) self.genres << g end end + + self.is_public = params[:is_public] unless params[:is_public].nil? + self.is_downloadable = params[:is_downloadable] unless params[:is_downloadable].nil? + save end @@ -227,21 +243,13 @@ module JamRuby end end - def self.validate_genres(genres, is_nil_ok) - if is_nil_ok && genres.nil? - return + def validate_genres(genres) + if genres.length < Limits::MIN_GENRES_PER_RECORDING + raise JamRuby::JamArgumentError, ValidationMessages::GENRE_MINIMUM_NOT_MET end - if genres.nil? - raise JamRuby::JamArgumentError, ValidationMessages::GENRE_MINIMUM_NOT_MET - else - if genres.size < Limits::MIN_GENRES_PER_RECORDING - raise JamRuby::JamArgumentError, ValidationMessages::GENRE_MINIMUM_NOT_MET - end - - if genres.size > Limits::MAX_GENRES_PER_RECORDING - raise JamRuby::JamArgumentError, ValidationMessages::GENRE_LIMIT_EXCEEDED - end + if genres.length > Limits::MAX_GENRES_PER_RECORDING + raise JamRuby::JamArgumentError, ValidationMessages::GENRE_LIMIT_EXCEEDED end end diff --git a/spec/jam_ruby/models/recording_spec.rb b/spec/jam_ruby/models/recording_spec.rb index afcd1402b..4be5cf523 100644 --- a/spec/jam_ruby/models/recording_spec.rb +++ b/spec/jam_ruby/models/recording_spec.rb @@ -13,19 +13,19 @@ describe Recording do end it "should not start a recording if the music session doesnt exist" do - expect { Recording.start("bad_music_session_id", @user, "Description") }.to raise_error + expect { Recording.start("bad_music_session_id", @user, "name") }.to raise_error end it "should set up the recording properly when recording is started with 1 user in the session" do @music_session.recording.should == nil - @recording = Recording.start(@music_session.id, @user, "Description") + @recording = Recording.start(@music_session.id, @user, "name") @music_session.reload @music_session.recording.should == @recording @recording.owner_id.should == @user.id @recording.genres.should == @music_session.genres @recording.users.length.should == 1 @recording.users.first.should == @user - @recording.description.should == "Description" + @recording.description.should be_nil @recorded_tracks = RecordedTrack.where(:recording_id => @recording.id) @recorded_tracks.length.should == 1 @@ -34,12 +34,12 @@ describe Recording do end it "should not start a recording if the session is already being recorded" do - Recording.start(@music_session.id, @user, "Description") - expect { Recording.start(@music_session.id, @user, "Description") }.to raise_error + Recording.start(@music_session.id, @user, "name") + expect { Recording.start(@music_session.id, @user, "name") }.to raise_error end it "should return the state to normal properly when you stop a recording" do - @recording = Recording.start(@music_session.id, @user, "Description") + @recording = Recording.start(@music_session.id, @user, "name") @recording.stop @music_session.reload @music_session.recording.should == nil @@ -49,15 +49,15 @@ describe Recording do it "should error when you stop a recording twice" do - @recording = Recording.start(@music_session.id, @user, "Description") + @recording = Recording.start(@music_session.id, @user, "name") @recording.stop expect { @recording.stop }.to raise_error end it "should be able to start, stop then start a recording again for the same music session" do - @recording = Recording.start(@music_session.id, @user, "Description") + @recording = Recording.start(@music_session.id, @user, "name") @recording.stop - @recording2 = Recording.start(@music_session.id, @user, "Description 2") + @recording2 = Recording.start(@music_session.id, @user, "name") @music_session.recording.should == @recording2 end @@ -69,16 +69,76 @@ describe Recording do @music_session.connections << @connection2 - @recording = Recording.start(@music_session.id, @user, "Description") + @recording = Recording.start(@music_session.id, @user, "name") @user.recordings.length.should == 1 @user.recordings.first.should == @recording @user2.recordings.length.should == 1 @user2.recordings.first.should == @recording - end + + it "should update fields in a recording properly after it finishes" do + @recording = Recording.start(@music_session.id, @user, "name") + @recording.stop + @recording.reload + + @genre1 = FactoryGirl.create(:genre) + @genre2 = FactoryGirl.create(:genre) + @genre3 = FactoryGirl.create(:genre) + + @recording.update_fields :name => "name1", :description => "description", :genres => [@genre1, @genre2], :is_downloadable => true, :is_public => true + @recording.name.should == "name1" + @recording.description.should == "description" + @recording.genres.length.should == 2 + @recording.is_public.should == true + @recording.is_downloadable.should == true + + @recording.update_fields :name => "name2", :description => "description2", :genres => [@genre3], :is_downloadable => false, :is_public => false + @recording.name.should == "name2" + @recording.description.should == "description2" + @recording.genres.length.should == 1 + @recording.genres.first.should == @genre3 + @recording.is_public.should == false + @recording.is_downloadable.should == false - + @recording.update_fields :fakeparam => "fake" + @recording.name.should == "name2" + @recording.description.should == "description2" + @recording.genres.length.should == 1 + @recording.genres.first.should == @genre3 + @recording.is_public.should == false + @recording.is_downloadable.should == false + + end + + it "should error if you update fields in a recording while its ongoing" do + @recording = Recording.start(@music_session.id, @user, "name") + expect { @recording.update_fields :description => "description" }.to raise_error + end + + it "should error if you try to assign more than 3 genres or fewer than 1" do + @recording = Recording.start(@music_session.id, @user, "name") + @recording.stop + @recording.reload + + @genre1 = FactoryGirl.create(:genre) + @genre2 = FactoryGirl.create(:genre) + @genre3 = FactoryGirl.create(:genre) + @genre4 = FactoryGirl.create(:genre) + + expect { @recording.update_fields :genres => [@genre1, @genre2, @genre3, @genre4] }.to raise_error + expect { @recording.update_fields :genres => [] }.to raise_error + end + + it "should delete a recording as expected" do + @recording = Recording.start(@music_session.id, @user, "name") + @recording.stop + Recording.delete(@recording.id) + expect { Recording.find(@recording.id) }.to raise_error + end + + + end From 9769ade049274411fd789d29964d1b05b7a13dbd Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Sat, 2 Feb 2013 18:55:26 -0500 Subject: [PATCH 155/274] added keyword search to find session --- lib/jam_ruby/models/music_session.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/jam_ruby/models/music_session.rb b/lib/jam_ruby/models/music_session.rb index 32cd4d030..f47263410 100644 --- a/lib/jam_ruby/models/music_session.rb +++ b/lib/jam_ruby/models/music_session.rb @@ -38,7 +38,7 @@ module JamRuby # This is a little confusing. You can specify *BOTH* friends_only and my_bands_only to be true # If so, then it's an OR condition. If both are false, you can get sessions with anyone. - def self.index(current_user, participants = nil, genres = nil, friends_only = false, my_bands_only = false) + def self.index(current_user, participants = nil, genres = nil, friends_only = false, my_bands_only = false, keyword = nil) query = MusicSession .joins( @@ -89,6 +89,7 @@ module JamRuby } ) + query = query.where("music_sessions.description like '%#{keyword}%'") unless keyword.nil? query = query.where("connections.user_id" => participants.split(',')) unless participants.nil? query = query.joins(:genres).where("genres.id" => genres.split(',')) unless genres.nil? From cfd5af8c95713744c491939685e80ab06e616ca8 Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Mon, 4 Feb 2013 11:12:20 -0800 Subject: [PATCH 156/274] more work --- lib/jam_ruby/models/recording.rb | 15 ++++++---- spec/jam_ruby/models/recording_spec.rb | 40 ++++++++++++++++---------- 2 files changed, 35 insertions(+), 20 deletions(-) diff --git a/lib/jam_ruby/models/recording.rb b/lib/jam_ruby/models/recording.rb index 6c110bbdd..e6f850514 100644 --- a/lib/jam_ruby/models/recording.rb +++ b/lib/jam_ruby/models/recording.rb @@ -17,7 +17,7 @@ module JamRuby has_many :favorites, :class_name => "JamRuby::UserFavorite", :foreign_key => "recording_id" has_many :inverse_favorites, :through => :favorites, :source => :recording, :class_name => "JamRuby::Recording" - validates :name, presence: true, length: { maximum: 100 } + validates :name, length: { maximum: 100 } validates :description, length: { maximum: 200 } validates :genres, length: { maximum: 3 } @@ -26,7 +26,7 @@ module JamRuby end # Start recording a session. - def self.start(music_session_id, owner, name) + def self.start(music_session_id, owner) recording = nil @@ -43,7 +43,6 @@ module JamRuby end recording = Recording.new - recording.name = name recording.music_session = music_session recording.owner = owner recording.is_public = false @@ -82,6 +81,8 @@ module JamRuby end # Stop recording a session + # FIXME: What to do about recordings which are started and then stopped but never get any metadata (not even a name)? + # I'm guessing they should be deleted eventually. def stop # Use a transaction and lock to avoid races. ActiveRecord::Base.transaction do @@ -97,12 +98,16 @@ module JamRuby end end - # Update the metadata for the recording + # Update the metadata for the recording. def update_fields(params) unless self.music_session.nil? raise PermissionError, "recording data cannot be changed while it is being recorded" end - + + if params[:name].nil? + raise PermissionError, "recording must have a name" if self.name.nil? + end + self.name = params[:name] unless params[:name].nil? self.description = params[:description] unless params[:description].nil? diff --git a/spec/jam_ruby/models/recording_spec.rb b/spec/jam_ruby/models/recording_spec.rb index 4be5cf523..58e6d8cfe 100644 --- a/spec/jam_ruby/models/recording_spec.rb +++ b/spec/jam_ruby/models/recording_spec.rb @@ -13,12 +13,12 @@ describe Recording do end it "should not start a recording if the music session doesnt exist" do - expect { Recording.start("bad_music_session_id", @user, "name") }.to raise_error + expect { Recording.start("bad_music_session_id", @user) }.to raise_error end it "should set up the recording properly when recording is started with 1 user in the session" do @music_session.recording.should == nil - @recording = Recording.start(@music_session.id, @user, "name") + @recording = Recording.start(@music_session.id, @user) @music_session.reload @music_session.recording.should == @recording @recording.owner_id.should == @user.id @@ -34,12 +34,12 @@ describe Recording do end it "should not start a recording if the session is already being recorded" do - Recording.start(@music_session.id, @user, "name") - expect { Recording.start(@music_session.id, @user, "name") }.to raise_error + Recording.start(@music_session.id, @user) + expect { Recording.start(@music_session.id, @user) }.to raise_error end it "should return the state to normal properly when you stop a recording" do - @recording = Recording.start(@music_session.id, @user, "name") + @recording = Recording.start(@music_session.id, @user) @recording.stop @music_session.reload @music_session.recording.should == nil @@ -49,15 +49,15 @@ describe Recording do it "should error when you stop a recording twice" do - @recording = Recording.start(@music_session.id, @user, "name") + @recording = Recording.start(@music_session.id, @user) @recording.stop expect { @recording.stop }.to raise_error end it "should be able to start, stop then start a recording again for the same music session" do - @recording = Recording.start(@music_session.id, @user, "name") + @recording = Recording.start(@music_session.id, @user) @recording.stop - @recording2 = Recording.start(@music_session.id, @user, "name") + @recording2 = Recording.start(@music_session.id, @user) @music_session.recording.should == @recording2 end @@ -69,7 +69,7 @@ describe Recording do @music_session.connections << @connection2 - @recording = Recording.start(@music_session.id, @user, "name") + @recording = Recording.start(@music_session.id, @user) @user.recordings.length.should == 1 @user.recordings.first.should == @recording @@ -78,7 +78,7 @@ describe Recording do end it "should update fields in a recording properly after it finishes" do - @recording = Recording.start(@music_session.id, @user, "name") + @recording = Recording.start(@music_session.id, @user) @recording.stop @recording.reload @@ -101,6 +101,7 @@ describe Recording do @recording.is_public.should == false @recording.is_downloadable.should == false + # Use a fake param here to simulate getting no params @recording.update_fields :fakeparam => "fake" @recording.name.should == "name2" @recording.description.should == "description2" @@ -112,12 +113,12 @@ describe Recording do end it "should error if you update fields in a recording while its ongoing" do - @recording = Recording.start(@music_session.id, @user, "name") - expect { @recording.update_fields :description => "description" }.to raise_error + @recording = Recording.start(@music_session.id, @user) + expect { @recording.update_fields :name => "name", :description => "description" }.to raise_error end it "should error if you try to assign more than 3 genres or fewer than 1" do - @recording = Recording.start(@music_session.id, @user, "name") + @recording = Recording.start(@music_session.id, @user) @recording.stop @recording.reload @@ -126,12 +127,21 @@ describe Recording do @genre3 = FactoryGirl.create(:genre) @genre4 = FactoryGirl.create(:genre) - expect { @recording.update_fields :genres => [@genre1, @genre2, @genre3, @genre4] }.to raise_error + expect { @recording.update_fields :name => "name", :genres => [@genre1, @genre2, @genre3, @genre4] }.to raise_error expect { @recording.update_fields :genres => [] }.to raise_error end + it "should error if you set fields on a recording with no name" do + @recording = Recording.start(@music_session.id, @user) + @recording.stop + @recording.reload + + @recording.name.should be_nil + expect { @recording.update_fields :description => "description" }.to raise_error + end + it "should delete a recording as expected" do - @recording = Recording.start(@music_session.id, @user, "name") + @recording = Recording.start(@music_session.id, @user) @recording.stop Recording.delete(@recording.id) expect { Recording.find(@recording.id) }.to raise_error From 4f4b7219d8321c91f237d71b7ea3ae6314465c98 Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Mon, 4 Feb 2013 18:07:08 -0800 Subject: [PATCH 157/274] more work --- lib/jam_ruby/models/recording.rb | 31 ++++++++++++++++++++++++++ spec/jam_ruby/models/recording_spec.rb | 20 ++++++++++++++++- 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/lib/jam_ruby/models/recording.rb b/lib/jam_ruby/models/recording.rb index e6f850514..3ea932606 100644 --- a/lib/jam_ruby/models/recording.rb +++ b/lib/jam_ruby/models/recording.rb @@ -25,6 +25,29 @@ module JamRuby return self.favorites.size end + def self.user_recordings(user_id) + query = Recording + .joins( + %Q{ + INNER JOIN + recordings_users + ON + recordings_users.recording_id = recordings.id + } + ) + .order( + %Q{ + recordings.created_at DESC + } + ) + .where( + %Q{ + recordings_users.user_id = '#{user_id}' + } + ) + query + end + # Start recording a session. def self.start(music_session_id, owner) @@ -127,6 +150,14 @@ module JamRuby save end + # Find out if all the tracks for this recording have been uploaded + def uploaded? + self.recorded_tracks.each do |recorded_track| + return false unless recorded_track.fully_uploaded + end + return true + end + def self.search(query, options = { :limit => 10 }) # only issue search if at least 2 characters are specified diff --git a/spec/jam_ruby/models/recording_spec.rb b/spec/jam_ruby/models/recording_spec.rb index 58e6d8cfe..76c5d9e10 100644 --- a/spec/jam_ruby/models/recording_spec.rb +++ b/spec/jam_ruby/models/recording_spec.rb @@ -147,8 +147,26 @@ describe Recording do expect { Recording.find(@recording.id) }.to raise_error end + it "should report correctly whether its tracks have been uploaded" do + @recording = Recording.start(@music_session.id, @user) + @recording.uploaded?.should == false + @recording.stop + @recording.reload + @recording.uploaded?.should == false + @recording.recorded_tracks.first.fully_uploaded = true + @recording.uploaded?.should == true + end - + it "should destroy a recording and all its recorded tracks properly" do + @recording = Recording.start(@music_session.id, @user) + @recording.stop + @recording.reload + @recorded_track = @recording.recorded_tracks.first + @recording.destroy + expect { Recording.find(@recording.id) }.to raise_error + expect { RecordedTracks.find(@recorded_track.id) }.to raise_error + end + end From 0e4163ebe40185590ec8085ca18ffcb286be56d0 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Tue, 5 Feb 2013 03:06:09 -0500 Subject: [PATCH 158/274] allow only 1 genre --- lib/jam_ruby/constants/limits.rb | 4 ++-- lib/jam_ruby/constants/validation_messages.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/jam_ruby/constants/limits.rb b/lib/jam_ruby/constants/limits.rb index dc534340a..5d5e409d0 100644 --- a/lib/jam_ruby/constants/limits.rb +++ b/lib/jam_ruby/constants/limits.rb @@ -2,11 +2,11 @@ module Limits # band genres MIN_GENRES_PER_BAND = 1 - MAX_GENRES_PER_BAND = 3 + MAX_GENRES_PER_BAND = 1 # recording genres MIN_GENRES_PER_RECORDING = 1 - MAX_GENRES_PER_RECORDING = 3 + MAX_GENRES_PER_RECORDING = 1 # instruments MIN_INSTRUMENTS_PER_MUSICIAN = 1 diff --git a/lib/jam_ruby/constants/validation_messages.rb b/lib/jam_ruby/constants/validation_messages.rb index 80379f92f..dd7dc9077 100644 --- a/lib/jam_ruby/constants/validation_messages.rb +++ b/lib/jam_ruby/constants/validation_messages.rb @@ -20,7 +20,7 @@ module ValidationMessages SESSION_NOT_FOUND = "Session not found." # genres - GENRE_LIMIT_EXCEEDED = "No more than 3 genres are allowed." + GENRE_LIMIT_EXCEEDED = "No more than 1 genre is allowed." GENRE_MINIMUM_NOT_MET = "At least 1 genre is required." # instruments From 47cc69a5f0473e937a7b3c568543f8ab25aedfa1 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Tue, 5 Feb 2013 23:14:07 -0600 Subject: [PATCH 159/274] * adding artifact_updte table --- lib/jam_ruby.rb | 1 + lib/jam_ruby/models/artifact_update.rb | 16 +++++++++++ spec/jam_ruby/models/artifact_update_spec.rb | 28 ++++++++++++++++++++ 3 files changed, 45 insertions(+) create mode 100644 lib/jam_ruby/models/artifact_update.rb create mode 100644 spec/jam_ruby/models/artifact_update_spec.rb diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index 80a53379a..8bd737ae0 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -25,6 +25,7 @@ require "jam_ruby/models/user" require "jam_ruby/models/user_authorization" require "jam_ruby/models/join_request" require "jam_ruby/models/band" +require "jam_ruby/models/artifact_update" require "jam_ruby/models/band_recording" require "jam_ruby/models/band_invitation" require "jam_ruby/models/band_liker" diff --git a/lib/jam_ruby/models/artifact_update.rb b/lib/jam_ruby/models/artifact_update.rb new file mode 100644 index 000000000..5b2603216 --- /dev/null +++ b/lib/jam_ruby/models/artifact_update.rb @@ -0,0 +1,16 @@ +module JamRuby + class ArtifactUpdate < ActiveRecord::Base + + DEFAULT_ENVIRONMENT = 'public' + + PRODUCTS = ['JamClient/Win32', 'JamClient/MacOSX'] + + self.primary_key = 'id' + + validate :version, :presence => true + validate :uri, :presence => true + validate :sha1, :presence => true + validate :environment, presence => true + validate :product, :inclusion => {:in => PRODUCTS} + end +end diff --git a/spec/jam_ruby/models/artifact_update_spec.rb b/spec/jam_ruby/models/artifact_update_spec.rb new file mode 100644 index 000000000..2873aa035 --- /dev/null +++ b/spec/jam_ruby/models/artifact_update_spec.rb @@ -0,0 +1,28 @@ +require 'spec_helper' + +describe ArtifactUpdate do + + it "return empty" do + ArtifactUpdate.find(:all).length.should == 0 + end + + it "should allow insertion" do + artifact = ArtifactUpdate.new + artifact.product = 'JamClient/Win32' + artifact.version = '0.1.1' + artifact.uri = 'http://nowhere/' + artifact.sha1 = 'blahablahblah' + + artifact.save! + + artifact.environment.should == "public" + artifact.product.should == "JamClient/Win32" + artifact.version.should == "0.1.1" + artifact.uri.should == "http://nowhere/" + artifact.sha1.should == "blahablahblah" + + found = ArtifactUpdate.find_by_product_and_version('JamClient/Win32', '0.1.1') + artifact.should == found + end + +end \ No newline at end of file From 6a31e05bbd315642749a77f2a23b3d20940b663b Mon Sep 17 00:00:00 2001 From: Jonathan Kolyer Date: Wed, 6 Feb 2013 07:11:05 -0600 Subject: [PATCH 160/274] vrfs192: added flag_stale_connections method; updated remove_stale_connections and create_connection --- lib/jam_ruby/connection_manager.rb | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/lib/jam_ruby/connection_manager.rb b/lib/jam_ruby/connection_manager.rb index 360726691..b3a5327eb 100644 --- a/lib/jam_ruby/connection_manager.rb +++ b/lib/jam_ruby/connection_manager.rb @@ -26,13 +26,32 @@ module JamRuby #TODO end + # remove stale connections + def flag_stale_connections(max_seconds) + ConnectionManager.active_record_transaction do |connection_manager| + conn = connection_manager.pg_conn + + conn.exec("UPDATE connections SET aasm_state = '#{Connection::DISCONNECT_STATE.to_s}' WHERE updated_at < (NOW() - interval '#{max_seconds} second') AND aasm_state = '#{Connection::CONNECT_STATE.to_s}'") + + # JamRuby::Connection.where("updated_at < (NOW() - interval '#{max_seconds} second') AND aasm_state = 'connected'").find do |conn| + # conn.update_attribute(:aasm_state, 'disconnected') + # # puts "*** flag_stale_connections: conn.state = #{conn.aasm_state}; #{conn.client_id}" + # # conn.disconnect + # # puts "*** flag_stale_connections: conn.state = #{conn.aasm_state}" + # # conn.save! + # # puts "*** flag_stale_connections: conn.state = #{conn.reload.aasm_state}" + # end + + end + end + # remove stale connections def remove_stale_connections(max_seconds) ConnectionManager.active_record_transaction do |connection_manager| conn = connection_manager.pg_conn stale_clients = [] - conn.exec("SELECT client_id FROM connections WHERE updated_at < (NOW() - interval '#{max_seconds} second')") do |result| + conn.exec("SELECT client_id FROM connections WHERE updated_at < (NOW() - interval '#{max_seconds} second') AND aasm_state = '#{Connection::DISCONNECT_STATE.to_s}'") do |result| result.each do |row| stale_clients.push(row['client_id']) end @@ -53,7 +72,8 @@ module JamRuby lock_connections(conn) - conn.exec("INSERT INTO connections (user_id, client_id, ip_address) VALUES ($1, $2, $3)", [user_id, client_id, ip_address]).clear + conn.exec("INSERT INTO connections (user_id, client_id, ip_address, aasm_state) VALUES ($1, $2, $3, $4)", + [user_id, client_id, ip_address, Connection::CONNECT_STATE.to_s]).clear # we just created a new connection-if this is the first time the user has shown up, we need to send out a message to his friends conn.exec("SELECT count(user_id) FROM connections WHERE user_id = $1", [user_id]) do |result| @@ -303,4 +323,4 @@ module JamRuby end end end -end \ No newline at end of file +end From 755d3a90256fd981080a44240228bcf631a89ae1 Mon Sep 17 00:00:00 2001 From: Jonathan Kolyer Date: Wed, 6 Feb 2013 07:11:31 -0600 Subject: [PATCH 161/274] vrfs192: added music_session_id to login_ack method --- lib/jam_ruby/message_factory.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/jam_ruby/message_factory.rb b/lib/jam_ruby/message_factory.rb index 6d93e8d04..ae26c8a6e 100644 --- a/lib/jam_ruby/message_factory.rb +++ b/lib/jam_ruby/message_factory.rb @@ -37,8 +37,8 @@ end # create a login ack (login was successful) - def login_ack(public_ip, client_id, token, heartbeat_interval) - login_ack = Jampb::LoginAck.new(:public_ip => public_ip, :client_id => client_id, :token => token, :heartbeat_interval => heartbeat_interval) + def login_ack(public_ip, client_id, token, heartbeat_interval, music_session_id) + login_ack = Jampb::LoginAck.new(:public_ip => public_ip, :client_id => client_id, :token => token, :heartbeat_interval => heartbeat_interval, :music_session_id => music_session_id) return Jampb::ClientMessage.new(:type => ClientMessage::Type::LOGIN_ACK, :route_to => CLIENT_TARGET, :login_ack => login_ack) end From f7728cc9fcfe813ad0cb309b737e9957d0a77cc7 Mon Sep 17 00:00:00 2001 From: Jonathan Kolyer Date: Wed, 6 Feb 2013 07:12:36 -0600 Subject: [PATCH 162/274] vrfs192: added AASM logic --- lib/jam_ruby/models/connection.rb | 44 ++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/lib/jam_ruby/models/connection.rb b/lib/jam_ruby/models/connection.rb index 8f3a1e3f0..652f527b1 100644 --- a/lib/jam_ruby/models/connection.rb +++ b/lib/jam_ruby/models/connection.rb @@ -1,3 +1,5 @@ +require 'aasm' + module JamRuby class Connection < ActiveRecord::Base @@ -20,6 +22,46 @@ module JamRuby validate :can_join_music_session, :if => :joining_session? after_save :require_at_least_one_track_when_in_session, :if => :joining_session? + CONNECT_STATE = :connected + DISCONNECT_STATE = :disconnected + + include AASM + aasm do + state :idle, :initial => true + state CONNECT_STATE + state :disconnected + state :expired + + event :connect do + transitions :from => :idle, :to => CONNECT_STATE + transitions :from => :disconnected, :to => CONNECT_STATE + end + event :disconnect do + transitions :from => CONNECT_STATE, :to => :disconnected + transitions :from => :idle, :to => :disconnected + end + event :expire, :after => :did_expire do + transitions :from => CONNECT_STATE, :to => :expired + transitions :from => :disconnected, :to => :expired + transitions :from => :idle, :to => :expired + end + end + + def state_message + case self.aasm_state.to_sym + when CONNECT_STATE + 'Connected' + when :disconnected + 'Disconnected' + else + 'Idle' + end + end + + def did_expire + self.destroy + end + def joining_session? return joining_session end @@ -76,4 +118,4 @@ module JamRuby end end -end \ No newline at end of file +end From f737cc8a72b8a0e5ce94cd534856e50baccae95d Mon Sep 17 00:00:00 2001 From: Jonathan Kolyer Date: Wed, 6 Feb 2013 07:12:58 -0600 Subject: [PATCH 163/274] vrfs192: added aasm gem --- Gemfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Gemfile b/Gemfile index 4c5750f6d..ebd9dc291 100644 --- a/Gemfile +++ b/Gemfile @@ -19,6 +19,7 @@ gem 'will_paginate' gem 'actionmailer' gem 'sendgrid' gem 'aws-sdk', '1.8.0' +gem 'aasm', '3.0.16' if devenv gem 'jam_db', :path=> "#{workspace}/jam-db/target/ruby_package" From be981979c827526bc4a5a3d747494193336e6ef1 Mon Sep 17 00:00:00 2001 From: Jonathan Kolyer Date: Wed, 6 Feb 2013 07:14:00 -0600 Subject: [PATCH 164/274] vrfs192: added tests for connection state and removing/flagging stale connections --- spec/jam_ruby/connection_manager_spec.rb | 45 ++++++++++++++++++++---- spec/jam_ruby/models/connection_spec.rb | 33 ++++++++++++++++- 2 files changed, 71 insertions(+), 7 deletions(-) diff --git a/spec/jam_ruby/connection_manager_spec.rb b/spec/jam_ruby/connection_manager_spec.rb index c4041e3db..eba73fcd9 100644 --- a/spec/jam_ruby/connection_manager_spec.rb +++ b/spec/jam_ruby/connection_manager_spec.rb @@ -70,6 +70,9 @@ describe ConnectionManager do result.getvalue(0, 0).should == "1" end + cc = Connection.find_by_client_id!(client_id) + cc.connected?.should be_true + @connman.delete_connection(client_id) @conn.exec("SELECT count(*) FROM connections where user_id = $1", [user_id]) do |result| @@ -171,23 +174,53 @@ describe ConnectionManager do @connman.gather_friends(@conn, user_id3).should == [user_id1] end - it "remove stale connection" do - + it "flag stale connection" do client_id = "client_id8" - user_id = create_user("test", "user8", "user8@jamkazam.com") - @connman.create_connection(user_id, client_id, "1.1.1.1") - @connman.remove_stale_connections(60) + num = JamRuby::Connection.count(:conditions => ['aasm_state = ?','connected']) + num.should == 1 + assert_num_connections(client_id, num) + @connman.flag_stale_connections(60) + assert_num_connections(client_id, num) + sleep(1) + + num = JamRuby::Connection.count(:conditions => ["updated_at < (NOW() - interval '#{1} second') AND aasm_state = 'connected'"]) + num.should == 1 + # this should change the aasm_state to disconnected + @connman.flag_stale_connections(1) + + num = JamRuby::Connection.count(:conditions => ["updated_at < (NOW() - interval '#{1} second') AND aasm_state = 'connected'"]) + num.should == 0 + + num = JamRuby::Connection.count(:conditions => ["updated_at < (NOW() - interval '#{1} second') AND aasm_state = 'disconnected'"]) + num.should == 1 + assert_num_connections(client_id, 1) + + @connman.remove_stale_connections(1) + sleep(1) + assert_num_connections(client_id, 0) + end + + it "remove stale connection" do + client_id = "client_id8" + user_id = create_user("test", "user8", "user8@jamkazam.com") + @connman.create_connection(user_id, client_id, "1.1.1.1") + + sleep(1) + @connman.flag_stale_connections(1) + assert_num_connections(client_id, 1) + # assert_num_connections(client_id, JamRuby::Connection.count(:conditions => ['aasm_state = ?','disconnected'])) + + @connman.remove_stale_connections(60) assert_num_connections(client_id, 1) sleep(1) # this should remove the stale connection @connman.remove_stale_connections(1) - assert_num_connections(client_id, 0) end diff --git a/spec/jam_ruby/models/connection_spec.rb b/spec/jam_ruby/models/connection_spec.rb index 8b66ffc16..1edd1c341 100644 --- a/spec/jam_ruby/models/connection_spec.rb +++ b/spec/jam_ruby/models/connection_spec.rb @@ -1,5 +1,36 @@ require 'spec_helper' describe Connection do + let(:user) { FactoryGirl.create(:user) } + let (:music_session) { FactoryGirl.create(:music_session, :creator => user) } -end \ No newline at end of file + it 'starts in the correct state' do + connection = FactoryGirl.create(:connection, + :user => user, + :music_session => music_session, + :ip_address => "1.1.1.1", + :client_id => "1") + + connection.idle?.should be_true + end + + it 'transitions properly' do + connection = FactoryGirl.create(:connection, + :user => user, + :music_session => music_session, + :ip_address => "1.1.1.1", + :client_id => "1") + + connection.connect! + connection.connected?.should be_true + connection.state_message.should == 'Connected' + + connection.disconnect! + connection.disconnected?.should be_true + connection.state_message.should == 'Disconnected' + + connection.expire! + connection.destroyed?.should be_true + end + +end From 44d2aba3c6c4a0b2dd3a7c7611aa2e4cae4f76ea Mon Sep 17 00:00:00 2001 From: Jonathan Kolyer Date: Mon, 11 Feb 2013 00:02:25 -0600 Subject: [PATCH 165/274] refactored names for connection states --- lib/jam_ruby/connection_manager.rb | 36 +++++++++++++++--------- lib/jam_ruby/models/connection.rb | 34 +++++++++++----------- spec/jam_ruby/connection_manager_spec.rb | 16 +++++------ spec/jam_ruby/models/connection_spec.rb | 6 ++-- 4 files changed, 52 insertions(+), 40 deletions(-) diff --git a/lib/jam_ruby/connection_manager.rb b/lib/jam_ruby/connection_manager.rb index b3a5327eb..da5e4c871 100644 --- a/lib/jam_ruby/connection_manager.rb +++ b/lib/jam_ruby/connection_manager.rb @@ -26,38 +26,48 @@ module JamRuby #TODO end - # remove stale connections + # flag connections as stale (disconnected) def flag_stale_connections(max_seconds) ConnectionManager.active_record_transaction do |connection_manager| conn = connection_manager.pg_conn - conn.exec("UPDATE connections SET aasm_state = '#{Connection::DISCONNECT_STATE.to_s}' WHERE updated_at < (NOW() - interval '#{max_seconds} second') AND aasm_state = '#{Connection::CONNECT_STATE.to_s}'") + sql =< :joining_session? after_save :require_at_least_one_track_when_in_session, :if => :joining_session? - CONNECT_STATE = :connected - DISCONNECT_STATE = :disconnected - include AASM + IDLE_STATE = :idle + CONNECT_STATE = :connected + STALE_STATE = :stale + EXPIRED_STATE = :expired + aasm do - state :idle, :initial => true + state IDLE_STATE, :initial => true state CONNECT_STATE - state :disconnected - state :expired + state STALE_STATE + state EXPIRED_STATE event :connect do - transitions :from => :idle, :to => CONNECT_STATE - transitions :from => :disconnected, :to => CONNECT_STATE + transitions :from => IDLE_STATE, :to => CONNECT_STATE + transitions :from => STALE_STATE, :to => CONNECT_STATE end - event :disconnect do - transitions :from => CONNECT_STATE, :to => :disconnected - transitions :from => :idle, :to => :disconnected + event :stale do + transitions :from => CONNECT_STATE, :to => STALE_STATE + transitions :from => IDLE_STATE, :to => STALE_STATE end event :expire, :after => :did_expire do - transitions :from => CONNECT_STATE, :to => :expired - transitions :from => :disconnected, :to => :expired - transitions :from => :idle, :to => :expired + transitions :from => CONNECT_STATE, :to => EXPIRED_STATE + transitions :from => STALE_STATE, :to => EXPIRED_STATE + transitions :from => IDLE_STATE, :to => EXPIRED_STATE end end @@ -51,8 +53,8 @@ module JamRuby case self.aasm_state.to_sym when CONNECT_STATE 'Connected' - when :disconnected - 'Disconnected' + when STALE_STATE + 'Stale' else 'Idle' end diff --git a/spec/jam_ruby/connection_manager_spec.rb b/spec/jam_ruby/connection_manager_spec.rb index eba73fcd9..c33198d03 100644 --- a/spec/jam_ruby/connection_manager_spec.rb +++ b/spec/jam_ruby/connection_manager_spec.rb @@ -189,22 +189,22 @@ describe ConnectionManager do num = JamRuby::Connection.count(:conditions => ["updated_at < (NOW() - interval '#{1} second') AND aasm_state = 'connected'"]) num.should == 1 - # this should change the aasm_state to disconnected + # this should change the aasm_state to stale @connman.flag_stale_connections(1) num = JamRuby::Connection.count(:conditions => ["updated_at < (NOW() - interval '#{1} second') AND aasm_state = 'connected'"]) num.should == 0 - num = JamRuby::Connection.count(:conditions => ["updated_at < (NOW() - interval '#{1} second') AND aasm_state = 'disconnected'"]) + num = JamRuby::Connection.count(:conditions => ["updated_at < (NOW() - interval '#{1} second') AND aasm_state = 'stale'"]) num.should == 1 assert_num_connections(client_id, 1) - @connman.remove_stale_connections(1) + @connman.expire_stale_connections(1) sleep(1) assert_num_connections(client_id, 0) end - it "remove stale connection" do + it "expires stale connection" do client_id = "client_id8" user_id = create_user("test", "user8", "user8@jamkazam.com") @connman.create_connection(user_id, client_id, "1.1.1.1") @@ -212,15 +212,15 @@ describe ConnectionManager do sleep(1) @connman.flag_stale_connections(1) assert_num_connections(client_id, 1) - # assert_num_connections(client_id, JamRuby::Connection.count(:conditions => ['aasm_state = ?','disconnected'])) + # assert_num_connections(client_id, JamRuby::Connection.count(:conditions => ['aasm_state = ?','stale'])) - @connman.remove_stale_connections(60) + @connman.expire_stale_connections(60) assert_num_connections(client_id, 1) sleep(1) - # this should remove the stale connection - @connman.remove_stale_connections(1) + # this should delete the stale connection + @connman.expire_stale_connections(1) assert_num_connections(client_id, 0) end diff --git a/spec/jam_ruby/models/connection_spec.rb b/spec/jam_ruby/models/connection_spec.rb index 1edd1c341..29d23f476 100644 --- a/spec/jam_ruby/models/connection_spec.rb +++ b/spec/jam_ruby/models/connection_spec.rb @@ -25,9 +25,9 @@ describe Connection do connection.connected?.should be_true connection.state_message.should == 'Connected' - connection.disconnect! - connection.disconnected?.should be_true - connection.state_message.should == 'Disconnected' + connection.stale! + connection.stale?.should be_true + connection.state_message.should == 'Stale' connection.expire! connection.destroyed?.should be_true From ddd402ba6b27c4198a3af12ac1341801f7fbe136 Mon Sep 17 00:00:00 2001 From: Jonathan Kolyer Date: Mon, 11 Feb 2013 00:26:46 -0600 Subject: [PATCH 166/274] made code layout more readable; tuned logging for connection state changes --- lib/jam_ruby/connection_manager.rb | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/lib/jam_ruby/connection_manager.rb b/lib/jam_ruby/connection_manager.rb index da5e4c871..119b1ca46 100644 --- a/lib/jam_ruby/connection_manager.rb +++ b/lib/jam_ruby/connection_manager.rb @@ -26,7 +26,7 @@ module JamRuby #TODO end - # flag connections as stale (disconnected) + # flag connections as stale def flag_stale_connections(max_seconds) ConnectionManager.active_record_transaction do |connection_manager| conn = connection_manager.pg_conn @@ -40,7 +40,7 @@ SQL conn.exec(sql) do |result| count = result.getvalue(0, 0) if 0 < count.to_i - @log.debug("flagging #{count} stale connections (#{max_seconds})") + @log.info("flag_stale_connections: flagging #{count.length} stale connections") sql =< Date: Tue, 12 Feb 2013 19:56:40 -0600 Subject: [PATCH 167/274] * artifact_update; changes with sha1 --- Gemfile | 1 + lib/jam_ruby.rb | 3 +++ lib/jam_ruby/models/artifact_update.rb | 4 +++- spec/jam_ruby/models/artifact_update_spec.rb | 16 ++++++++++++++-- spec/spec_helper.rb | 7 +++++++ 5 files changed, 28 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index 4c5750f6d..32957b1df 100644 --- a/Gemfile +++ b/Gemfile @@ -19,6 +19,7 @@ gem 'will_paginate' gem 'actionmailer' gem 'sendgrid' gem 'aws-sdk', '1.8.0' +gem 'carrierwave' if devenv gem 'jam_db', :path=> "#{workspace}/jam-db/target/ruby_package" diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index 8bd737ae0..d26069bac 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -1,5 +1,7 @@ require "pg" require "active_record" +require "carrierwave" +require "carrierwave/orm/activerecord" require "jampb" require "uuidtools" require "logging" @@ -19,6 +21,7 @@ require "jam_ruby/version" require "jam_ruby/environment" require "jam_ruby/init" require "jam_ruby/app/mailers/user_mailer" +require "jam_ruby/app/uploaders/artifact_uploader" require "jam_ruby/message_factory" require "jam_ruby/models/genre" require "jam_ruby/models/user" diff --git a/lib/jam_ruby/models/artifact_update.rb b/lib/jam_ruby/models/artifact_update.rb index 5b2603216..9367b25c3 100644 --- a/lib/jam_ruby/models/artifact_update.rb +++ b/lib/jam_ruby/models/artifact_update.rb @@ -6,10 +6,12 @@ module JamRuby PRODUCTS = ['JamClient/Win32', 'JamClient/MacOSX'] self.primary_key = 'id' + attr_accessible :version, :uri, :sha1, :environment, :product + mount_uploader :uri, ArtifactUploader validate :version, :presence => true validate :uri, :presence => true - validate :sha1, :presence => true + validate :sha1, :presence => false validate :environment, presence => true validate :product, :inclusion => {:in => PRODUCTS} end diff --git a/spec/jam_ruby/models/artifact_update_spec.rb b/spec/jam_ruby/models/artifact_update_spec.rb index 2873aa035..c4ed36193 100644 --- a/spec/jam_ruby/models/artifact_update_spec.rb +++ b/spec/jam_ruby/models/artifact_update_spec.rb @@ -2,15 +2,27 @@ require 'spec_helper' describe ArtifactUpdate do + include UsesTempFiles + + ARTIFACT_FILE='jkclient-0.1.1.exe' + + in_directory_with_file(ARTIFACT_FILE) + + before do + content_for_file("exe binary globby goo") + end + it "return empty" do ArtifactUpdate.find(:all).length.should == 0 end + it "should allow insertion" do + artifact = ArtifactUpdate.new artifact.product = 'JamClient/Win32' artifact.version = '0.1.1' - artifact.uri = 'http://nowhere/' + artifact.uri = File.open(ARTIFACT_FILE) artifact.sha1 = 'blahablahblah' artifact.save! @@ -18,7 +30,7 @@ describe ArtifactUpdate do artifact.environment.should == "public" artifact.product.should == "JamClient/Win32" artifact.version.should == "0.1.1" - artifact.uri.should == "http://nowhere/" + File.basename(artifact.uri.path).should == ARTIFACT_FILE artifact.sha1.should == "blahablahblah" found = ArtifactUpdate.find_by_product_and_version('JamClient/Win32', '0.1.1') diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 48c263bc6..e0a74ae8e 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -2,6 +2,7 @@ require 'active_record' require 'jam_db' require 'spec_db' +require 'uses_temp_files' # recreate test database and migrate it SpecDb::recreate_database @@ -22,6 +23,12 @@ include JamRuby # put ActionMailer into test mode ActionMailer::Base.delivery_method = :test +# set up carrierwave to use file (instead of say, fog) for testing +CarrierWave.configure do |config| + config.storage = :file + config.enable_processing = false +end + #uncomment the following line to use spork with the debugger #require 'spork/ext/ruby-debug' From 713b4b6f4b1cc6871faba655c650039b3f99edbb Mon Sep 17 00:00:00 2001 From: Seth Call Date: Tue, 12 Feb 2013 22:40:01 -0600 Subject: [PATCH 168/274] * VRFS-217; necessary changes to test and make artifact model work with carrierwave --- .../app/uploaders/artifact_uploader.rb | 59 +++++++++++++++++++ spec/uses_temp_files.rb | 33 +++++++++++ 2 files changed, 92 insertions(+) create mode 100644 lib/jam_ruby/app/uploaders/artifact_uploader.rb create mode 100644 spec/uses_temp_files.rb diff --git a/lib/jam_ruby/app/uploaders/artifact_uploader.rb b/lib/jam_ruby/app/uploaders/artifact_uploader.rb new file mode 100644 index 000000000..c7593fda5 --- /dev/null +++ b/lib/jam_ruby/app/uploaders/artifact_uploader.rb @@ -0,0 +1,59 @@ +# encoding: utf-8 + +class ArtifactUploader < CarrierWave::Uploader::Base + + # Include RMagick or MiniMagick support: + # include CarrierWave::RMagick + # include CarrierWave::MiniMagick + + # Include the Sprockets helpers for Rails 3.1+ asset pipeline compatibility: + # include Sprockets::Helpers::RailsHelper + # include Sprockets::Helpers::IsolatedHelper + + # Choose what kind of storage to use for this uploader: + # storage :file + # storage :fog + + # Override the directory where uploaded files will be stored. + # This is a sensible default for uploaders that are meant to be mounted: + def store_dir + "artifacts/#{model.product}/#{model.version}" + end + + def md5 + @md5 ||= ::Digest::MD5.file(current_path).hexdigest + end + + # Provide a default URL as a default if there hasn't been a file uploaded: + # def default_url + # # For Rails 3.1+ asset pipeline compatibility: + # # asset_path("fallback/" + [version_name, "default.png"].compact.join('_')) + # + # "/images/fallback/" + [version_name, "default.png"].compact.join('_') + # end + + # Process files as they are uploaded: + # process :scale => [200, 300] + # + # def scale(width, height) + # # do something + # end + + # Create different versions of your uploaded files: + # version :thumb do + # process :scale => [50, 50] + # end + + # Add a white list of extensions which are allowed to be uploaded. + # For images you might use something like this: + def extension_white_list + %w(exe msi dmg) + end + + # Override the filename of the uploaded files: + # Avoid using model.id or version_name here, see uploader/store.rb for details. + # def filename + # "something.jpg" if original_filename + # end + +end diff --git a/spec/uses_temp_files.rb b/spec/uses_temp_files.rb new file mode 100644 index 000000000..263bfed1b --- /dev/null +++ b/spec/uses_temp_files.rb @@ -0,0 +1,33 @@ +#http://gabebw.wordpress.com/2011/03/21/temp-files-in-rspec/ + +# this will make a folder jam-ruby/spec/tmp if used in an rspec test, and delete it after +# our .gitignore would also keep spec/tmp out, if somehow it did not get deleted. +module UsesTempFiles + def self.included(example_group) + example_group.extend(self) + end + + def in_directory_with_file(file) + before do + @pwd = Dir.pwd + @tmp_dir = File.join(File.dirname(__FILE__), 'tmp') + FileUtils.mkdir_p(@tmp_dir) + Dir.chdir(@tmp_dir) + + FileUtils.mkdir_p(File.dirname(file)) + FileUtils.touch(file) + end + + define_method(:content_for_file) do |content| + f = File.new(File.join(@tmp_dir, file), 'a+') + f.write(content) + f.flush # VERY IMPORTANT + f.close + end + + after do + Dir.chdir(@pwd) + FileUtils.rm_rf(@tmp_dir) + end + end +end \ No newline at end of file From d05edc371c4bb65b807f435dee221d20c13ae204 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Wed, 13 Feb 2013 02:09:31 -0500 Subject: [PATCH 169/274] order user instruments by proficiency / priority --- lib/jam_ruby/models/musician_instrument.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/jam_ruby/models/musician_instrument.rb b/lib/jam_ruby/models/musician_instrument.rb index 4ce963dcf..d8ef69378 100644 --- a/lib/jam_ruby/models/musician_instrument.rb +++ b/lib/jam_ruby/models/musician_instrument.rb @@ -5,6 +5,9 @@ module JamRuby self.primary_key = 'id' + # ensure most proficient, highest priority + default_scope order('proficiency_level DESC, priority ASC') + belongs_to :user, :class_name => "JamRuby::User" belongs_to :instrument, :class_name => "JamRuby::Instrument" From 997d0e356368bde6c425d1e637927cc50998414b Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Sat, 16 Feb 2013 01:33:58 -0800 Subject: [PATCH 170/274] Fix for broken test --- lib/jam_ruby.rb | 1 - lib/jam_ruby/constants/limits.rb | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index 7de1d50c1..f52126e8b 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -29,7 +29,6 @@ require "jam_ruby/models/user_authorization" require "jam_ruby/models/join_request" require "jam_ruby/models/band" require "jam_ruby/models/artifact_update" -require "jam_ruby/models/band_recording" require "jam_ruby/models/band_invitation" require "jam_ruby/models/band_liker" require "jam_ruby/models/band_follower" diff --git a/lib/jam_ruby/constants/limits.rb b/lib/jam_ruby/constants/limits.rb index 5d5e409d0..fc7158b10 100644 --- a/lib/jam_ruby/constants/limits.rb +++ b/lib/jam_ruby/constants/limits.rb @@ -6,7 +6,7 @@ module Limits # recording genres MIN_GENRES_PER_RECORDING = 1 - MAX_GENRES_PER_RECORDING = 1 + MAX_GENRES_PER_RECORDING = 3 # instruments MIN_INSTRUMENTS_PER_MUSICIAN = 1 From c229405069064a7c3b963cfb16fff90ed61546bc Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sat, 16 Feb 2013 09:01:06 -0600 Subject: [PATCH 171/274] * clear tracks out and reassert the list --- lib/jam_ruby/connection_manager.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/jam_ruby/connection_manager.rb b/lib/jam_ruby/connection_manager.rb index 119b1ca46..3a2cd424f 100644 --- a/lib/jam_ruby/connection_manager.rb +++ b/lib/jam_ruby/connection_manager.rb @@ -325,6 +325,8 @@ SQL def associate_tracks(connection, tracks) @log.debug "Tracks:" @log.debug tracks + connection.tracks.clear() + unless tracks.nil? tracks.each do |track| instrument = Instrument.find(track["instrument_id"]) From aa19d59914aeffc70043bd05b1f2226d2799fe49 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Sat, 16 Feb 2013 16:10:45 -0500 Subject: [PATCH 172/274] limit to 1 genre --- lib/jam_ruby/constants/limits.rb | 2 +- spec/jam_ruby/models/recording_spec.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/jam_ruby/constants/limits.rb b/lib/jam_ruby/constants/limits.rb index fc7158b10..5d5e409d0 100644 --- a/lib/jam_ruby/constants/limits.rb +++ b/lib/jam_ruby/constants/limits.rb @@ -6,7 +6,7 @@ module Limits # recording genres MIN_GENRES_PER_RECORDING = 1 - MAX_GENRES_PER_RECORDING = 3 + MAX_GENRES_PER_RECORDING = 1 # instruments MIN_INSTRUMENTS_PER_MUSICIAN = 1 diff --git a/spec/jam_ruby/models/recording_spec.rb b/spec/jam_ruby/models/recording_spec.rb index 76c5d9e10..62a529f50 100644 --- a/spec/jam_ruby/models/recording_spec.rb +++ b/spec/jam_ruby/models/recording_spec.rb @@ -86,10 +86,10 @@ describe Recording do @genre2 = FactoryGirl.create(:genre) @genre3 = FactoryGirl.create(:genre) - @recording.update_fields :name => "name1", :description => "description", :genres => [@genre1, @genre2], :is_downloadable => true, :is_public => true + @recording.update_fields :name => "name1", :description => "description", :genres => [@genre1], :is_downloadable => true, :is_public => true @recording.name.should == "name1" @recording.description.should == "description" - @recording.genres.length.should == 2 + @recording.genres.length.should == 1 @recording.is_public.should == true @recording.is_downloadable.should == true From 1b62522ce36580520679eabce5646b963e0237df Mon Sep 17 00:00:00 2001 From: Jonathan Kolyer Date: Thu, 21 Feb 2013 11:36:58 -0600 Subject: [PATCH 173/274] vrfs192: added stale_connection_client_ids and flag_connection_stale_with_client_ids to allow websocket-gateway ability to cleanup when stale connection is deleted --- lib/jam_ruby/connection_manager.rb | 36 +++++++++++++++--------- spec/jam_ruby/connection_manager_spec.rb | 7 +++-- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/lib/jam_ruby/connection_manager.rb b/lib/jam_ruby/connection_manager.rb index 3a2cd424f..4df6916fd 100644 --- a/lib/jam_ruby/connection_manager.rb +++ b/lib/jam_ruby/connection_manager.rb @@ -26,6 +26,17 @@ module JamRuby #TODO end + def flag_connection_stale_with_client_id(client_id) + sql =< Date: Thu, 21 Feb 2013 19:34:04 -0600 Subject: [PATCH 174/274] added reconnect method --- lib/jam_ruby/connection_manager.rb | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/jam_ruby/connection_manager.rb b/lib/jam_ruby/connection_manager.rb index 4df6916fd..a4f8f2cad 100644 --- a/lib/jam_ruby/connection_manager.rb +++ b/lib/jam_ruby/connection_manager.rb @@ -26,6 +26,16 @@ module JamRuby #TODO end + def reconnect(conn) + sql =< Date: Fri, 1 Mar 2013 07:35:50 -0600 Subject: [PATCH 175/274] * allow user model to permit null first/last on administrative signup --- lib/jam_ruby/models/user.rb | 12 ++++++++---- spec/jam_ruby/models/user_spec.rb | 5 +++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index cb5c128f2..99afc9041 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -2,7 +2,7 @@ module JamRuby class User < ActiveRecord::Base attr_accessible :first_name, :last_name, :email, :password, :password_confirmation, :city, :state, :country - attr_accessor :updating_password + attr_accessor :updating_password, :administratively_created self.primary_key = 'id' @@ -89,8 +89,8 @@ module JamRuby before_save { |user| user.email = email.downcase } before_save :create_remember_token, :if => :should_validate_password? - validates :first_name, presence: true, length: {maximum: 50} - validates :last_name, presence: true, length: {maximum: 50} + validates :first_name, presence: true, length: {maximum: 50}, :if => :end_user_created? + validates :last_name, presence: true, length: {maximum: 50}, :if => :end_user_created? VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i validates :email, presence: true, format: {with: VALID_EMAIL_REGEX}, uniqueness: {case_sensitive: false} @@ -123,7 +123,11 @@ module JamRuby end def should_validate_password? - updating_password || new_record? + !administratively_created && (updating_password || new_record?) + end + + def end_user_created? + return !administratively_created end def friends?(user) diff --git a/spec/jam_ruby/models/user_spec.rb b/spec/jam_ruby/models/user_spec.rb index aa31e26de..e2ca1683f 100644 --- a/spec/jam_ruby/models/user_spec.rb +++ b/spec/jam_ruby/models/user_spec.rb @@ -126,6 +126,11 @@ describe User do it { should be_invalid } end + describe "when password and password digest are not present, but admininstrator created it" do + before { @user.password = @user.password_confirmation = nil; @user.administratively_created = true } + it { should be_valid } + end + describe "set_password" do before do @user.confirm_email! From 8d68323d5c9461e29034fc48307cdacacf783677 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Sat, 2 Mar 2013 19:40:58 -0500 Subject: [PATCH 176/274] remove country from user location for now --- lib/jam_ruby/models/user.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 99afc9041..650284dd1 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -110,7 +110,7 @@ module JamRuby def location loc = self.city.blank? ? '' : self.city loc = loc.blank? ? self.state : "#{loc}, #{self.state}" unless self.state.blank? - loc = loc.blank? ? self.country : "#{loc}, #{self.country}" unless self.country.blank? + #loc = loc.blank? ? self.country : "#{loc}, #{self.country}" unless self.country.blank? loc end From 854c3a5e2e106096396a0d2fec7545b844f6f005 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Sat, 9 Mar 2013 23:29:10 -0500 Subject: [PATCH 177/274] changes --- lib/jam_ruby/models/user.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 650284dd1..6a2ae2f5f 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -1,7 +1,7 @@ module JamRuby class User < ActiveRecord::Base - attr_accessible :first_name, :last_name, :email, :password, :password_confirmation, :city, :state, :country + attr_accessible :first_name, :last_name, :email, :password, :password_confirmation, :city, :state, :country, :online attr_accessor :updating_password, :administratively_created self.primary_key = 'id' From 040b48a02a1ad8976824d7750bd9e41befccc37f Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Sat, 9 Mar 2013 23:31:09 -0500 Subject: [PATCH 178/274] rollback --- lib/jam_ruby/models/user.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 6a2ae2f5f..650284dd1 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -1,7 +1,7 @@ module JamRuby class User < ActiveRecord::Base - attr_accessible :first_name, :last_name, :email, :password, :password_confirmation, :city, :state, :country, :online + attr_accessible :first_name, :last_name, :email, :password, :password_confirmation, :city, :state, :country attr_accessor :updating_password, :administratively_created self.primary_key = 'id' From 42429d43954e93b46cb89c177c6d9f7318adbaca Mon Sep 17 00:00:00 2001 From: Seth Call Date: Thu, 14 Mar 2013 23:22:31 -0500 Subject: [PATCH 179/274] * VRFS-259: beta signup --- Gemfile | 2 +- lib/jam_ruby.rb | 5 + .../app/mailers/invited_user_mailer.rb | 54 +++ lib/jam_ruby/app/mailers/user_mailer.rb | 12 - .../friend_invitation.html.erb | 10 + .../friend_invitation.text.erb | 7 + .../welcome_betauser.html.erb | 6 + .../welcome_betauser.text.erb | 2 + .../user_mailer/welcome_betauser.html.erb | 6 - .../user_mailer/welcome_betauser.text.erb | 2 - lib/jam_ruby/models/friendship.rb | 17 + lib/jam_ruby/models/instrument.rb | 5 + lib/jam_ruby/models/invited_user.rb | 59 +++ lib/jam_ruby/models/invited_user_observer.rb | 14 + lib/jam_ruby/models/user.rb | 351 +++++++++++------- spec/factories.rb | 20 + spec/jam_ruby/connection_manager_spec.rb | 2 +- spec/jam_ruby/models/invited_user_spec.rb | 68 ++++ spec/jam_ruby/models/user_spec.rb | 13 +- spec/spec_helper.rb | 2 + 20 files changed, 485 insertions(+), 172 deletions(-) create mode 100644 lib/jam_ruby/app/mailers/invited_user_mailer.rb create mode 100644 lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/friend_invitation.html.erb create mode 100644 lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/friend_invitation.text.erb create mode 100644 lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/welcome_betauser.html.erb create mode 100644 lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/welcome_betauser.text.erb delete mode 100644 lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_betauser.html.erb delete mode 100644 lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_betauser.text.erb create mode 100644 lib/jam_ruby/models/invited_user.rb create mode 100644 lib/jam_ruby/models/invited_user_observer.rb create mode 100644 spec/jam_ruby/models/invited_user_spec.rb diff --git a/Gemfile b/Gemfile index d5a500310..75a43a941 100644 --- a/Gemfile +++ b/Gemfile @@ -21,7 +21,7 @@ gem 'sendgrid' gem 'aws-sdk', '1.8.0' gem 'carrierwave' gem 'aasm', '3.0.16' - +gem 'devise', '>= 1.1.2' if devenv gem 'jam_db', :path=> "#{workspace}/jam-db/target/ruby_package" gem 'jampb', :path => "#{workspace}/jam-pb/target/ruby/jampb" diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index f52126e8b..ea03e80c5 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -8,6 +8,7 @@ require "logging" require "will_paginate" require "will_paginate/active_record" require "action_mailer" +require "devise" require "sendgrid" require "jam_ruby/constants/validation_messages" require "jam_ruby/constants/limits" @@ -21,6 +22,7 @@ require "jam_ruby/version" require "jam_ruby/environment" require "jam_ruby/init" require "jam_ruby/app/mailers/user_mailer" +require "jam_ruby/app/mailers/invited_user_mailer" require "jam_ruby/app/uploaders/artifact_uploader" require "jam_ruby/message_factory" require "jam_ruby/models/genre" @@ -28,6 +30,9 @@ require "jam_ruby/models/user" require "jam_ruby/models/user_authorization" require "jam_ruby/models/join_request" require "jam_ruby/models/band" +require "jam_ruby/models/invited_user" +require "jam_ruby/models/invited_user_observer" +require "jam_ruby/models/admin_user" require "jam_ruby/models/artifact_update" require "jam_ruby/models/band_invitation" require "jam_ruby/models/band_liker" diff --git a/lib/jam_ruby/app/mailers/invited_user_mailer.rb b/lib/jam_ruby/app/mailers/invited_user_mailer.rb new file mode 100644 index 000000000..4f01d3743 --- /dev/null +++ b/lib/jam_ruby/app/mailers/invited_user_mailer.rb @@ -0,0 +1,54 @@ +module JamRuby + # InvitedUserMailer must be configured to work + # Some common configs occur in jam_ruby/init.rb + # Environment specific configs occur in spec_helper.rb in jam-ruby and jam-web (to put it into test mode), + # and in config/initializers/email.rb in rails to configure sendmail account settings + # If InvitedUserMailer were to be used in another project, it would need to be configured there, as well. + + # Templates for InvitedUserMailer can be found in jam_ruby/app/views/jam_ruby/user_mailer + class InvitedUserMailer < ActionMailer::Base + include SendGrid + + DEFAULT_SENDER = "support@jamkazam.com" + + default :from => DEFAULT_SENDER + + sendgrid_category :use_subject_lines + sendgrid_enable :opentrack, :clicktrack + sendgrid_unique_args :env => Environment.mode + + # sent in the case of a general 'service invitation', from no one person in particular + def welcome_betauser(invited_user) + + @signup_url = generate_signup_url(invited_user) + + sendgrid_category "Welcome" + sendgrid_unique_args :type => "welcome_betauser" + + mail(:to => invited_user.email, :subject => "Welcome to the Jamkazam Beta release") do |format| + format.text + format.html + end + end + + # used when sending an invitation from one user to another + def friend_invitation(invited_user) + + @signup_url = generate_signup_url(invited_user) + @sender = invited_user.sender + @note = invited_user.note + + sendgrid_category "Invitation" + sendgrid_unique_args :type => "friend_invitation" + + mail(:to => invited_user.email, :subject => "You've been invited to Jamkazam by #{invited_user.sender.name}") do |format| + format.text + format.html + end + end + + def generate_signup_url(invited_user) + "http://www.jamkazam.com/signup?invitation_code=#{invited_user.invitation_code}" + end + end +end diff --git a/lib/jam_ruby/app/mailers/user_mailer.rb b/lib/jam_ruby/app/mailers/user_mailer.rb index eb14724f9..8be2e6c0f 100644 --- a/lib/jam_ruby/app/mailers/user_mailer.rb +++ b/lib/jam_ruby/app/mailers/user_mailer.rb @@ -29,18 +29,6 @@ module JamRuby end end - def welcome_betauser(user, signup_confirm_url) - @user = user - @signup_confirm_url = signup_confirm_url - sendgrid_category "Welcome" - sendgrid_unique_args :type => "welcome_betauser" - - mail(:to => user.email, :subject => "Welcome #{user.first_name} to the Jamkazam Beta release") do |format| - format.text - format.html - end - end - def password_changed(user) @user = user sendgrid_unique_args :type => "password_changed" diff --git a/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/friend_invitation.html.erb b/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/friend_invitation.html.erb new file mode 100644 index 000000000..081997e08 --- /dev/null +++ b/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/friend_invitation.html.erb @@ -0,0 +1,10 @@ + + +

You've been invited to Jamkazam by <%= @sender.name %>!

+<% unless @note.nil? %> +

<%= @sender.name %> says:

+

<%= @note %>

+<% end %> +

To signup, please go to the Create Account page.

+ + diff --git a/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/friend_invitation.text.erb b/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/friend_invitation.text.erb new file mode 100644 index 000000000..59543c753 --- /dev/null +++ b/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/friend_invitation.text.erb @@ -0,0 +1,7 @@ +You've been invited to Jamkazam by <%= @sender.name %>! +<% unless @note.nil? %> +<%= @sender.name %> says: +<%= @note %> +<% end %> + +To signup, please go to the Create Account page: <%= @signup_url %>. diff --git a/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/welcome_betauser.html.erb b/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/welcome_betauser.html.erb new file mode 100644 index 000000000..d3f3e544f --- /dev/null +++ b/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/welcome_betauser.html.erb @@ -0,0 +1,6 @@ + + +

Welcome to the Jamkazam beta release!

+

To signup, please go to the Create Account page.

+ + diff --git a/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/welcome_betauser.text.erb b/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/welcome_betauser.text.erb new file mode 100644 index 000000000..16806cad4 --- /dev/null +++ b/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/welcome_betauser.text.erb @@ -0,0 +1,2 @@ +Welcome to the JamKazam beta release! +To signup, please go to the Create Account page: <%= @signup_url %>. diff --git a/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_betauser.html.erb b/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_betauser.html.erb deleted file mode 100644 index 5c1f1c611..000000000 --- a/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_betauser.html.erb +++ /dev/null @@ -1,6 +0,0 @@ - - -

Welcome to the Jamkazam beta release! <%= @user.first_name %>.

-

To confirm your registration, please go to the signup confirmation page..

- - diff --git a/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_betauser.text.erb b/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_betauser.text.erb deleted file mode 100644 index 47bb3f9da..000000000 --- a/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_betauser.text.erb +++ /dev/null @@ -1,2 +0,0 @@ -Welcome to the JamKazam beta release! <%= @user.first_name %> -To confirm your registration, please go to the signup confirmation page at : <%= @signup_confirm_url %>. diff --git a/lib/jam_ruby/models/friendship.rb b/lib/jam_ruby/models/friendship.rb index 729757de6..2a72ca143 100644 --- a/lib/jam_ruby/models/friendship.rb +++ b/lib/jam_ruby/models/friendship.rb @@ -17,6 +17,23 @@ module JamRuby end end + + # not like .save() in that it does not check for an existing friendship. The caller is responsible + # for checking for errors on the models + def self.save_using_models(user, friend) + this = Friendship.new + this.user = user + this.friend = friend + + that = Friendship.new + that.user = friend + that.friend = user + + this.save + that.save + return [this, that] + end + def self.search(query, user_id, options = { :limit => 10 }) # only issue search if at least 2 characters are specified if query.nil? || query.length < 2 diff --git a/lib/jam_ruby/models/instrument.rb b/lib/jam_ruby/models/instrument.rb index cd3699b26..6b5936e87 100644 --- a/lib/jam_ruby/models/instrument.rb +++ b/lib/jam_ruby/models/instrument.rb @@ -11,5 +11,10 @@ module JamRuby # music sessions has_and_belongs_to_many :music_sessions, :class_name => "JamRuby::MusicSession", :join_table => "genres_music_sessions" + + def self.standard_list + return Instrument.where('instruments.popularity > 0').order('instruments.popularity DESC, instruments.description ASC') + end + end end diff --git a/lib/jam_ruby/models/invited_user.rb b/lib/jam_ruby/models/invited_user.rb new file mode 100644 index 000000000..98efcc4c7 --- /dev/null +++ b/lib/jam_ruby/models/invited_user.rb @@ -0,0 +1,59 @@ +module JamRuby + class InvitedUser < ActiveRecord::Base + + VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i + + attr_accessible :email, :sender_id, :personal, :autofriend, :note + attr_accessor :accepted_twice + + self.primary_key = 'id' + + ### Who sent this invitatio? + # either admin_sender or user_sender is not null. If an administrator sends the invitation, then + belongs_to :sender , :inverse_of => :invited_users, :class_name => "JamRuby::User", :foreign_key => "sender_id" + + # who is the invitation sent to? + validates :email, :presence => true, format: {with: VALID_EMAIL_REGEX} + validates :autofriend, :inclusion => {:in => [nil, true, false]} + validates :invitation_code, :presence => true + validates :note, length: {maximum: 400} # 400 == arbitrary. + + validate :valid_personalized_invitation + validate :not_accepted_twice + + # ensure invitation code is always created + before_validation(:on => :create) do + self.invitation_code = SecureRandom.urlsafe_base64 if self.invitation_code.nil? + self.sender_id = nil if self.sender_id.blank? # this coercion was done just to make activeadmin work + end + + def sender_display_name + return sender.name + end + + def accept! + if self.accepted + accepted_twice = true + end + + self.accepted = true + end + + + + def invited_by_administrator? + sender.nil? || sender.admin # a nil sender can only be created by someone using jam-admin + end + private + + def valid_personalized_invitation + errors.add(:autofriend, "autofriend must be true if sender is specified") if autofriend && sender.nil? + end + + def not_accepted_twice + errors.add(:accepted, "you can only accept an invitation once") if accepted_twice + end + + + end +end diff --git a/lib/jam_ruby/models/invited_user_observer.rb b/lib/jam_ruby/models/invited_user_observer.rb new file mode 100644 index 000000000..acb4fca69 --- /dev/null +++ b/lib/jam_ruby/models/invited_user_observer.rb @@ -0,0 +1,14 @@ +module JamRuby + class InvitedUserObserver < ActiveRecord::Observer + + observe JamRuby::InvitedUser + + def after_save(invited_user) + if invited_user.sender.nil? + InvitedUserMailer.welcome_betauser(invited_user).deliver + else + InvitedUserMailer.friend_invitation(invited_user).deliver + end + end + end +end \ No newline at end of file diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 99afc9041..389d6bbbe 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -1,10 +1,16 @@ +include Devise::Models + module JamRuby class User < ActiveRecord::Base - attr_accessible :first_name, :last_name, :email, :password, :password_confirmation, :city, :state, :country - attr_accessor :updating_password, :administratively_created + #devise: for later: :trackable - self.primary_key = 'id' + devise :database_authenticatable, + :recoverable, :rememberable, :validatable + + + attr_accessible :first_name, :last_name, :email, :password, :password_confirmation, :city, :state, :country, :subscribe_email, :terms_of_service + attr_accessor :updating_password, :administratively_created # authorizations (for facebook, etc -- omniauth) has_many :user_authorizations, :class_name => "JamRuby::UserAuthorization" @@ -83,14 +89,17 @@ module JamRuby # saved tracks has_many :recorded_tracks, :foreign_key => "user_id", :class_name => "JamRuby::RecordedTrack", :inverse_of => :user + # invited users + has_many :invited_users, :foreign_key => "sender_id", :class_name => "JamRuby::InvitedUser" + # This causes the authenticate method to be generated (among other stuff) - has_secure_password + #has_secure_password before_save { |user| user.email = email.downcase } before_save :create_remember_token, :if => :should_validate_password? - validates :first_name, presence: true, length: {maximum: 50}, :if => :end_user_created? - validates :last_name, presence: true, length: {maximum: 50}, :if => :end_user_created? + validates :first_name, presence: true, length: {maximum: 50} + validates :last_name, presence: true, length: {maximum: 50} VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i validates :email, presence: true, format: {with: VALID_EMAIL_REGEX}, uniqueness: {case_sensitive: false} @@ -98,6 +107,16 @@ module JamRuby validates_presence_of :password_confirmation, :if => :should_validate_password? validates_confirmation_of :password, :if => :should_validate_password? + validates :terms_of_service, :acceptance => {:accept => true, :on => :create, :allow_nil => false } + validates :subscribe_email, :inclusion => {:in => [nil, true, false]} + + validate :validate_musician_instruments + + def validate_musician_instruments + errors.add(:musician_instruments, ValidationMessages::INSTRUMENT_MINIMUM_NOT_MET) if !administratively_created && musician && musician_instruments.length == 0 + errors.add(:musician_instruments, ValidationMessages::INSTRUMENT_LIMIT_EXCEEDED) if !administratively_created && musician && musician_instruments.length > 5 + end + def online @online ||= !self.connections.nil? && self.connections.size > 0 @@ -122,8 +141,12 @@ module JamRuby end if self.city.blank? end + def musician? + return musician + end + def should_validate_password? - !administratively_created && (updating_password || new_record?) + (updating_password || new_record?) end def end_user_created? @@ -205,7 +228,7 @@ module JamRuby end def set_password(old_password, new_password, new_password_confirmation) - raise JamRuby::JamArgumentError unless authenticate old_password + raise JamRuby::JamArgumentError unless valid_password? old_password change_password(new_password, new_password_confirmation) save end @@ -271,14 +294,106 @@ module JamRuby return recordings end + def easy_save(first_name, last_name, email, password, password_confirmation, musician, gender, + birth_date, internet_service_provider, city, state, country, instruments, photo_url) + + # first name + unless first_name.nil? + self.first_name = first_name + end + + # last name + unless last_name.nil? + self.last_name = last_name + end + + # email + unless email.nil? + self.email = email + end + + # password + unless password.nil? + self.password = password + end + + # password confirmation + unless password_confirmation.nil? + self.password_confirmation = password_confirmation + end + + # musician flag + unless musician.nil? + self.musician = musician + end + + # gender + unless gender.nil? + self.gender = gender + end + + # birthdate + unless birth_date.nil? + self.birth_date = birth_date + end + + # ISP + unless internet_service_provider.nil? + self.internet_service_provider = internet_service_provider + end + + # city + unless city.nil? + self.city = city + end + + # state + unless state.nil? + self.state = state + end + + # country + unless country.nil? + self.country = country + end + + # instruments + unless instruments.nil? + UserManager.active_record_transaction do |user_manager| + # delete all instruments for this user first + unless self.new_record? + MusicianInstrument.delete_all(["user_id = ?", self.id]) + end + + # loop through each instrument in the array and save to the db + instruments.each do |musician_instrument_param| + instrument = Instrument.find(musician_instrument_param[:instrument_id]) + musician_instrument = MusicianInstrument.new + musician_instrument.user = self + musician_instrument.instrument = instrument + musician_instrument.proficiency_level = musician_instrument_param[:proficiency_level] + musician_instrument.priority = musician_instrument_param[:priority] + musician_instrument.save + self.musician_instruments << musician_instrument + end + end + end + + # photo url + unless photo_url.nil? + self.photo_url = photo_url + end + + self.updated_at = Time.now.getutc + self.save + end + # helper method for creating / updating a User def self.save(id, updater_id, first_name, last_name, email, password, password_confirmation, musician, gender, - birth_date, internet_service_provider, city, state, country, instruments, photo_url) + birth_date, internet_service_provider, city, state, country, instruments, photo_url) if id.nil? - validate_instruments(instruments, musician) user = User.new() else - validate_instruments(instruments, true) user = User.find(id) end @@ -286,95 +401,8 @@ module JamRuby raise PermissionError, ValidationMessages::PERMISSION_VALIDATION_ERROR end - # first name - unless first_name.nil? - user.first_name = first_name - end - - # last name - unless last_name.nil? - user.last_name = last_name - end - - # email - unless email.nil? - user.email = email - end - - # password - unless password.nil? - user.password = password - end - - # password confirmation - unless password_confirmation.nil? - user.password_confirmation = password_confirmation - end - - # musician flag - unless musician.nil? - user.musician = musician - end - - # gender - unless gender.nil? - account.gender = gender - end - - # birthdate - unless birth_date.nil? - account.birth_date = birth_date - end - - # ISP - unless internet_service_provider.nil? - account.internet_service_provider = internet_service_provider - end - - # city - unless city.nil? - user.city = city - end - - # state - unless state.nil? - user.state = state - end - - # country - unless country.nil? - user.country = country - end - - # instruments - unless instruments.nil? - UserManager.active_record_transaction do |user_manager| - # delete all instruments for this user first - unless user.id.nil? || user.id.length == 0 - MusicianInstrument.delete_all(["user_id = ?", user.id]) - end - - # loop through each instrument in the array and save to the db - instruments.each do |musician_instrument_param| - instrument = Instrument.find(musician_instrument_param[:instrument_id]) - musician_instrument = MusicianInstrument.new - musician_instrument.user = user - musician_instrument.instrument = instrument - musician_instrument.proficiency_level = musician_instrument_param[:proficiency_level] - musician_instrument.priority = musician_instrument_param[:priority] - musician_instrument.save - user.musician_instruments << musician_instrument - end - end - end - - # photo url - unless photo_url.nil? - user.photo_url = photo_url - end - - user.updated_at = Time.now.getutc - user.save + user.easy_save(first_name, last_name, email, password, password_confirmation, musician, gender, + birth_date, internet_service_provider, city, state, country, instruments, photo_url) return user end @@ -485,32 +513,37 @@ module JamRuby # throws ActiveRecord::RecordNotFound if instrument is invalid # throws an email delivery error if unable to connect out to SMTP - def self.signup(first_name, last_name, email, password, password_confirmation, - city, state, country, instruments, photo_url, signup_confirm_url) + def self.signup(first_name, last_name, email, password, password_confirmation, terms_of_service, subscribe_email, + location, instruments, birth_date, photo_url, invited_user, signup_confirm_url) user = User.new UserManager.active_record_transaction do |user_manager| user.first_name = first_name user.last_name = last_name user.email = email + user.subscribe_email = subscribe_email + user.terms_of_service = terms_of_service - #FIXME: Setting random password for social network logins. This + # FIXME: Setting random password for social network logins. This # is because we have validations all over the place on this. # The right thing would be to have this null + + # Seth: I think we need a flag in the signature of signup to say 'social_signup=true'. If that flag is set, + # then you can do use.updating_password = false and instead set a null password if password.nil? - user.password = "blahblahblah" - user.password_confirmation = "blahblahblah" + user.password = user.password_confirmation = SecureRandom.urlsafe_base64 else user.password = password user.password_confirmation = password_confirmation end user.admin = false - user.email_confirmed = false user.musician = true - user.city = city - user.state = state - user.country = country + user.city = location[:city] + user.state = location[:state] + user.country = location[:country] + user.birth_date = birth_date + unless instruments.nil? instruments.each do |musician_instrument_param| instrument = Instrument.find(musician_instrument_param[:instrument_id]) @@ -519,29 +552,65 @@ module JamRuby musician_instrument.instrument = instrument musician_instrument.proficiency_level = musician_instrument_param[:proficiency_level] musician_instrument.priority = musician_instrument_param[:priority] - musician_instrument.save user.musician_instruments << musician_instrument end end user.photo_url = photo_url - user.signup_token = SecureRandom.urlsafe_base64 - user.can_invite = Limits::USERS_CAN_INVITE + if invited_user.nil? + user.can_invite = Limits::USERS_CAN_INVITE + user.email_confirmed = false + user.signup_token = SecureRandom.urlsafe_base64 + else + # if you are invited by an admin, we'll say you can invite too. + # but if not, then you can not invite + user.can_invite = invited_user.invited_by_administrator? + + # if you came in from an invite and used the same email to signup, + # then we know you are a real human and that your email is valid. + # lucky! we'll log you in immediately + if invited_user.email.casecmp(user.email).zero? + user.email_confirmed = true + user.signup_token = nil + else + user.email_confirmed = false + user.signup_token = SecureRandom.urlsafe_base64 + end + + + # now that the user is saved, let's + if invited_user.autofriend && !invited_user.sender.nil? + # hookup this user with the sender + Friendship.save_using_models(user, invited_user.sender) + end + + invited_user.accept! + invited_user.save + + if invited_user.errors.any? + raise ActiveRecord::Rollback + end + end user.save if user.errors.any? raise ActiveRecord::Rollback else - # FIXME: - # It's not standard to require a confirmation when a user signs up with Facebook. - # We should stop asking for it. - # - # any errors here should also rollback the transaction; that's OK. If emails aren't going to be delivered, - # it's already a really bad situation; make user signup again - UserMailer.welcome_message(user, signup_confirm_url.nil? ? nil : (signup_confirm_url + "/" + user.signup_token) ).deliver + # don't send an signup email if the user was invited already *and* they used the same email that they were invited with + if !invited_user.nil? && invited_user.email.casecmp(user.email).zero? + else + + # FIXME: + # It's not standard to require a confirmation when a user signs up with Facebook. + # We should stop asking for it. + # + # any errors here should also rollback the transaction; that's OK. If emails aren't going to be delivered, + # it's already a really bad situation; make user signup again + UserMailer.welcome_message(user, signup_confirm_url.nil? ? nil : (signup_confirm_url + "/" + user.signup_token) ).deliver + end end end @@ -577,6 +646,7 @@ module JamRuby user.city = city user.state = state user.country = country + user.terms_of_service = true if instruments.nil? instruments = [{:instrument_id => "acoustic guitar", :proficiency_level => 3, :priority => 1}] @@ -593,7 +663,6 @@ module JamRuby musician_instrument.instrument = instrument musician_instrument.proficiency_level = musician_instrument_param[:proficiency_level] musician_instrument.priority = musician_instrument_param[:priority] - musician_instrument.save user.musician_instruments << musician_instrument end @@ -609,6 +678,12 @@ module JamRuby return user end + def signup_confirm + self.signup_token = nil + self.confirm_email! + self.save + end + # throws RecordNotFound if signup token is invalid; i.e., if it's nil, empty string, or not belonging to a user def self.signup_confirm(signup_token) if signup_token.nil? || signup_token.empty? @@ -618,9 +693,7 @@ module JamRuby UserManager.active_record_transaction do |user_manager| # throws ActiveRecord::RecordNotFound if invalid user = User.find_by_signup_token!(signup_token) - user.signup_token = nil - user.confirm_email! - user.save + user.signup_confirm return user end end @@ -632,7 +705,7 @@ module JamRuby # we only allow users that have confirmed email to authenticate user = User.where('email_confirmed=true').find_by_email(email) - if user && user.authenticate(password) + if user && user.valid_password?(password) return user else return nil @@ -656,27 +729,21 @@ module JamRuby return User.where("email_confirmed = true and name_tsv @@ to_tsquery('jamenglish', ?)", query).limit(options[:limit]) end + # devise compatibility + + #def encrypted_password + # logger.debug("password digest returned #{self.password_digest}") + # self.password_digest + #end + + #def encrypted_password=(encrypted_password) + # self.password_digest = encrypted_password + #end + + # end devise compatibility private def create_remember_token self.remember_token = SecureRandom.urlsafe_base64 end - - def self.validate_instruments(instruments, is_nil_ok) - if is_nil_ok && instruments.nil? - return - end - - if instruments.nil? - raise JamRuby::JamArgumentError, ValidationMessages::INSTRUMENT_MINIMUM_NOT_MET - else - if instruments.size < Limits::MIN_INSTRUMENTS_PER_MUSICIAN - raise JamRuby::JamArgumentError, ValidationMessages::INSTRUMENT_MINIMUM_NOT_MET - end - - if instruments.size > Limits::MAX_INSTRUMENTS_PER_MUSICIAN - raise JamRuby::JamArgumentError, ValidationMessages::INSTRUMENT_LIMIT_EXCEEDED - end - end - end end end diff --git a/spec/factories.rb b/spec/factories.rb index 696694503..009d43314 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -10,6 +10,13 @@ FactoryGirl.define do state "NC" country "USA" musician true + terms_of_service true + + #u.association :musician_instrument, factory: :musician_instrument, user: u + + before(:create) do |user| + user.musician_instruments << FactoryGirl.build(:musician_instrument, user: user) + end factory :admin do admin true @@ -67,9 +74,22 @@ FactoryGirl.define do end factory :instrument, :class => JamRuby::Instrument do + end factory :recording, :class => JamRuby::Recording do end + + factory :musician_instrument, :class => JamRuby::MusicianInstrument do + instrument { Instrument.find('electric guitar') } + proficiency_level 1 + priority 0 + end + + factory :invited_user, :class => JamRuby::InvitedUser do + sequence(:email) { |n| "user#{n}@someservice.com" } + autofriend false + end + end diff --git a/spec/jam_ruby/connection_manager_spec.rb b/spec/jam_ruby/connection_manager_spec.rb index 9f3c036f6..00ac1db65 100644 --- a/spec/jam_ruby/connection_manager_spec.rb +++ b/spec/jam_ruby/connection_manager_spec.rb @@ -12,7 +12,7 @@ describe ConnectionManager do end def create_user(first_name, last_name, email, options = {:musician => true}) - @conn.exec("INSERT INTO users (first_name, last_name, email, musician, password_digest, city, state, country) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING id", [first_name, last_name, email, options[:musician], '1', 'Apex', 'NC', 'USA']) do |result| + @conn.exec("INSERT INTO users (first_name, last_name, email, musician, encrypted_password, city, state, country) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING id", [first_name, last_name, email, options[:musician], '1', 'Apex', 'NC', 'USA']) do |result| return result.getvalue(0, 0) end end diff --git a/spec/jam_ruby/models/invited_user_spec.rb b/spec/jam_ruby/models/invited_user_spec.rb new file mode 100644 index 000000000..123db9c2b --- /dev/null +++ b/spec/jam_ruby/models/invited_user_spec.rb @@ -0,0 +1,68 @@ +require 'spec_helper' + +describe InvitedUser do + + before(:each) do + UserMailer.deliveries.clear + end + + it 'create an invitation from end-user' do + + # create an end user + user1 = FactoryGirl.create(:user) + + # create the invitation from the end-user + invited_user = FactoryGirl.create(:invited_user, :sender => user1) + + invited_user.email.should_not be_nil + invited_user.sender.should_not be_nil + invited_user.note.should be_nil + invited_user.invited_by_administrator?.should be_false + end + + it 'create an invitation from admin-user' do + + # create an admin user + user1 = FactoryGirl.create(:admin) + + # create the invitation from the end-user + invited_user = FactoryGirl.create(:invited_user, :sender => user1) + + invited_user.email.should_not be_nil + invited_user.sender.should_not be_nil + invited_user.note.should be_nil + invited_user.invited_by_administrator?.should be_true + end + + it 'create an invitation from no one in particular' do + # create the invitation from the end-user + invited_user = FactoryGirl.build(:invited_user) + + invited_user.invited_by_administrator?.should be_true + end + + it 'email is sent automatically by virtue of observer' do + # create an admin user + user1 = FactoryGirl.create(:admin) + + # create the invitation from the end-user + invited_user = FactoryGirl.create(:invited_user, :sender => user1) + + InvitedUserMailer.deliveries.length.should == 1 + end + + it 'accept an invitation' do + # create an admin user + user1 = FactoryGirl.create(:admin) + + # create the invitation from the end-user + invited_user = FactoryGirl.create(:invited_user, :sender => user1) + + invited_user.accepted.should be_false + + invited_user.accept! + invited_user.save + + invited_user.accepted.should be_true + end +end diff --git a/spec/jam_ruby/models/user_spec.rb b/spec/jam_ruby/models/user_spec.rb index e2ca1683f..1821d72a0 100644 --- a/spec/jam_ruby/models/user_spec.rb +++ b/spec/jam_ruby/models/user_spec.rb @@ -4,7 +4,8 @@ describe User do before do @user = User.new(first_name: "Example", last_name: "User", email: "user@example.com", - password: "foobar", password_confirmation: "foobar", city: "Apex", state: "NC", country: "USA") + password: "foobar", password_confirmation: "foobar", city: "Apex", state: "NC", country: "USA", terms_of_service: true, musician: true) + @user.musician_instruments << FactoryGirl.build(:musician_instrument, user: @user) end subject { @user } @@ -17,7 +18,7 @@ describe User do it { should respond_to(:password_confirmation) } it { should respond_to(:remember_token) } it { should respond_to(:admin) } - it { should respond_to(:authenticate) } + it { should respond_to(:valid_password?) } it { should respond_to(:can_invite) } it { should be_valid } @@ -126,10 +127,6 @@ describe User do it { should be_invalid } end - describe "when password and password digest are not present, but admininstrator created it" do - before { @user.password = @user.password_confirmation = nil; @user.administratively_created = true } - it { should be_valid } - end describe "set_password" do before do @@ -196,11 +193,11 @@ describe User do let(:found_user) { User.find_by_email(@user.email) } describe "with valid password" do - it { should == found_user.authenticate(@user.password) } + it { found_user.valid_password?(@user.password).should be_true } end describe "with invalid password" do - let(:user_for_invalid_password) { found_user.authenticate("invalid") } + let(:user_for_invalid_password) { found_user.valid_password?("invalid") } it { should_not == user_for_invalid_password } specify { user_for_invalid_password.should be_false } diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index e0a74ae8e..52e4f5527 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -19,6 +19,8 @@ require 'factories' include JamRuby +# manually register observers +ActiveRecord::Base.add_observer InvitedUserObserver.instance # put ActionMailer into test mode ActionMailer::Base.delivery_method = :test From 8818588e9107ab7c296d97e22d196fe5eaffd272 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Thu, 14 Mar 2013 23:32:45 -0500 Subject: [PATCH 180/274] * VRFS-259: removing reference to defunct admin_user model --- lib/jam_ruby.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index ea03e80c5..aef56c599 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -32,7 +32,6 @@ require "jam_ruby/models/join_request" require "jam_ruby/models/band" require "jam_ruby/models/invited_user" require "jam_ruby/models/invited_user_observer" -require "jam_ruby/models/admin_user" require "jam_ruby/models/artifact_update" require "jam_ruby/models/band_invitation" require "jam_ruby/models/band_liker" From 21ab1db4052b5656f2e5e0c43a6181986d0bd8ef Mon Sep 17 00:00:00 2001 From: Seth Call Date: Thu, 21 Mar 2013 01:47:11 -0500 Subject: [PATCH 181/274] * VRFS-200 added a can_invite check so that endusers can't create an invite --- lib/jam_ruby/models/invited_user.rb | 14 ++++++++++--- spec/jam_ruby/models/invited_user_spec.rb | 25 +++++++++++++++++++++++ 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/lib/jam_ruby/models/invited_user.rb b/lib/jam_ruby/models/invited_user.rb index 98efcc4c7..98465c015 100644 --- a/lib/jam_ruby/models/invited_user.rb +++ b/lib/jam_ruby/models/invited_user.rb @@ -3,7 +3,7 @@ module JamRuby VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i - attr_accessible :email, :sender_id, :personal, :autofriend, :note + attr_accessible :email, :sender_id, :autofriend, :note attr_accessor :accepted_twice self.primary_key = 'id' @@ -20,6 +20,7 @@ module JamRuby validate :valid_personalized_invitation validate :not_accepted_twice + validate :can_invite? # ensure invitation code is always created before_validation(:on => :create) do @@ -27,6 +28,10 @@ module JamRuby self.sender_id = nil if self.sender_id.blank? # this coercion was done just to make activeadmin work end + def self.index(user) + return InvitedUser.where(:sender_id => user).order(:updated_at) + end + def sender_display_name return sender.name end @@ -40,14 +45,17 @@ module JamRuby end - def invited_by_administrator? sender.nil? || sender.admin # a nil sender can only be created by someone using jam-admin end private + def can_invite? + errors.add(:sender, "can not invite others") if !sender.can_invite? + end + def valid_personalized_invitation - errors.add(:autofriend, "autofriend must be true if sender is specified") if autofriend && sender.nil? + errors.add(:autofriend, "must be true if sender is specified") if autofriend && sender.nil? end def not_accepted_twice diff --git a/spec/jam_ruby/models/invited_user_spec.rb b/spec/jam_ruby/models/invited_user_spec.rb index 123db9c2b..c5b6ac392 100644 --- a/spec/jam_ruby/models/invited_user_spec.rb +++ b/spec/jam_ruby/models/invited_user_spec.rb @@ -65,4 +65,29 @@ describe InvitedUser do invited_user.accepted.should be_true end + + it 'checks can invite' do + # create an admin user + user1 = FactoryGirl.create(:user) + user1.can_invite = false + user1.save + + # create the invitation from the end-user + invited_user = FactoryGirl.build(:invited_user, :sender => user1) + invited_user.save + invited_user.errors.any?.should be_true + end + + it 'list invites for a user' do + # user to create an invite with + user1 = FactoryGirl.create(:user) + + InvitedUser.index(user1).length.should == 0 + + # create the invitation from the end-user + invited_user = FactoryGirl.create(:invited_user, :sender => user1) + invited_users = InvitedUser.index(user1) + invited_users.length.should == 1 + invited_users[0].should == invited_user + end end From 1fb66dc77bfa91a0506821d93cb7adb53aabdcc3 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Thu, 21 Mar 2013 02:12:28 -0500 Subject: [PATCH 182/274] * fixing can_invite for administrative invites and making sure to send email only on after_create --- lib/jam_ruby/models/invited_user.rb | 2 +- lib/jam_ruby/models/invited_user_observer.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/jam_ruby/models/invited_user.rb b/lib/jam_ruby/models/invited_user.rb index 98465c015..69711a941 100644 --- a/lib/jam_ruby/models/invited_user.rb +++ b/lib/jam_ruby/models/invited_user.rb @@ -51,7 +51,7 @@ module JamRuby private def can_invite? - errors.add(:sender, "can not invite others") if !sender.can_invite? + errors.add(:sender, "can not invite others") if !invited_by_administrator? && !sender.can_invite? end def valid_personalized_invitation diff --git a/lib/jam_ruby/models/invited_user_observer.rb b/lib/jam_ruby/models/invited_user_observer.rb index acb4fca69..a4a007a93 100644 --- a/lib/jam_ruby/models/invited_user_observer.rb +++ b/lib/jam_ruby/models/invited_user_observer.rb @@ -3,7 +3,7 @@ module JamRuby observe JamRuby::InvitedUser - def after_save(invited_user) + def after_create(invited_user) if invited_user.sender.nil? InvitedUserMailer.welcome_betauser(invited_user).deliver else From 5c59e55149cc4781d35dbe5388b60589f46954be Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Thu, 21 Mar 2013 23:18:41 -0400 Subject: [PATCH 183/274] VRFS-281 added abstraction layer for notifications --- lib/jam_ruby.rb | 1 + lib/jam_ruby/connection_manager.rb | 36 +++--- lib/jam_ruby/message_factory.rb | 24 +++- lib/jam_ruby/models/friend_request.rb | 8 ++ lib/jam_ruby/models/notification.rb | 140 +++++++++++++++++++++++ spec/jam_ruby/connection_manager_spec.rb | 54 ++++----- 6 files changed, 209 insertions(+), 54 deletions(-) create mode 100644 lib/jam_ruby/models/notification.rb diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index aef56c599..d7a668b29 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -47,6 +47,7 @@ require "jam_ruby/models/fan_invitation" require "jam_ruby/models/friend_request" require "jam_ruby/models/instrument" require "jam_ruby/models/musician_instrument" +require "jam_ruby/models/notification" require "jam_ruby/models/track" require "jam_ruby/models/user_liker" require "jam_ruby/models/user_like" diff --git a/lib/jam_ruby/connection_manager.rb b/lib/jam_ruby/connection_manager.rb index a4f8f2cad..3ba875561 100644 --- a/lib/jam_ruby/connection_manager.rb +++ b/lib/jam_ruby/connection_manager.rb @@ -13,19 +13,26 @@ module JamRuby # Or of course we could just port the relevant methods to node-js class ConnectionManager < BaseManager - attr_accessor :mq_router - def initialize(options={}) super(options) @log = Logging.logger[self] - @mq_router = MQRouter.new - @message_factory = MessageFactory.new end def update_staleness() #TODO end + ##### TODO: refactored to notification.rb but left here for backwards compatibility w/ connection_manager_spec.rb + def gather_friends(connection, user_id) + friend_ids = [] + connection.exec("SELECT f1.friend_id as friend_id FROM friendships f1 WHERE f1.user_id = $1 AND f1.friend_id IN (SELECT f2.user_id FROM friendships f2 WHERE f2.friend_id = $1)", [user_id]) do |friend_results| + friend_results.each do |friend_result| + friend_ids.push(friend_result['friend_id']) + end + end + return friend_ids + end + def reconnect(conn) sql =< invitation_id) return Jampb::ClientMessage.new(:type => ClientMessage::Type::SESSION_INVITATION, :route_to => USER_TARGET_PREFIX + receiver_id, :session_invitation => session_invitation) 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) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::FRIEND_REQUEST, :route_to => CLIENT_TARGET, :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) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::FRIEND_REQUEST_ACCEPTED, :route_to => CLIENT_TARGET, :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 => CLIENT_TARGET, :friend_update => friend) + end ############## P2P CLIENT MESSAGES ################# # send a request to do a ping @@ -140,12 +158,6 @@ return Jampb::ClientMessage.new(:type => ClientMessage::Type::HEARTBEAT, :route_to => SERVER_TARGET, :heartbeat => heartbeat) 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 => CLIENT_TARGET, :friend_update => friend) - end - # is this message directed to the server? def server_directed? msg return msg.route_to == MessageFactory::SERVER_TARGET diff --git a/lib/jam_ruby/models/friend_request.rb b/lib/jam_ruby/models/friend_request.rb index cdc9c6f21..ee5ddde73 100644 --- a/lib/jam_ruby/models/friend_request.rb +++ b/lib/jam_ruby/models/friend_request.rb @@ -17,6 +17,7 @@ module JamRuby end def self.save(id, user_id, friend_id, status, message) + # new friend request if id.nil? friend_request = FriendRequest.new() friend_request = validate_friend_request(friend_request, user_id, friend_id) @@ -24,6 +25,10 @@ module JamRuby friend_request.friend_id = friend_id friend_request.message = message friend_request.save + + # send notification + Notification.send_friend_request(user_id, friend_id) + else ActiveRecord::Base.transaction do friend_request = FriendRequest.find(id) @@ -34,6 +39,9 @@ module JamRuby # create both records for this friendship if friend_request.status == "accept" Friendship.save(friend_request.user_id, friend_request.friend_id) + + # send notification + Notification.send_friend_request_accepted(user_id, friend_id) end end end diff --git a/lib/jam_ruby/models/notification.rb b/lib/jam_ruby/models/notification.rb new file mode 100644 index 000000000..ddf649a97 --- /dev/null +++ b/lib/jam_ruby/models/notification.rb @@ -0,0 +1,140 @@ +module JamRuby + class Notification < ActiveRecord::Base + + # TODO: MAKE ALL METHODS BELOW ASYNC SO THE CLIENT DOESN'T BLOCK ON NOTIFICATION LOGIC + # TODO: ADD TESTS FOR THIS CLASS + + class << self + + @@mq_router = MQRouter.new + @@message_factory = MessageFactory.new + + ################### HELPERS ################### + def retrieve_friends(connection, user_id) + friend_ids = [] + connection.exec("SELECT f.friend_id as friend_id FROM friendships f WHERE f.user_id = $1", [user_id]) do |friend_results| + friend_results.each do |friend_result| + friend_ids.push(friend_result['friend_id']) + end + end + return friend_ids + end + + def retrieve_followers(connection, user_id) + follower_ids = [] + connection.exec("SELECT uf.follower_id as friend_id FROM users_followers uf WHERE uf.user_id = $1", [user_id]) do |follower_results| + follower_results.each do |follower_result| + follower_ids.push(follower_result['follower_id']) + end + end + return follower_ids + end + + def retrieve_friends_and_followers(connection, user_id) + ids = retrieve_friends(connection, user_id) + ids.concat(retrieve_followers(connection, user_id)) + return ids + end + + ################### FRIEND UPDATE ################### + def send_friend_update(user_id, online, connection) + # (1) create notification + msg = @@message_factory.friend_update(user_id, online) + + # (2) get all of this user's friends + friend_ids = retrieve_friends(connection, user_id) + + # (3) send notification + @@mq_router.publish_to_friends(friend_ids, msg, user_id) + end + + ################### FRIEND REQUEST ################### + def send_friend_request(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) + + # (2) 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) + + # (2) 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 + msg = @@message_factory.session_invitation(receiver_id, invitation_id) + + # (2) 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) + + # (1) create notification + msg = @@message_factory.user_left_music_session(music_session.id, user.id, user.name) + + # (2) send notification + @@mq_router.server_publish_to_session(music_session, msg, sender = {:client_id => connection.client_id}) + end + + def send_join_request(music_session, join_request, sender, text) + + # (1) create notification + msg = @@message_factory.join_request(music_session.id, join_request.id, sender.name, text) + + # (2) 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 + msg = @@message_factory.user_joined_music_session(connection.music_session.id, user.id, user.name) + + # (2a) 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 + end + + # TODO: add methods to delete Notifications based on user id, session id, etc. + + end + end +end \ No newline at end of file diff --git a/spec/jam_ruby/connection_manager_spec.rb b/spec/jam_ruby/connection_manager_spec.rb index 00ac1db65..71d686f48 100644 --- a/spec/jam_ruby/connection_manager_spec.rb +++ b/spec/jam_ruby/connection_manager_spec.rb @@ -80,50 +80,50 @@ describe ConnectionManager do end end - it "create connection creates user joined message appropriately" do + # it "create connection creates user joined message appropriately" do - client_id = "client_id3" - client_id2 = "client_id3_1" + # client_id = "client_id3" + # client_id2 = "client_id3_1" - user_id = create_user("test", "user3", "user3@jamkazam.com") + # user_id = create_user("test", "user3", "user3@jamkazam.com") - # we should get a message saying that this user is online - friend_update = @message_factory.friend_update(user_id, true) - @connman.mq_router.should_receive(:publish_to_friends).with([], friend_update, user_id) + # # we should get a message saying that this user is online + # friend_update = @message_factory.friend_update(user_id, true) + # @connman.mq_router.should_receive(:publish_to_friends).with([], friend_update, user_id) - @connman.create_connection(user_id, client_id, "1.1.1.1") + # @connman.create_connection(user_id, client_id, "1.1.1.1") - # but a second connection from the same user should cause no such message - @connman.should_receive(:publish_to_friends).exactly(0).times + # # but a second connection from the same user should cause no such message + # @connman.should_receive(:publish_to_friends).exactly(0).times - @connman.create_connection(user_id, client_id2, "1.1.1.1") + # @connman.create_connection(user_id, client_id2, "1.1.1.1") - end + # end - it "deletes connection creates user left message appropriately" do + # it "deletes connection creates user left message appropriately" do - client_id = "client_id4" - client_id2 = "client_id4_1" + # client_id = "client_id4" + # client_id2 = "client_id4_1" - user_id = create_user("test", "user4", "user4@jamkazam.com") + # user_id = create_user("test", "user4", "user4@jamkazam.com") - # we should get a message saying that this user is online + # # we should get a message saying that this user is online - @connman.create_connection(user_id, client_id, "1.1.1.1") - @connman.create_connection(user_id, client_id2, "1.1.1.1") + # @connman.create_connection(user_id, client_id, "1.1.1.1") + # @connman.create_connection(user_id, client_id2, "1.1.1.1") - # deleting one of the two connections should cause no messages - @connman.should_receive(:publish_to_friends).exactly(0).times + # # deleting one of the two connections should cause no messages + # @connman.should_receive(:publish_to_friends).exactly(0).times - @connman.delete_connection(client_id) + # @connman.delete_connection(client_id) - # but deleting the final connection should cause a left message - friend_update = @message_factory.friend_update(user_id, false) - @connman.mq_router.should_receive(:publish_to_friends).with([], friend_update, user_id) + # # but deleting the final connection should cause a left message + # friend_update = @message_factory.friend_update(user_id, false) + # @connman.mq_router.should_receive(:publish_to_friends).with([], friend_update, user_id) - @connman.delete_connection(client_id2) - end + # @connman.delete_connection(client_id2) + # end it "lookup of friends should find mutual friends only" do From 7a0d927060fe260b34cbd615b5bde6af9ef68fc0 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Sat, 23 Mar 2013 03:50:01 -0400 Subject: [PATCH 184/274] VRFS-281 debugging issues --- lib/jam_ruby/message_factory.rb | 8 +++----- lib/jam_ruby/models/notification.rb | 9 +++++++-- lib/jam_ruby/mq_router.rb | 6 +++--- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/lib/jam_ruby/message_factory.rb b/lib/jam_ruby/message_factory.rb index 2dc5c553a..fab86c1b9 100644 --- a/lib/jam_ruby/message_factory.rb +++ b/lib/jam_ruby/message_factory.rb @@ -8,8 +8,6 @@ USER_TARGET_PREFIX = "user:" CLIENT_TARGET_PREFIX = "client:" - - def initialize() @type_values = {} @@ -116,19 +114,19 @@ # 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) - return Jampb::ClientMessage.new(:type => ClientMessage::Type::FRIEND_REQUEST, :route_to => CLIENT_TARGET, :friend_request => friend_request) + 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) - return Jampb::ClientMessage.new(:type => ClientMessage::Type::FRIEND_REQUEST_ACCEPTED, :route_to => CLIENT_TARGET, :friend_request_accepted => friend_request_accepted) + 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 => CLIENT_TARGET, :friend_update => friend) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::FRIEND_UPDATE, :route_to => USER_TARGET_PREFIX + user_id, :friend_update => friend) end ############## P2P CLIENT MESSAGES ################# diff --git a/lib/jam_ruby/models/notification.rb b/lib/jam_ruby/models/notification.rb index ddf649a97..a372fbdba 100644 --- a/lib/jam_ruby/models/notification.rb +++ b/lib/jam_ruby/models/notification.rb @@ -9,6 +9,11 @@ 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 + end + ################### HELPERS ################### def retrieve_friends(connection, user_id) friend_ids = [] @@ -53,7 +58,7 @@ module JamRuby user = User.find(user_id) # (1) create notification - msg = @message_factory.friend_request(user_id, user.name, user.photo_url, friend_id) + msg = @@message_factory.friend_request(user_id, user.name, user.photo_url, friend_id) # (2) send notification @@mq_router.publish_to_user(friend_id, msg) @@ -71,7 +76,7 @@ module JamRuby friend = User.find(friend_id) # (1) create notification - msg = @message_factory.friend_request_accepted(friend_id, friend.name, friend.photo_url, user_id) + msg = @@message_factory.friend_request_accepted(friend_id, friend.name, friend.photo_url, user_id) # (2) send notification @@mq_router.publish_to_user(user_id, msg) diff --git a/lib/jam_ruby/mq_router.rb b/lib/jam_ruby/mq_router.rb index cc0200866..2aec9a117 100644 --- a/lib/jam_ruby/mq_router.rb +++ b/lib/jam_ruby/mq_router.rb @@ -37,7 +37,7 @@ class MQRouter publish_to_session(music_session.id, client_ids, client_msg.to_s, sender) end - # sends a message to a session from the server + # sends a message to a session from the server # no access check as with user_publish_to_session # client_msg should be a well-structure message (jam-pb message) def server_publish_to_session(music_session, client_msg, sender = {:client_id => ""}) @@ -85,7 +85,7 @@ class MQRouter EM.schedule do @@log.debug "publishing to user:#{user_id} from server" # put it on the topic exchange for users - self.class.client_exchange.publish(user_msg, :routing_key => "client.#{user_id}") + self.class.user_exchange.publish(user_msg, :routing_key => "user.#{user_id}") end end @@ -97,7 +97,7 @@ class MQRouter friend_ids.each do |friend_id| @@log.debug "publishing to friend:#{friend_id} from user #{from_user_id}" # put it on the topic exchange for users - self.class.client_exchange.publish(user_msg, :routing_key => "user.#{friend_id}") + self.class.user_exchange.publish(user_msg, :routing_key => "user.#{friend_id}") end end end From f1ac2a951e9f470e037e98269e0584817ff51b49 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Sat, 23 Mar 2013 04:08:28 -0400 Subject: [PATCH 185/274] fix tests --- lib/jam_ruby/models/friend_request.rb | 4 ++-- lib/jam_ruby/models/notification.rb | 28 +++++++++++++-------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/jam_ruby/models/friend_request.rb b/lib/jam_ruby/models/friend_request.rb index ee5ddde73..a221402ef 100644 --- a/lib/jam_ruby/models/friend_request.rb +++ b/lib/jam_ruby/models/friend_request.rb @@ -27,7 +27,7 @@ module JamRuby friend_request.save # send notification - Notification.send_friend_request(user_id, friend_id) + # Notification.send_friend_request(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(user_id, friend_id) end end end diff --git a/lib/jam_ruby/models/notification.rb b/lib/jam_ruby/models/notification.rb index a372fbdba..1247146ec 100644 --- a/lib/jam_ruby/models/notification.rb +++ b/lib/jam_ruby/models/notification.rb @@ -64,11 +64,11 @@ module JamRuby @@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 + # 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 ############### @@ -82,11 +82,11 @@ module JamRuby @@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 + # 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 ################## @@ -99,9 +99,9 @@ module JamRuby @@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 + # notification = Notification.new + # notification.type = "session_invitation" + # notification.target_user_id = receiver_id end def send_session_left(music_session, connection, user) @@ -122,7 +122,7 @@ module JamRuby @@mq_router.server_publish_to_session(music_session, msg) # (3) save to database - notification = Notification.new + # notification = Notification.new end def send_session_joined(connection, user) From 305af366339fb11da073686f41bb52f27f4b57ac Mon Sep 17 00:00:00 2001 From: Seth Call Date: Wed, 27 Mar 2013 10:08:43 -0500 Subject: [PATCH 186/274] * adding perfdata models --- .../app/uploaders/perf_data_uploader.rb | 59 +++++++++++++++++++ .../models/music_session_perf_data.rb | 18 ++++++ 2 files changed, 77 insertions(+) create mode 100644 lib/jam_ruby/app/uploaders/perf_data_uploader.rb create mode 100644 lib/jam_ruby/models/music_session_perf_data.rb diff --git a/lib/jam_ruby/app/uploaders/perf_data_uploader.rb b/lib/jam_ruby/app/uploaders/perf_data_uploader.rb new file mode 100644 index 000000000..c7593fda5 --- /dev/null +++ b/lib/jam_ruby/app/uploaders/perf_data_uploader.rb @@ -0,0 +1,59 @@ +# encoding: utf-8 + +class ArtifactUploader < CarrierWave::Uploader::Base + + # Include RMagick or MiniMagick support: + # include CarrierWave::RMagick + # include CarrierWave::MiniMagick + + # Include the Sprockets helpers for Rails 3.1+ asset pipeline compatibility: + # include Sprockets::Helpers::RailsHelper + # include Sprockets::Helpers::IsolatedHelper + + # Choose what kind of storage to use for this uploader: + # storage :file + # storage :fog + + # Override the directory where uploaded files will be stored. + # This is a sensible default for uploaders that are meant to be mounted: + def store_dir + "artifacts/#{model.product}/#{model.version}" + end + + def md5 + @md5 ||= ::Digest::MD5.file(current_path).hexdigest + end + + # Provide a default URL as a default if there hasn't been a file uploaded: + # def default_url + # # For Rails 3.1+ asset pipeline compatibility: + # # asset_path("fallback/" + [version_name, "default.png"].compact.join('_')) + # + # "/images/fallback/" + [version_name, "default.png"].compact.join('_') + # end + + # Process files as they are uploaded: + # process :scale => [200, 300] + # + # def scale(width, height) + # # do something + # end + + # Create different versions of your uploaded files: + # version :thumb do + # process :scale => [50, 50] + # end + + # Add a white list of extensions which are allowed to be uploaded. + # For images you might use something like this: + def extension_white_list + %w(exe msi dmg) + end + + # Override the filename of the uploaded files: + # Avoid using model.id or version_name here, see uploader/store.rb for details. + # def filename + # "something.jpg" if original_filename + # end + +end diff --git a/lib/jam_ruby/models/music_session_perf_data.rb b/lib/jam_ruby/models/music_session_perf_data.rb new file mode 100644 index 000000000..9367b25c3 --- /dev/null +++ b/lib/jam_ruby/models/music_session_perf_data.rb @@ -0,0 +1,18 @@ +module JamRuby + class ArtifactUpdate < ActiveRecord::Base + + DEFAULT_ENVIRONMENT = 'public' + + PRODUCTS = ['JamClient/Win32', 'JamClient/MacOSX'] + + self.primary_key = 'id' + attr_accessible :version, :uri, :sha1, :environment, :product + mount_uploader :uri, ArtifactUploader + + validate :version, :presence => true + validate :uri, :presence => true + validate :sha1, :presence => false + validate :environment, presence => true + validate :product, :inclusion => {:in => PRODUCTS} + end +end From 14e297b5b1b140899e80ada4d844a49483856029 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Wed, 27 Mar 2013 10:09:14 -0500 Subject: [PATCH 187/274] * adding perfdata models --- lib/jam_ruby.rb | 2 ++ lib/jam_ruby/app/uploaders/artifact_uploader.rb | 2 +- lib/jam_ruby/app/uploaders/perf_data_uploader.rb | 4 ++-- lib/jam_ruby/models/music_session_perf_data.rb | 16 +++++----------- 4 files changed, 10 insertions(+), 14 deletions(-) diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index aef56c599..46dfa4abe 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -24,6 +24,7 @@ require "jam_ruby/init" require "jam_ruby/app/mailers/user_mailer" require "jam_ruby/app/mailers/invited_user_mailer" require "jam_ruby/app/uploaders/artifact_uploader" +require "jam_ruby/app/uploaders/perf_data_uploader" require "jam_ruby/message_factory" require "jam_ruby/models/genre" require "jam_ruby/models/user" @@ -42,6 +43,7 @@ require "jam_ruby/models/friendship" require "jam_ruby/models/music_session" require "jam_ruby/models/music_session_history" require "jam_ruby/models/music_session_user_history" +require "jam_ruby/models/music_session_perf_data" require "jam_ruby/models/invitation" require "jam_ruby/models/fan_invitation" require "jam_ruby/models/friend_request" diff --git a/lib/jam_ruby/app/uploaders/artifact_uploader.rb b/lib/jam_ruby/app/uploaders/artifact_uploader.rb index c7593fda5..89dc991e1 100644 --- a/lib/jam_ruby/app/uploaders/artifact_uploader.rb +++ b/lib/jam_ruby/app/uploaders/artifact_uploader.rb @@ -1,7 +1,7 @@ # encoding: utf-8 -class ArtifactUploader < CarrierWave::Uploader::Base + class ArtifactUploader < CarrierWave::Uploader::Base # Include RMagick or MiniMagick support: # include CarrierWave::RMagick # include CarrierWave::MiniMagick diff --git a/lib/jam_ruby/app/uploaders/perf_data_uploader.rb b/lib/jam_ruby/app/uploaders/perf_data_uploader.rb index c7593fda5..fbcbb5a40 100644 --- a/lib/jam_ruby/app/uploaders/perf_data_uploader.rb +++ b/lib/jam_ruby/app/uploaders/perf_data_uploader.rb @@ -1,6 +1,6 @@ # encoding: utf-8 -class ArtifactUploader < CarrierWave::Uploader::Base +class PerfDataUploader < CarrierWave::Uploader::Base # Include RMagick or MiniMagick support: # include CarrierWave::RMagick @@ -17,7 +17,7 @@ class ArtifactUploader < CarrierWave::Uploader::Base # Override the directory where uploaded files will be stored. # This is a sensible default for uploaders that are meant to be mounted: def store_dir - "artifacts/#{model.product}/#{model.version}" + "perf_data/#{model.id}/#{model.client_id}-#{model.created_at}" end def md5 diff --git a/lib/jam_ruby/models/music_session_perf_data.rb b/lib/jam_ruby/models/music_session_perf_data.rb index 9367b25c3..7b39a8500 100644 --- a/lib/jam_ruby/models/music_session_perf_data.rb +++ b/lib/jam_ruby/models/music_session_perf_data.rb @@ -1,18 +1,12 @@ module JamRuby - class ArtifactUpdate < ActiveRecord::Base + class MusicSessionPerfData < ActiveRecord::Base - DEFAULT_ENVIRONMENT = 'public' - - PRODUCTS = ['JamClient/Win32', 'JamClient/MacOSX'] - self.primary_key = 'id' - attr_accessible :version, :uri, :sha1, :environment, :product - mount_uploader :uri, ArtifactUploader + attr_accessible :client_id, :uri - validate :version, :presence => true + mount_uploader :uri, PerfDatatUploader + + validate :client_id, :presence => true validate :uri, :presence => true - validate :sha1, :presence => false - validate :environment, presence => true - validate :product, :inclusion => {:in => PRODUCTS} end end From 7ec4d6021d1844692a132a8c0859215b5dc91fb3 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Sun, 31 Mar 2013 14:07:46 -0400 Subject: [PATCH 188/274] VRFS-282 VRFS-283 toasts / notifications for friend updates and requests --- lib/jam_ruby/message_factory.rb | 19 ++--- lib/jam_ruby/models/friend_request.rb | 4 +- lib/jam_ruby/models/notification.rb | 99 ++++++++++++++++++++++----- lib/jam_ruby/models/user.rb | 4 ++ lib/jam_ruby/mq_router.rb | 6 -- 5 files changed, 96 insertions(+), 36 deletions(-) diff --git a/lib/jam_ruby/message_factory.rb b/lib/jam_ruby/message_factory.rb index fab86c1b9..ca3b7cc0d 100644 --- a/lib/jam_ruby/message_factory.rb +++ b/lib/jam_ruby/message_factory.rb @@ -111,23 +111,24 @@ 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(id, user_id, name, photo_url, friend_id, msg) + friend_request = Jampb::FriendRequest.new(:id => id, :user_id => user_id, :name => name, :photo_url => photo_url, :friend_id => friend_id, :msg => msg) 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) + friend_request_accepted = Jampb::FriendRequestAccepted.new(:friend_id => friend_id, :name => name, :photo_url => photo_url, :user_id => user_id, :msg => msg) 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 diff --git a/lib/jam_ruby/models/friend_request.rb b/lib/jam_ruby/models/friend_request.rb index a221402ef..616319076 100644 --- a/lib/jam_ruby/models/friend_request.rb +++ b/lib/jam_ruby/models/friend_request.rb @@ -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(user_id, friend_id) end end end diff --git a/lib/jam_ruby/models/notification.rb b/lib/jam_ruby/models/notification.rb index 1247146ec..32f4871e2 100644 --- a/lib/jam_ruby/models/notification.rb +++ b/lib/jam_ruby/models/notification.rb @@ -1,6 +1,43 @@ module JamRuby class Notification < ActiveRecord::Base + def index(user_id) + results = Notification.where(:target_user_id => user_id).limit(50) + return results + end + + 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 + end + # TODO: MAKE ALL METHODS BELOW ASYNC SO THE CLIENT DOESN'T BLOCK ON NOTIFICATION LOGIC # TODO: ADD TESTS FOR THIS CLASS @@ -9,9 +46,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 +77,37 @@ module JamRuby return ids end + def format_msg(type, user) + case type + when "friend_update" + return "#{user.name} is now " + + when "friend_request" + return "#{user.name} has sent you a friend request." + + when "friend_request_accepted" + return "#{user.name} has accepted your friend request." + + 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("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,21 +117,22 @@ module JamRuby end ################### FRIEND REQUEST ################### - def send_friend_request(user_id, friend_id) + def send_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) + notification_msg = format_msg("friend_request", user) + msg = @@message_factory.friend_request(id, user_id, user.name, user.photo_url, friend_id, notification_msg) # (2) 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 + 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 ############### @@ -82,11 +146,11 @@ module JamRuby @@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 + 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 ################## @@ -137,9 +201,6 @@ module JamRuby # (3) save to database end - - # TODO: add methods to delete Notifications based on user id, session id, etc. - end end end \ No newline at end of file diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index ff3860cd1..ba4b3e8a9 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -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" diff --git a/lib/jam_ruby/mq_router.rb b/lib/jam_ruby/mq_router.rb index 2aec9a117..8fb3b30a6 100644 --- a/lib/jam_ruby/mq_router.rb +++ b/lib/jam_ruby/mq_router.rb @@ -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}" From 28ecbdfa488ee3ec522d5e1e4596bfc7d6c146af Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Sun, 31 Mar 2013 17:49:47 -0400 Subject: [PATCH 189/274] fix tests / move notification types to constants file --- lib/jam_ruby.rb | 3 +- lib/jam_ruby/constants/notification_types.rb | 7 ++++ lib/jam_ruby/models/friend_request.rb | 2 +- lib/jam_ruby/models/notification.rb | 35 +++++++++++--------- 4 files changed, 29 insertions(+), 18 deletions(-) create mode 100644 lib/jam_ruby/constants/notification_types.rb diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index d7a668b29..2bcc3471e 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -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" diff --git a/lib/jam_ruby/constants/notification_types.rb b/lib/jam_ruby/constants/notification_types.rb new file mode 100644 index 000000000..031409dc2 --- /dev/null +++ b/lib/jam_ruby/constants/notification_types.rb @@ -0,0 +1,7 @@ +module NotificationTypes + + FRIEND_UPDATE = "friend_update" + FRIEND_REQUEST = "friend_request" + FRIEND_REQUEST_ACCEPTED = "friend_request_accepted" + +end \ No newline at end of file diff --git a/lib/jam_ruby/models/friend_request.rb b/lib/jam_ruby/models/friend_request.rb index 616319076..348da4162 100644 --- a/lib/jam_ruby/models/friend_request.rb +++ b/lib/jam_ruby/models/friend_request.rb @@ -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 diff --git a/lib/jam_ruby/models/notification.rb b/lib/jam_ruby/models/notification.rb index 32f4871e2..28abcddc4 100644 --- a/lib/jam_ruby/models/notification.rb +++ b/lib/jam_ruby/models/notification.rb @@ -79,24 +79,26 @@ module JamRuby def format_msg(type, user) case type - when "friend_update" + when NotificationTypes::FRIEND_UPDATE return "#{user.name} is now " - when "friend_request" + when NotificationTypes::FRIEND_REQUEST return "#{user.name} has sent you a friend request." - when "friend_request_accepted" + when NotificationTypes::FRIEND_REQUEST_ACCEPTED return "#{user.name} has accepted your friend request." - 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 + 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 @@ -106,7 +108,7 @@ module JamRuby # (1) create notification online_msg = online ? "online." : "offline." - notification_msg = format_msg("friend_update", user) + online_msg + 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 @@ -121,7 +123,7 @@ module JamRuby user = User.find(user_id) # (1) create notification - notification_msg = format_msg("friend_request", user) + notification_msg = format_msg(NotificationTypes::FRIEND_REQUEST, user) msg = @@message_factory.friend_request(id, user_id, user.name, user.photo_url, friend_id, notification_msg) # (2) send notification @@ -129,7 +131,7 @@ module JamRuby # (3) save to database notification = Notification.new - notification.type = "friend_request" + notification.type = NotificationTypes::FRIEND_REQUEST notification.source_user_id = user_id notification.target_user_id = friend_id notification.save @@ -140,14 +142,15 @@ module JamRuby friend = User.find(friend_id) # (1) create notification - msg = @@message_factory.friend_request_accepted(friend_id, friend.name, friend.photo_url, user_id) + 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) # (2) send notification @@mq_router.publish_to_user(user_id, msg) # (3) save to database notification = Notification.new - notification.type = "friend_request_accepted" + notification.type = NotificationTypes::FRIEND_REQUEST_ACCEPTED notification.source_user_id = friend_id notification.target_user_id = user_id notification.save From e53f71277974fda2fda00af9f47743e34077ad90 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Thu, 4 Apr 2013 13:12:06 -0400 Subject: [PATCH 190/274] VRFS-282 changes to support real-time notification updates in sidebar --- lib/jam_ruby/message_factory.rb | 18 ++-- lib/jam_ruby/models/musician_instrument.rb | 1 - lib/jam_ruby/models/notification.rb | 95 +++++++++++++--------- 3 files changed, 68 insertions(+), 46 deletions(-) diff --git a/lib/jam_ruby/message_factory.rb b/lib/jam_ruby/message_factory.rb index ca3b7cc0d..5d8729023 100644 --- a/lib/jam_ruby/message_factory.rb +++ b/lib/jam_ruby/message_factory.rb @@ -106,8 +106,8 @@ return Jampb::ClientMessage.new(:type => ClientMessage::Type::TEST_SESSION_MESSAGE, :route_to => SESSION_TARGET_PREFIX + session_id, :test_session_message => test) end - def session_invitation(receiver_id, invitation_id) - session_invitation = Jampb::SessionInvitation.new(:invitation => invitation_id) + def session_invitation(receiver_id, invitation_id, notification_id) + session_invitation = Jampb::SessionInvitation.new(:invitation => invitation_id, :notification_id => notification_id) return Jampb::ClientMessage.new(:type => ClientMessage::Type::SESSION_INVITATION, :route_to => USER_TARGET_PREFIX + receiver_id, :session_invitation => session_invitation) end @@ -118,14 +118,20 @@ end # create a friend request message - def friend_request(id, user_id, name, photo_url, friend_id, msg) - friend_request = Jampb::FriendRequest.new(:id => id, :user_id => user_id, :name => name, :photo_url => photo_url, :friend_id => friend_id, :msg => msg) + def friend_request(id, user_id, name, photo_url, friend_id, msg, notification_id, created_at) + friend_request = Jampb::FriendRequest.new(:id => 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, msg) - friend_request_accepted = Jampb::FriendRequestAccepted.new(:friend_id => friend_id, :name => name, :photo_url => photo_url, :user_id => user_id, :msg => msg) + 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 diff --git a/lib/jam_ruby/models/musician_instrument.rb b/lib/jam_ruby/models/musician_instrument.rb index d8ef69378..503a4e6f3 100644 --- a/lib/jam_ruby/models/musician_instrument.rb +++ b/lib/jam_ruby/models/musician_instrument.rb @@ -14,6 +14,5 @@ module JamRuby def description @description = self.instrument.description end - end end \ No newline at end of file diff --git a/lib/jam_ruby/models/notification.rb b/lib/jam_ruby/models/notification.rb index 28abcddc4..8ad03142f 100644 --- a/lib/jam_ruby/models/notification.rb +++ b/lib/jam_ruby/models/notification.rb @@ -1,11 +1,26 @@ 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 + self.source_user.photo_url + end + + # used for persisted notifications def formatted_msg target_user, source_user, band, session, recording, invitation, join_request = nil @@ -36,6 +51,8 @@ module JamRuby 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 @@ -77,8 +94,8 @@ module JamRuby return ids end - def format_msg(type, user) - case type + def format_msg(description, user) + case description when NotificationTypes::FRIEND_UPDATE return "#{user.name} is now " @@ -122,53 +139,53 @@ module JamRuby def send_friend_request(id, user_id, friend_id) user = User.find(user_id) - # (1) create notification - notification_msg = format_msg(NotificationTypes::FRIEND_REQUEST, user) - msg = @@message_factory.friend_request(id, user_id, user.name, user.photo_url, friend_id, notification_msg) - - # (2) send notification - @@mq_router.publish_to_user(friend_id, msg) - - # (3) save to database + # (1) save to database notification = Notification.new - notification.type = NotificationTypes::FRIEND_REQUEST + notification.description = NotificationTypes::FRIEND_REQUEST notification.source_user_id = user_id notification.target_user_id = friend_id notification.save + + # (2) create notification + notification_msg = format_msg(NotificationTypes::FRIEND_REQUEST, user) + msg = @@message_factory.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) end ############### FRIEND REQUEST ACCEPTED ############### def send_friend_request_accepted(user_id, friend_id) friend = User.find(friend_id) - # (1) 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) - - # (2) send notification - @@mq_router.publish_to_user(user_id, msg) - - # (3) save to database + # (1) save to database notification = Notification.new - notification.type = NotificationTypes::FRIEND_REQUEST_ACCEPTED + notification.description = NotificationTypes::FRIEND_REQUEST_ACCEPTED notification.source_user_id = friend_id notification.target_user_id = user_id notification.save + + # (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) end ################## SESSION INVITATION ################## def send_session_invitation(receiver_id, invitation_id) - # (1) create notification - msg = @@message_factory.session_invitation(receiver_id, invitation_id) - - # (2) send notification - @@mq_router.publish_to_user(receiver_id, msg) + # (1) save to database + notification = Notification.new + notification.description = "session_invitation" + notification.target_user_id = receiver_id - # (3) save to database - # notification = Notification.new - # notification.type = "session_invitation" - # notification.target_user_id = receiver_id + # (2) create notification + msg = @@message_factory.session_invitation(receiver_id, invitation_id, notification.id) + + # (3) send notification + @@mq_router.publish_to_user(receiver_id, msg) end def send_session_left(music_session, connection, user) @@ -182,27 +199,27 @@ 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 end end From c3824cac258b89b8534fd8f0669758722fe6394b Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Thu, 4 Apr 2013 23:57:16 -0400 Subject: [PATCH 191/274] VRFS-282 changes to support real-time notification updates in sidebar --- lib/jam_ruby/message_factory.rb | 6 +++--- lib/jam_ruby/models/notification.rb | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/jam_ruby/message_factory.rb b/lib/jam_ruby/message_factory.rb index 5d8729023..efcffd1aa 100644 --- a/lib/jam_ruby/message_factory.rb +++ b/lib/jam_ruby/message_factory.rb @@ -106,8 +106,8 @@ return Jampb::ClientMessage.new(:type => ClientMessage::Type::TEST_SESSION_MESSAGE, :route_to => SESSION_TARGET_PREFIX + session_id, :test_session_message => test) end - def session_invitation(receiver_id, invitation_id, notification_id) - session_invitation = Jampb::SessionInvitation.new(:invitation => invitation_id, :notification_id => notification_id) + def session_invitation(receiver_id, invitation_id) + session_invitation = Jampb::SessionInvitation.new(:invitation => invitation_id) return Jampb::ClientMessage.new(:type => ClientMessage::Type::SESSION_INVITATION, :route_to => USER_TARGET_PREFIX + receiver_id, :session_invitation => session_invitation) end @@ -131,7 +131,7 @@ 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 diff --git a/lib/jam_ruby/models/notification.rb b/lib/jam_ruby/models/notification.rb index 8ad03142f..67b69cb2e 100644 --- a/lib/jam_ruby/models/notification.rb +++ b/lib/jam_ruby/models/notification.rb @@ -180,9 +180,10 @@ module JamRuby notification = Notification.new notification.description = "session_invitation" notification.target_user_id = receiver_id + notification.save # (2) create notification - msg = @@message_factory.session_invitation(receiver_id, invitation_id, notification.id) + msg = @@message_factory.session_invitation(receiver_id, invitation_id) # (3) send notification @@mq_router.publish_to_user(receiver_id, msg) From 23a0c16b7fff6df71b1d1a3e2c71c28b063847c6 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Sat, 6 Apr 2013 21:43:00 -0400 Subject: [PATCH 192/274] add null check for source_user before returning photo_url --- lib/jam_ruby/models/notification.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/jam_ruby/models/notification.rb b/lib/jam_ruby/models/notification.rb index 67b69cb2e..561ccf058 100644 --- a/lib/jam_ruby/models/notification.rb +++ b/lib/jam_ruby/models/notification.rb @@ -17,7 +17,9 @@ module JamRuby end def photo_url - self.source_user.photo_url + unless self.source_user.nil? + self.source_user.photo_url + end end # used for persisted notifications From 8ca4848d28f3df9e6af0f280e62708b005d74783 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Wed, 10 Apr 2013 01:16:04 -0500 Subject: [PATCH 193/274] * updating ArtifactUpdate to reflect new mandatory fields --- Gemfile | 4 +++- lib/jam_ruby/models/artifact_update.rb | 17 +++++++++++++++-- spec/jam_ruby/models/artifact_update_spec.rb | 6 +++--- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/Gemfile b/Gemfile index 75a43a941..7f01fde4b 100644 --- a/Gemfile +++ b/Gemfile @@ -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" diff --git a/lib/jam_ruby/models/artifact_update.rb b/lib/jam_ruby/models/artifact_update.rb index 9367b25c3..d224f6120 100644 --- a/lib/jam_ruby/models/artifact_update.rb +++ b/lib/jam_ruby/models/artifact_update.rb @@ -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 diff --git a/spec/jam_ruby/models/artifact_update_spec.rb b/spec/jam_ruby/models/artifact_update_spec.rb index c4ed36193..24075e310 100644 --- a/spec/jam_ruby/models/artifact_update_spec.rb +++ b/spec/jam_ruby/models/artifact_update_spec.rb @@ -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 From 3abde183b745e45b14decf6c533ccf409c8dabc7 Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Fri, 12 Apr 2013 15:36:05 -0700 Subject: [PATCH 194/274] added model stuff for mix --- lib/jam_ruby.rb | 1 + lib/jam_ruby/models/mix.rb | 51 +++++++++++++++++++++++++ spec/jam_ruby/models/mix_spec.rb | 64 ++++++++++++++++++++++++++++++++ 3 files changed, 116 insertions(+) create mode 100644 lib/jam_ruby/models/mix.rb create mode 100644 spec/jam_ruby/models/mix_spec.rb diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index 2bcc3471e..6f9063e1b 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -58,6 +58,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 diff --git a/lib/jam_ruby/models/mix.rb b/lib/jam_ruby/models/mix.rb new file mode 100644 index 000000000..a05e4e23d --- /dev/null +++ b/lib/jam_ruby/models/mix.rb @@ -0,0 +1,51 @@ +# 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 + + 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| + 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) + self.completed_at = Time.now + self.url = url + save + end + + end +end diff --git a/spec/jam_ruby/models/mix_spec.rb b/spec/jam_ruby/models/mix_spec.rb new file mode 100644 index 000000000..5e7753762 --- /dev/null +++ b/spec/jam_ruby/models/mix_spec.rb @@ -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 + + From 7ba0317aebb7242d605b79c321d46d5f38619942 Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Fri, 12 Apr 2013 17:16:40 -0700 Subject: [PATCH 195/274] return mixes with recordings --- lib/jam_ruby/models/mix.rb | 6 ++++++ lib/jam_ruby/models/recording.rb | 9 +++++++++ 2 files changed, 15 insertions(+) diff --git a/lib/jam_ruby/models/mix.rb b/lib/jam_ruby/models/mix.rb index a05e4e23d..9b9a4adac 100644 --- a/lib/jam_ruby/models/mix.rb +++ b/lib/jam_ruby/models/mix.rb @@ -5,6 +5,9 @@ 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 @@ -25,6 +28,7 @@ module JamRuby 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 @@ -42,6 +46,8 @@ module JamRuby def finish(url) + raise if url.nil? + self.completed_at = Time.now self.url = url save diff --git a/lib/jam_ruby/models/recording.rb b/lib/jam_ruby/models/recording.rb index 3ea932606..07b88302b 100644 --- a/lib/jam_ruby/models/recording.rb +++ b/lib/jam_ruby/models/recording.rb @@ -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 From 3700738b8470a891fb03a5dcf2b11d20503abe4a Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Sat, 13 Apr 2013 22:58:31 -0400 Subject: [PATCH 196/274] VRFS-284 support for Accept Friend Request button in sidebar --- lib/jam_ruby/constants/notification_types.rb | 7 ++++--- lib/jam_ruby/message_factory.rb | 4 ++-- lib/jam_ruby/models/notification.rb | 7 ++++--- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/jam_ruby/constants/notification_types.rb b/lib/jam_ruby/constants/notification_types.rb index 031409dc2..07a68a62c 100644 --- a/lib/jam_ruby/constants/notification_types.rb +++ b/lib/jam_ruby/constants/notification_types.rb @@ -1,7 +1,8 @@ module NotificationTypes - FRIEND_UPDATE = "friend_update" - FRIEND_REQUEST = "friend_request" - FRIEND_REQUEST_ACCEPTED = "friend_request_accepted" + FRIEND_UPDATE = "FRIEND_UPDATE" + FRIEND_REQUEST = "FRIEND_REQUEST" + FRIEND_REQUEST_ACCEPTED = "FRIEND_REQUEST_ACCEPTED" + SESSION_INVITATION = "SESSION_INVITATION" end \ No newline at end of file diff --git a/lib/jam_ruby/message_factory.rb b/lib/jam_ruby/message_factory.rb index efcffd1aa..6396895de 100644 --- a/lib/jam_ruby/message_factory.rb +++ b/lib/jam_ruby/message_factory.rb @@ -118,8 +118,8 @@ end # create a friend request message - def friend_request(id, user_id, name, photo_url, friend_id, msg, notification_id, created_at) - friend_request = Jampb::FriendRequest.new(:id => 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) diff --git a/lib/jam_ruby/models/notification.rb b/lib/jam_ruby/models/notification.rb index 561ccf058..7e7dc44a8 100644 --- a/lib/jam_ruby/models/notification.rb +++ b/lib/jam_ruby/models/notification.rb @@ -138,7 +138,7 @@ module JamRuby end ################### FRIEND REQUEST ################### - def send_friend_request(id, user_id, friend_id) + def send_friend_request(friend_request_id, user_id, friend_id) user = User.find(user_id) # (1) save to database @@ -146,11 +146,12 @@ module JamRuby 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) create notification notification_msg = format_msg(NotificationTypes::FRIEND_REQUEST, user) - msg = @@message_factory.friend_request(id, user_id, user.name, user.photo_url, friend_id, notification_msg, notification.id, notification.created_at.to_s) + 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) @@ -180,7 +181,7 @@ module JamRuby # (1) save to database notification = Notification.new - notification.description = "session_invitation" + notification.description = NotificationTypes::SESSION_INVITATION notification.target_user_id = receiver_id notification.save From 7d17742f466af007579d125212ad5d9f05c92fb5 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Mon, 15 Apr 2013 21:12:16 -0500 Subject: [PATCH 197/274] * perfdata --- lib/jam_ruby/models/music_session_perf_data.rb | 14 +++++++++++++- spec/factories.rb | 3 +++ .../models/music_session_perf_data_spec.rb | 15 +++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 spec/jam_ruby/models/music_session_perf_data_spec.rb diff --git a/lib/jam_ruby/models/music_session_perf_data.rb b/lib/jam_ruby/models/music_session_perf_data.rb index 7b39a8500..9e6e7dfce 100644 --- a/lib/jam_ruby/models/music_session_perf_data.rb +++ b/lib/jam_ruby/models/music_session_perf_data.rb @@ -1,12 +1,24 @@ +require 'securerandom' + module JamRuby class MusicSessionPerfData < ActiveRecord::Base self.primary_key = 'id' + attr_accessible :client_id, :uri - mount_uploader :uri, PerfDatatUploader + belongs_to :music_session, :class_name => "JamRuby::MusicSessionHistory" + #mount_uploader :uri, PerfDataUploader + + validate :music_session, :presence => true validate :client_id, :presence => true validate :uri, :presence => true + + before_validation(:on => :create) do + self.created_at ||= Time.now + self.id = SecureRandom.uuid + self.uri = "#{SampleApp::Application.config.perf_data}/#{self.id}/#{self.client_id}-#{self.created_at}" + end end end diff --git a/spec/factories.rb b/spec/factories.rb index 009d43314..58b4c25ba 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -92,4 +92,7 @@ FactoryGirl.define do autofriend false end + factory :music_session_perf_data, :class => JamRuby::MusicSessionPerfData do + association :music_session => + end end diff --git a/spec/jam_ruby/models/music_session_perf_data_spec.rb b/spec/jam_ruby/models/music_session_perf_data_spec.rb new file mode 100644 index 000000000..c2e84c774 --- /dev/null +++ b/spec/jam_ruby/models/music_session_perf_data_spec.rb @@ -0,0 +1,15 @@ +require 'spec_helper' + +describe MusicSessionPerfData do + + before do + music_session = FactoryGirl.create(:music_session) + connection = FactoryGirl.create(:connection, :music_session => music_session) + end + + it "create" do + M + end + + +end \ No newline at end of file From fe222ff13a1ba8bd101e00b72a3dce0d3123b25c Mon Sep 17 00:00:00 2001 From: Seth Call Date: Tue, 16 Apr 2013 19:36:01 -0500 Subject: [PATCH 198/274] * perrfdata model --- lib/jam_ruby/models/music_session_history.rb | 2 +- lib/jam_ruby/models/music_session_perf_data.rb | 2 +- spec/factories.rb | 6 ++++-- spec/jam_ruby/models/music_session_perf_data_spec.rb | 6 +++--- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/jam_ruby/models/music_session_history.rb b/lib/jam_ruby/models/music_session_history.rb index c70d58692..c09a76be2 100644 --- a/lib/jam_ruby/models/music_session_history.rb +++ b/lib/jam_ruby/models/music_session_history.rb @@ -46,7 +46,7 @@ module JamRuby session_history.user_id = music_session.creator.id session_history.band_id = music_session.band.id unless music_session.band.nil? session_history.genres = music_session.genres.map { |g| g.id }.join '|' - session_history.save + session_history.save! end end end \ No newline at end of file diff --git a/lib/jam_ruby/models/music_session_perf_data.rb b/lib/jam_ruby/models/music_session_perf_data.rb index 9e6e7dfce..5d0ae91cc 100644 --- a/lib/jam_ruby/models/music_session_perf_data.rb +++ b/lib/jam_ruby/models/music_session_perf_data.rb @@ -18,7 +18,7 @@ module JamRuby before_validation(:on => :create) do self.created_at ||= Time.now self.id = SecureRandom.uuid - self.uri = "#{SampleApp::Application.config.perf_data}/#{self.id}/#{self.client_id}-#{self.created_at}" + self.uri = "perf_data/#{self.id}/#{self.client_id}-#{self.created_at}" end end end diff --git a/spec/factories.rb b/spec/factories.rb index 58b4c25ba..f91a3e9f0 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -30,6 +30,8 @@ FactoryGirl.define do approval_required false musician_access true legal_terms true + + after(:create) { |session| MusicSessionHistory.save(session) } end factory :connection, :class => JamRuby::Connection do @@ -78,7 +80,7 @@ FactoryGirl.define do end factory :recording, :class => JamRuby::Recording do - + end factory :musician_instrument, :class => JamRuby::MusicianInstrument do @@ -93,6 +95,6 @@ FactoryGirl.define do end factory :music_session_perf_data, :class => JamRuby::MusicSessionPerfData do - association :music_session => + association :music_session => :music_session end end diff --git a/spec/jam_ruby/models/music_session_perf_data_spec.rb b/spec/jam_ruby/models/music_session_perf_data_spec.rb index c2e84c774..2ec72ba71 100644 --- a/spec/jam_ruby/models/music_session_perf_data_spec.rb +++ b/spec/jam_ruby/models/music_session_perf_data_spec.rb @@ -3,12 +3,12 @@ require 'spec_helper' describe MusicSessionPerfData do before do - music_session = FactoryGirl.create(:music_session) - connection = FactoryGirl.create(:connection, :music_session => music_session) + #music_session = FactoryGirl.create(:music_session) + #connection = FactoryGirl.create(:connection, :music_session => music_session) end it "create" do - M + end From 8d4fd2aeafc9e96fa730e6616c2f9f4b65386d03 Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Wed, 24 Apr 2013 23:50:52 -0700 Subject: [PATCH 199/274] more recordings work --- lib/jam_ruby.rb | 2 + lib/jam_ruby/lib/s3_manager.rb | 61 +++++++ lib/jam_ruby/models/mix.rb | 32 ++-- lib/jam_ruby/models/recorded_track.rb | 57 +++--- lib/jam_ruby/models/recording.rb | 187 ++++++-------------- lib/jam_ruby/models/search.rb | 7 +- lib/jam_ruby/models/user.rb | 3 +- spec/jam_ruby/models/mix_spec.rb | 5 +- spec/jam_ruby/models/recorded_track_spec.rb | 13 +- spec/jam_ruby/models/recording_spec.rb | 186 +++++++++++-------- 10 files changed, 288 insertions(+), 265 deletions(-) create mode 100644 lib/jam_ruby/lib/s3_manager.rb diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index e2c652195..b83e5869b 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -26,6 +26,7 @@ require "jam_ruby/app/mailers/user_mailer" require "jam_ruby/app/mailers/invited_user_mailer" require "jam_ruby/app/uploaders/artifact_uploader" require "jam_ruby/app/uploaders/perf_data_uploader" +require "jam_ruby/lib/s3_manager" require "jam_ruby/message_factory" require "jam_ruby/models/genre" require "jam_ruby/models/user" @@ -61,6 +62,7 @@ require "jam_ruby/models/search" require "jam_ruby/models/recording" require "jam_ruby/models/recorded_track" require "jam_ruby/models/mix" +require "jam_ruby/models/claimed_recording" include Jampb diff --git a/lib/jam_ruby/lib/s3_manager.rb b/lib/jam_ruby/lib/s3_manager.rb new file mode 100644 index 000000000..2ce44e8fc --- /dev/null +++ b/lib/jam_ruby/lib/s3_manager.rb @@ -0,0 +1,61 @@ +module JamRuby + class S3Manager + + SECRET = "krQP3fKpjAtWkApBEJwJJrCZ" + + def self.s3_url(filename) + "s3://#{aws_bucket}/#{filename}" + end + + def self.url(filename) + "https://s3.amazonaws.com/#{aws_bucket}/#{filename}" + end + + def self.upload_sign(filename, content_md5, upload_id) + hdt = http_date_time + str_to_sign = "PUT\n#{content_md5}\n#{content_type}\n#{hdt}\n/#{aws_bucket}/#{filename}" + signature = Base64.encode64(HMAC::SHA1.digest(aws_secret_key, str_to_sign)).chomp + { :filename => filename, :signature => signature, :datetime => hdt, :upload_id => upload_id } + end + + def self.hashed_filename(type, id) + Digest::SHA1.hexdigest "#{SECRET}_#{type}_#{id}" + end + + def self.multipart_upload_start(upload_filename) + s3_bucket.objects[upload_filename].multipart_upload.id + end + + def self.multipart_upload_complete(upload_id) + s3_bucket.objects[upload_filename].multipart_uploads[upload_id].upload_complete(:remote_parts) + end + + def self.delete(filename) + s3_bucket.objects[filename].delete + end + + private + + def self.s3_bucket + @s3 ||= AWS::S3.new + @s3.buckets[aws_bucket] + end + + def self.aws_bucket + "jamkazam-dev" + end + + def self.aws_secret_key + "XLq2mpJHNyA0bN7GBSdYyF/pWjfzGkDx92b1C+Wv" + end + + def self.content_type + "application/octet-stream" + end + + def self.http_date_time + Time.now.strftime("%a, %d %b %Y %H:%M:%S %z") + end + + end +end diff --git a/lib/jam_ruby/models/mix.rb b/lib/jam_ruby/models/mix.rb index 9b9a4adac..345074249 100644 --- a/lib/jam_ruby/models/mix.rb +++ b/lib/jam_ruby/models/mix.rb @@ -1,16 +1,15 @@ -# 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 + before_destroy :delete_s3_files + self.primary_key = 'id' belongs_to :recording, :class_name => "JamRuby::Recording", :inverse_of => :mix - def self.schedule(recording_id, user_id, description, spec) + def self.schedule(recording_id, user_id, description, manifest) # 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 + # 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 @@ -19,7 +18,7 @@ module JamRuby mix.recording_id = recording_id mix.owner_id = user_id mix.description = description - mix.spec = spec + mix.manifest = manifest mix.save mix @@ -44,14 +43,25 @@ module JamRuby mix end - - def finish(url) - raise if url.nil? - + def finish() self.completed_at = Time.now - self.url = url save end + def s3_url + S3Manager.s3_url(hashed_filename) + end + + private + + def delete_s3_files + S3Manager.delete(hashed_filename) + end + + def hashed_filename + S3Manager.hashed_filename('mix', id) + end + + end end diff --git a/lib/jam_ruby/models/recorded_track.rb b/lib/jam_ruby/models/recorded_track.rb index 9ef1fa501..053cf68ae 100644 --- a/lib/jam_ruby/models/recorded_track.rb +++ b/lib/jam_ruby/models/recorded_track.rb @@ -13,6 +13,8 @@ module JamRuby validates :sound, :inclusion => {:in => SOUND} + before_destroy :delete_s3_files + # Copy an ephemeral track to create a saved one. Some fields are ok with defaults def self.create_from_track(track, recording) recorded_track = self.new @@ -25,17 +27,14 @@ module JamRuby end def upload_start - self.upload_id = s3_bucket.objects[filename].multipart_upload.id + self.upload_id = S3Manager.multipart_upload_start(hashed_filename) save end - def upload_sign(filename, content_md5) - str_to_sign = "PUT\n#{content_md5}\n#{content_type}\n#{http_date_time}\n/#{aws_bucket}/#{filename}" - signature = Base64.encode64(HMAC::SHA1.digest(aws_secret_key, str_to_sign)).chomp - { :signature => signature, :datetime => http_date_time, :upload_id => upload_id } + def upload_sign(content_md5) + S3Manager.upload_sign(hashed_filename, content_md5, upload_id) end - def upload_part_complete(part) raise JamRuby::JamArgumentError unless part == next_part_to_upload self.next_part_to_upload = part + 1 @@ -43,45 +42,33 @@ module JamRuby end def upload_complete - s3_bucket.objects[filename].multipart_uploads[self.upload_id].upload_complete(:remote_parts) + S3Manager.multipart_upload_complete(upload_id) self.fully_uploaded = true save end + def url + S3Manager.url(hashed_filename) + end - # Format: "recording_#{saved_track_id}" + # Format: "recording_#{recorded_track_id}" # File extension is irrelevant actually. - def self.filename_to_recorded_track_id(filename) + def self.find_by_upload_filename(filename) matches = /^recording_([\w-]+)$/.match(filename) return nil unless matches && matches.length > 1 - matches[1] - end - - private - - def s3_bucket - @s3 ||= AWS::S3.new - @s3.buckets[aws_bucket] - end - - def filename - "recording_#{self.id}" - end - - def aws_bucket - "jamkazam-dev" - end - - def aws_secret_key - "XLq2mpJHNyA0bN7GBSdYyF/pWjfzGkDx92b1C+Wv" - end - - def content_type - "application/octet-stream" + RecordedTrack.find(matches[1]) end - def http_date_time - Time.now.strftime("%a, %d %b %Y %H:%M:%S %z") + private + + def delete_s3_files + S3Manager.delete(hashed_filename) end + + def hashed_filename + S3Manager.hashed_filename('recorded_track', id) + end + + end end diff --git a/lib/jam_ruby/models/recording.rb b/lib/jam_ruby/models/recording.rb index 07b88302b..2b65cb2ae 100644 --- a/lib/jam_ruby/models/recording.rb +++ b/lib/jam_ruby/models/recording.rb @@ -1,14 +1,20 @@ +# A bunch to do here: +# 1. Make it so users "own" recordings +# 2. Add a route for a user to specify if they want to save or discard a recording. If save, then add them to recordings_users (not sure anything else is even doing that now) +# 3. Need a way to get a list of all the recordings you "should" have as a user. That will be all the recorded tracks plus mixes for all recordings you "own" +# 4. I think the notion of the recording's "owner" being who started it can remain. + module JamRuby class Recording < ActiveRecord::Base - + self.primary_key = 'id' - has_and_belongs_to_many :users, :class_name => "JamRuby::User" + has_many :claimed_recordings, :class_name => "JamRuby::ClaimedRecording", :inverse_of => :recording + has_many :users, :through => :claimed_recordings, :class_name => "JamRuby::User" 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 # genres @@ -18,10 +24,6 @@ module JamRuby has_many :favorites, :class_name => "JamRuby::UserFavorite", :foreign_key => "recording_id" has_many :inverse_favorites, :through => :favorites, :source => :recording, :class_name => "JamRuby::Recording" - validates :name, length: { maximum: 100 } - validates :description, length: { maximum: 200 } - validates :genres, length: { maximum: 3 } - def favorite_count return self.favorites.size end @@ -77,11 +79,11 @@ module JamRuby recording = Recording.new recording.music_session = music_session recording.owner = owner - recording.is_public = false - recording.is_downloadable = false music_session.connections.each do |connection| - recording.users << connection.user + # Note that we do NOT connect the recording to any users at this point. + # That ONLY happens if a user clicks 'save' + # recording.users << connection.user connection.tracks.each do |track| RecordedTrack.create_from_track(track, recording) end @@ -113,8 +115,6 @@ module JamRuby end # Stop recording a session - # FIXME: What to do about recordings which are started and then stopped but never get any metadata (not even a name)? - # I'm guessing they should be deleted eventually. def stop # Use a transaction and lock to avoid races. ActiveRecord::Base.transaction do @@ -128,37 +128,43 @@ module JamRuby music_session.recording = nil music_session.save end - end - - # Update the metadata for the recording. - def update_fields(params) - unless self.music_session.nil? - raise PermissionError, "recording data cannot be changed while it is being recorded" - end - - if params[:name].nil? - raise PermissionError, "recording must have a name" if self.name.nil? - end - - self.name = params[:name] unless params[:name].nil? - self.description = params[:description] unless params[:description].nil? - genres = params[:genres] - unless genres.nil? - validate_genres(genres) - self.genres = [] - genres.each do |genre_id| - g = Genre.find(genre_id) - self.genres << g - end - end - - self.is_public = params[:is_public] unless params[:is_public].nil? - self.is_downloadable = params[:is_downloadable] unless params[:is_downloadable].nil? - + self.duration = Time.now - created_at save end + + # Called when a user wants to "claim" a recording. To do this, the user must have been one of the tracks in the recording. + def claim(user, name, genre, is_public, is_downloadable) + if self.users.include?(user) + raise PermissionError, "user already claimed this recording" + end + + unless self.recorded_tracks.find { |recorded_track| recorded_track.user == user } + raise PermissionError, "user was not in this session" + end + + unless self.music_session.nil? + raise PermissionError, "recording cannot be claimed while it is being recorded" + end + + if name.nil? || genre.nil? || is_public.nil? || is_downloadable.nil? + raise PermissionError, "recording must have name, genre and flags" + end + + claimed_recording = ClaimedRecording.new + claimed_recording.user = user + claimed_recording.recording = self + claimed_recording.name = name + claimed_recording.genre = genre + claimed_recording.is_public = is_public + claimed_recording.is_downloadable = is_downloadable + self.claimed_recordings << claimed_recording + save + + claimed_recording + end + # Find out if all the tracks for this recording have been uploaded def uploaded? self.recorded_tracks.each do |recorded_track| @@ -167,6 +173,13 @@ module JamRuby return true end + # Discards this recording and schedules deletion of all files associated with it. + def discard + self.destroy + end + +=begin +# This is no longer remotely right. def self.search(query, options = { :limit => 10 }) # only issue search if at least 2 characters are specified @@ -183,92 +196,9 @@ module JamRuby return Recording.where("description_tsv @@ to_tsquery('jamenglish', ?)", query).limit(options[:limit]) end - - - - - - def self.save(id, is_public, description, genres, updater_id, owner_id, is_band) - return nil - # Spec: https://jamkazam.atlassian.net/wiki/display/PS/Product+Specification+-+Studio - # This is seriously wrong. A recording needs to be created from inside a session. I'm not sure who owns - # the recording, but I do know that it's affiliated with the session and then should be able to get the band id - # and probably all this other stuff (genre, etc) from that. I need to read the recording spec in more detail - # to figure that out. I'll read it on bart. - # - # The studio spec is the key here. A couple notes: - # * Multiple musicians are all associated with a recording -- everyone who is in the music session when the recording starts - # * Associating a band with the recording is optional - # * The recording needs to be associated with a music_session for sure. Probably also a set of tracks (NOT musicians). Then, on - # recording completion is when you have the track -> saved_track (rename this to recorded_track) transition. - # * The metadata in general is filled in *after* the recording ends. This is important because it means the save method below - # is sort of jacked. -=begin - creator = User.find(updater_id) - - if is_band - band = Band.find(owner_id) - validate_user_is_band_member(creator, band) - else - user = User.find(owner_id) - validate_user_is_creator(user, creator) - validate_user_is_musician(user) - end - - if id.nil? - validate_genres(genres, false) - recording = Recording.new() - recording.creator_id = updater_id - else - validate_genres(genres, true) - recording = Recording.find(id) - end - - recording.updater_id = updater_id - - # public flag - unless is_public.nil? - recording.public = is_public - end - - # description - unless description.nil? - recording.description = description - end - - # genres - unless genres.nil? - ActiveRecord::Base.transaction do - # delete all genres for this recording first - unless recording.id.nil? || recording.id.length == 0 - recording.genres.delete_all - end - - # loop through each genre in the array and save to the db - genres.each do |genre_id| - g = Genre.find(genre_id) - recording.genres << g - end - end - end - - recording.updated_at = Time.now.getutc - - # TODO: wrap in transaction with statements below - recording.save - - if id.nil? - if is_band - recording.band_recordings << BandRecording.create(band_id: owner_id, recording_id: recording.id) - else - recording.musician_recordings << MusicianRecording.create(user_id: owner_id, recording_id: recording.id) - end - end - - return recording =end - end + private def self.validate_user_is_band_member(user, band) unless band.users.exists? user @@ -297,14 +227,5 @@ module JamRuby raise JamRuby::JamArgumentError, ValidationMessages::GENRE_LIMIT_EXCEEDED end end - -=begin - def self.delete(id, owner_id, is_band) - if is_band? - JamRuby::Recording.delete_all "(user_id = '#{user_id}' AND follower_id = '#{follower_id}')" - else - end - end -=end end - end +end diff --git a/lib/jam_ruby/models/search.rb b/lib/jam_ruby/models/search.rb index 1c62cb204..3cfaad1a7 100644 --- a/lib/jam_ruby/models/search.rb +++ b/lib/jam_ruby/models/search.rb @@ -10,8 +10,9 @@ module JamRuby users = User.search(query, :limit => LIMIT) bands = Band.search(query, :limit => LIMIT) - recordings = Recording.search(query, :limit => LIMIT) - return Search.new(users + bands + recordings) + # NOTE: I removed recordings from search here. This is because we switched + # to "claimed_recordings" so it's not clear what should be searched. + return Search.new(users + bands) end # performs a friend search scoped to a specific user @@ -78,4 +79,4 @@ module JamRuby end end -end \ No newline at end of file +end diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index ba4b3e8a9..244b616ae 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -31,7 +31,8 @@ module JamRuby # recordings has_many :owned_recordings, :class_name => "JamRuby::Recording" - has_and_belongs_to_many :recordings, :class_name => "JamRuby::Recording" + has_many :recordings, :through => :claimed_recordings, :class_name => "JamRuby::Recording" + has_many :claimed_recordings, :class_name => "JamRuby::ClaimedRecording", :inverse_of => :user # user likers (a musician has likers and may have likes too; fans do not have likers) has_many :likers, :class_name => "JamRuby::UserLiker", :foreign_key => "user_id", :inverse_of => :user diff --git a/spec/jam_ruby/models/mix_spec.rb b/spec/jam_ruby/models/mix_spec.rb index 5e7753762..5383a6adc 100644 --- a/spec/jam_ruby/models/mix_spec.rb +++ b/spec/jam_ruby/models/mix_spec.rb @@ -18,7 +18,7 @@ describe Mix do @mix.recording_id.should == @recording.id @mix.owner_id.should == @user.id @mix.description.should == "description" - @mix.spec.should == "{}" + @mix.manifest.should == "{}" @mix.url.should be_nil @mix.mix_server.should be_nil @mix.started_at.should be_nil @@ -44,10 +44,9 @@ describe Mix do end it "should record when a mix has finished" do - Mix.find(@mix.id).finish("http://blah") + Mix.find(@mix.id).finish @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 diff --git a/spec/jam_ruby/models/recorded_track_spec.rb b/spec/jam_ruby/models/recorded_track_spec.rb index b6bd0a450..dcd374aaf 100644 --- a/spec/jam_ruby/models/recorded_track_spec.rb +++ b/spec/jam_ruby/models/recorded_track_spec.rb @@ -7,7 +7,7 @@ describe RecordedTrack do @connection = FactoryGirl.create(:connection, :user => @user) @instrument = FactoryGirl.create(:instrument, :description => 'a great instrument') @track = FactoryGirl.create(:track, :connection => @connection, :instrument => @instrument) - @recording = FactoryGirl.create(:recording, :description => 'description', :owner => @user) + @recording = FactoryGirl.create(:recording, :owner => @user) end it "should copy from a regular track properly" do @@ -36,6 +36,17 @@ describe RecordedTrack do @recorded_track.next_part_to_upload.should == 2 end + it "properly finds a recorded track given its upload filename" do + @recorded_track = RecordedTrack.create_from_track(@track, @recording) + RecordedTrack.find_by_upload_filename("recording_#{@recorded_track.id}").should == @recorded_track + end + + it "gets a url for the track" do + @recorded_track = RecordedTrack.create_from_track(@track, @recording) + @recorded_track.url.should == S3Manager.url(S3Manager.hashed_filename("recorded_track", @recorded_track.id)) + end + + end diff --git a/spec/jam_ruby/models/recording_spec.rb b/spec/jam_ruby/models/recording_spec.rb index 62a529f50..8e035a986 100644 --- a/spec/jam_ruby/models/recording_spec.rb +++ b/spec/jam_ruby/models/recording_spec.rb @@ -23,9 +23,7 @@ describe Recording do @music_session.recording.should == @recording @recording.owner_id.should == @user.id @recording.genres.should == @music_session.genres - @recording.users.length.should == 1 - @recording.users.first.should == @user - @recording.description.should be_nil + @recording.users.length.should == 0 @recorded_tracks = RecordedTrack.where(:recording_id => @recording.id) @recorded_tracks.length.should == 1 @@ -61,7 +59,7 @@ describe Recording do @music_session.recording.should == @recording2 end - it "should attach the recording to all users in a the music session when recording started" do + it "should NOT attach the recording to all users in a the music session when recording started" do @user2 = FactoryGirl.create(:user) @connection2 = FactoryGirl.create(:connection, :user => @user2) @instrument2 = FactoryGirl.create(:instrument, :description => 'a great instrument') @@ -70,81 +68,11 @@ describe Recording do @music_session.connections << @connection2 @recording = Recording.start(@music_session.id, @user) - @user.recordings.length.should == 1 - @user.recordings.first.should == @recording + @user.recordings.length.should == 0 + #@user.recordings.first.should == @recording - @user2.recordings.length.should == 1 - @user2.recordings.first.should == @recording - end - - it "should update fields in a recording properly after it finishes" do - @recording = Recording.start(@music_session.id, @user) - @recording.stop - @recording.reload - - @genre1 = FactoryGirl.create(:genre) - @genre2 = FactoryGirl.create(:genre) - @genre3 = FactoryGirl.create(:genre) - - @recording.update_fields :name => "name1", :description => "description", :genres => [@genre1], :is_downloadable => true, :is_public => true - @recording.name.should == "name1" - @recording.description.should == "description" - @recording.genres.length.should == 1 - @recording.is_public.should == true - @recording.is_downloadable.should == true - - @recording.update_fields :name => "name2", :description => "description2", :genres => [@genre3], :is_downloadable => false, :is_public => false - @recording.name.should == "name2" - @recording.description.should == "description2" - @recording.genres.length.should == 1 - @recording.genres.first.should == @genre3 - @recording.is_public.should == false - @recording.is_downloadable.should == false - - # Use a fake param here to simulate getting no params - @recording.update_fields :fakeparam => "fake" - @recording.name.should == "name2" - @recording.description.should == "description2" - @recording.genres.length.should == 1 - @recording.genres.first.should == @genre3 - @recording.is_public.should == false - @recording.is_downloadable.should == false - - end - - it "should error if you update fields in a recording while its ongoing" do - @recording = Recording.start(@music_session.id, @user) - expect { @recording.update_fields :name => "name", :description => "description" }.to raise_error - end - - it "should error if you try to assign more than 3 genres or fewer than 1" do - @recording = Recording.start(@music_session.id, @user) - @recording.stop - @recording.reload - - @genre1 = FactoryGirl.create(:genre) - @genre2 = FactoryGirl.create(:genre) - @genre3 = FactoryGirl.create(:genre) - @genre4 = FactoryGirl.create(:genre) - - expect { @recording.update_fields :name => "name", :genres => [@genre1, @genre2, @genre3, @genre4] }.to raise_error - expect { @recording.update_fields :genres => [] }.to raise_error - end - - it "should error if you set fields on a recording with no name" do - @recording = Recording.start(@music_session.id, @user) - @recording.stop - @recording.reload - - @recording.name.should be_nil - expect { @recording.update_fields :description => "description" }.to raise_error - end - - it "should delete a recording as expected" do - @recording = Recording.start(@music_session.id, @user) - @recording.stop - Recording.delete(@recording.id) - expect { Recording.find(@recording.id) }.to raise_error + @user2.recordings.length.should == 0 + #@user2.recordings.first.should == @recording end it "should report correctly whether its tracks have been uploaded" do @@ -167,6 +95,108 @@ describe Recording do expect { RecordedTracks.find(@recorded_track.id) }.to raise_error end + it "should allow a user to claim a track" do + @recording = Recording.start(@music_session.id, @user) + @recording.stop + @recording.reload + @genre = FactoryGirl.create(:genre) + @recording.claim(@user, "name", @genre, true, true) + @recording.reload + @recording.users.length.should == 1 + @recording.users.first.should == @user + @user.recordings.length.should == 1 + @user.recordings.first.should == @recording + @recording.claimed_recordings.length.should == 1 + @claimed_recording = @recording.claimed_recordings.first + @claimed_recording.name.should == "name" + @claimed_recording.genre.should == @genre + @claimed_recording.is_public.should == true + @claimed_recording.is_downloadable.should == true + end + + it "should fail if a user who was not in the session claims a recording" do + @recording = Recording.start(@music_session.id, @user) + @recording.stop + @recording.reload + user2 = FactoryGirl.create(:user) + expect { @recording.claim(user2) }.to raise_error + end + + it "should fail if a user tries to claim a recording twice" do + @recording = Recording.start(@music_session.id, @user) + @recording.stop + @recording.reload + @genre = FactoryGirl.create(:genre) + @recording.claim(@user, "name", @genre, true, true) + @recording.reload + expect { @recording.claim(@user, "name", @genre, true, true) }.to raise_error + end + + it "should allow editing metadata for claimed recordings" do + @recording = Recording.start(@music_session.id, @user) + @recording.stop + @recording.reload + @genre = FactoryGirl.create(:genre) + @claimed_recording = @recording.claim(@user, "name", @genre, true, true) + @genre2 = FactoryGirl.create(:genre) + @claimed_recording.update_fields(@user, :name => "name2", :genre => @genre2.id, :is_public => false, :is_downloadable => false) + @claimed_recording.reload + @claimed_recording.name.should == "name2" + @claimed_recording.genre.should == @genre2 + @claimed_recording.is_public.should == false + @claimed_recording.is_downloadable.should == false + end + + it "should only allow the owner to edit a claimed recording" do + @recording = Recording.start(@music_session.id, @user) + @recording.stop + @recording.reload + @genre = FactoryGirl.create(:genre) + @claimed_recording = @recording.claim(@user, "name", @genre, true, true) + @user2 = FactoryGirl.create(:user) + expect { @claimed_recording.update_fields(@user2, "name2") }.to raise_error + end + + it "should record the duration of the recording properly" do + @recording = Recording.start(@music_session.id, @user) + @recording.duration.should be_nil + @recording.stop + @recording.reload + @recording.duration.should_not be_nil + # Note: it will be 0 since this was fast. You can see something non-zero by just + # inserting a sleep here. + # puts @recording.duration + end + + it "should only destroy a single claimed_recording if there are more than one" do + @user2 = FactoryGirl.create(:user) + @connection2 = FactoryGirl.create(:connection, :user => @user2) + @track = FactoryGirl.create(:track, :connection => @connection2, :instrument => @instrument) + @music_session.connections << @connection2 + @music_session.save + @recording = Recording.start(@music_session.id, @user) + @recording.stop + @recording.reload + @genre = FactoryGirl.create(:genre) + @claimed_recording = @recording.claim(@user, "name", @genre, true, true) + expect { @claimed_recordign.discard(@user2) }.to raise_error + @claimed_recording = @recording.claim(@user2, "name2", @genre, true, true) + @claimed_recording.discard(@user2) + @recording.reload + @recording.claimed_recordings.length.should == 1 + end + + it "should destroy the entire recording if there was only one claimed_recording which is discarded" do + @recording = Recording.start(@music_session.id, @user) + @recording.stop + @recording.reload + @genre = FactoryGirl.create(:genre) + @claimed_recording = @recording.claim(@user, "name", @genre, true, true) + @claimed_recording.discard(@user) + expect { Recording.find(@recording.id) }.to raise_error + expect { ClaimedRecording.find(@claimed_recording.id) }.to raise_error + end + end From a605fa20836401999dd8ff86220651dda21e8bea Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Wed, 24 Apr 2013 23:57:45 -0700 Subject: [PATCH 200/274] missing file --- lib/jam_ruby/models/claimed_recording.rb | 36 ++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 lib/jam_ruby/models/claimed_recording.rb diff --git a/lib/jam_ruby/models/claimed_recording.rb b/lib/jam_ruby/models/claimed_recording.rb new file mode 100644 index 000000000..0c07b81bd --- /dev/null +++ b/lib/jam_ruby/models/claimed_recording.rb @@ -0,0 +1,36 @@ +module JamRuby + class ClaimedRecording < ActiveRecord::Base + + belongs_to :recording, :class_name => "JamRuby::Recording", :inverse_of => :claimed_recordings + belongs_to :user, :class_name => "JamRuby::User", :inverse_of => :claimed_recordings + belongs_to :genre, :class_name => "JamRuby::Genre" + + # user must own this object + # params is a hash, and everything is optional + def update_fields(user, params) + if user != self.user + raise PermissionError, "user doesn't own claimed_recording" + end + + self.name = params[:name] unless params[:name].nil? + self.genre = Genre.find(params[:genre]) unless params[:genre].nil? + self.is_public = params[:is_public] unless params[:is_public].nil? + self.is_downloadable = params[:is_downloadable] unless params[:is_downloadable].nil? + save + end + + def discard(user) + if user != self.user + raise PermissionError, "user doesn't own claimed_recording" + end + + # If this is the only copy, destroy the entire recording. Otherwise, just destroy this claimed_recording + recording = self.recording + if recording.claimed_recordings.count == 1 + recording.discard + else + self.destroy + end + end + end +end From c03a6751dda6c5818550bee0a7e8508127a6587a Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Thu, 25 Apr 2013 00:08:19 -0700 Subject: [PATCH 201/274] fix test --- spec/jam_ruby/models/mix_spec.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/jam_ruby/models/mix_spec.rb b/spec/jam_ruby/models/mix_spec.rb index 5383a6adc..e06d263d0 100644 --- a/spec/jam_ruby/models/mix_spec.rb +++ b/spec/jam_ruby/models/mix_spec.rb @@ -19,7 +19,6 @@ describe Mix do @mix.owner_id.should == @user.id @mix.description.should == "description" @mix.manifest.should == "{}" - @mix.url.should be_nil @mix.mix_server.should be_nil @mix.started_at.should be_nil @mix.completed_at.should be_nil From 00578afc49833debd51b81cd11585e158ea9e536 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Fri, 26 Apr 2013 23:24:13 -0400 Subject: [PATCH 202/274] VRFS-306 bug fixes to support followers/followings --- lib/jam_ruby/models/user.rb | 12 ++++++++---- lib/jam_ruby/models/user_follower.rb | 3 ++- lib/jam_ruby/models/user_following.rb | 3 ++- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 244b616ae..53015dbca 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -47,12 +47,16 @@ module JamRuby has_many :inverse_band_likes, :through => :band_likes, :class_name => "JamRuby::Band", :foreign_key => "band_id" # followers - has_many :followers, :class_name => "JamRuby::UserFollower", :foreign_key => "user_id", :inverse_of => :user - has_many :inverse_followers, :through => :followers, :class_name => "JamRuby::User", :foreign_key => "follower_id" + has_many :user_followers, :class_name => "JamRuby::UserFollower", :foreign_key => "user_id" + has_many :followers, :through => :user_followers, :class_name => "JamRuby::User" + has_many :inverse_user_followers, :through => :followers, :class_name => "JamRuby::UserFollower", :foreign_key => "follower_id" + has_many :inverse_followers, :through => :inverse_user_followers, :source => :user, :class_name => "JamRuby::User" # user followings - has_many :followings, :class_name => "JamRuby::UserFollowing", :foreign_key => "follower_id", :inverse_of => :user - has_many :inverse_followings, :through => :followings, :class_name => "JamRuby::User", :foreign_key => "user_id" + has_many :user_followings, :class_name => "JamRuby::UserFollowing", :foreign_key => "follower_id" + has_many :followings, :through => :user_followings, :class_name => "JamRuby::User" + has_many :inverse_user_followings, :through => :followings, :class_name => "JamRuby::UserFollowing", :foreign_key => "user_id" + has_many :inverse_followings, :through => :inverse_user_followings, :source => :user, :class_name => "JamRuby::User" # band followings has_many :band_followings, :class_name => "JamRuby::BandFollower", :foreign_key => "follower_id", :inverse_of => :user diff --git a/lib/jam_ruby/models/user_follower.rb b/lib/jam_ruby/models/user_follower.rb index b461d5a82..e3cd4615a 100644 --- a/lib/jam_ruby/models/user_follower.rb +++ b/lib/jam_ruby/models/user_follower.rb @@ -5,6 +5,7 @@ module JamRuby self.primary_key = 'id' - belongs_to :user, :class_name => "JamRuby::User", :foreign_key => "follower_id", :inverse_of => :inverse_followers + belongs_to :user, :class_name => "JamRuby::User", :foreign_key => "user_id", :inverse_of => :inverse_followers + belongs_to :follower, :class_name => "JamRuby::User", :foreign_key => "follower_id", :inverse_of => :followers end end \ No newline at end of file diff --git a/lib/jam_ruby/models/user_following.rb b/lib/jam_ruby/models/user_following.rb index 55fd62f3f..ea9f99ab8 100644 --- a/lib/jam_ruby/models/user_following.rb +++ b/lib/jam_ruby/models/user_following.rb @@ -5,6 +5,7 @@ module JamRuby self.primary_key = 'id' - belongs_to :user, :class_name => "JamRuby::User", :foreign_key => "user_id", :inverse_of => :inverse_followings + belongs_to :user, :class_name => "JamRuby::User", :foreign_key => "follower_id", :inverse_of => :inverse_followings + belongs_to :following, :class_name => "JamRuby::User", :foreign_key => "user_id", :inverse_of => :followings end end \ No newline at end of file From 72cdd1b4d72295fe60cc83b49e81f1fdd3cc6f2a Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Sun, 28 Apr 2013 15:06:17 -0400 Subject: [PATCH 203/274] VRFS-305 musician profile bands tab --- lib/jam_ruby.rb | 1 + lib/jam_ruby/models/band.rb | 13 ++++++++----- lib/jam_ruby/models/band_follower.rb | 4 ++-- lib/jam_ruby/models/band_following.rb | 11 +++++++++++ lib/jam_ruby/models/user.rb | 6 ++++-- 5 files changed, 26 insertions(+), 9 deletions(-) create mode 100644 lib/jam_ruby/models/band_following.rb diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index b83e5869b..d65dbeed9 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -39,6 +39,7 @@ require "jam_ruby/models/artifact_update" require "jam_ruby/models/band_invitation" require "jam_ruby/models/band_liker" require "jam_ruby/models/band_follower" +require "jam_ruby/models/band_following" require "jam_ruby/models/band_musician" require "jam_ruby/models/connection" require "jam_ruby/models/friendship" diff --git a/lib/jam_ruby/models/band.rb b/lib/jam_ruby/models/band.rb index 68165f67b..4f462d28c 100644 --- a/lib/jam_ruby/models/band.rb +++ b/lib/jam_ruby/models/band.rb @@ -20,8 +20,10 @@ module JamRuby has_many :inverse_likers, :through => :likers, :class_name => "JamRuby::User", :foreign_key => "liker_id" # followers - has_many :followers, :class_name => "JamRuby::BandFollower", :foreign_key => "band_id", :inverse_of => :band - has_many :inverse_followers, :through => :followers, :class_name => "JamRuby::User", :foreign_key => "follower_id" + has_many :band_followers, :class_name => "JamRuby::BandFollower", :foreign_key => "band_id" + has_many :followers, :through => :band_followers, :class_name => "JamRuby::Band" + has_many :inverse_band_followers, :through => :followers, :class_name => "JamRuby::BandFollower", :foreign_key => "follower_id" + has_many :inverse_followers, :through => :inverse_band_followers, :source => :band, :class_name => "JamRuby::Band" # invitations has_many :invitations, :inverse_of => :band, :class_name => "JamRuby::BandInvitation", :foreign_key => "band_id" @@ -46,9 +48,10 @@ module JamRuby end def location - # TODO: implement a single string version of location; - # this will be indexed into elasticsearch and returned in search - return "#{self.city}, #{self.state}, #{self.country}" + loc = self.city.blank? ? '' : self.city + loc = loc.blank? ? self.state : "#{loc}, #{self.state}" unless self.state.blank? + #loc = loc.blank? ? self.country : "#{loc}, #{self.country}" unless self.country.blank? + loc end def add_member(user_id, admin) diff --git a/lib/jam_ruby/models/band_follower.rb b/lib/jam_ruby/models/band_follower.rb index 8c8ef6654..671e36dcf 100644 --- a/lib/jam_ruby/models/band_follower.rb +++ b/lib/jam_ruby/models/band_follower.rb @@ -5,7 +5,7 @@ module JamRuby self.primary_key = 'id' - belongs_to :band, :class_name => "JamRuby::Band", :foreign_key => "band_id", :inverse_of => :followers - belongs_to :user, :class_name => "JamRuby::User", :foreign_key => "follower_id", :inverse_of => :band_followings + belongs_to :band, :class_name => "JamRuby::Band", :foreign_key => "band_id", :inverse_of => :inverse_band_followers + belongs_to :follower, :class_name => "JamRuby::User", :foreign_key => "follower_id", :inverse_of => :band_followers end end \ No newline at end of file diff --git a/lib/jam_ruby/models/band_following.rb b/lib/jam_ruby/models/band_following.rb new file mode 100644 index 000000000..2f2a9cc02 --- /dev/null +++ b/lib/jam_ruby/models/band_following.rb @@ -0,0 +1,11 @@ +module JamRuby + class BandFollowing < ActiveRecord::Base + + self.table_name = "bands_followers" + + self.primary_key = 'id' + + belongs_to :user, :class_name => "JamRuby::User", :foreign_key => "follower_id", :inverse_of => :inverse_band_followings + belongs_to :band_following, :class_name => "JamRuby::Band", :foreign_key => "band_id", :inverse_of => :band_followings + end +end \ No newline at end of file diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 53015dbca..d6b6ee13d 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -59,8 +59,10 @@ module JamRuby has_many :inverse_followings, :through => :inverse_user_followings, :source => :user, :class_name => "JamRuby::User" # band followings - has_many :band_followings, :class_name => "JamRuby::BandFollower", :foreign_key => "follower_id", :inverse_of => :user - has_many :inverse_band_followings, :through => :band_followings, :class_name => "JamRuby::Band", :foreign_key => "band_id" + has_many :b_followings, :class_name => "JamRuby::BandFollowing", :foreign_key => "follower_id" + has_many :band_followings, :through => :b_followings, :class_name => "JamRuby::Band" + has_many :inverse_b_followings, :through => :band_followings, :class_name => "JamRuby::BandFollowing", :foreign_key => "band_id" + has_many :inverse_band_followings, :through => :inverse_band_followings, :source => :band, :class_name => "JamRuby::Band" # favorites has_many :favorites, :class_name => "JamRuby::UserFavorite", :foreign_key => "user_id" From 87564c442b904989ad5a213f87a5ce010e26ebce Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Sun, 28 Apr 2013 22:21:16 -0400 Subject: [PATCH 204/274] fix tests --- lib/jam_ruby/models/band.rb | 2 +- lib/jam_ruby/models/band_follower.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/jam_ruby/models/band.rb b/lib/jam_ruby/models/band.rb index 4f462d28c..d74ca4911 100644 --- a/lib/jam_ruby/models/band.rb +++ b/lib/jam_ruby/models/band.rb @@ -21,7 +21,7 @@ module JamRuby # followers has_many :band_followers, :class_name => "JamRuby::BandFollower", :foreign_key => "band_id" - has_many :followers, :through => :band_followers, :class_name => "JamRuby::Band" + has_many :followers, :through => :band_followers, :class_name => "JamRuby::User" has_many :inverse_band_followers, :through => :followers, :class_name => "JamRuby::BandFollower", :foreign_key => "follower_id" has_many :inverse_followers, :through => :inverse_band_followers, :source => :band, :class_name => "JamRuby::Band" diff --git a/lib/jam_ruby/models/band_follower.rb b/lib/jam_ruby/models/band_follower.rb index 671e36dcf..adac52892 100644 --- a/lib/jam_ruby/models/band_follower.rb +++ b/lib/jam_ruby/models/band_follower.rb @@ -5,7 +5,7 @@ module JamRuby self.primary_key = 'id' - belongs_to :band, :class_name => "JamRuby::Band", :foreign_key => "band_id", :inverse_of => :inverse_band_followers - belongs_to :follower, :class_name => "JamRuby::User", :foreign_key => "follower_id", :inverse_of => :band_followers + belongs_to :band, :class_name => "JamRuby::Band", :foreign_key => "band_id" + belongs_to :follower, :class_name => "JamRuby::User", :foreign_key => "follower_id" end end \ No newline at end of file From 5e64e6a7a24ee590f50e116766daceb959f633e9 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 5 May 2013 19:17:16 -0500 Subject: [PATCH 205/274] * perf-data upload fixes --- lib/jam_ruby/models/artifact_update.rb | 20 +++++++++---------- .../models/music_session_perf_data.rb | 10 +++++----- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/lib/jam_ruby/models/artifact_update.rb b/lib/jam_ruby/models/artifact_update.rb index d224f6120..f1301820a 100644 --- a/lib/jam_ruby/models/artifact_update.rb +++ b/lib/jam_ruby/models/artifact_update.rb @@ -8,24 +8,22 @@ 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 => true - validate :size, :presence => true - validate :environment, :presence => true - validate :product, :inclusion => {:in => PRODUCTS} + validates :version, :presence => true + validates :uri, :presence => true + validates :sha1, :presence => true + validates :size, :presence => true + validates :environment, :presence => true + validates :product, :inclusion => {:in => PRODUCTS} - private - - def update_uri_attributes + before_validation(:on => :create) do if uri.present? && uri_changed? self.size = uri.file.size self.sha1 = Digest::MD5.hexdigest(File.read(uri.current_path)) end end + end end diff --git a/lib/jam_ruby/models/music_session_perf_data.rb b/lib/jam_ruby/models/music_session_perf_data.rb index 5d0ae91cc..16ce5e3bb 100644 --- a/lib/jam_ruby/models/music_session_perf_data.rb +++ b/lib/jam_ruby/models/music_session_perf_data.rb @@ -5,20 +5,20 @@ module JamRuby self.primary_key = 'id' - attr_accessible :client_id, :uri + attr_accessible :uri belongs_to :music_session, :class_name => "JamRuby::MusicSessionHistory" #mount_uploader :uri, PerfDataUploader - validate :music_session, :presence => true - validate :client_id, :presence => true - validate :uri, :presence => true + validates :music_session, :presence => true + validates :client_id, :presence => true + validates :uri, :presence => true before_validation(:on => :create) do self.created_at ||= Time.now self.id = SecureRandom.uuid - self.uri = "perf_data/#{self.id}/#{self.client_id}-#{self.created_at}" + self.uri = "perf_data/#{self.music_session_id}/#{self.client_id}-#{self.created_at.to_i}" end end end From d3aa419b6006b97162867cfcd2838006d37349ed Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Mon, 6 May 2013 16:47:20 -0700 Subject: [PATCH 206/274] big recording push --- lib/jam_ruby/lib/s3_manager.rb | 7 ++ lib/jam_ruby/models/mix.rb | 20 ++-- lib/jam_ruby/models/recorded_track.rb | 5 +- lib/jam_ruby/models/recording.rb | 124 ++++++++++++++++++++----- spec/jam_ruby/models/mix_spec.rb | 12 +-- spec/jam_ruby/models/recording_spec.rb | 97 +++++++++++++++++-- 6 files changed, 214 insertions(+), 51 deletions(-) diff --git a/lib/jam_ruby/lib/s3_manager.rb b/lib/jam_ruby/lib/s3_manager.rb index 2ce44e8fc..edcd305ac 100644 --- a/lib/jam_ruby/lib/s3_manager.rb +++ b/lib/jam_ruby/lib/s3_manager.rb @@ -23,17 +23,24 @@ module JamRuby end def self.multipart_upload_start(upload_filename) + return 0 if @is_unit_test s3_bucket.objects[upload_filename].multipart_upload.id end def self.multipart_upload_complete(upload_id) + return if @is_unit_test s3_bucket.objects[upload_filename].multipart_uploads[upload_id].upload_complete(:remote_parts) end def self.delete(filename) + return if @is_unit_test s3_bucket.objects[filename].delete end + def self.set_unit_test + @is_unit_test = true + end + private def self.s3_bucket diff --git a/lib/jam_ruby/models/mix.rb b/lib/jam_ruby/models/mix.rb index 345074249..629a457b8 100644 --- a/lib/jam_ruby/models/mix.rb +++ b/lib/jam_ruby/models/mix.rb @@ -5,19 +5,13 @@ module JamRuby before_destroy :delete_s3_files self.primary_key = 'id' - belongs_to :recording, :class_name => "JamRuby::Recording", :inverse_of => :mix + belongs_to :recording, :class_name => "JamRuby::Recording", :inverse_of => :mixes - def self.schedule(recording_id, user_id, description, manifest) - # 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) + def self.schedule(recording, manifest) 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.recording = recording mix.manifest = manifest mix.save @@ -43,14 +37,20 @@ module JamRuby mix end - def finish() + def finish(length, md5) self.completed_at = Time.now + self.length = length + self.md5 = md5 save end def s3_url S3Manager.s3_url(hashed_filename) end + + def url + S3Manager.url(hashed_filename) + end private diff --git a/lib/jam_ruby/models/recorded_track.rb b/lib/jam_ruby/models/recorded_track.rb index 053cf68ae..df4876eef 100644 --- a/lib/jam_ruby/models/recorded_track.rb +++ b/lib/jam_ruby/models/recorded_track.rb @@ -26,8 +26,10 @@ module JamRuby recorded_track end - def upload_start + def upload_start(length, md5) self.upload_id = S3Manager.multipart_upload_start(hashed_filename) + self.length = length + self.md5 = md5 save end @@ -45,6 +47,7 @@ module JamRuby S3Manager.multipart_upload_complete(upload_id) self.fully_uploaded = true save + recording.upload_complete end def url diff --git a/lib/jam_ruby/models/recording.rb b/lib/jam_ruby/models/recording.rb index 2b65cb2ae..1ad79894e 100644 --- a/lib/jam_ruby/models/recording.rb +++ b/lib/jam_ruby/models/recording.rb @@ -1,9 +1,3 @@ -# A bunch to do here: -# 1. Make it so users "own" recordings -# 2. Add a route for a user to specify if they want to save or discard a recording. If save, then add them to recordings_users (not sure anything else is even doing that now) -# 3. Need a way to get a list of all the recordings you "should" have as a user. That will be all the recorded tracks plus mixes for all recordings you "own" -# 4. I think the notion of the recording's "owner" being who started it can remain. - module JamRuby class Recording < ActiveRecord::Base @@ -14,12 +8,9 @@ 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 :mixes, :class_name => "JamRuby::Mix", :inverse_of => :recording has_many :recorded_tracks, :class_name => "JamRuby::RecordedTrack", :foreign_key => :recording_id - # genres - has_and_belongs_to_many :genres, :class_name => "JamRuby::Genre", :join_table => "recordings_genres" - # favorites has_many :favorites, :class_name => "JamRuby::UserFavorite", :foreign_key => "recording_id" has_many :inverse_favorites, :through => :favorites, :source => :recording, :class_name => "JamRuby::Recording" @@ -28,6 +19,7 @@ module JamRuby return self.favorites.size end + # FIXME: This isn't right. def self.user_recordings(user_id) query = Recording .joins( @@ -89,10 +81,6 @@ module JamRuby end end - music_session.genres.each do |genre| - recording.genres << genre - end - # Note that I believe this can be nil. recording.band = music_session.band recording.save @@ -178,6 +166,90 @@ module JamRuby self.destroy end + # Returns the list of files the user needs to upload. This will only ever be recordings + def self.upload_file_list(user) + files = [] + User.joins(:recordings).joins(:recordings => :recorded_tracks) + .where(%Q{ recordings.duration IS NOT NULL }) + .where("recorded_tracks.user_id = '#{user.id}'") + .where(%Q{ recorded_tracks.fully_uploaded = FALSE }).each do |user| + user.recordings.each.do |recording| + recording.recorded_tracks.each do |recorded_track| + files.push( + { + :type => "recorded_track", + :id => recorded_track.id, + :url => recorded_track.url # FIXME IS THIS RIGHT? + } + ) + end + end + files + end + + # Returns the list of files this user should have synced to his computer, along with md5s and lengths + def self.file_list(user) + files = [] + + # That second join is important. It's saying join off of recordings, NOT user. If you take out the + # ":recordings =>" part, you'll just get the recorded_tracks that I played. Very different! + User.joins(:recordings).joins(:recordings => :recorded_tracks) + .order(%Q{ recordings.created_at DESC }) + .where(%Q{ recorded_tracks.fully_uploaded = TRUE }) + .where(:id => user.id).each do |user| + user.recordings.each do |recording| + recording.recorded_tracks.each do |recorded_track| + recorded_track = user.claimed_recordings.first.recording.recorded_tracks.first + files.push( + { + :type => "recorded_track", + :id => recorded_track.id, + :length => recorded_track.length, + :md5 => recorded_track.md5, + :url => recorded_track.url + } + ) + end + end + end + + User.joins(:recordings).joins(:recordings => :mixes) + .order(%Q{ recordings.created_at DESC }) + .where(%Q{ mixes.completed_at IS NOT NULL }).each do |user| + user.recordings.each do |recording| + recording.mixes.each do |mix| + files.push( + { + :type => "mix", + :id => mix.id, + :length => mix.length, + :md5 => mix.md5, + :url => mix.url + } + ) + end + end + end + + files + end + + # Check to see if all files have been uploaded. If so, kick off a mix. + def upload_complete + # Don't allow multiple mixes for now. + raise JamRuby::JamArgumentError unless self.mixes.length == 0 + + # FIXME: There's a possible race condition here. If two users complete + # uploads at the same time, we'll schedule 2 mixes. + recorded_tracks.each do |recorded_track| + return unless recorded_track.fully_uploaded + end + + self.mixes << Mix.schedule(self, base_mix_manifest.to_json) + + save + end + =begin # This is no longer remotely right. def self.search(query, options = { :limit => 10 }) @@ -198,6 +270,19 @@ module JamRuby end =end + def base_mix_manifest + manifest = { "files" => [], "timeline" => [] } + mix_params = [] + recorded_tracks.each do |recorded_track| + return nil unless recorded_track.fully_uploaded + manifest["files"] << { "url" => recorded_track.url, "codec" => "opus", "offset" => 0 } + mix_params << { "level" => 100, "balance" => 0 } + end + + manifest["timeline"] << { "timestamp" => 0, "mix" => mix_params } + manifest["timeline"] << { "timestamp" => duration, "end" => true } + manifest + end private def self.validate_user_is_band_member(user, band) @@ -218,14 +303,5 @@ module JamRuby end end - def validate_genres(genres) - if genres.length < Limits::MIN_GENRES_PER_RECORDING - raise JamRuby::JamArgumentError, ValidationMessages::GENRE_MINIMUM_NOT_MET - end - - if genres.length > Limits::MAX_GENRES_PER_RECORDING - raise JamRuby::JamArgumentError, ValidationMessages::GENRE_LIMIT_EXCEEDED - end - end - end + end end diff --git a/spec/jam_ruby/models/mix_spec.rb b/spec/jam_ruby/models/mix_spec.rb index e06d263d0..91f3d0463 100644 --- a/spec/jam_ruby/models/mix_spec.rb +++ b/spec/jam_ruby/models/mix_spec.rb @@ -11,13 +11,11 @@ describe Mix do @music_session.save @recording = Recording.start(@music_session.id, @user) @recording.stop - @mix = Mix.schedule(@recording.id, @user.id, "description", "{}") + @mix = Mix.schedule(@recording, "{}") 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.manifest.should == "{}" @mix.mix_server.should be_nil @mix.started_at.should be_nil @@ -26,11 +24,11 @@ describe Mix do 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 + expect { Mix.schedule(@recording) }.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 + expect { @mix2 = Mix.schedule(Recording.find('lskdjflsd')) }.to raise_error end it "should return a mix when the cron asks for it" do @@ -43,9 +41,11 @@ describe Mix do end it "should record when a mix has finished" do - Mix.find(@mix.id).finish + Mix.find(@mix.id).finish(10000, "md5hash") @mix.reload @mix.completed_at.should_not be_nil + @mix.length.should == 10000 + @mix.md5.should == "md5hash" end it "should re-run a mix if it was started a long time ago" do diff --git a/spec/jam_ruby/models/recording_spec.rb b/spec/jam_ruby/models/recording_spec.rb index 8e035a986..6a9f6ebea 100644 --- a/spec/jam_ruby/models/recording_spec.rb +++ b/spec/jam_ruby/models/recording_spec.rb @@ -3,13 +3,14 @@ require 'spec_helper' describe Recording 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 + S3Manager.set_unit_test + @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 end it "should not start a recording if the music session doesnt exist" do @@ -22,8 +23,6 @@ describe Recording do @music_session.reload @music_session.recording.should == @recording @recording.owner_id.should == @user.id - @recording.genres.should == @music_session.genres - @recording.users.length.should == 0 @recorded_tracks = RecordedTrack.where(:recording_id => @recording.id) @recorded_tracks.length.should == 1 @@ -95,7 +94,7 @@ describe Recording do expect { RecordedTracks.find(@recorded_track.id) }.to raise_error end - it "should allow a user to claim a track" do + it "should allow a user to claim a recording" do @recording = Recording.start(@music_session.id, @user) @recording.stop @recording.reload @@ -197,6 +196,84 @@ describe Recording do expect { ClaimedRecording.find(@claimed_recording.id) }.to raise_error end + it "should return a file list for a user properly" do + @recording = Recording.start(@music_session.id, @user) + @recording.stop + @recording.reload + @genre = FactoryGirl.create(:genre) + @recording.claim(@user, "Recording", @genre, true, true) + Recording.file_list(@user).length.should == 0 + @recorded_track = @recording.recorded_tracks.first + @recorded_track.upload_start(25000, "md5hash") + @recorded_track.upload_complete + Recording.file_list(@user).length.should == 1 + file = Recording.file_list(@user).first + file[:type].should == "recorded_track" + file[:id].should == @recorded_track.id + file[:length].should == 25000 + file[:md5].should == "md5hash" + file[:url].should == S3Manager.url(S3Manager.hashed_filename('recorded_track', @recorded_track.id)) + + # Note that the recording should automatically schedule a mix when the upload completes + @recording.mixes.length.should == 1 + @mix = Mix.next('server') + @mix.should_not be_nil + @mix.finish(50000, "md5hash") + Recording.file_list(@user).length.should == 2 + file = Recording.file_list(@user).last + file[:type].should == "mix" + file[:id].should == @mix.id + file[:length].should == 50000 + file[:md5].should == "md5hash" + file[:url].should == S3Manager.url(S3Manager.hashed_filename('mix', @mix.id)) + + end + + it "should create a base mix manifest properly" do + @user2 = FactoryGirl.create(:user) + @connection2 = FactoryGirl.create(:connection, :user => @user) + @instrument2 = FactoryGirl.create(:instrument, :description => 'a great instrument') + @track2 = FactoryGirl.create(:track, :connection => @connection2, :instrument => @instrument2) + @music_session.connections << @connection2 + @music_session.save + @recording = Recording.start(@music_session.id, @user) + #sleep 4 + @recording.stop + @recording.recorded_tracks.length.should == 2 + @recorded_track = @recording.recorded_tracks.first + @recorded_track.upload_start(25000, "md5hash") + @recorded_track.upload_complete + @recorded_track2 = @recording.recorded_tracks.last + @recorded_track2.upload_start(50000, "md5hash2") + @recorded_track2.upload_complete + mix_manifest = @recording.base_mix_manifest + mix_manifest.should_not be_nil + files = mix_manifest["files"] + files.should_not be_nil + files.length.should == 2 + files.first["codec"].should == "opus" + files.first["offset"].should == 0 + files.first["url"].should == @recording.recorded_tracks.first.url + files.last["codec"].should == "opus" + files.last["offset"].should == 0 + files.last["url"].should == @recording.recorded_tracks.last.url + + timeline = mix_manifest["timeline"] + timeline.should_not be_nil + timeline.length.should == 2 + timeline.first["timestamp"].should == 0 + timeline.first["end"].should be_nil + mix = timeline.first["mix"] + mix.should_not be_nil + mix.length.should == 2 + mix.first["balance"].should == 0 + mix.first["level"].should == 100 + mix.last["balance"].should == 0 + mix.last["level"].should == 100 + + timeline.last["timestamp"].should == @recording.duration + timeline.last["end"].should == true + end end From d51feece1290f9dfd8af55660a9a0a3b16cbfe3f Mon Sep 17 00:00:00 2001 From: Seth Call Date: Tue, 7 May 2013 11:52:31 -0500 Subject: [PATCH 207/274] * removing check for protected method (due to switch to ruby 2.0.0) --- spec/jam_ruby/models/user_spec.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/jam_ruby/models/user_spec.rb b/spec/jam_ruby/models/user_spec.rb index 1821d72a0..657d79332 100644 --- a/spec/jam_ruby/models/user_spec.rb +++ b/spec/jam_ruby/models/user_spec.rb @@ -13,7 +13,6 @@ describe User do it { should respond_to(:first_name) } it { should respond_to(:last_name) } it { should respond_to(:email) } - it { should respond_to(:password_digest) } it { should respond_to(:password) } it { should respond_to(:password_confirmation) } it { should respond_to(:remember_token) } From 2dfdf8463c58e92a7c122e5237f429e7f2347d59 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Fri, 10 May 2013 07:10:33 -0500 Subject: [PATCH 208/274] * VRFS-311 - emailing and user model business logic done --- lib/jam_ruby.rb | 1 + lib/jam_ruby/app/mailers/user_mailer.rb | 20 +++++ .../friend_invitation.html.erb | 18 ++++ .../user_mailer/updated_email.html.erb | 6 ++ .../user_mailer/updated_email.text.erb | 1 + .../user_mailer/updating_email.html.erb | 6 ++ .../user_mailer/updating_email.text.erb | 1 + .../app/views/layouts/user_mailer.html.erb | 49 +++++++++++ .../app/views/layouts/user_mailer.text.erb | 6 ++ lib/jam_ruby/constants/validation_messages.rb | 2 + lib/jam_ruby/models/user.rb | 53 +++++++++++- lib/jam_ruby/models/user_observer.rb | 14 +++ spec/jam_ruby/models/user_spec.rb | 85 +++++++++++++++++++ spec/spec_helper.rb | 2 + 14 files changed, 260 insertions(+), 4 deletions(-) create mode 100644 lib/jam_ruby/app/views/jam_ruby/user_mailer/updated_email.html.erb create mode 100644 lib/jam_ruby/app/views/jam_ruby/user_mailer/updated_email.text.erb create mode 100644 lib/jam_ruby/app/views/jam_ruby/user_mailer/updating_email.html.erb create mode 100644 lib/jam_ruby/app/views/jam_ruby/user_mailer/updating_email.text.erb create mode 100644 lib/jam_ruby/app/views/layouts/user_mailer.html.erb create mode 100644 lib/jam_ruby/app/views/layouts/user_mailer.text.erb create mode 100644 lib/jam_ruby/models/user_observer.rb diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index d65dbeed9..2d608b036 100644 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -30,6 +30,7 @@ require "jam_ruby/lib/s3_manager" require "jam_ruby/message_factory" require "jam_ruby/models/genre" require "jam_ruby/models/user" +require "jam_ruby/models/user_observer" require "jam_ruby/models/user_authorization" require "jam_ruby/models/join_request" require "jam_ruby/models/band" diff --git a/lib/jam_ruby/app/mailers/user_mailer.rb b/lib/jam_ruby/app/mailers/user_mailer.rb index 8be2e6c0f..33f0c5d86 100644 --- a/lib/jam_ruby/app/mailers/user_mailer.rb +++ b/lib/jam_ruby/app/mailers/user_mailer.rb @@ -9,6 +9,8 @@ module JamRuby class UserMailer < ActionMailer::Base include SendGrid + layout "user_mailer" + DEFAULT_SENDER = "support@jamkazam.com" default :from => DEFAULT_SENDER @@ -46,5 +48,23 @@ module JamRuby format.html end end + + def updating_email(user) + @user = user + sendgrid_unique_args :type => "updating_email" + mail(:to => user.email, :subject => "Jamkazam Email Change Confirmation") do |format| + format.text + format.html + end + end + + def updated_email(user) + @user = user + sendgrid_unique_args :type => "updated_email" + mail(:to => user.email, :subject => "Jamkazam Email Changed") do |format| + format.text + format.html + end + end end end diff --git a/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/friend_invitation.html.erb b/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/friend_invitation.html.erb index 081997e08..13dbd5f39 100644 --- a/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/friend_invitation.html.erb +++ b/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/friend_invitation.html.erb @@ -8,3 +8,21 @@

To signup, please go to the Create Account page.

+ + + + diff --git a/lib/jam_ruby/app/views/jam_ruby/user_mailer/updated_email.html.erb b/lib/jam_ruby/app/views/jam_ruby/user_mailer/updated_email.html.erb new file mode 100644 index 000000000..27f2c09fa --- /dev/null +++ b/lib/jam_ruby/app/views/jam_ruby/user_mailer/updated_email.html.erb @@ -0,0 +1,6 @@ + +

Jamkazam Email Confirmed

+

<%= @user.email %> is confirmed!

+
+ + \ No newline at end of file diff --git a/lib/jam_ruby/app/views/jam_ruby/user_mailer/updated_email.text.erb b/lib/jam_ruby/app/views/jam_ruby/user_mailer/updated_email.text.erb new file mode 100644 index 000000000..808d8eb93 --- /dev/null +++ b/lib/jam_ruby/app/views/jam_ruby/user_mailer/updated_email.text.erb @@ -0,0 +1 @@ +do it \ No newline at end of file diff --git a/lib/jam_ruby/app/views/jam_ruby/user_mailer/updating_email.html.erb b/lib/jam_ruby/app/views/jam_ruby/user_mailer/updating_email.html.erb new file mode 100644 index 000000000..295a8de9d --- /dev/null +++ b/lib/jam_ruby/app/views/jam_ruby/user_mailer/updating_email.html.erb @@ -0,0 +1,6 @@ + +

Please Confirm New Jamkazam Email

+

Please click the following link to confirm your change in email: confirm email.

+
+ + \ No newline at end of file diff --git a/lib/jam_ruby/app/views/jam_ruby/user_mailer/updating_email.text.erb b/lib/jam_ruby/app/views/jam_ruby/user_mailer/updating_email.text.erb new file mode 100644 index 000000000..13f92ba7a --- /dev/null +++ b/lib/jam_ruby/app/views/jam_ruby/user_mailer/updating_email.text.erb @@ -0,0 +1 @@ +Please click the following link to confirm your change in email: <%= @user.update_email_confirmation_url %>. diff --git a/lib/jam_ruby/app/views/layouts/user_mailer.html.erb b/lib/jam_ruby/app/views/layouts/user_mailer.html.erb new file mode 100644 index 000000000..e21294426 --- /dev/null +++ b/lib/jam_ruby/app/views/layouts/user_mailer.html.erb @@ -0,0 +1,49 @@ + + + + + JamKazam + + + + + + + + + + +
JamKazam
+ + + <%= yield %> + + + + +
+ + +
+ + +

+

This email was sent to you because you have an account at Jamkazam. +

+ +
+ + + + +
Copyright © 2013 JamKazam, Inc. All rights reserved. +
+ + + diff --git a/lib/jam_ruby/app/views/layouts/user_mailer.text.erb b/lib/jam_ruby/app/views/layouts/user_mailer.text.erb new file mode 100644 index 000000000..bc0f816eb --- /dev/null +++ b/lib/jam_ruby/app/views/layouts/user_mailer.text.erb @@ -0,0 +1,6 @@ +<%= yield %> + + +This email was sent to you because you have an account at Jamkazam / http://www.jamkazam.com. + +Copyright 2013 JamKazam, Inc. All rights reserved. \ No newline at end of file diff --git a/lib/jam_ruby/constants/validation_messages.rb b/lib/jam_ruby/constants/validation_messages.rb index dd7dc9077..50ce01c63 100644 --- a/lib/jam_ruby/constants/validation_messages.rb +++ b/lib/jam_ruby/constants/validation_messages.rb @@ -30,5 +30,7 @@ module ValidationMessages # user OLD_PASSWORD_DOESNT_MATCH = "Your old password is incorrect." EMAIL_NOT_FOUND = "Email address not found." + NOT_YOUR_PASSWORD = "The password you entered is not your current password." + EMAIL_ALREADY_TAKEN = "This email is already taken." end diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index d6b6ee13d..007ecd212 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -10,7 +10,7 @@ module JamRuby attr_accessible :first_name, :last_name, :email, :password, :password_confirmation, :city, :state, :country, :subscribe_email, :terms_of_service - attr_accessor :updating_password, :administratively_created + attr_accessor :updating_password, :updating_email, :updated_email, :update_email_confirmation_url, :administratively_created, :password_validation # authorizations (for facebook, etc -- omniauth) has_many :user_authorizations, :class_name => "JamRuby::UserAuthorization" @@ -114,6 +114,8 @@ module JamRuby VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i validates :email, presence: true, format: {with: VALID_EMAIL_REGEX}, uniqueness: {case_sensitive: false} + validates :update_email, presence: true, format: {with: VALID_EMAIL_REGEX}, + uniqueness: {case_sensitive: false}, :if => :updating_email validates_length_of :password, minimum: 6, maximum: 100, :if => :should_validate_password? validates_presence_of :password_confirmation, :if => :should_validate_password? @@ -122,12 +124,22 @@ module JamRuby validates :subscribe_email, :inclusion => {:in => [nil, true, false]} validate :validate_musician_instruments + validate :check_password_valid + validate :check_update_email def validate_musician_instruments errors.add(:musician_instruments, ValidationMessages::INSTRUMENT_MINIMUM_NOT_MET) if !administratively_created && musician && musician_instruments.length == 0 errors.add(:musician_instruments, ValidationMessages::INSTRUMENT_LIMIT_EXCEEDED) if !administratively_created && musician && musician_instruments.length > 5 end + def check_password_valid + # checks if the user put in their current password (used when changing your email, for instance) + errors.add(:password_validation, ValidationMessages::NOT_YOUR_PASSWORD) if should_confirm_existing_password? && !valid_password?(self.password_validation) + end + + def check_update_email + errors.add(:update_email, ValidationMessages::EMAIL_ALREADY_TAKEN) if updating_email && User.find_by_email(self.update_email) != nil + end def online @online ||= !self.connections.nil? && self.connections.size > 0 @@ -160,6 +172,10 @@ module JamRuby (updating_password || new_record?) end + def should_confirm_existing_password? + updating_email + end + def end_user_created? return !administratively_created end @@ -319,9 +335,10 @@ module JamRuby end # email - unless email.nil? - self.email = email - end + # !! Email is changed in a dedicated method, 'update_email' + #unless email.nil? + # self.email = email + #end # password unless password.nil? @@ -417,6 +434,34 @@ module JamRuby return user end + def begin_update_email(email, password_validation, confirmation_url) + # sets the user model in a state such that it's expecting to have it's email updated + # two columns matter for this; 'update_email_token' and 'update_email' + # confirmation_link is odd in the sense that it can likely only come from www.jamkazam.com (jam-web) + + # an observer should be set up to send an email based on this activity + self.updating_email = true + self.password_validation = password_validation + self.update_email = email + self.update_email_token = SecureRandom.urlsafe_base64 + self.update_email_confirmation_url = "#{confirmation_url}#{self.update_email_token}" + + self.save + end + + def self.finalize_update_email(update_email_token) + # updates the user model to have a new email address + user = User.find_by_update_email_token!(update_email_token) + + user.updated_email = true + user.email = user.update_email + user.update_email_token = nil + user.save + + return user + end + + def self.create_user_like(user_id, liker_id) liker = UserLiker.new() liker.user_id = user_id diff --git a/lib/jam_ruby/models/user_observer.rb b/lib/jam_ruby/models/user_observer.rb new file mode 100644 index 000000000..c71989f49 --- /dev/null +++ b/lib/jam_ruby/models/user_observer.rb @@ -0,0 +1,14 @@ +module JamRuby + class UserObserver < ActiveRecord::Observer + + observe JamRuby::User + + def after_save(user) + if user.updating_email && !user.errors.any? + UserMailer.updating_email(user).deliver + elsif user.updated_email && !user.errors.any? + UserMailer.updated_email(user).deliver + end + end + end +end \ No newline at end of file diff --git a/spec/jam_ruby/models/user_spec.rb b/spec/jam_ruby/models/user_spec.rb index 657d79332..b0082bfbe 100644 --- a/spec/jam_ruby/models/user_spec.rb +++ b/spec/jam_ruby/models/user_spec.rb @@ -254,4 +254,89 @@ describe User do end end + + describe "update email" do + + before do + UserMailer.deliveries.clear + end + + describe "begin email update" do + describe "success" do + before do + @user.begin_update_email("somenewemail@blah.com", "foobar", "http://www.jamkazam.com/confirm_email_update?token=") + end + + # useful to see contents of email without actually running the app and sending it + # it { puts UserMailer.deliveries[0].parts[0].body.decoded } + it { @user.errors.any?.should be_false } + it { @user.update_email.should == "somenewemail@blah.com" } + it { @user.update_email_confirmation_url.should == "http://www.jamkazam.com/confirm_email_update?token=#{@user.update_email_token}" } + it { UserMailer.deliveries.length.should == 1 } + + end + + it "no email on error" do + @user.begin_update_email("somenewemail@blah.com", "wrong password", "http://www.jamkazam.com/confirm_email_update?token=") + + UserMailer.deliveries.length.should == 0 + end + + it "bad password validation" do + @user.begin_update_email("somenewemail@blah.com", "wrong password", "http://www.jamkazam.com/confirm_email_update?token=") + + @user.errors[:password_validation][0].should == ValidationMessages::NOT_YOUR_PASSWORD + end + + it "existing email of another user" do + another_user = FactoryGirl.create(:user) + @user.begin_update_email(another_user.email, "foobar", "http://www.jamkazam.com/confirm_email_update?token=") + + @user.errors[:update_email][0].should == ValidationMessages::EMAIL_ALREADY_TAKEN + end + + it "bogus email" do + @user.begin_update_email("not_an_email", "foobar", "http://www.jamkazam.com/confirm_email_update?token=") + + @user.errors[:update_email][0].should == "is invalid" + end + + it "empty email" do + @user.begin_update_email(nil, "foobar", "http://www.jamkazam.com/confirm_email_update?token=") + + @user.errors[:update_email][0].should == "can't be blank" + end + end + + describe "finalize email update" do + before do + @user.begin_update_email("somenewemail@blah.com", "foobar", "http://www.jamkazam.com/confirm_email_update?token=") + UserMailer.deliveries.clear + end + + describe "success" do + + before do + @finalized = User.finalize_update_email(@user.update_email_token) + end + + it { @finalized.should == @user } + it { @finalized.email.should == "somenewemail@blah.com" } + it { UserMailer.deliveries.length.should == 1 } + end + + it "no email on unsuccessful finalize" do + expect { User.finalize_update_email("wrong_token") }.to raise_error(ActiveRecord::RecordNotFound) + UserMailer.deliveries.length.should == 0 + end + + it "bad token" do + expect { User.finalize_update_email("wrong_token") }.to raise_error(ActiveRecord::RecordNotFound) + end + + it "empty token" do + expect { User.finalize_update_email(nil) }.to raise_error(ActiveRecord::RecordNotFound) + end + end + end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 52e4f5527..b1686d708 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -21,6 +21,8 @@ include JamRuby # manually register observers ActiveRecord::Base.add_observer InvitedUserObserver.instance +ActiveRecord::Base.add_observer UserObserver.instance + # put ActionMailer into test mode ActionMailer::Base.delivery_method = :test From 5f31daccd174b9fbaa547ac1dd637e61497651b7 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 12 May 2013 22:27:12 -0500 Subject: [PATCH 209/274] * VRFS-311; reject users that try to set their email to the same email as it currently is --- lib/jam_ruby/app/mailers/user_mailer.rb | 2 +- lib/jam_ruby/constants/validation_messages.rb | 6 +++--- lib/jam_ruby/models/user.rb | 8 ++++++-- spec/jam_ruby/models/user_spec.rb | 6 ++++++ 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/lib/jam_ruby/app/mailers/user_mailer.rb b/lib/jam_ruby/app/mailers/user_mailer.rb index 33f0c5d86..3da683b70 100644 --- a/lib/jam_ruby/app/mailers/user_mailer.rb +++ b/lib/jam_ruby/app/mailers/user_mailer.rb @@ -52,7 +52,7 @@ module JamRuby def updating_email(user) @user = user sendgrid_unique_args :type => "updating_email" - mail(:to => user.email, :subject => "Jamkazam Email Change Confirmation") do |format| + mail(:to => user.update_email, :subject => "Jamkazam Email Change Confirmation") do |format| format.text format.html end diff --git a/lib/jam_ruby/constants/validation_messages.rb b/lib/jam_ruby/constants/validation_messages.rb index 50ce01c63..2ecf986a7 100644 --- a/lib/jam_ruby/constants/validation_messages.rb +++ b/lib/jam_ruby/constants/validation_messages.rb @@ -30,7 +30,7 @@ module ValidationMessages # user OLD_PASSWORD_DOESNT_MATCH = "Your old password is incorrect." EMAIL_NOT_FOUND = "Email address not found." - NOT_YOUR_PASSWORD = "The password you entered is not your current password." - EMAIL_ALREADY_TAKEN = "This email is already taken." - + NOT_YOUR_PASSWORD = "is not your current password" + EMAIL_ALREADY_TAKEN = "is already taken" + EMAIL_MATCHES_CURRENT = "is same as your current email" end diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 007ecd212..7ac8f8b32 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -114,7 +114,7 @@ module JamRuby VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i validates :email, presence: true, format: {with: VALID_EMAIL_REGEX}, uniqueness: {case_sensitive: false} - validates :update_email, presence: true, format: {with: VALID_EMAIL_REGEX}, + validates :update_email, presence: true, format: {with: VALID_EMAIL_REGEX}, uniqueness: {case_sensitive: false}, :if => :updating_email validates_length_of :password, minimum: 6, maximum: 100, :if => :should_validate_password? @@ -138,7 +138,11 @@ module JamRuby end def check_update_email - errors.add(:update_email, ValidationMessages::EMAIL_ALREADY_TAKEN) if updating_email && User.find_by_email(self.update_email) != nil + if updating_email && self.update_email == self.email + errors.add(:update_email, ValidationMessages::EMAIL_MATCHES_CURRENT) + elsif updating_email && User.find_by_email(self.update_email) != nil + errors.add(:update_email, ValidationMessages::EMAIL_ALREADY_TAKEN) + end end def online diff --git a/spec/jam_ruby/models/user_spec.rb b/spec/jam_ruby/models/user_spec.rb index b0082bfbe..aa8ea7d0e 100644 --- a/spec/jam_ruby/models/user_spec.rb +++ b/spec/jam_ruby/models/user_spec.rb @@ -288,6 +288,12 @@ describe User do @user.errors[:password_validation][0].should == ValidationMessages::NOT_YOUR_PASSWORD end + it "matches current email" do + @user.begin_update_email(@user.email, "foobar", "http://www.jamkazam.com/confirm_email_update?token=") + + @user.errors[:update_email][0].should == ValidationMessages::EMAIL_MATCHES_CURRENT + end + it "existing email of another user" do another_user = FactoryGirl.create(:user) @user.begin_update_email(another_user.email, "foobar", "http://www.jamkazam.com/confirm_email_update?token=") From faa3fe82a78d7e2b2e663e801d604b74f78ede20 Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Mon, 13 May 2013 22:09:17 -0700 Subject: [PATCH 210/274] more work --- lib/jam_ruby.rb | 1 - lib/jam_ruby/models/claimed_recording.rb | 2 +- lib/jam_ruby/models/mix.rb | 4 ++ lib/jam_ruby/models/recorded_track.rb | 6 ++ lib/jam_ruby/models/recording.rb | 72 ++++++++---------------- lib/jam_ruby/models/user.rb | 4 -- lib/jam_ruby/models/user_favorite.rb | 11 ---- spec/jam_ruby/models/recording_spec.rb | 15 +++-- 8 files changed, 44 insertions(+), 71 deletions(-) mode change 100644 => 100755 lib/jam_ruby.rb delete mode 100644 lib/jam_ruby/models/user_favorite.rb diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb old mode 100644 new mode 100755 index 2d608b036..0f0a2a02b --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -59,7 +59,6 @@ require "jam_ruby/models/user_liker" require "jam_ruby/models/user_like" require "jam_ruby/models/user_follower" require "jam_ruby/models/user_following" -require "jam_ruby/models/user_favorite" require "jam_ruby/models/search" require "jam_ruby/models/recording" require "jam_ruby/models/recorded_track" diff --git a/lib/jam_ruby/models/claimed_recording.rb b/lib/jam_ruby/models/claimed_recording.rb index 0c07b81bd..6c6e9b19c 100644 --- a/lib/jam_ruby/models/claimed_recording.rb +++ b/lib/jam_ruby/models/claimed_recording.rb @@ -4,6 +4,7 @@ module JamRuby belongs_to :recording, :class_name => "JamRuby::Recording", :inverse_of => :claimed_recordings belongs_to :user, :class_name => "JamRuby::User", :inverse_of => :claimed_recordings belongs_to :genre, :class_name => "JamRuby::Genre" + has_many :recorded_tracks, :through => :recording, :class_name => "JamRuby::RecordedTrack" # user must own this object # params is a hash, and everything is optional @@ -25,7 +26,6 @@ module JamRuby end # If this is the only copy, destroy the entire recording. Otherwise, just destroy this claimed_recording - recording = self.recording if recording.claimed_recordings.count == 1 recording.discard else diff --git a/lib/jam_ruby/models/mix.rb b/lib/jam_ruby/models/mix.rb index 629a457b8..c1867b427 100644 --- a/lib/jam_ruby/models/mix.rb +++ b/lib/jam_ruby/models/mix.rb @@ -52,6 +52,10 @@ module JamRuby S3Manager.url(hashed_filename) end + def is_completed + !completed_at.nil? + end + private def delete_s3_files diff --git a/lib/jam_ruby/models/recorded_track.rb b/lib/jam_ruby/models/recorded_track.rb index df4876eef..c53d02f62 100644 --- a/lib/jam_ruby/models/recorded_track.rb +++ b/lib/jam_ruby/models/recorded_track.rb @@ -54,6 +54,11 @@ module JamRuby S3Manager.url(hashed_filename) end + def filename + hashed_filename + end + + # Format: "recording_#{recorded_track_id}" # File extension is irrelevant actually. def self.find_by_upload_filename(filename) @@ -62,6 +67,7 @@ module JamRuby RecordedTrack.find(matches[1]) end + private def delete_s3_files diff --git a/lib/jam_ruby/models/recording.rb b/lib/jam_ruby/models/recording.rb index 1ad79894e..d52c66e84 100644 --- a/lib/jam_ruby/models/recording.rb +++ b/lib/jam_ruby/models/recording.rb @@ -10,47 +10,8 @@ module JamRuby belongs_to :music_session, :class_name => "JamRuby::MusicSession", :inverse_of => :recording has_many :mixes, :class_name => "JamRuby::Mix", :inverse_of => :recording has_many :recorded_tracks, :class_name => "JamRuby::RecordedTrack", :foreign_key => :recording_id - - # favorites - has_many :favorites, :class_name => "JamRuby::UserFavorite", :foreign_key => "recording_id" - has_many :inverse_favorites, :through => :favorites, :source => :recording, :class_name => "JamRuby::Recording" - def favorite_count - return self.favorites.size - end - - # FIXME: This isn't right. - def self.user_recordings(user_id) - query = Recording - .joins( - %Q{ - INNER JOIN - recordings_users - ON - recordings_users.recording_id = recordings.id - } - ) - .joins( - %Q{ - LEFT OUTER JOIN - mixes - ON - recordings.id = mixes.recording_id - } - ) - .order( - %Q{ - recordings.created_at DESC - } - ) - .where( - %Q{ - recordings_users.user_id = '#{user_id}' - } - ) - query - end - + # Start recording a session. def self.start(music_session_id, owner) @@ -188,19 +149,19 @@ module JamRuby end # Returns the list of files this user should have synced to his computer, along with md5s and lengths - def self.file_list(user) - files = [] + def self.list(user) + downloads = [] # That second join is important. It's saying join off of recordings, NOT user. If you take out the # ":recordings =>" part, you'll just get the recorded_tracks that I played. Very different! User.joins(:recordings).joins(:recordings => :recorded_tracks) .order(%Q{ recordings.created_at DESC }) .where(%Q{ recorded_tracks.fully_uploaded = TRUE }) - .where(:id => user.id).each do |user| - user.recordings.each do |recording| + .where(:id => user.id).each do |theuser| + theuser.recordings.each do |recording| recording.recorded_tracks.each do |recorded_track| recorded_track = user.claimed_recordings.first.recording.recorded_tracks.first - files.push( + downloads.push( { :type => "recorded_track", :id => recorded_track.id, @@ -215,10 +176,10 @@ module JamRuby User.joins(:recordings).joins(:recordings => :mixes) .order(%Q{ recordings.created_at DESC }) - .where(%Q{ mixes.completed_at IS NOT NULL }).each do |user| - user.recordings.each do |recording| + .where(%Q{ mixes.completed_at IS NOT NULL }).each do |theuser| + theuser.recordings.each do |recording| recording.mixes.each do |mix| - files.push( + downloads.push( { :type => "mix", :id => mix.id, @@ -231,7 +192,20 @@ module JamRuby end end - files + + uploads = [] + RecordedTrack + .joins(:recording) + .where(:user_id => user.id) + .where(:fully_uploaded => false) + .where("duration IS NOT NULL").each do |recorded_track| + uploads.push(recorded_track.filename) + end + + { + "downloads" => downloads, + "uploads" => uploads + } end # Check to see if all files have been uploaded. If so, kick off a mix. diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 7ac8f8b32..cd47e50cd 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -64,10 +64,6 @@ module JamRuby has_many :inverse_b_followings, :through => :band_followings, :class_name => "JamRuby::BandFollowing", :foreign_key => "band_id" has_many :inverse_band_followings, :through => :inverse_band_followings, :source => :band, :class_name => "JamRuby::Band" - # favorites - 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" diff --git a/lib/jam_ruby/models/user_favorite.rb b/lib/jam_ruby/models/user_favorite.rb deleted file mode 100644 index 04053bb22..000000000 --- a/lib/jam_ruby/models/user_favorite.rb +++ /dev/null @@ -1,11 +0,0 @@ -module JamRuby - class UserFavorite < ActiveRecord::Base - - self.table_name = "users_favorites" - - self.primary_key = 'id' - - belongs_to :user, :class_name => "JamRuby::User", :foreign_key => "user_id", :inverse_of => :inverse_favorites - belongs_to :recording, :class_name => "JamRuby::Recording", :foreign_key => "recording_id" - end -end \ No newline at end of file diff --git a/spec/jam_ruby/models/recording_spec.rb b/spec/jam_ruby/models/recording_spec.rb index 6a9f6ebea..463c56d51 100644 --- a/spec/jam_ruby/models/recording_spec.rb +++ b/spec/jam_ruby/models/recording_spec.rb @@ -202,12 +202,16 @@ describe Recording do @recording.reload @genre = FactoryGirl.create(:genre) @recording.claim(@user, "Recording", @genre, true, true) - Recording.file_list(@user).length.should == 0 + Recording.list(@user)["downloads"].length.should == 0 + Recording.list(@user)["uploads"].length.should == 1 + file = Recording.list(@user)["uploads"].first @recorded_track = @recording.recorded_tracks.first + file.should == @recorded_track.filename @recorded_track.upload_start(25000, "md5hash") @recorded_track.upload_complete - Recording.file_list(@user).length.should == 1 - file = Recording.file_list(@user).first + Recording.list(@user)["downloads"].length.should == 1 + Recording.list(@user)["uploads"].length.should == 0 + file = Recording.list(@user)["downloads"].first file[:type].should == "recorded_track" file[:id].should == @recorded_track.id file[:length].should == 25000 @@ -219,8 +223,9 @@ describe Recording do @mix = Mix.next('server') @mix.should_not be_nil @mix.finish(50000, "md5hash") - Recording.file_list(@user).length.should == 2 - file = Recording.file_list(@user).last + Recording.list(@user)["downloads"].length.should == 2 + Recording.list(@user)["uploads"].length.should == 0 + file = Recording.list(@user)["downloads"].last file[:type].should == "mix" file[:id].should == @mix.id file[:length].should == 50000 From c1e91d87c9a4facdafe7e0651d49ca4163225ae6 Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Mon, 13 May 2013 22:22:09 -0700 Subject: [PATCH 211/274] eliminate favorites for now --- lib/jam_ruby/models/user.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index cd47e50cd..6b1a829f1 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -212,10 +212,6 @@ module JamRuby return self.band_followings.size end - def favorite_count - return self.favorites.size - end - def recording_count return self.recordings.size end From a98a04738d032ac1ac943bac0ac5e978e6b77070 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Tue, 14 May 2013 14:02:22 -0500 Subject: [PATCH 212/274] * VRFS-312 in-app password updates --- lib/jam_ruby/app/mailers/user_mailer.rb | 2 +- .../user_mailer/password_changed.html.erb | 12 +++-- .../user_mailer/password_changed.text.erb | 2 +- .../user_mailer/updated_email.text.erb | 2 +- lib/jam_ruby/models/user.rb | 52 +++++++++++++------ lib/jam_ruby/models/user_observer.rb | 2 + spec/jam_ruby/models/user_spec.rb | 30 ++++++++--- 7 files changed, 71 insertions(+), 31 deletions(-) diff --git a/lib/jam_ruby/app/mailers/user_mailer.rb b/lib/jam_ruby/app/mailers/user_mailer.rb index 3da683b70..b1ed5041b 100644 --- a/lib/jam_ruby/app/mailers/user_mailer.rb +++ b/lib/jam_ruby/app/mailers/user_mailer.rb @@ -16,7 +16,7 @@ module JamRuby default :from => DEFAULT_SENDER sendgrid_category :use_subject_lines - sendgrid_enable :opentrack, :clicktrack + #sendgrid_enable :opentrack, :clicktrack # this makes our emails creepy, imo (seth) sendgrid_unique_args :env => Environment.mode def welcome_message(user, signup_confirm_url) diff --git a/lib/jam_ruby/app/views/jam_ruby/user_mailer/password_changed.html.erb b/lib/jam_ruby/app/views/jam_ruby/user_mailer/password_changed.html.erb index f9a712566..e2fe554ae 100644 --- a/lib/jam_ruby/app/views/jam_ruby/user_mailer/password_changed.html.erb +++ b/lib/jam_ruby/app/views/jam_ruby/user_mailer/password_changed.html.erb @@ -1,5 +1,7 @@ - - -You Just Changed Your Password! <%= @user.email %> - - + +

Jamkazam Password Changed

+

You just changed your password!

+
+ + + diff --git a/lib/jam_ruby/app/views/jam_ruby/user_mailer/password_changed.text.erb b/lib/jam_ruby/app/views/jam_ruby/user_mailer/password_changed.text.erb index 125193f24..7dec8f658 100644 --- a/lib/jam_ruby/app/views/jam_ruby/user_mailer/password_changed.text.erb +++ b/lib/jam_ruby/app/views/jam_ruby/user_mailer/password_changed.text.erb @@ -1 +1 @@ -You Just Changed Your Password! <%= @user.email %> +You just changed your password! \ No newline at end of file diff --git a/lib/jam_ruby/app/views/jam_ruby/user_mailer/updated_email.text.erb b/lib/jam_ruby/app/views/jam_ruby/user_mailer/updated_email.text.erb index 808d8eb93..d85bfcca8 100644 --- a/lib/jam_ruby/app/views/jam_ruby/user_mailer/updated_email.text.erb +++ b/lib/jam_ruby/app/views/jam_ruby/user_mailer/updated_email.text.erb @@ -1 +1 @@ -do it \ No newline at end of file +<%= @user.email %> is confirmed! \ No newline at end of file diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 7ac8f8b32..053e8434d 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -6,11 +6,13 @@ module JamRuby #devise: for later: :trackable devise :database_authenticatable, - :recoverable, :rememberable, :validatable + :recoverable, :rememberable - attr_accessible :first_name, :last_name, :email, :password, :password_confirmation, :city, :state, :country, :subscribe_email, :terms_of_service - attr_accessor :updating_password, :updating_email, :updated_email, :update_email_confirmation_url, :administratively_created, :password_validation + attr_accessible :first_name, :last_name, :email, :city, :password, :password_confirmation, :state, :country, :subscribe_email, :terms_of_service + + # updating_password corresponds to a lost_password + attr_accessor :updating_password, :updating_email, :updated_email, :update_email_confirmation_url, :administratively_created, :current_password, :setting_password, :confirm_current_password # authorizations (for facebook, etc -- omniauth) has_many :user_authorizations, :class_name => "JamRuby::UserAuthorization" @@ -116,28 +118,29 @@ module JamRuby uniqueness: {case_sensitive: false} validates :update_email, presence: true, format: {with: VALID_EMAIL_REGEX}, uniqueness: {case_sensitive: false}, :if => :updating_email - validates_length_of :password, minimum: 6, maximum: 100, :if => :should_validate_password? + validates_length_of :password, minimum: 6, maximum: 100, :if => :should_validate_password? validates_presence_of :password_confirmation, :if => :should_validate_password? validates_confirmation_of :password, :if => :should_validate_password? + validates :terms_of_service, :acceptance => {:accept => true, :on => :create, :allow_nil => false } validates :subscribe_email, :inclusion => {:in => [nil, true, false]} validate :validate_musician_instruments - validate :check_password_valid - validate :check_update_email + validate :validate_current_password + validate :validate_update_email def validate_musician_instruments errors.add(:musician_instruments, ValidationMessages::INSTRUMENT_MINIMUM_NOT_MET) if !administratively_created && musician && musician_instruments.length == 0 errors.add(:musician_instruments, ValidationMessages::INSTRUMENT_LIMIT_EXCEEDED) if !administratively_created && musician && musician_instruments.length > 5 end - def check_password_valid + def validate_current_password # checks if the user put in their current password (used when changing your email, for instance) - errors.add(:password_validation, ValidationMessages::NOT_YOUR_PASSWORD) if should_confirm_existing_password? && !valid_password?(self.password_validation) + errors.add(:current_password, ValidationMessages::NOT_YOUR_PASSWORD) if should_confirm_existing_password? && !valid_password?(self.current_password) end - def check_update_email + def validate_update_email if updating_email && self.update_email == self.email errors.add(:update_email, ValidationMessages::EMAIL_MATCHES_CURRENT) elsif updating_email && User.find_by_email(self.update_email) != nil @@ -177,7 +180,7 @@ module JamRuby end def should_confirm_existing_password? - updating_email + confirm_current_password end def end_user_created? @@ -259,9 +262,26 @@ module JamRuby end def set_password(old_password, new_password, new_password_confirmation) - raise JamRuby::JamArgumentError unless valid_password? old_password - change_password(new_password, new_password_confirmation) - save + + # so that UserObserver knows to send a confirmation email on success + self.setting_password = true + # so that should_validate_password? fires + self.updating_password = true + + attributes = { :password => new_password, :password_confirmation => new_password_confirmation } + + # taken liberally from Devise::DatabaseAuthenticatable.update_with_password + + if valid_password?(old_password) + update_attributes(attributes) + else + self.assign_attributes(attributes) + self.valid? + self.errors.add(:current_password, current_password.blank? ? :blank : :invalid) + end + + #clean_up_passwords + end def self.set_password_from_token(email, token, new_password, new_password_confirmation) @@ -438,14 +458,14 @@ module JamRuby return user end - def begin_update_email(email, password_validation, confirmation_url) + def begin_update_email(email, current_password, confirmation_url) # sets the user model in a state such that it's expecting to have it's email updated # two columns matter for this; 'update_email_token' and 'update_email' # confirmation_link is odd in the sense that it can likely only come from www.jamkazam.com (jam-web) # an observer should be set up to send an email based on this activity - self.updating_email = true - self.password_validation = password_validation + self.updating_email = self.confirm_current_password = true + self.current_password = current_password self.update_email = email self.update_email_token = SecureRandom.urlsafe_base64 self.update_email_confirmation_url = "#{confirmation_url}#{self.update_email_token}" diff --git a/lib/jam_ruby/models/user_observer.rb b/lib/jam_ruby/models/user_observer.rb index c71989f49..5a031340a 100644 --- a/lib/jam_ruby/models/user_observer.rb +++ b/lib/jam_ruby/models/user_observer.rb @@ -8,6 +8,8 @@ module JamRuby UserMailer.updating_email(user).deliver elsif user.updated_email && !user.errors.any? UserMailer.updated_email(user).deliver + elsif user.setting_password && !user.errors.any? + UserMailer.password_changed(user).deliver end end end diff --git a/spec/jam_ruby/models/user_spec.rb b/spec/jam_ruby/models/user_spec.rb index aa8ea7d0e..1964e8961 100644 --- a/spec/jam_ruby/models/user_spec.rb +++ b/spec/jam_ruby/models/user_spec.rb @@ -130,25 +130,42 @@ describe User do describe "set_password" do before do @user.confirm_email! + @user.save.should be_true + UserMailer.deliveries.clear end it "setting a new password should work" do - @user.set_password("foobar", "newpassword", "newpassword") - User.authenticate(@user.email, "newpassword").should_not be_nil + @user.set_password("foobar", "newpassword", "newpassword") + User.authenticate(@user.email, "newpassword").should_not be_nil + UserMailer.deliveries.length.should == 1 end it "setting a new password should fail if old one doesnt match" do - expect { @user.set_password("wrongold", "newpassword", "newpassword") }.to raise_error + @user.set_password("wrongold", "newpassword", "newpassword") + @user.errors.any?.should be_true + @user.errors[:current_password].length.should == 1 + UserMailer.deliveries.length.should == 0 end it "setting a new password should fail if new ones dont match" do @user.set_password("foobar", "newpassword", "newpassword2") - User.authenticate(@user.email, "newpassword").should be_nil + @user.errors.any?.should be_true + @user.errors[:password].length.should == 1 + UserMailer.deliveries.length.should == 0 end it "setting a new password should fail if new one doesnt validate" do @user.set_password("foobar", "a", "a") - User.authenticate(@user.email, "newpassword").should be_nil + @user.errors.any?.should be_true + @user.errors[:password].length.should == 1 + UserMailer.deliveries.length.should == 0 + end + + it "setting a new password should fail if the new one is null" do + @user.set_password("foobar", nil, nil) + @user.errors.any?.should be_true + @user.errors[:password].length.should == 1 + UserMailer.deliveries.length.should == 0 end end @@ -268,7 +285,6 @@ describe User do end # useful to see contents of email without actually running the app and sending it - # it { puts UserMailer.deliveries[0].parts[0].body.decoded } it { @user.errors.any?.should be_false } it { @user.update_email.should == "somenewemail@blah.com" } it { @user.update_email_confirmation_url.should == "http://www.jamkazam.com/confirm_email_update?token=#{@user.update_email_token}" } @@ -285,7 +301,7 @@ describe User do it "bad password validation" do @user.begin_update_email("somenewemail@blah.com", "wrong password", "http://www.jamkazam.com/confirm_email_update?token=") - @user.errors[:password_validation][0].should == ValidationMessages::NOT_YOUR_PASSWORD + @user.errors[:current_password][0].should == ValidationMessages::NOT_YOUR_PASSWORD end it "matches current email" do From 0637c4c0880311dedd70f7a6a597623e1ab83178 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Tue, 14 May 2013 17:33:02 -0500 Subject: [PATCH 213/274] * typo in set_password fixed VRFS-312 --- lib/jam_ruby/models/user.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 806219e38..0fb2a6811 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -269,7 +269,7 @@ module JamRuby else self.assign_attributes(attributes) self.valid? - self.errors.add(:current_password, current_password.blank? ? :blank : :invalid) + self.errors.add(:current_password, old_password.blank? ? :blank : :invalid) end #clean_up_passwords From 4b3185c0c9d39690e420f2a765d8af6ff40195dd Mon Sep 17 00:00:00 2001 From: Seth Call Date: Thu, 16 May 2013 22:44:26 -0500 Subject: [PATCH 214/274] * adding max_mind model and csv import added (using postgres-copy gem) --- Gemfile | 1 + lib/jam_ruby.rb | 3 ++ lib/jam_ruby/models/max_mind_geo.rb | 38 +++++++++++++++ lib/jam_ruby/models/max_mind_isp.rb | 57 +++++++++++++++++++++++ spec/jam_ruby/models/max_mind_geo_spec.rb | 39 ++++++++++++++++ spec/jam_ruby/models/max_mind_isp_spec.rb | 43 +++++++++++++++++ 6 files changed, 181 insertions(+) create mode 100644 lib/jam_ruby/models/max_mind_geo.rb create mode 100644 lib/jam_ruby/models/max_mind_isp.rb create mode 100644 spec/jam_ruby/models/max_mind_geo_spec.rb create mode 100644 spec/jam_ruby/models/max_mind_isp_spec.rb diff --git a/Gemfile b/Gemfile index 7f01fde4b..e449a487d 100644 --- a/Gemfile +++ b/Gemfile @@ -24,6 +24,7 @@ gem 'aws-sdk', '1.8.0' gem 'carrierwave' gem 'aasm', '3.0.16' gem 'devise', '>= 1.1.2' +gem 'postgres-copy' if devenv gem 'jam_db', :path=> "#{workspace}/jam-db/target/ruby_package" gem 'jampb', :path => "#{workspace}/jam-pb/target/ruby/jampb" diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index 0f0a2a02b..12ef8a628 100755 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -10,6 +10,7 @@ require "will_paginate/active_record" require "action_mailer" require "devise" require "sendgrid" +require 'postgres-copy' require "jam_ruby/constants/limits" require "jam_ruby/constants/notification_types" require "jam_ruby/constants/validation_messages" @@ -28,6 +29,8 @@ require "jam_ruby/app/uploaders/artifact_uploader" require "jam_ruby/app/uploaders/perf_data_uploader" require "jam_ruby/lib/s3_manager" require "jam_ruby/message_factory" +require "jam_ruby/models/max_mind_geo" +require "jam_ruby/models/max_mind_isp" require "jam_ruby/models/genre" require "jam_ruby/models/user" require "jam_ruby/models/user_observer" diff --git a/lib/jam_ruby/models/max_mind_geo.rb b/lib/jam_ruby/models/max_mind_geo.rb new file mode 100644 index 000000000..0c8f2c082 --- /dev/null +++ b/lib/jam_ruby/models/max_mind_geo.rb @@ -0,0 +1,38 @@ +module JamRuby + class MaxMindGeo < ActiveRecord::Base + + self.table_name = 'max_mind_geo' + + + def self.import_from_max_mind(file) + # File Geo-124 + # Format: + # startIpNum,endIpNum,country,region,city,postalCode,latitude,longitude,dmaCode,areaCode + + MaxMindGeo.transaction do + MaxMindGeo.delete_all + File.open(file, 'r:ISO-8859-1') do |io| + MaxMindGeo.pg_copy_from io, :map => { 'startIpNum' => 'ip_bottom', 'endIpNum' => 'ip_top', 'country' => 'country', 'region' => 'region', 'city' => 'city'}, :columns => [:startIpNum, :endIpNum, :country, :region, :city] do |row| + row[0] = ip_address_to_int(row[0]) + row[1] = ip_address_to_int(row[1]) + row.delete_at(5) + row.delete_at(5) + row.delete_at(5) + row.delete_at(5) + row.delete_at(5) + end + end + end + end + + + # Make an IP address fit in a signed int. Just divide it by 2, as the least significant part + # just can't possibly matter. We can verify this if needed. My guess is the entire bottom octet is + # actually irrelevant + def self.ip_address_to_int(ip) + ip.split('.').inject(0) {|total,value| (total << 8 ) + value.to_i} / 2 + end + end + + +end \ No newline at end of file diff --git a/lib/jam_ruby/models/max_mind_isp.rb b/lib/jam_ruby/models/max_mind_isp.rb new file mode 100644 index 000000000..2c7d6ed9a --- /dev/null +++ b/lib/jam_ruby/models/max_mind_isp.rb @@ -0,0 +1,57 @@ +module JamRuby + class MaxMindIsp < ActiveRecord::Base + + self.table_name = 'max_mind_isp' + + def self.import_from_max_mind(file) + + # File Geo-142 + # Format: + # "beginIp","endIp","countryCode","ISP" + + MaxMindIsp.transaction do + MaxMindIsp.delete_all + File.open(file, 'r:ISO-8859-1') do |io| + io.gets # eat the copyright line. gah, why do they have that in their file?? + MaxMindIsp.pg_copy_from io, :map => { 'beginIp' => 'ip_bottom', 'endIp' => 'ip_top', 'countryCode' => 'country', 'ISP' => 'isp'}, :columns => [:beginIp, :endIp, :countryCode, :ISP] do |row| + row[0] = ip_address_to_int(strip_quotes(row[0])) + row[1] = ip_address_to_int(strip_quotes(row[1])) + row[2] = row[2] + row[3] = row[3..-1].join(',') # this is because the parser just cuts on any ',' and ignores double quotes. essentially postgres-copy isn't a great csv parser -- or I need to configure it better + while row.length > 4 + row.delete_at(4) + end + + end + end + end + end + + # Make an IP address fit in a signed int. Just divide it by 2, as the least significant part + # just can't possibly matter. We can verify this if needed. My guess is the entire bottom octet is + # actually irrelevant + def self.ip_address_to_int(ip) + ip.split('.').inject(0) {|total,value| (total << 8 ) + value.to_i} / 2 + end + + private + + def self.strip_quotes str + return nil if str.nil? + + if str.chr == '"' + str = str[1..-1] + end + + if str.rindex('"') == str.length - 1 + str = str.chop + end + + return str + end + + def self.escape str + str.gsub(/\"/, '""') + end + end +end \ No newline at end of file diff --git a/spec/jam_ruby/models/max_mind_geo_spec.rb b/spec/jam_ruby/models/max_mind_geo_spec.rb new file mode 100644 index 000000000..ba49462de --- /dev/null +++ b/spec/jam_ruby/models/max_mind_geo_spec.rb @@ -0,0 +1,39 @@ +require 'spec_helper' + +describe MaxMindGeo do + + include UsesTempFiles + + GEO_CSV='small.csv' + + in_directory_with_file(GEO_CSV) + + before do + + content_for_file('startIpNum,endIpNum,country,region,city,postalCode,latitude,longitude,dmaCode,areaCode +0.116.0.0,0.119.255.255,"AT","","","",47.3333,13.3333,, +1.0.0.0,1.0.0.255,"AU","","","",-27.0000,133.0000,, +1.0.1.0,1.0.1.255,"CN","07","Fuzhou","",26.0614,119.3061,,'.encode(Encoding::ISO_8859_1)) + + MaxMindGeo.import_from_max_mind(GEO_CSV) + end + + let(:first) { MaxMindGeo.find_by_ip_bottom(MaxMindGeo.ip_address_to_int('0.116.0.0')) } + let(:second) { MaxMindGeo.find_by_ip_bottom(MaxMindGeo.ip_address_to_int('1.0.0.0')) } + let(:third) { MaxMindGeo.find_by_ip_bottom(MaxMindGeo.ip_address_to_int('1.0.1.0')) } + + it { MaxMindGeo.count.should == 3 } + + it { first.country.should == 'AT' } + it { first.ip_bottom.should == MaxMindGeo.ip_address_to_int('0.116.0.0') } + it { first.ip_top.should == MaxMindGeo.ip_address_to_int('0.119.255.255') } + + it { second.country.should == 'AU' } + it { second.ip_bottom.should == MaxMindGeo.ip_address_to_int('1.0.0.0') } + it { second.ip_top.should == MaxMindGeo.ip_address_to_int('1.0.0.255') } + + it { third.country.should == 'CN' } + it { third.ip_bottom.should == MaxMindGeo.ip_address_to_int('1.0.1.0') } + it { third.ip_top.should == MaxMindGeo.ip_address_to_int('1.0.1.255') } +end + diff --git a/spec/jam_ruby/models/max_mind_isp_spec.rb b/spec/jam_ruby/models/max_mind_isp_spec.rb new file mode 100644 index 000000000..b61f86cfc --- /dev/null +++ b/spec/jam_ruby/models/max_mind_isp_spec.rb @@ -0,0 +1,43 @@ +require 'spec_helper' + +describe MaxMindIsp do + + include UsesTempFiles + + ISP_CSV='small.csv' + + in_directory_with_file(ISP_CSV) + + before do + + content_for_file('Copyright (c) 2011 MaxMind Inc. All Rights Reserved. +"beginIp","endIp","countryCode","ISP" +"1.0.0.0","1.0.0.255","AU","APNIC Debogon Project" +"1.0.1.0","1.0.1.255","CN","Chinanet Fujian Province Network" +"1.0.4.0","1.0.7.255","AU","Bigred,inc"'.encode(Encoding::ISO_8859_1)) + + MaxMindIsp.import_from_max_mind(ISP_CSV) + end + + let(:first) { MaxMindIsp.find_by_ip_bottom(MaxMindIsp.ip_address_to_int('1.0.0.0')) } + let(:second) { MaxMindIsp.find_by_ip_bottom(MaxMindIsp.ip_address_to_int('1.0.1.0')) } + let(:third) { MaxMindIsp.find_by_ip_bottom(MaxMindIsp.ip_address_to_int('1.0.4.0')) } + + it { MaxMindIsp.count.should == 3 } + + it { first.country.should == 'AU' } + it { first.ip_bottom.should == MaxMindIsp.ip_address_to_int('1.0.0.0') } + it { first.ip_top.should == MaxMindIsp.ip_address_to_int('1.0.0.255') } + it { first.isp.should == 'APNIC Debogon Project' } + + it { second.country.should == 'CN' } + it { second.ip_bottom.should == MaxMindIsp.ip_address_to_int('1.0.1.0') } + it { second.ip_top.should == MaxMindIsp.ip_address_to_int('1.0.1.255') } + it { second.isp.should == 'Chinanet Fujian Province Network' } + + it { third.country.should == 'AU' } + it { third.ip_bottom.should == MaxMindIsp.ip_address_to_int('1.0.4.0') } + it { third.ip_top.should == MaxMindIsp.ip_address_to_int('1.0.7.255') } + it { third.isp.should == 'Bigred,inc' } +end + From 137ed1b65ac2a6234fe3e9ad1c0a7e5382e4bba9 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Thu, 23 May 2013 08:41:27 -0500 Subject: [PATCH 215/274] * checking in little s3 helper --- lib/jam_ruby.rb | 1 + lib/jam_ruby/lib/s3_manager.rb | 4 ++++ lib/jam_ruby/lib/s3_util.rb | 21 +++++++++++++++++++++ spec/jam_ruby/lib/s3_util_spec.rb | 12 ++++++++++++ 4 files changed, 38 insertions(+) create mode 100644 lib/jam_ruby/lib/s3_util.rb create mode 100644 spec/jam_ruby/lib/s3_util_spec.rb diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index 12ef8a628..c0519fa9e 100755 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -27,6 +27,7 @@ require "jam_ruby/app/mailers/user_mailer" require "jam_ruby/app/mailers/invited_user_mailer" require "jam_ruby/app/uploaders/artifact_uploader" require "jam_ruby/app/uploaders/perf_data_uploader" +require "jam_ruby/lib/s3_util" require "jam_ruby/lib/s3_manager" require "jam_ruby/message_factory" require "jam_ruby/models/max_mind_geo" diff --git a/lib/jam_ruby/lib/s3_manager.rb b/lib/jam_ruby/lib/s3_manager.rb index edcd305ac..32bf04d03 100644 --- a/lib/jam_ruby/lib/s3_manager.rb +++ b/lib/jam_ruby/lib/s3_manager.rb @@ -1,3 +1,6 @@ +require 'aws-sdk' +require 'active_support/all' + module JamRuby class S3Manager @@ -64,5 +67,6 @@ module JamRuby Time.now.strftime("%a, %d %b %Y %H:%M:%S %z") end + end end diff --git a/lib/jam_ruby/lib/s3_util.rb b/lib/jam_ruby/lib/s3_util.rb new file mode 100644 index 000000000..38667df7e --- /dev/null +++ b/lib/jam_ruby/lib/s3_util.rb @@ -0,0 +1,21 @@ +require 'aws-sdk' +require 'active_support/all' + +module JamRuby + class S3Util + @@def_opts = { :expires => 3600 * 24, :secure => true } # 24 hours from now + @@s3 = AWS::S3.new(:access_key_id => ENV['AWS_KEY'], :secret_access_key => ENV['AWS_SECRET']) + + def self.sign_url(bucket, path, options = @@def_opts) + + bucket_gen = @@s3.buckets[bucket] + + return "#{bucket_gen.objects[path].url_for(:read, :expires => options[:expires]).to_s}" + end + + def self.url(aws_bucket, filename, options = @@def_opts) + "http#{options[:secure] ? "s" : ""}://s3.amazonaws.com/#{aws_bucket}/#{filename}" + end + end +end + diff --git a/spec/jam_ruby/lib/s3_util_spec.rb b/spec/jam_ruby/lib/s3_util_spec.rb new file mode 100644 index 000000000..7fb01e844 --- /dev/null +++ b/spec/jam_ruby/lib/s3_util_spec.rb @@ -0,0 +1,12 @@ +require 'spec_helper' + +describe S3Util do + + describe "sign_url" do + it "returns something" do + S3Util.sign_url("jamkazam-dev", "avatar-tmp/user/image.png").should_not be_nil + end + end + +end + From 87f678669d00ca25b7c73fb2aa527f53f7f2f440 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Thu, 30 May 2013 20:59:37 -0500 Subject: [PATCH 216/274] * avatar changes to user model --- .ruby-gemset | 1 + .ruby-version | 1 + .rvmrc | 1 - lib/jam_ruby/constants/validation_messages.rb | 1 + lib/jam_ruby/lib/s3_util.rb | 8 +++ lib/jam_ruby/models/user.rb | 66 +++++++++++++++++-- spec/jam_ruby/models/user_spec.rb | 37 +++++++++++ 7 files changed, 110 insertions(+), 5 deletions(-) create mode 100644 .ruby-gemset create mode 100644 .ruby-version delete mode 100644 .rvmrc diff --git a/.ruby-gemset b/.ruby-gemset new file mode 100644 index 000000000..90127ddb7 --- /dev/null +++ b/.ruby-gemset @@ -0,0 +1 @@ +jamruby diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 000000000..95a5ad2d5 --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +ruby-2.0.0-p195 diff --git a/.rvmrc b/.rvmrc deleted file mode 100644 index aeb79c125..000000000 --- a/.rvmrc +++ /dev/null @@ -1 +0,0 @@ -rvm use ruby-1.9.3-p327@jamruby --create diff --git a/lib/jam_ruby/constants/validation_messages.rb b/lib/jam_ruby/constants/validation_messages.rb index 2ecf986a7..4c41a3e8a 100644 --- a/lib/jam_ruby/constants/validation_messages.rb +++ b/lib/jam_ruby/constants/validation_messages.rb @@ -33,4 +33,5 @@ module ValidationMessages NOT_YOUR_PASSWORD = "is not your current password" EMAIL_ALREADY_TAKEN = "is already taken" EMAIL_MATCHES_CURRENT = "is same as your current email" + INVALID_FPFILE = "is not valid" end diff --git a/lib/jam_ruby/lib/s3_util.rb b/lib/jam_ruby/lib/s3_util.rb index 38667df7e..6415ee376 100644 --- a/lib/jam_ruby/lib/s3_util.rb +++ b/lib/jam_ruby/lib/s3_util.rb @@ -16,6 +16,14 @@ module JamRuby def self.url(aws_bucket, filename, options = @@def_opts) "http#{options[:secure] ? "s" : ""}://s3.amazonaws.com/#{aws_bucket}/#{filename}" end + + def self.move(aws_bucket, source, destination) + @@s3.buckets[aws_bucket].objects[source].move_to[destination] + end + + def self.delete(aws_bucket, path) + @@s3.buckets[aws_bucket].objects[path].delete() + end end end diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 0fb2a6811..c256d0d04 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -5,14 +5,14 @@ module JamRuby #devise: for later: :trackable - devise :database_authenticatable, + devise :database_authenticatable, :recoverable, :rememberable - attr_accessible :first_name, :last_name, :email, :city, :password, :password_confirmation, :state, :country, :subscribe_email, :terms_of_service + attr_accessible :first_name, :last_name, :email, :city, :password, :password_confirmation, :state, :country, :subscribe_email, :terms_of_service, :original_fpfile, :cropped_fpfile, :cropped_s3_path, :photo_url, :crop_selection # updating_password corresponds to a lost_password - attr_accessor :updating_password, :updating_email, :updated_email, :update_email_confirmation_url, :administratively_created, :current_password, :setting_password, :confirm_current_password + attr_accessor :updating_password, :updating_email, :updated_email, :update_email_confirmation_url, :administratively_created, :current_password, :setting_password, :confirm_current_password, :updating_avatar # authorizations (for facebook, etc -- omniauth) has_many :user_authorizations, :class_name => "JamRuby::UserAuthorization" @@ -106,6 +106,7 @@ module JamRuby before_save { |user| user.email = email.downcase } before_save :create_remember_token, :if => :should_validate_password? + before_save :stringify_avatar_info , :if => :updating_avatar validates :first_name, presence: true, length: {maximum: 50} validates :last_name, presence: true, length: {maximum: 50} @@ -126,6 +127,8 @@ module JamRuby validate :validate_current_password validate :validate_update_email + validate :validate_avatar_info + def validate_musician_instruments errors.add(:musician_instruments, ValidationMessages::INSTRUMENT_MINIMUM_NOT_MET) if !administratively_created && musician && musician_instruments.length == 0 errors.add(:musician_instruments, ValidationMessages::INSTRUMENT_LIMIT_EXCEEDED) if !administratively_created && musician && musician_instruments.length > 5 @@ -144,6 +147,14 @@ module JamRuby end end + def validate_avatar_info + if updating_avatar + # we want to mak sure that original_fpfile and cropped_fpfile seems like real fpfile info objects (i.e, json objects from filepicker.io) + errors.add(:original_fpfile, ValidationMessages::INVALID_FPFILE) if self.original_fpfile.nil? || self.original_fpfile["key"].nil? || self.original_fpfile["url"].nil? + errors.add(:cropped_fpfile, ValidationMessages::INVALID_FPFILE) if self.cropped_fpfile.nil? || self.cropped_fpfile["key"].nil? || self.cropped_fpfile["url"].nil? + end + end + def online @online ||= !self.connections.nil? && self.connections.size > 0 end @@ -738,7 +749,10 @@ module JamRuby user.musician_instruments << musician_instrument end - user.photo_url = photo_url + if photo_url.nil? + user.photo_url = photo_url + end + user.signup_token = nil user.save @@ -756,6 +770,40 @@ module JamRuby self.save end + def update_avatar(original_fpfile, cropped_fpfile, crop_selection, aws_bucket) + self.updating_avatar = true + + cropped_s3_path = cropped_fpfile["key"] + + return self.update_attributes( + :original_fpfile => original_fpfile, + :cropped_fpfile => cropped_fpfile, + :cropped_s3_path => cropped_s3_path, + :crop_selection => crop_selection, + :photo_url => S3Util.url(aws_bucket, cropped_s3_path, :secure => false) + ) + end + + def delete_avatar(aws_bucket) + + User.transaction do + + unless self.cropped_s3_path.nil? + S3Util.delete(aws_bucket, File.dirname(self.cropped_s3_path) + '/cropped.jpg') + S3Util.delete(aws_bucket, self.cropped_s3_path) + end + + return self.update_attributes( + :original_fpfile => nil, + :cropped_fpfile => nil, + :cropped_s3_path => nil, + :photo_url => nil, + :crop_selection => nil + ) + end + + end + # throws RecordNotFound if signup token is invalid; i.e., if it's nil, empty string, or not belonging to a user def self.signup_confirm(signup_token) if signup_token.nil? || signup_token.empty? @@ -817,5 +865,15 @@ module JamRuby def create_remember_token self.remember_token = SecureRandom.urlsafe_base64 end + + def stringify_avatar_info + # fpfile comes in as a hash, which is a easy-to-use and validate form. However, we store it as a VARCHAR, + # so we need t oconvert it to JSON before storing it (otherwise it gets serialized as a ruby object) + # later, when serving this data out to the REST API, we currently just leave it as a string and make a JSON capable + # client parse it, because it's very rare when it's needed at all + self.original_fpfile = original_fpfile.to_json if !original_fpfile.nil? + self.cropped_fpfile = cropped_fpfile.to_json if !cropped_fpfile.nil? + self.crop_selection = crop_selection.to_json if !crop_selection.nil? + end end end diff --git a/spec/jam_ruby/models/user_spec.rb b/spec/jam_ruby/models/user_spec.rb index 1964e8961..d79762e38 100644 --- a/spec/jam_ruby/models/user_spec.rb +++ b/spec/jam_ruby/models/user_spec.rb @@ -361,4 +361,41 @@ describe User do end end end + +=begin + describe "update avatar" do + + describe "success" do + + let(:s3_path) { "/public/avatars/#{@user.id}/avatar.jpg" } + let(:original) { { "url" => "http://filepicker.io/blah", "key" => "/public/avatars/#{@user.id}/originals/avatar.jpg" } } + let(:clipped) { { "url" => "http://filepicker.io/blah", "key" => s3_path } } + + before(:each) do + @user.update_avatar(original, clipped, "jamkazam") + end + + it { @user.errors.any?.should be_false } + it { @user.original_fpfile.class == String } + it { @user.cropped_fpfile.class == String } + it { @user.photo_url = S3Util.url("jamkazam", s3_path, :secure => false ) } + end + + describe "bad fpfiles" do + let(:s3_path) { "/public/avatars/#{@user.id}/avatar.jpg" } + let(:original) { { "url" => "http://filepicker.io/blah" } } # take out 'key', which is required by model + let(:clipped) { { "url" => "http://filepicker.io/blah", } } # take out 'key', which is required by model + + before(:each) do + @user.update_avatar(original, clipped, "jamkazam") + end + + it { @user.errors.any?.should be_true } + it { @user.errors[:original_fpfile][0].should == ValidationMessages::INVALID_FPFILE } + it { @user.errors[:cropped_fpfile][0].should == ValidationMessages::INVALID_FPFILE } + end + + end +=end + end From d2747aa2f8b610ec55d55785bfeab373df89a29c Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Sat, 8 Jun 2013 21:38:00 -0400 Subject: [PATCH 217/274] VRFS-263 include instruments in user search - may need index added on instrument_id column in musicians_instruments table --- lib/jam_ruby/models/user.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index c256d0d04..5f2b821a0 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -839,6 +839,9 @@ module JamRuby return [] end + # save query for use in instrument search + search_criteria = query + # create 'anded' statement query = Search.create_tsquery(query) @@ -846,7 +849,9 @@ module JamRuby return [] end - return User.where("email_confirmed = true and name_tsv @@ to_tsquery('jamenglish', ?)", query).limit(options[:limit]) + return query = User + .where("email_confirmed = true AND (name_tsv @@ to_tsquery('jamenglish', ?) OR users.id in (select user_id from musicians_instruments where instrument_id like '%#{search_criteria.downcase}%'))", query) + .limit(options[:limit]) end # devise compatibility From 8594d6820fa257ac2a2808861a4df7eeefed1d48 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Mon, 10 Jun 2013 20:25:42 -0500 Subject: [PATCH 218/274] * not allowing null description; not validating genres when not being updated; not validating terms on update --- lib/jam_ruby/models/music_session.rb | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/jam_ruby/models/music_session.rb b/lib/jam_ruby/models/music_session.rb index ec52acd62..b51e243d2 100644 --- a/lib/jam_ruby/models/music_session.rb +++ b/lib/jam_ruby/models/music_session.rb @@ -3,7 +3,7 @@ module JamRuby self.primary_key = 'id' - attr_accessor :legal_terms + attr_accessor :legal_terms, :skip_genre_validation attr_accessible :creator, :description, :musician_access, :approval_required, :fan_chat, :fan_access belongs_to :creator, :inverse_of => :music_sessions, :class_name => "JamRuby::User", :foreign_key => "user_id" @@ -23,11 +23,12 @@ module JamRuby after_save :require_at_least_one_genre, :limit_to_three_genres + validates :description, :presence => true validates :fan_chat, :inclusion => {:in => [true, false]} validates :fan_access, :inclusion => {:in => [true, false]} validates :approval_required, :inclusion => {:in => [true, false]} validates :musician_access, :inclusion => {:in => [true, false]} - validates :legal_terms, :inclusion => {:in => [true]} + validates :legal_terms, :inclusion => {:in => [true]}, :on => :create validates :creator, :presence => true validate :creator_is_musician @@ -171,14 +172,18 @@ module JamRuby private def require_at_least_one_genre - if genres.count < Limits::MIN_GENRES_PER_RECORDING - errors.add(:genres, ValidationMessages::GENRE_MINIMUM_NOT_MET) + unless skip_genre_validation + if self.genres.count < Limits::MIN_GENRES_PER_RECORDING + errors.add(:genres, ValidationMessages::GENRE_MINIMUM_NOT_MET) + end end end def limit_to_three_genres - if genres.count > Limits::MAX_GENRES_PER_RECORDING - errors.add(:genres, ValidationMessages::GENRE_LIMIT_EXCEEDED) + unless skip_genre_validation + if self.genres.count > Limits::MAX_GENRES_PER_RECORDING + errors.add(:genres, ValidationMessages::GENRE_LIMIT_EXCEEDED) + end end end end From 80b23f7efe01ed42159a58f694c3f16f4addd50f Mon Sep 17 00:00:00 2001 From: Seth Call Date: Mon, 10 Jun 2013 20:37:10 -0500 Subject: [PATCH 219/274] * allow update of genre --- lib/jam_ruby/models/music_session.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/jam_ruby/models/music_session.rb b/lib/jam_ruby/models/music_session.rb index b51e243d2..31dbe5a53 100644 --- a/lib/jam_ruby/models/music_session.rb +++ b/lib/jam_ruby/models/music_session.rb @@ -4,7 +4,7 @@ module JamRuby self.primary_key = 'id' attr_accessor :legal_terms, :skip_genre_validation - attr_accessible :creator, :description, :musician_access, :approval_required, :fan_chat, :fan_access + attr_accessible :creator, :description, :musician_access, :approval_required, :fan_chat, :fan_access, :genres belongs_to :creator, :inverse_of => :music_sessions, :class_name => "JamRuby::User", :foreign_key => "user_id" @@ -21,7 +21,7 @@ module JamRuby belongs_to :band, :inverse_of => :music_sessions, :class_name => "JamRuby::Band", :foreign_key => "band_id" - after_save :require_at_least_one_genre, :limit_to_three_genres + after_save :require_at_least_one_genre, :limit_max_genres validates :description, :presence => true validates :fan_chat, :inclusion => {:in => [true, false]} @@ -179,7 +179,7 @@ module JamRuby end end - def limit_to_three_genres + def limit_max_genres unless skip_genre_validation if self.genres.count > Limits::MAX_GENRES_PER_RECORDING errors.add(:genres, ValidationMessages::GENRE_LIMIT_EXCEEDED) From bd0b42533cf4054ef96df73be075af040d5b0ef2 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Fri, 21 Jun 2013 21:28:42 -0500 Subject: [PATCH 220/274] * VRFS-378: allow user to be logged in right after signup, also supporting user signing up as a fan --- lib/jam_ruby/models/user.rb | 41 ++++++++++++++++-------- spec/jam_ruby/models/user_search_spec.rb | 4 +-- 2 files changed, 29 insertions(+), 16 deletions(-) diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 5f2b821a0..f80325886 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -116,17 +116,21 @@ module JamRuby validates :update_email, presence: true, format: {with: VALID_EMAIL_REGEX}, uniqueness: {case_sensitive: false}, :if => :updating_email + validates_length_of :password, minimum: 6, maximum: 100, :if => :should_validate_password? validates_presence_of :password_confirmation, :if => :should_validate_password? validates_confirmation_of :password, :if => :should_validate_password? validates :terms_of_service, :acceptance => {:accept => true, :on => :create, :allow_nil => false } validates :subscribe_email, :inclusion => {:in => [nil, true, false]} + validates :musician, :inclusion => {:in => [true, false]} + validate :validate_musician_instruments validate :validate_current_password validate :validate_update_email + validate :validate_avatar_info def validate_musician_instruments @@ -597,7 +601,7 @@ module JamRuby # throws ActiveRecord::RecordNotFound if instrument is invalid # throws an email delivery error if unable to connect out to SMTP def self.signup(first_name, last_name, email, password, password_confirmation, terms_of_service, subscribe_email, - location, instruments, birth_date, photo_url, invited_user, signup_confirm_url) + location, instruments, birth_date, musician, photo_url, invited_user, signup_confirm_url) user = User.new UserManager.active_record_transaction do |user_manager| @@ -606,6 +610,7 @@ module JamRuby user.email = email user.subscribe_email = subscribe_email user.terms_of_service = terms_of_service + user.musician = musician # FIXME: Setting random password for social network logins. This # is because we have validations all over the place on this. @@ -621,21 +626,22 @@ module JamRuby end user.admin = false - user.musician = true user.city = location[:city] user.state = location[:state] user.country = location[:country] user.birth_date = birth_date - unless instruments.nil? - instruments.each do |musician_instrument_param| - instrument = Instrument.find(musician_instrument_param[:instrument_id]) - musician_instrument = MusicianInstrument.new - musician_instrument.user = user - musician_instrument.instrument = instrument - musician_instrument.proficiency_level = musician_instrument_param[:proficiency_level] - musician_instrument.priority = musician_instrument_param[:priority] - user.musician_instruments << musician_instrument + if user.musician # only update instruments if the user is a musician + unless instruments.nil? + instruments.each do |musician_instrument_param| + instrument = Instrument.find(musician_instrument_param[:instrument_id]) + musician_instrument = MusicianInstrument.new + musician_instrument.user = user + musician_instrument.instrument = instrument + musician_instrument.proficiency_level = musician_instrument_param[:proficiency_level] + musician_instrument.priority = musician_instrument_param[:priority] + user.musician_instruments << musician_instrument + end end end @@ -822,8 +828,12 @@ module JamRuby # if valid credentials are supplied for an 'active' user, returns the user # if not authenticated, returns nil def self.authenticate(email, password) + # remove email_confirmed restriction due to VRFS-378 + # we only allow users that have confirmed email to authenticate - user = User.where('email_confirmed=true').find_by_email(email) + # user = User.where('email_confirmed=true').find_by_email(email) + + user = User.find_by_email(email) if user && user.valid_password?(password) return user @@ -849,9 +859,12 @@ module JamRuby return [] end + # remove email_confirmed restriction due to VRFS-378 + # .where("email_confirmed = true AND (name_tsv @@ to_tsquery('jamenglish', ?) OR users.id in (select user_id from musicians_instruments where instrument_id like '%#{search_criteria.downcase}%'))", query) + return query = User - .where("email_confirmed = true AND (name_tsv @@ to_tsquery('jamenglish', ?) OR users.id in (select user_id from musicians_instruments where instrument_id like '%#{search_criteria.downcase}%'))", query) - .limit(options[:limit]) + .where("(name_tsv @@ to_tsquery('jamenglish', ?) OR users.id in (select user_id from musicians_instruments where instrument_id like '%#{search_criteria.downcase}%'))", query) + .limit(options[:limit]) end # devise compatibility diff --git a/spec/jam_ruby/models/user_search_spec.rb b/spec/jam_ruby/models/user_search_spec.rb index f77d452b0..486b955b2 100644 --- a/spec/jam_ruby/models/user_search_spec.rb +++ b/spec/jam_ruby/models/user_search_spec.rb @@ -61,12 +61,12 @@ describe User do user_result.id.should == @user2.id end - it "users who have signed up, but not confirmed should not show up in search index" do + it "users who have signed up, but not confirmed should show up in search index due to VRFS-378" do @user3 = FactoryGirl.create(:user, first_name: "unconfirmed", last_name: "unconfirmed", email: "unconfirmed@example.com", password: "foobar", password_confirmation: "foobar", musician: true, email_confirmed: false, city: "Apex", state: "NC", country: "USA") ws = User.search("unconfirmed") - ws.length.should == 0 + ws.length.should == 1 # Ok, confirm the user, and see them show up @user3.email_confirmed = true From 3c3cc85b53b241a6251da584475cd3973b95ac57 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Sat, 22 Jun 2013 19:35:42 -0400 Subject: [PATCH 221/274] VRFS-385 search fix --- lib/jam_ruby/models/friendship.rb | 2 +- lib/jam_ruby/models/search.rb | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/jam_ruby/models/friendship.rb b/lib/jam_ruby/models/friendship.rb index 2a72ca143..52132d889 100644 --- a/lib/jam_ruby/models/friendship.rb +++ b/lib/jam_ruby/models/friendship.rb @@ -36,7 +36,7 @@ module JamRuby def self.search(query, user_id, options = { :limit => 10 }) # only issue search if at least 2 characters are specified - if query.nil? || query.length < 2 + if query.nil? || query.length < 2 || user_id.nil? return [] end diff --git a/lib/jam_ruby/models/search.rb b/lib/jam_ruby/models/search.rb index 3cfaad1a7..c3c33c7e2 100644 --- a/lib/jam_ruby/models/search.rb +++ b/lib/jam_ruby/models/search.rb @@ -6,20 +6,23 @@ module JamRuby LIMIT = 10 # performs a site-white search - def self.search(query) + def self.search(query, user_id = nil) users = User.search(query, :limit => LIMIT) bands = Band.search(query, :limit => LIMIT) # NOTE: I removed recordings from search here. This is because we switched # to "claimed_recordings" so it's not clear what should be searched. - return Search.new(users + bands) + + friends = Friendship.search(query, user_id, :limit => LIMIT) + + return Search.new(users + bands + friends) end # performs a friend search scoped to a specific user - def self.search_by_user(query, user_id) - friends = Friendship.search(query, user_id, :limit => LIMIT) - return Search.new(friends) - end + # def self.search_by_user(query, user_id) + # friends = Friendship.search(query, user_id, :limit => LIMIT) + # return Search.new(friends) + # end # search_results - results from a Tire search across band/user/recording def initialize(search_results) From a78c8e0d636855b31a159e2e2ab1d52e40463c58 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Mon, 24 Jun 2013 16:31:40 -0500 Subject: [PATCH 222/274] * VRFS-300 and VRFS-386 done-all emails are styled using a common template --- Gemfile | 2 + .../app/mailers/invited_user_mailer.rb | 7 +- lib/jam_ruby/app/mailers/user_mailer.rb | 4 +- .../friend_invitation.html.erb | 36 ++---- .../friend_invitation.text.erb | 2 +- .../welcome_betauser.html.erb | 9 +- .../welcome_betauser.text.erb | 4 +- .../user_mailer/password_changed.html.erb | 8 +- .../user_mailer/password_reset.html.erb | 13 +- .../user_mailer/password_reset.text.erb | 5 +- .../user_mailer/updated_email.html.erb | 9 +- .../user_mailer/updated_email.text.erb | 2 +- .../user_mailer/updating_email.html.erb | 9 +- .../user_mailer/welcome_message.html.erb | 10 +- .../user_mailer/welcome_message.text.erb | 5 +- lib/jam_ruby/app/views/layouts/README.md | 15 +++ .../views/layouts/from_user_mailer.html.erb | 64 +++++++++ .../views/layouts/from_user_mailer.text.erb | 6 + .../app/views/layouts/user_mailer.html.erb | 11 +- lib/jam_ruby/models/invited_user.rb | 1 - lib/jam_ruby/models/user.rb | 15 ++- spec/jam_ruby/models/user_spec.rb | 10 +- spec/mailers/render_emails_spec.rb | 73 +++++++++++ spec/mailers/user_mailer_spec.rb | 121 ++++++++++++++---- spec/spec_helper.rb | 3 + 25 files changed, 332 insertions(+), 112 deletions(-) create mode 100644 lib/jam_ruby/app/views/layouts/README.md create mode 100644 lib/jam_ruby/app/views/layouts/from_user_mailer.html.erb create mode 100644 lib/jam_ruby/app/views/layouts/from_user_mailer.text.erb create mode 100644 spec/mailers/render_emails_spec.rb diff --git a/Gemfile b/Gemfile index e449a487d..6880c1a77 100644 --- a/Gemfile +++ b/Gemfile @@ -25,6 +25,8 @@ gem 'carrierwave' gem 'aasm', '3.0.16' gem 'devise', '>= 1.1.2' gem 'postgres-copy' +gem 'actionmailer_extensions' + if devenv gem 'jam_db', :path=> "#{workspace}/jam-db/target/ruby_package" gem 'jampb', :path => "#{workspace}/jam-pb/target/ruby/jampb" diff --git a/lib/jam_ruby/app/mailers/invited_user_mailer.rb b/lib/jam_ruby/app/mailers/invited_user_mailer.rb index 4f01d3743..4e3b3f43f 100644 --- a/lib/jam_ruby/app/mailers/invited_user_mailer.rb +++ b/lib/jam_ruby/app/mailers/invited_user_mailer.rb @@ -14,7 +14,7 @@ module JamRuby default :from => DEFAULT_SENDER sendgrid_category :use_subject_lines - sendgrid_enable :opentrack, :clicktrack + #sendgrid_enable :opentrack, :clicktrack # this makes our emails creepy, imo (seth) sendgrid_unique_args :env => Environment.mode # sent in the case of a general 'service invitation', from no one person in particular @@ -27,13 +27,12 @@ module JamRuby mail(:to => invited_user.email, :subject => "Welcome to the Jamkazam Beta release") do |format| format.text - format.html + format.html { render :layout => "user_mailer" } end end # used when sending an invitation from one user to another def friend_invitation(invited_user) - @signup_url = generate_signup_url(invited_user) @sender = invited_user.sender @note = invited_user.note @@ -43,7 +42,7 @@ module JamRuby mail(:to => invited_user.email, :subject => "You've been invited to Jamkazam by #{invited_user.sender.name}") do |format| format.text - format.html + format.html { render :layout => "from_user_mailer" } end end diff --git a/lib/jam_ruby/app/mailers/user_mailer.rb b/lib/jam_ruby/app/mailers/user_mailer.rb index b1ed5041b..5f2f312f5 100644 --- a/lib/jam_ruby/app/mailers/user_mailer.rb +++ b/lib/jam_ruby/app/mailers/user_mailer.rb @@ -40,8 +40,10 @@ module JamRuby end end - def password_reset(user) + def password_reset(user, password_reset_url) @user = user + # /reset_password_token?token=#{@user.reset_password_token}&email=#{CGI.escape(@user.email)}" + @password_reset_url = password_reset_url sendgrid_unique_args :type => "password_reset" mail(:to => user.email, :subject => "Jamkazam Password Reset") do |format| format.text diff --git a/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/friend_invitation.html.erb b/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/friend_invitation.html.erb index 13dbd5f39..142659257 100644 --- a/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/friend_invitation.html.erb +++ b/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/friend_invitation.html.erb @@ -1,28 +1,12 @@ - - -

You've been invited to Jamkazam by <%= @sender.name %>!

-<% unless @note.nil? %> -

<%= @sender.name %> says:

-

<%= @note %>

-<% end %> -

To signup, please go to the Create Account page.

- - +<% provide(:title, "You've been invited to Jamkazam by #{@sender.name}!") %> +<% provide(:photo_url, @sender.resolved_photo_url) %> +To signup, please go to the create account page. - - +<% content_for :note do %> + <% if @note %> + <%= @sender.name %> says: <%= @note %> + <% else %> + <%= @sender.name %> would like you to join Jamkazam. + <% end %> +<% end %> \ No newline at end of file diff --git a/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/friend_invitation.text.erb b/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/friend_invitation.text.erb index 59543c753..0b1d6ecbd 100644 --- a/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/friend_invitation.text.erb +++ b/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/friend_invitation.text.erb @@ -4,4 +4,4 @@ You've been invited to Jamkazam by <%= @sender.name %>! <%= @note %> <% end %> -To signup, please go to the Create Account page: <%= @signup_url %>. +To signup, please go to the 'create 'ccount' page: <%= @signup_url %>. diff --git a/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/welcome_betauser.html.erb b/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/welcome_betauser.html.erb index d3f3e544f..ae32900a6 100644 --- a/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/welcome_betauser.html.erb +++ b/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/welcome_betauser.html.erb @@ -1,6 +1,3 @@ - - -

Welcome to the Jamkazam beta release!

-

To signup, please go to the Create Account page.

- - +<% provide(:title, 'Welcome to the Jamkazam Beta!') %> + +To signup, please go to the create account page. \ No newline at end of file diff --git a/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/welcome_betauser.text.erb b/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/welcome_betauser.text.erb index 16806cad4..24702c702 100644 --- a/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/welcome_betauser.text.erb +++ b/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/welcome_betauser.text.erb @@ -1,2 +1,2 @@ -Welcome to the JamKazam beta release! -To signup, please go to the Create Account page: <%= @signup_url %>. +Welcome to the JamKazam Beta! +To signup, please go to the 'create account' page: <%= @signup_url %>. diff --git a/lib/jam_ruby/app/views/jam_ruby/user_mailer/password_changed.html.erb b/lib/jam_ruby/app/views/jam_ruby/user_mailer/password_changed.html.erb index e2fe554ae..a140d13fe 100644 --- a/lib/jam_ruby/app/views/jam_ruby/user_mailer/password_changed.html.erb +++ b/lib/jam_ruby/app/views/jam_ruby/user_mailer/password_changed.html.erb @@ -1,7 +1,3 @@ - -

Jamkazam Password Changed

-

You just changed your password!

-
- - +<% provide(:title, 'Jamkazam Password Changed') %> +You just changed your password at Jamkazam. diff --git a/lib/jam_ruby/app/views/jam_ruby/user_mailer/password_reset.html.erb b/lib/jam_ruby/app/views/jam_ruby/user_mailer/password_reset.html.erb index 3e5020c5c..06bd2dcb2 100644 --- a/lib/jam_ruby/app/views/jam_ruby/user_mailer/password_reset.html.erb +++ b/lib/jam_ruby/app/views/jam_ruby/user_mailer/password_reset.html.erb @@ -1,10 +1,3 @@ - - -Reset Password! <%= @user.email %> -
-Here is the token: <%= @user.reset_password_token %> -
-Here is the URL to visit where you can type in your new password: -<%= "/reset_password_token?token=#{@user.reset_password_token}&email=#{CGI.escape(@user.email)}" %> - - +<% provide(:title, 'Jamkazam Password Reset') %> + +Visit this link so that you can change your Jamkazam password: reset password. \ No newline at end of file diff --git a/lib/jam_ruby/app/views/jam_ruby/user_mailer/password_reset.text.erb b/lib/jam_ruby/app/views/jam_ruby/user_mailer/password_reset.text.erb index 7039fe433..f0f0e7502 100644 --- a/lib/jam_ruby/app/views/jam_ruby/user_mailer/password_reset.text.erb +++ b/lib/jam_ruby/app/views/jam_ruby/user_mailer/password_reset.text.erb @@ -1,4 +1 @@ -Reset Password! <%= @user.email %> -Here is the token: <%= @user.reset_password_token %> -Here is the URL to visit where you can type in your new password: -<%= "/reset_password_token?token=#{@user.reset_password_token}&email=#{CGI.escape(@user.email)}" %> +Visit this link so that you can change your Jamkazam password: Reset Password. \ No newline at end of file diff --git a/lib/jam_ruby/app/views/jam_ruby/user_mailer/updated_email.html.erb b/lib/jam_ruby/app/views/jam_ruby/user_mailer/updated_email.html.erb index 27f2c09fa..4ca9c6e5d 100644 --- a/lib/jam_ruby/app/views/jam_ruby/user_mailer/updated_email.html.erb +++ b/lib/jam_ruby/app/views/jam_ruby/user_mailer/updated_email.html.erb @@ -1,6 +1,3 @@ - -

Jamkazam Email Confirmed

-

<%= @user.email %> is confirmed!

-
- - \ No newline at end of file +<% provide(:title, 'Jamkazam Email Confirmed') %> + +<%= @user.email %> has been confirmed as your new email address. \ No newline at end of file diff --git a/lib/jam_ruby/app/views/jam_ruby/user_mailer/updated_email.text.erb b/lib/jam_ruby/app/views/jam_ruby/user_mailer/updated_email.text.erb index d85bfcca8..dae13d028 100644 --- a/lib/jam_ruby/app/views/jam_ruby/user_mailer/updated_email.text.erb +++ b/lib/jam_ruby/app/views/jam_ruby/user_mailer/updated_email.text.erb @@ -1 +1 @@ -<%= @user.email %> is confirmed! \ No newline at end of file +<%= @user.email %> has been confirmed as your new email address. \ No newline at end of file diff --git a/lib/jam_ruby/app/views/jam_ruby/user_mailer/updating_email.html.erb b/lib/jam_ruby/app/views/jam_ruby/user_mailer/updating_email.html.erb index 295a8de9d..c6ecd5a1f 100644 --- a/lib/jam_ruby/app/views/jam_ruby/user_mailer/updating_email.html.erb +++ b/lib/jam_ruby/app/views/jam_ruby/user_mailer/updating_email.html.erb @@ -1,6 +1,3 @@ - -

Please Confirm New Jamkazam Email

-

Please click the following link to confirm your change in email: confirm email.

-
- - \ No newline at end of file +<% provide(:title, 'Please Confirm New Jamkazam Email') %> + +Please click the following link to confirm your change in email: confirm email. \ No newline at end of file diff --git a/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.html.erb b/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.html.erb index 351b4186f..922fb456a 100644 --- a/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.html.erb +++ b/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.html.erb @@ -1,6 +1,4 @@ - - -

Welcome! <%= @user.first_name %>.

-

To confirm your registration, please go to the signup confirmation page..

- - \ No newline at end of file +<% provide(:title, 'Welcome to Jamkazam') %> + +

Welcome <%= @user.first_name %> to Jamkazam!

+

To confirm this email address, please go to the signup confirmation page..

diff --git a/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.text.erb b/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.text.erb index d8601050b..7befb32f3 100644 --- a/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.text.erb +++ b/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.text.erb @@ -1,2 +1,3 @@ -Welcome! <%= @user.first_name %> -To confirm your registration, please go to the signup confirmation page at : <%= @signup_confirm_url %>. \ No newline at end of file +Welcome <%= @user.first_name %> to Jamkazam! + +To confirm this email address, please go to the signup confirmation page at: <%= @signup_confirm_url %>. \ No newline at end of file diff --git a/lib/jam_ruby/app/views/layouts/README.md b/lib/jam_ruby/app/views/layouts/README.md new file mode 100644 index 000000000..f24c4abc7 --- /dev/null +++ b/lib/jam_ruby/app/views/layouts/README.md @@ -0,0 +1,15 @@ +Understanding Jamkazam Email Layouts +==================================== + +We have two types of layouts; +* a 'standard' email layout which is an email to a user (user_mailer.html.erb/text) +* a 'from a user to another user' email layout (from_user_mailer.html.erb/text) + +## user_mailer.html.erb +To use the user_mailer.html.erb layout, you must provide a title section, as well as the body. +Look at 'password_changed.html.erb' for an example. + +## from_user_mailer.html.erb +To use the from_user_mailer.html.erb layout, you must provide a title section, photo_url section (photo of the person who sent the email), and a note section (any personalized note that the sender may have said, or boilerplate) +Look at 'friend_invitation.html.erb' for an example. + diff --git a/lib/jam_ruby/app/views/layouts/from_user_mailer.html.erb b/lib/jam_ruby/app/views/layouts/from_user_mailer.html.erb new file mode 100644 index 000000000..9c7ba243c --- /dev/null +++ b/lib/jam_ruby/app/views/layouts/from_user_mailer.html.erb @@ -0,0 +1,64 @@ + + + + + JamKazam + + + + + + + + + + +
JamKazam
+ + + + + + + + + + + +

<%= yield(:title) %>

+

<%= yield %>

+
+ + + + +

<%= yield(:note) %>

+
+ + +
+ + +
+ + +

+

This email was sent to you because you have an account at Jamkazam. +

+ +
+ + + + +
Copyright © 2013 JamKazam, Inc. All rights reserved. +
+ + + diff --git a/lib/jam_ruby/app/views/layouts/from_user_mailer.text.erb b/lib/jam_ruby/app/views/layouts/from_user_mailer.text.erb new file mode 100644 index 000000000..bc0f816eb --- /dev/null +++ b/lib/jam_ruby/app/views/layouts/from_user_mailer.text.erb @@ -0,0 +1,6 @@ +<%= yield %> + + +This email was sent to you because you have an account at Jamkazam / http://www.jamkazam.com. + +Copyright 2013 JamKazam, Inc. All rights reserved. \ No newline at end of file diff --git a/lib/jam_ruby/app/views/layouts/user_mailer.html.erb b/lib/jam_ruby/app/views/layouts/user_mailer.html.erb index e21294426..c306d8653 100644 --- a/lib/jam_ruby/app/views/layouts/user_mailer.html.erb +++ b/lib/jam_ruby/app/views/layouts/user_mailer.html.erb @@ -22,7 +22,14 @@ - <%= yield %> + + + + +

<%= yield(:title) %>

+

<%= yield %>

+
+
@@ -30,7 +37,7 @@
- +

This email was sent to you because you have an account at Jamkazam.

diff --git a/lib/jam_ruby/models/invited_user.rb b/lib/jam_ruby/models/invited_user.rb index 69711a941..456644491 100644 --- a/lib/jam_ruby/models/invited_user.rb +++ b/lib/jam_ruby/models/invited_user.rb @@ -44,7 +44,6 @@ module JamRuby self.accepted = true end - def invited_by_administrator? sender.nil? || sender.admin # a nil sender can only be created by someone using jam-admin end diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index f80325886..d580c544c 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -258,6 +258,17 @@ module JamRuby return MusicSessionUserHistory.where("music_session_id='#{session_id}'") end + # always returns a non-null value for photo-url, + # using the generic avatar if no user photo available + def resolved_photo_url + if self.photo_url == nil || self.photo_url == '' + # lame that this isn't environment, but boy this is hard to pass all the way down from jam-web! + "http://www.jamkazam.com/assets/shared/avatar_generic.png" + else + return self.photo_url + end + end + def to_s return email unless email.nil? @@ -312,7 +323,7 @@ module JamRuby UserMailer.password_changed(self).deliver end - def self.reset_password(email) + def self.reset_password(email, reset_password_url) user = User.find_by_email(email) raise JamRuby::JamArgumentError if user.nil? @@ -320,7 +331,7 @@ module JamRuby user.reset_password_token_created = Time.now user.save - UserMailer.password_reset(user).deliver + UserMailer.password_reset(user, reset_password_url).deliver user end diff --git a/spec/jam_ruby/models/user_spec.rb b/spec/jam_ruby/models/user_spec.rb index d79762e38..d6ea57ae4 100644 --- a/spec/jam_ruby/models/user_spec.rb +++ b/spec/jam_ruby/models/user_spec.rb @@ -1,5 +1,7 @@ require 'spec_helper' +RESET_PASSWORD_URL = "/reset_token" + describe User do before do @@ -177,11 +179,11 @@ describe User do end it "fails if the provided email address is unrecognized" do - expect { User.reset_password("invalidemail@invalid.com") }.to raise_error + expect { User.reset_password("invalidemail@invalid.com", RESET_PASSWORD_URL) }.to raise_error end it "assigns a reset_token and reset_token_created on reset" do - User.reset_password(@user.email) + User.reset_password(@user.email, RESET_PASSWORD_URL) @user.reload @user.reset_password_token.should_not be_nil @user.reset_password_token_created.should_not be_nil @@ -190,13 +192,13 @@ describe User do end it "errors if the wrong token is comes in" do - User.reset_password(@user.email) + User.reset_password(@user.email, RESET_PASSWORD_URL) @user.reload expect { User.set_password_from_token(@user.email, "wrongtoken", "newpassword", "newpassword") }.to raise_error end it "changes the password if the token is right" do - User.reset_password(@user.email) + User.reset_password(@user.email, RESET_PASSWORD_URL) @user.reload User.set_password_from_token(@user.email, @user.reset_password_token, "newpassword", "newpassword") User.authenticate(@user.email, "newpassword").should_not be_nil diff --git a/spec/mailers/render_emails_spec.rb b/spec/mailers/render_emails_spec.rb new file mode 100644 index 000000000..120a660f5 --- /dev/null +++ b/spec/mailers/render_emails_spec.rb @@ -0,0 +1,73 @@ +# The purpose of this 'test' is to render emails to disk, +# So that a developer can look in tmp/emails and open them up and verify that they look OK +# Also, to have Jenkins archive them to make it easier to check if a build look OK + +require "spec_helper" + +describe "RenderMailers", :slow => true do + + let(:user) { FactoryGirl.create(:user) } + + before(:each) do + @filename = nil # set this on your test to pin the filename; i just make it the name of the mailer method responsible for sending the mail + end + + describe "UserMailer emails" do + + before(:each) do + user.update_email = "my_new_email@jamkazam.com" + UserMailer.deliveries.clear + end + + after(:each) do + UserMailer.deliveries.length.should == 1 + mail = UserMailer.deliveries[0] + save_emails_to_disk(mail, @filename) + end + + it { @filename="welcome_message"; UserMailer.welcome_message(user, "/signup").deliver } + it { @filename="password_reset"; UserMailer.password_reset(user, '/reset_password').deliver } + it { @filename="password_changed"; UserMailer.password_changed(user).deliver } + it { @filename="updated_email"; UserMailer.updated_email(user).deliver } + it { @filename="updating_email"; UserMailer.updating_email(user).deliver } + end + + describe "InvitedUserMailer emails" do + + let(:user2) { FactoryGirl.create(:user) } + let(:invited_user) { FactoryGirl.create(:invited_user, :sender => user2) } + let(:admin_invited_user) { FactoryGirl.create(:invited_user) } + + before(:each) do + InvitedUserMailer.deliveries.clear + end + + after(:each) do + UserMailer.deliveries.length.should == 2 + # NOTE! we take the second email, because the act of creating the InvitedUser model + # sends an email too, before our it {} block runs. This is because we have an InvitedUserObserver + mail = InvitedUserMailer.deliveries[1] + save_emails_to_disk(mail, @filename) + end + + it { @filename="friend_invitation"; InvitedUserMailer.deliveries.clear; InvitedUserMailer.friend_invitation(invited_user).deliver } + it { @filename="welcome_betauser"; InvitedUserMailer.welcome_betauser(admin_invited_user).deliver } + end + +end + +def save_emails_to_disk(mail, filename) + # taken from: https://github.com/originalpete/actionmailer_extensions/blob/master/lib/actionmailer_extensions.rb + # this extension does not work with ActionMailer 3.x, but this method is all we need + + if filename.nil? + filename = mail.subject + end + + email_output_dir = 'tmp/emails' + FileUtils.mkdir_p(email_output_dir) unless File.directory?(email_output_dir) + filename = "#{filename}.eml" + File.open(File.join(email_output_dir, filename), "w+") {|f| + f << mail.encoded + } +end \ No newline at end of file diff --git a/spec/mailers/user_mailer_spec.rb b/spec/mailers/user_mailer_spec.rb index cdd91b65c..68d7da550 100644 --- a/spec/mailers/user_mailer_spec.rb +++ b/spec/mailers/user_mailer_spec.rb @@ -1,5 +1,12 @@ require "spec_helper" +########################################################### +# We test just the mailer templating here. # +# In other words, make sure there are no glaring oopsies, # +# such as broken templates, or sending to wrong address # +########################################################### + + describe UserMailer do let(:user) { FactoryGirl.create(:user) } @@ -8,37 +15,107 @@ describe UserMailer do UserMailer.deliveries.clear end - it "should send welcome email" do - signup_confirmation_url = "http://example.com/confirm" - signup_confirmation_url_with_token = "#{signup_confirmation_url}/#{user.signup_token}" - UserMailer.welcome_message(user, signup_confirmation_url_with_token).deliver - UserMailer.deliveries.length.should == 1 - mail = UserMailer.deliveries[0] - mail['from'].to_s.should == UserMailer::DEFAULT_SENDER - mail['to'].to_s.should == user.email - mail.multipart?.should == true # because we send plain + html + describe "should send welcome email" do + + let (:mail) { UserMailer.deliveries[0] } + let (:signup_confirmation_url) { "http://example.com/confirm" } + let (:signup_confirmation_url_with_token ) { "#{signup_confirmation_url}/#{user.signup_token}" } + + before(:each) do + UserMailer.welcome_message(user, signup_confirmation_url_with_token).deliver + end + + + it { UserMailer.deliveries.length.should == 1 } + it { mail['from'].to_s.should == UserMailer::DEFAULT_SENDER } + it { mail['to'].to_s.should == user.email } + it { mail.multipart?.should == true } # because we send plain + html # verify that the messages are correctly configured - mail.html_part.body.include?("Welcome").should be_true - mail.html_part.body.include?(signup_confirmation_url_with_token).should be_true - mail.text_part.body.include?("Welcome").should be_true - mail.text_part.body.include?(signup_confirmation_url_with_token).should be_true + it { mail.html_part.body.include?("Welcome").should be_true } + it { mail.html_part.body.include?(signup_confirmation_url_with_token).should be_true } + it { mail.text_part.body.include?("Welcome").should be_true } + it { mail.text_part.body.include?(signup_confirmation_url_with_token).should be_true } end - it "should send reset password" do - UserMailer.password_reset(user).deliver + describe "should send reset password" do - UserMailer.deliveries.length.should == 1 - mail = UserMailer.deliveries[0] + let(:mail) { UserMailer.deliveries[0] } + before(:each) do + UserMailer.password_reset(user, '/reset_password').deliver + end - mail['from'].to_s.should == UserMailer::DEFAULT_SENDER - mail['to'].to_s.should == user.email - mail.multipart?.should == true # because we send plain + html + + it { UserMailer.deliveries.length.should == 1 } + + it { mail['from'].to_s.should == UserMailer::DEFAULT_SENDER } + it { mail['to'].to_s.should == user.email } + it { mail.multipart?.should == true } # because we send plain + html # verify that the messages are correctly configured - mail.html_part.body.include?("Reset").should be_true - mail.text_part.body.include?("Reset").should be_true + it { mail.html_part.body.include?("Reset").should be_true } + it { mail.text_part.body.include?("Reset").should be_true } end + + describe "should send change password confirmation" do + + let(:mail) { UserMailer.deliveries[0] } + + before(:each) do + UserMailer.password_changed(user).deliver + end + + it { UserMailer.deliveries.length.should == 1 } + + it { mail['from'].to_s.should == UserMailer::DEFAULT_SENDER } + it { mail['to'].to_s.should == user.email } + it { mail.multipart?.should == true } # because we send plain + html + + # verify that the messages are correctly configured + it { mail.html_part.body.include?("changed your password").should be_true } + it { mail.text_part.body.include?("changed your password").should be_true } + end + + describe "should send update email confirmation" do + + let(:mail) { UserMailer.deliveries[0] } + + before(:each) do + UserMailer.updated_email(user).deliver + end + + it { UserMailer.deliveries.length.should == 1 } + + it { mail['from'].to_s.should == UserMailer::DEFAULT_SENDER } + it { mail['to'].to_s.should == user.email } + it { mail.multipart?.should == true } # because we send plain + html + + # verify that the messages are correctly configured + it { mail.html_part.body.include?("#{user.email} has been confirmed as your new email address.").should be_true } + it { mail.text_part.body.include?("#{user.email} has been confirmed as your new email address.").should be_true } + end + + describe "should send updating email" do + + let(:mail) { UserMailer.deliveries[0] } + + before(:each) do + user.update_email = "my_new_email@jamkazam.com" + UserMailer.updating_email(user).deliver + end + + it { UserMailer.deliveries.length.should == 1 } + + it { mail['from'].to_s.should == UserMailer::DEFAULT_SENDER } + it { mail['to'].to_s.should == user.update_email } + it { mail.multipart?.should == true } # because we send plain + html + + # verify that the messages are correctly configured + it { mail.html_part.body.include?("to confirm your change in email").should be_true } + it { mail.text_part.body.include?("to confirm your change in email").should be_true } + end + + end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index b1686d708..33261cdbe 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -56,6 +56,9 @@ Spork.prefork do config.run_all_when_everything_filtered = true config.filter_run :focus + # you can mark a test as slow so that developers won't commonly hit it, but build server will http://blog.davidchelimsky.net/2010/06/14/filtering-examples-in-rspec-2/ + config.filter_run_excluding slow: true unless ENV['RUN_SLOW_TESTS'] + config.before(:suite) do DatabaseCleaner.strategy = :truncation, {:except => %w[instruments genres] } DatabaseCleaner.clean_with(:truncation, {:except => %w[instruments genres] }) From b3e1b947649aaed3ead274f9133824d53ecc87b3 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Mon, 24 Jun 2013 16:38:31 -0500 Subject: [PATCH 223/274] * updating how slow tests are ran --- spec/spec_helper.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 33261cdbe..e8c095186 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -57,7 +57,7 @@ Spork.prefork do config.filter_run :focus # you can mark a test as slow so that developers won't commonly hit it, but build server will http://blog.davidchelimsky.net/2010/06/14/filtering-examples-in-rspec-2/ - config.filter_run_excluding slow: true unless ENV['RUN_SLOW_TESTS'] + config.filter_run_excluding slow: true unless ENV['RUN_SLOW_TESTS'] == "1" config.before(:suite) do DatabaseCleaner.strategy = :truncation, {:except => %w[instruments genres] } @@ -88,4 +88,4 @@ end Spork.each_run do # This code will be run each time you run your specs. -end \ No newline at end of file +end From 4a42cc2f33175fbb3c8027bd4219493c3732e443 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Mon, 24 Jun 2013 16:58:45 -0500 Subject: [PATCH 224/274] * removing dependency of actionmailer_extensios --- Gemfile | 1 - 1 file changed, 1 deletion(-) diff --git a/Gemfile b/Gemfile index 6880c1a77..4ba0e02ee 100644 --- a/Gemfile +++ b/Gemfile @@ -25,7 +25,6 @@ gem 'carrierwave' gem 'aasm', '3.0.16' gem 'devise', '>= 1.1.2' gem 'postgres-copy' -gem 'actionmailer_extensions' if devenv gem 'jam_db', :path=> "#{workspace}/jam-db/target/ruby_package" From bf6ed0f21c5acb7cbd3b67161594029251524493 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Mon, 24 Jun 2013 17:02:37 -0500 Subject: [PATCH 225/274] * removing HTTPS from rubygems because it supposedly helps things go faster --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 4ba0e02ee..da4822770 100644 --- a/Gemfile +++ b/Gemfile @@ -1,5 +1,5 @@ #ruby=1.9.3 -source 'https://rubygems.org' +source 'http://rubygems.org' unless ENV["LOCAL_DEV"] == "1" source 'https://jamjam:blueberryjam@www.jamkazam.com/gems/' end From c261fe4929e998a06a02d3141540439491069231 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Mon, 24 Jun 2013 20:17:36 -0500 Subject: [PATCH 226/274] * adding verbose flag to busndle update to see why it's taking hours on build server --- build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build b/build index df9ac25e3..c58cb31bc 100755 --- a/build +++ b/build @@ -2,7 +2,7 @@ echo "updating dependencies" bundle install --path vendor/bundle --local -bundle update +bundle update --verbose echo "running rspec tests" bundle exec rspec From b30e9d9006231db9dadb052bee02675079e1bdf0 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Mon, 24 Jun 2013 20:31:45 -0500 Subject: [PATCH 227/274] * pinning rails version to see if this fixes jam-ruby --- Gemfile | 3 +-- build | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index da4822770..f4caeba5f 100644 --- a/Gemfile +++ b/Gemfile @@ -11,7 +11,7 @@ devenv = ENV["BUILD_NUMBER"].nil? # Jenkins sets a build number environment vari gem 'pg', '0.14.0', :platform => [:mri, :mswin, :mingw] gem 'jdbc_postgres', :platform => [:jruby] -gem 'activerecord', '>=3.2.11' +gem 'activerecord', '3.2.13' gem 'uuidtools', '2.1.2' gem 'bcrypt-ruby', '3.0.1' gem 'ruby-protocol-buffers', '1.2.2' @@ -39,7 +39,6 @@ group :test do gem "rspec", "2.10.0" gem 'spork', '0.9.0' gem 'database_cleaner', '0.7.0' - gem 'pry' end # Specify your gem's dependencies in jam_ruby.gemspec diff --git a/build b/build index c58cb31bc..df9ac25e3 100755 --- a/build +++ b/build @@ -2,7 +2,7 @@ echo "updating dependencies" bundle install --path vendor/bundle --local -bundle update --verbose +bundle update echo "running rspec tests" bundle exec rspec From 9644e13812dd3accbd0e382612dbf8bc566e56c1 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Mon, 24 Jun 2013 20:33:56 -0500 Subject: [PATCH 228/274] * pinning eventmachine to see if that helps --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index f4caeba5f..59e868d96 100644 --- a/Gemfile +++ b/Gemfile @@ -15,7 +15,7 @@ gem 'activerecord', '3.2.13' gem 'uuidtools', '2.1.2' gem 'bcrypt-ruby', '3.0.1' gem 'ruby-protocol-buffers', '1.2.2' -gem 'eventmachine' +gem 'eventmachine', '1.0.3' gem 'amqp' gem 'will_paginate' gem 'actionmailer' From aa5f8d87d20a3f6a6e3f171fdf1940a2dc3a8c48 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Mon, 24 Jun 2013 20:35:40 -0500 Subject: [PATCH 229/274] * still pinning versions; actionmailer this time --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 59e868d96..a895dff45 100644 --- a/Gemfile +++ b/Gemfile @@ -18,7 +18,7 @@ gem 'ruby-protocol-buffers', '1.2.2' gem 'eventmachine', '1.0.3' gem 'amqp' gem 'will_paginate' -gem 'actionmailer' +gem 'actionmailer', '3.2.13' gem 'sendgrid' gem 'aws-sdk', '1.8.0' gem 'carrierwave' From 78b4b289205e02b64590d09ccd98dc8b18f3c53d Mon Sep 17 00:00:00 2001 From: Seth Call Date: Mon, 24 Jun 2013 20:44:16 -0500 Subject: [PATCH 230/274] * amqp gem pinned --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index a895dff45..9b6a75cb1 100644 --- a/Gemfile +++ b/Gemfile @@ -16,7 +16,7 @@ gem 'uuidtools', '2.1.2' gem 'bcrypt-ruby', '3.0.1' gem 'ruby-protocol-buffers', '1.2.2' gem 'eventmachine', '1.0.3' -gem 'amqp' +gem 'amqp', '1.0.2' gem 'will_paginate' gem 'actionmailer', '3.2.13' gem 'sendgrid' From 477954aa5ffb6b7b12af086a7677c98e494cbfd1 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Tue, 25 Jun 2013 23:10:21 -0400 Subject: [PATCH 231/274] VRFS-107 session join / departure notifications --- lib/jam_ruby/connection_manager.rb | 23 +++-- lib/jam_ruby/constants/notification_types.rb | 11 +++ lib/jam_ruby/message_factory.rb | 22 +++-- lib/jam_ruby/models/notification.rb | 88 +++++++++++++------- 4 files changed, 103 insertions(+), 41 deletions(-) diff --git a/lib/jam_ruby/connection_manager.rb b/lib/jam_ruby/connection_manager.rb index 3ba875561..82176e0cc 100644 --- a/lib/jam_ruby/connection_manager.rb +++ b/lib/jam_ruby/connection_manager.rb @@ -225,11 +225,14 @@ SQL end end - def join_music_session(user_id, client_id, music_session_id, as_musician, tracks) + def join_music_session(user, client_id, music_session, as_musician, tracks, send_notification = true) connection = nil + user_id = user.id + music_session_id = music_session.id + + ConnectionManager.active_record_transaction do |connection_manager| + db_conn = connection_manager.pg_conn - #ConnectionManager.active_record_transaction do |connection_manager| - ActiveRecord::Base.transaction do connection = Connection.find_by_client_id_and_user_id!(client_id, user_id) connection.music_session_id = music_session_id connection.as_musician = as_musician @@ -240,6 +243,10 @@ SQL if connection.errors.any? raise ActiveRecord::Rollback else + if as_musician && music_session.musician_access && send_notification + Notification.send_musician_session_join(music_session, connection, user) + Notification.send_friend_session_join(db_conn, connection, user) + end MusicSessionUserHistory.save(music_session_id, user_id, client_id) end end @@ -297,13 +304,17 @@ SQL # end end - def leave_music_session(user_id, client_id, music_session_id) + def leave_music_session(user, connection, music_session) ConnectionManager.active_record_transaction do |connection_manager| conn = connection_manager.pg_conn lock_connections(conn) + music_session_id = music_session.id + user_id = user.id + client_id = connection.client_id + previous_music_session_id = check_already_session(conn, client_id) if previous_music_session_id == nil @@ -317,9 +328,11 @@ SQL # can throw exception if the session is deleted just before this conn.exec("UPDATE connections SET music_session_id = NULL, as_musician = NULL WHERE client_id = $1 AND user_id =$2", [client_id, user_id]) do |result| if result.cmd_tuples == 1 - @log.debug("deassociated music_session with connection for client_id=#{client_id}, user_id=#{user_id}") + @log.debug("disassociated music_session with connection for client_id=#{client_id}, user_id=#{user_id}") session_checks(conn, previous_music_session_id, user_id) + + Notification.send_musician_session_depart(music_session, connection, user) elsif result.cmd_tuples == 0 @log.debug "leave_music_session no connection found with client_id=#{client_id}" raise ActiveRecord::RecordNotFound diff --git a/lib/jam_ruby/constants/notification_types.rb b/lib/jam_ruby/constants/notification_types.rb index 07a68a62c..f9faaef96 100644 --- a/lib/jam_ruby/constants/notification_types.rb +++ b/lib/jam_ruby/constants/notification_types.rb @@ -1,8 +1,19 @@ module NotificationTypes + # friend notifications FRIEND_UPDATE = "FRIEND_UPDATE" FRIEND_REQUEST = "FRIEND_REQUEST" FRIEND_REQUEST_ACCEPTED = "FRIEND_REQUEST_ACCEPTED" + FRIEND_SESSION_JOIN = "FRIEND_SESSION_JOIN" + + # session notifications SESSION_INVITATION = "SESSION_INVITATION" + # musician notifications + MUSICIAN_SESSION_JOIN = "MUSICIAN_SESSION_JOIN" + MUSICIAN_SESSION_DEPART = "MUSICIAN_SESSION_DEPART" + + # recording notifications + RECORDING_CREATED = "RECORDING_CREATED" + end \ No newline at end of file diff --git a/lib/jam_ruby/message_factory.rb b/lib/jam_ruby/message_factory.rb index 6396895de..a59322003 100644 --- a/lib/jam_ruby/message_factory.rb +++ b/lib/jam_ruby/message_factory.rb @@ -82,16 +82,22 @@ return Jampb::ClientMessage.new(:type => ClientMessage::Type::SERVER_PERMISSION_ERROR, :route_to => CLIENT_TARGET, :server_permission_error => error, :in_reply_to => original_message_id) end - # create a user-joined session message - def user_joined_music_session(session_id, user_id, username) - joined = Jampb::UserJoinedMusicSession.new(:session_id => session_id, :user_id => user_id, :username => username) - return Jampb::ClientMessage.new(:type => ClientMessage::Type::USER_JOINED_MUSIC_SESSION, :route_to => CLIENT_TARGET, :user_joined_music_session => joined) + # create a friend joined session message + def friend_session_join(session_id, user_id, username, photo_url) + join = Jampb::FriendSessionJoin.new(:session_id => session_id, :user_id => user_id, :username => username, :photo_url => photo_url) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::FRIEND_SESSION_JOIN, :route_to => CLIENT_TARGET, :friend_session_join => join) end - # create a user-joined session message - def user_left_music_session(session_id, user_id, username) - left = Jampb::UserLeftMusicSession.new(:session_id => session_id, :user_id => user_id, :username => username) - return Jampb::ClientMessage.new(:type => ClientMessage::Type::USER_LEFT_MUSIC_SESSION, :route_to => CLIENT_TARGET, :user_left_music_session => left) + # create a musician joined session message + def musician_session_join(session_id, user_id, username, photo_url) + join = Jampb::MusicianSessionJoin.new(:session_id => session_id, :user_id => user_id, :username => username, :photo_url => photo_url) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::MUSICIAN_SESSION_JOIN, :route_to => CLIENT_TARGET, :musician_session_join => join) + end + + # create a musician left session message + def musician_session_depart(session_id, user_id, username, photo_url) + left = Jampb::MusicianSessionDepart.new(:session_id => session_id, :user_id => user_id, :username => username, :photo_url => photo_url) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::MUSICIAN_SESSION_DEPART, :route_to => CLIENT_TARGET, :musician_session_depart => left) end # create a user-joined session message diff --git a/lib/jam_ruby/models/notification.rb b/lib/jam_ruby/models/notification.rb index 7e7dc44a8..6d82c2190 100644 --- a/lib/jam_ruby/models/notification.rb +++ b/lib/jam_ruby/models/notification.rb @@ -93,6 +93,18 @@ module JamRuby def retrieve_friends_and_followers(connection, user_id) ids = retrieve_friends(connection, user_id) ids.concat(retrieve_followers(connection, user_id)) + ids.uniq! {|id| id} + return ids + end + + def retrieve_friends_and_followers_not_in_session(connection, user_id, session_id) + ids = retrieve_friends_and_followers(connection, user_id) + connection.exec("SELECT c.user_id as musician_id FROM connections c WHERE c.music_session_id = $1", [session_id]) do |musicians| + musicians.each do |musician_result| + # remove users who are in the session + ids.reject! {|item| item == musician_result['musician_id']} + end + end return ids end @@ -107,9 +119,15 @@ module JamRuby when NotificationTypes::FRIEND_REQUEST_ACCEPTED return "#{user.name} has accepted your friend request." - else - return "" - # when "friend_joined_session" + when NotificationTypes::FRIEND_SESSION_JOIN + return "#{user.name} has joined the session." + + when NotificationTypes::MUSICIAN_SESSION_JOIN + return "#{user.name} has joined the session." + + when NotificationTypes::MUSICIAN_SESSION_DEPART + return "#{user.name} has left the session." + # when "social_media_friend_joined" # when "join_request_approved" # when "join_request_rejected" @@ -117,24 +135,28 @@ module JamRuby # when "band_invitation" # when "band_invitation_accepted" # when "recording_available" - # else + else + return "" end end ################### FRIEND UPDATE ################### def send_friend_update(user_id, online, connection) - user = User.find(user_id) - # (1) create notification - 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 + # (1) get all of this user's friends friend_ids = retrieve_friends(connection, user_id) - # (3) send notification - @@mq_router.publish_to_friends(friend_ids, msg, user_id) + if friend_ids.length > 0 + user = User.find(user_id) + + # (2) create notification + 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) + + # (3) send notification + @@mq_router.publish_to_friends(friend_ids, msg, user_id) + end end ################### FRIEND REQUEST ################### @@ -192,19 +214,41 @@ module JamRuby @@mq_router.publish_to_user(receiver_id, msg) end - def send_session_left(music_session, connection, user) + def send_musician_session_join(music_session, connection, user) # (1) create notification - msg = @@message_factory.user_left_music_session(music_session.id, user.id, user.name) + msg = @@message_factory.musician_session_join(music_session.id, user.id, user.name, user.photo_url) # (2) send notification @@mq_router.server_publish_to_session(music_session, msg, sender = {:client_id => connection.client_id}) end + def send_musician_session_depart(music_session, connection, user) + + # (1) create notification + msg = @@message_factory.musician_session_depart(music_session.id, user.id, user.name, user.photo_url) + + # (2) send notification + @@mq_router.server_publish_to_session(music_session, msg, sender = {:client_id => connection.client_id}) + end + + def send_friend_session_join(db_conn, connection, user) + ids = retrieve_friends_and_followers_not_in_session(db_conn, user.id, connection.music_session.id) + + if ids.length > 0 + # (1) save to database + + # (2) create notification + msg = @@message_factory.friend_session_join(connection.music_session.id, user.id, user.name, user.photo_url) + + # (3) send notification + @@mq_router.publish_to_friends(ids, msg, sender = {:client_id => connection.client_id}) + end + end + def send_join_request(music_session, join_request, sender, text) # (1) save to database - # notification = Notification.new # (2) create notification msg = @@message_factory.join_request(music_session.id, join_request.id, sender.name, text) @@ -213,18 +257,6 @@ module JamRuby @@mq_router.server_publish_to_session(music_session, msg) end - def send_session_joined(connection, user) - - # (1) save to database - - # (2) create notification - msg = @@message_factory.user_joined_music_session(connection.music_session.id, user.id, user.name) - - # (3a) send notification to session members - @@mq_router.server_publish_to_session(connection.music_session, msg, sender = {:client_id => connection.client_id}) - - # TODO: (3b) retrieve all friends and followers of user and send notification to them as well - end end end end \ No newline at end of file From 83ffdd6574354d887f4a323aa73849833d582bf9 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Tue, 25 Jun 2013 23:28:54 -0500 Subject: [PATCH 232/274] * updatingt size/md5 on both created and update of artifact --- lib/jam_ruby/models/artifact_update.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/jam_ruby/models/artifact_update.rb b/lib/jam_ruby/models/artifact_update.rb index f1301820a..d60d0be41 100644 --- a/lib/jam_ruby/models/artifact_update.rb +++ b/lib/jam_ruby/models/artifact_update.rb @@ -18,7 +18,7 @@ module JamRuby validates :environment, :presence => true validates :product, :inclusion => {:in => PRODUCTS} - before_validation(:on => :create) do + before_validation do if uri.present? && uri_changed? self.size = uri.file.size self.sha1 = Digest::MD5.hexdigest(File.read(uri.current_path)) From 7ddeab5ce2718b4a6ae7bbdfb031352817e0c621 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Wed, 26 Jun 2013 11:16:28 -0400 Subject: [PATCH 233/274] fix tests --- lib/jam_ruby/connection_manager.rb | 3 +- spec/jam_ruby/connection_manager_spec.rb | 76 ++++++++++++++++++------ 2 files changed, 59 insertions(+), 20 deletions(-) diff --git a/lib/jam_ruby/connection_manager.rb b/lib/jam_ruby/connection_manager.rb index 82176e0cc..38f2c15d8 100644 --- a/lib/jam_ruby/connection_manager.rb +++ b/lib/jam_ruby/connection_manager.rb @@ -304,7 +304,7 @@ SQL # end end - def leave_music_session(user, connection, music_session) + def leave_music_session(user, client_id, music_session) ConnectionManager.active_record_transaction do |connection_manager| conn = connection_manager.pg_conn @@ -313,7 +313,6 @@ SQL music_session_id = music_session.id user_id = user.id - client_id = connection.client_id previous_music_session_id = check_already_session(conn, client_id) diff --git a/spec/jam_ruby/connection_manager_spec.rb b/spec/jam_ruby/connection_manager_spec.rb index 71d686f48..9ddcf716b 100644 --- a/spec/jam_ruby/connection_manager_spec.rb +++ b/spec/jam_ruby/connection_manager_spec.rb @@ -233,8 +233,11 @@ describe ConnectionManager do user_id = create_user("test", "user9", "user9@jamkazam.com") music_session_id = create_music_session(user_id) + user = User.find(user_id) + music_session = MusicSession.find(music_session_id) + @connman.create_connection(user_id, client_id, "1.1.1.1") - connection = @connman.join_music_session(user_id, client_id, music_session_id, true, TRACKS) + connection = @connman.join_music_session(user, client_id, music_session, true, TRACKS) connection.errors.any?.should be_false @@ -256,7 +259,10 @@ describe ConnectionManager do user_id = create_user("test", "user10", "user10@jamkazam.com") music_session_id = create_music_session(user_id) - expect { @connman.join_music_session(user_id, client_id, music_session_id, true, TRACKS) }.to raise_error(ActiveRecord::RecordNotFound) + user = User.find(user_id) + music_session = MusicSession.find(music_session_id) + + expect { @connman.join_music_session(user, client_id, music_session, true, TRACKS) }.to raise_error(ActiveRecord::RecordNotFound) end @@ -269,10 +275,15 @@ describe ConnectionManager do @connman.create_connection(user_id, client_id, "1.1.1.1") @connman.create_connection(user_id2, client_id2, "1.1.1.1") - music_session_id = create_music_session(user_id) - @connman.join_music_session(user_id, client_id, music_session_id, true, TRACKS) + user = User.find(user_id) + music_session = MusicSession.find(music_session_id) - connection = @connman.join_music_session(user_id2, client_id2, music_session_id, true, TRACKS) + music_session_id = create_music_session(user_id) + @connman.join_music_session(user, client_id, music_session, true, TRACKS) + + user = User.find(user_id2) + + connection = @connman.join_music_session(user, client_id2, music_session, true, TRACKS) connection.errors.size.should == 1 connection.errors.get(:as_musician).should == [Connection::FAN_CAN_NOT_JOIN_AS_MUSICIAN] end @@ -284,7 +295,10 @@ describe ConnectionManager do music_session_id = create_music_session(user_id) - connection = @connman.join_music_session(user_id, client_id, music_session_id, 'blarg', TRACKS) + user = User.find(user_id) + music_session = MusicSession.find(music_session_id) + + connection = @connman.join_music_session(user, client_id, music_session, 'blarg', TRACKS) connection.errors.size.should == 0 connection.as_musician.should be_false end @@ -293,15 +307,21 @@ describe ConnectionManager do musician_client_id = "client_id10.3" fan_client_id = "client_id10.4" - musician = create_user("test", "user10.3", "user10.3@jamkazam.com") - fan = create_user("test", "user10.4", "user10.4@jamkazam.com", :musician => false) + musician_id = create_user("test", "user10.3", "user10.3@jamkazam.com") + fan_id = create_user("test", "user10.4", "user10.4@jamkazam.com", :musician => false) @connman.create_connection(musician, musician_client_id, "1.1.1.1") @connman.create_connection(fan, fan_client_id, "1.1.1.1") + music_session_id = create_music_session(musician, :fan_access => false) - @connman.join_music_session(musician, musician_client_id, music_session_id, true, TRACKS) + + user = User.find(musician_id) + music_session = MusicSession.find(music_session_id) + + @connman.join_music_session(musician, musician_client_id, music_session, true, TRACKS) # now join the session as a fan, bt fan_access = false - connection = @connman.join_music_session(fan, fan_client_id, music_session_id, false, TRACKS) + user = User.find(fan_id) + connection = @connman.join_music_session(user, fan_client_id, music_session, false, TRACKS) connection.errors.size.should == 1 end @@ -312,17 +332,23 @@ describe ConnectionManager do user_id2 = create_user("test", "user21", "user21@jamkazam.com") music_session_id = create_music_session(user_id) + user = User.find(user_id2) + music_session = MusicSession.find(music_session_id) + @connman.create_connection(user_id, client_id, "1.1.1.1") # specify real user id, but not associated with this session - expect { @connman.join_music_session(user_id2, client_id, music_session_id, true, TRACKS) } .to raise_error(ActiveRecord::RecordNotFound) + expect { @connman.join_music_session(user, client_id, music_session, true, TRACKS) } .to raise_error(ActiveRecord::RecordNotFound) end it "join_music_session fails if no music_session" do client_id = "client_id11" user_id = create_user("test", "user11", "user11@jamkazam.com") + user = User.find(user_id) + music_session = MusicSession.new + @connman.create_connection(user_id, client_id, "1.1.1.1") - connection = @connman.join_music_session(user_id, client_id, "some_bogus_music_session_id", true, TRACKS) + connection = @connman.join_music_session(user, client_id, music_session, true, TRACKS) connection.errors.size.should == 1 connection.errors.get(:music_session).should == [Connection::MUSIC_SESSION_MUST_BE_SPECIFIED] end @@ -333,9 +359,12 @@ describe ConnectionManager do user_id2 = create_user("test", "user11.2", "user11.2@jamkazam.com") music_session_id = create_music_session(user_id, :approval_required => true) + user = User.find(user_id2) + music_session = MusicSession.find(music_session_id) + @connman.create_connection(user_id, client_id, "1.1.1.1") # specify real user id, but not associated with this session - expect { @connman.join_music_session(user_id2, client_id, music_session_id, true, TRACKS) } .to raise_error(ActiveRecord::RecordNotFound) + expect { @connman.join_music_session(user, client_id, music_session, true, TRACKS) } .to raise_error(ActiveRecord::RecordNotFound) end @@ -344,9 +373,12 @@ describe ConnectionManager do client_id = "client_id12" user_id = create_user("test", "user12", "user12@jamkazam.com") + user = User.find(user_id) + dummy_music_session = MusicSession.new + @connman.create_connection(user_id, client_id, "1.1.1.1") - expect { @connman.leave_music_session(user_id, client_id, "some_bogus_music_session_id") }.to raise_error(JamRuby::StateError) + expect { @connman.leave_music_session(user, client_id, dummy_music_session) }.to raise_error(JamRuby::StateError) end it "leave_music_session fails if in different music_session" do @@ -355,9 +387,14 @@ describe ConnectionManager do user_id = create_user("test", "user13", "user13@jamkazam.com") music_session_id = create_music_session(user_id) + user = User.find(user_id) + music_session = MusicSession.find(music_session_id) + + dummy_music_session = MusicSession.new + @connman.create_connection(user_id, client_id, "1.1.1.1") - @connman.join_music_session(user_id, client_id, music_session_id, true, TRACKS) - expect { @connman.leave_music_session(user_id, client_id, "some_bogus_music_session_id") }.to raise_error(JamRuby::StateError) + @connman.join_music_session(user, client_id, music_session, true, TRACKS) + expect { @connman.leave_music_session(user, client_id, dummy_music_session) }.to raise_error(JamRuby::StateError) end it "leave_music_session works" do @@ -366,8 +403,11 @@ describe ConnectionManager do user_id = create_user("test", "user14", "user14@jamkazam.com") music_session_id = create_music_session(user_id) + user = User.find(user_id) + music_session = MusicSession.find(music_session_id) + @connman.create_connection(user_id, client_id, "1.1.1.1") - @connman.join_music_session(user_id, client_id, music_session_id, true, TRACKS) + @connman.join_music_session(user, client_id, music_session, true, TRACKS) assert_session_exists(music_session_id, true) @@ -375,7 +415,7 @@ describe ConnectionManager do result.getvalue(0, 0).should == music_session_id end - @connman.leave_music_session(user_id, client_id, music_session_id) + @connman.leave_music_session(user, client_id, music_session) @conn.exec("SELECT music_session_id FROM connections WHERE client_id = $1", [client_id]) do |result| result.getvalue(0, 0).should == nil From 4980f1929a8b98a9dcc21884febaf7dff142ddca Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Wed, 26 Jun 2013 12:24:38 -0400 Subject: [PATCH 234/274] fix tests --- lib/jam_ruby/connection_manager.rb | 3 ++- spec/jam_ruby/connection_manager_spec.rb | 13 +++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/jam_ruby/connection_manager.rb b/lib/jam_ruby/connection_manager.rb index 38f2c15d8..82176e0cc 100644 --- a/lib/jam_ruby/connection_manager.rb +++ b/lib/jam_ruby/connection_manager.rb @@ -304,7 +304,7 @@ SQL # end end - def leave_music_session(user, client_id, music_session) + def leave_music_session(user, connection, music_session) ConnectionManager.active_record_transaction do |connection_manager| conn = connection_manager.pg_conn @@ -313,6 +313,7 @@ SQL music_session_id = music_session.id user_id = user.id + client_id = connection.client_id previous_music_session_id = check_already_session(conn, client_id) diff --git a/spec/jam_ruby/connection_manager_spec.rb b/spec/jam_ruby/connection_manager_spec.rb index 9ddcf716b..0a4b45a4c 100644 --- a/spec/jam_ruby/connection_manager_spec.rb +++ b/spec/jam_ruby/connection_manager_spec.rb @@ -275,10 +275,11 @@ describe ConnectionManager do @connman.create_connection(user_id, client_id, "1.1.1.1") @connman.create_connection(user_id2, client_id2, "1.1.1.1") + music_session_id = create_music_session(user_id) + user = User.find(user_id) music_session = MusicSession.find(music_session_id) - music_session_id = create_music_session(user_id) @connman.join_music_session(user, client_id, music_session, true, TRACKS) user = User.find(user_id2) @@ -317,7 +318,7 @@ describe ConnectionManager do user = User.find(musician_id) music_session = MusicSession.find(music_session_id) - @connman.join_music_session(musician, musician_client_id, music_session, true, TRACKS) + @connman.join_music_session(user, musician_client_id, music_session, true, TRACKS) # now join the session as a fan, bt fan_access = false user = User.find(fan_id) @@ -376,9 +377,9 @@ describe ConnectionManager do user = User.find(user_id) dummy_music_session = MusicSession.new - @connman.create_connection(user_id, client_id, "1.1.1.1") + connection = @connman.create_connection(user_id, client_id, "1.1.1.1") - expect { @connman.leave_music_session(user, client_id, dummy_music_session) }.to raise_error(JamRuby::StateError) + expect { @connman.leave_music_session(user, connection, dummy_music_session) }.to raise_error(JamRuby::StateError) end it "leave_music_session fails if in different music_session" do @@ -392,9 +393,9 @@ describe ConnectionManager do dummy_music_session = MusicSession.new - @connman.create_connection(user_id, client_id, "1.1.1.1") + connection = @connman.create_connection(user_id, client_id, "1.1.1.1") @connman.join_music_session(user, client_id, music_session, true, TRACKS) - expect { @connman.leave_music_session(user, client_id, dummy_music_session) }.to raise_error(JamRuby::StateError) + expect { @connman.leave_music_session(user, connection, dummy_music_session) }.to raise_error(JamRuby::StateError) end it "leave_music_session works" do From 3569eecf3d2d03baccab0d74c8a3417c489b164e Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Wed, 26 Jun 2013 12:28:21 -0400 Subject: [PATCH 235/274] fix tests --- spec/jam_ruby/connection_manager_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/jam_ruby/connection_manager_spec.rb b/spec/jam_ruby/connection_manager_spec.rb index 0a4b45a4c..7a17ce8ab 100644 --- a/spec/jam_ruby/connection_manager_spec.rb +++ b/spec/jam_ruby/connection_manager_spec.rb @@ -407,7 +407,7 @@ describe ConnectionManager do user = User.find(user_id) music_session = MusicSession.find(music_session_id) - @connman.create_connection(user_id, client_id, "1.1.1.1") + connection = @connman.create_connection(user_id, client_id, "1.1.1.1") @connman.join_music_session(user, client_id, music_session, true, TRACKS) assert_session_exists(music_session_id, true) @@ -416,7 +416,7 @@ describe ConnectionManager do result.getvalue(0, 0).should == music_session_id end - @connman.leave_music_session(user, client_id, music_session) + @connman.leave_music_session(user, connection, music_session) @conn.exec("SELECT music_session_id FROM connections WHERE client_id = $1", [client_id]) do |result| result.getvalue(0, 0).should == nil From bb832f3fefd31d1f769ad1a22f44ec6f6bb6419a Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Wed, 26 Jun 2013 18:07:03 -0400 Subject: [PATCH 236/274] another attempt to fix tests --- spec/jam_ruby/connection_manager_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/jam_ruby/connection_manager_spec.rb b/spec/jam_ruby/connection_manager_spec.rb index 7a17ce8ab..1d46a382d 100644 --- a/spec/jam_ruby/connection_manager_spec.rb +++ b/spec/jam_ruby/connection_manager_spec.rb @@ -310,8 +310,8 @@ describe ConnectionManager do fan_client_id = "client_id10.4" musician_id = create_user("test", "user10.3", "user10.3@jamkazam.com") fan_id = create_user("test", "user10.4", "user10.4@jamkazam.com", :musician => false) - @connman.create_connection(musician, musician_client_id, "1.1.1.1") - @connman.create_connection(fan, fan_client_id, "1.1.1.1") + @connman.create_connection(musician_id, musician_client_id, "1.1.1.1") + @connman.create_connection(fan_id, fan_client_id, "1.1.1.1") music_session_id = create_music_session(musician, :fan_access => false) From 4e08d1180ec3bedcd6da4a94aebcfc9c83ba7fda Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Wed, 26 Jun 2013 20:11:13 -0400 Subject: [PATCH 237/274] another attempt to fix tests --- spec/jam_ruby/connection_manager_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/jam_ruby/connection_manager_spec.rb b/spec/jam_ruby/connection_manager_spec.rb index 1d46a382d..8b1239c55 100644 --- a/spec/jam_ruby/connection_manager_spec.rb +++ b/spec/jam_ruby/connection_manager_spec.rb @@ -313,7 +313,7 @@ describe ConnectionManager do @connman.create_connection(musician_id, musician_client_id, "1.1.1.1") @connman.create_connection(fan_id, fan_client_id, "1.1.1.1") - music_session_id = create_music_session(musician, :fan_access => false) + music_session_id = create_music_session(musician_id, :fan_access => false) user = User.find(musician_id) music_session = MusicSession.find(music_session_id) From 3a59c3b86b721f5fe4f8a48464b1b3db0d182dbc Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Sun, 30 Jun 2013 13:55:03 -0700 Subject: [PATCH 238/274] more work --- bin/mix_cron.rb | 97 ++++++++++++++++++++++++++++++++ lib/jam_ruby/models/mix.rb | 2 +- lib/jam_ruby/models/recording.rb | 50 ++++++++-------- spec/jam_ruby/models/mix_spec.rb | 0 4 files changed, 123 insertions(+), 26 deletions(-) create mode 100644 bin/mix_cron.rb mode change 100644 => 100755 spec/jam_ruby/models/mix_spec.rb diff --git a/bin/mix_cron.rb b/bin/mix_cron.rb new file mode 100644 index 000000000..2d106787b --- /dev/null +++ b/bin/mix_cron.rb @@ -0,0 +1,97 @@ +# Ensure the cron is using the proper version of ruby, and simply run this with: +# ruby mix_cron.rb +# + +require 'faraday' +require 'json' +require 'tempfile' + +MIXER_EXECUTABLE = "/usr/local/bin/audiomixerapp" +S3CMD = "s3cmd" + +# FIXME: This should probably come from an environments file or something +BASE_URL = "http://www.jamkazam.com" + +# This must be present on requests from the cron to prevent hackers from +# hitting these routes. +CRON_TOKEN = "2kkl39sjjf3ijdsflje2923j" + +AUDIOMIXER_LOG_FILE = "/var/log/audiomixer" + +MIX_CRON_WATCH_FILE = "/var/run/mix_cron" + + +# Don't do anything if the cron is arleady running. There's a theoretical race +# condition here, but it should never actually be a problem because the cron should +# only run every minute or two. +if File.exist?(MIX_CRON_WATCH_FILE) + psret = `ps axuw | grep mix_cron.rb | grep ruby` + unless psret.empty? + puts "Cron still running" + exit + end +end + +`touch #{MIX_CRON_WATCH_FILE}` + + +# Get the next manifest to mix +response = Faraday.get "#{BASE_URL}/mixes/next", :token => CRON_TOKEN +if response.status > 299 + puts "Error response getting next mix: #{response.status}, #{response.body}" + do_exit +end + +# This just means no mixes available. +if response.status == 204 + do_exit +end + + +if response.status != 200 + puts "Unexpected response received: #{response.status}, #{response.body}" + do_exit +end + +json = JSON.parse(response.body) +# This needs to download all the vorbis files, mix and then upload +# the finished one, and tell the server about that. +json['manifest']['files'].map! do |file| + file['filename'] = Dir::Tmpname.make_tmpname ['/tmp/', '.ogg'], nil + file_response = Faraday.get file.url + if file_response.status != 200 + puts "Error downloading url: #{file.url}" + do_exit + end + File.open(file['filename'], 'wb') { |fp| fp.write(file_response.body) } +end + +output_filename = "/tmp/mixout-#{json['id']}.ogg" +IO.popen("#{MIXER_EXECUTABLE} #{output_filename} vorbis >>& #{AUDIOMIXER_LOG_FILE}", "w") do |f| + f.puts JSON.generate(json) + f.close +end + +# First maybe make sure the length is reasonable or something? I bet sox can check that (duration i mean). + +# FIXME?: Need to check that the put succeeded before carrying on. Probably can use the exit code or some such. +# Or maybe just do an ls to sanity check it. +`#{S3CMD} -P put #{output_filename} #{json['destination']}` + +finish_response = Faraday.put "#{BASE_URL}/mixes/finish", :token => CRON_TOKEN, :id => json['id'] +if finish_response.status != 204 + puts "Error calling finish on server for mix_id #{json['id']}: #{finish_response.status}, #{finish_response.body}" + do_exit +end + +puts "Mix complete and uploaded to: #{json['destination']}" +do_exit + + + + +def do_exit + `rm -f #{MIX_CRON_WATCH_FILE}` +end + + diff --git a/lib/jam_ruby/models/mix.rb b/lib/jam_ruby/models/mix.rb index c1867b427..d1add4045 100644 --- a/lib/jam_ruby/models/mix.rb +++ b/lib/jam_ruby/models/mix.rb @@ -10,7 +10,7 @@ module JamRuby def self.schedule(recording, manifest) raise if recording.nil? - mix = Mix.new + mix = Mix.new mix.recording = recording mix.manifest = manifest mix.save diff --git a/lib/jam_ruby/models/recording.rb b/lib/jam_ruby/models/recording.rb index d52c66e84..d9e2a90ed 100644 --- a/lib/jam_ruby/models/recording.rb +++ b/lib/jam_ruby/models/recording.rb @@ -244,38 +244,38 @@ module JamRuby end =end - def base_mix_manifest - manifest = { "files" => [], "timeline" => [] } - mix_params = [] - recorded_tracks.each do |recorded_track| - return nil unless recorded_track.fully_uploaded - manifest["files"] << { "url" => recorded_track.url, "codec" => "opus", "offset" => 0 } - mix_params << { "level" => 100, "balance" => 0 } - end - - manifest["timeline"] << { "timestamp" => 0, "mix" => mix_params } - manifest["timeline"] << { "timestamp" => duration, "end" => true } - manifest + def base_mix_manifest + manifest = { "files" => [], "timeline" => [] } + mix_params = [] + recorded_tracks.each do |recorded_track| + return nil unless recorded_track.fully_uploaded + manifest["files"] << { "url" => recorded_track.url, "codec" => "vorbis", "offset" => 0 } + mix_params << { "level" => 100, "balance" => 0 } end + + manifest["timeline"] << { "timestamp" => 0, "mix" => mix_params } + manifest["timeline"] << { "timestamp" => duration, "end" => true } + manifest + end - private - def self.validate_user_is_band_member(user, band) - unless band.users.exists? user - raise PermissionError, ValidationMessages::USER_NOT_BAND_MEMBER_VALIDATION_ERROR - end + private + def self.validate_user_is_band_member(user, band) + unless band.users.exists? user + raise PermissionError, ValidationMessages::USER_NOT_BAND_MEMBER_VALIDATION_ERROR end + end - def self.validate_user_is_creator(user, creator) - unless user.id == creator.id - raise PermissionError, ValidationMessages::PERMISSION_VALIDATION_ERROR - end + def self.validate_user_is_creator(user, creator) + unless user.id == creator.id + raise PermissionError, ValidationMessages::PERMISSION_VALIDATION_ERROR end + end - def self.validate_user_is_musician(user) - unless user.musician? - raise PermissionError, ValidationMessages::USER_NOT_MUSICIAN_VALIDATION_ERROR - end + def self.validate_user_is_musician(user) + unless user.musician? + raise PermissionError, ValidationMessages::USER_NOT_MUSICIAN_VALIDATION_ERROR end + end end end diff --git a/spec/jam_ruby/models/mix_spec.rb b/spec/jam_ruby/models/mix_spec.rb old mode 100644 new mode 100755 From dfad349bc05bf31306df1911de44377d39bd440b Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Sun, 30 Jun 2013 18:25:01 -0700 Subject: [PATCH 239/274] fixed test --- spec/jam_ruby/models/recording_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/jam_ruby/models/recording_spec.rb b/spec/jam_ruby/models/recording_spec.rb index 463c56d51..ed1ce9a4e 100644 --- a/spec/jam_ruby/models/recording_spec.rb +++ b/spec/jam_ruby/models/recording_spec.rb @@ -256,10 +256,10 @@ describe Recording do files = mix_manifest["files"] files.should_not be_nil files.length.should == 2 - files.first["codec"].should == "opus" + files.first["codec"].should == "vorbis" files.first["offset"].should == 0 files.first["url"].should == @recording.recorded_tracks.first.url - files.last["codec"].should == "opus" + files.last["codec"].should == "vorbis" files.last["offset"].should == 0 files.last["url"].should == @recording.recorded_tracks.last.url From b09b2a5ad45a955cb2bf5282e2e9cf0aea087bae Mon Sep 17 00:00:00 2001 From: Seth Call Date: Tue, 2 Jul 2013 20:11:04 -0500 Subject: [PATCH 240/274] * adding a method called 'unique_users in MusicSessionHistory so that you can easily see what users joined the session VRFS-268 --- lib/jam_ruby/models/music_session_history.rb | 8 ++++++ lib/jam_ruby/models/user.rb | 1 + spec/factories.rb | 23 ++++++++++++++- .../models/music_session_history_spec.rb | 28 +++++++++++++++++++ spec/spec_helper.rb | 3 ++ 5 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 spec/jam_ruby/models/music_session_history_spec.rb diff --git a/lib/jam_ruby/models/music_session_history.rb b/lib/jam_ruby/models/music_session_history.rb index c09a76be2..7c83e9e4f 100644 --- a/lib/jam_ruby/models/music_session_history.rb +++ b/lib/jam_ruby/models/music_session_history.rb @@ -34,6 +34,14 @@ module JamRuby return query end + def unique_users + User + .joins(:music_session_user_histories) + .group("users.id") + .order("users.id") + .where(%Q{ music_sessions_user_history.music_session_id = '#{music_session_id}'}) + end + def self.save(music_session) session_history = MusicSessionHistory.find_by_music_session_id(music_session.id) diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index d580c544c..23f888e6d 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -94,6 +94,7 @@ module JamRuby # session history has_many :music_session_histories, :foreign_key => "user_id", :class_name => "JamRuby::MusicSessionHistory" + has_many :music_session_user_histories, :foreign_key => "user_id", :class_name => "JamRuby::MusicSessionUserHistory" # saved tracks has_many :recorded_tracks, :foreign_key => "user_id", :class_name => "JamRuby::RecordedTrack", :inverse_of => :user diff --git a/spec/factories.rb b/spec/factories.rb index f91a3e9f0..ddac6d64b 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -30,8 +30,29 @@ FactoryGirl.define do approval_required false musician_access true legal_terms true + association :creator, :factory => :user + end - after(:create) { |session| MusicSessionHistory.save(session) } + factory :music_session_history, :class => JamRuby::MusicSessionHistory do + ignore do + music_session nil + end + + music_session_id { music_session.id } + description { music_session.description } + user_id { music_session.user_id } + band_id { music_session.band_id } + end + + factory :music_session_user_history, :class => JamRuby::MusicSessionUserHistory do + ignore do + history nil + user nil + end + + music_session_id { history.music_session_id } + user_id { user.id } + sequence(:client_id) { |n| "Connection #{n}" } end factory :connection, :class => JamRuby::Connection do diff --git a/spec/jam_ruby/models/music_session_history_spec.rb b/spec/jam_ruby/models/music_session_history_spec.rb new file mode 100644 index 000000000..260917687 --- /dev/null +++ b/spec/jam_ruby/models/music_session_history_spec.rb @@ -0,0 +1,28 @@ +require 'spec_helper' + +describe MusicSessionHistory do + + let(:some_user) { FactoryGirl.create(:user) } + let(:music_session) { FactoryGirl.create(:music_session) } + let(:history) { FactoryGirl.create(:music_session_history, :music_session => music_session) } + let(:user_history1) { FactoryGirl.create(:music_session_user_history, :history => history, :user => music_session.creator) } + let(:user_history2) { FactoryGirl.create(:music_session_user_history, :history => history, :user => some_user) } + + it "create" do + history.description.should eql(music_session.description) + end + + it "unique users" do + user_history1.should_not be_nil + user_history2.should_not be_nil + users = history.unique_users + + users.length.should eql(2) + + users.include?(some_user).should be_true + users.include?(music_session.creator).should be_true + end + +end + + diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index e8c095186..eeab8e42e 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -17,6 +17,9 @@ require 'spork' require 'database_cleaner' require 'factories' +# uncomment this to see active record logs +# ActiveRecord::Base.logger = Logger.new(STDOUT) if defined?(ActiveRecord::Base) + include JamRuby # manually register observers From 5eb90fe4b544ef3f5ce652699db7b4f9d30366b3 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Thu, 4 Jul 2013 17:10:46 -0400 Subject: [PATCH 241/274] VRFS-377 desk.com integration --- lib/jam_ruby.rb | 1 + lib/jam_ruby/lib/desk_multipass.rb | 64 ++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 lib/jam_ruby/lib/desk_multipass.rb diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index c0519fa9e..7012f749b 100755 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -27,6 +27,7 @@ require "jam_ruby/app/mailers/user_mailer" require "jam_ruby/app/mailers/invited_user_mailer" require "jam_ruby/app/uploaders/artifact_uploader" require "jam_ruby/app/uploaders/perf_data_uploader" +require "jam_ruby/lib/desk_multipass" require "jam_ruby/lib/s3_util" require "jam_ruby/lib/s3_manager" require "jam_ruby/message_factory" diff --git a/lib/jam_ruby/lib/desk_multipass.rb b/lib/jam_ruby/lib/desk_multipass.rb new file mode 100644 index 000000000..3a679586e --- /dev/null +++ b/lib/jam_ruby/lib/desk_multipass.rb @@ -0,0 +1,64 @@ +require 'openssl' +require 'digest/sha1' +require 'base64' +require 'cgi' +require 'time' +require 'json' + +module JamRuby + class DeskMultipass + + # MULTIPASS_KEY = "453ddfc0bab00130a9c13bc9a68cf24c" + + API_KEY = "st3wSS1gmsaMwMtJuY9k" + SUBDOMAIN = "jamkazam.desk.com" + + # API_SECRET = "qQTFHvfJMtllKZade03MacVgbszQcAZMsbc6dp6I" + # API_TOKEN = "ZkQMhbwgF7DAgL0ykoMx" + # API_TOKEN_SECRET = "rz2suV3sHRhjYTD32orbrkMIZ23CqoVQ1QyzGPUS" + + def initialize(user) + @user = user + generate_token + generate_signature + end + + def token + @token + end + + def signature + @signature + end + + private + + def generate_token + key = Digest::SHA1.digest(API_KEY + SUBDOMAIN)[0...16] + + # Generate a random 16 byte IV + iv = OpenSSL::Random.random_bytes(16) + + json = JSON.generate( + :uid => @user.id, + :expires => (Time.now + 300).iso8601, + :customer_name => @user.name, + :customer_email => @user.email) + + cipher = OpenSSL::Cipher::Cipher.new("aes-128-cbc") + cipher.encrypt + cipher.key = key + cipher.iv = iv + encrypted = cipher.update(json) + cipher.final + + prepended = iv + encrypted + token = Base64.encode64(prepended) + @token = CGI.escape(token) + end + + def generate_signature + signature = Base64.encode64(OpenSSL::HMAC.digest('sha1', API_KEY, @token)) + @signature = CGI.escape(signature) + end + end +end \ No newline at end of file From c0b9472b074c8827aa2637d359147c3908e25ff6 Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Fri, 5 Jul 2013 01:24:12 -0700 Subject: [PATCH 242/274] VFRS-402 --- bin/mix_cron.rb | 0 lib/jam_ruby/app/mailers/user_mailer.rb | 1 - lib/jam_ruby/models/user.rb | 7 ++++--- spec/jam_ruby/models/user_spec.rb | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) mode change 100644 => 100755 bin/mix_cron.rb diff --git a/bin/mix_cron.rb b/bin/mix_cron.rb old mode 100644 new mode 100755 diff --git a/lib/jam_ruby/app/mailers/user_mailer.rb b/lib/jam_ruby/app/mailers/user_mailer.rb index 5f2f312f5..d63ce7d00 100644 --- a/lib/jam_ruby/app/mailers/user_mailer.rb +++ b/lib/jam_ruby/app/mailers/user_mailer.rb @@ -42,7 +42,6 @@ module JamRuby def password_reset(user, password_reset_url) @user = user - # /reset_password_token?token=#{@user.reset_password_token}&email=#{CGI.escape(@user.email)}" @password_reset_url = password_reset_url sendgrid_unique_args :type => "password_reset" mail(:to => user.email, :subject => "Jamkazam Password Reset") do |format| diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 23f888e6d..ed20af783 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -305,7 +305,7 @@ module JamRuby def self.set_password_from_token(email, token, new_password, new_password_confirmation) user = User.find_by_email(email) - if user.nil? || user.reset_password_token != token || Time.now - user.reset_password_token_created > 3.days + if user.nil? || user.reset_password_token != token || Time.now - user.reset_password_token_created > 3.days || new_password.length < 6 || new_password != new_password_confirmation raise JamRuby::JamArgumentError end user.reset_password_token = nil @@ -324,7 +324,7 @@ module JamRuby UserMailer.password_changed(self).deliver end - def self.reset_password(email, reset_password_url) + def self.reset_password(email, base_uri) user = User.find_by_email(email) raise JamRuby::JamArgumentError if user.nil? @@ -332,7 +332,8 @@ module JamRuby user.reset_password_token_created = Time.now user.save - UserMailer.password_reset(user, reset_password_url).deliver + reset_url = "#{base_uri}/reset_password_token?token=#{user.reset_password_token}&email=#{CGI.escape(email)}" + UserMailer.password_reset(user, reset_url).deliver user end diff --git a/spec/jam_ruby/models/user_spec.rb b/spec/jam_ruby/models/user_spec.rb index d6ea57ae4..a0b809a4d 100644 --- a/spec/jam_ruby/models/user_spec.rb +++ b/spec/jam_ruby/models/user_spec.rb @@ -191,7 +191,7 @@ describe User do @user.reset_password_token_created.should >= Time.now - 1.minute end - it "errors if the wrong token is comes in" do + it "errors if the wrong token comes in" do User.reset_password(@user.email, RESET_PASSWORD_URL) @user.reload expect { User.set_password_from_token(@user.email, "wrongtoken", "newpassword", "newpassword") }.to raise_error From 4bdca93b63dc11794f741506e36b6328eae241b0 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Fri, 5 Jul 2013 13:12:22 -0500 Subject: [PATCH 243/274] * updating pg gem to coincide with jam-db --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 9b6a75cb1..38039bed6 100644 --- a/Gemfile +++ b/Gemfile @@ -8,7 +8,7 @@ end workspace = ENV["WORKSPACE"] || "~/workspace" devenv = ENV["BUILD_NUMBER"].nil? # Jenkins sets a build number environment variable -gem 'pg', '0.14.0', :platform => [:mri, :mswin, :mingw] +gem 'pg', '0.15.1', :platform => [:mri, :mswin, :mingw] gem 'jdbc_postgres', :platform => [:jruby] gem 'activerecord', '3.2.13' From cb1e50374b5c15a7a381688c0fd47fab2c0ca768 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Mon, 8 Jul 2013 00:11:03 -0400 Subject: [PATCH 244/274] VRFS-377 fix API key --- lib/jam_ruby/lib/desk_multipass.rb | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/lib/jam_ruby/lib/desk_multipass.rb b/lib/jam_ruby/lib/desk_multipass.rb index 3a679586e..560551473 100644 --- a/lib/jam_ruby/lib/desk_multipass.rb +++ b/lib/jam_ruby/lib/desk_multipass.rb @@ -6,21 +6,17 @@ require 'time' require 'json' module JamRuby + + # Most of the code below was taken from the example located here: + # https://github.com/assistly/multipass-examples/blob/master/ruby.rb class DeskMultipass - # MULTIPASS_KEY = "453ddfc0bab00130a9c13bc9a68cf24c" - - API_KEY = "st3wSS1gmsaMwMtJuY9k" - SUBDOMAIN = "jamkazam.desk.com" - - # API_SECRET = "qQTFHvfJMtllKZade03MacVgbszQcAZMsbc6dp6I" - # API_TOKEN = "ZkQMhbwgF7DAgL0ykoMx" - # API_TOKEN_SECRET = "rz2suV3sHRhjYTD32orbrkMIZ23CqoVQ1QyzGPUS" - + API_KEY = "453ddfc0bab00130a9c13bc9a68cf24c" + SITE_KEY = "jamkazam" + def initialize(user) @user = user - generate_token - generate_signature + generate_token_and_signature end def token @@ -32,9 +28,8 @@ module JamRuby end private - - def generate_token - key = Digest::SHA1.digest(API_KEY + SUBDOMAIN)[0...16] + def generate_token_and_signature + key = Digest::SHA1.digest(API_KEY + SITE_KEY)[0...16] # Generate a random 16 byte IV iv = OpenSSL::Random.random_bytes(16) @@ -53,11 +48,9 @@ module JamRuby prepended = iv + encrypted token = Base64.encode64(prepended) - @token = CGI.escape(token) - end + signature = Base64.encode64(OpenSSL::HMAC.digest('sha1', API_KEY, token)) - def generate_signature - signature = Base64.encode64(OpenSSL::HMAC.digest('sha1', API_KEY, @token)) + @token = CGI.escape(token) @signature = CGI.escape(signature) end end From 8d3ebcabb65efc3c66efe5b491000818a9a6620f Mon Sep 17 00:00:00 2001 From: Seth Call Date: Wed, 10 Jul 2013 15:18:25 -0500 Subject: [PATCH 245/274] * VRFS-417; allowing emails to go in with case preservation; but check for existing using case insensitivity --- lib/jam_ruby/constants/validation_messages.rb | 2 +- lib/jam_ruby/models/user.rb | 39 ++++++++++++------- spec/jam_ruby/models/user_spec.rb | 5 ++- 3 files changed, 30 insertions(+), 16 deletions(-) diff --git a/lib/jam_ruby/constants/validation_messages.rb b/lib/jam_ruby/constants/validation_messages.rb index 4c41a3e8a..8f7e8b514 100644 --- a/lib/jam_ruby/constants/validation_messages.rb +++ b/lib/jam_ruby/constants/validation_messages.rb @@ -31,7 +31,7 @@ module ValidationMessages OLD_PASSWORD_DOESNT_MATCH = "Your old password is incorrect." EMAIL_NOT_FOUND = "Email address not found." NOT_YOUR_PASSWORD = "is not your current password" - EMAIL_ALREADY_TAKEN = "is already taken" + EMAIL_ALREADY_TAKEN = "has already been taken" EMAIL_MATCHES_CURRENT = "is same as your current email" INVALID_FPFILE = "is not valid" end diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index ed20af783..15ecc4e51 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -105,18 +105,14 @@ module JamRuby # This causes the authenticate method to be generated (among other stuff) #has_secure_password - before_save { |user| user.email = email.downcase } before_save :create_remember_token, :if => :should_validate_password? before_save :stringify_avatar_info , :if => :updating_avatar validates :first_name, presence: true, length: {maximum: 50} validates :last_name, presence: true, length: {maximum: 50} VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i - validates :email, presence: true, format: {with: VALID_EMAIL_REGEX}, - uniqueness: {case_sensitive: false} - validates :update_email, presence: true, format: {with: VALID_EMAIL_REGEX}, - uniqueness: {case_sensitive: false}, :if => :updating_email - + validates :email, presence: true, format: {with: VALID_EMAIL_REGEX} + validates :update_email, presence: true, format: {with: VALID_EMAIL_REGEX}, :if => :updating_email validates_length_of :password, minimum: 6, maximum: 100, :if => :should_validate_password? validates_presence_of :password_confirmation, :if => :should_validate_password? @@ -126,13 +122,13 @@ module JamRuby validates :subscribe_email, :inclusion => {:in => [nil, true, false]} validates :musician, :inclusion => {:in => [true, false]} - + # custom validators validate :validate_musician_instruments validate :validate_current_password validate :validate_update_email - - validate :validate_avatar_info + validate :email_case_insensitive_uniqueness + validate :update_email_case_insensitive_uniqueness, :if => :updating_email def validate_musician_instruments errors.add(:musician_instruments, ValidationMessages::INSTRUMENT_MINIMUM_NOT_MET) if !administratively_created && musician && musician_instruments.length == 0 @@ -147,7 +143,7 @@ module JamRuby def validate_update_email if updating_email && self.update_email == self.email errors.add(:update_email, ValidationMessages::EMAIL_MATCHES_CURRENT) - elsif updating_email && User.find_by_email(self.update_email) != nil + elsif updating_email && User.where("email ILIKE ?", self.update_email).first != nil errors.add(:update_email, ValidationMessages::EMAIL_ALREADY_TAKEN) end end @@ -160,6 +156,22 @@ module JamRuby end end + def email_case_insensitive_uniqueness + # using the case insensitive unique check of active record will downcase the field, which is not what we want--we want to preserve original casing + search = User.where("email ILIKE ?", self.email).first + if search != nil && search != self + errors.add(:email, ValidationMessages::EMAIL_ALREADY_TAKEN) + end + end + + def update_email_case_insensitive_uniqueness + # using the case insensitive unique check of active record will downcase the field, which is not what we want--we want to preserve original casing + search = User.where("update_email ILIKE ?", self.update_email).first + if search != nil && search != self + errors.add(:update_email, ValidationMessages::EMAIL_ALREADY_TAKEN) + end + end + def online @online ||= !self.connections.nil? && self.connections.size > 0 end @@ -304,7 +316,7 @@ module JamRuby end def self.set_password_from_token(email, token, new_password, new_password_confirmation) - user = User.find_by_email(email) + user = User.where("email ILIKE ?", email).first if user.nil? || user.reset_password_token != token || Time.now - user.reset_password_token_created > 3.days || new_password.length < 6 || new_password != new_password_confirmation raise JamRuby::JamArgumentError end @@ -325,7 +337,7 @@ module JamRuby end def self.reset_password(email, base_uri) - user = User.find_by_email(email) + user = User.where("email ILIKE ?", email).first raise JamRuby::JamArgumentError if user.nil? user.reset_password_token = SecureRandom.urlsafe_base64 @@ -846,7 +858,8 @@ module JamRuby # we only allow users that have confirmed email to authenticate # user = User.where('email_confirmed=true').find_by_email(email) - user = User.find_by_email(email) + # do a case insensitive search for email, because we store it case sensitive + user = User.where("email ILIKE ?", email).first if user && user.valid_password?(password) return user diff --git a/spec/jam_ruby/models/user_spec.rb b/spec/jam_ruby/models/user_spec.rb index a0b809a4d..122378e3f 100644 --- a/spec/jam_ruby/models/user_spec.rb +++ b/spec/jam_ruby/models/user_spec.rb @@ -101,10 +101,11 @@ describe User do describe "email address with mixed case" do let(:mixed_case_email) { "Foo@ExAMPle.CoM" } + it "should be saved as all lower-case" do @user.email = mixed_case_email - @user.save - @user.reload.email.should == mixed_case_email.downcase + @user.save! + @user.reload.email.should == mixed_case_email end end From bdac3e9fd95a3622bb59ccfd13b7e756fc0fde04 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Wed, 10 Jul 2013 21:46:38 -0400 Subject: [PATCH 246/274] fix text in email subject / body --- lib/jam_ruby/app/mailers/user_mailer.rb | 2 +- .../app/views/jam_ruby/user_mailer/welcome_message.html.erb | 2 +- .../app/views/jam_ruby/user_mailer/welcome_message.text.erb | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/jam_ruby/app/mailers/user_mailer.rb b/lib/jam_ruby/app/mailers/user_mailer.rb index d63ce7d00..96b322def 100644 --- a/lib/jam_ruby/app/mailers/user_mailer.rb +++ b/lib/jam_ruby/app/mailers/user_mailer.rb @@ -25,7 +25,7 @@ module JamRuby sendgrid_category "Welcome" sendgrid_unique_args :type => "welcome_message" - mail(:to => user.email, :subject => "Welcome #{user.first_name} to Jamkazam") do |format| + mail(:to => user.email, :subject => "Welcome to Jamkazam, #{user.first_name} ") do |format| format.text format.html end diff --git a/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.html.erb b/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.html.erb index 922fb456a..06d6c928a 100644 --- a/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.html.erb +++ b/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.html.erb @@ -1,4 +1,4 @@ <% provide(:title, 'Welcome to Jamkazam') %> -

Welcome <%= @user.first_name %> to Jamkazam!

+

Welcome to Jamkazam, <%= @user.first_name %> !

To confirm this email address, please go to the signup confirmation page..

diff --git a/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.text.erb b/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.text.erb index 7befb32f3..73ad001bd 100644 --- a/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.text.erb +++ b/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.text.erb @@ -1,3 +1,3 @@ -Welcome <%= @user.first_name %> to Jamkazam! +Welcome to Jamkazam, <%= @user.first_name %>! -To confirm this email address, please go to the signup confirmation page at: <%= @signup_confirm_url %>. \ No newline at end of file +To confirm this email address, please go to the signup confirmation page at: <%= @signup_confirm_url %>. \ No newline at end of file From a18610c92a5fc6b40899fb3340a18c02cf11369b Mon Sep 17 00:00:00 2001 From: Seth Call Date: Fri, 12 Jul 2013 16:18:42 -0500 Subject: [PATCH 247/274] * VRFS-196, VRFS-199, VRFS-195; a class AmqpConnectionManager, with a manual test, is new available that handles reconnect --- lib/jam_ruby.rb | 1 + lib/jam_ruby/amqp/amqp_connection_manager.rb | 97 ++++++++++++++++++++ scripts/simple_amqp_manager.rb | 78 ++++++++++++++++ 3 files changed, 176 insertions(+) create mode 100644 lib/jam_ruby/amqp/amqp_connection_manager.rb create mode 100644 scripts/simple_amqp_manager.rb diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index 7012f749b..fdaa4cd0e 100755 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -30,6 +30,7 @@ require "jam_ruby/app/uploaders/perf_data_uploader" require "jam_ruby/lib/desk_multipass" require "jam_ruby/lib/s3_util" require "jam_ruby/lib/s3_manager" +require "jam_ruby/amqp/amqp_connection_manager" require "jam_ruby/message_factory" require "jam_ruby/models/max_mind_geo" require "jam_ruby/models/max_mind_isp" diff --git a/lib/jam_ruby/amqp/amqp_connection_manager.rb b/lib/jam_ruby/amqp/amqp_connection_manager.rb new file mode 100644 index 000000000..fedbab581 --- /dev/null +++ b/lib/jam_ruby/amqp/amqp_connection_manager.rb @@ -0,0 +1,97 @@ +module JamRuby + # The purpose of this class is to handle reconnect logic and 'recover' logic (which means automatically resubscribe to topics/queues). + # It's 'leaky' in that it will give you a AMQP::Channel to do these subscriptions yourself in the block you pass to connect. + # Use the *connected* property to check if the connection is currently up. + class AmqpConnectionManager + + attr_accessor :should_reconnect, :reconnect_interval, :connection, :connect, :connect_options, :connect_block, :channel, + :connected + + def initialize(should_reconnect, reconnect_interval, connect_options = {}) + @should_reconnect = should_reconnect + @reconnect_interval = reconnect_interval + @connect_options = connect_options + @connected = false + @log = Logging.logger[self] + end + + # the block you pass in will be passed a channel upon successful connect. You need + # + def connect(&block) + @connect = true # indicate that we should be connected + @connect_block = block + + try_connect + end + + def try_connect + @connection = AMQP.connect(@connect_options, &method(:successful_connect)) + @connection.on_tcp_connection_failure(&method(:on_tcp_connection_failure)) + @connection.on_tcp_connection_loss(&method(:on_tcp_connection_loss)) + @connection.on_recovery(&method(:on_recovery)) + @connection.on_error(&method(:on_error)) + end + + def successful_connect(connection) + @log.debug "connected to #{@connect_options}" + @connected = true + + @channel = AMQP::Channel.new(connection) + @channel.auto_recovery = true + + unless @connect_block.nil? + @connect_block.call(@channel) + end + end + + def on_tcp_connection_failure(settings) + @connected = false + + if @connect && @should_reconnect + @log.warn "[network failure] Trying to connect in 4 seconds to #{@connect_options}" + EventMachine.add_timer(@reconnect_interval, &method(:try_connect)) + end + end + + def on_tcp_connection_loss(conn, settings) + @connected = false + if @connect && @should_reconnect + @log.warn "[network failure] Trying to reconnect..." + conn.reconnect(false, @reconnect_interval) + end + end + + def on_recovery(conn, settings) + @connected = true + + @log.debug "reconnected #{conn} #{settings}" + + #puts "#channel before #{@channel}" + #puts "recovered channel: #{@channel.reuse}" + end + + def disconnect + @connect = false # indicate that we should no longer be connected + + unless @connection.nil? + if @connection.connected? + @connection.disconnect do + @connected = false + @log.debug "disconnected" + end + end + end + end + + def on_error(connection, connection_close) + @log.error "Handling a connection-level exception." + + @log.error "AMQP class id : #{connection_close.class_id}" + @log.error "AMQP method id: #{connection_close.method_id}" + @log.error "Status code : #{connection_close.reply_code}" + @log.error "Error message : #{connection_close.reply_text}" + end + end + + +end \ No newline at end of file diff --git a/scripts/simple_amqp_manager.rb b/scripts/simple_amqp_manager.rb new file mode 100644 index 000000000..3c898b930 --- /dev/null +++ b/scripts/simple_amqp_manager.rb @@ -0,0 +1,78 @@ +############ +# USAGE +############ +# +# jam-ruby$> bundle exec ruby scripts/simple_amqp_manager.rb + +############ +# OVERVIEW +############ +# +# This is a simple user of AmqpConnectionManager (a jam-ruby class), which will continually +# send messages to rabbitmq, and, if it receives them, print them. +# + +############ +# TESTS +############ +# +# Test 1: start with rabbitmq down +# ------ +# * stop rabbitmq +# * run this file +# * start rabbitmq, and messages should be sent/received +# +# Test 2: restart rabbitmq at steady state +# ------ +# * start rabbitmq +# * run this file +# * messages should be sent/received +# * restart rabbitmq +# * once rabbitmq is back up, messages should be sent/received +# + +require 'amqp' +require 'active_record' +require 'jam_db' + +# initialize ActiveRecord's db connection +ActiveRecord::Base.establish_connection(YAML::load(File.open('config/database.yml'))["test"]) + +require 'jam_ruby' + +# initialize logging +Logging.logger.root.level = :debug +Logging.logger.root.appenders = Logging.appenders.stdout + + +log = Logging.logger['SimpleAmqpManager'] + + +include JamRuby + +users_exchange = nil + +EventMachine.run do + + manager = AmqpConnectionManager.new(true, 4, :host => '127.0.0.1', :port => 5672) + manager.connect do |channel| + log.debug "initializing channel with registration to dog topic" + + users_exchange = channel.topic('dogs') + # create user messaging topic + user_topic = channel.queue("", :auto_delete => true) + user_topic.bind(users_exchange, :routing_key => "dog.#") + user_topic.purge + + user_topic.subscribe(:ack => false) do |headers, msg| + log.debug("received message from dog queue: #{msg}") + end + end + + EventMachine.add_periodic_timer(2) do + unless users_exchange.nil? # if we have not connected yet ever, this will be nil + log.debug "sending message: [super secret message]" + users_exchange.publish("[super secret message]", :routing_key => "dog.leg") + end + end +end From d11828241ab361d65044f32582b80142347b3423 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 14 Jul 2013 21:48:16 -0500 Subject: [PATCH 248/274] * VRFS-196, VRFS-195, VRFS-197 - adding messages for server up/server down in response to pings in the message factory. also removing suppression of notification for creator of session (need to talk to Brian about this) --- lib/jam_ruby/amqp/amqp_connection_manager.rb | 4 ++++ lib/jam_ruby/connection_manager.rb | 4 ++-- lib/jam_ruby/message_factory.rb | 12 ++++++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/lib/jam_ruby/amqp/amqp_connection_manager.rb b/lib/jam_ruby/amqp/amqp_connection_manager.rb index fedbab581..644449b64 100644 --- a/lib/jam_ruby/amqp/amqp_connection_manager.rb +++ b/lib/jam_ruby/amqp/amqp_connection_manager.rb @@ -91,6 +91,10 @@ module JamRuby @log.error "Status code : #{connection_close.reply_code}" @log.error "Error message : #{connection_close.reply_text}" end + + def connected? + return @connected + end end diff --git a/lib/jam_ruby/connection_manager.rb b/lib/jam_ruby/connection_manager.rb index 82176e0cc..7b599420e 100644 --- a/lib/jam_ruby/connection_manager.rb +++ b/lib/jam_ruby/connection_manager.rb @@ -225,7 +225,7 @@ SQL end end - def join_music_session(user, client_id, music_session, as_musician, tracks, send_notification = true) + def join_music_session(user, client_id, music_session, as_musician, tracks) connection = nil user_id = user.id music_session_id = music_session.id @@ -243,7 +243,7 @@ SQL if connection.errors.any? raise ActiveRecord::Rollback else - if as_musician && music_session.musician_access && send_notification + if as_musician && music_session.musician_access Notification.send_musician_session_join(music_session, connection, user) Notification.send_friend_session_join(db_conn, connection, user) end diff --git a/lib/jam_ruby/message_factory.rb b/lib/jam_ruby/message_factory.rb index a59322003..82391f4f4 100644 --- a/lib/jam_ruby/message_factory.rb +++ b/lib/jam_ruby/message_factory.rb @@ -64,6 +64,12 @@ return Jampb::ClientMessage.new(:type => ClientMessage::Type::LEAVE_MUSIC_SESSION_ACK, :route_to => CLIENT_TARGET, :leave_music_session_ack => leave_music_session_ack) end + # create a server bad state recovered msg + def server_bad_state_recovered(original_message_id) + recovered = Jampb::ServerBadStateRecovered.new() + return Jampb::ClientMessage.new(:type => ClientMessage::Type::SERVER_BAD_STATE_RECOVERED, :route_to => CLIENT_TARGET, :server_bad_state_recovered => recovered, :in_reply_to => original_message_id) + end + # create a server error def server_generic_error(error_msg) error = Jampb::ServerGenericError.new(:error_msg => error_msg) @@ -82,6 +88,12 @@ return Jampb::ClientMessage.new(:type => ClientMessage::Type::SERVER_PERMISSION_ERROR, :route_to => CLIENT_TARGET, :server_permission_error => error, :in_reply_to => original_message_id) end + # create a server bad state error + def server_bad_state_error(original_message_id, error_msg) + error = Jampb::ServerBadStateError.new(:error_msg => error_msg) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::SERVER_BAD_STATE_ERROR, :route_to => CLIENT_TARGET, :server_bad_state_error => error, :in_reply_to => original_message_id) + end + # create a friend joined session message def friend_session_join(session_id, user_id, username, photo_url) join = Jampb::FriendSessionJoin.new(:session_id => session_id, :user_id => user_id, :username => username, :photo_url => photo_url) From 6ab57b4313e066a1c486d4350718978e6cd63335 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Fri, 19 Jul 2013 16:37:14 -0500 Subject: [PATCH 249/274] * updating to point to int.jamkazam.com VRFS-424 --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 38039bed6..0a2f8a710 100644 --- a/Gemfile +++ b/Gemfile @@ -1,7 +1,7 @@ #ruby=1.9.3 source 'http://rubygems.org' unless ENV["LOCAL_DEV"] == "1" - source 'https://jamjam:blueberryjam@www.jamkazam.com/gems/' + source 'https://jamjam:blueberryjam@int.jamkazam.com/gems/' end # Look for $WORKSPACE, otherwise use "workspace" as dev path. From 7bdca213fa9c158400e75d14dc2ba3caf3d22d38 Mon Sep 17 00:00:00 2001 From: Jonathan Kolyer Date: Tue, 23 Jul 2013 15:20:39 -0500 Subject: [PATCH 250/274] vrfs-310: added removed_music_session method, and after_destroy callback --- lib/jam_ruby/models/music_session.rb | 4 ++++ lib/jam_ruby/models/music_session_history.rb | 12 +++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/jam_ruby/models/music_session.rb b/lib/jam_ruby/models/music_session.rb index 31dbe5a53..828564905 100644 --- a/lib/jam_ruby/models/music_session.rb +++ b/lib/jam_ruby/models/music_session.rb @@ -23,6 +23,10 @@ module JamRuby after_save :require_at_least_one_genre, :limit_max_genres + after_destroy do |obj| + JamRuby::MusicSessionHistory.removed_music_session(obj.user_id, obj.id) + end + validates :description, :presence => true validates :fan_chat, :inclusion => {:in => [true, false]} validates :fan_access, :inclusion => {:in => [true, false]} diff --git a/lib/jam_ruby/models/music_session_history.rb b/lib/jam_ruby/models/music_session_history.rb index 7c83e9e4f..65e319984 100644 --- a/lib/jam_ruby/models/music_session_history.rb +++ b/lib/jam_ruby/models/music_session_history.rb @@ -56,5 +56,15 @@ module JamRuby session_history.genres = music_session.genres.map { |g| g.id }.join '|' session_history.save! end + + def self.removed_music_session(user_id, session_id) + hist = self + .where(:user_id => user_id) + .where(:music_session_id => session_id) + .limit(1) + .first + hist.update_attribute(:session_removed_at, Time.now) if hist + end + end -end \ No newline at end of file +end From 25fcaf79ca2b1121df5621d4cd4ac84b3e0658fb Mon Sep 17 00:00:00 2001 From: Jonathan Kolyer Date: Tue, 23 Jul 2013 15:22:03 -0500 Subject: [PATCH 251/274] vrfs-310: moved the delete_if_empty method into session_check, and added called to JamRuby::MusicSessionHistory.removed_music_session when the music session is deleted (refactoring should call this automatically from MusicSession callback) --- lib/jam_ruby/connection_manager.rb | 47 +++++++++++++++--------------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/lib/jam_ruby/connection_manager.rb b/lib/jam_ruby/connection_manager.rb index 7b599420e..f400c780d 100644 --- a/lib/jam_ruby/connection_manager.rb +++ b/lib/jam_ruby/connection_manager.rb @@ -178,29 +178,6 @@ SQL end end - def destroy_if_empty(conn, music_session_id) - - num_participants = nil - conn.exec("SELECT count(*) FROM connections WHERE music_session_id = $1", [music_session_id]) do |result| - num_participants = result.getvalue(0, 0).to_i - end - - if num_participants == 0 - # delete the music_session - conn.exec("DELETE from music_sessions WHERE id = $1", [music_session_id]) do |result| - if result.cmd_tuples == 0 - # no music session deleted. do nothing - elsif result.cmd_tuples == 1 - # music session deleted! - @log.debug("deleted music session #{music_session_id}") - else - @log.error("music_sessions table data integrity violation; multiple rows found with music_session_id=#{music_session_id}") - raise Exception, "music_sessions table data integrity violation; multiple rows found with music_session_id=#{music_session_id}" - end - end - end - end - def check_already_session(conn, client_id) conn.exec("SELECT music_session_id FROM connections WHERE client_id = $1", [client_id]) do |result| if result.num_tuples == 1 @@ -221,7 +198,29 @@ SQL unless previous_music_session_id.nil? # TODO: send notification to friends that this user left this session? @log.debug("user #{user_id} left music_session #{previous_music_session_id}") - destroy_if_empty(conn, previous_music_session_id) + + # destroy the music_session if it's empty + num_participants = nil + conn.exec("SELECT count(*) FROM connections WHERE music_session_id = $1", + [previous_music_session_id]) do |result| + num_participants = result.getvalue(0, 0).to_i + end + if num_participants == 0 + # delete the music_session + conn.exec("DELETE from music_sessions WHERE id = $1", + [previous_music_session_id]) do |result| + if result.cmd_tuples == 1 + # music session deleted! + @log.debug("deleted music session #{previous_music_session_id}") + JamRuby::MusicSessionHistory.removed_music_session(user_id, + previous_music_session_id) + elsif 1 < result.cmd_tuples + msg = "music_sessions table data integrity violation; multiple rows found with music_session_id=#{previous_music_session_id}" + @log.error(msg) + raise Exception, msg + end + end + end end end From 125898e01117f69ca8d056852892f39442cacba6 Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Wed, 24 Jul 2013 23:23:30 -0700 Subject: [PATCH 252/274] Added profanity filter and tests --- config/profanity.yml | 202 ++++++++++++++++++++++++++++ lib/jam_ruby.rb | 1 + lib/jam_ruby/lib/profanity.rb | 24 ++++ spec/jam_ruby/lib/profanity_spec.rb | 31 +++++ 4 files changed, 258 insertions(+) create mode 100644 config/profanity.yml create mode 100644 lib/jam_ruby/lib/profanity.rb create mode 100644 spec/jam_ruby/lib/profanity_spec.rb diff --git a/config/profanity.yml b/config/profanity.yml new file mode 100644 index 000000000..35767f0e0 --- /dev/null +++ b/config/profanity.yml @@ -0,0 +1,202 @@ +# Note: I got this from here: https://github.com/intridea/profanity_filter/blob/master/config/dictionary.yml +# I doubt that this list can be copyrighted +# the filter currently checks for words that are 3 or more characters. +--- +ass: "*ss" +asses: "*ss*s" +asshole: "*ssh*l*" +assholes: "*ssh*l*s" +bastard: b*st*rd +beastial: b**st**l +beastiality: b**st**l*ty +beastility: b**st*l*ty +bestial: b*st**l +bestiality: b*st**l*ty +bitch: b*tch +bitcher: b*tch*r +bitchers: b*tch*rs +bitches: b*tch*s +bitchin: b*tch*n +bitching: b*tch*ng +blowjob: bl*wj*b +blowjobs: bl*wj*bs +bullshit: b*llsh*t +clit: cl*t +cock: c*ck +cocks: c*cks +cocksuck: c*cks*ck +cocksucked: c*cks*ck*d +cocksucker: c*cks*ck*r +cocksucking: c*cks*ck*ng +cocksucks: c*cks*cks +cum: c*m +cummer: c*mm*r +cumming: c*mm*ng +cums: c*ms +cumshot: c*msh*t +cunillingus: c*n*ll*ng*s +cunnilingus: c*nn*l*ng*s +cunt: c*nt +cuntlick: c*ntl*ck +cuntlicker: c*ntl*ck*r +cuntlicking: c*ntl*ck*ng +cunts: c*nts +cyberfuc: cyb*rf*c +cyberfuck: cyb*rf*ck +cyberfucked: cyb*rf*ck*d +cyberfucker: cyb*rf*ck*r +cyberfuckers: cyb*rf*ck*rs +cyberfucking: cyb*rf*ck*ng +damn: d*mn +dildo: d*ld* +dildos: d*ld*s +dick: d*ck +dink: d*nk +dinks: d*nks +ejaculate: "*j*c*l*t*" +ejaculated: "*j*c*l*t*d" +ejaculates: "*j*c*l*t*s" +ejaculating: "*j*c*l*t*ng" +ejaculatings: "*j*c*l*t*ngs" +ejaculation: "*j*c*l*t**n" +fag: f*g +fagging: f*gg*ng +faggot: f*gg*t +faggs: f*ggs +fagot: f*g*t +fagots: f*g*ts +fags: f*gs +fart: f*rt +farted: f*rt*d +farting: f*rt*ng +fartings: f*rt*ngs +farts: f*rts +farty: f*rty +felatio: f*l*t** +fellatio: f*ll*t** +fingerfuck: f*ng*rf*ck +fingerfucked: f*ng*rf*ck*d +fingerfucker: f*ng*rf*ck*r +fingerfuckers: f*ng*rf*ck*rs +fingerfucking: f*ng*rf*ck*ng +fingerfucks: f*ng*rf*cks +fistfuck: f*stf*ck +fistfucked: f*stf*ck*d +fistfucker: f*stf*ck*r +fistfuckers: f*stf*ck*rs +fistfucking: f*stf*ck*ng +fistfuckings: f*stf*ck*ngs +fistfucks: f*stf*cks +fuck: f*ck +fucked: f*ck*d +fucker: f*ck*r +fuckers: f*ck*rs +fuckin: f*ck*n +fucking: f*ck*ng +fuckings: f*ck*ngs +fuckme: f*ckm* +fucks: f*cks +fuk: f*k +fuks: f*ks +gangbang: g*ngb*ng +gangbanged: g*ngb*ng*d +gangbangs: g*ngb*ngs +gaysex: g*ys*x +goddamn: g*dd*mn +hardcoresex: h*rdc*r*s*x +hell: h*ll +horniest: h*rn**st +horny: h*rny +hotsex: h*ts*x +jism: j*sm +jiz: j*z +jizm: j*zm +kock: k*ck +kondum: k*nd*m +kondums: k*nd*ms +kum: k*m +kumer: k*mm*r +kummer: k*mm*r +kumming: k*mm*ng +kums: k*ms +kunilingus: k*n*l*ng*s +lust: l*st +lusting: l*st*ng +mothafuck: m*th*f*ck +mothafucka: m*th*f*ck* +mothafuckas: m*th*f*ck*s +mothafuckaz: m*th*f*ck*z +mothafucked: m*th*f*ck*d +mothafucker: m*th*f*ck*r +mothafuckers: m*th*f*ck*rs +mothafuckin: m*th*f*ck*n +mothafucking: m*th*f*ck*ng +mothafuckings: m*th*f*ck*ngs +mothafucks: m*th*f*cks +motherfuck: m*th*rf*ck +motherfucked: m*th*rf*ck*d +motherfucker: m*th*rf*ck*r +motherfuckers: m*th*rf*ck*rs +motherfuckin: m*th*rf*ck*n +motherfucking: m*th*rf*ck*ng +motherfuckings: m*th*rf*ck*ngs +motherfucks: m*th*rf*cks +niger: n*gg*r +nigger: n*gg*r +niggers: n*gg*rs +orgasim: "*rg*s*m" +orgasims: "*rg*s*ms" +orgasm: "*rg*sm" +orgasms: "*rg*sms" +phonesex: ph*n*s*x +phuk: ph*k +phuked: ph*k*d +phuking: ph*k*ng +phukked: ph*kk*d +phukking: ph*kk*ng +phuks: ph*ks +phuq: ph*q +pis: p*ss +piss: p*ss +pisser: p*ss*r +pissed: p*ss*d +pisser: p*ss*r +pissers: p*ss*rs +pises: p*ss*s +pisses: p*ss*s +pisin: p*ss*n +pissin: p*ss*n +pising: p*ss*ng +pissing: p*ss*ng +pisof: p*ss*ff +pissoff: p*ss*ff +porn: p*rn +porno: p*rn* +pornography: p*rn*gr*phy +pornos: p*rn*s +prick: pr*ck +pricks: pr*cks +pussies: p*ss**s +pusies: p*ss**s +pussy: p*ssy +pusy: p*ssy +pussys: p*ssys +pusys: p*ssys +shit: sh*t +shited: sh*t*d +shitfull: sh*tf*ll +shiting: sh*t*ng +shitings: sh*t*ngs +shits: sh*ts +shitted: sh*tt*d +shitter: sh*tt*r +shitters: sh*tt*rs +shitting: sh*tt*ng +shittings: sh*tt*ngs +shitty: sh*tty +shity: sh*tty +slut: sl*t +sluts: sl*ts +smut: sm*t +spunk: sp*nk +twat: tw*t diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index fdaa4cd0e..19e638c35 100755 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -30,6 +30,7 @@ require "jam_ruby/app/uploaders/perf_data_uploader" require "jam_ruby/lib/desk_multipass" require "jam_ruby/lib/s3_util" require "jam_ruby/lib/s3_manager" +require "jam_ruby/lib/profanity" require "jam_ruby/amqp/amqp_connection_manager" require "jam_ruby/message_factory" require "jam_ruby/models/max_mind_geo" diff --git a/lib/jam_ruby/lib/profanity.rb b/lib/jam_ruby/lib/profanity.rb new file mode 100644 index 000000000..5060c0746 --- /dev/null +++ b/lib/jam_ruby/lib/profanity.rb @@ -0,0 +1,24 @@ +module JamRuby + class Profanity + @@dictionary_file = File.join('config/profanity.yml') + @@dictionary = nil + + def self.dictionary + @@dictionary ||= YAML.load_file(@@dictionary_file) + end + + def self.check_word(word) + dictionary.include?(word.downcase) + end + + def self.is_profane?(text) + return false if text.nil? + + text.split(/\W+/).each do |word| + return true if check_word(word) + end + return false + end + end +end + diff --git a/spec/jam_ruby/lib/profanity_spec.rb b/spec/jam_ruby/lib/profanity_spec.rb new file mode 100644 index 000000000..4da77b657 --- /dev/null +++ b/spec/jam_ruby/lib/profanity_spec.rb @@ -0,0 +1,31 @@ +require 'spec_helper' + +describe Profanity do + + describe "profanity_filter" do + + it "can handle a nil input" do + Profanity.is_profane?(nil).should be_false + end + + it "can handle a blank input" do + Profanity.is_profane?('').should be_false + end + + it "can handle a clean input" do + Profanity.is_profane?('you are a clean input').should be_false + end + + it "can handle a profane input" do + Profanity.is_profane?('fuck you!').should be_true + end + + it "is not fooled by punctuation" do + Profanity.is_profane?('fuck-you!').should be_true + Profanity.is_profane?('???$$fuck-you!').should be_true + Profanity.is_profane?('--!fuck-you!').should be_true + end + end + +end + From e6dd4fea7adc3d0fcfd166c412164b167c066a27 Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Fri, 26 Jul 2013 01:07:24 -0700 Subject: [PATCH 253/274] Added profanity filter - VRFS-219 --- lib/jam_ruby/lib/profanity.rb | 35 ++++++++++++++-------- lib/jam_ruby/models/band.rb | 2 ++ lib/jam_ruby/models/claimed_recording.rb | 2 ++ lib/jam_ruby/models/friend_request.rb | 3 +- lib/jam_ruby/models/invited_user.rb | 2 +- lib/jam_ruby/models/join_request.rb | 4 +-- lib/jam_ruby/models/music_session.rb | 2 +- lib/jam_ruby/models/user.rb | 4 +-- spec/jam_ruby/models/invited_user_spec.rb | 12 ++++++++ spec/jam_ruby/models/join_request_spec.rb | 11 ++++++- spec/jam_ruby/models/music_session_spec.rb | 9 ++++++ spec/jam_ruby/models/user_spec.rb | 15 ++++++++++ 12 files changed, 81 insertions(+), 20 deletions(-) diff --git a/lib/jam_ruby/lib/profanity.rb b/lib/jam_ruby/lib/profanity.rb index 5060c0746..4203f839d 100644 --- a/lib/jam_ruby/lib/profanity.rb +++ b/lib/jam_ruby/lib/profanity.rb @@ -1,24 +1,35 @@ module JamRuby - class Profanity - @@dictionary_file = File.join('config/profanity.yml') - @@dictionary = nil - def self.dictionary - @@dictionary ||= YAML.load_file(@@dictionary_file) - end + class Profanity + @@dictionary_file = File.join('config/profanity.yml') + @@dictionary = nil + + def self.dictionary + @@dictionary ||= YAML.load_file(@@dictionary_file) + end def self.check_word(word) - dictionary.include?(word.downcase) - end + dictionary.include?(word.downcase) + end def self.is_profane?(text) return false if text.nil? - text.split(/\W+/).each do |word| - return true if check_word(word) - end - return false + text.split(/\W+/).each do |word| + return true if check_word(word) + end + return false end end + + +end + +# This needs to be outside the module to work. +class NoProfanityValidator < ActiveModel::EachValidator + # implement the method called during validation + def validate_each(record, attribute, value) + record.errors[attribute] << 'Cannot contain profanity' if Profanity.is_profane?(value) + end end diff --git a/lib/jam_ruby/models/band.rb b/lib/jam_ruby/models/band.rb index d74ca4911..011e4b599 100644 --- a/lib/jam_ruby/models/band.rb +++ b/lib/jam_ruby/models/band.rb @@ -5,6 +5,8 @@ module JamRuby self.primary_key = 'id' + validates :biography, no_profanity: true + # musicians has_many :band_musicians, :class_name => "JamRuby::BandMusician" has_many :users, :through => :band_musicians, :class_name => "JamRuby::User" diff --git a/lib/jam_ruby/models/claimed_recording.rb b/lib/jam_ruby/models/claimed_recording.rb index 6c6e9b19c..5e8bb963d 100644 --- a/lib/jam_ruby/models/claimed_recording.rb +++ b/lib/jam_ruby/models/claimed_recording.rb @@ -1,6 +1,8 @@ module JamRuby class ClaimedRecording < ActiveRecord::Base + validates :name, no_profanity: true + belongs_to :recording, :class_name => "JamRuby::Recording", :inverse_of => :claimed_recordings belongs_to :user, :class_name => "JamRuby::User", :inverse_of => :claimed_recordings belongs_to :genre, :class_name => "JamRuby::Genre" diff --git a/lib/jam_ruby/models/friend_request.rb b/lib/jam_ruby/models/friend_request.rb index 348da4162..fa15e36e8 100644 --- a/lib/jam_ruby/models/friend_request.rb +++ b/lib/jam_ruby/models/friend_request.rb @@ -11,6 +11,7 @@ module JamRuby validates :user_id, :presence => true validates :friend_id, :presence => true #validates :status, :inclusion => {:in => STATUS} + validates :message, no_profanity: true def to_s return "#{self.id} => #{self.user.to_s}:#{self.friend.to_s}" @@ -67,4 +68,4 @@ module JamRuby return friend_request end end -end \ No newline at end of file +end diff --git a/lib/jam_ruby/models/invited_user.rb b/lib/jam_ruby/models/invited_user.rb index 456644491..47069c43e 100644 --- a/lib/jam_ruby/models/invited_user.rb +++ b/lib/jam_ruby/models/invited_user.rb @@ -16,7 +16,7 @@ module JamRuby validates :email, :presence => true, format: {with: VALID_EMAIL_REGEX} validates :autofriend, :inclusion => {:in => [nil, true, false]} validates :invitation_code, :presence => true - validates :note, length: {maximum: 400} # 400 == arbitrary. + validates :note, length: {maximum: 400}, no_profanity: true # 400 == arbitrary. validate :valid_personalized_invitation validate :not_accepted_twice diff --git a/lib/jam_ruby/models/join_request.rb b/lib/jam_ruby/models/join_request.rb index faa8d04fc..4561f03c7 100644 --- a/lib/jam_ruby/models/join_request.rb +++ b/lib/jam_ruby/models/join_request.rb @@ -11,7 +11,7 @@ module JamRuby validates :user, :presence => true validates :music_session, :presence => true - validates :text, presence: false, length: {maximum: 140} # arbitrary decision of 140. the database is at 2000 max on this field + validates :text, presence: false, no_profanity: true, length: {maximum: 140} # arbitrary decision of 140. the database is at 2000 max on this field validates_uniqueness_of :user_id, :scope => :music_session_id @@ -41,4 +41,4 @@ module JamRuby return JoinRequest.find(id, :conditions => ["user_id = ? OR music_session_id IN (select music_session_id from connections WHERE user_id = ?)", user.id, user.id]) end end -end \ No newline at end of file +end diff --git a/lib/jam_ruby/models/music_session.rb b/lib/jam_ruby/models/music_session.rb index 828564905..ba4054dff 100644 --- a/lib/jam_ruby/models/music_session.rb +++ b/lib/jam_ruby/models/music_session.rb @@ -27,7 +27,7 @@ module JamRuby JamRuby::MusicSessionHistory.removed_music_session(obj.user_id, obj.id) end - validates :description, :presence => true + validates :description, :presence => true, :no_profanity => true validates :fan_chat, :inclusion => {:in => [true, false]} validates :fan_access, :inclusion => {:in => [true, false]} validates :approval_required, :inclusion => {:in => [true, false]} diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 15ecc4e51..f7ba3e803 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -108,8 +108,8 @@ module JamRuby before_save :create_remember_token, :if => :should_validate_password? before_save :stringify_avatar_info , :if => :updating_avatar - validates :first_name, presence: true, length: {maximum: 50} - validates :last_name, presence: true, length: {maximum: 50} + validates :first_name, presence: true, length: {maximum: 50}, no_profanity: true + validates :last_name, presence: true, length: {maximum: 50}, no_profanity: true VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i validates :email, presence: true, format: {with: VALID_EMAIL_REGEX} validates :update_email, presence: true, format: {with: VALID_EMAIL_REGEX}, :if => :updating_email diff --git a/spec/jam_ruby/models/invited_user_spec.rb b/spec/jam_ruby/models/invited_user_spec.rb index c5b6ac392..b3de880b5 100644 --- a/spec/jam_ruby/models/invited_user_spec.rb +++ b/spec/jam_ruby/models/invited_user_spec.rb @@ -90,4 +90,16 @@ describe InvitedUser do invited_users.length.should == 1 invited_users[0].should == invited_user end + + it 'should not allow note to have profanity' do + + user1 = FactoryGirl.create(:user) + + # create the invitation from the end-user + invited_user = FactoryGirl.create(:invited_user, :sender => user1) + invited_user.note = 'fuck you' + invited_user.save + invited_user.valid?.should be_false + end + end diff --git a/spec/jam_ruby/models/join_request_spec.rb b/spec/jam_ruby/models/join_request_spec.rb index c13d890db..14412556f 100644 --- a/spec/jam_ruby/models/join_request_spec.rb +++ b/spec/jam_ruby/models/join_request_spec.rb @@ -38,5 +38,14 @@ describe JoinRequest do join_request2.save.should be_false join_request2.errors.get(:user_id) == ["has already been taken"] - end + end + + it "cant contain profanity in the text" do + user1 = FactoryGirl.create(:user) + music_session = FactoryGirl.create(:music_session, :creator => user1) + music_session_member1 = FactoryGirl.create(:connection, :user => user1, :music_session => music_session) + join_request = JoinRequest.new(:user => user1, :music_session => music_session, :text => "fuck you") + join_request.save + join_request.valid?.should be_false + end end diff --git a/spec/jam_ruby/models/music_session_spec.rb b/spec/jam_ruby/models/music_session_spec.rb index a4dc8d611..6a06555d2 100644 --- a/spec/jam_ruby/models/music_session_spec.rb +++ b/spec/jam_ruby/models/music_session_spec.rb @@ -360,4 +360,13 @@ describe MusicSession do music_session.valid?.should be_false music_session.errors["legal_terms"].should == ["is not included in the list"] end + + it "cannot have profanity in the description" do + user1 = FactoryGirl.create(:user) + music_session = FactoryGirl.build(:music_session, :creator => user1, :legal_terms=> false, :description => "fuck you") + music_session.save + music_session.valid?.should be_false + end + end + diff --git a/spec/jam_ruby/models/user_spec.rb b/spec/jam_ruby/models/user_spec.rb index 122378e3f..a0f7cd418 100644 --- a/spec/jam_ruby/models/user_spec.rb +++ b/spec/jam_ruby/models/user_spec.rb @@ -42,6 +42,7 @@ describe User do it { should be_admin } end + describe "when first name is not present" do before { @user.first_name = " " } @@ -68,6 +69,20 @@ describe User do it { should_not be_valid } end + describe "first or last name cant have profanity" do + it "should not let the first name have profanity" do + @user.first_name = "fuck you" + @user.save + @user.should_not be_valid + end + + it "should not let the last name have profanity" do + @user.last_name = "fuck you" + @user.save + @user.should_not be_valid + end + end + describe "when email format is invalid" do it "should be invalid" do addresses = %w[user@foo,com user_at_foo.org example.user@foo.] From a58db24d15fbf5f104f8a21ceed6ec4544ccbe9c Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Wed, 31 Jul 2013 18:46:24 -0700 Subject: [PATCH 254/274] added crash dumps and tests --- lib/jam_ruby.rb | 1 + lib/jam_ruby/lib/profanity.rb | 7 ++++++- lib/jam_ruby/models/crash_dump.rb | 10 ++++++++++ spec/jam_ruby/models/crash_dump_spec.rb | 19 +++++++++++++++++++ 4 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 lib/jam_ruby/models/crash_dump.rb create mode 100644 spec/jam_ruby/models/crash_dump_spec.rb diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index 19e638c35..a7aafe72c 100755 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -71,6 +71,7 @@ require "jam_ruby/models/recording" require "jam_ruby/models/recorded_track" require "jam_ruby/models/mix" require "jam_ruby/models/claimed_recording" +require "jam_ruby/models/crash_dump" include Jampb diff --git a/lib/jam_ruby/lib/profanity.rb b/lib/jam_ruby/lib/profanity.rb index 4203f839d..6caaead09 100644 --- a/lib/jam_ruby/lib/profanity.rb +++ b/lib/jam_ruby/lib/profanity.rb @@ -5,7 +5,12 @@ module JamRuby @@dictionary = nil def self.dictionary - @@dictionary ||= YAML.load_file(@@dictionary_file) + if File.exists? @@dictionary_file + @@dictionary ||= YAML.load_file(@@dictionary_file) + else + @@dictionary = [] + end + @@dictionary end def self.check_word(word) diff --git a/lib/jam_ruby/models/crash_dump.rb b/lib/jam_ruby/models/crash_dump.rb new file mode 100644 index 000000000..fc60e49e9 --- /dev/null +++ b/lib/jam_ruby/models/crash_dump.rb @@ -0,0 +1,10 @@ +module JamRuby + class CrashDump < ActiveRecord::Base + + self.table_name = "crash_dumps" + + self.primary_key = 'id' + + validates :client_type, presence: true + end +end diff --git a/spec/jam_ruby/models/crash_dump_spec.rb b/spec/jam_ruby/models/crash_dump_spec.rb new file mode 100644 index 000000000..fa253c6f3 --- /dev/null +++ b/spec/jam_ruby/models/crash_dump_spec.rb @@ -0,0 +1,19 @@ +require 'spec_helper' + +describe CrashDump do + before do + end + + it "should fail to save a crash dump without a client_type" do + CrashDump.new(:client_type => "").should_not be_valid + end + + it "should be able to save a crash dump with JUST a client_type" do + @cd = CrashDump.new + @cd.client_type = "Win32" + @cd.should be_valid + @cd.save + + CrashDump.first.id.should == @cd.id + end +end From 9d2b51c66e948b3dcb3040c3f6d90a2405545e47 Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Wed, 31 Jul 2013 19:05:38 -0700 Subject: [PATCH 255/274] added uri --- lib/jam_ruby/models/crash_dump.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/jam_ruby/models/crash_dump.rb b/lib/jam_ruby/models/crash_dump.rb index fc60e49e9..f48de2454 100644 --- a/lib/jam_ruby/models/crash_dump.rb +++ b/lib/jam_ruby/models/crash_dump.rb @@ -6,5 +6,11 @@ module JamRuby self.primary_key = 'id' validates :client_type, presence: true + + before_validation(:on => :create) do + self.created_at ||= Time.now + self.id = SecureRandom.uuid + self.uri = "dump/#{self.id}-#{self.created_at.to_i}" + end end end From 4cc679becd0fb3b7cf6c242f2044840b524f69e1 Mon Sep 17 00:00:00 2001 From: Jonathan Kolyer Date: Tue, 30 Jul 2013 12:26:26 -0500 Subject: [PATCH 256/274] vrfs-268: tweaking music_session_*history associations; added life cycle end for music_session_user_history --- lib/jam_ruby/connection_manager.rb | 3 +- lib/jam_ruby/models/band.rb | 1 + lib/jam_ruby/models/music_session_history.rb | 41 ++++++++++++++++++- .../models/music_session_perf_data.rb | 8 +++- .../models/music_session_user_history.rb | 39 ++++++++++++++++-- lib/jam_ruby/models/user.rb | 4 +- 6 files changed, 86 insertions(+), 10 deletions(-) diff --git a/lib/jam_ruby/connection_manager.rb b/lib/jam_ruby/connection_manager.rb index f400c780d..87ffc4016 100644 --- a/lib/jam_ruby/connection_manager.rb +++ b/lib/jam_ruby/connection_manager.rb @@ -329,9 +329,10 @@ SQL if result.cmd_tuples == 1 @log.debug("disassociated music_session with connection for client_id=#{client_id}, user_id=#{user_id}") + JamRuby::MusicSessionUserHistory.removed_music_session(user_id, music_session_id) session_checks(conn, previous_music_session_id, user_id) - Notification.send_musician_session_depart(music_session, connection, user) + elsif result.cmd_tuples == 0 @log.debug "leave_music_session no connection found with client_id=#{client_id}" raise ActiveRecord::RecordNotFound diff --git a/lib/jam_ruby/models/band.rb b/lib/jam_ruby/models/band.rb index 011e4b599..05f0d4645 100644 --- a/lib/jam_ruby/models/band.rb +++ b/lib/jam_ruby/models/band.rb @@ -32,6 +32,7 @@ module JamRuby # music_sessions has_many :music_sessions, :class_name => "JamRuby::MusicSession", :foreign_key => "band_id" + has_many :music_session_history, :class_name => "JamRuby::MusicSessionHistory", :foreign_key => "band_id", :inverse_of => :band def liker_count return self.likers.size diff --git a/lib/jam_ruby/models/music_session_history.rb b/lib/jam_ruby/models/music_session_history.rb index 65e319984..fe8e7d082 100644 --- a/lib/jam_ruby/models/music_session_history.rb +++ b/lib/jam_ruby/models/music_session_history.rb @@ -5,7 +5,34 @@ module JamRuby self.primary_key = 'id' - has_many :music_session_user_histories, :class_name => "JamRuby::MusicSessionUserHistory", :foreign_key => "music_session_id" + # for some reason the association is not working, i suspect has to do with the foreign key + def music_session_user_histories + @msuh ||= JamRuby::MusicSessionUserHistory + .where(:music_session_id => self.music_session_id) + .order('created_at DESC') + end + # has_many(:music_session_user_histories, + # :class_name => "JamRuby::MusicSessionUserHistory", + # :foreign_key => :music_session_id, + # :order => 'created_at DESC', + # :inverse_of => :music_session_history) + + has_one(:perf_data, + :class_name => "JamRuby::MusicSessionPerfData", + :foreign_key => "music_session_id", + :inverse_of => :music_session) + + belongs_to(:user, + :class_name => 'JamRuby::User', + :foreign_key => :user_id, + :inverse_of => :music_session_histories) + + belongs_to(:band, + :class_name => 'JamRuby::Band', + :foreign_key => :band_id, + :inverse_of => :music_session_history) + + GENRE_SEPARATOR = '|' def self.index(current_user, user_id, band_id = nil, genre = nil) hide_private = false @@ -53,7 +80,7 @@ module JamRuby session_history.description = music_session.description unless music_session.description.nil? session_history.user_id = music_session.creator.id session_history.band_id = music_session.band.id unless music_session.band.nil? - session_history.genres = music_session.genres.map { |g| g.id }.join '|' + session_history.genres = music_session.genres.map { |g| g.id }.join GENRE_SEPARATOR session_history.save! end @@ -64,6 +91,16 @@ module JamRuby .limit(1) .first hist.update_attribute(:session_removed_at, Time.now) if hist + JamRuby::MusicSessionUserHistory.removed_music_session(user_id, session_id) + end + + def duration_minutes + end_time = self.session_removed_at || Time.now + (end_time - self.created_at) / 60.0 + end + + def perf_uri + self.perf_data.try(:uri) end end diff --git a/lib/jam_ruby/models/music_session_perf_data.rb b/lib/jam_ruby/models/music_session_perf_data.rb index 16ce5e3bb..1133cb28e 100644 --- a/lib/jam_ruby/models/music_session_perf_data.rb +++ b/lib/jam_ruby/models/music_session_perf_data.rb @@ -7,9 +7,12 @@ module JamRuby attr_accessible :uri - belongs_to :music_session, :class_name => "JamRuby::MusicSessionHistory" + belongs_to(:music_session, + :class_name => "JamRuby::MusicSessionHistory", + :foreign_key => :music_session_id, + :inverse_of => :perf_data) - #mount_uploader :uri, PerfDataUploader + # mount_uploader :uri, PerfDataUploader validates :music_session, :presence => true validates :client_id, :presence => true @@ -21,4 +24,5 @@ module JamRuby self.uri = "perf_data/#{self.music_session_id}/#{self.client_id}-#{self.created_at.to_i}" end end + end diff --git a/lib/jam_ruby/models/music_session_user_history.rb b/lib/jam_ruby/models/music_session_user_history.rb index 5517e6f0f..f4de39dc1 100644 --- a/lib/jam_ruby/models/music_session_user_history.rb +++ b/lib/jam_ruby/models/music_session_user_history.rb @@ -5,8 +5,22 @@ module JamRuby self.primary_key = 'id' - has_many :users, :foreign_key => "user_id", :class_name => "JamRuby::User" - belongs_to :musician_session_history, :class_name => "JamRuby::MusicSessionHistory", :foreign_key => "music_session_id" + belongs_to(:user, + :class_name => "JamRuby::User", + :foreign_key => "user_id", + :inverse_of => :music_session_user_histories) + + # for some reason the association is not working, i suspect has to do with the foreign key + def music_session_history + @msh ||= JamRuby::MusicSessionHistory + .where(:music_session_id => self.music_session_id) + .limit(1) + .first + end + # belongs_to(:music_session_history, + # :class_name => "JamRuby::MusicSessionHistory", + # :foreign_key => :music_session_id, + # :inverse_of => :music_session_user_histories) def self.save(music_session_id, user_id, client_id) session_user_history = MusicSessionUserHistory.new() @@ -15,5 +29,24 @@ module JamRuby session_user_history.client_id = client_id session_user_history.save end + + def user_email + self.user ? self.user.email : '' + end + + def duration_minutes + end_time = self.session_removed_at || Time.now + (end_time - self.created_at) / 60.0 + end + + def self.removed_music_session(user_id, session_id) + hist = self + .where(:user_id => user_id) + .where(:music_session_id => session_id) + .limit(1) + .first + hist.update_attribute(:session_removed_at, Time.now) if hist + end + end -end \ No newline at end of file +end diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index f7ba3e803..025decdf2 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -93,8 +93,8 @@ module JamRuby has_many :sent_band_invitations, :inverse_of => :sender, :foreign_key => "creator_id", :class_name => "JamRuby::BandInvitation" # session history - has_many :music_session_histories, :foreign_key => "user_id", :class_name => "JamRuby::MusicSessionHistory" - has_many :music_session_user_histories, :foreign_key => "user_id", :class_name => "JamRuby::MusicSessionUserHistory" + has_many :music_session_histories, :foreign_key => "user_id", :class_name => "JamRuby::MusicSessionHistory", :inverse_of => :user + has_many :music_session_user_histories, :foreign_key => "user_id", :class_name => "JamRuby::MusicSessionUserHistory", :inverse_of => :user # saved tracks has_many :recorded_tracks, :foreign_key => "user_id", :class_name => "JamRuby::RecordedTrack", :inverse_of => :user From 43acebcd308383cc965f99bab5487a3a6d9ac3eb Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Tue, 30 Jul 2013 21:59:09 -0400 Subject: [PATCH 257/274] remove hard-coded year --- .../app/views/jam_ruby/user_mailer/welcome_message.html.erb | 2 +- lib/jam_ruby/app/views/layouts/from_user_mailer.text.erb | 2 +- lib/jam_ruby/app/views/layouts/user_mailer.text.erb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.html.erb b/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.html.erb index 06d6c928a..0358cb78d 100644 --- a/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.html.erb +++ b/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.html.erb @@ -1,4 +1,4 @@ <% provide(:title, 'Welcome to Jamkazam') %> -

Welcome to Jamkazam, <%= @user.first_name %> !

+

Welcome to Jamkazam, <%= @user.first_name %>!

To confirm this email address, please go to the signup confirmation page..

diff --git a/lib/jam_ruby/app/views/layouts/from_user_mailer.text.erb b/lib/jam_ruby/app/views/layouts/from_user_mailer.text.erb index bc0f816eb..4dea588f2 100644 --- a/lib/jam_ruby/app/views/layouts/from_user_mailer.text.erb +++ b/lib/jam_ruby/app/views/layouts/from_user_mailer.text.erb @@ -3,4 +3,4 @@ This email was sent to you because you have an account at Jamkazam / http://www.jamkazam.com. -Copyright 2013 JamKazam, Inc. All rights reserved. \ No newline at end of file +Copyright <%= Time.now.year %> JamKazam, Inc. All rights reserved. \ No newline at end of file diff --git a/lib/jam_ruby/app/views/layouts/user_mailer.text.erb b/lib/jam_ruby/app/views/layouts/user_mailer.text.erb index bc0f816eb..4dea588f2 100644 --- a/lib/jam_ruby/app/views/layouts/user_mailer.text.erb +++ b/lib/jam_ruby/app/views/layouts/user_mailer.text.erb @@ -3,4 +3,4 @@ This email was sent to you because you have an account at Jamkazam / http://www.jamkazam.com. -Copyright 2013 JamKazam, Inc. All rights reserved. \ No newline at end of file +Copyright <%= Time.now.year %> JamKazam, Inc. All rights reserved. \ No newline at end of file From 35e729fbf630457f8e03b07af71749a47462ee45 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Wed, 31 Jul 2013 09:33:51 -0500 Subject: [PATCH 258/274] * fixing email templates to suppress 'you have account at jamkazam' if invitation is being sent --- lib/jam_ruby/app/mailers/invited_user_mailer.rb | 3 ++- lib/jam_ruby/app/views/layouts/from_user_mailer.html.erb | 3 +++ lib/jam_ruby/app/views/layouts/from_user_mailer.text.erb | 2 ++ lib/jam_ruby/app/views/layouts/user_mailer.html.erb | 3 +++ lib/jam_ruby/app/views/layouts/user_mailer.text.erb | 3 ++- lib/jam_ruby/models/user.rb | 2 +- 6 files changed, 13 insertions(+), 3 deletions(-) diff --git a/lib/jam_ruby/app/mailers/invited_user_mailer.rb b/lib/jam_ruby/app/mailers/invited_user_mailer.rb index 4e3b3f43f..2d39c6876 100644 --- a/lib/jam_ruby/app/mailers/invited_user_mailer.rb +++ b/lib/jam_ruby/app/mailers/invited_user_mailer.rb @@ -21,6 +21,7 @@ module JamRuby def welcome_betauser(invited_user) @signup_url = generate_signup_url(invited_user) + @suppress_user_has_account_footer = true sendgrid_category "Welcome" sendgrid_unique_args :type => "welcome_betauser" @@ -36,7 +37,7 @@ module JamRuby @signup_url = generate_signup_url(invited_user) @sender = invited_user.sender @note = invited_user.note - + @suppress_user_has_account_footer = true sendgrid_category "Invitation" sendgrid_unique_args :type => "friend_invitation" diff --git a/lib/jam_ruby/app/views/layouts/from_user_mailer.html.erb b/lib/jam_ruby/app/views/layouts/from_user_mailer.html.erb index 9c7ba243c..b4f9a39ed 100644 --- a/lib/jam_ruby/app/views/layouts/from_user_mailer.html.erb +++ b/lib/jam_ruby/app/views/layouts/from_user_mailer.html.erb @@ -39,6 +39,8 @@ + + <% unless @suppress_user_has_account_footer == true %> @@ -52,6 +54,7 @@ + <% end %>
diff --git a/lib/jam_ruby/app/views/layouts/from_user_mailer.text.erb b/lib/jam_ruby/app/views/layouts/from_user_mailer.text.erb index bc0f816eb..114359dc8 100644 --- a/lib/jam_ruby/app/views/layouts/from_user_mailer.text.erb +++ b/lib/jam_ruby/app/views/layouts/from_user_mailer.text.erb @@ -1,6 +1,8 @@ <%= yield %> +<% unless @suppress_user_has_account_footer == true %> This email was sent to you because you have an account at Jamkazam / http://www.jamkazam.com. +<% end %> Copyright 2013 JamKazam, Inc. All rights reserved. \ No newline at end of file diff --git a/lib/jam_ruby/app/views/layouts/user_mailer.html.erb b/lib/jam_ruby/app/views/layouts/user_mailer.html.erb index c306d8653..93f818244 100644 --- a/lib/jam_ruby/app/views/layouts/user_mailer.html.erb +++ b/lib/jam_ruby/app/views/layouts/user_mailer.html.erb @@ -31,6 +31,7 @@ + <% unless @suppress_user_has_account_footer == true %>
@@ -44,6 +45,8 @@ + + <% end %>
diff --git a/lib/jam_ruby/app/views/layouts/user_mailer.text.erb b/lib/jam_ruby/app/views/layouts/user_mailer.text.erb index bc0f816eb..f07049ef2 100644 --- a/lib/jam_ruby/app/views/layouts/user_mailer.text.erb +++ b/lib/jam_ruby/app/views/layouts/user_mailer.text.erb @@ -1,6 +1,7 @@ <%= yield %> +<% unless @suppress_user_has_account_footer == true %> This email was sent to you because you have an account at Jamkazam / http://www.jamkazam.com. - +<% end %> Copyright 2013 JamKazam, Inc. All rights reserved. \ No newline at end of file diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index f7ba3e803..846c33021 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -673,7 +673,7 @@ module JamRuby user.photo_url = photo_url - if invited_user.nil? + if invited_user.nil? b user.can_invite = Limits::USERS_CAN_INVITE user.email_confirmed = false user.signup_token = SecureRandom.urlsafe_base64 From cdb138a4477ff4dac44f70ba1546c9ee14c7c8e4 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Wed, 31 Jul 2013 10:06:36 -0500 Subject: [PATCH 259/274] * removing errant 'b' in the code --- lib/jam_ruby/models/user.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 846c33021..f7ba3e803 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -673,7 +673,7 @@ module JamRuby user.photo_url = photo_url - if invited_user.nil? b + if invited_user.nil? user.can_invite = Limits::USERS_CAN_INVITE user.email_confirmed = false user.signup_token = SecureRandom.urlsafe_base64 From ab46fffa9bed6738a207a1408d4db92120ae0a00 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Wed, 31 Jul 2013 10:44:25 -0500 Subject: [PATCH 260/274] * typo in plain-text version of friend invitation --- .../jam_ruby/invited_user_mailer/friend_invitation.text.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/friend_invitation.text.erb b/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/friend_invitation.text.erb index 0b1d6ecbd..a63d6d104 100644 --- a/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/friend_invitation.text.erb +++ b/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/friend_invitation.text.erb @@ -4,4 +4,4 @@ You've been invited to Jamkazam by <%= @sender.name %>! <%= @note %> <% end %> -To signup, please go to the 'create 'ccount' page: <%= @signup_url %>. +To signup, please go to the 'create account' page: <%= @signup_url %>. From 6573efa107d71470b1c3fd709bd913eaff18e17f Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Tue, 6 Aug 2013 08:15:39 -0700 Subject: [PATCH 261/274] Added crash dump version --- lib/jam_ruby/models/crash_dump.rb | 1 + spec/jam_ruby/models/crash_dump_spec.rb | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/jam_ruby/models/crash_dump.rb b/lib/jam_ruby/models/crash_dump.rb index f48de2454..b3441ae34 100644 --- a/lib/jam_ruby/models/crash_dump.rb +++ b/lib/jam_ruby/models/crash_dump.rb @@ -6,6 +6,7 @@ module JamRuby self.primary_key = 'id' validates :client_type, presence: true + validates :client_version, presence: true before_validation(:on => :create) do self.created_at ||= Time.now diff --git a/spec/jam_ruby/models/crash_dump_spec.rb b/spec/jam_ruby/models/crash_dump_spec.rb index fa253c6f3..0e3c0b110 100644 --- a/spec/jam_ruby/models/crash_dump_spec.rb +++ b/spec/jam_ruby/models/crash_dump_spec.rb @@ -4,13 +4,15 @@ describe CrashDump do before do end - it "should fail to save a crash dump without a client_type" do - CrashDump.new(:client_type => "").should_not be_valid + it "should fail to save a crash dump without a client_type and client_version" do + CrashDump.new(:client_type => "", :client_version => "version").should_not be_valid + CrashDump.new(:client_type => "type", :client_version => "").should_not be_valid end - it "should be able to save a crash dump with JUST a client_type" do + it "should be able to save a crash dump with JUST a client_type and client_version" do @cd = CrashDump.new @cd.client_type = "Win32" + @cd.client_version = "version" @cd.should be_valid @cd.save From 9424ffa6aa02ca904894edc53b8f3f947cdbae4f Mon Sep 17 00:00:00 2001 From: Seth Call Date: Wed, 7 Aug 2013 10:35:27 -0500 Subject: [PATCH 262/274] * VRFS-484; more changes needed but this helps a lot of common leave/join scenarios --- lib/jam_ruby/connection_manager.rb | 172 ++++++++++++----------- lib/jam_ruby/message_factory.rb | 17 ++- lib/jam_ruby/models/notification.rb | 22 ++- spec/jam_ruby/connection_manager_spec.rb | 19 +-- 4 files changed, 133 insertions(+), 97 deletions(-) diff --git a/lib/jam_ruby/connection_manager.rb b/lib/jam_ruby/connection_manager.rb index f400c780d..c820abe74 100644 --- a/lib/jam_ruby/connection_manager.rb +++ b/lib/jam_ruby/connection_manager.rb @@ -11,6 +11,10 @@ module JamRuby # This may make sense in the short term if we are still managing connections in the database, but # we move to the node-js in the websocket gateway (because the websocket gateway needs to call some of these methods). # Or of course we could just port the relevant methods to node-js +# +# Also we don't send notifications from ConnectionManager; +# we just return enough data so that a caller can make the determination if it needs to + class ConnectionManager < BaseManager def initialize(options={}) @@ -33,32 +37,64 @@ module JamRuby return friend_ids end - def reconnect(conn) - sql =< 0) + # If a blk is passed in, on success, count is also passed back an the db connection, allowing for + # notifications to go out within the table log. music_session_id is also passed, if the music_session still exists + # and this connection was in a session + def delete_connection(client_id, &blk) + ConnectionManager.active_record_transaction do |connection_manager| - conn = connection_manager.pg_conn - + count = 0 user_id = nil music_session_id = nil @@ -152,7 +202,7 @@ SQL return elsif result.cmd_tuples == 1 user_id = result[0]['user_id'] - music_session_id = result[0]['client_id'] + music_session_id = result[0]['music_session_id'] else raise Exception, 'uniqueness constraint has been lost on client_id' @@ -164,17 +214,19 @@ SQL # since we did delete a row, check and see if any more connections for that user exist # if we are down to zero, send out user gone message conn.exec("SELECT count(user_id) FROM connections where user_id = $1", [user_id]) do |result| - count = result.getvalue(0, 0) - if count == "0" - # send notification - Notification.send_friend_update(user_id, false, conn) - end + count = result.getvalue(0, 0).to_i end # same for session-if we are down to the last participant, delete the session unless music_session_id.nil? - conn.exec("DELETE FROM music_sessions id = $1 AND 0 = (SELECT count(music_session_id) FROM connections where music_session_id = $1)", [music_session_id]).clear + result = conn.exec("DELETE FROM music_sessions WHERE id = $1 AND 0 = (select count(music_session_id) FROM connections where music_session_id = $1)", [music_session_id]) + if result.cmd_tuples == 1 + music_session_id = nil + end end + + blk.call(conn, count, music_session_id) unless blk.nil? + return count end end @@ -224,7 +276,9 @@ SQL end end - def join_music_session(user, client_id, music_session, as_musician, tracks) + # if a blk is passed in, upon success, it will be called and you can issue notifications + # within the connection table lock + def join_music_session(user, client_id, music_session, as_musician, tracks, &blk) connection = nil user_id = user.id music_session_id = music_session.id @@ -242,10 +296,7 @@ SQL if connection.errors.any? raise ActiveRecord::Rollback else - if as_musician && music_session.musician_access - Notification.send_musician_session_join(music_session, connection, user) - Notification.send_friend_session_join(db_conn, connection, user) - end + blk.call(db_conn, connection) unless blk.nil? MusicSessionUserHistory.save(music_session_id, user_id, client_id) end end @@ -253,57 +304,9 @@ SQL return connection end - def join_music_session_old(user_id, client_id, music_session_id, as_musician) - conn = @pg_conn - - lock_connections(conn) - - previous_music_session_id = check_already_session(conn, client_id) - - user = User.find(user_id) - - if as_musician != true && as_musician != false # checks that a boolean was passed in - raise JamArgumentError, "as_musician incorrectly specified" - end - - # determine if the user can join; if not, throw a PermissionError - music_session = MusicSession.find(music_session_id) - - - unless music_session.can_join?(user, as_musician) - @log.debug "user can not join a session user_id=#{user_id} and client_id=#{client_id}" - raise PermissionError, "unable to join the specified session" - end - - begin - # we include user_id in the query as an act of security, so that a user can't access someone else's client connection - conn.exec("UPDATE connections SET music_session_id = $1, as_musician = $2 WHERE client_id = $3 and user_id = $4", [music_session_id, as_musician, client_id, user_id]) do |result| - if result.cmd_tuples == 1 - @log.debug "associated music_session with connection for client=#{client_id}, music_session=#{music_session_id}, and user=#{user_id}" - - session_checks(conn, previous_music_session_id, user_id) - elsif result.cmd_tuples == 0 - @log.debug "join_music_session no connection found with client_id=#{client_id} and user_id=#{user_id}" - raise ActiveRecord::RecordNotFound - else - @log.error "database failure or logic error; this path should be impossible if the table is locked (join_music_session)" - raise Exception, "locked table changed state" - end - end - rescue PG::Error => pg_error - if pg_error.to_s.include?("insert or update on table \"connections\" violates foreign key constraint \"connections_music_session_id_fkey\"") - # if there is no music session that exists, we will receive this message - @log.debug "music_session does not exist. music_session=#{music_session_id}" - raise StateError, "music_session does not exist" - else - raise pg_error - end - - end - # end - end - - def leave_music_session(user, connection, music_session) + # if a blk is passed in, upon success, it will be called and you can issue notifications + # within the connection table lock + def leave_music_session(user, connection, music_session, &blk) ConnectionManager.active_record_transaction do |connection_manager| conn = connection_manager.pg_conn @@ -331,7 +334,8 @@ SQL session_checks(conn, previous_music_session_id, user_id) - Notification.send_musician_session_depart(music_session, connection, user) + blk.call() unless blk.nil? + elsif result.cmd_tuples == 0 @log.debug "leave_music_session no connection found with client_id=#{client_id}" raise ActiveRecord::RecordNotFound diff --git a/lib/jam_ruby/message_factory.rb b/lib/jam_ruby/message_factory.rb index 82391f4f4..144df8e35 100644 --- a/lib/jam_ruby/message_factory.rb +++ b/lib/jam_ruby/message_factory.rb @@ -35,8 +35,8 @@ end # create a login ack (login was successful) - def login_ack(public_ip, client_id, token, heartbeat_interval, music_session_id) - login_ack = Jampb::LoginAck.new(:public_ip => public_ip, :client_id => client_id, :token => token, :heartbeat_interval => heartbeat_interval, :music_session_id => music_session_id) + def login_ack(public_ip, client_id, token, heartbeat_interval, music_session_id, reconnected) + login_ack = Jampb::LoginAck.new(:public_ip => public_ip, :client_id => client_id, :token => token, :heartbeat_interval => heartbeat_interval, :music_session_id => music_session_id, :reconnected => reconnected) return Jampb::ClientMessage.new(:type => ClientMessage::Type::LOGIN_ACK, :route_to => CLIENT_TARGET, :login_ack => login_ack) end @@ -112,6 +112,19 @@ return Jampb::ClientMessage.new(:type => ClientMessage::Type::MUSICIAN_SESSION_DEPART, :route_to => CLIENT_TARGET, :musician_session_depart => left) end + # create a musician fresh session message + def musician_session_fresh(session_id, user_id, username, photo_url) + fresh = Jampb::MusicianSessionFresh.new(:session_id => session_id, :user_id => user_id, :username => username, :photo_url => photo_url) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::MUSICIAN_SESSION_FRESH, :route_to => CLIENT_TARGET, :musician_session_fresh => fresh) + end + + # create a musician stale session message + def musician_session_stale(session_id, user_id, username, photo_url) + stale = Jampb::MusicianSessionStale.new(:session_id => session_id, :user_id => user_id, :username => username, :photo_url => photo_url) + return Jampb::ClientMessage.new(:type => ClientMessage::Type::MUSICIAN_SESSION_STALE, :route_to => CLIENT_TARGET, :musician_session_stale => stale) + end + + # create a user-joined session message def join_request(session_id, join_request_id, username, text) join_request = Jampb::JoinRequest.new(:join_request_id => join_request_id, :username => username, :text => text) diff --git a/lib/jam_ruby/models/notification.rb b/lib/jam_ruby/models/notification.rb index 6d82c2190..0afc93ad5 100644 --- a/lib/jam_ruby/models/notification.rb +++ b/lib/jam_ruby/models/notification.rb @@ -223,13 +223,31 @@ module JamRuby @@mq_router.server_publish_to_session(music_session, msg, sender = {:client_id => connection.client_id}) end - def send_musician_session_depart(music_session, connection, user) + def send_musician_session_depart(music_session, client_id, user) # (1) create notification msg = @@message_factory.musician_session_depart(music_session.id, user.id, user.name, user.photo_url) # (2) send notification - @@mq_router.server_publish_to_session(music_session, msg, sender = {:client_id => connection.client_id}) + @@mq_router.server_publish_to_session(music_session, msg, sender = {:client_id => client_id}) + end + + def send_musician_session_fresh(music_session, client_id, user) + + # (1) create notification + msg = @@message_factory.musician_session_fresh(music_session.id, user.id, user.name, user.photo_url) + + # (2) send notification + @@mq_router.server_publish_to_session(music_session, msg, sender = {:client_id => client_id}) + end + + def send_musician_session_stale(music_session, client_id, user) + + # (1) create notification + msg = @@message_factory.musician_session_stale(music_session.id, user.id, user.name, user.photo_url) + + # (2) send notification + @@mq_router.server_publish_to_session(music_session, msg, sender = {:client_id => client_id}) end def send_friend_session_join(db_conn, connection, user) diff --git a/spec/jam_ruby/connection_manager_spec.rb b/spec/jam_ruby/connection_manager_spec.rb index 8b1239c55..0056e9486 100644 --- a/spec/jam_ruby/connection_manager_spec.rb +++ b/spec/jam_ruby/connection_manager_spec.rb @@ -62,8 +62,8 @@ describe ConnectionManager do client_id = "client_id2" user_id = create_user("test", "user2", "user2@jamkazam.com") - connection = @connman.create_connection(user_id, client_id, "1.1.1.1") - + count = @connman.create_connection(user_id, client_id, "1.1.1.1") + count.should == 1 # make sure the connection is seen @conn.exec("SELECT count(*) FROM connections where user_id = $1", [user_id]) do |result| @@ -73,7 +73,8 @@ describe ConnectionManager do cc = Connection.find_by_client_id!(client_id) cc.connected?.should be_true - @connman.delete_connection(client_id) + count = @connman.delete_connection(client_id) + count.should == 0 @conn.exec("SELECT count(*) FROM connections where user_id = $1", [user_id]) do |result| result.getvalue(0, 0).should == "0" @@ -377,9 +378,9 @@ describe ConnectionManager do user = User.find(user_id) dummy_music_session = MusicSession.new - connection = @connman.create_connection(user_id, client_id, "1.1.1.1") + @connman.create_connection(user_id, client_id, "1.1.1.1") - expect { @connman.leave_music_session(user, connection, dummy_music_session) }.to raise_error(JamRuby::StateError) + expect { @connman.leave_music_session(user, Connection.find_by_client_id(client_id), dummy_music_session) }.to raise_error(JamRuby::StateError) end it "leave_music_session fails if in different music_session" do @@ -393,9 +394,9 @@ describe ConnectionManager do dummy_music_session = MusicSession.new - connection = @connman.create_connection(user_id, client_id, "1.1.1.1") + @connman.create_connection(user_id, client_id, "1.1.1.1") @connman.join_music_session(user, client_id, music_session, true, TRACKS) - expect { @connman.leave_music_session(user, connection, dummy_music_session) }.to raise_error(JamRuby::StateError) + expect { @connman.leave_music_session(user, Connection.find_by_client_id(client_id), dummy_music_session) }.to raise_error(JamRuby::StateError) end it "leave_music_session works" do @@ -407,7 +408,7 @@ describe ConnectionManager do user = User.find(user_id) music_session = MusicSession.find(music_session_id) - connection = @connman.create_connection(user_id, client_id, "1.1.1.1") + @connman.create_connection(user_id, client_id, "1.1.1.1") @connman.join_music_session(user, client_id, music_session, true, TRACKS) assert_session_exists(music_session_id, true) @@ -416,7 +417,7 @@ describe ConnectionManager do result.getvalue(0, 0).should == music_session_id end - @connman.leave_music_session(user, connection, music_session) + @connman.leave_music_session(user, Connection.find_by_client_id(client_id), music_session) @conn.exec("SELECT music_session_id FROM connections WHERE client_id = $1", [client_id]) do |result| result.getvalue(0, 0).should == nil From 90dcfbf43757bfe909d1499e2c13023fbe3cc2cc Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Thu, 8 Aug 2013 18:03:41 -0700 Subject: [PATCH 263/274] tests passing plus added search api --- lib/jam_ruby/models/crash_dump.rb | 12 ++++++++++++ spec/factories.rb | 3 +++ spec/jam_ruby/models/crash_dump_spec.rb | 17 +++++++++++++++++ 3 files changed, 32 insertions(+) diff --git a/lib/jam_ruby/models/crash_dump.rb b/lib/jam_ruby/models/crash_dump.rb index b3441ae34..1a61da598 100644 --- a/lib/jam_ruby/models/crash_dump.rb +++ b/lib/jam_ruby/models/crash_dump.rb @@ -13,5 +13,17 @@ module JamRuby self.id = SecureRandom.uuid self.uri = "dump/#{self.id}-#{self.created_at.to_i}" end + + def self.search(start_ts, end_ts, user_id, client_id) + result = CrashDump.scoped + unless start_ts.nil? || end_ts.nil? + time_range = start_ts..end_ts + result = result.where("EXTRACT(EPOCH FROM created_at)::bigint >= ?", start_ts).where("EXTRACT(EPOCH FROM created_at)::bigint <= ?", end_ts) + end + result = result.where(:user_id => user_id) unless user_id.nil? + result = result.where(:client_id => client_id) unless client_id.nil? + result + end + end end diff --git a/spec/factories.rb b/spec/factories.rb index ddac6d64b..951cd7232 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -118,4 +118,7 @@ FactoryGirl.define do factory :music_session_perf_data, :class => JamRuby::MusicSessionPerfData do association :music_session => :music_session end + + factory :crash_dump, :class => JamRuby::CrashDump do + end end diff --git a/spec/jam_ruby/models/crash_dump_spec.rb b/spec/jam_ruby/models/crash_dump_spec.rb index 0e3c0b110..27dc4cd72 100644 --- a/spec/jam_ruby/models/crash_dump_spec.rb +++ b/spec/jam_ruby/models/crash_dump_spec.rb @@ -18,4 +18,21 @@ describe CrashDump do CrashDump.first.id.should == @cd.id end + + it "should be able to search for a crash dump" do + @cd = FactoryGirl.build(:crash_dump) + @cd.client_type = "Win32" + @cd.client_version = "version" + @cd.save + @cd.reload + + CrashDump.search(nil, nil, nil, nil).count.should == 1 + CrashDump.search(@cd.created_at.to_i - 100, @cd.created_at.to_i + 100, nil, nil).count.should == 1 + CrashDump.search(@cd.created_at.to_i - 100, @cd.created_at.to_i - 1, nil, nil).count.should == 0 + CrashDump.search(nil, nil, "blah", nil).count.should == 0 + CrashDump.search(nil, nil, @cd.user_id, nil).count.should == 1 + CrashDump.search(nil, nil, nil, "blah").count.should == 0 + CrashDump.search(nil, nil, nil, @cd.client_id).count.should == 1 + end + end From 856e95d2adb4f0452e5ef3b268b61f1f7ae919ff Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Thu, 8 Aug 2013 18:38:12 -0700 Subject: [PATCH 264/274] removing search since the admin stuff does it for you --- lib/jam_ruby/models/crash_dump.rb | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/lib/jam_ruby/models/crash_dump.rb b/lib/jam_ruby/models/crash_dump.rb index 1a61da598..b3441ae34 100644 --- a/lib/jam_ruby/models/crash_dump.rb +++ b/lib/jam_ruby/models/crash_dump.rb @@ -13,17 +13,5 @@ module JamRuby self.id = SecureRandom.uuid self.uri = "dump/#{self.id}-#{self.created_at.to_i}" end - - def self.search(start_ts, end_ts, user_id, client_id) - result = CrashDump.scoped - unless start_ts.nil? || end_ts.nil? - time_range = start_ts..end_ts - result = result.where("EXTRACT(EPOCH FROM created_at)::bigint >= ?", start_ts).where("EXTRACT(EPOCH FROM created_at)::bigint <= ?", end_ts) - end - result = result.where(:user_id => user_id) unless user_id.nil? - result = result.where(:client_id => client_id) unless client_id.nil? - result - end - end end From db15ac0fb5abfbd80bd3bf550e1d21219a4f0783 Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Thu, 8 Aug 2013 18:38:28 -0700 Subject: [PATCH 265/274] removing search tests --- spec/jam_ruby/models/crash_dump_spec.rb | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/spec/jam_ruby/models/crash_dump_spec.rb b/spec/jam_ruby/models/crash_dump_spec.rb index 27dc4cd72..c395f1038 100644 --- a/spec/jam_ruby/models/crash_dump_spec.rb +++ b/spec/jam_ruby/models/crash_dump_spec.rb @@ -19,20 +19,4 @@ describe CrashDump do CrashDump.first.id.should == @cd.id end - it "should be able to search for a crash dump" do - @cd = FactoryGirl.build(:crash_dump) - @cd.client_type = "Win32" - @cd.client_version = "version" - @cd.save - @cd.reload - - CrashDump.search(nil, nil, nil, nil).count.should == 1 - CrashDump.search(@cd.created_at.to_i - 100, @cd.created_at.to_i + 100, nil, nil).count.should == 1 - CrashDump.search(@cd.created_at.to_i - 100, @cd.created_at.to_i - 1, nil, nil).count.should == 0 - CrashDump.search(nil, nil, "blah", nil).count.should == 0 - CrashDump.search(nil, nil, @cd.user_id, nil).count.should == 1 - CrashDump.search(nil, nil, nil, "blah").count.should == 0 - CrashDump.search(nil, nil, nil, @cd.client_id).count.should == 1 - end - end From 7c7d9ec4e24d9b653a201ef6bf341ae76402fe37 Mon Sep 17 00:00:00 2001 From: Mike Slemmer Date: Thu, 8 Aug 2013 19:12:43 -0700 Subject: [PATCH 266/274] fixups for active admin --- lib/jam_ruby/models/crash_dump.rb | 10 ++++++++++ lib/jam_ruby/models/user.rb | 3 +++ 2 files changed, 13 insertions(+) diff --git a/lib/jam_ruby/models/crash_dump.rb b/lib/jam_ruby/models/crash_dump.rb index b3441ae34..6c4e54d84 100644 --- a/lib/jam_ruby/models/crash_dump.rb +++ b/lib/jam_ruby/models/crash_dump.rb @@ -4,14 +4,24 @@ module JamRuby self.table_name = "crash_dumps" self.primary_key = 'id' + + belongs_to :user, :inverse_of => :crash_dumps, :class_name => "JamRuby::User" validates :client_type, presence: true validates :client_version, presence: true + attr_accessor :user_email + before_validation(:on => :create) do self.created_at ||= Time.now self.id = SecureRandom.uuid self.uri = "dump/#{self.id}-#{self.created_at.to_i}" end + + def user_email + nil if user_id.nil? + self.user.email + end + end end diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 025decdf2..74360bf5a 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -102,6 +102,9 @@ module JamRuby # invited users has_many :invited_users, :foreign_key => "sender_id", :class_name => "JamRuby::InvitedUser" + # crash dumps + has_many :crash_dumps, :foreign_key => "user_id", :class_name => "JamRuby::CrashDump" + # This causes the authenticate method to be generated (among other stuff) #has_secure_password From aa7a6b3e04bb5e8689fd99cf8a8cabc2a846f28b Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sat, 10 Aug 2013 15:35:54 -0500 Subject: [PATCH 267/274] * VRFS-513; model in place to take feedback from corporate website --- lib/jam_ruby.rb | 3 + lib/jam_ruby/app/mailers/corp_mailer.rb | 35 ++++++ lib/jam_ruby/app/mailers/user_mailer.rb | 114 +++++++++--------- .../jam_ruby/corp_mailer/feedback.html.erb | 14 +++ .../jam_ruby/corp_mailer/feedback.text.erb | 8 ++ lib/jam_ruby/models/feedback.rb | 21 ++++ lib/jam_ruby/models/feedback_observer.rb | 10 ++ spec/jam_ruby/models/feedback_spec.rb | 54 +++++++++ spec/spec_helper.rb | 1 + 9 files changed, 203 insertions(+), 57 deletions(-) create mode 100644 lib/jam_ruby/app/mailers/corp_mailer.rb create mode 100644 lib/jam_ruby/app/views/jam_ruby/corp_mailer/feedback.html.erb create mode 100644 lib/jam_ruby/app/views/jam_ruby/corp_mailer/feedback.text.erb create mode 100644 lib/jam_ruby/models/feedback.rb create mode 100644 lib/jam_ruby/models/feedback_observer.rb create mode 100644 spec/jam_ruby/models/feedback_spec.rb diff --git a/lib/jam_ruby.rb b/lib/jam_ruby.rb index a7aafe72c..af074e607 100755 --- a/lib/jam_ruby.rb +++ b/lib/jam_ruby.rb @@ -25,6 +25,7 @@ require "jam_ruby/environment" require "jam_ruby/init" require "jam_ruby/app/mailers/user_mailer" require "jam_ruby/app/mailers/invited_user_mailer" +require "jam_ruby/app/mailers/corp_mailer" require "jam_ruby/app/uploaders/artifact_uploader" require "jam_ruby/app/uploaders/perf_data_uploader" require "jam_ruby/lib/desk_multipass" @@ -33,6 +34,8 @@ require "jam_ruby/lib/s3_manager" require "jam_ruby/lib/profanity" require "jam_ruby/amqp/amqp_connection_manager" require "jam_ruby/message_factory" +require "jam_ruby/models/feedback" +require "jam_ruby/models/feedback_observer" require "jam_ruby/models/max_mind_geo" require "jam_ruby/models/max_mind_isp" require "jam_ruby/models/genre" diff --git a/lib/jam_ruby/app/mailers/corp_mailer.rb b/lib/jam_ruby/app/mailers/corp_mailer.rb new file mode 100644 index 000000000..0bdd09858 --- /dev/null +++ b/lib/jam_ruby/app/mailers/corp_mailer.rb @@ -0,0 +1,35 @@ +module JamRuby + # CorpMail must be configured to work + # Some common configs occur in jam_ruby/init.rb + # Environment specific configs occur in spec_helper.rb in jam-ruby and jam-web (to put it into test mode), + # and in config/initializers/email.rb in rails to configure sendmail account settings + # If UserMailer were to be used in another project, it would need to be configured there, as well. + + # Templates for UserMailer can be found in jam_ruby/app/views/jam_ruby/user_mailer + class CorpMailer < ActionMailer::Base + include SendGrid + + layout "user_mailer" + + DEFAULT_SENDER = "noreply@jamkazam.com" + + default :from => DEFAULT_SENDER + + sendgrid_category :use_subject_lines + #sendgrid_enable :opentrack, :clicktrack # this makes our emails creepy, imo (seth) + sendgrid_unique_args :env => Environment.mode + + def feedback(feedback) + @email = feedback.email + @body = feedback.body + + sendgrid_category "Corporate" + sendgrid_unique_args :type => "feedback" + + mail(:to => "info@jamkazam.com", :subject => "Feedback received from #{@email} ") do |format| + format.text + format.html + end + end + end +end diff --git a/lib/jam_ruby/app/mailers/user_mailer.rb b/lib/jam_ruby/app/mailers/user_mailer.rb index 96b322def..3b84767cb 100644 --- a/lib/jam_ruby/app/mailers/user_mailer.rb +++ b/lib/jam_ruby/app/mailers/user_mailer.rb @@ -1,71 +1,71 @@ -module JamRuby - # UserMailer must be configured to work - # Some common configs occur in jam_ruby/init.rb - # Environment specific configs occur in spec_helper.rb in jam-ruby and jam-web (to put it into test mode), - # and in config/initializers/email.rb in rails to configure sendmail account settings - # If UserMailer were to be used in another project, it would need to be configured there, as well. + module JamRuby + # UserMailer must be configured to work + # Some common configs occur in jam_ruby/init.rb + # Environment specific configs occur in spec_helper.rb in jam-ruby and jam-web (to put it into test mode), + # and in config/initializers/email.rb in rails to configure sendmail account settings + # If UserMailer were to be used in another project, it would need to be configured there, as well. - # Templates for UserMailer can be found in jam_ruby/app/views/jam_ruby/user_mailer - class UserMailer < ActionMailer::Base - include SendGrid + # Templates for UserMailer can be found in jam_ruby/app/views/jam_ruby/user_mailer + class UserMailer < ActionMailer::Base + include SendGrid - layout "user_mailer" + layout "user_mailer" - DEFAULT_SENDER = "support@jamkazam.com" + DEFAULT_SENDER = "support@jamkazam.com" - default :from => DEFAULT_SENDER + default :from => DEFAULT_SENDER - sendgrid_category :use_subject_lines - #sendgrid_enable :opentrack, :clicktrack # this makes our emails creepy, imo (seth) - sendgrid_unique_args :env => Environment.mode + sendgrid_category :use_subject_lines + #sendgrid_enable :opentrack, :clicktrack # this makes our emails creepy, imo (seth) + sendgrid_unique_args :env => Environment.mode - def welcome_message(user, signup_confirm_url) - @user = user - @signup_confirm_url = signup_confirm_url - sendgrid_category "Welcome" - sendgrid_unique_args :type => "welcome_message" + def welcome_message(user, signup_confirm_url) + @user = user + @signup_confirm_url = signup_confirm_url + sendgrid_category "Welcome" + sendgrid_unique_args :type => "welcome_message" - mail(:to => user.email, :subject => "Welcome to Jamkazam, #{user.first_name} ") do |format| - format.text - format.html - end - end - - def password_changed(user) - @user = user - sendgrid_unique_args :type => "password_changed" - mail(:to => user.email, :subject => "Jamkazam Password Changed") do |format| - format.text - format.html - end - end - - def password_reset(user, password_reset_url) - @user = user - @password_reset_url = password_reset_url - sendgrid_unique_args :type => "password_reset" - mail(:to => user.email, :subject => "Jamkazam Password Reset") do |format| - format.text - format.html + mail(:to => user.email, :subject => "Welcome to Jamkazam, #{user.first_name} ") do |format| + format.text + format.html + end end - end - def updating_email(user) - @user = user - sendgrid_unique_args :type => "updating_email" - mail(:to => user.update_email, :subject => "Jamkazam Email Change Confirmation") do |format| - format.text - format.html + def password_changed(user) + @user = user + sendgrid_unique_args :type => "password_changed" + mail(:to => user.email, :subject => "Jamkazam Password Changed") do |format| + format.text + format.html + end end - end - def updated_email(user) - @user = user - sendgrid_unique_args :type => "updated_email" - mail(:to => user.email, :subject => "Jamkazam Email Changed") do |format| - format.text - format.html + def password_reset(user, password_reset_url) + @user = user + @password_reset_url = password_reset_url + sendgrid_unique_args :type => "password_reset" + mail(:to => user.email, :subject => "Jamkazam Password Reset") do |format| + format.text + format.html + end + end + + def updating_email(user) + @user = user + sendgrid_unique_args :type => "updating_email" + mail(:to => user.update_email, :subject => "Jamkazam Email Change Confirmation") do |format| + format.text + format.html + end + end + + def updated_email(user) + @user = user + sendgrid_unique_args :type => "updated_email" + mail(:to => user.email, :subject => "Jamkazam Email Changed") do |format| + format.text + format.html + end end end end -end diff --git a/lib/jam_ruby/app/views/jam_ruby/corp_mailer/feedback.html.erb b/lib/jam_ruby/app/views/jam_ruby/corp_mailer/feedback.html.erb new file mode 100644 index 000000000..7daab2af6 --- /dev/null +++ b/lib/jam_ruby/app/views/jam_ruby/corp_mailer/feedback.html.erb @@ -0,0 +1,14 @@ + + +

Feedback Received

+

From <%= @email %>:

+

<%= @body %>

+ +
+
+
+

+This email was received because someone left feedback at http://www.jamkazam.com/corp/contact +

+ + diff --git a/lib/jam_ruby/app/views/jam_ruby/corp_mailer/feedback.text.erb b/lib/jam_ruby/app/views/jam_ruby/corp_mailer/feedback.text.erb new file mode 100644 index 000000000..4612cf5ee --- /dev/null +++ b/lib/jam_ruby/app/views/jam_ruby/corp_mailer/feedback.text.erb @@ -0,0 +1,8 @@ +Feedback Received + +From <%= @email %>: + +<%= @body %> + + +This email was received because someone left feedback at http://www.jamkazam.com/corp/contact \ No newline at end of file diff --git a/lib/jam_ruby/models/feedback.rb b/lib/jam_ruby/models/feedback.rb new file mode 100644 index 000000000..454805855 --- /dev/null +++ b/lib/jam_ruby/models/feedback.rb @@ -0,0 +1,21 @@ +module JamRuby + class Feedback + include ActiveModel::Validations + include ActiveModel::Validations::Callbacks + include ActiveModel::Observing + extend ActiveModel::Callbacks + + VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i + validates :email, presence: true, format: {with: VALID_EMAIL_REGEX} + validates :body, :presence => true + + attr_accessor :email, :body + + + def save + + return valid? + + end + end +end \ No newline at end of file diff --git a/lib/jam_ruby/models/feedback_observer.rb b/lib/jam_ruby/models/feedback_observer.rb new file mode 100644 index 000000000..f409c753f --- /dev/null +++ b/lib/jam_ruby/models/feedback_observer.rb @@ -0,0 +1,10 @@ +module JamRuby + class FeedbackObserver < ActiveRecord::Observer + + observe JamRuby::Feedback + + def after_validation(feedback) + CorpMailer.feedback(feedback).deliver unless feedback.errors.any? + end + end +end \ No newline at end of file diff --git a/spec/jam_ruby/models/feedback_spec.rb b/spec/jam_ruby/models/feedback_spec.rb new file mode 100644 index 000000000..bf1260c76 --- /dev/null +++ b/spec/jam_ruby/models/feedback_spec.rb @@ -0,0 +1,54 @@ +require 'spec_helper' + +describe Feedback do + + let(:feedback) { Feedback.new } + + before(:each) do + CorpMailer.deliveries.clear + end + + describe "empty model" do + + before(:each) do + feedback.save + end + + it { feedback.valid?.should be_false } + it { feedback.errors.keys.length.should == 2} + it { feedback.errors["email"].length.should == 2} + it { feedback.errors["email"][0].include?("blank").should be_true} + it { feedback.errors["email"][1].include?("invalid").should be_true} + it { feedback.errors["body"].length.should == 1} + it { feedback.errors["body"][0].include?("blank").should be_true} + it { CorpMailer.deliveries.length.should == 0} + + end + + describe "bad email" do + before(:each) do + feedback.email = "blarg" + feedback.body = "here's the problem!" + feedback.save + end + + it { feedback.valid?.should be_false } + it { feedback.errors.keys.length.should == 1} + it { feedback.errors["email"].length.should == 1} + it { feedback.errors["email"][0].include?("invalid").should be_true} + it { CorpMailer.deliveries.length.should == 0} + end + + describe "populated model" do + before(:each) do + feedback.email = "seth@jamkazam.com" + feedback.body = "here's the problem!" + feedback.save + end + + it { feedback.valid?.should be_true } + it { feedback.errors.keys.length.should == 0 } + it { CorpMailer.deliveries.length.should == 1} + end +end + diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index eeab8e42e..71905ef15 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -25,6 +25,7 @@ include JamRuby # manually register observers ActiveRecord::Base.add_observer InvitedUserObserver.instance ActiveRecord::Base.add_observer UserObserver.instance +ActiveRecord::Base.add_observer FeedbackObserver.instance # put ActionMailer into test mode From 2431af4baab00497ee43f29a69b571ae4d14ccfd Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sat, 10 Aug 2013 20:38:15 -0500 Subject: [PATCH 268/274] * removing extraneous whitespace --- lib/jam_ruby/models/feedback.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/jam_ruby/models/feedback.rb b/lib/jam_ruby/models/feedback.rb index 454805855..791ef7833 100644 --- a/lib/jam_ruby/models/feedback.rb +++ b/lib/jam_ruby/models/feedback.rb @@ -11,11 +11,8 @@ module JamRuby attr_accessor :email, :body - def save - return valid? - end end end \ No newline at end of file From 39fa5bf0981f607e80ba347ed7ef6aaf170dbfbb Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 11 Aug 2013 10:48:12 -0500 Subject: [PATCH 269/274] * VRFS-521; all emails have Jamkazam > JamKazam fixed --- jam_ruby.gemspec | 4 ++-- lib/jam_ruby/app/mailers/invited_user_mailer.rb | 4 ++-- lib/jam_ruby/app/mailers/user_mailer.rb | 10 +++++----- .../invited_user_mailer/friend_invitation.html.erb | 4 ++-- .../invited_user_mailer/friend_invitation.text.erb | 2 +- .../invited_user_mailer/welcome_betauser.html.erb | 2 +- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/jam_ruby.gemspec b/jam_ruby.gemspec index 6722c6b78..ccb082418 100644 --- a/jam_ruby.gemspec +++ b/jam_ruby.gemspec @@ -4,8 +4,8 @@ require File.expand_path('../lib/jam_ruby/version', __FILE__) Gem::Specification.new do |gem| gem.authors = ["Seth Call"] gem.email = ["seth@jamkazam.com"] - gem.description = %q{Common library for Jamkazam Ruby code} - gem.summary = %q{Common library for Jamkazam Ruby code} + gem.description = %q{Common library for JamKazam Ruby code} + gem.summary = %q{Common library for JamKazam Ruby code} gem.homepage = "" gem.files = `git ls-files`.split($\) diff --git a/lib/jam_ruby/app/mailers/invited_user_mailer.rb b/lib/jam_ruby/app/mailers/invited_user_mailer.rb index 2d39c6876..a443f9c6d 100644 --- a/lib/jam_ruby/app/mailers/invited_user_mailer.rb +++ b/lib/jam_ruby/app/mailers/invited_user_mailer.rb @@ -26,7 +26,7 @@ module JamRuby sendgrid_category "Welcome" sendgrid_unique_args :type => "welcome_betauser" - mail(:to => invited_user.email, :subject => "Welcome to the Jamkazam Beta release") do |format| + mail(:to => invited_user.email, :subject => "Welcome to the JamKazam Beta release") do |format| format.text format.html { render :layout => "user_mailer" } end @@ -41,7 +41,7 @@ module JamRuby sendgrid_category "Invitation" sendgrid_unique_args :type => "friend_invitation" - mail(:to => invited_user.email, :subject => "You've been invited to Jamkazam by #{invited_user.sender.name}") do |format| + mail(:to => invited_user.email, :subject => "You've been invited to JamKazam by #{invited_user.sender.name}") do |format| format.text format.html { render :layout => "from_user_mailer" } end diff --git a/lib/jam_ruby/app/mailers/user_mailer.rb b/lib/jam_ruby/app/mailers/user_mailer.rb index 3b84767cb..ff5740718 100644 --- a/lib/jam_ruby/app/mailers/user_mailer.rb +++ b/lib/jam_ruby/app/mailers/user_mailer.rb @@ -25,7 +25,7 @@ sendgrid_category "Welcome" sendgrid_unique_args :type => "welcome_message" - mail(:to => user.email, :subject => "Welcome to Jamkazam, #{user.first_name} ") do |format| + mail(:to => user.email, :subject => "Welcome to JamKazam, #{user.first_name} ") do |format| format.text format.html end @@ -34,7 +34,7 @@ def password_changed(user) @user = user sendgrid_unique_args :type => "password_changed" - mail(:to => user.email, :subject => "Jamkazam Password Changed") do |format| + mail(:to => user.email, :subject => "JamKazam Password Changed") do |format| format.text format.html end @@ -44,7 +44,7 @@ @user = user @password_reset_url = password_reset_url sendgrid_unique_args :type => "password_reset" - mail(:to => user.email, :subject => "Jamkazam Password Reset") do |format| + mail(:to => user.email, :subject => "JamKazam Password Reset") do |format| format.text format.html end @@ -53,7 +53,7 @@ def updating_email(user) @user = user sendgrid_unique_args :type => "updating_email" - mail(:to => user.update_email, :subject => "Jamkazam Email Change Confirmation") do |format| + mail(:to => user.update_email, :subject => "JamKazam Email Change Confirmation") do |format| format.text format.html end @@ -62,7 +62,7 @@ def updated_email(user) @user = user sendgrid_unique_args :type => "updated_email" - mail(:to => user.email, :subject => "Jamkazam Email Changed") do |format| + mail(:to => user.email, :subject => "JamKazam Email Changed") do |format| format.text format.html end diff --git a/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/friend_invitation.html.erb b/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/friend_invitation.html.erb index 142659257..11c4bffd7 100644 --- a/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/friend_invitation.html.erb +++ b/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/friend_invitation.html.erb @@ -1,4 +1,4 @@ -<% provide(:title, "You've been invited to Jamkazam by #{@sender.name}!") %> +<% provide(:title, "You've been invited to JamKazam by #{@sender.name}!") %> <% provide(:photo_url, @sender.resolved_photo_url) %> To signup, please go to the create account page. @@ -7,6 +7,6 @@ To signup, please go to the create account page <% if @note %> <%= @sender.name %> says: <%= @note %> <% else %> - <%= @sender.name %> would like you to join Jamkazam. + <%= @sender.name %> would like you to join JamKazam. <% end %> <% end %> \ No newline at end of file diff --git a/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/friend_invitation.text.erb b/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/friend_invitation.text.erb index a63d6d104..5bfbd9b17 100644 --- a/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/friend_invitation.text.erb +++ b/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/friend_invitation.text.erb @@ -1,4 +1,4 @@ -You've been invited to Jamkazam by <%= @sender.name %>! +You've been invited to JamKazam by <%= @sender.name %>! <% unless @note.nil? %> <%= @sender.name %> says: <%= @note %> diff --git a/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/welcome_betauser.html.erb b/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/welcome_betauser.html.erb index ae32900a6..c1aafeaee 100644 --- a/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/welcome_betauser.html.erb +++ b/lib/jam_ruby/app/views/jam_ruby/invited_user_mailer/welcome_betauser.html.erb @@ -1,3 +1,3 @@ -<% provide(:title, 'Welcome to the Jamkazam Beta!') %> +<% provide(:title, 'Welcome to the JamKazam Beta!') %> To signup, please go to the create account page. \ No newline at end of file From df74fda51f5ef6fa324d55d7e17d087ecb896b90 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 18 Aug 2013 02:57:13 +0000 Subject: [PATCH 270/274] * updating ruby version --- .ruby-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ruby-version b/.ruby-version index 95a5ad2d5..abf2ccea0 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -ruby-2.0.0-p195 +ruby-2.0.0-p247 From 2428de8e8601fb32435a05fe07585f2f270867f8 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Sun, 18 Aug 2013 20:36:36 -0400 Subject: [PATCH 271/274] VRFS-541 track config bug fixes found during testing --- lib/jam_ruby/connection_manager.rb | 2 +- lib/jam_ruby/models/track.rb | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/jam_ruby/connection_manager.rb b/lib/jam_ruby/connection_manager.rb index c6b10f19b..9709837d6 100644 --- a/lib/jam_ruby/connection_manager.rb +++ b/lib/jam_ruby/connection_manager.rb @@ -278,7 +278,7 @@ SQL # if a blk is passed in, upon success, it will be called and you can issue notifications # within the connection table lock - def join_music_session(user, client_id, music_session, as_musician, tracks, &blk) + def join_music_session(user, client_id, music_session, as_musician, tracks, &blk) connection = nil user_id = user.id music_session_id = music_session.id diff --git a/lib/jam_ruby/models/track.rb b/lib/jam_ruby/models/track.rb index d39323bdc..7ae02b24c 100644 --- a/lib/jam_ruby/models/track.rb +++ b/lib/jam_ruby/models/track.rb @@ -5,6 +5,8 @@ module JamRuby self.primary_key = 'id' + default_scope order('created_at ASC') + SOUND = %w(mono stereo) belongs_to :connection, :class_name => "JamRuby::Connection", :inverse_of => :tracks From ca519021ff83f9c3c362ed84c2f885eabe215f8b Mon Sep 17 00:00:00 2001 From: Seth Call Date: Thu, 29 Aug 2013 12:12:38 +0000 Subject: [PATCH 272/274] * VRFS-509 - fixing birth date and gender set on account profile page --- lib/jam_ruby/models/user.rb | 43 +++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/lib/jam_ruby/models/user.rb b/lib/jam_ruby/models/user.rb index 74360bf5a..bccb851d1 100644 --- a/lib/jam_ruby/models/user.rb +++ b/lib/jam_ruby/models/user.rb @@ -9,7 +9,7 @@ module JamRuby :recoverable, :rememberable - attr_accessible :first_name, :last_name, :email, :city, :password, :password_confirmation, :state, :country, :subscribe_email, :terms_of_service, :original_fpfile, :cropped_fpfile, :cropped_s3_path, :photo_url, :crop_selection + attr_accessible :first_name, :last_name, :email, :city, :password, :password_confirmation, :state, :country, :birth_date, :subscribe_email, :terms_of_service, :original_fpfile, :cropped_fpfile, :cropped_s3_path, :photo_url, :crop_selection # updating_password corresponds to a lost_password attr_accessor :updating_password, :updating_email, :updated_email, :update_email_confirmation_url, :administratively_created, :current_password, :setting_password, :confirm_current_password, :updating_avatar @@ -380,6 +380,28 @@ module JamRuby return recordings end + # given an array of instruments, update a user's instruments + def update_instruments(instruments) + # delete all instruments for this user first + unless self.new_record? + MusicianInstrument.delete_all(["user_id = ?", self.id]) + end + + # loop through each instrument in the array and save to the db + instruments.each do |musician_instrument_param| + instrument = Instrument.find(musician_instrument_param[:instrument_id]) + musician_instrument = MusicianInstrument.new + musician_instrument.user = self + musician_instrument.instrument = instrument + musician_instrument.proficiency_level = musician_instrument_param[:proficiency_level] + musician_instrument.priority = musician_instrument_param[:priority] + musician_instrument.save + self.musician_instruments << musician_instrument + end + end + + # this easy_save routine guards against nil sets, but many of these fields can be set to null. + # I've started to use it less as I go forward def easy_save(first_name, last_name, email, password, password_confirmation, musician, gender, birth_date, internet_service_provider, city, state, country, instruments, photo_url) @@ -446,24 +468,7 @@ module JamRuby # instruments unless instruments.nil? - UserManager.active_record_transaction do |user_manager| - # delete all instruments for this user first - unless self.new_record? - MusicianInstrument.delete_all(["user_id = ?", self.id]) - end - - # loop through each instrument in the array and save to the db - instruments.each do |musician_instrument_param| - instrument = Instrument.find(musician_instrument_param[:instrument_id]) - musician_instrument = MusicianInstrument.new - musician_instrument.user = self - musician_instrument.instrument = instrument - musician_instrument.proficiency_level = musician_instrument_param[:proficiency_level] - musician_instrument.priority = musician_instrument_param[:priority] - musician_instrument.save - self.musician_instruments << musician_instrument - end - end + update_instruments(instruments) end # photo url From 05b51756d3af2806d70e9298b3a7e6ac363f4c5b Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sat, 31 Aug 2013 19:54:43 +0000 Subject: [PATCH 273/274] * return user_id as well with connections being deleted VRFS-608 --- lib/jam_ruby/connection_manager.rb | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/jam_ruby/connection_manager.rb b/lib/jam_ruby/connection_manager.rb index 9709837d6..1161e14b4 100644 --- a/lib/jam_ruby/connection_manager.rb +++ b/lib/jam_ruby/connection_manager.rb @@ -118,7 +118,8 @@ SQL end end - # NOTE this is only used for testing purposes; actual deletes will be processed in the websocket context which cleans up dependencies + # NOTE this is only used for testing purposes; + # actual deletes will be processed in the websocket context which cleans up dependencies def expire_stale_connections(max_seconds) self.stale_connection_client_ids(max_seconds).each { |cid| self.delete_connection(cid) } end @@ -143,8 +144,6 @@ SQL client_ids << client_id } - - # @log.debug("*** stale_connection_client_ids: client_ids = #{client_ids.inspect}") end end client_ids @@ -225,7 +224,7 @@ SQL end end - blk.call(conn, count, music_session_id) unless blk.nil? + blk.call(conn, count, music_session_id, user_id) unless blk.nil? return count end end From 102eeb8afb3711f38fb076b2b03fd7b02258fc56 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 1 Sep 2013 02:17:10 +0000 Subject: [PATCH 274/274] * VRFS-594 - adding message factory helper for creating heartbeat_ack --- lib/jam_ruby/message_factory.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/jam_ruby/message_factory.rb b/lib/jam_ruby/message_factory.rb index 144df8e35..05c244c9b 100644 --- a/lib/jam_ruby/message_factory.rb +++ b/lib/jam_ruby/message_factory.rb @@ -194,6 +194,12 @@ return Jampb::ClientMessage.new(:type => ClientMessage::Type::HEARTBEAT, :route_to => SERVER_TARGET, :heartbeat => heartbeat) end + # create a heartbeat ack + def heartbeat_ack() + heartbeat_ack = Jampb::HeartbeatAck.new() + return Jampb::ClientMessage.new(:type => ClientMessage::Type::HEARTBEAT_ACK, :route_to => CLIENT_TARGET, :heartbeat_ack => heartbeat_ack) + end + # is this message directed to the server? def server_directed? msg return msg.route_to == MessageFactory::SERVER_TARGET