Есть ли способ заставить attr_reader создавать методы с именем, отличным от имени переменной экземпляра?

#ruby #attributes #conventions #standard-library

#ruby #атрибуты #соглашения #стандартная библиотека

Вопрос:

В Ruby есть ли способ сделать что-то вроде

 class Foo
    attr_reader :var_name :reader_name #This won't work, of course
    def initialize
        @var_name = 0
    end
end

# stuff here ....

def my_method
    foo = Foo.new
    assert_equal 0,foo.reader_name
end
  

Другими словами, существует ли стандартный способ создания метода доступа для переменной, который использует имя, отличное от переменной. (Помимо ручного кодирования, конечно.)

Комментарии:

1. Зачем вам это нужно? Вероятно, это плохая практика.

2. Когда ваши внутренние имена отличаются от спецификации внешнего интерфейса. Это появилось при работе над about_dice_project ruby-koan. Они хотят вызвать список брошенных кубиков, сформированный самым последним values . Я хочу вызвать это @lastRolled . Один относится к способу его реализации (как и положено внутренней переменной), а другой относится к тому, как спецификатор думал об этом. В данном случае это не имеет особого значения, но я мог бы предположить, что это то, что я, возможно, захочу узнать в будущем.

3. голосую за хорошо написанный Q. Мне не обязательно нравится то, о чем вы просите 😉

Ответ №1:

Вы могли бы использовать alias_method :

 class Foo
  attr_reader :var_name
  alias_method :reader_name, :var_name
  def initialize
    @var_name = 0
  end
end
  

var_name Метод, созданный attr_reader , все равно был бы доступен. Вы могли бы использовать remove_method для избавления от var_name метода, если бы действительно хотели (но убедитесь, что вы получаете все в правильном порядке):

 class Foo
  attr_reader :var_name
  alias_method :reader_name, :var_name
  remove_method :var_name
  def initialize
    @var_name = 0
  end
end
  

Если бы вы действительно хотели, вы могли бы обезьяньим способом встроить attr_reader_as метод в Module:

 class Module
  def attr_reader_as(attr_name, alias_name)
    attr_reader attr_name
    alias_method alias_name, attr_name
    remove_method attr_name
  end
end

class Foo
  attr_reader_as :var_name, :reader_name
  def initialize
    @var_name = 0
  end
end
  

attr_reader_as Лучше было бы использовать хэш (например { var_name: :reader_name, var_name: :reader_name2} ), но я оставлю это как упражнение для читателя.

Ответ №2:

Вы не можете передать какие-либо параметры attr_reader

Это ручное кодирование?

 class Foo
  def initialize
      @var_name = 0
  end
  def reader_name; @var_name; end
end
  

Комментарии:

1. 1 за «не будь таким ленивым и сделай это сам». Извините, что испортил вашу репутацию крутого и удачливого 8888.

2. Да, это ручное кодирование. Но ваш ответ дает мне понять, что, скорее всего, большинство программистов ruby поступили бы именно так. Лень — это, конечно, достоинство .

Ответ №3:

Прежде всего, моя мантра: хороший программист хорошо играет со своими товарищами по команде. Следующие соглашения подходят другим.

То, что вы хотите, кажется нетрадиционным, и, как утверждает @edgerunner, плохая практика (читай: я бы тебя отшлепал). Все, что я говорю, дважды подумайте об этом, я не знаю вашего варианта использования, он может оказаться допустимым… В любом случае, немного веселья во время пасхи должно быть разрешено, поэтому я немного попрактиковался в метапрограммировании и поиграл с миксинами.

 class Foo
  include MyAttrReader
  my_attr_reader :var_name, :reader_name
  def initialize
    @var_name = 0
  end
end
  

довольно чисто. Модуль выполняет трюк (обязательно загрузите этот модуль перед классом)

 module MyAttrReader
  def self.included(base)
    base.extend ClassMethods
  end

  module ClassMethods
    def my_attr_reader(attribute, read_attribute)
      define_method "#{read_attribute}" do
        instance_variable_get "@#{attribute}"
      end
    end
  end
end
  

Тестирование в irb

 ruby-1.9.2-p180 :001 > load 'my_attr_reader.rb'
 => true 
ruby-1.9.2-p180 :002 > load 'foo.rb'
 => true 
ruby-1.9.2-p180 :003 > f = Foo.new
 => #<Foo:0x00000100979658 @var_name=0> 
ruby-1.9.2-p180 :004 > f.reader_name
 => 0 
ruby-1.9.2-p180 :005 > f.var_name
NoMethodError: undefined method `var_name' for #<Foo:0x00000100979658 @var_name=0>
  

Я надеюсь, вы видите, насколько неинтуитивным это будет для других. Вы могли бы переопределить метод to_s, чтобы показать, как получить доступ к @var_name, но я бы не стал туда заходить … или определить метод var_name тоже (два метода получения). Придерживайтесь соглашений. В любом случае, мне было очень весело, удачи!