I recently found myself facing a metaprogramming challenge. I solved it by combining two terrible ugly hacks, and as such I won’t say I found a solution that is anywhere near to be elegant.
My problem was this: I was developing a Radiant extension called tags_multi_site, which allows the tags extension to play nice with the multi_site extension. This required me to scope all tags within a site, so that tags with the same name could exist in different sites, but in the same physical database table.
The tags extension has this validation:
class MetaTag < ActiveRecord::Base validates_uniqueness_of :name, :case_sensitive => false end
I needed to add :scope => :site_id, but I couldn’t touch the code of tags extension itself, since that would terribly un-DRY and not reusable for anyone else. I had to either modify the existing validation programmatically from my own extension or to remove it and add my own.
I went for the last solution. I quickly discovered that validations are saved in an array available as an inheritable attribute on the model (read_inheritable_attribute(:validate)), and that the built-in Rails validation are stored as Procs in this array. One could remove all validations added so far by emptying this array, but I only wanted to remove validates_uniqueness_of to stay as loosely coupled as possible.
Procs can’t tell much about themselves – they are mostly just there to be called. But I knew from the Rails code that each validation Proc is added from inside the class method of the validation. So, I just had to figure out a way to determine the method context the Proc had been declared in to be able to remove the right one.
I realized I could read variables from the Proc’s context by doing an eval with the Proc’s binding applied. I also found an expression somewhere that returned the name of current method by using the stacktrace information in caller.
All in all, the solution ended up like this:
module TagsMultiSite
module MetaTagExtensions
def self.included(base)
base.extend(ClassMethods)
base.class_eval {
# HACK: Remove the existing validates_uniqueness_of block
read_inheritable_attribute(:validate).reject! do |proc|
if proc.is_a?(Proc)
method = eval("caller[0] =~ /`([^']*)'/ and $1", proc.binding).to_sym rescue nil # Returns the name of method the proc was declared in
:validates_uniqueness_of == method
else
false
end
end
# Add new validates_uniqueness_of with correct scope
validates_uniqueness_of :name, :case_sensitive => false, :scope => :site_id
}
end
end
end
It would be easy to make this into a generalized method for removing Rails validations, but I think this issue is pretty rare. Usually people can just change or remove the original validation. Still, this example demonstrates fairly well how the trusting nature of Ruby allow us to make far-fetched metaprogramming hacks to solve our problems.
Hello, I'm Casper Fabricius. I have developed for the web for 9 years, and have been enjoying Ruby on Rails for the past 4.
My experience covers communities, shopping solutions, multi-language sites, heavy back-end lifting and a wide selection of more traditional websites. I like to integrate Ruby with Java and .NET through JRuby and IronRuby when it makes sense. I am passionate about test- and behavior-driven development, but at the same time I am pragmatic and believe in getting things done.
I am based in Copenhagen, Denmark, but I take assignments from across the globe. Feel free to study my resumé, featured projects and - of course - to hire me.
buy paxil canada
buy viagra without a prescription
cheap levitra online
cheap discount soma
buy viagra pharmacy online
cheap discount levitra online
premarin
buy viagra now online
buy legal fda approved viagra
cheaper viagra levitra cyalis
buy medved viagra
discount viagra brand drug
discount viagra in the usa
buy viagra softtabs
buy viagra on line
buy cheap generic viagra
buy online viagra securely
buy viagra next day delivery
cheap online pill viagra
buy lasix
viagra canada prescription
viagra and cialis and
buy discount zoloft
cheap crestor online
buy buy cheap viagra
cheapest price for viagra
cheap pill viagra
buy sildenafil viagra
buy in online uk viagra
cheap cialis viagra
cheap drug retin viagra wellbutrin
order viagra licensed pharmacies online
buy viagra from brazil
cheapsest viagra online
cheap order prescription viagra
buy form generic viagra
buy viagra at safeway
cheap deal discount viagra viagra
cheapest viagra online in the uk
cheapest viagra uk
buy hgh now
buy crestor now
buy viagra in the philippines
buy generic online viagra
cheapest online viagra
order forms for buying viagra
cheapest generic silagra viagra
buy caverta
buy cheap cialis
buy now online viagra
buy cialis canada
buy viagra online 350
order prescription viagra
discount viagra sale
buy viagra from an online pharmacy
order generic viagra
cheap cheap viagra
cheap cheap herbal viagra viagra viagra
viagra by mail order
buy viagra low cost
order viagra usa
cialis 32
buy cheap soma
buy viagra cialis
cheapest viagra in uk cheap
buy online online pill viagra viagra
buy internet viagra
cheap online pharmacy viagra viagra
buy cheap discount levitra
cheap online purchase viagra
buy viagra onlines
buy real viagra pharmacy online
cheapest cialis
buy viagra and cilas usa
buy viagra online order
over the counter viagra in europe
cheap viagra online uk
viagra bullshit
buy viagra online 35008 buy
buy drug satellite tv viagra
viagra breathing
cheap generic india viagra
buy discount viagra online
cialis 1
buy crestor online
discount viagra cialis
buy viagra meds online
buy viagra ups
buy online viagra viagra
soma online
buy generic viagra online pharmacy online
buy viagra online without prescription
cheap viagra kamagra
cheap quality viagra
cheep viagra from indea
buy viagra inte
buy viagra online u
order viagra on line
buy pharmaceutical viagra
viagra and discovery
buy viagra levitra alternative lavitra
cheapest in uk viagra
buy herbal viagra
order viagra onlines
viagra buying online
buy viagra and overseas
buy cheap viagra on the net
cheapest brand viagra
cheap levitra online
buy hgh canada
buy cheap deal pill viagra
order viagra buying viagr
buy plavix
discount viagra generic
viagra buy viagra
buy viagra for cheap
buy viagra in england
buy deal herbal viagra viagra
cheap referrers total viagra
buy cheap cialis generic levitra viagra
order site viagra
cheapest viagra us pharmacy
buy viagra online australia
buy viagra for women
buy kamagra
discount viagra mastercard
cheap discount viagra
cheep generic viagra
buy isoptin
cheap viagra discount viagra buy viagra
buy cialis without prescription
cheap viagra uk
cheapest viagra world
discount viagra viagra
buy viagra in the uk
buy viagra per pill
discount viagra or cialis
cheap overnight viagra
buy viagra now
discount viagra drug
buy lipitor
order order viagra
buy viagra order viagra
discount viagra offers
buy p viagra
cheap kamagra uk viagra
cheapest viagra online plus zenegra
cheap viagra 25mg
buy viagra without prescription
buy in uk viagra
buy generic viagra pharmacy online
buy levitra viagra online
cheap molde ticket viagra
cheapest price viagra
cheapest viagra us licensed pharmacies
viagra and coupon
buy viagra viagra online
order viagra with mastercard
cheap no prescription viagra
cialis online
buy viagra online web meds
viagra buy uk
cheapest price for viagra and cialis
viagra and cialis cheap
generic soma
buy cheao cgeap kamagra uk viagra
buy levitra online viagra
buy prescription viagra without
buy viagra no prescription
buy cheapest online place viagra
buy viagra online online pharmacy
purchase levitra
buy levitra now
purchase levitra online
order levitra online
cheap viagra no presrciption 50mg
buy viagra where
buy online p viagra
buy online purchase viagra
buy viagra 100mg
cheapest generic viagra caverta veega
buy cheap viagra 32
buy online viagra in the uk
viagra by mail
purchase tramadol online
discount viagra prescription drug
buy cheap site viagra
buy viagra zenegra
buy softtabs viagra
over the counter viagra substitute
order viagra online consumer rx
cheap levitra viagra href foro forum
viagra buy it
buy cheap crestor
buy viagra or cilas
order mexican viagra
buy now viagra
buy crestor
buy viagra australia
cheap mexico viagra
buy viagra online india
order viagra online a href
cheap viagra canada
cheapest generic viagra and cialis
buy viagra online no prescription
cialis 20mg
allegra d
cheap deal pill pill viagra
buy viagra online now buy viagra
viagra buy now pay later
buy lexapro
buy viagra alternative
cheap phizer viagra
viagra buy in uk online
order crestor
cheap drug online prescription viagra
generic zoloft
imitrex
buy deal deal price viagra
buy viagra in uk
viagra and deafness
buy viagra cheap through online sales
cheap prescription viagra without
cheap viagra in uk
buy viagra 32
cheap testosterone viagra href foro
buy keyword viagra
order zoloft online
cheap generic viagra substitutes
buy discount soma
buy viagra contact us page
buy canada viagra
buy viagra in mexico
buy australian viagra
cheapest line viagra
order discount viagra
cheap pharmaceutical viagra
order 50mg viagra
viagra buy contest
buy levitra
buy viagra online discount
tramadol cod
cheapest generic viagra and cialis pills
buy viagra online alternative viagra
order crestor online
buy viagra over the counter
buy dot phentermine viagra
buy deal viagra
cheap online generic viagra
buy online sale viagra
buy viagra cheap fed ex
buy avandia
buy glucophage
buy viagra in spain
cheapest prices on generic viagra
buy non prescription generic viagra paypal
zoloft online
cheap meltabs viagra
buy cheap discount lexapro
order viagra softtabs
buy viagra buy cheap viagra index
buy cipro
cheap genric viagra online
generic levitra
buy viagra toronto
cheaper viagra
buy depakote
buy free viagra viagra
buy cheap uk viagra
viagra buy general
cheap viagra cialis
cheap discount soma online
cheap generic viagra from usa
purchase crestor online
buy cheap discount pill viagra viagra
seroquel
cheapest viagra on line
buy viagra safeway pharmacy
buy cheap viagra online uk
cheap soft tab viagra
order status viagra
cheap testosterone viagra href foro forum
cheap drugs viagra cialas
buy now soma
buy crestor
cheap discount cialis
order pfizer viagra with mastercard
cheap viagra credit
cheapest generic substitute viagra
viagra buy australia
cheap viagra online order viagra now
buy viagra in bangkok
buy viagra over the counter us
buy cheap viagra online now uk
buy cheap purchase uk viagra
cheapest viagra and regalis
cheapest generic viagra 99 cents
buy cailis viagra singapore
cheapest viagra prices uk
cheap crestor
purchase crestor
order viagra prescription
viagra and flomax
buy followup post viagra
buy generic viagra online
buy online online viagra viagra
viagra buy
purchase paxil online
order viagra now
cheap viagra
buy carisoprodol no prescription
purchase nexium online
order viagra online in wisconsin
buy in spain viagra
cheap viagra index
over the counter viagra alternative
buy sublingual viagra on the internet
buy imitrex
order viagra overnight shipping
cheap viagra online prescription
buy cheap p viagra
buy viagra with paypal
viagra canada price
buy viagra line
cheap cheap viagra viagra
buy viagra in london
viagra by money order
discount viagra overseas
viagra by phone
buy viagra in toronto
buy cheap levitra
cheapest place to buy viagra online
check generic order pay viagra
buy uk viagra
cheap viagra from pfizer
buy online pill viagra
buy generic viagra si br
buy pill price price viagra
buy generic viagra usa
cheap gerneric viagra
buy cheap online prescription viagra
cheaper viagra levitra apcalis
buy cheap viagra online here
buy viagra phentermine online pharmacy
buy discount price sale viagra viagra
buy discount crestor
buy viagra online cheap
viagra buy do nu
buy viagra by pill
over the counter viagra
buy cheap viagra online now
buy evista
cheap herbal sale viagra viagra
buy cheap lexapro
buy viagra online 35008 buy viagra
buy viagra without prescription pharmacy online
lexapro side effects
buy cheap free online viagra viagra
buy cheapest viagra
cheap discount levitra
cipro 20
cheap viagra fast shipping
relafen
buy online prescription viagra without
buy can reply viagra
buy site viagra
cheap deal viagra
buy online order viagra
cheapest uk supplier viagra
buy viagra in amsterdam
cheapest viagra in uk che
buy cheap sale viagra
order viagra online no rx prescription
buy soma
discount viagra
buy diet viagra online
buy neurontin
buy viagra new york
buy now hgh
cheap discount crestor online
buy online viagra viagra viagra
cheap paxil online
buy levitra canada
buy prescription viagra
discount viagra perscription drug
cheap herbal viagra viagra viagra
buy cheap online uk viagra
buy viagra in united kingdom
buy cheap viagra viagra
buy coumadin
viagra and cialas
cheapest viagra on the internet
cheap discount crestor
cheapest generic viagra sent overnight
buy viagra online get prescription
buy locally viagra
viagra by mail catalog
cheapest viagra overnight
Aditya
December 7th, 2008 at 10:35 am
Whoa. I was running into the exact same thing with mutli_site – login seems to be unque in the User model and there’s no good way of removing the validation. The thing I’m proposing is to add an :if => should_validate_unique_login but I’m not so sure if that’s the best option…
Overriding validations just seems like a hack.
Casper Fabricius
December 7th, 2008 at 12:01 pm
It is a hack, and an ugly one at that, but since I was writing an extension to be released, I could not rely on changing someone else’s code.
All changes had to be done from my code, but if you have a trick up your sleeve for adding that :if => without modifying existing code I’d like to see it :)
Aditya
December 10th, 2008 at 5:52 am
haha – nope, I was going to propose we coerce^Wconvince Sean to add it to Radiant ;-)
Great work on the extensions though, Josh Hart clued me in…
AC
January 23rd, 2009 at 10:19 pm
Looks good. I might have to use this for some crap validation that some plugins/gems have for email which deny valid RFC822 emails.
Will
February 22nd, 2009 at 3:26 pm
Hello Casper. Very interesting that so many of us are working on this. Here’s what I ended up with. It seems a cleaner way but without as much control as your later intervention.
To be included into ActiveRecord::Validations::ClassMethods:
module MultiSite::ScopedValidation
def self.included(base)
def validates_uniqueness_of_with_site(*attr)
if column_names.include?('site_id')
configuration = attr.extract_options!
configuration[:scope] ||= :site_id
attr.push(configuration)
end
validates_uniqueness_of_without_site(*attr)
end
base.alias_method_chain :validates_uniqueness_of, :site
end
end
I really don’t like the site_id assumption, but afaik validations are hit too early in the initialization sequence for anything more properly configurable to work. Would love to be wrong about that.
This is from a fork of multi_site that I’m using to provide site-scoping to other extensions. I’d be very glad of any comments. http://github.com/spanner/radiant-multi-site-extension
Casper Fabricius
February 22nd, 2009 at 5:40 pm
Nice solution, Will – I actually like that better than mine for this particular case. It is cleaner as you say, and easier to read and understand – less magic. Still, I mostly blogged about my solution because I found it an interesting study into how far you can go with Ruby :)
Ben Evans
February 12th, 2010 at 3:57 am
I was playing with trying to modify the validation for the slug on radiant pages, it currently does not allow anything but numbers/letters… I could not get your script working with my code, but I was able to get this guy’s solution working. The downside is that in my PageExtension class, I had to put all the validations from the Radiant Page class in the process. http://www.neverlet.be/2009/2/18/active-record-validations-are-callbacks
Ben Evans
February 17th, 2010 at 10:55 pm
After doing some more playing around with your method and the one I linked in my previous comment, I have created a Radiant extension that allows targeted removal of default validators without any “hack” style methods.
Using this extension, you can clear validations, or override targeted validations easily on Radiant core models. It does not take much more to also extend / override validations in extension models.
http://github.com/JediFreeman/Radiant-Validators-Extension
Casper Fabricius
February 18th, 2010 at 10:04 am
Ben, that looks awesome. Any chance you can change it into a plugin so it can be used in any Rails project?