用GSUB替换SmartCodes

发布于 2025-01-31 15:38:25 字数 891 浏览 2 评论 0原文

我从事Ruby> 3.0,我需要替换文本内容(用SmartCode在线HTML文本)。 本文可能很长,例如这样:“你好,{{viewer_name}}!你好吗?”

我有一种替换smartcodes的方法:

def populate_smartcodes(content)
  content.gsub(/\{{(.*?)\}}/).each do |value|
    smartcode = value[/#{Regexp.escape('{{')}(.*?)#{Regexp.escape('}}')}/m, 1]
    str_smartcode = "{{#{smartcode}}}"
    case smartcode
    when 'viewer_name'
      content = content.gsub(str_smartcode, viewer.name)
    when 'company_city'
      content = content.gsub(str_smartcode, company.city )
    end
    content
  end

company_cityviever_name是我需要向查看器提供一个user :: viever的实例。

而且我有很多智能编码可以替换...我认为这不是做到这一点的好方法,而是在起作用。

您能帮我改善性能还是做到这一点?

编辑:

我向用户显示了PDF内容,他们只有可用的“键”或“ SmartCode”列表,这样,每个人都可以(通过内容)更改其PDF。我们只保留数据库中具有PDF内容的文本。也许我们可以使用另一种策略? 现在,我想要一种将字符串元素替换为另一个值的方法。

I work on ruby > 3.0 and i need to replace a text content (in-line html text with smartcode).
this text can be long and for example it's like this : "Hello, {{viewer_name}} ! How are you ?"

I have a method to replace theses smartcodes :

def populate_smartcodes(content)
  content.gsub(/\{{(.*?)\}}/).each do |value|
    smartcode = value[/#{Regexp.escape('{{')}(.*?)#{Regexp.escape('}}')}/m, 1]
    str_smartcode = "{{#{smartcode}}}"
    case smartcode
    when 'viewer_name'
      content = content.gsub(str_smartcode, viewer.name)
    when 'company_city'
      content = content.gsub(str_smartcode, company.city )
    end
    content
  end

company_city and viewer_name are variables i need to provide with viewer an instance of User::Viewer.

And i have a lot of smartcodes to replace ... I think this is not a good way to do it but it's working.

Can you help me to improve the performance or the way to do it ?

Edit :

I have pdf content shown to the user, they just have a list of 'keys' or 'smartcode' available, this way everyone can change their pdf as the want to (through the content). We keep only a text with the pdf content in the database. Maybe we can use another strategy ?
For now i wanted a way to replace string element by another value.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

幸福不弃 2025-02-07 15:38:25

在这里,您正在在现有的GSUB块中运行一个新的GSUB,这不是必需的,因为GSUB块将无论如何都会用块的返回值代替每个匹配。只需使用2行代码即可实现相同的结果:

content.gsub!(/{{viewer_name}}/, viewer.name)
content.gsub!(/{{company_city}}/, company.city)

...但这仍在分析整个字符串两次。如果您事先知道所有SmartCodes,则可以做类似的事情:

def populate_smartcodes(content)
  content.gsub(/{{\w*}}/) do |smartcode|
    case smartcode
    when '{{viewer_name}}'
      viewer.name
    when '{{company_city}}'
      company.city
    else
      smartcode
    end
  end
end

这将返回输入字符串的副本,其中所有实例{{vuether_name}}{company_city} }更换。您也可以使用gsub!而不是gsub如果要修改原始字符串,因此可以像populate_smartcodes(onigral_string)而不是Original_string = populate_smartcodes(oilter_string)

Here you're running a new gsub inside an existing gsub block, which isn't necessary, since the gsub block will substitute each match with the return value of the block anyway. The same result could be achieved simply with 2 lines of code:

content.gsub!(/{{viewer_name}}/, viewer.name)
content.gsub!(/{{company_city}}/, company.city)

...but that is still analyzing the whole string twice. If you know all of the smartcodes beforehand, you could do something like this instead:

def populate_smartcodes(content)
  content.gsub(/{{\w*}}/) do |smartcode|
    case smartcode
    when '{{viewer_name}}'
      viewer.name
    when '{{company_city}}'
      company.city
    else
      smartcode
    end
  end
end

This will return a copy of the input string with all instances of {{viewer_name}} and {{company_city}} replaced. You could also use gsub! instead of gsub if you want to modify the original string in place, so you can call it like populate_smartcodes(original_string) instead of original_string = populate_smartcodes(original_string).

江南烟雨〆相思醉 2025-02-07 15:38:25

可以以更简单(更清洁?)方式从模板中提取智能编码。我们可以肯定地知道,它们总是包含4个括号-2个。因此,我们可以将gsub匹配的内容,然后将2 ..- 3 chars作为实际绑定名称:

content = "Hello, {{viewer_name}}! How are you?"

content.gsub!(/{{\w+?)}}/) do |matched_chunk|
  matched_chunk[2..-3] # => viewer_name
