mhc 1.0.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 +7 -0
- data/.gitignore +27 -0
- data/.rspec +2 -0
- data/.travis.yml +3 -0
- data/COPYRIGHT +28 -0
- data/Gemfile +8 -0
- data/README.org +209 -0
- data/Rakefile +13 -0
- data/bin/mhc +312 -0
- data/emacs/Cask +25 -0
- data/emacs/Makefile +58 -0
- data/emacs/mhc-calendar.el +1723 -0
- data/emacs/mhc-calfw.el +135 -0
- data/emacs/mhc-compat.el +90 -0
- data/emacs/mhc-date.el +642 -0
- data/emacs/mhc-day.el +149 -0
- data/emacs/mhc-db.el +158 -0
- data/emacs/mhc-draft.el +211 -0
- data/emacs/mhc-e21.el +167 -0
- data/emacs/mhc-face.el +236 -0
- data/emacs/mhc-file.el +224 -0
- data/emacs/mhc-guess.el +648 -0
- data/emacs/mhc-header.el +176 -0
- data/emacs/mhc-logic.el +563 -0
- data/emacs/mhc-message.el +130 -0
- data/emacs/mhc-minibuf.el +466 -0
- data/emacs/mhc-misc.el +248 -0
- data/emacs/mhc-mua.el +260 -0
- data/emacs/mhc-parse.el +286 -0
- data/emacs/mhc-process.el +35 -0
- data/emacs/mhc-ps.el +1174 -0
- data/emacs/mhc-record.el +201 -0
- data/emacs/mhc-schedule.el +202 -0
- data/emacs/mhc-summary.el +763 -0
- data/emacs/mhc-sync.el +158 -0
- data/emacs/mhc-vars.el +149 -0
- data/emacs/mhc.el +1114 -0
- data/icons/Anniversary.xbm +6 -0
- data/icons/Anniversary.xpm +27 -0
- data/icons/Birthday.xbm +6 -0
- data/icons/Birthday.xpm +25 -0
- data/icons/Business.xbm +6 -0
- data/icons/Business.xpm +24 -0
- data/icons/CheckBox.xbm +6 -0
- data/icons/CheckBox.xpm +24 -0
- data/icons/CheckedBox.xbm +6 -0
- data/icons/CheckedBox.xpm +25 -0
- data/icons/Conflict.xbm +6 -0
- data/icons/Conflict.xpm +22 -0
- data/icons/Date.xbm +6 -0
- data/icons/Date.xpm +29 -0
- data/icons/Holiday.xbm +6 -0
- data/icons/Holiday.xpm +25 -0
- data/icons/Link.xbm +6 -0
- data/icons/Link.xpm +25 -0
- data/icons/Other.xbm +6 -0
- data/icons/Other.xpm +28 -0
- data/icons/Party.xbm +6 -0
- data/icons/Party.xpm +23 -0
- data/icons/Private.xbm +6 -0
- data/icons/Private.xpm +26 -0
- data/icons/Recurrence.xbm +6 -0
- data/icons/Recurrence.xpm +98 -0
- data/icons/Vacation.xbm +6 -0
- data/icons/Vacation.xpm +26 -0
- data/lib/mhc.rb +45 -0
- data/lib/mhc/builder.rb +64 -0
- data/lib/mhc/caldav.rb +304 -0
- data/lib/mhc/calendar.rb +106 -0
- data/lib/mhc/command.rb +13 -0
- data/lib/mhc/command/cache.rb +14 -0
- data/lib/mhc/command/completions.rb +108 -0
- data/lib/mhc/command/init.rb +133 -0
- data/lib/mhc/command/scan.rb +33 -0
- data/lib/mhc/command/sync.rb +22 -0
- data/lib/mhc/config.rb +229 -0
- data/lib/mhc/converter.rb +330 -0
- data/lib/mhc/datastore.rb +164 -0
- data/lib/mhc/date_enumerator.rb +274 -0
- data/lib/mhc/date_frame.rb +124 -0
- data/lib/mhc/date_helper.rb +49 -0
- data/lib/mhc/etag.rb +68 -0
- data/lib/mhc/event.rb +396 -0
- data/lib/mhc/formatter.rb +312 -0
- data/lib/mhc/logger.rb +94 -0
- data/lib/mhc/modifier.rb +149 -0
- data/lib/mhc/occurrence.rb +94 -0
- data/lib/mhc/occurrence_enumerator.rb +113 -0
- data/lib/mhc/property_value.rb +33 -0
- data/lib/mhc/property_value/date.rb +190 -0
- data/lib/mhc/property_value/integer.rb +15 -0
- data/lib/mhc/property_value/list.rb +41 -0
- data/lib/mhc/property_value/period.rb +49 -0
- data/lib/mhc/property_value/range.rb +100 -0
- data/lib/mhc/property_value/recurrence_condition.rb +272 -0
- data/lib/mhc/property_value/text.rb +11 -0
- data/lib/mhc/property_value/time.rb +45 -0
- data/lib/mhc/query.rb +210 -0
- data/lib/mhc/sync.rb +46 -0
- data/lib/mhc/sync/driver.rb +108 -0
- data/lib/mhc/sync/status.rb +70 -0
- data/lib/mhc/sync/status_manager.rb +142 -0
- data/lib/mhc/sync/strategy.rb +233 -0
- data/lib/mhc/sync/syncinfo.rb +98 -0
- data/lib/mhc/templates/config.yml.erb +142 -0
- data/lib/mhc/version.rb +4 -0
- data/lib/mhc/webdav.rb +319 -0
- data/mhc.gemspec +24 -0
- data/samples/DOT.mhc-config.yml +116 -0
- data/samples/japanese-holidays.mhcc +153 -0
- data/samples/mhc-completions.zsh +11 -0
- data/spec/mhc_spec.rb +682 -0
- data/spec/spec_helper.rb +9 -0
- data/xpm/close.xpm +18 -0
- data/xpm/delete.xpm +19 -0
- data/xpm/exit.xpm +18 -0
- data/xpm/month.xpm +18 -0
- data/xpm/next.xpm +18 -0
- data/xpm/next2.xpm +18 -0
- data/xpm/next_year.xpm +18 -0
- data/xpm/open.xpm +19 -0
- data/xpm/prev.xpm +18 -0
- data/xpm/prev2.xpm +18 -0
- data/xpm/prev_year.xpm +18 -0
- data/xpm/save.xpm +19 -0
- data/xpm/today.xpm +18 -0
- metadata +214 -0
@@ -0,0 +1,274 @@
|
|
1
|
+
module Mhc
|
2
|
+
class EnumMerger
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
alias_method :with_index, :each_with_index
|
6
|
+
alias_method :with_object, :each_with_object
|
7
|
+
|
8
|
+
def initialize(&block)
|
9
|
+
@enumerators = []
|
10
|
+
@enumerators << Enumerator.new(&block) if block
|
11
|
+
end
|
12
|
+
|
13
|
+
def <<(o)
|
14
|
+
@enumerators << o
|
15
|
+
return self
|
16
|
+
end
|
17
|
+
|
18
|
+
def each
|
19
|
+
rewind
|
20
|
+
loop do
|
21
|
+
yield self.next
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# def feed ; end
|
26
|
+
|
27
|
+
def next
|
28
|
+
raise StopIteration if @enumerators.empty?
|
29
|
+
minimum_enumrator.next
|
30
|
+
end
|
31
|
+
|
32
|
+
# def next_values ; end
|
33
|
+
|
34
|
+
def peek
|
35
|
+
raise StopIteration if @enumerators.empty?
|
36
|
+
minimum_enumrator.peek
|
37
|
+
end
|
38
|
+
|
39
|
+
# def peek_values ; end
|
40
|
+
|
41
|
+
def rewind
|
42
|
+
send_all(:rewind)
|
43
|
+
end
|
44
|
+
|
45
|
+
def send_all(method, *args)
|
46
|
+
@enumerators.map{|e| e.send(method, *args)}
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def minimum_enumrator
|
52
|
+
min_e, min_v = @enumerators.first, nil
|
53
|
+
@enumerators.each do |e|
|
54
|
+
v = e.peek rescue nil
|
55
|
+
if v and (min_v.nil? or v < min_v)
|
56
|
+
min_e, min_v = e, v
|
57
|
+
end
|
58
|
+
end
|
59
|
+
return min_e
|
60
|
+
end
|
61
|
+
end # class EnumMerger
|
62
|
+
|
63
|
+
class DateEnumerator < EnumMerger
|
64
|
+
def initialize(start_date:, end_date:, interval: 1, &block)
|
65
|
+
@start_date, @end_date, @interval = start_date, end_date, interval
|
66
|
+
super(&block)
|
67
|
+
end
|
68
|
+
|
69
|
+
def add_yearly_by_day(start_date: @start_date, end_date: @end_date, interval: @interval, month:, nth:, wday:)
|
70
|
+
self << YearlyByDay.new(start_date: start_date, end_date: end_date, interval: interval, month: month, nth: nth, wday: wday).to_enum
|
71
|
+
end
|
72
|
+
|
73
|
+
def add_yearly_by_monthday(start_date: @start_date, end_date: @end_date, interval: @interval, month:, mday:)
|
74
|
+
self << YearlyByMonthday.new(start_date: start_date, end_date: end_date, interval: interval, month: month, mday: mday).to_enum
|
75
|
+
end
|
76
|
+
|
77
|
+
def add_monthly_by_day(start_date: @start_date, end_date: @end_date, interval: @interval, nth:, wday:)
|
78
|
+
self << MonthlyByDay.new(start_date: start_date, end_date: end_date, interval: interval, nth: nth, wday: wday).to_enum
|
79
|
+
end
|
80
|
+
|
81
|
+
def add_monthly_by_monthday(start_date: @start_date, end_date: @end_date, interval: @interval, mday:)
|
82
|
+
self << MonthlyByMonthday.new(start_date: start_date, end_date: end_date, interval: interval, mday: mday).to_enum
|
83
|
+
end
|
84
|
+
|
85
|
+
def add_weekly(start_date: @start_date, end_date: @end_date, interval: @interval, wday:)
|
86
|
+
self << Weekly.new(start_date: start_date, end_date: end_date, interval: interval, wday: wday).to_enum
|
87
|
+
end
|
88
|
+
|
89
|
+
def add_by_range_list(start_date: @start_date, end_date: @end_date, range_list:)
|
90
|
+
self << ByRangeList.new(start_date: start_date, end_date: end_date, range_list: range_list).to_enum
|
91
|
+
end
|
92
|
+
|
93
|
+
################################################################
|
94
|
+
class Base
|
95
|
+
include DateHelper
|
96
|
+
|
97
|
+
def initialize(start_date:, end_date:, interval: 1, wkst: 1)
|
98
|
+
@start_date, @end_date, @interval, @wkst = start_date, end_date, interval, wkst
|
99
|
+
@frame_manager = frame_manager.new(start_date, interval, wkst)
|
100
|
+
end
|
101
|
+
|
102
|
+
def each
|
103
|
+
head, tail = range
|
104
|
+
@frame_manager.forward_to(head).each do |frame|
|
105
|
+
break if frame > tail
|
106
|
+
date = occurrence_in_frame(frame)
|
107
|
+
next unless date
|
108
|
+
break if date > tail
|
109
|
+
next if date < head
|
110
|
+
yield date
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
def range
|
117
|
+
s = (@range_from and @start_date < @range_from) ? @range_from : @start_date
|
118
|
+
e = (@range_to and @end_date > @range_to) ? @range_to : @end_date
|
119
|
+
return [s, e]
|
120
|
+
end
|
121
|
+
|
122
|
+
def frame_manager
|
123
|
+
raise "should be defined in subclasses"
|
124
|
+
end
|
125
|
+
|
126
|
+
def occurrence_in_frame(date)
|
127
|
+
raise "should be defined in subclasses"
|
128
|
+
end
|
129
|
+
end # class Base
|
130
|
+
|
131
|
+
################################################################
|
132
|
+
# Enumerate yealy dates by day like: Apr 4th Tue
|
133
|
+
class YearlyByDay < Base
|
134
|
+
def initialize(start_date:, end_date:, interval: 1, month:, nth:, wday:)
|
135
|
+
super(start_date: start_date, end_date: end_date, interval: interval)
|
136
|
+
@month, @nth, @wday = month, nth, wday
|
137
|
+
end
|
138
|
+
|
139
|
+
private
|
140
|
+
|
141
|
+
def frame_manager
|
142
|
+
DateFrame::Yearly
|
143
|
+
end
|
144
|
+
|
145
|
+
def occurrence_in_frame(date)
|
146
|
+
make_date_by_day(year: date.year, month: @month, nth: @nth, wday: @wday) rescue nil
|
147
|
+
end
|
148
|
+
end # class YearlyByDay
|
149
|
+
|
150
|
+
################################################################
|
151
|
+
# Enumerate yealy dates by month-day like: Apr 22
|
152
|
+
class YearlyByMonthday < Base
|
153
|
+
def initialize(start_date:, end_date:, interval: 1, month:, mday:)
|
154
|
+
super(start_date: start_date, end_date: end_date, interval: interval)
|
155
|
+
@month, @mday = month, mday
|
156
|
+
end
|
157
|
+
|
158
|
+
private
|
159
|
+
|
160
|
+
def frame_manager
|
161
|
+
DateFrame::Yearly
|
162
|
+
end
|
163
|
+
|
164
|
+
def occurrence_in_frame(date)
|
165
|
+
Mhc::PropertyValue::Date.new(date.year, @month, @mday) rescue nil
|
166
|
+
end
|
167
|
+
end # class YearlyByMonthday
|
168
|
+
|
169
|
+
################################################################
|
170
|
+
# Enumerate monthly dates by day like: 4th Tue
|
171
|
+
class MonthlyByDay < Base
|
172
|
+
def initialize(start_date:, end_date:, interval: 1, nth:, wday:)
|
173
|
+
super(start_date: start_date, end_date: end_date, interval: interval)
|
174
|
+
@nth, @wday = nth, wday
|
175
|
+
end
|
176
|
+
|
177
|
+
private
|
178
|
+
|
179
|
+
def frame_manager
|
180
|
+
DateFrame::Monthly
|
181
|
+
end
|
182
|
+
|
183
|
+
def occurrence_in_frame(date)
|
184
|
+
make_date_by_day(year: date.year, month: date.month, nth: @nth, wday: @wday) rescue nil
|
185
|
+
end
|
186
|
+
end # class MonthlyByDay
|
187
|
+
|
188
|
+
################################################################
|
189
|
+
# Enumerate monthly dates by month-day like: 22
|
190
|
+
class MonthlyByMonthday < Base
|
191
|
+
def initialize(start_date:, end_date:, interval: 1, mday:)
|
192
|
+
super(start_date: start_date, end_date: end_date, interval: interval)
|
193
|
+
@mday = mday
|
194
|
+
end
|
195
|
+
|
196
|
+
private
|
197
|
+
|
198
|
+
def frame_manager
|
199
|
+
DateFrame::Monthly
|
200
|
+
end
|
201
|
+
|
202
|
+
def occurrence_in_frame(date)
|
203
|
+
Mhc::PropertyValue::Date.new(date.year, date.month, @mday) rescue nil
|
204
|
+
end
|
205
|
+
end # class MonthlyMonthday
|
206
|
+
|
207
|
+
################################################################
|
208
|
+
# Enumerate weekly dates like: Tue
|
209
|
+
class Weekly < Base
|
210
|
+
def initialize(start_date:, end_date:, interval: 1, wkst: 1, wday:)
|
211
|
+
super(start_date: start_date, end_date: end_date, interval: interval)
|
212
|
+
@wday = wday
|
213
|
+
end
|
214
|
+
|
215
|
+
private
|
216
|
+
|
217
|
+
def frame_manager
|
218
|
+
DateFrame::Weekly
|
219
|
+
end
|
220
|
+
|
221
|
+
# Sun Mon Tue Wed Thu Fri Sat Sun Mon Tue ...
|
222
|
+
# 0 1 2 3 4 5 6 0 1 2 ...
|
223
|
+
def occurrence_in_frame(date)
|
224
|
+
bof = date - ((date.wday - @wkst) % 7)
|
225
|
+
candidate = bof + (@wday - bof.wday) % 7
|
226
|
+
return candidate if date <= candidate
|
227
|
+
return nil
|
228
|
+
end
|
229
|
+
end # class Weekly
|
230
|
+
|
231
|
+
################################################################
|
232
|
+
# Enumerate every n days
|
233
|
+
class Daily < Base
|
234
|
+
def initialize(start_date:, end_date:, interval:1)
|
235
|
+
super(start_date: start_date, end_date: end_date, interval: interval)
|
236
|
+
end
|
237
|
+
|
238
|
+
private
|
239
|
+
|
240
|
+
def frame_manager
|
241
|
+
DateFrame::Daily
|
242
|
+
end
|
243
|
+
|
244
|
+
def occurrence_in_frame(date)
|
245
|
+
return date
|
246
|
+
end
|
247
|
+
end # class Daily
|
248
|
+
|
249
|
+
################################################################
|
250
|
+
# Enumerate dates from list.
|
251
|
+
class ByRangeList < Base
|
252
|
+
def initialize(start_date:, end_date:, range_list:)
|
253
|
+
super(start_date: start_date, end_date: end_date)
|
254
|
+
@range_list = range_list
|
255
|
+
end
|
256
|
+
|
257
|
+
def each
|
258
|
+
head, tail = range
|
259
|
+
@range_list.each do |date_range|
|
260
|
+
break if date_range.first > tail
|
261
|
+
next if date_range.last < head
|
262
|
+
yield date_range
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
private
|
267
|
+
|
268
|
+
def frame_manager
|
269
|
+
DateFrame::Dummy
|
270
|
+
end
|
271
|
+
end # class ByRangeList
|
272
|
+
|
273
|
+
end # class DateEnumerator
|
274
|
+
end # module Mhc
|
@@ -0,0 +1,124 @@
|
|
1
|
+
module Mhc
|
2
|
+
module DateFrame
|
3
|
+
class Dummy
|
4
|
+
def initialize(start_date, interval = 1, wkst = 1)
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
class Base
|
9
|
+
include DateHelper
|
10
|
+
|
11
|
+
def initialize(start_date, interval = 1, wkst = 1)
|
12
|
+
@start_date, @interval, @wkst = start_date, interval, wkst
|
13
|
+
rewind
|
14
|
+
end
|
15
|
+
|
16
|
+
def each
|
17
|
+
loop do
|
18
|
+
date = self.next
|
19
|
+
yield date
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def next(cycles = 1)
|
24
|
+
frame = @frame_start
|
25
|
+
@frame_start = next_frame_start(cycles)
|
26
|
+
return frame
|
27
|
+
end
|
28
|
+
|
29
|
+
def peek
|
30
|
+
@frame_start
|
31
|
+
end
|
32
|
+
|
33
|
+
def rewind
|
34
|
+
@frame_start = beginning_of_frame(@start_date)
|
35
|
+
return self
|
36
|
+
end
|
37
|
+
|
38
|
+
# go forward to the frame in which DATE is involved
|
39
|
+
def forward_to(date)
|
40
|
+
rewind
|
41
|
+
frames = frames_between(@frame_start, date)
|
42
|
+
cycles = (frames + (@interval - 1)) / @interval
|
43
|
+
self.next(cycles) if cycles > 0
|
44
|
+
return self
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
def next_frame_start(cycles = 1)
|
49
|
+
raise "should be defined in subclasses"
|
50
|
+
end
|
51
|
+
|
52
|
+
def beginning_of_frame(date)
|
53
|
+
raise "should be defined in subclasses"
|
54
|
+
end
|
55
|
+
|
56
|
+
def frames_between(date1, date2)
|
57
|
+
raise "should be defined in subclasses"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class Yearly < Base
|
62
|
+
private
|
63
|
+
|
64
|
+
def next_frame_start(cycles = 1)
|
65
|
+
@frame_start >> (@interval * 12 * cycles)
|
66
|
+
end
|
67
|
+
|
68
|
+
def beginning_of_frame(date)
|
69
|
+
beginning_of_year(date)
|
70
|
+
end
|
71
|
+
|
72
|
+
def frames_between(date1, date2)
|
73
|
+
years_between(date1, date2)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
class Monthly < Base
|
78
|
+
private
|
79
|
+
|
80
|
+
def next_frame_start(cycles = 1)
|
81
|
+
@frame_start >> (@interval * cycles)
|
82
|
+
end
|
83
|
+
|
84
|
+
def beginning_of_frame(date)
|
85
|
+
beginning_of_month(date)
|
86
|
+
end
|
87
|
+
|
88
|
+
def frames_between(date1, date2)
|
89
|
+
months_between(date1, date2)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
class Weekly < Base
|
94
|
+
private
|
95
|
+
def next_frame_start(cycles = 1)
|
96
|
+
@frame_start + (@interval * 7 * cycles)
|
97
|
+
end
|
98
|
+
|
99
|
+
def beginning_of_frame(date)
|
100
|
+
beginning_of_week(date, @wkst)
|
101
|
+
end
|
102
|
+
|
103
|
+
def frames_between(date1, date2)
|
104
|
+
(beginning_of_frame(date2) - beginning_of_frame(date1)) / 7
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
class Daily < Base
|
109
|
+
private
|
110
|
+
|
111
|
+
def next_frame_start(cycles = 1)
|
112
|
+
@frame_start + (@interval * cycles)
|
113
|
+
end
|
114
|
+
|
115
|
+
def beginning_of_frame(date)
|
116
|
+
date
|
117
|
+
end
|
118
|
+
|
119
|
+
def frames_between(date1, date2)
|
120
|
+
date2 - date1
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end # module DateFrame
|
124
|
+
end # module Mhc
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Mhc
|
2
|
+
module DateHelper
|
3
|
+
# Make a date by DAY like ``1st Wed of Nov, 1999''.
|
4
|
+
# caller must make sure:
|
5
|
+
# YEAR and MONTH must be valid.
|
6
|
+
# NTH must be <0 or >0.
|
7
|
+
# WDAY must be 0..6.
|
8
|
+
#
|
9
|
+
# returns nil if no date matches (for example, no 5th Saturday
|
10
|
+
# exists on April 2010).
|
11
|
+
#
|
12
|
+
private
|
13
|
+
def make_date_by_day(year:, month:, nth:, wday:)
|
14
|
+
direction = nth > 0 ? 1 : -1
|
15
|
+
|
16
|
+
edge = Mhc::PropertyValue::Date.new(year, month, direction)
|
17
|
+
ydiff = nth - direction
|
18
|
+
xdiff = direction * ((direction * (wday - edge.wday)) % 7)
|
19
|
+
mday = edge.mday + ydiff * 7 + xdiff
|
20
|
+
|
21
|
+
raise ArgumentError if mday < 1
|
22
|
+
return Mhc::PropertyValue::Date.new(year, month, mday)
|
23
|
+
end
|
24
|
+
|
25
|
+
def beginning_of_year(date)
|
26
|
+
date.class.new(date.year, 1, 1)
|
27
|
+
end
|
28
|
+
|
29
|
+
def beginning_of_month(date)
|
30
|
+
date.class.new(date.year, date.month, 1)
|
31
|
+
end
|
32
|
+
|
33
|
+
def beginning_of_week(date, wkst = 1)
|
34
|
+
date - ((date.wday - wkst) % 7)
|
35
|
+
end
|
36
|
+
|
37
|
+
def years_between(date1, date2)
|
38
|
+
date2.year - date1.year
|
39
|
+
end
|
40
|
+
|
41
|
+
def months_between(date1, date2)
|
42
|
+
(date2.year * 12 + date2.month) - (date1.year * 12 + date1.month)
|
43
|
+
end
|
44
|
+
|
45
|
+
def weeks_between(date1, date2)
|
46
|
+
(date2 - date1 + 6) / 7
|
47
|
+
end
|
48
|
+
end # module DateHelper
|
49
|
+
end # module Mhc
|
data/lib/mhc/etag.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
module Mhc
|
2
|
+
class EtagStore
|
3
|
+
def initialize(top_directory)
|
4
|
+
@top_directory = top_directory
|
5
|
+
end
|
6
|
+
|
7
|
+
def put(key, value)
|
8
|
+
if value.nil?
|
9
|
+
unlink(key)
|
10
|
+
else
|
11
|
+
store(key, value)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def get(uid)
|
16
|
+
if value = load(uid)
|
17
|
+
return value
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def uid_list
|
22
|
+
keys
|
23
|
+
end
|
24
|
+
|
25
|
+
def report_etags(uids = nil)
|
26
|
+
hash = {}
|
27
|
+
uid_list.each do |uid|
|
28
|
+
hash[uid] = get(uid)
|
29
|
+
end
|
30
|
+
return hash
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
def keys
|
35
|
+
Dir.glob(File.join(@top_directory, '*.etag')).map {|p| make_key(p)}
|
36
|
+
end
|
37
|
+
|
38
|
+
def store(key, value)
|
39
|
+
File.open(make_path(key), "w") do |f|
|
40
|
+
f.write(value)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def load(key)
|
45
|
+
begin
|
46
|
+
File.open(make_path(key), "r") do |f|
|
47
|
+
return f.read
|
48
|
+
end
|
49
|
+
rescue
|
50
|
+
return nil
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def unlink(key)
|
55
|
+
if File.exists?(path = make_path(key))
|
56
|
+
File.unlink(path)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def make_path(key)
|
61
|
+
File.join(@top_directory, key.to_s + '.etag')
|
62
|
+
end
|
63
|
+
|
64
|
+
def make_key(path)
|
65
|
+
File.basename(path, '.etag')
|
66
|
+
end
|
67
|
+
end # class EtagStore
|
68
|
+
end # module Mhc
|