sequel 3.13.0 → 3.14.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. data/CHANGELOG +36 -0
  2. data/doc/release_notes/3.14.0.txt +118 -0
  3. data/lib/sequel/adapters/oracle.rb +7 -2
  4. data/lib/sequel/adapters/shared/mssql.rb +9 -3
  5. data/lib/sequel/connection_pool/sharded_threaded.rb +1 -1
  6. data/lib/sequel/connection_pool/threaded.rb +3 -3
  7. data/lib/sequel/database/connecting.rb +47 -11
  8. data/lib/sequel/database/dataset.rb +17 -6
  9. data/lib/sequel/database/dataset_defaults.rb +15 -3
  10. data/lib/sequel/database/logging.rb +4 -3
  11. data/lib/sequel/database/misc.rb +33 -21
  12. data/lib/sequel/database/query.rb +61 -22
  13. data/lib/sequel/database/schema_generator.rb +108 -45
  14. data/lib/sequel/database/schema_methods.rb +8 -5
  15. data/lib/sequel/dataset/actions.rb +194 -45
  16. data/lib/sequel/dataset/features.rb +1 -1
  17. data/lib/sequel/dataset/graph.rb +51 -43
  18. data/lib/sequel/dataset/misc.rb +29 -5
  19. data/lib/sequel/dataset/mutation.rb +0 -1
  20. data/lib/sequel/dataset/prepared_statements.rb +14 -2
  21. data/lib/sequel/dataset/query.rb +268 -125
  22. data/lib/sequel/dataset/sql.rb +33 -44
  23. data/lib/sequel/extensions/migration.rb +3 -2
  24. data/lib/sequel/extensions/pagination.rb +1 -1
  25. data/lib/sequel/model/associations.rb +89 -87
  26. data/lib/sequel/model/base.rb +386 -109
  27. data/lib/sequel/model/errors.rb +15 -1
  28. data/lib/sequel/model/exceptions.rb +3 -3
  29. data/lib/sequel/model/inflections.rb +2 -2
  30. data/lib/sequel/model/plugins.rb +9 -5
  31. data/lib/sequel/plugins/rcte_tree.rb +43 -15
  32. data/lib/sequel/plugins/schema.rb +6 -5
  33. data/lib/sequel/plugins/serialization.rb +1 -1
  34. data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
  35. data/lib/sequel/plugins/tree.rb +33 -1
  36. data/lib/sequel/timezones.rb +16 -10
  37. data/lib/sequel/version.rb +1 -1
  38. data/spec/adapters/mssql_spec.rb +36 -2
  39. data/spec/adapters/mysql_spec.rb +4 -4
  40. data/spec/adapters/postgres_spec.rb +1 -1
  41. data/spec/adapters/spec_helper.rb +2 -2
  42. data/spec/core/database_spec.rb +8 -1
  43. data/spec/core/dataset_spec.rb +36 -1
  44. data/spec/extensions/pagination_spec.rb +1 -1
  45. data/spec/extensions/rcte_tree_spec.rb +40 -8
  46. data/spec/extensions/schema_spec.rb +5 -0
  47. data/spec/extensions/serialization_spec.rb +4 -4
  48. data/spec/extensions/single_table_inheritance_spec.rb +7 -0
  49. data/spec/extensions/tree_spec.rb +36 -0
  50. data/spec/integration/dataset_test.rb +19 -0
  51. data/spec/integration/prepared_statement_test.rb +2 -2
  52. data/spec/integration/schema_test.rb +1 -1
  53. data/spec/integration/spec_helper.rb +4 -4
  54. data/spec/integration/timezone_test.rb +27 -21
  55. data/spec/model/associations_spec.rb +5 -5
  56. data/spec/model/dataset_methods_spec.rb +13 -0
  57. data/spec/model/hooks_spec.rb +31 -0
  58. data/spec/model/record_spec.rb +24 -7
  59. data/spec/model/validations_spec.rb +9 -4
  60. metadata +6 -4
@@ -5,40 +5,28 @@ module Sequel
5
5
  # These are methods you can call to see what SQL will be generated by the dataset.
6
6
  # ---------------------
7
7
 
8
- # Formats a DELETE statement using the given options and dataset options.
8
+ # Returns a DELETE SQL query string. See +delete+.
9
9
  #
10
- # dataset.filter{|o| o.price >= 100}.delete_sql #=>
11
- # "DELETE FROM items WHERE (price >= 100)"
10
+ # dataset.filter{|o| o.price >= 100}.delete_sql
11
+ # # => "DELETE FROM items WHERE (price >= 100)"
12
12
  def delete_sql
13
13
  return static_sql(opts[:sql]) if opts[:sql]
14
14
  check_modification_allowed!
15
15
  clause_sql(:delete)
16
16
  end
17
17
 
18
- # Returns an EXISTS clause for the dataset as a LiteralString.
18
+ # Returns an EXISTS clause for the dataset as a +LiteralString+.
19
19
  #
20
- # DB.select(1).where(DB[:items].exists).sql
21
- # #=> "SELECT 1 WHERE (EXISTS (SELECT * FROM items))"
20
+ # DB.select(1).where(DB[:items].exists)
21
+ # # SELECT 1 WHERE (EXISTS (SELECT * FROM items))
22
22
  def exists
