Topics Covered in This Ruby on Rails Tutorial:
Sanitizing User Input, Integers & Decimals, If/else, Unless, & Case Statements, Constants, Symbols
Exercise Overview
Data integrity forms the backbone of any robust web application. User input—whether from forms, APIs, or database queries—arrives in unpredictable formats that can break functionality or compromise security. In this comprehensive exercise, you'll master essential Ruby techniques for sanitizing strings, manipulating numeric data types, and implementing control flow logic through conditional statements. We'll also explore Ruby's distinctive features: constants for maintaining immutable values and symbols for memory-efficient internal communication. These fundamentals will serve as building blocks for more sophisticated Rails development patterns you'll encounter in production environments.
This tutorial uses IRB (Interactive Ruby) to provide immediate feedback as you learn. Each concept is demonstrated with hands-on examples that you can execute in real-time.
Sanitizing User Input
Real-world user input is messy. Whether users accidentally include extra whitespace, hit enter prematurely, or copy-paste content with hidden characters, your application must handle these scenarios gracefully. Ruby's string manipulation methods provide powerful tools for cleaning and normalizing user data before processing or storage.
If you closed Terminal, closed the window, or exited IRB, open a Terminal window and type the
irbcommand to launch the Interactive Ruby shell.Let's simulate a common scenario: sloppy form input that could break email validation or database operations. Type the following exactly as shown, including the extra spaces and pressing enter after the email address to create a line break:
signup = " fluffy@gmail.com "Terminal should return:
" fluffy@gmail.com\n". Notice how Ruby represents the line break as\nand preserves those problematic leading spaces.This malformed input would cause issues in email delivery systems and create inconsistent data storage. Clean it up using Ruby's most essential sanitization method:
signup.stripTerminal should return:
"fluffy@gmail.com". The.stripmethod efficiently removes all leading and trailing whitespace characters, including spaces, tabs, and newlines—exactly what you need for reliable data processing.Here's a crucial point about Ruby methods. Check the original variable by typing:
signupThe original variable remains unchanged! This demonstrates that
.stripreturns a new cleaned string without modifying the original. In many cases, you'll want to permanently clean the data.Use the exclamation point (bang) operator to make the sanitization permanent:
signup.strip! signupSuccess! The bang operator
.strip!modifies the original variable in place, which is essential when you need to ensure data remains clean throughout your application's lifecycle.
String Sanitization Process
Identify the Problem
Raw user input often contains unwanted whitespace, line breaks, or formatting that can break functionality
Apply strip Method
Use .strip to remove whitespace and line breaks, returning a cleaned version without modifying the original
Make Changes Permanent
Use .strip! with exclamation point to permanently modify the string variable in place
Temporary vs Permanent String Methods
| Feature | strip | strip! |
|---|---|---|
| Modifies Original | No | Yes |
| Returns New String | Yes | No |
| Memory Usage | Higher | Lower |
| Safety | Safer | Destructive |
Integers & Decimals
Ruby's numeric system is both intuitive and powerful, supporting seamless operations between different number types. Understanding how Ruby handles integers and floating-point numbers—and the critical differences between them—is essential for building applications that perform accurate calculations, whether you're processing financial data, computing statistics, or handling user-generated numeric input.
Let's start with the simplest case. Type:
1Terminal returns
1—a basic integer that Ruby stores efficiently in memory.Basic arithmetic works as you'd expect:
1 + 1Terminal returns
2, demonstrating Ruby's clean syntax for mathematical operations.Floating-point (decimal) numbers enable precise calculations:
1.5 + 1.45Terminal returns
2.95. Ruby automatically handles decimal precision, making it ideal for financial calculations and scientific computing.Ruby seamlessly mixes integers and decimals in the same expression:
1 + 1 + 1.5Terminal returns
3.5. When any operand is a float, Ruby promotes the result to a float, preserving precision.Here's where Ruby's behavior might surprise you. Try this division:
1/2Terminal returns
0, not0.5. This demonstrates integer division—when both operands are integers, Ruby performs integer division and truncates the decimal portion. This behavior is crucial to understand for applications requiring precise calculations.To get decimal precision from division, ensure at least one operand is a float:
1.0/2.0Terminal returns
0.5. This pattern is essential for financial applications, statistical analysis, or any scenario where fractional results matter.In web applications, user input typically arrives as strings, even when users enter numbers. This creates a common integration challenge. Observe what happens when you try to add a string number to an integer:
"1" + 1Ruby throws a type error because it can't determine whether you want mathematical addition or string concatenation (remember,
+joins strings together). This ambiguity protection prevents subtle bugs in production applications.Convert string representations of numbers to integers using the
to_imethod:"1".to_i + 1Terminal returns
2. Theto_imethod is indispensable when processing form data, API responses, or any external input where numbers arrive as strings.For decimal strings, use
to_f(to float) to preserve fractional precision:"1.5".to_f + 2.0Terminal returns
3.5. This method is crucial when handling financial data, measurements, or any decimal input from users.Sometimes you actually do want string concatenation. Convert numbers to strings using
to_s:"1" + 1.to_sTerminal returns
"11". No mathematical operation occurs—Ruby treats both elements as strings and concatenates them. This technique is valuable for building display messages, generating IDs, or formatting output.The conversion methods
to_s,to_i, andto_fform Ruby's type conversion toolkit, essential for data processing. Try this practical example:"easy as " + 123.to_sTerminal returns
"easy as 123". This pattern appears frequently in Rails applications when building user messages, generating reports, or formatting data for display.
Ruby Division Behavior Examples
Essential Type Conversion Methods
to_i
Converts strings to integers. Essential for handling numeric user input that arrives as text.
to_f
Converts strings to floating-point numbers. Use when working with decimal values from forms.
to_s
Converts numbers to strings. Useful for concatenation and display formatting.
If/Else, Unless, & Case Statements
Control structures form the decision-making backbone of every application. They enable your code to respond intelligently to different conditions, user inputs, and system states. Ruby's control flow syntax is remarkably expressive, offering multiple ways to implement the same logic—from traditional if/else statements to Ruby's unique 'unless' keyword and powerful case statements.
Ruby's
ifstatements evaluate conditions and execute code when the condition is true:if 2 > 1 "All is right with the universe today" endTerminal returns
"All is right with the universe today"because the condition evaluates to true.The
if/elsepattern provides alternative execution paths, essential for handling different scenarios in your applications:if 1 > 2 and 3 <= 2 "Something is very wrong!" else "Phew! Basic principles of math are still in effect" endTerminal returns
"Phew! Basic principles of math are still in effect"because the compound condition fails. Note that<=means "less than or equal to"—a standard comparison operator in programming.Ruby's
elsifkeyword enables multiple condition checking. Pay careful attention to the spelling—it's elsif, not elseif:animal = "cat" if animal == "dog" "Arf!" elsif animal == "cat" "Meow!" endTerminal returns
"Meow!". The==operator checks value equality, a fundamental comparison you'll use constantly in conditional logic. If you receivenil, double-check that you spelledelsifcorrectly.Ruby offers elegant one-line conditional syntax for simple conditions, widely used in professional Ruby code:
"Meow!" if animal == "cat"Terminal returns
"Meow!". This postfix conditional syntax makes code more readable when the condition is simple and the action is clear.Ruby's
unlesskeyword provides intuitive negative conditionals, often making code more readable than negated if statements:"Arf" unless animal == "cat"Terminal returns
nilbecause the animal is indeed a cat.unlessexecutes when the condition is false, providing natural English-like logic flow.Ruby uses
!=for "not equal to" comparisons. Combined withunless, you can create expressive (though sometimes complex) logic:"Meow" unless animal != "cat"Terminal returns
"Meow". While this demonstrates the syntax, be cautious with double negatives in production code—clarity trumps cleverness.Case statements provide clean alternatives to lengthy if/elsif chains, especially when comparing a single variable against multiple values:
def birdsong(bird) case bird when "owl" "Hoot" when "crow" "Caw" else "tweet" endThis method demonstrates Ruby's case statement syntax, which is more readable than equivalent if/elsif chains when handling multiple discrete values.
Test the case statement with a specific match:
birdsong("owl")Terminal returns
"Hoot", demonstrating how case statements match exact values efficiently.Try a value that doesn't match any specific case:
birdsong("robin")Terminal returns
"tweet"—the default case handles any unspecified values, preventing unexpected nil returns.Verify another specific match:
birdsong("crow")Terminal returns
"Caw". Case statements excel at implementing lookup tables, state machines, and any logic requiring multiple discrete value comparisons.
Control Structure Comparison
| Feature | if/else | unless | case |
|---|---|---|---|
| Best For | Standard logic | Negative conditions | Multiple options |
| Readability | High | Can be confusing | Very clear |
| Single Line | Yes | Yes | No |
| Multiple Conditions | With elsif | Limited | Excellent |
Remember: it's 'elsif' not 'elseif'! Ruby's elsif keyword is missing the 'e' at the end, unlike some other programming languages.
The == operator checks if the value of two things are equal or not. If they are, then the condition becomes true, and if not it becomes false.Constants
Constants represent values that shouldn't change during program execution—configuration settings, mathematical constants, or application-wide settings. Ruby's constant system helps prevent accidental modifications while providing clear semantic meaning about which values should remain stable throughout your application's lifecycle.
Define constants using uppercase naming. Let's start with a mathematical constant:
PI = 3.14159Ruby recognizes uppercase identifiers as constants, treating them differently from regular variables.
Access constants by referencing their uppercase names:
PITerminal returns
3.14159. Constants provide reliable access to unchanging values throughout your application.Use constants in calculations just like variables, but with the semantic guarantee that the value won't change unexpectedly:
diameter = 2 circumference = PI * diameterTerminal returns
6.28318. Constants make mathematical formulas self-documenting and reduce the risk of using incorrect hardcoded values.While Ruby allows constant reassignment, it issues warnings to help you catch potential bugs:
PI = 12061Ruby displays
warning: already initialized constant PIwhile allowing the reassignment. This warning system helps identify accidental constant modifications that could introduce subtle bugs in production applications.Professional Ruby code typically organizes constants within modules to prevent naming conflicts and provide namespacing. Ruby's built-in
Mathmodule demonstrates this pattern:Math::PITerminal returns a highly precise value of pi. The
::syntax accesses constants within modules, providing both organization and precision.Your custom constants coexist independently with module constants:
PITerminal returns
12061—your modified constant remains separate fromMath::PI. This demonstrates Ruby's namespace system, crucial for avoiding naming conflicts in larger applications.
Using Constants in Ruby
Constants are most frequently encountered as parts of modules. Use the :: operator to access module constants like Math::PI for better organization and to avoid naming conflicts.
Symbols
Symbols represent one of Ruby's most distinctive and powerful features. Unlike strings, symbols are immutable identifiers that exist only once in memory, making them ideal for internal application communication, hash keys, and method names. They provide better performance than strings for repetitive use and clearly distinguish between user-facing text and internal application identifiers.
Create a method that leverages symbols for internal state management:
def speak(animal) case animal when :dog "arf" when :cat "meow" when :mouse "squeak" endNotice how symbols like
:dogand:catclearly represent internal identifiers rather than user-facing strings.Call the method using symbol syntax:
speak :catTerminal returns
"meow". The method receives a symbol but returns a string—a common pattern where symbols handle internal logic and strings provide user output.Symbols are intentionally limited compared to strings, making them faster and more memory-efficient. Try to modify a symbol:
:mouse.upcase!Ruby throws an
undefined method errorbecause symbols are immutable. They can't change case, be reversed, or perform most string operations. This limitation is actually a feature—it prevents accidental modification and ensures consistent performance. In Rails applications, you'll find symbols extensively used as hash keys, method parameters, and configuration options because of their efficiency and immutability.Ruby's flexible syntax allows you to omit parentheses for simple method calls, a common convention in the Ruby community:
speak :mouse speak(:mouse)Both approaches return
"squeak". The parentheses-free syntax is preferred for simple cases, while explicit parentheses improve clarity in complex method calls.Important Note: We've been working in IRB (Interactive Ruby), which provides a pure Ruby environment. When you transition to Rails development, you'll use the Rails console, which loads additional libraries and framework-specific methods not available in IRB. This foundational Ruby knowledge transfers directly to Rails, but Rails adds powerful additional capabilities for web application development.
Symbols vs Strings
| Feature | Symbols | Strings |
|---|---|---|
| Memory Efficiency | High | Lower |
| Mutability | Immutable | Mutable |
| Use Case | Internal values | User-facing text |
| Performance | Faster | Slower |
| String Methods | Limited | Full support |
Ruby allows you to omit parentheses for simple function calls, making code cleaner. Both 'speak :mouse' and 'speak(:mouse)' work identically.
Remember: IRB is pure Ruby and doesn't include Rails libraries. Many Rails-specific functions won't work in IRB - you'll need the Rails console for full framework functionality.