RMagick のインストールでコケる問題

昨日から、自分の Lion 環境上で rmagick を使う Rails プロダクトの Bundle install 時に、

/Users/xxx/.rvm/rubies/ruby-1.9.2-p290/bin/ruby extconf.rb
checking for Ruby version >= 1.8.5... yes
checking for /usr/bin/gcc-4.2... yes
checking for Magick-config... yes
checking for ImageMagick version >= 6.4.9... yes
checking for HDRI disabled version of ImageMagick... yes
checking for stdint.h... *** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of
necessary libraries and/or headers.  Check the mkmf.log file for more
details.  You may need configuration options.

Provided configuration options:
        --with-opt-dir
        --without-opt-dir
        --with-opt-include
        --without-opt-include=${opt-dir}/include
        --with-opt-lib
        --without-opt-lib=${opt-dir}/lib
        --with-make-prog
        --without-make-prog
        --srcdir=.
        --curdir
        --ruby=/Users/xxx/.rvm/rubies/ruby-1.9.2-p290/bin/ruby
/Users/xxx/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/1.9.1/mkmf.rb:368:in `try_do': The complier failed to generate an executable file. (RuntimeError)
You have to install development tools first.
        from /Users/xxx/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/1.9.1/mkmf.rb:452:in `try_cpp'
        from /Users/xxx/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/1.9.1/mkmf.rb:834:in `block in have_header'
        from /Users/xxx/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/1.9.1/mkmf.rb:693:in `block in checking_for'
        from /Users/xxx/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/1.9.1/mkmf.rb:280:in `block (2 levels) in postpone'
        from /Users/xxx/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/1.9.1/mkmf.rb:254:in `open'
        from /Users/xxx/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/1.9.1/mkmf.rb:280:in `block in postpone'
        from /Users/xxx/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/1.9.1/mkmf.rb:254:in `open'
        from /Users/xxx/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/1.9.1/mkmf.rb:276:in `postpone'
        from /Users/xxx/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/1.9.1/mkmf.rb:692:in `checking_for'
        from /Users/xxx/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/1.9.1/mkmf.rb:833:in `have_header'
        from extconf.rb:193:in `<main>'

というエラーで rmagick のインストールに失敗するという現象に悩まされていたのですが、
単純にググってもそれらしい情報に行き当たらず、苦労していたので原因と解決方法をメモしておきます。

まず、この環境ですが、ImageMagick を brew で入れた時から怪しいところがありました。

% brew install imagemagick
==> Downloading http://downloads.sf.net/project/machomebrew/Bottles/imagemagick-6.7.1-1-bottle.tar.gz
File already downloaded in /Users/xxx/Library/Caches/Homebrew
==> Pouring imagemagick-6.7.1-1.bottle.tar.gz
==> Caveats
Some tools will complain unless the ghostscript fonts are installed to:
  /usr/local/share/ghostscript/fonts
