2010/09/07

GAEでのアップデートエラー

GAEでアップデート中にキャンセル等をすると次回以降エラーでアップデートできなくなるときがあります。エラー内容は次の様な形でrollbackしろとの指示が出ます。
undo the transaction with appcfg.py's "rollback" command.
その場合はターミナルから次のコマンドを入力します。
appcfg.py -verbose --no_cookies --email=アドレス --passin rollback 対象のディレクトリ

2010/08/18

MixiアプリのDeveloper登録

Mixiアプリを作るときはhttps://sap.mixi.jp/home.plからDeveloper登録を行ないます。
最後に携帯メールでの認証があるのですがiPhoneの場合は@i.softbank.jpの方にメールをしないとうまくいきません。

2010/07/30

macportのインストール

macのパッケージ管理はmacportが便利です。毎回ビルドするのでちょっと遅いですが。
インストール方法はオフィシャルサイトからイメージをダウンロード後、インストールします。その後、.bash_profileに次のパスを追記します。
export PATH=/opt/local/bin:/opt/local/sbin/:$PATH
export MANPATH=/opt/local/man:$MANPATH

環境はSnowLeopardです。
Leopardからアップデートした場合にmigrationが必要になる場合があるようです。
方法はオフィシャルサイトに書いてあります。
port installed > myports.txt
sudo port -f uninstall installed
sudo port clean --work --archive all
これで全て消したあと自動的に再インストールするスクリプトを実行。
curl -O http://svn.macports.org/repository/macports/contrib/restore_ports/restore_ports.tcl
chmod +x restore_ports.tcl
sudo ./restore_ports.tcl myports.txt
上がダメな場合はmyport.txtを見ながら手動で入れてくれとのこと。
sudo port install portname +variant1 +variant2 ...

公開鍵の作り方

sftpなどのための公開鍵の作り方についてまとめておきます。
$ whoami
(ユーザー名)
$ ssh-keygen -t rsa //カギを作成する
Generating public/private rsa key pair.
Enter file in which to save the key (/home/(ユーザー名)/.ssh/id_rsa):
Created directory '/home/(ユーザー名)/.ssh'.   
Enter passphrase (empty for no passphrase): //パスワードを入力
Enter same passphrase again: //もう一度パスワードを入力
Your identification has been saved in /home/(ユーザー名)/.ssh/id_rsa.
Your public key has been saved in /home/(ユーザー名)/.ssh/id_rsa.pub.
The key fingerprint is:
ff:8a:6b:d8:eb:ad:68:90:33:eb:2c:6c:d8:ea:98:41 (ユーザー名)@(PC名)
$ ls -la ~/.ssh //確認
合計 16
drwx------    2 xxx     xxx         4096 Feb  2 13:53 ./
drwx------   22 xxx     xxx         4096 Feb  2 13:53 ../
-rw-------    1 xxx     xxx          951 Feb  2 13:53 id_rsa     //秘密鍵
-rw-r--r--    1 xxx     xxx          236 Feb  2 13:53 id_rsa.pub //公開鍵
$ mv ~/.ssh/id_rsa.pub ~/.ssh/authorized_keys //authorized_keysにリネーム

SnowLeopardにPerl環境構築

基本はモダンなPerlの開発環境の構築方法を参考にする。

Xcodeは普通に入れて下さい。
Perlbrewを入れる。
$ curl -LO http://xrl.us/perlbrew
$ chmod +x perlbrew
$ ./perlbrew install
.bachrc_profileにsource ~/perl5/perlbrew/etc/bashrc を追記。
.bachrc_profileファイルは無かったので新規に作る。
ターミナルを再起動してPerlを最新版にする。
% perlbrew install perl-5.12.1
% perlbrew switch perl-5.12.1
ちょっと時間がかかるので待つ。
cpanmを入れる。今回はGitで入れました。
$ git clone git://github.com/miyagawa/cpanminus.git
$ cd cpanminus
$ perl Makefile.PL
$ make install

local::lib をインストールする。
$ cpanm local::lib
ここまでがリンク先で書かれていたPerl開発環境。

cpam moduleをアップデート。
$ cpanm App::cpanoutdated
$ cpan-outdated | cpanm -L ~/perl5
Plaggerをインストール。
$ git clone git://github.com/miyagawa/plagger.git
$ cd plagger
$ cpanm -L ~/perl5 .

2010/07/20

memcachedの起動メモ

memcachedの起動メモ。メモリ1Gで接続許可はローカルホストの場合
memcached -u memcached -d -m 1024 -l 127.0.0.1

2010/07/19

ブラウザ三国志で書簡を保存するグリモンを修正

http://www.ai-mai.net/archives/2010/01/post-2.phpにて公開されていたGreasemonkeyスクリプトを取りあえず動くように修正。今回はChrome対応してません。(というか31行目jsonObj.toSource()が上手くいかなかった)自己責任でお願いします。

