lib/zhong/at.rb in zhong-0.1.4 vs lib/zhong/at.rb in zhong-0.1.5

- old
+ new

@@ -15,67 +15,94 @@ end end.freeze attr_accessor :minute, :hour, :wday + def self.parse(at, grace: 0.seconds) + if at.respond_to?(:each) + MultiAt.new(at.map { |a| parse_at(a, grace) }) + else + parse_at(at, grace) + end + rescue ArgumentError + fail FailedToParse, at + end + + def self.deserialize(at) + parse_serialized(MessagePack.unpack(at)) + end + def initialize(minute: nil, hour: nil, wday: nil, grace: 0.seconds) @minute = minute @hour = hour @wday = wday @grace = grace fail ArgumentError unless valid? end def next_at(time = Time.now) - at_time = @wday.nil? ? time.dup : (time + (@wday - time.wday).days) + at_time = at_time_day_hour_minute_adjusted(time) - at_time = if !@minute.nil? && !@hour.nil? - at_time.change(hour: @hour, min: @minute) - elsif !@minute.nil? - at_time.change(min: @minute) - elsif !@hour.nil? && @hour != time.hour - at_time.change(hour: @hour) - else - at_time.change(sec: 0) - end + grace_cutoff = time.change(sec: 0) - @grace - if at_time < (time.change(sec: 0) - @grace) + if at_time < grace_cutoff if @wday.nil? - if @hour.nil? - at_time += 1.hour - else - at_time += 1.day - end + at_time += @hour.nil? ? 1.hour : 1.day else at_time += 1.week end else at_time end end - private def valid? - (@minute.nil? || (0..59).cover?(@minute)) && - (@hour.nil? || (0..23).cover?(@hour)) && - (@wday.nil? || (0..6).cover?(@wday)) + def to_s + str = "#{formatted_time(@hour)}:#{formatted_time(@minute)}" + + if @wday + str += " on #{WDAYS.invert[@wday].capitalize}" + end + + str end - def self.parse(at, grace: 0.seconds) - return unless at + def as_json + {m: @minute, h: @hour, w: @wday, g: @grace} + end - # TODO: refactor this mess - if at.respond_to?(:each) - return MultiAt.new(at.map { |a| parse(a, grace: grace) }) + def serialize + MessagePack.pack(as_json) + end + + protected + + def formatted_time(t) + if t.nil? + "**" + else + t.to_s.rjust(2, "0") end + end + def ==(o) + o.class == self.class && o.state == state + end + + def state + [@minute, @hour, @wday] + end + + private + + def self.parse_at(at, grace) case at when /\A([[:alpha:]]+)\s+(.*)\z/ wday = WDAYS[$1.downcase] if wday - parsed_time = parse($2, grace: grace) + parsed_time = parse_at($2, grace) parsed_time.wday = wday parsed_time else fail FailedToParse, at end @@ -86,22 +113,66 @@ when /\A(\d{1,2}):\*{1,2}\z/ new(hour: $1.to_i, grace: grace) else fail FailedToParse, at end - rescue ArgumentError - fail FailedToParse, at end + + def self.parse_serialized(at) + if at.is_a?(Array) + MultiAt.new(at.map { |a| parse_serialized(a) }) + else + new(minute: at["m"], hour: at["h"], wday: at["w"], grace: at["g"]) + end + end + + def at_time_hour_minute_adjusted(time) + if @minute && @hour + time.change(hour: @hour, min: @minute) + elsif @minute + time.change(min: @minute) + elsif @hour && @hour != time.hour + time.change(hour: @hour) + else + time.change(sec: 0) + end + end + + def at_time_day_hour_minute_adjusted(time) + at_time_hour_minute_adjusted(time) + (@wday ? (@wday - time.wday) : 0).days + end + + def valid? + (@minute.nil? || (0..59).cover?(@minute)) && + (@hour.nil? || (0..23).cover?(@hour)) && + (@wday.nil? || (0..6).cover?(@wday)) + end end class MultiAt attr_accessor :ats def initialize(ats = []) @ats = ats end + def ==(o) + o.class == self.class && @ats == o.ats + end + def next_at(time = Time.now) ats.map { |at| at.next_at(time) }.min + end + + def to_s + ats.map(&:to_s).join(", ") + end + + def as_json + ats.map(&:as_json) + end + + def serialize + MessagePack.pack(as_json) end end end