montrose 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.rubocop.yml +113 -0
  4. data/.travis.yml +5 -0
  5. data/CODE_OF_CONDUCT.md +13 -0
  6. data/Gemfile +13 -0
  7. data/Guardfile +34 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +191 -0
  10. data/Rakefile +21 -0
  11. data/bin/_guard-core +16 -0
  12. data/bin/console +7 -0
  13. data/bin/guard +16 -0
  14. data/bin/m +16 -0
  15. data/bin/rake +16 -0
  16. data/bin/rubocop +16 -0
  17. data/bin/setup +7 -0
  18. data/lib/montrose.rb +20 -0
  19. data/lib/montrose/chainable.rb +210 -0
  20. data/lib/montrose/clock.rb +77 -0
  21. data/lib/montrose/errors.rb +5 -0
  22. data/lib/montrose/frequency.rb +63 -0
  23. data/lib/montrose/frequency/daily.rb +9 -0
  24. data/lib/montrose/frequency/hourly.rb +9 -0
  25. data/lib/montrose/frequency/minutely.rb +9 -0
  26. data/lib/montrose/frequency/monthly.rb +9 -0
  27. data/lib/montrose/frequency/weekly.rb +19 -0
  28. data/lib/montrose/frequency/yearly.rb +9 -0
  29. data/lib/montrose/options.rb +293 -0
  30. data/lib/montrose/recurrence.rb +67 -0
  31. data/lib/montrose/rule.rb +47 -0
  32. data/lib/montrose/rule/after.rb +27 -0
  33. data/lib/montrose/rule/before.rb +23 -0
  34. data/lib/montrose/rule/day_of_month.rb +31 -0
  35. data/lib/montrose/rule/day_of_week.rb +23 -0
  36. data/lib/montrose/rule/day_of_year.rb +37 -0
  37. data/lib/montrose/rule/hour_of_day.rb +23 -0
  38. data/lib/montrose/rule/month_of_year.rb +23 -0
  39. data/lib/montrose/rule/nth_day_matcher.rb +32 -0
  40. data/lib/montrose/rule/nth_day_of_month.rb +63 -0
  41. data/lib/montrose/rule/nth_day_of_year.rb +63 -0
  42. data/lib/montrose/rule/time_of_day.rb +33 -0
  43. data/lib/montrose/rule/total.rb +29 -0
  44. data/lib/montrose/rule/week_of_year.rb +23 -0
  45. data/lib/montrose/schedule.rb +42 -0
  46. data/lib/montrose/stack.rb +51 -0
  47. data/lib/montrose/utils.rb +32 -0
  48. data/lib/montrose/version.rb +3 -0
  49. data/montrose.gemspec +32 -0
  50. metadata +192 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ba11d2966c5dc7197138ee39c5fdcfc1466196f1
