Ruby: Unable to do math operations with two arguments - ruby-on-rails

Please bear in mind that I am fairly new to Ruby. I am currently following a tutorial that is asking me to create a basic calculator. I need to create a Calculator class, that has the following methods; description, add, subtract, multiply and divide.
My initialize method can successfully take two numbers, but I can't seem to get the other methods working.
Here is my code:
class Calculator
attr_accessor :x, :y
def self.description
"Performs basic mathematical operations"
end
def initialize(x, y)
#x = x
#y = y
end
def add(x, y)
x += y.to_i
end
def subtract(x, y)
x -= y.to_i
end
end
I am getting "wrong number of arguments (0 for 2)"

The code is correct, but it doesn't make a lot of sense. You are passing the values to the initializer, therefore I expect your code to be used as it follows
c = Calculator.new(7, 8)
c.add
# => 15
and it's probably the way you are calling it. However, this is not possible because you defined add() to take two arguments. Therefore, you should use
c = Calculator.new(7, 8)
c.add(1, 2)
# => 3
But then, what's the point of passing x and y to the initializer? The correct implementation is either
class Calculator
attr_accessor :x, :y
def self.description
"Performs basic mathematical operations"
end
def initialize(x, y)
#x = x.to_i
#y = y.to_i
end
def add
x + y
end
def subtract
x - y
end
end
or more likely
class Calculator
def self.description
"Performs basic mathematical operations"
end
def initialize
end
def add(x, y)
x.to_i + y.to_i
end
def subtract(x, y)
x.to_i - y.to_i
end
end

Right now your code doesn't make a lot of sense. Your Calculator class initializes with two values, but you never use them. If you really want to initialize with values, your class should look more like this:
class Calculator
attr_reader :x, :y
def self.description
"Performs basic mathematical operations"
end
def initialize(x, y)
#x = x
#y = y
end
def add
x + y
end
def subtract
x - y
end
end
You would then run code like: Calculator.new(3, 5).add which would return 8. You don't need an attr_accessor in this case, just an attr_reader.
Otherwise, you should not initialize with values at all like so:
class Calculator
def self.description
"Performs basic mathematical operations"
end
def add(x, y)
x + y
end
def subtract(x, y)
x - y
end
end
Which you would call like Calculator.new.add(3, 5) returning 8. This approach makes more sense to me, but it seems like the tutorial you are using expects the first approach.
Your current code also is using += and -=, but I'm not sure what you are trying to achieve with this. In your existing code they are pretty meaning less because you are operating on local variables, not your instance variables.

Related

I want to concatenate two values into a single string

I have two different values systolic and diastolic blood pressure readings in string. When those two values come from front-end I'll store them into a single string, e.g., if systolic ='120' and diastolic='80' I want bp='120/80'
module Api
module V1
module CheckinMachine
class BpsController < ApplicationController
include MachineError
before_action :authenticate_user!
def create
raise BatteryNotFunctionalError if battery_functional?
# user = User.find_by!(bp_machine_imei: params[:imei])
health_reading = current.health_readings.create!(key: :blood_pressure, value: bp_value)
Solera::PostActivityApi.call(user,
bp,
health_reading.solera_activities.new)
head :ok
rescue ActiveRecord::RecordNotFound => _e
render_machine_error and return
end
def show
puts params
end
private
def bp
{
systolic_blood_pressure: params[:systolic],
diastolic_blood_pressure: params[:diastolic]
}
end
end
end
end
end
That's what i have tried, what do i do to make it exactly like i want it to be
like bp = '120/80'
Since you already have the 2 values stored in params, this is super easy:
bp = " #{params[:systolic] / #{params[:diastolic]} "
> bp = " 120/80 "
Remember that Ruby has the variable substitution in strings using the #{x} syntax where x is a variable value.
So for instance:
x = "apples"
y = 5
string = "I have #{y} units of #{x} to sell you"
puts(string)
> "I have 5 units of apples to sell you"

