July 03, 2006

Setting up to use Perforce

A few weeks ago I got me a Macbook (not a Pro, just the lowly Black model) and so I had to go through all the setup steps again. Things have changed a bit since I documented this before, so here's the new info for Posterity:

First, I downloaded p4v, p4d, p4 and p4web from the Perforce website. I won't give URLs as they change when versions change. The last two are Beta versions of the Universal builds of those tools.

To install I mostly followed the directions at http://www.piap.com/faq/p4_setup.html but I made some changes:


  • put p4d, p4 and p4web in /usr/local/bin

  • create .bash_profile for each user:
    export P4ROOT=/Users/perforce/p4root
    export P4PORT=1666
    export PATH=$PATH:/usr/local/bin
  • StartupItems have been replaced by launchd in Tiger, so create

    /Library/LaunchDaemons/com.perforce.p4d.plist

    based on sample file at http://www.stuffonfire.com/2006/01.

    I had to reboot before this worked properly, but I'm not sure that's supposed to be necessary.

  • Install p4merge and p4v from dmg into /Developer/Applications

Now Perforce is nominally installed, but you've still got some setup to do. I found a few references that were helpful with this:

So, my steps, which roughly follow those at the Columbia link above:


  • start p4
  • connect to localhost:1666
  • select new user
  • fill in form to create new user
  • select new workspace
  • fill in name (mendis in my case)
  • set Root (/Users/janine/mendis). This would normally be a directory containing files to check in, but in this case I don't have any yet
  • Launch XCode, create project with directory inside your workspace root
  • Go to SCM->Configure SCM. Select Perforce, then edit to change settings (p4client is workspace name = mendis). Check box to Enable SCM.
  • Mark project directory for add. Since you haven't done anything in it yet, you don't have to worry about reverting the build directory. There may be other files you don't want to put under SCM control but I don't know about them yet.
  • Double-click on name of changelist to submit it

What you will now find is that regular files can be checked out and in via XCode just fine. Unfortunately, things fall apart when WOBuilder enters the picture. It always makes files writeable, and doesn't check them out first. So to be on the safe side, when working with WO it's probably smart to do a manual checkout before all edits. This is obviously a drawback to using Perforce; I haven't decided yet if it's worth the hassle or not.

By the way, since I'm sure someone will ask: I'm using XCode instead of Eclipse because that's what Griffin uses, and he wanted us to be using the same tools. And I'm using Perforce instead of Subversion because I wanted to try it out. Subversion doesn't really seem like it's enough better than CVS to be worth the hassle. I might yet go back to it, though, if Perforce turns out to be too much of a nuisance.

The WOBuilder problem goes away when using Eclipse, simply because there is no WOBuilder and .wod files get edited directly instead. I'm not sure I'm quite ready for that one. :)

Templating

Another bit of Griffin's wisdom...

How would one template a site so it's look can easly be changed by non-programmers?

There are two possibilities offered by WO that probably address similar concerns. The first is the basic ability to create a WOComponent as a subcomponent of other WOComponents. The second is the ability to extend the subcomponent concept to a page wrapper in which the wrapper contains a WOComponentContent element. But let's take these one at a time.

SUBCOMPONENTS

There are two general requirements for using a WOComponent as a subcomponent. The first is that it generally must be made into a partial page. This is easy to do in WOBuilder by selecting Document in the element browser strip for the page, bringing up the Info panel and selecting "Partial document" from the pop-up. This strips off the HTML header stuff so the component will generate only the HTML needed by another component.

The second requirement is that it must have the bindings needed to be bound to the parent component's attributes. In order to meet this requirement, one has to have the iVars or accessor methods in the subcomponent class to accept values sent down, and to provide values returned. Then, choose the Window->API menu item in WOBuilder (or press Cmd-3 on the keyboard) to bring up the API panel. In this panel you can indicate the keys to be bound from your subcomponent class, indicate which bindings are required, which are optional and provide some validation criteria.

When you're finished, if you've done your job well, there's almost no difference to the follow-on programmer whether he/she's using a standard WO dynamic element (like a WOTextField) or using one of your subcomponents. Each is named and bound into the page in almost identical ways. And, before you ask, yes, subcomponents can have their own subcomponents to any level you desire.

PAGE WRAPPERS

If you have a fairly constant set of headers and/or footers with, perhaps, a left or right nav bar for all or most of the pages, you can create a PageWrapper component containing all these elements and use it as a subcomponent of all your other components. The interesting thing about page wrappers is that though they are subcomponents, they tend to invert the logic a bit in that they contain the HTML header content and your standard page operates very much like a subcomponent to them, though in fact the opposite is true. In order to make them work, four things are required:

  * The page wrapper must be a complete page rather than a partial page, even though it's a subcomponent

  * The page wrapper must contain a WOComponentContent dynamic element to hold the content of the page

  * The actual pages must each be partial pages, even thought they're the parent component, since the wrapper will provide the HTML header information

  * The actual pages must generally start with a PageWrapper tag and end with its corresponding end tag wrapping the content of the page.

