DEVONthink and ScriptingBridge Question

hi,

lately I’ve tried to get some exerience in scripting DEVONthink using the ScriptingBridge. This works overall very well. Only the “create a record with in” command crates headaches for me. The following code-snippet exemplifies the problem:



#!/usr/local/bin/macruby

framework  "Cocoa"
framework  "Foundation"
framework  "ScriptingBridge"

devon =  SBApplication.applicationWithBundleIdentifier("com.devon-technologies.thinkpro2")

resultRecord = devon.createLocation '/root/scripts/myscript', :in => '/'
rec = devon.classForScriptingClass("record").alloc.initWithProperties({'type'=>'text','plainText'=>"Hallo Welt!",'name' =>"helloworld"})
puts rec.description


#  Scripting Bridge does not actually create an object in the target application until you add the allocated and 
#  initialized object to an appropriate element array (SBElementArray), such as records in my example.

#  thats's the way it should be done, but its not working

puts devon.databases[0].records.insertObject(rec, :atIndex => 11)
puts devon.databases[0].records.addObject(rec)

rec.createRecordWithIn resultRecord


So the problem is that i don’t find an appropriate way to create a new instance of the DEVONthinkRecord class.

This is the last hurdle I’ve to overcome to finish an accompanying application which turns DEVONthink into a real Agent Application.

Any help is appreciated !

Thank you,

Thomas Fuerstner

I don’t know the syntax of Ruby but basically it should work this way:



#!/usr/local/bin/macruby

framework  "Cocoa"
framework  "Foundation"
framework  "ScriptingBridge"

devon =  SBApplication.applicationWithBundleIdentifier("com.devon-technologies.thinkpro2")

theGroup = devon.createLocation '/root/scripts/myscript'

theRecord = devon.createRecordWith {'type'=>'text','plain text'=>"Hallo Welt!",'name' =>"helloworld"}, :in => theGroup

No, the problem is somwhere else.

Obviously, the createRecordWithIn method is a method of DEVONthinkProRecord class and not of DEVONthinProApplication, like all the other commands. This somehow “confuses” the ScriptingBridge. Sadly, actually there is no workaround, beside using NSAppleScript methods to compile and execute the equivalent Applescript code directly from within MacRuby. Not an elegant solution but at least a solution.

No, all commands are methods of the application.

then there exists an interesting incompatibility with the ScriptingBridge. As the SciptingBridge is going to become more important for scripting Mac OS X it will make sense to further explore this topic:

The ScriptingBridge generates the following DEVONthink.h header file to describe the "scriptability of DEVONthink. It’s quite big, so just the two important codesequences for the DEVONthinkProApplication interface and the DEVONthinkProRecord interface will be listed. There it will get obvious that the method createRecordWithIn is not a method of the application itself, definitely.

// An application's top level scripting object.
@interface DEVONthinkProApplication : SBApplication
+ (DEVONthinkProApplication *) application;

- (SBElementArray *) documents;
- (SBElementArray *) windows;

@property (readonly) BOOL frontmost;  // Is this the frontmost (active) application?
@property (copy, readonly) NSString *name;  // The name of the application.
@property (copy, readonly) NSString *version;  // The version of the application.

