:dependent => :destroy with nonstandard :foreign_key?

Assume the following contrived (but plausible) geometric model in which
every Edge has two Vertices, every Vertex has many edges.

So I know it's bad form to answer your own question, but I realized this
must be a job for callbacks, so I RTFM'd. My new definition for Vertex
is:

class Vertex < ActiveRecord::Base
  before_destroy do |record|
    Edge.destroy_all "vertex_a_id = #{record.id}"
    Edge.destroy_all "vertex_b_id = #{record.id}"
  end
end

It appears to do exactly what you'd expect: deleting a Vertex deletes
all connected Edges.

Note that I also deleted the ":has_many edges" association for the same
reason: Edge lacks the vertex_id field that AR depends upon to make the
association, so you can't ask for "va.edges()". But it's simple enough
to mimic its functionality, as in:

class Vertex
  def edges
    Edge.find_all_by_vertex_a_id(self.id) +
Edge.find_all_by_vertex_b_id(self.id)
  end
end

Thanks for listening...
- ff

Fearless Fool wrote:

So I know it's bad form to answer your own question, but I realized this
must be a job for callbacks, so I RTFM'd. My new definition for Vertex
is:

class Vertex < ActiveRecord::Base
  before_destroy do |record|
    Edge.destroy_all "vertex_a_id = #{record.id}"
    Edge.destroy_all "vertex_b_id = #{record.id}"
  end
end

It appears to do exactly what you'd expect: deleting a Vertex deletes
all connected Edges.

Great! You can probably unify those into one query and get better
performance at the expense of a bit of Rails magic.

Note that I also deleted the ":has_many edges" association for the same
reason: Edge lacks the vertex_id field that AR depends upon to make the
association, so you can't ask for "va.edges()". But it's simple enough
to mimic its functionality, as in:

class Vertex
  def edges
    Edge.find_all_by_vertex_a_id(self.id) +
Edge.find_all_by_vertex_b_id(self.id)
  end
end

...which you can now probably use a variant of in :finder_sql to restore
your has_many :edges. Again, you may be able to refactor it into one
query.

Thanks for listening...
- ff

Best,

Resurrecting an old thread because it has a useful lesson:

Marnen Laibow-Koser wrote in post #927576:

Fearless Fool wrote:

[stuff about a before_destroy method using two destroy_all calls]

Great! You can probably unify those into one query and get better
performance at the expense of a bit of Rails magic.

[stuff about an edges method that concatenates two arrays]

...which you can now probably use a variant of in :finder_sql to restore
your has_many :edges. Again, you may be able to refactor it into one
query.

So as usual, Marnen is right (if not ahead of his time). Using the
scoping constructs in Rails 3.0, this all becomes elegant and simple:

  class Edge << ActiveRecord::Base
    belongs_to :vertex_a, :class_name => 'Vertex', :foreign_key =>
'vertex_a_id'
    belongs_to :vertex_b, :class_name => 'Vertex', :foreign_key =>
'vertex_b_id'
    scope :connected_to, lambda (vertex) {
      where("edges.vertex_a_id = ? OR edges.vertex_b_id = ?", vertex.id,
vertex.id
    }
  end

  class Vertex < ActiveRecord::Base
    # caution: we are using delete_all for efficiency
    before_destroy { |vertex| Edge.connected_to(vertex).delete_all }
  end

This generates exactly the compact SQL that you'd expect, both for
finding:

  >> Edge.connected_to(v1)
  => SELECT "edges.*" FROM "edges" WHERE (edges.vertex_a_id = 2 OR
edges.vertex_b_id = 2)

and for deleting:

  >> Edge.connected_to(v1).delete_all
  => DELETE FROM "edges" WHERE (edges.vertex_a_id = 2 OR
edges.vertex_b_id = 2)

O Marnen, Marnen! wherefore art thou Marnen?

Fun fact: "wherefore" means "why". [1]

I wonder where Marnen went, but I never wondered why he was Marnen. I did appreciate his unique brand of scorched earth and incredibly precise answers.

Walter

1. In the famous quote, Juliet is wondering *why* Romeo is named the way he is; because of his name, she cannot be allowed to love him. Her logic, typical for Shakespeare women, is quite sound: "A rose by any other name would smell as sweet..."

Walter Davis wrote in post #1016477:

Resurrecting an old thread because it has a useful lesson:

Marnen Laibow-Koser wrote in post #927576:

[snip]

O Marnen, Marnen! wherefore art thou Marnen?

Fun fact: "wherefore" means "why". [1]

Indeed.

I wonder where Marnen went, but I never wondered why he was Marnen. I
did appreciate his unique brand of scorched earth and incredibly
precise answers.

Um, thanks...? I don't know whether to feel flattered or put down. :smiley:

Anyway, I'm still around. I just haven't been posting much. I went on
a self-imposed vacation from posting back in January (because I was
getting too upset to respond as nicely as I wanted to!), and never got
back into the habit. I'll probably be a more regular contributor again
in the near future.

Walter

1. In the famous quote, Juliet is wondering *why* Romeo is named the
way he is; because of his name, she cannot be allowed to love him. Her
logic, typical for Shakespeare women, is quite sound: "A rose by any
other name would smell as sweet..."

Yup.

Best,

I hope you feel flattered, at least that was what I was aiming for. I did very much enjoy your posts, and learned quite a lot from them. I hope you can find a middle path between saint-like "suffer fools gladly" and and "getting too upset" to start answering again. I'm sure you have quite a lot more to teach me, and others here.

Walter