How to access translation key for ActiveModel validation error? - ruby-on-rails

I Have a situation, where I wan't to store the translation key for a validation error in my db instead of the error message it self. Imagine the following situation:
class Car < ActiveRecord::Base
validates_presence_of :year, :fuel
end
car = Car.new(:fuel => 'Diesel')
car.save!
#=> ActiveRecord::RecordInvalid
Now if I call:
car.errors
#=> :year=>["can't be blank"]
I get the translated error message.
Instead I would like to extract the translation key that would generate this (in this case I think it would be something like errors.messages.blank), so I can store it in my database in a different model e.g. FailedCar so I can later on generate an I18n customized form for filling in the missing information manually in a view.
UPDATE
I think it's this method that I need to hook into. I want to fetch the key and the options returned, so I can perform the translations again at a later point in time.

Okay I finally got it!
The solution is NOT to patch or do anything on the rails side of things - instead - the answer lies in the I18n gem.
I18n, which rails uses for translations by standard, has the ability to plugin new backends to provide more flexibility to it. In this case, the backend called metadata does exactly what I needed. When adding I18n::Backend::Simple.include(I18n::Backend::Metadata) to an initializer, it gives me the possibility of extracting all translation related information directly from the error message string by adding the method call translation_metadata.
A fantastic simple solution to a complicated problem :-)

Have you looked at (.yml) local files too? You can store it as a translation string. For more Info Rails Internationalization.

Related

Rails - ActiveModel::Serializer virtual attribute error

I'm using the active_model_serializers gem for a RoR API. Versions:
Rails: 4.2.8
Ruby: 2.2.5
active_model_serializers: 0.10.0
I'm using a virtual attribute in a serializer. I get it by using a sub query when I retrieve the objects from the database.
You can find the code here: Github Gist
This is the error I'm getting:
undefined method 'number_of_reservations' for DiscountSchedule...
This field isn't defined in the table nor in the model (attr_accessor)
I'm not sure why it doesn't work, I have a very similar serializer and it's working OK.
Any help will be appreciated.
EDIT:
I have another serializer where the virtual/calculated field is working OK. My guess on this is that since AR is making a bunch of LEFT OUTER JOINS and the SELECT list of the query is very big at some point something is getting broke.
The link won't work for me as I don't have access at my work place, however, from the error I can recommend you to check if you have defined the attributes like this in your serializer attributes :number_of_reservations and have an action in the serializer that says
def number_of_reservations
// Your logic goes here.
end
I suspect this question has to be about ActiveRecord, rather than AMS. You're trying to use select and alias to collect some computed (aggregate) attribute along with objects themselves. This, unfortunately, won't work in ActiveRecord, at least not in versions below 4.2.X. And this is why you're observing this behavior, there is no number_of_reservations in your models.
To see what's going on, try to inspect #objects here: https://gist.github.com/LuisDeHaro/ebf92781b449aa1ee7b85f8f552dd672#file-some_controller-rb-L17
Indeed: the issue was by the big amount of LEFT JOINS that the includes(:table_name) is generating. The serializer then does not know what to do.
I found a monkey-patch gem that works for AR (Rails 4 & 5) that fix this.
https://github.com/alekseyl/rails_select_on_includes
So, the virtual field number_of_reservations is picked up by the serializer like a charm.
And, you might be wondering: why do you want to retrieve a field that is not in the table definition in the database. A: well, in some scenarios you will need a calculated field for EVERY row you are retrieving. A SQL sub query is one of the most efficient ways to do so.
It's working now for me.

Model named "Type" in Rails

