From 2792247757d394857d3e41d834e4c0b0f48803d8 Mon Sep 17 00:00:00 2001 From: Connor Shea <2977353+connorshea@users.noreply.github.com> Date: Thu, 2 Jul 2026 15:45:50 -0600 Subject: [PATCH] Add fast paths to Base.fetch/fetch_all/parse and Name.first_name - fetch/fetch_all: detect regex-shaped values with start_with?/end_with? instead of two Regexp matches (which allocate MatchData) per fetch. - parse: skip the token scan entirely when the fetched value contains no interpolation token; plain values go straight to numerify. - Name.first_name: call parse once instead of twice when the result is non-empty. Benchmark (Ruby 3.4.9, arm64-darwin25, benchmark-ips): require 'benchmark/ips' require 'faker' Benchmark.ips do |x| x.config(warmup: 1, time: 2) x.report('Name.first_name') { Faker::Name.first_name } x.report('Name.name') { Faker::Name.name } x.report('Address.city') { Faker::Address.city } end Results: main: Name.first_name 30.582k (+/-15.3%) i/s Name.name 18.900k (+/- 1.2%) i/s Address.city 21.428k (+/- 4.6%) i/s this commit: Name.first_name 61.882k (+/-14.6%) i/s (~2.0x) Name.name 28.181k (+/- 0.7%) i/s (~1.5x) Address.city 30.575k (+/- 0.7%) i/s (~1.4x) --- lib/faker.rb | 7 +++++-- lib/faker/default/name.rb | 7 ++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/faker.rb b/lib/faker.rb index a5d7d472b9..497b080078 100644 --- a/lib/faker.rb +++ b/lib/faker.rb @@ -121,7 +121,7 @@ def regexify(reg) # with an array of values and selecting one of them. def fetch(key) fetched = sample(translate("faker.#{key}")) - if fetched&.match(%r{^/}) && fetched.match(%r{/$}) # A regex + if fetched.is_a?(::String) && fetched.start_with?('/') && fetched.end_with?('/') # A regex regexify(fetched) else fetched @@ -133,7 +133,7 @@ def fetch(key) def fetch_all(key) fetched = translate("faker.#{key}") fetched = fetched.last if fetched.size <= 1 - if !fetched.respond_to?(:sample) && fetched.match(%r{^/}) && fetched.match(%r{/$}) # A regex + if !fetched.respond_to?(:sample) && fetched.is_a?(::String) && fetched.start_with?('/') && fetched.end_with?('/') # A regex regexify(fetched) else fetched @@ -146,6 +146,9 @@ def fetch_all(key) def parse(key) fetched = fetch(key) + # A plain value cannot contain any tokens, so skip scanning for them + return numerify(fetched) unless fetched.include?('#{') + parts = fetched.scan(/(\(?)#\{([A-Za-z]+\.)?([^}]+)\}([^#]++)?/).map do |prefix, kls, meth, etc| # If the token had a class Prefix (e.g., Name.first_name) # grab the constant, otherwise use self diff --git a/lib/faker/default/name.rb b/lib/faker/default/name.rb index 7c3f886a49..fc30c1722f 100644 --- a/lib/faker/default/name.rb +++ b/lib/faker/default/name.rb @@ -41,11 +41,8 @@ def name_with_middle # # @faker.version 0.9.0 def first_name - if parse('name.first_name').empty? - fetch('name.first_name') - else - parse('name.first_name') - end + parsed = parse('name.first_name') + parsed.empty? ? fetch('name.first_name') : parsed end ##