icalPal 2.1.0 → 2.2.0

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: 4dab143a05d8a63ae36b721ca6ba53180c75d8b4dd0f0cebdc84fb533968c248
4
- data.tar.gz: 8d243a120762c90059e4206a2deb5e79b821e558cf9ad9a437ad84fbfbb6a21b
3
+ metadata.gz: 76513daaf66f3c4aa8b2a9e1703f275ba5d5a65ef9e8b71a86b263c59d409fdd
4
+ data.tar.gz: c06430cfbdd5c415dae2d154b774c1ba1feb210534e6511f9fbb1cf12052cbfd
5
5
  SHA512:
6
- metadata.gz: 24f4b88c592e59f2ffbc116761d6da41b60a7e5d4554feea9bdb023450967eaa7921f5c4708a66bf76554926ab4e0ca52965470f7576cef1cce5aec9a6112cbb
7
- data.tar.gz: d69cc1d2cd63072020a4059a352ccdb2262a189e10c3c4e1891bd1c953ea2cb60a577fc83bd2ab55b561d0a752b0fb4198495bc2a5f57909d4216101cc7ea012
6
+ metadata.gz: 290f75e744ea88673f474c478565ec281a2b8eae8dcfc730ccb7abf88f33b5fdc8c10baf53b971c336d48a3a4abc3f311e02973e9f2295fea2d134e3d987a499
7
+ data.tar.gz: af3d7eec48d408f2c7ff15af7cdddcf581fa60574e1ab3602efb32e6625bfa61833919194108fe012fbae35a312c14af6ffbf628be4921d7cd11651e67717151
data/bin/icalPal CHANGED
@@ -130,6 +130,18 @@ $rows.each_with_index do |row, i|
130
130
  next
131
131
  end
132
132
 
133
+ # --regexp
134
+ if $opts[:match]
135
+ r = $opts[:match].split('=')
136
+
137
+ if item[r[0]].to_s.respond_to?(:match)
138
+ unless item[r[0]].to_s.match(Regexp.new(r[1].to_s, Regexp::IGNORECASE)) then
139
+ $log.debug(":regex")
140
+ next
141
+ end
142
+ end
143
+ end
144
+
133
145
  if ICalPal::Event === item
134
146
  # Check for all-day and cancelled events
135
147
  if $opts[:ea] && item['all_day'].positive? then
