views:

147

answers:

1

I have four models:

  • User
  • Award
  • Badge
  • GameWeek

The associations are as follows:

  • User has many awards.
  • Award belongs to user.
  • Badge has many awards.
  • Award belongs to badge.
  • User has many game_weeks.
  • GameWeek belongs to user.
  • GameWeek has many awards.
  • Award belongs to game_week.

Thus, user_id, badge_id and game_week_id are foreign keys in awards table.

Badge implements an STI model. Let's just say it has the following subclasses: BadgeA and BadgeB.

Some rules to note:

The game_week_id fk can be nil for BadgeA, but can't be nil for BadgeB.

Here are my questions:

  1. For BadgeA, how do I write a validation that it can only be awarded one time? That is, the user can't have more than one -- ever.
  2. For BadgeB, how do I write a validation that it can only be awarded one time per game week?
+1  A: 

Data model:

In my comprehension, here is your data model (click to enlarge):

Data model

Migration:

The migration will let you meet your second requirement, at the migration level:

class CreateAwards < ActiveRecord::Migration
  def self.up
    create_table :awards do |t|
      # custom attributes here
      t.string     :name
      t.text       :description
      t.references :user,       :null => false
      t.references :game_week#,  :null => false
      t.references :badge,      :null => false
      t.timestamps
    end
    # a user can be awarded no more than a badge per week
    add_index :awards, [:user_id, :badge_id, :game_week_id], :unique => true
    # a user can be awarded no more than a badge for ever
    #add_index :awards, [:user_id, :badge_id], :unique => true
  end

  def self.down
    drop_table :awards
  end
end

Model:

The model will let you meet both your requirements, at the model level:

class Award < ActiveRecord::Base
  validate_uniqueness_of :user, :badge,
    :if => Proc.new { |award| award.badge === BadgeA }
  validate_uniqueness_of :user, :badge, game_week,
    :unless => Proc.new { |award| award.badge === BadgeA }
  #validate_uniqueness_of :user, :badge, game_week,
  #  :if => Proc.new { |award| award.badge === BadgeB }
end

Note:

I didn't try these snippets, but I think that the idea is here :)

Arnaud Leymet
What's the === do?
keruilin
It's the case equality operator in Ruby.You can see more details in the [Object Ruby doc][1] and in this [blog post][2]. [1]: http://ruby-doc.org/core/classes/Object.html#M000345 [2]: http://www.pluitsolutions.com/2006/10/09/comparing-equality-eql-equal-and-case-equality-in-ruby/
Arnaud Leymet