Wednesday, March 26, 2008

This week in Wonder

AjaxModalContainer updated

The AjaxModalContainer has been updated to ibox 2.17. It now has an example with the various options it supports, like images, inline content, external URLs etc.

Wednesday, March 19, 2008

This Week In Wonder

Misc Stuff
AjaxTabbedPanel tracks selected tabs and allows you track the selection. Check out AjaxTabbedPanelDemo for an example.

ERXStyleSheet generates a <link> tag. For non-XHTML, browsers expect the link tag to not have a closing tag. You can now set "er.extensions.ERXStyleSheet.xhtml=false" to make your link tags not generate a closing tag.

ERXWOContext now has a bunch of static directActionUrl(..) generation methods that allow you to override various combinations of host name, port, path, directaction name, http/https, and query string params.

The big one for this week is the enhanced JSON features (with a lot more surprises coming). We've had page-based JSON proxy support almost since the beginning, but it has been enhanced quite a bit along with the addition of a JSON Request Handler for JSON web services. We've upgraded our implementation from the old JSON library to the new library (which metaparadigm turned into). We also spent some time to add some new JSON EO features. One of the problems we had previously was the complexity of the demo app for JSON. There are now much easier examples that show just how simple it is to pass data around. You can hand an NSArray of EO's to your browser client and the browser can make changes and hand them back -- more to come on this front.

Stateful Page Clients
Do you need access to stateful objects from your component in the browser? Just bind:

