Page 1 of 126

Does an Object Oriented MVC paradigm get Web Application Developers on track for Web 2.0? A study with Ruby on Rails.

P Shearan MSc Internet and Multimedia Systems October 2007
Faculty of Business, Computing & Information Management

London South Bank University

Page 2 of 126

ABSTRACT
The advent of AJAX, as a client-side technology, has heralded the introduction of Web 2.0, an environment of dynamic content, aimed at sharing information and collaboration, and producing desktop-like applications made available through web browsers. Web developers need to investigate the new technologies required for practical deployment of Web 2.0 and to prepare for Web 3.0. The majority of current web applications have been developed in transactional languages (PHP, PERL, ASP) but need to be moved to object oriented web Model-View-Controller (MVC) frameworks to allow developers to implement the new techniques in Web 2.0. Through assessment and development of a web application in the Ruby on Rails MVC framework, it has been demonstrated that the deployment of Web 2.0 technology becomes quickly available and repeatable. This allows the flexibility and speed of adoption of new features that customers of web application developers desire to stay competitive in the fast moving marketplace of web based business. During the research it has been highlighted that the transition to a framework such as Ruby on Rails could take as long as two months before a web application developer is fully productive in this new environment and that Ruby on Rails suffers from inconsistent sources of documentation. However, the benefits of fast and repeatable delivery of a Web 2.0 application in an object oriented web MVC framework like Ruby on Rails appear to outweigh the issues of migration to the new development platform from the existing transactional environment.

Page 3 of 126

ACKNOWLEDGEMENTS
Bellsy, without your constant support, encouragement and editing this never would have happened. Thommo, thanks for the kick up the “you know where” when I needed it most. Mark Josephs, my tutor, thank you for all the invaluable support, help and input.

“Full fathom five thy father lies; Of his bones are coral made; Those are pearls that were his eyes: Nothing of him that doth fade But doth suffer a sea-change Into something rich and strange.” Ariel‟s Song, Tempest by William Shakespeare

.........................3......21 Breaking the Page .....48 ...........1 2.0 APPLICATIONS IN DETAIL ..............0 ...................................................................................................................48 4..............................2 2.3.14 Web Application and Systems Development Tools ................................5 3....1 3....................................................27 THE WEB DEVELOPERS CONUNDRUM ...................................................................1.................2........36 Object Oriented Code – Relevance to Web Design ........................ THE CHANGING LANDSCAPE ..............4 3...................................................1 2.................1..........0 TO 2............................................7 2.....................................................13 WHERE ARE WE NOW? ................................................................32 SELECTING THE DEVELOPMENT ENVIRONMENT ..................................0 AND EVEN 3.......3 3...........................0 Application Architecture ..................................1.. Django...................0 DEVELOPMENT FRAMEWORKS .10 Web 1.......................0 and Web 2...................................................................................2 2................................................3....................1 2........41 Advantages of Ruby on Rails .........0 Services ...........................1 ABOUT THE PRACTICAL APPLICATION ..........................................................3.3....................................................2 3...5........................21 Tim O’Reilly’s Hierarchy of “Web 2..........................2 3..28 Prototype and Script.................................................................10 2.....................................3 3 WEB 1.................34 MVC – the Model-View-Controller ...............................................................11 Web 3.............................................................1 3..2 2....................................3........................1.14 What currently runs the Server-Side of the Web? ...................2 3.....................us......................................34 What is the “framework”...............................................................12 2..3...........23 Tagging ............................... 7 THEORETICAL CONCEPTS: EVOLUTION OF THE WEB .19 Mapping Technology to Web 2........27 CREATING THE ACTIVE DESKTOP IN WEB 2.......................2 2......22 Blogs ...............................2...........................3.....................................................0-ness”..............................1.....................................................2...............................................0 ....................................3 2..........1...........................aculo....................................................................................................................................34 Object-Oriented vs Procedural Development Paradigms ..........................................3 2.............................7 3..................28 AJAX – the great misnomer ............3 2......................................................15 Comparing Typical Web 1.....................................37 Why objects are good.......................1.......................................................................6 2.............0 ..........................Page 4 of 126 Table of Contents 1 2 INTRODUCTION .................................................19 A Business Layman’s view of Web 2........................9 2.......2......................................................0 ..................................................1 2.......0 – the O’Reilly Way ....24 Web Syndicated Feeds (RSS and Atom) .....5.............................................17 WEB 2..43 Test Driven Development with Ruby on Rails .......................................22 Ajax ............................................................................................2.............................24 Web Services .......45 4 PRACTICAL APPLICATION RESEARCH PROJECT ...........................0.....37 Potential Frameworks: CakePHP...............1......................38 Choosing Ruby On Rails ...........................................39 RUBY ON RAILS ...41 History of Ruby on Rails .......................................34 3...................................................................10 2...............25 Wikis ....1 3..............................................................................................................................................5 2...........................................0 ..........................................................3...............................5......................31 How AJAX works ............................3 WEB 2..................1...................................10 Web 2.................................................................................................................................................................................................4 2......................................22 Mashups .6 3........................................................................................5 2...................3 2................................8 2..3.......2 2...............3..........3..1.... and Ruby on Rails ...............1 2.................................4 2...2...........................................11 2...........19 Web 2.......

.....................62 Sprint 4: Implementing Tagging .......3 4.3.................................1 5............................................................................................................2 4............6 4..............................................3.practical application overview .......................74 BIBLOGRAPHY........................................49 PREPARING TO IMPLEMENT THE PRACTICAL APPLICATION .................5 5...................................................3...............4 4..................1......................................................53 A False Start with Ruby on Rails and Agile Development ...............................1.............................................69 Well designed Objects provide usability ..............................4 5.........3............2................79 APPENDIX C: FAMILY SCRAPBOOK APPLICATION ROR CODE (SPRINT 1-8) .............4 5...........68 Consistent Ruby on Rails Documentation is lacking ..........................................70 WHAT I‟VE ACHIEVED/DELIVERED .......................................................60 Sprint 2: Implementing Scrapbook Management.................................................51 Development and Production architecture .......................2 4...............................3 4.............................1......2 4...................3............................................................................................................................................Page 5 of 126 4..........66 Sprint 8: Providing extra Web 2....3 4.....78 APPENDIX B: STORYBOARDS FOR FAMILY SCRAPBOOK APPLICATION ..............................72 FOLLOW UP ......................3...............1............. 77 APPENDIX A: FAMILY SCRAPBOOK APPLICATION ENTITY DIAGRAM .........................................................................................................................................2.....................................................2 5.......53 Sprint 1: Implementing Photo Management ............3 4................70 If you keep faith with RoR there will be a breakthrough! ..3...............................................49 Applied Research ...........................................71 AND THE ANSWER TO THE QUESTION IS….................................................................2 5...........................63 Sprint 5: Implementing User Management ................64 Sprint 6: Adding GoogleMaps to Scrapbooks ..........72 REFERENCES .........1........................................9 4............1 4..................52 IMPLEMENTING THE PRACTICAL APPLICATION .......................8 4.............1...............................................................68 5.........................................5 4.....81 APPENDIX D: PHP & ROR COMPARISON CODE (SPRINT 0) .......................67 CONCLUSIONS AND FURTHER WORK ..68 Moving to an OO MVC from a transactional environment takes time ...48 Time taken to learn new development environment ...........................................114 .....................2....0 features .........................................50 Agile Development Process ......................68 Upfront Design is still vital ........................2 4.......................3 5....3..........10 5 Aims of the practical project......................................................................7 4.........................................................................50 Learning Ruby On Rails .............1 4.1 4...............53 Sprint 0: Compare PHP 5..................................3..................................................................................1......................................................................................5 WHAT I‟VE LEARNT ..............3.....71 WHAT I‟D DO DIFFERENTLY ....65 Sprint 7: Sorting Scrapbooks from the Web Page ................................................................................................1 5..................................... PHP 5 Objects and Ruby On Rails ................................................................3 5.........................................................61 Sprint 3: Implementing the User View ..1......................

............................Page 6 of 126 Table of Figures FIGURE 1: STAGES AND MAJOR LANDMARKS OF THE WEB .........................................................0 APPLICATION .................18 FIGURE 7: THE O'REILLY WEB 2.................33 FIGURE 12: DEVELOPMENT ENVIRONMENT SELECTION CRITERIA ....................10 FIGURE 2: PHP UTILISATION ON THE INTERNET.................................................................................................................14 FIGURE 4: TOP TEN MOST POPULAR PROGRAMMING LANGUAGES............................................21 FIGURE 9: A WEB SYNDICATED FEED THROUGH GOOGLE READER ..............................40 FIGURE 13: UNIT TEST EXAMPLE ... ............................................... MARCH 2007 .....26 FIGURE 10: NODES IN A DOM TREE ....................................................................20 FIGURE 8: VISUALISING WEB 2.......................................................59 .............. AND THEIR RELATIONSHIP TO WEB DEVELOPMENT...30 FIGURE 11: ANATOMY OF AN AJAX REQUESTANATOMY OF AN AJAX REQUEST .....................................47 FIGURE 15: COMPARISON OF AMOUNT OF CODE IN PHP AND ROR APPLICATION ..............0 APPLICATION ............................17 FIGURE 6: ANATOMY OF A WEB 2....................................................... MARCH 2007 .....................................0 (DON HINCHCLIFFE – SOCIAL COMPUTING)..........................0 MEME MAP ................15 FIGURE 5: ANATOMY OF A WEB 1............................................... AS SURVEYED BY TIOBE SOFTWARE.........14 FIGURE 3: VERSION DISTRIBUTION ON INTERNET WEBSITES RUNNING PHP......................46 FIGURE 14: FUNCTIONAL TEST EXAMPLE ...........