end

下一步,我们想将这些匹配的SmartCodes解析为有意义的事物。 我们想动态地做到这一点,以避免对许多人的智能编码进行硬编码,正确

在您的示例中,您将viewer_name解析为viever.namecompany_city to company.city - 看起来像某种模式<接收器>。property>。如果是这种情况,我们可以使用这种“通用性”来动态解析事物,而无需为每个“ smartcode”进行硬码分辨率:

content.gsub!(/{{\w+?)}}/) do |matched_chunk|
  recv, prop = matched_chunk[2..-3].split("_", 2)
  instance_eval("#{recv}.#{prop}") # the receiver should be defined in the current scope, obviously
rescue
  matched_chunk
end

嗯,它可以起作用。可能:)但是它闻起来像地狱:

  • 我将分裂限制在2中,以确保我们始终坚持使用receviver.property模式。因此,user_first_name之类的东西将被解析为user.first_name。但是,总体而言,这个假设可能完全是错误的!实际上,它可能是user.first.name,甚至foo_bar应该解决到sometsing> sosity_else - 因此,此解决方案是脆弱的< em>最少 ...

  • instance_eval是一个pandora框,我们应该非常小心。有思想的是,我们应该在孤立的安全上下文(一些空白的板岩对象)

    中称其为

为这些问题:我们可以创建一个特殊的模板构建器对象,作为我们instance_eval的安全空白后续上下文;然后,我们可以注入必要的绑定,以便instance_eval可以正确解析我们的智能事物(在这种情况下,我们甚至可以用更安全的魔法替换instance_eval,或者根本没有魔法 - 硬编码的东西有时不是 bad Idea)等...

但是,在这个兔子洞之后,我们迟早会重新创建LiquidMustache之类的东西,只是不那么成熟,更容易出错。

因此,我的建议是不要在Regexes上玩耍,请首先尝试采用一些战斗测试的模板引擎(仅当您真正有很强的理由时,只有在自定义实施中回滚):) :)

It is possible to extract smartcodes from the template in a much simpler (cleaner?) way. We know for sure, that they always contain 4 parentheses - 2 from each side. So we can just take what gsub matches, and take 2..-3 chars as the actual binding name:

content = "Hello, {{viewer_name}}! How are you?"

content.gsub!(/{{\w+?)}}/) do |matched_chunk|
  matched_chunk[2..-3] # => viewer_name
end

Next, we would like to resolve these matched smartcodes into something meaningful. And we would like to do it dynamically to avoid hardcoding of many-many individual smartcodes, correct?

In your example you resolve viewer_name into viewer.name and company_city into company.city - it looks like some pattern <receiver>.<property>. If this is the case, we could use this "generality" to resolve things dynamically without the need to hardcode resolution for each "smartcode":

content.gsub!(/{{\w+?)}}/) do |matched_chunk|
  recv, prop = matched_chunk[2..-3].split("_", 2)
  instance_eval("#{recv}.#{prop}") # the receiver should be defined in the current scope, obviously
rescue
  matched_chunk
end

Well, it could work. Probably :) But it smells as hell:

  • I limit the split by 2, to ensure we always stick with receiver.property pattern. So, something like user_first_name will be resolved as user.first_name. But this assumption might be just wrong in general! It might be in fact something like user.first.name, or even foo_bar that should be resolved into something_else - so this solution is fragile at minimum...

  • instance_eval is a Pandora box, and we should be extremely careful with it. Idealy, We should call it in an isolated, safe context (some blank slate object)

Both of these problems are solvable: we can create a special template builder object as a safe blank late context for our instance_eval; we could then inject the necessary bindings so that instance_eval could resolve our smartcodes properly (in this case we could even replace instance_eval with safer magic or maybe no magic at all - hardcoding things sometimes is not that bad idea) etc...

But following this rabbit hole, we would sooner or later recreate something like liquid or mustache, just way less mature and more error-prone.

So my proposal is don't play around regexes, try to adopt some battle-tested template engine first (and roll back to the custom implementation only if you have really strong reasons) :)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文