2010/12/20

VirtualBoxでホストOSからゲストOSに繋ぐ方法

VirutalBoxを使ってゲストOSにホストOSからアクセスする方法

1.VirtualBoxをSunのサイトからダウンロードして、普通に環境構築。
2.仮想マシンを作成し、ネットワークの設定でアダプタ1で”NAT”を、アダプタ2に”ホストオンリーアダプタ”を選択。

以上。NATの方で普通にゲストOSからインターネット接続できます。ゲストOSに繋ぎたい場合はホストオンリーアダプターのIPにホストOSからアクセスすればOK。初期のままであればDHCPによって192.168.56.101が割り当てられるはず。あとは固定にするも自由。簡単でしょ。

windowsのコマンドプロンプトのビープ音を消す

net stop beep
うるさい。以上。

2010/10/13

Twitter Botの登録(AccessTokenの発行)

http://dev.twitter.com/からログイン
http://dev.twitter.com/apps/からアプリケーション登録
Consumer keyとConsumer secretを取得

Access tokenとAccess secret取得には
http://gist.github.com/388067が便利なのでgitからclone
gem twitter oauthが必要

ruby make_token.rbをする
Consumer keyとConsumer secretを入力
表示されるURLにアクセスしてPINを入手
Access tokenとAccess secret取得

あとはこんな感じで
base = TwitterOAuth::Client.new({
  :consumer_key=>'',
  :consumer_secret=>'',
  :token=>'',
  :secret=>''
})

#base.update("もう"+Time.now.strftime("%H時%M分")+"か。。。。。" )
bit.lyを使うなら
def shorten(long_url)
  id = ''
  api_key = ''
  version = '2.0.1'
  query = "version=#{version}&longUrl=#{long_url}&login=#{id}&apiKey=#{api_key}"
  result = JSON.parse(Net::HTTP.get("api.bit.ly", "/shorten?#{query}"))
  result['results'].each_pair {|long_url, value|
 return value['shortUrl']
  }
end

非常に分かりやすい参考動画 http://www.youtube.com/watch?v=vTMuI4ln6Rc

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;
  }
 }
}

2010/07/16

ブラウザ三国志で武将を一覧表示するGreaseMonkeyスクリプトを修正

こちらにて公開されてたブラウザ三国志で武将を一覧表示するgreasemonkeyスクリプトが動かなくなっていたのでやっつけで修正しました。レベル取得の部分は面倒なので飛ばしました。Chromeでも動きます。

ファイルはこちら
// ==UserScript==
// @name           bura3deckext
// @version        0.0
// @description    
// @include        http://*.3gokushi.jp/card/deck.php*
// ==/UserScript


function getListElement()
{
 var list = document.getElementById("sol_list");
 if (!list) {
  list = document.createElement('table');
  list.id = "sol_list";

  var attrname = ["","コスト","ID","名前","HP","攻撃","知力","歩防",
   "槍防","弓防","騎防","速度","操作","cid"];
  var attr = ["","","cost","cardno","name1","status_hp","status_att",
   "status_int","status_wdef","status_sdef","status_bdef","status_rdef","status_speed","",""];
  var sortable = [0,1,1,0,0,1,1,1,1,1,1,1,0,0];

  row = document.createElement('tr');
  row.id = "sol_list_header"
  for (j=0;j<attrname.length;j++) {
   e = document.createElement('th');
   e.id="sol_list_" + attr[j];
   e.innerHTML = attrname[j];
   row.appendChild(e);
  }
  list.appendChild(row);


  var file = document.getElementById("card_uraomote");
  file.insertBefore(list,file.firstChild);

  sortableTable(list);
  for (j=0;j<attrname.length;j++) {
   if (sortable[j]==1) list.setSortable(j);
  }
 }
 return list;
}

function sortableTable(tbl)
{
 tbl.setSortable = function (col,mode)
 {
  var e = tbl.childNodes[0].childNodes[col];
  e.style.cursor = "pointer";
  e.orgClassName = e.className;
  e.addEventListener('click',function(s){return function(){tbl.sortTable(s);};}(col),false);
  e.addEventListener('mouseover',function(){this.className+=" hover";},false);
  e.addEventListener('mouseout' ,function(){this.className=this.orgClassName;},false);
 };

 tbl.sortTable = function sortTable(col)
 {
  rows= new Array();
  for (i=1;i<tbl.childNodes.length;i++) {rows[i-1]=tbl.childNodes[i];}
  rows.sort(function(a,b){
   ea=a.childNodes[col];
   eb=b.childNodes[col];
   if (!ea) return -1;
   if (!eb) return 1;
   return parseFloat(eb.firstChild.nodeValue)-parseFloat(ea.firstChild.nodeValue);
  });
  for (i=0;i<rows.length;i++) {tbl.appendChild(rows[i]);}
 }

 return tbl;
}


function addcards(d){
 var list = getListElement();
 var deckFile = d.getElementsByClassName("file")[0];
 var currentDeckFile = document.getElementById("deck_file");
 var cards = deckFile.getElementsByClassName("cardStatusDetail");
 if (!cards.length) {
  cards = deckFile.getElementsByClassName("cardColmn");
 }

 var attr = ["cost","cardno","name1","status_hp","status_att",
  "status_int","status_wdef","status_sdef","status_bdef","status_rdef","status_speed"];

 for (i=0;i<cards.length;i++) {
   row = document.createElement("tr");
   row.className="infile";

   t = cards[i].getElementsByClassName("soltype");
   e = document.createElement('td');
   e.innerHTML = t[0].innerHTML;
   row.appendChild(e);

   for (j=0;j<attr.length;j++) {
    t = cards[i].getElementsByClassName(attr[j]);
    if(!t) continue;
    e = document.createElement("td");
    e.className="cell_" + attr[j];
    e.innerHTML = t[0].innerHTML;
    row.appendChild(e);
   }

   t = cards[i].getElementsByClassName("cardWrapper2col");
   var cid = undefined;
   if (t.length) {
    cid = t[0].parentNode.id.split("_")[1];
   } else {
    t = cards[i].getElementsByClassName("cardDelete")[0].firstChild;
    if (t.getAttribute)
    if (re=t.getAttribute('onclick').match(/operationExecution\('[^']*', (\d+), '[^']*', \d+\)/)) {
     cid = re[1];
    }
   }

   e = document.createElement("td");

   t = cards[i].getElementsByClassName("aboutdeck");
   if(t.length && t[0].getAttribute('onclick')) {
    var link = document.createElement("a");
    link.href="javascript:((function(){"+t[0].getAttribute('onclick')+"})(),void(0));";
    link.appendChild(document.createTextNode("[セット]"));

    t=cards[i].getElementsByTagName("select");
    if (t.length) {
     t=t[0];
     e.appendChild(t);
     var v = document.createElement("input");
     v.name = t.name;
     v.value = t.value;
     currentDeckFile.appendChild(v);
     t.addEventListener('change',(function (a,b){return function(){a.value=b.value};})(v,t),false);
    }
    e.appendChild(link);
   } else {
    e.appendChild(document.createTextNode(" セット不可 "));
   }
   row.appendChild(e);

   e = document.createElement("td");
   if (cid) {
    e.innerHTML = "<a href='status_info.php?cid=" + cid + "'>"+cid+"</a>";
   } else {
    e.innerHTML = "-";
   }
   row.appendChild(e);

   list.appendChild(row);
 }
}

