blog

Ham Cutlet(en)2

HTMLのインデントを綺麗にするだけのサービス、Ham Cutletを英語対応にしました。ブラウザの言語設定で英語が優先になってる人には英語で表示されるようになりました。

HTMLのインデントを病的に気にする外人さんがいたらオススメしてください・・・。

この記事のタグ

View Comments

長らくブログを書かなかったので、右カラムの新着記事の一覧が@komagataさんのアイコンに占領されるという自体に。
ちょっと先月(2010年7月)はバタバタしてたのですが、お盆も終わったことだし、バタバタしてしまった反省もしたし、これからちょくちょくブログを書いていきます。

このバタバタしてたときにとっても役に立ったtodo管理のwebアプリ、todo.ly
ものすごく使いやすくて、これを知った瞬間からすっかりヘヴィユーザーになりました。

snapshot.png

http://todo.ly/
外国の先端のwebサービスって感じのトップページ。
僕はこの手のデザインに弱くて、ついつい使いたくなってしまいます。

sign upしたらメインのページへ。
すごくシンプルなメインページだけど、実はこのサービス、to do管理ツールの部分は下の画像にある1ページのみでページの遷移は一切ありません。

snapshot.png

todo.lyにあるもの

左カラム
  • Filters(to doの一覧を絞り込む)
  • Projects(to doのカテゴリーの一覧を表示させる)
  • Recycle Bin(ゴミ箱)
メインカラム
  • to doの一覧の表示
  • to doの新規追加の為の入力ボックス

これだけ!
このシンプルさは素敵過ぎ。

なんと言ってもtodo.lyはスピーディーに使えるところが使いたくなるポイント。
これはtodo.ly自体がAjaxでサクサク動くっていうのもあるし、UIもサクサクとタスクを登録できるように出来てる。

to do登録の手順
  1. まずはこれから登録するto doのカテゴリーを左カラムから選んでクリック。
  2. あとはto doの内容をメインカラムのボックスに入力するだけ。

to doを登録する際に考えることは、「to doのカテゴリー」と「to doの内容の文」だけ。
ページが遷移しないからサクサク動いてto doを登録するのに数秒しかかからない。
だからガンガンto do登録が進みます。

to doに期日(Due Date)も登録することができますが、しなくてもOK。必要なときはto do登録ボックスの下の「more」をクリックをすれば期日登録のボックスが表示されますが、「more」をクリックするまでは期日登録ボックスが目に入らないところもサクサク感を感じるポイントですね。

まぁ、上記の機能も動作も使い方もremember the milkにあるんだけど、Remember The Milkにはもっと機能があって、todo.lyの方はこれだけ。その分画面にある文字の量も情報も少ないので、Remember The Milkに比べてもっと使うのが簡単そうに見えます。

簡単そうだから特にIT系じゃないパソコンを使う人に勧めやすいです。僕が「使いたい」って思ったのも、やっぱり簡単そうだったから。まずは簡単そうに見えること、そして簡単なこと、これが大事ですね。

付箋にto doを書いてパソコンのモニターに貼ったりだとか、テキストエディタにto doリストを書いて終わったら消していく、なんて作業をやっているのを見たときに教えたくなるようなサービスです。

View Comments

 komagata

RubyInstallerの絶妙なロゴ

  • Aug
  • 12
  • 2010

RubyInstallerのロゴが凄いです。

RubyInstallerはプログラミング言語のRubyをWindowsに簡単にインストールできるソフトです。

Windowsロゴ

Supaki電記 Hamazo店:PC速報

Rubyロゴ

ruby_logo.png

RubyInstallerロゴ

RubyInstaller for Windows

RubyのロゴとWindowsのロゴをうまい具合に組み合わせてあるので、両方を知ってる人なら見ただけでどういうものか分かりそうです。

そもそもRubyInstallerのロゴはデザインコンテストを行って広く募集した中から投票で1位に選ばれた物です。オープンソースのソフトなどはどうしてもプログラマー寄りの物が多く、デザインは後回しになってしまうことが多いですが、一般の方にも使ってもらうにはデザインはとても重要なので、こうした成果物を見ると、こういったコンテストはとても有効だと思いました。

この記事のタグ

View Comments

