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:
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.
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
andGrandCentral::Model
to and from native JS objects. In Opal, the conventions for this areKlass#to_n
("to native") andKlass.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:
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 aFoo
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
: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.