function addcards_deck(d){
 var list = getListElement();
 var cardListDeck = document.getElementById("cardListDeck");
 var cards = cardListDeck.getElementsByClassName("cardColmn");

 var attr = ["cost","cardno","name1","status_hp","status_att",
  "status_int","status_wdef","status_sdef","status_bdef","status_rdef","status_speed"];

 for (i=0;i<cards.length;i++) {
  row = document.createElement('tr');
  row.className="indeck";

  t = cards[i].getElementsByClassName("soltype");
  e = document.createElement('td');
  e.innerHTML = t[0].innerHTML;
  row.appendChild(e);

  for (j=0;j<attr.length;j++) {
   t = cards[i].getElementsByClassName(attr[j]);
   if(!t) continue;
   e = document.createElement('td');
   e.className="cell_" + attr[j];
   e.innerHTML = t[0].innerHTML;
   row.appendChild(e);
  }

  t = cards[i].getElementsByTagName("dd")[2];
  e = document.createElement('td');
  e.innerHTML = t.innerHTML;

  t = cards[i].getElementsByClassName("aboutdeck");
  if(t.length && t[0].getAttribute('onclick')) {
   var link = document.createElement("a");
   link.href="javascript:((function(){"+t[0].getAttribute('onclick')+"})(),void(0));";
   link.appendChild(document.createTextNode("[戻す]"));
   e.appendChild(link);
  }


  row.appendChild(e);
  e = document.createElement("td");
  e.innerHTML = "-";
  row.appendChild(e);

  list.appendChild(row);
 }
}

function createlist(){
 addcards_deck(document);
 addcards(document);
 //return;

 t = document.getElementsByClassName("pager")[0].getElementsByTagName("a");
 for(i=0;i<t.length;i++) {
  if (!t[i].innerHTML.match(/\d/)) continue;

  httpRequest = new XMLHttpRequest();
  httpRequest.open('GET', t[i].href, true);

  httpRequest.onreadystatechange = function (){
   var req = arguments[0];
   return function(){
       if(req.readyState == 4 && req.status == 200) {
      var d = document.createElement('html');
      d.innerHTML=req.responseText;
      addcards(d);
    }
   };
  }(httpRequest);
  httpRequest.send(null);

 }
}


( function(){
 
 GM_addStyle(""
  + "#sol_list{border-style:solid;border-spacing: 2px;border-collapse: separate;width:100%;}"
  + "#sol_list tr{height:22px;}"
  + "#sol_list tr td{height:1.2em;padding-left:4px;}"
  + "#sol_list tr th{background-color:#ccc;text-align:center;}"
  + "#sol_list tr th.hover{background-color:#ddd;}"
  + " tr.indeck{background-color:#ddf}"
  + ".cell_name2{font-size:0.5em;color:#888;}"
 );

  var link = document.createElement("a");
  link.addEventListener('click',createlist,false);
  link.href="javascript:void(0)";
  link.innerHTML = "武将一覧"
  var menu = document.getElementById("statMenu");
  menu.appendChild(document.createTextNode(" | "));
  menu.appendChild(link);
     
}) (); 




いつものように自己責任で。

2010/07/10

shellスクリプトパラメーターの渡し方

Shellスクリプトのパラメーターは$0,$1,$2…で呼び出せます。$0はファイル名、$1からパラメーターになります。
#!/bin/sh
echo $1
という形でtest.shファイルを作成した場合は次の通りです。
$ sh test.sh hello
hello

2010/07/08

GAEでjrubyをためしてみる

まずここからSDKのインストール。ファイルを解凍すると一式が入ってます。

gemにてgoogle-appengneをインストール
sudo gem install google-appengine
サンプルアプリを作って動かす
appcfg.rb generate_app hello
dev_appserver.rb hello
helloディレクトリ以下にアプリが作られます。
config.ruファイルにアプリケーションID等が記載されています。

ローカルサーバーで動かします。
dev_appserver.rb hello
本番環境(GAE)にアップロード
appcfg.rb update hello
メールアドレス、パスワードを聞かれるので入力。
完了。

2010/07/05

ブラウザ三国志GreasemonkeyスクリプトMap-StarをChrome対応させてみた(2)

以前に書いたスクリプトですが、7月5日のアップデートにて動かなくなりました。またろむのシ的メモ+αさんが即対応してくださったみたいですが、Chrome版が動かなかったのでGreasemonkey版をベースに修正してみました。

ファイルをダウンロード
// ==UserScript==
// @name           3gokushi-MapStar-chrome
// @namespace      3gokushi
// @description    ブラウザ三国志のマップに★の数を表示します。クローム対応版
// @include        http://*.3gokushi.jp/map.php*
// ==/UserScript==

var LOCAL_STORAGE = "bro3_mapstar";