While many well known websites have emerged using Web 2. The advancement of the capabilities of web applications and the realisation of new web techniques over the last three years has led to change in the expectations and requirements of providers of web based business: To the clients of web application developers. RSS. and how much effort is required for a web application developer to learn one of these framework environments. and ultimately the customers of these clients. Wikis. Folksonomy. a conference was organised by O‟Reilly Media and MediaLive International [1] . A key part of this dissertation is to identify some important Web 2.us) a large number of sites are still at the Web 1. Web Services. Syndication. which .0. The topic of the conference was the newly coined phrase Web 2.icio. del. Mashups.0 stage. Flickr. This represented the first acknowledgement of the evolution of the World Wide Web. To the web application developer the challenge is to stay ahead of the market in technology terms while meeting the tight deadlines enforced by demanding customers.Page 7 of 126 1 INTRODUCTION In late 2004.0 features and demonstrate how these can be implemented using one of the new framework environments. the dynamic Web desktop. designed for implementing these features. Social Computing. Blogs. Tagging.0.0 technology and techniques (GoogleMaps. Along with this the developer needs to provide quality software. Facebook. the needs are simply to provide an up to the minute web experience to their customers that will involve all the buzz words and capabilities of the moment – Web 2.

Through personal experimentation. maintain. interviews with leading technology experts and independent research this report seeks to identify the possible transition path from tradition procedural coding such as PERL. PHP and ASP to the more modern object oriented approach of Ruby on Rails (RoR). through research of academic journals. A recent survey shows [13] that over 50% of all websites are still produced using well established but difficult to maintain environments using procedural type languages and development environments. articles and papers along with information published in books and on websites. enhance and reuse. or time required to identify. install. This dissertation intends to: Identify and discuss. These MVCs were chosen as they specifically address the needs of the web designer and will particularly focus on the development of an application using RoR. Does this happen now with Web 1. the key . learn and deploy a new development environment and produce competent.0 development environments? Generally not. Django Python and CakePHP MVC environments.0 type applications. web application developers will have the ability to meet the new requirements of their customers to provide Web 2. The proliferation of new web frameworks (discussed in chapter 3) indicates that through a move to object orientation and with the adoption of an Model-View-Controller (MVC) design paradigm. efficient and re-usable code in this environment while still managing to meet the demanding deadlines set by customers. The largest barrier to this transformation for web application developers is the “ramp up” time.Page 8 of 126 is easy to test.

the dynamic web desktop.0.0 capable environments. Discuss a number of Object Oriented (OO) MVC frameworks that have emerged to allow development of Web 2. As the selected framework RoR will be discussed in more detail and the reasons why it was chosen will be explained. Introduce and explain the practical application. that will be designed. specifically RoR. Family Scrapbook.0. Focus on a specific technical advancement provided by Web 2. and provide further insight into the business case to provide this facility. Through this research the need for a change of development paradigm is identified for the web application developer. tested and implemented. and the implementation of the practical application will be used to provide an insight into the learning curve that will need to be undertaken by web application developers to move from current technology to Web 2. . this needs to be balanced against the need for fast and responsive development and is discussed as the “web developers conundrum”.Page 9 of 126 transformations or “sea changes” of the World Wide Web. Django Python and Cake PHP.

Page 10 of 126 2 2.1 THEORETICAL CONCEPTS: EVOLUTION OF THE WEB WEB 1.0 AND EVEN 3.0 Tim Berners-Lee.0. THE CHANGING LANDSCAPE As with all things in the computing world a definitive.1. Since its inception in 1989 it has grown initially as a medium for the broadcast of readonly material from heavily loaded corporate servers to the mass of Internet connected consumers.1 Web 1.” . 1996 [2] original vision of the World Wide Web was: “designed originally as an interactive world of shared information through which people could communicate with each other and with machines. Figure 1: Stages and major landmarks of the Web 2.0 TO 2. concise and universally agreed set of defined terms for the evolution of the World Wide Web proves difficult to identify but the following stages have been common ground throughout my research.

Web 1. and JavaScript. The serverside architecture used in the open source community is commonly referred to as LAMP (Linux. 2007 [4] ) says: “Web 2. MySQL and Perl or PHP).0 is to provide seamless applications. accessed through web browsers and using urls to send http requests. Perl.1.0 was the original explosion of websites filled with static pages. The primary driver of Web 2. CSS. Chief among those rules is this: Build applications that harness network effects to get better the more people use them.” Web 2.0 went through many iterations with the adoption of technologies such as CGI. Within Web 1. O‟Reilly also details a four level model of . xHTML. without the requirement to upgrade or even know the version of the application you are using.Page 11 of 126 Web 1. which gain acceptance and improvements from user participation.0 is the business revolution in the computer industry caused by the move to the Internet as platform. whose company is credited with coining the term Web 2. and an attempt to understand the rules for success on that new platform. ASP). 2. Tcl. 2006 [3].2 Web 2.0 Tim O‟Reilly. XML.0 and is considered to be the most authoritative voice on the subject (Dion Hinchcliffe.0 is not a specific technology but a combination of technology developments along with a change in emphasis in the deployment of websites.0 the majority of the processing of information is performed on the server-side using procedural scripting languages (PHP 4. Apache.

Ajax (Asynchronous JavaScript and XmlHttpRequest(XHR)). Social Computing.0 also introduces the idea of community information. sharing information not just as pages served to the browser but made available by a number of methods such as tagging and RSS through to Web Services. Wikis. Blogs.4. This is not an exhaustive list but a good indicator of the term Web 2. it will allow for other methods like JavaScript Object Notation (JSON) and even text. frames [6]. Web Services introduces the concept of Representational State Transfer (REST). Ajax. introduced by Garrett in 2005 [5] is one of the main components of Web 2.0. While much of the communication between the client-side and the server-side is expected to be XML using XHR. While details of community information and web services are beyond the scope of this research. Further. it again indicates the importance of XML in the future of the Web. Web Syndicated Feeds (RSS and ATOM) are a short glossary of terms of the key concepts and technologies associated with Web 2. Tagging. it allows the changing of the contents of a web page without the necessity to reload the entire page or use artificial methods of providing dynamic updating of content e.g. These terms will be explored in more detail in following sections . Web 2.0 for Web Applications. Mashups. Web Services. originally defined in work by Fielding (2000)[7]. this is detailed in chapter 2.0.0. The use of the Ajax suite of technologies moves much of the processing requirements of a web application from the server-side to the client-side.Page 12 of 126 compliance to the ethos of Web 2. Ajax should lead to more dynamic and faster desktop applications using web technology.3.

In enabling Web 3. referencing and retrieval of multimedia objects is to be addressed as part of the Semantic Web. The Resource Description Framework (RDF) and Web Ontology Language (OWL) are seen by Schreiter (2007)[9] as core technology in the adoption of the Semantic Web. Web Services . SOAP and WSDL . also referred to as Web 3. Nykanen (2002) [11] describes that: "The Semantic Web brings to the Web the idea of having data defined and linked in a way that it can be used for more effective discovery. They make multimedia content meaningful.” Also. is to make all information on the Internet into a huge indexed database using many techniques. . automation.Page 13 of 126 2.0[8].are key and will be provided using XML as the unifying language says Paolucci et al (2003) [10]. and reuse across various applications.0 The basic idea behind the Semantic Web.1. Djeraba et al [12] say: “Semantic features involve varying degrees of semantics emphasized in multimedia information.3 Web 3.” Work on the Semantic Web continues. at pace. The issue of providing storage. within the research and academic community and generates much press but no real commercial mainstream deliverables at this point in time. In his talk to the W3C. integration.0.using standards such as REST.

While these figures cannot be taken as definitive they do indicate that PHP is the dominant server-side development tool in the open source web server side environment. These figures indicate that over 50% of websites still use procedural technologies.Page 14 of 126 This proposal does not seek to be comprehensive on the past and future of the web but is merely a scene setter. March 2007 Figure 3: Version Distribution on Internet Websites running PHP.net [13] identified in a survey over 10 million websites (Figure 2) that nearly 34% of websites used PHP as the server side environment. for example no discussion has been included on the relative merits of web server architectures or web portals. March 2007 . nearly 22% used Microsoft ASP and 44% had no discernable server-side environment (likely to use Java. 2.1 What currently runs the Server-Side of the Web? In March 2007. Nexen.2. Figure 2: PHP Utilisation on the Internet. Of the PHP environments identified only 16% were PHP 5 and above. see Figure 3.2 WHERE ARE WE NOW? 2. PERL or have no server-side processing).

While it is difficult to provide complete separation between web and system development. The list of programming languages provided in this article has been expanded to add information on how or if they relate to web application development.2 Web Application and Systems Development Tools In her recent article Paulson (2007) [14] identified the ten most popular programming languages according to a survey by Tiobe Software. C and C++ are used almost exclusively in major system development. Java is used extensively in the web but often in major systems development. as surveyed by Tiobe Software. and their relationship to web development.Page 15 of 126 2.2.0 and produced as Figure 4. Figure 4: Top Ten Most Popular Programming Languages. and Visual Basic is predominantly used in MS Windows applications. and if they are relevant to Web 1. if they are a procedural or object-oriented language.0 or Web 2. . the first four languages generally would be termed as system development environments.

C# is in MS ASP.0 server-side applications in the LAMP environment and will use JavaScript on the client-side.NET will generally be deployed using VBscript (a variant of Visual Basic). In the MS IIS environment ASP. Interestingly. Python is in the Django framework and Ruby in the Rails framework. there is concern about the ramp up time for learning and implementing the new development environments that introduce the object-oriented paradigm. . These Model-View-Controller (MVC) [15] type frameworks have been in the object-oriented system development environment. While web developers are aware of alternatives to the current LAMP type web development environment.NET 3.0 framework.0 style languages are in the top ten – C#. three Web 2. particularly Java.Page 16 of 126 PHP and Perl are currently deployed in the majority of Web 1. for some time but are now being designed specifically for web application development. Python and Ruby.

Figure 5. MS ASP. CGI (or similar if ASP) applications are mainly built using PHP 4. .0 Application Architecture Figure 5: Anatomy of a Web 1.0 Application [16] The anatomy of a typical Web 1. Perl or Java.NET.0 and Web 2.2. is generally described as client-side (requests for and rendering of (x)HTML documents and request for plug in applications) and the server-side (web server for handling requests from clients and passing on requests to the scripting and database environment through the Common Gateway Interface (CGI)).3 Comparing Typical Web 1.0 website.Page 17 of 126 2.

0 website. Figure 6 suggests an initial model and introduces the concept of the MVC framework into the architecture.0 Application There are many suggestions but as yet no clear anatomy of a typical Web 2. .Page 18 of 126 Figure 6: Anatomy of a Web 2.

3 WEB 2.0 fills the gap between a web browser and desktop applications.0 – the O’Reilly Way O‟Reilly media published their seminal and self aggrandised [18] Web 2.0 for uses of things other than social computing.3.0 application. it is interesting to gain an insight into the viewpoint of non-computing professionals on what Web 2. In an interesting perspective on Web 2.0 Before moving onto a detailed retrospective of Web 2. A number of these techniques will be implemented in the practical application for this dissertation. The CPA Journal. an article of Web 2. 2.1 A Business Layman’s view of Web 2.0.3. and facilitates collaboration and sharing”. In a specialist accounting publication. .2 Web 2. 2. It brings together documents and data scattered over local computers and the Internet.0 means to them.Page 19 of 126 2.0.0 APPLICATIONS IN DETAIL This section details many of the techniques used on a website that could be described as a Web 2.0 by Cong and Du [17] says: “Web 2.0 meme map produced as part of the kick off meeting for the joint O‟Reilly and CMP conference in October 2004 that launched Web 2. the article points out that small firms (like the accountancy firms this journal addresses) will gain the benefit of web based business application services that will become available to them “because they will cost only a fraction of what commercially designed software for large accounting firms cost”.

0 meme map represents a set of ideals and capabilities required from new web applications but does not seek to address the enabling technologies. The bottom layer of flesh coloured bubbles represent a set of vague or loosely defined concepts that people want to be reflected in Web 2. The middle box contains the firm concepts or mission statement for the delivery of Web 2.0 meme map represent. The top layer of bubbles represents a set of vendor products or available technologies and how they deliver Web 2. it appears to be this.0 capabilities now.0 Meme Map The Web 2. .0 applications. Figure 7: The O'Reilly Web 2.0 applications.Page 20 of 126 While I have struggled to identify an exact definition of what the different areas on the Web 2.

Through the use of web services and web syndication feeds – along with the proprietary application programming interface (API) – content and information can be gathered from third party sites without the need for them to be informed.0 Services The chart from Social Computing shown in figure 8. .3. shipping data. demonstrates the mapping between Web 2.4 Breaking the Page The key behind all the technologies used in Web 2. Figure 8: Visualising Web 2.Page 21 of 126 2.0 (Don Hinchcliffe – Social Computing) 2. Dave Thomas [27] says: “(Web 2.0 – the PAGE. partial pages. and code between the browser and the server to provide a more responsive and interactive experience” This concept can be further expanded to include application-to-application communications without the need for screen scraping and post processing of pages.3.3 Mapping Technology to Web 2.0 services (tagging.0 is by breaking the principle of Web 1. mashing etc) and the background technologies used to create this experience.0) is about breaking that core principle of the web (the page) and allowing your applications to deal in smaller units of granularity.

0-ness” Taken from his article on the hierarchy of web 2. 2.3. there are too many to mention. MapQuest. where individuals can manage a project but gain further productivity by sharing project information with others.5 Tim O’Reilly’s Hierarchy of “Web 2.6 Ajax Ajax will be covered in great detail in Chapter 2. Level 2 Level 1 Level 0 2. O‟Reilly provides a 4 layer hierarchy of how a web application can be compared for compliance to the ethos of Web 2. where an individuals set of photos gain added strength from sharing and tagging and BaseCamp. as it is more commonly known. Yahoo! Local and GoogleMaps are bracketed in this layer by O‟Reilly but I would argue that GoogleMaps with it‟s extensive API and publishable maps falls into Level 2.3. is a replacement for the personal web page where an individual created web content using html or an html editor. An example of this would be iTunes which is a music and video mangement system but becomes a media jukebox and store when connected to the Internet. The communication between people and application is made possible by the connectivity of the Internet. the better the application becomes because of the added richness of content.0. Good examples of this are WikiPedia and Amazon reviews.0 applications [19]. An application exists offline but gains additional features when being online. The application is a useful tool to an individual but as they share the information with others the better it becomes. The application was launched as an web application but if the information was locally cached it would still work as well.5. The Web Log or Blog. Many of the . Blogging sites.0: Leve1 3 An application that can ONLY exist on the web.7 Blogs Blogging is one of the earliest and most widely used forms of social computing and the ideals of Web 2. have replaced the need for hard work and maintenance of a website with an easy to use web application. Good examples are Flickr.Page 22 of 126 2.3. The more people use the application. An application that can exist without the web but becomes much richer when using it.

in the Blog without the need for regularly visits to the blog site to look for updates. This is a mashup as it uses GoogleMaps API in a way in which they have no control.8 Mashups A mashup is a web application that combines it‟s own information with the information and content source from third party websites.3. 2. Other sources like web services and web syndication feeds may then be added in to the application to further extend the mashup. fonts and colours of the entries or will allow pasting from the authors favourite editor. which allows interested parties to keep abreast of the latest entries. . The third party will not be aware of the use of it‟s content and the mashup will normally add value to the source from which it is taking information.Page 23 of 126 social networking sites like FaceBook. In this dissertation the practical application will use the api from GoogleMaps to overlay pin markers to a location on a map and link to photos. A Blog is presented in the form of a reverse chronological list of entries and allows readers to add comments and links to the blog. MySpace and Bebo also provide blogging facilities. The blogging application will normally provide an editor and allow the creation of a template to control the styles. reviews and blogs associated with that location. Most Blog applications incorporate a web syndication facility.

Searches will then be available on a single tag or a combination of tags to find items of similar interest. Amazon.3. is used by some of the most respected sites on the Internet. Web services can be open and freely accessible or limited by secure connections.Page 24 of 126 2.” Tagging will be used in the practical application for this dissertation. The tagged entity could be such items a web page. a photo. A number of architectures exist to provide web services but the three most popular are. Hellsten [30] says: “Tagging. A remote web service will provide a definition of the objects available for query and the methods that can be used to access the object. Yahoo and Google allow their users to categorize and link together a variety of information with the help of tags.9 Tagging Tagging is a way of associating and identifying commonality between various disparate entities.10 Web Services A web service is a programmatical way of accessing published and formatted information from a remote source. a blog entry or a book review. 2. as quoted by Marshall [20]: SOAP: Simple Object Access Protocol REST: Representational State Transfer . Photos will be able to be tagged by the owner and individuals. For example.3. which is done by assigning a set of tags (keywords) to an entity.

and DELETE. UPDATE. SELECT. and DELETE operations available in HTML which are often mapped onto the four basic SQL operations INSERT. though in an interesting development RoR release 2. Read.3. REST adopts CRUD (Create.Page 25 of 126 XML-RPC: Extensible Markup Language Remote Procedural Calls SOAP is a World Wide Web consortium (w3) standard and an extension of the more straightforward XML-RPC.0 technology. Update. SOAP further extends the content delivery through enriching the XML document to define the service using the Web Service Description Language (WSDL).0 will enhance its native support for REST while dropping it for SOAP and XML-RPC (they will still be available as a plug-in). PUT. 2. Delete) and maps them to the standard POST.11 Web Syndicated Feeds (RSS and Atom) The move from screen scraping technologies to author controlled syndicated feeds is another main advance of Web 2. GET. a debate rages in the computing world over the relative merits of SOAP and REST. As always. Both provide programming to operations and are supported by the further standard implementation of content delivery using XML. The idea of a syndicated feed is to . Most web frameworks. The REST interface is more lightweight than SOAP as it is connectionless and uses universal resource identifiers (URIs) rather than method calls. In the practical piece of this dissertation will use the GoogleMap API web service to gather geocoding information for the location object and plot it as a pin marker. including RoR and CakePHP provide support for web services that will convert objects into WSDL thus making the implementation straightforward.

or Atom . The browser or news reader then regularly checks for changes and updates. both of which use XML as their base Figure 9: A Web syndicated feed through Google Reader The receiving individual will update his/her browser or news reader application (such as Google Reader) with the syndication feed. Generally a syndicated feed is implemented by a content provider. provided in the XML document provided by the syndicated feed along with a link to the actual article on the syndicator‟s website.Page 26 of 126 “push” out the latest updated information from a website therefore removing the need for an individual from having to check a website for updates. The browser or reader will then display the headlines. normally by dragging and dropping the icons in the above paragraph. . see figure 9. using either Really Simple Syndication (RSS) technology. anything from the BBC news website through to an individuals Blog.

4 THE WEB DEVELOPERS CONUNDRUM The move from Web 1.3. Commit now to new enabling technology within their web application development thus suffering the shorter term pain of learning new techniques and tools . navigated by resource locators. Without these changes vast tracts of existing websites may become obsolete due to their inability to incorporate these functions.0 in context) is Wikipedia. There is also the need to incorporate blogging and wikis into web applications.12 Wikis A wiki is a lightweight web database that allows contribution and editing of entries through a standard browser. and identified by search tools to an idea of community information identified by tags.0 requires a transition from a mass of isolated information.Page 27 of 126 2. To ensure website development meets the requirements of Web 2. The most well know Wiki (and one of the best examples of Web 2.0 and Web 3. 2.0. and served out by RSS channels and the need to introduce Web Services.0 to Web 2. many are freely available for instant contribution and updating. web application developers need to make a decision to either: a. XML is the leading facilitator of information sharing in Web 2.0 and beyond and is a cornerstone of the new technologies. accessed through links. Also. These changes in use of the Web translate into the need for development or upgrade of websites using an appropriate paradigm. While some entries are controlled and invigilated.

Page 28 of 126 OR b. Garrett‟s acronym while not radical in technology terms. using the RoR MVC framework. „Family Scrapbook‟. While it is intended to incorporate a number of the Web 2. it is expected that this application will make widespread use of AJAX to give the web pages a more dynamic.0 techniques discussed in the previous chapter.1 AJAX – the great misnomer Jesse James Garrett coined the term Ajax in his 2005 article [5] it was not a new technology but a neat term to describe the ability to create dynamic web client side content. Developers of web applications need to consider a balance between the need to deliver new content to tight customer deadlines and looking to future proof their work with some design considerations. The main focus of the „Family Scrapbook‟ will be the creation and display of associated photo and location information. neatly encapsulated the basic techniques and requirements needed to bring abilities of a desktop application to web based . active desktop like feel. Wait to see how the new techniques are adopted and potentially be left behind by their competitors in the products they can deliver to their customers.5. 2. 2. no new language or development paradigm was proposed.0 The major focus for the practical application of this dissertation will be the development of a website.5 CREATING THE ACTIVE DESKTOP IN WEB 2. Pages will be updated without the need for page reloads and highlighting effects will be used to draw the users eye to updates and changes.

JavaScript and the DOM. In his book Ajax on Rails. allowing the user continue working on a page while awaiting the return updates from an AJAX request. Raymond (2007) says[21]: “Ajax is the use of browser native technology (e. J . However. XMLHttp was originally implemented as an ActiveX object and was solely used by Visual Basic in its early days. In fact.While AJAX can and often does act Asynchronously. JavaScript Object Notation (JSON). A . Javascript And XmlHTTPRequest(XHR).g.JavaScript is not the only client side language available – Visual Basic and Flash scripting can also be used to call the core XHR object. but not Flash) to decouple the user interaction processes from server communication processes” One thing the AJAX acronym does not credit (though Garrett does in his article) is the importance of standardised XHTML and CSS adherence in the web client. As with most computing terms this is a complete misnomer. AJAX can also be set to block further user activity until updates have been processed. namely the . The term AJAX became a label and easy to comprehend concept for dynamic web client taking the technologies out of the development labs and into the marketplace. X . images etc can all be proved as responses to the XHR object. Ajax stands for Asynchronous. plain text.XML is not the sole communications mechanism used by the XHR object. HTML.Page 29 of 126 applications.

Using the JavaScript notation document.getAttribute(„Customer‟) would return the string “Allison White”. This organisation is key to the effective use of AJAX.Page 30 of 126 Document Object Model (DOM).getElementById(„Invoice‟). The language independent DOM provides a structure to a web page that allows traversal through a XHTML document as an object and the elements contained within are locatable and addressable. . the DOM contains an element node (Invoice) which contains attribute nodes and sub elements. Figure 10: Nodes in a DOM Tree [22] In Figure 10.

These include the back button.us While it is possible to write AJAX functions using the raw XHR object (and I done this in the first piece of work when uploading photos using AJAX and PHP).5. These libraries are bundled with RoR and most of the other web MVC frameworks Prototype simplifies the process of creating XHR objects.City attribute which would be updated on the web page. While the name AJAX is often derided as a marketing term due to its inaccuracy. or AJAX.Updater. the important issue is that the Garrett‟s article was the point that married common understanding of the needs to produce interactive.aculo. which provides an object with the response from the remote server. 2.Page 31 of 126 Similarly document. responsive web applications with technology which enabled these requirements. This enabled development teams. The two main methods are AJAX. product managers and entrepreneurs to produce the elevator pitch [23] to raise funding or management commitment to projects using AJAX type applications.us JavaScript framework libraries. saving links and providing rich media content.Request. provides error handling and browser type discovery through its AJAX object. most books recommend the use of the freely available Prototype and Script.aculo. While AJAX provides a wealth of improvements in the dynamic abilities of a desktop to a web application there are currently a number of shortcomings that need to be addressed.setAttribute(„City‟.getElementById(„Invoice‟).‟Princeton‟) would update the value of the Invoice.2 Prototype and Script. which will actually update the webpage attribute without the need for .

us is built on the Prototype and provides the many screen effects used throughout the Family Scrapbook application. The server will handle the request and return results in XML. 2.us also provides the drag and drop capabilities used to reorder the Scrapbook photos in the practical application. Any effects seen on the web page after an Ajax update e.Request in the GoogleMap mashup of the Family Scrapbook application. The JavaScript event will create an AJAX request via the XHR object and send information back to the server. An event on the web page (mouse click. clicking a submit button etc) will trigger a JavaScript event.5. Script. highlighted images.aculo.3 How AJAX works The diagram in Figure 11 gives an outline of the workings of an Ajax request. I have used AJAX.Page 32 of 126 further coding.g. JSON or HTML to the XHR object JavaScript will then use the results in the XHR object to update the appropriate web page XHTML elements . Script.aculo. blind down effects and the like are all part of the Script.aculo.us framework.

Page 33 of 126 Figure 11: Anatomy of an Ajax RequestAnatomy of an Ajax request [24] This is a simplification of the process and I have suggested a number of sources of further information on AJAX in the bibliography. .

As Dave Thomas [27] said: “When developers first started producing web applications. The main idea of the MVC architecture is to decouple the knowledge of a web application (the data – usually held in a database) from the output mechanism (usually pages in a browser).1 SELECTING THE DEVELOPMENT ENVIRONMENT WEB 2. and event handling in one big ball of code” An MVC uses the controller to handle requests coming from a requesting source.1.1 What is the “framework” In this document and many other pieces of literature the word framework is used to describe a type of development environment. database access. business logic.0 DEVELOPMENT FRAMEWORKS 3. rather than on domain-specific functionality of the application‟s features.1. interacts with the model to gain the required knowledge and . they went back to writing monolithic programs that intermixed presentation. The collection of reusable features that is developed is commonly referred to as the applications framework” 3.2 MVC – the Model-View-Controller All the development environments under consideration support and attempt to enforce the MVC architecture on web applications under development. browser or web service. a large portion of development effort is spent on writing general purpose components that can be re-used throughout the main application. A good definition of the framework is provided by Guzewich et al [25]: “For many software systems.Page 34 of 126 3 3.

2. It enforces validations.g. 3.1. it will know what size image to show for each photo and how many photos to display per line but the actual names and locations of the photos will be passed to the view in an object from the controller which was populated by the model . as it would be transparent to anything but the model. which could include ensuring that certain fields must be present for an object to be permanently stored by the model. This means that a model could move from MySQL to Oracle without any changes being made to the rest of the application. the minimum price of a component cannot be less than £1 etc.2 The View The view provides the output from the application and presents data provided by the model through the controller. business logic can be enforced by the model e.2.1 The Model In web application models. so the actual underlying database is unknown and irrelevant to the controller. All calls from the controller to get information are handled by the model.Page 35 of 126 passes the knowledge to a view which is rendered in the appropriate format to the requesting source. a single model generally represents the object definition of a database table. enforcing enumerators so that a file type field must conform to a limited number of options. If a „scrapbook‟ view is passed 100 „photo‟ objects to display.1. The view has no knowledge of the contents of the model other than how to present it. 3. Also.

This is known as encapsulation or data hiding as the object can only be affected by its methods. If appropriate it will get from or store information in the model and ask the view to render the appropriate response to the user. Object-Oriented– creates a model of a real world object called a class. that defines properties of the object and the methods on the way that object behaves. Procedural development is generally quick to produce and the code easy to read. 3.1. Procedural Based on a group of procedures that perform a function on data.g.3 Object-Oriented vs Procedural Development Paradigms There are some basic differences between the two development paradigms. 3.1. a set of functions and the data is updated when appropriate.2.Page 36 of 126 A view may also present a form to the end user asking for information to input but any processing of the information put into that form will be handled by the controller and if permanent storage of the information is required the controller will communicate with the model.3 The controller The controller handles and validates incoming user requests from the users of the application. Inheritance means an object can inherit the properties and methods of another object e. If many workflows intersect on the same piece of data it may get changed in unexpected ways. The problem is that any procedures are normally based on a workflow. a man .

This means that each object can be created.1. In the MVC architecture discussed earlier in this chapter.1. The other major advantage is the ability to build testing throughout the lifecycle of the application through class tests. the model handles all encapsulation. In the terms of the three development environments discussed in this thesis. The methods can be updated or altered to improve logic or performance without affecting the whole system as long as the methods still perform the tasks stated originally. Unit testing performs class tests. a class can be reused by any other application. 3.5 Why objects are good A good example of the beauty of objects is demonstrated in the photos PHP object used in Sprint 0 of the implementation. 3.Page 37 of 126 object would inherit a human object and a father object would inherit a man object. maintained and implemented as individual building blocks for the application in the knowledge that they will react as expected when called.4 Object Oriented Code – Relevance to Web Design Encapsulation or Data-hiding is probably the single biggest advantage for web development using an OO paradigm. Later the photos were stored in a single . Finally. Encapsulation means that all access to data is strictly controlled by the methods of the class that provides access to the state of the underlying object. Each class can be treated individually to ensure that the object handles all input and output without the need for a fully working application. Early in the development cycle the photo files were stored by the object in directories representing the gallery and were retrieved from these directories when requested.

6 Potential Frameworks: CakePHP.0. Django. CakePHP supports both PHP 4 and 5. The page designer did not need to know any changes occurred at the back end. 3.Page 38 of 126 directory and a database entry was made for each photo denoting which gallery was the photos home gallery.6.3 CakePHP CakePHP is rapid development framework for PHP and is inspired by RoR (this is a quote from the CakePHP website [26]). Django utilises a MVC framework and includes support for AJAX and syndication feeds. the MVCs can be implemented using either transactional or object-oriented code.6.1.1. . It provides an MVC framework for developing web applications in PHP.6.1 Django Django is the Python Web Framework. 3. 3.1. These changes were made in the object but retained the same interface/method behaviour.2 Ruby on Rails RoR is a relatively new web application development environment based on objectoriented development techniques. Python is an object oriented scripting language. and Ruby on Rails 3. Django is a relatively new framework introduced specifically to allow Python developers to create web based applications.1. The market share of RoR is growing fast and claims to be an object-oriented language (Ruby) using an MVC framework (Rails) and is designed specifically for web development including all the functions required for Web 2. which has gained a large user base.

Django and RoR. the framework environment was far superior and quicker to produce product and test through.1 PHP 5 v Ruby on Rails – a false start In the initial proposal document for the dissertation I suggested that the practical application would be built and compared using two different development environments – PHP 5 and RoR. As has been detailed at length in this dissertation one is a programming language and one is a development framework. when I moved to produce the second part of the application.1. I discovered an uncomfortable truth.7 Choosing Ruby On Rails A major decision in this thesis was the selection of an appropriate tool to produce the practical part of the dissertation. I decided to create set of straightforward criteria to choose between the CakePHP. . the photo gallery in RoR. Unfortunately. the photo uploading facility.1.7. It is unfair to try and compare PHP 5 to RoR but that the comparison should be between CakePHP and RoR. 3.7. in PHP 5 using its object capabilities. My attempt to compare PHP 5 with RoR was completely misguided.Page 39 of 126 3.1.2 Selection Process and criteria Having narrowed the field to three possible development environments and having had an abortive and time-consuming diversion into developing an object model with PHP 5. Some time was spent on designing the initial piece of the practical application. 3.

detailed in Figure 12 were based on the ability to support an open source environment. be easy to install. quick to configure. straightforward to produce a simple page representing items in a database and have available supporting literature (I like to have books around to refer to rather than on line references – it is a personal choice). Apart from time to install. . RoR was equal or superior to the other two platforms.Page 40 of 126 CakePHP Open Source Native MySQL Support Runs on Mac OS X Runs on Red Hat Fedora Time to install Time to create database Time to create album page Books available (Amazon) Yes Yes Yes Yes 2 hours 2 hours unfinished 0 Django Yes Yes No Yes 7 hours unfinished unfinished 3 Ruby on Rails Yes Yes Yes Yes 3 hours 30 minutes 5 hours 16 Figure 12: Development Environment Selection Criteria The criteria used. RoR provided awkward to locate and install a native driver for MySQL 5 and Django required numerous „makes‟ to install correctly. CakePHP was quick to install. be available on my development and production environment.

1 History of Ruby on Rails Designed by David Heinemeier Hansson (commonly referred to as DHH) at the company 37 Signals to create Basecamp. it‟s main advantages and introduces the RoR concept of embedded test driven development. it was little used in Europe or the United States until the advent of Rails. RoR was first released in July 2004. Statically typing enforces strict adherence to the defined variable type and unless specifically allowed within the program will produce an error if assigned a new data type without being handled by . deploy and maintain web applications” Rails (the framework itself) is written in Ruby. Dynamic typing allows more flexibility in the way the variables are assigned a type when the program is running.2 RUBY ON RAILS This section looks to provide an overview of how RoR was developed. Ruby is an object-oriented programming language written by Yukihiro Matsumoto and first released in 1995 [28]. While it gained popularity in Japan.2. Ruby is a fully functional object-oriented programming language that supports dynamic typing. for example. an integer can become a string or more importantly a decimal without the need for specific transformation code and will rely on validation in the object before being stored in. a database. unlike Java and C++ which are said to be statically typed.Page 41 of 126 3. a project management system – RoR was born. 3. Described by Dave Thomas [27] : “Ruby on Rails is a MVC framework that makes it easier to develop.

Apple Mac OS X and MS Windows. Static typing is said to be “type-safe” avoiding an unexpected behaviour due to variable types during program execution time. . RoR provides all the tools necessary to create a web application except a persistent database and a web server. The distinction between dynamic and static typing is an area of heated debate in the development community – just google “static vs dynamic types” to get a feeling for this but the flexibility of dynamic typing is key to the success of Ruby.Page 42 of 126 the object. Dynamic typing allows for more flexibility but can cause issues at validation or storage time or with calculations if the variable has been transformed to an unexpected type. Postgres and SQLite and a number of major commercial databases including Oracle. It is provided through an open source license and can be installed on all major platforms including many Linux variants. WEBrick is not recommended as a production web server as it is single-threaded and lacks the security. The RoR framework provides a small lightweight web server called WEBrick that can be used for a development and test environment. support is provided for all the major open source databases including MySQL. DHH and 37 Signals are keen Mac OS X exponents and recommend this as the development environment While RoR does not provide an underlying database. and maturity of Apache and MS IIS. DB2 and SQL Server. It allows programs to be added to a running application without the need for recompilation and provides a greater margin for error in the creation and storage of objects.

Each new iteration of the RoR development environment is said to be a further extraction of work undertaken by the RoR community. the configs directory contains specific information. It .Page 43 of 126 3. so that any new feature or library within the framework reflects a real world need. models and views – this is where RoR expects the files relating to these parts of the framework to exist. for example.2. and making it a framework created RoR. Hence the RoR framework is said to be an extraction Convention over configuration When a new RoR application is created by running the command: rails new_application_name A complete directory structure containing all the required elements to run a new application along with the necessary configuration and template files are created. This abstraction allowed DHH to understand the user led requirements for an MVC framework that would easily and consistently create web applications. of how to connect to the database. The db directory contains the database schema. All these conventions could be overridden if wished but mean that the developer just concentrates on developing the application not building the framework. the Basecamp application. The app directory contains three sub directories controllers.2 Advantages of Ruby on Rails Guiding principals of RoR are the following (and reflect the introduction to Ajax on Rails by Scott Raymond [29]): Frameworks are extractions Extracting the infrastucture of the original project.

Any view that is requested from a web browser or a web service is rendered by the controller and updated with the current state of the model. The rendering negates the need for pre-formatted pages . This principle is common to application developers but maybe less well enforced in web applications. Don‟t Repeat Yourself There should be only one. If another migration is created called album [singular of albums] with a field called photo_id. RoR will create a database table called photos with a unique column called id for each row and a model in the framework called photo. RoR has assumed that there is a one [album] to many [photos] relationship – hence the name “Opinionated Software”. and a single line is added to the photo and album model files. this “opinion” can be overridden in the configuration of the model. authoritative source for each piece of information. Opinionated Software Terrible name.Page 44 of 126 may be argued that this is a restrictive environment but it really does concentrate developers on development. It will now understand that photos are a number of photo objects each uniquely identified by the id field. Adherence to an MVC framework should aid this aim by removing storage of data from the output source. RoR will automatically assume that this relates to a unique id field in the photos table. As with all things. great concept! If you want a database table to contain photos [plural] then you ask RoR to create a migration called photo [singular]. Without any major configuration work.

3 Test Driven Development with Ruby on Rails RoR supports the concept of test driven development (TDD). All the view needs to do is format the output and needs no knowledge of the previous or current contents of the album. . Without testing and the ability to perform regression testing (testing that everything that used to work before a new feature was added still works) has been time consuming and poorly done. 3.Page 45 of 126 and ensures that duplicate and potentially out of date information is not output to users of the web application. functional and integration that are built round the Test class using assert methods. If a user requests to view the photos in an album. TDD assumes the following four steps in each stage of development [30]: Write a test for a specific piece of functionality Ensure the test fails (as the code hasn‟t been written yet) Write only the code necessary to pass the test Refactor the code to ensure elegant and simple design RoR provides three types of testing. Testing is often an afterthought in software development and has traditionally been hard to achieve in web applications and heavily reliant on manual tests. An assertion will either be true and a test will pass or false and a test will fail.2. unit. the controller will ask the model for the current contents of the album and pass that to the view for formatting.

:photoType => „jpg‟ ) assert_equal 'mates'.3.2. Figure 14 shows two functional tests: .3. unit tests for a real web application will be far more complex and contain many assertions but this illustrates the ease of testing in RoR. photo. class PhotoTest < Test::Unit::TestCase fixtures : photos def test_new_photo photo = Photo. They are tests for use cases to ensure that a call receives the appropriate response.2 Functional Testing Functional tests are used to test complete controllers and imitate the calls made by web browsers and web services.photoName end end Figure 13: Unit Test Example Obviously. 3.1 Unit Testing A unit test is used to assess the functionality and accuracy of an object.create(:photoName => 'mates'. Figure 13 shows a simple unit test that creates a new Photo object then tests that the object responds to the correct name and method and that the value of the photoName attribute matches the string „mates‟.2.Page 46 of 126 3. :photoTitle => 'Luke Matt and Me'.

:password => 'shearan' assert session[:user] assert_response :redirect end def test_should_fail_login_and_not_redirect post :login.2.NEW @response = ActionController::TestResponse. . The test assertions then check that a user session has been created and that the user is then redirected to the next page.3 Integration Testing Integration tests can span controllers and would allow a complete end to end test. These tests ensure that the login part of the system is functioning correctly. 3. If these are true then the tests will pass. :password => 'bad password' assert_nil session[:user] assert_response :success end Figure 14: Functional Test Example The first attempts to access the login controller and post values „peter‟ as the login name and „shearan‟ as the password. :login => 'quentin'.new @REQUEST = ACTIONCONTROLLER::TESTREQUEST. :login => 'peter'.Page 47 of 126 class AccountControllerTest < Test::Unit::TestCase fixtures :users def setup @controller = AccountController.new end def test_should_login_and_redirect post :login. For example an integration test would combine the tests shown in Figures 13 and 14to prove that a user called „peter‟ could successfully log onto the system and add a new photo to the photos table.3. The second test posts incorrect user values and uses test assertions to ensure that the user session was not created and that the user was not redirected to the next page (the assert_response :success represents the object handling the incorrect login correctly).

4. Thirdly. Firstly. to answer the question posed as the title to the dissertation: “Does an Object Oriented MVC paradigm get Web Application Developers on track for Web 2.1. Secondly. Database design and storyboards will be included in the appendices to this document. tested application. Any major conclusions will be provided in the final section of the document. explains the development and deployment environment. As this MSc is a conversion course.Page 48 of 126 4 4. is the application and implementation of skills learnt on the previous units of this MSc course in Internet and Multimedia Systems. is to demonstrate a practical implementation of a number of the features of Web 2. Finally. The application is support for the research project to provide practical proof of the researched benefits of an OO MVC framework.0?” .1 Aims of the practical project There are a number of reasons to attempt a practical element to this dissertation submission.0 technology in an application. provides the project design plan and includes discussion pieces on discoveries and deliverables at various times of the project. details the basic features to be provided in the application. it is not intended that the practical application will be a production ready. is to demonstrate the ability to quickly comprehend and implement an application in a new development environment.1 PRACTICAL APPLICATION RESEARCH PROJECT ABOUT THE PRACTICAL APPLICATION This section identifies the practical research project.

Creating and storing relationships between multimedia files. location information and blogging text. Storing location information. a new platform (e.g linux to windows) or if a current system needs to be maintained 4. install and become proficient in the new language. The practical element of this project will be the definition and development of a web based application to share multimedia alongside relevant blogging and location information between family and friends.1.g moving to object orientation).2 Time taken to learn new development environment A key to the decision on moving to any new development environment is the time taken to assess.1. Referencing the files from a database.3 Applied Research . identify the main components and processes within that part.practical application overview A web application called “Family Scrapbook” will be the practical focus of this MSc dissertation. learn. Tagging the files and blogs. Storing blogs (blogging text) in a database. It comes with extreme difficulties if the new development environment uses a new paradigm (e. The applied research will investigate various parts of the anatomy of a web application.Page 49 of 126 4. and demonstrate the differences between a transactional and an object-oriented approach to the development and deployment of that part Key parts of the anatomy that will be investigated are: Storing and Tagging Information Storing multimedia files (photo. video and audio). .

4. Agile is a development process built around short development cycles (Sprints) that have a defined.2 PREPARING TO IMPLEMENT THE PRACTICAL APPLICATION 4. demonstrating the advantages of this approach over the transactional approach. Flickr) which provide many facilities of the practical application that is irrelevant. Agile is said to favour software over documentation and is based on Extreme Programming (XP). Applying constraints to information access Controlling access to stored information. Displaying and navigating to tagged information. . Creating constraints beyond the traditional user/group type access While this functionality is not unique and there are some commercial applications (e.2.0 technologies implemented using an object-oriented paradigm.1 Agile Development Process The practical application will be developed using the Agile development methodology supported by the creators of RoR. which was introduced by Kent Beck (1999) [31].g. Creating mashup web pages using the GoogleMaps API. prioritised customer feature list and encourage customer interaction and comments. The practical application is an example of the use of Web 2.Page 50 of 126 Display and Outputting Information Outputting information to a web page.

Page 51 of 126 4.2.2 Learning Ruby On Rails At the outset of this dissertation. I did a unit on this course that taught Java for one semester. PHP. I have limited previous experience of Perl. I have no experience of RoR and limited experience of object oriented programming. C and COBOL programming along with some experience of shell programming for Unix .

Page 52 of 126 administration.com and is accessible on the Internet. 4.4 (Tiger).shearan. I intend to start the learning process in mid-June 2007 and be ready to start work on the practical application by 1 September 2007. http://www.3 Development and Production architecture As detailed previously the majority of the work to be undertaken in the practical project will be using the RoR development environment.rubyonrails. This was chosen as the development work can be undertaken at any physical location and can act as a self contained environment. The software platform is: . As there are limited training courses. I could not be described by any stretch of the imagination as a programmer.2. I will attempt to learn RoR using the tutorials and books recommended on the RoR website. a linux based open source operating system. The development and testing work will be done on an Apple MacBook running Mac OS X 10. This system already hosts my personal website www.org/. some work will also use PHP 5 and all storage will be provided by MySQL. The production system will be a x86 Intel platform running Red Hat Fedora 6.

Page 53 of 126 4. At this time I was extremely concerned that after three months of study that I was nowhere near able to implement the model I required. mainly through lack of knowledge and understanding of RoR. 4.3 IMPLEMENTING THE PRACTICAL APPLICATION RoR code for the final application can be found in Appendix C. . In early October. The entity diagram for the database design and object models for the Family Scrapbook application can be found in Appendix A and the storyboards for the main web pages can be found in Appendix B. PHP code for Sprint 0 can be found in Appendix D. PHP has until the latest version been a transactional language but version 5 introduces the ability to produce object constructs with many of the capabilities of an objectoriented language while retaining the ability to be used as a purely transactional language. I decided at this point I needed to start again but this time I needed to understand and document the model up front and that I needed to produce a storyboard to have a planned user interface. For quite some time during the second iteration of the project I was still struggling with the implementation of objects and understanding the syntax of RoR but at some point in mid-October I realised that I has become very comfortable with development environment and that the application was starting to take shape. It is intended to confirm that RoR is the correct choice for the practical application development environment.3.2 Sprint 0: Compare PHP 5.1 A False Start with Ruby on Rails and Agile Development When attempting to build a RoR application from scratch using Agile development techniques my knowledge of RoR was quickly exhausted and my application was poorly designed and implemented. PHP 5 Objects and Ruby On Rails The aim of this sprint is to undertake two significant tasks in both PHP 5 and RoR and compare time and ease of development. 4. I sat back and reviewed my code and realised I had made the basic error of trying to write a transactional application in an object oriented language.3.

These characteristics of PHP 5 provided the ideal opportunity to highlight the differences and advantages of an object-oriented approach in “Family Scrapbook” web application.Page 54 of 126 PHP also features a set of libraries for manipulating image files. .

php Skills Used Allows users to browse.php Library Files filedir. The page is redrawn after upload as AJAX is not implemented. Web 2. error checking and the ability to affect the image file are controlled by the object and it‟s associated method calls.Page 55 of 126 Sprint 0. this is in the object. All control of the status. The harness makes use of PHP‟s built in image manipulation libraries. select images to upload and display the uploaded images.php justfile. The OO approach greatly reduces the workload and complexity of the harness program called initially by the “Submit” button. The files are stored on the web server. . The harness creates an instance of the photo object with the details of the image file.php Include Files connect.0 functionality None.1: Uploading pictures with PHP 5 Main Files testgall. The harness program simply executes some method calls to the object but has no need to know the state of the image file.php photo.

This demonstrates the practical implementation of the Google Maps API to embed a mashup within a web application.Page 56 of 126 Sprint 0. Main Files mapit. generates Latitude and Longitude coordinates using the API.js This example implements the GoogleMaps API.0 functionality Mashup. . Web 2. Using the information entered into the “Add a new location” form the web page takes a number of actions”: It validates the input fields using Javascript.php Skills Used Include Files dbconnector. this API is an object-oriented piece of code that extends the basic XHR object to allow the placement of information icons and overlay routes onto a map. The new entry is updated on the map.php locations.php process_form. an XHR Object is created in a Javascript function.php Library Files functions. the XHR Object is then passed a Universal Resource Locator (URL) containing the script to be running and a set of variables to be POSTed.2: Mapping Objects in PHP 5 with GoogleMaps API.

rb Skills used Installed and used „mini-magick‟ gem to manipulate images before storage on server. Implemented RoR AJAX helper calls to Prototype and Script.3: Uploading pictures in Ruby On Rails.us in the asset_controller create method.Page 57 of 126 Sprint 0. Web 2. This allows newly uploaded photo to be displayed on the page without a full refresh of the page.0 functionality AJAX based dynamic desktop.aculo.rhtml Controllers asset_controller. .rb View index.rhtml _list_item. Model asset.

rhtml application.0 functionality Mashups AJAX based dynamic desktop .4: Mapping objects in Ruby On Rails with GoogleMaps API Model scrapbook.rb View index.Page 58 of 126 Sprint 0. Web 2.js Controllers google_controller.rb Skills used Integrated GoogleMaps using GoogleMaps API to create and display location attributes in the scrapbook model.

PHP Uploading Pictures Mapping Objects 468 313 Ruby On Rails 152 222 Figure 15: Comparison of amount of code in PHP and RoR application The MVC framework of RoR adds control. The experiment in Sprint 0 appears to validate the claim of RoR to reduce the complexity and time taken to produce web applications.4 provide evidence that identical functionality and capabilities are available in both PHP and RoR. structure and clarity to the development of the example web applications. The table in figure 15 confirms that the use of the RoR MVC framework greatly reduces the amount of code that needs to be written to achieve the same task. For this reason the remainder of the practical application will be developed using RoR.Page 59 of 126 Sprint 0.5: Review PHP 5 vs RoR Sprints 0. .2 through to 0. In PHP the application was a selection of randomly named files in a directory while the RoR file location and naming conform to the conventions required to make best use of the framework.

3. Created method in picture.Page 60 of 126 4. Implemented uploading into the model using library „attachment_fu‟. Installed and used „mini-magick‟ gem to manipulate images before storage in image.rjs.rjs Controllers /admin/photo_controller.rhtml /admin/_list_item.3 Sprint 1: Implementing Photo Management Model photo.rb photopicture. to update the list of photos with new photo highlighted without a page refresh.rb picture.rb pictures_controller Created models with “has_many_and_belongs_to_many” properties.0 functionality AJAX based dynamic desktop.rthml /admin/create. Web 2. implemented in create. Used AJAX. .rb to allow rotation of the images.rb Skills used View /admin/index.

rjs /admin/make_default_photo.rb View /admin/add_photos. Implemented adding to scrapbook from existing photos using collections.rb connection.rjs Controllers /admin/scrapbook_controller.rb photo.rthml /admin/add_photos_to_scrapbook. to update scrapbook when photos are added or made default.rb Pictures_controller Skills used Created connection model with “join” properties between scrapbook and photo allowing a many to many relationship between the scrapbook and photos while recording the position and default photo attributes in the connection model. Web 2. Reused add photo method from the photo model and view.0 functionality AJAX based dynamic desktop .Page 61 of 126 4. Used AJAX.rjs.rhtml /admin/_list_item. implemented in create.3.4 Sprint 2: Implementing Scrapbook Management Model scrapbook.

rjs Controllers library_controller.rb cart. Update main photo from selection on slider collection or use of next and previous arrows.rb connection.Page 62 of 126 4.rjs main_picture.rhtml _slider. When in existence.rb Skills used View slider. Implemented shopping cart using add to cart button.5 Sprint 3: Implementing the User View Model scrapbook. shopping cart object and its attributes are displayed with control buttons.rb photo.3. Web 2.rb Created slider collection of photos in scrapbook and displayed tiny images of photos in collection as selection menu.0 functionality AJAX based dynamic desktop .rthml update_slider.

rb tag_controller.rthml update_slider.0 functionality AJAX based dynamic desktop Tagging .6 Sprint 4: Implementing Tagging Model photo.rhtml Controllers library_controller.rjs /admin/edit. Amended edit and update of photo model to allow tagging.rhtml _ main_picture. Display related photos and related tags on slider view.rb tag. Web 2. Created tag methods to locate related (tagged) photos and related (tagged photos other tags) tags.3.rb /admin/photo_controller Skills used Implemented tagging using „acts_as_taggable‟ library.rb View slider.Page 63 of 126 4.

rb View list_users. . Created user administration page.0 functionality AJAX based dynamic desktop NOTE: Sprint 5.rjs Controllers login_controller.rthml add_user.rb application_controller.3: Implement family groups based access has not yet been implemented due to time constraints.rhtml _ list_user.Page 64 of 126 4.3.rjs delete_user. This will be added if there is sufficient time before the submission date.7 Sprint 5: Implementing User Management Model user. Web 2. About 8 hours work is required to implement and test this facility. Implemented user login validation in application controller to restrict access.rb Skills used Created users and login facility.

rhtml Controllers library_controller.8 Sprint 6: Adding GoogleMaps to Scrapbooks Model scrapbook.rb /admin/scrapbook_controller.rb google_controller. Web 2.rhtml application.3. Updated home view to allow navigation to scrapbooks via icon or location on Google map.js /library/index.0 functionality Mashups AJAX based dynamic desktop .Page 65 of 126 4.rb Skills used Integrated GoogleMaps using GoogleMaps API to create and display location attributes in the scrapbook model.rb View /admin/scrapbook/index.

When save button is pressed the new order is saved in the model.rb . This allows drag and drop sorting on the screen.js Skills used Wrote JavaScript methods in Prototype and Script.3.rhtml application.Page 66 of 126 4.rb View /admin/scrapbook/index.9 Sprint 7: Sorting Scrapbooks from the Web Page Before Save After Save Model scrapbook. the default photo updated and the scrapbook redrawn using Ajax Web 2.aculo.0 functionality AJAX based dynamic desktop for sorting and redisplay Controllers /admin/scrapbook_controller.us.

rhtml /posts/edit.rb comment.rb Skills used Implemented basic blogging interface. Created syndication feed.rb /admin/scrapbook_controller. This will be added if there is sufficient time before the submission date. About 12 hours work is required to implement and test this facility. 8 hours work required to add to scrapbooks and test.0 functionality Blogging Syndication NOTE: Sprint 8.Page 67 of 126 4.3.10 Sprint 8: Providing extra Web 2.rb posts_controller.3: Add Web Service interface has not yet been implemented due to time constraints.rhtml /feed/photos. Web 2.0 features Model post.rxml Controllers feed_controller. 8 hours more work required to implement in scrapbook controller.rb View /posts/list. .

I feel the major lessons. blogs and websites. gaining new skills. Yes. Not having a well defined Entity Model that mapped onto a relationship diagram caused untold problems and resulted in a complete rewrite from scratch of the application. you can bolt on extra pieces to the Rails application but the core . As detailed in section 4. despite what you are told in numerous RoR books.Page 68 of 126 5 5. 5. It is poorly documented and everybody has his or her own method of approaching this issue. in terms of a technology discussion. applying those skills and reflecting on the initial question against this newly acquired perspective.1. was significant. Design is all. After working through the applications provided in the Agile Rails and Rails E-Commerce books.2 Upfront Design is still vital Understanding the Objects in Ruby and mapping those onto a relational database is an extremely confusing process. this proved to be false optimism. I have learnt are as follows. If you mix a number of different authors methods together things will end up working poorly and the code will have no consistency. I had far greater difficulty than I had anticipated applying my basic RoR and Agile skills on the practical application. It took around three and a half months of work for me to become confident in my abilities to write in RoR.1 Moving to an OO MVC from a transactional environment takes time The time taken to move past the basic RoR demonstration applications.1. illustrated in books and on web based tutorials. I was extremely confident of producing an application in a short period of time.3.1. 5.1 CONCLUSIONS AND FURTHER WORK WHAT I’VE LEARNT The overall project has been an extremely satisfying academic exercise in terms of learning new information.

Maybe this is a price paid for using an open-source development framework without the support of a major company (think Java and Sun Microsystems). In books and web sites I have come across remarkable capabilities for validation of objects in the model – but where is the list and explanation of all the validations available in a model. 5. however clear appropriate documentation for the „opinionated‟ pieces it contains need to be provided. In a world where everyone has an opinion and if these opinions are available ubiquitously. blogs. I just wish I knew what they all were. This lack of a coherent documentation resource is an extreme downfall of the RoR project and maybe a prime example of the strengths and weaknesses of Web 2.1.Page 69 of 126 Objects must be designed and their key methods defined before coding starts. It does highlight the fact that RoR is still to mature as a framework as so many seemingly core facilities are still lacking.0. People have produced some amazing work to provide plugins for Rails that make the application environment a wonderful place. Sorry DHH. . Rails claim to strength is being an „opinionated‟ framework. whose opinion do you trust and can the trusted source be bothered to tell you everything they know if they are not motivated to do so? However. web articles and books. Rails provides some amazing features in the framework. I am pleasantly surprised and impressed by the attitude and knowledge of the contributors to the many forums and blogs. During a steep learning curve on Rails my greatest frustration was a lack of a coherent documentation source. „attachment_fu‟ and „mini-magick‟ are three that spring to mind. „acts_as_taggable‟. you are wrong. However all these plugins suffer from extremely limited and disjointed documentation. I have to add a big thank you to many contributors in the RoR community. A model is a key „opinion‟ and surely deserves to be well documented.3 Consistent Ruby on Rails Documentation is lacking RoR documentation is an eclectic collection of wikis.

A function I might add that is not available through the Prototype libraries.1. and an existing partial „list_items‟ was used by Ajax to update the screen. I believe there are numerous reasons why it took this long to grasp the more complex issues of RoR but I feel that the main two reasons that it took this time for me were: 1) I learn quicker through a classroom environment than through a self teach environment. . the benefit of the Object and MVC framework came to the fore as the project continued.1. which holds the display order of the scrapbook. once this information was known an existing method (set_connection – original designed to set the default photo) was used to save the updated scrapbook connection information.4 Well designed Objects provide usability While the initial design and implementation stage of the project took far more time and was more difficult than expected.5 If you keep faith with RoR there will be a breakthrough! I am a self-professed slow learner. In the middle of October I suddenly realised that I was creating instance variables. After three and a half months I was finally comfortable with complex development requirements in RoR. I really get it. While work was required to implement an Ajax JavaScript method to discover the new order of the photos on the page. class methods and application helpers without referring to many different information sources. however when finally I get something. A good example was the implementation of a „sortable scrapbook‟ through the scrapbook administration “add photos” page. I fully expected the „sortable scrapbook‟ to be one of the hardest features to create but the existence of object methods to do the reordering and redisplay of information meant that the major work was the development of a Ajax JavaScript function. 5.Page 70 of 126 5. The consistency of the MVC meant that it was a clear decision where the next part of the application would be implemented and the access to information through the previously prepared object methods provided fast development using already tested components.

Implemented a practical application using newly learned skills in agile development and RoR along with techniques learnt during the MSc course. Learning a new language and development style from scratch was extremely time consuming and the project may have benefited more if the development had been undertaken in CakePHP or Apache Tomcat as I have a working knowledge of both PHP and Java.2 WHAT I’VE ACHIEVED/DELIVERED This dissertation has achieved the majority of the aims laid out in this documents introduction: Identified and discussed. 5. . learning a completely new language and way of development and producing a practical application was too ambitious a project for the time available. the changes in the World Wide Web.Page 71 of 126 2) Documentation for RoRs needs to be greatly improved. in some detail. Introduced Object Oriented Model-View-Controller frameworks and discussed one of these. The application implements a number of the characteristics of a Web 2.shearan.com/dissertation/ 5. An electronic copy of this dissertation and access to the practical application are available at http://www.0 web site. particularly the move from Web 1. RoR. through academic research. Discussed in detail the characteristics of Web 2. particularly the use of AJAX to provide an active desktop and tagging to introduce some of the social computing capabilities.0 to Web 2. The research paper alone could have been a dissertation and I was forced to reduce and heavily edit the theoretical content.0 type web sites and applications.0.3 WHAT I’D DO DIFFERENTLY Producing a research paper.

