Refactor CallAttempt factory to use registration pattern

Stephen Paul Weber created

Change summary

lib/call_attempt.rb      | 73 ++++++++++++++++++++++++++++-------------
lib/call_attempt_repo.rb |  4 +-
2 files changed, 52 insertions(+), 25 deletions(-)

Detailed changes

lib/call_attempt.rb 🔗

@@ -6,33 +6,32 @@ require_relative "tts_template"
 require_relative "low_balance"
 
 class CallAttempt
-	def self.for(customer, rate, usage, supported, **kwargs)
+	def self.for(customer:, usage:, **kwargs)
 		credit = [customer.minute_limit.to_d - usage, 0].max + customer.balance
-		if !supported
-			Unsupported.new(**kwargs.slice(:direction))
-		elsif credit < rate * 10
-			NoBalance.for(customer, rate, usage, supported, **kwargs)
-		else
-			for_ask_or_go(customer, rate, usage, credit, **kwargs)
+		@kinds.each do |kind|
+			ca = kind.call(
+				customer: customer, usage: usage, credit: credit,
+				**kwargs.merge(limits(customer, usage, credit, **kwargs))
+			)
+			return ca if ca
 		end
+
+		raise "No CallAttempt matched"
 	end
 
-	def self.for_ask_or_go(customer, rate, usage, credit, digits: nil, **kwargs)
-		kwargs.merge!(
-			customer_id: customer.customer_id,
-			limit_remaining: limit_remaining(customer, usage, rate),
+	def self.limits(customer, usage, credit, rate:, **)
+		return {} unless customer && usage && rate
+
+		can_use = customer.minute_limit.to_d + customer.monthly_overage_limit
+		{
+			limit_remaining: ([can_use - usage, 0].max / rate).to_i,
 			max_minutes: (credit / rate).to_i
-		)
-		if digits != "1" && limit_remaining(customer, usage, rate) < 10
-			AtLimit.new(**kwargs)
-		else
-			new(**kwargs)
-		end
+		}
 	end
 
-	def self.limit_remaining(customer, usage, rate)
-		can_use = customer.minute_limit.to_d + customer.monthly_overage_limit
-		([can_use - usage, 0].max / rate).to_i
+	def self.register(&maybe_mk)
+		@kinds ||= []
+		@kinds << maybe_mk
 	end
 
 	value_semantics do
@@ -68,6 +67,10 @@ class CallAttempt
 	end
 
 	class Unsupported
+		CallAttempt.register do |supported:, direction:, **|
+			new(direction: direction) unless supported
+		end
+
 		value_semantics do
 			direction Either(:inbound, :outbound)
 		end
@@ -96,12 +99,16 @@ class CallAttempt
 	end
 
 	class NoBalance
-		def self.for(customer, rate, usage, supported, direction:, **kwargs)
+		CallAttempt.register do |credit:, rate:, **kwargs|
+			self.for(rate: rate, **kwargs) if credit < rate * 10
+		end
+
+		def self.for(customer:, direction:, **kwargs)
 			LowBalance.for(customer).then(&:notify!).then do |amount|
 				if amount&.positive?
 					CallAttempt.for(
-						customer.with_balance(customer.balance + amount),
-						rate, usage, supported, direction: direction, **kwargs
+						customer: customer.with_balance(customer.balance + amount),
+						**kwargs.merge(direction: direction)
 					)
 				else
 					NoBalance.new(balance: customer.balance, direction: direction)
@@ -148,6 +155,18 @@ class CallAttempt
 			max_minutes Integer
 		end
 
+		CallAttempt.register do |digits: nil, limit_remaining:, customer:, **kwargs|
+			if digits != "1" && limit_remaining < 10
+				new(
+					**kwargs
+						.merge(
+							limit_remaining: limit_remaining,
+							customer_id: customer.customer_id
+						).slice(*value_semantics.attributes.map(&:name))
+				)
+			end
+		end
+
 		def view
 			"#{direction}/at_limit"
 		end
@@ -179,4 +198,12 @@ class CallAttempt
 			as_json.to_json(*args)
 		end
 	end
+
+	register do |**kwargs|
+		new(
+			**kwargs
+				.merge(customer_id: customer.customer_id)
+				.slice(*value_semantics.attributes.map(&:name))
+		)
+	end
 end

lib/call_attempt_repo.rb 🔗

@@ -58,8 +58,8 @@ protected
 	def find(customer, other_tel, direction:, **kwargs)
 		find_all(customer, other_tel, direction).then do |(rate, usage, tl, c)|
 			CallAttempt.for(
-				customer, rate, usage,
-				rate && tl.support_call?(rate, c || 0),
+				customer: customer, rate: rate, usage: usage,
+				supported: rate && tl.support_call?(rate, c || 0),
 				direction: direction, **kwargs
 			)
 		end