Skip to content

Commit

Permalink
Add :extend association option, for extending a dataset with module(s)
Browse files Browse the repository at this point in the history
Update the advanced_associations.rdoc file to show a cool usage of
this.
  • Loading branch information
jeremyevans committed Jul 5, 2008
1 parent fe47ea2 commit a01f10b
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 4 deletions.
4 changes: 4 additions & 0 deletions sequel/CHANGELOG
@@ -1,5 +1,9 @@
=== HEAD

* Add :extend association option, extending the dataset with module(s) (jeremyevans)

* Add :after_load association callback option, called after associated objects have been loaded from the database (jeremyevans)

* Make validation methods support a :tag option, to work correctly with source reloading (jeremyevans)

* Add :before_add, :after_add, :before_remove, :after_remove association callback options (jeremyevans)
Expand Down
31 changes: 28 additions & 3 deletions sequel/doc/advanced_associations.rdoc
Expand Up @@ -151,9 +151,22 @@ otherwise modified:
one_to_many :authorships
end
Author.first.authorships_dataset.filter(:number < 10).first

If you want to add methods to the association instead of just using existing
methods:

You can extend a dataset with a module easily with :extend:

module FindOrCreate
def find_or_create(vals)
first(vals) || @opts[:models][nil].create(vals)
end
end
class Author < Sequel::Model
one_to_many :authorships, :extend=>FindOrCreate
end
Author.first.authorships_dataset.find_or_create(:name=>'Blah', :number=>10)

However, note that the dataset doesn't have any knowledge of the model object
that created it via the association, so if you want to use attributes of the
model object, you'll have to use a closure:

class Author < Sequel::Model
one_to_many :authorships, :dataset=>(proc do
Expand All @@ -167,6 +180,18 @@ methods:
end
Author.first.authorships_dataset.find_or_create_by_name('Bob')

You can cheat if you want to:

module FindOrCreate
def find_or_create(vals)
# Exploits the fact that Sequel filters are ruby objects that
# can be introspected.
author_id = @opts[:where].args[1]
first(vals) || \
@opts[:models][nil].create(vals.merge(:author_id=>author_id))
end
end

===has_many :through associations

many_to_many handles the usual case of a has_many :through with a belongs_to in
Expand Down
5 changes: 4 additions & 1 deletion sequel/lib/sequel_model/associations.rb
Expand Up @@ -118,6 +118,7 @@ def all_association_reflections
# and a hash of dependent associations. The associated records should
# be queried from the database and the associations cache for each
# record should be populated for this to work correctly.
# - :extend - A module or array of modules to extend the dataset with.
# - :graph_block - The block to pass to join_table when eagerly loading
# the association via eager_graph.
# - :graph_conditions - The additional conditions to use on the SQL join when eagerly loading
Expand Down Expand Up @@ -193,7 +194,7 @@ def associate(type, name, opts = {}, &block)
opts[:graph_join_type] ||= :left_outer
opts[:graph_conditions] = opts[:graph_conditions] ? opts[:graph_conditions].to_a : []
opts[:graph_select] = Array(opts[:graph_select]) if opts[:graph_select]
[:before_add, :before_remove, :after_add, :after_remove, :after_load].each do |cb_type|
[:before_add, :before_remove, :after_add, :after_remove, :after_load, :extend].each do |cb_type|
opts[cb_type] = Array(opts[cb_type])
end

Expand Down Expand Up @@ -333,6 +334,7 @@ def def_association_dataset_methods(opts)
eager = opts[:eager]
eager_graph = opts[:eager_graph]
limit = opts[:limit]
exten = opts[:extend]
single = opts[:type] == :many_to_one
key = opts[:key] if opts[:type] == :many_to_one
set_reciprocal = opts[:type] == :one_to_many
Expand All @@ -348,6 +350,7 @@ def def_association_dataset_methods(opts)
class_def(dataset_method) do
raise(Sequel::Error, 'model object does not have a primary key') unless pk || single
ds = instance_eval(&dataset)
exten.each{|m| ds.extend(m)}
ds = ds.select(*opts.select) if opts.select
ds = ds.order(*order) if order
ds = ds.limit(*limit) if limit
Expand Down
26 changes: 26 additions & 0 deletions sequel/spec/associations_spec.rb
Expand Up @@ -15,6 +15,32 @@ class ParParent < Sequel::Model
klass.association_reflection(:"par_parent1s").associated_class.should == ParParent
klass.association_reflection(:"par_parent2s").associated_class.should == ParParent
end
it "should allow extending the dataset with :extend option" do
MODEL_DB.reset
klass = Class.new(Sequel::Model(:nodes)) do
columns :id, :a_id
end
mod = Module.new do
def blah
1
end
end
mod2 = Module.new do
def blar
2
end
end

klass.associate :many_to_one, :a, :class=>klass, :extend=>mod
klass.associate :one_to_many, :bs, :class=>klass, :extend=>[mod]
klass.associate :many_to_many, :cs, :class=>klass, :extend=>[mod, mod2]

node = klass.load(:id=>1)
node.a_dataset.blah.should == 1
node.bs_dataset.blah.should == 1
node.cs_dataset.blah.should == 1
node.cs_dataset.blar.should == 2
end
end

describe Sequel::Model, "many_to_one" do
Expand Down

0 comments on commit a01f10b

Please sign in to comment.