Switch to rubocop 0.89.1

Stephen Paul Weber created

This is the rubocop in new Debian stable

Change summary

.rubocop.yml                         | 72 +++++++++++++++++++++--------
Gemfile                              |  2 
lib/alt_top_up_form.rb               |  4 
lib/backend_sgx.rb                   |  4 -
lib/btc_sell_prices.rb               |  4 +
lib/buy_account_credit_form.rb       |  3 
lib/catapult.rb                      |  2 
lib/command.rb                       | 15 +++--
lib/configure_calls_form.rb          |  6 +-
lib/customer.rb                      |  6 ++
lib/customer_fwd.rb                  |  9 +--
lib/customer_info.rb                 |  5 +
lib/customer_ogm.rb                  |  6 +
lib/customer_plan.rb                 |  1 
lib/customer_repo.rb                 | 30 +++++++----
lib/form_template.rb                 | 17 +++---
lib/low_balance.rb                   |  1 
lib/not_loaded.rb                    |  2 
lib/oob.rb                           |  8 +-
lib/paypal_done.rb                   |  7 +-
lib/proxied_jid.rb                   |  3 
lib/registration.rb                  | 11 ++--
lib/statsd.rb                        |  4 
lib/tel_selections.rb                |  4 
lib/transaction.rb                   |  2 
lib/usage_report.rb                  |  4 
sgx_jmp.rb                           | 26 +++++-----
test/test_bandwidth_tn_order.rb      |  4 
test/test_buy_account_credit_form.rb |  8 ++-
test/test_customer_info_form.rb      |  4 +
test/test_customer_ogm.rb            |  6 +-
test/test_customer_repo.rb           |  9 ++-
test/test_low_balance.rb             |  2 
test/test_oob.rb                     | 20 ++++----
test/test_registration.rb            |  6 +-
test/test_xep0122_field.rb           |  8 +-
web.rb                               | 14 +++--
37 files changed, 204 insertions(+), 135 deletions(-)

Detailed changes

.rubocop.yml 🔗

@@ -1,10 +1,6 @@
 AllCops:
   TargetRubyVersion: 2.5
-
-Metrics/LineLength:
-  Max: 80
-  Exclude:
-    - Gemfile
+  NewCops: enable
 
 Metrics/ClassLength:
   Exclude:
