Monday, September 30, 2013

bundlerを使ったgemの作り方、公開方法

Rubyのgemパッケージを作成する手順について。bundlerにgem作成を助ける機能がいくつかあるので利用する。

ひな形の作成

bundle gemでひな形を生成する。

$ bundle gem mylib

gemspecは各gemごとに適当に修正する。ひな形を見ればすぐに内容はわかる。

依存ライブラリ

依存ライブラリはGemfileではなくgemspecファイルに記述して、Gemfileからはgemspecを参照するだけにする。

Gemfile

source 'https://rubygems.org'

gemspec

mylib.gemspec

Gem::Specification.new do |spec|
  ...
  spec.add_runtime_dependency "hashie"
  spec.add_runtime_dependency "json"
  spec.add_development_dependency "bundler"
  spec.add_development_dependency "rake"
end

ビルド

gem buildコマンドでビルド。

$ gem build mylib.gemspec

bundleでひな形を作っている場合は、rake buildでpkgフォルダにgemパッケージが作成される。

$ rake build

公開

gem pushコマンドで公開。このとき、rubygems.orgのアカウントが必要。

$ gem push mylib-x.x.x.gem

同様にbundleでひな形を作っている場合は、rake releaseリリースできる

$ rake release

Tuesday, September 24, 2013

Chrome App: JSON Pretty Printer をリリースしました

以前作ったJSONを整形するHTMLのツールをChrome Appとして公開しました。Chrome Web Storeに公開されています。

ただ受け取ったJSONを整形するだけのもの凄くシンプルなツールですが、よければ使ってください。

RubyでJSON/XMLをPretty Printするgem prettyprint

ファイルまたは標準入力から受け取ったJSON/XMLをインデント表示するRuby製のツールprettyprintをgemで公開しました。

インストール

$ gem install prettyprint

使い方

prettyprint [json|xml] [filepath]

gemでインストールするとprettyprintコマンドが使えるようになります。prettyprintコマンドはxmlまたはjsonというサブコマンドを持ちます。ファイル名を指定するとそのファイルを入力とします。

$ prettyprint json input.json

ファイル名を省略すると、標準入力を受け付けます。

$ echo '<html><title>prettyprint</title><body>happy pretty printing</body></html>' | prettyprint xml
<html>
 <title>
  prettyprint
 </title>
 <body>
  happy pretty printing
 </body>
</html>

Friday, September 06, 2013

JSONのPritty PrintするHTMLページを作った

ちょくちょく必要になるのでHTML/JavaScriptでJSONをPritty Printするツールというかページを作りました。

内容は単純に入力に対してJSON.stringifyしているだけ。おまけとして、zClipというクリップボード用jQueryプラグインを使って、出力結果をクリップボードにコピーできるようにしてある。prettyprintの部分はこんな感じ。

var src = $('#src').val();
var obj = JSON.parse(src);
var formatted = JSON.stringify(obj, undefined, 2);

ちなみに前にも書いているけど、これを使わない場合はPythonのワンライナーでやっていいる。

$ echo '{"foo":"FOO", "bar": [1, 2, 3]}' | python -mjson.tool
{
    "bar": [
        1, 
        2, 
        3
    ], 
    "foo": "FOO"
}

Tuesday, August 27, 2013

Rubyのワンライナーを使ってファイルの中で最も長い行を調べる

表題の通り、以下の処理をするワンライナー。

  • target.txtを読み込んで
  • ユニークをとって
  • 最も長い行(文字数)を調べ
  • 長い順にソートして
  • 先頭の5行を表示する

$ cat target.txt | uniq | ruby -ne 'puts sprintf("%03d %s",$_.chomp.size,$_)' | sort -r | head -5

Tuesday, August 20, 2013

MySQLでMD5のハッシュ値を計算する

今まで知らなかったが、MySQLではSQLで直接MD5のハッシュ値を計算できる。

mysql> select md5('test');
+----------------------------------+
| md5('test')                      |
+----------------------------------+
| 098f6bcd4621d373cade4e832627b4f6 |
+----------------------------------+

ちなみにコマンドラインで計算する方法もいつも忘れるのでメモ。

# -sオプションの直後に変換する元の文字列を続ける
$ md5 -stest
MD5 ("test") = 098f6bcd4621d373cade4e832627b4f6

Monday, August 19, 2013

Ruby製のテスト用メールサーバーmocksmtpdをBase64に対応

仕事でメール送信の機能を作るとき、ダミーのSMTPサーバーが必要になり、kosekiさんによるmocksmtpdというRuby製のものを見つけた。ただしBase64エンコードされたメールで日本語が表示できていなかったので、GithubでフォークしてBase64に対応した。あと、出力されるHTMLも若干変更しています。

daixque/mocksmtpd

特徴

  • Ruby製のSMTPサーバー
  • 受信したメールは転送せず、HTMLファイルに書き出す
  • Base64に対応

詳細な使い方は作者のkosekiさんのブログも参考に。

開発用のメールサーバ mocksmtpd を gem にしてみた。

