Skip to content

Commit 066e256

Browse files
committed
Add temporary names for internally defined anonymous classes and modules on Ruby 3.3+
These should hopefully aid debugging and ease understanding. I don't believe any of these calls are in performance-sensitive code. Most are called only during application startup. In terms of runtime calls, Dataset#with_extend may be the most common, but in that case, you are allocating a new class, so setting the temporary name shouldn't be that significant in comparison. The temporary names are provided inside a block, so the dynamically created strings are not allocated on Ruby < 3.3.
1 parent 0ad0bb3 commit 066e256

38 files changed

+147
-22
lines changed

CHANGELOG

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
=== master
2+
3+
* Add temporary names for internally defined anonymous classes and modules on Ruby 3.3+ (jeremyevans)
4+
15
=== 5.88.0 (2025-01-01)
26

37
* Add subset_static_cache plugin for statically caching subsets of a model class (jeremyevans)

lib/sequel/adapters/ibmdb.rb

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ module Sequel
77

88
module IBMDB
99
tt = Class.new do
10+
Sequel.set_temp_name(self){"Sequel::IBMDB::_TypeTranslator"}
1011
def boolean(s) !s.to_i.zero? end
1112
def int(s) s.to_i end
1213
end.new

lib/sequel/adapters/shared/access.rb

+1
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ def type_literal_generic_file(column)
8888

8989
module DatasetMethods
9090
include(Module.new do
91+
Sequel.set_temp_name(self){"Sequel::Access::DatasetMethods::_SQLMethods"}
9192
Dataset.def_sql_method(self, :select, %w'select distinct limit columns into from join where group order having compounds')
9293
end)
9394
include EmulateOffsetWithReverseAndCount

lib/sequel/adapters/shared/mssql.rb

+1
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,7 @@ def view_with_check_option_support
574574

575575
module DatasetMethods
576576
include(Module.new do
577+
Sequel.set_temp_name(self){"Sequel::MSSQL::DatasetMethods::_SQLMethods"}
577578
Dataset.def_sql_method(self, :select, %w'with select distinct limit columns into from lock join where group having compounds order')
578579
end)
579580
include EmulateOffsetWithRowNumber

lib/sequel/adapters/shared/oracle.rb

+1
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,7 @@ module DatasetMethods
333333
BITAND_PROC = lambda{|a, b| Sequel.lit(["CAST(BITAND(", ", ", ") AS INTEGER)"], a, b)}
334334

335335
include(Module.new do
336+
Sequel.set_temp_name(self){"Sequel::Oracle::DatasetMethods::_SQLMethods"}
336337
Dataset.def_sql_method(self, :select, %w'with select distinct columns from join where group having compounds order limit lock')
337338
end)
338339

lib/sequel/core.rb

+15
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,21 @@ def json_parser_error_class
164164
JSON::ParserError
165165
end
166166

167+
if RUBY_VERSION >= '3.3'
168+
# Create a new module using the block, and set the temporary name
169+
# on it using the given a containing module and name.
170+
def set_temp_name(mod)
171+
mod.set_temporary_name(yield)
172+
mod
173+
end
174+
# :nocov:
175+
else
176+
def set_temp_name(mod)
177+
mod
178+
end
179+
end
180+
# :nocov:
181+
167182
# Convert given object to json and return the result.
168183
# This can be overridden to use an alternative json implementation.
169184
def object_to_json(obj, *args, &block)

lib/sequel/database/dataset_defaults.rb

+3-3
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class Database
1717
# as the dataset class.
1818
def dataset_class=(c)
1919
unless @dataset_modules.empty?
20-
c = Class.new(c)
20+
c = Sequel.set_temp_name(Class.new(c)){"Sequel::Dataset::_Subclass"}
2121
@dataset_modules.each{|m| c.send(:include, m)}
2222
end
2323
@dataset_class = c
@@ -61,10 +61,10 @@ def dataset_class=(c)
6161
# # SELECT id, name FROM table WHERE active ORDER BY id
6262
def extend_datasets(mod=nil, &block)
6363
raise(Error, "must provide either mod or block, not both") if mod && block
64-
mod = Dataset::DatasetModule.new(&block) if block
64+
mod = Sequel.set_temp_name(Dataset::DatasetModule.new(&block)){"Sequel::Dataset::_DatasetModule(#{block.source_location.join(':')})"} if block
6565
if @dataset_modules.empty?
6666
@dataset_modules = [mod]
67-
@dataset_class = Class.new(@dataset_class)
67+
@dataset_class = Sequel.set_temp_name(Class.new(@dataset_class)){"Sequel::Dataset::_Subclass"}
6868
else
6969
@dataset_modules << mod
7070
end

lib/sequel/dataset/deprecated_singleton_class_methods.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def extension(*a)
1919
def with_extend(*mods, &block)
2020
c = _clone(:freeze=>false)
2121
c.extend(*mods) unless mods.empty?
22-
c.extend(DatasetModule.new(&block)) if block
22+
c.extend(Sequel.set_temp_name(DatasetModule.new(&block)){"Sequel::Dataset::_DatasetModule(#{block.source_location.join(':')})"}) if block
2323
c.freeze
2424
end
2525

lib/sequel/dataset/prepared_statements.rb

+2-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ class Dataset
2020
def self.prepared_statements_module(code, mods, meths=DEFAULT_PREPARED_STATEMENT_MODULE_METHODS, &block)
2121
code = PREPARED_STATEMENT_MODULE_CODE[code] || code
2222

23-
Module.new do
23+
Module.new do
24+
Sequel.set_temp_name(self){"Sequel::Dataset::_PreparedStatementsModule(#{block.source_location.join(':') if block})"}
2425
Array(mods).each do |mod|
2526
include mod
2627
end

lib/sequel/dataset/query.rb

+2-2
Original file line numberDiff line numberDiff line change
@@ -1238,9 +1238,9 @@ def with_recursive(name, nonrecursive, recursive, opts=OPTS)
12381238
# Note that like Object#extend, when multiple modules are provided
12391239
# as arguments the subclass includes the modules in reverse order.
12401240
def with_extend(*mods, &block)
1241-
c = Class.new(self.class)
1241+
c = Sequel.set_temp_name(Class.new(self.class)){"Sequel::Dataset::_Subclass"}
12421242
c.include(*mods) unless mods.empty?
1243-
c.include(DatasetModule.new(&block)) if block
1243+
c.include(Sequel.set_temp_name(DatasetModule.new(&block)){"Sequel::Dataset::_DatasetModule(#{block.source_location.join(':')})"}) if block
12441244
o = c.freeze.allocate
12451245
o.instance_variable_set(:@db, @db)
12461246
o.instance_variable_set(:@opts, @opts)

lib/sequel/extensions/pg_row.rb

+3-1
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ class << self
113113
# automatically casted to the database type when literalizing.
114114
def self.subclass(db_type)
115115
Class.new(self) do
116+
Sequel.set_temp_name(self){"Sequel::Postgres::PGRow::ArrayRow::_Subclass(#{db_type})"}
116117
@db_type = db_type
117118
end
118119
end
@@ -170,6 +171,7 @@ class << self
170171
# type and columns.
171172
def self.subclass(db_type, columns)
172173
Class.new(self) do
174+
Sequel.set_temp_name(self){"Sequel::Postgres::PGRow::HashRow::_Subclass(#{db_type})"}
173175
@db_type = db_type
174176
@columns = columns
175177
end
@@ -391,7 +393,7 @@ def self.extended(db)
391393
db.instance_exec do
392394
@row_types = {}
393395
@row_schema_types = {}
394-
extend(@row_type_method_module = Module.new)
396+
extend(@row_type_method_module = Sequel.set_temp_name(Module.new){"Sequel::Postgres::PGRow::DatabaseMethods::_RowTypeMethodModule"})
395397
add_conversion_proc(2249, PGRow::Parser.new(:converter=>PGRow::ArrayRow))
396398
if respond_to?(:register_array_type)
397399
register_array_type('record', :oid=>2287, :scalar_oid=>2249)

