store utf8 strings in db, but validate and parameterize transliteriated - ruby-on-rails

In my app there's a number of routes like :user_login/resource/resource_name
example:
:vlad/photo_albums/my-first-album
Users should be able to create an albums with their native language namings ( in my case its russian ). But if user named his album as, for example, "Привет, Мир!" ( which is "Hello World!" in English), I want to use a string where all letters of the Russian alphabet are replaced by similar-sounding Latin in a resource link. E.g, user provides album title "Привет Мир!" and the corresponding link looks like 'vlad/photo_albums/privet-mir'.
I've made all necessary methods to transform russian to latin, but now I'm trying to find the best way to arrange all this.
First issue is that I need to find album by it's title:
#album = #user.albums.
where( :title => params[:album_title] ).first
redirect_to user_albums_path(#user) unless #album
I would really want to avoid using anything but latin in my sql statements.
Second issue is that I don't want to run validations on non-latin string ( should I be? ) so I want to latinize and parameterize it before validation, but still save the original string if it's latinized version passed the validation:
validates :title, :presence => true, :length => { :within => (2..25) },
:allow_blank => false, :uniqueness => { :scope => :user_id }
What I was thinking about to accomplish this were hash serialization like {:latin_version => ..., :original_version => ..} or separate yaml file concepts.
I need your thoughts on how to arrange this properly and what would be the most elegant way. Or am I to pedantic about it? Would it be fine to just store/search for/validate/display non-latin characters?

It's completely fine to store, validate and search non-latin characters. Most Ruby on Rails companies that provide multilingual and international versions of their applications use UTF-8 in the application and database layers. UTF-8 can be properly parameterized, displayed and validated in Ruby on Rails and all major browsers so you should not see any issues there. The best way to handle this is to set your database encoding and/or table string encoding to UTF-8 and then your Ruby on Rails encoding in application.rb:
config.encoding = "UTF-8"
If you are using MySQL or Postgres you will probably also want to be explicit about the database encoding in your database.yml file:
development:
adapter: mysql2
database: development_db
user: root
password:
encoding: utf8

Related

Validate Numericality Conditions on English & Arabic Numbers

I'm using the following code to validate numericality in Rails:
validates :number, :numericality => {:greater_than_or_equal_to => 500}
And it works well, but then I added regex to allow Arabic numbers like this:
validates :number, :format =>{:with => /[0-9\u0660-\u0669]*/}
Here it accepts Arabic numbers but the condition greater_than_or_equal_to => 500 is working only on English numbers and I need it to support Arabic numbers too.
Alright, the digit can be defined as [0-9] and combined with Arabic ones, [0-9\u0660-\u0669]. We want to match 500 up to 999 first. We need to combine [5-9] with [\u0665-\u0669] -> [5-9\u0665-\u0669] that will match 5 to 9 in both ASCII and Arabic notations. After 5-9, there can be 2 more any digits, so we need to append [0-9\u0660-\u0669]{2} to it.
Next, we need to allow numbers more than 999, that is, 1000 and more. So, we need to add an alternative branch [0-9\u0660-\u066‌​9]{4,}.
The whole result is
/\A([5-9\u0665-\u0669][0-9\u0660-\u0669]{2}|[0-9\u0660-\u066‌​9]{4,})\z/
Where \A matches the start of string, \z matches the end of the string and (Branch_1|Branch_2) is a grouping construct that may be turned into a non-capturing one by adding ?: after the initial (:
/\A(?:[5-9\u0665-\u0669][0-9\u0660-\u0669]{2}|[0-9\u0660-\u066‌​9]{4,})\z/
See the regex demo
For ROR users who want to avoid using regex in such case to allow Arabic numbers to be working with Integer DB field (not string), I've found here a good example to convert Unicode numbers to Integer before validation, so it's still possible to use such condition:
validates :number, :numericality => {:greater_than_or_equal_to => 500}

Multilingual support for custom validation messages in Rails

Hi let's say I have the following in my accounts model:
validates :name, length: {in: 1..70, message:%Q|Please enter a decent name Sr.|}
How can I add multilingual support to those custom validation messages? I checked this tutorial
But I could not find out how to translate custom validation messages in the model.
I needed once to use translations in model so I went this way:
TITLE = { 0 => :"employee.title.mrs",
1 => :"employee.title.mr",
2 => :"employee.title.miss" }
these are options for select, and in select I used t(value_of_key_here), value was a string that was seen as a key to locale.
So in your case this might work (not really sure):
validates :name, length: {in: 1..70, message: :"enter_decent_name"}
that would return a key in your validation messages and rails will just complain about missing key in translations that you have to add into your yml file:
enter_decent_name: 'Please enter a decent name Sr.'

PG full text search on rails using pg_search gem for substring

I am using Pg full text search for my search . As i am using Ruby on rails, I am using pg_search gem. How do i configure it to give a hit for substring as well.
pg_search_scope :search_by_detail,
:against => [
[:first_name,'A'],
[:last_name,'B'],
[:email,'C']
],
:using => {
:tsearch => {:prefix => true}
}
Right now it gives a hit if the substring is in the start but it wont give a hit if the substring in the middle
example It gives a hit for sdate#example.com but not for example.com
I'm the author and maintainer of pg_search.
Unfortunately, PostgreSQL's tsearch by default doesn't split up email addresses and allow you to match against parts. It might work if you turned on :trigram search, though, since it matches arbitrary sub-strings that appear anywhere in the searchable text.
pg_search_scope :search_by_detail,
:against => [
[:first_name,'A'],
[:last_name,'B'],
[:email,'C']
],
:using => {
:tsearch => {:prefix => true},
:trigram => {}
}
I confirmed this by running the following command in psql:
grant=# SELECT plainto_tsquery('example.com') ## to_tsvector('english', 'name#example.com');
?column?
----------
f
(1 row)
I know that the parser does detect email addresses, so I think it must be possible. But it would involve building a text search dictionary in PostgreSQL that would properly split the email address up into tokens.
Here is evidence that the text search parser knows that it is an email address:
grant=# SELECT ts_debug('english', 'name#example.com');
ts_debug
-----------------------------------------------------------------------------
(email,"Email address",name#example.com,{simple},simple,{name#example.com})
(1 row)

rails 3.1 multiple currencies

I have to add more currencies to the app like on this web app http://www.designconnected.com/
as you can see, it converts the price in whatever currency you select and keep it this way.
I've been looking for gems that are out of date already, tutorials couldn't find any and in stackoverflow there are a few questions about it but none of them got what I need.
if any of you know a better gem, recently released... please let me know.
or if there is no gem for it, should I add a currency_id to current_user so the app will show the proper currency for this user.. but then where do I take the currency rates from.. I've been looking for a solution for 3 days now and nothing.
Thank you for any given advice..
this urls have been checked:
https://stackoverflow.com/questions/1368010/rails-currency-gem-or-plugin
Rails 3 - Multiple Currencies
https://github.com/RubyMoney/money
the last one in combination with https://github.com/RubyMoney/google_currency looks like it's the one I need.. but now would be the right time to get a tutorial on how to use this.
Please help with some ideas on how to get it started if there is no way to find/get a full tutorial about this.
Thank you.
https://github.com/RubyMoney/money-rails and https://github.com/RubyMoney/google_currency is the way to go. It's not what I asked or in the question, but anyway it's the closest answer I have right now. These are a few steps I did to get this working:
In gem file:
gem "json" #if you don't have it
gem "money"
gem "google_currency"
Create a file money.rb in config/initializers
require 'money'
require 'money/bank/google_currency'
require 'json'
MultiJson.engine = :json_gem
Money.default_bank = Money::Bank::GoogleCurrency.new
In product.rb (or whatever model you have that needs the price to be converted)
composed_of :price,
:class_name => "Money",
:mapping => [%w(price price), %w(currency currency_as_string)],
:constructor => Proc.new { |price, currency| Money.new(price || 0, currency || Money.default_currency) },
:converter => Proc.new { |value| value.respond_to?(:to_money) ? value.to_money : raise(ArgumentError, "Can't convert #{value.class} to Money") }
And in the view file:
<%= number_to_currency(product.price.exchange_to(:EUR)) %>
For exemple, I have an IT locale (Italian language) - Italy currency now is Euro:
You'll have the prices converted in EUR.. It worked for me really nice, money gem convert the price from USD in EUR using Google_currency, and the locale yml file changes the Currency for this locale so you'll have the price looking like XXX,XX EUR and not $XXX,XX.
To display the right currency for each locale you need to add:
it:
number:
currency:
format:
format: "%n %u"
unit: "EUR"
In it.yml file or for other language you have with currency for that country.
You probably don't need a gem for this. You can make a call directly to google's currency API at any point in your code using the URL's explained here. This could be done in your model or via AJAX directly in the view.
http://motyar.blogspot.com/2011/12/googles-currency-converter-and-json-api.html

Where should I store a list of select options in Rails 3?

Using Rails 3.1.3 and Ruby 1.9.3.
I want to give the user a list of possible date/time formats. The user's selection will be stored in the Users table. Date/time values are then formatted using the I18n.localize function. I actually have 10 formats; here by way of example are the first two:
config/locales/datetime.en.yml
en:
time:
format_labels:
mdyslash12: mm/dd/yyyy - hh:mm am (12-hour)
mdyslash24: mm/dd/yyyy - hh:mm (24-hour)
formats:
mdyslash12: ! '%m/%d/%Y %I:%M%p'
mdyslash24: ! '%m/%d/%Y %H:%M'
My question is where to store the list of possible date/time formats. I've identified three possibilities.
1. List options as a CONSTANT in model:
app/models/user.rb
DATETIME_FORMATS = %w[mdyslash12 mdyslash24]
validates :datetime_format, :presence => true,
:inclusion => { :in => DATETIME_FORMATS }
2. Create an application constant and validate against that:
config/initializers/constants.rb
Rails.configuration.datetime_formats = "mdyslash12 mdyslash24"
app/models/user.rb
validates :datetime_format, :presence => true,
:inclusion => { :in => Rails.application.config.datetime_formats.split(" ") }
3. Validate directly against the locale file:
app/models/user.rb
validates :datetime_format, :presence => true,
:inclusion => { :in => (I18n.t 'time.format_labels').stringify_keys.keys }
This option uses a feature that is new to me: I18n.t 'time.format_labels' returns a hash of ALL keys and values from that branch of the locale file. The hash keys are symbols, so to get a string array, I call stringify_keys to convert the symbols to strings, then keys to give me only the keys (no values).
Option #3 is the DRYest in that I don't have to list the possible values in two places. But it doesn't feel quite right to depend on the locale file for the discreet list of possible date/time formats.
What would you recommend? One of these options? Something else?
I'd go with option 1 to start with, since it's simple, clear, and fairly DRY. I might refactor to option 2 if I ended up needing that constant in another model.
Option 3 has the potential to behave differently based on the locale, so I don't like that. If you end up forgetting to specify your format labels in a new locale, your selection list might end up being empty (or if there's a typo in one locale, it might take longer to notice, since the typo would be treated as valid for that locale). Regardless it's probably a good idea to unittest this in all your supported locales.

Resources