SlideShare a Scribd company logo
Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止
Deep dive into
Ruby's require
Hiroshi SHIBATA @hsbt
2023/12/15 RubyConf Taiwan 2023
Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止
Hiroshi SHIBATA
https://hsbt.org
@hsbt
Ruby core team
RubyGems/Bundler team
Technical fellow at ANDPAD
Self introduction
Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止
Introduction of ANDPAD
Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止
Introduction of ANDPAD
Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止
What's feature of ANDPAD?
Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止
Learning Ruby's
require
Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止
Inside of `require` of Ruby
Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止
Basic knowlege of require
• `require` is major method in Ruby.
• `require` can handle Ruby and
C/Rust extension with your
platform like linux or macOS.
• `require` find $LOAD_PATH by your
installation path originally.
• `require_relative` find only
current path.
>> require 'rss'
=> true
>> require 'rss'
=> false
>> require "bigdecimal"
=> true
#
>> require "bigdecimal.bundle"
=> true
#
>> require "bigdecimal.so"
=> true
>> require_relative "rss"
...cannot load such file -- ...
Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止
load is a few different with require
• Ruby also has `load`. It's alternative of require.
• `load` didn't find your installation path. It find only current path.
• `load` need to extension of file like “.rb”.
• `load` can load ruby code many times.
>> load "psych"
(irb):1:in `load': cannot load such file -- psych (LoadError)
>> load "psych.rb"
=> true
>> load "psych.rb"
...lib/psych.rb:236: warning: already initialized constant
Psych::LIBYAML_VERSION
(snip)
=> true
Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止
autoload is more Ruby'ish way
• `require` and `load` are immediately evaluated by Ruby.
• But `autoload` is only load Ruby code when constant is
referenced.
• Ex. You call `Bundler::Definition`, Ruby will call `bundler/dependency.rb`
module Bundler
environment_preserver = EnvironmentPreserver.from_env
ORIGINAL_ENV = environment_preserver.restore
environment_preserver.replace_with_backup
SUDO_MUTEX = Thread::Mutex.new
autoload :Checksum, File.expand_path("bundler/checksum", __dir__)
autoload :Definition, File.expand_path("bundler/definition", __dir__)
autoload :Dependency, File.expand_path("bundler/dependency", __dir__)
(snip)
Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止
How load Ruby code from outside
of Ruby installation path?
Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止
We have RubyGems
• RubyGems is a package/library for the Ruby programming language
• We can install gems from rubygems.org
• gemspec is a file describing Gem::Specification
• This class for defining metadata including name, version,
platform, etc.
• RubyGems provide extended `require` and `gem` method for loading
gem for us.
Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止
gemspec in your code
• You can see gemspec with `Gem.loaded_specs` like this:
• `Gem::Speci
fi
cation#dependencies` is important parts of your application.
>> Gem.loaded_specs["rack"]
=>
Gem::Speci
fi
cation.new do |s|
s.name = "rack"
s.version = Gem::Version.new("2.2.8")
s.installed_by_version = Gem::Version.new("3.4.10")
s.authors = ["Leah Neukirchen"]
s.date = Time.utc(2023, 7, 31)
s.dependencies = [Gem::Dependency.new("minitest", Gem::Requirement.new(["~> 5.0"]), :development),
Gem::Dependency.new("minitest-sprint", Gem::Requirement.new([">= 0"]), :development),
Gem::Dependency.new("minitest-global_expectations", Gem::Requirement.new([">= 0"]), :development),
Gem::Dependency.new("rake", Gem::Requirement.new([">= 0"]), :development)]
s.description = "Rack provides a minimal, modular and adaptable interface for developingnweb
applications in Ruby. By wrapping HTTP requests and responses innthe simplest way possible, it uni
fi
es
and distills the API for webnservers, web frameworks, and software in between (the so-
callednmiddleware) into a single method call.n"
(...snip...)
end
Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止
Performance issues with RubyGems
• RubyGems extends `require` n `kernel_require.rb`
def require(path) # :doc:
return gem_original_require(path) unless
Gem.discover_gems_on_require
begin
RUBYGEMS_ACTIVATION_MONITOR.enter
path = path.to_path if path.respond_to? :to_path
if spec = Gem.
fi
nd_unresolved_default_spec(path)
# Ensure -I beats a default gem
resolved_path = begin
rp = nil
(snip)
$ wc -l *
165 kernel_require.rb
49 kernel_warn.rb
Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止
Why slow down with RubyGems?
• Find all of your gems: Gem::specification.find_by_path
def self.find_by_path(path)
path = path.dup.freeze
spec = @@spec_with_requirable_file[path] ||= stubs.find do |s|
s.contains_requirable_file? path
end || NOT_FOUND
spec.to_spec
end
This returns all of your gemspec
$ ruby -ve "t = Time.now; require 'bigdecimal'; p Time.now - t"
0.272687
$ set -e GEM_HOME
$ ruby -ve "t = Time.now; require 'bigdecimal'; p Time.now - t"
0.000786
$ ruby --disable-gems -ve "t = Time.now; require 'bigdecimal'; p Time.now - t"
0.000512
• My environment have 2800+ gems. RubyGems search them.
Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止
Introduce Kernel#gem method
• RubyGems provide `gem` method to
handle name and requirements.
• It's different with gem method on
Gemfile.
• This method inject gem path into your
$LOAD_PATH with requirements
>> require "rake"
=> true
>> Rake::VERSION
=> "13.1.0"
#
>> gem "rake", "13.0.0"
=> true
>> require "rake"
=> true
>> Rake::VERSION
=> "13.0.0"
#
>> gem "rake", "> 12", "< 13"
=> true
>> require "rake"
=> true
>> Rake::VERSION
=> "12.3.3"
>> orig_paths = $LOAD_PATH.dup
>> gem "rake", "> 12", "< 13"
=> true
>> $LOAD_PATH - orig_paths
=> ["/Users/hsbt/.local/share/gem/gems/rake-12.3.3/lib"]
Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止
Inside of gem method
• Kernel#gem create `Gem::Dependency` instance with name and
requirements. After that activate it.
def gem(gem_name, *requirements) # :doc:
(snip)
dep = Gem::Dependency.new(gem_name, *requirements)
loaded = Gem.loaded_specs[gem_name]
return false if loaded && dep.matches_spec?(loaded)
spec = dep.to_spec
if spec
if Gem::LOADED_SPECS_MUTEX.owned?
spec.activate
else
Gem::LOADED_SPECS_MUTEX.synchronize { spec.activate }
end
end
Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止
RubyGems has some problems
• RubyGems didn't have resolution mechanism with multiple libraries.
• If you activated Rails 7, you should specify all of related libraries
that support Rails 7.
• It's difficult to resolve only `gem` method.
• Extended `require` is slow if you have a lot of gems like Rails
application.
Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止
How works bundler?
Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止
Why Bundler come
here?
Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止
What’s Bundler?
# frozen_string_literal: true
source "https://rubygems.org"
gem "rake", ">= 11.1”
• Bundler is also package manager of Ruby language
• Bundler focused version locking feature
• Bundler extends a lot of RubyGems resources like gemspec.
• Bundler works with Gemfile like:
Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止
Introduction of Lockfile
• Bundler generates lockfile named Gemfile.lock from Gemfile with dependency
resolution.
• Bundler use this lockfile for version locking in your applicaiton
# Gemfile
# frozen_string_literal: true
source "https://rubygems.org"
gem "rss"
# Gemfile.lock
GEM
remote: https://rubygems.org/
specs:
rexml (3.2.5)
rss (0.2.9)
rexml
PLATFORMS
arm64-darwin-23
DEPENDENCIES
rss
BUNDLED WITH
2.5.0.dev
Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止
Performance issues
• Bundler also extends RubyGems classes/methods. It's enabled when we used Bundler.
• `Gem::Specification#extension_dir` needs to handle git resource of Gemfile like this:
# for gem “rails”, git: “https://github.com/rails/rails"
alias_method :rg_extension_dir, :extension_dir
def extension_dir
# following instance variable is already used in original method
# and that is the reason to prefix it with bundler_ and add rubocop exception
@bundler_extension_dir ||= if source.respond_to?(:extension_dir_name)
unique_extension_dir = [source.extension_dir_name,
File.basename(full_gem_path)].uniq.join("-")
File.expand_path(File.join(extensions_dir, unique_extension_dir))
else
rg_extension_dir
end
end
Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止
Basic case of Gemfile
and bundle exec
Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止
Basic case of Gemfile and Bundler
• How Bundler resolve to slow
down with RubyGems and
Bundler?
• I'll introduce it with
`bundle exec` command
with simple rss examples.
# Gemfile.lock
GEM
remote: https://rubygems.org/
specs:
rexml (3.2.5)
rss (0.2.9)
rexml
PLATFORMS
arm64-darwin-23
DEPENDENCIES
rss
BUNDLED WITH
2.5.0.dev
Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止
What's Bundler.setup
def setup(*groups)
@definition.ensure_equivalent_gemfile_and_lockfile if
Bundler.frozen_bundle?
# Has to happen first
clean_load_path
specs = @definition.specs_for(groups)
SharedHelpers.set_bundle_environment
Bundler.rubygems.replace_entrypoints(specs)
# Activate the specs
load_paths = specs.map do |spec|
check_for_activated_spec!(spec)
Bundler.rubygems.mark_loaded(spec)
spec.load_paths.reject {|path| $LOAD_PATH.include?(path) }
end.reverse.flatten
Bundler.rubygems.add_to_load_path(load_paths)
setup_manpath
lock(:preserve_unknown_sections => true)
self
end
• `Bundler.setup` and
`Bundler.require` is most
important parts of Bundler
• These methods
defined at `runtime.rb`.
• `bundle exec` call
`Bundler.setup` and
`Kernel.exec`.
Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止
Reset your environment with Bundler
@definition.ensure_equivalent_gemfile_and_lockfile if Bundler.frozen_bundle?
# Has to happen first
clean_load_path
• At first, Bundler update your lockfile and install new versions if it's
needed. After that, Reject gem paths that are not `require` yet.
def clean_load_path
loaded_gem_paths = Bundler.rubygems.loaded_gem_paths
$LOAD_PATH.reject! do |p|
resolved_path = resolve_path(p)
next if $LOADED_FEATURES.any? {|lf| lf.start_with?(resolved_path) }
loaded_gem_paths.delete(p)
end
$LOAD_PATH.uniq!
end
Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止
• `Bundler.definition` and `Bundler::Resolver` is core parts for this.
• Bundler.definition create instance of `Bundler::Resolver` and call
resolution methods inside `specs_for`.
• `specs` is instance of Bundler::SpecSet.
• Bundler inject `bundler` as dependency into Gemfile.
How select dependencies by Bundler.definition
specs = @definition.specs_for(groups)
SharedHelpers.set_bundle_environment
Bundler.rubygems.replace_entrypoints(specs)
>> specs.map(&:name)
=> ["bundler", "rexml", "rss"]
This method revert extended require by rubygems
Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止
Update your LOAD_PATH with scratch
~/.rbenv/versions/master/lib/ruby/gems/3.3.0+0/gems/rss-0.2.9/lib
~/.rbenv/versions/master/lib/ruby/gems/3.3.0+0/gems/rexml-3.2.5/lib
~/.rbenv/versions/master/lib/ruby/gems/3.3.0+0/gems/bundler-2.5.0.dev/lib
# Activate the specs
load_paths = specs.map do |spec|
check_for_activated_spec!(spec)
Bundler.rubygems.mark_loaded(spec)
spec.load_paths.reject {|path| $LOAD_PATH.include?(path) }
end.reverse.flatten
Bundler.rubygems.add_to_load_path(load_paths)
setup_manpath
lock(:preserve_unknown_sections => true)
self
• These logic is easy to understand. We generate paths generated by resolved gemspec.
`Gem::Specification#load_paths` returns load paths from gemspec.
• `load_paths` returns like this:
Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止
Future of standard libraries
Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止
What are default and
bundled gems?
Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止
What's Default gems
• The Ruby core team released "Default gems" to the rubygems.org.
• You can install standard libraries of Ruby via RubyGems.
• Default gems are openssl, psych, json, etc… You can see all of
default gems at https://stdgems.org/
• Rubygems have a detection method for default gems.
>> require 'rss'
=> true
>> Gem.loaded_specs["rss"].default_gem?
=> false
>> require 'openssl'
=> true
>> Gem.loaded_specs["openssl"].default_gem?
=> true
Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止
Basic scenario of Bundler resolution
• `Bundler.rubygems.replace_entrypoints` inject gemspecs of default
gems into dependencies of Gemfile.
specs = Declared in Gemfile: rails, nokogiri, sidekiq, etc...
default_spec = Default gems: csv, psych, json, etc...
+
Bundler.rubygems.default_stubs.each do |stub|
default_spec = stub.to_spec
default_spec_name = default_spec.name
next if specs_by_name.key?(default_spec_name)
specs << default_spec
specs_by_name[default_spec_name] = default_spec
end
Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止
What's Bundled gems
• We bundled *.gem and unpacked
fi
les to tarball package for Bundled
gems with `gems/bundled_gems` in ruby/ruby repository like this:
• `make install` installed Bundled gem your box.
Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止
The major problem for the bundled gems
If you use Bundler, you need to add the bundled gems into your Gem
fi
le.
source "https://rubygems.org"
gem “rss”
gem "bigdecimal" # You need to this always after Ruby 3.4
…
I need to consider to transition and migration plan for this.
But I have no idea yet. Maybe, I will add the some mechanism to Bundler internal
to care about this.
Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止
Warning feature of bundled gems
• I added warning feature about bundled
gems.
• You can see how handle bundled gems
like this.
module Gem::BUNDLED_GEMS
SINCE = {
"rexml" => "3.0.0",
"rss" => "3.0.0",
"webrick" => "3.0.0",
"matrix" => "3.1.0",
"net-ftp" => "3.1.0",
"net-imap" => "3.1.0",
"net-pop" => "3.1.0",
"net-smtp" => "3.1.0",
"prime" => "3.1.0",
"abbrev" => "3.4.0",
"base64" => "3.4.0",
"bigdecimal" => "3.4.0",
"csv" => "3.4.0",
"drb" => "3.4.0",
"getoptlong" => "3.4.0",
"mutex_m" => "3.4.0",
"nkf" => "3.4.0",
"observer" => "3.4.0",
"racc" => "3.4.0",
"resolv-replace" => "3.4.0",
"rinda" => "3.4.0",
"syslog" => "3.4.0",
}.freeze
$ cat -p Gemfile
source "https://rubygems.org"
gem "rss"
$ bundle exec irb
>> require "csv"
(irb):1: warning: csv which will no longer be part of
the default gems since Ruby 3.4.0. Add csv to your
Gemfile or gemspec.
=> true
Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止
How extend require in your use case?
We easily extend `require` like this. I only enabled this extension under
the bundler. I added this to monkey patch collection of bundler.
def replace_require(specs)
return if [::Kernel.singleton_class, ::Kernel].any? {|klass| klass.respond_to?(:no_warning_require) }
[::Kernel.singleton_class, ::Kernel].each do |kernel_class|
kernel_class.send(:alias_method, :no_warning_require, :require)
kernel_class.send(:define_method, :require) do |name|
if message = ::Gem::BUNDLED_GEMS.warning?(name, specs: specs)
warn message, :uplevel => 1
end
kernel_class.send(:no_warning_require, name)
end
if kernel_class == ::Kernel
kernel_class.send(:private, :require)
else
kernel_class.send(:public, :require)
end
end
end
Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止
We want Namespace feature
Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止
Activation issue of default gems
We still have a activation issue
with RubyGems and the default
gems like `json`.
How handle this problem?
You have already activated timeout 0.3.1, but
your Gemfile requires timeout 0.3.2. Since
timeout is a default gem, you can either remove
your dependency on it or try updating to a
newer version of bundler that supports timeout
as a default gem.
Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止
How handle activation problem?
1. Rewrite default gems to simple Ruby code
→ 😆
2. Vendoring approach when RubyGems uses only pure ruby libraries like URI.
→ 😩
3. Rewrite default gems written by C extension to pure ruby code.
→ 😩
Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止
Example for rewrite C to Ruby
You can use any versions of Psych.
Because RubyGems and Bundler use the
original Ruby implementation for YAML
serializer.
When RubyGems uses json-2.1.0, You
couldn’t use json 1.8.x. Because ruby
gems and rubygems.org never uses
JSON format.
Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止
We considered namespace feature with RubyGems and Bundler
tagomoris-san proposed namespace
feature for package system of Ruby.
It may help our activation problem.
Ex. RubyGems and Bundler will use JSON
under only their namespace. You can
activate any version of JSON on your
application.
Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止
Writing/Thinking Ruby continuously in 2024
RubyKaigi 2024 will be coming Okinawa island in Japan at May, 2024.
Looking forward seeing you again at RubyKaigi 2024 :wave:
Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止
Wrap up
Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止
Conclusion
• I talked about...
• Knowledge require, RubyGems, Bundler
• How works Bundler modify LOAD_PATH of Ruby
• How works extends require for use-cases
< Ruby is a programmer's best friend

