#62
Jul 25, 2007

Hacking ActiveRecord

Have you ever wanted to temporarily disable all validations? Well, ActiveRecord doesn't support this, but that doesn't mean we can't add it. This episode will show you how to open up an existing class and change its behavior.
Download (23.9 MB, 11:13)
alternative download for iPod & Apple TV (15.5 MB, 11:13)

Resources

# test_helper.rb
class Test::Unit::TestCase
  self.use_transactional_fixtures = true
  self.use_instantiated_fixtures  = false
  
  def disable_validation
    ActiveRecord::Base.disable_validation!
    yield
    ActiveRecord::Base.enable_validation!
  end
end

module ValidationDisabler
  def self.included(base)
    base.class_eval do
      extend ClassMethods
      alias_method_chain :valid?, :disable_check
    end
  end
  
  def valid_with_disable_check?
    if self.class.validation_disabled?
      true
    else
      valid_without_disable_check?
    end
  end
  
  module ClassMethods
    def disable_validation!
      @@disable_validation = true
    end
    
    def enable_validation!
      @@disable_validation = false
    end
    
    def validation_disabled?
      @@disable_validation ||= false
    end
  end
end

class ActiveRecord::Base
  include ValidationDisabler
end

Note there is a slight problem with how I set the default value for the class variable in the screencast. The code posted here should be correct.

Ideally I would use something like class_inheritable_accessor instead of a class variable, but I was unable to get this to work. Please comment if you have a suggestion.

RSS Feed for Episode Comments 21 comments

1. Niko Jul 25, 2007 at 01:07

This is a jewel. I used most of these techniques before without fully understanding what's that self.included(base) thing is about. Now I know! And the example is well choosen. In a real app often it doesn't make sense to cramp the different aspects of a technique into one place. Here it seems quite reasonable.

Thank you!


2. chineseGuy Jul 25, 2007 at 02:04

let me see


3. chivi Jul 25, 2007 at 03:00

Hi Ryan,
great job, congratulations!!!

Please, what program do you use for editing the video and export to .mov?


4. Zargony Jul 25, 2007 at 03:19

Great screencast, Ryan. You've put up a good summary of how to easily change the behavior of any class. Of course it does not only work with ActiveRecord, but with virtually every class.

To disable validations, so far I was using save_without_validation! instead of save! (AR itself uses alias_method_chain to add validations), however your plugin might come in handy to disable validations in a larger context.

Btw, you're publishing new screencasts faster than I can watch them :)


5. InMan Jul 25, 2007 at 08:47

Nice...


6. Ryan Bates Jul 25, 2007 at 08:47

@chivi, I'm using Final Cut Pro to edit them and Quicktime Pro to convert them.

@Zargony, right. You can also use "save(false)" to bypass validation. I often find myself using "create" more than "save" so it's convenient to have this plugin.


7. Carl Porth Jul 25, 2007 at 11:09

Great screencast on extending AR!

My favorite way to modify this sort of behavior is using mocha to do a:

Product.any_instance.stubs(:valid?).returns(true)

I think flexmock has a similar new_instances method. Unfortunately rspec doesn't have this functionality yet which is why I use mocha.


8. Frikki Jul 25, 2007 at 11:50

I have watched all your episodes so far and your hard work is greatly appreciated.

You have helped me greatly to jumpstart my web programming skills.

Your screencasts are simply the best I have found and their high quality makes every episode worth watching...

Keep up the great work!


9. Ted Jul 25, 2007 at 15:30

Once again you have saved me hours of reading and days of trial and error! Bless you for your charity :-)


10. youtubesearcher Jul 25, 2007 at 16:20

just pass false to model save, this saves without validations...like this

# will save person even though zip fails validation
person = Person.new
person.zip = "badzip"
person.save false


11. Ryan Bates Jul 25, 2007 at 22:13

@youtubesearcher, true. But this doesn't work for the convenient "create" and other methods related to saving models.


12. youtubesearcher Jul 26, 2007 at 09:17

True enough, but it also does not require hacking thru activerecord and exposing yourself to breakage when a new rails release or patch comes out...but hey...both work and it's just a matter of personal preference


13. alaa Jul 26, 2007 at 10:35

thanks for these screencasts.
i think it will be more ruby idiomatic if you did

  def valid_with_disable_check?
  return self.class.validation_disabled? ||
valid_without_disable_check?
  end

rather than

  def valid_with_disable_check?
    if self.class.validation_disabled?
      true
    else
      valid_without_disable_check?
    end
  end

check http://rubygarden.org/Ruby/page/show/RubyIdioms

regards and keep these gems


14. warlock_handler Jul 26, 2007 at 12:42

Hi ryan,
It was a good walk through... in a way also revising my OOPS concepts.. hehe


15. jack dempsey Jul 26, 2007 at 13:12

@youtubesearcher: "hacking" through activerecord was the point of the exercise. Lots of these vids aren't necessarily about the most efficient or best way to get X done, but teaching techniques and ways of doing things that lots of us find useful.

@ryan: excellent vid. I think lots of people know what they can do with self.included, sort of, but maybe not exactly why. Thanks for all your work.


16. caryl Jul 26, 2007 at 23:15

Great job! Thanks!


17. Henrik N Jul 30, 2007 at 06:06

Instead of
  base.class_eval do
    extend ClassMethods
  end
you can do just
  base.extend ClassMethods
. Since you're calling another method on the same object – alias_method_chain – I suppose there's a case for using class_eval, but viewers might be interested to know there is a slightly simpler way.


18. Stephane L Jul 31, 2007 at 18:25

Awesome screentcast. I was looking for a way to bypass validation with my functional testing and this provided an extensible solution. Great job and thank you for all your work.


19. cover Dec 26, 2007 at 13:57

I'm shocked, i'll have to see it another 2 or more times before understand it all. But it's really nice, and i'll use this approach for some good refactoring in the code :)


20. John F Mar 14, 2008 at 08:59

Thanks for another great screencasts. All of them have been very helpful and timesaving to me and my friends studying rails!


21. Rock Aug 06, 2008 at 01:53

Great! Just fit my need! Thank you!

Add your comment:

(SKIP THIS ONE)

(required)

(not shown)


(use pastie or gist for code)

sponsored by:
if you want to help:
required:
Get Quicktime Player