Rendering a simple calculation from a controller - ruby-on-rails

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.

Related

How to concatenate symbol in json rails 4

I need to add '%' symbol in 'votes_count' field
My controller,
#celebrity = Celebrity.includes(:category).order(votes_count: :desc)
I am counting the total votes here
total_votes = Vote.count.to_f
changing the total no. of votes to percentage
#celebrity.each do |celeb|
celeb["votes_count"] = (celeb.votes_count / total_votes * 100).round(2)
end
here, I am changing into json
respond_to do |format|
format.json { render json: #celebrity.to_json(:include =>{:category => {:only => [:category]}})}
end
My output is
[{"id":3,"name":"saravana","gender":false,"category_id":"1","votes_count":25}]
My question is how can I add '%' symbol in votes_count
You can just simply do this:
#celebrity.each do |celeb|
votes_count = ((celeb.votes_count / total_votes * 100).round(2))
celeb["votes_count"] = "#{votes_count} %"
end
Do this:
#celebrity.each do |celeb|
celeb["votes_count"] = (celeb.votes_count / total_votes * 100).round(2).to_s << "%"
end
#celebrity.each do |celeb|
celeb["votes_count"] = ((celeb.votes_count / total_votes * 100).round(2)).to_s + "%"
end
The real idea of MVC is to keep the data in its model - unformatted and present it, just how you need it, inside the view
Rails is giving you number_to_percentage
http://apidock.com/rails/ActionView/Helpers/NumberHelper/number_to_percentage
# view.html.erb
<%= number_to_percentage celeb.votes_count %>
// since i remember your last question:
you need to save the percentage at the celibrity and save into DB
if you dont you are loosing performance. imagine you have 1000 celibritys. for that you want to count every page requests 1000 times any percentages? that is stupid. if you just want to count the percentage of a current_selection of celibrities (lets say, the top3 and they are having 60/30/10 %) then your math is wrong since you relate the % to all votes and not to current_selection_votes_count)

Ruby: Unable to do math operations with two arguments

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.

Rails more idiomatic way of adding up values

I am working on a survey app, and an Organisation has 0 or more SurveyGroups which have 0 or more Members who take the survey
For a SurveyGroup I need to know how many surveys still need to be completed so I have this method:
SurveyGroup#surveys_outstanding
def surveys_outstanding
respondents_count - surveys_completed
end
But I also need to know how many surveys are outstanding at an organisational level, so I have a method like below, but is there a more idiomatic way to do this with Array#inject or Array#reduce or similar?
Organisation#surveys_pending
def surveys_pending
result = 0
survey_groups.each do |survey_group|
result += survey_group.surveys_outstanding
end
result
end
Try this:
def surveys_pending
#surveys_pending ||= survey_groups.map(&:surveys_outstanding).sum
end
I'm using memoization in case it is slow to calculate
def surveys_pending
survey_groups.inject(0) do |result, survey_group|
result + survey_group.surveys_outstanding
end
end

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)

Why this application controller won't return correct value to controller who called?

Why do I get empty when I execute this? Asumming User's point is 2500. It should return 83
posts_controller
#user = User.find(params[:id])
#user.percentage = count_percentage(#user)
#user.save
application_controller
def count_percentage(user)
if user
if user.point > 2999
percentage = ((user.point / 5000.0) * 100 ).to_i
elsif user.point > 1999
percentage = ((user.point / 3000.0) * 100 ).to_i
end
return percentage
end
end
It looks like there is some lack of basic understanding, so I try to focus on a few points here.
count_percentage belongs to your model. It is a method which does things that are tied to your User records. In your example, the code can only be used with a User record. Therefore it belongs to your User class!
class User < ActiveRecord::Base
def percentage
if self.point > 2999
return ((self.point / 5000.0) * 100 ).to_i
elsif user.point > 1999
return ((self.point / 3000.0) * 100 ).to_i
else
# personally, I'd add a case for points range 0...3000
end
end
As you said, you want to put that value into your "side menu", so this allows to e.g #user.percentage there which gives you the desired percentage.
According to your description (if I understood it the right way) you want to store the calculated value in your user model. And here we go again: Model logic belongs into your model class. If you want to keep your percentage value up to date, you'd add a callback, like:
class User < ActiveRecord::Base
before_save :store_percentage
def precentage
# your code here
end
private
def store_percentage
self.my_percentage_field_from_my_database = self.percentage
end
end
To your actual problem: user.point may be NULL (db) / nil (ruby), so you should convert it to_i before actually calculating with it. Also that's why I've suggested an else block in your condition; To return a value wether or not your user.point is bigger than 1999 (if it even is an integer.. which I doubt in this case).
To answer on this question you should try to examine user.point, percentage value in the count_percentage method.
I strongly recommend pry. Because it's awesome.

Resources