More Related Content

PDF
How resolve Gem dependencies in your code?
PDF
Long journey of Ruby Standard library at RubyKaigi 2024
PDF
Long journey of Ruby standard library at RubyConf AU 2024
PDF
How resolve Gem dependencies in your code?
PDF
Introduction of Cybersecurity with Ruby at RedDotRubyConf 2024
PDF
Introduction of Cybersecurity with OSS at Code Europe 2024
PDF
RubyGems 3 & 4
PDF
OSS Tools: Creating a Reverse Engineering Plug-in for r2frida
How resolve Gem dependencies in your code?
Long journey of Ruby Standard library at RubyKaigi 2024
Long journey of Ruby standard library at RubyConf AU 2024
How resolve Gem dependencies in your code?
Introduction of Cybersecurity with Ruby at RedDotRubyConf 2024
Introduction of Cybersecurity with OSS at Code Europe 2024
RubyGems 3 & 4
OSS Tools: Creating a Reverse Engineering Plug-in for r2frida

Similar to Deep dive into Ruby's require - RubyConf Taiwan 2023 (20)

PPTX
Android Penetration Testing - Day 3
PPTX
Running your Spring Apps in the Cloud Javaone 2014
PDF
20200303 AWS Black Belt Online Seminar AWS Cloud Development Kit (CDK)
PDF
Terraform & Azure
PPS
Hacking Client Side Insecurities
PDF
Fernando+Simon+-+HrOUG2020-ZDLRA_What_you_need_to_know_to_understand_it.pdf
PDF
Project Helidon Overview (Japanese)
PDF
Securing Prometheus. Lessons Learned from OpenShift.pdf
PDF
Big Data Step-by-Step: Infrastructure 3/3: Taking it to the cloud... easily.....
PPTX
Hado“OPS” or Had “oops”
PDF
Ruby with cucmber
PDF
Android's security architecture
PDF
Xfocus xcon 2008_aks_oknock
PDF
RefCard RESTful API Design
PDF
Building Hadoop-as-a-Service with Pivotal Hadoop Distribution, Serengeti, & I...
 
