Hi Scott,
When copying objects in Smalltalk you may also need to copy some component objects to ensure the copy has a sensible structure. This is particularly important when copying objects persisted with ReStore (in some cases ReStore will complain about invalid structures) however the principle is the same as for non-persistent objects.
As an example I'll use the classes from the "SSW ReStore Examples" package (see the package comment for more details). Starting with an empty database let's first create a Customer with an Address:
Customer new
firstName: 'John';
surname: 'Smith';
address: (Address new line1: '123 Oxford Street'; yourself);
store.
You should now have a database containing one Customer and one Address:
Customer storedInstances size. "1"
Address storedInstances size. "1"
Now let's try to copy the customer and persist the copy:
customer := Customer storedInstances first.
copy := customer copy.
copy firstName: 'James'; store.
At this point you should receive the error "attempt to assign collection to > 1 persistent object". This is because the default implementation of copy will just return a shallowCopy, so the copy has the exact same 'orders' collection as the original. Sharing persistent collections isn't valid in ReStore so the copy needs its own collection - we do this by adding an implementation of postCopy to Customer:
postCopy
super postCopy.
self orders: OrderedCollection new
We should now be able to create and persist a new copy of the customer:
copy := customer copy.
copy firstName: 'James'; store.
However note that although we've copied the customer the address has not been copied:
Customer storedInstances size. "2"
Address storedInstances size. "1"
The single persistent Address is shared by both customers (it's valid to share persistent non-collection objects in ReStore). It may be that this would be a valid situation in some databases, however for a database of customers lets assume we want each customer to have its own independent address, so we should also copy the address as part of the customer's postCopy method:
postCopy
super postCopy.
self orders: OrderedCollection new.
self address: self address copy
If we now create and persist an additional copy we will get both a new customer and a new address:
copy := customer copy.
copy firstName: 'Jack'; store.
Customer storedInstances size. "3"
Address storedInstances size. "2"
Just to reiterate, the important thing is to implement postCopy methods in your persistent classes so that the resulting copies represent a valid structure according to the requirements of your data model.
Hope this helps,
John Aspinall