Friday, March 29, 2013

Cameleon v0.1.1

Cameleon v0.1.1リリース

HTTPのスタブを作るためのフレームワークCameleonのv0.1.1をリリースしました。

Github : Cameleon

v0.1.1ではポストされたJSONをバリデーションする機能を追加しました。Cameleonはgemでインストールできます。

$ gem install cameleon

使い方の詳細は前の記事を参照してください。

JSONバリデーション

v0.1.1のJSONバリデーション機能では、POSTまたはGETのHTTPリクエストボディのJSONについて、データ構造の検証を行うことができます。バリデーションのエンジンにはjson-schema gemを利用し、JSON Schema Draft 3のスキーマ定義を利用します。このバリデーション処理をするには、まず設定ファイル(cameleon.yml)に以下の内容を記載します。

validation:
  type: json

その上で、レスポンスフォルダにrequest.schema.jsonというスキーマファイルをおいておくと、リクエストボディにそのスキーマに適合するJSONデータが含まれない場合にはステータス500のエラーが帰るようになります。

例えば次のようなJSONを期待するインターフェイスの場合

{"a": 1}

次のようなスキーマを定義します。

{
  "type" : "object",
  "properties" : {
    "a" : {
      "type" : "integer",
      "required" : true
    }
  }
}

Saturday, February 23, 2013

Handling remote bare Git repository

When you have Git repositories as below.

  • CENTER.git : Central repository you can not manage
  • MAIN.git : Main repository that your team push into
  • LOCAL : Local repository on developers local machine

Merge on bare repository

You can't merge on bare repository such as MAIN.git because bare repository has no working copy. There are no ways to resolve the conflict when it occurs. Therefore, you must checkout the target branch into your local repository, merge your commits, then push to bare repository(MAIN.git).

Merge from external repository

Use 'git fetch' when there are some changes on CENTER.git. But this command doesn't update HEAD position. So you should use 'git reset --soft'.

# on MAIN.git
$ git fetch <CENTER.git>
$ git reset --soft HEAD
check commit histories using 'git log'.

Push specified branch

You can push local branch 'dev-local' into 'dev-remote' on origin.

$ git push origin dev-local:dev-remote

Clone from remote branch

You can checkout 'dev-remote' branch on MAIN.git into 'dev-local' branch on local machine:

git clone <MAIN.git>
git checkout -b dev-local remotes/origin/dev-remote

Push tags into remote repository

You can push local tags into remote repository.

$ git push --tags

This article is translated from Japanese.

Friday, February 22, 2013

How to create new environment type (RAILS_ENV) for Rails3

You know there are development, test and production environments on Ruby on Rails. This article shows you how to create new environment for your system.

We are currently working with:

  • Ruby: 1.9.2
  • Ruby on Rails: 3.0.5

How to refer environment name

You can refer environment name through Rails.env.

p Rails.env #=> "development"

Adding new environment

For example, you can add your new 'staging' environment by these steps. Follow these steps to add your new 'staging' environment.

  1. copy RAILS_HOME/config/environments/production.rb as RAILS_HOME/config/environmentsstaging.rb
  2. add staging section into database.yml
If you have any other configuration file, edit the configuration too.

Then, create database, do migration, and start your staging server.

$ rake db:create RAILS_ENV=staging
$ rake db:migrate RAILS_ENV=staging
$ rails server -e staging
Yes! You now have new 'staging' environment for your project.

gem setting

You can use different gems for your each environment. This situation, write your Gemfile as below.

group :development, :test do
  gem 'sqlite3-ruby'
end
group :staging, :production do
  gem 'mysql2', '0.2.7'
end
You can reject unnecessary gems for specified environment.
$ bundle install --without test development
see: Bundler : Using Groups

This article is translated from Japanese.

Thursday, February 21, 2013

httpstatus コマンドで、HTTP のステータスコードをすばやくしらべる! in Ruby

なんだか流行っているようなので。

