Why is Ruby dynamic?

Z

Zhao Yi

People say Ruby is a dynamic language. I don't understand what the
"dynamic" mean here. Does this mean Ruby can dynamic change itself at
run-time?
 
D

David Masover

A nitpick, but... string evals considered harmful!

class Narcissus
def call_once
eval '
class Narcissus
def call_once
raise "nope!"
end
end'
end
end

I'd write this as:

class Narcissus
def call_once
def call_once
raise 'nope!'
end
end
end

But perhaps the more impressive demos are modifying system classes --
re-opening String, for instance -- or eigenclass hacks, or dynamically
generating functions. I always loved define_method. When you know exactly
what kind of things are acceptable:

class PunchCard
%w(fold bend mutilate).each do |action|
define_method action do
raise "Don't #{action} me!"
end
end
end

And when you don't necessarily know, there's method_missing:

class TwoYearOld
def method_missing method, *args
puts "That's MY #{method}!"
end
end

And Ruby makes it very difficult to build a cheat-proof robot game, as you can
always start rewriting the other robot:

class MyRobot
def move
class << other_robot
def move
raise 'Explode!'
end
end
end
end

Of course, it's comforting to know that string evals are there -- having a
working "eval" is a sign of a truly dynamic language. (Probably makes irb
easier to write, too!) But there's very few legitimate uses of it anymore, I
think.
 
P

Phlip

David said:
A nitpick, but... string evals considered harmful!
I always loved define_method.

I recently tried define_method and it didn't work, so I fell back to eval. (Note
to the newbs: Try things the most progressive way possible, such as a fixed
version of Dave's version of my example, before trying a hack...)

Here's the lines, from inspect_sql:

def self._decorate_explainers(explains) #:nodoc:
def explains.find_statements(regexp)
return AssertEfficientSql._decorate_explainers(select{|explanation|
explanation[:statement] =~ regexp
})
end

(_multiples + _singulars).each do |rollup, explainer|
eval "def explains.#{rollup}; map{|q|q[:#{rollup}]}.flatten.uniq; end"
end

return explains
end

For the eval line, I first tried explains.define_method, and it didn't work.
Instead of researching why, or finding a kindler gentler system, I whipped out
eval and put a bullet thru the problem.

It's the shortest possible eval, anyway!

Also I think you can't say 'def foo; def bar; end; end' - the def is in
method-space, not class-space.
 
D

David A. Black

Hi --

David said:
A nitpick, but... string evals considered harmful!
I always loved define_method.

I recently tried define_method and it didn't work, so I fell back to eval.
(Note to the newbs: Try things the most progressive way possible, such as a
fixed version of Dave's version of my example, before trying a hack...)

Here's the lines, from inspect_sql:

def self._decorate_explainers(explains) #:nodoc:
def explains.find_statements(regexp)
return AssertEfficientSql._decorate_explainers(select{|explanation|
explanation[:statement] =~ regexp
})
end

(_multiples + _singulars).each do |rollup, explainer|
eval "def explains.#{rollup}; map{|q|q[:#{rollup}]}.flatten.uniq; end"
end

return explains
end

For the eval line, I first tried explains.define_method, and it didn't work.
Instead of researching why, or finding a kindler gentler system, I whipped
out eval and put a bullet thru the problem.

define_method is a private instance method of Module, so a typical use
follow something like this path:

c.class_eval { define_method(m) {|x| puts x } }
It's the shortest possible eval, anyway!

Also I think you can't say 'def foo; def bar; end; end' - the def is in
method-space, not class-space.

You can nest def's, but the nested one (once the outer one is
executed) becomes an outer one:

class C
def x
def y
puts "Hi from y"
end
end
end

c = C.new
c.x
c.y # Hi from y

d = C.new
d.y # Hi from y


David

--
Rails training from David A. Black and Ruby Power and Light:
Intro to Ruby on Rails January 12-15 Fort Lauderdale, FL
Advancing with Rails January 19-22 Fort Lauderdale, FL *
* Co-taught with Patrick Ewing!
See http://www.rubypal.com for details and updates!
 
D

David Masover

This is worth bearing. I don't have a problem with eval, only with string
eval. Given that:
For the eval line, I first tried explains.define_method, and it didn't work.

According to Philip, define_method is a private method, so you can't do that
from outside that object.

Assuming "explains" is a class or a module, you could always go inside the
object:

explains.module_eval do
define_method :foo ...
# More methods?
define_method :bar ...
end

See, I love those, because they can actually take blocks, not just strings.
Since I'd mostly just be passing a variable in through string interpolation,
anyway, it's not only faster, it's safer -- SQL injection is enough of a
problem, we don't need Ruby injection, too!

If it's only a one-off, and you find that eval to be too verbose, there's
always the send hack:

explains.send :define_method, ...

This works mostly because send doesn't check for whether a given method is
actually reachable at that point -- I think you need public_send for that.
Obviously, send is useful for more than just that...

The main legitimate solution I've found is cases like ActiveSupport's
String#constantize -- this is a case where it would take quite a lot more
Ruby to accomplish the same thing as a simple eval. And even here, it's
surrounded by sanity checks, and it's isolated, so that you never have to
know it's there (I didn't, till I looked at the source of constantize out of
curiosity).

And there's always the performance -- I consider evals to be acceptable when
run once, during program start (or at some arbitrary point after that). It's
still code smell, but constantize is an example where the alternative might
be worse. But I'd never call constantize in a Rails view.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,201
Messages
2,571,049
Members
47,654
Latest member
LannySinge

Latest Threads

Top