- (DEVONthinkProDocument *) open:(NSURL *)x;  // Open an object.
- (void) print:(NSURL *)x printDialog:(BOOL)printDialog withProperties:(DEVONthinkProPrintSettings *)withProperties;  // Print an object.
- (void) quitSaving:(DEVONthinkProSavo)saving;  // Quit an application.
- (BOOL) addDownload:(NSString *)x automatic:(BOOL)automatic password:(NSString *)password referrer:(NSString *)referrer user:(NSString *)user;  // Add a URL to the download manager.
- (BOOL) backupDatabase:(DEVONthinkProDatabase *)database to:(NSString *)to includingFiles:(BOOL)includingFiles;  // Backup a DEVONthink Pro database.
- (NSArray *) classifyRecord:(DEVONthinkProRecord *)record;  // Get a list of classification proposals.
- (NSArray *) compareRecord:(DEVONthinkProRecord *)record;  // Get a list of similar contents.
- (DEVONthinkProRecord *) convertRecord:(DEVONthinkProRecord *)record in:(DEVONthinkProRecord *)in_ to:(DEVONthinkProCtyp)to;  // Convert a record to simple or rich text and create a new record afterwards.
- (NSString *) convertFeedToHTML:(NSString *)x baseURL:(NSString *)baseURL;  // Convert a RSS, RDF or Atom feed to HTML.
- (DEVONthinkProDatabase *) createDatabase:(NSString *)x;  // Create a new DEVONthink Pro database.
- (DEVONthinkProRecord *) createLocation:(NSString *)x in:(DEVONthinkProDatabase *)in_;  // Create a hierarchy of groups if necessary.
- (BOOL) createThumbnailFor:(DEVONthinkProRecord *)for_;  // Create image/movie thumbnail (and image info/characteristic) for a record.
- (DEVONthinkProRecord *) createWebDocumentFrom:(NSString *)x agent:(NSString *)agent in:(DEVONthinkProRecord *)in_ name:(NSString *)name password:(NSString *)password referrer:(NSString *)referrer user:(NSString *)user;  // Create a new record (picture, PDF or web archive) from a web resource.
- (BOOL) deleteRecord:(DEVONthinkProRecord *)record in:(DEVONthinkProRecord *)in_;  // Delete all instances of a record from the database or one instance from the specified group.
- (BOOL) deleteRowAt:(NSInteger)x;  // Remove specified row from current sheet.
- (BOOL) deleteThumbnailOf:(DEVONthinkProRecord *)of;  // Delete existing image/movie/web thumbnail of a record.
- (DEVONthinkProRecord *) displayAuthenticationDialog:(NSString *)x;  // Display a dialog to enter a username and its password.
- (DEVONthinkProRecord *) displayGroupSelector:(NSString *)x buttons:(NSArray *)buttons for:(DEVONthinkProDatabase *)for_;  // Display a dialog to select a (destination) group.
- (NSString *) downloadMarkupFrom:(NSString *)x agent:(NSString *)agent encoding:(NSString *)encoding password:(NSString *)password post:(DEVONthinkProRecord *)post referrer:(NSString *)referrer user:(NSString *)user;  // Download an HTML or XML page (including RSS, RDF or Atom feeds).
- (id) downloadURL:(NSString *)x agent:(NSString *)agent password:(NSString *)password post:(DEVONthinkProRecord *)post referrer:(NSString *)referrer user:(NSString *)user;  // Download a URL.
- (id) downloadWebArchiveFrom:(NSString *)x agent:(NSString *)agent password:(NSString *)password referrer:(NSString *)referrer user:(NSString *)user;  // Download a web archive (including subresources like images, scripts, stylesheets etc).
- (DEVONthinkProRecord *) duplicateRecord:(DEVONthinkProRecord *)record to:(DEVONthinkProRecord *)to;  // Duplicate a record.
- (BOOL) existsRecordAt:(NSString *)x in:(DEVONthinkProDatabase *)in_;  // Check if at least one record exists at the specified location.
- (BOOL) existsRecordWithComment:(NSString *)x in:(DEVONthinkProDatabase *)in_;  // Check if at least one record with the specified comment exists.
- (BOOL) existsRecordWithFile:(NSString *)x in:(DEVONthinkProDatabase *)in_;  // Check if at least one record with the specified last path component exists.
- (BOOL) existsRecordWithPath:(NSString *)x in:(DEVONthinkProDatabase *)in_;  // Check if at least one record with the specified path exists.
- (BOOL) existsRecordWithURL:(NSString *)x in:(DEVONthinkProDatabase *)in_;  // Check if at least one record with the specified URL exists.
- (NSString *) exportRecord:(DEVONthinkProRecord *)record to:(NSString *)to;  // Export a record (and its children).
- (NSArray *) getConcordanceOfRecord:(DEVONthinkProRecord *)record;  // Get list of words of a record sorted by weight.
- (DEVONthinkProDatabase *) getDatabaseWithId:(NSInteger)x;  // Get database with the specified id.
- (DEVONthinkProDatabase *) getDatabaseWithUuid:(NSString *)x;  // Get database with the specified uuid.
- (NSArray *) getEmbeddedImagesOf:(NSString *)x baseURL:(NSString *)baseURL type:(NSString *)type;  // Get the URLs of all embedded images of an HTML page.
- (NSArray *) getEmbeddedObjectsOf:(NSString *)x baseURL:(NSString *)baseURL type:(NSString *)type;  // Get the URLs of all embedded objects of an HTML page.
- (NSArray *) getEmbeddedSheetsAndScriptsOf:(NSString *)x baseURL:(NSString *)baseURL type:(NSString *)type;  // Get the URLs of all embedded style sheets and scripts of an HTML page.
- (NSArray *) getFramesOf:(NSString *)x baseURL:(NSString *)baseURL;  // Get the URLs of all frames of an HTML page.
- (NSArray *) getItemsOfFeed:(NSString *)x baseURL:(NSString *)baseURL;  // Get the items of a RSS, RDF or Atom feed. Dictionaries contain title (text), link (text), date (text), calendarDate (date), description (text), content (text), author (text), html (item converted to HTML)
- (NSArray *) getLinksOf:(NSString *)x baseURL:(NSString *)baseURL containing:(NSString *)containing type:(NSString *)type;  // Get the URLs of all links of an HTML page.
- (DEVONthinkProRecord *) getRecordAt:(NSString *)x in:(DEVONthinkProDatabase *)in_;  // Search for record at the specified location.
- (DEVONthinkProRecord *) getRecordWithId:(NSInteger)x in:(DEVONthinkProDatabase *)in_;  // Get record with the specified id.
- (DEVONthinkProRecord *) getRecordWithUuid:(NSString *)x in:(DEVONthinkProDatabase *)in_;  // Get record with the specified uuid.
- (DEVONthinkProText *) getRichTextOf:(NSString *)x baseURL:(NSString *)baseURL;  // Get the rich text of an HTML page.
- (NSString *) getTextOf:(NSString *)x;  // Get the text of an HTML page.
- (NSString *) getTitleOf:(NSString *)x;  // Get the title of an HTML page.
- (BOOL) hideProgressIndicator;  // Hide a visible progress indicator.
- (DEVONthinkProRecord *) import:(NSString *)x to:(DEVONthinkProRecord *)to type:(DEVONthinkProItyp)type unstyled:(BOOL)unstyled;  // Import a file or folder (including its subfolders).
- (DEVONthinkProRecord *) indicate:(NSString *)x to:(DEVONthinkProRecord *)to type:(DEVONthinkProItyp)type;  // Indicate ('index') a file or folder (including its subfolders). If no type is specified or the type is 'all', then links to unknown file types are created too.
- (BOOL) logMessage:(NSString *)x info:(NSString *)info;  // Log info for a file or action to the Log panel of DEVONthink Pro.
- (NSArray *) lookupRecordsWithComment:(NSString *)x in:(DEVONthinkProDatabase *)in_;  // Lookup records with specified comment.
- (NSArray *) lookupRecordsWithFile:(NSString *)x in:(DEVONthinkProDatabase *)in_;  // Lookup records whose last path component is the specified file.
- (NSArray *) lookupRecordsWithPath:(NSString *)x in:(DEVONthinkProDatabase *)in_;  // Lookup records with specified path.
- (NSArray *) lookupRecordsWithURL:(NSString *)x in:(DEVONthinkProDatabase *)in_;  // Lookup records with specified URL.
- (DEVONthinkProRecord *) moveRecord:(DEVONthinkProRecord *)record to:(DEVONthinkProRecord *)to from:(DEVONthinkProRecord *)from;  // Move all instances of a record or one instance from the specified group to a different group.
- (DEVONthinkProDatabase *) openDatabase:(NSString *)x;  // Open an existing DEVONthink Pro database.
- (DEVONthinkProThinkWindow *) openWindowForRecord:(DEVONthinkProRecord *)record;  // Open a (new) viewer or document window for the specified record (use the 'close' command to close a window).
- (BOOL) optimizeDatabase:(DEVONthinkProDatabase *)database;  // Backup & optimize a DEVONthink Pro database.
- (DEVONthinkProRecord *) pasteClipboardTo:(DEVONthinkProRecord *)to;  // Create a new record with the contents of the clipboard.
- (DEVONthinkProRecord *) replicateRecord:(DEVONthinkProRecord *)record to:(DEVONthinkProRecord *)to;  // Replicate a record.
- (NSArray *) search:(NSString *)x age:(double)age in:(DEVONthinkProRecord *)in_ label:(NSInteger)label locking:(BOOL)locking state:(BOOL)state unread:(BOOL)unread within:(DEVONthinkProLkwh)within;  // Search records by string, label, state, locking and/or age.
- (BOOL) showProgressIndicator:(NSString *)x cancelButton:(BOOL)cancelButton steps:(NSInteger)steps;  // Show a progress indicator or update an already visible indicator. You have to ensure that the indicator is hidden again via 'hide progress indicator' when the script ends or if an error occurs.
- (BOOL) stepProgressIndicator:(NSString *)x;  // Go to next step of a progress.
- (BOOL) synchronizeRecord:(DEVONthinkProRecord *)record;  // Synchronize a record (and its children) by importing/indexing updated external files.
- (BOOL) updateThumbnailOf:(DEVONthinkProRecord *)of;  // Update existing image/movie thumbnail (and image info/characteristic) of a record.
- (NSInteger) verifyDatabase:(DEVONthinkProDatabase *)database;  // Verify a DEVONthink Pro database. Returns total number of errors and missing files.
@end

