Dec 17, 2012

Applying MVC to your frontend application using Require and Backbone

I've been looking into BackboneJS for quite sometime now and thought it would interesting to build an application using the MVC pattern. Although Backbone provides various components - Models, Collections, Routers and Views; it doesn't provide a framework that can help you to structure the application using these components. While working on this application, I realized that I can separate these components in such a way that they can be made reusable or rather loosely coupled by defining the components in separate files. However, there was an interesting problem that I came across where the Views had a dependency on the Collections to be loaded first and then render the template data. I had read a little about RequireJS that allowed modules to be loaded asynchronously and also in resolving dependency. Thought I would give it a try and see whether it addresses this problem.

Nov 14, 2012

Using Routers in Backbone.js

After taking a break for a few days and then joining my new employer, I'm writing this long pending post on Routers in Backbone. A Router can be considered as a Controller in a MVC application. Controller in any MVC application defines how the incoming request should be handled. For example, a Servlet in a J2EE application accepts the request and looks into the configuration and delegates the request to one of the handlers.

Nov 5, 2012

Hello Myntra

Another big announcement today. I've joined which is India's largest online fashion store. I've ordered stuff from Myntra a few times in the past and it has always been a great experience buying from them. Now that I'm working with them, I'll buy more :)

More importantly it's a new day and beginning of a new journey. I'm all excited about my new job and I hope to learn a lot in coming days. Wish me luck!!

Oct 26, 2012

Good Bye Adobe

The title says it all. Today was my last working day at Adobe and I've decided to pursue my career elsewhere. I've spent more than three and half years at Adobe, working as a part of the ColdFusion Engineering team. Over the last few years I've learnt a lot from the team, from the ColdFusion community and from many others at Adobe. I've developed interests in HTML5, JavaScript and other related technologies and frameworks and I've decided to pursue my interests.

Oct 17, 2012

Backbone.js - Creating a RESTful CRUD application

I've been trying to build a CRUD application using Backbone.js and was able to retrieve a set of records into a collection using the fetch method. To perform other operations i.e. Create, Update and Delete I could always invoke Backbone.sync but I was exploring on the lines where this is performed implicitly. The fetch method sends an implicit GET request on url specified in the Collection, similarly I was looking for other methods that allow you to send POST, PUT and DELETE requests to the url. While I was building this application, I did come across a condition where Backbone was not sending a request. I was finally able to figure out as to why that happened and then it was a simple fix in my Backbone application as well as in the REST service.

Oct 12, 2012

Backbone.js - Parsing the response from the server

From past few weeks, I've been learning Backbone.js in great detail and I think it's a great framework that helps you modularize your code easily. Last week I wrote about 'Model validation in constructor' and then started to look at Collections in Backbone. The Model objects can be viewed as table rows and the Collection as a table. A Collection can declare the model property and indicate what kind of data it will hold. I was looking into ways in which a Collection can be populated by fetching the model data from the server. One way to do that is to ask the Model to fetch the data and then add the response to the Collection. The other way of doing this is to fetch the Collection data directly from the server i.e. instead of defining a Model you create a Collection by fetching the data from the server. Usually when you send a request to the server, the response data is essentially a collection of objects. In this case you really don't need a Model to be defined.

Oct 5, 2012

Backbone.js - Model validation in constructor

I've started to look at various Design Patterns in JavaScript and one of most popular Design Patterns in any language is the MVC Pattern. In JavaScript, there are various libraries out there which helps in modularizing the application. However, I've heard a lot about Backbone.js and I thought this would be the right time to give it a try. I've not learnt Backbone completely, but I've gained a good understanding of the Model part of the MVC in Backbone.

Sep 24, 2012

Pushing Ajax responses using Observer Pattern in JavaScript

Last week I'd played around with couple of design patterns in JavaScript (Constructor and Module pattern). I really liked the Module pattern i.e. the approach taken in JavaScript to enable encapsulation of data in a Class (functions in JavaScript). I was building an application using these Design patterns but found that making Ajax request inside a function in a module was not the right approach. JavaScript would send a request and start executing the next statement. I wanted to use an approach that would push the data from the Model whenever there was some new data available. This lead me to try the 'Observer pattern' in JavaScript.

Sep 12, 2012

Module Pattern in JavaScript

