montrose 0.10.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0db24b32e84647b7cbcc6ef866ae5e4b281586988c204db9121aa7e34b3bc00b
4
- data.tar.gz: de8cd6aff8fed30f62691433931fb20dab2f7d759f985969753f2615687f073d
3
+ metadata.gz: d44082eaa05a068714f05fe6116f8442b0507b3c7612174d93716ce6fd4af4ba
4
+ data.tar.gz: 691d88452db689b1555772b6749ae12e72bf675fbc55d4082b2d7f4cd41dd795
5
5
  SHA512:
6
- metadata.gz: a17683c42b4660c9c4b10c22566d33043b1fbf2e38ada33966520b69ff5ce64f7f420e992379b3db2f0d301d467afa69ff965bec42c184a09b466b686db5749e
7
- data.tar.gz: 4d5c02063938b9573efccfabf454ac733f6fbbcd916722ef6b923d8a9c70bb466d85c2dc2a9d3216fdc3a1e6c7463ada8cdf2b8740e82a5f967c1bae64039fcc
6
+ metadata.gz: 389adad3b13244fdb642204fa158bde6a204de4c752cab7a11dc1fe2a237a98f7b73c905477fd335d47164b795921a92090b68a528327b115ff2721b8c2945f7
7
+ data.tar.gz: 9ce4b11ae2d6c0fd06b06ca9fe56aa05767bca879be8c5cd3a24dae1c7fed442ca50d4195cf5225ff42f1bb2f2704a9ab8fb308b0a9167865317a245dd782441
@@ -0,0 +1,65 @@
1
+ version: 2.1
2
+ orbs:
3
+ ruby: circleci/ruby@1.1.2
4
+ commands:
5
+ run-tests:
6
+ description: Run tests
7
+ steps:
8
+ - run: bundle exec rake test
9
+ restore:
10
+ description: Restore cache
11
+ steps:
12
+ - restore_cache:
13
+ keys:
14
+ - v1_bundler_deps-
15
+ save:
16
+ description: Save cache
17
+ steps:
18
+ - save_cache:
19
+ paths:
20
+ - ./vendor/bundle
21
+ key: v1-bundler-deps-{{ .Environment.CIRCLE_JOB }}
22
+ bundle:
23
+ description: Install dependencies
24
+ steps:
25
+ - run:
26
+ echo "export BUNDLE_JOBS=4" >> $BASH_ENV
27
+ echo "export BUNDLE_RETRY=3" >> $BASH_ENV
28
+ echo "export BUNDLE_PATH=$(pwd)/vendor/bundle" >> $BASH_ENV
29
+ echo "export BUNDLE_GEMFILE=$(pwd)/${GEMFILE_NAME}" >> $BASH_ENV
30
+ - run: bundle install
31
+
32
+ jobs:
33
+ test:
34
+ parameters:
35
+ ruby_version:
36
+ type: string
37
+ gemfile:
38
+ type: string
39
+ docker:
40
+ - image: 'circleci/ruby:<< parameters.ruby_version >>'
41
+ environment:
42
+ GEMFILE_NAME: <<parameters.gemfile>>
43
+ steps:
44
+ - checkout
45
+ - restore
46
+ - bundle
47
+ - save
48
+ - run-tests
49
+
50
+ workflows:
51
+ all-tests:
52
+ jobs:
53
+ - test:
54
+ matrix:
55
+ parameters:
56
+ ruby_version: ['2.5', '2.6', '2.7']
57
+ gemfile:
58
+ [
59
+ 'gemfiles/activesupport_5.2.gemfile',
60
+ 'gemfiles/activesupport_6.0.gemfile',
61
+ 'gemfiles/activesupport_6.1.gemfile',
62
+ ]
63
+ # exclude:
64
+ # - ruby_version: '3.0'
65
+ # - gemfile: rails_5_2.gemfile
data/Appraisals CHANGED
@@ -1,21 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- appraise "activesupport-5.2" do
4
- gem "activesupport", "~> 5.2.0"
5
- end
6
-
7
- appraise "activesupport-5.1" do
8
- gem "activesupport", "~> 5.1.0"
3
+ appraise "activesupport-6.1" do
4
+ gem "activesupport", "~> 6.1.0"
9
5
  end
10
6
 
11
- appraise "activesupport-5.0" do
12
- gem "activesupport", "~> 5.0.0"
7
+ appraise "activesupport-6.0" do
8
+ gem "activesupport", "~> 6.0.0"
13
9
  end
14
10
 
15
- appraise "activesupport-4.2" do
16
- gem "activesupport", "~> 4.2.0"
17
- end
18
-
19
- appraise "activesupport-4.1" do
20
- gem "activesupport", "~> 4.1.0"
11
+ appraise "activesupport-5.2" do
12
+ gem "activesupport", "~> 5.2.0"
21
13
  end
