How to solve Mathematical Expressions in Rails 4 like 6000*70%? - ruby-on-rails

I am using Dentaku gem to solve little complex expressions like basic salary is 70% of Gross salary. As the formulas are user editable so I worked on dentaku.
When I write calculator = Dentaku::Calculator.new to initialize and then enter the command calculator.evaluate("60000*70%") then error comes like below:
Dentaku::ParseError: Dentaku::AST::Modulo requires numeric operands
from /Users/sulman/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/dentaku-2.0.8/lib/dentaku/ast/arithmetic.rb:11:in `initialize'
I have array is which formula is stored like: ["EarningItem-5","*","6","7","%"] where EarningItem-5 is an object and has value 60000
How can I resolve such expressions?

For this particular case you can use basic_salary = gross_salary * 0.7
Next you need to create the number field in your views which accepts 0..100 range. At last, set up the after_save callback and use this code:
model
after_create :percent_to_float
protected
def percent_to_float
self.percent = percent / 100.0
self.save
end
edit:
Of course, you can simply use this formula without any callbacks:
basic_salary = gross_salary / 100.0 * 70
where 70 is user defined value.

Dentaku does not appear to support "percent". Try this instead
calculator.evaluate('60000 * 0.7')

Related

Define Money Fomat in Laravel

I am trying to save money format in laravel 5.1.
Here is table price define:
$table->decimal(price,6,2);
For instance ; when 1.000,50 Turkish Liras saving to MySQL this format 1.00
How can solve this issue?
You can try defining your price like this
$table->decimal('price',9,3);
Where,
9 is the precision, ie 1234567.89 has a precision of 9
3 is the number of decimal places, ie 123456.789 has a scale of 3
In other words, if we use less decimal-places than 3, we can use remaining for real-number places.
You can refer to this link for about precision and scale of database
How do I interpret precision and scale of a number in a database?
I would suggest not using a float value to store currency as decimals, since floats don't act exactly as you would expect them to, due to the way they are stored in the system.
You would be much better off storing the value in "kuruş" (the subunit of Turkish Lira), as it will be much, much easier in the long run.
In other words, storing the lowest unit you think will be ever required, like storing Centi-meters instead of Meters (Centi is originally Greekish name for "0.01" number).
Secondly, if you're using Eloquent you can use mutators/accessors on the Model e.g.
public function getPriceAttribute($price)
{
return $price / 100;
}
public function setPriceAttribute($price)
{
$this->attributes['price'] = $price * 100;
}
That way you don't have to manually convert the price.
Update
If you're using Laravel 9 or above, you can use the new Attribute syntax instead:
use Illuminate\Database\Eloquent\Casts\Attribute;
protected function price(): Attribute
{
return Attribute::make(
get: fn ($price) => $price / 100,
set: fn ($price) => $price * 100,
);
}
Illuminate blueprints do not support money columns. AFAIK, the money column type itself is only supported in a couple of DBMSes.
What you can do is issue an ALTER TABLE statement to the database after the initial CREATE statement:
Schema::create('my_table', function(Blueprint $table) {
$table->decimal('my_money_column', 999, 2);
// ... the rest of the column definitions
});
DB::statement("
ALTER TABLE my_table ALTER COLUMN my_money_column
SET DATA TYPE MONEY;
");
Beware, though, as this will (probably) break cross-DBMS compatibility of your migration scripts.

Ruby ceil is not working for my data in method, only in view

I have such code in controller's method for rounding (only higher) and display ceil part of number:
#constr_num.each do |cn|
non_original_temp_var2 = get_non_tecdoc_analogs(cn.ARL_SEARCH_NUMBER, #article.supplier.SUP_BRAND, false)
non_original << non_original_temp_var2
end
#non_original = non_original.flatten!
#non_original.each do |n_original|
n_original.price = my_round2(n_original.price * markup_for_user)
end
def my_round2 a
res = (a / 1.0).ceil * 1
res
end
But for some reasons i see with every price comma with 0 after it, for example: 5142.0 but it must be 5142
Main strange part is that, if i try to write:
n_original.price = 123
in view i see 123.0
What happend?
Only when i write in view (when displaying price):
price.ceil
i see normal numbers, without comma
What i di wrong? How to ceil my numbers with rounding (but only high, for example 2.24 is 3 3.51 is 4 and 2.0 is 2)? Becouse now for some reasons i see comma and nul after my number, even if i try to "hardcode" number in controller.
How about using the next or succ function of the Integer class? Try something like the following:
def my_round2 a
(a.is_a? Integer) ? a : a.to_i.next
end
If a is an Integer then return a otherwise cast it to Integer using the to_i method and call next or succ method on it.
Reference: http://www.ruby-doc.org/core-2.0/Integer.html
I guess I missed the second part of your question. To avoid the decimal places I guess you would have to use the a.to_i like Philip Hallstrom has suggested.
My guess is that your price field is a Float. Floats will be printed with a decimal spot by default. You need to either cast it to an Integer earlier on (say in my_round2 method) or in your view task a .to_i onto the output.

Generating Random Fixed Decimal in Rails

I'm trying to generate random data in my rails application.
But I am having a problem with decimal amount. I get an error
saying bad value for range.
while $start < $max
$donation = Donation.new(member: Member.all.sample, amount: [BigDecimal('5.00')...BigDecimal('200.00')].sample,
date_give: Random.date_between(:today...Date.civil(2010,9,11)).to_date,
donation_reason: ['tithes','offering','undisclosed','building-fund'].sample )
$donation.save
$start +=1
end
If you want a random decimal between two numbers, sample isn't the way to go. Instead, do something like this:
random_value = (200.0 - 5.0) * rand() + 5
Two other suggestions:
1. if you've implemented this, great, but it doesn't look standard Random.date_between(:today...Date.civil(2010,9,11)).to_date
2. $variable means a global variable in Ruby, so you probably don't want that.
UPDATE --- way to really get random date
require 'date'
def random_date_between(first, second)
number_of_days = (first - second).abs
[first, second].min + rand(number_of_days)
end
random_date_between(Date.today, Date.civil(2010,9,11))
=> #<Date: 2012-05-15 ((2456063j,0s,0n),+0s,2299161j)>
random_date_between(Date.today, Date.civil(2010,9,11))
=> #<Date: 2011-04-13 ((2455665j,0s,0n),+0s,2299161j)>

How to enforce the number of significant digits of a BigDecimal

I defined a decimal field with a scale / significant digits of 4 in mysql (ex. 10.0001 ). ActiveRecord returns it as a BigDecimal.
I can set the field in ActiveRecord with a scale of 5 (ex. 10.00001 ), and save it, which effectively truncates the value (it's stored as 10.0000).
Is there a way to prevent this? I already looked at the BigDecimal class if there is a way to force scale. Couldn't find one. I can calculate the scale of a BigDecimal and return a validation error, but I wonder if there is a nicer way to enforce it.
You could add a before_save handler for your class and include logic to round at your preference, for example:
class MyRecord < ActiveRecord::Base
SCALE = 4
before_save :round_decimal_field
def round_decimal_field
self.decimal_field.round(SCALE, BigDecimal::ROUND_UP)
end
end
r = MyRecord.new(:decimal_field => 10.00009)
r.save!
r.decimal_field # => 10.0001
The scale factor might even be assignable automatically by reading the schema somehow.
See the ROUND_* constant names in the Ruby BigDecimal class documentation for other rounding modes.

Ruby: Math functions for Time Values

How do I add/subtract/etc. time values in Ruby? For example, how would I add the following times?
00:00:59 + 00:01:43 + 00:20:15 = ?
Use ActiveSupport, which has a ton of built-in date extensions.
require 'active_support/core_ext'
t1 = "#{Date.today} 00:00:59".to_time
t2 = "#{Date.today} 00:01:43".to_time
t3 = "#{Date.today} 00:20:15".to_time
t1.since(t2.seconds_since_midnight+t3.seconds_since_midnight)
or, if you don't care about the date, only time:
t1.since(t2.seconds_since_midnight+t3.seconds_since_midnight).strftime("%H:%M:%S")
For a full list, check out http://guides.rubyonrails.org/active_support_core_extensions.html#extensions-to-date
Kind of ugly, but you could use DateTime.parse(each_interval) & calculate the number of seconds in each. Like this:
require 'date'
def calc_seconds(time_string)
date_time = DateTime.parse(time_string)
hour_part = date_time.hour * 60 * 60
minute_part = date_time.minute * 60
second_part = date_time.second
hour_part + minute_part + second_part
end
...which gives you your result in seconds, assuming valid inputs. At which point you can add them together.
You could reverse the process to get the interval in your original notation.
I really think there ought to be an easier method, but I don't know of one.
One way would be to convert everything to seconds and then performing the operations... Then you would need to convert it again to a time object with
Time.at(seconds_result).strftime('%H:%M:%S')
And you would get the time nicely formatted (as a string).
I am trying to find a gem that does this, and other operations.
You probably want to use a gem that does not concern itself with the actual day. You could perform acrobatics using DateTime and or Time, but you would constantly be battling how to handle days.
One gem that may be useful is tod (TimeOfDay), https://github.com/JackC/tod
With that you could directly do TimeOfDay.parse "00:01:43", add the values, and print the result using strftime("%H:%M:%S").

Resources