As you can see the createRecordWithIn method is not listed as an application method. instead its listed here:


// A DEVONthink Pro database record.
@interface DEVONthinkProRecord : DEVONthinkProItem

- (SBElementArray *) children;
- (SBElementArray *) parents;

@property (copy, readonly) NSDate *additionDate;  // Date when the record was added to the database.
@property (copy) NSString *aliases;  // Wiki aliases (separated by commas or semicolons) of a record.
@property (copy) NSString *attachedScript;  // POSIX path of script attached to a record.
@property (copy) NSArray *cells;  // The cells of a sheet.
@property (readonly) NSInteger characterCount;  // The character count of a record.
@property (copy, readonly) NSArray *columns;  // The column names of a sheet.
@property (copy) NSString *comment;  // The comment of a record.
@property (copy) NSDate *creationDate;  // The creation date of a record.
@property (copy) id data;  // The file data of a record.
@property (copy) NSDate *date;  // The (creation/modification) date of a record.
@property (copy, readonly) NSArray *dimensions;  // The width and height of an image or PDF document in pixels.
@property (readonly) NSInteger dpi;  // The resultion of an image in dpi.
@property (copy, readonly) NSArray *duplicates;  // The duplicates of a record (only other instances, not including the record).
@property BOOL excludeFromClassification;  // Exclude group or record from classifying.
@property BOOL excludeFromSearch;  // Exclude group or record from searching.
@property BOOL excludeFromSeeAlso;  // Exclude record from see also.
@property BOOL excludeFromTagging;  // Exclude group from tagging.
@property (copy, readonly) NSString *filename;  // The proposed file name for a record.
@property (readonly) NSInteger height;  // The height of an image or PDF document in pixels.
- (NSInteger) id;  // The scripting identifier of a record. Optimizing or closing a database might modify this identifier.
@property (copy) id image;  // The image or PDF document of a record.
@property (readonly) BOOL indexed;  // Indexed or imported record.
@property double interval;  // Refresh interval of a feed. Currently overriden by preferences.
@property (copy, readonly) NSString *kind;  // The human readable kind of a record.
@property NSInteger label;  // Index of label (0-7) of a record.
@property (copy, readonly) NSString *location;  // The location in the database as a POSIX path (/ in names is replaced with \/).
@property BOOL locking;  // The locking of a record.
@property (copy, readonly) DEVONthinkProRecord *metaData;  // The meta data of a record as a dictionary containing key-value pairs.
@property (copy, readonly) NSString *MIMEType;  // The (proposed) MIME type of a record.
@property (copy) NSDate *modificationDate;  // The modification date of a record.
@property (copy) NSString *name;  // The name of a record.
@property (readonly) NSInteger numberOfDuplicates;  // The number of duplicates of a record.
@property (readonly) NSInteger numberOfReplicants;  // The number of replicants of a record.
@property (copy, readonly) NSDate *openingDate;  // Date when a content was opened the last time or when a feed was refreshed the last time.
@property (copy, readonly) NSString *path;  // The POSIX file path of a record.
@property (copy) NSString *plainText;  // The plain text of a record.
@property (copy) DEVONthinkProText *richText;  // The rich text of a record (see text suite).
@property (readonly) double score;  // The score of the last comparison, classification or search (value between 0.0 and 1.0) or undefined otherwise.
@property (readonly) NSInteger size;  // The size of a record in bytes.
@property (copy) NSString *source;  // The HTML or XML source of a record.
@property BOOL state;  // The state of a record.
@property BOOL stateVisibility;  // The visibility of the state of a record.
@property (copy, readonly) NSArray *tags;  // The tags of a record.
@property (copy) id thumbnail;  // The thumbnail of a record.
@property (readonly) DEVONthinkProDtyp type;  // The type of a record.
@property BOOL unread;  // The unread flag of a record.
@property (copy) NSString *URL;  // The URL of a record.
@property (copy, readonly) NSString *uuid;  // The unique and persistent identifier of a record for external referencing. Use URLs like x-devonthink-item://UUID to link to a record.
@property (readonly) NSInteger width;  // The width of an image or PDF document in pixels.
@property (readonly) NSInteger wordCount;  // The word count of a record.

