Skip to content

Commit 3eb3b45

Browse files
committed
Adds example of performing the Replace Conditional with Function Lookup refactoring
1 parent c780bb1 commit 3eb3b45

File tree

20 files changed

+566
-0
lines changed

20 files changed

+566
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Sample for Replace Conditional with Function Lookup
2+
3+
The `calculator/lib/calculator/default.rb` contains the original implementation that uses a conditional to determine the operation to perform. The file `calculator/lib/calculator/refactored.rb` contains the version that uses functions to separate the different operations.
4+
5+
The same set of tests are used to evaluate the correctness of each file. These are defined in `calculator/spec/calculator/calculator_examples.rb`. The `calculator/spec/calculator/default_spec.rb` and `calculator/spec/calculator/refactored_spec.rb` files pass in the appropriate `Calculator` instance to test.
6+
7+
## Running the Tests
8+
9+
Assuming that you have `ruby` version 3.0.5 or higher, and at least version 2.4.18 of `bundle`, then you should be able to run the tests with `bundle exec rake spec` after installing the dependencies with `bundle install`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/.bundle/
2+
/.yardoc
3+
/_yardoc/
4+
/coverage/
5+
/doc/
6+
/pkg/
7+
/spec/reports/
8+
/tmp/
9+
10+
# rspec failure tracking
11+
.rspec_status
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
--format documentation
2+
--color
3+
--require spec_helper
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
AllCops:
2+
TargetRubyVersion: 2.6
3+
4+
Style/StringLiterals:
5+
Enabled: true
6+
EnforcedStyle: double_quotes
7+
8+
Style/StringLiteralsInInterpolation:
9+
Enabled: true
10+
EnforcedStyle: double_quotes
11+
12+
Layout/LineLength:
13+
Max: 120
14+
15+
Metrics/BlockLength:
16+
IgnoredMethods: ['describe', 'shared_examples']
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# frozen_string_literal: true
2+
3+
source "https://rubygems.org"
4+
5+
# Specify your gem's dependencies in calculator.gemspec
6+
gemspec
7+
8+
gem "rake", "~> 13.0"
9+
10+
gem "rspec", "~> 3.0"
11+
12+
gem "rubocop", "~> 1.21"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
PATH
2+
remote: .
3+
specs:
4+
calculator (0.1.0)
5+
6+
GEM
7+
remote: https://rubygems.org/
8+
specs:
9+
ast (2.4.2)
10+
base64 (0.1.1)
11+
diff-lcs (1.5.0)
12+
json (2.6.3)
13+
language_server-protocol (3.17.0.3)
14+
parallel (1.23.0)
15+
parser (3.2.2.3)
16+
ast (~> 2.4.1)
17+
racc
18+
racc (1.7.1)
19+
rainbow (3.1.1)
20+
rake (13.0.6)
21+
regexp_parser (2.8.1)
22+
rexml (3.2.6)
23+
rspec (3.12.0)
24+
rspec-core (~> 3.12.0)
25+
rspec-expectations (~> 3.12.0)
26+
rspec-mocks (~> 3.12.0)
27+
rspec-core (3.12.2)
28+
rspec-support (~> 3.12.0)
29+
rspec-expectations (3.12.3)
30+
diff-lcs (>= 1.2.0, < 2.0)
31+
rspec-support (~> 3.12.0)
32+
rspec-mocks (3.12.6)
33+
diff-lcs (>= 1.2.0, < 2.0)
34+
rspec-support (~> 3.12.0)
35+
rspec-support (3.12.1)
36+
rubocop (1.56.0)
37+
base64 (~> 0.1.1)
38+
json (~> 2.3)
39+
language_server-protocol (>= 3.17.0)
40+
parallel (~> 1.10)
41+
parser (>= 3.2.2.3)
42+
rainbow (>= 2.2.2, < 4.0)
43+
regexp_parser (>= 1.8, < 3.0)
44+
rexml (>= 3.2.5, < 4.0)
45+
rubocop-ast (>= 1.28.1, < 2.0)
46+
ruby-progressbar (~> 1.7)
47+
unicode-display_width (>= 2.4.0, < 3.0)
48+
rubocop-ast (1.29.0)
49+
parser (>= 3.2.1.0)
50+
ruby-progressbar (1.13.0)
51+
unicode-display_width (2.4.2)
52+
53+
PLATFORMS
54+
arm64-darwin-22
55+
56+
DEPENDENCIES
57+
calculator!
58+
rake (~> 13.0)
59+
rspec (~> 3.0)
60+
rubocop (~> 1.21)
61+
62+
BUNDLED WITH
63+
2.4.18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# frozen_string_literal: true
2+
3+
require "bundler/gem_tasks"
4+
require "rspec/core/rake_task"
5+
6+
RSpec::Core::RakeTask.new(:spec)
7+
8+
require "rubocop/rake_task"
9+
10+
RuboCop::RakeTask.new
11+
12+
task default: %i[spec rubocop]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/usr/bin/env ruby
2+
# frozen_string_literal: true
3+
4+
require "bundler/setup"
5+
require "calculator"
6+
7+
# You can add fixtures and/or initialization code here to make experimenting
8+
# with your gem easier. You can also use a different console, if you like.
9+
10+
require "irb"
11+
IRB.start(__FILE__)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
IFS=$'\n\t'
4+
set -vx
5+
6+
bundle install
7+
8+
# Do any other automated setup that you need to do here
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# frozen_string_literal: true
2+
3+
require_relative "lib/calculator/version"
4+
5+
Gem::Specification.new do |spec|
6+
spec.name = "calculator"
7+
spec.version = Calculator::VERSION
8+
spec.authors = ["M. Scott Ford"]
9+
spec.email = ["[email protected]"]
10+
11+
spec.summary = "Sample project for Managing Complexity with Clean Code course"
12+
spec.description = "Demonstrates how to employ the Replace Conditional with Function Lookup refactoring."
13+
spec.required_ruby_version = ">= 2.6.0"
14+
15+
# Specify which files should be added to the gem when it is released.
16+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
17+
spec.files = Dir.chdir(__dir__) do
18+
`git ls-files -z`.split("\x0").reject do |f|
19+
(File.expand_path(f) == __FILE__) ||
20+
f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor Gemfile])
21+
end
22+
end
23+
spec.bindir = "exe"
24+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
25+
spec.require_paths = ["lib"]
26+
27+
# Uncomment to register a new dependency of your gem
28+
# spec.add_dependency "example-gem", "~> 1.0"
29+
30+
# For more information and examples about making a new gem, check out our
31+
# guide at: https://bundler.io/guides/creating_gem.html
32+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# frozen_string_literal: true
2+
3+
require_relative "calculator/version"
4+
require_relative "calculator/default"
5+
require_relative "calculator/refactored"
6+
7+
module Calculator
8+
class Error < StandardError; end
9+
# Your code goes here...
10+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# frozen_string_literal: true
2+
3+
module Calculator
4+
# Default calculator implementation. Uses a conditional to determine which
5+
# operation to perform.
6+
class Default
7+
def initialize(value = 0)
8+
@value = value
9+
end
10+
11+
def execute(command)
12+
values = command.split(" ")
13+
operation = values[0]
14+
first_operand = values[1].to_f
15+
second_operand = values.length > 2 ? values[2].to_f : nil
16+
17+
case operation
18+
when "add"
19+
if !second_operand.nil?
20+
@value = first_operand + second_operand
21+
else
22+
@value += first_operand
23+
end
24+
25+
when "subtract"
26+
if !second_operand.nil?
27+
@value = first_operand - second_operand
28+
else
29+
@value -= first_operand
30+
end
31+
32+
when "multiply"
33+
if !second_operand.nil?
34+
@value = first_operand * second_operand
35+
else
36+
@value *= first_operand
37+
end
38+
39+
when "divide"
40+
if !second_operand.nil?
41+
@value = first_operand / second_operand
42+
else
43+
@value /= first_operand
44+
end
45+
else
46+
raise "Invalid operation"
47+
end
48+
end
49+
50+
def result
51+
@value
52+
end
53+
end
54+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# frozen_string_literal: true
2+
3+
module Calculator
4+
# Refactored calculator implementation. Uses a function lookup to determine
5+
# which operation to perform.
6+
class Refactored
7+
def initialize(value = 0)
8+
@value = value
9+
end
10+
11+
OPERATIONS = {
12+
"add" => :add,
13+
"subtract" => :subtract,
14+
"multiply" => :multiply,
15+
"divide" => :divide
16+
}.freeze
17+
18+
def execute(command)
19+
values = command.split(" ")
20+
operation = values[0]
21+
first_operand = values[1].to_f
22+
second_operand = values.length > 2 ? values[2].to_f : nil
23+
24+
raise "Invalid operation" unless OPERATIONS.key?(operation)
25+
26+
send(OPERATIONS[operation], first_operand, second_operand)
27+
end
28+
29+
def add(first_operand, second_operand)
30+
if !second_operand.nil?
31+
@value = first_operand + second_operand
32+
else
33+
@value += first_operand
34+
end
35+
end
36+
37+
def subtract(first_operand, second_operand)
38+
if !second_operand.nil?
39+
@value = first_operand - second_operand
40+
else
41+
@value -= first_operand
42+
end
43+
end
44+
45+
def multiply(first_operand, second_operand)
46+
if !second_operand.nil?
47+
@value = first_operand * second_operand
48+
else
49+
@value *= first_operand
50+
end
51+
end
52+
53+
def divide(first_operand, second_operand)
54+
if !second_operand.nil?
55+
@value = first_operand / second_operand
56+
else
57+
@value /= first_operand
58+
end
59+
end
60+
61+
def result
62+
@value
63+
end
64+
end
65+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# frozen_string_literal: true
2+
3+
module Calculator
4+
VERSION = "0.1.0"
5+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module Calculator
2+
VERSION: String
3+
# See the writing guide of rbs: https://github.com/ruby/rbs#guides
4+
end

0 commit comments

Comments
 (0)