diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 000000000..15b0d7461 Binary files /dev/null and b/.DS_Store differ diff --git a/.rspec b/.rspec new file mode 100644 index 000000000..4e1e0d2f7 --- /dev/null +++ b/.rspec @@ -0,0 +1 @@ +--color diff --git a/Gemfile b/Gemfile index 6d829a5c8..569cabf53 100644 --- a/Gemfile +++ b/Gemfile @@ -8,6 +8,10 @@ gem 'haml-rails' gem 'sass-rails' gem 'uglifier' gem 'jquery-rails' +gem 'bootstrap_form' +gem 'bcrypt-ruby' +gem 'fabrication' +gem 'faker' group :development do gem 'sqlite3' @@ -16,6 +20,16 @@ group :development do gem 'thin' gem "better_errors" gem "binding_of_caller" + +end + +group :test, :development do + gem 'rspec-rails' +end + +group :test do + gem 'shoulda-matchers' + end group :production do diff --git a/Gemfile.lock b/Gemfile.lock index 219b62049..0a438767d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -27,6 +27,9 @@ GEM tzinfo (~> 0.3.37) arel (4.0.1) atomic (1.1.14) + bcrypt (3.1.7) + bcrypt-ruby (3.1.5) + bcrypt (>= 3.1.3) better_errors (1.0.1) coderay (>= 1.0.0) erubis (>= 2.6.6) @@ -34,6 +37,7 @@ GEM debug_inspector (>= 0.0.1) bootstrap-sass (3.0.2.1) sass (~> 3.2) + bootstrap_form (2.1.1) builder (3.1.4) coderay (1.1.0) coffee-rails (4.0.1) @@ -45,9 +49,13 @@ GEM coffee-script-source (1.6.3) daemons (1.1.9) debug_inspector (0.0.2) + diff-lcs (1.2.5) erubis (2.7.0) eventmachine (1.0.0) execjs (2.0.2) + fabrication (2.11.2) + faker (1.3.0) + i18n (~> 0.5) haml (4.0.4) tilt haml-rails (0.4) @@ -98,11 +106,25 @@ GEM rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) rake (10.1.0) + rspec-core (2.14.8) + rspec-expectations (2.14.5) + diff-lcs (>= 1.1.3, < 2.0) + rspec-mocks (2.14.6) + rspec-rails (2.14.2) + actionpack (>= 3.0) + activemodel (>= 3.0) + activesupport (>= 3.0) + railties (>= 3.0) + rspec-core (~> 2.14.0) + rspec-expectations (~> 2.14.0) + rspec-mocks (~> 2.14.0) sass (3.2.12) sass-rails (4.0.1) railties (>= 4.0.0, < 5.0) sass (>= 3.1.10) sprockets-rails (~> 2.0.0) + shoulda-matchers (2.6.1) + activesupport (>= 3.0.0) slop (3.4.7) sprockets (2.10.0) hike (~> 1.2) @@ -134,10 +156,14 @@ PLATFORMS ruby DEPENDENCIES + bcrypt-ruby better_errors binding_of_caller bootstrap-sass + bootstrap_form coffee-rails + fabrication + faker haml-rails jquery-rails pg @@ -145,7 +171,9 @@ DEPENDENCIES pry-nav rails rails_12factor + rspec-rails sass-rails + shoulda-matchers sqlite3 thin uglifier diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index e8065d950..b6b69c0c2 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,3 +1,13 @@ class ApplicationController < ActionController::Base protect_from_forgery + + def require_user + redirect_to sign_in_path unless current_user + end + + def current_user + User.find(session[:user_id]) if session[:user_id] + end + + helper_method :current_user end diff --git a/app/controllers/categories_controller.rb b/app/controllers/categories_controller.rb new file mode 100644 index 000000000..672444cf6 --- /dev/null +++ b/app/controllers/categories_controller.rb @@ -0,0 +1,10 @@ +class CategoriesController < ApplicationController + + def index + @categories = Category.all + end + + def show + @category = Category.find(params[:id]) + end +end \ No newline at end of file diff --git a/app/controllers/pages_controller.rb b/app/controllers/pages_controller.rb new file mode 100644 index 000000000..e2948a656 --- /dev/null +++ b/app/controllers/pages_controller.rb @@ -0,0 +1,5 @@ +class PagesController < ApplicationController + def front + redirect_to home_path if current_user + end +end \ No newline at end of file diff --git a/app/controllers/queue_items_controller.rb b/app/controllers/queue_items_controller.rb new file mode 100644 index 000000000..6231eda1a --- /dev/null +++ b/app/controllers/queue_items_controller.rb @@ -0,0 +1,34 @@ +class QueueItemsController < ApplicationController + before_filter :require_user + + def index + @queue_items = current_user.queue_items + end + + def create + video = Video.find(params[:video_id]) + queue_video(video) + redirect_to my_queue_path + end + + def destroy + queue_item = QueueItem.find(params[:id]) + queue_item.destroy if current_user.queue_items.include?(queue_item) + redirect_to my_queue_path + end + + private + + def queue_video(video) + QueueItem.create(video: video, user: current_user, position: new_queue_item_position) unless current_user_queued_vide?(video) + end + + def new_queue_item_position + current_user.queue_items.count + 1 + end + + def current_user_queued_vide?(video) + current_user.queue_items.map(&:video).include?(video) + end + +end \ No newline at end of file diff --git a/app/controllers/reviews_controller.rb b/app/controllers/reviews_controller.rb new file mode 100644 index 000000000..0a021508a --- /dev/null +++ b/app/controllers/reviews_controller.rb @@ -0,0 +1,20 @@ +class ReviewsController < ApplicationController + before_filter :require_user + + def create + @video = Video.find(params[:video_id]) + review = @video.reviews.build(review_params.merge!(user: current_user)) + if review.save + redirect_to @video + else + @reviews = @video.reviews.reload + render 'videos/show' + end + end + + private + + def review_params + params.require(:review).permit(:rating, :content) + end +end \ No newline at end of file diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb new file mode 100644 index 000000000..919217711 --- /dev/null +++ b/app/controllers/sessions_controller.rb @@ -0,0 +1,23 @@ +class SessionsController < ApplicationController + def new + redirect_to home_path if current_user + end + + def create + user = User.where(email: params[:email]).first + if user && user.authenticate(params[:password]) + session[:user_id] = user.id + flash[:success] = 'You are signed in, enjoy' + redirect_to home_path + else + flash[:danger] = "Invalid email or password." + redirect_to sign_in_path + end + end + + def destroy + session[:user_id] = nil + flash[:danger] = "You are signed out." + redirect_to root_path + end +end \ No newline at end of file diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb new file mode 100644 index 000000000..7027a97ed --- /dev/null +++ b/app/controllers/users_controller.rb @@ -0,0 +1,20 @@ +class UsersController < ApplicationController + def new + @user = User.new + end + + def create + @user = User.new(user_params) + if @user.save + redirect_to sign_in_path + else + render :new + end + end + + private + + def user_params + params.require(:user).permit(:full_name, :email, :password) + end +end \ No newline at end of file diff --git a/app/controllers/videos_controller.rb b/app/controllers/videos_controller.rb new file mode 100644 index 000000000..34e873503 --- /dev/null +++ b/app/controllers/videos_controller.rb @@ -0,0 +1,17 @@ +class VideosController < ApplicationController + before_filter :require_user + + def index + @videos = Video.all + @categories = Category.all + end + + def show + @video = Video.find(params[:id]) + @reviews = @video.reviews + end + + def search + @results = Video.search_by_title(params[:search_term]) + end +end \ No newline at end of file diff --git a/app/models/category.rb b/app/models/category.rb new file mode 100644 index 000000000..686a7f4b6 --- /dev/null +++ b/app/models/category.rb @@ -0,0 +1,8 @@ +class Category < ActiveRecord::Base + has_many :videos, order: "created_at DESC" + validates_presence_of :name + + def recent_videos + videos.first(6) + end +end diff --git a/app/models/queue_item.rb b/app/models/queue_item.rb new file mode 100644 index 000000000..702d9d016 --- /dev/null +++ b/app/models/queue_item.rb @@ -0,0 +1,16 @@ +class QueueItem < ActiveRecord::Base + belongs_to :user + belongs_to :video + + delegate :category, to: :video + delegate :title, to: :video, prefix: :video + + def rating + review = Review.where(user_id: user_id, video_id: video.id).first + review.rating if review + end + + def category_name + category.name + end +end \ No newline at end of file diff --git a/app/models/review.rb b/app/models/review.rb new file mode 100644 index 000000000..ef2211a3a --- /dev/null +++ b/app/models/review.rb @@ -0,0 +1,6 @@ +class Review < ActiveRecord::Base + belongs_to :video + belongs_to :user + + validates_presence_of :content, :rating +end \ No newline at end of file diff --git a/app/models/user.rb b/app/models/user.rb new file mode 100644 index 000000000..a45d9a238 --- /dev/null +++ b/app/models/user.rb @@ -0,0 +1,8 @@ +class User < ActiveRecord::Base + validates_presence_of :email, :password, :full_name + validates_uniqueness_of :email + + has_secure_password validations: false + + has_many :queue_items +end \ No newline at end of file diff --git a/app/models/video.rb b/app/models/video.rb new file mode 100644 index 000000000..86ee987b8 --- /dev/null +++ b/app/models/video.rb @@ -0,0 +1,13 @@ +class Video < ActiveRecord::Base + belongs_to :category + has_many :reviews, order: "created_at DESC" + #has_many :reviews, -> { order: ("created_at DESC") } + #validates :title, presence: true + #validates :description, presence: true + validates_presence_of :title, :description + + def self.search_by_title(search_term) + return [] if search_term.blank? + where("title LIKE ?", "%#{search_term}%").order("created_at DESC") + end +end \ No newline at end of file diff --git a/app/views/categories/show.html.haml b/app/views/categories/show.html.haml new file mode 100644 index 000000000..588e7b20d --- /dev/null +++ b/app/views/categories/show.html.haml @@ -0,0 +1,14 @@ +%section.genre + %header.clearfix + %h1= @category.name + .genre_sorting + Sort by: + %select(name="") + %option(value="a-z") Title: A - Z + %option(value="z-a") Title: Z - A + %option(value="rating") Rating + %article.row + - @category.videos.each do |video| + .video.col-md-2 + = link_to video_path(video) do + %img(src="#{video.small_cover_url}") \ No newline at end of file diff --git a/app/views/pages/front.html.haml b/app/views/pages/front.html.haml new file mode 100644 index 000000000..a472c5682 --- /dev/null +++ b/app/views/pages/front.html.haml @@ -0,0 +1,8 @@ +%section.site_title + %h1 + Unlimited Movies for Only 9.99 a Month. + = link_to "Sign Up Now!", register_path + %p.sign_up + %p.sign_in + Have an account? Please + = link_to "Sign In", sign_in_path \ No newline at end of file diff --git a/app/views/queue_items/index.html.haml b/app/views/queue_items/index.html.haml new file mode 100644 index 000000000..43911a187 --- /dev/null +++ b/app/views/queue_items/index.html.haml @@ -0,0 +1,38 @@ +%section.my_queue.container + .row + .col-sm-10.col-sm-offset-1 + %article + %header + %h2 My Queue + %table.table + %thead + %tr + %th(width="10%") List Order + %th(width="30%") Video Title + %th(width="10%") Play + %th(width="20%") Rating + %th(width="15%") Genre + %th(width="15%") Remove + %tbody + - @queue_items.each do |queue_item| + %tr + %td= queue_item.position + + %td + = link_to queue_item.video_title, queue_item.video + %td + = button_to "Play", nil, class: "btn btn-default" + %td + %select.form-group(name="") + %option(value="5") 5 Stars + %option(value="4") 4 Stars + %option(value="3") 3 Stars + %option(value="2") 2 Stars + %option(value="1") 1 Star + %td + = link_to queue_item.category_name, queue_item.category + %td + = link_to queue_item, method: :delete do + %i.glyphicon.glyphicon-remove + + = button_to "Update Instant Queue", nil, class: "btn btn-default" diff --git a/app/views/sessions/new.html.haml b/app/views/sessions/new.html.haml new file mode 100644 index 000000000..4e176f007 --- /dev/null +++ b/app/views/sessions/new.html.haml @@ -0,0 +1,13 @@ +%section.sign_in.container + .row + .col-sm-10.col-sm-offset-1 + = form_tag sessions_path, class: 'sign_in' do + %header + %h1 Sign in + %fieldset + = label_tag :email, "Email Address" + = email_field_tag :email + = label_tag :Password + = password_field_tag :password + %fieldset.form-group.action + %input(type="submit" value="Sign in" class="btn btn-default") diff --git a/app/views/shared/_header.html.haml b/app/views/shared/_header.html.haml index 21fbdacfe..25425bdd0 100644 --- a/app/views/shared/_header.html.haml +++ b/app/views/shared/_header.html.haml @@ -1,22 +1,24 @@ %section#top-header.row %h1.col-md-2 - = link_to "MyFLiX" - %ul.col-md-4.clearfix - %li= link_to "Videos" - %li= link_to "My Queue" - %li= link_to "People" - %form.col-md-5.navbar-form(action="") - .form-group - %input.form-control(type="text" placeholder="Search for videos here") - %button.btn.btn-default(type="submit") Search - #user_links.pull-right - %ul - %li.dropdown - %a(href="#" id="dlabel" role="button" data-toggle="dropdown" class="dropdown-toggle" data-target="#") - Welcome, Philip J. Fry - %b.caret - %ul.dropdown-menu(role="menu" aria-labelledby="dlabel") - %li - %a(href="#") Account - %a(href="#") Plan and Billing - %a(href="#") Sign Out + = link_to "MyFLiX", home_path + - if current_user + %ul.col-md-4.clearfix + %li= link_to "Videos" + %li= link_to "My Queue" + %li= link_to "People" + = form_tag search_videos_path, method: :get, class: "form.col-md-5 navbar-form" do + .form-group + %input.form-control(name="search_term" type="text" placeholder="Search for videos here") + %button.btn.btn-default(type="submit") Search + #user_links.pull-right + %ul + %li.dropdown + %a(href="#" id="dlabel" role="button" data-toggle="dropdown" class="dropdown-toggle" data-target="#") + Welcome, #{current_user.full_name} + %b.caret + %ul.dropdown-menu(role="menu" aria-labelledby="dlabel") + %li + %a(href="#") Account + %a(href="#") Plan and Billing + = link_to "Sign Out", sign_out_path + diff --git a/app/views/users/new.html.haml b/app/views/users/new.html.haml new file mode 100644 index 000000000..ff632266e --- /dev/null +++ b/app/views/users/new.html.haml @@ -0,0 +1,13 @@ +%section.register.container + .row + .col-sm-10.col-sm-offset-1 + = bootstrap_form_for @user, html: { class: "form-horizontal" } do |f| + %header + %h1 Register + %fieldset + = f.email_field :email, label: "Email Address" + = f.password_field :password + = f.text_field :full_name, label: "Full Name" + %fieldset.actions.control-group.col-sm-offset-2 + .controls + %input(type="submit" value="Sign Up" class="btn btn-default") diff --git a/app/views/videos/index.html.haml b/app/views/videos/index.html.haml new file mode 100644 index 000000000..acf849a8e --- /dev/null +++ b/app/views/videos/index.html.haml @@ -0,0 +1,9 @@ +%article.video_category + - @categories.each do |category| + %header + %h3= category.name + .videos.row + - category.recent_videos.each do |video| + .video.col-sm-2 + = link_to video do + %img(src="#{video.small_cover_url}") \ No newline at end of file diff --git a/app/views/videos/search.html.haml b/app/views/videos/search.html.haml new file mode 100644 index 000000000..ba2ef2eb1 --- /dev/null +++ b/app/views/videos/search.html.haml @@ -0,0 +1,5 @@ +%article.video_category + - @results.each do |video| + .video.span2 + = link_to video do + %img(src="#{video.small_cover_url}") \ No newline at end of file diff --git a/app/views/videos/show.html.haml b/app/views/videos/show.html.haml new file mode 100644 index 000000000..33f0b9254 --- /dev/null +++ b/app/views/videos/show.html.haml @@ -0,0 +1,43 @@ +%article.video + .container + .row + .video_large_cover.col-sm-7.col-sm-offset-1 + %img(src="#{@video.large_cover_url}") + .video_info.col-sm-3 + %header + %h3= @video.title + %span Rating: 4.5/5.0 + %p= @video.description + .actions + %a.btn.btn-primary(href="") Watch Now + %a.btn.btn-default(href="") + My Queue + = link_to "+ My Queue", queue_items_path(video_id: @video.id), method: :post, class: "btn btn-default" +%section.reviews.container + .row + .col-sm-10.col-sm-offset-1 + = form_for [@video, Review.new] do |f| + %fieldset + .form-group + %label Rate this video + = f.select :rating, options_for_select([5,4,3,2,1].map {|number| [pluralize(number, "Star")]}) + .form-group + %label Write Review + .row + .col-sm-8 + = f.text_area :content, rows: 6, class: "form-control" + %fieldset.form-group.actions.clearfix + %input(type="submit" value="Submit" class="btn") + = link_to "Cancel", @video + %header + %h3 User Reviews (#{@reviews.count}) + %ul + - @reviews.each do |review| + %article.review + %li.row + .col-sm-2 + %span Rating: #{review.rating} / 5 + %p by #{review.user.full_name} + .col-sm-8 + %p= review.content + + \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index 95cc741d1..02048bbb0 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,3 +1,25 @@ Myflix::Application.routes.draw do - get 'ui(/:action)', controller: 'ui' + root to: "pages#front" + get 'home', to: 'videos#index' + + resources :videos, only: [:index, :show] do + collection do + get :search, to: "videos#search" + end + resources :reviews, only: [:create] + end + + resources :categories, only: [:show] + resources :queue_items, only: [:create, :destroy] + + get 'my_queue', to: 'queue_items#index' + + get 'ui(/:action)', controller: 'ui' + get 'register', to: 'users#new' + get 'sign_in', to: 'sessions#new' + get 'sign_out', to: 'sessions#destroy' + + resources :users, only: [:create] + resources :sessions, only: [:create] + end diff --git a/db/migrate/20140519141833_create_videos.rb b/db/migrate/20140519141833_create_videos.rb new file mode 100644 index 000000000..dc2e76394 --- /dev/null +++ b/db/migrate/20140519141833_create_videos.rb @@ -0,0 +1,11 @@ +class CreateVideos < ActiveRecord::Migration + def change + create_table :videos do |t| + t.string :title + t.text :description + t.string :small_cover_url + t.string :large_cover_url + t.timestamps + end + end +end diff --git a/db/migrate/20140519181338_create_categories.rb b/db/migrate/20140519181338_create_categories.rb new file mode 100644 index 000000000..f4b307a06 --- /dev/null +++ b/db/migrate/20140519181338_create_categories.rb @@ -0,0 +1,8 @@ +class CreateCategories < ActiveRecord::Migration + def change + create_table :categories do |t| + t.string :name + t.timestamps + end + end +end diff --git a/db/migrate/20140519190153_add_categoryid_to_videos.rb b/db/migrate/20140519190153_add_categoryid_to_videos.rb new file mode 100644 index 000000000..ffa226819 --- /dev/null +++ b/db/migrate/20140519190153_add_categoryid_to_videos.rb @@ -0,0 +1,5 @@ +class AddCategoryidToVideos < ActiveRecord::Migration + def change + add_column :videos, :category_id, :integer + end +end diff --git a/db/migrate/20140528010226_create_users.rb b/db/migrate/20140528010226_create_users.rb new file mode 100644 index 000000000..e0bf4cfe7 --- /dev/null +++ b/db/migrate/20140528010226_create_users.rb @@ -0,0 +1,10 @@ +class CreateUsers < ActiveRecord::Migration + def change + create_table :users do |t| + t.string :email + t.string :password_digest + t.string :full_name + t.timestamps + end + end +end diff --git a/db/migrate/20140603153159_create_reviews.rb b/db/migrate/20140603153159_create_reviews.rb new file mode 100644 index 000000000..bd2b8c0aa --- /dev/null +++ b/db/migrate/20140603153159_create_reviews.rb @@ -0,0 +1,11 @@ +class CreateReviews < ActiveRecord::Migration + def change + create_table :reviews do |t| + t.integer :user_id + t.integer :video_id + t.text :content + t.integer :rating + t.timestamps + end + end +end diff --git a/db/migrate/20140609145631_create_queue_items.rb b/db/migrate/20140609145631_create_queue_items.rb new file mode 100644 index 000000000..4d453d7fb --- /dev/null +++ b/db/migrate/20140609145631_create_queue_items.rb @@ -0,0 +1,9 @@ +class CreateQueueItems < ActiveRecord::Migration + def change + create_table :queue_items do |t| + t.integer :video_id, :user_id + t.integer :position + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb new file mode 100644 index 000000000..d505afe2b --- /dev/null +++ b/db/schema.rb @@ -0,0 +1,57 @@ +# encoding: UTF-8 +# This file is auto-generated from the current state of the database. Instead +# of editing this file, please use the migrations feature of Active Record to +# incrementally modify your database, and then regenerate this schema definition. +# +# Note that this schema.rb definition is the authoritative source for your +# database schema. If you need to create the application database on another +# system, you should be using db:schema:load, not running all the migrations +# from scratch. The latter is a flawed and unsustainable approach (the more migrations +# you'll amass, the slower it'll run and the greater likelihood for issues). +# +# It's strongly recommended that you check this file into your version control system. + +ActiveRecord::Schema.define(version: 20140609145631) do + + create_table "categories", force: true do |t| + t.string "name" + t.datetime "created_at" + t.datetime "updated_at" + end + + create_table "queue_items", force: true do |t| + t.integer "video_id" + t.integer "user_id" + t.integer "position" + t.datetime "created_at" + t.datetime "updated_at" + end + + create_table "reviews", force: true do |t| + t.integer "user_id" + t.integer "video_id" + t.text "content" + t.integer "rating" + t.datetime "created_at" + t.datetime "updated_at" + end + + create_table "users", force: true do |t| + t.string "email" + t.string "password_digest" + t.string "full_name" + t.datetime "created_at" + t.datetime "updated_at" + end + + create_table "videos", force: true do |t| + t.string "title" + t.text "description" + t.string "small_cover_url" + t.string "large_cover_url" + t.datetime "created_at" + t.datetime "updated_at" + t.integer "category_id" + end + +end diff --git a/db/seeds.rb b/db/seeds.rb index 4edb1e857..dcf89fd9b 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -5,3 +5,13 @@ # # cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }]) # Mayor.create(name: 'Emanuel', city: cities.first) + +Video.create(title: "Futurama", description: "Pizza boy Philip J. Fry awakens in the 31st century after 1,000 years of cryogenic preservation in this animated series.", small_cover_url: "/tmp/futurama.jpg", large_cover_url: "/tmp/futurama.jpg") +Video.create(title: "Family Guy", description: "Story about a family", small_cover_url: "/tmp/family_guy.jpg", large_cover_url: "/tmp/family_guy") +Video.create(title: "South Park", description: "Kids in a park", small_cover_url: "/tmp/south_park.jpg", large_cover_url: "/tmp/south_park.jpg") +Video.create(title: "Monk", description: "Story about a monk detetctive", small_cover_url: "/tmp/monk.jpg", large_cover_url: "/tmp/monk_large") + +Category.create(name: "Comedy") +Category.create(name: "Drama") +Category.create(name: "Reality") + diff --git a/spec/controllers/queue_items_controller_spec.rb b/spec/controllers/queue_items_controller_spec.rb new file mode 100644 index 000000000..9db17eaec --- /dev/null +++ b/spec/controllers/queue_items_controller_spec.rb @@ -0,0 +1,99 @@ +require 'spec_helper' + +describe QueueItemsController do + describe "GET index" do + it "sets @queue_items to the queue items of the logged in user" do + alice = Fabricate(:user) + session[:user_id] = alice.id + queue_item1 = Fabricate(:queue_item, user: alice) + queue_item2 = Fabricate(:queue_item, user: alice) + get :index + expect(assigns(:queue_items)).to match_array([queue_item1, queue_item2]) + end + + it "redirects to the sign in page for unauthenticated users" do + get :index + expect(response).to redirect_to sign_in_path + end + end + describe "POST create" do + it "redirects to the my queue page" do + session[:user_id] = Fabricate(:user).id + video = Fabricate(:video) + post :create, video_id: video.id + expect(response).to redirect_to my_queue_path + end + it "creates a queue item" do + session[:user_id] = Fabricate(:user).id + video = Fabricate(:video) + post :create, video_id: video.id + expect(QueueItem.count).to eq(1) + end + it "creates the queue item that is associated with the video" do + session[:user_id] = Fabricate(:user).id + video = Fabricate(:video) + post :create, video_id: video.id + expect(QueueItem.first.video).to eq(video) + end + + it "creates the queue item that is associated with the sign in user" do + alice = Fabricate(:user) + session[:user_id] = alice.id + video = Fabricate(:video) + post :create, video_id: video.id + expect(QueueItem.first.user).to eq(alice) + end + + it "puts the vide as the last one in the queue" do + alice = Fabricate(:user) + session[:user_id] = alice.id + monk = Fabricate(:video) + Fabricate(:queue_item, video: monk, user: alice) + south_park = Fabricate(:video) + post :create, video_id: south_park.id + south_park_queue_item = QueueItem.where(video_id: south_park.id, user_id: alice.id).first + expect(south_park_queue_item.position).to eq(2) + end + it "does not add the video in the queue if the video is already in the queue" do + alice = Fabricate(:user) + session[:user_id] = alice.id + monk = Fabricate(:video) + Fabricate(:queue_item, video: monk, user: alice) + post :create, video_id: monk.id + expect(alice.queue_items.count).to eq(1) + end + it "redirects to the sign in page for unauthenticated users" do + post :create, video_id: 3 + expect(response).to redirect_to sign_in_path + end + end + + describe "DELETE destroy" do + it "redirects to the my queue page" do + session[:user_id] = Fabricate(:user).id + queue_item = Fabricate(:queue_item) + delete :destroy, id: queue_item.id + expect(response).to redirect_to my_queue_path + end + it "deletes the queue item" do + alice = Fabricate(:user) + session[:user_id] = alice.id + queue_item = Fabricate(:queue_item, user: alice) + delete :destroy, id: queue_item.id + expect(QueueItem.count).to eq(0) + end + it "does not delete the queue item if the queue item is not in the current user's queue" do + alice = Fabricate(:user) + bob = Fabricate(:user) + session[:user_id] = alice.id + queue_item = Fabricate(:queue_item, user: bob) + delete :destroy, id: queue_item.id + expect(QueueItem.count).to eq(1) + + end + it "redirects to the sign in page for unauthenticated users" do + delete :destroy, id: 3 + expect(response).to redirect_to sign_in_path + end + end +end \ No newline at end of file diff --git a/spec/controllers/reviews_controller_spec.rb b/spec/controllers/reviews_controller_spec.rb new file mode 100644 index 000000000..2474d76af --- /dev/null +++ b/spec/controllers/reviews_controller_spec.rb @@ -0,0 +1,62 @@ +require 'spec_helper' + +describe ReviewsController do + describe "POST create" do + let(:video) { Fabricate(:video) } + + context "with authenticated users" do + + let(:current_user) { Fabricate(:user) } + before { session[:user_id] = current_user.id } + + context "with valid inputs" do + before do + post :create, review: Fabricate.attributes_for(:review), video_id: video.id + end + it "redirects to the video show page" do + + expect(response).to redirect_to video + end + it "creates a review" do + + expect(Review.count).to eq(1) + end + it "creates a review associated with the video" do + + expect(Review.first.video).to eq(video) + end + it "creates a review associated with the signed in user" do + + expect(Review.first.user).to eq(current_user) + end + + end + context "with invalid inputs" do + it "does not create a review" do + post :create, review: {rating: 4}, video_id: video.id + expect(Review.count).to eq(0) + end + it "renders the videos/show template" do + post :create, review: {rating: 4}, video_id: video.id + expect(response).to render_template "videos/show" + end + + it "sets @video" do + post :create, review: {rating: 4}, video_id: video.id + expect(assigns(:video)).to eq(video) + end + it "sets @reviews" do + review = Fabricate(:review, video: video) + post :create, review: {rating: 4}, video_id: video.id + expect(assigns(:reviews)).to match_array([review]) + end + end + end + context "with unauthenticated users" do + it "redirects to the sign in path" do + post :create, review: Fabricate.attributes_for(:review), video_id: video.id + expect(response).to redirect_to sign_in_path + end + end + end +end \ No newline at end of file diff --git a/spec/controllers/sessions_controller_spec.rb b/spec/controllers/sessions_controller_spec.rb new file mode 100644 index 000000000..f9c714731 --- /dev/null +++ b/spec/controllers/sessions_controller_spec.rb @@ -0,0 +1,71 @@ +require 'spec_helper' + +describe SessionsController do + describe "GET new" do + it "renders the new template for unauthenticated users" do + get :new + expect(response).to render_template :new + end + it "redirects to the home page for authenticated users" do + session[:user_id] = Fabricate(:user).id + get :new + expect(response).to redirect_to home_path + end + end + + describe "POST create" do + context "with valid credentials" do + before do + alice = Fabricate(:user) + post :create, email: alice.email, password: alice.password + end + + it "puts the signed in user in the session" do + alice = Fabricate(:user) + post :create, email: alice.email, password: alice.password + expect(session[:user_id]).to eq(alice.id) + end + + it "redirects to the home page" do + expect(response).to redirect_to home_path + end + + it "sets the notice" do + expect(flash[:success]).not_to be_blank + end + end + + context "with invalid credentials" do + before do + alice = Fabricate(:user) + post :create, email: alice.email, password: alice.password + 'qwerty' + end + it "does not put the signed in user in the session" do + expect(session[:user_id]).to be_nil + end + it "redirects to the sign in page" do + expect(response).to redirect_to sign_in_path + end + it "sets the error message" do + expect(flash[:danger]).not_to be_blank + end + end + end + + describe "GET destroy" do + before do + session[:user_id] = Fabricate(:user).id + get :destroy + end + + it "clears the session for the user" do + expect(session[:user_id]).to be_nil + end + it "redirects to the root path" do + expect(response).to redirect_to root_path + end + it "sets the notice" do + expect(flash[:danger]).not_to be_blank + end + end +end \ No newline at end of file diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb new file mode 100644 index 000000000..3b74115ac --- /dev/null +++ b/spec/controllers/users_controller_spec.rb @@ -0,0 +1,39 @@ +require 'spec_helper' + +describe UsersController do + describe "GET new" do + it "sets @user" do + get :new + expect(assigns(:user)).to be_instance_of(User) + end + end + describe "POST create" do + context "with valid input" do + before do + post :create, user: Fabricate.attributes_for(:user) + end + it "creates the user" do + expect(User.count).to eq(1) + end + it "redirects to the sign in page" do + expect(response).to redirect_to sign_in_path + end + end + context "with invalid input" do + before do + post :create, user: { password: "password", full_name: "Chuck Norris"} + end + it "does not create the user" do + expect(User.count).to eq(0) + end + + it "render the :new template" do + expect(response).to render_template :new + end + + it "sets @user" do + expect(assigns(:user)).to be_instance_of(User) + end + end + end +end diff --git a/spec/controllers/videos_controller_spec.rb b/spec/controllers/videos_controller_spec.rb new file mode 100644 index 000000000..e3099c8e8 --- /dev/null +++ b/spec/controllers/videos_controller_spec.rb @@ -0,0 +1,42 @@ +require 'spec_helper' + +describe VideosController do + describe "GET show" do + it "sets @video for authenticated users" do + session[:user_id] = Fabricate(:user).id + video = Fabricate(:video) + get :show, id: video.id + expect(assigns(:video)).to eq(video) + end + + it "sets @reviews for authenticated users" do + session[:user_id] = Fabricate(:user).id + video = Fabricate(:video) + review1 = Fabricate(:review, video: video) + review2 = Fabricate(:review, video: video) + get :show, id: video.id + expect(assigns(:reviews)).to match_array([review1, review2]) + + end + + it "redirects the user to the sign in page for unauthenticated users" do + video = Fabricate(:video) + get :show, id: video.id + expect(response).to redirect_to sign_in_path + end + end + + describe "POST search" do + it "sets @results for authenticated users" do + session[:user_id] = Fabricate(:user).id + futurama = Fabricate(:video, title: 'Futurama') + post :search, search_term: 'rama' + expect(assigns(:results)).to eq([futurama]) + end + it "redirects to the sign in page for the unauthenticated users" do + futurama = Fabricate(:video, title: 'Futurama') + post :search, search_term: 'rama' + expect(response).to redirect_to sign_in_path + end + end +end \ No newline at end of file diff --git a/spec/fabricators/category_fabricator.rb b/spec/fabricators/category_fabricator.rb new file mode 100644 index 000000000..fb3ef3696 --- /dev/null +++ b/spec/fabricators/category_fabricator.rb @@ -0,0 +1,3 @@ +Fabricator(:category) do + name { Faker::Lorem.words(1) } +end \ No newline at end of file diff --git a/spec/fabricators/queue_item_fabricator.rb b/spec/fabricators/queue_item_fabricator.rb new file mode 100644 index 000000000..be82b7d0a --- /dev/null +++ b/spec/fabricators/queue_item_fabricator.rb @@ -0,0 +1,2 @@ +Fabricator(:queue_item) do +end \ No newline at end of file diff --git a/spec/fabricators/review_fabricator.rb b/spec/fabricators/review_fabricator.rb new file mode 100644 index 000000000..72884b607 --- /dev/null +++ b/spec/fabricators/review_fabricator.rb @@ -0,0 +1,4 @@ +Fabricator(:review) do + rating { (1..5).to_a.sample } + content { Faker::Lorem.paragraph(3) } +end \ No newline at end of file diff --git a/spec/fabricators/user_fabricator.rb b/spec/fabricators/user_fabricator.rb new file mode 100644 index 000000000..b5a9be89e --- /dev/null +++ b/spec/fabricators/user_fabricator.rb @@ -0,0 +1,5 @@ +Fabricator(:user) do + email { Faker::Internet.email } + password 'password' + full_name { Faker::Name.name} +end \ No newline at end of file diff --git a/spec/fabricators/video_fabricator.rb b/spec/fabricators/video_fabricator.rb new file mode 100644 index 000000000..c68966596 --- /dev/null +++ b/spec/fabricators/video_fabricator.rb @@ -0,0 +1,4 @@ +Fabricator(:video) do + title { Faker::Lorem.words(5).join(" ") } + description { Faker::Lorem.paragraph(2) } +end \ No newline at end of file diff --git a/spec/models/category_spec.rb b/spec/models/category_spec.rb new file mode 100644 index 000000000..70f0252f3 --- /dev/null +++ b/spec/models/category_spec.rb @@ -0,0 +1,36 @@ +require 'spec_helper' + +describe Category do + it { should have_many(:videos)} + it { should validate_presence_of(:name) } + + describe "#recent_videos" do + it "returns the videos in the reverse chronical order by created at" do + comedies = Category.create(name: "comedies") + futurama = Video.create(title: "Futurama", description: "space travel", category: comedies, created_at: 1.day.ago) + south_park = Video.create(title: "South Park", description: "crazy kids!", category: comedies) + expect(comedies.recent_videos).to eq([south_park, futurama]) + end + it "returns all the videos if there are less than 6 videos" do + comedies = Category.create(name: "comedies") + futurama = Video.create(title: "Futurama", description: "space travel", category: comedies, created_at: 1.day.ago) + south_park = Video.create(title: "South Park", description: "crazy kids!", category: comedies) + expect(comedies.recent_videos.count).to eq(2) + end + it "returns 6 videos if there are more than 6 videos" do + comedies = Category.create(name: "comedies") + 7.times { Video.create(title: "foo", description: "foobar", category: comedies)} + expect(comedies.recent_videos.count).to eq(6) + end + it "returns the most recent 6 videos" do + comedies = Category.create(name: "comedies") + 6.times { Video.create(title: "foo", description: "foobar", category: comedies)} + tonights_show = Video.create(title: "Tonights show", description: "talk show", category: comedies, created_at: 1.day.ago) + expect(comedies.recent_videos).not_to include(tonights_show) + end + it "returns an empty array if the category does not have any videos" do + comedies = Category.create(name: "comedies") + expect(comedies.recent_videos).to eq([]) + end + end +end diff --git a/spec/models/queue_item_spec.rb b/spec/models/queue_item_spec.rb new file mode 100644 index 000000000..da753aea0 --- /dev/null +++ b/spec/models/queue_item_spec.rb @@ -0,0 +1,48 @@ +require 'spec_helper' + +describe QueueItem do + it { should belong_to(:user) } + it { should belong_to(:video) } + + describe "#video_title" do + it "returns the title of the associated video" do + video = Fabricate(:video, title: "Monk") + queue_item = Fabricate(:queue_item, video: video) + expect(queue_item.video_title).to eq("Monk") + end + end + + describe "#rating" do + it "returns the rating from the review when the review is present" do + video = Fabricate(:video) + user = Fabricate(:user) + review = Fabricate(:review, user: user, video: video, rating: 4) + queue_item = Fabricate(:queue_item, user: user, video: video) + expect(queue_item.rating).to eq(4) + end + it "returns nil when the review is not present" do + video = Fabricate(:video) + user = Fabricate(:user) + queue_item = Fabricate(:queue_item, user: user, video: video) + expect(queue_item.rating).to eq(nil) + end + end + + describe "#category_name" do + it "returns the category's name of the video" do + category = Fabricate(:category, name: "comedies") + video = Fabricate(:video, category: category) + queue_item = Fabricate(:queue_item, video: video) + expect(queue_item.category_name).to eq("comedies") + end + end + + describe "#category" do + it "returns the category of the video" do + category = Fabricate(:category, name: "comedies") + video = Fabricate(:video, category: category) + queue_item = Fabricate(:queue_item, video: video) + expect(queue_item.category).to eq(category) + end + end +end \ No newline at end of file diff --git a/spec/models/video_spec.rb b/spec/models/video_spec.rb new file mode 100644 index 000000000..a51abbab3 --- /dev/null +++ b/spec/models/video_spec.rb @@ -0,0 +1,37 @@ +require 'spec_helper' + + +describe Video do + it { should belong_to(:category)} + it { should validate_presence_of(:title)} + it { should validate_presence_of(:description)} + it { should have_many(:reviews).order("created_at DESC")} + + describe "search_by_title" do + it "returns an empty array if there is no match" do + futurama = Video.create(title: "Futurama", description: "Space Travel!") + back_to_future = Video.create(title: "Back to Future", description: "Time Travel!") + expect(Video.search_by_title("hello")).to eq([]) + end + it "returns an array of one video for an exact match" do + futurama = Video.create(title: "Futurama", description: "Space Travel!") + back_to_future = Video.create(title: "Back to Future", description: "Time Travel!") + expect(Video.search_by_title("Futurama")).to eq([futurama]) + end + it "returns an array of one video for a partial match" do + futurama = Video.create(title: "Futurama", description: "Space Travel!") + back_to_future = Video.create(title: "Back to Future", description: "Time Travel!") + expect(Video.search_by_title("urama")).to eq([futurama]) + end + it "returns an array of all matches ordered by created_at" do + futurama = Video.create(title: "Futurama", description: "Space Travel!", created_at: 1.day.ago) + back_to_future = Video.create(title: "Back to Future", description: "Time Travel!") + expect(Video.search_by_title("Futur")).to eq([back_to_future, futurama]) + end + it "returns an empty array for a search with an empty string" do + futurama = Video.create(title: "Futurama", description: "Space Travel!", created_at: 1.day.ago) + back_to_future = Video.create(title: "Back to Future", description: "Time Travel!") + expect(Video.search_by_title("")).to eq([]) + end + end +end \ No newline at end of file diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 000000000..943bc1919 --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,42 @@ +# This file is copied to spec/ when you run 'rails generate rspec:install' +ENV["RAILS_ENV"] ||= 'test' +require File.expand_path("../../config/environment", __FILE__) +require 'rspec/rails' +require 'rspec/autorun' + +# 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 } + +# Checks for pending migrations before tests are run. +# If you are not using ActiveRecord, you can remove this line. +ActiveRecord::Migration.check_pending! if defined?(ActiveRecord::Migration) + +RSpec.configure do |config| + # ## Mock Framework + # + # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line: + # + # config.mock_with :mocha + # config.mock_with :flexmock + # config.mock_with :rr + + # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures + config.fixture_path = "#{::Rails.root}/spec/fixtures" + + # If you're not using ActiveRecord, or you'd prefer not to run each of your + # examples within a transaction, remove the following line or assign false + # instead of true. + config.use_transactional_fixtures = true + + # If true, the base class of anonymous controllers will be inferred + # automatically. This will be the default behavior in future versions of + # rspec-rails. + config.infer_base_class_for_anonymous_controllers = false + + # Run specs in random order to surface order dependencies. If you find an + # order dependency and want to debug it, you can fix the order by providing + # the seed, which is printed after each run. + # --seed 1234 + config.order = "random" +end