Couple of days back I wrote about the Constructor Pattern in JavaScript wherein I explained how classes can be simulated using functions in JavaScript. Also, on how the prototype property can be used to extend another class. The other important element in any of the Object-Oriented programming language is the concept of encapsulation i.e. providing private members that can be accessed only by the members of the same class. I came across the Module Pattern today and found that it is quite easy to achieve encapsulation in JavaScript. Though the variables in JavaScript can't be declared as private or public, but closures can be used to emulate encapsulation.

Sep 10, 2012

Constructor Pattern in JavaScript

Over the last few weeks I've been learning Object-Oriented concepts in JavaScript and have been building applications on top of it. I've an understanding of Object-Oriented concepts in many of the Server side programming languages and was trying to draw parallel lines with JavaScript. As it turns out, JavaScript is a class-less language but the concept of classes can be simulated using functions. In JavaScript, functions are not primitive types but are special kind of objects and hence you can set properties on them and can also invoke methods on them.

Aug 16, 2012

CSS FlexBox layout

Today I was trying to understand the FlexBox layout introduced in CSS and was impressed with what a few CSS properties can do to help you solve layout issues. The standard itself has gone through some iterations, but I believe the current one is stabilised and is not likely change completely. Anyway, the layout proposed here helps you align the elements in the DOM tree horizontally and vertically. It also helps you reorder these elements.

Aug 13, 2012

Uploading chunks of a large file using XHR2

I was having a conversation with my college friend about the enhancements in XHR2 and I was showing him how one can upload a file to the server using plain Ajax. He asked me whether I can upload a large file to the server such as a video file. I tried doing that but was bumped when the server reported with 400 error telling me that the 'POST size has exceeded maximum limit'. This got me thinking whether I can upload chunks of a large file to the server. I referred to the FileSystem API and came across the slice method that allows you to get a fragment of a file.

Jun 21, 2012

Using Source binding and templates in KendoUI

Yesterday I explored the MVVM design pattern in Kendo UI that allows the model data to be separated from the view. Also, whenever data in one of them changes it is reflected in the other. Today I was exploring various data bindings that can be used and one that caught my attention was the source binding. The source binding allows you to set the HTML content of the target element by rendering a Kendo template with a View-Model value. For example, say you have a combo box (select tag) and you want to populate it with data (option tags), then instead of writing several option tags you can define a template and provide source binding for the same. This will show a list of options from the ViewModel.

Jun 20, 2012

Using Model-View ViewModel design pattern in Kendo UI

Kendo UI is completely new to me and I got introduced to it when Brandon Satrom left Microsoft and joined the Kendo UI team. I had interacted with him when I was working on jQuery ‘Pinify’ plugin. Kendo UI is a HTML5, jQuery based framework for building both web and mobile applications. It not only provides a set of UI widgets and other data visualization components but also a framework for data binding, animation and drag-and-drop. Whilst I was looking into the framework I stumbled upon the Mode-View ViewModel (MVVM) design pattern built into it.

May 23, 2012

Book review: Steve Jobs Way by Jay Elliot

I finally completed reading ‘Steve Jobs Way’ by Jay Elliot. I wanted to know how Apple became so innovative and so successful and I knew that Steve did an awesome job to bring this company to a state which is very well known today. Before I started reading this book I had a notion that this book would focus on how hard work or dedication would pay off and all that stuff that one would generally see in any book. This book was different. Different in a way that it taught me the qualities a leader in any industry should have.

May 17, 2012

Adobe Developer Connect article on RESTful Web Services in ColdFusion 10

My Adobe Developer Connect article on 'Getting Started with RESTful Web Services in ColdFusion 10' is now live. Learn how you can get started with creating, publishing and consuming RESTful Web Services in ColdFusion.

May 14, 2012

CSS media queries in JavaScript

I like the media queries feature added to CSS 3, that allows web application developers or designers to define styling sheets for a range of output devices. A media query consists of a media type and one or more expressions that limits the style sheet scope. I was wondering if JavaScript can be used to update the elements in page, say changing the source attribute of an image based on the device width. Turns out there is a function matchMedia which accepts a media query string as input and returns the result.

May 7, 2012

Using HTML5's FullScreen API

