toilet_tracker 0.1.1 β†’ 0.2.1

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
  SHA256:
3
- metadata.gz: 64738532b1c80623a41ce64ead3709e4802702b03f87c593ccbc7edbeb1ab7a6
4
- data.tar.gz: 18c6967a7280153c7e8b0022bb4e8d42e2a422870e3d0af92e5a5b57c23f57eb
3
+ metadata.gz: 4e179c9943225797758be0ca803a1dcde9ee9f7b98ee7466d74cb208a49040b8
4
+ data.tar.gz: ea5b5ab8fb0e4da1ca4a8e7e48575bc72a9a967769c375e52e11dcfab3dd4801
5
5
  SHA512:
6
- metadata.gz: 1e45b15dc41c8d141284bd94abb2d650052722b02344830f8b06fcb264d7e2e0cb320ac2c845d11280eb3e1faa1cfa12ff2b670a65df4b5535f7b1b563517723
7
- data.tar.gz: 86504e632326c8f0c21a6ee3b7845bc129a809e1573c6efc2796232eed6dacffd02037a718c556db233b7e64aee1ab6cbc69efa701b4a657a7429eba492eeb94
6
+ metadata.gz: 33081c85110a522542ae3c50209cbe5a1d302bb34bb513bc4c6118daaabf0093673e37695a7200818ed77d5e37bd725e22074a7898cd82f63df9518c105beaf9
7
+ data.tar.gz: 0ebf346d3c2afde44224885c4879bad74e1d29203c7c2f567fc573fbc989e48eee71f613257707df3e4b4359a861356111cd1e2395f9035d573259de6d0579c8
data/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.2.1] - 2025-12-18
4
+
5
+ ### Changed
6
+ - Update activesupport to 8.1.1 (from 8.0.2.1)
7
+ - Update rubyzip to 3.2.2 (from 3.0.1)
8
+ - Update rake to 13.3.1 (from 13.3.0)
9
+ - Update rspec to 3.13.2 (from 3.13.1)
10
+ - Update overcommit to 0.68.0 (from 0.67.1)
11
+ - Relax version constraints for activesupport and rubyzip
12
+
13
+ ## [0.2.0] - 2025-12-18
14
+
15
+ ### Added
16
+ - Metadata emoji support for both πŸ’© and 🚽 commands
17
+ - Format: `[command emoji] [metadata emojis...] [shift/timezone] [time]`
18
+ - Metadata stored as array of individual emoji strings
19
+ - New `metadata` field on `PoopEventResult` and `ToiletShiftResult`
20
+ - `BaseParser#parse_metadata` and `#metadata_emoji?` helper methods
21
+ - Comprehensive test suite for metadata parsing (28 new tests)
22
+
23
+ ### Changed
24
+ - All regex patterns updated to capture optional metadata group
25
+ - Result objects now include `metadata: []` by default (backward compatible)
26
+
3
27
  ## [0.1.1] - 2025-12-18
4
28
 
5
29
  ### Added
data/PARSING_SPEC.md CHANGED
@@ -20,6 +20,24 @@ All WhatsApp messages follow this format:
20
20
  - **Functional Shifts**: Applied to event timestamps (patterns 1, 2, 5)
21
21
  - **Decorative Shifts**: For statistics only, do not affect timestamps (patterns 7, 9)
22
22
 