//main
(function(){
//window.addEventListener("load",function() {
initGMWrapper();

    if (typeof GM_addStyle == "undefined") {
        GM_addStyle = function(css) {
            var style = document.createElement('style');
            style.textContent = css;
            document.getElementsByTagName('head')[0].appendChild(style);
        };
    }


    GM_VAL_PREFIX = "GMMS_";

    /**
     * 設定データ初期化
     */
    var dataTable = {
        w : new Array("#FFFFFF", "#000000", GM_getValue(GM_VAL_PREFIX + "w", true)), // white
        r : new Array("#FF0000", "#FFFFFF", GM_getValue(GM_VAL_PREFIX + "r", true)), // red
        g : new Array("#00FF00", "#000000", GM_getValue(GM_VAL_PREFIX + "g", true)), // green
        b : new Array("#0000FF", "#FFFFFF", GM_getValue(GM_VAL_PREFIX + "b", true)), // blue
        y : new Array("#FFFF00", "#000000", GM_getValue(GM_VAL_PREFIX + "y", true)), // yellow
        p : new Array("#FF00FF", "#FFFFFF", GM_getValue(GM_VAL_PREFIX + "p", true)), // pink
        bk : new Array("#000000", "#FFFFFF", GM_getValue(GM_VAL_PREFIX + "bk", true)), // black
        bg : new Array("#0066FF", "#FFFFFF", GM_getValue(GM_VAL_PREFIX + "bg", true)), // sky blue
        o : new Array("#FFA500", "#000000", GM_getValue(GM_VAL_PREFIX + "o", true)) // orange
    };

    /**
     * styleの追加
     */
    GM_addStyle([
                 ".mapStar_outer{ width:10px;height:10px;margin:2px 4px 2px 0px;float:left;border:1px solid #000000;cursor:pointer; }",
                 ".mapStar_on{ filter:alpha(opacity=100);opacity:1; }",
                 ".mapStar_off{ filter:alpha(opacity=30);opacity:0.3;border:1px solid #999999; }",
                 ".mapStar_box{ filter:alpha(opacity=60);opacity:0.6; position: absolute; width: 8px; height: 8px; padding: 0px 0px 2px 3px; font-size: 8px;}",
               ].join("\n"));


    /**
     * 設定on/off処理関数
     */
    function onSettingClick() {
        var key = this.getAttribute("type");
        var gmv = GM_getValue(GM_VAL_PREFIX + key, true);
        GM_setValue(GM_VAL_PREFIX + key, !gmv);

        var clsName;
        var displayVal;
        if (gmv) {
            clsName = "mapStar_off";
            visibleVal = "hidden";
        } else {
            clsName = "mapStar_on";
            visibleVal = "visible";
        }

        var XPath = '//div[@class="mapStar_margin mapStar_box mapStar_' + key + '"]';
        var list = document.evaluate(XPath, document, null,
                XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);

        for ( var i = 0; i < list.snapshotLength; i++) {
            list.snapshotItem(i).style.visibility = visibleVal;
        }

        XPath = '//div[@type="' + key + '"]';
        document.evaluate(XPath, document, null,
                XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue.className = "mapStar_outer " + clsName;

    }

    /**
     * 設定ボックスの挿入
     */
    var insset = document.getElementById('mapboxInner');

    if (insset != null) {
        var set = document.createElement('div');
            set.style.backgroundColor = "#FFFFFF";
            set.setAttribute("id","mapStarBox");
            insset.appendChild(set);
        var cssText = "";
        for ( var key in dataTable) {
            var onoff = dataTable[key][2] ? "mapStar_on" : "mapStar_off";
            var setItem = document.createElement('div');
                setItem.className = "mapStar_outer " + onoff;
                setItem.style.backgroundColor = dataTable[key][0];

                set.appendChild(setItem);
                setItem.setAttribute("type", key);
                setItem.addEventListener("click", onSettingClick,false);
            cssText += ".mapStar_"+key+"{background-color:"+dataTable[key][0]+"; color:"+dataTable[key][1]+"} ";
        }
    }
    
    GM_addStyle(cssText);

    /**
     * MAPサイズ取得
     */
    var mapSize = document.getElementById('rollover').style.zIndex - 1;

    /**
     * 地図データの取得
     */
    var mapMap = new Array(mapSize + 1);
    var mapAreaDoc = document.getElementById('mapsAll');
    var imgRegCmp = new RegExp(/img\/panel\/[^_]*_([^_]*)_/);

    var imgMap = mapAreaDoc.getElementsByTagName('img');
    for ( var i = 0; i < imgMap.length; i++) {
        var clstxt = imgMap[i].className;
        if ((clstxt != null) && (clstxt.search(/mapAll(\d+)/) != -1)) {
            var mapIndex = RegExp.$1 - 0;
            var imgSrc = imgMap[i].src;
            if (imgRegCmp.exec(imgSrc)) {
                var imgRegCmp = new RegExp(/img\/panel\/[^_]*_([^_]*)_/);
                mapMap[mapIndex] = RegExp.$1;
            } else if (0 <= imgSrc.indexOf("blanc")) {
                mapMap[mapIndex] = "wall";
            }
        }
    }

    /**
     * 地図へ埋め込み
     *
     */
    var areas = mapAreaDoc.getElementsByTagName('area');

    // mapSizeからzIndexの値とmarginSizeを決定する
    var marginSize = "32px 0px 0px 23px";
    var zIndex = mapSize + 2;
    if (200 < zIndex && zIndex < 400) {
        marginSize = "25px 0px 0px 17px";
    } else if (400 <= zIndex) {
        marginSize = "16px 0px 0px 12px";
    }
    
    cssText = ".mapStar_margin{ margin:"+marginSize+"; z-index:"+zIndex+"; }";
    GM_addStyle(cssText);

    var regCmp = new RegExp(
            /(\'[^\']*\'[^\']*){5}\'(\u2605+)\'.*overOperation\(\'.*\'.*\'(.*)\'.*\'(.*)\'/);
    var j = 0;

    var alpha = 6;
    for ( var i = 1; i < mapMap.length; i++) {
        if (mapMap[i] != "undefined" && mapMap[i] == "wall") {
            continue;
        }

        if (areas[j] == "undefined") {
            break;
        }
        var mo = areas[j++].getAttribute('onmouseover');
        if (mo.search(/\u2605/) < 0) {
            continue;
        }

        if (regCmp.test(mo)) {
            var dataKey = (typeof mapMap[i] == "undefined") ? "w" : mapMap[i];

            var div = document.createElement('div');
            if (!dataTable[dataKey][2]) {
                div.style.visibility = "hidden";
            }

            div.className = "mapStar_margin mapStar_box mapStar_" + dataKey;
            div.setAttribute("id","mapStar_"+i);
            div.style.left = RegExp.$3;
            div.style.top = RegExp.$4;
            div.innerHTML = RegExp.$2.length;

            if (RegExp.$2.length >= 5) {
                div.style.border = "1px solid";
            }
            if (RegExp.$2.length >= 3) {
                div.style.fontWeight = "bold";
            }

            mapAreaDoc.appendChild(div);
        }
    }

//},false);
})();

//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;
  }
 }
}

