RubyでInteger値を持つEnum、Inumを作ってみた。
この記事はバージョン2.x系の記事で、現行バージョンの3.xとは互換性がありません。
RailsのEnumと相性が悪い人向けのInumをバージョンアップさせた話。 - 波打際のブログさん
こちらの記事がバージョン3.x系の記事となります。
はじめに
最近Rubyを使用する機会が増えてきたのですが、Rubyに列挙型の実装がありませんでした。そこで、Gemを探してみたものの、C言語ライクなInteger値を持つEnumで、思ったとおりの事が可能なGemが存在しなかったので、自前で実装する機会がありました。その実装の仕組みを参考に汎用的に使用可能なEnum(Inum)を作成してGemとして公開しました。
- 2013年11月28日、機能追加に伴い追記しました。
- 2013年12月03日、バージョン2.0.0リリースに伴い記事を修正しました。
- 2013年12月10日、バージョン2.0.5リリースに伴い記事を修正しました。
使い方
列挙型の定義
Inum::Baseを継承したクラス内で、define_enumメソッドを呼び出して列挙型を定義します。define_enumの第一引数には列挙型のラベル(名前)をSymbolで、第二引数には列挙型の値をIntegerでそれぞれ渡します。第二引数の列挙型の値は省略可能で、省略された場合0から自動的にインクリメントされます。
class AnimeType < Inum::Base define_enum :NyarukoSan, 0 define_enum :KiniroMosaic, 1 define_enum :KillMeBaby, 2 end
列挙型の使用方法
Inumを継承した列挙型は次のように使用出来ます。
p AnimeType::NyarukoSan # => NyarukoSan p AnimeType::NyarukoSan.to_i # => 0 # 数値や、シンボルから列挙型へのパーズが可能です。 type = AnimeType::parse(1) p type.equal?(AnimeType::KiniroMosaic) # => true # eql?メソッドを使用すると、比較対象のオブジェクトをパーズ後比較します。 p AnimeType::KillMeBaby.eql?('KillMeBaby') # => true # underscoreメソッドを使用すると、Enumのラベルをアンダースコアつなぎの文字列にします。 p AnimeType::NyarukoSan.underscore # => nyaruko_san # +と-の単項演算子も定義されています。 p AnimeType::NyarukoSan + 1 # => KiniroMosaic p AnimeType::KillMeBaby - 1 # => KiniroMosaic # eachによる繰り返しもできます。 AnimeType::each do |enum| p enum end
Enumerableモジュールをextendし、Comparableモジュールをincludeしていますので、mapなどのメソッドが利用可能です。
その他のメソッドについてはドキュメント(Class: Inum::Base — Documentation for alfa-jpn/inum (master))を参照してください。
その他の使い方
i18nを利用する
Enum(Inum)で to_t メソッドを利用することで、i18nでローカライズされた文字列を取得することが可能です。
次のような多言語化ファイルが存在した場合
ja: inum: anime_type: nyaruko_san: "這いよれ!ニャル子さん" kiniro_mosaic: "きんいろモザイク" kill_me_baby: "キルミーベイベー"
translateメソッドを用いると次の結果になります。
p AnimeType::NyarukoSan.translate # => 這いよれ!ニャル子さん p AnimeType::KiniroMosaic.translate # => きんいろモザイク p AnimeType::KillMeBaby.translate # => キルミーベイベー
i18nの辞書定義はデフォルトでは inum.{名前空間(モジュール名)}.{クラス名}.{メンバー名} を参照しますが、i18n_keyクラスメソッドをオーバーライドすることで参照先を書き換えることも可能です。
class AnimeType < Inum::Base define_enum :NyarukoSan, 0 define_enum :KiniroMosaic, 1 define_enum :KillMeBaby, 2 # i18n_keyをオーバーライド(内容はデフォルトの定義です。) def self.i18n_key(label) # underscoreメソッドでキャメルをアンダースコアつなぎにした上で、::を.に置き換えています。 Inum::Utils::underscore("inum::#{self.name}::#{label}") end end
Enumをクラスの変数に結びつける
DefineCheckMethodモジュールを利用すると、クラスの変数とEnumを関連付けし、判別用メソッドを自動的に定義することが可能です。
class Anime extend Inum::DefineCheckMethod attr_accessor :type define_check_method :type, AnimeType end
define_check_methodにカラム名と関連付けさせるInumを継承したEnumクラスを渡して判別用メソッドを自動的に定義しています。
anime = Anime.new anime.type = AnimeType::NyarukoSan p anime.nyaruko_san? # => true p anime.kiniro_mosaic? # => false # typeにはパーズ可能なメソッドなら何でも入ります。 anime.type = 1 p anime.kiniro_mosaic? # => true
また、define_check_methodの第三引数は省略可能オプションになっていて、prefixの設定が可能です。prefixを指定すると定義されるメソッドの先頭にprefixが付与されます。
class Anime extend Inum::DefineCheckMethod attr_accessor :type define_check_method :type, AnimeType, 'anime' end
prefixにanimeを指定すると、定義されるメソッドは次のようになります。
anime = Anime.new anime.type = AnimeType::NyarukoSan p anime.anime_nyaruko_san? # => true
おわりに
何か改善案あればpullrequest、issue気軽に投げてください、githubアカウントお持ちでない方はこのブログのコメント欄にもお気軽にどうぞ。
関連リンク
rbenv-railsを使用して、バージョン指定したrailsプロジェクトを30秒で生成する。
はじめに
以前、Bundlerを使ってローカルなRailsから新規プロジェクトを作る 方法を紹介しましたが、プロジェクトを生成するたびに実行するのは結構面倒です。そこでこの作業を自動的に行うシェルスクリプトを書いたので紹介します。 → 追記:せっかくなので rbenv のプラグインにしました。
インストール
rbenvのpluginsフォルダにrbenv-railsをgit cloneします。
# rbenvのプラグインフォルダにクローンする。
$ git clone https://github.com/alfa-jpn/rbenv-rails.git ~/.rbenv/plugins/rbenv-rails
インストールは以上です。
使い方
引数
rbenv rails <プロジェクト名> <railsのバージョン>
実行例
# rails 4.0.0 を使用して example_project を作成する。 rbenv rails example_project 4.0.0
もし、途中でsqlite3関連のエラーが出た際は libsqlite3-dev をパッケージマネージャからインストールしてください。
Playframework2.1のCSRF対策
はじめに
Playframework2.1では新機能としてCSRF対策が追加されたそうです。そもそもCSRF対策の機能はPlayframework1.x系には存在した機能ですが、2.0では機能そのものが消えていました。当然といえば当然の復活です。
公式の新機能紹介( Highlights )にも New Filter API and CSRF protection と大きく取り上げています。
新機能紹介のページを読み進めていくと、
The filters project that is part of the standard Play distribution contain a set of standard filter, such as the CSRF providing automatic token management against the CSRF security issue.
http://www.playframework.com/documentation/2.1.x/Highlights
素晴らしい!これでCSRF問題の苦悩から解放される・・・!と、感動すると同時にCSRFに関する説明はここで終わっていることに気づきます。
そうです、肝心なhow toや関連するリンクが一切ないのです!!
と、言うことで必死にぐぐって使えるようになったので結果をまとめておきます。
編集するファイル
project/build.scala
filtersを依存関係に追加します。appDependenciesの部分を下記のように編集します。
val appDependencies = Seq( // Add your project dependencies here, javaCore, javaJdbc, javaEbean, filters //←この行を追加 )
app/Global.java
アプリケーション全体の設定などを記述するGlobalクラスに、次のメソッドを追加します。Globalクラスは初期状態では存在しないので生成して、app直下(eclipse等では、default packages)に配置します。 Global.javaに関する詳しい記述は JavaGlobal を参照してください。
import play.*; import play.api.mvc.EssentialFilter; import play.filters.csrf.CSRFFilter; public class Global extends GlobalSettings { @Override @SuppressWarnings({"rawtypes", "unchecked"}) public <T extends EssentialFilter> Class<T>[] filters() { Class[] filters = {CSRFFilter.class}; return filters; } }
filters()でCSRFFilterのクラスを返すことで、リクエストをインターセプトした際に、フィルターが呼び出されて処理が行われるみたいです。
使い方
設定が終わったので、あとはフォームに設定を適用するだけで自動的にCSRF対策が行われます。具体的には helper関数を利用してフォームの宣言を行う際のactionに helper.CSRF関数を使用します。
@helper.form(action = helper.CSRF(routes.NyarukoController.postSanti)) { }
こんなかんじです。
Bundlerを使ってローカルなRailsから新規プロジェクトを作る
はじめに
本項では、ローカルなRailsから新規プロジェクトを作成し、gitで管理を行います。グローバルな環境にRailsをインストールしないことで、複数のバージョンのRailsを使い分けしやすくなります。
環境
- CentOS6.4
- rbenvを使って3分でRubyをインストールする方法まとめ。 - 波打際のブログさん を元にRubyがインストール済みであること。
Bundlerのインストール
Bundlerとは、RubyGemsのラッパーで、Gemのバージョン管理ツールです。Bundlerを使うことで複数の環境下で依存関係の解決が容易にできます。
# rbenvを使用してBundlerをインストール。 $ rbenv exec gem install bundler # rehashする。 $ rbenv rehash # インストールの確認。 $ rbenv exec bundle --version Bundler version 1.3.5
Bundlerを使ってローカルなRailsからプロジェクトを作る。
これ以降の手順を自動化するシェルスクリプトを書きました。詳細は バージョンを指定したRailsプロジェクトを自動で生成する。 - 波打際のブログさん を参照してください。
プロジェクトを生成したいディレクトリにGemfileを生成する。
$ bundle init
Gemfileの編集
initすると、カレントディレクトリにGemfileが生成されるので、それを編集してGemfileの依存関係にRailsを追加します。
$ echo 'gem "rails", "4.0.0"' >> Gemfile
Bundlerを利用してローカルにRailsをインストール
bundle installコマンドで、Gemfileの依存関係が解決されるのでRailsがインストールされます。
$ bundle install --path vendor/bundle
Railsプロジェクトをgitで管理できるようにする。
生成したRailsプロジェクトをgitで管理できるようにします。
依存関係の解決
生成されたRailsプロジェクトを使用可能にするために、bundle installを実行して、依存関係を解決します。
Nyaruko $ bundle install --path vendor/bundle
.gitignoreの設定
依存関係の解決のために生成されたファイル類は、gitにpushすべきものではないので、無視するように.gitignoreを修正します。
Nyaruko $ echo '/vendor/bundle' >> .gitignore
後はaddしてcommitすれば、gitで管理されたRailsプロジェクトの生成が完了です。
rbenvを使って3分でRubyをインストールする方法まとめ。
はじめに
最近モダンなrbenvを使って、3分の作業時間でRubyをインストールできたのでその方法をまとめておきます。
環境
- CentOS6.4
- SSHでgit(git-hub)が利用できる環境(本項では解説しません。)
rbenv
rbenvのインストール
インストールは非常に簡単で、公式リポジトリ(GitHub - rbenv/rbenv: Groom your app’s Ruby environment)をgitクローンしてbashにパスを通して、bash起動時に初期化されるように設定するだけで完了です。
# gitでローカルの ~/.rbenv にクローンを作る。 $ git clone git@github.com:rbenv/rbenv.git ~/.rbenv # bashにパスを通す。(ubuntuの場合は .bash_profile ではなく .profile) $ echo 'export RBENV_ROOT="$HOME/.rbenv"' >> ~/.bash_profile $ echo 'export PATH="$RBENV_ROOT/bin:$PATH"' >> ~/.bash_profile # bash起動時にrbenvの初期化を行うように設定。 $ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile # シェルの再起動。 $ exec $SHELL -l # インストールできているか確認。 $ rbenv --version rbenv x.x.x-xx-xxxxxx # バージョンは環境によって異なります。
Apacheのログ設定まとめ
バーチャルホスト毎にログを記録する
バーチャルホストで複数のサービスを運営している際に、それぞれのホスト毎にログファイルを分けると管理が容易になります。
バーチャルホスト設定の編集
設定はとても簡単で、バーチャルホストの設定(conf/extra/httpd-vhosts.conf)にCustomLog(アクセスログ)とErrorLog(エラーログ)の設定を記述するだけです。
<VirtualHost *:80> DocumentRoot "/htdocs/nyaruko" ServerName www.nyaruko.cqc CustomLog logs/nyaruko_server_access_log common ErrorLog logs/nyaruko_server_error_log </VirtualHost>
ログのローテートを行う
日別や週別、月別などにログを分けて保存することをログのローテートと言います。ログのローテート方法はいくつか有りますが、今回はapacheに付属しているrotatelogsを利用する方法と、logrotateを利用する方法の2つを紹介します。
rotatelogsを利用する
apacheに付属しているログのローテートツールです。ログの出力先としてパイプすることでログを分けて管理することができます。
CustomLog "|/usr/local/apache2/bin/rotatelogs /usr/local/apache2/logs/nyaruko_access_log_%Y%m%d 86400 540" common
第一引数
ログの出力先を記述します。今回は /usr/local/apache2/logs/nyaruko_access_log_%Y%m%d と記述しているので、logs/nyaruko_access_log_20130814 という形式で出力されます。
第二引数
ログの周期を記述します。86400sec=24h
第三引数
時差を記述します。540min = 9h GMT(日本標準時)
logrotateを利用する
CentOS6.4に標準で搭載されているlogrotateを利用すると、apacheに限らず様々なログを容易にローテートできます。 /etc/logrotate.d ディレクトリ以下に、新たにファイルを作成して設定を記述することでローテートが可能です。今回は /etc/logrotate.d/httpd-nyaruko という名前でファイルを作成して設定を記述しました。
# 対象のログファイル /usr/local/apache2/logs/nyaruko_access_log { # ローテートの実行方式 # 移動&作成 : create # コピー : copy # コピー&削除 : copytruncate create # ログファイルが見つからない際の挙動 # エラーを吐く : nomissingok # エラーを吐かない : missingok missingok # ログファイルが空の際の動作 # 空でも実行 : ifempty # 空の場合は実行しない : noifempty notifempty # ローテーションの頻度 # 日単位 : daily # 週単位 : weekly # 月単位 : monthly daily # 生成されるログファイル数の上限 rotate 7 # 圧縮設定 # 圧縮 : compres # 未圧縮 : nocompress # 遅延圧縮 : delaycompress compres # ローテート時にスクリプト実行 sharedscripts postrotate /sbin/service httpd reload endscript }
apache+logrotateではローテート実行時に、sharedscripts設定を記述して再起動を行います。再起動しないでローテートする場合はcreateではなくcopytruncateを指定します。しかし、ローテート中にアクセスがあった場合のログが記録されないのでログに漏れが発生します。
nginx+logrotateの場合は再起動せずに、ログの漏れもなくローテートが実行できますが、その設定はまた次の機会に・・・。
PHP5.4でLibpuzzleを使って画像の類似度判定を行う
はじめに
Libpuzzleとは特徴検出のアルゴリズムにより、高速な画像類似度判定を行うことができるライブラリです。しかし、このライブラリをPHP5.4で利用しようとした場合、エクステンションがPHP5.4からロードされた瞬間にセグメンテーション違反(segmentation fault)が発生し強制終了させられます。
調査した結果、原因はPHP用のエクステンションのメモリ確保方法に問題があると判断しました。そこで、PHP5.4に対応したエクステンションの開発を行いました。今回はそのエクステンションの導入と簡単な動作確認用のPHPプログラムを作成します。
また、この組み合わせで本家のPHPエクステンションがエラーを履かない場合は、本家を利用したほうが良いと思います。
環境
- CentOS6.4
- apache2.4.4
- PHP5.4.16
Libpuzzleの導入
PHP用のエクステンション導入の前にLibpuzzleを導入します。
GD2のインストール
LibpuzzleはGD2を利用して類似度判定を行うので、yumを利用してインストールします。
$ sudo yum install gd gd-devel
Libpuzzleのインストール
GD2のインストールが終わったら次はLibpuzzle本体をインストールします。本体は本家サイト(Libpuzzle - A library to find similar pictures)からダウンロードします。
$ wget http://download.pureftpd.org/pub/pure-ftpd/misc/libpuzzle/releases/libpuzzle-0.11.tar.bz2 $ tar -jxvf libpuzzle-0.11.tar.bz2 Libpuzzle-0.11 $ ./configure Libpuzzle-0.11 $ make clean Libpuzzle-0.11 $ make Libpuzzle-0.11 $ sudo make install
PHP用エクステンションのインストール
本来ならば、libpuzzle-0.11.tar.bz2に同梱されている php/Libpuzzle を利用しますが、冒頭で述べたようにPHP5.4でsegfaultを吐いてしまったので、PHP5.4用に開発を行ったphp-libpuzzle5(alfa-jpn/php-libpuzzle5 · GitHub)を利用します。
php-libpuzzle5のインストール
git-hubのreleaseからバージョン1.0.0をダウンロードしてインストールします。
$ wget https://github.com/alfa-jpn/php-libpuzzle5/archive/1.0.0.tar.gz $ tar -zxvf php-libpuzzle5-1.0.0.tar.gz php-libpuzzle5-1.0.0 $ phpize php-libpuzzle5-1.0.0 $ ./configure php-libpuzzle5-1.0.0 $ make clean php-libpuzzle5-1.0.0 $ make php-libpuzzle5-1.0.0 $ sudo make install
テストプログラムを書いてみる
こんな感じでプログラムを記述します。 $dは0〜1の間の値で、0に近いほど画像が似ていると判定されています。
<?php $cvec1 = puzzle_fill_cvec_from_file(dirname(__FILE__) . '0.jpg'); $cvec2 = puzzle_fill_cvec_from_file(dirname(__FILE__) . '1.jpg'); $d = puzzle_vector_normalized_distance($cvec1, $cvec2); echo '類似度は' . $d . 'です。'; ?>
puzzle_fill_cvec_from_fileの引数を絶対パスで指定している点に注意して下さい。PHPから呼び出せる関数は本家のlibpuzzleエクステンションと同様ですので、本家のドキュメントを参照してください。