Rails 5.2 default option to module & class attribute accessors

Vishal Telangre

By Vishal Telangre

on February 27, 2018

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

Stay up to date with our blogs. Sign up for our newsletter.

We write about Ruby on Rails, ReactJS, React Native, remote work,open source, engineering & design.