data/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ### 0.11.0 - (2019-08-16)
2
+
3
+ * enhancements
4
+ * Adds `Recurrence#during` to support recurrences within time-of-day ranges,
5
+ e.g. `Montrose.every(20.minutes).during("9am-5pm")`
6
+
7
+ ### 0.10.1 - (2019-07-22)
8
+
9
+ * enhancements
10
+ * Adds `Schedule.dump` and `Schedule.load` to support ActiveRecord column
11
+ serialization
12
+
1
13
  ### 0.10.0 - (2019-07-17)
2
14
 
3
15
  * enhancements
@@ -61,7 +73,7 @@
61
73
  * Previously, the :between option served as a shorthand for :starts to :until.
62
74
  Now, when both :starts and :between are provided, the recurrence will behave
63
75
  as if anchored by the given :starts option, but filtered through the given
64
- :betweeen option.
76
+ :between option.
65
77
  * The :exclude_end option changes the default behavior of :until--when the
66
78
  timestamp of the interval matches the :until timestamp, it will be included
67
79
  by default unless the :exclude_end option is set to true.
data/Guardfile CHANGED
@@ -25,8 +25,8 @@ guard :minitest do
25
25
 
26
26
  # with Minitest::Spec
27
27
  watch(%r{^spec/(.*)_spec\.rb$})
28
- watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
29
- watch(%r{^lib/(.+)\.rb$}) { "spec/rfc_spec.rb" }
28
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
29
+ watch(%r{^lib/(.+)\.rb$}) { "spec/rfc_spec.rb" }
30
30
  watch(%r{^spec/spec_helper\.rb$}) { "spec" }
31
31
  end
32
32
 
data/README.md CHANGED
@@ -277,6 +277,15 @@ Montrose.every(90.minutes, total: 4)
277
277
  # every 20 minutes from 9:00 AM to 4:40 PM every day
278
278
  Montrose.every(20.minutes, hour: 9..16)
279
279
 
280
+ # every 20 minutes from 9:00 AM to 4:40 PM every day with time-of-day precision
281
+ r = Montrose.every(20.minutes)
282
+ r.during("9am-4:40pm") # as semantic time-of-day range OR
283
+ r.during(time.change(hour: 9)..time.change(hour: 4: min: 40)) # as ruby time range OR
284
+ r.during([9, 0, 0], [16, 40, 0]) # as hour, min, sec tuple pairs for start, end
285
+
286
+ # every 20 minutes during multiple time-of-day ranges
287
+ Montrose.every(20.minutes).during("9am-12pm", "1pm-5pm")
288
+
280
289
  # Minutely
281
290
  Montrose.minutely
282
291
  Montrose.r(every: :minute)
@@ -366,6 +375,40 @@ r.events.take(10).each { |date| puts date.to_s }
366
375
  r.events.lazy.select { |time| time > 1.month.from_now }.take(3).each { |date| puts date.to_s }
367
376
  ```
368
377
 
378
+ ### Combining recurrences
379
+
380
+ It may be necessary to combine several recurrence rules into a single
381
+ enumeration of events. For this purpose, there is `Montrose::Schedule`. To create a schedule of multiple recurrences:
382
+
383
+ ```ruby
384
+ recurrence_1 = Montrose.monthly(day: { friday: [1] })
385
+ recurrence_2 = Montrose.weekly(on: :tuesday)
386
+
387
+ schedule = Montrose::Schedule.build do |s|
388
+ s << recurrence_1
389
+ s << recurrence_2
390
+ end
391
+
392
+ # add after building
393
+ s << Montrose.yearly
394
+ ```
395
+ The `Schedule#<<` method also accepts valid recurrence options as hashes:
396
+ ```ruby
397
+ schedule = Montrose::Schedule.build do |s|
398
+ s << { day: { friday: [1] } }
399
+ s << { on: :tuesday }
400
+ end
401
+ ```
402
+ A schedule acts like a collection of recurrence rules that also behaves as a single
403
+ stream of events:
404
+
405
+ ```ruby
406
+ schedule.events # => #<Enumerator: ...>
407
+ schedule.each do |event|
408
+ puts event
409
+ end
410
+ ```
411
+
369
412
  ### Ruby on Rails
370
413
 
371
414
  Instances of `Montrose::Recurrence` support the ActiveRecord serialization API so recurrence objects can be marshalled to and from a single database column:
@@ -374,6 +417,13 @@ Instances of `Montrose::Recurrence` support the ActiveRecord serialization API s
374
417
  class RecurringEvent < ApplicationRecord
375
418
  serialize :recurrence, Montrose::Recurrence
376
419
 
420
+ end
421
+ ```
422
+ `Montrose::Schedule` can also be serialized:
423
+ ```ruby
424
+ class RecurringEvent < ApplicationRecord
425
+ serialize :recurrence, Montrose::Schedule
426
+
377
427
  end
