Use a fake to not allocate an AR model for every reference to a person

This commit is contained in:
maxwell 2011-01-24 15:13:38 -08:00
parent 4217aa75e0
commit 1b3c58c2c2
13 changed files with 120 additions and 9 deletions

View file

@ -24,8 +24,9 @@ class AspectsController < ApplicationController
@aspect_ids = @aspects.map{|a| a.id} @aspect_ids = @aspects.map{|a| a.id}
@posts = StatusMessage.joins(:aspects).where(:pending => false, @posts = StatusMessage.joins(:aspects).where(:pending => false,
:aspects => {:id => @aspect_ids}).includes({:person => :profile}, {:comments => {:person => :profile}}, :photos).select('DISTINCT `posts`.*').paginate( :aspects => {:id => @aspect_ids}).includes(:comments, :photos).select('DISTINCT `posts`.*').paginate(
:page => params[:page], :per_page => 15, :order => 'created_at DESC') :page => params[:page], :per_page => 15, :order => 'created_at DESC')
@fakes = PostsFake.new(@posts)
@contacts = current_user.contacts.includes(:person => :profile).where(:pending => false) @contacts = current_user.contacts.includes(:person => :profile).where(:pending => false)
@ -34,7 +35,6 @@ class AspectsController < ApplicationController
end end
end end
def create def create
@aspect = current_user.aspects.create(params[:aspect]) @aspect = current_user.aspects.create(params[:aspect])
if @aspect.valid? if @aspect.valid?

View file

@ -62,6 +62,7 @@ class PeopleController < ApplicationController
end end
@posts = current_user.posts_from(@person).where(:type => "StatusMessage").paginate :page => params[:page] @posts = current_user.posts_from(@person).where(:type => "StatusMessage").paginate :page => params[:page]
@fakes = PostsFake.new(@posts)
respond_with @person, :locals => {:post_type => :all} respond_with @person, :locals => {:post_type => :all}

View file

@ -26,13 +26,13 @@ module ApplicationHelper
def aspects_with_post aspects, post def aspects_with_post aspects, post
aspects.select do |aspect| aspects.select do |aspect|
aspect.posts.include?(post) aspect.post_ids.include?(post.id)
end end
end end
def aspects_without_post aspects, post def aspects_without_post aspects, post
aspects.reject do |aspect| aspects.reject do |aspect|
aspect.posts.include?(post) aspect.post_ids.include?(post.id)
end end
end end

View file

@ -5,5 +5,5 @@
= render 'shared/publisher', :aspect => aspect, :aspect_ids => aspect_ids = render 'shared/publisher', :aspect => aspect, :aspect_ids => aspect_ids
#main_stream.stream{:data => {:guids => aspect_ids.join(',')}} #main_stream.stream{:data => {:guids => aspect_ids.join(',')}}
= render 'shared/stream', :posts => posts = render 'shared/stream', :posts => fakes
= will_paginate posts = will_paginate posts

View file

@ -13,7 +13,8 @@
= render 'aspect_stream', = render 'aspect_stream',
:aspect => @aspect, :aspect => @aspect,
:aspect_ids => @aspect_ids, :aspect_ids => @aspect_ids,
:posts => @posts :posts => @posts,
:fakes => @fakes
.span-7.last .span-7.last
#home_user_badge #home_user_badge

View file

@ -1,4 +1,4 @@
$('#aspect_stream_container').html("<%= escape_javascript(render('aspects/aspect_stream', :aspect => @aspect, :aspect_ids => @aspect_ids, :posts => @posts)) %>"); $('#aspect_stream_container').html("<%= escape_javascript(render('aspects/aspect_stream', :aspect => @aspect, :aspect_ids => @aspect_ids, :posts => @posts, :fakes => @fakes)) %>");
$('#aspect_listings').html("<%= escape_javascript(render('aspects/aspect_listings', :aspects => @aspects, :contacts => @contacts)) %>"); $('#aspect_listings').html("<%= escape_javascript(render('aspects/aspect_listings', :aspects => @aspects, :contacts => @contacts)) %>");
$('a[rel*=facebox]').facebox(); $('a[rel*=facebox]').facebox();

View file