PDF
RubyGems 3 & 4
PDF
The Future of Dependency Management for Ruby
PPTX
Not a Kubernetes fan? The state of PaaS in 2024
PDF
Damage Control
KEY
Sinatra for REST services
Android Penetration Testing - Day 3
Running your Spring Apps in the Cloud Javaone 2014
20200303 AWS Black Belt Online Seminar AWS Cloud Development Kit (CDK)
Terraform & Azure
Hacking Client Side Insecurities
Fernando+Simon+-+HrOUG2020-ZDLRA_What_you_need_to_know_to_understand_it.pdf
Project Helidon Overview (Japanese)
Securing Prometheus. Lessons Learned from OpenShift.pdf
Big Data Step-by-Step: Infrastructure 3/3: Taking it to the cloud... easily.....
Hado“OPS” or Had “oops”
Ruby with cucmber
Android's security architecture
Xfocus xcon 2008_aks_oknock
RefCard RESTful API Design
Building Hadoop-as-a-Service with Pivotal Hadoop Distribution, Serengeti, & I...
 
RubyGems 3 & 4
The Future of Dependency Management for Ruby
Not a Kubernetes fan? The state of PaaS in 2024
Damage Control
Sinatra for REST services
Ad

More from Hiroshi SHIBATA (20)

PDF
Ruby コミッターと歩む Ruby を用いたプロダクト開発
PDF
Why ANDPAD commit Ruby and RubyKaigi?
PDF
RailsGirls から始める エンジニアリングはじめの一歩
PDF
How to develop the Standard Libraries of Ruby?
PDF
The details of CI/CD environment for Ruby
PDF
Dependency Resolution with Standard Libraries
PDF
Roadmap for RubyGems 4 and Bundler 3
PDF
The Future of library dependency management of Ruby
PDF
Ruby Security the Hard Way
PDF
OSS Security the hard way
PDF
The Future of library dependency manageement of Ruby
PDF
The Future of Bundled Bundler
PDF
What's new in RubyGems3
PDF
Productive Organization with Ruby
PDF
Gems on Ruby
PDF
Gems on Ruby
PDF
How to distribute Ruby to the world
PDF
How to distribute Ruby to the world
PDF
Gemification for Ruby 2.5/3.0
PDF
Gemification for Ruby 2.5/3.0
Ruby コミッターと歩む Ruby を用いたプロダクト開発
Why ANDPAD commit Ruby and RubyKaigi?
RailsGirls から始める エンジニアリングはじめの一歩
How to develop the Standard Libraries of Ruby?
The details of CI/CD environment for Ruby
Dependency Resolution with Standard Libraries
Roadmap for RubyGems 4 and Bundler 3
The Future of library dependency management of Ruby
Ruby Security the Hard Way
OSS Security the hard way
The Future of library dependency manageement of Ruby
The Future of Bundled Bundler
What's new in RubyGems3
Productive Organization with Ruby
Gems on Ruby
Gems on Ruby
How to distribute Ruby to the world
How to distribute Ruby to the world
Gemification for Ruby 2.5/3.0
Gemification for Ruby 2.5/3.0
Ad

