Support full state/province names in search queries

Stephen Paul Weber created

Instead of broadening the patterns, this relies on realizing that almost all
query formats either won't match when a state name is present, or will work when
that name is replaced by the code.  So just preprocess the query string to
replace state names with their code before pattern matching.

Special case for vanity pattern ~ which can really be anything at all so don't
process that.

Change summary

Gemfile                     |  1 +
lib/tel_selections.rb       | 14 ++++++++++++++
test/test_tel_selections.rb | 37 +++++++++++++++++++++++++++++++++++++
3 files changed, 52 insertions(+)

Detailed changes

Gemfile 🔗

@@ -6,6 +6,7 @@ gem "amazing_print"
 gem "bandwidth-sdk", "<= 6.1.0"
 gem "blather", git: "https://github.com/psycotica0/blather", branch: "cv_new_id"
 gem "braintree"
+gem "countries"
 gem "dhall", ">= 0.5.3.fixed"
 gem "em-hiredis"
 gem "em-http-request", git: "https://github.com/singpolyma/em-http-request", branch: "fix-letsencrypt"

lib/tel_selections.rb 🔗

@@ -4,6 +4,7 @@ require "ruby-bandwidth-iris"
 Faraday.default_adapter = :em_synchrony
 
 require "cbor"
+require "countries"
 
 require_relative "area_code_repo"
 require_relative "form_template"
@@ -213,6 +214,8 @@ class TelSelections
 			end
 
 			def self.for(q, **kwa)
+				q = replace_region_names(q) unless q.start_with?("~")
+
 				@queries.each do |(regex, block)|
 					match_data = (q =~ regex)
 					return block.call($1 || $&, *$~.to_a[2..-1], **kwa) if match_data
@@ -221,6 +224,17 @@ class TelSelections
 				raise "Format not recognized: #{q}"
 			end
 
+			def self.replace_region_names(query)
+				ISO3166::Country[:US].subdivisions.merge(
+					ISO3166::Country[:CA].subdivisions
+				).reduce(query) do |q, (code, region)|
+					([region.name] + Array(region.unofficial_names))
+						.reduce(q) do |r, name|
+							r.sub(/#{name}\s*(?!,)/i, code)
+						end
+				end
+			end
+
 			def initialize(q)
 				@q = q
 			end

test/test_tel_selections.rb 🔗

@@ -193,6 +193,34 @@ class TelSelectionsTest < Minitest::Test
 			assert_equal({ state: "ON" }, q.iris_query)
 		end
 
+		def test_for_state_name
+			q = TelSelections::ChooseTel::Q.for("ontario")
+			assert_equal({ state: "ON" }, q.iris_query)
+		end
+
+		def test_for_new_york
+			q = TelSelections::ChooseTel::Q.for("New York")
+			assert_equal({ state: "NY" }, q.iris_query)
+		end
+
+		def test_for_new_york_ny
+			q = TelSelections::ChooseTel::Q.for(
+				"New York, NY",
+				db: FakeDB.new,
+				memcache: FakeMemcache.new
+			)
+			assert_equal({ city: "New York City", state: "NY" }, q.iris_query)
+		end
+
+		def test_for_new_york_new_york
+			q = TelSelections::ChooseTel::Q.for(
+				"New York, New York",
+				db: FakeDB.new,
+				memcache: FakeMemcache.new
+			)
+			assert_equal({ city: "New York City", state: "NY" }, q.iris_query)
+		end
+
 		def test_for_citystate
 			q = TelSelections::ChooseTel::Q.for(
 				"Toronto, ON",
@@ -202,6 +230,15 @@ class TelSelectionsTest < Minitest::Test
 			assert_equal({ city: "Toronto", state: "ON" }, q.iris_query)
 		end
 
+		def test_for_citystate_name
+			q = TelSelections::ChooseTel::Q.for(
+				"Toronto, Ontario",
+				db: FakeDB.new,
+				memcache: FakeMemcache.new
+			)
+			assert_equal({ city: "Toronto", state: "ON" }, q.iris_query)
+		end
+
 		def test_for_garbage
 			assert_raises { TelSelections::ChooseTel::Q.for("garbage") }
 		end