Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
ce0d022
add api routes
nkmr-mty Jul 16, 2025
f0863ab
add api controller with test code
nkmr-mty Jul 16, 2025
5408876
add gem active_hash
Jul 21, 2025
51405de
add csv files
Jul 21, 2025
eaadf5a
add models
Jul 21, 2025
4970ab2
rename controller, add codes
Jul 21, 2025
74a4b2f
add secret_key_base for production
Jul 21, 2025
771639c
update secret_key_base for production
Jul 21, 2025
46c3d9f
revise amp validatemethod, correct method name for error processing
Jul 23, 2025
ec7f650
add rounding decimal process, separate provider management data from …
Jul 23, 2025
4cc5b0e
add provider model
Jul 23, 2025
8eb6b5c
create migration, seed file, executed migration
Jul 24, 2025
e897b3c
rename existing model with 'csv' suffix, add provider model
Jul 24, 2025
f535198
add model for db management
Jul 24, 2025
2371165
correct seed.rb
Jul 24, 2025
061e019
change data reference from CSV to DB on processing calculation
Jul 24, 2025
2cdbfd2
add rubocop
Jul 24, 2025
5909b8b
rubocop code correction
Jul 24, 2025
78237d9
remove csv data and models
Jul 24, 2025
b9f9e59
add empty lines
Jul 24, 2025
14325c5
restore credentials.yml.enc
Jul 25, 2025
e48eade
correct meter charge amount calculation
Jul 25, 2025
56818cc
correct rouding calculation, correct single meter_rate_charge pattern…
Jul 26, 2025
5f7d63e
change column provider_name and plan_name to name
Jul 26, 2025
517b890
correct meter_rate calculation condition for single meter charge step…
Jul 28, 2025
519b5a6
rubocop code correction: db/seeds.rb
Jul 28, 2025
db2b5d6
add service class, spread processing respoisibiities from controller …
Jul 30, 2025
e1890a7
prevent n+1
Jul 31, 2025
a0d952f
add rspec gem
Jul 31, 2025
18d89ba
add spec
Jul 31, 2025
ce69d19
change api method post to get
Jul 31, 2025
344ac7d
truncate total amount
Jul 31, 2025
f437eb7
remove active_hash
Jul 31, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions serverside_challenge_2/challenge/.rspec
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--require spec_helper
10 changes: 10 additions & 0 deletions serverside_challenge_2/challenge/.rubocop.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Documentation:
Enabled: false
MethodLength:
CountComments: true
Max: 25
Style/FrozenStringLiteralComment:
Enabled: false
Metrics:
Enabled: false

3 changes: 3 additions & 0 deletions serverside_challenge_2/challenge/Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,12 @@ gem "bootsnap", require: false
# Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible
# gem "rack-cors"

gem 'rubocop-rails', '~> 2.32'

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 'rspec-rails'
end

group :development do
Expand Down
55 changes: 55 additions & 0 deletions serverside_challenge_2/challenge/Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ GEM
i18n (>= 1.6, < 2)
minitest (>= 5.1)
tzinfo (~> 2.0)
ast (2.4.3)
bootsnap (1.18.3)
msgpack (~> 1.2)
builder (3.2.4)
Expand All @@ -75,6 +76,7 @@ GEM
debug (1.9.1)
irb (~> 1.10)
reline (>= 0.3.8)
diff-lcs (1.6.2)
erubi (1.12.0)
globalid (1.2.1)
activesupport (>= 6.1)
Expand All @@ -84,6 +86,9 @@ GEM
irb (1.11.2)
rdoc
reline (>= 0.4.2)
json (2.13.0)
language_server-protocol (3.17.0.5)
lint_roller (1.1.0)
loofah (2.22.0)
crass (~> 1.0.2)
nokogiri (>= 1.12.0)
Expand Down Expand Up @@ -111,7 +116,12 @@ GEM
racc (~> 1.4)
nokogiri (1.16.2-x86_64-linux)
racc (~> 1.4)
parallel (1.27.0)
parser (3.3.8.0)
ast (~> 2.4.1)
racc
pg (1.5.4)
prism (1.4.0)
psych (5.1.2)
stringio
puma (5.6.8)
Expand Down Expand Up @@ -148,16 +158,59 @@ 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.5)
rspec-support (~> 3.13.0)
rspec-expectations (3.13.5)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-mocks (3.13.5)
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.4)
rubocop (1.78.0)
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.45.1, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 4.0)
rubocop-ast (1.46.0)
parser (>= 3.3.7.2)
prism (~> 1.4)
rubocop-rails (2.32.0)
activesupport (>= 4.2.0)
lint_roller (~> 1.1)
rack (>= 1.1)
rubocop (>= 1.75.0, < 2.0)
rubocop-ast (>= 1.44.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)
Expand All @@ -173,6 +226,8 @@ DEPENDENCIES
pg (~> 1.1)
puma (~> 5.0)
rails (~> 7.0.8)
rspec-rails
rubocop-rails (~> 2.32)
tzinfo-data