23
23
  LiteralString.new("EXISTS (#{select_sql})")
24
24
  end
25
25
 
26
- # Formats an INSERT statement using the given values. The API is a little
27
- # complex, and best explained by example:
26
+ # Returns an INSERT SQL query string. See +insert+.
28
27
  #
29
- # # Default values
30
- # DB[:items].insert_sql #=> 'INSERT INTO items DEFAULT VALUES'
31
- # DB[:items].insert_sql({}) #=> 'INSERT INTO items DEFAULT VALUES'
32
- # # Values without columns
33
- # DB[:items].insert_sql(1,2,3) #=> 'INSERT INTO items VALUES (1, 2, 3)'
34
- # DB[:items].insert_sql([1,2,3]) #=> 'INSERT INTO items VALUES (1, 2, 3)'
35
- # # Values with columns
36
- # DB[:items].insert_sql([:a, :b], [1,2]) #=> 'INSERT INTO items (a, b) VALUES (1, 2)'
37
- # DB[:items].insert_sql(:a => 1, :b => 2) #=> 'INSERT INTO items (a, b) VALUES (1, 2)'
38
- # # Using a subselect
39
- # DB[:items].insert_sql(DB[:old_items]) #=> 'INSERT INTO items SELECT * FROM old_items
40
- # # Using a subselect with columns
41
- # DB[:items].insert_sql([:a, :b], DB[:old_items]) #=> 'INSERT INTO items (a, b) SELECT * FROM old_items
28
+ # DB[:items].insert_sql(:a=>1)
29
+ # # => "INSERT INTO items (a) VALUES (1)"
42
30
  def insert_sql(*values)
43
31
  return static_sql(@opts[:sql]) if @opts[:sql]
44
32
 
@@ -80,13 +68,13 @@ module Sequel
80
68
  # Returns a literal representation of a value to be used as part
81
69
  # of an SQL expression.
82
70
  #
83
- # dataset.literal("abc'def\\") #=> "'abc''def\\\\'"
84
- # dataset.literal(:items__id) #=> "items.id"
85
- # dataset.literal([1, 2, 3]) => "(1, 2, 3)"
86
- # dataset.literal(DB[:items]) => "(SELECT * FROM items)"
87
- # dataset.literal(:x + 1 > :y) => "((x + 1) > y)"
71
+ # DB[:items].literal("abc'def\\") #=> "'abc''def\\\\'"
72
+ # DB[:items].literal(:items__id) #=> "items.id"
73
+ # DB[:items].literal([1, 2, 3]) => "(1, 2, 3)"
74
+ # DB[:items].literal(DB[:items]) => "(SELECT * FROM items)"
75
+ # DB[:items].literal(:x + 1 > :y) => "((x + 1) > y)"
88
76
  #
89
- # If an unsupported object is given, an exception is raised.
77
+ # If an unsupported object is given, an +Error+ is raised.
90
78
  def literal(v)
91
79
  case v
92
80
  when String
@@ -126,7 +114,7 @@ module Sequel
126
114
  end
127
115
 
128
116
  # Returns an array of insert statements for inserting multiple records.
129
- # This method is used by #multi_insert to format insert statements and
117
+ # This method is used by +multi_insert+ to format insert statements and
130
118
  # expects a keys array and and an array of value arrays.
131
119
  #
132
120
  # This method should be overridden by descendants if the support
@@ -135,7 +123,7 @@ module Sequel
135
123
  values.map{|r| insert_sql(columns, r)}
136
124
  end
137
125
 
138
- # Formats a SELECT statement
126
+ # Returns a SELECT SQL query string.
139
127
  #
140
128
  # dataset.select_sql # => "SELECT * FROM items"
141
129
  def select_sql
@@ -143,12 +131,14 @@ module Sequel
143
131
  clause_sql(:select)
144
132
  end
145
133
 
146
- # Same as select_sql, not aliased directly to make subclassing simpler.
134
+ # Same as +select_sql+, not aliased directly to make subclassing simpler.
147
135
  def sql
148
136
  select_sql
149
137
  end
150
138
 
151
- # SQL query to truncate the table
139
+ # Returns a TRUNCATE SQL query string. See +truncate+
140
+ #
141
+ # DB[:items].truncate_sql # => 'TRUNCATE items'
152
142
  def truncate_sql
153
143
  if opts[:sql]
154
144
  static_sql(opts[:sql])
@@ -159,12 +149,12 @@ module Sequel
159
149
  end
160
150
  end
161
151
 
162
- # Formats an UPDATE statement using the given values.
152
+ # Formats an UPDATE statement using the given values. See +update+.
163
153
  #
164
- # dataset.update_sql(:price => 100, :category => 'software') #=>
165
- # "UPDATE items SET price = 100, category = 'software'"
154
+ # DB[:items].update_sql(:price => 100, :category => 'software')
155
+ # # => "UPDATE items SET price = 100, category = 'software'
166
156
  #
167
- # Raises an error if the dataset is grouped or includes more
157
+ # Raises an +Error+ if the dataset is grouped or includes more
168
158
  # than one table.