一般的な Web Programmer ならば、HTTP Status code はすべて暗記していると聞きました。しかし、僕は初心者なので、なかなか覚えきれていないので、HTTPのステータスコードをさがすのに便利なツールを用意しました。Rubyで書いてます。Githubに上げてあります。

$ httpstatus 40
400: Bad Request
401: Unauthorized
402: Payment Required
403: Forbidden
404: Not Found
405: Method Not Allowed
406: Not Acceptable
407: Proxy Authentication Required
408: Request Timeout
409: Conflict

$ httpstatus bad
400: Bad Request
502: Bad Gateway
インストールはgemで。
$ gem install httpstatus
元ネタなど。

Thursday, February 07, 2013

CentOS, Redmine, ImageMagick, RMagick

CentOSにRedmineをインストールするために、ImageMagickとRubyバインディングのRMagickをインストールしたけど、やたら嵌ったのでメモ。なんか前からImageMagickのインストールはすんなり行ったことがないような。

まずはyumでインストールしようとしたけど、バージョンが古いのでNG。本家からRPMでインストールしてもエラーが出てNG。多分ヘッダーとかのパスの問題っぽいが、ImageMagick-develみたいなものをRPMでどう認識させて良いかわからず断念。(後から考えると、このタイミングでPKG_CONFIG_PATHを設定すればうまく行ったのかも)

最終的には本家からソースを持ってきてインストールして、PKG_CONFIG_PATHを設定した後にRMagickをインストールするとうまく行った。

$ tar xzvf ImageMagick.tar.gz
$ cd ImageMagick-6.8.2-6/./configure
$ make
$ sudo make install
$ sudo /sbin/ldconfig /usr/local/lib
$ gem install rmagick -v '2.13.2'

Bundlerでライブラリが取得さえできれば、Redmineのインストールは特に問題なし。

Tuesday, January 08, 2013

RubyのTwitter gemでrate_limit_statusを取得

Rubygemsにあるtwitterバージョン4以降は、rate_limit_statusを取得するAPIがなくなった。これは、このAPIの利用方法がTwitterのREST APIのエンドポイントURIに依存していて、Twitter側の仕様変更があったとき、このAPIをサポートしているとライブラリの更新だけで(アプリの修正なしに)この変更に追随することができなくなるためらしい。

まあ設計思想としては理解できるが、とはいえこのライブラリを使ってやりたいことはTwitterのREST APIを叩くことだから、Twitter側の仕様変更に影響を受けることはある程度は仕方ないと思うんだが、どうだろうか。もちろんそこが隠蔽されていて、Twitter側のバージョンが変わってもアプリのコードに変更がない、ということが可能であれば美しいけど。

で、無理やりrate_limit_statusを取得するには以下のコード。

Twitter.get('/1.1/application/rate_limit_status.json')[:body]

要するにgetというメソッドを使えば、直接使いたいAPIを叩ける。このコードで結果がHashで返ってくる。

参考)https://github.com/sferik/twitter/issues/318

Friday, November 16, 2012

mmrd (MongoDB MapReduce Driver) リリースしました

MongoDBのMapReduceをNode.jsから実行/テストするためのモジュール、mmrdをリリースしました。

インストール

$ npm install mmrd

使い方

例えばMongoDBのワードカウントなどで、以下のようなMapReduceを書いていたとします。

// map.js
function map() {
  var words = this.text.split(' ');
  words.forEach(function(word) {
    emit(word, 1);
  });
}
//reduce.js
function reduce(key, values) {
  var result = 0;
  values.forEach(function(v) {
    result += v;
  });
  return result;
}

このとき、mmrdを使うと以下のような形でこのMapReduceをNode.jsから実行できます。

// runner.js
var mmrd = require('mmrd');

// map test
(function() {
  console.log("Word count / Map test");
  var doc = { text: "foo bar baz" };
  mmrd.loadMap('map.js');
  mmrd.map(doc, function(key, value) {
    console.log(key, ":", value);
  });
})();

