Baike.dev
All toolsOnline toolsAI codingTrendingOpen sourceNewsSubmit
Log in
< Back to tools
M

montrose

> 编程语言
Open source

Recurring events library for Ruby. Enumerable recurrence objects and convenient chainable interface.

854 stars0 likes1 views
WebsiteGitHub

About

Recurring events library for Ruby. Enumerable recurrence objects and convenient chainable interface.

Montrose

Montrose is an easy-to-use library for defining recurring events in Ruby. It uses a simple chaining system for building enumerable recurrences, inspired heavily by the design principles of HTTP.rb and rule definitions available in Recurrence.

  • Introductory blog post
  • NYC.rb presentation

Installation

Add this line to your application's Gemfile:

ruby
gem "montrose"

And then execute:

$ bundle

Or install it yourself as:

$ gem install montrose

Why

Dealing with recurring events is hard. Montrose provides a simple interface for specifying and enumerating recurring events as Time objects for Ruby applications.

More specifically, this project intends to:

  • model recurring events in Ruby
  • embrace Ruby idioms
  • support recent Rubies
  • be reasonably performant
  • serialize to yaml, hash, and ical formats
  • be suitable for integration with persistence libraries

What Montrose doesn't do:

  • support all calendaring use cases under the sun
  • schedule recurring jobs for your Rails app. Use one of these instead: cron, rufus-scheduler, sidekiq-cron, sidetiq, whenever

Concepts

Montrose allows you to easily create "recurrence objects" through chaining:

ruby
# Every Monday at 10:30am
Montrose.weekly.on(:monday).at("10:30 am")
=> #

Each chained recurrence returns a new object so they can be composed and merged. In both examples below, recurrence r4 represents 'every week on Tuesday and Thursday at noon for four occurrences'.

ruby
# Example 1 - building recurrence in succession
r1 = Montrose.every(:week)
r2 = r1.on([:tuesday, :thursday])
r3 = r2.at("12 pm")
r4 = r3.total(4)

# Example 2 - merging distinct recurrences
r1 = Montrose.every(:week)
r2 = Montrose.on([:tuesday, :thursday])
r3 = Montrose.at("12 pm")
r4 = r1.merge(r2).merge(r3).total(4)

Most recurrence methods accept additional options if you favor the hash-syntax:

ruby
Montrose.r(every: :week, on: :monday, at: "10:30 am")
=> #

See the docs for Montrose::Chainable for more info on recurrence creation methods.

A Montrose recurrence responds to #events, which returns an Enumerator that can generate timestamps:

ruby
r = Montrose.hourly
=> #

r.events
=> #

r.events.take(10)
=> [2016-02-03 18:26:08 -0500,
2016-02-03 19:26:08 -0500,
2016-02-03 20:26:08 -0500,
2016-02-03 21:26:08 -0500,
2016-02-03 22:26:08 -0500,
2016-02-03 23:26:08 -0500,
2016-02-04 00:26:08 -0500,
2016-02-04 01:26:08 -0500,
2016-02-04 02:26:08 -0500,
2016-02-04 03:26:08 -0500]

Montrose recurrences are themselves enumerable:

ruby
# Every month starting a year from now on Friday the 13th for 5 occurrences
r = Montrose.monthly.starting(1.year.from_now).on(friday: 13).repeat(5)

r.map(&:to_date)
=> [Fri, 13 Oct 2017,
Fri, 13 Apr 2018,
Fri, 13 Jul 2018,
Fri, 13 Sep 2019,
Fri, 13 Dec 2019]

Conceptually, recurrences can represent an infinite sequence. When we say simply "every day", there is no implied ending. It's therefore possible to create a recurrence that can enumerate forever, so use your Enumerable methods wisely.

ruby
# Every day starting now
r = Montrose.daily

# this expression will never complete, Ctrl-c!
r.map(&:to_date)

# use `lazy` enumerator to avoid eager enumeration
r.lazy.map(&:to_date).select { |d| d.mday > 25 }.take(5).to_a
=> [Fri, 26 Feb 2016,
Sat, 27 Feb 2016,
Sun, 28 Feb 2016,
Mon, 29 Feb 2016,
Sat, 26 Mar 2016]

It's straightforward to convert a recurrence to a hash and back.

ruby
opts = Montrose::Recurrence.new(every: 10.minutes).to_h
=> {:every=>:minute, :interval=>10}

Montrose::Recurrence.new(opts).take(3)
=> [2016-02-03 19:06:07 -0500,
2016-02-03 19:16:07 -0500,
2016-02-03 19:26:07 -0500]

A recurrence object must minimally specify a frequency, e.g. :minute, :hour, :day, :week, :month, or, :year, to be viable. Otherwise, you'll see an informative error message when attempting to enumerate the recurrence.

ruby
r = Montrose.at("12pm")
=> #
r.each
Montrose::ConfigurationError: Please specify the :every option

Usage

…

Montrose relies on ActiveSupport for DateTime, Date, and Time calculations. As such, configuring ActiveSupport settings should work for Montrose recurrences.

For example, your application can configure the Date "beginning of the week" (docs):

ruby
Date.beginning_of_the_week = :sunday
# OR
Date.beginning_of_the_week = :monday

Similarly in Rails (docs):

ruby
config.beginning_of_week = :sunday
# OR
config.beginning_of_week = :monday

Changing these settings may affect the behavior of Montrose weekly recurrences.

Combining recurrences

It may be necessary to combine several recurrence rules into a single enumeration of events. For this purpose, there is Montrose::Schedule. To create a schedule of multiple recurrences:

ruby
recurrence_1 = Montrose.monthly(day: { friday: [1] })
recurrence_2 = Montrose.weekly(on: :tuesday)

schedule = Montrose::Schedule.build do |s|
  s  #
schedule.each do |event|
  puts event
end

Ruby on Rails

Instances of Montrose::Recurrence support the ActiveRecord serialization API so recurrence objects can be marshalled to and from a single database column:

ruby
class RecurringEvent < ApplicationRecord
  serialize :recurrence, Montrose::Recurrence

end

Montrose::Schedule can also be serialized:

ruby
class RecurringEvent < ApplicationRecord
  serialize :recurrence, Montrose::Schedule

end

Inspiration

Montrose is named after the beautifully diverse and artistic neighborhood in Houston, Texas.

Related Projects

Check out following related projects, all of which have provided inspiration for Montrose.

  • ice_cube
  • recurrence
  • runt
  • http.rb - not a recurrence project, but inspirational to design, implementation, and interface of Montrose

Development

After checking out the repo, run bin/setup to install dependencies. bin/setup will install gems for each gemfile in gemfiles/ against the current Ruby version.

To run tests against all gemfiles for current Ruby:

bin/spec

To update installed gems for gemfiles:

bin/update

To fix lint errors:

bin/standardrb --fix

When adding a new gemfile to gemfiles/, run bin/setup and commit the generated lock file.

You can also run bin/console for an interactive prompt that will allow you to experiment.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/rossta/montrose. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.

License

The gem is available as open source under the terms of the MIT License.

Issues· 0 open

View all issuesOpen on GitHub

No open issues yet, or sync has not completed.

> Tags

Rubyrecurrencerecurring-eventsruby

No comments yet. Be the first to share.

> Details

PublishedAug 1, 2026
UpdatedSep 17, 2026
Category编程语言
PricingOpen source

> Related tools

T
TypeScript
JavaScript 的超集,为前端与全栈提供静态类型
P
Python
通用编程语言,广泛用于 Web、数据与 AI
G
Go
Google 推出的简洁高效系统语言