major refactoring of post sets and pagination, incomplete

This commit is contained in:
albert
2011-06-03 19:47:16 -04:00
parent ce0695c606
commit ca3e9bb6db
21 changed files with 475 additions and 204 deletions

View File

@@ -5,6 +5,20 @@ class Favorite
"favorites_#{user_id.to_i % 10}"
end
def self.sql_order_clause(post_ids, posts_table_alias = "posts")
if post_ids.empty?
return "#{posts_table_alias}.id desc"
end
conditions = []
post_ids.each_with_index do |post_id, n|
conditions << "when #{post_id} then #{n}"
end
"case #{posts_table_alias}.id " + conditions.join(" ") + " end"
end
def self.create(attributes)
user_id = attributes[:user_id]
post_id = attributes[:post_id]
@@ -26,6 +40,19 @@ class Favorite
destroy_for_post(conditions[:post_id])
end
end
def self.find_post_ids(user_id, options)
limit = options[:limit] || 1 || Danbooru.config.posts_per_page
if options[:before_id]
select_values_sql("SELECT post_id FROM #{table_name_for(user_id)} WHERE id < ? ORDER BY id DESC LIMIT ?", options[:before_id], limit)
elsif options[:after_id]
select_values_sql("SELECT post_id FROM #{table_name_for(user_id)} WHERE id > ? ORDER BY id ASC LIMIT ?", options[:after_id], limit).reverse
elsif options[:offset]
select_values_sql("SELECT post_id FROM #{table_name_for(user_id)} ORDER BY id DESC LIMIT ? OFFSET ?", limit, options[:offset])
else
select_values_sql("SELECT post_id FROM #{table_name_for(user_id)} ORDER BY id DESC LIMIT ?", limit)
end
end
def self.exists?(conditions)
if conditions[:user_id] && conditions[:post_id]
@@ -39,26 +66,30 @@ class Favorite
end
end
private
def self.destroy_for_post_and_user(post_id, user_id)
execute_sql("DELETE FROM #{table_name_for(user_id)} WHERE post_id = #{post_id} AND user_id = #{user_id}")
end
def self.destroy_for_post(post)
0.upto(9) do |i|
execute_sql("DELETE FROM favorites_#{i} WHERE post_id = #{post.id}")
end
end
def self.destroy_for_user(user)
execute_sql("DELETE FROM #{table_name_for(user)} WHERE user_id = #{user.id}")
end
def self.select_value_sql(sql, *params)
ActiveRecord::Base.select_value_sql(sql, *params)
end
def self.execute_sql(sql, *params)
ActiveRecord::Base.execute_sql(sql, *params)
def self.destroy_for_post_and_user(post_id, user_id)
execute_sql("DELETE FROM #{table_name_for(user_id)} WHERE post_id = #{post_id} AND user_id = #{user_id}")
end
def self.destroy_for_post(post)
0.upto(9) do |i|
execute_sql("DELETE FROM favorites_#{i} WHERE post_id = #{post.id}")
end
end
def self.destroy_for_user(user)
execute_sql("DELETE FROM #{table_name_for(user)} WHERE user_id = #{user.id}")
end
def self.select_value_sql(sql, *params)
ActiveRecord::Base.select_value_sql(sql, *params)
end
def self.select_values_sql(sql, *params)
ActiveRecord::Base.select_values_sql(sql, *params)
end
def self.execute_sql(sql, *params)
ActiveRecord::Base.execute_sql(sql, *params)
end
end

View File

@@ -1,45 +1,78 @@
# A PostSet represents a paginated slice of posts. It is used in conjunction
# with the helpers to render the paginator.
#
# Usage:
#
# @post_set = PostSets::Base.new(params)
# @post_set.extend(PostSets::Sequential)
# @post_set.extend(PostSets::Post)
module PostSets
class Base
attr_accessor :page, :before_id, :count, :posts
def initialize(options = {})
@page = options[:page] ? options[:page].to_i : 1
@before_id = options[:before_id]
load_posts
attr_reader :params, :posts
delegate :to_xml, :to_json, :to => :posts
def initialize(params)
@params = params
end
def has_wiki?
false
end
def use_sequential_paginator?
!use_numbered_paginator?
end
def use_numbered_paginator?
before_id.nil?
end
def load_posts
# Should a return a paginated array of posts. This means it should have
# at most <limit> elements.
def posts
raise NotImplementedError
end
def to_xml
posts.to_xml
# Does this post set have a valid wiki page representation?
def has_wiki?
raise NotImplementedError
end
def to_json
posts.to_json
# Should return an array of strings representing the tags.
def tags
raise NotImplementedError
end
# Given an ActiveRelation object, perform the necessary pagination to
# extract at most <limit> elements. Should return an array.
def slice(relation)
raise NotImplementedError
end
# For cases where we're not relying on the default pagination
# implementation (for example, if the ids are cached in a string)
# then pass in the offset/before_id/after_id parameters here.
def pagination_options
raise NotImplementedError
end
# This method should throw an exception if for whatever reason the query
# is invalid or forbidden.
def validate
end
# Clear out any memoized instance variables.
def reload
@posts = nil
@presenter = nil
@tag_string = nil
end
def tag_string
@tag_string ||= tags.join(" ")
end
def is_first_page?
raise NotImplementedError
end
def is_last_page?
posts.size == 0
end
def presenter
@presenter ||= PostSetPresenter.new(self)
end
def offset
((page < 1) ? 0 : (page - 1)) * count
end
def limit
Danbooru.config.posts_per_page
end

