Add option for class_attribute default #29270

Merged
merged 5 commits into from May 29, 2017

Conversation

Projects
None yet
7 participants
@dhh
Member

dhh commented May 29, 2017

It's extremely common, as evidenced by the commit in this PR of Rails conversions, to set a default value for a class_attribute. So let's provide that directly.

actionview/lib/action_view/layouts.rb
- class_attribute :_layout, :_layout_conditions, instance_accessor: false
- self._layout = nil
- self._layout_conditions = {}
+ class_attribute :_layout

This comment has been minimized.

@matthewd

matthewd May 29, 2017

Member

Lost instance_accessor: false

@matthewd

matthewd May 29, 2017

Member

Lost instance_accessor: false

@@ -123,6 +124,10 @@ def class_attribute(*attrs)
remove_possible_method "#{name}="
attr_writer name
end
+
+ if default_value

This comment has been minimized.

@matthewd

matthewd May 29, 2017

Member

unless default_value.nil?

@matthewd

matthewd May 29, 2017

Member

unless default_value.nil?

This comment has been minimized.

@matthewd

matthewd May 29, 2017

Member

.. or just change the nil on line 79/80

@matthewd

matthewd May 29, 2017

Member

.. or just change the nil on line 79/80

@@ -53,9 +53,8 @@ class << self; attr_accessor :helpers_path; end
include AbstractController::Helpers
included do
- class_attribute :helpers_path, :include_all_helpers
- self.helpers_path ||= []

This comment has been minimized.

@dhh

dhh May 29, 2017

Member

Probably shouldn't replace this with a default:, right? Given that it checks the parent class first.

@dhh

dhh May 29, 2017

Member

Probably shouldn't replace this with a default:, right? Given that it checks the parent class first.

This comment has been minimized.

@matthewd

matthewd May 29, 2017

Member

I hesitated at that, but ultimately convinced myself the ||= is a lie: class_attribute will always define a method on the current class, with the value set to nil... so AFAICS, this is no different from if it were a straight assignment.

@matthewd

matthewd May 29, 2017

Member

I hesitated at that, but ultimately convinced myself the ||= is a lie: class_attribute will always define a method on the current class, with the value set to nil... so AFAICS, this is no different from if it were a straight assignment.

This comment has been minimized.

@dhh

dhh May 29, 2017

Member

Ah, right. Yup, good 👍

@dhh

dhh May 29, 2017

Member

Ah, right. Yup, good 👍

@@ -62,8 +62,7 @@ module Callbacks
included do
extend ActiveSupport::DescendantsTracker
- class_attribute :__callbacks, instance_writer: false
- self.__callbacks ||= {}

This comment has been minimized.

@dhh

dhh May 29, 2017

Member

Same issue here: "Probably shouldn't replace this with a default:, right? Given that it checks the parent class first."

@dhh

dhh May 29, 2017

Member

Same issue here: "Probably shouldn't replace this with a default:, right? Given that it checks the parent class first."

instance_predicate = options.fetch(:instance_predicate, true)
+ default_value = options.fetch(:default, nil)

This comment has been minimized.

@matthewd

matthewd May 29, 2017

Member

