ERModernLook

World? ERModernLook. ERModernLook? World.

ERModernLook is a modernized DirectToWeb look added to Project Wonder this morning. My sneak peek post from yesterday has a short screencast that gives you an idea of what it looks like.

Some additional details:

ERModernLook Frameworks

  • ERModernLook – contains the standard look page templates
  • ERModernDefaultSkin – contains the css and style resources
  • ERModernDirectToWeb – contains modernized versions of components from ERDirectToWeb

ERModernLook Applications

  • ERModernMoviesDemo – a demo application based on the ERMoviesLogic framework

Features

ERModernLook was designed with the following features in mind:

  • Tableless semantic markup – tables are used but only for tabular data (i.e: list pages).
  • Uses the Ajax framwork to supply pragmatic ajax behaviour.
    • When paginating
    • When switching inner page tabs.
    • In-line editing of related objects.
  • CSS inspired by: “Object-oriented CSS”
  • CSS and style resource are located in a separate skin framework making customizing easier.
  • All buttons/background images are references via CSS and are contained in the skin framework.
  • Enhanced edit relationship page that is embeddable and supports in-line related object editing and creation.
  • Enhanced edit date component based on Unobtrusive Date-Picker Widget V5.
  • Professionally designed default skin.

Getting Started

The easiest way to get started is to watch the demo videos (OK, not just right yet, but there will be demo videos) and then run and play with the ERModernMoviesDemo application. ERModernMoviesDemo uses the ERMoviesLogic framework which has an embedded H2 database and shouldn’t
require any configuration to get running.

The ERModernMoviesDemo can easily be copied and modified to use any business logic framework.

  • Modify the NavigationMenu.plist to customize the main menu.
  • Duplicate and modify the MoviesNavigationController referenced by the session to support your Entities.
  • Modify the user.d2wmodel to support you Entities.

Using your own skin

The CSS resource components in the look pages obtain the name of the framework that contains the css files from the
er.modern.look.skinframework property. This makes creating your own skin as easy as duplicate the ERModernDefaultSkin framework
and change the value of that property in it’s Properties file to match it’s new name.

Known issues

  • The look has seen VERY little testing on IE. Feel free to file bugs, but since I avoid IE at all costs, only bugs with patches will get serious consideration (and probably not at all for IE prior to 7).
  • The look is pretty young, so expect to there to be bugs. We will be using it in production apps so expect any obvious ones to be squashed pretty quickly.

That said, enjoy!

Screencast: Using ERExcelLook

Project Wonder’s ERExcelLook is a framework that leverages Direct To Web and the ExcelGenerator framework to easily generate Excel exports. I just finished a short screencast on adding it to an existing project. The topics covered are:

  • Adding Direct To Web to an existing project
  • Adding and action to trigger an .xls export for an array of entities
  • Creating a new model file
  • Modifying the rules

Links for the screencast:

  • There is an archive of the completed sample project available here.
  • The project is based on the Movies database, and a MySQL dump is available here.
  • The Rule Modeler tool used in the screencast can be found here.
  • The screencast itself can be found here.

I hope you enjoy it, let me know what you think.

Update: Dave Holt points out that you can specify the worksheet name by adding it in parenthesis as the first item in your displayPropertyKeys array. i.e:

(
    "(WorksheetName)",
    "title",
    "dateReleased",
    "category",
    "studio.name"
)

Alternate D2W Looks

A Direct To Web Look is a set of templates for the common task pages that defines the look and feel of the application. In the bad old days the templates were heavily table based and many of your rules would end up setting display parameters (like table cell background colour etc.)

Thankfully this is changing. I demoed Ravi Mendis’ amazing ERDivaLook (now part of Project Wonder) at WOWODC 2009 West, but since then several others have come to light. So here is a list of the Looks that I know about (excluding the ones from Apple)

  • Project Wonder – ERDirectToWeb – Though technically not a Look, the templates in ERDirectToWeb can be duplicated with Wonder’s Template_creator.pl script and can be used as the basis for your own look. Heavily leverages the power of D2W to offer massive flexibility. Not a good choice for a first attempt at creating your own look but definitely worth looking at for ideas.
  • Project Wonder – ERNeutralLook – A nasty old school Look with lots of table based layout. However, ERNeutralLook can easily be duplicated using the Template_creator.pl script and is simple enough to be useable as the basis for your first attempt at creating your own Look
  • Project Wonder – AjaxLook – I’ve not used or investigate this Look at all. However my understanding is this it is an experiment only and not ready for prime time.
  • Project Wonder – ExcelLook – This very useful Look is a bit of an oddity in that it is not designed to create an end user interface. It does however, generate easily customizable Excel exports to any application. Look for a screencast on this one soon.
  • Project Wonder – ERDivaLook – A very powerful Ajax enabled, XHTML 1.0 strict, semantic markup based Look that looks like it will be amazing once it is completed. It is currently under heavy development and has dependencies on the new WO2 framework and requires WebObjects 5.4.x. Keep an eye on this one.
  • DMSimpleLook – Used by Denis Frolov as part of his SiteMonitor demo at WOWODC 2009 West. It is a simple look with only a few templates but may be a good starting point as the basis for your own look. The SiteMontior project is available here.
  • R2D2W – Ramsey Lee Gurley’s custom Look. This look boasts XHTML 1.1 strict compliance, Section 508 compliance for accessibility, RSS, inline SVG, and user management. I haven’t explored this one in detail yet, but it definitely looks interesting. The project page is here.
  • EVDirectToWeb – Guido Neitzer’s custom Look. Currently listed as unstable pre-alpha so take that under advisment. The project page including screen shots is here.