Recently uploaded (20)

PPTX
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
PDF
Encapsulation_ Review paper, used for researhc scholars
PPTX
Big Data Technologies - Introduction.pptx
PDF
Machine learning based COVID-19 study performance prediction
PDF
Bridging biosciences and deep learning for revolutionary discoveries: a compr...
PDF
Building Integrated photovoltaic BIPV_UPV.pdf
PDF
Unlocking AI with Model Context Protocol (MCP)
PDF
Empathic Computing: Creating Shared Understanding
PDF
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
PDF
NewMind AI Monthly Chronicles - July 2025
PDF
Network Security Unit 5.pdf for BCA BBA.
PDF
Reach Out and Touch Someone: Haptics and Empathic Computing
PPT
Teaching material agriculture food technology
PPTX
PA Analog/Digital System: The Backbone of Modern Surveillance and Communication
PDF
Diabetes mellitus diagnosis method based random forest with bat algorithm
PDF
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
PDF
The Rise and Fall of 3GPP – Time for a Sabbatical?
PPTX
A Presentation on Artificial Intelligence
PDF
Approach and Philosophy of On baking technology
PDF
Shreyas Phanse Resume: Experienced Backend Engineer | Java • Spring Boot • Ka...
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
Encapsulation_ Review paper, used for researhc scholars
Big Data Technologies - Introduction.pptx
Machine learning based COVID-19 study performance prediction
Bridging biosciences and deep learning for revolutionary discoveries: a compr...
Building Integrated photovoltaic BIPV_UPV.pdf
Unlocking AI with Model Context Protocol (MCP)
Empathic Computing: Creating Shared Understanding
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
NewMind AI Monthly Chronicles - July 2025
Network Security Unit 5.pdf for BCA BBA.
Reach Out and Touch Someone: Haptics and Empathic Computing
Teaching material agriculture food technology
PA Analog/Digital System: The Backbone of Modern Surveillance and Communication
Diabetes mellitus diagnosis method based random forest with bat algorithm
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
The Rise and Fall of 3GPP – Time for a Sabbatical?
A Presentation on Artificial Intelligence
Approach and Philosophy of On baking technology
Shreyas Phanse Resume: Experienced Backend Engineer | Java • Spring Boot • Ka...

