diff --git a/Gemfile b/Gemfile index 2d53ba9c..53b3d210 100644 --- a/Gemfile +++ b/Gemfile @@ -45,6 +45,7 @@ group :development do # 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' + gem 'rb-readline' end diff --git a/Gemfile.lock b/Gemfile.lock index 7d037cf4..1ee7a9d1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -146,6 +146,7 @@ GEM rb-fsevent (0.10.3) rb-inotify (0.10.0) ffi (~> 1.0) + rb-readline (0.5.5) ruby-progressbar (1.10.1) ruby_dep (1.5.0) spring (2.1.0) @@ -187,6 +188,7 @@ DEPENDENCIES puma (~> 3.11) rack-cors rails (~> 5.2.0) + rb-readline spring spring-watcher-listen (~> 2.0.0) tzinfo-data diff --git a/app/controllers/movies_controller.rb b/app/controllers/movies_controller.rb index 362e2791..2ab9d4cf 100644 --- a/app/controllers/movies_controller.rb +++ b/app/controllers/movies_controller.rb @@ -8,7 +8,29 @@ def index data = Movie.all end - render status: :ok, json: data + render status: :ok, json: (data.map { + |movie| + movie.as_json( + only: [:id, :external_id, :title, :overview, :release_date, :inventory, :image_url], + methods: [:available_inventory], + ) + }) + end + + def create + movie = Movie.find_by(external_id: params[:external_id]) + if movie + movie.inventory = movie.inventory + params[:inventory].to_i + else + movie = Movie.new(movie_params) + end + success = movie.save + + if success + render status: :ok, json: movie.as_json(only: [:id, :title, :overview, :release_date, :image_url, :inventory], methods: [:available_inventory]) + else + render status: :bad_request, json: { errors: movie.errors.messages } + end end def show @@ -16,9 +38,9 @@ def show status: :ok, json: @movie.as_json( only: [:title, :overview, :release_date, :inventory], - methods: [:available_inventory] - ) - ) + methods: [:available_inventory], + ), + ) end private @@ -29,4 +51,8 @@ def require_movie render status: :not_found, json: { errors: { title: ["No movie with title #{params["title"]}"] } } end end + + def movie_params + params.require(:movie).permit(:title, :overview, :inventory, :release_date, :image_url, :external_id) + end end diff --git a/app/controllers/rentals_controller.rb b/app/controllers/rentals_controller.rb index 67e77073..f1a3e9c3 100644 --- a/app/controllers/rentals_controller.rb +++ b/app/controllers/rentals_controller.rb @@ -7,7 +7,7 @@ def check_out rental = Rental.new(movie: @movie, customer: @customer, due_date: params[:due_date]) if rental.save - render status: :ok, json: {} + render status: :ok, json: rental.as_json() else render status: :bad_request, json: { errors: rental.errors.messages } end @@ -17,10 +17,10 @@ def check_in rental = Rental.first_outstanding(@movie, @customer) unless rental return render status: :not_found, json: { - errors: { - rental: ["Customer #{@customer.id} does not have #{@movie.title} checked out"] - } - } + errors: { + rental: ["Customer #{@customer.id} does not have #{@movie.title} checked out"], + }, + } end rental.returned = true if rental.save @@ -33,18 +33,19 @@ def check_in def overdue rentals = Rental.overdue.map do |rental| { - title: rental.movie.title, - customer_id: rental.customer_id, - name: rental.customer.name, - postal_code: rental.customer.postal_code, - checkout_date: rental.checkout_date, - due_date: rental.due_date + title: rental.movie.title, + customer_id: rental.customer_id, + name: rental.customer.name, + postal_code: rental.customer.postal_code, + checkout_date: rental.checkout_date, + due_date: rental.due_date, } end render status: :ok, json: rentals end -private + private + # TODO: make error payloads arrays def require_movie @movie = Movie.find_by title: params[:title] diff --git a/app/models/movie.rb b/app/models/movie.rb index 0016080b..d9dc6f95 100644 --- a/app/models/movie.rb +++ b/app/models/movie.rb @@ -2,6 +2,10 @@ class Movie < ApplicationRecord has_many :rentals has_many :customers, through: :rentals + validates :title, :inventory, presence: true + + validates :inventory, numericality: { only_integer: true } + def available_inventory self.inventory - Rental.where(movie: self, returned: false).length end @@ -10,7 +14,7 @@ def image_url orig_value = read_attribute :image_url if !orig_value MovieWrapper::DEFAULT_IMG_URL - elsif external_id + elsif !orig_value.include?("https://") && external_id MovieWrapper.construct_image_url(orig_value) else orig_value diff --git a/app/models/rental.rb b/app/models/rental.rb index 18654f04..d3a81447 100644 --- a/app/models/rental.rb +++ b/app/models/rental.rb @@ -9,6 +9,13 @@ class Rental < ApplicationRecord after_initialize :set_checkout_date after_initialize :set_returned + validate :availability + + def availability + if self.movie.available_inventory < 1 + errors.add(:inventory, "All copies of that movie are currently checked out") + end + end def self.first_outstanding(movie, customer) self.where(movie: movie, customer: customer, returned: false).order(:due_date).first @@ -18,7 +25,8 @@ def self.overdue self.where(returned: false).where("due_date < ?", Date.today) end -private + private + def due_date_in_future return unless self.due_date unless due_date > Date.today diff --git a/config/routes.rb b/config/routes.rb index f4c99688..b72b7fe5 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -3,12 +3,11 @@ resources :customers, only: [:index] - resources :movies, only: [:index, :show], param: :title + resources :movies, only: [:index, :show, :create], param: :title post "/rentals/:title/check-out", to: "rentals#check_out", as: "check_out" post "/rentals/:title/return", to: "rentals#check_in", as: "check_in" get "/rentals/overdue", to: "rentals#overdue", as: "overdue" - root 'movies#index' - + root "movies#index" end diff --git a/package.json b/package.json index e5f1f022..7e274b58 100644 --- a/package.json +++ b/package.json @@ -8,10 +8,12 @@ }, "dependencies": { "body-parser": "~1.13.2", + "bootstrap": "^4.3.1", "cookie-parser": "~1.3.5", "debug": "~2.6.9", "express": "~4.13.1", "jade": "~1.11.0", + "moment": "^2.24.0", "morgan": "~1.9.1", "sequelize": "^5.1.0", "serve-favicon": "~2.3.0"