0

I have a variable that contains either '&&' or '||' and I need to use it in an if statement like so.

operator = '&&'

result = 1===1 operator 2===2

#=> result = 1===1 && 2===2

How can I achieve this?

I have tried using #public_send but to no avail.

Any help would be appreciated.

2
  • Why do you need to do it this way? While you can use Object#send for methods, you can't do the same thing without eval for keywords and operators. Commented Aug 17, 2020 at 0:13
  • @ToddAJacobs : You can do public_send for nearly all operators (for instance 2.public_send(:+, 5) works fine), but && is an exception. For the same reason, you can't override it for a class. Commented Aug 17, 2020 at 13:40

3 Answers 3

6

The && operator is more of a low-level syntax element than & and | which are method calls. That makes it harder to do.

However, there's two ways:

a = true
b = false
c = true

If you want && equivalence:

[ a, b, c ].all?
# => false
[ a, c ].all?
# => true

If you want || equivalence:

[ a, b, c ].any?
# => true
[ a, b ].any?
# => true
[ b ].any?
# => false
Sign up to request clarification or add additional context in comments.

6 Comments

Thanks but this doesn't solve the problem. The project I'm working on implements a user generated list of conditions that I need to test based on the operator the user selected. So the resulting if statement may contain a mixture of && and ||
This solves the problem you described. If you have a broader problem then it's worth asking a new question to address that, but be sure to be specific about the true scope of it.
It was specific, how to use a string variable as an operator, with an example. I appreciate your response, but in this case it didn't solve the actually question asked. No need to be salty.
Moving the goalposts is always a bit of a jerk move. Your question strongly implies there is one and only one operator. To be sure everyone's on the same page, always try and express the requirements more comprehensively, including edge cases that need to be covered. It's far from clear where you're taking this and how the any? or all? approach will break down.
I didn't move the goal posts, you just made an assumption about what I was doing or why I was doing it rather than just looking at the example and answering the question. But it's all good. I got the answer and I'm sure your answer will help someone in the future. No need to take it so personally :) Again, thanks for your answer, I do appreciate it.
|
0

You could do it like this:

operator = "&&"
result = eval "1===1 #{operator} 2===2"

does that do what you want?

or if 1===1 is just a placeholder here for some expression, assign the result to a variable, so:

operator = "&&"
a = 1===1
b = 2===2
result = eval "#{a} #{operator} #{b}"

(of course, as commenters have noted, you must pay attention to the security vulnerabilities of eval, e.g. check that the operator variable is either "&&" or "||".)

and if you really want an if statement:

if operator == '&&'
  1===1 && 2===2
elsif operator == '||'
  1===1 || 2===2
else
  # dunno
end

4 Comments

I hope you realize this comes with a giant blinking WARNING on it as eval exposes you to extraordinary levels of risk.
@Brad Where is operator coming from? Was the value of that variable provided by a user of your application? Do you trust that user? I am asking because imagine what will happen if operation was a string like ; FileUtils.remove_dir(...);?
@LesNightingill : Don't do eval, unless you have no other choice.
@tadman, @LesNightingill : Is there a way to ensure before eval that operator, a and b are safe for the eval? That is, limit the user input to only the "safe" type.
0

You can use public_send, but even if it were allowed with &&, it would not make sense. Note that E1 && E2 shortcircuits, in that E2 is not evaluated if E1 is already falsy. How could this work with public_send?

If you want to have this effect, you have to give up shortcircuiting, which means that you have to use & and | instead of && and ||. If you insist that your variable operator contains something like '&&', and not, say :& (which would make more sense), you could do a

result = (1===1).public_send(operator[0], 2===2)

to use your example for demonstration.

UPDATE: The comment of tadman to my answer made me think that I should warn you about the following trap: Based on your question, I assumed that the arguments you want to connect with your operator are either true or false. In this case, my solution indeed should work. However, if you they can be just any truey or falsy value of some class X, you need to convert them to true or false, because otherwise, the operator may have a different interpretation (for instance, if you operands happen to be integer). Hence, for some general operand_e1_ and e2, which - as is the general rule in Ruby - are supposed to be interpreted als false if it is either false or nil, and is taken as true otherwise, the safe way to calculate your result would be

result = (!!e1).public_send(operator[0], !!e2)

where !! ensures that the expressions are converted to true and false so that the operators & and | can safely be applied.

6 Comments

& is very different from &&.
@tadman : In general, yes, but in the scope of the OPs question, it is the same, because he (1) Does not want to have the short circuit feature of &&, and (2) Both of his arguments are boolean, i.e. either true or false. But you are right that here danger is lurking, and I will amend my answer.
I'm just not sure that this will work at all. You can't send(:&&, ...) to anything.
@tadman : You can't, and I don't. I'm doing a send(:&,...) instead, which for our case is sufficient, since we don't need && here.
Except that & is completely different from &&.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.