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.
Files changed (127) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +27 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +3 -0
  5. data/COPYRIGHT +28 -0
  6. data/Gemfile +8 -0
  7. data/README.org +209 -0
  8. data/Rakefile +13 -0
  9. data/bin/mhc +312 -0
  10. data/emacs/Cask +25 -0
  11. data/emacs/Makefile +58 -0
  12. data/emacs/mhc-calendar.el +1723 -0
  13. data/emacs/mhc-calfw.el +135 -0
  14. data/emacs/mhc-compat.el +90 -0
  15. data/emacs/mhc-date.el +642 -0
  16. data/emacs/mhc-day.el +149 -0
  17. data/emacs/mhc-db.el +158 -0
  18. data/emacs/mhc-draft.el +211 -0
  19. data/emacs/mhc-e21.el +167 -0
  20. data/emacs/mhc-face.el +236 -0
  21. data/emacs/mhc-file.el +224 -0
  22. data/emacs/mhc-guess.el +648 -0
  23. data/emacs/mhc-header.el +176 -0
  24. data/emacs/mhc-logic.el +563 -0
  25. data/emacs/mhc-message.el +130 -0
  26. data/emacs/mhc-minibuf.el +466 -0
  27. data/emacs/mhc-misc.el +248 -0
  28. data/emacs/mhc-mua.el +260 -0
  29. data/emacs/mhc-parse.el +286 -0
  30. data/emacs/mhc-process.el +35 -0
  31. data/emacs/mhc-ps.el +1174 -0
  32. data/emacs/mhc-record.el +201 -0
  33. data/emacs/mhc-schedule.el +202 -0
  34. data/emacs/mhc-summary.el +763 -0
  35. data/emacs/mhc-sync.el +158 -0
  36. data/emacs/mhc-vars.el +149 -0
  37. data/emacs/mhc.el +1114 -0
  38. data/icons/Anniversary.xbm +6 -0
  39. data/icons/Anniversary.xpm +27 -0
  40. data/icons/Birthday.xbm +6 -0
  41. data/icons/Birthday.xpm +25 -0
  42. data/icons/Business.xbm +6 -0
  43. data/icons/Business.xpm +24 -0
  44. data/icons/CheckBox.xbm +6 -0
  45. data/icons/CheckBox.xpm +24 -0
  46. data/icons/CheckedBox.xbm +6 -0
  47. data/icons/CheckedBox.xpm +25 -0
  48. data/icons/Conflict.xbm +6 -0
  49. data/icons/Conflict.xpm +22 -0
  50. data/icons/Date.xbm +6 -0
  51. data/icons/Date.xpm +29 -0
  52. data/icons/Holiday.xbm +6 -0
  53. data/icons/Holiday.xpm +25 -0
  54. data/icons/Link.xbm +6 -0
  55. data/icons/Link.xpm +25 -0
  56. data/icons/Other.xbm +6 -0
  57. data/icons/Other.xpm +28 -0
  58. data/icons/Party.xbm +6 -0
  59. data/icons/Party.xpm +23 -0
  60. data/icons/Private.xbm +6 -0
  61. data/icons/Private.xpm +26 -0
  62. data/icons/Recurrence.xbm +6 -0
  63. data/icons/Recurrence.xpm +98 -0
  64. data/icons/Vacation.xbm +6 -0
  65. data/icons/Vacation.xpm +26 -0
  66. data/lib/mhc.rb +45 -0
  67. data/lib/mhc/builder.rb +64 -0
  68. data/lib/mhc/caldav.rb +304 -0
  69. data/lib/mhc/calendar.rb +106 -0
  70. data/lib/mhc/command.rb +13 -0
  71. data/lib/mhc/command/cache.rb +14 -0
  72. data/lib/mhc/command/completions.rb +108 -0
  73. data/lib/mhc/command/init.rb +133 -0
  74. data/lib/mhc/command/scan.rb +33 -0
  75. data/lib/mhc/command/sync.rb +22 -0
  76. data/lib/mhc/config.rb +229 -0
  77. data/lib/mhc/converter.rb +330 -0
  78. data/lib/mhc/datastore.rb +164 -0
  79. data/lib/mhc/date_enumerator.rb +274 -0
  80. data/lib/mhc/date_frame.rb +124 -0
  81. data/lib/mhc/date_helper.rb +49 -0
  82. data/lib/mhc/etag.rb +68 -0
  83. data/lib/mhc/event.rb +396 -0
  84. data/lib/mhc/formatter.rb +312 -0
  85. data/lib/mhc/logger.rb +94 -0
  86. data/lib/mhc/modifier.rb +149 -0
  87. data/lib/mhc/occurrence.rb +94 -0
  88. data/lib/mhc/occurrence_enumerator.rb +113 -0
  89. data/lib/mhc/property_value.rb +33 -0
  90. data/lib/mhc/property_value/date.rb +190 -0
  91. data/lib/mhc/property_value/integer.rb +15 -0
  92. data/lib/mhc/property_value/list.rb +41 -0
  93. data/lib/mhc/property_value/period.rb +49 -0
  94. data/lib/mhc/property_value/range.rb +100 -0
  95. data/lib/mhc/property_value/recurrence_condition.rb +272 -0
  96. data/lib/mhc/property_value/text.rb +11 -0
  97. data/lib/mhc/property_value/time.rb +45 -0
  98. data/lib/mhc/query.rb +210 -0
  99. data/lib/mhc/sync.rb +46 -0
  100. data/lib/mhc/sync/driver.rb +108 -0
  101. data/lib/mhc/sync/status.rb +70 -0
  102. data/lib/mhc/sync/status_manager.rb +142 -0
  103. data/lib/mhc/sync/strategy.rb +233 -0
  104. data/lib/mhc/sync/syncinfo.rb +98 -0
  105. data/lib/mhc/templates/config.yml.erb +142 -0
  106. data/lib/mhc/version.rb +4 -0
  107. data/lib/mhc/webdav.rb +319 -0
  108. data/mhc.gemspec +24 -0
  109. data/samples/DOT.mhc-config.yml +116 -0
  110. data/samples/japanese-holidays.mhcc +153 -0
  111. data/samples/mhc-completions.zsh +11 -0
  112. data/spec/mhc_spec.rb +682 -0
  113. data/spec/spec_helper.rb +9 -0
  114. data/xpm/close.xpm +18 -0
  115. data/xpm/delete.xpm +19 -0
  116. data/xpm/exit.xpm +18 -0
  117. data/xpm/month.xpm +18 -0
  118. data/xpm/next.xpm +18 -0
  119. data/xpm/next2.xpm +18 -0
  120. data/xpm/next_year.xpm +18 -0
  121. data/xpm/open.xpm +19 -0
  122. data/xpm/prev.xpm +18 -0
  123. data/xpm/prev2.xpm +18 -0
  124. data/xpm/prev_year.xpm +18 -0
  125. data/xpm/save.xpm +19 -0
  126. data/xpm/today.xpm +18 -0
  127. 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