Calculating reactions to load on a beam
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

  attr_reader :span

  def initialize( span )
    @span        = span.to_f
    @load_points = {}
  end

  def add_load_point( distance, magnitude )
    distance, magnitude = distance.to_f, magnitude.to_f
    if distance &lt; 0 || distance &gt; span || @load_points[distance]
      raise ArgumentError, 'invalid load point' 
    end
    @load_points[distance] = magnitude
  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,
# does not matter) is only concerned about asking questions,
# 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
      beam.add_load_point( distance?, magnitude? )
    rescue ArgumentError =&gt; e
      puts "Invalid parameters for load point. Please retry." ; retry   
    end while more? 
    format_reactions( beam.reactions )
  end

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

  # adapted from @nakilon's answer, tail-recursive variant
  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)' )
    answer = prompt( 'please answer by y or n' ) unless answer =~ /^[y|n]$/
    answer == 'y'
  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.