PIYO - Tech & Life -

Rubyで繰り返し予定を扱えるice_cube gemをざっくり紹介

Ruby icecube

Googleカレンダーみたいな繰り返し予定をRubyやRailsで扱う方法を解説します。

GitHub - seejohnrun/ice_cube: Ruby Date Recurrence Library - Allows easy creation of recurrence rules and fast querying
Ruby Date Recurrence Library - Allows easy creation of recurrence rules and fast querying - GitHub - seejohnrun/ice_cube: Ruby Date Recurrence Library - Allows easy creation of recurrence rules and fast querying

ice_cube gemは繰り返し予定用のRubyのライブラリです。ちなみにRailsとは直接は連携しませんが工夫することで使えます。

簡単な使い方

かなりざっくり言うと、ice_cubeは繰り返し予定に関するいろいろな条件を与えたとき、その条件に該当する日付に関する処理を行うことができるライブラリです。

ユースケースで考えたほうがわかりやすいかもしれないですね。

例えば、2018年4月1日以降の毎月1日に繰り返す予定を表すとしましょう。

schedule = IceCube::Schedule.new(now = Date.new(2018, 4, 1))
schedule.add_recurrence_rule IceCube::Rule.monthly

これでOK。ではschedule.all_occurrencesで全ての日付を取得してみましょう。

schedule.all_occurrences
ArgumentError: All recurrence rules must specify .until or .count to use `all_occurrences'

怒られました。それもそうで、終わりを決められるようなルールがない場合は無限の集合になってしまうからですね。繰り返し回数か終了日を設定するか、日付の問い合わせを期間指定にすることで日付のArrayを得ることができます。

schedules.occurrences(1.year.from_now)
=> [2018-04-01 00:00:00 +0900,
 2018-05-01 00:00:00 +0900,
 2018-06-01 00:00:00 +0900,
 2018-07-01 00:00:00 +0900,
 2018-08-01 00:00:00 +0900,
 2018-09-01 00:00:00 +0900,
 2018-10-01 00:00:00 +0900,
 2018-11-01 00:00:00 +0900,
 2018-12-01 00:00:00 +0900,
 2019-01-01 00:00:00 +0900,
 2019-02-01 00:00:00 +0900,
 2019-03-01 00:00:00 +0900,
 2019-04-01 00:00:00 +0900]
schedule.occurrences_between(Date.new(2018, 5, 1), Date.new(2018, 8, 31))
=> [2018-05-01 00:00:00 +0900,
2018-06-01 00:00:00 +0900,
2018-07-01 00:00:00 +0900,
2018-08-01 00:00:00 +0900]
schedule.first(4)
=> [2018-04-01 00:00:00 +0900,
2018-05-01 00:00:00 +0900,
2018-06-01 00:00:00 +0900,
2018-07-01 00:00:00 +0900]

終わりがある繰り返し

繰り返し回数を指定してみます。

schedule = IceCube::Schedule.new(now = Date.new(2018, 4, 1))
schedule.add_recurrence_rule IceCube::Rule.monthly.count(4)
schedule.all_occurrences
=> [2018-04-01 00:00:00 +0900,
2018-05-01 00:00:00 +0900,
2018-06-01 00:00:00 +0900,
2018-07-01 00:00:00 +0900]

終了日を指定してみます。

schedule = IceCube::Schedule.new(now = Date.new(2018, 4, 1))
schedule.add_recurrence_rule IceCube::Rule.monthly.until(Date.new(2018, 5, 10))
schedule.all_occurrences.count
=> [2018-04-01 00:00:00 +0900,
2018-05-01 00:00:00 +0900]

除外日を設定する

Googleカレンダーなんかで繰り返し予定を作って、ある特定の日だけ予定を消そうとすると、このイベントだけ削除みたいなことができます。あれを実現できます。

4月1日から4回繰り返す、だけどその内の5月1日は無しにしてね、という感じでルールを指定します。

schedule = IceCube::Schedule.new(now = Date.new(2018, 4, 1))
schedule.add_recurrence_rule IceCube::Rule.monthly.count(4)
schedule.add_exception_time(Date.new(2018, 5, 1))
schedule.all_occurrences
=> [2018-04-01 00:00:00 +0900,
2018-06-01 00:00:00 +0900,
2018-07-01 00:00:00 +0900]

繰り返しが3回しか現れないですね。

シリアライズする

これまでice_cubeのscheduleクラスに色々設定してきた繰り返しルールをyamlやical形式などでテキスト化できます。

このgemを調べるまでしらなかったのですが、繰り返しルールの記述はRFCで定義されているんですね。icalがこの記法でデータを持っているようです。

例えば先ほど除外日を使ったときのscheduleをyamlやicalにしてみます。

schedule = IceCube::Schedule.new(now = Date.new(2018, 4, 1))
schedule.add_recurrence_rule IceCube::Rule.monthly.count(4)
schedule.add_exception_time(Date.new(2018, 5, 1))

yaml

---
:start_time: 2018-04-01 00:00:00.000000000 +09:00
:rrules:
- :validations: {}
  :rule_type: IceCube::MonthlyRule
  :interval: 1
  :count: 4
:rtimes: []
:extimes:
- 2018-05-01 00:00:00.000000000 +09:00

ical

DTSTART;TZID=JST:20180401T000000
RRULE:FREQ=MONTHLY;COUNT=4
EXDATE;TZID=JST:20180501T000000

またこれらの形式からscheduleクラスに戻すことも可能です。

ical = "DTSTART;TZID=JST:20180401T000000\nRRULE:FREQ=MONTHLY;COUNT=4\nEXDATE;TZID=JST:20180501T000000"
schedule = IceCube::Schedule.from_ical(ical)
schedule.all_occurrences
=> [2018-04-01 00:00:00 +0900,
2018-06-01 00:00:00 +0900,
2018-07-01 00:00:00 +0900]

特に編集しないアプリケーションであれば、yamlやicalの形式でDBに保存しておいて、使うときは再びscheduleに変換して使うことが可能です。

ただの紹介になっちゃったので詳しくはREADMEを見てもらうとして、今度は簡単なサンプルアプリを作って例を紹介してみようかなーと思っています。

RELATED

Rails周りで最近知ったこと 2018/04版

String#squish ActiveSupportで定義されているらしい。 def squish dup.squish! end def squish! gsub!(/[[:space:]]+/, " ") strip! self end halt_callback_chains_on_return_false がDeprecatedになった件でプルリク見てたら書いてあってみつけた。 deprecate halt_callback_chains_on_return_false instead of `halt_and… · rails/rails@4e63ce5 ActiveSupport::Deprecation.warn(<<-MSG.squish) ActiveSupport.halt_callback_chains_on_return_false is deprecated and will be removed in

markdown_section_numbering gemをアップデート

昔こんなのを作ったわけだけど、本当に少しだけ足りないなと思うところがあったのでアップデートしました。 <img src=“https://capture.heartrails.com/small?https://blog.piyo.tech/posts/2014-07-06-193000" alt=“マークダウンに見出し番号をつけるRuby Gem書いた - ぴよログ"