Is it possible to format all numbers in a rails app to be delimited?
I don't really think this is an i18n issue, as I'm fine with the default delimiter/separator characters. I'm simply trying to avoid putting number_with_delimiter(value) all over my views.
I always want numbers to be displayed as delimited. Always.
So far I've tried extending the Fixnum class with code cribbed from the number_with_delimiter method:
class Fixnum
def delimit(delimiter=",", separator=".")
begin
parts = self.to_s.split('.')
parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{delimiter}")
parts.join separator
rescue
self
end
end
end
> 98734578.delimit
=> "98,734,578"
> 223.delimit => "223"
So this is a step in the right direction- I like the dot notation as well as the slightly shorter method name. But I'd like to apply this to all instances of a Fixnum inside of a view without having to call .delimit.
Is this a bad idea? Should I be worried about implications this will have on numbers outside of the view context? Is there a better way to accomplish this goal?
Related
Rails 3.2
I am working on an application, where it's important to have leading and trailing white spaces removed, before saving to the DB.
I was planning on writing a helper method, to do:
if !self.attribute_name.nil?
self.attribute_name = self.attribute_name.strip
end
and calling it for every single attribute, in the various models, as a before_save action.
Can you think of simpler way to do this?
There are a lot of gems that handle this for you that I use. One is: github.com/rmm5t/strip_attributes
Then you just include it in each model file instead of every controller for create/update actions
Replacements can be done with the gsub methods. These are substitution methods. gsub applies the substitution globally.
You can use gsub also
if !self.attribute_name.nil?
self.attribute_name = self.attribute_name.gsub(" ", "")
end
One of the parameters my API controller receives has a big key name and I need to convert it from string to integer, if it is present, before sending it to the model to be persisted. Usually I would do one of the following:
params[:really_big_key_name] = params[:really_big_key_name].to_i unless params[:really_big_key_name].blank?
or
params[:really_big_key_name] = params[:really_big_key_name].present? ? params[:really_big_key_name].to_i : nil
As you can see, the code line becomes big, with more than 80 characters, and I want to stick with Ruby best practices. Is there a shorter, more Ruby way to do the same? Maybe "in place" methods. Something like arrays do with bang methods. Unfortunately, to_i! does not exist for strings, which is exactly what I need.
You could do this:
params[:really_big_key_name] = params[:really_big_key_name].try(:to_i)
In a Rails view, one can use try to output only if there is a value in the database, e.g
#model.try(:date)
And one can chain trys if, for example, the output is needed as a string
#model.try(:date).try(:to_s)
But what if I need to call a scoped format? I've tried
#model.try(:date).try(:to_s(:long))
#model.try(:date).try(:to_s).try(:long)
What is the correct syntax for this? And what is a good reference for more explanation?
Thanks
From the fine manual:
try(*a, &b)
[...]
try also accepts arguments and/or a block, for the method it is trying
Person.try(:find, 1)
So I think you want:
#model.try(:date).try(:to_s, :long)
This one won't work:
#model.try(:date).try(:to_s(:long))
because you're trying to access the :to_s symbol as a method (:to_s(:long)). This one won't work:
#model.try(:date).try(:to_s).try(:long)
because you're trying to call the long method on what to_s returns and you probably don't have a String#long method defined.
mu is too short's answer shows the correct usage for the try method with parameters:
#model.try(:date).try(:to_s, :long)
However, if you are using Ruby 2.3 or later, you should stop using try and give the safe navigation operator a try (no pun intended):
#model&.date&.to_s(:long)
The following answer is here for historical purposes – adding a rescue nil to the end of statements is considered bad practice, since it suppresses all exceptions:
For long chains that can fail, I'd rather use:
#model.date.to_s(:long) rescue nil
Instead of filling up my view with try(...) calls.
Also, try to use I18n.localize for date formatting, like this:
l #model.date, format: :long rescue nil
See:
http://rails-bestpractices.com/posts/42-use-i18n-localize-for-date-time-formating
In case you often use try chains without blocks, an option is to extend the Object class:
class Object
def try_chain(*args)
args.inject(self) do |result, method|
result.try(method)
end
end
end
And then simply use #model.try_chain(:date, :to_s)
I have an array of objects, some of which respond to :description, and I want to get the description from the first one with a truthy description. I could do this:
objects.detect{|o| o.try(:description)}.description
or this:
objects.map{|o| o.try(:description)}.detect{|o| o}
but the first isn't DRY (description is in there twice) and the second iterates through the whole array before finding the value. Is there anything in the ruby standard library, or in Rails' extensions to it, which would let me do something like this:
objects.detect_and_return{|o| o.try(:description)}
I know I could write it easily enough, but the standard libraries are big enough that I might not need to. Is there a function which works like my detect_and_return?
I haven't seen such a method, and the closest I found was a method capture_first which I found in the gem merb-cache. Seems they stumbled on the same problem and implemented this:
module Enumerable
def capture_first
each do |o|
return yield(o) || next
end
nil
end
end
You could also take a look at the Array and Enumerable methods in the Ruby facets library and see if you find something similar. Facets contains quite a lot of goodies, so you might get lucky.
I am finding myself specifying :rows => 5 on all my text_area form helpers, So I looked up its definition and found the DEFAULT_TEXT_AREA_OPTIONS to be the hash dictating these options. However, the hash has this freeze method on it and I looked it up, it means it can't be changed. If you could recommend me some options to try to do a app-wide :rows => 5 for all text area, I'd really appreciate it.
Thanks
You can do:
Write own helper:
def readable_text_area(form, method, options = {})
form.text_area(method, options)
end
or redefine text_area method delegating to original text_area with proper options
or extend ActionView::Helpers::InstanceTagMethods with your own method "my_text_area" and delegate to original text_area with proper options. Then you can use "f.my_text_area(...)"
or change DEFAULT_TEXT_AREA_OPTIONS:
.
module ActionView::Helpers::InstanceTagMethods
remove_const :DEFAULT_TEXT_AREA_OPTIONS
DEFAULT_TEXT_AREA_OPTIONS = { "cols" => 40, "rows" => 5 }
end
Option 1 is most clean. 2 & 3 patch known public interface - seems acceptable. 4 patches internals - risky.
I'm a fan of:
class ActionView::Helpers::InstanceTag
silence_warnings do
DEFAULT_FIELD_OPTIONS = {}
DEFAULT_TEXT_AREA_OPTIONS = {}
end
end
As #gertas warned this is patching internals so it comes with risk. These constants have moved around in Rails occasionally. But overall it is not a huge deal. Either:
You are using CSS for width and height so these attributes are being ignored anyway. In that case all you are doing is saving a few useless characters from moving across the screen. If it stops working you just spend a few extra characters until you notice it.
You are using these attributes so when the hack stops working it becomes obvious (the field size changes) and you can fix it quickly.
So it does come with risk. But not much and it the most straight forward way to adjust these defaults.