
jeremyevans/ruby-refrigerator: Freeze all core ruby classes
Refrigerator provides a simple way to freeze all Ruby core classes and modules. It is designed for use in production and testing to ensure that no code makes unintended changes to core classes or modules at runtime.
gem install refrigerator
The source code is available on GitHub: github.com/jeremyevans/ruby-refrigerator
After all of your application’s code is loaded, you can call freeze_core
Ways to freeze all core categories:
require 'refrigerator' Refrigerator.freeze_core
This will freeze all core classes so modifications to them at runtime will throw exceptions.
In a rack-based application, a good place to call “freeze_core” is at the end of the “config.ru” file.
You can also pass the :except option to freeze_core
With an array of category names that is not frozen:
Refrigerator.freeze_core(:except=>['BasicObject'])
One reason to exclude certain categories is because you know they will be modified at runtime.
A refrigerator is also provided check_require
Method to check if the library has made modifications to core classes. It allows you to easily see what’s failing when you try to require a library with frozen cores, and provides some options you can use to try to get the requirements to not throw exceptions. This allows you to view changes made to core categories by the library. This is a basic usage example:
Refrigerator.check_require('open3', :modules=>[:Open3])
this check_require
The method takes the following options:
- :module
-
Defines the given mod name before freezing the core (array of symbols)
- :course
-
Define the given category name before freezing the core (symbol array or two element array with category name symbol and supercategory name symbol)
- :exclude
-
Exclude the given category/mod name from freezing (array of strings)
- :depending on
-
Any dependencies needed before freezing the core (array of strings)
Without any choice, check_require
May throw an exception as it freezes the core before requesting, failing if the required archive attempts to add a class or module to the global namespace. The presence of the :modules and :classes options allows you to predefine classes so that the required files will reopen existing classes instead of defining new ones.
The :depends option can be easily used to load all dependencies of a required file before freezing the core. This is also necessary in most cases, especially when using stdlib, since many stdlib files modify core classes in ways other than adding modules or classes. The :exclude option is basically a last resort where you can disable freezing of required libraries if you know they modify some core classes.
Here is an example using Sequel, a ruby library toolkit:
Refrigerator.check_require 'sequel', :depends=>%w'bigdecimal date thread time uri', :modules=>[:Sequel]
Here’s another example using Roda, a Ruby web toolkit:
Refrigerator.check_require 'roda', :depends=>%w'rack uri fileutils set tmpdir tempfile thread date time', :classes=>[:Roda]
Note that many stdlib libraries will fail check_require
Unless you use the :exclude option, for example, date
:
Refrigerator.check_require 'date', :classes=>[:Date, [:DateTime, :Date]] # Fails due to Time#to_date addition Refrigerator.check_require 'date', :classes=>[:Date, [:DateTime, :Date]], :exclude=>['Time'] # => true
The refrigerator is shipped with check_require
Binary file that provides access rights Refrigerator.check_require
from the command line. This is the usage:
$ bin/check_require Usage: check_require [options] path Options: -m, --modules [Module1,Module2] define given modules under Object before freezing core classes -c [Class1,Class2/SuperclassOfClass2], --classes define given classes under Object before freezing core classes -r, --require [foo,bar/baz] require given libraries before freezing core classes -e, --exclude [Object,Array] exclude given core classes from freezing -h, -?, --help Show this message
You can use this to easily check if there is an issue with the ruby library when needed. For example, let’s try open3
:
$ bin/check_require open3 Traceback (most recent call last): 4: from bin/check_require:42:in `' 3: from /home/billg/ruby-refrigerator/lib/refrigerator.rb:35:in `check_require' 2: from /usr/local/lib/ruby/2.5/rubygems/core_ext/kernel_require.rb:59:in `require' 1: from /usr/local/lib/ruby/2.5/rubygems/core_ext/kernel_require.rb:59:in `require' /usr/local/lib/ruby/2.5/open3.rb:32:in ` ': can't modify frozen #
As shown above, except for the definition Open3
Mods.
let’s try it date
:
$ bin/check_require date Traceback (most recent call last): 6: from bin/check_require:42:in `' 5: from /home/billg/ruby-refrigerator/lib/refrigerator.rb:35:in `check_require' 4: from /usr/local/lib/ruby/2.5/rubygems/core_ext/kernel_require.rb:59:in `require' 3: from /usr/local/lib/ruby/2.5/rubygems/core_ext/kernel_require.rb:59:in `require' 2: from /usr/local/lib/ruby/2.5/date.rb:4:in ` ' 1: from /usr/local/lib/ruby/2.5/rubygems/core_ext/kernel_require.rb:59:in `require' /usr/local/lib/ruby/2.5/rubygems/core_ext/kernel_require.rb:59:in `require': can't modify frozen #
The first failure was because date
defined Date
and DateTime
classes, so you must first define these classes and use a slash to separate DateTime from the superclass Date. Note that it still fails in this case, but it doesn’t even tell you why. It turns out that the reason is this date
also added to_date
and other methods Time
so you need to exclude freezing Time
as well as.
Here are some more examples using Sequel and Roda:
bin/check_require -m Sequel -r bigdecimal,date,thread,time,uri sequel bin/check_require -c Roda -r rack,uri,fileutils,set,tmpdir,tempfile,thread,date,time roda
constant list freeze_core
Freezes will be stored in module_names/*.txt files in the repository, with a separate file for each ruby minor version. When a new Ruby minor version is released, module_names
The rake task can be run with the new Ruby minor version and it will produce the corresponding archive.
Internal, deprecated and private classes and mods are not frozen in the freezer.
and
Jeremy Evans
2024-12-28 19:17:26