sequel 3.13.0 → 3.14.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.
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)