Simple calculation causes syntax error in Ruby - ruby-on-rails

I have a simple financial app in Ruby that can keep track of a user's expenses and generate reports based on them.
Expenses belong to different categories, which affects how much of each expenses is taxes.
In my code to generate a report on expenses, I have this piece:
tax_totals = [0] * 13
totals = [0] * 13
expenses.each do |expense|
tax_ratio = tax_rate/(1+tax_rate)
category = Category.find(expense.category_id).first
tax_ratio *= category.tax_rate.to_f / 100
if !expense.rate_id.nil?
subcategory = Rate.where("id = ?", expense.rate_id).first
tax_ratio *= subcategory.tax_rate.to_f
end
tax_totals[expense.transaction_date.to_date.month] +=
(expense.amount * tax_ratio)
totals[expense.transaction_date.to_date.month] += expense.amount
end
I keep getting a syntax error on the line tax_ratio = tax_rate/(1+tax_rate):
syntax error, unexpected '(', expecting keyword_end
If I remove that line, the error moves to tax_ratio *= category.tax_rate.to_f / 100 line:
syntax error, unexpected tINTEGER, expecting keyword_end
and I have NO IDEA, where this is coming from. I don't see anything wrong with the code at all. I have very similar code in multiple functions each doing the calculations slightly differently. But only this one is an issue.
Maybe it's the lack of caffeine. Is there something wrong with this code? Could there be something else in the file causing this? How can I proceed with debugging?
Cheers!
EDIT: I figured it out. Ruby noob mistake. See answer below.

As written above, this is valid Ruby. I was able to get your code into a method and call it. See below:
require 'active_support/all'
require 'rspec'
class Category
def self.find(category_id)
[new]
end
def tax_rate
0.5
end
end
class Rate
def self.where(*args)
[new]
end
def tax_rate
0.5
end
end
def ratio(expenses, tax_rate)
tax_totals = [0] * 13
totals = [0] * 13
expenses.each do |expense|
tax_ratio = tax_rate/(1+tax_rate)
category = Category.find(expense.category_id).first
tax_ratio *= category.tax_rate.to_f / 100
if !expense.rate_id.nil?
subcategory = Rate.where("id = ?", expense.rate_id).first
tax_ratio *= subcategory.tax_rate.to_f
end
tax_totals[expense.transaction_date.to_date.month] +=
(expense.amount * tax_ratio)
totals[expense.transaction_date.to_date.month] += expense.amount
end
end
describe "#ratio" do
let(:expense) do
double("expense", category_id: 5, rate_id: 6, transaction_date: 5.days.ago, amount: 5)
end
let(:expenses) { [expense] }
let(:tax_rate) { 0.25 }
it "should run" do
ratio(expenses, tax_rate)
end
end

I am new to Ruby and Rails and to me this was the weirdest thing ever.
The error was coming from such an innocent looking line that I didn't even bother to include it in my original question.
tax_rate was a variable that was getting passed to the method. It's stored in a DB as an integer, so I needed to convert it to a decimal point. This was the more complete code:
tax_rate = tax_rate.to_f /100
tax_totals = [0] * 13
totals = [0] * 13
expenses.each do |expense|
tax_ratio = tax_rate/(1+tax_rate)
category = Category.find(expense.category_id).first
tax_ratio *= category.tax_rate.to_f / 100
if !expense.rate_id.nil?
subcategory = Rate.where("id = ?", expense.rate_id).first
tax_ratio *= subcategory.tax_rate.to_f
end
tax_totals[expense.transaction_date.to_date.month] +=
(expense.amount * tax_ratio)
totals[expense.transaction_date.to_date.month] += expense.amount
end
And that first line is what Ruby doesn't like and I still don't know why. But you can't put myVar /100 it has to be myVar / 100 or even myVar/ 100 but there absolutely needs to be a space between / and the number.

Related

doing away with loop result in rails

