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