recurify 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +5 -4
- data/lib/recurify/rule.rb +1 -1
- data/lib/recurify/rule_analysis.rb +8 -1
- data/lib/recurify/rule_translation.rb +61 -45
- data/lib/recurify/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4307b3688528bbcb1af64ae77bf4280a234add0e
|
4
|
+
data.tar.gz: e299a294802143fafa24566269bb99ff30e6cff7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f5fc6cac84aab3d11c52258492b3fe5848cfa25be00157c6f9ecb8a46f2b66caf2a8df44e8f7c3c2784d48a0f54c266320dfcc43398b0d441d103004e981a6b7
|
7
|
+
data.tar.gz: 54ed94321f48ed1a6a6340b9f2851f2974d9633622e7be2a4d03a8f1adef3af57d2ccc32caee9b887f2e87e036166db44c99952e81152b6e831e3356a84fbb8e
|
data/README.md
CHANGED
@@ -7,8 +7,9 @@
|
|
7
7
|
**Recurify is very much "work in progress" and shoudn't be used yet.**
|
8
8
|
|
9
9
|
Recurify is a simple, light-weight, evaluator for Recurrence Rules. It takes a
|
10
|
-
|
11
|
-
|
10
|
+
`Rule` as input and returns an [`Enumerable`][enumerable], yielding all
|
11
|
+
occurrences (i.e. simple [`Date`][date] objects) resulting from the `Rule`'s
|
12
|
+
evaluation in chronlogical order.
|
12
13
|
|
13
14
|
[enumerable]: http://ruby-doc.org/core-2.2.3/Enumerable.html
|
14
15
|
[date]: http://ruby-doc.org/stdlib-2.2.3/libdoc/date/rdoc/Date.html
|
@@ -23,11 +24,11 @@ all [`Date`][date] objects resulting from this Rule in chronlogical order.
|
|
23
24
|
* `F` - **F**eature commits with functional changes.
|
24
25
|
Example commit message: `F implement Rule#normalize`.
|
25
26
|
* `B` - **B**ugfix commits for fixing broken stuff.
|
26
|
-
Example commit message: `B consider Rule#count in Rule
|
27
|
+
Example commit message: `B consider Rule#count in Rule#==`.
|
27
28
|
* `R` - **R**efactoring commits without functional changes.
|
28
29
|
Example commit message: `R memoize Rule#normalize for performance`.
|
29
30
|
* `C` - **C**hore commits are for everything else.
|
30
|
-
Example commit message: `C
|
31
|
+
Example commit message: `C bump version to 1.0.0`.
|
31
32
|
|
32
33
|
Prefixed commit messages may seem a bit strange at first. The intention is
|
33
34
|
simply to keep functional and non-functional changes clearly apart. Therefore,
|
data/lib/recurify/rule.rb
CHANGED
@@ -73,7 +73,7 @@ module Recurify
|
|
73
73
|
# @param other [Rule]
|
74
74
|
# @return [Boolean]
|
75
75
|
def ==(other)
|
76
|
-
|
76
|
+
normalized_attributes == other.normalized_attributes
|
77
77
|
end
|
78
78
|
|
79
79
|
# Convert +self+ to a +Hash+, representing the same +Rule+. Note, that
|
@@ -7,7 +7,7 @@ module Recurify
|
|
7
7
|
#
|
8
8
|
# @return [Boolean]
|
9
9
|
def normalized?
|
10
|
-
|
10
|
+
attributes == normalized_attributes
|
11
11
|
end
|
12
12
|
|
13
13
|
# Returns +true+ if +self+ is denormalized. By definition, a +Rule+ is
|
@@ -42,6 +42,13 @@ module Recurify
|
|
42
42
|
!finite?
|
43
43
|
end
|
44
44
|
|
45
|
+
# Returns +true+ if and only if +self+ evaluates to a single occurrence.
|
46
|
+
#
|
47
|
+
# @return [Boolean]
|
48
|
+
def one?
|
49
|
+
starts_on == normalized_ends_on
|
50
|
+
end
|
51
|
+
|
45
52
|
# Returns +true+ if +self+ starts before or on +upper+. If +upper == nil+,
|
46
53
|
# it is interpreted as "positive infinity". In that case, +#starts_before?+
|
47
54
|
# always returns +true+, because all +Rule+ objects must have a finite
|
@@ -4,68 +4,78 @@ module Recurify
|
|
4
4
|
module RuleTranslation # :nodoc:
|
5
5
|
extend Forwardable
|
6
6
|
|
7
|
-
|
8
|
-
|
7
|
+
# Applicable frequency minimizations grouped by +#frequency+ ...
|
8
|
+
FREQUENCY_MINIMIZATION_MATRIX = {
|
9
9
|
'daily' => { target_frequency: 'daily', interval_multiplier: 1 },
|
10
10
|
'monthly' => { target_frequency: 'monthly', interval_multiplier: 1 },
|
11
|
-
# But, it is more interesting for sugar-frequencies ...
|
12
11
|
'weekly' => { target_frequency: 'daily', interval_multiplier: 7 },
|
13
12
|
'quarterly' => { target_frequency: 'monthly', interval_multiplier: 3 },
|
14
13
|
'yearly' => { target_frequency: 'monthly', interval_multiplier: 12 }
|
15
14
|
}.freeze
|
16
15
|
|
17
|
-
|
16
|
+
# Applicable frequency maximizations grouped by +#frequency+ and ordered by
|
17
|
+
# decreasing preferability ...
|
18
|
+
FREQUENCY_MAXIMIZATION_MATRIX = {
|
18
19
|
'daily' => [
|
19
|
-
# Applicable denormalizitions ordered by decreasing preferability ...
|
20
20
|
{ target_frequency: 'weekly', interval_divisor: 7 },
|
21
21
|
{ target_frequency: 'daily', interval_divisor: 1 }
|
22
22
|
],
|
23
23
|
'weekly' => [
|
24
|
-
# Applicable denormalizitions ordered by decreasing preferability ...
|
25
24
|
{ target_frequency: 'weekly', interval_divisor: 1 }
|
26
25
|
],
|
27
26
|
'monthly' => [
|
28
|
-
# Applicable denormalizitions ordered by decreasing preferability ...
|
29
27
|
{ target_frequency: 'yearly', interval_divisor: 12 },
|
30
28
|
{ target_frequency: 'quarterly', interval_divisor: 3 },
|
31
29
|
{ target_frequency: 'monthly', interval_divisor: 1 }
|
32
30
|
],
|
33
31
|
'quarterly' => [
|
34
|
-
# Applicable denormalizitions ordered by decreasing preferability ...
|
35
32
|
{ target_frequency: 'yearly', interval_divisor: 4 },
|
36
33
|
{ target_frequency: 'quarterly', interval_divisor: 1 }
|
37
34
|
],
|
38
35
|
'yearly' => [
|
39
|
-
# Applicable denormalizitions ordered by decreasing preferability ...
|
40
36
|
{ target_frequency: 'yearly', interval_divisor: 1 }
|
41
37
|
]
|
42
38
|
}.freeze
|
43
39
|
|
44
|
-
# Creates a new +Rule+, similar to +self+, but
|
45
|
-
# +#
|
40
|
+
# Creates a new +Rule+, similar to +self+, but with minimized +#frequency+.
|
41
|
+
# Note that +#minimize_frequency+ is idempotent.
|
46
42
|
#
|
47
|
-
#
|
43
|
+
# Frequency minimization means that +Rule+s with +#frequency+ ...
|
48
44
|
# * ... "weekly" are translated to +Rule+s with +#frequency+ "daily".
|
49
45
|
# * ... "quarterly" are translated to +Rule+s with +#frequency+ "monthly".
|
50
46
|
# * ... "yearly" are translated to +Rule+s with +#frequency+ "monthly".
|
51
47
|
#
|
52
48
|
# Additional translations may be added in the future.
|
53
49
|
#
|
50
|
+
# @see FREQUENCY_MINIMIZATION_MATRIX
|
51
|
+
#
|
52
|
+
# @return [Rule]
|
53
|
+
def minimize_frequency
|
54
|
+
translate_with = FREQUENCY_MINIMIZATION_MATRIX[frequency]
|
55
|
+
|
56
|
+
substitute(
|
57
|
+
frequency: translate_with[:target_frequency],
|
58
|
+
interval: interval * translate_with[:interval_multiplier]
|
59
|
+
)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Creates a new +Rule+, similar to +self+, but normalized. Note that
|
63
|
+
# +#normalize+ is idempotent.
|
64
|
+
#
|
54
65
|
# @todo This method is not yet complete, because +#normalize+ should also
|
55
|
-
#
|
66
|
+
# minimize +#ends_on+ and +#count+ (if possible).
|
56
67
|
#
|
57
68
|
# @return [Rule]
|
58
69
|
def normalize
|
59
|
-
@_normalized_rule ||=
|
60
|
-
translate_with = NORMALIZATION_MATRIX[frequency]
|
61
|
-
|
62
|
-
substitute(
|
63
|
-
frequency: translate_with[:target_frequency],
|
64
|
-
interval: interval * translate_with[:interval_multiplier]
|
65
|
-
)
|
66
|
-
end
|
70
|
+
@_normalized_rule ||= minimize_frequency
|
67
71
|
end
|
68
72
|
|
73
|
+
# @!method normalized_attributes
|
74
|
+
# @see Rule#attributes
|
75
|
+
# @see #normalize
|
76
|
+
# @return [Hash<Symbol,Object>]
|
77
|
+
def_delegator :normalize, :attributes, :normalized_attributes
|
78
|
+
|
69
79
|
# @!method normalized_frequency
|
70
80
|
# @see Rule#frequency
|
71
81
|
# @see #normalize
|
@@ -90,32 +100,48 @@ module Recurify
|
|
90
100
|
# @return [Date, nil]
|
91
101
|
def_delegator :normalize, :ends_on, :normalized_ends_on
|
92
102
|
|
93
|
-
# Creates a new +Rule+, similar to +self+, but
|
94
|
-
# +#
|
103
|
+
# Creates a new +Rule+, similar to +self+, but with maximized +#frequency+.
|
104
|
+
# Note that +#maximize_frequency+ is idempotent.
|
95
105
|
#
|
96
|
-
#
|
106
|
+
# Frequency maximization means that +Rule+s with +#frequency+ ...
|
97
107
|
# * ... "daily" are translated to +Rule+s with +#frequency+ "weekly".
|
98
108
|
# * ... "monthly" are translated to +Rule+s with +#frequency+ "yearly".
|
99
109
|
# * ... "monthly" are translated to +Rule+s with +#frequency+ "quarterly".
|
100
110
|
#
|
101
111
|
# Additional translations may be added in the future.
|
102
112
|
#
|
103
|
-
# Why +#
|
104
|
-
# easier to parse for humans. For instance, "every 7th week" is
|
105
|
-
# understand than "every 49th day".
|
113
|
+
# Why maximize the +#frequency+ at all? The idea is that frequency-maximized
|
114
|
+
# +Rule+s are easier to parse for humans. For instance, "every 7th week" is
|
115
|
+
# easier to understand than "every 49th day".
|
116
|
+
#
|
117
|
+
# @see FREQUENCY_MAXIMIZATION_MATRIX
|
106
118
|
#
|
107
119
|
# @return [Rule]
|
108
|
-
def
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
substitute(
|
113
|
-
frequency: translate_with[:target_frequency],
|
114
|
-
interval: interval / translate_with[:interval_divisor]
|
115
|
-
)
|
120
|
+
def maximize_frequency
|
121
|
+
translate_with = FREQUENCY_MAXIMIZATION_MATRIX[frequency].find do |c|
|
122
|
+
interval % c[:interval_divisor] == 0
|
116
123
|
end
|
124
|
+
|
125
|
+
substitute(
|
126
|
+
frequency: translate_with[:target_frequency],
|
127
|
+
interval: interval / translate_with[:interval_divisor]
|
128
|
+
)
|
129
|
+
end
|
130
|
+
|
131
|
+
# Creates a new +Rule+, similar to +self+, but denormalized. Note that
|
132
|
+
# +#denormalized+ is idempotent.
|
133
|
+
#
|
134
|
+
# @return [Rule]
|
135
|
+
def denormalize
|
136
|
+
@_denormalized_rule ||= maximize_frequency
|
117
137
|
end
|
118
138
|
|
139
|
+
# @!method denormalized_attributes
|
140
|
+
# @see Rule#attributes
|
141
|
+
# @see #denormalize
|
142
|
+
# @return [Hash<Symbol,Object>]
|
143
|
+
def_delegator :denormalize, :attributes, :denormalized_attributes
|
144
|
+
|
119
145
|
# @!method denormalized_frequency
|
120
146
|
# @see Rule#frequency
|
121
147
|
# @see #denormalize
|
@@ -139,15 +165,5 @@ module Recurify
|
|
139
165
|
# @see #denormalize
|
140
166
|
# @return [Date, nil]
|
141
167
|
def_delegator :denormalize, :ends_on, :denormalized_ends_on
|
142
|
-
|
143
|
-
private
|
144
|
-
|
145
|
-
# @see DENORMALIZATION_MATRIX
|
146
|
-
# @return [Hash<Symbol, Object>]
|
147
|
-
def find_best_denormalization
|
148
|
-
DENORMALIZATION_MATRIX[frequency].find do |contender|
|
149
|
-
normalize.interval % contender[:interval_divisor] == 0
|
150
|
-
end
|
151
|
-
end
|
152
168
|
end
|
153
169
|
end
|
data/lib/recurify/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: recurify
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Christoph Schiessl
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-12-
|
11
|
+
date: 2015-12-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|