sequel 2.0.1 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,3 +1,41 @@
1
+ === HEAD
2
+
3
+ * Break association add_/remove_/remove_all_ methods into two parts, for easier overriding (jeremyevans)
4
+
5
+ * Add Model.strict_param_setting, on by default, which raises errors if a missing/restricted method is called via new/set/update/etc. (jeremyevans)
6
+
7
+ * Raise errors when using association methods on objects without valid primary keys (jeremyevans)
8
+
9
+ * The model's primary key is a restricted column by default, Add model.unrestrict_primary_key to get the old behavior (jeremyevans)
10
+
11
+ * Add Model.set_(allowed|restricted)_columns, which affect which columns create/new/set/update/etc. modify (jeremyevans)
12
+
13
+ * Calls to Model.def_dataset_method with a block are cached and reapplied to the new dataset if set_dataset is called, even in a subclass (jeremyevans)
14
+
15
+ * The :reciprocal option to associations should now be the symbol name of the reciprocal association, not an instance variable symbol (jeremyevans)
16
+
17
+ * Add Model#associations, which is a hash holding a cache of associated objects, with each association being a separate key (jeremyevans)
18
+
19
+ * Make all associations support a :graph_select option, specifying a column or array of columns to select when using eager_graph (jeremyevans)
20
+
21
+ * Bring back Model#set and Model#update, now the same as Model#set_with_params and Model#update_with_params (jeremyevans)
22
+
23
+ * Allow model datasets to call to_hash without any arguments, which allows easy creation of identity maps (jeremyevans)
24
+
25
+ * Add Model.set_sti_key, for easily setting up single table inheritance (jeremyevans)
26
+
27
+ * Make all associations support a :read_only option, which doesn't add methods that modify the database (jeremyevans)
28
+
29
+ * Make *_to_many associations support a :limit option, for specifying a limit to the resulting records (and possibly an offset) (jeremyevans)
30
+
31
+ * Make association block argument and :eager option affect the _dataset method (jeremyevans)
32
+
33
+ * Add a :one_to_one option to one_to_many associations, which creates a getter and setter similar to many_to_one (a.k.a. has_one) (jeremyevans)
34
+
35
+ * add_ and remove_ one_to_many association methods now raise an error if the passed object cannot be saved, instead of saving without validation (jeremyevans)
36
+
37
+ * Add support for :if option on validations, using a symbol (specifying an instance method) or a proc (dtsato)
38
+
1
39
  === 2.0.1 (2008-06-04)
2
40
 
3
41
  * Make the choice of Time or DateTime optional for typecasting :datetime types, default to Time (jeremyevans)