- (DEVONthinkProRecord *) createRecordWithIn:(DEVONthinkProRecord *)in_;  // Create a new record.

@end

Right I can’t tell you why this happens but there is absolutely no doubt that for the ScriptingBridge createRecordWithIn is a method belonging to the DEVONthinkProRecord class.

I also used a F-Script code snippet to read out the DEVONthink classes and methods on the fly. this tells me the same.

Tell me how I could help you to do further investigations!

Freundlich gruesst aus wien,

Tom Fuerstner

I’ve just checked the AppleScript suite again and there’s no reason why this method should be part of DEVONthinkProRecord. All commands are declared the same way.

But are you able to create records after moving the definition from DEVONthinkProRecord to DEVONthinkProApplication?

Apologies to all, as I’m in way over my head here, but I wonder if this problem isn’t some strange effect caused by the fact that “with” and “in” (are?/can be?) reserved words in Applescript, and that the ScriptingBridge parser sees that “record” is a dt command word and then splits off “in” and “with” as as reserved words in some way that is strange and confuses the parsers/compilers in different ways.

I ask because it seems to me that there is something strange with the way that the “in_” particle dangles on the end of the declarations.

The repetition of “in” in the F-script definition seems strange to me:
( (DEVONthinkProRecord *) createRecordWithIn:(DEVONthinkProRecord *)in_; // Create a new record.)

Though I may be wrong about this, I can say that I struggled for a long time with python and appscript (rather than ScriptingBridge, which I don’t understand as well). To create a new group there, one issues the command:


newgroup=dt.create_record_with({k.type:k.group,k.name:the_time},in_=dt.databases[1].root)

But this syntax is FAR from obvious. It took me a long time to guess that the syntax shouldn’t be

dt.create_record(with_={xxx),in_=xxx)

nor is it:
dt.create_record_with({k.type=k.group,k.in=var})
nor
dt.create_record(k.with_={xxx),in_=xxx)
nor
dt.create_record_with_in({k.type=k.group},var})
(I think I tried all of these, but I would have never guessed it if the word in were actually repeated as it is in the F-Script definition above!)

