I made this math script, where you enter an equation and it solves it for you. I was wondering what you thought of the code, and what I could have done better or what doesn't follow Ruby best practices. The code is also on Github here if you want to see the readme and how to use define_equation()
#!/usr/bin/env ruby
require "readline"
require "cmath"
# PREFIXES = { -15 => "f", -12 => "p", -9 => "n",
# -6 => "µ", -3 => "m", 0 => "",
# 3 => "k", 6 => "M", 9 => "G",
# 12 => "T", 15 => "P", 18 => "E" }
def define_equation(file)
File.foreach(file) { |line|
name, var, equation = line.split(":")
Equations.class_eval %[
def #{name}(#{var})
#{equation}
end
]
}
end
def all_formulas
(Equations.instance_methods(false) - [:method_missing])
.map { |i| i.to_s.gsub(/_/, "-") }
end
class Equations
C = 3e8
def m_with_hz(frequency)
C / frequency
end
alias_method :hz_with_m, :m_with_hz
def qudrtc_frml_with_a_b_c(a, b, c)
positive = (-b + CMath.sqrt(b ** 2 - 4 * a * c)) / 2 * a
negative = (-b - CMath.sqrt(b ** 2 - 4 * a * c)) / 2 * a
# possibly add positive and negative .to_r as well as the float
[positive, negative]
end
def pythgrn_thrm_with_a_b(a, b)
a = Math.sqrt(a ** 2 + b ** 2)
a = "sqrt(#{(a ** 2).round})" unless a.to_i == a
a
end
def pythgrn_thrm_with_b_c(b, c)
a = Math.sqrt(c ** 2 - b ** 2)
a = "sqrt(#{(a ** 2).round})" unless a.to_i == a
a
end
def method_missing(m, *args, &block)
"That equation doesn't exist here. (yet)"
end
end
class Cli
attr_reader :equations
def initialize
@equations = Equations.new
end
def help
["To use an equation, just type something like this: hz-with-m 100",
"Thats 100 meters, converted to hertz",
"Or something like this: pythgrn-thrm-with-b-c 4 5",
"That's a^2 = 5^2 - 4^2, or more familiarly, a^2 + 4^2 = 5^2",
"You also can use e for scientific notation. Eg. 3e5 = 300000",
"Type quit to quit",
"",
"Available Equations:",
all_formulas]
end
def method_missing(m, *args, &block)
# checks if it only contains numbers, math symbols, and e
if args.all? { |arg| arg.match(/\A[\d+\-*\/.e ]+\z/) }
args.map! { |arg| eval(arg) }
@equations.send(m, *args)
else
"Invalid number"
end
end
def quit
exit
end
end
cli = Cli.new
case ARGV[0]
when "-l", "--load"
ARGV.shift
define_equation(ARGV.shift)
# I will add more stuff here
end
if __FILE__ == $0
puts "Type help for help."
Readline.completion_append_character = " "
Readline.completion_proc = proc { |s|
all_formulas.grep(/^#{Regexp.escape(s)}/)
}
loop do
command, *variables = Readline::readline("> ", true)
.downcase.strip.squeeze(" ").split(" ")
unless command.nil?
command.gsub!(/-/, "_")
puts begin
cli.send(command, *variables)
rescue ArgumentError
argc = cli.equations.method(command).arity
arg_list = (1..argc).map { |i| "<number_#{i}>" }.join(" ")
"Usage: #{command} #{arg_list}"
end
end
end
end
Here are the tests:
#!/usr/bin/env ruby
require "rubygems"
gem "minitest"
require "minitest/autorun"
require "minitest/pride"
load "solve"
describe Equations do
before do
@equations = Equations.new
end
describe "m_with_hz" do
it "must return correct value" do
@equations.m_with_hz(300e12).must_equal 1.0e-06
@equations.m_with_hz(3e6).must_equal 100
end
end
describe "hz_with_m" do
it "must return correct value" do
@equations.hz_with_m(100e3).must_equal 3e3
@equations.hz_with_m(100.0e-12).must_equal 3e18
end
end
describe "qudrtc_frml_with_a_b_c" do
it "must return correct value" do
@equations.qudrtc_frml_with_a_b_c(1, 2, -3).must_equal [1.0, -3.0]
end
end
describe "pythgrn_thrm_with_a_b" do
it "must return correct value" do
@equations.pythgrn_thrm_with_a_b(3, 4).must_equal 5
@equations.pythgrn_thrm_with_a_b(4, 5).must_equal "sqrt(41)"
end
end
describe "pythgrn_thrm_with_b_c" do
it "must return correct value" do
@equations.pythgrn_thrm_with_b_c(4, 5).must_equal 3
@equations.pythgrn_thrm_with_b_c(3, 4).must_equal "sqrt(7)"
end
end
describe "equation does not exist" do
it "must warn user" do
@equations.doesnt_exist(500).must_equal "That equation doesn't exist here. (yet)"
end
end
end
describe Cli do
before do
@cli = Cli.new
@error_msg = "Invalid number"
end
describe "an equation is ran" do
it "must warn user if an invalid number is used" do
@cli.doesnt_matter("hello").must_equal @error_msg
@cli.doesnt_matter("34five").must_equal @error_msg
@cli.doesnt_matter("q12").must_equal @error_msg
end
it "must work if a valid equation is ran" do
@cli.doesnt_matter("123.3").wont_equal @error_msg
@cli.doesnt_matter("12e3").wont_equal @error_msg
@cli.doesnt_matter("1e-4").wont_equal @error_msg
end
end
end
describe "flags" do
describe "--load" do
before do
@cli = Cli.new
end
it "must load equations" do
# in progress
define_equation("sample_equations")
end
end
end
# write tests for importing equations