clearwater-rb / grand_central

State-management and action-dispatching for Ruby apps
23 stars 3 forks source link

Make actions and models serializable #4

Open jgaskins opened 8 years ago

jgaskins commented 8 years ago

Given that current state is a reduction of the list of all actions performed on the initial state, making actions serializable could allow a store to send bug reports automatically. To do so, it could hold onto the initial state and action history, and when an exception is raised, it could be told to send a report to Bugsnag or another error-tracking service or even back to the app's home server.

Serializing actions would also make it possible to run the store and an entire Clearwater app on a web worker. The worker could take serialized actions from the UI thread using postMessage (which is not exposed in Bowser … yet), then the Clearwater app would generate the new vdom tree, diff against the previous tree, then pass the resulting patch back to the UI thread to make the changes to the DOM.

To make this possible, we would need to be able to convert arbitrary subclasses of GrandCentral::Action and GrandCentral::Model to and from native JS objects. In Opal, the conventions for this are Klass#to_n ("to native") and Klass.from_native(js_object).

For both classes, we control object initialization, so we know what gets passed in and what it's called. For example, with the following action:

Foo = GrandCentral::Action.with_attributes(:foo, :bar)

Then Foo.new(123, 'abc') can be serialized into { foo: 123, bar: "abc" }. We would have to have some additional metadata to be able to turn it back into a Foo action, though. Maybe { foo: 123, bar: "abc", __grand_central: { class: "Foo" } }? Once it's in the form of a native JS object, it's easily stringified to be able to send across the wire.

Same goes for a GrandCentral::Model:

class Bar < Model
  attributes(:id, :baz, :quux)
end

Bar.new(
  id: 123,
  baz: "abc",
  quux: "xyz",
).to_n
# {
#   id: 123,
#   baz: "abc",
#   quux: "xyz",
#   __grand_central: {
#     class: "Bar",
#   },
# }

I don't know what the CPU-time cost of all this is, but especially across a web worker it's probably significant; serializing large object graphs would mean recreating them on the other end (workers don't share memory with the UI thread). Either way, it'd be a good idea to measure.