From 01e91ca66b171265a227b7d8c3ab81a52c206116 Mon Sep 17 00:00:00 2001 From: dgpangan Date: Sun, 19 Feb 2023 00:30:41 +0800 Subject: [PATCH 1/9] feat: create guest and reservation models --- Gemfile | 6 +-- Gemfile.lock | 7 +++- app/models/guest.rb | 3 ++ app/models/reservation.rb | 3 ++ db/migrate/20230218155553_create_guests.rb | 12 ++++++ .../20230218160520_create_reservations.rb | 18 ++++++++ ...0230218161038_add_guest_to_reservations.rb | 5 +++ db/schema.rb | 41 +++++++++++++++++++ spec/models/guest_spec.rb | 7 ++++ spec/models/reservation_spec.rb | 7 ++++ spec/rails_helper.rb | 7 ++++ 11 files changed, 111 insertions(+), 5 deletions(-) create mode 100644 app/models/guest.rb create mode 100644 app/models/reservation.rb create mode 100644 db/migrate/20230218155553_create_guests.rb create mode 100644 db/migrate/20230218160520_create_reservations.rb create mode 100644 db/migrate/20230218161038_add_guest_to_reservations.rb create mode 100644 db/schema.rb create mode 100644 spec/models/guest_spec.rb create mode 100644 spec/models/reservation_spec.rb diff --git a/Gemfile b/Gemfile index e58cffc..384cc90 100644 --- a/Gemfile +++ b/Gemfile @@ -11,9 +11,7 @@ gem "bootsnap", require: false group :development, :test do gem "debug", platforms: %i[ mri mingw x64_mingw ] + gem "pry-rails" gem "rspec-rails" -end - -group :development do - gem 'pry' + gem "shoulda-matchers" end diff --git a/Gemfile.lock b/Gemfile.lock index 6d1f2f6..7b2cc45 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -109,6 +109,8 @@ GEM pry (0.14.2) coderay (~> 1.1) method_source (~> 1.0) + pry-rails (0.3.9) + pry (>= 0.10.4) puma (5.6.5) nio4r (~> 2.0) racc (1.6.2) @@ -159,6 +161,8 @@ GEM rspec-mocks (~> 3.11) rspec-support (~> 3.11) rspec-support (3.12.0) + shoulda-matchers (5.3.0) + activesupport (>= 5.2.0) thor (1.2.1) timeout (0.3.2) tzinfo (2.0.6) @@ -175,10 +179,11 @@ DEPENDENCIES bootsnap debug mysql2 (~> 0.5) - pry + pry-rails puma (~> 5.0) rails (~> 7.0.4, >= 7.0.4.2) rspec-rails + shoulda-matchers tzinfo-data RUBY VERSION diff --git a/app/models/guest.rb b/app/models/guest.rb new file mode 100644 index 0000000..562c0ff --- /dev/null +++ b/app/models/guest.rb @@ -0,0 +1,3 @@ +class Guest < ApplicationRecord + has_many :reservations +end diff --git a/app/models/reservation.rb b/app/models/reservation.rb new file mode 100644 index 0000000..e6dfaaa --- /dev/null +++ b/app/models/reservation.rb @@ -0,0 +1,3 @@ +class Reservation < ApplicationRecord + belongs_to :guest +end diff --git a/db/migrate/20230218155553_create_guests.rb b/db/migrate/20230218155553_create_guests.rb new file mode 100644 index 0000000..1fd1e3a --- /dev/null +++ b/db/migrate/20230218155553_create_guests.rb @@ -0,0 +1,12 @@ +class CreateGuests < ActiveRecord::Migration[7.0] + def change + create_table :guests do |t| + t.string :first_name + t.string :last_name + t.string :phone + t.string :email + + t.timestamps + end + end +end diff --git a/db/migrate/20230218160520_create_reservations.rb b/db/migrate/20230218160520_create_reservations.rb new file mode 100644 index 0000000..a22400b --- /dev/null +++ b/db/migrate/20230218160520_create_reservations.rb @@ -0,0 +1,18 @@ +class CreateReservations < ActiveRecord::Migration[7.0] + def change + create_table :reservations do |t| + t.string :code + t.date :date_start + t.date :date_end + t.integer :number_adults + t.integer :number_children + t.integer :number_infants + t.float :amount_payout + t.float :amount_security + t.string :currency + t.string :status + + t.timestamps + end + end +end diff --git a/db/migrate/20230218161038_add_guest_to_reservations.rb b/db/migrate/20230218161038_add_guest_to_reservations.rb new file mode 100644 index 0000000..3b19fb5 --- /dev/null +++ b/db/migrate/20230218161038_add_guest_to_reservations.rb @@ -0,0 +1,5 @@ +class AddGuestToReservations < ActiveRecord::Migration[7.0] + def change + add_reference :reservations, :guest, null: false, foreign_key: true + end +end diff --git a/db/schema.rb b/db/schema.rb new file mode 100644 index 0000000..99f2de7 --- /dev/null +++ b/db/schema.rb @@ -0,0 +1,41 @@ +# 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. +# +# This file is the source Rails uses to define your schema when running `bin/rails +# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to +# be faster and is potentially less error prone than running all of your +# migrations from scratch. Old migrations may fail to apply correctly if those +# migrations use external dependencies or application code. +# +# It's strongly recommended that you check this file into your version control system. + +ActiveRecord::Schema[7.0].define(version: 2023_02_18_161038) do + create_table "guests", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.string "first_name" + t.string "last_name" + t.string "phone" + t.string "email" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + create_table "reservations", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.string "code" + t.date "date_start" + t.date "date_end" + t.integer "number_adults" + t.integer "number_children" + t.integer "number_infants" + t.float "amount_payout" + t.float "amount_security" + t.string "currency" + t.string "status" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.bigint "guest_id", null: false + t.index ["guest_id"], name: "index_reservations_on_guest_id" + end + + add_foreign_key "reservations", "guests" +end diff --git a/spec/models/guest_spec.rb b/spec/models/guest_spec.rb new file mode 100644 index 0000000..940d6e2 --- /dev/null +++ b/spec/models/guest_spec.rb @@ -0,0 +1,7 @@ +require 'rails_helper' + +RSpec.describe Guest, type: :model do + describe 'associations' do + it { should have_many(:reservations) } + end +end diff --git a/spec/models/reservation_spec.rb b/spec/models/reservation_spec.rb new file mode 100644 index 0000000..4e70a7c --- /dev/null +++ b/spec/models/reservation_spec.rb @@ -0,0 +1,7 @@ +require 'rails_helper' + +RSpec.describe Reservation, type: :model do + describe 'associations' do + it { should belong_to(:guest) } + end +end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index a53bdba..3f9c1fe 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -61,3 +61,10 @@ # arbitrary gems may also be filtered via: # config.filter_gems_from_backtrace("gem name") end + +Shoulda::Matchers.configure do |config| + config.integrate do |with| + with.test_framework :rspec + with.library :rails + end +end From 040a64d3419a091e1779425527e657daf43a8674 Mon Sep 17 00:00:00 2001 From: dgpangan Date: Sun, 19 Feb 2023 16:01:17 +0800 Subject: [PATCH 2/9] test: add guest model spec and rubocop --- .rubocop.yml | 9 ++ Gemfile | 30 +++--- Gemfile.lock | 32 +++++++ Rakefile | 4 +- app/channels/application_cable/channel.rb | 2 + app/channels/application_cable/connection.rb | 2 + app/controllers/application_controller.rb | 2 + app/jobs/application_job.rb | 2 + app/mailers/application_mailer.rb | 6 +- app/models/application_record.rb | 2 + app/models/guest.rb | 9 ++ app/models/reservation.rb | 2 + bin/bundle | 40 ++++---- bin/rails | 8 +- bin/rake | 6 +- bin/setup | 18 ++-- config.ru | 4 +- config/application.rb | 6 +- config/boot.rb | 8 +- config/environment.rb | 4 +- config/environments/development.rb | 9 +- config/environments/production.rb | 14 +-- config/environments/test.rb | 8 +- config/initializers/cors.rb | 1 + .../initializers/filter_parameter_logging.rb | 6 +- config/initializers/inflections.rb | 1 + config/puma.rb | 14 +-- config/routes.rb | 2 + db/migrate/20230218155553_create_guests.rb | 2 + .../20230218160520_create_reservations.rb | 2 + ...0230218161038_add_guest_to_reservations.rb | 2 + db/schema.rb | 50 +++++----- db/seeds.rb | 1 + spec/factories/guest.rb | 18 ++++ spec/models/guest_spec.rb | 26 +++++ spec/models/reservation_spec.rb | 2 + spec/rails_helper.rb | 8 +- spec/spec_helper.rb | 94 +++++++++---------- .../application_cable/connection_test.rb | 22 +++-- test/test_helper.rb | 22 +++-- 40 files changed, 337 insertions(+), 163 deletions(-) create mode 100644 .rubocop.yml create mode 100644 spec/factories/guest.rb diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..5040ae9 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,9 @@ +AllCops: + Exclude: + - 'bin/*' + +Style/Documentation: + Enabled: false + +Metrics/MethodLength: + Max: 20 \ No newline at end of file diff --git a/Gemfile b/Gemfile index 384cc90..7f5408a 100644 --- a/Gemfile +++ b/Gemfile @@ -1,17 +1,25 @@ -source "https://rubygems.org" +# frozen_string_literal: true + +source 'https://rubygems.org' git_source(:github) { |repo| "https://github.com/#{repo}.git" } -ruby "3.2.1" +ruby '3.2.1' -gem "rails", "~> 7.0.4", ">= 7.0.4.2" -gem "mysql2", "~> 0.5" -gem "puma", "~> 5.0" -gem "tzinfo-data", platforms: %i[ mingw mswin x64_mingw jruby ] -gem "bootsnap", require: false +gem 'bootsnap', require: false +gem 'mysql2', '~> 0.5' +gem 'puma', '~> 5.0' +gem 'rails', '~> 7.0.4', '>= 7.0.4.2' +gem 'tzinfo-data', platforms: %i[mingw mswin x64_mingw jruby] group :development, :test do - gem "debug", platforms: %i[ mri mingw x64_mingw ] - gem "pry-rails" - gem "rspec-rails" - gem "shoulda-matchers" + gem 'debug', platforms: %i[mri mingw x64_mingw] + gem 'factory_bot_rails' + gem 'faker' + gem 'pry-rails' + gem 'rspec-rails' + gem 'shoulda-matchers' +end + +group :development do + gem 'rubocop', require: false end diff --git a/Gemfile.lock b/Gemfile.lock index 7b2cc45..b91313e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -66,6 +66,7 @@ GEM i18n (>= 1.6, < 2) minitest (>= 5.1) tzinfo (~> 2.0) + ast (2.4.2) bootsnap (1.16.0) msgpack (~> 1.2) builder (3.2.4) @@ -76,10 +77,18 @@ GEM debug (1.7.1) diff-lcs (1.5.0) erubi (1.12.0) + factory_bot (6.2.1) + activesupport (>= 5.0.0) + factory_bot_rails (6.2.0) + factory_bot (~> 6.2.0) + railties (>= 5.0.0) + faker (3.1.1) + i18n (>= 1.8.11, < 2) globalid (1.1.0) activesupport (>= 5.0) i18n (1.12.0) concurrent-ruby (~> 1.0) + json (2.6.3) loofah (2.19.1) crass (~> 1.0.2) nokogiri (>= 1.5.9) @@ -106,6 +115,9 @@ GEM nio4r (2.5.8) nokogiri (1.14.2-x86_64-darwin) racc (~> 1.4) + parallel (1.22.1) + parser (3.2.1.0) + ast (~> 2.4.1) pry (0.14.2) coderay (~> 1.1) method_source (~> 1.0) @@ -143,7 +155,10 @@ GEM rake (>= 12.2) thor (~> 1.0) zeitwerk (~> 2.5) + rainbow (3.1.1) rake (13.0.6) + regexp_parser (2.7.0) + rexml (3.2.5) rspec-core (3.12.1) rspec-support (~> 3.12.0) rspec-expectations (3.12.2) @@ -161,12 +176,26 @@ GEM rspec-mocks (~> 3.11) rspec-support (~> 3.11) rspec-support (3.12.0) + rubocop (1.45.1) + json (~> 2.3) + parallel (~> 1.10) + parser (>= 3.2.0.0) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 1.8, < 3.0) + rexml (>= 3.2.5, < 4.0) + rubocop-ast (>= 1.24.1, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 2.4.0, < 3.0) + rubocop-ast (1.26.0) + parser (>= 3.2.1.0) + ruby-progressbar (1.11.0) shoulda-matchers (5.3.0) activesupport (>= 5.2.0) thor (1.2.1) timeout (0.3.2) tzinfo (2.0.6) concurrent-ruby (~> 1.0) + unicode-display_width (2.4.2) websocket-driver (0.7.5) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) @@ -178,11 +207,14 @@ PLATFORMS DEPENDENCIES bootsnap debug + factory_bot_rails + faker mysql2 (~> 0.5) pry-rails puma (~> 5.0) rails (~> 7.0.4, >= 7.0.4.2) rspec-rails + rubocop shoulda-matchers tzinfo-data diff --git a/Rakefile b/Rakefile index 9a5ea73..488c551 100644 --- a/Rakefile +++ b/Rakefile @@ -1,6 +1,8 @@ +# frozen_string_literal: true + # 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" +require_relative 'config/application' Rails.application.load_tasks diff --git a/app/channels/application_cable/channel.rb b/app/channels/application_cable/channel.rb index d672697..9aec230 100644 --- a/app/channels/application_cable/channel.rb +++ b/app/channels/application_cable/channel.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ApplicationCable class Channel < ActionCable::Channel::Base end diff --git a/app/channels/application_cable/connection.rb b/app/channels/application_cable/connection.rb index 0ff5442..8d6c2a1 100644 --- a/app/channels/application_cable/connection.rb +++ b/app/channels/application_cable/connection.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ApplicationCable class Connection < ActionCable::Connection::Base end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 4ac8823..13c271f 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,2 +1,4 @@ +# frozen_string_literal: true + class ApplicationController < ActionController::API end diff --git a/app/jobs/application_job.rb b/app/jobs/application_job.rb index d394c3d..bef3959 100644 --- a/app/jobs/application_job.rb +++ b/app/jobs/application_job.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ApplicationJob < ActiveJob::Base # Automatically retry jobs that encountered a deadlock # retry_on ActiveRecord::Deadlocked diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb index 3c34c81..d84cb6e 100644 --- a/app/mailers/application_mailer.rb +++ b/app/mailers/application_mailer.rb @@ -1,4 +1,6 @@ +# frozen_string_literal: true + class ApplicationMailer < ActionMailer::Base - default from: "from@example.com" - layout "mailer" + default from: 'from@example.com' + layout 'mailer' end diff --git a/app/models/application_record.rb b/app/models/application_record.rb index b63caeb..08dc537 100644 --- a/app/models/application_record.rb +++ b/app/models/application_record.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ApplicationRecord < ActiveRecord::Base primary_abstract_class end diff --git a/app/models/guest.rb b/app/models/guest.rb index 562c0ff..c4719ec 100644 --- a/app/models/guest.rb +++ b/app/models/guest.rb @@ -1,3 +1,12 @@ +# frozen_string_literal: true + class Guest < ApplicationRecord has_many :reservations + + validates :first_name, + :last_name, + :phone, + :email, presence: true + + validates :email, format: { with: URI::MailTo::EMAIL_REGEXP } end diff --git a/app/models/reservation.rb b/app/models/reservation.rb index e6dfaaa..179111f 100644 --- a/app/models/reservation.rb +++ b/app/models/reservation.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class Reservation < ApplicationRecord belongs_to :guest end diff --git a/bin/bundle b/bin/bundle index ee73929..3cb2676 100755 --- a/bin/bundle +++ b/bin/bundle @@ -8,46 +8,46 @@ # this file is here to facilitate running it. # -require "rubygems" +require 'rubygems' m = Module.new do module_function def invoked_as_script? - File.expand_path($0) == File.expand_path(__FILE__) + File.expand_path($PROGRAM_NAME) == File.expand_path(__FILE__) end def env_var_version - ENV["BUNDLER_VERSION"] + ENV['BUNDLER_VERSION'] end def cli_arg_version return unless invoked_as_script? # don't want to hijack other binstubs - return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update` + return unless 'update'.start_with?(ARGV.first || ' ') # must be running `bundle update` + bundler_version = nil update_index = nil ARGV.each_with_index do |a, i| - if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN - bundler_version = a - end + bundler_version = a if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/ - bundler_version = $1 + + bundler_version = Regexp.last_match(1) update_index = i end bundler_version end def gemfile - gemfile = ENV["BUNDLE_GEMFILE"] + gemfile = ENV['BUNDLE_GEMFILE'] return gemfile if gemfile && !gemfile.empty? - File.expand_path("../Gemfile", __dir__) + File.expand_path('../Gemfile', __dir__) end def lockfile lockfile = case File.basename(gemfile) - when "gems.rb" then gemfile.sub(/\.rb$/, gemfile) + when 'gems.rb' then gemfile.sub(/\.rb$/, gemfile) else "#{gemfile}.lock" end File.expand_path(lockfile) @@ -55,8 +55,10 @@ m = Module.new do def lockfile_version return unless File.file?(lockfile) + lockfile_contents = File.read(lockfile) return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/ + Regexp.last_match(1) end @@ -76,20 +78,24 @@ m = Module.new do end def load_bundler! - ENV["BUNDLE_GEMFILE"] ||= gemfile + ENV['BUNDLE_GEMFILE'] ||= gemfile activate_bundler end def activate_bundler gem_error = activation_error_handling do - gem "bundler", bundler_requirement + gem 'bundler', bundler_requirement end return if gem_error.nil? + require_error = activation_error_handling do - require "bundler/version" + require 'bundler/version' + end + if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION)) + return end - return if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION)) + warn "Activating bundler (#{bundler_requirement}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_requirement}'`" exit 42 end @@ -104,6 +110,4 @@ end m.load_bundler! -if m.invoked_as_script? - load Gem.bin_path("bundler", "bundle") -end +load Gem.bin_path('bundler', 'bundle') if m.invoked_as_script? diff --git a/bin/rails b/bin/rails index efc0377..a31728a 100755 --- a/bin/rails +++ b/bin/rails @@ -1,4 +1,6 @@ #!/usr/bin/env ruby -APP_PATH = File.expand_path("../config/application", __dir__) -require_relative "../config/boot" -require "rails/commands" +# frozen_string_literal: true + +APP_PATH = File.expand_path('../config/application', __dir__) +require_relative '../config/boot' +require 'rails/commands' diff --git a/bin/rake b/bin/rake index 4fbf10b..c199955 100755 --- a/bin/rake +++ b/bin/rake @@ -1,4 +1,6 @@ #!/usr/bin/env ruby -require_relative "../config/boot" -require "rake" +# frozen_string_literal: true + +require_relative '../config/boot' +require 'rake' Rake.application.run diff --git a/bin/setup b/bin/setup index ec47b79..516b651 100755 --- a/bin/setup +++ b/bin/setup @@ -1,8 +1,10 @@ #!/usr/bin/env ruby -require "fileutils" +# frozen_string_literal: true + +require 'fileutils' # path to your application root. -APP_ROOT = File.expand_path("..", __dir__) +APP_ROOT = File.expand_path('..', __dir__) def system!(*args) system(*args) || abort("\n== Command #{args} failed ==") @@ -13,9 +15,9 @@ FileUtils.chdir APP_ROOT do # This script is idempotent, so that you can run it at any time and get an expectable outcome. # Add necessary setup steps to this file. - puts "== Installing dependencies ==" - system! "gem install bundler --conservative" - system("bundle check") || system!("bundle install") + 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") @@ -23,11 +25,11 @@ FileUtils.chdir APP_ROOT do # end puts "\n== Preparing database ==" - system! "bin/rails db:prepare" + system! 'bin/rails db:prepare' puts "\n== Removing old logs and tempfiles ==" - system! "bin/rails log:clear tmp:clear" + system! 'bin/rails log:clear tmp:clear' puts "\n== Restarting application server ==" - system! "bin/rails restart" + system! 'bin/rails restart' end diff --git a/config.ru b/config.ru index 4a3c09a..6dc8321 100644 --- a/config.ru +++ b/config.ru @@ -1,6 +1,8 @@ +# frozen_string_literal: true + # This file is used by Rack-based servers to start the application. -require_relative "config/environment" +require_relative 'config/environment' run Rails.application Rails.application.load_server diff --git a/config/application.rb b/config/application.rb index 147f25a..8cbf76f 100644 --- a/config/application.rb +++ b/config/application.rb @@ -1,6 +1,8 @@ -require_relative "boot" +# frozen_string_literal: true -require "rails/all" +require_relative 'boot' + +require 'rails/all' # Require the gems listed in Gemfile, including any gems # you've limited to :test, :development, or :production. diff --git a/config/boot.rb b/config/boot.rb index 988a5dd..c04863f 100644 --- a/config/boot.rb +++ b/config/boot.rb @@ -1,4 +1,6 @@ -ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) +# frozen_string_literal: true -require "bundler/setup" # Set up gems listed in the Gemfile. -require "bootsnap/setup" # Speed up boot time by caching expensive operations. +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) + +require 'bundler/setup' # Set up gems listed in the Gemfile. +require 'bootsnap/setup' # Speed up boot time by caching expensive operations. diff --git a/config/environment.rb b/config/environment.rb index cac5315..d5abe55 100644 --- a/config/environment.rb +++ b/config/environment.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Load the Rails application. -require_relative "application" +require_relative 'application' # Initialize the Rails application. Rails.application.initialize! diff --git a/config/environments/development.rb b/config/environments/development.rb index 3d6b073..2ee6f75 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -1,4 +1,6 @@ -require "active_support/core_ext/integer/time" +# frozen_string_literal: true + +require 'active_support/core_ext/integer/time' Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. @@ -19,10 +21,10 @@ # Enable/disable caching. By default caching is disabled. # Run rails dev:cache to toggle caching. - if Rails.root.join("tmp/caching-dev.txt").exist? + if Rails.root.join('tmp/caching-dev.txt').exist? config.cache_store = :memory_store config.public_file_server.headers = { - "Cache-Control" => "public, max-age=#{2.days.to_i}" + 'Cache-Control' => "public, max-age=#{2.days.to_i}" } else config.action_controller.perform_caching = false @@ -53,7 +55,6 @@ # Highlight code that triggered database queries in logs. config.active_record.verbose_query_logs = true - # Raises error for missing translations. # config.i18n.raise_on_missing_translations = true diff --git a/config/environments/production.rb b/config/environments/production.rb index cb0b679..d84418a 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -1,4 +1,6 @@ -require "active_support/core_ext/integer/time" +# frozen_string_literal: true + +require 'active_support/core_ext/integer/time' Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. @@ -13,7 +15,7 @@ config.eager_load = true # Full error reports are disabled and caching is turned on. - config.consider_all_requests_local = false + config.consider_all_requests_local = false # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"] # or in config/master.key. This key is used to decrypt credentials (and other encrypted files). @@ -21,7 +23,7 @@ # 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? + config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? # Enable serving of images, stylesheets, and JavaScripts from an asset server. # config.asset_host = "http://assets.example.com" @@ -46,7 +48,7 @@ config.log_level = :info # Prepend all log lines with the following tags. - config.log_tags = [ :request_id ] + config.log_tags = [:request_id] # Use a different cache store in production. # config.cache_store = :mem_cache_store @@ -75,8 +77,8 @@ # 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) + if ENV['RAILS_LOG_TO_STDOUT'].present? + logger = ActiveSupport::Logger.new($stdout) logger.formatter = config.log_formatter config.logger = ActiveSupport::TaggedLogging.new(logger) end diff --git a/config/environments/test.rb b/config/environments/test.rb index 6ea4d1e..8f3f63c 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -1,4 +1,6 @@ -require "active_support/core_ext/integer/time" +# frozen_string_literal: true + +require 'active_support/core_ext/integer/time' # The test environment is used exclusively to run your application's # test suite. You never need to work with it otherwise. Remember that @@ -14,12 +16,12 @@ # Eager loading loads your whole application. When running a single test locally, # this probably isn't necessary. It's a good idea to do in a continuous integration # system, or in some way before deploying your code. - config.eager_load = ENV["CI"].present? + config.eager_load = ENV['CI'].present? # 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.to_i}" + 'Cache-Control' => "public, max-age=#{1.hour.to_i}" } # Show full error reports and disable caching. diff --git a/config/initializers/cors.rb b/config/initializers/cors.rb index e5a82f1..38d411f 100644 --- a/config/initializers/cors.rb +++ b/config/initializers/cors.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: true # Be sure to restart your server when you modify this file. # Avoid CORS issues when API is called from the frontend app. diff --git a/config/initializers/filter_parameter_logging.rb b/config/initializers/filter_parameter_logging.rb index adc6568..3df77c5 100644 --- a/config/initializers/filter_parameter_logging.rb +++ b/config/initializers/filter_parameter_logging.rb @@ -1,8 +1,10 @@ +# frozen_string_literal: true + # Be sure to restart your server when you modify this file. # Configure parameters to be filtered from the log file. Use this to limit dissemination of # sensitive information. See the ActiveSupport::ParameterFilter documentation for supported # notations and behaviors. -Rails.application.config.filter_parameters += [ - :passw, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn +Rails.application.config.filter_parameters += %i[ + passw secret token _key crypt salt certificate otp ssn ] diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb index 3860f65..6c78420 100644 --- a/config/initializers/inflections.rb +++ b/config/initializers/inflections.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: true # Be sure to restart your server when you modify this file. # Add new inflection rules using the following format. Inflections diff --git a/config/puma.rb b/config/puma.rb index daaf036..1713441 100644 --- a/config/puma.rb +++ b/config/puma.rb @@ -1,28 +1,30 @@ +# frozen_string_literal: true + # 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. # -max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } -min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count } +max_threads_count = ENV.fetch('RAILS_MAX_THREADS', 5) +min_threads_count = ENV.fetch('RAILS_MIN_THREADS') { max_threads_count } threads min_threads_count, max_threads_count # Specifies the `worker_timeout` threshold that Puma will use to wait before # terminating a worker in development environments. # -worker_timeout 3600 if ENV.fetch("RAILS_ENV", "development") == "development" +worker_timeout 3600 if ENV.fetch('RAILS_ENV', 'development') == 'development' # Specifies the `port` that Puma will listen on to receive requests; default is 3000. # -port ENV.fetch("PORT") { 3000 } +port ENV.fetch('PORT', 3000) # Specifies the `environment` that Puma will run in. # -environment ENV.fetch("RAILS_ENV") { "development" } +environment ENV.fetch('RAILS_ENV', 'development') # Specifies the `pidfile` that Puma will use. -pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" } +pidfile ENV.fetch('PIDFILE', 'tmp/pids/server.pid') # Specifies the number of `workers` to boot in clustered mode. # Workers are forked web server processes. If using threads and workers together diff --git a/config/routes.rb b/config/routes.rb index 262ffd5..7b329f5 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + Rails.application.routes.draw do # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html diff --git a/db/migrate/20230218155553_create_guests.rb b/db/migrate/20230218155553_create_guests.rb index 1fd1e3a..e565adf 100644 --- a/db/migrate/20230218155553_create_guests.rb +++ b/db/migrate/20230218155553_create_guests.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class CreateGuests < ActiveRecord::Migration[7.0] def change create_table :guests do |t| diff --git a/db/migrate/20230218160520_create_reservations.rb b/db/migrate/20230218160520_create_reservations.rb index a22400b..86e1cd3 100644 --- a/db/migrate/20230218160520_create_reservations.rb +++ b/db/migrate/20230218160520_create_reservations.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class CreateReservations < ActiveRecord::Migration[7.0] def change create_table :reservations do |t| diff --git a/db/migrate/20230218161038_add_guest_to_reservations.rb b/db/migrate/20230218161038_add_guest_to_reservations.rb index 3b19fb5..61f0966 100644 --- a/db/migrate/20230218161038_add_guest_to_reservations.rb +++ b/db/migrate/20230218161038_add_guest_to_reservations.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AddGuestToReservations < ActiveRecord::Migration[7.0] def change add_reference :reservations, :guest, null: false, foreign_key: true diff --git a/db/schema.rb b/db/schema.rb index 99f2de7..4b48bf4 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # 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. @@ -10,32 +12,32 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2023_02_18_161038) do - create_table "guests", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "first_name" - t.string "last_name" - t.string "phone" - t.string "email" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false +ActiveRecord::Schema[7.0].define(version: 20_230_218_161_038) do + create_table 'guests', charset: 'utf8mb4', collation: 'utf8mb4_0900_ai_ci', force: :cascade do |t| + t.string 'first_name' + t.string 'last_name' + t.string 'phone' + t.string 'email' + t.datetime 'created_at', null: false + t.datetime 'updated_at', null: false end - create_table "reservations", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.string "code" - t.date "date_start" - t.date "date_end" - t.integer "number_adults" - t.integer "number_children" - t.integer "number_infants" - t.float "amount_payout" - t.float "amount_security" - t.string "currency" - t.string "status" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.bigint "guest_id", null: false - t.index ["guest_id"], name: "index_reservations_on_guest_id" + create_table 'reservations', charset: 'utf8mb4', collation: 'utf8mb4_0900_ai_ci', force: :cascade do |t| + t.string 'code' + t.date 'date_start' + t.date 'date_end' + t.integer 'number_adults' + t.integer 'number_children' + t.integer 'number_infants' + t.float 'amount_payout' + t.float 'amount_security' + t.string 'currency' + t.string 'status' + t.datetime 'created_at', null: false + t.datetime 'updated_at', null: false + t.bigint 'guest_id', null: false + t.index ['guest_id'], name: 'index_reservations_on_guest_id' end - add_foreign_key "reservations", "guests" + add_foreign_key 'reservations', 'guests' end diff --git a/db/seeds.rb b/db/seeds.rb index bc25fce..0664d1b 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: true # 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 bin/rails db:seed command (or created alongside the database with db:setup). # diff --git a/spec/factories/guest.rb b/spec/factories/guest.rb new file mode 100644 index 0000000..451bc50 --- /dev/null +++ b/spec/factories/guest.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :guest, class: 'Guest' do + first_name { Faker::Name.first_name } + last_name { Faker::Name.first_name } + phone { Faker::PhoneNumber.phone_number } + email { "#{first_name}_#{last_name}@somedomain.com" } + end + + trait :incomplete do + complete { false } + first_name { Faker::Name.first_name } + last_name { Faker::Name.first_name } + phone { Faker::PhoneNumber.phone_number } + email { "#{first_name}_#{last_name}@somedomain.com" } + end +end diff --git a/spec/models/guest_spec.rb b/spec/models/guest_spec.rb index 940d6e2..55379d9 100644 --- a/spec/models/guest_spec.rb +++ b/spec/models/guest_spec.rb @@ -1,7 +1,33 @@ +# frozen_string_literal: true + require 'rails_helper' RSpec.describe Guest, type: :model do describe 'associations' do it { should have_many(:reservations) } end + + describe 'validations' do + it 'is valid with attributes' do + expect(Guest.new(first_name: Faker::Name.first_name, + last_name: Faker::Name.last_name, + phone: Faker::PhoneNumber.phone_number)).to be_valid + end + + it 'is not valid without a first name' do + expect(Guest.new(first_name: nil)).not_to be_valid + end + + it 'is not valid without a last name' do + end + + it 'is not valid without a phone' do + end + + it 'is not valid without an email' do + end + + it 'is invalid if email is invalid' do + end + end end diff --git a/spec/models/reservation_spec.rb b/spec/models/reservation_spec.rb index 4e70a7c..35cd946 100644 --- a/spec/models/reservation_spec.rb +++ b/spec/models/reservation_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'rails_helper' RSpec.describe Reservation, type: :model do diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 3f9c1fe..ecebe02 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -1,9 +1,11 @@ +# frozen_string_literal: true + # This file is copied to spec/ when you run 'rails generate rspec:install' require 'spec_helper' ENV['RAILS_ENV'] ||= 'test' require_relative '../config/environment' # Prevent database truncation if the environment is production -abort("The Rails environment is running in production mode!") if Rails.env.production? +abort('The Rails environment is running in production mode!') if Rails.env.production? require 'rspec/rails' # Add additional requires below this line. Rails is not loaded until this point! @@ -68,3 +70,7 @@ with.library :rails end end + +RSpec.configure do |config| + config.include FactoryBot::Syntax::Methods +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index a0d4080..89f676c 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # This file was generated by the `rails generate rspec:install` command. Conventionally, all # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. # The generated `.rspec` file contains `--require spec_helper` which will cause @@ -44,51 +46,49 @@ # triggering implicit auto-inclusion in groups with matching metadata. config.shared_context_metadata_behavior = :apply_to_host_groups -# The settings below are suggested to provide a good initial experience -# with RSpec, but feel free to customize to your heart's content. -=begin - # This allows you to limit a spec run to individual examples or groups - # you care about by tagging them with `:focus` metadata. When nothing - # is tagged with `:focus`, all examples get run. RSpec also provides - # aliases for `it`, `describe`, and `context` that include `:focus` - # metadata: `fit`, `fdescribe` and `fcontext`, respectively. - config.filter_run_when_matching :focus - - # Allows RSpec to persist some state between runs in order to support - # the `--only-failures` and `--next-failure` CLI options. We recommend - # you configure your source control system to ignore this file. - config.example_status_persistence_file_path = "spec/examples.txt" - - # Limits the available syntax to the non-monkey patched syntax that is - # recommended. For more details, see: - # https://relishapp.com/rspec/rspec-core/docs/configuration/zero-monkey-patching-mode - config.disable_monkey_patching! - - # Many RSpec users commonly either run the entire suite or an individual - # file, and it's useful to allow more verbose output when running an - # individual spec file. - if config.files_to_run.one? - # Use the documentation formatter for detailed output, - # unless a formatter has already been configured - # (e.g. via a command-line flag). - config.default_formatter = "doc" - end - - # Print the 10 slowest examples and example groups at the - # end of the spec run, to help surface which specs are running - # particularly slow. - config.profile_examples = 10 - - # Run specs in random order to surface order dependencies. If you find an - # order dependency and want to debug it, you can fix the order by providing - # the seed, which is printed after each run. - # --seed 1234 - config.order = :random - - # Seed global randomization in this process using the `--seed` CLI option. - # Setting this allows you to use `--seed` to deterministically reproduce - # test failures related to randomization by passing the same `--seed` value - # as the one that triggered the failure. - Kernel.srand config.seed -=end + # The settings below are suggested to provide a good initial experience + # with RSpec, but feel free to customize to your heart's content. + # # This allows you to limit a spec run to individual examples or groups + # # you care about by tagging them with `:focus` metadata. When nothing + # # is tagged with `:focus`, all examples get run. RSpec also provides + # # aliases for `it`, `describe`, and `context` that include `:focus` + # # metadata: `fit`, `fdescribe` and `fcontext`, respectively. + # config.filter_run_when_matching :focus + # + # # Allows RSpec to persist some state between runs in order to support + # # the `--only-failures` and `--next-failure` CLI options. We recommend + # # you configure your source control system to ignore this file. + # config.example_status_persistence_file_path = "spec/examples.txt" + # + # # Limits the available syntax to the non-monkey patched syntax that is + # # recommended. For more details, see: + # # https://relishapp.com/rspec/rspec-core/docs/configuration/zero-monkey-patching-mode + # config.disable_monkey_patching! + # + # # Many RSpec users commonly either run the entire suite or an individual + # # file, and it's useful to allow more verbose output when running an + # # individual spec file. + # if config.files_to_run.one? + # # Use the documentation formatter for detailed output, + # # unless a formatter has already been configured + # # (e.g. via a command-line flag). + # config.default_formatter = "doc" + # end + # + # # Print the 10 slowest examples and example groups at the + # # end of the spec run, to help surface which specs are running + # # particularly slow. + # config.profile_examples = 10 + # + # # Run specs in random order to surface order dependencies. If you find an + # # order dependency and want to debug it, you can fix the order by providing + # # the seed, which is printed after each run. + # # --seed 1234 + # config.order = :random + # + # # Seed global randomization in this process using the `--seed` CLI option. + # # Setting this allows you to use `--seed` to deterministically reproduce + # # test failures related to randomization by passing the same `--seed` value + # # as the one that triggered the failure. + # Kernel.srand config.seed end diff --git a/test/channels/application_cable/connection_test.rb b/test/channels/application_cable/connection_test.rb index 800405f..4aee9b3 100644 --- a/test/channels/application_cable/connection_test.rb +++ b/test/channels/application_cable/connection_test.rb @@ -1,11 +1,15 @@ -require "test_helper" +# frozen_string_literal: true -class ApplicationCable::ConnectionTest < ActionCable::Connection::TestCase - # test "connects with cookies" do - # cookies.signed[:user_id] = 42 - # - # connect - # - # assert_equal connection.user_id, "42" - # end +require 'test_helper' + +module ApplicationCable + class ConnectionTest < ActionCable::Connection::TestCase + # test "connects with cookies" do + # cookies.signed[:user_id] = 42 + # + # connect + # + # assert_equal connection.user_id, "42" + # end + end end diff --git a/test/test_helper.rb b/test/test_helper.rb index d713e37..0c92e8e 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -1,13 +1,17 @@ -ENV["RAILS_ENV"] ||= "test" -require_relative "../config/environment" -require "rails/test_help" +# frozen_string_literal: true -class ActiveSupport::TestCase - # Run tests in parallel with specified workers - parallelize(workers: :number_of_processors) +ENV['RAILS_ENV'] ||= 'test' +require_relative '../config/environment' +require 'rails/test_help' - # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. - fixtures :all +module ActiveSupport + class TestCase + # Run tests in parallel with specified workers + parallelize(workers: :number_of_processors) - # Add more helper methods to be used by all tests here... + # 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 end From 2ed55feab327f3517cbb429a8769ece20dcf5d28 Mon Sep 17 00:00:00 2001 From: dgpangan Date: Sun, 19 Feb 2023 18:52:39 +0800 Subject: [PATCH 3/9] test: add tests for reservation model --- .rubocop.yml | 1 + app/models/reservation.rb | 11 +++++++++++ config/environments/production.rb | 2 +- spec/models/guest_spec.rb | 25 ++++--------------------- spec/models/reservation_spec.rb | 13 +++++++++++++ spec/rails_helper.rb | 2 +- 6 files changed, 31 insertions(+), 23 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 5040ae9..7b2bfb8 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,4 +1,5 @@ AllCops: + NewCops: enable Exclude: - 'bin/*' diff --git a/app/models/reservation.rb b/app/models/reservation.rb index 179111f..6e658a1 100644 --- a/app/models/reservation.rb +++ b/app/models/reservation.rb @@ -2,4 +2,15 @@ class Reservation < ApplicationRecord belongs_to :guest + + validates :code, + :date_start, + :date_end, + :number_adults, + :number_children, + :number_infants, + :amount_payout, + :amount_security, + :currency, + :status, presence: true end diff --git a/config/environments/production.rb b/config/environments/production.rb index d84418a..99eddd5 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -71,7 +71,7 @@ config.active_support.report_deprecations = false # Use default logging formatter so that PID and timestamp are not suppressed. - config.log_formatter = ::Logger::Formatter.new + config.log_formatter = Logger::Formatter.new # Use a different logger for distributed setups. # require "syslog/logger" diff --git a/spec/models/guest_spec.rb b/spec/models/guest_spec.rb index 55379d9..be2847e 100644 --- a/spec/models/guest_spec.rb +++ b/spec/models/guest_spec.rb @@ -8,26 +8,9 @@ end describe 'validations' do - it 'is valid with attributes' do - expect(Guest.new(first_name: Faker::Name.first_name, - last_name: Faker::Name.last_name, - phone: Faker::PhoneNumber.phone_number)).to be_valid - end - - it 'is not valid without a first name' do - expect(Guest.new(first_name: nil)).not_to be_valid - end - - it 'is not valid without a last name' do - end - - it 'is not valid without a phone' do - end - - it 'is not valid without an email' do - end - - it 'is invalid if email is invalid' do - end + it { should validate_presence_of(:first_name) } + it { should validate_presence_of(:last_name) } + it { should validate_presence_of(:phone) } + it { should validate_presence_of(:email) } end end diff --git a/spec/models/reservation_spec.rb b/spec/models/reservation_spec.rb index 35cd946..0358fbc 100644 --- a/spec/models/reservation_spec.rb +++ b/spec/models/reservation_spec.rb @@ -6,4 +6,17 @@ describe 'associations' do it { should belong_to(:guest) } end + + describe 'validations' do + it { should validate_presence_of(:code) } + it { should validate_presence_of(:date_start) } + it { should validate_presence_of(:date_end) } + it { should validate_presence_of(:number_adults) } + it { should validate_presence_of(:number_children) } + it { should validate_presence_of(:number_infants) } + it { should validate_presence_of(:amount_payout) } + it { should validate_presence_of(:amount_security) } + it { should validate_presence_of(:currency) } + it { should validate_presence_of(:status) } + end end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index ecebe02..a690998 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -33,7 +33,7 @@ end RSpec.configure do |config| # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures - config.fixture_path = "#{::Rails.root}/spec/fixtures" + config.fixture_path = "#{Rails.root}/spec/fixtures" # If you're not using ActiveRecord, or you'd prefer not to run each of your # examples within a transaction, remove the following line or assign false From ec59faa052ff8ef842b9ebb407f7f88ace0272a3 Mon Sep 17 00:00:00 2001 From: dgpangan Date: Sun, 19 Feb 2023 21:26:43 +0800 Subject: [PATCH 4/9] feat: create reservations controller --- Gemfile | 1 + Gemfile.lock | 2 + app/controllers/reservations_controller.rb | 48 ++++++++++++++++++++++ config/routes.rb | 5 +-- spec/requests/reservations_spec.rb | 19 +++++++++ 5 files changed, 71 insertions(+), 4 deletions(-) create mode 100644 app/controllers/reservations_controller.rb create mode 100644 spec/requests/reservations_spec.rb diff --git a/Gemfile b/Gemfile index 7f5408a..d3adf80 100644 --- a/Gemfile +++ b/Gemfile @@ -6,6 +6,7 @@ git_source(:github) { |repo| "https://github.com/#{repo}.git" } ruby '3.2.1' gem 'bootsnap', require: false +gem 'flatten' gem 'mysql2', '~> 0.5' gem 'puma', '~> 5.0' gem 'rails', '~> 7.0.4', '>= 7.0.4.2' diff --git a/Gemfile.lock b/Gemfile.lock index b91313e..10f8227 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -84,6 +84,7 @@ GEM railties (>= 5.0.0) faker (3.1.1) i18n (>= 1.8.11, < 2) + flatten (0.2.0) globalid (1.1.0) activesupport (>= 5.0) i18n (1.12.0) @@ -209,6 +210,7 @@ DEPENDENCIES debug factory_bot_rails faker + flatten mysql2 (~> 0.5) pry-rails puma (~> 5.0) diff --git a/app/controllers/reservations_controller.rb b/app/controllers/reservations_controller.rb new file mode 100644 index 0000000..293f6e5 --- /dev/null +++ b/app/controllers/reservations_controller.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +class ReservationsController < ApplicationController + def create + keys = { + reservation_code: { model: 'reservation', property: 'code' }, + start_date: { model: 'reservation', property: 'date_start' }, + end_date: { model: 'reservation', property: 'date_end' }, + adults: { model: 'reservation', property: 'number_adults' }, + children: { model: 'reservation', property: 'number_children' }, + infants: { model: 'reservation', property: 'number_infants' }, + status: { model: 'reservation', property: 'status' }, + first_name: { model: 'guest', property: 'first_name' }, + last_name: { model: 'guest', property: 'last_name' }, + phone: { model: 'guest', property: 'phone' }, + email: { model: 'guest', property: 'email' }, + currency: { model: 'reservation', property: 'currency' }, + payout_price: { model: 'reservation', property: 'amount_payout' }, + security_price: { model: 'reservation', property: 'amount_security' }, + code: { model: 'reservation', property: 'code' }, + expected_payout_amount: { model: 'reservation', property: 'amount_payout' }, + number_of_adults: { model: 'reservation', property: 'number_adults' }, + number_of_children: { model: 'reservation', property: 'number_children' }, + number_of_infants: { model: 'reservation', property: 'number_infants' }, + guest_email: { model: 'guest', property: 'email' }, + guest_first_name: { model: 'guest', property: 'first_name' }, + guest_last_name: { model: 'guest', property: 'last_name' }, + guest_phone_numbers: { model: 'guest', property: 'phone' }, + listing_security_price_accurate: { model: 'reservation', property: 'amount_security' }, + host_currency: { model: 'reservation', property: 'currency' }, + status_type: { model: 'reservation', property: 'status' } + } + + data = JSON.parse(request.raw_post).smash + guest = Hash.new + reservation = Hash.new + + data.each do |key, value| + record = keys[key.to_sym] + next if record.blank? + + reservation[record[:property]] = value if record[:model] == 'reservation' + guest[record[:property]] = value if record[:model] == 'guest' + end + + render json: { reservation:, guest: } + end +end diff --git a/config/routes.rb b/config/routes.rb index 7b329f5..8374cd0 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,8 +1,5 @@ # frozen_string_literal: true Rails.application.routes.draw do - # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html - - # Defines the root path route ("/") - # root "articles#index" + post 'reservations/create' end diff --git a/spec/requests/reservations_spec.rb b/spec/requests/reservations_spec.rb new file mode 100644 index 0000000..c565fcf --- /dev/null +++ b/spec/requests/reservations_spec.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'Reservations', type: :request do + describe 'GET /only:' do + it 'returns http success' do + get '/reservations/only:' + expect(response).to have_http_status(:success) + end + end + + describe 'GET /create' do + it 'returns http success' do + get '/reservations/create' + expect(response).to have_http_status(:success) + end + end +end From afbae2667413af083ad9d78129b56e0e0743befa Mon Sep 17 00:00:00 2001 From: dgpangan Date: Mon, 20 Feb 2023 00:51:19 +0800 Subject: [PATCH 5/9] feat: enhance reservations controller --- .rubocop.yml | 1 + app/controllers/reservations_controller.rb | 68 +++++++++---------- app/models/guest.rb | 2 +- app/models/reservation.rb | 2 + db/schema.rb | 10 ++- lib/lookup.yml | 79 ++++++++++++++++++++++ spec/requests/reservations_spec.rb | 13 +--- 7 files changed, 128 insertions(+), 47 deletions(-) create mode 100644 lib/lookup.yml diff --git a/.rubocop.yml b/.rubocop.yml index 7b2bfb8..6a91881 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -2,6 +2,7 @@ AllCops: NewCops: enable Exclude: - 'bin/*' + - 'db/*' Style/Documentation: Enabled: false diff --git a/app/controllers/reservations_controller.rb b/app/controllers/reservations_controller.rb index 293f6e5..47ba553 100644 --- a/app/controllers/reservations_controller.rb +++ b/app/controllers/reservations_controller.rb @@ -1,48 +1,46 @@ # frozen_string_literal: true class ReservationsController < ApplicationController - def create - keys = { - reservation_code: { model: 'reservation', property: 'code' }, - start_date: { model: 'reservation', property: 'date_start' }, - end_date: { model: 'reservation', property: 'date_end' }, - adults: { model: 'reservation', property: 'number_adults' }, - children: { model: 'reservation', property: 'number_children' }, - infants: { model: 'reservation', property: 'number_infants' }, - status: { model: 'reservation', property: 'status' }, - first_name: { model: 'guest', property: 'first_name' }, - last_name: { model: 'guest', property: 'last_name' }, - phone: { model: 'guest', property: 'phone' }, - email: { model: 'guest', property: 'email' }, - currency: { model: 'reservation', property: 'currency' }, - payout_price: { model: 'reservation', property: 'amount_payout' }, - security_price: { model: 'reservation', property: 'amount_security' }, - code: { model: 'reservation', property: 'code' }, - expected_payout_amount: { model: 'reservation', property: 'amount_payout' }, - number_of_adults: { model: 'reservation', property: 'number_adults' }, - number_of_children: { model: 'reservation', property: 'number_children' }, - number_of_infants: { model: 'reservation', property: 'number_infants' }, - guest_email: { model: 'guest', property: 'email' }, - guest_first_name: { model: 'guest', property: 'first_name' }, - guest_last_name: { model: 'guest', property: 'last_name' }, - guest_phone_numbers: { model: 'guest', property: 'phone' }, - listing_security_price_accurate: { model: 'reservation', property: 'amount_security' }, - host_currency: { model: 'reservation', property: 'currency' }, - status_type: { model: 'reservation', property: 'status' } - } + LOOKUP_ITEMS = YAML.load_file('lib/lookup.yml').deep_symbolize_keys + def create data = JSON.parse(request.raw_post).smash - guest = Hash.new - reservation = Hash.new + payload = parse_data(data) + guest = create_guest(payload[:guest_data]) + reservation = create_reservation(guest, payload[:reservation_data]) + render json: { guest:, reservation: } + end + + private + + def parse_data(data) + guest_data = {} + reservation_data = {} data.each do |key, value| - record = keys[key.to_sym] + record = LOOKUP_ITEMS[key.to_sym] next if record.blank? - reservation[record[:property]] = value if record[:model] == 'reservation' - guest[record[:property]] = value if record[:model] == 'guest' + reservation_data[record[:property].to_sym] = value if record[:model] == 'reservation' + guest_data[record[:property].to_sym] = value if record[:model] == 'guest' end - render json: { reservation:, guest: } + { reservation_data:, guest_data: } + end + + def create_guest(guest_data) + Guest.find_or_create_by(email: guest_data[:email]) do |g| + guest_data.each do |key, value| + g[key] = value + end + end + end + + def create_reservation(guest, reservation_data) + guest.reservations.find_or_create_by(code: reservation_data[:code]) do |r| + reservation_data.each do |key, value| + r[key] = value + end + end end end diff --git a/app/models/guest.rb b/app/models/guest.rb index c4719ec..2660ffc 100644 --- a/app/models/guest.rb +++ b/app/models/guest.rb @@ -8,5 +8,5 @@ class Guest < ApplicationRecord :phone, :email, presence: true - validates :email, format: { with: URI::MailTo::EMAIL_REGEXP } + validates :email, format: { with: URI::MailTo::EMAIL_REGEXP }, uniqueness: true end diff --git a/app/models/reservation.rb b/app/models/reservation.rb index 6e658a1..509abca 100644 --- a/app/models/reservation.rb +++ b/app/models/reservation.rb @@ -13,4 +13,6 @@ class Reservation < ApplicationRecord :amount_security, :currency, :status, presence: true + + validates :code, uniqueness: true end diff --git a/db/schema.rb b/db/schema.rb index 4b48bf4..53a3ef7 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -12,7 +12,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 20_230_218_161_038) do +ActiveRecord::Schema[7.0].define(version: 20_230_219_133_134) do create_table 'guests', charset: 'utf8mb4', collation: 'utf8mb4_0900_ai_ci', force: :cascade do |t| t.string 'first_name' t.string 'last_name' @@ -22,6 +22,14 @@ t.datetime 'updated_at', null: false end + create_table 'lookups', charset: 'utf8mb4', collation: 'utf8mb4_0900_ai_ci', force: :cascade do |t| + t.string 'key' + t.string 'model' + t.string 'property' + t.datetime 'created_at', null: false + t.datetime 'updated_at', null: false + end + create_table 'reservations', charset: 'utf8mb4', collation: 'utf8mb4_0900_ai_ci', force: :cascade do |t| t.string 'code' t.date 'date_start' diff --git a/lib/lookup.yml b/lib/lookup.yml new file mode 100644 index 0000000..3634aa5 --- /dev/null +++ b/lib/lookup.yml @@ -0,0 +1,79 @@ +--- +reservation_code: + model: reservation + property: code +start_date: + model: reservation + property: date_start +end_date: + model: reservation + property: date_end +adults: + model: reservation + property: number_adults +children: + model: reservation + property: number_children +infants: + model: reservation + property: number_infants +status: + model: reservation + property: status +first_name: + model: guest + property: first_name +last_name: + model: guest + property: last_name +phone: + model: guest + property: phone +email: + model: guest + property: email +currency: + model: reservation + property: currency +payout_price: + model: reservation + property: amount_payout +security_price: + model: reservation + property: amount_security +code: + model: reservation + property: code +expected_payout_amount: + model: reservation + property: amount_payout +number_of_adults: + model: reservation + property: number_adults +number_of_children: + model: reservation + property: number_children +number_of_infants: + model: reservation + property: number_infants +guest_email: + model: guest + property: email +guest_first_name: + model: guest + property: first_name +guest_last_name: + model: guest + property: last_name +guest_phone_numbers: + model: guest + property: phone +listing_security_price_accurate: + model: reservation + property: amount_security +host_currency: + model: reservation + property: currency +status_type: + model: reservation + property: status diff --git a/spec/requests/reservations_spec.rb b/spec/requests/reservations_spec.rb index c565fcf..3361936 100644 --- a/spec/requests/reservations_spec.rb +++ b/spec/requests/reservations_spec.rb @@ -3,17 +3,10 @@ require 'rails_helper' RSpec.describe 'Reservations', type: :request do - describe 'GET /only:' do + describe 'POST /create' do it 'returns http success' do - get '/reservations/only:' - expect(response).to have_http_status(:success) - end - end - - describe 'GET /create' do - it 'returns http success' do - get '/reservations/create' - expect(response).to have_http_status(:success) + # post '/reservations/create' + # expect(response).to have_http_status(:success) end end end From c67711f95d0810c5393602d0a3494702da8a5212 Mon Sep 17 00:00:00 2001 From: dgpangan Date: Mon, 20 Feb 2023 07:04:30 +0800 Subject: [PATCH 6/9] feat: finalize reservations controller --- .rubocop.yml | 3 + app/controllers/reservations_controller.rb | 37 ++++------- .../20230219221143_add_index_to_guests.rb | 7 ++ ...0230219221354_add_index_to_reservations.rb | 7 ++ db/schema.rb | 64 +++++++++---------- 5 files changed, 61 insertions(+), 57 deletions(-) create mode 100644 db/migrate/20230219221143_add_index_to_guests.rb create mode 100644 db/migrate/20230219221354_add_index_to_reservations.rb diff --git a/.rubocop.yml b/.rubocop.yml index 6a91881..74e22a8 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -8,4 +8,7 @@ Style/Documentation: Enabled: false Metrics/MethodLength: + Max: 20 + +Metrics/AbcSize: Max: 20 \ No newline at end of file diff --git a/app/controllers/reservations_controller.rb b/app/controllers/reservations_controller.rb index 47ba553..3e30f8c 100644 --- a/app/controllers/reservations_controller.rb +++ b/app/controllers/reservations_controller.rb @@ -4,43 +4,30 @@ class ReservationsController < ApplicationController LOOKUP_ITEMS = YAML.load_file('lib/lookup.yml').deep_symbolize_keys def create - data = JSON.parse(request.raw_post).smash - payload = parse_data(data) - guest = create_guest(payload[:guest_data]) - reservation = create_reservation(guest, payload[:reservation_data]) + data = parse_data(JSON.parse(request.raw_post).smash) + Guest.upsert(data[:guest]) + guest = Guest.find_by(email: data[:guest][:email]) + data[:reservation][:guest_id] = guest.id + Reservation.upsert(data[:reservation]) + reservation = Reservation.find_by(code: data[:reservation][:code]) render json: { guest:, reservation: } end private def parse_data(data) - guest_data = {} - reservation_data = {} + guest = {} + reservation = {} data.each do |key, value| record = LOOKUP_ITEMS[key.to_sym] next if record.blank? - reservation_data[record[:property].to_sym] = value if record[:model] == 'reservation' - guest_data[record[:property].to_sym] = value if record[:model] == 'guest' + reservation[record[:property].to_sym] = value if record[:model] == 'reservation' + guest[record[:property].to_sym] = value if record[:model] == 'guest' end - { reservation_data:, guest_data: } - end - - def create_guest(guest_data) - Guest.find_or_create_by(email: guest_data[:email]) do |g| - guest_data.each do |key, value| - g[key] = value - end - end - end - - def create_reservation(guest, reservation_data) - guest.reservations.find_or_create_by(code: reservation_data[:code]) do |r| - reservation_data.each do |key, value| - r[key] = value - end - end + reservation[:guest_id] = guest[:id] + { reservation:, guest: } end end diff --git a/db/migrate/20230219221143_add_index_to_guests.rb b/db/migrate/20230219221143_add_index_to_guests.rb new file mode 100644 index 0000000..987d437 --- /dev/null +++ b/db/migrate/20230219221143_add_index_to_guests.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddIndexToGuests < ActiveRecord::Migration[7.0] + def change + add_index :guests, :email, unique: true + end +end diff --git a/db/migrate/20230219221354_add_index_to_reservations.rb b/db/migrate/20230219221354_add_index_to_reservations.rb new file mode 100644 index 0000000..3da53ff --- /dev/null +++ b/db/migrate/20230219221354_add_index_to_reservations.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddIndexToReservations < ActiveRecord::Migration[7.0] + def change + add_index :reservations, :code, unique: true + end +end diff --git a/db/schema.rb b/db/schema.rb index 53a3ef7..c63beb1 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -1,5 +1,3 @@ -# frozen_string_literal: true - # 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. @@ -12,40 +10,42 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 20_230_219_133_134) do - create_table 'guests', charset: 'utf8mb4', collation: 'utf8mb4_0900_ai_ci', force: :cascade do |t| - t.string 'first_name' - t.string 'last_name' - t.string 'phone' - t.string 'email' - t.datetime 'created_at', null: false - t.datetime 'updated_at', null: false +ActiveRecord::Schema[7.0].define(version: 2023_02_19_221354) do + create_table "guests", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.string "first_name" + t.string "last_name" + t.string "phone" + t.string "email" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["email"], name: "index_guests_on_email", unique: true end - create_table 'lookups', charset: 'utf8mb4', collation: 'utf8mb4_0900_ai_ci', force: :cascade do |t| - t.string 'key' - t.string 'model' - t.string 'property' - t.datetime 'created_at', null: false - t.datetime 'updated_at', null: false + create_table "lookups", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.string "key" + t.string "model" + t.string "property" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false end - create_table 'reservations', charset: 'utf8mb4', collation: 'utf8mb4_0900_ai_ci', force: :cascade do |t| - t.string 'code' - t.date 'date_start' - t.date 'date_end' - t.integer 'number_adults' - t.integer 'number_children' - t.integer 'number_infants' - t.float 'amount_payout' - t.float 'amount_security' - t.string 'currency' - t.string 'status' - t.datetime 'created_at', null: false - t.datetime 'updated_at', null: false - t.bigint 'guest_id', null: false - t.index ['guest_id'], name: 'index_reservations_on_guest_id' + create_table "reservations", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.string "code" + t.date "date_start" + t.date "date_end" + t.integer "number_adults" + t.integer "number_children" + t.integer "number_infants" + t.float "amount_payout" + t.float "amount_security" + t.string "currency" + t.string "status" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.bigint "guest_id", null: false + t.index ["code"], name: "index_reservations_on_code", unique: true + t.index ["guest_id"], name: "index_reservations_on_guest_id" end - add_foreign_key 'reservations', 'guests' + add_foreign_key "reservations", "guests" end From 38e75de5cbc59949dfd6de0b6f10c82e90d7af2d Mon Sep 17 00:00:00 2001 From: dgpangan Date: Mon, 20 Feb 2023 07:21:41 +0800 Subject: [PATCH 7/9] ref: reservations controller --- app/controllers/reservations_controller.rb | 33 ++++++++++++++++++---- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/app/controllers/reservations_controller.rb b/app/controllers/reservations_controller.rb index 3e30f8c..46ccdb3 100644 --- a/app/controllers/reservations_controller.rb +++ b/app/controllers/reservations_controller.rb @@ -5,12 +5,22 @@ class ReservationsController < ApplicationController def create data = parse_data(JSON.parse(request.raw_post).smash) - Guest.upsert(data[:guest]) - guest = Guest.find_by(email: data[:guest][:email]) - data[:reservation][:guest_id] = guest.id - Reservation.upsert(data[:reservation]) - reservation = Reservation.find_by(code: data[:reservation][:code]) - render json: { guest:, reservation: } + + guest = create_or_update_guest(data[:guest]) + + unless guest.valid? + render json: { success: false, errors: guest.errors } + return + end + + reservation = create_or_update_reservation(data[:reservation], guest.id) + + unless reservation.valid? + render json: { success: false, errors: reservation.errors } + return + end + + render json: { success: true, guest:, reservation: } end private @@ -30,4 +40,15 @@ def parse_data(data) reservation[:guest_id] = guest[:id] { reservation:, guest: } end + + def create_or_update_guest(data) + Guest.upsert(data) + Guest.find_by(email: data[:email]) + end + + def create_or_update_reservation(data, guest_id) + data[:guest_id] = guest_id + Reservation.upsert(data) + Reservation.find_by(code: data[:code]) + end end From 57981f04a54ec7699a7364e35677546d5f264bd7 Mon Sep 17 00:00:00 2001 From: dgpangan Date: Mon, 20 Feb 2023 08:54:37 +0800 Subject: [PATCH 8/9] test(reservations_controller): test cases --- app/controllers/reservations_controller.rb | 2 +- .../reservations_controller_spec.rb | 127 ++++++++++++++++++ spec/fixtures/files/payload-1.json | 21 +++ spec/fixtures/files/payload-2.json | 27 ++++ spec/rails_helper.rb | 3 + spec/support/helpers.rb | 10 ++ 6 files changed, 189 insertions(+), 1 deletion(-) create mode 100644 spec/controllers/reservations_controller_spec.rb create mode 100644 spec/fixtures/files/payload-1.json create mode 100644 spec/fixtures/files/payload-2.json create mode 100644 spec/support/helpers.rb diff --git a/app/controllers/reservations_controller.rb b/app/controllers/reservations_controller.rb index 46ccdb3..f30192b 100644 --- a/app/controllers/reservations_controller.rb +++ b/app/controllers/reservations_controller.rb @@ -4,7 +4,7 @@ class ReservationsController < ApplicationController LOOKUP_ITEMS = YAML.load_file('lib/lookup.yml').deep_symbolize_keys def create - data = parse_data(JSON.parse(request.raw_post).smash) + data = parse_data(JSON.parse(request.body.read).smash) guest = create_or_update_guest(data[:guest]) diff --git a/spec/controllers/reservations_controller_spec.rb b/spec/controllers/reservations_controller_spec.rb new file mode 100644 index 0000000..74b288a --- /dev/null +++ b/spec/controllers/reservations_controller_spec.rb @@ -0,0 +1,127 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe ReservationsController, type: :controller do + describe 'POST #create' do + context 'with valid params' do + context 'using payload 1' do + let(:guest_attributes) do + { + first_name: "Wayne", + last_name: "Woodbridge", + phone: "639123456789", + email: "wayne_woodbridge@bnb.com" + } + end + + let(:reservation_attributes) do + { + code: "YYY12345678", + date_start: "2021-04-14".to_date, + date_end: "2021-04-18".to_date, + number_adults: 2, + number_children: 2, + number_infants: 0, + amount_payout: 4200.0, + amount_security: 500.0, + currency: "AUD", + status: "accepted" + } + end + + before do + post :create, body: file_fixture("payload-1.json").read + end + + subject { response } + + it { should have_http_status :ok } + + it 'creates the guest', skip_before: true do + guest = Guest.last + expect(guest.first_name).to eq(guest_attributes[:first_name]) + expect(guest.last_name).to eq(guest_attributes[:last_name]) + expect(guest.phone).to eq(guest_attributes[:phone]) + expect(guest.email).to eq(guest_attributes[:email]) + end + + it 'creates the reservation' do + reservation = Reservation.last + expect(reservation.code).to eq(reservation_attributes[:code]) + expect(reservation.date_start).to eq(reservation_attributes[:date_start]) + expect(reservation.date_end).to eq(reservation_attributes[:date_end]) + expect(reservation.number_adults).to eq(reservation_attributes[:number_adults]) + expect(reservation.number_children).to eq(reservation_attributes[:number_children]) + expect(reservation.number_infants).to eq(reservation_attributes[:number_infants]) + expect(reservation.amount_payout).to eq(reservation_attributes[:amount_payout]) + expect(reservation.currency).to eq(reservation_attributes[:currency]) + expect(reservation.status).to eq(reservation_attributes[:status]) + end + end + + context 'using payload 2' do + let(:guest_attributes) do + { + first_name: "Wayne", + last_name: "Woodbridge", + phone: "---\n- '639123456789'\n- '639123456789'\n", + email: "wayne_woodbridge@bnb.com" + } + end + + let(:reservation_attributes) do + { + code: "XXX12345678", + date_start: "2021-03-12".to_date, + date_end: "2021-03-16".to_date, + number_adults: 2, + number_children: 2, + number_infants: 0, + amount_payout: 3800.0, + amount_security: 500.0, + currency: "AUD", + status: "accepted" + } + end + + before do + post :create, body: file_fixture("payload-2.json").read + end + + subject { response } + + it { should have_http_status :ok } + + it 'creates the guest' do + guest = Guest.last + expect(guest.first_name).to eq(guest_attributes[:first_name]) + expect(guest.last_name).to eq(guest_attributes[:last_name]) + expect(guest.phone).to eq(guest_attributes[:phone]) + expect(guest.email).to eq(guest_attributes[:email]) + end + + it 'creates the reservation' do + reservation = Reservation.last + expect(reservation.code).to eq(reservation_attributes[:code]) + expect(reservation.date_start).to eq(reservation_attributes[:date_start]) + expect(reservation.date_end).to eq(reservation_attributes[:date_end]) + expect(reservation.number_adults).to eq(reservation_attributes[:number_adults]) + expect(reservation.number_children).to eq(reservation_attributes[:number_children]) + expect(reservation.number_infants).to eq(reservation_attributes[:number_infants]) + expect(reservation.amount_payout).to eq(reservation_attributes[:amount_payout]) + expect(reservation.currency).to eq(reservation_attributes[:currency]) + expect(reservation.status).to eq(reservation_attributes[:status]) + end + end + end + + context 'with invalid guest params' do + + end + + context 'with invalid reservation params' do + + end + end +end \ No newline at end of file diff --git a/spec/fixtures/files/payload-1.json b/spec/fixtures/files/payload-1.json new file mode 100644 index 0000000..ccb9deb --- /dev/null +++ b/spec/fixtures/files/payload-1.json @@ -0,0 +1,21 @@ +{ + "reservation_code": "YYY12345678", + "start_date": "2021-04-14", + "end_date": "2021-04-18", + "nights": 4, + "guests": 4, + "adults": 2, + "children": 2, + "infants": 0, + "status": "accepted", + "guest": { + "first_name": "Wayne", + "last_name": "Woodbridge", + "phone": "639123456789", + "email": "wayne_woodbridge@bnb.com" + }, + "currency": "AUD", + "payout_price": "4200.00", + "security_price": "500", + "total_price": "4700.00" +} \ No newline at end of file diff --git a/spec/fixtures/files/payload-2.json b/spec/fixtures/files/payload-2.json new file mode 100644 index 0000000..7843704 --- /dev/null +++ b/spec/fixtures/files/payload-2.json @@ -0,0 +1,27 @@ +{ + "reservation": { + "code": "XXX12345678", + "start_date": "2021-03-12", + "end_date": "2021-03-16", + "expected_payout_amount": "3800.00", + "guest_details": { + "localized_description": "4 guests", + "number_of_adults": 2, + "number_of_children": 2, + "number_of_infants": 0 + }, + "guest_email": "wayne_woodbridge@bnb.com", + "guest_first_name": "Wayne", + "guest_last_name": "Woodbridge", + "guest_phone_numbers": [ + "639123456789", + "639123456789" + ], + "listing_security_price_accurate": "500.00", + "host_currency": "AUD", + "nights": 4, + "number_of_guests": 4, + "status_type": "accepted", + "total_paid_amount_accurate": "4300.00" + } +} \ No newline at end of file diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index a690998..86ad6f4 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -31,6 +31,9 @@ rescue ActiveRecord::PendingMigrationError => e abort e.to_s.strip end + +Dir[Rails.root.join('spec/support/*.rb')].each { |path| require path } + RSpec.configure do |config| # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures config.fixture_path = "#{Rails.root}/spec/fixtures" diff --git a/spec/support/helpers.rb b/spec/support/helpers.rb new file mode 100644 index 0000000..391ca00 --- /dev/null +++ b/spec/support/helpers.rb @@ -0,0 +1,10 @@ +module Helpers + def json_data(filename:) + file_content = file_fixture("#{filename}.json").read + JSON.parse(file_content, symbolize_names: true) + end +end + +RSpec.configure do |config| + config.include Helpers +end \ No newline at end of file From e850f9465a4e8e407cbb5960ebd8d3d36cd53e98 Mon Sep 17 00:00:00 2001 From: dgpangan Date: Mon, 20 Feb 2023 10:23:33 +0800 Subject: [PATCH 9/9] ref: remove unnecessary files and dependencies --- .rubocop.yml | 9 +- Gemfile | 2 - Gemfile.lock | 9 -- .../reservations_controller_spec.rb | 108 +++++++++--------- spec/factories/guest.rb | 18 --- spec/fixtures/files/invalid-guest-data.json | 21 ++++ .../files/invalid-reservation-data.json | 27 +++++ spec/rails_helper.rb | 4 - spec/support/helpers.rb | 10 -- 9 files changed, 107 insertions(+), 101 deletions(-) delete mode 100644 spec/factories/guest.rb create mode 100644 spec/fixtures/files/invalid-guest-data.json create mode 100644 spec/fixtures/files/invalid-reservation-data.json delete mode 100644 spec/support/helpers.rb diff --git a/.rubocop.yml b/.rubocop.yml index 74e22a8..7bf3d5c 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,8 +1,11 @@ AllCops: - NewCops: enable Exclude: - - 'bin/*' - - 'db/*' + - bin/**/* + - db/**/* + - spec/**/**/**/* + - vendor/**/* + NewCops: enable + SuggestExtensions: false Style/Documentation: Enabled: false diff --git a/Gemfile b/Gemfile index d3adf80..29edf32 100644 --- a/Gemfile +++ b/Gemfile @@ -14,8 +14,6 @@ gem 'tzinfo-data', platforms: %i[mingw mswin x64_mingw jruby] group :development, :test do gem 'debug', platforms: %i[mri mingw x64_mingw] - gem 'factory_bot_rails' - gem 'faker' gem 'pry-rails' gem 'rspec-rails' gem 'shoulda-matchers' diff --git a/Gemfile.lock b/Gemfile.lock index 10f8227..2a45022 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -77,13 +77,6 @@ GEM debug (1.7.1) diff-lcs (1.5.0) erubi (1.12.0) - factory_bot (6.2.1) - activesupport (>= 5.0.0) - factory_bot_rails (6.2.0) - factory_bot (~> 6.2.0) - railties (>= 5.0.0) - faker (3.1.1) - i18n (>= 1.8.11, < 2) flatten (0.2.0) globalid (1.1.0) activesupport (>= 5.0) @@ -208,8 +201,6 @@ PLATFORMS DEPENDENCIES bootsnap debug - factory_bot_rails - faker flatten mysql2 (~> 0.5) pry-rails diff --git a/spec/controllers/reservations_controller_spec.rb b/spec/controllers/reservations_controller_spec.rb index 74b288a..a7e8c40 100644 --- a/spec/controllers/reservations_controller_spec.rb +++ b/spec/controllers/reservations_controller_spec.rb @@ -8,120 +8,118 @@ context 'using payload 1' do let(:guest_attributes) do { - first_name: "Wayne", - last_name: "Woodbridge", - phone: "639123456789", - email: "wayne_woodbridge@bnb.com" + first_name: 'Wayne', + last_name: 'Woodbridge', + phone: '639123456789', + email: 'wayne_woodbridge@bnb.com' } end - + let(:reservation_attributes) do { - code: "YYY12345678", - date_start: "2021-04-14".to_date, - date_end: "2021-04-18".to_date, + code: 'YYY12345678', + date_start: '2021-04-14'.to_date, + date_end: '2021-04-18'.to_date, number_adults: 2, number_children: 2, number_infants: 0, amount_payout: 4200.0, amount_security: 500.0, - currency: "AUD", - status: "accepted" + currency: 'AUD', + status: 'accepted' } end before do - post :create, body: file_fixture("payload-1.json").read + post :create, body: file_fixture('payload-1.json').read end - subject { response } - - it { should have_http_status :ok } + specify { expect(response).to have_http_status(:ok) } + specify { expect(JSON.parse(response.body)['success']).to be_truthy } - it 'creates the guest', skip_before: true do + it 'creates the guest' do guest = Guest.last - expect(guest.first_name).to eq(guest_attributes[:first_name]) - expect(guest.last_name).to eq(guest_attributes[:last_name]) - expect(guest.phone).to eq(guest_attributes[:phone]) - expect(guest.email).to eq(guest_attributes[:email]) + + guest_attributes.each do |key, value| + expect(guest[key]).to eq(value) + end end it 'creates the reservation' do reservation = Reservation.last - expect(reservation.code).to eq(reservation_attributes[:code]) - expect(reservation.date_start).to eq(reservation_attributes[:date_start]) - expect(reservation.date_end).to eq(reservation_attributes[:date_end]) - expect(reservation.number_adults).to eq(reservation_attributes[:number_adults]) - expect(reservation.number_children).to eq(reservation_attributes[:number_children]) - expect(reservation.number_infants).to eq(reservation_attributes[:number_infants]) - expect(reservation.amount_payout).to eq(reservation_attributes[:amount_payout]) - expect(reservation.currency).to eq(reservation_attributes[:currency]) - expect(reservation.status).to eq(reservation_attributes[:status]) + + reservation_attributes.each do |key, value| + expect(reservation[key]).to eq(value) + end end end context 'using payload 2' do let(:guest_attributes) do { - first_name: "Wayne", - last_name: "Woodbridge", + first_name: 'Wayne', + last_name: 'Woodbridge', phone: "---\n- '639123456789'\n- '639123456789'\n", - email: "wayne_woodbridge@bnb.com" + email: 'wayne_woodbridge@bnb.com' } end - + let(:reservation_attributes) do { - code: "XXX12345678", - date_start: "2021-03-12".to_date, - date_end: "2021-03-16".to_date, + code: 'XXX12345678', + date_start: '2021-03-12'.to_date, + date_end: '2021-03-16'.to_date, number_adults: 2, number_children: 2, number_infants: 0, amount_payout: 3800.0, amount_security: 500.0, - currency: "AUD", - status: "accepted" + currency: 'AUD', + status: 'accepted' } end before do - post :create, body: file_fixture("payload-2.json").read + post :create, body: file_fixture('payload-2.json').read end - subject { response } - - it { should have_http_status :ok } + specify { expect(response).to have_http_status(:ok) } + specify { expect(JSON.parse(response.body)['success']).to be_truthy } it 'creates the guest' do guest = Guest.last - expect(guest.first_name).to eq(guest_attributes[:first_name]) - expect(guest.last_name).to eq(guest_attributes[:last_name]) - expect(guest.phone).to eq(guest_attributes[:phone]) - expect(guest.email).to eq(guest_attributes[:email]) + + guest_attributes.each do |key, value| + expect(guest[key]).to eq(value) + end end it 'creates the reservation' do reservation = Reservation.last - expect(reservation.code).to eq(reservation_attributes[:code]) - expect(reservation.date_start).to eq(reservation_attributes[:date_start]) - expect(reservation.date_end).to eq(reservation_attributes[:date_end]) - expect(reservation.number_adults).to eq(reservation_attributes[:number_adults]) - expect(reservation.number_children).to eq(reservation_attributes[:number_children]) - expect(reservation.number_infants).to eq(reservation_attributes[:number_infants]) - expect(reservation.amount_payout).to eq(reservation_attributes[:amount_payout]) - expect(reservation.currency).to eq(reservation_attributes[:currency]) - expect(reservation.status).to eq(reservation_attributes[:status]) + + reservation_attributes.each do |key, value| + expect(reservation[key]).to eq(value) + end end end end context 'with invalid guest params' do + before do + post :create, body: file_fixture('invalid-guest-data.json').read + end + specify { expect(response).to have_http_status(:ok) } + specify { expect(JSON.parse(response.body)['success']).to be_falsey } end context 'with invalid reservation params' do + before do + post :create, body: file_fixture('invalid-reservation-data.json').read + end + specify { expect(response).to have_http_status(:ok) } + specify { expect(JSON.parse(response.body)['success']).to be_falsey } end end -end \ No newline at end of file +end diff --git a/spec/factories/guest.rb b/spec/factories/guest.rb deleted file mode 100644 index 451bc50..0000000 --- a/spec/factories/guest.rb +++ /dev/null @@ -1,18 +0,0 @@ -# frozen_string_literal: true - -FactoryBot.define do - factory :guest, class: 'Guest' do - first_name { Faker::Name.first_name } - last_name { Faker::Name.first_name } - phone { Faker::PhoneNumber.phone_number } - email { "#{first_name}_#{last_name}@somedomain.com" } - end - - trait :incomplete do - complete { false } - first_name { Faker::Name.first_name } - last_name { Faker::Name.first_name } - phone { Faker::PhoneNumber.phone_number } - email { "#{first_name}_#{last_name}@somedomain.com" } - end -end diff --git a/spec/fixtures/files/invalid-guest-data.json b/spec/fixtures/files/invalid-guest-data.json new file mode 100644 index 0000000..e661928 --- /dev/null +++ b/spec/fixtures/files/invalid-guest-data.json @@ -0,0 +1,21 @@ +{ + "reservation_code": "YYY12345678", + "start_date": "2021-04-14", + "end_date": "2021-04-18", + "nights": 4, + "guests": 4, + "adults": 2, + "children": 2, + "infants": 0, + "status": "accepted", + "guest": { + "first_name": "", + "last_name": "Woodbridge", + "phone": "639123456789", + "email": "wayne_woodbridge@bnb.com" + }, + "currency": "AUD", + "payout_price": "4200.00", + "security_price": "500", + "total_price": "4700.00" +} \ No newline at end of file diff --git a/spec/fixtures/files/invalid-reservation-data.json b/spec/fixtures/files/invalid-reservation-data.json new file mode 100644 index 0000000..8f21c35 --- /dev/null +++ b/spec/fixtures/files/invalid-reservation-data.json @@ -0,0 +1,27 @@ +{ + "reservation": { + "code": "XXX12345678", + "start_date": "2021-03-12", + "end_date": "2021-03-16", + "expected_payout_amount": "", + "guest_details": { + "localized_description": "4 guests", + "number_of_adults": 2, + "number_of_children": 2, + "number_of_infants": 0 + }, + "guest_email": "wayne_woodbridge@bnb.com", + "guest_first_name": "Wayne", + "guest_last_name": "Woodbridge", + "guest_phone_numbers": [ + "639123456789", + "639123456789" + ], + "listing_security_price_accurate": "500.00", + "host_currency": "AUD", + "nights": 4, + "number_of_guests": 4, + "status_type": "accepted", + "total_paid_amount_accurate": "4300.00" + } +} \ No newline at end of file diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 86ad6f4..2370b48 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -73,7 +73,3 @@ with.library :rails end end - -RSpec.configure do |config| - config.include FactoryBot::Syntax::Methods -end diff --git a/spec/support/helpers.rb b/spec/support/helpers.rb deleted file mode 100644 index 391ca00..0000000 --- a/spec/support/helpers.rb +++ /dev/null @@ -1,10 +0,0 @@ -module Helpers - def json_data(filename:) - file_content = file_fixture("#{filename}.json").read - JSON.parse(file_content, symbolize_names: true) - end -end - -RSpec.configure do |config| - config.include Helpers -end \ No newline at end of file