質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.47%

  • Ruby on Rails

    9057questions

    Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

Railsにて、セレクトボックスで制限時間を選び、DBに収め、現在の時刻と比較したい

解決済

回答 3

投稿

  • 評価
  • クリップ 1
  • VIEW 1,465

tm_rhcp

score 7

前提・実現したいこと

Rails4系にて、投票受付時間をセレクトボックスで選択できる、選択肢型のアンケート機能を作ろうとしています。
投票受付時間を超えていたら回答出来なくなる、という想定です。

発生している問題

アンケート機能自体はひと通り出来たのですが、各アンケート作成時に制限時間を設定する部分でつまづいています。
form_forで回しているフォームの中でアンケートのタイトル、詳細、選択肢を入力する欄が有り、その中に制限時間を選択するセレクトボックスを置きました。

制限時間の選択肢として、アンケートを作成した時間から30分後、1時間後、3時間後など、最長で24時間後まで選べるものにしたいです。

1.hours.from_nowなど、相対的な時間がとれるメソッドがあるのは見つけたのですが、今回の場合これをどの部分に置けば良いのか(view? controller?)、もしくは、他に良い方法があるのか、アドバイスを頂きたいです。
よろしくお願いします。

また、上記問題をクリアしたあとについてなのですが、制限時間を過ぎたかどうかは、DBに収めた制限時間の時刻と現在時間の比較は不等号機能でシンプルに調べられそうだなぁと見ていますが、もし違っていたらアドバイス頂ければと思います。
よろしくお願いしますm(_ _)m

補足情報

  • Rails 4.2.5で作成しています。
  • 現在DBはSQLite3を使用していますが、公開時は別なDBを使う予定です。
  • 制限時間を選んだらDBに現在時刻+制限時間の日時がtime型で収まるのかなと考え、time型のカラムを用意しています。
  • WebPackにてES6も導入しています。
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

+1

時刻の演算

Time は 秒単位で 足し算、引き算ができます。
↓では now という変数の 30 分後を now_after_30_min という変数に設定しています。
そして now_after_30_min と now の差が 1800 秒(30分) であることを計算しています。

1] pry(main)> now = Time.now
=> 2016-01-16 08:49:21 +0900
[2] pry(main)> after_30_min = now + 30 * 60
=> 2016-01-16 09:19:21 +0900
[3] pry(main)> puts after_30_min - now
1800.0
=> nil

時刻の範囲で DB を検索する

時刻の範囲を絞って検索ができます。
  Model.where(col: form..to)
  # 時間が保持されている col が from .. to の間にあるものを検索する)

時刻を扱う際は timezone について注意が必要です。
↓の例では DB には時間が UTC で入っています。
getlocal メソッドをつかって DB の時間の日本語時間を確認しています。
local メソッドをつかって適切な時間指定のSQLが発行されるようにしています。
(Rails での時間の扱いについては TimeWithZone クラスを参照してください)

[1] pry(main)> Team.all.each{|t| puts t.created_at.getlocal}
  Team Load (5.8ms)  SELECT "teams".* FROM "teams"  ORDER BY "teams"."name" ASC
2016-01-10 07:34:42 +0900
2016-01-16 08:03:35 +0900
+----+--------+-----------------+-------------------------+-------------------------+
| id | name   | description     | created_at              | updated_at              |
+----+--------+-----------------+-------------------------+-------------------------+
| 1  | Team A | In China        | 2016-01-09 22:34:42 UTC | 2016-01-09 22:34:42 UTC |
| 2  | Team B | In Tokyo, japan | 2016-01-15 23:03:35 UTC | 2016-01-15 23:03:40 UTC |
+----+--------+-----------------+-------------------------+-------------------------+
2 rows in set
[2] pry(main)> from = Time.local(2016, 1,10, 7)
=> 2016-01-10 07:00:00 +0900
[3] pry(main)> to = Time.local(2016, 1,10, 8)
=> 2016-01-10 08:00:00 +0900
[4] pry(main)> Team.where(created_at: from..to)
  Team Load (0.3ms)  SELECT "teams".* FROM "teams" WHERE ("teams"."created_at" BETWEEN '2016-01-09 22:00:00.000000' AND '2016-01-09 23:00:00.000000')  ORDER BY "teams"."name" ASC
+----+--------+-------------+-------------------------+-------------------------+
| id | name   | description | created_at              | updated_at              |
+----+--------+-------------+-------------------------+-------------------------+
| 1  | Team A | In China    | 2016-01-09 22:34:42 UTC | 2016-01-09 22:34:42 UTC |
+----+--------+-------------+-------------------------+-------------------------+
1 row in set

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/01/19 01:16

    アドバイス頂きましてありがとうございました!
    時間に足し算引き算できるのはまだ知りませんでした…。
    TimeWithZoneクラスについても参考にさせていただきます!

    キャンセル

+1

アンケート(下のサンプルではParent)の制限時間用の変数(limit)に分単位で制限時間を持たせておき、作成時間としてcreated_atを使えばできそうです。これで投票(Child)の登録時にpassed_limit?で制限時間を超えていないかバリデーションをかければどうでしょうか。

class Parent < ActiveRecord::Base
  has_many :children
end
class Child < ActiveRecord::Base
  belongs_to :parent
  validates :parent, presence: true, if: :passed_limit?

  private

  def passed_limit?
    if self.parent.created_at+(self.parent.limit*60) < Time.now
      errors.add(:parent, "Exceeded the time limit")
    end
  end
end

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/01/19 01:14

    「passed_limit?」の部分、まだ盛り込めていないですが使わせていただきますー!
    ありがとうございました!

    キャンセル

check解決した方法

-1

katoyさん、jincoさんアドバイスを頂きありがとうございました。
教えていただいた事を参考にしつつ、ここまで作って来たものとを照らし合わせて下記のようにして解決しました。
ちょっと泥臭い気もするんですが、動いているのは間違いないので共有致します。

まず、投票受付日時のカラム(limit)をtime型ではなく、text型に変更しました。

view部分は下記のようにしました。
セレクトボックスにはただの文字列が入るようになっています。

# view
<%= form.label :limit, '期限' %>
<%= form.select :limit, options_for_select(%w(15分 30分 1時間 3時間 6時間 12時間 24時間)) %>

そして、選択された文字列を受け取るmodelの方で、
下記のようにコールバックを使い、セーブ直前で時間の形式に変更しました。

# model
before_save :limit_to_time

private
def limit_to_time
  if self.limit == '15分'
    self.limit = Time.current.since(15.minute)
  elsif self.limit == '30分'
    self.limit = Time.current.since(30.minute)
  elsif self.limit == '1時間'
    self.limit = Time.current.since(1.hours)
  elsif self.limit == '3時間'
    self.limit = Time.current.since(3.hours)
  elsif self.limit == '6時間'
    self.limit = Time.current.since(6.hours)
  elsif self.limit == '12時間'
    self.limit = Time.current.since(12.hours)
  elsif self.limit == '24時間'
    self.limit = Time.current.since(24.hours)
  end
  puts self.limit
end

そしてコントローラの方には、投票受付時間を超えていたら投票画面ではなく投票結果画面にリダイレクトするように仕掛けました。

# controller
def show
  redirect_to result_path, notice: '投票は締め切りました' if @poll.limit < Time.current
end

jincoさんから頂いたアドバイス(:passed_limit?)も必要なのでそちらはこれから取り込ませて頂きますー!

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/01/08 09:11

    こんにちは

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.47%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • Ruby on Rails

    9057questions

    Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。