// reduce test
(function() {
  console.log("Word count / Reduce test");
  mmrd.loadReduce('reduce.js');
  var key = "foo";
  var values = [1, 1];
  var reduced = mmrd.reduce(key, values);
  console.log(key, ":", reduced);
})();

// integration test
(function() {
  console.log("Word count / Integration test");
  var docs = [
    { text: "foo bar baz" },
    { text: "foo baz baz"}
  ];
  mmrd.loadMap('map.js');
  mmrd.loadReduce('reduce.js');
  var result = mmrd.mapReduce(docs);
  console.log(result);
})();
起動
$ node runner.js

もちろん、mochaやshouldを使ったテストも書けます。当たり前ですが。

お試しあれ。

Tuesday, October 16, 2012

Rubyで短縮URL(リダイレクトされるURL)からコンテンツを取得する

短縮URLは普通リダイレクトで目的のコンテンツまで辿り着く。Rubyで短縮URLで指定されたコンテンツを扱いたいが、http_clientはリダイレクトには対応しているけど、デフォルトではhttpsの短縮URLに対してhttpのURLにリダイレクトされたときに例外を吐くのでbit.lyなんかではうまく使えない。

なので、以下のように常にリダイレクトするようにコールバックを設定すると、この制限を回避できる。

require 'httpclient'

def get_content_with_redirection(url)
  client = HTTPClient.new
  def client.allow_all_redirection_callback(uri, res)
    urify(res.header['location'][0])
  end
  client.redirect_uri_callback = client.method(:allow_all_redirection_callback)
  client.get_content url
end

html = get_content_with_redirection("http://bit.ly/RRKD7d")
puts html

Monday, September 24, 2012

fluentdのインストールと簡単な使い方

fluentdはRubyで書かれたsyslogdのようなロギングミドルウェア。様々なログをネットワーク上で色々ルーティングして、たとえば最終的にMongoDBに登録したりすることができる。

fluentdをインストールして使ってみる。 インストールはgemで一発。

$ gem install fluentd

fluentdを起動するには、作業ディレクトリを作ってコマンドで設定ファイルを作成する。起動はfluentdを-cオプションでキックする。

$ fluentd --setup ./fluent
$ cd fluent
$ fluentd -c conf/fluent.conf

動作確認は別コンソールで次のように打ち込むと、先ほどのコンソールにログが出力される。

$ echo '{"json":"message"}' | fluent-cat debug.test

ここまでは公式サイトと同内容。 次に、テキストファイルをtailのようにウォッチして、fluentdに転送してみる。 conf/fluent.confを次のように記述する。

<source>
  type tail
  path /path/to/test.log
  tag debug.*
  format /^(?<log>.*)$/
</source>

<match debug.**>
  type stdout
</match>

ここで、sourceディレクティブのtagはどのmatchディレクティブにログをルーティングするかを決めるためのキーとなる。RabbitMQのルーティングキーと同じ感じ。formatディレクティブは、出力形式を正規表現で設定する。このとき、マッチした箇所に?<name>で名前を付ける。上記例だとlogという名前にしてる。

この状態でfluentdを起動して/path/to/test.logに追記すると、コンソールにログが出力される。

$ echo test >> /path/to/test.log

Wednesday, September 19, 2012

Rails3のviewで、JavascriptのオブジェクトにRubyのオブジェクトをjson(など)で渡す

Rails3のview(erb)で、Rubyのオブジェクトをto_jsonしたものをJavascriptの変数に設定する際は、html_safeメソッドを使う。
var json = <%= {:foo => "FOO"}.to_json.html_safe %>;

Saturday, September 08, 2012

MacにR + randomForestをインストール

OSX 10.7にRをインストールする。今回はMacPortsを使う。

$ sudo port install R
これでRコマンド(対話型のインタープリター)がインストールされる。終了はq()関数。
$ R
> q()

対話型のインタープリターではなく、ソースをファイルから実行するにはRscriptコマンドを使う。とりあえず引数を出力するサンプルを作ってみる。

