Pages

2011年5月28日土曜日

Coffee Script 最初の一杯

coffee scriptを試してみる。
Coffee Scriptは、文法がPythonに似ている言語で、コンパイルするとjavascriptを生成する。
恐らく、javascriptの強力な部分を活かし、判りにくい部分を隠ぺいしたモダンな開発言語を作成することが目的なのだと思う。javascriptベースのサーバサイド実行環境node.jsなど、javascriptだけでアプリケーションを開発する環境が整ってきている。javascriptは、インターネット・アプリケーション開発で、その言語の強力さとブラウザが実行エンジンを持つことから、重要な位置を占めてきた。サーバサイドが全てjavascriptで作成できれば、一つの言語でクライアントサイド、サーバサイドを共に開発することが出来る。しかし、ロジックを記述したり、トランザクションを記述するには、正直判りにくいと思う。coffee scriptは、そのようなニーズから生まれたのではないか。

Coffee Scriptとは
Coffee Scriptは、小規模な言語であり、コンパイルしてJavaScriptを生成する。中括弧やセミコロンに幻惑されて気づかないが、JavaScriptはいつも、その内に素晴らしいオブジェクトモデルを隠し持っている。Coffee Scriptは、JavaScriptの、その良い部分をシンプルな方法で顕在化する試みである。

CoffeeScriptの黄金律は、「まさにそれはJavascriptだ」と言うこと。コードはJavaScriptに一対一でコンパイルされ、実行時に不要な処理が行われることはない。専用のライブラリは不要だ。コンパイルされたJavaScriptコードは、読みやすく 警告なし、注釈なしで、全てのJavascript実行環境で動作するし、多くの場合、手書きのコードと同等か早く動作するでしょう。

CoffeeScript is a little language that compiles into JavaScript. Underneath all of those embarrassing braces and semicolons, JavaScript has always had a gorgeous object model at its heart. CoffeeScript is an attempt to expose the good parts of JavaScript in a simple way.

The golden rule of CoffeeScript is: "It's just JavaScript". The code compiles one-to-one into the equivalent JS, and there is no interpretation at runtime. You can use any existing JavaScript library seamlessly (and vice-versa). The compiled output is readable and pretty-printed, passes through JavaScript Lint without warnings, will work in every JavaScript implementation, and tends to run as fast or faster than the equivalent handwritten JavaScript.
(Coffee script ホームページから 引用)

最初のサンプルをコンパイルしてみる。
  1. # ファイル square.coffee  
  2. square = (x) -> x * x         # 無名関数"(x) -> x * x"を値にもつ。つまり関数名がsquare。  
  3. cube = (x) -> square(x) * x   # カリー化?みたいな感じ?  
  4. alert cube(5)                 # htmlでウインドウを表示させる。  
# ファイル square.coffee
square = (x) -> x * x         # 無名関数"(x) -> x * x"を値にもつ。つまり関数名がsquare。
cube = (x) -> square(x) * x   # カリー化?みたいな感じ?
alert cube(5)                 # htmlでウインドウを表示させる。

$ vim square.coffee
$ coffee -c square.coffee
$ ls 
square.coffee square.js

コンパイルした結果(square.js)を見てみると
  1. (function() {  
  2.   var cube, square;  
  3.   square = function(x) {  
  4.     return x * x;  
  5.   };  
  6.   cube = function(x) {  
  7.     return square(x) * x;  
  8.   };  
  9.   alert(cube(5));  
  10. }).call(this);  
(function() {
  var cube, square;
  square = function(x) {
    return x * x;
  };
  cube = function(x) {
    return square(x) * x;
  };
  alert(cube(5));
}).call(this);

実際に実行させてみる。以下のようなテスト用ソースを用意した。
実行結果はここで

2011年5月16日月曜日

node.js と npmをインストール

node.jsやらcoffee scriptやらTitanium を試してみたくて、さくらのVPSにインストールする。
例によって、いろいろと試行錯誤した(混乱した)ので、又記録しておく。

実は、node.jsだけならば一度入れたことがありました。その時の手順は以下の通り。

$ mkdir work/node
 $ cd work/node/
 $ wget http://nodejs.org/dist/node-v0.4.0.tar.gz 
 $ tar xzf node-v0.4.0.tar.gz 
 $ cd node-v0.4.0
 $ ./configure 
 $ make 
 $ sudo make install

