montrose 0.10.0 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
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