applescript elements, classes, and objects - collections

I'm writing an applescript to automate some tedious work in Aperture, and I'm having a hell of a time understanding some of the peculiarities of the language. In particular, I'm having a really difficult time dealing with collections (lists and elements). I'm stumped by object specifiers that include collections and applying commands to elements in a collection. Moreover, I'm confused by strange differences in object specifiers that seem to behave differently when referring to object elements by their class within a class versus, well, let me show you.
Here's the beginning of a script.
prop Movies : {}
tell Application "Aperture"
set Movies to every image version whose name of every keywords contains "Movie"
end tell
length of Movies -- result => 601
The application object in Aperture contains an elements collection of image versions. image version objects contain an elements collection of keywords. keywords are objects/lists with a name and id property.
So the global script property Movies now contains all of the image version objects in my Aperture library that are actually videos. Now, when I try to do this:
repeat with movie in Movies
log ("Movie name is '" & name of movie & "'.")
log (" the class of this object is '" & class of movie & "'.")
end
the output, as expected, is:
Movie name is 'MOV03510.MPG'.
the class of this object is 'image version'.
Movie name is 'MOV00945'.
the class of this object is 'image version'.
Movie name is 'MOV03228.MPG'.
the class of this object is 'image version'.
Movie name is 'MOV02448'.
the class of this object is 'image version'.
...
However, I'm stuck with how to access an elements collection within those image versions. When I do this:
repeat with movie in Movies
log ("Movie name is '" & name of movie & "'.")
log (" the class of this object is '" & class of movie & "'.")
set kwnamelist to name of every keyword in movie
if kwnamelist contains "Movie"
log (" Yes, '" & name of movie & "' is indeed a video.")
end if
end
gives me
find_videos.applescript:1089:1096: script error: Expected class name but found identifier. (-2741)
The error, to me, sounds like applescript is confused by the object specifier name of every keyword in movie. BUT, the reason I'm so confused about this is that if I write this code:
tell Application "Aperture"
repeat with imageind from 1 to 1000
set img to item imageind of image versions
tell me to log ("Image name is '" & name of img & "'.")
tell me to log (" the class of this object is '" & class of img & "'.")
set kwnamelist to name of every keyword in img
if kwnamelist contains "Movie"
tell me to log (" '" & name of img & "' is actually a video!")
end if
end
end tell
then I get the expected output:
...
Image name is 'DSC_4650'.
the class of this object is 'image version'.
Image name is '104-0487_IMG'.
the class of this object is 'image version'.
Image name is 'MOV02978.MPG'.
the class of this object is 'image version'.
'MOV02978.MPG' is actually a video!
Image name is '108-0833_IMG'.
the class of this object is 'image version'.
...
Can anyone tell me what's wrong with my object specifier? Why is it that I can essentially apply get name to every keyword in img when the image version is in one context, but I can't in a different context? Is there something I'm missing here? Is it that the keyword class is internal to the Aperture application object? How would I specify something like application::keyword if that's the case?
UPDATE:
I've solved this particular problem, but I'd really appreciate it if someone could explain exactly why this solves it. I did this:
tell Application "Aperture"
repeat with movie in Movies
tell me to log ("Movie name is '" & name of movie & "'.")
tell me to log (" the class of this object is '" & class of movie & "'.")
set kwnamelist to name of every keyword in movie
if kwnamelist contains "Movie"
tell me to log (" Yes, '" & name of movie & "' is indeed a video.")
end if
end
end tell
gives the expected output:
...
Movie name is 'IMG_0359'.
the class of this object is 'image version'.
Yes, 'IMG_0359' is indeed a video.
Movie name is 'MOV02921.MPG'.
the class of this object is 'image version'.
Yes, 'MOV02921.MPG' is indeed a video.
Movie name is 'MOV02249'.
the class of this object is 'image version'.
Yes, 'MOV02249' is indeed a video.
...
It seems like there's a very peculiar scope issue at work here. Could someone explain to me how keyword objects are in scope in this new version, but out of scope in the previous version where we weren't in a tell block? I thought that tell blocks were just for directing commands without direct parameters? Do they determine type scope as well? Or is there a command hidden somewhere here in the construction/execution of the object specifier that depends on getting sent to the Application "Aperture" object?

