Tutorials

Loading...

Flash Player 9 (or above) is needed to view presentations.
We have detected that you do not have it on your computer. To install it, go here.

0 comments

Post a comment

    Post a comment
    Embed Video
    Edit your comment Cancel

    Favorites, Groups & Events

    Tutorials - Presentation Transcript

    1. Tutorials Version: 0.6 Release Date: 19/05/2008 Copyright © Syger 2008. All rights reserved.
    2. Table of Contents Tutorials .................................................................................................................................. 4 JPA 1.0 ............................................................................................................................... 4 Grails .................................................................................................................................. 4 Ruby on Rails .................................................................................................................... 4 JavaScript ........................................................................................................................... 5 Contacts ............................................................................................................................. 5 Java Persistence API 1.0 Gotchas ....................................................................................... 6 The Missing Information ................................................................................................ 6 The Work Arounds .......................................................................................................... 7 Caveat Emptor ................................................................................................................ 10 The Third Option, Take Two ....................................................................................... 10 The Players ...................................................................................................................... 10 JPA 2.0 – the Next Generation .................................................................................... 10 The Third Option Code ................................................................................................ 11 Special Thanks ................................................................................................................ 11 Contacts ........................................................................................................................... 11 Grails WebAlbum ............................................................................................................... 12 Getting Started ................................................................................................................ 12 Pause for Thought .......................................................................................................... 18 Security, and other Tidbits ............................................................................................ 19 The Final Cut .................................................................................................................. 22 Contacts ........................................................................................................................... 24 Ruby on Rails WebAlbum .................................................................................................. 25 The Original Tutorial ..................................................................................................... 25 Unhappy in Paradise ...................................................................................................... 25 Reworking the Application ........................................................................................... 26 The Results ...................................................................................................................... 27 Contacts ........................................................................................................................... 29 Using JavaScript .................................................................................................................. 31 The Tutorials ................................................................................................................... 31 Further Reading .............................................................................................................. 32 Contacts ........................................................................................................................... 32 JavaScript Introspection ..................................................................................................... 33 The Namespace .............................................................................................................. 33 The typeOf Function .................................................................................................... 33 The exists Function ........................................................................................................ 34 The introspect Function ................................................................................................ 35 Using the Code ............................................................................................................... 36 Contacts ........................................................................................................................... 37 JavaScript Inheritance ......................................................................................................... 38 The Extensions Code .................................................................................................... 38 Using the Code ............................................................................................................... 39 Contacts ........................................................................................................................... 41 ii © Syger 2008. All rights reserved. Tutorials
    3. JavaScript Closures .............................................................................................................. 42 The Problem ................................................................................................................... 42 The Solution .................................................................................................................... 42 The Closure Code .......................................................................................................... 43 Using the Code ............................................................................................................... 44 Contacts ........................................................................................................................... 45 JavaScript Library Design .................................................................................................. 46 The Problem ................................................................................................................... 46 The Specifications .......................................................................................................... 47 Caveats ............................................................................................................................. 48 Contacts ........................................................................................................................... 48 JavaScript Library Code ..................................................................................................... 49 The Helper Functions .................................................................................................... 49 The toXML() Function ................................................................................................. 51 Contacts ........................................................................................................................... 54 JavaScript Library Example ............................................................................................... 55 Using the Code ............................................................................................................... 55 Contacts ........................................................................................................................... 58 John Leach ........................................................................................................................... 59 © Syger 2008. Content licensed according to the Artistic License 2.0. Tutorials © Syger 2008. All rights reserved. 3
    4. Tutorials by John Leach In parallel with the articles I write as current topics, I also do quite a lot of experimentation to improve my overall programming technique. This document is a compendium of the positive results of this experimentation. Most of the information is of a technical nature, so you should have at least some experience of programming, though the JavaScript Source BigFoto.com tutorials were written with the beginner in mind. Where I discuss using some particular aspect of a programming language, framework or library, I'll do my best to explain the reasoning in an abstract manner, so that the motivation does not become too obfuscated by the necessary specific details. Some of this work is derived from the programming course I held in collaboration with the Linux User Group in Villafranca, Verona. JPA 1.0 The Japa Persistence API 1.0 promises to bring vendor neutral object relational mapping to the Java platform. I am particularly interested in finding out how far that promise holds out, where things break down, and how to make sure you keep your code base clean and portable. My development notes, and the source code can be found in the Java Persistence API 1.0 Gotchas article, on page 6. Grails After making some progress using Groovy, and having finished the preparatory work for my programming course, I decided to try out Grails, using the WebAlbum application as a model. This is a simple photo album web application, which worked quite nicely as a more complex example in Ruby on Rails for the course. The intention is not to create another Pet Store war, between Ruby on Rails and Grails, but to experience the learning curve, and make some comparisons of the development cycle from a Ruby and a Java programmer prospective. My development notes, and the source code can be found in the Grails WebAlbum article, on page 12. Ruby on Rails With the release of version 2.0, and the programming course which I had just started, the time seemed ripe to check out the novelties, and put together a small, but reasonably complex example for my students. The result was WebAlbum, a simple photo album web application. With only a limited amount of time available, the final 4 © Syger 2008. All rights reserved. Tutorials
    5. application was not sufficiently robust for production status. I've produced development notes of the modifications and experiments I made to bring the application closer to production quality, which, together with the source code can be found in the Ruby on Rails WebAlbum article, on page 25. JavaScript The original tutorials started with a series of articles that I wrote back in 2007. I had finished a long period of software development initially using VBScript, and then moving to JavaScript for two ASP projects. Since then, thanks to the programming course, I've spent some time using two truly excellent libraries, prototype and script.aculo.us. I'm happy to say that most of the code that I wrote in my tutorials still stands up against the high quality of these modern JavaScript libraries. The style of these articles is much more detailed than that of the later articles above. As such they can be consisted interesting material even for beginners, but possibly less so for advanced programmers. The JavaScript tutorials, together with the source code, begin with the Using JavaScript article, on page 31. Contacts Syger can be contacted for consultancy work on any of the topics mentioned in this article, by sending an email to info@syger.it. Tutorials © Syger 2008. All rights reserved. 5
    6. Java Persistence API 1.0 Gotchas by John Leach I've been spending some time checking out the Java Persistence API (JPA) version 1.0 over the past couple of weeks. While the prospect of being able to pick and choose the underlying ORM is enticing, there are several grey areas still not covered by JPA. In fact, some of them can be a very dark grey indeed. This tutorial aims to address two aspects of using Source BigFoto.com the JPA 1.0; understanding the missing functionality, and providing a work around. Understanding what's missing is important for a couple of reasons; firstly to know when you'll have to use vendor specific annotations (or metadata mapping files), secondly to estimate the cost of transferring your ORM domain model to a different vendor. The examples and code shown here refer exclusively to Hibernate as the vendor ORM beneath JPA 1.0, the actual project is currently using Hibernate, OpenJPA, Oracle TopLink Essentials and EclipseLink. In addition, the test harness code uses Spring. The Missing Information Well, missing functionality, really. As much as I respect and rejoice the efforts made by the JPA 1.0 committee in levelling the ground for ORM domain model development, I would have preferred that at least some information was available explaining which aspects of ORM modelling have not been addressed by JPA 1.0. I have not been able to find such information directly – but that could be just me. From various sources, including the JPA 2.0 specifications, blogs, articles, and an interesting thread on the JUG Trento mailing list, the following, almost certainly incomplete, list has been compiled: • No caching strategy • No custom types • No one-to-one primary key only associations (known as strategy=\"foreign\", in Hibernate circles) • No \"delete-orphan\" cascade type • No Criteria API • No table per concrete class inheritance • No collections of basic types • No ordered list support (position column) 6 © Syger 2008. All rights reserved. Tutorials
    7. • No read-only entities • No dynamic insert and update • No pessimistic locking Additionally, the JPA 1.0 has some features 'considered harmful', at least by Patrick Linskey, such as globally scoped queries (which makes local scoped queries difficult in a future version). The Work Arounds As we're all good and seasoned programmers, we've come to realise that nothing is perfect – except, maybe, our own code. So the first thing to do is explore the extensions available to us. All of the vendor ORMs provide their own specific annotations, which resolve the problems listed previously. If you're only ever going to use one specific vendor ORM, this is certainly the least painful path. On the other hand, if you're developing a product where more than one vendor ORM will eventually be required, then your domain model code is going to become pretty messy, as in this - admittedly contrived - pseudo-code example: @Column(...) @HibernateExtension(...) @OpenJPAExtension(...) @EclipseLinkExtension(...) @UncleTomCobleyExtension(...) String myPoorLittleProperty; // Here I am... Personally, I think that's a lot of noise, and not much signal. It is however, still a solution. An interesting alternative, provided by JPA 1.0, is the partial metadata mapping file. In theory, you can add further, partial, mapping information in a separate XML file, which will be added to, or replace, the information already supplied by the annotations. Unfortunately, the specifications (or rather the XSD) for this metadata mapping file does not allow for vendor specific tags. I certainly haven't seen anything in the Hibernate (nor OpenJPA, and EclipseLink) manuals explaining how to add, say, Hibernate specific elements to the XML file. Hibernate also provides an ORM metadata XML based mapping file, but unfortunately, it does not have a partial mechanism. So you would have to repeat the entire entity mapping, just to add, say, the delete-orphan cascade type. The Third Option Which leaves us with the third option – programmatic modification. The idea is to make programmatic changes to the annotations and / or XML mapping information metadata before the EntityManagerFactory is created. The metadata must be modified before the EntityManagerFactory is created because, after creation, the metadata is considered read-only. Tutorials © Syger 2008. All rights reserved. 7
    8. All vendor specific metadata modifications can then be isolated in one or more classes. These classes can then be replicated for other vendor ORMs (or at least the cost of doing so can be quantified). Using Spring, we start with a concrete class of the org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter class, which for Hibernate is the HibernateJpaVendorAdapter class. Much as I would have liked to simply inject a modified HibernatePersistence object in this class, via Spring itself, my many attempts failed, possibly because the private persistenceProvider property is also final. So I had to sub class it: package it.syger.jpa; import javax.persistence.spi.PersistenceProvider; /** * Extension to the original HibernateJpaVendorAdapter class to allow * a sub class of the HibernatePersistence class to be used. * * @author john.leach */ public class HibernateJpaVendorAdapter extends org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter { private PersistenceProvider provider; public void setPersistenceProvider(PersistenceProvider provider) { this.provider = provider; } public PersistenceProvider getPersistenceProvider() { return this.provider; } } The org.hibernate.ejb.HibernatePersistence class does not provide an overrideable hook method where the Ejb3Configuration object can be modified before the EntityManagerFactory is created. So again I had to sub class it, but worse, I had to copy the two different createEntityManagerFactory methods, to add the postConfigure method. Not something to be proud of, but I did not see any alternative: package it.syger.jpa; import java.util.Map; import javax.persistence.EntityManagerFactory; import javax.persistence.spi.PersistenceUnitInfo; import org.hibernate.ejb.Ejb3Configuration; /** * Extension to the original HibernatePersistence class to allow * the configured metadata to be programmatically modified before * the EntityManagerFactory is created. * * This corresponds to a third configuration level, after class * annotations, and orm.xml or *.hbm.xml files. * * @author john.leach */ public class HibernatePersistence extends org.hibernate.ejb.HibernatePersistence { private HibernateConfigurationBuilder builder; 8 © Syger 2008. All rights reserved. Tutorials
    9. public void setHibernateConfigurationBuilder(HibernateConfigurationBuilder builder) { this.builder = builder; } public EntityManagerFactory createEntityManagerFactory(String persistenceUnitName, Map overridenProperties) { Ejb3Configuration cfg = new Ejb3Configuration(); Ejb3Configuration configured = cfg.configure( persistenceUnitName, overridenProperties ); postConfigure(configured); return configured != null ? configured.buildEntityManagerFactory() : null; } public EntityManagerFactory createContainerEntityManagerFactory(PersistenceUnitInfo info, Map map) { Ejb3Configuration cfg = new Ejb3Configuration(); Ejb3Configuration configured = cfg.configure( info, map ); postConfigure(configured); return configured != null ? configured.buildEntityManagerFactory() : null; } /** * Allow for programmatic modification of the configuration, * before the EntityManagerFactory is created. * * @param cfg the configuration. */ public void postConfigure(Ejb3Configuration cfg) { if (builder != null) { builder.setConfiguration(cfg); builder.postConfigure(); } } } With these two classes in place, we can now use Spring to inject a sub class of it.syger.jpa.HibernateConfigurationBuilder which will make the required programmatic configuration changes. This following code snippet adds delete-orphan to the User domain class: package it.syger.example.domain; import org.hibernate.ejb.Ejb3Configuration; import it.syger.jpa.HibernateConfigurationBuilder; /** * Additional modifications, Hibernate specific. * * @author john.leach */ public class HibernateConfiguration extends HibernateConfigurationBuilder { public void postConfigure() { configureUser(); } private void configureUser() { String className = \"it.syger.example.domain.User\"; setPropertyCascade(className, \"clients\", \"all-delete-orphan\"); } } Remove the call to the configureUser() method, and some unit tests will fail. So far I have been able to achieve the same effect, but in a much less brittle and disruptive fashion, for EclipseLink. Achieving the same feat with OpenJPA is proving Tutorials © Syger 2008. All rights reserved. 9
    10. to be a much harder task, which I'm still working on. Caveat Emptor As you can see from the above examples, this third solution is fragile and dangerous. I will have to keep a close eye on the Spring framework, and Hibernate Entitymanager otherwise the code may break in future releases. It may be possible to convince both Spring and Hibernate to modify their own code, but I'd rather wait to see if this solution is deemed acceptable (or interesting) by more than one person before making a formal request. The Third Option, Take Two The code shown above is, well, just plain ugly. Based on the excellent persistence.xml property mechanism provided by Oracle TopLink Essentials and EclipseLink I have substituted it with a similar mechanism for Hibernate, and added this code to the Hibernate issue tracking system (EJB-360) in the hope that it will eventually find its way into the code base. The Players The most important players seem to be Hibernate, Oracle TopLink Essentials, OpenJPA, and EclipseLink. There are also other runners, such as Cayenne, JPOX, and JDO, but they don't seem to have such an important market share. For your information: • Hibernate has the lion's share of the market (about or take a look at 54%, some graphs), though do remember Disraeli's quote. • OpenJPA is the open source project based on BEA Kodo. • Oracle TopLink Essentials is the open source project based on Oracle TopLink and is the Reference Implementation for JPA 1.0. • EclipseLink is the open source project based on Oracle and is (or will TopLink, be) the Reference Implementation for JPA 2.0. JPA 2.0 – the Next Generation The JCP (JSR 317) web page. Coming to your IDEs towards the end of 2008. See the film, which specifically talks about criteria, collections, and ordered lists. The JPA 2.0 committee are already hard at work, and will almost certainly address some of the grey areas presented in this article. I doubt, however, that they will be able to address them all. When the new JPA 2.0 implementations become available, I will revise this tutorial. 10 © Syger 2008. All rights reserved. Tutorials
    11. The Third Option Code The results of this short expedition into JPA consists of a simple domain model, the Hibernate specific 'programmatic configuration' classes, and a series of simple unit tests. The Source code is available here, released under the Apache version 2.0 license, but you will have to install the required libraries for yourself. The library installation process is explained in the README file, and requires three downloads, including the mighty (79 MB) Spring framework download. You have been warned. The JavaDocs and unit test results are included in the download, for those of you (like myself) who'd rather take a quick look at the contents, before investing any further time actually compiling and running the code for themselves. The project is a simple test harness. There is plenty of room for improvement. I will be very happy to receive any criticisms or improvements you might wish to supply, if accompanied by a beer. Nota Bene Per chi parla italiano, il progetto ha adesso un suo sito, cortesia della JUG di Torino: Spikes. Special Thanks This mini project was put together with the help of several members of the Java Users Group Trento, and the Java Users Group Torino, Italy. Grazie mille, ragazzi. A particular thank you to Simon Bordet, and Nicola Pedot. The parancoe-yaml.jar file included in the download comes from the JUG Padova Parancoe project sources. They seem to have been the first to use this underrated format for fixtures on the Java platform, though it is in common use in Ruby on Rails. The jar file simply contains the parancoe-yaml classes from that project, which are based on a modified version of JYaml. Many thanks to Lucio Benfante, and Paolo Donà and Michele Franzin of SeeSaw. Contacts Syger can be contacted for consultancy work on any of the topics mentioned in this article, by sending an email to info@syger.it. Tutorials © Syger 2008. All rights reserved. 11
    12. Grails WebAlbum by John Leach After completing my programming course, which introduced the students to Ruby on Rails, and developing a simple photo album application called WebAlbum, I decided that it was time to explore one of the more interesting of the new web application frameworks in the Java arena – Grails. This article outlines my first attempt using Grails, though I already had experience with Groovy, the JVM compatible dynamic language, the Spring framework, Hibernate, and of course, Java. I used Grails version 1.0.2, see the Grails installation article for further details on downloading and installing Grails. Getting Started The original application was explained in the programming course during two lessons, each two hours long. I won't go over the details again here, but you can take a look at the articles I wrote in Ruby on Rails, Part 1, and Ruby on Rails, Part 2. You can also check out the entire course, there is also a PDF available. The application itself is relatively straightforward, there are users, who have albums, which contain pictures, that are actually a small set of four images. Only the respective users can change albums and pictures, but all the information is available to the casual visitor. Lightbox is used to produce a slide show effect for the images. Documentation I spent most of my time with “The Definitive Guide to Grails” balanced on my knee, though it's a little outdated now – see the addendum for updates. There's also the InfoQ book(let) “Getting Started with Grails”, and the excellent user guide and reference documentation. The InfoQ booklet is short and free, the Definitive Guide is longer and broader in scope, but a little shallow for my tastes, and the reference documentation is just wonderful. It has almost everything and is pretty concise. Generating the Application If only that were true! Actually Grails provides generators which produce scaffolding code for the generic application – we're still going to have to write some code. First, we'll build the application structure. Select a folder where you want the application to be generated (I chose Grails) then open a command console in that folder and type: grails create-app WebAlbum which produces the following output: Welcome to Grails 1.0.2 - http://grails.org/ Licensed under Apache Standard License 2.0 Grails home is set to: C:\\java\\grails-1.0.2 Base Directory: C:\\Grails Environment set to development Note: No plugin scripts found 12 © Syger 2008. All rights reserved. Tutorials
    13. Running script C:\\java\\grails-1.0.2\\scripts\\CreateApp.groovy [mkdir] Created dir: C:\\Grails\\WebAlbum\\src [mkdir] Created dir: C:\\Grails\\WebAlbum\\src\\java [mkdir] Created dir: C:\\Grails\\WebAlbum\\src\\groovy [mkdir] Created dir: C:\\Grails\\WebAlbum\\grails-app [mkdir] Created dir: C:\\Grails\\WebAlbum\\grails-app\\controllers [mkdir] Created dir: C:\\Grails\\WebAlbum\\grails-app\\services [mkdir] Created dir: C:\\Grails\\WebAlbum\\grails-app\\domain [mkdir] Created dir: C:\\Grails\\WebAlbum\\grails-app\\taglib [mkdir] Created dir: C:\\Grails\\WebAlbum\\grails-app\\utils [mkdir] Created dir: C:\\Grails\\WebAlbum\\grails-app\\views [mkdir] Created dir: C:\\Grails\\WebAlbum\\grails-app\\views\\layouts [mkdir] Created dir: C:\\Grails\\WebAlbum\\grails-app\\i18n [mkdir] Created dir: C:\\Grails\\WebAlbum\\grails-app\\conf [mkdir] Created dir: C:\\Grails\\WebAlbum\\test [mkdir] Created dir: C:\\Grails\\WebAlbum\\test\\unit [mkdir] Created dir: C:\\Grails\\WebAlbum\\test\\integration [mkdir] Created dir: C:\\Grails\\WebAlbum\\scripts [mkdir] Created dir: C:\\Grails\\WebAlbum\\web-app [mkdir] Created dir: C:\\Grails\\WebAlbum\\web-app\\js [mkdir] Created dir: C:\\Grails\\WebAlbum\\web-app\\css [mkdir] Created dir: C:\\Grails\\WebAlbum\\web-app\\images [mkdir] Created dir: C:\\Grails\\WebAlbum\\web-app\\META-INF [mkdir] Created dir: C:\\Grails\\WebAlbum\\lib [mkdir] Created dir: C:\\Grails\\WebAlbum\\grails-app\\conf\\spring [mkdir] Created dir: C:\\Grails\\WebAlbum\\grails-app\\conf\\hibernate [propertyfile] Creating new property file: C:\\Grails\\WebAlbum\\application.properties [copy] Copying 2 files to C:\\Grails\\WebAlbum [copy] Copying 2 files to C:\\Grails\\WebAlbum\\web-app\\WEB-INF [copy] Copying 5 files to C:\\Grails\\WebAlbum\\web-app\\WEB-INF\\tld [copy] Copying 87 files to C:\\Grails\\WebAlbum\\web-app [copy] Copying 17 files to C:\\Grails\\WebAlbum\\grails-app [copy] Copying 1 file to C:\\Grails\\WebAlbum [copy] Copying 1 file to C:\\Grails\\WebAlbum [copy] Copying 1 file to C:\\Grails\\WebAlbum [propertyfile] Updating property file: C:\\Grails\\WebAlbum\\application.properties Created Grails Application at C:\\Grails/WebAlbum C:\\Grails> That's it. Now we have the application structure. The most important folders (where we'll be doing almost all the work) are grails-app/domain, grails-app/views, and grails-app/controllers, which make up the triumvirate Model / View / Controller pattern The next step is to create the domain models; User, Album, Picture, and Image. Type: grails create-domain-class User which produces the following output: C:\\Grails\\WebAlbum>grails create-domain-class User Welcome to Grails 1.0.2 - http://grails.org/ Licensed under Apache Standard License 2.0 Grails home is set to: C:\\java\\grails-1.0.2 Base Directory: C:\\Grails\\WebAlbum Environment set to development Note: No plugin scripts found Running script C:\\java\\grails-1.0.2\\scripts\\CreateDomainClass.groovy [copy] Copying 1 file to C:\\Grails\\WebAlbum\\grails-app\\domain Created for User [copy] Copying 1 file to C:\\Grails\\WebAlbum\\test\\integration Created Tests for User C:\\Grails\\WebAlbum> This produces an empty User domain class, which we'll have to flesh out in a Tutorials © Syger 2008. All rights reserved. 13
    14. moment. Grails also produces an integration test skeleton class, but that is another story. Now rinse and repeat for Album, Picture, and Image. Before we move on, we'll need to add some content to these files, since they'll be read by the controller view generators. The more work we do now, the more the generators will do for us. So, for grails-app/domain/User.groovy: class User { String name String motto String salt String password String passwordConfirmation SortedSet albums SortedSet pictures Integer albumsCount = 0 Integer picturesCount = 0 Date dateCreated = new Date() Date lastUpdated = new Date() static hasMany = [ albums: Album, pictures: Picture ] static transients = [ 'passwordConfirmation' ] static constraints = { name(size: 1..40, blank: false, unique: true) motto(size: 0..80, nullable: true) salt(maxSize: 40, blank: false, nullable: false) } } Now, that might look a little frightening (and it's not the whole story – check the source code), but it pretty well covers the Groovy bean, and the GORM mapping. The first block defines the User properties, then we get the associations – a User hasMany Albums and Pictures, followed by the transients (which aren't persisted to the database), and finally the constraints – name must be in this size range, can't be blank, and must be unique, etc. Reads nicely doesn't it? Please note that you can do all of this in stages, start with the properties, then add associations and constraints later. I'm just trying to be a little more concise. I'll show you the Album class next, but then I'll skip Picture and Image – they're pretty much similar. In grails-app/domain/Album.groovy: class Album implements Comparable { User user String caption String description SortedSet pictures Integer picturesCount = 0 Date dateCreated = new Date() Date lastUpdated = new Date() static belongsTo = User static hasMany = [ pictures : Picture ] static optionals = [ 'description' ] static constraints = { caption(size: 1..40, blank: false) description() } 14 © Syger 2008. All rights reserved. Tutorials
    15. int compareTo(obj) { obj.id.compareTo(id) } } Similar, but not identical. The Album belongsTo a User (that's the other side of the association), and hasMany Pictures. Entering a description is optional, the constraints are so and so, you get the idea. This class implements Comparable, so that it can be stored as a SortedSet in the User domain class (most recent to least recent, using the id value). Next we'll need the controllers and views for User, Album and Picture. Since you're getting the hang of things, you'll expect that we'll use a generator: grails generate-all User which produces: C:\\Grails\\WebAlbum>grails generate-all User Welcome to Grails 1.0.2 - http://grails.org/ Licensed under Apache Standard License 2.0 Grails home is set to: C:\\java\\grails-1.0.2 Base Directory: C:\\Grails\\WebAlbum Environment set to development Note: No plugin scripts found Running script C:\\java\\grails-1.0.2\\scripts\\GenerateAll.groovy [groovyc] Compiling 6 source files to C:\\Documents and Settings\\...\\.grails\\1.0.2\\projects\\WebAlbum\\classes [native2ascii] Converting 10 files from C:\\Grails\\WebAlbum\\grails-app\\i18n to ... [copy] Copying 1 file to C:\\Documents and Settings\\...\\.grails\\1.0.2\\projects\\WebAlbum\\classes [copy] Copying 1 file to C:\\Documents and Settings\\...\\.grails\\1.0.2\\projects\\WebAlbum\\resources ... Generating views for domain class User ... Generating controller for domain class User ... Finished generation for domain class User C:\\Grails\\WebAlbum> Rinse and repeat for Album and Picture. We don't want a controller or views for Image. Next we need to do a little configuration (no not in XML) in grails- app/conf/DataSource.groovy, change the following (highlighted) lines: ... environments { development { dataSource { dbCreate = \"update\" // one of 'create', 'create-drop','update' url = \"jdbc:hsqldb:file:db/devDB\" } } ... That way we'll have a file based HSQLDB database, which will be stored in db/devDB.*. Because I can't stand writing WebAlbum all the time in the URL, we'll change the web application root for Jetty (as explained in this article) by creating a file web-app/WEB-INF/web-jetty.xml with the following contents: <?xml version=\"1.0\" encoding=\"ISO-8859-1\"?> <!DOCTYPE Configure PUBLIC \"-//Mort Bay Consulting//DTD Configure//EN\" \"http://jetty.mortbay.org/configure.dtd\"> <Configure class=\"org.mortbay.jetty.webapp.WebAppContext\"> Tutorials © Syger 2008. All rights reserved. 15
    16. <Set name=\"contextPath\">/</Set> </Configure> Which is XML, of course. However this is for Jetty, not Grails. Ready for the First Run Now we're ready for our first run. Yes, two hours from the start, most of which was spent fiddling with the domain class associations. Type: grails run-app The console will prattle on for a while, finishing with: Server running. Browse to http://localhost:8080/WebAlbum Now open up your favourite web browser and type http://localhost:8080, and you'll see the Grails home page: There are our three controllers, so click on UserController, and we'll see what will become the home page: Hmm, seven HTML errors. Not happy about that. Nice layout though, better than most programmers put together (myself included). Let's try the New User page next: 16 © Syger 2008. All rights reserved. Tutorials
    17. Hmm, twelve HTML errors. Again, the layout is good. What about the error messages? Click Create: Feedback looks alright, but those messages read like a compiler output. Some work still to be done, but there's plenty that's been done for us. First Impressions If you know Ruby on Rails, well there's nothing new here. Seen it, done it. If you're a Java developer on the other hand, there's a very large Wow! factor, or at least there was for me. Changed two lines of configuration code, added a six line XML file (which could have been skipped). Maybe a hundred lines of code for the domain class files. That's all? Yes, that's all. Really, I kid you not. The controllers and views were free of charge - and effort. Tutorials © Syger 2008. All rights reserved. 17
    18. Pause for Thought Of course, the application is not quite ready for prime time. We will have to write a little more code ourselves. Two things, in fact. I want the user password to be encrypted, and I need to scale down the images, just as I did for the Ruby on Rails version. Also I need to get rid of those HTML errors, and create more human error messages. Then there's uploading the image file. That's five things, damn. The password encryption is actually quite easy to do, thanks to Grails encoding. Same for file uploading, thanks to the Spring framework. The error messages can be tailored by defining the right messages in the i18n/messages*.properties file. The HTML can be fixed by modifying the templates (see below). Which leaves the image scaling. The images can be scaled using Java – but that's another story again. It took me five hours, five, to do that little task. And I never got the thing to save in GIF format properly. I had to use PNG in the end, and to hell with transparent backgrounds in IE6. Thankfully Chet Haase has written a lot about ImageIO and BufferedImage, and the other fifteen different image packages in Java. Phew! Modifying the Templates Firefox (actually the HTMLTidy plugin) showed us that the generated pages have errors. That means the browser will probably switch to quirks mode, which gives, uhm, quirky results. Don't want that. So let's fix the generated pages – via the templates. But, where are the templates? They're in three places in the %GRAILS_HOME% directory; %GRAILS_HOME%/src/war/css/main.css, %GRAILS_HOME %/src/grails/grails-app/views/layouts/main.gsp and %GRAILS_HOME%/ src/grails/templates/scaffolding/*.gsp. There are others, all within the %GRAILS_HOME%/src folder, but I'm only interested in the ones I've listed. I've included the modified templates in the WebAlbum/templates folder, and I won't go into the details of all the modifications here. The point is that it is easy to do, and saves a lot of time – especially if you have many controllers and views. I'll make just one observation though. %GRAILS_HOME%/src/war/css/main.css and %GRAILS_HOME%/src/grails/grails-app/views/layouts/main.gsp are both generated by grails create-app. To get rid of almost all the HTML errors I added this: <!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\"> to main.gsp. There was no point in regenerating the application for such a simple change, so I did the same in the grails-app/views/layouts/main.gsp file. In a similar vein, the CSS file can simply be copied back and forth. 18 © Syger 2008. All rights reserved. Tutorials
    19. Play it again, Sam! The remaining templates are used to generate code by the grails generate- views command. But, wait a minute, we've already created the controllers and views. The grails generate-views command is well behaved, it will prompt you before attempting to overwrite a file. So all you have to do is delete the view files you want regenerated, run grails generate-views again, and answer n every time you're asked if you want to overwrite the existing file. I wish there was a flag to set, you know –-overwrite=false, but I couldn't find one. Security, and other Tidbits We need to make sure that certain operations can only be performed under certain conditions. For example, a User can only be edited if the logged in user has the same id, or a picture can only be created if the user is logged in and has at least one album. There are two ways of handling this; either using a beforeInterceptor or by creating a xxxFilters class in the grails-app/conf folder. I tried both, but found the interceptor mechanism much more friendly. So let's take a quick look at UserController: ... def beforeInterceptor = [ action: this.&intercept, except: [ 'logout', 'list', 'show', 'create' ] ] ... boolean intercept() { def methods = actionMethods() if (methods && (flash.warning = validMethod(methods))) { redirect(action: 'list') return false } if (actionName == 'save' || actionName == 'login') { return true } if ((flash.warning = sameUser())) { redirect(action: 'list') return false } true } ... We're using a little Groovy magic (this.&intercept) to convert our method into a closure, but the method itself has full access to params, redirect, and all the other controller stuff. Something I couldn't do with the xxxFilters alternative. The code basically checks the request.method type ('GET', 'POST', 'PUT', 'DELETE', etc), and if that's alright, we're done if the action is save or login. Otherwise we then check that the logged in User is the same as the requested User. Tutorials © Syger 2008. All rights reserved. 19
    20. Tag Libraries Well, that certainly made me shudder. TLDs, gruesome code, yuk! Not Tag libraries. going to do that, much as very few others do either. But then I read Graeme Rocher's article about them, and I realised, well, this is Grails, which is bending over backwards to make my life easier, so tag libraries it is. This turned out to be one of those Wow! moments. Although even Grails can't bind the controller data, or GSP data to the tag library, it is just so simple to use. Here's an extract from grails-app/taglib/WebAlbumTagLib.groovy: class WebAlbumTagLib { static namespace = \"wa\" // if development environment def ifDevEnv = { attrs, body -> def map = grailsApplication.metadata String env = map[grailsApplication.ENVIRONMENT] if (env == grailsApplication.ENV_DEVELOPMENT) { out << body() } } // if user logged in... def ifUser = { attrs, body -> boolean valid = session.userId != null if (attrs.not) { valid = !valid } if (valid) { out << body() } } ... In the GSP pages, this looks like: ... <div class='nav'> <span class=\"menuButton\"> <g:link controller=\"user\" class=\"create\" action=\"create\"> New User </g:link> </span> <wa:ifUser> <span class=\"menuButton\"> <g:link controller=\"album\" class=\"create\" action=\"create\"> New Album </g:link> </span> <wa:ifUserHasAlbums> <span class=\"menuButton\"> <g:link controller=\"picture\" class=\"create\" action=\"create\"> New Picture </g:link> </span> </wa:ifUserHasAlbums> </wa:ifUser> </div> ... Which is actually cleaner than the usual <% code %> blocks that you find in ASP or Rails, as far as I'm concerned. The important point is that it takes very little extra effort to do this. No Tag Library Descriptor configuration in sight. 20 © Syger 2008. All rights reserved. Tutorials
    21. Transferring objects from the GSP to the tag library is also surprisingly simple, and a little magical. For example, the following tag library 'method': def pictureLightboxAnchor = { attrs -> def picture = attrs.remove('picture') def size = attrs.remove('size') def lightboxSize = attrs.remove('lightboxSize') def caption = picture.caption if (!caption) { caption = '...' } ... is picking up the picture object (and referencing its properties) as an attrs property, which we gave it via this GSP code: <wa:pictureLightboxAnchor picture=\"${picture}\" size=\"${Image.Small}\" lightboxSize=\"$ {Image.Large}\" /> Clever stuff, indeed. Partial to Partials Well, that's really Rails talk, but they're there nonetheless. Using the render tag, you can 'call' templates from your GSP pages. You can also pass a model to them, which is better than not having any binding at all. Internationalisation is covered with the message tag and method. Using the Internationalisation grails-app/i18n/messages*.properties files you can personalise error messages, and add your own. Here's a short extract of the messages I added: # WebAlbum Application messages user.name.blank=You must specify at least one character for the name user.name.size.toosmall=You must specify at least one character for the name user.name.size.toobig=The name cannot exceed 40 characters user.name.unique=The name you have chosen has already been taken user.motto.size.toobig=The motto cannot exceed 80 characters user.password.length=The password must be between 6 and 40 characters user.password.match=The password does not match the confirmation ... intercept.not.owner.user=You must be the owner to change the user information intercept.not.owner.album=You must be the owner to change the album information intercept.not.owner.picture=You must be the owner to change the picture information intercept.no.albums=You must have an album to create a picture My only complaint is that I had some problems with apostrophes in the text. I didn't have time to investigate properly, so I just got rid of 'em, er, them. Configuration In grails-app/conf/UrlMappings.groovy I made a couple of modifications, to map image/ requests to the PictureController, and to set the root URL / to the UserController list action: class UrlMappings { static mappings = { \"/$controller/$action?/$id?\" { constraints { Tutorials © Syger 2008. All rights reserved. 21
    22. // apply constraints here } } \"/image/$id/$size?/$filename?\" { constraints { size(matches: /\\d+/) } controller = 'picture' action = 'view' } \"500\" (view: '/error') \"/\" (controller: 'user', action: 'list') } } Again, Grails tries hard to help, allowing us to constrain the size parameter to numeric values. The Final Cut I've written rather more than I had originally intended. Part of that is due to my enthusiasm for Grails. I don't usually get enthusiastic about frameworks these days, but Grails (together with Groovy, of course), just seems to emit small packets of happiness all over the place. The final statistics were: 3 controllers with 571 lines of code, 4 domain classes with 165 lines of code, and a tag library with 150 lines of code, 0 lines of SQL, and about 12 lines of configuration. Oh, and four days development time, almost one of which spent messing around with the Java imaging libraries. That also included the time spent learning the framework. The results look like this: As you can see, we've removed the HTML errors, and the error messages read a little better. 22 © Syger 2008. All rights reserved. Tutorials
    23. The edit-delete view. I moved the delete button and added a checkbox (paranoid tactics, perhaps). The typographical error is at no further charge. Sigh. The show page. The albums pictures are listed below the album information. Finally, Lightbox in all it's glory: Tutorials © Syger 2008. All rights reserved. 23
    24. The WebAlbum application, and the modified templates used can be found in the GrailsWebAlbum.zip archived file. Be warned, though – this is a 5 MB file. The password for both users (jhl and lug) is villafranca. The source code is licensed identically to Grails, using the Apache version 2.0 license. Addendum Nothing is perfect. I received an email from Alex, which showed that WebAlbum didn't work with MySQL, complete with stack trace. The culprit was the byte[] data field in Image.groovy. Because the size wasn't set, MySQL was using a 256 byte field, whereas HSQLDB is much less fussy. Alex made a three line change to Image.groovy: static constraints = { data(size: 0..10000000) } and he was back in business. Obviously GORM sets the correct type (BLOB in this case) based on the maximum size, and the actual RDBMS – with help from Hibernate, I think. In any case, it is always a good idea to limit the upload size, so I have made the necessary changes. PictureController.groovy will now give an error message if the file size is above 10 MB in size (10,485,760 bytes). Thanks, Alex. Contacts Syger can be contacted for consultancy work on any of the topics mentioned in this article, by sending an email to info@syger.it. 24 © Syger 2008. All rights reserved. Tutorials
    25. Ruby on Rails WebAlbum by John Leach This tutorial discusses a few observations, and some improvements made to the WebAlbum application developed using Ruby on Rails 2.0.2 for a programming course I held between January and March, 2008. You'll need to install Ruby on Rails – version 2.0.2. The application also uses RMagick and SQLite. Installing RMagick is explained in this FAQ, this tutorial uses version 2.0.0. Installing SQLite is explained in this How-to, and the tutorial uses the Ruby SQLite3 wrapper version 1.2.1. The Ruby on Rails Wiki page also gives helpful information for SQLite. Not all of the modifications were a total success, nonetheless I have documented those as well. I can't sell mistakes as a consultant, but I can at least learn from them. The Original Tutorial The original application was explained in the programming course during two lessons, each two hours long. I won't go over the details again here, but you can take a look at the articles I wrote in Ruby on Rails, Part 1, and Ruby on Rails, Part 2. You can also check out the entire course, there is also a PDF available. The application itself is relatively straightforward, there are users, who have albums, which contain pictures, that are actually a small set of images. Only the respective users can change albums and pictures, but all the information is available to the casual visitor. Lightbox was used to produce a slide show effect for the images. My first impressions were that Ruby on Rails 2.0 is a definite improvement on the previous 1.2 version, even though this has caused some major breakages. The learning curve is gradual, as each problem presented itself I could reference the Wiki or the API, and almost always find the answer I was looking for. Otherwise Googling “ruby on rails api something” would usually find a relevant article. My second impressions were that some things could be improved. While I had more or less followed the guidelines developing the models, controllers and views, there remained a few points that I was not happy with. Some of this unhappiness lay within my own code, and some was in the Rails generated code. Of course some mistakes were due to ignorance. Rails can achieve a lot of things for you – if you know what's there. The trouble is that developers outside the Rails project, such as myself, don't have that much time to study all the details – we're too busy, er, writing code. Unhappy in Paradise Well, I can't disagree that Ruby on Rails has brought back the fun to web application development. It is probably because of the fact that I can produce results quickly with very small quantities of my own code that I'm prepared to spend more time improving the code itself. Using other frameworks, I was too exhausted simply Tutorials © Syger 2008. All rights reserved. 25
    26. getting the thing to work, to do anything more. Also, I am extremely happy with the framework itself. A lot of people have put many man years of effort into creating and perfecting it. All I'm concerned with are the generators, or more precisely, the generator templates. Since they are there, in glorious source code, I see no reason not to modify them – at least for this particular application. The Models What I didn't like about the User and Picture models was adding instance variables for what are, essentially, form fields – the password_confirmation and the uploaded picture file. The idea was to create sub classes for the 'form' versions of the model. The Controllers The generated controllers use the exception throwing find method to retrieve the object with a specified identifier. I thought I'd need to modify the template, but actually Rails provides several methods to look after exceptions – you've just got to know that they're there. I do, however, think that the update and destroy methods needs some protection – at least they should check that the request was a post?, otherwise, in the case of destroy, all you have to do is type /users/destroy/1, and the user (and associated albums and pictures) are gone forever. Maybe there should be a delete page, to confirm that you really do want to (permanently) remove all that work? The Views The generated views are clean and simple. Unfortunately, they're a little too simple to be easily manipulated by CSS rules. I don't see any reason for not using a table to format the new, edit and show pages. I also decided that I wanted a delete page, and add a delete? option to the edit page. Reworking the Application I froze the Rails application (which I do as habit), so the modifications have only been made to this single application, not to Rails itself, stored in the Ruby folder, because they'd be overwritten on the next gem update. All of the templates, and their scripts can be found in the railties/lib folder, more precisely in vendor/rails/railties/lib/rails_generator/generators/components/. The model script and templates that I modified are in, surprisingly, the model folder: model/ model_generator.rb 26 © Syger 2008. All rights reserved. Tutorials
    27. templates/ model.rb edit_model.rb I modified the model_generator.rb to create a second model file, called edit_model.rb, which handles the edit form properties. I added comments in the model.rb file to remind myself where to look for the association and validation methods that will almost surely need to be added. The major modifications, and general tweaking take place in the scaffold folder: scaffold\\ scaffold_generator.rb templates\\ style.css view_index.html.erb view_show.html.erb view_new.html.erb view_edit.html.erb view_delete.html.erb controller.rb Basically, I made a one line change to the script scaffold_generator.rb to create an additional view_delete.html.erb template, and then added quite a lot of HTML code to the view templates themselves. That meant updating the style.css file too. In the controller.rb template I added a set of verify method calls, and commented lines for filtering. I also added a delete method to handle delete page requests, and dummy rescue_action_locally and rescue_action_in_public methods, to remind myself that they're there, and should be used. I also added a generic filter_rescue_action method, as a dummy example of around_filter implementation. The edit method also got a work over, to accept deletion code (so that I would have a choice, either a delete page, or a delete button in the edit page). The Results Generally, I am happy with the new views. Here are a few screenshots, after hand tuning the pages (mostly deleting unnecessary fields). The first is the home (index) page: Next is the show page for an album, including picture list (which I cut and pasted from the picture index page): Tutorials © Syger 2008. All rights reserved. 27
    28. Finally, the edit page, with edit or delete form: In the end I decided that I preferred having the delete option in the edit page – deleting something is an editing action. I simply couldn't think of a link text to the delete page that wouldn't frighten the user; “Confirm to delete” just didn't seem to cut it. Minor breakages Unfortunately, sub classing the model for the forms created some problems. Rails 2.0 adds extra features to the link_to and redirect_to methods where you only need specify the object (a @user for example), and Rails works out the rest from the class name. Change the class name and this breaks, so I had to do a little more tweaking than I'd anticipated. In the views, for example, the original 28 © Syger 2008. All rights reserved. Tutorials
    29. <%= link_to 'Show', @user %> had to be converted to <%= link_to 'Show', user_path(@user) %> since @user had changed from being a User class, to being an EditUser sub class of User. The same problem occurred in the form itself: <% form_for(@user) do |f| %> became <% form_for(:user, @user, :url => user_path(@user)) do |f| %> Yuk. Maybe sticking to Modules is the better policy. The controller also had to endure similar modifications: # POST /users # POST /users.xml def create @user = User.new(params[:user]) respond_to do |format| if @user.save session[:user_id] = @user.id flash[:notice] = 'User was successfully created.' format.html { redirect_to(@user) } format.xml { render :xml => @user, :status => :created, :location => @user } else format.html { render :action => \"new\" } format.xml { render :xml => @user.errors, :status => :unprocessable_entity } end end end becomes: # POST /users # POST /users.xml def create @user = CreateUser.new(params[:user]) respond_to do |format| if @user.save session[:user_id] = @user.id flash[:notice] = 'User was successfully created.' format.html { redirect_to user_path(@user) } format.xml { render :xml => @user, :status => :created, :location => @user } else format.html { render :action => 'new' } format.xml { render :xml => @user.errors, :status => :unprocessable_entity } end end end Not overly taxing modifications, but modifications nonetheless. The WebAlbum application, and the modified templates used can be found in the RubyOnRailsWebAlbum.zip archived file. Be warned, though – this is a 4 MB file. The password for both users (jhl and lug) is villafranca. The source code is licensed identically to Ruby on Rails, using the MIT license. Contacts Syger can be contacted for consultancy work on any of the topics mentioned in this Tutorials © Syger 2008. All rights reserved. 29
    30. article, by sending an email to info@syger.it. 30 © Syger 2008. All rights reserved. Tutorials
    31. Using JavaScript by John Leach JavaScript is, as Douglas Crockford puts it, a very misunderstood programming language. These tutorials are designed to provide an overview, with examples, of the some of the power of this programming language. Since the advent of AJAX, and more recent developments such as Google Gears, the importance of being able to program well in JavaScript has grown enormously. The tutorials are not just aimed at the developer who works on code for the browser or client side. I have made presentations to classic ASP developers, in an attempt to move them away from VBScript and towards JScript, for web application development. There are also many other implementations of JavaScript interpreters within web servers available, apart from IIS. Together with this series of tutorials, I have also made available two compressed archives; Scripts.zip and AspScripts.zip. The first (which is about 20KB in size) contains the JavaScript code used in these tutorials. This can be used for your client side projects, as the code is distributed under the GNU Lesser General Public License. The second (which is about 100KB in size) is a small, but complete ASP project, using exactly the same code as the first. It is designed to be a starting point for your classic ASP web application development, as the code is also distributed under the GNU Lesser General Public License. Unzip the contents into your IIS wwwroot directory, create a Virtual Directory called AspScripts, set the default document to then point your browser to index.asp, http://localhost/AspScripts/. The Tutorials I will not attempt to introduce you to the syntax of JavaScript. This has already been done in an excellent paper by Simon Willison, entitled “A (Re)-Introduction to JavaScript”, prepared for a presentation he gave. He has also generously made slides available of the presentation. The first tutorial looks at one of the dynamic aspects of JavaScript - being able to introspect an object to discover its properties. Apart from being a useful debugging tool, it also introduces simple namespacing, and recursion. The second tutorial investigates inheritance, adding functions to existing objects, via prototypal inheritance. JavaScript is not a restrictive programming language, you can add methods and properties to even built-in objects. The third tutorial discusses closures, which are used primarily in JavaScript to create private properties, using inner functions. The final tutorial builds a small library to generate XML. This is by far the largest of the tutorials, so it is split into design, the code, and the example. The emphasis is Tutorials © Syger 2008. All rights reserved. 31
    32. on using the least possible amount of code to just do the job in hand. Further Reading Douglas Crockford has spent a lot of energy explaining the power and versatility of JavaScript. I highly recommend reading all the articles he has published, which he has grouped together on this page. There are also links to a series of presentation videos, ranging from 30 to 60 minutes. Again, there is much valuable information to be found here, and I think that you will find the time well spent. He has also created the JSLint program. This program checks the construction of Javascript programs, and offers suggestions to improve the code, and reduce potential errors. There is a JSDoc tool to create HTML documentation from specially formed documentation comments in the JavaScript source code. It follows a very similar syntax to Java's javadoc tool. Contacts Syger can be contacted for consultancy work on any of the topics mentioned in this article, by sending an email to info@syger.it. 32 © Syger 2008. All rights reserved. Tutorials
    33. JavaScript Introspection by John Leach This tutorial introduces you to JavaScript introspection. Being able to interrogate an object to discover its properties can be a great help when debugging. Additionally, given the great number of implementations of JavaScript (not all of which are compatible between themselves), the possibility of interrogating an object to find if it contains specific functions is essential in developing cross browser, and cross implementation code. The Scripts/Introspector.js file, explained in this tutorial, contains three helper functions; typeOf, an enhancement to the typeof keyword, exists, a function to test if an object contains a specified property, and introspect, which produces a list of the specified object's properties. The Namespace First, we'll take a look at the JavaScript code. There are several important concepts here, which I think are worth describing in some detail. I'll start with the syger namespace: var syger = { // ... }; All variables and functions that you create, unless properties of an object, will be added to the Global Object. Within the web browser, this is window. Apart from the generally agreed concept that global variables are a bad programming practice, the Global Object will quickly become cluttered with properties. There may even be naming collisions. Here I am introducing three new methods, but by making them properties of an object (syger), I am only adding one variable to the Global Object. True it is only a variable, but it does work as a namespace. The drawback is that I will have to write syger.exists(...), instead of just exists(...), but this is a price that I am more than willing to pay to avoid name collisions. The Function typeOf There are three functions in this tutorial, and I'll start with the simplest of them, typeOf: /** Checks the type of a given object. @param obj the object to check. @returns one of; \"boolean\", \"number\", \"string\", \"object\", \"function\", or \"null\". */ typeOf : function (obj) { type = typeof obj; return type === \"object\" && !obj ? \"null\" : type; } Tutorials © Syger 2008. All rights reserved. 33
    34. Unfortunately, the typeof keyword returns \"object\" even when the object is null. This is generally considered an error, which typeOf corrects. The first thing to note is that I have used javadoc style commenting for the function. This allows me to create HTML documentation from the JavaScript source code using the JSDoc tool. It follows a very similar syntax to Java's javadoc tool. JavaScript is a case sensitive language so typeOf will not clash with the keyword typeof. Since it's inside an object definition, I can use object literal syntax; { name : value, name : value, ... }. The function body uses the ternary operation (?:) to return the typeof value for all objects that are truthy, or the string \"null\" otherwise. In JavaScript, truthy means any object that is not 0, NaN, \"\", null or undefined. Only the last two refer to objects. The Function exists The next function is exists. This function will check that, for a specified object, the named property exists and has the required type: /** Checks if a property of a specified object has the given type. @param obj the object to check. @param name the property name. @param type the property type (optional, default is \"function\"). @returns true if the property exists and has the specified type, otherwise false. */ exists: function (obj, name, type) { type = type || \"function\"; return (obj ? this.typeOf(obj[name]) : \"null\") === type; } Again, there are a few interesting points to outline. The JavaScript interpreter will match a method call to a defined function, irrespective of the number of method arguments provided. This can make function overloading a little tricky, but it offers a simple mechanism to provide optional function arguments. Essentially, this means that I can call the syger.exists function, as follows syger.exists(this, \"puts\"); syger.exists(this, \"puts\", \"function\"); and both will give identical results. The first line of the function takes care of setting type to a valid value, if it wasn't present in the argument list. This is a short cut which takes advantage of the fact that JavaScript returns the object within a boolean expression, rather than true or false. It can be read as set type to itself if truthy, otherwise set it to the string \"function\". I also use the equality operator === to check the type. This is not a typing mistake, JavaScript has two equality operators. The first, and probably more familiar is the == operator. It uses type coercion when making the comparison, which means that the 34 © Syger 2008. All rights reserved. Tutorials
    35. expression 1 == \"1\" will return true, because the number on the left hand side is coerced to a string. The second type of equality operator does not coerce the values, so 1 === \"1\" will return false. The Function introspect Finally, there is the introspect function itself: /** Introspects an object. @param name the object name. @param obj the object to introspect. @param indent the indentation (optional, defaults to \"\"). @param levels the introspection nesting level (defaults to 1). @returns a plain text analysis of the object. */ introspect : function (name, obj, indent, levels) { indent = indent || \"\"; if (this.typeOf(levels) !== \"number\") levels = 1; var objType = this.typeOf(obj); var result = [indent, name, \" \", objType, \" :\"].join(''); if (objType === \"object\") { if (level > 0) { indent = [indent, \" \"].join(''); for (prop in obj) { var prop = this.introspect(prop, obj[prop], indent, level - 1); result = [result, \"\\n\", prop].join(''); } return result; } else { return [result, \" ...\"].join(''); } } else if (objType === \"null\") { return [result, \" null\"].join(''); } return [result, \" \", obj].join(''); } For a given named object, this function will display the object's type, and the types of all its enumerable properties. Since some of these may also be objects, these will also be displayed. To achieve this, the function calls itself recursively. Obviously, if the object hierarchy is very deep, and you have set the levels value very high, you will get a lot of text back. Fortunately, JavaScript rarely requires such an architecture. Here, the var keyword is used to ensure that the variables remain inside the function scope. I make a point of checking that all variables within a function are preceded by var, otherwise they will become a property of the Global Object. I use the Array.join() function to concatenate a series of strings, in favour of using the + operator, because it will only create one new string. Strings in JavaScript are immutable, so a statement like \"Now\" + \" is\" + \" the\" + \" right\" + \" time\" will create four strings \"Now is\" + \"the\" + \"right\" + \"time\" Tutorials © Syger 2008. All rights reserved. 35
    36. \"Now is the\" + \"right\" + \"time\" \"Now is the right\" + \"time\" \"Now is the right time\" three of which will be discarded, to be swept up by the garbage collector at some later time. The levels parameter was added in a second moment, thanks to observations made by Ming-fai Ma, who demonstrated that the function can loop until the stack overflows, when there are circular references. Using the Code Now we can execute the example test code, provided in the Scripts/IntrospectExample.js file, which is shown below: // Create a short cut to the normal output device if (!syger.exists(this, \"puts\")) { var puts = function (str) { document.write(str); }; } var obj = \"string value\"; puts(syger.introspect(\"string variable\", obj) + \"\\n\"); if (syger.exists(obj, \"indexOf\")) { puts('string variable.indexOf(\" \") returns ' + obj.indexOf(\" \") + \"\\n\\n\"); } else { puts('string variable has no \"indexOf\" method\\n\\n'); } obj = 1.2345; puts(syger.introspect(\"numeric variable\", obj) + \"\\n\"); if (syger.exists(obj, \"toFixed\")) { puts('numeric variable.toFixed(1) returns ' + obj.toFixed(1) + \"\\n\\n\"); } else { puts('numeric variable has no \"toFixed\" method\\n\\n'); } obj = { first: 1, second: \"2nd\", third: function () { return \"3rd\"; }, fourth: new Object(), fifth: null }; obj.sixth = obj; puts(syger.introspect(\"user_defined_object\", obj) + \"\\n\"); if (syger.exists(obj, \"third\")) { puts('user_defined_object.third() returns ' + obj.third()); } else { puts('user_defined_object has no \"third\" method'); } The five lines of code at the start simply create a short cut puts to the longer function document.write. The surrounding test creates the short cut only if no previous definition of puts has been made. In the ASP version of this tutorial I can use exactly the same code, by previously defining a puts variable which is a short cut to Response.Write, the standard output function in the ASP environment. Another thing to note is that I am setting the variable to a function definition. In JavaScript function definitions are also objects. Note also the ; after the final 36 © Syger 2008. All rights reserved. Tutorials
    37. function brace. This terminates the statement puts = ... ;, and is required. Now we can run the example code, which gives the following results: The first and second examples demonstrate the results of introspecting a string variable and a numeric variable respectively. As you can see, it would appear that neither have any methods, even though the standard documentation shows that the indexOf or toFixed method should be present. The methods do exist, of course, which is shown in the following line. In both examples we have been introspecting two native objects of JavaScript. Native objects do not always provide information for introspection. The last example, where we introspect an object we have defined, gives the expected results, including the function definition. I also deliberately set the sixth value to be circular. The next tutorial discusses inheritance. All the scripts in these tutorials are available for download as two compressed archives; Scripts.zip and AspScripts.zip, both distributed under the GNU Lesser General Public License. Contacts Syger can be contacted for consultancy work on any of the topics mentioned in this article, by sending an email to info@syger.it. Tutorials © Syger 2008. All rights reserved. 37
    38. JavaScript Inheritance by John Leach This tutorial discusses JavaScript inheritance. However, since JavaScript is a class free programming language, the mechanism used is prototypal, rather than classical. For this tutorial, I want to add functions to the String object. In JavaScript, the String object is pretty lean, if you're an experienced programmer, you'll be surprised to find that it doesn't have trim(), startsWith() or endsWith(). If you check out the documentation for Microsoft JScript, or Mozilla JavaScript, for the information on the JScript or JavaScript String object, you'll see what I mean. Fortunately, in JavaScript we can add functions to any object using the prototype property of the object. This is, in essence, prototypal inheritance, and personally, it is one of the features of JavaScript which makes it so enjoyable. Now, although I've scrupulously read the documentation, it may be that the functions I want to add (trim(), startsWith() and endsWith()) already exist. This could be because the documentation is wrong, or that this code is running in a newer JavaScript interpreter which has added these functions. In the previous tutorial I explained a function called exists(), which can check if a specific property of specified type exists. So all I need to do is wrap my function definition in a test – if the function already exists, then I'll use that, otherwise I'll add my function: if (!syger.exists(String.prototype, \"newFunctionName”)) { String.prototype.newFunctionName = function () { // ... }; } The Extensions Code Let's now look at the code. First trim: /** Trims leading and trailing whitespace from a string. From Douglas Crockford \"Remedial JavaScript\", http://javascript.crockford.com/remedial.html @returns the trimmed string. */ String.prototype.trim = function () { return this.replace(/^\\s+|\\s+$/g, \"\"); }; The next function is startsWith: /** Checks if the string starts with the specified sub string. @param str the sub string to check for. @returns true if the string starts with the sub string, otherwise false. */ String.prototype.startsWith = function (str) { 38 © Syger 2008. All rights reserved. Tutorials
    39. return this.indexOf(str) === 0; }; Finally, we have the endsWith function: /** Checks if the string ends with the specified sub string. @param str the sub string to check for. @returns true if the string ends with the sub string, otherwise false. */ String.prototype.endsWith = function (str) { var offset = this.length – str.length; return offset >= 0 && this.lastIndexOf(str) === offset; }; There really isn't anything new here, except perhaps for the use of the this keyword, and regular expression syntax, both seen in the trim() function: return this.replace(/^\\s+|\\s+$/g, \"\"); When used inside a function the this keyword refers to the current object, which is the String object in the example above. Here it is calling the method replace which belongs to the String object. can be defined within two slash characters (/), the following g Regular expressions specifies that the expression is global to the entire string. This particular regular expression reads: one or more whitespace characters at the start of the string, one or more whitespace characters at the end of the string. The replace method will substitute each match with the empty string \"\". Using the Code Now we can execute the example test code, provided in the Scripts/ExtensionsExample.js file, which is shown below: // Create a short cut to the normal output device if (!syger.exists(this, \"puts\")) { var puts = function (str) { document.write(str); }; } obj = new String(\" string object \"); puts(syger.introspect(\"string_object\", obj) + \"\\n\\n\"); obj = \" string object \"; puts(\"obj returns '\" + obj + \"'\\n\\n\"); puts(\"trim() returns '\" + obj.trim() + \"'\\n\\n\"); puts(\"startsWith(' str') returns \" + obj.startsWith(' str') + \"\\n\"); puts(\"startsWith('str') returns \" + obj.startsWith('str') + \"\\n\"); puts(\"startsWith(' string object longer ') returns \" + obj.startsWith(' string object longer ') + \"\\n\\n\"); puts(\"endsWith('ect ') returns \" + obj.endsWith('ect ') + \"\\n\"); puts(\"endsWith('ect') returns \" + obj.endsWith('ect') + \"\\n\"); puts(\"endsWith(' string object longer ') returns \" + obj.endsWith(' string object longer ') + \"\\n\\n\"); obj = typeOf(\"string literal\"); puts(\"obj returns '\" + obj + \"'\\n\\n\"); Tutorials © Syger 2008. All rights reserved. 39
    40. puts(\"startsWith('str') returns \" + obj.startsWith('str') + \"\\n\"); puts(\"endsWith('ing') returns \" + obj.endsWith('ing')); You've already met the five lines of code at the start in the previous tutorial. The next two lines call the introspect method to see if the new functions have been added. The following two lines create a string and print it's value. Next the trim() method is tested, then the startsWith() method, and finally the endsWith() method. The last four lines of the example use the new methods on a string which is returned by the typeof keyword (a built-in method). As you can see in the results below, even this string has the additional methods, even though it was produced by code which did not know of their existence. Now we can run the example code, which gives the following results: The introspect method currently shows the three new methods. As I explained above, this could change in the future. Next we see the value of our string, which has leading and trailing spaces (the example code adds the single quotes, by the way). Then we see the result of trimming the string. The startsWith and endsWith tests use the original string, not the trimmed string. They also test for substrings which are actually longer than the string being tested. Finally, the same methods are used on the string returned by the typeof keyword, which has the value \"string\". 40 © Syger 2008. All rights reserved. Tutorials
    41. The next tutorial explains closures. All the scripts in these tutorials are available for download as two compressed archives; Scripts.zip and AspScripts.zip. Contacts Syger can be contacted for consultancy work on any of the topics mentioned in this article, by sending an email to info@syger.it. Tutorials © Syger 2008. All rights reserved. 41
    42. JavaScript Closures by John Leach This tutorial takes a look at JavaScript closures. In JavaScript, this means is that an inner function always has access to the variables and parameters of its outer function, even after the outer function has returned. This is an extremely powerful property of the language. In Douglas Crockford's article entitled “Private Members in JavaScript”, he uses this technique to produce private members, and privileged methods in a JavaScript object. Rather than repeat that work, I'll look at a pragmatic problem that appears many times in web applications – correct link formation. The Problem Simple links usually do not represent a problem. You can embed them directly in a document thus: <a href='http://www.syger.it/Tutorials/UsingJavaScript.html'> Using JavaScript </a> Links which require a query part, however, can become problematic: <a href='http://finance.yahoo.com/q/bc?s=YHOO&t=5d'> Ticker 'YHOO'. </a> Here, we have the URL (http://finance.yahoo.com/q/bc) and the query (s=YHOO&t=5d), which obviously could be modified to display a different company (the s parameter) for a different period of time (the t parameter). Clearly, we can use this information to provide links to all companies on the American stock market, for different lengths of time. Let's say that this idea spurs us on to create a financial advice web site, where we want to reference different companies over different time periods, in several different pages. Simple hard coding would not be a sensible solution because Yahoo! Inc's financial service might change their URL, or parameter names. It could be that they keep the old URL for compatibility reasons, but create an entirely new URL which gives much better information. Possibly, we decide that Google Inc's financial service is better, but we find that it has a different URL (http://finance.google.com/finance), and query (q=YHOO). The Solution We'd like to build a function which gives us the complete URL, which we can then paste into any position on our pages, as a link, but we don't want any of the component information in the URL to be available outside the function, to avoid polluting the pages with potentially dangerous information. We can limit the damage by creating an object which has a series of fixed functions for each of the stocks we're interested in. For this example I'll use four; Yahoo! Inc., 42 © Syger 2008. All rights reserved. Tutorials
    43. Google Inc., Microsoft Corporation, and Sun Microsystems Inc. The choice is purely casual, I'm not a financial advisor. The Closure Code The Scripts/Closure.js file, explained in this tutorial, contains a single function which creates an object containing the link creating functions for our four stock tickers. As in the introspection tutorial, I've placed this function in a namespace: var app = { // ... }; I'm assuming that as the application grows, so will the number of properties in the app namespace. Then we have the createFinancialLinks function, which uses inner functions and closure: /** Creates the company financial information object. Example usage: <pre> var refs = app.createFinancialLinks(\"http://finance.yahoo.com/q/bc\", [\"s\", \"t\"], { \"Yahoo\" : \"YHOO\", \"Google\" : \"GOOG\", \"Microsoft\" : \"MSFT\", \"Sun\" : \"JAVA\" }); </pre> @param url the financial service URL. @param params an array of the query parameter names, in order: ticker name, time span. @param tickers a collection of the company identifier : ticker names. */ createFinancialLinks : function (url, params, tickers) { function makeLinker(ticker) { return function (span) { var timeSpan = \"\"; if (span && params.length > 1 && typeof params[1] === \"string\") { timeSpan = [\"&\", params[1], \"=\", span].join(''); } return [url, \"?\", params[0], \"=\", ticker, timeSpan].join(''); }; } var info = {}; for (var idx in tickers) { if (tickers.hasOwnProperty(idx)) { info[\"linkTo\" + idx] = makeLinker(tickers[idx]); } } return info; } To understand this code, I'll separate it into two parts; the inner function makeLinker, and the function body. Since it's the easy part, I'll start with the function body, which begins after the inner function definition. This code creates a collection called info which will contain a method linkToXXX for each of the Tutorials © Syger 2008. All rights reserved. 43
    44. tickers we supply as the tickers parameter. The XXX part will be replaced by the company identifier, such as linkToSun. This collection is then returned to the caller. The real magic takes place inside the inner function makeLinker. Firstly, using an inner function can increase the readability of the code. Here, for example, it much simplifies the loop in the body of our createFinancialLinks function, without having to create another publicly visible function. Secondly, it has references to the parameters and local variables of its parent function, so they don't have to be passed as parameters, and you don't have to create public properties. Our inner function makeLinker returns an anonymous function definition, which has one parameter, span. But this function definition also uses parameters from the createFinancialLinks function, and from makeLinker to be able to do its job. Since it is a function definition, it is not actually being called, but simply stored in our info object. At some later time this function will actually be called, and will produce the desired results, almost magically. This is where closure comes in. Not only does JavaScript store the function definition, but it also stores the 'environment' - the parameters and variables which existed at the time the function was defined. This information is stored in the scope object for the function. Using the Code Now we can execute the example test code, provided in the Scripts/ClosureExample.js file, which is shown below: // Create a short cut to the normal output device if (!syger.exists(this, \"puts\")) { var puts = function (str) { document.write(str); }; } // Utility function to print the list function printList(tickers) { puts(\"<ul>\\n\"); for (var idx in tickers) { if (tickers.hasOwnProperty(idx)) { puts(\" <li><a href='\" + refs[\"linkTo\" + idx](\"5d\") + \"'>\" + idx + \"</a></li>\\n\"); } } puts(\"</ul>\\n\"); } // Create references for Yahoo! Inc. financial services var systemTickers = { \"Yahoo\" : \"YHOO\", \"Google\" : \"GOOG\", \"Microsoft\" : \"MSFT\", \"Sun\" : \"JAVA\" }; var refs = app.createFinancialLinks(\"http://finance.yahoo.com/q/bc\", [\"s\", \"t\"], systemTickers); // Show the Yahoo! Inc. links puts(\"<p>Links for tickers in \"); puts(\"<a href='\" + refs.linkToYahoo(\"5d\") + \"'>Yahoo! Inc</a>\"); puts(\" financial services:</p>\\n\"); 44 © Syger 2008. All rights reserved. Tutorials
    45. printList(systemTickers); // Create references for Google Inc. financial services refs = app.createFinancialLinks(\"http://finance.google.com/finance\", [\"q\"], systemTickers); // Show the Google links puts(\"<p>Links for tickers in \"); puts(\"<a href='\" + refs.linkToGoogle(\"5d\") + \"'>Google Inc</a>\"); puts(\" financial services:</p>\\n\"); printList(systemTickers); You've already met the five lines of code at the start in the introspection tutorial. The printList function simply writes an unordered list of all the tickers. I created a function because I use this code twice in the example. Note that this function uses the index reference mechanism (object[property]) to refer to, and call, the linksToXXX functions, rather than dot notation (object.property). Next, I provide the information to produce the links to the Yahoo! Inc., financial services. Then I output a list, with the paragraph header using the dot notation to refer to the Yahoo! Inc., stock quote. So far so good. But Google Inc., also provides financial services, so the next three lines creates a new set of link functions to this service, and finally I write out the same structure, but this time using Google's service. Now we can run the example code, which gives the following results: The next and final tutorial explains the XML generator library, starting with the design of the library. All the scripts in these tutorials are available for download as two compressed archives; Scripts.zip and AspScripts.zip, both distributed under the GNU Lesser General Public License. Contacts Syger can be contacted for consultancy work on any of the topics mentioned in this article, by sending an email to info@syger.it. Tutorials © Syger 2008. All rights reserved. 45
    46. JavaScript Library Design by John Leach This final tutorial explains the XML generator JavaScript library. Whether you like angle bracket tax or not, XML is used in web development for XHTML pages, RSS feeds, and AJAX, to name but a few. This library is designed to produce XML (it has nothing to do with parsing XML). It was inspired by Sean M. Burke's digression in his article “Higher-Order JavaScript”, and by JSON, the JavaScript Object Notation. The Problem Personally, I am very tired of reading, let alone writing code like this: <div id=\"sidebar\"> <h3>Action</h3> <ul> <li><a href=\"<%=newItem%>\">New</a></li> <li><a href=\"<%=tagCloud%>\">Tag cloud</a></li> </ul> <h3>Navigation</h3> <ul> <li><a href=\"<%=cJU%>\">Detail</a></li> <li><a href=\"<%=AppPath + \"List.asp\"%>\">All</a></li> </ul> </div> Or even this: var x = document.createElement(\"div\"); x.setAttribute(\"class\", \"foo\"); document.body.appendChild(x); var y = document.createElement(\"div\"); y.setAttribute(\"class\", \"bar\"); x.appendChild(y); y.appendChild(document.createTextNode(\"Baz\")); The first is, of course, classic ASP. The problem is that you have to escape code snippets using <% and %>, or <%= and %>. Highlighting helps a little, but it is still all too easy to make a mistake with those escapes. It can also be extremely frustrating to debug. The second is standard DOM notation. As you can see, it is extremely verbose, and quite difficult to understand what it is doing. Want to know? It is simply producing this: <div class=\"foo\"> <div class=\"bar\"> Baz </div> </div> I don't want to do that, either. What I want is some terse format, in JavaScript, which will produce XML using less text than the XML itself. Here's an mock-up, to produce the first example: [\"div#sidebar\", [\"h3\", \"Action\"], [\"ul\", [\"li\", [\"a\", { \"href\" : newItem }, \"New\"]], 46 © Syger 2008. All rights reserved. Tutorials
    47. [\"li\", [\"a\", { \"href\" : tagCloud }, \"Tag Cloud\"]] ], [\"h3\", \"Navigation\"], [\"ul\", [\"li\", [\"a\", { \"href\" : cJU }, \"Detail\"]], [\"li\", [\"a\", { \"href\" : AppPath + \"List.asp\" }, \"All\"]] ] ] And the second example would look like: ['div.foo', ['div.bar', \"Baz!\" ] ] I hope you agree with me that these last two examples look easier than the first two. Both of the examples are valid JavaScript, though the resulting array is not stored anywhere. Being JavaScript, we can modify the array contents programmatically, before converting to XML. As long as the variables in the first example return strings, then this is also valid JSON. The second example is certainly valid JSON. The Specifications An element is defined by an array. The array contains the name of the element, followed by optional attributes (specified as a collection), followed by any number of text or elements (including zero). For example: [\"img\", { \"src\" : \"...\" }] Or: [\"a\", { \"href\" : \"...\" }, [\"img\", { \"src\" : \"...\" }]] Or: [\"pre\", \"Text\"] Or: [\"br\"] The name of the element can be 'decorated' with a single identifier, using the '#' character, and/or by a single class name, using the '.' character. When used together, the '#' character must appear first. For example: [\"p.normal\", \"Text\"] which is identical to: [\"p\", { \"class\" : \"normal\" }, \"Text\"] Or: [\"p#id123\", \"Text\"] which is identical to: [\"p\", { \"id\" : \"id123\" }, \"Text\"] Or: Tutorials © Syger 2008. All rights reserved. 47
    48. [\"p#id123.normal\", \"Text\"] which is identical to: [\"p\", { \"id\" : \"id123\", \"class\" : \"normal\" }, \"Text\"] Caveats It is impossible to specify a text node, on its own, with this syntax. At best the text would have to be wrapped in an element, such as span: [\"span\", \"Text\"] Element names, attribute names, and text must be of type string. Attribute values can be either of type string or number. Obviously, they can also be substituted by method calls, though these must return the required type. No effort has been made to encapsulate more exotic node types, such as document type, comments, CData, or processing instructions. The converter always encloses attribute values in single quotes ('). This means that font names in CSS style attributes can use double quotes (\"), without having to escape the string. The converter makes no attempt to encode illegal characters (<, >, &, \", and ') in attributes or text. Rather, it assumes that the programmer has taken care of this detail beforehand. The library does provide helper functions, however. The next part of this tutorial describes the library code. All the scripts in these tutorials are available for download as two compressed archives; Scripts.zip and AspScripts.zip, both distributed under the GNU Lesser General Public License. Contacts Syger can be contacted for consultancy work on any of the topics mentioned in this article, by sending an email to info@syger.it. 48 © Syger 2008. All rights reserved. Tutorials
    49. JavaScript Library Code by John Leach This second part of the final tutorial takes a look at the XML generator library code. This library also depends on code from the introspection tutorial. The intention is to discuss the problems encountered, and their solutions. Personally, although the specifications were quite simple, I am disappointed that so much code was required to implement it. Nonetheless, it is all functional, and has been tested. Firstly, I will discuss the few helper functions, then we'll get down to the details of the converter function toXML(). The Helper Functions As for the closures tutorial, I have placed all the functions in the app namespace. There are three helper functions, and one debugging function, which I will discuss first: /** Emits an error. @param text the error message. */ app.complain = function (text) { if (syger.exists(window, \"alert\")) { window.alert(text); } return new Error(text); }; This is a generic debugging function. Clearly, although the JavaScript syntax for our XML structures is quite simple, it is possible to get it wrong. When this happens the complain() method is called, which will display an alert (when available on the platform), and then create an Error object. This should be used in your code together with the throw keyword, thus: if (some_error_occurred) { throw complain(\"Some error occurred\"); } The three helper functions are designed to provide escaping or encoding mechanisms for three distinct circumstances; in text (app.escape()), in attribute values (app.escapeAttribute()), and in a component of a query (app.escapeQueryComponent()). /** Escapes plain text. Converts <, & and > to XML entities. See http://javascript.crockford.com/remedial.html @param text the plain text. @returns the modified text. */ app.escape = function (text) { if (text.match(/\\S/g) === null) { Tutorials © Syger 2008. All rights reserved. 49
    50. return text; } return text.replace(/&/g, \"&amp;\").replace(/</g, \"&lt;\") .replace(/>/g, \"&gt;\"); }; The app.escape() method replaces all occurrences of '<', '&', and '>' into their entity equivalents of &lt;, &amp; and &gt;. These characters must necessarily be converted, since otherwise they would (falsely) trigger the XML parser. /** Escapes plain text in an attribute. Converts &lt;, &amp; &#39; and &gt; to XML entities. See http://javascript.crockford.com/remedial.html @param text the plain text. @returns the modified text. */ app.escapeAttribute = function (text) { if (text.match(/\\S/g) === null) { return text; } return text.replace(/&/g, \"&amp;\").replace(/</g, \"&lt;\") .replace(/>/g, \"&gt;\").replace(/'/g, \"&#39;\"); }; Similarly, attribute values must also be escaped. This function is nearly identical to app.escape() except that it also escapes the single quote (') character. The toXML() method always delimits attribute quotes with the single quote. The method uses the &#39; entity, which is identical to the &apos; entity, but has the advantage of being recognised by all browsers. The third helper function escapes illegal characters in components of the query part of a hyperlink. This is explained in detail in RFC 2396. /** Escapes plain text in an query (name or parameter). Converts illegal characters to %xx format. @param text the plain text. @returns the modified text. */ app.escapeQueryComponent = function (text) { if (text.match(/\\S/g) === null) { return text; } if (syger.exists(Server, \"URLEncode\")) { return Server.URLEncode(text); } else { return encodeURIComponent(text); } }; The function delegates to the built-in method available, depending on the platform. As an example, suppose we have a blog site which can find a specific blog item given the title. If the title is “JavaScript: == considered harmful?” then the correctly escaped query part is: title=JavaScript%3A%20%3D%3D%20considered%20harmful%3F Which can be produced using: var query = [\"title\", \"=\", 50 © Syger 2008. All rights reserved. Tutorials
    51. app.escapeQueryComponent(\"JavaScript: == considered harmful?\")].join(''); The Function toXML() The major part of the code lies in the toXML() function, which actually consists of an inner function printXML(). Firstly I'll look at the outer function, then go through the inner function, section by section. /** Converts the element array to a string. Example usage: <pre> var elem = [\"p\", \"Paragraph text\"]; app.toXML(elem, \"\", \" \"); </pre> @param elem the element to convert. @param indent the optional initial indentation to use. @param gap the optional indentation increment, the default is two spaces. */ app.toXML = function (elem, indent) { var hasIndent = typeof indent === \"string\"; gap = gap || \" \"; // The function that does the actual work function printXML(elem, indent) { // explained later... } // Do the work return printXML(elem, indent)[0]; }; The first two lines take care of the optional arguments. Then comes the inner function definition printXML(), which is called and the first part of the returned value (the converted text) is returned. The reason for this extra layer of complexity is indentation. I like the generated code to look good, not just because it is 'pretty', but also because it is easier to follow. Let me give you an example: <div class=\"foo\"> <div class=\"bar\"> Baz </div> </div> and the same text without the 'pretty' indentation: <div class=\"foo\"><div class=\"bar\">Baz</div></div> For just a few lines of XML its still understandable, but I wouldn't like to try reading a page full of flattened XML text. Both are perfectly acceptable to the browser's parser, of course. The dilema is that this is quite difficult to do. Firstly, the generator has no idea which tags shouldn't be indented. In theory we should be able to indent them all, but in my experience I have found that it is best not to do so with a, img, and span tags. When they are surrounded by text, browsers seem to make mistakes. Most notable is a small underline 'tail' visible in an anchored text: <p>Text <a href=\"#\"> Tutorials © Syger 2008. All rights reserved. 51
    52. anchored text </a> more text. </p> If I'm lucky, you'll see the defect below, otherwise you'll just have to take my word for it: The problem disappears when flat: <p>Text <a href=\"#\">anchored text</a> more text.</p> As you can see below: So we have to tell toXML() which tags we don't want to indent. This is achieved with the app.nonIndentingXML collection: /** Non indenting elements. Used by toXML to prevent indentation. */ app.nonIndentingXML = {\"a\": true, \"img\": true, \"span\": true}; Just add the name of any other tags you do not want to be indented. Going back to the toXML() function, after this rather long parenthesis, to get indentation to work properly, I need to know if the child nodes performed indentation or not, together with the converted text, before deciding on whether the parent node should use indentation or not. This requires that the converting function returns two pieces of information, whereas toXML() will only return one – the converted text. Hence the need for the inner function, which we'll look at next. // The function that does the actual work function printXML(elem, indent, gap) { // Sanity checking... if (!elem || !elem.push) { throw app.complain(\"element must be an array\"); } var len = elem.length; if (len < 1) { throw app.complain(\"element array is empty\"); } var name = elem[0]; if (typeof name !== \"string\") { throw app.complain(\"element name must be a string\"); } As explained in the documentation comment, the indent and gap parameters specify the initial indent, and succeeding indent increments. Then the code checks that the elem parameter is an array, and that its first value is a string. // Convert the element name and any embedded attributes var identName = null; var className = null; var openTag = \"\"; var pos = name.indexOf('.'); if (pos > 0) { hasAttrs = true; className = name.substring(pos + 1); openTag = [openTag, \" class='\", className, \"'\"].join(''); name = name.substring(0, pos); 52 © Syger 2008. All rights reserved. Tutorials
    53. } pos = name.indexOf('#'); if (pos > 0) { hasAttrs = true; identName = name.substring(pos + 1); openTag = [openTag, \" id='\", identName, \"'\"].join(''); name = name.substring(0, pos); } Next it parses the name to extract the embedded identifier, and class name. // Build the opening tag openTag = [\"<\", name, openTag].join(''); // Get the element attributes var idx = 1; if (len > 1) { var attrs = elem[1]; var type = syger.typeOf(attrs); // Try to be sure it's an Object and not an Array if (type === \"object\" && !attrs.push) { idx = 2; for(var attr in attrs) { if (attr === \"id\" && typeof identName === \"string\") { // Already added } else if (attr === \"class\" && typeof className === \"string\") { // Already added } else { var value = attrs[attr]; type = typeof value; if (type === \"number\") { value = value.toString(); } if (type === \"string\" || type === \"number\") { openTag = [openTag, \" \", attr, \"='\", value, \"'\"].join(''); } } } } } At this point, the opening tag can be partially built. The array value after the name, if it exists, can be either the attributes collection or a child node. If it is the collection, the code adds the attributes and their values to the opening tag, but skips the id and class attributes if they have already been written. // Now check the indenting var indented = false; var result = \"\"; if (idx < len) { openTag += \">\"; var closeTag = [\"</\", name, \">\"].join(''); Next comes the indenting. This first part above, simply separates an element by whether it has children, or is childless. The opening tag, and closing tag can be prepared at this point. // Print the children nodes var nextIndent = hasIndent ? indent + gap : null; for ( ; idx < len; idx++) { var child = elem[idx]; if (typeof child === \"string\") { result += child; } else if (child && child.push) { var printed = printXML(child, nextIndent); Tutorials © Syger 2008. All rights reserved. 53
    54. result += printed[0]; indented = indented || printed[1]; } else { throw app.complain(\"child must be a string or array\"); } } The code iterates though the child nodes, accumulating converted text, and whether the children used indenting. // Print this node if (hasIndent && app.nonIndentingXML[name] === undefined) { if (indented) { result = [indent, openTag, \"\\n\", result, indent, closeTag, \"\\n\"].join(''); } else { result = [indent, openTag, result, closeTag, \"\\n\"].join(''); } indented = true; } else { result = [openTag, result, closeTag].join(''); } } Now this node can finally be printed (converted to a string). It has two choices; if it is an indenting node, then it will indent, otherwise it won't. The first case is a little more complex, because it will add a newline after the opening tag, only if the children used indentation. This gives a more compact format, while still using indentation. else { openTag += \" />\"; if (hasIndent && app.nonIndentingXML[name] === undefined) { indented = true; result = [indent, openTag, \"\\n\"].join(''); } else { result = openTag; } } return [result, indented]; } This last part of the code takes care of childless elements. The opening tag is completed, and the element is checked for indentation. The function then returns the text, indentation flag pair as an array. After this long explanation of what is a very large function, we can move on to the example. All the scripts in these tutorials are available for download as two compressed archives; Scripts.zip and AspScripts.zip, both distributed under the GNU Lesser General Public License. Contacts Syger can be contacted for consultancy work on any of the topics mentioned in this article, by sending an email to info@syger.it. 54 © Syger 2008. All rights reserved. Tutorials
    55. JavaScript Library Example by John Leach This final part of the final tutorial demonstrates the XML generator library code. We'll use the same information for our fictitious financial web site, from the closures tutorial, only this time we'll try to make the output a little more professional. This is not a lesson in using CSS, it is a simple exercise in using the XML generating library. Using the Code Now we can execute the example test code, provided in the Scripts/XmlExample.js file, which is shown in segments below: // Create a short cut to the normal output device if (!syger.exists(this, \"puts\")) { var puts = function (str) { document.write(str); }; } The omnipresent puts function. // Create references for Yahoo! Inc. financial services var systemTickers = { \"Yahoo! Inc\" : \"YHOO\", \"Google Inc\" : \"GOOG\", \"Microsoft Corp\" : \"MSFT\", \"Sun Microsystems Inc\" : \"JAVA\" }; var tickerRefs = app.createFinancialLinks(\"http://finance.yahoo.com/q/bc\", [\"s\", \"t\"], systemTickers); Here we create our list of tickers and hyperlink references, exactly as before in the closures tutorial. // Fake the financial information (Correct as of 12 October 2007) var stockInfo = { \"YHOO\" : { trade: \"28.48\", min52: \"22.27\", max52: \"33.61\" }, \"GOOG\" : { trade: \"637.39\", min52: \"416.70\", max52: \"641.41\" }, \"MSFT\" : { trade: \"30.17\", min52: \"26.60\", max52: \"31.84\" }, \"JAVA\" : { trade: \"6.23\", min52: \"4.50\", max52: \"6.78\" } } This is a set of data for the tickers. In a real application, we would get the latest data, probably via http, but for this demonstration a static set is adequate. /** Utility function to generate the ticker list. @param layout the layout to use; one of \"data-table\" or \"unordered-list\". @returns the XML element (an Array). */ function generateTickers(layout) { var result = null; var idx = null; switch (layout) { case \"data-table\": result = [\"tbody\"]; for (var idx in systemTickers) { if (systemTickers.hasOwnProperty(idx)) { var item = [\"tr\"]; Tutorials © Syger 2008. All rights reserved. 55
    56. var ticker = systemTickers[idx]; var quoteAttrs = { \"style\" : \"text-align: right; padding-right: 0.20cm;\" }; result.push(item); item.push([\"td\", idx]); item.push([\"td\", [\"a\", { \"href\" : tickerRefs[\"linkTo\" + idx](\"5d\") }, ticker ] ]); item.push([\"td\", quoteAttrs, stockInfo[ticker].trade]); item.push([\"td\", quoteAttrs, stockInfo[ticker].min52]); item.push([\"td\", quoteAttrs, stockInfo[ticker].max52]); } } var tableAttrs = { \"style\" : \"background-color: #e8f8e8; margin-bottom: 0.40cm;\", \"align\" : \"center\" }; var hdrAttrs = { \"style\" : \"border-bottom: 1px solid #000; padding: 0.20cm;\" }; result = [\"table\", tableAttrs, [\"thead\", [\"tr\", [\"th\", hdrAttrs, \"Company\"], [\"th\", hdrAttrs, \"Ticker\"], [\"th\", hdrAttrs, \"Current\"], [\"th\", hdrAttrs, \"Min 52 wk\"], [\"th\", hdrAttrs, \"Max 52 wk\"] ] ], result ]; break; default: result = [\"ul\"]; for (var idx in systemTickers) { if (systemTickers.hasOwnProperty(idx)) { var item = [\"li\"]; result.push(item); item.push([\"a\", { \"href\" : tickerRefs[\"linkTo\" + idx](\"5d\") }, idx, \" (\", systemTickers[idx], \")\"]); } } break; } return result; } The generateTickers() function produces JavaScript elements for two layouts, a new table based layout, which also adds ticker data, and the old original unordered list layout. // ASP padding var padding = \" \"; // Create and print the XML var fragment = [\"div\", { \"class\" : \"Code\" }, [\"p\", \"Links for stock tickers in \", [\"a\", { \"href\" : tickerRefs[\"linkToYahoo! Inc\"](\"5d\") }, \"Yahoo! Inc\" ], \" financial services:\" ], generateTickers(\"unordered-list\"), generateTickers(\"data-table\"), [\"p\", [\"span\", { \"style\" : 56 © Syger 2008. All rights reserved. Tutorials
    57. \"font-style: italic; font-weight: bold;\" }, \"Note:\" ], \" Values correct as of 12 October 2007.\" ] ]; puts(app.toXML(fragment, padding, \" \")); puts(app.toXML([\"br\"], padding, \" \")); fragment = [\"div\", { \"class\" : \"Code\" }, [\"p\", 'The \"data-table\" output in XML:'], [\"pre\", {\"style\" : \"font-size: 8pt;\" }, app.escape(app.toXML(generateTickers(\"data-table\"), \"\")) ], [\"p\", \"End of XML excerpt.\"] ]; puts(app.toXML(fragment, padding, \" \")); The padding variable ensures that the generated XML lines up correctly with the surrounding XHTML in the ASP page. You won't see this in the client side version. The first fragment creates the original unordered list output, and the new table based layout. The second fragment adds a spacer, then the third fragment displays the XML so that you can see the alignment at work. Now we can run the example code, which gives the following results: Tutorials © Syger 2008. All rights reserved. 57
    58. This concludes the JavaScript tutorials, I hope you found something to spark your imagination. All the scripts in these tutorials are available for download as two compressed archives; Scripts.zip and AspScripts.zip, both distributed under the GNU Lesser General Public License. Contacts Syger can be contacted for consultancy work on any of the topics mentioned in this article, by sending an email to info@syger.it. 58 © Syger 2008. All rights reserved. Tutorials
    59. John Leach I'm a professional programmer, and Chief Technical Officer of a small software house in Verona, Italy, called Syger. The name came about from being influenced by a drawing by Roger Dean, of ferocious, intelligent badgers, which I transposed to the Siberian Tiger, my favourite animal from childhood, hence Syger. Most of the work done by my company is consultancy and software development for other software houses. I originally started professional programming in the industrial automation field. At the time, systems were based on small 8 bit microprocessors, such as the Zilog Z80, or Intel 8085, with very limited memory, connected to input and / or output cards via a bus. Because of the limited memory available, most of the work was done in assembly language. The most exciting thing about this type of work is the wide range of problems which we had to solve. One month you'd be working on a data logging system, reading information in from a dozen serial lines, the next month it would be a plastic injection moulding machine. As more and faster microprocessors became available, it became obvious to me that writing in assembly language was not the solution. Each microprocessor had its own set of mnemonics, which meant you had to start over from scratch. About this time (circa 1987), I became interested in operating systems, and had moved my software development platform from CP/M to MS-DOS, on one of the early (and cheap) IBM PC clones. The Coherent operating system, from the Mark Williams company introduced me both to the Unix operating system and to the C language. I think I bought about every toolkit they supplied, which was also available in source code even at that time. Learning C was a huge step forward after assembly language. It allowed me to experiment with much more complex data structures, and, to me then, revolutionary concepts of dynamic memory management. Finding out about GNU, and the enormous amount of source code that they made available on CD-ROM was like giving a drink to a man in the desert. So I set myself up as a freelance consultant, and looked around for a software project which I could write in C. I found a company that produced electroencephalographic equipment, and who had the foresight to move away from a proprietary system, onto the IBM PC platform. The platform was MS-DOS, the program was written almost entirely in C, with a small quantity of assembly language for the data capture (via the printer port, believe it or not), and video display. This was probably the largest program I have ever written to date. Tutorials © Syger 2008. All rights reserved. 59
    60. By this time, C was becoming a straight jacket. My programming style had moved to producing data structures, with function pointers actually in the data structure to modify that data, which is probably as close to object oriented as you can get in C. The next obvious step was C++, but I have to admit that I found the syntax, and redundancy, a major handicap. Windows was still at an early stage, though Windows 95 was on its way, the programming tools were pretty poor, and the learning curve was extremely high. At about this time (1995) Java and the Internet came onto my horizon. Java was as much a quantum leap for me, as C had been before. No more memory management, object oriented, exceptions, documentation comments, library source code, this was all very fascinating stuff. I learned a little HTML, put together a few applets, and convinced my Internet Service Provider to give me some space on one of their servers to get me onto the World Wide Web. The site is long gone, sadly, but it helped move me on to web application development. I found another company, which developed enterprise software written in COBOL, using a relational database via SQL, but had a big contract for a web interface to their software. Their client didn't want the standard client software, written in Visual Basic. It was too early to use Java as the server software, so we used C via CGI. Fortunately, I had a free hand in the development process, so I built a templating system in Java, which took plain HTML, and converted it into C. It had a few novel ideas, of course, so that I could place C function calls in strategic places in the HTML templates. The results were quite satisfying. The C code could talk to the Cobol code directly, using the linker, the web pages were pretty small in size, so the web application was acceptably fast even on slow dial up connections. A couple of years later, Java was making an impact on the server side, so the whole application was rewritten using Servlets and JSP pages, which was very similar to our own templating system. It took about nine man months to produce the original application, and about three man months to produce the Java based application. We used the JNI to interface with the Cobol code. Working in a large software house gave me plenty of opportunity to work with new tools. I became a CVS fan, though I now use Subversion, and I got to brush up on my limited Unix skills, and learned how to tweak the Apache web server. The World Wide Web was moving on too. I learned the benefits of using CSS, and improved my JavaScript skills, though it took me a while to realise the full power of this programming language. At about this time I started working in closer contact with a German software house, which developed software using Microsoft products. They were also quite open to new programming ideas, which gave me the chance to put together development environments using Subversion, and Trac for simple project management. I still keep the original proposal original proposal document on my site. I was glad to get away from COBOL, and didn't mind learning to use Visual Basic, or 60 © Syger 2008. All rights reserved. Tutorials
    61. too much, though they are both severely limited programming languages. VBScript The relationship was profitable from both points of view. I developed a reasonable knowledge of Microsoft programming languages such as Visual Basic, and VBScript, we put Subversion to work, and made heavy use of Trac, though this was later replaced by Basecamp. Additionally, we explored inter process communications using XML-RPC, and developed applications using REALBasic, which seems a valid alternative to Visual Basic. For ASP development, I built a small JavaScript framework, which replaced the original VBScript code, reducing the lines of code by about 25%. I now spend most of my time divided between scripting languages and frameworks such as Ruby, Groovy, Ruby on Rails, and Grails, and my old time favourite, Java. Tutorials © Syger 2008. All rights reserved. 61
    SlideShare Zeitgeist 2009

    + Minh Chuc HoMinh Chuc Ho Nominate

    custom

    770 views, 0 favs, 0 embeds more stats

    More info about this document

    © All Rights Reserved

    Go to text version

    • Total Views 770
      • 770 on SlideShare
      • 0 from embeds
    • Comments 0
    • Favorites 0
    • Downloads 18
    Most viewed embeds

    more

    All embeds

    less

    Flagged as inappropriate Flag as inappropriate
    Flag as inappropriate

    Select your reason for flagging this presentation as inappropriate. If needed, use the feedback form to let us know more details.

    Cancel
    File a copyright complaint
    Having problems? Go to our helpdesk?

    Categories