The mid-career course correction is now public and underway, and I’m feeling
pretty good in that brief interval between relief at what I can leave behind
and panicked conviction that I’ll soon be living in a cardboard box.
Rubber Duck [UX] Debugging - Or, you could also train your significant other to recognize when they can just nod, say “hmm” now and then and think about other things, as I do.
My app design workflow - I was most struck by how few Photoshop features I was even aware of, let alone able to use.
The Problem with Rails Callbacks - Callbacks are the path to the dark side. Callbacks lead to coupling. Coupling leads to anger. Anger leads to hate. Hate leads to suffering, and so on (h/t @steveklabnik).
Framer - A JavaScript-based alternative (‽) to Quartz Composer.
Xcode 4’s Less Obvious Shortcuts - Assertion: nearly every program would be better if the UX/UI effort spent on editing were instead spent on navigating.
Sandi Metz’ rules for developers - A winning combination: rules that will improve 95% of all Rails code, plus a clear guideline for when I get to pull the ripcord and do my own thing, since I always have to.
SketchMine - A repository of files for Sketch.app, so you can see how it’s done.
Signs that you’re a bad programmer - The feel-not-terrible aspect of this is that I at least recognized that I have some of these going on before reading the article. I’m scared of the equivalent one for ops.
Why I Develop For The Mac - It’s possible I just lack the native-side depth of perspective, but I think I have it for web and I find it a wonder anything works at all over here.
Giddily excited that Briefs is finally out in the light of day.
Your Body Does Not Want to Be an Interface - Uses the important philosophical distinction between “ready-to-hand” and “present-to-hand” to explain why using your eyes to move a mouse pointer or throwing your head back to activate Google Glass just sucks.
Five Obstacles To Simplicity In Mac App Design - Rob Rhyne on the design challenges in shipping Briefs. (You will be wanting the free trial while you go to whatever length is necessary to free up $200 and buy the full version. Go ahead; I’ll wait).
It’s one of those pull-quotes-from-the-same-article-series kinds of day. (Seriously, just go read the whole damned thing).
The Ethics of Code - “When people don’t see the big picture, or when they think they’re only responsible for the thing that’s right in front of them, it’s easy for many individuals to be complicit in the creation of damaging things.”
Healers & Dealers - “There is an ancient pact between tools and their users which says that tools should be used by their users, and not the other way around. Good tools should help their users accomplish a task by satisfying some pre-existing urge and then getting out of the way. Attention economies, at their most addictive, violate this pact.”
A Staging Ground for the Future - “Soon, through the Internet, we are likely to fulfill the ancient Buddhist idea of experiencing the suffering of all living things.”
WWDC excitement is certainly tinged with more bittersweetness than last year’s.
Auto Layout Performance on iOS - Everything costs something, including–it turns out–asking your phone to solve middling-sized simultaneous sets of linear equations to position stuff.1
Downfalls of Distributed Startups - Astute, and unlikely to be less important by this time next year, or the year after (and so on).
Usability checklist - A useful last-minute punch list for common usability goofs.
Common Misconceptions About Touch - TL;DR: Minimum tap target is 8mm (not 44 “points”) and shouldn’t be closer together than 10mm on centre, minimum readable point size is bigger on a tablet because they’re usually farther from your eyes, and the touch target can and usually should be bigger than the visual target. (You should still read this, though).
To paraphrase Boss Wolf from Kung Fu Panda 2: “Is layout code part of ‘everything?’“ “Uh, yeah?” “Then it COSTS SOMETHING!” (What can I say… having a four-year-old involves a great many repeated viewings of the Kung Fu Panda oeuvre).↩
You should be testing your code. As should you be flossing. My prolific friend
Rob Rix of
RXCollections fame is seeking to
give you one less excuse on the testing front, anyway. His theory: writing
tests inline with the code under test reduces drag and encourages one to write
more tests.1
His Lagrangian test framework (L3 for short) brings inline tests
to Objective-C.2
Using some clever preprocessor hacks, the inline tests are stripped out of
production builds. Set the -DDEBUG=1 compiler flag, and some even more
clever preprocessor hacks will turn the test macros into a web of test objects
and blocks, lying latent in the binary. Shine a black light on it by handing
the binary to the test runner, and you’ll know if this specific binary is
passing its tests.
We have no separate test classes (that you generated, anyway), no separate
test files, and no separate test bundle.3
We’ll walk through testing “Hello World,” so you can get the flavour of how to
get started.4 You’ll need Mac OS X 10.8, Xcode and git.5
Here are the steps we’ll follow from the shell:
Clone the sample project and build the executable.
Add inline tests.
Add the L3 framework.
Show a test failure.
Get to green.
1. Clone the sample project and build the executable.
Our test application is a Foundation command-line tool that looks like this:
Ok, so. Not the most technically-challenging program for modern hardware. Start
by cloning the sample project from Github, building an executable and
running it:
(Note that this checks out the start branch. Switch to the master
branch to see what the project should look like when we’re done).6
2. Add inline tests.
We’ll need to define at least (a) a single suite, and (b) a single test
within that suite. These are defined at the top, outside of any function or
method. We’ll also need to add (c) a hook for the test runner within the
top-level autorelease pool.
For religious reasons, the test we’ve added must first fail, then
we’ll fix it.
#import <Foundation/Foundation.h>
@l3_suite("main"); // (a) suite declaration
@l3_test("this test should succeed") { // (b) test case
l3_assert(YES, l3_equals(NO));
}
int main(int argc, const char * argv[])
{
@autoreleasepool {
l3_main(argc, argv); // (c) test runner hook
NSLog(@"Hello, Lagrangian");
}
return 0;
}
Now we’ve broken make. Let’s fix it.
3. Add the L3 framework.
We need to tell the compiler about @l3_suite(), @l3_test() and l3_main()
syntax by including a header file, and linking against the L3 library. It can
currently be built as an iOS static framework, OS X dylib or OS X framework. I
prefer the OS X framework for our purposes: it includes the headers and it’s
easy for the compiler to find them.
Add this line to main.m below the Foundation include:
#import <Lagrangian/Lagrangian.h>
You can either build the framework from the L3 source and
copy it into the project folder, or pull a prebuilt one from the project repo:
$ curl -L -O https://github.com/rgm/hello-lagrangian/raw/master/Lagrangian.tgz && tar zxf Lagrangian.tgz
Edit the executable target in Makefile to add the framework and enable DEBUG:
Now make and run the executable. If all went well, you should see the same
log output as step 1.
We haven’t actually run the tests yet. But, because we set the DEBUG flag,
they’re in the executable, lying in wait. Note that the Lagrangian framework
gets weak-linked; none of the test code will be run nor the framework loaded
unless asked to by the test runner.
4. Show a test failure.
To see test results, we’ll run our executable within the test runner. Things
are a little different with a full Cocoa app (ie. one that passes off to
NSApplicationMain()), and hopefully a future tutorial will show that.
Like the library, you can either build the test runner from the L3
source and copy it into the project folder, or pull a prebuilt
one from the project repo:
$ curl -L -O https://github.com/rgm/hello-lagrangian/raw/master/lagrangian-test-runner.tgz && tar zxf lagrangian-test-runner.tgz
Add a test target to Makefile. This tells the test runner where to find the
L3 library and executes it, passing it the command-line invocation needed to
run our executable:
And now, run the test target. Note that we set a DYLD environment variable to
tell the dynamic linker where to nab L3 from:
% make test
DYLD_FRAMEWORK_PATH=. ./lagrangian-test-runner -command hello
Test Suite 'hello_lagrangian' started at 2013-03-03 22:58:54 +0000
Test Suite 'main' started at 2013-03-03 22:58:54 +0000
Test Case '-[main this_test_should_succeed]' started.
main.m:7: error: -[main this_test_should_succeed] : 'YES' was '1' but should have matched 'l3_equals(NO)'
Test Case '-[main this_test_should_succeed]' failed (0.000 seconds).
Test Suite 'main' finished at 2013-03-03 22:58:54 +0000.
Executed 1 test, with 1 failure (0 unexpected) in 0.000 (0.003) seconds
Test Suite 'hello_lagrangian' finished at 2013-03-03 22:58:54 +0000.
Executed 1 test, with 1 failure (0 unexpected) in 0.000 (0.007) seconds
If all went well, you’ll see a log of the test run with our (expected) failure.
5. Get to green.
And now, make the test pass by changing the assertion in main.m from
l3_assert(YES, l3_equals(NO));
to
l3_assert(YES, l3_equals(YES));
Back at the shell, rebuild the executable and start a test run:
% make test
clang -fobjc-arc -F . -framework Foundation -framework Lagrangian -o hello -DDEBUG=1 main.m
DYLD_FRAMEWORK_PATH=. ./lagrangian-test-runner -command hello
Test Suite 'hello_lagrangian' started at 2013-03-03 23:04:31 +0000
Test Suite 'main' started at 2013-03-03 23:04:31 +0000
Test Case '-[main this_test_should_succeed]' started.
Test Case '-[main this_test_should_succeed]' passed (0.000 seconds).
Test Suite 'main' finished at 2013-03-03 23:04:31 +0000.
Executed 1 test, with 0 failures (0 unexpected) in 0.000 (0.003) seconds
Test Suite 'hello_lagrangian' finished at 2013-03-03 23:04:31 +0000.
Executed 1 test, with 0 failures (0 unexpected) in 0.000 (0.007) seconds
And with that–assuming you agree there’s not much to refactor here–we’ve
completed one full TDD cycle using Lagrangian.
I hope this has piqued your interest to learn more. As of this writing, L3 is
barely four months old yet it’s already achieved that neat test-framework
crane-building-tail-swallowing trick, and so the best place for learning more
about using L3 is to look over the extensive self-tests in its own
source.
(Things can change fast. If you find a broken step, I’d appreciate it if you’d let me know).
(Kind of like keeping the floss in the washroom, I suppose).↩
If, like me, you washed out of physics before getting to Lagrangian mechanics, the name is a reference to something I don’t actually understand, but that I am for the time being taking on faith is a rich and appropriate metaphor. ↩
Well, sort-of true. Yes, there’s a octest bundle target in Xcode, but that’s mainly to trick Xcode’s test machinery. This project won’t have one.↩
Note that you wouldn’t normally do all this at the shell. I’m reducing the number of moving parts for learning purposes. Lagrangian has extensive Xcode integration, and Rob is performing yeoman’s work in keeping it working. Frustrating and mounting evidence suggests that Apple may not rely on its own unit-testing tools as much as one would hope.↩
You could make this work with older versions of OS X or even Linux / GNUStep. For convenience, the project uses binaries pre-built against 10.8 using Xcode 4.6. You could build your own. The real prerequisite is a relatively recent version of clang, since L3 makes heavy use of blocks and ARC. ↩
I have a foot in both the building design and software development fields, so I tend to run into DHH’s subject matter–the fruits of Christopher Alexander’s impressive thought processes–pretty often.
I’m starting to think that the overzealous embrace of some but not all of the ideas behind design patterns by a field and the subsequent reaction against their predictable misuse is becoming itself a design anti-pattern.2
A woefully inadequate summary of my sample size of one
Formal design patterns–the first time around, in building design–were big in the late 1970s. I went to architecture school after that, in the heyday of deconstructivism. Mainstream architectural fashion had pretty much rejected patterns as a useful design tool by then.3
Ben Regnier–a fellow foot-in-both-camper–sums up the architectural counter-reformation thinking well. Something essential to being the sort of person who becomes a designer also leads a lot of us to see ourselves as Heroic Shapers of Stuff. So there’s something offensive about the existence of what is frequently mistaken for a paint-by-numbers approach:
Anybody passionate about their profession will bristle at the idea that what they do can be broken down into a simple set of guidelines, and this reaction is based largely on the truth that the whole is immensely more complicated than the sum of the parts. For all of these reasons, A Pattern Language seems doomed to a status as one of the many cul-de-sacs of architectural theory.
So what?
At core, I think we’re talking about the powerful concept of extracting ideas that have survived contact with reality into a conveniently reusable form. It all starts by applying this self-evidently useful approach. And it all seems to end in a world where one can make a living not making stuff, but by harassing those who do as a given field’s equivalent of the professional feng shui consultant.
I don’t know why that is. I don’t know how to make us not do that anymore. But I do:
think most of what goes wrong comes when one drifts too far to the formalist side of the humanist-formalist axis;
suspect there’s something pretty mathematicky-General-Relativity-Lisp-level truth-of-the-universe capital-T-True lurking in Alexander’s ideas; and
know neither you nor I actually understand those ideas, so let’s go reread them.
Title shamefully stolen from Ben Regnier’s experience of bringing up design patterns while their influence was on the wane. I find it to have the ring of truth.↩
Here’s some guidance in getting Vectorworks to talk to MySQL on
a Mac. Brace yourself: this will probably hurt a bit, and involve more
than a few acronyms.
Should I care?
Getting and setting key-value data is basic to any interesting
idea in using a computer to help manage the firehose of data that is
an architectural project. Vectorworks has had the ability to assign
key-value data to any object since forever, using Records.
But getting at this data is finicky.
Worse–like so much of Vectorworks–it’s very one-document-one-database.
We run teams of six architects on some projects, and have to split up our
drawings into multiple files. I’ve tried all sorts of horrible
workgroup-reference hacks involving worksheets or layer referencing to
collect this data–examples are sheet numbers, and door & room finish
scheduling information–with varying degrees of success, workflow
compromise and hair-pulling.
It would be better for Vectorworks to store the data in an external
project database. Any document in the project can get and set to the
external database. Other processes (web servers, cron jobs, Ruby/Python
scripts, you name it) can also get and set to the database without
having to resort to brittle Rube-Goldbergesque automations of the
Vectorworks UI using Applescript and/or Vectorscript.
Since version 16 (aka Vectorworks 2011), Vectorworks has had the ability
to talk to such external databases through ODBC. I’m sure ODBC is lovely
if you’re on Windows, where it originated. But it’s a horrible bag of
hurt on a Mac.
ODBwhat?
ODBC is a shim standard that’s supposed to abstract away any need for
application developers to care about the idiosyncracies of talking to
any particular database server.1 If your database (Filemaker, MySQL,
PostgreSQL, Excel, whatever) has an ODBC driver, then–in
theory–Vectorworks can talk to it through the shim.2
Here’s my superficial, as-much-as-I-needed-to-know understanding of how
the moving parts are communicating:
Vectorworks is hard-wired to talk to your Mac’s built-in ODBC
manager. On Mac OS 10.6 and 10.7, this is iODBC and gets
installed as part of the OS.
Your Mac’s ODBC manager is hard-wired to go looking for its
configuration files in /Library/ODBC/ and ~/Library/ODBC/, to
figure out what data sources are available and how to get at them.
A data source is, more or less, the name of a driver saying what
database server program to use, the name of the database file itself,
and the necessary addresses and credentials for finding and connecting
to it.
The ODBC manager translates Vectorworks’s requests to get and set
data from generic ODBCese into MySQLese on the fly, using the driver
we told it about in Step 2.
The ODBC driver pushes the request and pulls the response through
a socket or TCP port to the running MySQL process. In our demo, we’ll
be using a MySQL server running on our local machine. Using a TCP port
would mean that the MySQL server could be anywhere on your local network
or the internet.
These points are numbered in red on this diagram; we’ll need to build
the parts shown in yellow:
Getting down to work
Here are the steps we’ll follow, finishing with Vectorworks being able
to connect to our database. Once we’re there, you should be able to
start using Nemetschek’s documentation and play around with
the connection on your own.
Go install Xcode and Homebrew
Set up the database server and an ODBC driver
Set up a test database
Try a test query through iodbctest
Connect from within Vectorworks to the MySQL data source
1. Go install Xcode and Homebrew
You’ll need a working C/C++ compiler. Macs don’t have them installed by
default, but Xcode from the Mac App Store is just a few clicks
and a hefty download away.
Homebrew is system that takes much of the pain away from installing
Unix stuff (like MySQL) on your Mac. Highly recommended. You’ll need it
to keep the steps below from devolving into a terrible, uninteresting
discussion about how to compile programs from scratch.
2. Set up the database server and an ODBC driver
Open /Applications/Utilities/Terminal.app and type in this line below:
brew install mysql --universal
Follow the instructions you see on screen for getting the MySQL server process up and listening on your machine.
Next, type:
brew install mysql-connector-odbc --universal
This installs the ODBC driver.
Everything is living under the directory /usr/local, nicely isolated from the rest of the system. Make sure to add --universal to each line. If not, you’ll get strange-looking errors when you go to connect in Vectorworks.3
3. Set up a test database
I’ve put up a project at Github to help you set up a test database and the ODBC configuration. In Terminal.app, cd yourself to ~/Desktop or wherever you’d like to keep this, and type each line below:
git clone git@github.com:rgm/vectorworks-mysql.git
cd vectorworks-mysql
script/bootstrap
You’ll see some log output. What just happened:
There is now a sample database in MySQL with the same name as your Mac OS X username.
That database has a couple of tables of sample data in it, so we have something to see when this all works.
Configuration files got copied to ~/Library/ODBC so that Vectorworks knows that we’re using MySQL.
In those configuration files, we set up a data source named mysql-vw-test for Vectorworks to look at, pointed at the sample database.
Longer-term, you’ll probably want a tool like Navicat to poke around the database, unless you really like command line interfaces.
4. Try a test query through iodbctest
This step is optional, but it does demonstrate how something that isn’t Vectorworks can be talking to the database at the same time. iodbctest is a command-line utility to test that the drivers are working correctly.
Again, from Terminal.app, type:
iodbctest
What follows is a transcript of what it should look like, if everything is working. You’ll need to type the ?, the DSN=mysql-vw-test, and the select * from sheets; when you see the prompts.
$ iodbctest
iODBC Demonstration program
This program shows an interactive SQL processor
Driver Manager: 03.52.0607.1008
Enter ODBC connect string (? shows list): ?
DSN | Driver
-----------------------------------------------------------
mysql-vw-test | mysql-odbc-connector
Enter ODBC connect string (? shows list): DSN=mysql-vw-test
Driver: 05.01.0011 (libmyodbc5.so)
SQL>select * from sheets;
id |sheet_number |sheet_name
-----------+---------------+-------------------------------
1 |A101 |L1 Plan
2 |A401 |L1 Reflected Ceiling Plan
3 |A102 |L2 Plan
4 |A201 |Sections
5 |A301 |Elevations
result set 1 returned 5 rows.
SQL>quit
Have a nice day.
This step is useful if you’re trying to figure out if a connection error
is happening in your Vectorworks configuration or your setup of ODBC.
5. Connect from within Vectorworks to the MySQL data source.
The moment of truth. There are two sorts of connection you can make:
an ad-hoc connection from within scripts.
a persistent connection at the document level.
Connecting from a script is the more flexible system: executing
arbitrary SQL queries from within a Vectorscript. Sky’s the limit.
I’ve supplied a test Vectorscript in the Github project directory. Go to the menu Tools > Scripts > Run VectorScript... and select odbc_test.vss from wherever you cloned the Github project.
Success is getting a few alert dialogs that look like this:
You can also establish a connection that stays with the document. It’s meant for tying in Records so that changes to the record data are “automatically” synchronized with the external database.
I won’t go too much into how this works, since you can read more by going to the menu Help > Vectorworks Help... and searching for “ODBC”.4
Start a new document, and go to the menu Tools > Database > Manage
Databases.
Hit the Connect... button.
Under the Use Data Source Name pulldown, select mysql-vw-test,
enter root as the ‘User Name’ and leave ‘Password’ blank.
The connection dialog should look like this before you hit “OK”:
A successful connection is going to look like
this (notice Vectorworks has used ODBC to figure out the table schema):
C’est tout
Congratulations. We’re done (but hopefully you’re not). Getting this
working at all used to be the deep end. Now it isn’t. Go do
something cool with your new arbitrary key-value pair assigning powers.
Go ahead and read up on ODBC at Wikipedia if you want, but not even the English page is written in English.↩
In practice, this approach swaps the time of a small group of developers for that of a large group of users, but hey, excruciating-but-possible is still better than impossible. Story for another day.↩
Short version: Vectorworks is still a 32-bit app, unlike most of the rest of Mac OS X these days. It’s easy to leave out 32-bit code by mistake, since the build tools now default to 64-bit. And Vectorworks can’t run 64-bit code.↩
In Mac Vectorworks 2011, at least, I find this system buggy and question its basic design: both its comprehensibility as an end-user feature, and the potential for confusing data loss in having implemented immediate writes to the database but only periodic polling for reads from the database. Another story for another day.↩