ファイルはこちら
// ==UserScript==
// @name           bura3_MailBox
// @description    書簡を保存する
// @include        http://*.3gokushi.jp/message/inbox.php
// @include        http://*.3gokushi.jp/message/inbox.php#*
// @include        http://*.3gokushi.jp/message/inbox.php?p=1*
// ==/UserScript==

var oldData = getData();
//初回(自ページから)のnext link
var nextUri = "";
//カウンタ
var offset = 0;
//本文内の情報を格納
var mailBoxSet = new Array();
//本文へのURIを格納
var mailUri =  new Array();

var LOCAL_STORAGE = "bro3_mailbox";

//XPath
function xpath(query,targetDoc) {return document.evaluate(query, targetDoc, null,XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);}

//GM永続データの取得
function getData(){
  return eval(GM_getValue(location.host + "MailBox"));
}

//GM永続データの保存
function setData(jsonObj){
  GM_setValue(location.host + "MailBox", jsonObj.toSource());
}

//渡されたHTMLデータから書簡本文へのリンクだけを抜き出す
function mailUriSet(htmlData){
  var htmlSnap = xpath("//table[@class='commonTables']/tbody/tr/td[2]/a", htmlData);
  for (i=0; i < htmlSnap.snapshotLength; i++){
    mailUri[offset] = htmlSnap.snapshotItem(i).href;
    offset++;
  }
  if (htmlData == document){
    //次に画面下部のnext linkから抜き出し
    nextUriFetch();
  }
}

//リンクを一気に取得
function nextUriFetch(){
  var nextXhr = new XMLHttpRequest();
  //nextXhr.open("GET",nextUri);
  //nextXhr.send(null);

 // nextXhr.onreadystatechange = function(){
  //  if(nextXhr.readyState ==4){
  //    if(nextXhr.status == 200){
        //パーサーチックに
        var newDiv = document.createElement("div");
        newDiv.innerHTML = nextXhr.responseText;
        //パースしたのをmailUriSetに投げる。
        mailUriSet(newDiv);
        //スマートじゃないけど面倒臭いからこれで
        try{
            nextUri = newDiv.getElementsByClassName("last")[1].childNodes[0].href
        }
        catch(e){
          offset = 0;
          //アーカイブと同じIDのは削除する。
          var tmpIdx = 0;
          var changeFlag = false;
          if(oldData){
            for(var i=0; i < mailUri.length;){
              for(var j=0; j<oldData.length; j++){
                if(oldData[j][0] == mailUri[i].match("id=[0-9]+")[0].split("=")[1]){
                  changeFlag = true;
                  break;
                }
              }
              if(changeFlag){
                mailUri.splice(i,1);
                changeFlag = false;
              }
              else{
                i++;
              }
            }
          }
          //nextをすべて抜き出しおわったら本文内の処理をやっていく。
          xhr();
          return;
        }
        nextUriFetch();
      //}
    //}
  //}
}

//XHR用の再帰関数
function xhr(){
  //オブジェクトが無かったらおしまい。
  if(!mailUri[offset]){
    //make save data
    makeSaveData();
    nextUri = document.getElementsByClassName("last")[1].childNodes[0].href;
    alert("保存処理が終わりました。");
    return 0;
  }
  targetUri = mailUri[offset];
  //XHR
  var xmlhttp = new XMLHttpRequest();
  xmlhttp.open("GET",targetUri);
  xmlhttp.send(null);
  //ステータスチェンジで発生するイベント
  xmlhttp.onreadystatechange = function(){
    //ifネストしてるのはキャッシュ判定とかのためだけどたぶん使わない
    if(xmlhttp.readyState ==4){
      if(xmlhttp.status == 200){
        //パーサー
        var responseXML = document.createElement("div");
        responseXML.innerHTML = xmlhttp.responseText;
        //書簡の部分だけを取得
        var message = responseXML.getElementsByClassName("ttl w80");
        mailBoxSet[offset] = new Array();
        //uriのIDをメッセージIDにしてunique管理
        mailBoxSet[offset][0] = targetUri.match("id=[0-9]+")[0].split("=")[1];
        for(var i=0; i<message .length; i++){
            mailBoxSet[offset][i+1]  = message [i].parentNode.getElementsByTagName("td")[0].innerHTML
        }
        offset++;
        xhr();
      }
    }
  }
}

function makeSaveData(){
  var saveData = getData();
  if(saveData){
    var numOfOld = saveData.length;
  }
  else{
    var numOfOld = 0;
    saveData = new Array();
  }
  for(i=0; i < mailBoxSet.length; i++){
    saveData[numOfOld + i] = mailBoxSet[i];
  }
  setData(saveData);
}

function startSave(){
  //まずは自ページ(p=1)から書簡本文リンクの抜き出し
  mailUriSet(document);
}

//このへんから実行
initGMWrapper();
var mailMenu = document.getElementById("statMenu");

//保存用
var saveButton = document.createElement("span");
saveButton.innerHTML = "<input id='saveMail' type='button' value='書簡保存' />";
saveButton.id = "saveButton";
saveButton.style.padding = "0 10px 0 0";
mailMenu.appendChild(saveButton);

