Ruby: автоматически устанавливать переменную экземпляра в качестве аргумента метода?

Планируется ли реализовать поведение ruby, подобное функции CoffeeScript, заключающейся в указании имени переменной экземпляра в списке аргументов метода? подобно

class User
  def initialize(@name, age)
    # @name is set implicitly, but @age isn't.
    # the local variable "age" will be set, just like it currently works.
  end
end

Мне известен этот вопрос:в Ruby я могу как-то автоматически заполнить переменные экземпляра в методе initialize? , но все решения (включая мое собственное), похоже, не соответствуют философии простоты рубина.

И будут ли какие-либо недостатки такого поведения?

ОБНОВИТЬ

Одной из причин этого является философия DRY (не повторяйся) сообщества ruby. Мне часто приходится повторять имя переменной аргумента, потому что я хочу, чтобы оно было присвоено переменной экземпляра с тем же именем.

def initialize(name)
  # not DRY
  @name = name
end

Единственный недостаток, о котором я могу подумать, это то, что метод может ничего не делать, если у него нет тела. Если вы сканируете быстро, это может выглядеть как запрет. Но я думаю, что со временем мы можем адаптироваться.

Другой недостаток: если вы устанавливаете другие переменные экземпляра в теле, и вы пытаетесь быть удобочитаемыми, помещая все назначения в начале, это может потребовать большей когнитивной силы. чтобы увидеть, что назначения также происходят в списке аргументов. Но я не думаю, что это сложнее, чем, скажем, увидеть вызов константы или метода и перейти к его определению.

# notice: instance var assignments are happening in 2 places! 
def initialize(@name)
  @errors = []
end

Ответы на вопрос(3)

что вы ответили на свой вопрос, он не соответствует философии простоты рубина. Это добавило бы дополнительную сложность к тому, как параметры обрабатываются в методах, и перемещает логику управления переменными в параметры метода. Я вижу аргумент, что это делает код менее читабельным, но это кажется мне не слишком многословным.

Некоторые сценарии, с которыми придется столкнуться @ param:

def initialize( first, last, @scope, @opts = {} )

def search( @query, condition )

def ratchet( @*arg  ) 

Должны ли все эти сценарии быть действительными? Толькоinitialize?@*arg кажется мне особенно рискованным. Все эти правила и исключения делают язык Ruby более сложным. В пользу автоматических переменных экземпляра, я не думаю, что это того стоит.

можно ли на самом деле получить имена аргументов из метода ruby. Если это так, я мог бы использовать специальный префикс аргумента, такой как «iv_», чтобы указать, какие аргументы должны быть установлены в качестве переменных экземпляра.

И это возможно: Как получить имена аргументов, используя отражение.

Да! Так что я могу написать модуль, чтобы справиться с этим для меня. Затем я застрял, потому что, если я вызываю вспомогательный метод модуля, он не знает значений аргументов, потому что они локальны для вызывающей стороны. Ах, но у Рубина есть Связывающие объекты.

Вот модуль (только ruby 1.9):

module InstanceVarsFromArgsSlurper
  # arg_prefix must be a valid local variable name, and I strongly suggest
  # ending it with an underscore for readability of the slurped args.
  def self.enable_for(mod, arg_prefix)
    raise ArgumentError, "invalid prefix name" if arg_prefix =~ /[^a-z0-9_]/i
    mod.send(:include, self)
    mod.instance_variable_set(:@instance_vars_from_args_slurper_prefix, arg_prefix.to_s)
  end

  def slurp_args(binding)
    defined_prefix = self.class.instance_variable_get(:@instance_vars_from_args_slurper_prefix)
    method_name = caller[0][/`.*?'/][1..-2]
    param_names = method(method_name).parameters.map{|p| p.last.to_s }
    param_names.each do |pname|
      # starts with and longer than prefix
      if pname.start_with?(defined_prefix) and (pname <=> defined_prefix) == 1
        ivar_name = pname[defined_prefix.size .. -1]
        eval "@#{ivar_name} = #{pname}", binding
      end
    end
    nil
  end
end

А вот и использование:

class User
  InstanceVarsFromArgsSlurper.enable_for(self, 'iv_')

  def initialize(iv_name, age)
    slurp_args(binding)  # this line does all the heavy lifting
    p [:iv_name, iv_name]
    p [:age, age]
    p [:@name, @name]
    p [:@age, @age]
  end
end

user = User.new("Methuselah", 969)
p user

Выход

[:iv_name, "Methuselah"]
[:age, 969]
[:@name, "Methuselah"]
[:@age, nil]
#<User:0x00000101089448 @name="Methuselah">

Он не позволяет иметь пустое тело метода, но он СУХОЙ. Я уверен, что его можно улучшить, просто указав, какие методы должны иметь такое поведение (реализованное через alias_method), а не вызывая slurp_args в каждом методе - хотя спецификация должна быть после того, как все методы определены.

Обратите внимание, что имя модуля и вспомогательного метода, вероятно, можно улучшить. Я просто использовал первое, что пришло в голову.

А на самом деле..

class User
  define_method(:initialize) { |@name| }
end

User.new(:name).instance_variable_get :@name
# => :name

Работает в 1.8.7, но не в 1.9.3. Теперь простогд я узнал об этом ...

ВАШ ОТВЕТ НА ВОПРОС