169
159
  def update_sql(values = {})
170
160
  return static_sql(opts[:sql]) if opts[:sql]
@@ -210,12 +200,12 @@ module Sequel
210
200
  WILDCARD = LiteralString.new('*').freeze
211
201
  SQL_WITH = "WITH ".freeze
212
202
 
213
- # SQL fragment for the aliased expression
203
+ # SQL fragment for AliasedExpression
214
204
  def aliased_expression_sql(ae)
215
205
  as_sql(literal(ae.expression), ae.aliaz)
216
206
  end
217
207
 
218
- # SQL fragment for the SQL array.
208
+ # SQL fragment for Array
219
209
  def array_sql(a)
220
210
  a.empty? ? '(NULL)' : "(#{expression_list(a)})"
221
211
  end
@@ -225,7 +215,7 @@ module Sequel
225
215
  literal(constant)
226
216
  end
227
217
 
228
- # SQL fragment for specifying given CaseExpression.
218
+ # SQL fragment for CaseExpression
229
219
  def case_expression_sql(ce)
230
220
  sql = '(CASE '
231
221
  sql << "#{literal(ce.expression)} " if ce.expression?
@@ -235,12 +225,12 @@ module Sequel
235
225
  sql << "ELSE #{literal(ce.default)} END)"
236
226
  end
237
227
 
238
- # SQL fragment for the SQL CAST expression.
228
+ # SQL fragment for the SQL CAST expression
239
229
  def cast_sql(expr, type)
240
230
  "CAST(#{literal(expr)} AS #{db.cast_type_literal(type)})"
241
231
  end
242
232
 
243
- # SQL fragment for specifying all columns in a given table.
233
+ # SQL fragment for specifying all columns in a given table
244
234
  def column_all_sql(ca)
245
235
  "#{quote_schema_table(ca.table)}.*"
246
236
  end
@@ -427,7 +417,7 @@ module Sequel
427
417
  end
428
418
  end
429
419
 
430
- # SQL fragment for specifying subscripts (SQL arrays)
420
+ # SQL fragment for specifying subscripts (SQL array accesses)
431
421
  def subscript_sql(s)
432
422
  "#{literal(s.f)}[#{expression_list(s.sub)}]"
433
423
  end
@@ -610,7 +600,6 @@ module Sequel
610
600
  sprintf(".%06d", usec)
611
601
  end
612
602
 
613
- # SQL fragment specifying a list of identifiers
614
603
  # SQL fragment specifying a list of identifiers
615
604
  def identifier_list(columns)
616
605
  columns.map{|i| quote_identifier(i)}.join(COMMA_SEPARATOR)
@@ -725,8 +714,8 @@ module Sequel
725
714
  end
726
715
 
727
716
  # SQL fragment for a type of object not handled by Dataset#literal.
728
- # Calls sql_literal if object responds to it, otherwise raises an error.
729
- # Classes implementing sql_literal should call a class-specific method on the dataset
717
+ # Calls +sql_literal+ if object responds to it, otherwise raises an error.
718
+ # Classes implementing +sql_literal+ should call a class-specific method on the dataset
730
719
  # provided and should add that method to Sequel::Dataset, allowing for adapters
731
720
  # to provide customized literalizations.
732
721
  # If a database specific type is allowed, this should be overriden in a subclass.
@@ -305,11 +305,12 @@ module Sequel
305
305
  super
306
306
  @target = opts[:target] || latest_migration_version
307
307
  @current = opts[:current] || current_migration_version
308
- @direction = current < target ? :up : :down
309
- @migrations = get_migrations
310
308
 
311
309
  raise(Error, "No current version available") unless current
312
310
  raise(Error, "No target version available") unless target
311
+
312
+ @direction = current < target ? :up : :down
313
+ @migrations = get_migrations
313
314
  end
314
315
 
315
316
  # Apply all migrations on the database
@@ -17,7 +17,7 @@ module Sequel
17
17
 
18
18
  # Yields a paginated dataset for each page and returns the receiver. Does
19
19
  # a count to find the total number of records for this dataset.
20
- def each_page(page_size, &block)
20
+ def each_page(page_size)
21
21
  raise(Error, "You cannot paginate a dataset that already has a limit") if @opts[:limit]
22
22
  record_count = count
23
23
  total_pages = (record_count / page_size.to_f).ceil
@@ -442,13 +442,13 @@ module Sequel
442
442
  # Project.associations
443
443
  # => [:portfolio, :milestones]
444
444
  # Project.association_reflection(:portfolio)
445
- # => {:type => :many_to_one, :name => :portfolio, :class_name => "Portfolio"}
445
+ # => {:type => :many_to_one, :name => :portfolio, ...}
446
446
  #
447
447
  # For a more in depth general overview, as well as a reference guide,
448
448
  # see the {Association Basics guide}[link:files/doc/association_basics_rdoc.html].
449
449
  # For examples of advanced usage, see the {Advanced Associations guide}[link:files/doc/advanced_associations_rdoc.html].
450
450
  module ClassMethods