5.4 AND THE ANSWER TO THE QUESTION IS… From my limited experience of designing web applications using the transactional approach of PERL and PHP. possibly about two months. By the time I understood how to create RoR objects and get them to interact. it appears that RoR is a far superior environment for web application development. it was clear where each piece of code needed to be placed within the MVC and that objects really were easily testable and reusable. Django and CakePHP. This means that web application developers will need to dedicate an extended period of time.0 applications in a shorter timescale than with the existing market dominant tools of PHP and ASP. as I thought it may be constrictive. to learn the skills required to produce a RoR application BUT I believe that the learning curve flattens out quickly and the developer will be able to implement Web 2. as I was able to write a solution of far more complexity than I expected to be possible during the project. I found the experience extremely satisfying. including Apache Tomcat (and other Java based Web MVCs).Page 72 of 126 However. I am satisfied that I have been able to achieve the majority of the goals I set out to meet. I am extremely pleased with the capabilities of uploading. . My two major concerns are: The time it took me to become comfortable with implementing complex methods within the RoR development MVC The poor standard of documentation available for RoR. 5. tagging and displaying images through the web interface and do not believe that I would be anywhere near this point if I had decided to progress this project in PHP5 objects. manipulating. RoR upfront organisation of a web application and the opinionated design theory was a concern before I started to develop the application. but it proved to be completely the opposite.5 FOLLOW UP There are a number of other Object Oriented MVC frameworks currently available.

