Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Association autorefresh plugin #17

Merged
1 commit merged into from

2 participants

@jfirebaugh

No description provided.

@jeremyevans
Owner

This causes a performance hit for every many_to_one association access. Instead, the implementation should just clear the cache for any related associations when the foreign key changes. Also, I think stale_associations would be a better plugin name than association_autorefresh.

John Firebaugh Add AssociationAutoreloading plugin.
The AssociationAutoreloading plugin makes many_to_one association
accessor methods automatically reload the cached object whenever
the association's foreign key is modified:

   album = Album.first
   album.artist_id #=> 1
   album.artist # caches associated artist
   album.artist_id = 2
   album.artist # reloads associated artist
4d547ab
@jfirebaugh

Pushed a new version using attribute setters.

I'm not crazy about stale_associations as a name. If I'm a user I'd be thinking, why would I want my associations to be stale? I think it's clearer to have a name that describes what the plugin does in the positive, rather than the problem it corrects, in the negative. That said, the correct verb here is "reload", not "refresh", so I went with association_autoreloading. This also is a closer match with other association-related plugins (association_dependencies, association_proxies, etc.), and "loading" matches with tactical_eager_loading.

@jeremyevans
Owner

I looked at the patch earlier today, it looks good. I'm in the middle of writing a tiny_tds adapter, which is the only reason I didn't try merging it today. I'll try to merge this tomorrow, and assuming no problems, it will be committed.

I still don't like the plugin name, as autoreloading is not what the plugin actually does (it clears the cache, it doesn't reload). But since you wrote the support, you get to name it. :)

This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Feb 10, 2011
  1. Add AssociationAutoreloading plugin.

    John Firebaugh authored
    The AssociationAutoreloading plugin makes many_to_one association
    accessor methods automatically reload the cached object whenever
    the association's foreign key is modified:
    
       album = Album.first
       album.artist_id #=> 1
       album.artist # caches associated artist
       album.artist_id = 2
       album.artist # reloads associated artist
This page is out of date. Refresh to see the latest.
View
36 lib/sequel/plugins/association_autoreloading.rb
@@ -0,0 +1,36 @@
+module Sequel
+ module Plugins
+ # The AssociationAutoreloading plugin makes many_to_one association
+ # accessor methods automatically reload the cached object whenever
+ # the association's foreign key is modified:
+ #
+ # Album.many_to_one :artists
+ # album = Album.first
+ # album.artist_id #=> 1
+ # album.artist # caches associated artist
+ # album.artist_id = 2
+ # album.artist # reloads associated artist
+ #
+ module AssociationAutoreloading
+ module ClassMethods
+ def def_many_to_one(opts)
+ super
+ @autoreloading_associations ||= {}
+ include(@autoreloading_associations_module ||= Module.new) unless @autoreloading_associations_module
+ opts[:keys].each do |k|
+ assocs = @autoreloading_associations[k] ||= []
+ assocs << opts[:name]
+ @autoreloading_associations_module.class_eval do
+ unless method_defined?("#{k}=")
+ define_method("#{k}=") do |v|
+ super(v)
+ assocs.each{|a| associations.delete(a)}
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
View
62 spec/extensions/association_autoreloading_spec.rb
@@ -0,0 +1,62 @@
+require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
+
+describe "AssociationAutoreloading plugin" do
+ before do
+ @c = Class.new(Sequel::Model)
+ @c.plugin :association_autoreloading
+ @Artist = Class.new(@c).set_dataset(:artists)
+ ds1 = @Artist.dataset
+ def ds1.fetch_rows(s)
+ (MODEL_DB.sqls ||= []) << s
+ yield({:id=>2, :name=>'Ar'})
+ end
+ @Album = Class.new(@c).set_dataset(:albums)
+ @Artist.columns :id, :name
+ @Album.columns :id, :name, :artist_id
+ @Album.many_to_one :artist, :class=>@Artist
+ MODEL_DB.reset
+ end
+
+ specify "should reload many_to_one association when foreign key is modified" do
+ album = @Album.load(:id => 1, :name=>'Al', :artist_id=>2)
+ album.artist
+ MODEL_DB.sqls.should == ['SELECT * FROM artists WHERE (artists.id = 2) LIMIT 1']
+ MODEL_DB.reset
+
+ album.artist_id = 1
+ album.artist
+ MODEL_DB.sqls.should == ['SELECT * FROM artists WHERE (artists.id = 1) LIMIT 1']
+ end
+
+ specify "should reload all associations which use the foreign key" do
+ @Album.many_to_one :other_artist, :key => :artist_id, :foreign_key => :id, :class => @Artist
+ album = @Album.load(:id => 1, :name=>'Al', :artist_id=>2)
+ album.artist
+ album.other_artist
+ MODEL_DB.reset
+
+ album.artist_id = 1
+ album.artist
+ MODEL_DB.sqls.should == ['SELECT * FROM artists WHERE (artists.id = 1) LIMIT 1']
+ MODEL_DB.reset
+
+ album.other_artist
+ MODEL_DB.sqls.should == ['SELECT * FROM artists WHERE (artists.id = 1) LIMIT 1']
+ end
+
+ specify "should work with composite keys" do
+ @Album.many_to_one :composite_artist, :key => [:artist_id, :name], :primary_key => [:id, :name], :class => @Artist
+ album = @Album.load(:id => 1, :name=>'Al', :artist_id=>2)
+ album.composite_artist
+ MODEL_DB.reset
+
+ album.artist_id = 1
+ album.composite_artist
+ MODEL_DB.sqls.should == ["SELECT * FROM artists WHERE ((artists.id = 1) AND (artists.name = 'Al')) LIMIT 1"]
+ MODEL_DB.reset
+
+ album.name = 'Al2'
+ album.composite_artist
+ MODEL_DB.sqls.should == ["SELECT * FROM artists WHERE ((artists.id = 1) AND (artists.name = 'Al2')) LIMIT 1"]
+ end
+end
Something went wrong with that request. Please try again.