Skip to content

Instantly share code, notes, and snippets.

@jimeh
Created July 16, 2009 07:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jimeh/148296 to your computer and use it in GitHub Desktop.
Save jimeh/148296 to your computer and use it in GitHub Desktop.
weight.rb (RoR): Lets you pick a row from a table randomly based on the rows weight.
#
#
# weight.rb (A Ruby on Rails Model).
# Lets you pick a row from a table randomly based on the rows weight.
#
# Your table will need three columns:
# - weight (float)
# - weight_begin (float)
# - weight_end (float)
#
# You can also support multiple categories by adding a string column to your
# table, and setting @@category_column to the columns name. Each category
# will have it's own weight scope.
#
# The code within the class...end definition can be pasted into your own
# Model without any modification cause I'm using "self.class.to_s.constantize"
# instead of "Weight" within the instance methods.
#
# The weight_begin and weight_end "index" columns are auto-updated when
# you call the save method on new or existing row objects.
#
#
class Weight < ActiveRecord::Base
after_create :update_weights
after_update :update_weights
after_destroy :update_weights
@@category_column = nil
def self.pick(category = nil)
scope = rand(10000).to_f / 100.00
if category.nil?
conditions = ["weight_begin <= ? AND weight_end >= ?", scope, scope]
else
conditions = ["weight_begin <= ? AND weight_end >= ? AND #{@@category_column} = ?", scope, scope, category]
end
self.find(:first, :conditions => conditions)
end
def self.reset_weights
self.update_all({:weight_begin => nil, :weight_end => nil})
end
def self.update_weights
self.new.update_weights
end
def update_weights
self_class = self.class.to_s.constantize
category_changed = self.send("#{@@category_column}_changed?") || false
if self.new_record? || self.weight_changed? || !category_changed.nil?
categories = [nil]
if !@@category_column.nil? && self_class.column_names.include?(@@category_column.to_s)
categories = self_class.find(:all, :select => "distinct #{@@category_column}").map { |cat| cat.send(@@category_column) }
end
categories.each do |category|
conditions = (!category.nil?) ? {@@category_column.to_sym => category} : {}
total = self_class.sum(:weight, :conditions => conditions)
rows = self_class.find(:all, :conditions => conditions)
previous = 0.00
size = rows.size
rows.each_with_index do |row, i|
previous += 0.01 if previous > 0.00
weight_begin = previous
weight_end = ((previous + (row.weight.to_f / total.to_f * 100))).round(2)
weight_end = 100.00 if i == (size-1)
previous = weight_end
self_class.update_all({:weight_begin => weight_begin, :weight_end => weight_end}, {:id => row.id})
end
end
end
return nil
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment