From 066e256b703cb5cd3633bef8309a16fdb41636a3 Mon Sep 17 00:00:00 2001 From: Jeremy Evans Date: Thu, 16 Jan 2025 12:33:15 -0800 Subject: [PATCH] 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. --- CHANGELOG | 4 ++++ lib/sequel/adapters/ibmdb.rb | 1 + lib/sequel/adapters/shared/access.rb | 1 + lib/sequel/adapters/shared/mssql.rb | 1 + lib/sequel/adapters/shared/oracle.rb | 1 + lib/sequel/core.rb | 15 +++++++++++++++ lib/sequel/database/dataset_defaults.rb | 6 +++--- .../dataset/deprecated_singleton_class_methods.rb | 2 +- lib/sequel/dataset/prepared_statements.rb | 3 ++- lib/sequel/dataset/query.rb | 4 ++-- lib/sequel/extensions/pg_row.rb | 4 +++- lib/sequel/extensions/virtual_row_method_block.rb | 1 + lib/sequel/model/base.rb | 6 +++--- lib/sequel/plugins/composition.rb | 2 +- lib/sequel/plugins/enum.rb | 2 +- lib/sequel/plugins/inverted_subsets.rb | 1 + lib/sequel/plugins/lazy_attributes.rb | 2 +- lib/sequel/plugins/nested_attributes.rb | 2 +- lib/sequel/plugins/rcte_tree.rb | 2 +- lib/sequel/plugins/serialization.rb | 2 +- lib/sequel/plugins/sql_comments.rb | 2 +- lib/sequel/plugins/subset_conditions.rb | 1 + lib/sequel/plugins/subset_static_cache.rb | 2 +- lib/sequel/sql.rb | 1 + spec/core/database_spec.rb | 11 +++++++++++ spec/core/dataset_spec.rb | 12 +++++++++--- spec/extensions/composition_spec.rb | 6 ++++++ spec/extensions/enum_spec.rb | 7 +++++++ spec/extensions/inverted_subsets_spec.rb | 6 ++++++ spec/extensions/lazy_attributes_spec.rb | 4 ++++ spec/extensions/nested_attributes_spec.rb | 8 ++++++++ spec/extensions/pg_row_spec.rb | 2 ++ spec/extensions/rcte_tree_spec.rb | 7 +++++++ spec/extensions/serialization_spec.rb | 6 ++++++ spec/extensions/sql_comments_plugin_spec.rb | 9 +++++++++ spec/extensions/subset_conditions_spec.rb | 6 ++++++ spec/extensions/subset_static_cache_spec.rb | 10 ++++++++++ spec/model/model_spec.rb | 7 +++++++ 38 files changed, 147 insertions(+), 22 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 4409512bc3..c73bcdb3a5 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,7 @@ +=== master + +* Add temporary names for internally defined anonymous classes and modules on Ruby 3.3+ (jeremyevans) + === 5.88.0 (2025-01-01) * Add subset_static_cache plugin for statically caching subsets of a model class (jeremyevans) diff --git a/lib/sequel/adapters/ibmdb.rb b/lib/sequel/adapters/ibmdb.rb index fca376f94e..ca72e210ee 100644 --- a/lib/sequel/adapters/ibmdb.rb +++ b/lib/sequel/adapters/ibmdb.rb @@ -7,6 +7,7 @@ module Sequel module IBMDB tt = Class.new do + Sequel.set_temp_name(self){"Sequel::IBMDB::_TypeTranslator"} def boolean(s) !s.to_i.zero? end def int(s) s.to_i end end.new diff --git a/lib/sequel/adapters/shared/access.rb b/lib/sequel/adapters/shared/access.rb index 6993cb6c5c..a3aee68275 100644 --- a/lib/sequel/adapters/shared/access.rb +++ b/lib/sequel/adapters/shared/access.rb @@ -88,6 +88,7 @@ def type_literal_generic_file(column) module DatasetMethods include(Module.new do + Sequel.set_temp_name(self){"Sequel::Access::DatasetMethods::_SQLMethods"} Dataset.def_sql_method(self, :select, %w'select distinct limit columns into from join where group order having compounds') end) include EmulateOffsetWithReverseAndCount diff --git a/lib/sequel/adapters/shared/mssql.rb b/lib/sequel/adapters/shared/mssql.rb index d8947df596..89c9d1e29a 100644 --- a/lib/sequel/adapters/shared/mssql.rb +++ b/lib/sequel/adapters/shared/mssql.rb @@ -574,6 +574,7 @@ def view_with_check_option_support module DatasetMethods include(Module.new do + Sequel.set_temp_name(self){"Sequel::MSSQL::DatasetMethods::_SQLMethods"} Dataset.def_sql_method(self, :select, %w'with select distinct limit columns into from lock join where group having compounds order') end) include EmulateOffsetWithRowNumber diff --git a/lib/sequel/adapters/shared/oracle.rb b/lib/sequel/adapters/shared/oracle.rb index 922c368847..8253c4626f 100644 --- a/lib/sequel/adapters/shared/oracle.rb +++ b/lib/sequel/adapters/shared/oracle.rb @@ -333,6 +333,7 @@ module DatasetMethods BITAND_PROC = lambda{|a, b| Sequel.lit(["CAST(BITAND(", ", ", ") AS INTEGER)"], a, b)} include(Module.new do + Sequel.set_temp_name(self){"Sequel::Oracle::DatasetMethods::_SQLMethods"} Dataset.def_sql_method(self, :select, %w'with select distinct columns from join where group having compounds order limit lock') end) diff --git a/lib/sequel/core.rb b/lib/sequel/core.rb index 5e40ba73e9..c7b72bf38d 100644 --- a/lib/sequel/core.rb +++ b/lib/sequel/core.rb @@ -164,6 +164,21 @@ def json_parser_error_class JSON::ParserError end + if RUBY_VERSION >= '3.3' + # Create a new module using the block, and set the temporary name + # on it using the given a containing module and name. + def set_temp_name(mod) + mod.set_temporary_name(yield) + mod + end + # :nocov: + else + def set_temp_name(mod) + mod + end + end + # :nocov: + # Convert given object to json and return the result. # This can be overridden to use an alternative json implementation. def object_to_json(obj, *args, &block) diff --git a/lib/sequel/database/dataset_defaults.rb b/lib/sequel/database/dataset_defaults.rb index afe03fa054..76232e1f15 100644 --- a/lib/sequel/database/dataset_defaults.rb +++ b/lib/sequel/database/dataset_defaults.rb @@ -17,7 +17,7 @@ class Database # as the dataset class. def dataset_class=(c) unless @dataset_modules.empty? - c = Class.new(c) + c = Sequel.set_temp_name(Class.new(c)){"Sequel::Dataset::_Subclass"} @dataset_modules.each{|m| c.send(:include, m)} end @dataset_class = c @@ -61,10 +61,10 @@ def dataset_class=(c) # # SELECT id, name FROM table WHERE active ORDER BY id def extend_datasets(mod=nil, &block) raise(Error, "must provide either mod or block, not both") if mod && block - mod = Dataset::DatasetModule.new(&block) if block + mod = Sequel.set_temp_name(Dataset::DatasetModule.new(&block)){"Sequel::Dataset::_DatasetModule(#{block.source_location.join(':')})"} if block if @dataset_modules.empty? @dataset_modules = [mod] - @dataset_class = Class.new(@dataset_class) + @dataset_class = Sequel.set_temp_name(Class.new(@dataset_class)){"Sequel::Dataset::_Subclass"} else @dataset_modules << mod end diff --git a/lib/sequel/dataset/deprecated_singleton_class_methods.rb b/lib/sequel/dataset/deprecated_singleton_class_methods.rb index 76aab0e3eb..42ff696cda 100644 --- a/lib/sequel/dataset/deprecated_singleton_class_methods.rb +++ b/lib/sequel/dataset/deprecated_singleton_class_methods.rb @@ -19,7 +19,7 @@ def extension(*a) def with_extend(*mods, &block) c = _clone(:freeze=>false) c.extend(*mods) unless mods.empty? - c.extend(DatasetModule.new(&block)) if block + c.extend(Sequel.set_temp_name(DatasetModule.new(&block)){"Sequel::Dataset::_DatasetModule(#{block.source_location.join(':')})"}) if block c.freeze end diff --git a/lib/sequel/dataset/prepared_statements.rb b/lib/sequel/dataset/prepared_statements.rb index 413ef5defb..81a9516885 100644 --- a/lib/sequel/dataset/prepared_statements.rb +++ b/lib/sequel/dataset/prepared_statements.rb @@ -20,7 +20,8 @@ class Dataset def self.prepared_statements_module(code, mods, meths=DEFAULT_PREPARED_STATEMENT_MODULE_METHODS, &block) code = PREPARED_STATEMENT_MODULE_CODE[code] || code - Module.new do + Module.new do + Sequel.set_temp_name(self){"Sequel::Dataset::_PreparedStatementsModule(#{block.source_location.join(':') if block})"} Array(mods).each do |mod| include mod end diff --git a/lib/sequel/dataset/query.rb b/lib/sequel/dataset/query.rb index 4557a45924..d82b5c19c8 100644 --- a/lib/sequel/dataset/query.rb +++ b/lib/sequel/dataset/query.rb @@ -1238,9 +1238,9 @@ def with_recursive(name, nonrecursive, recursive, opts=OPTS) # Note that like Object#extend, when multiple modules are provided # as arguments the subclass includes the modules in reverse order. def with_extend(*mods, &block) - c = Class.new(self.class) + c = Sequel.set_temp_name(Class.new(self.class)){"Sequel::Dataset::_Subclass"} c.include(*mods) unless mods.empty? - c.include(DatasetModule.new(&block)) if block + c.include(Sequel.set_temp_name(DatasetModule.new(&block)){"Sequel::Dataset::_DatasetModule(#{block.source_location.join(':')})"}) if block o = c.freeze.allocate o.instance_variable_set(:@db, @db) o.instance_variable_set(:@opts, @opts) diff --git a/lib/sequel/extensions/pg_row.rb b/lib/sequel/extensions/pg_row.rb index b1e1c95ead..826cf6c730 100644 --- a/lib/sequel/extensions/pg_row.rb +++ b/lib/sequel/extensions/pg_row.rb @@ -113,6 +113,7 @@ class << self # automatically casted to the database type when literalizing. def self.subclass(db_type) Class.new(self) do + Sequel.set_temp_name(self){"Sequel::Postgres::PGRow::ArrayRow::_Subclass(#{db_type})"} @db_type = db_type end end @@ -170,6 +171,7 @@ class << self # type and columns. def self.subclass(db_type, columns) Class.new(self) do + Sequel.set_temp_name(self){"Sequel::Postgres::PGRow::HashRow::_Subclass(#{db_type})"} @db_type = db_type @columns = columns end @@ -391,7 +393,7 @@ def self.extended(db) db.instance_exec do @row_types = {} @row_schema_types = {} - extend(@row_type_method_module = Module.new) + extend(@row_type_method_module = Sequel.set_temp_name(Module.new){"Sequel::Postgres::PGRow::DatabaseMethods::_RowTypeMethodModule"}) add_conversion_proc(2249, PGRow::Parser.new(:converter=>PGRow::ArrayRow)) if respond_to?(:register_array_type) register_array_type('record', :oid=>2287, :scalar_oid=>2249) diff --git a/lib/sequel/extensions/virtual_row_method_block.rb b/lib/sequel/extensions/virtual_row_method_block.rb index d5747972c9..55659fd46b 100644 --- a/lib/sequel/extensions/virtual_row_method_block.rb +++ b/lib/sequel/extensions/virtual_row_method_block.rb @@ -14,6 +14,7 @@ module Sequel module SQL class VirtualRow < BasicObject include(Module.new do + Sequel.set_temp_name(self){"Sequel::SQL:VirtualRow::_MethodBlockMethodMissing"} # Handle blocks passed to methods and change the behavior. def method_missing(m, *args, &block) if block diff --git a/lib/sequel/model/base.rb b/lib/sequel/model/base.rb index 13dfa30b7c..80fb951bb6 100644 --- a/lib/sequel/model/base.rb +++ b/lib/sequel/model/base.rb @@ -184,7 +184,7 @@ def Model(source) end end - klass = Class.new(self) + klass = Sequel.set_temp_name(Class.new(self)){"Sequel::_Model(#{source.inspect})"} if source.is_a?(::Sequel::Database) klass.db = source @@ -768,7 +768,7 @@ def def_column_accessor(*columns) # default behavior. def dataset_methods_module return @dataset_methods_module if defined?(@dataset_methods_module) - Sequel.synchronize{@dataset_methods_module ||= Module.new} + Sequel.synchronize{@dataset_methods_module ||= Sequel.set_temp_name(Module.new){"#{name}::@dataset_methods_module"}} extend(@dataset_methods_module) @dataset_methods_module end @@ -956,7 +956,7 @@ def method_added(meth) # Module that the class includes that holds methods the class adds for column accessors and # associations so that the methods can be overridden with +super+. def overridable_methods_module - include(@overridable_methods_module = Module.new) unless @overridable_methods_module + include(@overridable_methods_module = Sequel.set_temp_name(Module.new){"#{name}::@overridable_methods_module"}) unless @overridable_methods_module @overridable_methods_module end diff --git a/lib/sequel/plugins/composition.rb b/lib/sequel/plugins/composition.rb index 3e15adce7f..71ef9fd2c5 100644 --- a/lib/sequel/plugins/composition.rb +++ b/lib/sequel/plugins/composition.rb @@ -61,7 +61,7 @@ module Composition def self.apply(model) model.instance_exec do @compositions = {} - include(@composition_module ||= Module.new) + include(@composition_module ||= Sequel.set_temp_name(Module.new){"#{name}::@composition_module"}) end end diff --git a/lib/sequel/plugins/enum.rb b/lib/sequel/plugins/enum.rb index 0725f868a6..393108605f 100644 --- a/lib/sequel/plugins/enum.rb +++ b/lib/sequel/plugins/enum.rb @@ -80,7 +80,7 @@ def enum(column, values, opts=OPTS) inverted = values.invert.freeze unless @enum_methods - @enum_methods = Module.new + @enum_methods = Sequel.set_temp_name(Module.new){"#{name}::@enum_methods"} include @enum_methods end diff --git a/lib/sequel/plugins/inverted_subsets.rb b/lib/sequel/plugins/inverted_subsets.rb index badb079250..6ff29b3d71 100644 --- a/lib/sequel/plugins/inverted_subsets.rb +++ b/lib/sequel/plugins/inverted_subsets.rb @@ -32,6 +32,7 @@ module InvertedSubsets def self.apply(model, &block) model.instance_exec do @dataset_module_class = Class.new(@dataset_module_class) do + Sequel.set_temp_name(self){"#{model.name}::@dataset_module_class(InvertedSubsets)"} include DatasetModuleMethods if block define_method(:inverted_subset_name, &block) diff --git a/lib/sequel/plugins/lazy_attributes.rb b/lib/sequel/plugins/lazy_attributes.rb index c6a11ae352..c8040a5240 100644 --- a/lib/sequel/plugins/lazy_attributes.rb +++ b/lib/sequel/plugins/lazy_attributes.rb @@ -64,7 +64,7 @@ def lazy_attributes(*attrs) # :dataset :: The base dataset to use for the lazy attribute lookup # :table :: The table name to use to qualify the attribute and primary key columns. def define_lazy_attribute_getter(a, opts=OPTS) - include(@lazy_attributes_module ||= Module.new) unless @lazy_attributes_module + include(@lazy_attributes_module ||= Sequel.set_temp_name(Module.new){"#{name}::@lazy_attributes_module"}) unless @lazy_attributes_module @lazy_attributes_module.class_eval do define_method(a) do if !values.has_key?(a) && !new? diff --git a/lib/sequel/plugins/nested_attributes.rb b/lib/sequel/plugins/nested_attributes.rb index 218779585d..74c9f038f1 100644 --- a/lib/sequel/plugins/nested_attributes.rb +++ b/lib/sequel/plugins/nested_attributes.rb @@ -129,7 +129,7 @@ def freeze # # If a block is provided, it is used to set the :reject_if option. def nested_attributes(*associations, &block) - include(@nested_attributes_module ||= Module.new) unless @nested_attributes_module + include(@nested_attributes_module ||= Sequel.set_temp_name(Module.new){"#{name}::@nested_attributes_module"}) unless @nested_attributes_module opts = associations.last.is_a?(Hash) ? associations.pop : OPTS reflections = associations.map{|a| association_reflection(a) || raise(Error, "no association named #{a} for #{self}")} reflections.each do |r| diff --git a/lib/sequel/plugins/rcte_tree.rb b/lib/sequel/plugins/rcte_tree.rb index a25bea7d6e..36b2aef17e 100644 --- a/lib/sequel/plugins/rcte_tree.rb +++ b/lib/sequel/plugins/rcte_tree.rb @@ -81,7 +81,7 @@ def self.apply(model, opts=OPTS) opts = opts.dup opts[:class] = model - opts[:methods_module] = Module.new + opts[:methods_module] = Sequel.set_temp_name(Module.new){"#{model.name}::_rcte_tree[:methods_module]"} opts[:union_all] = opts[:union_all].nil? ? true : opts[:union_all] model.send(:include, opts[:methods_module]) diff --git a/lib/sequel/plugins/serialization.rb b/lib/sequel/plugins/serialization.rb index 5b8cc1bbae..53442e1926 100644 --- a/lib/sequel/plugins/serialization.rb +++ b/lib/sequel/plugins/serialization.rb @@ -146,7 +146,7 @@ def serialize_attributes(format, *columns) # Add serializated attribute acessor methods to the serialization_module def define_serialized_attribute_accessor(serializer, deserializer, *columns) m = self - include(@serialization_module ||= Module.new) unless @serialization_module + include(@serialization_module ||= Sequel.set_temp_name(Module.new){"#{name}::@serialization_module"}) unless @serialization_module @serialization_module.class_eval do columns.each do |column| m.serialization_map[column] = serializer diff --git a/lib/sequel/plugins/sql_comments.rb b/lib/sequel/plugins/sql_comments.rb index 5beb290637..2dc4c5721d 100644 --- a/lib/sequel/plugins/sql_comments.rb +++ b/lib/sequel/plugins/sql_comments.rb @@ -85,7 +85,7 @@ def sql_comments_instance_methods(*meths) # Use automatic SQL comments for the given dataset methods. def sql_comments_dataset_methods(*meths) unless @_sql_comments_dataset_module - dataset_module(@_sql_comments_dataset_module = Module.new) + dataset_module(@_sql_comments_dataset_module = Sequel.set_temp_name(Module.new){"#{name}::@_sql_comments_dataset_module"}) end _sql_comments_methods(@_sql_comments_dataset_module, :dataset, meths) end diff --git a/lib/sequel/plugins/subset_conditions.rb b/lib/sequel/plugins/subset_conditions.rb index 7b2f3c4aad..47bb564bd5 100644 --- a/lib/sequel/plugins/subset_conditions.rb +++ b/lib/sequel/plugins/subset_conditions.rb @@ -48,6 +48,7 @@ module SubsetConditions def self.apply(model, &block) model.instance_exec do @dataset_module_class = Class.new(@dataset_module_class) do + Sequel.set_temp_name(self){"#{model.name}::@dataset_module_class(SubsetConditions)"} include DatasetModuleMethods end end diff --git a/lib/sequel/plugins/subset_static_cache.rb b/lib/sequel/plugins/subset_static_cache.rb index 798e07620d..9dd5e07893 100644 --- a/lib/sequel/plugins/subset_static_cache.rb +++ b/lib/sequel/plugins/subset_static_cache.rb @@ -99,7 +99,7 @@ def subset_static_cache_module # it before calling creating this module. dataset_methods_module - Sequel.synchronize{@subset_static_cache_module ||= Module.new} + Sequel.synchronize{@subset_static_cache_module ||= Sequel.set_temp_name(Module.new){"#{name}::@subset_static_cache_module"}} extend(@subset_static_cache_module) @subset_static_cache_module end diff --git a/lib/sequel/sql.rb b/lib/sequel/sql.rb index b17618bca4..56038a256e 100644 --- a/lib/sequel/sql.rb +++ b/lib/sequel/sql.rb @@ -1915,6 +1915,7 @@ def initialize end m = Module.new do + Sequel.set_temp_name(Module.new){"Sequel::SQL::VirtualRow::_BaseMethodMissing"} # Return an +Identifier+, +QualifiedIdentifier+, or +Function+, depending # on arguments and whether a block is provided. Does not currently call the block. # See the class level documentation. diff --git a/spec/core/database_spec.rb b/spec/core/database_spec.rb index 7a7b88adf2..0d5fc921c0 100644 --- a/spec/core/database_spec.rb +++ b/spec/core/database_spec.rb @@ -510,6 +510,11 @@ def dataset_class_default; Sequel::Dataset end ds.db.must_be_same_as(@db) end + it "should give temporary name to implicitly created subclass" do + @db.extend_datasets{} + @db.dataset_class.name.must_equal "Sequel::Dataset::_Subclass" + end if RUBY_VERSION >= '3.3' + it "should have getter return the class to use to create datasets" do [@db.dataset_class, @db.dataset_class.superclass].must_include(Sequel::Dataset) @db.dataset_class = @dsc @@ -561,6 +566,12 @@ def dataset_class_default; Sequel::Dataset end @db.dataset.foo.must_equal [5, 3] end + it "should give temporary name to class and module" do + @db.extend_datasets{def foo() [5] + super end} + @db.dataset_class.ancestors[1].name.must_equal "Sequel::Dataset::_DatasetModule(#{__FILE__}:#{__LINE__-1})" + @db.dataset_class.name.must_equal "Sequel::Dataset::_Subclass" + end if RUBY_VERSION >= '3.3' + it "should raise an error if both a module and a block are provided" do proc{@db.extend_datasets(@m2){def foo() [5] + super end}}.must_raise(Sequel::Error) end diff --git a/spec/core/dataset_spec.rb b/spec/core/dataset_spec.rb index 7f33c9885a..4db4ca3690 100644 --- a/spec/core/dataset_spec.rb +++ b/spec/core/dataset_spec.rb @@ -1907,6 +1907,12 @@ def supports_cte_in_subselect?; false end it "should work with just a block" do Sequel.mock.dataset.with_extend{def a; 1 end}.a.must_equal 1 end + + it "should give temporary name to class and module" do + ds = Sequel.mock.dataset.with_extend{def a; 1 end} + ds.class.ancestors[1].name.must_equal "Sequel::Dataset::_DatasetModule(#{__FILE__}:#{__LINE__-1})" + ds.class.name.must_equal "Sequel::Dataset::_Subclass" + end if RUBY_VERSION >= '3.3' end describe "Dataset#with_extend custom methods" do @@ -4333,8 +4339,8 @@ def delete_sql; super << " RETURNING *" end end it "#inspect should indicate it is a prepared statement with the prepared SQL" do - @ds.filter(:num=>:$n).prepare(:select, :sn).inspect.must_equal \ - '' + @ds.filter(:num=>:$n).prepare(:select, :sn).inspect.must_match \ + %r'\A\z' end it "should handle literal strings" do @@ -4423,7 +4429,7 @@ def prepared_statement_modules end it "#inspect should show the actual SQL submitted to the database" do - @ps.first.inspect.must_equal '' + @ps.first.inspect.must_match %r'\A\z' end it "should submit the SQL to the database with placeholders and bind variables" do diff --git a/spec/extensions/composition_spec.rb b/spec/extensions/composition_spec.rb index cbac1af861..0de03230e9 100644 --- a/spec/extensions/composition_spec.rb +++ b/spec/extensions/composition_spec.rb @@ -9,6 +9,12 @@ DB.reset end + it "should give temporary name to name model-specific module" do + c = Sequel::Model(:items) + c.plugin :composition + c.ancestors[2].name.must_equal "Sequel::_Model(:items)::@composition_module" + end if RUBY_VERSION >= '3.3' + it ".composition should add compositions" do @o.wont_respond_to(:date) @c.composition :date, :mapping=>[:year, :month, :day] diff --git a/spec/extensions/enum_spec.rb b/spec/extensions/enum_spec.rb index da1e41f34f..f5fb59e281 100644 --- a/spec/extensions/enum_spec.rb +++ b/spec/extensions/enum_spec.rb @@ -9,6 +9,13 @@ @album = @Album.load(:status_id=>3) end + it "should give temporary name to name model-specific module" do + c = Sequel::Model(:items) + c.plugin :enum + c.enum :status_id, :good=>3, :bad=>5 + c.ancestors[1].name.must_equal "Sequel::_Model(:items)::@enum_methods" + end if RUBY_VERSION >= '3.3' + it "should add enum_value! and enum_value? methods for setting/checking the enum values" do @album.good?.must_equal true @album.bad?.must_equal false diff --git a/spec/extensions/inverted_subsets_spec.rb b/spec/extensions/inverted_subsets_spec.rb index 8a2e020f8f..60786d3a3c 100644 --- a/spec/extensions/inverted_subsets_spec.rb +++ b/spec/extensions/inverted_subsets_spec.rb @@ -1,6 +1,12 @@ require_relative "spec_helper" describe "Sequel::Plugins::InvertedSubsets" do + it "should name generated dataset module class" do + c = Sequel::Model(:items) + c.plugin :inverted_subsets + c.dataset_module_class.name.must_equal "Sequel::_Model(:items)::@dataset_module_class(InvertedSubsets)" + end if RUBY_VERSION >= '3.3' + it "should add an inverted subset method which inverts the condition" do c = Class.new(Sequel::Model(:a)) c.plugin :inverted_subsets diff --git a/spec/extensions/lazy_attributes_spec.rb b/spec/extensions/lazy_attributes_spec.rb index 9c68d7fe16..458d3f332c 100644 --- a/spec/extensions/lazy_attributes_spec.rb +++ b/spec/extensions/lazy_attributes_spec.rb @@ -43,6 +43,10 @@ def self.columns; [:id] end Object.send(:remove_const, :LazyAttributesModel) end + it "should give temporary name to name model-specific module" do + LazyAttributesModel.ancestors[1].name.must_equal "LazyAttributesModel::@lazy_attributes_module" + end if RUBY_VERSION >= '3.3' + it "should allowing adding additional lazy attributes via plugin :lazy_attributes" do @c.set_dataset(@ds.select(:id, :blah)) @c.dataset.sql.must_equal 'SELECT id, blah FROM la' diff --git a/spec/extensions/nested_attributes_spec.rb b/spec/extensions/nested_attributes_spec.rb index 73b353ec89..dfa1f740b5 100644 --- a/spec/extensions/nested_attributes_spec.rb +++ b/spec/extensions/nested_attributes_spec.rb @@ -49,6 +49,14 @@ def check_sql_array(*shoulds) @db.sqls end + it "should give temporary name to name model-specific module" do + c = Sequel::Model(:items) + c.plugin :nested_attributes + c.many_to_one :c, :class=>c + c.nested_attributes :c + c.ancestors[1].name.must_equal "Sequel::_Model(:items)::@nested_attributes_module" + end if RUBY_VERSION >= '3.3' + it "should not modify options hash when loading plugin" do h = {} @Concert.nested_attributes :albums, h diff --git a/spec/extensions/pg_row_spec.rb b/spec/extensions/pg_row_spec.rb index 6461a8a822..ad2673c143 100644 --- a/spec/extensions/pg_row_spec.rb +++ b/spec/extensions/pg_row_spec.rb @@ -46,6 +46,7 @@ it "should be able to register custom parsing of row types as array-like objects" do klass = @m::ArrayRow.subclass(:foo) + klass.name.must_equal "Sequel::Postgres::PGRow::ArrayRow::_Subclass(foo)" if RUBY_VERSION >= '3.3' parser = @m::Parser.new(:converter=>klass) a = parser.call("(a,b,c)") a.class.must_equal(klass) @@ -197,6 +198,7 @@ @db.send(:schema_composite_type, 'other').must_equal :composite c = p1.converter + c.name.must_equal "Sequel::Postgres::PGRow::HashRow::_Subclass(foo)" if RUBY_VERSION >= '3.3' c.superclass.must_equal @m::HashRow c.columns.must_equal [:bar, :baz] c.db_type.must_equal :foo diff --git a/spec/extensions/rcte_tree_spec.rb b/spec/extensions/rcte_tree_spec.rb index c787a802c9..1156a79a6c 100644 --- a/spec/extensions/rcte_tree_spec.rb +++ b/spec/extensions/rcte_tree_spec.rb @@ -16,6 +16,13 @@ def self.name; 'Node'; end @db.sqls end + it "should give temporary name to name model-specific module" do + c = Sequel::Model(:items) + c.columns :id, :name, :parent_id, :i, :pi + c.plugin :rcte_tree + c.ancestors[1].name.must_equal "Sequel::_Model(:items)::_rcte_tree[:methods_module]" + end if RUBY_VERSION >= '3.3' + it "should define the correct associations" do @c.plugin :rcte_tree @c.associations.sort_by{|x| x.to_s}.must_equal [:ancestors, :children, :descendants, :parent] diff --git a/spec/extensions/serialization_spec.rb b/spec/extensions/serialization_spec.rb index 232ecad776..7ca3832382 100644 --- a/spec/extensions/serialization_spec.rb +++ b/spec/extensions/serialization_spec.rb @@ -12,6 +12,12 @@ DB.reset end + it "should give temporary name to name model-specific module" do + c = Sequel::Model(:items) + c.plugin :serialization, :yaml, :abc + c.ancestors[1].name.must_equal "Sequel::_Model(:items)::@serialization_module" + end if RUBY_VERSION >= '3.3' + it "should allow setting additional serializable attributes via plugin :serialization call" do @c.plugin :serialization, :yaml, :abc @c.create(:abc => 1, :def=> 2) diff --git a/spec/extensions/sql_comments_plugin_spec.rb b/spec/extensions/sql_comments_plugin_spec.rb index 53705ea004..9cd9930326 100644 --- a/spec/extensions/sql_comments_plugin_spec.rb +++ b/spec/extensions/sql_comments_plugin_spec.rb @@ -14,6 +14,15 @@ def @c.to_s; 'C' end @db.sqls end + it "should give temporary name to name model-specific dataset module" do + def @c.name; "Foo" end + @c.dataset_module{where :a, :a} + @c.sql_comments_dataset_methods :a + @c.dataset.class.ancestors[1].name.must_equal "Foo::@_sql_comments_dataset_module" + @c.a.all + @db.sqls.must_equal ["SELECT * FROM t WHERE a -- model:C,method_type:dataset,method:all\n"] + end if RUBY_VERSION >= '3.3' + it "should include SQL comments for default class methods that issue queries" do @c.with_pk!(1) @db.sqls.must_equal ["SELECT * FROM t WHERE (id = 1) LIMIT 1 -- model:C,method_type:class,method:with_pk\n"] diff --git a/spec/extensions/subset_conditions_spec.rb b/spec/extensions/subset_conditions_spec.rb index faefd8d1ec..a0c1220d8f 100644 --- a/spec/extensions/subset_conditions_spec.rb +++ b/spec/extensions/subset_conditions_spec.rb @@ -6,6 +6,12 @@ @c.plugin :subset_conditions end + it "should name generated dataset module class" do + c = Sequel::Model(:items) + c.plugin :subset_conditions + c.dataset_module_class.name.must_equal "Sequel::_Model(:items)::@dataset_module_class(SubsetConditions)" + end if RUBY_VERSION >= '3.3' + it "should provide *_conditions method return the arguments passed" do @c.dataset_module{subset(:published, :published => true)} @c.where(@c.published_conditions).sql.must_equal @c.published.sql diff --git a/spec/extensions/subset_static_cache_spec.rb b/spec/extensions/subset_static_cache_spec.rb index 9d9d0b0d01..d116de5064 100644 --- a/spec/extensions/subset_static_cache_spec.rb +++ b/spec/extensions/subset_static_cache_spec.rb @@ -24,6 +24,16 @@ @c2 = @c.load(:id=>2) end + it "should give temporary name to name model-specific module" do + c = Sequel::Model(:items) + c.class_eval do + dataset_module{where :foo, :bar} + plugin :subset_static_cache + cache_subset :foo + end + c.singleton_class.ancestors[1].name.must_equal "Sequel::_Model(:items)::@subset_static_cache_module" + end if RUBY_VERSION >= '3.3' + it "should have .with_pk use the cache without a query" do @ds.with_pk(1) @ds.with_pk(1).must_equal @c1 diff --git a/spec/model/model_spec.rb b/spec/model/model_spec.rb index 524251a50a..29530572d1 100644 --- a/spec/model/model_spec.rb +++ b/spec/model/model_spec.rb @@ -5,6 +5,13 @@ @db = Sequel::Model.db end + it "should return a model with a temporary name" do + m = Sequel::Model(:blah) + m.name.must_equal "Sequel::_Model(:blah)" + m.send(:dataset_methods_module).name.must_equal "Sequel::_Model(:blah)::@dataset_methods_module" + m.send(:overridable_methods_module).name.must_equal "Sequel::_Model(:blah)::@overridable_methods_module" + end if RUBY_VERSION >= '3.3' + it "should return a model subclass with the given dataset if given a dataset" do ds = @db[:blah] c = Sequel::Model(ds)