Home > Programming

Programming Archive

RSpecでMacro?を書く。

  • 2010-01-08 (Fri)
  • Ruby

RSpecを使ってSpecを書いていると何度も同じことを書くことが多いです。

たとえばパラメータに応じてページタイトルを変えたい場合とか、validationがきちんと行われているかとか。。。
毎回、 it “should have hoge” do … end と書いているのは大変です。
RSpecではいくつかの方法を使用して、もっと簡単に書くことができます。

クラスメソッドを使う

一つのspecファイルで何度も使うものであればクラスメソッドにします。

[$RAILS_ROOT/spec/views/articles/index.html.erb_spec.rb]

describe "/articles" do
  def self.it_should_have_tag(selector, content)
    it "should have '#{content}' at '#{selector}'" do
      response.should(have_tag(selector, content))
    end
  end
end

と書いておけば、以下のような書き方が可能になります。

[$RAILS_ROOT/spec/views/articles/index.html.erb_spec.rb]

describe "/articles" do
  def self.it_should_have_tag(selector, content)
    it "should have '#{content}' at '#{selector}'" do
      response.should(have_tag(selector, content))
    end
  end

  it_should_have_tag('tr>td', 'MyString')
end

it_should_have_tag(’tr>td’, ‘MyString’) の部分だけで適切なspecに展開されます。
1行で簡潔に書けますので、理解する時間も短くてすみます。

自分はページタイトル、meta description、meta keywords、RSSのスペックに以下のコードを使用しています。

[$RAILS_ROOT/spec/views/articles/index.html.erb_spec.rb]

describe "/articles" do
  def self.it_should_have_tag(selector, content)
    it "should have '#{content}' at '#{selector}'" do
      response.should(have_tag(selector, content))
    end
  end

  def self.it_should_have_page_title(title)
    it_should_have_tag('head>title', title)
  end

  def self.it_should_have_meta_description(meta_description)
    it_should_have_tag('head>meta[name=description][content=?]', meta_description)
  end

  def self.it_should_have_meta_keywords(meta_keywords)
    it_should_have_tag('head>meta[name=keywords][content=?]', meta_keywords)
  end

  def self.it_should_have_rss_link(link)
    it_should_have_tag('head>link[rel=alternate][type=application/rss+xml][href=?]', link)
  end
end

モジュールに追い出す

さらによく使う項目、上記のhtmlに関するspecなどはたくさんのところに書かれるであろうことが推測されます。
そういう場合には、モジュールに追い出してしまうということが可能だということを同僚のquekさんに教えていただきました。1

$RAILS_ROOT/spec/support フォルダにモジュールのファイルを書きます。
上記の例であれば

[$RAILS_ROOT/spec/support/tag_macros.rb]

module TagMacros
  def self.included(base)
    base.extend(ClassMethods)
  end

  module ClassMethods
    def it_should_have_tag(selector, content)
      it "should have '#{content}' at '#{selector}'" do
        response.should(have_tag(selector, content))
      end
    end

    def it_should_have_page_title(title)
      it_should_have_tag('head>title', title)
    end

    def it_should_have_meta_description(meta_description)
      it_should_have_tag('head>meta[name=description][content=?]', meta_description)
    end

    def it_should_have_meta_keywords(meta_keywords)
      it_should_have_tag('head>meta[name=keywords][content=?]', meta_keywords)
    end

    def it_should_have_rss_link(link)
      it_should_have_tag('head>link[rel=alternate][type=application/rss+xml][href=?]', link)
    end
  end
end

使用する際には include TagMacros を書いておきます。

[$RAILS_ROOT/spec/views/articles/index.html.erb_spec.rb]

describe "/articles" do
  include TagMacros

  it_should_have_tag('tr>td', 'MyString')
end

自分のような include 〜 を書くのもものぐさだという人は、 $RAILS_ROOT/spec/spec_helper.rb に config.include メソッドを使用すれば、自動的に読み込まれるようになります。

[$RAILS_ROOT/spec/spec_helper.rb]

Spec::Runner.configure do |config|
  # ...
  config.include(TagMacros, :type => [:views])
  # ...
end

上記のように :type を指定すれば view だけで読み込まれるというような指定も可能です。

参考