# hello.r
arg <- commandArgs(TRUE)
cat("hello", arg, "\n")
実行
$ Rscript hello.r world
hello world

PerlのCPANやRubyのRubygemsのようなパッケージ管理ツールが組み込まれていて、install.packages関数でパッケージをインストールできる(CRANというらしい)。試しに機械学習ライブラリのrandomForestをインストール。

> install.packages("randomForest")

MacPortsでインストールしていると、/opt/local/以下の書き込み権限がないという警告がでるので、今回はコンソールにいわれる通り、~/R/...にライブラリフォルダを作った。途中でTclのダイアログでダウンロード元を選ばされるので、普通に東京を選んだ。

インストールしたライブラリを使うのにはlibrary関数を使う。

> library("randomForest")
randomForest 4.6-6
Type rfNews() to see new features/changes/bug fixes.

その他、参考サイト。

Tuesday, September 04, 2012

RubyでCSVをエスケープして出力

改行を含んだりするカラムがあるCSVを出力するとき、全てのカラムを""で囲んで出力するには:force_quotesオプションを使う。
CSV.open(filepath, "w", :force_quotes => true) do |csv_writer|
  csv_writer << ["出力する文字列"]
end
カラムごとに出力フォーマットを変更する場合は、convertersオプションを利用する。例えば2番目のカラムをテキスト形式で出力する場合は:
converter = ->(field, field_info) {
  case field_info.index
  when 1
    CSV::Converters[:text].call(field, field_info)
  else
    field
  end
}
CSV.open(filepath, "w", :converters => converter) do |csv_writer|
  csv_writer << [1, "出力する文字列"]
end
ここではCSVクラスにデフォルトで用意されているCSV::Convertersにある、文字列用のコンバーターを利用している。

Gitでタグ間の変更量を調べる

たとえばtag_1というタグを打っていて、このタグとHEADとの差分をappディレクトリ以下のみ調べるコマンドは以下。
$ git diff --shortstat tag_1 HEAD ./app
 79 files changed, 2657 insertions(+), 531 deletions(-)
--shortstatの代わりに--statを指定すると、変更したファイルごとの変更量も出力される。

Sunday, April 08, 2012

Rails3でscaffoldのテンプレートをカスタマイズ