This (really, the fact it's in the options hash at all) does mean the methods below will capture the default value in their closure -- so even if the value is changed on the root class, the original default won't be GCed.

I don't think we particularly need to care about a couple of extra empty hashes and arrays floating around, but I figured I'd at least mention it.

@matthewd

matthewd May 29, 2017

Member

This (really, the fact it's in the options hash at all) does mean the methods below will capture the default value in their closure -- so even if the value is changed on the root class, the original default won't be GCed.

I don't think we particularly need to care about a couple of extra empty hashes and arrays floating around, but I figured I'd at least mention it.

This comment has been minimized.

@dhh

dhh May 29, 2017

Member

Would be nice to convert all these to kwargs. Should probably do a big sweep for Rails 6.

@dhh

dhh May 29, 2017

Member

Would be nice to convert all these to kwargs. Should probably do a big sweep for Rails 6.

This comment has been minimized.

@rafaelfranca

rafaelfranca May 30, 2017

Member

We can do it right now. We only support Ruby versions that work with kwargs

@rafaelfranca

rafaelfranca May 30, 2017

Member

We can do it right now. We only support Ruby versions that work with kwargs

@@ -70,9 +70,10 @@ class Class
# To opt out of both instance methods, pass <tt>instance_accessor: false</tt>.
def class_attribute(*attrs)
options = attrs.extract_options!
- instance_reader = options.fetch(:instance_accessor, true) && options.fetch(:instance_reader, true)
- instance_writer = options.fetch(:instance_accessor, true) && options.fetch(:instance_writer, true)
+ instance_reader = options.fetch(:instance_accessor, true) && options.fetch(:instance_reader, true)

This comment has been minimized.

@simi

simi May 29, 2017

Contributor

Sorry for a little off-topic here: I have spotted this formatting changes in few recent PRs. Is this new standard? Wasn't this considered "cosmetic" change before?

@simi

simi May 29, 2017

Contributor

Sorry for a little off-topic here: I have spotted this formatting changes in few recent PRs. Is this new standard? Wasn't this considered "cosmetic" change before?

@dhh

This comment has been minimized.

Show comment
Hide comment
@dhh

dhh May 29, 2017

Member
Member

dhh commented May 29, 2017

@dhh dhh merged commit 1c275d8 into master May 29, 2017

0 of 3 checks passed

codeclimate Code Climate is analyzing this code.
Details
continuous-integration/travis-ci/pr The Travis CI build is in progress
Details
continuous-integration/travis-ci/push The Travis CI build is in progress
Details

@dhh dhh deleted the class-attribute-default branch May 29, 2017

@gsamokovarov

This comment has been minimized.

Show comment
Hide comment
@gsamokovarov

gsamokovarov May 29, 2017

Contributor

David, have you tried defaulting with a block?

class_attribute :periodic_timers, instance_reader: false, default: []

vs

class_attribute(:periodic_timers, instance_reader: false) { [] }

The reason I'm bringing it up is because mattr_accessor works like 👆 and does not have the default option. Should we pair the functionality so it's symmetrical across all the attribute extensions?

Contributor

gsamokovarov commented May 29, 2017

David, have you tried defaulting with a block?

class_attribute :periodic_timers, instance_reader: false, default: []

vs

class_attribute(:periodic_timers, instance_reader: false) { [] }

The reason I'm bringing it up is because mattr_accessor works like 👆 and does not have the default option. Should we pair the functionality so it's symmetrical across all the attribute extensions?

@dhh

This comment has been minimized.

Show comment
Hide comment
@dhh

dhh May 29, 2017

Member

That strikes me as less revealing, but the upside is that it won't get evaluated until you access it, which is a nice perk. Default does have symmetry with belongs_to/default, though.

Member

dhh commented May 29, 2017

That strikes me as less revealing, but the upside is that it won't get evaluated until you access it, which is a nice perk. Default does have symmetry with belongs_to/default, though.

@gsamokovarov

This comment has been minimized.

Show comment
Hide comment
@gsamokovarov

gsamokovarov May 29, 2017

Contributor

Add a default to mattr_accessor as well then? Dunno if it will make the interface too big though.

Contributor

gsamokovarov commented May 29, 2017

Add a default to mattr_accessor as well then? Dunno if it will make the interface too big though.

@dhh

This comment has been minimized.

Show comment
Hide comment
@dhh

dhh May 29, 2017

Member
Member

dhh commented May 29, 2017

@dhh

This comment has been minimized.

Show comment
Hide comment
@dhh

dhh May 30, 2017

Member
Member

dhh commented May 30, 2017

@gsamokovarov

This comment has been minimized.

Show comment
Hide comment
@gsamokovarov

gsamokovarov May 30, 2017

Contributor

mattr_accessor family actually accept options as well, they let you choose whether or not to have the instance methods. The interfaces between class_attributes and cattr_accessor aren't that far off.

Contributor

gsamokovarov commented May 30, 2017

mattr_accessor family actually accept options as well, they let you choose whether or not to have the instance methods. The interfaces between class_attributes and cattr_accessor aren't that far off.

@kaspth

This comment has been minimized.

Show comment
Hide comment
@kaspth

kaspth May 30, 2017

Member

Let's make it so!

@dhh GitHub didn't connect your email reply: this was in reference to kwargs across the board right?

Member

kaspth commented May 30, 2017

Let's make it so!

@dhh GitHub didn't connect your email reply: this was in reference to kwargs across the board right?

@dhh

This comment has been minimized.

Show comment
Hide comment
@dhh

dhh May 30, 2017

Member
Member

dhh commented May 30, 2017

@dhh

This comment has been minimized.

Show comment
Hide comment
@dhh

dhh May 30, 2017

Member
Member

dhh commented May 30, 2017

@kaspth

This comment has been minimized.

Show comment
Hide comment
@kaspth

kaspth May 30, 2017

Member

Looks like mattr_* just yields to the block immediately, so there's no lazy loading at all:

class_variable_set("@@#{sym}", yield) if block_given?

Then there doesn't seem to be a need for default: to take lambdas — besides all our class_attribute didn't need deferred evaluation, and I haven't seen the need for it in my app's usage of mattr_* either.

I'd be 👍 on deprecating the block to mattr_* and cattr_* and replacing it with a default: arg.

Member

kaspth commented May 30, 2017

Looks like mattr_* just yields to the block immediately, so there's no lazy loading at all:

class_variable_set("@@#{sym}", yield) if block_given?

Then there doesn't seem to be a need for default: to take lambdas — besides all our class_attribute didn't need deferred evaluation, and I haven't seen the need for it in my app's usage of mattr_* either.

I'd be 👍 on deprecating the block to mattr_* and cattr_* and replacing it with a default: arg.

@dhh

This comment has been minimized.

Show comment
Hide comment
@dhh

dhh May 30, 2017

Member
Member

dhh commented May 30, 2017

@gsamokovarov

This comment has been minimized.

Show comment
Hide comment
@gsamokovarov

gsamokovarov May 30, 2017

Contributor

Cool, I can take stab at that.

Contributor

gsamokovarov commented May 30, 2017

Cool, I can take stab at that.

@teeceepee teeceepee referenced this pull request in rails/activestorage Jul 11, 2017

Closed

Incompatible with Rails 5.1.2 #39

@kennyadsl kennyadsl referenced this pull request in solidusio/solidus Aug 1, 2017

Merged

Allow dev mode code reloading of configured classes #2126

jbourassa pushed a commit to jbourassa/rails that referenced this pull request Aug 24, 2017

Jimmy Bourassa
Fix AM::Base.default proc arity breaking change
PR #29270 changed the number of arguments that gets passed to Procs
defined in ActionMail::Base.default.

This fixes it and introduce a deprecation so the hack can be removed
(the argument is useless since it has the same value as `self`).

jbourassa pushed a commit to jbourassa/rails that referenced this pull request Aug 24, 2017

Jimmy Bourassa
Fix AM::Base.default proc arity breaking change
PR #29270 changed the number of arguments that gets passed to Procs
defined in ActionMail::Base.default.

This fixes it and introduce a deprecation so the hack can be removed
(the argument is useless since it has the same value as `self`).

jbourassa pushed a commit to jbourassa/rails that referenced this pull request Aug 24, 2017

Jimmy Bourassa
Fix AM::Base.default proc arity breaking change
PR #29270 changed the number of arguments that gets passed to Procs
defined in ActionMail::Base.default.

This fixes it and introduce a deprecation so the hack can be removed
(the argument is useless since it has the same value as `self`).

jbourassa pushed a commit to jbourassa/rails that referenced this pull request Aug 29, 2017

Jimmy Bourassa
Fix AM::Base.default proc arity breaking change
PR #29270 changed the number of arguments that gets passed to Procs
defined in ActionMail::Base.default. With this changeset, Procs can
now have 1 or 0 arguments

Also adds test coverage for AM::Base.default Proc arity.

jbourassa pushed a commit to jbourassa/rails that referenced this pull request Aug 29, 2017

Jimmy Bourassa
Fix AM::Base.default proc arity breaking change
PR #29270 changed the number of arguments that gets passed to Procs
defined in ActionMail::Base.default. With this changeset, Procs can
now have 1 or 0 arguments

Also adds test coverage for AM::Base.default Proc arity.

antonzaytsev added a commit to sdtechdev/activestorage-rails4 that referenced this pull request Dec 19, 2017

Set default value of service.url_expires_in after initialization
As default value for class_attribute introduced only in Rails 5
rails/rails#29270

matthewrudy pushed a commit to matthewrudy/rude-rails that referenced this pull request Feb 12, 2018

Add option for class_attribute default (#29270)
* Allow a default value to be declared for class_attribute

* Convert to using class_attribute default rather than explicit setter

* Removed instance_accessor option by mistake

* False is a valid default value

* Documentation
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment