class Mhc::Event

Mhc::Event defines a simple representation of calendar events. It looks like a RFC822 message with some X- headers to represent event properties:

Public Class Methods

new() click to toggle source

initializers

# File lib/mhc/event.rb, line 30
def initialize
  clear
end
new_from_ics(ics_string) click to toggle source
# File lib/mhc/event.rb, line 72
def self.new_from_ics(ics_string)
  Mhc::Converter::IcalendarImporter.parse_ics(ics_string)
end
parse(string) click to toggle source
# File lib/mhc/event.rb, line 34
def self.parse(string)
  return new.parse(string)
end
parse_file(path, lazy = true) click to toggle source
# File lib/mhc/event.rb, line 38
def self.parse_file(path, lazy = true)
  return new.parse_file(path, lazy)
end
validate(string) click to toggle source
# File lib/mhc/event.rb, line 42
def self.validate(string)
  return new.validate(string)
end

Public Instance Methods

alarm() click to toggle source

alarm

# File lib/mhc/event.rb, line 83
def alarm
  return @alarm ||= Mhc::PropertyValue::Period.new
end
alarm=(string) click to toggle source
# File lib/mhc/event.rb, line 87
def alarm=(string)
  return @alarm = alarm.parse(string)
end
allday?() click to toggle source
# File lib/mhc/event.rb, line 262
def allday?
  time_range.blank?
end
body()
Alias for: description
categories() click to toggle source

category

# File lib/mhc/event.rb, line 92
def categories
  return @categories ||=
    Mhc::PropertyValue::List.new(Mhc::PropertyValue::Text)
end
categories=(string) click to toggle source
# File lib/mhc/event.rb, line 97
def categories=(string)
  return @categories = categories.parse(string)
end
dates() click to toggle source

date list is a list of date range

# File lib/mhc/event.rb, line 161
def dates
  return @dates ||=
    Mhc::PropertyValue::List.new(Mhc::PropertyValue::Range.new(Mhc::PropertyValue::Date.new))
end
dates=(string) click to toggle source
# File lib/mhc/event.rb, line 166
def dates=(string)
  string = string.split.select {|s| /^!/ !~ s}.join(" ")
  return @dates = dates.parse(string)
end
description() click to toggle source

description

# File lib/mhc/event.rb, line 110
def description
  unless @description
    @description = Mhc::PropertyValue::Text.new

    if lazy? && File.file?(@path)
      File.open(@path, "r") do |file|
        file.gets("\n\n") # discard header.
        @description.parse(file.gets(nil))
      end
    end
  end
  return @description
end
Also aliased as: body
description=(string) click to toggle source
# File lib/mhc/event.rb, line 125
def description=(string)
  return @description = description.parse(string)
end
dump() click to toggle source

dump

# File lib/mhc/event.rb, line 289
def dump
  non_xsc_header = @non_xsc_header.to_s.sub(/\n+\z/, "")
  non_xsc_header += "\n" if non_xsc_header != ""

  body = description.to_mhc_string
  body += "\n" if body != "" && body !~ /\n\z/

  return dump_header + non_xsc_header + "\n" + body
end
Also aliased as: to_mhc_string
dump_header() click to toggle source
# File lib/mhc/event.rb, line 299
def dump_header
  return "X-SC-Subject: #{subject.to_mhc_string}\n"      +
    "X-SC-Location: #{location.to_mhc_string}\n"         +
    "X-SC-Day: " + "#{dates.to_mhc_string} #{exceptions.to_mhc_string}".strip + "\n" +
    "X-SC-Time: #{time_range.to_mhc_string}\n"           +
    "X-SC-Category: #{categories.to_mhc_string}\n"       +
    "X-SC-Mission-Tag: #{mission_tag.to_mhc_string}\n"   +
    "X-SC-Recurrence-Tag: #{recurrence_tag.to_mhc_string}\n"       +
    "X-SC-Cond: #{recurrence_condition.to_mhc_string}\n" +
    "X-SC-Duration: #{duration.to_mhc_string}\n"         +
    "X-SC-Alarm: #{alarm.to_mhc_string}\n"               +
    "X-SC-Record-Id: #{record_id.to_mhc_string}\n"       +
    "X-SC-Sequence: #{sequence.to_mhc_string}\n"
end
duration() click to toggle source

duration

# File lib/mhc/event.rb, line 205
def duration
  return @duration ||=
    Mhc::PropertyValue::Range.new(Mhc::PropertyValue::Date)
end
duration=(string) click to toggle source
# File lib/mhc/event.rb, line 210
def duration=(string)
  return @duration = duration.parse(string)
end
etag() click to toggle source
# File lib/mhc/event.rb, line 254
def etag
  return "#{uid.to_s}-#{sequence.to_s}"
end
exceptions() click to toggle source
# File lib/mhc/event.rb, line 183
def exceptions
  return @exceptions ||=
    Mhc::PropertyValue::List.new(Mhc::PropertyValue::Range.new(Mhc::PropertyValue::Date.new, "!"))