I’m not going to sit here and rant about white space being a delimiter in applescript or anything like that. But am curious about how definitions are parsed and what words “belong” to the command name and what words are methods in these examples.

I also wonder if it may be an example of strange AE definition syntax parsing. I may be way off, but I do note that applescript uses “with” and “in” as reserved words for its comparison types (“starts with” “ends with” ) and its list evaluation (e.g. “is in”).

Since I am thinking about using scriptingbridge and F-Script myself, I am curious about this issue…is there any chance that dt would create the expected result with scripting bridge if the DT applescript command defintion had an alternative version that worked like this in standard applescript:

“create record from data {type:x,name:y,destination:x}”?
which I would imagine would translate in python as:
dt.create_record_from_data({k.type=k.group,k.destination=var})

Once again, sorry to all if the question is dumb; I just thought the guess might go in the right direction. But I’m going to be quick to admit that I only know bits of objective C and can only guess how scriptingbridge works, but I thought maybe raising the question of reserved words and parsing might be helpful in figuring this problem out, and I do want to say I too, think it is probably important to figure out for the long run.

best,

erico

perhaps this is relevant: http://lists.apple.com/archives/Applescript-implementors/2006/Oct/msg00021.html?

or this http://developer.apple.com/technotes/tn2002/tn2106.html

Possible but IMO unlikely. “in” is used by several commands and the full name of several commands contains “with”, e.g.“create record with” or “get record with id”. “create database” isn’t affected either although “database” is a term like “record”.

The scripting bridge definition is actually correct, it’s “only” in the wrong class.