rubyでepubファイルを作ることができるRepubを試してみる

rubyでepubファイルを作ることができるrepubを試してみた。

まずインストール
sudo gem install repub
libxm2が古いと言われたので更新してnokogiriを再インストール
sudo port install libxml2
sudo gem install nokogiri 
** ERROR: : helper not found.のエラー
wgetが入ってなかったのでインストール
sudo port install wget
コマンド実行でOK(エンコードをUTF-8を設定)
repub -e 'UTF-8' サイトURL
ただ色々とサポートしてない部分があるようでかなりのサイトで利用できないみたい。Tumblrはepub化できました。
補足 epubリーダーとしてはFirefox Add-onのEPUBReaderがいい感じ。

2010/06/30

ブラウザ三国志GreasemonkeyスクリプトMap-StarをChrome対応させてみた(1)

http://d.hatena.ne.jp/romer/20100201で公開されているMap-StarをChrome対応させてみました。以下のコピーして3gokushi-mapstar-chrome.user.jsなどのファイル名を記載して保存します。そのファイルをブラウザで読み込めばインストールされると思います。

追記:配下同盟対応版はこちら
// ==UserScript==
// @name  3gokushi-MapStar-Chrome
// @namespace  3gokushi
// @description ブラウザ三国志のマップに★の数を表示します。Chrome対応版。
// @include http://*.3gokushi.jp/map.php*
// ==/UserScript==

var LOCAL_STORAGE = "bro3_mapstar";

//main
(function(){
//window.addEventListener("load",function() {
initGMWrapper();


 GM_VAL_PREFIX = "GMMS_";

 /**
  * 設定データ初期化
  */
 var dataTable = new Array();
 dataTable["w"] = new Array("#FFFFFF", "#000000", GM_getValue(GM_VAL_PREFIX
   + "w", true)); // white
 dataTable["r"] = new Array("#FF0000", "#FFFFFF", GM_getValue(GM_VAL_PREFIX
   + "r", true)); // red
 dataTable["g"] = new Array("#00FF00", "#000000", GM_getValue(GM_VAL_PREFIX
   + "g", true)); // green
 dataTable["b"] = new Array("#0000FF", "#FFFFFF", GM_getValue(GM_VAL_PREFIX
   + "b", true)); // blue
 dataTable["y"] = new Array("#FFFF00", "#000000", GM_getValue(GM_VAL_PREFIX
   + "y", true)); // yellow
 dataTable["p"] = new Array("#FF00FF", "#FFFFFF", GM_getValue(GM_VAL_PREFIX
   + "p", true)); // pink
 dataTable["bk"] = new Array("#000000", "#FFFFFF", GM_getValue(GM_VAL_PREFIX
   + "bk", true)); // black
 dataTable["bg"] = new Array("#0066FF", "#FFFFFF", GM_getValue(GM_VAL_PREFIX
   + "bg", true)); // skyblue

 /**
  * 設定on/off処理関数
  */
 function onSettingClick() {
  var key = this.getAttribute("type");
  var gmv = GM_getValue(GM_VAL_PREFIX + key, true);
  GM_setValue(GM_VAL_PREFIX + key, !gmv);

  var clsName;
  var displayVal;
  if (gmv) {
   clsName = "off";
   visibleVal = "hidden";
  } else {
   clsName = "on";
   visibleVal = "visible";
  }

  var XPath = '//div[@class="mapStar_' + key + '"]';
  var list = document.evaluate(XPath, document, null,
    XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);

  for ( var i = 0; i < list.snapshotLength; i++) {
   list.snapshotItem(i).style.visibility = visibleVal;
  }

  XPath = '//div[@type="' + key + '"]';
  document.evaluate(XPath, document, null,
    XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue.firstChild.className = "outer "
    + clsName;

 }

 /**
  * 設定ボックスの挿入
  */
 var insset = document.getElementById('mapboxInner');

 if (insset != null) {

  var html = "\
     <style>\
     .outer{ width:10px;height:10px;margin:2px 4px 2px 0px;float:left;border:1px solid #000000;cursor:pointer; }\
     .on{ filter:alpha(opacity=100);-moz-opacity:1;opacity:1; }\
     .off{ filter:alpha(opacity=30);-moz-opacity:0.3;opacity:0.3;border:1px solid #999999; }\
     </style>\
     <div style='background-color:#ffffff;'> </div>";

  var set = document.createElement('div');
  set.innerHTML = html;
  insset.appendChild(set);

  for ( var key in dataTable) {
   var onoff = dataTable[key][2] ? "on" : "off";
   html = '<div class="outer ' + onoff + '" style="background-color:'
     + dataTable[key][0] + ' ;"> </div>';

   var setItem = document.createElement('div');
   setItem.innerHTML = html;
   set.appendChild(setItem);

   setItem.setAttribute("type", key);
   setItem.addEventListener("click", onSettingClick, false);
  }
 }

 /**
  * MAPサイズ取得
  */
 var mapSize = document.getElementById('rollover').style.zIndex - 1;

 /**
  * 地図データの取得
  */
 var mapMap = new Array(mapSize + 1);
 var mapAreaDoc = document.getElementById('mapsAll');
 var imgRegCmp = new RegExp(/img\/panel\/[^_]*_([^_]*)_/);

 var imgMap = mapAreaDoc.getElementsByTagName('img');
 for ( var i = 0; i < imgMap.length; i++) {
  var clstxt = imgMap[i].className;
  if ((clstxt != null) && (clstxt.search(/mapAll(\d+)/) != -1)) {
   var mapIndex = RegExp.$1 - 0;
   var imgSrc = imgMap[i].src;
   if (imgRegCmp.exec(imgSrc)) {
   var imgRegCmp = new RegExp(/img\/panel\/[^_]*_([^_]*)_/);
    mapMap[mapIndex] = RegExp.$1;
   } else if (0 <= imgSrc.indexOf("blanc")) {
    mapMap[mapIndex] = "wall";
   }
  }
 }

 /**
  * 地図へ埋め込み
  *
  */
 var areas = mapAreaDoc.getElementsByTagName('area');

 // mapSizeからzIndexの値とmarginSizeを決定する
 var marginSize = "32px 0px 0px 23px";
 var zIndex = mapSize + 2;
 if (200 < zIndex && zIndex < 400) {
  marginSize = "25px 0px 0px 17px";
 } else if (400 <= zIndex) {
  marginSize = "16px 0px 0px 12px";
 }

 var regCmp = new RegExp(
   /(\'[^\']*\'[^\']*){5}\'(\u2605+)\'.*overOperation\(\'.*\'.*\'(.*)\'.*\'(.*)\'/);
 var j = 0;

 var alpha = 6;
 for ( var i = 1; i < mapMap.length; i++) {
  if (mapMap[i] != undefined && mapMap[i] == "wall") {
   continue;
  }

  if (areas[j] == undefined) {
   break;
  }
  var mo = areas[j++].getAttribute('onmouseover');
  if (mo.search(/\u2605/) < 0) {
   continue;
  }

  if (regCmp.test(mo)) {
   var dataKey = (mapMap[i] == undefined) ? "w" : mapMap[i];
   var bg = dataTable[dataKey][0];
   var cl = dataTable[dataKey][1];

   if ( RegExp.$2.length < 1 ) {
    continue;
   }

   var div = document.createElement('div');
   if (!dataTable[dataKey][2]) {
    div.style.visibility = "hidden";
   }

   if (bg != null) {
    div.style.backgroundColor = bg;
   }

   if (cl != null) {
    div.style.color = cl;
   }

   div.className = "mapStar_" + dataKey;
   div.style.left = RegExp.$3;
   div.style.top = RegExp.$4;
   div.style.position = "absolute";
   div.style.width = "8px";
   div.style.height = "8px";
   div.style.margin = marginSize;
   div.style.padding = "0px 0px 2px 3px";
   div.style.zIndex = zIndex;
   div.style.fontSize = "8px";
   div.innerHTML = RegExp.$2.length;

   if (RegExp.$2.length >= 6) {
    div.style.border = "1px solid";
   }
   if (RegExp.$2.length >= 4) {
    div.style.fontWeight = "bold";
   }

   div.style.filter = 'alpha(opacity=' + (alpha * 10) + ')';
   div.style.MozOpacity = alpha / 10;
   div.style.opacity = alpha / 10;

   mapAreaDoc.appendChild(div);
  }
 }
//},false);
})();

//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;
  }
 }
}
何か問題点があれば連絡ください。ファイル保存はUTF8形式でお願いします。
もちろん自己責任でお願いします。

