ruby-hyperloop / hyper-mesh

The project has moved to Hyperstack!! - Synchronization of active record models across multiple clients using Pusher, ActionCable, or Polling
https://hyperstack.org/
MIT License
22 stars 12 forks source link

BREAKING CHANGE: rewrite aggregations (composed_of) #80

Open catmando opened 6 years ago

catmando commented 6 years ago

The following will introduce a big breaking change to the semantics of composed_of (aggregations) on the client side.

   # assume address is a composed of object in User
  some_user.address = Address.new
  some_user.address.city = "Philly" 
  some_user.address.save
  ... later reload some_user from server
  some_user.address.city # ? pre edge city == "Philly", on edge will now == nil!!!!

Do this instead

  address = Address.new
  address.city = "Philly" 
  address.save.then { some_user.address = address }

Currently, aggregations are overly complicated, tests are failing, and implementation is very incompatible with synchromesh, and strongly typed dummy values. It is also completely incompatible with AR semantics.

The key is that AR does not allow updating of aggregate (composed_of) values. This has been true since at least rails 4. Perhaps it was not so in Rails 3 when hyper-mesh (reactive-record) was first developed. https://github.com/rails/rails/blob/24027162dbe226acfbf3a91872237a9557764d72/activerecord/lib/active_record/aggregations.rb#L101

This is even true if the composed_of class is an AR model. If you save the composed of item as in user.address.save this will indeed save the address, but user.address will not be updated to the new address.

for example:

  it "even AR models (that are aggregates) are immutable" do
    user = User.new
    address = user.address
    address.save
    address.reload
    user.save
    user.reload
    expect(user.address).not_to eq(address) #!!!!
    user.address = address
    user.save
    user.reload
    expect(user.address).to eq(address)
  end

composed_of creates a special attribute that works like any other attribute with the exception that when you write to that attribute, the result "flows through" to other attributes by using the mapping on the composed_of macro.

actually we can just reuse most of the AR's code: https://github.com/rails/rails/blob/6c5bbb4b7d3bdd1b43e512fb6ae764c373c7827b/activerecord/lib/active_record/aggregations.rb#L224

The only problem is that when we receive updates to the base attributes, we have to be smart and reinitialize the composed of object.

How to do this will require a little thinking ...