Apply ruby-style conventions across the codebase
Frozen string literal enforcement moved from per-file comments to
RUBYOPT in .envrc, which is untracked — as are the binstubs, ignored
via /bin.
Assisted-by: GLM-5.3 via pi
diff --git a/.gitignore b/.gitignore
index 17f4392..e6a5295 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,4 @@
/tmp
/servers
/carddav.mobileconfig
+/bin
diff --git a/Rakefile b/Rakefile
index 2f0e1e5..5006457 100644
--- a/Rakefile
+++ b/Rakefile
@@ -1,12 +1,15 @@
-# frozen_string_literal: true
-$LOAD_PATH.unshift(File.expand_path("lib", __dir__))
+require "pathname"
+
+$LOAD_PATH.unshift(Pathname.new(__dir__) / "lib")
require "minitest/test_task"
Minitest::TestTask.create
-Dir.glob("tasks/*.rake").sort.each { import it }
+Dir.glob("tasks/*.rake").sort.each do
+ import it
+end
desc "Start development server, reloading on changes"
task :dev do
@@ -54,7 +57,9 @@ namespace :profile do
if identifiers.empty?
puts "No pro-tacts profiles found; remove by hand in System Settings → Profiles if one lingers."
else
- identifiers.each { sh "profiles", "remove", "-identifier", it }
+ identifiers.each do
+ sh "profiles", "remove", "-identifier", it
+ end
end
end
end
diff --git a/config.ru b/config.ru
index 7de5d55..20261fe 100644
--- a/config.ru
+++ b/config.ru
@@ -1,6 +1,7 @@
-# frozen_string_literal: true
-$LOAD_PATH.unshift File.expand_path("lib", __dir__)
+require "pathname"
+
+$LOAD_PATH.unshift(Pathname.new(__dir__) / "lib")
require "pro_tacts/web"
run ProTacts::Web.freeze.app
diff --git a/config/puma.rb b/config/puma.rb
index a1dbc39..ec4bd7a 100644
--- a/config/puma.rb
+++ b/config/puma.rb
@@ -1,4 +1,3 @@
-# frozen_string_literal: true
# Allow WebDAV methods (PROPFIND, REPORT) in addition to standard HTTP methods
supported_http_methods %w[GET HEAD OPTIONS PROPFIND REPORT]
diff --git a/lib/pro_tacts.rb b/lib/pro_tacts.rb
index 882af77..31381b8 100644
--- a/lib/pro_tacts.rb
+++ b/lib/pro_tacts.rb
@@ -1,4 +1,3 @@
-# frozen_string_literal: true
require "pro_tacts/config"
diff --git a/lib/pro_tacts/config.rb b/lib/pro_tacts/config.rb
index d8233ab..9781472 100644
--- a/lib/pro_tacts/config.rb
+++ b/lib/pro_tacts/config.rb
@@ -1,4 +1,3 @@
-# frozen_string_literal: true
module ProTacts
# Single source of truth for configuration read from the environment.
diff --git a/lib/pro_tacts/debug_logger.rb b/lib/pro_tacts/debug_logger.rb
index def352d..b278ada 100644
--- a/lib/pro_tacts/debug_logger.rb
+++ b/lib/pro_tacts/debug_logger.rb
@@ -1,7 +1,7 @@
-# frozen_string_literal: true
require "fileutils"
require "logger"
+require "pathname"
require "rack"
module ProTacts
@@ -18,10 +18,10 @@ module ProTacts
# (Logger syncs its own device), one timestamped line per dump. path
# "stderr" writes to the process's stderr.
def self.open_log(path)
- target = if path == "stderr"
+ target = if path.to_s == "stderr"
$stderr
else
- FileUtils.mkdir_p(File.dirname(path))
+ FileUtils.mkdir_p(Pathname.new(path).dirname)
path
end
@@ -48,17 +48,23 @@ module ProTacts
private
def log_request(env)
- write(">>", "#{env['REQUEST_METHOD']} #{full_path(env)} #{env['SERVER_PROTOCOL']}")
- each_header(env) { |name, value| write(">>", "#{name}: #{value}") }
+ write(">>", "#{env.fetch('REQUEST_METHOD')} #{full_path(env)} #{env.fetch('SERVER_PROTOCOL')}")
+ each_header(env) do |name, value|
+ write(">>", "#{name}: #{value}")
+ end
body = read_request_body(env)
write(">>", body) unless body.empty?
end
def log_response(status, headers, body)
write("<<", "#{status}#{reason(status)}")
- headers.each { |name, value| write("<<", "#{name}: #{value}") }
+ headers.each do |name, value|
+ write("<<", "#{name}: #{value}")
+ end
parts = []
- body.each { |part| parts << part }
+ body.each do |part|
+ parts << part
+ end
body.close if body.respond_to?(:close)
write("<<", parts.join) unless parts.join.empty?
parts
@@ -101,7 +107,9 @@ module ProTacts
end
def write(prefix, text)
- text.to_s.each_line(chomp: true) { |line| @logger.debug("#{prefix} #{line}") }
+ text.to_s.lines(chomp: true).each do |line|
+ @logger.debug("#{prefix} #{line}")
+ end
end
end
end
diff --git a/lib/pro_tacts/profile.rb b/lib/pro_tacts/profile.rb
index 0c5664b..08c8410 100644
--- a/lib/pro_tacts/profile.rb
+++ b/lib/pro_tacts/profile.rb
@@ -1,4 +1,3 @@
-# frozen_string_literal: true
require "nokogiri"
diff --git a/lib/pro_tacts/vcard.rb b/lib/pro_tacts/vcard.rb
index 7bdaa37..6636f71 100644
--- a/lib/pro_tacts/vcard.rb
+++ b/lib/pro_tacts/vcard.rb
@@ -1,4 +1,3 @@
-# frozen_string_literal: true
require "kdl"
@@ -23,14 +22,14 @@ module ProTacts
ALLOWED_PROPERTIES = {
"phone" => %w[type],
"email" => %w[type],
- "address" => %w[type]
+ "address" => %w[type],
}.freeze
TEXT_ESCAPES = {
"\\" => "\\\\",
";" => "\\;",
"," => "\\,",
- "\n" => "\\n"
+ "\n" => "\\n",
}.freeze
module_function
@@ -100,19 +99,19 @@ module ProTacts
end
def typed_property_lines(contact, kdl_name, vcard_name)
- contact.children.select { it.name == kdl_name }.map do |node|
+ contact.children.select { it.name == kdl_name }.map { |node|
validate_properties(node)
type = node.properties["type"]&.value
prefix = type ? "#{vcard_name};TYPE=#{type}" : vcard_name
"#{prefix}:#{escape(string_argument(node))}"
- end
+ }
end
# ADR's seven components in order: pobox, extended address, street,
# locality, region, postal code, country. The first two have no KDL
# counterpart and stay empty.
def address_lines(contact)
- contact.children.select { it.name == "address" }.map do |node|
+ contact.children.select { it.name == "address" }.map { |node|
validate_children(node, ADDRESS_PARTS, "address")
validate_properties(node)
parts = node.children
@@ -123,7 +122,7 @@ module ProTacts
type = node.properties["type"]&.value
prefix = type ? "ADR;TYPE=#{type}" : "ADR"
"#{prefix}:#{components(components)}"
- end
+ }
end
# Text values escape backslash, the component separator, and the
diff --git a/lib/pro_tacts/web.rb b/lib/pro_tacts/web.rb
index 1587cd4..d8b4797 100644
--- a/lib/pro_tacts/web.rb
+++ b/lib/pro_tacts/web.rb
@@ -1,4 +1,3 @@
-# frozen_string_literal: true
require "pro_tacts"
require "sentry-ruby"
@@ -141,7 +140,7 @@ module ProTacts
contact_etag = %("a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2")
collection_ctag = "ctag-2"
- depth = request.env["HTTP_DEPTH"] || "infinity"
+ depth = request.env.fetch("HTTP_DEPTH", "infinity")
# Check if this is an etag-only request (Depth:1 listing)
etag_only = body.include?("getetag") && !body.include?("displayname") && !body.include?("resourcetype")
diff --git a/lib/roda/plugins/dav_verbs.rb b/lib/roda/plugins/dav_verbs.rb
index 2a810cd..b9bef93 100644
--- a/lib/roda/plugins/dav_verbs.rb
+++ b/lib/roda/plugins/dav_verbs.rb
@@ -1,4 +1,3 @@
-# frozen_string_literal: true
require "roda"
diff --git a/tasks/libhegel.rake b/tasks/libhegel.rake
index b3d549e..526e203 100644
--- a/tasks/libhegel.rake
+++ b/tasks/libhegel.rake
@@ -1,4 +1,3 @@
-# frozen_string_literal: true
# Stages the native libhegel engine that the hegeltest gem drives — the
# gem is pre-release and bundles no binary yet. Once it ships a real gem
@@ -13,6 +12,7 @@
# whichever platform's asset landed there resolves.
require "digest"
+require "pathname"
require "rbconfig"
require "fileutils"
require "tmpdir"
@@ -22,7 +22,7 @@ LIBHEGEL_VERSION = Hegel::LIBHEGEL_VERSION
LIBHEGEL_ASSETS = {
"arm64-darwin" => "libhegel-darwin-arm64.dylib",
"aarch64-linux" => "libhegel-linux-arm64.so",
- "x86_64-linux" => "libhegel-linux-amd64.so"
+ "x86_64-linux" => "libhegel-linux-amd64.so",
}.freeze
# RbConfig's host_os carries version detail ("darwin25", "linux-gnu") that
@@ -36,29 +36,30 @@ rescue KeyError
raise "no published libhegel for #{RbConfig::CONFIG.fetch('host_cpu')}-#{RbConfig::CONFIG.fetch('host_os')}; build one and point HEGEL_LIBHEGEL_PATH at it"
end
-LIBHEGEL_DIR = File.expand_path("tmp/libhegel/#{LIBHEGEL_VERSION}", __dir__ + "/..")
-LIBHEGEL_PATH = File.join(LIBHEGEL_DIR, libhegel_asset_name)
+LIBHEGEL_DIR = Pathname.new(__dir__).parent / "tmp" / "libhegel" / LIBHEGEL_VERSION
+LIBHEGEL_PATH = LIBHEGEL_DIR / libhegel_asset_name
file LIBHEGEL_PATH do |task|
- asset = File.basename(task.name)
- Dir.mktmpdir do |staging|
+ asset = Pathname.new(task.name).basename.to_s
+ Dir.mktmpdir do |dir|
+ staging = Pathname.new(dir)
sh "gh", "release", "download", "v#{LIBHEGEL_VERSION}",
"--repo", "hegeldev/hegel-rust",
"--pattern", asset, "--pattern", "#{asset}.sha256",
- "--dir", staging, verbose: false
+ "--dir", dir, verbose: false
- expected = File.read(File.join(staging, "#{asset}.sha256")).split.first
- actual = Digest::SHA256.hexdigest(File.binread(File.join(staging, asset)))
+ expected = File.read(staging / "#{asset}.sha256").split.first
+ actual = Digest::SHA256.hexdigest(File.binread(staging / asset))
raise "SHA-256 mismatch for #{asset}: expected #{expected}, got #{actual}" unless actual == expected
FileUtils.mkdir_p(LIBHEGEL_DIR)
- FileUtils.mv(File.join(staging, asset), task.name)
+ FileUtils.mv(staging / asset, task.name)
end
end
# Keep a bare `rake test` self-contained: the engine resolves through
# HEGEL_LIBHEGEL_PATH when direnv has loaded .envrc, and through the
# staged copy otherwise.
-ENV["HEGEL_LIBHEGEL_PATH"] = LIBHEGEL_DIR if ENV["HEGEL_LIBHEGEL_PATH"].nil? || ENV["HEGEL_LIBHEGEL_PATH"].empty?
+ENV["HEGEL_LIBHEGEL_PATH"] = LIBHEGEL_DIR.to_s if ENV.fetch("HEGEL_LIBHEGEL_PATH", "").empty?
task test: LIBHEGEL_PATH
diff --git a/test/pro_tacts/exchange_fixtures.rb b/test/pro_tacts/exchange_fixtures.rb
index 16277d4..72a5a2d 100644
--- a/test/pro_tacts/exchange_fixtures.rb
+++ b/test/pro_tacts/exchange_fixtures.rb
@@ -1,12 +1,12 @@
-# frozen_string_literal: true
+require "pathname"
require "rack/test"
# Loads and replays the recorded macOS Contacts exchange in
# test/fixtures/macos-exchange. See that directory's README for provenance
# and the request/response file format.
module ExchangeFixtures
- DIRECTORY = File.expand_path("../fixtures/macos-exchange", __dir__)
+ DIRECTORY = Pathname.new(__dir__).parent / "fixtures" / "macos-exchange"
Step = Struct.new(:name, :method, :path, :headers, :body, keyword_init: true)
Response = Struct.new(:status, :headers, :body, keyword_init: true)
@@ -18,7 +18,7 @@ module ExchangeFixtures
class << self
def steps
Dir.children(DIRECTORY)
- .select { |child| File.directory?(File.join(DIRECTORY, child)) }
+ .select { (DIRECTORY / it).directory? }
.sort
.map { |name| Step.new(name:, **parse_request(name)) }
end
@@ -37,10 +37,10 @@ module ExchangeFixtures
# Rack env for the recorded headers: Content-Type and Depth are the only
# ones the app reads, but replaying all of them keeps the fidelity.
def env_for(step)
- step.headers.to_h do |name, value|
+ step.headers.to_h { |name, value|
key = name == "Content-Type" ? "CONTENT_TYPE" : "HTTP_#{name.tr('-', '_').upcase}"
[key, value]
- end
+ }
end
# Replays every recorded request against the app and rewrites the
@@ -57,7 +57,7 @@ module ExchangeFixtures
private
def read(name, file)
- File.read(File.join(DIRECTORY, name, file))
+ File.read(DIRECTORY / name / file)
end
def split_message(raw)
@@ -76,7 +76,7 @@ module ExchangeFixtures
content = ([response.status.to_s] + headers).join("\n") + "\n\n"
body = response.body.chomp
content = "#{content}#{body}\n" unless body.empty?
- File.write(File.join(DIRECTORY, step.name, "response"), content)
+ File.write(DIRECTORY / step.name / "response", content)
end
end
end
diff --git a/test/pro_tacts/test_config.rb b/test/pro_tacts/test_config.rb
index 9268eea..d32215e 100644
--- a/test/pro_tacts/test_config.rb
+++ b/test/pro_tacts/test_config.rb
@@ -1,4 +1,3 @@
-# frozen_string_literal: true
require "minitest/autorun"
diff --git a/test/pro_tacts/test_debug_logger.rb b/test/pro_tacts/test_debug_logger.rb
index 8ecd32f..fd76286 100644
--- a/test/pro_tacts/test_debug_logger.rb
+++ b/test/pro_tacts/test_debug_logger.rb
@@ -1,4 +1,3 @@
-# frozen_string_literal: true
require "minitest/autorun"
require "logger"
@@ -25,8 +24,8 @@ class DebugLoggerTest < Minitest::Test
"PATH_INFO" => "/dav/addressbook/",
"QUERY_STRING" => "",
"SERVER_PROTOCOL" => "HTTP/1.1",
- "rack.input" => StringIO.new(body)
- }.merge(headers.transform_keys { |k| "HTTP_#{k.to_s.upcase.tr('-', '_')}" })
+ "rack.input" => StringIO.new(body),
+ }.merge(headers.transform_keys { "HTTP_#{it.to_s.upcase.tr('-', '_')}" })
end
def test_dumps_request_line_with_method_path_and_protocol
@@ -53,7 +52,7 @@ class DebugLoggerTest < Minitest::Test
read = nil
app = ProTacts::DebugLogger.new(
->(e) { read = e["rack.input"].read; [200, {}, [""]] },
- logger: Logger.new(StringIO.new)
+ logger: Logger.new(StringIO.new),
)
app.call(env(body: "<x/>"))
@@ -86,9 +85,10 @@ class DebugLoggerTest < Minitest::Test
class OpenLogTest < Minitest::Test
def test_appends_timestamped_lines_to_a_file_it_creates
+ require "pathname"
require "tmpdir"
Dir.mktmpdir do |dir|
- path = File.join(dir, "nested", "debug.log")
+ path = Pathname.new(dir) / "nested" / "debug.log"
logger = ProTacts::DebugLogger.open_log(path)
logger.debug(">> PROPFIND / HTTP/1.1")
diff --git a/test/pro_tacts/test_macos_exchange.rb b/test/pro_tacts/test_macos_exchange.rb
index 9d0ae3b..594e97c 100644
--- a/test/pro_tacts/test_macos_exchange.rb
+++ b/test/pro_tacts/test_macos_exchange.rb
@@ -1,4 +1,3 @@
-# frozen_string_literal: true
require_relative "../test_helper"
require "rack/test"
diff --git a/test/pro_tacts/test_profile.rb b/test/pro_tacts/test_profile.rb
index 49ed2c1..3eb5bb5 100644
--- a/test/pro_tacts/test_profile.rb
+++ b/test/pro_tacts/test_profile.rb
@@ -1,4 +1,3 @@
-# frozen_string_literal: true
require_relative "../test_helper"
@@ -62,7 +61,7 @@ class ProfileTest < Minitest::Test
assert_equal [
"#{ProTacts::Profile::IDENTIFIER_PREFIX}-20260818ab12",
- "#{ProTacts::Profile::IDENTIFIER_PREFIX}-20260818cd34"
+ "#{ProTacts::Profile::IDENTIFIER_PREFIX}-20260818cd34",
], ProTacts::Profile.installed_identifiers(list_output)
end
diff --git a/test/pro_tacts/test_vcard.rb b/test/pro_tacts/test_vcard.rb
index d1ca07a..756c239 100644
--- a/test/pro_tacts/test_vcard.rb
+++ b/test/pro_tacts/test_vcard.rb
@@ -1,4 +1,3 @@
-# frozen_string_literal: true
require_relative "../test_helper"
@@ -127,7 +126,9 @@ class VCardTest < Minitest::Test
physical = vcard.split("\r\n")
assert_operator physical.length, :>, 1, "expected folding"
- physical.each { assert_operator it.bytesize, :<=, 75 }
+ physical.each do
+ assert_operator it.bytesize, :<=, 75
+ end
logical = unfold(physical)
assert_equal "FN:#{"x" * 30}#{("é" * 60)}", logical.find { it.start_with?("FN:") }
@@ -304,7 +305,7 @@ class VCardTest < Minitest::Test
# control characters that KDL forbids raw inside a string become \u
# escapes.
def kdl_string(text)
- escaped = normalize(text).chars.map do |char|
+ escaped = normalize(text).chars.map { |char|
case char
when "\\" then "\\\\"
when '"' then '\\"'
@@ -312,7 +313,7 @@ class VCardTest < Minitest::Test
when /[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/ then format('\u{%02x}', char.ord)
else char
end
- end.join
+ }.join
"\"#{escaped}\""
end
@@ -467,16 +468,16 @@ class VCardTest < Minitest::Test
given: tc.draw(optional(text(max_size: 20))),
additional: tc.draw(optional(text(max_size: 20))),
prefix: tc.draw(optional(text(max_size: 20))),
- suffix: tc.draw(optional(text(max_size: 20)))
+ suffix: tc.draw(optional(text(max_size: 20))),
}
phones = tc.draw(arrays(tuples(text(max_size: 30), optional(type)), max_size: 5))
emails = tc.draw(arrays(tuples(text(max_size: 30), optional(type)), max_size: 5))
addresses = tc.draw(arrays(
tuples(
text(max_size: 20), text(max_size: 20), text(max_size: 20),
- text(max_size: 20), text(max_size: 20), optional(type)
+ text(max_size: 20), text(max_size: 20), optional(type),
),
- max_size: 3
+ max_size: 3,
))
uid = tc.draw(uuids)
# The renderer rejects a components-only name whose parts are all
diff --git a/test/pro_tacts/test_web.rb b/test/pro_tacts/test_web.rb
index a381e98..f61aabf 100644
--- a/test/pro_tacts/test_web.rb
+++ b/test/pro_tacts/test_web.rb
@@ -1,4 +1,3 @@
-# frozen_string_literal: true
require_relative "../test_helper"
require "rack/test"
diff --git a/test/roda/plugins/test_dav_verbs.rb b/test/roda/plugins/test_dav_verbs.rb
index 7b8ac0c..31ef275 100644
--- a/test/roda/plugins/test_dav_verbs.rb
+++ b/test/roda/plugins/test_dav_verbs.rb
@@ -1,4 +1,3 @@
-# frozen_string_literal: true
require "minitest/autorun"
require "rack/test"
diff --git a/test/test_helper.rb b/test/test_helper.rb
index bc8d5e0..81d6508 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -1,4 +1,3 @@
-# frozen_string_literal: true
# Marks the process as running tests before anything requires the app, so
# web.rb skips Sentry.init and no SENTRY_DSN is needed.