Basically, you lay out the page wrapper as you would have it appear for all pages, then insert a WOComponentContent element within the page wrapper where you want the page's content to appear within the wrapper.

Your application selects your page (not the wrapper, but the actual page with content) as it would normally, but your page contains a WebObjects tag for the PageWrapper subcomponent at the start, followed by its own content and ends with the PageWrapper end tag.

When WO renders the page you selected, it finds the PageWrapper (subcomponent) tag in your page and sends the appendToResponse to the page wrapper to render the headers, footers, etc. While rendering the page wrapper, it parses the WOComponentContent tag and from within the page wrapper, sends an appendToResponse to the page to render the content inside the page wrapper tags. When it parses the PageWrapper end tag, it goes back to the page wrapper to finish rendering it, then returns to your actual page which returns to the Session's appendToResponse.

Overall, it's more difficult to describe than it is to do, once you've gotten the hang of it.

There's a caveat to subcomponents that you may see discussed on the lists every once in a while. That is, by default, they're pretty safe but not very efficient. As they are initially set up, during the appendToResponse that creates the page, the parent component's values are synchronized with the child component first, then the child is sent the appendToResponse, then the child component's values are synchronized with the parent. The same thing happens on takeValuesFromRequest and on invokeAction. So that for any given Request-Response loop, the values between a parent and a child component are synchronized 6 times.

Furthermore, if several pages are using the same subcomponent (as is typical, for instance, with a page wrapper), then a new instance of that subcomponent is created and stashed in the page queue for each page that uses it.

One can increase efficiency appreciably by controlling the synchronization explicitly. For most components, there is a critical point at which the values must be synchronized or, at least, there is a critical point for each value at which it must be synchronized. Now is not the time to go into the details, but by overriding the method

    public boolean synchronizesVariablesWithBindings()

and returning a false value, WO will not synchronize the subcomponent's values at all. Then by using the methods valueForBinding() and setValueForBinding() the synchronization can be handled manually. This resolves the problem of inefficiency in synchronizing the values, but doesn't address the extra instances of the subcomponent on the page cache.

In order to deal with this second problem (if, for your application, it does indeed become a problem), you can attempt to redesign the logic of the component so that it needs no state memory from one invocation to another. If you're successful at doing that, then you can override the method

    public boolean isStateless()

and return true (the method returns false by default). If you do this, then WO presumes that the component need only be instantiated once and that instance can be reused by as many components as need it without an additional instantiation. Making a component stateless, doesn't necessarily make it thread safe. However, if multiple threads need the component, WO will automatically instantiate as many instances of the subcomponent as are necessary for the number of threads needing it simultaneously. It's a bit deeper than that, but you've been given way more than you need to know for now.

We've worked with graphic designers who quickly became used to WO. Remember, WO adds only ONE TAG to standard HTML. The tag is differentiated in its actions by the WOActiveElement or subcomponent to which the tag is bound in the .wod file.

If the designers have trouble with that (and a few have), we've typically asked them to lay the page out first. If there are no repetitions nor conditional segments of HTML, this is fairly straightforward. We then take the results of the graphic artists design and plug in the WO tags appropriately.

With conditional elements or repetitions, it's slightly harder, but in general, we typically ask them to design the page as if three repetitions occurred and redesign it as if no repetitions occurred, or design it with and without the conditional HTML. Then we identify what they want in each case and substitute the WOConditionals and WORepetitions for their conditional and repeated HTML and we've got the page.

Unless there's a separate requirement for JavaScript somewhere, there's no code in the WO HTML file at all.

Implementing "pretty" URLs

This isn't related to anything from the Mendis book, but rather comes from a discussion Griffin and I were having about a project I plan to work on in the future. He wrote some really great stuff, and I wanted to share it.

How would you implement URLs like http://www.site.com/articles/1234 in WO?

It's already handled quite conveniently (and nearly invisibly) by the WOResourceManager. This is a singleton object that manages an application's resources.

If you want to add an image or a reference to a static page in your web site, first copy the file into your Web Server Resources group in Xcode by the following steps:

* select the Web Server Resources group in the Groups & Files pane

* either right click on the group and choose Add->Existing Files... from the pop-down menu, or choose the Project->Add to Project... menu item

* navigate the Open panel to the directory containing your static resources and select it (or them)

* click the Add button in the Open panel to display the Add Items sheet

* check the "Copy items into destination group's folder (if needed)"

* you can typically leave the "Reference Type:" and "Text Encoding" pop-down menus alone

