Skip to content

Commit

Permalink
Support :adder, :remover, and :clearer association options that use k…
Browse files Browse the repository at this point in the history
…eyword arguments in Ruby 2.7+
  • Loading branch information
jeremyevans committed Jan 25, 2021
1 parent b10dcdc commit 54a8500
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 5 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
=== master

* Support :adder, :remover, and :clearer association options that use keyword arguments in Ruby 2.7+ (jeremyevans)

* Make pg_interval use the same number of seconds per year and per month as ActiveSupport::Duration when using ActiveSupport 5.1+ (jeremyevans)

=== 5.40.0 (2021-01-01)
Expand Down
33 changes: 28 additions & 5 deletions lib/sequel/model/associations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1929,8 +1929,22 @@ def association_module(opts=OPTS)
# can be easily overridden in the class itself while allowing for
# super to be called.
def association_module_def(name, opts=OPTS, &block)
association_module(opts).send(:define_method, name, &block)
association_module(opts).send(:alias_method, name, name)
mod = association_module(opts)
mod.send(:define_method, name, &block)
mod.send(:alias_method, name, name)
end

# Add a method to the module included in the class, so the method
# can be easily overridden in the class itself while allowing for
# super to be called. This method allows passing keywords through
# the defined methods.
def association_module_delegate_def(name, opts, &block)
mod = association_module(opts)
mod.send(:define_method, name, &block)
# :nocov:
mod.send(:ruby2_keywords, name) if mod.respond_to?(:ruby2_keywords, true)
# :nocov:
mod.send(:alias_method, name, name)
end

# Add a private method to the module included in the class.
Expand Down Expand Up @@ -1982,17 +1996,17 @@ def def_association_instance_methods(opts)

if adder = opts[:adder]
association_module_private_def(opts[:_add_method], opts, &adder)
association_module_def(opts[:add_method], opts){|o,*args| add_associated_object(opts, o, *args)}
association_module_delegate_def(opts[:add_method], opts){|o,*args| add_associated_object(opts, o, *args)}
end

if remover = opts[:remover]
association_module_private_def(opts[:_remove_method], opts, &remover)
association_module_def(opts[:remove_method], opts){|o,*args| remove_associated_object(opts, o, *args)}
association_module_delegate_def(opts[:remove_method], opts){|o,*args| remove_associated_object(opts, o, *args)}
end

if clearer = opts[:clearer]
association_module_private_def(opts[:_remove_all_method], opts, &clearer)
association_module_def(opts[:remove_all_method], opts){|*args| remove_all_associated_objects(opts, *args)}
association_module_delegate_def(opts[:remove_all_method], opts){|*args| remove_all_associated_objects(opts, *args)}
end
end

Expand Down Expand Up @@ -2424,6 +2438,9 @@ def add_associated_object(opts, o, *args)
run_association_callbacks(opts, :after_add, o)
o
end
# :nocov:
ruby2_keywords(:add_associated_object) if respond_to?(:ruby2_keywords, true)
# :nocov:

# Add/Set the current object to/as the given object's reciprocal association.
def add_reciprocal_object(opts, o)
Expand Down Expand Up @@ -2566,6 +2583,9 @@ def remove_all_associated_objects(opts, *args)
associations[opts[:name]] = []
ret
end
# :nocov:
ruby2_keywords(:remove_all_associated_objects) if respond_to?(:ruby2_keywords, true)
# :nocov:

# Remove the given associated object from the given association
def remove_associated_object(opts, o, *args)
Expand All @@ -2587,6 +2607,9 @@ def remove_associated_object(opts, o, *args)
run_association_callbacks(opts, :after_remove, o)
o
end
# :nocov:
ruby2_keywords(:remove_associated_object) if respond_to?(:ruby2_keywords, true)
# :nocov:

# Check that the object from the associated table specified by the primary key
# is currently associated to the receiver. If it is associated, return the object, otherwise
Expand Down
25 changes: 25 additions & 0 deletions spec/model/associations_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2011,6 +2011,15 @@ def c._node_id=; raise; end
p.instance_variable_get(:@x).must_equal c
end

it "should support an :adder option that accepts keywords" do
@c2.one_to_many :attributes, :class => @c1, :adder=>eval('proc{|x, foo: nil| @x = [x, foo]}')
p = @c2.load(:id=>10)
c = @c1.load(:id=>123)
def c._node_id=; raise; end
p.add_attribute(c, foo: 1)
p.instance_variable_get(:@x).must_equal [c, 1]
end if RUBY_VERSION >= '2.0'

it "should allow additional arguments given to the add_ method and pass them onwards to the _add_ method" do
@c2.one_to_many :attributes, :class => @c1
p = @c2.load(:id=>10)
Expand Down Expand Up @@ -2047,6 +2056,15 @@ def c._node_id=; raise; end
p.instance_variable_get(:@x).must_equal c
end

it "should support a :remover option that accepts keywords" do
@c2.one_to_many :attributes, :class => @c1, :remover=>eval('proc{|x, foo: nil| @x = [x, foo]}')
p = @c2.load(:id=>10)
c = @c1.load(:id=>123)
def c._node_id=; raise; end
p.remove_attribute(c, foo: 1)
p.instance_variable_get(:@x).must_equal [c, 1]
end if RUBY_VERSION >= '2.0'

it "should allow additional arguments given to the remove_ method and pass them onwards to the _remove_ method" do
@c2.one_to_many :attributes, :class => @c1, :reciprocal=>nil
p = @c2.load(:id=>10)
Expand Down Expand Up @@ -2091,6 +2109,13 @@ def p._remove_all_attributes
p.instance_variable_get(:@x).must_equal :foo
end

it "should support a :clearer option that supports keywords" do
@c2.one_to_many :attributes, :class => @c1, :clearer=>eval('proc{|foo: nil| @x = foo}')
p = @c2.load(:id=>10)
p.remove_all_attributes(foo: 1)
p.instance_variable_get(:@x).must_equal 1
end if RUBY_VERSION >= '2.0'

it "should support (before|after)_(add|remove) callbacks" do
h = []
@c2.one_to_many :attributes, :class => @c1, :before_add=>[proc{|x,y| h << x.pk; h << -y.pk}, :blah], :after_add=>proc{h << 3}, :before_remove=>:blah, :after_remove=>[:blahr]
Expand Down

0 comments on commit 54a8500

Please sign in to comment.