hypern
hypern

Reputation: 887

Defining an order to a set of associated records

I have two models, Task and Unit. A task has_many :units. I would like to order the units based on some kind of precedence. For example: There are two units A and B. A must be completed before B. Users can add another unit, for example C, between A and B. Unit C could also then be deleted - A and B's precedence will remain intact. Whats the most simple/straight forward way to implement this in Rails?

Ideas I have considered:

  1. Adding a precedence integer column to the units table that gets updated on any operations that change the collection of units.

  2. Adding a self join association to unit model pointing to the next/previous unit and modifying the associations when the unit collection is changed.

Upvotes: 1

Views: 60

Answers (2)

Dave Schweisguth
Dave Schweisguth

Reputation: 37657

The right way to model your problem depends on how complicated your Task structures are going to be.

If a Task's Units can always be put into first-to-last order, giving each Unit an integer order will make it easiest to put Units into that order, with order in ActiveRecord or sort in memory. The only disadvantage is that when you add or remove a Unit you may need to save other Units to update their orders. That's probably not a big disadvantage if Tasks have smallish integer numbers of Units.

If a Task can have cycles, each Unit will need a reference to the Unit that precedes it. You might even end up needing a model to represent the relationship between a Unit and the Unit that follows it. So think through the use cases you expect to need to support and see which applies.

Upvotes: 1

StanisLove Sid
StanisLove Sid

Reputation: 322

You can use acts_as_votable gem. This migration can solve your problem:

class AddCachedVotesToPosts < ActiveRecord::Migration
  def self.up
    add_column :posts, :cached_votes_total, :integer, :default => 0
    add_column :posts, :cached_votes_score, :integer, :default => 0
    add_column :posts, :cached_votes_up, :integer, :default => 0
    add_column :posts, :cached_votes_down, :integer, :default => 0
    add_column :posts, :cached_weighted_score, :integer, :default => 0
    add_column :posts, :cached_weighted_total, :integer, :default => 0
    add_column :posts, :cached_weighted_average, :float, :default => 0.0
    add_index  :posts, :cached_votes_total
    add_index  :posts, :cached_votes_score
    add_index  :posts, :cached_votes_up
    add_index  :posts, :cached_votes_down
    add_index  :posts, :cached_weighted_score
    add_index  :posts, :cached_weighted_total
    add_index  :posts, :cached_weighted_average

    # Uncomment this line to force caching of existing votes
    # Post.find_each(&:update_cached_votes)
  end

  def self.down
    remove_column :posts, :cached_votes_total
    remove_column :posts, :cached_votes_score
    remove_column :posts, :cached_votes_up
    remove_column :posts, :cached_votes_down
    remove_column :posts, :cached_weighted_score
    remove_column :posts, :cached_weighted_total
    remove_column :posts, :cached_weighted_average
  end
end

Especially this column add_column :posts, :cached_votes_up, :integer, :default => 0 and this index add_index :posts, :cached_votes_score

Upvotes: 0

Related Questions