From 551c25909cf94bdeee3701781aed8658cefac95a Mon Sep 17 00:00:00 2001 From: albert Date: Thu, 4 Nov 2010 18:17:03 -0400 Subject: [PATCH] * Denormalized post versions. Testing has shown it reduces the size of the table 66%. --- app/logical/date_tag.rb | 4 +- app/models/post.rb | 35 +++--- app/models/post_history.rb | 40 +++++++ app/models/post_version.rb | 6 - app/views/artists/_search.html.erb | 2 +- db/development_structure.sql | 109 ++++++++---------- .../20100205163027_create_post_histories.rb | 16 +++ .../20100205163027_create_post_versions.rb | 26 ----- test/unit/post_history_test.rb | 33 ++++++ 9 files changed, 157 insertions(+), 114 deletions(-) create mode 100644 app/models/post_history.rb delete mode 100644 app/models/post_version.rb create mode 100644 db/migrate/20100205163027_create_post_histories.rb delete mode 100644 db/migrate/20100205163027_create_post_versions.rb create mode 100644 test/unit/post_history_test.rb diff --git a/app/logical/date_tag.rb b/app/logical/date_tag.rb index a87ba00cf..c58e6f50c 100644 --- a/app/logical/date_tag.rb +++ b/app/logical/date_tag.rb @@ -34,7 +34,7 @@ class DateTag end def next_week - DateTag.new_from_range(1.week.since(start_date)), 1.week.since(end_date) + DateTag.new_from_range(1.week.since(start_date), 1.week.since(end_date)) end def previous_month @@ -42,7 +42,7 @@ class DateTag end def next_month - DateTag.new_from_range(1.month.since(start_date)), 1.month.since(end_date) + DateTag.new_from_range(1.month.since(start_date), 1.month.since(end_date)) end def date diff --git a/app/models/post.rb b/app/models/post.rb index fb662e038..39fb492a6 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -1,7 +1,7 @@ class Post < ActiveRecord::Base attr_accessor :old_tag_string, :old_parent_id after_destroy :delete_files - after_save :create_version + after_save :update_history after_save :update_parent_on_save before_save :merge_old_tags before_save :normalize_tags @@ -15,7 +15,7 @@ class Post < ActiveRecord::Base has_one :unapproval, :dependent => :destroy has_one :upload, :dependent => :destroy has_one :moderation_detail, :class_name => "PostModerationDetail", :dependent => :destroy - has_many :versions, :class_name => "PostVersion", :dependent => :destroy + has_one :history, :class_name => "PostHistory" has_many :votes, :class_name => "PostVote", :dependent => :destroy has_many :notes, :dependent => :destroy has_many :comments @@ -233,24 +233,21 @@ class Post < ActiveRecord::Base end end - module VersionMethods - def create_version - version = versions.create( - :source => source, - :rating => rating, - :tag_string => tag_string, - :updater_id => CurrentUser.user.id, - :updater_ip_addr => CurrentUser.ip_addr - ) - - raise PostVersion::Error.new(version.errors.full_messages.join("; ")) if version.errors.any? + module HistoryMethods + def revisions + if history.nil? + update_history + end + + history.revisions end - def revert_to!(version) - self.source = version.source - self.rating = version.rating - self.tag_string = version.tag_string - save! + def update_history + if history.nil? + create_history + end + + history << self end end @@ -788,7 +785,7 @@ class Post < ActiveRecord::Base include ImageMethods include ApprovalMethods include PresenterMethods - include VersionMethods + include HistoryMethods include TagMethods include FavoriteMethods include UploaderMethods diff --git a/app/models/post_history.rb b/app/models/post_history.rb new file mode 100644 index 000000000..2aac39eea --- /dev/null +++ b/app/models/post_history.rb @@ -0,0 +1,40 @@ +class PostHistory < ActiveRecord::Base + class Error < Exception ; end + + before_validation :initialize_revisions, :on => :create + belongs_to :post + + def self.build_revision_for_post(post) + hash = { + :source => post.source, + :rating => post.rating, + :tag_string => post.tag_string, + :parent_id => post.parent_id, + :user_id => CurrentUser.id, + :ip_addr => CurrentUser.ip_addr, + :updated_at => revision_time + } + end + + def self.revision_time + Time.now + end + + def initialize_revisions + write_attribute(:revisions, "[]") + end + + def revisions + if read_attribute(:revisions).blank? + [] + else + JSON.parse(read_attribute(:revisions)) + end + end + + def <<(post) + revision = self.class.build_revision_for_post(post) + write_attribute(:revisions, (revisions << revision).to_json) + save + end +end diff --git a/app/models/post_version.rb b/app/models/post_version.rb deleted file mode 100644 index 165d39c5b..000000000 --- a/app/models/post_version.rb +++ /dev/null @@ -1,6 +0,0 @@ -class PostVersion < ActiveRecord::Base - class Error < Exception ; end - - validates_presence_of :updater_id, :updater_ip_addr - belongs_to :post -end diff --git a/app/views/artists/_search.html.erb b/app/views/artists/_search.html.erb index 547332856..072712489 100644 --- a/app/views/artists/_search.html.erb +++ b/app/views/artists/_search.html.erb @@ -1,5 +1,5 @@
- <% form_tag(artists_path, :method => :get) do %> + <%= form_tag(artists_path, :method => :get) do %> <%= text_field_tag "name", params[:name], :size => 40 %> <%= submit_tag "Search" %> <% end %> diff --git a/db/development_structure.sql b/db/development_structure.sql index b60a926e9..a08ab56c0 100644 --- a/db/development_structure.sql +++ b/db/development_structure.sql @@ -1013,6 +1013,38 @@ CREATE SEQUENCE pools_id_seq ALTER SEQUENCE pools_id_seq OWNED BY pools.id; +-- +-- Name: post_histories; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- + +CREATE TABLE post_histories ( + id integer NOT NULL, + created_at timestamp without time zone, + updated_at timestamp without time zone, + post_id integer NOT NULL, + revisions text NOT NULL +); + + +-- +-- Name: post_histories_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE post_histories_id_seq + START WITH 1 + INCREMENT BY 1 + NO MAXVALUE + NO MINVALUE + CACHE 1; + + +-- +-- Name: post_histories_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE post_histories_id_seq OWNED BY post_histories.id; + + -- -- Name: post_moderation_details; Type: TABLE; Schema: public; Owner: -; Tablespace: -- @@ -1045,42 +1077,6 @@ CREATE SEQUENCE post_moderation_details_id_seq ALTER SEQUENCE post_moderation_details_id_seq OWNED BY post_moderation_details.id; --- --- Name: post_versions; Type: TABLE; Schema: public; Owner: -; Tablespace: --- - -CREATE TABLE post_versions ( - id integer NOT NULL, - created_at timestamp without time zone, - updated_at timestamp without time zone, - post_id integer NOT NULL, - source character varying(255), - rating character(1) DEFAULT 'q'::bpchar NOT NULL, - tag_string text NOT NULL, - updater_id integer NOT NULL, - updater_ip_addr inet NOT NULL -); - - --- --- Name: post_versions_id_seq; Type: SEQUENCE; Schema: public; Owner: - --- - -CREATE SEQUENCE post_versions_id_seq - START WITH 1 - INCREMENT BY 1 - NO MAXVALUE - NO MINVALUE - CACHE 1; - - --- --- Name: post_versions_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - --- - -ALTER SEQUENCE post_versions_id_seq OWNED BY post_versions.id; - - -- -- Name: post_votes; Type: TABLE; Schema: public; Owner: -; Tablespace: -- @@ -1812,14 +1808,14 @@ ALTER TABLE pools ALTER COLUMN id SET DEFAULT nextval('pools_id_seq'::regclass); -- Name: id; Type: DEFAULT; Schema: public; Owner: - -- -ALTER TABLE post_moderation_details ALTER COLUMN id SET DEFAULT nextval('post_moderation_details_id_seq'::regclass); +ALTER TABLE post_histories ALTER COLUMN id SET DEFAULT nextval('post_histories_id_seq'::regclass); -- -- Name: id; Type: DEFAULT; Schema: public; Owner: - -- -ALTER TABLE post_versions ALTER COLUMN id SET DEFAULT nextval('post_versions_id_seq'::regclass); +ALTER TABLE post_moderation_details ALTER COLUMN id SET DEFAULT nextval('post_moderation_details_id_seq'::regclass); -- @@ -2137,6 +2133,14 @@ ALTER TABLE ONLY pools ADD CONSTRAINT pools_pkey PRIMARY KEY (id); +-- +-- Name: post_histories_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: +-- + +ALTER TABLE ONLY post_histories + ADD CONSTRAINT post_histories_pkey PRIMARY KEY (id); + + -- -- Name: post_moderation_details_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: -- @@ -2145,14 +2149,6 @@ ALTER TABLE ONLY post_moderation_details ADD CONSTRAINT post_moderation_details_pkey PRIMARY KEY (id); --- --- Name: post_versions_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: --- - -ALTER TABLE ONLY post_versions - ADD CONSTRAINT post_versions_pkey PRIMARY KEY (id); - - -- -- Name: post_votes_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: -- @@ -2712,6 +2708,13 @@ CREATE INDEX index_pools_on_creator_id ON pools USING btree (creator_id); CREATE INDEX index_pools_on_name ON pools USING btree (name); +-- +-- Name: index_post_histories_on_post_id; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- + +CREATE INDEX index_post_histories_on_post_id ON post_histories USING btree (post_id); + + -- -- Name: index_post_moderation_details_on_post_id; Type: INDEX; Schema: public; Owner: -; Tablespace: -- @@ -2726,20 +2729,6 @@ CREATE INDEX index_post_moderation_details_on_post_id ON post_moderation_details CREATE INDEX index_post_moderation_details_on_user_id ON post_moderation_details USING btree (user_id); --- --- Name: index_post_versions_on_post_id; Type: INDEX; Schema: public; Owner: -; Tablespace: --- - -CREATE INDEX index_post_versions_on_post_id ON post_versions USING btree (post_id); - - --- --- Name: index_post_versions_on_updater_id; Type: INDEX; Schema: public; Owner: -; Tablespace: --- - -CREATE INDEX index_post_versions_on_updater_id ON post_versions USING btree (updater_id); - - -- -- Name: index_posts_on_created_at; Type: INDEX; Schema: public; Owner: -; Tablespace: -- diff --git a/db/migrate/20100205163027_create_post_histories.rb b/db/migrate/20100205163027_create_post_histories.rb new file mode 100644 index 000000000..6ed171978 --- /dev/null +++ b/db/migrate/20100205163027_create_post_histories.rb @@ -0,0 +1,16 @@ +class CreatePostHistories < ActiveRecord::Migration + def self.up + create_table :post_histories do |t| + t.timestamps + + t.column :post_id, :integer, :null => false + t.column :revisions, :text, :null => false + end + + add_index :post_histories, :post_id + end + + def self.down + drop_table :post_histories + end +end diff --git a/db/migrate/20100205163027_create_post_versions.rb b/db/migrate/20100205163027_create_post_versions.rb deleted file mode 100644 index 377316248..000000000 --- a/db/migrate/20100205163027_create_post_versions.rb +++ /dev/null @@ -1,26 +0,0 @@ -class CreatePostVersions < ActiveRecord::Migration - def self.up - create_table :post_versions do |t| - t.timestamps - - # Post - t.column :post_id, :integer, :null => false - - # Versioned - t.column :source, :string - t.column :rating, :character, :null => false, :default => 'q' - t.column :tag_string, :text, :null => false - - # Updater - t.column :updater_id, :integer, :null => false - t.column :updater_ip_addr, "inet", :null => false - end - - add_index :post_versions, :post_id - add_index :post_versions, :updater_id - end - - def self.down - drop_table :post_versions - end -end diff --git a/test/unit/post_history_test.rb b/test/unit/post_history_test.rb new file mode 100644 index 000000000..18c678aaf --- /dev/null +++ b/test/unit/post_history_test.rb @@ -0,0 +1,33 @@ +require_relative '../test_helper' + +class PostHistoryTest < ActiveSupport::TestCase + context "A post" do + 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 + + should "create a revision after creation" do + PostHistory.stubs(:revision_time).returns("TIME") + post = Factory.create(:post, :tag_string => "aaa bbb ccc") + assert_equal(1, post.revisions.size) + assert_equal({"source"=>nil, "rating"=>"q", "tag_string"=>"aaa bbb ccc", "parent_id"=>nil, "user_id"=>1, "ip_addr"=>"127.0.0.1", "updated_at"=>"TIME"}, post.revisions.last) + end + + should "create additional revisions after updating" do + PostHistory.stubs(:revision_time).returns("TIME") + post = Factory.create(:post, :tag_string => "aaa bbb ccc") + post.update_attributes(:tag_string => "bbb ccc ddd") + post.reload + assert_equal(2, post.revisions.size) + assert_equal({"source"=>nil, "rating"=>"q", "tag_string"=>"bbb ccc ddd", "parent_id"=>nil, "user_id"=>3, "ip_addr"=>"127.0.0.1", "updated_at"=>"TIME"}, post.revisions.last) + end + end +end