インストール

今はGithubがgemのホスティングをやめているので、specific_installというgemを利用する。これを使うとGithubのソースから直接gemをインストールできる。

$ gem install specific_install
$ gem specific_install -l https://github.com/daixque/mocksmtpd.git

起動

mocksmtpd initでmocksmtpdフォルダに設定ファイルなどを生成。そのままの設定でよければ、そのフォルダに移動してmocksmtpdコマンドを実行する。デフォルトでは25番ポートを使おうとするので、sudoが必要。メールを受信するとinboxフォルダにHTMLファイルが作られる。
$ mocksmtpd init
$ cd ./mocksmtpd
$ sudo mocksmtpd

Saturday, August 10, 2013

Spring MVCでアスペクト(インターセプター)を実装する

Spring MVCでアスペクト(インターセプター)を実装するのは結構簡単。アスペクトっていうのは、要するにメソッドに対して前後処理をするモジュールのこと。フィルターみたいなやつですね。Springの場合はSpring配下にある任意のクラスのメソッドに適応可能。アスペクトで実装する一番ありがちな処理はログでしょうね。私もそのために使いました。あとはエラー処理とか。

SpringではどうもAspect Jを使ってAOPをしていて、用語もそれに従っている。ここでは面倒なので細かい用語の説明はなし。気になる人はアドバイス、ポイントカット、ジョインポイントあたりについて調べるといいんじゃないでしょうか。もっと色々ありそうですけど。

アスペクトの種類

アスペクトには以下の種類がある。

  • Before : メソッドの処理の前
  • AfterReturning : メソッドの処理の後例外は除く)
  • AfterThrowing : メソッドで例外が発生したとき
  • After : メソッドの処理の後(例外も含む)
  • Around: メソッドの前後処理を記述
単純なログならBeforeやAfter*、処理時間の計測とかを含むならAround、っていうのがありがちのようですね。

アスペクトのクラスを実装

すごい簡単。アスペクトのクラスに@Aspect、インターセプターの処理のメソッドに、種類に応じて上記の名前のアノテーション(@Beforeとか)をつけるだけ。

例えば、Service層のクラスの中で、自作の@UpdateDatasourceというアノテーションのついているメソッドのみ特定のログを出力したいときの例。ここで、サービス層はorg.opentechnica.sample.serviceパッケージにあるとする。

アノテーションは普通に実装。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface UpdateDatasource {
    public String value();
}

アスペクトも、アノテーション以外は普通に実装。

@Aspect
@Component
public class LoggingAspect {
    @Before("execution(public * org.opentechnica.sample.service..*(..))")
    public void before(JoinPoint jp) {
        try {
            Class targetClass = jp.getTarget().getClass();
            String methodName = jp.getSignature().getName();
            MethodSignature signature = (MethodSignature) jp.getSignature();
            Method method = signature.getMethod();

            UpdateDatasource updateDatasourceAnnotation = method
                    .getAnnotation(UpdateDatasource.class);
            if (updateDatasourceAnnotation != null) {
                Logger logger = Logger.getLogger(targetClass);
                logger.info("before");
            }
        } catch (SecurityException e) {
            // do nothing
        }
    }
}

@Beforeアノテーション

@Beforeアノテーションでメソッドの実行前にインジェクションする処理であることを表している。アノテーションの引数("execution(public * org.opentechnica.sample.service..*(..))")は、この処理を実行する対象をフィルタリングする条件。ポイントカットっていやつですね。ここでの指定は以下のような意味。

  • publicで
  • 任意の戻り値型で
  • org.opentechnica.sample.serviceパッケージ以下の任意のクラスの
  • 任意の引数を持つ

対象のメソッドが目的のアノテーションを持つかどうか調べる

Javaは同一のメソッド名でも引数が違うと違うシグニチャになるので普通のリフレクションだと面倒だけど、beforeメソッドに渡されるJoinPointから、以下のようMethodSignatureにキャストすると直接取得できる。

MethodSignature signature = (MethodSignature) jp.getSignature();
Method method = signature.getMethod();
で、methodがとれたらもうmethod.getAnnotation(UpdateDatasource.class)とすれば、自作の@UpdateDatasourceが付加されているかどうかわかる。

applicationContext.xmlでAOPの設定

アスペクトを実装したら、applicationContext.xmlに以下の一行を追加するだけで動く。便利!

<aop:aspectj-autoproxy/>

jQueryでsubmitするとき、name属性があるとうまくいかない

タイトルで全て。

jQueryを使ってフォームをsubmitするとき、submit要素にname属性ががあるとうまく動作しないようなので、使わないようにする。

jQueryでチェックボックス

jQueryでラジオボタン/チェックボックスを操作するとき、attr("checked", "checked")だと2回目以降うまく動かないので、prop("checked", true)を使うのが正解。

// これはOK
$("input[type=radio]").prop("checked", true);

// こっちは2回目以降うごかない
$("input[type=raido]").attr("checked", "checked");

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 %>;