NOTE:
- The behavior and shortcomings explained below apply to Mongoid versions 2.4.0 (released on 5th Jan, 2012) and releases previous to that. A recent commit made on 10 Jan, 2012 fixes all these shortcomings.
- For those using the affected versions (all Rails 3.0 developers), this monkey patch will address the shortcomings.
In my previous post I wrote about getting a list of aliased field names. From that post it might be evident that dealing with aliased field names is not that straight forward in Mongoid. I am using Mongoid v2.2.4 which the latest version working with Rails 3.0. Mongoid v2.3 and later require ActiveModel 3.1 and hence Rails 3.1.
Anyways, aliased field names have these shortcomings :
- Accessor methods are defined only with the aliased names and not the actual field names.
- Dirty attribute tracking methods are not defined for the aliased names.
- attr_protected, if used, should be used with both short and long forms of field names.
Accessor methods are defined only with the aliased names and not the actual field names.
Consider the following model definition:
class User include Mongoid::Document field :fn, as: :first_name field :ln, as: :last_name endI would have expected the additional accessor methods names 'first_name', 'first_name=', 'last_name' and 'last_name=' to be simple wrapper methods which just forward the calls to the original accessor methods :- 'fn', 'fn=', 'ln' and 'ln='. But Mongoid just doesn't create the shorter form of the accessor methods at all.
user = User.new user.respond_to?(:fn) # Returns false user.respond_to?(:ln) # Returns false user.respond_to?(:first_name) # Returns true user.respond_to?(:last_name) # Returns trueThis doesn't appear like a problem at first sight because an application developer would use the long form of the methods in the application code. Trouble begins in the dirty tracking methods which use the actual attribute name and consequently the shorter form of field names. Take a look at these parts of Mongoid and ActiveModel:
- Definition of setter method for any attribute - Github link for v2.2.4
define_method("#{meth}=") do |value| write_attribute(name, value) end
Notice that the field name (i.e. the short form) is being passed to write_attribute, which eventually gets passed to ActiveModel's dirty attribute tracking method attribute_will_change! - Definition of the ActiveModel method : attribute_will_change! -- Githib link for v3.0.11
def attribute_will_change!(attr) begin value = __send__(attr) value = value.duplicable? ? value.clone : value rescue TypeError, NoMethodError end changed_attributes[attr] = value end
Object.ln
FileUtils.ln
Rake::DSL.ln
That could result in pretty nasty errors about these ln methods and you wouldn't even know why these are being called!. Now whether it is a good practice to name your attributes in a way that clash with already defined methods is a totally different thing. But just remember that the cause of a weird error is probably aliasing.
No comments:
Post a Comment