通过其他模型保存虚拟属性

时间:2012-01-23 09:03:43

标签: ruby-on-rails ruby-on-rails-3

我有一个城市模型和一个wood_production模型。一个城市有一个wood_production,wood_production存储该城市的木材数量(列数量)。

现在,我在城市模型中有一个虚拟属性,如:

has_one :wood_production
delegate :amount, :to => :wood_production, :prefix => true, :allow_nil => true

def wood
    self.wood_production_amount
end

因此,wood实际上来自wood_production模型,名为amount(通过委托)列。

现在,我想在不使用中间wood_production模型的情况下通过城市减少木材。理想情况下,我希望能够做到:

city.decrement(:wood)

或至少

city.wood -= ..

如果我现在尝试,则不保存新的木材值(在self.save之后)。我有什么想法可以正确保存它吗?

编辑:这是我现在尝试的代码:

has_one :wood_production, :autosave => true
delegate :amount, :to => :wood_production, :prefix => true, :allow_nil => true

    def wood
        self.wood_production_amount
    end

    def wood= amt
      wood_production_amount = amt
    end

现在,如果我这样做:

u = User.first
c = u.cities.first
c.wood -= 1000

我得到了(这确实是正确的)

 => 7432.778424219838 

但是当我尝试保存时:

1.9.2p290 :006 > c.save
   (0.1ms)  BEGIN
   (0.1ms)  COMMIT
 => true

2 个答案:

答案 0 :(得分:1)

您可能想要使用

 accepts_nested_attributes_for :wood_production

 has_one :wood_production, :autosave => true

答案 1 :(得分:1)

对于初学者,您可以使用别名wood_production_amount

class City < ActiveRecord::Base
  has_one :wood_production, :autosave => true

  delegate :amount, :to => :wood_production, :prefix => true, :allow_nil => true

  alias_method :wood,  :wood_production_amount
  alias_method :wood=, :wood_production_amount=

  # ...
end

这会为您提供city.woodcity.wood = n,因为Ruby是魔术,您可以免费获得+=-=等。非常方便。

如果您希望能够city.decrement(:wood)(除city.wood += n之外),您需要更加神奇。

def decrement name, amt=1
  # make sure it's an attribute we can set
  unless respond_to? "#{name}="
    raise ArgumentError, "Invalid attribute name for decrement"
  end

  # call the method by name to get the current value, then
  # subtract amt from it
  new_amt = send( name ) - amt

  # set the new amount
  send "#{name}=", new_amt 
end

# Usage:
some_city = City.find(...)

some_city.wood
# => 90

some_city.decrement :wood
# => 89

some_city.decrement :wood, 80
# =>  9

顺便说一句,首先实施increment可能更为明智,因为这是decrement的一般情况:

def increment name, amt=1
  check_argument name, :increment

  new_amt = send( name ) + amt

  send "#{name}=", new_amt 
end

def decrement name, amt=1
  check_argument name, :decrement

  increment name, -amt
end

private
def check_argument name, meth
  # make sure it's an attribute we can set
  unless respond_to? "#{name}="
    raise ArgumentError, "Invalid attribute name for #{meth}"
  end
end