开发者

How to write a method for all Classes

开发者 https://www.devze.com 2023-02-12 11:26 出处:网络
I am trying to write a method that would appl开发者_如何学Goy directly to several models with HABTM relations to clean up any unused relations.

I am trying to write a method that would appl开发者_如何学Goy directly to several models with HABTM relations to clean up any unused relations.

 
   def cleanup
        self.all.each do |f|
            if f.videos.count == 0
                self.destroy(f)
            end
        end
    end

Where do I save this method to and is this even the correct syntax for such a method? It would theoretically be run as:

    >>Tag.cleanup


Write external module and include it in each Model you need


Sadly people keep on using has_and_belongs_to_many even though it leads to all kinds of orphans like this. A has_many ..., :through relationship can be flagged :dependent => :destroy to clean up unused children automatically. It's common that you'll have unused join records and they are obnoxious to remove.

What you might do is approach this from a SQL angle since has_and_belongs_to_many records are inaccessible if their parent records are no longer defined. They simply do not exist as far as ActiveRecord is concerned. Using a join model means you can always access this data since they are issued their own ids.

has_and_belongs_to_many relationships are based on a compound key which makes removing them a serious nuisance. Normally you'd do a DELETE FROM table WHERE id IN (...) AND ... and be confident that only the target records are removed. With a compound key you can't do this.

You may find this works for an example Tag to Item relationship:

DELETE FROM item_tags, tags, items WHERE item_tags.tag_id=tags.id AND item_tags.item_id=items.id AND tags.id IS NULL AND items.id IS NULL

The DELETE statement can be really particular about how it operates and does not give the same latitude as a SELECT with joins that can be defined as left or right, inner or outer as required.

If you had a primary ID key in your join table you could do it easily:

DELETE FROM item_tags WHERE id IN (SELECT id FROM item_tags LEFT JOIN tags ON item_tags.tag_id=tags.id LEFT JOIN items ON item_tags.item_id=items.id WHERE tags.id IS NULL AND items.id IS NULL)

In fact, it might be advantageous to add a primary key to your relationship table even if ActiveRecord ignores it.

Edit:

As for your module issue, if you're stuck with that approach:

module CleanupMethods
  def cleanup
    # ...
  end
end

class Tag
  # Import the module methods as class methods
  extend CleanupMethods
end

If you use a counter cache column you can do this a lot more easily, but you will also have to ensure your counter caches are accurate.


You want to add a class method to the Tag class, and instead of iterating through all the tag objects (requiring rails to load each one) and then checking for videos through Active Record, it's faster to load all the orphaned records using a query and then destroy only those.

Guessing that you have tags and videos, here, and that tag_videos is your join table, in Rails 2.x you might write

def self.cleanup
  find(:all, :conditions => "id NOT IN (select tag_id from tag_videos)").destroy_all
end

In Rails 3 you'd write

def self.cleanup
  where("id NOT IN (select tag_id from tag_videos)").destroy_all
end
0

精彩评论

暂无评论...
验证码 换一张
取 消