I've been looking into ways in which a web application can be made more user friendly and HTML5 does include some powerful features such as PageVisibility, Navigation Timing, etc,. that can be used to provide good user experience. Today I was looking into FullScreen API that allows you show any of the elements in the DOM in Full-Screen mode. Suppose you have a HTML5 video embedded in a page and would like to play that video in full-screen mode then it is now possible with the FullScreen API. Similarly say if you have an image element, it can also be shown in full screen mode.

May 4, 2012

ColdFusion 10: Application specific VFS

I’ve always liked the Virtual File System (VFS) implementation in ColdFusion, but I always griped about one thing. The files written to the VFS are available at the Server level and are not specific to an application. However, ColdFusion 10 now includes support for Application specific in-memory file system. This means that, if you write a file to the virtual file system, only those files in the same application can access it. If an attempt is made by another application to access this file, the server would happily throw an error.

Apr 18, 2012

Pushing HTML5 Video content over ColdFusion WebSockets

I’ve been playing with the WebSocket feature introduced in ColdFusion 10 for some time now. I was trying out pushing images over a ColdFusion WebSocket channel and it worked just fine. But this time I wanted to put WebSockets to test and wanted to push large data at regular intervals. I thought maybe I can push video data over WebSockets and it turned out that there is no direct way to stream video data to many clients. I came across the function - drawImage that can be used to draw an Image or Video on a HTML5 Canvas. Once an image is drawn on the Canvas, it’s base64 encoded data can be obtained by calling the toDataURL function on the Canvas object.  This data can then be transferred over a ColdFusion WebSocket to all subscribers who can then use this  data to draw the image(video frame) on a Canvas.

Apr 13, 2012

Anchor elements in jQueryMobile referring to the same page retain the state