end
exceptions=(string) click to toggle source
# File lib/mhc/event.rb, line 188
def exceptions=(string)
  string = string.split.select {|s| /^!/ =~ s}.map{|s| s[1..-1]}.join(" ")
  return @exceptions = exceptions.parse(string)
end
holiday?() click to toggle source
# File lib/mhc/event.rb, line 101
def holiday?
  in_category?("holiday")
end
in_category?(category) click to toggle source
# File lib/mhc/event.rb, line 105
def in_category?(category)
  categories.map{|c| c.to_s.downcase}.member?(category.downcase)
end
location() click to toggle source

location

# File lib/mhc/event.rb, line 130
def location
  return @location ||= Mhc::PropertyValue::Text.new
end
location=(string) click to toggle source
# File lib/mhc/event.rb, line 134
def location=(string)
  return @location = location.parse(string)
end
mission_tag() click to toggle source

mission-tag

# File lib/mhc/event.rb, line 233
def mission_tag
  return @mission_tag ||= Mhc::PropertyValue::Text.new
end
mission_tag=(string) click to toggle source
# File lib/mhc/event.rb, line 237
def mission_tag=(string)
  return @mission_tag = mission_tag.parse(string)
end
obsolete_dates=(string) click to toggle source
# File lib/mhc/event.rb, line 171
def obsolete_dates=(string)
  # STDERR.print "Obsolete X-SC-Date: header.\n"
  if /(\d+)\s+([A-Z][a-z][a-z])\s+(\d+)\s+(\d\d:\d\d)/ =~ string
    dd, mm, yy, hhmm = $1.to_i, $2, $3.to_i + 1900, $4
    mm = ("JanFebMarAprMayJunJulAugSepOctNovDec".index(mm)) / 3 + 1
    @dates = dates.parse("%04d%02d%02d" % [yy, mm, dd])
    if hhmm and hhmm != '00:00'
      @time_range = time_range.parse(hhmm)
    end
  end
end
occurrences(range:nil) click to toggle source
# File lib/mhc/event.rb, line 250
def occurrences(range:nil)
  Mhc::OccurrenceEnumerator.new(self, dates, exceptions, recurrence_condition, duration, range)
end
parse(string) click to toggle source
# File lib/mhc/event.rb, line 63
def parse(string)
  clear
  header, body = string.scrub.split(/\n\n/, 2)

  parse_header(header)
  self.description = body
  return self
end
parse_file(path, lazy = true) click to toggle source
# File lib/mhc/event.rb, line 46
def parse_file(path, lazy = true)
  STDOUT.puts "parsing #{File.expand_path(path)}" if $MHC_DEBUG

  clear
  header, body = nil, nil

  File.open(path, "r") do |file|
    header = file.gets("\n\n")
    body   = file.gets(nil) unless lazy
  end

  @path = path if lazy
  parse_header(header)
  self.description = body if body
  return self
end
path() click to toggle source
# File lib/mhc/event.rb, line 76
def path
  return @path
end
range() click to toggle source
# File lib/mhc/event.rb, line 266
def range
  min0, max0 = Mhc::PropertyValue::Date.parse("19000101"),
               Mhc::PropertyValue::Date.parse("99991231")

  if recurring?
    min, max = min0, max0
  else
    min, max = dates.min, dates.max
    min = min.respond_to?(:first) ? min.first : min0
    max = max.respond_to?(:last)  ? max.last  : max0
  end
  min = duration.first if duration.first && duration.first > min
  max = duration.last  if duration.last  && duration.last  < max

  return min..max if min && max && min <= max

  STDERR.puts "Warn: invalid date range? #{self.uid}"
  return min0..max0
end
record_id() click to toggle source

record-id

# File lib/mhc/event.rb, line 139
def record_id
  return @record_id ||= Mhc::PropertyValue::Text.new
end
record_id=(string) click to toggle source
# File lib/mhc/event.rb, line 143
def record_id=(string)
  return @record_id = record_id.parse(string)
end
recurrence_condition() click to toggle source

recurrence condition

# File lib/mhc/event.rb, line 215
def recurrence_condition
  return @cond ||= Mhc::PropertyValue::RecurrenceCondition.new
end
recurrence_condition=(string) click to toggle source
# File lib/mhc/event.rb, line 219
def recurrence_condition=(string)
  return @cond = recurrence_condition.parse(string)
end
recurrence_tag() click to toggle source

recurrence-tag

# File lib/mhc/event.rb, line 224
def recurrence_tag
  return @recurrence_tag ||= Mhc::PropertyValue::Text.new
end
recurrence_tag=(string) click to toggle source
# File lib/mhc/event.rb, line 228
def recurrence_tag=(string)
  return @recurrence_tag = recurrence_tag.parse(string)
end
recurring?() click to toggle source
# File lib/mhc/event.rb, line 258
def recurring?
  not recurrence_condition.empty?
end
sequence() click to toggle source

sequence

# File lib/mhc/event.rb, line 242
def sequence
  return @sequence ||= Mhc::PropertyValue::Integer.new.parse("0")
end
sequence=(string) click to toggle source
# File lib/mhc/event.rb, line 246
def sequence=(string)
  return @sequence = sequence.parse(string.to_s)
end
subject() click to toggle source

subject

# File lib/mhc/event.rb, line 152
def subject
  return @subject ||= Mhc::PropertyValue::Text.new
end
subject=(string) click to toggle source
# File lib/mhc/event.rb, line 156
def subject=(string)
  return @subject = subject.parse(string)
end
time_range() click to toggle source

time

# File lib/mhc/event.rb, line 194
def time_range
  return @time_range ||=
    Mhc::PropertyValue::Range.new(Mhc::PropertyValue::Time)
end
time_range=(string) click to toggle source
# File lib/mhc/event.rb, line 199
def time_range=(string)
  @time_range = time_range.parse(string)
  return @time_range
end
to_icalendar() click to toggle source
# File lib/mhc/event.rb, line 323
def to_icalendar
  Mhc::Converter::Icalendar.new.to_icalendar(self)
end
to_ics() click to toggle source

converter

# File lib/mhc/event.rb, line 319
def to_ics
  Mhc::Converter::Icalendar.new.to_ics(self)
end
to_ics_string() click to toggle source
# File lib/mhc/event.rb, line 327
def to_ics_string
  Mhc::Converter::Icalendar.new.to_ics_string(self)
end
to_mhc_string()
Alias for: dump
uid() click to toggle source
# File lib/mhc/event.rb, line 147
def uid
  record_id.to_s
end
validate(string) click to toggle source
# File lib/mhc/event.rb, line 331
def validate(string)
  header, _ = string.scrub.split(/\n\n/, 2)
  errors = parse_header(header)

  errors << ["no subject"] if subject.empty?
  errors << ["no record-id"] if record_id.empty?
  errors << ["no effective date specified"] if dates.empty? && recurrence_condition.empty?

  return errors
end

Private Instance Methods

clear() click to toggle source
# File lib/mhc/event.rb, line 349
def clear
  @alarm, @categories, @description, @location = [nil]*4
  @record_id, @subject = [nil]*2
  @dates, @exceptions, @time_range, @duration, @cond, @oc = [nil]*6
  @non_xsc_header, @path = [nil]*2
  return self
end
lazy?() click to toggle source
# File lib/mhc/event.rb, line 345
def lazy?
  return !@path.nil?
end
parse_header(string) click to toggle source
# File lib/mhc/event.rb, line 363
def parse_header(string)
  hash = {}
  string.scrub.scan(/^x-sc-([^:]++):[ \t]*([^\n]*(?:\n[ \t]+[^\n]*)*)/i) do |key, val|
    hash[key.downcase] = val.gsub("\n", " ").strip
  end
  return parse_xsc_header(hash)
end
parse_header_full(string) click to toggle source
# File lib/mhc/event.rb, line 357
def parse_header_full(string)
  xsc, @non_xsc_header = separate_header(string)
  parse_xsc_header(xsc)
  return self
end
parse_xsc_header(hash) click to toggle source
# File lib/mhc/event.rb, line 371
def parse_xsc_header(hash)
  errors = []
  hash.each do |key, val|
    next if val.empty?
    begin
      case key
      when "day"            ; self.dates                = val
                            ; self.exceptions           = val
      when "date"           ; self.obsolete_dates       = val
      when "subject"        ; self.subject              = val
      when "location"       ; self.location             = val
      when "time"           ; self.time_range           = val
      when "duration"       ; self.duration             = val
      when "category"       ; self.categories           = val
      when "mission-tag"    ; self.mission_tag          = val
      when "recurrence-tag" ; self.recurrence_tag       = val
      when "cond"           ; self.recurrence_condition = val
      when "alarm"          ; self.alarm                = val
      when "record-id"      ; self.record_id            = val
      when "sequence"       ; self.sequence             = val
      else
        raise Mhc::PropertyValue::ParseError,
              "invalid X-SC-#{key.capitalize} header"
      end
    rescue Mhc::PropertyValue::ParseError => e
      errors << [e, key]
    end
  end
  return errors
end
separate_header(header) click to toggle source

return: X-SC-* headers as a hash and

non-X-SC-* headers as one string.
# File lib/mhc/event.rb, line 404
def separate_header(header)
  xsc, non_xsc, xsc_key = {}, "", nil

  header.split("\n").each do |line|
    if line =~ /^X-SC-([^:]+):(.*)/i
      xsc_key = $1.downcase
      xsc[xsc_key] = $2.to_s.strip

    elsif line =~ /^\s/ && xsc_key
      xsc[xsc_key] += " " + line

    else
      xsc_key = nil
      non_xsc += line + "\n"
    end
  end
  return [xsc, non_xsc]
end