From ea0206fb3c6802401522b3baf8c4ecd6a6f7902e Mon Sep 17 00:00:00 2001 From: yoshiro fujimaki Date: Wed, 12 Mar 2025 01:16:43 +0900 Subject: [PATCH 01/19] =?UTF-8?q?=E5=88=9D=E6=9C=9FDB=E3=80=81seed?= =?UTF-8?q?=E3=80=81model=E3=82=92=E4=BD=9C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/models/energy_charges_basic_rate.rb | 6 + .../app/models/energy_charges_usage_rate.rb | 28 +++++ .../challenge/app/models/plan.rb | 27 +++++ .../challenge/app/models/provider.rb | 5 + .../20250311091328_create_providers.rb | 11 ++ .../db/migrate/20250311091421_create_plans.rb | 12 ++ ...91545_create_energy_charges_basic_rates.rb | 13 +++ ...91629_create_energy_charges_usage_rates.rb | 12 ++ serverside_challenge_2/challenge/db/schema.rb | 56 +++++++++ serverside_challenge_2/challenge/db/seeds.rb | 2 + .../challenge/db/seeds/provider_plan_rates.rb | 110 ++++++++++++++++++ .../fixtures/energy_charges_basic_rates.yml | 11 ++ .../fixtures/energy_charges_usage_rates.yml | 13 +++ .../challenge/test/fixtures/plans.yml | 9 ++ .../challenge/test/fixtures/provider.yml | 7 ++ .../models/energy_charges_basic_rate_test.rb | 7 ++ .../models/energy_charges_usage_rate_test.rb | 7 ++ .../challenge/test/models/plan_test.rb | 7 ++ .../challenge/test/models/provider_test.rb | 7 ++ 19 files changed, 350 insertions(+) create mode 100644 serverside_challenge_2/challenge/app/models/energy_charges_basic_rate.rb create mode 100644 serverside_challenge_2/challenge/app/models/energy_charges_usage_rate.rb create mode 100644 serverside_challenge_2/challenge/app/models/plan.rb create mode 100644 serverside_challenge_2/challenge/app/models/provider.rb create mode 100644 serverside_challenge_2/challenge/db/migrate/20250311091328_create_providers.rb create mode 100644 serverside_challenge_2/challenge/db/migrate/20250311091421_create_plans.rb create mode 100644 serverside_challenge_2/challenge/db/migrate/20250311091545_create_energy_charges_basic_rates.rb create mode 100644 serverside_challenge_2/challenge/db/migrate/20250311091629_create_energy_charges_usage_rates.rb create mode 100644 serverside_challenge_2/challenge/db/schema.rb create mode 100644 serverside_challenge_2/challenge/db/seeds/provider_plan_rates.rb create mode 100644 serverside_challenge_2/challenge/test/fixtures/energy_charges_basic_rates.yml create mode 100644 serverside_challenge_2/challenge/test/fixtures/energy_charges_usage_rates.yml create mode 100644 serverside_challenge_2/challenge/test/fixtures/plans.yml create mode 100644 serverside_challenge_2/challenge/test/fixtures/provider.yml create mode 100644 serverside_challenge_2/challenge/test/models/energy_charges_basic_rate_test.rb create mode 100644 serverside_challenge_2/challenge/test/models/energy_charges_usage_rate_test.rb create mode 100644 serverside_challenge_2/challenge/test/models/plan_test.rb create mode 100644 serverside_challenge_2/challenge/test/models/provider_test.rb diff --git a/serverside_challenge_2/challenge/app/models/energy_charges_basic_rate.rb b/serverside_challenge_2/challenge/app/models/energy_charges_basic_rate.rb new file mode 100644 index 000000000..d1cfabfc6 --- /dev/null +++ b/serverside_challenge_2/challenge/app/models/energy_charges_basic_rate.rb @@ -0,0 +1,6 @@ +class EnergyChargesBasicRate < ApplicationRecord + belongs_to :plan + + validates :basic_rate, presence: true + validates :ampere, presence: true, uniqueness: { scope: :plan_id } +end diff --git a/serverside_challenge_2/challenge/app/models/energy_charges_usage_rate.rb b/serverside_challenge_2/challenge/app/models/energy_charges_usage_rate.rb new file mode 100644 index 000000000..4eb334bec --- /dev/null +++ b/serverside_challenge_2/challenge/app/models/energy_charges_usage_rate.rb @@ -0,0 +1,28 @@ +class EnergyChargesUsageRate < ApplicationRecord + belongs_to :plan + + validates :min_usage, :unit_rate, presence: true + validates :max_usage, numericality: { greater_than: :min_usage }, allow_nil: true + + validate :validate_usage_ranges + + private + + def validate_usage_ranges + # usageがplan内で重複していないかを確認する + energy_chages_usage_rates = EnergyChargesUsageRate.where(plan: plan).order(:min_usage) + before_max_usage = nil + energy_chages_usage_rates.each do |rates| + if before_max_usage.present? && rates.min_usage <= before_max_usage + errors.add(:base, '同一プラン内で使用量の範囲が他のレコードと重複しています') + break + end + + before_max_usage = rates.max_usage + end + + if energy_chages_usage_rates.where(max_usage: nil).count > 1 + errors.add(:base, '同一プラン内で使用量上限がない項目が2つ以上存在します') + end + end +end diff --git a/serverside_challenge_2/challenge/app/models/plan.rb b/serverside_challenge_2/challenge/app/models/plan.rb new file mode 100644 index 000000000..c87a224c1 --- /dev/null +++ b/serverside_challenge_2/challenge/app/models/plan.rb @@ -0,0 +1,27 @@ +class Plan < ApplicationRecord + belongs_to :provider + has_many :energy_charges_basic_rates, dependent: :destroy + has_many :energy_charges_usage_rates, dependent: :destroy + + validates :name, presence: true, uniqueness: { scope: :provider_id } + + def energy_price(ampere, usage) + # 基本料金(契約アンペア数(A)に対応する基本料金(円))の計算 + energy_charges_basic_rate = energy_charges_basic_rates.find_by(ampere: ampere) + basic_price = energy_charges_basic_rate.basic_rate + + # 従量料金(電気使用量(kWh) * 従量料金単価(円/kWh))の計算 + energy_charges_usage_rate = energy_charges_usage_rate_from_usage(usage) + usage_price = usage * energy_charges_usage_rate.unit_rate + + # 電気料金(基本料金 + 従量料金)を返却 + return basic_price + usage_price + end + + def energy_charges_usage_rate_from_usage(usage) + energy_charges_usage_rates + .where('min_usage <= :usage AND (max_usage IS NULL OR max_usage >= :usage)', usage: usage) + .order(:min_usage) + .first + end +end diff --git a/serverside_challenge_2/challenge/app/models/provider.rb b/serverside_challenge_2/challenge/app/models/provider.rb new file mode 100644 index 000000000..9d46f4104 --- /dev/null +++ b/serverside_challenge_2/challenge/app/models/provider.rb @@ -0,0 +1,5 @@ +class Provider < ApplicationRecord + has_many :plans, dependent: :destroy + + validates :name, presence: true, uniqueness: true +end diff --git a/serverside_challenge_2/challenge/db/migrate/20250311091328_create_providers.rb b/serverside_challenge_2/challenge/db/migrate/20250311091328_create_providers.rb new file mode 100644 index 000000000..a4ebb014f --- /dev/null +++ b/serverside_challenge_2/challenge/db/migrate/20250311091328_create_providers.rb @@ -0,0 +1,11 @@ +class CreateProviders < ActiveRecord::Migration[7.0] + def change + create_table :providers, comment: '電力会社を格納する' do |t| + t.string :name, null: false, comment: '会社名' + + t.timestamps + end + + add_index :providers, :name, unique: true + end +end diff --git a/serverside_challenge_2/challenge/db/migrate/20250311091421_create_plans.rb b/serverside_challenge_2/challenge/db/migrate/20250311091421_create_plans.rb new file mode 100644 index 000000000..688520c4c --- /dev/null +++ b/serverside_challenge_2/challenge/db/migrate/20250311091421_create_plans.rb @@ -0,0 +1,12 @@ +class CreatePlans < ActiveRecord::Migration[7.0] + def change + create_table :plans, comment: '各電力会社毎のプランを格納する' do |t| + t.references :provider, null: false, foreign_key: true + t.string :name, null: false, comment: 'プラン名' + + t.timestamps + end + + add_index :plans, [:provider_id, :name], unique: true + end +end diff --git a/serverside_challenge_2/challenge/db/migrate/20250311091545_create_energy_charges_basic_rates.rb b/serverside_challenge_2/challenge/db/migrate/20250311091545_create_energy_charges_basic_rates.rb new file mode 100644 index 000000000..78f884794 --- /dev/null +++ b/serverside_challenge_2/challenge/db/migrate/20250311091545_create_energy_charges_basic_rates.rb @@ -0,0 +1,13 @@ +class CreateEnergyChargesBasicRates < ActiveRecord::Migration[7.0] + def change + create_table :energy_charges_basic_rates, comment: 'プラン毎の基本料金を格納する' do |t| + t.references :plan, null: false, foreign_key: true + t.integer :ampere, null: false, comment: '契約アンペア数(A)' + t.decimal :basic_rate, null: false, comment: '基本料金(円)' + + t.timestamps + end + + add_index :energy_charges_basic_rates, [:plan_id, :ampere], unique: true + end +end diff --git a/serverside_challenge_2/challenge/db/migrate/20250311091629_create_energy_charges_usage_rates.rb b/serverside_challenge_2/challenge/db/migrate/20250311091629_create_energy_charges_usage_rates.rb new file mode 100644 index 000000000..b58b71101 --- /dev/null +++ b/serverside_challenge_2/challenge/db/migrate/20250311091629_create_energy_charges_usage_rates.rb @@ -0,0 +1,12 @@ +class CreateEnergyChargesUsageRates < ActiveRecord::Migration[7.0] + def change + create_table :energy_charges_usage_rates, comment: 'プラン毎の従量料金を格納する' do |t| + t.references :plan, null: false, foreign_key: true + t.integer :min_usage, null: false, comment: '電気使用量(kWh)の下限値' + t.integer :max_usage, comment: '電気使用量(kWh)の上限値' + t.decimal :unit_rate, null: false, comment: '従量料金単価(円/kWh)' + + t.timestamps + end + end +end diff --git a/serverside_challenge_2/challenge/db/schema.rb b/serverside_challenge_2/challenge/db/schema.rb new file mode 100644 index 000000000..5ef3deabc --- /dev/null +++ b/serverside_challenge_2/challenge/db/schema.rb @@ -0,0 +1,56 @@ +# 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: 2025_03_11_091629) do + # These are extensions that must be enabled in order to support this database + enable_extension "plpgsql" + + create_table "energy_charges_basic_rates", comment: "プラン毎の基本料金を格納する", force: :cascade do |t| + t.bigint "plan_id", null: false + t.integer "ampere", null: false, comment: "契約アンペア数(A)" + t.decimal "basic_rate", null: false, comment: "基本料金(円)" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["plan_id", "ampere"], name: "index_energy_charges_basic_rates_on_plan_id_and_ampere", unique: true + t.index ["plan_id"], name: "index_energy_charges_basic_rates_on_plan_id" + end + + create_table "energy_charges_usage_rates", comment: "プラン毎の従量料金を格納する", force: :cascade do |t| + t.bigint "plan_id", null: false + t.integer "min_usage", null: false, comment: "電気使用量(kWh)の下限値" + t.integer "max_usage", comment: "電気使用量(kWh)の上限値" + t.decimal "unit_rate", null: false, comment: "従量料金単価(円/kWh)" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["plan_id"], name: "index_energy_charges_usage_rates_on_plan_id" + end + + create_table "plans", comment: "各電力会社毎のプランを格納する", force: :cascade do |t| + t.bigint "provider_id", null: false + t.string "name", null: false, comment: "プラン名" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["provider_id", "name"], name: "index_plans_on_provider_id_and_name", unique: true + t.index ["provider_id"], name: "index_plans_on_provider_id" + end + + create_table "providers", comment: "電力会社を格納する", force: :cascade do |t| + t.string "name", null: false, comment: "会社名" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["name"], name: "index_providers_on_name", unique: true + end + + add_foreign_key "energy_charges_basic_rates", "plans" + add_foreign_key "energy_charges_usage_rates", "plans" + add_foreign_key "plans", "providers" +end diff --git a/serverside_challenge_2/challenge/db/seeds.rb b/serverside_challenge_2/challenge/db/seeds.rb index bc25fce30..bcf7c548f 100644 --- a/serverside_challenge_2/challenge/db/seeds.rb +++ b/serverside_challenge_2/challenge/db/seeds.rb @@ -5,3 +5,5 @@ # # movies = Movie.create([{ name: "Star Wars" }, { name: "Lord of the Rings" }]) # Character.create(name: "Luke", movie: movies.first) + +require_relative 'seeds/provider_plan_rates' diff --git a/serverside_challenge_2/challenge/db/seeds/provider_plan_rates.rb b/serverside_challenge_2/challenge/db/seeds/provider_plan_rates.rb new file mode 100644 index 000000000..7e180bf41 --- /dev/null +++ b/serverside_challenge_2/challenge/db/seeds/provider_plan_rates.rb @@ -0,0 +1,110 @@ +def create_provider_and_plans(provider_name, plans_data) + provider = Provider.find_or_create_by!(name: provider_name) + + plans_data.each do |plan_data| + plan = Plan.find_or_create_by!(provider: provider, name: plan_data[:name]) + + create_basic_rates(plan, plan_data[:basic_rates]) + create_usage_rates(plan, plan_data[:usage_rates]) + end +end + +def create_basic_rates(plan, rates) + rates.each do |rate_data| + EnergyChargesBasicRate.find_or_create_by!( + plan: plan, + ampere: rate_data[:ampere] + ) do |rate| + rate.basic_rate = rate_data[:basic_rate] + end + end +end + +def create_usage_rates(plan, rates) + rates.each do |rate_data| + EnergyChargesUsageRate.find_or_create_by!( + plan: plan, + min_usage: rate_data[:min_usage], + max_usage: rate_data[:max_usage] + ) do |rate| + rate.unit_rate = rate_data[:unit_rate] + end + end +end + +# 東京電力エナジーパートナー +create_provider_and_plans('東京電力エナジーパートナー', [ + { + name: '従量電灯B', + basic_rates: [ + { ampere: 10, basic_rate: 286.00 }, + { ampere: 15, basic_rate: 429.00 }, + { ampere: 20, basic_rate: 572.00 }, + { ampere: 30, basic_rate: 858.00 }, + { ampere: 40, basic_rate: 1144.00 }, + { ampere: 50, basic_rate: 1430.00 }, + { ampere: 60, basic_rate: 1716.00 } + ], + usage_rates: [ + { min_usage: 0, max_usage: 120, unit_rate: 19.88 }, + { min_usage: 121, max_usage: 300, unit_rate: 26.48 }, + { min_usage: 301, max_usage: nil, unit_rate: 30.57 } + ] + }, + { + name: 'スタンダードS', + basic_rates: [ + { ampere: 10, basic_rate: 311.75 }, + { ampere: 15, basic_rate: 467.63 }, + { ampere: 20, basic_rate: 623.50 }, + { ampere: 30, basic_rate: 935.25 }, + { ampere: 40, basic_rate: 1247.00 }, + { ampere: 50, basic_rate: 1558.75 }, + { ampere: 60, basic_rate: 1870.50 } + ], + usage_rates: [ + { min_usage: 0, max_usage: 120, unit_rate: 29.80 }, + { min_usage: 121, max_usage: 300, unit_rate: 36.40 }, + { min_usage: 301, max_usage: nil, unit_rate: 40.49 } + ] + } +]) + +# 東京ガス +create_provider_and_plans('東京ガス', [ + { + name: 'ずっとも電気1', + basic_rates: [ + { ampere: 30, basic_rate: 858.00 }, + { ampere: 40, basic_rate: 1144.00 }, + { ampere: 50, basic_rate: 1430.00 }, + { ampere: 60, basic_rate: 1716.00 } + ], + usage_rates: [ + { min_usage: 0, max_usage: 140, unit_rate: 23.67 }, + { min_usage: 141, max_usage: 350, unit_rate: 23.88 }, + { min_usage: 351, max_usage: nil, unit_rate: 26.41 } + ] + } +]) + +# Looopでんき +create_provider_and_plans('Looopでんき', [ + { + name: 'おうちプラン', + basic_rates: [ + { ampere: 10, basic_rate: 0.00 }, + { ampere: 15, basic_rate: 0.00 }, + { ampere: 20, basic_rate: 0.00 }, + { ampere: 30, basic_rate: 0.00 }, + { ampere: 40, basic_rate: 0.00 }, + { ampere: 50, basic_rate: 0.00 }, + { ampere: 60, basic_rate: 0.00 } + ], + usage_rates: [ + { min_usage: 0, max_usage: nil, unit_rate: 28.8 } + ] + } +]) + +puts '各電力会社と、そのプラン作成が完了しました' diff --git a/serverside_challenge_2/challenge/test/fixtures/energy_charges_basic_rates.yml b/serverside_challenge_2/challenge/test/fixtures/energy_charges_basic_rates.yml new file mode 100644 index 000000000..b16a5dd65 --- /dev/null +++ b/serverside_challenge_2/challenge/test/fixtures/energy_charges_basic_rates.yml @@ -0,0 +1,11 @@ +# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + plan: one + ampere: 1 + basic_rate: 9.99 + +two: + plan: two + ampere: 1 + basic_rate: 9.99 diff --git a/serverside_challenge_2/challenge/test/fixtures/energy_charges_usage_rates.yml b/serverside_challenge_2/challenge/test/fixtures/energy_charges_usage_rates.yml new file mode 100644 index 000000000..99e07102e --- /dev/null +++ b/serverside_challenge_2/challenge/test/fixtures/energy_charges_usage_rates.yml @@ -0,0 +1,13 @@ +# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + plan: one + min_usage: 1 + max_usage: 1 + unit_rate: 9.99 + +two: + plan: two + min_usage: 1 + max_usage: 1 + unit_rate: 9.99 diff --git a/serverside_challenge_2/challenge/test/fixtures/plans.yml b/serverside_challenge_2/challenge/test/fixtures/plans.yml new file mode 100644 index 000000000..ebbc0b472 --- /dev/null +++ b/serverside_challenge_2/challenge/test/fixtures/plans.yml @@ -0,0 +1,9 @@ +# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + company: one + name: MyString + +two: + company: two + name: MyString diff --git a/serverside_challenge_2/challenge/test/fixtures/provider.yml b/serverside_challenge_2/challenge/test/fixtures/provider.yml new file mode 100644 index 000000000..7d4122404 --- /dev/null +++ b/serverside_challenge_2/challenge/test/fixtures/provider.yml @@ -0,0 +1,7 @@ +# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + name: MyString + +two: + name: MyString diff --git a/serverside_challenge_2/challenge/test/models/energy_charges_basic_rate_test.rb b/serverside_challenge_2/challenge/test/models/energy_charges_basic_rate_test.rb new file mode 100644 index 000000000..643c7ae24 --- /dev/null +++ b/serverside_challenge_2/challenge/test/models/energy_charges_basic_rate_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class EnergyChargesBasicRateTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/serverside_challenge_2/challenge/test/models/energy_charges_usage_rate_test.rb b/serverside_challenge_2/challenge/test/models/energy_charges_usage_rate_test.rb new file mode 100644 index 000000000..2b0a85705 --- /dev/null +++ b/serverside_challenge_2/challenge/test/models/energy_charges_usage_rate_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class EnergyChargesUsageRateTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/serverside_challenge_2/challenge/test/models/plan_test.rb b/serverside_challenge_2/challenge/test/models/plan_test.rb new file mode 100644 index 000000000..c1b4ca447 --- /dev/null +++ b/serverside_challenge_2/challenge/test/models/plan_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class PlanTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/serverside_challenge_2/challenge/test/models/provider_test.rb b/serverside_challenge_2/challenge/test/models/provider_test.rb new file mode 100644 index 000000000..5c62c6c6b --- /dev/null +++ b/serverside_challenge_2/challenge/test/models/provider_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class ProviderTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end From 0feb1fef162562addba9fe6884a6ddbf520886c1 Mon Sep 17 00:00:00 2001 From: yoshiro fujimaki Date: Wed, 12 Mar 2025 02:29:07 +0900 Subject: [PATCH 02/19] =?UTF-8?q?=E3=83=97=E3=83=A9=E3=83=B3=E3=81=94?= =?UTF-8?q?=E3=81=A8=E3=81=AE=E9=9B=BB=E6=B0=97=E6=96=99=E9=87=91=E3=82=92?= =?UTF-8?q?=E8=BF=94=E3=81=99API=E3=82=92=E4=BD=9C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/controllers/energy_prices_controller.rb | 14 ++++++++++++++ .../challenge/app/models/plan.rb | 11 ++++++++--- .../challenge/app/models/provider.rb | 13 +++++++++++++ .../app/services/energy_price_calculate_service.rb | 11 +++++++++++ serverside_challenge_2/challenge/config/routes.rb | 2 ++ .../controllers/energy_prices_controller_test.rb | 7 +++++++ 6 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 serverside_challenge_2/challenge/app/controllers/energy_prices_controller.rb create mode 100644 serverside_challenge_2/challenge/app/services/energy_price_calculate_service.rb create mode 100644 serverside_challenge_2/challenge/test/controllers/energy_prices_controller_test.rb diff --git a/serverside_challenge_2/challenge/app/controllers/energy_prices_controller.rb b/serverside_challenge_2/challenge/app/controllers/energy_prices_controller.rb new file mode 100644 index 000000000..936b9f54b --- /dev/null +++ b/serverside_challenge_2/challenge/app/controllers/energy_prices_controller.rb @@ -0,0 +1,14 @@ +class EnergyPricesController < ApplicationController + def index + ampere = Integer(index_params[:ampere]) + usage = Integer(index_params[:usage]) + plans = EnergyPriceCalculateService.new(ampere, usage).calc + render json: plans + end + + private + + def index_params + params.permit(:ampere, :usage) + end +end diff --git a/serverside_challenge_2/challenge/app/models/plan.rb b/serverside_challenge_2/challenge/app/models/plan.rb index c87a224c1..e7d09fb4b 100644 --- a/serverside_challenge_2/challenge/app/models/plan.rb +++ b/serverside_challenge_2/challenge/app/models/plan.rb @@ -6,16 +6,21 @@ class Plan < ApplicationRecord validates :name, presence: true, uniqueness: { scope: :provider_id } def energy_price(ampere, usage) - # 基本料金(契約アンペア数(A)に対応する基本料金(円))の計算 + # 基本料金、もしくは従量料金がなければ計算不能のためnilを返却する energy_charges_basic_rate = energy_charges_basic_rates.find_by(ampere: ampere) + return nil if energy_charges_basic_rate.nil? + + energy_charges_usage_rate = energy_charges_usage_rate_from_usage(usage) + return nil if energy_charges_usage_rate.nil? + + # 基本料金(契約アンペア数(A)に対応する基本料金(円))の計算 basic_price = energy_charges_basic_rate.basic_rate # 従量料金(電気使用量(kWh) * 従量料金単価(円/kWh))の計算 - energy_charges_usage_rate = energy_charges_usage_rate_from_usage(usage) usage_price = usage * energy_charges_usage_rate.unit_rate # 電気料金(基本料金 + 従量料金)を返却 - return basic_price + usage_price + basic_price + usage_price end def energy_charges_usage_rate_from_usage(usage) diff --git a/serverside_challenge_2/challenge/app/models/provider.rb b/serverside_challenge_2/challenge/app/models/provider.rb index 9d46f4104..5c16fca95 100644 --- a/serverside_challenge_2/challenge/app/models/provider.rb +++ b/serverside_challenge_2/challenge/app/models/provider.rb @@ -2,4 +2,17 @@ class Provider < ApplicationRecord has_many :plans, dependent: :destroy validates :name, presence: true, uniqueness: true + + def energy_prices(ampere, usage) + plans.map do |plan| + price = plan.energy_price(ampere, usage) + if price + { + provider_name: name, + plan_name: plan.name, + price: plan.energy_price(ampere, usage) + } + end + end.compact + end end diff --git a/serverside_challenge_2/challenge/app/services/energy_price_calculate_service.rb b/serverside_challenge_2/challenge/app/services/energy_price_calculate_service.rb new file mode 100644 index 000000000..976384731 --- /dev/null +++ b/serverside_challenge_2/challenge/app/services/energy_price_calculate_service.rb @@ -0,0 +1,11 @@ +class EnergyPriceCalculateService + def initialize(ampere, usage) + @ampere = ampere + @usage = usage + end + + def calc + Provider.order(:id).map{|provider| provider.energy_prices(@ampere, @usage)}.flatten + end +end + diff --git a/serverside_challenge_2/challenge/config/routes.rb b/serverside_challenge_2/challenge/config/routes.rb index 262ffd547..cdea3f147 100644 --- a/serverside_challenge_2/challenge/config/routes.rb +++ b/serverside_challenge_2/challenge/config/routes.rb @@ -3,4 +3,6 @@ # Defines the root path route ("/") # root "articles#index" + + resources :energy_prices, only: [:index] end diff --git a/serverside_challenge_2/challenge/test/controllers/energy_prices_controller_test.rb b/serverside_challenge_2/challenge/test/controllers/energy_prices_controller_test.rb new file mode 100644 index 000000000..b7152d1b7 --- /dev/null +++ b/serverside_challenge_2/challenge/test/controllers/energy_prices_controller_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class EnergyPricesControllerTest < ActionDispatch::IntegrationTest + # test "the truth" do + # assert true + # end +end From e8e9196a3daf101dc14792d4e274b8c589475fe7 Mon Sep 17 00:00:00 2001 From: yoshiro fujimaki Date: Wed, 12 Mar 2025 08:45:46 +0900 Subject: [PATCH 03/19] =?UTF-8?q?table=E3=81=AB=E4=BD=BF=E3=82=8F=E3=82=8C?= =?UTF-8?q?=E3=82=8Benergy=E3=81=8C=E3=82=A8=E3=83=8D=E3=83=AB=E3=82=AE?= =?UTF-8?q?=E3=83=BC=E5=85=A8=E8=88=AC=E3=82=92=E6=8C=87=E3=81=99=E3=81=9F?= =?UTF-8?q?=E3=82=81=E3=80=81=E9=9B=BB=E6=B0=97=E3=82=A8=E3=83=8D=E3=83=AB?= =?UTF-8?q?=E3=82=AE=E3=83=BC=E3=81=AE=E3=81=BF=E3=82=92=E6=8C=87=E3=81=99?= =?UTF-8?q?electricity=E3=81=AB=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...er.rb => electricity_prices_controller.rb} | 4 ++-- ...e.rb => electricity_charges_basic_rate.rb} | 2 +- ...e.rb => electricity_charges_usage_rate.rb} | 8 +++---- .../challenge/app/models/plan.rb | 22 +++++++++---------- .../challenge/app/models/provider.rb | 6 ++--- .../electricity_price_calculate_service.rb | 11 ++++++++++ .../energy_price_calculate_service.rb | 11 ---------- .../challenge/config/routes.rb | 2 +- ..._create_electricity_charges_basic_rates.rb | 13 +++++++++++ ...91545_create_energy_charges_basic_rates.rb | 13 ----------- ...create_electricity_charges_usage_rates.rb} | 4 ++-- serverside_challenge_2/challenge/db/schema.rb | 14 ++++++------ .../challenge/db/seeds/provider_plan_rates.rb | 4 ++-- ... => electricity_prices_controller_test.rb} | 2 +- ...ml => electricity_charges_basic_rates.yml} | 0 ...ml => electricity_charges_usage_rates.yml} | 0 ...=> electricity_charges_basic_rate_test.rb} | 2 +- ...=> electricity_charges_usage_rate_test.rb} | 2 +- 18 files changed, 60 insertions(+), 60 deletions(-) rename serverside_challenge_2/challenge/app/controllers/{energy_prices_controller.rb => electricity_prices_controller.rb} (61%) rename serverside_challenge_2/challenge/app/models/{energy_charges_basic_rate.rb => electricity_charges_basic_rate.rb} (71%) rename serverside_challenge_2/challenge/app/models/{energy_charges_usage_rate.rb => electricity_charges_usage_rate.rb} (71%) create mode 100644 serverside_challenge_2/challenge/app/services/electricity_price_calculate_service.rb delete mode 100644 serverside_challenge_2/challenge/app/services/energy_price_calculate_service.rb create mode 100644 serverside_challenge_2/challenge/db/migrate/20250311091545_create_electricity_charges_basic_rates.rb delete mode 100644 serverside_challenge_2/challenge/db/migrate/20250311091545_create_energy_charges_basic_rates.rb rename serverside_challenge_2/challenge/db/migrate/{20250311091629_create_energy_charges_usage_rates.rb => 20250311091629_create_electricity_charges_usage_rates.rb} (64%) rename serverside_challenge_2/challenge/test/controllers/{energy_prices_controller_test.rb => electricity_prices_controller_test.rb} (51%) rename serverside_challenge_2/challenge/test/fixtures/{energy_charges_basic_rates.yml => electricity_charges_basic_rates.yml} (100%) rename serverside_challenge_2/challenge/test/fixtures/{energy_charges_usage_rates.yml => electricity_charges_usage_rates.yml} (100%) rename serverside_challenge_2/challenge/test/models/{energy_charges_basic_rate_test.rb => electricity_charges_basic_rate_test.rb} (54%) rename serverside_challenge_2/challenge/test/models/{energy_charges_usage_rate_test.rb => electricity_charges_usage_rate_test.rb} (54%) diff --git a/serverside_challenge_2/challenge/app/controllers/energy_prices_controller.rb b/serverside_challenge_2/challenge/app/controllers/electricity_prices_controller.rb similarity index 61% rename from serverside_challenge_2/challenge/app/controllers/energy_prices_controller.rb rename to serverside_challenge_2/challenge/app/controllers/electricity_prices_controller.rb index 936b9f54b..2d5e850e1 100644 --- a/serverside_challenge_2/challenge/app/controllers/energy_prices_controller.rb +++ b/serverside_challenge_2/challenge/app/controllers/electricity_prices_controller.rb @@ -1,8 +1,8 @@ -class EnergyPricesController < ApplicationController +class ElectricityPricesController < ApplicationController def index ampere = Integer(index_params[:ampere]) usage = Integer(index_params[:usage]) - plans = EnergyPriceCalculateService.new(ampere, usage).calc + plans = ElectricityPriceCalculateService.new(ampere, usage).calc render json: plans end diff --git a/serverside_challenge_2/challenge/app/models/energy_charges_basic_rate.rb b/serverside_challenge_2/challenge/app/models/electricity_charges_basic_rate.rb similarity index 71% rename from serverside_challenge_2/challenge/app/models/energy_charges_basic_rate.rb rename to serverside_challenge_2/challenge/app/models/electricity_charges_basic_rate.rb index d1cfabfc6..5c08c3cb7 100644 --- a/serverside_challenge_2/challenge/app/models/energy_charges_basic_rate.rb +++ b/serverside_challenge_2/challenge/app/models/electricity_charges_basic_rate.rb @@ -1,4 +1,4 @@ -class EnergyChargesBasicRate < ApplicationRecord +class ElectricityChargesBasicRate < ApplicationRecord belongs_to :plan validates :basic_rate, presence: true diff --git a/serverside_challenge_2/challenge/app/models/energy_charges_usage_rate.rb b/serverside_challenge_2/challenge/app/models/electricity_charges_usage_rate.rb similarity index 71% rename from serverside_challenge_2/challenge/app/models/energy_charges_usage_rate.rb rename to serverside_challenge_2/challenge/app/models/electricity_charges_usage_rate.rb index 4eb334bec..256a361e0 100644 --- a/serverside_challenge_2/challenge/app/models/energy_charges_usage_rate.rb +++ b/serverside_challenge_2/challenge/app/models/electricity_charges_usage_rate.rb @@ -1,4 +1,4 @@ -class EnergyChargesUsageRate < ApplicationRecord +class ElectricityChargesUsageRate < ApplicationRecord belongs_to :plan validates :min_usage, :unit_rate, presence: true @@ -10,9 +10,9 @@ class EnergyChargesUsageRate < ApplicationRecord def validate_usage_ranges # usageがplan内で重複していないかを確認する - energy_chages_usage_rates = EnergyChargesUsageRate.where(plan: plan).order(:min_usage) + electricity_chages_usage_rates = ElectricityChargesUsageRate.where(plan: plan).order(:min_usage) before_max_usage = nil - energy_chages_usage_rates.each do |rates| + electricity_chages_usage_rates.each do |rates| if before_max_usage.present? && rates.min_usage <= before_max_usage errors.add(:base, '同一プラン内で使用量の範囲が他のレコードと重複しています') break @@ -21,7 +21,7 @@ def validate_usage_ranges before_max_usage = rates.max_usage end - if energy_chages_usage_rates.where(max_usage: nil).count > 1 + if electricity_chages_usage_rates.where(max_usage: nil).count > 1 errors.add(:base, '同一プラン内で使用量上限がない項目が2つ以上存在します') end end diff --git a/serverside_challenge_2/challenge/app/models/plan.rb b/serverside_challenge_2/challenge/app/models/plan.rb index e7d09fb4b..723fff785 100644 --- a/serverside_challenge_2/challenge/app/models/plan.rb +++ b/serverside_challenge_2/challenge/app/models/plan.rb @@ -1,30 +1,30 @@ class Plan < ApplicationRecord belongs_to :provider - has_many :energy_charges_basic_rates, dependent: :destroy - has_many :energy_charges_usage_rates, dependent: :destroy + has_many :electricity_charges_basic_rates, dependent: :destroy + has_many :electricity_charges_usage_rates, dependent: :destroy validates :name, presence: true, uniqueness: { scope: :provider_id } - def energy_price(ampere, usage) + def electricity_price(ampere, usage) # 基本料金、もしくは従量料金がなければ計算不能のためnilを返却する - energy_charges_basic_rate = energy_charges_basic_rates.find_by(ampere: ampere) - return nil if energy_charges_basic_rate.nil? + electricity_charges_basic_rate = electricity_charges_basic_rates.find_by(ampere: ampere) + return nil if electricity_charges_basic_rate.nil? - energy_charges_usage_rate = energy_charges_usage_rate_from_usage(usage) - return nil if energy_charges_usage_rate.nil? + electricity_charges_usage_rate = electricity_charges_usage_rate_from_usage(usage) + return nil if electricity_charges_usage_rate.nil? # 基本料金(契約アンペア数(A)に対応する基本料金(円))の計算 - basic_price = energy_charges_basic_rate.basic_rate + basic_price = electricity_charges_basic_rate.basic_rate # 従量料金(電気使用量(kWh) * 従量料金単価(円/kWh))の計算 - usage_price = usage * energy_charges_usage_rate.unit_rate + usage_price = usage * electricity_charges_usage_rate.unit_rate # 電気料金(基本料金 + 従量料金)を返却 basic_price + usage_price end - def energy_charges_usage_rate_from_usage(usage) - energy_charges_usage_rates + def electricity_charges_usage_rate_from_usage(usage) + electricity_charges_usage_rates .where('min_usage <= :usage AND (max_usage IS NULL OR max_usage >= :usage)', usage: usage) .order(:min_usage) .first diff --git a/serverside_challenge_2/challenge/app/models/provider.rb b/serverside_challenge_2/challenge/app/models/provider.rb index 5c16fca95..a998b1c8a 100644 --- a/serverside_challenge_2/challenge/app/models/provider.rb +++ b/serverside_challenge_2/challenge/app/models/provider.rb @@ -3,14 +3,14 @@ class Provider < ApplicationRecord validates :name, presence: true, uniqueness: true - def energy_prices(ampere, usage) + def electricity_prices(ampere, usage) plans.map do |plan| - price = plan.energy_price(ampere, usage) + price = plan.electricity_price(ampere, usage) if price { provider_name: name, plan_name: plan.name, - price: plan.energy_price(ampere, usage) + price: plan.electricity_price(ampere, usage) } end end.compact diff --git a/serverside_challenge_2/challenge/app/services/electricity_price_calculate_service.rb b/serverside_challenge_2/challenge/app/services/electricity_price_calculate_service.rb new file mode 100644 index 000000000..931da47b3 --- /dev/null +++ b/serverside_challenge_2/challenge/app/services/electricity_price_calculate_service.rb @@ -0,0 +1,11 @@ +class ElectricityPriceCalculateService + def initialize(ampere, usage) + @ampere = ampere + @usage = usage + end + + def calc + Provider.order(:id).map{|provider| provider.electricity_prices(@ampere, @usage)}.flatten + end +end + diff --git a/serverside_challenge_2/challenge/app/services/energy_price_calculate_service.rb b/serverside_challenge_2/challenge/app/services/energy_price_calculate_service.rb deleted file mode 100644 index 976384731..000000000 --- a/serverside_challenge_2/challenge/app/services/energy_price_calculate_service.rb +++ /dev/null @@ -1,11 +0,0 @@ -class EnergyPriceCalculateService - def initialize(ampere, usage) - @ampere = ampere - @usage = usage - end - - def calc - Provider.order(:id).map{|provider| provider.energy_prices(@ampere, @usage)}.flatten - end -end - diff --git a/serverside_challenge_2/challenge/config/routes.rb b/serverside_challenge_2/challenge/config/routes.rb index cdea3f147..7f4e72405 100644 --- a/serverside_challenge_2/challenge/config/routes.rb +++ b/serverside_challenge_2/challenge/config/routes.rb @@ -4,5 +4,5 @@ # Defines the root path route ("/") # root "articles#index" - resources :energy_prices, only: [:index] + resources :electricity_prices, only: [:index] end diff --git a/serverside_challenge_2/challenge/db/migrate/20250311091545_create_electricity_charges_basic_rates.rb b/serverside_challenge_2/challenge/db/migrate/20250311091545_create_electricity_charges_basic_rates.rb new file mode 100644 index 000000000..9d36dd74b --- /dev/null +++ b/serverside_challenge_2/challenge/db/migrate/20250311091545_create_electricity_charges_basic_rates.rb @@ -0,0 +1,13 @@ +class CreateElectricityChargesBasicRates < ActiveRecord::Migration[7.0] + def change + create_table :electricity_charges_basic_rates, comment: 'プラン毎の電気基本料金を格納する' do |t| + t.references :plan, null: false, foreign_key: true + t.integer :ampere, null: false, comment: '契約アンペア数(A)' + t.decimal :basic_rate, null: false, comment: '基本料金(円)' + + t.timestamps + end + + add_index :electricity_charges_basic_rates, [:plan_id, :ampere], unique: true + end +end diff --git a/serverside_challenge_2/challenge/db/migrate/20250311091545_create_energy_charges_basic_rates.rb b/serverside_challenge_2/challenge/db/migrate/20250311091545_create_energy_charges_basic_rates.rb deleted file mode 100644 index 78f884794..000000000 --- a/serverside_challenge_2/challenge/db/migrate/20250311091545_create_energy_charges_basic_rates.rb +++ /dev/null @@ -1,13 +0,0 @@ -class CreateEnergyChargesBasicRates < ActiveRecord::Migration[7.0] - def change - create_table :energy_charges_basic_rates, comment: 'プラン毎の基本料金を格納する' do |t| - t.references :plan, null: false, foreign_key: true - t.integer :ampere, null: false, comment: '契約アンペア数(A)' - t.decimal :basic_rate, null: false, comment: '基本料金(円)' - - t.timestamps - end - - add_index :energy_charges_basic_rates, [:plan_id, :ampere], unique: true - end -end diff --git a/serverside_challenge_2/challenge/db/migrate/20250311091629_create_energy_charges_usage_rates.rb b/serverside_challenge_2/challenge/db/migrate/20250311091629_create_electricity_charges_usage_rates.rb similarity index 64% rename from serverside_challenge_2/challenge/db/migrate/20250311091629_create_energy_charges_usage_rates.rb rename to serverside_challenge_2/challenge/db/migrate/20250311091629_create_electricity_charges_usage_rates.rb index b58b71101..cd0978720 100644 --- a/serverside_challenge_2/challenge/db/migrate/20250311091629_create_energy_charges_usage_rates.rb +++ b/serverside_challenge_2/challenge/db/migrate/20250311091629_create_electricity_charges_usage_rates.rb @@ -1,6 +1,6 @@ -class CreateEnergyChargesUsageRates < ActiveRecord::Migration[7.0] +class CreateElectricityChargesUsageRates < ActiveRecord::Migration[7.0] def change - create_table :energy_charges_usage_rates, comment: 'プラン毎の従量料金を格納する' do |t| + create_table :electricity_charges_usage_rates, comment: 'プラン毎の電気従量料金を格納する' do |t| t.references :plan, null: false, foreign_key: true t.integer :min_usage, null: false, comment: '電気使用量(kWh)の下限値' t.integer :max_usage, comment: '電気使用量(kWh)の上限値' diff --git a/serverside_challenge_2/challenge/db/schema.rb b/serverside_challenge_2/challenge/db/schema.rb index 5ef3deabc..a98300b0b 100644 --- a/serverside_challenge_2/challenge/db/schema.rb +++ b/serverside_challenge_2/challenge/db/schema.rb @@ -14,24 +14,24 @@ # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" - create_table "energy_charges_basic_rates", comment: "プラン毎の基本料金を格納する", force: :cascade do |t| + create_table "electricity_charges_basic_rates", comment: "プラン毎の電気基本料金を格納する", force: :cascade do |t| t.bigint "plan_id", null: false t.integer "ampere", null: false, comment: "契約アンペア数(A)" t.decimal "basic_rate", null: false, comment: "基本料金(円)" t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.index ["plan_id", "ampere"], name: "index_energy_charges_basic_rates_on_plan_id_and_ampere", unique: true - t.index ["plan_id"], name: "index_energy_charges_basic_rates_on_plan_id" + t.index ["plan_id", "ampere"], name: "index_electricity_charges_basic_rates_on_plan_id_and_ampere", unique: true + t.index ["plan_id"], name: "index_electricity_charges_basic_rates_on_plan_id" end - create_table "energy_charges_usage_rates", comment: "プラン毎の従量料金を格納する", force: :cascade do |t| + create_table "electricity_charges_usage_rates", comment: "プラン毎の電気従量料金を格納する", force: :cascade do |t| t.bigint "plan_id", null: false t.integer "min_usage", null: false, comment: "電気使用量(kWh)の下限値" t.integer "max_usage", comment: "電気使用量(kWh)の上限値" t.decimal "unit_rate", null: false, comment: "従量料金単価(円/kWh)" t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.index ["plan_id"], name: "index_energy_charges_usage_rates_on_plan_id" + t.index ["plan_id"], name: "index_electricity_charges_usage_rates_on_plan_id" end create_table "plans", comment: "各電力会社毎のプランを格納する", force: :cascade do |t| @@ -50,7 +50,7 @@ t.index ["name"], name: "index_providers_on_name", unique: true end - add_foreign_key "energy_charges_basic_rates", "plans" - add_foreign_key "energy_charges_usage_rates", "plans" + add_foreign_key "electricity_charges_basic_rates", "plans" + add_foreign_key "electricity_charges_usage_rates", "plans" add_foreign_key "plans", "providers" end diff --git a/serverside_challenge_2/challenge/db/seeds/provider_plan_rates.rb b/serverside_challenge_2/challenge/db/seeds/provider_plan_rates.rb index 7e180bf41..0c5a2fb49 100644 --- a/serverside_challenge_2/challenge/db/seeds/provider_plan_rates.rb +++ b/serverside_challenge_2/challenge/db/seeds/provider_plan_rates.rb @@ -11,7 +11,7 @@ def create_provider_and_plans(provider_name, plans_data) def create_basic_rates(plan, rates) rates.each do |rate_data| - EnergyChargesBasicRate.find_or_create_by!( + ElectricityChargesBasicRate.find_or_create_by!( plan: plan, ampere: rate_data[:ampere] ) do |rate| @@ -22,7 +22,7 @@ def create_basic_rates(plan, rates) def create_usage_rates(plan, rates) rates.each do |rate_data| - EnergyChargesUsageRate.find_or_create_by!( + ElectricityChargesUsageRate.find_or_create_by!( plan: plan, min_usage: rate_data[:min_usage], max_usage: rate_data[:max_usage] diff --git a/serverside_challenge_2/challenge/test/controllers/energy_prices_controller_test.rb b/serverside_challenge_2/challenge/test/controllers/electricity_prices_controller_test.rb similarity index 51% rename from serverside_challenge_2/challenge/test/controllers/energy_prices_controller_test.rb rename to serverside_challenge_2/challenge/test/controllers/electricity_prices_controller_test.rb index b7152d1b7..cc5ded0f8 100644 --- a/serverside_challenge_2/challenge/test/controllers/energy_prices_controller_test.rb +++ b/serverside_challenge_2/challenge/test/controllers/electricity_prices_controller_test.rb @@ -1,6 +1,6 @@ require "test_helper" -class EnergyPricesControllerTest < ActionDispatch::IntegrationTest +class ElectricityPricesControllerTest < ActionDispatch::IntegrationTest # test "the truth" do # assert true # end diff --git a/serverside_challenge_2/challenge/test/fixtures/energy_charges_basic_rates.yml b/serverside_challenge_2/challenge/test/fixtures/electricity_charges_basic_rates.yml similarity index 100% rename from serverside_challenge_2/challenge/test/fixtures/energy_charges_basic_rates.yml rename to serverside_challenge_2/challenge/test/fixtures/electricity_charges_basic_rates.yml diff --git a/serverside_challenge_2/challenge/test/fixtures/energy_charges_usage_rates.yml b/serverside_challenge_2/challenge/test/fixtures/electricity_charges_usage_rates.yml similarity index 100% rename from serverside_challenge_2/challenge/test/fixtures/energy_charges_usage_rates.yml rename to serverside_challenge_2/challenge/test/fixtures/electricity_charges_usage_rates.yml diff --git a/serverside_challenge_2/challenge/test/models/energy_charges_basic_rate_test.rb b/serverside_challenge_2/challenge/test/models/electricity_charges_basic_rate_test.rb similarity index 54% rename from serverside_challenge_2/challenge/test/models/energy_charges_basic_rate_test.rb rename to serverside_challenge_2/challenge/test/models/electricity_charges_basic_rate_test.rb index 643c7ae24..14129e683 100644 --- a/serverside_challenge_2/challenge/test/models/energy_charges_basic_rate_test.rb +++ b/serverside_challenge_2/challenge/test/models/electricity_charges_basic_rate_test.rb @@ -1,6 +1,6 @@ require "test_helper" -class EnergyChargesBasicRateTest < ActiveSupport::TestCase +class ElectricityChargesBasicRateTest < ActiveSupport::TestCase # test "the truth" do # assert true # end diff --git a/serverside_challenge_2/challenge/test/models/energy_charges_usage_rate_test.rb b/serverside_challenge_2/challenge/test/models/electricity_charges_usage_rate_test.rb similarity index 54% rename from serverside_challenge_2/challenge/test/models/energy_charges_usage_rate_test.rb rename to serverside_challenge_2/challenge/test/models/electricity_charges_usage_rate_test.rb index 2b0a85705..4589388b8 100644 --- a/serverside_challenge_2/challenge/test/models/energy_charges_usage_rate_test.rb +++ b/serverside_challenge_2/challenge/test/models/electricity_charges_usage_rate_test.rb @@ -1,6 +1,6 @@ require "test_helper" -class EnergyChargesUsageRateTest < ActiveSupport::TestCase +class ElectricityChargesUsageRateTest < ActiveSupport::TestCase # test "the truth" do # assert true # end From 3479ad4feaee8932c9718cdf6340ca3308e74cca Mon Sep 17 00:00:00 2001 From: yoshiro fujimaki Date: Wed, 12 Mar 2025 09:08:48 +0900 Subject: [PATCH 04/19] =?UTF-8?q?=E4=BE=8B=E5=A4=96=E5=87=A6=E7=90=86?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0=E3=80=82development=E3=81=A7?= =?UTF-8?q?=E3=81=AElog=20format=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/controllers/application_controller.rb | 9 +++++++++ .../controllers/electricity_prices_controller.rb | 15 +++++++++++++++ .../challenge/config/environments/development.rb | 1 + 3 files changed, 25 insertions(+) diff --git a/serverside_challenge_2/challenge/app/controllers/application_controller.rb b/serverside_challenge_2/challenge/app/controllers/application_controller.rb index 4ac8823b0..7e80cf50a 100644 --- a/serverside_challenge_2/challenge/app/controllers/application_controller.rb +++ b/serverside_challenge_2/challenge/app/controllers/application_controller.rb @@ -1,2 +1,11 @@ class ApplicationController < ActionController::API + rescue_from StandardError, with: :handle_unexpected_error + + private + + def handle_unexpected_error(exception) + Rails.logger.error "予期しないエラーが発生しました: #{exception.message}" + Rails.logger.error exception.backtrace.join('\n') + render json: { error: '予期しないエラーが発生しました' }, status: :internal_server_error + end end diff --git a/serverside_challenge_2/challenge/app/controllers/electricity_prices_controller.rb b/serverside_challenge_2/challenge/app/controllers/electricity_prices_controller.rb index 2d5e850e1..ac6641c80 100644 --- a/serverside_challenge_2/challenge/app/controllers/electricity_prices_controller.rb +++ b/serverside_challenge_2/challenge/app/controllers/electricity_prices_controller.rb @@ -1,9 +1,24 @@ class ElectricityPricesController < ApplicationController + ALLOWED_AMPERES = [10, 15, 20, 30, 40, 50, 60] + def index ampere = Integer(index_params[:ampere]) usage = Integer(index_params[:usage]) + + unless ALLOWED_AMPERES.include?(ampere) + render json: { error: "契約アンペア数は #{ALLOWED_AMPERES.join('/')} のいずれかを指定してください" }, status: :bad_request + return + end + + if usage < 0 + render json: { error: '使用量は 0 以上の整数を指定してください' }, status: :bad_request + return + end + plans = ElectricityPriceCalculateService.new(ampere, usage).calc render json: plans + rescue ArgumentError + render json: { error: 'アンペア数と使用量は整数で指定してください' }, status: :bad_request end private diff --git a/serverside_challenge_2/challenge/config/environments/development.rb b/serverside_challenge_2/challenge/config/environments/development.rb index 3d6b07360..760f33119 100644 --- a/serverside_challenge_2/challenge/config/environments/development.rb +++ b/serverside_challenge_2/challenge/config/environments/development.rb @@ -40,6 +40,7 @@ # Print deprecation notices to the Rails logger. config.active_support.deprecation = :log + config.log_formatter = ::Logger::Formatter.new # Raise exceptions for disallowed deprecations. config.active_support.disallowed_deprecation = :raise From 4c4c8e7fb87fbf50f4c6d014e31433e3c3213604 Mon Sep 17 00:00:00 2001 From: yoshiro fujimaki Date: Wed, 12 Mar 2025 14:09:41 +0900 Subject: [PATCH 05/19] =?UTF-8?q?controller=E3=81=AE=E3=83=86=E3=82=B9?= =?UTF-8?q?=E3=83=88=E3=82=92=E8=BF=BD=E5=8A=A0=E3=80=81rubocop,=20rspec?= =?UTF-8?q?=E3=82=92=E5=B0=8E=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- serverside_challenge_2/challenge/.rspec | 2 + serverside_challenge_2/challenge/.rubocop.yml | 8 ++ serverside_challenge_2/challenge/Gemfile | 22 +-- serverside_challenge_2/challenge/Gemfile.lock | 59 ++++++++ serverside_challenge_2/challenge/Rakefile | 4 +- .../app/channels/application_cable/channel.rb | 2 + .../channels/application_cable/connection.rb | 2 + .../app/controllers/application_controller.rb | 2 + .../electricity_prices_controller.rb | 14 +- .../challenge/app/jobs/application_job.rb | 2 + .../app/mailers/application_mailer.rb | 6 +- .../app/models/application_record.rb | 2 + .../models/electricity_charges_basic_rate.rb | 2 + .../models/electricity_charges_usage_rate.rb | 8 +- .../challenge/app/models/plan.rb | 2 + .../challenge/app/models/provider.rb | 16 ++- .../electricity_price_calculate_service.rb | 5 +- serverside_challenge_2/challenge/bin/rails | 8 +- serverside_challenge_2/challenge/bin/rake | 6 +- serverside_challenge_2/challenge/bin/setup | 18 +-- serverside_challenge_2/challenge/config.ru | 4 +- .../challenge/config/application.rb | 6 +- .../challenge/config/boot.rb | 8 +- .../challenge/config/environment.rb | 4 +- .../config/environments/development.rb | 9 +- .../config/environments/production.rb | 14 +- .../challenge/config/environments/test.rb | 8 +- .../challenge/config/initializers/cors.rb | 2 + .../initializers/filter_parameter_logging.rb | 6 +- .../config/initializers/inflections.rb | 2 + .../challenge/config/puma.rb | 14 +- .../challenge/config/routes.rb | 2 + .../20250311091328_create_providers.rb | 2 + .../db/migrate/20250311091421_create_plans.rb | 4 +- ..._create_electricity_charges_basic_rates.rb | 4 +- ..._create_electricity_charges_usage_rates.rb | 2 + serverside_challenge_2/challenge/db/schema.rb | 68 ++++----- serverside_challenge_2/challenge/db/seeds.rb | 2 + .../challenge/db/seeds/provider_plan_rates.rb | 134 +++++++++--------- .../challenge/spec/rails_helper.rb | 72 ++++++++++ .../electricity_prices_controller_spec.rb | 111 +++++++++++++++ .../challenge/spec/spec_helper.rb | 94 ++++++++++++ .../application_cable/connection_test.rb | 11 -- .../challenge/test/controllers/.keep | 0 .../electricity_prices_controller_test.rb | 7 - .../electricity_charges_basic_rates.yml | 11 -- .../electricity_charges_usage_rates.yml | 13 -- .../challenge/test/fixtures/files/.keep | 0 .../challenge/test/fixtures/plans.yml | 9 -- .../challenge/test/fixtures/provider.yml | 7 - .../challenge/test/integration/.keep | 0 .../challenge/test/mailers/.keep | 0 .../challenge/test/models/.keep | 0 .../electricity_charges_basic_rate_test.rb | 7 - .../electricity_charges_usage_rate_test.rb | 7 - .../challenge/test/models/plan_test.rb | 7 - .../challenge/test/models/provider_test.rb | 7 - .../challenge/test/test_helper.rb | 13 -- 58 files changed, 591 insertions(+), 270 deletions(-) create mode 100644 serverside_challenge_2/challenge/.rspec create mode 100644 serverside_challenge_2/challenge/.rubocop.yml create mode 100644 serverside_challenge_2/challenge/spec/rails_helper.rb create mode 100644 serverside_challenge_2/challenge/spec/requests/electricity_prices_controller_spec.rb create mode 100644 serverside_challenge_2/challenge/spec/spec_helper.rb delete mode 100644 serverside_challenge_2/challenge/test/channels/application_cable/connection_test.rb delete mode 100644 serverside_challenge_2/challenge/test/controllers/.keep delete mode 100644 serverside_challenge_2/challenge/test/controllers/electricity_prices_controller_test.rb delete mode 100644 serverside_challenge_2/challenge/test/fixtures/electricity_charges_basic_rates.yml delete mode 100644 serverside_challenge_2/challenge/test/fixtures/electricity_charges_usage_rates.yml delete mode 100644 serverside_challenge_2/challenge/test/fixtures/files/.keep delete mode 100644 serverside_challenge_2/challenge/test/fixtures/plans.yml delete mode 100644 serverside_challenge_2/challenge/test/fixtures/provider.yml delete mode 100644 serverside_challenge_2/challenge/test/integration/.keep delete mode 100644 serverside_challenge_2/challenge/test/mailers/.keep delete mode 100644 serverside_challenge_2/challenge/test/models/.keep delete mode 100644 serverside_challenge_2/challenge/test/models/electricity_charges_basic_rate_test.rb delete mode 100644 serverside_challenge_2/challenge/test/models/electricity_charges_usage_rate_test.rb delete mode 100644 serverside_challenge_2/challenge/test/models/plan_test.rb delete mode 100644 serverside_challenge_2/challenge/test/models/provider_test.rb delete mode 100644 serverside_challenge_2/challenge/test/test_helper.rb diff --git a/serverside_challenge_2/challenge/.rspec b/serverside_challenge_2/challenge/.rspec new file mode 100644 index 000000000..5be63fcb0 --- /dev/null +++ b/serverside_challenge_2/challenge/.rspec @@ -0,0 +1,2 @@ +--require spec_helper +--format documentation diff --git a/serverside_challenge_2/challenge/.rubocop.yml b/serverside_challenge_2/challenge/.rubocop.yml new file mode 100644 index 000000000..a056b9148 --- /dev/null +++ b/serverside_challenge_2/challenge/.rubocop.yml @@ -0,0 +1,8 @@ +Style/Documentation: + Enabled: false +Metrics/MethodLength: + Enabled: false +Metrics/BlockLength: + Enabled: false +Layout/FirstHashElementIndentation: + Enabled: false diff --git a/serverside_challenge_2/challenge/Gemfile b/serverside_challenge_2/challenge/Gemfile index 43bf67fe3..e9222995a 100644 --- a/serverside_challenge_2/challenge/Gemfile +++ b/serverside_challenge_2/challenge/Gemfile @@ -1,16 +1,18 @@ -source "https://rubygems.org" +# frozen_string_literal: true + +source 'https://rubygems.org' git_source(:github) { |repo| "https://github.com/#{repo}.git" } -ruby "3.1.2" +ruby '3.1.2' # Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main" -gem "rails", "~> 7.0.8" +gem 'rails', '~> 7.0.8' # Use postgresql as the database for Active Record -gem "pg", "~> 1.1" +gem 'pg', '~> 1.1' # Use the Puma web server [https://github.com/puma/puma] -gem "puma", "~> 5.0" +gem 'puma', '~> 5.0' # Build JSON APIs with ease [https://github.com/rails/jbuilder] # gem "jbuilder" @@ -25,10 +27,10 @@ gem "puma", "~> 5.0" # gem "bcrypt", "~> 3.1.7" # Windows does not include zoneinfo files, so bundle the tzinfo-data gem -gem "tzinfo-data", platforms: %i[ mingw mswin x64_mingw jruby ] +gem 'tzinfo-data', platforms: %i[mingw mswin x64_mingw jruby] # Reduces boot times through caching; required in config/boot.rb -gem "bootsnap", require: false +gem 'bootsnap', require: false # Use Active Storage variants [https://guides.rubyonrails.org/active_storage_overview.html#transforming-images] # gem "image_processing", "~> 1.2" @@ -38,11 +40,13 @@ gem "bootsnap", require: false group :development, :test do # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem - gem "debug", platforms: %i[ mri mingw x64_mingw ] + gem 'debug', platforms: %i[mri mingw x64_mingw] + gem 'factory_bot_rails' + gem 'rspec-rails', '~> 7.0' end group :development do # Speed up commands on slow machines / big apps [https://github.com/rails/spring] # gem "spring" + gem 'rubocop-rails', require: false end - diff --git a/serverside_challenge_2/challenge/Gemfile.lock b/serverside_challenge_2/challenge/Gemfile.lock index a47fb85f5..d580c7ae2 100644 --- a/serverside_challenge_2/challenge/Gemfile.lock +++ b/serverside_challenge_2/challenge/Gemfile.lock @@ -66,6 +66,7 @@ GEM i18n (>= 1.6, < 2) minitest (>= 5.1) tzinfo (~> 2.0) + ast (2.4.2) bootsnap (1.18.3) msgpack (~> 1.2) builder (3.2.4) @@ -75,7 +76,13 @@ GEM debug (1.9.1) irb (~> 1.10) reline (>= 0.3.8) + diff-lcs (1.6.0) erubi (1.12.0) + factory_bot (6.5.1) + activesupport (>= 6.1.0) + factory_bot_rails (6.4.4) + factory_bot (~> 6.5) + railties (>= 5.0.0) globalid (1.2.1) activesupport (>= 6.1) i18n (1.14.1) @@ -84,6 +91,9 @@ GEM irb (1.11.2) rdoc reline (>= 0.4.2) + json (2.10.1) + language_server-protocol (3.17.0.4) + lint_roller (1.1.0) loofah (2.22.0) crass (~> 1.0.2) nokogiri (>= 1.12.0) @@ -111,6 +121,10 @@ GEM racc (~> 1.4) nokogiri (1.16.2-x86_64-linux) racc (~> 1.4) + parallel (1.26.3) + parser (3.3.7.1) + ast (~> 2.4.1) + racc pg (1.5.4) psych (5.1.2) stringio @@ -148,16 +162,58 @@ GEM rake (>= 12.2) thor (~> 1.0) zeitwerk (~> 2.5) + rainbow (3.1.1) rake (13.1.0) rdoc (6.6.2) psych (>= 4.0.0) + regexp_parser (2.10.0) reline (0.4.2) io-console (~> 0.5) + rspec-core (3.13.3) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.3) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.2) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-rails (7.1.1) + actionpack (>= 7.0) + activesupport (>= 7.0) + railties (>= 7.0) + rspec-core (~> 3.13) + rspec-expectations (~> 3.13) + rspec-mocks (~> 3.13) + rspec-support (~> 3.13) + rspec-support (3.13.2) + rubocop (1.73.2) + json (~> 2.3) + language_server-protocol (~> 3.17.0.2) + lint_roller (~> 1.1.0) + parallel (~> 1.10) + parser (>= 3.3.0.2) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 2.9.3, < 3.0) + rubocop-ast (>= 1.38.0, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 2.4.0, < 4.0) + rubocop-ast (1.38.1) + parser (>= 3.3.1.0) + rubocop-rails (2.30.3) + activesupport (>= 4.2.0) + lint_roller (~> 1.1) + rack (>= 1.1) + rubocop (>= 1.72.1, < 2.0) + rubocop-ast (>= 1.38.0, < 2.0) + ruby-progressbar (1.13.0) stringio (3.1.0) thor (1.3.0) timeout (0.4.1) tzinfo (2.0.6) concurrent-ruby (~> 1.0) + unicode-display_width (3.1.4) + unicode-emoji (~> 4.0, >= 4.0.4) + unicode-emoji (4.0.4) websocket-driver (0.7.6) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) @@ -170,9 +226,12 @@ PLATFORMS DEPENDENCIES bootsnap debug + factory_bot_rails pg (~> 1.1) puma (~> 5.0) rails (~> 7.0.8) + rspec-rails (~> 7.0) + rubocop-rails tzinfo-data RUBY VERSION diff --git a/serverside_challenge_2/challenge/Rakefile b/serverside_challenge_2/challenge/Rakefile index 9a5ea7383..488c551fe 100644 --- a/serverside_challenge_2/challenge/Rakefile +++ b/serverside_challenge_2/challenge/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/serverside_challenge_2/challenge/app/channels/application_cable/channel.rb b/serverside_challenge_2/challenge/app/channels/application_cable/channel.rb index d67269728..9aec23053 100644 --- a/serverside_challenge_2/challenge/app/channels/application_cable/channel.rb +++ b/serverside_challenge_2/challenge/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/serverside_challenge_2/challenge/app/channels/application_cable/connection.rb b/serverside_challenge_2/challenge/app/channels/application_cable/connection.rb index 0ff5442f4..8d6c2a1bf 100644 --- a/serverside_challenge_2/challenge/app/channels/application_cable/connection.rb +++ b/serverside_challenge_2/challenge/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/serverside_challenge_2/challenge/app/controllers/application_controller.rb b/serverside_challenge_2/challenge/app/controllers/application_controller.rb index 7e80cf50a..f0661b705 100644 --- a/serverside_challenge_2/challenge/app/controllers/application_controller.rb +++ b/serverside_challenge_2/challenge/app/controllers/application_controller.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ApplicationController < ActionController::API rescue_from StandardError, with: :handle_unexpected_error diff --git a/serverside_challenge_2/challenge/app/controllers/electricity_prices_controller.rb b/serverside_challenge_2/challenge/app/controllers/electricity_prices_controller.rb index ac6641c80..89d8cecc2 100644 --- a/serverside_challenge_2/challenge/app/controllers/electricity_prices_controller.rb +++ b/serverside_challenge_2/challenge/app/controllers/electricity_prices_controller.rb @@ -1,24 +1,28 @@ +# frozen_string_literal: true + class ElectricityPricesController < ApplicationController - ALLOWED_AMPERES = [10, 15, 20, 30, 40, 50, 60] + ALLOWED_AMPERES = [10, 15, 20, 30, 40, 50, 60].freeze def index ampere = Integer(index_params[:ampere]) usage = Integer(index_params[:usage]) unless ALLOWED_AMPERES.include?(ampere) - render json: { error: "契約アンペア数は #{ALLOWED_AMPERES.join('/')} のいずれかを指定してください" }, status: :bad_request + render json: { error: "アンペア数(ampere)は #{ALLOWED_AMPERES.join('/')} のいずれかを指定してください" }, status: :bad_request return end - if usage < 0 - render json: { error: '使用量は 0 以上の整数を指定してください' }, status: :bad_request + if usage.negative? + render json: { error: '使用量(usage)は 0 以上の整数を指定してください' }, status: :bad_request return end plans = ElectricityPriceCalculateService.new(ampere, usage).calc render json: plans + rescue TypeError + render json: { error: 'アンペア数(ampere)と使用量(usage)の両方が指定されていません' }, status: :bad_request rescue ArgumentError - render json: { error: 'アンペア数と使用量は整数で指定してください' }, status: :bad_request + render json: { error: 'アンペア数(ampere)と使用量(usage)を整数で指定してください' }, status: :bad_request end private diff --git a/serverside_challenge_2/challenge/app/jobs/application_job.rb b/serverside_challenge_2/challenge/app/jobs/application_job.rb index d394c3d10..bef395997 100644 --- a/serverside_challenge_2/challenge/app/jobs/application_job.rb +++ b/serverside_challenge_2/challenge/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/serverside_challenge_2/challenge/app/mailers/application_mailer.rb b/serverside_challenge_2/challenge/app/mailers/application_mailer.rb index 3c34c8148..d84cb6e71 100644 --- a/serverside_challenge_2/challenge/app/mailers/application_mailer.rb +++ b/serverside_challenge_2/challenge/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/serverside_challenge_2/challenge/app/models/application_record.rb b/serverside_challenge_2/challenge/app/models/application_record.rb index b63caeb8a..08dc53798 100644 --- a/serverside_challenge_2/challenge/app/models/application_record.rb +++ b/serverside_challenge_2/challenge/app/models/application_record.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ApplicationRecord < ActiveRecord::Base primary_abstract_class end diff --git a/serverside_challenge_2/challenge/app/models/electricity_charges_basic_rate.rb b/serverside_challenge_2/challenge/app/models/electricity_charges_basic_rate.rb index 5c08c3cb7..74b44d1ac 100644 --- a/serverside_challenge_2/challenge/app/models/electricity_charges_basic_rate.rb +++ b/serverside_challenge_2/challenge/app/models/electricity_charges_basic_rate.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ElectricityChargesBasicRate < ApplicationRecord belongs_to :plan diff --git a/serverside_challenge_2/challenge/app/models/electricity_charges_usage_rate.rb b/serverside_challenge_2/challenge/app/models/electricity_charges_usage_rate.rb index 256a361e0..14e2ee36d 100644 --- a/serverside_challenge_2/challenge/app/models/electricity_charges_usage_rate.rb +++ b/serverside_challenge_2/challenge/app/models/electricity_charges_usage_rate.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ElectricityChargesUsageRate < ApplicationRecord belongs_to :plan @@ -21,8 +23,8 @@ def validate_usage_ranges before_max_usage = rates.max_usage end - if electricity_chages_usage_rates.where(max_usage: nil).count > 1 - errors.add(:base, '同一プラン内で使用量上限がない項目が2つ以上存在します') - end + return unless electricity_chages_usage_rates.where(max_usage: nil).count > 1 + + errors.add(:base, '同一プラン内で使用量上限がない項目が2つ以上存在します') end end diff --git a/serverside_challenge_2/challenge/app/models/plan.rb b/serverside_challenge_2/challenge/app/models/plan.rb index 723fff785..d227d7663 100644 --- a/serverside_challenge_2/challenge/app/models/plan.rb +++ b/serverside_challenge_2/challenge/app/models/plan.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class Plan < ApplicationRecord belongs_to :provider has_many :electricity_charges_basic_rates, dependent: :destroy diff --git a/serverside_challenge_2/challenge/app/models/provider.rb b/serverside_challenge_2/challenge/app/models/provider.rb index a998b1c8a..f05a63f3b 100644 --- a/serverside_challenge_2/challenge/app/models/provider.rb +++ b/serverside_challenge_2/challenge/app/models/provider.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class Provider < ApplicationRecord has_many :plans, dependent: :destroy @@ -6,13 +8,13 @@ class Provider < ApplicationRecord def electricity_prices(ampere, usage) plans.map do |plan| price = plan.electricity_price(ampere, usage) - if price - { - provider_name: name, - plan_name: plan.name, - price: plan.electricity_price(ampere, usage) - } - end + next unless price + + { + provider_name: name, + plan_name: plan.name, + price: plan.electricity_price(ampere, usage) + } end.compact end end diff --git a/serverside_challenge_2/challenge/app/services/electricity_price_calculate_service.rb b/serverside_challenge_2/challenge/app/services/electricity_price_calculate_service.rb index 931da47b3..74399dd01 100644 --- a/serverside_challenge_2/challenge/app/services/electricity_price_calculate_service.rb +++ b/serverside_challenge_2/challenge/app/services/electricity_price_calculate_service.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ElectricityPriceCalculateService def initialize(ampere, usage) @ampere = ampere @@ -5,7 +7,6 @@ def initialize(ampere, usage) end def calc - Provider.order(:id).map{|provider| provider.electricity_prices(@ampere, @usage)}.flatten + Provider.order(:id).map { |provider| provider.electricity_prices(@ampere, @usage) }.flatten end end - diff --git a/serverside_challenge_2/challenge/bin/rails b/serverside_challenge_2/challenge/bin/rails index efc037749..a31728ab9 100755 --- a/serverside_challenge_2/challenge/bin/rails +++ b/serverside_challenge_2/challenge/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/serverside_challenge_2/challenge/bin/rake b/serverside_challenge_2/challenge/bin/rake index 4fbf10b96..c19995500 100755 --- a/serverside_challenge_2/challenge/bin/rake +++ b/serverside_challenge_2/challenge/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/serverside_challenge_2/challenge/bin/setup b/serverside_challenge_2/challenge/bin/setup index ec47b79b3..516b651e3 100755 --- a/serverside_challenge_2/challenge/bin/setup +++ b/serverside_challenge_2/challenge/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/serverside_challenge_2/challenge/config.ru b/serverside_challenge_2/challenge/config.ru index 4a3c09a68..6dc832180 100644 --- a/serverside_challenge_2/challenge/config.ru +++ b/serverside_challenge_2/challenge/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/serverside_challenge_2/challenge/config/application.rb b/serverside_challenge_2/challenge/config/application.rb index b39913bf7..9727c90fb 100644 --- a/serverside_challenge_2/challenge/config/application.rb +++ b/serverside_challenge_2/challenge/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/serverside_challenge_2/challenge/config/boot.rb b/serverside_challenge_2/challenge/config/boot.rb index 988a5ddc4..c04863fa7 100644 --- a/serverside_challenge_2/challenge/config/boot.rb +++ b/serverside_challenge_2/challenge/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/serverside_challenge_2/challenge/config/environment.rb b/serverside_challenge_2/challenge/config/environment.rb index cac531577..d5abe5580 100644 --- a/serverside_challenge_2/challenge/config/environment.rb +++ b/serverside_challenge_2/challenge/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/serverside_challenge_2/challenge/config/environments/development.rb b/serverside_challenge_2/challenge/config/environments/development.rb index 760f33119..5a23d6a20 100644 --- a/serverside_challenge_2/challenge/config/environments/development.rb +++ b/serverside_challenge_2/challenge/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 @@ -54,7 +56,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/serverside_challenge_2/challenge/config/environments/production.rb b/serverside_challenge_2/challenge/config/environments/production.rb index 0376660ba..afded9dbc 100644 --- a/serverside_challenge_2/challenge/config/environments/production.rb +++ b/serverside_challenge_2/challenge/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/serverside_challenge_2/challenge/config/environments/test.rb b/serverside_challenge_2/challenge/config/environments/test.rb index 6ea4d1e70..8f3f63ce7 100644 --- a/serverside_challenge_2/challenge/config/environments/test.rb +++ b/serverside_challenge_2/challenge/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/serverside_challenge_2/challenge/config/initializers/cors.rb b/serverside_challenge_2/challenge/config/initializers/cors.rb index e5a82f162..62875c1a4 100644 --- a/serverside_challenge_2/challenge/config/initializers/cors.rb +++ b/serverside_challenge_2/challenge/config/initializers/cors.rb @@ -1,3 +1,5 @@ +# 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/serverside_challenge_2/challenge/config/initializers/filter_parameter_logging.rb b/serverside_challenge_2/challenge/config/initializers/filter_parameter_logging.rb index adc6568ce..3df77c5be 100644 --- a/serverside_challenge_2/challenge/config/initializers/filter_parameter_logging.rb +++ b/serverside_challenge_2/challenge/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/serverside_challenge_2/challenge/config/initializers/inflections.rb b/serverside_challenge_2/challenge/config/initializers/inflections.rb index 3860f659e..9e049dcc9 100644 --- a/serverside_challenge_2/challenge/config/initializers/inflections.rb +++ b/serverside_challenge_2/challenge/config/initializers/inflections.rb @@ -1,3 +1,5 @@ +# 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/serverside_challenge_2/challenge/config/puma.rb b/serverside_challenge_2/challenge/config/puma.rb index daaf03699..1713441e5 100644 --- a/serverside_challenge_2/challenge/config/puma.rb +++ b/serverside_challenge_2/challenge/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/serverside_challenge_2/challenge/config/routes.rb b/serverside_challenge_2/challenge/config/routes.rb index 7f4e72405..26f56fd47 100644 --- a/serverside_challenge_2/challenge/config/routes.rb +++ b/serverside_challenge_2/challenge/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/serverside_challenge_2/challenge/db/migrate/20250311091328_create_providers.rb b/serverside_challenge_2/challenge/db/migrate/20250311091328_create_providers.rb index a4ebb014f..4db41f597 100644 --- a/serverside_challenge_2/challenge/db/migrate/20250311091328_create_providers.rb +++ b/serverside_challenge_2/challenge/db/migrate/20250311091328_create_providers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class CreateProviders < ActiveRecord::Migration[7.0] def change create_table :providers, comment: '電力会社を格納する' do |t| diff --git a/serverside_challenge_2/challenge/db/migrate/20250311091421_create_plans.rb b/serverside_challenge_2/challenge/db/migrate/20250311091421_create_plans.rb index 688520c4c..b02fe538d 100644 --- a/serverside_challenge_2/challenge/db/migrate/20250311091421_create_plans.rb +++ b/serverside_challenge_2/challenge/db/migrate/20250311091421_create_plans.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class CreatePlans < ActiveRecord::Migration[7.0] def change create_table :plans, comment: '各電力会社毎のプランを格納する' do |t| @@ -7,6 +9,6 @@ def change t.timestamps end - add_index :plans, [:provider_id, :name], unique: true + add_index :plans, %i[provider_id name], unique: true end end diff --git a/serverside_challenge_2/challenge/db/migrate/20250311091545_create_electricity_charges_basic_rates.rb b/serverside_challenge_2/challenge/db/migrate/20250311091545_create_electricity_charges_basic_rates.rb index 9d36dd74b..6abc6f41e 100644 --- a/serverside_challenge_2/challenge/db/migrate/20250311091545_create_electricity_charges_basic_rates.rb +++ b/serverside_challenge_2/challenge/db/migrate/20250311091545_create_electricity_charges_basic_rates.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class CreateElectricityChargesBasicRates < ActiveRecord::Migration[7.0] def change create_table :electricity_charges_basic_rates, comment: 'プラン毎の電気基本料金を格納する' do |t| @@ -8,6 +10,6 @@ def change t.timestamps end - add_index :electricity_charges_basic_rates, [:plan_id, :ampere], unique: true + add_index :electricity_charges_basic_rates, %i[plan_id ampere], unique: true end end diff --git a/serverside_challenge_2/challenge/db/migrate/20250311091629_create_electricity_charges_usage_rates.rb b/serverside_challenge_2/challenge/db/migrate/20250311091629_create_electricity_charges_usage_rates.rb index cd0978720..15db9773e 100644 --- a/serverside_challenge_2/challenge/db/migrate/20250311091629_create_electricity_charges_usage_rates.rb +++ b/serverside_challenge_2/challenge/db/migrate/20250311091629_create_electricity_charges_usage_rates.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class CreateElectricityChargesUsageRates < ActiveRecord::Migration[7.0] def change create_table :electricity_charges_usage_rates, comment: 'プラン毎の電気従量料金を格納する' do |t| diff --git a/serverside_challenge_2/challenge/db/schema.rb b/serverside_challenge_2/challenge/db/schema.rb index a98300b0b..4b70d9df9 100644 --- a/serverside_challenge_2/challenge/db/schema.rb +++ b/serverside_challenge_2/challenge/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,47 +12,47 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2025_03_11_091629) do +ActiveRecord::Schema[7.0].define(version: 20_250_311_091_629) do # These are extensions that must be enabled in order to support this database - enable_extension "plpgsql" + enable_extension 'plpgsql' - create_table "electricity_charges_basic_rates", comment: "プラン毎の電気基本料金を格納する", force: :cascade do |t| - t.bigint "plan_id", null: false - t.integer "ampere", null: false, comment: "契約アンペア数(A)" - t.decimal "basic_rate", null: false, comment: "基本料金(円)" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["plan_id", "ampere"], name: "index_electricity_charges_basic_rates_on_plan_id_and_ampere", unique: true - t.index ["plan_id"], name: "index_electricity_charges_basic_rates_on_plan_id" + create_table 'electricity_charges_basic_rates', comment: 'プラン毎の電気基本料金を格納する', force: :cascade do |t| + t.bigint 'plan_id', null: false + t.integer 'ampere', null: false, comment: '契約アンペア数(A)' + t.decimal 'basic_rate', null: false, comment: '基本料金(円)' + t.datetime 'created_at', null: false + t.datetime 'updated_at', null: false + t.index %w[plan_id ampere], name: 'index_electricity_charges_basic_rates_on_plan_id_and_ampere', unique: true + t.index ['plan_id'], name: 'index_electricity_charges_basic_rates_on_plan_id' end - create_table "electricity_charges_usage_rates", comment: "プラン毎の電気従量料金を格納する", force: :cascade do |t| - t.bigint "plan_id", null: false - t.integer "min_usage", null: false, comment: "電気使用量(kWh)の下限値" - t.integer "max_usage", comment: "電気使用量(kWh)の上限値" - t.decimal "unit_rate", null: false, comment: "従量料金単価(円/kWh)" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["plan_id"], name: "index_electricity_charges_usage_rates_on_plan_id" + create_table 'electricity_charges_usage_rates', comment: 'プラン毎の電気従量料金を格納する', force: :cascade do |t| + t.bigint 'plan_id', null: false + t.integer 'min_usage', null: false, comment: '電気使用量(kWh)の下限値' + t.integer 'max_usage', comment: '電気使用量(kWh)の上限値' + t.decimal 'unit_rate', null: false, comment: '従量料金単価(円/kWh)' + t.datetime 'created_at', null: false + t.datetime 'updated_at', null: false + t.index ['plan_id'], name: 'index_electricity_charges_usage_rates_on_plan_id' end - create_table "plans", comment: "各電力会社毎のプランを格納する", force: :cascade do |t| - t.bigint "provider_id", null: false - t.string "name", null: false, comment: "プラン名" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["provider_id", "name"], name: "index_plans_on_provider_id_and_name", unique: true - t.index ["provider_id"], name: "index_plans_on_provider_id" + create_table 'plans', comment: '各電力会社毎のプランを格納する', force: :cascade do |t| + t.bigint 'provider_id', null: false + t.string 'name', null: false, comment: 'プラン名' + t.datetime 'created_at', null: false + t.datetime 'updated_at', null: false + t.index %w[provider_id name], name: 'index_plans_on_provider_id_and_name', unique: true + t.index ['provider_id'], name: 'index_plans_on_provider_id' end - create_table "providers", comment: "電力会社を格納する", force: :cascade do |t| - t.string "name", null: false, comment: "会社名" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["name"], name: "index_providers_on_name", unique: true + create_table 'providers', comment: '電力会社を格納する', force: :cascade do |t| + t.string 'name', null: false, comment: '会社名' + t.datetime 'created_at', null: false + t.datetime 'updated_at', null: false + t.index ['name'], name: 'index_providers_on_name', unique: true end - add_foreign_key "electricity_charges_basic_rates", "plans" - add_foreign_key "electricity_charges_usage_rates", "plans" - add_foreign_key "plans", "providers" + add_foreign_key 'electricity_charges_basic_rates', 'plans' + add_foreign_key 'electricity_charges_usage_rates', 'plans' + add_foreign_key 'plans', 'providers' end diff --git a/serverside_challenge_2/challenge/db/seeds.rb b/serverside_challenge_2/challenge/db/seeds.rb index bcf7c548f..dcfb8c629 100644 --- a/serverside_challenge_2/challenge/db/seeds.rb +++ b/serverside_challenge_2/challenge/db/seeds.rb @@ -1,3 +1,5 @@ +# 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/serverside_challenge_2/challenge/db/seeds/provider_plan_rates.rb b/serverside_challenge_2/challenge/db/seeds/provider_plan_rates.rb index 0c5a2fb49..14cd52851 100644 --- a/serverside_challenge_2/challenge/db/seeds/provider_plan_rates.rb +++ b/serverside_challenge_2/challenge/db/seeds/provider_plan_rates.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + def create_provider_and_plans(provider_name, plans_data) provider = Provider.find_or_create_by!(name: provider_name) @@ -34,77 +36,77 @@ def create_usage_rates(plan, rates) # 東京電力エナジーパートナー create_provider_and_plans('東京電力エナジーパートナー', [ - { - name: '従量電灯B', - basic_rates: [ - { ampere: 10, basic_rate: 286.00 }, - { ampere: 15, basic_rate: 429.00 }, - { ampere: 20, basic_rate: 572.00 }, - { ampere: 30, basic_rate: 858.00 }, - { ampere: 40, basic_rate: 1144.00 }, - { ampere: 50, basic_rate: 1430.00 }, - { ampere: 60, basic_rate: 1716.00 } - ], - usage_rates: [ - { min_usage: 0, max_usage: 120, unit_rate: 19.88 }, - { min_usage: 121, max_usage: 300, unit_rate: 26.48 }, - { min_usage: 301, max_usage: nil, unit_rate: 30.57 } - ] - }, - { - name: 'スタンダードS', - basic_rates: [ - { ampere: 10, basic_rate: 311.75 }, - { ampere: 15, basic_rate: 467.63 }, - { ampere: 20, basic_rate: 623.50 }, - { ampere: 30, basic_rate: 935.25 }, - { ampere: 40, basic_rate: 1247.00 }, - { ampere: 50, basic_rate: 1558.75 }, - { ampere: 60, basic_rate: 1870.50 } - ], - usage_rates: [ - { min_usage: 0, max_usage: 120, unit_rate: 29.80 }, - { min_usage: 121, max_usage: 300, unit_rate: 36.40 }, - { min_usage: 301, max_usage: nil, unit_rate: 40.49 } - ] - } -]) + { + name: '従量電灯B', + basic_rates: [ + { ampere: 10, basic_rate: 286.00 }, + { ampere: 15, basic_rate: 429.00 }, + { ampere: 20, basic_rate: 572.00 }, + { ampere: 30, basic_rate: 858.00 }, + { ampere: 40, basic_rate: 1144.00 }, + { ampere: 50, basic_rate: 1430.00 }, + { ampere: 60, basic_rate: 1716.00 } + ], + usage_rates: [ + { min_usage: 0, max_usage: 120, unit_rate: 19.88 }, + { min_usage: 121, max_usage: 300, unit_rate: 26.48 }, + { min_usage: 301, max_usage: nil, unit_rate: 30.57 } + ] + }, + { + name: 'スタンダードS', + basic_rates: [ + { ampere: 10, basic_rate: 311.75 }, + { ampere: 15, basic_rate: 467.63 }, + { ampere: 20, basic_rate: 623.50 }, + { ampere: 30, basic_rate: 935.25 }, + { ampere: 40, basic_rate: 1247.00 }, + { ampere: 50, basic_rate: 1558.75 }, + { ampere: 60, basic_rate: 1870.50 } + ], + usage_rates: [ + { min_usage: 0, max_usage: 120, unit_rate: 29.80 }, + { min_usage: 121, max_usage: 300, unit_rate: 36.40 }, + { min_usage: 301, max_usage: nil, unit_rate: 40.49 } + ] + } + ]) # 東京ガス create_provider_and_plans('東京ガス', [ - { - name: 'ずっとも電気1', - basic_rates: [ - { ampere: 30, basic_rate: 858.00 }, - { ampere: 40, basic_rate: 1144.00 }, - { ampere: 50, basic_rate: 1430.00 }, - { ampere: 60, basic_rate: 1716.00 } - ], - usage_rates: [ - { min_usage: 0, max_usage: 140, unit_rate: 23.67 }, - { min_usage: 141, max_usage: 350, unit_rate: 23.88 }, - { min_usage: 351, max_usage: nil, unit_rate: 26.41 } - ] - } -]) + { + name: 'ずっとも電気1', + basic_rates: [ + { ampere: 30, basic_rate: 858.00 }, + { ampere: 40, basic_rate: 1144.00 }, + { ampere: 50, basic_rate: 1430.00 }, + { ampere: 60, basic_rate: 1716.00 } + ], + usage_rates: [ + { min_usage: 0, max_usage: 140, unit_rate: 23.67 }, + { min_usage: 141, max_usage: 350, unit_rate: 23.88 }, + { min_usage: 351, max_usage: nil, unit_rate: 26.41 } + ] + } + ]) # Looopでんき create_provider_and_plans('Looopでんき', [ - { - name: 'おうちプラン', - basic_rates: [ - { ampere: 10, basic_rate: 0.00 }, - { ampere: 15, basic_rate: 0.00 }, - { ampere: 20, basic_rate: 0.00 }, - { ampere: 30, basic_rate: 0.00 }, - { ampere: 40, basic_rate: 0.00 }, - { ampere: 50, basic_rate: 0.00 }, - { ampere: 60, basic_rate: 0.00 } - ], - usage_rates: [ - { min_usage: 0, max_usage: nil, unit_rate: 28.8 } - ] - } -]) + { + name: 'おうちプラン', + basic_rates: [ + { ampere: 10, basic_rate: 0.00 }, + { ampere: 15, basic_rate: 0.00 }, + { ampere: 20, basic_rate: 0.00 }, + { ampere: 30, basic_rate: 0.00 }, + { ampere: 40, basic_rate: 0.00 }, + { ampere: 50, basic_rate: 0.00 }, + { ampere: 60, basic_rate: 0.00 } + ], + usage_rates: [ + { min_usage: 0, max_usage: nil, unit_rate: 28.8 } + ] + } + ]) puts '各電力会社と、そのプラン作成が完了しました' diff --git a/serverside_challenge_2/challenge/spec/rails_helper.rb b/serverside_challenge_2/challenge/spec/rails_helper.rb new file mode 100644 index 000000000..aa65ee868 --- /dev/null +++ b/serverside_challenge_2/challenge/spec/rails_helper.rb @@ -0,0 +1,72 @@ +# 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? +# Uncomment the line below in case you have `--require rails_helper` in the `.rspec` file +# that will avoid rails generators crashing because migrations haven't been run yet +# return unless Rails.env.test? +require 'rspec/rails' +# Add additional requires below this line. Rails is not loaded until this point! + +# Requires supporting ruby files with custom matchers and macros, etc, in +# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are +# run as spec files by default. This means that files in spec/support that end +# in _spec.rb will both be required and run as specs, causing the specs to be +# run twice. It is recommended that you do not name files matching this glob to +# end with _spec.rb. You can configure this pattern with the --pattern +# option on the command line or in ~/.rspec, .rspec or `.rspec-local`. +# +# The following line is provided for convenience purposes. It has the downside +# of increasing the boot-up time by auto-requiring all files in the support +# directory. Alternatively, in the individual `*_spec.rb` files, manually +# require only the support files necessary. +# +# Rails.root.glob('spec/support/**/*.rb').sort_by(&:to_s).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. +begin + ActiveRecord::Migration.maintain_test_schema! +rescue ActiveRecord::PendingMigrationError => e + abort e.to_s.strip +end +RSpec.configure do |config| + # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures + config.fixture_path = Rails.root.join('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 + # instead of true. + config.use_transactional_fixtures = true + + # You can uncomment this line to turn off ActiveRecord support entirely. + # config.use_active_record = false + + # RSpec Rails uses metadata to mix in different behaviours to your tests, + # for example enabling you to call `get` and `post` in request specs. e.g.: + # + # RSpec.describe UsersController, type: :request do + # # ... + # end + # + # The different available types are documented in the features, such as in + # https://rspec.info/features/7-1/rspec-rails + # + # You can also this infer these behaviours automatically by location, e.g. + # /spec/models would pull in the same behaviour as `type: :model` but this + # behaviour is considered legacy and will be removed in a future version. + # + # To enable this behaviour uncomment the line below. + # config.infer_spec_type_from_file_location! + + # Filter lines from Rails gems in backtraces. + config.filter_rails_from_backtrace! + # arbitrary gems may also be filtered via: + # config.filter_gems_from_backtrace("gem name") + + config.include FactoryBot::Syntax::Methods +end diff --git a/serverside_challenge_2/challenge/spec/requests/electricity_prices_controller_spec.rb b/serverside_challenge_2/challenge/spec/requests/electricity_prices_controller_spec.rb new file mode 100644 index 000000000..6114881ad --- /dev/null +++ b/serverside_challenge_2/challenge/spec/requests/electricity_prices_controller_spec.rb @@ -0,0 +1,111 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'ElectricityPrices', type: :request do + describe 'GET /electricity_prices' do + let(:valid_params) { { ampere: 30, usage: 200 } } + let(:invalid_ampere_params) { { ampere: 100, usage: 200 } } + let(:negative_usage_params) { { ampere: 30, usage: -50 } } + let(:string_ampere_params) { { ampere: 'abc', usage: 200 } } + let(:string_usage_params) { { ampere: 30, usage: 'xyz' } } + let(:missing_ampere_params) { { usage: 200 } } + let(:missing_usage_params) { { ampere: 30 } } + + # 正常系 + describe '正常系' do + context '正しいパラメータが指定された場合' do + let(:expected_response) do + [ + { + 'company_name' => '東京電力エナジーパートナー', + 'plan_name' => '従量電灯B', + 'price' => '2274.0' + }, + { + 'company_name' => '東京電力エナジーパートナー', + 'plan_name' => 'スタンダードS', + 'price' => '3291.75' + } + ] + end + + before do + allow(ElectricityPriceCalculateService).to receive(:new) + .and_return(double(calc: expected_response)) + end + + it '成功の応答を返し、レスポンスのボディにサービスで計算された結果が入っている' do + get electricity_prices_url, params: valid_params + expect(response).to have_http_status(:success) + expect(JSON.parse(response.body)).to eq(expected_response) + end + end + end + + # 異常系 + describe '異常系' do + context '無効なアンペア数が指定された場合' do + it 'それに対応するエラー応答を返す' do + get electricity_prices_url, params: invalid_ampere_params + expect(response).to have_http_status(:bad_request) + expect(JSON.parse(response.body)).to eq({ + 'error' => "アンペア数(ampere)は #{ElectricityPricesController::ALLOWED_AMPERES.join('/')} のいずれかを指定してください" + }) + end + end + + context '使用量が負の数の場合' do + it 'それに対応するエラー応答を返す' do + get electricity_prices_url, params: negative_usage_params + expect(response).to have_http_status(:bad_request) + expect(JSON.parse(response.body)).to eq({ 'error' => '使用量(usage)は 0 以上の整数を指定してください' }) + end + end + + context 'アンペア数が文字列の場合' do + it 'それに対応するエラー応答を返す' do + get electricity_prices_url, params: string_ampere_params + expect(response).to have_http_status(:bad_request) + expect(JSON.parse(response.body)).to eq({ 'error' => 'アンペア数(ampere)と使用量(usage)を整数で指定してください' }) + end + end + + context '使用量が文字列の場合' do + it 'それに対応するエラー応答を返す' do + get electricity_prices_url, params: string_usage_params + expect(response).to have_http_status(:bad_request) + expect(JSON.parse(response.body)).to eq({ 'error' => 'アンペア数(ampere)と使用量(usage)を整数で指定してください' }) + end + end + + context 'アンペア数が指定されていない場合' do + it 'それに対応するエラー応答を返す' do + get electricity_prices_url, params: missing_ampere_params + expect(response).to have_http_status(:bad_request) + expect(JSON.parse(response.body)).to eq({ 'error' => 'アンペア数(ampere)と使用量(usage)の両方が指定されていません' }) + end + end + + context '使用量が指定されていない場合' do + it 'それに対応するエラー応答を返す' do + get electricity_prices_url, params: missing_usage_params + expect(response).to have_http_status(:bad_request) + expect(JSON.parse(response.body)).to eq({ 'error' => 'アンペア数(ampere)と使用量(usage)の両方が指定されていません' }) + end + end + + context 'サービス内でエラーが発生した場合' do + before do + allow(ElectricityPriceCalculateService).to receive(:new).and_raise(StandardError) + end + + it '予期しないエラー応答を返す' do + get electricity_prices_url, params: valid_params + expect(response).to have_http_status(:internal_server_error) + expect(JSON.parse(response.body)).to eq({ 'error' => '予期しないエラーが発生しました' }) + end + end + end + end +end diff --git a/serverside_challenge_2/challenge/spec/spec_helper.rb b/serverside_challenge_2/challenge/spec/spec_helper.rb new file mode 100644 index 000000000..409c64b6c --- /dev/null +++ b/serverside_challenge_2/challenge/spec/spec_helper.rb @@ -0,0 +1,94 @@ +# 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 +# this file to always be loaded, without a need to explicitly require it in any +# files. +# +# Given that it is always loaded, you are encouraged to keep this file as +# light-weight as possible. Requiring heavyweight dependencies from this file +# will add to the boot time of your test suite on EVERY test run, even for an +# individual file that may not need all of that loaded. Instead, consider making +# a separate helper file that requires the additional dependencies and performs +# the additional setup, and require it from the spec files that actually need +# it. +# +# See https://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration +RSpec.configure do |config| + # rspec-expectations config goes here. You can use an alternate + # assertion/expectation library such as wrong or the stdlib/minitest + # assertions if you prefer. + config.expect_with :rspec do |expectations| + # This option will default to `true` in RSpec 4. It makes the `description` + # and `failure_message` of custom matchers include text for helper methods + # defined using `chain`, e.g.: + # be_bigger_than(2).and_smaller_than(4).description + # # => "be bigger than 2 and smaller than 4" + # ...rather than: + # # => "be bigger than 2" + expectations.include_chain_clauses_in_custom_matcher_descriptions = true + end + + # rspec-mocks config goes here. You can use an alternate test double + # library (such as bogus or mocha) by changing the `mock_with` option here. + config.mock_with :rspec do |mocks| + # Prevents you from mocking or stubbing a method that does not exist on + # a real object. This is generally recommended, and will default to + # `true` in RSpec 4. + mocks.verify_partial_doubles = true + end + + # This option will default to `:apply_to_host_groups` in RSpec 4 (and will + # have no way to turn it off -- the option exists only for backwards + # compatibility in RSpec 3). It causes shared context metadata to be + # inherited by the metadata hash of host groups and examples, rather than + # 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. + # # 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://rspec.info/features/3-12/rspec-core/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/serverside_challenge_2/challenge/test/channels/application_cable/connection_test.rb b/serverside_challenge_2/challenge/test/channels/application_cable/connection_test.rb deleted file mode 100644 index 800405f15..000000000 --- a/serverside_challenge_2/challenge/test/channels/application_cable/connection_test.rb +++ /dev/null @@ -1,11 +0,0 @@ -require "test_helper" - -class ApplicationCable::ConnectionTest < ActionCable::Connection::TestCase - # test "connects with cookies" do - # cookies.signed[:user_id] = 42 - # - # connect - # - # assert_equal connection.user_id, "42" - # end -end diff --git a/serverside_challenge_2/challenge/test/controllers/.keep b/serverside_challenge_2/challenge/test/controllers/.keep deleted file mode 100644 index e69de29bb..000000000 diff --git a/serverside_challenge_2/challenge/test/controllers/electricity_prices_controller_test.rb b/serverside_challenge_2/challenge/test/controllers/electricity_prices_controller_test.rb deleted file mode 100644 index cc5ded0f8..000000000 --- a/serverside_challenge_2/challenge/test/controllers/electricity_prices_controller_test.rb +++ /dev/null @@ -1,7 +0,0 @@ -require "test_helper" - -class ElectricityPricesControllerTest < ActionDispatch::IntegrationTest - # test "the truth" do - # assert true - # end -end diff --git a/serverside_challenge_2/challenge/test/fixtures/electricity_charges_basic_rates.yml b/serverside_challenge_2/challenge/test/fixtures/electricity_charges_basic_rates.yml deleted file mode 100644 index b16a5dd65..000000000 --- a/serverside_challenge_2/challenge/test/fixtures/electricity_charges_basic_rates.yml +++ /dev/null @@ -1,11 +0,0 @@ -# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html - -one: - plan: one - ampere: 1 - basic_rate: 9.99 - -two: - plan: two - ampere: 1 - basic_rate: 9.99 diff --git a/serverside_challenge_2/challenge/test/fixtures/electricity_charges_usage_rates.yml b/serverside_challenge_2/challenge/test/fixtures/electricity_charges_usage_rates.yml deleted file mode 100644 index 99e07102e..000000000 --- a/serverside_challenge_2/challenge/test/fixtures/electricity_charges_usage_rates.yml +++ /dev/null @@ -1,13 +0,0 @@ -# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html - -one: - plan: one - min_usage: 1 - max_usage: 1 - unit_rate: 9.99 - -two: - plan: two - min_usage: 1 - max_usage: 1 - unit_rate: 9.99 diff --git a/serverside_challenge_2/challenge/test/fixtures/files/.keep b/serverside_challenge_2/challenge/test/fixtures/files/.keep deleted file mode 100644 index e69de29bb..000000000 diff --git a/serverside_challenge_2/challenge/test/fixtures/plans.yml b/serverside_challenge_2/challenge/test/fixtures/plans.yml deleted file mode 100644 index ebbc0b472..000000000 --- a/serverside_challenge_2/challenge/test/fixtures/plans.yml +++ /dev/null @@ -1,9 +0,0 @@ -# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html - -one: - company: one - name: MyString - -two: - company: two - name: MyString diff --git a/serverside_challenge_2/challenge/test/fixtures/provider.yml b/serverside_challenge_2/challenge/test/fixtures/provider.yml deleted file mode 100644 index 7d4122404..000000000 --- a/serverside_challenge_2/challenge/test/fixtures/provider.yml +++ /dev/null @@ -1,7 +0,0 @@ -# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html - -one: - name: MyString - -two: - name: MyString diff --git a/serverside_challenge_2/challenge/test/integration/.keep b/serverside_challenge_2/challenge/test/integration/.keep deleted file mode 100644 index e69de29bb..000000000 diff --git a/serverside_challenge_2/challenge/test/mailers/.keep b/serverside_challenge_2/challenge/test/mailers/.keep deleted file mode 100644 index e69de29bb..000000000 diff --git a/serverside_challenge_2/challenge/test/models/.keep b/serverside_challenge_2/challenge/test/models/.keep deleted file mode 100644 index e69de29bb..000000000 diff --git a/serverside_challenge_2/challenge/test/models/electricity_charges_basic_rate_test.rb b/serverside_challenge_2/challenge/test/models/electricity_charges_basic_rate_test.rb deleted file mode 100644 index 14129e683..000000000 --- a/serverside_challenge_2/challenge/test/models/electricity_charges_basic_rate_test.rb +++ /dev/null @@ -1,7 +0,0 @@ -require "test_helper" - -class ElectricityChargesBasicRateTest < ActiveSupport::TestCase - # test "the truth" do - # assert true - # end -end diff --git a/serverside_challenge_2/challenge/test/models/electricity_charges_usage_rate_test.rb b/serverside_challenge_2/challenge/test/models/electricity_charges_usage_rate_test.rb deleted file mode 100644 index 4589388b8..000000000 --- a/serverside_challenge_2/challenge/test/models/electricity_charges_usage_rate_test.rb +++ /dev/null @@ -1,7 +0,0 @@ -require "test_helper" - -class ElectricityChargesUsageRateTest < ActiveSupport::TestCase - # test "the truth" do - # assert true - # end -end diff --git a/serverside_challenge_2/challenge/test/models/plan_test.rb b/serverside_challenge_2/challenge/test/models/plan_test.rb deleted file mode 100644 index c1b4ca447..000000000 --- a/serverside_challenge_2/challenge/test/models/plan_test.rb +++ /dev/null @@ -1,7 +0,0 @@ -require "test_helper" - -class PlanTest < ActiveSupport::TestCase - # test "the truth" do - # assert true - # end -end diff --git a/serverside_challenge_2/challenge/test/models/provider_test.rb b/serverside_challenge_2/challenge/test/models/provider_test.rb deleted file mode 100644 index 5c62c6c6b..000000000 --- a/serverside_challenge_2/challenge/test/models/provider_test.rb +++ /dev/null @@ -1,7 +0,0 @@ -require "test_helper" - -class ProviderTest < ActiveSupport::TestCase - # test "the truth" do - # assert true - # end -end diff --git a/serverside_challenge_2/challenge/test/test_helper.rb b/serverside_challenge_2/challenge/test/test_helper.rb deleted file mode 100644 index d713e377c..000000000 --- a/serverside_challenge_2/challenge/test/test_helper.rb +++ /dev/null @@ -1,13 +0,0 @@ -ENV["RAILS_ENV"] ||= "test" -require_relative "../config/environment" -require "rails/test_help" - -class ActiveSupport::TestCase - # Run tests in parallel with specified workers - parallelize(workers: :number_of_processors) - - # 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 From faccfdacf4138d3e0805e7445f2d906c6416b8e6 Mon Sep 17 00:00:00 2001 From: yoshiro fujimaki Date: Wed, 12 Mar 2025 19:12:02 +0900 Subject: [PATCH 06/19] =?UTF-8?q?model=E3=81=AE=E3=83=86=E3=82=B9=E3=83=88?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../models/electricity_charges_usage_rate.rb | 38 ++++++++-- .../challenge/app/models/plan.rb | 2 + .../electricity_charges_basic_rates.rb | 9 +++ .../electricity_charges_usage_rates.rb | 28 +++++++ .../challenge/spec/factories/plan.rb | 8 ++ .../challenge/spec/factories/providers.rb | 7 ++ .../electricity_charges_usage_rate_spec.rb | 58 ++++++++++++++ .../challenge/spec/models/plan_spec.rb | 76 +++++++++++++++++++ 8 files changed, 219 insertions(+), 7 deletions(-) create mode 100644 serverside_challenge_2/challenge/spec/factories/electricity_charges_basic_rates.rb create mode 100644 serverside_challenge_2/challenge/spec/factories/electricity_charges_usage_rates.rb create mode 100644 serverside_challenge_2/challenge/spec/factories/plan.rb create mode 100644 serverside_challenge_2/challenge/spec/factories/providers.rb create mode 100644 serverside_challenge_2/challenge/spec/models/electricity_charges_usage_rate_spec.rb create mode 100644 serverside_challenge_2/challenge/spec/models/plan_spec.rb diff --git a/serverside_challenge_2/challenge/app/models/electricity_charges_usage_rate.rb b/serverside_challenge_2/challenge/app/models/electricity_charges_usage_rate.rb index 14e2ee36d..a11b550ba 100644 --- a/serverside_challenge_2/challenge/app/models/electricity_charges_usage_rate.rb +++ b/serverside_challenge_2/challenge/app/models/electricity_charges_usage_rate.rb @@ -11,19 +11,43 @@ class ElectricityChargesUsageRate < ApplicationRecord private def validate_usage_ranges - # usageがplan内で重複していないかを確認する - electricity_chages_usage_rates = ElectricityChargesUsageRate.where(plan: plan).order(:min_usage) - before_max_usage = nil - electricity_chages_usage_rates.each do |rates| - if before_max_usage.present? && rates.min_usage <= before_max_usage + # usageがplan内で重複しないかを確認する + usage_ranges = fetch_existing_usage_ranges + [{ min: min_usage, max: max_usage }] + usage_ranges.sort_by { |usage_range| usage_range[:min] } + # puts usage_ranges + + # 使用量範囲の重複チェック + validate_no_overlap_in_usage_ranges(usage_ranges) + + # 上限なしの範囲が複数存在しないかチェック + validate_single_unbounded_range(usage_ranges) + end + + def fetch_existing_usage_ranges + ElectricityChargesUsageRate + .where(plan: plan) + .order(:min_usage) + .pluck(:min_usage, :max_usage) + .map { |min, max| { min: min, max: max } } + end + + def validate_no_overlap_in_usage_ranges(usage_ranges) + previous_max_usage = nil + + usage_ranges.each do |usage_range| + if previous_max_usage.present? && usage_range[:min] <= previous_max_usage errors.add(:base, '同一プラン内で使用量の範囲が他のレコードと重複しています') break end - before_max_usage = rates.max_usage + previous_max_usage = usage_range[:max] end + end + + def validate_single_unbounded_range(usage_ranges) + unbounded_count = usage_ranges.count { |usage_range| usage_range[:max].nil? } - return unless electricity_chages_usage_rates.where(max_usage: nil).count > 1 + return unless unbounded_count > 1 errors.add(:base, '同一プラン内で使用量上限がない項目が2つ以上存在します') end diff --git a/serverside_challenge_2/challenge/app/models/plan.rb b/serverside_challenge_2/challenge/app/models/plan.rb index d227d7663..693abe8e7 100644 --- a/serverside_challenge_2/challenge/app/models/plan.rb +++ b/serverside_challenge_2/challenge/app/models/plan.rb @@ -25,6 +25,8 @@ def electricity_price(ampere, usage) basic_price + usage_price end + private + def electricity_charges_usage_rate_from_usage(usage) electricity_charges_usage_rates .where('min_usage <= :usage AND (max_usage IS NULL OR max_usage >= :usage)', usage: usage) diff --git a/serverside_challenge_2/challenge/spec/factories/electricity_charges_basic_rates.rb b/serverside_challenge_2/challenge/spec/factories/electricity_charges_basic_rates.rb new file mode 100644 index 000000000..48960f599 --- /dev/null +++ b/serverside_challenge_2/challenge/spec/factories/electricity_charges_basic_rates.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :electricity_charges_basic_rate do + association :plan + ampere { 10 } + basic_rate { BigDecimal('100.00') } + end +end diff --git a/serverside_challenge_2/challenge/spec/factories/electricity_charges_usage_rates.rb b/serverside_challenge_2/challenge/spec/factories/electricity_charges_usage_rates.rb new file mode 100644 index 000000000..a5461de9c --- /dev/null +++ b/serverside_challenge_2/challenge/spec/factories/electricity_charges_usage_rates.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :electricity_charges_usage_rate do + association :plan + min_usage { 0 } + max_usage { 120 } + unit_rate { BigDecimal('10.00') } + + trait :low_usage do + min_usage { 0 } + max_usage { 120 } + unit_rate { 20.0 } + end + + trait :mid_usage do + min_usage { 121 } + max_usage { 300 } + unit_rate { 25.0 } + end + + trait :high_usage do + min_usage { 301 } + max_usage { nil } + unit_rate { 30.0 } + end + end +end diff --git a/serverside_challenge_2/challenge/spec/factories/plan.rb b/serverside_challenge_2/challenge/spec/factories/plan.rb new file mode 100644 index 000000000..11973447a --- /dev/null +++ b/serverside_challenge_2/challenge/spec/factories/plan.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :plan do + association :provider + sequence(:name) { |n| "プラン#{n}" } + end +end diff --git a/serverside_challenge_2/challenge/spec/factories/providers.rb b/serverside_challenge_2/challenge/spec/factories/providers.rb new file mode 100644 index 000000000..9fe3ad2f2 --- /dev/null +++ b/serverside_challenge_2/challenge/spec/factories/providers.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :provider do + sequence(:name) { |n| "プロバイダー#{n}" } + end +end diff --git a/serverside_challenge_2/challenge/spec/models/electricity_charges_usage_rate_spec.rb b/serverside_challenge_2/challenge/spec/models/electricity_charges_usage_rate_spec.rb new file mode 100644 index 000000000..60c9d3e32 --- /dev/null +++ b/serverside_challenge_2/challenge/spec/models/electricity_charges_usage_rate_spec.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe ElectricityChargesUsageRate, type: :model do + let(:provider) { create(:provider) } + let(:plan) { create(:plan, provider: provider) } + + describe 'バリデーション' do + describe '正常系' do + context '使用量の範囲が他と重複していない場合' do + it 'バリデーションが通る' do + rate1 = build(:electricity_charges_usage_rate, plan: plan, min_usage: 0, max_usage: 120) + rate2 = build(:electricity_charges_usage_rate, plan: plan, min_usage: 121, max_usage: 300) + + expect(rate1).to be_valid + expect(rate2).to be_valid + end + end + + context '使用量の上限がnilで、他のデータと重複していない場合' do + it 'バリデーションが通る' do + rate1 = build(:electricity_charges_usage_rate, plan: plan, min_usage: 0, max_usage: 120) + rate2 = build(:electricity_charges_usage_rate, plan: plan, min_usage: 121, max_usage: nil) + + expect(rate1).to be_valid + expect(rate2).to be_valid + end + end + end + + describe '異常系' do + context '同一プラン内で使用量の範囲が重複する場合' do + before do + create(:electricity_charges_usage_rate, plan: plan, min_usage: 0, max_usage: 120) + end + + it 'バリデーションエラーが発生する' do + overlapping_rate = build(:electricity_charges_usage_rate, plan: plan, min_usage: 100, max_usage: 200) + expect(overlapping_rate).to be_invalid + expect(overlapping_rate.errors[:base]).to include('同一プラン内で使用量の範囲が他のレコードと重複しています') + end + end + + context '同一プラン内で使用量上限がnilのデータが複数存在する場合' do + before do + create(:electricity_charges_usage_rate, plan: plan, min_usage: 121, max_usage: nil) + end + + it 'バリデーションエラーが発生する' do + duplicate_max_nil = build(:electricity_charges_usage_rate, plan: plan, min_usage: 301, max_usage: nil) + expect(duplicate_max_nil).to be_invalid + expect(duplicate_max_nil.errors[:base]).to include('同一プラン内で使用量上限がない項目が2つ以上存在します') + end + end + end + end +end diff --git a/serverside_challenge_2/challenge/spec/models/plan_spec.rb b/serverside_challenge_2/challenge/spec/models/plan_spec.rb new file mode 100644 index 000000000..83ec2b56e --- /dev/null +++ b/serverside_challenge_2/challenge/spec/models/plan_spec.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Plan, type: :model do + describe '#electricity_price' do + let(:provider) { create(:provider) } + let(:plan) do + create(:plan, provider: provider, electricity_charges_basic_rates: [], electricity_charges_usage_rates: []) + end + + let!(:basic_rate_20A) do + create(:electricity_charges_basic_rate, plan: plan, ampere: 20, basic_rate: 1000) + end + + let!(:basic_rate_30A) do + create(:electricity_charges_basic_rate, plan: plan, ampere: 30, basic_rate: 1000) + end + + let!(:usage_rate_low) do + create(:electricity_charges_usage_rate, plan: plan, min_usage: 0, max_usage: 120, unit_rate: 20) + end + + let!(:usage_rate_middle) do + create(:electricity_charges_usage_rate, plan: plan, min_usage: 121, max_usage: 300, unit_rate: 25) + end + + let!(:usage_rate_high) do + create(:electricity_charges_usage_rate, plan: plan, min_usage: 301, max_usage: nil, unit_rate: 30) + end + + describe '正常系' do + context 'アンペア数と使用量が条件に合致する場合' do + let(:ampere) { 20 } + let(:usage) { 150 } + + it '基本料金と従量料金の合計を返す' do + expect( + plan.electricity_price(ampere, usage) + ).to eq(basic_rate_20A.basic_rate + (usage * usage_rate_middle.unit_rate)) + end + end + + context '使用量が0の場合' do + let(:ampere) { 30 } + let(:usage) { 0 } + + it '基本料金と従量料金(0円)の合計を返す' do + expect(plan.electricity_price(ampere, usage)).to eq(basic_rate_30A.basic_rate) + end + end + + context '使用量が上限のないレートに該当する場合' do + let(:ampere) { 30 } + let(:usage) { 0 } + + it '基本料金と従量料金の合計を返す' do + expect( + plan.electricity_price(ampere, usage) + ).to eq(basic_rate_30A.basic_rate + (usage * usage_rate_high.unit_rate)) + end + end + end + + describe '異常系' do + context '指定したアンペア数に対応する基本料金が存在しない場合' do + let(:ampere) { 40 } + let(:usage) { 150 } + + it 'nilを返す' do + expect(plan.electricity_price(ampere, usage)).to be_nil + end + end + end + end +end From c72c496cc8c40dfd56915210d46c049d3647deb8 Mon Sep 17 00:00:00 2001 From: yoshiro fujimaki Date: Wed, 12 Mar 2025 21:19:17 +0900 Subject: [PATCH 07/19] =?UTF-8?q?frontend=E3=81=AE=E5=88=9D=E6=9C=9F?= =?UTF-8?q?=E6=A7=8B=E7=AF=89=EF=BC=88helloworld=E3=81=BE=E3=81=A7?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../config/environments/development.rb | 3 + .../challenge/docker-compose.yml | 11 +- .../challenge/frontend/.gitignore | 24 + .../frontend/.vscode/extensions.json | 3 + .../challenge/frontend/Dockerfile | 5 + .../challenge/frontend/README.md | 5 + .../challenge/frontend/index.html | 13 + .../challenge/frontend/package.json | 18 + .../challenge/frontend/public/vite.svg | 1 + .../challenge/frontend/src/App.vue | 30 ++ .../challenge/frontend/src/assets/vue.svg | 1 + .../frontend/src/components/HelloWorld.vue | 43 ++ .../challenge/frontend/src/main.js | 5 + .../challenge/frontend/src/style.css | 79 +++ .../challenge/frontend/vite.config.js | 7 + .../challenge/frontend/yarn.lock | 475 ++++++++++++++++++ 16 files changed, 722 insertions(+), 1 deletion(-) create mode 100644 serverside_challenge_2/challenge/frontend/.gitignore create mode 100644 serverside_challenge_2/challenge/frontend/.vscode/extensions.json create mode 100644 serverside_challenge_2/challenge/frontend/Dockerfile create mode 100644 serverside_challenge_2/challenge/frontend/README.md create mode 100644 serverside_challenge_2/challenge/frontend/index.html create mode 100644 serverside_challenge_2/challenge/frontend/package.json create mode 100644 serverside_challenge_2/challenge/frontend/public/vite.svg create mode 100644 serverside_challenge_2/challenge/frontend/src/App.vue create mode 100644 serverside_challenge_2/challenge/frontend/src/assets/vue.svg create mode 100644 serverside_challenge_2/challenge/frontend/src/components/HelloWorld.vue create mode 100644 serverside_challenge_2/challenge/frontend/src/main.js create mode 100644 serverside_challenge_2/challenge/frontend/src/style.css create mode 100644 serverside_challenge_2/challenge/frontend/vite.config.js create mode 100644 serverside_challenge_2/challenge/frontend/yarn.lock diff --git a/serverside_challenge_2/challenge/config/environments/development.rb b/serverside_challenge_2/challenge/config/environments/development.rb index 5a23d6a20..24d035105 100644 --- a/serverside_challenge_2/challenge/config/environments/development.rb +++ b/serverside_challenge_2/challenge/config/environments/development.rb @@ -64,4 +64,7 @@ # Uncomment if you wish to allow Action Cable access from any origin. # config.action_cable.disable_request_forgery_protection = true + + # http://web:3000 で frontendコンテナからアクセスを許可する + config.hosts << "web" end diff --git a/serverside_challenge_2/challenge/docker-compose.yml b/serverside_challenge_2/challenge/docker-compose.yml index 723d14368..53846e140 100644 --- a/serverside_challenge_2/challenge/docker-compose.yml +++ b/serverside_challenge_2/challenge/docker-compose.yml @@ -23,5 +23,14 @@ services: stdin_open: true depends_on: - db + frontend: + build: ./frontend + command: bash -c "yarn dev --host" + ports: + - "5173:5173" + volumes: + - ./frontend:/app + tty: true + stdin_open: true volumes: - postgres_volume: \ No newline at end of file + postgres_volume: diff --git a/serverside_challenge_2/challenge/frontend/.gitignore b/serverside_challenge_2/challenge/frontend/.gitignore new file mode 100644 index 000000000..a547bf36d --- /dev/null +++ b/serverside_challenge_2/challenge/frontend/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/serverside_challenge_2/challenge/frontend/.vscode/extensions.json b/serverside_challenge_2/challenge/frontend/.vscode/extensions.json new file mode 100644 index 000000000..a7cea0b06 --- /dev/null +++ b/serverside_challenge_2/challenge/frontend/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["Vue.volar"] +} diff --git a/serverside_challenge_2/challenge/frontend/Dockerfile b/serverside_challenge_2/challenge/frontend/Dockerfile new file mode 100644 index 000000000..527ed34be --- /dev/null +++ b/serverside_challenge_2/challenge/frontend/Dockerfile @@ -0,0 +1,5 @@ +FROM node:23-bookworm + +WORKDIR /app + +RUN yarn install diff --git a/serverside_challenge_2/challenge/frontend/README.md b/serverside_challenge_2/challenge/frontend/README.md new file mode 100644 index 000000000..1511959c2 --- /dev/null +++ b/serverside_challenge_2/challenge/frontend/README.md @@ -0,0 +1,5 @@ +# Vue 3 + Vite + +This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 ` + + diff --git a/serverside_challenge_2/challenge/frontend/package.json b/serverside_challenge_2/challenge/frontend/package.json new file mode 100644 index 000000000..598db77fd --- /dev/null +++ b/serverside_challenge_2/challenge/frontend/package.json @@ -0,0 +1,18 @@ +{ + "name": "app", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "vue": "^3.5.13" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.2.1", + "vite": "^6.2.0" + } +} diff --git a/serverside_challenge_2/challenge/frontend/public/vite.svg b/serverside_challenge_2/challenge/frontend/public/vite.svg new file mode 100644 index 000000000..e7b8dfb1b --- /dev/null +++ b/serverside_challenge_2/challenge/frontend/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/serverside_challenge_2/challenge/frontend/src/App.vue b/serverside_challenge_2/challenge/frontend/src/App.vue new file mode 100644 index 000000000..81c28ec8c --- /dev/null +++ b/serverside_challenge_2/challenge/frontend/src/App.vue @@ -0,0 +1,30 @@ + + + + + diff --git a/serverside_challenge_2/challenge/frontend/src/assets/vue.svg b/serverside_challenge_2/challenge/frontend/src/assets/vue.svg new file mode 100644 index 000000000..770e9d333 --- /dev/null +++ b/serverside_challenge_2/challenge/frontend/src/assets/vue.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/serverside_challenge_2/challenge/frontend/src/components/HelloWorld.vue b/serverside_challenge_2/challenge/frontend/src/components/HelloWorld.vue new file mode 100644 index 000000000..546ebbc62 --- /dev/null +++ b/serverside_challenge_2/challenge/frontend/src/components/HelloWorld.vue @@ -0,0 +1,43 @@ + + + + + diff --git a/serverside_challenge_2/challenge/frontend/src/main.js b/serverside_challenge_2/challenge/frontend/src/main.js new file mode 100644 index 000000000..2425c0f74 --- /dev/null +++ b/serverside_challenge_2/challenge/frontend/src/main.js @@ -0,0 +1,5 @@ +import { createApp } from 'vue' +import './style.css' +import App from './App.vue' + +createApp(App).mount('#app') diff --git a/serverside_challenge_2/challenge/frontend/src/style.css b/serverside_challenge_2/challenge/frontend/src/style.css new file mode 100644 index 000000000..f69131543 --- /dev/null +++ b/serverside_challenge_2/challenge/frontend/src/style.css @@ -0,0 +1,79 @@ +:root { + font-family: system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} +a:hover { + color: #535bf2; +} + +body { + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +button { + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; +} +button:hover { + border-color: #646cff; +} +button:focus, +button:focus-visible { + outline: 4px auto -webkit-focus-ring-color; +} + +.card { + padding: 2em; +} + +#app { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + a:hover { + color: #747bff; + } + button { + background-color: #f9f9f9; + } +} diff --git a/serverside_challenge_2/challenge/frontend/vite.config.js b/serverside_challenge_2/challenge/frontend/vite.config.js new file mode 100644 index 000000000..bbcf80cca --- /dev/null +++ b/serverside_challenge_2/challenge/frontend/vite.config.js @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [vue()], +}) diff --git a/serverside_challenge_2/challenge/frontend/yarn.lock b/serverside_challenge_2/challenge/frontend/yarn.lock new file mode 100644 index 000000000..df45b7e47 --- /dev/null +++ b/serverside_challenge_2/challenge/frontend/yarn.lock @@ -0,0 +1,475 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/helper-string-parser@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c" + integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA== + +"@babel/helper-validator-identifier@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7" + integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ== + +"@babel/parser@^7.25.3": + version "7.26.10" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.10.tgz#e9bdb82f14b97df6569b0b038edd436839c57749" + integrity sha512-6aQR2zGE/QFi8JpDLjUZEPYOs7+mhKXm86VaKFiLP35JQwQb6bwUE+XbvkH0EptsYhbNBSUGaUBLKqxH1xSgsA== + dependencies: + "@babel/types" "^7.26.10" + +"@babel/types@^7.26.10": + version "7.26.10" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.10.tgz#396382f6335bd4feb65741eacfc808218f859259" + integrity sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ== + dependencies: + "@babel/helper-string-parser" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" + +"@esbuild/aix-ppc64@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.1.tgz#c33cf6bbee34975626b01b80451cbb72b4c6c91d" + integrity sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ== + +"@esbuild/android-arm64@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.25.1.tgz#ea766015c7d2655164f22100d33d7f0308a28d6d" + integrity sha512-50tM0zCJW5kGqgG7fQ7IHvQOcAn9TKiVRuQ/lN0xR+T2lzEFvAi1ZcS8DiksFcEpf1t/GYOeOfCAgDHFpkiSmA== + +"@esbuild/android-arm@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.25.1.tgz#e84d2bf2fe2e6177a0facda3a575b2139fd3cb9c" + integrity sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q== + +"@esbuild/android-x64@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.25.1.tgz#58337bee3bc6d78d10425e5500bd11370cfdfbed" + integrity sha512-GCj6WfUtNldqUzYkN/ITtlhwQqGWu9S45vUXs7EIYf+7rCiiqH9bCloatO9VhxsL0Pji+PF4Lz2XXCES+Q8hDw== + +"@esbuild/darwin-arm64@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.1.tgz#a46805c1c585d451aa83be72500bd6e8495dd591" + integrity sha512-5hEZKPf+nQjYoSr/elb62U19/l1mZDdqidGfmFutVUjjUZrOazAtwK+Kr+3y0C/oeJfLlxo9fXb1w7L+P7E4FQ== + +"@esbuild/darwin-x64@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.25.1.tgz#0643e003bb238c63fc93ddbee7d26a003be3cd98" + integrity sha512-hxVnwL2Dqs3fM1IWq8Iezh0cX7ZGdVhbTfnOy5uURtao5OIVCEyj9xIzemDi7sRvKsuSdtCAhMKarxqtlyVyfA== + +"@esbuild/freebsd-arm64@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.1.tgz#cff18da5469c09986b93e87979de5d6872fe8f8e" + integrity sha512-1MrCZs0fZa2g8E+FUo2ipw6jw5qqQiH+tERoS5fAfKnRx6NXH31tXBKI3VpmLijLH6yriMZsxJtaXUyFt/8Y4A== + +"@esbuild/freebsd-x64@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.1.tgz#362fc09c2de14987621c1878af19203c46365dde" + integrity sha512-0IZWLiTyz7nm0xuIs0q1Y3QWJC52R8aSXxe40VUxm6BB1RNmkODtW6LHvWRrGiICulcX7ZvyH6h5fqdLu4gkww== + +"@esbuild/linux-arm64@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.25.1.tgz#aa90d5b02efc97a271e124e6d1cea490634f7498" + integrity sha512-jaN3dHi0/DDPelk0nLcXRm1q7DNJpjXy7yWaWvbfkPvI+7XNSc/lDOnCLN7gzsyzgu6qSAmgSvP9oXAhP973uQ== + +"@esbuild/linux-arm@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.25.1.tgz#dfcefcbac60a20918b19569b4b657844d39db35a" + integrity sha512-NdKOhS4u7JhDKw9G3cY6sWqFcnLITn6SqivVArbzIaf3cemShqfLGHYMx8Xlm/lBit3/5d7kXvriTUGa5YViuQ== + +"@esbuild/linux-ia32@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.25.1.tgz#6f9527077ccb7953ed2af02e013d4bac69f13754" + integrity sha512-OJykPaF4v8JidKNGz8c/q1lBO44sQNUQtq1KktJXdBLn1hPod5rE/Hko5ugKKZd+D2+o1a9MFGUEIUwO2YfgkQ== + +"@esbuild/linux-loong64@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.25.1.tgz#287d2412a5456e5860c2839d42a4b51284d1697c" + integrity sha512-nGfornQj4dzcq5Vp835oM/o21UMlXzn79KobKlcs3Wz9smwiifknLy4xDCLUU0BWp7b/houtdrgUz7nOGnfIYg== + +"@esbuild/linux-mips64el@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.1.tgz#530574b9e1bc5d20f7a4f44c5f045e26f3783d57" + integrity sha512-1osBbPEFYwIE5IVB/0g2X6i1qInZa1aIoj1TdL4AaAb55xIIgbg8Doq6a5BzYWgr+tEcDzYH67XVnTmUzL+nXg== + +"@esbuild/linux-ppc64@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.1.tgz#5d7e6b283a0b321ea42c6bc0abeb9eb99c1f5589" + integrity sha512-/6VBJOwUf3TdTvJZ82qF3tbLuWsscd7/1w+D9LH0W/SqUgM5/JJD0lrJ1fVIfZsqB6RFmLCe0Xz3fmZc3WtyVg== + +"@esbuild/linux-riscv64@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.1.tgz#14fa0cd073c26b4ee2465d18cd1e18eea7859fa8" + integrity sha512-nSut/Mx5gnilhcq2yIMLMe3Wl4FK5wx/o0QuuCLMtmJn+WeWYoEGDN1ipcN72g1WHsnIbxGXd4i/MF0gTcuAjQ== + +"@esbuild/linux-s390x@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.25.1.tgz#e677b4b9d1b384098752266ccaa0d52a420dc1aa" + integrity sha512-cEECeLlJNfT8kZHqLarDBQso9a27o2Zd2AQ8USAEoGtejOrCYHNtKP8XQhMDJMtthdF4GBmjR2au3x1udADQQQ== + +"@esbuild/linux-x64@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.25.1.tgz#f1c796b78fff5ce393658313e8c58613198d9954" + integrity sha512-xbfUhu/gnvSEg+EGovRc+kjBAkrvtk38RlerAzQxvMzlB4fXpCFCeUAYzJvrnhFtdeyVCDANSjJvOvGYoeKzFA== + +"@esbuild/netbsd-arm64@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.1.tgz#0d280b7dfe3973f111b02d5fe9f3063b92796d29" + integrity sha512-O96poM2XGhLtpTh+s4+nP7YCCAfb4tJNRVZHfIE7dgmax+yMP2WgMd2OecBuaATHKTHsLWHQeuaxMRnCsH8+5g== + +"@esbuild/netbsd-x64@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.1.tgz#be663893931a4bb3f3a009c5cc24fa9681cc71c0" + integrity sha512-X53z6uXip6KFXBQ+Krbx25XHV/NCbzryM6ehOAeAil7X7oa4XIq+394PWGnwaSQ2WRA0KI6PUO6hTO5zeF5ijA== + +"@esbuild/openbsd-arm64@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.1.tgz#d9021b884233673a05dc1cc26de0bf325d824217" + integrity sha512-Na9T3szbXezdzM/Kfs3GcRQNjHzM6GzFBeU1/6IV/npKP5ORtp9zbQjvkDJ47s6BCgaAZnnnu/cY1x342+MvZg== + +"@esbuild/openbsd-x64@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.1.tgz#9f1dc1786ed2e2938c404b06bcc48be9a13250de" + integrity sha512-T3H78X2h1tszfRSf+txbt5aOp/e7TAz3ptVKu9Oyir3IAOFPGV6O9c2naym5TOriy1l0nNf6a4X5UXRZSGX/dw== + +"@esbuild/sunos-x64@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.25.1.tgz#89aac24a4b4115959b3f790192cf130396696c27" + integrity sha512-2H3RUvcmULO7dIE5EWJH8eubZAI4xw54H1ilJnRNZdeo8dTADEZ21w6J22XBkXqGJbe0+wnNJtw3UXRoLJnFEg== + +"@esbuild/win32-arm64@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.25.1.tgz#354358647a6ea98ea6d243bf48bdd7a434999582" + integrity sha512-GE7XvrdOzrb+yVKB9KsRMq+7a2U/K5Cf/8grVFRAGJmfADr/e/ODQ134RK2/eeHqYV5eQRFxb1hY7Nr15fv1NQ== + +"@esbuild/win32-ia32@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.25.1.tgz#8cea7340f2647eba951a041dc95651e3908cd4cb" + integrity sha512-uOxSJCIcavSiT6UnBhBzE8wy3n0hOkJsBOzy7HDAuTDE++1DJMRRVCPGisULScHL+a/ZwdXPpXD3IyFKjA7K8A== + +"@esbuild/win32-x64@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.25.1.tgz#7d79922cb2d88f9048f06393dbf62d2e4accb584" + integrity sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg== + +"@jridgewell/sourcemap-codec@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" + integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== + +"@rollup/rollup-android-arm-eabi@4.35.0": + version "4.35.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.35.0.tgz#e1d7700735f7e8de561ef7d1fa0362082a180c43" + integrity sha512-uYQ2WfPaqz5QtVgMxfN6NpLD+no0MYHDBywl7itPYd3K5TjjSghNKmX8ic9S8NU8w81NVhJv/XojcHptRly7qQ== + +"@rollup/rollup-android-arm64@4.35.0": + version "4.35.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.35.0.tgz#fa6cdfb1fc9e2c8e227a7f35d524d8f7f90cf4db" + integrity sha512-FtKddj9XZudurLhdJnBl9fl6BwCJ3ky8riCXjEw3/UIbjmIY58ppWwPEvU3fNu+W7FUsAsB1CdH+7EQE6CXAPA== + +"@rollup/rollup-darwin-arm64@4.35.0": + version "4.35.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.35.0.tgz#6da5a1ddc4f11d4a7ae85ab443824cb6bf614e30" + integrity sha512-Uk+GjOJR6CY844/q6r5DR/6lkPFOw0hjfOIzVx22THJXMxktXG6CbejseJFznU8vHcEBLpiXKY3/6xc+cBm65Q== + +"@rollup/rollup-darwin-x64@4.35.0": + version "4.35.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.35.0.tgz#25b74ce2d8d3f9ea8e119b01384d44a1c0a0d3ae" + integrity sha512-3IrHjfAS6Vkp+5bISNQnPogRAW5GAV1n+bNCrDwXmfMHbPl5EhTmWtfmwlJxFRUCBZ+tZ/OxDyU08aF6NI/N5Q== + +"@rollup/rollup-freebsd-arm64@4.35.0": + version "4.35.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.35.0.tgz#be3d39e3441df5d6e187c83d158c60656c82e203" + integrity sha512-sxjoD/6F9cDLSELuLNnY0fOrM9WA0KrM0vWm57XhrIMf5FGiN8D0l7fn+bpUeBSU7dCgPV2oX4zHAsAXyHFGcQ== + +"@rollup/rollup-freebsd-x64@4.35.0": + version "4.35.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.35.0.tgz#cd932d3ec679711efd65ca25821fb318e25b7ce4" + integrity sha512-2mpHCeRuD1u/2kruUiHSsnjWtHjqVbzhBkNVQ1aVD63CcexKVcQGwJ2g5VphOd84GvxfSvnnlEyBtQCE5hxVVw== + +"@rollup/rollup-linux-arm-gnueabihf@4.35.0": + version "4.35.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.35.0.tgz#d300b74c6f805474225632f185daaeae760ac2bb" + integrity sha512-mrA0v3QMy6ZSvEuLs0dMxcO2LnaCONs1Z73GUDBHWbY8tFFocM6yl7YyMu7rz4zS81NDSqhrUuolyZXGi8TEqg== + +"@rollup/rollup-linux-arm-musleabihf@4.35.0": + version "4.35.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.35.0.tgz#2caac622380f314c41934ed1e68ceaf6cc380cc3" + integrity sha512-DnYhhzcvTAKNexIql8pFajr0PiDGrIsBYPRvCKlA5ixSS3uwo/CWNZxB09jhIapEIg945KOzcYEAGGSmTSpk7A== + +"@rollup/rollup-linux-arm64-gnu@4.35.0": + version "4.35.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.35.0.tgz#1ec841650b038cc15c194c26326483fd7ebff3e3" + integrity sha512-uagpnH2M2g2b5iLsCTZ35CL1FgyuzzJQ8L9VtlJ+FckBXroTwNOaD0z0/UF+k5K3aNQjbm8LIVpxykUOQt1m/A== + +"@rollup/rollup-linux-arm64-musl@4.35.0": + version "4.35.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.35.0.tgz#2fc70a446d986e27f6101ea74e81746987f69150" + integrity sha512-XQxVOCd6VJeHQA/7YcqyV0/88N6ysSVzRjJ9I9UA/xXpEsjvAgDTgH3wQYz5bmr7SPtVK2TsP2fQ2N9L4ukoUg== + +"@rollup/rollup-linux-loongarch64-gnu@4.35.0": + version "4.35.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.35.0.tgz#561bd045cd9ce9e08c95f42e7a8688af8c93d764" + integrity sha512-5pMT5PzfgwcXEwOaSrqVsz/LvjDZt+vQ8RT/70yhPU06PTuq8WaHhfT1LW+cdD7mW6i/J5/XIkX/1tCAkh1W6g== + +"@rollup/rollup-linux-powerpc64le-gnu@4.35.0": + version "4.35.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.35.0.tgz#45d849a0b33813f33fe5eba9f99e0ff15ab5caad" + integrity sha512-c+zkcvbhbXF98f4CtEIP1EBA/lCic5xB0lToneZYvMeKu5Kamq3O8gqrxiYYLzlZH6E3Aq+TSW86E4ay8iD8EA== + +"@rollup/rollup-linux-riscv64-gnu@4.35.0": + version "4.35.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.35.0.tgz#78dde3e6fcf5b5733a97d0a67482d768aa1e83a5" + integrity sha512-s91fuAHdOwH/Tad2tzTtPX7UZyytHIRR6V4+2IGlV0Cej5rkG0R61SX4l4y9sh0JBibMiploZx3oHKPnQBKe4g== + +"@rollup/rollup-linux-s390x-gnu@4.35.0": + version "4.35.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.35.0.tgz#2e34835020f9e03dfb411473a5c2a0e8a9c5037b" + integrity sha512-hQRkPQPLYJZYGP+Hj4fR9dDBMIM7zrzJDWFEMPdTnTy95Ljnv0/4w/ixFw3pTBMEuuEuoqtBINYND4M7ujcuQw== + +"@rollup/rollup-linux-x64-gnu@4.35.0": + version "4.35.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.35.0.tgz#4f9774beddc6f4274df57ac99862eb23040de461" + integrity sha512-Pim1T8rXOri+0HmV4CdKSGrqcBWX0d1HoPnQ0uw0bdp1aP5SdQVNBy8LjYncvnLgu3fnnCt17xjWGd4cqh8/hA== + +"@rollup/rollup-linux-x64-musl@4.35.0": + version "4.35.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.35.0.tgz#dfcff2c1aed518b3d23ccffb49afb349d74fb608" + integrity sha512-QysqXzYiDvQWfUiTm8XmJNO2zm9yC9P/2Gkrwg2dH9cxotQzunBHYr6jk4SujCTqnfGxduOmQcI7c2ryuW8XVg== + +"@rollup/rollup-win32-arm64-msvc@4.35.0": + version "4.35.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.35.0.tgz#b0b37e2d77041e3aa772f519291309abf4c03a84" + integrity sha512-OUOlGqPkVJCdJETKOCEf1mw848ZyJ5w50/rZ/3IBQVdLfR5jk/6Sr5m3iO2tdPgwo0x7VcncYuOvMhBWZq8ayg== + +"@rollup/rollup-win32-ia32-msvc@4.35.0": + version "4.35.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.35.0.tgz#5b5a40e44a743ddc0e06b8e1b3982f856dc9ce0a" + integrity sha512-2/lsgejMrtwQe44glq7AFFHLfJBPafpsTa6JvP2NGef/ifOa4KBoglVf7AKN7EV9o32evBPRqfg96fEHzWo5kw== + +"@rollup/rollup-win32-x64-msvc@4.35.0": + version "4.35.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.35.0.tgz#05f25dbc9981bee1ae6e713daab10397044a46ca" + integrity sha512-PIQeY5XDkrOysbQblSW7v3l1MDZzkTEzAfTPkj5VAu3FW8fS4ynyLg2sINp0fp3SjZ8xkRYpLqoKcYqAkhU1dw== + +"@types/estree@1.0.6": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" + integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== + +"@vitejs/plugin-vue@^5.2.1": + version "5.2.1" + resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-5.2.1.tgz#d1491f678ee3af899f7ae57d9c21dc52a65c7133" + integrity sha512-cxh314tzaWwOLqVes2gnnCtvBDcM1UMdn+iFR+UjAn411dPT3tOmqrJjbMd7koZpMAmBM/GqeV4n9ge7JSiJJQ== + +"@vue/compiler-core@3.5.13": + version "3.5.13" + resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.5.13.tgz#b0ae6c4347f60c03e849a05d34e5bf747c9bda05" + integrity sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q== + dependencies: + "@babel/parser" "^7.25.3" + "@vue/shared" "3.5.13" + entities "^4.5.0" + estree-walker "^2.0.2" + source-map-js "^1.2.0" + +"@vue/compiler-dom@3.5.13": + version "3.5.13" + resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.5.13.tgz#bb1b8758dbc542b3658dda973b98a1c9311a8a58" + integrity sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA== + dependencies: + "@vue/compiler-core" "3.5.13" + "@vue/shared" "3.5.13" + +"@vue/compiler-sfc@3.5.13": + version "3.5.13" + resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.5.13.tgz#461f8bd343b5c06fac4189c4fef8af32dea82b46" + integrity sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ== + dependencies: + "@babel/parser" "^7.25.3" + "@vue/compiler-core" "3.5.13" + "@vue/compiler-dom" "3.5.13" + "@vue/compiler-ssr" "3.5.13" + "@vue/shared" "3.5.13" + estree-walker "^2.0.2" + magic-string "^0.30.11" + postcss "^8.4.48" + source-map-js "^1.2.0" + +"@vue/compiler-ssr@3.5.13": + version "3.5.13" + resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.5.13.tgz#e771adcca6d3d000f91a4277c972a996d07f43ba" + integrity sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA== + dependencies: + "@vue/compiler-dom" "3.5.13" + "@vue/shared" "3.5.13" + +"@vue/reactivity@3.5.13": + version "3.5.13" + resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.5.13.tgz#b41ff2bb865e093899a22219f5b25f97b6fe155f" + integrity sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg== + dependencies: + "@vue/shared" "3.5.13" + +"@vue/runtime-core@3.5.13": + version "3.5.13" + resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.5.13.tgz#1fafa4bf0b97af0ebdd9dbfe98cd630da363a455" + integrity sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw== + dependencies: + "@vue/reactivity" "3.5.13" + "@vue/shared" "3.5.13" + +"@vue/runtime-dom@3.5.13": + version "3.5.13" + resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.5.13.tgz#610fc795de9246300e8ae8865930d534e1246215" + integrity sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog== + dependencies: + "@vue/reactivity" "3.5.13" + "@vue/runtime-core" "3.5.13" + "@vue/shared" "3.5.13" + csstype "^3.1.3" + +"@vue/server-renderer@3.5.13": + version "3.5.13" + resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.5.13.tgz#429ead62ee51de789646c22efe908e489aad46f7" + integrity sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA== + dependencies: + "@vue/compiler-ssr" "3.5.13" + "@vue/shared" "3.5.13" + +"@vue/shared@3.5.13": + version "3.5.13" + resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.5.13.tgz#87b309a6379c22b926e696893237826f64339b6f" + integrity sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ== + +csstype@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" + integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== + +entities@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" + integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== + +esbuild@^0.25.0: + version "0.25.1" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.25.1.tgz#a16b8d070b6ad4871935277bda6ccfe852e3fa2f" + integrity sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ== + optionalDependencies: + "@esbuild/aix-ppc64" "0.25.1" + "@esbuild/android-arm" "0.25.1" + "@esbuild/android-arm64" "0.25.1" + "@esbuild/android-x64" "0.25.1" + "@esbuild/darwin-arm64" "0.25.1" + "@esbuild/darwin-x64" "0.25.1" + "@esbuild/freebsd-arm64" "0.25.1" + "@esbuild/freebsd-x64" "0.25.1" + "@esbuild/linux-arm" "0.25.1" + "@esbuild/linux-arm64" "0.25.1" + "@esbuild/linux-ia32" "0.25.1" + "@esbuild/linux-loong64" "0.25.1" + "@esbuild/linux-mips64el" "0.25.1" + "@esbuild/linux-ppc64" "0.25.1" + "@esbuild/linux-riscv64" "0.25.1" + "@esbuild/linux-s390x" "0.25.1" + "@esbuild/linux-x64" "0.25.1" + "@esbuild/netbsd-arm64" "0.25.1" + "@esbuild/netbsd-x64" "0.25.1" + "@esbuild/openbsd-arm64" "0.25.1" + "@esbuild/openbsd-x64" "0.25.1" + "@esbuild/sunos-x64" "0.25.1" + "@esbuild/win32-arm64" "0.25.1" + "@esbuild/win32-ia32" "0.25.1" + "@esbuild/win32-x64" "0.25.1" + +estree-walker@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" + integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== + +fsevents@~2.3.2, fsevents@~2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +magic-string@^0.30.11: + version "0.30.17" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.17.tgz#450a449673d2460e5bbcfba9a61916a1714c7453" + integrity sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA== + dependencies: + "@jridgewell/sourcemap-codec" "^1.5.0" + +nanoid@^3.3.8: + version "3.3.9" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.9.tgz#e0097d8e026b3343ff053e9ccd407360a03f503a" + integrity sha512-SppoicMGpZvbF1l3z4x7No3OlIjP7QJvC9XR7AhZr1kL133KHnKPztkKDc+Ir4aJ/1VhTySrtKhrsycmrMQfvg== + +picocolors@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + +postcss@^8.4.48, postcss@^8.5.3: + version "8.5.3" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.3.tgz#1463b6f1c7fb16fe258736cba29a2de35237eafb" + integrity sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A== + dependencies: + nanoid "^3.3.8" + picocolors "^1.1.1" + source-map-js "^1.2.1" + +rollup@^4.30.1: + version "4.35.0" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.35.0.tgz#76c95dba17a579df4c00c3955aed32aa5d4dc66d" + integrity sha512-kg6oI4g+vc41vePJyO6dHt/yl0Rz3Thv0kJeVQ3D1kS3E5XSuKbPc29G4IpT/Kv1KQwgHVcN+HtyS+HYLNSvQg== + dependencies: + "@types/estree" "1.0.6" + optionalDependencies: + "@rollup/rollup-android-arm-eabi" "4.35.0" + "@rollup/rollup-android-arm64" "4.35.0" + "@rollup/rollup-darwin-arm64" "4.35.0" + "@rollup/rollup-darwin-x64" "4.35.0" + "@rollup/rollup-freebsd-arm64" "4.35.0" + "@rollup/rollup-freebsd-x64" "4.35.0" + "@rollup/rollup-linux-arm-gnueabihf" "4.35.0" + "@rollup/rollup-linux-arm-musleabihf" "4.35.0" + "@rollup/rollup-linux-arm64-gnu" "4.35.0" + "@rollup/rollup-linux-arm64-musl" "4.35.0" + "@rollup/rollup-linux-loongarch64-gnu" "4.35.0" + "@rollup/rollup-linux-powerpc64le-gnu" "4.35.0" + "@rollup/rollup-linux-riscv64-gnu" "4.35.0" + "@rollup/rollup-linux-s390x-gnu" "4.35.0" + "@rollup/rollup-linux-x64-gnu" "4.35.0" + "@rollup/rollup-linux-x64-musl" "4.35.0" + "@rollup/rollup-win32-arm64-msvc" "4.35.0" + "@rollup/rollup-win32-ia32-msvc" "4.35.0" + "@rollup/rollup-win32-x64-msvc" "4.35.0" + fsevents "~2.3.2" + +source-map-js@^1.2.0, source-map-js@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" + integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== + +vite@^6.2.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/vite/-/vite-6.2.1.tgz#ae865d4bb93a11844be1bc647c8b2dd1856ea180" + integrity sha512-n2GnqDb6XPhlt9B8olZPrgMD/es/Nd1RdChF6CBD/fHW6pUyUTt2sQW2fPRX5GiD9XEa6+8A6A4f2vT6pSsE7Q== + dependencies: + esbuild "^0.25.0" + postcss "^8.5.3" + rollup "^4.30.1" + optionalDependencies: + fsevents "~2.3.3" + +vue@^3.5.13: + version "3.5.13" + resolved "https://registry.yarnpkg.com/vue/-/vue-3.5.13.tgz#9f760a1a982b09c0c04a867903fc339c9f29ec0a" + integrity sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ== + dependencies: + "@vue/compiler-dom" "3.5.13" + "@vue/compiler-sfc" "3.5.13" + "@vue/runtime-dom" "3.5.13" + "@vue/server-renderer" "3.5.13" + "@vue/shared" "3.5.13" From c2380ec775eb86d79f9edac1859d48b5d0b651a1 Mon Sep 17 00:00:00 2001 From: yoshiro fujimaki Date: Wed, 12 Mar 2025 22:52:02 +0900 Subject: [PATCH 08/19] =?UTF-8?q?=E3=83=95=E3=83=AD=E3=83=B3=E3=83=88?= =?UTF-8?q?=E3=82=A8=E3=83=B3=E3=83=89=E3=82=92=E4=BD=9C=E6=88=90=E3=80=82?= =?UTF-8?q?cors=E3=81=AE=E5=88=B6=E7=B4=84=E3=82=92rails=E5=81=B4=E3=81=AB?= =?UTF-8?q?=E8=BF=BD=E8=A8=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- serverside_challenge_2/challenge/Gemfile | 2 +- serverside_challenge_2/challenge/Gemfile.lock | 3 + .../config/environments/development.rb | 3 - .../challenge/config/initializers/cors.rb | 19 ++- .../challenge/frontend/package.json | 1 + .../challenge/frontend/src/App.vue | 12 +- .../components/ElectricityRateSimulation.vue | 91 ++++++++++ .../frontend/src/components/HelloWorld.vue | 43 ----- .../challenge/frontend/yarn.lock | 160 ++++++++++++++++++ 9 files changed, 268 insertions(+), 66 deletions(-) create mode 100644 serverside_challenge_2/challenge/frontend/src/components/ElectricityRateSimulation.vue delete mode 100644 serverside_challenge_2/challenge/frontend/src/components/HelloWorld.vue diff --git a/serverside_challenge_2/challenge/Gemfile b/serverside_challenge_2/challenge/Gemfile index e9222995a..92463de5e 100644 --- a/serverside_challenge_2/challenge/Gemfile +++ b/serverside_challenge_2/challenge/Gemfile @@ -36,7 +36,7 @@ gem 'bootsnap', require: false # gem "image_processing", "~> 1.2" # Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible -# gem "rack-cors" +gem "rack-cors" group :development, :test do # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem diff --git a/serverside_challenge_2/challenge/Gemfile.lock b/serverside_challenge_2/challenge/Gemfile.lock index d580c7ae2..549168fe2 100644 --- a/serverside_challenge_2/challenge/Gemfile.lock +++ b/serverside_challenge_2/challenge/Gemfile.lock @@ -132,6 +132,8 @@ GEM nio4r (~> 2.0) racc (1.7.3) rack (2.2.8) + rack-cors (2.0.2) + rack (>= 2.0.0) rack-test (2.1.0) rack (>= 1.3) rails (7.0.8) @@ -229,6 +231,7 @@ DEPENDENCIES factory_bot_rails pg (~> 1.1) puma (~> 5.0) + rack-cors rails (~> 7.0.8) rspec-rails (~> 7.0) rubocop-rails diff --git a/serverside_challenge_2/challenge/config/environments/development.rb b/serverside_challenge_2/challenge/config/environments/development.rb index 24d035105..5a23d6a20 100644 --- a/serverside_challenge_2/challenge/config/environments/development.rb +++ b/serverside_challenge_2/challenge/config/environments/development.rb @@ -64,7 +64,4 @@ # Uncomment if you wish to allow Action Cable access from any origin. # config.action_cable.disable_request_forgery_protection = true - - # http://web:3000 で frontendコンテナからアクセスを許可する - config.hosts << "web" end diff --git a/serverside_challenge_2/challenge/config/initializers/cors.rb b/serverside_challenge_2/challenge/config/initializers/cors.rb index 62875c1a4..df749704c 100644 --- a/serverside_challenge_2/challenge/config/initializers/cors.rb +++ b/serverside_challenge_2/challenge/config/initializers/cors.rb @@ -7,12 +7,13 @@ # Read more: https://github.com/cyu/rack-cors -# Rails.application.config.middleware.insert_before 0, Rack::Cors do -# allow do -# origins "example.com" -# -# resource "*", -# headers: :any, -# methods: [:get, :post, :put, :patch, :delete, :options, :head] -# end -# end +Rails.application.config.middleware.insert_before 0, Rack::Cors do + allow do + origins "localhost:5173" + + resource "*", + headers: :any, + #methods: [:get, :post, :put, :patch, :delete, :options, :head] + methods: [:get] + end +end diff --git a/serverside_challenge_2/challenge/frontend/package.json b/serverside_challenge_2/challenge/frontend/package.json index 598db77fd..7d035e9b5 100644 --- a/serverside_challenge_2/challenge/frontend/package.json +++ b/serverside_challenge_2/challenge/frontend/package.json @@ -9,6 +9,7 @@ "preview": "vite preview" }, "dependencies": { + "axios": "^1.8.3", "vue": "^3.5.13" }, "devDependencies": { diff --git a/serverside_challenge_2/challenge/frontend/src/App.vue b/serverside_challenge_2/challenge/frontend/src/App.vue index 81c28ec8c..2f83c662f 100644 --- a/serverside_challenge_2/challenge/frontend/src/App.vue +++ b/serverside_challenge_2/challenge/frontend/src/App.vue @@ -1,17 +1,9 @@ diff --git a/serverside_challenge_2/challenge/frontend/src/components/HelloWorld.vue b/serverside_challenge_2/challenge/frontend/src/components/HelloWorld.vue deleted file mode 100644 index 546ebbc62..000000000 --- a/serverside_challenge_2/challenge/frontend/src/components/HelloWorld.vue +++ /dev/null @@ -1,43 +0,0 @@ - - - - - diff --git a/serverside_challenge_2/challenge/frontend/yarn.lock b/serverside_challenge_2/challenge/frontend/yarn.lock index df45b7e47..b12e7ae78 100644 --- a/serverside_challenge_2/challenge/frontend/yarn.lock +++ b/serverside_challenge_2/challenge/frontend/yarn.lock @@ -342,16 +342,86 @@ resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.5.13.tgz#87b309a6379c22b926e696893237826f64339b6f" integrity sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ== +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + +axios@^1.8.3: + version "1.8.3" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.8.3.tgz#9ebccd71c98651d547162a018a1a95a4b4ed4de8" + integrity sha512-iP4DebzoNlP/YN2dpwCgb8zoCmhtkajzS48JvwmkSkXvPI3DHc7m+XYL5tGnSlJtR6nImXZmdCuN5aP8dh1d8A== + dependencies: + follow-redirects "^1.15.6" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + +call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" + integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + csstype@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + +dunder-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" + integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== + dependencies: + call-bind-apply-helpers "^1.0.1" + es-errors "^1.3.0" + gopd "^1.2.0" + entities@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== +es-define-property@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" + integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" + integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== + dependencies: + es-errors "^1.3.0" + +es-set-tostringtag@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d" + integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA== + dependencies: + es-errors "^1.3.0" + get-intrinsic "^1.2.6" + has-tostringtag "^1.0.2" + hasown "^2.0.2" + esbuild@^0.25.0: version "0.25.1" resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.25.1.tgz#a16b8d070b6ad4871935277bda6ccfe852e3fa2f" @@ -388,11 +458,79 @@ estree-walker@^2.0.2: resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== +follow-redirects@^1.15.6: + version "1.15.9" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1" + integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== + +form-data@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.2.tgz#35cabbdd30c3ce73deb2c42d3c8d3ed9ca51794c" + integrity sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + es-set-tostringtag "^2.1.0" + mime-types "^2.1.12" + fsevents@~2.3.2, fsevents@~2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +get-intrinsic@^1.2.6: + version "1.3.0" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" + integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== + dependencies: + call-bind-apply-helpers "^1.0.2" + es-define-property "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.1.1" + function-bind "^1.1.2" + get-proto "^1.0.1" + gopd "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + math-intrinsics "^1.1.0" + +get-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" + integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== + dependencies: + dunder-proto "^1.0.1" + es-object-atoms "^1.0.0" + +gopd@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" + integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== + +has-symbols@^1.0.3, has-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" + integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== + +has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== + dependencies: + has-symbols "^1.0.3" + +hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + magic-string@^0.30.11: version "0.30.17" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.17.tgz#450a449673d2460e5bbcfba9a61916a1714c7453" @@ -400,6 +538,23 @@ magic-string@^0.30.11: dependencies: "@jridgewell/sourcemap-codec" "^1.5.0" +math-intrinsics@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" + integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + nanoid@^3.3.8: version "3.3.9" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.9.tgz#e0097d8e026b3343ff053e9ccd407360a03f503a" @@ -419,6 +574,11 @@ postcss@^8.4.48, postcss@^8.5.3: picocolors "^1.1.1" source-map-js "^1.2.1" +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + rollup@^4.30.1: version "4.35.0" resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.35.0.tgz#76c95dba17a579df4c00c3955aed32aa5d4dc66d" From 76b8126cdf1bbd67d320e68c4db3c719a97a0c41 Mon Sep 17 00:00:00 2001 From: yoshiro fujimaki Date: Thu, 13 Mar 2025 00:36:35 +0900 Subject: [PATCH 09/19] =?UTF-8?q?=E4=BD=BF=E7=94=A8=E9=87=8F=E8=A8=88?= =?UTF-8?q?=E7=AE=97=E3=81=AE=E3=83=AD=E3=82=B8=E3=83=83=E3=82=AF=E3=81=8C?= =?UTF-8?q?=E6=AE=B5=E9=9A=8E=E7=9A=84=E3=81=AA=E6=96=99=E9=87=91=E3=82=92?= =?UTF-8?q?=E8=80=83=E6=85=AE=E3=81=A7=E3=81=8D=E3=81=A6=E3=81=84=E3=81=AA?= =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=9F=E3=81=9F=E3=82=81=E4=BF=AE=E6=AD=A3?= =?UTF-8?q?=E3=80=81min=5Fusage=E3=81=A8max=5Fusage=E3=81=AE=E5=AE=9A?= =?UTF-8?q?=E7=BE=A9=E5=8F=8A=E3=81=B3=E3=81=9D=E3=82=8C=E3=81=AB=E4=BC=B4?= =?UTF-8?q?=E3=81=86seed=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- serverside_challenge_2/challenge/Gemfile | 2 +- .../models/electricity_charges_usage_rate.rb | 2 +- .../challenge/app/models/plan.rb | 39 ++++++++---- .../challenge/config/initializers/cors.rb | 10 +-- ..._create_electricity_charges_usage_rates.rb | 4 +- serverside_challenge_2/challenge/db/schema.rb | 4 +- .../challenge/db/seeds/provider_plan_rates.rb | 12 ++-- .../challenge/spec/models/plan_spec.rb | 63 ++++++++++++++----- 8 files changed, 90 insertions(+), 46 deletions(-) diff --git a/serverside_challenge_2/challenge/Gemfile b/serverside_challenge_2/challenge/Gemfile index 92463de5e..15782da93 100644 --- a/serverside_challenge_2/challenge/Gemfile +++ b/serverside_challenge_2/challenge/Gemfile @@ -36,7 +36,7 @@ gem 'bootsnap', require: false # gem "image_processing", "~> 1.2" # Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible -gem "rack-cors" +gem 'rack-cors' group :development, :test do # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem diff --git a/serverside_challenge_2/challenge/app/models/electricity_charges_usage_rate.rb b/serverside_challenge_2/challenge/app/models/electricity_charges_usage_rate.rb index a11b550ba..757a08cc7 100644 --- a/serverside_challenge_2/challenge/app/models/electricity_charges_usage_rate.rb +++ b/serverside_challenge_2/challenge/app/models/electricity_charges_usage_rate.rb @@ -35,7 +35,7 @@ def validate_no_overlap_in_usage_ranges(usage_ranges) previous_max_usage = nil usage_ranges.each do |usage_range| - if previous_max_usage.present? && usage_range[:min] <= previous_max_usage + if previous_max_usage.present? && usage_range[:min] < previous_max_usage errors.add(:base, '同一プラン内で使用量の範囲が他のレコードと重複しています') break end diff --git a/serverside_challenge_2/challenge/app/models/plan.rb b/serverside_challenge_2/challenge/app/models/plan.rb index 693abe8e7..fc61cd22f 100644 --- a/serverside_challenge_2/challenge/app/models/plan.rb +++ b/serverside_challenge_2/challenge/app/models/plan.rb @@ -8,18 +8,15 @@ class Plan < ApplicationRecord validates :name, presence: true, uniqueness: { scope: :provider_id } def electricity_price(ampere, usage) - # 基本料金、もしくは従量料金がなければ計算不能のためnilを返却する + # アンペア数に対応する基本料金がなければ計算不能のためnilを返却する electricity_charges_basic_rate = electricity_charges_basic_rates.find_by(ampere: ampere) return nil if electricity_charges_basic_rate.nil? - electricity_charges_usage_rate = electricity_charges_usage_rate_from_usage(usage) - return nil if electricity_charges_usage_rate.nil? - - # 基本料金(契約アンペア数(A)に対応する基本料金(円))の計算 + # 基本料金 basic_price = electricity_charges_basic_rate.basic_rate - # 従量料金(電気使用量(kWh) * 従量料金単価(円/kWh))の計算 - usage_price = usage * electricity_charges_usage_rate.unit_rate + # 従量料金 + usage_price = calc_usage_price(usage) # 電気料金(基本料金 + 従量料金)を返却 basic_price + usage_price @@ -27,10 +24,28 @@ def electricity_price(ampere, usage) private - def electricity_charges_usage_rate_from_usage(usage) - electricity_charges_usage_rates - .where('min_usage <= :usage AND (max_usage IS NULL OR max_usage >= :usage)', usage: usage) - .order(:min_usage) - .first + def calc_usage_price(usage) + usage_rates = electricity_charges_usage_rates.order(:min_usage) + + usage_price = 0 + remaining_usage = usage + + usage_rates.each do |rate| + # 使用量の上限がない場合、すべてその単価で計算 + if rate.max_usage.nil? + usage_price += remaining_usage * rate.unit_rate + break + end + + # 範囲内の使用量のみ計算 + range_usage = [remaining_usage, rate.max_usage - rate.min_usage].min + usage_price += range_usage * rate.unit_rate + remaining_usage -= range_usage + + # 使用量が計算し終わったら終了 + break if remaining_usage <= 0 + end + + usage_price end end diff --git a/serverside_challenge_2/challenge/config/initializers/cors.rb b/serverside_challenge_2/challenge/config/initializers/cors.rb index df749704c..d035be3c1 100644 --- a/serverside_challenge_2/challenge/config/initializers/cors.rb +++ b/serverside_challenge_2/challenge/config/initializers/cors.rb @@ -9,11 +9,11 @@ Rails.application.config.middleware.insert_before 0, Rack::Cors do allow do - origins "localhost:5173" + origins 'localhost:5173' - resource "*", - headers: :any, - #methods: [:get, :post, :put, :patch, :delete, :options, :head] - methods: [:get] + resource '*', + headers: :any, + # methods: [:get, :post, :put, :patch, :delete, :options, :head] + methods: [:get] end end diff --git a/serverside_challenge_2/challenge/db/migrate/20250311091629_create_electricity_charges_usage_rates.rb b/serverside_challenge_2/challenge/db/migrate/20250311091629_create_electricity_charges_usage_rates.rb index 15db9773e..88be843b1 100644 --- a/serverside_challenge_2/challenge/db/migrate/20250311091629_create_electricity_charges_usage_rates.rb +++ b/serverside_challenge_2/challenge/db/migrate/20250311091629_create_electricity_charges_usage_rates.rb @@ -4,8 +4,8 @@ class CreateElectricityChargesUsageRates < ActiveRecord::Migration[7.0] def change create_table :electricity_charges_usage_rates, comment: 'プラン毎の電気従量料金を格納する' do |t| t.references :plan, null: false, foreign_key: true - t.integer :min_usage, null: false, comment: '電気使用量(kWh)の下限値' - t.integer :max_usage, comment: '電気使用量(kWh)の上限値' + t.integer :min_usage, null: false, comment: '電気使用量(kWh)の下限値(境界値を含まない)' + t.integer :max_usage, comment: '電気使用量(kWh)の上限値(境界値を含む)' t.decimal :unit_rate, null: false, comment: '従量料金単価(円/kWh)' t.timestamps diff --git a/serverside_challenge_2/challenge/db/schema.rb b/serverside_challenge_2/challenge/db/schema.rb index 4b70d9df9..8c40154fc 100644 --- a/serverside_challenge_2/challenge/db/schema.rb +++ b/serverside_challenge_2/challenge/db/schema.rb @@ -28,8 +28,8 @@ create_table 'electricity_charges_usage_rates', comment: 'プラン毎の電気従量料金を格納する', force: :cascade do |t| t.bigint 'plan_id', null: false - t.integer 'min_usage', null: false, comment: '電気使用量(kWh)の下限値' - t.integer 'max_usage', comment: '電気使用量(kWh)の上限値' + t.integer 'min_usage', null: false, comment: '電気使用量(kWh)の下限値(境界値を含まない)' + t.integer 'max_usage', comment: '電気使用量(kWh)の上限値(境界値を含む)' t.decimal 'unit_rate', null: false, comment: '従量料金単価(円/kWh)' t.datetime 'created_at', null: false t.datetime 'updated_at', null: false diff --git a/serverside_challenge_2/challenge/db/seeds/provider_plan_rates.rb b/serverside_challenge_2/challenge/db/seeds/provider_plan_rates.rb index 14cd52851..8e58f6d58 100644 --- a/serverside_challenge_2/challenge/db/seeds/provider_plan_rates.rb +++ b/serverside_challenge_2/challenge/db/seeds/provider_plan_rates.rb @@ -49,8 +49,8 @@ def create_usage_rates(plan, rates) ], usage_rates: [ { min_usage: 0, max_usage: 120, unit_rate: 19.88 }, - { min_usage: 121, max_usage: 300, unit_rate: 26.48 }, - { min_usage: 301, max_usage: nil, unit_rate: 30.57 } + { min_usage: 120, max_usage: 300, unit_rate: 26.48 }, + { min_usage: 300, max_usage: nil, unit_rate: 30.57 } ] }, { @@ -66,8 +66,8 @@ def create_usage_rates(plan, rates) ], usage_rates: [ { min_usage: 0, max_usage: 120, unit_rate: 29.80 }, - { min_usage: 121, max_usage: 300, unit_rate: 36.40 }, - { min_usage: 301, max_usage: nil, unit_rate: 40.49 } + { min_usage: 120, max_usage: 300, unit_rate: 36.40 }, + { min_usage: 300, max_usage: nil, unit_rate: 40.49 } ] } ]) @@ -84,8 +84,8 @@ def create_usage_rates(plan, rates) ], usage_rates: [ { min_usage: 0, max_usage: 140, unit_rate: 23.67 }, - { min_usage: 141, max_usage: 350, unit_rate: 23.88 }, - { min_usage: 351, max_usage: nil, unit_rate: 26.41 } + { min_usage: 140, max_usage: 350, unit_rate: 23.88 }, + { min_usage: 350, max_usage: nil, unit_rate: 26.41 } ] } ]) diff --git a/serverside_challenge_2/challenge/spec/models/plan_spec.rb b/serverside_challenge_2/challenge/spec/models/plan_spec.rb index 83ec2b56e..86c082ced 100644 --- a/serverside_challenge_2/challenge/spec/models/plan_spec.rb +++ b/serverside_challenge_2/challenge/spec/models/plan_spec.rb @@ -14,50 +14,79 @@ end let!(:basic_rate_30A) do - create(:electricity_charges_basic_rate, plan: plan, ampere: 30, basic_rate: 1000) + create(:electricity_charges_basic_rate, plan: plan, ampere: 30, basic_rate: 2000) end - let!(:usage_rate_low) do + let!(:usage_rate_first) do create(:electricity_charges_usage_rate, plan: plan, min_usage: 0, max_usage: 120, unit_rate: 20) end - let!(:usage_rate_middle) do - create(:electricity_charges_usage_rate, plan: plan, min_usage: 121, max_usage: 300, unit_rate: 25) + let!(:usage_rate_second) do + create(:electricity_charges_usage_rate, plan: plan, min_usage: 120, max_usage: 300, unit_rate: 25) end - let!(:usage_rate_high) do - create(:electricity_charges_usage_rate, plan: plan, min_usage: 301, max_usage: nil, unit_rate: 30) + let!(:usage_rate_third) do + create(:electricity_charges_usage_rate, plan: plan, min_usage: 300, max_usage: nil, unit_rate: 30) end describe '正常系' do - context 'アンペア数と使用量が条件に合致する場合' do + context '使用量が0の場合' do + let(:ampere) { 30 } + let(:usage) { 0 } + + it '基本料金を返す' do + expect(plan.electricity_price(ampere, usage)).to eq(basic_rate_30A.basic_rate) + end + end + + context '使用量が第一段階内の時' do let(:ampere) { 20 } - let(:usage) { 150 } + let(:usage) { 100 } - it '基本料金と従量料金の合計を返す' do + it '基本料金と1段階目までの従量料金の合計を返す' do expect( plan.electricity_price(ampere, usage) - ).to eq(basic_rate_20A.basic_rate + (usage * usage_rate_middle.unit_rate)) + ).to eq(basic_rate_20A.basic_rate + (usage * usage_rate_first.unit_rate)) end end - context '使用量が0の場合' do + context '使用量が第一段階上限の時' do let(:ampere) { 30 } - let(:usage) { 0 } + let(:usage) { 120 } - it '基本料金と従量料金(0円)の合計を返す' do - expect(plan.electricity_price(ampere, usage)).to eq(basic_rate_30A.basic_rate) + it '基本料金と1段階目までの従量料金の合計を返す' do + expect( + plan.electricity_price(ampere, usage) + ).to eq(basic_rate_30A.basic_rate + (usage * usage_rate_first.unit_rate)) end end - context '使用量が上限のないレートに該当する場合' do + context '使用量が第二段階内の時' do let(:ampere) { 30 } - let(:usage) { 0 } + let(:usage) { 201 } + + it '基本料金と2段階目までの従量料金の合計を返す' do + usage_first_price = usage_rate_first.max_usage * usage_rate_first.unit_rate + usage_second_price = (usage - usage_rate_first.max_usage) * usage_rate_second.unit_rate + + expect( + plan.electricity_price(ampere, usage) + ).to eq(basic_rate_30A.basic_rate + usage_first_price + usage_second_price) + end + end + + context '使用量が上限の無い(max_usage: nil)レート内の時' do + let(:ampere) { 30 } + let(:usage) { 352 } it '基本料金と従量料金の合計を返す' do + usage_first_price = usage_rate_first.max_usage * usage_rate_first.unit_rate + usage_second_price = (usage_rate_second.max_usage - usage_rate_first.max_usage) * usage_rate_second.unit_rate + usage_third_price = (usage - usage_rate_second.max_usage) * usage_rate_third.unit_rate + expect( plan.electricity_price(ampere, usage) - ).to eq(basic_rate_30A.basic_rate + (usage * usage_rate_high.unit_rate)) + ).to eq(basic_rate_30A.basic_rate + usage_first_price + usage_second_price + usage_third_price) end end end From eb7491dde65c0cfbdd1e3fd9e0347476a6bc1e52 Mon Sep 17 00:00:00 2001 From: yoshiro fujimaki Date: Thu, 13 Mar 2025 01:06:00 +0900 Subject: [PATCH 10/19] =?UTF-8?q?README=E3=82=92=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- serverside_challenge_2/challenge/README.md | 166 +++++++++++++++++++-- 1 file changed, 153 insertions(+), 13 deletions(-) diff --git a/serverside_challenge_2/challenge/README.md b/serverside_challenge_2/challenge/README.md index 7db80e4ca..39bb445a4 100644 --- a/serverside_challenge_2/challenge/README.md +++ b/serverside_challenge_2/challenge/README.md @@ -1,24 +1,164 @@ -# README +# 概要 -This README would normally document whatever steps are necessary to get the -application up and running. +このアプリケーションは、指定した契約アンペア数および使用量に基づき、各電力プランの料金を計算を実行します -Things you may want to cover: +# バージョン情報 -* Ruby version +- バックエンド + - Ruby `3.1.2` + - Rails `7.0.8` +- フロントエンド + - vue `3.5.13` + - vite `6.2.0` -* System dependencies +# 環境構築手順 -* Configuration +1. Dockerコンテナを起動 -* Database creation +``` +docker compose up --build +``` -* Database initialization +2. データベースを作成 -* How to run the test suite +``` +docker compose run web rails db:create +``` -* Services (job queues, cache servers, search engines, etc.) +3. マイグレーション実行 -* Deployment instructions -* ... +``` +docker compose run web rails db:migrate +``` + +4. シードデータの投入 + +``` +docker compose run web rails db:seed +``` + + +# アプリケーションへのアクセス + +http://localhost:5173 + +# アプリケーションの利用方法 + +- 以下項目を入力する + - 契約アンペア数(A) + - 1ヶ月の使用量(kWh) +- 計算するを押下する +- 正常な値を入力した場合 + - 画面上に電力会社、プラン、料金(円)が表示される +- 不正な値を入力した場合 + - エラーが表示される + +# データベース設計 + +## テーブル一覧 +- `providers` (電力会社情報) +- `plans` (電力会社ごとのプラン) +- `electricity_charges_basic_rates` (プランごとの基本料金) +- `electricity_charges_usage_rates` (プランごとの従量料金) + +## テーブル詳細 + +### `providers` (電力会社情報) +| カラム名 | 型 | 制約 | 説明 | +|:---|:---|:---|:---| +| `id` | `bigint` | PK, NOT NULL | ID (主キー) | +| `name` | `string` | NOT NULL, UNIQUE | 会社名 | +| `created_at` | `datetime` | NOT NULL | 作成日時 | +| `updated_at` | `datetime` | NOT NULL | 更新日時 | + +--- + +### `plans` (電力会社ごとのプラン) +| カラム名 | 型 | 制約 | 説明 | +|:---|:---|:---|:---| +| `id` | `bigint` | PK, NOT NULL | ID (主キー) | +| `provider_id` | `bigint` | FK, NOT NULL | 電力会社ID (外部キー) | +| `name` | `string` | NOT NULL, UNIQUE | プラン名 | +| `created_at` | `datetime` | NOT NULL | 作成日時 | +| `updated_at` | `datetime` | NOT NULL | 更新日時 | + +--- + +### `electricity_charges_basic_rates` (プランごとの基本料金) +| カラム名 | 型 | 制約 | 説明 | +|:---|:---|:---|:---| +| `id` | `bigint` | PK, NOT NULL | ID (主キー) | +| `plan_id` | `bigint` | FK, NOT NULL, ampereとの複合UNIQUE | プランID (外部キー) | +| `ampere` | `integer` | NOT NULL, planとの複合UNIQUE | 契約アンペア数(A) | +| `basic_rate` | `decimal` | NOT NULL | 基本料金 (円) | +| `created_at` | `datetime` | NOT NULL | 作成日時 | +| `updated_at` | `datetime` | NOT NULL | 更新日時 | + +--- + +### `electricity_charges_usage_rates` (プランごとの従量料金) +| カラム名 | 型 | 制約 | 説明 | +|:---|:---|:---|:---| +| `id` | `bigint` | PK, NOT NULL | ID (主キー) | +| `plan_id` | `bigint` | FK, NOT NULL | プランID (外部キー) | +| `min_usage` | `integer` | NOT NULL | 電気使用量(kWh)の下限値 (境界値を含まない) | +| `max_usage` | `integer` | NULL許可 | 電気使用量(kWh)の上限値 (境界値を含む) | +| `unit_rate` | `decimal` | NOT NULL | 従量料金単価 (円/kWh) | +| `created_at` | `datetime` | NOT NULL | 作成日時 | +| `updated_at` | `datetime` | NOT NULL | 更新日時 | + +# API + +## 電力料金計算API + +### エンドポイント + +``` +GET /electricity_prices +``` + +### リクエストパラメータ + +| パラメータ | 必須 | 説明 | 例 | +| -- | -- | -- | -- | +| ampere | ○ | 契約アンペア数 (10/15/20/30/40/50/60のいずれか) | 30 | +| usage | ○ | 使用量 (0以上の整数) | 200 | + +### レスポンス + +レスポンス形式: JSON + +レスポンス例 (成功時) + +```json +[ + { + "provider_name": "東京電力エナジーパートナー", + "plan_name": "従量電灯B", + "price": "5648.0" + }, + { + "provider_name": "東京ガス", + "plan_name": "ずっとも電気1", + "price": "5890.6" + } +] +``` + +レスポンス例 (エラー時) + +```json +{ + "error": "アンペア数(ampere)は 10/15/20/30/40/50/60 のいずれかを指定してください" +} + +``` + +# テストの実行 + +rspecを導入しており、以下のコマンドでテスト実行できます + +``` +docker compose run web rspec +``` From a5757236763f364ecab12cdecfc30715cc32621f Mon Sep 17 00:00:00 2001 From: yoshiro fujimaki Date: Thu, 13 Mar 2025 01:46:53 +0900 Subject: [PATCH 11/19] =?UTF-8?q?=E9=9B=BB=E6=B0=97=E6=96=99=E9=87=91?= =?UTF-8?q?=E3=82=B7=E3=83=9F=E3=83=A5=E3=83=AC=E3=83=BC=E3=82=B7=E3=83=A7?= =?UTF-8?q?=E3=83=B3=E3=81=A7=E5=85=A5=E5=8A=9B=E9=A0=85=E7=9B=AE=E3=81=AB?= =?UTF-8?q?=E3=81=A4=E3=81=84=E3=81=A6=E8=AA=AC=E6=98=8E=E3=81=99=E3=82=8B?= =?UTF-8?q?=E3=82=AC=E3=82=A4=E3=83=89=E3=82=92=E4=BB=98=E4=B8=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../challenge/frontend/package.json | 1 + .../components/ElectricityRateSimulation.vue | 14 ++++++-- .../challenge/frontend/src/main.js | 6 +++- .../challenge/frontend/yarn.lock | 32 +++++++++++++++++++ 4 files changed, 50 insertions(+), 3 deletions(-) diff --git a/serverside_challenge_2/challenge/frontend/package.json b/serverside_challenge_2/challenge/frontend/package.json index 7d035e9b5..7692b6798 100644 --- a/serverside_challenge_2/challenge/frontend/package.json +++ b/serverside_challenge_2/challenge/frontend/package.json @@ -10,6 +10,7 @@ }, "dependencies": { "axios": "^1.8.3", + "floating-vue": "^5.2.2", "vue": "^3.5.13" }, "devDependencies": { diff --git a/serverside_challenge_2/challenge/frontend/src/components/ElectricityRateSimulation.vue b/serverside_challenge_2/challenge/frontend/src/components/ElectricityRateSimulation.vue index 0835d6d71..79ba32734 100644 --- a/serverside_challenge_2/challenge/frontend/src/components/ElectricityRateSimulation.vue +++ b/serverside_challenge_2/challenge/frontend/src/components/ElectricityRateSimulation.vue @@ -33,14 +33,21 @@ const fetchData = async () => {