lib/chronic/handlers.rb in chronic-0.4.1 vs lib/chronic/handlers.rb in chronic-0.4.2
- old
+ new
@@ -1,189 +1,48 @@
module Chronic
+ module Handlers
+ module_function
- class << self
-
- def definitions(options={}) #:nodoc:
- options[:endian_precedence] ||= [:middle, :little]
- # ensure the endian precedence is exactly two elements long
- raise ChronicPain, "More than two elements specified for endian precedence array" unless options[:endian_precedence].length == 2
-
- # handler for dd/mm/yyyy
- @little_endian_handler ||= Handler.new([:scalar_day, :separator_slash_or_dash, :scalar_month, :separator_slash_or_dash, :scalar_year, :separator_at?, 'time?'], :handle_sd_sm_sy)
-
- # handler for mm/dd/yyyy
- @middle_endian_handler ||= Handler.new([:scalar_month, :separator_slash_or_dash, :scalar_day, :separator_slash_or_dash, :scalar_year, :separator_at?, 'time?'], :handle_sm_sd_sy)
-
- # ensure we have valid endian values
- options[:endian_precedence].each do |e|
- raise ChronicPain, "Unknown endian type: #{e.to_s}" unless instance_variable_defined?(endian_variable_name_for(e))
- end
-
- @definitions ||= {
- :time => [
- Handler.new([:repeater_time, :repeater_day_portion?], nil)
- ],
-
- :date => [
- Handler.new([:repeater_day_name, :repeater_month_name, :scalar_day, :repeater_time, :separator_slash_or_dash?, :time_zone, :scalar_year], :handle_rdn_rmn_sd_t_tz_sy),
- Handler.new([:repeater_month_name, :scalar_day, :scalar_year], :handle_rmn_sd_sy),
- Handler.new([:repeater_month_name, :ordinal_day, :scalar_year], :handle_rmn_od_sy),
- Handler.new([:repeater_month_name, :scalar_day, :scalar_year, :separator_at?, 'time?'], :handle_rmn_sd_sy),
- Handler.new([:repeater_month_name, :ordinal_day, :scalar_year, :separator_at?, 'time?'], :handle_rmn_od_sy),
- Handler.new([:repeater_month_name, :scalar_day, :separator_at?, 'time?'], :handle_rmn_sd),
- Handler.new([:repeater_time, :repeater_day_portion?, :separator_on?, :repeater_month_name, :scalar_day], :handle_rmn_sd_on),
- Handler.new([:repeater_month_name, :ordinal_day, :separator_at?, 'time?'], :handle_rmn_od),
- Handler.new([:repeater_time, :repeater_day_portion?, :separator_on?, :repeater_month_name, :ordinal_day], :handle_rmn_od_on),
- Handler.new([:repeater_month_name, :scalar_year], :handle_rmn_sy),
- Handler.new([:scalar_day, :repeater_month_name, :scalar_year, :separator_at?, 'time?'], :handle_sd_rmn_sy),
- @middle_endian_handler,
- @little_endian_handler,
- Handler.new([:scalar_year, :separator_slash_or_dash, :scalar_month, :separator_slash_or_dash, :scalar_day, :separator_at?, 'time?'], :handle_sy_sm_sd),
- Handler.new([:scalar_month, :separator_slash_or_dash, :scalar_year], :handle_sm_sy)
- ],
-
- # tonight at 7pm
- :anchor => [
- Handler.new([:grabber?, :repeater, :separator_at?, :repeater?, :repeater?], :handle_r),
- Handler.new([:grabber?, :repeater, :repeater, :separator_at?, :repeater?, :repeater?], :handle_r),
- Handler.new([:repeater, :grabber, :repeater], :handle_r_g_r)
- ],
-
- # 3 weeks from now, in 2 months
- :arrow => [
- Handler.new([:scalar, :repeater, :pointer], :handle_s_r_p),
- Handler.new([:pointer, :scalar, :repeater], :handle_p_s_r),
- Handler.new([:scalar, :repeater, :pointer, 'anchor'], :handle_s_r_p_a)
- ],
-
- # 3rd week in march
- :narrow => [
- Handler.new([:ordinal, :repeater, :separator_in, :repeater], :handle_o_r_s_r),
- Handler.new([:ordinal, :repeater, :grabber, :repeater], :handle_o_r_g_r)
- ]
- }
-
- apply_endian_precedences(options[:endian_precedence])
-
- @definitions
- end
-
- def tokens_to_span(tokens, options) #:nodoc:
- # maybe it's a specific date
-
- definitions = self.definitions(options)
- definitions[:date].each do |handler|
- if handler.match(tokens, definitions)
- puts "-date" if Chronic.debug
- good_tokens = tokens.select { |o| !o.get_tag Separator }
- return self.send(handler.handler_method, good_tokens, options)
- end
- end
-
- # I guess it's not a specific date, maybe it's just an anchor
-
- definitions[:anchor].each do |handler|
- if handler.match(tokens, definitions)
- puts "-anchor" if Chronic.debug
- good_tokens = tokens.select { |o| !o.get_tag Separator }
- return self.send(handler.handler_method, good_tokens, options)
- end
- end
-
- # not an anchor, perhaps it's an arrow
-
- definitions[:arrow].each do |handler|
- if handler.match(tokens, definitions)
- puts "-arrow" if Chronic.debug
- good_tokens = tokens.reject { |o| o.get_tag(SeparatorAt) || o.get_tag(SeparatorSlashOrDash) || o.get_tag(SeparatorComma) }
- return self.send(handler.handler_method, good_tokens, options)
- end
- end
-
- # not an arrow, let's hope it's a narrow
-
- definitions[:narrow].each do |handler|
- if handler.match(tokens, definitions)
- puts "-narrow" if Chronic.debug
- #good_tokens = tokens.select { |o| !o.get_tag Separator }
- return self.send(handler.handler_method, tokens, options)
- end
- end
-
- # I guess you're out of luck!
- puts "-none" if Chronic.debug
- return nil
- end
-
- #--------------
-
- def apply_endian_precedences(precedences)
- date_defs = @definitions[:date]
-
- # map the precedence array to indices on @definitions[:date]
- indices = precedences.map { |e|
- handler = instance_variable_get(endian_variable_name_for(e))
- date_defs.index(handler)
- }
-
- # swap the handlers if we discover they are at odds with the desired preferences
- swap(date_defs, indices.first, indices.last) if indices.first > indices.last
- end
-
- def endian_variable_name_for(e)
- "@#{e.to_s}_endian_handler".to_sym
- end
-
- # exchange two elements in an array
- def swap(arr, a, b); arr[a], arr[b] = arr[b], arr[a]; end
-
- def day_or_time(day_start, time_tokens, options)
- outer_span = Span.new(day_start, day_start + (24 * 60 * 60))
-
- if !time_tokens.empty?
- @now = outer_span.begin
- get_anchor(dealias_and_disambiguate_times(time_tokens, options), options)
- else
- outer_span
- end
- end
-
- #--------------
-
+ # Handle month/day
def handle_m_d(month, day, time_tokens, options) #:nodoc:
- month.start = @now
+ month.start = Chronic.now
span = month.this(options[:context])
day_start = Chronic.time_class.local(span.begin.year, span.begin.month, day)
day_or_time(day_start, time_tokens, options)
end
+ # Handle repeater-month-name/scalar-day
def handle_rmn_sd(tokens, options) #:nodoc:
handle_m_d(tokens[0].get_tag(RepeaterMonthName), tokens[1].get_tag(ScalarDay).type, tokens[2..tokens.size], options)
end
+ # Handle repeater-month-name/scalar-day with separator-on
def handle_rmn_sd_on(tokens, options) #:nodoc:
if tokens.size > 3
handle_m_d(tokens[2].get_tag(RepeaterMonthName), tokens[3].get_tag(ScalarDay).type, tokens[0..1], options)
else
handle_m_d(tokens[1].get_tag(RepeaterMonthName), tokens[2].get_tag(ScalarDay).type, tokens[0..0], options)
end
end
+ # Handle repeater-month-name/ordinal-day
def handle_rmn_od(tokens, options) #:nodoc:
handle_m_d(tokens[0].get_tag(RepeaterMonthName), tokens[1].get_tag(OrdinalDay).type, tokens[2..tokens.size], options)
end
+ # Handle repeater-month-name/ordinal-day with separator-on
def handle_rmn_od_on(tokens, options) #:nodoc:
if tokens.size > 3
handle_m_d(tokens[2].get_tag(RepeaterMonthName), tokens[3].get_tag(OrdinalDay).type, tokens[0..1], options)
else
handle_m_d(tokens[1].get_tag(RepeaterMonthName), tokens[2].get_tag(OrdinalDay).type, tokens[0..0], options)
end
end
+ # Handle repeater-month-name/scalar-year
def handle_rmn_sy(tokens, options) #:nodoc:
month = tokens[0].get_tag(RepeaterMonthName).index
year = tokens[1].get_tag(ScalarYear).type
if month == 12
@@ -199,15 +58,17 @@
rescue ArgumentError
nil
end
end
+ # Handle generic timestamp
def handle_rdn_rmn_sd_t_tz_sy(tokens, options) #:nodoc:
- t = Chronic.time_class.parse(@text)
+ t = Chronic.time_class.parse(options[:text])
Span.new(t, t + 1)
end
+ # Handle repeater-month-name/scalar-day/scalar-year
def handle_rmn_sd_sy(tokens, options) #:nodoc:
month = tokens[0].get_tag(RepeaterMonthName).index
day = tokens[1].get_tag(ScalarDay).type
year = tokens[2].get_tag(ScalarYear).type
@@ -219,10 +80,11 @@
rescue ArgumentError
nil
end
end
+ # Handle repeater-month-name/ordinal-day/scalar-year
def handle_rmn_od_sy(tokens, options) #:nodoc:
month = tokens[0].get_tag(RepeaterMonthName).index
day = tokens[1].get_tag(OrdinalDay).type
year = tokens[2].get_tag(ScalarYear).type
@@ -234,16 +96,18 @@
rescue ArgumentError
nil
end
end
+ # Handle scalar-day/repeater-month-name/scalar-year
def handle_sd_rmn_sy(tokens, options) #:nodoc:
new_tokens = [tokens[1], tokens[0], tokens[2]]
time_tokens = tokens.last(tokens.size - 3)
self.handle_rmn_sd_sy(new_tokens + time_tokens, options)
end
+ # Handle scalar-month/scalar-day/scalar-year (endian middle)
def handle_sm_sd_sy(tokens, options) #:nodoc:
month = tokens[0].get_tag(ScalarMonth).type
day = tokens[1].get_tag(ScalarDay).type
year = tokens[2].get_tag(ScalarYear).type
@@ -255,22 +119,25 @@
rescue ArgumentError
nil
end
end
+ # Handle scalar-day/scalar-month/scalar-year (endian little)
def handle_sd_sm_sy(tokens, options) #:nodoc:
new_tokens = [tokens[1], tokens[0], tokens[2]]
time_tokens = tokens.last(tokens.size - 3)
self.handle_sm_sd_sy(new_tokens + time_tokens, options)
end
+ # Handle scalar-year/scalar-month/scalar-day
def handle_sy_sm_sd(tokens, options) #:nodoc:
new_tokens = [tokens[1], tokens[2], tokens[0]]
time_tokens = tokens.last(tokens.size - 3)
self.handle_sm_sd_sy(new_tokens + time_tokens, options)
end
+ # Handle scalar-month/scalar-year
def handle_sm_sy(tokens, options) #:nodoc:
month = tokens[0].get_tag(ScalarMonth).type
year = tokens[1].get_tag(ScalarYear).type
if month == 12
@@ -288,62 +155,56 @@
end
end
# anchors
+ # Handle repeaters
def handle_r(tokens, options) #:nodoc:
dd_tokens = dealias_and_disambiguate_times(tokens, options)
self.get_anchor(dd_tokens, options)
end
+ # Handle repeater/grabber/repeater
def handle_r_g_r(tokens, options) #:nodoc:
new_tokens = [tokens[1], tokens[0], tokens[2]]
self.handle_r(new_tokens, options)
end
# arrows
+ # Handle scalar/repeater/pointer helper
def handle_srp(tokens, span, options) #:nodoc:
distance = tokens[0].get_tag(Scalar).type
repeater = tokens[1].get_tag(Repeater)
pointer = tokens[2].get_tag(Pointer).type
repeater.offset(span, distance, pointer)
end
+ # Handle scalar/repeater/pointer
def handle_s_r_p(tokens, options) #:nodoc:
repeater = tokens[1].get_tag(Repeater)
+ span = Span.new(Chronic.now, Chronic.now + 1)
- # span =
- # case true
- # when [RepeaterYear, RepeaterSeason, RepeaterSeasonName, RepeaterMonth, RepeaterMonthName, RepeaterFortnight, RepeaterWeek].include?(repeater.class)
- # self.parse("this hour", :guess => false, :now => @now)
- # when [RepeaterWeekend, RepeaterDay, RepeaterDayName, RepeaterDayPortion, RepeaterHour].include?(repeater.class)
- # self.parse("this minute", :guess => false, :now => @now)
- # when [RepeaterMinute, RepeaterSecond].include?(repeater.class)
- # self.parse("this second", :guess => false, :now => @now)
- # else
- # raise(ChronicPain, "Invalid repeater: #{repeater.class}")
- # end
-
- span = Span.new(@now, @now + 1)
-
self.handle_srp(tokens, span, options)
end
+ # Handle pointer/scalar/repeater
def handle_p_s_r(tokens, options) #:nodoc:
new_tokens = [tokens[1], tokens[2], tokens[0]]
self.handle_s_r_p(new_tokens, options)
end
+ # Handle scalar/repeater/pointer/anchor
def handle_s_r_p_a(tokens, options) #:nodoc:
anchor_span = get_anchor(tokens[3..tokens.size - 1], options)
self.handle_srp(tokens, anchor_span, options)
end
# narrows
+ # Handle oridinal repeaters
def handle_orr(tokens, outer_span, options) #:nodoc:
repeater = tokens[1].get_tag(Repeater)
repeater.start = outer_span.begin - 1
ordinal = tokens[0].get_tag(Ordinal).type
span = nil
@@ -355,22 +216,35 @@
end
end
span
end
+ # Handle ordinal/repeater/separator/repeater
def handle_o_r_s_r(tokens, options) #:nodoc:
outer_span = get_anchor([tokens[3]], options)
handle_orr(tokens[0..1], outer_span, options)
end
+ # Handle ordinal/repeater/grabber/repeater
def handle_o_r_g_r(tokens, options) #:nodoc:
outer_span = get_anchor(tokens[2..3], options)
handle_orr(tokens[0..1], outer_span, options)
end
# support methods
+ def day_or_time(day_start, time_tokens, options)
+ outer_span = Span.new(day_start, day_start + (24 * 60 * 60))
+
+ if !time_tokens.empty?
+ Chronic.now = outer_span.begin
+ get_anchor(dealias_and_disambiguate_times(time_tokens, options), options)
+ else
+ outer_span
+ end
+ end
+
def get_anchor(tokens, options) #:nodoc:
grabber = Grabber.new(:this)
pointer = :future
repeaters = self.get_repeaters(tokens)
@@ -380,11 +254,11 @@
grabber = tokens.first.get_tag(Grabber)
tokens.pop
end
head = repeaters.shift
- head.start = @now
+ head.start = Chronic.now
case grabber.type
when :last
outer_span = head.next(:past)
when :this
@@ -402,12 +276,12 @@
find_within(repeaters, outer_span, pointer)
end
def get_repeaters(tokens) #:nodoc:
repeaters = []
- tokens.each do |token|
- if t = token.get_tag(Repeater)
+ tokens.each do |token|
+ if t = token.get_tag(Repeater)
repeaters << t
end
end
repeaters.sort.reverse
end
@@ -462,25 +336,10 @@
t1.untag(RepeaterDayPortion)
t1.tag(RepeaterDayPortion.new(:pm))
end
end
- # tokens.each_with_index do |t0, i|
- # t1 = tokens[i + 1]
- # if t1 && (t1tag = t1.get_tag(RepeaterDayPortion)) && t0.get_tag(RepeaterTime)
- # if [:morning].include?(t1tag.type)
- # puts '--morning->am' if Chronic.debug
- # t1.untag(RepeaterDayPortion)
- # t1.tag(RepeaterDayPortion.new(:am))
- # elsif [:afternoon, :evening, :night].include?(t1tag.type)
- # puts "--#{t1tag.type}->pm" if Chronic.debug
- # t1.untag(RepeaterDayPortion)
- # t1.tag(RepeaterDayPortion.new(:pm))
- # end
- # end
- # end
-
# handle ambiguous times if :ambiguous_time_range is specified
if options[:ambiguous_time_range] != :none
ttokens = []
tokens.each_with_index do |t0, i|
ttokens << t0
@@ -506,18 +365,17 @@
@pattern = pattern
@handler_method = handler_method
end
def constantize(name)
- camel = name.to_s.gsub(/(^|_)(.)/) { $2.upcase }
- ::Chronic.module_eval(camel, __FILE__, __LINE__)
+ Chronic.const_get name.to_s.gsub(/(^|_)(.)/) { $2.upcase }
end
def match(tokens, definitions)
token_index = 0
@pattern.each do |element|
name = element.to_s
- optional = name.reverse[0..0] == '?'
+ optional = name[-1, 1] == '?'
name = name.chop if optional
if element.instance_of? Symbol
klass = constantize(name)
match = tokens[token_index] && !tokens[token_index].tags.select { |o| o.kind_of?(klass) }.empty?
return false if !match && !optional