2010-11-15

内部リンクを置き換える #2 (Blogger Glass)

#1 のデザイン(設計)にしたがい、最小限の機能を実装できた。

実装

permalink と Post ID をひもづける

ひもづけ情報としてデータストアに保存するのは、(1) permalink、(2) Blog ID、(3) Post ID の 3 つ。このうち、(1) をデータ実体へのキーに使う。というのも、この情報を利用するときには permalink に一致する Post ID を引き出すことになるからだ。permalink をキーにしておけばデータストアに問い合わせるクエリを組み立てる必要がない。

以下が、「ひもづけ情報」を保存するデータ実体の定義と、それを利用するための関数だ(src/model.py)。

class PairOfLinks(db.Model):
    """
    PairOfLinks binds a permalink to a post_id of a blog specified
    with a blog_id.
    Each entity of PairOfLinks must be created specifying a permalink
    as its key.
    """
    blog_id = db.StringProperty()
    post_id = db.StringProperty()

def get_post_id(permalink):
    post_id = None
    pol = db.get(db.Key.from_path('PairOfLinks', permalink))
    if pol:
        post_id = pol.post_id
    return post_id

def bind_post_id_with_permalink(permalink, blog_id, post_id):
    pol = db.get(db.Key.from_path('PairOfLinks', permalink))
    if not pol:                 # new bind
        pol = PairOfLinks(key_name=permalink)
        pol.blog_id = blog_id
        pol.post_id = post_id
        pol.put()

get_post_id 関数は permalink にひもづけた Post ID を取り出すためのもの。記事のテキストを置換する際に用いることを想定している。permalink に対してひもづけられた Post ID がないときには None を返す。

bind_post_id_with_permalink 関数は permalink と Post ID のひもづけを保存するもの。「一覧」系の画面を作る際に用いることを想定している。指定された permalink に対して、すでにひもづけが存在した場合には何もしない。これは、permalink と Post ID の組み合わせが変わることはない、という前提に立つもの。Blogger の場合、記事をポストした後にこの 2 つを変更する手段がないから、この前提が成り立つ。

ひもづけを生成する部分のコードは以下のようになる(src/listview.py)。

        feed = util.get_posts(q)
        if feed:
            [...snip...]
            for entry in feed.entry:
                util.regist_pair_of_links(entry)

実際に、ひもづけ情報を保存する regist_pair_of_links 関数は util モジュールで定義している。上述の bind_post_id_with_permalink 関数を呼び出すだけのものだ。

def regist_pair_of_links(entry):
    model.bind_post_id_with_permalink(entry.get_html_link().href,
                                      entry.get_blog_id(),
                                      entry.get_post_id())

permalink へのリンクを置き換える

記事テキスト内のリンクの置換は以下の関数で行う(src/util.py)。

def replace_permalinks(original):
    pat = re.compile('<a[^>]*href=\"([^"]*)\"[^>]*>')
    new_text = original
    pos = 0
    while pos < len(new_text):
        m = pat.search(new_text, pos)
        if not m:
            break
        permalink = m.group(1)
        logging.info("Permlink: %s" % permalink)
        post_id = model.get_post_id(permalink)
        if post_id:
            postlink = "/post/?id=%s" % post_id
            new_text = new_text.replace(permalink, postlink)
            pos = m.end() - len(permalink) + len(postlink) + 1
        else:
            pos = m.end() + 1
    return new_text

正規表現を使い href 属性を持った a 要素を探し、href 属性の値を取り出す。その値を permalink としてひもづけられた Post ID が記録されていれば、それを Blogger Glass の「記事」画面へのリクエスト URL に置き換える。この処理はテキスト中から a 要素が見つからなくなるまで行う。

テキスト中を(正規表現で)探す際には、開始位置を pos で指定しているが、置換を実行した場合は次の開始位置がずれるため 185 行目で調整している。

これを呼び出す部分が src/postview.py 中にある。

    def fill_view_attrs(self, post_id):
        [...snip...]
        entry = client.get_one_post(settings.get('blog_id'), post_id)
        if entry:
            [...snip...]
            self.view.content = util.replace_permalinks(entry.content.text)

これで完成か?

#1 でも書いたように、データストアに保存したひもづけ情報を「いつ消すか」という問題が残っている。経過時間で切るなら保存した実体の中に記録した日時を保管しなければならない。加えて、「消す」作業を開始するトリガーをどうするかという問題もある。「設定」画面でユーザに明示的に消させるか。その場合、リンクの置き換えが機能するにはログインが前提となる。それでも構わないかな。

関連リンク

関連記事

0 件のコメント:

コメントを投稿