@ -55,7 +55,7 @@
= render 'photos/index', :photos => @posts = render 'photos/index', :photos => @posts
- else - else
#main_stream.stream #main_stream.stream
= render 'shared/stream', :posts => @posts = render 'shared/stream', :posts => @fakes
= will_paginate @posts = will_paginate @posts

View file

@ -10,6 +10,8 @@ require 'rails/all'
Bundler.require(:default, Rails.env) if defined?(Bundler) Bundler.require(:default, Rails.env) if defined?(Bundler)
require File.expand_path('../../lib/log_overrider', __FILE__) require File.expand_path('../../lib/log_overrider', __FILE__)
require File.expand_path('../../lib/fake', __FILE__)
module Diaspora module Diaspora
class Application < Rails::Application class Application < Rails::Application
# Settings in config/environments/* take precedence over those specified here. # Settings in config/environments/* take precedence over those specified here.

View file

@ -78,7 +78,7 @@ module Diaspora
def posts_from(person) def posts_from(person)
asp = Aspect.arel_table asp = Aspect.arel_table
p = Post.arel_table p = Post.arel_table
person.posts.includes(:aspects, :comments => {:person => :profile}).where( p[:public].eq(true).or(asp[:user_id].eq(self.id))).select('DISTINCT `posts`.*').order("posts.updated_at DESC") person.posts.includes(:aspects, :comments).where( p[:public].eq(true).or(asp[:user_id].eq(self.id))).select('DISTINCT `posts`.*').order("posts.updated_at DESC")
end end
end end
end end

48
lib/fake.rb Normal file
View file

@ -0,0 +1,48 @@
class PostsFake
attr_reader :people_hash, :post_fakes
def method_missing(method, *args, &block)
@post_fakes.send(method, *args, &block)
end
def initialize(posts)
person_ids = []
posts.each do |p|
person_ids << p.person_id
p.comments.each do |c|
person_ids << c.person_id
end
end
people = Person.where(:id => person_ids).includes(:profile)
@people_hash = {}
people.each{|person| @people_hash[person.id] = person}
@post_fakes = posts.map do |post|
f = Fake.new(post, self)
f.comments = post.comments.map do |comment|
Fake.new(comment, self)
end
f
end
end
class Fake
attr_accessor :comments
def initialize(model, fakes_collection)
@fakes_collection = fakes_collection
@model = model
end
def id
@model.id
end
def person
@fakes_collection.people_hash[@model.person_id]
end
def method_missing(method, *args)
@model.send(method, *args)
end
end
end

View file

@ -303,4 +303,9 @@ describe AspectsController do
@aspect0.contacts.include?(@contact).should be false @aspect0.contacts.include?(@contact).should be false
end end
end end
describe "#hashes_for_posts" do
it 'returns only distinct people' do
end
end
end end

View file

@ -78,5 +78,6 @@ end
Factory.define(:comment) do |comment| Factory.define(:comment) do |comment|
comment.sequence(:text) {|n| "#{n} cats"} comment.sequence(:text) {|n| "#{n} cats"}
comment.association(:person)
end end

53
spec/lib/fake_spec.rb Normal file
View file

@ -0,0 +1,53 @@
require 'spec_helper'
describe PostsFake do
before do
@posts = []
@people = []
4.times do
post = Factory(:status_message)
@people << post.person
4.times do
comment = Factory(:comment, :post => post)
@people << comment.person
end
@posts << post
end
end
describe '#initialize' do
before do
@posts_fake = PostsFake.new(@posts)
end
it 'sets @people_hash' do
@people.each do |person|
@posts_fake.people_hash[person.reload.id].should == person
end
@posts_fake.people_hash.length.should == @people.length
end
it 'sets @post_fakes to an array of fakes' do
@posts_fake.post_fakes.each{|x| x.class.should be PostsFake::Fake}
end
end
describe PostsFake::Fake do
before do
@post = mock()
@fakes = mock()
@fake = PostsFake::Fake.new(@post, @fakes)
end
it 'refers to the parent collection for a person' do
@post.should_receive(:person_id)
@fakes.should_receive(:people_hash).and_return({})
@fake.person
end
it 'refers to its comments array for comments' do
@fake.comments = [mock()]
@fake.comments
end
it 'refers to its post for any other field' do
@post.should_receive(:text)
@fake.text
end
end
end