Deep dive into Ruby's require - RubyConf Taiwan 2023

  • 1. Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止 Deep dive into Ruby's require Hiroshi SHIBATA @hsbt 2023/12/15 RubyConf Taiwan 2023
  • 2. Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止 Hiroshi SHIBATA https://hsbt.org @hsbt Ruby core team RubyGems/Bundler team Technical fellow at ANDPAD Self introduction
  • 3. Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止 Introduction of ANDPAD
  • 4. Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止 Introduction of ANDPAD
  • 5. Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止 What's feature of ANDPAD?
  • 6. Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止 Learning Ruby's require
  • 7. Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止 Inside of `require` of Ruby
  • 8. Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止 Basic knowlege of require • `require` is major method in Ruby. • `require` can handle Ruby and C/Rust extension with your platform like linux or macOS. • `require` find $LOAD_PATH by your installation path originally. • `require_relative` find only current path. >> require 'rss' => true >> require 'rss' => false >> require "bigdecimal" => true # >> require "bigdecimal.bundle" => true # >> require "bigdecimal.so" => true >> require_relative "rss" ...cannot load such file -- ...
  • 9. Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止 load is a few different with require • Ruby also has `load`. It's alternative of require. • `load` didn't find your installation path. It find only current path. • `load` need to extension of file like “.rb”. • `load` can load ruby code many times. >> load "psych" (irb):1:in `load': cannot load such file -- psych (LoadError) >> load "psych.rb" => true >> load "psych.rb" ...lib/psych.rb:236: warning: already initialized constant Psych::LIBYAML_VERSION (snip) => true
  • 10. Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止 autoload is more Ruby'ish way • `require` and `load` are immediately evaluated by Ruby. • But `autoload` is only load Ruby code when constant is referenced. • Ex. You call `Bundler::Definition`, Ruby will call `bundler/dependency.rb` module Bundler environment_preserver = EnvironmentPreserver.from_env ORIGINAL_ENV = environment_preserver.restore environment_preserver.replace_with_backup SUDO_MUTEX = Thread::Mutex.new autoload :Checksum, File.expand_path("bundler/checksum", __dir__) autoload :Definition, File.expand_path("bundler/definition", __dir__) autoload :Dependency, File.expand_path("bundler/dependency", __dir__) (snip)
  • 11. Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止 How load Ruby code from outside of Ruby installation path?
  • 12. Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止 We have RubyGems • RubyGems is a package/library for the Ruby programming language • We can install gems from rubygems.org • gemspec is a file describing Gem::Specification • This class for defining metadata including name, version, platform, etc. • RubyGems provide extended `require` and `gem` method for loading gem for us.
  • 13. Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止 gemspec in your code • You can see gemspec with `Gem.loaded_specs` like this: • `Gem::Speci fi cation#dependencies` is important parts of your application. >> Gem.loaded_specs["rack"] => Gem::Speci fi cation.new do |s| s.name = "rack" s.version = Gem::Version.new("2.2.8") s.installed_by_version = Gem::Version.new("3.4.10") s.authors = ["Leah Neukirchen"] s.date = Time.utc(2023, 7, 31) s.dependencies = [Gem::Dependency.new("minitest", Gem::Requirement.new(["~> 5.0"]), :development), Gem::Dependency.new("minitest-sprint", Gem::Requirement.new([">= 0"]), :development), Gem::Dependency.new("minitest-global_expectations", Gem::Requirement.new([">= 0"]), :development), Gem::Dependency.new("rake", Gem::Requirement.new([">= 0"]), :development)] s.description = "Rack provides a minimal, modular and adaptable interface for developingnweb applications in Ruby. By wrapping HTTP requests and responses innthe simplest way possible, it uni fi es and distills the API for webnservers, web frameworks, and software in between (the so- callednmiddleware) into a single method call.n" (...snip...) end
  • 14. Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止 Performance issues with RubyGems • RubyGems extends `require` n `kernel_require.rb` def require(path) # :doc: return gem_original_require(path) unless Gem.discover_gems_on_require begin RUBYGEMS_ACTIVATION_MONITOR.enter path = path.to_path if path.respond_to? :to_path if spec = Gem. fi nd_unresolved_default_spec(path) # Ensure -I beats a default gem resolved_path = begin rp = nil (snip) $ wc -l * 165 kernel_require.rb 49 kernel_warn.rb
  • 15. Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止 Why slow down with RubyGems? • Find all of your gems: Gem::specification.find_by_path def self.find_by_path(path) path = path.dup.freeze spec = @@spec_with_requirable_file[path] ||= stubs.find do |s| s.contains_requirable_file? path end || NOT_FOUND spec.to_spec end This returns all of your gemspec $ ruby -ve "t = Time.now; require 'bigdecimal'; p Time.now - t" 0.272687 $ set -e GEM_HOME $ ruby -ve "t = Time.now; require 'bigdecimal'; p Time.now - t" 0.000786 $ ruby --disable-gems -ve "t = Time.now; require 'bigdecimal'; p Time.now - t" 0.000512 • My environment have 2800+ gems. RubyGems search them.
  • 16. Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止 Introduce Kernel#gem method • RubyGems provide `gem` method to handle name and requirements. • It's different with gem method on Gemfile. • This method inject gem path into your $LOAD_PATH with requirements >> require "rake" => true >> Rake::VERSION => "13.1.0" # >> gem "rake", "13.0.0" => true >> require "rake" => true >> Rake::VERSION => "13.0.0" # >> gem "rake", "> 12", "< 13" => true >> require "rake" => true >> Rake::VERSION => "12.3.3" >> orig_paths = $LOAD_PATH.dup >> gem "rake", "> 12", "< 13" => true >> $LOAD_PATH - orig_paths => ["/Users/hsbt/.local/share/gem/gems/rake-12.3.3/lib"]
  • 17. Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止 Inside of gem method • Kernel#gem create `Gem::Dependency` instance with name and requirements. After that activate it. def gem(gem_name, *requirements) # :doc: (snip) dep = Gem::Dependency.new(gem_name, *requirements) loaded = Gem.loaded_specs[gem_name] return false if loaded && dep.matches_spec?(loaded) spec = dep.to_spec if spec if Gem::LOADED_SPECS_MUTEX.owned? spec.activate else Gem::LOADED_SPECS_MUTEX.synchronize { spec.activate } end end
  • 18. Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止 RubyGems has some problems • RubyGems didn't have resolution mechanism with multiple libraries. • If you activated Rails 7, you should specify all of related libraries that support Rails 7. • It's difficult to resolve only `gem` method. • Extended `require` is slow if you have a lot of gems like Rails application.
  • 19. Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止 How works bundler?
  • 20. Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止 Why Bundler come here?
  • 21. Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止 What’s Bundler? # frozen_string_literal: true source "https://rubygems.org" gem "rake", ">= 11.1” • Bundler is also package manager of Ruby language • Bundler focused version locking feature • Bundler extends a lot of RubyGems resources like gemspec. • Bundler works with Gemfile like:
  • 22. Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止 Introduction of Lockfile • Bundler generates lockfile named Gemfile.lock from Gemfile with dependency resolution. • Bundler use this lockfile for version locking in your applicaiton # Gemfile # frozen_string_literal: true source "https://rubygems.org" gem "rss" # Gemfile.lock GEM remote: https://rubygems.org/ specs: rexml (3.2.5) rss (0.2.9) rexml PLATFORMS arm64-darwin-23 DEPENDENCIES rss BUNDLED WITH 2.5.0.dev
  • 23. Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止 Performance issues • Bundler also extends RubyGems classes/methods. It's enabled when we used Bundler. • `Gem::Specification#extension_dir` needs to handle git resource of Gemfile like this: # for gem “rails”, git: “https://github.com/rails/rails" alias_method :rg_extension_dir, :extension_dir def extension_dir # following instance variable is already used in original method # and that is the reason to prefix it with bundler_ and add rubocop exception @bundler_extension_dir ||= if source.respond_to?(:extension_dir_name) unique_extension_dir = [source.extension_dir_name, File.basename(full_gem_path)].uniq.join("-") File.expand_path(File.join(extensions_dir, unique_extension_dir)) else rg_extension_dir end end
  • 24. Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止 Basic case of Gemfile and bundle exec
  • 25. Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止 Basic case of Gemfile and Bundler • How Bundler resolve to slow down with RubyGems and Bundler? • I'll introduce it with `bundle exec` command with simple rss examples. # Gemfile.lock GEM remote: https://rubygems.org/ specs: rexml (3.2.5) rss (0.2.9) rexml PLATFORMS arm64-darwin-23 DEPENDENCIES rss BUNDLED WITH 2.5.0.dev
  • 26. Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止 What's Bundler.setup def setup(*groups) @definition.ensure_equivalent_gemfile_and_lockfile if Bundler.frozen_bundle? # Has to happen first clean_load_path specs = @definition.specs_for(groups) SharedHelpers.set_bundle_environment Bundler.rubygems.replace_entrypoints(specs) # Activate the specs load_paths = specs.map do |spec| check_for_activated_spec!(spec) Bundler.rubygems.mark_loaded(spec) spec.load_paths.reject {|path| $LOAD_PATH.include?(path) } end.reverse.flatten Bundler.rubygems.add_to_load_path(load_paths) setup_manpath lock(:preserve_unknown_sections => true) self end • `Bundler.setup` and `Bundler.require` is most important parts of Bundler • These methods defined at `runtime.rb`. • `bundle exec` call `Bundler.setup` and `Kernel.exec`.
  • 27. Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止 Reset your environment with Bundler @definition.ensure_equivalent_gemfile_and_lockfile if Bundler.frozen_bundle? # Has to happen first clean_load_path • At first, Bundler update your lockfile and install new versions if it's needed. After that, Reject gem paths that are not `require` yet. def clean_load_path loaded_gem_paths = Bundler.rubygems.loaded_gem_paths $LOAD_PATH.reject! do |p| resolved_path = resolve_path(p) next if $LOADED_FEATURES.any? {|lf| lf.start_with?(resolved_path) } loaded_gem_paths.delete(p) end $LOAD_PATH.uniq! end
  • 28. Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止 • `Bundler.definition` and `Bundler::Resolver` is core parts for this. • Bundler.definition create instance of `Bundler::Resolver` and call resolution methods inside `specs_for`. • `specs` is instance of Bundler::SpecSet. • Bundler inject `bundler` as dependency into Gemfile. How select dependencies by Bundler.definition specs = @definition.specs_for(groups) SharedHelpers.set_bundle_environment Bundler.rubygems.replace_entrypoints(specs) >> specs.map(&:name) => ["bundler", "rexml", "rss"] This method revert extended require by rubygems
  • 29. Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止 Update your LOAD_PATH with scratch ~/.rbenv/versions/master/lib/ruby/gems/3.3.0+0/gems/rss-0.2.9/lib ~/.rbenv/versions/master/lib/ruby/gems/3.3.0+0/gems/rexml-3.2.5/lib ~/.rbenv/versions/master/lib/ruby/gems/3.3.0+0/gems/bundler-2.5.0.dev/lib # Activate the specs load_paths = specs.map do |spec| check_for_activated_spec!(spec) Bundler.rubygems.mark_loaded(spec) spec.load_paths.reject {|path| $LOAD_PATH.include?(path) } end.reverse.flatten Bundler.rubygems.add_to_load_path(load_paths) setup_manpath lock(:preserve_unknown_sections => true) self • These logic is easy to understand. We generate paths generated by resolved gemspec. `Gem::Specification#load_paths` returns load paths from gemspec. • `load_paths` returns like this:
  • 30. Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止 Future of standard libraries
  • 31. Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止 What are default and bundled gems?
  • 32. Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止 What's Default gems • The Ruby core team released "Default gems" to the rubygems.org. • You can install standard libraries of Ruby via RubyGems. • Default gems are openssl, psych, json, etc… You can see all of default gems at https://stdgems.org/ • Rubygems have a detection method for default gems. >> require 'rss' => true >> Gem.loaded_specs["rss"].default_gem? => false >> require 'openssl' => true >> Gem.loaded_specs["openssl"].default_gem? => true
  • 33. Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止 Basic scenario of Bundler resolution • `Bundler.rubygems.replace_entrypoints` inject gemspecs of default gems into dependencies of Gemfile. specs = Declared in Gemfile: rails, nokogiri, sidekiq, etc... default_spec = Default gems: csv, psych, json, etc... + Bundler.rubygems.default_stubs.each do |stub| default_spec = stub.to_spec default_spec_name = default_spec.name next if specs_by_name.key?(default_spec_name) specs << default_spec specs_by_name[default_spec_name] = default_spec end
  • 34. Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止 What's Bundled gems • We bundled *.gem and unpacked fi les to tarball package for Bundled gems with `gems/bundled_gems` in ruby/ruby repository like this: • `make install` installed Bundled gem your box.
  • 35. Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止 The major problem for the bundled gems If you use Bundler, you need to add the bundled gems into your Gem fi le. source "https://rubygems.org" gem “rss” gem "bigdecimal" # You need to this always after Ruby 3.4 … I need to consider to transition and migration plan for this. But I have no idea yet. Maybe, I will add the some mechanism to Bundler internal to care about this.
  • 36. Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止 Warning feature of bundled gems • I added warning feature about bundled gems. • You can see how handle bundled gems like this. module Gem::BUNDLED_GEMS SINCE = { "rexml" => "3.0.0", "rss" => "3.0.0", "webrick" => "3.0.0", "matrix" => "3.1.0", "net-ftp" => "3.1.0", "net-imap" => "3.1.0", "net-pop" => "3.1.0", "net-smtp" => "3.1.0", "prime" => "3.1.0", "abbrev" => "3.4.0", "base64" => "3.4.0", "bigdecimal" => "3.4.0", "csv" => "3.4.0", "drb" => "3.4.0", "getoptlong" => "3.4.0", "mutex_m" => "3.4.0", "nkf" => "3.4.0", "observer" => "3.4.0", "racc" => "3.4.0", "resolv-replace" => "3.4.0", "rinda" => "3.4.0", "syslog" => "3.4.0", }.freeze $ cat -p Gemfile source "https://rubygems.org" gem "rss" $ bundle exec irb >> require "csv" (irb):1: warning: csv which will no longer be part of the default gems since Ruby 3.4.0. Add csv to your Gemfile or gemspec. => true
  • 37. Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止 How extend require in your use case? We easily extend `require` like this. I only enabled this extension under the bundler. I added this to monkey patch collection of bundler. def replace_require(specs) return if [::Kernel.singleton_class, ::Kernel].any? {|klass| klass.respond_to?(:no_warning_require) } [::Kernel.singleton_class, ::Kernel].each do |kernel_class| kernel_class.send(:alias_method, :no_warning_require, :require) kernel_class.send(:define_method, :require) do |name| if message = ::Gem::BUNDLED_GEMS.warning?(name, specs: specs) warn message, :uplevel => 1 end kernel_class.send(:no_warning_require, name) end if kernel_class == ::Kernel kernel_class.send(:private, :require) else kernel_class.send(:public, :require) end end end
  • 38. Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止 We want Namespace feature
  • 39. Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止 Activation issue of default gems We still have a activation issue with RubyGems and the default gems like `json`. How handle this problem? You have already activated timeout 0.3.1, but your Gemfile requires timeout 0.3.2. Since timeout is a default gem, you can either remove your dependency on it or try updating to a newer version of bundler that supports timeout as a default gem.
  • 40. Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止 How handle activation problem? 1. Rewrite default gems to simple Ruby code → 😆 2. Vendoring approach when RubyGems uses only pure ruby libraries like URI. → 😩 3. Rewrite default gems written by C extension to pure ruby code. → 😩
  • 41. Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止 Example for rewrite C to Ruby You can use any versions of Psych. Because RubyGems and Bundler use the original Ruby implementation for YAML serializer. When RubyGems uses json-2.1.0, You couldn’t use json 1.8.x. Because ruby gems and rubygems.org never uses JSON format.
  • 42. Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止 We considered namespace feature with RubyGems and Bundler tagomoris-san proposed namespace feature for package system of Ruby. It may help our activation problem. Ex. RubyGems and Bundler will use JSON under only their namespace. You can activate any version of JSON on your application.
  • 43. Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止 Writing/Thinking Ruby continuously in 2024 RubyKaigi 2024 will be coming Okinawa island in Japan at May, 2024. Looking forward seeing you again at RubyKaigi 2024 :wave:
  • 44. Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止 Wrap up
  • 45. Copyright © 2020 Present ANDPAD Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止 Conclusion • I talked about... • Knowledge require, RubyGems, Bundler • How works Bundler modify LOAD_PATH of Ruby • How works extends require for use-cases < Ruby is a programmer's best friend