diff --git a/Gemfile b/Gemfile index 5165996b1..14074582d 100644 --- a/Gemfile +++ b/Gemfile @@ -18,8 +18,10 @@ gem 'jam_db', :path => "#{workspace}/jam-db/target/ruby_package" gem 'jam_ruby', :path => "#{workspace}/jam-ruby" gem 'jampb', :path => "#{workspace}/jam-pb/target/ruby/jampb" gem 'pg', '0.14.0' -gem 'gon' gem 'compass-rails' +gem 'rabl' # for JSON API development +gem 'gon' # for passthrough of Ruby variables to Javascript variables + group :development, :test do gem 'rspec-rails', '2.11.0' @@ -42,6 +44,7 @@ group :test do gem 'guard-spork', '0.3.2' gem 'spork', '0.9.0' gem 'launchy', '2.1.0' + gem 'rack-test' # gem 'rb-fsevent', '0.9.1', :require => false # gem 'growl', '1.0.3' end diff --git a/Gemfile.lock b/Gemfile.lock index 2708c2e65..a62a9655c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -141,6 +141,9 @@ GEM pg (= 0.14.0) thor (= 0.15.4) polyglot (0.3.3) + rabl (0.7.2) + activesupport (>= 2.3.14) + multi_json (~> 1.0) rack (1.4.1) rack-cache (1.2) rack (>= 0.4) @@ -239,6 +242,8 @@ DEPENDENCIES launchy (= 2.1.0) pg (= 0.14.0) pg_migrate (= 0.1.5) + rabl + rack-test rails (= 3.2.8) rspec-rails (= 2.11.0) ruby-protocol-buffers (= 1.2.2) diff --git a/app/assets/javascripts/jamsocket.js b/app/assets/javascripts/jamsocket.js index c1357d50d..ce482160c 100644 --- a/app/assets/javascripts/jamsocket.js +++ b/app/assets/javascripts/jamsocket.js @@ -51,7 +51,7 @@ if(msg.type == LOGIN_ACK) { // we are in... sign in to jam session - var login_jam = mf.login_jam_session(gon.jam_session_id) + var login_jam = mf.login_music_session(gon.music_session_id) send(login_jam) } }; diff --git a/app/assets/javascripts/message_factory.js b/app/assets/javascripts/message_factory.js index 5d8dc8ea7..046fb0141 100644 --- a/app/assets/javascripts/message_factory.js +++ b/app/assets/javascripts/message_factory.js @@ -10,11 +10,11 @@ LOGIN = "LOGIN" LOGIN_ACK = "LOGIN_ACK" - LOGIN_JAM_SESSION = "LOGIN_JAM_SESSION" - LOGIN_JAM_SESSION_ACK = "LOGIN_JAM_SESSION_ACK" - USER_JOINED_JAM_SESSION = "USER_JOINED_JAM_SESSION" - LEAVE_JAM_SESSION = "LEAVE_JAM_SESSION" - LEAVE_JAM_SESSION_ACK = "LEAVE_JAM_SESSION_ACK" + LOGIN_MUSIC_SESSION = "LOGIN_MUSIC_SESSION" + LOGIN_MUSIC_SESSION_ACK = "LOGIN_MUSIC_SESSION_ACK" + USER_JOINED_MUSIC_SESSION = "USER_JOINED_MUSIC_SESSION" + LEAVE_MUSIC_SESSION = "LEAVE_MUSIC_SESSION" + LEAVE_MUSIC_SESSION_ACK = "LEAVE_MUSIC_SESSION_ACK" HEARTBEAT = "HEARTBEAT" TEST_SESSION_MESSAGE = "TEST_SESSION_MESSAGE" @@ -42,10 +42,10 @@ return client_container(LOGIN, SERVER_TARGET, login) } - // create a jam session login message - message_factory.login_jam_session = function(jam_session) { - login_jam_session = { jam_session : jam_session } - return client_container(LOGIN_JAM_SESSION, SERVER_TARGET, login_jam_session) + // create a music session login message + message_factory.login_music_session = function(music_session) { + login_music_session = { music_session : music_session } + return client_container(LOGIN_MUSIC_SESSION, SERVER_TARGET, login_music_session) } window.message_factory = message_factory diff --git a/app/assets/stylesheets/common.css.scss b/app/assets/stylesheets/client/common.css.scss similarity index 100% rename from app/assets/stylesheets/common.css.scss rename to app/assets/stylesheets/client/common.css.scss diff --git a/app/assets/stylesheets/ie.css.scss b/app/assets/stylesheets/client/ie.css.scss similarity index 96% rename from app/assets/stylesheets/ie.css.scss rename to app/assets/stylesheets/client/ie.css.scss index 7311ed60a..6e6016af1 100644 --- a/app/assets/stylesheets/ie.css.scss +++ b/app/assets/stylesheets/client/ie.css.scss @@ -5,7 +5,7 @@ * */ @import "compass/css3/images"; -@import "common.css.scss"; +@import "client/common.css.scss"; /* Gradients in IE work with filter-gradient, but mess up event handling. * Using solid colors via background-color for now. diff --git a/app/assets/stylesheets/jamkazam.css.scss b/app/assets/stylesheets/client/jamkazam.css.scss similarity index 99% rename from app/assets/stylesheets/jamkazam.css.scss rename to app/assets/stylesheets/client/jamkazam.css.scss index 92d8f0d7c..bd5153887 100644 --- a/app/assets/stylesheets/jamkazam.css.scss +++ b/app/assets/stylesheets/client/jamkazam.css.scss @@ -8,7 +8,7 @@ @import "compass/css3/images"; @import "compass/css3/background-size"; -@import "common.css.scss"; +@import "client/common.css.scss"; body { color: $text; diff --git a/app/assets/stylesheets/client/lato.css.erb b/app/assets/stylesheets/client/lato.css.erb new file mode 100644 index 000000000..0069d53a4 --- /dev/null +++ b/app/assets/stylesheets/client/lato.css.erb @@ -0,0 +1,117 @@ +@font-face { + font-family: 'LatoBlackItalic'; + src: url('<%= asset_path('lato/Lato-BlaIta-webfont.eot') %>'); + src: url('<%= asset_path('lato/Lato-BlaIta-webfont.eot?#iefix') %>') format('embedded-opentype'), + url('<%= asset_path('lato/Lato-BlaIta-webfont.woff') %>') format('woff'), + url('<%= asset_path('lato/Lato-BlaIta-webfont.ttf') %>') format('truetype'), + url('<%= asset_path('lato/Lato-BlaIta-webfont.svg#LatoBlackItalic') %>') format('svg'); + font-weight: normal; + font-style: normal; + +} + +@font-face { + font-family: 'LatoBlack'; + src: url('<%= asset_path('lato/Lato-Bla-webfont.eot') %>'); + src: url('<%= asset_path('lato/Lato-Bla-webfont.eot?#iefix') %>') format('embedded-opentype'), + url('<%= asset_path('lato/Lato-Bla-webfont.woff') %>') format('woff'), + url('<%= asset_path('lato/Lato-Bla-webfont.ttf') %>') format('truetype'), + url('<%= asset_path('lato/Lato-Bla-webfont.svg#LatoBlack') %>') format('svg'); + font-weight: normal; + font-style: normal; + +} +/** +@font-face { + font-family: 'LatoBoldItalic'; + src: url('<%= asset_path('lato/Lato-BolIta-webfont.eot') %>'); + src: url('<%= asset_path('lato/Lato-BolIta-webfont.eot?#iefix') %>') format('embedded-opentype'), + url('<%= asset_path('lato/Lato-BolIta-webfont.woff') %>') format('woff'), + url('<%= asset_path('lato/Lato-BolIta-webfont.ttf') %>') format('truetype'), + url('<%= asset_path('lato/Lato-BolIta-webfont.svg#LatoBoldItalic') %>') format('svg'); + font-weight: normal; + font-style: normal; + + } + */ + +@font-face { + font-family: 'LatoBold'; + src: url('<%= asset_path('lato/Lato-Bol-webfont.eot') %>'); + src: url('<%= asset_path('lato/Lato-Bol-webfont.eot?#iefix') %>') format('embedded-opentype'), + url('<%= asset_path('lato/Lato-Bol-webfont.woff') %>') format('woff'), + url('<%= asset_path('lato/Lato-Bol-webfont.ttf') %>') format('truetype'), + url('<%= asset_path('lato/Lato-Bol-webfont.svg#LatoBold') %>') format('svg'); + font-weight: normal; + font-style: normal; + +} + +@font-face { + font-family: 'LatoItalic'; + src: url('<%= asset_path('lato/Lato-RegIta-webfont.eot') %>'); + src: url('<%= asset_path('lato/Lato-RegIta-webfont.eot?#iefix') %>') format('embedded-opentype'), + url('<%= asset_path('lato/Lato-RegIta-webfont.woff') %>') format('woff'), + url('<%= asset_path('lato/Lato-RegIta-webfont.ttf') %>') format('truetype'), + url('<%= asset_path('lato/Lato-RegIta-webfont.svg#LatoItalic') %>') format('svg'); + font-weight: normal; + font-style: normal; + +} + +@font-face { + font-family: 'LatoRegular'; + src: url('<%= asset_path('lato/Lato-Reg-webfont.eot') %>'); + src: url('<%= asset_path('lato/Lato-Reg-webfont.eot?#iefix') %>') format('embedded-opentype'), + url('<%= asset_path('lato/Lato-Reg-webfont.woff') %>') format('woff'), + url('<%= asset_path('lato/Lato-Reg-webfont.ttf') %>') format('truetype'), + url('<%= asset_path('lato/Lato-Reg-webfont.svg#LatoRegular') %>') format('svg'); + font-weight: normal; + font-style: normal; + +} +@font-face { + font-family: 'LatoLightItalic'; + src: url('<%= asset_path('lato/Lato-LigIta-webfont.eot') %>'); + src: url('<%= asset_path('lato/Lato-LigIta-webfont.eot?#iefix') %>') format('embedded-opentype'), + url('<%= asset_path('lato/Lato-LigIta-webfont.woff') %>') format('woff'), + url('<%= asset_path('lato/Lato-LigIta-webfont.ttf') %>') format('truetype'), + url('<%= asset_path('lato/Lato-LigIta-webfont.svg#LatoLightItalic') %>') format('svg'); + font-weight: normal; + font-style: normal; + +} +@font-face { + font-family: 'LatoLight'; + src: url('<%= asset_path('lato/Lato-Lig-webfont.eot') %>'); + src: url('<%= asset_path('lato/Lato-Lig-webfont.eot?#iefix') %>') format('embedded-opentype'), + url('<%= asset_path('lato/Lato-Lig-webfont.woff') %>') format('woff'), + url('<%= asset_path('lato/Lato-Lig-webfont.ttf') %>') format('truetype'), + url('<%= asset_path('lato/Lato-Lig-webfont.svg#LatoLight') %>') format('svg'); + font-weight: normal; + font-style: normal; + +} +@font-face { + font-family: 'LatoHairlineItalic'; + src: url('<%= asset_path('lato/Lato-HaiIta-webfont.eot') %>'); + src: url('<%= asset_path('lato/Lato-HaiIta-webfont.eot?#iefix') %>') format('embedded-opentype'), + url('<%= asset_path('lato/Lato-HaiIta-webfont.woff') %>') format('woff'), + url('<%= asset_path('lato/Lato-HaiIta-webfont.ttf') %>') format('truetype'), + url('<%= asset_path('lato/Lato-HaiIta-webfont.svg#LatoHairlineItalic') %>') format('svg'); + font-weight: normal; + font-style: normal; + +} + +@font-face { + font-family: 'LatoHairline'; + src: url('<%= asset_path('lato/Lato-Hai-webfont.eot') %>'); + src: url('<%= asset_path('lato/Lato-Hai-webfont.eot?#iefix') %>') format('embedded-opentype'), + url('<%= asset_path('lato/Lato-Hai-webfont.woff') %>') format('woff'), + url('<%= asset_path('lato/Lato-Hai-webfont.ttf') %>') format('truetype'), + url('<%= asset_path('lato/Lato-Hai-webfont.svg#LatoHairline') %>') format('svg'); + font-weight: normal; + font-style: normal; + +} diff --git a/app/controllers/jam_sessions_controller.rb b/app/controllers/jam_sessions_controller.rb deleted file mode 100644 index ca44c4d18..000000000 --- a/app/controllers/jam_sessions_controller.rb +++ /dev/null @@ -1,47 +0,0 @@ -class JamSessionsController < ApplicationController - - # have to be signed in currently to see this screen - before_filter :signed_in_user - - def index - @jam_sessions = JamSession.paginate(page: params[:page]) - end - - def show - @jam_session = JamSession.find(params[:id]) - - # use gon to pass variables into javascript - gon.websocket_gateway_uri = Rails.application.config.websocket_gateway_uri - gon.jam_session_id = @jam_session.id - end - - def new - @jam_session = JamSession.new - end - - def create - @jam_session = JamSession.new() - @jam_session.creator = current_user - @jam_session.name = params[:jam_ruby_jam_session][:name] - if @jam_session.save - flash[:success] = "Jam Session created" - redirect_to @jam_session - else - render 'new' - end - end - - def edit - end - - def update - - end - - def destroy - JamSession.find(params[:id]).destroy - flash[:success] = "Jam Session deleted." - redirect_to jam_sessions_url - end - -end diff --git a/app/controllers/music_sessions_controller.rb b/app/controllers/music_sessions_controller.rb new file mode 100644 index 000000000..74cff92a4 --- /dev/null +++ b/app/controllers/music_sessions_controller.rb @@ -0,0 +1,72 @@ +class MusicSessionsController < ApplicationController + + # have to be signed in currently to see this screen + before_filter :signed_in_user + + respond_to :html, :xml, :json + + + def api_index + @music_sessions = MusicSession.paginate(page: params[:page]) + end + + def api_create + @music_session = MusicSession.new() + @music_session.creator = current_user + @music_session.description = params[:description] + @music_session.save + respond_with @music_session, responder: ApiResponder, :location => api_session_detail_url(@music_session) + end + + def api_show + @music_session = MusicSession.find(params[:id]) + end + + def api_member_create + @music_session = MusicSession.find(params[:id]) + + MusicSessionClient.ip_address = params[:ip_address] + end + + def index + @music_sessions = MusicSession.paginate(page: params[:page]) + end + + def show + + # use gon to pass variables into javascript + gon.websocket_gateway_uri = Rails.application.config.websocket_gateway_uri + gon.music_session_id = @music_session.id + end + + def new + @music_session = MusicSession.new + end + + def create + @music_session = MusicSession.new() + @music_session.creator = current_user + @music_session.description = params[:jam_ruby_music_session][:description] + if @music_session.save + flash[:success] = "Music Session created" + redirect_to @music_session + else + render 'new' + end + end + + + def edit + end + + def update + + end + + def destroy + MusicSession.find(params[:id]).destroy + flash[:success] = "Jam Session deleted." + redirect_to music_sessions_url + end + +end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index fd5a6dd0f..61f5c8078 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -8,7 +8,7 @@ class SessionsController < ApplicationController user = User.find_by_email(params[:session][:email]) if user && user.authenticate(params[:session][:password]) sign_in user - redirect_back_or jam_sessions_url + redirect_back_or music_sessions_url else flash.now[:error] = 'Invalid email/password combination' render 'new' diff --git a/app/responders/api_responder.rb b/app/responders/api_responder.rb new file mode 100644 index 000000000..1d25f9c4a --- /dev/null +++ b/app/responders/api_responder.rb @@ -0,0 +1,14 @@ +class ApiResponder < ActionController::Responder + def to_format + case + when has_errors? + controller.response.status = :unprocessable_entity + when post? + controller.response.status = :created + end + + default_render + rescue ActionView::MissingTemplate => e + api_behavior(e) + end +end diff --git a/app/views/jam_ruby/jam_sessions/_jam_session.html.erb b/app/views/jam_ruby/jam_sessions/_jam_session.html.erb deleted file mode 100644 index d51a0d770..000000000 --- a/app/views/jam_ruby/jam_sessions/_jam_session.html.erb +++ /dev/null @@ -1,7 +0,0 @@ -
Wait a moment...
diff --git a/config/initializers/rabl_init.rb b/config/initializers/rabl_init.rb new file mode 100644 index 000000000..5d9ac3cde --- /dev/null +++ b/config/initializers/rabl_init.rb @@ -0,0 +1,20 @@ +Rabl.configure do |config| + # Commented as these are defaults + # config.cache_all_output = false + # config.cache_sources = Rails.env != 'development' # Defaults to false + # config.cache_engine = Rabl::CacheEngine.new # Defaults to Rails cache + # config.escape_all_output = false + # config.json_engine = nil # Any multi_json engines or a Class with #encode method + # config.msgpack_engine = nil # Defaults to ::MessagePack + # config.bson_engine = nil # Defaults to ::BSON + # config.plist_engine = nil # Defaults to ::Plist::Emit + config.include_json_root = false + # config.include_msgpack_root = true + # config.include_bson_root = true + # config.include_plist_root = true + # config.include_xml_root = false + # config.include_child_root = true + # config.enable_json_callbacks = false + # config.xml_options = { :dasherize => true, :skip_types => false } + # config.view_paths = [] +end \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index d3ceb3b4c..ff861752e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -2,11 +2,11 @@ SampleApp::Application.routes.draw do scope :as => 'jam_ruby' do resources :users - resources :jam_sessions + resources :music_sessions end resources :users - resources :jam_sessions + resources :music_sessions resources :sessions, only: [:new, :create, :destroy] @@ -22,60 +22,9 @@ SampleApp::Application.routes.draw do match '/client', to: 'clients#index' - # The priority is based upon order of creation: - # first created -> highest priority. - - # Sample of regular route: - # match 'products/:id' => 'catalog#view' - # Keep in mind you can assign values other than :controller and :action - - # Sample of named route: - # match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase - # This route can be invoked with purchase_url(:id => product.id) - - # Sample resource route (maps HTTP verbs to controller actions automatically): - # resources :products - - # Sample resource route with options: - # resources :products do - # member do - # get 'short' - # post 'toggle' - # end - # - # collection do - # get 'sold' - # end - # end - - # Sample resource route with sub-resources: - # resources :products do - # resources :comments, :sales - # resource :seller - # end - - # Sample resource route with more complex sub-resources - # resources :products do - # resources :comments - # resources :sales do - # get 'recent', :on => :collection - # end - # end - - # Sample resource route within a namespace: - # namespace :admin do - # # Directs /admin/products/* to Admin::ProductsController - # # (app/controllers/admin/products_controller.rb) - # resources :products - # end - - # You can have the root of your site routed with "root" - # just remember to delete public/index.html. - # root :to => 'welcome#index' - - # See how all your routes lay out with "rake routes" - - # This is a legacy wild controller route that's not recommended for RESTful applications. - # Note: This route will make all actions in every controller accessible via GET requests. - # match ':controller(/:action(/:id))(.:format)' + scope '/api' do + match '/sessions/:id' => 'music_sessions#api_show', :via => :get, :as => 'api_session_detail' + match '/sessions' => 'music_sessions#api_index', :via => :get + match '/sessions' => 'music_sessions#api_create', :via => :post + end end diff --git a/spec/requests/music_session_pages_spec.rb b/spec/requests/music_session_pages_spec.rb new file mode 100644 index 000000000..568a2dc1e --- /dev/null +++ b/spec/requests/music_session_pages_spec.rb @@ -0,0 +1,38 @@ +require 'spec_helper' + +describe "Music Session API ", :type => :api do + + include Rack::Test::Methods + + subject { page } + + + describe "profile page" do + let(:user) { FactoryGirl.create(:user) } + before do 2 + #sign_in user + post '/sessions', "session[email]" => user.email, "session[password]" => user.password + rack_mock_session.cookie_jar["remember_token"].should == user.remember_token + end + + it "should list no sessions" do + get '/api/sessions.json' + last_response.body.should eql('[]') + end + + it "should create session" do + # create the session + post '/api/sessions.json', '{"description" : "a session"}', "CONTENT_TYPE" => 'application/json' + last_response.status.should eql(201) + + # now fetch it's data + get last_response.headers["Location"] + music_session = JSON.parse(last_response.body) + + get '/api/sessions.json' + list = JSON.parse(last_response.body) + + list[0]["id"].should == music_session["id"] + end + end +end