The Downside of Cleverness
Ruby lets you do all sorts of clever, fancy tricks. This cleverness is a big part of what makes Ruby so elegant, but it also can be downright dangerous in the wrong hands. To illustrate this, we’ll look at the kind of trouble you can get in if you aren’t careful.
The Evils of eval()
Throughout this book, we’ve dynamically evaluated code blocks all
over the place. However, what you have not seen much of is the use of
eval()
, class_eval()
, or even instance_eval()
with a string. Some might
wonder why this is, because eval()
can be so useful! For example, imagine that you are exposing a way for
users to filter through some data. You would like to be able to support
an interface like this:
user1 = User.new("Gregory Brown", balance: 2500) user2 = User.new("Arthur Brown", balance: 3300) user3 = User.new("Steven Brown", balance: 3200) f = Filter.new([user1, user2, user3]) f.search("balance > 3000") #=> [user2, user3]
Armed with instance_eval
, this
task is so easy that you barely bat an eye as you type out the following
code:
class User def initialize(name, options) @name = name @balance = options[:balance] end attr_reader :name, :balance end class Filter def initialize(enum) @collection = enum end def search(query) @collection.select { |e| e.instance_eval(query) } end end
Running the earlier example, you see that this code works great, exactly as expected. But unfortunately, trouble strikes when you see queries like this:
>> f.search("@balance = 0") => [#<User:0x40caa4 ...
Get Ruby Best Practices now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.