451
- # All association reflections defined for this model (default: none).
451
+ # All association reflections defined for this model (default: {}).
452
452
  attr_reader :association_reflections
453
453
 
454
454
  # Array of all association reflections for this model class
@@ -497,11 +497,11 @@ module Sequel
497
497
  # - :before_set - Symbol, Proc, or array of both/either specifying a callback to call
498
498
  # before an item is set using the association setter method.
499
499
  # - :cartesian_product_number - the number of joins completed by this association that could cause more
500
- # than one row for each row in the current table (default: 0 for many_to_one associations,
501
- # 1 for *_to_many associations).
500
+ # than one row for each row in the current table (default: 0 for many_to_one and one_to_one associations,
501
+ # 1 for one_to_many and many_to_many associations).
502
502
  # - :class - The associated class or its name. If not
503
503
  # given, uses the association's name, which is camelized (and
504
- # singularized unless the type is :many_to_one)
504
+ # singularized unless the type is :many_to_one or :one_to_one)
505
505
  # - :clone - Merge the current options and block into the options and block used in defining
506
506
  # the given association. Can be used to DRY up a bunch of similar associations that
507
507
  # all share the same options such as :class and :key, while changing the order and block used.
@@ -510,18 +510,12 @@ module Sequel
510
510
  # to use for the _dataset method (before the other options are applied).
511
511
  # - :distinct - Use the DISTINCT clause when selecting associating object, both when
512
512
  # lazy loading and eager loading via .eager (but not when using .eager_graph).
513
- # - :eager - The associations to eagerly load via #eager when loading the associated object(s).
514
- # For many_to_one associations, this is ignored unless this association is
515
- # being eagerly loaded, as it doesn't save queries unless multiple objects
516
- # can be loaded at once.
513
+ # - :eager - The associations to eagerly load via +eager+ when loading the associated object(s).
517
514
  # - :eager_block - If given, use the block instead of the default block when
518
515
  # eagerly loading. To not use a block when eager loading (when one is used normally),
519
516
  # set to nil.
520
- # - :eager_graph - The associations to eagerly load via #eager_graph when loading the associated object(s).
521
- # For many_to_one associations, this is ignored unless this association is
522
- # being eagerly loaded, as it doesn't save queries unless multiple objects
523
- # can be loaded at once.
524
- # - :eager_grapher - A proc to use to implement eager loading via eager graph, overriding the default.
517
+ # - :eager_graph - The associations to eagerly load via +eager_graph+ when loading the associated object(s).
518
+ # - :eager_grapher - A proc to use to implement eager loading via +eager_graph+, overriding the default.
525
519
  # Takes three arguments, a dataset, an alias to use for the table to graph for this association,
526
520
  # and the alias that was used for the current table (since you can cascade associations),
527
521
  # Should return a copy of the dataset with the association graphed into it.
@@ -531,38 +525,38 @@ module Sequel
531
525
  # :rows, and :associations, corresponding to the three arguments, and an additional key :self, which is
532
526
  # the dataset doing the eager loading. In the proc, the associated records should
533
527
  # be queried from the database and the associations cache for each
534
- # record should be populated for this to work correctly.
528
+ # record should be populated.
535
529
  # - :eager_loader_key - A symbol for the key column to use to populate the key hash
536
530
  # for the eager loader.
537
531
  # - :extend - A module or array of modules to extend the dataset with.
538
532
  # - :graph_block - The block to pass to join_table when eagerly loading
539
- # the association via eager_graph.
533
+ # the association via +eager_graph+.
540
534
  # - :graph_conditions - The additional conditions to use on the SQL join when eagerly loading
541
- # the association via eager_graph. Should be a hash or an array of all two pairs. If not
542
- # specified, the :conditions option is used if it is a hash or array of all two pairs.
535
+ # the association via +eager_graph+. Should be a hash or an array of two element arrays. If not
536
+ # specified, the :conditions option is used if it is a hash or array of two element arrays.
543
537
  # - :graph_join_type - The type of SQL join to use when eagerly loading the association via
544
538
  # eager_graph. Defaults to :left_outer.
545
539
  # - :graph_only_conditions - The conditions to use on the SQL join when eagerly loading
546
- # the association via eager_graph, instead of the default conditions specified by the
540
+ # the association via +eager_graph+, instead of the default conditions specified by the
547
541
  # foreign/primary keys. This option causes the :graph_conditions option to be ignored.
548
542
  # - :graph_select - A column or array of columns to select from the associated table
549
- # when eagerly loading the association via eager_graph. Defaults to all
543
+ # when eagerly loading the association via +eager_graph+. Defaults to all
550
544
  # columns in the associated table.
551
545
  # - :limit - Limit the number of records to the provided value. Use
552
- # an array with two arguments for the value to specify a limit and an offset.
546
+ # an array with two elements for the value to specify a limit (first element) and an offset (second element).
553
547
  # - :methods_module - The module that methods the association creates will be placed into. Defaults
554
548
  # to the module containing the model's columns.
555
549
  # - :order - the column(s) by which to order the association dataset. Can be a
