When serializing an object you often want to select which part of the object’s state should be serialized. To achieve this with Fuel you can selectively ig- nore instance variables.
Ignoring Instance Variables
Under certain conditions it may be desirable to prevent serialization of cer- tain instance variables for a given class. A straightforward way to do this is to override the hook methodfuelIgnoredInstanceVariableNames, at class side of the given class. It returns an array of instance variable names (as symbols) andallinstances of the class will be serialized without these instance variables.
For example, let’s say we have the classUserand we do not want to serialize the instance variables'accumulatedLogins'and'applications'. So we implement:
User class>>fuelIgnoredInstanceVariableNames ^ #('accumulatedLogins' 'applications')
Post-Materialization Action
When materialized, ignored instance variables will benil. To re-initialize and set values to those instance variables, send thefuelAfterMaterializa- tionmessage.
The messagefuelAfterMaterializationlets you execute some action once an object has been materialized. For example, let’s say we would like to set back the instance variable'accumulatedLogins'during materialization. We can implement:
10.4 Customizing the Graph
User>>fuelAfterMaterialization accumulatedLogins := 0.
Substitution on Serialization
Sometimes it is useful to serialize something different than the original ob- ject, without altering the object itself. Fuel proposes two different ways to do this: dynamically and statically.
Dynamically
You can establish a specific substitution for a particular serialization. Let’s illustrate with an example, where the graph includes aStreamand you want to serializenilinstead.
objectToSerialize := { 'hello' . '' writeStream}. 'demo.fuel' asFileReference writeStreamDo: [ :aStream |
aSerializer := FLSerializer newDefault. aSerializer analyzer
when: [ :object | object isStream ] substituteBy: [ :object | nil ]. aSerializer
serialize: objectToSerialize on: aStream binary ].
'demo.fuel' asFileReference readStreamDo: [ :aStream | materializedObject := (FLMaterializer newDefault
materializeFrom: aStream binary) root]
After executing this code,materializedObjectwill contain#('hello' nil), i.e. without the instance of aStream.
Statically
You can also do substitution for each serialization of an object by overrid- ing itsfuelAccept:method. Fuel visits each object in the graph by sending this message to determine how to trace and serialize it. The argument of the message is an instance of aFLMappersubclass.
As an example, imagine we want to replace an object directly with nil. In other words, we want to make all objects of a class transient, for example all
CachedResultinstances. For that, we should implement: CachedResult>>fuelAccept: aGeneralMapper
^ aGeneralMapper
visitSubstitution: self by: nil
Serializing Complex Objects with Fuel
As another example, we have aProxyclass and when serializing we want to serialize itstargetinstead of the proxy. So we redefinefuelAccept:as follows:
Proxy>>fuelAccept: aGeneralMapper ^ aGeneralMapper
visitSubstitution: self by: target
The use offuelAccept:also allows for deciding about serialization con- ditionally. For example, we have the classUserand we want tonilthe in- stance variablehistorywhen its size is greater than 100. A naive implemen- tation is as follows:
User>>fuelAccept: aGeneralMapper ^ self history size > 100
ifTrue: [
aGeneralMapper
visitSubstitution: self
by: (self copy history: #()) ].
ifFalse: [ super fuelAccept: aGeneralMapper ]
Note We are substituting the original user by another instance ofUser, which Fuel will visit with the samefuelAccept:method. Because of this we fall into an infinite sequence of substitutions!
UsingfuelAccept:we can easily fall into an infinite sequence of substitu- tions. To avoid this problem, the messagevisitSubstitution:by:onRe- cursionDo:should be used. In it, an alternative mapping is provided for the case of mapping an object which is already a substitute of another one. The example above should be written as follows:
User>>fuelAccept: aGeneralMapper aGeneralMapper
visitSubstitution: self by: (self copy history: #())
onRecursionDo: [ super fuelAccept: aGeneralMapper ]
In this case, the substituted user (i.e., the one with the empty history) will be visited via its super implementation.
Substitution on Materialization
In the same way that we may want to customize object serialization, we may want to customize object materialization. This can be done either by treat- ing an object as a globally obtained reference, or by hooking into instance creation.
10.4 Customizing the Graph
Global References
Suppose we have a special instance ofUserthat represents the admin user, and it is a unique instance in the image. In the case that the admin user is referenced in our graph, we want to get that object from a global when the graph is materialized. This can be achieved by modifying theserialization process as follows:
User>>fuelAccept: aGeneralMapper ^ self == User admin
ifTrue: [
aGeneralMapper
visitGlobalSend: self name: #User
selector: #admin ]
ifFalse: [ super fuelAccept: aGeneralMapper ]
During serialization the admin user won’t be serialized but instead its global name and selector are stored. Then, at materialization time, Fuel will send the messageadminto the classUser, and use the returned value as the ad- min user of the materialized graph.
Hooking into Instance Creation
Fuel provides two hook methods to customise how instances are created:
fuelNewandfuelNew:.
For (regular) fixed objects, the methodfuelNewis defined inBehavioras: fuelNew
^ self basicNew
But we can override it to our needs, for example: fuelNew
^ self uniqueInstance
This similarly applies to variable sized objects through the methodfuelNew:
which by default sendsbasicNew:.
Not Serializable Objects
You may want to make sure that some objects are not part of the graph dur- ing serialization. Fuel provides the hook method namedvisitNotSerial- izable:which signals anFLNotSerializableexception if such an object is found in the graph that is to be serialized.
MyNotSerializableObject>>fuelAccept: aGeneralMapper aGeneralMapper visitNotSerializable: self
Serializing Complex Objects with Fuel