If you know of any other D2W Looks that are released into the wild, or have any additional experience with any of these. Let me know.

Update: Oops, forgot Guido’s EVDirectToWeb

Using multiple EOEditingContexts

The job of an EOEditingContext (EC) in WebObjects is to manage an object graph of EOEnterpriseObjects (EOs). Think of it as a sandbox that your EOs live in. Any changes made to those EOs – attributes changed, relationships created or destroyed, EOs added or deleted – are tracked by the EC and committed to the database by calling its saveChanges() method.

When you create a new WebObjects application you get one EC associated with each Session object. This Session Default Editing Context (SDEC) is obtained by calling defaultEditingContext() on the Session.

The SDEC has one major benefit over the ECs you create yourself: It is automatically locked and unlocked by the frameworks freeing you from this confusing and unwieldily task. For this reason most WebObjects examples/demos/tutorials rely on the SDEC exclusively leaving each user with one global EC for all of their EOs.

Unfortunately, following this example and using only the SDEC in a production application can prove to be highly dangerous.

No one likes a dirty sandbox

I have kids, and a cat, and we live in a cat filled neighbourhood. Cats and kids both really like sandboxes, but for different reasons. I’m sure you can see where this is going. No one likes to play in a dirty sandbox.

The problem with only using the SDEC for your application is this: It is really easy to dirty the sandbox. Because an EC tracks all of the changes made anywhere in its object graph, the potential for unwanted changes getting committed to the database grows with the complexity of your application.

When bad things happen to good object graphs

Let’s say a user lands on an edit page that has a bunch of fields including a WOPopup with an onclick binding that calls some JS to submit the form. They make a change via that popup and then choose to abandon their edit and navigate somewhere else on the site.

Unbeknownst to them, when the form was submitted the EO bound to that WOPopup had it’s values altered and the SDEC faithfully tracked that change. Even though the user didn’t save those changes by calling saveChanges() the SDEC still knows about them and is patiently waiting to commit them when told to.

If the user later chooses to edit another EO, or performs any action that calls saveChanges() on the SDEC that abandoned edit will get committed.

Or, perhaps you have a new object action that creates a new EO and binds it to an edit form. Again, for some reason, the user abandons the form. You now have an empty EO sitting in the SDEC just waiting to get committed at some point in the future. When it occurs, that commit will probably fail, throwing validation errors that will make absolutely no sense to the user, or worse yet it will succeed resulting in unwanted data in your database.

Solutions?

One possible solution to this problem is to tightly manage the contents of your SDEC. This can be done by routinely calling revert() on the SDEC to eliminate unwanted changes. This requires either making your application highly modal or working very hard to trap every possible way the user can abandon any part of the site that could result in EO changes.

Believe me, this can become tired very quickly.

Hrm, any other way?

I thought you’d never ask.

In my opinion, the cleanest way to handle these issues in your application is by using multiple ECs. By creating temporary ECs to wrap any EOs that are being edited, we can eliminate many of the problems mentioned above. If the user chooses to abandon an edit mid stream, the EC (and all of it’s changes) will just disappear when it is garbage collected.

“But you said I’d have to handle icky locking issues.”

You are right, I did. But there is an easy solution. If you use Project Wonder and follow the instructions in this post, then pretty much all of the common cases of EC locking are handled automatically for you.

So it’s as easy as that?

Unfortunately no. There are a few additional things you need to think about.

  • There are peer and nested ECs
  • You cannot relate EOs in different ECs
  • You cannot move uncommitted (new) EOs between ECs

Peer and nested ECs

Usually peer ECs are the same as the SDEC, differing only in the fact that you created them. Because we are using Project Wonder for all of these examples, creating a peer EC is done like this:

EOEditingContext peerEc = ERXEC.newEditingContext();

When saveChanges() is called on peerEc any changes in it’s EOs will be committed to the database.

As their name implies, nested ECs are nested in another EC. They are created like this:

EOEditingContext peerEc = ERXEC.newEditingContext();
EOEditingContext nestedEc = ERXEC.newEditingContext(peerEc);

Unlike the peerEc, calling saveChanges() on the nestedEc does not committed the changes directly to the database. Instead it saves the changes up into it’s parent (peerEC).

Nested ECs are useful if you have a edit step that is dependent on another one. i.e. Creating a new Person EO, and from within that edit form provide the ability to open a new Address form – You wouldn’t want the new Address to be committed to the database until the Person was saved.