23
+ ### Metadata Emojis
24
+ Both πŸ’© and 🚽 commands support optional metadata emojis placed between the command emoji and the time/shift specifications. Metadata emojis are stored as an array and can be used for additional categorization or tagging of events.
25
+
26
+ **Format**: `[command emoji] [metadata emojis...] [shift/timezone] [time]`
27
+
28
+ **Examples**:
29
+ - `πŸ’© 🩸` β†’ Event with metadata `["🩸"]`
30
+ - `πŸ’© πŸ©ΈπŸ’§πŸ”₯` β†’ Event with metadata `["🩸", "πŸ’§", "πŸ”₯"]`
31
+ - `πŸ’© 🩸 +2` β†’ Event with metadata `["🩸"]` and shift `+2`
32
+ - `🚽 ✈️🌍 +5:30` β†’ Setting with metadata `["✈️", "🌍"]` and shift `+5:30`
33
+
34
+ **Behavior**:
35
+ - Metadata is any emoji placed after the command emoji but before time specs
36
+ - Emojis are extracted as individual grapheme clusters (supports complex emojis)
37
+ - Command emojis (πŸ’©, 🚽) are excluded from metadata
38
+ - Stored as an array of strings in result objects
39
+ - Does not affect parsing of shifts, timezones, or times
40
+
23
41
  ## Supported Parsing Patterns
24
42
 
25
43
  ### WhatsApp Message Format
@@ -262,8 +280,9 @@ When timezone shifts are set retroactively:
262
280
  type: :shift_set | :shift_set_at_time | :timezone_set,
263
281
  status: :ok | :alert | :error,
264
282
  message: Core::Message,
265
- shift: Integer,
283
+ shift: Integer | Float,
266
284
  effective_time: Time,
285
+ metadata: Array<String>,
267
286
  error_details: String | nil
268
287
  }
269
288
  ```
@@ -275,9 +294,10 @@ When timezone shifts are set retroactively:
275
294
  :event_in_timezone | :event_in_timezone_at_time,
276
295
  status: :ok | :alert | :error,
277
296
  message: Core::Message,
278
- shift: Integer | nil,
297
+ shift: Integer | Float | nil,
279
298
  poop_time: Time,
280
299
  timezone: String | nil,
300
+ metadata: Array<String>,
281
301
  error_details: String | nil
282
302
  }
283
303
  ```
@@ -342,6 +362,16 @@ When timezone shifts are set retroactively:
342
362
  [02/01/25, 15:00:00] Bob: πŸ’© PST 2025-01-02 12:00:00
343
363
  ```
344
364
 
365
+ ### Valid Examples with Metadata
366
+ ```
367
+ [01/01/25, 08:30:00] Alice: πŸ’© 🩸 # metadata: ["🩸"]
368
+ [01/01/25, 10:00:00] Alice: πŸ’© πŸ©ΈπŸ’§ +2 # metadata: ["🩸", "πŸ’§"], shift: 2
369
+ [01/01/25, 12:00:00] Alice: πŸ’© 🧱 14:30 # metadata: ["🧱"], time: 14:30
370
+ [01/01/25, 14:00:00] Alice: πŸ’© πŸ”₯ +2 2025-01-01 13:00 # metadata + shift + time
371
+ [02/01/25, 09:00:00] Bob: 🚽 ✈️ +5:30 # metadata: ["✈️"], shift: 5.5
372
+ [02/01/25, 10:00:00] Bob: 🚽 🏠✈️ Europe/Rome # metadata: ["🏠", "✈️"], timezone
373
+ ```
374
+
345
375
  ### Invalid Examples
346
376
  ```
347
377
  [01/01/25, 10:00:00] Alice: 🚽 +25 # Exceeds max_shift_hours
@@ -9,37 +9,38 @@ module ToiletTracker
9
9
  def initialize
10
10
  @default_timezone = "UTC"
11
11
  @max_shift_hours = 12