View File

@@ -1,18 +1,29 @@
module PostSets
class Favorite < Base
attr_accessor :user
def initialize(user)
@user = user
super()
module Favorite
def user
@user ||= User.find(params[:id])
end
def tags
"fav:#{user.name}"
@tags ||= ["fav:#{user.name}"]
end
def has_wiki?
false
end
def reload
super
@user = nil
@count = nil
end
def count
@count ||= Favorite.count(user.id)
end
def load_posts
@posts = user.favorite_posts(:before_id => before_id)
def posts
@posts ||= user.favorites(pagination_options)
end
end
end

View File

@@ -0,0 +1,35 @@
module PostSets
module Numbered
attr_reader :page
def initialize(params)
super
@page = options[:page] ? options[:page].to_i : 1
end
def total_pages
@total_pages ||= (count / limit.to_f).ceil.to_i
end
def reload
super
@total_pages = nil
end
def slice(relation)
relation.offset(offset).all
end
def pagination_options
{:offset => offset}
end
def is_first_page?
offset == 0
end
def offset
((page < 1) ? 0 : (page - 1)) * limit
end
end
end

View File

@@ -1,33 +1,30 @@
# This only works with the numbered paginator because of the way
# the association is stored.
module PostSets
class Pool < Base
attr_reader :pool
def initialize(pool, options = {})
@pool = pool
@count = pool.post_id_array.size
super(options)
module Pool
def pool
@pool ||= Pool.find(params[:id])
end
def tags
"pool:#{pool.name}"
["pool:#{pool.name}"]
end
def has_wiki?
true
end
def count
pool.post_count
end
def load_posts
@posts = pool.posts(:limit => limit, :offset => offset).order("posts.id")
def posts
@posts ||= pool.posts(pagination_options)
end
def sorted_posts
sort_posts(@posts)
end
private
def sort_posts(posts)
posts_by_id = posts.inject({}) do |hash, post|
hash[post.id] = post
hash
end
@pool.post_id_array.map {|x| posts_by_id[x]}
def reload
super
@pool = nil
end
end
end

View File

@@ -1,78 +1,56 @@
module PostSets
class Post < Base
module Post
class Error < Exception ; end
attr_accessor :tags, :errors, :count
attr_accessor :wiki_page, :artist, :suggestions
def initialize(tags, options = {})
super(options)
@tags = Tag.normalize(tags)
@errors = []
load_associations
load_suggestions
validate
attr_accessor :tags, :count, :wiki_page, :artist, :suggestions
def tags
@tags ||= Tag.normalize(params[:tags])
end
def count
@count ||= ::Post.fast_count(tag_string)
end
def posts
@posts ||= slice(::Post.tag_match(tags).limit(limit))
end
def reload
super
@tags = nil
@tag_string = nil
@count = nil
@wiki_page = nil
@artist = nil
end
def wiki_page
@wiki_page ||= ::WikiPage.titled(tag_string).first
end
def artist
@artist ||= ::Artist.find_by_name(tag_string)
end
def has_wiki?
is_single_tag?
end
def has_errors?
errors.any?
end
def offset
x = (page - 1) * limit
if x < 0
x = 0
end
x
end
def is_single_tag?
tag_array.size == 1
end
def date_tag
tag_array.grep(/date:/).first
end
def load_associations
if is_single_tag?
@wiki_page = ::WikiPage.titled(tags).first
@artist = ::Artist.find_by_name(tags)
end
end
def load_posts
@count = ::Post.fast_count(tags)
@posts = ::Post.tag_match(tags).before_id(before_id).all(:order => "posts.id desc", :limit => limit, :offset => offset)
end
def load_suggestions
if count < limit && is_single_tag?
@suggestions = Tag.find_suggestions(tags)
else
@suggestions = []
end
end
def tag_array
@tag_array ||= Tag.scan_query(tags)
end
def tag
tag_array.first
end
def validate
super
validate_page
validate_query_count
rescue Error => x
@errors << x.to_s
end
def validate_page
if page > 1_000
raise Error.new("You cannot explicitly specify the page after page 1000")

View File

@@ -0,0 +1,29 @@
module PostSets
module Sequential
attr_reader :before_id, :after_id
def initialize(params)
super
@before_id = params[:before_id]
@after_id = params[:after_id]
end
def slice(relation)
if before_id
relation.where("id < ?", before_id).all
elsif after_id
relation.where("id > ?", after_id).order("id asc").all.reverse
else
relation.all
end
end
def pagination_options
{:before_id => before_id, :after_id => after_id}
end
def is_first_page?
before_id.nil?
end
end
end

View File

@@ -1,34 +1,29 @@
module PostSets
class WikiPage < Base
attr_reader :tag_name
def initialize(tag_name)
@tag_name = tag_name
super()
module WikiPage
def wiki_page
@wiki_page ||= begin
if params[:id]
::WikiPage.find(params[:id])
elsif params[:tags]
::WikiPage.titled(params[:tags]).first
end
end
end
def load_posts
@posts = ::Post.tag_match(tag_name).all(:order => "posts.id desc", :limit => limit, :offset => offset)
end
def limit
8
end
def offset
0
def has_wiki?
true
end
def tags
[@tag_name]
@tags ||= Tag.normalize(wiki_page.title)
end
def use_sequential_paginator?
false
def posts
@posts ||= slice(::Post.tag_match(tag_string))
end
def use_numbered_paginator?
false
def count
@count ||= ::Post.fast_count(tag_string)
end
end
end