@@ -21,20 +17,53 @@ Metrics/BlockLength:
   Exclude:
     - test/*
 
-Metrics/ClassLength:
-  Exclude:
-    - test/*
-
 Metrics/AbcSize:
   Exclude:
     - test/*
 
-Style/Tab:
+Metrics/ParameterLists:
+  Max: 6
+
+Naming/MethodParameterName:
+  AllowNamesEndingInNumbers: false
+  AllowedNames:
+    - m
+    - e
+    - q
+    - s
+    - k
+    - v
+    - ex
+    - tx
+    - id
+    - iq
+    - db
+
+Layout/IndentationStyle:
   Enabled: false
+  EnforcedStyle: tabs
+  IndentationWidth: 2
 
-Style/IndentationWidth:
+Layout/IndentationWidth:
   Width: 1 # one tab
 
+Layout/LineLength:
+  Max: 80
+  Exclude:
+    - Gemfile
+
+Layout/SpaceAroundEqualsInParameterDefault:
+  EnforcedStyle: no_space
+
+Layout/AccessModifierIndentation:
+  EnforcedStyle: outdent
+
+Layout/FirstParameterIndentation:
+  EnforcedStyle: consistent
+
+Style/AccessModifierDeclarations:
+  Enabled: false
+
 Style/StringLiterals:
   EnforcedStyle: double_quotes
 
@@ -64,22 +93,25 @@ Style/RegexpLiteral:
   EnforcedStyle: slashes
   AllowInnerSlashes: true
 
-Layout/SpaceAroundEqualsInParameterDefault:
-  EnforcedStyle: no_space
-
-Layout/AccessModifierIndentation:
-  EnforcedStyle: outdent
+Lint/OutOfRangeRegexpRef:
+  Enabled: false
 
-Layout/FirstParameterIndentation:
-  EnforcedStyle: consistent
+Lint/MissingSuper:
+  Enabled: false
 
 Style/BlockDelimiters:
-  EnforcedStyle: braces_for_chaining
+  EnforcedStyle: semantic
+  AllowBracesOnProceduralOneLiners: true
+  ProceduralMethods:
+    - execute_command
 
 Style/MultilineBlockChain:
   Enabled: false
 
-Layout/IndentArray:
+Layout/FirstArgumentIndentation:
+  EnforcedStyle: consistent
+
+Layout/FirstArrayElementIndentation:
   EnforcedStyle: consistent
 
 Style/FormatString:

Gemfile 🔗

@@ -10,8 +10,8 @@ gem "dhall"
 gem "em-hiredis"
 gem "em-http-request", git: "https://github.com/singpolyma/em-http-request", branch: "fix-letsencrypt"
 gem "em-pg-client", git: "https://github.com/royaltm/ruby-em-pg-client"
-gem "em-synchrony"
 gem "em_promise.rb", "~> 0.0.3"
+gem "em-synchrony"
 gem "eventmachine"
 gem "money-open-exchange-rates"
 gem "multibases"

lib/alt_top_up_form.rb 🔗

@@ -63,13 +63,13 @@ class AltTopUpForm
 		end
 	end
 
-	IS_CAD = [
+	IS_CAD = [{
 		var: "adr",
 		type: "fixed",
 		label: "Interac eTransfer Address",
 		description: "Please include your Jabber ID in the note",
 		value: CONFIG[:interac]
-	].freeze
+	}].freeze
 
 	class AddBtcAddressField
 		def self.for(addrs)

lib/backend_sgx.rb 🔗

@@ -24,9 +24,7 @@ class BackendSgx
 		ibr.username = creds[:username]
 		ibr.password = creds[:password]
 		ibr.phone = tel
-		IQ_MANAGER.write(ibr).then do
-			with(registered?: irb)
-		end
+		IQ_MANAGER.write(ibr)
 	end
 
 	def stanza(s)

lib/btc_sell_prices.rb 🔗

@@ -22,7 +22,9 @@ class BTCSellPrices
 			canadianbitcoins = Nokogiri::HTML.parse(http.response)
 
 			bitcoin_row = canadianbitcoins.at("#ticker > table > tbody > tr")
-			raise "Bitcoin row has moved" unless bitcoin_row.at("td").text == "Bitcoin"
+			unless bitcoin_row.at("td").text == "Bitcoin"
+				raise "Bitcoin row has moved"
+			end
 
 			BigDecimal(
 				bitcoin_row.at("td:nth-of-type(3)").text.match(/^\$(\d+\.\d+)/)[1]

lib/buy_account_credit_form.rb 🔗

@@ -23,7 +23,7 @@ class BuyAccountCreditForm
 		@payment_methods = payment_methods
 	end
 
-	RANGE = (15..1000)
+	RANGE = (15..1000).freeze
 
 	AMOUNT_FIELD =
 		XEP0122Field.new(
@@ -54,6 +54,7 @@ class BuyAccountCreditForm
 	def parse(form)
 		amount = form.field("amount")&.value&.to_s
 		raise AmountValidationError, amount unless RANGE.include?(amount.to_i)
+
 		{
 			payment_method: @payment_methods.fetch(
 				form.field("payment_method")&.value.to_i

lib/catapult.rb 🔗

@@ -27,6 +27,7 @@ class Catapult
 			unless http.response_header.status == 201
 				raise "Create new SIP account failed"
 			end
+
 			http.response_header["location"]
 		end
 	end
@@ -81,6 +82,7 @@ class Catapult
 	def mkurl(path)
 		base = "https://api.catapult.inetwork.com/v1/users/#{@user}/"
 		return path if path.start_with?(base)
+
 		"#{base}#{path}"
 	end
 

lib/command.rb 🔗

@@ -58,9 +58,9 @@ class Command
 		end
 
 		def reply(stanza=nil)
-			stanza ||= iq.reply.tap do |reply|
+			stanza ||= iq.reply.tap { |reply|
 				reply.status = :executing
-			end
+			}
 			yield stanza if block_given?
 			COMMAND_MANAGER.write(stanza).then { |new_iq|
 				@iq = new_iq
@@ -96,13 +96,13 @@ class Command
 		end
 
 		def customer
-			@customer ||= @customer_repo.find_by_jid(@iq.from.stripped).then do |c|
+			@customer ||= @customer_repo.find_by_jid(@iq.from.stripped).then { |c|
 				sentry_hub.current_scope.set_user(
 					id: c.customer_id,
 					jid: @iq.from.stripped
 				)
 				c
-			end
+			}
 		end
 
 	protected
@@ -143,17 +143,18 @@ class Command
 		name,
 		customer_repo: CustomerRepo.new,
 		list_for: ->(tel:, **) { !!tel },
-		format_error: ->(e) { e.respond_to?(:message) ? e.message : e.to_s }
+		format_error: ->(e) { e.respond_to?(:message) ? e.message : e.to_s },
+		&blk
 	)
 		@node = node
 		@name = name
 		@customer_repo = customer_repo
 		@list_for = list_for
 		@format_error = format_error
-		@blk = ->(exe) { yield exe }
+		@blk = blk
 	end
 
-	def register(blather, guards: [:execute?, node: @node, sessionid: nil])
+	def register(blather, guards: [:execute?, { node: @node, sessionid: nil }])
 		blather.command(*guards) do |iq|
 			Execution.new(@customer_repo, blather, @format_error, iq).execute(&@blk)
 		end

lib/configure_calls_form.rb 🔗

@@ -21,14 +21,14 @@ class ConfigureCallsForm
 				result[:transcription_enabled] =
 					["1", "true"].include?(params["voicemail_transcription"])
 			end
-			result[:lidb_name] = params["lidb_name"] if lidb_guard(params["lidb_name"])
+			result[:lidb_name] = params["lidb_name"] if lidb_guard(params)
 		end
 	end
 
 protected
 
-	def lidb_guard(lidb_name)
-		!lidb_name.to_s.strip.empty? &&
+	def lidb_guard(params)
+		!params["lidb_name"].to_s.strip.empty? &&
 			!@customer.tndetails.dig(:features, :lidb)
 	end
 

lib/customer.rb 🔗

@@ -20,6 +20,7 @@ class Customer
 	extend Forwardable
 
 	attr_reader :customer_id, :balance, :jid
+
 	def_delegators :@plan, :active?, :activate_plan_starting_now, :bill_plan,
 	               :currency, :merchant_account, :plan_name, :auto_top_up_amount
 	def_delegators :@sgx, :register!, :registered?, :set_ogm_url,
@@ -109,7 +110,10 @@ class Customer
 			"jmp_available_btc_addresses",
 			"jmp_customer_btc_addresses-#{customer_id}"
 		]).then do |addr|
-			ELECTRUM.notify(addr, CONFIG[:electrum_notify_url].call(addr, customer_id))
+			ELECTRUM.notify(
+				addr,
+				CONFIG[:electrum_notify_url].call(addr, customer_id)
+			)
 			addr
 		end
 	end

lib/customer_fwd.rb 🔗

@@ -7,6 +7,7 @@ class CustomerFwd
 	def self.for(uri:, timeout:)
 		timeout = Timeout.new(timeout)
 		return None.new(uri: uri, timeout: timeout) if !uri || timeout.zero?
+
 		if uri =~ /\Asip:(.*)@sip.cheogram.com\Z/
 			uri = "xmpp:#{$1.gsub(/%([0-9A-F]{2})/i) { $1.to_i(16).chr }}"
 		end
@@ -35,9 +36,7 @@ class CustomerFwd
 
 	value_semantics do
 		uri Either(String, NilClass)
-		# rubocop:disable Style/RedundantSelf
-		self.timeout Timeout, coerce: Timeout.method(:new)
-		# rubocop:enable Style/RedundantSelf
+		def_attr :timeout, Timeout, coerce: Timeout.method(:new)
 	end
 
 	def with(new_attrs)
@@ -45,11 +44,11 @@ class CustomerFwd
 	end
 
 	def create_call(account)
-		request = Bandwidth::ApiCreateCallRequest.new.tap do |cc|
+		request = Bandwidth::ApiCreateCallRequest.new.tap { |cc|
 			cc.to = to
 			cc.call_timeout = timeout.to_i
 			yield cc if block_given?
-		end
+		}
 		BANDWIDTH_VOICE.create_call(account, body: request).data.call_id
 	end
 

lib/customer_info.rb 🔗

@@ -37,11 +37,14 @@ class CustomerInfo
 	end
 
 	def next_renewal
-		{ var: "Next renewal", value: expires_at.strftime("%Y-%m-%d") } if expires_at
+		return unless expires_at
+
+		{ var: "Next renewal", value: expires_at.strftime("%Y-%m-%d") }
 	end
 
 	def monthly_amount
 		return unless plan.monthly_price
+
 		{ var: "Renewal", value: "$%.4f / month" % plan.monthly_price }
 	end
 

lib/customer_ogm.rb 🔗

@@ -3,6 +3,7 @@
 module CustomerOGM
 	def self.for(url, fetch_vcard_temp)
 		return Media.new(url) if url
+
 		TTS.for(fetch_vcard_temp)
 	end
 
@@ -12,7 +13,7 @@ module CustomerOGM
 		end
 
 		def to_render
-			[:voicemail_ogm_media, locals: { url: @url }]
+			[:voicemail_ogm_media, { locals: { url: @url } }]
 		end
 	end
 
@@ -30,6 +31,7 @@ module CustomerOGM
 		def [](k)
 			value = @vcard[k]
 			return if value.to_s.empty?
+
 			value
 		end
 
@@ -38,7 +40,7 @@ module CustomerOGM
 		end
 
 		def to_render
-			[:voicemail_ogm_tts, locals: { fn: fn }]
+			[:voicemail_ogm_tts, { locals: { fn: fn } }]
 		end
 	end
 end

lib/customer_plan.rb 🔗

@@ -8,6 +8,7 @@ class CustomerPlan
 	extend Forwardable
 
 	attr_reader :expires_at
+
 	def_delegator :@plan, :name, :plan_name
 	def_delegators :@plan, :currency, :merchant_account, :monthly_price
 

lib/customer_repo.rb 🔗

@@ -24,6 +24,7 @@ class CustomerRepo
 	def find(customer_id)
 		@redis.get("jmp_customer_jid-#{customer_id}").then do |jid|
 			raise NotFound, "No jid" unless jid
+
 			find_inner(customer_id, jid)
 		end
 	end
@@ -34,6 +35,7 @@ class CustomerRepo
 		else
 			@redis.get("jmp_customer_id-#{jid}").then do |customer_id|
 				next find_legacy_customer(jid) unless customer_id
+
 				find_inner(customer_id, jid)
 			end
 		end
@@ -42,6 +44,7 @@ class CustomerRepo
 	def find_by_tel(tel)
 		@redis.get("catapult_jid-#{tel}").then do |jid|
 			raise NotFound, "No jid" unless jid
+
 			find_by_jid(jid)
 		end
 	end
@@ -49,11 +52,13 @@ class CustomerRepo
 	def create(jid)
 		@braintree.customer.create.then do |result|
 			raise "Braintree customer create failed" unless result.success?
+
 			cid = result.customer.id
 			@redis.msetnx(
 				"jmp_customer_id-#{jid}", cid, "jmp_customer_jid-#{cid}", jid
 			).then do |redis_result|
 				raise "Saving new customer to redis failed" unless redis_result == 1
+
 				Customer.new(cid, Blather::JID.new(jid), sgx: new_sgx(cid))
 			end
 		end
@@ -65,8 +70,7 @@ class CustomerRepo
 			lidb_tn_groups: { lidb_tn_group: {
 				telephone_numbers: [customer.registered?.phone.sub(/\A\+1/, "")],
 				subscriber_information: lidb_name,
-				use_type: "RESIDENTIAL",
-				visibility: "PUBLIC"
+				use_type: "RESIDENTIAL", visibility: "PUBLIC"
 			} }
 		)
 	end
@@ -79,9 +83,7 @@ class CustomerRepo
 
 	def put_fwd(customer, customer_fwd)
 		@sgx_repo.put_fwd(
-			customer.customer_id,
-			customer.registered?.phone,
-			customer_fwd
+			customer.customer_id, customer.registered?.phone, customer_fwd
 		)
 	end
 
@@ -94,6 +96,7 @@ protected
 	def find_legacy_customer(jid)
 		@redis.lindex("catapult_cred-#{jid}", 3).then do |tel|
 			raise NotFound, "No customer" unless tel
+
 			LegacyCustomer.new(Blather::JID.new(jid), tel)
 		end
 	end
@@ -108,14 +111,19 @@ protected
 		end
 	end
 
+	SQL = <<~SQL
+		SELECT COALESCE(balance,0) AS balance, plan_name, expires_at
+		FROM customer_plans LEFT JOIN balances USING (customer_id)
+		WHERE customer_id=$1 LIMIT 1
+	SQL
+
 	def find_inner(customer_id, jid)
-		result = @db.query_defer(<<~SQL, [customer_id])
-			SELECT COALESCE(balance,0) AS balance, plan_name, expires_at
-			FROM customer_plans LEFT JOIN balances USING (customer_id)
-			WHERE customer_id=$1 LIMIT 1
-		SQL
+		result = @db.query_defer(SQL, [customer_id])
 		EMPromise.all([@sgx_repo.get(customer_id), result]).then do |(sgx, rows)|
-			data = hydrate_plan(customer_id, rows.first&.transform_keys(&:to_sym) || {})
+			data = hydrate_plan(
+				customer_id,
+				rows.first&.transform_keys(&:to_sym) || {}
+			)
 			Customer.new(customer_id, Blather::JID.new(jid), sgx: sgx, **data)
 		end
 	end

lib/form_template.rb 🔗

@@ -48,21 +48,21 @@ class FormTemplate
 			@__form.instructions = s
 		end
 
-		def validate(f, datatype: nil, **kwargs)
-			Nokogiri::XML::Builder.with(f) do |x|
-				x.validate(
+		def validate(field, datatype: nil, **kwargs)
+			Nokogiri::XML::Builder.with(field) do |xml|
+				xml.validate(
 					xmlns: "http://jabber.org/protocol/xdata-validate",
 					datatype: datatype || "xs:string"
 				) do
-					x.basic unless validation_type(x, **kwargs)
+					xml.basic unless validation_type(xml, **kwargs)
 				end
 			end
 		end
 
-		def validation_type(x, open: false, regex: nil, range: nil)
-			x.open if open
-			x.range(min: range.first, max: range.last) if range
-			x.regex(regex.source) if regex
+		def validation_type(xml, open: false, regex: nil, range: nil)
+			xml.open if open
+			xml.range(min: range.first, max: range.last) if range
+			xml.regex(regex.source) if regex
 			open || regex || range
 		end
 
@@ -80,6 +80,7 @@ class FormTemplate
 
 		def form
 			raise "Type never set" unless @__type_set
+
 			@__form
 		end
 	end

lib/low_balance.rb 🔗

@@ -41,6 +41,7 @@ class LowBalance
 
 	def btc_addresses_for_notification
 		return if @btc_addresses.empty?
+
 		"\nYou can buy credit by sending any amount of Bitcoin to one of " \
 		"these addresses:\n#{@btc_addresses.join("\n")}"
 	end

lib/not_loaded.rb 🔗

@@ -11,7 +11,7 @@ class NotLoaded
 		true
 	end
 
-	def method_missing(*) # rubocop:disable Style/MethodMissing
+	def method_missing(*)
 		raise NotLoadedError, "#{@name} not loaded"
 	end
 end

lib/oob.rb 🔗

@@ -34,10 +34,10 @@ class OOB < Blather::XMPPNode
 		find("ns:url", ns: self.class.registered_ns).first&.content
 	end
 
-	def url=(u)
+	def url=(url)
 		remove_children :url
 		i = Niceogiri::XML::Node.new(:url, document, namespace)
-		i.content = u
+		i.content = url
 		self << i
 	end
 
@@ -45,10 +45,10 @@ class OOB < Blather::XMPPNode
 		find("ns:desc", ns: self.class.registered_ns).first&.content
 	end
 
-	def desc=(d)
+	def desc=(description)
 		remove_children :desc
 		i = Niceogiri::XML::Node.new(:desc, document, namespace)
-		i.content = d
+		i.content = description
 		self << i
 	end
 end

lib/paypal_done.rb 🔗

@@ -2,9 +2,10 @@
 
 class PaypalDone
 	MESSAGE =
-		"\n\nPayPal users must now go to https://www.paypal.com/myaccount/autopay/ " \
-		"and cancel their PayPal subscription to JMP. Then contact support and " \
-		"provide them with your PayPal email address."
+		"\n\nPayPal users must now go to " \
+		"https://www.paypal.com/myaccount/autopay/ and cancel their PayPal " \
+		"subscription to JMP. Then contact support and provide them with " \
+		"your PayPal email address."
 
 	def initialize(*); end
 

lib/proxied_jid.rb 🔗

@@ -4,7 +4,8 @@ require "delegate"
 require "blather"
 
 class ProxiedJID < SimpleDelegator
-	ESCAPED = /20|22|26|27|2f|3a|3c|3e|40|5c/
+	ESCAPED = /20|22|26|27|2f|3a|3c|3e|40|5c/.freeze
+
 	def unproxied
 		Blather::JID.new(
 			node.gsub(/\\(#{ESCAPED})/) { |s|

lib/registration.rb 🔗

@@ -110,9 +110,9 @@ class Registration
 				ACTIVATE_INSTRUCTION,
 				CRYPTOCURRENCY_INSTRUCTION
 			].each do |txt|
-				form << Blather::XMPPNode.new(:instructions, form.document).tap do |i|
+				form << Blather::XMPPNode.new(:instructions, form.document).tap { |i|
 					i << txt
-				end
+				}
 			end
 		end
 
@@ -202,9 +202,9 @@ class Registration
 		protected
 
 			def addr
-				@addr ||= @customer.btc_addresses.then do |addrs|
+				@addr ||= @customer.btc_addresses.then { |addrs|
 					addrs.first || @customer.add_btc_address
-				end
+				}
 			end
 		end
 
@@ -380,6 +380,7 @@ class Registration
 							WHERE code=$2 AND used_by_id IS NULL
 						SQL
 						raise Invalid, "Not a valid invite code: #{code}" unless valid
+
 						@customer.activate_plan_starting_now
 					end
 				end
@@ -459,7 +460,7 @@ class Registration
 				EMPromise.all([
 					REDIS.del("pending_tel_for-#{@customer.jid}"),
 					Bwmsgsv2Repo.new.put_fwd(@customer.customer_id, @tel, CustomerFwd.for(
-						uri: "xmpp:#{@customer.jid}", timeout: 25 # ~5 seconds / ring, 5 rings
+						uri: "xmpp:#{@customer.jid}", timeout: 25 # ~5s / ring, 5 rings
 					))
 				])
 			}.then do

lib/statsd.rb 🔗

@@ -3,7 +3,7 @@
 require "statsd-instrument"
 
 # These are basically data, not code, I find them more readable on one line each
-# rubocop:disable Metrics/LineLength
+# rubocop:disable Layout/LineLength
 
 Registration::Registered.extend StatsD::Instrument
 Registration::Registered.statsd_count :write, "registration.registered"
@@ -30,4 +30,4 @@ Registration::Payment::Mail.statsd_count :write, "registration.payment.mail"
 Registration::Finish.extend StatsD::Instrument
 Registration::Finish.statsd_count :write, "registration.finish"
 
-# rubocop:enable Metrics/LineLength
+# rubocop:enable Layout/LineLength

lib/tel_selections.rb 🔗

@@ -174,11 +174,11 @@ class TelSelections
 			}.each do |k, args|
 				klass = const_set(
 					args[0],
-					Class.new(Q) do
+					Class.new(Q) {
 						define_method(:iris_query) do
 							{ k => @q }
 						end
-					end
+					}
 				)
 
 				args[1..-1].each do |regex|

lib/transaction.rb 🔗

@@ -59,6 +59,7 @@ class Transaction
 
 	def bonus
 		return BigDecimal(0) if amount <= 15
+
 		amount *
 			case amount
 			when (15..29.99)
@@ -89,6 +90,7 @@ protected
 
 	def insert_bonus
 		return if bonus <= 0
+
 		params = [@customer_id, "bonus_for_#{@transaction_id}", @created_at, bonus]
 		DB.exec(<<~SQL, params)
 			INSERT INTO transactions

lib/usage_report.rb 🔗

@@ -29,11 +29,11 @@ class UsageReport
 		total_minutes = 0
 
 		FormTable.new(
-			@report_for.first.downto(@report_for.last).map do |day|
+			@report_for.first.downto(@report_for.last).map { |day|
 				total_messages += @messages[day]
 				total_minutes += @minutes[day]
 				[day, @messages[day], @minutes[day]]
-			end + [["Total", total_messages, total_minutes]],
+			} + [["Total", total_messages, total_minutes]],
 			day: "Day", messages: "Messages", minutes: "Minutes"
 		)
 	end

sgx_jmp.rb 🔗

@@ -134,7 +134,7 @@ class AsyncBraintree
 	end
 
 	def respond_to_missing?(m, *)
-		@gateway.respond_to?(m)
+		@gateway.respond_to?(m) || super
 	end
 
 	def method_missing(m, *args)
@@ -147,11 +147,12 @@ class AsyncBraintree
 
 	class PromiseChain < EMPromise
 		def respond_to_missing?(*)
-			false # We don't actually know what we respond to...
+			false && super # We don't actually know what we respond to...
 		end
 
 		def method_missing(m, *args)
 			return super if respond_to_missing?(m, *args)
+
 			self.then { |o| o.public_send(m, *args) }
 		end
 	end
@@ -188,10 +189,10 @@ when_ready do
 	REDIS = EM::Hiredis.connect
 	TEL_SELECTIONS = TelSelections.new
 	BTC_SELL_PRICES = BTCSellPrices.new(REDIS, CONFIG[:oxr_app_id])
-	DB = PG::EM::ConnectionPool.new(dbname: "jmp") do |conn|
+	DB = PG::EM::ConnectionPool.new(dbname: "jmp") { |conn|
 		conn.type_map_for_results = PG::BasicTypeMapForResults.new(conn)
 		conn.type_map_for_queries = PG::BasicTypeMapForQueries.new(conn)
-	end
+	}
 
 	DB.hold do |conn|
 		conn.query("LISTEN low_balance")
@@ -224,9 +225,9 @@ setup(
 message to: /\Aaccount@/, body: /./ do |m|
 	StatsD.increment("deprecated_account_bot")
 
-	self << m.reply.tap do |out|
+	self << m.reply.tap { |out|
 		out.body = "This bot is deprecated. Please talk to xmpp:cheogram.com"
-	end
+	}
 end
 
 before(
@@ -304,7 +305,8 @@ message do |m|
 				BLATHER.join(CONFIG[:notify_admin], "sgx-jmp")
 				BLATHER.say(
 					CONFIG[:notify_admin],
-					"#{customer.customer_id} has used #{usage} messages since #{today - 30}",
+					"#{customer.customer_id} has used #{usage} " \
+					"messages since #{today - 30}",
 					:groupchat
 				)
 			end
@@ -376,13 +378,13 @@ disco_items node: "http://jabber.org/protocol/commands" do |iq|
 	}.then { |customer|
 		CommandList.for(customer)
 	}.then { |list|
-		reply.items = list.map do |item|
+		reply.items = list.map { |item|
 			Blather::Stanza::DiscoItems::Item.new(
 				iq.to,
 				item[:node],
 				item[:name]
 			)
-		end
+		}
 		self << reply
 	}.catch { |e| panic(e, sentry_hub) }
 end
@@ -468,9 +470,7 @@ Command.new(
 			EMPromise.all(cc_form.parse(iq.form).map { |k, v|
 				Command.execution.customer_repo.public_send("put_#{k}", customer, v)
 			})
-		}.then do
-			Command.finish("Configuration saved!")
-		end
+		}.then { Command.finish("Configuration saved!") }
 	end
 }.register(self).then(&CommandList.method(:register))
 
@@ -726,7 +726,7 @@ command :execute?, node: "web-register" do |iq|
 			IQ_MANAGER.write(Blather::Stanza::Iq::Command.new.tap { |cmd|
 				cmd.to = CONFIG[:web_register][:to]
 				cmd.node = "push-register"
-				cmd.form.fields = [var: "to", value: jid]
+				cmd.form.fields = [{ var: "to", value: jid }]
 				cmd.form.type = "submit"
 			}).then { |result|
 				TEL_SELECTIONS.set(result.form.field("from")&.value.to_s.strip, tel)

test/test_bandwidth_tn_order.rb 🔗

@@ -64,7 +64,7 @@ class BandwidthTNOrderTest < Minitest::Test
 				:post,
 				"https://api.catapult.inetwork.com/v1/users/catapult_user/phoneNumbers"
 			).with(
-				body: open(__dir__ + "/data/catapult_import_body.json").read.chomp,
+				body: File.open("#{__dir__}/data/catapult_import_body.json").read.chomp,
 				headers: {
 					"Authorization" => "Basic Y2F0YXB1bHRfdG9rZW46Y2F0YXB1bHRfc2VjcmV0",
 					"Content-Type" => "application/json"
@@ -90,7 +90,7 @@ class BandwidthTNOrderTest < Minitest::Test
 				:post,
 				"https://api.catapult.inetwork.com/v1/users/catapult_user/phoneNumbers"
 			).with(
-				body: open(__dir__ + "/data/catapult_import_body.json").read.chomp,
+				body: File.open("#{__dir__}/data/catapult_import_body.json").read.chomp,
 				headers: {
 					"Authorization" => "Basic Y2F0YXB1bHRfdG9rZW46Y2F0YXB1bHRfc2VjcmV0",
 					"Content-Type" => "application/json"

test/test_buy_account_credit_form.rb 🔗

@@ -18,9 +18,11 @@ class BuyAccountCreditFormTest < Minitest::Test
 	def test_for
 		braintree_customer = Minitest::Mock.new
 		Customer::BRAINTREE.expect(:customer, braintree_customer)
-		braintree_customer.expect(:find, EMPromise.resolve(
-			OpenStruct.new(payment_methods: [])
-		), ["test"])
+		braintree_customer.expect(
+			:find,
+			EMPromise.resolve(OpenStruct.new(payment_methods: [])),
+			["test"]
+		)
 
 		assert_kind_of(
 			BuyAccountCreditForm,

test/test_customer_info_form.rb 🔗

@@ -18,7 +18,9 @@ class FakeRepo
 
 	def find_by_jid(jid)
 		EMPromise.resolve(nil).then do
-			@customers.find { |cust| cust.jid.to_s == jid.to_s } || raise("No Customer")
+			@customers.find { |cust|
+				cust.jid.to_s == jid.to_s
+			} || raise("No Customer")
 		end
 	end
 

test/test_customer_ogm.rb 🔗

@@ -23,7 +23,7 @@ class CustomerOGMTest < Minitest::Test
 		def test_to_render_empty_vcard
 			vcard = Blather::Stanza::Iq::Vcard::Vcard.new
 			assert_equal(
-				[:voicemail_ogm_tts, locals: { fn: "a user of JMP.chat" }],
+				[:voicemail_ogm_tts, { locals: { fn: "a user of JMP.chat" } }],
 				CustomerOGM::TTS.new(vcard).to_render
 			)
 		end
@@ -32,7 +32,7 @@ class CustomerOGMTest < Minitest::Test
 			vcard = Blather::Stanza::Iq::Vcard::Vcard.new
 			vcard["FN"] = "name"
 			assert_equal(
-				[:voicemail_ogm_tts, locals: { fn: "name" }],
+				[:voicemail_ogm_tts, { locals: { fn: "name" } }],
 				CustomerOGM::TTS.new(vcard).to_render
 			)
 		end
@@ -41,7 +41,7 @@ class CustomerOGMTest < Minitest::Test
 			vcard = Blather::Stanza::Iq::Vcard::Vcard.new
 			vcard["NICKNAME"] = "name"
 			assert_equal(
-				[:voicemail_ogm_tts, locals: { fn: "name" }],
+				[:voicemail_ogm_tts, { locals: { fn: "name" } }],
 				CustomerOGM::TTS.new(vcard).to_render
 			)
 		end

test/test_customer_repo.rb 🔗

@@ -146,9 +146,12 @@ class CustomerRepoTest < Minitest::Test
 		repo = mkrepo(redis: redis, braintree: braintree)
 		braintree_customer = Minitest::Mock.new
 		braintree.expect(:customer, braintree_customer)
-		braintree_customer.expect(:create, EMPromise.resolve(
-			OpenStruct.new(success?: true, customer: OpenStruct.new(id: "test"))
-		))
+		braintree_customer.expect(
+			:create,
+			EMPromise.resolve(
+				OpenStruct.new(success?: true, customer: OpenStruct.new(id: "test"))
+			)
+		)
 		redis.expect(
 			:msetnx,
 			EMPromise.resolve(1),

test/test_low_balance.rb 🔗

@@ -85,7 +85,7 @@ class LowBalanceTest < Minitest::Test
 			LowBalance::AutoTopUp::Transaction.expect(
 				:sale,
 				tx,
-				[@customer, amount: 100]
+				[@customer, { amount: 100 }]
 			)
 			@auto_top_up.notify!
 			assert_mock tx

test/test_oob.rb 🔗

@@ -15,11 +15,11 @@ class OOBTest < Minitest::Test
 	end
 
 	property(:new_with_attrs) { [string(:alnum), string] }
-	def new_with_attrs(u, d)
-		oob = OOB.new(u, desc: d)
+	def new_with_attrs(url, description)
+		oob = OOB.new(url, desc: description)
 		assert_kind_of OOB, oob
-		assert_equal u, oob.url
-		assert_equal d, oob.desc
+		assert_equal url, oob.url
+		assert_equal description, oob.desc
 	end
 
 	def test_find_or_create_not_found
@@ -34,16 +34,16 @@ class OOBTest < Minitest::Test
 	end
 
 	property(:url) { string(:alnum) }
-	def url(u)
+	def url(a_url)
 		oob = OOB.new
-		oob.url = u
-		assert_equal u, oob.url
+		oob.url = a_url
+		assert_equal a_url, oob.url
 	end
 
 	property(:desc) { string }
-	def desc(d)
+	def desc(description)
 		oob = OOB.new
-		oob.desc = d
-		assert_equal d, oob.desc
+		oob.desc = description
+		assert_equal description, oob.desc
 	end
 end

test/test_registration.rb 🔗

@@ -324,14 +324,14 @@ class RegistrationTest < Minitest::Test
 				)
 				iq = Blather::Stanza::Iq::Command.new
 				iq.from = "test@example.com"
+				msg = Registration::Payment::CreditCard::Activate::DECLINE_MESSAGE
 				Command::COMMAND_MANAGER.expect(
 					:write,
 					EMPromise.reject(:test_result),
 					[Matching.new do |reply|
 						assert_equal :error, reply.note_type
 						assert_equal(
-							Registration::Payment::CreditCard::Activate::DECLINE_MESSAGE +
-							": http://creditcard.example.com",
+							"#{msg}: http://creditcard.example.com",
 							reply.note.content
 						)
 					end]
@@ -557,7 +557,7 @@ class RegistrationTest < Minitest::Test
 				:post,
 				"https://api.catapult.inetwork.com/v1/users/catapult_user/phoneNumbers"
 			).with(
-				body: open(__dir__ + "/data/catapult_import_body.json").read.chomp,
+				body: File.open("#{__dir__}/data/catapult_import_body.json").read.chomp,
 				headers: {
 					"Authorization" => "Basic Y2F0YXB1bHRfdG9rZW46Y2F0YXB1bHRfc2VjcmV0",
 					"Content-Type" => "application/json"

test/test_xep0122_field.rb 🔗

@@ -13,7 +13,7 @@ class XEP0122FieldTest < Minitest::Test
 			type: "text-single"
 		).field
 
-		example = Nokogiri::XML::Builder.new do |xml|
+		example = Nokogiri::XML::Builder.new { |xml|
 			xml.field(
 				xmlns: "jabber:x:data",
 				var: "v",
@@ -27,7 +27,7 @@ class XEP0122FieldTest < Minitest::Test
 					xml.range(min: 0, max: 3)
 				end
 			end
-		end
+		}
 
 		assert_equal example.doc.root.to_xml, field.to_xml
 	end
@@ -40,7 +40,7 @@ class XEP0122FieldTest < Minitest::Test
 			type: "text-single"
 		).field
 
-		example = Nokogiri::XML::Builder.new do |xml|
+		example = Nokogiri::XML::Builder.new { |xml|
 			xml.field(
 				xmlns: "jabber:x:data",
 				var: "v",
@@ -54,7 +54,7 @@ class XEP0122FieldTest < Minitest::Test
 					xml.basic
 				end
 			end
-		end
+		}
 
 		assert_equal example.doc.root.to_xml, field.to_xml
 	end

web.rb 🔗

@@ -55,8 +55,7 @@ class Web < Roda
 	plugin RodaEMPromise # Must go last!
 
 	class << self
-		attr_reader :customer_repo, :log
-		attr_reader :true_inbound_call, :outbound_transfers
+		attr_reader :customer_repo, :log, :true_inbound_call, :outbound_transfers
 
 		def run(log, *listen_on)
 			plugin :common_logger, log, method: :info
@@ -118,7 +117,7 @@ class Web < Roda
 		elsif candidate[0] == "+" && /\A\d+\z/.match(candidate[1..-1])
 			candidate
 		elsif candidate == "Restricted"
-			TEL_CANDIDATES.fetch(candidate, "19") +
+			"#{TEL_CANDIDATES.fetch(candidate, '19')}" \
 				";phone-context=anonymous.phone-context.soprani.ca"
 		end
 	end
@@ -157,6 +156,7 @@ class Web < Roda
 						call_id = params["callId"]
 						EM.promise_timer(2).then {
 							next unless true_inbound_call[p_call_id] == call_id
+
 							true_inbound_call.delete(p_call_id)
 
 							if (outbound_leg = outbound_transfers.delete(p_call_id))
@@ -257,13 +257,13 @@ class Web < Roda
 					CustomerRepo.new(
 						sgx_repo: Bwmsgsv2Repo.new
 					).find_by_tel(params["to"]).then(&:fwd).then do |fwd|
-						call = fwd.create_call(CONFIG[:creds][:account]) do |cc|
+						call = fwd.create_call(CONFIG[:creds][:account]) { |cc|
 							true_inbound_call[pseudo_call_id] = params["callId"]
 							cc.from = params["from"]
 							cc.application_id = params["applicationId"]
 							cc.answer_url = url inbound_calls_path(nil)
 							cc.disconnect_url = url inbound_calls_path(:transfer_complete)
-						end
+						}
 
 						if call
 							outbound_transfers[pseudo_call_id] = call
@@ -288,7 +288,9 @@ class Web < Roda
 
 				r.post do
 					customer_id = params["from"].sub(/^\+1/, "")
-					CustomerRepo.new(sgx_repo: Bwmsgsv2Repo.new).find(customer_id).then do |c|
+					CustomerRepo.new(
+						sgx_repo: Bwmsgsv2Repo.new
+					).find(customer_id).then do |c|
 						render :forward, locals: {
 							from: c.registered?.phone,
 							to: params["to"]