0

im-kayac gemでiOSのカスタムURLスキーム使えるようになった

なんかよくわかってなかったのでオプションを付けてなかったんだけどjune29さんからpull requestが来て実装された。

handlerオプションが追加された。

% gem install im-kayac
% im-kayac -to shokai -handler twitter://twitter.com/shokai 私のツイッターデス

これでiPhoneでtwitterアプリが起動するプッシュ通知が送れる。


Rubyで書くならこう
require 'rubygems'
require 'im-kayac'
ImKayac.to('username').handler('twitter://twitter.com/shokai').post('私のツイッターデス')

shokai/ruby-im-kayac · GitHub

5

Ruby書くならBundler使え

Rubyでプログラム書くにあたって、bundlerを使わないプログラムは1年ぐらい経つと動かなくなってる事が多々あって、bundlerマジ重要なのと
ぐぐってもあまりbundlerの利点や説明がまとまってる記事がなかったので

研究室のwikiに書いた記事を転載しておく。



Bundlerとは


Bundler: The best way to manage a Ruby application's gems

プロジェクト内で使うRubygemsを管理するしくみ。
プロジェクトの一番上のディレクトリに「Gemfile」というテキストファイルを置き、その中にgemの名前(と必要あればバージョンも)書く。
% bundle install
というコマンドで、gemが一括インストールできる

プログラム内で
require 'bundler'
Bundler.require
と書くと、gemが一括requireできる


どんな時に便利なの?


使用するgemのバージョンを指定できる


開発環境とデプロイ環境で完全に同じバージョンのgemを使えると、無駄なバグが起こらない


gemがメジャーバージョンアップして仕様が変わってしまったので、古いのを使いたい事がある。
railsとかtwitter gemとか、バージョンアップも速いし各バージョンでAPIに互換性がない


あるプロジェクトではtwitter gem 5.x系を、別のプロジェクトでは4.x系を使いたい場合
bundlerなしで
require 'twitter'
するとどちらのプロジェクトでも、バージョンの大きい5.xを読み込んでしまう
そのマシンに存在する最もバージョンの大きいtwitter gemを読み込んでしまうのだ
つまり、新しくtwitter gemを使ったプロジェクトを始めるだけで、古いプロジェクトは何も手を触れていないのに壊れてしまう


作ったプログラムが1年後もちゃんと動くにはbundler必須だとお分かりいただけましたか


rubygems.org以外でホストされているgemをインストールできる

gitリポジトリを指定してインストールできるので、
部外秘なgemは学内に置いておきつつ、Herokuにインストールさせるとかも可能
特定のgit branchやtagを指定してインストールも可能
既存のgemをgithub上でforkして、ちょっとカスタムして使う事もできる
rubygems.orgが死亡してしまった時にも使える


プロジェクトのディレクトリにgemをインストールできる

% bundle install --path (ディレクトリ名)
gemそのものをちょっと修正して使う時や、gemのバグ探しなどに便利


使用方法伝授


準備
まずRubygemsを2.0.0以上にアップデートしておく。
% gem update --system
% gem -v


bundlerをインストール
% gem install bundler
最新版は1.3.4です


Gemfile というテキストファイルを作成し、使用するgemを列挙する
source 'https://rubygems.org'
gem 'sinatra'
gem 'mongoid', '>=2.4.0', '<3.0.0'
gem 'json', '~> 1.7'
gem 'tw', :git => 'git@github.com:shokai/tw.git'
意味 – sinatraはどのバージョンでもいい、mongoidは2.4以上3未満、jsonは1.7.x系の最新、twはgithubから開発版をインストール

このファイルはbundle initすれば雛形を生成してくれるので、書式忘れたらinit。


gemをインストール

% bundle install


Gemfile.lockが無い場合(はじめてbundle installした時)
Gemfile.lockというファイルが生成される
gemの名前とバージョンが列挙されている
これをgitにcommitしておくと吉
Gemfileではゆるめにバージョン指定して、詳細はGemfile.lockに任せた方がいい


gemのインストール元
ローカルにないgemはrubygemsから最新版が
ローカルにあるgemは、ローカルにある中で一番バージョン番号が大きいものがインストールされる


