DotLessResult – MVC ActionResult for dotless

I’ve recently had the need to generate dynamic css for a themable MVC web application. I considered using a basic homegrown templating mechanism, or repurposing Razor, but then found the dotless project on the interwebs.

With the NuGet package it was relatively easy to get up & running, and I’ve abstracted the clunky regex code into an ActionResult subclass, recorded here in case it’s useful for somebody else.

    public class DotLessResult : ActionResult
    {
        public IDictionary<string, string> Parameters { get; set;}
        public string Less { get; set; }
        public bool Minify { get; set; }

        public DotLessResult(string less, IDictionary<string, string> parameters = null, bool minify = false)
        {
            Less = less;
            Parameters = parameters ?? new Dictionary<string, string>();
            Minify = minify;
        }
        
        public DotLessResult(Stream stream, IDictionary<string, string> parameters = null, bool minify = false)
            : this(new StreamReader(stream).ReadToEnd(), parameters, minify) { }

        public override void ExecuteResult(ControllerContext context)
        {
            var output = Less;
            foreach (var key in Parameters.Keys)
            {
                output = Regex.Replace(output, @"\s*@" + key + @":\s*\S+;", "@" + key + ":" + Parameters[key] + ";");
            }
            var css = dotless.Core.Less.Parse(output, new DotlessConfiguration { MinifyOutput = Minify });
            context.HttpContext.Response.ContentType = "text/css";
            using (var writer = new StreamWriter(context.HttpContext.Response.OutputStream, Encoding.UTF8)) {
                writer.Write(css);
                writer.Flush();
            }
        }
    }

This can then be used from your action method like so:

        public ActionResult Styles(string id)
        {
            var stream = GetType().Assembly.GetManifestResourceStream(stylePath + id.Replace(".css", ".less"));
            if (stream == null)
            {
                return HttpNotFound();
            }
            Dictionary<string, string> parameters = new Dictionary<string, string>();
            parameters["backgroundcolor"] = "#1f1400"; // continue for all replaceable parameters
            return new DotLessResult(stream, parameters, true);
        }

The .less source should be stored in your web assembly as an embedded resource, with configurable parameters declared using the syntax “@: <placeholderValue;". Then it's just a case of looping through the supplied parameters dictionary and changing the declaration in the .less source. In the example code here I’m looking for a .less resource with the same name as the requested .css file, but you can obviously use your imagination.

Advertisements

Google Analytics Razor Helper

I experimented today with the Razor helper syntax to consolidate generation of the Google Analytics tracking boilerplate (in ~/App_Code/Helpers.cshtml):

@helper GoogleAnalyticsTracker(string analyticsId) 
{
   if (!String.IsNullOrEmpty(analyticsId))
   {
   <script type="text/javascript">
            var _gaq = _gaq || [];
            _gaq.push(['_setAccount', '@analyticsId']);
            _gaq.push(['_trackPageview']);

            (function () {
                var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
                ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
                var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
            })();
    </script>
    }
}

which can then be used in your layout pages with:

@Helpers.GoogleAnalyticsTracker(Model.GoogleAnalyticsId)

It’s a bit unclear where this mechanism should fit between custom HtmlHelper extension methods and Razor partial views. I guess you’d be looking for functionality that’s less generic than an HtmlHelper extension – you can modify without recompiling, and it’s less simple to share between projects – and smaller/more generic than a partial.

The App_Code requirement for sharing between pages is not ideal, particularly as HtmlHelper extension methods aren’t available without workarounds. Hopefully the team get an update out in a reasonably timeframe.