つくる社さんの環境構築ガイドを勉強してKEIO Railsのイベントに参加しようとした矢先、@machidaさんがWindowsからMacにSwitchしてしまって、当日何も出来ずに精神的に追い詰められてゲロを吐いてしまいそうだというのでMac用のガイドを書いてみます。

全面参考:デザイナーのための Ruby on Rails + Windows 環境構築ガイド | つくる社LLC

Railsのアプリを作るには下記の物をインストールする必要があります。

  1. Xcode + iOS SDK(両方必要無いが、MacPortsを使うのに必要なソフトが一杯付いてくる)
  2. MacPorts(Macのアプリを簡単にインストールするプログラム)
  3. Ruby(プログラミング言語)
  4. RubyGems(Rubyのライブラリを簡単にインストールするプログラム)
  5. SQLite3(データベース。Railsのデフォルトデータベース)
  6. Ruby on Rails(RubyのWebアプリが簡単に作れるフレームワーク)

Xcode + iOS SDKのインストール

下記から最新版をダウンロードしてインストール。(要デベロッパー登録)

https://developer.apple.com/mac/

MacPortsをインストール

下記から最新版をダウンロードしてインストール。(数字が一番大きくて、SnowLeopard対応で拡張子がdmgのヤツ)
例:MacPorts-1.9.1-10.6-SnowLeopard.dmg

http://distfiles.macports.org/MacPorts/

Ruby、RubyGems、SQLite3をインストール

ターミナル.app(別名「黒い画面」)で下記を入力。

1
sudo port install ruby rb-rubygems sqlite3 rb-sqlite3

ターミナル — bash — 80×24

RubyGemsを最新版にアップデート

1
sudo gem update --system

Ruby on Railsをインストール

1
sudo gem install rails

サンプルアプリを作成してRailsの動作確認

1
2
3
4
5
6
7
8
cd (ホームディレクトリに移動)
mkdir rails
cd rails
rails test (test という Rails アプリを作成)
cd test
ruby script/generate scaffold Customer name:string (name というフィールドをひとつだけ持った Customer というモデルをベースにテンプレートの画面を作成)
rake db:migrate (DB にテーブルを作成)
ruby script/server (アプリケーションサーバーを起動)

ターミナル — bash — 80×24

ブラウザより、http://localhost:3000/customers にアクセスし、New customer で新しいレコードを追加したり、そのあと編集・削除などの操作ができれば成功です。

Customers: index

View Comments

毎週火曜日、Help me, hackers!に上がったコードを紹介していくコードDJ 第9回。

「あなだのコードがー しぬほどスキダカダー!」(@チャン・ドンゴン)

komagata a.k.a. DJです。

今回紹介するのはコレ。

Ham Cutlet Chrome Extension – Help me, hackers!

HTMLのインデントを綺麗にする誰得サービスHam CutletのGoogle Chrome Extension。ブラウザの「このページのソースを見る」の代わりにインデントを綺麗にしたソースを表示するボタンをOmniBoxの右に追加するExtensionだ。

mongorian_chop a.k.a. 自由人が作ってくれた。ボタンのアイコンはmachida a.k.a. 漫★画太郎。軽いコラボ。

コードはココ。

mongorian-chop’s hamcutlet-extension at master – GitHub

manifest.json

1
2
3
4
5
6
7
8
9
10
11
12
13
{
    "name": "Ham Cutlet Extension",
    "version": "0.1",
    "description": "Ham Cutletを使用したHTMLソース表示",
    "background_page": "background.html",
    "browser_action": {
        "default_icon": "icon.png",
        "defailt_title": "Ham Cutlet"
    },
    "permissions": [
        "tabs"
    ]
}

background.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<html>
<head>
<script>

function openTab(v) {
    t = "http://hamcutlet.fjord.jp/?url=" + v;
    chrome.tabs.create({url: t});
}

chrome.browserAction.onClicked.addListener(function(tab) {
  getUrl();
});

function getUrl() {
    chrome.tabs.getSelected(null, function(tab) {
        var url = tab.url;
        if(url.match(/^http(s)?/)) {
            openTab(tab.url);
        }
    });
}

</script>
</head>
<body>
</body>
</html>

