Feature #6470
closedMake attr_accessor return the list of generated method
Description
attr_accesor currently returns nil. It would be more helpful if it return list of generated methods so that it can become an argument to other methods like :private or :protected. That way private accessors can still be defined at top of the class and be private without changing the visibility of next methods.
class Something
private *attr_accessor :user, :action # IMHO This is nice
# private attr_accessor :user, :action # <-- would be even better if :private method accepted arrays
def initialize(user, action)
self.user = user
self.action = action
end
def public_method
user.do_something(action)
end
end
VS
class Something
private; attr_accessor :user, :action; public # IMHO Hack!!
def initialize(user, action)
self.user = user
self.action = action
end
def public_method
user.do_something(action)
end
end
VS
class Something
def initialize(user, action)
self.user = user
self.action = action
end
def public_method
user.do_something(action)
end
private
attr_accessor :user, :action # IMHO Does not look nice at bottom of the class definition
end
Updated by henry.maddocks (Henry Maddocks) over 12 years ago
Aren't accessors public by definition? If you want them to be private use attr.
Updated by shevegen (Robert A. Heiler) over 12 years ago
Yes, they are public.
If I understood them correctly, they are the same as this in pure ruby code:
attr_accessor :foo
def foo
@foo
end
def foo=(i)
@foo = i
end
I found that I personally only need attr_reader
, attr_writer
and attr_accessor
.
I have not found a use case for attr
alone yet.
Updated by mame (Yusuke Endoh) over 12 years ago
- Status changed from Open to Assigned
- Assignee set to matz (Yukihiro Matsumoto)
This duplicates #6198.
BTW, why don't you use instance variables directly? That is:
class Something
def initialize(user, action)
@user = user
@action = action
end
def public_method
@user.do_something(@action)
end
end
--
Yusuke Endoh mame@tsg.ne.jp
Updated by rupert (Robert Pankowecki) over 12 years ago
I want to access my private fields also via methods instead of directly via instance variables so refactoring in future is easier. For example instead of finding in class all occurrences of @var = something and changing them into either "@var = something.strip" or extracting it into setter, I already use the private setter defined with attr_accessor everywhere. That way I only need to change the setter implementation in one place and don't need to look for code setting instance variable because there is non such, only calls for the accessor.
Updated by matz (Yukihiro Matsumoto) almost 6 years ago
- Status changed from Assigned to Rejected
There's no use for private
attr_reader, attr_writer, etc.
And protected
is not encouraged enough for new features. So I reject this.
Matz.
Updated by Eregon (Benoit Daloze) almost 6 years ago
- Status changed from Rejected to Open
@matz (Yukihiro Matsumoto) There are use cases, see https://bugs.ruby-lang.org/issues/11539 and https://bugs.ruby-lang.org/issues/11541.
Also, one case which has been IIRC frequently requested (mentioned just above in this issue, https://bugs.ruby-lang.org/issues/6470#note-4) is:
attr_reader :foo
private attr_writer :foo
So one can use the symmetric foo
and foo=
in the class, but only the getter would be public.
This is also useful to evolve foo=
(e.g., to invalidate some caches if set) and add extra logic in it, without having to change all places from @foo =
to self.foo =
.
I reopen because I think not all relevant issues have been considered.
In general, I support this feature as it is a general and composable extension which enables many more things such as for def
(decorators, debugging, concurrency, etc).
Updated by Eregon (Benoit Daloze) almost 6 years ago
- Related to Feature #11539: Support explicit declaration of volatile instance variables added
Updated by Eregon (Benoit Daloze) almost 6 years ago
- Related to Feature #11541: Let attr_accessor, _reader & _writer return symbols of the defined methods added
Updated by phluid61 (Matthew Kerwin) almost 6 years ago
In general I support this request, but in this proposed use-case ...
attr_reader :foo private attr_writer :foo
So one can use the symmetric
foo
andfoo=
in the class, but only the getter would be public.
This is also useful to evolvefoo=
(e.g., to invalidate some caches if set) and add extra logic in it, without having to change all places from@foo =
toself.foo =
.
foo=
without @
or self.
will assign a local variable. You'd have to change it to foo=(...)
or send :foo=, ...
anyway, no?
Updated by rupert (Robert Pankowecki) almost 6 years ago
There's no use for private attr_reader, attr_writer, etc.
The intended usage is to ease future refactorings. If you always start with a method then later you can easily redefine just the method.
Initial code
class Something
private attr_accessor :x, :y
def something(a)
self.x = a + y
end
end
Code after refactoring:
class Something
private attr_accessor :y
private attr_reader :x
def something(a)
self.x = a + y
end
private
def x=(new_value)
@x_set_at = Time.now
@x = new_value
end
Notice that nothing setting @x
had to be refactored because @x
variable was always changed via the self.x=
setter.
So when the time comes and cache expiration or additional logic needs to be added, makes it easy to just redefine the setter or getter with additional logic.
That's why I always prefer to use private accessors instead of instance variables. They are more flexible.
Updated by schneems (Richard Schneeman) over 3 years ago
This is implemented https://twitter.com/avdi/status/1380213296108867586. Let's close this ticket!
:)
Updated by k0kubun (Takashi Kokubun) over 3 years ago
- Related to Feature #17314: Provide a way to declare visibility of attributes defined by attr* methods in a single expression added
Updated by k0kubun (Takashi Kokubun) over 3 years ago
- Status changed from Open to Closed