diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 000000000..9654d4420 Binary files /dev/null and b/.DS_Store differ diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..68ac019ec --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +# 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 + +.byebug_history diff --git a/Gemfile b/Gemfile new file mode 100644 index 000000000..a6e0bdd54 --- /dev/null +++ b/Gemfile @@ -0,0 +1,54 @@ +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.1.4' +# Use postgresql as the database for Active Record +gem 'pg', '~> 0.18' +# Use Puma as the app server +gem 'puma', '~> 3.7' +# 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', platforms: [:mri, :mingw, :x64_mingw] + gem 'pry-rails' +end + +group :development do + gem 'listen', '>= 3.0.5', '< 3.2' + # 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] + +gem 'jquery-turbolinks' +group :development do + gem 'better_errors' + gem 'pry-rails' + gem 'binding_of_caller' +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..ae9937c72 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,170 @@ +GEM + remote: https://rubygems.org/ + specs: + actioncable (5.1.4) + actionpack (= 5.1.4) + nio4r (~> 2.0) + websocket-driver (~> 0.6.1) + actionmailer (5.1.4) + actionpack (= 5.1.4) + actionview (= 5.1.4) + activejob (= 5.1.4) + mail (~> 2.5, >= 2.5.4) + rails-dom-testing (~> 2.0) + actionpack (5.1.4) + actionview (= 5.1.4) + activesupport (= 5.1.4) + rack (~> 2.0) + rack-test (>= 0.6.3) + rails-dom-testing (~> 2.0) + rails-html-sanitizer (~> 1.0, >= 1.0.2) + actionview (5.1.4) + activesupport (= 5.1.4) + builder (~> 3.1) + erubi (~> 1.4) + rails-dom-testing (~> 2.0) + rails-html-sanitizer (~> 1.0, >= 1.0.3) + activejob (5.1.4) + activesupport (= 5.1.4) + globalid (>= 0.3.6) + activemodel (5.1.4) + activesupport (= 5.1.4) + activerecord (5.1.4) + activemodel (= 5.1.4) + activesupport (= 5.1.4) + arel (~> 8.0) + activesupport (5.1.4) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (~> 0.7) + minitest (~> 5.1) + tzinfo (~> 1.1) + ansi (1.5.0) + arel (8.0.0) + better_errors (2.4.0) + coderay (>= 1.0.0) + erubi (>= 1.0.0) + rack (>= 0.9.0) + binding_of_caller (0.7.3) + debug_inspector (>= 0.0.1) + builder (3.2.3) + byebug (9.1.0) + coderay (1.1.2) + concurrent-ruby (1.0.5) + crass (1.0.2) + debug_inspector (0.0.3) + erubi (1.7.0) + ffi (1.9.18) + globalid (0.4.1) + activesupport (>= 4.2.0) + i18n (0.9.1) + concurrent-ruby (~> 1.0) + jquery-turbolinks (2.1.0) + railties (>= 3.1.0) + turbolinks + listen (3.1.5) + rb-fsevent (~> 0.9, >= 0.9.4) + rb-inotify (~> 0.9, >= 0.9.7) + ruby_dep (~> 1.2) + loofah (2.1.1) + crass (~> 1.0.2) + nokogiri (>= 1.5.9) + mail (2.7.0) + mini_mime (>= 0.1.1) + method_source (0.9.0) + mini_mime (0.1.4) + mini_portile2 (2.3.0) + minitest (5.10.3) + minitest-rails (3.0.0) + minitest (~> 5.8) + railties (~> 5.0) + minitest-reporters (1.1.18) + ansi + builder + minitest (>= 5.0) + ruby-progressbar + nio4r (2.1.0) + nokogiri (1.8.1) + mini_portile2 (~> 2.3.0) + pg (0.21.0) + pry (0.11.2) + coderay (~> 1.1.0) + method_source (~> 0.9.0) + pry-rails (0.3.6) + pry (>= 0.10.4) + puma (3.10.0) + rack (2.0.3) + rack-test (0.7.0) + rack (>= 1.0, < 3) + rails (5.1.4) + actioncable (= 5.1.4) + actionmailer (= 5.1.4) + actionpack (= 5.1.4) + actionview (= 5.1.4) + activejob (= 5.1.4) + activemodel (= 5.1.4) + activerecord (= 5.1.4) + activesupport (= 5.1.4) + bundler (>= 1.3.0) + railties (= 5.1.4) + sprockets-rails (>= 2.0.0) + rails-dom-testing (2.0.3) + activesupport (>= 4.2.0) + nokogiri (>= 1.6) + rails-html-sanitizer (1.0.3) + loofah (~> 2.0) + railties (5.1.4) + actionpack (= 5.1.4) + activesupport (= 5.1.4) + method_source + rake (>= 0.8.7) + thor (>= 0.18.1, < 2.0) + rake (12.2.1) + rb-fsevent (0.10.2) + rb-inotify (0.9.10) + ffi (>= 0.5.0, < 2) + ruby-progressbar (1.9.0) + ruby_dep (1.5.0) + spring (2.0.2) + 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-rails (3.2.1) + actionpack (>= 4.0) + activesupport (>= 4.0) + sprockets (>= 3.0.0) + thor (0.20.0) + thread_safe (0.3.6) + turbolinks (5.0.1) + turbolinks-source (~> 5) + turbolinks-source (5.0.3) + tzinfo (1.2.4) + thread_safe (~> 0.1) + websocket-driver (0.6.5) + websocket-extensions (>= 0.1.0) + websocket-extensions (0.1.2) + +PLATFORMS + ruby + +DEPENDENCIES + better_errors + binding_of_caller + byebug + jquery-turbolinks + listen (>= 3.0.5, < 3.2) + minitest-rails + minitest-reporters + pg (~> 0.18) + pry-rails + puma (~> 3.7) + rails (~> 5.1.4) + spring + spring-watcher-listen (~> 2.0.0) + tzinfo-data + +BUNDLED WITH + 1.16.0 diff --git a/README.md b/README.md index e47ff3004..8de70751a 100644 --- a/README.md +++ b/README.md @@ -1,301 +1,5 @@ -# Project: VideoStoreAPI -The goal of this project is to create a system that a video store (remember those?) could use to track their inventory of rental videos and their list of customers. +# README - VideoStoreAPI -We will use Rails to construct a RESTful API. The purpose of this API is to quickly serve information about the store's video collection, customer information, and to update rental status. This repository provides two JSON datafiles to serve as the initial seeds for this system. +API used with database to keep track of customers, movie inventory, due dates, etc, for a video store. -This is a pair project. You and your partner should use all the techniques we've learned so far to keep yourselves organized and on track, and ensure that no requirements slip through the cracks. - -## Learning Goals -Upon completing this project, students should be able to: - -- Build an ERD and set up ActiveRecord models for a given dataset / use-case -- Expose database contents through a web API -- Respond reasonably to bad user data in the context of an API -- Verify the correctness of an API using controller tests - -This is a [stage 2](https://github.com/Ada-Developers-Academy/pedagogy/blob/master/rule-of-three.md) project. - -## Success Criteria -Your project will be evaluated against the following requirements: - -- API conformance - - The provided smoke tests should pass (see the subfolder) - - Bad data sent to the API should result in an appropriate status code and helpful error -- Test coverage - - Models: All relations, validations, and custom model methods should include at least one positive and one negative test case - - Controllers: Every API endpoint should include at least one positive and one negative test case -- Style and Organization - - Business logic should be live in models - -## Project Baseline -- Read the API Requirements below and create a pseudo-code "routes" file that specifies - - The _endpoints_ your API will need - - The _HTTP verbs_ each endpoint will use - - Any data that must be provided to the endpoint in order for it to do its work -- Read the Seed Data description below and, bearing in mind the API Requirements, create an ERD for your database that specifies - - The _models_ your database will require - - The _attributes_ for each model - - Any _relationships_ between models -- Create a new Rails app to serve as the API - - **Create the rails app with:** `$ rails new . --api` -- Create a route that responds to `/zomg` that serves a json-encoded "it works!" - -## Wave 1: Database Models, Tables, & Seeds -- Generate Rails models and associations to match your ERD -- Use the provided seed script `db/seeds.rb` to import the provided JSON data into your database - -In the past, many students have spent lots of time writing and testing validations for these models. Because project time is limited and validations are not an important learning objective this week, we do not recommend this. Instead, validate only those fields that, if they are absent, will break your API. - -### Seed Data -`movies.json` contains information about the videos available to rent at the store. The data is presented as an array of objects, with each object having the following key-value pairs: - -| Field | Datatype | Description -|----------------|----------|------------ -| `title` | string | The title of the film -| `overview` | string | A short plot synopsis -| `release_date` | string | `YYYY-MM-DD`, Day the film was originally released -| `inventory` | integer | How many copies of the film the video store owns - -`customers.json` contains information about the customers that have rented with the store in the past. The data is presented as, you guessed it, an array of objects, with each object have the following key-value pairs: - -| Field | Datatype | Description -|------------------|----------|------------ -| `name` | string | The customer's name -| `registered_at` | string | `Wed, 29 Apr 2015 07:54:14 -0700`, When the customer first visited the store -| `address` | string | Street address -| `city` | string | -| `state` | string | -| `postal_code` | string | -| `phone` | string | Primary contact phone number - -### Testing -As with all Rails projects, model testing is a requirement. You should have _at least_ one positive and one negative test case for each relation, validation, and custom function you add to your models. - -Use good TDD practices, and test _before_ you code. Remember: red-green-refactor. - -## Waves 2 Coding The API -In this wave, you will implement the API described below. The endpoints are described more-or-less in order of complexity, and we recommend you build them in that order. Every endpoint must serve JSON data, and must use HTTP response codes to indicate the status of the request. - -The schema of your database and the structure of your rails app are completely up to you, so long as the API conforms to the description and provided script. - -### Error Handling -If something goes wrong, your API should return an appropriate [HTTP status code](http://billpatrianakos.me/blog/2013/10/13/list-of-rails-status-code-symbols/), as well as a list of errors. The list should be formatted like this: - -```json -{ - "errors": { - "title": ["Movie 'Revenge of the Gnomes' not found"] - } -} -``` - -All errors your API can return should be covered by at least one test case. - -### Testing -Because APIs are often open to the public, thorough testing is essential. For a Rails API, that means controller testing. - -For each API endpoint, you should have _at least_: -- A basic test with no parameters, if applicable -- Positive and negative tests for any URI parameters (user ID, movie title) -- Testing around any data in the request body - -Use good TDD practices, and test _before_ you code. Remember: red-green-refactor. - -#### Smoke Tests -Because this API will be used as the backend for a future project, there are strict requirements about how it should be structured. To this end, we have provided a set of [smoke tests](http://softwaretestingfundamentals.com/smoke-testing/) written in Postman to exercise all the endpoints. - -The smoke tests will verify that your API looks correct to the outside world, by sending actual HTTP requests to your running server and checking the results. They test things like: - -- Did I get a success response for a valid request? -- Did the API return JSON? -- Does the JSON contain the expected property names? - -**The smoke tests are not a substitute for writing your own tests!!!!!** They do **not** check that the content is _correct_, nor do they cover any negative or edge cases. Verifying correctness in these cases is **your** responsibility. - -The smoke tests live in the file [`test/VideoStoreAPI_smoke_tests.postman_collection.json`](test/VideoStoreAPI_smoke_tests.postman_collection.json). To run them: - -1. Open Postman -1. Click `Import` in the top left -1. Drag-and-drop the file into the box -1. In the left sidebar, click on the `Collections` tab -1. There should now be an entry for the smoke tests. Hover over it and click the `>` icon for a detail view. You will notice they are in the format `{{url}}/movies`. `{{url}}` is a key which you can give a value on your computer. -1. To do so go to the Gearbox in the top-right and select `Manage Environments` - -1. Then Select `Add` - -1. Lastly add a key `url` and value `http://localhost:3000` - -1. Click the blue `Run` button. This will launch the collection runner. -1. In the collection runner, scroll down in the center pane and click the blue `Start Test` button - -## API Description - -#### `GET /customers` -List all customers - -Fields to return: -- `id` -- `name` -- `registered_at` -- `postal_code` -- `phone` -- `movies_checked_out_count` - - This will be 0 unless you've completed optional requirements - -#### `GET /movies` -List all movies - -Fields to return: -- `id` -- `title` -- `release_date` - -#### `GET /movies/:id` -Look a movie up by `id` - -URI parameters: -- `id`: Movie identifier - -Fields to return: -- `title` -- `overview` -- `release_date` -- `inventory` (total) -- `available_inventory` (not currently checked-out to a customer) - - This will be the same as `inventory` unless you've completed the optional endpoints. - -#### `POST /movies` -Create a new movie in the video store inventory. - -Upon success, this request should return the `id` of the movie created. - -Request body: - -| Field | Datatype | Description -|---------------|---------------------|------------ -| `title` | string | Title of the movie -| `overview` | string | Descriptive summary of the movie -| `release_date` | string `YYYY-MM-DD` | Date the movie was released -| `inventory` | integer | Quantity available in the video store - -### Optional Rentals - -Wave 2 focused on working with customers and movies. With these endpoints you can extend the functionality of your API to allow managing the rental process. - -#### `POST /rentals/check-out` -Check out one of the movie's inventory to the customer. The rental's check-out date should be set to today. - -**Note:** Some of the fields from wave 2 should now have interesting values. Good thing you wrote tests for them, right... right? - -Request body: - -| Field | Datatype | Description -|---------------|---------------------|------------ -| `customer_id` | integer | ID of the customer checking out this film -| `movie_id` | integer | ID of the movie to be checked out -| `due_date` | string `YYYY-MM-DD` | When should this movie be checked back in? - -#### `POST /rentals/check-in` -Check in one of a customer's rentals - -Request body: - -| Field | Datatype | Description -|---------------|----------|------------ -| `customer_id` | integer | ID of the customer checking in this film -| `movie_id` | integer | ID of the movie to be checked in - -#### `GET /rentals/overdue` -List all customers with overdue movies - -Fields to return: -- `title` -- `customer_id` -- `name` -- `postal_code` -- `checkout_date` -- `due_date` - -## Going Further -These really are **optional** - if you've gotten here and you have time left, that means you're moving speedy fast! - -### Query Parameters -Any endpoint that returns a list should accept 3 _optional_ [query parameters](http://guides.rubyonrails.org/action_controller_overview.html#parameters): - -| Name | Value | Description -|--------|---------|------------ -| `sort` | string | Sort objects by this field, in ascending order -| `n` | integer | Number of responses to return per page -| `p` | integer | Page of responses to return - -So, for an API endpoint like `GET /customers`, the following requests should be valid: -- `GET /customers`: All customers, sorted by ID -- `GET /customers?sort=name`: All customers, sorted by name -- `GET /customers?n=10&p=2`: Customers 10-19, sorted by ID -- `GET /customers?sort=name&n=10&p=2`: Customers 10-19, sorted by name - -Of course, adding new features means you should be adding new controller tests to verify them. - -Things to note: -- Sorting by ID is the rails default -- Possible sort fields: - - Customers can be sorted by `name`, `registered_at` and `postal_code` - - Movies can be sorted by `title` and `release_date` - - Overdue rentals can be sorted by `title`, `name`, `checkout_date` and `due_date` -- If the client requests both sorting and pagination, pagination should be relative to the sorted order -- Check out the [will_paginate gem](https://github.com/mislav/will_paginate) - -### More Endpoints: Inventory Management -All these endpoints should support all 3 query parameters. All fields are sortable. - -#### `GET /movies/:id/current` -List customers that have _currently_ checked out a copy of the film - -URI parameters: -- `id`: Movie identifier - -Fields to return: -- `customer_id` -- `name` -- `postal_code` -- `checkout_date` -- `due_date` - -#### `GET /movies/:id/history` -List customers that have checked out a copy of the film _in the past_ - -URI parameters: -- `id`: Movie identifier - -Fields to return: -- `customer_id` -- `name` -- `postal_code` -- `checkout_date` -- `due_date` - -#### `GET /customers/:id/current` -List the movies a customer _currently_ has checked out - -URI parameters: -- `id`: Customer ID - -Fields to return: -- `title` -- `checkout_date` -- `due_date` - -#### `GET /customers/:id/history` -List the movies a customer has checked out _in the past_ - -URI parameters: -- `id`: Customer ID - -Fields to return: -- `title` -- `checkout_date` -- `due_date` - - -# Reference -- [Postman on Environments](https://www.getpostman.com/docs/environments) +* Ruby on Rails API using Rails 5.1.4 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..69ea87a39 --- /dev/null +++ b/app/controllers/application_controller.rb @@ -0,0 +1,4 @@ +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/controllers/customers_controller.rb b/app/controllers/customers_controller.rb new file mode 100644 index 000000000..1de34aebc --- /dev/null +++ b/app/controllers/customers_controller.rb @@ -0,0 +1,47 @@ +class CustomersController < ApplicationController + + def index + customers = Customer.all + render( + :json => customers.as_json(only: [:id, :name, :registered_at, :postal_code, :phone, :movies_checked_out_count]), status: :ok + ) + end + + def show + customer = Customer.find_by(id: params[:id]) + + if customer + render( + json: customer.as_json(only: [:id, :name, :registered_at, :address, :city, :state, :postal_code, :phone]), + status: :ok + ) + else + render( + json: {id: "Invalid Customer ID"}, + status: :not_found + ) + end + end + + def create + customer = Customer.new(customer_params) + + if customer.save + render( + json: {id: customer.id}, + status: :ok + ) + else + render( + json: {errors: customer.errors.messages}, status: :bad_request + ) + end + end + + private + + def customer_params + params.require(:customer).permit(:name, :registered_at, :address, :city, :state, :postal_code, :phone) + end + +end diff --git a/app/controllers/movies_controller.rb b/app/controllers/movies_controller.rb new file mode 100644 index 000000000..2e5a530e6 --- /dev/null +++ b/app/controllers/movies_controller.rb @@ -0,0 +1,54 @@ +class MoviesController < ApplicationController + + def zomg + render json: { + status: 200, + message: "IT WORKS!" + }.to_json + end + + def index + movies = Movie.all + render( + :json => movies.as_json(only: [:id, :title, :release_date]), + status: :ok + ) + end + + def show + movie = Movie.find_by(id: params[:id]) + + if movie + render( + :json => movie.as_json(only: [:title, :overview, :release_date, :inventory, :available_inventory]), + status: :ok + ) + else + render( + json: {id: "Invalid Movie ID"}, + status: :not_found + ) + end + end + + def create + movie = Movie.new(movie_params) + + if movie.save + render( + json: {id: movie.id}, status: :ok + ) + else + render( + json: {errors: movie.errors.messages}, status: :bad_request + ) + end + end + + private + + def movie_params + params.require(:movie).permit(:title, :overview, :release_date, :inventory) + end + +end diff --git a/app/controllers/rentals_controller.rb b/app/controllers/rentals_controller.rb new file mode 100644 index 000000000..97c24082e --- /dev/null +++ b/app/controllers/rentals_controller.rb @@ -0,0 +1,87 @@ +class RentalsController < ApplicationController + + # def index + # rentals = Rental.all + # + # render( + # :json => rentals.as_json(only: [:customer_id, :movie_id, :checkout_id, :due_date]), + # status: :ok + # ) + # end + + def checkout + rental = Rental.new(rental_params) + movie = rental.movie + + if movie.nil? + render( + json: {errors: ["Movie does not exist"]}, + status: :bad_request + ) + return + end + + unless movie.check_inventory? + render( + json: {errors: ["Not enough inventory"]}, + status: :bad_request + ) + return + end + + if rental.save + movie.reduce_inventory! + render( + json: {id: rental.id}, status: :ok + ) + else + render( + json: {errors: rental.errors.messages}, status: :bad_request + ) + end + end + + def checkin + rental = Rental.where(customer_id: params[:customer_id], movie_id: params[:movie_id]).order(due_date: :asc).first + + if rental.nil? + render( + json: {errors: ["Rental not found"]}, + status: :not_found + ) + return + else + rental.checkin_date = Date.today + + if rental.save + + rental.movie.increase_inventory! + render( + json: {id: rental.id}, + status: :ok + ) + else + render( + json: {errors: rental.error.messages}, + status: :bad_request + ) + end + end + end + + def overdue + # overdue_rentals = Rental.find_overdue + + render( + json: Rental.find_overdue.as_json, status: :ok + ) + end + + + private + + def rental_params + params.require(:rental).permit(:customer_id, :movie_id, :checkout_date, :due_date) + end + +end 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/models/customer.rb b/app/models/customer.rb new file mode 100644 index 000000000..d57136f52 --- /dev/null +++ b/app/models/customer.rb @@ -0,0 +1,14 @@ +class Customer < ApplicationRecord +has_many :rentals +has_many :movies, through: :rentals + + +validates :name, presence: true +validates :registered_at, presence: true +validates :address, presence: true +validates :city, presence: true +validates :state, presence: true +validates :postal_code, presence: true +validates :phone, presence: true + +end diff --git a/app/models/movie.rb b/app/models/movie.rb new file mode 100644 index 000000000..cc1a7b611 --- /dev/null +++ b/app/models/movie.rb @@ -0,0 +1,30 @@ +class Movie < ApplicationRecord + has_many :rentals + has_many :customers, through: :rentals + + validates :title, presence: true + validates :overview, presence: true + validates :release_date, presence: true + validates :inventory, presence: true + + + def check_inventory? + if self.available_inventory < 1 + return false + else + return true + end + end + + def reduce_inventory! + self.available_inventory = self.available_inventory - 1 + + self.save + end + + def increase_inventory! + self.available_inventory = self.available_inventory + 1 + + self.save + end +end diff --git a/app/models/rental.rb b/app/models/rental.rb new file mode 100644 index 000000000..19d071470 --- /dev/null +++ b/app/models/rental.rb @@ -0,0 +1,34 @@ +class Rental < ApplicationRecord + belongs_to :customer + belongs_to :movie + + validates :customer_id, presence: true, numericality: { only_integer: true } + validates :movie_id, presence: true, numericality: { only_integer: true } + validates :checkout_date, presence: true + validates :due_date, presence: true + + + def self.find_overdue + #first check that check-in date is nil + #then check if today's date is past the due date + overdue_rentals = Rental.where(checkin_date: nil).where("due_date < ?", Date.today) + + overdue_rentals_array = [] + + overdue_rentals.each do |rental| + c = rental.customer + overdue = { + title: rental.movie.title, + customer_id: rental.customer_id, + name: c.name, + postal_code: c.postal_code, + checkout_date: rental.checkout_date, + due_date: rental.due_date + } + overdue_rentals_array << overdue + end + + return overdue_rentals_array + end + +end 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..104e40c1c --- /dev/null +++ b/bin/setup @@ -0,0 +1,35 @@ +#!/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..7d3c866ff --- /dev/null +++ b/config/application.rb @@ -0,0 +1,40 @@ +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 + config.generators do |g| + # Force new test files to be generated in the minitest-spec style + g.test_framework :minitest, spec: true + + # Always use .js files, never .coffee + g.javascript_engine :js + end + # Initialize configuration defaults for originally generated Rails version. + config.load_defaults 5.1 + + # 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..ad59bcd88 --- /dev/null +++ b/config/cable.yml @@ -0,0 +1,10 @@ +development: + adapter: async + +test: + adapter: async + +production: + adapter: redis + url: redis://localhost:6379/1 + channel_prefix: VideoStoreAPI_production diff --git a/config/database.yml b/config/database.yml new file mode 100644 index 000000000..720570700 --- /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..abc82221c --- /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=#{2.days.seconds.to_i}" + } + 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..3bd8115ea --- /dev/null +++ b/config/environments/production.rb @@ -0,0 +1,83 @@ +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 + + # Attempt to read encrypted secrets from `config/secrets.yml.enc`. + # Requires an encryption key in `ENV["RAILS_MASTER_KEY"]` or + # `config/secrets.yml.key`. + config.read_encrypted_secrets = 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..8e5cbde53 --- /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=#{1.hour.seconds.to_i}" + } + + # 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..89d2efab2 --- /dev/null +++ b/config/initializers/application_controller_renderer.rb @@ -0,0 +1,8 @@ +# Be sure to restart your server when you modify this file. + +# ActiveSupport::Reloader.to_prepare do +# ApplicationController.renderer.defaults.merge!( +# http_host: 'example.org', +# https: false +# ) +# end 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/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..decc5a857 --- /dev/null +++ b/config/locales/en.yml @@ -0,0 +1,33 @@ +# 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. +# +# The following keys must be escaped otherwise they will not be retrieved by +# the default I18n backend: +# +# true, false, on, off, yes, no +# +# Instead, surround them with single quotes. +# +# en: +# 'true': 'foo' +# +# 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..1e19380dc --- /dev/null +++ b/config/puma.rb @@ -0,0 +1,56 @@ +# 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 } +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! + +# If you are preloading your application and using Active Record, it's +# recommended that you close any connections to the database before workers +# are forked to prevent connection leakage. +# +# before_fork do +# ActiveRecord::Base.connection_pool.disconnect! if defined?(ActiveRecord) +# end + +# 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 the `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, as 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..78fc44274 --- /dev/null +++ b/config/routes.rb @@ -0,0 +1,15 @@ +Rails.application.routes.draw do + # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html + + get '/zomg', to: 'movies#zomg' + + resources :movies, only: [:index, :show, :create] + resources :customers, only: [:index, :show, :create] + + post 'rentals/checkout', to: 'rentals#checkout', as: 'checkout' + + post 'rentals/checkin', to: 'rentals#checkin', as: 'checkin' + + get 'rentals/overdue', to: 'rentals#overdue', as: 'overdue' + +end diff --git a/config/secrets.yml b/config/secrets.yml new file mode 100644 index 000000000..55f95aef7 --- /dev/null +++ b/config/secrets.yml @@ -0,0 +1,32 @@ +# 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. + +# Shared secrets are available across all environments. + +# shared: +# api_key: a1B2c3D4e5F6 + +# Environmental secrets are only available for that specific environment. + +development: + secret_key_base: a9f4eef79415c6b308c1ef248cbfdd62363d984f8c77c344be022995415860697dc32ed2ff0cb9519104c0a108b77649326aebf9c780eecfdeb6fd0c7c77a364 + +test: + secret_key_base: c0a8888e800510dd630d6991fadac4173c592f6c16d86006e106093d52e0a49cc40d2fb1076d8d07beda812106635bde330f7960038c1cc529a63110106b1645 + +# Do not keep production secrets in the unencrypted secrets file. +# Instead, either read values from the environment. +# Or, use `bin/rails secrets:setup` to configure encrypted secrets +# and move the `production:` environment over there. + +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/db/migrate/20171106215333_create_movies.rb b/db/migrate/20171106215333_create_movies.rb new file mode 100644 index 000000000..c0e32f968 --- /dev/null +++ b/db/migrate/20171106215333_create_movies.rb @@ -0,0 +1,11 @@ +class CreateMovies < ActiveRecord::Migration[5.1] + 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/db/migrate/20171106215454_create_customers.rb b/db/migrate/20171106215454_create_customers.rb new file mode 100644 index 000000000..f2eaedc0e --- /dev/null +++ b/db/migrate/20171106215454_create_customers.rb @@ -0,0 +1,15 @@ +class CreateCustomers < ActiveRecord::Migration[5.1] + 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.float :account_credit, default: 0.00 + t.timestamps + end + end +end diff --git a/db/migrate/20171107182008_create_rentals.rb b/db/migrate/20171107182008_create_rentals.rb new file mode 100644 index 000000000..75a4dcbe5 --- /dev/null +++ b/db/migrate/20171107182008_create_rentals.rb @@ -0,0 +1,11 @@ +class CreateRentals < ActiveRecord::Migration[5.1] + def change + create_table :rentals do |t| + t.integer :customer_id + t.integer :movie_id + t.string :checkout_date + t.string :due_date + t.timestamps + end + end +end diff --git a/db/migrate/20171107195527_add_checkin_date_to_rentals.rb b/db/migrate/20171107195527_add_checkin_date_to_rentals.rb new file mode 100644 index 000000000..4ac1137d7 --- /dev/null +++ b/db/migrate/20171107195527_add_checkin_date_to_rentals.rb @@ -0,0 +1,5 @@ +class AddCheckinDateToRentals < ActiveRecord::Migration[5.1] + def change + add_column :rentals, :checkin_date, :string + end +end diff --git a/db/migrate/20171108185147_change_customers_registered_at_from_string_to_date.rb b/db/migrate/20171108185147_change_customers_registered_at_from_string_to_date.rb new file mode 100644 index 000000000..813bd6513 --- /dev/null +++ b/db/migrate/20171108185147_change_customers_registered_at_from_string_to_date.rb @@ -0,0 +1,5 @@ +class ChangeCustomersRegisteredAtFromStringToDate < ActiveRecord::Migration[5.1] + def change + change_column :customers, :registered_at, 'date USING CAST(registered_at AS date)' + end +end diff --git a/db/migrate/20171108190434_change_rentals_data_for_checkout_date_due_date_checkin_date.rb b/db/migrate/20171108190434_change_rentals_data_for_checkout_date_due_date_checkin_date.rb new file mode 100644 index 000000000..ce9c6856d --- /dev/null +++ b/db/migrate/20171108190434_change_rentals_data_for_checkout_date_due_date_checkin_date.rb @@ -0,0 +1,7 @@ +class ChangeRentalsDataForCheckoutDateDueDateCheckinDate < ActiveRecord::Migration[5.1] + def change + change_column :rentals, :checkout_date, 'date USING CAST(checkout_date AS date)' + change_column :rentals, :due_date, 'date USING CAST(due_date AS date)' + change_column :rentals, :checkin_date, 'date USING CAST(checkin_date AS date)' + end +end diff --git a/db/migrate/20171108195943_add_available_inventory_to_movies.rb b/db/migrate/20171108195943_add_available_inventory_to_movies.rb new file mode 100644 index 000000000..8d07ed201 --- /dev/null +++ b/db/migrate/20171108195943_add_available_inventory_to_movies.rb @@ -0,0 +1,5 @@ +class AddAvailableInventoryToMovies < ActiveRecord::Migration[5.1] + def change + add_column :movies, :available_inventory, :integer + end +end diff --git a/db/migrate/20171108200209_add_movies_checked_out_count_customers.rb b/db/migrate/20171108200209_add_movies_checked_out_count_customers.rb new file mode 100644 index 000000000..b1be8c824 --- /dev/null +++ b/db/migrate/20171108200209_add_movies_checked_out_count_customers.rb @@ -0,0 +1,5 @@ +class AddMoviesCheckedOutCountCustomers < ActiveRecord::Migration[5.1] + def change + add_column :customers, :movies_checked_out_count, :integer + end +end diff --git a/db/schema.rb b/db/schema.rb new file mode 100644 index 000000000..f83ed9ae6 --- /dev/null +++ b/db/schema.rb @@ -0,0 +1,52 @@ +# 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: 20171108200209) 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.date "registered_at" + t.string "address" + t.string "city" + t.string "state" + t.string "postal_code" + t.string "phone" + t.float "account_credit", default: 0.0 + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.integer "movies_checked_out_count" + 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 + t.integer "available_inventory" + end + + create_table "rentals", force: :cascade do |t| + t.integer "customer_id" + t.integer "movie_id" + t.date "checkout_date" + t.date "due_date" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.date "checkin_date" + end + +end diff --git a/db/seeds.rb b/db/seeds.rb index 5322340ba..e9ac9c831 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -1,7 +1,27 @@ -JSON.parse(File.read('db/seeds/customers.json')).each do |customer| - Customer.create!(customer) -end +# This file should contain all the record creation needed to seed the database with its default values. +# The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup). +# +# Examples: +# +# movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }]) +# Character.create(name: 'Luke', movie: movies.first) + +MOVIE_FILE = Rails.root.join('db', 'seeds', 'movies.json') -JSON.parse(File.read('db/seeds/movies.json')).each do |movie| +movies = JSON.parse(File.read(MOVIE_FILE)) +# +# PRODUCT_FILE = Rails.root.join('db', 'seed_data', 'products.csv') +# puts "Loading raw product data from #{PRODUCT_FILE}" + +movies.each do |movie| Movie.create!(movie) end + + +CUSTOMER_FILE = Rails.root.join('db', 'seeds', 'customers.json') + +customers = JSON.parse(File.read(CUSTOMER_FILE)) + +customers.each do |customer| + Customer.create!(customer) +end diff --git a/db/seeds/movies.json b/db/seeds/movies.json index 98c6cf8e3..2701c9a19 100644 --- a/db/seeds/movies.json +++ b/db/seeds/movies.json @@ -3,600 +3,700 @@ "title": "Psycho", "overview": "When larcenous real estate clerk Marion Crane goes on the lam with a wad of cash and hopes of starting a new life, she ends up at the notorious Bates Motel, where manager Norman Bates cares for his housebound mother. The place seems quirky, but fine… until Marion decides to take a shower.", "release_date": "1960-06-16", -"inventory": 8 +"inventory": 8, +"available_inventory": 8 }, { "title": "Jaws", "overview": "An insatiable great white shark terrorizes the townspeople of Amity Island, The police chief, an oceanographer and a grizzled shark hunter seek to destroy the bloodthirsty beast.", "release_date": "1975-06-19", -"inventory": 6 +"inventory": 6, +"available_inventory": 6 }, { "title": "The Exorcist", "overview": "12-year-old Regan MacNeil begins to adapt an explicit new personality as strange events befall the local area of Georgetown. Her mother becomes torn between science and superstition in a desperate bid to save her daughter, and ultimately turns to her last hope: Father Damien Karras, a troubled priest who is struggling with his own faith.", "release_date": "1973-12-26", -"inventory": 7 +"inventory": 7, +"available_inventory": 7 }, { "title": "North by Northwest", "overview": "Madison Avenue advertising man Roger Thornhill finds himself thrust into the world of spies when he is mistaken for a man by the name of George Kaplan. Foreign spy Philip Vandamm and his henchman Leonard try to eliminate him but when Thornhill tries to make sense of the case, he is framed for murder. Now on the run from the police, he manages to board the 20th Century Limited bound for Chicago where he meets a beautiful blond, Eve Kendall, who helps him to evade the authorities. His world is turned upside down yet again when he learns that Eve isn't the innocent bystander he thought she was. Not all is as it seems however, leading to a dramatic rescue and escape at the top of Mt. Rushmore.", "release_date": "1959-07-17", -"inventory": 10 +"inventory": 10, +"available_inventory": 10 }, { "title": "The Silence of the Lambs", "overview": "FBI trainee Clarice Starling ventures into a maximum-security asylum to pick the diseased brain of Hannibal Lecter, a psychiatrist turned homicidal cannibal. Starling needs clues to help her capture a serial killer. Unfortunately, her Faustian relationship with Lecter soon leads to his escape, and now two deranged killers are on the loose.", "release_date": "1991-02-14", -"inventory": 3 +"inventory": 3, +"available_inventory": 3 }, { "title": "Alien", "overview": "During its return to the earth, commercial spaceship Nostromo intercepts a distress signal from a distant planet. When a three-member team of the crew discovers a chamber containing thousands of eggs on the planet, a creature inside one of the eggs attacks an explorer. The entire crew is unaware of the impending nightmare set to descend upon them when the alien parasite planted inside its unfortunate host is birthed.", "release_date": "1979-05-25", -"inventory": 4 +"inventory": 4, +"available_inventory": 4 }, { "title": "The Birds", "overview": "Chic socialite Melanie Daniels enjoys a passing flirtation with an eligible attorney in a San Francisco pet shop and, on an impulse, follows him to his hometown bearing a gift of lovebirds. But upon her arrival, the bird population runs amok. Suddenly, the townsfolk face a massive avian onslaught, with the feathered fiends inexplicably attacking people all over Bodega Bay.", "release_date": "1963-03-28", -"inventory": 1 +"inventory": 1, +"available_inventory": 1 }, { "title": "The French Connection", "overview": "A pair of NYC cops in the Narcotics Bureau stumble onto a drug smuggling job with a French connection.", "release_date": "1971-10-07", -"inventory": 8 +"inventory": 8, +"available_inventory": 8 }, { "title": "Rosemary's Baby", "overview": "A young couple moves into an infamous New York apartment building to start a family. Things become frightening as Rosemary begins to suspect her unborn baby isn't safe around their strange neighbors.", "release_date": "1968-06-12", -"inventory": 2 +"inventory": 2, +"available_inventory": 2 }, { "title": "Raiders of the Lost Ark", "overview": "When Dr. Indiana Jones – the tweed-suited professor who just happens to be a celebrated archaeologist – is hired by the government to locate the legendary Ark of the Covenant, he finds himself up against the entire Nazi regime.", "release_date": "1981-06-12", -"inventory": 9 +"inventory": 9, +"available_inventory": 9 }, { "title": "The Godfather", "overview": "The story spans the years from 1945 to 1955 and chronicles the fictional Italian-American Corleone crime family. When organized crime family patriarch Vito Corleone barely survives an attempt on his life, his youngest son, Michael, steps in to take care of the would-be killers, launching a campaign of bloody revenge.", "release_date": "1972-03-15", -"inventory": 5 +"inventory": 5, +"available_inventory": 5 }, { "title": "King Kong", "overview": "An adventure film about a film crew in search of a monster on a remote island. The crew finds King Kong and decides to take him back to New York as a money making spectacle. The film is a masterpiece of Stop-Motion in filmmaking history and inspired a line of King Kong films.", "release_date": "1933-03-02", -"inventory": 10 +"inventory": 10, +"available_inventory": 10 }, { "title": "Bonnie and Clyde", "overview": "Bonnie and Clyde is based on the true stories of the gangster pair Bonnie Parker and Clyde Barrow who in the 1930s began robbing banks in U.S. cities until they were eventually killed. The film is a major landmark in the aesthetic movement known as the New Hollywood.", "release_date": "1967-08-04", -"inventory": 4 +"inventory": 4, +"available_inventory": 4 }, { "title": "Rear Window", "overview": "Professional photographer L.B. \"Jeff\" Jeffries breaks his leg while getting an action shot at an auto race. Confined to his New York apartment, he spends his time looking out of the rear window observing the neighbors. He begins to suspect that a man across the courtyard may have murdered his wife. Jeff enlists the help of his high society fashion-consultant girlfriend Lisa Freemont and his visiting nurse Stella to investigate.", "release_date": "1954-08-01", -"inventory": 10 +"inventory": 10, +"available_inventory": 10 }, { "title": "Deliverance", "overview": "The Cahulawassee River valley in Northern Georgia is one of the last natural pristine areas of the state, which will soon change with the imminent building of a dam on the river, which in turn will flood much of the surrounding land. As such, four Atlanta city slickers - alpha male Lewis Medlock, generally even-keeled Ed Gentry, slightly condescending Bobby Trippe, and wide-eyed Drew Ballinger - decide to take a multi-day canoe trip on the river, which turns into a trip they'll never forget into the dangerous American back-country.", "release_date": "1972-07-30", -"inventory": 2 +"inventory": 2, +"available_inventory": 2 }, { "title": "Chinatown", "overview": "Private eye Jake Gittes lives off the murky moral climate of sunbaked, pre-World War II Southern California. Hired by a beautiful socialite to investigate her husband's extra-marital affair, Gittes is swept into a maelstrom of double dealings and deadly deceits, uncovering a web of personal and political scandals that come crashing together.", "release_date": "1974-06-20", -"inventory": 6 +"inventory": 6, +"available_inventory": 6 }, { "title": "The Manchurian Candidate", "overview": "The Manchurian Candidate is a political thriller from American director John Frankenheimer. An American soldier is brainwashed into being a killer for the communist Russians during the Korean War.", "release_date": "1962-10-24", -"inventory": 1 +"inventory": 1, +"available_inventory": 1 }, { "title": "Vertigo", "overview": "A retired San Francisco detective suffering from acrophobia investigates the strange activities of an old friend's wife, all the while becoming dangerously obsessed with her.", "release_date": "1958-05-28", -"inventory": 10 +"inventory": 10, +"available_inventory": 10 }, { "title": "The Great Escape", "overview": "The Nazis, exasperated at the number of escapes from their prison camps by a relatively small number of Allied prisoners, relocates them to a high-security \"escape-proof\" camp to sit out the remainder of the war. Undaunted, the prisoners plan one of the most ambitious escape attempts of World War II. Based on a true story.", "release_date": "1963-07-04", -"inventory": 4 +"inventory": 4, +"available_inventory": 4 }, { "title": "High Noon", "overview": "High Noon is about a recently freed leader of a gang of bandits in the desert who is looking to get revenge on the Sheriff who put him in jail. A legendary western film from the Austrian director Fred Zinnemann.", "release_date": "1952-07-24", -"inventory": 4 +"inventory": 4, +"available_inventory": 4 }, { "title": "A Clockwork Orange", "overview": "The head of a gang of toughs, in an insensitive futuristic society, is conditioned to become physically ill at sex and violence during a prison sentence. When he is released, he's brutally beaten by all of his old adversaries.", "release_date": "1971-12-18", -"inventory": 4 +"inventory": 4, +"available_inventory": 4 }, { "title": "Taxi Driver", "overview": "Robert De Niro stars as Travis Bickle in this oppressive psychodrama about a Vietnam veteran who rebels against the decadence and immorality of big city life in New York while working the nightshift as a taxi driver.", "release_date": "1976-02-08", -"inventory": 4 +"inventory": 4, +"available_inventory": 4 }, { "title": "Lawrence of Arabia", "overview": "Lawrence of Arabia is the classic film from David Lean starring Peter O’Toole and based on the autobiography from Thomas Edward Lawrence who during the first World War was on assignment by the British Empire in Arabia. The film would become a cult classic and is known today as a masterpiece.", "release_date": "1962-12-10", -"inventory": 6 +"inventory": 6, +"available_inventory": 6 }, { "title": "Double Indemnity", "overview": "Unsuspecting Mr. Dietrichson becomes increasingly accident prone after his icily calculating wife encourages him to sign a double indemnity policy proposed by a smooth-talking insurance agent. Against a backdrop of distinctly California settings, the partners in crime plan the perfect murder to collect the insurance. Perfect until a claims manager gets a familiar feeling of foul play and pursues the matter relentlessly.", "release_date": "1944-04-24", -"inventory": 5 +"inventory": 5, +"available_inventory": 5 }, { "title": "Titanic", "overview": "84 years later, a 101-year-old woman named Rose DeWitt Bukater tells the story to her granddaughter Lizzy Calvert, Brock Lovett, Lewis Bodine, Bobby Buell and Anatoly Mikailavich on the Keldysh about her life set in April 10th 1912, on a ship called Titanic when young Rose boards the departing ship with the upper-class passengers and her mother, Ruth DeWitt Bukater, and her fiancé, Caledon Hockley. Meanwhile, a drifter and artist named Jack Dawson and his best friend Fabrizio De Rossi win third-class tickets to the ship in a game. And she explains the whole story from departure until the death of Titanic on its first and last voyage April 15th, 1912 at 2:20 in the morning.", "release_date": "1997-12-19", -"inventory": 5 +"inventory": 5, +"available_inventory": 5 }, { "title": "The Maltese Falcon", "overview": "Spade and Archer is the name of a San Francisco detective agency. That's for Sam Spade and Miles Archer. The two men are partners, but Sam doesn't like Miles much. A knockout, who goes by the name of Miss Wanderly, walks into their office; and by that night everything's changed. Miles is dead. And so is a man named Floyd Thursby. It seems Miss Wanderly is surrounded by dangerous men. There's Joel Cairo, who uses gardenia-scented calling cards. There's Kasper Gutman, with his enormous girth and feigned civility. Her only hope of protection comes from Sam, who is suspected by the police of one or the other murder. More murders are yet to come.", "release_date": "1941-11-18", -"inventory": 2 +"inventory": 2, +"available_inventory": 2 }, { "title": "Star Wars: Episode IV - A New Hope", "overview": "Princess Leia is captured and held hostage by the evil Imperial forces in their effort to take over the galactic Empire. Venturesome Luke Skywalker and dashing captain Han Solo team together with the loveable robot duo R2-D2 and C-3PO to rescue the beautiful princess and restore peace and justice in the Empire.", "release_date": "1977-05-25", -"inventory": 2 +"inventory": 2, +"available_inventory": 2 }, { "title": "Fatal Attraction", "overview": "A married man's one night stand comes back to haunt him when that lover begins to stalk him and his family.", "release_date": "1987-09-11", -"inventory": 8 +"inventory": 8, +"available_inventory": 8 }, { "title": "The Shining", "overview": "Jack Torrance accepts a caretaker job at the Overlook Hotel, where he, along with his wife Wendy and their son Danny, must live isolated from the rest of the world for the winter. But they aren't prepared for the madness that lurks within.", "release_date": "1980-05-22", -"inventory": 3 +"inventory": 3, +"available_inventory": 3 }, { "title": "The Deer Hunter", "overview": "A group of working-class friends decides to enlist in the Army during the Vietnam War and finds it to be hellish chaos -- not the noble venture they imagined. Before they left, Steven married his pregnant girlfriend -- and Michael and Nick were in love with the same woman. But all three are different men upon their return.", "release_date": "1978-12-08", -"inventory": 4 +"inventory": 4, +"available_inventory": 4 }, { "title": "Close Encounters of the Third Kind", "overview": "After an encounter with UFOs, a line worker feels undeniably drawn to an isolated area in the wilderness where something spectacular is about to happen.", "release_date": "1977-11-16", -"inventory": 3 +"inventory": 3, +"available_inventory": 3 }, { "title": "Strangers on a Train", "overview": "A psychotic socialite confronts a pro tennis star with a theory on how two complete strangers can get away with murder...a theory that he plans to implement.", "release_date": "1951-06-30", -"inventory": 9 +"inventory": 9, +"available_inventory": 9 }, { "title": "The Fugitive", "overview": "Dr. Richard Kimble, unjustly accused of murdering his wife, must find the real killer while being the target of a nationwide manhunt.", "release_date": "1993-08-06", -"inventory": 9 +"inventory": 9, +"available_inventory": 9 }, { "title": "The Night of the Hunter", "overview": "Harry Powell marries and murders widows for their money, believing he is helping God do away with women who arouse men's carnal instincts. Arrested for auto theft, he shares a cell with condemned killer Ben Harper and tries to get him to reveal the whereabouts of the $10,000 he stole. Only Ben's nine-year-old son, John, and four-year-old daughter, Pearl, know the money is in Pearl's doll; and they have sworn to their father to keep this secret. After Ben is executed, Preacher goes to Cresap's Landing to court Ben's widow, Willa. When he overwhelms her with his Scripture quoting, sermons, and hymns, she agrees to marry him. On their wedding night, he tells her they will never have sex because it is sinful.", "release_date": "1955-07-26", -"inventory": 9 +"inventory": 9, +"available_inventory": 9 }, { "title": "Jurassic Park", "overview": "A wealthy entrepreneur secretly creates a theme park featuring living dinosaurs drawn from prehistoric DNA. Before opening day, he invites a team of experts and his two eager grandchildren to experience the park and help calm anxious investors. However, the park is anything but amusing as the security systems go off-line and the dinosaurs escape.", "release_date": "1993-06-08", -"inventory": 4 +"inventory": 4, +"available_inventory": 4 }, { "title": "Bullitt", "overview": "Bullitt is an American action thriller from director Peter Yates from 1968. Steven Mcqueen plays the leading role as a mafia-chasing police officer who must protect a valuable witness. The film’s ten minute high speed pursuit is legendary.", "release_date": "1968-10-17", -"inventory": 8 +"inventory": 8, +"available_inventory": 8 }, { "title": "Casablanca", "overview": "Casablanca is a classic and one of the most revered films of all time. Starring Humphrey Bogart and Ingrid Bergman in a love triangle in the city of Casablanca which is a refuge for many fleeing foreigners looking for a new life during the war. Political romance with a backdrop of war conflict between democracy and totalitarianism. A landmark in film history.", "release_date": "1942-11-26", -"inventory": 8 +"inventory": 8, +"available_inventory": 8 }, { "title": "Notorious", "overview": "Released shortly after the war, this classic Hitchcock film illustrates the battle between German Nazis and American spies in Rio de Janeiro where a German businessman keeps a wine cellar with uranium ore.", "release_date": "1946-08-15", -"inventory": 1 +"inventory": 1, +"available_inventory": 1 }, { "title": "Die Hard", "overview": "NYPD cop John McClane's plan to reconcile with his estranged wife, Holly, is thrown for a serious loop when minutes after he arrives at her office, the entire building is overtaken by a group of pitiless terrorists. With little help from the LAPD, wisecracking McClane sets out to single-handedly rescue the hostages and bring the bad guys down.", "release_date": "1988-07-14", -"inventory": 4 +"inventory": 4, +"available_inventory": 4 }, { "title": "2001: A Space Odyssey", "overview": "Humanity finds a mysterious object buried beneath the lunar surface and sets off to find its origins with the help of HAL 9000, the world's most advanced super computer.", "release_date": "1968-04-05", -"inventory": 7 +"inventory": 7, +"available_inventory": 7 }, { "title": "Dirty Harry", "overview": "When a madman dubbed the \"Scorpio Killer\" terrorizes San Francisco, hard-boiled cop Harry Callahan -- famous for his take-no-prisoners approach to law enforcement -- is tasked with hunting down the psychopath. Harry eventually collars Scorpio in the process of rescuing a kidnap victim, only to see him walk on technicalities. Now, the maverick detective is determined to nail the maniac himself.", "release_date": "1971-12-22", -"inventory": 4 +"inventory": 4, +"available_inventory": 4 }, { "title": "The Terminator", "overview": "In the post-apocalyptic future, reigning tyrannical supercomputers teleport a cyborg assassin known as the \"Terminator\" back to 1984 to kill Sarah Connor, whose unborn son is destined to lead insurgents against 21st century mechanical hegemony. Meanwhile, the human-resistance movement dispatches a lone warrior to safeguard Sarah. Can he stop the virtually indestructible killing machine?", "release_date": "1984-10-26", -"inventory": 7 +"inventory": 7, +"available_inventory": 7 }, { "title": "The Wizard of Oz", "overview": "One of the most famous musical films and the first film from Hollywood to use color. Young Dorothy finds herself in a magical world where she makes friends with a lion, a scarecrow and a tin man as they make their way along the yellow brick road to talk with the Wizard and ask for the things they miss most in their lives. The Wicked Witch of the West is the only thing that could stop them.", "release_date": "1939-08-25", -"inventory": 1 +"inventory": 1, +"available_inventory": 1 }, { "title": "E.T. the Extra-Terrestrial", "overview": "A science fiction fairytale about an extra-terrestrial who is left behind on Earth and is found by a young boy who befriends him. This heart-warming fantasy from Director Steven Spielberg became one of the most commercially successful films of all time.", "release_date": "1982-06-11", -"inventory": 2 +"inventory": 2, +"available_inventory": 2 }, { "title": "Saving Private Ryan", "overview": "As U.S. troops storm the beaches of Normandy, three brothers lie dead on the battlefield, with a fourth trapped behind enemy lines. Ranger captain John Miller and seven men are tasked with penetrating German-held territory and bringing the boy home.", "release_date": "1998-07-24", -"inventory": 2 +"inventory": 2, +"available_inventory": 2 }, { "title": "Carrie", "overview": "Carrie may be ostracized, but the shy teen has the ability to move objects with her mind. So when the high school \"in crowd\" torments her with a sick joke at the prom, she lashes out with devastating -- and deadly -- power.", "release_date": "1976-11-03", -"inventory": 3 +"inventory": 3, +"available_inventory": 3 }, { "title": "Invasion of the Body Snatchers", "overview": "A small-town doctor learns that the population of his community is being replaced by emotionless alien duplicates.", "release_date": "1956-02-05", -"inventory": 1 +"inventory": 1, +"available_inventory": 1 }, { "title": "Dial M for Murder", "overview": "An ex-tennis pro carries out a plot to have his wife murdered after discovering she is having an affair, and assumes she will soon leave him for the other man anyway. When things go wrong, he improvises a new plan - to frame her for murder instead.", "release_date": "1954-05-29", -"inventory": 6 +"inventory": 6, +"available_inventory": 6 }, { "title": "Ben-Hur", "overview": "Ben-Hur is a 1959 epic film directed by William Wyler, the third film version of Lew Wallace's 1880 novel Ben-Hur: A Tale of the Christ. It premiered at Loew's State Theatre in New York City on November 18, 1959. The film went on to win a record of eleven Academy Awards, including Best Picture, a feat equaled only by Titanic in 1998 and The Lord of the Rings: The Return of the King in 2004. It was also the last film to win the Oscar for both Best Actor and Best Supporting Actor, until nearly 44 years later when Mystic River achieved the same feat.The movie revolves around a Jewish prince who is betrayed and sent into slavery by a Roman friend and how he regains his freedom and comes back for revenge.", "release_date": "1959-11-18", -"inventory": 5 +"inventory": 5, +"available_inventory": 5 }, { "title": "Marathon Man", "overview": "A graduate student and obsessive runner in New York is drawn into a mysterious plot involving his brother, a member of the secretive Division. This film, famous for its excruciating \"Is it safe?\" torture scene by a Nazi dentist, is a spy classic with an all star cast.", "release_date": "1976-10-06", -"inventory": 1 +"inventory": 1, +"available_inventory": 1 }, { "title": "Raging Bull", "overview": "An emotionally self-destructive boxer's journey through life, as the violence and temper that leads him to the top in the ring, destroys his life outside it.", "release_date": "1980-11-14", -"inventory": 2 +"inventory": 2, +"available_inventory": 2 }, { "title": "Rocky", "overview": "When world heavyweight boxing champ Apollo Creed wants to give an unknown fighter a shot at the title as a publicity stunt, his handlers pick palooka Rocky Balboa, an uneducated collector for a Philadelphia loan shark.", "release_date": "1976-11-21", -"inventory": 10 +"inventory": 10, +"available_inventory": 10 }, { "title": "Pulp Fiction", "overview": "A burger-loving hit man, his philosophical partner, a drug-addled gangster's moll and a washed-up boxer converge in this sprawling, comedic crime caper. Their adventures unfurl in three stories that ingeniously trip back and forth in time.", "release_date": "1994-10-14", -"inventory": 10 +"inventory": 10, +"available_inventory": 10 }, { "title": "Butch Cassidy and the Sundance Kid", "overview": "In late 1890s Wyoming, Butch Cassidy is the affable, clever, talkative leader of the outlaw Hole in the Wall Gang. His closest companion is the laconic dead-shot \"Sundance Kid\". As the west rapidly becomes civilized, the law finally catches up to Butch, Sundance and their gang. Chased doggedly by a special posse, the two decide to make their way to South America in hopes of evading their pursuers once and for all.", "release_date": "1969-09-23", -"inventory": 5 +"inventory": 5, +"available_inventory": 5 }, { "title": "Wait Until Dark", "overview": "After a flight back home, Sam Hendrix returns with a doll he innocently acquired along the way. As it turns out, the doll is actually stuffed with heroin, and a group of criminals led by the ruthless Roat has followed Hendrix back to his place to retrieve it. When Hendrix leaves for business, the crooks make their move -- and find his blind wife, Susy, alone in the apartment. Soon, a life-threatening game begins between Susy and the thugs.", "release_date": "1967-10-26", -"inventory": 6 +"inventory": 6, +"available_inventory": 6 }, { "title": "Frankenstein", "overview": "Henry Frankenstein is a doctor who is trying to discover a way to make the dead walk. He succeeds and creates a monster that has to deal with living again.", "release_date": "1931-11-21", -"inventory": 8 +"inventory": 8, +"available_inventory": 8 }, { "title": "All the President's Men", "overview": "In the run-up to the 1972 elections, Washington Post reporter Bob Woodward covers what seems to be a minor break-in at the Democratic Party National headquarters. He is surprised to find top lawyers already on the defense case, and the discovery of names and addresses of Republican fund organizers on the accused further arouses his suspicions. The editor of the Post is prepared to run with the story and assigns Woodward and Carl Bernstein to it. They find the trail leading higher and higher in the Republican Party, and eventually into the White House itself.", "release_date": "1976-04-04", -"inventory": 4 +"inventory": 4, +"available_inventory": 4 }, { "title": "The Bridge on the River Kwai", "overview": "A classic story of English POWs in Burma forced to build a bridge to aid the war effort of their Japanese captors. British and American intelligence officers conspire to blow up the structure, but Col. Nicholson , the commander who supervised the bridge's construction, has acquired a sense of pride in his creation and tries to foil their plans.", "release_date": "1957-10-02", -"inventory": 9 +"inventory": 9, +"available_inventory": 9 }, { "title": "Planet of the Apes", "overview": "Taylor and two other astronauts come out of deep hibernation to find that their ship has crashed. Escaping with little more than clothes they find that they have landed on a planet where men are pre-lingual and uncivilized while apes have learned speech and technology. Taylor is captured and taken to the city of the apes after damaging his throat so that he is silent and cannot communicate with the apes.", "release_date": "1968-02-07", -"inventory": 10 +"inventory": 10, +"available_inventory": 10 }, { "title": "The Sixth Sense", "overview": "A psychological thriller about an eight year old boy named Cole Sear who believes he can see into the world of the dead. A child psychologist named Malcolm Crowe comes to Cole to help him deal with his problem, learning that he really can see ghosts of dead people.", "release_date": "1999-08-02", -"inventory": 6 +"inventory": 6, +"available_inventory": 6 }, { "title": "Cape Fear", "overview": "Sam Bowden, witnesses a rape committed by Max Cady and testifies against him. When released after 8 years in prison, Cady stalks Bowden and his family but is always clever enough not to violate the law. Bowden enlists the aid of a local police chief, a private detective and then hires thugs to harass Cady all to no avail. The film climaxes pitting Bowden and his family against Cady.", "release_date": "1962-04-12", -"inventory": 3 +"inventory": 3, +"available_inventory": 3 }, { "title": "Spartacus", "overview": "Spartacus is a 1960 American historical drama film directed by Stanley Kubrick and based on the novel of the same name by Howard Fast about the historical life of Spartacus and the Third Servile War. The film stars Kirk Douglas as the rebellious slave Spartacus who leads a violent revolt against the decadent Roman empire. The film was awarded four Oscars and stands today as one of the greatest classics of the Sword and Sandal genre.", "release_date": "1960-10-06", -"inventory": 4 +"inventory": 4, +"available_inventory": 4 }, { "title": "What Ever Happened to Baby Jane?", "overview": "Two aging film actresses live as virtual recluses in an old Hollywood mansion. Jane Hudson, a successful child star, cares for her crippled sister Blanche, whose career in later years eclipsed that of Jane. Now the two live together, their relationship affected by simmering subconscious thoughts of mutual envy, hate and revenge.", "release_date": "1962-10-31", -"inventory": 7 +"inventory": 7, +"available_inventory": 7 }, { "title": "Touch of Evil", "overview": "Stark, perverse story of murder, kidnapping, and police corruption in Mexican border town.", "release_date": "1958-04-23", -"inventory": 5 +"inventory": 5, +"available_inventory": 5 }, { "title": "The Dirty Dozen", "overview": "Classic World War II action drama about a group of 12 American military prisoners, who are ordered to infiltrate a well-guarded enemy château and kill the Nazi officers vacationing there. The soldiers, most of whom are facing death sentences for a variety of violent crimes, agree to the mission and the possible commuting of their sentences.", "release_date": "1967-06-15", -"inventory": 10 +"inventory": 10, +"available_inventory": 10 }, { "title": "The Matrix", "overview": "Thomas A. Anderson is a man living two lives. By day he is an average computer programmer and by night a malevolent hacker known as Neo, who finds himself targeted by the police when he is contacted by Morpheus, a legendary computer hacker, who reveals the shocking truth about our reality.", "release_date": "1999-03-30", -"inventory": 1 +"inventory": 1, +"available_inventory": 1 }, { "title": "The Treasure of the Sierra Madre", "overview": "Fred C. Dobbs and Bob Curtin, both down on their luck in Tampico, Mexico in 1925, meet up with a grizzled prospector named Howard and decide to join with him in search of gold in the wilds of central Mexico. Through enormous difficulties, they eventually succeed in finding gold, but bandits, the elements, and most especially greed threaten to turn their success into disaster.", "release_date": "1948-01-24", -"inventory": 4 +"inventory": 4, +"available_inventory": 4 }, { "title": "Halloween", "overview": "A psychotic murderer institutionalized since childhood for the murder of his sister, escapes and stalks a bookish teenage girl and her friends while his doctor chases him through the streets.", "release_date": "1978-10-25", -"inventory": 4 +"inventory": 4, +"available_inventory": 4 }, { "title": "The Wild Bunch", "overview": "Aging outlaw Pike Bishop (William Holden) prepares to retire after one final robbery. Joined by his gang, which includes Dutch Engstrom (Ernest Borgnine) and brothers Lyle (Warren Oates) and Tector Gorch (Ben Johnson), Bishop discovers the heist is a setup orchestrated in part by his old partner, Deke Thornton (Robert Ryan). As the remaining gang takes refuge in Mexican territory, Thornton trails them, resulting in fierce gunfights with plenty of casualties", "release_date": "1969-06-17", -"inventory": 2 +"inventory": 2 , +"available_inventory": 2 }, { "title": "Dog Day Afternoon", "overview": "A man robs a bank to pay for his lover's operation; it turns into a hostage situation and a media circus.", "release_date": "1975-09-21", -"inventory": 9 +"inventory": 9, +"available_inventory": 9 }, { "title": "Goldfinger", "overview": "Bond is in Miami on holiday when M tells him to observe Auric Goldfinger. Bond steals Goldfinger's girlfriend, Jill Masterson, but after being knocked out, he awakes to find her dead and covered in gold paint. Upon returning to London, Bond is told to further investigate Goldfinger who is believed to be smuggling gold out of Britain, but warned he will be replaced if he turns the mission into a personal vendetta. After failing to befriend Goldfinger, Bond is caught spying and taken to America as a captive. Bond learns of Goldfinger's plan, codenamed Operation Grand Slam, which involves attacking Fort Knox to increase his gold riches. Can 007 find a way to stop Goldfinger despite being held prisoner? This is the third film from the legendary James Bond series starring Sean Connery as the British super agent.", "release_date": "1964-09-17", -"inventory": 1 +"inventory": 1, +"available_inventory": 1 }, { "title": "Platoon", "overview": "Chris Taylor, a young, naive recruit in Vietnam, faces a moral crisis when confronted with the horrors of war and the duality of man.", "release_date": "1986-12-18", -"inventory": 1 +"inventory": 1, +"available_inventory": 1 }, { "title": "Laura", "overview": "A police detective falls in love with the woman whose murder he's investigating.", "release_date": "1944-10-11", -"inventory": 5 +"inventory": 5, +"available_inventory": 5 }, { "title": "Blade Runner", "overview": "In the smog-choked dystopian Los Angeles of 2019, blade runner Rick Deckard is called out of retirement to kill a quartet of replicants who have escaped to Earth seeking their creator for a way to extend their short life spans.", "release_date": "1982-06-25", -"inventory": 9 +"inventory": 9, +"available_inventory": 9 }, { "title": "The Third Man", "overview": "An American pulp writer arrives in post-WWII Vienna only to find that the friend who waited for him is killed under mysterious circumstances. The ensuing mystery entangles him in his friend's involvement in the black market, with the multinational police, and with his Czech girlfriend.", "release_date": "1949-08-31", -"inventory": 9 +"inventory": 9, +"available_inventory": 9 }, { "title": "Thelma & Louise", "overview": "Whilst on a short weekend getaway, Louise shoots a man who had tried to rape Thelma. Due to the incriminating circumstances, they make a run for it and thus a cross country chase ensues for the two fugitives. Along the way, both women rediscover the strength of their friendship and surprising aspects of their personalities and self-strengths in the trying times.", "release_date": "1991-05-24", -"inventory": 4 +"inventory": 4, +"available_inventory": 4 }, { "title": "Terminator 2: Judgment Day", "overview": "Nearly 10 years have passed since Sarah Connor was targeted for termination by a cyborg from the future. Now her son, John, the future leader of the resistance, is the target for a newer, more deadly terminator. Once again, the resistance has managed to send a protector back to attempt to save John and his mother Sarah.", "release_date": "1991-07-01", -"inventory": 1 +"inventory": 1, +"available_inventory": 1 }, { "title": "Gaslight", "overview": "In the late 19th century, Paula Alquist is studying music in Italy, but ends up abandoning her classes because she's fallen in love with the gallant Gregory Anton. The couple marries and moves to England to live in a home inherited by Paula from her aunt, herself a famous singer, who was mysteriously murdered in the house ten years before. Once they have moved in, Gregory, who is in reality a jewel thief and the murderer of Paula's aunt, launches a campaign of terror designed to drive his new bride insane. Though Paula is certain that she sees the house's gaslights dim every evening and that there are strange noises coming from the attic, Gregory convinces Paula that she's imagining things. Gregory's efforts to make Paula unstable are aided by an impertinent maid, Nancy. Meanwhile, a Scotland Yard inspector, Brian Cameron, becomes suspicious of Gregory and sympathetic to Paula's plight.", "release_date": "1944-05-04", -"inventory": 8 +"inventory": 8, +"available_inventory": 8 }, { "title": "The Magnificent Seven", "overview": "The Magnificent Seven is a western film from John Sturges and a remake of the Akira Kurosawa's film The Seven Samurai from 1954.", "release_date": "1960-10-23", -"inventory": 10 +"inventory": 10, +"available_inventory": 10 }, { "title": "Rebecca", "overview": "A self-conscious bride is tormented by the memory of her husband's dead first wife.", "release_date": "1940-04-12", -"inventory": 3 +"inventory": 3, +"available_inventory": 3 }, { "title": "The Omen", "overview": "Immediately after their miscarriage, the US diplomat Robert Thorn adopts the newborn Damien without the knowledge of his wife. Yet what he doesn’t know is that their new son is the son of the devil. A classic horror film with Gregory Peck from 1976.", "release_date": "1976-06-25", -"inventory": 1 +"inventory": 1, +"available_inventory": 1 }, { "title": "The Day the Earth Stood Still", "overview": "An alien and a robot land on earth after World War II and tell mankind to be peaceful or face destruction. A classic science fiction film from Robert Wise with an exceptional message.", "release_date": "1951-09-17", -"inventory": 4 +"inventory": 4, +"available_inventory": 4 }, { "title": "The Phantom of the Opera", "overview": "A grotesquely disfigured composer known as the \"Phantom\" haunts Paris' opera house, where he's secretly grooming Christine Daae to be an opera diva. Luring her to his underground lair, the Phantom declares his love. But Christine loves Raoul de Chagny and plans to elope with him after her next performance. When the Phantom finds out, he abducts Christine, incurring the wrath of Raoul -- and a horde of rabid Parisians.", "release_date": "1925-09-06", -"inventory": 4 +"inventory": 4, +"available_inventory": 4 }, { "title": "Poltergeist", "overview": "Craig T. Nelson stars as Steve Freeling, the main protagonist, who lives with his wife, Diane, (JoBeth Williams) and their three children, Dana (Dominique Dunne), Robbie (Oliver Robins), and Carol Anne (Heather O'Rourke), in Southern California where he sells houses for the company that built the neighborhood. It starts with just a few odd occurrences, such as broken dishes and furniture moving around by itself. However, a tree comes alive and takes Robbie through his bedroom window, and Carol Anne is abducted by ghosts. Realizing that something evil haunts his home, Steve calls in a team of parapsychologists led by Dr. Lesh (Beatrice Straight) to investigate, hoping to get Carol Anne back, so he can remove his family from the house before it's too late.", "release_date": "1982-06-04", -"inventory": 4 +"inventory": 4, +"available_inventory": 4 }, { "title": "Dracula", "overview": "The legend of vampire Count Dracula begins here with this original 1931 Dracula film from Bela Lugosi.", "release_date": "1931-02-12", -"inventory": 9 +"inventory": 9, +"available_inventory": 9 }, { "title": "The Picture of Dorian Gray", "overview": "Dorian Gray, wishing to remain young and handsome for eternity, essentially sells his soul so that a portrait can age instead of him. Over the course of the years, Dorian commits every sort of sin, heavily influenced by his friend Lord Henry Wotton. But as his life goes on, he slowly realises the emptiness and evil which he has succumbed to.", "release_date": "1945-03-01", -"inventory": 8 +"inventory": 8, +"available_inventory": 8 }, { "title": "The Thing from Another World", "overview": "Scientists and American Air Force officials fend off a blood-thirsty alien organism while at a remote arctic outpost.", "release_date": "1951-04-29", -"inventory": 10 +"inventory": 10, +"available_inventory": 10 }, { "title": "12 Angry Men", "overview": "The defense and the prosecution have rested and the jury is filing into the jury room to decide if a young Spanish-American is guilty or innocent of murdering his father. What begins as an open and shut case soon becomes a mini-drama of each of the jurors' prejudices and preconceptions about the trial, the accused, and each other.", "release_date": "1957-04-10", -"inventory": 9 +"inventory": 9, +"available_inventory": 9 }, { "title": "The Guns of Navarone", "overview": "A team of allied saboteurs are assigned an impossible mission: infiltrate an impregnable Nazi-held island and destroy the two enormous long-range field guns that prevent the rescue of 2,000 trapped British soldiers.", "release_date": "1961-06-22", -"inventory": 3 +"inventory": 3, +"available_inventory": 3 }, { "title": "The Poseidon Adventure", "overview": "The Poseidon Adventure was one of the first Catastrophe films and began the Disaster Film genre. Director Neame tells the story of a group of people that must fight for their lives aboard a sinking ship. Based on the novel by Paul Gallico.", "release_date": "1972-12-12", -"inventory": 3 +"inventory": 3, +"available_inventory": 3 }, { "title": "Braveheart", "overview": "Enraged at the slaughter of Murron, his new bride and childhood love, legendary Scottish warrior William Wallace slays a platoon of the local English lord's soldiers. This leads the village to revolt and, eventually, the entire country to rise up against English rule.", "release_date": "1995-05-24", -"inventory": 9 +"inventory": 9, +"available_inventory": 9 }, { "title": "Body Heat", "overview": "In the midst of a searing Florida heat wave, a woman convinces her lover, a small-town lawyer, to murder her rich husband.", "release_date": "1981-08-28", -"inventory": 5 +"inventory": 5, +"available_inventory": 5 }, { "title": "Night of the Living Dead", "overview": "A group of people try to survive an attack of bloodthirsty zombies while trapped in a rural Pennsylvania farmhouse. Although not the first zombie film, Night of the Living Dead is the progenitor of the contemporary \"zombie apocalypse\" horror film, and it greatly influenced the modern pop-culture zombie archetype.", "release_date": "1968-10-01", -"inventory": 9 +"inventory": 9, +"available_inventory": 9 }, { "title": "The China Syndrome", "overview": "While doing a series of reports on alternative energy sources, an opportunistic reporter Kimberly Wells witnesses an accident at a nuclear power plant. Wells is determined to publicise the incident but soon finds herself entangled in a sinister conspiracy to keep the full impact of the incident a secret.", "release_date": "1979-03-16", -"inventory": 4 +"inventory": 4, +"available_inventory": 4 }, { "title": "Full Metal Jacket", "overview": "A pragmatic U.S. Marine observes the dehumanizing effects the U.S.-Vietnam War has on his fellow recruits from their brutal boot camp training to the bloody street fighting in Hue.", "release_date": "1987-05-31", -"inventory": 3 +"inventory": 3, +"available_inventory": 3 }, { "title": "Blue Velvet", "overview": "The discovery of a severed human ear found in a field leads a young man on an investigation related to a beautiful, mysterious nightclub singer and a group of criminals who have kidnapped her child.", "release_date": "1986-08-01", -"inventory": 7 +"inventory": 7, +"available_inventory": 7 }, { "title": "Safety Last!", "overview": "When a store clerk organizes a contest to climb the outside of a tall building, circumstances force him to make the perilous climb himself.", "release_date": "1923-04-01", -"inventory": 6 +"inventory": 6, +"available_inventory": 6 }, { "title": "Blood Simple", "overview": "A rich but jealous man hires a private investigator to kill his cheating wife and her new man. But, when blood is involved, nothing is simple.", "release_date": "1984-09-07", -"inventory": 4 +"inventory": 4, +"available_inventory": 4 }, { "title": "Speed", "overview": "Los Angeles SWAT cop Jack Traven is up against bomb expert Howard Payne, who's after major ransom money. First it's a rigged elevator in a very tall building. Then it's a rigged bus--if it slows, it will blow, bad enough any day, but a nightmare in LA traffic. And that's still not the end.", "release_date": "1994-06-09", -"inventory": 10 +"inventory": 10, +"available_inventory": 10 }, { "title": "The Adventures of Robin Hood", "overview": "Robin Hood (Errol Flynn) fights nobly for justice against the evil Sir Guy of Gisbourne (Basil Rathbone) while striving to win the hand of the beautiful Maid Marian (Olivia de Havilland).", "release_date": "1938-05-14", -"inventory": 3 +"inventory": 3, +"available_inventory": 3 } ] 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..37b576a4a --- /dev/null +++ b/public/robots.txt @@ -0,0 +1 @@ +# See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file diff --git a/test/controllers/.keep b/test/controllers/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/test/controllers/customers_controller_test.rb b/test/controllers/customers_controller_test.rb new file mode 100644 index 000000000..11fa3bb27 --- /dev/null +++ b/test/controllers/customers_controller_test.rb @@ -0,0 +1,108 @@ +require "test_helper" + +describe CustomersController do + describe "index" do + it "is a real working route" do + get customers_path + must_respond_with :success + end + + it "returns json" do + get customers_path + response.header['Content-Type'].must_include 'json' + end + + it "returns an Array" do + get customers_path + + body = JSON.parse(response.body) + body.must_be_kind_of Array + end + + it "returns all of the customers" do + get customers_path + + body = JSON.parse(response.body) + body.length.must_equal Customer.count + end + + it "returns customers with exactly the required fields" do + keys = %w(id movies_checked_out_count name phone postal_code registered_at) + + get customers_path + body = JSON.parse(response.body) + body.each do |customer| + customer.keys.sort.must_equal keys + end + end + + it "returns an empty array if there are no customers" do + Customer.destroy_all + + get customers_path + + must_respond_with :success + body = JSON.parse(response.body) + body.must_be_kind_of Array + body.must_be :empty? + end + end + + describe "show" do + it "is a real working route" do + first_id = Customer.first.id + get customer_path(first_id) + must_respond_with :success + end + + it "responds correctly when the customer is not found" do + invalid_id = Customer.last.id + 1 + get customer_path(invalid_id) + must_respond_with :not_found + + body = JSON.parse(response.body) + body.must_equal "id" => "Invalid Customer ID" + end + end + + describe "create" do + it "Can create a new customer" do + customer_data = { + name: "name", + phone: "phone", + registered_at: Date.today, + address: "address", + city: "city", + state: "state", + postal_code: "postal code" + } + + proc{ + post customers_path, params: {customer: customer_data} + }.must_change 'Customer.count', 1 + must_respond_with :success + end + + #only testing one missing attribute; validation model testing covers some of this(I think...) + it "won't change db if data is missing" do + invalid_customer_data = { + name: "name", + registered_at: Date.today, + address: "address", + city: "city", + state: "state", + postal_code: "postal code" + } + proc { + post customers_path, params: {customer: invalid_customer_data} + }.wont_change 'Customer.count' + + must_respond_with :bad_request + body = JSON.parse(response.body) + body.must_equal "errors" => {"phone" => ["can't be blank"]} + end + end + + + +end diff --git a/test/controllers/movies_controller_test.rb b/test/controllers/movies_controller_test.rb new file mode 100644 index 000000000..f210d12de --- /dev/null +++ b/test/controllers/movies_controller_test.rb @@ -0,0 +1,104 @@ +require "test_helper" + +describe MoviesController do + describe "index" do + + it "is a working route" do + get movies_path + must_respond_with :success + end + + it "returns json" do + get movies_path + response.header['Content-Type'].must_include 'json' + end + + it "returns an array" do + get movies_path + + body = JSON.parse(response.body) + body.must_be_kind_of Array + end + + it "returns all of the movies" do + get movies_path + + body = JSON.parse(response.body) + body.length.must_equal Movie.count + end + + it "returns movies with the required fields" do + keys = %w(id release_date title) + + get movies_path + body = JSON.parse(response.body) + body.each do |movie| + movie.keys.sort.must_equal keys + end + end + + it "returns an empty array if there are no movies" do + Movie.destroy_all + + get movies_path + + must_respond_with :success + body = JSON.parse(response.body) + body.must_be_kind_of Array + body.must_be :empty? + end + end + + describe "show" do + it "is a working route" do + first_id = Movie.first.id + get movie_path(first_id) + must_respond_with :success + end + + it "responds as expected when movie not found" do + first_id = Movie.first.id + 1 + get movie_path(first_id) + must_respond_with :not_found + + body = JSON.parse(response.body) + body.must_equal "id" => "Invalid Movie ID" + end + end + + describe "create" do + + let(:movie_data) { + { + title: "Jack", + overview: "Blahblah", + release_date: "1984", + inventory: 5 + } + } + + it "Create a Movie" do + proc { + post movies_path, params: {movie: movie_data} + }.must_change 'Movie.count', 1 + + must_respond_with :success + end + + it "Won't change the database if data is missing" do + invalid_movie_data = { + overview: "Nada", + release_date: "1984", + inventory: 5 + } + + proc { + post movies_path, params: { movie: invalid_movie_data} + }.wont_change 'Movie.count' + + body = JSON.parse(response.body) + body.must_equal "errors"=>{"title"=>["can't be blank"]} + end + + end +end diff --git a/test/controllers/rentals_controller_test.rb b/test/controllers/rentals_controller_test.rb new file mode 100644 index 000000000..9e460f646 --- /dev/null +++ b/test/controllers/rentals_controller_test.rb @@ -0,0 +1,175 @@ +require "test_helper" + +describe RentalsController do + describe "checkout" do + let(:customer_one) { customers :customer_one} + let(:movie_one) { movies :movie_one} + + it "is a working route" do + valid_rental_info = { + rental: { + customer_id: customer_one.id, + movie_id: movie_one.id, + due_date: "2019-11-14", + checkout_date: "2019-11-07", + } + } + + post checkout_path(params: valid_rental_info) + must_respond_with :success + end + + it "return json" do + valid_rental_info = { + rental: { + customer_id: customer_one.id, + movie_id: movie_one.id, + due_date: "2019-11-14", + checkout_date: "2019-11-07", + } + } + post checkout_path(params: valid_rental_info) + response.header['Content-Type'].must_include 'json' + end + + it "returns a hash" do + valid_rental_info = { + rental: { + customer_id: customer_one.id, + movie_id: movie_one.id, + due_date: "2019-11-14", + checkout_date: "2019-11-07", + } + } + post checkout_path(params: valid_rental_info) + + body = JSON.parse(response.body) + body.must_be_kind_of Hash + end + + it "increases the number of rentals in db" do + rental_count = Rental.count + + valid_rental_info = { + rental: { + customer_id: customer_one.id, + movie_id: movie_one.id, + due_date: "2019-11-14", + checkout_date: "2019-11-07", + } + } + post checkout_path(params: valid_rental_info) + + Rental.count.must_equal (rental_count + 1) + end + + it "returns rental id" do + valid_rental_info = { + rental: { + customer_id: customer_one.id, + movie_id: movie_one.id, + due_date: "2019-11-14", + checkout_date: "2019-11-07", + } + } + post checkout_path(params: valid_rental_info) + + body = JSON.parse(response.body) + body.must_include "id" + + end + end + + it "returns an error message if not enough inventory" do + movie = Movie.create(title: "title", overview: "Great story", inventory: 0, available_inventory: 0, release_date: "2017-4-17") + + rental_info = { + rental: { + customer_id: customer_one.id, + movie_id: movie.id, + due_date: "2019-11-14", + checkout_date: "2019-11-07" + } + } + + post checkout_path(params: rental_info) + + must_respond_with :bad_request + + body = JSON.parse(response.body) + body.must_equal "errors" => ["Not enough inventory"] + + end + + it "returns an error message if the input is incorrect" do + invalid_rental_info = { + rental: { + customer_id: customer_one.id, + movie_id: 44444444444, + due_date: "2019-11-14", + checkout_date: "2019-11-07", + } + } + + post checkout_path(params: invalid_rental_info) + + must_respond_with :bad_request + + body = JSON.parse(response.body) + body.must_equal "errors" => ["Movie does not exist"] + end + + let(:customer_one) { customers :customer_one} + let(:movie_one) { movies :movie_one} + + it "won't change db if data is missing" do + invalid_rental_info = { + rental: { + customer_id: customer_one.id, + movie_id: movie_one.id, + due_date: "2019-11-14", + # checkout_date: "2019-11-07", + } + } + + proc { + post checkout_path(params: invalid_rental_info) + }.wont_change 'Rental.count' + + must_respond_with :bad_request + body = JSON.parse(response.body) + #this error message could change but just a sample of what we might want to output + body.must_equal "errors" => {"checkout_date" => ["can't be blank"]} + end + + describe "check_in" do + let(:rental_one) { rentals :rental_one} + let(:customer_one) { customers :customer_one} + let(:movie_one) { movies :movie_one} + + it "is a working route" do + post checkin_path(params: {customer_id: customer_one.id, movie_id: movie_one.id}) + must_respond_with :ok + end + + it "returns a json" do + post checkin_path(params: {customer_id: customer_one.id, movie_id: movie_one.id}) + response.header['Content-Type'].must_include 'json' + end + + it "returns a hash" do + post checkin_path(params: {customer_id: customer_one.id, movie_id: movie_one.id}) + body = JSON.parse(response.body) + body.must_be_kind_of Hash + end + + it "changes check_in from nil to today's date" do + + end + end + + describe "overdue" do + end + + +end diff --git a/test/fixtures/.keep b/test/fixtures/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/test/fixtures/customers.yml b/test/fixtures/customers.yml new file mode 100644 index 000000000..841f83298 --- /dev/null +++ b/test/fixtures/customers.yml @@ -0,0 +1,27 @@ +# 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 +# +customer_one: + name: Betsy + registered_at: Tues, 07 Nov 2017 + address: 123 Glisan St. + city: Seattle + state: WA + postal_code: "12345" + phone: "12345678" + account_credit: 12.34 + movies_checked_out_count: 0 + +customer_two: + name: Betsy + registered_at: Wed, 29 Apr 2015 + address: 45 Columbia Street + city: Hood River + state: OR + postal_code: "12345" + phone: "56789043" + account_credit: 12.34 + movies_checked_out_count: 0 diff --git a/test/fixtures/files/.keep b/test/fixtures/files/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/test/fixtures/movies.yml b/test/fixtures/movies.yml new file mode 100644 index 000000000..3ec0992f2 --- /dev/null +++ b/test/fixtures/movies.yml @@ -0,0 +1,26 @@ +# 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 +# +movie_one: + title: "Gone with the Wind" + overview: "Lovely drama" + release_date: "1950" + inventory: 2 + available_inventory: 2 + +movie_two: + title: "Hitchiker's Guide to the Galaxy" + overview: "Philosophical fiction" + release_date: "2000" + inventory: 3 + available_inventory: 3 + +movie_three: + title: "Sharknato" + overview: "Just Because" + release_date: "1995" + inventory: 5 + available_inventory: 5 diff --git a/test/fixtures/rentals.yml b/test/fixtures/rentals.yml new file mode 100644 index 000000000..6f9bf8167 --- /dev/null +++ b/test/fixtures/rentals.yml @@ -0,0 +1,29 @@ +# 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 +# +rental_one: + customer: customer_one + movie: movie_one + checkout_date: "2017-11-06" + due_date: "2017-11-13" + +rental_two: + customer: customer_two + movie: movie_one + checkout_date: "2017-11-07" + due_date: "2017-11-14" + +rental_three: + customer: customer_one + movie: movie_two + checkout_date: "2017-11-07" + due_date: "2017-11-14" + +rental_four: + customer: customer_two + movie: movie_two + checkout_date: "2017-11-07" + due_date: "2017-11-14" 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/models/customer_test.rb b/test/models/customer_test.rb new file mode 100644 index 000000000..8d39f4370 --- /dev/null +++ b/test/models/customer_test.rb @@ -0,0 +1,76 @@ +require "test_helper" + +describe Customer do + let(:customer_one) { customers(:customer_one) } + let(:customer_two) { customers(:customer_two) } + + describe "valid" do + + it "will return false without name" do + customer_one.name = nil + customer_one.wont_be :valid? + end + + it "will return false without registered_at" do + customer_one.registered_at = nil + customer_one.wont_be :valid? + end + + it "will return false without address" do + customer_one.address = nil + customer_one.wont_be :valid? + end + + it "will return false without city" do + customer_one.city = nil + customer_one.wont_be :valid? + end + + it "will return false without state" do + customer_one.state = nil + customer_one.wont_be :valid? + end + + it "will return false without postal code" do + customer_one.postal_code = nil + customer_one.wont_be :valid? + end + + it "will return false without phone" do + customer_one.phone = nil + customer_one.wont_be :valid? + end + + end + + describe "relationships" do + before do + @customer = customers(:customer_one) + end + + describe "relationship between customers and movies" do + + it "customer responds to movies" do + @customer.must_respond_to :movies + end + + it "lists movies for a given customer" do + @customer.movies.each do |movie| + movie.must_be_kind_of Movie + end + end + end + + describe "relationship between customers and rentals" do + it "responds to rentals" do + @customer.must_respond_to :rentals + end + + it "lists rentals for a given customer" do + @customer.rentals.each do |rental| + rental.must_be_kind_of Rental + end + end + end + end +end diff --git a/test/models/movie_test.rb b/test/models/movie_test.rb new file mode 100644 index 000000000..7af0a764d --- /dev/null +++ b/test/models/movie_test.rb @@ -0,0 +1,115 @@ +require "test_helper" + +describe Movie do + before do + @invalid_movie_data = {} + end + describe "validations" do + it "can be created with all fields" do + valid = movies(:movie_one).valid? + valid.must_equal true + end + + it "requires a title" do + invalid_movie = Movie.new(@invalid_movie_data) + valid = invalid_movie.valid? + valid.must_equal false + invalid_movie.errors.messages.must_include :title + end + + it "requires an overview" do + invalid_movie = Movie.new(@invalid_movie_data) + valid = invalid_movie.valid? + valid.must_equal false + invalid_movie.errors.messages.must_include :overview + end + + it "requires a release date" do + invalid_movie = Movie.new(@invalid_movie_data) + valid = invalid_movie.valid? + valid.must_equal false + invalid_movie.errors.messages.must_include :release_date + end + + it "requires inventory to be specified" do + invalid_movie = Movie.new(@invalid_movie_data) + valid = invalid_movie.valid? + valid.must_equal false + invalid_movie.errors.messages.must_include :inventory + end + end + + describe "relationship between movie and rentals" do + before do + @movie = movies(:movie_one) + end + + it "movie responds to rentals" do + @movie.must_respond_to :rentals + end + + it "lists rentals for a given movie" do + @movie.rentals.each do |rental| + rental.must_be_kind_of Rental + end + end + end + + describe "relationship between movies and customers" do + before do + @movie = movies(:movie_one) + end + + it "movie responds to customers" do + @movie.must_respond_to :customers + end + + it "lists customers for a given movie" do + @movie.customers.each do |customer| + customer.must_be_kind_of Customer + end + end + end + + describe "inventory" do + it "returns false if a movie does not have any inventory" do + movie_data = + { + title: "Jack", + overview: "Blahblah", + release_date: "1984", + inventory: 0, + available_inventory: 0 + } + + + movie = Movie.new(movie_data) + + movie.check_inventory?.must_equal false + end + + it "returns true if a movie has at least one in inventory" do + movie = movies(:movie_one) + + movie.check_inventory?.must_equal true + end + end + + describe "reduce inventory" do + it "will reduce the inventory of a movie by one" do + movie = movies(:movie_one) + start_inventory = movie.available_inventory + movie.reduce_inventory! + movie.available_inventory.must_equal start_inventory - 1 + end + end + + describe "increase inventory" do + it "will reduce the inventory of a movie by one" do + movie = movies(:movie_one) + start_inventory = movie.available_inventory + movie.increase_inventory! + movie.available_inventory.must_equal start_inventory + 1 + end + end +end diff --git a/test/models/rental_test.rb b/test/models/rental_test.rb new file mode 100644 index 000000000..35253ab9f --- /dev/null +++ b/test/models/rental_test.rb @@ -0,0 +1,77 @@ +require "test_helper" + +describe Rental do + + #rentals YAML + let(:rental_one) { rentals :rental_one } + let(:rental_two) { rentals :rental_two } + let(:rental_three) { rentals :rental_three } + let(:rental_four) { rentals :rental_four } + + #customer YAML + let(:customer_one) { customers :customer_one} + let(:customer_two) { customers :customer_two} + + #movie YAML + let(:movie_one) { movies :movie_one} + let(:movie_two) { movies :movie_two} + + describe "relations" do + it "has a customer" do + rental_one.must_respond_to :customer + rental_one.customer.must_be_kind_of Customer + end + + it "has a movie" do + rental_one.must_respond_to :movie + rental_one.movie.must_be_kind_of Movie + end + + it "allows one movie to have many customers" do + movie_one.customers.count.must_be :>, 1 + movie_one.customers.must_include customer_one + movie_one.customers.must_include customer_two + end + + it "allows one customer to have many movies" do + customer_one.movies.count.must_be :>, 1 + customer_one.movies.must_include movie_one + customer_one.movies.must_include movie_two + end + + end + + describe "valid" do + it "will return false without a customer id" do + rental_one.customer_id = nil + rental_one.wont_be :valid? + end + + it "will return false if customer id is not a number" do + rental_one.customer_id = " " + rental_one.wont_be :valid? + end + + it "will return false without a movie id" do + rental_one.movie_id = nil + rental_one.wont_be :valid? + end + + it "will return false if movie id is not a number" do + rental_one.movie_id = " " + rental_one.wont_be :valid? + end + + it "will return false without a checkout date" do + rental_two.checkout_date = nil + rental_two.wont_be :valid? + end + + it "will return false without a due date" do + rental_three.due_date = nil + rental_three.wont_be :valid? + end + + + end +end 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 diff --git a/vendor/.keep b/vendor/.keep new file mode 100644 index 000000000..e69de29bb