Get a List of All ContainerControls in a Window

· 143 days ago

It 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 ago

I’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 ago

Quite 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 ago

A review of the Dakim Brain Fitness Unit; its GUI is written in REALbasic.

---

Create a Dictionary From a List of Pairs

· 450 days ago

The 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 ago

The 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 ago

Command-line-fu aims to be a repository for all those command-line gems.

---

MacLockButton

· 520 days ago

In 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 ago

Andy 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 ago

First-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]

---

Older