Chrome Extensionの作り方なんて知らないって?それならDJに任せろ!当然の如くDJも知らないからお前らの代わりに泣きながら覚えてやる!

モダンブラウザ・JS好きなら知らぬものはいないid:os0xの連載、続・先取り! Google Chrome Extensions:第1回 Chrome ExtensionsのAPI#1|gihyo.jp … 技術評論社で丁寧に説明されてるのでこれでわかりそうだ。

DJが超訳するとこんな感じだ。

  • manifest.jsonの入ったディレクトリを作る
  • 上のバーの右にあるボタンはbrowser_actionという
  • browser_actionのボタンを押した時の動きはbackground_pageで指定したhtmlに書く
  • Chromeで.crxパッケージを作れる。(単なるzipなので解凍も出来る)(CUI野郎にはConstellation’s crxmake at master – GitHubというパッケージングコマンドもあるらしい。)

作れる気がしてきた!早速コードを見ていこう。

6行目のbrowser_actionでタイトルとアイコンを設定してる。

10行目のpermissionsはクロスドメインでアクセスできるURLを指定するようだが、tabsはなんだろう?

5行目のbackground_pageで実際の動作を書くファイルを指定している。

background.htmlを見ていこう。

10行目のchrome.browserAction.onClicked.addListenerがキモだ。こうやってbrowser_actionをクリックした時の動作を設定するのだろう。

7行目のchrome.tabs.createでURLを指定してタブを開けるらしい。何かグリモンでも思ったけどクロスブラウザ問題が無いとJSってこんなにシンプルに書けるのかという・・・。

URLは以前、「HTMLのインデントを綺麗にするAPI – Help me, hackers!」でkyanny a.k.a. 刺身ブーメランが作ってくれたHam Cutlet APIを使って該当ページのソースをインデントを綺麗にした上で取得している。

これは簡単・・・。グリモンやFF拡張もそうだけど、実用的なものが簡単に作れるのがイイ!ECMAScripter的にもクロスブラウザに悩まされず思いっきりJSを書けるのも気持ちがイイ。

この記事のタグ

View Comments

毎週火曜日、Help me, hackers!に上がったコードを紹介していくコードDJ 第8回。

「おでのからだはぼどぼどだー!」

komagata a.k.a. DJです。

今週はコレ。

WordPress.orgのプラグイン一覧でダウンロード数に目印をつけて欲しい – Help me, hackers!

登録してくれたのはmonoooki a.k.a. 前田製作所。速攻解決してくれたのはazu_re a.k.a. Firefoxの人(DJ主観)。

ソースコード:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
// ==UserScript==
// @name           WordPress.org highlight counter
// @namespace      http://efcl.info/
// @description    WordPress.orgのタグ/単語検索のダウンロード数によって色を変える
// @include        http://wordpress.org/extend/plugins/tags/*
// @include        http://wordpress.org/extend/plugins/search*
// ==/UserScript==
/*  TEST URL
    http://wordpress.org/extend/plugins/search.php?q=word
    http://wordpress.org/extend/plugins/tags/widget
*/

GM_addStyle(<><![CDATA[
    .GM_downloads_count_50000{
        background: #cc0000; color: #fff; padding: 1px 3px; -moz-border-radius: 3px;
    }
    .GM_downloads_count_10000{
        background:#ffb0b0; padding: 1px 3px; -moz-border-radius: 3px;
    }
    .GM_downloads_count_5000{
        background:#ffd792; padding: 1px 3px; -moz-border-radius: 3px;
    }
    .GM_downloads_count_2000{
        background:#f9f49d; padding: 1px 3px; -moz-border-radius: 3px;
    }
]]></>);
function hilightCounter(node){
    var downloadsSpan = $X('//span[@class="info-marker"][text()="Downloads"]' ,node);
    for(var i=0,len=downloadsSpan.length;i<len;i++){
        var countTextNode = downloadsSpan[i].nextSibling;
        var downloadCount = parseInt(countTextNode.textContent.replace(",","","g"), "10");// 数値化
        var parDownload = downloadsSpan[i].parentNode;
        var span = document.createElement("span");
        if(downloadCount > 50000){
            span.setAttribute("class" , "GM_downloads_count_50000");
        }else if(downloadCount > 10000){
            span.setAttribute("class" , "GM_downloads_count_10000");
        }else if(downloadCount > 5000){
            span.setAttribute("class" , "GM_downloads_count_5000");
        }else if(downloadCount > 2000){
            span.setAttribute("class" , "GM_downloads_count_2000");
        }else{
            continue;
        }
        span.textContent = countTextNode.textContent;
        parDownload.replaceChild(span , countTextNode);
    }
}