I am working on a kind of order where I have multiple amount in different currencies in just one attribute. So am trying to make sum on different currencies inside that attribute using other attributes in the table, which works fine but outputs the result as a count of all the rows instead of just showing the sum of the random values calculated.
orders_controller.rb
module Admin
module Statistic
class OrdersController < BaseController
def show
#orders_grid = ::Statistic::OrdersGrid.new(params[:statistic_orders_grid]) do |value|
value.page(params[:page]).per(20)
end
#assets = #orders_grid.assets
##fee_groups = {:fee => #assets.sum(:fee)}
#fee_groups = {
:fee => #assets.sum{|t|
olaoa = t.type
market_string = t.currency
base_currency = market_string.slice(0..2)
quote_currency = market_string.slice(3..5)
if olaoa == 'OrderBid' and base_currency == 'btc'
"#{ t.fee.to_s + ' ' + base_currency.upcase }"
elsif olaoa == 'OrderAsk' and quote_currency == 'ngn'
"#{ t.fee.to_s + ' ' + quote_currency.upcase }"
end
}
}
#orders_filter = true
#orders_group = true
end
end
end
end
summary.htm.slim
.panel.panel-default
.panel-heading
h4.panel-title.row
a data-parent="#filter-accordion" data-toggle="collapse" href="#summary"
span.col-xs-8.text-muted = t('admin.statistic.summary')
span.col-xs-4.text-right.text-muted = t('admin.statistic.click-to-expand')
#summary.panel-collapse.collapse
.panel-body
.datagrid-groups
- if !#orders_group
- if groups
- groups.each do |key, val|
.datagrid.group.row
span.col-xs-2.title = t("admin.statistic.#{controller_name}.#{controller.action_name}.#{key}")
span.col-xs-10.value = val
- if #orders_group
/ Summary Count Loop
- if groups
- groups.each do |key, val|
.datagrid.group.row
span.col-xs-2.title = t("admin.statistic.#{controller_name}.#{controller.action_name}.#{key}")
span.col-xs-10.value = pluralize(val, 'Order')
/ Summary Fees Loop. This is the Fee loop causing problem if am rigth and I dont know how to fix this.
- if #fee_groups
- #fee_groups.each do |key, val|
.datagrid.group.row
span.col-xs-2.title = t("admin.statistic.#{controller_name}.#{controller.action_name}.#{key}")
span.col-xs-10.value = val
The result of the code
So as you can see it renders 0.0BTC 5 times because the filter only has 5 orders. How do i deal with this. I want just the sum of all BTCs to show in the result instead of showing it 5 times.
Any help will be appreciated.
Because you sum strings in you're #fee_groups query that will results in putting strings next to each other instead of a total amount.
If you call it like this
#fee_groups = { fee: #assets.sum{|t| t.fee}}
You will get the total sum of you're assets.

Repeating same block of code over an ActiveRecord Object's attributes in Ruby (Rails)

I have a basic function which takes in different values for weight and reps for a barbell lift, and converts it to an estimated one rep max, which gets stored in my database. Here is what the code currently looks like:
if deadlift_reps != 1
self.deadlift = deadlift_wt * deadlift_reps * 0.0333 + deadlift_wt
else
self.deadlift = deadlift_wt
end
if squat_reps != 1
self.squat = squat_wt * squat_reps * 0.0333 + squat_wt
else
self.squat = squat_wt
end
if benchpress_reps != 1
self.benchpress = benchpress_wt * benchpress_reps * 0.0333 + benchpress_wt
else
self.benchpress = benchpress_wt
end
if overheadpress_reps != 1
self.overheadpress = overheadpress_wt * overheadpress_reps * 0.0333 + overheadpress_wt
else
self.overheadpress = overheadpress_wt
end
I am obviously repeating myself a lot in this code block, but cannot figure out how to break it out and interpolate the method names. I've tried using an array with
lifts = [deadlift, squat, benchpress, overheadpress]
But when I try using each do on this to string interpolate, I am getting an error that lifts doesn't have this method.
Can anyone offer me any advice on how to proceed?
edit:
The loop I was trying to do was:
lifts = [deadlift, squat, benchpress, overheadpress]
lifts each do |lift|
if '#{lift}_reps' !=1
self.#{lift} = '#{lift}_wt' * '#{lift}_reps' * 0.0333 + '#{lift}'_wt
else
self.#{lift} = '#{lift}_wt'
end
end
I can see why this isn't working, but, using a similar idea, how would I make it work?
I would create a private method to calculate every piece of:
if deadlift_reps != 1
...
end
and assign the values to different fields (deadlift, squat, benchpress, overheadpress) using the assign_attributes Active record methods.
Here is the code, I didn't test it, but it should work:
[["deadlift", 20, 8],
["squat", 10, 12],
["benchpress", 30, 6],
["overheadpress", 20, 10]]).each do |exercize, wt, reps|
set_exercize(exercize, wt, reps)
end
private
def set_exercize(exercize, wt, reps)
value = reps != 1 ? wt * reps * 0.0333 + wt : wt
self.assign_attributes(exercize => value)
end
I hope it can help you.

