mhc 1.1.1 → 1.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 +5 -5
- data/bin/mhc +66 -3
- data/emacs/Cask +1 -1
- data/emacs/mhc-date.el +1 -1
- data/emacs/mhc-day.el +1 -1
- data/emacs/mhc-db.el +57 -38
- data/emacs/mhc-draft.el +36 -22
- data/emacs/mhc-face.el +1 -1
- data/emacs/mhc-header.el +20 -1
- data/emacs/mhc-minibuf.el +12 -7
- data/emacs/mhc-parse.el +1 -1
- data/emacs/mhc-process.el +26 -9
- data/emacs/mhc-ps.el +1 -1
- data/emacs/mhc-schedule.el +5 -2
- data/emacs/mhc-summary.el +31 -12
- data/emacs/mhc-vars.el +15 -2
- data/emacs/mhc.el +50 -24
- data/lib/mhc.rb +3 -1
- data/lib/mhc/builder.rb +5 -1
- data/lib/mhc/calendar.rb +5 -1
- data/lib/mhc/command/cache.rb +5 -4
- data/lib/mhc/converter.rb +3 -2
- data/lib/mhc/datastore.rb +52 -13
- data/lib/mhc/date_enumerator.rb +2 -2
- data/lib/mhc/event.rb +42 -21
- data/lib/mhc/formatter.rb +17 -312
- data/lib/mhc/formatter/base.rb +125 -0
- data/lib/mhc/formatter/emacs.rb +47 -0
- data/lib/mhc/formatter/howm.rb +35 -0
- data/lib/mhc/formatter/icalendar.rb +17 -0
- data/lib/mhc/formatter/json.rb +27 -0
- data/lib/mhc/formatter/mail.rb +20 -0
- data/lib/mhc/formatter/org_table.rb +24 -0
- data/lib/mhc/formatter/symbolic_expression.rb +42 -0
- data/lib/mhc/formatter/text.rb +29 -0
- data/lib/mhc/occurrence.rb +27 -5
- data/lib/mhc/occurrence_enumerator.rb +1 -1
- data/lib/mhc/property_value.rb +6 -0
- data/lib/mhc/property_value/date.rb +23 -14
- data/lib/mhc/property_value/date_time.rb +19 -0
- data/lib/mhc/property_value/integer.rb +5 -1
- data/lib/mhc/property_value/list.rb +7 -6
- data/lib/mhc/property_value/period.rb +3 -1
- data/lib/mhc/property_value/range.rb +1 -1
- data/lib/mhc/property_value/time.rb +8 -1
- data/lib/mhc/version.rb +1 -1
- data/spec/mhc_spec.rb +83 -0
- metadata +13 -3
data/lib/mhc/property_value.rb
CHANGED
@@ -15,12 +15,18 @@ module Mhc
|
|
15
15
|
return @value.to_s
|
16
16
|
end
|
17
17
|
|
18
|
+
def empty?
|
19
|
+
return true if @value.nil? || (@value.respond_to?(:empty?) && @value.empty?)
|
20
|
+
return false
|
21
|
+
end
|
22
|
+
|
18
23
|
alias_method :to_s, :to_mhc_string
|
19
24
|
end
|
20
25
|
|
21
26
|
dir = File.dirname(__FILE__) + "/property_value"
|
22
27
|
|
23
28
|
autoload :Date, "#{dir}/date.rb"
|
29
|
+
autoload :DateTime, "#{dir}/date_time.rb"
|
24
30
|
autoload :Integer, "#{dir}/integer.rb"
|
25
31
|
autoload :List, "#{dir}/list.rb"
|
26
32
|
autoload :Period, "#{dir}/period.rb"
|
@@ -8,12 +8,25 @@ module Mhc
|
|
8
8
|
|
9
9
|
DAYS_OF_MONTH = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
|
10
10
|
|
11
|
-
def self.parse(string)
|
12
|
-
|
13
|
-
#
|
14
|
-
|
15
|
-
|
16
|
-
|
11
|
+
def self.parse(string, default = nil)
|
12
|
+
begin
|
13
|
+
# YYYYMMDD/HH:MM => DateTime
|
14
|
+
if /^(\d{4})(\d{2})(\d{2})\/(\d{2}):(\d{2})$/ =~ string
|
15
|
+
DateTime.new($1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, 0)
|
16
|
+
|
17
|
+
# YYYYMMDD => Date
|
18
|
+
elsif /^(\d{4})(\d{2})(\d{2})$/ =~ string
|
19
|
+
Date.new($1.to_i, $2.to_i, $3.to_i)
|
20
|
+
|
21
|
+
# YYYYMMDD/hh:mm-HH:MM => DateTime taking YYYYMMDD, HH:MM
|
22
|
+
elsif /^(\d+):(\d+)$/ =~ string && default
|
23
|
+
DateTime.new(default.year, default.month, default.day, $1.to_i, $2.to_i, 0)
|
24
|
+
|
25
|
+
else
|
26
|
+
fail ParseError
|
27
|
+
end
|
28
|
+
rescue
|
29
|
+
raise ParseError, "invalid date string \"#{string}\""
|
17
30
|
end
|
18
31
|
end
|
19
32
|
|
@@ -38,7 +51,7 @@ module Mhc
|
|
38
51
|
return self.today.first_day_of_month.next_month
|
39
52
|
|
40
53
|
else
|
41
|
-
raise ParseError, "invalid date string
|
54
|
+
raise ParseError, "invalid date string \"#{date_string}\""
|
42
55
|
end
|
43
56
|
end
|
44
57
|
|
@@ -65,7 +78,7 @@ module Mhc
|
|
65
78
|
date = parse_relative($1)
|
66
79
|
return date..date
|
67
80
|
else
|
68
|
-
raise ParseError, "invalid date range string
|
81
|
+
raise ParseError, "invalid date range string \"#{range_string}\""
|
69
82
|
end
|
70
83
|
end
|
71
84
|
|
@@ -80,12 +93,8 @@ module Mhc
|
|
80
93
|
end
|
81
94
|
end
|
82
95
|
|
83
|
-
def parse(string)
|
84
|
-
|
85
|
-
self.class.parse(string)
|
86
|
-
else
|
87
|
-
return nil # raise ParseError
|
88
|
-
end
|
96
|
+
def parse(string, default = nil)
|
97
|
+
self.class.parse(string, default)
|
89
98
|
end
|
90
99
|
|
91
100
|
def add_time(time = nil)
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require "date"
|
2
|
+
|
3
|
+
module Mhc
|
4
|
+
module PropertyValue
|
5
|
+
class DateTime < ::DateTime
|
6
|
+
|
7
|
+
def to_mhc_string
|
8
|
+
return strftime("%Y%m%d/%H:%M")
|
9
|
+
end
|
10
|
+
|
11
|
+
def absolute_from_epoch
|
12
|
+
return (self - Date.new(1970, 1, 1)).to_i
|
13
|
+
end
|
14
|
+
|
15
|
+
alias_method :to_s, :to_mhc_string
|
16
|
+
|
17
|
+
end # class DateTime
|
18
|
+
end # module PropertyValue
|
19
|
+
end # module Mhc
|
@@ -2,7 +2,11 @@ module Mhc
|
|
2
2
|
module PropertyValue
|
3
3
|
class Integer < Base
|
4
4
|
def parse(string)
|
5
|
-
|
5
|
+
if /^\d+$/ =~ string
|
6
|
+
@value = string.to_i
|
7
|
+
else
|
8
|
+
raise ParseError, "invalid integer format \"#{string}\""
|
9
|
+
end
|
6
10
|
return self
|
7
11
|
end
|
8
12
|
|
@@ -6,35 +6,36 @@ module Mhc
|
|
6
6
|
ITEM_SEPARATOR = " "
|
7
7
|
|
8
8
|
def initialize(item_class)
|
9
|
-
@
|
9
|
+
@value = []
|
10
10
|
@item_class = item_class
|
11
11
|
end
|
12
12
|
|
13
13
|
def each
|
14
|
-
@
|
14
|
+
@value.each do |value|
|
15
15
|
yield value
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
19
|
def include?(o)
|
20
|
-
@
|
20
|
+
@value.include?(o)
|
21
21
|
end
|
22
22
|
|
23
23
|
def empty?
|
24
|
-
@
|
24
|
+
@value.empty?
|
25
25
|
end
|
26
26
|
|
27
27
|
def parse(string)
|
28
28
|
string.strip.split(ITEM_SEPARATOR).each do |str|
|
29
29
|
item = @item_class.parse(str)
|
30
|
-
@
|
30
|
+
@value << item if item
|
31
31
|
end
|
32
32
|
return self
|
33
33
|
end
|
34
34
|
|
35
35
|
def to_mhc_string
|
36
|
-
@
|
36
|
+
@value.map{|item| item.to_mhc_string}.join(ITEM_SEPARATOR)
|
37
37
|
end
|
38
|
+
alias_method :to_s, :to_mhc_string
|
38
39
|
|
39
40
|
end # class List
|
40
41
|
end # module PropertyValue
|
@@ -4,7 +4,7 @@ module Mhc
|
|
4
4
|
|
5
5
|
UNIT2MIN = {'minute' => 1, 'hour' => 60, 'day' => 60*24}
|
6
6
|
UNITS = UNIT2MIN.keys
|
7
|
-
REGEXP = /(\d+)\s*(#{UNITS.join("|")})s
|
7
|
+
REGEXP = /(\d+)\s*(#{UNITS.join("|")})s?$/
|
8
8
|
|
9
9
|
def self.parse(string)
|
10
10
|
return new.parse(string)
|
@@ -13,6 +13,8 @@ module Mhc
|
|
13
13
|
def parse(string)
|
14
14
|
if REGEXP =~ string
|
15
15
|
@minutes = (UNIT2MIN[$2] * $1.to_i)
|
16
|
+
else
|
17
|
+
raise ParseError, "invalid period string \"#{string}\""
|
16
18
|
end
|
17
19
|
return self
|
18
20
|
end
|
@@ -25,7 +25,7 @@ module Mhc
|
|
25
25
|
last = first if last.nil? # single "A" means "A-A"
|
26
26
|
|
27
27
|
@first = @item_class.parse(first) unless first.to_s == ""
|
28
|
-
@last = @item_class.parse(last) unless last.to_s == ""
|
28
|
+
@last = @item_class.parse(last, @first) unless last.to_s == ""
|
29
29
|
return self.class.new(@item_class, @prefix, @first, @last)
|
30
30
|
end
|
31
31
|
|
@@ -7,13 +7,20 @@ module Mhc
|
|
7
7
|
class Time < Base
|
8
8
|
include Comparable
|
9
9
|
|
10
|
-
def parse(string)
|
10
|
+
def parse(string, default = nil)
|
11
|
+
# default is dummy for matching interface
|
11
12
|
if /^(\d+):(\d+)$/ =~ string
|
12
13
|
@sec = ($1.to_i) * 3600 + ($2.to_i) * 60
|
14
|
+
else
|
15
|
+
raise ParseError, "invalid time format \"#{string}\""
|
13
16
|
end
|
14
17
|
return self
|
15
18
|
end
|
16
19
|
|
20
|
+
def self.parse(string, default = nil)
|
21
|
+
new.parse(string, default)
|
22
|
+
end
|
23
|
+
|
17
24
|
def days; (@sec ) / 86400 ;end
|
18
25
|
def hour; (@sec % 86400) / 3600 ;end
|
19
26
|
def minute; (@sec % 3600) / 60 ;end
|
data/lib/mhc/version.rb
CHANGED
data/spec/mhc_spec.rb
CHANGED
@@ -125,6 +125,20 @@ describe Mhc::Event do
|
|
125
125
|
end
|
126
126
|
|
127
127
|
|
128
|
+
it "should emit an error on parsing invalid time format." do
|
129
|
+
str = <<-EOF.strip_heredoc
|
130
|
+
X-SC-Subject: Invalid Date format
|
131
|
+
X-SC-Location: Room1
|
132
|
+
X-SC-Day: 19960930
|
133
|
+
X-SC-Duration: 19960930-
|
134
|
+
X-SC-Time: 19:30-2030
|
135
|
+
X-SC-Record-Id: 54A339AD-F7FD-4E56-9B70-2D09F840E94D
|
136
|
+
X-SC-Sequence: 0
|
137
|
+
EOF
|
138
|
+
errors = Mhc::Event.validate(str)
|
139
|
+
expect(errors.length).to eq 1
|
140
|
+
expect(errors.first.first.message).to match /invalid time format/
|
141
|
+
end
|
128
142
|
|
129
143
|
it "should occur weekly on Monday and Thursday from 2014-04-01 to 2014-04-30 with exception of 2014-04-10 (Thu)" do
|
130
144
|
ev = Mhc::Event.parse <<-EOF.strip_heredoc
|
@@ -204,6 +218,53 @@ describe Mhc::Event do
|
|
204
218
|
["20140203 10:00-12:00 TEST", "20140509 10:00-12:00 TEST", "20140831 10:00-12:00 TEST"]
|
205
219
|
end
|
206
220
|
|
221
|
+
it "should produce a list of occurrences, and one occurrence has different Time" do
|
222
|
+
ev = Mhc::Event.parse <<-EOF.strip_heredoc
|
223
|
+
X-SC-Subject: X-Project
|
224
|
+
X-SC-Location: 101
|
225
|
+
X-SC-Day: 20160601 20160602/16:00-18:00 20160616 20160617 20160623 20160624
|
226
|
+
X-SC-Time: 09:00-11:00
|
227
|
+
X-SC-Category: Conference
|
228
|
+
X-SC-Record-Id: FEDA4C97-21C2-46AA-A395-075856FBD5C3
|
229
|
+
EOF
|
230
|
+
expect(ev.occurrences.take(30).map{|o| "#{o.dtstart.strftime("%Y%m%d/%H:%M")}-#{o.dtend.strftime("%Y%m%d/%H:%M")} #{o.subject}"}).to eq \
|
231
|
+
["20160601/09:00-20160601/11:00 X-Project",
|
232
|
+
"20160602/16:00-20160602/18:00 X-Project",
|
233
|
+
"20160616/09:00-20160616/11:00 X-Project",
|
234
|
+
"20160617/09:00-20160617/11:00 X-Project",
|
235
|
+
"20160623/09:00-20160623/11:00 X-Project",
|
236
|
+
"20160624/09:00-20160624/11:00 X-Project"]
|
237
|
+
end
|
238
|
+
|
239
|
+
it "should produce a list of occurrences, and one occurrence has different Time crossing days" do
|
240
|
+
ev = Mhc::Event.parse <<-EOF.strip_heredoc
|
241
|
+
X-SC-Subject: TEST
|
242
|
+
X-SC-Time: 10:00-12:00
|
243
|
+
X-SC-Day: 20150203/10:30-20150205/12:30 20150509 20150831
|
244
|
+
X-SC-Record-Id: FEDA4C97-21C2-46AA-A395-075856FBD5C3
|
245
|
+
EOF
|
246
|
+
|
247
|
+
expect(ev.occurrences.take(30).map{|o| "#{o.dtstart.strftime("%Y%m%d/%H:%M")}-#{o.dtend.strftime("%Y%m%d/%H:%M")} #{o.subject}"}).to eq \
|
248
|
+
["20150203/10:30-20150205/12:30 TEST",
|
249
|
+
"20150509/10:00-20150509/12:00 TEST",
|
250
|
+
"20150831/10:00-20150831/12:00 TEST"]
|
251
|
+
|
252
|
+
expect(ev.to_ics).to eq <<-'EOF'.strip_heredoc
|
253
|
+
BEGIN:VEVENT
|
254
|
+
RDATE;VALUE=PERIOD:20150509T100000Z/20150509T120000Z,20150831T100000Z/20150831T120000Z
|
255
|
+
CREATED;VALUE=DATE-TIME:20140101T000000Z
|
256
|
+
DTEND;VALUE=DATE-TIME:20150205T123000Z
|
257
|
+
DTSTART;VALUE=DATE-TIME:20150203T103000Z
|
258
|
+
DTSTAMP;VALUE=DATE-TIME:20140101T000000Z
|
259
|
+
LAST-MODIFIED;VALUE=DATE-TIME:20140101T000000Z
|
260
|
+
UID:FEDA4C97-21C2-46AA-A395-075856FBD5C3
|
261
|
+
DESCRIPTION:
|
262
|
+
SUMMARY:TEST
|
263
|
+
SEQUENCE:0
|
264
|
+
END:VEVENT
|
265
|
+
EOF
|
266
|
+
end
|
267
|
+
|
207
268
|
it "should return true when #allday? is called if X-SC-Time: is blank" do
|
208
269
|
ev = Mhc::Event.parse <<-EOF.strip_heredoc
|
209
270
|
X-SC-Subject: TEST
|
@@ -397,6 +458,28 @@ describe Mhc::Event do
|
|
397
458
|
EOF
|
398
459
|
end
|
399
460
|
|
461
|
+
it "should return icalendar VEVENT over 24h event" do
|
462
|
+
ev = Mhc::Event.parse <<-EOF.strip_heredoc
|
463
|
+
X-SC-Subject: CS1
|
464
|
+
X-SC-Time: 12:00-13:00
|
465
|
+
X-SC-Day: 20140508/12:00-20140509/10:10
|
466
|
+
X-SC-Record-Id: 69CFD0DF-4058-425B-8C2B-40D81E6A2392
|
467
|
+
EOF
|
468
|
+
expect(ev.to_ics).to eq <<-'EOF'.strip_heredoc
|
469
|
+
BEGIN:VEVENT
|
470
|
+
CREATED;VALUE=DATE-TIME:20140101T000000Z
|
471
|
+
DTEND;VALUE=DATE-TIME:20140509T101000Z
|
472
|
+
DTSTART;VALUE=DATE-TIME:20140508T120000Z
|
473
|
+
DTSTAMP;VALUE=DATE-TIME:20140101T000000Z
|
474
|
+
LAST-MODIFIED;VALUE=DATE-TIME:20140101T000000Z
|
475
|
+
UID:69CFD0DF-4058-425B-8C2B-40D81E6A2392
|
476
|
+
DESCRIPTION:
|
477
|
+
SUMMARY:CS1
|
478
|
+
SEQUENCE:0
|
479
|
+
END:VEVENT
|
480
|
+
EOF
|
481
|
+
end
|
482
|
+
|
400
483
|
it "should return icalendar VEVENT two day's allday event" do
|
401
484
|
ev = Mhc::Event.parse <<-EOF.strip_heredoc
|
402
485
|
X-SC-Subject: CS1
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mhc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yoshinari Nomura
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-07-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -198,12 +198,22 @@ files:
|
|
198
198
|
- lib/mhc/etag.rb
|
199
199
|
- lib/mhc/event.rb
|
200
200
|
- lib/mhc/formatter.rb
|
201
|
+
- lib/mhc/formatter/base.rb
|
202
|
+
- lib/mhc/formatter/emacs.rb
|
203
|
+
- lib/mhc/formatter/howm.rb
|
204
|
+
- lib/mhc/formatter/icalendar.rb
|
205
|
+
- lib/mhc/formatter/json.rb
|
206
|
+
- lib/mhc/formatter/mail.rb
|
207
|
+
- lib/mhc/formatter/org_table.rb
|
208
|
+
- lib/mhc/formatter/symbolic_expression.rb
|
209
|
+
- lib/mhc/formatter/text.rb
|
201
210
|
- lib/mhc/logger.rb
|
202
211
|
- lib/mhc/modifier.rb
|
203
212
|
- lib/mhc/occurrence.rb
|
204
213
|
- lib/mhc/occurrence_enumerator.rb
|
205
214
|
- lib/mhc/property_value.rb
|
206
215
|
- lib/mhc/property_value/date.rb
|
216
|
+
- lib/mhc/property_value/date_time.rb
|
207
217
|
- lib/mhc/property_value/integer.rb
|
208
218
|
- lib/mhc/property_value/list.rb
|
209
219
|
- lib/mhc/property_value/period.rb
|
@@ -260,7 +270,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
260
270
|
version: '0'
|
261
271
|
requirements: []
|
262
272
|
rubyforge_project:
|
263
|
-
rubygems_version: 2.
|
273
|
+
rubygems_version: 2.7.6
|
264
274
|
signing_key:
|
265
275
|
specification_version: 4
|
266
276
|
summary: Message Harmonized Calendaring
|