Вызов метода из строки с именем метода в Ruby

157

Как я могу сделать то, о чем они говорят здесь , но в Ruby?

Как бы вы сделали функцию на объекте? и как бы вы сделали глобальную функцию (см. ответ Jetxee на упомянутый пост)?

ПРИМЕР КОДА:

event_name = "load"

def load()
  puts "load() function was executed."
end

def row_changed()
  puts "row_changed() function was executed."
end 

#something here to see that event_name = "load" and run load()

ОБНОВЛЕНИЕ: Как вы получаете глобальные методы? или мои глобальные функции?

Я пробовал эту дополнительную строчку

puts methods

и загрузить и row_change, где нет в списке.

BuddyJoe
источник

Ответы:

232

Для вызова функций непосредственно на объекте

a = [2, 2, 3]
a.send("length")
# or
a.public_send("length")

который возвращает 3, как и ожидалось

или для функции модуля

FileUtils.send('pwd')
# or
FileUtils.public_send(:pwd)

и локально определенный метод

def load()
    puts "load() function was executed."
end

send('load')
# or
public_send('load')

Документация:

Колин Гравилл
источник
3
+1 Это работает. Это может быть глупым продолжением ... но почему я не могу найти слово send в источнике Ruby по адресу - C: \ ruby ​​\ lib \ ruby ​​\ 1.8 \ fileutils.rb? Думал, я бы нашел там функцию отправки.
BuddyJoe
1
Мне было любопытно, что он делал под капотом.
BuddyJoe
Это определено для объекта - ruby-doc.org/core/classes/Object.html#M000332 Я выбрал случайную функцию для значения интереса.
Колин Гравилл,
1
Интересно, потому что прежде чем я прочитал ваш ответ дважды и полностью его обработал, я запустил FileUtils.send («загрузка»), и он запустил мою функцию. поэтому, если я правильно понимаю, создавая функции в «глобальном», я добавляю методы на корневой объект? или не?
BuddyJoe
12
Хорошо, что вы нашли вещи в источнике! :)
Колин Гравилл
34

Три пути: send/ call/ eval- и их контрольные показатели

Типичный вызов (для справки):

s= "hi man"
s.length #=> 6

С помощью send

s.send(:length) #=> 6

С помощью call

method_object = s.method(:length) 
p method_object.call #=> 6

С помощью eval

eval "s.length" #=> 6

 

Ориентиры

require "benchmark" 
test = "hi man" 
m = test.method(:length) 
n = 100000 
Benchmark.bmbm {|x| 
  x.report("call") { n.times { m.call } } 
  x.report("send") { n.times { test.send(:length) } } 
  x.report("eval") { n.times { eval "test.length" } } 
} 

... как видите, создание экземпляра объекта метода - это самый быстрый динамический способ вызова метода, также обратите внимание на то, насколько медленно работает eval.

#######################################
#####   The results
#######################################
#Rehearsal ----------------------------------------
#call   0.050000   0.020000   0.070000 (  0.077915)
#send   0.080000   0.000000   0.080000 (  0.086071)
#eval   0.360000   0.040000   0.400000 (  0.405647)
#------------------------------- total: 0.550000sec

#          user     system      total        real
#call   0.050000   0.020000   0.070000 (  0.072041)
#send   0.070000   0.000000   0.070000 (  0.077674)
#eval   0.370000   0.020000   0.390000 (  0.399442)

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

УХО
источник
Я просто нашел это, и я заметил что-то, что не было покрыто. Что бы я сделал, если бы хотел Class.send("classVariable") = 5? Это выдает ошибку. Есть ли способ обойти это? То же самое верно для использованияClass.method("classVariable").call() = 5
thesecretmaster
если вы хотите послать какой - то спор с использованием посыла вызова что - то вроде этого ClassName.send ( «имя_метода», arg1, arg2)
Алим
Не стоит ли в вашем тесте callвключить создание экземпляра метода object ( m.test.method(:length)) для точного представления его истинного времени? При использовании callвы, вероятно, будете каждый раз создавать экземпляр объекта метода.
Джошуа Пинтер
Ссылка на запись в блоге мертва, кстати.
Джошуа Пинтер
33

Использовать это:

> a = "my_string"
> meth = a.method("size")
> meth.call() # call the size method
=> 9

Просто, правда?

Что касается глобального , я думаю, что Ruby мог бы найти его, используя methodsметод.

Geo
источник
мет = а.метод? это уже не вызов функции? Что делать, если метод возвращает что-то?
user1735921
К вашему сведению, когда метод не существует: "my_string".method('blah') #=> NameError: undefined method бла 'для классаString'
Джошуа Пинтер
3

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

PS Не ленитесь и напишите весь вопрос, вместо того, чтобы ссылаться на что-то.

dlamblin
источник
Сожалею. Я скопирую некоторые формулировки и переведу, чтобы сделать их специфичными для Ruby. +1
BuddyJoe