Page 73 of 126 It would be an informative exercise to produce a comparative application with each of these frameworks and provide a solid comparison between these three and RoR. .

edu/~fielding/pubs/dissertation/top. T. 2007. 2007 from http://www. US: Texere 3 O‟Reilly. report CMU-RI-TR-02-32. K.com/article2/0. 2007 from http://www. (2006): Best (or Most Interesting) Web 2.uci.0 Semantic Web.htm 8 Metz. Retrieved September 17. University of Potsdam. Retrieved 21 April.com/frames/goodorbad.yourhtmlsource. M. R. Germany.1895. (2000): Architectural Styles and the Design of Network-based Software Architectures. Carnegie Mellon University.html 2 Berners-Lee. 2003 11 Nykanen.com/pub/a/oreilly/tim/news/2005/09/30/what-is-web-20. 2007 from http://www. C (2007): Web 3. Retrieved 22 April.com/publications/essays/archives/000385.T.php 6 Shannon. Retrieved April 26. T.oreilly.htm Garrett. (2002): W3C FI & W3C Semantic Web.html . Sycara.adaptivepath.tut. T.Page 74 of 126 References 1 O‟Reilly. R T. 2007 from http://radar.com/review_of_the_years_best_web_20_ explanations. PC Magazine March. (2003): Delivering Semantic Web Services. (2005): Ajax: A new Approach to Web Applications. Robotics Institute.. 2007 from http://www.0 Definitions and Explanations. 10 Paolucci.html 7 Fielding. Retrieved September 16.pcmag. 2007 from http://www. Tech. Retrieved March 12.asp 9 Schreiter. Retrieved January 5.socialcomputingmagazine. J J.fi/talks/2003/0331umedia-on/slide6-0. and Kawamura. 2007 from http://web2. (2005): What is Web 2. (2006): Web 2. UC Irvine.com/archives/2006/12/web_20_compact.00. PhD thesis. (1999): Weaving the Web: Origins and Future of the World Wide Web.oreilly.0: Design Patterns and Business Models for the Next Generation of Software. May.html 4 5 Hinchcliffe. O.w3c.0 Compact Definition: Trying Again. 2007 from http://www. D.2102852.ics. (2007): An Introduction into Semantic Web Services. Retrieved March 23. (2006): Frames: Good or Bad. T.