//表示
var archiveButton = document.createElement("span");
archiveButton.innerHTML = "<input id='archiveButton' type='button' value='アーカイブ' />";
archiveButton.id = "archivebutton";
archiveButton.style.padding = "0 10px 0 0";
mailMenu.appendChild(archiveButton);

//削除
var delButton = document.createElement("span");
delButton.innerHTML = "<input id='delButton' type='button' value='削除' />";
delButton.id = "delButton";
delButton.style.padding = "0 10px 0 0";
mailMenu.appendChild(delButton);

document.getElementById("saveButton").addEventListener("click",
    function() {
        oldData = getData();
        nextUri = document.getElementsByClassName("last")[1].childNodes[0].href;
        mailBoxSet = []
        //本文へのURIを格納
        mailUri =  []
        offset = 0;
        startSave();
    },true);

document.getElementById("archiveButton").addEventListener("click",
  function() {
    var archiveInner = "";
    var archiveData = getData();
    if(archiveData){
      for(var i=0; i < archiveData.length; i++){
        archiveInner += 
          "<tr><td><input name='chk[]' value='" + archiveData[i][0] + "' type='checkbox'>" +
          "</td><td><a href='javascript:void(0)' name='mboxTitle' id='" + archiveData[i][0] + "'>" + archiveData[i][4] + "</a>" +
          "</td><td>" + archiveData[i][1] +
          "</td><td class='fs77'>" + archiveData[i][3] +
          "</td></tr>";
      }
      var tableHead = "<tbody><tr><th class='ttl w30'>選択</th><th class='ttl w300'>件名</th><th class='ttl'>送信者</th><th class='ttl w120'>送信時間</th></tr><tr></tr>";
      var tableFoot = "</tbody>";
      document.getElementsByClassName("commonTables")[0].innerHTML = tableHead + archiveInner + tableFoot;
      //本文表示イベントリスナーの設定
      var mboxTitle = document.getElementsByName("mboxTitle");
      for(var i=0; i<mboxTitle.length; i++){
        (function(i){
          mboxTitle[i].addEventListener("click",function(){
              for(var j=0; j<archiveData.length; j++){
                if(archiveData[j][0] == mboxTitle[i].id){
                  var mesinnerHTML = "<tbody><tr><th class='ttl w80'>送信者</th><td>" + archiveData[j][1] +
                                     "</td></tr><tr><th class='ttl w80'>宛先</th><td>" + archiveData[j][2] +
                                     "</td></tr><tr><th class='ttl w80'>日時</th><td>" + archiveData[j][3] +
                                     "</td></tr><tr><th class='ttl w80'>件名</th><td>" + archiveData[j][4] +
                                     "</tr><tr><th class='ttl w80'>本文</th><td>" +archiveData[j][5] + "</td></tr></tbody>";
                  document.getElementsByClassName("commonTables")[0].innerHTML = mesinnerHTML;
                  return;
                }
              }
          },false);
        })(i);
      }
    }
    else{
      alert("アーカイブデータはありません。")
    }
  },true
);

document.getElementById("delButton").addEventListener("click",
   function() {
      if(confirm('全てのデータを削除します。')){
        GM_setValue(location.host + "MailBox", "")
        alert("削除しました。")
      }
    },true);

//Google Chrome用GM_*系ラッパー関数
function initGMWrapper() {
  // @copyright   2009, James Campos
 // @license  cc-by-3.0; http://creativecommons.org/licenses/by/3.0/
 if ((typeof GM_getValue == 'undefined') || (GM_getValue('a', 'b') == undefined)) {
  GM_addStyle = function(css) {
   var style = document.createElement('style');
   style.textContent = css;
   document.getElementsByTagName('head')[0].appendChild(style);
  }

  GM_deleteValue = function(name) {
   localStorage.removeItem(LOCAL_STORAGE + "." + name);
  }

  GM_getValue = function(name, defaultValue) {
   var value = localStorage.getItem(LOCAL_STORAGE + "." + name);
   if (!value)
    return defaultValue;
   var type = value[0];
   value = value.substring(1);
   switch (type) {
    case 'b':
     return value == 'true';
    case 'n':
     return Number(value);
    default:
     return value;
   }
  }

  GM_log = function(message) {
   console.log(message);
  }

  GM_registerMenuCommand = function(name, funk) {
  //todo
  }

  GM_setValue = function(name, value) {
   value = (typeof value)[0] + value;
   localStorage.setItem(LOCAL_STORAGE + "." + name, value);
  }
  
  //by froo
  GM_listValues = function() {
   var res = new Array();
   for (var i = 0; i < localStorage.length; i++) {
    var key = localStorage.key(i);
    if (key.indexOf(LOCAL_STORAGE + ".", 0) == 0) {
     res.push(key.replace(/^.*?\./, ""));
    }
   }
   return res;
  }
 }
}