* click the "Recursively create groups for any added folders" (for reasons I'll explain below)

* click the "Web Server" check box in the "Add To Targets" list

* click the Add button.

Once your static resources have been added to this group in your project directory, they are available to your WOComponents. So, if you're adding an image that you've just copied to your project to a WOComponent page:

* open the WOComponent in WOBuilder

* add a WOImage to your page from the Dynamic pop-down menu

* double click on the WOImage in the page to simultaneously select it and to open the WOImage Binding Inspector

* click the reveal triangles to the far right side of the "filename" attribute to reveal a drop-down menu

* choose the filename of the resource that you had copied into the Web Server Resources group earlier

On doing so, you will see the file name (without associated path) appear in quotes as the binding for the "filename" attribute

The rest is up to the resource manager. You can move the file anywhere you want, so long as you let Xcode know where you moved it by updating the GetInfo panel for that resource after you move it. Clearly, there are limits. In particular, you have to move it somewhere in which it can be found in a comparable location on your deployment server.

If you have a hierarchy of resources in a directory with subdirectories, you can move the entire hierarchy into your project at once. The easiest way that I've found to do this is with both Xcode and the Finder. With your project displayed in an open Xcode window, open a Finder window and navigate it to the hierarchy of resources that you want in your project. Then click and option-drag (to copy) the hierarchy from the Finder window into the Groups & Files pane of Xcode, dropping the hierarchy directly beneath the group in which you want it copied.

Earlier, I suggested clicking "Recursively create groups for any added folders" when moving the resources. If you do this, then Xcode recognizes every file in every directory and sub-directory of the copied hierarchy. If you click the other radio button ("Create folder references for any added folders"), then although Xcode will copy the entire hierarchy, it only recognizes (indexes) the top directory as an independent entity and ignores (fails to index) every file and directory below the top, presuming that some other tool (e.g. EOModeler, WOBuilder or some such) will deal with the files and directories below that top level.

MVC

I've never been terribly clear on this concept and I seem to recall reading a thread a while back when several experienced WO people had different definitions of M, V and C. Is there a definitive reference on this I should read?

Whenever I'm unclear on such a technical subject, I usually go to Wikipedia for the first step at understanding. Try:

http://en.wikipedia.org/wiki/Model-view-controller

The article gives several other references for Model-View-Controller (MVC) and ostensibly cites the original source of the concepts. I've come to know about MVC primarily through WebObjects, associated documentation and discussions. I wouldn't dig too far in that direction yet. I think it's sufficient for now to know that Model objects are used to model the problem domain, whatever that domain is. Any code dealing directly with the problem domain should go in the Model classes.

So, for instance, if your system is a banking system, then you should expect model objects like Account, Deposit, Withdrawal, Depositor, Bank and such. The methods in these classes should reflect the bank's policy and should effect that policy in the system.

View objects should provide the application user's view into the data of the problem domain. In particular, there should be a very clear separation between Model objects/methods and View objects/methods. Code used to maintain and change the Model data should go into the Model classes. Code used to view that data should go into the View classes. Code to synchronize and coordinate those two types of classes should go into the Control classes.

In a WebObjects application, the Model code starts with the EOModel whose entities should reflect real world entities, whose attributes should reflect the characteristics of the real world entities, and whose relationships should reflect the relationships among those real world entities, at least so far as they are significant to the application.

The View objects start with the WODynamicElements and WOComponents. You can, and will, provide your own custom view objects, but you should avoid putting code in them that directly depends on some aspect of the Model objects. WOComponents are typical View objects, as are WOTextFields, WOBrowsers, etc.

The Control objects in WebObjects start with Application, Session and DirectAction. Customize these as much as necessary, but avoid putting View code or Model code in them. There are those who will say that WebObjects isn't pure and that there's a lot of Control code in the WOComponents. I think that's less important than the fact that the distributed WOElement classes and the basic WOComponent class are all pretty much pure View classes and don't muddy the water to begin with.

Satisfy your curiosity, but for now, there is little more that you need to know about MVC. WebObjects is built up around it, so as you gain experience with WO, you will naturally gain a feel for MVC.

I'm baaack

Gee, one update every six months isn't going to keep me moving very fast, is it? What can I say, I have a day job that takes a lot of time, plus I am on the board of my homeowner's association and that takes more time than you can possibly imagine.

I did make it through Chapter 1 of the Mendis book back in March. Then I got busy, and Griffin was also busy, and time kind of slipped away. However, I'm going to WWDC this year for the first time, and I am trying to cram in some more WO knowledge before I go. So I've gotten back to it, and have completed Chapters 2 and 3. They were hard-fought, especially the last one. I can't believe how many unclear, vague, incorrect and just plain missing things there are in that book! I'm now attending to other things, like catching up on this blog, while Griffin catches up to me in the book.

By the way, I said previously that I would be documenting here the problems I found in the book. Well, I had good intentions, but I got stuck so many times that in the end I was never sure which problems were real and which were my own fault. At various times I mistyped things, misunderstood things, skipped little one-line steps at the end of paragraphs that were crucial later on... this book offers many ways to screw up due to it's format and lack of repetition of important information. I'm sure some prefer it that way, but I'm not one of them. Unfortunately, this means I don't have good notes on where the actual mistakes are. If I have anything useful to share as I go along, I will do so.

I told Griffin that I would post some of his most useful writing here on this blog, so others could benefit from it, but I'm not exactly sure of the best form to use. For now, I'm just going to make new posts for each nugget of wisdom, but I'm open to suggestions on a better way if anyone has any.

Onward...