supporting has_many reification based on @lyfeyaj and @NullVoxPopuli 's works

This is based on @lyfeyaj and @NullVoxPopuli 's works. 
What I changed are mainly:
1. Reification for has_many through
2. Add tests and make sure all of them pass
3. Add option mark_for_destruction to mark the has_one/has_many associations that did not exist in the
reified version for destruction, instead of remove them.
4. Disable recursive reification of has_one/has_many associations. See the scenario below for more.
5. Some small tunings like using a hash to reduce the number of loop from n*m to n

### Some notes:

There is a limitation that the Reification will not work properly if the parent of a child can be changed.
Consider this scenario:
1. Create a customer A.
2. Create a customer B
3. Create an order for B.
3. Update A.
4. Change the customer of the order to A
5. Now reify the last version of A.  Ideally we would expect no orders in the reified A.

Recursive reification is not supported. Take the customer, order, and line item model for example, when a customer is reified, the line item is not reified.
Consider this scenario:
1. Create a customer
2. Create an order for the customer with a line item
3. Update the customer
4. Update the line item
5. Now reify the last version of the customer. If we support recursive reification, we would expect to see the line item before the updates in step 4.

There is an assumption in the code that the version_id is always incremental. In reality, it could possibly reach the upper limit of Integer and become 1 again. Thus there is a very rare edge case wherein the reification would not work properly. This is due to selecting 'MIN(version_id)' in the SQL.
I managed to put together the easy ones. need some more time tomorrow to
crack the harder one (the data persistent one)

On Thu, Dec 4, 2014 at 9:08 AM, Ben Atkins <notifications@github.com> wrote:

> In lib/paper_trail/version_concern.rb:
>
> >        model.class.reflect_on_all_associations(:has_one).each do |assoc|
> > -        child = model.send assoc.name
> > -        if child.respond_to? :version_at
> > -          # N.B. we use version of the child as it was `lookback` seconds before the parent was updated.
> > -          # Ideally we want the version of the child as it was just before the parent was updated...
> > -          # but until PaperTrail knows which updates are "together" (e.g. parent and child being
> > -          # updated on the same form), it's impossible to tell when the overall update started;
> > -          # and therefore impossible to know when "just before" was.
> > -          if (child_as_it_was = child.version_at(send(PaperTrail.timestamp_field) - lookback.seconds))
> > -            child_as_it_was.attributes.each do |k,v|
> > -              model.send(assoc.name).send :write_attribute, k.to_sym, v rescue nil
> > +        version = model.class.paper_trail_version_class.joins(:version_associations).
>
> That seems like a good idea. Do you want to take a crack at some of those
> changes? I'm happy to chip in where and however I can as well.
>
> —
> Reply to this email directly or view it on GitHub
> <https://github.com/airblade/paper_trail/pull/439/files#r21269387>.
>



-- 
*Folio* - Instant Clarity on Contracts
www.usefolio.com

*Kwela Solutions Pty. Ltd. *
607A, 250 Pitt St Sydney 2000
D: +61 2 8283 3868
M: +61 451 489 390
E: ben@kwelasolutions.com