rvmからrbenvへ乗り換えにちょっと苦労した

まずは rbenvと必要なものをインストール。このころはさくっと終わると思っていました。。

brew install rbenv ruby-build rbenv-gem-rehash readline

で、rubyをinstallしようとしたが失敗。

takayukishimizu-no-MacBook-Pro:trabby takayuki$ rbenv install 2.0.0-rc2
Downloading ruby-2.0.0-rc2.tar.gz...
-> http://dqw8nmjcqpjn7.cloudfront.net/9d5e6f26db7c8c3ddefc81fdb19bd41a
Installing ruby-2.0.0-rc2...
       
BUILD FAILED

Inspect or clean up the working tree at /var/folders/7z/5nqy88hd35j8q35jkt3hgbsh0000gn/T/ruby-build.20131210192317.25684
Results logged to /var/folders/7z/5nqy88hd35j8q35jkt3hgbsh0000gn/T/ruby-build.20131210192317.25684.log

Last 10 log lines:
installing default gems:      /Users/takayuki/.rbenv/versions/2.0.0-rc2/lib/ruby/gems/2.0.0 (build_info, cache, doc, gems, specifications)
                              bigdecimal 1.1.0
                              io-console 0.4.1
                              json 1.7.5
                              minitest 4.3.2
                              psych 2.0.0
                              rake 0.9.6
                              rdoc 4.0.0.rc.2.1
                              test-unit 2.0.0.0
The Ruby openssl extension was not compiled. Missing the OpenSSL lib?

http://nick.hateblo.jp/entry/2013/06/12/232300 をみて足りないものを入れる

brew install openssl 

そしたらまたエラー

curl: (7) couldn't connect to host
Error: Download failed: http://openssl.org/source/openssl-1.0.1e.tar.gz

ぐぐるとこれがでてきたので、FIX: Error: Download failed: http://openssl.org/source/openssl... https://gist.github.com/devinbrown/5073115

brew edit openssl

してmirrorサイトに切り替える。

require 'formula'

  class Openssl < Formula
  homepage 'http://openssl.org'
  url 'http://mirrors.ibiblio.org/openssl/source/openssl-1.0.1e.tar.gz'
  ...

でもういっちょトライ。

brew install openssl

イケタ! ので再チャレンジ!

rbenv install 2.0.0-rc2

・・・\(^o^)/

さっきと同じエラー・・・ あ、よく記事をみると依存ライブラリを指定するらしい。

RUBY_CONFIGURE_OPTS="--with-readline-dir=`brew --prefix readline` --with-openssl-dir=`brew --prefix openssl`" rbenv install 2.0.0-rc2 

・・・・(´・ω・`)・・・・

いけた!

この状態だとsystemのrubyが使われるので、いれたrubyをデフォルトに指定。

rbenv global 2.0.0-rc2 

rvmのuninstallは切腹と打てばいいそう。

rvm seppuku

bash_profileにあったrvmの記述も忘れずに消す。

全部gemがなくなっちゃったみたいだから地道にいれなおす。

$ bundle install
Fetching source index from https://rubygems.org/
Could not verify the SSL certificate for https://rubygems.org/.
There is a chance you are experiencing a man-in-the-middle attack, but most likely your system doesn't have the CA certificates needed for verification. For information about OpenSSL certificates, see bit.ly/ruby-ssl. To connect without using SSL, edit your Gemfile sources and change 'https' to 'http'.

また怒られました(´・ω・`)

またググると Installing Ruby 2.0 - Benjamin Curtis http://www.bencurtis.com/2013/08/installing-ruby-2-dot-0/ とあって、たしかにssl入れた時にcurl-ca-bundle入れろよって言われてスルーしてたなと反省しつつ

$ brew install curl-ca-bundle 

~/.bash_profileに追記

$ export SSL_CERT_FILE=/usr/local/opt/curl-ca-bundle/share/ca-bundle.crt 

したあとbundle install するとやっと成功。

上記のssl設定のおかげで、facebook loginが

SSL_connect returned=1 errno=0 state=SSLv3 read server key exchange B: bad ecpoint

とコケていたのも解決されました。

mongoDB用のO/RMapperにMongoidを使う