Railscasts – RSpec Matchers & Macros
Writing Macros in RSpec • Blog Archive • Ben Mabey

P.S.
参考にしたサイトではMacroと書いてありますが、どうもこれをMacroと読んでいいものなのかどうかかなり謎なので、タイトルはMacro?と?付きにしてみましたwww

  1. quekさんによると、下の「参考」のところに書いた Railscasts に同様のことが書いてあり、そのアドレスを過去の自分は社内のチケットに書いていたらしいのですが、モジュールに出すことは全く知りませんでしたwww []

Grand Central Dispatchを試してみる。

Snow LeopardではOSの基礎部分にかなりの改良を加えたと、Appleはいいます。
その基礎部分の改良の目玉機能の一つであるGrand Central Dispatchは、マルチコアでのプログラミングを簡単に行うためのフレームワークを提供してくれます。

マルチコアでのプログラミングはいろいろと難しいことが多く自分は興味を持ったので、プログラムプレゼンテーションのネタに少し試してみることにしました。

すでに最初の一歩を踏み出された方がいます。
Grand Central Dispatch at mootoh.log
Grand Central Dispatch をためす at mootoh.log
Grand Central Dispatchを試してみる – 理想未来はどうなった?

dispatch_asyncというのは非同期に与えたブロックを実行するもので、
対してdispatch_syncというものは同期的にブロックを実行するものようです。

この同期、非同期というものを自分はきちんと理解していないのですが、Concurrency Programming Guildeというドキュメントによると。。。

dispatch_async

Submits a block for asynchronous execution on a dispatch queue and returns immediately.

dispatch_sync

Submits a block object for execution on a dispatch queue and waits synchronously until that block completes.

引用元: Grand Central Dispatch (GCD) Reference

dispatch_syncはブロックが終了するまで待ってるよという風に見えます。

上記のサンプルを参考にしつつ、ためしにこんなコードを書いてみました。

#include <dispatch/dispatch.h>
#include <iostream>
#include <stdio.h>
#include <unistd.h>

void c_hello(int count = 10)
{
	void (^hello)(int) = ^(int x)
	{
		printf("Hello, C World! %d\n", x);
	};

	for (int i = 0; i < count; ++i)
	{
		dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
					   ^{
						   hello(i);
					   });
	}
}

void cpp_hello(int count = 10)
{
	void (^hello)(int) = ^(int x)
	{
		std::cout << "Hello, C++ World! " << x << std::endl;
	};

	for (int i = 0; i < count; ++i)
	{
		dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
					   ^{
						   hello(i);
					   });
	}
}

int main (int argc, char * const argv[])
{
	c_hello(1000);
	cpp_hello(1000);

	std::cout << "done!" << std::endl;

	// sleep がないと処理が終了する前に return してしまう。
	sleep(5);

    return 0;
}

カリー化はブロックをブロックでくくればいいのでしょうか?

わざとCのprintfとC++のstd::coutの両方を書いてみました。
面白かったのが1000回ループをさせたときでした。
10回では起こりませんでしたが、回数が増えるにつれ、C++の方はこんな風にごちゃ混ぜになりました。

Hello, C++ World! Hello, C++ World! Hello, C++ World! 974975976977978

ちょっと考えてみると、<< 演算子で何個ものメソッドが入り組んでいる分、多数のスレッドが起動されるとこうなるのでしょう。

実際にプログラムプレゼンテーションで見せて、なにか簡単ないいサンプルがないと話してみたところ、g000001さん疑似並行処理 どう書く?orgという課題をやってみてはどうか?というアドバイスをいただきました。

これがその解答になるかと思います。

#include <dispatch/dispatch.h>
#include <stdio.h>
#include <unistd.h>

int main (int argc, char * const argv[])
{
    void (^alphabet)(int x) = ^(int x)
    {
        putchar('a' + x);
        putchar(' ');
    };

    void (^number)(int x) = ^(int x)
    {
        printf("%d ", x);
    };

    for (int i = 0; i < 26; ++i)
    {
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        dispatch_async(queue, ^{ alphabet(i); });
        dispatch_async(queue, ^{ number(i); });
    }

    printf("done!\n");
    sleep(5);

    return 0;
}

結果は次のようになって、アルファベットと数字が無事混ざって表示されました。

