module AccomParse
Parse !accom command tokens into check-in, nights, and location.
Constants
- MAX_NIGHTS
- MIN_NIGHTS
- USAGE_MESSAGE
- WEEKDAYS
Public Instance Methods
Source
# File lib/accom_parse.rb, line 60 def extract_when_tokens(tokens, today:) return [nil, tokens] if tokens.empty? (1..[3, tokens.length].min).each do |size| chunk = tokens.first(size) joined = chunk.join(" ") next unless resolve_check_in(joined, today: today) return [chunk, tokens[size..] || []] end [nil, tokens] end
Source
# File lib/accom_parse.rb, line 127 def month_number(name) months = { "jan" => 1, "january" => 1, "feb" => 2, "february" => 2, "mar" => 3, "march" => 3, "apr" => 4, "april" => 4, "may" => 5, "jun" => 6, "june" => 6, "jul" => 7, "july" => 7, "aug" => 8, "august" => 8, "sep" => 9, "sept" => 9, "september" => 9, "oct" => 10, "october" => 10, "nov" => 11, "november" => 11, "dec" => 12, "december" => 12 } months[name.downcase] end
Source
# File lib/accom_parse.rb, line 94 def next_weekday(target_wday, today) days_ahead = (target_wday - today.wday) % 7 today + days_ahead end
Source
# File lib/accom_parse.rb, line 186 def nights_error(nights) return "Nights must be at least #{MIN_NIGHTS}." if nights.to_i < MIN_NIGHTS "Nights cannot exceed #{MAX_NIGHTS}." end
Source
# File lib/accom_parse.rb, line 145 def normalize_year(value) return value if value >= 100 2000 + value end
Source
# File lib/accom_parse.rb, line 99 def parse_calendar_date(text, today:) today = today_date(today: today) if text.match?(/\A\d{4}-\d{2}-\d{2}\z/) return Date.parse(text) end if (match = text.match(/\A(\d{1,2})[\/\-](\d{1,2})(?:[\/\-](\d{2,4}))?\z/)) day = match[1].to_i month = match[2].to_i year = match[3] ? normalize_year(match[3].to_i) : today.year return Date.new(year, month, day) end if (match = text.match(/\A(\d{1,2})\s+([A-Za-z]{3,9})(?:\s+(\d{4}))?\z/)) day = match[1].to_i month = month_number(match[2]) return nil unless month year = match[3] ? match[3].to_i : today.year return Date.new(year, month, day) end nil rescue Date::Error nil end
Source
# File lib/accom_parse.rb, line 33 def parse_command(text, today: nil) raw = text.to_s.strip return { error: usage_message } if raw.empty? tokens = tokenize(raw) return { error: usage_message } if tokens.empty? today = today_date(today: today) when_tokens, remainder = extract_when_tokens(tokens, today: today) nights = 1 location_tokens = remainder.dup if location_tokens.first.to_s.match?(/\A\d+\z/) nights = location_tokens.shift.to_i end return { error: nights_error(nights) } unless valid_nights?(nights) location = location_tokens.join(" ").strip location = unwrap_quotes(location) return { error: usage_message } if location.empty? when_token = when_tokens&.join(" ") stay = resolve_stay(when_token, nights, today: today) stay.merge(location: location) end
Source
# File lib/accom_parse.rb, line 83 def resolve_check_in(token, today: nil) today = today_date(today: today) text = token.to_s.strip return nil if text.empty? weekday = WEEKDAYS[text.downcase] return next_weekday(weekday, today) unless weekday.nil? parse_calendar_date(text, today: today) end
Source
# File lib/accom_parse.rb, line 74 def resolve_stay(when_token, nights, today: nil) today = today_date(today: today) check_in = when_token ? resolve_check_in(when_token, today: today) : today nights = nights.to_i check_out = check_in + nights { check_in: check_in, check_out: check_out, nights: nights } end
Source
# File lib/accom_parse.rb, line 29 def today_date(today: nil) today || HolidayCalendar.local_date end
Source
# File lib/accom_parse.rb, line 151 def tokenize(text) tokens = [] current = +"" in_quote = false text.each_char do |char| if char == '"' in_quote = !in_quote next end if char.match?(/\s/) && !in_quote tokens << current unless current.empty? current = +"" next end current << char end tokens << current unless current.empty? tokens end
Source
# File lib/accom_parse.rb, line 175 def unwrap_quotes(text) value = text.to_s.strip return value[1..-2].strip if value.start_with?('"') && value.end_with?('"') value end
Source
# File lib/accom_parse.rb, line 182 def valid_nights?(nights) nights.is_a?(Integer) && (MIN_NIGHTS..MAX_NIGHTS).cover?(nights) end