repeatable 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +59 -0
- data/README.md +23 -0
- data/lib/repeatable/expression/base.rb +9 -0
- data/lib/repeatable/expression/date.rb +26 -0
- data/lib/repeatable/expression/day_in_month.rb +1 -5
- data/lib/repeatable/expression/range_in_year.rb +1 -1
- data/lib/repeatable/expression/set.rb +10 -6
- data/lib/repeatable/expression/weekday.rb +1 -5
- data/lib/repeatable/expression/weekday_in_month.rb +1 -5
- data/lib/repeatable/parser.rb +10 -1
- data/lib/repeatable/schedule.rb +5 -3
- data/lib/repeatable/version.rb +1 -1
- data/lib/repeatable.rb +1 -0
- data/repeatable.gemspec +0 -2
- metadata +5 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 49614dbb77808403fec7ce7b17f8e6b4c5c6ee99
|
4
|
+
data.tar.gz: 19759e4931d334759c54f6a80c9856fe92bc01e3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 89a121c2ecf46fbb6cd89bf150a00dff53452fa2cbfddaf4bff89255455f7bab86aa09d50ea0e5a4d60de7306f70096657dfb2f63a59b2092aa2fe5bb9bb149c
|
7
|
+
data.tar.gz: f0c55a63c03e9055b40cc90183ad176eb75027411a71de000a4f95c0f57a6dd85b9ee98a4c581539475c9712fb08f4e59c13f765b3711abb9b2c5719ecf65416
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
## CHANGELOG
|
2
|
+
|
3
|
+
### Unreleased
|
4
|
+
|
5
|
+
[Commits](https://github.com/molawson/repeatable/compare/v0.4.0...master)
|
6
|
+
|
7
|
+
### 0.4.0 (2015-06-29)
|
8
|
+
|
9
|
+
Features:
|
10
|
+
|
11
|
+
* Define equivalence `#==` for `Expression` and `Schedule` objects
|
12
|
+
* Define hash equality `#eql?` for `Expression::Date` objects
|
13
|
+
* Remove `ActiveSupport` dependency
|
14
|
+
|
15
|
+
[Commits](https://github.com/molawson/repeatable/compare/v0.3.0...v0.4.0)
|
16
|
+
|
17
|
+
### 0.3.0 (2015-03-11)
|
18
|
+
|
19
|
+
Features:
|
20
|
+
|
21
|
+
* Ensure `end_date` on or after `start_date` for `Schedule#occurrences` ([@danott][])
|
22
|
+
* Consider any invalid argument to `Schedule.new` a `ParseError` ([@danott][])
|
23
|
+
|
24
|
+
[Commits](https://github.com/molawson/repeatable/compare/v0.2.1...v0.3.0)
|
25
|
+
|
26
|
+
### 0.2.1 (2015-03-09)
|
27
|
+
|
28
|
+
Features:
|
29
|
+
|
30
|
+
* Add `ParseError` class for better error handling
|
31
|
+
* Extract `Parser` class from `Schedule`
|
32
|
+
|
33
|
+
Bugfixes:
|
34
|
+
|
35
|
+
* Enable `Schedule` to take a hash with string keys
|
36
|
+
|
37
|
+
[Commits](https://github.com/molawson/repeatable/compare/v0.2.0...v0.2.1)
|
38
|
+
|
39
|
+
### 0.2.0 (2015-03-03)
|
40
|
+
|
41
|
+
Features:
|
42
|
+
|
43
|
+
* Add `Schedule#to_h` and `Expression#to_h` methods
|
44
|
+
* Enable building a `Schedule` from composed `Expression` objects
|
45
|
+
|
46
|
+
Bugfixes:
|
47
|
+
|
48
|
+
* Fix default case equality for `Expression::Base` to work with classes and instances
|
49
|
+
|
50
|
+
[Commits](https://github.com/molawson/repeatable/compare/v0.1.0...v0.2.0)
|
51
|
+
|
52
|
+
### 0.1.0 (2015-02-23)
|
53
|
+
|
54
|
+
Initial Release
|
55
|
+
|
56
|
+
[Commits](https://github.com/molawson/repeatable/compare/531d40c...v0.1.0)
|
57
|
+
|
58
|
+
|
59
|
+
[@danott]: https://github.com/danott
|
data/README.md
CHANGED
@@ -138,6 +138,29 @@ schedule.to_h
|
|
138
138
|
# can be used to recreate an identical Schedule object at a later time
|
139
139
|
```
|
140
140
|
|
141
|
+
#### Equivalence
|
142
|
+
|
143
|
+
Both `Repeatable::Schedule` and all `Repeatable::Expression` classes have equivalence `#==` defined according to what's appropriate for each class, so regardless of the order of arguments passed to each, you can tell whether one object is equivalent to the other in terms of whether or not, when asked the same questions, you'd receive the same results from each.
|
144
|
+
|
145
|
+
```ruby
|
146
|
+
Repeatable::Expression::DayInMonth.new(day: 1) == Repeatable::Expression::DayInMonth.new(day: 1)
|
147
|
+
# => true
|
148
|
+
|
149
|
+
first = Repeatable::Expression::DayInMonth.new(day: 1)
|
150
|
+
fifteenth = Repeatable::Expression::DayInMonth.new(day: 15)
|
151
|
+
first == fifteenth
|
152
|
+
# => false
|
153
|
+
|
154
|
+
union = Repeatable::Expression::Union.new(first, fifteenth)
|
155
|
+
another_union = Repeatable::Expression::Union.new(fifteenth, first)
|
156
|
+
union == another_union
|
157
|
+
# => true (order of Union and Intersection arguments doesn't their affect output)
|
158
|
+
|
159
|
+
Repeatable::Schedule.new(union) == Repeatable::Schedule.new(another_union)
|
160
|
+
# => true (their expressions are equivalent, so they'll produce the same results)
|
161
|
+
|
162
|
+
```
|
163
|
+
|
141
164
|
## Development
|
142
165
|
|
143
166
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
|
@@ -23,6 +23,15 @@ module Repeatable
|
|
23
23
|
"Don't use Expression::Base directly. Subclasses must implement `#to_h`"
|
24
24
|
)
|
25
25
|
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def hash_key
|
30
|
+
self.class.name.split('::').last
|
31
|
+
.gsub(/(?<!\b)[A-Z]/) { "_#{Regexp.last_match[0]}" }
|
32
|
+
.downcase
|
33
|
+
.to_sym
|
34
|
+
end
|
26
35
|
end
|
27
36
|
end
|
28
37
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Repeatable
|
2
|
+
module Expression
|
3
|
+
class Date < Base
|
4
|
+
|
5
|
+
def to_h
|
6
|
+
Hash[hash_key, attributes]
|
7
|
+
end
|
8
|
+
|
9
|
+
def ==(other)
|
10
|
+
other.is_a?(self.class) && attributes == other.attributes
|
11
|
+
end
|
12
|
+
|
13
|
+
alias eql? ==
|
14
|
+
|
15
|
+
def hash
|
16
|
+
[attributes.values, self.class.name].hash
|
17
|
+
end
|
18
|
+
|
19
|
+
protected
|
20
|
+
|
21
|
+
def attributes
|
22
|
+
Hash[instance_variables.map { |name| [name[1..-1].to_sym, instance_variable_get(name)] }]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Repeatable
|
2
2
|
module Expression
|
3
|
-
class DayInMonth <
|
3
|
+
class DayInMonth < Date
|
4
4
|
def initialize(day:)
|
5
5
|
@day = day
|
6
6
|
end
|
@@ -9,10 +9,6 @@ module Repeatable
|
|
9
9
|
date.day == day
|
10
10
|
end
|
11
11
|
|
12
|
-
def to_h
|
13
|
-
{ day_in_month: { day: day } }
|
14
|
-
end
|
15
|
-
|
16
12
|
private
|
17
13
|
|
18
14
|
attr_reader :day
|
@@ -2,21 +2,25 @@ module Repeatable
|
|
2
2
|
module Expression
|
3
3
|
class Set < Base
|
4
4
|
def initialize(*elements)
|
5
|
-
@elements = elements.flatten
|
5
|
+
@elements = elements.flatten.uniq
|
6
6
|
end
|
7
7
|
|
8
8
|
def <<(element)
|
9
|
-
elements << element
|
9
|
+
elements << element unless elements.include?(element)
|
10
10
|
self
|
11
11
|
end
|
12
12
|
|
13
13
|
def to_h
|
14
|
-
|
15
|
-
hash[self.class.name.demodulize.underscore.to_sym] = elements.map(&:to_h)
|
16
|
-
hash
|
14
|
+
Hash[hash_key, elements.map(&:to_h)]
|
17
15
|
end
|
18
16
|
|
19
|
-
|
17
|
+
def ==(other)
|
18
|
+
other.is_a?(self.class) &&
|
19
|
+
elements.size == other.elements.size &&
|
20
|
+
other.elements.all? { |e| elements.include?(e) }
|
21
|
+
end
|
22
|
+
|
23
|
+
protected
|
20
24
|
|
21
25
|
attr_reader :elements
|
22
26
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Repeatable
|
2
2
|
module Expression
|
3
|
-
class Weekday <
|
3
|
+
class Weekday < Date
|
4
4
|
def initialize(weekday:)
|
5
5
|
@weekday = weekday
|
6
6
|
end
|
@@ -9,10 +9,6 @@ module Repeatable
|
|
9
9
|
date.wday == weekday
|
10
10
|
end
|
11
11
|
|
12
|
-
def to_h
|
13
|
-
{ weekday: { weekday: weekday } }
|
14
|
-
end
|
15
|
-
|
16
12
|
private
|
17
13
|
|
18
14
|
attr_reader :weekday
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Repeatable
|
2
2
|
module Expression
|
3
|
-
class WeekdayInMonth <
|
3
|
+
class WeekdayInMonth < Date
|
4
4
|
def initialize(weekday:, count:)
|
5
5
|
@weekday = weekday
|
6
6
|
@count = count
|
@@ -10,10 +10,6 @@ module Repeatable
|
|
10
10
|
day_matches?(date) && week_matches?(date)
|
11
11
|
end
|
12
12
|
|
13
|
-
def to_h
|
14
|
-
{ weekday_in_month: { weekday: weekday, count: count } }
|
15
|
-
end
|
16
|
-
|
17
13
|
private
|
18
14
|
|
19
15
|
attr_reader :weekday, :count
|
data/lib/repeatable/parser.rb
CHANGED
@@ -25,7 +25,7 @@ module Repeatable
|
|
25
25
|
end
|
26
26
|
|
27
27
|
def expression_for(key, value)
|
28
|
-
klass =
|
28
|
+
klass = expression_klass(key.to_s)
|
29
29
|
case klass
|
30
30
|
when nil
|
31
31
|
fail(ParseError, "Unknown mapping: Can't map key '#{key.inspect}' to an expression class")
|
@@ -40,5 +40,14 @@ module Repeatable
|
|
40
40
|
def symbolize_keys(hash)
|
41
41
|
hash.each_with_object({}) { |(k, v), a| a[k.to_sym] = v }
|
42
42
|
end
|
43
|
+
|
44
|
+
def expression_klass(string)
|
45
|
+
camel_cased_string = string
|
46
|
+
.capitalize
|
47
|
+
.gsub(/(?:_)(?<word>[a-z\d]+)/i) { Regexp.last_match[:word].capitalize }
|
48
|
+
Repeatable::Expression.const_get(camel_cased_string)
|
49
|
+
rescue NameError => e
|
50
|
+
raise if e.name && e.name.to_s != camel_cased_string
|
51
|
+
end
|
43
52
|
end
|
44
53
|
end
|
data/lib/repeatable/schedule.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'active_support/core_ext/string/inflections'
|
2
|
-
|
3
1
|
module Repeatable
|
4
2
|
class Schedule
|
5
3
|
def initialize(arg)
|
@@ -44,7 +42,11 @@ module Repeatable
|
|
44
42
|
expression.to_h
|
45
43
|
end
|
46
44
|
|
47
|
-
|
45
|
+
def ==(other)
|
46
|
+
other.is_a?(self.class) && expression == other.expression
|
47
|
+
end
|
48
|
+
|
49
|
+
protected
|
48
50
|
|
49
51
|
attr_reader :expression
|
50
52
|
end
|
data/lib/repeatable/version.rb
CHANGED
data/lib/repeatable.rb
CHANGED
@@ -13,6 +13,7 @@ require 'repeatable/parse_error'
|
|
13
13
|
require 'repeatable/expression'
|
14
14
|
require 'repeatable/expression/base'
|
15
15
|
|
16
|
+
require 'repeatable/expression/date'
|
16
17
|
require 'repeatable/expression/weekday'
|
17
18
|
require 'repeatable/expression/weekday_in_month'
|
18
19
|
require 'repeatable/expression/day_in_month'
|
data/repeatable.gemspec
CHANGED
@@ -16,8 +16,6 @@ Gem::Specification.new do |spec|
|
|
16
16
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
17
17
|
spec.require_paths = ['lib']
|
18
18
|
|
19
|
-
spec.add_dependency 'activesupport', '>= 3.0'
|
20
|
-
|
21
19
|
spec.add_development_dependency 'bundler', '~> 1.6'
|
22
20
|
spec.add_development_dependency 'rake', '~> 10.0'
|
23
21
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
metadata
CHANGED
@@ -1,29 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: repeatable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mo Lawson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-06-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: activesupport
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - ">="
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '3.0'
|
20
|
-
type: :runtime
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - ">="
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '3.0'
|
27
13
|
- !ruby/object:Gem::Dependency
|
28
14
|
name: bundler
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
@@ -77,6 +63,7 @@ files:
|
|
77
63
|
- ".gitignore"
|
78
64
|
- ".rspec"
|
79
65
|
- ".travis.yml"
|
66
|
+
- CHANGELOG.md
|
80
67
|
- Gemfile
|
81
68
|
- LICENSE.txt
|
82
69
|
- README.md
|
@@ -87,6 +74,7 @@ files:
|
|
87
74
|
- lib/repeatable/conversions.rb
|
88
75
|
- lib/repeatable/expression.rb
|
89
76
|
- lib/repeatable/expression/base.rb
|
77
|
+
- lib/repeatable/expression/date.rb
|
90
78
|
- lib/repeatable/expression/day_in_month.rb
|
91
79
|
- lib/repeatable/expression/intersection.rb
|
92
80
|
- lib/repeatable/expression/range_in_year.rb
|
@@ -124,3 +112,4 @@ signing_key:
|
|
124
112
|
specification_version: 4
|
125
113
|
summary: Describe recurring event schedules and calculate their occurrences
|
126
114
|
test_files: []
|
115
|
+
has_rdoc:
|