2015-01-07 2 views
0

Я ищу способ разработки модели баланса между двумя пользователями в Ruby on Rails (но может быть более общим). Два пользователя Алиса и Боб начнут с баланса нуля в отношении другого. Теперь, если Алиса получит +4, я хочу, чтобы у Боба было -4. Вот что я придумал до сих пор:Дизайн базы данных/Rails - дизайн баланса между двумя пользователями

  • баланс с тремя полями: user_one, user_two и one_to_two. То есть, если я хочу, чтобы баланс для user_one был user_two, я просто принимаю one_to_two, и если я хочу от two_to_one, я беру -one_to_two (просто метод в модели баланса). Проблема в том, что я не мог просто использовать «has_many balanceances through» из модели User, так как баланс должен был знать, является ли пользователь user_one или user_two, который звонит (я должен был бы передать пользователя)

  • Баланс с тремя полями снова, но полностью асимметричными: если я создам баланс для Алисы Бобу, мне придется создать еще один пример баланса от Боба до Алисы. Это позволит удвоить объем памяти, необходимый для каждого «баланса» в базе данных, и потребовать обновления bob_to_alice каждый раз, когда я обновляю alice_to_bob.

Может ли кто-нибудь придумать что-то лучше или объяснить причину выбора одного из вышеуказанных? Дайте мне знать, если я смогу сделать что-то более ясное.

ответ

0

Итак, я думаю, что нашел что-то довольно аккуратное. Он использует тот факт, что user alice всегда совпадает с наименьшим идентификатором (через: обеспечить_alice_before_bob). Остальное должно быть ясно.

Модель пользователя имеет только два дополнительных «has_many: balances_as_alice, has_many: balances_as_bob». Он может работать только с одним (например: balances_as_alice), но с этими двумя настоящими + зависимыми:: destroy вы должны избегать сиротских балансов.

# == Schema Information 
# 
# Table name: balances 
# 
# id   :integer   not null, primary key 
# alice_id :integer   not null 
# bob_id  :integer   not null 
# alice_value :float   default("0.0") 
# created_at :datetime   not null 
# updated_at :datetime   not null 
# 

class Balance < ActiveRecord::Base 
    belongs_to :alice, class_name: 'User' 
    belongs_to :bob, class_name: 'User' 
    before_validation :ensure_alice_before_bob 
    validates :alice_id, presence: true 
    validates :bob_id, presence: true, uniqueness: { scope: :alice_id} 
    validate :different_users 

    def value 
    @for_bob ? -alice_value : alice_value 
    end 

    def value=(new_val) 
    @for_bob? self.alice_value = -new_val : self.alice_value = new_val 
    end 

    def Balance.get_for alice, bob 
    user = 'alice' 
    alice, bob, user = bob, alice, 'bob' if alice.id > bob.id 
    balance= alice.balances_as_alice.find_by(bob: bob) || 
     Balance.new(bob: bob, alice: alice, alice_value: 0.0) 
    balance.send("for_#{user}") 
    end 

    def for_bob 
    @for_bob = true; self 
    end 

    def for_alice 
    @for_bob = false; self 
    end 

    private 
    def ensure_alice_before_bob 
    return if self.alice_id.nil? || self.bob_id.nil? 
    unless self.alice_id < self.bob_id 
     self.bob_id, self.alice_id = self.alice_id, self.bob_id 
    end 
    end 

    def different_users 
    if self.alice_id == self.bob_id 
     errors.add(:bob_id, "Cannot be the same user") 
    end 
    end 
end