Как сделать «позднюю» интерполяцию строк в Ruby

17
>> string = '#{var}'
=> "\#{var}"

>> proc = Proc.new { |var| string }
=> #<Proc:[email protected](pry):6>

>> proc.call(123)
=> "\#{var}"

Не то, что я хочу. Двойные кавычки вокруг string приводят к очевидному undefined local variable .

    
задан Paweł Gościcki 12.09.2011 в 20:51
источник
  • Что для этого используется? Это звучит очень странно, что хочется делать. –  Chris Jester-Young 12.09.2011 в 20:53
  • Да ... Я знаю свои потребности. Это для этих двух строк больше DRY github.com/pjg/dotfiles/blob/... –  Paweł Gościcki 12.09.2011 в 21:00
  • Я думаю, что что-то подобное существует в Ruby Facets. –  Andrew Grimm 13.09.2011 в 00:42

5 ответов

17

Хотя это возможно, он не будет работать так, как вы намереваетесь здесь, не используя eval , и, как правило, это плохая идея, если есть альтернатива. Хорошая новость заключается в том, что у вас есть несколько вариантов.

Наиболее простым является использование форматирования sprintf , которое упрощается с помощью метода String#% :

string = '%s'

proc = Proc.new { |var| string % var }

proc.call(123)
# => "123"

Это действительно надежный метод, так как все, что поддерживает метод .to_s , будет работать и не вызовет юниоризацию, если он содержит исполняемый код.

    
ответ дан tadman 12.09.2011 в 20:59
источник
  • String #% также поддерживает именованные параметры с помощью Hash в качестве аргумента, что придает ей действительно большую силу встроенной интерполяции строк. –  Mladen Jablanović 12.09.2011 в 21:22
  • Я обычно использую его в стиле C для того, чтобы быть таким компактным, но, как вы указываете, это намного больше. –  tadman 12.09.2011 в 21:44
  • Это трюк красиво :) Thx! –  Paweł Gościcki 13.09.2011 в 09:16
19

В моем случае мне нужна конфигурация, хранящаяся внутри yml, с интерполяцией, но которая только интерполируется, когда мне это нужно. Принятый ответ с Proc казался мне слишком сложным.

В ruby ​​1.8.7 вы можете использовать синтаксис % следующим образом:

"This is a %s verb, %s" % ["nice", "woaaaah"]

При использовании по крайней мере рубина 1.9.x (или рубина 1.8.7 с i18n) существует более чистая альтернатива:

my_template = "This is a %{adjective} verb, %{super}!"

my_template % { adjective: "nice", super: "woah" }
=> "This is a nice verb, woah!"
    
ответ дан nathanvda 20.08.2013 в 14:37
источник
4

Он работает с eval:

proc = Proc.new { |var| eval(%Q{"#{string}"}) }

(Если вы доверяете значению string .)

    
ответ дан arnaud576875 12.09.2011 в 20:57
источник
  • Когда вы цитируете цитаты, вы должны, вероятно, выразить это, используя синглы или% q [], например:% q ["# {string}"], чтобы избежать всех ужасных побегов. –  tadman 12.09.2011 в 21:00
  • Хм ... кажется, как работы eval. Но может быть, есть другой способ? –  Paweł Gościcki 12.09.2011 в 21:01
  • @tadman не будет интерполировать # {string}, тогда –  arnaud576875 12.09.2011 в 21:04
  • @ Paweł, любая поздняя интерполяция с синтаксисом # {} Ruby будет делать все равно, так как вы можете поместить любое выражение ruby ​​в # {}, которое нужно было бы оценить –  arnaud576875 12.09.2011 в 21:06
  • Но% Q сделает интерполяцию, чтобы вы могли% Q {# {string}}, если бы вы были так склонны. –  mu is too short 12.09.2011 в 22:13
Показать остальные комментарии
1

Вы можете достичь СУХОЙ, которую вы ищете, создавая функцию «curried» (то есть: Proc, которая возвращает Proc), где внутренняя функция содержит базовую строку с переменными для каждой части, которая отличается.

Исправьте меня, если я ошибаюсь, но в вашем коде за вашей прокомментированной ссылкой единственная разница между двумя строками - это один символ в конце. (Даже если это не так, вы можете использовать эту технику для достижения той же цели). Вы можете создать Proc, который возвращает Proc, который содержит вашу строку, затем дважды вызовите внешний Proc для двух ваших завершающих символов:

rails_root = "whatever" # Not variant for the string
rails_env_prompt = "whatever" #not variant for the string

spec = Proc.new { |tail_char| 
  Proc.new {|obj, nest_level, *| 
    "#{rails_root} #{rails_env_prompt} #{obj}:#{nest_level}#{tail_char} "
  }
}

Pry.config.prompt = [ spec.call(">"), spec.call("*") ]  

Pry.config.prompt[0].call("My obj", "My Nest Level")
# result: "whatever whatever My obj:My Nest Level> "
    
ответ дан Craig Walker 02.04.2013 в 20:18
источник
  • Это способ усложнить :) Строчная интерполяция из принятых ответов работает для меня. –  Paweł Gościcki 03.04.2013 в 10:30
0

Вам нужно определить строку, несущую интерполяцию вне вашего Proc?

proc = Proc.new { |var| "#{var}" }
proc.call(123) # "123"

Это было бы самым чистым способом, я думаю.

    
ответ дан Craig Walker 02.04.2013 в 19:58
источник
  • Я вижу, что это не работает для вашего конкретного примера ... но он все равно может применяться к другим подобным ситуациям. Я отправлю лучший ответ отдельно. –  Craig Walker 02.04.2013 в 20:12