556
- # singular column or an array.
557
- # - :order_eager_graph - Whether to add the order to the dataset's order when graphing
558
- # via eager graph. Defaults to true, so set to false to disable.
559
- # - :read_only - Do not add a setter method (for many_to_one or one_to_many with :one_to_one),
560
- # or add_/remove_/remove_all_ methods (for one_to_many, many_to_many)
550
+ # singular column symbol or an array of column symbols.
551
+ # - :order_eager_graph - Whether to add the association's order to the graphed dataset's order when graphing
552
+ # via +eager_graph+. Defaults to true, so set to false to disable.
553
+ # - :read_only - Do not add a setter method (for many_to_one or one_to_one associations),
554
+ # or add_/remove_/remove_all_ methods (for one_to_many and many_to_many associations).
561
555
  # - :reciprocal - the symbol name of the reciprocal association,
562
- # if it exists. By default, sequel will try to determine it by looking at the
556
+ # if it exists. By default, Sequel will try to determine it by looking at the
563
557
  # associated model's assocations for a association that matches
564
558
  # the current association's key(s). Set to nil to not use a reciprocal.
565
- # - :select - the attributes to select. Defaults to the associated class's
559
+ # - :select - the columns to select. Defaults to the associated class's
566
560
  # table_name.* in a many_to_many association, which means it doesn't include the attributes from the
567
561
  # join table. If you want to include the join table attributes, you can
568
562
  # use this option, but beware that the join table attributes can clash with
@@ -585,16 +579,16 @@ module Sequel
585
579
  # Defaults to primary key of the current table. Can use an
586
580
  # array of symbols for a composite key association.
587
581
  # * :many_to_many:
588
- # - :graph_join_table_block - The block to pass to join_table for
589
- # the join table when eagerly loading the association via eager_graph.
582
+ # - :graph_join_table_block - The block to pass to +join_table+ for
583
+ # the join table when eagerly loading the association via +eager_graph+.
590
584
  # - :graph_join_table_conditions - The additional conditions to use on the SQL join for
591
- # the join table when eagerly loading the association via eager_graph. Should be a hash
592
- # or an array of all two pairs.
585
+ # the join table when eagerly loading the association via +eager_graph+. Should be a hash
586
+ # or an array of two element arrays.
593
587
  # - :graph_join_table_join_type - The type of SQL join to use for the join table when eagerly
594
- # loading the association via eager_graph. Defaults to the :graph_join_type option or
588
+ # loading the association via +eager_graph+. Defaults to the :graph_join_type option or
595
589
  # :left_outer.
596
590
  # - :graph_join_table_only_conditions - The conditions to use on the SQL join for the join
597
- # table when eagerly loading the association via eager_graph, instead of the default
591
+ # table when eagerly loading the association via +eager_graph+, instead of the default
598
592
  # conditions specified by the foreign/primary keys. This option causes the
599
593
  # :graph_join_table_conditions option to be ignored.
600
594
  # - :join_table - name of table that includes the foreign keys to both
@@ -622,7 +616,7 @@ module Sequel
622
616
  raise(Error, 'Model.associate name argument must be a symbol') unless Symbol === name
623
617
  raise(Error, ':eager_loader option must have an arity of 1 or 3') if opts[:eager_loader] && ![1, 3].include?(opts[:eager_loader].arity)
624
618
 
625
- # merge early so we don't modify opts
619
+ # dup early so we don't modify opts
626
620
  orig_opts = opts.dup
627
621
  orig_opts = association_reflection(opts[:clone])[:orig_opts].merge(orig_opts) if opts[:clone]
628
622
  opts = orig_opts.merge(:type => type, :name => name, :cache => true, :model => self)
@@ -659,7 +653,7 @@ module Sequel
659
653
  association_reflections.keys
660
654
  end
661
655
 
662
- # Modify and return eager loading dataset based on association options. Options:
656
+ # Modify and return eager loading dataset based on association options.
663
657
  def eager_loading_dataset(opts, ds, select, associations, eager_options={})
664
658
  ds = ds.select(*select) if select
665
659
  if c = opts[:conditions]
@@ -736,7 +730,7 @@ module Sequel
736
730
  association_module_def(opts.add_method, opts){|o,*args| add_associated_object(opts, o, *args)}
737
731
  end
738
732
 
739
- # Adds methods related to the association's dataset to the module included in the class.
733
+ # Adds the association dataset methods to the association methods module.
740
734
  def def_association_dataset_methods(opts)
741
735
  # If a block is given, define a helper method for it, because it takes
742
736
  # an argument. This is unnecessary in Ruby 1.9, as that has instance_exec.
@@ -746,12 +740,12 @@ module Sequel
746
740
  def_association_method(opts)
747
741
  end
748
742
 
749
- # Adds method for retrieving the associated objects to the module included in the class.
743
+ # Adds the association method to the association methods module.
750
744
  def def_association_method(opts)
751
745
  association_module_def(opts.association_method, opts){|*reload| load_associated_objects(opts, reload[0])}
752
746
  end
753
747
 
754
- # Adds many_to_many association instance methods
748
+ # Configures many_to_many association reflection and adds the related association methods
755
749
  def def_many_to_many(opts)
756
750
  name = opts[:name]
757
751
  model = self
