Understanding `cattr_accessor` in Ruby on Rails


In Ruby on Rails development, sharing data across multiple instances of a class often requires careful handling of class-level variables. ActiveSupport, a core component of Rails, provides several elegant methods to define class-level attributes. Among these, cattr_accessor
stands out as a powerful yet sometimes overlooked utility. This post explores what cattr_accessor
is, how it works, and when to use it in your Rails applications.
What is cattr_accessor
?
cattr_accessor
is a method provided by Rails' ActiveSupport that creates both getter and setter methods for class variables. It generates methods to access a class variable from both the class level and instance level, streamlining the management of shared state across all instances of a class.
How cattr_accessor
Works
When you declare a class attribute using cattr_accessor
, Rails automatically creates:
A class variable (prefixed with
@@
)Class-level getter and setter methods
Instance-level methods that read from and write to the class variable
This single declaration produces multiple methods, saving you from writing boilerplate code.
A Practical Example: Configuration Settings
Consider a scenario where you need to manage configuration settings for a payment gateway integration:
class PaymentGateway
cattr_accessor :api_key, :environment, :timeout
# Default values
@@api_key = nil
@@environment = :sandbox
@@timeout = 30
def process_payment(amount)
# The instance method can access the class variables
client = ApiClient.new(
key: self.api_key,
env: self.environment,
timeout: self.timeout
)
client.charge(amount)
end
def self.configure
yield self if block_given?
end
end
This implementation enables elegant configuration patterns:
# In an initializer file:
PaymentGateway.configure do |config|
config.api_key = ENV['PAYMENT_API_KEY']
config.environment = Rails.env.production? ? :production : :sandbox
config.timeout = 60
end
# Later, in your application:
gateway = PaymentGateway.new
gateway.process_payment(100.00) # Uses the configured values
Comparing with Other Accessor Types
To understand cattr_accessor
better, let's compare it with other accessor types:
Accessor Type | Creates | Access Level | Use Case |
attr_accessor | Instance variables | Instance only | Per-instance state |
cattr_accessor | Class variables | Class & instance | Shared state across all instances |
mattr_accessor | Module variables | Module & included classes | Shared state in a module context |
Real-World Application: Circuit Breaker Pattern
A practical application of cattr_accessor
is implementing a circuit breaker pattern for external API calls:
class ExternalApiClient
cattr_accessor :failure_count, :last_failure_time, :circuit_open
# Initialize class variables
@@failure_count = 0
@@last_failure_time = nil
@@circuit_open = false
FAILURE_THRESHOLD = 5
RETRY_TIMEOUT = 300 # seconds
def self.check_circuit
# Reset circuit if timeout has passed
if @@circuit_open && @@last_failure_time && (Time.now - @@last_failure_time) > RETRY_TIMEOUT
@@circuit_open = false
@@failure_count = 0
end
@@circuit_open
end
def make_api_call(endpoint)
if self.class.check_circuit
raise "Circuit breaker open - too many failures"
end
begin
response = HTTP.get(endpoint)
# Reset on success
@@failure_count = 0
response
rescue => e
@@failure_count += 1
@@last_failure_time = Time.now
if @@failure_count >= FAILURE_THRESHOLD
@@circuit_open = true
end
raise e
end
end
end
In this example, all instances of ExternalApiClient
share the same circuit state. If one instance experiences multiple failures, all instances will stop attempting API calls until the retry timeout expires.
Best Practices
When using cattr_accessor
, consider these best practices:
Use sparingly: Class variables maintain state across all instances, which can lead to unexpected behavior if overused.
Initialize values: Always set default values for your class variables to avoid nil errors.
Consider thread safety: In multi-threaded environments, protect writes to class variables with synchronization mechanisms if needed.
Document clearly: Since class variables affect all instances, clearly document their purpose and expected usage.
Conclusion
The cattr_accessor
method elegantly solves the problem of sharing state across all instances of a class. It's particularly useful for configuration settings, global state management, and patterns like circuit breakers. By understanding how and when to use this utility, you can write more maintainable and concise Rails code.
When used appropriately, cattr_accessor
helps strike the right balance between encapsulation and shared functionality, enabling clean patterns that would otherwise require more complex code structures.
Subscribe to my newsletter
Read articles from phanil kumar directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

phanil kumar
phanil kumar
Ruby/Rails