Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ruby.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,4 @@ jobs:
bundler-cache: true

- name: Run tests
run: bundle exec rake test
run: AUTOLOAD=1 bundle exec rake test
3 changes: 3 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ Metrics/CyclomaticComplexity:
Enabled: false
Max: 6

Lint/EmptyClass:
AllowComments: true

Metrics/MethodLength:
Description: This cop checks if the length of a method exceeds some maximum value.
Enabled: false
Expand Down
2 changes: 2 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ PATH
specs:
faker (3.6.0)
i18n (>= 1.8.11, < 2)
zeitwerk (~> 2.7.3)

GEM
remote: https://rubygems.org/
Expand Down Expand Up @@ -93,6 +94,7 @@ GEM
unicode-emoji (~> 4.1)
unicode-emoji (4.2.0)
yard (0.9.38)
zeitwerk (2.7.4)

PLATFORMS
arm64-darwin
Expand Down
10 changes: 10 additions & 0 deletions benchmark/load.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# frozen_string_literal: true

require 'benchmark/ips'

Benchmark.ips do |x|
x.report('require') { system('ruby load_faker.rb') }
x.report('autoload') { system('AUTOLOAD=1 ruby load_faker.rb') }

x.compare!(order: :baseline)
end
8 changes: 8 additions & 0 deletions benchmark/load_faker.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/usr/bin/env ruby
# frozen_string_literal: true

if defined?(Faker)
raise 'fake is already defined...'
end

load('/Users/stefannibrasil/projects/faker/lib/faker.rb')
59 changes: 59 additions & 0 deletions experiments/autoload.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Autoload with zeitwerk experiment results

