Archive for July, 2007 Page 2 of 2

04JulMinimizing css files

Before I join the language debate I’d like to share something entirely different. My take on optimizing your css files for production.

Making css files production ready is surprisingly simple.  I use the following setup in my applications.

The css compressor (just removes whitespace in this configuration).

public class CssMinifier

    {

        static readonly Regex whitespaceRegex = new Regex(@"\s+", RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.ECMAScript);

 

        // You can uncomment the following line if you're not using css hacks that rely on comment notation.

        //static readonly Regex commentsRegex = new Regex(@"\/\*(.*?)\*\/ ", RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.ECMAScript);

 

        static readonly Regex addLineBreaksRegex = new Regex(@"\} ", RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.ECMAScript);

        static readonly Regex removeLastBreakRegex = new Regex(@"\n$", RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.ECMAScript);

        static readonly Regex trimInsideLeftBracketsRegex = new Regex(@" \{ ", RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.ECMAScript);

        static readonly Regex trimInsideRightBracketsRegex = new Regex(@"; \}", RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.ECMAScript);

 

        public static string Compress(string source)

        {

            source = whitespaceRegex.Replace(source, " ");

 

            // You can uncomment the following line if you're not using css hacks that rely on comment notation.

            //source = commentsRegex.Replace(source, string.Empty);

 

            source = addLineBreaksRegex.Replace(source, "}\r\n");

            source = removeLastBreakRegex.Replace(source, string.Empty);

            source = trimInsideLeftBracketsRegex.Replace(source, " {");

            source = trimInsideRightBracketsRegex.Replace(source, "}");

 

            return source;

        }

    }

