Ruby is known for its elegant syntax and flexibility, which allows developers to create concise and readable code. One of the lesser-known but powerful tools in Ruby is Struct
, a built-in class that provides a simple way to group related attributes without the overhead of defining a full class. While not as commonly used as ActiveRecord models or POROs (Plain Old Ruby Objects), Struct
can be incredibly useful in Rails applications for certain scenarios.
What is Struct
?
Struct
in Ruby is a shortcut to defining simple classes that bundle together a few attributes, providing getter and setter methods for those attributes automatically. It creates a new class that behaves like a lightweight object, with attributes you define when initializing.
For example, let’s say you want to create a simple object to represent a point in a 2D space with x
and y
coordinates. You could do this:
1
2
3
4
5
Point = Struct.new(:x, :y)
point = Point.new(10, 20)
puts point.x # Output: 10
puts point.y # Output: 20
Here, Struct.new
generates a class Point
with x
and y
attributes, along with methods to get and set these values. In this case, Struct
saves you the trouble of explicitly defining a class with an initializer and accessors for these attributes.
When to Use Struct
in Rails
While you’ll primarily work with ActiveRecord models and service objects in Rails, Struct
has its place for simple, lightweight objects that don’t need the full capabilities of a model. Here are some cases where Struct
can be helpful:
1. Temporary Data Storage
When you need to store data temporarily but don’t want to create a full model or class, Struct
is ideal. This is especially true when the data doesn’t need to persist in the database but only exists for the duration of a request or session.
Example: Imagine you are working on a booking system and want to create a summary object for a trip without saving it to the database.
1
2
3
4
5
TripSummary = Struct.new(:destination, :duration, :price)
summary = TripSummary.new("Hawaii", "7 days", 1500)
puts summary.destination # Output: Hawaii
puts summary.price # Output: 1500
2. API Responses
If your Rails application interacts with external APIs, you might receive JSON responses that map to structured data. You can easily wrap these responses in a Struct
to make handling them more organized and readable.
Example: Suppose you’re working with an API that returns weather data:
1
2
3
4
5
6
7
WeatherData = Struct.new(:temperature, :humidity, :wind_speed)
response = { temperature: 75, humidity: 65, wind_speed: 10 }
weather = WeatherData.new(response[:temperature], response[:humidity], response[:wind_speed])
puts weather.temperature # Output: 75
puts weather.humidity # Output: 65
Using Struct
here helps encapsulate the response in a clear, domain-specific object.
3. Form Objects
In Rails, form objects are often used to handle complex forms that involve multiple models or non-database attributes. Instead of creating a whole new class for every form, you can use Struct
to gather attributes temporarily.
Example: Let’s say you’re building a multi-step signup process. For one step, you need to collect personal information:
1
2
3
PersonalInfo = Struct.new(:first_name, :last_name, :email)
info = PersonalInfo.new("John", "Doe", "john.doe@example.com")
With this, you can pass the PersonalInfo
object around, making the form process more manageable.
Enhancing Struct
with Custom Methods
One of the great things about Struct
is that it behaves just like a regular Ruby class, so you can add custom methods to it. This allows you to encapsulate logic related to your Struct
object.
Example:
Let’s enhance the TripSummary
example by adding a method to calculate the total cost with taxes.
1
2
3
4
5
6
7
8
TripSummary = Struct.new(:destination, :duration, :price) do
def total_cost_with_tax(tax_rate)
price + (price * tax_rate)
end
end
summary = TripSummary.new("Hawaii", "7 days", 1500)
puts summary.total_cost_with_tax(0.10) # Output: 1650
This allows you to keep related logic within the object, just like you would with a full-fledged class.
Comparing Struct
to POROs and Models
While Struct
is a useful tool, it’s important to know when not to use it. Here’s a quick comparison of Struct
vs. other common objects in Rails:
Aspect | Struct | PORO | ActiveRecord Model |
---|---|---|---|
Complexity | Lightweight, simple | Can be lightweight or complex | Full-featured ORM |
Persistence | No database interaction | No database interaction | Interacts with the database |
Customization | Easy to define with custom methods | Fully customizable | Fully customizable |
Use Case | Temporary data, API responses, form data | Business logic, service objects | Persistent data, business logic |
Struct
in Ruby on Rails is a versatile and efficient way to handle simple data structures without the need for full-blown classes or ActiveRecord models. It’s perfect for lightweight tasks like temporary data storage, handling API responses, or encapsulating form attributes. By using Struct
in the right context, you can simplify your code and make it more readable while avoiding unnecessary complexity.