This week I started to learn jQueryMobile and was going through the online documentation that explained the basics. While I was trying out the sample examples, I came across an issue that I thought I’ll share with you. I had posted this on the jQueryMobile forum but I didn’t receive any solution to this. The issue here is when you have two anchor elements with its ‘href’ attribute pointing to the same div (#child data-role=”page”) with one marked as a button and the other marked as a popup dialog, then which ever button you choose first its action will be applied to the other button as well. Say if you click the second button that shows the dialog box containing the page it works fine, but now when you click the first button it shows the dialog box instead of showing it as a page. This is how it works the other way too i.e. if you click the first button the page is shown (as expected), but on clicking the second button it shows the page instead of displaying it as a dialog box.

Apr 2, 2012

Content flow in CSS3 regions

Today I was reading about CSS3 and I stumbled upon CSS3 regions. I was completely flabbergasted with what I saw and learnt today. Imagine that you’re trying to build a website for a magazine containing multiple columns (say 3). It would be arduous to fix the textual content on a particular column and then move the rest of the text to other columns. Now with Content Flow mechanism, the extra content can be moved to other columns in the layout with ease. There are just a couple of properties that you need to define in the styling sheet and you’re done. But this is not it. When you resize the browser window, the content automatically flows to other regions depending on the browser size.

Mar 29, 2012

ColdFusion 10: For-In construct to loop over Query and List

The for-in loop construct in ColdFusion is generally used to loop over a structure variable. In ColdFusion 10, one can use the same for-in construct to loop over Query and List variables. Here is an example in which the for-in construct is used to loop over Query and List variables.

Mar 20, 2012

ColdFusion 10: Using filterCriteria in WebSockets for subscribing and publishing

Yesterday Ben Nadel asked me a question on Twitter, on using filterCriteria when publishing a message on a web socket channel from server side. The method ‘wspublish’ allows you to perform a server side push to a client who has subscribed to a channel. It takes three parameters – channelName, message and filterCriteria. I always assumed that the values present in subscriberInfo and publisherInfo can be compared in Channel Listener functions (canSendMessage, beforePublish etc) before the message can be received by the client. Although this technique is available, what I found after having a discussion with a fellow developer (Awdhesh), is that I can specify simple conditions when subscribing or publishing.

Mar 19, 2012

ColdFusion 10: Working with REST Sub-resources

In REST everything is treated as a resource and each resource is associated with a URI from which it can be accessed. As mentioned in my series of posts on REST; in ColdFusion methods defined in a CFC are made RESTful by adding the attributes httpmethod and restpath. The restpath value specified in the method can then be used to access the resource. Although this would serve the given purpose, it would be a good idea to have a root resource resolve a generic URL and then have different methods that resolve other path of the URL.

Mar 9, 2012

WebSocket authentication in ColdFusion 10

I was looking into ways in which users can be authenticated before they start to receive or send messages over a WebSocket channel in ColdFusion. I found that there are two ways in which this can be done. The first approach is to call the 'authenticate' method on the socket object. The other way is to use cflogin to authenticate the user. The authentication level can be taken a step further in various channel listener functions. By implementing these listener functions, some sort business logic can be devised wherein the logged in user with certain credentials can be allowed to subscribe or publish to a channel.

Mar 7, 2012

HTML5 WebSockets in ColdFusion 10 - Workflow diagram

The HTML5 Web Sockets represent the next evolution of web communications. A WebSocket is a full duplex, bidirectional communication channel that operates over a single TCP socket. It has become a standard for building real time web applications. In ColdFusion 10, a messaging layer has been provided that implements the Web Socket protocol. It enables you to build applications that are based on publisher/subscriber model and applications that are based on subscriber model wherein a push from a server is possible.

Mar 6, 2012

ColdFusion 10: Disabling request timeout

A very quick post on disabling timeout for the requests. You can now set the requesttimeout to zero in cfsetting tag i.e.

<cfsetting requesttimeout=0>

Mar 5, 2012

ColdFusion 10: Making REST Pathparams optional

I was working on creating a CRUD application with REST and wanted to retrieve records with a GET request. The GET request would contain the ARTID for which the record has to be retrieved in the URL itself. To do this I had added an argument to the method implementing the GET request with ‘restargsource’ attribute set to path. The URL would be of the form:


However, in another scenario I wanted to retrieve all the records from the ART table when there is no ARTID specified in the URI i.e. with the URL:


PathParams are always required and if they’re not specified then a ‘404 Not found’ error would be thrown. I thought of adding another method to handle this GET request with no PathParams. But, it would have made my code look clumsy and semantically not correct. However, I was able resolve this by providing a regular expression for the PathParam argument.

Mar 2, 2012

ColdFusion 10: New function arraySlice

A quick post on the new function arraySlice added to the list of functions in ColdFusion 10. The arraySlice function is used to  select a part of the array. It takes three arguments – array, offset and length.

Mar 1, 2012

ColdFusion 10: CFFILE Restricting file types to upload

In ColdFusion 10, one can restrict the type of file being uploaded to the server when using CFFILE to upload the files. The new attribute accept allows the user to specify various MIME types or extensions of the file that can be accepted by the server. If the user tries to upload a file with a .txt extension but it contains xml data (application/xml MIME type) then the server would accept or throw an exception based on the value specified for the strict attribute. 'strict' is a boolean attribute added to the CFFILE tag. By default it is true and therefore wouldn't allow the user to upload a file whose contents are of different MIME type. When the strict attribute is set to false it would allow the user to upload a file irrespective of its content. However, an error would be thrown if the extension of the file doesn't match the ones specified in the accept attribute.

Feb 29, 2012

ColdFusion 10: REST settings in Application.cfc

There are a couple of variables that have been introduced in Application.cfc which are REST specific. These are this.restsettings.cfclocation and this.restsettings.skipCFCWithError. If you have a list of directories containing REST enabled CFCs then you can specify the same in the variable this.restsettings.cfclocation. At the time of registration, the specified directories and its subdirectories will be scanned for REST enabled CFCs and then deployed. If any of these CFCs contain compilation errors then an error is thrown and the registration would fail. To tackle this another variable this.restsettings.skipCFCWithError is provided. When set to true, the CFCs with compilation errors would be skipped. Only those without any compilation errors would be deployed successfully.

Feb 28, 2012

ColdFusion 10: onAbort and onRequestEnd behavior

Today, I wrote a post on the new lifecycle method - ‘onAbort’, which can be defined in an Applicaiton.cfc file. It is invoked when the cfabort tag is executed. David Boyer, asked me whether the onRequestEnd method is also invoked on executing the cfabort tag. In CF 9.0.1 it did invoke the onRequestEnd method. See Ben’s post:
Now in CF 10, this behavior has changed. The onRequestEnd method is no longer invoked when cfabort, cflocation or cfcontent tag is executed. The onAbort handler method defined in Application.cfc file would be invoked on executing these tags. Also, the onRequestEnd method would not be invoked even if the abort handler(onAbort) is not defined in Application.cfc.

ColdFusion 10 : onAbort method in Application.cfc

I was experimenting with a new method 'onAbort' introduced in ColdFuion 10 which can be defined in an Application.cfc file. This method is invoked when cfabort is called. What if showerror attribute is also present in the cfabort tag? Would it still invoke the onAbort method and then onError? The onAbort method would be ignored and the onError method would be invoked. Even in a case wherein the onError method is not defined in Application.cfc, the onAbort method wouldn't be invoked. In this case the error message would be shown on the standard error template.

Feb 27, 2012

ColdFusion 10: Returning Complex data from a REST service

There are various complex types in ColdFusion – Array, Struct, Query. When a REST service in ColdFusion returns one of these complex types, it has to be serialized to either JSON or XML format. As explained in my previous post, the HTTP protocol can be used in content type negotiation. You can specify the desired content type either by specifying it at the end of the URL or in the Accept header of HTTP request. In this post, I’ll explain the format in which the complex types are returned from a ColdFusion REST service.

Feb 26, 2012

ColdFusion 10: CFFILE - Specifying file content in the tag body

Prior to ColdFusion 10, to write or append to a file one had to specify the file content in the output attribute of CFFILE tag. In ColdFusion 10, you can specify the file content in the body of the cffile tag. In cases where the file content is specified in body as well as in the output attribute, the output attribute would be ignored.

Feb 25, 2012

ColdFusion 10: Geo-location on a Google map created with CFMAP tag

In ColdFusion 10, you can now show the users location on a Google map created with the CFMAP tag. The CFMAP and CFMAPITEM tags now have a boolean attribute showuser. When set to true the browser would ask for the users permission and then display users geo-location on the map. This is now supported in most of the modern browsers. In cases where the browser doesn’t support Geo-location API then the values provided in centeraddress or centerlatitude\centerlongitude attributes will be used to add a marker in the Google map.

Feb 24, 2012

ColdFusion 10: HTTP Content Negotiation + REST– Part 2

Following with my previous post on how content negotiation between the client and server help in invoking an appropriate REST service. In this post I’ll explain how one can specify multiple mime types in the request header and also the quality factor that enables server to decide which mime type to serve for the incoming request. The client sends a HTTP request to the Server along with several headers, one of them is the Accept header. The Accept header can contain a list of mime types that the client (user agent) is willing to process. These mime types are separated by a comma and may optionally be combined with the quality factor.

A CFC can contain several methods with produces attribute set to a specific mime type. The Accept header may contain several mime types:

text/html, text/plain, application/xml, application/json, */*

Server would parse the Accept header and will search for a REST service that matches the mime type specified first in the Accept header list. In this case ‘text/html’, if the same is not found then the next one in the list – text/plain. The client would specify the order of preference in the Accept header.

The Accept header can also specify the quality factory along with the mime types which defines relative degree of preference between different mime types. Here the header will be of the form:

text/html;q=0.7, text/plain;q=0.8, application/xml;q=0.9

Here the preference for each of the mime types is specified as quality factor in the header. The method with produces attribute set to ‘application/xml’ would be invoked because its quality factor – 0.9 is higher than the other mime types. By default the quality factory is 1 and it can have values between 0 and 1.

A REST request can also be sent through a browser by providing the URI in the address bar. Here the browser would send the default Accept header and an appropriate REST service would be invoked. Therefore if you try to send the same request from two different browsers, it would return different results.

Feb 23, 2012

ColdFusion 10: Using HTTP Content Negotiation to invoke a REST service

The HTTP protocol provides a Content negotiation mechanism using which different formats of the document can be served using the same URI. For example, a JavaScript application can request the content in JSON format and an external system say a Java client can request for the same content in XML format. Here the clients need to specify the content format in the Accept attribute of the HTTP request. The REST service can specify the format in which the data will be returned to the client in ‘produces’ attribute.

Feb 22, 2012

ColdFusion 10: Understanding REST parameters

As mentioned in my earlier post there are various ways in which data can passed to a REST service. The cfargument tag has a new attribute restargsource, it can take one of these values – path, query, matrix, form, cookie and header.

Feb 21, 2012

ColdFusion 10: Accessing a REST service without specifying the Application name or Service mapping in URL

In my previous post, I’d explained how REST services can be created, published and accessed in ColdFusion 10. Andy, asked me “Is there any way to avoid having the /rest/restapp/ in the URL?”. In short Yes. ‘rest’ in the URL lets the ColdFusion Server know that the incoming request is for a REST service. As mentioned in my previous post, you can update the servlet mapping defined in web.xml located at cfusion_home\wwwroot\WEB-INF directory. But it can't be avoided. Coming to the ‘restapp’ in the URL which indicates the Application name or the Servlet mapping. It can be removed from the URL by placing the CFCs in a default directory. While registering a REST service in the ColdFusion Administrator, there is an option to set the service as a default service, meaning all the CFCs placed in the directory would not require the Application name or Service mapping to be provided in the URL.

Feb 20, 2012

ColdFusion 10: RESTful WebServices in ColdFusion - Introduction

In ColdFusion 10, support for creating and publishing REST services has been added. The ColdFusion components can now be made available as REST services and these services can be consumed by various clients. REST stands for Representational State Transfer. It is an architectural style which is based on web standards and HTTP protocol. The idea here is to use HTTP protocol instead of complex mechanisms such as CORBA, RPC or SOAP to connect between the machines. In fact, the World Wide Web which is based on HTTP can be viewed as a REST based architecture.

Feb 19, 2012

ColdFusion 10: Using arrays in queryNew and queryAddRow

The queryNew() and queryAddRow() functions are used to create a query data structure and add empty rows to it. To add record sets to the query, function querySetCell() is used. If there are say three columns and you want to add say five rows to the query, then you would have about sixteen lines of code. That's huge and it will increase with number of columns and the number of records that you want to add. In ColdFusion 10 there's a third parameter introduced in queryNew() and queryAddRow() functions. The third parameter can accept either a struct,  an array of structs. Ray Camden had covered this in one of his POTW posts. One feedback that came from one of the ColdFusion users is to use an array or a two dimensional array to initialize or add rows to the query. This has now been implemented and is available in the public beta release. Here’s how it works:

Feb 18, 2012

ColdFusion 10: concatenate arrays using arrayAppend function

The arrayAppend function in ColdFusion is used to append an element to the end of the array. Now in ColdFusion 10, it is possible to concatenate two arrays using the same arrayAppend function. A third parameter of type boolean is added to the function; when it is set to true the two arrays are concatenated.

Now Showing - ColdFusion Zeus

After more than 20 months of research, planning, development and keeping  you all in suspense about the next version of ColdFusion, code named  Zeus; I'm finally proud to announce the release of the public beta version of none else than ColdFusion Zeus. You are free to download it here and report bugs here.

This release comes with tonnes of new features and enhancements. It not only focuses on HTML5 features but also provides rich features such as RESTful WebServices, Closures, Java Integration, Security enhancements and many more.

I wanted to create a picture that depicted all the new features in ColdFusion 10, like I did when public beta of CFBuilder 2 was released. But I couldn't since its a massive release and capturing everything in one single picture was not possible at all. However, I managed to create an app (like a movie clip) and hence the title 'Now Showing - ColdFusion Zeus'. You can access the app here. Click on the chapters and learn what's new in this mega release. The app is best experienced on Chrome, Firefox or Safari.

Jan 23, 2012

ColdFusion Tip: How to tell if path is file or directory

Today, one of the ColdFusion users asked me a question "Is there anyway I can find out whether a path is a directory or a file? There is no isDirectory() function available in ColdFusion and I don't want to write Java code to do this. Any help?"
For a moment I thought really! is there no way that ColdFusion can tell you whether a given path is of a directory or file? But sooner, I came across the function 'getFileInfo' which takes the path as an argument and returns a struct data that contains various metadata properties of the file. The struct includes a key - 'type' whose value can either be a directory or file. The below code shows how you can determine whether the given path is of a directory or file:

Jan 16, 2012

2011 - A year full of surprises

I know it's been more than 15 days since the world celebrated new year and every one made some big and small resolutions which they hope they will live up to in this year. I had a fair 2011; there were a few ups and downs but in the end its gone and 2012 is here. Last year I presented ColdFusion Builder 2 at SOTR on the very day it was released to the public (how exciting!!). I was very much excited and nervous at the same time since this was my first talk at a conference. Nevertheless it went well, I have said this before and will say it again, 'Edinburgh is beautiful'.