Branch: [sb-autoload-zeitwerk-experiment-3207](https://github.com/faker-ruby/faker/compare/sb-autoload-zeitwerk-experiment-3207?expand=1)
Date: February 17th, 2026
Owner(s): Stefanni Brasil and Thiago Araujo

## Impact

We want to compare improving faker's performance by lazy loading the generators or autoloading with Zeitwerk. This document shows the benchmarks, and other changes needed to configure Zeitwerk.

With the experiments documented, we can assess the pros and cons of maintainability, fewer breaking changes, easier adoption factors as guiding points for choosing the strategy we will move forward.

### Changes needed

- load and require generators in the correct order

Similarly to the [lazy load experiment](./lazy_load.md), it would be required to load `faker/music` and `faker/internet` first, before the nested namespaces such as `Faker::Music::BossaNova`, as they inherit from class `Music`, for example.

- added a runtime dependency for the library

- Zeitwerk 2.7 requires Ruby >= 3.2. This isn't a deal breaker because we will remove EOL Ruby 3.1 soon, but it would require releasing that version separately.

- our other goal besides improving performance, is allowing users to create their own Faker generators. Zeitwerk scans the file systems to setup the autoloads, so it would not setup autoloads for these external generators.

#### File location changes

To prevent other generators from erroring out due to namespace clashing, some generators have to be moved around (ex. `Faker::Quote` was moved from `/faker/quotes/quote` to `faker/default/quote`). Users can still use the generators as before, their namespaces didn't change.

### Benefits

- less code changes than lazy loading, and lots of customization options available (i.e., eager loading)
- code is extremely faster, but a bit slower than lazy loading
- we can enable this as an opt-in configuration

## Results

Machine specs: Apple M1 Pro 16GB memory on MacOS Sequoia 15.7.3.

profiler:

[AUTOLOAD=1 bundle exec vernier run -- ruby -e "require 'faker'"](https://share.firefox.dev/4aAJJee)
[bundle exec vernier run -- ruby -e "require 'faker'"](https://share.firefox.dev/4bWViih)

benchmark:

```sh
benchmark % ruby load.rb
ruby 3.3.10 (2025-10-23 revision 343ea05002) [arm64-darwin24]
Warming up --------------------------------------
require 1.000 i/100ms
autoload 1.000 i/100ms
Calculating -------------------------------------
require 6.026 (± 0.0%) i/s (165.96 ms/i) - 31.000 in 5.145463s
autoload 11.730 (± 0.0%) i/s (85.25 ms/i) - 59.000 in 5.032426s

Comparison:
require: 6.0 i/s
autoload: 11.7 i/s - 1.95x faster
```
7 changes: 4 additions & 3 deletions experiments/lazy_load.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Lazy load experiment results

Branch: sb-ta/lazy-load-experiment
Branch: [sb-ta/lazy-load-experiment](https://github.com/faker-ruby/faker/compare/main...sb-ta/lazy-load-experiment)
Date: February 10th, 2026
Owner(s): Stefanni Brasil and Thiago Araujo

Expand All @@ -21,11 +21,12 @@ To prevent other generators from erroring out due to namespace clashing, some ge

- no additional dependencies needed
- code is extremely faster
- we can enable this as an opt-in configuration
- no breaking changes
- we can enable this as an opt-in configuration, but any other customization would need to be implemented

## Results

Machine specs: Apple M1 Pro 16GB memory on MacOS Sequoia 15.7.3..
Machine specs: Apple M1 Pro 16GB memory on MacOS Sequoia 15.7.3.

profiler:

Expand Down
1 change: 1 addition & 0 deletions faker.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,5 @@ Gem::Specification.new do |spec|
spec.metadata['rubygems_mfa_required'] = 'true'

spec.add_dependency('i18n', '>= 1.8.11', '< 2')
spec.add_dependency 'zeitwerk', '~> 2.7.3'
end
28 changes: 26 additions & 2 deletions lib/faker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

require 'psych'
require 'i18n'
require 'zeitwerk'

autoload(:OpenSSL, 'openssl')

Expand Down Expand Up @@ -277,5 +278,28 @@ def disable_enforce_available_locales
end
end

# require faker objects
Dir.glob(File.join(mydir, 'faker', '/**/*.rb')).each { |file| require file }
if ENV['AUTOLOAD'] == '1'
loader = Zeitwerk::Loader.new
loader.tag = 'faker'
loader.push_dir(File.join(mydir, 'faker'), namespace: Faker)
loader.ignore("#{mydir}/faker/version.rb")
loader.inflector.inflect(
'dnd' => 'DnD',
'final_fantasy_xiv' => 'FinalFantasyXIV',
'html' => 'HTML',
'the_it_crowd' => 'TheITCrowd',
'http' => 'HTTP'
)
loader.collapse(
"#{mydir}/faker/default"
)
loader.setup
end

if ENV['AUTOLOAD'] != '1'
rb_files = []
rb_files << File.join(mydir, 'faker', '*.rb')
rb_files << File.join(mydir, 'faker', '/**/*.rb')

Dir.glob(rb_files).each { |file| require file }
end
7 changes: 7 additions & 0 deletions lib/faker/blockchain.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

module Faker
class Blockchain
# reopening class
end
end
7 changes: 7 additions & 0 deletions lib/faker/books.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

module Faker
class Books
# reopening class
end
end
7 changes: 7 additions & 0 deletions lib/faker/creature.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

module Faker
class Creature
# reopening class
end
end
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
7 changes: 7 additions & 0 deletions lib/faker/fantasy.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

module Faker
class Fantasy
# reopening class
end
end
7 changes: 7 additions & 0 deletions lib/faker/games.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

module Faker
class Games
# reopening class
end
end
File renamed without changes.
File renamed without changes.
7 changes: 7 additions & 0 deletions lib/faker/japanese_media.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

module Faker
class JapaneseMedia
# reopening class
end
end
7 changes: 7 additions & 0 deletions lib/faker/locations.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

module Faker
class Locations
# reopening class
end
end
7 changes: 7 additions & 0 deletions lib/faker/movies.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

module Faker
class Movies
# reopening class
end
end
File renamed without changes.
2 changes: 0 additions & 2 deletions lib/faker/music/bossa_nova.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# frozen_string_literal: true

require_relative 'music'

module Faker
class Music
class BossaNova < Base
Expand Down
2 changes: 0 additions & 2 deletions lib/faker/music/grateful_dead.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# frozen_string_literal: true

require_relative 'music'

module Faker
class Music
class GratefulDead < Base
Expand Down
2 changes: 0 additions & 2 deletions lib/faker/music/pearl_jam.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# frozen_string_literal: true

require_relative 'music'

module Faker
class Music
class PearlJam < Base
Expand Down
2 changes: 0 additions & 2 deletions lib/faker/music/rush.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# frozen_string_literal: true

require_relative 'music'

module Faker
class Music
class Rush < Base
Expand Down
2 changes: 0 additions & 2 deletions lib/faker/music/smashing_pumpkins.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# frozen_string_literal: true

require_relative 'music'

module Faker
class Music
class SmashingPumpkins < Base
Expand Down
7 changes: 7 additions & 0 deletions lib/faker/quotes.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

module Faker
class Quotes
# reopening class
end
end
7 changes: 7 additions & 0 deletions lib/faker/religion.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

module Faker
module Religion
# reopening class
end
end
7 changes: 7 additions & 0 deletions lib/faker/sports.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

module Faker
class Sports
# reopening class
end
end
7 changes: 7 additions & 0 deletions lib/faker/travel.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

module Faker
class Travel
# reopening class
end
end
7 changes: 7 additions & 0 deletions lib/faker/tv_shows.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

module Faker
class TvShows
# reopening class
end
end
1 change: 1 addition & 0 deletions test/test_i18n_reload.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ def test_faker_i18n
code = <<-RUBY
require 'bundler/inline'
require 'test/unit'
require 'zeitwerk'

gemfile do
source 'https://rubygems.org'
Expand Down
Loading