diff --git a/challengeA/tokiwa_takumi/.gitignore b/challengeA/tokiwa_takumi/.gitignore new file mode 100644 index 000000000..f05cf8c8d --- /dev/null +++ b/challengeA/tokiwa_takumi/.gitignore @@ -0,0 +1 @@ +/spec/examples.txt diff --git a/challengeA/tokiwa_takumi/.rspec b/challengeA/tokiwa_takumi/.rspec new file mode 100644 index 000000000..c99d2e739 --- /dev/null +++ b/challengeA/tokiwa_takumi/.rspec @@ -0,0 +1 @@ +--require spec_helper diff --git a/challengeA/tokiwa_takumi/Gemfile b/challengeA/tokiwa_takumi/Gemfile new file mode 100644 index 000000000..c8b0363b5 --- /dev/null +++ b/challengeA/tokiwa_takumi/Gemfile @@ -0,0 +1,8 @@ +# frozen_string_literal: true + + source "https://rubygems.org" + + git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } + + gem "rspec" + \ No newline at end of file diff --git a/challengeA/tokiwa_takumi/Gemfile.lock b/challengeA/tokiwa_takumi/Gemfile.lock new file mode 100644 index 000000000..be655ec0c --- /dev/null +++ b/challengeA/tokiwa_takumi/Gemfile.lock @@ -0,0 +1,26 @@ +GEM + remote: https://rubygems.org/ + specs: + diff-lcs (1.4.4) + rspec (3.10.0) + rspec-core (~> 3.10.0) + rspec-expectations (~> 3.10.0) + rspec-mocks (~> 3.10.0) + rspec-core (3.10.1) + rspec-support (~> 3.10.0) + rspec-expectations (3.10.1) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.10.0) + rspec-mocks (3.10.2) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.10.0) + rspec-support (3.10.2) + +PLATFORMS + ruby + +DEPENDENCIES + rspec + +BUNDLED WITH + 2.1.2 diff --git a/challengeA/tokiwa_takumi/csv/jxtg/basic_charge.csv b/challengeA/tokiwa_takumi/csv/jxtg/basic_charge.csv new file mode 100644 index 000000000..17285d199 --- /dev/null +++ b/challengeA/tokiwa_takumi/csv/jxtg/basic_charge.csv @@ -0,0 +1,5 @@ +ampere,basic_charge +30,858.00 +40,1144.00 +50,1430.00 +60,1716.00 \ No newline at end of file diff --git a/challengeA/tokiwa_takumi/csv/jxtg/info.csv b/challengeA/tokiwa_takumi/csv/jxtg/info.csv new file mode 100644 index 000000000..14f58d9ae --- /dev/null +++ b/challengeA/tokiwa_takumi/csv/jxtg/info.csv @@ -0,0 +1,2 @@ +provider_name,plan_name +JXTGでんき,従量電灯Bたっぷりプラン \ No newline at end of file diff --git a/challengeA/tokiwa_takumi/csv/jxtg/usage_charge.csv b/challengeA/tokiwa_takumi/csv/jxtg/usage_charge.csv new file mode 100644 index 000000000..5a87cb863 --- /dev/null +++ b/challengeA/tokiwa_takumi/csv/jxtg/usage_charge.csv @@ -0,0 +1,5 @@ +greater_than,less_than,unit_charge +0,120,19.88 +120,300,26.48 +300,600,25.08 +600,,26.15 \ No newline at end of file diff --git a/challengeA/tokiwa_takumi/csv/looop/basic_charge.csv b/challengeA/tokiwa_takumi/csv/looop/basic_charge.csv new file mode 100644 index 000000000..5a58ea5a9 --- /dev/null +++ b/challengeA/tokiwa_takumi/csv/looop/basic_charge.csv @@ -0,0 +1,7 @@ +ampere,basic_charge +10,0.00 +20,0.00 +30,0.00 +40,0.00 +50,0.00 +60,0.00 \ No newline at end of file diff --git a/challengeA/tokiwa_takumi/csv/looop/info.csv b/challengeA/tokiwa_takumi/csv/looop/info.csv new file mode 100644 index 000000000..e5c2b1a54 --- /dev/null +++ b/challengeA/tokiwa_takumi/csv/looop/info.csv @@ -0,0 +1,2 @@ +provider_name,plan_name +Looopでんき,おうちプラン \ No newline at end of file diff --git a/challengeA/tokiwa_takumi/csv/looop/usage_charge.csv b/challengeA/tokiwa_takumi/csv/looop/usage_charge.csv new file mode 100644 index 000000000..77eb6f09e --- /dev/null +++ b/challengeA/tokiwa_takumi/csv/looop/usage_charge.csv @@ -0,0 +1,2 @@ +greater_than,less_than,unit_charge +0,,26.40 \ No newline at end of file diff --git a/challengeA/tokiwa_takumi/csv/tepco/basic_charge.csv b/challengeA/tokiwa_takumi/csv/tepco/basic_charge.csv new file mode 100644 index 000000000..93eb363fa --- /dev/null +++ b/challengeA/tokiwa_takumi/csv/tepco/basic_charge.csv @@ -0,0 +1,8 @@ +ampere,basic_charge +10,286.00 +15,419.00 +20,572.00 +30,858.00 +40,1144.00 +50,1430.00 +60,1716.00 \ No newline at end of file diff --git a/challengeA/tokiwa_takumi/csv/tepco/info.csv b/challengeA/tokiwa_takumi/csv/tepco/info.csv new file mode 100644 index 000000000..dc1a3549f --- /dev/null +++ b/challengeA/tokiwa_takumi/csv/tepco/info.csv @@ -0,0 +1,2 @@ +provider_name,plan_name +東京電力エナジーパートナー,従量電灯B \ No newline at end of file diff --git a/challengeA/tokiwa_takumi/csv/tepco/usage_charge.csv b/challengeA/tokiwa_takumi/csv/tepco/usage_charge.csv new file mode 100644 index 000000000..40f568915 --- /dev/null +++ b/challengeA/tokiwa_takumi/csv/tepco/usage_charge.csv @@ -0,0 +1,4 @@ +greater_than,less_than,unit_charge +0,120,19.88 +120,300,26.48 +300,,30.57 \ No newline at end of file diff --git a/challengeA/tokiwa_takumi/csv/tokyo_gas/basic_charge.csv b/challengeA/tokiwa_takumi/csv/tokyo_gas/basic_charge.csv new file mode 100644 index 000000000..17285d199 --- /dev/null +++ b/challengeA/tokiwa_takumi/csv/tokyo_gas/basic_charge.csv @@ -0,0 +1,5 @@ +ampere,basic_charge +30,858.00 +40,1144.00 +50,1430.00 +60,1716.00 \ No newline at end of file diff --git a/challengeA/tokiwa_takumi/csv/tokyo_gas/info.csv b/challengeA/tokiwa_takumi/csv/tokyo_gas/info.csv new file mode 100644 index 000000000..9457d9b77 --- /dev/null +++ b/challengeA/tokiwa_takumi/csv/tokyo_gas/info.csv @@ -0,0 +1,2 @@ +provider_name,plan_name +東京ガス,ずっともでんき1 \ No newline at end of file diff --git a/challengeA/tokiwa_takumi/csv/tokyo_gas/usage_charge.csv b/challengeA/tokiwa_takumi/csv/tokyo_gas/usage_charge.csv new file mode 100644 index 000000000..98c5cd088 --- /dev/null +++ b/challengeA/tokiwa_takumi/csv/tokyo_gas/usage_charge.csv @@ -0,0 +1,4 @@ +greater_than,less_than,unit_charge +0,140,23.67 +140,350,23.88 +350,,26.41 \ No newline at end of file diff --git a/challengeA/tokiwa_takumi/lib/csv_plan.rb b/challengeA/tokiwa_takumi/lib/csv_plan.rb new file mode 100644 index 000000000..a3a782847 --- /dev/null +++ b/challengeA/tokiwa_takumi/lib/csv_plan.rb @@ -0,0 +1,38 @@ +require 'csv' +require_relative 'plan' + +class CSVPlan + attr_reader :provider_name, :plan_name, :basic_charge_list, :usage_charge_list + + def initialize(provider_name, plan_name, basic_charge_list, usage_charge_list) + @provider_name = provider_name + @plan_name = plan_name + @basic_charge_list = basic_charge_list + @usage_charge_list = usage_charge_list + end + + def convert_to_plan + Plan.new( + provider_name, + plan_name, + basic_charge_list, + usage_charge_list + ) + end + + class << self + def create_list_from_csv + plans = [] + plans_dir = Dir.glob('../csv/*', base: __dir__) + plans_dir.each do |plan| + info = CSV.table(File.expand_path("./#{plan}/info.csv", __dir__)) + plans << CSVPlan.new(info[:provider_name][0], + info[:plan_name][0], + CSV.read(File.expand_path("./#{plan}/basic_charge.csv", __dir__), headers: true).map(&:to_hash), + CSV.read(File.expand_path("./#{plan}/usage_charge.csv", __dir__), headers: true).map(&:to_hash) + ) + end + plans + end + end +end diff --git a/challengeA/tokiwa_takumi/lib/plan.rb b/challengeA/tokiwa_takumi/lib/plan.rb new file mode 100644 index 000000000..b79537e85 --- /dev/null +++ b/challengeA/tokiwa_takumi/lib/plan.rb @@ -0,0 +1,32 @@ +class Plan + attr_reader :provider_name, :plan_name, :basic_charge_list, :usage_charge_list + + def initialize(provider_name, plan_name, basic_charge_list, usage_charge_list) + @provider_name = provider_name + @plan_name = plan_name + @basic_charge_list = basic_charge_list + @usage_charge_list = usage_charge_list + end + + def basic_charge(ampere) + basic_charge_plan = basic_charge_list.find{ |c| c["ampere"].to_i == ampere } + basic_charge_plan["basic_charge"].to_i unless basic_charge_plan.nil? + end + + def usage_charge(monthly_amount_kwh) + charge = 0 + usage_charge_list.each do |usage_charge| + gt = usage_charge["greater_than"].to_i + lt = usage_charge["less_than"].to_i + unit_charge = usage_charge["unit_charge"].to_f + if lt.zero? && gt < monthly_amount_kwh # 最大区分 + charge += (monthly_amount_kwh - gt) * unit_charge + elsif lt <= monthly_amount_kwh && !lt.zero? # 超過する区分 + charge += (lt - gt) * unit_charge + elsif gt < monthly_amount_kwh && monthly_amount_kwh < lt # 超過しない区分 + charge += (monthly_amount_kwh - gt) * unit_charge + end + end + charge + end +end diff --git a/challengeA/tokiwa_takumi/lib/simulator.rb b/challengeA/tokiwa_takumi/lib/simulator.rb new file mode 100644 index 000000000..dfe778406 --- /dev/null +++ b/challengeA/tokiwa_takumi/lib/simulator.rb @@ -0,0 +1,34 @@ +require_relative 'csv_plan' + +class Simulator + + def initialize(ampere, monthly_amount_kwh) + @ampere = ampere + @monthly_amount_kwh = monthly_amount_kwh + suggest_plan + end + + def simulate + @suggest_plans.flatten + end + + private + + def suggest_plan + @suggest_plans = [] + plan_list = CSVPlan.create_list_from_csv.map(&:convert_to_plan) + @suggest_plans = plan_list.map{ |plan| simulate_charge(plan) } + end + + def simulate_charge(plan) + basic_charge = plan.basic_charge(@ampere) + return if basic_charge.nil? ## 対応するアンペアなし + + price = basic_charge + plan.usage_charge(@monthly_amount_kwh) + { + "provider_name": plan.provider_name, + "plan_name": plan.plan_name, + "price": price + } + end +end diff --git a/challengeA/tokiwa_takumi/spec/plan_spec.rb b/challengeA/tokiwa_takumi/spec/plan_spec.rb new file mode 100644 index 000000000..c106ce630 --- /dev/null +++ b/challengeA/tokiwa_takumi/spec/plan_spec.rb @@ -0,0 +1,119 @@ +require "spec_helper" + +RSpec.describe Plan do + let!(:plan) { CSVPlan.create_list_from_csv.map(&:convert_to_plan) } + + describe "東京ガスずっともでんき1" do + let!(:tokyo_gas) { plan.find{ |plan| plan.provider_name == "東京ガス" }} + + describe "基本料金の算出が正しいこと" do + it "対応アンペアが存在する" do + expect(tokyo_gas.basic_charge(30)).to eq 858 + end + + it "対応アンペアが存在しない" do + expect(tokyo_gas.basic_charge(100)).to eq nil + end + end + + describe "従量課金料金の算出が正しいこと" do + it "1つの区分で収まる使用量で算出する" do + expect(tokyo_gas.usage_charge(100)).to eq (23.67 * 100) + end + + it "2つ以上の区分にまたがる使用量で算出する" do + expect_value = (23.67 * 140) + (23.88 * 160) + expect(tokyo_gas.usage_charge(300)).to eq expect_value + end + + it "一番上の区分まで使用した算出をする" do + expect_value = (23.67 * 140) + (23.88 * 210) + (26.41 * 50) + expect(tokyo_gas.usage_charge(400)).to eq expect_value + end + end + end + + describe "東京エナジーパートナー従量電灯B" do + let!(:tepco) { plan.find{ |plan| plan.provider_name == "東京電力エナジーパートナー" }} + + describe "基本料金の算出が正しいこと" do + it "対応アンペアが存在する" do + expect(tepco.basic_charge(40)).to eq 1144 + end + + it "対応アンペアが存在しない" do + expect(tepco.basic_charge(100)).to eq nil + end + end + + describe "従量課金料金の算出が正しいこと" do + it "1つの区分で収まる使用量で算出する" do + expect(tepco.usage_charge(100)).to eq (19.88 * 100) + end + + it "2つ以上の区分にまたがる使用量で算出する" do + expect_value = (19.88 * 120) + (26.48 * 180) + expect(tepco.usage_charge(300)).to eq expect_value + end + + it "一番上の区分まで使用した算出をする" do + expect_value = (19.88 * 120) + (26.48 * 180) + (30.57 * 100) + expect(tepco.usage_charge(400)).to eq expect_value + end + end + end + + describe "Loop電気おうちプラン" do + let!(:loop) { plan.find{ |plan| plan.provider_name == "Looopでんき" }} + + describe "基本料金の算出が正しいこと" do + it "対応アンペアが存在する" do + expect(loop.basic_charge(40)).to eq 0 + end + + it "対応アンペアが存在しない" do + expect(loop.basic_charge(100)).to eq nil + end + end + + it "基本料金の算出が正しいこと" do + expect(loop.basic_charge(40)).to eq 0 + end + + describe "従量課金料金の算出が正しいこと(区分が1つのみ)" do + it "1つの区分だけで算出されること" do + expect(loop.usage_charge(500)).to eq (26.40 * 500) + end + end + end + + describe "JXTGでんき従量電灯Bたっぷりプラン" do + let!(:jxtg) { plan.find{ |plan| plan.provider_name == "JXTGでんき" }} + + describe "基本料金の算出が正しいこと" do + it "対応アンペアが存在する" do + expect(jxtg.basic_charge(50)).to eq 1430 + end + + it "対応アンペアが存在しない" do + expect(jxtg.basic_charge(100)).to eq nil + end + end + + describe "従量課金料金の算出が正しいこと" do + it "1つの区分で収まる使用量で算出する" do + expect(jxtg.usage_charge(100)).to eq (19.88 * 100) + end + + it "2つ以上の区分にまたがる使用量で算出する" do + expect_value = (19.88 * 120) + (26.48 * 180) + expect(jxtg.usage_charge(300)).to eq expect_value + end + + it "一番上の区分まで使用した算出をする" do + expect_value = (19.88 * 120) + (26.48 * 180) + (25.08 * 300) + (26.15 * 50) + expect(jxtg.usage_charge(650)).to eq expect_value + end + end + end +end diff --git a/challengeA/tokiwa_takumi/spec/simulator_spec.rb b/challengeA/tokiwa_takumi/spec/simulator_spec.rb new file mode 100644 index 000000000..33b9be3a6 --- /dev/null +++ b/challengeA/tokiwa_takumi/spec/simulator_spec.rb @@ -0,0 +1,33 @@ +require "spec_helper" + +RSpec.describe Simulator do + describe "simulatorの結果をが期待どおりであること" do + let!(:simulator) { Simulator.new(40, 300) } + + it "simulator" do + expect(simulator.simulate.count).to eq 4 + expect(simulator.simulate).to match_array [ + { + "provider_name": "東京ガス", + "plan_name": "ずっともでんき1", + "price": 1144 + (23.67 * 140) + (23.88 * 160) + }, + { + "provider_name": "JXTGでんき", + "plan_name": "従量電灯Bたっぷりプラン", + "price": 1144 + (19.88 * 120) + (26.48 * 180) + }, + { + "provider_name": "Looopでんき", + "plan_name": "おうちプラン", + "price": (26.40 * 300) + }, + { + "provider_name": "東京電力エナジーパートナー", + "plan_name": "従量電灯B", + "price": 1144 + (19.88 * 120) + (26.48 * 180) + } + ] + end + end +end diff --git a/challengeA/tokiwa_takumi/spec/spec_helper.rb b/challengeA/tokiwa_takumi/spec/spec_helper.rb new file mode 100644 index 000000000..d592cedc7 --- /dev/null +++ b/challengeA/tokiwa_takumi/spec/spec_helper.rb @@ -0,0 +1,100 @@ +# This file was generated by the `rspec --init` 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 http://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: + # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/ + # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ + # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode + config.disable_monkey_patching! + + # This setting enables warnings. It's recommended, but in some cases may + # be too noisy due to issues in dependencies. + config.warnings = true + + # 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 + + Dir[File.join(File.dirname(__FILE__), '../lib/*.rb')].each { |f| require f } +end