首页 > 解决方案 > 插入 ruby​​ 变量和对象的动态 json 模板

问题描述

我们正在我们的应用程序中构建一个传出 webhook 功能。我们希望添加 webhook 的用户能够自定义发送的 json 有效负载。为此,我们需要一种允许将变量插入最终有效负载的语法。

示例:用户管理的模板:

{:id=>"%{id}", :message=>"%{message}", :badge=>"%{badge}"}

希望的输出:

{
  "id": "f1ih5g3",
  "message": "Thanks for your help",
  "badge": {
    "id": "M7nk8ojK",
    "name": "Thanks Badge",
    "points": 10,
  }
}

因此,在上面的示例中,有一个主要的 webhook 对象,以及 和 的基本变量idmessage插值并作为字符串返回。但是,当模板中包含对完整对象的引用时,我们还希望能够支持嵌套结构。当包含对完整对象的引用时,它应该返回该对象的 json 表示。

我们尝试使用 String#% 运算符,它适用于基本变量,但对于嵌套对象,它会产生如下字符串化版本:

{
  "id": "f1ih5g3",
  "message": "Thanks for your help",
  "badge": "{:id=>\"M7nk8ojK\", :name=>\"Thanks Badge\", :points=>10}"
}

我探索了 RABL 和 Jsonnet,它们似乎并不真正支持动态模板(即由用户管理的模板)。

ERB 语法可以工作,但似乎不安全,因为任何任意 ruby​​ 都可以包含在模板中。

标签: ruby-on-railsjsonruby

解决方案


如果是我,我会选择这样的解决方案。我会收集用户可以在一个类下使用的变量并编写以下代码。也许我误解了代码,因为我无法完全预测问题。

require 'json'
require 'pry'
require 'securerandom'

class CustomerArea
  ALLOWED_METHODS = %i[id message badge]

  def self.id
    SecureRandom.uuid
  end

  def self.message
    "Lorem ipsum dolar sit amet"
  end

  def self.badge
    {
      id: SecureRandom.uuid,
      name: "Foo",
      points: 5
    }
  end
end


class Processor
  def process(input)
    json_object = JSON.parse(input, symbolize_names: true)
    result      = {}
    
    json_object.each do |key, value|
      extracts = value.match /\A%{insert_(?<field>\w+)}\z/
      if extracts.nil?
        result[key] = value
        next
      end

      field           = extracts[:field].to_sym
      allowed_methods = CustomerArea.const_get(:ALLOWED_METHODS)
      next unless allowed_methods.include? field

      result[key] = CustomerArea.send(field)
    end

    result.to_json
  end
end

customer_input = '{ "id": "%{insert_id}", "message": "%{insert_message}", "badge": "%{insert_badge}"}'
processor = Processor.new
puts processor.process(customer_input)

推荐阅读