How to store properly a BigDecimal in Ruby on Rails - ruby-on-rails

i have this big problem, i have a input where i format my amounts like this.
15,550.55, the problem is that once i submit that value it store it as 15.50 because of the BigDecimal Object, it is not taking the whole number.
In my Parameters Im recieving that value as "dr_business_remuneration"=>"15,550.55"
The type of data in my postgresql DB is Numeric(19,2) for that attribute
The only thing i want is that once a submit the value be stored as 15550.55, but be shown in my view as 15,550.55.
i tried with a method in my model with a before_save callback, that erase my "," before saving that value.
before_save: montos_formateados
def montos_formateados
if !self.dr_business_remuneration.blank?
self.dr_business_remuneration.sub(",", "").to_f
end
end
but returns this error.
no implicit conversion of String into Integer

There are a few ways to do this. First, if you want it coded to show in terminal correctly, this should work:
'15550.55'.reverse.scan(/.{1,3}/).join(',').reverse
If you're displaying it on a webpage, this is the best:
<%= number_with_delimiter(#number, :delimiter => ',') %>
If you're OK with a few additional dependencies, this is probably the best way:
require 'active_support'
require 'active_support/core_ext/numeric/conversions'
number = 15550.55
number.to_s(:delimited) # => "12,345"
number.to_s(:delimited) # => "12,345.6789"
I hope that helps!

Related

What is the difference between :id, id: and id in ruby?

I am trying to build a ruby on rails and graphQL app, and am working on a user update mutation. I have spent a long time trying to figure it out, and when I accidentally made a typo, it suddenly worked.
The below is the working migration:
module Mutations
class UpdateUser < BaseMutation
argument :id, Int
argument :first_name, String
argument :last_name, String
argument :username, String
argument :email, String
argument :password, String
field :user, Types::User
field :errors, [String], null: false
def resolve(id:, first_name:, last_name:, username:, email:, password:)
user = User.find(id)
user.update(first_name:, last_name:, username:, email:, password:)
{ user:, errors: [] }
rescue StandardError => e
{ user: nil, errors: [e.message] }
end
end
end
The thing I am confused about is when I define the arguments, they are colon first: eg :id or :first_name
When I pass them to the resolve method they only work if they have the colon after: eg id: or first_name:
When I pass the variables to the update method, they use the same syntax of colon after, for all variables other than ID. For some reason, when I used id: it was resolving to a string "id", and using colon first :id was returning an undefined error.
It is only when I accidentally deleted the colon, and tried id that it actually resolved to the passed through value.
My question for this, is why and how this is behaving this way? I have tried finding the answer in the docs, and reading other posts, but have been unable to find an answer.
Please someone help my brain get around this, coming from a PHP background, ruby is melting my brain.
It's going to take some time to get used to Ruby, coming from PHP, but it won't be too bad.
Essentially id is a variable, or object/model attribute when used like model_instance.id. In PHP this would be like $id or $object_instance->id.
When you see id: it is the key in a key-value pair, so it expects something (a value) after it (or assumes nil if nothing follows, often in method definitions using keyword arguments like your example). A typical use might be model_instance.update(id: 25) where you are essentially passing in a hash to the update method with id as the key and 25 as the value. The older way to write this in Ruby is with a "hash rocket" like so: model_instance.update(:id => 25).
More reading on Ruby hashes: https://www.rubyguides.com/2020/05/ruby-hash-methods
More reading on keyword arguments: https://www.rubyguides.com/2018/06/rubys-method-arguments
Now if you're paying attention that hash rocket now uses the 3rd type you're asking about. When you see a colon preceding a string like that it is called a "symbol" and it will take some time to get used to them but they are essentially strings in Ruby that are one character fewer to define (and immutable). Instead of using 'id' or "id" as a string, Ruby folks often like to use :id as a symbol and it will typically auto-convert to a string when needed. A good example might be an enumerator of sorts.
state = :ready
if state == :ready
state = :finished
else
state = :undefined
end
More reading on Ruby symbols: https://www.rubyguides.com/2018/02/ruby-symbols

What is the most elegant Ruby expression for comparing and selecting values from a 2D Array?

I have some code that is chugging through a set of Rails Active Record models, and setting an attribute based on a related value from a 2D Array.
I am essentially setting a US State abbreviation code in a table of US States which was previously only storing the full names. A library of state names is being used to derive the abbreviations, and it contains a 2D Array with each sub-array having a full name, and an abbreviation (i.e., [['New York', 'NY']['Pennsylvania', 'PA'][etc]]). I compare the state name from each record in the database to each full text name in this Array, then grab the corresponding sibling Array cell when there is a match.
This code works fine, and produces the correct results, but its frumpy looking and not easily understood without reading many lines:
# For the following code, StatesWithNames is an Active Record model, which is
# having a new column :code added to its table.
# Sates::USA represents a 2D Array as: [['StateName', 'NY']], and is used to
# populate the codes for StatesWithNames.
# A comparison is made between StatesWithNames.name and the text name found in
# States::USA, and if there is a match, the abbreviation from States::USA is
# used
if StatesWithNames.any?
StatesWithNames.all.each do |named_state|
if named_state.code.blank?
States::USA.each do |s|
if s[0] == named_state.name
named_state.update_column(:code, s[1])
break
end
end
end
end
end
What is the most Ruby style way of expressing assignments with logic like this? I experimented with a few different procs / blocks, but arrived at even cludgier expressions, or incorrect results. Is there a more simple way to express this in fewer lines and/or if-end conditionals?
Yea, there is a few ifs and checks, that are not needed.
Since it is Rails even though it does not state so in question's tags, you might want to use find_each, which is one of the most efficient way to iterate over a AR collection:
StatesWithNames.find_each do |named_state|
next unless named_state.code.blank?
States::USA.each do |s|
named_state.update_column(:code, s[1]) if s[0] == named_state.name
end
end
Also be aware, that update_column bypasses any validations, and if you wish to keep your objects valid, stick to update!.
And last thing - wrap it all in transaction, so if anything goes wrong all the way - it would rollback any changes.
StatesWithNames.transaction do
StatesWithNames.find_each do |named_state|
next unless named_state.code.blank?
States::USA.each do |s|
named_state.update!(:code, s[1]) if s[0] == named_state.name
end
end
end
You might use a different data structure for this.
With your existing 2D array, you can call to_h on it to get a Hash where
a = [['California', 'CA'], ['Oregon', 'OR']].to_h
=> { 'California' => 'CA', 'Oregon' => 'OR' }
Then in your code you can do
state_hash = States::USA.to_h
if StatesWithNames.any?
StatesWithNames.all.each do |named_state|
if named_state.code.blank?
abbreviation = state_hash[named_state.name]
if !abbreviation.nil?
named_state.update_column(:code, abbreviation)
end
end
end
end
the first thing you want to do is convert the lookup from an array of arrays to a hash.
state_hash = States::USA.to_h
if StatesWithNames.any?
StatesWithNames.all.select{|state| state.code.blank?}.each do |named_state|
named_state.update_column(:code, state_hash[named_state.name]) if state_hash[named_state.name]
end
end

Dynamically creating hash key name in Rails 4

Is it possible to dynamically create key names of a hash? I'm passing the following hash parameters:
params[:store][:store_mon_open(5i)]
params[:store][:store_mon_closed(5i)]
params[:store][:store_tue_open(5i)]
params[:store][:store_tue_closed(5i)]
.
.
.
params[:store][:store_sun_open(5i)]
params[:store][:store_sun_closed(5i)]
To check if each parameter exists, I'm using two arrays:
days_of_week = [:mon, :tue, ..., :sun]
open_or_closed = [:open, :closed]
But, I can't seem to figure out how to dynamically create the params hash (the second key( with the array. Here's what I have so far:
days_of_week.each do |day_of_week|
open_or_closed.each do |store_status|
if !eval("params[:store][:store_#{day_of_week}_#{store_status}(5i)").nil
[DO SOMETHING]
end
end
end
I've tried a bunch of things including the eval method (as listed above) but rails seems to dislike the parentheses around the "5i". Any help is greatly appreciated!
You should be able to do
if params[:store]["store_#{day_of_week}_#{store_status}(5i)".to_sym]
Note that you were missing the ? on .nil? and that !object.nil? can be shortened to just object
Assuming this is a HashWithIndifferentAccess, you should be able to access it via string just as you could with a symbol. Thus:
days_of_week.each do |day_of_week|
open_or_closed.each do |store_status|
key = "store_#{day_of_week}_#{store_status}(5i)"
unless params[:store][key]
# DO SOMETHING
end
end
end
If it's not a HashWithIndifferentAccess then you should just be able to call key.to_sym to turn it into a symbol.

Overriding the way Integers print in .erb

I want to print numbers in my rails application with commas. As per the answer here, I could wrap every single number I print with
number_with_delimiter(#number, :delimiter => ',')
However, I don't want to go into my views and apply this manually. I'd much rather override the way integers are printed.
In java/jsp, anything inside a <%= %> tag gets a toString() call to evaluate what is printed on the page, so I figured that overriding the to_s method for the Integer class would do:
class Integer
def to_s
number_with_delimiter(self, :delimiter => ',')
end
end
Unfortunately, this doesn't work in that the numbers printed using the <%=%> tag don't appear with commas. (No errors are raised.)
How do I get this right? Does the <%=%> block not automagically call a to_s method on the given object? How does it evaluate what to print?
You're monkey patching the wrong thing, you want to patch Fixnum. For example:
>> class Fixnum
>> def to_s
>> 'pancakes'
>> end
>> end
=> nil
>> 1.to_s
=> "pancakes"
and for ERB:
>> ERB.new('<%= 11 %>').result
=> "pancakes"
That said, your shortcut (like most shortcuts) will probably end up causing you various new and interesting problems elsewhere. You will end up sending '1,000' to something (such as a database or client-side JavaScript or ...) that expects '1000' and you'll get a confusing hissy fit for your efforts. You'll also have to worry about the other numeric classes such as Float and Bignum.
Fix your views, don't kludge around your own laziness.

Saving values from a time select into a string

I want to have a drop down time select that saves as a string but I keep getting the following exception: 1 error(s) on assignment of multiparameter attributes. Here is the code that I have written:
<%= f.time_select :appointment_time, :minute_step => 5, :ignore_date => true %>
When I submit the form I get the following values in a params hash
"appointment_time(4i)"=>"12", "appointment_time(5i)"=>"00"
Is there any way to easily grab those two values and save the result as "12:00:00"?
Assuming that there are more than just the appointment keys in the params hash:
params[:your_model].select {|k,v| k =~ /^appointment_time/ }.
sort.map {|f| f.last.to_s }.join(':')
Of course, this also assumes that the appointment_time field in your table isn't a DateTime or Date field. You'd have to do this before updating the (assumed) ActiveRecord model instance's attributes. There are simpler ways to do this, but without seeing the code and backtrace it's tough to know whether they'd work.
Edit
processed = params.dup
processed[:your_model][:appointment_time] = params[:your_model].
select {|k,v| k =~ /^appointment_time/ }.sort.map {|f| f.last.to_s}.join(':')
#model.update_params(processed[:your_model])
That's probably more along the lines of what you're looking for.
I solved my problem by creating different columns for each value of the time as strings, and then joining everything together after creation. This is not the most elegant solution, but it works for my application

Resources