Good Day.
I have a problem with my Model named "Type". The error is:
undefined method `all' for ActiveRecord::AttributeMethods::Serialization::Type:Class
In this particular part of validation:
validates :type_id, presence: { message: 'selected is invalid' }, inclusion: { in: 1..Type.all.count }
Maybe it's due to reserved names conflict in Rails. But is there a way that Rails will consider this name before I proceed to refactor? (if nothing else to do).
It's really a bad choice for a name but it's too late.
PS.
When I do 'Type.all.count' in rails console, there are no errors that occured.
Here is a list of reserved words in Rails. As you can see, type has also been reported to cause problems.
Rails is built over convention over configuration agreement, so I would suggest just to pick another model name (which is not mentioned in stated blog post).
Take a look at this thread to get an idea of what should be done to turn your Type model to something else.
In case you are completely sure model name is not the case, you could try to trick around with different validation notations like
inclusion: { in: 1.upto(Type.all.count).to_a}
Ruby's constant lookup is resulting in it finding the "wrong" Type class. Using "::Type" forces ruby to use the top level Type constant.
You should note that this count will be done once only: when the rails instance starts. If new types were to be added, the validation would not take that into account. If a type were ever deleted then it would not allow the last type to be used.
If you have a type association then you could do
validates_presence_of :type
Upon saving it would try and load the corresponding Type object from the database, so saving would fail if there was no such row.
For a really strong guarantee, you could use a foreign key constraint (the foreigner gem adds helpers to rails migrations for this, but you can also just write the sql statement by hand)
Fortunately, I found a workaround. However, it is not recommended but if you don't want to do Migration and ton's of Refractions, here is my solution (credits to twonegatives).
in my validation code:
inclusion: { in: 1.upto(:type_alias) }
then create that method type_alias
def type_alias
Type.all.count
end

Rails ActiveRecord override default find

I'm building an API wrapper that will query objects from a third-party API and build them into objects to be used in my Rails environment. To do that, I'm building a set of models that use ActiveRecord (for some of its functionality) but are not database backed. I would like to be able to make a call like this:
obj = MyModel.find(1)
And have the code be something like this:
def MyModel.find id
# check for object in cache
# check for object in db
# grab object from API
# return object
end
Am I going to do something horribly wrong if I override the default find method? Am I approaching this in totally the wrong way?
If you are not using a database, then you do not need ActiveRecord. The entire purpose of ActiveRecord is to give you a mapping to a relational database.
I think what you want is for a class to implement certain pieces of what ActiveRecord provides, and Rails 3 has made those pieces into classes that you can include into regular 'ol classes on an as-needed basis. Look at this article for more details: http://www.rubyinside.com/rails-3-0s-activemodel-how-to-give-ruby-classes-some-activerecord-magic-2937.html
For instance, if you only want validations on a class, you can use include ActiveModel::Validations and then you'll get all of the nice error handling and .valid? and validates presence: true kind of behavior you're used to.
I would also suggest the railscast by Ryan Bates: http://railscasts.com/episodes/219-active-model which goes into more detail.

How do you internationalize the message being thrown

class Foo < ActiveRecord::Base
...
validates_presence_of :name, :message => "cannot be blank.
...
I am looking for ways to internationalize the message so that a localized message is shown from users from a different locale. How should I do that?
Rails Guides: Translations for active records will give you a hint about it. Basically, you don't have to store the string in your model. You can change it in your locale files.
I18n is one of the good way to internationalize the message.
But if your message customization level is too much then every time you need to modify the ymls and also need to add new yml for different set of users.
In such case you can use 'redis' it gives you more dynamic control. It store data in key => value hash. It is really fast. Following links will help youl
http://redis.io/documentation
http://jimneath.org/2011/03/24/using-redis-with-ruby-on-rails.html
https://github.com/jodosha/redis-store
http://www.engineyard.com/blog/2009/key-value-stores-for-ruby-part-4-to-redis-or-not-to-redis/

Problem with validates_uniqueness_of

I'm using rails 2.3.5 and Authlogic in our website, and I have been getting errors on the database through hoptoad of users with duplicated emails trying to be saved. The problem is, obviously I have the validates_uniqueness_of :email on the model.
On my tests here in development I get the expected validation error and the user is not saved, but in production, I keep getting this errors on the DB layer.
I've tested with case sensitive emails and it also validated correctly.
I've checked and the class and there is no attr_accessor or any other attribute override, and I don't think Authlogic would do it in a wrong way...
What could be happening in production? Are there any cases where rails validates doesn't work?
Locate the SQL running validates_uniqueness_of in your development log, and if you see something like WHERE (email = BINARY 'foo#example.com'), try creating a user with FOO#EXAMPLE.COM and now you can reproduce the DB-level duplicate exception.
To fix this, put the following code in config/initializers/patches.rb:
class ActiveRecord::ConnectionAdapters::Mysql2Adapter
def case_sensitive_equality_operator
"="
end
end
Note that Mysql2Adapter should be MysqlAdapter if you're on Rails 2.
On a side note, it is a longstanding bug in Rails IMO - handling case-sensitivity in Ruby level doesn't make sense at all. If you need case-sensitive lookup, you should have the column collation of utf8_bin. If you need case-insensitive lookup, you should have the column collation of utf8_general_ci. Applying BINARY function in the where clause will disable the use of index, and validates_uniqueness_of causes full table scan every time you try to create/update a record. If you have millions of records, you're totally screwed. The patch above will fix that, too - in fact, it was my original motivation to create that patch.
If you agree, please +1 to https://github.com/rails/rails/issues/1399 :)
Have you tried recreating the scenario. Why should throw errors that warrant Hoptoad Notification. I mean, basically if you have a it should not save the user and not throw an error for hoptoad to notify you about.
Also with authlogic, i don't think you are required to specify the validate_uniqueness_of for email. Usually authlogic will take care of that for you.
So I guess, its time for you to deep dive.
Look at the logs, and try recreating this error locally. Its always best to retrace the steps leading to error.
More details, error stack , code would definitely be helpful.
Just a guess but could it be that your email column allows null, validates_uniqueness_of is ignoring nil (or blank) values and that your users are trying to register without specifying their email addresses?

Resources