だるろぐ

明日できることは、今日しない。

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)記法の簡略表記として使えるので便利