If I understand the core of your question correctly - because you have solved the practical part, screwy dereferencing, which is a bit of an old chestnut for applescripters - you must only use terminology inside the relevant tell blocks, so if your application (Aperture) supplies the terminology "keyword", you can't refer to "keyword" unless you are inside a tell application "Aperture" block.
OR if for some reason you want to use terminology from a given app without using a tell block, you can wrap your code like this:
using terms from application "Aperture"
-- blah blah about 'keyword' and other Aperture terminology
end using terms from

Applescript is rather funny like this unfortunately and often times it's hit or miss. In this case repeat with item in list, item is a reference to the contents, not the contents itself. Often times AppleScript will dereference this for you, but not always. Typically I always use the repeat with x from 1 to count method to avoid this problem.

About scripting Aperture keywords
Keyword scripting in Aperture 3 is quite unintuitive. Here's what Ive learned:
A keyword's id is a string with the path from the keyword name up to its top-level parent, with each parent's name preceded by a tab.
The value of the parents property is just the id without the name and, if the keyword is not at the top-level, the tab following the name.
Keywords don't compare equal even if their values are the same; in fact, keyword 1 of animage != keyword 1 of anImage.
An image may have multiple keywords with the same name if they have different parents. However, the name will only appear once wherever the image's metadata is displayed.
The way to add a keyword to anImage with name aName and parents theParents is tell anImage to make new keyword with properties {name:aName, parents:theParents, id:aName&tab&theParents}
See my entry here for a fuller explanation plus add and remove handlers.

Related

Get value node in XML with vbscript ASP

