sequel 2.6.0 → 2.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|