How do I change the separator in parameterize for Rails - ruby-on-rails

This seems like a dumb question, but how do I use parameterize in Rails? I've seen this doc: http://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-parameterize
In my Model I can get string.parameterize to work, but I don't understand how to use the separator param. parameterize(string, separator: '') says I can't use parameterize on main, and string.parameterize(separator: '') says can't implicitly convert from Hash to String

string.parameterize without specifying any character will give you your string separated by words, removing any character that's not a letter, and "joining" them with '-':
string = 'Donald E. Knuth'
string.parameterize
# => "donald-e-knuth"
This way specifying a separator:
string.parameterize(separator: '*')
# => donald*e*knuth
The method acts using the I18n.transliterate method to the passed string, and then applying a destructive gsub! which will check any non-letter character and apply the substitution, is like to do:
# without separator specified
I18n.transliterate(string).gsub!(/[^a-z0-9\-_]+/i, 'separator')
# => Donald-E-Knuth
So this way if there's no separator specified, the method has one already defined as its third parameter:
def parameterize(string, sep = :unused, separator: '-', preserve_case: false)
...
end
Note the use is first the string and then the parameterize method call, unlike what the documentation exemplifies.
Note: Tested on ruby 2.3.1 and Rails 5.0.2 it works well, Rails 4.2.5, 4.2.6 (as you say) and 4.2.7 throws this error:
Loading development environment (Rails 4.2.5)
> string = 'x y z'
# => "x y z"
> string.parameterize(separator: '*')
TypeError: no implicit conversion of Hash into String
In Rails < 5 it must be used as ActiveSupport::Inflector.parameterize(string, separator):
> #item = Item.first
# => #<Item id: 1, name: "new item" ...>
> ActiveSupport::Inflector.parameterize(#item.name, '*')
# => "new*item"

Take a look at this for more info:
parameterize("Donald E. Knuth", separator: '_') # => "donald_e_knuth"
parameterize("^très|Jolie__ ", separator: '_') # => "tres_jolie"
This method comes from the ActiveSupport::Inflector class, so you can do:
ActiveSupport::Inflector.parameterize("Hello World Yeah!", separator: "*")
# => "Hello*World*Yeah!"

Related

Rails helper pass negative symbol as argument in self method

In my Rails 6, Ruby 2.7 app I'm using ActionView::Helpers::NumberHelper and number_to_currency method. Everything works fine but in one place I need to have negative amount instead of positive. To do so I created two methods:
formatters/document_amount_formatter.rb
module Formatters
# Formats the amount in a suitable form to be used in PDF creator.
class DocumentAmountFormatter
extend ActionView::Helpers::NumberHelper
# The method to call.
#
# #return [String]
def self.call(amount)
number_to_currency(amount.to_f, delimiter: '.', separator: ',', format: '%n €')
end
def self.negative_amount(amount)
number_to_currency(-amount.to_f, delimiter: '.', separator: ',', format: '%n €')
end
end
end
Both works well:
Formatters::CashbookDocumentAmountFormatter.call(cash_transactions.first.gross_amount)
=> "100,00 €"
Formatters::CashbookDocumentAmountFormatter.negative_amount(cash_transactions.first.gross_amount)
=> "-100,00 €"
But I'm not so sure if this is a good approach, tbh the code seems to be smelly. Is it possible to change those two methods into one? How to pass '-' or '+' as an argument inside of one of these methods?
Call call from within negative_amount.
def self.negative_amount(amount)
call(-amount)
end
The next question being, why have this method at all? The caller can write formatter.call(-amount) just as easily and more obviously.
Note that you probably shouldn't hard-code currency formatting and instead make use if internationalization.

Remove , from all integers in rails

All integers are by default delimited by ',' .
For ex: 123456 is shown as 1,23,456.
Is there a way to remove ',' from all integers for all tables.
If it is for a single table and a particular field, we can do in the following way in its controller.
config.columns[:<int_field>].options={:i18n_options => {delimiter: ""}}
Is there a way to do this for all integer fields?
PS: Using Activescaffolding in my application.
Thanks.
Use tr as:-
2.0.0-p645 :005 > "1,23,456".tr(',', '')
=> "123456"
Convert the result to integer as:-
2.0.0-p645 :005 > "1,23,456".tr(',', '').to_i
=> 123456
Check methods in string in irb as:-
"".methods
Use it in view as:-
<%= "1,23,456".tr(',', '').to_i %>
For more details see the document
I assume Rails with Activescaffold uses formatting rules from its I18n module by default. You can change them in config/locales/en-US.yml. Here is the default one with the relevant lines highlighted.
This should affect all formatting of numbers in views. I think you just have to change the delimiter to a blank string:
...
format:
delimiter: ""
...
You can find general information about Rails I18n in the corresponding I18n guide.
This worked perfectly.
Added the following lines in application_helper.rb:
def format_number_value(value, delimiter = '')
if value.is_a? Integer
ActiveSupport::NumberHelper.number_to_delimited(value, delimiter: '')
else
super
end
end
Thanks.

Interacting programmatically with the equivalent of puts -ruby

I have a string, that I need to save escaped and then need to interact with programmatically without any backspaces:
string = 'first=#{first_name}&last=#{last_name}'
p string.to_s
=> "first=\#{first_name}&last=\#{last_name}"
puts string.to_s
=> first=#{first_name}&last=#{last_name}
How do I get first=#{first_name}&last=#{last_name} to assign to a variable that I can scan, that does not have the "\" character?
These two are equivalent:
# double quotes
"first=\#{first_name}&last=\#{last_name}"
# single quotes
'first=#{first_name}&last=#{last_name}'
In neither case is the backslash actually part of the string. If say string.include? '\' it will return false.
However, if you were to say '\#{}' the backslash would be part of the string. That's because in single quotes, #{} does not interpolate but is interpreted as literal characters.
Some example:
foo = 1
'#{foo}' # => "\#{foo}"
"#{foo}" # => "1"
'#{foo}' == "\#{foo}" # => true
"\#{foo}".include? '\' # => false
'\#{foo}'.include? '\' # => true
Note that "\" is an invalid string in ruby, but '\' is valid.

How can I write quoted values in en.yml?

I'm writing a script that will add new translations to the en.yml file. However, when I'm dumping them back to the file, my strings are in the following format:
some_key: This is the value
I'm trying to make the output be:
some_key: "This is the value"
I'm writing the translations like this:
File.open(yaml_file, "w") do |f|
f.write(translations.to_yaml)
end
Where translations is the hash containing all the translations.
Is there any way of adding these quotes, besides manually parsing/rewriting the YAML file?
The plan (unquotes) scalar representation is the preferred version when the scalar type doesn't require escaping.
In your case, the String:
This is the value
doesn't need to be in quotes, thus, if you supply the following YAML:
key: "This is the value"
the processor may return:
key: This is the value
because they are totally equivalent. However, if you actually want to enter a quoted string as value, then you should use:
key: '"This is the value"'
or escape the double quote:
key: "\"This is the value\""
I gave a quick look at the Psych emitter code, the one invoked by the to_yaml, and there doesn't seem to be an option to force quoting on scalar.
I don't even see the option implemented in the scalar emitter code.
def visit_Psych_Nodes_Scalar o
#handler.scalar o.value, o.anchor, o.tag, o.plain, o.quoted, o.style
end
In other words, you cannot enforce quoting.
Updated for hash conversion
def value_stringify(hash)
hash.each do |k,v|
if v.kind_of? Hash
hash[k]= value_stringify(v)
else
hash[k]="\"#{v}\""
end
end
end
Now use the converted hash to store yaml.
File.open(yaml_file, "w") do |f|
f.write(value_stringify(translations).to_yaml)
end
Now it should work..
The format you get is valid YAML. However, if you really want this you could temporarily modify your data before converting it.
Normal:
{ foo: "bar" }.to_yaml
# => foo: bar
With an space after:
{ foo: "bar " }.to_yaml
# => foo: 'bar '
Note that you get single quotes and not double quotes. So if you temporarily modifying your data you could add in an placeholder which you remove later.
Example:
{ foo: "foo --REMOVE-- ", bar: "bar --REMOVE-- " }.to_yaml
.gsub(' --REMOVE-- ', '')
# => foo: 'foo'
# bar: 'bar'

Overloading ActiveSupport's default to_sentence behaviour

ActiveSupport offers the nice method to_sentence. Thus,
require 'active_support'
[1,2,3].to_sentence # gives "1, 2, and 3"
[1,2,3].to_sentence(:last_word_connector => ' and ') # gives "1, 2 and 3"
it's good that you can change the last word connector, because I prefer not to have the extra comma. but it takes so much extra text: 44 characters instead of 11!
the question: what's the most ruby-like way to change the default value of :last_word_connector to ' and '?
Well, it's localizable so you could just specify a default 'en' value of ' and ' for support.array.last_word_connector
See:
from: conversion.rb
def to_sentence(options = {})
...
default_last_word_connector = I18n.translate(:'support.array.last_word_connector', :locale => options[:locale])
...
end
Step by step guide:
First, Create a rails project
rails i18n
Next, edit your en.yml file: vim config/locales/en.yml
en:
support:
array:
last_word_connector: " and "
Finally, it works:
Loading development environment (Rails 2.3.3)
>> [1,2,3].to_sentence
=> "1, 2 and 3"
As an answer to how to override a method in general, a post here gives a nice way of doing it. It doesn't suffer from the same problems as the alias technique, as there isn't a leftover "old" method.
Here how you could use that technique with your original problem (tested with ruby 1.9)
class Array
old_to_sentence = instance_method(:to_sentence)
define_method(:to_sentence) { |options = {}|
options[:last_word_connector] ||= " and "
old_to_sentence.bind(self).call(options)
}
end
You might also want read up on UnboundMethod if the above code is confusing. Note that old_to_sentence goes out of scope after the end statement, so it isn't a problem for future uses of Array.
class Array
alias_method :old_to_sentence, :to_sentence
def to_sentence(args={})
a = {:last_word_connector => ' and '}
a.update(args) if args
old_to_sentence(a)
end
end

Resources