i'm trying to get the node value in an XML Response. I'm very new to ASP.
Here is the XML:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"/>
<soap:Body>
<ns2:getTextoTsjResponse xmlns:ns2="http://x.com/x/act" xmlns:ns3="http://i.e.com" xmlns:ns4="http://comun.e.com">
<return>
<ns3:texto>
<ns3:datos>
<xop:Include href="cid:888a-4ad6-a511-9c6f0490590e-398#entidad.com" xmlns:xop="http://www.w3.org/2004/08/xop/include"/>
</ns3:datos>
<ns3:extension>pdf</ns3:extension>
</ns3:texto>
<ns3:textoAnonimizado>
<ns3:datos>
<xop:Include href="cid:888a-4ad6-a511-9c6f0490590e-399#entidad.com" xmlns:xop="http://www.w3.org/2004/08/xop/include"/>
</ns3:datos>
<ns3:extension>pdf</ns3:extension>
</ns3:textoAnonimizado>
</return>
</ns2:getTextoTsjResponse>
</soap:Body>
</soap:Envelope>
I succeded geting the XMLDoc and parse. What im trying to do then is get href values within node.
Here is the ASP code.
'Response from server with the XML
xmlParseado = response.Text
Set xmlDoc = Server.CreateObject("MSXML2.DOMDocument")
xmlDoc.loadXML(xmlParseado)
if xmlDoc.parseError.errorcode <> 0 then
Response.Write("XML Error...<br>")
else
Call xmlDoc.setProperty("SelectionLanguage", "XPath")
Call xmlDoc.setProperty("SelectionNamespaces", "xmlns:ns3")
Dim node
'Here im trying to get href value from <ns3:datos> node.
Set node = xmlDoc.selectSingleNode("//ns3:texto//ns3:datos//*")
If (node Is Nothing) Then
Response.write "nothing"
Else
response.write(TypeName(node) & "<br />")
End If
end if
Some help would be apreciated.
Thanks in advance
You are really close.
Sadly your XML (like many poorly designed XML documents) suffers from namespace overkill. If you think of elements like data files on a file system, these XML designers are paranoid that two files (elements) will have the same name, so they put every file (element) on a separate disk instead of just using folders (nesting elements) for context. In my entire career, I've never run into an issue with two files having the same name, but every day, there's a post on Stackoverflow from someone having trouble with namespaces.
So think of namespaces like a separate disk in a filesystem. The document specifies each "disk" with a nick name (the prefix like ns3), and a location (the URI like http://i.e.com). Now when you're querying XML with XPath, have to tell the parser about all of the namespaces (disks), and you specify the prefix in front of every element on the "disk". You're XPath will look like "//ns3:datos", not just "//datos".
So armed with namespaces, they became the "solution" for everything. When SOAP was created, the designers went crazy with namespaces, and as a result, everyone put namespaces in everything. The designers of SOAP must have been worried that their Header, Envelope and Body element names would be re-used in SOAP message data. Instead of naming the elements "SOAP_Header", "SOAP_Envelope", and "SOAP_Body" that everyone could remember, they implemented SOAP with XML namespaces just in case the message content accidentally also included an element named "Header", "Envelope" or "Body".
When you set the SelectionNamesSpaces, you need to set both the prefix and the URI. In this example I have all the namespaces in your document.
Good luck with your project, and please join me in killing namespaces where ever possible :)
if xmlDoc.parseError.errorcode <> 0 then
Response.Write("XML Error...<br>")
' NOTE: Avoid nesting else statements, just stop.
Response.End
End If
Call xmlDoc.setProperty("SelectionLanguage", "XPath")
' NOTE: you're missing the URI here in your namespace http://i.e.com
' Call xmlDoc.setProperty("SelectionNamespaces", "xmlns:ns3")
' Add all of the namespaces, including the prefix and URI
Call xmlDoc.setProperty("SelectionNamespaces", "xmlns:xop='http://www.w3.org/2004/08/xop/include' xmlns:ns2='http://x.com/x/act' xmlns:ns3='http://i.e.com' xmlns:ns4='http://comun.e.com'")
'Here im trying to get href value from <ns3:datos> node.
Dim node
Set node = xmlDoc.selectSingleNode("//ns3:texto//ns3:datos//xop:Include")
If (node Is Nothing) Then
Response.write "nothing"
Else
Response.write( node.getAttribute( "href" ) )
End If
' Here's how to get all of the Includes
Dim ndList
set ndList = xmlDoc.selectNodes( "//xop:Include" )
for each ndInclude in ndList
Response.write( ndInclude.getAttribute( "href" ) )
next

How can I verify a field is required using Geb?

I am writing an automated test using Spock/Geb and I want to verify that a field is actually required.
Here is the code:
Code for Client Name and Client ID fields which are both required
Here is what I have tried to write that did NOT successfully check to make sure the two fields are actually required:
Assertions
Does anyone have any idea what my code should look like?
Here is what the actual page looks like - I am trying to verify the red asterisk:
enter image description here
Update: The following code worked:
assert clientConfigPage.modalClientNameTextBox.getAttribute("required")
You want to use Navigator.hasClass(String className). This will return a boolean whether the element has a class by the given name. In your example you use .getAttribute("class") == "req" but the class attribute is "required" so you must check for that string literal.

Robot framework, AndroidLibrary: How to get Locator value to access an view for keyword "Set Text Locator Value"?

I am trying to enter a value to the edit View using keyword "Set Text". But I do not understand what to put for argument "Locator"?
"locator" is a term borrowed from selenium, and describes a string that identifies an object in the UI. For a web based app a locator might be an object id, an xpath, a css path, etc. For AndroidLibrary, the locator should be either an integer or the string "num=<int>" (eg: num=3), or "name=<string>" for a named field (eg: name=username).

Check if an Object exists in VBScript

