Rails - 可扩展的计算模型

时间:2012-07-04 10:07:38

标签: ruby-on-rails database ruby-on-rails-3 model-view-controller model

我目前在我的rails应用中有一个包含模型metricoperandoperation_type的计算结构。

目前,metric模型有很多operands,可以根据operation_type执行计算(例如求和,乘法等),每个operand@customer.sales.selling_price.sum定义为右或左(即如果操作是除法,则可以识别分子和分母)。

目前,操作数始终是某个模型的属性,例如calculated_metric

为了使这个可伸缩,需要允许操作数 某种属性,或者前一个操作的结果,即操作数可以是度量。

我已经包含了我的模型当前外观的图表:

enter image description here

任何人都可以用最优雅的方式帮助我将操作数作为实际操作数或其他指标吗?

谢谢!

编辑:似乎基于迄今为止唯一的答案,也许多态关联是这样做的方式,但答案是如此简短,我不知道如何以这种方式使用它们 - 任何人都可以详细说明吗? / p> 编辑2:好的,我想我已经到了某个地方 - 基本上我现在有一个度量标准,其中包含多个操作数,以及一个操作数has_many指标。我需要一个多态自联接,其中度量也可以有许多度量 - 我是否需要将其称为其他东西,也许是calculate_metrics,以便度量模型可以自己使用?这将使我的情况是指标具有多个操作数,而指标具有许多calculated_metrics。

编辑3:我已经更新了我的模型如下,并且对于这是否是解决问题的好方法有任何批评。您将注意到我添加了一个名为{{1}}的模型,该模型本质上是其他指标的持有者 - 即可以使用两个操作数或操作数和calculated_metric的组合来计算度量。

Revised Model

编辑4:Bounty为任何可以向我展示详细的轨道方式的人添加了。

编辑5:赏金仍在争夺中;虽然下面提供的答案是详细的,但我正在寻找最好的方法,而不是替代方法来处理这个问题(即请让我知道解决问题的最佳方法,而不是寻找方法来回避它,因为我需要这个功能)。谢谢!

编辑6:对此没有太多的关注 - 会增加赏金帮助,还是这个问题不是跑步者?

2 个答案:

答案 0 :(得分:1)

考虑退后一步,问自己是否真的需要撕开表达式,并将它们一块一块地存储在关系数据库中。

  • 这对你的目的真的有意义吗?
  • 其他应用程序是否会查询包含表达式片段的基础表?
  • 您是否会生成报告,显示每个运营商的频率 使用
  • 或者您是否正在使用关系数据库来存储表达式,因为这是最熟悉的?

RDBMS似乎并不适合这项工作。

您可以将表达式存储为字符串,然后在从文件或数据库加载字符串时将它们解析为表达式树。有很多例子展示了如何编写表达式解析器。

所以把它放在Ruby和Rails术语中。我会做以下事情:

  1. 将包含表达式树的大多数类放入不扩展的普通旧Ruby类中 ActiveRecord的。
  2. 在每个类中实现to_s,这样如果您将to_s发送到表达式树的根,那么您将获得表达式的良好形式的字符串表示形式。
  3. 实现可以从字符串创建表达式树的ExpressionParser或ExpressionBuilder或ExpressionFactory(使用您喜欢的任何术语)。
  4. 然后,如果某个ActiveRecord模型用于指向它独占“拥有”的度量标准,您可以让它指向内存中的表达式树,但是在保存时自己存储字符串表示(通过to_s)。 LI>
  5. 如果几个ActiveRecord模型指向旧设计中的相同(共享)表达式,那么您将必须为SharedExpressions创建一个类,该类使用字符串进行持久表示,然后将AciveRecords更改为指向新的SharedExpression。
  6. 如果你走这条路线,像before_save这样的ActiveRecord回调可能会有用。祝你好运。

答案 1 :(得分:0)

如果我理解你的问题,我相信你正在寻找Polymorphic Associations

示例:

class Metric < ActiveRecord::Base
   belongs_to :calculable, :polymorphic => true
   has_many :operations, :as => calculable
end

class Operand < ActiveRecord::Base
   belongs_to :calculable, :polymorphic => true
end