Commit 73c400bbb76b8d7e7f02783fc4aa6b3165575008

Authored by Dhruv Kapadia
1 parent 47e920d6

Refactoring skip and vote to use polymorphism in appearance

app/models/appearance.rb
... ... @@ -3,12 +3,9 @@ class Appearance < ActiveRecord::Base
3 3 belongs_to :prompt
4 4 belongs_to :question
5 5  
6   - #technically, an appearance should either one vote or one skip, not one of both objects, but these declarations provide some useful helper methods
7   - # we could refactor this to use rails polymorphism, but currently the foreign key is stored in the vote and skip object
8   - has_one :vote
9   - has_one :skip
  6 + belongs_to :answerable, :polymorphic => true
10 7  
11 8 def answered?
12   - vote || skip
  9 + !self.answerable_id.nil?
13 10 end
14 11 end
... ...
app/models/skip.rb
... ... @@ -2,5 +2,5 @@ class Skip < ActiveRecord::Base
2 2 belongs_to :skipper, :class_name => "Visitor", :foreign_key => "skipper_id"
3 3 belongs_to :question
4 4 belongs_to :prompt
5   - belongs_to :appearance
  5 + has_one :appearance, :as => :answerable
6 6 end
... ...
app/models/user.rb
... ... @@ -41,8 +41,7 @@ class User < ActiveRecord::Base
41 41 end
42 42  
43 43 def record_appearance(visitor, prompt)
44   - a = Appearance.create(:voter => visitor, :prompt => prompt, :question_id => prompt.question_id,
45   - :lookup => Digest::MD5.hexdigest(rand(10000000000).to_s + visitor.id.to_s + prompt.id.to_s) )
  44 + a = Appearance.create(:voter => visitor, :prompt => prompt, :question_id => prompt.question_id, :site_id => self.id, :lookup => Digest::MD5.hexdigest(rand(10000000000).to_s + visitor.id.to_s + prompt.id.to_s) )
46 45 end
47 46  
48 47  
... ...
app/models/visitor.rb
... ... @@ -25,7 +25,7 @@ class Visitor < ActiveRecord::Base
25 25 if options[:appearance_lookup]
26 26 @appearance = prompt.appearances.find_by_lookup(options.delete(:appearance_lookup))
27 27 return nil unless @appearance # don't allow people to fake appearance lookups
28   - options.merge!(:appearance_id => @appearance.id)
  28 + options.merge!(:appearance => @appearance)
29 29 end
30 30  
31 31 choice = prompt.choices[ordinality] #we need to guarantee that the choices are in the right order (by position)
... ... @@ -45,7 +45,7 @@ class Visitor < ActiveRecord::Base
45 45 if options[:appearance_lookup]
46 46 @appearance = prompt.appearances.find_by_lookup(options.delete(:appearance_lookup))
47 47 return nil unless @appearance
48   - options.merge!(:appearance_id => @appearance.id)
  48 + options.merge!(:appearance => @appearance)
49 49 end
50 50  
51 51 options.merge!(:question_id => prompt.question_id, :prompt_id => prompt.id, :skipper_id => self.id)
... ...
app/models/vote.rb
... ... @@ -11,7 +11,7 @@ class Vote < ActiveRecord::Base
11 11 belongs_to :prompt, :counter_cache => true
12 12 belongs_to :choice, :counter_cache => true
13 13 belongs_to :loser_choice, :class_name => "Choice", :foreign_key => "loser_choice_id"
14   - belongs_to :appearance
  14 + has_one :appearance, :as => :answerable
15 15  
16 16 named_scope :recent, lambda { |*args| {:conditions => ["created_at > ?", (args.first || Date.today.beginning_of_day)]} }
17 17 named_scope :with_question, lambda { |*args| {:conditions => {:question_id => args.first }} }
... ...
db/migrate/20100714160406_add_polymorphic_answerable_to_appearance.rb 0 → 100644
... ... @@ -0,0 +1,11 @@
  1 +class AddPolymorphicAnswerableToAppearance < ActiveRecord::Migration
  2 + def self.up
  3 + add_column :appearances, :answerable_id, :integer
  4 + add_column :appearances, :answerable_type, :string
  5 + end
  6 +
  7 + def self.down
  8 + remove_column :appearances, :answerable_id
  9 + remove_column :appearances, :answerable_type
  10 + end
  11 +end
... ...
db/schema.rb
... ... @@ -9,7 +9,7 @@
9 9 #
10 10 # It's strongly recommended to check this file into your version control system.
11 11  
12   -ActiveRecord::Schema.define(:version => 20100621164536) do
  12 +ActiveRecord::Schema.define(:version => 20100714160406) do
13 13  
14 14 create_table "appearances", :force => true do |t|
15 15 t.integer "voter_id"
... ... @@ -19,6 +19,8 @@ ActiveRecord::Schema.define(:version =&gt; 20100621164536) do
19 19 t.string "lookup"
20 20 t.datetime "created_at"
21 21 t.datetime "updated_at"
  22 + t.integer "answerable_id"
  23 + t.string "answerable_type"
22 24 end
23 25  
24 26 add_index "appearances", ["lookup"], :name => "index_appearances_on_lookup"
... ...
lib/tasks/prune_db.rake
... ... @@ -62,5 +62,20 @@ namespace :prune_db do
62 62 end
63 63 end
64 64 end
  65 +
  66 + task(:move_vote_and_skip_ids_to_appearance => :environment) do
  67 + Vote.find_each do |v|
  68 + @appearance = Appearance.find(v.appearance_id)
  69 + @appearance.answerable = v
  70 + @appearance.save
  71 +
  72 + end
  73 + Skip.find_each do |s|
  74 + @appearance = Appearance.find(s.appearance_id)
  75 + @appearance.answerable = s
  76 + @appearance.save
  77 + end
  78 + end
  79 +
65 80  
66 81 end
... ...
spec/models/appearance_spec.rb
... ... @@ -7,8 +7,7 @@ describe Appearance do
7 7 it {should belong_to :voter}
8 8 it {should belong_to :prompt}
9 9 it {should belong_to :question}
10   - it {should have_one :vote}
11   - it {should have_one :skip}
  10 + it {should belong_to :answerable}
12 11  
13 12 it "should create a new instance given valid attributes" do
14 13 Appearance.create!(@valid_attributes)
... ...
spec/models/skip_spec.rb
... ... @@ -3,6 +3,7 @@ require File.expand_path(File.dirname(__FILE__) + &#39;/../spec_helper&#39;)
3 3 describe Skip do
4 4 it {should belong_to :prompt}
5 5 it {should belong_to :skipper}
  6 + it {should have_one :appearance}
6 7 before(:each) do
7 8 @valid_attributes = {
8 9  
... ...
spec/models/vote_spec.rb
... ... @@ -6,7 +6,7 @@ describe Vote do
6 6 it {should belong_to :prompt}
7 7 it {should belong_to :choice}
8 8 it {should belong_to :loser_choice}
9   - it {should belong_to :appearance}
  9 + it {should have_one :appearance}
10 10  
11 11 before(:each) do
12 12 @question = Factory.create(:aoi_question)
... ...