Showing posts with label prototype. Show all posts
Showing posts with label prototype. Show all posts

Saturday, December 06, 2008

The Properties Pattern and Functors

This week I have nothing to write about - I am in various stages of completion on multiple things I started, but none complete enough to post. Accordingly, in true blogging tradition, I do the next best thing - find someone else's blog, and comment on it, thereby creating a post of my own :-). This week's lucky winner is Steve Yegge's "The Universal Design Pattern" post. Granted, the post is long, but it is very interesting and informative - and entertaining. I strongly suggest you go read it first.

If you did as I suggested, you would know that Steve's Universal Design Pattern is the Properties Pattern, or an approach of modeling your object's data as Maps of name-value pairs instead of member variables. I was quite enamored with DynaBeans at one point, so much so that I built a very flexible (but somewhat difficult to maintain) content generation system around it, so this post was a major source of validation for me. I went with DynaBeans and DynaClasses because I wanted an inheritance structure, which I could not figure out how to model with maps at that time - Steve's post describes how to do this, with a special _parent key pointing to the Map that the current map extends.

So here is my take on the Properties Pattern. Each object has a Map of name-value pairs instead of traditional member variables and getters and setters. Instead, there is a get(String) and a set(String, Object) method which get and set the property named by the String argument. A get() will recursively climb the inheritance tree using the _parent key until it finds the value for the key before giving up and returning null.

I think it may be possible to take this one step further. If you look at a Prototype Ajax.Request call, it looks something like this. I choose Javascript because Javascript uses the Properties Pattern very extensively and this particular example illustrates that.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
  var request = new Ajax.Request(
    "/path/to/service/url",
    {
      method: 'get', 
      parameters: { 
        a : $F('a'),
        b : $F('b')
      }, 
      asynchronous: true,
      onLoading: function(transport) {
        // do something while the request is processing
      },
      onSuccess: function(transport) {
        // do something when the request is complete
      }
    });