data/README CHANGED
@@ -26,8 +26,7 @@ You can, however, explicitly set the table name or even the dataset used:
26
26
  * {Source code}[http://github.com/jeremyevans/sequel]
27
27
  * {Bug tracking}[http://code.google.com/p/ruby-sequel/issues/list]
28
28
  * {Google group}[http://groups.google.com/group/sequel-talk]
29
- * {RubyForge page}[http://rubyforge.org/projects/sequel/]
30
- * {API RDoc}[http://sequel.rubyforge.org]
29
+ * {RDoc}[http://sequel.rubyforge.org]
31
30
 
32
31
  To check out the source code:
33
32
 
@@ -127,11 +126,11 @@ Hooks are defined by supplying a block:
127
126
 
128
127
  class Post < Sequel::Model
129
128
  after_create do
130
- self.created_at = Time.now
129
+ author.increase_post_count
131
130
  end
132
131
 
133
132
  after_destroy do
134
- author.update_post_count
133
+ author.decrease_post_count
135
134
  end
136
135
  end
137
136
 
data/Rakefile CHANGED
@@ -9,8 +9,8 @@ include FileUtils
9
9
  # Configuration
10
10
  ##############################################################################
11
11
  NAME = "sequel"
12
- VERS = "2.0.1"
13
- SEQUEL_CORE_VERS= "2.0.1"
12
+ VERS = "2.1.0"
13
+ SEQUEL_CORE_VERS= "2.1.0"
14
14
  CLEAN.include ["**/.*.sw?", "pkg", ".config", "rdoc", "coverage"]
15
15
  RDOC_OPTS = ["--quiet", "--line-numbers", "--inline-source", '--title', \
16
16
  'Sequel: The Database Toolkit for Ruby: Model Classes', '--main', 'README']
@@ -60,13 +60,13 @@ end
60
60
  desc "Install sequel gem"
61
61
  task :install do
62
62
  sh %{rake package}
63
- sh %{sudo gem install pkg/#{NAME}-#{VERS}}
63
+ sh %{sudo gem install pkg/#{NAME}-#{VERS} --local}
64
64
  end
65
65
 
66
66
  desc "Install sequel gem without docs"
67
67
  task :install_no_docs do
68
68
  sh %{rake package}
69
- sh %{sudo gem install pkg/#{NAME}-#{VERS} --no-rdoc --no-ri}
69
+ sh %{sudo gem install pkg/#{NAME}-#{VERS} --no-rdoc --no-ri --local}
70
70
  end
71
71
 
72
72
  desc "Uninstall sequel gem"
data/lib/sequel_model.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  require 'sequel_core'
2
- %w"inflector base hooks record schema association_reflection
2
+ %w"inflector base hooks record schema association_reflection dataset_methods
3
3
  associations caching plugins validations eager_loading".each do |f|
4
4
  require "sequel_model/#{f}"
5
5
  end
@@ -49,9 +49,29 @@ module Sequel
49
49
  # should not override these instance methods.
50
50
  # * The following instance_methods all call the class method of the same
51
51
  # name: columns, dataset, db, primary_key, str_columns.
52
+ # * The following class level attr_readers are created: allowed_columns,
53
+ # cache_store, cache_ttl, dataset_methods, primary_key, restricted_columns,
54
+ # sti_dataset, and sti_key. You should not usually need to
55
+ # access these directly.
56
+ # * The following class level attr_accessors are created: strict_param_setting
57
+ # and typecast_on_assignment:
58
+ #
59
+ # # Don't raise errors in new/set/update/etc. if an attempt to
60
+ # # access a missing/restricted method occurs (just silently
61
+ # # skip it)
62
+ # Model.strict_param_setting = false
63
+ # Model.new(:id=>1) # No Error
64
+ # # Don't typecast attribute values on assignment
65
+ # Model.typecast_on_assignment = false
66
+ # m = Model.new
67
+ # m.number = '10'
68
+ # m.number # => '10' instead of 10
69
+ #
70
+ # * The following class level method aliases are defined:
71
+ # * Model.dataset= => set_dataset
72
+ # * Model.is_a => is
52
73
  class Model
53
74
  extend Enumerable
54
75
  extend Associations
55
- include Validation
56
76
  end
57
77
  end
@@ -28,14 +28,14 @@ module Sequel
28
28
  associated_class.all_association_reflections.each do |assoc_reflect|
29
29
  if assoc_reflect[:type] == :many_to_many && assoc_reflect[:left_key] == right_key \
30
30
  && assoc_reflect[:right_key] == left_key && assoc_reflect[:join_table] == join_table
31
- return self[:reciprocal] = association_ivar(assoc_reflect[:name])
31
+ return self[:reciprocal] = assoc_reflect[:name]
32
32
  end
33
33
  end
34
34
  else
35
35
  key = self[:key]
36
36
  associated_class.all_association_reflections.each do |assoc_reflect|
37
37
  if assoc_reflect[:type] == reciprocal_type && assoc_reflect[:key] == key
38
- return self[:reciprocal] = association_ivar(assoc_reflect[:name])
38
+ return self[:reciprocal] = assoc_reflect[:name]
39
39
  end
40
40
  end
41
41
  end
@@ -46,13 +46,6 @@ module Sequel
46
46
  def select
47
47
  self[:select] ||= associated_class.table_name.*
48
48
  end
49
-
50
- private
51
-
52
- # Name symbol of association instance variable
53
- def association_ivar(name)
54
- :"@#{name}"
55
- end
56
49
  end
57
50
  end
58
51
  end
@@ -21,6 +21,12 @@
21
21
  # * remove_milestone(obj) - Removes the association with the passed milestone
22
22
  # * remove_all_milestones - Removes associations with all associated milestones
23
23
  #
24
+ # If you want to override the behavior of the add_/remove_/remove_all_ methods,
25
+ # there are private instance methods created that a prepended with an
26
+ # underscore (e.g. _add_milestone). The private instance methods can be
27
+ # easily overridden, but you shouldn't override the public instance methods,
28
+ # as they deal with how associations are cached.
29
+ #
24
30
  # By default the classes for the associations are inferred from the association
25
31
  # name, so for example the Project#portfolio will return an instance of
26
32
  # Portfolio, and Project#milestones will return an array of Milestone
@@ -68,6 +74,12 @@ module Sequel::Model::Associations
68
74
  # model object can be associated with many current model objects.
69
75
  # Similar to ActiveRecord/DataMapper's has_and_belongs_to_many.
70
76
  #
77
+ # A one to one relationship can be set up with a many_to_one association
78
+ # on the table with the foreign key, and a one_to_many association with the
79
+ # :one_to_one option specified on the table without the foreign key. The
80
+ # two associations will operate similarly, except that the many_to_one
81
+ # association setter doesn't update the database until you call save manually.
82
+ #
71
83
  # The following options can be supplied:
72
84
  # * *ALL types*:
73
85
  # - :allow_eager - If set to false, you cannot load the association eagerly
@@ -86,9 +98,14 @@ module Sequel::Model::Associations
86
98
  # the association via eager_graph
87
99
  # - :graph_join_type - The type of SQL join to use when eagerly loading the association via
88
100
  # eager_graph
101
+ # - :graph_select - A column or array of columns to select from the associated table
102
+ # when eagerly loading the association via eager_graph. Defaults to all
103
+ # columns in the associated table.
89
104
  # - :order - the column(s) by which to order the association dataset. Can be a
90
105
  # singular column or an array.
91
- # - :reciprocal - the symbol name of the instance variable of the reciprocal association,
106
+ # - :read_only - Do not add a setter method (for many_to_one or one_to_many with :one_to_one),
107
+ # or add_/remove_/remove_all_ methods (for one_to_many, many_to_many)
108
+ # - :reciprocal - the symbol name of the reciprocal association,
92
109
  # if it exists. By default, sequel will try to determine it by looking at the
93
110
  # associated model's assocations for a association that matches
94
111
  # the current association's key(s). Set to nil to not use a reciprocal.
@@ -98,6 +115,9 @@ module Sequel::Model::Associations
98
115
  # use this option, but beware that the join table attributes can clash with
99
116
  # attributes from the model table, so you should alias any attributes that have
100
117
  # the same name in both the join table and the associated table.
118
+ # * :one_to_many, :many_to_many:
119
+ # - :limit - Limit the number of records to the provided value. Use
120
+ # an array with two arguments for the value to specify a limit and offset.
101
121
  # * :many_to_one:
102
122
  # - :key - foreign_key in current model's table that references
103
123
  # associated model's primary key, as a symbol. Defaults to :"#{name}_id".
@@ -105,6 +125,14 @@ module Sequel::Model::Associations
105
125
  # - :key - foreign key in associated model's table that references
106
126
  # current model's primary key, as a symbol. Defaults to
107
127
  # :"#{self.name.underscore}_id".
128
+ # - :one_to_one: Create a getter and setter similar to those of many_to_one
129
+ # associations. The getter returns a singular matching record, or raises an
130
+ # error if multiple records match. The setter updates the record given and removes
131
+ # associations with all other records. When this option is used, the other
132
+ # association methods usually added are either removed or made private,
133
+ # so using this is similar to using many_to_one, in terms of the methods
134
+ # it adds, the main difference is that the foreign key is in the associated
135
+ # table instead of the current table.
108
136
  # * :many_to_many:
109
137
  # - :join_table - name of table that includes the foreign keys to both
110
138
  # the current model and the associated model, as a symbol. Defaults to the name
@@ -126,6 +154,7 @@ module Sequel::Model::Associations
126
154
  opts[:eager_block] = block unless opts.include?(:eager_block)
127
155
  opts[:graph_join_type] ||= :left_outer
128
156
  opts[:graph_conditions] = opts[:graph_conditions] ? opts[:graph_conditions].to_a : []
157
+ opts[:graph_select] = Array(opts[:graph_select]) if opts[:graph_select]
129
158
 
130
159
  # find class
131
160
  case opts[:class]
@@ -177,11 +206,6 @@ module Sequel::Model::Associations
177
206
  :"add_#{name.to_s.singularize}"
178
207
  end
179
208
 
180
- # Name symbol of association instance variable
181
- def association_ivar(name)
182
- :"@#{name}"
183
- end
184
-
185
209
  # Name symbol for remove_all association method
186
210
  def association_remove_all_method_name(name)
187
211
  :"remove_all_#{name}"
@@ -203,47 +227,44 @@ module Sequel::Model::Associations
203
227
  dataset_method = :"#{name}_dataset"
204
228
  helper_method = :"#{name}_helper"
205
229
  dataset_block = opts[:block]
206
- ivar = association_ivar(name)
207
-
208
- # define a method returning the association dataset (with optional order)
209
- if order = opts[:order]
210
- class_def(dataset_method) {instance_eval(&block).order(*order)}
211
- else
212
- class_def(dataset_method, &block)
213
- end
214
-
230
+ order = opts[:order]
231
+ eager = opts[:eager]
232
+ limit = opts[:limit]
233
+
215
234
  # If a block is given, define a helper method for it, because it takes
216
235
  # an argument. This is unnecessary in Ruby 1.9, as that has instance_exec.
217
236
  if dataset_block
218
237
  class_def(helper_method, &dataset_block)
238
+ private helper_method
239
+ end
240
+
241
+ # define a method returning the association dataset (with optional order)
242
+ class_def(dataset_method) do
243
+ raise(Sequel::Error, 'model object does not have a primary key') unless pk
244
+ ds = instance_eval(&block).select(*opts.select)
245
+ ds = ds.order(*order) if order
246
+ ds = ds.limit(*limit) if limit
247
+ ds = ds.eager(eager) if eager
248
+ ds = send(helper_method, ds) if dataset_block
249
+ ds
219
250
  end
220
251
 
221
252
  class_def(name) do |*reload|
222
- if !reload[0] && obj = instance_variable_get(ivar)
223
- obj
253
+ if (assoc = @associations).include?(name) and !reload[0]
254
+ assoc[name]
224
255
  else
225
- ds = send(dataset_method)
226
- # if the a dataset block was specified, we need to call it and use
227
- # the result as the dataset to fetch records from.
228
- if dataset_block
229
- ds = send(helper_method, ds)
230
- end
231
- if eager = opts[:eager]
232
- ds = ds.eager(eager)
233
- end
234
- objs = ds.all
256
+ objs = send(dataset_method).all
235
257
  # Only one_to_many associations should set the reciprocal object
236
258
  if (opts[:type] == :one_to_many) && (reciprocal = opts.reciprocal)
237
- objs.each{|o| o.instance_variable_set(reciprocal, self)}
259
+ objs.each{|o| o.associations[reciprocal] = self}
238
260
  end
239
- instance_variable_set(ivar, objs)
261
+ assoc[name] = objs
240
262
  end
241
263
  end
242
264
  end
243
265
 
244
266
  # Adds many_to_many association instance methods
245
267
  def def_many_to_many(name, opts)
246
- ivar = association_ivar(name)
247
268
  left = (opts[:left_key] ||= default_remote_key)
248
269
  right = (opts[:right_key] ||= default_foreign_key(opts))
249
270
  opts[:class_name] ||= name.to_s.singularize.camelize
@@ -254,75 +275,100 @@ module Sequel::Model::Associations
254
275
  database = db
255
276
 
256
277
  def_association_dataset_methods(name, opts) do
257
- opts.associated_class.select(*opts.select).inner_join(join_table, [[right, opts.associated_primary_key], [left, pk]])
278
+ opts.associated_class.inner_join(join_table, [[right, opts.associated_primary_key], [left, pk]])
258
279
  end
259
280
 
260
- class_def(association_add_method_name(name)) do |o|
281
+ return if opts[:read_only]
282
+
283
+ add_meth = association_add_method_name(name)
284
+ internal_add_meth = :"_#{add_meth}"
285
+ remove_meth = association_remove_method_name(name)
286
+ internal_remove_meth = :"_#{remove_meth}"
287
+ remove_all_meth = association_remove_all_method_name(name)
288
+ internal_remove_all_meth = :"_#{remove_all_meth}"
289
+
290
+ class_def(internal_add_meth) do |o|
261
291
  database[join_table].insert(left=>pk, right=>o.pk)
262
- if arr = instance_variable_get(ivar)
263
- arr.push(o)
292
+ end
293
+ class_def(internal_remove_meth) do |o|
294
+ database[join_table].filter([[left, pk], [right, o.pk]]).delete
295
+ end
296
+ class_def(internal_remove_all_meth) do
297
+ database[join_table].filter(left=>pk).delete
298
+ end
299
+ private internal_add_meth, internal_remove_meth, internal_remove_all_meth
300
+
301
+ class_def(add_meth) do |o|
302
+ raise(Sequel::Error, 'model object does not have a primary key') unless pk && o.pk
303
+ send(internal_add_meth, o)
304
+ if (assoc = @associations).include?(name)
305
+ assoc[name].push(o)
264
306
  end
265
- if (reciprocal = opts.reciprocal) && (list = o.instance_variable_get(reciprocal)) \
266
- && !(list.include?(self))
267
- list.push(self)
307
+ if reciprocal = opts.reciprocal and array = o.associations[reciprocal] and !array.include?(self)
308
+ array.push(self)
268
309
  end
269
310
  o
270
311
  end
271
- class_def(association_remove_method_name(name)) do |o|
272
- database[join_table].filter([[left, pk], [right, o.pk]]).delete
273
- if arr = instance_variable_get(ivar)
274
- arr.delete(o)
312
+ class_def(remove_meth) do |o|
313
+ raise(Sequel::Error, 'model object does not have a primary key') unless pk && o.pk
314
+ send(internal_remove_meth, o)
315
+ if (assoc = @associations).include?(name)
316
+ assoc[name].delete_if{|x| o === x}
275
317
  end
276
- if (reciprocal = opts.reciprocal) && (list = o.instance_variable_get(reciprocal))
277
- list.delete(self)
318
+ if reciprocal = opts.reciprocal and array = o.associations[reciprocal]
319
+ array.delete_if{|x| self === x}
278
320
  end
279
321
  o
280
322
  end
281
- class_def(association_remove_all_method_name(name)) do
282
- database[join_table].filter(left=>pk).delete
283
- if arr = instance_variable_get(ivar)
323
+ class_def(remove_all_meth) do
324
+ raise(Sequel::Error, 'model object does not have a primary key') unless pk
325
+ send(internal_remove_all_meth)
326
+ if (assoc = @associations).include?(name)
284
327
  reciprocal = opts.reciprocal
328
+ arr = assoc[name]
285
329
  ret = arr.dup
286
330
  arr.each do |o|
287
- if reciprocal && (list = o.instance_variable_get(reciprocal))
288
- list.delete(self)
331
+ if reciprocal and array = o.associations[reciprocal]
332
+ array.delete_if{|x| self === x}
289
333
  end
290
334
  end
291
335
  end
292
- instance_variable_set(ivar, [])
336
+ assoc[name] = []
293
337
  ret
294
338
  end
295
339
  end
296
340
 
297
341
  # Adds many_to_one association instance methods
298
342
  def def_many_to_one(name, opts)
299
- ivar = association_ivar(name)
300
-
301
343
  key = (opts[:key] ||= default_foreign_key(opts))
302
344
  opts[:class_name] ||= name.to_s.camelize
303
345
 
304
346
  class_def(name) do |*reload|
305
- if !reload[0] && obj = instance_variable_get(ivar)
306
- obj == :null ? nil : obj
347
+ if (assoc = @associations).include?(name) and !reload[0]
348
+ assoc[name]
307
349
  else
308
350
  obj = if fk = send(key)
309
351
  opts.associated_class.select(*opts.select).filter(opts.associated_primary_key=>fk).first
310
352
  end
311
- instance_variable_set(ivar, obj || :null)
312
- obj
353
+ assoc[name] = obj
313
354
  end
314
355
  end
356
+ return if opts[:read_only]
315
357
 
316
- class_def(:"#{name}=") do |o|
317
- old_val = instance_variable_get(ivar) if reciprocal = opts.reciprocal
318
- instance_variable_set(ivar, o)
358
+ class_def(:"#{name}=") do |o|
359
+ raise(Sequel::Error, 'model object does not have a primary key') if o && !o.pk
360
+ reciprocal = opts.reciprocal
361
+ if (assoc = @associations).include?(name) and reciprocal
362
+ old_val = assoc[name]
363
+ end
364
+ assoc[name] = o
319
365
  send(:"#{key}=", (o.pk if o))
320
- if reciprocal && (old_val != o)
321
- if old_val && (list = old_val.instance_variable_get(reciprocal))
322
- list.delete(self)
366
+ if reciprocal and old_val != o
367
+ if old_val and array = old_val.associations[reciprocal]
368
+ array.delete_if{|x| self === x}
323
369
  end
324
- if o && (list = o.instance_variable_get(reciprocal)) && !(list.include?(self))
325
- list.push(self)
370
+ if o and array = o.associations[reciprocal] and !array.include?(self)
371
+ array.push(self)
326
372
  end
327
373
  end
328
374
  o
@@ -331,44 +377,91 @@ module Sequel::Model::Associations
331
377
 
332
378
  # Adds one_to_many association instance methods
333
379
  def def_one_to_many(name, opts)
334
- ivar = association_ivar(name)
335
380
  key = (opts[:key] ||= default_remote_key)
336
381
  opts[:class_name] ||= name.to_s.singularize.camelize
337
382
 
338
- def_association_dataset_methods(name, opts) {opts.associated_class.select(*opts.select).filter(key => pk)}
383
+ def_association_dataset_methods(name, opts) {opts.associated_class.filter(key => pk)}
339
384
 
340
- class_def(association_add_method_name(name)) do |o|
341
- o.send(:"#{key}=", pk)
342
- o.save!
343
- if arr = instance_variable_get(ivar)
344
- arr.push(o)
345
- end
346
- if reciprocal = opts.reciprocal
347
- o.instance_variable_set(reciprocal, self)
385
+ unless opts[:read_only]
386
+ add_meth = association_add_method_name(name)
387
+ internal_add_meth = :"_#{add_meth}"
388
+ class_def(internal_add_meth) do |o|
389
+ o.send(:"#{key}=", pk)
390
+ o.save || raise(Sequel::Error, "invalid associated object, cannot save")
348
391
  end
349
- o
350
- end
351
- class_def(association_remove_method_name(name)) do |o|
352
- o.send(:"#{key}=", nil)
353
- o.save!
354
- if arr = instance_variable_get(ivar)
355
- arr.delete(o)
392
+ private internal_add_meth
393
+
394
+ class_def(add_meth) do |o|
395
+ raise(Sequel::Error, 'model object does not have a primary key') unless pk
396
+ send(internal_add_meth, o)
397
+ if (assoc = @associations).include?(name)
398
+ assoc[name].push(o)
399
+ end
400
+ if reciprocal = opts.reciprocal
401
+ o.associations[reciprocal] = self
402
+ end
403
+ o
356
404
  end
357
- if reciprocal = opts.reciprocal
358
- o.instance_variable_set(reciprocal, :null)
405
+ unless opts[:one_to_one]
406
+ remove_meth = association_remove_method_name(name)
407
+ internal_remove_meth = :"_#{remove_meth}"
408
+ remove_all_meth = association_remove_all_method_name(name)
409
+ internal_remove_all_meth = :"_#{remove_all_meth}"
410
+
411
+ class_def(internal_remove_meth) do |o|
412
+ o.send(:"#{key}=", nil)
413
+ o.save || raise(Sequel::Error, "invalid associated object, cannot save")
414
+ end
415
+ class_def(internal_remove_all_meth) do
416
+ opts.associated_class.filter(key=>pk).update(key=>nil)
417
+ end
418
+ private internal_remove_meth, internal_remove_all_meth
419
+
420
+ class_def(remove_meth) do |o|
421
+ raise(Sequel::Error, 'model object does not have a primary key') unless pk
422
+ send(internal_remove_meth, o)
423
+ if (assoc = @associations).include?(name)
424
+ assoc[name].delete_if{|x| o === x}
425
+ end
426
+ if reciprocal = opts.reciprocal
427
+ o.associations[reciprocal] = nil
428
+ end
429
+ o
430
+ end
431
+ class_def(remove_all_meth) do
432
+ raise(Sequel::Error, 'model object does not have a primary key') unless pk
433
+ send(internal_remove_all_meth)
434
+ if (assoc = @associations).include?(name)
435
+ arr = assoc[name]
436
+ ret = arr.dup
437
+ if reciprocal = opts.reciprocal
438
+ arr.each{|o| o.associations[reciprocal] = nil}
439
+ end
440
+ end
441
+ assoc[name] = []
442
+ ret
443
+ end
359
444
  end
360
- o
361
445
  end
362
- class_def(association_remove_all_method_name(name)) do
363
- opts.associated_class.filter(key=>pk).update(key=>nil)
364
- if arr = instance_variable_get(ivar)
365
- ret = arr.dup
366
- if reciprocal = opts.reciprocal
367
- arr.each{|o| o.instance_variable_set(reciprocal, :null)}
446
+ if opts[:one_to_one]
447
+ private name, :"#{name}_dataset"
448
+ n = name.to_s.singularize.to_sym
449
+ raise(Sequel::Error, "one_to_many association names should still be plural even when using the :one_to_one option") if n == name
450
+ class_def(n) do |*o|
451
+ objs = send(name, *o)
452
+ raise(Sequel::Error, "multiple values found for a one-to-one relationship") if objs.length > 1
453
+ objs.first
454
+ end
455
+ unless opts[:read_only]
456
+ private add_meth
457
+ class_def(:"#{n}=") do |o|
458
+ klass = opts.associated_class
459
+ model.db.transaction do
460
+ send(add_meth, o)
461
+ klass.filter(Sequel::SQL::BooleanExpression.new(:AND, {key=>pk}, ~{klass.primary_key=>o.pk}.sql_expr)).update(key=>nil)
462
+ end
368
463
  end
369
464
  end
370
- instance_variable_set(ivar, [])
371
- ret
372
465
  end
373
466
  end
374
467