• No results found

Customizing the Graph

In document Enterprise Pharo a Web Perspective (Page 152-156)

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

In document Enterprise Pharo a Web Perspective (Page 152-156)