Ternary means composed of three parts. In Ruby (and also in other programming languages), there is a common programming idiom called the ternary operator, which allows a quick if/else conditional to be put on one line by breaking it into three parts:
The three parts are the condition (user_input == secret), the statement if true (true), and the statement if false (false). The three parts are separated first by a ? and secondly by a : notation.
While Ruby doesn't always require syntax such as parentheses, the preceding statement may be a bit hard to read unless you are very familiar with how Ruby handles the order of operations. Here is a clearer way of writing the preceding code:
The ternary operator is great for quick one-liners. However, if lots of complex logic is starting to creep into any of the three parts, this may be considered a code smell and the code will need refactoring into simpler logic. You can either refactor it into a proper if/else statement or you can refactor the complex logic into separate variables that are then evaluated in the ternary condition.
Consider the following example:
user_input = 'secret'
password = 'secret'
def login_user
puts "logging in user"
end
def show_access_denied
puts "Password was incorrect, try again"
end
user_has_access = user_input == secret
user_has_access ? login_user : show_access_denied
By using clearly labeled method names and simplifying the ternary statement, we can clearly see that if the user has access, we will log them in; otherwise, we will ask them to re-input their password.
Exercise 3.03: Speed Determiner
Write a method that determines the speed that a self-driving car should adjust to, based on environmental conditions, as well as the traffic status and distance to a traffic light. The method should return the new speed. Perform the following steps:
Open a new session on IRB.
First, we write the logic in pseudocode:
Green light:
Sunny, all conditions: speed_limit
Rainy, distance >= 50ft: speed_limit
Rainy, distance < 50ft: 90% speed_limit
Yellow light:
Sunny, distance >= 50ft: 80% speed_limit
Sunny, distance < 50ft: 50% speed_limit
Rainy, distance >= 50f: 80% speed_limit
Rainy, distance < 50f: 25% speed limit
Red light:
Sunny, distance >= 50ft: 50% speed limit
Sunny, distance <= 50ft: 0% speed limit
Rainy, distance >= 50ft: 25% speed limit
Rainy, distance <= 50ft: 0% speed limit
Implement the logic in a method. Define the method as drive_decision and also the parameters we consider in the method.
Note
We are introducing raise here, which will fatally exit the program if the code is encountered. This is a basic way to make sure that if a parameter is not passed in correctly, we get notified about it.
We can see, in the preceding code, that we are repeating code in the case statement. We can refactor this code by creating a new method that checks for valid traffic, weather states and writing this as a one-liner at the top of a method definition like this:
This line of code, which starts off a method implementation, is called a guard clause because it makes a quick decision about whether we should proceed with the method implementation. In other words, it guards the method implementation by making sure only valid parameters are passed in. Guard clauses are a great way to refactor complicated if/else conditionals by checking for these conditions at the top of a method implementation with a guard clause. Then, you can keep the rest of your method implementation clean of additional checks on the data.