tags:

views:

78

answers:

3

Suppose we have n constants like:

FOO = 'foo'
BAR = 'bar'
...

I need to check in the block if they exists and are non empty.

%w(FOO BAR FOOBAR).each {|i|
  # this doesn't work
  fail "#{i} is missing or empty" if (! defined?(i) || i.empty?)
}
A: 
BAR = 'bar'
%w(FOO BAR FOOBAR).each {|i|
  begin
    eval(i)
  rescue NameError=>e
    puts e
  end
}
I was thinking about eval() too. I wonder if there is a more elegant solution.
Henry Flower
A: 

In your original code, i is a string, so it won't be empty.

I assume you're checking whether the constant with the name of i is empty, right? This is not a pipe.

%w(FOO BAR FOOBAR).each do |const_name|
  raise "#{const_name} is missing or empty" if (! eval("defined?#{const_name}") || eval(const_name).empty?)
end

Notes:

  • I've renamed i to const_name
  • Ruby tends to use raise rather than fail
  • You've got a fair bit of logic in one line. I'd be tempted to break it into two lines, one checking if the constant exists, and one checking if the string the constant refers to is empty. Better yet, make it into a method.
  • Multi-line blocks tend to use do ... end rather than { ... } (that's more for single lines. I think there's a SO question comparing the two, however - it's worth looking up because the two forms aren't totally identical)

Out of curiosity, which programming language were you using before using Ruby? Was it Perl?

Andrew Grimm
Why the downvote? The question didn't state that `eval` couldn't be used.
Andrew Grimm
i took back the downvote...but it just bugs me that ppl use `eval` to do something like this when there is `const_defined?` especially for this purpose :)
banister
@banister: Fair enough.
Andrew Grimm
@andrew: it was Tcl.
Henry Flower
What's the point of the first `eval`?
Mladen Jablanović
@Mladen, because `defined?` takes an actual constant or variable name as a parameter, not just a string/symbol
banister
Ah ok, I confused it with `const_defined?` method.
Mladen Jablanović
I wonder if I'd have received a sympathy upvote like user229426 if banister didn't take back the downvote?
Andrew Grimm
+3  A: 

This is the best way, in my opinion:

[:FOO, :BAR, :FOOBAR].each do |i|
    raise "constant #{i} not defined" unless Object.const_defined?(i)

    puts "constant #{i} exists and has value #{Object.const_get(i)}"
end

EDIT:

Things are a bit more complicated if you want to look up constants in a scope sensitive way (i.e not just top-level constants):

def const_receiver
    is_a?(Module) ? self : class << self; self; end
end

[:FOO, :BAR, :FOOBAR].each do |i|
    raise "constant #{i} not defined" unless const_receiver.const_defined?(i)

    puts "constant #{i} exists and has value #{const_receiver.const_get(i)}"
end
banister
You should use `unless const_defined?(i)`, not `if ! ...`.
Andrew Grimm
@Andrew, you're right `unless` is better here...though i usually avoid it. And yes i originally went on a skirmish downvoting anyone who had used `eval` for this, but i took back your downvote as your commentary was good. :)
banister
`eval` considered evil.
Andrew Grimm
OK, but how can I read value of constants in the block without eval()?
Henry Flower
@Henry, `const_get(i)`
banister
@Henry, so did this do what you wanted? :)
banister
It did, thanks. I just forgot to "accept the answer".
Henry Flower