RUBY VERSION
Expand Down

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

全体的に、コントローラーにすべての責務が集中してしまっている印象を受けました。

具体的には、以下のような処理がすべて controller に記述されているため、保守性や可読性の観点から、責務ごとに適切に分離した方が良いと考えています。

  • リクエスト/レスポンスの処理
  • ビジネスロジック(例:料金計算など)
  • パラメータのバリデーションチェック

現時点で大きく実装を変更するのは大変かと思いますので、「今後リファクタリングする場合に、どのように責務を分けて実装するべきか」について、テキストベースで構いませんので、考えを共有いただけると嬉しいです。

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

コントローラの処理を最小限にし、処理を分離いたしました。db2b5d6

  • 分離先
    • PowerSupplyPlanSimulator(service class)
    • PowerSupplyPlan(model)
    • MeterRateCharge(model)

Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# frozen_string_literal: true

module Api
module V1
class PowerSupplyPlanController < ApplicationController
def simulate_all
simulator = PowerSupplyPlanSimulator.new(params[:amp], params[:meter_rate])
result = simulator.call

return render json: [result] if result.is_a?(Hash) && result.key?(:error)

render json: result
end
end
end
end
5 changes: 5 additions & 0 deletions serverside_challenge_2/challenge/app/models/basic_charge.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# frozen_string_literal: true

class BasicCharge < ApplicationRecord
belongs_to :power_supply_plan
end
27 changes: 27 additions & 0 deletions serverside_challenge_2/challenge/app/models/meter_rate_charge.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# frozen_string_literal: true

class MeterRateCharge < ApplicationRecord
belongs_to :power_supply_plan

def calculate_charge(meter_rate)
min_rate = min_meter_rate
max_rate = max_meter_rate
step_price = price
return 0 if min_rate > meter_rate

if max_rate.nil?
return step_price * meter_rate if min_rate.zero?

return step_price * (meter_rate - min_rate + 1)
end
if max_rate.to_i < meter_rate.to_i
return step_price * (max_rate - min_rate) if min_rate.zero?

step_price * (max_rate - min_rate + 1)
else
return step_price * (meter_rate - min_rate) if min_rate.zero?

step_price * (meter_rate - min_rate + 1)
end
end
end
25 changes: 25 additions & 0 deletions serverside_challenge_2/challenge/app/models/power_supply_plan.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# frozen_string_literal: true

class PowerSupplyPlan < ApplicationRecord
belongs_to :provider
has_many :meter_rate_charges
has_many :basic_charges

def calculate_total_amount(amp, meter_rate)
basic_charge_record = basic_charges.where(amp: amp)
return nil if basic_charge_record.blank?

basic_charge_amount = basic_charge_record.first.price.to_f
meter_rate_charge_amount = calculate_meter_rate_charge(meter_rate)
(basic_charge_amount + meter_rate_charge_amount).floor(0).to_i
end

def calculate_meter_rate_charge(meter_rate)
total_meter_rate_charges = 0

meter_rate_charges.each do |step|
total_meter_rate_charges += step.calculate_charge(meter_rate.to_i)
end
total_meter_rate_charges
end
end
5 changes: 5 additions & 0 deletions serverside_challenge_2/challenge/app/models/provider.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# frozen_string_literal: true

class Provider < ApplicationRecord
has_many :power_supply_plans
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
class PowerSupplyPlanSimulator
VALID_AMPS = [10, 15, 20, 30, 40, 50, 60].freeze

def initialize(amp, meter_rate)
@amp = amp
@meter_rate = meter_rate
@errors = []
end

def call
unless validate_params
return {
error: {
code: 400,
message: @errors.join('\n')
}
}
end

result = []
PowerSupplyPlan.includes(:provider, :meter_rate_charges, :basic_charges).each do |plan|
calculate_result = plan.calculate_total_amount(@amp, @meter_rate)
next unless calculate_result

result << {
"provider_name": plan.provider.name,
"plan_name": plan.name,
"price": calculate_result
}
end
result
end

def validate_params
if @amp.blank?
@errors << "リクエストパラメータ 'amp' が不足しています。"
else
@errors << "リクエストパラメータ 'amp' の値が不正です。'10 / 15 / 20 / 30 / 40 / 50 / 60' のいずれかの値を指定してください。" unless valid_amp?(@amp)
end
if @meter_rate.blank?
@errors << "リクエストパラメータ 'meter_rate' が不足しています。"
else
unless numeric?(@meter_rate) && @meter_rate.to_i >= 0
@errors << "リクエストパラメータ 'meter_rate' の値が不正です。0以上の整数を指定してください。"
end
end
@errors.blank?
end

