recurify 0.0.2 → 0.0.3
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 +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
|