What is cattr_accesor and mattr_accessor, thread_cattr or thread_mattr in Ruby and in Rails?

In Ruby on Rails, the methods cattr_*, mattr_*, thread_cattr_*, and thread_mattr_* methods are used to define class-level and thread-safe class-level attributes and variables. These methods are provided by the ActiveSupport library, which is a core component of Rails that provides various utility classes and methods. These methods are useful for scenarios where you want to store data at the class or module level, rather than at the instance level. In addition, the "thread" variants are useful in multi-threaded applications where you want to avoid data inconsistencies between threads.

cattr_* - Class-Level Attribute Methods

It defines methods to set a variable per class. We have three methods:

  • cattr_accessor: Defines both a getter and a setter method for a class-level attribute.
  • cattr_reader: Defines a getter method for a class-level attribute.
  • cattr_writer: Defines a setter method for a class-level attribute.
# cattr_*  example in Ruby
class PlainRubyClass
  def self.only_readable
    @@only_readable ||= 111
  end

  def self.only_writeable=(value)
    @@only_writeable = value
  end

  def self.count
    @@count ||= 0
  end

  def self.count=(value)
    @@count = value
  end
end

puts PlainRubyClass.count  # Output: 0
PlainRubyClass.count = 10
puts PlainRubyClass.count  # Output: 10
PlainRubyClass.count = 11
puts PlainRubyClass.count  # Output: 11

puts PlainRubyClass.only_readable # Output: 111
# puts PlainRubyClass.only_readable = 333 # error
# puts PlainRubyClass.only_writeable # error
puts PlainRubyClass.only_writeable = 222 # Output: 222

# cattr_* example in Rails
require 'active_support/core_ext/class/attribute_accessors'
class RailsClass
  cattr_accessor :count, default: 0
  cattr_reader :only_readable, default: 111
  cattr_writer :only_writeable, default: 222
end

puts RailsClass.count # Output: 0
RailsClass.count = 10
puts RailsClass.count  # Output: 10
RailsClass.count = 11
puts RailsClass.count  # Output: 11

puts RailsClass.only_readable # Output: 111
# puts RailsClass.only_readable = 333 # error
# puts RailsClass.only_writeable # error

mattr_* - Module-Level Attribute Methods

Similar to cattr_*, but used inside modules instead of classes. These methods allow you to define module-level attributes and accessors. Currently, it's an alias of cattr_* so it behaves the same as from above.

thread_cattr_* - Thread-Safe Class-Level Attribute Methods

Similar to cattr_*, but the attribute values are stored separately for each thread. This ensures that changes to the attribute in one thread do not affect its value in other threads.

# thread_cattr_* in plain Ruby
module ThreadCattrAccessor
  def thread_cattr_accessor(*names)
    names.each do |name|
      define_singleton_method(name) do
        Thread.current[name]
      end

      define_singleton_method("#{name}=") do |value|
        Thread.current[name] = value
      end
    end
  end
end

class Current
  extend ThreadCattrAccessor
  thread_cattr_accessor :user
end

Current.user = "YourUser"
puts "Current thread: #{Current.user}" # => Current thread: YourUser

Thread.new { p "New thread: #{Current.user}" }.value # => "New thread: nil"

# thread_cattr_* in Rails
require 'active_support/all'

class Current
  thread_cattr_accessor :user
end

Current.user = "YourUser"
puts "Current thread: #{Current.user}" # => Current thread: YourUser
Thread.new { p "New thread: #{Current.user}" }.value # => "New thread: nil"

thread_mattr_* - Thread-Safe Module-Level Attribute Methods

Currently, it's an alias of thread_cattr_accessor, so it behaves the same as above.

Summary

Now you know what the difference is between plain Ruby solutions and Rails solutions, which are just shorthand for doing things like assigning variables per module or class or even safely per thread.

Happy coding!