Tuesday 27 March 2018

Prevent CSS and Javascript caching

Quite often, during the early days of a web site's/application's life, a developer can make minor corrections to JavaScript and CSS, whether to correct an issue, or in response to customer feedback.
Unfortunately, nearly all browsers cache these files to facilitate faster loading times during a revisit to a site. Whilst this is desirable for normal day to day use, it can be a developer's nightmare as they have to let the users, if known, know to refresh their cache, or if unknown, find another way to communicate this message.
However, there is a way around this.

The answer is known as using a phantom query string. Like requests for HTML pages, it is possible to append a query string to the references of the scripts and stylesheets. If these are unique on each call, then the file is reloaded from the internet, rather than the cache.
To accomplish this, it is as easy as adding a date and time stamp to the query part of the reference:
e.g.

    <script src="Scripts/script.js?636577391632578321"></script>
    <link href="Styles/style.css?636577391632578321" rel="stylesheet" />

If this date and time stamp is refreshed on each page load, then we can ensure that the file is also refreshed.
To accomplish this in PHP, we can use code like this:

    <link href="Styles/style.css?<?php echo time(); ?>" rel="stylesheet" />

Whereas in ASP/MVC, this will accomplish a similar task:

    <link href="Styles/style.css?@DateTime.Now.Ticks" rel="stylesheet" />

Use the same technique for JavaScript files.

Sunday 18 March 2018

VS Code save as Admin

For about 2 years now, VS Code has been my go to text editor. It has replaced BBEdit for standard text and NetBeans for web development. I even prefer it to MySQL Workbench when working with databases.
Part of the appeal of VS Code is its extensibility and the growing number of plugins that allow me to do almost anything.
However, I have had to keep BBEdit around for one particular scenario: saving system configuration files, or any file that requires elevated privileges. On Windows, this is not such an issue, as I normally run VS Code as Administrator, but on a Mac it has caused problems.
I was perusing the forums to see if there was a plugin to allow this feature, but no luck. Each of the posts and issues I read that were related to this, seemed to say that it wasn't going to happen for some time. So, BBEdit has to remain.

For clarity, the only reason I decided to go away from BBEdit was the lack of debugging features.

Out of curiosity, and to prove to myself, that this is still an issue, I opened VS Code this morning and attempted to edit my Hosts file, and then resave it... but this happened:


Did I miss this in the change log?
How long has this been here?

I clicked the 'Retry as Admin...' button, and low and behold, I was asked for my password and the file was saved.
Well done Microsoft (and community), you have now managed to convert me over to VS Code completely.


Saturday 17 March 2018

Regular Expressions in Swift 4

I have recently been looking at converting some of my Xojo code to Swift, more of an exercise to see how easy it is, rather than anything productive.
However, I found that using regular expressions in Swift was not as straight forward as in some languages, like Xojo, Javascript and C#.

The following example will show what you need to do to find a pattern in a string and print each match to the console:

if let regPattern = try? NSRegularExpression(pattern: pattern, options: []) {
    let matches = regPattern.matches(in: text, options: [],
                                     range: NSRange(location:0, length: text.count))
    for match in matches {
        for n in 0..<match.numberOfRanges {
            let range = match.range(at: n)
            let start = self.index(self.startIndex,
                                   offsetBy: range.lowerBound)
            let end = self.index(self.startIndex,
                                 offsetBy: range.upperBound)
            let substring = String(self[start..<end])
            print(substring)
        }
    }

}

As you can see, this is a lot to do for a simple regex search.

To simplify this, I have created some extension methods for the String class, that allow you to accomplish this with far less effort. These methods overload each other, to allow reducing complexity, whilst still allowing the full range of options, should it be required:

extension String {

    func regex(_ pattern:String, options: NSRegularExpression.Options,
               matchingOptions: NSRegularExpression.MatchingOptions,
               range: NSRange) -> [[String]] {
        var result = [[String]]()
        if let regPattern = try? NSRegularExpression(pattern: pattern,
                                                     options: options) {
            let matches = regPattern.matches(in: self, options:
                matchingOptions, range: range)
            for match in matches {
                var matchArray = [String]()
                for n in 0..<match.numberOfRanges {
                    let range = match.range(at: n)
                    let start = self.index(self.startIndex,
                                           offsetBy: range.lowerBound)
                    let end = self.index(self.startIndex,
                                          offsetBy: range.upperBound)
                    let substring = String(self[start..<end])
                    matchArray.append(substring)
                }
                result.append(matchArray)
            }
        }
        return result
    }

    func regex(_ pattern:String, options: NSRegularExpression.Options,
               matchingOptions: NSRegularExpression.MatchingOptions)
        -> [[String]] {
        return self.regex(pattern, options: options,
                          matchingOptions: matchingOptions,
                          range:NSRange(location:0, length:self.count))
    }

    func regex(_ pattern:String, options: NSRegularExpression.Options)
        -> [[String]] {
        return self.regex(pattern, options: options, matchingOptions: [])
    }

    func regex(_ pattern:String) -> [[String]] {
        return self.regex(pattern, options: [])
    }

}

These methods return an array of match arrays. In each of the match arrays, the first element is the entire match, whilst each of the subsequent elements match the capture blocks from the regular expression.
Working from the top to bottom, the methods reduce the complexity exposed to the user, allowing them to only use the options required.
If we start with the first method, you can see that it takes the full range of options for a regular expression search. This can be called by:

let matches = text.regex(pattern, options: [],
                         matchingOptions: [],
                         range: NSRange(location:0, length:text.count))

The next method assumes the range is the full size of the string:

let matches = text.regex(pattern, options: [],
                         matchingOptions: [])

The next method assumes that the matching options are the default:

let matches = text.regex(pattern, options: [])

The final method assumes the regular expressions requires the default options:

let matches = text.regex(pattern)

As you can see, we can reduce the code down to a very manageable call.

To see the results of the match, we can simply iterate over the returned array:

for match in matches {
    for item in match {
        print(item)
    }
}

Now we no longer need to mess around with the ranges and manually extract the strings from the text.