12
+ # Capture group 1 is always metadata emojis, subsequent groups are pattern-specific
12
13
  @message_patterns = {
13
- # πŸ’© 14:30
14
- event_at_time_only: /πŸ’©\s*(\d{1,2}:\d{2})$/,
15
- # πŸ’© +5 14:30, πŸ’© +5:30 14:30
16
- event_at_time_only_shifted: /πŸ’©\s*([+-]?\d+(?::\d{2})?)\s+(\d{1,2}:\d{2})$/,
17
- # πŸ’© Asia/Kolkata 14:30
18
- event_in_timezone_at_time_only: %r{πŸ’©\s*([A-Za-z_/][A-Za-z0-9_/+-]*)\s+(\d{1,2}:\d{2})$},
19
- # 🚽 +5 14:30, 🚽 +5:30 14:30
20
- shift_set_at_time_only: /🚽\s*([+-]?\d+(?::\d{2})?)\s+(\d{1,2}:\d{2})$/,
21
- # 🚽 Asia/Kolkata 14:30
22
- timezone_set_at_time_only: %r{🚽\s*([A-Za-z_/][A-Za-z0-9_/+-]*)\s+(\d{1,2}:\d{2})$},
23
- # 🚽 +2, 🚽 -3, 🚽 +5:30
24
- shift_set: /🚽\s*([+-]?\d+(?::\d{2})?)$/,
25
- # 🚽 +2 2025-01-15 14:30, 🚽 +5:30 2025-01-15 14:30
26
- shift_set_at_time: %r{🚽\s*([+-]?\d+(?::\d{2})?)\s+(\d{4}[-/]\d{1,2}[-/]\d{1,2}\s+\d{1,2}:\d{1,2}(?::\d{1,2})?)$},
27
- # 🚽 Europe/Rome, 🚽 PST
28
- timezone_set: %r{🚽\s*([A-Za-z_/][A-Za-z0-9_/+-]*)$},
29
- # 🚽 Asia/Kolkata 2025-01-15 14:30
30
- timezone_set_at_time: %r{🚽\s*([A-Za-z_/][A-Za-z0-9_/+-]*)\s+(\d{4}[-/]\d{1,2}[-/]\d{1,2}\s+\d{1,2}:\d{1,2}(?::\d{1,2})?)$},
31
- # πŸ’©
32
- event_now: /πŸ’©$/,
33
- # πŸ’© +2, πŸ’© -1, πŸ’© +5:30
34
- event_now_shifted: /πŸ’©\s*([+-]?\d+(?::\d{2})?)$/,
35
- # πŸ’© 2025-01-15 14:30
36
- event_at_time: %r{πŸ’©\s*(\d{4}[-/]\d{1,2}[-/]\d{1,2}\s+\d{1,2}:\d{1,2}(?::\d{1,2})?)$},
37
- # πŸ’© +2 2025-01-15 14:30, πŸ’© +5:30 2025-01-15 14:30
38
- event_at_time_shifted: %r{πŸ’©\s*([+-]?\d+(?::\d{2})?)\s+(\d{4}[-/]\d{1,2}[-/]\d{1,2}\s+\d{1,2}:\d{1,2}(?::\d{1,2})?)$},
39
- # πŸ’© Europe/Rome, πŸ’© PST
40
- event_in_timezone: %r{πŸ’©\s*([A-Za-z_/][A-Za-z0-9_/+-]*)\s*$},
41
- # πŸ’© Asia/Kolkata 2025-01-15 14:30
42
- event_in_timezone_at_time: %r{πŸ’©\s*([A-Za-z_/][A-Za-z0-9_/+-]*)\s+(\d{4}[-/]\d{1,2}[-/]\d{1,2}\s+\d{1,2}:\d{1,2}(?::\d{1,2})?)$}
14
+ # πŸ’© 🩸 14:30
15
+ event_at_time_only: /πŸ’©\s*([^+\-\dA-Za-z\s]*)\s*(\d{1,2}:\d{2})$/,
16
+ # πŸ’© 🩸 +5 14:30, πŸ’© 🩸 +5:30 14:30
17
+ event_at_time_only_shifted: /πŸ’©\s*([^+\-\dA-Za-z\s]*)\s*([+-]?\d+(?::\d{2})?)\s+(\d{1,2}:\d{2})$/,
18
+ # πŸ’© 🩸 Asia/Kolkata 14:30
19
+ event_in_timezone_at_time_only: %r{πŸ’©\s*([^+\-\dA-Za-z\s]*)\s*([A-Za-z_/][A-Za-z0-9_/+-]*)\s+(\d{1,2}:\d{2})$},
20
+ # 🚽 πŸ“ +5 14:30, 🚽 πŸ“ +5:30 14:30
21
+ shift_set_at_time_only: /🚽\s*([^+\-\dA-Za-z\s]*)\s*([+-]?\d+(?::\d{2})?)\s+(\d{1,2}:\d{2})$/,
22
+ # 🚽 πŸ“ Asia/Kolkata 14:30
23
+ timezone_set_at_time_only: %r{🚽\s*([^+\-\dA-Za-z\s]*)\s*([A-Za-z_/][A-Za-z0-9_/+-]*)\s+(\d{1,2}:\d{2})$},
24
+ # 🚽 πŸ“ +2, 🚽 πŸ“ -3, 🚽 πŸ“ +5:30
25
+ shift_set: /🚽\s*([^+\-\dA-Za-z\s]*)\s*([+-]?\d+(?::\d{2})?)$/,
26
+ # 🚽 πŸ“ +2 2025-01-15 14:30, 🚽 πŸ“ +5:30 2025-01-15 14:30
27
+ shift_set_at_time: %r{🚽\s*([^+\-\dA-Za-z\s]*)\s*([+-]?\d+(?::\d{2})?)\s+(\d{4}[-/]\d{1,2}[-/]\d{1,2}\s+\d{1,2}:\d{1,2}(?::\d{1,2})?)$},
28
+ # 🚽 πŸ“ Europe/Rome, 🚽 πŸ“ PST
29
+ timezone_set: %r{🚽\s*([^+\-\dA-Za-z\s]*)\s*([A-Za-z_/][A-Za-z0-9_/+-]*)$},
30
+ # 🚽 πŸ“ Asia/Kolkata 2025-01-15 14:30
31
+ timezone_set_at_time: %r{🚽\s*([^+\-\dA-Za-z\s]*)\s*([A-Za-z_/][A-Za-z0-9_/+-]*)\s+(\d{4}[-/]\d{1,2}[-/]\d{1,2}\s+\d{1,2}:\d{1,2}(?::\d{1,2})?)$},
32
+ # πŸ’©, πŸ’© 🩸
33
+ event_now: /πŸ’©\s*([^+\-\dA-Za-z\s]*)$/,
34
+ # πŸ’© 🩸 +2, πŸ’© 🩸 -1, πŸ’© 🩸 +5:30
35
+ event_now_shifted: /πŸ’©\s*([^+\-\dA-Za-z\s]*)\s*([+-]?\d+(?::\d{2})?)$/,
36
+ # πŸ’© 🩸 2025-01-15 14:30
37
+ event_at_time: %r{πŸ’©\s*([^+\-\dA-Za-z\s]*)\s*(\d{4}[-/]\d{1,2}[-/]\d{1,2}\s+\d{1,2}:\d{1,2}(?::\d{1,2})?)$},
38
+ # πŸ’© 🩸 +2 2025-01-15 14:30, πŸ’© 🩸 +5:30 2025-01-15 14:30
39
+ event_at_time_shifted: %r{πŸ’©\s*([^+\-\dA-Za-z\s]*)\s*([+-]?\d+(?::\d{2})?)\s+(\d{4}[-/]\d{1,2}[-/]\d{1,2}\s+\d{1,2}:\d{1,2}(?::\d{1,2})?)$},
40
+ # πŸ’© 🩸 Europe/Rome, πŸ’© 🩸 PST
41
+ event_in_timezone: %r{πŸ’©\s*([^+\-\dA-Za-z\s]*)\s*([A-Za-z_/][A-Za-z0-9_/+-]*)\s*$},
42
+ # πŸ’© 🩸 Asia/Kolkata 2025-01-15 14:30
43
+ event_in_timezone_at_time: %r{πŸ’©\s*([^+\-\dA-Za-z\s]*)\s*([A-Za-z_/][A-Za-z0-9_/+-]*)\s+(\d{4}[-/]\d{1,2}[-/]\d{1,2}\s+\d{1,2}:\d{1,2}(?::\d{1,2})?)$}
43
44
  }