Javascriptで要素を取得する方法

JavaScriptで要素を取得する方法のメモ


・DOM
idから検索 document.getElementById()
タグから検索 document.getElementsByTagName()
CSSのクラスから検索 document.getElementsByClassName() - IE等では使えない

取得した中身は配列として扱う
ただし入り組んだデータを取得するのは結構めんどい

・XPath
idから検索 document.evaluate('id("...")')
タグから検索 document.evaluate('.//div')
CSSのクラスから検索 document.evaluate('.//div[@class="..."]')
var xpathResult = document.evaluate(xpathExpression, contextNode, 
            namespaceResolver, resultType, result );
引数は下記の5つ
xpathExpression:XPath
contextNode:(HTMLなら基本documentで。)
namespaceResolver:基本null
resultType:戻り値の型の指定
result:基本null

resultType(戻り値の型)指定は次の通り
0 ANY_TYPE
1 NUMBER_TYPE - 倍精度浮動小数点数 (double)
2 STRING_TYPE - 文字列
3 BOOLEAN_TYPE - 真偽値
4 UNORDERED_NODE_ITERATOR_TYPE
5 ORDERED_NODE_ITERATOR_TYPE
6 UNORDERED_NODE_SNAPSHOT_TYPE
7 ORDERED_NODE_SNAPSHOT_TYPE
8 ANY_UNORDERED_NODE_TYPE
9 FIRST_ORDERED_NODE_TYPE


6~7で取得した中身にはsnapshotプロパティでアクセスする


無名関数の基礎

Javascriptの基本として関数を宣言して実行するのが通常のフローですが無名関数を使いその場で実行することもできます。

(function(){
処理を記載
})();


()の中に無名関数を入れて、その後();で実行します。関数に変数を使わないし、ライブラリの重複、定義と実行を分けないで良いので簡単な処理を書くときに便利です。

2010/06/29

TomblooでYoutubeにポストエラー

Tomblooは数クリックでTumblrなどにポストできるアドオンです。非常に便利なのですが、Youtubeの画面仕様の変更でエラーが出るようになりました。対応方法がhttp://d.hatena.ne.jp/tondol/20100402/1270206802
にありましたので対応したところ無事動きました。

追記としては該当ファイルの場所ですが下記の場所ににあります。
macでは
/Users/ユーザー名/Library/Application Support/Firefox/Profiles/XXXXX.default/extensions/tombloo@brasil.to/chrome/content/library

winでは
C:\Documents and Settings\ユーザー名\Application Data\Mozilla\Firefox\Profiles\XXXX.default\extensions\tombloo@brasil.to\chrome\content\library

書き換え後はブラウザーを再起動して下さい。

2010/06/26

CakePHPの初期設定

CakePHPは便利なフレームワークです。初期設定のデータベース設定の時に凡ミスをしていたのでメモしておきます。データベースの設定はデータベースを作成後、app/config/database.phpのファイルを設定すれば良いです。これをちゃんとやっているのにうまく接続できない場合はPHPがデータベース(MySQLなど)のdriverを持っているか確認しましょう。持ってなかった場合はインストール後、Apacheを再起動してください。

MySQLのユーザー管理コマンドまとめ

MySQLのユーザー管理コマンドをまとめておきます。

ユーザーの作成
GRANT 権限 ON *.* TO ユーザ名 IDENTIFIED BY 'パスワード' WITH GRANT OPTION;
ユーザーの確認
SELECT host,user FROM mysql.user;
パスワードの変更
SET PASSWORD FOR ユーザ名@"ホスト名"=password('変更後のパスワード');
ユーザーの削除
DELETE FROM mysql.user WHERE user='ユーザ名';

2010/06/22

MySQLのインストールと自動起動

MySQLのインストールと自動起動は基本ですが念のため。
yum -y install mysql-server
/etc/init.d/mysqld start
chkconfig --add mysqld
chkconfig mysqld on
chkconfig --list mysqld //確認

ついでにデータベースの作成と削除
CREATE database データベース名; //作成 
DROP databae データベース名;  //削除