@@ -761,8 +755,11 @@ module Sequel
761
755
  rcks = opts[:right_keys] = Array(right)
762
756
  left_pk = (opts[:left_primary_key] ||= self.primary_key)
763
757
  lcpks = opts[:left_primary_keys] = Array(left_pk)
764
- raise(Error, 'mismatched number of left composite keys') unless lcks.length == lcpks.length
765
- raise(Error, 'mismatched number of right composite keys') if opts[:right_primary_key] && rcks.length != Array(opts[:right_primary_key]).length
758
+ raise(Error, "mismatched number of left composite keys: #{lcks.inspect} vs #{lcpks.inspect}") unless lcks.length == lcpks.length
759
+ if opts[:right_primary_key]
760
+ rcpks = Array(opts[:right_primary_key])
761
+ raise(Error, "mismatched number of right composite keys: #{rcks.inspect} vs #{rcpks.inspect}") unless rcks.length == rcpks.length
762
+ end
766
763
  uses_lcks = opts[:uses_left_composite_keys] = lcks.length > 1
767
764
  uses_rcks = opts[:uses_right_composite_keys] = rcks.length > 1
768
765
  opts[:cartesian_product_number] ||= 1
@@ -826,14 +823,17 @@ module Sequel
826
823
  def_remove_methods(opts)
827
824
  end
828
825
 
829
- # Adds many_to_one association instance methods
826
+ # Configures many_to_one association reflection and adds the related association methods
830
827
  def def_many_to_one(opts)
831
828
  name = opts[:name]
832
829
  model = self
833
830
  opts[:key] = opts.default_key unless opts.include?(:key)
834
831
  key = opts[:key]
835
832
  cks = opts[:keys] = Array(opts[:key])
836
- raise(Error, 'mismatched number of composite keys') if opts[:primary_key] && cks.length != Array(opts[:primary_key]).length
833
+ if opts[:primary_key]
834
+ cpks = Array(opts[:primary_key])
835
+ raise(Error, "mismatched number of composite keys: #{cks.inspect} vs #{cpks.inspect}") unless cks.length == cpks.length
836
+ end
837
837
  uses_cks = opts[:uses_composite_keys] = cks.length > 1
838
838
  opts[:cartesian_product_number] ||= 0
839
839
  opts[:dataset] ||= proc do
@@ -875,7 +875,7 @@ module Sequel
875
875
  association_module_def(opts.setter_method, opts){|o| set_associated_object(opts, o)}
876
876
  end
877
877
 
878
- # Adds one_to_many association instance methods
878
+ # Configures one_to_many and one_to_one association reflections and adds the related association methods
879
879
  def def_one_to_many(opts)
880
880
  one_to_one = opts[:type] == :one_to_one
881
881
  name = opts[:name]
@@ -884,7 +884,7 @@ module Sequel
884
884
  cks = opts[:keys] = Array(key)
885
885
  primary_key = (opts[:primary_key] ||= self.primary_key)
886
886
  cpks = opts[:primary_keys] = Array(primary_key)
887
- raise(Error, 'mismatched number of composite keys') unless cks.length == cpks.length
887
+ raise(Error, "mismatched number of composite keys: #{cks.inspect} vs #{cpks.inspect}") unless cks.length == cpks.length
888
888
  uses_cks = opts[:uses_composite_keys] = cks.length > 1
889
889
  opts[:dataset] ||= proc do
890
890
  klass = opts.associated_class
@@ -970,7 +970,7 @@ module Sequel
970
970
  end
971
971
  end
972
972
 
973
- # Alias of def_one_to_many
973
+ # Alias of def_one_to_many, since they share pretty much the same code.
974
974
  def def_one_to_one(opts)
975
975
  def_one_to_many(opts)
976
976
  end
@@ -982,7 +982,7 @@ module Sequel
982
982
  end
983
983
  end
984
984
 
985
- # Private instance methods used to implement the associations support.
985
+ # Instance methods used to implement the associations support.
986
986
  module InstanceMethods
987
987
  # The currently cached associations. A hash with the keys being the
988
988
  # association name symbols and the values being the associated object
@@ -1000,6 +1000,7 @@ module Sequel
1000
1000
 
1001
1001
  private
1002
1002
 
1003
+ # Apply the association options such as :order and :limit to the given dataset, returning a modified dataset.
1003
1004
  def _apply_association_options(opts, ds)
1004
1005
  ds.extend(AssociationDatasetMethods)
1005
1006
  ds.model_object = self
@@ -1019,7 +1020,7 @@ module Sequel
1019
1020
  ds
1020
1021
  end
1021
1022
 
1022
- # Backbone behind association dataset methods
1023
+ # Return an association dataset for the given association reflection
1023
1024
  def _dataset(opts)
1024
1025
  raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk
1025
1026
  _apply_association_options(opts, send(opts._dataset_method))
@@ -1144,8 +1145,8 @@ module Sequel
1144
1145
  o
1145
1146
  end
1146
1147
 
1147
- # If necessary, check that the object from the associated table specified by the primary key
1148
- # is currently associated to the object. If it is associated, return the object, otherwise
1148
+ # Check that the object from the associated table specified by the primary key
1149
+ # is currently associated to the receiver. If it is associated, return the object, otherwise
1149
1150
  # raise an error.
