module ClockFlipMeaning
ClockFlipMeaning — resolve upside-down clock flip meanings with cache and fallbacks. Public: lookup, association_lines, lookup_with_hint, stale?, fresh_entry, sibling_minute Depends: ClockFlip, ClockFlipStore, ClockFlipRank, ClockFlipPick, ClockLookupThrottle, ClockWordnet, UrbanLookup, RabbotDb Tests: test/lib/clock_flip_meaning_test.rb
Constants
- CACHE_TTL_SEC
- PLUGIN
Public Instance Methods
Source
# File lib/clock/flip_meaning.rb, line 174 def association_lines(time_label:, hour:, minute:, **options) lookup(time_label: time_label, hour: hour, minute: minute, **options)[:lines] end
Source
# File lib/clock/flip_meaning.rb, line 356 def default_wiki_lookup lambda do |flip_word| ClockWikiLookup.lookup(flip_word, fetch: ->(url) { RabbotHttp.fetch(url) }) end end
Source
# File lib/clock/flip_meaning.rb, line 349 def default_wordnet_lookup lambda do |flip_word| require_relative "../../plugins/dictionary" ClockWordnet.association_lines(flip_word, lookup: Dictionary.default_run) end end
Source
# File lib/clock/flip_meaning.rb, line 205 def display_result(entry, hour:, minute:, store:, exclude:, rng:, hint:, pick_from: nil, spread_count: 1) pool = Array(pick_from || entry["lines"]) if spread_count.to_i > 1 spread_lines = ClockMeaningSpread.spread(pool, lines: spread_count, exclude: exclude, rng: rng) if spread_lines.any? ClockFlipStore.record_last_shown!( hour: hour, minute: minute, line: spread_lines.last, store: store ) end return { lines: spread_lines, hint: hint, flip_word: entry["flip_word"] } end picked = ClockFlipPick.pick_line(lines: pool, exclude: exclude, rng: rng) if picked ClockFlipStore.record_last_shown!(hour: hour, minute: minute, line: picked, store: store) end display_line = picked ? ClockFlipPick.expand_numbered_senses(picked, pool) : nil { lines: display_line ? [display_line] : [], hint: hint, flip_word: entry["flip_word"] } end
Source
# File lib/clock/flip_meaning.rb, line 337 def expand_winner_lines(winner, urban_fetch:) base_lines = Array(winner[:lines]) return base_lines unless winner[:source].to_s == "urban" expanded = UrbanLookup.unique_lines(winner[:flip_word], fetch: urban_fetch) expanded.empty? ? base_lines : expanded end
Source
# File lib/clock/flip_meaning.rb, line 240 def fresh_entry(hour:, minute:, store:, now:) entry = ClockFlipStore.load_entry(hour: hour, minute: minute, store: store) return nil unless entry return entry if ClockFlipStore.manual_entry?(entry) return entry unless stale?(entry, now: now) nil end
Source
# File lib/clock/flip_meaning.rb, line 38 def hint_for(time_label:, flip_word:, candidates: nil) hint = "add meaning: !time #{time_label} #{flip_word} …" options = Array(candidates).map { |word| word.to_s.strip.downcase }.reject(&:empty?).uniq return hint if options.length <= 1 "#{hint} (valid: #{options.join(', ')})" end
Source
# File lib/clock/flip_meaning.rb, line 46 def lookup( time_label:, hour:, minute:, flip_word: nil, store: RabbotDb, now: Time.now, wordnet_lookup: nil, urban_lookup: nil, wiki_lookup: nil, urban_fetch: nil, throttle: ClockLookupThrottle, rng: Random.new, spread_count: 1 ) wordnet_lookup ||= default_wordnet_lookup urban_lookup ||= ->(term) { UrbanLookup.best_result(term) } wiki_lookup ||= default_wiki_lookup urban_fetch ||= ->(url) { RabbotHttp.fetch_json(url) } candidates = ClockFlip.upside_down_candidates(hour, minute) primary = ClockFlip.upside_down_word(hour, minute) display_flip_word = flip_word.to_s.strip.downcase display_flip_word = primary if display_flip_word.empty? exclude = Array(ClockFlipPick.sibling_last_shown(hour: hour, minute: minute, store: store)).compact cached = fresh_entry(hour: hour, minute: minute, store: store, now: now) stale_entry = nil unless cached maybe_stale = ClockFlipStore.load_entry(hour: hour, minute: minute, store: store) stale_entry = maybe_stale if maybe_stale && stale?(maybe_stale, now: now) end if cached if ClockFlipStore.manual_entry?(cached) return display_result( cached, hour: hour, minute: minute, store: store, exclude: exclude, rng: rng, hint: nil, pick_from: cached["lines"], spread_count: spread_count ) end return resolve_cached_with_alternates( cached: cached, candidates: candidates, hour: hour, minute: minute, store: store, now: now, wordnet_lookup: wordnet_lookup, urban_lookup: urban_lookup, wiki_lookup: wiki_lookup, urban_fetch: urban_fetch, throttle: throttle, copy_to_current: false, exclude: exclude, rng: rng, spread_count: spread_count ) end sibling = sibling_minute(hour, minute) if sibling sibling_hour, sibling_minute = sibling sibling_cached = fresh_entry(hour: sibling_hour, minute: sibling_minute, store: store, now: now) if sibling_cached && candidates.include?(sibling_cached["flip_word"]) return resolve_cached_with_alternates( cached: sibling_cached, candidates: candidates, hour: hour, minute: minute, store: store, now: now, wordnet_lookup: wordnet_lookup, urban_lookup: urban_lookup, wiki_lookup: wiki_lookup, urban_fetch: urban_fetch, throttle: throttle, copy_to_current: true, exclude: exclude, rng: rng, spread_count: spread_count ) end end winner = ClockFlipRank.pick_best( candidates: candidates, wordnet_lookup: wordnet_lookup, urban_lookup: urban_lookup, wiki_lookup: wiki_lookup, throttle: throttle, store: store, now: now ) if winner lines = expand_winner_lines(winner, urban_fetch: urban_fetch) stored_lines = stale_entry ? merge_cached_lines(stale_entry, lines) : lines return persist_and_result( hour: hour, minute: minute, flip_word: winner[:flip_word], lines: stored_lines, source: winner[:source], store: store, now: now, hint: nil, exclude: exclude, rng: rng, pick_from: lines, spread_count: spread_count ) end { lines: [], hint: hint_for(time_label: time_label, flip_word: primary, candidates: candidates), flip_word: primary } end
Source
# File lib/clock/flip_meaning.rb, line 178 def lookup_with_hint(time_label:, hour:, minute:, **options) lookup(time_label: time_label, hour: hour, minute: minute, **options) end
Source
# File lib/clock/flip_meaning.rb, line 345 def merge_cached_lines(cached, incoming) ClockFlipStore.merge_unique_lines(cached["lines"], incoming) end
Source
# File lib/clock/flip_meaning.rb, line 182 def persist_and_result(hour:, minute:, flip_word:, lines:, source:, store:, now:, hint:, exclude: [], rng: Random.new, pick_from: nil, spread_count: 1) entry = ClockFlipStore.save_entry( hour: hour, minute: minute, flip_word: flip_word, lines: lines, source: source, store: store, now: now ) display_result( entry, hour: hour, minute: minute, store: store, exclude: exclude, rng: rng, hint: hint, pick_from: pick_from, spread_count: spread_count ) end
Source
# File lib/clock/flip_meaning.rb, line 256 def resolve_cached_with_alternates( cached:, candidates:, hour:, minute:, store:, now:, wordnet_lookup:, urban_lookup:, wiki_lookup:, urban_fetch:, throttle:, copy_to_current:, exclude:, rng:, spread_count: 1 ) cached_word = cached["flip_word"] alternates = candidates.reject { |candidate| candidate == cached_word } alternate_winner = if alternates.empty? nil else ClockFlipRank.pick_best( candidates: alternates, wordnet_lookup: wordnet_lookup, urban_lookup: urban_lookup, wiki_lookup: wiki_lookup, throttle: throttle, store: store, now: now ) end if alternate_winner && ClockFlipRank.beats_cached?(alternate_winner, cached) lines = expand_winner_lines(alternate_winner, urban_fetch: urban_fetch) return persist_and_result( hour: hour, minute: minute, flip_word: alternate_winner[:flip_word], lines: merge_cached_lines(cached, lines), source: alternate_winner[:source], store: store, now: now, hint: nil, exclude: exclude, rng: rng, pick_from: lines, spread_count: spread_count ) end if copy_to_current return persist_and_result( hour: hour, minute: minute, flip_word: cached_word, lines: cached["lines"], source: cached["source"], store: store, now: now, hint: nil, exclude: exclude, rng: rng, pick_from: cached["lines"], spread_count: spread_count ) end display_result( cached, hour: hour, minute: minute, store: store, exclude: exclude, rng: rng, hint: nil, pick_from: cached["lines"], spread_count: spread_count ) end
Source
# File lib/clock/flip_meaning.rb, line 249 def sibling_minute(hour, minute) case minute % 10 when 8 then [hour, minute + 1] when 9 then [hour, minute - 1] end end
Source
# File lib/clock/flip_meaning.rb, line 26 def stale?(entry, now: Time.now, ttl_sec: CACHE_TTL_SEC) return true if entry.nil? return false if ClockFlipStore.manual_entry?(entry) fetched_at = RabbotDb.parse_time(entry["cached_at"]) return true unless fetched_at (now - fetched_at) > ttl_sec rescue ArgumentError, TypeError true end