Ответ 1
Вы можете использовать instance_eval
:
obj.instance_eval("foo.bar")
Вы даже можете получить доступ к переменной экземпляра напрямую:
obj.instance_eval("@foo.bar")
Скажем, у меня есть объект с методом, который обращается к объекту:
def foo
@foo
end
Я знаю, что могу использовать send для доступа к этому методу:
obj.send("foo") # Returns @foo
Есть ли простой способ сделать рекурсивную отправку, чтобы получить параметр в объекте @foo, например:
obj.send("foo.bar") # Returns @foo.bar
Вы можете использовать instance_eval
:
obj.instance_eval("foo.bar")
Вы даже можете получить доступ к переменной экземпляра напрямую:
obj.instance_eval("@foo.bar")
Пока OP уже принял ответ, используя instance_eval(string)
, я настоятельно призывал бы OP избегать строковых форм eval
, если это абсолютно необходимо. Eval вызывает компилятор ruby - он дорого вычисляется и опасен для использования, поскольку он открывает вектор для инъекций инъекций кода.
Как указано, нет необходимости отправлять вообще:
obj.foo.bar
Если действительно имена foo и bar исходят из некоторого нестатического вычисления, то
obj.send(foo_method).send(bar_method)
просто, и все это нужно для этого.
Если методы идут в виде точечной строки, можно использовать split и inject для цепочки методов:
'foo.bar'.split('.').inject(obj, :send)
Уточнение в ответ на комментарии: String eval - одна из самых рискованных вещей, которые можно сделать с точки зрения безопасности. Если какой-либо способ строка создается из введенного пользователем ввода без невероятно тщательного контроля и проверки этого ввода, вы должны просто подумать о своей системе.
send (method), где метод получен из пользовательского ввода, также имеет риски, но существует более ограниченный вектор атаки. Вход пользователя может привести к тому, что любой метод 0-arghument будет отправлен через ресивер. Хорошая практика здесь заключалась бы в том, чтобы всегда включать белый список методов перед отправкой:
VALID_USER_METHODS = %w{foo bar baz}
def safe_send(method)
raise ArgumentError, "#{method} not allowed" unless VALID_USER_METHODS.include?(method.to_s)
send(method)
end
Немного поздно для вечеринки, но мне пришлось сделать что-то похожее, которое должно было объединить как "отправку", так и доступ к данным из хэша/массива за один вызов. В основном это позволяет сделать что-то вроде следующего
value = obj.send_nested("data.foo['bar'].id")
и под капотом это сделает что-то похожее на
obj.send(data).send(foo)['bar'].send(id)
Это также работает с символами в строке атрибута
value = obj.send_nested('data.foo[:bar][0].id')
который будет делать что-то похожее на
obj.send(data).send(foo)[:bar][0].send(id)
В случае, если вы хотите использовать равнодушный доступ, вы можете добавить это как параметр. Например.
value = obj.send_nested('data.foo[:bar][0].id', with_indifferent_access: true)
Поскольку это немного более важно, вот ссылка на суть, которую вы можете использовать, чтобы добавить этот метод к базовому объекту Ruby, (Он также включает тесты, чтобы вы могли видеть, как это работает)