data/lib/defaults.rb CHANGED
@@ -26,6 +26,7 @@ $defaults = {
26
26
  output: 'default',
27
27
  ps: [ "\n " ],
28
28
  r: false,
29
+ match: nil,
29
30
  sc: false,
30
31
  sd: false,
31
32
  sep: false,
data/lib/event.rb CHANGED
@@ -110,20 +110,22 @@ module ICalPal
110
110
  # If an event spans multiple days, the return value will contain
111
111
  # a unique {Event} for each day that falls within our window
112
112
  def non_recurring
113
- retval = []
113
+ events = []
114
114
 
115
115
  # Repeat for multi-day events
116
116
  ((self['duration'] / 86400).to_i + 1).times do |i|
117
117
  break if self['sdate'] > $opts[:to]
118
118
 
119
119
  $log.debug("multi-day event #{i + 1}") if (i > 0)
120
+
120
121
  self['daynum'] = i + 1
121
- retval.push(clone) if in_window?(self['sdate'])
122
+ events.push(clone) if in_window?(self['sdate'])
123
+
122
124
  self['sdate'] += 1
123
125
  self['edate'] += 1
124
126
  end
125
127
 
126
- retval
128
+ events
127
129
  end
128
130
 
129
131
  # Check recurring events
@@ -131,35 +133,49 @@ module ICalPal
131
133
  # @return [Array<Event>]
132
134
  # All occurrences of a recurring event that are within our window
133
135
  def recurring
134
- retval = []
136
+ stop = [ $opts[:to], (self['rdate'] || $opts[:to]) ].min
135
137
 
136
138
  # See if event ends before we start
137
- stop = [ $opts[:to], (self['rdate'] || $opts[:to]) ].min
138
139
  if stop < $opts[:from] then
139
140
  $log.debug("#{stop} < #{$opts[:from]}")
140
- return(retval)
141
+ return(Array.new)
141
142
  end
142
143
 
143
144
  # Get changes to series
144
- changes = $rows.select { |r| r['orig_item_id'] == self['ROWID'] }
145
+ changes = [ { 'orig_date' => -1 } ]
146
+ changes += $rows.select { |r| r['orig_item_id'] == self['ROWID'] }
147
+
148
+ events = []
149
+ count = 1
145
150
 
146
- i = 1
147
151
  while self['sdate'] <= stop
148
- if self['count'].positive? && i > self['count'] then
149
- $log.debug("count exceeded: #{i} > #{self['count']}")
150
- return(retval)
152
+ # count
153
+ break if self['count'].positive? and count > self['count']
154
+ count += 1
155
+
156
+ # Handle specifier or clone self
157
+ if self['specifier'] and self['specifier'].length.positive?
158
+ occurrences = get_occurrences(changes)
159
+ else
160
+ occurrences = [ clone ]
151
161
  end
152
- i += 1
153
162
 
154
- unless @self['xdate'].any?(@self['sdate']) # Exceptions?
155
- o = get_occurrences(changes)
156
- o.each { |r| retval.push(r) if in_window?(r['sdate'], r['edate']) }
163
+ # Check for changes
164
+ occurrences.each do |occurrence|
165
+ changes.each do |change|
166
+ next if change['orig_date'] == self['sdate'].to_i - ITIME
167
+ events.push(occurrence) if in_window?(occurrence['sdate'], occurrence['edate'])
168
+ end
157
169
  end
158
170
 
171
+ break if self['specifier']
159
172
  apply_frequency!
160
173
  end
161
174
 
162
- retval
175
+ # Remove exceptions
176
+ events.delete_if { |event| event['xdate'].any?(event['sdate']) }
177
+
178
+ return(events)
163
179
  end
164
180
 
165
181
  private
@@ -168,67 +184,72 @@ module ICalPal
168
184
 
169
185
  # @return a deep clone of self
170
186
  def clone()
171
- self['stime'] = @self['sdate'].to_i
172
- self['etime'] = @self['edate'].to_i
173
-
174
187
  Marshal.load(Marshal.dump(self))
175
188
  end
176
189
 
177
- # Get next occurences of a recurring event
190
+ # Get next occurences of a recurring event from a specifier
178
191
  #
179
192
  # @param changes [Array] Recurrence changes for the event
180
193
  # @return [Array<IcalPal::Event>]
181
194
  def get_occurrences(changes)
182
- ndate = self['sdate']
183
- odays = []
184
- retval = []
185
-
186
- # Deconstruct specifier(s)
187
- if self['specifier']
188
- self['specifier'].split(';').each do |k|
189
- j = k.split('=')
190
-
191
- # M=Day of the month, O=Month of the year, S=Nth
192
- case j[0]
193
- when 'M' then ndate = RDT.new(ndate.year, ndate.month, j[1].to_i)
194
- when 'O' then ndate = RDT.new(ndate.year, j[1].to_i, ndate.day)
195
- when 'S' then @self['specifier'].sub!(/D=0/, "D=+#{j[1].to_i}")
196
- end
197
-
198
- # No time travel!
199
- ndate = self['sdate'] if ndate <= self['sdate']
195
+ occurrences = []
196
+
197
+ dow = DOW.keys
198
+ dom = [ nil ]
199
+ moy = 1..12
200
+ nth = nil
201
+
202
+ specifier = self['specifier']? self['specifier'] : []
203
+
204
+ # Deconstruct specifier
205
+ specifier.split(';').each do |k|
206
+ j = k.split('=')
207
+
208
+ # D=Day of the week, M=Day of the month, O=Month of the year, S=Nth
209
+ case j[0]
210
+ when 'D' then dow = j[1].split(',')
211
+ when 'M' then dom = j[1].split(',')
212
+ when 'O' then moy = j[1].split(',')
213
+ when 'S' then nth = j[1].to_i
214
+ else $log.warn("Unknown specifier: #{k}")
200
215
  end
216
+ end
201
217
 
202
- # D=Day of the week
203
- self['specifier'].split(';').each do |k|
204
- j = k.split('=')
218
+ # Build array of DOWs
219
+ dows = [ nil ]
220
+ dow.each { |d| dows.push(DOW[d[-2..-1].to_sym]) }
205
221
 
206
- odays = j[1].split(',') if j[0] == 'D'
207
- end
208
- end
222
+ # Months of the year (O)
223
+ moy.each do |m|
224
+ next unless m
209
225
 
210
- # Deconstruct occurence day(s)
211
- odays.each do |n|
212
- dow = DOW[n[-2..-1].to_sym]
213
- ndate += 1 until ndate.wday == dow
214
- ndate = ICalPal.nth(Integer(n[0..1]), n[-2..-1], ndate) unless (n[0] == '0')
226
+ nsdate = RDT.new(self['sdate'].year, m.to_i, 1)
227
+ nedate = RDT.new(self['edate'].year, m.to_i, 1)
215
228
 
216
- # Check for changes
217
- changes.detect(
218
- proc {
219
- self['sdate'] = RDT.new(*ndate.to_a[0..2], *self['sdate'].to_a[3..])
220
- self['edate'] = RDT.new(*ndate.to_a[0..2], *self['edate'].to_a[3..])
221
- retval.push(clone)
222
- }) { |i| @self['sday'] == i['sday'] }
223
- end
229
+ # Days of the month (M)
230
+ dom.each do |x|
231
+ next unless x
224
232
 
225
- # Check for changes
226
- changes.detect(
227
- proc {
228
- retval.push(clone)
229
- }) { |i| @self['sday'] == i['sday'] } unless retval.count.positive?
233
+ self['sdate'] = RDT.new(nsdate.year, nsdate.month, x.to_i)
234
+ self['edate'] = RDT.new(nedate.year, nedate.month, x.to_i)
235
+ occurrences.push(clone)
236
+ end
237
+
238
+ # Days of the week (D)
239
+ if nth
240
+ self['sdate'] = ICalPal.nth(nth, dows, nsdate)
241
+ self['edate'] = ICalPal.nth(nth, dows, nedate)
242
+ occurrences.push(clone)
243
+ else
244
+ if dows[0]
245
+ self['sdate'] = RDT.new(nsdate.year, m.to_i, nsdate.wday)
246
+ self['edate'] = RDT.new(nedate.year, m.to_i, nedate.wday)
247
+ occurrences.push(clone)
248
+ end
249
+ end
250
+ end
230
251
 
231
- retval
252
+ return(occurrences)
232
253
  end
233
254
 
234
255
  # Apply frequency and interval
@@ -243,6 +264,7 @@ module ICalPal
243
264
  when 'weekly' then self[d] += self['interval'] * 7
244
265
  when 'monthly' then self[d] >>= self['interval']
245
266
  when 'yearly' then self[d] >>= self['interval'] * 12
267
+ else $log.error("Unknown frequency: #{self['frequency']}")
246
268
  end
247
269
  end if self['frequency'] && self['interval']
248
270
  end
data/lib/icalPal.rb CHANGED
@@ -141,7 +141,7 @@ module ICalPal
141
141
  # Get the +n+'th +dow+ in month +m+
142
142
  #
143
143
  # @param n [Integer] Integer between -4 and +4
144
- # @param dow [String] Day of the week abbreviation from ICalPal::DOW
144
+ # @param dow [Array] Days of the week
145
145
  # @param m [RDT] The RDT with the year and month we're searching
146
146
  # @return [RDT] The resulting day
147
147
  def self.nth(n, dow, m)
@@ -155,7 +155,7 @@ module ICalPal
155
155
 
156
156
  j = 0
157
157
  a[0].step(a[1], step) do |i|
158
- j += step if i.wday == DOW[dow.to_sym]
158
+ j += step if dow.any?(i.wday)
159
159
  return i if j == n
160
160
  end
161
161
  end
data/lib/options.rb CHANGED
@@ -55,7 +55,7 @@ module ICalPal
55
55
  "Print as FORMAT (default: #{$defaults[:common][:output]})", "[#{OUTFORMATS.join(', ')}]")
56
56
 
57
57
  # include/exclude
58
- @op.separator("\nIncluding/excluding calendars:\n\n")
58
+ @op.separator("\nIncluding/excluding accounts, calendars, items:\n\n")
59
59
 
60
60
  @op.on('--is=ACCOUNTS', Array, 'List of accounts to include')
61
61
  @op.on('--es=ACCOUNTS', Array, 'List of accounts to exclude')
@@ -73,6 +73,9 @@ module ICalPal
73
73
  @op.on('--il=LISTS', Array, 'List of reminder lists to include')
74
74
  @op.on('--el=LISTS', Array, 'List of reminder lists to exclude')
75
75
 
76
+ @op.separator('')
77
+ @op.on('--match=FIELD=REGEXP', String, 'Include only items whose FIELD matches REGEXP (ignoring case)')
78
+
76
79
  # dates
77
80
  @op.separator("\nChoosing dates:\n\n")
78
81
 
data/lib/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module ICalPal
2
- VERSION = '2.1.0'
2
+ VERSION = '2.2.0'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: icalPal
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Rosen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-04-28 00:00:00.000000000 Z
11
+ date: 2024-05-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sqlite3