Rails3ではscaffoldなどで生成するテンプレートをカスタマイズできる。まず、自動生成するコードの元になるテンプレートを、以下のコマンドでlib/templatesに作成する。
$ rake rails:templates:copy
あとは作成されたテンプレートをいじるだけ。以下、作業内容など。
  • デザインに関すること(CSS用のクラス設定など)
  • will_pagenateのページング対応
  • 文言の日本語化
  • .rbファイルにマジックコメントを追加 (# -*- coding: utf-8 -*-)

Thursday, March 01, 2012

Javascriptで年初からの日数/週数を取得する

Javascriptで年初からの日数と週数を数えるメソッドをDateクラスに追加する。週数の方は、日曜を起点とした週数を数える(Rubyの%Uに相当)。

Date.prototype.getDayOfYear = function() {
  var onejan = new Date(this.getFullYear(), 0, 1);
  return Math.ceil((this - onejan) / 86400000);
};

Date.prototype.getWeekOfYear = function() {
  var onejan = new Date(this.getFullYear(), 0, 1);
  var offset = onejan.getDay() - 1;
  var weeks = Math.floor((this.getDayOfYear() + offset) / 7);
  return (onejan.getDay() == 0) ? weeks + 1 : weeks;
};
使い方は:
var time = Date.now();
var today = new Date(time); // Fri Mar 02 2012 16:49:11 GMT+0900 (JST)
today.getDayOfYear(); // => 62
today.getWeekOfYear(); // => 9

参考

Saturday, February 25, 2012

RabbitMQ + RubyでRPC

RabbitMQとRubyで、同期的なRPC (Remote Procedure Call)を実装する。

server

client

サーバーにメッセージを送信するときは普通にサーバーがsubscribeしてるキューに届くように、適当なexchangeにpublishする。ここではex.rpc。

サーバーからのレスポンスの方は若干工夫が必要で、クライアントはリクエストのメッセージ送信前に、受信用の一時キューを作成しておいて、AMQPメッセージのreply_toヘッダーにキュー名を入れておく。サーバー側はリクエストを受け取ったら、処理結果をreply_toのキューに向けて送信する。このときはamq.directというデフォルトで用意されているexchangeを利用する。

サーバー側ではapiを増やすのが簡単なように、@acceptable_methodsにメソッド名を列挙しておくと、そのメソッドをクライアントから呼び出すことができるようにしてある。

このコードではクライアントはRPCの呼び出しの度にAMQPのコネクションを作成していて非効率なので、実際のアプリではAMQP.start (EventMachineのメインループ)はもっと外側にないとダメでしょう。

Tuesday, February 21, 2012

Rails3からRabbitMQに接続

Ruby AMQPはEventMachineに依存しているため、利用する際にはEventMachineのイベントループ内にいる必要がある。RailsからRabbitMQに接続する場合は、RailsのプロセスからRabbitMQにTCPのコネクションを張り、その中でchannelを作成することになる。

RabbitMQとのコネクションは、$RAILS_ROOT/config/initializers/amqp.rbというファイルを作成してその中で作成すると、起動時に実行されてコネクションを繋いでおくことができる。

接続設定

接続の設定ファイルはconfig/amqp.ymlなどとして保存しておけばいいんじゃないでしょうか。

development:
  host: localhost
  vhost: development

test:
  host: localhost
  vhost: test

production:
  host: localhost
  vhost: production

Thin

RailsのサーバーにThinを利用している場合、ThinがEventMachineベースなので特に気にせずconfig/initializers/amqp.rbには以下のように記述すれば良い。

def connect
  yml = YAML.load File.read(File.join(Rails.root, 'config/amqp.yml'))
  config = {}
  yml[Rails.env].each do |key, value|
    config[key.to_sym] = value
  end

  AMQP.connection = AMQP.connect(config)
end

EventMachine.next_tick do
  connect
end

controllerでパブリッシュ

例えばAmqpController/publishでパブリッシュする場合、以下のようになる。

class AmqpController < ApplicationController
  def publish
    connection = AMQP.connection
    channel = AMQP::Channel.new(connection)
    exchange = channel.direct 'ex.direct'
    msg = 'Hello, world'
    exchange.publish(msg, :routing_key => 'tasks')
    render :text => msg
  end
end

参考

Monday, February 20, 2012

ruby amqpで手動acknowledge

AMQPでは、キューからコンシューマーにメッセージが配信されても、コンシューマーがそのメッセージを正しく受け取ったことをブローカー(RabbitMQ)に通知(acknowledge)しないといけない。

subscribeメソッドを引数なしで使うと、自動的にacknowledgeされるが、:ack => trueを指定すると手動で設定することになる(最初、逆かと思った)。

# 自動 ack
queue.bind(exchange, :routing_key => 'tasks').subscribe do |headers, payload|
  # do some process
end
# 手動 ack
queue.bind(exchange, :routing_key => 'tasks').subscribe(:ack => true) do |headers, payload|
  # do some process
  headers.ack
end
メッセージ内容に応じてackするか決める場合は後者を使う。headers.ackメソッドでブローカーにacknowledgeしている。これをしないとメッセージはキューから削除されず、例えばこのコンシューマーがブローカーに再接続すると前回受け取ったメッセージが再送される。

RubyからRabbitMQに接続

RubyのクライアントからRabbitMQに接続し、Publish / Subscribeの動作確認をする。ここではdirect exchangeを使う。

ライブラリインストール

AMQPクライアントライブラリはamqpを使う。よくメンテナンスされているし、ドキュメントもよく整備されているので決定版でしょう。

$ gem install amqp

consumer

まずはメッセージを購読するconsumerを作成する。 ここでは以下のような設定でメッセージを待ち受けている。

  • queue名:サーバー自動生成(空文字を渡すことで設定)
  • exchange名:ex.direct
  • exchangeからqueueに送信する際のルーティングキー:'tasks'
最後のあたりでCTRL+Cで止められるようにしている。

producer

次にメッセージをpublishするスクリプト。

こちらは単純にexchangeを指定して送信するだけ。その際、consumerに合わせてルーティングキーを'tasks'にする。

実行

最初にconsumer.rbを実行し、別ウィンドウでproducer.rbを実行して、consumer.rbに'Hello, world'と表示されれば成功。その際、procuder.rbの方にも'sent: Hello, world'と表示される。publishメソッドでパブリッシュし、RabbitMQから送信完了の応答があるとブロック内が呼ばれる。

Sunday, February 19, 2012

MacでのRabbiqMQ インストール

MacにRabbitMQとWebの管理コンソールをインストールする。

  • MacOS X 10.6.8
  • Erlang 5.9
  • RabbitMQ v2.7.1

RabbitMQインストール

RabbitMQはMacPortsでインストール。

$ sudo port install rabbitmq-server
起動はrabbitmq-serverコマンド。
$ sudo rabbitmq-server
Activating RabbitMQ plugins ...

+---+   +---+
|   |   |   |
|   |   |   |
|   |   |   |
|   +---+   +-------+
|                   |
| RabbitMQ  +---+   |
|           |   |   |
|   v2.7.1  +---+   |
|                   |
+-------------------+
AMQP 0-9-1 / 0-9 / 0-8
Copyright (C) 2007-2011 VMware, Inc.
Licensed under the MPL.  See http://www.rabbitmq.com/

起動できたらCTRL-Cでメニューっぽいものが表示されるのでAキー(abort)で一旦終了。本来、rabbitmqctl stopとするのが正常な止め方のようだが、僕の環境でsudo rabbitmqctl stopとするとログ(/opt/local/var/log/rabbitmq/rabbit@hostname.log)に以下のようなエラーメッセージが出た。
=ERROR REPORT==== 19-Feb-2012::23:03:53 ===
** Connection attempt from disallowed node 'rabbitmqctl67610@xxx' **
これはErlang cookieというのが正しく設定されていないのが原因のようだが、ひとまずここでは放っておく。

ホスト名に注意

Erlang関連はホスト名に注意が必要。会社の環境にインストールしたとき、DHCPを使っていた影響で、macのホスト名がIPアドレスになっていた。この状態だとRabbitMQを起動しようとしても、以下のようなログが出力されうまく起動できない。(以下の149はDHCPで振られたIPの最後の8ビットの値)

ERROR: epmd error for host "149": timeout (timed out establishing tcp connection)
正しいホスト名に設定すれば起動できる。デフォルトのbashならターミナルのプロンプトの一番左側におそらくホスト名が表示されていると思われるので、参考に。

RabbitMQ Web管理コンソールプラグイン インストール

RabbitMQにはWeb画面でRabbitMQを管理できるプラグインがデフォルトでバンドルされているので、公式ページを参考にインストールする。 最初に必要なディレクトリがないので作成してから、コマンドを一発叩くだけ。

$ sudo mkdir /opt/local/etc/rabbitmq/
$ sudo rabbitmq-plugins enable rabbitmq_management
# 再度RabbitMQ起動
$ sudo rabbitmq-server
Activating RabbitMQ plugins ...
6 plugins activated:
* amqp_client-0.0.0
* mochiweb-1.3-rmq0.0.0-git
* rabbitmq_management-0.0.0
* rabbitmq_management_agent-0.0.0
* rabbitmq_mochiweb-0.0.0
* webmachine-1.7.0-rmq0.0.0-hg
# ... 略
そうしたら以下にアクセス。デフォルトのユーザー/パスワードはguest/guest。
RabbitMQ WebConsole
これでWeb画面からexchangeやらqueueやらをWebコンソールから作ったり削除したりできる。

参考URL