Markdown で好みのヘルパーを使えるようにする

執筆日時:

連日プログラミングの話になる。

WebMatrix + Markdown …… リファクタリング。 で、

Daruboard.Transform(content); は、あらかじめ登録したヘルパーを利用してテキストを整形する仕組みを呼び出している。これについては、また今度。

と書いたと思うんだけど、今日はその話。以下に /App_Code/Daruboard.cshtml の内容を示す。

	@using System.Text.RegularExpressions
@using System.Reflection

@functions {

private struct FormatHelper { public string Signature; public Type Type; public string Method; public object[] DefaultParams; }

private static List<FormatHelper> FormatHelpers = new List<FormatHelper>();

public static void RegisterHelper( string signature, Type type, string method_name, object[] default_params) { if (FormatHelpers.Any(f => f.Signature == signature)) { throw new Exception(string.Format( "Daruboard.RegisterHelper(): {0} has already exist.", signature)); }

MethodInfo method = type.GetMethod(method_name); if (method == null) { throw new Exception(string.Format( "Daruboard.RegisterHelper(): {0} does not have {1}().", type, method_name)); }

FormatHelpers.Add(new FormatHelper() { Signature = signature, Type = type, Method = method_name, DefaultParams = default_params }); }

public static string Transform(string text) { var r = new Regex(@" (<p>)?[[ (?<signature>[^[]|])(|(?<params>[^[]]))* ]](</p>)?", RegexOptions.Singleline | RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled );

text = r.Replace(text, m => { var s = m.Groups["signature"].Value; var p = m.Groups["params"].Value.Split('|'); var f = FormatHelpers.Find(_ => _.Signature == s);

if (f.Signature == null) { return string.Format("<a href='/{0}'>{0}</a>", s); } else { try { MethodInfo method = f.Type.GetMethod(f.Method); object[] parameter = f.DefaultParams; parameter[0] = p[0]; return method.Invoke(null, parameter).ToString(); } catch (Exception e) { return string.Format("[{0}: {1}]", e.GetType(), e.Message); } } });

return text; } }

sigunature|param1|param2… という書式を見つけたら、指定した Signature をもつヘルパーを登録済みリストから探し出し、登録時に指定した Method をリフレクションで呼び出す。ヘルパーの登録は、_AppStart.cshtmlあたりで、

    Daruboard.RegisterHelper("twitter",
typeof(BlackbirdPie),
"GetHtml",
new object[] { null, false, true, Locale.ja }
);

などとと書いておく *1 。書式に Signature しかない場合は、それを URL とみなしリンクにする *2 処理を入れてある。

まぁ、これで、

    [[twitter|ツイートへのURL]]

が、

    @BlackbirdPie.GetHtml(ツイートへのURL)

と解釈されてレンダリングされるというわけ。

さて、ここまでやっちゃうとだんだん読み込み速度が気になってきた。というわけで、今度はキャッシュの使い方を勉強しようかなぁと思う。これまで作った Webアプリは、daruyanagi.net で実際に利用されている(11/11/13現在)。

*1:今のところ、パラメーターは一つ、しかも string 型しか渡せない。true/false ならともかく、Location.ja を string 型から変換するのは面倒くさすぎると思った。将来的には、もう少し改善したい

*2:Markdown の\[URL\](URL)記法の簡略表記として使えるので便利