1150
1151
  def remove_check_existing_object_from_pk(opts, o, *args)
1151
1152
  key = o
@@ -1186,7 +1187,7 @@ module Sequel
1186
1187
  end
1187
1188
  end
1188
1189
 
1189
- # Set the given object as the associated object for the given association
1190
+ # Set the given object as the associated object for the given many_to_one association reflection
1190
1191
  def set_associated_object(opts, o)
1191
1192
  raise(Error, "associated object #{o.inspect} does not have a primary key") if o && !o.pk
1192
1193
  run_association_callbacks(opts, :before_set, o)
@@ -1200,7 +1201,7 @@ module Sequel
1200
1201
  o
1201
1202
  end
1202
1203
 
1203
- # Set the given object as the associated object for the given association
1204
+ # Set the given object as the associated object for the given one_to_one association reflection
1204
1205
  def set_one_to_one_associated_object(opts, o)
1205
1206
  raise(Error, "object #{inspect} does not have a primary key") unless pk
1206
1207
  run_association_callbacks(opts, :before_set, o)
@@ -1218,15 +1219,15 @@ module Sequel
1218
1219
  # Eager loading makes it so that you can load all associated records for a
1219
1220
  # set of objects in a single query, instead of a separate query for each object.
1220
1221
  #
1221
- # Two separate implementations are provided. #eager should be used most of the
1222
+ # Two separate implementations are provided. +eager+ should be used most of the
1222
1223
  # time, as it loads associated records using one query per association. However,
1223
- # it does not allow you the ability to filter based on columns in associated tables. #eager_graph loads
1224
- # all records in one query. Using #eager_graph you can filter based on columns in associated
1225
- # tables. However, #eager_graph can be slower than #eager, especially if multiple
1226
- # *_to_many associations are joined.
1224
+ # it does not allow you the ability to filter or order based on columns in associated tables. +eager_graph+ loads
1225
+ # all records in a single query using JOINs, allowing you to filter or order based on columns in associated
1226
+ # tables. However, +eager_graph+ can be slower than +eager+, especially if multiple
1227
+ # one_to_many or many_to_many associations are joined.
1227
1228
  #
1228
- # You can cascade the eager loading (loading associations' associations)
1229
- # with no limit to the depth of the cascades. You do this by passing a hash to #eager or #eager_graph
1229
+ # You can cascade the eager loading (loading associations on associated objects)
1230
+ # with no limit to the depth of the cascades. You do this by passing a hash to +eager+ or +eager_graph+
1230
1231
  # with the keys being associations of the current model and values being
1231
1232
  # associations of the model associated with the current model via the key.
1232
1233
  #
@@ -1244,7 +1245,7 @@ module Sequel
1244
1245
  # Artist.eager(:albums=>{:tracks=>:genre}).all
1245
1246
  # Artist.eager_graph(:albums=>{:tracks=>:genre}).all
1246
1247
  module DatasetMethods
1247
- # Add the #eager! and #eager_graph! mutation methods to the dataset.
1248
+ # Add the <tt>eager!</tt> and <tt>eager_graph!</tt> mutation methods to the dataset.
1248
1249
  def self.extended(obj)
1249
1250
  obj.def_mutation_method(:eager, :eager_graph)
1250
1251
  end
@@ -1253,23 +1254,23 @@ module Sequel
1253
1254
  # query for each association.
1254
1255
  #
1255
1256
  # The basic idea for how it works is that the dataset is first loaded normally.
1256
- # Then it goes through all associations that have been specified via eager.
1257
+ # Then it goes through all associations that have been specified via +eager+.
1257
1258
  # It loads each of those associations separately, then associates them back
1258
1259
  # to the original dataset via primary/foreign keys. Due to the necessity of
1259
- # all objects being present, you need to use .all to use eager loading, as it
1260
- # can't work with .each.
1260
+ # all objects being present, you need to use +all+ to use eager loading, as it
1261
+ # can't work with +each+.
1261
1262
  #
1262
1263
  # This implementation avoids the complexity of extracting an object graph out
1263
1264
  # of a single dataset, by building the object graph out of multiple datasets,
1264
1265
  # one for each association. By using a separate dataset for each association,
1265
1266
  # it avoids problems such as aliasing conflicts and creating cartesian product
1266
- # result sets if multiple *_to_many eager associations are requested.
1267
+ # result sets if multiple one_to_many or many_to_many eager associations are requested.
1267
1268
  #
1268
1269
  # One limitation of using this method is that you cannot filter the dataset
1269
1270
  # based on values of columns in an associated table, since the associations are loaded
1270
1271
  # in separate queries. To do that you need to load all associations in the
1271
1272
  # same query, and extract an object graph from the results of that query. If you
1272
- # need to filter based on columns in associated tables, look at #eager_graph
1273
+ # need to filter based on columns in associated tables, look at +eager_graph+
1273
1274
  # or join the tables you need to filter on manually.
1274
1275
  #
1275
1276
  # Each association's order, if defined, is respected. Eager also works
@@ -1293,25 +1294,26 @@ module Sequel
1293
1294
  end
