Hatena::ブログ(Diary)

L’Isle joyeuse このページをアンテナに追加 RSSフィード Twitter

2011-08-22

open_sessionで特異メソッドを定義するときの注意

| open_sessionで特異メソッドを定義するときの注意を含むブックマーク open_sessionで特異メソッドを定義するときの注意のブックマークコメント

open_sessionで特異メソッドを定義したら、変数の持ち主に注意しましょうというお話。


次のように定義したとします。

def user
  open_session do |u|
    def u.access_to_home
      get '/home/index'
    end
  end
end

まず、これはエラーなく実行できます。

get '/home/index'
assert_response :success

ところが、これはエラーしてしまいます。

@user = user
@user.access_to_home
assert_response :success   #=> NoMethodError: undefined method `success?' for nil:NilClass

assert_response内で呼びだそうとした@responseは、テストを実行するコントローラがもつ変数です。

一方、@user.access_to_homeのレスポンスを受ける変数は@userセッションがもっているもので、コントローラから見た@responseとは別物なのですね。(あるいは、コントローラ自体がひとつのセッションだと見做すこともできる)


@user.access_to_homeのレスポンスを見るには、userの定義内で呼び出してやる必要があります。

def user
  open_session do |u|
    def u.access_to_home
      get '/home/index'
      assert_response :success
    end
  end
end

今度はエラーなく実行できました。

@user = user
@user.access_to_home

@user.responseと明示してやる方法もあります。

def user
  open_session do |u|
    def u.access_to_home
      get '/home/index'
    end
  end
end
@user = user
@user.access_to_home
@user.response.success?

# こちらも実行可
@user.assert_response :success

もしくは、instance_evalとか。

def user
  open_session do |u|
    def u.access_to_home(&block)
      get '/home/index'

      instance_eval(&block) if block
    end
  end
end
@user = user
@user.access_to_home do
  assert_response :success
end

2011-02-15

vimで編集中のrubyテストを実行できるプラグイン

|  vimで編集中のrubyテストを実行できるプラグインを含むブックマーク  vimで編集中のrubyテストを実行できるプラグインのブックマークコメント

vimでrubyのテストを編集しているとき、テストメソッドごとに結果を確認しながら書き進めたいことなどないでしょうか。

ruby test/unit/hoge.rb -n test_fuga でテストメソッド単位の実行はできるけど、テストケース名が長かったりするとかなり面倒・・・


ところが、そんな悩みを解消してくれる子に出会いました。

GitHub - janx/vim-rubytest: Run ruby test in vim

vimで編集中のテストをその場で実行できるプラグインです。


install
$ git clone git://github.com/janx/vim-rubytest.git

で落として、 plugin/rubytest.vim を ~/.vim/ に配置するだけです。

(READMEには 'Copy all files to your ~/.vim directory.' とありますが、こちらの方法ではできませんでした)

ちなみにvimのバージョンは7.2でした。


usage

vimでの編集中にコマンドを打つとテストが実行されます。

  • <Leader>t (\t) すると、カーソルの乗っているテストケースを実行
  • <Leader>T (\T) すると、そのスクリプト内の全テストケースを実行

注意点としては、

  • 実行中はvimのウィンドウからシェルに切り替わってしまう
  • テストケース単体実行と言っても、テストケース名の部分一致で見ている
    • test_fuga と test_fugafuga があって test_fuga 上で <Leader>t した場合、test_fuga と test_fugafuga の両者が実行される
    • 要は、 ruby test/unit/hoge.rb -n /test_fuga/ が実行されるということ

ていうか、 -n /test_xxx/ で部分一致指定ができたのですね・・・知らなんだ・・・後方一致もできましたし。

完全一致にしたい場合は、~/.vimrc でカスタマイズしてやりましょう。

let g:rubytest_cmd_testcase = "ruby %p -n %c"

さよなら -n オプション!


---


はてブコメント見てたら、quickrun.vimというのもあるんですね。

スクリプト単位の実行なら、シェルに切り替わらないこちらが便利かもです。


参考:no title


コメントありがとうございます!


---


さらに追記。

本日ずっとコケ続けていたscreenのインストールに成功したので、コードとテスト結果が同画面で閲覧でき便利になりました。


---


2011/08/22

さらにさらに追記。


