Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Make one_to_one setter and *_to_many remove_all methods apply the ass…

…ociation options

Before, association options were ignored by these methods, which lead
to some interesting behavior:

  Artist.one_to_many :good_albums, :class=>:Album,
   :conditions=>{:good=>true}
  Artist.first.remove_all_good_albums
  # Sets artist_id = nil for all of the artist's albums, not just the
  # good ones

I think applying the filters is a better default for these methods.
It's possible this will break some code, especially in the
many_to_many case, but it's better to have a query that doesn't work
than a query that makes modifications you didn't expect.
  • Loading branch information...
commit c32456dcb62761c3132b79d001918d73d7f665ca 1 parent f7e17b3
@jeremyevans authored
View
4 CHANGELOG
@@ -1,5 +1,9 @@
=== HEAD
+* Make one_to_one setter and *_to_many remove_all methods apply the association options (jeremyevans)
+
+* Make nested_attributes plugin handle invalid many_to_one associations better (john_firebaugh)
+
* Remove private methods from Sequel::BasicObject on ruby 1.8 (i.e. most Kernel methods) (jeremyevans)
* Add Sequel::BasicObject.remove_methods!, useful on 1.8 if libraries required after Sequel add methods to Object (jeremyevans)
View
19 lib/sequel/model/associations.rb
@@ -798,7 +798,7 @@ def def_many_to_many(opts)
database.dataset.from(join_table).filter(lcks.zip(lcpks.map{|k| send(k)}) + rcks.zip(opts.right_primary_keys.map{|k| o.send(k)})).delete
end
association_module_private_def(opts._remove_all_method) do
- database.dataset.from(join_table).filter(lcks.zip(lcpks.map{|k| send(k)})).delete
+ _apply_association_options(opts, database.dataset.from(join_table).filter(lcks.zip(lcpks.map{|k| send(k)}))).delete
end
def_add_method(opts)
@@ -919,7 +919,7 @@ def def_one_to_many(opts)
if one_to_one
association_module_private_def(opts._setter_method) do |o|
- up_ds = opts.associated_class.filter(cks.zip(cpks.map{|k| send(k)}))
+ up_ds = _apply_association_options(opts, opts.associated_class.filter(cks.zip(cpks.map{|k| send(k)})))
if o
up_ds = up_ds.exclude(o.pk_hash)
cks.zip(cpks).each{|k, pk| o.send(:"#{k}=", send(pk))}
@@ -943,7 +943,7 @@ def def_one_to_many(opts)
o.save(:validate=>validate) || raise(Sequel::Error, "invalid associated object, cannot save")
end
association_module_private_def(opts._remove_all_method) do
- opts.associated_class.filter(cks.zip(cpks.map{|k| send(k)})).update(ck_nil_hash)
+ _apply_association_options(opts, opts.associated_class.filter(cks.zip(cpks.map{|k| send(k)}))).update(ck_nil_hash)
end
def_remove_methods(opts)
end
@@ -968,11 +968,8 @@ def pk_or_nil
end
private
-
- # Backbone behind association dataset methods
- def _dataset(opts)
- raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk
- ds = send(opts._dataset_method)
+
+ def _apply_association_options(opts, ds)
ds.extend(AssociationDatasetMethods)
ds.model_object = self
ds.association_reflection = opts
@@ -991,6 +988,12 @@ def _dataset(opts)
ds
end
+ # Backbone behind association dataset methods
+ def _dataset(opts)
+ raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk
+ _apply_association_options(opts, send(opts._dataset_method))
+ end
+
# Return the associated objects from the dataset, without callbacks, reciprocals, and caching.
def _load_associated_objects(opts)
if opts.returns_array?
View
27 spec/model/associations_spec.rb
@@ -653,6 +653,17 @@ def d.fetch_rows(s); yield({:id=>3}) end
"UPDATE attributes SET node_id = 1234 WHERE (id = 3)",
'COMMIT']
end
+
+ it "should have setter method respect association filters" do
+ @c2.one_to_one :attribute, :class => @c1, :conditions=>{:a=>1} do |ds|
+ ds.filter(:b=>2)
+ end
+ MODEL_DB.sqls.clear
+ attrib = @c1.load(:id=>3)
+ @c2.new(:id => 1234).attribute = attrib
+ MODEL_DB.sqls.should == ['UPDATE attributes SET node_id = NULL WHERE ((node_id = 1234) AND (a = 1) AND (b = 2) AND (id != 3))',
+ "UPDATE attributes SET node_id = 1234 WHERE (id = 3)"]
+ end
it "should have the setter method respect the :primary_key option" do
@c2.one_to_one :attribute, :class => @c1, :primary_key=>:xxx
@@ -1569,6 +1580,14 @@ def a.valid?; false; end
MODEL_DB.sqls.first.should == 'UPDATE attributes SET node_id = NULL WHERE (node_id = 1234)'
end
+ it "should have remove_all method respect association filters" do
+ @c2.one_to_many :attributes, :class => @c1, :conditions=>{:a=>1} do |ds|
+ ds.filter(:b=>2)
+ end
+ @c2.new(:id => 1234).remove_all_attributes
+ MODEL_DB.sqls.should == ['UPDATE attributes SET node_id = NULL WHERE ((node_id = 1234) AND (a = 1) AND (b = 2))']
+ end
+
it "should have the remove_all_ method respect the :primary_key option" do
@c2.one_to_many :attributes, :class => @c1, :primary_key=>:xxx
@c2.new(:id => 1234, :xxx=>5).remove_all_attributes
@@ -2314,6 +2333,14 @@ def n.valid?; false; end
MODEL_DB.sqls.first.should == 'DELETE FROM attributes_nodes WHERE (node_id = 1234)'
end
+ it "should have remove_all method respect association filters" do
+ @c2.many_to_many :attributes, :class => @c1, :conditions=>{:a=>1} do |ds|
+ ds.filter(:b=>2)
+ end
+ @c2.new(:id => 1234).remove_all_attributes
+ MODEL_DB.sqls.should == ['DELETE FROM attributes_nodes WHERE ((node_id = 1234) AND (a = 1) AND (b = 2))']
+ end
+
it "should have the remove_all_ method respect the :left_primary_key option" do
@c2.many_to_many :attributes, :class => @c1, :left_primary_key=>:xxx
@c2.new(:id => 1234, :xxx=>5).remove_all_attributes
Please sign in to comment.
Something went wrong with that request. Please try again.