This blog is part of our Rails 5.2 series.
When DHH introduced support for specifying a default value for class_attribute, Genadi Samokovarov brought to notice that the module and class attribute accessor macros also support specifying a default value but using a block and not with a default option.
To have consistent and symmetrical behaviour across all the attribute extensions, it was decided to support specifying a default value using default option for all the module and class attribute macros as well.
mattr_accessor, mattr_reader and mattr_writer macros generate getter and setter methods at the module level.
Similarly, cattr_accessor, cattr_reader, and cattr_writer macros generate getter and setter methods at the class level.
Before Rails 5.2
Before Rails 5.2, this is how we would set the default values for the module and class attribute accessor macros.
1module ActivityLoggerHelper 2 mattr_accessor :colorize_logs 3 mattr_writer :log_ip { false } 4 5 self.colorize_logs = true 6end 7 8class ActivityLogger 9 include ActivityLoggerHelper 10 11 cattr_writer :logger { Logger.new(STDOUT) } 12 cattr_accessor :level 13 cattr_accessor :settings 14 cattr_reader :pid { Process.pid } 15 16 @@level = Logger::DEBUG 17 self.settings = {} 18end
After Rails 5.2
We can still set a default value of a module or class attribute accessor by providing a block. In this pull request, support for specifying a default value using a new default option has been introduced.
So instead of
1cattr_writer :logger { Logger.new(STDOUT) }
or
1cattr_writer :logger 2self.logger = Logger.new(STDOUT)
or
1cattr_writer :logger 2@@logger = Logger.new(STDOUT)
we can now easily write
1cattr_writer :logger, default: Logger.new(STDOUT)
Same applies to the other attribute accessor macros like mattr_accessor, mattr_reader, mattr_writer, cattr_accessor, and cattr_reader.
Note that, the old way of specifying a default value using the block syntax will work but will not be documented anywhere.
Also, note that if we try to set the default value by both ways i.e. by providing a block as well as by specifying a default option; the value provided by default option will always take the precedence.
1mattr_accessor(:colorize_logs, default: true) { false }
Here, @@colorize_logs would be set with true as per the above precedence rule.
Here is a test which verifies this behavior.
Finally, here is simplified version using the new default option.
1module ActivityLoggerHelper 2 mattr_accessor :colorize_logs, default: true 3 mattr_writer :log_ip, default: false 4end 5 6class ActivityLogger 7 include ActivityLoggerHelper 8 9 cattr_writer :logger, default: Logger.new(STDOUT) 10 cattr_accessor :level, default: Logger::DEBUG 11 cattr_accessor :settings, default: {} 12 cattr_reader :pid, default: Process.pid 13end