document.body.addEventListener('AutoPagerize_DOMNodeInserted',function(evt){
    var node = evt.target;
    //var requestURL = evt.newValue;
    //var parentNode = evt.relatedNode;
    hilightCounter(node);
}, false);
hilightCounter(document);


// $X on XHTML
// @target Freifox3, Chrome3, Safari4, Opera10
// @source http://gist.github.com/184276.txt
function $X (exp, context) {
    context || (context = document);
    var _document  = context.ownerDocument || context,
    documentElement = _document.documentElement,
    isXHTML = documentElement.tagName !== 'HTML' && _document.createElement('p').tagName === 'p',
    defaultPrefix = null;
    if (isXHTML) {
        defaultPrefix = '__default__';
        exp = addDefaultPrefix(exp, defaultPrefix);
    }
    function resolver (prefix) {
        return context.lookupNamespaceURI(prefix === defaultPrefix ? null : prefix) ||
               documentElement.namespaceURI || "";
    }

    var result = _document.evaluate(exp, context, resolver, XPathResult.ANY_TYPE, null);
    switch (result.resultType) {
        case XPathResult.STRING_TYPE : return result.stringValue;
        case XPathResult.NUMBER_TYPE : return result.numberValue;
        case XPathResult.BOOLEAN_TYPE: return result.booleanValue;
        case XPathResult.UNORDERED_NODE_ITERATOR_TYPE:
            // not ensure the order.
            var ret = [], i = null;
            while (i = result.iterateNext()) ret.push(i);
            return ret;
    }
}

このブログを読んでる人達ならそろそろグリモン見慣れてきたんじゃないか?泣きながらソース読んでたDJも慣れてきたよ。

12行目からはxmlリテラルでstyleを追加。でも、先頭の<>ってのはなんだろう?

61行目の$X関数はXPathのためのラッパー。こうして見ると色々な人が色々なXPathユーティリティーを選んでるのがわかる。$X関数は比較的昔からあって有名な方なんじゃないかと思う。

26行目のhilightCounterで注文通り、ダウンロード数別に色を変えるためにclassを割り当ててる。

41行目はびっくり。AutoPagerizeのイベントにひっかけてる。ブログのソースコードのSyntax HilightingとかJSのonloadでやってる場合に、AutoPagerizeの2ページ目からは反映されなくてガッカリ・・・ってことが多かったんだけどこれを使えばイケる!

これはDJ得した気分。

WordPressのプラグインページは見る人が多いからuserscripts.orgにきちんと登録してある点も素敵だ。

この記事のタグ

View Comments

毎週火曜日、Help me, hackers!に上がったコードを紹介していくコードDJ 第7回。

今、流行の24シーズン1を見ているkomagata a.k.a. DJ(@流行に疎い方)です。

今回紹介するコードはコレ。

任意の期間、ブログが更新されないとメールが飛ぶwordpressのplugin – Help me, hackers!

出題したのは@machida a.k.a. お米嫌い。議論に対していきなりコードで回答を示したのが@terakuma a.k.a. 法務系PHPer。

コード:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
<?php
/*
Plugin Name: Remember The WordPress
Plugin URI: http://github.com/e2esoundcom/Remember-The-WordPress
Description: If you forgot write a new post,this plugin send you E-mail.
Version: 0.2
Author: Yuya Terajima
Author URI: http://www.e2esound.com/
*/


/* When Activate Plugin
==================================================================== */