done!
a 0 b 1 c 2 d 3 e 4 f 5 g 6 h 7 i 8 j 9 k 10 l 11 m 12 n 13 o 14 p 15 q 16 r 17 s 18 t 19 u 20 v 21 w 22 x 23 y 24 z 25

これからは共有リソースをどうやって扱うことができるのか調べたいところです。

P.S.
スレッド関連の用語を知らないので、適当に書いたところが多々あります。
うまく説明できないのが非常にもどかしいところです。

ssl_requirement pluginでssl_required(:all)と書きたい!

  • 2009-08-27 (Thu)
  • Ruby

Ruby on RailsでSSLで通信する必要があるページには、DHH謹製らしい?ssl_requirement pluginを使用すると、以下のようにに簡単にhttpsにリダイレクトしてくれるようにできます。

class Hoge < ApplicationController
  ssl_required(:index)  # インデックスページだけSSLで通信する。

  def index
    # some crazy codes ...
  end

  def show
    # some rotten codes ...
  end
end

残念ながら、このssl_requiredはアクションに:allとか書いてコントローラ全部を対象にするというようなことができません。
allというアクションを定義する場合もあるからこうなっているのかもしれません。

コントローラ全体にSSL通信をさせるようにするための方法については、コントローラ全体をssl強制に – 車輪の再発明というサイトに、trueを返すssl_required?メソッドを定義する方法が書かれています。

現在書いているRailsアプリでも同様のことが行われているものの、いかんせんたびたび見る気がしてきて、クラスメソッドにしようかと思いました。
でも、すでにssl_requiredというメソッドがあるところに、さらなる同じようなメソッドを作るのは避けたいし、やっぱりssl_required(:all)と書きたいと思いました。

そこでalias_method_chainを使用して、ApplicationControllerをこんな風にしてみました。

class ApplicationController < ActionController::Base
  include SslRequirement

  # some codes...

  class << self
    def ssl_required_with_all(*actions)
      if actions.include?(:all)
        class_eval do
          def ssl_required?
            true
          end
        end
      else
        class_eval do
          ssl_required_without_all(*actions)
        end
      end
    end
    alias_method_chain(:ssl_required, :all)
  end
end

まともにclass_evalとかがわかっていないので、かなり怪しいです!!!

Apple Push Notification Serviceを利用した、iPhone クライアントと、Rubyによるサーバの作成。

[とりあえず表示しましたが、これからコードをアップロードしたり改変していきます。]

先週から、システム部で何か動くものを作って発表するという企画?が始まりました。
プログラムプレゼンテーションを始めてみたら意外に良かった

第一弾は自分だったので、1週間しかなかったので、ちょこっとやればできそうで今までやってみたかったiPhone OS 3.0の目玉機能の一つ、Push Notification Serviceの実装をやってみることにしてみました。

iPhoneクライアントはほぼObjective-Cしかありませんのでそれを使い、サーバはRubyで実装しました。

以下が発表したスライドを少し改変したものになります。

コードは今回はbitbucket.orgにアップロードすることにしました!
コードはこちらになります。
http://bitbucket.org/milkcocoa/apns-test/

ただしサーバのファイルに関しては

  • デバイスIDとかの部分はxでマスクしています。
  • キーに関してはスライドで指定した方法で記述してください。

[ポイントを書く。]

mislav-will_paginate pluginを使用したページをRSpecでテストする。

  • 2009-07-19 (Sun)
  • Ruby

Webアプリケーションでは、たくさんの項目を一覧表示する場合、1ページに全部見せるのではなくpaginate処理をして数ページに分ける場合があります。
たとえばGoogleの検索結果は通常、1ページに10件しか表示されません。

そういうページをRuby on Railsで作成する場合には、mislav-will_paginate pluginというプラグインを使用するということは割とよく知られていることです。

先日もそういうページを作成する必要があり、作成していましたが、RSpecでテストを作成しようとしたところやり方がわからず詰まりました。
そのため、その方法をここに記しておこうと思います。

ドキュメントを読もう!