(\cf4=”" <\cf1="" list\cf0="" new\cf0="" csslib="\par" >=”" .mappath(folder);\par=”" fileoperations\cf0=”" path=”\cf4″ folder)\par=”" target,=”" stringbuilder\cf0=”" readfolder(\cf4=”" void\cf0=”" static\cf0=”" private\cf0=”" );\par=”" ?\\r\\n?\cf0=”" target.append(\cf5=”" target.append(readfile(fpath));\par=”" fpath);\par=”" \\r\\n\\r\\n?\cf0=”" *=”" \{0\}=”" ?=”" target.appendformat(\cf5=”" fpath)\par=”" readpath(\cf4=”" ??\cf0=”" #endif\par=”" ??\cf1=”" .readalltext(path);\par=”" file\cf0=”" system.io.\cf4=”" #else\par=”" fileoperations.readfile(path);\par=”" return=”" ??\cf7=”" mode.\par=”" production=”" in=”" is=”" app=”" the=”" when=”" disk=”" from=”" read=”" file=”" cache=”" \cf6=”" !debug=”" #if\cf0=”" .mappath(relativepath);\par=”" relativepath)\par=”" readfile(\cf1=”" context.response.write(sb.tostring());\par=”" context.response.write(cssminifier.compress(sb.tostring()));\par=”" context.response.cache.setvaliduntilexpires(true);\par=”" context.response.cache.setexpires(datetime.now.adddays(1));\par=”" context.response.cache.setcacheability(httpcacheability.public);\par=”" mode\par=”" release=”" handler=”" this=”" of=”" results=”" caching=”" start=”" ;\par=”" css?\cf0=”" ?text=”" context.response.contenttype=”\cf5″ cssbase);\par=”" readfolder(sb,=”" ))\par=”" ?application?\cf0=”" (context.request.url.absoluteuri.contains(\cf5=”" ();\par=”" sb=”\cf1″ ];\par=”" ?windows?\cf0=”" windowstheme=”context.Request.QueryString[\cf5" context.response.clear();\par="" context)\par="" httpcontext\cf0="" processrequest(\cf4="" common="" ~="">I will typically have a FileOperations helper class that takes care of common file system things.


public class StylesHandler :IHttpHandler

    {

        private static string windowsTheme;

        private const string CSSBASE = "~/common/css";

 

        public void ProcessRequest(HttpContext context)

        {

            context.Response.Clear();

 

            windowsTheme = context.Request.QueryString["windows"];

 

            StringBuilder sb = new StringBuilder();

 

            if (context.Request.Url.AbsoluteUri.Contains("application"))

                ReadFolder(sb, CSSBASE);

 

            context.Response.ContentType = "text/css";

 

#if !DEBUG //start caching the results of this handler when in release mode

            context.Response.Cache.SetCacheability(HttpCacheability.Public);

            context.Response.Cache.SetExpires(DateTime.Now.AddDays(1));

            context.Response.Cache.SetValidUntilExpires(true);

            context.Response.Write(CssMinifier.Compress(sb.ToString()));

#else

            context.Response.Write(sb.ToString());

#endif

        }

 

        private static string ReadFile(string relativePath)

        {

            string path = FileOperations.MapPath(relativePath);

#if !DEBUG //Cache the file read from disk when the app is in production mode.

            return FileOperations.ReadFile(path);

#else

            return System.IO.File.ReadAllText(path);

#endif

        }

 

        private static void ReadPath(StringBuilder target, string fPath)

        {

            target.AppendFormat("/* {0} */\r\n\r\n", fPath);

            target.Append(ReadFile(fPath));

            target.Append("\r\n");

        }

 

        private static void ReadFolder(StringBuilder target, string folder)

        {

            string path = FileOperations.MapPath(folder);

 

            List<string> csslib =

                new List<string>(Directory.GetFiles(path, "*.css", SearchOption.TopDirectoryOnly));

 

            foreach (string s in csslib)

            {

                ReadPath(target, s);

            }

 

            if(!string.IsNullOrEmpty(windowsTheme))

            {

                ReadPath(target, string.Format("{0}/window_themes/{1}.css", CSSBASE, windowsTheme));

            }

        }

 

 

        public bool IsReusable

        {

            get { return true; }

        }

    }

03JulAbout javascript compression

If you believe this guy then javascript packing is a bad thing.

http://batiste.dosimple.ch/blog/2007-07/

I don’t agree with this post and here’s why (i couldn’t comment on his blog because he doesn’t have comments):

IMHO that’s not entirely correct. 
I’m sure that if you’re not packing stuff but just removing comments and whitespace you’ll get completely different results.

If you’re packing stuff then the reason should be because you want to obfuscate your javascript not because you want to get some speed improvement. It’s just common sense to work out that
1. javascript is slow
2. more javascript execution (i.e. un-obfuscating) is even slower.

Another benefit of packing javascript is that you/your company saves $ on bandwidth. 5k x 5000 pageviews x 30 days = lots of $.

The guy is just looking at 1 aspect or 1 reason which gives a distorted image.
Anyway my experience tells me that removing whitespace and comments in a production environment and then have them sent compressed by the webserver to the client gives me the best results. Regardless of this guys 1x test setup with 1 machine.

I will generally read all the files into one file strip it from extra content after which they get gzipped (deflate is better for xml type structures) and sent to the browser.  Once the javascript has been prepared for the page it should remain cached on the webserver and preferrably on the client.

02JulLunch with geeks – Wellington – 3/7/2007

Another week, another lunch.

This lunch had the biggest attendance yet.  I won’t be putting up all the names because I can’t remember them all :-$. We had about 20 people showing up, most of them were new faces. It was very good to see that Tim Haines could make it as well as Owen Evans. Another new addition was John Rusk and a couple of people from the TradeMe development team.

With lots of people it becomes a lot more difficult to follow all the conversations at once. I’ll do my best to write up the minutes, if I miss something please let me know so I can add it.

My start question for today was: How do people do integration testing?

The question is a bit on the broad side I admit but Owen Evans stepped up and gave us a rundown of how they do it. The first thing I got from his explanation is that there is a right way to write tests and a wrong way to write them. Because unit tests shouldn’t do integration testing etc.
He explained that in the beginning they had a build process that would take about 2 hours to complete and after refactoring their app to use the right way of testing it only takes them a couple of minutes.  Owen uses Fit and Fitnesse for doing the integration testing.  He also explained that integration tests are not tests you would want to run on every build but preferrably during the nightly build so you have something to look forward to when you start your programming day :)   Owen said a lot more but I can’t remember all of it -sorry Owen-
The guys at TradeMe have a manual testing process as do we (Xero).
I think JD was the first to mention watir for automated integration testing, which I second. I think watir or watin are beautiful tools as is Selenium.
For more info on which one fits your project best: http://adamesterline.com/2007/04/23/watin-watir-and-selenium-reviewed/

Somehow we got on the subject of source control and the different source control systems were using. My personal preference is subversion closely followed by Team Foundation Server.  Most people in our group liked subversion the best some of the reasons for it are the fact that it is not bound to a visual studio solution, the cost of subversion is also a major factor and the fact that it is not related to any specific technology and the fact that subversion can be easily integrated with other systems.

JD then asked who did use unit testing and it turns out that about 6 people actually did use unit testing. One of the reasons mentioned for people not using it was that tests take too much maintenance as your code evolves.  The response to that objection was that in that case you’re probably writing your tests wrong.  The benefits in my opinion were that you write code that is a lot more beautifull and you get a nice, warm fuzzy feeling inside called confidence in your code.

Next John Rusk took the floor bringing us back to the original question and he introduced us to a new term: exploratory testing. Which is a way of trying to bring structure to an unstructured process (manual testing). It wasn’t very clear to me where the distinction lies with manual testing except for the fact that the user that does the actual testing does need to have some knowledge of the application domain.

JB said that another good way to understand what your application is going to withstand is to take a couple of uneducated users (they don’t know anything about the application) and put them in front of a terminal and watch what they are doing. This gives you some very valuable insights for usabilty as well as finding some bugs.

My second question was: What differentiates an average developer from a great developer ?

This question didn’t get the same attention as the first one because the answers to this one were almost unanimously: Passion
The willingness to make mistakes, the drive to look for answers yourself, an inquisitve mind.

I personally don’t think of software development as being a science for me it’s more an art form which might be a good topic for next week.

02JulAlex James got his MVP

I’m a bit late in the line of congratulations, but I’d still want to congratulate Alex James with his being awarded an MVP.

He truly is a pilar in the NZ community for .NET


Recent Flickrs

    Blogroll

    Recent Listening

    Scrobbler