5

我目前正在 Rails 中开发一个大型自定义内容管理解决方案,以处理许多不同的内容类型(模型)及其关系。

整个数据模型建立在活动记录之上,并具有内容导入和导出以及与其他服务同步等功能(例如移动同步将内容更改推送到智能手机)。

对于这些任务,我有许多数据对话,一方面是活动记录模型,另一方面是许多不同且已经存在的目标格式。

  • JSON REST 服务反映模型层的变化
  • RSS 提要发布新内容
  • 导入/导出为专有 xml 格式
  • ETC

对于新的数据格式,我可以自己定义结构,在大多数情况下,这意味着,让 rails 使用整洁的编组功能来处理它

    format.html do
      render 'show'
    end
    format.xml do
      render xml: { content:@content }
    end
    format.json do
      render json: { content:@content }
    end

但是在必须提供现有数据模式的情况下,必须进行几个对话:

重命名键:在模型中,每个对象都由一个 id 属性标识,但在目标格式中,对象属性是名称 uid 或 OBJECT-ID ...

内联相关对象:假设我有一个名为 Person 的模型,它与 Address 模型相关。当使用 Rails xml 序列化时,地址对象将被省略或内联在标签下。在给定的目标格式中,地址可能必须内联在 Person 对象中,这意味着将包含以下输出

<person>
   <name>Ben</name>
   <street>Some Street</street>
   <city>Berlin</city>
</person>

值转换: 可能需要日期属性作为 unix 时间戳而不是 utc 字符串

天真的解决方案:

所有这些转换都可以在需要时手动完成,这意味着只需放置一些创建目标数据结构的 ruby​​ 代码:

data = {}
Person.all.each do |p|
    # rename property
    data[:guid] = p.id
    data[:name] = p.full_name
    # inline relation
    data[:street] = p.primary_address.street    
    data[:city] = p.primary_address.locality
    data[:member_since] = p.created_at.format(...)
end
render xml: { persons:data}

或者对于 xml,只能使用转换构建器模板。

虽然此选项可行且灵活,但它将对话逻辑传播到整个应用程序并使控制器增长,并且在大型应用程序中这将不利于可维护性......

我正在寻找的是基于模式的模型转换。这意味着我在某处定义了从我的 activerecord 模型到目标模式的映射(使用 ruby​​ dsl,在 xml 中......),并且只要我需要某种数据格式,就必须执行模式对话:

data = Article.all
# the parameter is the name of the target schema
converter = ModelConversation.new(:legacy_contact_list)
render xml: { contacts: converter.execute(data) }

所以我真正在寻找的是类似于 xslt 但也适用于 json 输出并由 ruby​​ 提供支持的东西。

您如何在 Rails 中进行数据对话的任何帮助/想法或故事将不胜感激。

4

1 回答 1

2

几年来我一直在编写 XSLT 转换,我只能建议不要使用 XSLT 或“类似的东西”。

既然你有一个 Ruby 应用程序,那就使用 Ruby!我认为它已经符合您的需求。

关于您的担忧:

虽然此选项可行且灵活,但它将对话逻辑传播到整个应用程序并使控制器增长,并且在大型应用程序中这将不利于可维护性......

这将在你的控制之下。只需将转换器视为应用程序的任何其他部分并保持高代码质量。如果您将转换逻辑放入模型本身或将其移动到库中,您的控制器将不会增长。重构您的转换器以使其简洁。

看看你的“天真”例子

    # rename property
    data[:guid] = p.id
    data[:name] = p.full_name
    # inline relation
    data[:street] = p.primary_address.street    
    data[:city] = p.primary_address.locality
    data[:member_since] = p.created_at.format(...)

这段代码基本上说在你的目标格式中,id被调用guidfull_name被调用name等等。我怀疑你写的代码比你已经​​给出的代码要短得多。所以我认为这里不需要另一种技术。

于 2012-12-20T16:02:50.670 回答