在RoR中设计模式以在多个对象中使用方法

时间:2013-06-24 22:59:45

标签: ruby-on-rails design-patterns

我有以下型号:

Shop, :belongs_to => Currency
Product, :belongs_to => Shop
Company, :belongs_to => Currency
CompanyUser, :belongs_to => Company
UserTransaction, :belongs_to => CompanyUser

因此,商店和公司都使用某种货币。这是货币的模型

include ActionView::Helpers

class Currency < ActiveRecord::Base
  attr_accessible :description, :iso_code, :number_of_decimals, :symbol

  def to_currency(number)
    number_to_currency(number, :precision => self.number_of_decimals, :unit => self.symbol)
  end    
end

好的,现在当我想显示我可以做的产品价格时:

product.shop.currency.to_currency(product.price)

如果我想显示公司用户的余额,我可以这样做:

company_user.company.currency.to_currency(company_user.balance)

如果我想显示我需要做的UserTransaction的价格:

user_transaction.company_user.company.currency.to_currency(user_transaction.amount)

这一切都有效。但我想知道是否存在我可以应用的设计模式,这将使所有连接对象中的to_currency可用。请注意,这不仅仅是我可以使用的方法助手,因为有时它需要使用商店的货币(例如使用产品),有时需要公司的货币(如果是CompanyUser,UserTransaction,......)。

理想情况下,我想这样做:product.to_currency(product.price)或product.price.to_currency,它会通过查看商店的货币来查找要使用的货币。

这个例子很简单,我还有其他一些模型需要转换,但所有这些都可以连接到商店或公司。

2 个答案:

答案 0 :(得分:1)

您可以使用through关联通过相关遍历来关联记录。和/或类似下面的内容(但要注意每个对象遍历将触及数据库):

class CompanyUser
  def currency
    company.currency
  end
end

class UserTransaction
  def currency
    company_user.currency
  end

  def to_currency
    currency.to_currency(amount)
  end
end

# ...
puts UserTransaction.find(5).to_currency

答案 1 :(得分:1)

product.to_currency(product.price)工作,你可以做这样的事情

# common module that can be used with any object that has a currency method or attribute
module HasCurrency
  def to_currency(value)
    if respond_to?(:currency)
      currency.to_currency(value)
    else
      raise "#{class} does not have a currency"
    end
  end
end


class Product
  # mix in the module, but...
  include HasCurrency

  belongs_to :shop
  # ... product has no currency method, delegate it to the associated shop
  delegate :currency, to: :shop

  # delegate is the same as
  def currency
    shop.currency
  end
end

上面注意到UserTransaction的效果不会很好,请参阅z5h回答,您需要包含关联并公开currency的方法


实现product.price.to_currency你需要猴子补丁BigDecimal(如果这是价格的类型),它有点棘手;相反,使用简单的包装方法可能会更好吗?

不可重复使用,但明确且易于理解

class Product
  belongs_to :shop

  def formatted_price
    shop.currency.to_currency(price)
  end
end

从符号

调用方法
def to_currency(method_name)
  self.public_send(method_name) if self.respond_to?(method_name)
end