I'm maintaining a Classic ASP app written in VB Script by an outside company long, long ago.
I have an array of imagefile paths, like so:
dim banners, arrKeys, i
set banners=CreateObject("Scripting.Dictionary")
banners.Add "banner1.jpg", "http://www.somelink.com"
banners.Add "banner2.jpg", "http://www.somelink.com"
banners.Add "banner3.jpg", "http://www.somelink.com"
This will exist ONLY on pages that have banner ads. There is some standard code that iterates through this list in an include file (common to all pages).
If Not banners Is Nothing then
' then loop through the Dictionary and make a list of image links
End if
The problem is that if banners is not instantiated on the page (it's not on all pages), I get a Can't find object error
What's the proper way to check if an object exists in VB Script?
#Atømix: Replace
If Not banners Is Nothing then
and use
If IsObject(banners) Then
Your other code you can then place into an include file and use it at the top of your pages to avoid unnecessary duplication.
#Cheran S: I tested my snippets above with Option Explicit on/off and didn't encounter errors for either version, regardless of whether Dim banners was there or not. :-)
IsObject could work, but IsEmpty might be a better option - it is specifically intended to check if a variable exists or has been initialised.
To summarize:
IsEmpty(var) will test if a variable exists (without Object Explicit), or is initialised
IsNull(var) will test if a variable has been assigned to Null
var Is Nothing will test if a variable has been Set to Nothing, but will throw an error if you try it on something that isn't an object
IsObject(var) will test if a variable is an object (and will apparently still return False if var is Empty).
If a variable is declared, but not initialized, its value will be Empty, which you can check for with the IsEmpty() function:
Dim banners
If IsEmpty(banners) Then
Response.Write "Yes"
Else
Response.Write "No"
End If
' Should result in "Yes" being written
banners will only be equal to Nothing if you explicitly assign it that value with Set banners = Nothing.
You will have problems, though, with this technique if you have Option Explicit turned on (which is the recommendation, but isn't always the case). In that case, if banners hasn't been Dimed and you try to test IsEmpty(banners), you will get a runtime error. If you don't have Option Explicit on, you shouldn't have any problems.
edit: I just saw this related question and answer which might help, too.
Somewhat related is IsMissing() to test if an optional parameter was passed, in this case an object, like this:
Sub FooBar(Optional oDoc As Object)
'if parameter is missing then simulate it
If IsMissing(oDoc) Then Dim oDoc as Object: oDoc = something
...
You need to have at least dim banners on every page.
Don't you have a head.asp or something included on every page?
Neither of IsEmpty, Is Object, IsNull work with the "Option Explicit" Setting, as stealthyninja above has misleadingly answered.
The single way i know is to 'hack' the 'Option Explicit' with the 'On Error Resume Next' setting, as Tristan Havelick nicely does it here:
Is there any way to check to see if a VBScript function is defined?

Converting Plain Text to Clickable link or Link to PlainText in asp.net

I need your advice with converting plain text to an URL.
The scenario will be this: The user will select some entry and then click a "convert to link" button.
The entry text the user selected will convert to (link: selected_text). I do it with JavaScript. And after that, when he clicks the Save button to save all his entry, I don't know how to store (link: selected_text) in tha database.
The URL will be like this: www.mysite.aspx?t=selected_text.
I can convert (link: selected_text) by using replace function in code-behind. But then I don't know how to show user as clickable and also by not showing <a href="www.mysite.aspx?t=selected_text">
It can be difficult to understand therefore I will show some of my codes to explain.
Private Sub Save(ByVal Entry As String) ' Entry Comes from entry textbox '
Dim elected As String
selected = Entry.Replace("(link: ", "<a href http://www.mysite.com?link=")
selected = Entry.Replace(")", ">")
' then here starts save but not necessary to show '
End Sub
If you must save processed input for some reason
(link: here)
must be converted to
(link: here)
To store in database, you'll have to track the changes separately somehow and post them back to the server. I'd suggest a HiddenInput control.
Do not save it as www.mysite.com?t=here. Just save the entry as the user types it. While showing it to user later, convert the "(link: here)" to link and show that.
Save the post as the user wrote it. This will make it easier to allow editing of the post later. When you render the message you should use a regular expression to replace it with a real link. You should never replace all ")" with ">". What happends if i write "hello (world)"?
The result:
Hello (world>
You can find great regular expressions here:
http://regexlib.com

Resources