Skip to content

Commit

Permalink
Allow class-level dataset methods to be overridable and call super to…
Browse files Browse the repository at this point in the history
… get the default behavior

This is similar to how other model methods are handled, such as
column methods and association methods.
  • Loading branch information
jeremyevans committed Dec 19, 2024
1 parent eba1a8e commit 14fb130
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 9 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
=== master

* Allow class-level dataset methods to be overridable and call super to get the default behavior (jeremyevans)

* Support column aliases with data types on PostgreSQL, useful for selecting from functions returning records (jeremyevans)

* Break ties in timestamp migrator version handling using lexicographic sort of rest of migration filename (jeremyevans)
Expand Down
31 changes: 22 additions & 9 deletions lib/sequel/model/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -762,22 +762,35 @@ def def_column_accessor(*columns)
end
end
end

# Module that the class methods that call dataset methods are kept in.
# This allows the methods to be overridden and call super with the
# default behavior.
def dataset_methods_module
return @dataset_methods_module if defined?(@dataset_methods_module)
Sequel.synchronize{@dataset_methods_module ||= Module.new}
extend(@dataset_methods_module)
@dataset_methods_module
end

# Define a model method that calls the dataset method with the same name,
# only used for methods with names that can't be represented directly in
# ruby code.
# Define a model method that calls the dataset method with the same name.
def def_model_dataset_method(meth)
return if respond_to?(meth, true)

mod = dataset_methods_module

if meth.to_s =~ /\A[A-Za-z_][A-Za-z0-9_]*\z/
instance_eval("def #{meth}(*args, &block); dataset.#{meth}(*args, &block) end", __FILE__, __LINE__)
mod.module_eval(<<END, __FILE__, __LINE__ + 1)
def #{meth}(*args, &block); dataset.#{meth}(*args, &block) end
ruby2_keywords :#{meth} if respond_to?(:ruby2_keywords, true)
END
else
define_singleton_method(meth){|*args, &block| dataset.public_send(meth, *args, &block)}
mod.send(:define_method, meth){|*args, &block| dataset.public_send(meth, *args, &block)}
# :nocov:
mod.send(:ruby2_keywords, meth) if respond_to?(:ruby2_keywords, true)
# :nocov:
end
singleton_class.send(:alias_method, meth, meth)
# :nocov:
singleton_class.send(:ruby2_keywords, meth) if respond_to?(:ruby2_keywords, true)
# :nocov:
mod.send(:alias_method, meth, meth)
end

# Get the schema from the database, fall back on checking the columns
Expand Down
6 changes: 6 additions & 0 deletions spec/model/base_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,12 @@ class BlahBlah < Class.new(Sequel::Model); end
@c.return_3.must_equal 3
end

it "should have methods defined on the class be overridable and allow calling super" do
@c.dataset_module{def return_3() 3 end}
@c.define_singleton_method(:return_3){super()}
@c.return_3.must_equal 3
end

it "should add methods defined in the module outside the block to the class" do
@c.dataset_module.module_eval{def return_3() 3 end}
@c.return_3.must_equal 3
Expand Down

0 comments on commit 14fb130

Please sign in to comment.