この時はサンプルプログラム(以下)を作って実行してみた。
  1. /* file name : server.js  
  2. var http = require('http'); // httpライブラリを呼ぶ  
  3. http.createServer(function(request,response){  
  4.  response.writeHead(200, {'Content-Type':'text/plain'});  
  5.  response.end('Hello World');  
  6. }).listen(8124);  
  7. console.log('Server running at http://127.0.0.1:8124');  
/* file name : server.js
var http = require('http'); // httpライブラリを呼ぶ
http.createServer(function(request,response){
 response.writeHead(200, {'Content-Type':'text/plain'});
 response.end('Hello World');
}).listen(8124);
console.log('Server running at http://127.0.0.1:8124');

$ node server.js & 

今回はnode.js環境のパッケージマネージャであるnpmを導入して、様々なモジュールを試すための準備をすることだった。

混乱の最初は、nave。node.jsの複数のバージョンを切り替えるツールだが、これが必須だと勘違いしてしまったこと。node.jsは凄い勢いでヴァージョンがアップされているらしく、まだ仕様が安定していない。そのための対策で、rubyではrvmが有名。naveの他にnvmと言う同様の機能をもつツールもあるのだが、それがnpmと名前が似ているもんで、さらに混乱。

結局、naveは今回スキップ。
npmをgitで入れることにした。
$ git clone http://github.com/isaacs/npm.git
 $ cd npm
 $ sudo make install
! [ -d .git ] || git submodule update --init
bash: git: command not found
make: *** [submodules] エラー 127
失敗。「modulesが見つからないです」と叫ばれる。gitコマンドを調べてみる。
$ git submodule
-3d509ef6a503703e357f2ffa1df05e4f09a43bba node_modules/abbrev
-8b4395bf931a9b0d27fbb24d6f41d2a1cdbb61cb node_modules/nopt
-6e8da86d6a391f4a001971209cf1f4a66d060c4e node_modules/semver
なんだ。パッケージがないのか!と勝った気になって
$ git submodule init
$ git submodule update
$ git submodule
 3d509ef6a503703e357f2ffa1df05e4f09a43bba node_modules/abbrev (v1.0.2-3-g3d509ef)
 8b4395bf931a9b0d27fbb24d6f41d2a1cdbb61cb node_modules/nopt (v1.0.4)
 6e8da86d6a391f4a001971209cf1f4a66d060c4e node_modules/semver (v1.0.5)
よし入った。これで $ sudo make install が入らない?! 似たようなエラー。
それでは、本家npmの指示に従い
$ sudo node ./cli.js install -g
 $ npm -v
 1.0.6
これで、入ったっぽい。
良し。それでは、パッケージを試しに入れてみる。
$ npm install socket.io
 $ npm ls
 (empty)
ん? ない。そして、カレントディレクトリにnpm_modulesとか出来ている。その中にsocket.ioが入っている。なんじゃ?
いろいろ、やったが不明なので、npmをアンインストールすることに。
$ sudo npm uninstall npm -g
/usr/bin/env: node: そのようなファイルやディレクトリはありません
ええ!? これは、調べて visudoコマンドで、sudo時の環境変数リセット停止を設定。

Defaults    env_reset
Defaluts:%wheel !env_reset
Defaults    env_keep = "COLORS DISPLAY HOSTNAME HISTSIZE INPUTRC KDEDIR \
                        LS_COLORS MAIL PS1 PS2 QTDIR USERNAME \
                        LANG LC_ADDRESS LC_CTYPE LC_COLLATE LC_IDENTIFICATION \
                        LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC \
                        LC_PAPER LC_TELEPHONE LC_TIME LC_ALL LANGUAGE LINGUAS \
                        _XKB_CHARSET XAUTHORITY"
どうも、原因は
  • sudo実行時のnodeへのパスが通っていなかったこと
  • npmのインストール先は、/usr/localらしいが、此処には userレベルでのインストール権限がない
ことのようだ。npmの思想として、ローカルにインストールすることを前提としているらしい。恐らく、セキュリティの関係だと思うが。
結局、以下のサイトに従い、インストールした。
http://havelog.ayumusato.com/develop/javascript/e210-install_nodejs_and_npm.html

$ sudo chown -R $USER /usr/local
 $ curl http://npmjs.org/install.sh | sh
 $ sudo chown -R root /usr/local
 $ sudo npm install socket.io 
 $ sudo npm install express # 注: この前にnode.jsのバージョンを上げている
 $ npm ls
/home/baker
├─┬ express@2.3.4 
│ ├── connect@1.4.1 
│ ├── mime@1.2.2 
│ └── qs@0.1.0 
└── socket.io@0.6.17 
気持ちが悪いのは、node_modulesフォルダが、ユーザのホームディレクトリに出来ていること。
早速、coffee script をインストールしてみた。
$ npm install coffee-script
$ coffee -v
-bash: coffee: command not found
$ npm ls
/home/baker
├── coffee-script@1.1.1 
├─┬ express@2.3.4 
│ ├── connect@1.4.1 
│ ├── mime@1.2.2 
│ └── qs@0.1.0 
└── socket.io@0.6.17 
また変だ! 大体、プログラム本体は何処にいったのか?
結局、私が作ったnode, npm の環境はグローバルなので、
  • sudo による実行
  • -g オプションは必須
  • その結果 /usr/local/bin/ に、コマンドがインストールされる(リンク)
以下、インストールのやり直し
$ sudo npm install -g coffee-script
/usr/local/bin/cake -> /usr/local/lib/node_modules/coffee-script/bin/cake
/usr/local/bin/coffee -> /usr/local/lib/node_modules/coffee-script/bin/coffee
coffee-script@1.1.1 /usr/local/lib/node_modules/coffee-script
$ sudo npm install -g express
/usr/local/bin/express -> /usr/local/lib/node_modules/express/bin/express
connect@1.4.1 /usr/local/lib/node_modules/express/node_modules/connect
mime@1.2.2 /usr/local/lib/node_modules/express/node_modules/mime
qs@0.1.0 /usr/local/lib/node_modules/express/node_modules/qs
express@2.3.4 /usr/local/lib/node_modules/express
$ sudo npm -g install socket.io
socket.io@0.6.18 /usr/local/lib/node_modules/socket.io

$ npm uninstall express
$ npm uninstall socket.io
$ npm uninstall coffee-script

$ npm ls
/home/baker
(empty)

$ sudo npm -g ls
/usr/local/lib
├── coffee-script@1.1.1 
├─┬ express@2.3.4 
│ ├── connect@1.4.1 
│ ├── mime@1.2.2 
│ └── qs@0.1.0 
├─┬ npm@1.0.6 
│ ├── abbrev@1.0.3 
│ ├── node-uuid@1.1.0 
│ ├── nopt@1.0.4 
│ └── semver@1.0.5 
└── socket.io@0.6.18 
(追記)
あとで調べてみたところでは、.npmrcにパスを書き、あくまでローカルで実行する手もあったようだし、それが推薦のようだ。
実際に、npm install でローカルにインストールされたコマンドは、node_modules/.binにインストールされていたようで、そこにパスを通しても解決できたらしい。
散々な結果です。

2011年5月14日土曜日

チェックディジットを計算してみる

JANコードの末桁は、チェックデジットと呼ばれる読み取り防止のための数字だ。
計算方法は、モジュラス10 ウエイト3-1 (一括) と呼ばれる。
アルゴリズム名がmodulus10で、奇数桁計3倍、偶数桁 1倍、足し方が一括?だから!
JANバーコードのアルゴリズムは、末尾から奇数桁に3、偶数桁に1を掛けてから単純に足し算し、結果を10で割った余り(剰余)を求め、10からその剰余を引いた値をチェックディジットとして用いている。
例えば「49722536」というJAN8バーコードを例にすると、末尾6がチェックディジットである。この6はどう求められているかというと、次のように求めているわけである。
奇数桁の算出(4+7+2+3)×3→48
偶数桁の加算48+(9+2+5)→64
10から(2)の10の剰余を引く10-4→6
もって、ディジット6は正当であることが求められた。

rubyで書いてみた。
  1. # check_digit.rb   
  2. # check digit / modulus10 wait 3-1 (MOD 10)  
  3. # for JAN (EAN), ISBN-13  
  4. # usage : $ check_digit.rb <number>  
  5.   
  6. exit unless ARGV.size == 1  
  7. num = ARGV.shift # チェックデジットを取得したい数字をセット  
  8. exit unless [7,12].include?(num.size) # JAN(EAN)は、8桁,13桁のいずれか。C/D分を減らす。  
  9.   
  10. pos = total_even = total_odd = 0 # even 偶数、odd 奇数  
  11. # C/D分の桁を補完し、文字列を逆にして桁を1桁目から取る。split(//)で1文字単位に分割。  
  12. (num + '0').reverse.split(//).each do |s|  
  13.   pos += 1  
  14.   # 偶数桁、奇数桁を判断し、それぞれ加算。  
  15.   (pos % 2) == 0 ? total_even += s.to_i : total_odd += s.to_i  
  16. end  
  17. p 10 - (total_even * 3 + total_odd * 1) % 10 # ウエイト付けて、補数にして  
  18.   
  19. </number>  
# check_digit.rb 
# check digit / modulus10 wait 3-1 (MOD 10)
# for JAN (EAN), ISBN-13
# usage : $ check_digit.rb 

exit unless ARGV.size == 1
num = ARGV.shift # チェックデジットを取得したい数字をセット
exit unless [7,12].include?(num.size) # JAN(EAN)は、8桁,13桁のいずれか。C/D分を減らす。

pos = total_even = total_odd = 0 # even 偶数、odd 奇数
# C/D分の桁を補完し、文字列を逆にして桁を1桁目から取る。split(//)で1文字単位に分割。
(num + '0').reverse.split(//).each do |s|
  pos += 1
  # 偶数桁、奇数桁を判断し、それぞれ加算。
  (pos % 2) == 0 ? total_even += s.to_i : total_odd += s.to_i
end
p 10 - (total_even * 3 + total_odd * 1) % 10 # ウエイト付けて、補数にして



実行結果は、以下の通り。
[baker@www work]$ ruby check_digit.rb 4972253
6  # 最終コードは、49722536 
[baker@www work]$ ruby check_digit.rb 491012345064
3  # 最終コードは、4910123450643

2011年5月11日水曜日

さくらVPSにscalaをいれた

scalaをさくらインターネット VPSにインストールしてみた。

$ su -
$ cd /usr/local/src/
$ wget http://www.scala-lang.org/downloads/distrib/files/scala-2.8.1.final.tgz
$ tar xzf scala-2.8.1.final.tgz
$ mv scala-2.8.1.final ../scala
$ exit
$ vim .bashrc

.bashrcにパスを書き込んでおくことにした。

export SCALA_HOME=/usr/local/scala
export PATH=$PATH:$SCALA_HOME/bin

手動でscala用のパスを通して、動作を確認。

[baker@www ~]$ scala
Welcome to Scala version 2.8.1.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_23).
Type in expressions to have them evaluated.
Type :help for more information.

scala> printf("hello world!");
hello world!

全ユーザで利用できるようにしたほうがよいかな。

2011年5月8日日曜日

さくら(VPS)大戦 Redmine 編

さくらインターネットVPSのサーバに、ruby on rails製プロジェクト管理ソフトウエア Redmine をインストールした。過去サーバを設定した時の資料を見なかったこともあり、結構苦戦。

以下、失敗したところを記載しておく。実際の手順や詳細などは、以下のサイトを参考。
Mysql 5.5の評価など
version 5.5へのアップグレード
mysql_upgradeコマンド
さくらサーバへのインストールもろもろ
全般の工程(この方のサイト・デザインは美しい)
全般の工程 (たぶん、一番詳しい)

redmine用のデータベースを作成後
  $ wget http://rubyforge.org/frs/download.php/74722/redmine-1.1.3.tar.gz
  $ tar xzvf redmine-1.1.3.tar.gz 
  $ mv redmine-1.1.3 /var/www/html/redmine
  $ cd /var/www/html/config/
  $ cp database.yml.example database.yml
  $ vim database.yml
  $ rake generate_session_store
  $ rake db:migrate RAILS_ENV=production # 失敗
redmineの1.1.3は、rails 2.3.5で動作している。
  $ vim environment.rb 
  $ gem install rails -v=2.3.5
  $ rake db:migrate RAILS_ENV=production # 失敗
railsのmysqlドライバーは、2.2以降標準ではインストールされない。mysqlの接続に失敗。
  $ yum install mysql-devel
失敗。remi リポジトリからインストールしていた特別なVersionのMysqlだった。
  $ mysql --version
  $ yum install mysql-devel --enablerepo=remi
失敗。i386用のmysqlやmysql-develもアップグレードしようとしている。
さくらのVPSのCentOSは64bit。i386ではなくx86_64のみを指定。
  $ yum install mysql-devel.x86_64 --enablerepo=remi
結果、mysql 5.1 -> 5.5 に変更された。
mysqlドライバーをインストール。
  $ gem install mysql
  $ cd /var/www/html/redmine
  $ rake db:migrate RAILS_ENV=production
失敗。mysqldが起動していない。
  $ /etc/init.d/mysqld start
失敗する。ログをみると、
[ERROR] /usr/libexec/mysqld: unknown variable 'default-character-set=utf8'
とりあえず、/etc/my.cnfの[mysqld]の default-character-set=utf8 をコメントアウト
  $ /etc/init.d/mysqld start
  $ vim /var/log/mysqld.log
起動に成功したが、エラーが一杯でている。mysql_upgradeをやる。
  $ mysql_upgrade -p
  $ /etc/init.d/mysqld restart
logでは問題ないようだ。
mysql5.5では、文字列指定のオプションが変更されている。
サーバー側 --character-set-server
クライアント側 --default-character-set (今まで通り)
よって、/etc/my.cnf [mysqld] 内の指定を charset-set-server=utf8 と書き換える。
Redmineのスキーマ作成、初期データ作成(途中で、jaと指定)
  $ vim /etc/my.cnf
  $ /etc/init.d/mysqld restart
  $ rake db:migrate RAILS_ENV=production
  $ rake redmine:load_default_data RAILS_ENV=production
メールも設定
  $ cp config/email.yml.example config/email.yml
  $ vim config/email.yml
さくらのドメインサービスにて、redmine.xxxxをバーチャルホストとして指定
passenger用にヴァーチャルホスト設定を追加
  $ vim /etc/httpd/conf.d/vhost.conf 
  $ /etc/init.d/httpd restart

結局、過去の更新、設定記録を見なかった事が敗因。PHP5.3 をインストールするために、CentOSのデフォルトmysqlのVersino 5.0.x ではなく、5.1.x を入れた時の記録。これがあれば、随分と工数削減できたかも。

2011年5月4日水曜日

Actorをさわってみる

最近、システム障害で並列処理について考える機会があった。並列処理といえば、気になっていたscalaのActorについて勉強してみる。
参考にしたサイト
アクターで楽々並行プログラミング
Scalaの平行性を掘り下げる

復習も兼ねて、例にあった簡単なコードをコピーしてみる。
このプログラムでは、文字列を5回しゃべるアクターが2つ立ち上がり、お互いに関係なくしゃべったら終了します。
  1. import scala.actors.Actor   // Actorトレイトをインポート  
  2.   
  3. class Printer(val msg : String) extends Actor { // Actorを継承したPrinterクラス  
  4.   def act () { // Actorクラスの場合、act()メソッド必須。これを実行する。  
  5.     for(i <- 1 to 5) { // scalaのfor構文  
  6.       println(msg)  
  7.       Thread.sleep(3)  
  8.     }  
  9.   }  
  10. }  
  11.   
  12. object App { // 実行の主体  
  13.   def main (args : Array[String]) {  
  14.     (new Printer("abcdefg")).start // Printerクラスをインスタンスにして実行させる   
  15.     (new Printer("hijklmn")).start // Actorではstartメソッドが実行されるとact()が実施される  
  16.   }  
  17. }  
import scala.actors.Actor   // Actorトレイトをインポート

class Printer(val msg : String) extends Actor { // Actorを継承したPrinterクラス
  def act () { // Actorクラスの場合、act()メソッド必須。これを実行する。
    for(i <- 1 to 5) { // scalaのfor構文
      println(msg)
      Thread.sleep(3)
    }
  }
}

object App { // 実行の主体
  def main (args : Array[String]) {
    (new Printer("abcdefg")).start // Printerクラスをインスタンスにして実行させる 
    (new Printer("hijklmn")).start // Actorではstartメソッドが実行されるとact()が実施される
  }
}
Scalaの"思い出し”をメモ。
  • トレイトは、rubyのモジュールみたいなもの
  • objectはScalaではシングルトンパターンの実装。クラスのインスタンスであり定数ではない。
さて、早速実行してみました。
scala-example$ vim actor_test1.scala     # ソースを作成
scala-example$ scalac actor_test1.scala  # コンパイル
scala-example$ ls -altr                  # クラスファイルが2つ出来ている。
-rw-r--r--   1 baker  staff    276  5  4 15:36 actor_test1.scala
-rw-r--r--   1 baker  staff  14692  5  4 15:38 Printer.class
-rw-r--r--   1 baker  staff   1327  5  4 15:38 Printer$$anonfun$act$1.class
-rw-r--r--   1 baker  staff    604  5  4 15:38 App.class
-rw-r--r--   1 baker  staff    649  5  4 15:38 App$.class
drwxr-xr-x  14 baker  staff    476  5  4 15:56 .
scala-example$ scala -cp . App           # クラスを指定して実行。クラスパスはカレント。
abcdefg
hijklmn
abcdefg
hijklmn
abcdefg
hijklmn
abcdefg
hijklmn
hijklmn
abcdefg
classファイルが複数出来るのは知らなかった。また、この中間ファイルみたいな$$がついたファイルはなんだろう。これを削除すると実行がうまくいかないので、コンパイルされた実態(中間コード)なのかな?