44
45
  @date_formats = [
45
46
  "%Y-%m-%d %H:%M:%S",
@@ -22,15 +22,15 @@ module ToiletTracker
22
22
  end
23
23
 
24
24
  # Result for toilet shift settings
25
- ToiletShiftResult = Data.define(:type, :status, :message, :shift, :effective_time, :error_details) do
26
- def initialize(type:, status:, message:, shift: nil, effective_time: nil, error_details: nil)
25
+ ToiletShiftResult = Data.define(:type, :status, :message, :shift, :effective_time, :metadata, :error_details) do
26
+ def initialize(type:, status:, message:, shift: nil, effective_time: nil, metadata: [], error_details: nil)
27
27
  super
28
28
  end
29
29
  end
30
30
 
31
31
  # Result for poop events
32
- PoopEventResult = Data.define(:type, :status, :message, :shift, :poop_time, :timezone, :error_details) do
33
- def initialize(type:, status:, message:, shift: nil, poop_time: nil, timezone: nil, error_details: nil)
32
+ PoopEventResult = Data.define(:type, :status, :message, :shift, :poop_time, :timezone, :metadata, :error_details) do
33
+ def initialize(type:, status:, message:, shift: nil, poop_time: nil, timezone: nil, metadata: [], error_details: nil)
34
34
  super
35
35
  end
36
36
  end
@@ -98,6 +98,21 @@ module ToiletTracker
98
98
  time_diff = (message_time - event_time).abs
99
99
  (time_diff > 30.days) ? :alert : :ok
100
100
  end
101
+
102
+ def parse_metadata(metadata_string)
103
+ return [] if metadata_string.nil? || metadata_string.strip.empty?
104
+
105
+ metadata_string.grapheme_clusters
106
+ .reject { |char| char.match?(/\s/) }
107
+ .select { |char| metadata_emoji?(char) }
108
+ end
109
+
110
+ def metadata_emoji?(char)
111
+ return false if char.match?(/\A[\p{ASCII}]\z/)
112
+ return false if %w[πŸ’© 🚽].include?(char)
113
+
114
+ char.match?(/\p{Emoji}/) && !char.match?(/\A\d\z/)
115
+ end
101
116
  end
102
117
  end
103
118
  end
@@ -96,7 +96,7 @@ module ToiletTracker
96
96
  def parse_poop_type(type, match, message)
97
97
  case type
98
98
  when :event_now
99
- parse_poop_live(message)
99
+ parse_poop_live(match, message)
100
100
  when :event_now_shifted
101
101
  parse_poop_live_shift(match, message)
102
102
  when :event_at_time
@@ -111,20 +111,23 @@ module ToiletTracker
111
111
  end
112
112
 
113
113
  def parse_toilet_shift(match, message)
114
- shift = parse_shift(match[1])
114
+ metadata = parse_metadata(match[1])
115
+ shift = parse_shift(match[2])
115
116
 
116
117
  Core::ToiletShiftResult.new(
117
118
  type: :shift_set,
118
119
  status: :ok,
119
120
  message: message,
120
121
  shift: shift,
121
- effective_time: message.timestamp
122
+ effective_time: message.timestamp,
123
+ metadata: metadata
122
124
  )
123
125
  end
124
126
 
125
127
  def parse_toilet_shift_with_time(match, message)
126
- shift = parse_shift(match[1])
127
- effective_time = parse_datetime(match[2], @current_message)
128
+ metadata = parse_metadata(match[1])
129
+ shift = parse_shift(match[2])
130
+ effective_time = parse_datetime(match[3], @current_message)
128
131
 
129
132
  status = determine_status(effective_time, message.timestamp)
130
133
 
@@ -133,12 +136,14 @@ module ToiletTracker
133
136
  status: status,
134
137
  message: message,
135
138
  shift: shift,
136
- effective_time: effective_time
139
+ effective_time: effective_time,
140
+ metadata: metadata
137
141
  )
