From 2b3756e2c082de58eaa5cd137eac31ab41fbc818 Mon Sep 17 00:00:00 2001 From: Tamiko Terada Date: Tue, 9 May 2017 14:48:22 -0700 Subject: [PATCH 01/42] new rails app created with ERD diagram --- .gitignore | 17 ++ Gemfile | 52 ++++++ Gemfile.lock | 173 ++++++++++++++++++ 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/ERD.jpeg | Bin 0 -> 148780 bytes 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 54 files changed, 907 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/ERD.jpeg 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..48fb168f6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +# 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 diff --git a/Gemfile b/Gemfile new file mode 100644 index 000000000..d3767b997 --- /dev/null +++ b/Gemfile @@ -0,0 +1,52 @@ +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 'pry-rails' + gem 'foundation-rails' +end + +group :test do + gem 'minitest-rails' + gem 'minitest-reporters' +end diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 000000000..f1afe1b7b --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,173 @@ +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) + ansi (1.5.0) + arel (7.1.4) + 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) + builder (3.2.3) + byebug (9.0.6) + coderay (1.1.1) + concurrent-ruby (1.0.5) + erubis (2.7.0) + execjs (2.7.0) + 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) + i18n (0.8.1) + 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) + 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 + nio4r (2.0.0) + nokogiri (1.7.2) + mini_portile2 (~> 2.1.0) + 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) + 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-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-progressbar (1.8.1) + 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) + websocket-driver (0.6.5) + websocket-extensions (>= 0.1.0) + websocket-extensions (0.1.2) + +PLATFORMS + ruby + +DEPENDENCIES + better_errors + byebug + foundation-rails + listen (~> 3.0.5) + minitest-rails + minitest-reporters + pg (~> 0.18) + pry-rails + puma (~> 3.0) + rails (~> 5.0.2) + spring + spring-watcher-listen (~> 2.0.0) + tzinfo-data + +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..66a3a0459 --- /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: 6f1c804fb7563de3356dc4e3ba1fe9f3358da703d7af43c3257e0e51a3800a4f21505f8de62dfbedc365dc1c5e86b2f061e5123332fdecf5fe6102077e4fc632 + +test: + secret_key_base: 5cbadaaf66929644ee478ec9e1187ab26d0fe4c0a3c26abc67c7e25cd804b5a6ef4fa5f03d3a1032210bbb6eb5af379bf5789718a04911e55e013ecc90514627 + +# 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/ERD.jpeg b/lib/ERD.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..1b6fa94d48dc06fecd6f6fd7f7f91e5b607636c0 GIT binary patch literal 148780 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 ZpbY?R!wk>{H~E*$u3<&te8J_b_!(B6MDPFr literal 0 HcmV?d00001 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 99c1b9e848a5b8880ede2d9cfb93a28cd1ebbba3 Mon Sep 17 00:00:00 2001 From: Sahana Murthy Date: Tue, 9 May 2017 15:08:05 -0700 Subject: [PATCH 02/42] /zomg route is rendering success message --- app/controllers/customers_controller.rb | 6 ++++++ config/routes.rb | 2 +- test/controllers/customers_controller_test.rb | 12 ++++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 app/controllers/customers_controller.rb create mode 100644 test/controllers/customers_controller_test.rb diff --git a/app/controllers/customers_controller.rb b/app/controllers/customers_controller.rb new file mode 100644 index 000000000..5f65468f8 --- /dev/null +++ b/app/controllers/customers_controller.rb @@ -0,0 +1,6 @@ +class CustomersController < ApplicationController + + def index + render :json => { silly_message: "it works!" } + end +end diff --git a/config/routes.rb b/config/routes.rb index 787824f88..9d175a92b 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,3 +1,3 @@ Rails.application.routes.draw do - # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html + get "/zomg", to: 'customers#index' end diff --git a/test/controllers/customers_controller_test.rb b/test/controllers/customers_controller_test.rb new file mode 100644 index 000000000..9f85c285e --- /dev/null +++ b/test/controllers/customers_controller_test.rb @@ -0,0 +1,12 @@ +require "test_helper" + +describe CustomersController do + + describe "index" do + + it "is a real working route" do + get pets_url + must_respond_with :success + end + end +end From 04e40179b3455f775bfac13cd5cbdfa0a516d1d4 Mon Sep 17 00:00:00 2001 From: Tamiko Terada Date: Tue, 9 May 2017 15:43:17 -0700 Subject: [PATCH 03/42] pseudocode for routes --- config/routes.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/config/routes.rb b/config/routes.rb index 9d175a92b..95c9d6873 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,3 +1,12 @@ Rails.application.routes.draw do get "/zomg", to: 'customers#index' + + # CUSTOMERS: + # all /customers + + # MOVIES: + # all /movies + # find by title + + # RENTALS: end From 3873aef08be3e72ffa0489b4ced7e2e5d03469eb Mon Sep 17 00:00:00 2001 From: Tamiko Terada Date: Tue, 9 May 2017 15:45:57 -0700 Subject: [PATCH 04/42] notes for routes --- config/routes.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/config/routes.rb b/config/routes.rb index 9d175a92b..957896154 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,3 +1,13 @@ Rails.application.routes.draw do get "/zomg", to: 'customers#index' + + # CUSTOMERS: + # /customers #all for returning all customers + + # MOVIES: + # /movies #all for returning all ovies + # /movie #find(title) for a movie by title + + # RENTALS: + # (optional) end From ffb0c0939da5fe6a896ea0093874429d1fe26711 Mon Sep 17 00:00:00 2001 From: Sahana Murthy Date: Tue, 9 May 2017 15:49:52 -0700 Subject: [PATCH 05/42] Created customer and rental models --- app/models/customer.rb | 2 ++ app/models/rental.rb | 2 ++ db/migrate/20170509224927_create_rentals.rb | 8 ++++++++ db/migrate/20170509224933_create_customers.rb | 8 ++++++++ test/fixtures/customers.yml | 11 +++++++++++ test/fixtures/rentals.yml | 11 +++++++++++ test/models/customer_test.rb | 9 +++++++++ test/models/rental_test.rb | 9 +++++++++ 8 files changed, 60 insertions(+) create mode 100644 app/models/customer.rb create mode 100644 app/models/rental.rb create mode 100644 db/migrate/20170509224927_create_rentals.rb create mode 100644 db/migrate/20170509224933_create_customers.rb create mode 100644 test/fixtures/customers.yml create mode 100644 test/fixtures/rentals.yml create mode 100644 test/models/customer_test.rb create mode 100644 test/models/rental_test.rb 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/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/20170509224927_create_rentals.rb b/db/migrate/20170509224927_create_rentals.rb new file mode 100644 index 000000000..a26308fb6 --- /dev/null +++ b/db/migrate/20170509224927_create_rentals.rb @@ -0,0 +1,8 @@ +class CreateRentals < ActiveRecord::Migration[5.0] + def change + create_table :rentals do |t| + + t.timestamps + end + end +end diff --git a/db/migrate/20170509224933_create_customers.rb b/db/migrate/20170509224933_create_customers.rb new file mode 100644 index 000000000..507fa41c3 --- /dev/null +++ b/db/migrate/20170509224933_create_customers.rb @@ -0,0 +1,8 @@ +class CreateCustomers < ActiveRecord::Migration[5.0] + def change + create_table :customers do |t| + + t.timestamps + end + 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/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/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/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 881c2becae65a7c8e80e31f96c2a3a8b522600b8 Mon Sep 17 00:00:00 2001 From: Tamiko Terada Date: Tue, 9 May 2017 15:52:39 -0700 Subject: [PATCH 06/42] create movie model --- app/models/movie.rb | 2 ++ db/migrate/20170509225135_create_movies.rb | 12 ++++++++++++ test/fixtures/movies.yml | 13 +++++++++++++ test/models/movie_test.rb | 9 +++++++++ 4 files changed, 36 insertions(+) create mode 100644 app/models/movie.rb create mode 100644 db/migrate/20170509225135_create_movies.rb create mode 100644 test/fixtures/movies.yml create mode 100644 test/models/movie_test.rb 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/db/migrate/20170509225135_create_movies.rb b/db/migrate/20170509225135_create_movies.rb new file mode 100644 index 000000000..909b24e50 --- /dev/null +++ b/db/migrate/20170509225135_create_movies.rb @@ -0,0 +1,12 @@ +class CreateMovies < ActiveRecord::Migration[5.0] + def change + create_table :movies do |t| + t.string :title + t.string :overview + t.string :release_date + t.integer :inventory + + t.timestamps + end + end +end diff --git a/test/fixtures/movies.yml b/test/fixtures/movies.yml new file mode 100644 index 000000000..d774de5f1 --- /dev/null +++ b/test/fixtures/movies.yml @@ -0,0 +1,13 @@ +# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + title: MyString + overview: MyString + release_date: MyString + inventory: 1 + +two: + title: MyString + overview: MyString + release_date: MyString + inventory: 1 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 ebb97e392662adcd860afc5f30de2d69cc14b1ca Mon Sep 17 00:00:00 2001 From: Sahana Murthy Date: Tue, 9 May 2017 16:05:49 -0700 Subject: [PATCH 07/42] Migrated rental and customer models --- db/migrate/20170509224927_create_rentals.rb | 2 +- db/migrate/20170509224933_create_customers.rb | 8 ++++- db/schema.rb | 35 +++++++++++++++++++ 3 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 db/schema.rb diff --git a/db/migrate/20170509224927_create_rentals.rb b/db/migrate/20170509224927_create_rentals.rb index a26308fb6..60bde3805 100644 --- a/db/migrate/20170509224927_create_rentals.rb +++ b/db/migrate/20170509224927_create_rentals.rb @@ -1,7 +1,7 @@ class CreateRentals < ActiveRecord::Migration[5.0] def change create_table :rentals do |t| - + t.timestamps end end diff --git a/db/migrate/20170509224933_create_customers.rb b/db/migrate/20170509224933_create_customers.rb index 507fa41c3..7eb34ebb8 100644 --- a/db/migrate/20170509224933_create_customers.rb +++ b/db/migrate/20170509224933_create_customers.rb @@ -1,7 +1,13 @@ 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 diff --git a/db/schema.rb b/db/schema.rb new file mode 100644 index 000000000..f5db559c7 --- /dev/null +++ b/db/schema.rb @@ -0,0 +1,35 @@ +# 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: 20170509224933) 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 + end + + create_table "rentals", force: :cascade do |t| + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + +end From b9a842aba7742f4a3802e66bdf17311f78679496 Mon Sep 17 00:00:00 2001 From: Sahana Murthy Date: Tue, 9 May 2017 16:15:05 -0700 Subject: [PATCH 08/42] Seeded customers info into db --- ...70509231351_add_acount_credit_to_customers.rb | 5 +++++ db/schema.rb | 16 +++++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 db/migrate/20170509231351_add_acount_credit_to_customers.rb diff --git a/db/migrate/20170509231351_add_acount_credit_to_customers.rb b/db/migrate/20170509231351_add_acount_credit_to_customers.rb new file mode 100644 index 000000000..e375f56b2 --- /dev/null +++ b/db/migrate/20170509231351_add_acount_credit_to_customers.rb @@ -0,0 +1,5 @@ +class AddAcountCreditToCustomers < ActiveRecord::Migration[5.0] + def change + add_column :customers, :account_credit, :float + end +end diff --git a/db/schema.rb b/db/schema.rb index f5db559c7..6d68c3c54 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: 20170509224933) do +ActiveRecord::Schema.define(version: 20170509231351) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -23,8 +23,18 @@ 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" + end + + create_table "movies", force: :cascade do |t| + t.string "title" + t.string "overview" + t.string "release_date" + t.integer "inventory" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false end create_table "rentals", force: :cascade do |t| From c10cb78e5840fa12266cc4fd8859636185dfa350 Mon Sep 17 00:00:00 2001 From: Tamiko Terada Date: Tue, 9 May 2017 16:16:29 -0700 Subject: [PATCH 09/42] schema changed from db:migrate --- db/schema.rb | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 db/schema.rb diff --git a/db/schema.rb b/db/schema.rb new file mode 100644 index 000000000..273f212ca --- /dev/null +++ b/db/schema.rb @@ -0,0 +1,37 @@ +# 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: 20170509225135) 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.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + create_table "movies", force: :cascade do |t| + t.string "title" + t.string "overview" + t.string "release_date" + t.integer "inventory" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + create_table "rentals", force: :cascade do |t| + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + +end From be17996a4381d2a874b8916bdc7a609524018337 Mon Sep 17 00:00:00 2001 From: Tamiko Terada Date: Tue, 9 May 2017 16:17:43 -0700 Subject: [PATCH 10/42] TDD: movie tests outline as it statements --- test/models/movie_test.rb | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/test/models/movie_test.rb b/test/models/movie_test.rb index 34d1d30a5..c03640d9b 100644 --- a/test/models/movie_test.rb +++ b/test/models/movie_test.rb @@ -6,4 +6,33 @@ it "must be valid" do value(movie).must_be :valid? end + + describe 'Movies#all' do + it "must return a collection of Movies" do + end + + it "cannot be created without a title" do + end + end + + describe 'Movies#create' do + it "must be created with a title" do # validaiton for :title + end + + it "cannot be created without a title" do + end + + it "adds a movie to our active record" do + end + end + + describe 'Movies#find' do + it "can find a Movie with a valid title" do + end + + it "cannot find a Movie with a bogus title" do + end + end end + +# set up fixtures From 09af9982a2d679c8ac63766e9214e7ccaaeb7f4b Mon Sep 17 00:00:00 2001 From: Sahana Murthy Date: Tue, 9 May 2017 16:22:26 -0700 Subject: [PATCH 11/42] Added has_many :rentals relationship --- app/models/customer.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/models/customer.rb b/app/models/customer.rb index 0b5277335..740c779ff 100644 --- a/app/models/customer.rb +++ b/app/models/customer.rb @@ -1,2 +1,3 @@ class Customer < ApplicationRecord + has_many :rentals end From 51c12d93d789174dcdfd9126a6135c6daca7337e Mon Sep 17 00:00:00 2001 From: Sahana Murthy Date: Wed, 10 May 2017 10:04:20 -0700 Subject: [PATCH 12/42] Created some tests (passing) for customer model --- app/models/customer.rb | 6 +++- test/controllers/customers_controller_test.rb | 4 +-- test/fixtures/customers.yml | 28 ++++++++++++------- test/models/customer_test.rb | 24 ++++++++++++++-- 4 files changed, 46 insertions(+), 16 deletions(-) diff --git a/app/models/customer.rb b/app/models/customer.rb index 740c779ff..3a0fd4d74 100644 --- a/app/models/customer.rb +++ b/app/models/customer.rb @@ -1,3 +1,7 @@ class Customer < ApplicationRecord - has_many :rentals + has_many :rentals + validates :name, presence: true + validates :phone, presence: true + + end diff --git a/test/controllers/customers_controller_test.rb b/test/controllers/customers_controller_test.rb index 9f85c285e..4dc728301 100644 --- a/test/controllers/customers_controller_test.rb +++ b/test/controllers/customers_controller_test.rb @@ -5,8 +5,8 @@ describe "index" do it "is a real working route" do - get pets_url + skip must_respond_with :success end - end + end end diff --git a/test/fixtures/customers.yml b/test/fixtures/customers.yml index dc3ee79b5..03fa56009 100644 --- a/test/fixtures/customers.yml +++ b/test/fixtures/customers.yml @@ -1,11 +1,19 @@ -# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html +sahana: + name: + registered_at: "05/10/17" + address: "1234 Blah Rd" + city: "Blahvue" + state: "WAH" + postal_code: "98008" + phone: "707-238-3894" + account_credit: 0 -# 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 +tamiko: + name: "Tamiko" + registered_at: "05/09/2017" + address: "1 Yemen Street" + city: "Yemen City" + state: "YE" + postal_code: "74849" + phone: + account_credit: 0 diff --git a/test/models/customer_test.rb b/test/models/customer_test.rb index 5ebc5c850..e51292f40 100644 --- a/test/models/customer_test.rb +++ b/test/models/customer_test.rb @@ -1,9 +1,27 @@ require "test_helper" describe Customer do - let(:customer) { Customer.new } - it "must be valid" do - value(customer).must_be :valid? + describe "relationships" do + + it "has many rentals" do + ar = Customer.reflect_on_association(:rentals) + expect(ar.macro) == :has_many + end + + end + + describe "validations" do + + it "is invalid without a name" do + customer = customers(:sahana) + customer.valid?.must_equal false + end + + it "is invalid without a phone number" do + customer = customers(:tamiko) + customer.valid?.must_equal false + end + end end From 2cf4432ce5ea1f54469b3ea1fffbd8c9f9a14ccb Mon Sep 17 00:00:00 2001 From: Tamiko Terada Date: Wed, 10 May 2017 10:05:07 -0700 Subject: [PATCH 13/42] movies test outlined --- db/schema.rb | 11 +------- test/controllers/customers_controller_test.rb | 23 +++++++++++++++- test/models/movie_test.rb | 26 ++----------------- 3 files changed, 25 insertions(+), 35 deletions(-) diff --git a/db/schema.rb b/db/schema.rb index 273f212ca..6624101d6 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: 20170509225135) do +ActiveRecord::Schema.define(version: 20170509224933) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -20,15 +20,6 @@ t.datetime "updated_at", null: false end - create_table "movies", force: :cascade do |t| - t.string "title" - t.string "overview" - t.string "release_date" - t.integer "inventory" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - end - create_table "rentals", force: :cascade do |t| t.datetime "created_at", null: false t.datetime "updated_at", null: false diff --git a/test/controllers/customers_controller_test.rb b/test/controllers/customers_controller_test.rb index 9f85c285e..bb8402935 100644 --- a/test/controllers/customers_controller_test.rb +++ b/test/controllers/customers_controller_test.rb @@ -8,5 +8,26 @@ get pets_url must_respond_with :success end - end + + describe 'Movies#all' do + it "must return a collection of Movies" do + end + + it "returns empty Array when no movies exist" do + end + end + + describe 'Movies#create' do + it "adds a movie to our active record" do + end + end + + describe 'Movies#find' do + it "finds one Movie with a valid title" do + end + + it "find an empty array with a nonexistant title" do + end + end + end end diff --git a/test/models/movie_test.rb b/test/models/movie_test.rb index c03640d9b..c17c5a546 100644 --- a/test/models/movie_test.rb +++ b/test/models/movie_test.rb @@ -7,32 +7,10 @@ value(movie).must_be :valid? end - describe 'Movies#all' do - it "must return a collection of Movies" do - end - - it "cannot be created without a title" do - end + it "must be created with a title" do # validaiton for :title end - describe 'Movies#create' do - it "must be created with a title" do # validaiton for :title - end - - it "cannot be created without a title" do - end - - it "adds a movie to our active record" do - end + it "cannot be created without a title" do end - describe 'Movies#find' do - it "can find a Movie with a valid title" do - end - - it "cannot find a Movie with a bogus title" do - end - end end - -# set up fixtures From 889f6dad92f3148928ddd9bc195fa5ed9c8021e5 Mon Sep 17 00:00:00 2001 From: Sahana Murthy Date: Wed, 10 May 2017 10:11:03 -0700 Subject: [PATCH 14/42] installed serializer gem --- Gemfile | 1 + Gemfile.lock | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/Gemfile b/Gemfile index d3767b997..036bea26c 100644 --- a/Gemfile +++ b/Gemfile @@ -24,6 +24,7 @@ gem 'puma', '~> 3.0' # Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible # gem 'rack-cors' +gem 'active_model_serializers', '~> 0.10.0' group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console diff --git a/Gemfile.lock b/Gemfile.lock index f1afe1b7b..c63d3319a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -24,6 +24,11 @@ GEM erubis (~> 2.7.0) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.3) + active_model_serializers (0.10.6) + actionpack (>= 4.1, < 6) + activemodel (>= 4.1, < 6) + case_transform (>= 0.2) + jsonapi-renderer (>= 0.1.1.beta1, < 0.2) activejob (5.0.2) activesupport (= 5.0.2) globalid (>= 0.3.6) @@ -50,6 +55,8 @@ GEM rack (>= 0.9.0) builder (3.2.3) byebug (9.0.6) + case_transform (0.2) + activesupport coderay (1.1.1) concurrent-ruby (1.0.5) erubis (2.7.0) @@ -62,6 +69,7 @@ GEM globalid (0.4.0) activesupport (>= 4.2.0) i18n (0.8.1) + jsonapi-renderer (0.1.2) listen (3.0.8) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) @@ -155,6 +163,7 @@ PLATFORMS ruby DEPENDENCIES + active_model_serializers (~> 0.10.0) better_errors byebug foundation-rails From 2a4ffa093e79dbaf9b82944fa425ad3756ea6fc7 Mon Sep 17 00:00:00 2001 From: Sahana Murthy Date: Wed, 10 May 2017 10:14:53 -0700 Subject: [PATCH 15/42] Created serializers for movie and customer --- app/serializers/customer_serializer.rb | 3 +++ app/serializers/movie_serializer.rb | 3 +++ 2 files changed, 6 insertions(+) create mode 100644 app/serializers/customer_serializer.rb create mode 100644 app/serializers/movie_serializer.rb diff --git a/app/serializers/customer_serializer.rb b/app/serializers/customer_serializer.rb new file mode 100644 index 000000000..dfc786e0e --- /dev/null +++ b/app/serializers/customer_serializer.rb @@ -0,0 +1,3 @@ +class CustomerSerializer < ActiveModel::Serializer + attributes :id, :name, :registered_at, :address, :city, :state, :postal_code, :phone, :account_credit +end diff --git a/app/serializers/movie_serializer.rb b/app/serializers/movie_serializer.rb new file mode 100644 index 000000000..e0cfaac99 --- /dev/null +++ b/app/serializers/movie_serializer.rb @@ -0,0 +1,3 @@ +class MovieSerializer < ActiveModel::Serializer + attributes :id, :title, :overview, :release_date, :inventory +end From 5808b904ee59ea024709721cafe69c033d7e83e1 Mon Sep 17 00:00:00 2001 From: Tamiko Terada Date: Wed, 10 May 2017 10:19:42 -0700 Subject: [PATCH 16/42] created moviescontroller --- app/controllers/movies_controller.rb | 2 ++ test/controllers/movies_controller_test.rb | 7 +++++++ 2 files changed, 9 insertions(+) create mode 100644 app/controllers/movies_controller.rb create mode 100644 test/controllers/movies_controller_test.rb 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/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 From cd4dabad42746a33f5a53949bb33473a392a2676 Mon Sep 17 00:00:00 2001 From: Sahana Murthy Date: Wed, 10 May 2017 11:35:31 -0700 Subject: [PATCH 17/42] All customer model tests passing --- Gemfile | 2 +- app/models/customer.rb | 5 +- test/fixtures/customers.yml | 112 ++++++++++++++++++++++++++++++++++- test/models/customer_test.rb | 59 ++++++++++++++++++ 4 files changed, 174 insertions(+), 4 deletions(-) diff --git a/Gemfile b/Gemfile index 036bea26c..e5900b6d8 100644 --- a/Gemfile +++ b/Gemfile @@ -29,6 +29,7 @@ gem 'active_model_serializers', '~> 0.10.0' group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'byebug', platform: :mri + gem 'pry-rails' end group :development do @@ -43,7 +44,6 @@ gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] group :development do gem 'better_errors' - gem 'pry-rails' gem 'foundation-rails' end diff --git a/app/models/customer.rb b/app/models/customer.rb index 3a0fd4d74..bc3a743e9 100644 --- a/app/models/customer.rb +++ b/app/models/customer.rb @@ -2,6 +2,7 @@ class Customer < ApplicationRecord has_many :rentals validates :name, presence: true validates :phone, presence: true - - + validates :registered_at, presence: true + validates :postal_code, length: { is: 5 }, allow_nil: true, allow_blank: true + validates :account_credit, numericality: {greater_than_or_equal_to: 0} end diff --git a/test/fixtures/customers.yml b/test/fixtures/customers.yml index 03fa56009..9fb364245 100644 --- a/test/fixtures/customers.yml +++ b/test/fixtures/customers.yml @@ -1,3 +1,13 @@ +perfect: + name: "Ada" + registered_at: "12894567" + address: "237055" + city: "dnfjkd" + state: "AF" + postal_code: "24895" + phone: "234-689-2345" + account_credit: 13.7 + sahana: name: registered_at: "05/10/17" @@ -8,6 +18,16 @@ sahana: phone: "707-238-3894" account_credit: 0 +sahana1: + name: Sahana + registered_at: "05/10/17" + address: "1234 Blah Rd" + city: "Blahvue" + state: "WAH" + postal_code: "98008" + phone: "707-238-3894" + account_credit: 0 + tamiko: name: "Tamiko" registered_at: "05/09/2017" @@ -15,5 +35,95 @@ tamiko: city: "Yemen City" state: "YE" postal_code: "74849" - phone: + phone: + account_credit: 0 + +tamiko1: + name: "Tamiko" + registered_at: "05/09/2017" + address: "1 Yemen Street" + city: "Yemen City" + state: "YE" + postal_code: "74849" + phone: "238-439-2348" + account_credit: 0 + +bahana: + name: "Bahana" + registered_at: + address: "sfkhjdlfkjdlkg" + city: "dkfjdl" + state: "fkg" + postal_code: "17593" + phone: "2738249304" + account_credit: 13 + +no_postal: + name: "Seahorse" + registered_at: "12894457" + address: "The great sea" + city: "Seaville" + state: "SE" + postal_code: + phone: "1324398546" + account_credit: 12 + +four: + name: "Seahorse" + registered_at: "12894457" + address: "The great sea" + city: "Seaville" + state: "SE" + postal_code: "1728" + phone: "1324398546" + account_credit: 12 + +six: + name: "Seahorse" + registered_at: "12894457" + address: "The great sea" + city: "Seaville" + state: "SE" + postal_code: "172890" + phone: "1324398546" + account_credit: 12 + +no_credit: + name: "Seahorse" + registered_at: "12894457" + address: "The great sea" + city: "Seaville" + state: "SE" + postal_code: "17290" + phone: "1324398546" + account_credit: + +string_credit: + name: "Seahorse" + registered_at: "12894457" + address: "The great sea" + city: "Seaville" + state: "SE" + postal_code: "17290" + phone: "1324398546" + account_credit: gjhgjh + +negative_credit: + name: "Seahorse" + registered_at: "12894457" + address: "The great sea" + city: "Seaville" + state: "SE" + postal_code: "17290" + phone: "1324398546" + account_credit: -1 + +zero_credit: + name: "Seahorse" + registered_at: "12894457" + address: "The great sea" + city: "Seaville" + state: "SE" + postal_code: "17280" + phone: "1324398546" account_credit: 0 diff --git a/test/models/customer_test.rb b/test/models/customer_test.rb index e51292f40..6fbc628ca 100644 --- a/test/models/customer_test.rb +++ b/test/models/customer_test.rb @@ -18,10 +18,69 @@ customer.valid?.must_equal false end + it "is valid with a name" do + customer = customers(:sahana1) + customer.valid?.must_equal true + end + it "is invalid without a phone number" do customer = customers(:tamiko) customer.valid?.must_equal false end + it "is valid with a phone number" do + customer = customers(:tamiko1) + customer.valid?.must_equal true + end + + it "is invalid without a registered_at date" do + customer = customers(:bahana) + customer.valid?.must_equal false + end + + it "is valid with a registered_at date" do + customer = customers(:perfect) + customer.valid?.must_equal true + end + + it "is valid without a postal code" do + customer = customers(:no_postal) + customer.valid?.must_equal true + end + + it "is valid with a 5 character postal code" do + customer = customers(:perfect) + customer.valid?.must_equal true + end + + it "is invalid without a 5 character postal code" do + customer = customers(:four) + customer.valid?.must_equal false + + customer_one = customers(:six) + customer_one.valid?.must_equal false + end + + it "is invalid without an account_credit" do + customer = customers(:no_credit) + customer.valid?.must_equal false + end + + # Tried to test and invalidate strings, but found out strings will automatically get converted to a zero value + # it "is invalid with a non-numerical account_credit" do + # customer = customers(:string_credit) + # binding.pry + # customer.valid?.must_equal false + # end + + it "is invalid with a negative account_credit" do + customer = customers(:negative_credit) + customer.valid?.must_equal false + end + + it "is valid with a 0 account_credit value" do + customer = customers(:zero_credit) + customer.valid?.must_equal true + end end end From ee65fcb7dd21e4394e4553235437fb0bd3580822 Mon Sep 17 00:00:00 2001 From: Sahana Murthy Date: Wed, 10 May 2017 14:50:51 -0700 Subject: [PATCH 18/42] Customer controller --- app/controllers/customers_controller.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/controllers/customers_controller.rb b/app/controllers/customers_controller.rb index 5f65468f8..75779c869 100644 --- a/app/controllers/customers_controller.rb +++ b/app/controllers/customers_controller.rb @@ -1,6 +1,8 @@ class CustomersController < ApplicationController def index - render :json => { silly_message: "it works!" } + customers = Customer.all + render :json => customers, status: :ok end + end From 9942481a65e7218e05fc6eb648eb7068512ea605 Mon Sep 17 00:00:00 2001 From: Tamiko Terada Date: Wed, 10 May 2017 14:52:54 -0700 Subject: [PATCH 19/42] add model tests for validations --- app/models/customer.rb | 4 ++-- app/models/movie.rb | 2 ++ db/schema.rb | 8 -------- test/fixtures/movies.yml | 21 +++++++++++++-------- test/models/movie_test.rb | 20 ++++++++++++++++---- 5 files changed, 33 insertions(+), 22 deletions(-) diff --git a/app/models/customer.rb b/app/models/customer.rb index 3a0fd4d74..02aa29adb 100644 --- a/app/models/customer.rb +++ b/app/models/customer.rb @@ -1,7 +1,7 @@ class Customer < ApplicationRecord has_many :rentals - validates :name, presence: true + # validates :name, presence: true validates :phone, presence: true - + end diff --git a/app/models/movie.rb b/app/models/movie.rb index dc614df15..2e102bd0d 100644 --- a/app/models/movie.rb +++ b/app/models/movie.rb @@ -1,2 +1,4 @@ class Movie < ApplicationRecord + validates :title, uniqueness: true + validates :inventory, numericality: { greater_than_or_equal_to: 0 } end diff --git a/db/schema.rb b/db/schema.rb index 4ede7cfa5..2223dd21f 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -16,14 +16,6 @@ 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" diff --git a/test/fixtures/movies.yml b/test/fixtures/movies.yml index d774de5f1..852f4e498 100644 --- a/test/fixtures/movies.yml +++ b/test/fixtures/movies.yml @@ -1,13 +1,18 @@ # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html -one: - title: MyString - overview: MyString - release_date: MyString +all_fields: + title: Best Movie + overview: best movie + release_date: "01-01-2000" inventory: 1 -two: - title: MyString - overview: MyString - release_date: MyString +missing_title: + overview: great movie + release_date: "01-01-2000" + inventory: 1 + +same_title: + title: Same Title + overview: great movie + release_date: "01-01-2000" inventory: 1 diff --git a/test/models/movie_test.rb b/test/models/movie_test.rb index c17c5a546..c0092d8cb 100644 --- a/test/models/movie_test.rb +++ b/test/models/movie_test.rb @@ -7,10 +7,22 @@ value(movie).must_be :valid? end - it "must be created with a title" do # validaiton for :title - end + describe 'validations' do + it 'can be created with all attributes' do + @all_fields.valid?.must_equal true + end - it "cannot be created without a title" do - end + it 'cannot be created without a title' do # validaiton for :title + @missing_title.valid?.must_equal false + # movie.errors.messages.must_include :title + end + it "must have a unique title" do + # Fixture w/ title: "same title" + @same_title.valid?.must_equal true + + dupe_book = Book.create!(title: "Same Title") + dupe_book.valid?.must_equal false + end + end end From fb692ffae10a1766fa331641b56217180e97a1db Mon Sep 17 00:00:00 2001 From: Tamiko Terada Date: Wed, 10 May 2017 15:04:06 -0700 Subject: [PATCH 20/42] added movies_checked_out to customers --- ...170510215935_add_movies_checked_out_count_to_customers.rb | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 db/migrate/20170510215935_add_movies_checked_out_count_to_customers.rb diff --git a/db/migrate/20170510215935_add_movies_checked_out_count_to_customers.rb b/db/migrate/20170510215935_add_movies_checked_out_count_to_customers.rb new file mode 100644 index 000000000..0dbb3c664 --- /dev/null +++ b/db/migrate/20170510215935_add_movies_checked_out_count_to_customers.rb @@ -0,0 +1,5 @@ +class AddMoviesCheckedOutCountToCustomers < ActiveRecord::Migration[5.0] + def change + add_column :customers, :movies_checked_out, :integer, :default => 0 + end +end From 31cbfcd190b4a970b1a053c7e7a4412cae75bde5 Mon Sep 17 00:00:00 2001 From: Tamiko Terada Date: Wed, 10 May 2017 15:08:18 -0700 Subject: [PATCH 21/42] TDD add index test and route --- config/routes.rb | 3 ++- test/controllers/customers_controller_test.rb | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/config/routes.rb b/config/routes.rb index 1e2875400..6540d9ad7 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,8 +1,9 @@ Rails.application.routes.draw do - get "/zomg", to: 'customers#index' + # get "/zomg", to: 'customers#index' # CUSTOMERS: # /customers #all for returning all customers + get "customers", to: "customers#index", as: "customers" # MOVIES: # /movies #all for returning all ovies diff --git a/test/controllers/customers_controller_test.rb b/test/controllers/customers_controller_test.rb index 49df2df46..be8a907e4 100644 --- a/test/controllers/customers_controller_test.rb +++ b/test/controllers/customers_controller_test.rb @@ -5,7 +5,7 @@ describe "index" do it "is a real working route" do - skip + get customers_url must_respond_with :success end end From 4402da5472337d23607a3d10dd815d43087d169f Mon Sep 17 00:00:00 2001 From: Tamiko Terada Date: Wed, 10 May 2017 15:16:25 -0700 Subject: [PATCH 22/42] TDD tests for JSON for customers#index --- test/controllers/customers_controller_test.rb | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/test/controllers/customers_controller_test.rb b/test/controllers/customers_controller_test.rb index be8a907e4..bf08ad37a 100644 --- a/test/controllers/customers_controller_test.rb +++ b/test/controllers/customers_controller_test.rb @@ -8,6 +8,32 @@ get customers_url must_respond_with :success end + + it "returns json" do + get customers_url + response.header['Content-Type'].must_include 'json' + end + + it "returns an array" do + get customers_url + body = JSON.parse(response.body) + body.must_be_kind_of Array + end + + it "returns all of the customers" do + get customers_url + body = JSON.parse(response.body) + body.length.must_equal Customer.count + end + + it "returns customers with exactly the required fields" do + keys = %w(name resistered_at postal_code phone movies_checked_out) + get customers_url + body = JSON.parse(response.body) + body.each do |customer| + customer.keys.sort.must_equal keys + end + end end end From 0e35ee940e86f99c9c67a134134dab3d429b0ad4 Mon Sep 17 00:00:00 2001 From: Tamiko Terada Date: Wed, 10 May 2017 15:27:13 -0700 Subject: [PATCH 23/42] TDD change serializer to pass test --- app/serializers/customer_serializer.rb | 2 +- db/schema.rb | 8 ++++---- test/controllers/customers_controller_test.rb | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/serializers/customer_serializer.rb b/app/serializers/customer_serializer.rb index dfc786e0e..264c27c6d 100644 --- a/app/serializers/customer_serializer.rb +++ b/app/serializers/customer_serializer.rb @@ -1,3 +1,3 @@ class CustomerSerializer < ActiveModel::Serializer - attributes :id, :name, :registered_at, :address, :city, :state, :postal_code, :phone, :account_credit + attributes :id, :name, :registered_at, :address, :city, :state, :postal_code, :phone, :account_credit, :movies_checked_out end diff --git a/db/schema.rb b/db/schema.rb index 4ede7cfa5..621072391 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,13 +10,12 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170509231351) do +ActiveRecord::Schema.define(version: 20170510215935) 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" @@ -24,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", default: 0 end create_table "movies", force: :cascade do |t| diff --git a/test/controllers/customers_controller_test.rb b/test/controllers/customers_controller_test.rb index bf08ad37a..7364aa8d5 100644 --- a/test/controllers/customers_controller_test.rb +++ b/test/controllers/customers_controller_test.rb @@ -27,7 +27,7 @@ end it "returns customers with exactly the required fields" do - keys = %w(name resistered_at postal_code phone movies_checked_out) + keys = ["account_credit", "address", "city", "id", "movies_checked_out", "name", "phone", "postal_code", "registered_at", "state"] get customers_url body = JSON.parse(response.body) body.each do |customer| From 15dcfccea6b509a9d0c4e31ef3d54254da5fdfa7 Mon Sep 17 00:00:00 2001 From: Sahana Murthy Date: Wed, 10 May 2017 15:31:37 -0700 Subject: [PATCH 24/42] Changed movies_checked_out column to movies_checked_out_count --- app/serializers/customer_serializer.rb | 2 +- ...name_movies_checked_out_to_movies_checked_out_count.rb | 5 +++++ db/schema.rb | 8 ++++---- test/controllers/customers_controller_test.rb | 2 +- 4 files changed, 11 insertions(+), 6 deletions(-) create mode 100644 db/migrate/20170510222903_rename_movies_checked_out_to_movies_checked_out_count.rb diff --git a/app/serializers/customer_serializer.rb b/app/serializers/customer_serializer.rb index 264c27c6d..21de979ab 100644 --- a/app/serializers/customer_serializer.rb +++ b/app/serializers/customer_serializer.rb @@ -1,3 +1,3 @@ class CustomerSerializer < ActiveModel::Serializer - attributes :id, :name, :registered_at, :address, :city, :state, :postal_code, :phone, :account_credit, :movies_checked_out + attributes :id, :name, :registered_at, :address, :city, :state, :postal_code, :phone, :account_credit, :movies_checked_out_count end diff --git a/db/migrate/20170510222903_rename_movies_checked_out_to_movies_checked_out_count.rb b/db/migrate/20170510222903_rename_movies_checked_out_to_movies_checked_out_count.rb new file mode 100644 index 000000000..d983a91f1 --- /dev/null +++ b/db/migrate/20170510222903_rename_movies_checked_out_to_movies_checked_out_count.rb @@ -0,0 +1,5 @@ +class RenameMoviesCheckedOutToMoviesCheckedOutCount < ActiveRecord::Migration[5.0] + def change + rename_column :customers, :movies_checked_out, :movies_checked_out_count + end +end diff --git a/db/schema.rb b/db/schema.rb index 621072391..8767f2b51 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: 20170510215935) do +ActiveRecord::Schema.define(version: 20170510222903) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -23,10 +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", default: 0 + t.integer "movies_checked_out_count", default: 0 end create_table "movies", force: :cascade do |t| diff --git a/test/controllers/customers_controller_test.rb b/test/controllers/customers_controller_test.rb index 7364aa8d5..b4749c29f 100644 --- a/test/controllers/customers_controller_test.rb +++ b/test/controllers/customers_controller_test.rb @@ -27,7 +27,7 @@ end it "returns customers with exactly the required fields" do - keys = ["account_credit", "address", "city", "id", "movies_checked_out", "name", "phone", "postal_code", "registered_at", "state"] + keys = ["account_credit", "address", "city", "id", "movies_checked_out_count", "name", "phone", "postal_code", "registered_at", "state"] get customers_url body = JSON.parse(response.body) body.each do |customer| From 6475a7bb55727e080a373f4de91219a721aabe63 Mon Sep 17 00:00:00 2001 From: Sahana Murthy Date: Wed, 10 May 2017 15:44:56 -0700 Subject: [PATCH 25/42] changed syntax in routes --- config/routes.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/routes.rb b/config/routes.rb index 6540d9ad7..34cc73110 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -3,7 +3,7 @@ # CUSTOMERS: # /customers #all for returning all customers - get "customers", to: "customers#index", as: "customers" + get "/customers", to: "customers#index", as: "customers" # MOVIES: # /movies #all for returning all ovies From 1a2cb5072ed73fccbcf39abb64a5fb79bd0b7ce5 Mon Sep 17 00:00:00 2001 From: Sahana Murthy Date: Wed, 10 May 2017 15:55:33 -0700 Subject: [PATCH 26/42] Movies controller index method completed and index method tests passing --- app/controllers/movies_controller.rb | 5 +++ config/routes.rb | 3 +- test/controllers/movies_controller_test.rb | 37 ++++++++++++++++++++-- 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/app/controllers/movies_controller.rb b/app/controllers/movies_controller.rb index 6c4c51614..4979be457 100644 --- a/app/controllers/movies_controller.rb +++ b/app/controllers/movies_controller.rb @@ -1,2 +1,7 @@ class MoviesController < ApplicationController + + def index + movies = Movie.all + render :json => movies, status: :ok + end end diff --git a/config/routes.rb b/config/routes.rb index 34cc73110..c997e6aa2 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -3,10 +3,11 @@ # CUSTOMERS: # /customers #all for returning all customers - get "/customers", to: "customers#index", as: "customers" + get "/customers", to: "customers#index", as: "customers" # MOVIES: # /movies #all for returning all ovies + get "/movies", to: "movies#index", as: "movies" # /movie #find(title) for a movie by title # RENTALS: diff --git a/test/controllers/movies_controller_test.rb b/test/controllers/movies_controller_test.rb index 67fabbcfb..b0587829e 100644 --- a/test/controllers/movies_controller_test.rb +++ b/test/controllers/movies_controller_test.rb @@ -1,7 +1,38 @@ require "test_helper" describe MoviesController do - # it "must be a real test" do - # flunk "Need real tests" - # end + + describe "index" do + + it "is a real working route" do + get movies_url + must_respond_with :success + end + + it "returns json" do + get movies_url + response.header['Content-Type'].must_include 'json' + end + + it "returns an array" do + get movies_url + body = JSON.parse(response.body) + body.must_be_kind_of Array + end + + it "returns all of the movies" do + get movies_url + body = JSON.parse(response.body) + body.length.must_equal Movie.count + end + + it "returns movies with exactly the required fields" do + keys = ["id", "inventory", "overview", "release_date", "title"] + get movies_url + body = JSON.parse(response.body) + body.each do |movie| + movie.keys.sort.must_equal keys + end + end + end end From c3b5177e3d947b7f217f4d259a219b8c0bf0d371 Mon Sep 17 00:00:00 2001 From: Tamiko Terada Date: Thu, 11 May 2017 21:46:17 -0700 Subject: [PATCH 27/42] TDD add specs and validations for Movies --- app/models/customer.rb | 2 +- app/models/movie.rb | 6 +++-- test/fixtures/movies.yml | 32 +++++++++++++++++++++------ test/models/movie_test.rb | 46 +++++++++++++++++++++++++++++---------- 4 files changed, 65 insertions(+), 21 deletions(-) diff --git a/app/models/customer.rb b/app/models/customer.rb index b778389ba..bc3a743e9 100644 --- a/app/models/customer.rb +++ b/app/models/customer.rb @@ -1,6 +1,6 @@ class Customer < ApplicationRecord has_many :rentals - # validates :name, presence: true + validates :name, presence: true validates :phone, presence: true validates :registered_at, presence: true validates :postal_code, length: { is: 5 }, allow_nil: true, allow_blank: true diff --git a/app/models/movie.rb b/app/models/movie.rb index 2e102bd0d..9196c923a 100644 --- a/app/models/movie.rb +++ b/app/models/movie.rb @@ -1,4 +1,6 @@ class Movie < ApplicationRecord - validates :title, uniqueness: true - validates :inventory, numericality: { greater_than_or_equal_to: 0 } + has_many :rentals + + validates :title, presence: true, uniqueness: true + validates :inventory, numericality: { only_integer: true, greater_than_or_equal_to: 0 }# by default does not allow nil end diff --git a/test/fixtures/movies.yml b/test/fixtures/movies.yml index 852f4e498..33c0f9870 100644 --- a/test/fixtures/movies.yml +++ b/test/fixtures/movies.yml @@ -1,18 +1,36 @@ # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html -all_fields: - title: Best Movie - overview: best movie +best_case: + title: "Best Movie" + overview: "best movie" release_date: "01-01-2000" inventory: 1 -missing_title: - overview: great movie +z_missing_title: + overview: "great movie" release_date: "01-01-2000" inventory: 1 same_title: - title: Same Title - overview: great movie + title: "Same Title" + overview: "great movie" release_date: "01-01-2000" inventory: 1 + +inv_zero: + title: "test1" + overview: "great movie" + release_date: "01-01-2000" + inventory: 0 + +z_inv_negative: + title: "test2" + overview: "great movie" + release_date: "01-01-2000" + inventory: -1g + +z_inv_float: + title: "test3" + overview: "great movie" + release_date: "01-01-2000" + inventory: 1.5 diff --git a/test/models/movie_test.rb b/test/models/movie_test.rb index c0092d8cb..ba77180ab 100644 --- a/test/models/movie_test.rb +++ b/test/models/movie_test.rb @@ -1,28 +1,52 @@ require "test_helper" describe Movie do - let(:movie) { Movie.new } - it "must be valid" do - value(movie).must_be :valid? + # it "must be valid" do + # value(movie).must_be :valid? + # end + + describe "relationships" do + + it "has many rentals" do + ar = Movie.reflect_on_association(:rentals) + expect(ar.macro) == :has_many + end end describe 'validations' do - it 'can be created with all attributes' do - @all_fields.valid?.must_equal true + + it 'is valid with all attributes' do + movies(:best_case).valid?.must_equal true end - it 'cannot be created without a title' do # validaiton for :title - @missing_title.valid?.must_equal false - # movie.errors.messages.must_include :title + it 'is invalid without a title' do + movies(:z_missing_title).valid?.must_equal false end - it "must have a unique title" do + it "is invalid with a duplicate title" do # Fixture w/ title: "same title" - @same_title.valid?.must_equal true + movies(:same_title).valid?.must_equal true - dupe_book = Book.create!(title: "Same Title") + dupe_book = Movie.create(title: "Same Title") dupe_book.valid?.must_equal false end + + it "is valid with a valid inventory" do + movies(:best_case).valid?.must_equal true + end + + it "is valid with zero (0) inventory" do # edge case + movies(:inv_zero).valid?.must_equal true + end + + it "is invalid with negative (-1) inventory" do # edge case + movies(:z_inv_negative).valid?.must_equal false + end + + # because the inventory's datatype is integer on the model the float is converted to integer before the validation only_integer + it "changes a float (1.5) to an integer" do # edge case + movies(:z_inv_float).inventory.must_equal 1 + end end end From a1096876f5758505eba5bcd449285bd7eb4bb41e Mon Sep 17 00:00:00 2001 From: Sahana Murthy Date: Fri, 12 May 2017 09:28:52 -0700 Subject: [PATCH 28/42] Some tests for movies show method passing --- app/controllers/movies_controller.rb | 10 ++++++++++ config/routes.rb | 1 + test/controllers/movies_controller_test.rb | 14 ++++++++++++++ 3 files changed, 25 insertions(+) diff --git a/app/controllers/movies_controller.rb b/app/controllers/movies_controller.rb index 4979be457..a9561fa4e 100644 --- a/app/controllers/movies_controller.rb +++ b/app/controllers/movies_controller.rb @@ -4,4 +4,14 @@ def index movies = Movie.all render :json => movies, status: :ok end + + def show + movie = Movie.find_by(title: params[:title]) + if movie + render :json => movie, status: :ok + else + render :json => movie, status: :no_content + end + end + end diff --git a/config/routes.rb b/config/routes.rb index c997e6aa2..493a2f37a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -8,6 +8,7 @@ # MOVIES: # /movies #all for returning all ovies get "/movies", to: "movies#index", as: "movies" + get "/movies/:title", to: "movies#show", as: "movie" # /movie #find(title) for a movie by title # RENTALS: diff --git a/test/controllers/movies_controller_test.rb b/test/controllers/movies_controller_test.rb index b0587829e..8a7772816 100644 --- a/test/controllers/movies_controller_test.rb +++ b/test/controllers/movies_controller_test.rb @@ -35,4 +35,18 @@ end end end + + describe "show" do + + it "can get a movie" do + get movie_path(movies(:best_case).id) + must_respond_with :success + end + + it "returns 204 no content if movie does not exist" do + get movie_path(Movie.last.id + 1) + must_respond_with :no_content + end + + end end From 843bcb6975e427ec625063fd369a6c1fae46c5e8 Mon Sep 17 00:00:00 2001 From: Sahana Murthy Date: Fri, 12 May 2017 10:22:50 -0700 Subject: [PATCH 29/42] added available_inventory to movies table --- .../20170512172013_add_available_inventory_to_movies.rb | 5 +++++ db/schema.rb | 7 ++++--- 2 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 db/migrate/20170512172013_add_available_inventory_to_movies.rb diff --git a/db/migrate/20170512172013_add_available_inventory_to_movies.rb b/db/migrate/20170512172013_add_available_inventory_to_movies.rb new file mode 100644 index 000000000..631c8bcd3 --- /dev/null +++ b/db/migrate/20170512172013_add_available_inventory_to_movies.rb @@ -0,0 +1,5 @@ +class AddAvailableInventoryToMovies < ActiveRecord::Migration[5.0] + def change + add_column :movies, :available_inventory, :integer, :default => :inventory + end +end diff --git a/db/schema.rb b/db/schema.rb index 8767f2b51..052943317 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: 20170510222903) do +ActiveRecord::Schema.define(version: 20170512172013) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -34,8 +34,9 @@ t.string "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 create_table "rentals", force: :cascade do |t| From 2d40f5cfdb9c6b17d5fdf4c7f2776daf98739cd4 Mon Sep 17 00:00:00 2001 From: Sahana Murthy Date: Fri, 12 May 2017 10:35:04 -0700 Subject: [PATCH 30/42] reset default value of available_inventory in movies --- app/models/movie.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/models/movie.rb b/app/models/movie.rb index 9196c923a..85e1e170b 100644 --- a/app/models/movie.rb +++ b/app/models/movie.rb @@ -1,6 +1,13 @@ class Movie < ApplicationRecord + after_initialize :set_available_inventory + has_many :rentals validates :title, presence: true, uniqueness: true validates :inventory, numericality: { only_integer: true, greater_than_or_equal_to: 0 }# by default does not allow nil + + def set_available_inventory + self.available_inventory = self.inventory + end + end From 2c4de7e7070dea5687c43b7a078fcf50ccc20ef2 Mon Sep 17 00:00:00 2001 From: Sahana Murthy Date: Fri, 12 May 2017 10:39:41 -0700 Subject: [PATCH 31/42] All smoke tests passinggit add . Hooraygit add . --- app/serializers/movie_serializer.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/serializers/movie_serializer.rb b/app/serializers/movie_serializer.rb index e0cfaac99..510aad5d6 100644 --- a/app/serializers/movie_serializer.rb +++ b/app/serializers/movie_serializer.rb @@ -1,3 +1,3 @@ class MovieSerializer < ActiveModel::Serializer - attributes :id, :title, :overview, :release_date, :inventory + attributes :id, :title, :overview, :release_date, :inventory, :available_inventory end From f20b578901aceb248ccf7d18f0a42682eeed32e1 Mon Sep 17 00:00:00 2001 From: Tamiko Terada Date: Fri, 12 May 2017 11:08:39 -0700 Subject: [PATCH 32/42] Add test for no customer --- test/controllers/customers_controller_test.rb | 31 +++++-------------- 1 file changed, 8 insertions(+), 23 deletions(-) diff --git a/test/controllers/customers_controller_test.rb b/test/controllers/customers_controller_test.rb index b4749c29f..e439f0cbb 100644 --- a/test/controllers/customers_controller_test.rb +++ b/test/controllers/customers_controller_test.rb @@ -34,28 +34,13 @@ customer.keys.sort.must_equal keys end end + + it "returns empty array when there are no customers" do + Customer.destroy_all + get customers_url + body = JSON.parse(response.body) + body.must_be_kind_of Array + body.must_be_empty + end end end - - # T_T: this belongs in Movies Controller (after we make one) - # - # describe 'Movies#all' do - # it "must return a collection of Movies" do - # end - # - # it "returns empty Array when no movies exist" do - # end - # end - # - # describe 'Movies#create' do - # it "adds a movie to our active record" do - # end - # end - # - # describe 'Movies#find' do - # it "finds one Movie with a valid title" do - # end - # - # it "find an empty array with a nonexistant title" do - # end - # end From d6f8e7a36310340f5cd131f82d01d9aff2ec4c86 Mon Sep 17 00:00:00 2001 From: Sahana Murthy Date: Fri, 12 May 2017 11:26:53 -0700 Subject: [PATCH 33/42] JSON responses changed to match the project requirements. All smoke tests and rails tests passing --- app/controllers/movies_controller.rb | 2 +- app/serializers/customer_serializer.rb | 2 +- app/serializers/movie_serializer.rb | 2 +- test/controllers/customers_controller_test.rb | 2 +- test/controllers/movies_controller_test.rb | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/controllers/movies_controller.rb b/app/controllers/movies_controller.rb index a9561fa4e..dc95af946 100644 --- a/app/controllers/movies_controller.rb +++ b/app/controllers/movies_controller.rb @@ -2,7 +2,7 @@ class MoviesController < ApplicationController def index movies = Movie.all - render :json => movies, status: :ok + render :json => movies.as_json(only: [:title, :release_date]), status: :ok end def show diff --git a/app/serializers/customer_serializer.rb b/app/serializers/customer_serializer.rb index 21de979ab..902e6fea9 100644 --- a/app/serializers/customer_serializer.rb +++ b/app/serializers/customer_serializer.rb @@ -1,3 +1,3 @@ class CustomerSerializer < ActiveModel::Serializer - attributes :id, :name, :registered_at, :address, :city, :state, :postal_code, :phone, :account_credit, :movies_checked_out_count + attributes :id, :name, :registered_at, :postal_code, :phone, :movies_checked_out_count end diff --git a/app/serializers/movie_serializer.rb b/app/serializers/movie_serializer.rb index 510aad5d6..8ecd37d50 100644 --- a/app/serializers/movie_serializer.rb +++ b/app/serializers/movie_serializer.rb @@ -1,3 +1,3 @@ class MovieSerializer < ActiveModel::Serializer - attributes :id, :title, :overview, :release_date, :inventory, :available_inventory + attributes :title, :overview, :release_date, :inventory, :available_inventory end diff --git a/test/controllers/customers_controller_test.rb b/test/controllers/customers_controller_test.rb index e439f0cbb..99f43f902 100644 --- a/test/controllers/customers_controller_test.rb +++ b/test/controllers/customers_controller_test.rb @@ -27,7 +27,7 @@ end it "returns customers with exactly the required fields" do - keys = ["account_credit", "address", "city", "id", "movies_checked_out_count", "name", "phone", "postal_code", "registered_at", "state"] + keys = ["id", "movies_checked_out_count", "name", "phone", "postal_code", "registered_at"] get customers_url body = JSON.parse(response.body) body.each do |customer| diff --git a/test/controllers/movies_controller_test.rb b/test/controllers/movies_controller_test.rb index 8a7772816..b2872743d 100644 --- a/test/controllers/movies_controller_test.rb +++ b/test/controllers/movies_controller_test.rb @@ -27,7 +27,7 @@ end it "returns movies with exactly the required fields" do - keys = ["id", "inventory", "overview", "release_date", "title"] + keys = ["release_date", "title"] get movies_url body = JSON.parse(response.body) body.each do |movie| From d919d24ab590cac3d3b9b7c97a18bbb2758b7bb0 Mon Sep 17 00:00:00 2001 From: Sahana Murthy Date: Fri, 12 May 2017 11:33:58 -0700 Subject: [PATCH 34/42] Wrote a movie edge test --- test/controllers/movies_controller_test.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/controllers/movies_controller_test.rb b/test/controllers/movies_controller_test.rb index b2872743d..103e09591 100644 --- a/test/controllers/movies_controller_test.rb +++ b/test/controllers/movies_controller_test.rb @@ -34,6 +34,15 @@ movie.keys.sort.must_equal keys end end + + it "returns empty array if no movies" do + Movie.destroy_all + get movies_url + body = JSON.parse(response.body) + body.must_be_kind_of Array + body.must_be_empty + end + end describe "show" do From 691909666445c4cf0130a1b8a3ca8279069baf93 Mon Sep 17 00:00:00 2001 From: Sahana Murthy Date: Fri, 12 May 2017 11:50:27 -0700 Subject: [PATCH 35/42] fixed success case test for movies show method --- test/controllers/movies_controller_test.rb | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/test/controllers/movies_controller_test.rb b/test/controllers/movies_controller_test.rb index 103e09591..df7f95b00 100644 --- a/test/controllers/movies_controller_test.rb +++ b/test/controllers/movies_controller_test.rb @@ -42,16 +42,24 @@ body.must_be_kind_of Array body.must_be_empty end - + end describe "show" do it "can get a movie" do - get movie_path(movies(:best_case).id) + movie = movies(:best_case) + get movie_path(movie.title) must_respond_with :success end + it "gets the right movie" do + movie = movies(:best_case) + get movie_path(movie.title) + body = JSON.parse(response.body) + body["title"].must_equal movie.title + end + it "returns 204 no content if movie does not exist" do get movie_path(Movie.last.id + 1) must_respond_with :no_content From 4cea72385924808cb77bcee40a296983bd4e3e1f Mon Sep 17 00:00:00 2001 From: Sahana Murthy Date: Fri, 12 May 2017 11:52:33 -0700 Subject: [PATCH 36/42] fixed 204 no content movie show test --- test/controllers/movies_controller_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/controllers/movies_controller_test.rb b/test/controllers/movies_controller_test.rb index df7f95b00..56c7e74a0 100644 --- a/test/controllers/movies_controller_test.rb +++ b/test/controllers/movies_controller_test.rb @@ -61,7 +61,7 @@ end it "returns 204 no content if movie does not exist" do - get movie_path(Movie.last.id + 1) + get movie_path("Bogus title") must_respond_with :no_content end From 1bafde545f8758c71432e3b94d8f245909bae45c Mon Sep 17 00:00:00 2001 From: Sahana Murthy Date: Fri, 12 May 2017 12:02:47 -0700 Subject: [PATCH 37/42] finished tests for movies show --- test/controllers/movies_controller_test.rb | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/controllers/movies_controller_test.rb b/test/controllers/movies_controller_test.rb index 56c7e74a0..01bc3e18e 100644 --- a/test/controllers/movies_controller_test.rb +++ b/test/controllers/movies_controller_test.rb @@ -47,6 +47,12 @@ describe "show" do + it "returns JSON" do + movie = movies(:best_case) + get movies_url(movie.title) + response.header['Content-Type'].must_include 'json' + end + it "can get a movie" do movie = movies(:best_case) get movie_path(movie.title) @@ -60,6 +66,14 @@ body["title"].must_equal movie.title end + it "returns a movie with exactly the required fields" do + keys = ["available_inventory", "inventory", "overview", "release_date", "title"] + movie = movies(:best_case) + get movie_path(movie.title) + body = JSON.parse(response.body) + body.keys.sort.must_equal keys + end + it "returns 204 no content if movie does not exist" do get movie_path("Bogus title") must_respond_with :no_content From f23035762fb889bbedec8ebcb8fcf3172e36e1db Mon Sep 17 00:00:00 2001 From: Tamiko Terada Date: Fri, 12 May 2017 15:05:00 -0700 Subject: [PATCH 38/42] add rentals columns --- app/models/rental.rb | 2 ++ db/migrate/20170512215235_change_table_rentals.rb | 7 +++++++ db/schema.rb | 9 ++++++--- 3 files changed, 15 insertions(+), 3 deletions(-) create mode 100644 db/migrate/20170512215235_change_table_rentals.rb diff --git a/app/models/rental.rb b/app/models/rental.rb index 79e3a65ca..34d3f4df8 100644 --- a/app/models/rental.rb +++ b/app/models/rental.rb @@ -1,2 +1,4 @@ class Rental < ApplicationRecord + belongs_to :movie + belongs_to :customer end diff --git a/db/migrate/20170512215235_change_table_rentals.rb b/db/migrate/20170512215235_change_table_rentals.rb new file mode 100644 index 000000000..7d1ece2fc --- /dev/null +++ b/db/migrate/20170512215235_change_table_rentals.rb @@ -0,0 +1,7 @@ +class ChangeTableRentals < ActiveRecord::Migration[5.0] + def change + add_column :rentals, :customer_id, :integer + add_column :rentals, :movie_id, :integer + add_column :rentals, :due_date, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index 052943317..5b5b40d3e 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: 20170512172013) do +ActiveRecord::Schema.define(version: 20170512215235) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -40,8 +40,11 @@ end create_table "rentals", force: :cascade do |t| - 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 "customer_id" + t.integer "movie_id" + t.string "due_date" end end From cfed6050ff346489d17347255de080638c95029a Mon Sep 17 00:00:00 2001 From: Sahana Murthy Date: Fri, 12 May 2017 15:25:17 -0700 Subject: [PATCH 39/42] FINISHED OPTIONAL CHECKOUT FEATURE YAYgit add . --- app/controllers/movies_controller.rb | 13 +++++++++++++ app/models/movie.rb | 4 ++-- config/routes.rb | 2 +- .../20170512215828_add_due_date_to_rentals.rb | 5 +++++ db/schema.rb | 2 +- 5 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 db/migrate/20170512215828_add_due_date_to_rentals.rb diff --git a/app/controllers/movies_controller.rb b/app/controllers/movies_controller.rb index dc95af946..2d065ebe0 100644 --- a/app/controllers/movies_controller.rb +++ b/app/controllers/movies_controller.rb @@ -14,4 +14,17 @@ def show end end + def checkout + movie = Movie.find_by(title: params[:title]) + movie.available_inventory -= 1 + movie.save + + customer = Customer.find_by(id: params[:customer_id]) + customer.movies_checked_out_count += 1 + customer.save + + rental = Rental.create(customer_id: customer.id, due_date: params[:due_date], movie_id: movie.id) + render :json => rental, status: :ok + end + end diff --git a/app/models/movie.rb b/app/models/movie.rb index 85e1e170b..10ca15679 100644 --- a/app/models/movie.rb +++ b/app/models/movie.rb @@ -7,7 +7,7 @@ class Movie < ApplicationRecord validates :inventory, numericality: { only_integer: true, greater_than_or_equal_to: 0 }# by default does not allow nil def set_available_inventory - self.available_inventory = self.inventory + self.available_inventory ||= self.inventory end - + end diff --git a/config/routes.rb b/config/routes.rb index 493a2f37a..7f021696f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -13,5 +13,5 @@ # RENTALS: # (optional) - + post "/rentals/:title/check-out", to: "movies#checkout", as: "checkout" end diff --git a/db/migrate/20170512215828_add_due_date_to_rentals.rb b/db/migrate/20170512215828_add_due_date_to_rentals.rb new file mode 100644 index 000000000..8faa2d8eb --- /dev/null +++ b/db/migrate/20170512215828_add_due_date_to_rentals.rb @@ -0,0 +1,5 @@ +class AddDueDateToRentals < ActiveRecord::Migration[5.0] + def change + + end +end diff --git a/db/schema.rb b/db/schema.rb index 5b5b40d3e..f9ecdab95 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: 20170512215235) do +ActiveRecord::Schema.define(version: 20170512215828) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" From 27741d1c28b321a285642cd35b0e54b8733b2d96 Mon Sep 17 00:00:00 2001 From: Sahana Murthy Date: Fri, 12 May 2017 15:38:51 -0700 Subject: [PATCH 40/42] Passing test for movie set_available_inventory method --- test/models/movie_test.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/models/movie_test.rb b/test/models/movie_test.rb index ba77180ab..4a718bd22 100644 --- a/test/models/movie_test.rb +++ b/test/models/movie_test.rb @@ -49,4 +49,13 @@ movies(:z_inv_float).inventory.must_equal 1 end end + + describe "set_available_inventory" do + + it "sets the available_inventory" do + movie = movies(:best_case) + movie.available_inventory.must_equal movie.inventory + end + + end end From 22af05176c26ae4533f89049fcb51f0ca9095f27 Mon Sep 17 00:00:00 2001 From: Sahana Murthy Date: Fri, 12 May 2017 17:27:29 -0700 Subject: [PATCH 41/42] Movies controller tests check with Charlesgit add . --- app/controllers/movies_controller.rb | 4 ++-- test/controllers/movies_controller_test.rb | 15 +++++++++++++-- test/models/movie_test.rb | 5 ++++- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/app/controllers/movies_controller.rb b/app/controllers/movies_controller.rb index 2d065ebe0..fceda4c5c 100644 --- a/app/controllers/movies_controller.rb +++ b/app/controllers/movies_controller.rb @@ -10,7 +10,7 @@ def show if movie render :json => movie, status: :ok else - render :json => movie, status: :no_content + render :json => movie, status: :not_found end end @@ -22,7 +22,7 @@ def checkout customer = Customer.find_by(id: params[:customer_id]) customer.movies_checked_out_count += 1 customer.save - + rental = Rental.create(customer_id: customer.id, due_date: params[:due_date], movie_id: movie.id) render :json => rental, status: :ok end diff --git a/test/controllers/movies_controller_test.rb b/test/controllers/movies_controller_test.rb index 01bc3e18e..8a2d7b785 100644 --- a/test/controllers/movies_controller_test.rb +++ b/test/controllers/movies_controller_test.rb @@ -74,10 +74,21 @@ body.keys.sort.must_equal keys end - it "returns 204 no content if movie does not exist" do + it "returns a single JSON object" do + movie = movies(:best_case) + get movie_path(movie.title) + body = JSON.parse(response.body) + body.must_be_kind_of Hash + end + + it "returns 404 not found if movie does not exist" do get movie_path("Bogus title") - must_respond_with :no_content + must_respond_with :not_found end end + + describe "checkout" do + + end end diff --git a/test/models/movie_test.rb b/test/models/movie_test.rb index 4a718bd22..ed23ab1dc 100644 --- a/test/models/movie_test.rb +++ b/test/models/movie_test.rb @@ -57,5 +57,8 @@ movie.available_inventory.must_equal movie.inventory end - end + it "allows the available_inventory to change if a movie has been rented" do + # movie = movies(:best_case) + # Rental.create(customer_id: 1, ) + end end From 5c838d7fbaf2616f176a989d3459d80e88408d75 Mon Sep 17 00:00:00 2001 From: Sahana Murthy Date: Fri, 12 May 2017 18:04:16 -0700 Subject: [PATCH 42/42] we're learning thingsgit add . ! on a friday evening. with some wine --- test/controllers/movies_controller_test.rb | 26 ++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/test/controllers/movies_controller_test.rb b/test/controllers/movies_controller_test.rb index 8a2d7b785..1c9252a74 100644 --- a/test/controllers/movies_controller_test.rb +++ b/test/controllers/movies_controller_test.rb @@ -90,5 +90,31 @@ describe "checkout" do + it "decreases movie available_inventory by 1" do + customer = customers(:perfect) + due_date = "2017-06-01" + movie = movies(:best_case) + start_inventory = movie.available_inventory + + post checkout_path(movie.title), params:{customer_id: customer.id, due_date: due_date} + movie.reload + + end_inventory = movie.available_inventory + start_inventory.must_equal end_inventory + 1 + end + + it "increases customer movies_checked_out_count by 1" do + customer = customers(:perfect) + due_date = "2017-06-01" + movie = movies(:best_case) + start_count = customer.movies_checked_out_count + + post checkout_path(movie.title), params:{customer_id: customer.id, due_date: due_date} + customer.reload + + end_count = customer.movies_checked_out_count + start_count.must_equal end_count - 1 + end + end end