call function until (with call limit)

I have a function that generates random output (string).
I need to call that function until I get 3 different outputs (strings).
What is the most elegant way to generate array with 3 unique strings by calling the function, with the limit how many times the function can be called if the output is not generated in specified number of attempts?
Here's what I currently have:
output = []
limit_calls = 5
limit_calls.times do |i|
str = generate_output_function
output.push str
break if output.uniq.size > 2
end
Can this be beautified / shortened to 1 line? I'm pretty sure in ruby.. :)
Thanks
Using a set makes it (a bit) easier:
require 'set'
output = Set.new
limit_calls = 5
call_count = 0
while output.size < 3 and call_count < limit_calls
output << generate_output_function
call_count += 1
end
output
or with an array
output = []
limit_calls = 5
while output.size < limit_calls and output.uniq.size < 3
output << generate_output_function
end
output.uniq
UPDATE with the call limit. Seems like the Array version wins! Thanks Iain!
Will also ponder a version using inject.
UPDATE 2 - with inject:
5.times.inject([]) { |a, el| a.uniq.size < 3 ? a << generate_output_function : a }
there is your oneliner. I am not sure I prefer it cause it is a bit hard to follow.....
Froderik's answer missed out the call_limit requirement. What about a function like...
def unique_string_array(call_limit)
output = []
calls = 0
until (output.size == 3 || calls == call_limit) do
(output << generate_output_function).uniq! && calls+=1
end
output
end
It isn't a one-liner but it is readable... with this implementation, you may end up with arrays less than size 3. The most important thing is that you have a test that asserts the behaviour you want! (in order to test this thoroughly you'll have to stub out the call to generate_output_function)

How do I initialize a primitive, and add to it?

I'm trying to initialize a variable as a float and then add to it in a times loop. Here's my code:
amt = 0.0
5.times do
amt = amt + ("%.2f" % (item.price * 0.05))
end
Here's the error:
String can't be coerced into Float
EDIT:
I've changed the 1.times do. I had adapted my code so that it would be more readable, the 1.times wasn't in my actual code.
Firstly, try not to use floats for money - what you'll need to do in this case is use BigDecimal - floating point math is not accurate.
To use BigDecimal, require 'bigdecimal' in your application.rb
Then, what you want is:
amt = BigDecimal.new("0")
1.times do
price = BigDecimal.new(item.price.to_s)
factor = BigDecimal.new("0.05")
amt += (price * factor).round(2)
end
amt
Yeah sorry everyone. I am new to Ruby so I didn't know that this was the problem, but the "%.2f" was causing the problem. (Although irrelevant to this question, what this does is convert the amount into two decimal precision.) Stupid mistake!

LocalJumpError (Ruby on Rails)

I have searched/Googled around but I'm struggling with the following problem.
I am building a Rails 2.3.2 application and one of the requirements is to calculate the median of an array of results. I am using code for calculating the median from the Ruby Cookbook but keep running in to a problem with receiving an error 'LocalJumpError - no block given' when I attempt to find the median of an array where there are an odd number of members.
The example code in my view is as follows:
<%= survey_response.median([6,4,5,4,4,2]) %>
Then in survey_response.rb model the methods are as follows:
def mean(array)
array.inject(array.inject(0) { |sum, x| sum += x } / array.size.to_f)
end
def median(array,already_sorted=false)
return nil if array.empty?
array = array.sort unless already_sorted
m_pos = array.size / 2
return array.size % 2 == 1 ? array[m_pos] : mean(array[m_pos-1..m_pos])
end
The error is caused when the median method refers back to the mean method to get the media of an odd total of items in the array. I just can't figure out why I get that error or indeed how to fix it - so I'd hugely appreciate any help/guidance/laughing anybody could offer me!
Thanks
Simon
Lis looks like it's due to you using a fractional index into the array. Try replacing:
m_pos = array.size / 2
with:
m_pos = (array.size / 2).ceil
Also, try changing your mean function to this:
def mean(array)
array.inject(0) { |sum, x| sum += x } / array.size.to_f
end
That mean method looks horribly botched. Try this:
def mean(array)
a.inject(0) { |sum,x| sum += x } / a.size.to_f
end
Better code:
def mean(array)
array.inject { |sum, n| sum + n } / array.length.to_f
end
def median(array)
return nil if array.empty?
array.sort!
middle = array.length / 2
(array.length % 2 == 1) ? array[middle] : mean([array[middle-1], array[middle]])
end
puts median([5,11,12,4,8,21]) # => 9.5

Resources