(2006): Ajax. Pages 6-10.com (2002): DOM – Document Object Model Retrieved September 25. N. Retrieved 20 April. LSBU. K. Y. October 2005. S.cakephp. Semester 2 2006/7. D P. (2006): Software Architecture Case Study: Ruby on Rails. (2007): PHP with MySQL Coursenotes. US: O‟Reilly Media Inc. (2007). and Heinemeier-Hansson. 2007 from http://www. (2007): PHP Statistics For March 2007. (2007): Ajax on Rails.net/chiffres_cles/phpversion/16811php_statistics_for_march_2007. Week 6. M. LightReading.org/wiki/Model-view-controller 16 Macaulay. Anon. Kent. T. February 2007. Retrieved October 24. Developers Shift to Dynamic Programming Languages. A.S.wikipedia. (2007): Model-view-controller. 17 Cong.oreilly. 18 19 20 21 22 23 24 25 26 27 . and the Separation of Layout Logic. Number 2. (2007): Agile Web Development with Rails. Pages 457-463. Web Database for Multimedia. Nexen. J. O‟Reilly. 15 Anon. US: Pragmatic Bookshelf.com/story/tag/diagram Guzewich. D.nexen. D. Musser.org/chapter/intro Thomas.lightreading. 2007 from http://borkweb. C. 2007 from http://radar. K. Anon.. May 2007. and Du.. 2007 form http://en. Pfieffer.. H. Retrieved October 24. and Lew. 2007 from http://www..com/techenc. with O‟Reilly.net. (2006): Web Services on Rails.com/archives/2006/07/levels_of_the_game. Retrieved September 25. 13 Seguy. M.wikipedia. (2007): Introduction to CakePHP. (2007): Welcome to the World of Web 2. Multimedia Systems Volume 6. Templating. Computer Volume 40. and Shank. (2005): Systems and architectures for multimedia information retrieval. Retrieved April 24. Sebe.asp?term=DOM Anon(2007): Elevator Pitch.Page 75 of 126 12 Djeraba.0 Applications. Raymond. Number 10. T.0.0 Principles and Best Practices. M. (2006): Web 2.org/wiki/Elevator_pitch Batchelder. 2007 from http://manual.php 14 Paulson. T. The CPA Journal. US: O‟Reilly Media Inc. D.html Marshall. US: O‟Reilly Media Inc. M. Pages 12-15. Retrieved September 17. (2006): Levels of the Game: The Hierarchy of Web 2.. 2007 from http://en.

Addison-Wesley. (1999) Extreme Programming Explained: Embrace Change. L. Ajax on Rails. Inc. 31 Beck. (2006): Beginning Ruby on Rails E-Commerce.Page 76 of 126 28 29 30 Anon. Biblography Babin. Hellsten. US: O‟Reilly Media. From Novice to Professional.rubylang. New York: Apress.org/en/about/ Raymond. K. C. . S. J. (2004): About Ruby. (2007): Beginning Ajax with PHP. From Novice to Professional. New York. 2007 from http://www. Retrieved from September 7. (2007). New York: Apress. and Laine.

L. A.. M. Peachpit Press. Fowler. Brinzarae. C. Darie. Indianapolis: Wiley Publishing. Purvis. (2005): PHP and MySQL Web Development.(2007): Google Maps Applications with Rails and Ajax. and Schwartzbach (2006): An Introduction to XML and Web Technologies. (2006). UK: Packt Publishing. US: Pragmatic Bookshelf. L and Thomson. Lewis.Page 77 of 126 Welling. New York: Apress. A. Sambells. the Pragmatic Programmers‟ Guide. US: Pragmatic Bookshelf. (2007): Ajax Bible. Building Responsive Web Applications. Rails Recipes. J and Turner. (2006): Programming Ruby. B. F. C. MØller. Thomas. Birmingham. S. Berkeley. Essex: Addison Wesley. .. 3rd Edition. From Novice to Professional.. C. and Chereches-Tosa. (2006): Ajax and PHP. Indiana: Developer‟s Library. (2002): Visual Quickstart Guide. Holzner. Harlow. Bucica. M. Fehily. D. C. Python..

Page 78 of 126

APPENDIX A: Family Scrapbook Application Entity Diagram

Com m ents id :int :int :stri g n 1 user_id :int 1 1

S cr apbook id name user_id creat date e lat lng :int :stri g n 1 :int :datetime :dec (15,1 0) :dec (15, 0) 1 :text(100)

User s 1 id name hashed_password salt

1 :int :stri g n :stri g n :stri g n

1

*

post_ id body

P osts 1 id title body creat d_at e scrapbook_id 1 user_id :int :stri g n :text :datetime :int :int

icon

Connections id :int :int :int :T/F :int id name

Tags :int :stri g n :datetime 1

*

* *

scrapbook_id photo_id default_photo position

created

P hoto s_tags

*
P ictur es id filename content_type size width heigh t parent_id thumbnail createdat :int :stri g n :stri g n :int :int :int :int P hotos - pictur es :stri g n :datetime 1 1 1 P hotos id name desc owner_id date aspect :int :stri g n :stri g n :int :datetime :stri g n 1

photo_id tag_i d

:int :int

*

K ey :int :int 1 1 1

* *

photo_id picture_id

*

One to many relationships One to one relationships

Page 79 of 126

APPENDIX B: Storyboards for Family Scrapbook Application
Slide Page r

ScrapbookT itl e Logo Scrapbook T itle
Vis iting Info
Shows no. of times visited this page illustrating successful use of ajax Thumb nails

M ENU M ain Photo
Ajax updates Visib if exisi s le t

R elated Photos

Toggl s to sh e ow tiny related photos

SHOPPING CART
Description

R elated Tags

Tags

Add to shoppi g cart n

Clicking thumbnail or related photos or ta creates an Aj x transition of main ph gs a oto

Scrap book vie w

ScrapbookT itl e Logo
Q a G r r e u i c k T m i a p h s c i e n e d e d e ™ d e t c o o s a n d m p r e e a e t s h s s i o p r c i t u r e . Q a G r r e

Scrapbook T itle
Vis iting Info
u i c k T m i a p h s c i e n e d e d e ™ d e t c o o s a n d m p r e e a e t s h s s i o p r c i t u r e .

a

Q G r e

r

u i c k T m i a p h s c i e n e d e

d

e ™ d e c t o

o s

a n d m p r e e

a e t

s h

s s i

o p

r c i

t

u

r

e

.

Icons of Defa ult Photo for scrapbook
Nam e Nam e Nam e Nam e Nam e Nam e

M ENU
Na m e De s c r i t i n p o Li k n

Mashu from Googl p e Maps

Pinhe locations for ad Scrap boook

N gae t o scapbookvi pi hea or i on avi t r a n d c

Page 80 of 126

Add Photos to Scrapbo ok

Add photos to ScrapbookT itlÓ Ņ e Logo Add photos to ScrapbookT itlÓ Ņ e
Vis iting Info
Curre photo in nt s scrap book
D efault Photo

M ENU

save

Edit buttons:Rotate Left / Right Make Default Delete photo

Add e xisting photos to scrap book

Uploa photos to d scrap book

Save new photo order after so rting on screen

