How do I pass a conditional expression as a parameter in Ruby?

For example this what I am trying to do,

def method_a(condition, params={}, &block) if condition method_b(params, &block) else yield end end

and I am trying to call the method like this,

method_a(#{@date > Date.today}, {:param1 => 'value1', :param2 => 'value2'}) do end

The result is the condition is always evaluated to true. How do I make it work?

-------------Problems Reply------------

Can't you just pass condition as the result of a conditional evaluation?

method_a(@date > Date.today, {:param1 => 'value1', :param2 => 'value2'}) do
puts "Do stuff"
end

I think the sensible way to do this would be using a Proc or lambda:

def method_a(condition, params={}, &block)
if condition.call
method_b(params, &block)
else
yield
end
end

method_a(lambda { @date > Date.today }, { :param1 => 'value1', :param2 => 'value2' }) do
# ...
end

A lambda is not evaluated until you call call on it.

Actually, if you didn't have that comment there in the middle of the line, it would pretty much just work:

method_a(@date > Date.today, {:param1 => 'value1', :param2 => 'value2'}) do; end

By the way: if the very last argument to a method is a hash, you can leave off the curly braces, which makes it read almost like Python-style keyword arguments:

method_a(@date > Date.today, :param1 => 'value1', :param2 => 'value2') do; end

If you use Ruby 1.9, and you have a hash where the keys are symbols, you can use the new alternative hash syntax:

method_a(@date > Date.today, {param1: 'value1', param2: 'value2'}) do; end

Combining the two really looks like keyword arguments:

method_a(@date > Date.today, param1: 'value1', param2: 'value2') do; end

In your method_a, you could greatly improve the readability by using a guard clause instead of the big honking if expression:

def method_a(condition, params={}, &block)
return method_b(params, &block) if condition
yield
end

Or the other way around, whichever you think reads better:

def method_a(condition, params={}, &block)
return yield unless condition
method_b(params, &block)
end

However, this is a giant code smell. A method should always do one thing, and one thing only. Every method which takes a boolean argument violates this rule, because it pretty much by definition does two things: one thing if the condition is true and a different thing if the condition is false.

In your original code, this is blatantly obvious, because you have the giant if expression surrounding the entire method and the code in the two branches is completely different. It is even more obvious than that, because the else branch not only has completely different code than the then branch, it also completely ignores the arguments that are passed into the method! So, not only does the method behave differently depending on the condition, it even has a different signature!

What you really want to do, is split the method into two methods. The user of method_a needs to know anyway what the different behavior between the two cases is, and he has to supply the conditional himself. Instead, he could just call the right method in the first place. So, I would split method_a into two:

def method_one(params={}, &block)
method_b(params, &block)
end

def method_two
yield
end

And the client can decide which one to call:

if @date > Date.today then
method_two(param1: 'value1', param2: 'value2')
else
method_one do
# something
end
end

But, if you look closely at method_one, you will see that all it does is just forwarding its arguments unmodified to method_b. So, we can just get rid of method_one altogether, and have the client call method_b directly.

The same goes for method_two: all it does is call the block. The client could have just as well run the code in the first place.

So, now our library code looks like this:

# there is no spoon

That's right! There is no library code left! (Except for method_b which is not part of your question.)

And the client code looks like this:

if @date > Date.today then
method_b(param1: 'value1', param2: 'value2')
else
# something
end

A very good example of a method that violates this rule, is Module#instance_methods in the Ruby core library. It tells you all the instance methods defined in a particular module and class and it takes a boolean argument which decides whether or not this list will include the methods inherited from superclasses. Noone can ever remember whether to pass false or true. Noone. Jim Weirich uses this example in his talks about good design and he usually asks the audience which is the inherited case and which is the immediate case. Usually, a high percentage get it wrong. Sometimes, the percentage is worse than just flipping a coin!

If you look at the documentation, it is utterly confusing. I can never remember which way around the conditional goes, I always have to look it up in the documentation. Which isn't really that helpful, because the official documentation, which is part of the actual sourcecode of YARV and MRI, has it the wrong way round, too!

Category:ruby Views:0 Time:2010-06-10
Tags: ruby

Related post

  • How to pass a Lambda Expression as method parameter with EF 2012-04-07

    How do I pass an EF expression as a method parameter? To illustrate my question I have created a pseudo code example: The first example is my method today. The example utilizes EF and a Fancy Retry Logic. What I need to do is to encapsulate the Fancy

  • Is there any way to pass the lambda expression as a variable or argument? 2012-01-30

    I need to pass the lambda query as a parameter, the followings code is sample and I am interesting to find an implement for it, there is samples: some thing like this: var expr1 = Where(n => n > 6).OrderBy(n => n % 2 == 0).Select(n => n);

  • Parsing Conditional Expressions to String 2010-11-17

    I'm looking for a way of parsing a conditional expression to a string. The best example I can think of is LINQ-to-SQL. It uses ExpressionVisitors to format "Where" clauses. Example: from a in b where a.x == 5 && a.y < 3 select a That would

  • In Java, can I use the conditional expression to "choose" reference types? 2011-01-29

    Can I use the conditional expression to "choose" a reference type, as shown below? ??? = isTrue() ? Integer : Double; Is there something I can place in "???" to make the code snippet compilable? Edit: When I wrote Integer and Double, I didn't mean an

  • How can I dynamically pass a condition and a method to a recursive method 2011-10-24

    I want to make a method like this, public dynamic Traverse(dynamic entity, conditions, method) { foreach (var propInfo in GetTraversableProperties(entity)) { if (condition) method(propInfo.GetValue(etc)); Traverse(propInfo, condition, method); } retu

  • In line Conditional Expression or Function - Pythonic? 2011-11-07

    I have a situation where I would like to conditionally slice a string from the reported position of an '@' symbol; the condition being: slice the string if the '@' is there, else leave it untouched. I thought up two ways, one using a function, the ot

  • Constant value in conditional expression 2008-10-22

    In a coding style question about infinite loops, some people mentioned they prefer the for(;;) style because the while(true) style gives warning messages on MSVC about a conditional expression being constant. This surprised me greatly, since the use

  • Can every if-else construct be replaced by an equivalent conditional expression? 2009-11-30

    (I don't have a serious need for this answer, I am just inquisitive.) Can every if-else construct be replaced by an equivalent conditional expression using the conditional operator ?:? --------------Solutions------------- Does every if-else construct

  • Conditional expression in SQL 2010-03-05

    I am using MYSQL and PHP. I have a table called item. Two of its attributes are price and discounted price.I have SELECT statement as below: $sql = 'SELECT C.cart_id,I.item_id,C.quantity, I.discounted_price,I.price FROM cart C, item I WHERE I.item_id

  • Missing Java error on conditional expression? 2010-06-07

    With methods test1() and test2(), I get a Type Mismatch Error: Cannot convert from null to int, which is correct; but why am I not getting the same in method test3()? How does Java evaluate the conditional expression differently in that case? (obviou

  • Passing expression as a parameter in Call by reference 2010-06-12

    All, When we are passing an expression as a parameter, how does the evaluation occur? Here is a small example. This is just a pseudocode kind of example: f (x,y) { y = y+1; x = x+y; } main() { a = 2; b = 2; f(a+b, a) print a; } When accessing variabl

  • What does 'Conditional expressions can be only boolean, not integral.' mean? 2010-06-14

    What does 'Conditional expressions can be only boolean, not integral.' mean? I do not know Java and I know C++ deffenetly not enought to understend what it means.. Please help (found in http://www.javacoffeebreak.com/articles/thinkinginjava/comparing

  • Passing a Linq expression as a string? 2010-07-19

    The following code works fine using (var ctx = new MyEntities()) { var devices = ctx.Devices .Where(x=> x.Device == "TEST") .ToList(); return devices; } What I would like to do is to pass in the expression that goes in the “Where” clause. I see th

  • Pass Expression as a parameter into a Generic method, and plug the Expression into a CreateCriteria? 2010-08-10

    I have a generic method that exists in EntityRepository that gets entities by Name, which is defined as follows: public IEnumerable<T> GetEntitiesByName<T>(string searchExpression) where T : class, ISearchableEntity, new() { return _sessi

  • What's the reason for having conditional expressions and not loop expressions? 2010-11-09

    I'm using Java 6. Do you think there's a reason why there are conditional expressions String plan = ( isEconomyGood() ? "find a job" : "stay with mom" ) ; and not loop expressions weeklyPlan.add( schedule.hasNext() => schedule.next() ); which adds

  • hql conditional expressions in aggregate functions 2010-11-12

    Does HQL support conditional expressions in aggregate functions? I would like to do something like this select o.id, count(o), count(o.something is null and o.otherthing = :other) from objects o But I get a MissingTokenException from the Antlr parser

  • Conditional expression of distinct Objective-C types lacks a cast? 2011-02-04

    I recieve this error: Conditional expression of distinct Objective-C types 'struct NSNull *' and 'struct NSNMutableArray *' lacks a cast On this line of code: ( (tempArray != nil) ? tempArray : [NSNull null] ) Why? --------------Solutions------------

  • JSLINT warning about style or errror about Conditional Expression 2011-03-28

    I'm trying to fix what JSLINT is warning about two things tha I don't know how to fix. Wich line should be replaced with what code? I have pasted the code in question and the warnings about them below. Thanks very much in advance for your suggestion

  • Type of conditional expression cannot be determined as 2011-04-02

    When I compile my C# project in MonoDevelop, I get the following error: Type of conditional expression cannot be determined as 'byte' and 'int' convert implicitly to each other Code Snippet: byte oldType = type; type = bindings[type]; //Ignores updat

Copyright (C) dskims.com, All Rights Reserved.

processed in 0.211 (s). 11 q(s)