138
142
  end
139
143
 
140
144
  def parse_toilet_timezone(match, message)
141
- timezone_str = match[1]
145
+ metadata = parse_metadata(match[1])
146
+ timezone_str = match[2]
142
147
  shift = calculate_shift_from_timezone(timezone_str)
143
148
 
144
149
  Core::ToiletShiftResult.new(
@@ -146,13 +151,15 @@ module ToiletTracker
146
151
  status: :ok,
147
152
  message: message,
148
153
  shift: shift,
149
- effective_time: message.timestamp
154
+ effective_time: message.timestamp,
155
+ metadata: metadata
150
156
  )
151
157
  end
152
158
 
153
159
  def parse_toilet_timezone_with_time(match, message)
154
- timezone_str = match[1]
155
- datetime_str = match[2]
160
+ metadata = parse_metadata(match[1])
161
+ timezone_str = match[2]
162
+ datetime_str = match[3]
156
163
 
157
164
  effective_time = parse_datetime(datetime_str, @current_message)
158
165
  status = determine_status(effective_time, message.timestamp)
@@ -163,7 +170,8 @@ module ToiletTracker
163
170
  status: status,
164
171
  message: message,
165
172
  shift: shift,
166
- effective_time: effective_time
173
+ effective_time: effective_time,
174
+ metadata: metadata
167
175
  )
