sequel 2.6.0 → 2.7.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 +64 -0
- data/Rakefile +1 -1
- data/lib/sequel_core/adapters/jdbc.rb +6 -2
- data/lib/sequel_core/adapters/jdbc/oracle.rb +23 -0
- data/lib/sequel_core/adapters/oracle.rb +4 -77
- data/lib/sequel_core/adapters/postgres.rb +39 -26
- data/lib/sequel_core/adapters/shared/mssql.rb +0 -1
- data/lib/sequel_core/adapters/shared/mysql.rb +1 -1
- data/lib/sequel_core/adapters/shared/oracle.rb +82 -0
- data/lib/sequel_core/adapters/shared/postgres.rb +65 -46
- data/lib/sequel_core/core_ext.rb +10 -0
- data/lib/sequel_core/core_sql.rb +7 -0
- data/lib/sequel_core/database.rb +22 -0
- data/lib/sequel_core/database/schema.rb +1 -1
- data/lib/sequel_core/dataset.rb +29 -11
- data/lib/sequel_core/dataset/sql.rb +27 -7
- data/lib/sequel_core/migration.rb +20 -2
- data/lib/sequel_core/object_graph.rb +24 -10
- data/lib/sequel_core/schema/generator.rb +22 -9
- data/lib/sequel_core/schema/sql.rb +13 -9
- data/lib/sequel_core/sql.rb +27 -2
- data/lib/sequel_model/association_reflection.rb +251 -141
- data/lib/sequel_model/associations.rb +114 -61
- data/lib/sequel_model/base.rb +25 -21
- data/lib/sequel_model/eager_loading.rb +17 -40
- data/lib/sequel_model/hooks.rb +25 -24
- data/lib/sequel_model/record.rb +29 -51
- data/lib/sequel_model/schema.rb +1 -1
- data/lib/sequel_model/validations.rb +13 -3
- data/spec/adapters/postgres_spec.rb +104 -18
- data/spec/adapters/spec_helper.rb +4 -1
- data/spec/integration/eager_loader_test.rb +5 -4
- data/spec/integration/spec_helper.rb +4 -1
- data/spec/sequel_core/connection_pool_spec.rb +24 -24
- data/spec/sequel_core/core_sql_spec.rb +12 -0
- data/spec/sequel_core/dataset_spec.rb +77 -2
- data/spec/sequel_core/expression_filters_spec.rb +6 -0
- data/spec/sequel_core/object_graph_spec.rb +40 -2
- data/spec/sequel_core/schema_spec.rb +13 -0
- data/spec/sequel_model/association_reflection_spec.rb +8 -8
- data/spec/sequel_model/associations_spec.rb +164 -3
- data/spec/sequel_model/caching_spec.rb +2 -1
- data/spec/sequel_model/eager_loading_spec.rb +107 -3
- data/spec/sequel_model/hooks_spec.rb +38 -22
- data/spec/sequel_model/model_spec.rb +11 -35
- data/spec/sequel_model/plugins_spec.rb +4 -2
- data/spec/sequel_model/record_spec.rb +8 -5
- data/spec/sequel_model/validations_spec.rb +25 -0
- data/spec/spec_config.rb +4 -3
- metadata +21 -19
@@ -33,9 +33,7 @@ module Sequel
|
|
33
33
|
# or an object that responds to .dataset and yields a symbol or a dataset
|
34
34
|
# * join_conditions - Any condition(s) allowed by join_table.
|
35
35
|
# * options - A hash of graph options. The following options are currently used:
|
36
|
-
# * :
|
37
|
-
# alias the table. You will get an error if the the alias (or table) name is
|
38
|
-
# used more than once.
|
36
|
+
# * :implicit_qualifier - The qualifier of implicit conditions, see #join_table.
|
39
37
|
# * :join_type - The type of join to use (passed to join_table). Defaults to
|
40
38
|
# :left_outer.
|
41
39
|
# * :select - An array of columns to select. When not used, selects
|
@@ -43,6 +41,9 @@ module Sequel
|
|
43
41
|
# columns and is like simply joining the tables, though graph keeps
|
44
42
|
# some metadata about join that makes it important to use graph instead
|
45
43
|
# of join.
|
44
|
+
# * :table_alias - The alias to use for the table. If not specified, doesn't
|
45
|
+
# alias the table. You will get an error if the the alias (or table) name is
|
46
|
+
# used more than once.
|
46
47
|
# * block - A block that is passed to join_table.
|
47
48
|
def graph(dataset, join_conditions = nil, options = {}, &block)
|
48
49
|
# Allow the use of a model, dataset, or symbol as the first argument
|
@@ -69,7 +70,7 @@ module Sequel
|
|
69
70
|
raise_alias_error.call if @opts[:graph] && @opts[:graph][:table_aliases] && @opts[:graph][:table_aliases].include?(table_alias)
|
70
71
|
|
71
72
|
# Join the table early in order to avoid cloning the dataset twice
|
72
|
-
ds = join_table(options[:join_type] || :left_outer, table, join_conditions, table_alias, &block)
|
73
|
+
ds = join_table(options[:join_type] || :left_outer, table, join_conditions, :table_alias=>table_alias, :implicit_qualifier=>options[:implicit_qualifier], &block)
|
73
74
|
opts = ds.opts
|
74
75
|
|
75
76
|
# Whether to include the table in the result set
|
@@ -155,18 +156,31 @@ module Sequel
|
|
155
156
|
# The first element of the array should be the table alias,
|
156
157
|
# and the second should be the actual column name.
|
157
158
|
def set_graph_aliases(graph_aliases)
|
158
|
-
|
159
|
-
identifier = tc[1].qualify(tc[0])
|
160
|
-
identifier = identifier.as(col_alias) unless tc[1] == col_alias
|
161
|
-
identifier
|
162
|
-
end
|
163
|
-
ds = select(*cols)
|
159
|
+
ds = select(*graph_alias_columns(graph_aliases))
|
164
160
|
ds.opts[:graph_aliases] = graph_aliases
|
165
161
|
ds
|
166
162
|
end
|
167
163
|
|
164
|
+
# Adds the give graph aliases to the list of graph aliases to use,
|
165
|
+
# unlike #set_graph_aliases, which replaces the list. See
|
166
|
+
# #set_graph_aliases.
|
167
|
+
def add_graph_aliases(graph_aliases)
|
168
|
+
ds = select_more(*graph_alias_columns(graph_aliases))
|
169
|
+
ds.opts[:graph_aliases] = (ds.opts[:graph_aliases] || {}).merge(graph_aliases)
|
170
|
+
ds
|
171
|
+
end
|
172
|
+
|
168
173
|
private
|
169
174
|
|
175
|
+
# Transform the hash of graph aliases to an array of columns
|
176
|
+
def graph_alias_columns(graph_aliases)
|
177
|
+
graph_aliases.collect do |col_alias, tc|
|
178
|
+
identifier = tc[2] || tc[1].qualify(tc[0])
|
179
|
+
identifier = SQL::AliasedExpression.new(identifier, col_alias) if tc[2] or tc[1] != col_alias
|
180
|
+
identifier
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
170
184
|
# Fetch the rows, split them into component table parts,
|
171
185
|
# tranform and run the row_proc on each part (if applicable),
|
172
186
|
# and yield a hash of the parts.
|
@@ -2,15 +2,16 @@ module Sequel
|
|
2
2
|
# The Schema module holds the schema generators and the SQL code relating
|
3
3
|
# to SQL DDL (Data Definition Language).
|
4
4
|
module Schema
|
5
|
-
# Schema::Generator is
|
5
|
+
# Schema::Generator is an internal class that the user is not expected
|
6
|
+
# to instantiate directly. Instances are created by Database#create_table.
|
7
|
+
# It is used to specify table creation parameters. It takes a Database
|
6
8
|
# object and a block of column/index/constraint specifications, and
|
7
|
-
#
|
9
|
+
# gives the Database a table description, which the database uses to
|
10
|
+
# create a table.
|
8
11
|
#
|
9
12
|
# Schema::Generator has some methods but also includes method_missing,
|
10
13
|
# allowing users to specify column type as a method instead of using
|
11
14
|
# the column method, which makes for a nicer DSL.
|
12
|
-
#
|
13
|
-
# See Database#create_table.
|
14
15
|
class Generator
|
15
16
|
# Set the database in which to create the table, and evaluate the block
|
16
17
|
# in the context of this object.
|
@@ -118,9 +119,17 @@ module Sequel
|
|
118
119
|
name ? column(name, type, opts) : super
|
119
120
|
end
|
120
121
|
|
121
|
-
# Add
|
122
|
-
#
|
123
|
-
#
|
122
|
+
# Add primary key information to the DDL. Takes between one and three
|
123
|
+
# arguments. The last one is an options hash as for Generator#column.
|
124
|
+
# The first one distinguishes two modes: an array of existing column
|
125
|
+
# names adds a composite primary key constraint. A single symbol adds a
|
126
|
+
# new column of that name and makes it the primary key. In that case the
|
127
|
+
# optional middle argument denotes the type.
|
128
|
+
#
|
129
|
+
# Examples:
|
130
|
+
# primary_key(:id) primary_key(:name, :text)
|
131
|
+
# primary_key(:zip_code, :null => false)
|
132
|
+
# primary_key([:street_number, :house_number])
|
124
133
|
def primary_key(name, *args)
|
125
134
|
return composite_primary_key(name, *args) if name.is_a?(Array)
|
126
135
|
@primary_key = @db.serial_primary_key_options.merge({:name => name})
|
@@ -165,8 +174,12 @@ module Sequel
|
|
165
174
|
end
|
166
175
|
end
|
167
176
|
|
168
|
-
#
|
169
|
-
#
|
177
|
+
# Schema::AlterTableGenerator is an internal class that the user is not expected
|
178
|
+
# to instantiate directly. Instances are created by Database#alter_table.
|
179
|
+
# It is used to specify table alteration parameters. It takes a Database
|
180
|
+
# object and a block of operations to perform on the table, and
|
181
|
+
# gives the Database a table an array of operations, which the database uses to
|
182
|
+
# alter a table's description.
|
170
183
|
class AlterTableGenerator
|
171
184
|
# An array of DDL operations to perform
|
172
185
|
attr_reader :operations
|
@@ -20,7 +20,6 @@ module Sequel
|
|
20
20
|
# The SQL to execute to modify the DDL for the given table name. op
|
21
21
|
# should be one of the operations returned by the AlterTableGenerator.
|
22
22
|
def alter_table_sql(table, op)
|
23
|
-
quoted_table = quote_identifier(table)
|
24
23
|
quoted_name = quote_identifier(op[:name]) if op[:name]
|
25
24
|
alter_table_op = case op[:op]
|
26
25
|
when :add_column
|
@@ -46,7 +45,7 @@ module Sequel
|
|
46
45
|
else
|
47
46
|
raise Error, "Unsupported ALTER TABLE operation"
|
48
47
|
end
|
49
|
-
"ALTER TABLE #{
|
48
|
+
"ALTER TABLE #{quote_schema_table(table)} #{alter_table_op}"
|
50
49
|
end
|
51
50
|
|
52
51
|
# Array of SQL DDL modification statements for the given table,
|
@@ -83,7 +82,7 @@ module Sequel
|
|
83
82
|
|
84
83
|
# SQL DDL fragment for column foreign key references
|
85
84
|
def column_references_sql(column)
|
86
|
-
sql = " REFERENCES #{
|
85
|
+
sql = " REFERENCES #{quote_schema_table(column[:table])}"
|
87
86
|
sql << "(#{Array(column[:key]).map{|x| quote_identifier(x)}.join(COMMA_SEPARATOR)})" if column[:key]
|
88
87
|
sql << " ON DELETE #{on_delete_clause(column[:on_delete])}" if column[:on_delete]
|
89
88
|
sql << " ON UPDATE #{on_delete_clause(column[:on_update])}" if column[:on_update]
|
@@ -112,7 +111,7 @@ module Sequel
|
|
112
111
|
# name and column specifications, and the others for specifying indexes on
|
113
112
|
# the table.
|
114
113
|
def create_table_sql_list(name, columns, indexes = nil)
|
115
|
-
sql = ["CREATE TABLE #{
|
114
|
+
sql = ["CREATE TABLE #{quote_schema_table(name)} (#{column_list_sql(columns)})"]
|
116
115
|
sql.concat(index_list_sql_list(name, indexes)) if indexes && !indexes.empty?
|
117
116
|
sql
|
118
117
|
end
|
@@ -120,7 +119,8 @@ module Sequel
|
|
120
119
|
# Default index name for the table and columns, may be too long
|
121
120
|
# for certain databases.
|
122
121
|
def default_index_name(table_name, columns)
|
123
|
-
|
122
|
+
schema, table = schema_and_table(table_name)
|
123
|
+
"#{"#{schema}_" if schema and schema != default_schema}#{table}_#{columns.join(UNDERSCORE)}_index"
|
124
124
|
end
|
125
125
|
|
126
126
|
# The SQL to drop an index for the table.
|
@@ -130,7 +130,7 @@ module Sequel
|
|
130
130
|
|
131
131
|
# SQL DDL statement to drop the table with the given name.
|
132
132
|
def drop_table_sql(name)
|
133
|
-
"DROP TABLE #{
|
133
|
+
"DROP TABLE #{quote_schema_table(name)}"
|
134
134
|
end
|
135
135
|
|
136
136
|
# Proxy the filter_expr call to the dataset, used for creating constraints.
|
@@ -187,6 +187,11 @@ module Sequel
|
|
187
187
|
end
|
188
188
|
end
|
189
189
|
|
190
|
+
def quote_schema_table(table)
|
191
|
+
schema, table = schema_and_table(table)
|
192
|
+
"#{"#{quote_identifier(schema)}." if schema}#{quote_identifier(table)}"
|
193
|
+
end
|
194
|
+
|
190
195
|
# Proxy the quote_identifier method to the dataset, used for quoting tables and columns.
|
191
196
|
def quote_identifier(v)
|
192
197
|
schema_utility_dataset.quote_identifier(v)
|
@@ -194,7 +199,7 @@ module Sequel
|
|
194
199
|
|
195
200
|
# SQL DDL statement for renaming a table.
|
196
201
|
def rename_table_sql(name, new_name)
|
197
|
-
"ALTER TABLE #{
|
202
|
+
"ALTER TABLE #{quote_schema_table(name)} RENAME TO #{quote_schema_table(new_name)}"
|
198
203
|
end
|
199
204
|
|
200
205
|
# Parse the schema from the database using the SQL standard INFORMATION_SCHEMA.
|
@@ -249,7 +254,6 @@ module Sequel
|
|
249
254
|
@schema_utility_dataset ||= dataset
|
250
255
|
end
|
251
256
|
|
252
|
-
|
253
257
|
private
|
254
258
|
|
255
259
|
# Match the database's column type to a ruby type via a
|
@@ -273,7 +277,7 @@ module Sequel
|
|
273
277
|
:boolean
|
274
278
|
when /\A(real|float|double( precision)?)\z/
|
275
279
|
:float
|
276
|
-
when /\A(numeric
|
280
|
+
when /\A(numeric(\(\d+,\d+\))?|decimal|money)\z/
|
277
281
|
:decimal
|
278
282
|
when "bytea"
|
279
283
|
:blob
|
data/lib/sequel_core/sql.rb
CHANGED
@@ -98,6 +98,15 @@ module Sequel
|
|
98
98
|
def to_s(ds)
|
99
99
|
ds.complex_expression_sql(@op, @args)
|
100
100
|
end
|
101
|
+
|
102
|
+
# Returns true if the receiver is the same expression as the
|
103
|
+
# the +other+ expression.
|
104
|
+
def eql?( other )
|
105
|
+
return other.is_a?( self.class ) &&
|
106
|
+
@op.eql?( other.op ) &&
|
107
|
+
@args.eql?( other.args )
|
108
|
+
end
|
109
|
+
alias_method :==, :eql?
|
101
110
|
end
|
102
111
|
|
103
112
|
# The base class for expressions that can be used in multiple places in
|
@@ -398,6 +407,22 @@ module Sequel
|
|
398
407
|
end
|
399
408
|
end
|
400
409
|
|
410
|
+
# Represents an SQL array. Added so it is possible to deal with a
|
411
|
+
# ruby array of all two pairs as an SQL array instead of an ordered
|
412
|
+
# hash-like conditions specifier.
|
413
|
+
class SQLArray < Expression
|
414
|
+
# Create an object with the given array.
|
415
|
+
def initialize(array)
|
416
|
+
@array = array
|
417
|
+
end
|
418
|
+
|
419
|
+
# Delegate the creation of the resulting SQL to the given dataset,
|
420
|
+
# since it may be database dependent.
|
421
|
+
def to_s(ds)
|
422
|
+
ds.array_sql(@array)
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
401
426
|
# Blob is used to represent binary data in the Ruby environment that is
|
402
427
|
# stored as a blob type in the database. In PostgreSQL, the blob type is
|
403
428
|
# called bytea. Sequel represents binary data as a Blob object because
|
@@ -438,7 +463,7 @@ module Sequel
|
|
438
463
|
ce = case r
|
439
464
|
when Range
|
440
465
|
new(:AND, new(:>=, l, r.begin), new(r.exclude_end? ? :< : :<=, l, r.end))
|
441
|
-
when Array, ::Sequel::Dataset
|
466
|
+
when Array, ::Sequel::Dataset, SQLArray
|
442
467
|
new(:IN, l, r)
|
443
468
|
when NilClass
|
444
469
|
new(:IS, l, r)
|
@@ -732,7 +757,7 @@ module Sequel
|
|
732
757
|
def self.like(l, *ces)
|
733
758
|
case_insensitive = ces.extract_options![:case_insensitive]
|
734
759
|
ces.collect! do |ce|
|
735
|
-
op, expr = Regexp === ce ? [ce.casefold? || case_insensitive ? :'~*' : :~, ce.source] : [case_insensitive ? :ILIKE : :LIKE, ce
|
760
|
+
op, expr = Regexp === ce ? [ce.casefold? || case_insensitive ? :'~*' : :~, ce.source] : [case_insensitive ? :ILIKE : :LIKE, ce]
|
736
761
|
BooleanExpression.new(op, l, expr)
|
737
762
|
end
|
738
763
|
ces.length == 1 ? ces.at(0) : BooleanExpression.new(:OR, *ces)
|
@@ -1,157 +1,267 @@
|
|
1
|
-
module Sequel
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
:"_add_#{self[:name].to_s.singularize}"
|
14
|
-
end
|
15
|
-
|
16
|
-
# Name symbol for _dataset association method
|
17
|
-
def _dataset_method
|
18
|
-
:"_#{self[:name]}_dataset"
|
19
|
-
end
|
20
|
-
|
21
|
-
# Name symbol for _remove_all internal association method
|
22
|
-
def _remove_all_method
|
23
|
-
:"_remove_all_#{self[:name]}"
|
24
|
-
end
|
25
|
-
|
26
|
-
# Name symbol for _remove_ internal association method
|
27
|
-
def _remove_method
|
28
|
-
:"_remove_#{self[:name].to_s.singularize}"
|
29
|
-
end
|
30
|
-
|
31
|
-
# Name symbol for setter association method
|
32
|
-
def _setter_method
|
33
|
-
:"_#{self[:name]}="
|
34
|
-
end
|
35
|
-
|
36
|
-
# Name symbol for add_ association method
|
37
|
-
def add_method
|
38
|
-
:"add_#{self[:name].to_s.singularize}"
|
39
|
-
end
|
40
|
-
|
41
|
-
# Name symbol for association method, the same as the name of the association.
|
42
|
-
def association_method
|
43
|
-
self[:name]
|
44
|
-
end
|
45
|
-
|
46
|
-
# The class associated to the current model class via this association
|
47
|
-
def associated_class
|
48
|
-
self[:class] ||= self[:class_name].constantize
|
49
|
-
end
|
1
|
+
module Sequel::Model::Associations
|
2
|
+
# Map of association type symbols to association reflection classes.
|
3
|
+
ASSOCIATION_TYPES = {}
|
4
|
+
|
5
|
+
# AssociationReflection is a Hash subclass that keeps information on Sequel::Model associations. It
|
6
|
+
# provides methods to reduce internal code duplication. It should not
|
7
|
+
# be instantiated by the user.
|
8
|
+
class AssociationReflection < Hash
|
9
|
+
# Name symbol for _add_ internal association method
|
10
|
+
def _add_method
|
11
|
+
:"_add_#{self[:name].to_s.singularize}"
|
12
|
+
end
|
50
13
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
14
|
+
# Name symbol for _dataset association method
|
15
|
+
def _dataset_method
|
16
|
+
:"_#{self[:name]}_dataset"
|
17
|
+
end
|
55
18
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
19
|
+
# Name symbol for _remove_all internal association method
|
20
|
+
def _remove_all_method
|
21
|
+
:"_remove_all_#{self[:name]}"
|
22
|
+
end
|
23
|
+
|
24
|
+
# Name symbol for _remove_ internal association method
|
25
|
+
def _remove_method
|
26
|
+
:"_remove_#{self[:name].to_s.singularize}"
|
27
|
+
end
|
28
|
+
|
29
|
+
# Name symbol for setter association method
|
30
|
+
def _setter_method
|
31
|
+
:"_#{self[:name]}="
|
32
|
+
end
|
33
|
+
|
34
|
+
# Name symbol for add_ association method
|
35
|
+
def add_method
|
36
|
+
:"add_#{self[:name].to_s.singularize}"
|
37
|
+
end
|
38
|
+
|
39
|
+
# Name symbol for association method, the same as the name of the association.
|
40
|
+
def association_method
|
41
|
+
self[:name]
|
42
|
+
end
|
43
|
+
|
44
|
+
# The class associated to the current model class via this association
|
45
|
+
def associated_class
|
46
|
+
self[:class] ||= self[:class_name].constantize
|
47
|
+
end
|
48
|
+
|
49
|
+
# Name symbol for dataset association method
|
50
|
+
def dataset_method
|
51
|
+
:"#{self[:name]}_dataset"
|
52
|
+
end
|
53
|
+
|
54
|
+
# Name symbol for _helper internal association method
|
55
|
+
def dataset_helper_method
|
56
|
+
:"_#{self[:name]}_dataset_helper"
|
57
|
+
end
|
58
|
+
|
59
|
+
# Whether the dataset needs a primary key to function, true by default.
|
60
|
+
def dataset_need_primary_key?
|
61
|
+
true
|
62
|
+
end
|
70
63
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
end
|
76
|
-
|
77
|
-
# Default foreign key name symbol for key in associated table that points to
|
78
|
-
# current table's primary key.
|
79
|
-
def default_left_key
|
80
|
-
:"#{self[:model].name.to_s.demodulize.underscore}_id"
|
81
|
-
end
|
64
|
+
# Whether to eagerly graph a lazy dataset, true by default.
|
65
|
+
def eager_graph_lazy_dataset?
|
66
|
+
true
|
67
|
+
end
|
82
68
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
# Whether to eagerly graph a lazy dataset
|
90
|
-
def eager_graph_lazy_dataset?
|
91
|
-
self[:type] != :many_to_one or self[:key].nil?
|
92
|
-
end
|
69
|
+
# Whether the associated object needs a primary key to be added/removed,
|
70
|
+
# false by default.
|
71
|
+
def need_associated_primary_key?
|
72
|
+
false
|
73
|
+
end
|
93
74
|
|
94
|
-
|
95
|
-
|
96
|
-
|
75
|
+
# Returns/sets the reciprocal association variable, if one exists
|
76
|
+
def reciprocal
|
77
|
+
return self[:reciprocal] if include?(:reciprocal)
|
78
|
+
r_type = reciprocal_type
|
79
|
+
key = self[:key]
|
80
|
+
associated_class.all_association_reflections.each do |assoc_reflect|
|
81
|
+
if assoc_reflect[:type] == r_type && assoc_reflect[:key] == key
|
82
|
+
return self[:reciprocal] = assoc_reflect[:name]
|
97
83
|
end
|
84
|
+
end
|
85
|
+
self[:reciprocal] = nil
|
86
|
+
end
|
98
87
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
left_key = self[:left_key]
|
105
|
-
right_key = self[:right_key]
|
106
|
-
join_table = self[:join_table]
|
107
|
-
associated_class.all_association_reflections.each do |assoc_reflect|
|
108
|
-
if assoc_reflect[:type] == :many_to_many && assoc_reflect[:left_key] == right_key \
|
109
|
-
&& assoc_reflect[:right_key] == left_key && assoc_reflect[:join_table] == join_table
|
110
|
-
return self[:reciprocal] = assoc_reflect[:name]
|
111
|
-
end
|
112
|
-
end
|
113
|
-
else
|
114
|
-
key = self[:key]
|
115
|
-
associated_class.all_association_reflections.each do |assoc_reflect|
|
116
|
-
if assoc_reflect[:type] == reciprocal_type && assoc_reflect[:key] == key
|
117
|
-
return self[:reciprocal] = assoc_reflect[:name]
|
118
|
-
end
|
119
|
-
end
|
120
|
-
end
|
121
|
-
self[:reciprocal] = nil
|
122
|
-
end
|
88
|
+
# Whether the reciprocal of this association returns an array of objects instead of a single object,
|
89
|
+
# true by default.
|
90
|
+
def reciprocal_array?
|
91
|
+
true
|
92
|
+
end
|
123
93
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
94
|
+
# Name symbol for remove_all_ association method
|
95
|
+
def remove_all_method
|
96
|
+
:"remove_all_#{self[:name]}"
|
97
|
+
end
|
98
|
+
|
99
|
+
# Name symbol for remove_ association method
|
100
|
+
def remove_method
|
101
|
+
:"remove_#{self[:name].to_s.singularize}"
|
102
|
+
end
|
103
|
+
|
104
|
+
# Whether this association returns an array of objects instead of a single object,
|
105
|
+
# true by default.
|
106
|
+
def returns_array?
|
107
|
+
true
|
108
|
+
end
|
139
109
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
110
|
+
# The columns to select when loading the association, nil by default.
|
111
|
+
def select
|
112
|
+
self[:select]
|
113
|
+
end
|
144
114
|
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
115
|
+
# By default, associations shouldn't set the reciprocal association to self.
|
116
|
+
def set_reciprocal_to_self?
|
117
|
+
false
|
118
|
+
end
|
119
|
+
|
120
|
+
# Name symbol for setter association method
|
121
|
+
def setter_method
|
122
|
+
:"#{self[:name]}="
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
|
127
|
+
class ManyToOneAssociationReflection < AssociationReflection
|
128
|
+
ASSOCIATION_TYPES[:many_to_one] = self
|
129
|
+
|
130
|
+
# Whether the dataset needs a primary key to function, false for many_to_one associations.
|
131
|
+
def dataset_need_primary_key?
|
132
|
+
false
|
133
|
+
end
|
134
|
+
|
135
|
+
# Default foreign key name symbol for foreign key in current model's table that points to
|
136
|
+
# the given association's table's primary key.
|
137
|
+
def default_key
|
138
|
+
:"#{self[:name]}_id"
|
139
|
+
end
|
140
|
+
|
141
|
+
# Whether to eagerly graph a lazy dataset, true for many_to_one associations
|
142
|
+
# only if the key is nil.
|
143
|
+
def eager_graph_lazy_dataset?
|
144
|
+
self[:key].nil?
|
145
|
+
end
|
146
|
+
|
147
|
+
# The key to use for the key hash when eager loading
|
148
|
+
def eager_loader_key
|
149
|
+
self[:key]
|
150
|
+
end
|
151
|
+
|
152
|
+
# The column in the associated table that the key in the current table references.
|
153
|
+
def primary_key
|
154
|
+
self[:primary_key] ||= associated_class.primary_key
|
155
|
+
end
|
156
|
+
|
157
|
+
# Whether this association returns an array of objects instead of a single object,
|
158
|
+
# false for a many_to_one association.
|
159
|
+
def returns_array?
|
160
|
+
false
|
161
|
+
end
|
162
|
+
|
163
|
+
private
|
164
|
+
|
165
|
+
# The reciprocal type of a many_to_one association is a one_to_many association.
|
166
|
+
def reciprocal_type
|
167
|
+
:one_to_many
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
class OneToManyAssociationReflection < AssociationReflection
|
172
|
+
ASSOCIATION_TYPES[:one_to_many] = self
|
173
|
+
|
174
|
+
# Default foreign key name symbol for key in associated table that points to
|
175
|
+
# current table's primary key.
|
176
|
+
def default_key
|
177
|
+
:"#{self[:model].name.to_s.demodulize.underscore}_id"
|
178
|
+
end
|
179
|
+
|
180
|
+
# The key to use for the key hash when eager loading
|
181
|
+
def eager_loader_key
|
182
|
+
primary_key
|
183
|
+
end
|
184
|
+
|
185
|
+
# The column in the current table that the key in the associated table references.
|
186
|
+
def primary_key
|
187
|
+
self[:primary_key] ||= self[:model].primary_key
|
188
|
+
end
|
189
|
+
|
190
|
+
# One to many associations set the reciprocal to self.
|
191
|
+
def set_reciprocal_to_self?
|
192
|
+
true
|
193
|
+
end
|
194
|
+
|
195
|
+
# Whether the reciprocal of this association returns an array of objects instead of a single object,
|
196
|
+
# false for a one_to_many association.
|
197
|
+
def reciprocal_array?
|
198
|
+
false
|
199
|
+
end
|
200
|
+
|
201
|
+
private
|
202
|
+
|
203
|
+
# The reciprocal type of a one_to_many association is a many_to_one association.
|
204
|
+
def reciprocal_type
|
205
|
+
:many_to_one
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
class ManyToManyAssociationReflection < AssociationReflection
|
210
|
+
ASSOCIATION_TYPES[:many_to_many] = self
|
211
|
+
|
212
|
+
# Default name symbol for the join table.
|
213
|
+
def default_join_table
|
214
|
+
([self[:class_name].demodulize, self[:model].name.to_s.demodulize]. \
|
215
|
+
map{|i| i.pluralize.underscore}.sort.join('_')).to_sym
|
216
|
+
end
|
217
|
+
|
218
|
+
# Default foreign key name symbol for key in join table that points to
|
219
|
+
# current table's primary key (or :left_primary_key column).
|
220
|
+
def default_left_key
|
221
|
+
:"#{self[:model].name.to_s.demodulize.underscore}_id"
|
222
|
+
end
|
223
|
+
|
224
|
+
# Default foreign key name symbol for foreign key in join table that points to
|
225
|
+
# the association's table's primary key (or :right_primary_key column).
|
226
|
+
def default_right_key
|
227
|
+
:"#{self[:name].to_s.singularize}_id"
|
228
|
+
end
|
229
|
+
|
230
|
+
# The key to use for the key hash when eager loading
|
231
|
+
def eager_loader_key
|
232
|
+
self[:left_primary_key]
|
233
|
+
end
|
149
234
|
|
150
|
-
|
151
|
-
|
152
|
-
|
235
|
+
# Whether the associated object needs a primary key to be added/removed,
|
236
|
+
# true for many_to_many associations.
|
237
|
+
def need_associated_primary_key?
|
238
|
+
true
|
239
|
+
end
|
240
|
+
|
241
|
+
# Returns/sets the reciprocal association variable, if one exists
|
242
|
+
def reciprocal
|
243
|
+
return self[:reciprocal] if include?(:reciprocal)
|
244
|
+
left_key = self[:left_key]
|
245
|
+
right_key = self[:right_key]
|
246
|
+
join_table = self[:join_table]
|
247
|
+
associated_class.all_association_reflections.each do |assoc_reflect|
|
248
|
+
if assoc_reflect[:type] == :many_to_many && assoc_reflect[:left_key] == right_key \
|
249
|
+
&& assoc_reflect[:right_key] == left_key && assoc_reflect[:join_table] == join_table
|
250
|
+
return self[:reciprocal] = assoc_reflect[:name]
|
153
251
|
end
|
154
252
|
end
|
253
|
+
self[:reciprocal] = nil
|
254
|
+
end
|
255
|
+
|
256
|
+
# The primary key column to use in the associated table.
|
257
|
+
def right_primary_key
|
258
|
+
self[:right_primary_key] ||= associated_class.primary_key
|
259
|
+
end
|
260
|
+
|
261
|
+
# The columns to select when loading the association, associated_class.table_name.* by default.
|
262
|
+
def select
|
263
|
+
return self[:select] if include?(:select)
|
264
|
+
self[:select] ||= associated_class.table_name.*
|
155
265
|
end
|
156
266
|
end
|
157
267
|
end
|