378
428
  ```
379
429
 
@@ -396,6 +446,13 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
396
446
 
397
447
  You can also run `bin/console` for an interactive prompt that will allow you to experiment.
398
448
 
449
+ To run tests against multiple versions of activesupport, use the Appraisals:
450
+
451
+ ```sh
452
+ bin/appraisal install
453
+ bin/appraisal rake test
454
+ ```
455
+
399
456
  ## Contributing
400
457
 
401
458
  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](contributor-covenant.org) code of conduct.
data/Rakefile CHANGED
@@ -3,7 +3,7 @@
3
3
  require "bundler/setup"
4
4
  require "bundler/gem_tasks"
5
5
  require "rake/testtask"
6
- require "rubocop/rake_task"
6
+ require "standard/rake"
7
7
  require "yard"
8
8
 
9
9
  Rake::TestTask.new(:spec) do |t|
@@ -15,9 +15,7 @@ end
15
15
 
16
16
  task test: :spec
17
17
 
18
- RuboCop::RakeTask.new
19
-
20
- task default: %i[spec rubocop]
18
+ task default: %i[spec standard]
21
19
 
22
20
  namespace :doc do
23
21
  desc "Generate docs and publish to gh-pages"
data/bin/setup CHANGED
@@ -3,5 +3,6 @@ set -euo pipefail
3
3
  IFS=$'\n\t'
4
4
 
5
5
  bundle install
6
+ bundle update
6
7
 
7
8
  # Do any other automated setup that you need to do here
data/bin/standardrb ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'standardrb' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ require "pathname"
12
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13
+ Pathname.new(__FILE__).realpath)
14
+
15
+ bundle_binstub = File.expand_path("../bundle", __FILE__)
16
+
17
+ if File.file?(bundle_binstub)
18
+ if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19
+ load(bundle_binstub)
20
+ else
21
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23
+ end
24
+ end
25
+
26
+ require "rubygems"
27
+ require "bundler/setup"
28
+
29
+ load Gem.bin_path("standard", "standardrb")
@@ -7,6 +7,10 @@ gem "activesupport", "~> 5.2.0"
7
7
  group :development do
8
8
  gem "coveralls"
9
9
  gem "yard"
10
+ gem "guard"
11
+ gem "guard-minitest"
12
+ gem "guard-rubocop"
13
+ gem "pry-byebug"
10
14
  end
11
15
 
12
16
  gemspec path: "../"
@@ -2,11 +2,15 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "activesupport", "~> 6.0.0.beta2"
5
+ gem "activesupport", "~> 6.0.0"
6
6
 
7
7
  group :development do
8
8
  gem "coveralls"
9
9
  gem "yard"
10
+ gem "guard"
11
+ gem "guard-minitest"
12
+ gem "guard-rubocop"
13
+ gem "pry-byebug"
10
14
  end
11
15
 
12
16
  gemspec path: "../"
@@ -2,11 +2,15 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "activesupport", "~> 4.2.0"
5
+ gem "activesupport", "~> 6.1.0"
6
6
 
7
7
  group :development do
8
8
  gem "coveralls"
9
9
  gem "yard"
10
+ gem "guard"
11
+ gem "guard-minitest"
12
+ gem "guard-rubocop"
13
+ gem "pry-byebug"
10
14
  end
11
15
 
12
16
  gemspec path: "../"
data/lib/montrose.rb CHANGED
@@ -2,10 +2,13 @@
2
2
 
3
3
  require "active_support"
4
4
  require "active_support/core_ext/object"
5
- require "active_support/core_ext/numeric"
5
+
6
6
  require "active_support/core_ext/date"
7
- require "active_support/core_ext/time"
8
7
  require "active_support/core_ext/date_time"
8
+ require "active_support/core_ext/integer"
9
+ require "active_support/core_ext/numeric"
10
+ require "active_support/core_ext/string"
11
+ require "active_support/core_ext/time"
9
12
 
10
13
  require "montrose/utils"
11
14
  require "montrose/rule"
@@ -35,6 +38,23 @@ module Montrose
35
38
  def recurrence(options = {})
36
39
  branch(options)
37
40
  end
38
- alias r recurrence
41
+ alias_method :r, :recurrence
42
+
43
+ # Create a new recurrence from given options
44
+ # An alias to {Montrose::Recurrence.new}
45
+ attr_reader :enable_deprecated_between_masking
46
+
47
+ def enable_deprecated_between_masking=(value)
48
+ warn "[DEPRECATION] Montrose.enable_deprecated_between_masking is deprecated and will be removed in a future version."
49
+ @enable_deprecated_between_masking = value
50
+ end
51
+
52
+ def enable_deprecated_between_masking?
53
+ result = !!enable_deprecated_between_masking
54
+ if result
55
+ warn "[DEPRECATION] Legacy Montrose.between masking behavior is deprecated. Please use Montrose.covering instead to retain this behavior."
56
+ end
57
+ result
58
+ end
39
59
  end
40
60
  end
@@ -139,7 +139,7 @@ module Montrose
139
139
  def starts(starts_at)
140
140
  merge(starts: starts_at)
141
141
  end
142
- alias starting starts
142
+ alias_method :starting, :starts
143
143
 
144
144
  # Create a recurrence ending at given timestamp.
145
145
  #
@@ -153,9 +153,12 @@ module Montrose
153
153
  def until(ends_at)
154
154
  merge(until: ends_at)
155
155
  end
156
- alias ending until
156
+ alias_method :ending, :until
157
157
 
158
- # Create a recurrence occurring during date range.
158
+ # Create a recurrence occurring between the start and end
159
+ # of a given date range; :between is shorthand for separate
160
+ # :starts and :until options. When used with explicit :start
161
+ # and/or :until options, those will take precedence.
159
162
  #
160
163
  # @param [Range<Date>] date_range
161
164
  #
@@ -168,6 +171,37 @@ module Montrose
168
171
  merge(between: date_range)
169
172
  end
170
173
 
174
+ # Create a recurrence which will only emit values within the
175
+ # date range, also called "masking."
176
+ #
177
+ # @param [Range<Date>] date_range
178
+ #
179
+ # @example
180
+ # Montrose.weekly.covering(Date.tomorrow..Date.new(2016, 3, 15))
181
+ #
182
+ # @return [Montrose::Recurrence]
183
+ #
184
+ def covering(date_range)
185
+ merge(covering: date_range)
186
+ end
187
+
188
+ # Create a recurrence occurring within a time-of-day range or ranges.
189
+ # Given time ranges will parse as times-of-day and ignore given dates.
190
+ #
191
+ # @param [Range<Time>,String,Array<Array>] time-of-day range(s)
192
+ #
193
+ # @example
194
+ # Montrose.every(20.minutes).during("9am-5pm")
195
+ # Montrose.every(20.minutes).during(time.change(hour: 9)..time.change(hour: 5))
196
+ # Montrose.every(20.minutes).during([9, 0, 0], [17, 0, 0])
197
+ # Montrose.every(20.minutes).during("9am-12pm", "1pm-5pm")
198
+ #
199
+ # @return [Montrose::Recurrence]
200
+ #
201
+ def during(time_of_day, *extras)
202
+ merge(during: time_of_day.array_concat(extras))
203
+ end
204
+
171
205
  # Create a recurrence through :on option
172
206
  #
173
207
  # @param day [Hash,Symbol] weekday or day of month as hash, e.g. { friday: 13 }
@@ -224,7 +258,7 @@ module Montrose
224
258
  def day_of_month(days, *extras)
225
259
  merge(mday: days.array_concat(extras))
226
260
  end
227
- alias mday day_of_month
261
+ alias_method :mday, :day_of_month
228
262
 
229
263
  # Create a recurrence for given days of week
230
264
  #
@@ -240,7 +274,7 @@ module Montrose
240
274
  def day_of_week(weekdays, *extras)
241
275
  merge(day: weekdays.array_concat(extras))
242
276
  end
243
- alias day day_of_week
277
+ alias_method :day, :day_of_week
244
278
 
245
279
  # Create a recurrence for given days of year
246
280
  #
@@ -256,7 +290,7 @@ module Montrose
256
290
  def day_of_year(days, *extras)
257
291
  merge(yday: days.array_concat(extras))
258
292
  end
259
- alias yday day_of_year
293
+ alias_method :yday, :day_of_year
260
294
 
261
295
  # Create a recurrence for given hours of day
262
296
  #
@@ -272,7 +306,7 @@ module Montrose
272
306
  def hour_of_day(hours, *extras)
273
307
  merge(hour: hours.array_concat(extras))
274
308
  end
275
- alias hour hour_of_day
309
+ alias_method :hour, :hour_of_day
276
310
 
277
311
  # Create a recurrence for given months of year
278
312
  #
@@ -288,7 +322,7 @@ module Montrose
288
322
  def month_of_year(months, *extras)
289
323
  merge(month: months.array_concat(extras))
290
324
  end
291
- alias month month_of_year
325
+ alias_method :month, :month_of_year
292
326
 
293
327
  # Create a recurrence for given weeks of year
294
328
  #
@@ -318,7 +352,7 @@ module Montrose
318
352
  def total(total)
319
353
  merge(total: total)
320
354
  end
321
- alias repeat total
355
+ alias_method :repeat, :total
322
356
 
323
357
  # Create a new recurrence combining options of self
324
358
  # and other. The value of entries with duplicate