railsのテストでrubytestを使っていたところ、次のエラーが出てテストが実行できない不具合に遭遇しました。

:!echo 'ruby integration/hogehoge_test.rb' && cd test && ruby integration/hogehoge_test.rb
ruby integration/hogehoge_test.rb
/usr/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `gem_original_require': no such file to load -- lib/piyopiyo (MissingSourceFile)
   ・
   ・
   ・

テストコード中で require 'lib/piyopiyo' しているところで、そんなのいないよと怒られる。でも実際にはいる。

rakeや、コンソールからの ruby test/integration/hogehoge_test.rb 実行なら落ちない。


よく見ると、rubytestが cd test してからテストを実行しているのがわかります。

rubytestのソースを確認すると、確かに cd しているところがありました。


自分の環境では cd しなくても不都合はないため、次のようにパッチをあてました。(リビジョンはこちら)

[admin@localhost plugin]$ diff ~/.vim/plugin/rubytest.vim.orig ~/.vim/plugin/rubytest.vim
64,67c64,69
<     if @% =~ '^test'
<       let cmd = substitute(cmd, '%p', s:EscapeBackSlash(strpart(@%,5)), '')
<       exe "!echo '" . cmd . "' && cd test && " . cmd
<     else
---
> 
>     " fix bug 'no such file to load', due to missing load path.
>     "if @% =~ '^test'
>     "  let cmd = substitute(cmd, '%p', s:EscapeBackSlash(strpart(@%,5)), '')
>     "  exe "!echo '" . cmd . "' && cd test && " . cmd
>     "else
70c72
<     end
---
>     "end

なお、ソースが新しければこの不具合は起こらないと思います。

おそらくこれ以前のものが該当するのでは、と。


アップデートしましょという話かもしれないガ━━(;゚Д゚)━━ン!!

2010-12-03

In App Purchase (Apple アプリ内課金システム) のしくみ

|  In App Purchase (Apple アプリ内課金システム) のしくみを含むブックマーク  In App Purchase (Apple アプリ内課金システム) のしくみのブックマークコメント

iOS SDK Hacksに載っていた内容のまとめ。


販売コンテンツの幅

  • Non-Consumable (非消費型) プロダクト
    • 一度しか購入できない
    • この型に限り、Apple側で購入情報が管理される
    • 再度購入しようとした場合、無料でダウンロードできる
    • 別の端末にもリストアできる
      • 無償版から有償版へのアップグレード
      • 電子書籍等のダウンロード etc.
  • Consumable (消費型) プロダクト
    • Apple側では購入情報は管理されない
    • ダウンロードが消えてしまった場合は再購入
    • 別の端末に移せない
      • ゲームの武器のようなバーチャルアイテム etc.
  • Subcription (継続課金)
    • Apple側では購入情報は管理されない
    • ダウンロードが消えてしまった場合は再購入
    • 別の端末に移せない
    • 次回請求のタイミングはアプリデベロッパ側が管理 (自動決済はされない)
      • 月額・年額等、一定期間ごとの課金商品

特徴

  • 2009/10より、本体が無料のアプリでも利用可能になった
    • 有料・無料どちらにしても、Appleと有料販売契約(Paid Contract)を結ぶ必要がある
  • 課金プロダクトの価格帯は本体アプリと同じ
    • デベロッパが自由に設定できない
    • 無料にすることもできない
  • 決済はApple ID (App Storeと同じ)
  • 決済にはトランザクションがかかる
    • 課金の失敗や二重課金が起こらないよう、一連の課金フローが保護されるということ
  • アプリを起動しない限り課金できない
    • 継続課金の場合も自動決済はされない。単に複数回の課金が可能というだけ
  • 課金用プロダクトもAppleの審査が必要
    • プロダクトごとのIDをふるため
    • プロダクトのデータ自体は審査しない。説明文とスクリーンショットが対象

Appleのガイドライン

  • 課金プロダクトはデジタルコンテンツ、オンラインサービスに限る
    • 現実の物販やサービスは不可ということ
  • すべてのユーザが平等に購入権を得られなければならない
  • ポルノ、不快な文章や中傷、ギャンブル等の不適切なコンテンツは販売できない
    • ただし、ギャンブルは以下の条件を満たせば可
      • シミュレーションものであること
      • 仮想通貨が外部のコンテンツや別アプリの通貨との引換に対応していないこと

Appleの決済を使わないアプリ内課金

  • 原則できないと考えたほうがよい(App Store Review Guidelinesにもそのような記述がある)が、以下は可能 ※2010/10月現在
    • WEBサイトでの決済
      • iOS SDK Hacksには、UIWebView (アプリ内ブラウザ) を立ち上げてサイトを表示すれば決済できるように書いてあるが、2010/12月現在はmobile safariに切り替える方式でないとリジェクトされるという話も聞く。
        事例として載っているebi Readerも、現在はmobile safariに切り替わる仕様になっている模様。(iPhone版のみ確認)
    • PayPalを通しての決済
      • PayPal提供のSDKをアプリに組み込むと、PayPalの決済を使えるようになる no title

参考

トラックバック - http://d.hatena.ne.jp/apo-co/20101203

2010-11-09

演算子の優先順位

| 演算子の優先順位を含むブックマーク 演算子の優先順位のブックマークコメント

演算子の優先順位を意識していないと予想外の結果になることがあるので注意しましょうというお話。

  • 和集合を代入したい場合
irb(main):001:0> hoge = nil || 'hoge'
=> "hoge"
irb(main):002:0> hoge = (nil || 'hoge')
=> "hoge"

結果は同じ。

  • 和集合を配列に追加したい場合
irb(main):003:0> hoge = []
=> []
irb(main):004:0> hoge << (nil || 'hoge')
=> ["hoge"]
irb(main):005:0> hoge = []
=> []
irb(main):006:0> hoge << nil || 'hoge'
=> [nil]

|| より << が強いので、カッコの有無で結果が変わってしまいます。


確かに、リファレンスを見ると = と << の優先順位が全然違いますね。

トラックバック - http://d.hatena.ne.jp/apo-co/20101109

2010-07-15

画像の非同期ロード

| 画像の非同期ロードを含むブックマーク 画像の非同期ロードのブックマークコメント

前回の内容とも関わるのですが、UITableViewCellの画像をWEBからロードする場合も、スクロールが非常に重くなることがあります。

前回同様、配列を使って回避できるかなと思ったのですが、うまく表示させることができませんでした。(もし方法があればご教示頂けると幸いです!)

そこで、画像は非同期通信でロードし、ロードできたものから順次表示されるようにしました。


環境
  • iPhoneSDK 3.1.3

方法はこちらのページを参考にさせて頂きました。

非同期通信で画像をロードする方法について - プログラミングノート

大まかな流れとしては、UIImageViewを継承したクラスを作成し、そのクラスに画像データのロードを行わせ、cell.imageViewにaddSubviewする形となります。


ただ、私の環境で試したところ、addSubviewだけでは表示されなかった(cell.imageView.imageが確保されていない感じ)ため、下記のようにデフォルト画像を明示的に指定しました。

// tableView:cellForRowAtIndexPath

UIAsyncImageView *ai = [[UIAsyncImageView alloc] initWithFrame:CGRectMake(0, 0, 50, 50)];
[ai loadImage:@"URL"];
[cell.imageView addSubview:ai];
cell.imageView.image = [UIImage imageNamed:@"NoPhoto.png"];   // デフォルト画像を明示的に指定

また、これだけですと、スクロールしてセルが再利用されたとき、新しい画像がロードされるまで古い画像が残っていて違和感がありました。

そこで、UIAsyncImageView.mにinitWithFrameを定義し、セル再利用時もデフォルト画像にリセットされるようにしました。

- (id)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
	
    if (self) {
	self.image = [UIImage imageNamed:@"NoPhoto.png"];   // セル再利用時にもこのメソッドが呼ばれるため、画像をリセット
    }
	
    return self;
}

ntakuntaku 2010/07/15 22:26 こんにちは
非同期通信ですが、参照して頂いた記事はUITableViewで利用するのに十分な情報を記載できていないのです。iPhone Dev CenterからダウンロードできるLazyTableImagesというサンプルコードがそのものズバリな内容ですので参考になりますよ。

apo-coapo-co 2010/07/17 20:25 ntakuさん
そうだったのですね。わざわざフォロー入れて頂きありがとうございます!
iPhone開発はサンプルコードがあるのが嬉しいですね。見てみます。
ご紹介ありがとうございます。

トラックバック - http://d.hatena.ne.jp/apo-co/20100715