Gemfile.lockがある場合(2回目以降のbundle install時)
書いてあるバージョンのgemがインストールされる


gemのアップデート

% bundle update
rubygems.orgから最新版を取得し、Gemfile.lockを更新する



bundlerで指定したgemを使う

これやらないと、システムに入っている最新版gemを使ってしまいます


bundlerで指定したgemを使う(実行時に指定)
今まで
% ruby foo.rb
% rackup config.ru
等で実行していたのを、
% bundle exec ruby foo.rb
% bundle exec rackup config.ru
とする


bundlerで指定したgemを使う(プログラム内で指定)
require 'bundler/setup'
これを書けば使用バージョンがGemfile.lockに書かれている物に固定される(俺はこっち派)

個別にrequireしてもいいし、
Bundler.require
でgemを一括requireしてもいい。


Gemfileはカレントディレクトリにある物を参照するので、crontabで使う場合はプロジェクトの中にcdしてからrubyしないといけません。
7時30分に実行する例
30 7 * * * cd $HOME/src/ruby/myapp && bundle exec ruby start.rb > /dev/null 2>&1


gemのテンプレも作れる


% bundle gem 名前
でgemの雛形が作れる。
作ったgemは特に審査などなくrubygems.orgにホストしてもらえて、世界中に大公開されてみんなに使ってもらえるのでどんどん作るといいと思います



まとめ


そんな感じで、Rubygemsたちの更新は速いのでGemfileをちゃんと書かないとすぐにプログラムは動かなくなります。
Bundlerを使いましょう

0

sinatra/rocketio/clientとem-rocketio-client作った

Sinatra RocketIOのRubyクライアントを作った。
Thread版とEventMachine版がある。
コレを使うとサーバーにwebブラウザ以外からデータを投入したり取得したりできて便利。
ワーカーに作業を任せるのにも使えなくもないかも。


Thread版

sinatra-rocketio gemに付属してる。

Sinatra::RocketIO::Client · shokai/sinatra-rocketio Wiki


例えばhttp://rocketio-chat.herokuapp.com/のCUIクライアントを作る例

#!/usr/bin/env ruby
require 'rubygems'
require 'sinatra/rocketio/client'

io = Sinatra::RocketIO::Client.new('http://rocketio-chat.herokuapp.com').connect

io.on :connect do |session|
puts "#{io.type} connect!! (session_id:#{session})"
end

io.on :chat do |data|
puts "<#{data['name']}> #{data['message']}"
end

io.on :error do |err|
STDERR.puts err
end

io.on :disconnect do
puts "disconnected!!"
end

loop do
line = STDIN.gets.strip
next if line.empty?
io.push :chat, {:message => line, :name => name}
end
Threadなので、最後にloopで止めないとRubyが終了する。


デフォルトでwebsocketを先に試すので、最初からcometを使いたいのならtypeオプションを渡す。
io = Sinatra::RocketIO::Client.new('http://rocketio-chat.herokuapp.com', :type => :comet).connect


仕様

コンストラクタにアプリのURLを渡すと、 /rocketio/settingsというJSONを読みにいってそのアプリでcometとwebsocketが使えるか確認する(RocketIOは設定でcometのみとかできる)

settingsの読み込みに失敗すると10秒ぐらい待ってからまた見に行く。

websocketとcometが両方使えるなら、websocketから接続を試す。接続できなかったらcometを使う。

cometにはHTTParty、websocketにはwebsocket-client-simpleを使っている。
どちらも切断されても自動的に再接続しにいく。


EventMachine版


EM::RocketIO::Client · shokai/sinatra-rocketio Wiki
shokai/em-rocketio-client · GitHub

eventmachine版の方が高速に動作する。
通信にem-http-clientとem-websocket-clientを使っているが、thread版と動作は同じ。


インストール
gem install em-rocketio-client


同様にチャットクライアントを作ってみる

require 'rubygems'
require 'eventmachine'
require 'em-rocketio-client'

url = 'http://rocketio-chat.herokuapp.com'
EM::run do
io = EM::RocketIO::Client.new(url).connect

