diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..f3b6411 --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +**/.git diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..c05cf05 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,25 @@ +FROM ruby:3.2.0-slim AS base + +RUN apt-get update && \ + apt-get install -y \ + build-essential \ + pkg-config && \ + rm -rf /var/lib/apt/lists/* + +RUN groupadd --gid 999 cruises && \ + useradd --system --create-home --uid 999 --gid cruises cruises + +WORKDIR /app +RUN chown cruises:cruises . + +COPY Gemfile* ./ + +RUN bundle install + +FROM base + +USER cruises + +COPY --chown=cruises:cruises . . + +CMD rake console diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..548f73b --- /dev/null +++ b/Gemfile @@ -0,0 +1,4 @@ +source 'https://rubygems.org' + +gem 'activerecord', require: "active_record" +gem 'sqlite3' diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..449dd15 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,32 @@ +GEM + remote: https://rubygems.org/ + specs: + activemodel (7.0.4) + activesupport (= 7.0.4) + activerecord (7.0.4) + activemodel (= 7.0.4) + activesupport (= 7.0.4) + activesupport (7.0.4) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (>= 1.6, < 2) + minitest (>= 5.1) + tzinfo (~> 2.0) + concurrent-ruby (1.1.10) + i18n (1.12.0) + concurrent-ruby (~> 1.0) + mini_portile2 (2.8.1) + minitest (5.17.0) + sqlite3 (1.5.4) + mini_portile2 (~> 2.8.0) + tzinfo (2.0.5) + concurrent-ruby (~> 1.0) + +PLATFORMS + x86_64-linux + +DEPENDENCIES + activerecord + sqlite3 + +BUNDLED WITH + 2.4.1 diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..9e048fe --- /dev/null +++ b/Rakefile @@ -0,0 +1,18 @@ +require "bundler/setup" + +load "active_record/railties/databases.rake" + +task :environment do + require "./config/application" +end + +task :console => :environment do + require "irb" + + ARGV.clear + IRB.start +end + +task :import => :environment do + CruiseImporter.new.import +end diff --git a/config/application.rb b/config/application.rb new file mode 100644 index 0000000..2597ab8 --- /dev/null +++ b/config/application.rb @@ -0,0 +1,18 @@ +RAKE_PATH = File.expand_path(".") +RAKE_ENV = ENV.fetch("RAKE_ENV", "development") + +Bundler.require :default, RAKE_ENV + +# Configuration for ActiveRecord rake tasks +ActiveRecord::Tasks::DatabaseTasks.root = RAKE_PATH +ActiveRecord::Tasks::DatabaseTasks.env = RAKE_ENV +ActiveRecord::Tasks::DatabaseTasks.db_dir = "db" +ActiveRecord::Tasks::DatabaseTasks.migrations_paths = ["db/migrate"] +ActiveRecord::Tasks::DatabaseTasks.seed_loader = nil + +# Set up ActiveRecord database connection +ActiveRecord::Base.configurations = YAML.load_file("config/database.yml") +ActiveRecord::Base.establish_connection RAKE_ENV.to_sym + +# Load all of our libs and models +Dir[File.join(__dir__, '..', '{lib,models}', '*.rb')].each { |file| require file } diff --git a/config/database.yml b/config/database.yml new file mode 100644 index 0000000..6359bf2 --- /dev/null +++ b/config/database.yml @@ -0,0 +1,3 @@ +development: + adapter: sqlite3 + database: ./db/cruises.db diff --git a/db/cruises.db b/db/cruises.db new file mode 100644 index 0000000..50c3ebb Binary files /dev/null and b/db/cruises.db differ diff --git a/db/cruises.json b/db/cruises.json new file mode 100644 index 0000000..1d422ac --- /dev/null +++ b/db/cruises.json @@ -0,0 +1,285 @@ +[ + { + "name": "7 Night Western Caribbean & Perfect Day", + "cruiseline": "Royal Caribbean", + "ship": "Harmony of the Seas", + "start_date": "2022-07-17", + "end_date": "2022-07-24", + "cabin": "12643", + "itinerary": [ + { + "type": "port", + "date": "2022-07-17", + "name": "Port Canaveral, Florida" + }, + { + "type": "port", + "date": "2022-07-18", + "name": "Perfect Day at CocoCay, Bahamas" + }, + { + "type": "port", + "date": "2022-07-19", + "name": "Nassau, Bahamas" + }, + { + "type": "cruising", + "date": "2022-07-20" + }, + { + "type": "port", + "date": "2022-07-21", + "name": "Falmouth, Jamaica" + }, + { + "type": "port", + "date": "2022-07-22", + "name": "Labadee, Haiti" + }, + { + "type": "cruising", + "date": "2022-07-23" + }, + { + "type": "port", + "date": "2022-07-24", + "name": "Port Canaveral, Florida" + } + ] + }, + { + "name": "Caribbean - Western", + "cruiseline": "Carnival", + "ship": "Carnival Breeze", + "start_date": "2021-12-27", + "end_date": "2022-01-03", + "cabin": "9346", + "itinerary": [ + { + "type": "port", + "date": "2021-12-27", + "name": "Galveston, Texas" + }, + { + "type": "cruising", + "date": "2021-12-28" + }, + { + "type": "port", + "date": "2021-12-29", + "name": "Cozumel, Mexico" + }, + { + "type": "port", + "date": "2021-12-30", + "name": "Belize City, Belize" + }, + { + "type": "port", + "date": "2021-12-31", + "name": "Roatan, Bay Islands, Honduras" + }, + { + "type": "cruising", + "date": "2022-01-01" + }, + { + "type": "cruising", + "date": "2022-01-02" + }, + { + "type": "port", + "date": "2022-01-03", + "name": "Galveston, Texas" + } + ] + }, + { + "name": "7 Night Eastern Caribbean Cruise", + "cruiseline": "Royal Caribbean", + "ship": "Symphony of the Seas", + "start_date": "2019-06-29", + "end_date": "2019-07-06", + "cabin": "8262", + "itinerary": [ + { + "type": "port", + "date": "2019-06-29", + "name": "Miami, Florida" + }, + { + "type": "cruising", + "date": "2019-06-30" + }, + { + "type": "cruising", + "date": "2019-07-01" + }, + { + "type": "port", + "date": "2019-07-02", + "name": "Saint Maarten (Phillipsburg), Netherlands Antilles" + }, + { + "type": "port", + "date": "2019-07-03", + "name": "San Juan, Puerto Rico" + }, + { + "type": "port", + "date": "2019-07-04", + "name": "Labadee, Haiti" + }, + { + "type": "cruising", + "date": "2019-07-05" + }, + { + "type": "port", + "date": "2019-07-06", + "name": "Miami, Florida" + } + ] + }, + { + "name": "7 Night Eastern Caribbean Cruise", + "cruiseline": "Royal Caribbean", + "ship": "Harmony of the Seas", + "start_date": "2018-07-07", + "end_date": "2018-07-14", + "cabin": "10560", + "itinerary": [ + { + "type": "port", + "date": "2018-07-07", + "name": "Fort Lauderdale, Florida" + }, + { + "type": "port", + "date": "2018-07-08", + "name": "Nassau, Bahamas" + }, + { + "type": "cruising", + "date": "2018-07-09" + }, + { + "type": "port", + "date": "2018-07-10", + "name": "Saint Thomas, US Virgin Islands" + }, + { + "type": "port", + "date": "2018-07-11", + "name": "Saint Kitts and Nevis (Basse Terre)" + }, + { + "type": "cruising", + "date": "2018-07-12" + }, + { + "type": "cruising", + "date": "2018-07-13" + }, + { + "type": "port", + "date": "2018-07-14", + "name": "Fort Lauderdale, Florida" + } + ] + }, + { + "name": "7 Night Western Caribbean Cruise", + "cruiseline": "Royal Caribbean", + "ship": "Liberty of the Seas", + "start_date": "2017-07-02", + "end_date": "2017-07-09", + "cabin": "8640", + "itinerary": [ + { + "type": "port", + "date": "2017-07-02", + "name": "Galveston, Texas" + }, + { + "type": "cruising", + "date": "2017-07-03" + }, + { + "type": "port", + "date": "2017-07-04", + "name": "Cozumel, Mexico" + }, + { + "type": "port", + "date": "2017-07-05", + "name": "Grand Cayman, Cayman Islands" + }, + { + "type": "port", + "date": "2017-07-06", + "name": "Falmouth, Jamaica" + }, + { + "type": "cruising", + "date": "2017-07-07" + }, + { + "type": "cruising", + "date": "2017-07-08" + }, + { + "type": "port", + "date": "2017-07-09", + "name": "Galveston, Texas" + } + ] + }, + { + "name": "Caribbean - Western", + "cruiseline": "Carnival", + "ship": "Carnival Freedom", + "start_date": "2015-07-25", + "end_date": "2015-08-01", + "cabin": "6289", + "itinerary": [ + { + "type": "port", + "date": "2015-07-25", + "name": "Galveston, Texas" + }, + { + "type": "cruising", + "date": "2015-07-26" + }, + { + "type": "cruising", + "date": "2015-07-27" + }, + { + "type": "port", + "date": "2015-07-28", + "name": "Roatan, Bay Islands, Honduras" + }, + { + "type": "port", + "date": "2015-07-29", + "name": "Belize City, Belize" + }, + { + "type": "port", + "date": "2015-07-30", + "name": "Cozumel, Mexico" + }, + { + "type": "cruising", + "date": "2015-07-31" + }, + { + "type": "port", + "date": "2015-08-01", + "name": "Galveston, Texas" + } + ] + } +] diff --git a/db/migrate/20230101232116_create_cruiselines.rb b/db/migrate/20230101232116_create_cruiselines.rb new file mode 100644 index 0000000..9936c30 --- /dev/null +++ b/db/migrate/20230101232116_create_cruiselines.rb @@ -0,0 +1,13 @@ +class CreateCruiselines < ActiveRecord::Migration[7.0] + def up + create_table :cruiselines do |t| + t.string :name + + t.timestamps + end + end + + def down + drop_table :cruiselines + end +end diff --git a/db/migrate/20230101232336_create_ships.rb b/db/migrate/20230101232336_create_ships.rb new file mode 100644 index 0000000..f18a111 --- /dev/null +++ b/db/migrate/20230101232336_create_ships.rb @@ -0,0 +1,14 @@ +class CreateShips < ActiveRecord::Migration[7.0] + def up + create_table :ships do |t| + t.string :name + t.references :cruiseline, foreign_key: true + + t.timestamps + end + end + + def down + drop_table :ships + end +end diff --git a/db/migrate/20230102152439_create_cruises.rb b/db/migrate/20230102152439_create_cruises.rb new file mode 100644 index 0000000..b015461 --- /dev/null +++ b/db/migrate/20230102152439_create_cruises.rb @@ -0,0 +1,18 @@ +class CreateCruises < ActiveRecord::Migration[7.0] + def up + create_table :cruises do |t| + t.string :name + t.string :cabin + t.references :cruiseline, foreign_key: true + t.references :ship, foreign_key: true + t.date :start_date + t.date :end_date + + t.timestamps + end + end + + def down + drop_table :cruises + end +end diff --git a/db/migrate/20230102163811_create_cruise_events.rb b/db/migrate/20230102163811_create_cruise_events.rb new file mode 100644 index 0000000..68e773e --- /dev/null +++ b/db/migrate/20230102163811_create_cruise_events.rb @@ -0,0 +1,16 @@ +class CreateCruiseEvents < ActiveRecord::Migration[7.0] + def up + create_table :cruise_events do |t| + t.date :date + t.integer :event_type, default: 0 + t.references :cruise, foreign_key: true + t.references :port, foreign_key: true + + t.timestamps + end + end + + def down + drop_table :cruise_events + end +end diff --git a/db/migrate/20230102163814_create_ports.rb b/db/migrate/20230102163814_create_ports.rb new file mode 100644 index 0000000..bddbb43 --- /dev/null +++ b/db/migrate/20230102163814_create_ports.rb @@ -0,0 +1,13 @@ +class CreatePorts < ActiveRecord::Migration[7.0] + def up + create_table :ports do |t| + t.string :name + + t.timestamps + end + end + + def down + drop_table :ports + end +end diff --git a/db/schema.rb b/db/schema.rb new file mode 100644 index 0000000..c987d22 --- /dev/null +++ b/db/schema.rb @@ -0,0 +1,63 @@ +# 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_01_02_163814) do + create_table "cruise_events", force: :cascade do |t| + t.date "date" + t.integer "event_type", default: 0 + t.integer "cruise_id" + t.integer "port_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["cruise_id"], name: "index_cruise_events_on_cruise_id" + t.index ["port_id"], name: "index_cruise_events_on_port_id" + end + + create_table "cruiselines", force: :cascade do |t| + t.string "name" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + create_table "cruises", force: :cascade do |t| + t.string "name" + t.string "cabin" + t.integer "cruiseline_id" + t.integer "ship_id" + t.date "start_date" + t.date "end_date" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["cruiseline_id"], name: "index_cruises_on_cruiseline_id" + t.index ["ship_id"], name: "index_cruises_on_ship_id" + end + + create_table "ports", force: :cascade do |t| + t.string "name" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + create_table "ships", force: :cascade do |t| + t.string "name" + t.integer "cruiseline_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["cruiseline_id"], name: "index_ships_on_cruiseline_id" + end + + add_foreign_key "cruise_events", "cruises" + add_foreign_key "cruise_events", "ports" + add_foreign_key "cruises", "cruiselines" + add_foreign_key "cruises", "ships" + add_foreign_key "ships", "cruiselines" +end diff --git a/lib/cruise_importer.rb b/lib/cruise_importer.rb new file mode 100644 index 0000000..f6c821e --- /dev/null +++ b/lib/cruise_importer.rb @@ -0,0 +1,25 @@ +# Imports cruises into the database from a canonical JSON file + +class CruiseImporter + DEFAULT_JSON_PATH = "./db/cruises.json" + + def initialize(json_path = DEFAULT_JSON_PATH) + @json_file = File.read(json_path) + @json_data = JSON.parse(@json_file) + end + + def import + @json_data.each do |cruise_data| + cruiseline = Cruiseline.find_or_create_by(name: cruise_data['cruiseline']) + ship = cruiseline.ships.find_or_create_by(name: cruise_data['ship']) + + cruise = cruiseline.cruises.find_or_create_by(name: cruise_data['name'], cabin: cruise_data['cabin'], start_date: cruise_data['start_date'], end_date: cruise_data['end_date'], ship: ship) + + cruise_data['itinerary'].each do |event| + port = Port.find_or_create_by(name: event['name']) if event['type'] == "port" + + cruise.cruise_events.find_or_create_by(event_type: event['type'], date: event['date'], port: port) + end + end + end +end diff --git a/models/cruise.rb b/models/cruise.rb new file mode 100644 index 0000000..6d99325 --- /dev/null +++ b/models/cruise.rb @@ -0,0 +1,5 @@ +class Cruise < ActiveRecord::Base + belongs_to :cruiseline + belongs_to :ship + has_many :cruise_events +end diff --git a/models/cruise_event.rb b/models/cruise_event.rb new file mode 100644 index 0000000..56e600a --- /dev/null +++ b/models/cruise_event.rb @@ -0,0 +1,5 @@ +class CruiseEvent < ActiveRecord::Base + belongs_to :cruise + belongs_to :port + enum event_type: [ :port, :cruising ] +end diff --git a/models/cruiseline.rb b/models/cruiseline.rb new file mode 100644 index 0000000..984e0c8 --- /dev/null +++ b/models/cruiseline.rb @@ -0,0 +1,4 @@ +class Cruiseline < ActiveRecord::Base + has_many :ships + has_many :cruises +end diff --git a/models/port.rb b/models/port.rb new file mode 100644 index 0000000..9f812ce --- /dev/null +++ b/models/port.rb @@ -0,0 +1,5 @@ +class Port < ActiveRecord::Base + def cruises + @cruises ||= Cruise.includes(:cruise_events).where('cruise_events.port_id': id) + end +end diff --git a/models/ship.rb b/models/ship.rb new file mode 100644 index 0000000..719fd36 --- /dev/null +++ b/models/ship.rb @@ -0,0 +1,4 @@ +class Ship < ActiveRecord::Base + belongs_to :cruiseline + has_many :cruises +end