RB ***** class Cart attr_reader :items def initialize @items = [] end def add_hardCopy(photo) current_item = @items.photo == photo} current_item.find {|item| item.sum { |item| item.increment_quantity else current_item = CartItem.quantity} end def total_quantity @items. :quantity def initialize(photo) @photo = photo @quantity = 1 end def increment_quantity @quantity += 1 end .rb ***** class CartItem attr_reader :photo.Page 81 of 126 APPENDIX C: Family Scrapbook Application RoR Code (Sprint 1 – 8) Models ***** CART.decrement_quantity if current_item.find{|item| item.quantity == 0 @items.new(photo) @items << current_item end current_item end def remove_product(photo) current_item = @items.quantity } end end ***** cart_item.delete(current_item) end current_item end def total_items @items.sum{ |item| item.photo == photo } if current_item current_item.

rb ***** class Connection < ActiveRecord::Base belongs_to :scrapbook acts_as_list :scope => :scrapbook_id belongs_to :photo end ***** photo.basename(picture.create(params[:picture]) photo = Photo.name end def title @photo.public_filename(:thumb) end end ***** comment.name}%'") photo.new(params[:photo]) photo. :through => :connections acts_as_taggable validates_presence_of :name def name_desc "#{name} #{description}" end def add_photo_with_pictures(params) picture = Picture.save .now piccies = Picture.description end def size @photo.find_by_sql("select * from pictures where filename like '#{photo.*") photo.Page 82 of 126 def decrement_quantity @quantity -= 1 end def name @photo.".size photo.rb ***** class Comment < ActiveRecord::Base belongs_to :post end ***** connection.date = Time.owner_id = piccies.origSize end def thumb thumb = photo.name = File.first.pictures.rb ***** class Photo < ActiveRecord::Base has_and_belongs_to_many :pictures has_many :connections has_many :scrapbooks.filename.

rotate(degrees) image.direction) types = ["".find_not_in_scrapbook(target_scrapbook) in_scrapbook = target_scrapbook. :medium => '480x480>'.new all_photos = Photo. :tiny => '40x40>' }."tiny"] if direction == 'left' degrees = "-90" else degrees = "90" end for type in types do image = MiniMagick::Image.public_filename(type)) end end end ***** post ***** class Post < ActiveRecord::Base has_many :comments belongs_to :scrapbook validates_presence_of :title.rb ***** class Picture < ActiveRecord::Base has_and_belongs_to_many :photos has_attachment :storage => :file_system. :order => :position has_many :default_photos.each do |piccie| photo. :source => :photo."thumb". :body end ***** scrapbook ***** class Scrapbook < ActiveRecord::Base has_many :connections. :thumbnails => { :large => '600x600'.write("#{RAILS_ROOT}/public"+picture. :through => :connections. :conditions => ['default_photo = 1'] validates_presence_of :name def self.find(:all) not_in_scrapbook = all_photos . :processor => :MiniMagick validates_as_attachment def rotate_image(picture.public_filename(type)) image. :max_size => 4.megabytes. :order => :position has_many :posts has_many :photos.photos not_in_scrapbook = Array."large". :thumb => '100x100!'."medium".in_scrapbook .pictures << piccie end photo end end ***** picture. :through => :connections.from_file("#{RAILS_ROOT}/public"+picture.Page 83 of 126 piccies.

blank? .rb ***** class Tag < ActiveRecord::Base has_and_belongs_to_many :photos end ***** user.authenticate(name.salt) if user.find_all_except_default find_by_sql("select * from scrapbooks where name not like 'default%'") end def self. password) user = self.hashed_password != expected_password user = nil end end user end def password @password end def password=(pwd) @password=pwd return if pwd.blank? end def after_destroy if User.count.find_by_name(name) if user expected_password = encrypted_password(password.add_to_base("Missing Password") if hashed_password.rb ***** require 'digest/sha1' class User < ActiveRecord::Base validates_presence_of :name validates_uniqueness_of :name attr_accessor :password_confirmation validates_confirmation_of :password def validate errors.find_with_marker find_by_sql("select * from scrapbooks where lat is NOT NULL and lng is NOT NULL") end end ***** tag.zero? raise "You can't delete the last user!!!" end end def self.Page 84 of 126 not_in_scrapbook end def self. user.

to_s end end VIEWS ***** /admin/photo/_form. :action => 'show'.salt = self.tagged_related %> <%= link_to image_tag(photo.name %></dd> <dt>Description</dt><dd><%= @photo.public_filename(:medium)) %> </td> </tr> </table> <% if @photo.aspect%></dd> <dt>Tags</dt><dd><%= display_tags_in_link @photo%></dd> </dl> </td> <td> <%= image_tag(@photo.object_id. self. :limit => 100)%> <%= link_to tag['name'].collect(&:name).rhtml ***** <table> <tr> <td> <dl> <dt>ID</dt><dd><%= @photo.first.description%></dd> <dt>Owner ID</dt><dd><%= @photo.pictures.to_s + rand.owner_id%></dd> <dt>Date</dt><dd><%= @photo.first.encrypted_password(self.name.password.'. salt) string_to_hash = password + "wibble" + salt # wibble just makes it harder Digest::SHA1.public_filename(:tiny)). :action => 'show'. :controller => '/tag'.hashed_password = User. :raw => true. :action => 'show'.size > 0 %> <div id="recommended"> <h2>Recommendations</h2> <h4>Photos</h4> <% for photo in @photo. :id => tag['name']%><br /> <% end %> </div> <% end %> . :separator => '.salt) end private def self.id%></dd> <dt>Name</dt><dd><%= @photo. :id => photo %><br /> <% end %> <h4>Tags</h4> <% for tag in Photo.encrypted_password(password.pictures. :id => photo %> <%= link_to photo.hexdigest(string_to_hash) end def create_new_salt self.tags.tags.Page 85 of 126 create_new_salt self.find_related_tags(@photo.date%></dd> <dt>Aspect</dt><dd><%= @photo.

:action => 'show'.jpg")%> <%= end_form_tag %> </td> <td> <%= form_remote_tag :url => {:action => :spin_right. :id => photo %></td> <td><%= photo. :action => 'edit'.id}" page. "photos". :partial => 'list_item'. :id => photo %></td> <td><%= button_to "Delete".jpg". :object => @new_photo page.insert_html :bottom.parent_id? %> <%= link_to image_tag(picture. :id => @photo %> | <%= link_to 'Back'.alert("Cmmooonnnn: #{@new_photo. :id => list_item } %> <% end %> <% end%> </td></tr> <tr><td> <%= form_remote_tag :url => {:action => :spin_left.name.rjs ***** page.visual_effect :blind_up . :action => 'edit'.pictures %> <% if !picture. :id => list_item } %> <%= image_submit_tag("/images/rotleft.rjs ***** page["picture_#{@photo.rhtml ***** <div id="picture_<%= list_item.id}"].name}"%> <%= image_submit_tag("/images/del.description%></td> <td><%= photo. :id => photo}. :id => list_item } %> </td> <td> <%= form_remote_tag :url => {:action => 'destroy'. "picture_#{@new_photo. :action => 'index' %></p> ***** /admin/photo/_list_item. :id => list_item }.visual_effect :highlight.Page 86 of 126 <p><%= link_to 'Edit'.owner_id%></td> <td><%= photo. :id => list_item } %> <%= image_submit_tag("/images/rotright.{ :action => 'destroy'.public_filename(:thumb)).name}"%></td> </tr> ***** /admin/photo/create. :confirm => "Do you really want to delete #{photo.name}") ***** /admin/photo/destroy.id%></td> <td><%= link_to photo.jpg")%> <%= end_form_tag %> </td></tr></table> </div> ***** /admin/photo/_photo. { :action => 'show'.rhtml ***** <tr> <td><%= photo.jpg")%> <%= end_form_tag %> </td> <td> <%= link_image_to "/images/edit.date%></td> <td><%= photo.aspect%></td> <td><%= link_to 'Edit'. :confirm => "Do you really want to delete #{list_item.id %>" class="thumb"> <table id="picture"> <tr><td colspan="4"> <% for picture in list_item.{:action => 'edit'.

id %>" <% if scrapbook. :html => { :multipart => true. {:action => 'index'} %> ***** /admin/photo/index.:id => @photo do %> <%= render(:partial => 'form') %> <%= submit_tag 'Edit' %> <% end %> </td></tr> </table> </div> <%= link_to 'Show'.pictures. :target => 'upload_frame'. :id => @photo. :id=> 'add_photo' }) do |form| %> <p> <label for="uploaded_data">Upload a file:</label> <%= form.height:1px.border:0px" src="about:blank"></iframe> . :object => photo) %> <% end %> </div> <!--Ajaxified Form .Replaces New --> <% form_for(:scrapbook.file_field "uploaded_data".rhtml ***** div id="photos"> <% @photos.text_field "description". {:action => 'show'.first.Page 87 of 126 ***** /admin/photo/edit. :name => 'photo[description]'%></p> <p> <label for="Scrapbook Name">Allocate to scrapbook:</label> <select id="scrapbook_id" name="scrapbook"> <% for scrapbook in @scrapbooks %> <option value="<%= scrapbook. :url => {:action => 'create'}.name %></option> <% end%> </select> <%= submit_tag "Create" %> </p> <% end %> <iframe id='upload_frame' name="upload_frame" style="width:1px.each do |photo| %> <%= render(:partial => 'list_item'.id }%>| <%= link_to 'Back'.id == 12 %> selected="selected"> <% else %> > <% end %> <%= scrapbook.rhtml ***** <div id="main_picture"> <table> <tr><td> <%= image_tag(@photo.public_filename(:medium)) %> </td><td> <% form_tag :action => 'update'. :name => 'picture[uploaded_data]' %></p> <p> <label for="Description">Description:</label> <%= form.

next link_to("Next page". :raw => true. :tag => 'div'%> <%= if @photo_pages.pictures.first. :id => photo %> <%= link_to photo.previous && @photo_pages. { :page => @photo_pages.pictures.owner_id%></dd> <dt>Date</dt><dd><%= @photo.aspect%></dd> <dt>Tags</dt><dd><%= display_tags_in_link @photo%></dd> </dl> </td> <td> <%= image_tag(@photo.name. { :page => @photo_pages.id%></dd> <dt>Name</dt><dd><%= @photo.public_filename(:medium)) %> </td> </tr> </table> <% if @photo. :id => tag['name']%><br /> <% end %> </div> <% end %> . :separator => '. :limit => 100)%> <%= link_to tag['name'].size > 0 %> <div id="recommended"> <h2>Recommendations</h2> <h4>Photos</h4> <% for photo in @photo.Page 88 of 126 <!--Make the divs sortable with Ajax--> <%= sortable_element :photos. :controller => '/tag'.'.previous link_to("Previous page".current.tags.current.collect(&:name).public_filename(:tiny)).next %> | <% end %> <%= if @photo_pages.current.first.next }) end %> </p> ***** /admin/photo/show.find_related_tags(@photo.previous }) end %> <% if @photo_pages.tagged_related %> <%= link_to image_tag(photo.name %></dd> <dt>Description</dt><dd><%= @photo.rhtml ***** <table> <tr> <td> <dl> <dt>ID</dt><dd><%= @photo.current. :action => 'show'.date%></dd> <dt>Aspect</dt><dd><%= @photo. :action => 'show'.current.current.description%></dd> <dt>Owner ID</dt><dd><%= @photo. :action => 'show'. :id => photo %><br /> <% end %> <h4>Tags</h4> <% for tag in Photo.tags.

public_filename(:thumb)) %></td></tr> <tr><td>Default</td></tr></table></div> <% else %> <div id="photo_<%= current_photo.size > 0 default_photo = @scrapbook. :id => @scrapbook.rhtml ***** % if @scrapbook.rhtml ***** <h2>Photos to Add?</h2> <% form_remote_for :scrapbook.id %>" /> <%= image_tag(photo. .jpg")%> <%= end_form_tag %> </td> <td> <%= form_remote_tag :url => {:action => 'destroy_connection'. :scrapbook => "#{@scrapbook.pictures. :scrapbook => "#{@scrapbook. :html => {:id => "add_scrapbook"} do |form| %> <% for photo in @not_in_scrapbook%> <input type="checkbox" name="photo_ids[]" value="<%= photo. :scrapbook => "#{@scrapbook. :id => current_photo.id %>" class="default_thumb"> <table id="picture"> <tr><td><%= image_tag(current_photo. :id => @photo %> | <%= link_to 'Back'.default_photos else default_photo = [0] end %> <% if default_photo[0] == current_photo %> <div id="photo_<%= current_photo. :url => {:action => 'add_photos_to_scrapbook'.id %>" class="thumb"> <table class="picture"> <tr><td colspan="5"><%= image_tag(current_photo.public_filename(:tiny)) %> <% end %> <%= submit_tag "Add Photos" %> <% end %> ***** /admin/scrapbook/_current_photo.Page 89 of 126 <p><%= link_to 'Edit'. :action => 'index' %></p> ***** /admin/scrapbook/_addable_photos.public_filename(:thumb)) %></td></tr> <tr><td> <%= form_remote_tag :url => {:action => :spin_left.first.id}" } %> <%= image_submit_tag("/images/rotleft. :action => 'edit'.pictures. :id => current_photo.first. :id => current_photo.jpg")%> <%= end_form_tag %> </td> <td> <%= form_remote_tag :url => {:action => 'make_default_photo'.default_photos.first.id}.pictures.id}" }%> <%= image_submit_tag("/images/tick.id}" }.

public_filename(:thumb)). :scrapbook => "#{@scrapbook. :id => list_item}) %> <% else %> <%= link_to(image_tag('/images/quest.id}" } %> <%= image_submit_tag("/images/rotright.id%>"> <h2>Current Photos</h2> <% for current_photo in @scrapbook.Page 90 of 126 :confirm => "Do you really want to delete the #{current_photo.{:action =>'add_photos'.jpg').jpg")%> <%= end_form_tag %> </td> <td> <%= form_remote_tag :url => {:action => :spin_right. :id => current_photo. :url => {:action => 'sortem'}%> .jpg")%> <%= end_form_tag %> </td> </tr></table></div> <% end %> ***** /admin/scrapbook/_list_item.first.rhtml ***** <div id="scrapbook_<%= list_item.pictures. :confirm => "Do you really want to delete the #{list_item.jpg")%> <%= end_form_tag %> </td></tr> </table> </div> ***** /admin/scrapbook/_scrapbook_sortable.default_photos.default_photos.name} scrapbook?"%> <%= image_submit_tag("/images/del. {:action =>'add_photos'. :id => list_item }. :id => list_item}) %> <% end %> </td></tr> <tr><td> <%= link_to list_item. :tag => 'div'.empty? %> <%= link_to(image_tag(list_item.first. :action => 'add_photos'. :object => current_photo %> <% end %> </div> <%= sortable_element :scrapbook_sortable.name} photo?"%> <%= image_submit_tag("/images/del.rhtml ***** <div id="scrapbook_sortable" class="scrapbook_sortable" scrapbook="<%= @scrapbook. :id => list_item %> </td> <td> <%= form_remote_tag :url => {:action => 'destroy'.name.photos%> <%= render :partial => 'current_photo'.id %>" class="thumb"> <table class="picture"> <tr><td colspan="2"> <% if !list_item.

previous %> <div class="thumb"> <%= link_to("Previous page". :partial => 'current_photo'.id %>" selected="selected"><%= @scrapbook.form.visual_effect :blind_up page. { :page => @scrapbook_pages. "photo_#{photo.Page 91 of 126 ***** /admin/scrapbook/add_photos.id}" end page.replace_html("addable_photos".file_field "uploaded_data".current. :name => 'picture[uploaded_data]' %></p> <p> <label for="Description">Description:</label> <%= form. :name => 'photo[description]'%></p> <p> <label for="Scrapbook Name">Allocate to scrapbook:</label> <select name="scrapbook"> <option value="<%= @scrapbook.form.id}"].reset "add_scrapbook" ***** /admin/scrapbook/destroy_connection.rjs ***** for photo in @updated_photos page.Replaces New --> <h2>Photos to Upload?</h2> <% form_for(:scrapbook.border:0px" src="about:blank"></iframe> ***** /admin/scrapbook/add_photos_to_scrapbook. :partial => 'addable_photos'. :object => photo page.insert_html :bottom. "scrapbook_#{@scrapbook.height:1px.id}" page. :html => { :multipart => true.visual_effect :highlight.rhtml ***** div id="scrapbooks"> <% if @scrapbook_pages. :partial => 'addable_photos'.rhtml ***** <%= render :partial => 'scrapbook_sortable'%> <input type="button" onClick="updateScrapbookOrder()" value="Save updated scrapbook order"/> <div id="addable_photos"> <%= render :partial => 'addable_photos'%> </div> <!--Ajaxified Form . :object => @not_in_scrapbook) ***** /admin/scrapbook/index.replace_html("addable_photos". "scrapbooks". "scrapbook_sortable".reset "add_scrapbook" ***** /admin/scrapbook/create.rjs ***** page. :object => @scrapbook page.text_field "description". :object => @not_in_scrapbook) page.rjs ***** page["photo_#{@photo. :partial => 'list_item'.name %></option> </select> <%= submit_tag "Create" %> </p> <% end %> <iframe id='upload_frame' name="upload_frame" style="width:1px.visual_effect :highlight. :id=> 'add_photo' }) do |form| %> <p> <label for="uploaded_data">Upload a file:</label> <%= form.previous}) %> .current.insert_html :bottom. :target => 'upload_frame'. :url => {:action => 'create'}.

name xml.1" do xml. :controller => 'library'.channel do xml.link url_for(:only_path => false.com/maps?file=api&amp.description h("New Photos from the Family Scrapbook") @photos. :controller => 'library'.item do xml.0".google. "xmlns:dc" => "http://purl.current.current.org/1999/xhtml" xml:lang="en" lang="en"> <head> <meta http-equiv="Content-Type" content="text/html. :object => scrapbook) %> <% end %> <% if @scrapbook_pages.next %> <div class="thumb"> <%= link_to("Next page". :action => 'show_photo'.0 Transitional//EN" "http://www.title photo.key=ABQIAAAAWBs8BZ4TjrRayH7tC7eahRSDJ5FbnUnSozdd4sY_1qb9zLM_xSU4k8noXKwWKCmq1xvX0xsZWTh6g" type="text/javascript"></script> <%= stylesheet_link_tag "scrapbook".pubDate CGI. { :page => @scrapbook_pages.w3. :media => "all" %> <%= javascript_include_tag :defaults %> .rss "version" => "2.link url_for(:only_path => false.guid url_for(:only_path => false.next }) %> </div> <%end %> </div> <div class="depot-peterform"> <fieldset> <legend>Create a new scrapbook</legend> <div id="map" class="mediummap"></div> </fieldset> </div> ***** /admin/scrapbook/resort_scrapbook.rjs ***** page['scrapbook_sortable']. charset=utf-8"/> <title><%= @page_title || "Family Scrapbook" %></title> <script src="http://maps.first.first.pubDate CGI.org/TR/xhtml1/DTD/xhtml1-transitional.org/dc/elements/1.instruct! xml.date) xml.image image_tag(photo. :id => photo ) xml.each do |scrapbook| %> <%= render(:partial => 'list_item'. :action => 'index' ) xml. :id => photo ) xml. :action => 'show_photo'. :controller => 'library'.rfc1123_date(photo.description) xml.description h(photo.title 'Family Scrapbook' xml.pictures.Page 92 of 126 </div> <%end %> <% @scrapbooks.rfc1123_date(@photos.public_filename(:tiny)) end end end end ***** /layouts/application.replace_html :partial => 'scrapbook_sortable' ***** /feed/photos.v=2&amp.rxml xml.rhtml ***** <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.w3.each do |photo| xml.date) xml.dtd"> <html xmlns="http://www.

"time")%> <% end%></td></tr> </table> </div> <div id="store"> <div id="columns"> <div id="admin_user"> <% if @user %> <h4>user id : <%= @user.items.name %></h4> <%= render(:partial => "library/admin_menu")%> <% else %> <%= render(:partial => "library/user_menu")%> <% end %> </div> <div id="shop"> <%= hidden_div_if(@cart.empty?.new @page_title = 'Create new photo' end .</td></tr> <tr><td><%= @current_time%></td></tr> <tr><td><% if session[:counter] %> You've been here <%= pluralize(session[:counter].rb ***** class Admin::PhotoController < ApplicationController before_filter :authorize def new @photo = Photo.Page 93 of 126 </head> <body id="screen"> <div id="banner" align="right"> <%= @page_title || "Family Travel"%> <table id="counter" align="right"> <tr><td>Page last fully loaded:&nbsp. :object => @cart)%></div> </div> </div> <div id="main"> <%if flash[:notice] -%> <div id="notice"><%= flash[:notice ]%></div> <% end -%> <%= yield :layout %> </div> </div> </body> </html CONTROLLERS ***** /admin/photo_controller. :id => "cart") %> <%= render(:partial => "library/cart".

tag(params[:tags].scrapbooks << scrapbook rescue redirect_to_index("Failed to create the picture .connections connection.visual_effect :highlight. "stuff" end end end def create begin photo = Photo. "picture_#{@new_photo. :object => @new_photo page. "photos".'.do it properly") else responds_to_parent do render :update do |page| page. :separator => '.innerHTML)" page.description} was successfully updated" redirect_to :action => 'index' else @page_title = 'Edit Photo Information' render :action => 'edit' end end def destroy @photo = Photo.destroy .insert_html :bottom.find(params[:id]) for connection in @photo.form. :separator => '.reset "add_photo" end end end end def edit set_up @photo = Photo. :partial => 'list_item'.add_photo_with_pictures(params) @new_photo.tag(params[:tags].Page 94 of 126 def create_ajax responds_to_parent do render :update do |page| page << "alert($('stuff'). :clear => true) if @photo.find(params[:scrapbook]) @new_photo.find(params[:id]) @page_title = 'Edit Photo Information' end def update @photo = Photo.update_attributes(params[:photo]) flash[:notice] = "Photo #{@photo.find(params[:id]) @photo.new @new_photo = photo.') scrapbook = Scrapbook.id}" page.visual_effect :highlight.destroy end @photo.

name end def index set_up @photo_pages.find(params[:id]) spin_photo(photo. :order => 'name') end def get_time sleep 1.id}"].strftime("%d-%m-%Y %H:%M:%S") end def spin_left photo = Photo.find(:all) @page_title = "Listing All Photos" @scrapbooks = Scrapbook.pictures.'right') end def spin_photo(photo.first picture.find(params[:id]) @page_title = @photo.Page 95 of 126 end def show set_up @photo = Photo.'left') end def spin_right photo = Photo.direction) render :update do |page| page["picture_#{photo. @photos = paginate :photos.visual_effect :highlight end end private def redirect_to_index(msg = nil) flash[:notice] = msg if msg redirect_to :action => :index end end ***** /library/_admin_menu. :per_page => 18 # @photos = Photo.id}"].replace :partial => "list_item".second render :text => Time.find(params[:id]) spin_photo(photo.rhtml ***** <a href="/library/">Home</a><br/> <a href="/posts">Blog</a><br /> <a href="/about/">About</a><br/> <a href="/admin/scrapbook/">Manage Scrapbooks</a><br/> <a href="/admin/photo/">Manage Photos</a><br/> . :object => photo page["picture_#{photo.direction) picture = photo.find(:all.rotate_image(picture.now.

name)%></td> <td class="item-price"><%= number_to_currency(cart_item.public_filename(:thumb)).</td> </tr> <tr><td> <% form_tag :url => { :controller => '/library' .quantity)%></td> <td> <% form_remote_tag :url => { :controller => '/library' .jpg")%> <% end %> </td></tr> </table> ***** /library/_cart_item.:action => :remove_from_cart.quantity %> &times.:action => :checkout} do %> <%= image_submit_tag("/images/checkout.:action => :empty_cart } do %> <%= image_submit_tag("/images/emptycart. :id => cart_item.jpg")%> <% end %> </td> </tr> ***** /library/_list_item. :action => 'show'.empty? %> <div class="thumb"> <table id="picture"> <tr><td><%= link_to(image_tag(list_item.total_quantity)%></td> <td>&nbsp.jpg")%> <% end %> </td> <td> <% form_remote_tag :url => { :controller => '/library' .first.rhtml ***** <div class="cart-title">Request to print photos:</div> <table> <%= render(:partial => "library/cart_item".Page 96 of 126 <a href="/login/list_users">Manage Users</a><br/> <a href="/tag/list">Manage Tags</a><br/> <a href="/login/logout">Logout</a><br/> ***** /library/_cart.default_photos.photo } do %> <%= image_submit_tag("/images/del.first.rhtml ***** <% if !list_item.items) %> <tr class="total-line"> <td>Total</td> <td class="total-cell"><%= number_to_currency(cart.name %></td></tr> </table> </div> <% end %> .rhtml ***** <% if cart_item == @current_item %> <tr id="current_item"> <% else %> <tr> <% end %> <td><%= cart_item. :collection => cart.default_photos. <%= h(cart_item. :id => list_item ) %></td></tr> <tr><td><%= list_item.pictures.

photos if is_next n_photie = photo is_next = false end if main_picture == photo is_next = true p_photie = prev end prev = photo end %> <table><tr> <% if p_photie %> <td> <%= form_remote_tag :url => {:action =>'update_slider'.size > 0 %> <h3>Related Photos</h3> <% if main_picture.tagged_related.pictures.jpg") %> <%= end_form_tag %> </td> <% else %> <td>&nbsp.tagged_related %> <%= form_remote_tag :url => {:action =>'update_slider'.Page 97 of 126 ***** /library/_main_picture. :id => photo.tags.public_filename(:medium)) %> </td> <% if n_photie %> <td> <%= form_remote_tag :url => {:action =>'update_slider'.pictures.</td> <% end %> <td id="recommended"> <% if main_picture. :scrapbook => @scrapbook } %> <%= image_submit_tag("/images/fwdarrow.first.rhtml ***** <% is_next = false prev = false n_photie = false for photo in @scrapbook. :scrapbook => @scrapbook } %> <%= image_submit_tag(photo.jpg") %> <%= end_form_tag %> </td> <% else %> <td><img src="/images/space. :id => p_photie.size == 0%> No Similarly tagged photos <% end%> <% for photo in main_picture.jpg" /></td> <% end %> <td class="mainpic" align="center"> <%= image_tag(main_picture.public_filename(:tiny)) %> <%= end_form_tag %> .first. :id => n_photie. :scrapbook => @scrapbook } %> <%= image_submit_tag("/images/bwdarrow.

'. :separator => '. :limit => 100)%> <%= link_to_remote tag['name'].find_related_tags(main_picture.visual_effect :blind_down if @cart.</td> <td align="center"><%= main_picture.rhtml ***** <a href="/library/">Home</a><br /> <a href="/posts">Blog</a><br /> <a href="/about/">About</a><br/> <a href="/login/login">Login</a><br /> ***** /library/add_to_cart.public_filename(:tiny)) %> <%= end_form_tag %> </td> <% end %> </tr></table> ***** /library/_user_menu.hide } page. :id => photo. :separator => '. :url => {:action => 'toggle'.jpg")%> <%= end_form_tag %> </td> </tr> </table> ***** /library/_slider.rhtml ***** <table id="slidertab"><tr> <% for photo in @scrapbook. :partial => "cart".rjs ***** page. :raw => true. :id => photo. :tag_name => tag['name']}%><br /> <div id="related_<%= tag['name']%>" style="display:none"> <% @tagged_list = Photo.pictures.public_filename(:tiny)) %> <%= end_form_tag %> <% end %> </div> <% end %> <% end %> </td> </tr> <tr> <td>&nbsp.replace_html("cart".find_tagged_with(:any => tag['name'].') %> <% for photo in @tagged_list %> <%= form_remote_tag :url => {:action =>'update_slider'. :scrapbook => @scrapbook } %> <%= image_submit_tag(photo.select("div#notice").description %></td> <td><%= form_remote_tag :url => {:action => :add_to_cart. :id => main_picture} %> <%= image_submit_tag("/images/cart.each {|div| div. :object => @cart) page[:cart].tags.Page 98 of 126 <% end %> <h3>Related Tags</h3> <% for tag in Photo.first.collect(&:name).photos %> <td> <%= form_remote_tag :url => {:action =>'update_slider'.total_quantity == 1 .pictures. :scrapbook => @scrapbook } %> <%= image_submit_tag(photo.first.

visual_effect :highlight.rb ***** class Admin::ScrapbookController < ApplicationController before_filter :authorize def index set_up @scrapbook_pages. :object => @photo) ***** /admin/scrapbook_controller.rhtml ***** <div id="scrapbooks"> <% @scrapbooks.each do |scrapbook| %> <%= render(:partial => 'list_item'.first.pictures. :startcolor => "#C0C0C0".find(params[:scrapbook]) .replace_html("main_picture".public_filename(:thumb)). :object => scrapbook) %> <% end %> </div> <div id="map" class="mediummap"><div id="map2"></div></div> ***** /library/show.:endcolor => "#114411" ***** /library/index. :object => @photo. { :action => 'slider'. :id => photo.rhtml ***** <div id="slider_page"> <div id="slider"> <%= render :partial => 'slider' %> </div> <div id="main_picture"> <%= render :partial => 'main_picture'. :locals => {:photo_size => ':medium'} %> </div> </div> ***** /library/toggle. :action => 'index' %></p> ***** /library/slider. :partial => "main_picture".rhtml ***** <div id="library_show"> <% for photo in @scrapbook.new @new_photo = photo.rjs ***** page. :per_page => 4 @page_title = "Listing All Scrapbooks" end def create begin photo = Photo.rjs ***** page["related_#{@toggle}"].photos %> <div class="thumb"> <%= link_to image_tag(photo.toggle ***** /library/update_slider. @scrapbooks = paginate :scrapbooks. :scrapbook => @scrapbook } %> </div> <% end %> </div> <%= link_to 'Back'.add_photo_with_pictures(params) @scrapbook = Scrapbook.Page 99 of 126 page[:current_item].

@photo) conn.find_not_in_scrapbook(@scrapbook) @page_title = "Add photos to #{@scrapbook.find_not_in_scrapbook(@scrapbook) end def destroy_connection @photo = Photo.photo. :object => @new_photo page.find(params[:id]) for connection in @scrapbook.find(params[:scrapbook]) conn = Connection.id}" page.find(params[:scrapbook]) if @scrapbook.find_by_scrapbook_id_and_photo_id(@scrapbook. "photo_#{@new_photo.find(photo_id) @scrapbook.find(params[:id]) @scrapbook = Scrapbook.photos << @photo @updated_photos << @photo end @not_in_scrapbook =Scrapbook.insert_html :bottom.reset "add_photo" end end end end def destroy @scrapbook= Scrapbook.default_photos do set_connection(@scrapbook.find(params[:id]) @updated_photos = Array.destroy @not_in_scrapbook =Scrapbook.default_photos.scrapbooks << @scrapbook rescue redirect_to_index("Failed to create the picture .form.do it properly") else responds_to_parent do render :update do |page| page.Page 100 of 126 @new_photo.find_not_in_scrapbook(@scrapbook) end def make_default_photo @new_default_photo = Photo.visual_effect :highlight.connections connection.false) .new for photo_id in params[:photo_ids] @photo = Photo. "scrapbook_sortable".destroy end def add_photos set_up @scrapbook = Scrapbook. :partial => 'current_photo'.destroy end @scrapbook.size > 0 for photo in @scrapbook.find(params[:id]) @not_in_scrapbook =Scrapbook.name} Scrapbook" end def add_photos_to_scrapbook @scrapbook = Scrapbook.find(params[:id]) @scrapbook = Scrapbook.

direction) render :update do |page| page["photo_#{photo.false) end end set_connection(@scrapbook.photo) conn.state) conn = Connection.find_by_scrapbook_id_and_photo_id(@scrapbook. :object => photo page["photo_#{photo.reload @not_in_scrapbook =Scrapbook.find(params[:scrapbook]) photo = Photo.rotate_image(picture.default_photo = state conn.find(params[:id]) spin_photo(photo.find( @scrapbook_sortable[0]) if @scrapbook.'left') end def spin_right @scrapbook = Scrapbook.id}"].find_not_in_scrapbook(@scrapbook) redirect_to :action => 'add_photos'.direction) picture = photo.move_to_top if state conn.photo) conn.move_to_top end default_photo = Photo.find(params[:scrapbook]) @scrapbook_sortable = params[:scrapbook_sortable] for photo_id in @scrapbook_sortable.id}"].@new_default_photo.Page 101 of 126 end end set_connection(@scrapbook.size > 0 for photo in @scrapbook.reload end def set_connection(scrapbook.true) @scrapbook.find(params[:id]) spin_photo(photo.default_photos.pictures.photo.'right') end def spin_photo(photo.reverse photo = Photo.first picture.find(photo_id) conn = Connection.true) @scrapbook.default_photo. :id => scrapbook.find(params[:scrapbook]) photo = Photo.id unless request.replace :partial => "current_photo".visual_effect :highlight end end def resort_scrapbook @scrapbook = Scrapbook.xhr? end def spin_left @scrapbook = Scrapbook.save end end .default_photos do set_connection(@scrapbook.photo.find_by_scrapbook_id_and_photo_id(scrapbook.

:icon=>scrapbook. :order => "date".find(session[:user_id]) end @tags = Tag. :limit => 10) @headers["Content-Type"] = "application/rss+xml" end end ***** google_controller.find(:all) end private def authorize unless User.new end end ***** feed_controller.:content => "Could not save the marker"} end .create_date = Time.icon} else res = {:success => false.save res = {:success => true.find(:all.rb ***** class FeedController < ApplicationController def photos set_up @photos = Photo.strftime("%d-%m-%Y %H:%M:%S") @cart = find_cart if session[:user_id] @user = User.rb ***** class ApplicationController < ActionController::Base # Pick a unique cookie name to distinguish our session data from others' session :session_key => '_scrapbook_session_id' def set_up() @current_time = Time. :action => "login") end end def find_cart session[:cart] ||= Cart.name}</div><div><strong>Description: </strong>#{scrapbook.now if scrapbook.description}</div>".find_by_id(session[:user_id]) session[:original_uri] = request. :content => "<div><strong>Name: </strong>#{scrapbook.rb ***** class GoogleController < ApplicationController def index set_up @page_title = "Locations on Google Maps" end def create begin scrapbook = Scrapbook.now.create(params[:s]) scrapbook.Page 102 of 126 ***** application.request_uri flash[:notice] = "Please Log In" redirect_to(:controller => "/login".

to_json end end ***** library_controller.find(params[:id]) @scrapbook = Scrapbook.find(params[:scrapbook]) rescue logger.Page 103 of 126 rescue res = {:success => false.rb ***** class LibraryController < ApplicationController def index set_up @scrapbooks = Scrapbook.name} Scrapbook" end def slider set_up @scrapbook = Scrapbook. ID passed: #{params[:id]}") else redirect_to_index unless request.error("Attempt to access invalid photo #{params[:id]}") redirect_to_index("Invalid Photo.xhr? end end def checkout set_up end def toggle @toggle = params[:tag_name] end .to_json end end def list render :text => Scrapbook.find_with_marker.name} Scrapbook" end def update_slider begin @photo = Photo.to_json else render :text => res.find_all_except_default @count = increment_count end def show set_up @scrapbook = Scrapbook.find(params[:id]) @page_title = "#{@scrapbook.find(params[:scrapbook]) @photo = Photo.:content => "Went pear shaped"} render :text => res.find(params[:id]) @page_title = "#{@scrapbook.

xhr? end end def empty_cart session[:cart] = nil redirect_to_index unless request.find(params[:id]) rescue ActiveRecord::RecordNotFound logger.add_hardCopy(photo) redirect_to_index unless request.new end def redirect_to_index(msg = nil) flash[:notice] = msg if msg redirect_to :action => :index end def increment_count session[:counter] ||= 0 session[:counter] += 1 end end ***** login_controller.xhr? end end def remove_from_cart begin @cart = find_cart photo = Photo.Page 104 of 126 def add_to_cart begin photo = Photo.remove_product(photo) redirect_to_index unless request. ID passed: #{params[:id]}") else @cart = find_cart @current_item = @cart.new(params[:user]) .find(params[:id]) rescue ActiveRecord::RecordNotFound logger. :except => :login def add_users set_up @user = User.xhr? end private def find_cart session[:cart] ||= Cart.rb ***** class LoginController < ApplicationController before_filter :authorize.error("Attempt to access invalid photo #{params[:id]}") redirect_to_index("Invalid product" ) else @current_item = @cart.error("Attempt to access invalid photo #{params[:id]}") redirect_to_index("Invalid Photo.

post? user = User.id uri = session[:original_uri] session[:original_uri] = nil redirect_to (uri || {:controller => 'library'.find(:all) end def login set_up session[:user_id] = nil if request.find(:all) end end def list_users set_up @page_title = "Listing All Users" @all_users = User.xml .find(:all) end end ***** pictures_controller.count @total_scrapbooks = Scrapbook.authenticate(params[:name].find(params[:id]) begin user.save @all_users = User. params[:password]) if user session[:user_id] = user. :action => 'index' end def index set_up @total_photos = Photo.Page 105 of 126 @user.count end def delete_users if request. :action => 'index'}) else flash[:notice] = "Invalid username/password combination" end end end def logout session[:user_id] = nil flash[:notice] = "Logged Out" redirect_to :controller => '/library'.post? user = User.rb ***** class PicturesController < ApplicationController # GET /pictures # GET /pictures.destroy rescue Exception => e flash[:notice] = e.message end @all_users = User.

Page 106 of 126

def index @pictures = Picture.find(:all) respond_to do |format| format.html # index.rhtml format.xml { render :xml => @pictures.to_xml } end end # GET /pictures/1 # GET /pictures/1.xml def show @picture = Picture.find(params[:id]) respond_to do |format| format.html # show.rhtml format.xml { render :xml => @picture.to_xml } end end # GET /pictures/new def new @picture = Picture.new end # GET /pictures/1;edit def edit @picture = Picture.find(params[:id]) end # POST /pictures # POST /pictures.xml def create @picture = Picture.new(params[:picture]) respond_to do |format| if @picture.save flash[:notice] = 'Picture was successfully created.' format.html { redirect_to picture_url(@picture) } format.xml { head :created, :location => picture_url(@picture) } else format.html { render :action => "new" } format.xml { render :xml => @picture.errors.to_xml } end end end # PUT /pictures/1 # PUT /pictures/1.xml def update @picture = Picture.find(params[:id]) respond_to do |format| if @picture.update_attributes(params[:picture]) flash[:notice] = 'Picture was successfully updated.'

Page 107 of 126

format.html { redirect_to picture_url(@picture) } format.xml { head :ok } else format.html { render :action => "edit" } format.xml { render :xml => @picture.errors.to_xml } end end end # DELETE /pictures/1 # DELETE /pictures/1.xml def destroy @picture = Picture.find(params[:id]) @picture.destroy respond_to do |format| format.html { redirect_to pictures_url } format.xml { head :ok } end end end ***** posts_controller.rb ***** class PostsController < ApplicationController def index list render :action => 'list' end # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html) verify :method => :post, :only => [ :destroy, :create, :update ], :redirect_to => { :action => :list } def list set_up @posts = Post.find :all end def show @post = Post.find(params[:id]) end def new @post = Post.new end def create @post = Post.new(params[:post]) if @post.save flash[:notice] = 'Post was successfully created.' redirect_to :action => 'list' else render :action => 'new' end end

Page 108 of 126

def edit @post = Post.find(params[:id]) end def update set_up @post = Post.find(params[:id]) if @post.update_attributes(params[:post]) flash[:notice] = 'Post was successfully updated.' redirect_to :action => 'show', :id => @post else render :action => 'edit' end end def comments set_up @post = Post.find(params[:id]) if @post.comments.create(:body => params[:comment]) flash[:notice] = 'Comment was successfully added.' else flash[:notice] = 'Failed to add Comment.' end redirect_to :action => 'show', :id => @post end def destroy Post.find(params[:id]).destroy redirect_to :action => 'list' end end ***** tag_controller.rb ***** class TagController < ApplicationController def list set_up @page_title = 'Listing tags' @tag_pages, @tags = paginate :tags, :order => :name, :per_page => 15 end def show set_up tag = params[:id] @page_title = "Photos tagged with #{tag}" @photos = Photo.find_tagged_with(:any => tag, :separator => ',') end end

MISCELLANEOUS ***** /public/javascripts/application.js *****
var centerLatitude = 51.465471; var centerLongitude = -0.143320;

var lng = latlng. } map = new GMap2(document. inputForm. var map. if (GBrowserIsCompatible()) { if (map2) { startZoom = 2.addListener(map. centerLongitude).}. inputForm. return false.onsubmit = function() {createMarkerInPrototype(). var map2 function init() { if (document. inputForm."/>' +'<input type="submit" value="create" />' +'<input type="hidden" id="longitude" name="s[lng]" value="'+lng+'" />' +'<input type="hidden" id="latitude" name="s[lat]" value="'+lat+'"/>' map.setCenter(new GLatLng(centerLatitude.latlng){ var inputForm = document. }).createElement("form"). var lat = latlng. } else { startZoom =13.function(overlay.innerHTML = '<fieldset style="width:150px.setAttribute("action".id='geocache-input'. if (!map2) { GEvent. } } function drawMap() { map2 = document.inputForm). map.getElementById("map")) { drawMap().addControl(new GSmallMapControl()). } } } . inputForm.Page 109 of 126 var startZoom = 13. map.openInfoWindow(latlng. startZoom)."click"."/>' +'<label for="description">Description</label>' +'<input type="text" id="description" name="s[description]"style="width:100%."").getElementById("map2").getElementById("map")).lat()."' +'<legend>New Scrapbook V14</legend>' +'<label for="name">Name</label>' +'<input type="text" id="name" name="s[name]" style="width:100%.lng(). listMarkers(). map.addControl(new GMapTypeControl()).

res.success) { alert(res.25). for (var i=0. } } }).content. icon. iconImage ) { if (iconImage != ''){ var icon = new GIcon(). icon. res. i++ ) { var marker=markers[i].Request('/google/create'. 'click'. var formValues=Form. i < markers. html. } else { var latlng = new GLatLng(parseFloat(lat).attributes.iconSize = new GSize(25. } function listMarkers () { new Ajax. { method: 'post'.value. new Ajax.serialize('geocache-input'). } GEvent.addOverlay(marker). .openInfoWindowHtml(markerHTML) }). onComplete: function(request) { res=eval( "(" + request.iconAnchor = new GPoint(14.image = iconImage. var marker = new GMarker(latlng.icon). map.length. map.icon).parseFloat(lng)). if(!res.addListener(marker.responseText + ")" ).infoWindowAnchor = new GPoint(14. var lat = $("latitude"). var marker = new GMarker(latlng).14). parameters: formValues.closeInfoWindow().value. } function addMarkerToMap(latlng.content).Request('/google/list'. icon. icon. } else { alert("didn't get in").responseText + ")" ). marker. var marker = addMarkerToMap(latlng. onComplete: function(request) { markers=eval( "(" + request. return marker. { method: 'post'.25). function() { var markerHTML = html.Page 110 of 126 function createMarkerInPrototype() { var lng = $("longitude").

id +'">Open Scrapbook</a></div>'.addOverlay(marker).lat. parameters: sendit }). new Ajax.serialize(sectionID).longtitude.addOverlay(marker) } function getGroupOrder() { var sections = document.Request('/admin/scrapbook/resort_scrapbook'.addListener(marker. html. iconImage ).getElementById("scrapbook_sortable").serialize("scrapbook_sortable"). } } } }). }).each(function(section) { var sectionID = section. alerttext += sectionID + ': ' + Sortable. asynchronous:true. 'click'. var html = '<div><b>Name:</b> ' + marker. var scrapbook = document. var marker = addMarkerToMap(latlng. evalScripts:true.parseFloat(lng)).icon. var lng=marker. function() { marker.openInfoWindowHtml(description) } ). } function addMarker(latitude.lng. sections.sequence(section) + '\n'. if (lat && lng) { var latlng = new GLatLng(parseFloat(lat). { method: 'post'. GEvent.id. . map. var alerttext = ''.longtitude)). map. var iconImage = marker.getAttribute('scrapbook').Page 111 of 126 var lat=marker.getElementsByClassName('scrapbook_sortable'). } function updateScrapbookOrder() { var fred = Sortable. var order = Sortable. alert(alerttext). var sendit = "scrapbook="+scrapbook+"&"+fred.description + '</div><div><a href="/library/show/' +marker.name + '</div><div><b>Description:</b> ' + marker.description) { var marker = new GMarker(new GLatLng(latitude.

php"). window. require_once("justfile. if (!$_POST['submit']) { ?> .Page 112 of 126 return false.onload=init.unload=GUnload.php"). } window. APPENDIX D: PHP and RoR Comparison Code (Sprint 0) ***** testgall.php ***** <html> <head> </head> <body> <?php include("connect.

It is a [$joe[1]]</b></p>")."</b>] bytes</p>". thumb and web versions of the original photo $uploadFile->makeWebTypes(). $thumbFile = $uploadFile->getFileName('thumb'). $webFile = $uploadFile->getFileName('web'). } // Set up temp array in case the response is the bad file type array $joe = $uploadFile->mimeToWord().$uploadFile->mimeToWord(). $origFile = $uploadFile->getFileName('orig'). // Display tiny. ?> . } else { echo "<p>The file is a [<b>". // Top yourself if the object isn't move than 0 bytes if (!$uploadFile->fileSize()) { die("<b><p>Bad upload!</b></p>"). // Move the photo to a new location . } // Display the size of the file echo "<p>The file size is [<b>"."</b>]</p>".$uploadFile->fileSize(). web and orig files in doc $tinyFile = $uploadFile->getFileName('tiny').Page 113 of 126 <form enctype="multipart/form-data" action="<?=$_SERVER['PHP_SELF']?>"method="post"> <b>Gallery Name (files go in default if no gallery provided):</b> <br> <input type="Text" name="gallery" size="40"> <p> File: <br> <input type="file" name="file"> <p> <input type="submit" name="submit" value="Add Image"> </form> <?php } else { // validate form data // Create the file object $uploadFile = new JustFile($_FILES['file']).thumb. // Display the details of the file that is converted $uploadFile->show(). // Create the tiny. // If unknown then top yourself otherwise print out the filetype if ($joe[0] == "Unknown") { die("<p><b>Not a photo filetype.a gallery // The gallery is the name provided or "default" $uploadFile->moveToGallery($_POST['gallery']).

$result = mysql_query($query) or die("Error in query: " . function __construct($file=false) { if ($file) { $this->file = $file. $result = mysql_query($query) or die("Error in query: " . "peter". require_once("photo. public $realFile. // copy file to new location copy($_FILES['file']['tmp_name']. $this->tmpFile = $file['tmp_name'].php ***** <?php @mysql_connect("localhost".going to be put in object // formulate and execute query $query = "INSERT INTO gallery (dsc) VALUES ('" .php'). . public $tmpFile.php"). } ?> </body> </html> ***** connect.Page 114 of 126 <img src="<?php echo $tinyFile ?>">Tiny File</> <img src="<?php echo $thumbFile ?>">Thumb File</> <img src="<?php echo $webFile ?>">Web File</> <img src="<?php echo $origFile ?>">Original File</> <?php // // OBJECTIFY!!! // open connection to database // Done via including connect. // update database with new file name $query = "UPDATE gallery SET filename = '$newFileName' WHERE id = '$id'". "')". //The $ext is done in the object in mimeToWord. $this->realFile = $file['name']. public $ext.this will not work now $newFileName = $id .php --. $_POST['desc'].. @mysql_select_db("webdbcw1") or die("<h1>PS-DB-2: Unable to select database</h1>"). public $destination = false. mysql_error()). $ext. "blahblah") or die("<h1>PS-DB-1: Unable to connect to database</h1>"). class JustFile { public $file = array(). "/mydatadir/" . $id = mysql_insert_id($connection). mysql_error()). ?> ***** justfile..php ***** <?php require_once('filedir. public $moved = false. $newFileName).

Page 115 of 126

} else { echo "<p><b>PS-OB-JF-Co: No file name </b></p>"; } $this->fileSize(); $this->mimeToWord(); } function fileSize() { $size = filesize($this->tmpFile); if ($this->file['size'] == 0) { echo "<p><b>PS-OB-JF-Fs: Bad file size [$this->file['size']] bytes</b></p>"; return false; break; } else { return $size; } } function mimeToWord() { switch ($this->file['type']){ case "image/jpeg": $tested = "JPEG"; $this->ext = ".jpg"; break; case "image/pjpeg": $tested = "JPEG"; $this->ext = ".jpg"; break; case "image/gif": $tested = "GIF"; $this->ext = ".gif"; break; default: $tested = array("Unknown",$this->file['type']); } return $tested; } function moveToGallery($galleryName) { $gallery = new FileDir($galleryName); if (!$gallery->dirExists()) { // echo "<p><b>PS-OB-JF-Mtg1: Needs to create the gallery directory</b></p>"; $newDir = $gallery->createDir(); $moveIt = $this->moveFile($gallery); if ($newDir && $moveIt) { $this->moved = true; // echo "<p><b>PS-OB-JF-Mtg2: Created the gallery directory and moved the file</b></p>"; return true; break; } } else { // echo "<p><b>PS-OB-JF-Mtg3: Gallery directory already exists</b></p>";

Page 116 of 126

//

$moveIt = $this->moveFile($gallery); if ($moveIt) { $this->moved = true; return true; echo "<p><b>PS-OB-JF-Mtg4: Moved the file</b></p>"; break; } } echo "<p><b>PS-OB-JF-Mtg5: FAILED to create gallery directory OR move the

file</b></p>"; return false; } function moveFile($gallery) { $this->destination = $gallery->fileDir.'/'.$this->realFile; echo "<p><b>This is destination [$this->destination]</b></p>"; if (move_uploaded_file($this->tmpFile, $this->destination)) { return true; break; } return false; } function makeWebTypes() { if ($this->moved) { $this->photo = new Photo($this->destination); $this->photo->createWebPics(); } else { echo "<p><b>PS-OB-JF-Mwp1: File needs to be moved first</b></p>"; } return false; } function getFileName($type) { if ($this->moved) { $getFile = $this->photo->newFileName($type); return $getFile; } else { return "<p><b>PS-OB-JF-Nfn1: File needs to be moved first</b></p>"; } } function show() { if ($this->moved) { $this->photo->show(); } } } ?>

//

Page 117 of 126

***** filedir.php *****
<?php // Written by Peter Shearan 12/8/7 // This class will be constructed with either the directory name supplied on instantiation // or will use "default" as the base directory. // // Methods are then provided for creating, listing, destroying and other information provision // about the object // class FileDir { public $fileDir; function __construct($fileDir) { if ($fileDir == "") { $this->fileDir = "default"; } else { $this->fileDir = $fileDir; } } function dirExists($gotOne = false) { if (!$gotOne) { $dirFind = $this->fileDir; } else { $dirFind = $gotOne; } // if(is_dir($dirFind)) { echo "<p><b>PS-OB-FD-De1: Directory $dirFind exists</b></p>"; return true; break; } else { echo "<p><b>PS-OB-FD-De2: Directory $dirFind does not exist</b></p>"; } return false; } function dirPermiss($dirFind) { // display directory permissions return false; } function dirList() { // list directory contents - TBC return false; }

//

public $photoOrigHeight = 0. public $photoBaseName. $this->dimensions().$isGallery = true) { if (!$dirName) { $newDir = $this->fileDir.php ***** <?php class Photo { public $photo. } else { return false.'/thumb'). if (!is_dir( $mkfolder )) { mkdir($newDir. mkdir($newDir. public $aspect. 0777). $mkfolder = ''. $i++ ) { $mkfolder . for( $i=0 . } } $mkfolder .'/web'). } $folder = explode( DIRECTORY_SEPARATOR . public $photoType. function __construct($photofile) { $this->photo = $photofile. public $photoOrigWidth = 0.Page 118 of 126 function createDir($dirName = false. } if ($this->dirExists($newDir)) { return true.= DIRECTORY_SEPARATOR. if ($isGallery) { mkdir($newDir. } . } } } ?> ***** photo. mkdir($newDir. public $origFileSize = 0.= $folder[$i]. isset( $folder[$i] ) .'/tiny'). } else { $newDir = $dirName. $newDir ).

if ($filemb >= 1 ) { $filenice = round($filemb.$high) { $size = $this->calculateDimensions($this->photoOrig['w'].$this>photoOrig['h']. } else { $filenice = round($filekb.100. } public function setAspect($wide.Page 119 of 126 private function dimensions() { $dims = getimagesize($this->photo).$high). } else { $this->aspect = "portrait".60. } } public function dispFileSize($filebytes) { $filekb = $filebytes/1024. $this->photoOrig['h']= $dims[1]. default: $tested = "Unknown". $this->photoOrig['w'] = $dims[0]. $this->mimeToWord($dims[mime]). } $this->photoType = $tested.$wide. 2).500. break.$high) { if ($wide/$high >= 1) { $this->aspect = "landscape". . } return $filenice. $this->origFileSize = filesize($this->photo). "KB". } elseif ($wide/$high == 0) { $this->aspect = "square".60).$wide. $this->convertPhotos('thumb'. $this->convertPhotos('web'. $this->setAspect($dims[0].100). "MB". $filemb = $filekb/1024.$dims[1]). } public function createWebPics() { if ($this->photoType != "Unknown") { $this->convertPhotos('tiny'.500). 2). } } function convertPhotos($type. } private function mimeToWord($fred) { switch ($fred){ case "image/jpeg": $tested = "JPEG".

$newFile).0. $ret['w'] = $ret['h'] * $ratio.$pathInfo['extension'].'/'. } function show() { $fs = $this->dispFileSize($this->origFileSize)."]<br /> The photo type is [$this->photoType]<br /> The original file size is [$fs]<br />".$type.''. } return $newFile. } } ?> ***** mapit. true).$this->photoOrig['w'].'.0.$size['h'].$srcPhoto.$size['h']). 'h' => $height).$this->photoOrig['h'].$type. $newFile = $pathInfo['dirname'].0."]<br /> This is the original Height [". } function calculateDimensions($width.Page 120 of 126 $load = 'ImageCreateFromJpeg'. $srcPhoto = $load($this->photo). } } return $ret. $save = 'ImageJpeg'. $ret['h'] = $ret['w']/$ratio.$this->photoOrig['w'].$this->photoOrig['h']). } else { $pathInfo = pathinfo($this->photo).$maxWidth. if ($ret['h'] > $maxHeight) { $ret['h'] = $maxHeight.$height.'.0. $ratio = $width/$height. echo "This is the file [$this->photo] <br /> The photo is [$this->aspect] <br /> This is the original Width [". } function __toString() { return var_export($this. $save($destPhoto.'/'.php ***** . $destPhoto = imageCreateTrueColor($size['w']. $newFile = $this->newFileName($type). imagecopyresampled($destPhoto.$pathInfo['filename'].$maxHeight) { $ret = array('w' => $width. if ( $width > $maxWidth || $height > $maxHeight) { $ret['w'] = $maxWidth. $size['w']. } function newFileName($type) { if ($type == 'orig') { $newFile = $this->photo.

com/maps?file=api&amp.google.org/1999/xhtml" xml:lang="en" lang="en"> <head> <meta http-equiv="Content-Type" content="text/html.org/TR/xhtml1/DTD/xhtml1-strict. } ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.Page 121 of 126 <?php if (isset($_GET['message'])) { $message = trim(strip_tags(stripslashes($_GET['message']))). return false.dtd"> <html xmlns="http://www.0 Strict//EN" "http://www.w3. ?> </div> <?php } else { ?> <div id="messages" style="display: none"></div> <?php } ?> <h3>Add a new location</h3> <form method="post" action="process_form.css" /> </head> <body onload="init('map'. } else { $message = ''.w3.js"></script> <link rel="stylesheet" type="text/css" href="style.php" onsubmit="submitForm(this).key=ABQIAAAAWBs8BZ4TjrRayH7tC7eahSQyRsya9AfG3FfrJQ0LsXZ4YxFeBRkUOIFOoHML_01ujzsfpBPoMadXA" type="text/javascript"></script> <script type="text/javascript" src="functions."> <table> <tr> <td>Name:</td> <td><input type="text" name="locname" maxlength="150" /></td> </tr> <tr> <td>Address:</td> <td><input type="text" name="address" maxlength="150" /></td> </tr> <tr> <td>City:</td> <td><input type="text" name="city" maxlength="150" /></td> . charset=utf-8"/> <title>Google map stuff</title> <script src="http://maps.'message')"> <div id="main"> <div id="map"></div> <div id="formwrapper"> <?php if (strlen($message) > 0) { ?> <div id="messages"> <?php echo htmlentities($message).v=2&amp.

'address' => ''. require_once('locations. 'county' => ''. $ajax = (bool) $_POST['ajax'].org.php require_once('dbconnector. $error = false. 'city' => ''. </td></tr> <tr><td colspan="2"> <A HREF="http://nearby.uk" TARGET="_blank">PostCode to co-ordinates at nearby.uk</A> </td></tr> </table> </form> </div> </div> </body> </html> ***** process_form.php').php').Page 122 of 126 </tr> <tr> <td>County:</td> <td><input type="text" name="county" maxlength="150" /></td> </tr> <tr> <td>Postcode:</td> <td><input type="text" name="postcode" maxlength="150" /></td> </tr> <tr><td> &nbsp. </td></tr> <tr><td> <input type="submit" value="Add Location" /> </td></tr> <tr><td> &nbsp. opendatabase(). $values = array('locname' => ''. 'postcode' => ''). $values[$field] = mysql_real_escape_string($val). foreach ($values as $field => $value) { $val = trim(strip_tags(stripslashes($_POST[$field]))). if (strlen($values[$field]) == 0) { .php ***** <?php //process_form.org.

urlencode($message)). } else { header('Location: mapit. $fred = $values[$field]. mysql_error(). $GLOBALS['pass'] = 'blahblah'. $message = 'Location has been added: '. $GLOBALS['host'] = 'localhost'. $exceptionstring . } else { mysql_select_db ($GLOBALS['db']. $GLOBALS['user'] = 'apressauth'.$values)).Page 123 of 126 $error = true. define ("MYSQLUSER".join("'. ": " .$GLOBALS['user']."blahblah").$fred. } return $db.php?message=' . mysql_query($query). $values['latitude'] = $location->lat. '. } } //Get the longitude and latitude $location = new Locations($values['postcode'])."apressauth"). $values['longitude'] = $location->long. } ?> ***** dbconnector.$db). define ("MYSQLDB". } catch (exception $e) { echo $e->getmessage(). $GLOBALS['db'] = 'taskdb'. } else { $query = sprintf("insert into store (%s) values ('%s')". try { if (!$db){ $exceptionstring = "<em>Error connecting to database:</em> <br />". join('. array_keys($values))."localhost"). if ($error) { $message = 'Error adding Location'."taskdb").php ***** <?php //dbconnector.php //define mysql connect variables define ("MYSQLHOST".$values['locname']. define ("MYSQLPASS". exit.$GLOBALS['pass']). throw new exception ($exceptionstring).= mysql_errno() . function opendatabase(){ $db = mysql_connect($GLOBALS['host']. } if ($ajax) { echo $message. . '".

public $long.uk/api/convert. $this->lat = $convert_output['ll-wgs84']['lat']. var locationsXml = 'locations. . var mapZoom = 2.js //div to hold the map var mapContainer = null.org. } function showMessage(msg) { msgContainer = document. private $nearbyKey = '90b6be422e81fc'. function __construct($postCode) { $this->locCode = preg_replace('/\s+/'. } function getLatLong() { $URL = $this->createURL().143320. return $URL.php ***** <?php class Locations { public $locCode.''. $this->long = $convert_output['ll-wgs84']['long']. } } ?> ***** locations.'&output=php&p='. $this->getLatLong().$postCode).php'. //co ords for SW4 0LE var mapLng = -0.getElementById("messages"). var mapLat = 51.replace(/^(\s+)?(\S*)(\s+)?$/.php'. } function createURL() { $URL = $this->uri.465471.js ***** //functions.$this->nearbyKey. public $lat.Page 124 of 126 die().nearby. private $uri = 'http://www. include("$URL$this->locCode"). function trim(str) { return str. } } ?> ***** function. '$2').'?key='.

request. mapLat).getAttribute("latitude"))). map.getAttribute('locname') + '</strong><br />' + markers[i].getAttribute('city') + '. for (var i = 0.getElementById(msgId).addControl(new GMapTypeControl()). var markers = xmlDoc. var theaddy = '<div class="location"><strong>' + markers[i].innerHTML = msg.length. parseFloat(markers[i]. map.getAttribute('county') + '<br />' + markers[i].create().Page 125 of 126 if (msg. } ). msgId) { // showMessage('').'location. theaddy). } function createInfoMarker(point.responseXML.display = 'block'. map.openInfoWindowHtml(theaddy). var marker = createInfoMarker(point.getAttribute('postcode') + '</div>'.centerAndZoom(new GPoint(mapLng. theaddy) { var marker = new GMarker(point).getElementsByTagName("marker"). map. var request = GXmlHttp. return marker. loadmap(). } .onreadystatechange = function() { if (request. else { msgContainer. "click".addOverlay(marker). msgContainer. mapZoom).php'.length == 0) msgContainer.getAttribute('address') + '<br />' + markers[i]. request.addListener(marker. GEvent.readyState == 4) { var xmlDoc = request.addControl(new GSmallMapControl()).documentElement.style. i++ ) { var point = new GPoint(parseFloat(markers[i]. msgContainer = document.getAttribute("longitude")).display = 'none'.open('POST'. mapContainer = document. } } function init(mapId. i < markers. } function loadmap() { var map = new GMap(mapContainer).' + markers[i].true).style.getElementById(mapId). function () { marker.

var xmlhttp = GXmlHttp.Page 126 of 126 // var polyline = new GPolyline([new GPoint(0.addOverlay(polyline).</b>". alert("This is values["+values+"]").4654708660116)]. true)..readyState == 4 && xmlhttp.51. } function submitForm(frm) { // alert("This is frm["+frm+"]"). // map.10). xmlhttp. setTimeout("loadmap()".send('a'). charset=UTF-8").responseText).action.send(values). var values = 'ajax=1'. } } xmlhttp. } errMsg += '</ul>\n'."application/x-www-form-urlencoded.getElementById("map"). var fields = { locname : 'You must enter the location name'. xmlhttp. for (var i = 0.length] = fields[field].1000). mapContainer.status == 200) { showMessage(xmlhttp.value. // } .onreadystatechange = function() { if (xmlhttp.51. postcode : 'You must enter a postcode' }. city : 'You must enter the city'.4616028588849).length == 0) { errors[errors.length.create(). county : 'You must enter a county'. } mapContainer = document.143319752644715."#ff0000".open("POST". showMessage(''). i++) { errMsg += '<li>' + errors[i] + '</li>\n'.length > 0 ) { var errMsg = '<strong>The following errors have occured: </strong><br /><ul>\n'.137574481662604. } if (errors. for (field in fields) { val = frm[field].frm. return false..setRequestHeader("Content-Type". if (trim(val)..new GPoint(0. xmlhttp. } } request. } values += '&' + field + '=' + escape(val). showMessage(errMsg). i < errors. address : 'You must enter a address'. var errors = [].innerHTML = "<b>Loading map.

Sign up to vote on this title
UsefulNot useful