Initial commit — 16 posts converted from dasBlog XML, media assets
@@ -0,0 +1,8 @@
|
|||||||
|
# popcyclical.com blog archive
|
||||||
|
|
||||||
|
Archived content from the popcyclical.com dasBlog site (2009–2013).
|
||||||
|
|
||||||
|
- `posts/` — blog posts converted from dasBlog XML to Markdown
|
||||||
|
- `media/` — post images and attachments
|
||||||
|
|
||||||
|
One post (2012-06-26) was truncated/corrupted in the source data and could not be converted.
|
||||||
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 8.7 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 78 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 6.6 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 526 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 5.3 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 104 KiB |
|
After Width: | Height: | Size: 104 KiB |
|
After Width: | Height: | Size: 129 KiB |
|
After Width: | Height: | Size: 85 KiB |
|
After Width: | Height: | Size: 177 KiB |
|
After Width: | Height: | Size: 114 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 212 KiB |
|
After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 74 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 90 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 5.6 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 9.4 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 7.6 KiB |
|
After Width: | Height: | Size: 7.0 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 8.7 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 6.7 KiB |
@@ -0,0 +1,94 @@
|
|||||||
|
function convertXvalToValidateOptions(xRulesConfig) {
|
||||||
|
// this is adapted from xVal.jquery.validate.js so that it can generate
|
||||||
|
// the rule/message set rather than binding rules directly to DOM elements
|
||||||
|
var vRules = {};
|
||||||
|
var vMessages = {};
|
||||||
|
|
||||||
|
// returns true only if object is empty
|
||||||
|
function isEmpty(obj) {
|
||||||
|
for (var i in obj) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < xRulesConfig.Fields.length; i++) {
|
||||||
|
var xFieldName = xRulesConfig.Fields[i].FieldName;
|
||||||
|
var xFieldRules = xRulesConfig.Fields[i].FieldRules;
|
||||||
|
|
||||||
|
var vFieldRules = {};
|
||||||
|
var vFieldMessages = {};
|
||||||
|
|
||||||
|
for (var j = 0; j < xFieldRules.length; j++) {
|
||||||
|
var xRule = xFieldRules[j];
|
||||||
|
if (xRule != null) {
|
||||||
|
var ruleName = xRule.RuleName;
|
||||||
|
var ruleParams = xRule.RuleParameters;
|
||||||
|
var errorText = (typeof (xRule.Message) == 'undefined' ? null : xRule.Message);
|
||||||
|
|
||||||
|
switch (ruleName) {
|
||||||
|
case "Required":
|
||||||
|
vFieldRules.required = true;
|
||||||
|
if (errorText)
|
||||||
|
vFieldMessages.required = errorText;
|
||||||
|
break;
|
||||||
|
case "Range":
|
||||||
|
// NOTE: If DataAnnotations could be sending different types for string and DateTime,
|
||||||
|
// implement the checks here if needed
|
||||||
|
if (typeof (ruleParams.Min) == 'undefined') {
|
||||||
|
vFieldRules.max = ruleParams.Max;
|
||||||
|
if (errorText)
|
||||||
|
vFieldMessages.max = errorText;
|
||||||
|
}
|
||||||
|
else if (typeof (ruleParams.Max) == 'undefined') {
|
||||||
|
vFieldRules.min = ruleParams.Min;
|
||||||
|
if (errorText)
|
||||||
|
vFieldMessages.min = errorText;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
vFieldRules.range = [ruleParams.Min, ruleParams.Max];
|
||||||
|
if (errorText)
|
||||||
|
vFieldMessages.range = errorText;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "StringLength":
|
||||||
|
// NOTE: the StringLengthAttribute only takes a max length parameter
|
||||||
|
vFieldRules.maxlength = ruleParams.MaxLength;
|
||||||
|
if (errorText)
|
||||||
|
vFieldMessages.maxlength = errorText;
|
||||||
|
break;
|
||||||
|
case "DataType":
|
||||||
|
switch (ruleParams.Type) {
|
||||||
|
case "EmailAddress":
|
||||||
|
vFieldRules.email = true;
|
||||||
|
if (errorText)
|
||||||
|
vFieldMessages.email = errorText;
|
||||||
|
break;
|
||||||
|
case "Integer":
|
||||||
|
vFieldRules.digits = true;
|
||||||
|
if (errorText)
|
||||||
|
vFieldMessages.digits = errorText;
|
||||||
|
break;
|
||||||
|
case "Decimal":
|
||||||
|
vFieldRules.number = true;
|
||||||
|
if (errorText)
|
||||||
|
vFieldMessages.number = errorText;
|
||||||
|
break;
|
||||||
|
case "Date":
|
||||||
|
vFieldRules.date = true;
|
||||||
|
if (errorText)
|
||||||
|
vFieldMessages.date = errorText;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isEmpty(vFieldRules))
|
||||||
|
vRules[xFieldName] = vFieldRules;
|
||||||
|
if (!isEmpty(vFieldMessages))
|
||||||
|
vMessages[xFieldName] = vFieldMessages;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
removeNumberRuleForCurrency(vRules);
|
||||||
|
return { rules: vRules, messages: vMessages };
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
title: "Introducing…"
|
||||||
|
date: 2009-12-12T09:17:00.9744-06:00
|
||||||
|
slug: introducing
|
||||||
|
published: true
|
||||||
|
---
|
||||||
|
|
||||||
|
Hi, I’m James Kolpack, and I’m a code-aholic.
|
||||||
|
#### Where I’m from
|
||||||
|
|
||||||
|
|
||||||
|
Ever since I was a kid I’ve been programming at various degrees of sophistication. Being a child of the 80’s, I started with Basic (of the [GW variety](http://en.wikipedia.org/wiki/GW-BASIC) on a [8086 based IBM](http://en.wikipedia.org/wiki/IBM_Personal_System/2)) with a dash of [Logo](http://en.wikipedia.org/wiki/Logo_%28programming_language%29) and [batch scripting](http://en.wikipedia.org/wiki/Batch_file). Later in college I became learned in [Pascal](http://en.wikipedia.org/wiki/Pascal) and then [C](http://en.wikipedia.org/wiki/C_%28programming_language%29) and the trials and tribulations of [memory management](http://en.wikipedia.org/wiki/Malloc) and [complex data structures](http://en.wikipedia.org/wiki/Tree_%28data_structure%29). I loved the ability to solve interesting problems by the clever assembly of instructions to be executed on a computer. It was a bit of a power trip, really - “Pow, I just made a program that can find the optimal combination of items to fill a [knapsack in psuedo-polynomial time](http://en.wikipedia.org/wiki/Knapsack_problem#Dynamic_programming_solution).” This capability does not, I discovered, transcend one-to-one to “real-world” power – but you’ve got to [](http://popcyclical.com/content/binary/WindowsLiveWriter/Introducing_90B9/LegoMan_bigger_2.png)do what you can with what you’ve got.
|
||||||
|
|
||||||
|
As for the name of the blog, “popcyclical”, it’s a dual homage to my interest in cyclical relationships and, of course, popsicles. It also references my moniker “poprhythm” which I was given many years ago for my ever-loving devotion to excellent music.
|
||||||
|
#### Where I’m going
|
||||||
|
|
||||||
|
|
||||||
|
On this blog I will be exploring ideas and issues that are relevant to me at the given time. As a developer employed at a small shop, [](http://popcyclical.com/content/binary/WindowsLiveWriter/Introducing_90B9/100px-Directed_graph_with_back_edge.svg_4.png)the focus is guaranteed to wander over time. For any given week, I may be highly invested in any number of topics. This may include : [upcoming features](http://msdn.microsoft.com/en-us/library/dd264736%28VS.100%29.aspx) of programming languages, [graph algorithm design and application](http://www.codeplex.com/quickgraph/), [development tooling](http://www.jetbrains.com/resharper/), [data mining](http://en.wikipedia.org/wiki/Data_mining), [system architecture](http://martinfowler.com/books.html), [user experience](http://mitpress.mit.edu/catalog/item/default.asp?tid=5393&ttype=2), [natural language processing](http://opennlp.sourceforge.net/), [semantic web](http://en.wikipedia.org/wiki/Semantic_Web), [test driven development](http://en.wikipedia.org/wiki/Test-driven_development), [statistics](http://www.r-project.org/), [data management](http://en.wikipedia.org/wiki/Database), … these and many others are all fair game. As of today, the technologies I’m working in from day to day are [C#](http://msdn.microsoft.com/en-us/vcsharp/aa336809.aspx) on the [.NET](http://www.microsoft.com/NET/) framework, [ASP.NET MVC](http://www.asp.net/%28S%28d35rmemuuono1wvm1gsp2n45%29%29/mvc/) and [jQuery](http://jquery.com/), [WCF](http://msdn.microsoft.com/en-us/netframework/aa663324.aspx), and a [variety](http://www.microsoft.com/sqlserver/2008/en/us/default.aspx) of [database](http://www.oracle.com/database/berkeley-db/xml/index.html) [engines](http://www.mysql.com/).
|
||||||
|
#### I can use your help
|
||||||
|
|
||||||
|
|
||||||
|
When pointing the finger of blame for a problem I’m faced with, more often than not, that finger will eventually turn back to point at myself. To err is human, and for software creators, this is repeatedly beaten into our skulls. Every time a compile fails, a stack overflows, a user is confused with our design, an out of memory exception is thrown, and every other time we’ve had a slip of the finger or a misconception about the problem or a technology. To combat this, we must flock together to overcome our individual weakness. I welcome all relevant comments, suggestions, and criticism.
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
---
|
||||||
|
title: "Console Launcher Hot Key - “Tilda”"
|
||||||
|
date: 2009-12-30T04:01:00-06:00
|
||||||
|
slug: console-launcher-hot-key-tilda
|
||||||
|
published: true
|
||||||
|
---
|
||||||
|
|
||||||
|
Interacting with your computer using a mouse-driven GUI makes many tasks quick and easy to accomplish. However, when use of the keyboard is required, a healthy amount of context shifting between these two non-complimentary input devices can occur. And that’s bad news for turning thoughts into actions in a timely matter. So, what’s the consequence of spending a few extra seconds to switch back and forth? As [John D. Cook points out](http://www.johndcook.com/blog/2009/11/09/using-windows-without-a-mouse/), it adds up:
|
||||||
|
>
|
||||||
|
|
||||||
|
**It’s a matter of keeping up with your thoughts**. Suppose some series of tasks takes 20 seconds with a mouse but you can accomplish the same tasks in 12 seconds using the keyboard. The big deal isn’t that you’ve saved 8 seconds; the big deal is that you’re more likely to finish your tasks before you lose the thought that motivated them.
|
||||||
|
|
||||||
|
I interact with the[](http://popcyclical.com/content/binary/images/ConsoleLauncherHotKeyTilda_66F3/tilda_console.png) file system on a very regular basis. I haven’t measured this, but I bet I’ll open Windows Explorer (or [xplorer²](http://www.zabkat.com/)) at least two or three times for every hour I’m at the computer. I’m forever poking around for documents and restructuring directories so that they’ll be easier to find next time. Many times I’ll want to execute a command line statement in the path that I’m browsing. There are [several](http://www.burgaud.com/open-command-window-here/) [different](http://www.microsoft.com/windowsxp/downloads/powertoys/xppowertoys.mspx) [strategies](http://code.kliu.org/cmdopen/) for getting a “Open Command Prompt Here” to the explorer context menu, but they all require a right-click (or [simulating one with the keyboard](http://www.ehow.com/how_8219_click-using-keyboard.html)). There’s an easier way.
|
||||||
|
|
||||||
|
A couple years ago I read an article on [instructables](http://www.instructables.com) about a [“Drop Down”, Quake-style command prompt for Windows](http://www.instructables.com/id/%22Drop-Down%22%2c-Quake-style-command-prompt-for-Window/). The project uses [AutoHotKey](http://www.autohotkey.com/) to launch and hide a console window using a keyboard shortcut. Neat! It works great, except that it always dumps you in the your %HOMEPATH%. I’ve taken the script and upgraded it to navigate directly to the currently open path in Windows Explorer – it’s name is **Tilda**.
|
||||||
|
|
||||||
|
For example – I’ve got an explorer window open in C:\console\UnxUtils\usr\local\wbin and I want to string some of those juicy unix command line utilities together. I can now simply do **Win**+**~**, and I’ve got a new console instance in the right place. As with the original, it will minimize/maximize the console window on subsequent usage of the key combination.
|
||||||
|
|
||||||
|
I have this nasty habit of navigating around the file system when I’m using Windows Explorer. After running some unix commands, I might have a hankering to run MSBuild.exe (ok, probably not, but who knows?). I could tediously type the path, or, I could have Tilda automatically enter it for me. From the new path in Windows Explorer, I type a combination **Win**+**Shift**+**~** and the chdir command gets sent to the current console window.[](http://popcyclical.com/content/binary/images/ConsoleLauncherHotKeyTilda_66F3/tilda_chdir.png)
|
||||||
|
|
||||||
|
Please note that I’m using (and recommend) [Console](http://sourceforge.net/projects/console/) as a command line window host. **Tilda** is currently set to use this, but the script can be easily modified to use cmd.exe or whichever console window host you prefer.
|
||||||
|
|
||||||
|
**Tilda **- open and close a console window using a hot key. Automatically navigates to the currently open explorer window. Save some trips to the mouse and make getting to the command line easier.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[Download Tilda](/content/binary/tilda.zip) ([AutoHotKey](http://www.autohotkey.com/) source and binary)
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
---
|
||||||
|
title: "jQuery Validate and Jeditable, Part 1"
|
||||||
|
date: 2009-12-31T21:32:00-06:00
|
||||||
|
slug: jquery-validate-and-jeditable-part-1
|
||||||
|
published: true
|
||||||
|
---
|
||||||
|
|
||||||
|
I was recently tasked to add server-and-client-side form validation for an ASP.NET MVC site - which already uses in-place editing courtesy [Jeditable](http://www.appelsiini.net/projects/jeditable). I really like the field editing experience that Jeditable provides – it makes form entry in the browser interactive, is fairly straightforward to integrate, and it’s adaptable to many scenarios. It does not, however, have any validation mechanism built in.
|
||||||
|
|
||||||
|
Our project already used [jQuery Validate](http://bassistance.de/jquery-plugins/jquery-plugin-validation/) for a few forms by using the HTML class definitions – like adding class=”required phone” to an INPUT element. This works great, but doesn’t provide any server-side validation tie-in.
|
||||||
|
|
||||||
|
Earlier this year, I remembered having seen a [presentation](http://speakerrate.com/talks/1218-useful-jquery-tips-tricks-and-plugins-with-asp-net-mvc) by [Elijah Manor](http://elijahmanor.com/) who mentioned using [xVal](http://xval.codeplex.com/) for robust server and client side validation. And with the news that [MVC 2 will have a built in validation technique similar to xVal](http://haacked.com/archive/2009/10/01/asp.net-mvc-preview-2-released.aspx), it was an easy decision to start investigating this library.
|
||||||
|
|
||||||
|
[xVal](http://xval.codeplex.com/) is a pretty easy to get integrated. The first step is to decorate the model with validation rules – I’ve decided to use .NET framework’s [DataAnnotations](http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.aspx), which ends up looking like:public class ValidateViewModel
|
||||||
|
{
|
||||||
|
[Required(ErrorMessage = "This string is required")]
|
||||||
|
public string StringRequired { get; set; }
|
||||||
|
|
||||||
|
[Range(13, 100, ErrorMessage = "Must be between 13 and 100")]
|
||||||
|
public double DoubleRange13_100 { get; set; }
|
||||||
|
}public class ValidatedController
|
||||||
|
{
|
||||||
|
public ViewResult Index()
|
||||||
|
{
|
||||||
|
return View(new ValidateViewModel());
|
||||||
|
}
|
||||||
|
}<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<ValidateViewModel>" %>
|
||||||
|
<asp:Content ContentPlaceHolderID="MainContent" runat="server">
|
||||||
|
<% using(Html.BeginForm()) {%>
|
||||||
|
<%= Html.ValidationSummary() %>
|
||||||
|
<label for="StringRequired">String Required:</label>
|
||||||
|
<%= Html.TextBoxFor(m => m.StringRequired) %>
|
||||||
|
<%= Html.ValidationMessageFor(m => m.StringRequired)%>
|
||||||
|
|
||||||
|
<label for="DoubleRange13_100">Doulbe between 13 and 100:</label>
|
||||||
|
<%= Html.TextBoxFor(m => m.DoubleRange13_100) %>
|
||||||
|
<%= Html.ValidationMessageFor(m => m.DoubleRange13_100)%>
|
||||||
|
<%} %>
|
||||||
|
</asp:Content>
|
||||||
|
|
||||||
|
To validate this ViewModel server side, we use a [DataAnnotationsValidationRunner](http://blog.codeville.net/2009/01/10/xval-a-validation-framework-for-aspnet-mvc/) like the one in xVal’s documentation:[AcceptVerbs(HttpVerbs.Post)]
|
||||||
|
public ActionResult Update(ValidateViewModel model)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
DataAnnotationsValidationRunner.ValidateModel(model);
|
||||||
|
// it’s valid, do the actual update
|
||||||
|
var domainObject = ValidateDomainModel.Find(model.id);
|
||||||
|
Map(model, domainObject);
|
||||||
|
domainObject.Update();
|
||||||
|
}
|
||||||
|
catch(RulesException ex) {
|
||||||
|
ex.AddModelStateErrors(ModelState, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ModelState.IsValid ? RedirectToAction("Completed")
|
||||||
|
: (ActionResult) View();
|
||||||
|
}public static class DataAnnotationsValidationRunner
|
||||||
|
{
|
||||||
|
private static IEnumerable<ErrorInfo> GetErrors(object instance)
|
||||||
|
{
|
||||||
|
return from prop in TypeDescriptor.GetProperties(instance).Cast<PropertyDescriptor>()
|
||||||
|
from attribute in prop.Attributes.OfType<ValidationAttribute>()
|
||||||
|
where !attribute.IsValid(prop.GetValue(instance))
|
||||||
|
select new ErrorInfo(prop.Name, attribute.FormatErrorMessage(String.Empty), instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Validates the given <param name="model">model</param></summary>
|
||||||
|
/// <exception cref="RulesException">Thrown if any errors are found</exception>
|
||||||
|
public static void ValidateModel(object model)
|
||||||
|
{
|
||||||
|
var errors = GetErrors(model);
|
||||||
|
if (errors.Any())
|
||||||
|
throw new RulesException(errors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*Aside: the project uses the *[*Active record pattern*](http://en.wikipedia.org/wiki/Active_record_pattern)* via *[*Castle ActiveRecord*](http://www.castleproject.org/activerecord/index.html)* for data access without an intermediate business-logic layer. For this case, the rules are defined on the ViewModel and are validated in the controller. This does add some noise in the actions – I’m definitely interested in other methods for handling this. Such as - perhaps the validation could be placed inside the ViewModel?*
|
||||||
|
|
||||||
|
The next step is to add client-side validation. [xVal](http://xval.codeplex.com/)’s built-in [jQuery Validate](http://bassistance.de/jquery-plugins/jquery-plugin-validation/) rule generator makes this ridiculously simple – just reference jquery.validate.js and xVal.jquery.validate.js in the view, and then this single line:<%= Html.ClientSideValidation<ValidateViewModel>() %>
|
||||||
|
|
||||||
|
The rules defined in the ViewModel will now be validated client side and enforced for server side actions. This works great for a statically defined HTML form, but I learned that integrating with [Jeditable](http://www.appelsiini.net/projects/jeditable)’s dynamic inline forms to be not so straight forward.
|
||||||
|
|
||||||
|
Continued in [Part 2](/2010/01/03/jQueryValidateAndJeditablePart2.aspx)…
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
---
|
||||||
|
title: "jQuery Validate and Jeditable, Part 2"
|
||||||
|
date: 2010-01-02T23:47:00-06:00
|
||||||
|
slug: jquery-validate-and-jeditable-part-2
|
||||||
|
published: true
|
||||||
|
---
|
||||||
|
|
||||||
|
When using [Jeditable](http://www.appelsiini.net/projects/jeditable), there is no form element to bind [jQuery Validate](http://bassistance.de/jquery-plugins/jquery-plugin-validation/) rules with. Instead, when an editable element is clicked or activated, it dynamically creates a new form and input element and destroys them after the user is done editing. For the ViewModel from [Part 1](/2010/01/01/jQueryValidateAndJeditablePart1.aspx), the View might be rendered like so for [Jeditable](http://www.appelsiini.net/projects/jeditable):<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<ValidateViewModel>" %>
|
||||||
|
<asp:Content ContentPlaceHolderID="MainContent" runat="server">
|
||||||
|
<% using(Html.BeginForm()) {%>
|
||||||
|
<%= Html.ValidationSummary() %>
|
||||||
|
<label for="StringRequired">StringRequired:</label>
|
||||||
|
<div class="editable" id="StringRequired" name="StringRequired">
|
||||||
|
<%= Model.StringRequired %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label for="DoubleRange13_100">DoulbeRange13_100:</label>
|
||||||
|
<div class="editable" id="DoubleRange13_100" name="DoubleRange13_100">
|
||||||
|
<%= Model.DoubleRange13_100%>
|
||||||
|
</div>
|
||||||
|
<%} %>
|
||||||
|
</asp:Content>
|
||||||
|
|
||||||
|
[xVal](http://xval.codeplex.com/)’s ClientSideValidation<TViewModel>() used in [Part 1](/2010/01/01/jQueryValidateAndJeditablePart1.aspx) won’t work to validate this. The reason? It generates a script that binds validation directly to the form elements on page load. The rendered script looks for the ViewModel looks like:<script type="text/javascript">
|
||||||
|
xVal.AttachValidator(null,
|
||||||
|
{"Fields":[
|
||||||
|
{
|
||||||
|
"FieldName":"StringRequired",
|
||||||
|
"FieldRules":[
|
||||||
|
{
|
||||||
|
"RuleName":"Required",
|
||||||
|
"RuleParameters":{
|
||||||
|
|
||||||
|
},
|
||||||
|
"Message":"This string is required"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"FieldName":"DoubleRange13_100",
|
||||||
|
"FieldRules":[
|
||||||
|
{
|
||||||
|
"RuleName":"Range",
|
||||||
|
"RuleParameters":{
|
||||||
|
"Min":"13",
|
||||||
|
"Max":"100",
|
||||||
|
"Type":"decimal"
|
||||||
|
},
|
||||||
|
"Message":"Must be between 13 and 100"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]}, {})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
The rules are in the xVal’s StandardJSON format and the AttachValidator function (in xVal.jquery.validate.js) scans the DOM and attaches [jQuery Validate](http://bassistance.de/jquery-plugins/jquery-plugin-validation/) rules as attributes to the matched input elements. Since [Jeditable](http://www.appelsiini.net/projects/jeditable) doesn’t create these elements until they’re actively being edited, the rules have nothing to attach to since they don’t exist yet. Fortunately, [jQuery Validate](http://bassistance.de/jquery-plugins/jquery-plugin-validation/) provides several strategies for defining the rules. In addition to being able to attach attributes to the input elements, the rules can be placed in a separate data structure. [jQuery Validate](http://bassistance.de/jquery-plugins/jquery-plugin-validation/) refers to these as “static rules”. Instead of attaching the xVal rule set directly to the elements, it can be adapted to the static rule set that [jQuery Validate](http://bassistance.de/jquery-plugins/jquery-plugin-validation/) can use directly. The structure for the ViewModel rules will look like: {
|
||||||
|
rules: {
|
||||||
|
StringRequired: {
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
DoubleRange13_100: {
|
||||||
|
number: true,
|
||||||
|
range: ["13”, "100"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
messages: {
|
||||||
|
StringRequired: {
|
||||||
|
required: "This string is required."
|
||||||
|
},
|
||||||
|
DoubleRange13_100: {
|
||||||
|
range: "Must be between 13 and 100"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
I've adapted some javascript to do this conversion - it's available [here](http://popcyclical.com/content/binary/images/jQueryValidateandJeditable_E71E/xValRuleAdapter.js). To get the ViewModel’s rules into this format for javascript consumption, this line is added:<script type="text/javascript">
|
||||||
|
var validateOptions
|
||||||
|
= convertXvalToValidateOptions(
|
||||||
|
<%= Html.ClientSideValidationRules<ValidateViewModel>()%>
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
To get these attached to form elements as soon as the user activates them, [Jeditable](http://www.appelsiini.net/projects/jeditable)’s “plugin” feature is utilized:$(function() { // <- on document ready
|
||||||
|
// register plugin with Jeditable to tie in jQuery Validate
|
||||||
|
$.editable.types['text'].plugin = bindValidate;
|
||||||
|
|
||||||
|
// attach Jeditable to each element with class "editable"
|
||||||
|
// Note: this must be done one-by-one so that the
|
||||||
|
// element's name can be assigned to Jeditable's "name"
|
||||||
|
// option which is used by jQuery Validate
|
||||||
|
$('.editable').each(function() {
|
||||||
|
var element = $(this);
|
||||||
|
|
||||||
|
element.editable(
|
||||||
|
'SaveUrlOrFunctionGoesHere',
|
||||||
|
{
|
||||||
|
// submit when the element is blurred
|
||||||
|
onblur: 'submit',
|
||||||
|
onsubmit: jeditableValidate,
|
||||||
|
// assign the name of the input element
|
||||||
|
// from the element's name - this is needed
|
||||||
|
// because it's what jQuery Validate uses
|
||||||
|
// to bind the rules to the input element
|
||||||
|
name: element.attr('name')
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Jeditable plugin
|
||||||
|
function bindValidate(settings, self) {
|
||||||
|
// attach jQuery Validate to
|
||||||
|
// Jeditable's dynamically created form
|
||||||
|
$('form', self).validate(validateOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
// runs before values are submitted to server
|
||||||
|
function jeditableValidate(settings, self) {
|
||||||
|
// validate the Jeditable dynamically created form
|
||||||
|
return $('form', self).valid();
|
||||||
|
}
|
||||||
|
|
||||||
|
With this glue in place, the form elements will now be validated with the rules defined in the ViewModel. All fields valid:
|
||||||
|
|
||||||
|
[](http://popcyclical.com/content/binary/images/jQueryValidateandJeditable_E71E/jQueryValidateJeditable1.png)
|
||||||
|
|
||||||
|
…and here after both have invalid values:
|
||||||
|
|
||||||
|
[](http://popcyclical.com/content/binary/images/jQueryValidateandJeditable_E71E/jQueryValidateJeditable2.png)
|
||||||
|
|
||||||
|
A few notes:
|
||||||
|
|
||||||
|
- Any additional options to be sent to [jQuery Validate](http://bassistance.de/jquery-plugins/jquery-plugin-validation/) can be attached to the validateOptions object. I’ve used this to place all error messages into a separate errorLabelContainer (like [here](http://stackoverflow.com/questions/61456/mvc-net-jquery-validation)).
|
||||||
|
|
||||||
|
- I feel that AttachValidator function in xVal.jquery.validate.js from could become more loosely coupled by separating the rule conversion from the DOM element attachment.
|
||||||
|
|
||||||
|
I think both of these jQuery libraries provide a great benefit when creating interactive and helpful forms. Kudos to [Jörn Zaefferer](http://bassistance.de/) and [Mika Tuupola](http://www.appelsiini.net/) for the good work. xVal is likewise an excellent library – thanks to [Steve Sanderson](http://www.codeplex.com/site/users/view/SteveSanderson).
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
---
|
||||||
|
title: "ASP.NET MVC ViewModel Value Formatting using AutoMapper"
|
||||||
|
date: 2010-01-04T19:49:33.31-06:00
|
||||||
|
slug: aspnet-mvc-viewmodel-value-formatting-using-automapper
|
||||||
|
published: true
|
||||||
|
---
|
||||||
|
|
||||||
|
From [ASP.NET MVC in Action](http://www.manning.com/palermo/) section 4.4.1:
|
||||||
|
>
|
||||||
|
|
||||||
|
*Views are difficult to unit test, so we want to keep them as thin as possible. … Notice in [the View Model] that all of the properties are strings. We’ll have the [properties] properly formatted before this view model object is placed in view data. This way, the view need not consider the object, and it can format the information properly.*
|
||||||
|
|
||||||
|
To facilitate the formatting between the Domain Model and the View Model, a few of [AutoMapper](http://www.codeplex.com/AutoMapper)’s features may be utilized. Here’s a DomainModel containing a CurrencyProperty which will needed to formatted for human consumption:public class DomainModel
|
||||||
|
{
|
||||||
|
public decimal CurrencyProperty { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
Now, here is a ViewModel which will be used to transport the formatted value to the View:public class ViewModel
|
||||||
|
{
|
||||||
|
///<summary>Currency Property - formatted as $#,###.##</summary>
|
||||||
|
public string CurrencyProperty { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
####
|
||||||
|
|
||||||
|
#### Mapping from Domain Model to View Model
|
||||||
|
|
||||||
|
[AutoMapper](http://www.codeplex.com/AutoMapper) provides an easy way to create a mapping between two object types. For particular tweaks for individual property mappings, the ForMember method can be used like:///<summary>Setup mapping between domain and view model</summary>
|
||||||
|
static ViewModel()
|
||||||
|
{
|
||||||
|
// map dm to vm
|
||||||
|
Mapper.CreateMap<DomainModel, ViewModel>()
|
||||||
|
.ForMember(vm => vm.CurrencyProperty, mc => mc.AddFormatter<CurrencyFormatter>());
|
||||||
|
}
|
||||||
|
|
||||||
|
This sets up a mapping between the DomainModel and ViewModel and additionally applies a custom formatter for CurrencyProperty. The formatter must implement the IValueFormatter interface like so:public class CurrencyFormatter : IValueFormatter
|
||||||
|
{
|
||||||
|
///<summary>Formats source value as currency</summary>
|
||||||
|
public string FormatValue(ResolutionContext context)
|
||||||
|
{
|
||||||
|
return string.Format(CultureInfo.CurrentCulture, "{0:c}", context.SourceValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
…and a simple conversion constructor on the ViewModel:/// <summary> Creates the view model from the domain model.</summary>
|
||||||
|
public ViewModel(DomainModel domainModel)
|
||||||
|
{
|
||||||
|
Mapper.Map(domainModel, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
Now, neither the Controller or View need concern about any formatting and can stay focused on orchestrating and layout:public ViewResult Index()
|
||||||
|
{
|
||||||
|
var model = new DomainModel{CurrencyProperty = 19.95m};
|
||||||
|
|
||||||
|
var viewModel = new ViewModel(model);
|
||||||
|
|
||||||
|
return View(viewModel);
|
||||||
|
}<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<ViewModel>" %>
|
||||||
|
<asp:Content ContentPlaceHolderID="MainContent" runat="server">
|
||||||
|
<% using(Html.BeginForm()) {%>
|
||||||
|
<%= Html.TextBoxFor(m=>m.CurrencyProperty)%>
|
||||||
|
<%} %>
|
||||||
|
</asp:Content>
|
||||||
|
|
||||||
|
*Aside: TextBoxFor is an upcoming ASP.NET MVC 2 feature that’s available today in MVC Futures or the RC. Check out *[*Matt’s post*](http://www.trycatchfail.com/blog/post/2009/12/11/My-best-%28or-worst%29-MVC-hack-to-datehellip3b.aspx)* for some neat stuff.*
|
||||||
|
|
||||||
|
#### Mapping from View Model back to Domain Model
|
||||||
|
|
||||||
|
So now the formatted value is being rendered – but how do we go about the reverse trip back to the server? First, to define an action:[AcceptVerbs(HttpVerbs.Post)]
|
||||||
|
public ActionResult Index(ViewModel viewModel)
|
||||||
|
{
|
||||||
|
var model = new DomainModel();
|
||||||
|
|
||||||
|
viewModel.MapTo(model);
|
||||||
|
|
||||||
|
// ... return a view or action result
|
||||||
|
}
|
||||||
|
|
||||||
|
*Aside: for validating that what the user enters is in the correct format, see *[*another post about jQuery Validate here*](/2010/01/01/jQueryValidateAndJeditablePart1.aspx)*.*
|
||||||
|
|
||||||
|
… and a Map method on the ViewModel:public void MapTo(DomainModel domainModel)
|
||||||
|
{
|
||||||
|
Mapper.Map(this, domainModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
But this mapping will fail without first creating a definition back from the ViewModel to the Model. In the ViewModel’s static constructor: // from vm to dm
|
||||||
|
Mapper.CreateMap<ViewModel, DomainModel>()
|
||||||
|
.ForMember(dm => dm.CurrencyProperty,
|
||||||
|
mc => mc
|
||||||
|
.ResolveUsing<CurrencyResolver>()
|
||||||
|
.FromMember(vm => vm.CurrencyProperty));
|
||||||
|
|
||||||
|
This utilizes another [AutoMapper](http://www.codeplex.com/AutoMapper) method - ResolveUsing - which can be used to get the string property back to a decimal. The ValueResolver<TSource,TDestination> is defined like so:public class CurrencyResolver : ValueResolver<string, decimal>
|
||||||
|
{
|
||||||
|
///<summary>Parses source value as currency</summary>
|
||||||
|
protected override decimal ResolveCore(string source)
|
||||||
|
{
|
||||||
|
return decimal.Parse(source, NumberStyles.Currency, CultureInfo.CurrentCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#### Conclusions
|
||||||
|
|
||||||
|
There may be more elegant ways to accomplish formatting for MVC Views, but this method is quite workable. In particular, I can imagine utilizing [DataAnnotations’s DisplayFormatAttribute](http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.displayformatattribute.aspx) to decorate the Model or ViewModel and the framework automagically applying the formatting while rendering the View.
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
---
|
||||||
|
title: "Resharper 5 Beta Impressions"
|
||||||
|
date: 2010-01-10T20:21:29.7145757-06:00
|
||||||
|
slug: resharper-5-beta-impressions
|
||||||
|
published: true
|
||||||
|
---
|
||||||
|
|
||||||
|
Being a [Resharper](http://www.jetbrains.com/resharper/) user for the past 5 years, I had to jump on opportunity to [try out the publicly released beta for the new 5.0 version](http://www.jetbrains.com/resharper/beta/beta.html?utm_source=jetbrains-dotnet-outlook4&utm_medium=resharper5beta-page&utm_campaign=resharper5beta). I’m currently using Visual Studio 2008, but I’ll be glad to have the updated VS2010 support from Resharper once it’s released. As for the changes in this major revision, I’m excited to try out new code inspections, LINQ integration improvements, and native NUnit integration.
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
- [](http://popcyclical.com/content/binary/images/Resharper5BetaImpressions_7EC3/Resharper5BetaInstallComplete.png)Install was quick and easy. It uninstalled the version 4.5 and questioned me about killing a task that was getting in the way.
|
||||||
|
- Starting Resharper, I’m greeted with a “License to version 4.0 is not acceptable”. This is troubling in two ways:
|
||||||
|
- The license that I bought is for 4.5 C# Edition.
|
||||||
|
- Why does a beta need a license?
|
||||||
|
[](http://popcyclical.com/content/binary/images/Resharper5BetaImpressions_7EC3/Resharper5BetaLicense.png)
|
||||||
|
- For now, using “free evaluation” – this seems to do the trick.
|
||||||
|
- As expected, the [AgentSmith plugin](http://www.agentsmithplugin.com/) is no longer installed (duh), but an updated version is available on their site.
|
||||||
|
### New Features
|
||||||
|
|
||||||
|
|
||||||
|
I’d like to be pretty thorough in acquainting myself with the enhancements, so I’ll touch on each of them from the [list here](http://blogs.jetbrains.com/dotnet/2009/10/resharper-50-overview/).
|
||||||
|
- **Structure Patterns** [](http://popcyclical.com/content/binary/images/Resharper5BetaImpressions_7EC3/image.png)
|
||||||
|
- Custom built code refactorings. This could be a godsend for [brownfield development](http://en.wikipedia.org/wiki/Brownfield_%28software_development%29) – enabling project-wide cleanup for stinky “[code smells](http://en.wikipedia.org/wiki/Code_smell)”. The real power is in the “Placeholder” templating – it’s much like the Live Templates but for refactoring. The image onthe right has a pattern that I made to change from timeSpan to happyHour. Needless to say, this is trivial (and useless!), but I’m readily awaiting the next time I find a [code smell](http://en.wikipedia.org/wiki/Code_smell) I can’t live with.
|
||||||
|
- **Project Refactoring and Dependencies View**
|
||||||
|
- I’ve been waiting for the ability to mass-rename namespaces. Resharper5 : check.
|
||||||
|
- So what does “Project Refactoring” mean? Does a project have a bunch of types declared in the same files? After a few clicks, they can all moved into their own:[](http://popcyclical.com/content/binary/images/Resharper5BetaImpressions_7EC3/image_3.png)
|
||||||
|
[](http://popcyclical.com/content/binary/images/Resharper5BetaImpressions_7EC3/image_4.png)
|
||||||
|
- Dependency View is basically “find usages by project” – which could certainly be useful for larger solutions.
|
||||||
|
- **Call Tracking, Value Tracking**
|
||||||
|
- Examines method, variable, field or property usage through the solution and finds where it’s being generated or called from, as well as the opposite - where it’s being used. It’s the static-analysis version of the call stack.
|
||||||
|
[](http://popcyclical.com/content/binary/images/Resharper5BetaImpressions_7EC3/image_5.png)
|
||||||
|
- **Internationalization**
|
||||||
|
- I’ve never worked on a project using [Internationalization](http://en.wikipedia.org/wiki/Internationalization_and_localization), but it’s bound to happen sooner than later. Resharper 5 adds the ability to move string to resource files as well as refactoring and inspection to support multiple languages.
|
||||||
|
- **ASP.NET**
|
||||||
|
- [](http://popcyclical.com/content/binary/images/Resharper5BetaImpressions_7EC3/image_6.png)Syntax highlighting! Check it out - unused namespaces inside ASP.NET markup will now appear grayed out, just as they do in source code.
|
||||||
|
- Templates for ASP.NET:
|
||||||
|
[](http://popcyclical.com/content/binary/images/Resharper5BetaImpressions_7EC3/image_7.png)
|
||||||
|
- **ASP.NET MVC**
|
||||||
|
- [](http://popcyclical.com/content/binary/images/Resharper5BetaImpressions_7EC3/image_8.png)View name autocomplete from the controller, as well as navigation to and from actions.
|
||||||
|
- … and navigation to Views. **Shift+Click** on a view name to jump there:
|
||||||
|
[](http://popcyclical.com/content/binary/images/Resharper5BetaImpressions_7EC3/image_9.png)
|
||||||
|
- *Aside: Our project has some calls to HtmlHelper.RenderPartial(“<ViewName>”) called inside of a class instantiated with an instance of HtmlHelper (it’s a Helper for HtmlHelper). Resharper can’t resolve these names… but I wouldn’t expect it to.*
|
||||||
|
- **IntelliSense Changes**
|
||||||
|
- In addition to performance improvements, completion can now done using abbreviated names based on [CamelHumps](http://animals.howstuffworks.com/mammals/question104.htm):
|
||||||
|
[](http://popcyclical.com/content/binary/images/Resharper5BetaImpressions_7EC3/image_10.png)
|
||||||
|
- [](http://popcyclical.com/content/binary/images/Resharper5BetaImpressions_7EC3/image_11.png)**Bookmarks**
|
||||||
|
- Set and jump to bookmarks with quick keystrokes. **Ctrl+Shift+[0-9]** to set, and **Ctrl+[0-9]** jump back. **Ctr+~** to see which bookmarks are available:
|
||||||
|
[](http://popcyclical.com/content/binary/images/Resharper5BetaImpressions_7EC3/image_12.png)
|
||||||
|
- **Upgrade to LINQ**
|
||||||
|
- Now this is pretty slick – parses a foreach loop and its innards and [translates it to a single LINQ statement](http://blogs.jetbrains.com/dotnet/2009/12/resharper-50-preview-loops-2-linq/):
|
||||||
|
[](http://popcyclical.com/content/binary/images/Resharper5BetaImpressions_7EC3/image_19.png)
|
||||||
|
- **New and Improved Code Inspections**
|
||||||
|
- So, JetBrain’s says they’ve added a bunch of new code inspections – I’m counting a little over 100 C# Context Actions in 5.0, where as 4.5 had closer to 80. There appear to be some LINQ related ones in there. They’ve also called out that it can now highlight errors in XML comments (something which the [AgentSmith](http://www.agentsmithplugin.com/) plugin already did quite well).
|
||||||
|
- [](http://popcyclical.com/content/binary/images/Resharper5BetaImpressions_7EC3/image_14.png)**Native NUnit Support**
|
||||||
|
- To be honest, I’ve been using the previous Resharper version’s NUnit support without complaint. I’m thinking that the that the improvements are “under the hood” – it works just as well now as it did before.
|
||||||
|
- **XML Formatting**
|
||||||
|
- Inspection and refactoring support - “Reorder attributes” and “Collapse Empty Tag”, for instance.
|
||||||
|
[](http://popcyclical.com/content/binary/images/Resharper5BetaImpressions_7EC3/image_15.png)[](http://popcyclical.com/content/binary/images/Resharper5BetaImpressions_7EC3/image_16.png)
|
||||||
|
- [](http://popcyclical.com/content/binary/images/Resharper5BetaImpressions_7EC3/image_17.png)**External Sources**
|
||||||
|
- This promises to add navigation to referenced libraries that before could only be accessed at the higher namespace-class-method level via Visual Studio’s Object Browser. I poked around a bit on JetBrain’s site looking how to configure the symbol locations, but it doesn’t seem to be documented yet. Perhaps it might need to have the symbols locations populated in VS->Options->Debugging-Symbols, but perhaps not.
|
||||||
|
### Surprises
|
||||||
|
|
||||||
|
- Transparently imported settings from 4.5 – horray! Appears to be able to use the 4.5.resharper shared solution settings
|
||||||
|
- [](http://popcyclical.com/content/binary/images/Resharper5BetaImpressions_7EC3/image_18.png)Running “Find Usages” on a class is taking much longer – in previous version it was instantaneous, now it appears to be scrounging through files instead of an index (could be External sources feature?)
|
||||||
|
- Quick navigate to Type/Filename/Symbol now match partial names – no more needing to put in a “*” to match wildcards.
|
||||||
|
- In the day or two I’ve been using it, I don’t think I’ve encountered any crashes. This is a good thing – the [multitude of errors being automatically reported](http://www.jetbrains.net/jira/browse/RSRP) seem to be going to good use.
|
||||||
|
### Wish List
|
||||||
|
|
||||||
|
- Javascript. Being a dynamic language and all, it’d be pretty difficult to implement the full set of navigation and refactoring helpers Resharper provides for C# and VB. But oh, wouldn’t it be slick if it could.
|
||||||
|
- Community sharing for code-style and and structure patterns. There’s already preference and template sharing with team members via a shared settings file. The next evolution is to extend this to the cloud and create a public library to exchange ideas with all Resharper users.
|
||||||
|
|
||||||
|
For every one of the new features I’ve encountered in the past couple of days, I’m sure there are two or three that I haven’t stumbled over yet. That’s a great thing about this product – utilizing a small subset of it’s features can greatly [streamline development and increase productivity](http://blog.briandicroce.com/2008/05/12/the-case-for-resharper-in-the-enterprise/). Even after years of use, I am still happily surprised to discover new facets of the tool I hadn’t noticed or investigated before.
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
---
|
||||||
|
title: "Automated Software Delivery Dataflow"
|
||||||
|
date: 2010-02-28T21:03:24.838-06:00
|
||||||
|
slug: automated-software-delivery-dataflow
|
||||||
|
published: true
|
||||||
|
---
|
||||||
|
|
||||||
|
As software developers we are forever scheming on ways to increase the quality of our software. It’s a great feeling when customers are enjoying some bit of code that you wrote, so making it even better next time is a worthwhile goal. In contrast, I’ve observed at a number of [SaaS](http://en.wikipedia.org/wiki/Software_as_a_service) development shops have a **lack of quality** when it comes to the **delivery mechanics**. It’s as if once the software is written, the development effort is over and it’s time to tediously prepare for the deployment ceremony. What I want to write about is the deployment process itself – there’s a whole other topic relating to deployment project management which includes tracking changes, scheduling – perhaps another day on that.
|
||||||
|
|
||||||
|
The fact is that many software shops will deploy their services manually. This may (hopefully) include **multiple environments** for development, QA, staging, and finally production. Deployments will have checklists looking something like:
|
||||||
|
- Log into target machine.
|
||||||
|
- Shut down service.
|
||||||
|
- Copy updated binaries.
|
||||||
|
- Make any configuration changes.
|
||||||
|
- Restart service.
|
||||||
|
- Repeat for every target.
|
||||||
|
|
||||||
|
If the application has data/schema updates, it could be added as another step in the deployment process. Likewise, in a multi-target environment there may be preliminary steps for switching out the targets from an application pool. Finally, in the case of a software emergency, a working roll-back strategy is essential. A few points:
|
||||||
|
- **Deployment is time consuming**
|
||||||
|
- While each step may only take a few minutes, together it can add up to a significant chunk of a work day. Multiply that by the target count for the environment - lather, rinse, repeat.
|
||||||
|
- **Steps are error prone**
|
||||||
|
- A missed or botched step may not be noticed until it’s time to turn on the service – or worse, afterwards. There’s a lot of click-click-typedy-typedy-clicking with not a lot of feedback.
|
||||||
|
- **None of the steps actually require human interaction**
|
||||||
|
- I have never seen a deployment plan where all of the steps and details were not known before hand. If the plan is not 100% deterministic, it’s probably more of a deployment *idea*** **and should be re-thunk’d.
|
||||||
|
|
||||||
|
Automating the deployment seems like a no-brainer. [Ayende Rahien makes his views clear](http://ayende.com/Blog/archive/2008/09/08/Requirements-101-Have-an-automated-deployment.aspx) -
|
||||||
|
>
|
||||||
|
|
||||||
|
*If you don't have an automated deployment, it generally means that you are in a bad position. By automated, I mean that you should be able to push a new version out by double clicking something. If you can't get automated deployment script in under an hour, you most certainly have a problem.*
|
||||||
|
|
||||||
|
But how to get from source code to having it automatically deployed? It takes a bit of setup, but it’s well worth the effort for a project in active development. Here’s one potential dataflow:
|
||||||
|
|
||||||
|
[](http://popcyclical.com/content/binary/images/AutomatedServiceDeploymentWorkflow_1325C/ServiceDevelopmentWorkflow.png)
|
||||||
|
- **Code gets written for the project and placed in source control**
|
||||||
|
- All code needs to be in source control – no exceptions!
|
||||||
|
- **Continuous integration triggers a build**
|
||||||
|
- **Builds artifacts are created by the build server**
|
||||||
|
- For reliable builds, it’s important to have a [dedicated build machine](http://blog.ianuy.com/2009/06/07/the-importance-of-a-dedicated-build-machine/) - [magical or not](http://www.codinghorror.com/blog/2004/12/the-magical-build-machine.html)
|
||||||
|
- **Project configurations for each potential target**
|
||||||
|
- This includes setting environmental variables and injecting any content needed to make the code run correctly once it is deployed
|
||||||
|
- **Automated Deployment**
|
||||||
|
- This is essentially a scripted equivalent of the manual deployment process
|
||||||
|
|
||||||
|
There are many open source and commercially available technologies catering to each of these functions – I’ll dig into a few of them in some future posts. In broad terms, it’s a worthwhile endeavor to have an **end-to-end software delivery process**. It’s a guarantee that your time and energy are kept focused on doing what’s important – developing software!
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
title: "Simplified Performance Counters in .NET"
|
||||||
|
date: 2010-03-20T21:00:54.891-05:00
|
||||||
|
slug: simplified-performance-counters-in-net
|
||||||
|
published: true
|
||||||
|
---
|
||||||
|
|
||||||
|
I just posted an article on [CodeProject](http://www.codeproject.com) about a generalized code block which can be used in a common scenario for simple performance benchmarking. Check out ["Simplified Performance Counters for Timed Operations" here](http://www.codeproject.com/KB/dotnet/simpleperfcounters.aspx)…
|
||||||
|
|
||||||
|
[](http://popcyclical.com/content/binary/images/SimplifiedPerformanceCountersin.NET_E293/OperationPerformanceCounter_AddCounters.png) [](http://popcyclical.com/content/binary/images/SimplifiedPerformanceCountersin.NET_E293/OperationPerformanceCounter_OperationsPerSecond.png) [](http://popcyclical.com/content/binary/images/SimplifiedPerformanceCountersin.NET_E293/OperationPerformanceCounter_AverageDuration.png)
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
---
|
||||||
|
title: "Programming Language Misuse"
|
||||||
|
date: 2010-03-23T20:06:11.401-05:00
|
||||||
|
slug: programming-language-misuse
|
||||||
|
published: true
|
||||||
|
---
|
||||||
|
|
||||||
|
I’m feeling a bit guilty about some code I wrote:using (new OperationTimer("MyOperation", this))
|
||||||
|
{
|
||||||
|
// ... complete operation
|
||||||
|
}
|
||||||
|
|
||||||
|
This innocent looking C# snippet is hiding a tricky secret - the using statement is being misused (no pun intended). The [documentation defines the intended usage clearly](http://msdn.microsoft.com/en-us/library/yh598w02%28VS.80%29.aspx):
|
||||||
|
|
||||||
|
using Statement
|
||||||
|
Defines a scope, outside of which an object or objects will be disposed.
|
||||||
|
|
||||||
|
The problem? The notion of “object disposal” is being hijacked! In your garden variety [IDisposable](http://msdn.microsoft.com/en-us/library/system.idisposable.aspx) implementation, you’d be dealing with an [external resource that needs to be released](http://msdn.microsoft.com/en-us/library/fs2xkftw.aspx) before the object can be removed from memory. Instead, I’m using it to time a block of code like so:class OperationTimer : IDisposable
|
||||||
|
{
|
||||||
|
private readonly string _operationName;
|
||||||
|
private readonly ITimable _obj;
|
||||||
|
private readonly Stopwatch _stopwatch;
|
||||||
|
|
||||||
|
public OperationTimer(string operationName, ITimable obj)
|
||||||
|
{
|
||||||
|
_operationName = operationName;
|
||||||
|
_obj = obj;
|
||||||
|
_stopwatch = new Stopwatch();
|
||||||
|
_stopwatch.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_stopwatch.Stop();
|
||||||
|
_obj.OnOperationCompleted(_operationName, _stopwatch.Elapsed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
The constructor starts a timer and the Dispose() method stops it and reports the elapsed time. (*aside: if you’re interested in how I’m using the timer, check out my previous article *[*Simplified Performance Counters*](http://popcyclical.com/2010/03/21/SimplifiedPerformanceCountersInNET.aspx)) There are certainly other ways to accomplish this same behavior, but they lack the elegance of a neatly scoped code block. It’s arguably an acceptable way to repurpose the language. In fact, the ASP.NET MVC authors saw fit to use it in a similar fashion with the [BeginForm helper](http://msdn.microsoft.com/en-us/library/dd410596.aspx). The only “resource” it disposes of is to render a closing </form> tag.
|
||||||
|
|
||||||
|
My question is: **When does repurposing language constructs turn from “acceptable language use” to a “dirty trick”, or worse, “illegible line noise”**?
|
||||||
|
|
||||||
|
It seems like a slippery slope. One instance that I don’t care for is controlling execution flow by-way-of logical operator precedence in most C-like languages:expression1 && expression2 || expression3
|
||||||
|
|
||||||
|
Which is equivalent to:if (expression1)
|
||||||
|
expression2
|
||||||
|
else
|
||||||
|
expression3
|
||||||
|
|
||||||
|
This takes advantage of the order of evaluation in a logical statement – it is assumed (correctly) that expression2 will never be evaluated if expression1 is evaluated as false, and instead, expression3 will get to run. Likewise, if the first two evaluate to true, the truth value is known for the statement and expression3 is never evaluated. This is clearly not the intended usage which the language designers had in mind, but it works, and it saves any keywords from being written.
|
||||||
|
|
||||||
|
Some truly beautiful code has been written by way of hijacking the language. For instance, [here’s a program that will calculate the value of pi using an ascii circle](http://www.cise.ufl.edu/~manuel/obfuscate/pi.c). Truly neat - but also completely useless from a software development standpoint.
|
||||||
|
|
||||||
|
What do you think? Should I just get over my guilt about repurposing IDisposable? Or, should I be true to the original intent of the language and find another way?
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
title: "Trust Issues with Source Code"
|
||||||
|
date: 2010-05-22T21:51:48.2472794-05:00
|
||||||
|
slug: trust-issues-with-source-code
|
||||||
|
published: true
|
||||||
|
---
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Digging around in some code circa 6 months ago I discovered a method that I had scrounged from the web and, in my apparent haste at the time, had not build any unit tests. It was less than 20 lines of code doing some simple array manipulation – and it was from a pretty decent site, so it seemed pretty safe. It’s the weekend so I thought, hey, time to plug that gap! I started with some simple cases and soon realized that one of the execution paths was just … well, *plain wrong.*
|
||||||
|
|
||||||
|
Luckily, that behavior wasn’t being used anywhere in my project (yet!), but still, it was essentially a land mine waiting for someone to trip it. My first reaction was “shame on them for posting that without testing it!” Of course, this code didn’t end up in my project because of the author. It was I who blindly accepted and given it the “it’s from the internet!”-stamp-of-approval.
|
||||||
|
|
||||||
|
Lessons learned today:[](http://popcyclical.com/content/binary/images/fd79eac90e05_13202/itsfromtheinternet.png)
|
||||||
|
- Trust is earned, not given.
|
||||||
|
- Source code becomes trusted by-way-of thorough unit and functional testing.
|
||||||
|
- Do not trust untested code from the internet.
|
||||||
|
- Do not trust untested code from your own keyboard even more so – at least on the internet it’s likely that someone else has reviewed it.
|
||||||
|
|
||||||
|
I’ve written the author a friendly note with a simple fix – it’s better to diffuse that bomb than let it get somebody else!
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
title: "CodeStock 2010"
|
||||||
|
date: 2010-06-27T19:21:01.92-05:00
|
||||||
|
slug: codestock-2010
|
||||||
|
published: true
|
||||||
|
---
|
||||||
|
|
||||||
|
This last week I gave two presentations at [CodeStock 2010](http://codestock.org/). Thanks to everyone attended my sessions, hope you got something out of them, and thanks to the [CodeStock organizers](http://www.vinull.com/) for all your hard work. Here’s some content from the presentations:
|
||||||
|
|
||||||
|
[Parallel Computing in .NET 4 and VS 2010 - Presentation](http://popcyclical.com/content/binary/images/CodeStock2010_A334/ParallelComputing_VS2010.pptx)
|
||||||
|
[Parallel Computing in .NET 4 and VS 2010 - Code](http://popcyclical.com/content/binary/images/CodeStock2010_A334/ParallelComputing_VS2010_code.zip)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[Introduction to Windows PowerShell - Presentation](http://popcyclical.com/content/binary/images/CodeStock2010_A334/Intro_To_PowerShell.pptx)
|
||||||
|
[Introduction to Windows PowerShell - Code](http://popcyclical.com/content/binary/images/CodeStock2010_A334/Intro_To_PowerShell_code.zip)
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
---
|
||||||
|
title: "Poem for ReSharper"
|
||||||
|
date: 2010-08-27T16:19:31.242-05:00
|
||||||
|
slug: poem-for-resharper
|
||||||
|
published: true
|
||||||
|
---
|
||||||
|
|
||||||
|
*With tongue implementation injected dynamically into cheek object*[](http://www.jetbrains.com/resharper/)…
|
||||||
|
|
||||||
|
ReSharper is my shepherd,
|
||||||
|
I shall not want;
|
||||||
|
He lets me apply quick refactors.
|
||||||
|
He leads me to errors before compile;
|
||||||
|
He cleans up my code.
|
||||||
|
He navigates me in paths of inheritance
|
||||||
|
for each namespace.
|
||||||
|
|
||||||
|
Even though I strive to remove cruft
|
||||||
|
of the brownfield app,
|
||||||
|
I fear no error;
|
||||||
|
for You are in my IDE;
|
||||||
|
Your red and Your green unit tests, they comfort me.
|
||||||
|
|
||||||
|
Surely correctness and maintainability shall follow me
|
||||||
|
all the deployments of my app;
|
||||||
|
and I shall dwell favorably in the thoughts of my
|
||||||
|
Users forever.
|
||||||
|
|
||||||
|
*Sum(23)*
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
title: "Splitting Pascal/Camel Case with RegEx Enhancements"
|
||||||
|
date: 2010-09-11T21:03:15.087-05:00
|
||||||
|
slug: splitting-pascalcamel-case-with-regex-enhancements
|
||||||
|
published: true
|
||||||
|
---
|
||||||
|
|
||||||
|
In [Jon Galloway’s](http://weblogs.asp.net/jgalloway/) [Splitting Camel Case with RegEx](http://weblogs.asp.net/jgalloway/archive/2005/09/27/426087.aspx) blog post, he introduced a simple regular expression replacement which can split “ThisIsInPascalCase” into “This Is In Pascal Case”. Here’s the original code:output = System.Text.RegularExpressions.Regex.Replace(
|
||||||
|
input,
|
||||||
|
"([A-Z])",
|
||||||
|
" $1",
|
||||||
|
System.Text.RegularExpressions.RegexOptions.Compiled).Trim();
|
||||||
|
|
||||||
|
Simple and effective. Matches any capital letters and inserts a space before them. But there’s room for improvement. First, the call to String.Trim() to remove any spaces potentially added if the first letter is uppercase – this can be handled with a [“Match if prefix is absent” group](http://msdn.microsoft.com/en-us/library/az24scfc.aspx#grouping_constructs) containing the “beginning of line” character ^. This prevents any matches from occurring on the first character, which eliminates the need for the String.Trim() call. The formal name for this grouping construct is “Zero-width negative lookbehind assertion”, but just think of it as “if you see what’s in here, don’t match the next thing”. (?<!^)([A-Z])
|
||||||
|
|
||||||
|
Next - there’s a potential issue with how acronyms get handled with this. Given this fictional book title: “WCFForNoobs” – the split will occur on each uppercase letter resulting in “W C F For Noobs”. The fix is simple, though – require that uppercase letters be followed by a lowercase: (?<!^)([A-Z][a-z])
|
||||||
|
|
||||||
|
…Now it’ll result in “WCF For Noobs” (aren’t we all!). But now it won’t add a space before the acronym – for “LearnWCFInSixEasyMonths”, the result will be “LearnWCF In Six Easy Months”. No problem – add an alternate match for a lowercase letter coming before the uppercase letter. The replace pattern makes this more difficult – we don’t want the space to go before the lowercase letter, we want it between the lowercase and the first capital letter of the acronym. RegEx can handle this with another lookbehind match group – “Match prefix but exclude it” - (?<=). This allows the match to occur on the lowercase-uppercase pair, but only the uppercase portion will get matched, so when it comes time to run the replacement, the space will get inserted between the two letters. By itself, that’ll look like this: ((?<=[a-z])[A-Z])
|
||||||
|
|
||||||
|
Great! But this needs to be combined with previous expression. Easy accomplished with an either/or match using the vertical bar “or” construct: (?<!^)([A-Z][a-z]|(?<=[a-z])[A-Z])
|
||||||
|
|
||||||
|
The example “LearnWCFInSixEasyMonths” will now be split into “Learn WCF In Six Easy Months”. These same techniques can be used for additional splits – perhaps on numbers or underscores. More generally, [lookbehind and lookahead are great tools](http://www.regular-expressions.info/lookaround.html) to have in your RegEx toolbelt.
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
---
|
||||||
|
title: "How Do You Solve a Problem Like a Euler? Using F#"
|
||||||
|
date: 2012-04-25T19:44:10.7264-05:00
|
||||||
|
slug: how-do-you-solve-a-problem-like-a-euler-using-f
|
||||||
|
published: true
|
||||||
|
---
|
||||||
|
|
||||||
|
I’ve been slowly making my way through the first 100 [Project Euler](http://projecteuler.net/) problems while learning F# in an attempt to be [more pragmatic](http://www.amazon.com/gp/product/020161622X?ie=UTF8&tag=popcyclical-20&linkCode=shr&camp=213733&creative=393185&creativeASIN=020161622X) - and to try and prevent my grey matter from getting too stale. After many hours of keyboard-head-banging, I’m now getting to the point where I don’t flail uselessly when beginning to type in some F# code – the [pattern matching](http://msdn.microsoft.com/en-us/library/dd547125.aspx), [automatic generalization](http://msdn.microsoft.com/en-us/library/dd233183.aspx) and [higher-order functions](http://en.wikibooks.org/wiki/F_Sharp_Programming/Higher_Order_Functions) now feel like useful tools rather than strange curiosities. Since I’ve primarily coded in C# for many years, I’ve been using the book “[Real World Functional Programming](http://www.amazon.com/gp/product/1933988924?ie=UTF8&tag=popcyclical-20&linkCode=shr&camp=213733&creative=393185&creativeASIN=1933988924)” by [Tomas Petricek](http://tomasp.net/) as a starting point – it’s been a pretty good read. So, for this post the code will be in F#, but it’ll be secondary to the more general topic of problem solving for puzzle-type scenarios commonly found on Project Euler.
|
||||||
|
|
||||||
|
I found this [particular problem, #98](http://projecteuler.net/problem=98), a bit more challenging than usual - and kind of fun to work through. It involves matching anagrams with square numbers using character replacement – here’s the full description:
|
||||||
|
>
|
||||||
|
|
||||||
|
By replacing each of the letters in the word CARE with 1, 2, 9, and 6 respectively, we form a square number: 1296 = 362. What is remarkable is that, by using the same digital substitutions, the anagram, RACE, also forms a square number: 9216 = 962. We shall call CARE (and RACE) a square anagram word pair and specify further that leading zeroes are not permitted, neither may a different letter have the same digital value as another letter.
|
||||||
|
|
||||||
|
Using words.txt, a 16K text file containing nearly two-thousand common English words, find all the square anagram word pairs (a palindromic word is NOT considered to be an anagram of itself).
|
||||||
|
|
||||||
|
What is the largest square number formed by any member of such a pair?
|
||||||
|
|
||||||
|
This is what I’d consider a pretty good puzzle – the elements (a word list and square numbers) are easy enough to grasp, the way they’re related together is unique for this scenario, and an effective solution does not immediately pop into mind. Well, it didn’t pop into *my *mind, at least. So – where to begin?
|
||||||
|
|
||||||
|
Many of the problems (at least the ones I’ve solved so far) on Project Euler involve a few common steps to reach a solution:
|
||||||
|
- **Generate a set of input values for the problem** – usually this will be a large set of potential values, such as all the prime numbers below 10,000,000, or in this case, 16K of words and a bunch of square numbers.
|
||||||
|
- Given these inputs, **build an algorithm that can test for the solution**.
|
||||||
|
- **Add constraints to reduce the size of the input set** so that the solution can quickly be found. On modern hardware, this is usually under 1 second – but anything under a minute is [considered kosher](http://projecteuler.net/about).
|
||||||
|
### Input Values
|
||||||
|
|
||||||
|
|
||||||
|
The input values are easy here – they’ve provided the word list, and a sequence of square numbers should be trivial. First, reading the word list. Instead of placing the words each on a new line, the file instead contains a single line formatted like:
|
||||||
|
> "A","ABILITY","ABLE","ABOUT","ABOVE","ABSENCE"...
|
||||||
|
|
||||||
|
Ok – so it’ll take a little parsing. No problem.let words =
|
||||||
|
System.IO.File.ReadAllLines(@"words.txt")
|
||||||
|
|> Array.collect (fun line -> line.Split(','))
|
||||||
|
|> Array.map (fun w -> w.Replace("\"", "").ToLower())
|
||||||
|
|
||||||
|
This loads the line from the file, splits the words by commas, and removes the quotations… and I threw in making the words lowercase – since I hate having my puzzles constantly shouting at me. A little aside for the language particulars: the |> is the magical-looking-but-actually-quite-simple [pipeline operator](http://msdn.microsoft.com/en-us/magazine/cc164244.aspx#S6) – it passes the output from the function on the left to be input for the function on the right. And the fun –> is [F# syntax for lambda expressions](http://msdn.microsoft.com/en-us/library/dd233201.aspx). At the end of the pipeline comes a simple array containing the parsed words.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
So for the rest of the input - the square numbers. F# has the means to generate an infinite sequence which can then be sliced and diced to get at the bits that are needed. The perfect squares can be generated with:let allSquares =
|
||||||
|
Seq.unfold(fun (square,odd) -> Some(square, (square+odd, odd+2))) (0,1)
|
||||||
|
|
||||||
|
The square numbers are “[unfolded](http://msdn.microsoft.com/en-us/library/ee340363.aspx)” – that is, each element is calculated based on the result from the previous element, like you’d do with. Square numbers have a [property where they can be generated by as the sum of a list of successive odd numbers](http://en.wikipedia.org/wiki/Square_number#Properties). The [tuple](http://msdn.microsoft.com/en-us/library/dd233200.aspx) of (0,1) on the far right is fed in as an initial value, with 0 being the first “square” number and 1 being the first odd number. At each step of the unfold, the odd number is increased by 2, and the square number is calculated by the adding the previous odd number. So, the (square,odd) tuples being computed will look like:(0,1),(1,3),(4,5),(9,7),(16,9), with only the squares being captured as output. Works for me!
|
||||||
|
|
||||||
|
As with most simple-yet-effective bits of code, this one went through a couple of refinements before nailing it. The first pass looked like this:Seq.unfold(fun i -> Some(i, i + 1)) 0
|
||||||
|
|> Seq.map (fun i -> i*i)
|
||||||
|
|
||||||
|
…where the squares are generated in two steps instead of one. The first bit unfolds all the natural numbers from 0 to infinity (or more likely, maxint), and the second part maps these each by squaring them. It produces the same result, but is less elegant because it uses more moving pieces.
|
||||||
|
|
||||||
|
That’s all the input this problem should require – next, to design the…
|
||||||
|
|
||||||
|
### Success Algorithm
|
||||||
|
|
||||||
|
A useful bit of the Project Euler problem descriptions is that they usually include an example which can be used as a test case for a solution algorithm. Here they’ve provided:care, race // <- anagrams map to
|
||||||
|
1296, 9216 // <- squares
|
||||||
|
|
||||||
|
…with a replacement dictionary of (c,1),(a,2),(r,9),(e,6). It’s important to note that the anagrams and squares might be symmetric, where the numbers can be swapped (i.e. (c,9),(a,2),(r,1),(e,6) works as well), but there are cases (such as the final answer) where this doesn’t hold.
|
||||||
|
|
||||||
|
To devise an algorithm to test for an answer, the first step is to think it over before doing any typing. It’s better to have some notion of a strategy than to rush in with the codes. I struggle with adhering to this – it’s just too easy to think “I’m so good, I’ll just figure it out as I program.” I’ve found it’s more productive to get away - get a pen and some paper and start sketching ideas, take a walk, etc. There’ll be plenty of time to work through all the details in code, but it’s best to go in with a plan – a vague or even an incorrect strategy is better than none at all.
|
||||||
|
|
||||||
|
For this problem, I started with the idea of comparing each letter of the first word with the first square number, building a replacement dictionary while iterating through them. If a valid replacement dictionary for the first set is found, it would then be tested against the second word-square-number pair. Writing in algorithm in F# meant I could treat the words as lists and iterate them using a common combination of recursive function calls and pattern matching.let rec getReplacementDictionary (sToT, tToS) (source, target) =
|
||||||
|
match source, target with
|
||||||
|
// have already seen this exact mapping -> skip it
|
||||||
|
| s::ss, t::tt when Map.containsKey s sToT && (Map.find s sToT) = t
|
||||||
|
-> getReplacementDictionary (sToT, tToS) (ss, tt)
|
||||||
|
// have a mapping for the source, but it's not the target -> failure
|
||||||
|
| s::_, _ when Map.containsKey s sToT
|
||||||
|
-> None
|
||||||
|
// have a mapping for the target, but it's not the source -> failure
|
||||||
|
| _::_, t::_ when Map.containsKey t tToS
|
||||||
|
-> None
|
||||||
|
// never before seen mapping -> add it
|
||||||
|
| s::ss, t::tt
|
||||||
|
-> getReplacementDictionary (Map.add s t sToT, Map.add t s tToS) (ss, tt)
|
||||||
|
// end of the line - a successful translation!
|
||||||
|
| [], [] -> Some(sToT, tToS)
|
||||||
|
| _ -> raise(System.ArgumentException("words not equal length"))
|
||||||
|
|
||||||
|
I’ve named the variables “source” and “target” – they’ll get passed the word and the number, respectively. They are “matched” against conditions using the patterns seen on each line following the pipe “|” character. The easiest of these to grasp is near the end – “| [], [] –> Some(sToT, tToS)“ which matches two empty lists, indicating that the words have been completely checked and that there is a valid dictionary. For the dictionary itself, I found that it was necessary to keep tabs on both the source-to-target values (sToT) as well as the target-to-source (tToS) values. A bi-directional mapping structure would be ideal, but it would have been more effort to construct that than to fudge it with an extra variable. If there is a better way to handle this, I’d be interested to hear it…
|
||||||
|
|
||||||
|
At any rate, the result of this function will either be a failure - with the None value - or with the completed dictionary(s) – the Some(sToT, tToS). Testing it against “care” and “1296” produces the expect result:> getReplacementDictionary (Map.empty, Map.empty) ("care" |> List.ofSeq, "1296" |> List.ofSeq);;
|
||||||
|
val it : (Map<char,char> * Map<char,char>) option =
|
||||||
|
Some
|
||||||
|
(map [('a', '2'); ('c', '1'); ('e', '6'); ('r', '9')],
|
||||||
|
map [('1', 'c'); ('2', 'a'); ('6', 'e'); ('9', 'r')])
|
||||||
|
|
||||||
|
…and changing a digit in the number to one of the already mapped digits results (I’m using 1292 which repeats the 2) in a failure:> getReplacementDictionary (Map.empty, Map.empty) ("care" |> List.ofSeq, "1292" |> List.ofSeq);;
|
||||||
|
val it : (Map<char,char> * Map<char,char>) option = None
|
||||||
|
|
||||||
|
To test this dictionary against the second group, “race” and “9216”, the original function may be reused because all of the character mappings will have been seen in the first group and they will simply be verified via the first pattern. let dict = getReplacementDictionary (Map.empty, Map.empty) ("care" |> List.ofSeq, "1296" |> List.ofSeq)
|
||||||
|
getReplacementDictionary dict.Value ("race" |> List.ofSeq, "9216" |> List.ofSeq);;
|
||||||
|
|
||||||
|
val dict : (Map<char,char> * Map<char,char>) option =
|
||||||
|
Some
|
||||||
|
(map [('a', '2'); ('c', '1'); ('e', '6'); ('r', '9')],
|
||||||
|
map [('1', 'c'); ('2', 'a'); ('6', 'e'); ('9', 'r')])
|
||||||
|
|
||||||
|
This is making an assumption that the input words are anagrams of each other – this is an OK assumption to make for now. Indeed, it can be even assumed that the input data may be filtered for anagrams – I’ll cover that in the next section.
|
||||||
|
|
||||||
|
I noticed that this could be further streamlined by simply appending the words and the numbers together, since that is more-or-less what is occurring when calling it twice. And in that case, the function no longer needs to return the mapping dictionary – just a simple boolean indicating if a valid dictionary can be applied to the input. So, the function can be modified as follows, making sure to rename it:let rec hasReplacementDictionary (sToT, tToS) (source, target) =
|
||||||
|
match source, target with
|
||||||
|
| s::ss, t::tt when Map.containsKey s sToT && (Map.find s sToT) = t
|
||||||
|
-> hasReplacementDictionary (sToT, tToS) (ss, tt)
|
||||||
|
| s::_, _ when Map.containsKey s sToT
|
||||||
|
-> false
|
||||||
|
| _::_, t::_ when Map.containsKey t tToS
|
||||||
|
-> false
|
||||||
|
| s::ss, t::tt
|
||||||
|
-> hasReplacementDictionary (Map.add s t sToT, Map.add t s tToS) (ss, tt)
|
||||||
|
| [], [] -> true
|
||||||
|
| _ -> raise(System.ArgumentException("words not equal length"))
|
||||||
|
|
||||||
|
and run a test:> let los = List.ofSeq
|
||||||
|
let s = List.append (List.ofSeq "care") (List.ofSeq "race")
|
||||||
|
let t = List.append (List.ofSeq "1296") (List.ofSeq "9216");;
|
||||||
|
|
||||||
|
> hasReplacementDictionary (Map.empty, Map.empty) (s, t);;
|
||||||
|
val it : bool = true
|
||||||
|
|
||||||
|
Success! Now, having all the necessary input and a valid algorithm, it would be possible to test every possibly combination of inputs to find the correct answer.
|
||||||
|
|
||||||
|
### Input Constraints
|
||||||
|
|
||||||
|
Project Euler problems are usually infeasible to answer without a computer. (There’s sometimes really smart math folks in the forums who will find clever ways around this, but for the rest of us…)
|
||||||
|
|
||||||
|
How many combinations of the input data could there be, anyway? There’s a total of 1,786 words ranging in length from 1 to 14 characters, which leads to ~3.2 million combinations of just the words. My script to calculate the count of all square numbers under 14 digits ran out of memory, but it turns out only up to 9 digit squares are needed. There are 31,623 of those. The number of combinations would be 1,7862 × 31,6232 ≈ 3.2E15. That’s several thousand billion – got to keep filtering or it’ll take a year!
|
||||||
|
|
||||||
|
The next obvious step is to pair up the anagrams and discard the rest of the words. A simple way to do this is to alphabetically sort each character in each word and group together the ones that are exactly the same. For example “care” and “race” will both map to “acer” – neat trick! Here’s the codelet groupAnagrams ws =
|
||||||
|
ws
|
||||||
|
// sort by characters in each string and then glue them back together
|
||||||
|
|> Seq.groupBy (Array.ofSeq >> Array.sort >> (fun cs -> new string(cs)))
|
||||||
|
// take only the anagrams - where the number of words is greater than one
|
||||||
|
|> Seq.map snd
|
||||||
|
|> Seq.filter (Seq.length >> ((<) 1))
|
||||||
|
|
||||||
|
This is a great first step – but it’s not quite enough. I want all the pairs of anagrams and there are cases where there are more than 2 words that all have the same letters. For example, “post”, “spot” and “stop” will all group together. What I need is combinations of all the pairs – so I’ll end up with (“post”, “spot”), (“spot”, stop”), and (“post”, “stop”). Fortunately, I had already borrowed/stolen a generic combination function that [Tomas had posted on Stack Overflow](http://stackoverflow.com/a/4495708/99492).let combinations size set =
|
||||||
|
let rec combinations acc size set = seq {
|
||||||
|
match size, set with
|
||||||
|
| n, x::xs ->
|
||||||
|
if n > 0 then yield! combinations (x::acc) (n - 1) xs
|
||||||
|
if n >= 0 then yield! combinations acc n xs
|
||||||
|
| 0, [] -> yield acc
|
||||||
|
| _, [] -> () }
|
||||||
|
combinations [] size (set |> List.ofSeq)
|
||||||
|
|
||||||
|
Now I can run all the whole list of anagram groups through the combinations algorithm and produce a single list of anagram pairs:let pairwiseTuples =
|
||||||
|
Seq.collect (
|
||||||
|
combinations 2
|
||||||
|
>> Seq.map (fun l -> l.[0], l.[1])
|
||||||
|
)
|
||||||
|
|
||||||
|
And indeed – it does its job:[|("cat", "act"); ... ("race", "care");
|
||||||
|
...
|
||||||
|
("spot", "post"); ("stop", "post"); ("stop", "spot");
|
||||||
|
...|]
|
||||||
|
|
||||||
|
Running the words through this results in 44 pairs of anagrams with the lengthiest pair being 9 letters long ("reduction", "introduce"). This is starting to sound reasonable to process – and it’s where I got stuck for a bit. Looping through all of the squares for each pair still seems computationally prohibitive.
|
||||||
|
|
||||||
|
I walked away from it for the day and in the shower the next morning – “duh, the square numbers can be bundled up as anagrams as well!" These are the ah-ha moments that make it all worthwhile. A few examples – (“1024”, “2401”), (“14400”, “10404”). There turned out to still be quite a few for the larger digit counts (for example, there are 70052 anagram pairs for square numbers with 9 digits), but since there are so few words in the list that are above 5 characters long, it turned out to be OK.
|
||||||
|
|
||||||
|
All was left was to write a few more lines to run all the equal-length anagram pairs through the success function and find the result. It ran in about a second on my hardware, so that is an acceptable solution in my mind. There are undoubtedly numerous additional optimizations that could be applied – and perhaps even a way to word it out on paper (I doubt it for this one) – so I’d be eager to hear any comments.
|
||||||