From 49b3d43ddd0204f8375234a05f22d4f8c46b7aa2 Mon Sep 17 00:00:00 2001 From: albert Date: Tue, 7 Jun 2011 17:34:09 -0400 Subject: [PATCH] * meta_search now pulls directly from GitHub * Updated gems * [inprogress] New pagination helpers used instead of pagination presenters * [inprogress] Favorites refactored to use ActiveRecord * [inprogress] PostSets refactored to use a decorator/dependency injection pattern * [inprogress] Made pool/post interaction more robust * Pool#posts now returns an ActiveRelation object * Fixed unit tests --- Gemfile | 2 +- Gemfile.lock | 26 ++--- app/models/favorite.rb | 2 +- app/models/forum_post.rb | 6 +- app/models/pool.rb | 90 +++++++++++++----- app/models/post.rb | 21 ++-- .../initializers/active_record_extensions.rb | 39 ++++++++ config/initializers/core_extensions.rb | 20 ---- db/development_structure.sql | 4 +- db/migrate/20100211025616_create_pools.rb | 11 --- .../20110607194023_create_pool_versions.rb | 17 ++++ test/unit/pool_test.rb | 75 +++++++++++---- test/unit/post_sets/pool_test.rb | 3 - test/unit/post_test.rb | 25 +++-- test/unit/upload_test.rb | 2 +- test/unit/user_feedback_test.rb | 41 +++----- tmp/test.jpg | Bin 0 -> 28086 bytes 17 files changed, 248 insertions(+), 136 deletions(-) create mode 100644 config/initializers/active_record_extensions.rb create mode 100644 db/migrate/20110607194023_create_pool_versions.rb create mode 100644 tmp/test.jpg diff --git a/Gemfile b/Gemfile index 24f6b7059..254945c6d 100644 --- a/Gemfile +++ b/Gemfile @@ -18,6 +18,6 @@ gem "haml" gem "simple_form" gem "mechanize" gem "nokogiri" -gem "meta_search" +gem "meta_search", :git => "git://github.com/ernie/meta_search.git" gem "will_paginate", :git => "git://github.com/mmack/will_paginate.git", :branch => "rails3.1" gem "silent-postgres" diff --git a/Gemfile.lock b/Gemfile.lock index 626d719ad..a81fee34a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,3 +1,12 @@ +GIT + remote: git://github.com/ernie/meta_search.git + revision: 79806e6a9db5dc4eedbd45bccc5929cf6fbf3593 + specs: + meta_search (1.1.0.pre) + actionpack (~> 3.1.0.alpha) + activerecord (~> 3.1.0.alpha) + activesupport (~> 3.1.0.alpha) + GIT remote: git://github.com/mmack/will_paginate.git revision: 5816b4e8d4382b4b167621a9aea4b8fe72983c37 @@ -7,9 +16,9 @@ GIT GIT remote: http://github.com/EmmanuelOga/ffaker.git - revision: baf4891a5351ad01775f3e4bb77c78ceed349360 + revision: 52feff4ecddbe8834b63beb122b153b65833e309 specs: - ffaker (1.7.0) + ffaker (1.8.0) GEM remote: http://gemcutter.org/ @@ -64,11 +73,6 @@ GEM mechanize (1.0.0) nokogiri (>= 1.2.1) memcache-client (1.8.5) - meta_search (0.5.4) - actionpack (>= 3.0.0.beta4) - activerecord (>= 3.0.0.beta4) - activesupport (>= 3.0.0.beta4) - arel (>= 0.4.0) mime-types (1.16) mocha (0.9.12) multi_json (1.0.3) @@ -98,14 +102,14 @@ GEM rack-ssl (~> 1.3.2) rake (>= 0.8.7) thor (~> 0.14.6) - rake (0.9.0) + rake (0.9.2) shoulda (2.11.3) silent-postgres (0.0.8) simple_form (1.4.0) simplecov (0.4.2) simplecov-html (~> 0.4.4) simplecov-html (0.4.5) - sprockets (2.0.0.beta.8) + sprockets (2.0.0.beta.10) hike (~> 1.0) rack (~> 1.0) tilt (!= 1.3.0, ~> 1.1) @@ -113,7 +117,7 @@ GEM actionmailer rake thor (0.14.6) - tilt (1.3.1) + tilt (1.3.2) treetop (1.4.9) polyglot (>= 0.3.1) tzinfo (0.3.27) @@ -129,7 +133,7 @@ DEPENDENCIES imagesize mechanize memcache-client - meta_search + meta_search! mocha nokogiri pg diff --git a/app/models/favorite.rb b/app/models/favorite.rb index b0ef6b9dd..bb4a1c6f8 100644 --- a/app/models/favorite.rb +++ b/app/models/favorite.rb @@ -3,7 +3,7 @@ class Favorite < ActiveRecord::Base validates_uniqueness_of :post_id, :scope => :user_id def self.model_for(user_id) - mod = user_id % TABLE_COUNT + mod = user_id.to_i % TABLE_COUNT Object.const_get("Favorite#{mod}") end diff --git a/app/models/forum_post.rb b/app/models/forum_post.rb index cdca4a6fc..260239f1b 100644 --- a/app/models/forum_post.rb +++ b/app/models/forum_post.rb @@ -38,8 +38,10 @@ class ForumPost < ActiveRecord::Base end def update_topic_updated_at - topic.update_attribute(:updater_id, CurrentUser.id) - topic.touch + if topic + topic.update_attribute(:updater_id, CurrentUser.id) + topic.touch + end end def initialize_creator diff --git a/app/models/pool.rb b/app/models/pool.rb index 357aeff55..b85d233bf 100644 --- a/app/models/pool.rb +++ b/app/models/pool.rb @@ -6,9 +6,10 @@ class Pool < ActiveRecord::Base has_many :versions, :class_name => "PoolVersion", :dependent => :destroy, :order => "pool_versions.id ASC" before_validation :normalize_name before_validation :normalize_post_ids + before_validation :initialize_post_count before_validation :initialize_creator, :on => :create after_save :create_version - after_save :update_posts + after_save :balance_post_ids attr_accessible :name, :description, :post_ids, :is_active, :post_id_array def self.name_to_id(name) @@ -29,58 +30,90 @@ class Pool < ActiveRecord::Base end end + def self.normalize_name(name) + name.downcase.gsub(/\s+/, "_") + end + + def self.normalize_post_ids(post_ids) + post_ids.gsub(/\s{2,}/, " ").strip + end + def initialize_creator self.creator_id = CurrentUser.id end def normalize_name - self.name = name.downcase + self.name = Pool.normalize_name(name) end def normalize_post_ids - self.post_ids = post_ids.gsub(/\s\s+/, " ") - self.post_ids = post_ids.gsub(/^\s+/, "") - self.post_ids = post_ids.gsub(/\s+$/, "") + self.post_ids = Pool.normalize_post_ids(post_ids) end def revert_to!(version) self.post_ids = version.post_ids save end - - def update_posts - post_id_array.each do |post_id| - post = Post.find(post_id) - post.add_pool(self) - end + + def contains_post?(post_id) + post_ids =~ /(?:\A| )#{post_id}(?:\Z| )/ end def add_post!(post) - return if post_ids =~ /(?:\A| )#{post.id}(?:\Z| )/ - self.post_ids += " #{post.id}" - self.post_ids = post_ids.strip + return if contains_post?(post.id) + + increment!(:post_count) + update_attribute(:post_ids, "#{post_ids} #{post.id}".strip) + post.add_pool!(self) clear_post_id_array - save end def remove_post!(post) - self.post_ids = post_ids.gsub(/(?:\A| )#{post.id}(?:\Z| )/, " ") - self.post_ids = post_ids.strip + return unless contains_post?(post.id) + + decrement!(:post_count) + update_attribute(:post_ids, Pool.normalize_post_ids(post_ids.gsub(/(?:\A| )#{post.id}(?:\Z| )/, " "))) + post.remove_pool!(self) clear_post_id_array - save end def posts(options = {}) - offset = options[:offset] || 0 - limit = options[:limit] || Danbooru.config.posts_per_page - ids = post_id_array[offset, limit] - Post.where(["id IN (?)", ids]).order(Favorite.sql_order_clause(ids)) + if options[:offset] + limit = options[:limit] || Danbooru.config.posts_per_page + slice = post_id_array.slice(options[:offset], limit) + if slice && slice.any? + Post.where("id in (?)", slice).order(arbitrary_sql_order_clause(slice, "posts")) + else + Post.where("false") + end + else + Post.where("id IN (?)", post_id_array).order(arbitrary_sql_order_clause(post_id_array, "posts")) + end + end + + def balance_post_ids + added = post_id_array - post_id_array_was + removed = post_id_array_was - post_id_array + + added.each do |post_id| + post = Post.find(post_id) + post.add_pool!(self) + end + + removed.each do |post_id| + post = Post.find(post_id) + post.remove_pool!(self) + end end def post_id_array @post_id_array ||= post_ids.scan(/\d+/).map(&:to_i) end + def post_id_array_was + @post_id_array_was ||= post_ids_was.scan(/\d+/).map(&:to_i) + end + def post_id_array=(array) self.post_ids = array.join(" ") clear_post_id_array @@ -88,6 +121,11 @@ class Pool < ActiveRecord::Base def clear_post_id_array @post_id_array = nil + @post_id_array_was = nil + end + + def initialize_post_count + self.post_count = post_id_array.size end def neighbor_posts(post) @@ -95,13 +133,13 @@ class Pool < ActiveRecord::Base post_ids =~ /\A#{post.id} (\d+)|(\d+) #{post.id} (\d+)|(\d+) #{post.id}\Z/ if $2 && $3 - {:previous => $2.to_i, :next => $3.to_i} + OpenStruct.new(:previous => $2.to_i, :next => $3.to_i) elsif $1 - {:next => $1.to_i} + OpenStruct.new(:next => $1.to_i) elsif $4 - {:previous => $4.to_i} + OpenStruct.new(:previous => $4.to_i) else - {} + OpenStruct.new end end end diff --git a/app/models/post.rb b/app/models/post.rb index 9c0f883c3..01aba4ada 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -632,17 +632,20 @@ class Post < ActiveRecord::Base end end - def add_pool(pool) - return if pool_string =~ /(?:\A| )pool:#{pool.id}(?:\Z| )/ - self.pool_string += " pool:#{pool.id}" - self.pool_string.strip! - execute_sql("UPDATE posts SET pool_string = ? WHERE id = ?", pool_string, id) + def belongs_to_pool?(pool) + pool_string =~ /(?:\A| )pool:#{pool.id}(?:\Z| )/ end - def remove_pool(pool) - self.pool_string.gsub!(/(?:\A| )pool:#{pool.id}(?:\Z| )/, " ") - self.pool_string.strip! - execute_sql("UPDATE posts SET pool_string = ? WHERE id = ?", pool_string, id) + def add_pool!(pool) + return if belongs_to_pool?(pool) + update_attribute(:pool_string, "#{pool_string} pool:#{pool.id}".strip) + pool.add_post!(self) + end + + def remove_pool!(pool) + return unless belongs_to_pool?(pool) + update_attribute(:pool_string, pool_string.gsub(/(?:\A| )pool:#{pool.id}(?:\Z| )/, " ").strip) + pool.remove_post!(self) end end diff --git a/config/initializers/active_record_extensions.rb b/config/initializers/active_record_extensions.rb new file mode 100644 index 000000000..b83192c25 --- /dev/null +++ b/config/initializers/active_record_extensions.rb @@ -0,0 +1,39 @@ +module Danbooru + module Extensions + module ActiveRecord + %w(execute select_value select_values select_all).each do |method_name| + define_method("#{method_name}_sql") do |sql, *params| + connection.__send__(method_name, self.class.sanitize_sql_array([sql, *params])) + end + + self.class.__send__(:define_method, "#{method_name}_sql") do |sql, *params| + connection.__send__(method_name, sanitize_sql_array([sql, *params])) + end + end + + def arbitrary_sql_order_clause(ids, table_name = nil) + table_name = self.class.table_name if table_name.nil? + + if ids.empty? + return "#{table_name}.id desc" + end + + conditions = [] + + ids.each_with_index do |x, n| + conditions << "when #{x} then #{n}" + end + + "case #{table_name}.id " + conditions.join(" ") + " end" + end + end + end +end + +class ActiveRecord::Base + class << self + public :sanitize_sql_array + end + + include Danbooru::Extensions::ActiveRecord +end diff --git a/config/initializers/core_extensions.rb b/config/initializers/core_extensions.rb index c7f2482f3..88dfdbfdb 100644 --- a/config/initializers/core_extensions.rb +++ b/config/initializers/core_extensions.rb @@ -1,17 +1,5 @@ module Danbooru module Extensions - module ActiveRecord - %w(execute select_value select_values select_all).each do |method_name| - define_method("#{method_name}_sql") do |sql, *params| - connection.__send__(method_name, self.class.sanitize_sql_array([sql, *params])) - end - - self.class.__send__(:define_method, "#{method_name}_sql") do |sql, *params| - connection.__send__(method_name, sanitize_sql_array([sql, *params])) - end - end - end - module String def to_escaped_for_sql_like return self.gsub(/\\/, '\0\0').gsub(/%/, '\\%').gsub(/_/, '\\_').gsub(/\*/, '%') @@ -24,14 +12,6 @@ module Danbooru end end -class ActiveRecord::Base - class << self - public :sanitize_sql_array - end - - include Danbooru::Extensions::ActiveRecord -end - class String include Danbooru::Extensions::String end diff --git a/db/development_structure.sql b/db/development_structure.sql index 2e6fed053..14ee6d555 100644 --- a/db/development_structure.sql +++ b/db/development_structure.sql @@ -8385,4 +8385,6 @@ INSERT INTO schema_migrations (version) VALUES ('20100826232512'); INSERT INTO schema_migrations (version) VALUES ('20110328215652'); -INSERT INTO schema_migrations (version) VALUES ('20110328215701'); \ No newline at end of file +INSERT INTO schema_migrations (version) VALUES ('20110328215701'); + +INSERT INTO schema_migrations (version) VALUES ('20110607194023'); \ No newline at end of file diff --git a/db/migrate/20100211025616_create_pools.rb b/db/migrate/20100211025616_create_pools.rb index 53ca23905..e53ef9e0c 100644 --- a/db/migrate/20100211025616_create_pools.rb +++ b/db/migrate/20100211025616_create_pools.rb @@ -12,17 +12,6 @@ class CreatePools < ActiveRecord::Migration add_index :pools, :name add_index :pools, :creator_id - - - create_table :pool_versions do |t| - t.column :pool_id, :integer - t.column :post_ids, :text, :null => false, :default => "" - t.column :updater_id, :integer, :null => false - t.column :updater_ip_addr, "inet", :null => false - t.timestamps - end - - add_index :pool_versions, :pool_id end def self.down diff --git a/db/migrate/20110607194023_create_pool_versions.rb b/db/migrate/20110607194023_create_pool_versions.rb new file mode 100644 index 000000000..9717ec57a --- /dev/null +++ b/db/migrate/20110607194023_create_pool_versions.rb @@ -0,0 +1,17 @@ +class CreatePoolVersions < ActiveRecord::Migration + def up + create_table :pool_versions do |t| + t.column :pool_id, :integer + t.column :post_ids, :text, :null => false, :default => "" + t.column :updater_id, :integer, :null => false + t.column :updater_ip_addr, "inet", :null => false + t.timestamps + end + + add_index :pool_versions, :pool_id + end + + def down + drop_table :pool_versions + end +end diff --git a/test/unit/pool_test.rb b/test/unit/pool_test.rb index 72f5a7779..a8fc8997d 100644 --- a/test/unit/pool_test.rb +++ b/test/unit/pool_test.rb @@ -14,10 +14,6 @@ class PoolTest < ActiveSupport::TestCase end context "A pool" do - setup do - MEMCACHE.flush_all - end - should "create versions for each distinct user" do pool = Factory.create(:pool) user = Factory.create(:user) @@ -39,9 +35,6 @@ class PoolTest < ActiveSupport::TestCase p2 = Factory.create(:post) p3 = Factory.create(:post) p4 = Factory.create(:post) - p1.add_pool(pool) - p2.add_pool(pool) - p3.add_pool(pool) pool.add_post!(p1) pool.add_post!(p2) pool.add_post!(p3) @@ -52,7 +45,7 @@ class PoolTest < ActiveSupport::TestCase posts = pool.posts.all assert_equal(3, posts.size) assert_equal([p1.id, p2.id, p3.id], posts.map(&:id)) - posts = pool.posts(:limit => 1, :offset => 1).all + posts = pool.posts.limit(1).offset(1).all assert_equal(1, posts.size) assert_equal([p2.id], posts.map(&:id)) end @@ -62,27 +55,75 @@ class PoolTest < ActiveSupport::TestCase p1 = Factory.create(:post) p2 = Factory.create(:post) p3 = Factory.create(:post) - p1.add_pool(pool) - p2.add_pool(pool) - p3.add_pool(pool) pool.add_post!(p1) pool.add_post!(p2) pool.add_post!(p3) pool.reload neighbors = pool.neighbor_posts(p1) - assert_nil(neighbors[:previous]) - assert_equal(p2.id, neighbors[:next]) + assert_nil(neighbors.previous) + assert_equal(p2.id, neighbors.next) pool.reload neighbors = pool.neighbor_posts(p2) - assert_equal(p1.id, neighbors[:previous]) - assert_equal(p3.id, neighbors[:next]) + assert_equal(p1.id, neighbors.previous) + assert_equal(p3.id, neighbors.next) pool.reload neighbors = pool.neighbor_posts(p3) - assert_equal(p2.id, neighbors[:previous]) - assert_nil(neighbors[:next]) + assert_equal(p2.id, neighbors.previous) + assert_nil(neighbors.next) + end + + should "know what its post_ids were" do + p1 = Factory.create(:post) + p2 = Factory.create(:post) + pool = Factory.create(:pool, :post_ids => "#{p1.id}") + pool.post_id_array = [p1.id, p2.id] + assert_equal([p1.id], pool.post_id_array_was) + end + + should "update its posts if the post_ids is updated directly" do + p1 = Factory.create(:post) + p2 = Factory.create(:post) + pool = Factory.create(:pool, :post_ids => "#{p1.id}") + pool.post_id_array = [p1.id, p2.id] + pool.save + p1.reload + p2.reload + assert_equal("pool:#{pool.id}", p1.pool_string) + assert_equal("pool:#{pool.id}", p2.pool_string) + end + + should "set its post count even if post_ids is updated directly" do + p1 = Factory.create(:post) + p2 = Factory.create(:post) + pool = Factory.create(:pool, :post_ids => "#{p1.id}") + pool.post_id_array = [p1.id, p2.id] + pool.save + assert_equal(2, pool.post_count) + end + + should "increment the post count every time a post is added" do + p1 = Factory.create(:post) + pool = Factory.create(:pool) + pool.add_post!(p1) + assert_equal(1, pool.post_count) + end + + should "not double increment when the same post is readded" do + p1 = Factory.create(:post) + pool = Factory.create(:pool) + pool.add_post!(p1) + pool.add_post!(p1) + assert_equal(1, pool.post_count) + end + + should "not double decrement" do + p1 = Factory.create(:post) + pool = Factory.create(:pool) + pool.remove_post!(p1) + assert_equal(0, pool.post_count) end end diff --git a/test/unit/post_sets/pool_test.rb b/test/unit/post_sets/pool_test.rb index 9c4dd228c..95f4b047b 100644 --- a/test/unit/post_sets/pool_test.rb +++ b/test/unit/post_sets/pool_test.rb @@ -16,9 +16,6 @@ module PostSets @pool.add_post!(@post_2) @pool.add_post!(@post_1) @pool.add_post!(@post_3) - @post_2.add_pool(@pool) - @post_1.add_pool(@pool) - @post_3.add_pool(@pool) @set = PostSets::Pool.new(@pool, :page => 1) end diff --git a/test/unit/post_test.rb b/test/unit/post_test.rb index 56057b9cf..9987df120 100644 --- a/test/unit/post_test.rb +++ b/test/unit/post_test.rb @@ -356,20 +356,31 @@ class PostTest < ActiveSupport::TestCase end context "Pools:" do + context "Removing a post from a pool" do + should "update the post's pool string" do + post = Factory.create(:post) + pool = Factory.create(:pool) + post.add_pool!(pool) + post.remove_pool!(pool) + post.reload + assert_equal("", post.pool_string) + post.remove_pool!(pool) + post.reload + assert_equal("", post.pool_string) + end + end + context "Adding a post to a pool" do should "update the post's pool string" do post = Factory.create(:post) pool = Factory.create(:pool) - post.add_pool(pool) + post.add_pool!(pool) post.reload assert_equal("pool:#{pool.id}", post.pool_string) - post.add_pool(pool) + post.add_pool!(pool) post.reload assert_equal("pool:#{pool.id}", post.pool_string) - post.remove_pool(pool) - post.reload - assert_equal("", post.pool_string) - post.remove_pool(pool) + post.remove_pool!(pool) post.reload assert_equal("", post.pool_string) end @@ -474,7 +485,7 @@ class PostTest < ActiveSupport::TestCase post2 = Factory.create(:post) post3 = Factory.create(:post) pool = Factory.create(:pool) - post1.add_pool(pool) + post1.add_pool!(pool) relation = Post.tag_match("pool:#{pool.name}") assert_equal(1, relation.count) assert_equal(post1.id, relation.first.id) diff --git a/test/unit/upload_test.rb b/test/unit/upload_test.rb index a31f1693c..c0c52b66c 100644 --- a/test/unit/upload_test.rb +++ b/test/unit/upload_test.rb @@ -2,7 +2,7 @@ require_relative '../test_helper' class UploadTest < ActiveSupport::TestCase setup do - user = Factory.create(:user) + user = Factory.create(:contributor_user) CurrentUser.user = user CurrentUser.ip_addr = "127.0.0.1" MEMCACHE.flush_all diff --git a/test/unit/user_feedback_test.rb b/test/unit/user_feedback_test.rb index 1da4b337d..7267faed8 100644 --- a/test/unit/user_feedback_test.rb +++ b/test/unit/user_feedback_test.rb @@ -1,39 +1,28 @@ require_relative '../test_helper' class UserFeedbackTest < ActiveSupport::TestCase - setup do - user = Factory.create(:user) - CurrentUser.user = user - CurrentUser.ip_addr = "127.0.0.1" - MEMCACHE.flush_all - end - - teardown do - CurrentUser.user = nil - CurrentUser.ip_addr = nil - end - context "A user's feedback" do + setup do + CurrentUser.ip_addr = "127.0.0.1" + MEMCACHE.flush_all + end + + teardown do + CurrentUser.user = nil + CurrentUser.ip_addr = nil + end + should "should not validate if the creator is not privileged" do user = Factory.create(:user) - admin = Factory.create(:admin_user) - moderator = Factory.create(:moderator_user) - janitor = Factory.create(:janitor_user) - contributor = Factory.create(:contributor_user) privileged = Factory.create(:privileged_user) member = Factory.create(:user) - feedback = Factory.create(:user_feedback, :user => user, :creator => admin) + CurrentUser.user = privileged + feedback = Factory.create(:user_feedback, :user => user) assert(feedback.errors.empty?) - feedback = Factory.create(:user_feedback, :user => user, :creator => moderator) - assert(feedback.errors.empty?) - feedback = Factory.create(:user_feedback, :user => user, :creator => janitor) - assert(feedback.errors.empty?) - feedback = Factory.create(:user_feedback, :user => user, :creator => contributor) - assert(feedback.errors.empty?) - feedback = Factory.create(:user_feedback, :user => user, :creator => privileged) - assert(feedback.errors.empty?) - feedback = Factory.build(:user_feedback, :user => user, :creator => member) + + CurrentUser.user = member + feedback = Factory.build(:user_feedback, :user => user) feedback.save assert(feedback.errors.any?) end diff --git a/tmp/test.jpg b/tmp/test.jpg new file mode 100644 index 0000000000000000000000000000000000000000..253c508d9c84cee6433f57c5c756f06dc25534a8 GIT binary patch literal 28086 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 PaJbDXdPt*R{r^n>pWW2} literal 0 HcmV?d00001