実はGitHubのpluginのWikiページに最低限必要なことは書いてありました。
気がついていればもっと早く解決していたところでした。
「I’m getting “undefined method `total_pages’” error when rendering in the view!」という部分ですね。

ポイントはpaginateやtotal_pagesというメソッドをstub!で定義してあげることです。

実際のコード

  • Controller: ArticlesController
  • Model: Article
    • title: string
    • body: text

Controllerのテスト(の一部)

[$RAILS_ROOT/spec/controllers/articles_controller_spec.rb]

require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')

describe ArticlesController do
  describe "handling GET /articles" do
    before(:each) do
      article = mock_model(Article)
      article.stub!(:find).and_return([article])

      @articles = [article]
      @articles.stub!(:total_pages).and_return(1)
      @articles.stub!(:paginate).and_return(@articles)
    end

    def do_get
      get(:index)
    end

    it "should be successful" do
      do_get
      response.should be_success
    end

    it "should render index template" do
      do_get
      response.should render_template('index')
    end

    it "should find all articles" do
      Article.should_receive(:paginate).with(:page => nil, :per_page => 100).and_return(@articles)
      do_get
    end

    it "should assign the found articles for the view" do
      do_get
      assigns[:articles].should == @articles
    end
  end
end

ちなみに29行目の:page => nilになっているのは、たぶんassingsとかでparameterを指定していないからこうなるのだと思います。
まだshould_receiveをきちんと理解していないで、まともな説明が書けません。

Viewのテスト(の一部)

[$RAILS_ROOT/spec/views/articles/index.html.erb_spec.rb]

require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')

describe "/articles/index.html.erb" do
  include ArticlesHelper

  before(:each) do
    article_98 = mock_model(Article)
    article_98.stub!(:id).and_return(1)
    article_98.stub!(:title).and_return('MyString')
    article_98.stub!(:body).and_return('MyText')

    article_99 = mock_model(Article)
    article_99.stub!(:id).and_return(2)
    article_99.stub!(:title).and_return('MyString')
    article_99.stub!(:body).and_return('MyText')

    @articles = [article_98, article_99]
    @articles.stub!(:total_pages).and_return(1)
    @articles.stub!(:paginate).and_return(@articles)

    assigns[:articles] = @articles
  end

  it "should render list of articles" do
    render "/articles/index.html.erb"
    response.should have_tag("tr>td", "MyString", 2)
    response.should have_tag("tr>td", "MyText", 2)
  end
end

RubyでArrayの部分集合を作成する。

  • 2009-07-10 (Fri)
  • Ruby

Arrayの部分集合を作成する必要があったので、作ってみました。

class Array
  def subset
    rec = Proc.new do |univ|
      # subset!でポインタが循環しないようにcloneを使う。
      (ret = []) << univ.clone
      univ.each do |e|
        ret += rec.call(univ - [e])
      end
      ret
    end

    rec.call(self).uniq
  end

  def subset!
    self.replace(self.subset)
  end
end

他には同じサイズの配列を作成して、その要素を使用するかどうかtrueかfalseのフラグを立てていくという手法もあるようです。

irbで実行するとこうなります。

>> a = %w(1 2 3 4)
=> ["1", "2", "3", "4"]
>> a.subset
=> [["1", "2", "3", "4"], ["2", "3", "4"], ["3", "4"], ["4"], [], ["3"], ["2", "4"], ["2"], ["2", "3"], ["1", "3", "4"], ["1", "4"], ["1"], ["1", "3"], ["1", "2", "4"], ["1", "2"], ["1", "2", "3"]]

2009/7/20 追記

twitterですばらしい方法を教えていただきました!

ただしRuby 1.8.7で追加されたcombinationというメソッドを使用しているので1.8.7以降限定です。
cf. Ruby 1.8.7で使えるようになったRuby 1.9のメソッドたち – (rubikitch loves (Emacs Ruby CUI))

class Array
  def subset
    (0..self.length).inject([]) do |ret, n|
      ret.push(*self.combination(n))
    end
  end
end

irbでの実行結果。

>> a = %w(1 2 3 4)
=> ["1", "2", "3", "4"]
>> a.subset
=> [[], ["1"], ["2"], ["3"], ["4"], ["1", "2"], ["1", "3"], ["1", "4"], ["2", "3"], ["2", "4"], ["3", "4"], ["1", "2", "3"], ["1", "2", "4"], ["1", "3", "4"], ["2", "3", "4"], ["1", "2", "3", "4"]]

すごくすっきりしました!

レセプトオンライン送信で請求ができたかを確認するRubyスクリプト

すごいニッチなところをすみませんwww。

レセプトオンライン送信について知っている人だけをまずは対象にしています。

複数の店舗のレセプトを一括してオンライン請求するときにきちんと請求済みになっているかどうか1 を(念のため)確認するのは2(社保、国保) × 店舗数分やらないといけないので大変です。
特にEeePCを使っていると遅いので、だいぶん大変度が増します。
ということで、Rubyでオートマティックに確認するスクリプトを書いてみました。
神奈川県の事例しか知らないので神奈川県限定です。
スクリプト中の神奈川県に関する部分を修正すれば、ほかの県でももしかしたら利用できるかもしれません。
またHTMLを解析して、やっていますのでサイトの構成やHTML等が変わってしまった場合には利用ができません。

共有はしますが、ノーサポートです。
私はこのスクリプトを使用して生じた一切の責任を負いません。

ライセンスをどうしようかと思いましたが、どのみちスクリプトですから改変しても公開せざるを得ないと思うので、GPLv3にしておきます。2

動作環境は

  • Windows3
  • Ruby 1.8.7
  • RubyGems 1.3.2
    RubyGemsによってインストールした

    • WWW::Mechanize 0.9.2
    • Term::ANSIColor 1.0.3
    • Win32Console 1.2.0
    • およびそれらの依存ライブラリ(Nokogiri等)

です。
これらのインストールに関する質問はお答えできませんので、あらかじめご了承を。

さらにopenssl(UNIX系だと普通にあると思われる4 )を用いて配布されているp12形式の証明書ファイルからcerファイルとkeyファイル(この二つをいまいちわかっていない)を作成します。

パスワードの入力を求められますが、これは証明書発行時に発行されたパスワードになります。

openssl pkcs12 -in hoge.p12 -clcerts -out hoge.cer -nokeys
openssl pkcs12 -in hoge.p12 -clcerts -out hoge.key -nocerts -nodes

さらに店舗情報としてYAMLファイルを作成します。
名前は適当につけていただいて、ファイルの中身は

- name: hoge
  id: 0120444444
  password: hogepass
  cert: hoge.cer
  key: hoge.key
- name: piyo
  id: 0120888888
  password: piyopass
  cert: piyo.cer
  key: piyo.key

とかしてください。

nameは任意の区別できる名前にします。
id, passwordはその薬局がログインするときのIDとパスワードです。
certとkeyは上記で作成したcertファイルとkeyファイルのパスを指定します。
後のオプションはきっとわかると思います。
このときWindowsでパス区切りに使われるバックスラッシュ(\)はうまく動いてくれなかったように思えるので、スラッシュでパスを区切りました。

あとは

ruby receipt_check.rb hoge.yaml

とでも打ち込めば、請求済みであれば緑色の字で[OK]、請求済みになっていない場合には[Failed]と表示されます。

ruby receipt_check.rb -t hoge.yaml

のように -t オプションをつけると、確認試験で実行します。

さらに自分は上記コマンドを呼び出すバッチファイルを作成し、それをcmd.exeで呼び出すリンクファイルを作ってリンクファイルをクリックするだけで確認できるようにしてあります。

TODOはそこにも書いてありますが、エラー処理を全くしていません。
そのため運用時間外などに実行すると、運用時間外ですとか知らせることなく、思い切りスクリプトのエラーが出ます。
もしやる気があれば、未請求になっていた場合にはスクリプトで請求できるようにしたり、もうスクリプトだけで請求するということも考えています(こちらは実現が難しそう)。

あと、できるだけ修正はしたいと思いますが、最終的なデバッグはレセプトオンライン送信の端末でしかできないので、修正まで非常に時間がかかる場合、修正しない場合があります。

ということで、以上を理解した方のみダウンロードはこちらから。

  1. 送信してから、請求するボタンを押さないといけません []
  2. 正直ここらは返るかもしれません []
  3. 今から考えると確認するだけならWindowsである必要はありませんね。 []
  4. MacでやったのでWindowsだとわからない []

Xcodeで0xE8000001エラー(iPhone OS 2.2.1)

iPhone OS 2.2.1をインストールしたiPhoneにXcodeで作成したプログラムを転送しようとすると、

Your mobile device has encountered an unexpected error (0xE8000001)
Try disconnecting and powering off the device; then power the device on and reconnect it.

などというエラーが発生し、XcodeがiPhoneを認識しないという状況に陥ります。

これの解決策は

/var/mobile/Media/PublicStaging

を削除し(jailbreakする必要があります)、再起動することです。

なぜ発生するのかは、今の自分にはよくわかりません(jailbreakしたから???)。

WP-Analog: IE6で閲覧すると右上に「アナログ」と表示されるWordPressプラグイン 〜 初めてのWordPressプラグイン作り

ネタが古い感がありますが、Internet Explorer 6だと右上に「アナログ」という画像が表示されるようにしてあります。

当サイトではこのように表示されます。1
この画像はIE NetRenderer – Browser Compatibility Check -を使って取得しました。

Internet Explorer 6で表示した場合

ネタ元はIE6で閲覧すると。|CSS HappyLifeです。

CSS Happy Lifeで発表後Javascriptを使用して、埋め込むようにするものも登場しました。
IE6でアクセスしたらページ右上に「アナログ」と表示するJavaScript – The blog of H.Fujimoto

今までWordPressのプラグインを作ったことがなく、 せっかくなので上記のJavascriptを利用して、学習がてらWordPressのプラグインにしてしまうことにしました。

WordPressのプラグイン作り

WordPressのプラグインを作るに当たり、元々付属しているhello.phpを参考にしました。
WordPressのプラグインはおろか、PHPのコーディングをするのが初めてだったので、たった数行だったのに色々とはまりました:-P2

1. プラグイン作りの準備

まず最初にanalogDisp.jsをフォルダに入れます。
今回は、/wp-content/plugins/wp-analogというフォルダに入れました。 

2. PHPのコードの記述

WordPressのプラグイン作りのポイントは、アクションとフィルタです。
SPaiS – Wordpress プラグインの作り方(1)というサイトに詳しく記されています。

この場合、必要なのはブログのヘッダにJavascriptを指し示す、scriptタグを埋め込むことです。
つまり、WordPressがヘッダを作成する際に、scriptタグを埋め込む関数を実行すればよいということになります。
ということで、add_actionを以下のように使用する訳です。 

<?php
/*
Plugin Name: WP-Analog
Plugin URI: http://www.cocoalife.net/wp-analog/
Description: This is the WordPress plugin which shows "アナログ" when a browser is IE 6.
Author: milkcocoa
Version: 1.0
Author URI: http://www.cocoalife.net/
*/

function add_analog_js() {
    echo("\t<script type=\"text/javascript\" src=\"" . get_bloginfo('wpurl') . "/wp-content/plugins/wp-analog/analogDisp.js" . "\"></script>\n");
}

add_action('wp_head', 'add_analog_js');

?>

これで、ヘッダが作成される際に、add_analog_js()という関数が呼び出されるようになります。

add_analog_js()関数ではechoを使って、scriptタグを埋め込みをしています。
get_bloginfo(’wpurl’)でWordPressのSettings – General - WordPress address (URL)という項目で指定されるWordPressのURLを取得しています。
さらに、”.”を用いて文字列を連結しています。

一つはまった?(というほどでもないけれど)のはPHPの言語仕様で、Rubyと同様に一重引用符ではタブ文字を表す\tなどのバックスラッシュ記法が展開されないということです。
ですので、二重引用符を使用します。

さて、これをwp-analog.phpとでもして保存し、先ほどのフォルダにアップロードしましょう。
そしてアクティベートすれば終了です。 

と、かなり丁寧にWordPressのプラグインとPHPについて説明しました(つもりです)。

  1. それにしても、タイトルの部分と中身の部分の幅が違って表示されるんですね・・・。 []
  2. functionをfuctionと書いていてずっとエラーの原因がわからなかったのは内緒だ。 []

Ultraviolet Syntax Highlighting Engineをさくらインターネットのサーバにインストールする

UltravioletはRubyによって書かれた様々なSyntax(文法)に対応したSyntax Highlighting Engineです。
今回はこれをさくらインターネットのサーバにインストールしてみましょう。

RubyGemsを使ってインストールをするのですが、以前RubyGemsのインストール方法については書きました。
cocoa*life – ブログをさくらインターネットへ移行しました。

ということで、RubyGemsのインストールが終わっている状態で書き始めます。

Ultravioletのインストール

正規表現ライブラリOniguruma(鬼車)のインストール

まずは正規表現ライブラリであるOnigurumaをインストールします。
普通にconfigureしてmakeしてinstallすれば良いだけです。

wget http://www.geocities.jp/kosako3/oniguruma/archive/onig-5.9.1.tar.gz
tar zxf onig-5.9.1.tar.gz
cd onig-5.9.1
./configure --prefix=$HOME/local
make
make install

RubyGemsを使ってUltravioletのインストール

一直線に答えを書いてしまってもいいのですが、問題解決法を書くのも忘れてしまう自分のためになると思うので、書きます。

gem install -r ultraviolet

とすればいいと思いきや、こんなエラーが出て止まります。

oregexp.c:2:23: oniguruma.h: No such file or directory

どうも標準ではない/home/hoge/local/以下にインストールしてあるので、oniguruma.hなどが見つからないようです。
さらに、エラーの最後を見ていると

Stop in /home/hoge/local/gems/oniguruma-1.1.0/ext.

なんて書いてあって、 どうも依存関係によりまず他のライブラリをインストールしているよう。
調べてみるとOniguruma for Rubyというものらしい。

そこでまず~/local/gems/oniguruma-1.1.0というフォルダを見てみると、Rakefileがある。
さらに中身を読んでみると、30 〜 36行目に以下のような記述があります。

if ENV['PLATFORM'] =~ /win32/
   p.lib_files = ["win/oregexp.so"]
   p.spec_extras[:require_paths] = ["win", "lib", "ext" ]
   p.spec_extras[:platform] = Gem::Platform::WIN32
else
   p.spec_extras[:extensions] = ["ext/extconf.rb"]
end

どうも、ext/extconf.rbがくさそうだという気がしたので、ext/extconf.rbを見てみます。

require 'mkmf'
have_library("onig")
$CFLAGS='-Wall'
create_makefile( "oregexp" )

たった4行!
ですがcreate_makefileでMakefileを作成しているというのは読み取れますので、何かありそうです。
そしてどうも、require ’mkmf’というのが鍵を握っている様な気がしました。
mkmf – Rubyリファレンスマニュアル

Ruby の拡張ライブラリのための Makefile を作成するライブラリです。

mkmf – Rubyリファレンスマニュアル

ということで、ビンゴでしょうか?
さらに読み進めると 

–with-opt-dir=directory
ヘッダファイル、ライブラリファイルを探索するディレクトリ directory/include、directory/lib をそれぞれ追加します。  

mkmf – Rubyリファレンスマニュアル

と書いてあるので、これを指定できればいいということになります。
ということで、

gem install -r oniguruma -- --with-opt-dir=$HOME/local
gem install -r ultraviolet

として、これでめでたくインストールが完了します。1

RubyGemsに慣れている人であれば当たり前のことなのかもしれませんが、自分は初めてだったので良い経験になりました。

Ultravioletの使い方

uvというコマンドがUltravioletのコマンドになりますが、uv –helpでヘルプを見てみると

uv -t amy -h ~/.bashrc > bashrc.html

なんてのがサンプルとして書いてあります。
amyというのはテーマの名前のようです。

テーマはUltraviolet Theme Galleryで見ることができます。
-sでSyntaxを指定することができますが、ある程度は自動的に認識されるのかもしれません。

uv -l syntax

で利用可能なSyntax一覧を表示することが可能です。 

自分は.zshrcをやってみました。
Syntaxが認識されないので、-s shell-unix-genericというのを指定しました。

uv -t blackboard -s shell-unix-generic -h ~/.zshrc > zshrc.html

生成されたzshrc.htmlはcssを参照しているところがあるのですが、そのcssは
/home/hoge/local/gems/ultraviolet-0.10.2/render/xhtml/files/cssにあります。

Rubyからもライブラリとして使うことができるようです。
Usageのところを見ると書いてあります。

  1. 試してはいないけれど、gem install -r ultraviolet — –with-opt-dir=$HOME/localでうまくいくのかなぁ? []

Home > Programming

Feeds
Meta

Return to page top