All 3 entries tagged Web

View all 68 entries tagged Web on Warwick Blogs | View entries tagged Web at Technorati | There are no images tagged Web on this blog

August 23, 2005

Release once every half hour

Writing about web page http://www.plasticbag.org/archives/2005/06/cal_henderson_on_how_we_built_flickr.shtml

…Cal revealed that on 'good days', Flickr releases a new version every half an hour…

{gasp}. And I thought we were doing well with our once-every-two-weeks cycle times…


August 22, 2005

Live search with Prototype Ajax.Updater

I've been playing around with prototype to see if I could make a 'live search' feature. It turns out to be quite straightforward.

1) Make an interface that returns a div's worth of content from the search engine.
2) Import prototype.js on your page
3) Add a text field to hold the search query
4) Add a div to hold the results
5) Create a Form.Element.Observer to watch the text field for changes
6) Create an Ajax.Updater to replace to contents of the div from step 4 with the results of the search
7) Sit back and bask in reflected glory for a while.

Things still to do

make the search engine not explode when searching for an empty string fixed

Things I haven't worked out yet:

– how to pass more than one parameter to the search service (I guess I could just hard-code them into the 'parameters' string) update use method=post and append the fixed params to the URL

– how to modify the delay before the service gets called (though I'm guessing that's param. 2 in the observer)

Here's an example code snippet. n.b. there's a missing opening script tag between the end of the form and the start of the script, because the blogs textile-parser is over-zealous about removing them.


<input id="searchText" name="q" size="15" title="Type a word or phrase"> ^M
<img alt="Spinner" id="search_spinner" src="spinner.gif" style="display:none;" />
</form>

   new Form.Element.Observer('searchText', 1, 
      function(element, value) {new Ajax.Updater(
        'search_results', 
        'http://localhost:8080/search/micro.html',
         {asynchronous:true, evalScripts:true, 
           onComplete:function(request)
            {Element.hide('search_spinner')}, 
           onLoading:function(request)
            {Element.show('search_spinner')},
           method:'post', parameters:'q=' + value
})})
<div id="search_results"></div>

update It works much better if you set the method to be 'post' rather than 'get', even though (in this case) 'get' is appropriate. Post prevents caching of the response, and a retrieving cached response appears not to fire the onComplete event (though it does fire onLoading), causing the spinner to spin forever.


August 17, 2005

Binding things othe than HTTP request parameters in Spring

Spring's support for binding HTTP request parameters to command objects is superb. You can do pretty much any mapping from parameters to domain objects that you want, in a totally non-intrusive way, using PropertyEditor classes to manage the translation.

However, woe betide you if you ever want to bind something other than request parameters. A request attribute, say. Because within AbtractCommandController, ServletRequestDataBinder is new()ed up inside a heap of other code inside createBinder() – so it's not easy to inject an alternate binder. And ServletRequestDataBinder itself new()s RequestParameterPropertyValues inside another heap of hard-to-override code. So trying to mess with the binding phase itself is not worth the pain.

All is not lost, however. Conveniently, there's an onBind() lifecycle method in AbstractCommandController that you can intercept to do your own custom binding. And because you can create DataBinders yourself, it's easy to bind stuff in a way that's not at all coupled to your domain. Time for an example:

public class SearchQueryHandler extends AbstractCommandController{

    protected void onBind(HttpServletRequest request, Object command) throws Exception {
        new UserBinder(request).bindUser(command);
    }

    protected ModelAndView handle(HttpServletRequest req, HttpServletResponse resp, Object obj , BindException arg3) throws Exception {

        QuerySource query = (QuerySource) obj; // with request params and user already bound
        SearchResults results = new SearchResults(query.getSearchResultsDecorator(decoratorConfig), query);
        search.execute(query, results);
...

UserBinder's bindUser() method looks like this: Note how it doesn't care at all about what class it's binding to
...
    public void bindUser(Object o ) {

        String propName = (propertyName == null)?DEFAULT_PROPERTY_NAME:propertyName; 
        PropertyValues pv = new MutablePropertyValues(Collections.singletonMap(propName, user));
        DataBinder binder = new DataBinder(o,DUMMY_COMMAND_NAME);
        binder.bind(pv);
    }

Nicely separated. To take it one step further, it would be cool to invoke UserBinder.bindUser from an AOP advice around the onBind() method, rather than in the class itself. That way, controllers could be configured to bind or not bind users as appropriate, rather than explicitly calling the binder in code. I think that's a good thing, though I'm not quite sure why.
update Of course, it's a good thing, because it means that the same controller class can be configured to execute different commands without needing to know whether or not they need users (or whatever) binding to them.


Most recent entries

Loading…

Search this blog

on twitter...


    Tags

    RSS2.0 Atom
    Not signed in
    Sign in

    Powered by BlogBuilder
    © MMXXI