From d5b7ec1a9bc9d80aaa7907c3dbad900af5b1f5ef Mon Sep 17 00:00:00 2001 From: Seth Call Date: Wed, 19 Feb 2014 05:37:25 +0000 Subject: [PATCH 01/21] * focus email when you open signin dialog --- web/app/assets/javascripts/web/signinDialog.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/web/app/assets/javascripts/web/signinDialog.js b/web/app/assets/javascripts/web/signinDialog.js index 5d1f4e989..9662a95e8 100644 --- a/web/app/assets/javascripts/web/signinDialog.js +++ b/web/app/assets/javascripts/web/signinDialog.js @@ -62,7 +62,11 @@ } function beforeShow() { - reset(); + reset(); + } + + function afterShow() { + $(dialogId + ' input[name=email]').focus(); } function afterHide() { @@ -73,6 +77,7 @@ var dialogBindings = { 'beforeShow' : beforeShow, + 'afterShow' : afterShow, 'afterHide': afterHide }; From 7a96a0e48e52ce2d0e3ccbf24bd9db690ae368ba Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Wed, 19 Feb 2014 01:27:18 -0500 Subject: [PATCH 02/21] VRFS-1058 fix Join link issue and fix Listen link --- web/app/assets/javascripts/session.js | 52 +++++++++++++++++++-- web/app/assets/javascripts/sessionList.js | 34 +++++++------- web/app/views/clients/_findSession.html.erb | 2 +- 3 files changed, 67 insertions(+), 21 deletions(-) diff --git a/web/app/assets/javascripts/session.js b/web/app/assets/javascripts/session.js index 73d77dc65..b7f20f61e 100644 --- a/web/app/assets/javascripts/session.js +++ b/web/app/assets/javascripts/session.js @@ -86,15 +86,42 @@ 8: { "title": "Session Latency", "message": "The latency of your audio device combined with your Internet connection has become high enough to impact your session quality. For troubleshooting tips, click here." }, // NETWORK_PING, 9: {"title": "", "message": ""}, // BITRATE_THROTTLE_WARN, 10: { "title": "Low Bandwidth", "message": "The available bandwidth on your network has become too low,and this may impact your audio quality. For troubleshooting tips, click here." }, // BANDWIDTH_LOW - //IO related events + + // IO related events 11: { "title": "Input Rate", "message": "The input rate of your audio device is varying too much to deliver good audio quality. For troubleshooting tips, click here." }, // INPUT_IO_RATE 12: {"title": "", "message": ""}, // INPUT_IO_JTR, 13: { "title": "Output Rate", "message": "The output rate of your audio device is varying too much to deliver good audio quality. For troubleshooting tips, click here." }, // OUTPUT_IO_RATE 14: {"title": "", "message": ""}, // OUTPUT_IO_JTR, + // CPU load related 15: { "title": "CPU Utilization High", "message": "The CPU of your computer is unable to keep up with the current processing load, and this may impact your audio quality. For troubleshooting tips, click here." }, // CPU_LOAD 16: {"title": "", "message": ""}, // DECODE_VIOLATIONS, - 17: {"title": "", "message": ""} // LAST_THRESHOLD + 17: {"title": "", "message": ""}, // LAST_THRESHOLD + 18: {"title": "", "message": ""}, // WIFI_NETWORK_ALERT, //user or peer is using wifi + 19: {"title": "", "message": ""}, // NO_VALID_AUDIO_CONFIG, // alert the user to popup a config + 20: {"title": "", "message": ""}, // AUDIO_DEVICE_NOT_PRESENT, // the audio device is not connected + 21: {"title": "", "message": ""}, // RECORD_PLAYBACK_STATE, // record/playback events have occurred + 22: {"title": "", "message": ""}, // RUN_UPDATE_CHECK_BACKGROUND, //this is auto check - do + 23: {"title": "", "message": ""}, // RUN_UPDATE_CHECK_INTERACTIVE, //this is initiated by user + 24: {"title": "", "message": ""}, // STUN_EVENT, // system completed stun test... come get the result + 25: {"title": "", "message": ""}, // DEAD_USER_WARN_EVENT, //the backend is not receiving audio from this peer + 26: {"title": "", "message": ""}, // DEAD_USER_REMOVE_EVENT, //the backend is removing the user from session as no audio is coming from this peer + 27: {"title": "", "message": ""}, // WINDOW_CLOSE_BACKGROUND_MODE, //the user has closed the window and the client is now in background mode + 28: {"title": "", "message": ""}, // WINDOW_OPEN_FOREGROUND_MODE, //the user has opened the window and the client is now in forground mode/ + + 29: {"title": "", "message": ""}, // SESSION_LIVEBROADCAST_FAIL, //error of some sort - so can't broadcast + 30: {"title": "", "message": ""}, // SESSION_LIVEBROADCAST_ACTIVE, //active + 31: {"title": "", "message": ""}, // SESSION_LIVEBROADCAST_STOPPED, //stopped by server/user + 32: {"title": "", "message": ""}, // SESSION_LIVEBROADCAST_PINNED, //node pinned by user + 33: {"title": "", "message": ""}, // SESSION_LIVEBROADCAST_UNPINNED, //node unpinned by user + + 34: {"title": "", "message": ""}, // BACKEND_STATUS_MSG, //status/informational message + 35: {"title": "", "message": ""}, // LOCAL_NETWORK_VARIANCE_HIGH,//the ping time via a hairpin for the user network is unnaturally high or variable. + + //indicates problem with user computer stack or network itself (wifi, antivirus etc) + 36: {"title": "", "message": ""}, // LOCAL_NETWORK_LATENCY_HIGH, + 37: {"title": "", "message": ""}, // RECORDING_CLOSE, //update and remove tracks from front-end + 38: {"title": "", "message": ""} // LAST_ALERT }; @@ -141,7 +168,26 @@ sessionModel.refreshCurrentSession(); // XXX: race condition possible here; other client may not have updated server yet }, 1000); } - } else { + } + else if (type === 24) { // STUN_EVENT + var testResults = context.jamClient.NetworkTestResult(); + + $.each(testResults, function(index, val) { + if (val.bStunFailed) { + // if true we could not reach a stun server + } + else if (val.bRemoteUdpBocked) { + // if true the user cannot communicate with peer via UDP, although they could do LAN based session + } + }); + } + else if (type === 25) { // DEAD_USER_WARN_EVENT + + } + else if (type === 26) { // DEAD_USER_REMOVE_EVENT + + } + else { context.setTimeout(function() { var alert = alert_type[type]; diff --git a/web/app/assets/javascripts/sessionList.js b/web/app/assets/javascripts/sessionList.js index 7c3fd1efb..c275321ac 100644 --- a/web/app/assets/javascripts/sessionList.js +++ b/web/app/assets/javascripts/sessionList.js @@ -116,8 +116,8 @@ latency_text: latencyDescription, latency_style: latencyStyle, sortScore: latencyInfo.sortScore, - play_url: "TODO", - join_link_display_style: "block" // showJoinLink ? "block" : "none" + play_url: context.JK.root_url + "sessions/" + session.id, + join_link_display_style: showJoinLink ? "block" : "none" }; var row = context.JK.fillTemplate(rowTemplate, sessionVals); @@ -131,24 +131,24 @@ return false; // break } }); + if (!insertedEarly) { $(tbGroup).append(row); - - // wire up the Join Link to the T&Cs dialog - var $parentRow = $('tr[id=' + session.id + ']', tbGroup); - - - $('.join-link', $parentRow).click(function(evt) { - // If no FTUE, show that first. - if (!(context.jamClient.FTUEGetStatus())) { - app.afterFtue = function() { joinClick(session.id); }; - app.layout.showDialog('ftue'); - return; - } else { - joinClick(session.id); - } - }); } + + // wire up the Join Link to the T&Cs dialog + var $parentRow = $('tr[id=' + session.id + ']', tbGroup); + + $('.join-link', $parentRow).click(function(evt) { + // If no FTUE, show that first. + if (!(context.jamClient.FTUEGetStatus())) { + app.afterFtue = function() { joinClick(session.id); }; + app.layout.showDialog('ftue'); + return; + } else { + joinClick(session.id); + } + }); } function joinClick(sessionId) { diff --git a/web/app/views/clients/_findSession.html.erb b/web/app/views/clients/_findSession.html.erb index c4ca01ee5..2765fa7a2 100644 --- a/web/app/views/clients/_findSession.html.erb +++ b/web/app/views/clients/_findSession.html.erb @@ -71,7 +71,7 @@
{latency_text}
- + <%= image_tag "content/icon_playbutton.png", :size => "20x20" %> From b21fcd74b234b322a57f48223b32e89a2135f0c9 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Wed, 19 Feb 2014 15:15:55 +0000 Subject: [PATCH 03/21] * allow duration to be specified --- admin/app/views/admin/recordings/_form.html.haml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/admin/app/views/admin/recordings/_form.html.haml b/admin/app/views/admin/recordings/_form.html.haml index 880f26823..2c1bb2ce0 100644 --- a/admin/app/views/admin/recordings/_form.html.haml +++ b/admin/app/views/admin/recordings/_form.html.haml @@ -26,6 +26,8 @@ = f.input :band, :as => :autocomplete, :url => autocomplete_band_name_admin_bands_path, :input_html => { :id => "jam_ruby_recording_band", :name => "", :id_element => "#jam_ruby_recording_band_id" } = f.input :band_id, :as => :hidden, :input_html => { :name => "jam_ruby_recording[band_id]" } + = f.input :duration, :hint => 'how long the recording is (in seconds)' + = f.semantic_fields_for :recorded_tracks do |recorded_track, index| = render 'recorded_track_fields', f: recorded_track .links From 857dd27404b46997b83f40dd98429286ff0da7f4 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Wed, 19 Feb 2014 22:56:13 +0000 Subject: [PATCH 04/21] * VRFS-1191, VRFS-1120, VRFS-1079, VRFS-1087, VRFS-1190, VRFS-1192--many band page changes, fixes. small fix for notification icons showing orange circle if icon_url not specified --- ruby/lib/jam_ruby.rb | 1 + ruby/lib/jam_ruby/constants/limits.rb | 4 + .../jam_ruby/constants/validation_messages.rb | 9 +- ruby/lib/jam_ruby/models/band.rb | 141 ++++++-------- ruby/lib/jam_ruby/models/band_genre.rb | 11 ++ ruby/lib/jam_ruby/models/genre.rb | 3 +- ruby/lib/jam_ruby/models/music_session.rb | 8 +- ruby/lib/jam_ruby/models/recording.rb | 2 +- ruby/spec/factories.rb | 8 + .../jam_ruby/models/band_location_spec.rb | 8 +- ruby/spec/jam_ruby/models/band_search_spec.rb | 56 +++--- ruby/spec/jam_ruby/models/band_spec.rb | 77 +++++++- web/app/assets/javascripts/bandProfile.js | 74 +++---- web/app/assets/javascripts/band_setup.js | 181 ++++++------------ .../assets/javascripts/band_setup_photo.js | 9 +- web/app/assets/javascripts/jam_rest.js | 12 ++ web/app/assets/javascripts/layout.js | 5 +- web/app/assets/javascripts/profile.js | 4 +- .../assets/stylesheets/client/band.css.scss | 11 +- web/app/controllers/api_bands_controller.rb | 38 ++-- web/app/controllers/api_controller.rb | 19 ++ web/app/views/api_bands/validate.rabl | 2 + web/app/views/clients/_band_setup.html.erb | 49 +++-- .../views/clients/_band_setup_photo.html.erb | 9 +- web/app/views/clients/_notify.html.erb | 2 +- web/app/views/clients/_profile.html.erb | 6 +- web/app/views/users/_user_dropdown.html.erb | 2 +- web/config/routes.rb | 1 + web/lib/tasks/import_max_mind.rake | 6 +- web/lib/tasks/sample_data.rake | 87 +++++---- web/spec/factories.rb | 20 +- web/spec/features/bands_spec.rb | 30 ++- web/spec/features/musician_search_spec.rb | 2 + web/spec/features/whats_next_spec.rb | 1 + web/spec/requests/bands_api_spec.rb | 87 +++++---- web/spec/requests/search_api_spec.rb | 16 +- web/spec/spec_helper.rb | 2 - 37 files changed, 580 insertions(+), 423 deletions(-) create mode 100644 ruby/lib/jam_ruby/models/band_genre.rb create mode 100644 web/app/views/api_bands/validate.rabl diff --git a/ruby/lib/jam_ruby.rb b/ruby/lib/jam_ruby.rb index 6997821b9..973ed5efe 100755 --- a/ruby/lib/jam_ruby.rb +++ b/ruby/lib/jam_ruby.rb @@ -57,6 +57,7 @@ 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/band_genre" require "jam_ruby/models/genre" require "jam_ruby/models/user" require "jam_ruby/models/user_observer" diff --git a/ruby/lib/jam_ruby/constants/limits.rb b/ruby/lib/jam_ruby/constants/limits.rb index 52e2036f1..a0932a389 100644 --- a/ruby/lib/jam_ruby/constants/limits.rb +++ b/ruby/lib/jam_ruby/constants/limits.rb @@ -1,5 +1,9 @@ module Limits + # session genres + MIN_GENRES_PER_SESSION = 1 + MAX_GENRES_PER_SESSION = 3 + # band genres MIN_GENRES_PER_BAND = 1 MAX_GENRES_PER_BAND = 3 diff --git a/ruby/lib/jam_ruby/constants/validation_messages.rb b/ruby/lib/jam_ruby/constants/validation_messages.rb index 8c2b8d3df..4a308798b 100644 --- a/ruby/lib/jam_ruby/constants/validation_messages.rb +++ b/ruby/lib/jam_ruby/constants/validation_messages.rb @@ -20,8 +20,13 @@ module ValidationMessages SESSION_NOT_FOUND = "Session not found." # genres - GENRE_LIMIT_EXCEEDED = "No more than 1 genre is allowed." - GENRE_MINIMUM_NOT_MET = "At least 1 genre is required." + RECORDING_GENRE_LIMIT_EXCEEDED = "No more than 1 genre is allowed." + BAND_GENRE_LIMIT_EXCEEDED = "No more than 3 genres are allowed." + SESSION_GENRE_LIMIT_EXCEEDED = "No more than 3 genres are allowed." + + RECORDING_GENRE_MINIMUM_NOT_MET = "At least 1 genre is required." + BAND_GENRE_MINIMUM_NOT_MET = "At least 1 genre is required." + SESSION_GENRE_MINIMUM_NOT_MET = "At least 1 genre is required." # instruments INSTRUMENT_LIMIT_EXCEEDED = "No more than 5 instruments are allowed." diff --git a/ruby/lib/jam_ruby/models/band.rb b/ruby/lib/jam_ruby/models/band.rb index 88d97bc14..72838fad0 100644 --- a/ruby/lib/jam_ruby/models/band.rb +++ b/ruby/lib/jam_ruby/models/band.rb @@ -5,13 +5,20 @@ module JamRuby :country, :original_fpfile_photo, :cropped_fpfile_photo, :cropped_large_fpfile_photo, :cropped_s3_path_photo, :cropped_large_s3_path_photo, :crop_selection_photo, :photo_url, :large_photo_url - attr_accessor :updating_photo + attr_accessor :updating_photo, :skip_location_validation self.primary_key = 'id' before_save :stringify_photo_info , :if => :updating_photo + validates :biography, no_profanity: true, presence:true + validates :name, presence: true + validates :country, presence: true, :unless => :skip_location_validation + validates :state, presence: true, :unless => :skip_location_validation + validates :city, presence: true, :unless => :skip_location_validation + validate :validate_photo_info - validates :biography, no_profanity: true + validate :require_at_least_one_genre + validate :limit_max_genres before_save :check_lat_lng before_save :check_website_url @@ -21,7 +28,8 @@ module JamRuby 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" + has_many :band_genres, class_name: "JamRuby::BandGenre" + has_many :genres, class_name: "JamRuby::Genre", :through => :band_genres # recordings has_many :recordings, :class_name => "JamRuby::Recording", :foreign_key => "band_id" @@ -123,74 +131,52 @@ module JamRuby return recordings end + def self.build_band(user, params) + + id = params[:id] + + # ensure person creating this Band is a Musician + unless user.musician? + raise PermissionError, "must be a musician" + end + + band = id.blank? ? Band.new : Band.find(id) + + # ensure user updating Band details is a Band member + unless band.new_record? || band.users.exists?(user) + raise PermissionError, ValidationMessages::USER_NOT_BAND_MEMBER_VALIDATION_ERROR + end + + band.name = params[:name] if params.has_key?(:name) + band.website = params[:website] if params.has_key?(:website) + band.biography = params[:biography] if params.has_key?(:biography) + band.city = params[:city] if params.has_key?(:city) + band.state = params[:state] if params.has_key?(:state) + band.country = params[:country] if params.has_key?(:country) + band.photo_url = params[:photo_url] if params.has_key?(:photo_url) + band.logo_url = params[:logo_url] if params.has_key?(:logo_url) + + if params.has_key?(:genres) && params[:genres] + # loop through each genre in the array and save to the db + genres = [] + params[:genres].each { |genre_id| genres << Genre.find(genre_id) } + band.genres = genres + end + + + band + 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) - user = User.find(user_id) + def self.save(user, params) + band = build_band(user, params) - # new band - if id.blank? - - # 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() - - # band update - 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 + if band.save + # add the creator as the admin + BandMusician.create(:band_id => band.id, :user_id => user.id, :admin => true) if params[:id].blank? end - # name - band.name = name unless name.nil? - - # website - band.website = website unless website.nil? - - # biography - band.biography = biography unless biography.nil? - - # city - band.city = city unless city.nil? - - # state - band.state = state unless state.nil? - - # country - band.country = country unless country.nil? - - # photo url - band.photo_url = photo_url unless photo_url.nil? - - # logo url - band.logo_url = logo_url unless logo_url.nil? - - # band.updated_at = Time.now.getutc - band.save! - band.reload - - # genres - unless genres.nil? - ActiveRecord::Base.transaction do - # delete all genres for this band first - band.genres.delete_all if id.present? - # loop through each genre in the array and save to the db - genres.each { |genre_id| band.genres << Genre.find(genre_id) } - end - end - - # add the creator as the admin - BandMusician.create(:band_id => band.id, :user_id => user_id, :admin => true) if id.blank? - - return band + band end def escape_filename(path) @@ -274,21 +260,16 @@ module JamRuby end private - def self.validate_genres(genres, is_nil_ok) - if is_nil_ok && genres.nil? - return + + def require_at_least_one_genre + if self.genres.size < Limits::MIN_GENRES_PER_BAND + errors.add(:genres, ValidationMessages::BAND_GENRE_MINIMUM_NOT_MET) end + 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 + def limit_max_genres + if self.genres.size > Limits::MAX_GENRES_PER_BAND + errors.add(:genres, ValidationMessages::BAND_GENRE_LIMIT_EXCEEDED) end end diff --git a/ruby/lib/jam_ruby/models/band_genre.rb b/ruby/lib/jam_ruby/models/band_genre.rb new file mode 100644 index 000000000..9bd4051d4 --- /dev/null +++ b/ruby/lib/jam_ruby/models/band_genre.rb @@ -0,0 +1,11 @@ +module JamRuby + class BandGenre < ActiveRecord::Base + + self.table_name = "bands_genres" + + self.primary_key = 'id' + + belongs_to :user, class_name: "JamRuby::User" + belongs_to :genre, class_name: "JamRuby::Genre" + end +end \ No newline at end of file diff --git a/ruby/lib/jam_ruby/models/genre.rb b/ruby/lib/jam_ruby/models/genre.rb index 45776c59f..18fef77b8 100644 --- a/ruby/lib/jam_ruby/models/genre.rb +++ b/ruby/lib/jam_ruby/models/genre.rb @@ -4,7 +4,8 @@ module JamRuby self.primary_key = 'id' # bands - has_and_belongs_to_many :bands, :class_name => "JamRuby::Band", :join_table => "bands_genres" + has_many :band_genres, class_name: "JamRuby::BandGenre" + has_many :bands, class_name: "JamRuby::Band", :through => :band_genres # genres has_and_belongs_to_many :recordings, :class_name => "JamRuby::Recording", :join_table => "recordings_genres" diff --git a/ruby/lib/jam_ruby/models/music_session.rb b/ruby/lib/jam_ruby/models/music_session.rb index 57ebcee50..e4ca47cfb 100644 --- a/ruby/lib/jam_ruby/models/music_session.rb +++ b/ruby/lib/jam_ruby/models/music_session.rb @@ -264,16 +264,16 @@ module JamRuby def require_at_least_one_genre unless skip_genre_validation - if self.genres.count < Limits::MIN_GENRES_PER_RECORDING - errors.add(:genres, ValidationMessages::GENRE_MINIMUM_NOT_MET) + if self.genres.count < Limits::MIN_GENRES_PER_SESSION + errors.add(:genres, ValidationMessages::SESSION_GENRE_MINIMUM_NOT_MET) end end end def limit_max_genres unless skip_genre_validation - if self.genres.count > Limits::MAX_GENRES_PER_RECORDING - errors.add(:genres, ValidationMessages::GENRE_LIMIT_EXCEEDED) + if self.genres.count > Limits::MAX_GENRES_PER_SESSION + errors.add(:genres, ValidationMessages::SESSION_GENRE_LIMIT_EXCEEDED) end end end diff --git a/ruby/lib/jam_ruby/models/recording.rb b/ruby/lib/jam_ruby/models/recording.rb index 19ddd357b..8fc666e0f 100644 --- a/ruby/lib/jam_ruby/models/recording.rb +++ b/ruby/lib/jam_ruby/models/recording.rb @@ -3,7 +3,7 @@ module JamRuby self.primary_key = 'id' - attr_accessible :owner, :owner_id, :band, :band_id, :recorded_tracks_attributes, :mixes_attributes, :claimed_recordings_attributes, :name, :description, :genre, :is_public, :is_downloadable, as: :admin + attr_accessible :owner, :owner_id, :band, :band_id, :recorded_tracks_attributes, :mixes_attributes, :claimed_recordings_attributes, :name, :description, :genre, :is_public, :is_downloadable, :duration, as: :admin has_many :claimed_recordings, :class_name => "JamRuby::ClaimedRecording", :inverse_of => :recording, :foreign_key => 'recording_id', :dependent => :destroy has_many :users, :through => :recorded_tracks, :class_name => "JamRuby::User" diff --git a/ruby/spec/factories.rb b/ruby/spec/factories.rb index 9868bf2e5..8c61cc99d 100644 --- a/ruby/spec/factories.rb +++ b/ruby/spec/factories.rb @@ -14,12 +14,17 @@ FactoryGirl.define do 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 :fan do + musician false + end + factory :admin do admin true end @@ -101,6 +106,9 @@ FactoryGirl.define do city "Apex" state "NC" country "US" + before(:create) { |band| + band.genres << Genre.first + } end factory :genre, :class => JamRuby::Genre do diff --git a/ruby/spec/jam_ruby/models/band_location_spec.rb b/ruby/spec/jam_ruby/models/band_location_spec.rb index fae562a53..e70fd09fd 100644 --- a/ruby/spec/jam_ruby/models/band_location_spec.rb +++ b/ruby/spec/jam_ruby/models/band_location_spec.rb @@ -2,6 +2,11 @@ require 'spec_helper' describe Band do + before(:all) do + MaxMindIsp.delete_all + MaxMindGeo.delete_all + end + before do @geocode1 = FactoryGirl.create(:geocoder) @geocode2 = FactoryGirl.create(:geocoder) @@ -27,7 +32,8 @@ describe Band do describe "without location data" do it "should have nil lat/lng values without address" do - @band.update_attributes({ :city => nil, + @band.skip_location_validation = true + @band.update_attributes({ :city => nil, :state => nil, :country => nil, }) diff --git a/ruby/spec/jam_ruby/models/band_search_spec.rb b/ruby/spec/jam_ruby/models/band_search_spec.rb index 5d693ba5f..2f885138f 100644 --- a/ruby/spec/jam_ruby/models/band_search_spec.rb +++ b/ruby/spec/jam_ruby/models/band_search_spec.rb @@ -3,12 +3,24 @@ require 'spec_helper' describe User do let(:user) { FactoryGirl.create(:user) } + let(:band) { FactoryGirl.create(:band, name: "Example Band") } + let(:band_params) { + { + name: "The Band", + biography: "Biography", + city: 'Austin', + state: 'TX', + country: 'US', + genres: ['country'] + } + } before(:each) do @geocode1 = FactoryGirl.create(:geocoder) @geocode2 = FactoryGirl.create(:geocoder) @user = FactoryGirl.create(:user) - @band = Band.save(nil, "Example Band", "www.bands.com", "zomg we rock", "Apex", "NC", "US", ["hip hop"], user.id, nil, nil) + band.touch + end @@ -16,43 +28,43 @@ describe User do ws = Search.band_search("Example Band").results 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 + band_result.name.should == band.name + band_result.id.should == band.id + band_result.location.should == band.location end it "should allow search of one band with partial matches" do ws = Search.band_search("Ex").results ws.length.should == 1 - ws[0].id.should == @band.id + ws[0].id.should == band.id ws = Search.band_search("Exa").results ws.length.should == 1 - ws[0].id.should == @band.id + ws[0].id.should == band.id ws = Search.band_search("Exam").results ws.length.should == 1 - ws[0].id.should == @band.id + ws[0].id.should == band.id ws = Search.band_search("Examp").results ws.length.should == 1 - ws[0].id.should == @band.id + ws[0].id.should == band.id ws = Search.band_search("Exampl").results ws.length.should == 1 - ws[0].id.should == @band.id + ws[0].id.should == band.id ws = Search.band_search("Example").results ws.length.should == 1 - ws[0].id.should == @band.id + ws[0].id.should == band.id ws = Search.band_search("Ba").results ws.length.should == 1 - ws[0].id.should == @band.id + ws[0].id.should == band.id ws = Search.band_search("Ban").results ws.length.should == 1 - ws[0].id.should == @band.id + ws[0].id.should == band.id end it "should not match mid-word searchs" do @@ -67,9 +79,9 @@ describe User do ws = Search.band_search("Example Band").results ws.length.should == 1 band_result = ws[0] - band_result.id.should == @band.id + band_result.id.should == band.id - @band.destroy # delete doesn't work; you have to use destroy. + band.destroy # delete doesn't work; you have to use destroy. ws = Search.band_search("Example Band").results ws.length.should == 0 @@ -79,10 +91,10 @@ describe User do ws = Search.band_search("Example Band").results ws.length.should == 1 band_result = ws[0] - band_result.id.should == @band.id + band_result.id.should == band.id - @band.name = "bonus-stuff" - @band.save + band.name = "bonus-stuff" + band.save ws = Search.band_search("Example Band").results ws.length.should == 0 @@ -90,25 +102,25 @@ describe User do ws = Search.band_search("Bonus").results ws.length.should == 1 band_result = ws[0] - band_result.id.should == @band.id + 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", "US", ["hip hop"], user.id, nil, nil) + band2 = FactoryGirl.create(:band, name: 'Peach pit') ws = Search.band_search("pea").results ws.length.should == 1 user_result = ws[0] - user_result.id.should == @band2.id + 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", "US", ["hip hop"], user.id, nil, nil) + band2 = FactoryGirl.create(:band, name: 'Peach pit') ws = Search.band_search("pe").results ws.length.should == 1 user_result = ws[0] - user_result.id.should == @band2.id + user_result.id.should == band2.id ws = Search.band_search("p").results ws.length.should == 0 diff --git a/ruby/spec/jam_ruby/models/band_spec.rb b/ruby/spec/jam_ruby/models/band_spec.rb index 3f1c1c289..25f2b4bb1 100644 --- a/ruby/spec/jam_ruby/models/band_spec.rb +++ b/ruby/spec/jam_ruby/models/band_spec.rb @@ -2,7 +2,21 @@ require 'spec_helper' describe Band do - let(:band) { FactoryGirl.create(:band) } + let(:user) { FactoryGirl.create(:user) } + let(:user2) { FactoryGirl.create(:user) } + let(:fan) { FactoryGirl.create(:fan) } + let(:band) { FactoryGirl.create(:band) } + let(:new_band) { FactoryGirl.build(:band) } + let(:band_params) { + { + name: "The Band", + biography: "Biography", + city: 'Austin', + state: 'TX', + country: 'US', + genres: ['country'] + } + } describe 'website update' do it 'should have http prefix on website url' do @@ -12,4 +26,65 @@ describe Band do end end + describe 'band validations' do + it "minimum genres" do + new_band.save.should be_false + new_band.errors[:genres].should == [ValidationMessages::BAND_GENRE_MINIMUM_NOT_MET] + end + + it "maximum genres" do + new_band.genres = Genre.limit(4) + new_band.save.should be_false + new_band.errors[:genres].should == [ValidationMessages::BAND_GENRE_LIMIT_EXCEEDED] + end + end + + describe "save" do + it "can succeed" do + band = Band.save(user, band_params) + band.errors.any?.should be_false + band.name.should == band_params[:name] + band.biography.should == band_params[:biography] + band.genres.should == [Genre.find(band_params[:genres][0])] + band.city.should == band_params[:city] + band.state.should == band_params[:state] + band.country.should == band_params[:country] + end + + it "ensures user is a musician" do + expect{ Band.save(fan, band_params) }.to raise_error("must be a musician") + end + + it "can update" do + band = Band.save(user, band_params) + band.errors.any?.should be_false + band_params[:id] = band.id + band_params[:name] = "changed name" + band = Band.save(user, band_params) + band.errors.any?.should be_false + Band.find(band.id).name.should == band_params[:name] + end + + it "stops non-members from updating" do + band = Band.save(user, band_params) + band.errors.any?.should be_false + band_params[:id] = band.id + band_params[:name] = "changed name" + expect{ Band.save(user2, band_params) }.to raise_error(ValidationMessages::USER_NOT_BAND_MEMBER_VALIDATION_ERROR) + end + end + + describe "validate" do + it "can pass" do + band = Band.build_band(user, band_params) + band.valid?.should be_true + end + + it "can fail" do + band_params[:name] = nil + band = Band.build_band(user, band_params) + band.valid?.should be_false + band.errors[:name].should == ["can't be blank"] + end + end end diff --git a/web/app/assets/javascripts/bandProfile.js b/web/app/assets/javascripts/bandProfile.js index cdb49ae8b..20ff38afc 100644 --- a/web/app/assets/javascripts/bandProfile.js +++ b/web/app/assets/javascripts/bandProfile.js @@ -16,9 +16,20 @@ } function afterShow(data) { - resetForm(); - events(); - renderActive(); + + // hide until we know if 'isMember' + $("#btn-follow-band").hide(); + $("#btn-edit-band-profile").hide(); + + resetForm(); + events(); + determineMembership() + .done(function() { + renderActive(); + }) + .fail(function(jqXHR) { + app.notifyServerError(jqXHR, "Unable to talk to server") + }) } function resetForm() { @@ -128,6 +139,16 @@ // refreshes the currently active tab function renderActive() { + + if (isMember) { + $("#btn-follow-band").hide(); + $("#btn-edit-band-profile").show(); + } + else { + $("#btn-follow-band").show(); + $("#btn-edit-band-profile").hide(); + } + if ($('#band-profile-about-link').hasClass('active')) { renderAbout(); } @@ -161,7 +182,6 @@ rest.getBand(bandId) .done(function(response) { band = response; - setIsMember(); if (band) { // name $('#band-profile-name').html(band.name); @@ -379,47 +399,37 @@ } } - // TODO: refactor // checks if person viewing the profile is also a band member - function setIsMember() { + function determineMembership() { var url = "/api/bands/" + bandId + "/musicians"; - $.ajax({ + return $.ajax({ type: "GET", dataType: "json", url: url, - async: false, processData:false, - success: function(response) { - $.each(response, function(index, val) { - if (val.id === context.JK.currentUserId) { - isMember = true; - } - }); - }, error: app.ajaxError - }); + }) + .done(function(response) { + isMember = false; + $.each(response, function(index, val) { + if (val.id === context.JK.currentUserId) { + isMember = true; + } + }); + }) } // events for main screen function events() { // wire up panel clicks - $('#band-profile-about-link').click(renderAbout); - $('#band-profile-history-link').click(renderHistory); - $('#band-profile-members-link').click(renderMembers); - $('#band-profile-social-link').click(renderSocial); + $('#band-profile-about-link').unbind('click').click(renderAbout); + $('#band-profile-history-link').unbind('click').click(renderHistory); + $('#band-profile-members-link').unbind('click').click(renderMembers); + $('#band-profile-social-link').unbind('click').click(renderSocial); - if (isMember) { - $("#btn-follow-band").hide(); - $("#btn-edit-band-profile").show(); - } - else { - $("#btn-follow-band").show(); - $("#btn-edit-band-profile").hide(); - } - - $("#btn-edit-band-profile").click(function() { - $('div[layout-id="band/setup"] .hdn-band-id').val(bandId); - context.location = "/client#/band/setup"; + $("#btn-edit-band-profile").unbind('click').click(function() { + //$('div[layout-id="band/setup"] .hdn-band-id').val(bandId); + context.location = "/client#/band/setup/" + bandId; return false; }); } diff --git a/web/app/assets/javascripts/band_setup.js b/web/app/assets/javascripts/band_setup.js index 79d25bcb1..875f31f41 100644 --- a/web/app/assets/javascripts/band_setup.js +++ b/web/app/assets/javascripts/band_setup.js @@ -21,45 +21,43 @@ var nilOptionText = 'n/a'; var bandId = ''; + function is_new_record() { + return bandId.length == 0; + } + + function removeErrors() { + $('#band-setup-form .error-text').remove() + $('#band-setup-form .error').removeClass("error") + + } + function resetForm() { + removeErrors(); + // name $("#band-name").val(''); - var $tdName = $("#tdBandName"); - $tdName.find('.error-text').remove(); - $tdName.removeClass("error"); // country $("#band-country").empty(); $("#band-country").val(''); - var $tdCountry = $("#tdBandCountry"); - $tdCountry.find('.error-text').remove(); - $tdCountry.removeClass("error"); // region $("#band-region").empty(); $("#band-region").val(''); - var $tdRegion = $("#tdBandRegion"); - $tdRegion.find('.error-text').remove(); - $tdRegion.removeClass("error"); // city $("#band-city").empty(); $("#band-city").val(''); - var $tdCity = $("#tdBandCity"); - $tdCity.find('.error-text').remove(); - $tdCity.removeClass("error"); // description $("#band-biography").val(''); - var $tdBiography = $("#tdBandBiography"); - $tdBiography.find('.error-text').remove(); - $tdBiography.removeClass("error"); + + // website + $('#band-website').val(''); resetGenres(); - $("#hdn-band-id").val(''); - $("#band-setup-step-1").show(); $("#band-setup-step-2").hide(); @@ -75,8 +73,6 @@ }); var $tdGenres = $("#tdBandGenres"); - $tdGenres.find('.error-text').remove(); - $tdGenres.removeClass("error"); } function getSelectedGenres() { @@ -90,96 +86,32 @@ } function validateGeneralInfo() { - var isValid = true; + removeErrors(); - // name - var $name = $("#band-name"); - var $tdName = $("#tdBandName"); - var name = $.trim($name.val()); - if (name.length === 0) { - $tdName.find('.error-text').remove(); - $tdName.addClass("error"); - $name.after("
  • Name is required
"); - isValid = false; - } - else { - $tdName.removeClass("error"); - } + var band = buildBand(); - // country - var $country = $("#band-country"); - var $tdCountry = $("#tdBandCountry"); - var country = $.trim($country.val()); - if (country.length === 0) { - $tdCountry.find('.error-text').remove(); - $tdCountry.addClass("error"); - $country.after("
  • Country is required
"); - isValid = false; - } - else { - $tdCountry.removeClass("error"); - } - - // region - var $region = $("#band-region"); - var $tdRegion = $("#tdBandRegion"); - var region = $.trim($region.val()); - if (region.length === 0) { - $tdRegion.find('.error-text').remove(); - $tdRegion.addClass("error"); - $region.after("
  • Region is required
"); - isValid = false; - } - else { - $tdRegion.removeClass("error"); - } - - // city - var $city = $("#band-city"); - var $tdCity = $("#tdBandCity"); - var city = $.trim($city.val()); - if (city.length === 0) { - $tdCity.find('.error-text').remove(); - $tdCity.addClass("error"); - $city.after("
  • City is required
"); - isValid = false; - } - else { - $tdCity.removeClass("error"); - } - - // genres (no more than 3) - var $genres = $(".band-setup-genres"); - var $tdGenres = $("#tdBandGenres"); - var selectedGenres = getSelectedGenres(); - if (selectedGenres.length === 0 || selectedGenres.length > 3) { - $tdGenres.find('.error-text').remove(); - $tdGenres.addClass("error"); - $genres.after("
  • Genre is required (3 maximum)
"); - isValid = false; - } - else { - $tdGenres.removeClass("error"); - } - - // description - var $biography = $("#band-biography"); - var $tdBiography = $("#tdBandBiography"); - var biography = $.trim($biography.val()); - if (biography.length === 0) { - $tdBiography.find('.error-text').remove(); - $tdBiography.addClass("error"); - $biography.after("
  • Biography is required
"); - isValid = false; - } - else { - $tdBiography.removeClass("error"); - } - - return isValid; + return rest.validateBand(band); } - function saveBand() { + function renderErrors(errors) { + var name = context.JK.format_errors("name", errors); + var country = context.JK.format_errors("country", errors); + var state = context.JK.format_errors("state", errors); + var city = context.JK.format_errors("city", errors); + var biography = context.JK.format_errors("biography", errors); + var genres = context.JK.format_errors("genres", errors); + var website = context.JK.format_errors("website", errors); + + if(name) $("#band-name").closest('div.field').addClass('error').end().after(name); + if(country) $("#band-country").closest('div.field').addClass('error').end().after(country); + if(state) $("#band-region").closest('div.field').addClass('error').end().after(state); + if(city) $("#band-city").closest('div.field').addClass('error').end().after(city); + if(genres) $(".band-setup-genres").closest('div.field').addClass('error').end().after(genres); + if(biography) $("#band-biography").closest('div.field').addClass('error').end().after(biography); + if(website) $("#band-website").closest('div.field').addClass('error').end().after(website); + } + + function buildBand() { var band = {}; band.name = $("#band-name").val(); band.website = $("#band-website").val(); @@ -188,8 +120,13 @@ band.state = $("#band-region").val(); band.country = $("#band-country").val(); band.genres = getSelectedGenres(); + return band; + } - if (bandId.length === 0) { + function saveBand() { + var band = buildBand() + + if (is_new_record()) { rest.createBand(band) .done(function (response) { createBandInvitations(response.id, function () { @@ -244,8 +181,7 @@ userNames = []; userIds = []; userPhotoUrls = []; - //bandId = "1158c8b6-4c92-47dc-82bf-1e390c4f9b2c"; - bandId = $("#hdn-band-id").val(); + bandId = data.id == 'new' ? '' : data.id; resetForm(); } @@ -253,10 +189,11 @@ friendSelectorDialog.setCallback(friendSelectorCallback); loadFriends(); - if (bandId.length > 0) { + if (!is_new_record()) { $("#band-setup-title").html("edit band"); $("#btn-band-setup-save").html("SAVE CHANGES"); $("#band-change-photo").html('Upload band photo.'); + $('#tdBandPhoto').css('visibility', 'visible'); // retrieve and initialize band profile data points loadBandDetails(); @@ -276,8 +213,7 @@ $("#band-setup-title").html("set up band"); $("#btn-band-setup-save").html("CREATE BAND"); - - $("#band-change-photo").html('Upload band photo (optional).'); + $('#tdBandPhoto').css('visibility', 'hidden'); // can't upload photo when going through initial setup } } @@ -479,8 +415,7 @@ function navigateToBandPhoto(evt) { evt.stopPropagation(); - $("#hdn-band-id").val(bandId); - context.location = '/client#/band/setup/photo'; + context.location = '/client#/band/setup/photo/' + bandId; return false; } @@ -499,14 +434,24 @@ $('#btn-band-setup-cancel').click(function () { resetForm(); - context.location = "/client#/home"; + window.history.go(-1); + return false; }); $('#btn-band-setup-next').click(function () { - if (validateGeneralInfo()) { - $("#band-setup-step-2").show(); - $("#band-setup-step-1").hide(); - } + validateGeneralInfo() + .done(function (response) { + $("#band-setup-step-2").show(); + $("#band-setup-step-1").hide(); + }) + .fail(function (jqXHR) { + if(jqXHR.status == 422) { + renderErrors(JSON.parse(jqXHR.responseText)) + } + else { + app.notifyServerError(jqXHR, "Unable to validate band") + } + }); }); $('#btn-band-setup-back').click(function () { diff --git a/web/app/assets/javascripts/band_setup_photo.js b/web/app/assets/javascripts/band_setup_photo.js index 193266131..b758d2737 100644 --- a/web/app/assets/javascripts/band_setup_photo.js +++ b/web/app/assets/javascripts/band_setup_photo.js @@ -18,11 +18,7 @@ var updatingBandPhoto = false; function beforeShow(data) { - bandId = $("#hdn-band-id").val(); - logger.debug("bandId=" + bandId); - if (!bandId) { - context.location = '/client#/home'; - } + bandId = data.id; } function afterShow(data) { @@ -143,8 +139,7 @@ function navToEditProfile() { resetForm(); - $("#hdn-band-id").val(bandId); - context.location = '/client#/band/setup'; + context.location = '/client#/band/setup/' + bandId; } function renderBandPhotoSpinner() { diff --git a/web/app/assets/javascripts/jam_rest.js b/web/app/assets/javascripts/jam_rest.js index f80c0f037..5090066db 100644 --- a/web/app/assets/javascripts/jam_rest.js +++ b/web/app/assets/javascripts/jam_rest.js @@ -103,6 +103,17 @@ }); } + function validateBand(band) { + return $.ajax({ + type: "POST", + dataType: "json", + url: '/api/bands/validate', + contentType: 'application/json', + processData: false, + data: JSON.stringify(band) + }); + } + function getBand(bandId) { return $.ajax({ type: "GET", @@ -894,6 +905,7 @@ this.deleteBandPhoto = deleteBandPhoto; this.getBandPhotoFilepickerPolicy = getBandPhotoFilepickerPolicy; this.getBand = getBand; + this.validateBand = validateBand; this.createBandInvitation = createBandInvitation; this.updateBandInvitation = updateBandInvitation; this.removeBandMember = removeBandMember; diff --git a/web/app/assets/javascripts/layout.js b/web/app/assets/javascripts/layout.js index b80f7aa48..ef88e9f06 100644 --- a/web/app/assets/javascripts/layout.js +++ b/web/app/assets/javascripts/layout.js @@ -678,13 +678,12 @@ $('p', $notify).html( message.text); } - if (message.icon_url) { $('#avatar', $notify).attr('src', message.icon_url); - $('#avatar', $notify).show(); + $('#notify-avatar', $notify).show(); } else { - $('#avatar', $notify).hide(); + $('#notify-avatar', $notify).hide(); } if (message.detail) { diff --git a/web/app/assets/javascripts/profile.js b/web/app/assets/javascripts/profile.js index d4e5ec5b3..ff15ff574 100644 --- a/web/app/assets/javascripts/profile.js +++ b/web/app/assets/javascripts/profile.js @@ -512,7 +512,7 @@ bandId: val.id, biography: val.biography, band_url: "/client#/bandProfile/" + val.id, - avatar_url: context.JK.resolveBandAvatarUrl(val.logo_url), + avatar_url: context.JK.resolveBandAvatarUrl(val.photo_url), name: val.name, location: val.location, genres: formatGenres(val.genres), @@ -594,7 +594,7 @@ } function configureBandFollowingButton(following, bandId) { - var $btnFollowBand = $('div[band-id=' + bandId + ']', '#profile-bands').find('#btn-follow-band'); + var $btnFollowBand = $('div[band-id=' + bandId + ']', '#profile-bands').find('#btn-follow-band-2'); $btnFollowBand.unbind("click"); if (following) { diff --git a/web/app/assets/stylesheets/client/band.css.scss b/web/app/assets/stylesheets/client/band.css.scss index 37f40c2bb..25f45e9a2 100644 --- a/web/app/assets/stylesheets/client/band.css.scss +++ b/web/app/assets/stylesheets/client/band.css.scss @@ -1,5 +1,6 @@ @import "client/common.css.scss"; + .band-setup-bio { height:90px; overflow:auto; @@ -21,7 +22,7 @@ .avatar-space { color: $color2; - margin-bottom: 20px; + margin: 20px; position:relative; min-height:300px; @@ -336,4 +337,12 @@ .easydropdown-wrapper { width:80%; } + + div.field { + margin: 10px; + } + + label { + margin-bottom:2px; + } } \ No newline at end of file diff --git a/web/app/controllers/api_bands_controller.rb b/web/app/controllers/api_bands_controller.rb index fab1e96bc..e3fcfedbc 100644 --- a/web/app/controllers/api_bands_controller.rb +++ b/web/app/controllers/api_bands_controller.rb @@ -14,38 +14,26 @@ class ApiBandsController < ApiController def show @band = Band.find(params[:id]) + respond_with_model(@band) end def create - @band = Band.save(nil, - params[:name], - params[:website], - params[:biography], - params[:city], - params[:state], - params[:country], - params[:genres], - current_user.id, - params[:photo_url], - params[:logo_url]) - - respond_with @band, responder: ApiResponder, :status => 201, :location => api_band_detail_url(@band) + @band = Band.save(current_user, params) + + respond_with_model(@band, new: true, location: lambda { return api_band_detail_url(@band.id) }) end def update - @band = Band.save(params[:id], - params[:name], - params[:website], - params[:biography], - params[:city], - params[:state], - params[:country], - params[:genres], - current_user.id, - params[:photo_url], - params[:logo_url]) + @band = Band.save(current_user, params) - respond_with @band, responder: ApiResponder, :status => :ok + respond_with_model(@band) + end + + def validate + @band = Band.build_band(current_user, params) + @band.valid? + + respond_with_model(@band) end def musician_index diff --git a/web/app/controllers/api_controller.rb b/web/app/controllers/api_controller.rb index 2d1e54c5d..6c7ac8b58 100644 --- a/web/app/controllers/api_controller.rb +++ b/web/app/controllers/api_controller.rb @@ -28,7 +28,26 @@ class ApiController < ApplicationController end end + protected + + def respond_with_model(model, options = {}) + if model.errors.any? + response.status = :unprocessable_entity + respond_with model + else + status = options[:new] && options[:new] == true ? 201 : 200 + redirect_on_success = options[:location] + if redirect_on_success + location = redirect_on_success.call + raise "location must return something" unless location # development time error + respond_with model, responder: ApiResponder, status: status, location: location + else + respond_with model, responder: ApiResponder, status: status + end + end + end + def auth_user unless current_user.id == params[:id] raise PermissionError, ValidationMessages::PERMISSION_VALIDATION_ERROR diff --git a/web/app/views/api_bands/validate.rabl b/web/app/views/api_bands/validate.rabl new file mode 100644 index 000000000..fad461386 --- /dev/null +++ b/web/app/views/api_bands/validate.rabl @@ -0,0 +1,2 @@ +object @band + diff --git a/web/app/views/clients/_band_setup.html.erb b/web/app/views/clients/_band_setup.html.erb index dd82bac8b..e3dd3ae13 100644 --- a/web/app/views/clients/_band_setup.html.erb +++ b/web/app/views/clients/_band_setup.html.erb @@ -1,5 +1,5 @@ -
+
<%= image_tag "content/icon_bands.png", :size => "19x19" %> @@ -13,7 +13,6 @@
-

@@ -21,44 +20,66 @@ - - - - - - - -

Step 1: General Information

+ <%= image_tag "shared/avatar_generic_band.png", {:id => "band-avatar", :align=>"absmiddle", :height => 88, :width => 88 } %>

Upload band photo.

+
Band Name:
+
+
+
+
Web Site:
+
+
+ +
Country:
+
+
+

+ +
State/Region:
-

+
+
+ + +
City:
+
+
+

+ +
Genres:
+
+
+
+
Description / Bio:
+
+
+ +
diff --git a/web/app/views/clients/_band_setup_photo.html.erb b/web/app/views/clients/_band_setup_photo.html.erb index 986821592..4f3cba9ce 100644 --- a/web/app/views/clients/_band_setup_photo.html.erb +++ b/web/app/views/clients/_band_setup_photo.html.erb @@ -1,5 +1,5 @@ -
+
@@ -12,9 +12,12 @@
- -
+
+ +
+ +
diff --git a/web/app/views/clients/_notify.html.erb b/web/app/views/clients/_notify.html.erb index fb400a692..2683524db 100644 --- a/web/app/views/clients/_notify.html.erb +++ b/web/app/views/clients/_notify.html.erb @@ -1,7 +1,7 @@
diff --git a/web/app/views/users/_user_dropdown.html.erb b/web/app/views/users/_user_dropdown.html.erb index de58a65f5..6f2b785f4 100644 --- a/web/app/views/users/_user_dropdown.html.erb +++ b/web/app/views/users/_user_dropdown.html.erb @@ -21,7 +21,7 @@ <% if current_user && current_user.musician? %>
  • <%= link_to "Audio Gear", '/client#/account/audio' %>
  • -
  • <%= link_to "Band Setup", '/client#/band/setup' %>
  • +
  • <%= link_to "Band Setup", '/client#/band/setup/new' %>
  • <% end %>
  • <%= link_to "Invite Friends", '#' %>
      diff --git a/web/config/routes.rb b/web/config/routes.rb index a37c1f78f..dae5bba9f 100644 --- a/web/config/routes.rb +++ b/web/config/routes.rb @@ -225,6 +225,7 @@ SampleApp::Application.routes.draw do # bands match '/bands' => 'api_bands#index', :via => :get + match '/bands/validate' => 'api_bands#validate', :via => :post match '/bands/:id' => 'api_bands#show', :via => :get, :as => 'api_band_detail' match '/bands' => 'api_bands#create', :via => :post match '/bands/:id' => 'api_bands#update', :via => :post diff --git a/web/lib/tasks/import_max_mind.rake b/web/lib/tasks/import_max_mind.rake index 08098a6e7..6d1d9044e 100644 --- a/web/lib/tasks/import_max_mind.rake +++ b/web/lib/tasks/import_max_mind.rake @@ -1,16 +1,16 @@ namespace :db do desc "Import a maxmind database; run like this: rake db:import_maxmind_geo file=" - task :import_maxmind_geo do + task import_maxmind_geo: :environment do MaxMindGeo.import_from_max_mind ENV['file'] end desc "Import a maxmind isp database; run like this: rake db:import_maxmind_isp file=" - task :import_maxmind_isp do + task import_maxmind_isp: :environment do MaxMindIsp.import_from_max_mind ENV['file'] end desc "Create a fake set of maxmind data" - task :phony_maxmind do + task phony_maxmind: :environment do MaxMindManager.active_record_transaction do |manager| manager.create_phony_database() end diff --git a/web/lib/tasks/sample_data.rake b/web/lib/tasks/sample_data.rake index e1c0ec1cd..622173630 100644 --- a/web/lib/tasks/sample_data.rake +++ b/web/lib/tasks/sample_data.rake @@ -31,6 +31,13 @@ namespace :db do DatabaseCleaner.clean end + task populate: :environment do + make_friends + make_bands + make_band_members + make_recording + end + task populate_friends: :environment do make_friends end @@ -48,44 +55,7 @@ namespace :db do end task populate_claimed_recording: :environment do - # need 4 users. - users = User.where(musician: true).limit(4) - raise "need at least 4 musicians in the database to create a recording" if users.length < 4 - - user1 = users[0] - user2 = users[1] - user3 = users[2] - user4 = users[3] - - recording = Recording.new - recording.name = 'sample data' - recording.owner = user1 - - make_recorded_track(recording, user1, 'bass guitar', 'f86949abc213a3ccdc9d266a2ee56453', 2579467, 'https://jamjam:blueberryjam@int.jamkazam.com/stuff/lc_rocks_poison/track-adrian-bass.ogg') - make_recorded_track(recording, user1, 'voice', '264cf4e0bf14d44109322a504d2e6d18', 2373055, 'https://jamjam:blueberryjam@int.jamkazam.com/stuff/lc_rocks_poison/track-adrian-vox.ogg') - make_recorded_track(recording, user2, 'electric guitar', '9f322e1991b8c04b00dc9055d6be933c', 2297867, 'https://jamjam:blueberryjam@int.jamkazam.com/stuff/lc_rocks_poison/track-chris-guitar.ogg') - make_recorded_track(recording, user2, 'voice', '3c7dcb7c4c35c0bb313fc15ee3e6bfd5', 2244968, 'https://jamjam:blueberryjam@int.jamkazam.com/stuff/lc_rocks_poison/track-chris-vox.ogg') - make_recorded_track(recording, user3, 'voice', '10ca4c6ef5b98b3489ae8da1c7fa9cfb', 2254275, 'https://jamjam:blueberryjam@int.jamkazam.com/stuff/lc_rocks_poison/track-matt-lead-vox.ogg') - make_recorded_track(recording, user4, 'drums', 'ea366f482fa969e1fd8530c13cb75716', 2386250, 'https://jamjam:blueberryjam@int.jamkazam.com/stuff/lc_rocks_poison/track-randy-drum1.ogg') - make_recorded_track(recording, user4, 'drums', '4c693c6e99117719c6340eb68b0fe574', 2566463, 'https://jamjam:blueberryjam@int.jamkazam.com/stuff/lc_rocks_poison/track-randy-drum2.ogg') - - make_claimed_recording(recording, user1, 'Poison', 'Classic song that you all know -- user1') - make_claimed_recording(recording, user2, 'Poison', 'Classic song that you all know -- user2') - make_claimed_recording(recording, user3, 'Poison', 'Classic song that you all know -- user3') - make_claimed_recording(recording, user4, 'Poison', 'Classic song that you all know -- user4') - - mix = Mix.new - mix.started_at = Time.now - mix.completed_at = Time.now - mix[:ogg_url] = 'https://jamjam:blueberryjam@int.jamkazam.com/stuff/lc_rocks_poison/master-out.ogg' - mix.ogg_md5 = 'f1fee708264602e1705638e53f0ea667' - mix.ogg_length = 2500633 - mix[:mp3_url] = 'https://jamjam:blueberryjam@int.jamkazam.com/stuff/lc_rocks_poison/master-out.mp3' - mix.mp3_md5 = 'df05abad96e5cb8439f7cd6e31b5c503' - mix.mp3_length = 3666137 - mix.completed = true - recording.mixes << mix - recording.save!(validate:false) + make_recording end desc "Fill database with music session sample data" @@ -257,3 +227,44 @@ def make_claimed_recording(recording, user, name, description) end + +def make_recording + # need 4 users. + users = User.where(musician: true).limit(4) + raise "need at least 4 musicians in the database to create a recording" if users.length < 4 + + user1 = users[0] + user2 = users[1] + user3 = users[2] + user4 = users[3] + + recording = Recording.new + recording.name = 'sample data' + recording.owner = user1 + + make_recorded_track(recording, user1, 'bass guitar', 'f86949abc213a3ccdc9d266a2ee56453', 2579467, 'https://jamjam:blueberryjam@int.jamkazam.com/stuff/lc_rocks_poison/track-adrian-bass.ogg') + make_recorded_track(recording, user1, 'voice', '264cf4e0bf14d44109322a504d2e6d18', 2373055, 'https://jamjam:blueberryjam@int.jamkazam.com/stuff/lc_rocks_poison/track-adrian-vox.ogg') + make_recorded_track(recording, user2, 'electric guitar', '9f322e1991b8c04b00dc9055d6be933c', 2297867, 'https://jamjam:blueberryjam@int.jamkazam.com/stuff/lc_rocks_poison/track-chris-guitar.ogg') + make_recorded_track(recording, user2, 'voice', '3c7dcb7c4c35c0bb313fc15ee3e6bfd5', 2244968, 'https://jamjam:blueberryjam@int.jamkazam.com/stuff/lc_rocks_poison/track-chris-vox.ogg') + make_recorded_track(recording, user3, 'voice', '10ca4c6ef5b98b3489ae8da1c7fa9cfb', 2254275, 'https://jamjam:blueberryjam@int.jamkazam.com/stuff/lc_rocks_poison/track-matt-lead-vox.ogg') + make_recorded_track(recording, user4, 'drums', 'ea366f482fa969e1fd8530c13cb75716', 2386250, 'https://jamjam:blueberryjam@int.jamkazam.com/stuff/lc_rocks_poison/track-randy-drum1.ogg') + make_recorded_track(recording, user4, 'drums', '4c693c6e99117719c6340eb68b0fe574', 2566463, 'https://jamjam:blueberryjam@int.jamkazam.com/stuff/lc_rocks_poison/track-randy-drum2.ogg') + + make_claimed_recording(recording, user1, 'Poison', 'Classic song that you all know -- user1') + make_claimed_recording(recording, user2, 'Poison', 'Classic song that you all know -- user2') + make_claimed_recording(recording, user3, 'Poison', 'Classic song that you all know -- user3') + make_claimed_recording(recording, user4, 'Poison', 'Classic song that you all know -- user4') + + mix = Mix.new + mix.started_at = Time.now + mix.completed_at = Time.now + mix[:ogg_url] = 'https://jamjam:blueberryjam@int.jamkazam.com/stuff/lc_rocks_poison/master-out.ogg' + mix.ogg_md5 = 'f1fee708264602e1705638e53f0ea667' + mix.ogg_length = 2500633 + mix[:mp3_url] = 'https://jamjam:blueberryjam@int.jamkazam.com/stuff/lc_rocks_poison/master-out.mp3' + mix.mp3_md5 = 'df05abad96e5cb8439f7cd6e31b5c503' + mix.mp3_length = 3666137 + mix.completed = true + recording.mixes << mix + recording.save!(validate:false) +end \ No newline at end of file diff --git a/web/spec/factories.rb b/web/spec/factories.rb index 215db3715..71b608c6a 100644 --- a/web/spec/factories.rb +++ b/web/spec/factories.rb @@ -16,6 +16,9 @@ FactoryGirl.define do terms_of_service true subscribe_email true + factory :fan do + musician false + end factory :admin do admin true @@ -33,20 +36,6 @@ FactoryGirl.define do end end - factory :fan, :class => JamRuby::User do - sequence(:first_name) { |n| "Person" } - sequence(:last_name) { |n| "#{n}" } - sequence(:email) { |n| "fan_#{n}@example.com"} - password "foobar" - password_confirmation "foobar" - email_confirmed true - musician false - city "Apex" - state "NC" - country "US" - terms_of_service true - end - factory :invited_user, :class => JamRuby::InvitedUser do sequence(:email) { |n| "user#{n}@someservice.com" } autofriend false @@ -92,6 +81,9 @@ FactoryGirl.define do city "Apex" state "NC" country "US" + before(:create) { |band| + band.genres << Genre.first + } end factory :join_request, :class => JamRuby::JoinRequest do diff --git a/web/spec/features/bands_spec.rb b/web/spec/features/bands_spec.rb index 4343af2ba..f61e65ff1 100644 --- a/web/spec/features/bands_spec.rb +++ b/web/spec/features/bands_spec.rb @@ -4,10 +4,18 @@ describe "Bands", :js => true, :type => :feature, :capybara_feature => true do subject { page } + + before(:all) do Capybara.javascript_driver = :poltergeist Capybara.current_driver = Capybara.javascript_driver Capybara.default_wait_time = 15 + + MaxMindIsp.delete_all # prove that city/state/country will remain nil if no maxmind data + MaxMindGeo.delete_all + MaxMindManager.active_record_transaction do |manager| + manager.create_phony_database() + end end let(:user) { FactoryGirl.create(:user) } @@ -15,9 +23,10 @@ describe "Bands", :js => true, :type => :feature, :capybara_feature => true do before(:each) do UserMailer.deliveries.clear + navigate_band_setup end - it "band setup is accessible through profile page" do + def navigate_band_setup sign_in_poltergeist(user) wait_until_curtain_gone find('div.homecard.profile').trigger(:click) @@ -26,6 +35,25 @@ describe "Bands", :js => true, :type => :feature, :capybara_feature => true do expect(page).to have_selector('#band-setup-title') end + it "have validation errors shown, but then can navigate past and eventually save" do + find('#btn-band-setup-next').trigger(:click) + find('#tdBandName .error-text li', text: "can't be blank") + find('#tdBandBiography .error-text li', text: "can't be blank") + find('#tdBandGenres .error-text li', text: "At least 1 genre is required.") + + within('#band-setup-form') do + fill_in "band-name", with: "The Band" + fill_in "band-biography", with: "Biography" + first('#band-genres input[type="checkbox"]').trigger(:click) + end + + find('#btn-band-setup-next').trigger(:click) + find('h2', text: 'Step 2: Add Band Members') + + find('#btn-band-setup-save').trigger(:click) + find('#band-profile-name', text: "The Band") + find('#band-profile-biography', text: "Biography") + end end diff --git a/web/spec/features/musician_search_spec.rb b/web/spec/features/musician_search_spec.rb index 4fe89d6c9..5f7a42b4c 100644 --- a/web/spec/features/musician_search_spec.rb +++ b/web/spec/features/musician_search_spec.rb @@ -28,6 +28,8 @@ describe "Musician Search", :js => true, :type => :feature, :capybara_feature => end it "shows blank result set" do + pending "fails intermittently on build server" + wait_for_easydropdown('#musician_instrument') # get the 2nd option from the instruments list text = find('#musician_instrument', :visible => false).find(:xpath, 'option[2]', :visible => false).text diff --git a/web/spec/features/whats_next_spec.rb b/web/spec/features/whats_next_spec.rb index 1946e622e..3c31c8848 100644 --- a/web/spec/features/whats_next_spec.rb +++ b/web/spec/features/whats_next_spec.rb @@ -63,6 +63,7 @@ describe "Home Screen", :js => true, :type => :feature, :capybara_feature => tru # needed because we poke the server with an updateUser call, but their is no indication in the UI that it's done wait_for_ajax page.driver.headers = { 'User-Agent' => ' JamKazam ' } + sleep 1 visit "/client" wait_until_curtain_gone should_not have_selector('h1', text: 'what\'s next?') diff --git a/web/spec/requests/bands_api_spec.rb b/web/spec/requests/bands_api_spec.rb index 528ef656a..fcfe4cded 100644 --- a/web/spec/requests/bands_api_spec.rb +++ b/web/spec/requests/bands_api_spec.rb @@ -10,6 +10,16 @@ describe "Band API", :type => :api do let(:band) { FactoryGirl.create(:band) } let(:user) { FactoryGirl.create(:user) } let(:fan) { FactoryGirl.create(:fan) } + let(:band_params) { + { + name: "My Band", + biography: "Biography", + city: 'Austin', + state: 'TX', + country: 'US', + genres: ['rock'] + } + } def login(email, password, http_code, success) # login as fan @@ -19,45 +29,33 @@ describe "Band API", :type => :api do end ################################## BANDS ################################## - def create_band(authenticated_user, name, website, biography, city, state, country, genres, photo_url, logo_url) - post "/api/bands.json", { :name => name, - :website => website, - :biography => biography, - :city => city, - :state => state, - :country => country, - :genres => genres, - :photo_url => photo_url, - :logo_url => logo_url - }.to_json, - "CONTENT_TYPE" => 'application/json' - return last_response + def create_band(authenticated_user, options={}) + options = band_params.merge(options) + post "/api/bands.json", options.to_json, "CONTENT_TYPE" => 'application/json' + last_response end - def update_band(authenticated_user, band_id, name, website, biography, city, state, country, genres, photo_url, logo_url) - post "/api/bands/#{band_id}.json", { :name => name, - :website => website, - :biography => biography, - :city => city, - :state => state, - :country => country, - :genres => genres, - :photo_url => photo_url, - :logo_url => logo_url - }.to_json, - "CONTENT_TYPE" => 'application/json' - return last_response + def validate_band(authenticated_user, options={}) + options = band_params.merge(options) + post "/api/bands/validate.json", options.to_json, "CONTENT_TYPE" => 'application/json' + last_response + end + + def update_band(authenticated_user, band_id, options={}) + options = band_params.merge(options) + post "/api/bands/#{band_id}.json", options.to_json, "CONTENT_TYPE" => 'application/json' + last_response end def get_band(authenticated_user, band_id) get "/api/bands/#{band_id}.json", "CONTENT_TYPE" => 'application/json' - return last_response + last_response end ########################## RECORDINGS ######################### def create_band_recording(authenticated_user, band_id, description, public, genres) post "/api/bands/#{band_id}/recordings.json", { :description => description, :public => public, :genres => genres }.to_json, "CONTENT_TYPE" => 'application/json' - return last_response + last_response end def update_band_recording() @@ -98,9 +96,19 @@ describe "Band API", :type => :api do login(user.email, user.password, 200, true) end + it "should pass validation" do + last_response = validate_band(user) + last_response.status.should == 200 + end + + it "should fail validation" do + last_response = validate_band(user, name: nil) + last_response.status.should == 422 + end + it "should allow band creation" do - last_response = create_band(user, "My Band", "http://www.myband.com", "Bio", "Apex", "NC", "US", ["country"], "www.photos.com", "www.logos.com") + last_response = create_band(user) last_response.status.should == 201 new_band = JSON.parse(last_response.body) @@ -117,17 +125,17 @@ describe "Band API", :type => :api do end it "should prevent bands with less than 1 genre" do - last_response = create_band(user, "My Band", "http://www.myband.com", "Bio", "Apex", "NC", "US", nil, "www.photos.com", "www.logos.com") - last_response.status.should == 400 + last_response = create_band(user, genres: []) + last_response.status.should == 422 error_msg = JSON.parse(last_response.body) - error_msg["message"].should == ValidationMessages::GENRE_MINIMUM_NOT_MET + error_msg["errors"]["genres"].should == [ValidationMessages::BAND_GENRE_MINIMUM_NOT_MET] end it "should prevent bands with more than 3 genres" do - last_response = create_band(user, "My Band", "http://www.myband.com", "Bio", "Apex", "NC", "US", ["african", "country", "ambient", "asian"], "www.photos.com", "www.logos.com") - last_response.status.should == 400 + last_response = create_band(user, genres: ["african", "country", "ambient", "asian"]) + last_response.status.should == 422 error_msg = JSON.parse(last_response.body) - error_msg["message"].should == ValidationMessages::GENRE_LIMIT_EXCEEDED + error_msg["errors"]["genres"].should == [ValidationMessages::BAND_GENRE_LIMIT_EXCEEDED] end end @@ -135,9 +143,6 @@ describe "Band API", :type => :api do before(:each) do login(user.email, user.password, 200, true) - band.genres << Genre.find("hip hop") - #band.genres << Genre.find("african") - #band.genres << Genre.find("country") user.bands << band end @@ -145,7 +150,7 @@ describe "Band API", :type => :api do band.genres.size.should == 1 - last_response = update_band(user, band.id, "Brian's Band", "http://www.briansband.com", "Bio", "Apex", "NC", "US", ["african"], "www.photos.com", "www.logos.com") + last_response = update_band(user, band.id, name: "Brian's Band", website: "http://www.briansband.com", genres: ["african"]) last_response.status.should == 200 updated_band = JSON.parse(last_response.body) @@ -161,8 +166,8 @@ describe "Band API", :type => :api do band_details = JSON.parse(last_response.body) band_details["name"].should == "Brian's Band" band_details["website"].should == "http://www.briansband.com" - band_details["biography"].should == "Bio" - band_details["genres"].size.should == 1 + band_details["biography"].should == "Biography" + band_details["genres"].should == [{"id"=>"african", "description"=>"African"}] end it "should allow user to create recording for band A" do diff --git a/web/spec/requests/search_api_spec.rb b/web/spec/requests/search_api_spec.rb index a3475939f..29751d904 100644 --- a/web/spec/requests/search_api_spec.rb +++ b/web/spec/requests/search_api_spec.rb @@ -7,6 +7,16 @@ describe "Search API", :type => :api do describe "profile page" do let(:user) { FactoryGirl.create(:user) } + let(:band_params) { + { + name: "The Band", + biography: "Biography", + city: 'Austin', + state: 'TX', + country: 'US', + genres: ['country'] + } + } before(:each) do post '/sessions', "session[email]" => user.email, "session[password]" => user.password @@ -22,8 +32,10 @@ describe "Search API", :type => :api do it "simple search" do @musician = FactoryGirl.create(:user, first_name: "Peach", last_name: "Nothing", email: "user@example.com", musician: true) @fan = FactoryGirl.create(:user, first_name: "Peach Peach", last_name: "Grovery", email: "fan@example.com", musician: false) - @band = Band.save(nil, "Peach pit", "www.bands.com", "zomg we rock", "Apex", "NC", "US", ["hip hop"], user.id, nil, nil) - @band2 = Band.save(nil, "Peach", "www.bands2.com", "zomg we rock", "Apex", "NC", "US", ["hip hop"], user.id, nil, nil) + band_params[:name] = "Peach pit" + @band = Band.save(user, band_params) + band_params[:name] = "Peach" + @band2 = Band.save(user, band_params) get '/api/search.json?query=peach&search_text_type=bands' last_response.status.should == 200 diff --git a/web/spec/spec_helper.rb b/web/spec/spec_helper.rb index 8ac3f7d4d..69ef24cb3 100644 --- a/web/spec/spec_helper.rb +++ b/web/spec/spec_helper.rb @@ -19,7 +19,6 @@ db_config = YAML::load(File.open('config/database.yml'))["test"] # initialize ActiveRecord's db connection\ SpecDb::recreate_database(db_config) ActiveRecord::Base.establish_connection(YAML::load(File.open('config/database.yml'))["test"]) - require 'jam_ruby' include JamRuby @@ -51,7 +50,6 @@ Spork.prefork do Capybara.default_wait_time = 10 if defined?(TEST_CONNECT_STATES) && TEST_CONNECT_STATES - require 'capybara/poltergeist' TEST_CONNECT_STATE_JS_CONSOLE_IO = File.open(TEST_CONNECT_STATE_JS_CONSOLE, 'w') Capybara.register_driver :poltergeist do |app| Capybara::Poltergeist::Driver.new(app, { phantomjs_logger: TEST_CONNECT_STATE_JS_CONSOLE_IO }) From 238fba1fc3958b0cd7cd7c3d33e135de974353ab Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Wed, 19 Feb 2014 20:04:06 -0500 Subject: [PATCH 05/21] fix bug with recording link/hover bubble in landing page sidebar --- web/app/views/shared/_landing_sidebar.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/app/views/shared/_landing_sidebar.html.erb b/web/app/views/shared/_landing_sidebar.html.erb index c7ddd78d4..ab14d09fc 100644 --- a/web/app/views/shared/_landing_sidebar.html.erb +++ b/web/app/views/shared/_landing_sidebar.html.erb @@ -10,7 +10,7 @@
  • RECORDING: - Test + Test
    <% elsif history_record.instance_of? MusicSessionHistory %> From f388f1c7e261902bb62192e96e33d942bbfcc4dc Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Wed, 19 Feb 2014 20:06:40 -0500 Subject: [PATCH 06/21] add likes and play count back to model...not sure how it got deleted --- ruby/lib/jam_ruby/models/recording.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ruby/lib/jam_ruby/models/recording.rb b/ruby/lib/jam_ruby/models/recording.rb index 8fc666e0f..7e8ea3ac9 100644 --- a/ruby/lib/jam_ruby/models/recording.rb +++ b/ruby/lib/jam_ruby/models/recording.rb @@ -40,6 +40,14 @@ module JamRuby self.band_id = nil if self.band_id == '' end + def like_count + self.likes.size + end + + def play_count + self.plays.size + end + def comment_count self.comments.size end From c95a650cdcb9f6fbc99b03f2c907a4e8d5206938 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Wed, 19 Feb 2014 20:31:44 -0500 Subject: [PATCH 07/21] add POST button to landing pages for comments, add like_count back to session model --- .../jam_ruby/models/music_session_history.rb | 5 ++++- web/app/assets/javascripts/web/recordings.js | 12 +++++----- web/app/assets/javascripts/web/sessions.js | 22 +++++++++---------- web/app/assets/stylesheets/web/main.css.scss | 13 +++++++++++ web/app/views/shared/_comments.html.erb | 3 +++ 5 files changed, 36 insertions(+), 19 deletions(-) diff --git a/ruby/lib/jam_ruby/models/music_session_history.rb b/ruby/lib/jam_ruby/models/music_session_history.rb index 29be1f4e1..b98d1d106 100644 --- a/ruby/lib/jam_ruby/models/music_session_history.rb +++ b/ruby/lib/jam_ruby/models/music_session_history.rb @@ -39,11 +39,14 @@ module JamRuby feed.music_session_history = self end + def like_count + self.likes.size + end + def comment_count self.comments.size end - def tracks tracks = [] self.music_session_user_histories.each do |msuh| diff --git a/web/app/assets/javascripts/web/recordings.js b/web/app/assets/javascripts/web/recordings.js index e942bcd5f..4644d5beb 100644 --- a/web/app/assets/javascripts/web/recordings.js +++ b/web/app/assets/javascripts/web/recordings.js @@ -53,12 +53,12 @@ shareDialog.showDialog(); }); - $("#txtRecordingComment", $scope).keypress(function(e) { - if (e.which === 13) { - addComment(); - $(this).val(''); - $(this).blur(); - } + $("#btnPostComment").click(function(e) { + if ($.trim($("#txtRecordingComment").val()).length > 0) { + addComment(); + $("#txtRecordingComment").val(''); + $("#txtRecordingComment").blur(); + } }); } else { diff --git a/web/app/assets/javascripts/web/sessions.js b/web/app/assets/javascripts/web/sessions.js index 05e79e7db..19f01c8d7 100644 --- a/web/app/assets/javascripts/web/sessions.js +++ b/web/app/assets/javascripts/web/sessions.js @@ -36,20 +36,18 @@ sessionId = musicSessionId; if (JK.currentUserId) { - var shareDialog = new JK.ShareDialog(context.JK.app, sessionId, "session"); - shareDialog.initialize(JK.FacebookHelperInstance); + var shareDialog = new JK.ShareDialog(context.JK.app, sessionId, "session"); + shareDialog.initialize(JK.FacebookHelperInstance); - $("#btnShare").click(function(e) { - shareDialog.showDialog(); - }); + $("#btnShare").click(function(e) { + shareDialog.showDialog(); + }); - $("#txtSessionComment").keypress(function(e) { - if (e.which === 13) { - addComment(); - $(this).val(''); - $(this).blur(); - } - }); + $("#btnPostComment").click(function(e) { + addComment(); + $(this).val(''); + $(this).blur(); + }); } else { $("#txtSessionComment").attr("disabled", "disabled"); diff --git a/web/app/assets/stylesheets/web/main.css.scss b/web/app/assets/stylesheets/web/main.css.scss index 7942f386d..c2ba9e1be 100644 --- a/web/app/assets/stylesheets/web/main.css.scss +++ b/web/app/assets/stylesheets/web/main.css.scss @@ -159,6 +159,19 @@ body.web { font-weight:bold; color:#ED3618; } + + .button-orange { + margin: 0px 8px 0px 8px; + background-color: #ED3618; + border: solid 1px #F27861; + outline: solid 2px #ED3618; + padding: 3px 10px; + font-size: 12px; + font-weight: 300; + cursor: pointer; + color: #FC9; + text-decoration: none; + } } .landing-comment-scroller { diff --git a/web/app/views/shared/_comments.html.erb b/web/app/views/shared/_comments.html.erb index 116c6bf51..c13f3559a 100644 --- a/web/app/views/shared/_comments.html.erb +++ b/web/app/views/shared/_comments.html.erb @@ -6,6 +6,9 @@
    +
    + POST +

    From 76d8e84a620408087e9983e0b18e5a0dc1745b9a Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Wed, 19 Feb 2014 20:35:34 -0500 Subject: [PATCH 08/21] check for empty comment before posting --- web/app/assets/javascripts/web/sessions.js | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/web/app/assets/javascripts/web/sessions.js b/web/app/assets/javascripts/web/sessions.js index 19f01c8d7..7a95a05e8 100644 --- a/web/app/assets/javascripts/web/sessions.js +++ b/web/app/assets/javascripts/web/sessions.js @@ -36,18 +36,20 @@ sessionId = musicSessionId; if (JK.currentUserId) { - var shareDialog = new JK.ShareDialog(context.JK.app, sessionId, "session"); - shareDialog.initialize(JK.FacebookHelperInstance); + var shareDialog = new JK.ShareDialog(context.JK.app, sessionId, "session"); + shareDialog.initialize(JK.FacebookHelperInstance); - $("#btnShare").click(function(e) { - shareDialog.showDialog(); - }); + $("#btnShare").click(function(e) { + shareDialog.showDialog(); + }); - $("#btnPostComment").click(function(e) { - addComment(); - $(this).val(''); - $(this).blur(); - }); + $("#btnPostComment").click(function(e) { + if ($.trim($("#txtSessionComment").val()).length > 0) { + addComment(); + $("#txtSessionComment").val(''); + $("#txtSessionComment").blur(); + } + }); } else { $("#txtSessionComment").attr("disabled", "disabled"); From 9bd673e67d85e10cbfffc791816bbafb8812f958 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Wed, 19 Feb 2014 21:07:03 -0500 Subject: [PATCH 09/21] VRFS-1185 fix piano and upright bass icons --- .../images/content/icon_instrument_piano24.png | Bin 0 -> 758 bytes .../images/content/icon_instrument_piano45.png | Bin 0 -> 1335 bytes .../content/icon_instrument_upright_bass24.png | Bin 0 -> 961 bytes .../content/icon_instrument_upright_bass45.png | Bin 0 -> 1990 bytes web/app/assets/images/shared/icon_piano_256.png | Bin 0 -> 3884 bytes .../images/shared/icon_upright_bass_256.png | Bin 0 -> 14033 bytes web/app/assets/javascripts/utils.js | 4 ++-- 7 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 web/app/assets/images/content/icon_instrument_piano24.png create mode 100644 web/app/assets/images/content/icon_instrument_piano45.png create mode 100644 web/app/assets/images/content/icon_instrument_upright_bass24.png create mode 100644 web/app/assets/images/content/icon_instrument_upright_bass45.png create mode 100644 web/app/assets/images/shared/icon_piano_256.png create mode 100644 web/app/assets/images/shared/icon_upright_bass_256.png diff --git a/web/app/assets/images/content/icon_instrument_piano24.png b/web/app/assets/images/content/icon_instrument_piano24.png new file mode 100644 index 0000000000000000000000000000000000000000..6f5761df97ebde69203dd88e4fb83609c329fff5 GIT binary patch literal 758 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM0wlfaz7_*1rX+877l!}s{b%+Ad7K3vkswhI zFm^kcZ3k2#?CIhdVsU!vWPgv#i89CR_m}S%Q#tGFY9yPy)pLu|2Y$EKjMP9uxkb%) z*Ek0p*Nb&~-dC#8v!c8G1V`5fo?vIsiCt`|x6Zf;J9SL_!XBCJ-yR?RPkY<*51;<- zd;i?BX8!kD-A_UhES4U|jMo^sRU{t@7H$fgx9O9?m4YrYJHultnl1fNvp5V+nc6)G zWS_p&?CWapuTjd!-@VzrC3d>Wk}^r%Ll+BsU#~fpa(Z#l&ZiT@^VUue&#Sz$Qrzlg zgbw4I<=vWE9>xyRZFW%_3;W%)Bo<7|stY(-`L2mEtK-sH-lipz$C8xKcLuvN``+N0 zl*Vow^Gqi3_yy0F+dQ9pGk^U&Vbk2hoG|05VxiHVCDQuo25kzm64Opc1#%bmbR^0r zZW39fQ^zMAwUPUf>{h95VvUEzoGSy_=0_UEF&ZuDdi0!k@@lyk@?WhF3rJoo>ad-% zVdmZ+noDQ4**k_6Hwc)0l{v_6N>sVv96UfPS}YNvmRh*#;&@Vm4kO+_^{ zz2ma;JG0`|Ta~z$?{`1LaJ_TwCyQz4Q!37j)=4TjPC5VMU-k->!>notwh7oQ=;`i= zO!Ii8$ddV`s!7_CD`%j(~0qnl_5)gvo84Vy~uep%A`lQ zwBxeLr)lX&JQeq}+&F3$S3I?I>CPuUdP*{>=AkRdP`(kYX@0Ff`URFw-?O3o)>?GBUCDSr1<%~X^wgl##FWay Slc_d9MGT&j#v6BB`XM zP$9Hd0&OjA13`Z~Fc&W++pd8AqMDtCxHK=XcLL_uP+T5v2(X3n*bF#T1ZD3J$|u z<0740V1OwrinSFH3Yk<>$2yiW50^XsTaE(U`^}N24SFYOh zl2Q}{I9u;)NE7-_&nWd_=nN%F(rWFOPovtayvW+1c!`=crHiW9+$hp;n`G6gO|DLA zRRjwmv|O!nbwow6V8iV4v`>C@YE8JE6}EJR?B^-Ir->mvxSR({F@T?6c!VOr!pB*b zlaH_%j)Pcn^97*8O6rNVIJZR9&FOdV?G9( zF~*or01q~XG1|GwTHa;O%xG=Ukgn)yEmV#^mn1c6zH;=ABj zDqk7eF2AIHRVq`ZI;}}<3;JycZjiUg<_c{LFvJJ|UT!ml7l2{zaD%~z)^>!e__&FH z0E3Kho@SES#bN*>fX&oIO#>_DQ3qH_JqQD2vX$f*b1k;Eg@Rx}s;P`C7>cM3qBoHi zXSAV?R6{On;|vBUXPKdx!UUjmsW7Y}BLV1S$_>SM5`czc!=i+MA4GEZPR1NxMzS!B=nozf&-LwKWN&gX?OaahEBg4TC z8^A(vja__6F`54g_~3a{z#{tC#fAHwCQQ-6>vWSp{d2ys`1=@}j^!{Dq3=m<;SZ^k z7~~C(KbS4-Q6+#fK4u*Oj&XwHzWclrP&%ZPgUBjOOh^Y zr?O^)Bh`%s)KkxL#_8e=?eyYhlmH&)l0zY7tf7<)`uK(~=ti?ak*i&cd}^tql3Wa9 z+?__JlLp-8Jk1=V7dPf=cZT<=EagcmC?=mQY^LyWogUgbOAq&=0saOQ9~eM0DPbr8 z001R)MObuXVRU6WV{&C-bY%cCFflVNFg7hQHdHV%i@wvUXW)L^=g_0(akRf|(XwO@7bU+QLlL2=%jAu7cNV{| zJ^#4L$XImV+1(ZO&*!K=pYy$R%hbfcEpr98ck{Y57#^N-ZpE`G$Js^Z{nB3+os}tR z-t|N+wET02P->gx6aTpvSVhW;j_a^|Tc!492k(-`_p2nW@2*c?b*s^2e(8tT=k_Ot zR$Fo!|6AXZnX`QE=H$$C%qD_i8&{QeUd#Pqe#GQ+_xB&$-)@o>(Ead({cZGZ<)pmp z;wd`zvt+-||Iy|9OTAa&z}x3ZMOKTy^D*iR?3$OGH#au!*QE1zqwV^0w|)M{d48_R z@p#9ukM5WLEqQSA-R8n-v!dOly1OMx5;cngZ`v-(UNt=~G27}{T6M?EovY^G<@j}m zul)CX#~*=z7rKO(UOINkSCX57L;B+P6sOe$(xbN5?JV+7 z*euUXF#eS(6udWMJ6{WVhf2K!vlvcs%4bF$JY+$s0Uv6S{=)fdJN0Z759~cw@{PGo|91JlNp@-J zf0xhNY1#Ot^y1154Ij=&Ty5it;*j2Y^3BmDNzYY0KP`XO-5a`jJEQb=XKm?=izHw8 z?tb9@=kPCwk@T z><9aYdb27{@9~}oO!BHFt`Q|Ei6yC4$wjF^iowXh&{)^NOxMsX#K6+Z$jHjT2*|ZE zFxcun^*D-#-29Zxv`VN3Bd7*LGbgTe~DWM4f`-Zc! literal 0 HcmV?d00001 diff --git a/web/app/assets/images/content/icon_instrument_upright_bass45.png b/web/app/assets/images/content/icon_instrument_upright_bass45.png new file mode 100644 index 0000000000000000000000000000000000000000..7c2b8e1d13194b2c8a4495620a6db2fbfecfee5a GIT binary patch literal 1990 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 literal 0 HcmV?d00001 diff --git a/web/app/assets/images/shared/icon_upright_bass_256.png b/web/app/assets/images/shared/icon_upright_bass_256.png new file mode 100644 index 0000000000000000000000000000000000000000..c5373a2156c71f73d2e0495347015f70117908af GIT binary patch literal 14033 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 Date: Thu, 20 Feb 2014 02:22:41 +0000 Subject: [PATCH 10/21] * VRFS-1135 add time filters and type filter --- ruby/lib/jam_ruby/models/feed.rb | 12 ++-- ruby/spec/jam_ruby/models/feed_spec.rb | 55 ++++++++++++++++++- web/app/controllers/api_feeds_controller.rb | 3 +- .../controllers/api_feeds_controller_spec.rb | 38 +++++++++++++ web/spec/spec_helper.rb | 4 ++ 5 files changed, 105 insertions(+), 7 deletions(-) diff --git a/ruby/lib/jam_ruby/models/feed.rb b/ruby/lib/jam_ruby/models/feed.rb index fd930102f..923239b7a 100644 --- a/ruby/lib/jam_ruby/models/feed.rb +++ b/ruby/lib/jam_ruby/models/feed.rb @@ -7,8 +7,8 @@ module JamRuby FIXNUM_MAX = (2**(0.size * 8 -2) -1) SORT_TYPES = ['date', 'plays', 'likes'] - TIME_RANGES = ['today', 'week', 'month', 'all'] - TYPE_FILTERS = ['session', 'recording', 'all'] + TIME_RANGES = { "today" => 1 , "week" => 7, "month" => 30, "all" => 0} + TYPE_FILTERS = ['music_session_history', 'recording', 'all'] def self.index(params = {}) limit = params[:limit] @@ -30,7 +30,7 @@ module JamRuby time_range = params[:time_range] time_range ||= 'month' - raise "not valid time_range #{time_range}" unless TIME_RANGES.include?(time_range) + raise "not valid time_range #{time_range}" unless TIME_RANGES.has_key?(time_range) type_filter = params[:type] type_filter ||= 'all' @@ -53,9 +53,13 @@ module JamRuby end # handle time range + days = TIME_RANGES[time_range] + if days > 0 + query = query.where("feeds.created_at > NOW() - '#{days} day'::INTERVAL") + end # handle type filters - if type_filter == 'session' + if type_filter == 'music_session_history' query = query.where('music_session_id is not NULL') elsif type_filter == 'recording' query = query.where('recording_id is not NULL') diff --git a/ruby/spec/jam_ruby/models/feed_spec.rb b/ruby/spec/jam_ruby/models/feed_spec.rb index 13cb877b6..ff97e9f5d 100644 --- a/ruby/spec/jam_ruby/models/feed_spec.rb +++ b/ruby/spec/jam_ruby/models/feed_spec.rb @@ -114,7 +114,7 @@ describe Feed do # creates both recording and history record in feed claimed_recording1 = FactoryGirl.create(:claimed_recording) - feeds, start = Feed.index(:type => 'session') + feeds, start = Feed.index(:type => 'music_session_history') feeds.length.should == 1 feeds[0].music_session_history == claimed_recording1.recording.music_session.music_session_history end @@ -123,11 +123,62 @@ describe Feed do # creates both recording and history record in feed claimed_recording1 = FactoryGirl.create(:claimed_recording) - feeds, start = Feed.index(:type => 'session') + feeds, start = Feed.index(:type => 'music_session_history') feeds.length.should == 1 feeds[0].music_session_history == claimed_recording1.recording.music_session.music_session_history end end + + describe "time ranges" do + it "month" do + # creates both recording and history record in feed + claimed_recording1 = FactoryGirl.create(:claimed_recording) + + # move the feed entry created for the recording back more than a months ago + claimed_recording1.recording.feed.created_at = 32.days.ago + claimed_recording1.recording.feed.save! + + feeds, start = Feed.index(:type => 'recording') + feeds.length.should == 0 + end + + it "day" do + # creates both recording and history record in feed + claimed_recording1 = FactoryGirl.create(:claimed_recording) + + # move the feed entry created for the recording back more than a months ago + claimed_recording1.recording.feed.created_at = 25.hours.ago + claimed_recording1.recording.feed.save! + + feeds, start = Feed.index(:type => 'recording', time_range: 'today') + feeds.length.should == 0 + end + + it "week" do + # creates both recording and history record in feed + claimed_recording1 = FactoryGirl.create(:claimed_recording) + + # move the feed entry created for the recording back more than a months ago + claimed_recording1.recording.feed.created_at = 8.days.ago + claimed_recording1.recording.feed.save! + + feeds, start = Feed.index(:type => 'recording', time_range: 'week') + feeds.length.should == 0 + end + + it "all" do + # creates both recording and history record in feed + claimed_recording1 = FactoryGirl.create(:claimed_recording) + + # move the feed entry created for the recording back more than a months ago + claimed_recording1.recording.feed.created_at = 700.days.ago + claimed_recording1.recording.feed.save! + + feeds, start = Feed.index(:type => 'recording', time_range: 'all') + feeds.length.should == 1 + end + end + describe "pagination" do it "supports date pagination" do claimed_recording = FactoryGirl.create(:claimed_recording) diff --git a/web/app/controllers/api_feeds_controller.rb b/web/app/controllers/api_feeds_controller.rb index a875395b3..41493fbba 100644 --- a/web/app/controllers/api_feeds_controller.rb +++ b/web/app/controllers/api_feeds_controller.rb @@ -7,7 +7,8 @@ class ApiFeedsController < ApiController start: params[:since], limit: params[:limit], sort: params[:sort], - time_range: params[:time_range]) + time_range: params[:time_range], + type: params[:type]) render "api_feeds/index", :layout => nil end diff --git a/web/spec/controllers/api_feeds_controller_spec.rb b/web/spec/controllers/api_feeds_controller_spec.rb index 0044beec6..61cd5c828 100644 --- a/web/spec/controllers/api_feeds_controller_spec.rb +++ b/web/spec/controllers/api_feeds_controller_spec.rb @@ -46,6 +46,44 @@ describe ApiFeedsController do music_session[:type].should == 'music_session_history' end + describe "time range" do + + it "today and month find different results" do + music_session.music_session_history.touch + music_session.music_session_history.feed.created_at = 3.days.ago + music_session.music_session_history.feed.save! + + get :index + json = JSON.parse(response.body, :symbolize_names => true) + json[:entries].length.should == 1 + + get :index, { time_range:'today' } + json = JSON.parse(response.body, :symbolize_names => true) + json[:entries].length.should == 0 + end + end + + describe "type filter" do + + it "can filter by type" do + claimed_recording.touch + + get :index + json = JSON.parse(response.body, :symbolize_names => true) + json[:entries].length.should == 2 + + get :index, { type: 'recording'} + json = JSON.parse(response.body, :symbolize_names => true) + json[:entries].length.should == 1 + json[:entries][0][:type].should == 'recording' + + get :index, { type: 'music_session_history'} + json = JSON.parse(response.body, :symbolize_names => true) + json[:entries].length.should == 1 + json[:entries][0][:type].should == 'music_session_history' + end + end + describe "pagination" do it "since parameter" do diff --git a/web/spec/spec_helper.rb b/web/spec/spec_helper.rb index 69ef24cb3..fcc8f0c3f 100644 --- a/web/spec/spec_helper.rb +++ b/web/spec/spec_helper.rb @@ -21,6 +21,10 @@ SpecDb::recreate_database(db_config) ActiveRecord::Base.establish_connection(YAML::load(File.open('config/database.yml'))["test"]) require 'jam_ruby' +# uncomment this to see active record logs +# ActiveRecord::Base.logger = Logger.new(STDOUT) if defined?(ActiveRecord::Base) + + include JamRuby # put ActionMailer into test mode From 3efb3fc5ab2324c86625d08d14fb69feec0112d9 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Thu, 20 Feb 2014 03:23:38 +0000 Subject: [PATCH 11/21] * feed test fixes --- ruby/lib/jam_ruby/models/feed.rb | 14 ++++- ruby/spec/jam_ruby/models/feed_spec.rb | 55 ++++++++++--------- web/Gemfile | 2 +- web/app/controllers/api_feeds_controller.rb | 2 +- .../controllers/api_feeds_controller_spec.rb | 1 - web/spec/spec_helper.rb | 5 ++ 6 files changed, 48 insertions(+), 31 deletions(-) diff --git a/ruby/lib/jam_ruby/models/feed.rb b/ruby/lib/jam_ruby/models/feed.rb index 923239b7a..27ba46c68 100644 --- a/ruby/lib/jam_ruby/models/feed.rb +++ b/ruby/lib/jam_ruby/models/feed.rb @@ -10,7 +10,7 @@ module JamRuby TIME_RANGES = { "today" => 1 , "week" => 7, "month" => 30, "all" => 0} TYPE_FILTERS = ['music_session_history', 'recording', 'all'] - def self.index(params = {}) + def self.index(user, params = {}) limit = params[:limit] limit ||= 20 limit = limit.to_i @@ -36,6 +36,11 @@ module JamRuby type_filter ||= 'all' raise "not valid type #{type_filter}" unless TYPE_FILTERS.include?(type_filter) + include_private = params[:include_private] + include_private ||= false # default to false + include_private = false if user.nil? # and force to false if the current user is nil + + query = Feed.includes([:recording]).includes([:music_session_history]).limit(limit) # handle sort @@ -65,6 +70,13 @@ module JamRuby query = query.where('recording_id is not NULL') end + # handle private times + if include_private + + else + + end + if query.length == 0 [query, nil] elsif query.length < limit diff --git a/ruby/spec/jam_ruby/models/feed_spec.rb b/ruby/spec/jam_ruby/models/feed_spec.rb index ff97e9f5d..9c86b974c 100644 --- a/ruby/spec/jam_ruby/models/feed_spec.rb +++ b/ruby/spec/jam_ruby/models/feed_spec.rb @@ -8,14 +8,14 @@ describe Feed do let (:user4) { FactoryGirl.create(:user) } it "no result" do - feeds, start = Feed.index() + feeds, start = Feed.index(user1) feeds.length.should == 0 end it "one claimed recording" do claimed_recording = FactoryGirl.create(:claimed_recording) MusicSessionHistory.delete_all # the factory makes a music_session while making the recording/claimed_recording - feeds, start = Feed.index() + feeds, start = Feed.index(user1) feeds.length.should == 1 feeds[0].recording == claimed_recording.recording end @@ -30,13 +30,13 @@ describe Feed do # verify the mess above only made one recording Recording.count.should == 1 - feeds, start = Feed.index + feeds, start = Feed.index(user1) feeds.length.should == 1 end it "one music session" do music_session = FactoryGirl.create(:music_session) - feeds, start = Feed.index + feeds, start = Feed.index(user1) feeds.length.should == 1 feeds[0].music_session_history == music_session.music_session_history end @@ -45,7 +45,7 @@ describe Feed do it "sorts by index (date) DESC" do claimed_recording = FactoryGirl.create(:claimed_recording) - feeds, start = Feed.index + feeds, start = Feed.index(user1) feeds.length.should == 2 feeds[0].recording.should == claimed_recording.recording feeds[1].music_session_history.should == claimed_recording.recording.music_session.music_session_history @@ -57,13 +57,13 @@ describe Feed do FactoryGirl.create(:recording_play, recording: claimed_recording1.recording, user:claimed_recording1.user) - feeds, start = Feed.index(:sort => 'plays') + feeds, start = Feed.index(user1, :sort => 'plays') feeds.length.should == 4 FactoryGirl.create(:recording_play, recording: claimed_recording2.recording, user:claimed_recording1.user) FactoryGirl.create(:recording_play, recording: claimed_recording2.recording, user:claimed_recording2.user) - feeds, start = Feed.index(:sort => 'plays') + feeds, start = Feed.index(user1, :sort => 'plays') feeds.length.should == 4 feeds[0].recording.should == claimed_recording2.recording feeds[1].recording.should == claimed_recording1.recording @@ -73,7 +73,7 @@ describe Feed do FactoryGirl.create(:music_session_play, music_session: claimed_recording1.recording.music_session.music_session_history, user: user3) - feeds, start = Feed.index(:sort => 'plays') + feeds, start = Feed.index(user1, :sort => 'plays') feeds.length.should == 4 feeds[0].music_session_history.should == claimed_recording1.recording.music_session.music_session_history feeds[1].recording.should == claimed_recording2.recording @@ -86,13 +86,13 @@ describe Feed do FactoryGirl.create(:recording_like, recording: claimed_recording1.recording, user:claimed_recording1.user) - feeds, start = Feed.index(:sort => 'likes') + feeds, start = Feed.index(user1, :sort => 'likes') feeds.length.should == 4 FactoryGirl.create(:recording_like, recording: claimed_recording2.recording, user:claimed_recording1.user) FactoryGirl.create(:recording_like, recording: claimed_recording2.recording, user:claimed_recording2.user) - feeds, start = Feed.index(:sort => 'likes') + feeds, start = Feed.index(user1, :sort => 'likes') feeds.length.should == 4 feeds[0].recording.should == claimed_recording2.recording feeds[1].recording.should == claimed_recording1.recording @@ -101,7 +101,7 @@ describe Feed do FactoryGirl.create(:music_session_like, music_session_history: claimed_recording1.recording.music_session.music_session_history, user: user2) FactoryGirl.create(:music_session_like, music_session_history: claimed_recording1.recording.music_session.music_session_history, user: user3) - feeds, start = Feed.index(:sort => 'likes') + feeds, start = Feed.index(user1, :sort => 'likes') feeds.length.should == 4 feeds[0].music_session_history.should == claimed_recording1.recording.music_session.music_session_history feeds[1].recording.should == claimed_recording2.recording @@ -114,7 +114,7 @@ describe Feed do # creates both recording and history record in feed claimed_recording1 = FactoryGirl.create(:claimed_recording) - feeds, start = Feed.index(:type => 'music_session_history') + feeds, start = Feed.index(user1, :type => 'music_session_history') feeds.length.should == 1 feeds[0].music_session_history == claimed_recording1.recording.music_session.music_session_history end @@ -123,7 +123,7 @@ describe Feed do # creates both recording and history record in feed claimed_recording1 = FactoryGirl.create(:claimed_recording) - feeds, start = Feed.index(:type => 'music_session_history') + feeds, start = Feed.index(user1, :type => 'music_session_history') feeds.length.should == 1 feeds[0].music_session_history == claimed_recording1.recording.music_session.music_session_history end @@ -138,7 +138,7 @@ describe Feed do claimed_recording1.recording.feed.created_at = 32.days.ago claimed_recording1.recording.feed.save! - feeds, start = Feed.index(:type => 'recording') + feeds, start = Feed.index(user1, :type => 'recording') feeds.length.should == 0 end @@ -150,7 +150,7 @@ describe Feed do claimed_recording1.recording.feed.created_at = 25.hours.ago claimed_recording1.recording.feed.save! - feeds, start = Feed.index(:type => 'recording', time_range: 'today') + feeds, start = Feed.index(user1, :type => 'recording', time_range: 'today') feeds.length.should == 0 end @@ -162,7 +162,7 @@ describe Feed do claimed_recording1.recording.feed.created_at = 8.days.ago claimed_recording1.recording.feed.save! - feeds, start = Feed.index(:type => 'recording', time_range: 'week') + feeds, start = Feed.index(user1, :type => 'recording', time_range: 'week') feeds.length.should == 0 end @@ -174,7 +174,7 @@ describe Feed do claimed_recording1.recording.feed.created_at = 700.days.ago claimed_recording1.recording.feed.save! - feeds, start = Feed.index(:type => 'recording', time_range: 'all') + feeds, start = Feed.index(user1, :type => 'recording', time_range: 'all') feeds.length.should == 1 end end @@ -184,17 +184,17 @@ describe Feed do claimed_recording = FactoryGirl.create(:claimed_recording) options = {limit: 1} - feeds, start = Feed.index(options) + feeds, start = Feed.index(user1, options) feeds.length.should == 1 feeds[0].recording.should == claimed_recording.recording options[:start] = start - feeds, start = Feed.index(options) + feeds, start = Feed.index(user1, options) feeds.length.should == 1 feeds[0].music_session_history.should == claimed_recording.recording.music_session.music_session_history options[:start] = start - feeds, start = Feed.index(options) + feeds, start = Feed.index(user1, options) feeds.length.should == 0 start.should be_nil end @@ -205,17 +205,17 @@ describe Feed do FactoryGirl.create(:music_session_like, music_session_history: claimed_recording1.recording.music_session.music_session_history, user: user1) options = {limit: 1, sort: 'likes'} - feeds, start = Feed.index(options) + feeds, start = Feed.index(user1, options) feeds.length.should == 1 feeds[0].music_session_history.should == claimed_recording1.recording.music_session.music_session_history options[:start] = start - feeds, start = Feed.index(options) + feeds, start = Feed.index(user1, options) feeds.length.should == 1 feeds[0].recording.should == claimed_recording1.recording options[:start] = start - feeds, start = Feed.index(options) + feeds, start = Feed.index(user1, options) feeds.length.should == 0 start.should be_nil end @@ -226,21 +226,22 @@ describe Feed do FactoryGirl.create(:music_session_play, music_session: claimed_recording1.recording.music_session.music_session_history, user: user1) options = {limit: 1, sort: 'plays'} - feeds, start = Feed.index(options) + feeds, start = Feed.index(user1, options) feeds.length.should == 1 feeds[0].music_session_history.should == claimed_recording1.recording.music_session.music_session_history options[:start] = start - feeds, start = Feed.index(options) + feeds, start = Feed.index(user1, options) feeds.length.should == 1 feeds[0].recording.should == claimed_recording1.recording options[:start] = start - feeds, start = Feed.index(options) + feeds, start = Feed.index(user1, options) feeds.length.should == 0 start.should be_nil end - end + + end diff --git a/web/Gemfile b/web/Gemfile index 8d6e06166..b6e8db78b 100644 --- a/web/Gemfile +++ b/web/Gemfile @@ -54,7 +54,7 @@ gem 'fog' gem 'haml-rails' gem 'unf' #optional fog dependency gem 'devise', '>= 1.1.2' -#gem 'thin' # the presence of this gem on mac seems to prevent normal startup of rails. +gem 'puma' # the presence of this gem on mac seems to prevent normal startup of rails. gem 'postgres-copy' #group :libv8 do # gem 'libv8', "~> 3.11.8" diff --git a/web/app/controllers/api_feeds_controller.rb b/web/app/controllers/api_feeds_controller.rb index 41493fbba..370f17647 100644 --- a/web/app/controllers/api_feeds_controller.rb +++ b/web/app/controllers/api_feeds_controller.rb @@ -3,7 +3,7 @@ class ApiFeedsController < ApiController respond_to :json def index - @feeds, @next = Feed.index(user: current_user, + @feeds, @next = Feed.index(current_user, start: params[:since], limit: params[:limit], sort: params[:sort], diff --git a/web/spec/controllers/api_feeds_controller_spec.rb b/web/spec/controllers/api_feeds_controller_spec.rb index 61cd5c828..bc581e267 100644 --- a/web/spec/controllers/api_feeds_controller_spec.rb +++ b/web/spec/controllers/api_feeds_controller_spec.rb @@ -41,7 +41,6 @@ describe ApiFeedsController do json = JSON.parse(response.body, :symbolize_names => true) json[:entries].length.should == 1 - music_session = json[:entries][0] music_session[:type].should == 'music_session_history' end diff --git a/web/spec/spec_helper.rb b/web/spec/spec_helper.rb index fcc8f0c3f..ab3f7597c 100644 --- a/web/spec/spec_helper.rb +++ b/web/spec/spec_helper.rb @@ -67,6 +67,11 @@ Spork.prefork do config.visible_text_only = true end + Capybara.server do |app, port| + require 'rack/handler/puma' + Rack::Handler::Puma.run(app, :Port => port) + end + # Requires supporting ruby files with custom matchers and macros, etc, # in spec/support/ and its subdirectories. Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f} From 956ff422f403610518048b5b364af75037855f89 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Wed, 19 Feb 2014 22:48:34 -0500 Subject: [PATCH 12/21] fix sidebar for recordings --- ruby/lib/jam_ruby/models/band.rb | 3 +-- ruby/lib/jam_ruby/models/music_session_history.rb | 4 ---- ruby/lib/jam_ruby/models/recording.rb | 8 -------- ruby/lib/jam_ruby/models/user.rb | 3 +-- web/app/views/recordings/show.html.erb | 2 +- web/app/views/shared/_landing_sidebar.html.erb | 7 ++++--- 6 files changed, 7 insertions(+), 20 deletions(-) diff --git a/ruby/lib/jam_ruby/models/band.rb b/ruby/lib/jam_ruby/models/band.rb index 72838fad0..ea14cabe5 100644 --- a/ruby/lib/jam_ruby/models/band.rb +++ b/ruby/lib/jam_ruby/models/band.rb @@ -67,8 +67,7 @@ module JamRuby end def recent_history - recordings = ClaimedRecording.joins(:recording) - .where(:recordings => {:band_id => "#{self.id}"}) + recordings = Recording.where(:band_id => self.id) .order('created_at DESC') .limit(10) diff --git a/ruby/lib/jam_ruby/models/music_session_history.rb b/ruby/lib/jam_ruby/models/music_session_history.rb index b98d1d106..1c917fa2a 100644 --- a/ruby/lib/jam_ruby/models/music_session_history.rb +++ b/ruby/lib/jam_ruby/models/music_session_history.rb @@ -39,10 +39,6 @@ module JamRuby feed.music_session_history = self end - def like_count - self.likes.size - end - def comment_count self.comments.size end diff --git a/ruby/lib/jam_ruby/models/recording.rb b/ruby/lib/jam_ruby/models/recording.rb index 7e8ea3ac9..8fc666e0f 100644 --- a/ruby/lib/jam_ruby/models/recording.rb +++ b/ruby/lib/jam_ruby/models/recording.rb @@ -40,14 +40,6 @@ module JamRuby self.band_id = nil if self.band_id == '' end - def like_count - self.likes.size - end - - def play_count - self.plays.size - end - def comment_count self.comments.size end diff --git a/ruby/lib/jam_ruby/models/user.rb b/ruby/lib/jam_ruby/models/user.rb index 1c4f49700..7ff867717 100644 --- a/ruby/lib/jam_ruby/models/user.rb +++ b/ruby/lib/jam_ruby/models/user.rb @@ -275,8 +275,7 @@ module JamRuby end def recent_history - recordings = ClaimedRecording.joins(:recording) - .where(:recordings => {:owner_id => "#{self.id}"}) + recordings = Recording.where(:owner_id => self.id) .order('created_at DESC') .limit(10) diff --git a/web/app/views/recordings/show.html.erb b/web/app/views/recordings/show.html.erb index 82ea6adb5..dff09958b 100644 --- a/web/app/views/recordings/show.html.erb +++ b/web/app/views/recordings/show.html.erb @@ -18,7 +18,7 @@ <% end %>
    - <% unless @claimed_recording.recording.band.nil? %> + <% unless @claimed_recording.recording.band.blank? %>
    <% unless @claimed_recording.recording.band.photo_url.blank? %> <%= image_tag "#{@claimed_recording.recording.band.photo_url}", {:alt => ""} %> diff --git a/web/app/views/shared/_landing_sidebar.html.erb b/web/app/views/shared/_landing_sidebar.html.erb index ab14d09fc..673acebf7 100644 --- a/web/app/views/shared/_landing_sidebar.html.erb +++ b/web/app/views/shared/_landing_sidebar.html.erb @@ -4,14 +4,15 @@ <% recent_history.each do |history_record| %> - <% if history_record.instance_of? ClaimedRecording %> + <% if history_record.instance_of? Recording %>
    <%= history_record.created_at.strftime("%b #{history_record.created_at.day.ordinalize}") %>:
    +
    <%= history_record.claimed_recordings.first.description %>
    <% elsif history_record.instance_of? MusicSessionHistory %>
    @@ -31,9 +32,9 @@ Session Ended. Unavailable. <% end %>
    +
    <%= history_record.description %>
    <% end %> -
    <%= history_record.description %>
    <% if history_record != recent_history.last %>

    <% end %> From 069e9c829708fb696218e1f3eb500f5cbaa0448c Mon Sep 17 00:00:00 2001 From: Daniel Weigh Date: Thu, 20 Feb 2014 00:18:11 -0500 Subject: [PATCH 13/21] VRFS-1135 allow empty string to be passed in to start parameter (due to jQuery not stripping null / empty params and refactor) --- ruby/lib/jam_ruby/models/feed.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruby/lib/jam_ruby/models/feed.rb b/ruby/lib/jam_ruby/models/feed.rb index 27ba46c68..f85912c58 100644 --- a/ruby/lib/jam_ruby/models/feed.rb +++ b/ruby/lib/jam_ruby/models/feed.rb @@ -20,7 +20,7 @@ module JamRuby sort ||= 'date' raise "not valid sort #{sort}" unless SORT_TYPES.include?(sort) - start = params[:start] + start = params[:start].presence if sort == 'date' start ||= FIXNUM_MAX else From ca64966c7f2dcd2b5098539237290fe70caa0a66 Mon Sep 17 00:00:00 2001 From: Daniel Weigh Date: Thu, 20 Feb 2014 00:16:59 -0500 Subject: [PATCH 14/21] * duplicate entry $ bundle exec rails server Your Gemfile lists the gem bcrypt-ruby (= 3.0.1) more than once. You should probably keep only one of them. While it's not a problem now, it could cause errors if you change the version of just one of them later. --- admin/Gemfile | 1 - 1 file changed, 1 deletion(-) diff --git a/admin/Gemfile b/admin/Gemfile index 74f6a2125..5ec8a3411 100644 --- a/admin/Gemfile +++ b/admin/Gemfile @@ -37,7 +37,6 @@ gem 'bootstrap-will_paginate', '0.0.6' gem 'carrierwave', '0.9.0' gem 'carrierwave_direct' gem 'uuidtools', '2.1.2' -gem 'bcrypt-ruby', '3.0.1' gem 'jquery-rails' # , '2.3.0' # pinned because jquery-ui-rails was split from jquery-rails, but activeadmin doesn't support this gem yet gem 'jquery-ui-rails' gem 'rails3-jquery-autocomplete' From 1ecc21fd9c1ca053dbf74709af510bf0a632d9a3 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Thu, 20 Feb 2014 02:03:00 -0500 Subject: [PATCH 15/21] VRFS-1188 play/pause for recording landing page --- .../stylesheets/web/recordings.css.scss | 1 - web/app/views/recordings/show.html.erb | 47 +++++++++++++++++-- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/web/app/assets/stylesheets/web/recordings.css.scss b/web/app/assets/stylesheets/web/recordings.css.scss index 45e591aa1..2e3ac9e24 100644 --- a/web/app/assets/stylesheets/web/recordings.css.scss +++ b/web/app/assets/stylesheets/web/recordings.css.scss @@ -45,7 +45,6 @@ .recording-slider { position:absolute; - left:25%; top:0px; } diff --git a/web/app/views/recordings/show.html.erb b/web/app/views/recordings/show.html.erb index dff09958b..be3de35d8 100644 --- a/web/app/views/recordings/show.html.erb +++ b/web/app/views/recordings/show.html.erb @@ -51,17 +51,19 @@
    <%= @claimed_recording.description %>

    - <%= image_tag "content/icon_playbutton.png", {:width => 20, :height => 20, :alt => ""} %> + <%= image_tag "content/icon_playbutton.png", {:id => "imgPlayPause", :width => 20, :height => 20, :alt => ""} %>
    0:00
    <%= image_tag "content/slider_playcontrols.png", {:width => 5, :height => 16, :alt => ""} %>
    -
    4:59
    +
    -
    1:23
    +
    0:00
    +
    -
    <%= @claimed_recording.genre_id.capitalize %>
    <%= @claimed_recording.recording.play_count %> @@ -99,9 +101,44 @@ <% content_for :extra_js do %> <% end %> From f37e7de42ed61c6ab3a86bec91539f4925bce52e Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Thu, 20 Feb 2014 02:07:09 -0500 Subject: [PATCH 16/21] VRFS-1188 remove hard-coded audio source / reset slider when playback is complete --- web/app/views/recordings/show.html.erb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/web/app/views/recordings/show.html.erb b/web/app/views/recordings/show.html.erb index be3de35d8..1eef34c11 100644 --- a/web/app/views/recordings/show.html.erb +++ b/web/app/views/recordings/show.html.erb @@ -61,7 +61,8 @@
    0:00
    <%= @claimed_recording.genre_id.capitalize %>
    @@ -123,8 +124,10 @@ $(".recording-slider").css({'left': percentComplete + '%'}); $(".recording-current").html(formatTime(htmlAudio.currentTime)); + // reset icon to play and slider to far left when done if (percentComplete === 100) { $("#imgPlayPause").attr('src', '/assets/content/icon_playbutton.png'); + $(".recording-slider").css({'left': 0 + '%'}); } }); From df14676495dedd01d12cd0590280dea3122a12b0 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Thu, 20 Feb 2014 07:45:51 +0000 Subject: [PATCH 17/21] * VRFS-1135 - user and band targetting possible --- db/manifest | 3 +- db/up/music_session_history_public.sql | 1 + ruby/lib/jam_ruby/models/feed.rb | 71 +++++- ruby/lib/jam_ruby/models/music_session.rb | 6 +- .../jam_ruby/models/music_session_history.rb | 3 +- ruby/spec/factories.rb | 3 +- ruby/spec/jam_ruby/connection_manager_spec.rb | 2 +- ruby/spec/jam_ruby/models/feed_spec.rb | 212 ++++++++++++++++++ .../models/music_session_history_spec.rb | 9 +- .../music_sessions_user_history_spec.rb | 11 +- ruby/spec/spec_helper.rb | 1 + web/Gemfile | 1 - web/app/controllers/api_feeds_controller.rb | 4 +- web/lib/music_session_manager.rb | 10 +- .../controllers/api_feeds_controller_spec.rb | 27 +++ web/spec/factories.rb | 10 +- web/spec/spec_helper.rb | 54 +++-- web/spec/support/utilities.rb | 4 +- 18 files changed, 363 insertions(+), 69 deletions(-) create mode 100644 db/up/music_session_history_public.sql diff --git a/db/manifest b/db/manifest index def0d818f..6d546452d 100755 --- a/db/manifest +++ b/db/manifest @@ -113,4 +113,5 @@ feed_use_recording.sql feed_autoincrement_primary_key.sql music_sessions_plays.sql plays_likes_counters.sql -add_upright_bass.sql \ No newline at end of file +add_upright_bass.sql +music_session_history_public.sql \ No newline at end of file diff --git a/db/up/music_session_history_public.sql b/db/up/music_session_history_public.sql new file mode 100644 index 000000000..cbff9b62a --- /dev/null +++ b/db/up/music_session_history_public.sql @@ -0,0 +1 @@ +ALTER TABLE music_sessions_history ADD COLUMN fan_access BOOLEAN NOT NULL; \ No newline at end of file diff --git a/ruby/lib/jam_ruby/models/feed.rb b/ruby/lib/jam_ruby/models/feed.rb index 27ba46c68..b505b71af 100644 --- a/ruby/lib/jam_ruby/models/feed.rb +++ b/ruby/lib/jam_ruby/models/feed.rb @@ -36,17 +36,18 @@ module JamRuby type_filter ||= 'all' raise "not valid type #{type_filter}" unless TYPE_FILTERS.include?(type_filter) - include_private = params[:include_private] - include_private ||= false # default to false - include_private = false if user.nil? # and force to false if the current user is nil + target_user = params[:user] + target_band = params[:band] - - query = Feed.includes([:recording]).includes([:music_session_history]).limit(limit) + #query = Feed.includes([:recording]).includes([:music_session_history]).limit(limit) + query = Feed.joins("LEFT OUTER JOIN recordings ON recordings.id = feeds.recording_id") + .joins("LEFT OUTER JOIN music_sessions_history ON music_sessions_history.id = feeds.music_session_id") + .limit(limit) # handle sort if sort == 'date' - query = query.where("id < #{start}") - query = query.order('id DESC') + query = query.where("feeds.id < #{start}") + query = query.order('feeds.id DESC') elsif sort == 'plays' query = query.offset(start) query = query.order("COALESCE(recordings.play_count, music_sessions_history.play_count) DESC ") @@ -65,18 +66,64 @@ module JamRuby # handle type filters if type_filter == 'music_session_history' - query = query.where('music_session_id is not NULL') + query = query.where('feeds.music_session_id is not NULL') elsif type_filter == 'recording' - query = query.where('recording_id is not NULL') + query = query.where('feeds.recording_id is not NULL') end - # handle private times - if include_private + if target_user + + if target_user != user.id + require_public_recordings = "claimed_recordings.is_public = TRUE AND" + require_public_sessions = "music_sessions_history.fan_access = TRUE AND" + end + + query = query.joins("LEFT OUTER JOIN claimed_recordings ON recordings.id = claimed_recordings.recording_id AND #{require_public_recordings} (claimed_recordings.user_id = '#{target_user}' OR (recordings.band_id IN (SELECT band_id FROM bands_musicians where user_id='#{target_user}')))") + query = query.joins("LEFT OUTER JOIN music_sessions_user_history ON music_sessions_history.id = music_sessions_user_history.music_session_id AND #{require_public_sessions} music_sessions_user_history.user_id = '#{target_user}'") + query = query.group("feeds.id, feeds.recording_id, feeds.music_session_id, feeds.created_at, feeds.updated_at, recordings.id, music_sessions_history.id") + if sort == 'plays' + query = query.group("COALESCE(recordings.play_count, music_sessions_history.play_count)") + elsif sort == 'likes' + query = query.group("COALESCE(recordings.like_count, music_sessions_history.like_count)") + end + query = query.where('recordings.id is NULL OR claimed_recordings.id IS NOT NULL') + query = query.where('music_sessions_history.id is NULL OR music_sessions_user_history.id IS NOT NULL') + + elsif target_band + + unless Band.find(target_band).users.include?(user) + require_public_recordings = "claimed_recordings.is_public = TRUE AND" + require_public_sessions = "music_sessions_history.fan_access = TRUE AND" + end + + query = query.joins("LEFT OUTER JOIN claimed_recordings ON recordings.id = claimed_recordings.recording_id AND #{require_public_recordings} recordings.band_id = '#{target_band}'") + query = query.where("music_sessions_history IS NULL OR #{require_public_sessions} music_sessions_history.band_id = '#{target_band}'") + query = query.group("feeds.id, feeds.recording_id, feeds.music_session_id, feeds.created_at, feeds.updated_at, recordings.id, music_sessions_history.id") + if sort == 'plays' + query = query.group("COALESCE(recordings.play_count, music_sessions_history.play_count)") + elsif sort == 'likes' + query = query.group("COALESCE(recordings.like_count, music_sessions_history.like_count)") + end + query = query.where('recordings.id is NULL OR claimed_recordings.id IS NOT NULL') + #query = query.where('music_sessions_history.id is NULL OR music_sessions_user_history.id IS NOT NULL') else - + query = query.joins('LEFT OUTER JOIN claimed_recordings ON recordings.id = claimed_recordings.recording_id AND claimed_recordings.is_public = TRUE') + query = query.joins("LEFT OUTER JOIN music_sessions_user_history ON music_sessions_history.id = music_sessions_user_history.music_session_id AND music_sessions_history.fan_access = TRUE") + query = query.group("feeds.id, feeds.recording_id, feeds.music_session_id, feeds.created_at, feeds.updated_at, recordings.id, music_sessions_history.id") + if sort == 'plays' + query = query.group("COALESCE(recordings.play_count, music_sessions_history.play_count)") + elsif sort == 'likes' + query = query.group("COALESCE(recordings.like_count, music_sessions_history.like_count)") + end + query = query.where('recordings.id is NULL OR claimed_recordings.is_public = TRUE') + query = query.where('music_sessions_history.id is NULL OR music_sessions_user_history.id IS NOT NULL') end + + + + if query.length == 0 [query, nil] elsif query.length < limit diff --git a/ruby/lib/jam_ruby/models/music_session.rb b/ruby/lib/jam_ruby/models/music_session.rb index e4ca47cfb..c32e2650e 100644 --- a/ruby/lib/jam_ruby/models/music_session.rb +++ b/ruby/lib/jam_ruby/models/music_session.rb @@ -24,7 +24,7 @@ module JamRuby has_many :recordings, :class_name => "JamRuby::Recording", :inverse_of => :music_session belongs_to :band, :inverse_of => :music_sessions, :class_name => "JamRuby::Band", :foreign_key => "band_id" - after_save :require_at_least_one_genre, :limit_max_genres + after_save :require_at_least_one_genre, :limit_max_genres, :sync_music_session_history after_destroy do |obj| JamRuby::MusicSessionHistory.removed_music_session(obj.id) @@ -277,5 +277,9 @@ module JamRuby end end end + + def sync_music_session_history + MusicSessionHistory.save(self) + end end end diff --git a/ruby/lib/jam_ruby/models/music_session_history.rb b/ruby/lib/jam_ruby/models/music_session_history.rb index b98d1d106..09b6ab5b2 100644 --- a/ruby/lib/jam_ruby/models/music_session_history.rb +++ b/ruby/lib/jam_ruby/models/music_session_history.rb @@ -19,7 +19,7 @@ module JamRuby :class_name => 'JamRuby::MusicSession', :foreign_key => 'music_session_id') - has_many :music_session_user_histories, :class_name => "JamRuby::MusicSessionUserHistory", :foreign_key => "music_session_id" + has_many :music_session_user_histories, :class_name => "JamRuby::MusicSessionUserHistory", :foreign_key => "music_session_id", :dependent => :delete_all has_many :comments, :class_name => "JamRuby::MusicSessionComment", :foreign_key => "music_session_id" has_many :likes, :class_name => "JamRuby::MusicSessionLiker", :foreign_key => "session_id" has_many :plays, :class_name => "JamRuby::MusicSessionPlay", :foreign_key => "music_session_id" @@ -131,6 +131,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 SEPARATOR + session_history.fan_access = music_session.fan_access session_history.save! end diff --git a/ruby/spec/factories.rb b/ruby/spec/factories.rb index 8c61cc99d..8f277cd51 100644 --- a/ruby/spec/factories.rb +++ b/ruby/spec/factories.rb @@ -50,7 +50,7 @@ FactoryGirl.define do factory :music_session do after(:create) { |session| - MusicSessionHistory.save(session) + FactoryGirl.create(:music_session_user_history, :history => session.music_session_history, :user => session.creator) } factory :music_session_with_mount do @@ -66,6 +66,7 @@ FactoryGirl.define do music_session nil end + fan_access true music_session_id { music_session.id } description { music_session.description } user_id { music_session.user_id } diff --git a/ruby/spec/jam_ruby/connection_manager_spec.rb b/ruby/spec/jam_ruby/connection_manager_spec.rb index dc093a0cb..99bc51a4e 100644 --- a/ruby/spec/jam_ruby/connection_manager_spec.rb +++ b/ruby/spec/jam_ruby/connection_manager_spec.rb @@ -23,7 +23,7 @@ describe ConnectionManager do 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| 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]) + @conn.exec("INSERT INTO music_sessions_history (music_session_id, description, user_id, fan_access) VALUES ($1, $2, $3, $4)", [session_id, description, user_id, true]) return session_id end end diff --git a/ruby/spec/jam_ruby/models/feed_spec.rb b/ruby/spec/jam_ruby/models/feed_spec.rb index 9c86b974c..7ec9a5f79 100644 --- a/ruby/spec/jam_ruby/models/feed_spec.rb +++ b/ruby/spec/jam_ruby/models/feed_spec.rb @@ -6,6 +6,7 @@ describe Feed do let (:user2) { FactoryGirl.create(:user) } let (:user3) { FactoryGirl.create(:user) } let (:user4) { FactoryGirl.create(:user) } + let (:band) { FactoryGirl.create(:band) } it "no result" do feeds, start = Feed.index(user1) @@ -14,6 +15,7 @@ describe Feed do it "one claimed recording" do claimed_recording = FactoryGirl.create(:claimed_recording) + MusicSessionUserHistory.delete_all # the factory makes a music_session while making the recording/claimed_recording MusicSessionHistory.delete_all # the factory makes a music_session while making the recording/claimed_recording feeds, start = Feed.index(user1) feeds.length.should == 1 @@ -25,6 +27,7 @@ describe Feed do second_track = FactoryGirl.create(:recorded_track, recording: recording) recording.recorded_tracks << second_track FactoryGirl.create(:claimed_recording, recording: recording, user: second_track.user) + MusicSessionUserHistory.delete_all # the factory makes a music_session while making the recording/claimed_recording MusicSessionHistory.delete_all # verify the mess above only made one recording @@ -41,6 +44,16 @@ describe Feed do feeds[0].music_session_history == music_session.music_session_history end + it "does not return a recording with no claimed recordings" do + recording = FactoryGirl.create(:recording) + MusicSessionUserHistory.delete_all # the factory makes a music_session while making the recording/claimed_recording + MusicSessionHistory.delete_all + + + feeds, start = Feed.index(user1) + feeds.length.should == 0 + end + describe "sorting" do it "sorts by index (date) DESC" do claimed_recording = FactoryGirl.create(:claimed_recording) @@ -242,6 +255,205 @@ describe Feed do end end + describe "public feed" do + it "only public" do + claimed_recording1 = FactoryGirl.create(:claimed_recording) + claimed_recording1.is_public = false + claimed_recording1.save! + + feeds, start = Feed.index(claimed_recording1.user) + feeds.length.should == 1 + + claimed_recording1.recording.music_session.fan_access = false + claimed_recording1.recording.music_session.save! + + feeds, start = Feed.index(claimed_recording1.user) + feeds.length.should == 0 + end + end + + describe "band feeds" do + it "does show other band's stuff in this feed" do + other_band = FactoryGirl.create(:band) + music_session = FactoryGirl.create(:music_session, band: other_band) + FactoryGirl.create(:music_session_user_history, :history => music_session.music_session_history, :user => user1) + + claimed_recording1 = FactoryGirl.create(:claimed_recording) + claimed_recording1.is_public = true + claimed_recording1.recording.band = other_band + claimed_recording1.recording.save! + claimed_recording1.save! + + feeds, start = Feed.index(user1, band: band.id) + feeds.length.should == 0 + end + + it "shows public recordings to you and to others" do + user1.bands << band + user1.save! + music_session = FactoryGirl.create(:music_session, band: band) + music_session.music_session_history.fan_access.should be_true + + feeds, start = Feed.index(user1, band: band.id) + feeds.length.should == 1 + feeds[0].music_session_history.should == music_session.music_session_history + + feeds, start = Feed.index(user2, band: band.id) + feeds.length.should == 1 + feeds[0].music_session_history.should == music_session.music_session_history + end + + it "shows private sessions to you, not to others" do + user1.bands << band + user1.save! + music_session = FactoryGirl.create(:music_session, band: band, fan_access: false) + music_session.music_session_history.fan_access.should be_false + + feeds, start = Feed.index(user1, band: band.id) + feeds.length.should == 1 + feeds[0].music_session_history.should == music_session.music_session_history + feeds[0].music_session_history.fan_access.should be_false + + + feeds, start = Feed.index(user2, band: band.id) + feeds.length.should == 0 + end + + it "shows public recordings to you and to others" do + claimed_recording1 = FactoryGirl.create(:claimed_recording) + claimed_recording1.is_public = true + claimed_recording1.recording.band = band + claimed_recording1.recording.save! + claimed_recording1.save! + + feeds, start = Feed.index(claimed_recording1.user, band: band.id) + feeds.length.should == 1 + feeds[0].recording.should == claimed_recording1.recording + + feeds, start = Feed.index(user1, band: band.id) + feeds.length.should == 1 + feeds[0].recording.should == claimed_recording1.recording + end + + it "shows private recordings to you, not to others" do + claimed_recording1 = FactoryGirl.create(:claimed_recording) + claimed_recording1.is_public = false + claimed_recording1.recording.band = band + claimed_recording1.recording.save! + claimed_recording1.save! + + claimed_recording1.user.bands << band + claimed_recording1.user.save! + + feeds, start = Feed.index(claimed_recording1.user, band: band.id) + feeds.length.should == 1 + feeds[0].recording.should == claimed_recording1.recording + + feeds, start = Feed.index(user1, band: band.id) + feeds.length.should == 0 + end + end + + describe "user feeds" do + it "does not show stuff from other people" do + music_session = FactoryGirl.create(:music_session) + FactoryGirl.create(:music_session_user_history, :history => music_session.music_session_history, :user => user2) + + claimed_recording1 = FactoryGirl.create(:claimed_recording) + claimed_recording1.is_public = true + claimed_recording1.save! + + feeds, start = Feed.index(user1, user: user1.id) + feeds.length.should == 0 + end + + it "shows public sessions to you and to others" do + music_session = FactoryGirl.create(:music_session) + FactoryGirl.create(:music_session_user_history, :history => music_session.music_session_history, :user => user1) + + feeds, start = Feed.index(user1, user: user1.id) + feeds.length.should == 1 + feeds[0].music_session_history.should == music_session.music_session_history + + feeds, start = Feed.index(user2, user: user1.id) + feeds.length.should == 1 + feeds[0].music_session_history.should == music_session.music_session_history + end + + + it "shows private sessions to you, not to others" do + music_session = FactoryGirl.create(:music_session, fan_access: false) + music_session.music_session_history.fan_access.should be_false + FactoryGirl.create(:music_session_user_history, :history => music_session.music_session_history, :user => user1) + + feeds, start = Feed.index(user1, user: user1.id) + feeds.length.should == 1 + feeds[0].music_session_history.should == music_session.music_session_history + feeds[0].music_session_history.fan_access.should be_false + + + feeds, start = Feed.index(user2, user: user1.id) + feeds.length.should == 0 + end + + it "shows public recordings to you and to others" do + claimed_recording1 = FactoryGirl.create(:claimed_recording) + claimed_recording1.is_public = true + claimed_recording1.save! + + feeds, start = Feed.index(claimed_recording1.user, user: claimed_recording1.user.id) + feeds.length.should == 1 + feeds[0].recording.should == claimed_recording1.recording + + feeds, start = Feed.index(user1, user: claimed_recording1.user.id) + feeds.length.should == 1 + feeds[0].recording.should == claimed_recording1.recording + end + + it "shows private recordings to you, not to others" do + claimed_recording1 = FactoryGirl.create(:claimed_recording) + claimed_recording1.is_public = false + claimed_recording1.save! + + feeds, start = Feed.index(claimed_recording1.user, user: claimed_recording1.user.id) + feeds.length.should == 1 + feeds[0].recording.should == claimed_recording1.recording + + feeds, start = Feed.index(user1, user: claimed_recording1.user.id) + feeds.length.should == 0 + end + + it "shows band recordings to you even though you did not claim a recording" do + user1.bands << band + user1.save! + user2.bands << band + user2.save! + + claimed_recording1 = FactoryGirl.create(:claimed_recording, user: user2) + claimed_recording1.is_public = true + claimed_recording1.recording.band = band + claimed_recording1.recording.save! + claimed_recording1.save! + + feeds, start = Feed.index(user1, user: user1.id) + feeds.length.should == 1 + feeds[0].recording.should == claimed_recording1 .recording + + # make it private; should still be available + claimed_recording1.is_public = false + claimed_recording1.save! + + feeds, start = Feed.index(user1, user: user1.id) + feeds.length.should == 1 + feeds[0].recording.should == claimed_recording1 .recording + + # take user1 out of the band; shouldn't be able to see it + user1.bands.delete_all + + feeds, start = Feed.index(user1, user: user1.id) + feeds.length.should == 0 + end + end end diff --git a/ruby/spec/jam_ruby/models/music_session_history_spec.rb b/ruby/spec/jam_ruby/models/music_session_history_spec.rb index 183e9f6c4..0a66ec69b 100644 --- a/ruby/spec/jam_ruby/models/music_session_history_spec.rb +++ b/ruby/spec/jam_ruby/models/music_session_history_spec.rb @@ -4,18 +4,17 @@ describe MusicSessionHistory do let(:some_user) { FactoryGirl.create(:user) } let(:music_session) { FactoryGirl.create(:music_session_no_history) } - 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) } + let(:user_history1) { FactoryGirl.create(:music_session_user_history, :history => music_session.music_session_history, :user => music_session.creator) } + let(:user_history2) { FactoryGirl.create(:music_session_user_history, :history => music_session.music_session_history, :user => some_user) } it "create" do - history.description.should eql(music_session.description) + music_session.music_session_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 = music_session.music_session_history.unique_users users.length.should eql(2) diff --git a/ruby/spec/jam_ruby/models/music_sessions_user_history_spec.rb b/ruby/spec/jam_ruby/models/music_sessions_user_history_spec.rb index b6c719151..3d7713b86 100644 --- a/ruby/spec/jam_ruby/models/music_sessions_user_history_spec.rb +++ b/ruby/spec/jam_ruby/models/music_sessions_user_history_spec.rb @@ -4,9 +4,8 @@ describe MusicSessionUserHistory do let(:some_user) { FactoryGirl.create(:user) } let(:music_session) { FactoryGirl.create(:music_session_no_history) } - 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) } + let(:user_history1) { FactoryGirl.create(:music_session_user_history, :history => music_session.music_session_history, :user => music_session.creator) } + let(:user_history2) { FactoryGirl.create(:music_session_user_history, :history => music_session.music_session_history, :user => some_user) } describe "create" do it {user_history1.music_session_id.should == music_session.id } @@ -78,7 +77,7 @@ describe MusicSessionUserHistory do end it "two histories with same user within bounds of history1" do - user_history3 = FactoryGirl.create(:music_session_user_history, :history => history, :user => some_user) + user_history3 = FactoryGirl.create(:music_session_user_history, :history => music_session.music_session_history, :user => some_user) # if user2 comes and goes 2 times while user one is there, it shouldn't be a false 3 user_history1.session_removed_at = user_history1.created_at + 5 @@ -99,7 +98,7 @@ describe MusicSessionUserHistory do it "two histories with different user within bounds of history1" do third_user = FactoryGirl.create(:user); - user_history3 = FactoryGirl.create(:music_session_user_history, :history => history, :user => third_user) + user_history3 = FactoryGirl.create(:music_session_user_history, :history => music_session.music_session_history, :user => third_user) # if user2 comes and goes 2 times while user one is there, it shouldn't be a false 3 user_history1.session_removed_at = user_history1.created_at + 5 @@ -120,7 +119,7 @@ describe MusicSessionUserHistory do it "two overlapping histories with different user within bounds of history1" do third_user = FactoryGirl.create(:user); - user_history3 = FactoryGirl.create(:music_session_user_history, :history => history, :user => third_user) + user_history3 = FactoryGirl.create(:music_session_user_history, :history => music_session.music_session_history, :user => third_user) # if user2 comes and goes 2 times while user one is there, it shouldn't be a false 3 user_history1.session_removed_at = user_history1.created_at + 5 diff --git a/ruby/spec/spec_helper.rb b/ruby/spec/spec_helper.rb index 8b747ead5..60f32513b 100644 --- a/ruby/spec/spec_helper.rb +++ b/ruby/spec/spec_helper.rb @@ -61,6 +61,7 @@ Spork.prefork do # # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration RSpec.configure do |config| + config.color_enabled = true config.treat_symbols_as_metadata_keys_with_true_values = true config.run_all_when_everything_filtered = true config.filter_run :focus diff --git a/web/Gemfile b/web/Gemfile index b6e8db78b..beabc2ec6 100644 --- a/web/Gemfile +++ b/web/Gemfile @@ -54,7 +54,6 @@ gem 'fog' gem 'haml-rails' gem 'unf' #optional fog dependency gem 'devise', '>= 1.1.2' -gem 'puma' # the presence of this gem on mac seems to prevent normal startup of rails. gem 'postgres-copy' #group :libv8 do # gem 'libv8', "~> 3.11.8" diff --git a/web/app/controllers/api_feeds_controller.rb b/web/app/controllers/api_feeds_controller.rb index 370f17647..db17218b1 100644 --- a/web/app/controllers/api_feeds_controller.rb +++ b/web/app/controllers/api_feeds_controller.rb @@ -8,7 +8,9 @@ class ApiFeedsController < ApiController limit: params[:limit], sort: params[:sort], time_range: params[:time_range], - type: params[:type]) + type: params[:type], + user: params[:user], + band: params[:band]) render "api_feeds/index", :layout => nil end diff --git a/web/lib/music_session_manager.rb b/web/lib/music_session_manager.rb index b9d69fae7..3efc2cb90 100644 --- a/web/lib/music_session_manager.rb +++ b/web/lib/music_session_manager.rb @@ -43,9 +43,6 @@ MusicSessionManager < BaseManager # save session parameters for next session User.save_session_settings(user, music_session) - # save session history - MusicSessionHistory.save(music_session) - # auto-join this user into the newly created session as_musician = true connection = ConnectionManager.new.join_music_session(user, client_id, music_session, as_musician, tracks) @@ -96,12 +93,9 @@ MusicSessionManager < BaseManager update[:genres] = genre_array end - if music_session.update_attributes(update) - # save session history (only thing that could change is description) - MusicSessionHistory.save(music_session) - end + music_session.update_attributes(update) - return music_session + music_session end def participant_create(user, music_session_id, client_id, as_musician, tracks) diff --git a/web/spec/controllers/api_feeds_controller_spec.rb b/web/spec/controllers/api_feeds_controller_spec.rb index bc581e267..4841f3f73 100644 --- a/web/spec/controllers/api_feeds_controller_spec.rb +++ b/web/spec/controllers/api_feeds_controller_spec.rb @@ -9,6 +9,7 @@ describe ApiFeedsController do before(:each) do MusicSession.delete_all + MusicSessionUserHistory.delete_all MusicSessionHistory.delete_all Recording.delete_all end @@ -23,6 +24,7 @@ describe ApiFeedsController do it "returns a recording" do claimed_recording.touch # artifact of factory of :claimed_recording that this gets created + MusicSessionUserHistory.delete_all MusicSessionHistory.delete_all get :index @@ -109,4 +111,29 @@ describe ApiFeedsController do _next.should be_nil end end + + describe "user targetting" do + + it "user viewing own profile" do + pending 'not tested' + music_session.fan_access = false + music_session.save! + controller.current_user = music_session.creator + + get :index + json = JSON.parse(response.body, :symbolize_names => true) + json[:entries].length.should == 1 + end + + it "user viewing someone else's profile" do + pending 'not tested' + music_session.fan_access = false + music_session.save! + controller.current_user = user + + get :index + json = JSON.parse(response.body, :symbolize_names => true) + json[:entries].length.should == 0 + end + end end diff --git a/web/spec/factories.rb b/web/spec/factories.rb index 71b608c6a..1d5716708 100644 --- a/web/spec/factories.rb +++ b/web/spec/factories.rb @@ -52,12 +52,20 @@ FactoryGirl.define do association :creator, :factory => :user after(:create) { |session| - MusicSessionHistory.save(session) + FactoryGirl.create(:music_session_user_history, :history => session.music_session_history, :user => session.creator) } 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 diff --git a/web/spec/spec_helper.rb b/web/spec/spec_helper.rb index ab3f7597c..24192489b 100644 --- a/web/spec/spec_helper.rb +++ b/web/spec/spec_helper.rb @@ -1,6 +1,6 @@ require 'simplecov' require 'rubygems' -require 'spork' +#require 'spork' require 'omniauth' #uncomment the following line to use spork with the debugger #require 'spork/ext/ruby-debug' @@ -33,44 +33,23 @@ ActionMailer::Base.delivery_method = :test RecordedTrack.observers.disable :all # only a few tests want this observer active -Spork.prefork do +#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' require File.expand_path("../../config/environment", __FILE__) require 'rspec/rails' - require 'rspec/autorun' + #require 'rspec/autorun' require 'capybara' require 'capybara/rspec' require 'capybara-screenshot/rspec' require 'capybara/poltergeist' - Capybara.register_driver :poltergeist do |app| - driver = Capybara::Poltergeist::Driver.new(app, { debug: false, phantomjs_logger: File.open('log/phantomjs.out', 'w') }) - end - Capybara.javascript_driver = :poltergeist - Capybara.default_wait_time = 10 - if defined?(TEST_CONNECT_STATES) && TEST_CONNECT_STATES - TEST_CONNECT_STATE_JS_CONSOLE_IO = File.open(TEST_CONNECT_STATE_JS_CONSOLE, 'w') - Capybara.register_driver :poltergeist do |app| - Capybara::Poltergeist::Driver.new(app, { phantomjs_logger: TEST_CONNECT_STATE_JS_CONSOLE_IO }) - end - Capybara.javascript_driver = :poltergeist - end - Capybara.configure do |config| - config.match = :one - config.exact_options = true - config.ignore_hidden_elements = true - config.visible_text_only = true - end - Capybara.server do |app, port| - require 'rack/handler/puma' - Rack::Handler::Puma.run(app, :Port => port) - end + # Requires supporting ruby files with custom matchers and macros, etc, # in spec/support/ and its subdirectories. @@ -79,6 +58,25 @@ Spork.prefork do ActiveRecord::Base.logger = Logger.new(STDOUT) if defined?(ActiveRecord::Base) RSpec.configure do |config| + Capybara.register_driver :poltergeist do |app| + driver = Capybara::Poltergeist::Driver.new(app, { debug: false, phantomjs_logger: File.open('log/phantomjs.out', 'w') }) + end + Capybara.javascript_driver = :poltergeist + Capybara.default_wait_time = 10 + + Capybara.configure do |config| + config.match = :one + config.exact_options = true + config.ignore_hidden_elements = true + config.visible_text_only = true + config.run_server = false + end + + Capybara.server do |app, port| + raise "gaaag" + require 'rack/handler/puma' + Rack::Handler::Puma.run(app, :Port => port) + end # ## Mock Framework # # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line: @@ -154,12 +152,12 @@ Spork.prefork do wipe_s3_test_bucket end end -end +#end -Spork.each_run do +#Spork.each_run do # This code will be run each time you run your specs. -end +#end diff --git a/web/spec/support/utilities.rb b/web/spec/support/utilities.rb index a38b77ce7..7d44a27ea 100644 --- a/web/spec/support/utilities.rb +++ b/web/spec/support/utilities.rb @@ -56,8 +56,8 @@ end def sign_in_poltergeist(user) visit signin_path - fill_in "Email Address:", with: user.email - fill_in "Password:", with: user.password + fill_in "session_email", with: user.email + fill_in "session_password", with: user.password click_button "SIGN IN" if Capybara.javascript_driver == :poltergeist From 1940e6d8ffd0cde5ec2dd4efe275c501aafda32a Mon Sep 17 00:00:00 2001 From: Seth Call Date: Thu, 20 Feb 2014 08:01:18 +0000 Subject: [PATCH 18/21] * VRFS-1135 a few more tests in controller for user and band targetting --- web/app/views/api_feeds/show.rabl | 4 +- .../controllers/api_feeds_controller_spec.rb | 46 +++++++++++++++++-- 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/web/app/views/api_feeds/show.rabl b/web/app/views/api_feeds/show.rabl index d3253d3d7..899cb9b52 100644 --- a/web/app/views/api_feeds/show.rabl +++ b/web/app/views/api_feeds/show.rabl @@ -7,7 +7,7 @@ glue :music_session_history do 'music_session_history' end - attributes :id, :description, :genres, :created_at, :session_removed_at, :comment_count, :like_count, :play_count + attributes :id, :description, :genres, :created_at, :session_removed_at, :comment_count, :like_count, :play_count, :fan_access child(:user => :creator) { @@ -67,7 +67,7 @@ glue :recording do child(:claimed_recordings => :claimed_recordings) { - attributes :id, :name, :description, :is_public, :is_downloadable, :genre_id + attributes :id, :name, :description, :is_public, :genre_id child(:user => :creator) { attributes :id, :first_name, :last_name, :photo_url diff --git a/web/spec/controllers/api_feeds_controller_spec.rb b/web/spec/controllers/api_feeds_controller_spec.rb index 4841f3f73..e3c906744 100644 --- a/web/spec/controllers/api_feeds_controller_spec.rb +++ b/web/spec/controllers/api_feeds_controller_spec.rb @@ -4,6 +4,8 @@ describe ApiFeedsController do render_views let(:user) { FactoryGirl.create(:user) } + let(:user2) { FactoryGirl.create(:user) } + let(:band) { FactoryGirl.create(:band) } let(:music_session) {FactoryGirl.create(:music_session, creator: user) } let(:claimed_recording) {FactoryGirl.create(:claimed_recording) } @@ -115,23 +117,59 @@ describe ApiFeedsController do describe "user targetting" do it "user viewing own profile" do - pending 'not tested' music_session.fan_access = false music_session.save! controller.current_user = music_session.creator - get :index + get :index, { user: music_session.creator.id } json = JSON.parse(response.body, :symbolize_names => true) json[:entries].length.should == 1 end it "user viewing someone else's profile" do - pending 'not tested' music_session.fan_access = false music_session.save! + controller.current_user = user2 + music_session.music_session_history.reload + music_session.music_session_history.fan_access.should be_false + + get :index, { user: music_session.creator.id } + json = JSON.parse(response.body, :symbolize_names => true) + json[:entries].length.should == 0 + end + end + + describe "band targetting" do + + it "user viewing own band" do + user.bands << band + user.save! + claimed_recording1 = FactoryGirl.create(:claimed_recording, user: user) + claimed_recording1.is_public = false + claimed_recording1.recording.band = band + claimed_recording1.recording.save! + claimed_recording1.save! + controller.current_user = user - get :index + get :index, { band: band.id } + json = JSON.parse(response.body, :symbolize_names => true) + json[:entries].length.should == 1 + end + + it "user viewing someone else's band" do + user.bands << band + user.save! + + claimed_recording1 = FactoryGirl.create(:claimed_recording, user: user) + claimed_recording1.is_public = false + claimed_recording1.recording.band = band + claimed_recording1.recording.save! + claimed_recording1.save! + + controller.current_user = user2 + + get :index, { band: band.id } json = JSON.parse(response.body, :symbolize_names => true) json[:entries].length.should == 0 end From cda7ef8ce9ce51e1ef9ce23fe1481d569d9aa759 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Thu, 20 Feb 2014 08:02:50 +0000 Subject: [PATCH 19/21] * revert back spec_helper --- web/spec/spec_helper.rb | 55 +++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 29 deletions(-) diff --git a/web/spec/spec_helper.rb b/web/spec/spec_helper.rb index 24192489b..40d775465 100644 --- a/web/spec/spec_helper.rb +++ b/web/spec/spec_helper.rb @@ -1,6 +1,6 @@ require 'simplecov' require 'rubygems' -#require 'spork' +require 'spork' require 'omniauth' #uncomment the following line to use spork with the debugger #require 'spork/ext/ruby-debug' @@ -33,23 +33,39 @@ ActionMailer::Base.delivery_method = :test RecordedTrack.observers.disable :all # only a few tests want this observer active -#Spork.prefork do +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' require File.expand_path("../../config/environment", __FILE__) require 'rspec/rails' - #require 'rspec/autorun' + require 'rspec/autorun' require 'capybara' require 'capybara/rspec' require 'capybara-screenshot/rspec' require 'capybara/poltergeist' + Capybara.register_driver :poltergeist do |app| + driver = Capybara::Poltergeist::Driver.new(app, { debug: false, phantomjs_logger: File.open('log/phantomjs.out', 'w') }) + end + Capybara.javascript_driver = :poltergeist + Capybara.default_wait_time = 10 - - + if defined?(TEST_CONNECT_STATES) && TEST_CONNECT_STATES + TEST_CONNECT_STATE_JS_CONSOLE_IO = File.open(TEST_CONNECT_STATE_JS_CONSOLE, 'w') + Capybara.register_driver :poltergeist do |app| + Capybara::Poltergeist::Driver.new(app, { phantomjs_logger: TEST_CONNECT_STATE_JS_CONSOLE_IO }) + end + Capybara.javascript_driver = :poltergeist + end + Capybara.configure do |config| + config.match = :one + config.exact_options = true + config.ignore_hidden_elements = true + config.visible_text_only = true + end # Requires supporting ruby files with custom matchers and macros, etc, # in spec/support/ and its subdirectories. @@ -58,25 +74,6 @@ RecordedTrack.observers.disable :all # only a few tests want this observer activ ActiveRecord::Base.logger = Logger.new(STDOUT) if defined?(ActiveRecord::Base) RSpec.configure do |config| - Capybara.register_driver :poltergeist do |app| - driver = Capybara::Poltergeist::Driver.new(app, { debug: false, phantomjs_logger: File.open('log/phantomjs.out', 'w') }) - end - Capybara.javascript_driver = :poltergeist - Capybara.default_wait_time = 10 - - Capybara.configure do |config| - config.match = :one - config.exact_options = true - config.ignore_hidden_elements = true - config.visible_text_only = true - config.run_server = false - end - - Capybara.server do |app, port| - raise "gaaag" - require 'rack/handler/puma' - Rack::Handler::Puma.run(app, :Port => port) - end # ## Mock Framework # # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line: @@ -139,8 +136,8 @@ RecordedTrack.observers.disable :all # only a few tests want this observer activ # dump response.body if an example fails if example.metadata[:type] == :controller && example.exception - puts "'#{determine_test_name(example.metadata)}' controller test failed." - puts "response.status = #{response.status}, response.body = " + response.body + puts "'#{determine_test_name(example.metadata)}' controller test failed." + puts "response.status = #{response.status}, response.body = " + response.body end end @@ -152,12 +149,12 @@ RecordedTrack.observers.disable :all # only a few tests want this observer activ wipe_s3_test_bucket end end -#end +end -#Spork.each_run do +Spork.each_run do # This code will be run each time you run your specs. -#end +end From e8e224dbaa0eafc90f28173d67c33231c72458ca Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Thu, 20 Feb 2014 08:38:36 -0500 Subject: [PATCH 20/21] VRFS-1189 music session playback --- web/app/views/music_sessions/show.html.erb | 41 +++++++++++++++++++--- web/app/views/recordings/show.html.erb | 9 ++--- 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/web/app/views/music_sessions/show.html.erb b/web/app/views/music_sessions/show.html.erb index 74ae13253..4b997877e 100644 --- a/web/app/views/music_sessions/show.html.erb +++ b/web/app/views/music_sessions/show.html.erb @@ -52,10 +52,13 @@
    - <%= image_tag "content/icon_playbutton.png", {:width => 20, :height => 20, :alt => ""} %> + <%= image_tag "content/icon_playbutton.png", {:id => "imgPlayPause", :width => 20, :height => 20, :alt => ""} %> <% if @music_session.session_removed_at.blank? %>
    SESSION IN PROGRESS
    -
    1:23
    +
    + <% else %>
    LIVE SESSION ENDED
    <% end %> @@ -99,8 +102,38 @@ <% content_for :extra_js do %> <% end %> diff --git a/web/app/views/recordings/show.html.erb b/web/app/views/recordings/show.html.erb index 1eef34c11..ceb08a75f 100644 --- a/web/app/views/recordings/show.html.erb +++ b/web/app/views/recordings/show.html.erb @@ -51,7 +51,7 @@
    <%= @claimed_recording.description %>

    - <%= image_tag "content/icon_playbutton.png", {:id => "imgPlayPause", :width => 20, :height => 20, :alt => ""} %> + <%= image_tag "content/icon_playbutton.png", {:id => "imgPlayPause", :width => 20, :height => 20, :alt => ""} %>
    0:00
    @@ -102,6 +102,10 @@ <% content_for :extra_js do %>