ln: Wand.pc: Permission denied
Error: The linking step did not complete successfully
The formula built, but is not symlinked into /usr/local
You can try again using `brew link imagemagick'
==> Summary
/usr/local/Cellar/imagemagick/6.7.1-1: 1389 files, 32M

良く見ると、途中でエラーを吐いています。シンボリックリンクが作れなかったから、手動で brew link しろと。
単純に実行しただけだと ” ln: Wand.pc: Permission denied ” を解決できず同じ様に死んでしまうものの、 sudo で実行すると完了し、一見問題ないように見えます。
しかし、肝心の rmagick のインストール時には上記のようなエラーになってしまいました。

bundle が悪いのかと思って、単純に

% gem install rmagick

しても、同じでした。冒頭のエラーを良く見ると

Could not create Makefile due to some reason, probably lack of
necessary libraries and/or headers. Check the mkmf.log file for more
details. You may need configuration options.

とあるので、mkmf.log を読んでみる事に。

% less ~/.rvm/gems/ruby-1.9.2-p290/gems/rmagick-2.13.1/ext/RMagick/mkmf.log
checking for Ruby version >= 1.8.5... -------------------- yes

--------------------

find_executable: checking for /usr/bin/gcc-4.2... -------------------- yes

--------------------

find_executable: checking for Magick-config... -------------------- yes

--------------------

checking for ImageMagick version >= 6.4.9... -------------------- yes

--------------------

checking for HDRI disabled version of ImageMagick... -------------------- yes

--------------------

"/usr/bin/gcc-4.2 -o conftest -I/Users/xxx/.rvm/rubies/ruby-1.9.2-p290/include/ruby-1.9.1/x86_64-darwin11.0.1 -I/Users/xxx/.rvm/rubies/ruby-1.9.2-p290/include/ruby-1.9.1/ruby/backward -I/Users/xxx/.rvm/rubies/ruby-1.9.2-p290/include/ruby-1.9.1 -I.  -I/usr/local/Cellar/imagemagick/6.7.1-1/include/ImageMagick  -I/usr/local/Cellar/imagemagick/6.7.1-1/include/ImageMagick -fopenmp conftest.c  -L. -L/Users/xxx/.rvm/rubies/ruby-1.9.2-p290/lib  -L/usr/local/Cellar/imagemagick/6.7.1-1/lib -L/usr/X11/lib    -L/usr/local/Cellar/imagemagick/6.7.1-1/lib -lMagickCore -llcms -ltiff -lfreetype -ljpeg -L/usr/X11/lib -lfontconfig -lXext -lSM -lICE -lX11 -lXt -lbz2 -lz -lm -lgomp -lpthread -lltdl  -lruby.1.9.1-static  -lpthread -ldl -lobjc "
ld: library not found for -llcms
collect2: ld returned 1 exit status
checked program was:
/* begin */
1: #include "ruby.h"
2:
3: int main() {return 0;}
/* end */

-llcms を見つけられないのが原因だったようです。
liblcms とはなんぞ、と思ってググったらこのページに引っ掛かりました。Libraries for the Little CMS Engine というものらしいので、 再度この名前でググると、どうやらカラーマネジメントシステムのライブラリみたい。

しかし、必要なら brew が依存性解決に入れてくれているだろうと見てみると

$ brew list
cmake          imagemagick     jpeg          little-cms     mysql          nkf          readline     zsh
gdbm          jasper          libtiff          mongodb          netcat          pidof          wget

案の定、little-cms というソレらしいのが居ます。
が、ここで先程、brew で ImageMagick 入れようとしたときにエラーが出ていたのを思い出しました。
依存性解決のために自動で入れる途中で、同じようなエラーに遭遇して、little-cms パッケージが正常にインストールされていないのではないかという仮説を思いつき、この腐った brew 環境を叩き直すために、お医者様の力を借りる事にします。

% brew doctor
Unbrewed dylibs were found in /usr/local/lib.

If you didn't put them there on purpose they could cause problems when
building Homebrew formulae, and may need to be deleted.

Unexpected dylibs:
    /usr/local/lib/libmacfuse_i32.2.dylib
    /usr/local/lib/libmacfuse_i64.2.dylib
    /usr/local/lib/libosxfuse_i32.2.dylib
    /usr/local/lib/libosxfuse_i64.2.dylib

Unbrewed .pc files were found in /usr/local/lib/pkgconfig.

If you didn't put them there on purpose they could cause problems when
building Homebrew formulae, and may need to be deleted.

Unexpected .pc files:
    /usr/local/lib/pkgconfig/osxfuse.pc

Unbrewed .la files were found in /usr/local/lib.

If you didn't put them there on purpose they could cause problems when
building Homebrew formulae, and may need to be deleted.

Unexpected .la files:
    /usr/local/lib/libosxfuse_i32.la
    /usr/local/lib/libosxfuse_i64.la

/usr/local/lib/pkgconfig isn't writable.
This can happen if you "sudo make install" software that isn't managed
by Homebrew.

If a brew tries to write a .pc file to this folder, the install will\nfail during the link step.

You should probably `chown` /usr/local/lib/pkgconfig

Some "config" scripts were found in your path, but not in system or Homebrew folders.

`./configure` scripts often look for *-config scripts to determine if software packages
are installed, and what additional flags to use when compiling and linking.

Having additional scripts in your path can confuse software installed via Homebrew if
the config script overrides a system or Homebrew provided script of the same name.

/usr/X11R6/bin
    freetype-config libpng-config libpng15-config

brew doctor は概ね、勝手に /usr/local 以下に入ったosxfuseがお気に召さないようですが、

/usr/local/lib/pkgconfig isn’t writable.
This can happen if you “sudo make install” software that isn’t managed
by Homebrew.

If a brew tries to write a .pc file to this folder, the install will\nfail during the link step.

You should probably `chown` /usr/local/lib/pkgconfig

大事なのはおそらくここでしょう。
見ると osxfuse インストール時に作られたたらしいこのディレクトリは

drwxr-xr-x 4 root wheel 136 9 27 22:02 pkgconfig

と、root 権限で置かれていました。これを自分に chown し、

% brew uninstall little-cms
% brew install little-cms

としたら、案の定、/usr/local/lib/pkgconfig 内に、先程までは存在しなかった、lcms.pc というファイルが作られました。little-cms の環境がやはり不完全だったようです。
この状態で、ImageMagick も再インストールすると

% brew uninstall imagemagick
% brew install imagemagick

bundle install で RMagick のインストール時に失敗する事は無くなりました。
環境系のトラブルは面倒ですね。

Redis 使ってみました。

会社に転がってた、↓ の本をつらつらと眺めていて

今まで、よくわかっていなかった NoSQL という物に興味が湧いてきました。
上記の本は、そろそろ出揃った感のある NoSQL 系のデータストアエンジンを一通り並列に紹介してくれる本で、見て、軽くわかった気になるには最適です。

ただ、結局のところ、いつも使っている SQL と何が違うのかまでは読んだだけでは充分に理解できないよね、やっぱり試してみなくちゃね、という事で、使ってみました。

NoSQL と言えども、データストア!データを保存しなくては。入れるデータの元ネタに困りましたが、困った時は他人のふんどしを借りようと Twitter から適宜引っ張ってくる事にしました。キャズムを越えたらしいという噂の Twitter から URL を含んだツイートを引っ張りだして集計したら、世間で話題になっているネタが拾えそうかなと。誰かが勝手にデータをたれ流してくれるのは収集に持ってこいですし。この辺を思い付きだけでやってしまったのが後で祟る事になるのですが、そこは後述。

保存用のスクリプトは Ruby で書く事にしたので、Redis へのアクセスに redis の gem 、また、Twitter からデータを取ってくるのにTwitter の gem を使います。

Twitter はクライアントなどが自動的に URL を短縮した状態で投稿する事が多いので、それを元 URL に戻す処理も入れる事にしました。

#!/usr/bin/ruby

require "rubygems"
require "redis"
require 'twitter'
require 'uri'
require 'net/http'

$lang = "ja"

# 短縮 URL を元に戻すための処理
def extract_url(url)
  begin
    uri = URI.parse(url)
    # 引数で与えられた URL に対して HEAD メソッドを実行
    Net::HTTP.start(uri.host, uri.port) do |http|
      response = http.head(uri.path, 'User-Agent' => "Redirect Tracker")
      # 実URLだと判断する条件は、以下のいずれかに合致する事
      # ・レスポンスコードが200
      # ・レスポンスヘッダにLocationが含まれない
      # ・レスポンスヘッダLocationが、元URLと同一
      if (response.code == "200") or (response['Location'] == nil) or (url == response['Location']) then
        return url
      else
        moved_to = response['Location'].match(/^https?:\/\//) ? response['Location'] : "#{uri.host}/#{response['Location']}"
        # 二重に短縮されている場合などのために再帰処理
        extract_url(moved_to)
      end
    end
  rescue => e
    puts " === ERROR ( #{url} ) ==== "
    puts e
  end
end

begin
  # ローカルホストの Redis に接続
  redis = Redis.new(:host => "localhost")
  # 前回取得時の最終 Tweet ID を拾う
  last_id = redis.get "meta:last_id:#{$lang}"
  result = nil

  # 前回取得した以降の、日本語のツイートの中から、http という文字列で検索
  Twitter::Search.new.containing("http").since_id(last_id).lang($lang).per_page(100).filter.reverse_each do |result|
    # URL らしき文字列の切り出し
    result.text.scan(/https?:\/\/[-_.!~*'()a-zA-Z0-9;\/?:@&=+$,%#]+/).each do |url|

      # 短縮URL を元に戻す
      exturl =  extract_url(url)
      if (exturl != nil) then
        uri = URI.parse(exturl)
      else
        next
      end

      # 集計系は incr メソッドでアトミックに処理
      id = redis.incr "data:count:whole"
      host_count = redis.incr "data:count:#{uri.host}"
      url_count = redis.incr "data:count:#{uri.host}:#{uri.path}"

      # URL情報をリスト型で記録
      redis.lpush "list:whole", exturl
      redis.lpush "list:#{$lang}", exturl
      redis.lpush "list:hosts", uri.host

      # 念のため、ツイート情報も取っておく / Marshal.dump でオブジェクトを serialize する
      redis.set "data:tweet:#{id}", Marshal.dump(result)
    end
  end
rescue => e
  puts e
ensure
  # 最後に処理した Tweet ID を記録しておく
  redis.set "meta:last_id:#{$lang}", result.id
end

上記スクリプトを、cron で定期実行して、情報を収集しておきます。
読み返してみたら、検索結果を Reverse_each した上でリストに lpush してるけど、素直に each & rpush したらよかったんじゃないだろうか…。

表示系は Sinatra で間に合わせ。

require 'rubygems'
require 'sinatra'
require 'redis'
require 'uri'

set :haml, {:format => :html5}

before do
	@redis = Redis.new(:host => "localhost")
end

get %r{/([0-9]+|all)?} do
        # 全データ件数を取得
	@total_count = @redis.get "data:count:whole"
	@datas = []
        # リスト長を取得(アレ?上の全データ件数と同一では?)
	length = @redis.llen "list:whole"

        # データ表示件数を取得。デフォルトは 100 件で、指定があれば、その件数。all なら全件
	limit = 100
	if params[:captures] != nil then
		if params[:captures].first == "all" then
			limit = length
		else
			limit = params[:captures].first.to_i
		end
	end
        # downto に渡す都合で加工。リスト長が指定件数に満たない場合はリスト長を限界にする
	limit = length - (length < limit ? length - 1 : limit - 1)

        # 表示用データを配列に突っ込む
	length.downto(limit) do |i|
		url = @redis.lindex("list:whole", length - i)
		begin
			uri = URI.parse(url)
			count = @redis.get "data:count:#{uri.host}:#{uri.path}"
		rescue
			count = 0
		end
		@datas << { :url => url, :count => count }
	end
	haml :index
end

__END__

@@ layout
%html
	%head
		%title twitter url counter
	%body
		= yield

@@ index
%h1 twitter url counter
%a{:href => "/hosts"} host oriented
%div
	There are #{@total_count} datas here.
%div
	- @datas.each do |item|
		%div
			#{item[:count]} :
			%a{:href => "#{item[:url]}"} #{item[:url]}

所定の件数を Redis から取得して表示するだけです。

これで、こんな表示が出来るようになりました。

しかし、KVS ほぼ初体験な自分には「ほぼハッシュだからそのつもりで使えよ」という概念がどうも理解されていなかったようです。こんなランキング系のコンテンツなのにソートの機能を持たない list 型に突っ込んでいるのが致命的。ダラダラ出すのには使えますが、それ以外の表示方法の可能性がほぼスポイルされてしまいました。

上記の本の中にあったベンチマークのデータでは、”スピードの向こう側”に突き抜けそうなブッチ切りの爆速を示していた、Redis の速度も、集計時に、URL のリダイレクトを辿るケースが頻発する今回のプログラムではネットワークの遅延が問題になってしまって、あまり恩恵受けれず…。結局何のための Redis なのか、というところが全く見えませんでした。使ってみた、というのが唯一の成果。ちゃんとアドバンテージとディスアドバンテージを見極めた上で新技術を導入しましょうね!というお話でした。

次回は今回の反省を活かして、データストアを MongoDB とかにしてみる予定!!


参考文献

Hoppy を使ってみる

自分、このブログ作らせた言い出しっぺなのに記事全然書けてませんね。
文章書くの好きだけども、苦手だからか。
ずっと書かないのもなんなので、仕事で触れたネタは極力書くようにします。

今、Flash を使ってリアルタイム Web するお仕事をしてます。Flash は余り詳しくないのですが、XMLSocket という機能を使って サーバと通信する事で、それを実現出来るらしいですね。

結構昔からある技術らしくてサーバとしてのソリューションはそれなりの数が出ているらしいけれども、何がいいかはよくわからない、というところで Hoppy を使ってみる事にしました。選んだ理由は、Perl で書かれていたから。とは言え、いわゆるモダン Perl という奴も実は余り詳しくなかったりします。Perl の知識は10年以上前に CGI とか書いてた頃のままなので。とはいえ、Java とかで書かれてる他のソリューションよりは扱い易いかな、という気がしたんです。Java も不案内なので。そうするとお前何が出来るんだよ、とかいう話になりそうですが、そんな事考えると眠くなるので、弄ってみる事にします。

で、使おうと決めたらまずは資料探しなのですが、残念な事に余りヒットしない。

このくらい?

仕方ないので体当たりで使ってみる事にしました。しかし、恐しい事に「POEって何?」というところから始まる手探り感です。まずは Hoppy.pm を眺めてみます。

Hoppy クラスを new した時に _setup メソッドが呼ばれて POE の本体部分が初期化されるのだな。


POE::Component::Server::TCP->new(
	Alias => $self->config->{alias} || 'xmlsocketd',
	Port  => $self->config->{port}  || 10000,
	ClientConnected    => sub { $self->_tcp_handle( Connected    => @_ ) },
	ClientInput        => sub { $self->_tcp_handle( Input        => @_ ) },
	ClientDisconnected => sub { $self->_tcp_handle( Disconnected => @_ ) },
	ClientError        => sub { $self->_tcp_handle( Error        => @_ ) },

	ClientFilter => $filter,
	InlineStates => {
		Send => sub {
			$self->_tcp_handle( Send => @_ );
		},
	},
);
POE::Kernel->sig( INT => sub { POE::Kernel->stop } );

その前に、_load_classes メソッドで諸々の、部品として使われそうな各クラスを読み込んでいる。これは、プラガブルにするための工夫だよね。

で、そうして作った POE を start メソッドで走らせ始める、とそういう流れらしい。その程度のホントに漠然とした理解ですけど、実際に使ってみようと書いたコードが以下

test.pl

#!/usr/bin/perl

BEGIN {
	unshift(@INC, '.');
}

use Hoppy;
use Data::Dumper;

my $config = {
	alias => 'hoppy',
	port => 12345,
	test => 2,
};

my $server = Hoppy->new(config => $config );

$server->regist_service(
	broadcast => 'MyService::Broadcast',
	unicast => 'MyService::Unicast',
	multicast => 'MyService::Multicast',
);

$server->regist_hook(
	client_input => 'MyHook::ClientInput',
);

$server->start;

基本的には、作者様のスライドで示されたサンプルの猿真似。

最初に @INC を弄っているのは、同一パスにある Hoppy を CPAN で入れた物より優先ささせるため。その下で読み込んでる Data::Dumper と組み合わせて、本体のあちこちに中身どうなってるのか変数をダンプしてまわる行を足してみたかったのでこういう事をしてます。

Hoppy.pm 内には、broadcast, multicast, unicast の3つのメソッドがあったのでそれらを全て試してみることに。また、Hook の仕組みも試してみたかったので、クライアントからの入力をフックして処理を走らせるようにもしてみました。

MyService/Broadcast.pm

package MyService::Broadcast;

use base qw(Hoppy::Service::Base);
use Data::Dumper;

sub work {
	my $self = shift;
	my $args = shift;
	my $c = $self->context;
	my $in_data = $args->{in_data};
	my $poe = $args->{poe};
	my $session_id = $poe->session->ID;

	print "now broadcastting\n";

	$c->broadcast({
			sender => $session_id,
			message => $in_data->{params}->{message},
		});

	print "broadcasting done\n";
}

1;

MyService/Multicast.pm

package MyService::Multicast;

use base qw(Hoppy::Service::Base);
use Data::Dumper;

sub work {
	my $self = shift;
	my $args = shift;
	my $c = $self->context;
	my $in_data = $args->{in_data};
	my $poe = $args->{poe};
	my $session_id = $poe->session->ID;
	my $user_id = $in_data->{params}->{user_id};

	print "now multicastting\n";

	$c->multicast({
			sender => $session_id,
			room_id => $c->{room}->{where_in}->{$user_id},
			message => $in_data->{params}->{message},
		});

	print "multicasting done\n";
}

1;

MyService/Unicast.pm

package MyService::Unicast;

use base qw(Hoppy::Service::Base);
use Data::Dumper;

sub work {
	my $self = shift;
	my $args = shift;
	my $c = $self->context;
	my $in_data = $args->{in_data};
	my $poe = $args->{poe};
	my $session_id = $poe->session->ID;
	my $user_id = $in_data->{params}->{user_id};

	print "now unicastting\n";

	$c->unicast({
			sender => $session_id,
			user_id => $in_data->{params}->{target_id},
			message => $in_data->{params}->{message},
		});

	print "unicasting done\n";
}

1;

MyHook/ClientInput.pm

package MyHook::ClientInput;

use Data::Dumper;
use base qw(Hoppy::Hook::Base);

sub work {
	my $self = shift;
	my $args = shift;
	my $c = $self->context;

	print Dumper($c);
}

1;

これで、チャットサーバ的な物が出来ました。

test.pl を実行しておいて、telnet から接続し、以下のような入力を順に送るとメッセージが送れたりします。(送った本人には届かないので、テストの為には二カ所以上から telnet する必要があります)

{"method":"login","params":{"user_id":"test","room_id":"testing_room"}}
{"method":"broadcast","params":{"user_id":"test","message":"broadtestes"}}
{"method":"multicast","params":{"user_id":"test","message":"multitestes"}}
{"method":"unicast","params":{"user_id":"test","target_id":"test2","message":"unitestes"}}

Perl よくわからないながらも、とりあえず動くようになったので、もう少しいじり回してみようと思います

Ethna でフォーム定義の値を取ってくる

Ethna で下記のようなフォーム定義を使えば

'pref' => array(
       'type' => VAR_TYPE_INT,
       'form_type' => FORM_TYPE_SELECT,
       'name'  => '出身地',
       'required' => 'true',
       'option' => array(
            '01' => '北海道', '02' => '青森県', '03' => '岩手県', '04' => '宮城県', '05' => '秋田県',
            '06' => '山形県', '07' => '福島県', '08' => '茨城県', '09' => '栃木県', '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' => '沖縄県',
       ),
)

テンプレートではフォームヘルパを利用して

{form_input name=”pref”}

と書くだけで都道府県を選択する確認ダイアログが作れる。
ただ、この次の確認画面で

{$form.pref}

とだけ書いた場合に、意味の無い値が表示されてしまう。

こういう場合は、View でフォーム定義を取得して setApp しておく。

$pref_def = $this->af->getDef(‘pref’);
$this->af->setApp(‘pref’, $pref_def['option']);

すると、template では

{$app.pref[$form.pref]}

として、都道府県名のラベルと値を紐付ける事ができる。やったね!


普通にフォーム定義で上記定義使ってる画面はこれでいいけど、フォーム定義なしで、単にDBから取ってきただけの値から都道府県名を表示したい時はどうするんだろう。

プロジェクトの ActionForm ベースクラスで form_template に代入する形で冒頭のコードようなフォーム定義が行なわれていたら

$this->af->form_template['pref'];

とする事で、上記の getDef で取ってきたもの相当は取れるからこれを使えばいいのかな。
プロパティだから、2行に分けなくても

$this->af->setApp($this->af->form_template['pref']['option']);

みたいに書けるし。

CentOS 5 に Ruby 1.8.7 をセットアップする(ために checkinstall をセットアップする)

先日、弊社内の開発サーバがハードディスクトラブルにより起動不能になりました。
すぐさま別のサーバに環境を構築して引き継ぎを行なったのですが、短期間で行なった応急処置で100%完全に引き継げるはずもなく、ささいなトラブルはいくつか生じているようです。

例えば、先日のFlash Lite の中身を動的に変更するという記事に貼ってあった、サンプルの CGI が出力されなくなってしまいました。当該記事の中で入れた諸々のライブラリが、移転先サーバには入っていないせいですね。

今回はこれらをセットアップしようと思います。とはいえ、以前書いた内容をそのままやるだけだと思っていたんですが…。

まず、移転先サーバには、Ruby や RubyGems のインストールに用いた checkinstall が入っていませんでした。
CentOS 4 だった旧サーバでは、rpmforge のレポジトリを有効にしてやれば、yum コマンドで checkinstall をセットアップ出来たのに、CentOS 5になった移転先サーバではなぜか以下のように “Nothing to do” になってしまいます。
Continue reading »

Dreamweaver から Subversion を利用する

Dreamweaver は CS4 から Subversion によるバージョン管理に対応しています。
社内のデザイナさん達に、Subversion 管理を布教したいので、まずは評価してみる事にしました。

とはいえ、結構めんどくさかったです…。
社内にある Subversion は、SVN+SSH 以外のアクセス方法を提供していません。
数年前までは WebDAV もあったのですが、諸般の事情で無くなったままです。
Dreamweaver で SVN+SSH を利用するには、割と手がかかります。

以下のような情報を教科書にして設定を行ないました。

まずは、Subversioning in Adobe Dreamweaver CS4 の情報に従い、SVN+SSH を使うための事前準備を行ないます。

vi で ~/.subversion/config ファイルの、[tunnels]セクションに以下の内容を追記します。

ssh = $SVN_SSH /usr/bin/ssh

また、ssh の鍵を作っていない人は、ssh-keygen コマンドを使って鍵ペアを作って、SVN+SSH で接続する先のサーバに登録しておかなくてはなりませんが、特に特別な事もない通常の鍵生成&登録手順だったので、そこの詳細は省略します。

次に、Dreamweaver 側の設定を行ないます。
Continue reading »

Ruby で Excel を弄る

趣味です。

VBA の書き方はわからないけど、Ruby の書き方ならわかるんです。
Ruby で書いたって、win32ole 挟んだ状態で使ったら、そんなに VBA 書くのと変わんないコードになるんだけどね。

今回は、日毎に時間帯別の統計情報が記載されたテキストファイルをもとに整形された Excel ファイルを出力するのが目標。
テキストファイルは、こういう感じの内容。

集計対象名1 —–
2010/02/21 0 15
2010/02/21 1 8
(中略)
2010/02/21 22 21
2010/02/21 23 18

集計対象名2 —–
2010/02/22 0 15
2010/02/22 1 8
(中略)
2010/02/22 22 21
2010/02/22 23 18

(以下略)

最初に、

集計対象名(スペース)ハイフンx5

のヘッダが来て、それに続いて0時から23時までの

日時(TAB)時間帯(TAB)件数(TAB)

という行が繰り返されるこのセットを一日分として、数日分の内容が繰り返されて、更にそれが集計対象毎にも繰り返される。

そんなテキストデータをパースして、Excel化するのに用いたスクリプトが以下。
Continue reading »

Flash Lite の中身を動的に変更する

swfmill というツールの存在を知った。swf ファイルと XML ファイルを相互に変換するという。Flash Lite 1.1 の中身を動的に更新したいという声が社内で上がっていた(何年周回遅れで??)ので、それを試しに使ってみる事にした。

サンプルで使う Flash Lite 1.1 のファイルは以下のようなものを用意した。
手抜きだが必要充分だろう。

This movie requires Flash Player 4.0.0
QRコード
http://dev.uniba.jp/~rei/swfmill/test.swf

swfmill の利用実績は豊富にある。
まずは、Flash4 の仕様に準拠するために文字コードが UTF-8 でない Flash Lite 1.1 に対応するために下記ページで配布されているパッチを当てる必要があるらしい。

Continue reading »

WP-Al-SWFObject プラグイン

一つ前の記事で SWFObject を使いたかったので、こちらのプラグインを入れた。

でも、Open Flash Chart2 を使おうとしたら問題が起きた。

[embedSWF]/blog/wp-content/uploads/2010/02/ofc/open-flash-chart.swf,550,200,,9.0.0,,{“data-file”:”/blog/wp-content/uploads/2010/02/ofc/line-dot.json”}[/embedSWF]※最初の e は本来小文字

のように指定をして、SWFObject を呼び出すのだが、上記の指定が、

<div style="text-align: center; width: 550px; height: 200px; line-height: 200px; background: #ffffff;">
<div id="swfc0ec1">This movie requires Flash Player 9.0.0</div>
</div>
<script type="text/javascript">
	swfobject.embedSWF("/blog/wp-content/uploads/2010/02/ofc/open-flash-chart.swf", "swfc0ec1", "550", "200", "9.0.0", "http://rd.uniba.jp/blog/wp-content/plugins/wp-al-swfobject/expressInstall.swf", {&#8220;data-file":"/blog/wp-content/uploads/2010/02/ofc/line-dot.json"}, {}, {});
</script>

と展開されてしまい、3行目の &#8220; のところでパースエラーとなってしまう。

Continue reading »

Flash で グラフを書く

以前、案件で使ったライブラリについて書いておく。
Open Flash Chart 2 といって、Flash で書いたグラフをページ内に埋めこめるツール。
シンプルだけど高機能で、色んな種類のグラフが書ける。

単純なグラフはもちろん

This movie requires Flash Player 9.0.0

棒グラフ(ガラス的装飾付き)とか・・・

This movie requires Flash Player 9.0.0

円グラフ

This movie requires Flash Player 9.0.0

こういうのはローソクチャートって言うんだっけ

This movie requires Flash Player 9.0.0

公式サイトにはこの他にも     なサンプルがあるみたい。

使い方は続きで
Continue reading »

preload preload preload