From bb123a7a02a9d379e6d1097909bec22526d41b0b Mon Sep 17 00:00:00 2001 From: Mike Heft Date: Tue, 2 Jul 2024 12:42:22 -0600 Subject: [PATCH 01/10] Add address model --- .rubocop.yml | 4 ++ app/models/address.rb | 5 +++ app/models/application_record.rb | 32 +++++++++++++++ db/migrate/20240702182238_create_addresses.rb | 24 ++++++++++++ db/schema.rb | 39 +++++++++++++++++++ spec/factories/addresses.rb | 10 +++++ spec/models/address_spec.rb | 7 ++++ 7 files changed, 121 insertions(+) create mode 100644 app/models/address.rb create mode 100644 db/migrate/20240702182238_create_addresses.rb create mode 100644 db/schema.rb create mode 100644 spec/factories/addresses.rb create mode 100644 spec/models/address_spec.rb diff --git a/.rubocop.yml b/.rubocop.yml index 2246bcf..e9ad22f 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -22,12 +22,16 @@ Layout/IndentationWidth: Width: 2 Lint/UnusedMethodArgument: AutoCorrect: false +Metrics/AbcSize: + Exclude: + - "app/models/application_record.rb" Metrics/CyclomaticComplexity: Exclude: - "lib/rubocop/cop/custom/*.rb" Metrics/MethodLength: Exclude: - "lib/rubocop/cop/custom/*.rb" + - "app/models/application_record.rb" Metrics/PerceivedComplexity: Exclude: - "lib/rubocop/cop/custom/*.rb" diff --git a/app/models/address.rb b/app/models/address.rb new file mode 100644 index 0000000..8b509de --- /dev/null +++ b/app/models/address.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +class Address < ApplicationRecord + belongs_to :owner, polymorphic: true +end diff --git a/app/models/application_record.rb b/app/models/application_record.rb index 08dc537..757f93a 100644 --- a/app/models/application_record.rb +++ b/app/models/application_record.rb @@ -2,4 +2,36 @@ class ApplicationRecord < ActiveRecord::Base primary_abstract_class + self.primary_key = :token + + ASCII_LOWER = Array("a".."z").freeze + ASCII_UPPER = Array("A".."Z").freeze + DIGITS = Array("0".."9").freeze + ASCII_LETTERS = [ASCII_LOWER + ASCII_UPPER].sort_by(&:ord).freeze + ASCII_ALNUM = [ASCII_LETTERS + DIGITS].sort_by(&:ord).freeze + private_constant :ASCII_LOWER, :ASCII_UPPER, :DIGITS, :ASCII_LETTERS, :ASCII_ALNUM + + before_save :generate_token + + private def generate_token + return if token.present? + + klass_name = self.class.name + prefix = klass_name.split("::")&.last&.[](0..2)&.downcase + at = Time.stamp + # Jan 1, 2024 + from = 1704070800 # rubocop:disable Style/NumericLiterals + delta = (at - from) * 1.0 + + letters = [] + time_char_length = ASCII_ALNUM.length + length.times do + letters << ASCII_ALNUM[delta % time_char_length] + delta /= time_char_length + end + + suffix = letters.reverse.join + + "#{prefix}_#{suffix}" + end end diff --git a/db/migrate/20240702182238_create_addresses.rb b/db/migrate/20240702182238_create_addresses.rb new file mode 100644 index 0000000..6ec73ab --- /dev/null +++ b/db/migrate/20240702182238_create_addresses.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +class CreateAddresses < ActiveRecord::Migration[7.1] + def change + create_table :addresses, id: false, primary_key: :token do |t| + t.string :token, null: false + t.string :line_1 + t.string :line_2 + t.string :city + t.string :state, index: true + t.string :zip_code, index: true + t.float :latitude + t.float :longitude + t.string :place_id, null: true, index: true + t.references :owner, polymorphic: true, null: false + + t.timestamps + + t.index :token, unique: true + end + + add_index :addresses, %i[city state] + end +end diff --git a/db/schema.rb b/db/schema.rb new file mode 100644 index 0000000..e996b0b --- /dev/null +++ b/db/schema.rb @@ -0,0 +1,39 @@ +# 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.1].define(version: 2024_07_02_182238) do + # These are extensions that must be enabled in order to support this database + enable_extension "plpgsql" + + create_table "addresses", id: false, force: :cascade do |t| + t.string "token", null: false + t.string "line_1" + t.string "line_2" + t.string "city" + t.string "state" + t.string "zip_code" + t.float "latitude" + t.float "longitude" + t.string "place_id" + t.string "owner_type", null: false + t.bigint "owner_id", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["city", "state"], name: "index_addresses_on_city_and_state" + t.index ["owner_type", "owner_id"], name: "index_addresses_on_owner" + t.index ["place_id"], name: "index_addresses_on_place_id" + t.index ["state"], name: "index_addresses_on_state" + t.index ["token"], name: "index_addresses_on_token", unique: true + t.index ["zip_code"], name: "index_addresses_on_zip_code" + end + +end diff --git a/spec/factories/addresses.rb b/spec/factories/addresses.rb new file mode 100644 index 0000000..e20c723 --- /dev/null +++ b/spec/factories/addresses.rb @@ -0,0 +1,10 @@ +FactoryBot.define do + factory :address do + line_1 { "MyString" } + line_2 { "MyString" } + city { "MyString" } + state { "MyString" } + zip_code { "MyString" } + owner { nil } + end +end diff --git a/spec/models/address_spec.rb b/spec/models/address_spec.rb new file mode 100644 index 0000000..d64f82c --- /dev/null +++ b/spec/models/address_spec.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +require "rails_helper" + +RSpec.describe Address, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end From 7466470de3a830f3d0bf35dfe6d05622c9cbdff8 Mon Sep 17 00:00:00 2001 From: Mike Heft Date: Tue, 2 Jul 2024 13:16:43 -0600 Subject: [PATCH 02/10] Add tests for addresses --- .rubocop.yml | 2 ++ Gemfile | 1 + Gemfile.lock | 3 ++ app/models/address.rb | 4 ++- app/models/application_record.rb | 32 ------------------- app/models/driver.rb | 3 ++ db/migrate/20240702182238_create_addresses.rb | 8 ++--- db/migrate/20240702184830_create_drivers.rb | 11 +++++++ db/schema.rb | 16 +++++++--- spec/factories/addresses.rb | 15 +++++---- spec/factories/drivers.rb | 7 ++++ spec/models/address_spec.rb | 8 ++++- 12 files changed, 60 insertions(+), 50 deletions(-) create mode 100644 app/models/driver.rb create mode 100644 db/migrate/20240702184830_create_drivers.rb create mode 100644 spec/factories/drivers.rb diff --git a/.rubocop.yml b/.rubocop.yml index e9ad22f..e695858 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -35,6 +35,8 @@ Metrics/MethodLength: Metrics/PerceivedComplexity: Exclude: - "lib/rubocop/cop/custom/*.rb" +Naming/VariableNumber: + EnforcedStyle: snake_case Style/AccessModifierDeclarations: EnforcedStyle: inline Style/Documentation: diff --git a/Gemfile b/Gemfile index f3666a7..73f6eee 100644 --- a/Gemfile +++ b/Gemfile @@ -11,6 +11,7 @@ gem "rails", "~> 7.1.3", ">= 7.1.3.4" gem "pg", "~> 1.1" # Use the Puma web server [https://github.com/puma/puma] +gem "faker" gem "puma", ">= 5.0" # Build JSON APIs with ease [https://github.com/rails/jbuilder] diff --git a/Gemfile.lock b/Gemfile.lock index c39eba5..1585a77 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -108,6 +108,8 @@ GEM factory_bot_rails (4.11.1) factory_bot (~> 4.11.1) railties (>= 3.0.0) + faker (3.3.1) + i18n (>= 1.8.11, < 2) faraday (2.9.0) faraday-net_http (>= 2.0, < 3.2) faraday-net_http (3.1.0) @@ -295,6 +297,7 @@ DEPENDENCIES database_cleaner debug factory_bot_rails (~> 4.0) + faker faraday figaro pg (~> 1.1) diff --git a/app/models/address.rb b/app/models/address.rb index 8b509de..5a4092c 100644 --- a/app/models/address.rb +++ b/app/models/address.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true class Address < ApplicationRecord - belongs_to :owner, polymorphic: true + belongs_to :owner, polymorphic: true, optional: false + + validates :line_1, :city, :state, :zip_code, :latitude, :longitude, presence: true end diff --git a/app/models/application_record.rb b/app/models/application_record.rb index 757f93a..08dc537 100644 --- a/app/models/application_record.rb +++ b/app/models/application_record.rb @@ -2,36 +2,4 @@ class ApplicationRecord < ActiveRecord::Base primary_abstract_class - self.primary_key = :token - - ASCII_LOWER = Array("a".."z").freeze - ASCII_UPPER = Array("A".."Z").freeze - DIGITS = Array("0".."9").freeze - ASCII_LETTERS = [ASCII_LOWER + ASCII_UPPER].sort_by(&:ord).freeze - ASCII_ALNUM = [ASCII_LETTERS + DIGITS].sort_by(&:ord).freeze - private_constant :ASCII_LOWER, :ASCII_UPPER, :DIGITS, :ASCII_LETTERS, :ASCII_ALNUM - - before_save :generate_token - - private def generate_token - return if token.present? - - klass_name = self.class.name - prefix = klass_name.split("::")&.last&.[](0..2)&.downcase - at = Time.stamp - # Jan 1, 2024 - from = 1704070800 # rubocop:disable Style/NumericLiterals - delta = (at - from) * 1.0 - - letters = [] - time_char_length = ASCII_ALNUM.length - length.times do - letters << ASCII_ALNUM[delta % time_char_length] - delta /= time_char_length - end - - suffix = letters.reverse.join - - "#{prefix}_#{suffix}" - end end diff --git a/app/models/driver.rb b/app/models/driver.rb new file mode 100644 index 0000000..1ffcc6e --- /dev/null +++ b/app/models/driver.rb @@ -0,0 +1,3 @@ +class Driver < ApplicationRecord + belongs_to :address +end diff --git a/db/migrate/20240702182238_create_addresses.rb b/db/migrate/20240702182238_create_addresses.rb index 6ec73ab..63b102f 100644 --- a/db/migrate/20240702182238_create_addresses.rb +++ b/db/migrate/20240702182238_create_addresses.rb @@ -2,8 +2,7 @@ class CreateAddresses < ActiveRecord::Migration[7.1] def change - create_table :addresses, id: false, primary_key: :token do |t| - t.string :token, null: false + create_table :addresses do |t| t.string :line_1 t.string :line_2 t.string :city @@ -12,13 +11,10 @@ def change t.float :latitude t.float :longitude t.string :place_id, null: true, index: true - t.references :owner, polymorphic: true, null: false + t.references :owner, polymorphic: true, null: false, index: true t.timestamps - - t.index :token, unique: true end - add_index :addresses, %i[city state] end end diff --git a/db/migrate/20240702184830_create_drivers.rb b/db/migrate/20240702184830_create_drivers.rb new file mode 100644 index 0000000..35d1597 --- /dev/null +++ b/db/migrate/20240702184830_create_drivers.rb @@ -0,0 +1,11 @@ +class CreateDrivers < ActiveRecord::Migration[7.1] + def change + create_table :drivers do |t| + t.string :first_name + t.string :last_name + t.references :address, null: false, foreign_key: true + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index e996b0b..8006c8c 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,12 +10,11 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.1].define(version: 2024_07_02_182238) do +ActiveRecord::Schema[7.1].define(version: 2024_07_02_184830) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" - create_table "addresses", id: false, force: :cascade do |t| - t.string "token", null: false + create_table "addresses", force: :cascade do |t| t.string "line_1" t.string "line_2" t.string "city" @@ -32,8 +31,17 @@ t.index ["owner_type", "owner_id"], name: "index_addresses_on_owner" t.index ["place_id"], name: "index_addresses_on_place_id" t.index ["state"], name: "index_addresses_on_state" - t.index ["token"], name: "index_addresses_on_token", unique: true t.index ["zip_code"], name: "index_addresses_on_zip_code" end + create_table "drivers", force: :cascade do |t| + t.string "first_name" + t.string "last_name" + t.bigint "address_id", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["address_id"], name: "index_drivers_on_address_id" + end + + add_foreign_key "drivers", "addresses" end diff --git a/spec/factories/addresses.rb b/spec/factories/addresses.rb index e20c723..ae2e6a0 100644 --- a/spec/factories/addresses.rb +++ b/spec/factories/addresses.rb @@ -1,10 +1,13 @@ FactoryBot.define do factory :address do - line_1 { "MyString" } - line_2 { "MyString" } - city { "MyString" } - state { "MyString" } - zip_code { "MyString" } - owner { nil } + line_1 { Faker::Address.street_address} + line_2 { nil } + city { Faker::Address.city} + state { Faker::Address.state} + zip_code { Faker::Address.zip_code} + place_id { Faker::Internet.unique.device_token} + latitude { Faker::Address.latitude } + longitude { Faker::Address.longitude } + association :owner, factory: [:driver] end end diff --git a/spec/factories/drivers.rb b/spec/factories/drivers.rb new file mode 100644 index 0000000..667080c --- /dev/null +++ b/spec/factories/drivers.rb @@ -0,0 +1,7 @@ +FactoryBot.define do + factory :driver do + first_name { Faker::Name.first_name } + last_name { Faker::Name.last_name } + association :address, factory: :address + end +end diff --git a/spec/models/address_spec.rb b/spec/models/address_spec.rb index d64f82c..b5d9e97 100644 --- a/spec/models/address_spec.rb +++ b/spec/models/address_spec.rb @@ -3,5 +3,11 @@ require "rails_helper" RSpec.describe Address, type: :model do - pending "add some examples to (or delete) #{__FILE__}" + it {is_expected.to belong_to(:owner)} + it {is_expected.to validate_presence_of(:line_1)} + it {is_expected.to validate_presence_of(:city)} + it {is_expected.to validate_presence_of(:state)} + it {is_expected.to validate_presence_of(:zip_code)} + it {is_expected.to validate_presence_of(:latitude)} + it {is_expected.to validate_presence_of(:longitude)} end From 1096f0170060be338a8a11b10f90c7c6e6f0f318 Mon Sep 17 00:00:00 2001 From: Mike Heft Date: Tue, 2 Jul 2024 13:31:36 -0600 Subject: [PATCH 03/10] Add basic driver test --- .rubocop.yml | 3 ++- app/models/driver.rb | 6 +++++- db/migrate/20240702184830_create_drivers.rb | 1 - db/schema.rb | 3 --- spec/models/driver_spec.rb | 9 +++++++++ 5 files changed, 16 insertions(+), 6 deletions(-) create mode 100644 spec/models/driver_spec.rb diff --git a/.rubocop.yml b/.rubocop.yml index e695858..a6fd31b 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -5,7 +5,6 @@ require: AllCops: TargetRubyVersion: 3.3.0 Exclude: - - "spec/**/*.rb" - "db/**/*" - "config/**/*" - "bin/**/*" @@ -20,6 +19,8 @@ Custom/PrivateMethodStyle: Layout/IndentationWidth: Enabled: true Width: 2 +Layout/SpaceInsideBlockBraces: + EnforcedStyle: space Lint/UnusedMethodArgument: AutoCorrect: false Metrics/AbcSize: diff --git a/app/models/driver.rb b/app/models/driver.rb index 1ffcc6e..e17cc82 100644 --- a/app/models/driver.rb +++ b/app/models/driver.rb @@ -1,3 +1,7 @@ +# frozen_string_literal: true + class Driver < ApplicationRecord - belongs_to :address + has_one :address, as: :owner, dependent: :destroy + + validates :first_name, :last_name, presence: true end diff --git a/db/migrate/20240702184830_create_drivers.rb b/db/migrate/20240702184830_create_drivers.rb index 35d1597..5219d93 100644 --- a/db/migrate/20240702184830_create_drivers.rb +++ b/db/migrate/20240702184830_create_drivers.rb @@ -3,7 +3,6 @@ def change create_table :drivers do |t| t.string :first_name t.string :last_name - t.references :address, null: false, foreign_key: true t.timestamps end diff --git a/db/schema.rb b/db/schema.rb index 8006c8c..3cd10e8 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -37,11 +37,8 @@ create_table "drivers", force: :cascade do |t| t.string "first_name" t.string "last_name" - t.bigint "address_id", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.index ["address_id"], name: "index_drivers_on_address_id" end - add_foreign_key "drivers", "addresses" end diff --git a/spec/models/driver_spec.rb b/spec/models/driver_spec.rb new file mode 100644 index 0000000..08c8296 --- /dev/null +++ b/spec/models/driver_spec.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +require "rails_helper" + +RSpec.describe Driver, type: :model do + it { is_expected.to validate_presence_of(:first_name) } + it { is_expected.to validate_presence_of(:last_name) } + it { is_expected.to have_one(:address) } +end From 9cfad83cc500739b958672cc24b00333009156a7 Mon Sep 17 00:00:00 2001 From: Mike Heft Date: Tue, 2 Jul 2024 13:45:15 -0600 Subject: [PATCH 04/10] add rides --- .rubocop.yml | 2 + Gemfile | 1 + Gemfile.lock | 10 ++ app/models/ride.rb | 13 ++ config/initializers/money.rb | 115 ++++++++++++++++++ db/migrate/20240702182238_create_addresses.rb | 14 +-- db/migrate/20240702184830_create_drivers.rb | 4 +- db/migrate/20240702193305_create_rides.rb | 12 ++ db/schema.rb | 17 ++- spec/factories/addresses.rb | 12 +- spec/factories/drivers.rb | 2 + spec/models/address_spec.rb | 14 +-- spec/rails_helper.rb | 22 ++-- spec/spec_helper.rb | 4 +- spec/support/factory_bot.rb | 2 + 15 files changed, 211 insertions(+), 33 deletions(-) create mode 100644 app/models/ride.rb create mode 100644 config/initializers/money.rb create mode 100644 db/migrate/20240702193305_create_rides.rb diff --git a/.rubocop.yml b/.rubocop.yml index a6fd31b..31ec9a4 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -16,6 +16,8 @@ Custom/PrivateMethodStyle: ################### # End Custom Cops # ################### +Layout/ArgumentAlignment: + EnforcedStyle: with_fixed_indentation Layout/IndentationWidth: Enabled: true Width: 2 diff --git a/Gemfile b/Gemfile index 73f6eee..d5d95e8 100644 --- a/Gemfile +++ b/Gemfile @@ -12,6 +12,7 @@ gem "pg", "~> 1.1" # Use the Puma web server [https://github.com/puma/puma] gem "faker" +gem "money-rails" gem "puma", ">= 5.0" # Build JSON APIs with ease [https://github.com/rails/jbuilder] diff --git a/Gemfile.lock b/Gemfile.lock index 1585a77..f942826 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -139,6 +139,15 @@ GEM method_source (1.1.0) mini_mime (1.1.5) minitest (5.24.1) + monetize (1.13.0) + money (~> 6.12) + money (6.19.0) + i18n (>= 0.6.4, <= 2) + money-rails (1.15.0) + activesupport (>= 3.0) + monetize (~> 1.9) + money (~> 6.13) + railties (>= 3.0) msgpack (1.7.2) mutex_m (0.2.0) net-http (0.4.1) @@ -300,6 +309,7 @@ DEPENDENCIES faker faraday figaro + money-rails pg (~> 1.1) pry-rails puma (>= 5.0) diff --git a/app/models/ride.rb b/app/models/ride.rb new file mode 100644 index 0000000..c5656a6 --- /dev/null +++ b/app/models/ride.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +class Ride < ApplicationRecord + belongs_to :driver, optional: true + + validates :duration, :distance, :commute_duration, :amount_cents, presence: true + monetize :amount_cents, + as: :amount, + allow_nil: false, + numericality: { + greater_than_or_equal_to: 0 + } +end diff --git a/config/initializers/money.rb b/config/initializers/money.rb new file mode 100644 index 0000000..21ecaf7 --- /dev/null +++ b/config/initializers/money.rb @@ -0,0 +1,115 @@ +# encoding : utf-8 + +MoneyRails.configure do |config| + + # To set the default currency + # + config.default_currency = :usd + + # Set default bank object + # + # Example: + # config.default_bank = EuCentralBank.new + + # Add exchange rates to current money bank object. + # (The conversion rate refers to one direction only) + # + # Example: + # config.add_rate "USD", "CAD", 1.24515 + # config.add_rate "CAD", "USD", 0.803115 + + # To handle the inclusion of validations for monetized fields + # The default value is true + # + config.include_validations = true + + # Default ActiveRecord migration configuration values for columns: + # + # config.amount_column = { prefix: '', # column name prefix + # postfix: '_cents', # column name postfix + # column_name: nil, # full column name (overrides prefix, postfix and accessor name) + # type: :integer, # column type + # present: true, # column will be created + # null: false, # other options will be treated as column options + # default: 0 + # } + # + # config.currency_column = { prefix: '', + # postfix: '_currency', + # column_name: nil, + # type: :string, + # present: true, + # null: false, + # default: 'USD' + # } + + # Register a custom currency + # + # Example: + # config.register_currency = { + # priority: 1, + # iso_code: "EU4", + # name: "Euro with subunit of 4 digits", + # symbol: "€", + # symbol_first: true, + # subunit: "Subcent", + # subunit_to_unit: 10000, + # thousands_separator: ".", + # decimal_mark: "," + # } + + # Specify a rounding mode + # Any one of: + # + # BigDecimal::ROUND_UP, + # BigDecimal::ROUND_DOWN, + # BigDecimal::ROUND_HALF_UP, + # BigDecimal::ROUND_HALF_DOWN, + # BigDecimal::ROUND_HALF_EVEN, + # BigDecimal::ROUND_CEILING, + # BigDecimal::ROUND_FLOOR + # + # set to BigDecimal::ROUND_HALF_EVEN by default + # + # config.rounding_mode = BigDecimal::ROUND_HALF_UP + + # Set default money format globally. + # Default value is nil meaning "ignore this option". + # Example: + # + # config.default_format = { + # no_cents_if_whole: nil, + # symbol: nil, + # sign_before_symbol: nil + # } + + # If you would like to use I18n localization (formatting depends on the + # locale): + # config.locale_backend = :i18n + # + # Example (using default localization from rails-i18n): + # + # I18n.locale = :en + # Money.new(10_000_00, 'USD').format # => $10,000.00 + # I18n.locale = :es + # Money.new(10_000_00, 'USD').format # => $10.000,00 + # + # For the legacy behaviour of "per currency" localization (formatting depends + # only on currency): + # config.locale_backend = :currency + # + # Example: + # Money.new(10_000_00, 'USD').format # => $10,000.00 + # Money.new(10_000_00, 'EUR').format # => €10.000,00 + # + # In case you don't need localization and would like to use default values + # (can be redefined using config.default_format): + # config.locale_backend = nil + + # Set default raise_error_on_money_parsing option + # It will be raise error if assigned different currency + # The default value is false + # + # Example: + # config.raise_error_on_money_parsing = false +end diff --git a/db/migrate/20240702182238_create_addresses.rb b/db/migrate/20240702182238_create_addresses.rb index 63b102f..929bb01 100644 --- a/db/migrate/20240702182238_create_addresses.rb +++ b/db/migrate/20240702182238_create_addresses.rb @@ -3,13 +3,13 @@ class CreateAddresses < ActiveRecord::Migration[7.1] def change create_table :addresses do |t| - t.string :line_1 - t.string :line_2 - t.string :city - t.string :state, index: true - t.string :zip_code, index: true - t.float :latitude - t.float :longitude + t.string :line_1, null: false + t.string :line_2, null: true + t.string :city, null: false + t.string :state, index: true, null: false + t.string :zip_code, index: true, null: false + t.float :latitude, null: false + t.float :longitude, null: false t.string :place_id, null: true, index: true t.references :owner, polymorphic: true, null: false, index: true diff --git a/db/migrate/20240702184830_create_drivers.rb b/db/migrate/20240702184830_create_drivers.rb index 5219d93..1f45deb 100644 --- a/db/migrate/20240702184830_create_drivers.rb +++ b/db/migrate/20240702184830_create_drivers.rb @@ -1,8 +1,8 @@ class CreateDrivers < ActiveRecord::Migration[7.1] def change create_table :drivers do |t| - t.string :first_name - t.string :last_name + t.string :first_name, null: false + t.string :last_name, null: false t.timestamps end diff --git a/db/migrate/20240702193305_create_rides.rb b/db/migrate/20240702193305_create_rides.rb new file mode 100644 index 0000000..1da2b8c --- /dev/null +++ b/db/migrate/20240702193305_create_rides.rb @@ -0,0 +1,12 @@ +class CreateRides < ActiveRecord::Migration[7.1] + def change + create_table :rides do |t| + t.float :duration, index: true, null: false + t.float :distance, index: true, null: false + t.float :commute_duration, index: true, null: false + t.monetize :amount + t.references :driver, null: true, index: true + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 3cd10e8..30a4e1a 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.1].define(version: 2024_07_02_184830) do +ActiveRecord::Schema[7.1].define(version: 2024_07_02_193305) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -41,4 +41,19 @@ t.datetime "updated_at", null: false end + create_table "rides", force: :cascade do |t| + t.float "duration", null: false + t.float "distance", null: false + t.float "commute_duration", null: false + t.integer "amount_cents", default: 0, null: false + t.string "amount_currency", default: "USD", null: false + t.bigint "driver_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["commute_duration"], name: "index_rides_on_commute_duration" + t.index ["distance"], name: "index_rides_on_distance" + t.index ["driver_id"], name: "index_rides_on_driver_id" + t.index ["duration"], name: "index_rides_on_duration" + end + end diff --git a/spec/factories/addresses.rb b/spec/factories/addresses.rb index ae2e6a0..ef3b609 100644 --- a/spec/factories/addresses.rb +++ b/spec/factories/addresses.rb @@ -1,11 +1,13 @@ +# frozen_string_literal: true + FactoryBot.define do factory :address do - line_1 { Faker::Address.street_address} + line_1 { Faker::Address.street_address } line_2 { nil } - city { Faker::Address.city} - state { Faker::Address.state} - zip_code { Faker::Address.zip_code} - place_id { Faker::Internet.unique.device_token} + city { Faker::Address.city } + state { Faker::Address.state } + zip_code { Faker::Address.zip_code } + place_id { Faker::Internet.unique.device_token } latitude { Faker::Address.latitude } longitude { Faker::Address.longitude } association :owner, factory: [:driver] diff --git a/spec/factories/drivers.rb b/spec/factories/drivers.rb index 667080c..d5e4f9a 100644 --- a/spec/factories/drivers.rb +++ b/spec/factories/drivers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :driver do first_name { Faker::Name.first_name } diff --git a/spec/models/address_spec.rb b/spec/models/address_spec.rb index b5d9e97..d07f2b6 100644 --- a/spec/models/address_spec.rb +++ b/spec/models/address_spec.rb @@ -3,11 +3,11 @@ require "rails_helper" RSpec.describe Address, type: :model do - it {is_expected.to belong_to(:owner)} - it {is_expected.to validate_presence_of(:line_1)} - it {is_expected.to validate_presence_of(:city)} - it {is_expected.to validate_presence_of(:state)} - it {is_expected.to validate_presence_of(:zip_code)} - it {is_expected.to validate_presence_of(:latitude)} - it {is_expected.to validate_presence_of(:longitude)} + it { is_expected.to belong_to(:owner) } + it { is_expected.to validate_presence_of(:line_1) } + it { is_expected.to validate_presence_of(:city) } + it { is_expected.to validate_presence_of(:state) } + it { is_expected.to validate_presence_of(:zip_code) } + it { is_expected.to validate_presence_of(:latitude) } + it { is_expected.to validate_presence_of(:longitude) } end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index cc55681..9179db6 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -1,15 +1,17 @@ +# frozen_string_literal: true + # This file is copied to spec/ when you run 'rails generate rspec:install' -ENV['RAILS_ENV'] ||= 'test' -require_relative '../config/environment' +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? -require 'rspec/rails' -require 'spec_helper' -require 'webmock/rspec' -require 'vcr' +abort("The Rails environment is running in production mode!") if Rails.env.production? +require "rspec/rails" +require "spec_helper" +require "webmock/rspec" +require "vcr" VCR.configure do |config| - config.cassette_library_dir = 'spec/cassettes' + config.cassette_library_dir = "spec/cassettes" config.hook_into :webmock end # Add additional requires below this line. Rails is not loaded until this point! @@ -27,7 +29,7 @@ # directory. Alternatively, in the individual `*_spec.rb` files, manually # require only the support files necessary. # -Rails.root.glob('spec/support/**/*.rb').sort.each { |f| require f } +Rails.root.glob("spec/support/**/*.rb").sort.each { |f| require f } # Checks for pending migrations and applies them before tests are run. # If you are not using ActiveRecord, you can remove these lines. @@ -49,7 +51,7 @@ RSpec.configure do |config| # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures config.fixture_paths = [ - Rails.root.join('spec/fixtures') + Rails.root.join("spec/fixtures") ] # If you're not using ActiveRecord, or you'd prefer not to run each of your diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 19fc493..3de88d3 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,4 +1,6 @@ -require 'rails_helper' +# frozen_string_literal: true + +require "rails_helper" # 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`. diff --git a/spec/support/factory_bot.rb b/spec/support/factory_bot.rb index c7890e4..2e7665c 100644 --- a/spec/support/factory_bot.rb +++ b/spec/support/factory_bot.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec.configure do |config| config.include FactoryBot::Syntax::Methods end From 7d95926a027318db46aa325b8d343c106fe5c926 Mon Sep 17 00:00:00 2001 From: Mike Heft Date: Tue, 2 Jul 2024 15:22:35 -0600 Subject: [PATCH 05/10] Work on relationship to allow for to/from addresses on rides --- app/models/address.rb | 6 ++- app/models/driver.rb | 4 +- app/models/driver_address.rb | 9 +++++ app/models/ride.rb | 2 + db/migrate/20240702182238_create_addresses.rb | 1 - db/migrate/20240702193305_create_rides.rb | 2 + .../20240702210002_create_driver_addresses.rb | 11 +++++ ...0854_add_uniq_index_to_driver_addresses.rb | 7 ++++ db/schema.rb | 40 +++++++++++++------ spec/factories/addresses.rb | 1 - spec/factories/drivers.rb | 1 - spec/factories/rides.rb | 15 +++++++ spec/models/address_spec.rb | 8 +++- 13 files changed, 88 insertions(+), 19 deletions(-) create mode 100644 app/models/driver_address.rb create mode 100644 db/migrate/20240702210002_create_driver_addresses.rb create mode 100644 db/migrate/20240702210854_add_uniq_index_to_driver_addresses.rb create mode 100644 spec/factories/rides.rb diff --git a/app/models/address.rb b/app/models/address.rb index 5a4092c..eb9d78d 100644 --- a/app/models/address.rb +++ b/app/models/address.rb @@ -1,7 +1,9 @@ # frozen_string_literal: true class Address < ApplicationRecord - belongs_to :owner, polymorphic: true, optional: false + has_many :driver_addresses, dependent: :destroy + has_one :current_driver_address, -> { where(current: true) }, class_name: "DriverAddress", dependent: :destroy + has_one :current_driver, through: :current_driver_address, source: :address, dependent: :destroy - validates :line_1, :city, :state, :zip_code, :latitude, :longitude, presence: true + validates :line_1, :city, :state, :zip_code, :place_id, :latitude, :longitude, presence: true end diff --git a/app/models/driver.rb b/app/models/driver.rb index e17cc82..f35dee1 100644 --- a/app/models/driver.rb +++ b/app/models/driver.rb @@ -1,7 +1,9 @@ # frozen_string_literal: true class Driver < ApplicationRecord - has_one :address, as: :owner, dependent: :destroy + has_many :driver_addresses, dependent: :destroy + has_one :current_driver_address, -> { where(current: true) }, class_name: "DriverAddress", dependent: :destroy + has_one :current_address, through: :current_driver_address, source: :driver, dependent: :destroy validates :first_name, :last_name, presence: true end diff --git a/app/models/driver_address.rb b/app/models/driver_address.rb new file mode 100644 index 0000000..e41faa4 --- /dev/null +++ b/app/models/driver_address.rb @@ -0,0 +1,9 @@ +class DriverAddress < ApplicationRecord + belongs_to :driver + belongs_to :address + + validates :current, + if: -> { current }, + uniqueness: { scope: :driver_id, message: "can only have one current Driver" }, + inclusion: { in: [true, false] } +end diff --git a/app/models/ride.rb b/app/models/ride.rb index c5656a6..618ab04 100644 --- a/app/models/ride.rb +++ b/app/models/ride.rb @@ -2,6 +2,8 @@ class Ride < ApplicationRecord belongs_to :driver, optional: true + belongs_to :from_address, class_name: "Address" + belongs_to :to_address, class_name: "Address" validates :duration, :distance, :commute_duration, :amount_cents, presence: true monetize :amount_cents, diff --git a/db/migrate/20240702182238_create_addresses.rb b/db/migrate/20240702182238_create_addresses.rb index 929bb01..c0c110a 100644 --- a/db/migrate/20240702182238_create_addresses.rb +++ b/db/migrate/20240702182238_create_addresses.rb @@ -11,7 +11,6 @@ def change t.float :latitude, null: false t.float :longitude, null: false t.string :place_id, null: true, index: true - t.references :owner, polymorphic: true, null: false, index: true t.timestamps end diff --git a/db/migrate/20240702193305_create_rides.rb b/db/migrate/20240702193305_create_rides.rb index 1da2b8c..78a8251 100644 --- a/db/migrate/20240702193305_create_rides.rb +++ b/db/migrate/20240702193305_create_rides.rb @@ -6,6 +6,8 @@ def change t.float :commute_duration, index: true, null: false t.monetize :amount t.references :driver, null: true, index: true + t.references :from_address, foreign_key: {to_table: :addresses}, index: true, null: false + t.references :to_address, foreign_key: {to_table: :addresses}, index: true, null: false t.timestamps end end diff --git a/db/migrate/20240702210002_create_driver_addresses.rb b/db/migrate/20240702210002_create_driver_addresses.rb new file mode 100644 index 0000000..7ed08e8 --- /dev/null +++ b/db/migrate/20240702210002_create_driver_addresses.rb @@ -0,0 +1,11 @@ +class CreateDriverAddresses < ActiveRecord::Migration[7.1] + def change + create_table :driver_addresses do |t| + t.boolean :current, null: false, default: false + t.references :driver, null: false, foreign_key: true + t.references :address, null: false, foreign_key: true + + t.timestamps + end + end +end diff --git a/db/migrate/20240702210854_add_uniq_index_to_driver_addresses.rb b/db/migrate/20240702210854_add_uniq_index_to_driver_addresses.rb new file mode 100644 index 0000000..637419f --- /dev/null +++ b/db/migrate/20240702210854_add_uniq_index_to_driver_addresses.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddUniqIndexToDriverAddresses < ActiveRecord::Migration[7.1] + def change + add_index :driver_addresses, %i[current driver_id], unique: true, where: "(current IS TRUE)" + end +end diff --git a/db/schema.rb b/db/schema.rb index 30a4e1a..c9ad6d7 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,33 +10,41 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.1].define(version: 2024_07_02_193305) do +ActiveRecord::Schema[7.1].define(version: 2024_07_02_210854) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" create_table "addresses", force: :cascade do |t| - t.string "line_1" + t.string "line_1", null: false t.string "line_2" - t.string "city" - t.string "state" - t.string "zip_code" - t.float "latitude" - t.float "longitude" + t.string "city", null: false + t.string "state", null: false + t.string "zip_code", null: false + t.float "latitude", null: false + t.float "longitude", null: false t.string "place_id" - t.string "owner_type", null: false - t.bigint "owner_id", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false t.index ["city", "state"], name: "index_addresses_on_city_and_state" - t.index ["owner_type", "owner_id"], name: "index_addresses_on_owner" t.index ["place_id"], name: "index_addresses_on_place_id" t.index ["state"], name: "index_addresses_on_state" t.index ["zip_code"], name: "index_addresses_on_zip_code" end + create_table "driver_addresses", force: :cascade do |t| + t.boolean "current", default: false, null: false + t.bigint "driver_id", null: false + t.bigint "address_id", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["address_id"], name: "index_driver_addresses_on_address_id" + t.index ["current", "driver_id"], name: "index_driver_addresses_on_current_and_driver_id", unique: true, where: "(current IS TRUE)" + t.index ["driver_id"], name: "index_driver_addresses_on_driver_id" + end + create_table "drivers", force: :cascade do |t| - t.string "first_name" - t.string "last_name" + t.string "first_name", null: false + t.string "last_name", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false end @@ -48,12 +56,20 @@ t.integer "amount_cents", default: 0, null: false t.string "amount_currency", default: "USD", null: false t.bigint "driver_id" + t.bigint "from_address_id", null: false + t.bigint "to_address_id", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false t.index ["commute_duration"], name: "index_rides_on_commute_duration" t.index ["distance"], name: "index_rides_on_distance" t.index ["driver_id"], name: "index_rides_on_driver_id" t.index ["duration"], name: "index_rides_on_duration" + t.index ["from_address_id"], name: "index_rides_on_from_address_id" + t.index ["to_address_id"], name: "index_rides_on_to_address_id" end + add_foreign_key "driver_addresses", "addresses" + add_foreign_key "driver_addresses", "drivers" + add_foreign_key "rides", "addresses", column: "from_address_id" + add_foreign_key "rides", "addresses", column: "to_address_id" end diff --git a/spec/factories/addresses.rb b/spec/factories/addresses.rb index ef3b609..4566130 100644 --- a/spec/factories/addresses.rb +++ b/spec/factories/addresses.rb @@ -10,6 +10,5 @@ place_id { Faker::Internet.unique.device_token } latitude { Faker::Address.latitude } longitude { Faker::Address.longitude } - association :owner, factory: [:driver] end end diff --git a/spec/factories/drivers.rb b/spec/factories/drivers.rb index d5e4f9a..34d7220 100644 --- a/spec/factories/drivers.rb +++ b/spec/factories/drivers.rb @@ -4,6 +4,5 @@ factory :driver do first_name { Faker::Name.first_name } last_name { Faker::Name.last_name } - association :address, factory: :address end end diff --git a/spec/factories/rides.rb b/spec/factories/rides.rb new file mode 100644 index 0000000..554a575 --- /dev/null +++ b/spec/factories/rides.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :ride do + duration { 2.3 } + commute_duration { 1.0 } + distance { 30.1 } + amount_cents { 1200 } + + driver { nil } + + association :from_address, factory: :address + association :to_address, factory: :address + end +end diff --git a/spec/models/address_spec.rb b/spec/models/address_spec.rb index d07f2b6..ea76106 100644 --- a/spec/models/address_spec.rb +++ b/spec/models/address_spec.rb @@ -3,11 +3,17 @@ require "rails_helper" RSpec.describe Address, type: :model do - it { is_expected.to belong_to(:owner) } + it { is_expected.to have_one(:current_driver) } + it { is_expected.to validate_presence_of(:line_1) } it { is_expected.to validate_presence_of(:city) } it { is_expected.to validate_presence_of(:state) } it { is_expected.to validate_presence_of(:zip_code) } it { is_expected.to validate_presence_of(:latitude) } it { is_expected.to validate_presence_of(:longitude) } + + describe "instance_methods" do + it "can query for associated rides" do + end + end end From 920504084004fd23a4603fc8d6696053586301e8 Mon Sep 17 00:00:00 2001 From: Mike Heft Date: Tue, 2 Jul 2024 15:29:17 -0600 Subject: [PATCH 06/10] Add validations for new relationships --- app/models/driver.rb | 6 +++++- spec/models/address_spec.rb | 3 ++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/models/driver.rb b/app/models/driver.rb index f35dee1..9c00472 100644 --- a/app/models/driver.rb +++ b/app/models/driver.rb @@ -2,7 +2,11 @@ class Driver < ApplicationRecord has_many :driver_addresses, dependent: :destroy - has_one :current_driver_address, -> { where(current: true) }, class_name: "DriverAddress", dependent: :destroy + has_one :current_driver_address, + -> { where(current: true) }, + class_name: "DriverAddress", + dependent: :destroy, + inverse_of: :driver has_one :current_address, through: :current_driver_address, source: :driver, dependent: :destroy validates :first_name, :last_name, presence: true diff --git a/spec/models/address_spec.rb b/spec/models/address_spec.rb index ea76106..26d23cf 100644 --- a/spec/models/address_spec.rb +++ b/spec/models/address_spec.rb @@ -3,7 +3,8 @@ require "rails_helper" RSpec.describe Address, type: :model do - it { is_expected.to have_one(:current_driver) } + it { is_expected.to have_many(:driver_addresses) } + it { is_expected.to have_one(:current_driver).through(:current_driver_address) } it { is_expected.to validate_presence_of(:line_1) } it { is_expected.to validate_presence_of(:city) } From c155a01aab8558d511ef57fa3594731e4ea2c157 Mon Sep 17 00:00:00 2001 From: Mike Heft Date: Tue, 2 Jul 2024 16:13:15 -0600 Subject: [PATCH 07/10] Work on association validations --- app/models/address.rb | 2 -- spec/models/address_spec.rb | 19 +++++++++++-------- spec/models/driver_spec.rb | 13 ++++++++++--- 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/app/models/address.rb b/app/models/address.rb index eb9d78d..b2523c5 100644 --- a/app/models/address.rb +++ b/app/models/address.rb @@ -2,8 +2,6 @@ class Address < ApplicationRecord has_many :driver_addresses, dependent: :destroy - has_one :current_driver_address, -> { where(current: true) }, class_name: "DriverAddress", dependent: :destroy - has_one :current_driver, through: :current_driver_address, source: :address, dependent: :destroy validates :line_1, :city, :state, :zip_code, :place_id, :latitude, :longitude, presence: true end diff --git a/spec/models/address_spec.rb b/spec/models/address_spec.rb index 26d23cf..344db8e 100644 --- a/spec/models/address_spec.rb +++ b/spec/models/address_spec.rb @@ -3,15 +3,18 @@ require "rails_helper" RSpec.describe Address, type: :model do - it { is_expected.to have_many(:driver_addresses) } - it { is_expected.to have_one(:current_driver).through(:current_driver_address) } + describe "associations" do + it { is_expected.to have_many(:driver_addresses).dependent(:destroy) } + end - it { is_expected.to validate_presence_of(:line_1) } - it { is_expected.to validate_presence_of(:city) } - it { is_expected.to validate_presence_of(:state) } - it { is_expected.to validate_presence_of(:zip_code) } - it { is_expected.to validate_presence_of(:latitude) } - it { is_expected.to validate_presence_of(:longitude) } + describe "attributes" do + it { is_expected.to validate_presence_of(:line_1) } + it { is_expected.to validate_presence_of(:city) } + it { is_expected.to validate_presence_of(:state) } + it { is_expected.to validate_presence_of(:zip_code) } + it { is_expected.to validate_presence_of(:latitude) } + it { is_expected.to validate_presence_of(:longitude) } + end describe "instance_methods" do it "can query for associated rides" do diff --git a/spec/models/driver_spec.rb b/spec/models/driver_spec.rb index 08c8296..349e92b 100644 --- a/spec/models/driver_spec.rb +++ b/spec/models/driver_spec.rb @@ -3,7 +3,14 @@ require "rails_helper" RSpec.describe Driver, type: :model do - it { is_expected.to validate_presence_of(:first_name) } - it { is_expected.to validate_presence_of(:last_name) } - it { is_expected.to have_one(:address) } + describe "associations" do + it { is_expected.to have_many(:driver_addresses).dependent(:destroy) } + it { is_expected.to have_one(:current_driver_address).inverse_of(:driver).dependent(:destroy) } + it { is_expected.to have_one(:current_address).through(:current_driver_address).dependent(:destroy) } + end + + describe "attributes" do + it { is_expected.to validate_presence_of(:first_name) } + it { is_expected.to validate_presence_of(:last_name) } + end end From 00828a923717d9b0ccddea8971b120d72c10147e Mon Sep 17 00:00:00 2001 From: Mike Heft Date: Tue, 2 Jul 2024 16:24:19 -0600 Subject: [PATCH 08/10] Add spec for joins table --- app/models/driver_address.rb | 7 ++++++- spec/factories/driver_addresses.rb | 8 ++++++++ spec/models/driver_address_spec.rb | 17 +++++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 spec/factories/driver_addresses.rb create mode 100644 spec/models/driver_address_spec.rb diff --git a/app/models/driver_address.rb b/app/models/driver_address.rb index e41faa4..d6162b0 100644 --- a/app/models/driver_address.rb +++ b/app/models/driver_address.rb @@ -1,9 +1,14 @@ +# frozen_string_literal: true + class DriverAddress < ApplicationRecord belongs_to :driver belongs_to :address validates :current, if: -> { current }, - uniqueness: { scope: :driver_id, message: "can only have one current Driver" }, + uniqueness: { + scope: :driver_id, + message: "can only have one current Driver" + }, inclusion: { in: [true, false] } end diff --git a/spec/factories/driver_addresses.rb b/spec/factories/driver_addresses.rb new file mode 100644 index 0000000..a882534 --- /dev/null +++ b/spec/factories/driver_addresses.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :driver_address do + driver + address + end +end diff --git a/spec/models/driver_address_spec.rb b/spec/models/driver_address_spec.rb new file mode 100644 index 0000000..1810f5b --- /dev/null +++ b/spec/models/driver_address_spec.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +require "rails_helper" + +RSpec.describe DriverAddress, type: :model do + subject { create(:driver_address) } + describe "associations" do + it { is_expected.to belong_to(:driver) } + it { is_expected.to belong_to(:address) } + + it do + is_expected.to validate_uniqueness_of(:current) + .scoped_to(:driver_id) + .with_message("can only have one current Driver") + end + end +end From 6f56dc6483011d7fced70e1c12406a83ef24f11b Mon Sep 17 00:00:00 2001 From: Mike Heft Date: Tue, 2 Jul 2024 16:35:40 -0600 Subject: [PATCH 09/10] Add base ride spec --- spec/models/ride_spec.rb | 21 +++++++++++++++++++++ spec/spec_helper.rb | 1 + 2 files changed, 22 insertions(+) create mode 100644 spec/models/ride_spec.rb diff --git a/spec/models/ride_spec.rb b/spec/models/ride_spec.rb new file mode 100644 index 0000000..8140476 --- /dev/null +++ b/spec/models/ride_spec.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +require "rails_helper" + +RSpec.describe Ride, type: :model do + subject { create(:ride) } + describe "associations" do + it { is_expected.to belong_to(:driver).optional(true) } + it { is_expected.to belong_to(:from_address).class_name("Address") } + it { is_expected.to belong_to(:to_address).class_name("Address") } + end + + describe "attributes" do + it { is_expected.to validate_presence_of(:duration) } + it { is_expected.to validate_presence_of(:distance) } + it { is_expected.to validate_presence_of(:commute_duration) } + it { is_expected.to validate_presence_of(:amount_cents) } + it { is_expected.to monetize(:amount_cents).as(:amount) } + it { is_expected.to validate_numericality_of(:amount_cents) } + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 3de88d3..d835b85 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require "rails_helper" +require "money-rails/test_helpers" # 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`. From 5a042db7aec136a71890b5538e1916b9fb9db6f5 Mon Sep 17 00:00:00 2001 From: Mike Heft Date: Tue, 2 Jul 2024 16:56:49 -0600 Subject: [PATCH 10/10] Add specs --- .rubocop.yml | 2 ++ app/models/address.rb | 6 ++++++ app/models/ride.rb | 4 ++++ spec/models/address_spec.rb | 6 ++++++ 4 files changed, 18 insertions(+) diff --git a/.rubocop.yml b/.rubocop.yml index 31ec9a4..a8841c9 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -40,6 +40,8 @@ Metrics/PerceivedComplexity: - "lib/rubocop/cop/custom/*.rb" Naming/VariableNumber: EnforcedStyle: snake_case +Rails/InverseOf: + Enabled: false Style/AccessModifierDeclarations: EnforcedStyle: inline Style/Documentation: diff --git a/app/models/address.rb b/app/models/address.rb index b2523c5..80eb353 100644 --- a/app/models/address.rb +++ b/app/models/address.rb @@ -2,6 +2,12 @@ class Address < ApplicationRecord has_many :driver_addresses, dependent: :destroy + has_many :ride_origins, class_name: "Ride", foreign_key: "from_address_id", dependent: nil, inverse_of: :from_address + has_many :ride_destinations, class_name: "Ride", foreign_key: "to_address_id", dependent: nil, inverse_of: :to_address validates :line_1, :city, :state, :zip_code, :place_id, :latitude, :longitude, presence: true + + def rides + Ride.by_address(id) + end end diff --git a/app/models/ride.rb b/app/models/ride.rb index 618ab04..eea732c 100644 --- a/app/models/ride.rb +++ b/app/models/ride.rb @@ -12,4 +12,8 @@ class Ride < ApplicationRecord numericality: { greater_than_or_equal_to: 0 } + + scope :by_address, ->(address_id) { + where(from_address_id: address_id).or(where(to_address_id: address_id)) + } end diff --git a/spec/models/address_spec.rb b/spec/models/address_spec.rb index 344db8e..1cab9ba 100644 --- a/spec/models/address_spec.rb +++ b/spec/models/address_spec.rb @@ -18,6 +18,12 @@ describe "instance_methods" do it "can query for associated rides" do + ride = create(:ride) + from_address = ride.from_address + to_address = ride.to_address + + expect(from_address.rides).to include(ride) + expect(to_address.rides).to include(ride) end end end