You cannot relate EOs in different ECs

As the heading states: YOU CANNOT RELATE EOs IN DIFFERENT ECs. This is going to be the biggest issue you face when working with multiple editing contexts. For instance this code will not work:

EOEditingContext sdec = Session().defaultEditingContext();
EOEditingContext myEc = ERXEC.newEditingContext();
EOQualifier qual = Person.EMAIL.eq("moc.bobnull@bob");
Person myPerson = Person.fetchPerson(sdec, qual);

Address newAddress = Address.newAddress(myEc);
newAddress.addToPeopleRelationship(myPerson);
newAddress.editingContext().saveChanges(); //gonna go boom!

To make this work, you are going to need to change the code to something like this:

EOEditingContext sdec = Session().defaultEditingContext();
EOQualifier qual = Person.EMAIL.eq("moc.bobnull@bob");
Person myPerson = Person.fetchPerson(sdec, qual);

EOEditingContext myEc = ERXEC.newEditingContext();
Address newAddress = Address.newAddress(myEc);//* see note
Person localPerson = myPerson.localInstanceIn(myEc);
newAddress.addToPeopleRelationship(localPerson);
newAddress.editingContext().saveChanges();

The myPerson.localInstanceIn(myEc) method is a convenience method in the WOLips Velocity eogeneration templates. It essentially wraps a call to:

EOUtilities.localInstanceOfObject(myEc, myPerson);

You cannot move uncommitted (new) EOs between ECs

The observant of you out there may have noticed that I called localInstance on the Person EO. The EO that we fetched from the DB. Not the new Address EO that we created. The reason I did that is because you cannot move uncommitted EOs between ECs. If you do this:

EOEditingContext ec1 = ERXEC.newEditingContext();
EOEditingContext ec2 = ERXEC.newEditingContext();
Person person = Person.newPerson(ec1);
Person localPerson =
    EOUtilities.localInstanceOfObject(ec, person);

localPerson will be null. And if you replace that EOUtilities call with:

Person localPerson = Person.localInstanceIn(ec2);

The call to localInstanceIn() will throw an Exception.

So, be sure to think about what objects are new and what are pre-existing. Either create your new EOs in the EC that you use to fetch your existing ones, or move your existing ones into the EC you use to create your new ones.

What about nested ECs?

Given a parent EC with a child EC: Saving the parent EC will commit its changes but will not save the changes in its children unless they’ve been saved first. So this code:

EOEditingContext parentEc = ERXEC.newEditingContext();
EOEditingContext childEc = ERXEC.newEditingContext(parentEc);
Person person = Person.newPerson(childEc);
parentEc.saveChanges();

Will not result in a new Person object being saved to the DB. For that to occur, the child EC needs to be saved first:

EOEditingContext parentEc = ERXEC.newEditingContext();
EOEditingContext childEc = ERXEC.newEditingContext(parentEc);
Person person = Person.newPerson(childEc);
childEc.saveChanges(); // move changes into my parent
parentEc.saveChanges();

The same rules that govern EOs in different ECs apply to nested ECs. However with nested ECs, once you’ve saved the child the EO will exist in the parent. You just need to retrieve it:

EOEditingContext parentEc = ERXEC.newEditingContext();
Person person = Person.newPerson(parentEc);

EOEditingContext childEc = ERXEC.newEditingContext(parentEc);
Address address = Address.newAddress(childEc);
childEc.saveChanges(); // move changes into my parent

// Get the address instance in the parent EC.
EOGlobalID gid = childEc.globalIDForObject(address);
address = (Address)parentEc.objectForGlobalID(gid);

person.setAddressRelationship(address);
parentEc.saveChanges();

The End

If you have stuck with me this long, I thank you. This is a fairly long (and perhaps rambling post) but working with multiple EOEditingContexts in WebObjects is an essential skill.

Project Wonder takes a great deal of pain out of working with multiple ECs (by eliminating most of the locking issues) but for the uninitiated, there are still some head scratchers that can crop up and I hope this post helps to illuminate them.

* Hey were does that newAddress() method come from? Well, I usually have a factory method generated by my eogeneration template that looks something like this:

public static Address newAddress(EOEditingContext ec) {
	return (Address)EOUtilities.createAndInsertInstance(ec,
						ENTITY_NAME);
}

Connection Dictionary Twiddling – Part 2

I wrote about using Project Wonder’s ERXConfigurationManager to ‘twiddle’ your model connection dictionary settings here.

I only listed the global properties, but there are per model properties as well. Unfortunately the JDBC settings are not very well documented. So, to google index my brain, here are my notes: Most of the heavy lifting is done by the fixJDBCDictionary method in ERXModelGroup – you can read the source if you want to know all the values – here is a (MySQL) example with the most common ones:

  • MyModelName.URL=jdbc:mysql://localhost/my_dbname?capitalize…etc
  • MyModelName.DBUser=db_user
  • MyModelName.DBPassword=db_password
  • MyModelName.DBDriver=com.mysql.jdbc.Driver