Closed clkao closed 8 years ago
Record is tricky, serialisation is fairly straightforward - but how do you find the record instance when deserialising?
I'm open to suggestions - perhaps the user provides a function which will receive the record name and has to return the record constructor?
My app makes heavy use of records, so I looked into this.
The biggest obstacle I found is that transit-js's writer API requires pairs of [Constructor, WriteHandler]
to figure out how to serialize various types. However, a record's constructor isn't actually Immutable.Record
- it's the Record
constructor created from Immutable.Record()
. E.g.:
var MyRecord = Immutable.Record({});
var instance = new MyRecord({}); // instance's constructor is `MyRecord`
Because of this, we can't simply add a pair like [Immutable.Record, transit.makeWriteHandler({...})]
to the writer, as transit won't be able to match instance
to Immutable.Record
.
Transit's solution for this was to add custom tags to objects. We could add some sort of prototype patcher to records to set instance.prototype.transitTag
to record
to do this, but that'd be an ugly bit of boilerplate.
The solution I went with was to create a reader/writer instance, like withPredicate
does, which is passed an array of record types:
var transit = require('transit-immutable-js');
var MyRecord = Immutable.Record({}, 'MyRecord');
var recordsTransit = transit.withRecords([MyRecord]);
The writer then creates a handler for each record type.
On the reader side, Record instances actually have an under-documented name field, which is set by the second argument passed to Immutable.Record
. Thus, as long as every record passed to withRecords()
has a name set on it, the writer can write the name alongside the values, and the reader can read the values.
I did an experimental implementation of this here.
The main issue I see with this implementation is, if you use it outside of the context of this library, you have to read the generated record map a little awkwardly, since it serializes into a map with name and values fields. I suppose this could be solved by using generated tags for each record type instead of a single record tag and a name field embedded in the value? I'm not experienced enough with transit to know best practices here.
Falling back to Maps when the records aren't set up is probably correct.
I quite like the idea of using the custom tag feature - but having withRecords
do the prototype modification.
The code itself looks broadly sensible.
Instead of toJS you should turn the values into a raw map - otherwise you can't put immutable types (including records) inside records
It would also be good to ensure withPredicate and withRecords can be chained together.
Guys, any plans of merging this into the master?
It's not currently high on my list, but I will happily accept a PR that makes the adjustments I mentioned above.
Records are now supported in version 0.5.0
thanks to @corbt !
thanks @corbt! sorry I didn't have time to follow-up after my initial stab at this, glad to see it land :)
It seems
Record
can't be serialized.