Pass through customer, redis, allow promise in Q.for

Stephen Paul Weber created

Change summary

lib/registration.rb         |   6 
lib/tel_selections.rb       |  65 +++++++++++--------
test/test_helper.rb         |  11 ++
test/test_registration.rb   |   4 
test/test_tel_selections.rb | 131 +++++++++++++++++++++++++-------------
5 files changed, 137 insertions(+), 80 deletions(-)

Detailed changes

lib/registration.rb 🔗

@@ -128,7 +128,7 @@ class Registration
 		if (reg = customer.registered?)
 			Registered.for(customer, reg.phone)
 		else
-			tel_selections[customer.jid].then(&:choose_tel_or_data).then do |product|
+			tel_selections[customer].then(&:choose_tel_or_data).then do |product|
 				reserve_and_continue(tel_selections, customer, product).then do
 					RegistrationType.for(customer, google_play_userid, product)
 				end
@@ -142,7 +142,7 @@ class Registration
 	def self.reserve_and_continue(tel_selections, customer, product)
 		product.reserve(customer).catch do
 			tel_selections.delete(customer.jid).then {
-				tel_selections[customer.jid]
+				tel_selections[customer]
 			}.then { |choose|
 				choose.choose_tel_or_data(
 					error: "The JMP number #{tel} is no longer available."
@@ -752,7 +752,7 @@ class Registration
 		def number_purchase_error(e)
 			Command.log.error "number_purchase_error", e
 			TEL_SELECTIONS.delete(@customer.jid)
-				.then { TEL_SELECTIONS[@customer.jid] }
+				.then { TEL_SELECTIONS[@customer] }
 				.then { |c|
 					c.choose_tel_or_data(
 						error: TN_UNAVAILABLE % @tel,

lib/tel_selections.rb 🔗

@@ -46,9 +46,11 @@ class TelSelections
 		@redis.del("pending_tel_for-#{jid}")
 	end
 
-	def [](jid)
-		@redis.get("pending_tel_for-#{jid}").then do |tel|
-			tel ? HaveTel.new(tel) : ChooseTel.new(db: @db, memcache: @memcache)
+	def [](customer)
+		@redis.get("pending_tel_for-#{customer.jid}").then do |tel|
+			next HaveTel.new(tel) if tel
+
+			ChooseTel.new(customer, redis: @redis, db: @db, memcache: @memcache)
 		end
 	end
 
@@ -65,8 +67,10 @@ class TelSelections
 	class ChooseTel
 		class Fail < RuntimeError; end
 
-		def initialize(db: DB, memcache: MEMCACHE)
+		def initialize(customer, redis: REDIS, db: DB, memcache: MEMCACHE)
+			@customer = customer
 			@db = db
+			@redis = redis
 			@memcache = memcache
 		end
 
@@ -92,12 +96,15 @@ class TelSelections
 		end
 
 		def choose_tel(iq)
-			available = AvailableNumber.for(iq.form, db: @db, memcache: @memcache)
-			return available if available.is_a?(Tn::Bandwidth)
-
-			choose_from_list(available.tns)
-		rescue Fail
-			choose_tel_or_data(error: $!.to_s)
+			AvailableNumber.for(
+				iq.form, customer: customer, redis: @redis, db: @db, memcache: @memcache
+			).then { |avail|
+				next avail if avail.is_a?(Tn::Bandwidth)
+
+				choose_from_list(avail.tns)
+			}.catch_only(Fail) do
+				choose_tel_or_data(error: $!.to_s)
+			end
 		end
 
 		def choose_from_list(tns)
@@ -119,19 +126,18 @@ class TelSelections
 		end
 
 		class AvailableNumber
-			def self.for(form, db: DB, memcache: MEMCACHE)
+			def self.for(form, **kwargs)
 				qs = form.field("q")&.value.to_s.strip
 				return Tn.for_pending_value(qs) if qs =~ /\A\+1\d{10}\Z/
 
-				quantity = Quantity.for(form)
-				q = Q.for(feelinglucky(qs, form), db: db, memcache: memcache)
-
-				new(
-					q.iris_query
-					.merge(enableTNDetail: true, LCA: false),
-					q.sql_query, quantity,
-					fallback: q.fallback, memcache: memcache, db: db
-				)
+				Q.for(feelinglucky(qs, form), **kwargs).then do |q|
+					new(
+						q.iris_query
+						.merge(enableTNDetail: true, LCA: false),
+						q.sql_query, Quantity.for(form),
+						fallback: q.fallback, **kwargs
+					)
+				end
 			end
 
 			ACTION_FIELD = "http://jabber.org/protocol/commands#actions"
@@ -145,9 +151,9 @@ class TelSelections
 
 			def initialize(
 				iris_query, sql_query, quantity,
-				fallback: [], memcache: MEMCACHE, db: DB
+				fallback: [], memcache: MEMCACHE, db: DB, **
 			)
-				@iris_query = iris_query.merge(quantity.iris_query)
+				@iris_query = iris_query&.merge(quantity.iris_query)
 				@sql_query = sql_query
 				@quantity = quantity
 				@fallback = fallback
@@ -166,6 +172,8 @@ class TelSelections
 			end
 
 			def fetch_bandwidth_inventory
+				return [] unless @iris_query
+
 				BandwidthIris::AvailableNumber
 					.list(@iris_query)
 					.map { |tn| Tn::Bandwidth.new(Tn::Option.new(**tn)) }
@@ -402,12 +410,15 @@ class TelSelections
 			def self.for(q, **kwa)
 				q = replace_region_names(q) unless q.start_with?("~")
 
-				@queries.each do |(regex, block)|
+				EMPromise.all(@queries.map { |(regex, block)|
 					match_data = (q =~ regex)
-					return block.call($1 || $&, *$~.to_a[2..-1], **kwa) if match_data
-				end
+					block.call($1 || $&, *$~.to_a[2..-1], **kwa) if match_data
+				}).then do |qs|
+					qs = qs.compact
+					raise Fail, "Format not recognized: #{q}" if qs.empty?
 
-				raise Fail, "Format not recognized: #{q}"
+					qs.first
+				end
 			end
 
 			def self.replace_region_names(query)
@@ -547,7 +558,7 @@ class TelSelections
 					"west durham" => "Durham"
 				}.freeze
 
-				def initialize(city, state, db: DB, memcache: MEMCACHE)
+				def initialize(city, state, db: DB, memcache: MEMCACHE, **)
 					@city = CITY_MAP.fetch(city.downcase, city)
 					@state = State.new(state)
 					@db = db

test/test_helper.rb 🔗

@@ -264,9 +264,14 @@ class FakeTelSelections
 		EMPromise.resolve("OK")
 	end
 
-	def [](jid)
-		@selections.fetch(jid) do
-			TelSelections::ChooseTel.new(db: FakeDB.new, memcache: FakeMemcache.new)
+	def [](customer)
+		@selections.fetch(customer.jid) do
+			TelSelections::ChooseTel.new(
+				customer,
+				redis: FakeRedis.new,
+				db: FakeDB.new,
+				memcache: FakeMemcache.new
+			)
 		end
 	end
 end

test/test_registration.rb 🔗

@@ -1834,7 +1834,9 @@ class RegistrationTest < Minitest::Test
 			assert_mock Command::COMMAND_MANAGER
 			assert_instance_of(
 				TelSelections::ChooseTel,
-				Registration::Finish::TEL_SELECTIONS["test@example.com"]
+				Registration::Finish::TEL_SELECTIONS[
+					OpenStruct.new(jid: "test@example.com")
+				]
 			)
 			assert_requested create_order
 		end

test/test_tel_selections.rb 🔗

@@ -13,12 +13,13 @@ class TelSelectionsTest < Minitest::Test
 	end
 
 	def test_set_get
-		assert_kind_of TelSelections::ChooseTel, @manager["jid@example.com"].sync
+		cust = OpenStruct.new(jid: "jid@example.com")
+		assert_kind_of TelSelections::ChooseTel, @manager[cust].sync
 		@manager.set(
 			"jid@example.com",
 			TelSelections::ChooseTel::Tn.for_pending_value("+15555550000")
 		).sync
-		assert_kind_of TelSelections::HaveTel, @manager["jid@example.com"].sync
+		assert_kind_of TelSelections::HaveTel, @manager[cust].sync
 	end
 	em :test_set_get
 
@@ -30,7 +31,7 @@ class TelSelectionsTest < Minitest::Test
 		).sync
 		assert_equal(
 			"+15555550000",
-			@manager[jid].then(&:choose_tel_or_data).sync.tel
+			@manager[OpenStruct.new(jid: jid)].then(&:choose_tel_or_data).sync.tel
 		)
 	end
 	em :test_choose_tel_have_tel
@@ -40,13 +41,16 @@ class TelSelectionsTest < Minitest::Test
 			form = Blather::Stanza::X.new
 			form.fields = [{ var: "q", value: "226" }]
 			iris_query = TelSelections::ChooseTel::AvailableNumber
-				.for(form, db: FakeDB.new, memcache: FakeMemcache.new)
-				.instance_variable_get(:@iris_query)
+				.for(
+					form,
+					redis: FakeRedis.new, db: FakeDB.new, memcache: FakeMemcache.new
+				).sync.instance_variable_get(:@iris_query)
 			assert_equal(
 				{ areaCode: "226", enableTNDetail: true, LCA: false, quantity: 10 },
 				iris_query
 			)
 		end
+		em :test_for_no_rsm
 
 		def test_for_rsm
 			form = Blather::Stanza::X.new
@@ -57,14 +61,17 @@ class TelSelectionsTest < Minitest::Test
 				end
 			end
 			iris_query = TelSelections::ChooseTel::AvailableNumber
-				.for(form, db: FakeDB.new, memcache: FakeMemcache.new)
-				.instance_variable_get(:@iris_query)
+				.for(
+					form,
+					redis: FakeRedis.new, db: FakeDB.new, memcache: FakeMemcache.new
+				).sync.instance_variable_get(:@iris_query)
 			# quantity should be 500 due to max inside tel selections
 			assert_equal(
 				{ areaCode: "226", enableTNDetail: true, LCA: false, quantity: 500 },
 				iris_query
 			)
 		end
+		em :test_for_rsm
 
 		def test_for_feelinglucky
 			form = Blather::Stanza::X.new
@@ -76,13 +83,16 @@ class TelSelectionsTest < Minitest::Test
 				}
 			]
 			iris_query = TelSelections::ChooseTel::AvailableNumber
