youRoomのAPIを使ってみた
youRoomのAPIを使ってなにかしてみたいと思いました。
APIを使うには申し込みが必要です。申し込みはここからできました。
そうすると、Consumer KeyとConsumer Secretをすぐに発行してもらえます。
発行してもらったConsumer KeyとConsumer SecretをもとにAPIをたたくために必要なアクセストークンを取ってみます。
こちらの記事を参考にしてます。
Sinatra と OAuth を使って Twitter のタイムラインを取得してみた
こんな環境で試しました。
# ruby -v ruby 1.9.2p0 (2010-08-18 revision 29036) [i386-darwin9.8.0] # gem list *** LOCAL GEMS *** oauth (0.4.3) rack (1.2.1) sinatra (1.0)
youroom.rb
require 'rubygems' require 'sinatra' require 'oauth' helpers do include Rack::Utils alias_method :h, :escape_html end enable :sessions configure do # use Rack::Session::Cookie, :secret=> Digest::SHA1.hexdigest(rand.to_s) KEY = "Consumer Key" SECRET = "Consumer Secret" end template :layout do <<-EOS <html> <head><title>OAuth Example with Sinatra</title></head> <body> <%= yield %> </body> </html> EOS end def base_url default_port = (request.scheme == "http") ? 80 : 443 port = (request.port == default_port) ? "" : ":#{request.port.to_s}" "#{request.scheme}://#{request.host}#{port}" end def oauth_consumer OAuth::Consumer.new(KEY, SECRET, :site => "http://youroom.in") end get '/' do erb %{ <a href="/request_token">OAuth Login</a> } end get '/request_token' do puts base_url callback_url = "#{base_url}/access_token" request_token = oauth_consumer.get_request_token(:oauth_callback => callback_url) session[:request_token] = request_token.token session[:request_token_secret] = request_token.secret redirect request_token.authorize_url end get '/access_token' do request_token = OAuth::RequestToken.new( oauth_consumer, session[:request_token], session[:request_token_secret]) begin @access_token = request_token.get_access_token( {}, :oauth_token => params[:oauth_token], :oauth_verifier => params[:oauth_verifier]) rescue OAuth::Unauthorized => @exception return erb %{ oauth failed: <%=h @exception.message %> } end session[:access_token] = @access_token.token session[:access_token_secret] = @access_token.secret erb %{ oauth success! <dl> <dt>access token</dt> <dd><%=h @access_token.token %></dd> <dt>secret</dt> <dd><%=h @access_token.secret %></dd> </dl> } end #for 1.9.2 enable :run
KEYとSECRETには発行してもらったConsumer KeyとConsumer Secretを設定します。
起動します。
# ruby youroom.rb
ブラウザでhttp://localhost:4567にアクセスします。
「OAuth Login」のリンクをクリックします。
youRoomの承認画面が表示されるので、「Allow」をクリックします。
Access TokenとAccess Secretが画面に表示されます。
アクセストークンを取得しました!
ruby-1.9.2-p0 > require 'rubygems' ruby-1.9.2-p0 > require 'oauth' ruby-1.9.2-p0 > require 'json' ruby-1.9.2-p0 > consumer = OAuth::Consumer.new(<Consumer Key>, <Consumer Secret>, :site => "http://youroom.in") ruby-1.9.2-p0 > access_token = OAuth::AccessToken.new(consumer, <Access Token>, <Access Secret>) ruby-1.9.2-p0 > res = access_token.get("https://www.youroom.in/r/<group-param>/all?format=json") #<group-param>のRoomのタイムラインを取得する ruby-1.9.2-p0 > JSON.parse(res.body)
10個しか発言がとれてない。
pageパラメータを指定して10個ずつ取らないとダメみたいです。こんな感じで
ruby-1.9.2-p0 > res = access_token.get("https://www.youroom.in/r/<group-param>/all?format=json&flat=true&page=3")
いったい全部で何ページあるのかわからない・・
GmailからIMAPで取ったメールの添付ファイルを取ろうとしたらTMailでエラーになる
#attachmentsでエラーが出る
tmail = TMail::Mail.parse(mail_data.attr['BODY[]']) @read = tmail.attachments #ここ
こんなエラー
undefined local variable or method `jp2CharContext' for #<CharDet::SJISContextAnalysis:0x1e35b38>
RubyForgeでこの件は報告されてるけど対応されてないようす。
ワークアラウンド
TMailを使わないこのやり方だとうまくいくみたいです。
Tmail + IMAP + Attachments
モデルのバリデーションのテストをダラダラ書きたくない
RSpecでモデルのバリデーションのテストをダラダラ書かないで済むうまいやり方ってあるんでしょうか?
ダラダラ書かないで済む方法を模索してみました。
spec_helper.rbにこういうのを追加します。ここではバリデーションの種類はverify_hours(入力値が24(時間)以内かチェックする)だけあります。
種類を追加するときは、verify_hoursをまるっとコピーしたメソッドをつくって、valid_values、invalid_valuesの配列にそれぞれ有効値、無効値を書くだけです。
def verify_valid(klass, col_name, value) valid_check = Proc.new { attributes = @valid_attributes.merge(col_name.to_sym => value) obj = klass.new(attributes) obj.should be_valid obj.should have(0).errors_on(col_name.to_sym) obj.should have(0).errors obj.save.should be_true } end def verify_invalid(klass, col_name, value) invalid_check = Proc.new { attributes = @valid_attributes.merge("#{col_name}" => value) obj = klass.new(attributes) obj.should be_invalid obj.should have(1).errors_on(col_name.to_sym) obj.should have(1).errors obj.save.should be_false } end def verify_hours(klass, col_name) valid_values = [0, 3.5, 12, 24] invalid_values = [nil, "", "hankaku_moji", "全角文字", -1, -2.3, 25] valid_values.each{|value| it "#{col_name} 正常(#{value.inspect})", &verify_valid(klass, col_name, value) } invalid_values.each{|value| it "#{col_name} 異常(#{value.inspect})", &verify_invalid(klass, col_name, value)} end
モデルのSpecにはこんな感じで書きます。
before(:each) do @valid_attributes = { #各項目がvalidになる値を定義しておく } end describe "target_attributeは1日の時間であること" do verify_hours(TargetClass, :target_attribute) end
出力はこんな感じになります。
TargetClass target_attributeは1日の使用時間であること - target_attribute 正常(0) - target_attribute 正常(3.5) - target_attribute 正常(12) - target_attribute 正常(24) - target_attribute 異常(nil) - target_attribute 異常("") - target_attribute 異常("hankaku_moji") - target_attribute 異常("全角文字") - target_attribute 異常(-1) - target_attribute 異常(-2.3) - target_attribute 異常(25)
ちょっと調べてみたところ、accept_values_forというマッチャを追加するよいソリューションがありました。
accept_values_for
describe User do subject { User.new(@valid_attributes)} it { should accept_values_for(:email, "john@example.com", "lambda@gusiev.com") } it { should_not accept_values_for(:email, "invalid", nil, "a@b", "john@.com") } end
これはいいですね。
integration testではAuthenticatedTestHelperのlogin_asが使えない
RestfulAuthenticationプラグインで認証しているアプリのintegration testを書こうとしました。
プラグインに同梱されているAuthenticatedTestHelperのlogin_asというログインのヘルパメソッドを使ってみたのですが、なぜか認証できてない。
AuthenticatedTestHelperのlogin_asの中をみてみると、ActionController::TestRequestのオブジェクトのセッションに認証対象のモデルのidを入れてました。
でも、integration testはActionController::Integration::Sessionのコンテキストで実行されていて、TestRequest,TestResponseは使ってないんですね、、、このヘルパはfunctional testしか使えなかったのか、、、
とりあえずintegration test向けにヘルパメソッドを書きました。これをintegration testのクラスでincludeします。
def login_as_for_it(user) reset! u = user ? (user.is_a?(User) ? user.id : users(user)) : nil get "/login" post "/login", :login => u.login, :password => u.password end
(追記)インターフェースを変えないほうがいいですね。
alias login_as_original login_as def login_as(user) if self.class.superclass == ActionController::IntegrationTest #IntegrationTest reset! u = user ? (user.is_a?(User) ? user.id : users(user)) : nil get "/login" post "/login", :login => u.login, :password => u.password else #FunctionalTest login_as_original(user) end end
MacVim KaoriYaを使い始めたところ文字が見えなくて困った
MacVim KaoriYaを始めました。
今まではitermの上でvimを使ってました。
MacVimを立ち上げたところ、グレー背景に文字が見えない!
なにか色系の設定がおかしなことになってるんだろうなーといろいろ試したところ、.vimrcにcolorschemeの設定を書いていて、.gvimrcに書いてないときにこうなってしまうことが分かりました。
こういうの書いた~/.gvimrcをつくりました。
colorscheme railscasts set showtabline=2 " タブを常に表示 set imdisable " IMを無効化 set transparency=5 " 透明度を指定 set antialias set guifont=Monaco:h14 set columns=120 set lines=50
Amazon SimpleDBのConsistentReadを指定してみる
SimpleRecordというSimpleDB専用のActiveRecordクローンを試しているときに登録したはずのデータが取得できない現象が頻発しました。
POSTでデータ登録 -> Redirect GETで登録したデータを取得して表示しようとしたら、データが取得できてない!という流れです。
これはEventually Consistentが働いている現象なんだなーと思いました。(取得するデータが最新の状態である保証がない)
SimpleDBは今年の2月に取得するデータが最新の状態であることを保証するオプション「ConsistentRead」が追加されていました。
が、SimpleRecordもその中で使われているawsもConsistentReadは未対応のようです。
なので無理矢理ConsistentReadを使うように変えてみました。
aws-2.3.9/lib/awsbase/right_awsbase.rb
def self.sign_request_v2(aws_secret_access_key, service_hash, http_verb, host, uri) fix_service_params(service_hash, '2') # select a signing method (make an old openssl working with sha1) # make 'HmacSHA256' to be a default one service_hash['SignatureMethod'] = 'HmacSHA256' unless ['HmacSHA256', 'HmacSHA1'].include?(service_hash['SignatureMethod']) service_hash['SignatureMethod'] = 'HmacSHA1' unless @@digest256 #FORCE ConsistentRead service_hash['ConsistentRead'] = 'true' # select a digest digest = (service_hash['SignatureMethod'] == 'HmacSHA256' ? @@digest256 : @@digest1) # form string to sign canonical_string = service_hash.keys.sort.map do |key| "#{amz_escape(key)}=#{amz_escape(service_hash[key])}" end.join('&') string_to_sign = "#{http_verb.to_s.upcase}\n#{host.downcase}\n#{uri}\n#{canonical_string}" # sign the string signature = escape_sig(Base64.encode64(OpenSSL::HMAC.digest(digest, aws_secret_access_key, string_to_sign)).strip) ret = "#{canonical_string}&Signature=#{signature}" # puts 'full=' + ret.inspect ret end
この
service_hash['ConsistentRead'] = 'true'
一行を追加です。
これでConsistentReadできているようです(たぶん)
クエリーごとに指定できるようなのがほんとはええわな。
Eventually Consistentなデータストアを使う開発では気をつけよう。