In our example above, the second argument is a Map of name-value pairs. To our Properties Pattern enabled Java application object, this would like like a map with the keys {"parameters", "asynchronous", "onLoading", onSuccess"} with corresponding values hanging off them. All but the last two are simply data, but the last two are really function objects.

Although Java does not provide us ways to instantiate functions directly, we can still attach standard functor classes, such as a Transformer, from commons-collections. The onLoading and onSuccess methods are listeners that get invoked when the state of the enclosing object changes. To emulate that behavior, we could have a check to see if any of the "on*" methods should be invoked before we set the key in our set() call. Something like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
  private Map<String,Object>> map = new HashMap<String,Object>();
  ...
  public void set(String key, Object value) {
    for (String key : map.keySet()) {
      if (key.startsWith("on")) {
        // shouldFire is application or object specific
        if (shouldFire(key)) {
          Object value = fire(key, data);
          // if value is returned, do something specific with the value.
          // But most of the time (at least in the event handler case)
          // it would just be null.
        }
      }
    }
    map.put(key, value);
  }

  private Object fire(String eventName, Map<String,Object> data) {
    Transformer<Map<String,Object>,Object> handler = 
      (Transformer<Map<String,Object>,Object>) map.get(eventName);
    return handler.transform(data);
  }

It is quite possible that being able to set a function as a property may not be all that helpful, since the behavior of a prototype in most situations is directed through code -- the behavior may be slightly modifiable using data. Allowing functions to be specified in the property map means that the new instance may have completely different (overridden) behavior from its parent prototype. While this may not be the desired behavior in most cases, there can be situations, where you want to have different behavior in the child than in the parent, and being allowed to override or add functionality via function objects can be helpful.

The problem of serialization and user-friendliness can be tackled together by "allowing" the user to specify the functions as scripts in interpreted languages such as Jython or Javascript, which also run in the JVM through the ScriptEngine interface available since Java 6. Since they are scripts, they can be serialized and deserialized as text or JSON if needed.

Saturday, June 21, 2008

Searchmash Javascript client using Prototype

I haven't used Javascript for a while. The last time I used it actively, to consume JSON results (generated from local backend components) on a web page, was over three years ago, and even then, I would deliberately keep the Javascript side real simple, doing all the processing of the JSON in a server component and then just popping the formatted HTML output into the innerHTML of the div element on the web page. In my defense, this was before all these Javascript frameworks that wrap the XmlHttpRequest up into nice functions, and decent Javascript debuggers such as Firebug. So the Javascript was complicated enough without having to compose HTML from JSON at the browser side.

Lately, however, I have been thinking of ways clients can leverage our API (which returns RSS 2.0 XML results by default, but can return JSON results if requested with output=json on the query parameters). During the last two years, Javascript has become more popular, various frameworks have matured and debuggers have improved. So trying these tools out and getting a feel for them tools would not only update my skills to something approaching real-world Javascript programmers, but also allow me to apply the lessons learnt here, so I can advise clients on how they can use our API in different ways.

After I moved out of the Javascript-heavy project I mentioned earlier, others in our group continued to improve the application, and I kept hearing real good things about this (then new) Javascript framework called Prototype, which provided a nice set of functions that made Javascript coding easier and much more fun. So I decided to try out Prototype first, in order to make a Javascript based widget to return search results for my blog, using Searchmash (the apparently secret Google JSON API) as the search results provider.

The first problem I ran into was Javascript's same origin policy restriction. According to this, Javascript would not allow me to make calls on a remote server. The workaround for this is to set up a proxy on your own site that will forward the request over to the remote server and give back the results to the Javascript code as if it originated at the same server. This is explained in detail in this Yahoo Developer Howto article. Being averse to adding more code than is absolutely necessary, I tried enabling mod_proxy and then mod_rewrite on my local Lighttpd webserver, but was not successful, so I ended up using a custom PHP proxy adapted from the code in the Yahoo article. This is shown below.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?php
# searchmash-proxy.php
// Adapted from:
// PHP Proxy example for Yahoo! Web services. 
// Responds to both HTTP GET and POST requests (only GET for this one).
// Author: Jason Levitt
// December 7th, 2005
//

$url = 'http://www.searchmash.com/results/%query%+site:sujitpal.blogspot.com';

// Get the REST GET call from the AJAX application
$qt = $_GET['qt'];
$url = str_replace("%query%", $qt, $url);

// Open the Curl session
$session = curl_init($url);

// Don't return HTTP headers. Do return the contents of the call
curl_setopt($session, CURLOPT_HEADER, false);
curl_setopt($session, CURLOPT_RETURNTRANSFER, true);

// Make the call
$results = curl_exec($session);

// The web service returns JSON. Set the Content-Type appropriately
header("Content-Type: application/json");

echo $results;
curl_close($session);

?>

This proxy is called from the Javascript code. The search term is plugged into the URL, and the proxy builds the URL for the call to Searchmash, executes the request, resets the Content-Type of the request to "application/json" and spits out the text. To the Javascript code, it is as if this all happened when it called the PHP proxy. We did not need to change the Content-Type, but if we do, we can use Prototype's built-in text to JSON parsing functionality, otherwise we will have to eval(transport.responseText) ourself. The HTML page with embedded Javascript is shown below:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <title>My Blog Search Widget</title>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
    <script type="text/javascript" 
      src="http://prototypejs.org/assets/2008/1/25/prototype-1.6.0.2.js"></script>
    <script type="text/javascript">
function BlogSearch() {
  var request = new Ajax.Request(
    "/searchmash-proxy.php",
    {
      method: 'get', 
      parameters: { 
        qt : $F('q'),
      }, 
      asynchronous: false,
      onLoading: function(transport) {
        var html = '<b><blink>Searching...Please wait</blink></b>';
        document.getElementById('results').innerHTML = html;
      },
      onSuccess: function(transport) {
        var json = transport.responseJSON;
        var estimatedCount = json.estimatedCount;
        var term = json.query.terms;
        var results = json.results;
        var html = '<b>Total hits: ' +
            json.estimatedCount +
            ' for term: </b>' + 
            json.query.terms + 
            '<br/><br/>';
        results.each(function(result) {
          html += '<b><a href="' + 
            result.url + 
            '">' +
            result.title + 
            '</b></a><br/>' +
            result.snippet +
            '<br/><b>' +
            result.displayUrl + 
            '&nbsp;' +
            '<a href="' +
            result.cachedUrl + 
            '">Cached</a></b><br/><br/>';
        });
        document.getElementById('results').innerHTML = html;
      }
    }
  );
}
    </script>
  </head>
  <body>
    <p>
    <b>Enter your query:</b>
    <input type="text" id="q" name="q"/>
    <input type="button" name="Search" value="Search!" 
      onclick="BlogSearch()"/>
    </p>
    <hr/>
    <b>Results</b><br/>
    <div id="results"></div>
  </body>
</html>

The "Search!" button has an onclick handler that calls the BlogSearch Javascript function. This will make the call to the proxy with the content of the text input element. While the proxy is returning results, the anonymous function associated with the onLoading event will be called (simply setting the results div element's innerHTML to a blinking message, and once the response is available, the anonymous function associated with the onSuccess event will be called. Inside the onSuccess method, each result is parsed by yet another anonymous function, wrapped in a Ruby-like results.each() iterator. Finally the composed HTML is set into the innerHTML property of the results div block.

I copy both these files to the document root of my Lighttpd server and navigate to the HTML file (http://localhost:81/search-blog.html) on my browser, then enter the term in the search box and hit the 'Search!' button. Search results for the term 'json' are shown below:

There are several things I liked about this approach. First, no more futzing with browser detection and using XmlHttpRequest or its Microsoft cousin XMLHTTP directly. Second, the use of nested anonymous functions that improves the readability of the code. And third, the use of nested JSON objects to pass arguments to the function.

However, I felt the documentation for Prototype was rather sketchy. It is possible that I feel this because my Javascript is rusty, but this is likely to be the case for any newbie. Its not that the documentation is bad, its actually very well structured (much like Javadocs), it is just aimed at experienced Javascript developers. It may be helpful to have more examples of actual usage in the docs, much like the PHP docs on the net.