lib/sequel/extensions/virtual_row_method_block.rb

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ module Sequel
1414
module SQL
1515
class VirtualRow < BasicObject
1616
include(Module.new do
17+
Sequel.set_temp_name(self){"Sequel::SQL:VirtualRow::_MethodBlockMethodMissing"}
1718
# Handle blocks passed to methods and change the behavior.
1819
def method_missing(m, *args, &block)
1920
if block

lib/sequel/model/base.rb

+3-3
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ def Model(source)
184184
end
185185
end
186186

187-
klass = Class.new(self)
187+
klass = Sequel.set_temp_name(Class.new(self)){"Sequel::_Model(#{source.inspect})"}
188188

189189
if source.is_a?(::Sequel::Database)
190190
klass.db = source
@@ -768,7 +768,7 @@ def def_column_accessor(*columns)
768768
# default behavior.
769769
def dataset_methods_module
770770
return @dataset_methods_module if defined?(@dataset_methods_module)
771-
Sequel.synchronize{@dataset_methods_module ||= Module.new}
771+
Sequel.synchronize{@dataset_methods_module ||= Sequel.set_temp_name(Module.new){"#{name}::@dataset_methods_module"}}
772772
extend(@dataset_methods_module)
773773
@dataset_methods_module
774774
end
@@ -956,7 +956,7 @@ def method_added(meth)
956956
# Module that the class includes that holds methods the class adds for column accessors and
957957
# associations so that the methods can be overridden with +super+.
958958
def overridable_methods_module
959-
include(@overridable_methods_module = Module.new) unless @overridable_methods_module
959+
include(@overridable_methods_module = Sequel.set_temp_name(Module.new){"#{name}::@overridable_methods_module"}) unless @overridable_methods_module
960960
@overridable_methods_module
961961
end
962962

lib/sequel/plugins/composition.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ module Composition
6161
def self.apply(model)
6262
model.instance_exec do
6363
@compositions = {}
64-
include(@composition_module ||= Module.new)
64+
include(@composition_module ||= Sequel.set_temp_name(Module.new){"#{name}::@composition_module"})
6565
end
6666
end
6767

lib/sequel/plugins/enum.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ def enum(column, values, opts=OPTS)
8080
inverted = values.invert.freeze
8181

8282
unless @enum_methods
83-
@enum_methods = Module.new
83+
@enum_methods = Sequel.set_temp_name(Module.new){"#{name}::@enum_methods"}
8484
include @enum_methods
8585
end
8686

lib/sequel/plugins/inverted_subsets.rb

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ module InvertedSubsets
3232
def self.apply(model, &block)
3333
model.instance_exec do
3434
@dataset_module_class = Class.new(@dataset_module_class) do
35+
Sequel.set_temp_name(self){"#{model.name}::@dataset_module_class(InvertedSubsets)"}
3536
include DatasetModuleMethods
3637
if block
3738
define_method(:inverted_subset_name, &block)

lib/sequel/plugins/lazy_attributes.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ def lazy_attributes(*attrs)
6464
# :dataset :: The base dataset to use for the lazy attribute lookup
6565
# :table :: The table name to use to qualify the attribute and primary key columns.
6666
def define_lazy_attribute_getter(a, opts=OPTS)
67-
include(@lazy_attributes_module ||= Module.new) unless @lazy_attributes_module
67+
include(@lazy_attributes_module ||= Sequel.set_temp_name(Module.new){"#{name}::@lazy_attributes_module"}) unless @lazy_attributes_module
6868
@lazy_attributes_module.class_eval do
6969
define_method(a) do
7070
if !values.has_key?(a) && !new?

lib/sequel/plugins/nested_attributes.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ def freeze
129129
#
130130
# If a block is provided, it is used to set the :reject_if option.
131131
def nested_attributes(*associations, &block)
132-
include(@nested_attributes_module ||= Module.new) unless @nested_attributes_module
132+
include(@nested_attributes_module ||= Sequel.set_temp_name(Module.new){"#{name}::@nested_attributes_module"}) unless @nested_attributes_module
133133
opts = associations.last.is_a?(Hash) ? associations.pop : OPTS
134134
reflections = associations.map{|a| association_reflection(a) || raise(Error, "no association named #{a} for #{self}")}
135135
reflections.each do |r|

lib/sequel/plugins/rcte_tree.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ def self.apply(model, opts=OPTS)
8181

8282
opts = opts.dup
8383
opts[:class] = model
84-
opts[:methods_module] = Module.new
84+
opts[:methods_module] = Sequel.set_temp_name(Module.new){"#{model.name}::_rcte_tree[:methods_module]"}
8585
opts[:union_all] = opts[:union_all].nil? ? true : opts[:union_all]
8686
model.send(:include, opts[:methods_module])
8787

lib/sequel/plugins/serialization.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ def serialize_attributes(format, *columns)
146146
# Add serializated attribute acessor methods to the serialization_module
147147
def define_serialized_attribute_accessor(serializer, deserializer, *columns)
148148
m = self
149-
include(@serialization_module ||= Module.new) unless @serialization_module
149+
include(@serialization_module ||= Sequel.set_temp_name(Module.new){"#{name}::@serialization_module"}) unless @serialization_module
150150
@serialization_module.class_eval do
151151
columns.each do |column|
152152
m.serialization_map[column] = serializer

lib/sequel/plugins/sql_comments.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ def sql_comments_instance_methods(*meths)
8585
# Use automatic SQL comments for the given dataset methods.
8686
def sql_comments_dataset_methods(*meths)
8787
unless @_sql_comments_dataset_module
88-
dataset_module(@_sql_comments_dataset_module = Module.new)
88+
dataset_module(@_sql_comments_dataset_module = Sequel.set_temp_name(Module.new){"#{name}::@_sql_comments_dataset_module"})
8989
end
9090
_sql_comments_methods(@_sql_comments_dataset_module, :dataset, meths)
9191
end

lib/sequel/plugins/subset_conditions.rb

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ module SubsetConditions
4848
def self.apply(model, &block)
4949
model.instance_exec do
5050
@dataset_module_class = Class.new(@dataset_module_class) do
51+
Sequel.set_temp_name(self){"#{model.name}::@dataset_module_class(SubsetConditions)"}
5152
include DatasetModuleMethods
5253
end
5354
end

lib/sequel/plugins/subset_static_cache.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ def subset_static_cache_module
9999
# it before calling creating this module.
100100
dataset_methods_module
101101

102-
Sequel.synchronize{@subset_static_cache_module ||= Module.new}
102+
Sequel.synchronize{@subset_static_cache_module ||= Sequel.set_temp_name(Module.new){"#{name}::@subset_static_cache_module"}}
103103
extend(@subset_static_cache_module)
104104
@subset_static_cache_module
105105
end

lib/sequel/sql.rb

+1
Original file line numberDiff line numberDiff line change
@@ -1915,6 +1915,7 @@ def initialize
19151915
end
19161916

19171917
m = Module.new do
1918+
Sequel.set_temp_name(Module.new){"Sequel::SQL::VirtualRow::_BaseMethodMissing"}
19181919
# Return an +Identifier+, +QualifiedIdentifier+, or +Function+, depending
19191920
# on arguments and whether a block is provided. Does not currently call the block.
19201921
# See the class level documentation.

spec/core/database_spec.rb

+11
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,11 @@ def dataset_class_default; Sequel::Dataset end
510510
ds.db.must_be_same_as(@db)
511511
end
512512

513+
it "should give temporary name to implicitly created subclass" do
514+
@db.extend_datasets{}
515+
@db.dataset_class.name.must_equal "Sequel::Dataset::_Subclass"
516+
end if RUBY_VERSION >= '3.3'
517+
513518
it "should have getter return the class to use to create datasets" do
514519
[@db.dataset_class, @db.dataset_class.superclass].must_include(Sequel::Dataset)
515520
@db.dataset_class = @dsc
@@ -561,6 +566,12 @@ def dataset_class_default; Sequel::Dataset end
561566
@db.dataset.foo.must_equal [5, 3]
562567
end
563568

569+
it "should give temporary name to class and module" do
570+
@db.extend_datasets{def foo() [5] + super end}
571+
@db.dataset_class.ancestors[1].name.must_equal "Sequel::Dataset::_DatasetModule(#{__FILE__}:#{__LINE__-1})"
572+
@db.dataset_class.name.must_equal "Sequel::Dataset::_Subclass"
573+
end if RUBY_VERSION >= '3.3'
574+
564575
it "should raise an error if both a module and a block are provided" do
565576
proc{@db.extend_datasets(@m2){def foo() [5] + super end}}.must_raise(Sequel::Error)
566577
end

spec/core/dataset_spec.rb

+9-3
Original file line numberDiff line numberDiff line change
@@ -1907,6 +1907,12 @@ def supports_cte_in_subselect?; false end
19071907
it "should work with just a block" do
19081908
Sequel.mock.dataset.with_extend{def a; 1 end}.a.must_equal 1
19091909
end
1910+
1911+
it "should give temporary name to class and module" do
1912+
ds = Sequel.mock.dataset.with_extend{def a; 1 end}
1913+
ds.class.ancestors[1].name.must_equal "Sequel::Dataset::_DatasetModule(#{__FILE__}:#{__LINE__-1})"
1914+
ds.class.name.must_equal "Sequel::Dataset::_Subclass"
1915+
end if RUBY_VERSION >= '3.3'
19101916
end
19111917

19121918
describe "Dataset#with_extend custom methods" do
@@ -4333,8 +4339,8 @@ def delete_sql; super << " RETURNING *" end
43334339
end
43344340

43354341
it "#inspect should indicate it is a prepared statement with the prepared SQL" do
4336-
@ds.filter(:num=>:$n).prepare(:select, :sn).inspect.must_equal \
4337-
'<Sequel::Mock::Dataset/PreparedStatement "SELECT * FROM items WHERE (num = $n)">'
4342+
@ds.filter(:num=>:$n).prepare(:select, :sn).inspect.must_match \
4343+
%r'\A<Sequel::.*/PreparedStatement "SELECT \* FROM items WHERE \(num = \$n\)">\z'
43384344
end
43394345

43404346
it "should handle literal strings" do
@@ -4423,7 +4429,7 @@ def prepared_statement_modules
44234429
end
44244430

44254431
it "#inspect should show the actual SQL submitted to the database" do
4426-
@ps.first.inspect.must_equal '<Sequel::Mock::Dataset/PreparedStatement "SELECT * FROM items WHERE (num = ?)">'
4432+
@ps.first.inspect.must_match %r'\A<Sequel::.*/PreparedStatement "SELECT \* FROM items WHERE \(num = \?\)">\z'
44274433
end
44284434

44294435
it "should submit the SQL to the database with placeholders and bind variables" do

spec/extensions/composition_spec.rb

+6
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@
99
DB.reset
1010
end
1111

12+
it "should give temporary name to name model-specific module" do
13+
c = Sequel::Model(:items)
14+
c.plugin :composition
15+
c.ancestors[2].name.must_equal "Sequel::_Model(:items)::@composition_module"
16+
end if RUBY_VERSION >= '3.3'
17+
1218
it ".composition should add compositions" do
1319
@o.wont_respond_to(:date)
1420
@c.composition :date, :mapping=>[:year, :month, :day]

spec/extensions/enum_spec.rb

+7
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@
99
@album = @Album.load(:status_id=>3)
1010
end
1111

12+
it "should give temporary name to name model-specific module" do
13+
c = Sequel::Model(:items)
14+
c.plugin :enum
15+
c.enum :status_id, :good=>3, :bad=>5
16+
c.ancestors[1].name.must_equal "Sequel::_Model(:items)::@enum_methods"
17+
end if RUBY_VERSION >= '3.3'
18+
1219
it "should add enum_value! and enum_value? methods for setting/checking the enum values" do
1320
@album.good?.must_equal true
1421
@album.bad?.must_equal false

spec/extensions/inverted_subsets_spec.rb

+6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
require_relative "spec_helper"
22

33
describe "Sequel::Plugins::InvertedSubsets" do
4+
it "should name generated dataset module class" do
5+
c = Sequel::Model(:items)
6+
c.plugin :inverted_subsets
7+
c.dataset_module_class.name.must_equal "Sequel::_Model(:items)::@dataset_module_class(InvertedSubsets)"
8+
end if RUBY_VERSION >= '3.3'
9+
410
it "should add an inverted subset method which inverts the condition" do
511
c = Class.new(Sequel::Model(:a))
612
c.plugin :inverted_subsets

spec/extensions/lazy_attributes_spec.rb

+4
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ def self.columns; [:id] end
4343
Object.send(:remove_const, :LazyAttributesModel)
4444
end
4545

46+
it "should give temporary name to name model-specific module" do
47+
LazyAttributesModel.ancestors[1].name.must_equal "LazyAttributesModel::@lazy_attributes_module"
48+
end if RUBY_VERSION >= '3.3'
49+
4650
it "should allowing adding additional lazy attributes via plugin :lazy_attributes" do
4751
@c.set_dataset(@ds.select(:id, :blah))
4852
@c.dataset.sql.must_equal 'SELECT id, blah FROM la'

spec/extensions/nested_attributes_spec.rb

+8
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,14 @@ def check_sql_array(*shoulds)
4949
@db.sqls
5050
end
5151

52+
it "should give temporary name to name model-specific module" do
53+
c = Sequel::Model(:items)
54+
c.plugin :nested_attributes
55+
c.many_to_one :c, :class=>c
56+
c.nested_attributes :c
57+
c.ancestors[1].name.must_equal "Sequel::_Model(:items)::@nested_attributes_module"
58+
end if RUBY_VERSION >= '3.3'
59+
5260
it "should not modify options hash when loading plugin" do
5361
h = {}
5462
@Concert.nested_attributes :albums, h

0 commit comments

Comments
 (0)