168
176
  end
169
177
 
@@ -176,30 +184,36 @@ module ToiletTracker
176
184
  (shift == shift.to_i) ? shift.to_i : shift
177
185
  end
178
186
 
179
- def parse_poop_live(message)
187
+ def parse_poop_live(match, message)
188
+ metadata = parse_metadata(match[1])
189
+
180
190
  Core::PoopEventResult.new(
181
191
  type: :event_now,
182
192
  status: :ok,
183
193
  message: message,
184
194
  shift: 0,
185
- poop_time: message.timestamp
195
+ poop_time: message.timestamp,
196
+ metadata: metadata
186
197
  )
187
198
  end
188
199
 
189
200
  def parse_poop_live_shift(match, message)
190
- shift = parse_shift(match[1])
201
+ metadata = parse_metadata(match[1])
202
+ shift = parse_shift(match[2])
191
203
 
192
204
  Core::PoopEventResult.new(
193
205
  type: :event_now_shifted,
194
206
  status: :ok,
195
207
  message: message,
196
208
  shift: shift,
197
- poop_time: message.timestamp
209
+ poop_time: message.timestamp,
210
+ metadata: metadata
198
211
  )
199
212
  end
200
213
 
201
214
  def parse_poop_past(match, message)
202
- poop_time = parse_datetime(match[1], @current_message)
215
+ metadata = parse_metadata(match[1])
216
+ poop_time = parse_datetime(match[2], @current_message)
203
217
  status = determine_status(poop_time, message.timestamp)
204
218
 
205
219
  Core::PoopEventResult.new(
@@ -207,13 +221,15 @@ module ToiletTracker
207
221
  status: status,
208
222
  message: message,
209
223
  shift: 0,
210
- poop_time: poop_time
224
+ poop_time: poop_time,
225
+ metadata: metadata
211
226
  )
212
227
  end
213
228
 
214
229
  def parse_poop_past_with_shift(match, message)