-				.for(form, db: FakeDB.new, memcache: FakeMemcache.new)
-				.instance_variable_get(:@iris_query)
+				.for(
+					form,
+					redis: FakeRedis.new, db: FakeDB.new, memcache: FakeMemcache.new
+				).sync.instance_variable_get(:@iris_query)
 			assert_equal(
 				{ areaCode: "810", enableTNDetail: true, LCA: false, quantity: 10 },
 				iris_query
 			)
 		end
+		em :test_for_feelinglucky
 
 		def test_fallback
 			stub_request(
@@ -123,8 +133,10 @@ class TelSelectionsTest < Minitest::Test
 			form.fields = [{ var: "q", value: "Kitchener, ON" }]
 			tns = execute_command do
 				TelSelections::ChooseTel::AvailableNumber
-					.for(form, db: db, memcache: FakeMemcache.new)
-					.tns
+					.for(
+						form,
+						redis: FakeRedis.new, db: db, memcache: FakeMemcache.new
+					).sync.tns
 			end
 			assert_equal(
 				["(226) 555-12345 (Somewhere, ON)"],
@@ -152,8 +164,10 @@ class TelSelectionsTest < Minitest::Test
 			form.fields = [{ var: "q", value: "Kitchener, ON" }]
 			tns = execute_command do
 				TelSelections::ChooseTel::AvailableNumber
-					.for(form, db: db, memcache: FakeMemcache.new)
-					.tns
+					.for(
+						form,
+						redis: FakeRedis.new, db: db, memcache: FakeMemcache.new
+					).sync.tns
 			end
 			assert_equal(
 				["(226) 555-12345 (Kitchener-Waterloo, ON)"],
@@ -218,12 +232,13 @@ class TelSelectionsTest < Minitest::Test
 
 	class QTest < Minitest::Test
 		def test_for_area_code
-			q = TelSelections::ChooseTel::Q.for("226")
+			q = TelSelections::ChooseTel::Q.for("226").sync
 			assert_equal({ areaCode: "226" }, q.iris_query)
 		end
+		em :test_for_area_code
 
 		def test_for_area_code_sql
-			q = TelSelections::ChooseTel::Q.for("226")
+			q = TelSelections::ChooseTel::Q.for("226").sync
 			assert_equal(
 				[
 					"SELECT * FROM tel_inventory " \
@@ -234,14 +249,16 @@ class TelSelectionsTest < Minitest::Test
 				q.sql_query
 			)
 		end
+		em :test_for_area_code_sql
 
 		def test_for_npanxx
-			q = TelSelections::ChooseTel::Q.for("226666")
+			q = TelSelections::ChooseTel::Q.for("226666").sync
 			assert_equal({ npaNxx: "226666" }, q.iris_query)
 		end
+		em :test_for_npanxx
 
 		def test_for_npanxx_sql
-			q = TelSelections::ChooseTel::Q.for("226666")
+			q = TelSelections::ChooseTel::Q.for("226666").sync
 			assert_equal(
 				[
 					"SELECT * FROM tel_inventory " \
@@ -252,14 +269,16 @@ class TelSelectionsTest < Minitest::Test
 				q.sql_query
 			)
 		end
+		em :test_for_npanxx_sql
 
 		def test_for_npanxxx
-			q = TelSelections::ChooseTel::Q.for("2266667")
+			q = TelSelections::ChooseTel::Q.for("2266667").sync
 			assert_equal({ npaNxxx: "2266667" }, q.iris_query)
 		end
+		em :test_for_npanxxx
 
 		def test_for_npanxxx_sql
-			q = TelSelections::ChooseTel::Q.for("2266667")
+			q = TelSelections::ChooseTel::Q.for("2266667").sync
 			assert_equal(
 				[
 					"SELECT * FROM tel_inventory " \
@@ -270,24 +289,28 @@ class TelSelectionsTest < Minitest::Test
 				q.sql_query
 			)
 		end
+		em :test_for_npanxxx_sql
 
 		def test_for_zip
-			q = TelSelections::ChooseTel::Q.for("90210")
+			q = TelSelections::ChooseTel::Q.for("90210").sync
 			assert_equal({ zip: "90210" }, q.iris_query)
 		end
+		em :test_for_zip
 
 		def test_for_zip_sql
-			q = TelSelections::ChooseTel::Q.for("90210")
+			q = TelSelections::ChooseTel::Q.for("90210").sync
 			refute q.sql_query
 		end
+		em :test_for_zip_sql
 
 		def test_for_localvanity
-			q = TelSelections::ChooseTel::Q.for("~mboa")
+			q = TelSelections::ChooseTel::Q.for("~mboa").sync
 			assert_equal({ localVanity: "mboa" }, q.iris_query)
 		end
+		em :test_for_localvanity
 
 		def test_for_localvanity_sql
-			q = TelSelections::ChooseTel::Q.for("~mboa")
+			q = TelSelections::ChooseTel::Q.for("~mboa").sync
 			assert_equal(
 				[
 					"SELECT * FROM tel_inventory " \
@@ -298,14 +321,16 @@ class TelSelectionsTest < Minitest::Test
 				q.sql_query
 			)
 		end
+		em :test_for_localvanity_sql
 
 		def test_for_state
-			q = TelSelections::ChooseTel::Q.for("ON")
+			q = TelSelections::ChooseTel::Q.for("ON").sync
 			assert_equal({ state: "ON" }, q.iris_query)
 		end
+		em :test_for_state
 
 		def test_for_state_sql
-			q = TelSelections::ChooseTel::Q.for("ON")
+			q = TelSelections::ChooseTel::Q.for("ON").sync
 			assert_equal(
 				[
 					"SELECT * FROM tel_inventory " \
@@ -316,14 +341,16 @@ class TelSelectionsTest < Minitest::Test
 				q.sql_query
 			)
 		end
+		em :test_for_state_sql
 
 		def test_for_state_name
-			q = TelSelections::ChooseTel::Q.for("ontario")
+			q = TelSelections::ChooseTel::Q.for("ontario").sync
 			assert_equal({ state: "ON" }, q.iris_query)
 		end
+		em :test_for_state_name
 
 		def test_for_state_name_sql
-			q = TelSelections::ChooseTel::Q.for("ontario")
+			q = TelSelections::ChooseTel::Q.for("ontario").sync
 			assert_equal(
 				[
 					"SELECT * FROM tel_inventory " \
@@ -334,14 +361,16 @@ class TelSelectionsTest < Minitest::Test
 				q.sql_query
 			)
 		end
+		em :test_for_state_name_sql
 
 		def test_for_new_york
-			q = TelSelections::ChooseTel::Q.for("New York")
+			q = TelSelections::ChooseTel::Q.for("New York").sync
 			assert_equal({ state: "NY" }, q.iris_query)
 		end
+		em :test_for_new_york
 
 		def test_for_new_york_sql
-			q = TelSelections::ChooseTel::Q.for("New York")
+			q = TelSelections::ChooseTel::Q.for("New York").sync
 			assert_equal(
 				[
 					"SELECT * FROM tel_inventory " \
@@ -352,22 +381,24 @@ class TelSelectionsTest < Minitest::Test
 				q.sql_query
 			)
 		end
+		em :test_for_new_york_sql
 
 		def test_for_new_york_ny
 			q = TelSelections::ChooseTel::Q.for(
 				"New York, NY",
-				db: FakeDB.new,
+				redis: FakeRedis.new, db: FakeDB.new,
 				memcache: FakeMemcache.new
-			)
+			).sync
 			assert_equal({ city: "New York City", state: "NY" }, q.iris_query)
 		end
+		em :test_for_new_york_ny
 
 		def test_for_new_york_ny_sql
 			q = TelSelections::ChooseTel::Q.for(
 				"New York, NY",
-				db: FakeDB.new,
+				redis: FakeRedis.new, db: FakeDB.new,
 				memcache: FakeMemcache.new
-			)
+			).sync
 			assert_equal(
 				[
 					"SELECT * FROM tel_inventory " \
@@ -379,22 +410,24 @@ class TelSelectionsTest < Minitest::Test
 				q.sql_query
 			)
 		end
+		em :test_for_new_york_ny_sql
 
 		def test_for_new_york_new_york
 			q = TelSelections::ChooseTel::Q.for(
 				"New York, New York",
-				db: FakeDB.new,
+				redis: FakeRedis.new, db: FakeDB.new,
 				memcache: FakeMemcache.new
-			)
+			).sync
 			assert_equal({ city: "New York City", state: "NY" }, q.iris_query)
 		end
+		em :test_for_new_york_new_york
 
 		def test_for_new_york_new_york_sql
 			q = TelSelections::ChooseTel::Q.for(
 				"New York, New York",
-				db: FakeDB.new,
+				redis: FakeRedis.new, db: FakeDB.new,
 				memcache: FakeMemcache.new
-			)
+			).sync
 			assert_equal(
 				[
 					"SELECT * FROM tel_inventory " \
@@ -406,22 +439,24 @@ class TelSelectionsTest < Minitest::Test
 				q.sql_query
 			)
 		end
+		em :test_for_new_york_new_york_sql
 
 		def test_for_citystate
 			q = TelSelections::ChooseTel::Q.for(
 				"Toronto, ON",
-				db: FakeDB.new,
+				redis: FakeRedis.new, db: FakeDB.new,
 				memcache: FakeMemcache.new
-			)
+			).sync
 			assert_equal({ city: "Toronto", state: "ON" }, q.iris_query)
 		end
+		em :test_for_citystate
 
 		def test_for_citystate_sql
 			q = TelSelections::ChooseTel::Q.for(
 				"Toronto, ON",
-				db: FakeDB.new,
+				redis: FakeRedis.new, db: FakeDB.new,
 				memcache: FakeMemcache.new
-			)
+			).sync
 			assert_equal(
 				[
 					"SELECT * FROM tel_inventory " \
@@ -433,22 +468,24 @@ class TelSelectionsTest < Minitest::Test
 				q.sql_query
 			)
 		end
+		em :test_for_citystate_sql
 
 		def test_for_citystate_name
 			q = TelSelections::ChooseTel::Q.for(
 				"Toronto, Ontario",
-				db: FakeDB.new,
+				redis: FakeRedis.new, db: FakeDB.new,
 				memcache: FakeMemcache.new
-			)
+			).sync
 			assert_equal({ city: "Toronto", state: "ON" }, q.iris_query)
 		end
+		em :test_for_citystate_name
 
 		def test_for_citystate_name_sql
 			q = TelSelections::ChooseTel::Q.for(
 				"Toronto, Ontario",
-				db: FakeDB.new,
+				redis: FakeRedis.new, db: FakeDB.new,
 				memcache: FakeMemcache.new
-			)
+			).sync
 			assert_equal(
 				[
 					"SELECT * FROM tel_inventory " \
@@ -460,9 +497,11 @@ class TelSelectionsTest < Minitest::Test
 				q.sql_query
 			)
 		end
+		em :test_for_citystate_name_sql
 
 		def test_for_garbage
-			assert_raises { TelSelections::ChooseTel::Q.for("garbage") }
+			assert_raises { TelSelections::ChooseTel::Q.for("garbage").sync }
 		end
+		em :test_for_garbage
 	end
 end