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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f67467f617bc9f0301b2adea071f0399f6764157
4
- data.tar.gz: b1406f9990dc5ce45a8b176b623f7ade80d0c2de
3
+ metadata.gz: 4307b3688528bbcb1af64ae77bf4280a234add0e
4
+ data.tar.gz: e299a294802143fafa24566269bb99ff30e6cff7
5
5
  SHA512:
6
- metadata.gz: 2866e6d97a2077b9161075adb1212aa83c6524f06a241b3803dd069c4c9c8a8c2e1d897834ffa612482689c662db88518fbf201174e0733d484320ae87b661c2
7
- data.tar.gz: 41175f2ce891a1dbc2e3b522ebdba54f0d10cacfbbed277fd2454fe6ab4776cd2ccf36a6d9f49f0592c5322c12561b01791ce559a5170426187581aa2f18b107
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
- Recurrence Rule as input and returns an [`Enumerable`][enumerable], containing
11
- all [`Date`][date] objects resulting from this Rule in chronlogical order.
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 upgrade to latest version of rubocop`.
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,
@@ -73,7 +73,7 @@ module Recurify
73
73
  # @param other [Rule]
74
74
  # @return [Boolean]
75
75
  def ==(other)
76
- normalize.attributes == other.normalize.attributes
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
- Rule::BASE_FREQUENCIES.include?(frequency)
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
- NORMALIZATION_MATRIX = {
8
- # Normalization is a no-op for base-frequencies ...
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
- DENORMALIZATION_MATRIX = {
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 normalized. Note that
45
- # +#normalize+ is idempotent.
40
+ # Creates a new +Rule+, similar to +self+, but with minimized +#frequency+.
41
+ # Note that +#minimize_frequency+ is idempotent.
46
42
  #
47
- # Normalization means that +Rule+s with +#frequency+ ...
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
- # minify the +#ends_on+ and +#count+ attributes (if possible).
66
+ # minimize +#ends_on+ and +#count+ (if possible).
56
67
  #
57
68
  # @return [Rule]
58
69
  def normalize
59
- @_normalized_rule ||= begin
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 denormalized. Note that
94
- # +#denormalized+ is idempotent.
103
+ # Creates a new +Rule+, similar to +self+, but with maximized +#frequency+.
104
+ # Note that +#maximize_frequency+ is idempotent.
95
105
  #
96
- # Denormalizion means that +Rule+s with +#frequency+ ...
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 +#denormalize+ at all? The idea is that denormalized +Rule+s are
104
- # easier to parse for humans. For instance, "every 7th week" is easier to
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 denormalize
109
- @_denormalized_rule ||= begin
110
- translate_with = find_best_denormalization
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
@@ -1,3 +1,3 @@
1
1
  module Recurify # :nodoc:
2
- VERSION = '0.0.2'.freeze
2
+ VERSION = '0.0.3'.freeze
3
3
  end
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.2
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-02 00:00:00.000000000 Z
11
+ date: 2015-12-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec