コピーアプリ 1 : ヤフオク!のまとめ
2個目のコピーアプリ実装完了。 今回は目標の12時間を下回って、11時間で実装出来た!
コピーした機能リスト
ログインすると使える機能
- ユーザーはログインできる
- ログインユーザーはオークションを作成できる
- ログインユーザーはオークションに参加できる(入札)
- オークション終了日に最高額の入札者は落札できる
- 落札者は出品者を評価できる
ログインしなくても使える機能
- 全オークションを閲覧できる
- 出品者の過去の評価(コメント)を閲覧できる
前回からの改善点
前回のモデリングで苦戦(大幅手戻り発生)したので、今回はちょっと慎重にモデリングしてみた。
以下をコーディング前に意識して取り組んだ。
ルーティング
わりかし素直なルーティングになったかと思う。
ログインユーザー用のルーティングを namespace :my
以下に置いてみた。
Rails.application.routes.draw do devise_for :users root 'auctions#index' resources :auctions, only: [ :show, :index ] do resource :bid, only: [ :new, :create, :destroy ] end namespace :my do resources :products resources :auctions resources :bids, only: [:index] do resource :comment end end end
・・・と、今改めて書いてみたら、namespace外に置いたルーティングが、ログインユーザー用のものになってる箇所があった・・
/auctions/bid はログインしてないと使えない。 Singular resource にした理由は、ログインユーザー1人に対して1つのオークションに1回しか入札できないことを表したかったから。
/my/bids の方では、自分が落札した入札を一覧表示してる。(落札商品の評価コメントを付けるための親として使ってる)
・・ちょっと説明無いとわかりづらいかも・・・・(ちゃんと出来たと思ったんだけどな・・)
悩んだ箇所(指定時間に○○を実行)
オークションが終了する時の締め処理的なものが、最初にユーザーがオークション締切日に設定した日に発生させなきゃいけないイベントだったので、どうやって実現するかちょっと悩んだ。
普通だったら、指定日時に実行させるジョブみたいの・・? まだRailsで試したことがなかったので、色々調べてみたところ、Rails4.2以降だとActiveJobとやらを使って、バックグラウンド実行してくれるgem達と連携して実現するっぽい。
・・目標時間の半分くらい経過した頃にこの辺りをどうするか考えだしたので、このままではまた時間が大幅オーバーしてしまう;;
ということで、仕様を単純にして間に合わせる方式にスイッチしてみた。
採用した実装方針
最後まで終わって、時間が残ればジョブ版に挑戦してみることに。 (結果11時間で終わったので、出来るか出来ないか微妙そうな時間が残ったけど、結局最初の実装でやった方法がさほど悪くないかも!と気に入ってきたので、ジョブ版にスイッチしなかった)
オークション入札画面が表示された時に、締め処理が必要なオークションを探す処理が走るため、 なるべく単純に絞り込めるように、オークションに終了しているかしていないかのフラグを持たせることにした。
オークションの締め処理でハマった箇所
オークション終了日時に関して
何故かモデルのオークション終了日を :date で作ってたらしく、時間が指定出来ない状態になってた
直し(マイグレーションでカラムの修正)
def change change_column :auctions, :deadline_date, :datetime end
タイムゾーンの指定をしてなかった
時間が指定出来るようになったら、やっと気づいた・・ 普通に9時間ズレてた;;
日時関係で悩むと何度か見てるページ。
オークション終了日時の値にオークションの終了状態を追従させたい
モデルのコールバックを初めて使ってみた。
メソッド名・・長っ・・
class Auction < ActiveRecord::Base # ... before_update :check_deadline_date_and_set_closed_if_needed # ... def check_deadline_date_and_set_closed_if_needed if self.deadline_date < Time.current self.closed = true else self.closed = false if self.can_reopen? true # 注意! end end # 既に一度クローズされているオークションを再オープンする場合のチェック # 落札がなければ再オープンできる def can_reopen? self.closed && self.successful_bid.nil? end end
コメントの注意!(↓が怖いので値セット後にtrueを返すようにした)