AjaxProxy : AjaxProxy {
name = "json";
proxyName = "example";
proxy = proxy;

make a "proxy" method that returns an object that you want to be able to message (if you leave the proxy binding off, it will bind to your component directly):

public SomeProxyObject proxy() {
return _proxy;

public class SomeProxyObject {
public NSArray people() { ... } // these can be EO's!
public void doSomethingWithAPerson(Person person) { ... }

and then in javascript:

var people = json.example.people();
people.nsarray.each(function(person, index) { alert(person.firstName); }

It's pretty cool how easy it is.

JSON Web Services
Or you can use JSON for web services instead of SOAP/WOWebServices:

In your Application:

JSONRequestHandler requestHandler = JSONRequestHandler.register();
requestHandler.registerService("exampleService", new ExampleService());

Your service class is similar to a registered WOWebService class:

public class ExampleService {
public void printThisString(String string) { .. }

JSON services also support "local args", which allow the server to fill in parameters to your method for you based on the request. For instance, if your method is stateful and you need a session, you can just add a WOSession parameter to your method:

public class ExampleService {
public void printThisStringStateful(WOSession session, String string) { .. }

And you will be given a session, and the session will be maintained with cookies. The external signature of the method does not change. This works for WOSession, WORequest, WOResponse, and WOContext.

JSON Browser Client
If you want to call your JSON service from a component:

<webobject name = "AjaxJSONClient"/>.exampleService.printThisString('hi');

AjaxJSONClient : AjaxJSONClient {

JSON Java Client
If you need JSON in a Java client app:

Client client = JavaJSONClient.create("http://yourhost/cgi-bin/WebObjects/YourApp.woa/-yourPort/json", true);
IExampleService exampleService = (IExample) client.openProxy("exampleService", IExampleService.class);

public interface IExampleService {
public void printThisString(String str);

(the "true" means you want to use HttpClient as the backing impl, which requires you have that installed in your classpath). Currently there is no stubs generator, so you have to write the stub classes yourself. For most classes it's pretty straightforward (just write the accessor methods). For EO's, it's the same, but you should extend JSONEnterpriseObject on the client. JSONEnterpriseObject just exposes a set/getGlobalID method for tracking purposes. Note that EO's in a Java JSON client app do not track changes -- they only support pass-by-reference right now. That may change at some point.

Check out JSONExample in the AjaxExample app for a bunch of examples that show various ways to use the feature.

Wednesday, March 12, 2008

This Week in Wonder

Automatic Inverse Relationship Updating
This feature was partially implemented a while back, but it didn't work in all cases. This now should work all the time (Chuck! It has test cases!). Essentially what it means is that you no longer have to think about addObjectToBothSidesOfBlah or removeObjectFromBothSidesBlah, you can just use the normal EOF methods and it will automatically call the necessary methods to update your relationships.

The EOGenerator templates that ship with WOLips (and that were inherited from some long-since-paste original version) have always had methods that made this easier -- the person.setCompanyRelationship(company) and company.addToEmployeesRelationship(Person person) methods, and these are nice in code, but you are vulnerable in certain cases (like binding in components) that don't know about those methods and can leave you with only one side being updated properly.

There are new Wonder templates in WOLips that provide overrides for the core setXxx method variants, and they check to see if inverse updating is enabled. If it is, then the new way will be used, if not, the old way will be. So in the above example, when auto-updating of inverse relationships is enabled, you can can person.setCompany(company) and company.addToEmployees(person), and it will Do The Right Thing™.

To enable it, in your Properties file, just set:


Misc API
ERXJavaScript now has the same binding naming conventions as ERXStyleSheet (and the rest of WO) -- framework = "xxx" filename = "yyy", though the old bindings are still maintained.

ERXUtilities.deepClone (for anything -- it will call the corresponding object, array, or dictionary methods), ERXArrayUtilities.deepClone (for arrays), and ERXDictionaryUtilities.deepClone (for dictionaries) have been added. If you ever run into a situation where cloning a dictionary is not enough, and you need to clone arrays and dictionaries INSIDE that dictionary, these are the methods for you. deepClone will attempt to recursively clone all of the objects in the graph. You can optionally have it clone just the collections or the leaf nodes as well. Leaf nodes will only clone if they implement Cloneable and provide a clone method -- Currently this will not properly clone EO's, for instance.

Database PlugIns
There's been a long-standing totally annoying (what I consider a) bug in EOF that you have to open a database connection to be able to retrieve jdbc2Info, which is required to do things like SQL generation. If you've ever noticed when you made a new model in EOModeler, or when you try to generate SQL in Entity Modeler, it would yell at you if it couldn't connect to your DB, that's why. jdbc2Info contains things like database type information, which is almost always static with your database version.

Thanks to Andrew Lindesay, the plugins in Wonder (Postgresql and FrontBase right now) both now provide overrides for loading jdbc2Info from a static resource in the plugin jar, which means you can fully create a model and generate SQL without needing a database connection.

For postgresql, make your connection string: jdbc:postgresql://yourhost/yourdb?useBundledJdbcInfo=true

For FrontBase, make your connection string: jdbc:FrontBase://yourhost/yourdb/useBundledJdbcInfo=true

Chuck documented Click-To-Open support here.

Ajax framework has been upgraded to Prototype 1.6 and Scriptaculous 1.8. Check your apps accordingly.

Wednesday, March 5, 2008

This Week in Wonder

Every two weeks might make more sense, but as soon as I say that, they'll be a flurry of commits to Wonder and it will be a huge post :)

Migrations were made more compatible for people not using ERPrototypes. One of the tricky parts of migrations is converting the API request into a database type (for instance, getting newStringColumn to turn into a VARCHAR in your db). EOF doesn't do a very good job at guessing these values, and in some cases it's impossible to guess. When you use ERPrototypes, we can secretly cheat and lookup some known prototype values. If you don't, though, we have to guess effectively. The guessing code was made better, as well as an explicit override for an unguessable type in Postgresql. So migrations should work properly for PG and FB (at least) even if you don't use ERPrototypes now.

Why aren't you using ERPrototypes again? When you use ERPrototypes, Entity Modeler will work more efficiently as well. For instance, when you create a new entity, it will automatically give you an "id" column that is of the "id" prototype.

Property Operators (Properators?)
There are certain cases where you want to be able to have conditional Properties, or properties that get processed in some special way at load time. Wonder now offers support for "property operators". Similar to NSArray operators, you can register operators that will allow you to convert, extend, modify, and add properties during the loading process.

As an example, you can register and use the "Encrypted" operator in your Application's static block (these have to load very early):

static {
ERXProperties.setOperatorForKey(new ERXProperties.EncryptedOperator(), ERXProperties.EncryptedOperator.Key);

and you can then specify properties like:


which will use Wonder's default crypter to decrypt the value when it loads.

Or, if you happen to have, say, a really really big WO deployment, you may want to override properties based on instance range, so you can register the InRange operator named "forInstance":

static {
ERXProperties.setOperatorForKey(new ERXProperties.InRangeOperator(ERXProperties.intValueForKey("yourInstanceNumber")), ERXProperties.InRangeOperator.ForInstanceKey);

and make a property:

Note that instance number is a per-request concept, generally, so it's up to you to specify what "instance number" means to your app (so you would have to define and pass in instance number in Java Monitor). This is a pretty specialized feature, but if you have the problem, this should help.

You can write and register your own operators, though, so if you ever have to perform some processing on keys and values, you know where to look.