RSRubyの使い方

よく忘れるのでメモ

#!/bin/env ruby
# coding:utf-8

require  "rsruby"
require "rsruby/dataframe"

r=RSRuby.instance
r.class_table["data.frame"]=lambda{|x| DataFrame.new(x)}
RSRuby.set_default_mode(RSRuby::CLASS_CONVERSION)

x=[1,2,3]
y=[2,4,6]
df=r.as_data_frame(:x=>{'x'=>x,'y'=>y})
r.assign('df',df)
res=r.eval_R(<

WindowsでJekyll環境構築

jekyllをご存知でしょうか?そこいら中に記事があるので詳細はググればわかりますが、簡単にいえば静的HTMLのジェネレータフレームワークです。

Rubyで作成されていますのでgemで簡単にインストールできます。

がちょっとハマったので備忘録。。

環境

  • Windows8.1

インストール

まずはRUBYを入れます。Windows用のRubyはいろいろありますが、

http://rubyinstaller.org/downloads/

こちらからWindows用のMSIをダウンロードしインストールします。

C:\Ruby21-x64にインストールした後には環境変数PATHにC:\Ruby21-x64\binを追加します。

そのままgemでインストールしようとすると以下のエラーが出ます

C:\>gem install jekyll
ERROR:	Error installing jekyll:
The 'yajl-ruby' native gem requires installed build tools.
Please update your PATH to include build tools or download the DevKit
from 'http://rubyinstaller.org/downloads' and follow the instructions
at 'http://github.com/oneclick/rubyinstaller/wiki/Development-Kit'

どうやらMakeできなよと怒られたようです

DevelopmentKitなるものを同じページからダウンロードします。

DevKit-mingw64-64-4.7.2-20130224-1432-sfx.exe

現在だとこれになります。

これをC直下にc:\devkitというフォルダを作成しその中に解凍します

その後以下のコマンドを実行

C:\devkit>ruby dk.rb init
[INFO] found RubyInstaller v2.1.3 at C:/Ruby21-x64
Initialization complete! Please review and modify the auto-generated
'config.yml' file to ensure it contains the root directories to all
of the installed Rubies you want enhanced by the DevKit.
C:\devkit>ruby dk.rb install
[INFO] Updating convenience notice gem override for 'C:/Ruby21-x64'
[INFO] Installing 'C:/Ruby21-x64/lib/ruby/site_ruby/devkit.rb'

これで準備が整いました

あとはgemでインストール

C:\devkit>gem install jekyll
Temporarily enhancing PATH to include DevKit...
Building native extensions.	This could take a while...
Successfully installed yajl-ruby-1.1.0
Fetching: posix-spawn-0.3.9.gem (100%)
Building native extensions.	This could take a while...
Successfully installed posix-spawn-0.3.9
Fetching: pygments.rb-0.6.0.gem (100%)
Successfully installed pygments.rb-0.6.0
Fetching: redcarpet-3.2.0.gem (100%)
Building native extensions.	This could take a while...
Successfully installed redcarpet-3.2.0
Fetching: blankslate-2.1.2.4.gem (100%)
Successfully installed blankslate-2.1.2.4
Fetching: parslet-1.5.0.gem (100%)
Successfully installed parslet-1.5.0
Fetching: toml-0.1.1.gem (100%)
Successfully installed toml-0.1.1
Fetching: jekyll-paginate-1.1.0.gem (100%)
Successfully installed jekyll-paginate-1.1.0
Fetching: jekyll-gist-1.1.0.gem (100%)
Successfully installed jekyll-gist-1.1.0
Fetching: coffee-script-source-1.8.0.gem (100%)
Successfully installed coffee-script-source-1.8.0
Fetching: execjs-2.2.2.gem (100%)
Successfully installed execjs-2.2.2
Fetching: coffee-script-2.3.0.gem (100%)
Successfully installed coffee-script-2.3.0
Fetching: jekyll-coffeescript-1.0.1.gem (100%)
Successfully installed jekyll-coffeescript-1.0.1
Fetching: sass-3.4.5.gem (100%)
Successfully installed sass-3.4.5
Fetching: jekyll-sass-converter-1.2.1.gem (100%)
Successfully installed jekyll-sass-converter-1.2.1
Fetching: ffi-1.9.6-x64-mingw32.gem (100%)
Successfully installed ffi-1.9.6-x64-mingw32
Fetching: rb-inotify-0.9.5.gem (100%)
Successfully installed rb-inotify-0.9.5
Fetching: rb-fsevent-0.9.4.gem (100%)
Successfully installed rb-fsevent-0.9.4
Fetching: hitimes-1.2.2.gem (100%)
Building native extensions.	This could take a while...
Successfully installed hitimes-1.2.2
Fetching: timers-4.0.1.gem (100%)
Successfully installed timers-4.0.1
Fetching: celluloid-0.16.0.gem (100%)
Successfully installed celluloid-0.16.0
Fetching: listen-2.7.11.gem (100%)
Successfully installed listen-2.7.11
Fetching: jekyll-watch-1.1.1.gem (100%)
Successfully installed jekyll-watch-1.1.1
Fetching: fast-stemmer-1.0.2.gem (100%)
Building native extensions.	This could take a while...
Successfully installed fast-stemmer-1.0.2
Fetching: classifier-reborn-2.0.1.gem (100%)
Successfully installed classifier-reborn-2.0.1
Fetching: jekyll-2.4.0.gem (100%)
Successfully installed jekyll-2.4.0
Parsing documentation for blankslate-2.1.2.4
Installing ri documentation for blankslate-2.1.2.4
Parsing documentation for celluloid-0.16.0
Installing ri documentation for celluloid-0.16.0
Parsing documentation for classifier-reborn-2.0.1
Installing ri documentation for classifier-reborn-2.0.1
Parsing documentation for coffee-script-2.3.0
Installing ri documentation for coffee-script-2.3.0
Parsing documentation for coffee-script-source-1.8.0
Installing ri documentation for coffee-script-source-1.8.0
Parsing documentation for execjs-2.2.2
Installing ri documentation for execjs-2.2.2
Parsing documentation for fast-stemmer-1.0.2
Installing ri documentation for fast-stemmer-1.0.2
Parsing documentation for ffi-1.9.6-x64-mingw32
Installing ri documentation for ffi-1.9.6-x64-mingw32
Parsing documentation for hitimes-1.2.2
Installing ri documentation for hitimes-1.2.2
Parsing documentation for jekyll-2.4.0
Installing ri documentation for jekyll-2.4.0
Parsing documentation for jekyll-coffeescript-1.0.1
Installing ri documentation for jekyll-coffeescript-1.0.1
Parsing documentation for jekyll-gist-1.1.0
Installing ri documentation for jekyll-gist-1.1.0
Parsing documentation for jekyll-paginate-1.1.0
Installing ri documentation for jekyll-paginate-1.1.0
Parsing documentation for jekyll-sass-converter-1.2.1
Installing ri documentation for jekyll-sass-converter-1.2.1
Parsing documentation for jekyll-watch-1.1.1
Installing ri documentation for jekyll-watch-1.1.1
Parsing documentation for listen-2.7.11
Installing ri documentation for listen-2.7.11
Parsing documentation for parslet-1.5.0
Installing ri documentation for parslet-1.5.0
Parsing documentation for posix-spawn-0.3.9
Installing ri documentation for posix-spawn-0.3.9
Parsing documentation for pygments.rb-0.6.0
Installing ri documentation for pygments.rb-0.6.0
Parsing documentation for rb-fsevent-0.9.4
Installing ri documentation for rb-fsevent-0.9.4
Parsing documentation for rb-inotify-0.9.5
Installing ri documentation for rb-inotify-0.9.5
Parsing documentation for redcarpet-3.2.0
Installing ri documentation for redcarpet-3.2.0
Parsing documentation for sass-3.4.5
Installing ri documentation for sass-3.4.5
Parsing documentation for timers-4.0.1
Installing ri documentation for timers-4.0.1
Parsing documentation for toml-0.1.1
Installing ri documentation for toml-0.1.1
Parsing documentation for yajl-ruby-1.1.0
Installing ri documentation for yajl-ruby-1.1.0
Done installing documentation for blankslate, celluloid, classifier-reborn, coff
ee-script, coffee-script-source, execjs, fast-stemmer, ffi, hitimes, jekyll, jek
yll-coffeescript, jekyll-gist, jekyll-paginate, jekyll-sass-converter, jekyll-wa
tch, listen, parslet, posix-spawn, pygments.rb, rb-fsevent, rb-inotify, redcarpe
t, sass, timers, toml, yajl-ruby after 16 seconds
26 gems installed

rubyでMongoDBにISODateを入れる試み

  • ruby1.9.3

apacheのログなどを解析する場合にはmongodbに一度入れると扱いやすくなります。

そこでこのプレーンテキストのapacheのログをrubyを使っていれる試みをしてみました

この辺りを参考に

http://mironal-memo.blogspot.jp/2012/07/mongodb-ruby.html

gem install mongo
gem install bson_ext

ログからip,時間、urlの3つ、必要なものを抽出します

zcat access.log.gz |awk '{print $1"\t"$4"\t"$7}' > log.txt
#!/bin/env ruby
require "mongo"

conn=Mongo::Connection.new
db=conn.db("database")
col=db.collection("logs")
open("log.txt"){|f|
	f.each{|line|
		line.chomp!
		ay=line.split("\t")
		col.insert({"ip"=>ay[0],"date":"ISODate(Time.parse(ay[1].gsub("[","").sub(":"," ")).strftime('%Y-%m-%dT%H:%M:%SZ'))","url":ay[2])
	}
}

こんな感じで入るのかと思ったのですがISODateにはなりません

仕方がないので一度ファイルにします

  • create.rb
#!/bin/env ruby
puts "use database"
open("log.txt"){|f|
	f.each{|line|
		line.chomp!
		ay=line.split("\t")
		puts 'db.logs.insert({ip:"'+ay[0]+'","date":ISODate("'+Time.parse(ay[1].gsub("[","").sub(":"," ")).strftime('%Y-%m-%dT%H:%M:%SZ'))+'"),"url":"'+ay[2]+'"))'
	}
}
ruby create.rb > mongo.dat
mongo < mongo.dat

これくらいしかわかりませんでした

20131225 追記

一度ファイルにせずともできました

#!/bin/env ruby
require "rubygems"
require "mongo"
require "time"
include Mongo
db=MongoClient.new("localhost",27017).db("database")
col=db.collection("test")
col.insert({"id"=>"id0","date"=>Time.parse("2013-12-25 00:00:00").utc})

Time型のオブジェクトを作成し、utcへ変換するとMongodbではISODateへとなっています

Kakasiをrubyでつかう(その後)

ruby-kakasiを導入してみたのですが、どうも動きがおかしいことに気づきました

どういう時におかしいかというと連続で違う引数で呼び出す際にどうやら2回めの呼び出し時の引数が聞いていないような感じです。しかもローマ字に変換する際にヘボン式か訓令式か変更した場合のみです

こんなコード

#!ruby
#coding:utf-8

require "kakasi"
require "nkf"
keyword="し ち つ ふ じ ぢ しゃ しゅ しょ ちゃ ちゅ ちょ じゃ じゅ じょ ぢゃ ぢゅ ぢょ"
p Kakasi.kakasi("-Ea -Ja -Ka -Ha ",NKF.nkf("-e",keyword))
p Kakasi.kakasi("-Ea -Ja -Ka -Ha -rk",NKF.nkf("-e",keyword))

結果がこれ

$ ruby test.rb
"shi chi tsu fu ji di sha shu sho cha chu cho ja ju jo dya dyu dyo"
"shi chi tsu fu ji di sha shu sho cha chu cho ja ju jo dya dyu dyo"

最初kakasi-rubyがおかしいのかと思いソースコードをいじってみていたのですがどうやらそうではなく、本体側がおかしいことがわかりました。

このぶぶん

--- kakasi-2.3.4/src/hh2.c			2001-05-30 14:46:55.000000000 +0900
+++ kakasi-2.3.4_mod/src/hh2.c	2013-04-23 18:23:04.000000000 +0900
@@ -192,7 +192,7 @@
		 int max_match, match_more;
		 char *max_romaji;

-		if (index_made == 0) {
+//		if (index_made == 0) {
				int last;

				for (i = 0; i < 0x81; ++ i) {
@@ -211,7 +211,7 @@
								last = index_table[i];
				}
				index_made = 1;
-		}
+//		}

		 buffer[H2rom_buflen-1] = '\0'; clen = H2rom_buflen-1;
		 for (i = 0; i < (H2rom_buflen-1)/2; i ++) {

どうも訓令式とヘボン式のテーブルを切り替えて使っているようなのですが、高速化のためか一文字目の際にテーブルを作成し、それ以降はキャッシュを見るような感じになっています。しかしstatic変数で管理しているため、Library使用時にはこれがクリアされず、2回めのライブラリ呼び出し時には前回のキャッシュをそのまま使っていたようです

とりあえずキャッシュ部分を削除してみました。

本来ならばきちんと治すべきなのですが、、、、、

本体修正後rubyを実行

ruby test.rb
"shi chi tsu fu ji di sha shu sho cha chu cho ja ju jo dya dyu dyo"
"si ti tu hu zi di sya syu syo tya tyu tyo zya zyu zyo dya dyu dyo"

期待通りとなりました

kakasi-rubyをruby1.9で動かす

https://github.com/hogelog/kakasi-ruby

ruby用のkakasiモジュールはそのままではruby1.9ではコンパイルが通りません

そこで↓を参考に修正

http://www.metareal.org/2007/06/14/ruby-extension-struct-rstring-macros/

パッチ

diff -u kakasi-ruby-master/kakasi.c kakasi-ruby-master-1.9/kakasi.c
--- kakasi-ruby-master/kakasi.c 2013-04-19 10:14:17.000000000 +0900
+++ kakasi-ruby-master-1.9/kakasi.c		 2013-04-19 10:08:46.000000000 +0900
@@ -3,24 +3,28 @@
	*	Copyright (c) 1999-2002 GOTO Kentaro
	*/

-
 #include 
 #include "ruby.h"
 #include "libkakasi.h"

-
 #define OPTMAX 1024
 #define min(x,y) ((x)<(y) ? (x) : (y))


+// 130419 for 1.9
+#ifndef RSTRING_PTR
+#define RSTRING_PTR(s) (RSTRING(s)->ptr)
+#endif
+#ifndef RSTRING_LEN
+#define RSTRING_LEN(s) (RSTRING(s)->len)
+#endif
+
 static char const rcsid[] =
	 "$kNotwork: kakasi.c,v 1.3 2002/09/28 05:21:37 gotoken Exp $";

-
 static int dic_closed = 1, len = 0;
 static char prev_opt_ptr[OPTMAX];

-
 static VALUE
 rb_kakasi_kakasi(obj, opt, src)
		 VALUE obj, opt, src;
@@ -30,45 +34,36 @@
		 char *buf, *opt_ptr, *t;
		 VALUE dst;

-
		 Check_Type(src, T_STRING);

-
		 /* return "" immediately if source str is empty */
-		if (RSTRING(src)->len == 0)
+		if(RSTRING_LEN(src)==0)
				return rb_str_new2("");

-
		 Check_Type(opt, T_STRING);

-
			/* initialize kakasi iff opt != previous opt */
-		if (0 == len || 0 != strncmp(RSTRING(opt)->ptr, prev_opt_ptr,
-																min(RSTRING(opt)->len, len))) {
-			 strncpy(prev_opt_ptr, RSTRING(opt)->ptr, RSTRING(opt)->len);
-			 len = RSTRING(opt)->len;
-
+		if (0 == len || 0 != strncmp(RSTRING_PTR(opt), prev_opt_ptr,
+																min(RSTRING_LEN(opt), len))) {
+			 strncpy(prev_opt_ptr, RSTRING_PTR(opt), RSTRING_LEN(opt));
+			 len = RSTRING_LEN(opt);

				if (len + 1 > OPTMAX) {
						rb_raise(rb_eArgError, "too long 1st arg (should be < 1023)");
				}

-
				if (!dic_closed) {
						kakasi_close_kanwadict();
						dic_closed = 1;
				}

-
-			 argv = opts = ALLOCA_N(char*, RSTRING(opt)->len);
+			 argv = opts = ALLOCA_N(char*, RSTRING_LEN(opt));
				*opts++ = "kakasi";
				argc++;

-
-			 opt_ptr = ALLOCA_N(char, 1 + RSTRING(opt)->len);
-			 strncpy(opt_ptr, RSTRING(opt)->ptr, RSTRING(opt)->len);
-			 opt_ptr[RSTRING(opt)->len] = '\0';
-
+			 opt_ptr = ALLOCA_N(char, 1 + RSTRING_LEN(opt));
+			 strncpy(opt_ptr, RSTRING_PTR(opt), RSTRING_LEN(opt));
+			 opt_ptr[RSTRING_LEN(opt)] = '\0';

				if (*opts++ = strtok(opt_ptr, " \t")) {
						argc++;
@@ -77,42 +72,37 @@
								argc++;
						}
				}
-
-
+
				if (0 != kakasi_getopt_argv(argc, argv))
						rb_raise(rb_eRuntimeError, "failed to initialize kakasi");
				dic_closed = 0;
		 }

-
		 dst = rb_str_new2("");
-		while (i < RSTRING(src)->len) {
-			if (*(RSTRING(src)->ptr + i) != '\0') {
-			 buf = kakasi_do((RSTRING(src)->ptr + i));
+		while (i < RSTRING_LEN(src)) {
+			if (*(RSTRING_PTR(src) + i) != '\0') {
+			 buf = kakasi_do((RSTRING_PTR(src) + i));
				rb_str_concat(dst, rb_str_new2(buf));
				if (*buf) free(buf);
-			 while (*(RSTRING(src)->ptr + i) != '\0') {
+			 while (*(RSTRING_PTR(src) + i) != '\0') {
					i++;
				}
			 }
-			if (i == RSTRING(src)->len) {
+			if (i == RSTRING_LEN(src)) {
				break;
			 }
			 rb_str_concat(dst, rb_str_new("\0", 1));
			 i++;
		 }

-
		 return dst;
 }

-
 void
 Init_kakasi()
 {
		 VALUE mKakasi = rb_define_module("Kakasi");

-
		 rb_define_module_function(mKakasi, "kakasi", rb_kakasi_kakasi, 2);
		 rb_define_const(mKakasi, "KAKASI_VERSION", rb_str_new2("2002-09-28"));
 }

と思って作成していたらすでに修正している人がいました。_| ̄|○

https://github.com/hogelog/kakasi-ruby/tree/1.9

YAML.dumpで日本語が文字化けする

YAML.dumpでUTF8文字列をダンプする際にはバイナリと認識されてしまい、ファイルに保存した際にちょっと困ったことになります。

Psychなどを使う方法があるとのことですが、1.8だと気軽には使えません。

一つすばらしいやり方が紹介されていたのでちょっと改造してみます

http://projectzero-swb.blogspot.jp/2010/06/rubyyaml2.html

  • CentOS5.4
  • ruby1.8.7
#!/usr/bin/env ruby																																											 
# -*- encoding: utf-8 -*-

require "yaml"
require "yaml/encoding"

# String.is_binary_data?で必ずfalseを返すように書き換える
class String
	def is_binary_data?
		return false
	end
end

class Name
	attr_reader:no,:name
	def initialize(no,name)
		 @no=no
		 @name=name
	end
	def to_s
		return @no.to_s+":"+@name.to_s
	end
end

data = {"はつね"=>Name.new(1,"みく"), "はちゅね"=>Name.new(2,"みく"), "かがみね"=>Name.new(3,"りん・
れん"), "めぐりね"=>Name.new(4,"るか")}

# 通常どおり表示
#puts "Escape:"
yaml = YAML.dump(data)

# アンエスケープして表示
#puts "Unescape:"
yaml = YAML.unescape(yaml)

f=open("test3.yaml","w")
f.write(yaml)
f.close

f=YAML.load_file("test3.yaml")
f.each{|k,v|
	puts k+","+v.to_s
}

実行結果

はちゅね,2:みく
めぐりね,4:るか
かがみね,3:りん・れん
はつね,1:みく

test3.yaml

--- 
"はちゅね": !ruby/object:Name
	name: "みく"
	"no": 2
"めぐりね": !ruby/object:Name
	name: "るか"
	"no": 4
"かがみね": !ruby/object:Name
	name: "りん・れん"
	"no": 3
"はつね": !ruby/object:Name
	name: "みく"
	"no": 1

rubyのマルチスレッド

rubyはマルチスレッドのプログラムを書いても、CPUは一個しか使ってくれないらしい。

GIL というもののせいらしい。。

http://ja.wikipedia.org/wiki/グローバルインタプリタロック

そこでマルチプロセスのプログラムを作ってみる

#!/bin/env ruby
class Test
	def exec
		t1=fork{calc}
		t2=fork{calc}
		Process.waitpid t1
		Process.waitpid t2
	end
	def calc
		tim=rand(10)
p Process.pid.to_s+",start,"+tim.to_s
		sleep tim
p Process.pid.to_s+",end,"+tim.to_s
		exit
	end
end

if __FILE__ == $0 then
	Test.new.exec
end