215
- shift = parse_shift(match[1])
216
- poop_time = parse_datetime(match[2], @current_message)
230
+ metadata = parse_metadata(match[1])
231
+ shift = parse_shift(match[2])
232
+ poop_time = parse_datetime(match[3], @current_message)
217
233
  status = determine_status(poop_time, message.timestamp)
218
234
 
219
235
  Core::PoopEventResult.new(
@@ -221,25 +237,29 @@ module ToiletTracker
221
237
  status: status,
222
238
  message: message,
223
239
  shift: shift,
224
- poop_time: poop_time
240
+ poop_time: poop_time,
241
+ metadata: metadata
225
242
  )
226
243
  end
227
244
 
228
245
  def parse_poop_timezone(match, message)
229
- timezone_str = match[1]
246
+ metadata = parse_metadata(match[1])
247
+ timezone_str = match[2]
230
248
 
231
249
  Core::PoopEventResult.new(
232
250
  type: :event_in_timezone,
233
251
  status: :ok,
234
252
  message: message,
235
253
  poop_time: message.timestamp,
236
- timezone: timezone_str
254
+ timezone: timezone_str,
255
+ metadata: metadata
237
256
  )
238
257
  end
239
258
 
240
259
  def parse_poop_timezone_with_time(match, message)
241
- timezone_str = match[1]
242
- poop_time = parse_datetime(match[2], @current_message)
260
+ metadata = parse_metadata(match[1])
261
+ timezone_str = match[2]
262
+ poop_time = parse_datetime(match[3], @current_message)
243
263
  status = determine_status(poop_time, message.timestamp)
244
264
 
245
265
  Core::PoopEventResult.new(
@@ -247,7 +267,8 @@ module ToiletTracker
247
267
  status: status,
248
268
  message: message,
249
269
  poop_time: poop_time,
250
- timezone: timezone_str
270
+ timezone: timezone_str,
271
+ metadata: metadata
251
272
  )
252
273
  end
253
274
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ToiletTracker
4
- VERSION = "0.1.1"
4
+ VERSION = "0.2.1"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: toilet_tracker
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - NetherSoul
@@ -13,16 +13,22 @@ dependencies:
13
13
  name: activesupport
14
14
  requirement: !ruby/object:Gem::Requirement
15
15
  requirements:
16
- - - "~>"
16
+ - - ">="
17
17
  - !ruby/object:Gem::Version
18
18
  version: 8.0.2.1
19
+ - - "<"
20
+ - !ruby/object:Gem::Version
21
+ version: 8.2.0
19
22
  type: :runtime
20
23
  prerelease: false
21
24
  version_requirements: !ruby/object:Gem::Requirement
22
25
  requirements:
23
- - - "~>"
26
+ - - ">="
24
27
  - !ruby/object:Gem::Version
25
28
  version: 8.0.2.1
29
+ - - "<"
30
+ - !ruby/object:Gem::Version
31
+ version: 8.2.0
26
32
  - !ruby/object:Gem::Dependency
27
33
  name: csv
28
34
  requirement: !ruby/object:Gem::Requirement
@@ -41,16 +47,22 @@ dependencies:
41
47
  name: rubyzip
42
48
  requirement: !ruby/object:Gem::Requirement
43
49
  requirements:
44
- - - "~>"
50
+ - - ">="
45
51
  - !ruby/object:Gem::Version
46
52
  version: 3.0.1
53
+ - - "<"
54
+ - !ruby/object:Gem::Version
55
+ version: 3.3.0
47
56
  type: :runtime
48
57
  prerelease: false
49
58
  version_requirements: !ruby/object:Gem::Requirement
50
59
  requirements:
51
- - - "~>"
60
+ - - ">="
52
61
  - !ruby/object:Gem::Version
53
62
  version: 3.0.1
63
+ - - "<"
64
+ - !ruby/object:Gem::Version
65
+ version: 3.3.0
54
66
  - !ruby/object:Gem::Dependency
55
67
  name: thor
56
68
  requirement: !ruby/object:Gem::Requirement