Get a List of All ContainerControls in a Window
· 143 days agoIt has long annoyed REALbasic developers that the Window.Control method does not return references to ContainerControls embedded in a window. Here is a method that does so.
Function ContainerControls(extends w as Window) As ContainerControl()
dim theList() as ContainerControl
dim o as Runtime.ObjectIterator = Runtime.IterateObjects
while o.MoveNext
if o.Current isA ContainerControl and ContainerControl(o.Current).Window is w then
theList.Append ContainerControl(o.Current)
end if
wend
return theList
End Function
GZip Code for REALbasic
· 275 days agoI’ve added support for gzip to my zlib project. The project code is now available at my github page.
I intend to move all of my code to github as time permits to simplify maintenance, unless something trendier comes along.
Casting Booleans
· 371 days agoQuite often I need to convert a Boolean to an Integer for use with external functions or SQL. My solution has long been to extend the Boolean datatype.
Function IntegerValue(extends b as Boolean) as Integer
if b then
return 1
else
return 0
end if
End Function
The addition of the CType keyword to the REALbasic language provides an even easier solution.
CType(b, Integer)
returns 1, if b is true, or 0, if b is false.
Made With REALbasic
· 441 days agoA review of the Dakim Brain Fitness Unit; its GUI is written in REALbasic.
Create a Dictionary From a List of Pairs
· 450 days agoThe Pair class and a Pair operator crept silently into the REALbasic language in REALbasic 2008r2. A Pair is an immutable object consisting of a pair of Variants. You create a Pair object using the : operator.
dim p as Pair = 3:5
You retrieve the elements of a Pair using the (computed) properties Left as Variant and Right as Variant.
I am not sure why Real Software added Pairs to the language — perhaps it was a parting gift from Mars Saxman — but they are quite handy when one wants a lightweight way to group two objects together. For example, I’ve used them in a web server to queue pairs of HTTPRequest objects and the sockets on which they were received for later processing.
And now, just a year later, I’ve discovered that the Dictionary class gained a constructor at the same time.
Sub Constructor(paramarray entries() as Pair)
This allows you to create a Dictionary object as follows.
dim d as new Dictionary("foo":"bar", "x":"y")
For Dictionaries with a few items, I much prefer this to
dim d as new Dictionary
d.Value("foo") = "bar"
d.Value("x") = "y"
In the course of this, I also stumbled on a syntactic quirk: in a method declaration, both
paramarray entries() as Pair
and
paramarray entries as Pair
are legal. I’ve no idea whether this is intentional; the language reference entry for ParamArray is inconsistent on this point.
Comment [3]
Get the Boot Time of a Linux Machine
· 480 days agoThe boot time and uptime of a Linux computer are available from the /proc filesystem. /proc/stat returns, among other information, the boot time in seconds since the Unix epoch. /proc/uptime returns the number of seconds the system has been up.
Perhaps the easiest way to get this information is to execute the shell command cat and extract the time data from the output. Here is the output from a terminal session.
foo@bar:~$ cat /proc/uptime 5377546.89 4696377.57
The first time is the uptime of the system; the second is the time the machine has spent idle.
Here is some REALbasic code to get the time.
dim s as new Shell
s.Execute "cat /proc/uptime"
dim uptimeSeconds as Double = Val(NthField(s.Result, " " , 1))
dim uptime as new Date
uptime.TotalSeconds = uptime.TotalSeconds - uptimeSeconds
Now let’s refactor this by isolating the code that parses the shell output. This makes it easier to write unit tests for this bit of code, and to easily change the parsing code if, for instance, I learn how to use regular expressions.
Function ParseUptime(s as String) as String
return NthField(s, " ", 1)
End Function
Now the code above becomes this.
dim s as new Shell
s.Execute "cat /proc/uptime"
dim uptimeSeconds as Double = Val(ParseUptime(s.Result))
dim uptime as new Date
uptime.TotalSeconds = uptime.TotalSeconds - uptimeSeconds
Something is still missing — error checking. I expect /proc/uptime to be available, but I cannot guarantee it. I don’t know how to handle an error, because I don’t know how this code is to be used. So I raise a ShellError and let the caller deal with it.
dim uptime as Date
dim s as new Shell
s.Execute "cat /proc/uptime"
if s.ErrorCode = 0 then
dim uptimeSeconds as Double = Val(ParseUptime(s.Result))
uptime = new Date
uptime.TotalSeconds = uptime.TotalSeconds - uptimeSeconds
else
raise new ShellError(s)
end if
The error-checking logic is code that needs to be written for each invocation of Shell.Execute. That is to say, it will be duplicated code, and duplication should usually be refactored out.
The part of the code that varies is the code that parses the shell output. It already lives in a separate method; thus we can pass it as a delegate.
So let us first define a module ShellExtension. To it, add the following delegate declaration.
Delegate Function ShellOutputParser(s as String) As String
Next is a method that extends the Shell class.
Function Execute(extends s as Shell, command as String, outputParser as ShellOutputParser=nil) As String
if outputParser = nil then
outputParser = TrivialParser
end if
s.Execute command
if s.ErrorCode = 0 then
return outputParser.Invoke(s.Result)
else
raise new ShellError(s)
end if
End Function
The outputParser parameter has default value Nil. In this case, the method uses a trivial parsing function that returns the string passed to it, unchanged.
Private Function id(s as String) As String
return s
End Function
The function TrivialParser wraps the construction of the delegate, and returns it.
Private Function TrivialParser() As ShellOutputParser
return AddressOf id
End Function
I considered having this trivial parser trim trailing whitespace, but realized that was unnecessary, because I can simply pass the existing function RTrim.
Here is a final version of the code that gets the machine uptime.
dim uptime as new Date
dim s as new Shell
uptime.TotalSeconds = uptime.TotalSeconds - Val(s.Execute("cat /proc/uptime", AddressOf ParseUptime))
Finally, I was a little surprised that my code compiled, because the extension method overloads the existing method Shell.Execute, and uses a default parameter. My guess is that the presence of a return type means that the compiler finds no match when it first searches for a Shell method with the signature, then searches for extension methods and finds a match.
Were I feeling more cautious, I would change the name of the extension method. But it works now, and I like being able to use the same name as the existing method.
Command Line Fu
· 507 days agoCommand-line-fu aims to be a repository for all those command-line gems.
MacLockButton
· 520 days agoIn response to a question on the NUG, I knocked out a MacLockButton class. This class is intended to replicate the animated lock button used in the Cocoa SFAuthorizationView.
The project is available from github . It is tested with Mac OS 10.5.6; I would appreciate any feedback on experience with other systems.
Strings Can Be Nil, Even If They Can't Be Set To Nil
· 529 days agoAndy Dent has made available an open-source report-writing engine. Having written some simple report-writing functionality myself, I was interested to see how the professionals do it.
While reading his code, I saw a curious thing.
if aChar is nil or aChar.Len <> 1 then return False
Surely, I thought, he isn’t comparing a string to nil?
Indeed he is, and it appears that as of REALbasic 2008 r4, this is legal. It is, however, undocumented as far as I can tell; I found no mention of it in the release notes nor in the language reference for that version.
Some testing suggests that s is nil returns true if s = “”, and false otherwise. One still cannot assign Nil to a String reference, nor can one compare a String reference to Nil using =.
The ability to compare other String types (CFStringRef, CString, WString, PString) to Nil and to set references of these types was added to REALbasic several releases prior. Thus we can generate pathological results like the following.
dim s as String = ""
dim c as CString = s
Then (s is nil) = true, but (c is nil) = false.
I am not sure whether the ability to compare String references to Nil was an intentional change, or a by-product of some internal fix. Either way, this is a language feature I probably won’t use.
Comment [3]
REALbasic Shells Are Not Unix Shells
· 538 days agoFirst-time users of REALbasic Shell objects are often confused by an aspect of its behavior. Consider the following code.
dim s as new Shell
s.Execute "cd foo"
s.Execute "ls"
Print s.Result
If you are drawing on your experience using Unix shells, you probably expect the output to be the contents of foo. But it is not; instead, it will be the contents of some other directory. In my tests on Mac OS, for GUI applications, that directory is /, and for console applications it is ~.
This is not a bug; the Shell object is behaving as designed, and the design is correct. In fact, Shell is probably misnamed. A better name might be "Subprocess", like that of the corresponding object in Python. For this better describes the functionality of the Shell class.
Setting the Shell.Mode property determines the interaction of the subprocess with the parent: blocking or non-blocking, and whether or not it accepts input from the parent. But each invocation of Shell.Execute creates and executes a new subprocess.
Comment [2]