テーブルの作成(CakePHPのチュートリアルから)
CREATE TABLE posts (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(50),
body TEXT,
created DATETIME DEFAULT NULL,
modified DATETIME DEFAULT NULL
);
テストデータ投入(CakePHPのチュートリアルから)
INSERT INTO posts (title,body,created)
VALUES ('タイトル', 'これは、記事の本文です。', NOW());
INSERT INTO posts (title,body,created)
VALUES ('またタイトル', 'そこに本文が続きます。', NOW());
INSERT INTO posts (title,body,created)
VALUES ('タイトルの逆襲', 'こりゃ本当に面白そう!うそ。', NOW());

2010/06/21

Dropboxを用いた開発環境

Dropboxを用いて開発環境を作ると便利です。ソースをそのまま突っ込んでもいいですがSubversionなんかを使っている場合はレポジトリをDropboxにしてしまう方法もあります。注意する点としては共有するマシン間でマシン名が異なるとディレクトリが異なるのでDropboxの初期設定から「Dropbox Folder Location」で名前を設定するとよいでしょう。レポジトリのURLはfile:///Users/設定名/Dropbox/SVNRepository みたいな感じになります。

2010/06/17

シェルスクリプトの書き方

サーバーメンテナンスなど定期的な処理は自動化しておくとミスも少なくなり便利です。基本的なシェルスクリプトの書き方をまとめておきます。


文章の頭には実行するための決まり文句を書きます。これは実行するファイルを指示しています。rubyやpythonであれば#!/bin/ruby、#!/bin/pythonなどと書きます。また指定位置は実行環境によって異なります。
#!/bin/sh 

次にファイルに実行属性をつけます。
$ chmod u+x ファイル名.sh 

あとはファイル内に命令を書き込むだけです。$で始まる文字列は変数として扱われます。条件分岐はif文、forループ、while文など使えます。
#!/bin/sh
TEST_TXT=/users/user/test.txt
TEST01=test01

echo replace test01.txt
cp ${TEST_TXT}.${TEST01} $TEST.TXT

