Сохранить виртуальный атрибут через другую модель - PullRequest
1 голос
/ 23 января 2012

У меня есть модель города и модель производства древесины.В городе есть одно 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_production, из столбца с названием количество (по делегированию).

Теперь я хочу уменьшить древесину по городу без использования промежуточной модели 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

Iполучить (что действительно правильно)

 => 7432.778424219838 

но когда я пытаюсь сохранить:

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

Ответы [ 2 ]

1 голос
/ 23 января 2012

Для начала вы можете использовать псевдоним 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.wood и city.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
1 голос
/ 23 января 2012

Вы, вероятно, хотите использовать

 accepts_nested_attributes_for :wood_production

или

 has_one :wood_production, :autosave => true
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...