From: radek.bulat@...
Date: 2020-11-11T12:54:19+00:00
Subject: [ruby-core:100782] [Ruby master Feature#17314] Provide a way to declare visibility of attributes defined by attr* methods in a single expression

Issue #17314 has been updated by radarek (Rados��aw Bu��at).


sawa (Tsuyoshi Sawada) wrote in #note-2:
> Private attribute methods defeat the purpose of attribute methods. If you want to access them within the class, you can directly access the instance variables.
> 
> >[I]t's convenient to see what kind of attributes [a] class has
> 
> For that purpose, you can write that as a comment in the code. Defining methods just for that purpose is waste of resource.

What kind of resource, cpu? But there are other resources involved which is more importand - humans. Using private attributes gives you more flexibility and it's easier to refactor.

If you use accessor methods you can later refactor them without checking where instance variables is used.

``` ruby
class Foo
  attr_accessor :bar
  private :bar, :bar=
end
```

Later you decided to change setter so it validates value

``` ruby
class Foo
  attr_reader :bar
  private :bar

  private def bar=(value)
    raise ArgumentError if if value < 0
    @bar = value
  end
end
```

It also works with subclassing. If you access or change your state through accessors methods then it's much easier to refactor you base class without worrying about other places.

zverok (Victor Shepelev) wrote in #note-4:
> The idea behind private attribute methods (which I've seen used in a lot of codebases, and use myself) is uniformity of the call-sequence of different values. I believe Avdi Grimm had a nice article on this point of view (will try to find it).

I guess you are talking about this Ruby Tapas episode: https://www.rubytapas.com/2018/06/05/barewords/


----------------------------------------
Feature #17314: Provide a way to declare visibility of attributes defined by attr* methods in a single expression
https://bugs.ruby-lang.org/issues/17314#change-88427

* Author: radarek (Rados��aw Bu��at)
* Status: Open
* Priority: Normal
----------------------------------------
**Description**

Many of us (me included) declare class attributes, even if they are private, on the top of class definition. When reading source code it's convinient to see what kind of attributes class has.

To declare private attributes we can:
* declare them with one of `attr*` methods and later change visiblity calling `private`
* call `private` without argument, then declare attributes and finally call (in most cases) `public` to keep defining public methods
* declare attribute on top of the class but make them private in private section later in a file

``` ruby
clsss Foo
  attr_accessor :foo
  private :foo, :foo= # we have to remember about :foo= too

  private

  attr_accessor :bar

  public

  # rest of the code
end
```

To simplify it and create other possibilites I propose to:
* change `attr*` methods so as they return array of defined methods names
* allow `public/protected/private` methods to receive array of methods names (single argument)

With requested feature we could write code like this:

``` ruby
class Foo
  private attr_accessor :foo, :bar
end
```

Additionaly you could use `attr*` with your own methods. Something like this:

``` ruby
class Module
  def traceable(names)
    # ...
    names
  end
end

class Foo
  traceable attr_accessor :foo
  # it can be mixed with public/protected/private too
  protected traceable attr_accessor :bar
end
```

**Backward compatibility**

* `attr*` methods currently return `nil` so there should be no problem with changing them
* `public/protected/private` methods receive multiple positional arguments and convert all non symbol/string objects to strings. I can imagine only one case where compatibility would be broken:

``` ruby
class Foo
  def foo; end
  def bar; end

  arr = [:foo]
  def arr.to_str
    'bar'
  end

  private arr
end

p [Foo.public_instance_methods(false), Foo.private_instance_methods(false)]
```

Currently `[[:foo], [:bar]]` would be displayed, `[[:bar], [:foo]]` after requested feature is implemented.

**Implementation**

You can view my implementation in this (draft) PR: https://github.com/ruby/ruby/pull/3757




-- 
https://bugs.ruby-lang.org/

Unsubscribe: <mailto:ruby-core-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>