4
+ data.tar.gz: 3a41d2be8cfd2a9b5a38f1ae06432be7b6345c30
5
+ SHA512:
6
+ metadata.gz: cd28c825b715713b722c41ae2402235fe8c9aa1b251448c941864e360d6898d8456f97a8a5d8a254cbabc4cd1172dc9147075b3591142fd5a1c1ea6589adb15c
7
+ data.tar.gz: 612673faf64c6800d5c6915b57af335171557dd9e066569c4750d7926e975123c304f80e1968660b048c328dfeeb751a9afd08cef79ddea8f32247fd7b867448
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /profile.log
11
+ /trace.txt
@@ -0,0 +1,113 @@
1
+ AllCops:
2
+ Exclude:
3
+ - 'bin/**/*'
4
+
5
+ Lint/HandleExceptions:
6
+ Exclude:
7
+ - 'test/test_helper.rb'
8
+
9
+ Metrics/LineLength:
10
+ Max: 120
11
+
12
+ Metrics/ClassLength:
13
+ Max: 300
14
+
15
+ Metrics/ModuleLength:
16
+ Max: 300
17
+
18
+ Metrics/MethodLength:
19
+ Max: 100
20
+
21
+ Metrics/ParameterLists:
22
+ Max: 8
23
+
24
+ Metrics/AbcSize:
25
+ Enabled: false
26
+
27
+ Metrics/CyclomaticComplexity:
28
+ Enabled: false
29
+
30
+ Metrics/PerceivedComplexity:
31
+ Enabled: false
32
+
33
+ Documentation:
34
+ Enabled: false # TODO: Enable again once we have more docs
35
+
36
+ Lint/EndAlignment:
37
+ Enabled: false
38
+
39
+ Lint/DefEndAlignment:
40
+ Enabled: false
41
+
42
+ Lint/HandleExceptions:
43
+ Enabled: false
44
+
45
+ Style/GlobalVars:
46
+ Exclude:
47
+ - spec/support/trace.rb
48
+
49
+ Style/SpecialGlobalVars:
50
+ Enabled: false
51
+
52
+ Style/TrivialAccessors:
53
+ Enabled: false
54
+
55
+ Style/HashSyntax:
56
+ Enabled: true
57
+
58
+ Style/MethodName:
59
+ Enabled: false
60
+
61
+ Style/AlignParameters:
62
+ EnforcedStyle: with_fixed_indentation
63
+
64
+ Style/AccessModifierIndentation:
65
+ Enabled: true
66
+
67
+ Style/StringLiterals:
68
+ EnforcedStyle: double_quotes
69
+
70
+ Style/StringLiteralsInInterpolation:
71
+ EnforcedStyle: double_quotes
72
+
73
+ Style/ClosingParenthesisIndentation:
74
+ Enabled: false
75
+
76
+ Style/OneLineConditional:
77
+ Enabled: false
78
+
79
+ Style/AndOr:
80
+ Enabled: false
81
+
82
+ Style/Not:
83
+ Enabled: false
84
+
85
+ Style/CaseIndentation:
86
+ IndentWhenRelativeTo: case
87
+ SupportedStyles:
88
+ - case
89
+ - end
90
+ IndentOneStep: false
91
+
92
+ Style/PercentLiteralDelimiters:
93
+ PreferredDelimiters:
94
+ '%w': "[]"
95
+ '%W': "[]"
96
+
97
+ Style/AccessModifierIndentation:
98
+ EnforcedStyle: indent
99
+
100
+ Style/SignalException:
101
+ Enabled: false
102
+
103
+ Style/IndentationWidth:
104
+ Enabled: false
105
+
106
+ Style/TrivialAccessors:
107
+ ExactNameMatch: true
108
+
109
+ Style/RegexpLiteral:
110
+ EnforcedStyle: percent_r
111
+
112
+ Style/DotPosition:
113
+ EnforcedStyle: trailing
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1
4
+ - 2.2
5
+ before_install: gem install bundler -v 1.10.6
@@ -0,0 +1,13 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
4
+
5
+ We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
6
+
7
+ Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
8
+
9
+ Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
10
+
11
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
12
+
13
+ This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in cure.gemspec
4
+ gemspec
5
+
6
+ group :development do
7
+ gem "pry-byebug", platforms: :ruby_22
8
+ gem "guard"
9
+ gem "guard-minitest"
10
+ gem "guard-rubocop"
11
+ gem "yard"
12
+ gem "coveralls"
13
+ end
@@ -0,0 +1,34 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ ## Uncomment and set this to only include directories you want to watch
5
+ # directories %w(app lib config test spec features) \
6
+ # .select{|d| Dir.exists?(d) ? d : UI.warning("Directory #{d} does not exist")}
7
+
8
+ ## Note: if you are using the `directories` clause above and you are not
9
+ ## watching the project directory ('.'), then you will want to move
10
+ ## the Guardfile to a watched dir and symlink it back, e.g.
11
+ #
12
+ # $ mkdir config
13
+ # $ mv Guardfile config/
14
+ # $ ln -s config/Guardfile .
15
+ #
16
+ # and, you'll have to watch "config/Guardfile" instead of "Guardfile"
17
+
18
+ guard :minitest do
19
+ # with Minitest::Unit
20
+ # watch(%r{^test/(.*)\/?test_(.*)\.rb$})
21
+ # watch(%r{^lib/(.*/)?([^/]+)\.rb$}) { |m| "test/#{m[1]}test_#{m[2]}.rb" }
22
+ # watch(%r{^test/test_helper\.rb$}) { "test" }
23
+
24
+ # with Minitest::Spec
25
+ watch(%r{^spec/(.*)_spec\.rb$})
26
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
27
+ watch(%r{^lib/(.+)\.rb$}) { "spec/rfc_spec.rb" }
28
+ watch(%r{^spec/spec_helper\.rb$}) { "spec" }
29
+ end
30
+
31
+ guard :rubocop do
32
+ watch(%r{.+\.rb$})
33
+ watch(%r{(?:.+/)?\.rubocop\.yml$}) { |m| File.dirname(m[0]) }
34
+ end
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Ross Kaffenberger
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,191 @@
1
+ # Montrose
2
+
3
+ [![Build Status](https://travis-ci.org/rossta/montrose.svg?branch=master)](https://travis-ci.org/rossta/montrose)
4
+ [![Coverage Status](https://coveralls.io/repos/rossta/montrose/badge.svg?branch=master&service=github)](https://coveralls.io/github/rossta/montrose?branch=master)
5
+ [![Code Climate](https://codeclimate.com/github/rossta/montrose/badges/gpa.svg)](https://codeclimate.com/github/rossta/montrose)
6
+ [![Dependency Status](https://gemnasium.com/rossta/montrose.svg)](https://gemnasium.com/rossta/montrose)
7
+
8
+ Recurring events in Ruby.
9
+
10
+ This library is currently under development. Most of the functionality does not
11
+ exist yet.
12
+
13
+ ## Installation
14
+
15
+ Add this line to your application's Gemfile:
16
+
17
+ ```ruby
18
+ gem "montrose"
19
+ ```
20
+
21
+ And then execute:
22
+
23
+ $ bundle
24
+
25
+ Or install it yourself as:
26
+
27
+ $ gem install montrose
28
+
29
+ ## Usage
30
+
31
+ ```ruby
32
+ require "montrose"
33
+
34
+ # Minutely
35
+ Montrose.minutely
36
+ Montrose::Recurrence.new(every: :minute)
37
+
38
+ Montrose.every(10.minutes)
39
+ Montrose::Recurrence.new(every: 10.minutes)
40
+ Montrose::Recurrence.new(every: :minute, interval: 10) # every 10 minutes
41
+
42
+ Montrose.minutely(until: "9:00 PM")
43
+ Montrose::Recurrence.new(every: :minute, until: "9:00 PM")
44
+
45
+ # Daily
46
+ Montrose.daily
47
+ Montrose::Recurrence.new(every: :day)
48
+
49
+ Montrose.every(9.days)
50
+ Montrose::Recurrence.new(every: 9.days)
51
+ Montrose::Recurrence.new(every: :day, interval: 9)
52
+
53
+ Montrose.daily(at: "9:00 AM")
54
+ Montrose.every(:day, at: "9:00 AM")
55
+ Montrose::Recurrence.new(every: :day, at: "9:00 AM")
56
+
57
+ Montrose.daily(total: 7)
58
+ Montrose.every(:day, total: 7)
59
+ Montrose::Recurrence.new(every: :day, total: 7)
60
+
61
+ # Weekly
62
+ Montrose.weekly
63
+ Montrose.every(:week)
64
+ Montrose::Recurrence.new(every: :week)
65
+
66
+ # TODO: :on option not yet supported
67
+ Montrose::Recurrence.new(every: :week, on: 5)
68
+ Montrose::Recurrence.new(every: :week, on: :monday)
69
+ Montrose::Recurrence.new(every: :week, on: [:monday, :wednesday, :friday])
70
+ Montrose::Recurrence.new(every: :week, on: :friday, interval: 2)
71
+ Montrose::Recurrence.new(every: :week, on: :friday, repeat: 4)
72
+ Montrose::Recurrence.new(every: :week, on: :friday, at: "3:41 PM")
73
+ Montrose::Recurrence.new(every: :week, on: [:monday, :wednesday, :friday], at: "12:00 PM")
74
+ Montrose::Recurrence.weekly(on: :thursday)
75
+
76
+ # Monthly by month day
77
+ Montrose.monthly(mday: 1) # first of the month
78
+ Montrose.every(:month, mday: 1)
79
+ Montrose::Recurrence.new(every: :month, mday: 1)
80
+
81
+ Montrose.monthly(mday: [2, 15]) # 2nd and 15th of the month
82
+ Montrose.monthly(mday: -3) # third-to-last day of the month
83
+ Montrose.monthly(mday: 10..15) # 10th through the 15th day of the month
84
+
85
+ # TODO: :on option not yet supported
86
+ Montrose::Recurrence.new(every: :month, on: 15)
87
+ Montrose::Recurrence.new(every: :month, on: 31)
88
+ Montrose::Recurrence.new(every: :month, on: 7, interval: 2)
89
+ Montrose::Recurrence.new(every: :month, on: 7, interval: :monthly)
90
+ Montrose::Recurrence.new(every: :month, on: 7, interval: :bimonthly)
91
+ Montrose::Recurrence.new(every: :month, on: 7, repeat: 6)
92
+ Montrose::Recurrence.monthly(on: 31)
93
+
94
+ # Monthly by week day
95
+ Montrose.monthly(day: :friday, interval: 2) # every Friday every other month
96
+ Montrose.every(:month, day: :friday, interval: 2)
97
+ Montrose::Recurrence.new(every: :month, day: :friday, interval: 2)
98
+
99
+ Montrose.monthly(day: { friday: [1] }) # 1st Friday of the month
100
+ Montrose.monthly(day: { Sunday: [1, =1] }) # first and last Sunday of the month
101
+
102
+ Montrose.monthly(mday: 7..13, day: :saturday) # first Saturday that follow the
103
+ first Sunday of the month
104
+
105
+ # TODO: :on option not yet supported
106
+ Montrose::Recurrence.new(every: :month, on: :first, weekday: :sunday)
107
+ Montrose::Recurrence.new(every: :month, on: :third, weekday: :monday)
108
+ Montrose::Recurrence.new(every: :month, on: :last, weekday: :friday)
109
+ Montrose::Recurrence.new(every: :month, on: :last, weekday: :friday, interval: 2)
110
+ Montrose::Recurrence.new(every: :month, on: :last, weekday: :friday, interval: :quarterly)
111
+ Montrose::Recurrence.new(every: :month, on: :last, weekday: :friday, interval: :semesterly)
112
+ Montrose::Recurrence.new(every: :month, on: :last, weekday: :friday, repeat: 3)
113
+
114
+ # Yearly
115
+ Montrose.yearly
116
+ Montrose.every(:year)
117
+ Montrose::Recurrence.new(every: :year)
118
+
119
+ Montrose.yearly(month: [:june, :july]) # yearly in June and July
120
+ Montrose.yearly(month: 6..8, day: :thursday) # yearly in June, July, August on
121
+ Thursday
122
+ Montrose.yearly(yday: [1, 100]) # yearly on the 1st and 100th day of year
123
+ Montrose.yearly(month: :november, day: :tuesday, mday: 2..8) # every four years,
124
+ the first Tuesday after a Monday in November, i.e., Election Day
125
+
126
+ # TODO: :on option not yet supported
127
+ Montrose::Recurrence.new(every: :year, on: [7, 4]) # => [month, day]
128
+ Montrose::Recurrence.new(every: :year, on: [10, 31], interval: 3)
129
+ Montrose::Recurrence.new(every: :year, on: [:jan, 31])
130
+ Montrose::Recurrence.new(every: :year, on: [:january, 31])
131
+ Montrose::Recurrence.new(every: :year, on: [10, 31], repeat: 3)
132
+ Montrose::Recurrence.yearly(on: [:january, 31])
133
+
134
+ # TODO: Remove a date in the series on the given except date(s)
135
+ # :except defaults to being unset
136
+ Montrose::Recurrence.new(every: :day, except: "2017-01-31")
137
+ Montrose::Recurrence.new(every: :day, except: [Date.today, "2017-01-31"])
138
+
139
+ # Enumerating events
140
+ r = Montrose::Recurrence.new(every: :month, mday: 31, until: "January 1, 2017")
141
+ r.each { |time| puts time.to_s }
142
+
143
+ # Merging rules and enumerating
144
+ r.merge(starts: "2017-01-01").each { |time| puts time.to_s }
145
+ r.merge(starts: "2017-01-01").each { |date| puts date.to_s }
146
+ r.merge(until: "2017-01-10").each { |date| puts date.to_s }
147
+ r.merge(through: "2017-01-10").each { |date| puts date.to_s }
148
+ r.merge(starts: "2017-01-05", until: "2017-01-10").each {|date| puts date.to_s }
149
+
150
+ # Using #events Enumerator
151
+ r.events # => #<Enumerator: ...>
152
+ r.events.take(10).each { |date| puts date.to_s }
153
+ r.events.lazy.select { |time| time > 1.month.from_now }.take(3).each { |date| puts date.to_s }
154
+ ```
155
+
156
+ ## Goals
157
+
158
+ `Montrose` intends to:
159
+
160
+ * embrace Ruby idioms
161
+ * support Ruby 2.1+
162
+ * provide a simple-to-use interface
163
+ * be reasonably performant
164
+ * serialize to yaml, hash, and [ical](http://www.kanzaki.com/docs/ical/rrule.html#basic) formats
165
+ * be suitable for integration with persistence libraries
166
+
167
+ What `Montrose` isn't:
168
+
169
+ * support all calendaring use cases under the sun
170
+
171
+ ## Related Projects
172
+
173
+ Check out following related projects, all of which have provided inspiration for `Montrose`.
174
+
175
+ * [recurrence](https://github.com/fnando/recurrence)
176
+ * [ice_cube](https://github.com/seejohnrun/ice_cube)
177
+ * [runt](https://github.com/mlipper/runt)
178
+
179
+ ## Development
180
+
181
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/rake` to run the tests.
182
+
183
+ You can also run `bin/console` for an interactive prompt that will allow you to experiment.
184
+
185
+ ## Contributing
186
+
187
+ 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.
188
+
189
+ ## License
190
+
191
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -0,0 +1,21 @@
1
+ require "bundler/setup"
2
+ require "bundler/gem_tasks"
3
+ require "rake/testtask"
4
+ require "rubocop/rake_task"
5
+ require "yard"
6
+
7
+ YARD::Rake::YardocTask.new do |t|
8
+ t.files = ["README.md", "lib/**/*.rb"]
9
+ end
10
+
11
+ Rake::TestTask.new(:spec) do |t|
12
+ t.libs << "spec"
13
+ t.libs << "lib"
14
+ t.test_files = FileList["spec/**/*_spec.rb"]
15
+ end
16
+
17
+ task test: :spec
18
+
19
+ RuboCop::RakeTask.new
20
+
21
+ task default: [:spec, :rubocop]
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application '_guard-core' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('guard', '_guard-core')