Rendering a simple calculation from a controller

I'm sorry if this looks like a very easy (too easy) question but I'm struggling to find a way into working this out.
I need to calculate the Basal Metabolic Rate according to the data enter into a Body model and render it in my show view. The problem is that I can run the calculation in the view but, of course, this is not a proper way.
<%= ((13.397*#user.bodies.last.weight)+(479.9 * #user.bodies.last.height)-(5.677 * #user.bodies.last.age)+(88.362)) * (1 - (#user.bodies.last.fat / 100.00)) %>
this code snippet is in a partial that feeds Body#show view. Of course, I want to have this done at the controller level.
So I wrote a method in my bodies_controller that looks like this:
# Calculate Basal Metabolic Rate for Males
def bmr
#user = User.find(params[:user_id])
#bmr = ((13.397 * #user.bodies.last.weight) + (479.9 *
#user.bodies.last.height) - (5.677 * #user.bodies.last.age) +
88.362) * (1 - (#user.bodies.last.fat / 100.00))
end
When trying to pull the result of this simple calculation into my Body#show view like this: <%= #bmr %> nothing shows. Furthermore typing #bmr into the console returns NIL. How do I feed the Body attributes (Weight, age, height....) into the method?
Sorry if this question sounds stupid but I'm still learning.
Thanks a million in advance!
you can move bmr method to body model as follow
body.rb
class Body < ApplicationRecord
# your other codes
def bmr
((13.397 * self.weight) +
(479.9 * self.height) -
(5.677 * self.age) + 88.362) *
(1 - (self.fat / 100.00))
end
end
for your note: self is equal to last bodies object
and from your view you can call the result as follow
show.html.erb
<%= #user.bodies.last.bmr %>
There are a few pointers I'd give you in solving this more elegantly. Breakdown the problem into more manageable parts, and try to stay away from magic numbers.
class Body
def bmr
#bmr ||= Bmr.calculate(self)
end
end
(below) obviously, replace num1 etc. with descriptive names for those numbers so we have an idea of what they mean in this calculation. Also, consider using constants NUM1 defined at top of class instead of methods.
class Bmr
attr_reader :body
private :body
def initialize(body)
#body = body
end
def self.calculate(body)
new(body).calculate
end
def calculate
((num1 * body.weight) +
(num2 * body.height) -
(num3 * body.age) +
(num4) *
(num5 - (body.fat / 100.00))
end
def num1
13.397
end
def num2
479.9
end
def num3
5.677
end
def num4
88.362
end
def num5
1
end
end
On top of that, you could further break the inner calculations down..
def calculated_weight
num1 * body.weight
end
# then
def calculate
calculated_weight + calculated_height - calculated_age
# etc
end
This gives you a great opportunity to describe the overall calculation and makes it much easier to grasp at a glance, and dig into if necessary.

extract `.for()` into a method

I have a class like this one:
class House
def bricks
Brick.for(#house_plan).where(size: 5)
end
def wood
Wood.for(#house_plan).where(size: 5)
end
end
My goal is to extract the call for(self).where(size: 5):
What I tried first was:
class House
def bricks
Brick.match_material
end
def wood
Wood.match_material
end
def match_material
for(#house_plan).where(size: 5)
end
end
But then I got this error:
syntax error, unexpected '\n', expecting :: or '[' or '.'
Then I changed my code to:
def match_material
.for(#house_plan).where(size: 5)
end
And now when I do:
house = House.new(HousePlan.new)
house.bricks
I get this error:
formal argument cannot be an instance variable
In this line: for(#house_plan).where(size: 5)
What do I wrong?
Your approach isn't right, remember match_material method will always be called in the context of your self. I would do it this way:
def bricks
match_material(Brick)
end
def wood
match_material(Wood)
end
def match_material(klass)
klass.for(#house_plan).where(size: 5)
end
Just out of curiosity:
def bricks
klazz = Kernel.const_get(__callee__[0...-1].capitalize)
klazz.for(#house_plan).where(size: 5)
end
alias :woods :bricks
NB: In this approach aliased methods are to be named consistently(bricks, woods.) Please don’t use it in production unless you understand what you are doing.

How can I cleanly define "antonym" or "opposite" methods in Ruby / Rails?

I'm pretty often defining methods and their antonyms in the code I'm writing, as in:
def happy?
#happiness > 3
end
def sad?
!happy?
end
Which is fine, but I'm a little surprised that Ruby or ActiveSupport doesn't give me something like:
def happy?
#happiness > 3
end
alias_opposite :sad? :happy?
Or am I just looking in the wrong place?
There is no such method in popular libraries, but there is how this could be implemented
class Module
def alias_opposite(a, b)
define_method(a) { !self.send(b) }
end
end
Usage
class A < Struct.new(:happiness)
def happy?
happiness > 3
end
alias_opposite :sad?, :happy?
end
p A.new(1).sad? # => true
p A.new(5).sad? # => false
I suspect this pattern is not as common in ruby because the unless keyword often does the trick:
# ...
clap_your_hands if happy?
stomp_your_feet unless happy?
# ...
Of course, its simple to roll your own:
module Antonymator
def define_antonym(as, of)
define_method(as.to_sym) do |*args|
return !(send(of.to_sym, *args))
end
end
end
# Usage Example
class AreThey
extend Antonymator
define_antonym :uneql?, :eql?
define_antonym :nonconsecutive?, :consecutive?
def eql?(a, b)
a == b
end
def consecutive?(a, b)
a.next == b
end
end
are_they = AreThey.new
puts are_they.uneql? 1, 2 # true
puts are_they.nonconsecutive? 1, 2 # false
If your methods return a Boolean, you can always include the positive method in the negative method.
def drinking_age?(age)
age > #restricted_age
end
def not_drinking_age?(age)
!drinking_age?(age)
end
#restricted_age = 20
Hope that helps.
I guess it depends on what 'opposite' means in the context.

Is it possible to define two methods in a Rails model that require different initialization?

Hi I'm attempting to create a model in Rails that can perform two calculations. This is my code:
class Calculator
def initialize(nair, cppy, interest_rate, payment, periods)
#nair = nair.to_f / 100
#cppy = cppy.to_f
#interest_rate = interest_rate
#payment = payment
#periods = periods
end
def effective
Refinance::Annuities.effective_interest_rate(#nair, #cppy)
end
def principal
Refinance::Annuities.principal(#interest_rate, #payment, #periods)
end
end
I have two forms that reside in different views that take input from the user including 'nair' and 'cppy' on one and 'interest_rate', 'payment' and 'periods' on the other.
The problem I've run into is that to use this model all five arguments need to be available.
Do I need to have separate models for each calculation?
I'm a complete beginning sorry if there is a really obvious answer.
Thanks!
There's probably a dozen different ways you could solve this, but one possible approach would be to use default arguments in your initialize method.
class Calculator
def initialize(nair=0, cppy=0, interest_rate=0, payment=0, periods=0)
#nair = nair.to_f / 100
#cppy = cppy.to_f
#interest_rate = interest_rate
#payment = payment
#periods = periods
end
def effective
Refinance::Annuities.effective_interest_rate(#nair, #cppy)
end
def principal
Refinance::Annuities.principal(#interest_rate, #payment, #periods)
end
end
Another possible solution is to make them class methods and not deal with instances or state:
class Calculator
def self.effective(nair, cppy)
nair = nair.to_f / 100
cppy = cppy.to_f
Refinance::Annuities.effective_interest_rate(nair, cppy)
end
def self.principal(interest_rate, payment, periods)
Refinance::Annuities.principal(interest_rate, payment, periods)
end
end
Calculator.effective(x, y)
Calculator.principal(x, y, z)

Resources