1294
1295
 
1295
1296
  # The secondary eager loading method. Loads all associations in a single query. This
1296
- # method should only be used if you need to filter based on columns in associated tables.
1297
+ # method should only be used if you need to filter or order based on columns in associated tables.
1297
1298
  #
1298
- # This method builds an object graph using Dataset#graph. Then it uses the graph
1299
+ # This method builds an object graph using <tt>Dataset#graph</tt>. Then it uses the graph
1299
1300
  # to build the associations, and finally replaces the graph with a simple array
1300
1301
  # of model objects.
1301
1302
  #
1302
- # Be very careful when using this with multiple *_to_many associations, as you can
1303
- # create large cartesian products. If you must graph multiple *_to_many associations,
1304
- # make sure your filters are specific if you have a large database.
1303
+ # Be very careful when using this with multiple one_to_many or many_to_many associations, as you can
1304
+ # create large cartesian products. If you must graph multiple one_to_many and many_to_many associations,
1305
+ # make sure your filters are narrow if you have a large database.
1305
1306
  #
1306
- # Each association's order, if definied, is respected. #eager_graph probably
1307
+ # Each association's order, if definied, is respected. +eager_graph+ probably
1307
1308
  # won't work correctly on a limited dataset, unless you are
1308
- # only graphing many_to_one associations.
1309
+ # only graphing many_to_one and one_to_one associations.
1309
1310
  #
1310
1311
  # Does not use the block defined for the association, since it does a single query for
1311
1312
  # all objects. You can use the :graph_* association options to modify the SQL query.
1312
1313
  #
1313
- # Like eager, you need to call .all on the dataset for the eager loading to work. If you just
1314
- # call each, you will get a normal graphed result back (a hash with model object values).
1314
+ # Like +eager+, you need to call +all+ on the dataset for the eager loading to work. If you just
1315
+ # call +each+, you will get a normal graphed result back (a hash with table alias symbol keys and
1316
+ # model object values).
1315
1317
  def eager_graph(*associations)
1316
1318
  ds = if @opts[:eager_graph]
1317
1319
  self
@@ -1342,12 +1344,12 @@ module Sequel
1342
1344
  # (which would be dependencies of the current association)
1343
1345
  #
1344
1346
  # Arguments:
1345
- # * ds - Current dataset
1346
- # * model - Current Model
1347
- # * ta - table_alias used for the parent association
1348
- # * requirements - an array, used as a stack for requirements
1349
- # * r - association reflection for the current association
1350
- # * *associations - any associations dependent on this one
1347
+ # ds :: Current dataset
1348
+ # model :: Current Model
1349
+ # ta :: table_alias used for the parent association
1350
+ # requirements :: an array, used as a stack for requirements
1351
+ # r :: association reflection for the current association
1352
+ # *associations :: any associations dependent on this one
1351
1353
  def eager_graph_association(ds, model, ta, requirements, r, *associations)
1352
1354
  klass = r.associated_class
1353
1355
  assoc_name = r[:name]
@@ -1367,11 +1369,11 @@ module Sequel
1367
1369
  # Call eager_graph_association on each association.
1368
1370
  #
1369
1371
  # Arguments:
1370
- # * ds - Current dataset
1371
- # * model - Current Model
1372
- # * ta - table_alias used for the parent association
1373
- # * requirements - an array, used as a stack for requirements
1374
- # * *associations - the associations to add to the graph
1372
+ # ds :: Current dataset
1373
+ # model :: Current Model
1374
+ # ta :: table_alias used for the parent association
1375
+ # requirements :: an array, used as a stack for requirements
1376
+ # *associations :: the associations to add to the graph
1375
1377
  def eager_graph_associations(ds, model, ta, requirements, *associations)
1376
1378
  return ds if associations.empty?
1377
1379
  associations.flatten.each do |association|
@@ -1465,7 +1467,7 @@ module Sequel
1465
1467
  # to build all dependencies.
1466
1468
  def eager_graph_build_associations_graph(dependency_map, alias_map, type_map, reciprocal_map, records_map, current, record_graph)
1467
1469
  return if dependency_map.empty?
1468
- # Don't clobber the instance variable array for *_to_many associations if it has already been setup
1470
+ # Don't clobber the cached association value for one_to_many and many_to_many associations if it has already been setup
1469
1471
  dependency_map.keys.each do |ta|
1470
1472
  assoc_name = alias_map[ta]
1471
1473
  current.associations[assoc_name] = type_map[ta] ? [] : nil unless current.associations.include?(assoc_name)
@@ -1494,7 +1496,7 @@ module Sequel
1494
1496
 
1495
1497
  # If the result set is the result of a cartesian product, then it is possible that
1496
1498
  # there are multiple records for each association when there should only be one.
1497
- # In that case, for each object in all associations loaded via #eager_graph, run
1499
+ # In that case, for each object in all associations loaded via +eager_graph+, run
1498
1500
  # uniq! on the association to make sure no duplicate records show up.
1499
1501
  # Note that this can cause legitimate duplicate records to be removed.
1500
1502
  def eager_graph_make_associations_unique(records, dependency_map, alias_map, type_map)