Note how, in the Picture model, the scope we pass to acts_as_list is an array of the two columns that make up the polymorphic association. If we were to follow our template from TodoItem and do it this way:
acts_as_list in a polymorphic scope (the WRONG way)
Everything would run correctly, but imageables with the same ID and different types would have some problems. Specifically, determining which pictures are first and last:
Bugs caused by using acts_as_list in a polymorphic scope the WRONG way
123456789101112131415161718192021222324
classPicture<ActiveRecord::Basebelongs_to:imageable,polymorphic:trueacts_as_listscope::imageableendclassEmployee<ActiveRecord::Basehas_many:pictures,as::imageableendclassProduct<ActiveRecord::Basehas_many:pictures,as::imageableendemployee1=Employee.create# employee1 is an Employee with id=1employee2=Employee.create# employee2 is an Employee with id=2product1=Product.create# product1 is a Product with id=1employee1.pictures.create# Create a new picture on employee 1employee2.pictures.create# Create a new picture on employee 2product1.pictures.create# Create a new picture on product 1putsemployee1.pictures[0].last?# false, unexpectedputsemployee2.pictures[0].last?# trueputsproduct1.pictures[0].last?# true
Since each imageable has only one Picture, last? (an acts_as_list method that checks if the item is the last one in scope) should return true for all three. But, the first line says false. Since we’re only telling acts_as_list to scope on :imageable (which it converts to imageable_id), it has no way of telling between the Employee with an id of 1 and the Product with an id of 1. So, it thinks that Employee 1’s picture belongs to the same record as Product 1’s picture, and thus, thinks that only Product 1’s picture is last?.
If you tell acts_as_list the full story, it won’t get confused:
Correct results from using acts_as_list in a polymorphic scope the RIGHT way
123456789101112131415161718192021222324
classPicture<ActiveRecord::Basebelongs_to:imageable,polymorphic:trueacts_as_listscope:[:imageable_id,:imageable_type]endclassEmployee<ActiveRecord::Basehas_many:pictures,as::imageableendclassProduct<ActiveRecord::Basehas_many:pictures,as::imageableendemployee1=Employee.create# employee1 is an Employee with id=1employee2=Employee.create# employee2 is an Employee with id=2product1=Product.create# product1 is a Product with id=1employee1.pictures.create# Create a new picture on employee 1employee2.pictures.create# Create a new picture on employee 2product1.pictures.create# Create a new picture on product 1putsemployee1.pictures[0].last?# trueputsemployee2.pictures[0].last?# trueputsproduct1.pictures[0].last?# true
Now that acts_as_list knows to look at both the imageable_id and the imageable_type, it can tell the difference between Employee 1 and Product 1, and returns true for all three last?s, as expected.