ActiveRecord hack

0 views
Skip to first unread message

eric draut

unread,
Sep 25, 2009, 8:08:08 PM9/25/09
to progm...@googlegroups.com
Hi there coders,

I have a question for you. I've hacked ActiveRecord to automatically provide a default length validation for strings based on database length limits. I haven't tested it rigorously, but it seems to work so far. My question is, is there a better way? Have I created some horrible performance sandbag or potential memory leak by using instance_eval on ActiveRecord::Base?

Here's the hack, included via the standard config/initializers/ scheme.

class ActiveRecord::Base
  self.instance_eval{alias :__orig_inherited__ :inherited}
  def self.inherited(subclass)
    unless subclass.abstract_class?
      subclass.columns.each do |column|
        unless column.limit.nil?
          subclass.validates_length_of column.name.to_sym, :maximum => column.limit, :allow_nil => true
        end
      end
    end
    __orig_inherited__(subclass)
  end
end

And if I haven't done something untoward, does this seem like an appropriate feature to submit for inclusion in a later release of active record? I can't imagine why one would prefer to have AR throw a StatementInvalid error over having a built-in validation fallback. If so, there may be other things one can inspect in the database and add default validations for.

There is the possibility of altering the data schema at runtime; which would potentially render such validations problematic if fields were removed, or introduce new fields which wouldn't benefit from the default validations, but that could be handled if it proves useful.

Any thoughts? 

Thanks a bunch,
Eric

(update on my hermit status: my first curated commerce app is in soft launch this week, full public launch next week, then make magazine comes in a few weeks, WOOT!)


CLR

unread,
Sep 30, 2009, 12:07:56 PM9/30/09
to progm...@googlegroups.com
Eric,

Instance_eval in AR may have threading implications. [Rails is suppose
to be "mostly" thread-safe now, whatever that means.] Aside from that,
you should submit a bug report with tests that demonstrate the behavior,
and offer a patch, and see what they say.

It definitely makes sense to trigger a validation rather than a
StatementInvalid error in the app layer, but I'm not sure about in the
framework layer. I think if the query is being sent and the DB rejects
it, then there is an incongruity between the DB and the ORM that
shouldn't exist. Is this a bug in the DB, the ORM, or the application?

I don't think it's a bug in either the DB or the ORM software. I think
the developer set up the application incorrectly. The ORM is suppose to
be DB agnostic. Postgres, for example, doesn't block off memory for
varchars any differently than it does for a string or text, so it
doesn't care how long the string is. There is only one reason to add a
constraint here -- specifically, for the purpose of forcing a
constraint. By convention, this ORM says don't do that. Put all
constraints in the model of the application, not in the DB. If you have
an arbitrary constraint like field length, put it as a validation in the
model for that field.

So in a Rails app, the DB should not have length or null constraints,
unless you have a scenario where you specifically want a
StatementInvalid error generated. Otherwise, that's what the
validations are for, and they belong visibly in the model, not buried in
the ORM.


-CLR

eric draut

unread,
Sep 30, 2009, 1:55:36 PM9/30/09
to progm...@googlegroups.com
Hi Casey,

Thanks for the thoughtful response. 

I don't really understand rails threading, is it something rails does by default or is it something one has to explicitly employ as an application designer? Either way, if instance_eval breaks threading, it will need to be dealt with. I'm hoping that if my idea is a good one, someone on the core team who understands rails threading will handle that part.

Regarding the appropriateness of default validations in an ORM, I agree with this premise:

If the application designer explicitly adds constraints in the database layer, then the application designer should also be responsible to explicitly add validations in the application layer.

However, this is not the case I'm dealing with. If I create a migration with a field of type string, and if I make no mention of limits or constraints of any kind in my migration, even in postgresql, activerecord creates "character varying(255)". If I then try to enter a string that has 300 characters in it, activerecord throws a StatementInvalid error.

So here is a second premise I would like to add to the first:

If an ORM adds a constraint to the database, then the ORM should also be responsible to add default validations in the application layer to avoid tripping database errors when the contraints are violated.

Does that make sense? This second premise is the reason I decided to put default validations in ActiveRecord instead of in my application. ActiveRecord goes out of its way to put a 256 char limit on all strings, regardless of database. Therefore it should provide a fallback validation in the application layer to cover the constraint it created.

Eric
Reply all
Reply to author
Forward
0 new messages