sequel 2.0.1 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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