Topics Covered in This Ruby on Rails Tutorial:
Properties of Objects, Instance Variables & Local Variables, Global Variables
Exercise Overview
In this exercise, you'll deepen your understanding of Ruby fundamentals by exploring object properties and variable scope—core concepts that separate junior developers from those who truly understand object-oriented programming. Properties function like attributes, providing descriptive information about objects, while different variable types control how data flows through your applications. Mastering these concepts is essential for building maintainable, professional Ruby applications and will directly impact your effectiveness as a Rails developer.
You'll need Terminal access and IRB (Interactive Ruby) to follow along. If you've closed Terminal or exited IRB, open a new Terminal window and type the irb command to get started.
Properties of Objects
Objects aren't merely collections of methods—they encapsulate both behavior and state. Just as a cat doesn't only swipe fish and claw furniture but also possesses a name, age, and coat color, Ruby objects combine actions with properties that define their identity. Properties are instance variables attached to objects, creating persistent state that methods can access and modify. Understanding this relationship between state and behavior is fundamental to object-oriented design.
Consider our previous cat examples: we've been using fluffy and george as variable names pointing to objects, but these cats don't actually know their own names yet. Let's fix that by giving them proper identities.
If you've closed Terminal or exited IRB, open a new Terminal window and launch IRB with the
irbcommand.Enter the following code to create cats with proper names:
class Cat def initialize(name) @name = name end def say_name @name end fluffy = Cat.new("Miss Fluffy") fluffy.say_name george = Cat.new("Mr George") george.say_nameWe've just implemented proper names (Miss Fluffy and Mr George) for our Cat instances. Now these objects maintain their own identity and can report their names when asked. Let's examine the key components:
- The
initializemethod is Ruby's constructor—a special method automatically invoked when creating new objects. By requiring a name parameter, we ensure every cat is "born" with an identity. - In
initialize(name), the parentheses contain a parameter, indicating this method expects input. Parameters make methods flexible and reusable, allowing us to create unique instances from the same class template.
- The
Let's see what happens when we attempt to create a cat without providing the required name parameter:
roger = Cat.newRuby throws an error because our
initializemethod expects a name argument, and we haven't provided one. This demonstrates Ruby's parameter validation, but we can make our code more forgiving by implementing default values.Create a default parameter value to handle unnamed cats gracefully:
class Cat def initialize(name = "Kitty") @name = name endTest the default parameter functionality:
roger = Cat.newExcellent! No error this time. Default parameters provide fallback behavior when arguments aren't supplied, making your methods more robust and user-friendly.
Verify that Roger received the default name:
roger.say_nameRoger responds with
"Kitty"because we didn't explicitly name him. The default value(name = "Kitty")ensures all cats have names, even when none is provided during instantiation.Confirm that explicit naming still works alongside the default:
sophie = Cat.new("Miss Sophie") sophie.say_namePerfect! Miss Sophie knows her given name, demonstrating how default parameters enhance flexibility without breaking existing functionality. This pattern—providing sensible defaults while allowing customization—is a hallmark of well-designed APIs.
Variable Names vs Object Properties
| Feature | Variable Names | Object Properties |
|---|---|---|
| Cat References | fluffy, george | Miss Fluffy, Mr George |
| Perspective | External reference | Internal identity |
| Purpose | Code organization | Object state |
Creating Objects with Properties
Define initialize method
The initialize method is automatically called when creating new objects. It accepts parameters that become object properties.
Set instance variables
Use @name syntax to create instance variables that store object properties and can be accessed by other methods.
Create accessor methods
Define methods like say_name that return instance variables, allowing objects to share their properties.
You can set default values for parameters using name = 'Kitty' syntax. This prevents errors when objects are created without required parameters and provides fallback values.
Instance Variables & Local Variables
Understanding variable scope is crucial for writing maintainable Ruby code. The @name variable you've been using is an instance variable, distinguished by the @ prefix. Ruby also uses local variables without special prefixes. The difference between these variable types determines how data flows through your objects and can make or break your application architecture.
- Instance variables (@ prefix) belong to specific object instances and remain accessible to all methods within that object's lifetime.
- Local variables (no prefix) exist only within their defining scope—typically a single method—and disappear when that scope ends.
Let's explore these concepts through practical examples that illustrate why scope matters in real applications.
Create methods that demonstrate both variable types in action:
class Cat def learn_stuff @remember_this = "location of tuna can" forget_this = "how to catch a mouse" "I learned stuff!" endNOTE: We've created an instance variable
@remember_thisand a local variableforget_this. The @ symbol is your visual cue for instance variable scope. The string"I learned stuff!"serves as this method's return value, demonstrating Ruby's implicit returns.Create accessor methods to test variable persistence across different scopes:
def what_did_you_remember? @remember_this end def what_did_you_forget? forget_this endNOTE: Ruby conventions allow question marks in method names for predicate methods (those returning boolean values) or, as here, methods that query object state. This syntactic sugar makes code more readable and self-documenting.
Test instance variable accessibility across methods:
fluffy.learn_stuff fluffy.what_did_you_remember?Success! Fluffy retrieves the tuna can location because
@remember_thisis an instance variable. Instance variables persist throughout an object's lifetime, making them perfect for storing object state that multiple methods need to access. This is how objects maintain their identity and remember information between method calls.Now observe how local variables behave differently:
fluffy.what_did_you_forget?Ruby raises a
NameError: undefined local variable or method 'forget_this'because local variables are confined to their defining scope. Theforget_thisvariable existed only within thelearn_stuffmethod and was destroyed when that method completed execution. This scoping behavior prevents accidental variable sharing and keeps method internals properly encapsulated.
This distinction between instance and local variables becomes critical in Rails applications, where instance variables defined in controllers automatically become available to view templates, while local variables remain controller-private. Understanding variable scope helps you write more predictable, maintainable code and debug issues more effectively when data doesn't flow as expected through your application.
Instance vs Local Variables
| Feature | Instance Variables | Local Variables |
|---|---|---|
| Syntax | @variable_name | variable_name |
| Scope | Throughout the class | Within method only |
| Access | Any method can use | Same method only |
| Example | @remember_this | forget_this |
When to Use Each Variable Type
Attempting to access a local variable from a different method results in NameError: undefined local variable. This demonstrates the scope limitation of local variables.
Later when you get to Rails you'll see that instance variables are often used to share data between controllers and views.