def numeric?(string)
Integer(string)
true
rescue ArgumentError, TypeError
false
end

def valid_amp?(amp)
VALID_AMPS.include?(amp.to_i)
end
end
Original file line number Diff line number Diff line change
@@ -1 +1 @@
bZE5VzGsIQoeBEOa79I28cq/Ax0SuIaPhy5+NFktLVZLNGpuzqmkTkAiWc+arbGUDU8aaedg9zUvnBBc0dPrATeWISHtFxVwJMScZQeSW1k7BUgSD6B6YsewlgtpEDFDAXoGZEQoTbM2FY3lDCd0dZF7I1+DsB4I9Z5NamOgOlNEK8gMQD3p3Csqc/mVhmdI3PjBEmblvTjkZIVOdfX1CmqJ/RZm1Lxu3RdNMaiXiOZL7I+oXE1SFfjWm7+CTwb+Vfs6nGMviDHDKh9k3jyb7Vflpx/frY4vYzP8WMzgbJPX4UHmxa7qS51tJ1ECpK265nS1mLsqxL7wRbjyzfUNtXknSSopGD7sDDm7F9ta8/e1nzU79uTH1LKFpWQWGMSbXmSu4EKPu655BA9ckdwnSsf67RYNX7/Nw5pp--9AT1V0z/SBAdVTZ5--JK/4x776vcnN7qGZuylM0g==
aLpSiYFmK7YeuO/q62KJR/UyXPpBWhH+oS6rzGWSQH/9p53vCpRMvfaUsZSneP9wYMioohjDqHdeY0bpOAXjMPy5suQk7yGANivapLmd2qNLt1wpIYzKGJiEwSP2gQD4Hyo/K6qGF2fchngDFhy0WWAe8Q0kcIM7WzUSV+VilcYylHcHHpdYhjsBx4+7ev2dbWLlYxbYuFSDGklGP85oYBmIH2QIzcYmwHhBkjwzFWvIl3xqL+/8iQlYsFyBsTk3tntgD/Au3kv6qYXQnf3ITXgHTKlaooxLTy9ZYwMOhJvPwtkMsWv9CVY+ACkUi5h9WY+rSHiBVCDHJwnEhwpz8/n4kzGXLuGRburdDZkA1IZb+j7+KzX625Y+x4+zR7P1wc8vJRw+/5O4MSndI28y0KkX15Qd8vMYNrowsXx+0WFtvo8LTh0J6p84b4bJyVkD1bGUpvpz+UADtGfTbuDqqRXNDCRM7NITVYcOg74QKiwi57gzRJgKOYt1KhXMLA5y4V+xo2zBZuTzGAqNS2rHcfwil9lcsmsTOQL7felWv2C2qqUllUo3CqTGNlXo3A24guP25tr3vDm2IrH7V/TLSjJcDAI1JUi35wwrCErgxrwK9eG0Y4w=--97QWVDZ45R2Bjr3G--byi/t6soITzi5vJv5PEsvw==
5 changes: 5 additions & 0 deletions serverside_challenge_2/challenge/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,9 @@

# Defines the root path route ("/")
# root "articles#index"
namespace :api do
namespace :v1 do
get "simulate_all_plan", to: "power_supply_plan#simulate_all"
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
class CreateBasicCharges < ActiveRecord::Migration[7.0]
def change
create_table :providers do |t|
t.string :provider_name, :null => false
t.string :rounding_amount_method, :null => false

t.timestamps
end

create_table :power_supply_plans do |t|
t.belongs_to :provider
t.string :plan_name, :null => false

t.timestamps
end

create_table :meter_rate_charges do |t|
t.belongs_to :power_supply_plan
t.integer :min_meter_rate, :null => false
t.integer :max_meter_rate
t.decimal :price, precision: 6, scale: 2, :null => false

t.timestamps
end

create_table :basic_charges do |t|
t.belongs_to :power_supply_plan
t.integer :amp, :null => false
t.decimal :price, precision: 6, scale: 2, :null => false

t.timestamps
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
class RemoveRoundingAmountMethodFromProvider < ActiveRecord::Migration[7.0]
def up
remove_column :providers, :rounding_amount_method, :string
end

def down
add_column :providers, :rounding_amount_method, :string
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class RenameProviderNameColumnToName < ActiveRecord::Migration[7.0]
def change
rename_column :providers, :provider_name, :name
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class RenamePlanNameColumnToName < ActiveRecord::Migration[7.0]
def change
rename_column :power_supply_plans, :plan_name, :name
end
end
Loading