Metaprogramming in Ruby / Rails is so sweet. In one of my core projects, I have utilised Rails'
constantize method to dynamically call classes. Let's say we are keeping a record of people interested in adopting a pet and the pets available for adoption.
- dream_pet (tiger, lion, snake, wolf)
Hypothetical data model above. Using single table inheritance will be a better solution, but let's just say we are forced to use this data model and we want to find the number of available pets for a particular individual. This is where constantize comes in very handy:
person = Person.create(name: 'Lola', dream_pet: 'tiger')
count = person.dream_pet.classify.cosntantize.all.count
Using the dream_pet attribute, we dynamically call the correct class so that we can do a count. Convenient.
Eval VS Constantize
While doing a security check on said Rails project yesterday, this came up:
Unsafe reflection method constantize called with parameter value. Wait, what? Everyone probably knows the absolute dangers of using
eval, allowing potential hackers to exploit and run any ruby code.
constantize has been considered the much safer version hence. What's going on here?
A quick google search pulls up quite a few blog posts about this subject. The summary of it is that while
constantize prevents any ruby code from being executed, it allows unintended classes to be initiated, and depending on how your code, the results can be a minor annoyance to a security exploit.
Whitelist That Class
The best solution of course would be to not use
constantize at all. However, you might be giving up some good convenience isn't it?
In my case, it's pretty easy to whitelist the classes being dynamically called. You could do a simple check like so:
person = Person.create(name: 'Bad Person', dream_pet: 'admin')
PETS_LIST = %w(tiger lion snake dog cat)
In my case, this simple check did the job well enough. Gavin Miller has another take on a whitelist solution that covers a wider range of use cases too.