From ff548227de106a920a3341ef2a9a82a244d9a72d Mon Sep 17 00:00:00 2001 From: add2point71dots Date: Tue, 9 May 2017 13:08:03 -0700 Subject: [PATCH 01/41] initial rails setup --- .gitignore | 23 ++ Gemfile | 66 +++++ Gemfile.lock | 245 ++++++++++++++++++ Rakefile | 6 + app/channels/application_cable/channel.rb | 4 + app/channels/application_cable/connection.rb | 4 + app/controllers/application_controller.rb | 2 + app/controllers/concerns/.keep | 0 app/jobs/application_job.rb | 2 + app/mailers/application_mailer.rb | 4 + app/models/application_record.rb | 3 + app/models/concerns/.keep | 0 app/views/layouts/mailer.html.erb | 13 + app/views/layouts/mailer.text.erb | 1 + bin/bundle | 3 + bin/rails | 9 + bin/rake | 9 + bin/setup | 34 +++ bin/spring | 17 ++ bin/update | 29 +++ config.ru | 5 + config/application.rb | 34 +++ config/boot.rb | 3 + config/cable.yml | 9 + config/database.yml | 85 ++++++ config/environment.rb | 5 + config/environments/development.rb | 47 ++++ config/environments/production.rb | 78 ++++++ config/environments/test.rb | 42 +++ .../application_controller_renderer.rb | 6 + config/initializers/backtrace_silencers.rb | 7 + config/initializers/cors.rb | 16 ++ .../initializers/filter_parameter_logging.rb | 4 + config/initializers/inflections.rb | 16 ++ config/initializers/mime_types.rb | 4 + config/initializers/new_framework_defaults.rb | 18 ++ config/initializers/wrap_parameters.rb | 14 + config/locales/en.yml | 23 ++ config/puma.rb | 47 ++++ config/routes.rb | 3 + config/secrets.yml | 22 ++ config/spring.rb | 6 + lib/tasks/.keep | 0 log/.keep | 0 public/robots.txt | 5 + test/controllers/.keep | 0 test/fixtures/.keep | 0 test/fixtures/files/.keep | 0 test/integration/.keep | 0 test/mailers/.keep | 0 test/models/.keep | 0 test/test_helper.rb | 26 ++ tmp/.keep | 0 53 files changed, 999 insertions(+) create mode 100644 .gitignore create mode 100644 Gemfile create mode 100644 Gemfile.lock create mode 100644 Rakefile create mode 100644 app/channels/application_cable/channel.rb create mode 100644 app/channels/application_cable/connection.rb create mode 100644 app/controllers/application_controller.rb create mode 100644 app/controllers/concerns/.keep create mode 100644 app/jobs/application_job.rb create mode 100644 app/mailers/application_mailer.rb create mode 100644 app/models/application_record.rb create mode 100644 app/models/concerns/.keep create mode 100644 app/views/layouts/mailer.html.erb create mode 100644 app/views/layouts/mailer.text.erb create mode 100755 bin/bundle create mode 100755 bin/rails create mode 100755 bin/rake create mode 100755 bin/setup create mode 100755 bin/spring create mode 100755 bin/update create mode 100644 config.ru create mode 100644 config/application.rb create mode 100644 config/boot.rb create mode 100644 config/cable.yml create mode 100644 config/database.yml create mode 100644 config/environment.rb create mode 100644 config/environments/development.rb create mode 100644 config/environments/production.rb create mode 100644 config/environments/test.rb create mode 100644 config/initializers/application_controller_renderer.rb create mode 100644 config/initializers/backtrace_silencers.rb create mode 100644 config/initializers/cors.rb create mode 100644 config/initializers/filter_parameter_logging.rb create mode 100644 config/initializers/inflections.rb create mode 100644 config/initializers/mime_types.rb create mode 100644 config/initializers/new_framework_defaults.rb create mode 100644 config/initializers/wrap_parameters.rb create mode 100644 config/locales/en.yml create mode 100644 config/puma.rb create mode 100644 config/routes.rb create mode 100644 config/secrets.yml create mode 100644 config/spring.rb create mode 100644 lib/tasks/.keep create mode 100644 log/.keep create mode 100644 public/robots.txt create mode 100644 test/controllers/.keep create mode 100644 test/fixtures/.keep create mode 100644 test/fixtures/files/.keep create mode 100644 test/integration/.keep create mode 100644 test/mailers/.keep create mode 100644 test/models/.keep create mode 100644 test/test_helper.rb create mode 100644 tmp/.keep diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..2eaabb750 --- /dev/null +++ b/.gitignore @@ -0,0 +1,23 @@ +# See https://help.github.com/articles/ignoring-files for more about ignoring files. +# +# If you find yourself ignoring temporary files generated by your text editor +# or operating system, you probably want to add a global ignore instead: +# git config --global core.excludesfile '~/.gitignore_global' + +# Ignore bundler config. +/.bundle + +# Ignore all logfiles and tempfiles. +/log/* +/tmp/* +!/log/.keep +!/tmp/.keep + +# Ignore Byebug command history file. +.byebug_history + +# Ignore coverage +/coverage/ + +# Ignore .env file +.env diff --git a/Gemfile b/Gemfile new file mode 100644 index 000000000..ba7ec62a3 --- /dev/null +++ b/Gemfile @@ -0,0 +1,66 @@ +source 'https://rubygems.org' + +git_source(:github) do |repo_name| + repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/") + "https://github.com/#{repo_name}.git" +end + + +# Bundle edge Rails instead: gem 'rails', github: 'rails/rails' +gem 'rails', '~> 5.0.2' +# Use postgresql as the database for Active Record +gem 'pg', '~> 0.18' +# Use Puma as the app server +gem 'puma', '~> 3.0' +# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder +# gem 'jbuilder', '~> 2.5' +# Use Redis adapter to run Action Cable in production +# gem 'redis', '~> 3.0' +# Use ActiveModel has_secure_password +# gem 'bcrypt', '~> 3.1.7' + +# Use Capistrano for deployment +# gem 'capistrano-rails', group: :development + +# Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible +# gem 'rack-cors' + +group :development, :test do + # Call 'byebug' anywhere in the code to stop execution and get a debugger console + gem 'byebug', platform: :mri +end + +group :development do + gem 'listen', '~> 3.0.5' + # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring + gem 'spring' + gem 'spring-watcher-listen', '~> 2.0.0' +end + +# Windows does not include zoneinfo files, so bundle the tzinfo-data gem +gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] + +group :development do + gem 'better_errors' + gem 'binding_of_caller' + gem 'pry-rails' + gem 'rails-erd' +end + +group :test do + gem 'minitest-rails' + gem 'minitest-reporters' +end + +group :development, :test do + gem 'minitest-vcr' + gem 'webmock' + gem 'dotenv-rails' +end + +gem 'awesome_print' +gem 'omniauth' +gem 'omniauth-github' +gem 'omniauth-google-oauth2' +gem 'foundation-rails' +gem 'httparty' \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 000000000..664464c12 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,245 @@ +GEM + remote: https://rubygems.org/ + specs: + actioncable (5.0.2) + actionpack (= 5.0.2) + nio4r (>= 1.2, < 3.0) + websocket-driver (~> 0.6.1) + actionmailer (5.0.2) + actionpack (= 5.0.2) + actionview (= 5.0.2) + activejob (= 5.0.2) + mail (~> 2.5, >= 2.5.4) + rails-dom-testing (~> 2.0) + actionpack (5.0.2) + actionview (= 5.0.2) + activesupport (= 5.0.2) + rack (~> 2.0) + rack-test (~> 0.6.3) + rails-dom-testing (~> 2.0) + rails-html-sanitizer (~> 1.0, >= 1.0.2) + actionview (5.0.2) + activesupport (= 5.0.2) + builder (~> 3.1) + erubis (~> 2.7.0) + rails-dom-testing (~> 2.0) + rails-html-sanitizer (~> 1.0, >= 1.0.3) + activejob (5.0.2) + activesupport (= 5.0.2) + globalid (>= 0.3.6) + activemodel (5.0.2) + activesupport (= 5.0.2) + activerecord (5.0.2) + activemodel (= 5.0.2) + activesupport (= 5.0.2) + arel (~> 7.0) + activesupport (5.0.2) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (~> 0.7) + minitest (~> 5.1) + tzinfo (~> 1.1) + addressable (2.5.1) + public_suffix (~> 2.0, >= 2.0.2) + ansi (1.5.0) + arel (7.1.4) + awesome_print (1.7.0) + babel-source (5.8.35) + babel-transpiler (0.7.0) + babel-source (>= 4.0, < 6) + execjs (~> 2.0) + better_errors (2.1.1) + coderay (>= 1.0.0) + erubis (>= 2.6.6) + rack (>= 0.9.0) + binding_of_caller (0.7.2) + debug_inspector (>= 0.0.1) + builder (3.2.3) + byebug (9.0.6) + choice (0.2.0) + coderay (1.1.1) + concurrent-ruby (1.0.5) + crack (0.4.3) + safe_yaml (~> 1.0.0) + debug_inspector (0.0.3) + dotenv (2.2.1) + dotenv-rails (2.2.1) + dotenv (= 2.2.1) + railties (>= 3.2, < 5.2) + erubis (2.7.0) + execjs (2.7.0) + faraday (0.11.0) + multipart-post (>= 1.2, < 3) + ffi (1.9.18) + foundation-rails (6.3.1.0) + railties (>= 3.1.0) + sass (>= 3.3.0, < 3.5) + sprockets-es6 (>= 0.9.0) + globalid (0.4.0) + activesupport (>= 4.2.0) + hashdiff (0.3.4) + hashie (3.5.5) + httparty (0.14.0) + multi_xml (>= 0.5.2) + i18n (0.8.1) + jwt (1.5.6) + listen (3.0.8) + rb-fsevent (~> 0.9, >= 0.9.4) + rb-inotify (~> 0.9, >= 0.9.7) + loofah (2.0.3) + nokogiri (>= 1.5.9) + mail (2.6.5) + mime-types (>= 1.16, < 4) + method_source (0.8.2) + mime-types (3.1) + mime-types-data (~> 3.2015) + mime-types-data (3.2016.0521) + mini_portile2 (2.1.0) + minispec-metadata (2.0.0) + minitest + minitest (5.10.1) + minitest-rails (3.0.0) + minitest (~> 5.8) + railties (~> 5.0) + minitest-reporters (1.1.14) + ansi + builder + minitest (>= 5.0) + ruby-progressbar + minitest-vcr (1.4.0) + minispec-metadata (~> 2.0) + minitest (>= 4.7.5) + vcr (>= 2.9) + multi_json (1.12.1) + multi_xml (0.6.0) + multipart-post (2.0.0) + nio4r (2.0.0) + nokogiri (1.7.1) + mini_portile2 (~> 2.1.0) + oauth2 (1.3.1) + faraday (>= 0.8, < 0.12) + jwt (~> 1.0) + multi_json (~> 1.3) + multi_xml (~> 0.5) + rack (>= 1.2, < 3) + omniauth (1.6.1) + hashie (>= 3.4.6, < 3.6.0) + rack (>= 1.6.2, < 3) + omniauth-github (1.2.3) + omniauth (~> 1.5) + omniauth-oauth2 (>= 1.4.0, < 2.0) + omniauth-google-oauth2 (0.4.1) + jwt (~> 1.5.2) + multi_json (~> 1.3) + omniauth (>= 1.1.1) + omniauth-oauth2 (>= 1.3.1) + omniauth-oauth2 (1.4.0) + oauth2 (~> 1.0) + omniauth (~> 1.2) + pg (0.20.0) + pry (0.10.4) + coderay (~> 1.1.0) + method_source (~> 0.8.1) + slop (~> 3.4) + pry-rails (0.3.6) + pry (>= 0.10.4) + public_suffix (2.0.5) + puma (3.8.2) + rack (2.0.2) + rack-test (0.6.3) + rack (>= 1.0) + rails (5.0.2) + actioncable (= 5.0.2) + actionmailer (= 5.0.2) + actionpack (= 5.0.2) + actionview (= 5.0.2) + activejob (= 5.0.2) + activemodel (= 5.0.2) + activerecord (= 5.0.2) + activesupport (= 5.0.2) + bundler (>= 1.3.0, < 2.0) + railties (= 5.0.2) + sprockets-rails (>= 2.0.0) + rails-dom-testing (2.0.2) + activesupport (>= 4.2.0, < 6.0) + nokogiri (~> 1.6) + rails-erd (1.5.0) + activerecord (>= 3.2) + activesupport (>= 3.2) + choice (~> 0.2.0) + ruby-graphviz (~> 1.2) + rails-html-sanitizer (1.0.3) + loofah (~> 2.0) + railties (5.0.2) + actionpack (= 5.0.2) + activesupport (= 5.0.2) + method_source + rake (>= 0.8.7) + thor (>= 0.18.1, < 2.0) + rake (12.0.0) + rb-fsevent (0.9.8) + rb-inotify (0.9.8) + ffi (>= 0.5.0) + ruby-graphviz (1.2.3) + ruby-progressbar (1.8.1) + safe_yaml (1.0.4) + sass (3.4.23) + slop (3.6.0) + spring (2.0.1) + activesupport (>= 4.2) + spring-watcher-listen (2.0.1) + listen (>= 2.7, < 4.0) + spring (>= 1.2, < 3.0) + sprockets (3.7.1) + concurrent-ruby (~> 1.0) + rack (> 1, < 3) + sprockets-es6 (0.9.2) + babel-source (>= 5.8.11) + babel-transpiler + sprockets (>= 3.0.0) + sprockets-rails (3.2.0) + actionpack (>= 4.0) + activesupport (>= 4.0) + sprockets (>= 3.0.0) + thor (0.19.4) + thread_safe (0.3.6) + tzinfo (1.2.3) + thread_safe (~> 0.1) + vcr (3.0.3) + webmock (3.0.1) + addressable (>= 2.3.6) + crack (>= 0.3.2) + hashdiff + websocket-driver (0.6.5) + websocket-extensions (>= 0.1.0) + websocket-extensions (0.1.2) + +PLATFORMS + ruby + +DEPENDENCIES + awesome_print + better_errors + binding_of_caller + byebug + dotenv-rails + foundation-rails + httparty + listen (~> 3.0.5) + minitest-rails + minitest-reporters + minitest-vcr + omniauth + omniauth-github + omniauth-google-oauth2 + pg (~> 0.18) + pry-rails + puma (~> 3.0) + rails (~> 5.0.2) + rails-erd + spring + spring-watcher-listen (~> 2.0.0) + tzinfo-data + webmock + +BUNDLED WITH + 1.14.6 diff --git a/Rakefile b/Rakefile new file mode 100644 index 000000000..e85f91391 --- /dev/null +++ b/Rakefile @@ -0,0 +1,6 @@ +# Add your own tasks in files placed in lib/tasks ending in .rake, +# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. + +require_relative 'config/application' + +Rails.application.load_tasks diff --git a/app/channels/application_cable/channel.rb b/app/channels/application_cable/channel.rb new file mode 100644 index 000000000..d67269728 --- /dev/null +++ b/app/channels/application_cable/channel.rb @@ -0,0 +1,4 @@ +module ApplicationCable + class Channel < ActionCable::Channel::Base + end +end diff --git a/app/channels/application_cable/connection.rb b/app/channels/application_cable/connection.rb new file mode 100644 index 000000000..0ff5442f4 --- /dev/null +++ b/app/channels/application_cable/connection.rb @@ -0,0 +1,4 @@ +module ApplicationCable + class Connection < ActionCable::Connection::Base + end +end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb new file mode 100644 index 000000000..4ac8823b0 --- /dev/null +++ b/app/controllers/application_controller.rb @@ -0,0 +1,2 @@ +class ApplicationController < ActionController::API +end diff --git a/app/controllers/concerns/.keep b/app/controllers/concerns/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/app/jobs/application_job.rb b/app/jobs/application_job.rb new file mode 100644 index 000000000..a009ace51 --- /dev/null +++ b/app/jobs/application_job.rb @@ -0,0 +1,2 @@ +class ApplicationJob < ActiveJob::Base +end diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb new file mode 100644 index 000000000..286b2239d --- /dev/null +++ b/app/mailers/application_mailer.rb @@ -0,0 +1,4 @@ +class ApplicationMailer < ActionMailer::Base + default from: 'from@example.com' + layout 'mailer' +end diff --git a/app/models/application_record.rb b/app/models/application_record.rb new file mode 100644 index 000000000..10a4cba84 --- /dev/null +++ b/app/models/application_record.rb @@ -0,0 +1,3 @@ +class ApplicationRecord < ActiveRecord::Base + self.abstract_class = true +end diff --git a/app/models/concerns/.keep b/app/models/concerns/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/app/views/layouts/mailer.html.erb b/app/views/layouts/mailer.html.erb new file mode 100644 index 000000000..cbd34d2e9 --- /dev/null +++ b/app/views/layouts/mailer.html.erb @@ -0,0 +1,13 @@ + + + + + + + + + <%= yield %> + + diff --git a/app/views/layouts/mailer.text.erb b/app/views/layouts/mailer.text.erb new file mode 100644 index 000000000..37f0bddbd --- /dev/null +++ b/app/views/layouts/mailer.text.erb @@ -0,0 +1 @@ +<%= yield %> diff --git a/bin/bundle b/bin/bundle new file mode 100755 index 000000000..66e9889e8 --- /dev/null +++ b/bin/bundle @@ -0,0 +1,3 @@ +#!/usr/bin/env ruby +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) +load Gem.bin_path('bundler', 'bundle') diff --git a/bin/rails b/bin/rails new file mode 100755 index 000000000..5badb2fde --- /dev/null +++ b/bin/rails @@ -0,0 +1,9 @@ +#!/usr/bin/env ruby +begin + load File.expand_path('../spring', __FILE__) +rescue LoadError => e + raise unless e.message.include?('spring') +end +APP_PATH = File.expand_path('../config/application', __dir__) +require_relative '../config/boot' +require 'rails/commands' diff --git a/bin/rake b/bin/rake new file mode 100755 index 000000000..d87d5f578 --- /dev/null +++ b/bin/rake @@ -0,0 +1,9 @@ +#!/usr/bin/env ruby +begin + load File.expand_path('../spring', __FILE__) +rescue LoadError => e + raise unless e.message.include?('spring') +end +require_relative '../config/boot' +require 'rake' +Rake.application.run diff --git a/bin/setup b/bin/setup new file mode 100755 index 000000000..e620b4dad --- /dev/null +++ b/bin/setup @@ -0,0 +1,34 @@ +#!/usr/bin/env ruby +require 'pathname' +require 'fileutils' +include FileUtils + +# path to your application root. +APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) + +def system!(*args) + system(*args) || abort("\n== Command #{args} failed ==") +end + +chdir APP_ROOT do + # This script is a starting point to setup your application. + # Add necessary setup steps to this file. + + puts '== Installing dependencies ==' + system! 'gem install bundler --conservative' + system('bundle check') || system!('bundle install') + + # puts "\n== Copying sample files ==" + # unless File.exist?('config/database.yml') + # cp 'config/database.yml.sample', 'config/database.yml' + # end + + puts "\n== Preparing database ==" + system! 'bin/rails db:setup' + + puts "\n== Removing old logs and tempfiles ==" + system! 'bin/rails log:clear tmp:clear' + + puts "\n== Restarting application server ==" + system! 'bin/rails restart' +end diff --git a/bin/spring b/bin/spring new file mode 100755 index 000000000..fb2ec2ebb --- /dev/null +++ b/bin/spring @@ -0,0 +1,17 @@ +#!/usr/bin/env ruby + +# This file loads spring without using Bundler, in order to be fast. +# It gets overwritten when you run the `spring binstub` command. + +unless defined?(Spring) + require 'rubygems' + require 'bundler' + + lockfile = Bundler::LockfileParser.new(Bundler.default_lockfile.read) + spring = lockfile.specs.detect { |spec| spec.name == "spring" } + if spring + Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path + gem 'spring', spring.version + require 'spring/binstub' + end +end diff --git a/bin/update b/bin/update new file mode 100755 index 000000000..a8e4462f2 --- /dev/null +++ b/bin/update @@ -0,0 +1,29 @@ +#!/usr/bin/env ruby +require 'pathname' +require 'fileutils' +include FileUtils + +# path to your application root. +APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) + +def system!(*args) + system(*args) || abort("\n== Command #{args} failed ==") +end + +chdir APP_ROOT do + # This script is a way to update your development environment automatically. + # Add necessary update steps to this file. + + puts '== Installing dependencies ==' + system! 'gem install bundler --conservative' + system('bundle check') || system!('bundle install') + + puts "\n== Updating database ==" + system! 'bin/rails db:migrate' + + puts "\n== Removing old logs and tempfiles ==" + system! 'bin/rails log:clear tmp:clear' + + puts "\n== Restarting application server ==" + system! 'bin/rails restart' +end diff --git a/config.ru b/config.ru new file mode 100644 index 000000000..f7ba0b527 --- /dev/null +++ b/config.ru @@ -0,0 +1,5 @@ +# This file is used by Rack-based servers to start the application. + +require_relative 'config/environment' + +run Rails.application diff --git a/config/application.rb b/config/application.rb new file mode 100644 index 000000000..54782d010 --- /dev/null +++ b/config/application.rb @@ -0,0 +1,34 @@ +require_relative 'boot' + +require "rails" +# Pick the frameworks you want: +require "active_model/railtie" +require "active_job/railtie" +require "active_record/railtie" +require "action_controller/railtie" +require "action_mailer/railtie" +require "action_view/railtie" +require "action_cable/engine" +# require "sprockets/railtie" +require "rails/test_unit/railtie" + +# Require the gems listed in Gemfile, including any gems +# you've limited to :test, :development, or :production. +Bundler.require(*Rails.groups) + +module VideoStoreAPI + class Application < Rails::Application + # Force new test files to be generated in the minitest-spec style + config.generators do |g| + g.test_framework :minitest, spec: true + end + # Settings in config/environments/* take precedence over those specified here. + # Application configuration should go into files in config/initializers + # -- all .rb files in that directory are automatically loaded. + + # Only loads a smaller set of middleware suitable for API only apps. + # Middleware like session, flash, cookies can be added back manually. + # Skip views, helpers and assets when generating a new resource. + config.api_only = true + end +end diff --git a/config/boot.rb b/config/boot.rb new file mode 100644 index 000000000..30f5120df --- /dev/null +++ b/config/boot.rb @@ -0,0 +1,3 @@ +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) + +require 'bundler/setup' # Set up gems listed in the Gemfile. diff --git a/config/cable.yml b/config/cable.yml new file mode 100644 index 000000000..0bbde6f74 --- /dev/null +++ b/config/cable.yml @@ -0,0 +1,9 @@ +development: + adapter: async + +test: + adapter: async + +production: + adapter: redis + url: redis://localhost:6379/1 diff --git a/config/database.yml b/config/database.yml new file mode 100644 index 000000000..aead51124 --- /dev/null +++ b/config/database.yml @@ -0,0 +1,85 @@ +# PostgreSQL. Versions 9.1 and up are supported. +# +# Install the pg driver: +# gem install pg +# On OS X with Homebrew: +# gem install pg -- --with-pg-config=/usr/local/bin/pg_config +# On OS X with MacPorts: +# gem install pg -- --with-pg-config=/opt/local/lib/postgresql84/bin/pg_config +# On Windows: +# gem install pg +# Choose the win32 build. +# Install PostgreSQL and put its /bin directory on your path. +# +# Configure Using Gemfile +# gem 'pg' +# +default: &default + adapter: postgresql + encoding: unicode + # For details on connection pooling, see rails configuration guide + # http://guides.rubyonrails.org/configuring.html#database-pooling + pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> + +development: + <<: *default + database: VideoStoreAPI_development + + # The specified database role being used to connect to postgres. + # To create additional roles in postgres see `$ createuser --help`. + # When left blank, postgres will use the default role. This is + # the same name as the operating system user that initialized the database. + #username: VideoStoreAPI + + # The password associated with the postgres role (username). + #password: + + # Connect on a TCP socket. Omitted by default since the client uses a + # domain socket that doesn't need configuration. Windows does not have + # domain sockets, so uncomment these lines. + #host: localhost + + # The TCP port the server listens on. Defaults to 5432. + # If your server runs on a different port number, change accordingly. + #port: 5432 + + # Schema search path. The server defaults to $user,public + #schema_search_path: myapp,sharedapp,public + + # Minimum log levels, in increasing order: + # debug5, debug4, debug3, debug2, debug1, + # log, notice, warning, error, fatal, and panic + # Defaults to warning. + #min_messages: notice + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + <<: *default + database: VideoStoreAPI_test + +# As with config/secrets.yml, you never want to store sensitive information, +# like your database password, in your source code. If your source code is +# ever seen by anyone, they now have access to your database. +# +# Instead, provide the password as a unix environment variable when you boot +# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database +# for a full rundown on how to provide these environment variables in a +# production deployment. +# +# On Heroku and other platform providers, you may have a full connection URL +# available as an environment variable. For example: +# +# DATABASE_URL="postgres://myuser:mypass@localhost/somedatabase" +# +# You can use this database configuration with: +# +# production: +# url: <%= ENV['DATABASE_URL'] %> +# +production: + <<: *default + database: VideoStoreAPI_production + username: VideoStoreAPI + password: <%= ENV['VIDEOSTOREAPI_DATABASE_PASSWORD'] %> diff --git a/config/environment.rb b/config/environment.rb new file mode 100644 index 000000000..426333bb4 --- /dev/null +++ b/config/environment.rb @@ -0,0 +1,5 @@ +# Load the Rails application. +require_relative 'application' + +# Initialize the Rails application. +Rails.application.initialize! diff --git a/config/environments/development.rb b/config/environments/development.rb new file mode 100644 index 000000000..082a013ab --- /dev/null +++ b/config/environments/development.rb @@ -0,0 +1,47 @@ +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. + + # In the development environment your application's code is reloaded on + # every request. This slows down response time but is perfect for development + # since you don't have to restart the web server when you make code changes. + config.cache_classes = false + + # Do not eager load code on boot. + config.eager_load = false + + # Show full error reports. + config.consider_all_requests_local = true + + # Enable/disable caching. By default caching is disabled. + if Rails.root.join('tmp/caching-dev.txt').exist? + config.action_controller.perform_caching = true + + config.cache_store = :memory_store + config.public_file_server.headers = { + 'Cache-Control' => 'public, max-age=172800' + } + else + config.action_controller.perform_caching = false + + config.cache_store = :null_store + end + + # Don't care if the mailer can't send. + config.action_mailer.raise_delivery_errors = false + + config.action_mailer.perform_caching = false + + # Print deprecation notices to the Rails logger. + config.active_support.deprecation = :log + + # Raise an error on page load if there are pending migrations. + config.active_record.migration_error = :page_load + + + # Raises error for missing translations + # config.action_view.raise_on_missing_translations = true + + # Use an evented file watcher to asynchronously detect changes in source code, + # routes, locales, etc. This feature depends on the listen gem. + config.file_watcher = ActiveSupport::EventedFileUpdateChecker +end diff --git a/config/environments/production.rb b/config/environments/production.rb new file mode 100644 index 000000000..49275a9d6 --- /dev/null +++ b/config/environments/production.rb @@ -0,0 +1,78 @@ +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. + + # Code is not reloaded between requests. + config.cache_classes = true + + # Eager load code on boot. This eager loads most of Rails and + # your application in memory, allowing both threaded web servers + # and those relying on copy on write to perform better. + # Rake tasks automatically ignore this option for performance. + config.eager_load = true + + # Full error reports are disabled and caching is turned on. + config.consider_all_requests_local = false + config.action_controller.perform_caching = true + + # Disable serving static files from the `/public` folder by default since + # Apache or NGINX already handles this. + config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? + + + # Enable serving of images, stylesheets, and JavaScripts from an asset server. + # config.action_controller.asset_host = 'http://assets.example.com' + + # Specifies the header that your server uses for sending files. + # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache + # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX + + # Mount Action Cable outside main process or domain + # config.action_cable.mount_path = nil + # config.action_cable.url = 'wss://example.com/cable' + # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] + + # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. + # config.force_ssl = true + + # Use the lowest log level to ensure availability of diagnostic information + # when problems arise. + config.log_level = :debug + + # Prepend all log lines with the following tags. + config.log_tags = [ :request_id ] + + # Use a different cache store in production. + # config.cache_store = :mem_cache_store + + # Use a real queuing backend for Active Job (and separate queues per environment) + # config.active_job.queue_adapter = :resque + # config.active_job.queue_name_prefix = "VideoStoreAPI_#{Rails.env}" + config.action_mailer.perform_caching = false + + # Ignore bad email addresses and do not raise email delivery errors. + # Set this to true and configure the email server for immediate delivery to raise delivery errors. + # config.action_mailer.raise_delivery_errors = false + + # Enable locale fallbacks for I18n (makes lookups for any locale fall back to + # the I18n.default_locale when a translation cannot be found). + config.i18n.fallbacks = true + + # Send deprecation notices to registered listeners. + config.active_support.deprecation = :notify + + # Use default logging formatter so that PID and timestamp are not suppressed. + config.log_formatter = ::Logger::Formatter.new + + # Use a different logger for distributed setups. + # require 'syslog/logger' + # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') + + if ENV["RAILS_LOG_TO_STDOUT"].present? + logger = ActiveSupport::Logger.new(STDOUT) + logger.formatter = config.log_formatter + config.logger = ActiveSupport::TaggedLogging.new(logger) + end + + # Do not dump schema after migrations. + config.active_record.dump_schema_after_migration = false +end diff --git a/config/environments/test.rb b/config/environments/test.rb new file mode 100644 index 000000000..30587ef6d --- /dev/null +++ b/config/environments/test.rb @@ -0,0 +1,42 @@ +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. + + # The test environment is used exclusively to run your application's + # test suite. You never need to work with it otherwise. Remember that + # your test database is "scratch space" for the test suite and is wiped + # and recreated between test runs. Don't rely on the data there! + config.cache_classes = true + + # Do not eager load code on boot. This avoids loading your whole application + # just for the purpose of running a single test. If you are using a tool that + # preloads Rails for running tests, you may have to set it to true. + config.eager_load = false + + # Configure public file server for tests with Cache-Control for performance. + config.public_file_server.enabled = true + config.public_file_server.headers = { + 'Cache-Control' => 'public, max-age=3600' + } + + # Show full error reports and disable caching. + config.consider_all_requests_local = true + config.action_controller.perform_caching = false + + # Raise exceptions instead of rendering exception templates. + config.action_dispatch.show_exceptions = false + + # Disable request forgery protection in test environment. + config.action_controller.allow_forgery_protection = false + config.action_mailer.perform_caching = false + + # Tell Action Mailer not to deliver emails to the real world. + # The :test delivery method accumulates sent emails in the + # ActionMailer::Base.deliveries array. + config.action_mailer.delivery_method = :test + + # Print deprecation notices to the stderr. + config.active_support.deprecation = :stderr + + # Raises error for missing translations + # config.action_view.raise_on_missing_translations = true +end diff --git a/config/initializers/application_controller_renderer.rb b/config/initializers/application_controller_renderer.rb new file mode 100644 index 000000000..51639b67a --- /dev/null +++ b/config/initializers/application_controller_renderer.rb @@ -0,0 +1,6 @@ +# Be sure to restart your server when you modify this file. + +# ApplicationController.renderer.defaults.merge!( +# http_host: 'example.org', +# https: false +# ) diff --git a/config/initializers/backtrace_silencers.rb b/config/initializers/backtrace_silencers.rb new file mode 100644 index 000000000..59385cdf3 --- /dev/null +++ b/config/initializers/backtrace_silencers.rb @@ -0,0 +1,7 @@ +# Be sure to restart your server when you modify this file. + +# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. +# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } + +# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. +# Rails.backtrace_cleaner.remove_silencers! diff --git a/config/initializers/cors.rb b/config/initializers/cors.rb new file mode 100644 index 000000000..3b1c1b5ed --- /dev/null +++ b/config/initializers/cors.rb @@ -0,0 +1,16 @@ +# Be sure to restart your server when you modify this file. + +# Avoid CORS issues when API is called from the frontend app. +# Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests. + +# Read more: https://github.com/cyu/rack-cors + +# Rails.application.config.middleware.insert_before 0, Rack::Cors do +# allow do +# origins 'example.com' +# +# resource '*', +# headers: :any, +# methods: [:get, :post, :put, :patch, :delete, :options, :head] +# end +# end diff --git a/config/initializers/filter_parameter_logging.rb b/config/initializers/filter_parameter_logging.rb new file mode 100644 index 000000000..4a994e1e7 --- /dev/null +++ b/config/initializers/filter_parameter_logging.rb @@ -0,0 +1,4 @@ +# Be sure to restart your server when you modify this file. + +# Configure sensitive parameters which will be filtered from the log file. +Rails.application.config.filter_parameters += [:password] diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb new file mode 100644 index 000000000..ac033bf9d --- /dev/null +++ b/config/initializers/inflections.rb @@ -0,0 +1,16 @@ +# Be sure to restart your server when you modify this file. + +# Add new inflection rules using the following format. Inflections +# are locale specific, and you may define rules for as many different +# locales as you wish. All of these examples are active by default: +# ActiveSupport::Inflector.inflections(:en) do |inflect| +# inflect.plural /^(ox)$/i, '\1en' +# inflect.singular /^(ox)en/i, '\1' +# inflect.irregular 'person', 'people' +# inflect.uncountable %w( fish sheep ) +# end + +# These inflection rules are supported but not enabled by default: +# ActiveSupport::Inflector.inflections(:en) do |inflect| +# inflect.acronym 'RESTful' +# end diff --git a/config/initializers/mime_types.rb b/config/initializers/mime_types.rb new file mode 100644 index 000000000..dc1899682 --- /dev/null +++ b/config/initializers/mime_types.rb @@ -0,0 +1,4 @@ +# Be sure to restart your server when you modify this file. + +# Add new mime types for use in respond_to blocks: +# Mime::Type.register "text/richtext", :rtf diff --git a/config/initializers/new_framework_defaults.rb b/config/initializers/new_framework_defaults.rb new file mode 100644 index 000000000..d859e4bea --- /dev/null +++ b/config/initializers/new_framework_defaults.rb @@ -0,0 +1,18 @@ +# Be sure to restart your server when you modify this file. +# +# This file contains migration options to ease your Rails 5.0 upgrade. +# +# Read the Guide for Upgrading Ruby on Rails for more info on each option. + +# Make Ruby 2.4 preserve the timezone of the receiver when calling `to_time`. +# Previous versions had false. +ActiveSupport.to_time_preserves_timezone = true + +# Require `belongs_to` associations by default. Previous versions had false. +Rails.application.config.active_record.belongs_to_required_by_default = true + +# Do not halt callback chains when a callback returns false. Previous versions had true. +ActiveSupport.halt_callback_chains_on_return_false = false + +# Configure SSL options to enable HSTS with subdomains. Previous versions had false. +Rails.application.config.ssl_options = { hsts: { subdomains: true } } diff --git a/config/initializers/wrap_parameters.rb b/config/initializers/wrap_parameters.rb new file mode 100644 index 000000000..bbfc3961b --- /dev/null +++ b/config/initializers/wrap_parameters.rb @@ -0,0 +1,14 @@ +# Be sure to restart your server when you modify this file. + +# This file contains settings for ActionController::ParamsWrapper which +# is enabled by default. + +# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. +ActiveSupport.on_load(:action_controller) do + wrap_parameters format: [:json] +end + +# To enable root element in JSON for ActiveRecord objects. +# ActiveSupport.on_load(:active_record) do +# self.include_root_in_json = true +# end diff --git a/config/locales/en.yml b/config/locales/en.yml new file mode 100644 index 000000000..065395716 --- /dev/null +++ b/config/locales/en.yml @@ -0,0 +1,23 @@ +# Files in the config/locales directory are used for internationalization +# and are automatically loaded by Rails. If you want to use locales other +# than English, add the necessary files in this directory. +# +# To use the locales, use `I18n.t`: +# +# I18n.t 'hello' +# +# In views, this is aliased to just `t`: +# +# <%= t('hello') %> +# +# To use a different locale, set it with `I18n.locale`: +# +# I18n.locale = :es +# +# This would use the information in config/locales/es.yml. +# +# To learn more, please read the Rails Internationalization guide +# available at http://guides.rubyonrails.org/i18n.html. + +en: + hello: "Hello world" diff --git a/config/puma.rb b/config/puma.rb new file mode 100644 index 000000000..c7f311f81 --- /dev/null +++ b/config/puma.rb @@ -0,0 +1,47 @@ +# Puma can serve each request in a thread from an internal thread pool. +# The `threads` method setting takes two numbers a minimum and maximum. +# Any libraries that use thread pools should be configured to match +# the maximum value specified for Puma. Default is set to 5 threads for minimum +# and maximum, this matches the default thread size of Active Record. +# +threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }.to_i +threads threads_count, threads_count + +# Specifies the `port` that Puma will listen on to receive requests, default is 3000. +# +port ENV.fetch("PORT") { 3000 } + +# Specifies the `environment` that Puma will run in. +# +environment ENV.fetch("RAILS_ENV") { "development" } + +# Specifies the number of `workers` to boot in clustered mode. +# Workers are forked webserver processes. If using threads and workers together +# the concurrency of the application would be max `threads` * `workers`. +# Workers do not work on JRuby or Windows (both of which do not support +# processes). +# +# workers ENV.fetch("WEB_CONCURRENCY") { 2 } + +# Use the `preload_app!` method when specifying a `workers` number. +# This directive tells Puma to first boot the application and load code +# before forking the application. This takes advantage of Copy On Write +# process behavior so workers use less memory. If you use this option +# you need to make sure to reconnect any threads in the `on_worker_boot` +# block. +# +# preload_app! + +# The code in the `on_worker_boot` will be called if you are using +# clustered mode by specifying a number of `workers`. After each worker +# process is booted this block will be run, if you are using `preload_app!` +# option you will want to use this block to reconnect to any threads +# or connections that may have been created at application boot, Ruby +# cannot share connections between processes. +# +# on_worker_boot do +# ActiveRecord::Base.establish_connection if defined?(ActiveRecord) +# end + +# Allow puma to be restarted by `rails restart` command. +plugin :tmp_restart diff --git a/config/routes.rb b/config/routes.rb new file mode 100644 index 000000000..787824f88 --- /dev/null +++ b/config/routes.rb @@ -0,0 +1,3 @@ +Rails.application.routes.draw do + # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html +end diff --git a/config/secrets.yml b/config/secrets.yml new file mode 100644 index 000000000..4dacec0ec --- /dev/null +++ b/config/secrets.yml @@ -0,0 +1,22 @@ +# Be sure to restart your server when you modify this file. + +# Your secret key is used for verifying the integrity of signed cookies. +# If you change this key, all old signed cookies will become invalid! + +# Make sure the secret is at least 30 characters and all random, +# no regular words or you'll be exposed to dictionary attacks. +# You can use `rails secret` to generate a secure secret key. + +# Make sure the secrets in this file are kept private +# if you're sharing your code publicly. + +development: + secret_key_base: 1f456190e13d296a3684783cba72e5101ee17e00e391b30ee6193a7eefef5743f251548fe4723a330a7184f00457edbaf30ac812fb1022e62b595f10b37cbe57 + +test: + secret_key_base: f87bd88c3620ede20388cc3de8b7bd4c4868b2accdf9eca2072edb0f7455446e4d2362e27e7e2e08d3ba52fa79a9c3debf6d71c18dbf7d767458e5e6cef4bf2d + +# Do not keep production secrets in the repository, +# instead read values from the environment. +production: + secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> diff --git a/config/spring.rb b/config/spring.rb new file mode 100644 index 000000000..c9119b40c --- /dev/null +++ b/config/spring.rb @@ -0,0 +1,6 @@ +%w( + .ruby-version + .rbenv-vars + tmp/restart.txt + tmp/caching-dev.txt +).each { |path| Spring.watch(path) } diff --git a/lib/tasks/.keep b/lib/tasks/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/log/.keep b/log/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 000000000..3c9c7c01f --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,5 @@ +# See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file +# +# To ban all spiders from the entire site uncomment the next two lines: +# User-agent: * +# Disallow: / diff --git a/test/controllers/.keep b/test/controllers/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/test/fixtures/.keep b/test/fixtures/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/test/fixtures/files/.keep b/test/fixtures/files/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/test/integration/.keep b/test/integration/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/test/mailers/.keep b/test/mailers/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/test/models/.keep b/test/models/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/test/test_helper.rb b/test/test_helper.rb new file mode 100644 index 000000000..10594a324 --- /dev/null +++ b/test/test_helper.rb @@ -0,0 +1,26 @@ +ENV["RAILS_ENV"] = "test" +require File.expand_path("../../config/environment", __FILE__) +require "rails/test_help" +require "minitest/rails" +require "minitest/reporters" # for Colorized output + +# For colorful output! +Minitest::Reporters.use!( + Minitest::Reporters::SpecReporter.new, + ENV, + Minitest.backtrace_filter +) + + +# To add Capybara feature tests add `gem "minitest-rails-capybara"` +# to the test group in the Gemfile and uncomment the following: +# require "minitest/rails/capybara" + +# Uncomment for awesome colorful output +# require "minitest/pride" + +class ActiveSupport::TestCase + # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. + fixtures :all + # Add more helper methods to be used by all tests here... +end diff --git a/tmp/.keep b/tmp/.keep new file mode 100644 index 000000000..e69de29bb From d8cf182f9f385150a1c56687bd41f0be0774463b Mon Sep 17 00:00:00 2001 From: add2point71dots Date: Tue, 9 May 2017 13:29:02 -0700 Subject: [PATCH 02/41] created models and controllers for movie and customer --- app/controllers/customers_controller.rb | 2 + app/controllers/movies_controller.rb | 2 + app/models/customer.rb | 2 + app/models/movie.rb | 2 + config/routes.rb | 4 ++ db/migrate/20170509201346_create_customers.rb | 14 +++++++ db/migrate/20170509201355_create_movies.rb | 11 +++++ ...09201859_add_account_credit_to_customer.rb | 5 +++ db/schema.rb | 40 +++++++++++++++++++ test/controllers/customers_controller_test.rb | 7 ++++ test/controllers/movies_controller_test.rb | 7 ++++ test/fixtures/customers.yml | 11 +++++ test/fixtures/movies.yml | 11 +++++ test/models/customer_test.rb | 9 +++++ test/models/movie_test.rb | 9 +++++ 15 files changed, 136 insertions(+) create mode 100644 app/controllers/customers_controller.rb create mode 100644 app/controllers/movies_controller.rb create mode 100644 app/models/customer.rb create mode 100644 app/models/movie.rb create mode 100644 db/migrate/20170509201346_create_customers.rb create mode 100644 db/migrate/20170509201355_create_movies.rb create mode 100644 db/migrate/20170509201859_add_account_credit_to_customer.rb create mode 100644 db/schema.rb create mode 100644 test/controllers/customers_controller_test.rb create mode 100644 test/controllers/movies_controller_test.rb create mode 100644 test/fixtures/customers.yml create mode 100644 test/fixtures/movies.yml create mode 100644 test/models/customer_test.rb create mode 100644 test/models/movie_test.rb diff --git a/app/controllers/customers_controller.rb b/app/controllers/customers_controller.rb new file mode 100644 index 000000000..ca3b6e024 --- /dev/null +++ b/app/controllers/customers_controller.rb @@ -0,0 +1,2 @@ +class CustomersController < ApplicationController +end diff --git a/app/controllers/movies_controller.rb b/app/controllers/movies_controller.rb new file mode 100644 index 000000000..6c4c51614 --- /dev/null +++ b/app/controllers/movies_controller.rb @@ -0,0 +1,2 @@ +class MoviesController < ApplicationController +end diff --git a/app/models/customer.rb b/app/models/customer.rb new file mode 100644 index 000000000..0b5277335 --- /dev/null +++ b/app/models/customer.rb @@ -0,0 +1,2 @@ +class Customer < ApplicationRecord +end diff --git a/app/models/movie.rb b/app/models/movie.rb new file mode 100644 index 000000000..dc614df15 --- /dev/null +++ b/app/models/movie.rb @@ -0,0 +1,2 @@ +class Movie < ApplicationRecord +end diff --git a/config/routes.rb b/config/routes.rb index 787824f88..fe0c8acfc 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,3 +1,7 @@ Rails.application.routes.draw do # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html + get '/customers', to: 'customers#index' + + get '/movies', to: 'movies#index' + get '/movies/:title', to: 'movies#show' end diff --git a/db/migrate/20170509201346_create_customers.rb b/db/migrate/20170509201346_create_customers.rb new file mode 100644 index 000000000..fe8a0fe37 --- /dev/null +++ b/db/migrate/20170509201346_create_customers.rb @@ -0,0 +1,14 @@ +class CreateCustomers < ActiveRecord::Migration[5.0] + def change + create_table :customers do |t| + t.string :name + t.string :registered_at + t.string :address + t.string :city + t.string :state + t.string :postal_code + t.string :phone + t.timestamps + end + end +end diff --git a/db/migrate/20170509201355_create_movies.rb b/db/migrate/20170509201355_create_movies.rb new file mode 100644 index 000000000..2963ae651 --- /dev/null +++ b/db/migrate/20170509201355_create_movies.rb @@ -0,0 +1,11 @@ +class CreateMovies < ActiveRecord::Migration[5.0] + def change + create_table :movies do |t| + t.string :title + t.text :overview + t.string :release_date + t.integer :inventory + t.timestamps + end + end +end diff --git a/db/migrate/20170509201859_add_account_credit_to_customer.rb b/db/migrate/20170509201859_add_account_credit_to_customer.rb new file mode 100644 index 000000000..d1218fe88 --- /dev/null +++ b/db/migrate/20170509201859_add_account_credit_to_customer.rb @@ -0,0 +1,5 @@ +class AddAccountCreditToCustomer < ActiveRecord::Migration[5.0] + def change + add_column :customers, :account_credit, :float + end +end diff --git a/db/schema.rb b/db/schema.rb new file mode 100644 index 000000000..8dfd3581f --- /dev/null +++ b/db/schema.rb @@ -0,0 +1,40 @@ +# 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: 20170509201859) do + + # These are extensions that must be enabled in order to support this database + enable_extension "plpgsql" + + create_table "customers", force: :cascade do |t| + t.string "name" + t.string "registered_at" + t.string "address" + t.string "city" + t.string "state" + t.string "postal_code" + t.string "phone" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.float "account_credit" + end + + create_table "movies", force: :cascade do |t| + t.string "title" + t.text "overview" + t.string "release_date" + t.integer "inventory" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + +end diff --git a/test/controllers/customers_controller_test.rb b/test/controllers/customers_controller_test.rb new file mode 100644 index 000000000..5e123f6cd --- /dev/null +++ b/test/controllers/customers_controller_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +describe CustomersController do + # it "must be a real test" do + # flunk "Need real tests" + # end +end diff --git a/test/controllers/movies_controller_test.rb b/test/controllers/movies_controller_test.rb new file mode 100644 index 000000000..67fabbcfb --- /dev/null +++ b/test/controllers/movies_controller_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +describe MoviesController do + # it "must be a real test" do + # flunk "Need real tests" + # end +end diff --git a/test/fixtures/customers.yml b/test/fixtures/customers.yml new file mode 100644 index 000000000..dc3ee79b5 --- /dev/null +++ b/test/fixtures/customers.yml @@ -0,0 +1,11 @@ +# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +# This model initially had no columns defined. If you add columns to the +# model remove the "{}" from the fixture names and add the columns immediately +# below each fixture, per the syntax in the comments below +# +one: {} +# column: value +# +two: {} +# column: value diff --git a/test/fixtures/movies.yml b/test/fixtures/movies.yml new file mode 100644 index 000000000..dc3ee79b5 --- /dev/null +++ b/test/fixtures/movies.yml @@ -0,0 +1,11 @@ +# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +# This model initially had no columns defined. If you add columns to the +# model remove the "{}" from the fixture names and add the columns immediately +# below each fixture, per the syntax in the comments below +# +one: {} +# column: value +# +two: {} +# column: value diff --git a/test/models/customer_test.rb b/test/models/customer_test.rb new file mode 100644 index 000000000..5ebc5c850 --- /dev/null +++ b/test/models/customer_test.rb @@ -0,0 +1,9 @@ +require "test_helper" + +describe Customer do + let(:customer) { Customer.new } + + it "must be valid" do + value(customer).must_be :valid? + end +end diff --git a/test/models/movie_test.rb b/test/models/movie_test.rb new file mode 100644 index 000000000..34d1d30a5 --- /dev/null +++ b/test/models/movie_test.rb @@ -0,0 +1,9 @@ +require "test_helper" + +describe Movie do + let(:movie) { Movie.new } + + it "must be valid" do + value(movie).must_be :valid? + end +end From 3ccc5bc55814e421f7bcd885257d9e17c7a9528e Mon Sep 17 00:00:00 2001 From: add2point71dots Date: Tue, 9 May 2017 14:20:04 -0700 Subject: [PATCH 03/41] customer validations and tests --- app/models/customer.rb | 8 ++++++++ test/fixtures/customers.yml | 34 +++++++++++++++++++++++++++++----- test/models/customer_test.rb | 29 ++++++++++++++++++++++++++--- 3 files changed, 63 insertions(+), 8 deletions(-) diff --git a/app/models/customer.rb b/app/models/customer.rb index 0b5277335..6e1165681 100644 --- a/app/models/customer.rb +++ b/app/models/customer.rb @@ -1,2 +1,10 @@ class Customer < ApplicationRecord + validates :name, presence: true + validates :registered_at, presence: true + validates :address, presence: true + validates :city, presence: true + validates :state, presence: true + validates :postal_code, presence: true + validates :phone, presence: true + validates :account_credit, presence: true end diff --git a/test/fixtures/customers.yml b/test/fixtures/customers.yml index dc3ee79b5..eedf23031 100644 --- a/test/fixtures/customers.yml +++ b/test/fixtures/customers.yml @@ -4,8 +4,32 @@ # model remove the "{}" from the fixture names and add the columns immediately # below each fixture, per the syntax in the comments below # -one: {} -# column: value -# -two: {} -# column: value +one: + name: "Addie" + registered_at: "2017-05-09" + address: "123 Fake Street" + city: "Seattle" + state: "WA" + postal_code: "98123" + phone: "555-555-5555" + account_credit: 4.44 + +two: + name: "Ashton" + registered_at: "2017-05-09" + address: "987 Fake Ave" + city: "Seattle" + state: "WA" + postal_code: "98987" + phone: "555-555-4444" + account_credit: 3.14 + +three: + name: "Ada" + registered_at: "2010-03-21" + address: "111 Ada Street" + city: "Ada Town" + state: "OR" + postal_code: "11111" + phone: "555-555-1111" + account_credit: 0 diff --git a/test/models/customer_test.rb b/test/models/customer_test.rb index 5ebc5c850..a2ca77166 100644 --- a/test/models/customer_test.rb +++ b/test/models/customer_test.rb @@ -1,9 +1,32 @@ require "test_helper" describe Customer do - let(:customer) { Customer.new } + REQUIRED_FIELDS = %w(name registered_at address city state postal_code phone account_credit) - it "must be valid" do - value(customer).must_be :valid? + let(:new_customer) { Customer.new( + name: "Orange", + registered_at: "2015-05-09", + address: "123 Cool Street", + city: "Portland", + state: "OR", + postal_code: "22222", + phone: "555-555-5556", + account_credit: 1.23 + ) } + + describe "Validations" do + it "can create a customer" do + customer = new_customer + customer.must_be :valid? + customer.save.must_equal true + end + + REQUIRED_FIELDS.each do |field| + it "#{field} is required" do + new_customer[field] = nil + new_customer.valid?.must_equal false + new_customer.errors.messages.must_include field.to_sym + end + end end end From 392bcb518163c97dd19673e0b866b6e9bf7da035 Mon Sep 17 00:00:00 2001 From: add2point71dots Date: Tue, 9 May 2017 14:22:02 -0700 Subject: [PATCH 04/41] added simplecov --- Gemfile | 3 ++- Gemfile.lock | 8 ++++++++ test/test_helper.rb | 2 ++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index ba7ec62a3..3501799bb 100644 --- a/Gemfile +++ b/Gemfile @@ -50,6 +50,7 @@ end group :test do gem 'minitest-rails' gem 'minitest-reporters' + gem 'simplecov' end group :development, :test do @@ -63,4 +64,4 @@ gem 'omniauth' gem 'omniauth-github' gem 'omniauth-google-oauth2' gem 'foundation-rails' -gem 'httparty' \ No newline at end of file +gem 'httparty' diff --git a/Gemfile.lock b/Gemfile.lock index 664464c12..c4deef0e2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -61,6 +61,7 @@ GEM crack (0.4.3) safe_yaml (~> 1.0.0) debug_inspector (0.0.3) + docile (1.1.5) dotenv (2.2.1) dotenv-rails (2.2.1) dotenv (= 2.2.1) @@ -81,6 +82,7 @@ GEM httparty (0.14.0) multi_xml (>= 0.5.2) i18n (0.8.1) + json (2.0.2) jwt (1.5.6) listen (3.0.8) rb-fsevent (~> 0.9, >= 0.9.4) @@ -183,6 +185,11 @@ GEM ruby-progressbar (1.8.1) safe_yaml (1.0.4) sass (3.4.23) + simplecov (0.13.0) + docile (~> 1.1.0) + json (>= 1.8, < 3) + simplecov-html (~> 0.10.0) + simplecov-html (0.10.0) slop (3.6.0) spring (2.0.1) activesupport (>= 4.2) @@ -236,6 +243,7 @@ DEPENDENCIES puma (~> 3.0) rails (~> 5.0.2) rails-erd + simplecov spring spring-watcher-listen (~> 2.0.0) tzinfo-data diff --git a/test/test_helper.rb b/test/test_helper.rb index 10594a324..0d25fd43c 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -1,5 +1,7 @@ ENV["RAILS_ENV"] = "test" require File.expand_path("../../config/environment", __FILE__) +require "simplecov" +SimpleCov.start require "rails/test_help" require "minitest/rails" require "minitest/reporters" # for Colorized output From 05aa5bda194b28d7d92271d9e4772cee63428e34 Mon Sep 17 00:00:00 2001 From: add2point71dots Date: Tue, 9 May 2017 14:53:39 -0700 Subject: [PATCH 05/41] movie model validations and testing --- app/models/movie.rb | 4 ++++ test/fixtures/movies.yml | 22 ++++++++++++++++----- test/models/movie_test.rb | 40 ++++++++++++++++++++++++++++++++++++--- 3 files changed, 58 insertions(+), 8 deletions(-) diff --git a/app/models/movie.rb b/app/models/movie.rb index dc614df15..389113c4c 100644 --- a/app/models/movie.rb +++ b/app/models/movie.rb @@ -1,2 +1,6 @@ class Movie < ApplicationRecord + validates :title, presence: true, uniqueness: true + validates :release_date, presence: true + validates :inventory, presence: true + validates_numericality_of :inventory, only_integer: true, greater_than_or_equal_to: 0 end diff --git a/test/fixtures/movies.yml b/test/fixtures/movies.yml index dc3ee79b5..896f1f465 100644 --- a/test/fixtures/movies.yml +++ b/test/fixtures/movies.yml @@ -4,8 +4,20 @@ # model remove the "{}" from the fixture names and add the columns immediately # below each fixture, per the syntax in the comments below # -one: {} -# column: value -# -two: {} -# column: value +one: + title: "Hedwig and the Angry Inch" + overview: "Six inches forward and five inches back." + release_date: "2001-01-19" + inventory: 10 + +two: + title: "The Graduate" + overview: "One word: plastics." + release_date: "1967-12-22" + inventory: 6 + +three: + title: "Bye Bye Birdie" + overview: "Bye, bye, Birdie. I'm gonna miss you so. Bye, bye, Birdie. Why'd ya have to go?" + release_date: "1963-04-04" + inventory: 7 diff --git a/test/models/movie_test.rb b/test/models/movie_test.rb index 34d1d30a5..a35d1c374 100644 --- a/test/models/movie_test.rb +++ b/test/models/movie_test.rb @@ -1,9 +1,43 @@ require "test_helper" describe Movie do - let(:movie) { Movie.new } + let(:new_movie) { Movie.new( + title: "Bubble Boy", + overview: "Super cool movie about a boy in a bubble.", + release_date: "2001-08-24", + inventory: 10 + ) } - it "must be valid" do - value(movie).must_be :valid? + REQUIRED_FIELDS = %w(title release_date inventory) + + describe "validations" do + it "can create a movie" do + new_movie.must_be :valid? + new_movie.save.must_equal true + end + + REQUIRED_FIELDS.each do |field| + it "#{field} is required" do + new_movie[field] = nil + new_movie.valid?.must_equal false + new_movie.errors.messages.must_include field.to_sym + end + end + + it "inventory must be a non-negative integer" do + new_movie.inventory = "number" + new_movie.valid?.must_equal false + + new_movie.inventory = -1 + new_movie.valid?.must_equal false + + new_movie.inventory = 4.5 + new_movie.valid?.must_equal false + end + + it "title must be unique" do + new_movie.title = movies(:one).title + new_movie.valid?.must_equal false + end end end From f47b9772e72b560efd46e8eaf45f91ec196f964c Mon Sep 17 00:00:00 2001 From: add2point71dots Date: Tue, 9 May 2017 15:00:17 -0700 Subject: [PATCH 06/41] customer's account_credit must be a number --- app/models/customer.rb | 1 + test/models/customer_test.rb | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/app/models/customer.rb b/app/models/customer.rb index 6e1165681..a2fa72d69 100644 --- a/app/models/customer.rb +++ b/app/models/customer.rb @@ -7,4 +7,5 @@ class Customer < ApplicationRecord validates :postal_code, presence: true validates :phone, presence: true validates :account_credit, presence: true + validates_numericality_of :account_credit end diff --git a/test/models/customer_test.rb b/test/models/customer_test.rb index a2ca77166..5a93c1081 100644 --- a/test/models/customer_test.rb +++ b/test/models/customer_test.rb @@ -28,5 +28,10 @@ new_customer.errors.messages.must_include field.to_sym end end + + it "account_credit must be a number" do + new_customer.account_credit = "number" + new_customer.valid?.must_equal false + end end end From 292b1e23aa311884cb1f8d407de3af4afe227de6 Mon Sep 17 00:00:00 2001 From: add2point71dots Date: Tue, 9 May 2017 15:23:53 -0700 Subject: [PATCH 07/41] index method and tests for customers --- app/controllers/customers_controller.rb | 4 +++ test/controllers/customers_controller_test.rb | 32 +++++++++++++++++-- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/app/controllers/customers_controller.rb b/app/controllers/customers_controller.rb index ca3b6e024..4786b71e7 100644 --- a/app/controllers/customers_controller.rb +++ b/app/controllers/customers_controller.rb @@ -1,2 +1,6 @@ class CustomersController < ApplicationController + def index + customer = Customer.all + render json: customer.as_json(only: [:id, :name, :registered_at, :postal_code, :phone]) + end end diff --git a/test/controllers/customers_controller_test.rb b/test/controllers/customers_controller_test.rb index 5e123f6cd..9b4e21ed3 100644 --- a/test/controllers/customers_controller_test.rb +++ b/test/controllers/customers_controller_test.rb @@ -1,7 +1,33 @@ require "test_helper" describe CustomersController do - # it "must be a real test" do - # flunk "Need real tests" - # end + KEYS = %w(id name phone postal_code registered_at) + + describe "index" do + it "is a real working route" do + get customers_path + must_respond_with :success + end + + it "returns json" do + get customers_path + response.header['Content-Type'].must_include 'json' + end + + it "returns an Array of all the customers" do + get customers_path + body = JSON.parse(response.body) + + body.must_be_kind_of Array + body.length.must_equal Customer.count + end + + it "returns customers with exactly the required fields" do + get customers_path + body = JSON.parse(response.body) + body.each do |customer| + customer.keys.sort.must_equal KEYS + end + end + end end From d5b5b2b5977e1d4ca1a73a80fb68d80cf0f85295 Mon Sep 17 00:00:00 2001 From: add2point71dots Date: Tue, 9 May 2017 15:33:35 -0700 Subject: [PATCH 08/41] added movies_checked_out_count to customer model as a required field (plus tests) --- app/models/customer.rb | 2 ++ ...add_movies_checked_out_field_to_customer.rb | 5 +++++ db/schema.rb | 7 ++++--- test/models/customer_test.rb | 18 +++++++++++++++++- 4 files changed, 28 insertions(+), 4 deletions(-) create mode 100644 db/migrate/20170509222543_add_movies_checked_out_field_to_customer.rb diff --git a/app/models/customer.rb b/app/models/customer.rb index a2fa72d69..c9220747c 100644 --- a/app/models/customer.rb +++ b/app/models/customer.rb @@ -6,6 +6,8 @@ class Customer < ApplicationRecord validates :state, presence: true validates :postal_code, presence: true validates :phone, presence: true + validates :movies_checked_out_count, presence: true + validates_numericality_of :movies_checked_out_count, only_integer: true, greater_than_or_equal_to: 0 validates :account_credit, presence: true validates_numericality_of :account_credit end diff --git a/db/migrate/20170509222543_add_movies_checked_out_field_to_customer.rb b/db/migrate/20170509222543_add_movies_checked_out_field_to_customer.rb new file mode 100644 index 000000000..0662cb87e --- /dev/null +++ b/db/migrate/20170509222543_add_movies_checked_out_field_to_customer.rb @@ -0,0 +1,5 @@ +class AddMoviesCheckedOutFieldToCustomer < ActiveRecord::Migration[5.0] + def change + add_column :customers, :movies_checked_out_count, :integer, default: 0 + end +end diff --git a/db/schema.rb b/db/schema.rb index 8dfd3581f..d4bf25a1e 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170509201859) do +ActiveRecord::Schema.define(version: 20170509222543) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -23,9 +23,10 @@ t.string "state" t.string "postal_code" t.string "phone" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.float "account_credit" + t.integer "movies_checked_out_count", default: 0 end create_table "movies", force: :cascade do |t| diff --git a/test/models/customer_test.rb b/test/models/customer_test.rb index 5a93c1081..940eb4832 100644 --- a/test/models/customer_test.rb +++ b/test/models/customer_test.rb @@ -1,7 +1,7 @@ require "test_helper" describe Customer do - REQUIRED_FIELDS = %w(name registered_at address city state postal_code phone account_credit) + REQUIRED_FIELDS = %w(name registered_at address city state postal_code phone account_credit movies_checked_out_count) let(:new_customer) { Customer.new( name: "Orange", @@ -33,5 +33,21 @@ new_customer.account_credit = "number" new_customer.valid?.must_equal false end + + it "movies_checked_out_count must be a non-negative integer" do + new_customer.movies_checked_out_count = "number" + new_customer.valid?.must_equal false + + new_customer.movies_checked_out_count = 3.4 + new_customer.valid?.must_equal false + + new_customer.movies_checked_out_count = -5 + new_customer.valid?.must_equal false + end + + it "movies_checked_out_count defaults to 0" do + new_customer.save + new_customer.movies_checked_out_count.must_equal 0 + end end end From e7a35f8bf53591a196f838ad4961203aad9ea886 Mon Sep 17 00:00:00 2001 From: add2point71dots Date: Tue, 9 May 2017 15:37:48 -0700 Subject: [PATCH 09/41] gives movies_checked_out_count data in customers show --- app/controllers/customers_controller.rb | 2 +- test/controllers/customers_controller_test.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/customers_controller.rb b/app/controllers/customers_controller.rb index 4786b71e7..86c966a07 100644 --- a/app/controllers/customers_controller.rb +++ b/app/controllers/customers_controller.rb @@ -1,6 +1,6 @@ class CustomersController < ApplicationController def index customer = Customer.all - render json: customer.as_json(only: [:id, :name, :registered_at, :postal_code, :phone]) + render json: customer.as_json(only: [:id, :name, :registered_at, :postal_code, :phone, :movies_checked_out_count]) end end diff --git a/test/controllers/customers_controller_test.rb b/test/controllers/customers_controller_test.rb index 9b4e21ed3..cb84f9754 100644 --- a/test/controllers/customers_controller_test.rb +++ b/test/controllers/customers_controller_test.rb @@ -1,7 +1,7 @@ require "test_helper" describe CustomersController do - KEYS = %w(id name phone postal_code registered_at) + KEYS = %w(id movies_checked_out_count name phone postal_code registered_at) describe "index" do it "is a real working route" do From 3006e38bbc083f92fa9dc917ba2669b9ab134a21 Mon Sep 17 00:00:00 2001 From: add2point71dots Date: Tue, 9 May 2017 15:52:22 -0700 Subject: [PATCH 10/41] cleaned up code slightly --- test/models/customer_test.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/models/customer_test.rb b/test/models/customer_test.rb index 940eb4832..e7a59829c 100644 --- a/test/models/customer_test.rb +++ b/test/models/customer_test.rb @@ -16,9 +16,8 @@ describe "Validations" do it "can create a customer" do - customer = new_customer - customer.must_be :valid? - customer.save.must_equal true + new_customer.must_be :valid? + new_customer.save.must_equal true end REQUIRED_FIELDS.each do |field| From 7fa0fa1620dcb5a470a041ceda416defba34a796 Mon Sep 17 00:00:00 2001 From: add2point71dots Date: Tue, 9 May 2017 15:53:23 -0700 Subject: [PATCH 11/41] fixed confusing variable name --- app/controllers/customers_controller.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/customers_controller.rb b/app/controllers/customers_controller.rb index 86c966a07..013c9daa9 100644 --- a/app/controllers/customers_controller.rb +++ b/app/controllers/customers_controller.rb @@ -1,6 +1,6 @@ class CustomersController < ApplicationController def index - customer = Customer.all - render json: customer.as_json(only: [:id, :name, :registered_at, :postal_code, :phone, :movies_checked_out_count]) + customers = Customer.all + render json: customers.as_json(only: [:id, :name, :registered_at, :postal_code, :phone, :movies_checked_out_count]) end end From 07ebb0b7a07191fac250d5ad532eef6be5c542e2 Mon Sep 17 00:00:00 2001 From: add2point71dots Date: Tue, 9 May 2017 15:53:58 -0700 Subject: [PATCH 12/41] changed constant name so it wasn't the same across different tests --- test/controllers/customers_controller_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/controllers/customers_controller_test.rb b/test/controllers/customers_controller_test.rb index cb84f9754..b7f1e56b0 100644 --- a/test/controllers/customers_controller_test.rb +++ b/test/controllers/customers_controller_test.rb @@ -1,7 +1,7 @@ require "test_helper" describe CustomersController do - KEYS = %w(id movies_checked_out_count name phone postal_code registered_at) + CUSTOMER_KEYS = %w(id movies_checked_out_count name phone postal_code registered_at) describe "index" do it "is a real working route" do @@ -26,7 +26,7 @@ get customers_path body = JSON.parse(response.body) body.each do |customer| - customer.keys.sort.must_equal KEYS + customer.keys.sort.must_equal CUSTOMER_KEYS end end end From bb157a9faba6a232dc9f8c3219a77205f846df64 Mon Sep 17 00:00:00 2001 From: add2point71dots Date: Tue, 9 May 2017 15:54:09 -0700 Subject: [PATCH 13/41] movies index method and tests --- app/controllers/movies_controller.rb | 4 +++ test/controllers/movies_controller_test.rb | 33 ++++++++++++++++++++-- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/app/controllers/movies_controller.rb b/app/controllers/movies_controller.rb index 6c4c51614..119ddbc8b 100644 --- a/app/controllers/movies_controller.rb +++ b/app/controllers/movies_controller.rb @@ -1,2 +1,6 @@ class MoviesController < ApplicationController + def index + movies = Movie.all + render json: movies.as_json(only: [:title, :release_date]) + end end diff --git a/test/controllers/movies_controller_test.rb b/test/controllers/movies_controller_test.rb index 67fabbcfb..ecbd89d89 100644 --- a/test/controllers/movies_controller_test.rb +++ b/test/controllers/movies_controller_test.rb @@ -1,7 +1,34 @@ require "test_helper" describe MoviesController do - # it "must be a real test" do - # flunk "Need real tests" - # end + MOVIE_KEYS = %w(release_date title) + + describe "index" do + it "is a real working route" do + get movies_path + must_respond_with :success + end + + it "returns json" do + get movies_path + response.header['Content-Type'].must_include 'json' + end + + it "returns an Array of all the movies" do + get movies_path + body = JSON.parse(response.body) + + body.must_be_kind_of Array + body.length.must_equal Movie.count + end + + it "returns movies with exactly the required fields" do + get movies_path + body = JSON.parse(response.body) + + body.each do |movie| + movie.keys.sort.must_equal MOVIE_KEYS + end + end + end end From 04942374e9d168c0f0042b4ad45785161dac0c4c Mon Sep 17 00:00:00 2001 From: add2point71dots Date: Tue, 9 May 2017 16:26:26 -0700 Subject: [PATCH 14/41] movies show method and tests --- app/controllers/movies_controller.rb | 11 +++++- config/routes.rb | 2 +- test/controllers/movies_controller_test.rb | 45 +++++++++++++++++++++- 3 files changed, 54 insertions(+), 4 deletions(-) diff --git a/app/controllers/movies_controller.rb b/app/controllers/movies_controller.rb index 119ddbc8b..918cd9fb6 100644 --- a/app/controllers/movies_controller.rb +++ b/app/controllers/movies_controller.rb @@ -1,6 +1,15 @@ class MoviesController < ApplicationController def index movies = Movie.all - render json: movies.as_json(only: [:title, :release_date]) + render json: movies.as_json(only: [:title, :release_date]), status: :ok + end + + def show + movie = Movie.find_by(title: params[:title]) + if movie + render json: movie.as_json(only: [:inventory, :overview, :release_date, :title]), status: :ok + else + render json: { errors: { "title": ["Movie '#{params[:title]}' not found"] } }, status: :not_found + end end end diff --git a/config/routes.rb b/config/routes.rb index fe0c8acfc..7aa83e5aa 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -3,5 +3,5 @@ get '/customers', to: 'customers#index' get '/movies', to: 'movies#index' - get '/movies/:title', to: 'movies#show' + get '/movies/:title', to: 'movies#show', as: 'movie' end diff --git a/test/controllers/movies_controller_test.rb b/test/controllers/movies_controller_test.rb index ecbd89d89..4503122d1 100644 --- a/test/controllers/movies_controller_test.rb +++ b/test/controllers/movies_controller_test.rb @@ -1,7 +1,8 @@ require "test_helper" describe MoviesController do - MOVIE_KEYS = %w(release_date title) + MOVIE_INDEX_KEYS = %w(release_date title) + MOVIE_SHOW_KEYS = %w(inventory overview release_date title) describe "index" do it "is a real working route" do @@ -27,8 +28,48 @@ body = JSON.parse(response.body) body.each do |movie| - movie.keys.sort.must_equal MOVIE_KEYS + movie.keys.sort.must_equal MOVIE_INDEX_KEYS end end end + + describe "show" do + it "can get a movie" do + get movie_path(movies(:two).title) + must_respond_with :success + end + + it "returns a Hash in json" do + get movie_path(movies(:two).title) + body = JSON.parse(response.body) + + body.must_be_kind_of Hash + response.header['Content-Type'].must_include 'json' + end + + it "returns movie with exactly the required fields" do + get movie_path(movies(:three).title) + body = JSON.parse(response.body) + + body.keys.sort.must_equal MOVIE_SHOW_KEYS + end + + it "movie has the right information" do + get movie_path(movies(:one).title) + body = JSON.parse(response.body) + + MOVIE_SHOW_KEYS.each do |key| + body[key].must_equal movies(:one)[key] + end + end + + it "responds appropriately when given an invalid title" do + get movie_path("BadTitle") + body = JSON.parse(response.body) + + body.must_include "errors" + body["errors"]["title"].must_equal ["Movie 'BadTitle' not found"] + must_respond_with :not_found + end + end end From b3a790ff857c08dee0df7a6dfc33967d6e0adf02 Mon Sep 17 00:00:00 2001 From: add2point71dots Date: Tue, 9 May 2017 16:34:17 -0700 Subject: [PATCH 15/41] added available_inventory as a field and set it to the inventory number when created --- app/models/movie.rb | 8 ++++++++ db/migrate/20170509232829_add_available_inventory.rb | 5 +++++ db/schema.rb | 7 ++++--- 3 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 db/migrate/20170509232829_add_available_inventory.rb diff --git a/app/models/movie.rb b/app/models/movie.rb index 389113c4c..fe94670f6 100644 --- a/app/models/movie.rb +++ b/app/models/movie.rb @@ -1,6 +1,14 @@ class Movie < ApplicationRecord + before_create :set_available_inventory_default + validates :title, presence: true, uniqueness: true validates :release_date, presence: true validates :inventory, presence: true validates_numericality_of :inventory, only_integer: true, greater_than_or_equal_to: 0 + + private + + def set_available_inventory_default + self.available_inventory = self.inventory + end end diff --git a/db/migrate/20170509232829_add_available_inventory.rb b/db/migrate/20170509232829_add_available_inventory.rb new file mode 100644 index 000000000..cbf4b42f1 --- /dev/null +++ b/db/migrate/20170509232829_add_available_inventory.rb @@ -0,0 +1,5 @@ +class AddAvailableInventory < ActiveRecord::Migration[5.0] + def change + add_column :movies, :available_inventory, :integer + end +end diff --git a/db/schema.rb b/db/schema.rb index d4bf25a1e..f88eb6967 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170509222543) do +ActiveRecord::Schema.define(version: 20170509232829) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -34,8 +34,9 @@ t.text "overview" t.string "release_date" t.integer "inventory" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.integer "available_inventory" end end From a78854fac4d2dc15be4949c09b3802945cdba2aa Mon Sep 17 00:00:00 2001 From: add2point71dots Date: Tue, 9 May 2017 16:44:14 -0700 Subject: [PATCH 16/41] validations and tests on available_inventory --- app/models/movie.rb | 1 + test/models/movie_test.rb | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/app/models/movie.rb b/app/models/movie.rb index fe94670f6..459142712 100644 --- a/app/models/movie.rb +++ b/app/models/movie.rb @@ -5,6 +5,7 @@ class Movie < ApplicationRecord validates :release_date, presence: true validates :inventory, presence: true validates_numericality_of :inventory, only_integer: true, greater_than_or_equal_to: 0 + validates_numericality_of :available_inventory, only_integer: true, greater_than_or_equal_to: 0, less_than_or_equal_to: :inventory, allow_blank: true private diff --git a/test/models/movie_test.rb b/test/models/movie_test.rb index a35d1c374..50b8b9c35 100644 --- a/test/models/movie_test.rb +++ b/test/models/movie_test.rb @@ -35,9 +35,30 @@ new_movie.valid?.must_equal false end + it "available_inventory must be an integer between 0 and inventory" do + new_movie.available_inventory = "number" + new_movie.valid?.must_equal false + + new_movie.available_inventory = 1.5 + new_movie.valid?.must_equal false + + new_movie.available_inventory = -4 + new_movie.valid?.must_equal false + + new_movie.available_inventory = new_movie.inventory + 1 + new_movie.valid?.must_equal false + end + it "title must be unique" do new_movie.title = movies(:one).title new_movie.valid?.must_equal false end end + + describe "custom methods" do + it "available_inventory gets set to inventory by default" do + new_movie.save + new_movie.available_inventory.must_equal new_movie.inventory + end + end end From 4657fd70ec1a2f95491f69666477a36936ef7f15 Mon Sep 17 00:00:00 2001 From: add2point71dots Date: Tue, 9 May 2017 16:56:39 -0700 Subject: [PATCH 17/41] made available_inventory available in movies show --- app/controllers/movies_controller.rb | 2 +- test/controllers/movies_controller_test.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/movies_controller.rb b/app/controllers/movies_controller.rb index 918cd9fb6..d70cf4802 100644 --- a/app/controllers/movies_controller.rb +++ b/app/controllers/movies_controller.rb @@ -7,7 +7,7 @@ def index def show movie = Movie.find_by(title: params[:title]) if movie - render json: movie.as_json(only: [:inventory, :overview, :release_date, :title]), status: :ok + render json: movie.as_json(only: [:inventory, :overview, :release_date, :title, :available_inventory]), status: :ok else render json: { errors: { "title": ["Movie '#{params[:title]}' not found"] } }, status: :not_found end diff --git a/test/controllers/movies_controller_test.rb b/test/controllers/movies_controller_test.rb index 4503122d1..733b4c3ad 100644 --- a/test/controllers/movies_controller_test.rb +++ b/test/controllers/movies_controller_test.rb @@ -2,7 +2,7 @@ describe MoviesController do MOVIE_INDEX_KEYS = %w(release_date title) - MOVIE_SHOW_KEYS = %w(inventory overview release_date title) + MOVIE_SHOW_KEYS = %w(available_inventory inventory overview release_date title) describe "index" do it "is a real working route" do From 4a46faaa90b349db734f1c93d98e4bad4206269d Mon Sep 17 00:00:00 2001 From: Ashtn Date: Wed, 10 May 2017 10:58:23 -0700 Subject: [PATCH 18/41] adding erd.pdf/test pushing --- erd.pdf | Bin 0 -> 32580 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 erd.pdf diff --git a/erd.pdf b/erd.pdf new file mode 100644 index 0000000000000000000000000000000000000000..1c65e7d1ac0b664bbd8b29810bb89374f515d946 GIT binary patch literal 32580 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 BUCsai literal 0 HcmV?d00001 From e5a020b57620f5156aba3fd1bca652b243873037 Mon Sep 17 00:00:00 2001 From: Ashtn Date: Wed, 10 May 2017 11:11:11 -0700 Subject: [PATCH 19/41] updated customer and movie model --- ...0857_remove_movie_check_outand_avail_inventory.rb | 6 ++++++ db/schema.rb | 12 +++++------- 2 files changed, 11 insertions(+), 7 deletions(-) create mode 100644 db/migrate/20170510180857_remove_movie_check_outand_avail_inventory.rb diff --git a/db/migrate/20170510180857_remove_movie_check_outand_avail_inventory.rb b/db/migrate/20170510180857_remove_movie_check_outand_avail_inventory.rb new file mode 100644 index 000000000..b0ce179af --- /dev/null +++ b/db/migrate/20170510180857_remove_movie_check_outand_avail_inventory.rb @@ -0,0 +1,6 @@ +class RemoveMovieCheckOutandAvailInventory < ActiveRecord::Migration[5.0] + def change + remove_column :movies, :available_inventory + remove_column :customers, :movies_checked_out_count + end +end diff --git a/db/schema.rb b/db/schema.rb index f88eb6967..14277f4d5 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170509232829) do +ActiveRecord::Schema.define(version: 20170510180857) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -23,10 +23,9 @@ t.string "state" t.string "postal_code" t.string "phone" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.float "account_credit" - t.integer "movies_checked_out_count", default: 0 end create_table "movies", force: :cascade do |t| @@ -34,9 +33,8 @@ t.text "overview" t.string "release_date" t.integer "inventory" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.integer "available_inventory" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false end end From babc29c35b8ae42ffc9de0724c75a844674a9448 Mon Sep 17 00:00:00 2001 From: Ashtn Date: Wed, 10 May 2017 11:19:27 -0700 Subject: [PATCH 20/41] adjusted movie tests to account for removed schema column --- app/models/movie.rb | 8 ++------ test/models/movie_test.rb | 36 ++++++++++++++++++------------------ 2 files changed, 20 insertions(+), 24 deletions(-) diff --git a/app/models/movie.rb b/app/models/movie.rb index 459142712..cb06029b9 100644 --- a/app/models/movie.rb +++ b/app/models/movie.rb @@ -1,15 +1,11 @@ class Movie < ApplicationRecord - before_create :set_available_inventory_default + validates :title, presence: true, uniqueness: true validates :release_date, presence: true validates :inventory, presence: true validates_numericality_of :inventory, only_integer: true, greater_than_or_equal_to: 0 - validates_numericality_of :available_inventory, only_integer: true, greater_than_or_equal_to: 0, less_than_or_equal_to: :inventory, allow_blank: true - private - def set_available_inventory_default - self.available_inventory = self.inventory - end + end diff --git a/test/models/movie_test.rb b/test/models/movie_test.rb index 50b8b9c35..a07202d46 100644 --- a/test/models/movie_test.rb +++ b/test/models/movie_test.rb @@ -10,7 +10,7 @@ REQUIRED_FIELDS = %w(title release_date inventory) - describe "validations" do + describe "validations ===========" do it "can create a movie" do new_movie.must_be :valid? new_movie.save.must_equal true @@ -35,19 +35,19 @@ new_movie.valid?.must_equal false end - it "available_inventory must be an integer between 0 and inventory" do - new_movie.available_inventory = "number" - new_movie.valid?.must_equal false - - new_movie.available_inventory = 1.5 - new_movie.valid?.must_equal false - - new_movie.available_inventory = -4 - new_movie.valid?.must_equal false - - new_movie.available_inventory = new_movie.inventory + 1 - new_movie.valid?.must_equal false - end + # it "available_inventory must be an integer between 0 and inventory" do + # new_movie.available_inventory = "number" + # new_movie.valid?.must_equal false + # + # new_movie.available_inventory = 1.5 + # new_movie.valid?.must_equal false + # + # new_movie.available_inventory = -4 + # new_movie.valid?.must_equal false + # + # new_movie.available_inventory = new_movie.inventory + 1 + # new_movie.valid?.must_equal false + # end it "title must be unique" do new_movie.title = movies(:one).title @@ -56,9 +56,9 @@ end describe "custom methods" do - it "available_inventory gets set to inventory by default" do - new_movie.save - new_movie.available_inventory.must_equal new_movie.inventory - end + # it "available_inventory gets set to inventory by default" do + # new_movie.save + # new_movie.available_inventory.must_equal new_movie.inventory + # end end end From 14dd978219a707f20e7dcae4e2b1eb301b0787ec Mon Sep 17 00:00:00 2001 From: Ashtn Date: Wed, 10 May 2017 11:23:48 -0700 Subject: [PATCH 21/41] adjusted customer model test to account for removed schema column --- app/models/customer.rb | 2 -- test/models/customer_test.rb | 17 +---------------- 2 files changed, 1 insertion(+), 18 deletions(-) diff --git a/app/models/customer.rb b/app/models/customer.rb index c9220747c..a2fa72d69 100644 --- a/app/models/customer.rb +++ b/app/models/customer.rb @@ -6,8 +6,6 @@ class Customer < ApplicationRecord validates :state, presence: true validates :postal_code, presence: true validates :phone, presence: true - validates :movies_checked_out_count, presence: true - validates_numericality_of :movies_checked_out_count, only_integer: true, greater_than_or_equal_to: 0 validates :account_credit, presence: true validates_numericality_of :account_credit end diff --git a/test/models/customer_test.rb b/test/models/customer_test.rb index e7a59829c..9b5134cc3 100644 --- a/test/models/customer_test.rb +++ b/test/models/customer_test.rb @@ -1,7 +1,7 @@ require "test_helper" describe Customer do - REQUIRED_FIELDS = %w(name registered_at address city state postal_code phone account_credit movies_checked_out_count) + REQUIRED_FIELDS = %w(name registered_at address city state postal_code phone account_credit) let(:new_customer) { Customer.new( name: "Orange", @@ -33,20 +33,5 @@ new_customer.valid?.must_equal false end - it "movies_checked_out_count must be a non-negative integer" do - new_customer.movies_checked_out_count = "number" - new_customer.valid?.must_equal false - - new_customer.movies_checked_out_count = 3.4 - new_customer.valid?.must_equal false - - new_customer.movies_checked_out_count = -5 - new_customer.valid?.must_equal false - end - - it "movies_checked_out_count defaults to 0" do - new_customer.save - new_customer.movies_checked_out_count.must_equal 0 - end end end From 6de94ed1d40e5b6e74c06b3fd3f61adb7d14a156 Mon Sep 17 00:00:00 2001 From: Ashtn Date: Wed, 10 May 2017 11:28:45 -0700 Subject: [PATCH 22/41] adjusted movies and customers controller to account for removed schema columns --- app/controllers/customers_controller.rb | 2 +- app/controllers/movies_controller.rb | 2 +- test/controllers/customers_controller_test.rb | 2 +- test/controllers/movies_controller_test.rb | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/controllers/customers_controller.rb b/app/controllers/customers_controller.rb index 013c9daa9..24a056cd2 100644 --- a/app/controllers/customers_controller.rb +++ b/app/controllers/customers_controller.rb @@ -1,6 +1,6 @@ class CustomersController < ApplicationController def index customers = Customer.all - render json: customers.as_json(only: [:id, :name, :registered_at, :postal_code, :phone, :movies_checked_out_count]) + render json: customers.as_json(only: [:id, :name, :registered_at, :postal_code, :phone]) end end diff --git a/app/controllers/movies_controller.rb b/app/controllers/movies_controller.rb index d70cf4802..918cd9fb6 100644 --- a/app/controllers/movies_controller.rb +++ b/app/controllers/movies_controller.rb @@ -7,7 +7,7 @@ def index def show movie = Movie.find_by(title: params[:title]) if movie - render json: movie.as_json(only: [:inventory, :overview, :release_date, :title, :available_inventory]), status: :ok + render json: movie.as_json(only: [:inventory, :overview, :release_date, :title]), status: :ok else render json: { errors: { "title": ["Movie '#{params[:title]}' not found"] } }, status: :not_found end diff --git a/test/controllers/customers_controller_test.rb b/test/controllers/customers_controller_test.rb index b7f1e56b0..78f9c4a67 100644 --- a/test/controllers/customers_controller_test.rb +++ b/test/controllers/customers_controller_test.rb @@ -1,7 +1,7 @@ require "test_helper" describe CustomersController do - CUSTOMER_KEYS = %w(id movies_checked_out_count name phone postal_code registered_at) + CUSTOMER_KEYS = %w(id name phone postal_code registered_at) describe "index" do it "is a real working route" do diff --git a/test/controllers/movies_controller_test.rb b/test/controllers/movies_controller_test.rb index 733b4c3ad..4503122d1 100644 --- a/test/controllers/movies_controller_test.rb +++ b/test/controllers/movies_controller_test.rb @@ -2,7 +2,7 @@ describe MoviesController do MOVIE_INDEX_KEYS = %w(release_date title) - MOVIE_SHOW_KEYS = %w(available_inventory inventory overview release_date title) + MOVIE_SHOW_KEYS = %w(inventory overview release_date title) describe "index" do it "is a real working route" do From e03bf9d71ce1c905b0e680c5b42cbbba71f561cb Mon Sep 17 00:00:00 2001 From: Ashtn Date: Wed, 10 May 2017 12:43:39 -0700 Subject: [PATCH 23/41] added 3 rental routes --- config/routes.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/config/routes.rb b/config/routes.rb index 7aa83e5aa..af2c2ffdf 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -4,4 +4,10 @@ get '/movies', to: 'movies#index' get '/movies/:title', to: 'movies#show', as: 'movie' + + post '/rentals/:title/check-out', to: 'rentals#check_out', as: 'check_out' + + post '/rentals/:title/check-in', to: 'rentals#check_in', as: 'check_in' + + get '/rentals/overdue', to: 'rentals#overdue', as: 'overdue' end From 72a11e81d71b7fba26fcfb6bf2ffee6c6f1db4f5 Mon Sep 17 00:00:00 2001 From: Ashtn Date: Wed, 10 May 2017 13:01:21 -0700 Subject: [PATCH 24/41] added rental model --- app/models/rental.rb | 2 ++ db/migrate/20170510194600_create_rentals.rb | 12 ++++++++++++ db/schema.rb | 13 ++++++++++++- test/fixtures/rentals.yml | 11 +++++++++++ test/models/rental_test.rb | 9 +++++++++ 5 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 app/models/rental.rb create mode 100644 db/migrate/20170510194600_create_rentals.rb create mode 100644 test/fixtures/rentals.yml create mode 100644 test/models/rental_test.rb diff --git a/app/models/rental.rb b/app/models/rental.rb new file mode 100644 index 000000000..79e3a65ca --- /dev/null +++ b/app/models/rental.rb @@ -0,0 +1,2 @@ +class Rental < ApplicationRecord +end diff --git a/db/migrate/20170510194600_create_rentals.rb b/db/migrate/20170510194600_create_rentals.rb new file mode 100644 index 000000000..cd9cb82d2 --- /dev/null +++ b/db/migrate/20170510194600_create_rentals.rb @@ -0,0 +1,12 @@ +class CreateRentals < ActiveRecord::Migration[5.0] + def change + create_table :rentals do |t| + t.belongs_to :movie, index: true + t.belongs_to :customer, index: true + t.datetime :check_out + t.datetime :check_in + t.timestamps + + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 14277f4d5..cac4630bf 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170510180857) do +ActiveRecord::Schema.define(version: 20170510194600) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -37,4 +37,15 @@ t.datetime "updated_at", null: false end + create_table "rentals", force: :cascade do |t| + t.integer "movie_id" + t.integer "customer_id" + t.datetime "check_out" + t.datetime "check_in" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["customer_id"], name: "index_rentals_on_customer_id", using: :btree + t.index ["movie_id"], name: "index_rentals_on_movie_id", using: :btree + end + end diff --git a/test/fixtures/rentals.yml b/test/fixtures/rentals.yml new file mode 100644 index 000000000..dc3ee79b5 --- /dev/null +++ b/test/fixtures/rentals.yml @@ -0,0 +1,11 @@ +# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +# This model initially had no columns defined. If you add columns to the +# model remove the "{}" from the fixture names and add the columns immediately +# below each fixture, per the syntax in the comments below +# +one: {} +# column: value +# +two: {} +# column: value diff --git a/test/models/rental_test.rb b/test/models/rental_test.rb new file mode 100644 index 000000000..6ea53d94f --- /dev/null +++ b/test/models/rental_test.rb @@ -0,0 +1,9 @@ +require "test_helper" + +describe Rental do + let(:rental) { Rental.new } + + it "must be valid" do + value(rental).must_be :valid? + end +end From 8a1a9d3566994f3275da3556938d62432418407c Mon Sep 17 00:00:00 2001 From: Ashtn Date: Wed, 10 May 2017 13:26:01 -0700 Subject: [PATCH 25/41] added a column i forgot whoooops! --- db/migrate/20170510202328_add_due_date_to_rental.rb | 5 +++++ db/schema.rb | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20170510202328_add_due_date_to_rental.rb diff --git a/db/migrate/20170510202328_add_due_date_to_rental.rb b/db/migrate/20170510202328_add_due_date_to_rental.rb new file mode 100644 index 000000000..3d61ccbae --- /dev/null +++ b/db/migrate/20170510202328_add_due_date_to_rental.rb @@ -0,0 +1,5 @@ +class AddDueDateToRental < ActiveRecord::Migration[5.0] + def change + add_column :rentals, :due_date, :datetime + end +end diff --git a/db/schema.rb b/db/schema.rb index cac4630bf..36dad799d 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170510194600) do +ActiveRecord::Schema.define(version: 20170510202328) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -44,6 +44,7 @@ t.datetime "check_in" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.datetime "due_date" t.index ["customer_id"], name: "index_rentals_on_customer_id", using: :btree t.index ["movie_id"], name: "index_rentals_on_movie_id", using: :btree end From bd94fe34683fb356e8f70655f96801dec94c432a Mon Sep 17 00:00:00 2001 From: Ashtn Date: Wed, 10 May 2017 13:58:35 -0700 Subject: [PATCH 26/41] added model tests for rental --- app/models/rental.rb | 3 +++ test/models/rental_test.rb | 51 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/app/models/rental.rb b/app/models/rental.rb index 79e3a65ca..167868caf 100644 --- a/app/models/rental.rb +++ b/app/models/rental.rb @@ -1,2 +1,5 @@ class Rental < ApplicationRecord + belongs_to :movie + belongs_to :customer + validates :due_date, presence: true end diff --git a/test/models/rental_test.rb b/test/models/rental_test.rb index 6ea53d94f..df7cc9314 100644 --- a/test/models/rental_test.rb +++ b/test/models/rental_test.rb @@ -2,8 +2,55 @@ describe Rental do let(:rental) { Rental.new } + let(:rental2) {Rental.new(customer_id: customers(:one).id, movie_id: movies(:one).id, due_date: "2017-05-10" )} - it "must be valid" do - value(rental).must_be :valid? + RENTAL_VALIDATIONS = %w( customer_id movie_id due_date) + + it ' Will create a new rental with valid inputs' do + rental2.save.must_equal true + rental2.customer_id.must_equal customers(:one).id + rental2.movie_id.must_equal movies(:one).id + rental2.due_date.must_equal DateTime.parse("2017-05-10") + end + + + it " Won't create a rental without validated information" do + rental.save.must_equal false + end + + + it " Customers must be present " do + rental2.customer_id = nil + rental2.valid?.must_equal false + rental2.errors.messages.must_include :customer + end + + it " Movie must be present " do + rental2.movie_id = nil + rental2.valid?.must_equal false + rental2.errors.messages.must_include :movie end + + it " Due date must be present " do + rental2.due_date = nil + rental2.valid?.must_equal false + rental2.errors.messages.must_include :due_date + end + + describe "Rental Relationships" do + + it "Has access to customer object" do + rental2.customer.must_be_instance_of Customer + rental2.customer.phone.present?.must_equal true + rental2.customer.phone.must_equal customers(:one).phone + end + + it " Has access to movie object" do + rental2.movie.must_be_instance_of Movie + rental2.movie.title.present?.must_equal true + rental2.movie.title.must_equal movies(:one).title + end + + end + end From cdaea5cfc180411194f6d7ccd2496b4a037e5323 Mon Sep 17 00:00:00 2001 From: Ashtn Date: Wed, 10 May 2017 15:00:03 -0700 Subject: [PATCH 27/41] added available_inventory to model + tests, and check_out method rental model + tests --- app/models/movie.rb | 7 +++++++ app/models/rental.rb | 10 ++++++++++ test/fixtures/rentals.yml | 25 +++++++++++++++++++++++-- test/models/movie_test.rb | 38 +++++++++++++++++++------------------- test/models/rental_test.rb | 18 +++++++++++++++++- 5 files changed, 76 insertions(+), 22 deletions(-) diff --git a/app/models/movie.rb b/app/models/movie.rb index cb06029b9..ce4d38272 100644 --- a/app/models/movie.rb +++ b/app/models/movie.rb @@ -7,5 +7,12 @@ class Movie < ApplicationRecord validates_numericality_of :inventory, only_integer: true, greater_than_or_equal_to: 0 + def available_inventory + # TODO Fix this + rentals = Rental.checked_out(id) + whats_left = inventory - rentals.length + return whats_left + end + end diff --git a/app/models/rental.rb b/app/models/rental.rb index 167868caf..bfabf3af8 100644 --- a/app/models/rental.rb +++ b/app/models/rental.rb @@ -2,4 +2,14 @@ class Rental < ApplicationRecord belongs_to :movie belongs_to :customer validates :due_date, presence: true + + + def self.checked_out(movie_id) + if Movie.find_by_id(movie_id) + rentals = self.where(movie_id: movie_id) + return rentals.where(check_in: nil) + else + return nil + end + end end diff --git a/test/fixtures/rentals.yml b/test/fixtures/rentals.yml index dc3ee79b5..a251b1077 100644 --- a/test/fixtures/rentals.yml +++ b/test/fixtures/rentals.yml @@ -4,8 +4,29 @@ # model remove the "{}" from the fixture names and add the columns immediately # below each fixture, per the syntax in the comments below # -one: {} +one: { + customer: one, + movie: one, + due_date: 2017-05-09 + } # column: value # -two: {} +two: { + customer: one, + movie: two, + due_date: 2017-05-11 + } + +three: { + customer: two, + movie: two, + due_date: 2017-05-08, + check_in: 2017-05-08 + } + +four: { + customer: two, + movie: one, + due_date: 2017-05-08 + } # column: value diff --git a/test/models/movie_test.rb b/test/models/movie_test.rb index a07202d46..9ffa7ddbe 100644 --- a/test/models/movie_test.rb +++ b/test/models/movie_test.rb @@ -10,7 +10,7 @@ REQUIRED_FIELDS = %w(title release_date inventory) - describe "validations ===========" do + describe " Validations" do it "can create a movie" do new_movie.must_be :valid? new_movie.save.must_equal true @@ -35,19 +35,6 @@ new_movie.valid?.must_equal false end - # it "available_inventory must be an integer between 0 and inventory" do - # new_movie.available_inventory = "number" - # new_movie.valid?.must_equal false - # - # new_movie.available_inventory = 1.5 - # new_movie.valid?.must_equal false - # - # new_movie.available_inventory = -4 - # new_movie.valid?.must_equal false - # - # new_movie.available_inventory = new_movie.inventory + 1 - # new_movie.valid?.must_equal false - # end it "title must be unique" do new_movie.title = movies(:one).title @@ -55,10 +42,23 @@ end end - describe "custom methods" do - # it "available_inventory gets set to inventory by default" do - # new_movie.save - # new_movie.available_inventory.must_equal new_movie.inventory - # end + describe " Custom methods" do + + it " Returns number of available movies for rent" do + movies(:one).available_inventory.must_equal 8 + + end + + it " Returns total inventory count when the movie has yet to be checked out" do + + movies(:three).available_inventory.must_equal movies(:three).inventory + end + + it " Returns correct number of available_inventory when movie has check outs that have been returned." do + + movies(:two).available_inventory.must_equal 5 + + end + end end diff --git a/test/models/rental_test.rb b/test/models/rental_test.rb index df7cc9314..4e9c4e728 100644 --- a/test/models/rental_test.rb +++ b/test/models/rental_test.rb @@ -50,7 +50,23 @@ rental2.movie.title.present?.must_equal true rental2.movie.title.must_equal movies(:one).title end - end + describe "custom methods" do + + it ' Returns list of checked out movies based on movie id ' do + Rental.checked_out(movies(:one).id).length.must_equal 2 + Rental.checked_out(movies(:one).id).must_include rentals(:one) + end + + it ' Returns empty array when no movie(s) is/are checked out match the id that was passed ' do + + Rental.checked_out(movies(:three).id).must_equal [] + end + + it 'returns nil when no id matches ' do + + Rental.checked_out(Movie.last.id + 1).must_equal nil + end + end end From d9aa2b2d0e4cd56e1f45e17748c223a8d5f22792 Mon Sep 17 00:00:00 2001 From: Ashtn Date: Wed, 10 May 2017 15:21:10 -0700 Subject: [PATCH 28/41] added movie_checked_out_count model method + test --- app/models/customer.rb | 7 ++++++ app/models/movie.rb | 2 +- app/models/rental.rb | 13 ++++++++-- test/models/customer_test.rb | 46 ++++++++++++++++++++++++------------ test/models/rental_test.rb | 8 +++---- 5 files changed, 54 insertions(+), 22 deletions(-) diff --git a/app/models/customer.rb b/app/models/customer.rb index a2fa72d69..ebd56175a 100644 --- a/app/models/customer.rb +++ b/app/models/customer.rb @@ -8,4 +8,11 @@ class Customer < ApplicationRecord validates :phone, presence: true validates :account_credit, presence: true validates_numericality_of :account_credit + + + def movie_checked_out_count + # TODO: Fix this + rentals = Rental.checked_out_by_customer(id) + return rentals.count + end end diff --git a/app/models/movie.rb b/app/models/movie.rb index ce4d38272..85dee7090 100644 --- a/app/models/movie.rb +++ b/app/models/movie.rb @@ -9,7 +9,7 @@ class Movie < ApplicationRecord def available_inventory # TODO Fix this - rentals = Rental.checked_out(id) + rentals = Rental.checked_out_by_movie(id) whats_left = inventory - rentals.length return whats_left end diff --git a/app/models/rental.rb b/app/models/rental.rb index bfabf3af8..1eef45d1b 100644 --- a/app/models/rental.rb +++ b/app/models/rental.rb @@ -4,11 +4,20 @@ class Rental < ApplicationRecord validates :due_date, presence: true - def self.checked_out(movie_id) + def self.checked_out_by_movie(movie_id) if Movie.find_by_id(movie_id) rentals = self.where(movie_id: movie_id) return rentals.where(check_in: nil) - else + else + return nil + end + end + + def self.checked_out_by_customer(customer_id) + if Customer.find_by_id(customer_id) + rentals = self.where(customer_id: customer_id) + return rentals.where(check_in: nil) + else return nil end end diff --git a/test/models/customer_test.rb b/test/models/customer_test.rb index 9b5134cc3..da3d2fd22 100644 --- a/test/models/customer_test.rb +++ b/test/models/customer_test.rb @@ -12,26 +12,42 @@ postal_code: "22222", phone: "555-555-5556", account_credit: 1.23 - ) } + ) } - describe "Validations" do - it "can create a customer" do - new_customer.must_be :valid? - new_customer.save.must_equal true - end + describe "Validations" do + it "can create a customer" do + new_customer.must_be :valid? + new_customer.save.must_equal true + end + + REQUIRED_FIELDS.each do |field| + it "#{field} is required" do + new_customer[field] = nil + new_customer.valid?.must_equal false + new_customer.errors.messages.must_include field.to_sym + end + end - REQUIRED_FIELDS.each do |field| - it "#{field} is required" do - new_customer[field] = nil + it " Account_credit must be a number" do + new_customer.account_credit = "number" new_customer.valid?.must_equal false - new_customer.errors.messages.must_include field.to_sym end - end - it "account_credit must be a number" do - new_customer.account_credit = "number" - new_customer.valid?.must_equal false end + describe " Custom Methods" do + + it " Returns number of movies checked out by customer instance" do + customers(:one).movie_checked_out_count.must_equal 2 + end + + it " Returns 0 if customer has not checked out any movies" do + customers(:three).movie_checked_out_count.must_equal 0 + end + + it " Retuns correct number when customer has checked in rentals " do + customers(:two).movie_checked_out_count.must_equal 1 + end + + end end -end diff --git a/test/models/rental_test.rb b/test/models/rental_test.rb index 4e9c4e728..a9f24947f 100644 --- a/test/models/rental_test.rb +++ b/test/models/rental_test.rb @@ -55,18 +55,18 @@ describe "custom methods" do it ' Returns list of checked out movies based on movie id ' do - Rental.checked_out(movies(:one).id).length.must_equal 2 - Rental.checked_out(movies(:one).id).must_include rentals(:one) + Rental.checked_out_by_movie(movies(:one).id).length.must_equal 2 + Rental.checked_out_by_movie(movies(:one).id).must_include rentals(:one) end it ' Returns empty array when no movie(s) is/are checked out match the id that was passed ' do - Rental.checked_out(movies(:three).id).must_equal [] + Rental.checked_out_by_movie(movies(:three).id).must_equal [] end it 'returns nil when no id matches ' do - Rental.checked_out(Movie.last.id + 1).must_equal nil + Rental.checked_out_by_movie(Movie.last.id + 1).must_equal nil end end end From e70914293c4538da40b2d72621e2658a2f7df5ee Mon Sep 17 00:00:00 2001 From: Ashtn Date: Wed, 10 May 2017 15:25:43 -0700 Subject: [PATCH 29/41] added test for find rentals by customer_id --- test/models/rental_test.rb | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/models/rental_test.rb b/test/models/rental_test.rb index a9f24947f..f5c4e7c76 100644 --- a/test/models/rental_test.rb +++ b/test/models/rental_test.rb @@ -68,5 +68,20 @@ Rental.checked_out_by_movie(Movie.last.id + 1).must_equal nil end + + it ' Returns list of checked out movies based on customer id ' do + Rental.checked_out_by_customer(customers(:one).id).length.must_equal 2 + Rental.checked_out_by_customer(customers(:one).id).must_include rentals(:one) + end + + it ' Returns empty array when no moives(s) is/are checked out match the customer id that was passed ' do + + Rental.checked_out_by_customer(customers(:three).id).must_equal [] + end + + it 'returns nil when no id matches ' do + + Rental.checked_out_by_customer(Customer.last.id + 1).must_equal nil + end end end From 240d59f180e3534b99c883214e152d461ece8a76 Mon Sep 17 00:00:00 2001 From: Ashtn Date: Wed, 10 May 2017 15:43:52 -0700 Subject: [PATCH 30/41] added custom methods to json output in controller --- app/controllers/customers_controller.rb | 2 +- app/controllers/movies_controller.rb | 2 +- app/models/rental.rb | 2 +- test/controllers/customers_controller_test.rb | 2 +- test/controllers/movies_controller_test.rb | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/controllers/customers_controller.rb b/app/controllers/customers_controller.rb index 24a056cd2..68dfe4835 100644 --- a/app/controllers/customers_controller.rb +++ b/app/controllers/customers_controller.rb @@ -1,6 +1,6 @@ class CustomersController < ApplicationController def index customers = Customer.all - render json: customers.as_json(only: [:id, :name, :registered_at, :postal_code, :phone]) + render json: customers.as_json(only: [:id, :name, :registered_at, :postal_code, :phone], methods: :movie_checked_out_count ), status: :ok end end diff --git a/app/controllers/movies_controller.rb b/app/controllers/movies_controller.rb index 918cd9fb6..a42453a02 100644 --- a/app/controllers/movies_controller.rb +++ b/app/controllers/movies_controller.rb @@ -7,7 +7,7 @@ def index def show movie = Movie.find_by(title: params[:title]) if movie - render json: movie.as_json(only: [:inventory, :overview, :release_date, :title]), status: :ok + render json: movie.as_json(only: [:inventory, :overview, :release_date, :title], methods: :available_inventory), status: :ok else render json: { errors: { "title": ["Movie '#{params[:title]}' not found"] } }, status: :not_found end diff --git a/app/models/rental.rb b/app/models/rental.rb index 1eef45d1b..b50d3beef 100644 --- a/app/models/rental.rb +++ b/app/models/rental.rb @@ -3,7 +3,7 @@ class Rental < ApplicationRecord belongs_to :customer validates :due_date, presence: true - +private def self.checked_out_by_movie(movie_id) if Movie.find_by_id(movie_id) rentals = self.where(movie_id: movie_id) diff --git a/test/controllers/customers_controller_test.rb b/test/controllers/customers_controller_test.rb index 78f9c4a67..88ed08cc2 100644 --- a/test/controllers/customers_controller_test.rb +++ b/test/controllers/customers_controller_test.rb @@ -1,7 +1,7 @@ require "test_helper" describe CustomersController do - CUSTOMER_KEYS = %w(id name phone postal_code registered_at) + CUSTOMER_KEYS = %w(id movie_checked_out_count name phone postal_code registered_at) describe "index" do it "is a real working route" do diff --git a/test/controllers/movies_controller_test.rb b/test/controllers/movies_controller_test.rb index 4503122d1..2fcb1bc55 100644 --- a/test/controllers/movies_controller_test.rb +++ b/test/controllers/movies_controller_test.rb @@ -2,7 +2,7 @@ describe MoviesController do MOVIE_INDEX_KEYS = %w(release_date title) - MOVIE_SHOW_KEYS = %w(inventory overview release_date title) + MOVIE_SHOW_KEYS = %w(available_inventory inventory overview release_date title) describe "index" do it "is a real working route" do @@ -58,7 +58,7 @@ get movie_path(movies(:one).title) body = JSON.parse(response.body) - MOVIE_SHOW_KEYS.each do |key| + MOVIE_SHOW_KEYS[1..-1].each do |key| body[key].must_equal movies(:one)[key] end end From a67b6dc1075807bd5b1bcf35d1b51c85aee63e97 Mon Sep 17 00:00:00 2001 From: Ashtn Date: Fri, 12 May 2017 11:10:04 -0700 Subject: [PATCH 31/41] created check out action method plus tests --- app/controllers/rentals_controller.rb | 49 ++++++++++++++ test/controllers/rentals_controller_test.rb | 73 +++++++++++++++++++++ test/fixtures/movies.yml | 6 ++ 3 files changed, 128 insertions(+) create mode 100644 app/controllers/rentals_controller.rb create mode 100644 test/controllers/rentals_controller_test.rb diff --git a/app/controllers/rentals_controller.rb b/app/controllers/rentals_controller.rb new file mode 100644 index 000000000..9bac6293f --- /dev/null +++ b/app/controllers/rentals_controller.rb @@ -0,0 +1,49 @@ +class RentalsController < ApplicationController + + def check_out + movie = Movie.find_by_title(params[:title]) + + if !movie + + render status: :bad_request, json: { errors: "Sorry we do not currently carry #{params[:title]}"} + + elsif movie.available_inventory > 0 + + rental = Rental.new(rental_params) + rental.movie_id = movie.id + rental.check_out = DateTime.now + + if rental.save + render status: :ok, json: { + id: rental.id, + due_date: rental.due_date + } + else + render status: :bad_request, json: {errors: rental.errors.messages} + end + + else + render status: :bad_request, json: {errors: "Sorry, #{params[:title]} is not available at this moment" } + end + end + + def check_in + + # params[:customer_id] + # + # movie = Movie.find_by_title(params[:title]) + + + #update rental model check_in column + + #find rental where customer_id = params and movie = movies + end + + def overdue + end + + private + def rental_params + params.require(:rental).permit(:customer_id, :due_date) + end +end diff --git a/test/controllers/rentals_controller_test.rb b/test/controllers/rentals_controller_test.rb new file mode 100644 index 000000000..2924ea768 --- /dev/null +++ b/test/controllers/rentals_controller_test.rb @@ -0,0 +1,73 @@ +require "test_helper" + +describe RentalsController do + + + describe "check-out" do + + let(:rental_data) { + {customer_id: customers(:one).id, + due_date: "2017-05-13"} + } + + it " Can check out a movie" do + + proc { + post check_out_path( movies(:one).title), params: { rental: rental_data } + }.must_change 'Rental.count', 1 + + must_respond_with :success + + Rental.last.customer_id.must_equal rental_data[:customer_id] + Rental.last.due_date.must_equal DateTime.parse(rental_data[:due_date]) + Rental.last.check_out.wont_be_nil + Rental.last.movie.must_equal movies(:one) + end + + it "available_inventory is adjusted when movie is checked out" do + proc { + post check_out_path( movies(:one).title), params: { rental: rental_data } + }.must_change 'movies(:one).available_inventory', -1 + + end + + it " Won't change rental count if missing required params " do + rental_data[:due_date] = nil + + proc { + post check_out_path( movies(:one).title), params: { rental: rental_data } + }.wont_change 'Rental.count' + + must_respond_with :bad_request + body = JSON.parse(response.body) + body.must_include "errors" + body['errors'].must_include "due_date" + end + + it " Retuns bad_request if given a movie that doesn't exist " do + + proc { + post check_out_path( "Mean Girls"), params: { rental: rental_data } + }.wont_change 'Rental.count' + + must_respond_with :bad_request + body = JSON.parse(response.body) + body.must_include "errors" + body['errors'].must_equal "Sorry we do not currently carry Mean Girls" + end + + it " Won't change rental count if available_inventory is 0 " do + + proc { + post check_out_path( movies(:four).title), params: { rental: rental_data } + }.wont_change 'Rental.count' + + must_respond_with :bad_request + body = JSON.parse(response.body) + body.must_include "errors" + body['errors'].must_equal "Sorry, #{movies(:four).title} is not available at this moment" + movies(:four).available_inventory.must_equal 0 + end + end + +end diff --git a/test/fixtures/movies.yml b/test/fixtures/movies.yml index 896f1f465..c37308f88 100644 --- a/test/fixtures/movies.yml +++ b/test/fixtures/movies.yml @@ -21,3 +21,9 @@ three: overview: "Bye, bye, Birdie. I'm gonna miss you so. Bye, bye, Birdie. Why'd ya have to go?" release_date: "1963-04-04" inventory: 7 + +four: + title: "War Games" + overview: " Keep Humans in the Loop, please" + release_date: "1983-06-03" + inventory: 0 From 7329c240bf912b7f2f6cd6c8d99549036ac6188e Mon Sep 17 00:00:00 2001 From: add2point71dots Date: Fri, 12 May 2017 11:30:32 -0700 Subject: [PATCH 32/41] wrote check_in method --- app/controllers/rentals_controller.rb | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/app/controllers/rentals_controller.rb b/app/controllers/rentals_controller.rb index 9bac6293f..a83b25509 100644 --- a/app/controllers/rentals_controller.rb +++ b/app/controllers/rentals_controller.rb @@ -28,7 +28,23 @@ def check_out end def check_in + movie = Movie.find_by_title(params[:title]) + if !movie + render status: :bad_request, json: { errors: "Sorry we do not currently carry #{params[:title]}"} + else + rentals = Rental.checked_out_by_customer(params[:rental][:customer_id]) + rental = rentals.find_by(movie_id: movie.id) + if rental + rental.check_in = DateTime.now + render status: :ok, json: { + id: rental.id, + check_in: rental.check_in + } + else + render status: :bad_request, json: { error: "This customer does not have this movie checked out." } + end + end # params[:customer_id] # # movie = Movie.find_by_title(params[:title]) From d68879d56a76963e4bf033619f61f099c759d264 Mon Sep 17 00:00:00 2001 From: add2point71dots Date: Fri, 12 May 2017 13:58:34 -0700 Subject: [PATCH 33/41] tests for check_in for rentals controller --- app/controllers/rentals_controller.rb | 3 +- test/controllers/rentals_controller_test.rb | 74 +++++++++++++++++++++ 2 files changed, 76 insertions(+), 1 deletion(-) diff --git a/app/controllers/rentals_controller.rb b/app/controllers/rentals_controller.rb index a83b25509..3b7508b99 100644 --- a/app/controllers/rentals_controller.rb +++ b/app/controllers/rentals_controller.rb @@ -37,12 +37,13 @@ def check_in if rental rental.check_in = DateTime.now + rental.save render status: :ok, json: { id: rental.id, check_in: rental.check_in } else - render status: :bad_request, json: { error: "This customer does not have this movie checked out." } + render status: :bad_request, json: { errors: "This customer does not have this movie checked out." } end end # params[:customer_id] diff --git a/test/controllers/rentals_controller_test.rb b/test/controllers/rentals_controller_test.rb index 2924ea768..96cf8b238 100644 --- a/test/controllers/rentals_controller_test.rb +++ b/test/controllers/rentals_controller_test.rb @@ -70,4 +70,78 @@ end end + describe "check-in" do + let(:rental_data) { + {customer_id: customers(:one).id, + due_date: "2017-05-13"} + } + + it "can return a movie" do + post check_out_path(movies(:three).title), params: { rental: rental_data } + proc { + post check_in_path(movies(:three).title), params: { rental: { customer_id: rental_data[:customer_id] } } + }.wont_change 'Rental.count' + + must_respond_with :success + + body = JSON.parse(response.body) + body.must_include "id" + body.must_include "check_in" + + Rental.last.check_in.wont_be_nil + end + + it "available inventory goes up by one after check in" do + post check_out_path(movies(:three).title), params: { rental: rental_data } + + proc { + post check_in_path(movies(:three).title), params: { rental: { customer_id: rental_data[:customer_id] } } + }.must_change 'movies(:three).available_inventory', 1 + end + + it "cannot check in a movie that hasn't been checked out" do + proc { + post check_in_path(movies(:three).title), params: { rental: { customer_id: rental_data[:customer_id] } } + }.wont_change 'movies(:three).available_inventory' + + must_respond_with :bad_request + + body = JSON.parse(response.body) + body.must_include "errors" + body["errors"].must_equal "This customer does not have this movie checked out." + end + + it "cannot check in a movie that's checked out by a different customer" do + proc { + post check_in_path(movies(:two).title), params: { rental: { customer_id: customers(:three).id } } + }.wont_change 'movies(:two).available_inventory' + + must_respond_with :bad_request + + body = JSON.parse(response.body) + body.must_include "errors" + body["errors"].must_equal "This customer does not have this movie checked out." + end + + it "cannot check in a movie that doesn't exist" do + post check_in_path("MOVIE THAT DOESN'T EXIST"), params: { rental: { customer_id: customers(:three).id } } + must_respond_with :bad_request + + body = JSON.parse(response.body) + body.must_include "errors" + body["errors"].must_equal "Sorry we do not currently carry MOVIE THAT DOESN'T EXIST" + end + + it "if customer has same movie checked out more than once, it only checks in once" do + proc { + post check_out_path(movies(:one).title), params: { rental: rental_data } + rental_data[:due_date] = "2017-05-31" + post check_out_path(movies(:one).title), params: { rental: rental_data } + }.must_change 'movies(:one).available_inventory', -2 + + proc { + post check_in_path(movies(:one).title), params: { rental: { customer_id: customers(:one).id } } + }.must_change 'movies(:one).available_inventory', 1 + end + end end From 9f453b5130e3837d145121eeed1801762258d315 Mon Sep 17 00:00:00 2001 From: Ashtn Date: Fri, 12 May 2017 14:51:07 -0700 Subject: [PATCH 34/41] added over model method plus tests --- app/models/rental.rb | 5 ++ test/controllers/rentals_controller_test.rb | 74 --------------------- test/fixtures/customers.yml | 10 +++ test/fixtures/movies.yml | 6 ++ test/fixtures/rentals.yml | 5 ++ test/models/rental_test.rb | 33 +++++++++ 6 files changed, 59 insertions(+), 74 deletions(-) diff --git a/app/models/rental.rb b/app/models/rental.rb index b50d3beef..54224d740 100644 --- a/app/models/rental.rb +++ b/app/models/rental.rb @@ -21,4 +21,9 @@ def self.checked_out_by_customer(customer_id) return nil end end + + def self.overdue + rentals = self.where(check_in: nil) + return rentals.where("due_date < ?", DateTime.now) + end end diff --git a/test/controllers/rentals_controller_test.rb b/test/controllers/rentals_controller_test.rb index 96cf8b238..2924ea768 100644 --- a/test/controllers/rentals_controller_test.rb +++ b/test/controllers/rentals_controller_test.rb @@ -70,78 +70,4 @@ end end - describe "check-in" do - let(:rental_data) { - {customer_id: customers(:one).id, - due_date: "2017-05-13"} - } - - it "can return a movie" do - post check_out_path(movies(:three).title), params: { rental: rental_data } - proc { - post check_in_path(movies(:three).title), params: { rental: { customer_id: rental_data[:customer_id] } } - }.wont_change 'Rental.count' - - must_respond_with :success - - body = JSON.parse(response.body) - body.must_include "id" - body.must_include "check_in" - - Rental.last.check_in.wont_be_nil - end - - it "available inventory goes up by one after check in" do - post check_out_path(movies(:three).title), params: { rental: rental_data } - - proc { - post check_in_path(movies(:three).title), params: { rental: { customer_id: rental_data[:customer_id] } } - }.must_change 'movies(:three).available_inventory', 1 - end - - it "cannot check in a movie that hasn't been checked out" do - proc { - post check_in_path(movies(:three).title), params: { rental: { customer_id: rental_data[:customer_id] } } - }.wont_change 'movies(:three).available_inventory' - - must_respond_with :bad_request - - body = JSON.parse(response.body) - body.must_include "errors" - body["errors"].must_equal "This customer does not have this movie checked out." - end - - it "cannot check in a movie that's checked out by a different customer" do - proc { - post check_in_path(movies(:two).title), params: { rental: { customer_id: customers(:three).id } } - }.wont_change 'movies(:two).available_inventory' - - must_respond_with :bad_request - - body = JSON.parse(response.body) - body.must_include "errors" - body["errors"].must_equal "This customer does not have this movie checked out." - end - - it "cannot check in a movie that doesn't exist" do - post check_in_path("MOVIE THAT DOESN'T EXIST"), params: { rental: { customer_id: customers(:three).id } } - must_respond_with :bad_request - - body = JSON.parse(response.body) - body.must_include "errors" - body["errors"].must_equal "Sorry we do not currently carry MOVIE THAT DOESN'T EXIST" - end - - it "if customer has same movie checked out more than once, it only checks in once" do - proc { - post check_out_path(movies(:one).title), params: { rental: rental_data } - rental_data[:due_date] = "2017-05-31" - post check_out_path(movies(:one).title), params: { rental: rental_data } - }.must_change 'movies(:one).available_inventory', -2 - - proc { - post check_in_path(movies(:one).title), params: { rental: { customer_id: customers(:one).id } } - }.must_change 'movies(:one).available_inventory', 1 - end - end end diff --git a/test/fixtures/customers.yml b/test/fixtures/customers.yml index eedf23031..68a0c2083 100644 --- a/test/fixtures/customers.yml +++ b/test/fixtures/customers.yml @@ -33,3 +33,13 @@ three: postal_code: "11111" phone: "555-555-1111" account_credit: 0 + +four: + name: "Not Ada" + registered_at: "2010-03-21" + address: "111 Ada Street" + city: "Ada Town" + state: "OR" + postal_code: "11111" + phone: "555-555-1111" + account_credit: 0 diff --git a/test/fixtures/movies.yml b/test/fixtures/movies.yml index c37308f88..d94309de9 100644 --- a/test/fixtures/movies.yml +++ b/test/fixtures/movies.yml @@ -27,3 +27,9 @@ four: overview: " Keep Humans in the Loop, please" release_date: "1983-06-03" inventory: 0 + +five: + title: "Sleepless in Seattle" + overview: " Rain and Love" + release_date: "1993-06-25" + inventory: 3 diff --git a/test/fixtures/rentals.yml b/test/fixtures/rentals.yml index a251b1077..34739a793 100644 --- a/test/fixtures/rentals.yml +++ b/test/fixtures/rentals.yml @@ -30,3 +30,8 @@ four: { due_date: 2017-05-08 } # column: value +five: { + customer: four, + movie: five, + due_date: 3017-05-31 + } diff --git a/test/models/rental_test.rb b/test/models/rental_test.rb index f5c4e7c76..34a06c37f 100644 --- a/test/models/rental_test.rb +++ b/test/models/rental_test.rb @@ -84,4 +84,37 @@ Rental.checked_out_by_customer(Customer.last.id + 1).must_equal nil end end + + describe " Overdue" do + + it " Returns a collection of overdue rental objects " do + + Rental.overdue.each do |rental| + rental.must_be_instance_of Rental + rental.due_date.must_be :<, DateTime.now + end + + end + + it " Does not return rental object that is not overdue " do + + Rental.overdue.wont_include rentals(:five) + end + + it " Does not return rental object that has been checked in " do + + Rental.overdue.wont_include rentals(:three) + end + + it "Does something if now rentals are overdue" do + Rental.all.each do |rental| + if rental.check_in.nil? + rental.check_in = DateTime.yesterday + rental.save + end + end + + Rental.overdue.must_equal [] + end + end end From 9a2718f1b90ec0a9a852560f455e8291d8eb1ce8 Mon Sep 17 00:00:00 2001 From: add2point71dots Date: Fri, 12 May 2017 15:04:48 -0700 Subject: [PATCH 35/41] deleted a space --- app/controllers/rentals_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/rentals_controller.rb b/app/controllers/rentals_controller.rb index 3b7508b99..5a4039f93 100644 --- a/app/controllers/rentals_controller.rb +++ b/app/controllers/rentals_controller.rb @@ -38,7 +38,7 @@ def check_in if rental rental.check_in = DateTime.now rental.save - render status: :ok, json: { + render status: :ok, json: { id: rental.id, check_in: rental.check_in } From 508be023fb5aaf4c5c50db134abde0c2b084d762 Mon Sep 17 00:00:00 2001 From: add2point71dots Date: Fri, 12 May 2017 15:06:57 -0700 Subject: [PATCH 36/41] re-added tests that got lost on github somehow --- test/controllers/rentals_controller_test.rb | 75 +++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/test/controllers/rentals_controller_test.rb b/test/controllers/rentals_controller_test.rb index 2924ea768..77b32611e 100644 --- a/test/controllers/rentals_controller_test.rb +++ b/test/controllers/rentals_controller_test.rb @@ -70,4 +70,79 @@ end end + describe "check-in" do + let(:rental_data) { + {customer_id: customers(:one).id, + due_date: "2017-05-13"} + } + + it "can return a movie" do + post check_out_path(movies(:three).title), params: { rental: rental_data } + proc { + post check_in_path(movies(:three).title), params: { rental: { customer_id: rental_data[:customer_id] } } + }.wont_change 'Rental.count' + + must_respond_with :success + + body = JSON.parse(response.body) + body.must_include "id" + body.must_include "check_in" + + Rental.last.check_in.wont_be_nil + end + + it "available inventory goes up by one after check in" do + post check_out_path(movies(:three).title), params: { rental: rental_data } + + proc { + post check_in_path(movies(:three).title), params: { rental: { customer_id: rental_data[:customer_id] } } + }.must_change 'movies(:three).available_inventory', 1 + end + + it "cannot check in a movie that hasn't been checked out" do + proc { + post check_in_path(movies(:three).title), params: { rental: { customer_id: rental_data[:customer_id] } } + }.wont_change 'movies(:three).available_inventory' + + must_respond_with :bad_request + + body = JSON.parse(response.body) + body.must_include "errors" + body["errors"].must_equal "This customer does not have this movie checked out." + end + + it "cannot check in a movie that's checked out by a different customer" do + proc { + post check_in_path(movies(:two).title), params: { rental: { customer_id: customers(:three).id } } + }.wont_change 'movies(:two).available_inventory' + + must_respond_with :bad_request + + body = JSON.parse(response.body) + body.must_include "errors" + body["errors"].must_equal "This customer does not have this movie checked out." + end + + it "cannot check in a movie that doesn't exist" do + post check_in_path("MOVIE THAT DOESN'T EXIST"), params: { rental: { customer_id: customers(:three).id } } + must_respond_with :bad_request + + body = JSON.parse(response.body) + body.must_include "errors" + body["errors"].must_equal "Sorry we do not currently carry MOVIE THAT DOESN'T EXIST" + end + + it "if customer has same movie checked out more than once, it only checks in once" do + proc { + post check_out_path(movies(:one).title), params: { rental: rental_data } + rental_data[:due_date] = "2017-05-31" + post check_out_path(movies(:one).title), params: { rental: rental_data } + }.must_change 'movies(:one).available_inventory', -2 + + proc { + post check_in_path(movies(:one).title), params: { rental: { customer_id: customers(:one).id } } + }.must_change 'movies(:one).available_inventory', 1 + end + end + end From 19a5327aa082611264d610c27bb17f6a6b1747dc Mon Sep 17 00:00:00 2001 From: Ashtn Date: Fri, 12 May 2017 15:22:17 -0700 Subject: [PATCH 37/41] added overdue controller method in rentals controller --- app/controllers/rentals_controller.rb | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/app/controllers/rentals_controller.rb b/app/controllers/rentals_controller.rb index 5a4039f93..4d0ea1007 100644 --- a/app/controllers/rentals_controller.rb +++ b/app/controllers/rentals_controller.rb @@ -57,6 +57,20 @@ def check_in end def overdue + + rentals = [] + Rental.overdue.each do |rental| + + rentals << { title: rental.movie.title, + customer_id:rental.customer_id, + name: rental.customer.name, + postal_code: rental.customer.postal_code, + due_date: rental.due_date } + + end + + render status: :ok, json: rentals.as_json + end private From bacc49623c4aacfe8c0bc7a97ab72df9eaf103ac Mon Sep 17 00:00:00 2001 From: add2point71dots Date: Fri, 12 May 2017 15:28:10 -0700 Subject: [PATCH 38/41] changed name of movies_checked_out_method --- app/controllers/customers_controller.rb | 2 +- app/models/customer.rb | 2 +- test/controllers/customers_controller_test.rb | 2 +- test/models/customer_test.rb | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/controllers/customers_controller.rb b/app/controllers/customers_controller.rb index 68dfe4835..c79a5b7c8 100644 --- a/app/controllers/customers_controller.rb +++ b/app/controllers/customers_controller.rb @@ -1,6 +1,6 @@ class CustomersController < ApplicationController def index customers = Customer.all - render json: customers.as_json(only: [:id, :name, :registered_at, :postal_code, :phone], methods: :movie_checked_out_count ), status: :ok + render json: customers.as_json(only: [:id, :name, :registered_at, :postal_code, :phone], methods: :movies_checked_out_count ), status: :ok end end diff --git a/app/models/customer.rb b/app/models/customer.rb index ebd56175a..8dca566cd 100644 --- a/app/models/customer.rb +++ b/app/models/customer.rb @@ -10,7 +10,7 @@ class Customer < ApplicationRecord validates_numericality_of :account_credit - def movie_checked_out_count + def movies_checked_out_count # TODO: Fix this rentals = Rental.checked_out_by_customer(id) return rentals.count diff --git a/test/controllers/customers_controller_test.rb b/test/controllers/customers_controller_test.rb index 88ed08cc2..b7f1e56b0 100644 --- a/test/controllers/customers_controller_test.rb +++ b/test/controllers/customers_controller_test.rb @@ -1,7 +1,7 @@ require "test_helper" describe CustomersController do - CUSTOMER_KEYS = %w(id movie_checked_out_count name phone postal_code registered_at) + CUSTOMER_KEYS = %w(id movies_checked_out_count name phone postal_code registered_at) describe "index" do it "is a real working route" do diff --git a/test/models/customer_test.rb b/test/models/customer_test.rb index da3d2fd22..1e0a35f2f 100644 --- a/test/models/customer_test.rb +++ b/test/models/customer_test.rb @@ -38,15 +38,15 @@ describe " Custom Methods" do it " Returns number of movies checked out by customer instance" do - customers(:one).movie_checked_out_count.must_equal 2 + customers(:one).movies_checked_out_count.must_equal 2 end it " Returns 0 if customer has not checked out any movies" do - customers(:three).movie_checked_out_count.must_equal 0 + customers(:three).movies_checked_out_count.must_equal 0 end it " Retuns correct number when customer has checked in rentals " do - customers(:two).movie_checked_out_count.must_equal 1 + customers(:two).movies_checked_out_count.must_equal 1 end end From f379347e76e8b4d0d6200a5aff5d2979d6f90888 Mon Sep 17 00:00:00 2001 From: Ashtn Date: Fri, 12 May 2017 16:20:37 -0700 Subject: [PATCH 39/41] removed pseudo code --- app/controllers/rentals_controller.rb | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/app/controllers/rentals_controller.rb b/app/controllers/rentals_controller.rb index 4d0ea1007..68d8b68ac 100644 --- a/app/controllers/rentals_controller.rb +++ b/app/controllers/rentals_controller.rb @@ -46,14 +46,6 @@ def check_in render status: :bad_request, json: { errors: "This customer does not have this movie checked out." } end end - # params[:customer_id] - # - # movie = Movie.find_by_title(params[:title]) - - - #update rental model check_in column - - #find rental where customer_id = params and movie = movies end def overdue @@ -67,7 +59,7 @@ def overdue postal_code: rental.customer.postal_code, due_date: rental.due_date } - end + end render status: :ok, json: rentals.as_json From 4a82001d15c7e48eb09ededbee697802ad81bbbf Mon Sep 17 00:00:00 2001 From: Ashtn Date: Fri, 12 May 2017 16:36:41 -0700 Subject: [PATCH 40/41] added overdue rental controller tests --- test/controllers/rentals_controller_test.rb | 43 +++++++++++++++++++++ test/models/rental_test.rb | 2 +- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/test/controllers/rentals_controller_test.rb b/test/controllers/rentals_controller_test.rb index 77b32611e..76940eaf3 100644 --- a/test/controllers/rentals_controller_test.rb +++ b/test/controllers/rentals_controller_test.rb @@ -145,4 +145,47 @@ end end + describe "overdue" do + + OVERDUE_KEYS = %w(customer_id due_date name postal_code title) + + it " Gets list of Overdue Rentals " do + get overdue_path + must_respond_with :success + response.header['Content-Type'].must_include 'json' + + end + + it " Returns the correct type of information" do + get overdue_path + body = JSON.parse(response.body) + + body.each do | rental | + rental.keys.sort.must_equal OVERDUE_KEYS + end + end + + it " Retuns the correct amount of overdue rentals" do + get overdue_path + body = JSON.parse(response.body) + + body.count.must_equal Rental.overdue.count + end + + it " Does something if there are no overdue rentals " do + Rental.all.each do |rental| + if rental.check_in.nil? + rental.check_in = DateTime.yesterday + rental.save + end + end + + get overdue_path + + must_respond_with :success + body = JSON.parse(response.body) + body.must_equal [] + end + end + end diff --git a/test/models/rental_test.rb b/test/models/rental_test.rb index 34a06c37f..b0c52bc55 100644 --- a/test/models/rental_test.rb +++ b/test/models/rental_test.rb @@ -106,7 +106,7 @@ Rental.overdue.wont_include rentals(:three) end - it "Does something if now rentals are overdue" do + it "Does something if no rentals are overdue" do Rental.all.each do |rental| if rental.check_in.nil? rental.check_in = DateTime.yesterday From 733923e885302fcad25fd8e12393f223363c4f47 Mon Sep 17 00:00:00 2001 From: Ashtn Date: Fri, 12 May 2017 16:46:04 -0700 Subject: [PATCH 41/41] minor tweak to overdue edge case --- test/models/rental_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/models/rental_test.rb b/test/models/rental_test.rb index b0c52bc55..28486d9d6 100644 --- a/test/models/rental_test.rb +++ b/test/models/rental_test.rb @@ -106,14 +106,14 @@ Rental.overdue.wont_include rentals(:three) end - it "Does something if no rentals are overdue" do + it "Retuns status ok and empty array if if no rentals are overdue" do Rental.all.each do |rental| if rental.check_in.nil? rental.check_in = DateTime.yesterday rental.save end end - + must_respond_with :success Rental.overdue.must_equal [] end end