Permalink
Browse files

add association calculation method to order by results of a sql funct…

…ion on a column of a joined table
  • Loading branch information...
1 parent b0b56b1 commit b4abc54674cad4aac19baa916fb7f42623667c02 @mjankowski mjankowski committed Jan 20, 2011
View
12 README.md
@@ -56,6 +56,7 @@ Assuming a database schema...
create_table :comments, :force => true do |t|
t.integer :user_id
t.text :description
+ t.integer :rating
t.timestamps
end
end
@@ -71,6 +72,7 @@ And some basic model declarations...
has_ranking :comments
has_recent_records :comments
has_recent_records :articles, :comments
+ has_calculated_records :comments, :on => :rating
end
class Post < ActiveRecord::Base
@@ -199,6 +201,16 @@ Records with associated records since a certain time...
User.recent_comments_and_posts_since(3.days.ago)
User.recent_comments_or_posts_since(4.days.ago)
+Records with highest and lowest association column average...
+
+ User.by_comments_highest_rating_average
+ User.by_comments_lowest_rating_average
+
+Records with highest and lowest association column total...
+
+ User.by_comments_highest_rating_total
+ User.by_comments_lowest_rating_total
+
State columns
-------------
View
19 lib/pacecar/associations.rb
@@ -6,6 +6,25 @@ def self.included(base)
module ClassMethods
+ def has_calculated_records(*names)
+ opts = names.extract_options!
+ names.each do |name|
+ *columns = opts[:on] || []
+ columns.flatten.each do |column|
+ { 'highest' => 'desc', 'lowest' => 'asc' }.each do |direction_name, direction|
+ { 'average' => 'avg', 'total' => 'sum' }.each do |function_name, function_method|
+ scope "by_#{name}_#{direction_name}_#{column}_#{function_name}".to_sym, {
+ :select => "#{quoted_table_name}.*, #{function_method}(#{connection.quote_table_name(name)}.#{connection.quote_column_name column}) as #{name}_#{column}_#{function_name}",
+ :joins => "inner join #{connection.quote_table_name(name)} on #{connection.quote_table_name(name)}.#{connection.quote_column_name reflections[name.to_sym].primary_key_name} = #{quoted_table_name}.#{connection.quote_column_name primary_key}",
+ :group => safe_column_names.collect { |column_name| "#{quoted_table_name}.#{connection.quote_column_name(column_name)}" }.join(', '),
+ :order => "#{name}_#{column}_#{function_name} #{direction}"
+ }
+ end
+ end
+ end
+ end
+ end
+
def has_recent_records(*names)
names.each do |name|
scope "recent_#{name}_since".to_sym, lambda { |since|
View
39 spec/associations_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe 'Associations' do
+describe 'Associations', 'has recent records' do
before do
@comment_user = Factory :user
@@ -30,3 +30,40 @@
end
end
+
+
+describe 'Associations', 'has calculated records' do
+ before do
+ @user_one = Factory :user
+ @user_two = Factory :user
+ Factory :comment, :user => @user_one, :rating => 8
+ Factory :comment, :user => @user_one, :rating => 6
+ Factory :comment, :user => @user_two, :rating => 4
+ Factory :comment, :user => @user_two, :rating => 3
+ Factory :comment, :user => @user_two, :rating => 2
+ end
+ it "should order records based on association column highest average" do
+ output = User.by_comments_highest_rating_average
+ output.should == [@user_one, @user_two]
+ output.first.comments_rating_average.to_i.should == 7
+ output.last.comments_rating_average.to_i.should == 3
+ end
+ it "should order records based on association column lowest average" do
+ output = User.by_comments_lowest_rating_average
+ output.should == [@user_two, @user_one]
+ output.first.comments_rating_average.to_i.should == 3
+ output.last.comments_rating_average.to_i.should == 7
+ end
+ it "should order records based on association column highest total" do
+ output = User.by_comments_highest_rating_total
+ output.should == [@user_one, @user_two]
+ output.first.comments_rating_total.to_i.should == 14
+ output.last.comments_rating_total.to_i.should == 9
+ end
+ it "should order records based on association column lowest total" do
+ output = User.by_comments_lowest_rating_total
+ output.should == [@user_two, @user_one]
+ output.first.comments_rating_total.to_i.should == 9
+ output.last.comments_rating_total.to_i.should == 14
+ end
+end
View
1 spec/dummy/app/models/user.rb
@@ -9,5 +9,6 @@ class User < ActiveRecord::Base
has_ranking :comments
has_recent_records :comments
has_recent_records :posts, :comments
+ has_calculated_records :comments, :on => :rating
end
View
1 spec/dummy/db/migrate/20100419201348_create_schema.rb
@@ -24,6 +24,7 @@ def self.up
create_table :comments, :force => true do |t|
t.integer :user_id
t.text :description
+ t.integer :rating
t.timestamps
end
create_table :mammals, :force => true do |t|
View
2 spec/helpers_spec.rb
@@ -14,7 +14,7 @@
describe "A class with a db table" do
it "should should return columns for #safe_column_names" do
- Comment.safe_column_names.should == ['id', 'user_id', 'description', 'created_at', 'updated_at']
+ Comment.safe_column_names.should == ['id', 'user_id', 'description', 'rating', 'created_at', 'updated_at']
end
end
View
10 spec/ranking_spec.rb
@@ -11,11 +11,17 @@
end
it "should set the correct expected values on a maximum_ column method" do
- User.maximum_comments.should == [@many, @few]
+ output = User.maximum_comments
+ output.should == [@many, @few]
+ output.first.comments_count.to_i.should == 5
+ output.last.comments_count.to_i.should == 2
end
it "should set the correct expected values on a minimum_ column method" do
- User.minimum_comments.should == [@few, @many]
+ output = User.minimum_comments
+ output.should == [@few, @many]
+ output.first.comments_count.to_i.should == 2
+ output.last.comments_count.to_i.should == 5
end
end

0 comments on commit b4abc54

Please sign in to comment.