function rtw_activate() {
    $time = time();
    $terms = 7; //default terms
    $unixtime_per_day = 86400;
    $email = get_bloginfo('admin_email');
    $subject = "Remember the WordPress!!";
    $message = "Blogの更新が滞っているようです。サイトの更新をお願いします。";

    if(!get_option('rtw_initialized') || get_option('rtw_initialized') !== $time) {
        update_option('rtw_initialized',$time);
    }
    if(!get_option('rtw_terms')) {
        update_option('rtw_terms',$terms * $unixtime_per_day);
    }
    if(!get_option('rtw_email')) {
        update_option('rtw_email',$email);
    }
    if(!get_option('rtw_subject')) {
        update_option('rtw_subject',$subject);
    }
    if(!get_option('rtw_message')) {
        update_option('rtw_message',$message);
    }
    wp_schedule_event($time + 86400, 'daily', 'rtw_cron');
}
register_activation_hook(__FILE__, 'rtw_activate');

function get_latest_post_time() {
    global $wpdb;
    $query = "SELECT post_date FROM ".$wpdb->posts."
            WHERE post_status = 'publish'
            ORDER BY `"
.$wpdb->posts."`.`post_date` DESC LIMIT 0,1";
    $query = $wpdb->prepare($query);
    return strtotime($wpdb->get_var($query));
    }

function rtw_sendmail()
{
    $subject  = get_option('rtw_subject');
    $mailbody = get_option('rtw_message');
    $to_email = get_option('rtw_email');
   
    mb_send_mail($to_email,$subject,$mailbody);
}

function rtw_compare_time(){
    $unixtime_per_day = 86400;
    $latest = get_latest_post_time();
    $terms = intval(get_option('rtw_terms')) + intval($latest);
    $time = time();

    if(intval($terms) < intval($time)){
        rtw_sendmail();
    }
}
add_action('rtw_cron', 'rtw_compare_time');

/* When New Post or Edited Post.
==================================================================== */


function rtw_new_posted(){
    update_option('rtw_initialized',time());
    }
add_action('publish_post','rtw_new_posted');

/* When De-Activate Plugin
==================================================================== */


function rtw_deactivate() {
    wp_clear_scheduled_hook('rtw_cron');
    delete_option('rtw_initialized');
    delete_option('rtw_terms');
    delete_option('rtw_message');
    delete_option('rtw_subject');
    delete_option('rtw_email');
}
register_deactivation_hook(__FILE__, 'rtw_deactivate');


/* Admin
==================================================================== */

function rtw_add_admin_menu(){
    add_options_page('Remember The WordPress','Remember The WP','administrator',__FILE__,'rtw_add_admin_page');
}
add_action('admin_menu','rtw_add_admin_menu');

function rtw_add_admin_page(){    
   
    if(isset($_POST['posted']) === FALSE){
        $posted = FALSE;
    }elseif(isset($_POST['posted']) === TRUE){
        $posted = TRUE;
    }

    $unixtime_per_day = 86400;
    if($posted) {
        //Validation
        if(preg_match('/[1-3][0-9]|[1-9]/',intval($_POST['terms']) AND intval($_POST['terms']) <= 30)){
            update_option('rtw_terms',intval($_POST['terms'] * $unixtime_per_day));
            update_option('rtw_email',stripslashes($_POST['email']));
            update_option('rtw_subject',stripslashes($_POST['subject']));
            update_option('rtw_message',stripslashes($_POST['message']));
            $rtw_error = FALSE;
        }else{
            $rtw_error = TRUE;
        }
    }
?>

<?php
    //Admin Page Start
    //Updated Message
    if($posted === TRUE AND $rtw_error === FALSE) : ?>
<div class="updated"><p><strong>設定を保存しました</strong></p></div>
<?php elseif($posted === TRUE AND $rtw_error === TRUE):?>
<div class="error"><p><strong>アラート発生日数は1-30の間の値を入力して下さい。</strong></p></div>
<?php endif; ?>

<div class="wrap">
    <h2>Remember The WordPress Settings</h2>
    <form method="post" action="<?php echo str_replace( '%7E', '~', $_SERVER['REQUEST_URI']); ?>">
        <input type="hidden" name="posted" value="yes">
        <table class="form-table">
            <tr valign="top">
                <th scope="row"><label for="terms">アラート発生日数<label></th>
                <td>
                    <input name="terms" type="text" id="terms" value="<?php echo intval(get_option('rtw_terms') / $unixtime_per_day); ?>" class="regular-text code" /><br />
                    1-30までの数字を入力してください。
                </td>
            </tr>
            <tr valign="top">
                <th scope="row"><label for="email">送信先E-mailアドレス<label></th>
                <td>
                    <input name="email" type="text" id="email" value="<?php echo htmlspecialchars(get_option('rtw_email'),ENT_QUOTES); ?>" class="regular-text code" /><br />
                    アラートメールの送信先E-mailアドレスを入力してください。
                </td>
            </tr>
            <tr valign="top">
                <th scope="row"><label for="terms">メールタイトル<label></th>
                <td>
                    <input name="subject" type="text" id="subject" value="<?php echo htmlspecialchars(get_option('rtw_subject'),ENT_QUOTES); ?>" class="regular-text code" /><br />
                    送信メッセージの「タイトル」を入力してください。
                </td>
            </tr>
            <tr valign="top">
                <th scope="row"><label for="terms">メール本文<label></th>
                <td>
                <textarea name='message' id='message' cols='50' rows='10'><?php echo htmlspecialchars(get_option('rtw_message'),ENT_QUOTES); ?></textarea>
                    <br />
                    送信メッセージの「内容」を入力してください。
                </td>
            </tr>

           
        </table>

        <p class="submit">
            <input type="submit" name="Submit" class="button-primary" value="変更を保存" />
        </p>
    </form>
</div>
<?php } ?>

