Виклик методу з рядка з ім'ям методу в 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 там, де вони не вказані.

Відповіді:


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 Це працює. Це може бути німим подальшою діяльністю ... але як же я не можу знайти надіслати слово у джерелі Ruby за адресою - C: \ ruby ​​\ lib \ ruby ​​\ 1.8 \ fileutils.rb? Думав, що я знайду функцію надсилання там.
BuddyJoe

1
Мені було цікаво, що це робить під капотом.
BuddyJoe

Це визначено на об'єкті - ruby-doc.org/core/classes/Object.html#M000332 Я вибрав випадкову функцію для значення відсотка.
Колін Гравілл

1
Цікаво, тому що, перш ніж я прочитав вашу відповідь двічі, і повністю grok'd це, я запустив FileUtils.send ("load"), і він запустив мою функцію. тож якщо я правильно це розумію, створюючи функції в "глобальному", я додаю методи до кореневого об'єкта? чи ні?
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
цихкреммайстрів

якщо ви хочете надіслати якийсь аргумент з відправленням дзвінка, використовуйте щось подібне до цього ClassName.send ("method_name", arg1, arg2)
Aleem

Чи не повинен ваш орієнтир для callвключення екземпляра методу object ( m.test.method(:length)) для точного відображення справжнього часу? При використанні callви, ймовірно, збираєтеся інстанціювати об'єкт методу кожен раз.
Джошуа Пінтер

Посилання в щоденнику блогу мертве, btw.
Джошуа Пінтер

33

Використовуй це:

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

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

Щодо глобального , я думаю, що спосіб Ruby полягав би в пошуку його methodsметодом.


meth = a.method? це вже не функція виклику? що робити, якщо метод щось повертає?
користувач1735921

FYI for коли метод не існує: "my_string".method('blah') #=> NameError: undefined method blah 'для класуString'
Джошуа Пінтер

3

Особисто я встановив би хеш для функціонування посилань, а потім використовувати рядок як індекс до хешу. Потім ви викликаєте посилання функції з її параметрами. Це має перевагу в тому, що не дозволяти неправильній рядку викликати те, що ви не хочете дзвонити. Інший спосіб - це в основному evalструна. Не роби цього.

PS не лінуйтеся і фактично набирайте все своє питання, а не пов'язуючи щось.


Вибачте. Я скопію деякі формулювання та перекладу, щоб зробити Ruby конкретним. +1
BuddyJoe
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.