あとは定期的に実行させるにはcrontabなどに登録しておけば良いです。
    下記コマンドにてcrontabの登録を行います。
    $ crontab -e
    
    設定項目は左から「分」「時」「日」「月」「曜日」となっています。
    下記の場合であれば毎月10日6時45分に実行されます。
    45 6 10 * * ファイル名
    
    「/」の後に実行したい間隔の値を設定することで、その間隔を設定可能。
    「-」で指定することで、実行範囲を設定可能。
    「,」で区切ることで、複数の値を設定可能。

    2010/06/16

    Mac Portのメンテナンスについて

    mac を使う時にパッケージ管理のためMac Portsを導入すると便利です。また定期的にメンテナンスをしましょう。メンテナンスコードは下記の通り。なおGemを使っている場合はそちらもついでに更新すると良いでしょう。


    $ sudo port -d selfupdate 
    $ sudo port upgrade installed 
    $ sudo gem update --system
    $ sudo gem update 


    追記:こっちの方が良いっぽい。
    $ sudo port -d selfupdate
    $ sudo port -v selfupdate //portの更新
    $ sudo port upgrade outdated //古いバージョンをアップデート
    $ sudo gem update --system
    $ sudo gem update
    

    2010/06/15

    Wordpressのインストールの仕方

    WordpressはCMSとして大変便利でサイト制作によく使われます。インストールの仕方も極めて簡単です。
    1. zip ファイルを空のディレクトリに展開します。
    2. WordPad などのテキストエディタで wp-config-sample.php を開き、データベースの接続情報 (データベース名、ユーザー名、パスワード、およびホスト名) を記入します。
    3. このファイルの名前を wp-config.php として保存します。
    4. すべてのファイルをアップロードします。
    5. /wp-admin/install.php をブラウザで開きます。これによりあなたのブログに必要なデータベースのテーブルが設定されるはずです。もしエラーが発生するようなら wp-config.php ファイルをもう一度確認し、再度このインストーラを試してください。それでも失敗する場合は、できるだけ多くのデータを集めてサポートフォーラムに行ってください。
    6. パスワードが自動的に生成され表示されます。これをメモしてください。
    7. その後、このインストールスクリプトはあなたをログインページに案内するはずです。ユーザ名 admin と、インストール中に生成されてメモしたパスワードを使ってログインしてください。このパスワードはログイン後、管理画面の「ユーザー」をクリックして変 更することができます。
    公式サイトより

    事前準備としてデータベースを用意しておく必要があります。データベースの作成の仕方は次の通り。


    mysql> CREATE DATABASE databasename;
    Query OK, 1 row affected (0.00 sec)
     
    mysql> GRANT ALL PRIVILEGES ON databasename.* TO "wordpressusername"@"hostname"
        -> IDENTIFIED BY "password";
    Query OK, 0 rows affected (0.00 sec)
      
    mysql> FLUSH PRIVILEGES;
    Query OK, 0 rows affected (0.01 sec)
    
    mysql> EXIT
    Bye

    簡単ですね。なれれば10分くらいでインストールできます。

    2010/06/14

    iPad,iPhoneと母艦(mac)間のファイル連携

    JailBreak後の対応限定ですが母艦(mac)とiPadのファイル連携についてまとめておきます。方法は3つあるそうです。
    • Netatalkインストール後、共有ファイル設定
    • OpenSSHインストール後、SCP などでファイルをアップ
    • DiskAidと言うツールを使う(シェアウェア)
    二つ目の方法を使うときのmac用のSCPソフトはCyberduckと言うものがあります。

    2010/06/13

    GAEで静的ファイルを表示させる方法

    静的ファイルhtdocsフォルダ以下に配置します。
    app.yamlに次のように追記します。
    - url: (.*)/
    static_files: htdocs\1/index.html
    upload: htdocs(.*)/index.html
    
    - url: /(.*)
    static_files: htdocs/\1
    upload: htdocs/(.*)
    
    
    ちなみに静的ファイルのみでも運用できます。

    2010/06/11

    MySQLのログファイルの管理について

    GAEを使っているとKey-Value Storeの方に慣れてしまいますが、MySQLのログを管理する機会があったのでまとめておきます。


    mysql-bin.xxxxxx
      レプリケーション設定のために保存される「mysql-bin.xxxxxx」ファイルが有りますが、この末尾の番号は世代管理になります。よってレプリケーションに必要な物は最新(番号が大きいもの)があれば問題ありません。


      ホスト名-slow.log
        実行に long_query_time 秒より長くかかった SQL コマンドがすべてログファイルに書き込まれるファイルです。パフォーマンスチューニングを行うときに参考になります。

        また、ログファイルは放っておくとどんどん肥大化していくので適宜ログローテートをした方が良いです。強制的にログファイルの入れ替えをするには下記のコマンドすれば良いです。


        mysql > FLUSH LOGS;
        


        ログデータは放置しておくと意外とストレージ容量を圧迫しますので、定期的に確認した方が良いでしょう。




        海外用のiPhoneアプリやiPadアプリについて

         iPhoneやiPadアプリを見ていると日本国内では販売(無料を含む)していないのに、アメリカ合衆国では販売している物がチラホラあります。iTumesStoreで販売してる映画なんかもアメリカ合衆国向けだけです。

         恐らくこれらは映画会社やアプリ制作会社などの権利者とApple社との交渉でアメリカ国内のみでの販売しか認められていないからでしょう。

        その為、ソフトを入手するにはiTunes USのアカウントが必要になります。ただし、iTuneUSのアカウントを作る上で、居住地がアメリカにないと規約違反になるそうです。一応、アカウント作成方法についてまとめておきます。
        1. iTunes store右下の国旗のマークをクリックしアメリカ合衆国に切り替える
        2. iTunes USのアカウントを作成しログインする
        3. 決済情報を求められるので海外のギフトカードなどで対応する。
        住所等は実在の住所かかなり厳しくチェックされます。

        最初にも書きました通り居住地以外の登録は規約違反になるらしいので自己責任でお願いします。権利者交渉が上手く進み、こうしたこと全てが必要なくなれば良いのに。

        2010/06/09

        GAEの最初の手順

        Google App Engine(GAE)は無料で利用できるクラウドサービスです。手軽に利用できるので小規模開発にもってこいです。GAEを利用するにはアカウントを作る必要があります。まずはhttps://appengine.google.com/にてログインしてください。Googleアカウントがあればそのまま入れます。


        ログイン後「Create an Application」をクリックするとアプリケーションを作れるのですが、初回のみ携帯のメールにて認証が必要になります。携帯のメールアドレスを入力すると、認証コードが送られてくるので、それを登録すればOKです。

        違うGoogleアカウントにログインしながらこれらの作業をやると、上手くいかない場合があるので、違うウィンドウは閉じて行う方が安全です。

        アプリケーションを作成するときのIDを入れますので、アップロードするときにはこちらのIDを入力して下さい。

        2010/06/08

        Vi,Emacsの基本操作まとめ

        開発をしているとViを触る機会が多いかと思いますがついついコマンドを忘れがちなのでまとめておきます。ここに記載してるのは本当に基礎的な部分のみです。

        vi ファイル名新規作成
        i入力モードへ
        ESCビジュアルモードへ
        :w (ファイル名)保存(ファイル名が無い場合は上書き保存)
        :q終了
        h,j,k,l左、下、上、右 (キーが一列に並んでいる)
        /文字列検索 (nで次へ)
        yw単語のコピー
        Ctrl-vビジュアルモード(矩形選択)
        pペースト
        u元に戻す
        Ctrl-rやり直す

        ついでEmacsについてもまとめておきます。

        Ctrl-x Ctrl-f新規作成
        Ctrl-x Ctrl-s保存
        Ctrl-x Ctrl-w名前を付けて保存
        Ctrl-f
        Ctrl-b
        Ctrl-n
        Ctrl-p
        Ctrl-x u元の戻す
        Ctrl-u 1やり直す
        Ctrl-vコピー
        Ctrl-y張り付け
        Ctrl-@範囲の開始位置を指定
        ESC-w選択範囲をバッッファにコピー

        普段、使わないと忘れがちなのでテキスト編集する時こまめに使うと良いのではないでしょうか。

        Windows環境のファイルバックアップ用バッチ

        Window XPでファイルサーバーを用意するのはWindows XP Professionalでも同時接続が最大10人までなのであまりオススメはできません。とはいえ、皆がWindowsを使っている環境で手軽に(一時的に)ファイル共有するには便利です。

        もしくは、Wiki等を立ち上げたりするかも知れません。そんな時忘れてはいけないのがデータのバックアップです。そんな時はバッチファイルを作り、タスクスケジューラーにそのバッチを実行させると簡単です。バッチファイルの書き方は次のような感じで。
        XCOPY /Y コピー元のディレクトリ コピー先のディレクトリ /e /s /f
        
        ファイル名はbackup.batか何かで保存すればOKです。複数に分けて保存したい場合は下にどんどん追記すればOKです。

        2010/06/07

        iPadのWifiネットワーク設定について

        iPadのwifiネットワーク設定についていろいろトラブルが報告されています。そもそもwifiネットワークが弱いとも言われていますが、wifiネットワークに接続できても全くインターネットに接続できないケースがあります。

        この現象が起こるケースとして2つのケースがありました。

        一つ目はスリープモードから復帰した時にインターネットに繋がらなるケースです。このケースでは面倒ですが、設定画面から一旦、wifiをOFFにしてすぐONに入れ直せば復帰します。おそらくこれはiPad側の問題で、将来のアップデートで対応されるかと思います。(予想ですが)

        二つ目はルーターのネットワークが192.168で始まっていない場合(169.254.0.2など)です。古いルーターなどで見られるケースです。通常のネットワーク機器であれば、他のネットワークアドレスでもインターネットにつながるのですがiPadはダメみたいです。同じケースとしてWiiも同じ問題をかかえています。

        対応策としてはルーターのファームウェアを最新のものに更新したあと、DHCPの設定を確認し、192.168で始まるように(例えば192.168.0.2〜など)再設定してください。同一ネットワークにある機器全てに影響が出るので、若干面倒ですが、今後の運用も考えて一般的な192.168の設定しておく方が良いのではないでしょうか。

        2010/06/06

        バージョン管理について

        システム開発では各バージョンを管理するためバージョン管理のシステムを使うことがほとんどです。このバージョン管理はソースコードだけでなく、画像ファイルやデータなども管理できるので大変便利です。具体的には、何かのトラブルで前のバージョンに戻したいときや。バグの修正ログを確認する際に非常に有効です。

        さてバージョン管理用のソフトですが、CVSからSubversion、そしてGitという風なトレンドになっています。私個人はSubversionを今でも使用していますが、Gitもたまに使います。いまはGitがアツイようです。SubversionからGitへの以降も下記で簡単にできます。

        
        $ git svn clone -T trunk -b branches -t tags <subversion リポジトリ="">
        
        Gitの有利な点は分散管理を取っている点で、Subversionでは毎回変更をサーバー側に登録(コミット)しなくてはいけません。そのコミット作業がファイル作業が膨大になってくると意外と時間がかかります。Gitではそのコミット作業をローカル環境で行えます。小さい修正をローカルで行い、ある程度の段階で中央に反映するといったことができるのが利点です。

        まあいずれにせよバージョン管理システムを管理する人が必要ですので、その管理のシステムは一本化した方が良いと思われます。今からバージョン管理を仕様とする人はGitにしておくほうがオススメです。

        ただプロジェクトを複数持っている場合は管理人も複数いる場合が多く、一本化するのは中々難しいです。私はまだSubversionを使っていますので、その場合のケースをまとめておきます。

        Subversionのサーバーを立てる方法はいろいろありますが、linuxの場合はyumコマンドでインストール(yum install httpd subversion subversion-server)、Windowsの場合はバグトラッキングシステムも含めたtrac lightningをインストールするのが簡単です。

        私の場合プロジェクトメンバーが各所にいる場合はLinuxでサーバーを立ち上げApacheのDAVモジュールも利用します。サーバーには認証キーを用い、ssh経由で接続($svn co svn+ssh://なんたらかんた)してもらいます。

        ローカル環境で開発を行う場合は、わざわざバグ管理も面倒ですのでtrac lightningを使用します。tracを使うメリットとしては、GUIが優れていますのでシステム屋でなくともバグ登録ができ、wikiシステムも備えていますのでナレッジの一本化ができます。(Gitに移行する際に諸刃ともなりますが)

        クライアント側ではターミナルを使っての管理か、Eclipseのプラグイン管理か、 tortoiseSVNでの管理かは好みによって使い分ければよいかと思います。

        あと忘れてはいけないのがコードバックアップです。ちゃんと定期的に異なるハードディスクにコピーできるようにしておきましょう。バッチファイルか何かで処理してしまうのが手っ取り早いでしょう。trac lightningであればbackup.batがついてきますのでこれを利用すればOKです。

        2010/06/05

        時間がかかる処理などでターミナルが切断されてしまうとき

        恐らくサーバー機器にはリモート環境で接続してメンテナンスを行う人が多いかと思います。ただしターミナル接続は長時間放置すると自動的に切断されるような設定になっています。定期的にパケットを送り合い切断されないようにする方法もありますが、ここでは簡単なコマンドを紹介します。
        nohup コマンド内容
        
        行いたい処理の最初にnohupをつけるだけでログアウト後も実行をし続けてくれます。
        nohup コマンド内容 &
        
        最後にアンパサンド(&)をつけるとコマンド内容で指定した処理がバックグラウンドで実行され、割り込みも無視されます 。

        シェルか何かで大規模な処書き換え理を行う時など時間がかかる処理を行うときは大変便利です。よく使う機能では無いですが覚えておくと便利でしょう。 

        2010/06/04

        CSSの指定の方法

        CSSの書き方は対応するブロックに対してデザインを指定するだけですので、基本的にはそんなに難しくはありません。求められるのデザインセンスです。ただ初心者にはわかりにくいクラスとIDについてまとめておきます。

        CSSのはクラスとIDの2つの方法で記述されます。

        HTML側で <div id="header">〜</div>
        となっている場合はIDでCSSでは#header { color: #FFF; }のように指定します。

        HTML側で<p class="note">〜</p>
        となっている場合はクラスでCSSではp.note { color: #000; }のように指定します。

        IDとクラスの違いは、IDは一度しか出てこないもの、クラスは何度も出てくるものと理解しておけば良いでしょう。綺麗にCSSを記述することでファイルサイズも小さくなり、微差ですがページ読み込みも早くなります。最近はSEOのもページ読み込みスピードは多少影響するので綺麗記述しておくことに越したことはないでしょう。

        2010/06/03

        仮想環境構築

        ウェブサービスを開発を行う上で必要なものはサーバーが必要ですが,状況に応じてローカル環境にて行う必要があります。そんな時、仮想化環境を構築するのが便利です。私の場合だとWindowsは必要ありませんが、メインマシンはMacですので仮想環境内にCentOSを動かしています。Linuxが必要なときは仮想環境を使います。

        さて仮想環境ですが仮想環境を作るには主に3つのソフトがあります。

        Virtual PC(Microsoft)
        Virtual Box(Sun)
        VMware(VMware)
          VMwareは有料になるので試していません。VirtualPCとVirtualBoxですがファイル連携などからVirualBoxの方がややオススメです。ファイル共有やネットワーク設定等比較的簡単に出来ます。環境構築やLinuxのインストールはさほど難しく無いので、普段環境を作ったことがある人であればできるでしょう。インストールはisoファイルでネットインストールするのが便利です。

          GAE開発環境構築

          GAE(Google App Engine)は無料で使えるクラウド環境で非常に便利です。気軽に試すにはもってこいです。GAEを利用するにはアカウント認証の際に携帯電話のメールアドレスが必要です。おそらく個人で複数アカウントを使いまわせないようにだと思うんですが、携帯のアドレスを変換すればいくつでも作れます。ただ1アカウントで10アプリケーションまで使えるのでその必要は余りなさそうですが。

          GAEにアップロードするには専用のツールを使いますが、オフィシャルサイトで落とすことができます。MacはGUIが在りますがWinはコマンドからアップします。ただEclipseのプラグインでも開発環境を整えることができ、アップロードもこちらからできるのでおすすめです。

          Eclipseは普通にインストールすれば良いです。オフィシャルのツールはプラグインを通して使うのでダウンロードしておく必要があります。プラグインとしてpydevをインストールすればあとは完成。設定で先のオフィシャルのツールの場所を指定してやればOKです。

          iPad開発で気を付けること

          iPad開発にはiPhoneSDKが必要ですがそのインストールにはSnow Leopardが必要になります。Leopardではダメです。Snow Leopardのシングルユーザライセンスは米国で29ドル、日本では3300円で提供され、一家5人までが使えるSnow Leopard Family Packは米国で49ドル、日本で5600円で販売されるとのこと。

          2010/06/02

          最初の投稿

          このサイトはGAEやiPhoneやRubyやcssなどについての備忘録です。