Необязательные символы в Ruby для метода с заглавной буквой в верхнем регистре? - PullRequest
2 голосов
/ 01 июня 2010

Я только начал использовать IronRuby (но поведение, кажется, непротиворечивое, когда я тестировал его в простом Ruby) для DSL в моем приложении .NET - и как часть этого я определяю методы, которые будут вызываться из DSL через define_method .

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

Учитывая следующую программу:

class DemoClass
    define_method :test do puts "output from test" end
    define_method :Test do puts "output from Test" end

    def run
        puts "Calling 'test'"
        test()
        puts "Calling 'test'"
        test
        puts "Calling 'Test()'"
        Test()
        puts "Calling 'Test'"
        Test
    end
end

demo = DemoClass.new
demo.run

Запуск этого кода в консоли (с использованием обычного ruby) дает следующий вывод:

ruby .\test.rb
Calling 'test'
output from test
Calling 'test'
output from test
Calling 'Test()'
output from Test
Calling 'Test'
./test.rb:13:in `run': uninitialized constant DemoClass::Test (NameError)
    from ./test.rb:19:in `<main>'

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

Есть ли способ обойти эту проблему?

1 Ответ

7 голосов
/ 01 июня 2010

Это только часть разрешения неоднозначности Руби.

В Ruby методы и переменные находятся в разных пространствах имен, поэтому могут существовать методы и переменные (или константы) с одинаковыми именами. Это означает, что когда использует их, необходим какой-то способ их различения. В общем, это не проблема: у сообщений есть получатели, а у переменных нет. Сообщения имеют аргументы, переменные - нет. Переменные назначены, сообщения - нет.

Единственная проблема - когда у вас нет получателя, аргумента и присваивания. Тогда Ruby не может определить разницу между отправкой сообщения без получателя без аргументов и переменной. Итак, он должен составить несколько произвольных правил, и эти правила в основном:

  • для неоднозначного токена, начинающегося с строчной буквы, предпочитайте интерпретировать его как отправку сообщения, , если вы не уверены, что это переменная (то есть синтаксический анализатор (не (! ) переводчик) видел задание раньше)
  • для неоднозначного токена, начинающегося с заглавной буквы, предпочитайте интерпретировать его как константу

Обратите внимание, что для сообщения, отправляемого с аргументами (даже если список аргументов пуст), двусмысленности нет, поэтому ваш третий пример работает.

  • test(): очевидно, что сообщение отправлено, здесь нет двусмысленности
  • test: может быть отправка сообщения или переменная; правила разрешения говорят, что это сообщение отправить
  • Test(): очевидно, что сообщение отправлено, здесь нет двусмысленности
  • self.Test: также очевидно, что сообщение отправлено, здесь нет двусмысленности
  • Test: может быть отправка сообщения или константа; правила разрешения говорят, что это константа

Обратите внимание, что эти правила немного тонкие, например, здесь:

if false
  foo = 'This will never get executed'
end

foo # still this will get interpreted as a variable

Правила гласят, что интерпретация неоднозначного токена как переменной или отправки сообщения определяется синтаксическим анализатором , а не интерпретатором. Таким образом, поскольку синтаксический анализатор видел foo = whatever, он помечает foo как переменную, даже если код никогда не будет выполнен и foo будет оцениваться до nil, как и все неинициализированные переменные в Ruby.

TL; DR сводка: вы СОЛ.

То, что вы могли бы сделать, это переопределить const_missing для перевода в отправку сообщения. Примерно так:

class DemoClass
  def test; puts "output from test" end
  def Test; puts "output from Test" end

  def run
    puts "Calling 'test'"
    test()
    puts "Calling 'test'"
    test
    puts "Calling 'Test()'"
    Test()
    puts "Calling 'Test'"
    Test
  end

  def self.const_missing(const)
    send const.downcase
  end
end

demo = DemoClass.new
demo.run

За исключением того, что это, очевидно, не будет работать, поскольку const_missing определено для DemoClass и, таким образом, когда const_missing выполняется, self равно DemoClass, что означает, что он пытается вызвать DemoClass.test, когда он следует звонить DemoClass#test через demo.test.

Я не знаю, как легко это решить.

...