少々長いがへこたれずに頑張ってみていくことにしよう。(自分自身を鼓舞)

WordPressプラグインの作成方法

前提としてこれはWordPressプラグインなのでその大まかな作り方を紹介しよう。

まず、単体のphpファイルか、もしくは同名のディレクトリにプラグイン用のファイルを作る。
(例:foo_plugin/foo_plugin.php)

プラグインの各種情報は決まったコメントの書き方があるのでそれにしたがってファイル内先頭に書く。これがWordPressのプラグイン管理画面に出てくる情報になる。

一番単純なプラグインとしては、プラグインを管理画面から有効にすると、プラグインのphpファイルがWordPressにincludeされる。よって適当な関数をプラグイン内で定義しておき、テーマの中で使うといったことが可能だ。(モチロン名前はぶつかる可能性がある。プラグイン名をprefixにした関数を作るか、プラグイン名のクラスに閉じ込めることが推奨されている。)

これでWordPressの関数や変数、DBをプラグイン内からでも使うことが出来る。また、プラグイン用にactionやfilterといったフックをかける事ができる。この辺は公式ドキュメントに全関数リファレンスやプラグイン用のフックの説明がちゃんとあるのでPHPが分かれば理解するのは容易だ。(Main Page – WordPress Codex 日本語版

擬似cron

今回のコードを理解する上でもう一つ必要になるのが、wp_schedule_event関数を使った擬似cronの仕組みだ。WordPressにはwp_schedule_event関数でcronのように定期スケジュールを登録することが出来る。こういう処理はcron daemonのように常に起動しているプロセスと連携する必要があり、一見単純なPHPウェブアプリだけでは無理に思えるが、Webからのユーザーリクエストのみをトリガーにcronのようなスケジュール実行を可能にしている。

もちろんWebからのリクエスト頼みなのでアクセスがまったく来なければスケジュールした時間には実行されない擬似的な機能なので厳密な処理には向かない。しかし、一般的なサイトであれば大抵毎分1アクセスぐらいはあるので大体このぐらいの頻度でやって欲しいといった処理なら実用上は問題なさそうだ。

参照:Function Reference/wp schedule event « WordPress Codex

こんないい加減な仕組み使うなんてありえねー!とヤルタイプからは言われそうだが、phpのsession削除は同様の仕組みで行われている。(アクセスが来なければ永久にセッションファイルは消えない)
根本的なこの仕組の是非は兎も角、PHPの流儀に従った実装方法ではあると言える。

参照:PHP: 実行時設定 – Manual

Remenber The WordPress

まず、38行目でregister_activation_hook関数で自分自身のファイルのrtw_activate関数をactivation_hookに登録している。activationとはプラグイン管理画面でそのプラグインを「有効」にしたときのことだ。データベーステーブルを独自に持つようなプラグインでは大抵この場所でテーブルをCREATEしている。(WordPressはORMを提供していないので、各種プラグインのこの場所にはMySQLに特化したSQLが大抵使用されている。WordPressはプラグインエコシステムがキモであるため、コレのせいで他DBへの対応が非常に難しくなっている。)

rtw_activate関数の中でwp_schedule_eventを使ってdailyで実行されるようにrtw_cronを登録している。

あとは、40行目のget_latest_post_time関数で最終投稿日を取得して、管理画面で設定できる「何日更新が無かったらメールを送るか?」という値と比較してmb_send_mailでメールを送っている。

殆どの行数は管理画面用なので長く見えるし大変そうだが、管理画面からプラグインの設定が出来るというのがWordPressユーザーに非常に訴える箇所なので重要だ。

議論にコードで答えた事。(これが結構貴重なんだが)WordPress流儀に則った実装。管理画面まで手を抜かない所。等々、@terakuma a.k.a. 法務系PHPerは素晴らしい。それにRemember The MilkをパクったRemember The WordPressというプラグイン名も素敵だ。

WordPressプラグインで困ったことがあったら@terakuma a.k.a. 法務系PHPerに相談してみたらいいかもしれない!

この記事のタグ

View Comments

毎週火曜日、Help me, hackers!に上がったコードを紹介していくコードDJ 第6回。

おい!ワールドカップ見てない癖に適当に話を合わせるのは辞めろ!DJ?モチロン見てたよ。本田のシュートは凄かったよね!・・・くらいやがれ!これが俺のワールドカップに対する知識の全てだ!

komagata a.k.a. DJです。

今回紹介するのはコレ。

無印良品のサイトの表示をデフォルトで「安い順、150件表示」にする – Help me, hackers!

まさに「プログラマーにとって解きたくなるような問題」といったセンスの良い問題を投稿してくれたのは@monoooki a.k.a. 前田製作所

そして解決してくれたのはお馴染み@milk1000cc a.k.a. 牛乳嫌いプログラマ on Rails。(@milk1000ccはDJのストーキング情報によれば最近PONPONというグルーポン情報まとめサイトをRails, Herokuで公開したらしいRails野郎だ。)

二人ともありがとう!

しかし、ちょっと待ってくれ。コイツをどう思う?(@阿部高和)

1
2
3
4
5
% heroku console
>> Task.find(106).created_at
=> Tue, 06 Jul 2010 04:16:07 UTC +00:00
>> Task.find(106).comments.correct.first.created_at
=> Tue, 06 Jul 2010 07:02:54 UTC +00:00

「すごく・・・早いです・・・。」(@道下正樹)

タスクが登録されてからわずか3時間足らずで解決されている。(@荒巻)

たしかにDJも「これは解きたくなる問題だな」とは思ってはいたが、あまりにもはやすぐるでしょう?

本題のタスクの内容は、

無印良品ネットストア

こんな感じで無印良品ネットストアのデフォルトは「おすすめ順」「50件表示」になってる。それを「安い順」「150件表示」をデフォルトになるようにして欲しいというタスク。

あるある。確かにハードに使ってるサイトでこういうちょっとしたところを俺仕様にしたいことある。

それを通常ならありえない速さできょうきょ参戦(@ブロントさん)してくれたグリモンコードがコレ。

http://gist.github.com/465095
(右上のrawのリンクをクリックでグリモンスクリプトインストール)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// ==UserScript==
// @name           AutoMujiSort
// @namespace      http://www.milk1000.cc/
// @include        http://www.muji.net/store/cmdty/section/*
// ==/UserScript==

(function() {
    var OLD_PATTERN = { sort: 4, count: 12 };
    var NEW_PATTERN = { sort: 0, count: 150 };

    var $ = function(selector) { return document.querySelector(selector); };

    if ($('select[name="sort"]') && $('select[name="sort"]').value == OLD_PATTERN.sort &&
        $('select[name="count"]') && $('select[name="count"]').value == OLD_PATTERN.count) {
        $('select[name="sort"]').value = NEW_PATTERN.sort;
        $('select[name="count"]').value = NEW_PATTERN.count;
        (typeof unsafeWindow != 'undefined' ? unsafeWindow : window).sort();
    }
})();

きれいなコード。DJ以前はグリモンのコード「嫌だなぁ、嫌だなぁ、怖いなぁ」(@稲川淳二)なんて思ってたけどこの連載のお陰でむしろ好きになってきた。読み易い。

11行目のquerySelectorを$に割り当てるのはECMAScripterお馴染みパンチライン。最初のvarはconstにしたら速くなるのかも?

17行目のunsafeWindowを見てるのは多分ブラウザによって違うところなんだろう。

DJそろそろ気付いてきたんだが、今、タスクの需要と供給は依頼が少ない状態にある。特にJavascriptのタスクは監視してるイカレタHacker達が飢えた獣のように跳びかかり、瞬時に解決されてしまう。

成果物の公開方法やパッケージング方法、環境作りが楽なグリモン・JavascriptはHacker側にとって非常にやりやすい。そういう事情を踏まえた依頼の仕方もセンスが問われている。

センスを磨くか、金を積むか。そういう仕組みになっているらしい。

この記事のタグ

View Comments

毎週火曜日、Help me, hackers!に上がったコードを紹介していくコードDJ 第5回。

TimeMachineで自動バックアップを取っていても結局クリーンインストール後は手でデータを復旧させる昭和の男、komagata a.k.a. DJです。

今回はコレ、フォームのデフォルトの文字をフォーカスが移ったら消す – Help me, hackers!

コレの経緯はこんな感じだ。

  1. F5V a.k.a. PHPerよりHam CutletにTwitter経由で上記の要望が入る。
  2. DJが要望をHelp me, hacekrs!に登録する。
  3. mongorian_chop a.k.a. 自由人からjQuery Watermark Pluginの情報を貰う。
  4. USTREAMの開発生放送でDJが上記プラグインでの実装に敗北。
  5. 見るに見かねたmongorian_chopが実装してpull request。

100万語の言葉より1つのpull request。コードは偉大だ。

「コード!コード!コード!」

「よし、ならばコードだ。」

今回のキモはココ。

views/index.haml at c7cf55da11f2131568deda5b1d4b7a46742943f8 from mongorian-chop’s hamcutlet – GitHub

1
2
3
4
5
6
7
8
9
$(document).ready(function(){
    $.updnWatermark.attachAll();
    $('.updnWatermark > label').click(function(){
        $(this).toggle();
        $("textarea").select();
    }).blur(function(){
        $(this).toggle();
    });
});

$(document).ready(function(){ … }) はお馴染み、DOMの構築が終わった瞬間に実行してくれるjQueryの書き方だ。$(function() { … }) でも同じ。画像の読み込み等を待たない分、onloadより速いのでjQuerystはドンドン使ってこ!

今回、DJが挫折してたのはjQuery Watermark Pluginをtextareaに使った時に、そのWatermarkのテキスト自身をクリックした場合にWatermakが消えないところだ。

mongorian_chopはそこをちゃんと消すコードを書いてくれた。ありがとう!

jQueryだとちょっと面倒に思えた処理もこんなにスマートな見た目で書けるなんて。DJこの連載のおかげで強制的にJS勉強させられてるね!

DJ、今現在もHelp me, hacekers!上で複数のコード野郎共とコラボ中だ。議論する暇があったらコードを送りつけろ!

この記事のタグ

View Comments

help me, hackers!でも色々参考に(ときにパクったり)させていただいた@func09さんの美味しそう!を共有するサービス「ラペコ」。

美味しいそうな食べ物の写真が並んでて見ていて楽しい、サイトのデザインも可愛いくて素敵なサイトです。モニターに映るラペコを前に僕らの間は嫉妬の空気がいつも充満。

ラペコにアップした@komagataさんの肉の写真がかなり長い間人気のペコフォトランキングで上位に。
@komagataさんが「komagata a.k.a. rapeco」の肉番長の名を欲しいままにしています。

snapshot.png

2010年6月26日現在2位!

snapshot.png

これが肉番長の肉写真

ちなみに写真の右側にいるのが僕で、左がアクトインディ社長の@lupin13さんです。ついでに僕の当時の携帯にもその肉の写真が入ってました(ココ)。

@func09さんはhelp me, hackers!にあげたハムカツ改良タスクを見事な方法で解決にしていただいたり、生放送に付き合ってくれたり、本当ナイスガイなプログラマーさん。いつもありがとうございます。

「komagata a.k.a. ラペコの肉番長」への挑戦者求む!

この記事のタグ

View Comments