io.on :* do |event_name, data|
puts "(#{io.type}) #{event_name} - #{data.inspect}"
end

EM::defer do
loop do
line = STDIN.gets.strip
next if line.empty?
io.push :chat, {:message => line, :name => name}
end
end
end

*で全イベントを取得できる。


こっちは下の2つのgemを合体させて実装している。
shokai/em-cometio-client · GitHub
shokai/em-websocketio-client · GitHub

0

websocket-client-simple 作った

eventmachineに依存していないruby用websocketクライアントでまともなのが無いので作った。

shokai/websocket-client-simple · GitHub

既にwebsocket-clientというgemがあるけど、ドラフト0しか実装されていないしpull requestできるリポジトリが無かったので作った。
名前はwebsocket-client2というnode臭がするのと後ろにliteとかsimpleとか付けるperl臭がするのと、わけわからない名前付けるruby臭で迷った。


インストール


gem install websocket-client-simple


使い方


JavaScriptのWebSocketと同じように使える
require 'rubygems'
require 'websocket-client-simple'

ws = WebSocket::Client::Simple.connect 'http://example.com:8888'

ws.on :message do |msg|
puts msg.data
end

ws.on :open do
ws.send 'hello!!!'
end

ws.on :close do |e|
p e
exit 1
end

loop do
ws.send STDIN.gets.strip
end
threadで動くので、最後にloopしておかないとRubyが終了してしまいます


サンプルに、
  • rubyで書いたwebsocketチャットサーバー
  • webブラウザ用チャットクライアント
  • websocket-client-simpleで書いたチャットクライアント
を入れておいた。



websocketのデータのparseにはimanel氏のwebsocketというgemを使った。
とても良くテストなども書かれていてメンテされているので、TCPSocketをそれに食わせるだけでクライアント書けた。

あとちゃんと試してないけど多分
ws.send binary_data, :type => :binary
でバイナリ送信できると思う。

0

Bundlerとgem.dependencyの順序

bundler使う時、依存関係の根っこのgemほど下に書くべきらしい。

sinatra-rocketio -> (sinatra-cometio/sinatra-websocketio) -> sinatra-contrib -> sinatra -> rack
みたいな依存の階層関係があるgemから、複数のgemをGemfile/.gemspecに書いてしまうと、親のgemの方がメジャーバージョンアップした時に依存解決できなくなる事がある。


sinatra-rocketioのリポジトリで

bundle install
するとこういうエラーがでてた(もう直った)
Fetching gem metadata from https://rubygems.org/........
Fetching gem metadata from https://rubygems.org/..
Resolving dependencies...
Bundler could not find compatible versions for gem "sinatra":
In Gemfile:
sinatra-rocketio (>= 0) ruby depends on
sinatra (~> 1.3.0) ruby

sinatra-rocketio (>= 0) ruby depends on
sinatra (1.4.1)
何を言っているのかわからない・・


これがsinatra-rocketio.gemspec
gem.add_dependency "rack", ">= 1.5.0"
gem.add_dependency "sinatra", ">= 1.3.6"
gem.add_dependency "eventmachine", ">= 1.0.0"
gem.add_dependency "event_emitter", ">= 0.2.3"
gem.add_dependency "sinatra-contrib", ">= 1.3.2"
gem.add_dependency "sinatra-cometio", ">= 0.3.7"
gem.add_dependency "sinatra-websocketio", ">= 0.1.5"

これはsintra1.4がリリースされたが、sinatra-contribのgemspecがsinatra1.3系統の最新版を使うようになっているため。

上の行から解釈していくっぽいので、sinatra-contribをsinatraより上に書けばsinatra1.3.6が使われる。


というわけでこういう感じで書けばいい
gem.add_dependency "sinatra-cometio", ">= 0.3.8"
gem.add_dependency "sinatra-websocketio", ">= 0.1.6"
gem.add_dependency "sinatra"
gem.add_dependency "eventmachine", ">= 1.0.0"
gem.add_dependency "event_emitter", ">= 0.2.3"

これのおかげで、bundlerでインストールするとずっと0.0.3がインストールされちゃってたけど今の最新は0.0.8です