fluentdで吐いたデータをmongoDB用のormを使って扱いたいので準備をしていきます。

ORMは

あたりがメジャーらしいですが、MongoManagerは以前使ったことある&検索してみるとMongoidのほうが記事数が多い&ドキュメントが豊富、ということでMongoidを使ってみることにします。

rails+mongoidセットアップ

2,3ステップで済んだのでとても簡単でした。

  • gemのインストール
  • 設定ファイルconfig/mongoid.ymlを作成
  • (デフォルトのORMをactive recordに戻す)

まずはgemを追加.

#ORMapper for mongodb
gem 'mongoid'
gem 'bson_ext'

install.

bundle install

で、コマンドでmongoid用の設定ファイルを作ろうとしたら、エラー(ヽ'ω`)

i$ rails generate mongoid:config
/Users/takayuki/.rvm/gems/ruby-1.9.3-p385/gems/kaminari-0.14.1/lib/kaminari/models/mongoid_extension.rb:24:in `block in <module:Document>': undefined method `scope' for Mongoid::Document:Module (NoMethodError)
・・・

undefined method `scope' for Mongoid::Document:Module · Issue #415 · amatsuda/kaminari によると「rails4に対応してるこっち使いなYO!」ということなのでGemfileのmongoidのところを書き換えて、入れなおす。

gem 'mongoid', '~> 4', github: 'mongoid/mongoid'

次は問題なくコマンド通りました。

$ rails generate mongoid:config
      create  config/mongoid.yml

mongoidはデフォルトのormであるactive recordを置き換えてる形になっているのですが、今回はログ用にサブで使うので、デフォルトをactive recordに戻します.

config/application.rb:

config.generators do |g|
  g.orm :active_record
end

これでmongo generate model hogeとしたときは、activerecordのモデルが今まで通りできます。 mondoidのmodelを作りたいときは

$ rails generate mongoid:model

とすればOK。

あと参考にさせていただいた RailsでActiveRecordとMongoidの共存のさせ方 - Qiita [キータ] によるとclassのロードがデフォだとmodels以下すべてになってしまうそうなので、逐次設定します。 アナログ設定になりますが、自分のケースだとそこまでmodel数も増えないだろうからそんな手間にはならなそうです。

これであとはもうコマンドでmodelを逐次追加するだけ!

fluentd+rails+mongoでサクッとログ環境を整備してみる

今更さわりだしたfluentdですが、簡単に導入まわりをまとめておきます。 rails上でない場合はライブラリは変わりますが、やることはほとんど同じだと思います。

fluentd+railsを動かすまでの手順

fluentd+railsを最短で確認するためは以下のステップが必要です。

  • fluentd or td-agentのinstall
  • gemfileにtd-loggerを追加してbundle install
  • td-loggerの設定ファイル追加
  • fluentdサーバーの設定ファイルを作成(fluentd.conf)
  • fluentdサーバーを起動
  • ログをアプリケーションコードに仕込んでアクセス
  • ログがfluentdサーバーから出力されているのを確認

そのあとおまけでmongoDBに出力するところまでやってみます。

実際やってみる

macへfluentdをインストール

gem install fluentd

注:linuxの場合はyumで入れる /etc/yum.repos.d/td.repo

[treasuredata]
name=TreasureData
baseurl=http://packages.treasure-data.com/redhat/$basearch
gpgcheck=0

$ yum update
$ yum install td-agent

Gemfleに追加

gem 'td-logger'

gemをインストール

bundle install

送り側となるtd-loggerの設定ファイルをconfig/treasure_data.ymlに作成。ここでどこのfluentdサーバーに送るか定義します。今回は受け側もlocalhostなので以下のようになります。 trpとなってるところはプロジェクトネームなどをおいて下さい。

development:
  agent: localhost:24224
  tag: trp
  debug_mode: true  # enable debug mode

production:
  agent: localhost:24224
  tag: trp

test:

プロジェクトのルートにfluentdの設定ファイルを作る。

mkdir fluentd
vim fluentd/fluentd.conf

とりあえず出力してみる。 fluentd/fluentd.conf

<source> #setting for sending
  type forward
  port 24224
</source>
 
<match trp.**> #setting for receiving
   type stdout
</match>

fluentdを起動させましょう。

fluentd -c fluentd/fluentd.conf 
2013-11-14 15:30:34 +0900 [info]: starting fluentd-0.10.33
2013-11-14 15:30:34 +0900 [info]: reading config file path="fluentd/fluentd.conf"
2013-11-14 15:30:34 +0900 [info]: using configuration file: <ROOT>
  <source>
    type forward
    port 24224
  </source>
  <match trp.**>
    type stdout
  </match>
</ROOT>
2013-11-14 15:30:34 +0900 [info]: adding source type="forward"
2013-11-14 15:30:34 +0900 [info]: adding match pattern="trp.**" type="stdout"
2013-11-14 15:30:34 +0900 [info]: listening fluent socket on 0.0.0.0:24224

ちゃんと動くか任意の場所に以下を追加してアクセスしてみます。

    TD.event.post('test', {:foo=>:bar})

rails sでサーバーを起動して、アクセスしてみると、先ほどのfluentdのconsole画面に

2013-11-14 15:37:23 +0900 trp.test: {"foo":"bar"}

と出力されます。 シンプルですがこれでinput/outputを確認できました。

ついでに向け先をmongoDBにしてみます。

まずはローカルのmongoを起動させます。

$ /usr/local/bin/mongod  
all output going to: /usr/local/var/log/mongodb/mongo.log

mongoの様子を見たいときは上記のlogをtail -fすればいいですね。

受け側の定義を変えて、typeをmongoにするだけ。

<source>
  type forward
  port 24224
</source>
 

<match trp.access>
  type mongo         # output type
  database trp        # db name
  collection access # table name

  # Following attibutes are optional
  host localhost
  port 27017
</match>

ログもtable名をaccessに変更します。

    TD.event.post('access', {:foo=>:bar})

アクセスが終わったらmongo shellで確認。

$ mongo
> use trp
switched to db trp
> db.access.find()
{ "_id" : ObjectId("5284760e8b5f352e17000001"), "foo" : "bar", "time" : ISODate("2013-11-14T07:04:03Z") }

mongoにfoo => barが記録されていることを確認!

mongoに入ったログはオブジェクトとして扱えるようになるので集計が楽になりますし、カラムを自由に変更できたり、気軽にログを追加できるメリットもあります。

 自由度が高い分、カラムやテーブルを自由気ままに作ってしまうといざほしいデータを整形するときにいろいろメンドくさかったので、ある程度そのログをどう集計するか、なにに使うかを予め決めてから使うと良いと思います。その辺はまたの機会に。

参考:

treasure-data/td-logger-ruby https://github.com/treasure-data/td-logger-ruby

cohakim's blog » Railsからfluentdにイベントログを出力する(開発環境編) http://blog.yabasoft.biz/archives/4401

chef soloの基本コマンド

vagrantつかってvirtualbox上でchefの勉強中。抵抗あったけど整理すれば思ったより複雑じゃないかも。 とりあえず主要なコマンドたちを。

公式サイトはこちら。

Opscode Community

All about Chef ... — Chef Docs

chef用のrepoを作る

knife solo init chef-repo

こんなディレクトリ構成が作られます。

├── cookbooks(オープンソースからとってきたもの)
├── data_bags
├── environments
├── nodes
│   └── hostname.json
├── roles
└── site-cookbooks(自分のサイト用に変更したもの)

ここでいろいろ作業していきます。

対象のnode(server)をchef対応にする。

chefで環境を作る前に、まずサーバーにchefを入れます。

knife solo prepare hostname

cookbookを反映する

chef-repoに移動して

knife solo cook hostname

あとは

  • cookbookを作る

    • knife cookbook create cookbookname -o site-cookbooks/
  • node/hostname.jsonに使うcookbookを追加する。

  • site-cookbooks以下にあるcookbookを編集する。
  • cookbookを反映する

をぐるぐると回りながらカスタマイズしていく感じのようです。recipeの書き方はdocsを見て別途勉強が必要。

Bufferクラスから16進数の文字列を取り出す

APNs=Apple Push Notification serviceを叩いてたら、エラーのコールバックでBufferクラスなるものでtoken が帰ってきた。無知すぎるのでggり、もとのtokenの文字列にしたいんだけど

var token = notification.device['token']
console.log(token.toString('utf8', 0, token.length));

とやったらめっちゃ文字化けしてたのでおかしいな~と思ったら

はじめてのNode.js:Node.js内でバイナリデータを扱うための「Buffer」クラス | SourceForge.JP Magazine

にあるようにutf8じゃなくてhexでとれってさ。

var token = notification.device['token']
console.log(token.toString('hex', 0, token.length));

すると 3fa1327c1e25b58dc351c110d1b55a613168cd6b91a7b911e47ef88ce3XXXXXX みたいな文字列になってちゃんと取れました。

nodeでAPIサーバーをつくろうとおもった時のメモ

nodeでシンプルなAPIサーバーはどう作るのが( ・∀・)イイ!!んだろう

ってことでとりあえず2つの方法でやってみました。 とりあえずAPIインターフェースをどう作るかが目的なので、RESTful*かどうかは置いときます。

*RESTfulなAPIって?→連載:ASP.NET Web API 入門:第2回 RESTfulなAPIの設計を学ぼう (2/2) - @IT

今回の選択肢

WAFとライブラリなので比較する話じゃないですが(そもそもcompoundJSはexpressベースだし)、APIでググって引っかかった2つをチョイス。Tower.js とかもほぼcompoundJSと同じ方向性なので省略。

compoundJSでAPI作ってみる

まずはnpmでinstall.

$ sudo npm install compound -g

プロジェクト作成。

$ compound init api_sample && cd api_sample

package.jsonの内容をinstall.

$ sudo npm install -l

controllerを作成

$ compound g controller test index

routes.jsに/test/indexを追加。

exports.routes = function (map) {
    map.get("/test/index", "test#index");

    map.all(':controller/:action');
    map.all(':controller/:action/:id');
};

test_controllerを若干編集。

load('application');
 
action('index', function () {
  send({
    status : 200,
    title: "test#index"
  });
});

サーバーを起動。

$ compound server

以下のようなjsonレスポンスが取れます。

{
status : 200,
title: "test#index"
}

express-resouceでAPI作ってみる

とりあえずinstall.

$ sudo npm install express-resource

expressが入ってない人はこちらも.

$ sudo npm install -g express

プロジェクト作るよ。

$ express api_test

package.jsonにexpress-resourceを追加して

$ sudo npm install 

routes/user.jsを下記のように書き換え。 express-resourceは

express-resource provides resourceful routing to express

とあるようにREST APIのルーティングを提供してくれるもの。

module.exports = {
   index: function(req, res){
    res.send("index: called as GET method");
  }
  ,new: function(req, res){
    res.send("new: called as GET method");
  }
  ,create: function(req, res){
    res.send("create: called as POST method");
  }
  ,show: function(req, res){
    res.send("show: called as GET method");
  }
  ,edit: function(req, res){
    res.send("edit: called as GET method");
  }
  ,update: function(req, res){
    res.send("update: called as PUT method");
  }
  ,destroy: function(req, res){
    res.send("destroy: called as DELETE method");
  }
};

app.jsを編集してroutingを変更します。

app.get('/', routes.index);
//app.get('/users', user.list);
app.resource('users', require('./routes/user'), { id: 'id' });

node app.jsをしてサーバーをたてたら、http://localhost:3000/usersにアクセス。「index: called as GET method」というレスポンスが帰ってきました。

jsonで返すようにするには、routes/user.jsでindexのところを

res.json({
   "status": 200
});

と変更すれば、

{
status: 200
}

と帰ってきました。 その他にもjson、xmlなどのフォーマットの出し分けをスマートにかけたりするそう。

まとめ

apiをサクッと作るのであればcompoundJSでやろうかなーと思いました。

ormとしてSequelize ∴ A multi-dialect Object-Relational-Mapper for Node.JSというのがあってそれを使うとMVCそろってexpress でも書きやすくなりそうなのでそこまでやって比較かなーと思うけど、compoundJSに入ってるjugglingdbで問題なさそうなので,そのあたりはまたの機会に。

compoundJSはもろもろ入ってるexpressベースのようなのだがブラックボックスなところ多いのでソースを読んでみようと思う。

参考