# Calculating reactions to load on a beam

Posted on

Problem

I am new to Ruby and would like some help with re-factoring my code.

``````class Beam
def initialize
puts "Please specify span of beam"
@span = gets.chomp.to_f
puts "How many number of point loads do you want?"
@reaction = Array.new(2,0)
end
def setValues
count = 0
puts "At what dist should point load #{count+1} be placed from left?"
puts "Dist of Point Load #{count+1} From Left should be less than span length of beam"
else
puts "Magnitude of point load #{count+1} should be?"
@magnitude[count] = gets.chomp.to_f
count += 1
end
end
end
def calReactions
i = 0
i += 1
end
puts "Reaction at Left: #{@reaction[0]}"
puts "Reaction at Left: #{@reaction[1]}"
end
end

beam = Beam.new
beam.setValues
beam.calReactions
``````

Solution

First of all, you should not get user input from inside your `Beam` class. This ties it to the console and makes automated testing more difficult. A layered architecture is more suitable :

``````# The Beam class is only concerned by business logic.
# Notice how the logic and workflow are clear and readable,
# and how easy you it would be to write tests for it.
#
class Beam

def initialize( span )
@span        = span.to_f
end

distance, magnitude = distance.to_f, magnitude.to_f
if distance < 0 || distance > span || @load_points[distance]
end
end

def reactions
@load_points.inject( left: 0, right: 0 ) do |hash, (distance, magnitude)|
hash[:left]  += distance          * magnitude / span
hash[:right] += (span - distance) * magnitude / span
hash
end
end

end

# this module is an interface between your class and the user
# via the console. Notice that this module (could be a class,
# adapting user input to feed the Beam class interface, and
# formatting output.
#
module BeamConsole

# here we chose to move all questions to the user to methods,
# and name these methods with an exclamation mark at the end
#
def self.invoke
beam = Beam.new( span? )
begin
rescue ArgumentError => e
end while more?
format_reactions( beam.reactions )
end

private #===================================================================

def self.prompt( msg )
output = gets.strip!
return output unless output.empty?
prompt( msg )
end

def self.span?
prompt( 'Please specify span of beam' ).to_f
end

def self.more?
answer = prompt( 'Do you want to add points ? (y/n)' )
end

def self.distance?
prompt( 'At what distance should the point be placed from left?' ).to_f
end

def self.magnitude?
prompt( 'Magnitude should be?' ).to_f
end

def self.format_reactions( reactions )
puts "Reaction on the left is  : %.4f" % reactions[:left]
puts "Reaction on the right is : %.4f" % reactions[:right]
end
end
``````

This may seem overkill, but separating concerns is the core of OOP. With this approach, you’ll end up with a `Beam` class devoid of any responsability concerning user interaction, and that’s a good thing : you want your class to perform business logic before anything else.

This way, your code will be more readable (no logic riddled with prompts), so more maintainable, but also more reusable. Imagine that one day you want to allow users to perform calculations from a web page : you could not reuse your `Beam` class if it is tied to the console, but you could if you separated responsibilities.

I would write something like:

``````class Beam
def prompt msg
# a bit kung-fu, sorry
puts msg while gets.strip!.empty?
yield \$_
end
def initialize
@span = prompt "Please specify span of beam", &:to_f
@pointLoadNos = prompt "How many number of point loads do you want?", &:to_i
@magnitude = []
end
def setValues
prompt("At what dist should point load #{count + 1} be placed from left?", &:to_f)
puts "Dist of Point Load #{count + 1} From Left should be less than span length of beam"
end
@magnitude[count] = prompt "Magnitude of point load #{count + 1} should be?", &:to_f
end
end
def calReactions
@reaction = [0, 0]
@reaction[0] += (left * mag) / @span
@reaction[1] += (right * mag) / @span
end
puts "Reaction at Left: #{@reaction[0]}"
puts "Reaction at Right: #{@reaction[1]}"
end
end

beam = Beam.new
beam.setValues
beam.calReactions
``````

See this and this – when you pass a block, you don’t have to catch it into argument, but can just use Ruby’s keyword `yield`. Also instead of describing a block as a proc, you can use `&:method` notation.