Online Book Reader

Home Category

AppleScript_ The Definitive Guide - Matt Neuburg [251]

By Root 1656 0
arises immediately, illustrating why it's so hard to get started with Cocoa scripting.

In good object-oriented programming, objects are assigned appropriate tasks. Some objects are just data ("model"); other objects control that data ("controller"). In my application, a Person in MyObject's persons array is just data; it is MyObject that should be responsible for creating and validating a Person. But key-value coding slams into your existing application like a sudden side wind, ignoring your architecture and surprising your code. When the user tries to change the name of an existing person, it is Person's setPersonName: that is called, even though it is MyObject that should decide whether the new name is valid. Accordingly, I've given Person a master instance variable pointing at its creator, which in this case is MyObject; when the user asks to change a person's name, the request is shuttled off to MyObject, which will decide the suitability of the requested change and comply if appropriate.

But it gets worse. We have an additional problem when the user says make new person, because at that moment the scriptability framework creates a Person object by calling alloc and init directly on our Person class; any designated initializer is ignored, and MyObject doesn't have a chance to perform initializations or pass judgment. There is no easy way to prevent this (such as saying to the framework, "When you want to create a Person, call such-and-such a method"). Furthermore, if the user's command also says with properties {name:"whatever"}, setPersonName: is called to set the new name. This puts our code in a quandary; there is no master, so there is no one to judge the suitability of the new name.

Fortunately, if the user is creating this person (a condition for which we can test, as the code demonstrates), the scriptability framework will send the resulting Person object to MyObject anyway, for insertion into the persons collection. So we set the name as requested, because MyObject will eventually get a chance to pass judgment on this proposed new person—and initialize it properly.

Now let's talk about the category on MyObject. Here's the first part of it:

@implementation MyObject (MNscriptability)

- (BOOL)application:(NSApplication *)sender delegateHandlesKey:(NSString *)key {

if ([key isEqualToString: @"personsArray"]) return YES;

return NO;

}

- (unsigned int)countOfPersonsArray {

return [persons count];

}

- (Person *)objectInPersonsArrayAtIndex:(unsigned int)i {

return [persons objectAtIndex: i];

}

First, we have our same old application:delegateHandlesKey: method. Next, we've implemented our own access to the persons array through the Cocoa key "personsArray"; we will report its size and return an object in it, so that the scriptability framework never gets its hands on the array directly.

Here's more of the MyObject category:

- (BOOL) canGivePerson:(Person*)p name:(NSString*)name {

if (!name || [name isEqualToString:@""]) {

[self returnError:errOSACantAssign

string:@"Can't give person empty name."];

return NO;

}

if ([self existsPersonWithName: name]) {

[self returnError:errOSACantAssign

string:@"Can't give person same name as existing person."];

return NO;

}

return YES;

}

- (void) scripterWantsToChangeName:(NSString*)n of:(Person*)p {

if ([n isEqualToString: [p name]]) return; // nothing to do

if (![self canGivePerson:p name:n]) return;

[p setName: n];

}

- (void)insertObject:(Person *)p inPersonsArrayAtIndex:(unsigned int)index {

if (![self canGivePerson:p name:[p name]]) return;

[p setMaster: self];

[persons insertObject:p atIndex:index];

}

- (void)insertInPersonsArray:(Person *)p {

if (![self canGivePerson:p name:[p name]]) return;

[p setMaster: self];

[persons addObject:p];

}

First we have a general name-checking routine. If the user wants to assign a person a name, either as part of creating that person or altering the name of an existing person, we report an error to AppleScript if the name is the empty string or matches that of an existing person. Then we have the

Return Main Page Previous Page Next Page

®Online Book Reader