activerecord 1.13.2 → 1.14.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- data/CHANGELOG +452 -10
- data/RUNNING_UNIT_TESTS +1 -1
- data/lib/active_record.rb +5 -2
- data/lib/active_record/acts/list.rb +1 -1
- data/lib/active_record/acts/tree.rb +29 -25
- data/lib/active_record/aggregations.rb +3 -2
- data/lib/active_record/associations.rb +783 -337
- data/lib/active_record/associations/association_collection.rb +7 -12
- data/lib/active_record/associations/association_proxy.rb +62 -24
- data/lib/active_record/associations/belongs_to_association.rb +27 -46
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +50 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +38 -38
- data/lib/active_record/associations/has_many_association.rb +61 -56
- data/lib/active_record/associations/has_many_through_association.rb +144 -0
- data/lib/active_record/associations/has_one_association.rb +22 -16
- data/lib/active_record/base.rb +482 -182
- data/lib/active_record/calculations.rb +225 -0
- data/lib/active_record/callbacks.rb +7 -7
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +162 -47
- data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +21 -1
- data/lib/active_record/connection_adapters/abstract_adapter.rb +34 -2
- data/lib/active_record/connection_adapters/db2_adapter.rb +107 -61
- data/lib/active_record/connection_adapters/mysql_adapter.rb +29 -6
- data/lib/active_record/connection_adapters/openbase_adapter.rb +349 -0
- data/lib/active_record/connection_adapters/{oci_adapter.rb → oracle_adapter.rb} +125 -59
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +24 -21
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +47 -8
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +36 -16
- data/lib/active_record/connection_adapters/sybase_adapter.rb +684 -0
- data/lib/active_record/fixtures.rb +42 -17
- data/lib/active_record/locking.rb +36 -15
- data/lib/active_record/migration.rb +111 -8
- data/lib/active_record/observer.rb +25 -1
- data/lib/active_record/reflection.rb +103 -41
- data/lib/active_record/schema.rb +2 -2
- data/lib/active_record/schema_dumper.rb +55 -18
- data/lib/active_record/timestamp.rb +6 -6
- data/lib/active_record/validations.rb +65 -40
- data/lib/active_record/vendor/db2.rb +10 -5
- data/lib/active_record/vendor/simple.rb +693 -702
- data/lib/active_record/version.rb +2 -2
- data/rakefile +4 -4
- data/test/aaa_create_tables_test.rb +25 -6
- data/test/abstract_unit.rb +39 -1
- data/test/adapter_test.rb +31 -4
- data/test/associations_cascaded_eager_loading_test.rb +106 -0
- data/test/associations_go_eager_test.rb +85 -16
- data/test/associations_join_model_test.rb +338 -0
- data/test/associations_test.rb +129 -50
- data/test/base_test.rb +204 -49
- data/test/binary_test.rb +1 -1
- data/test/calculations_test.rb +169 -0
- data/test/callbacks_test.rb +5 -23
- data/test/class_inheritable_attributes_test.rb +1 -1
- data/test/column_alias_test.rb +1 -1
- data/test/connections/native_mysql/connection.rb +1 -0
- data/test/connections/native_openbase/connection.rb +22 -0
- data/test/connections/{native_oci → native_oracle}/connection.rb +7 -9
- data/test/connections/native_sqlite/connection.rb +1 -1
- data/test/connections/native_sqlite3/connection.rb +1 -0
- data/test/connections/native_sqlite3/in_memory_connection.rb +1 -0
- data/test/connections/native_sybase/connection.rb +24 -0
- data/test/defaults_test.rb +18 -0
- data/test/deprecated_associations_test.rb +2 -2
- data/test/deprecated_finder_test.rb +0 -6
- data/test/finder_test.rb +26 -23
- data/test/fixtures/accounts.yml +10 -0
- data/test/fixtures/author.rb +31 -6
- data/test/fixtures/author_favorites.yml +4 -0
- data/test/fixtures/categories/special_categories.yml +9 -0
- data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +4 -0
- data/test/fixtures/categories_posts.yml +4 -0
- data/test/fixtures/categorization.rb +5 -0
- data/test/fixtures/categorizations.yml +11 -0
- data/test/fixtures/category.rb +6 -0
- data/test/fixtures/company.rb +17 -5
- data/test/fixtures/company_in_module.rb +19 -5
- data/test/fixtures/db_definitions/db2.drop.sql +3 -0
- data/test/fixtures/db_definitions/db2.sql +121 -100
- data/test/fixtures/db_definitions/db22.sql +2 -2
- data/test/fixtures/db_definitions/firebird.drop.sql +4 -0
- data/test/fixtures/db_definitions/firebird.sql +26 -0
- data/test/fixtures/db_definitions/mysql.drop.sql +3 -0
- data/test/fixtures/db_definitions/mysql.sql +21 -1
- data/test/fixtures/db_definitions/openbase.drop.sql +2 -0
- data/test/fixtures/db_definitions/openbase.sql +282 -0
- data/test/fixtures/db_definitions/openbase2.drop.sql +2 -0
- data/test/fixtures/db_definitions/openbase2.sql +7 -0
- data/test/fixtures/db_definitions/{oci.drop.sql → oracle.drop.sql} +6 -0
- data/test/fixtures/db_definitions/{oci.sql → oracle.sql} +25 -4
- data/test/fixtures/db_definitions/{oci2.drop.sql → oracle2.drop.sql} +0 -0
- data/test/fixtures/db_definitions/{oci2.sql → oracle2.sql} +0 -0
- data/test/fixtures/db_definitions/postgresql.drop.sql +4 -0
- data/test/fixtures/db_definitions/postgresql.sql +22 -1
- data/test/fixtures/db_definitions/schema.rb +32 -0
- data/test/fixtures/db_definitions/sqlite.drop.sql +3 -0
- data/test/fixtures/db_definitions/sqlite.sql +18 -0
- data/test/fixtures/db_definitions/sqlserver.drop.sql +3 -0
- data/test/fixtures/db_definitions/sqlserver.sql +23 -3
- data/test/fixtures/db_definitions/sybase.drop.sql +31 -0
- data/test/fixtures/db_definitions/sybase.sql +204 -0
- data/test/fixtures/db_definitions/sybase2.drop.sql +4 -0
- data/test/fixtures/db_definitions/sybase2.sql +5 -0
- data/test/fixtures/developers.yml +6 -1
- data/test/fixtures/developers_projects.yml +4 -0
- data/test/fixtures/funny_jokes.yml +14 -0
- data/test/fixtures/joke.rb +6 -0
- data/test/fixtures/legacy_thing.rb +3 -0
- data/test/fixtures/legacy_things.yml +3 -0
- data/test/fixtures/mixin.rb +1 -1
- data/test/fixtures/person.rb +4 -1
- data/test/fixtures/post.rb +26 -1
- data/test/fixtures/project.rb +1 -0
- data/test/fixtures/reader.rb +4 -0
- data/test/fixtures/readers.yml +4 -0
- data/test/fixtures/reply.rb +2 -1
- data/test/fixtures/tag.rb +5 -0
- data/test/fixtures/tagging.rb +6 -0
- data/test/fixtures/taggings.yml +18 -0
- data/test/fixtures/tags.yml +7 -0
- data/test/fixtures/tasks.yml +2 -2
- data/test/fixtures/topic.rb +2 -2
- data/test/fixtures/topics.yml +1 -0
- data/test/fixtures_test.rb +47 -13
- data/test/inheritance_test.rb +2 -2
- data/test/locking_test.rb +15 -1
- data/test/method_scoping_test.rb +248 -13
- data/test/migration_test.rb +68 -11
- data/test/mixin_nested_set_test.rb +1 -1
- data/test/modules_test.rb +6 -1
- data/test/readonly_test.rb +1 -1
- data/test/reflection_test.rb +63 -9
- data/test/schema_dumper_test.rb +41 -0
- data/test/{synonym_test_oci.rb → synonym_test_oracle.rb} +1 -1
- data/test/threaded_connections_test.rb +10 -0
- data/test/unconnected_test.rb +12 -5
- data/test/validations_test.rb +197 -10
- metadata +295 -260
- data/test/fixtures/db_definitions/create_oracle_db.bat +0 -0
- data/test/fixtures/db_definitions/create_oracle_db.sh +0 -0
- data/test/fixtures/fixture_database.sqlite +0 -0
- data/test/fixtures/fixture_database_2.sqlite +0 -0
@@ -1,20 +1,18 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module Associations
|
3
3
|
class HasManyAssociation < AssociationCollection #:nodoc:
|
4
|
-
def initialize(owner,
|
4
|
+
def initialize(owner, reflection)
|
5
5
|
super
|
6
|
-
@conditions = sanitize_sql(options[:conditions])
|
7
|
-
|
8
6
|
construct_sql
|
9
7
|
end
|
10
8
|
|
11
9
|
def build(attributes = {})
|
12
10
|
if attributes.is_a?(Array)
|
13
|
-
attributes.collect { |attr|
|
11
|
+
attributes.collect { |attr| build(attr) }
|
14
12
|
else
|
15
13
|
load_target
|
16
|
-
record = @
|
17
|
-
record
|
14
|
+
record = @reflection.klass.new(attributes)
|
15
|
+
set_belongs_to_association_for(record)
|
18
16
|
@target << record
|
19
17
|
record
|
20
18
|
end
|
@@ -22,13 +20,13 @@ module ActiveRecord
|
|
22
20
|
|
23
21
|
# DEPRECATED.
|
24
22
|
def find_all(runtime_conditions = nil, orderings = nil, limit = nil, joins = nil)
|
25
|
-
if @options[:finder_sql]
|
26
|
-
@
|
23
|
+
if @reflection.options[:finder_sql]
|
24
|
+
@reflection.klass.find_by_sql(@finder_sql)
|
27
25
|
else
|
28
26
|
conditions = @finder_sql
|
29
27
|
conditions += " AND (#{sanitize_sql(runtime_conditions)})" if runtime_conditions
|
30
|
-
orderings ||= @options[:order]
|
31
|
-
@
|
28
|
+
orderings ||= @reflection.options[:order]
|
29
|
+
@reflection.klass.find_all(conditions, orderings, limit, joins)
|
32
30
|
end
|
33
31
|
end
|
34
32
|
|
@@ -39,14 +37,14 @@ module ActiveRecord
|
|
39
37
|
|
40
38
|
# Count the number of associated records. All arguments are optional.
|
41
39
|
def count(runtime_conditions = nil)
|
42
|
-
if @options[:counter_sql]
|
43
|
-
@
|
44
|
-
elsif @options[:finder_sql]
|
45
|
-
@
|
40
|
+
if @reflection.options[:counter_sql]
|
41
|
+
@reflection.klass.count_by_sql(@counter_sql)
|
42
|
+
elsif @reflection.options[:finder_sql]
|
43
|
+
@reflection.klass.count_by_sql(@finder_sql)
|
46
44
|
else
|
47
45
|
sql = @finder_sql
|
48
46
|
sql += " AND (#{sanitize_sql(runtime_conditions)})" if runtime_conditions
|
49
|
-
@
|
47
|
+
@reflection.klass.count(sql)
|
50
48
|
end
|
51
49
|
end
|
52
50
|
|
@@ -54,7 +52,7 @@ module ActiveRecord
|
|
54
52
|
options = Base.send(:extract_options_from_args!, args)
|
55
53
|
|
56
54
|
# If using a custom finder_sql, scan the entire collection.
|
57
|
-
if @options[:finder_sql]
|
55
|
+
if @reflection.options[:finder_sql]
|
58
56
|
expects_array = args.first.kind_of?(Array)
|
59
57
|
ids = args.flatten.compact.uniq
|
60
58
|
|
@@ -72,64 +70,63 @@ module ActiveRecord
|
|
72
70
|
end
|
73
71
|
options[:conditions] = conditions
|
74
72
|
|
75
|
-
if options[:order] && @options[:order]
|
76
|
-
options[:order] = "#{options[:order]}, #{@options[:order]}"
|
77
|
-
elsif @options[:order]
|
78
|
-
options[:order] = @options[:order]
|
73
|
+
if options[:order] && @reflection.options[:order]
|
74
|
+
options[:order] = "#{options[:order]}, #{@reflection.options[:order]}"
|
75
|
+
elsif @reflection.options[:order]
|
76
|
+
options[:order] = @reflection.options[:order]
|
79
77
|
end
|
80
78
|
|
79
|
+
merge_options_from_reflection!(options)
|
80
|
+
|
81
81
|
# Pass through args exactly as we received them.
|
82
82
|
args << options
|
83
|
-
@
|
83
|
+
@reflection.klass.find(*args)
|
84
84
|
end
|
85
85
|
end
|
86
86
|
|
87
87
|
protected
|
88
88
|
def method_missing(method, *args, &block)
|
89
|
-
if @target.respond_to?(method) || (!@
|
89
|
+
if @target.respond_to?(method) || (!@reflection.klass.respond_to?(method) && Class.respond_to?(method))
|
90
90
|
super
|
91
91
|
else
|
92
|
-
@
|
92
|
+
@reflection.klass.with_scope(
|
93
93
|
:find => {
|
94
94
|
:conditions => @finder_sql,
|
95
95
|
:joins => @join_sql,
|
96
96
|
:readonly => false
|
97
97
|
},
|
98
98
|
:create => {
|
99
|
-
@
|
99
|
+
@reflection.primary_key_name => @owner.id
|
100
100
|
}
|
101
101
|
) do
|
102
|
-
@
|
102
|
+
@reflection.klass.send(method, *args, &block)
|
103
103
|
end
|
104
104
|
end
|
105
105
|
end
|
106
106
|
|
107
107
|
def find_target
|
108
|
-
if @options[:finder_sql]
|
109
|
-
@
|
108
|
+
if @reflection.options[:finder_sql]
|
109
|
+
@reflection.klass.find_by_sql(@finder_sql)
|
110
110
|
else
|
111
|
-
|
112
|
-
:conditions => @finder_sql,
|
113
|
-
:order => @options[:order],
|
114
|
-
:limit => @options[:limit],
|
115
|
-
:joins => @options[:joins],
|
116
|
-
:include => @options[:include],
|
117
|
-
:group => @options[:group]
|
118
|
-
)
|
111
|
+
find(:all)
|
119
112
|
end
|
120
113
|
end
|
121
114
|
|
122
115
|
def count_records
|
123
116
|
count = if has_cached_counter?
|
124
117
|
@owner.send(:read_attribute, cached_counter_attribute_name)
|
125
|
-
elsif @options[:counter_sql]
|
126
|
-
@
|
118
|
+
elsif @reflection.options[:counter_sql]
|
119
|
+
@reflection.klass.count_by_sql(@counter_sql)
|
127
120
|
else
|
128
|
-
@
|
121
|
+
@reflection.klass.count(@counter_sql)
|
129
122
|
end
|
130
123
|
|
131
124
|
@target = [] and loaded if count == 0
|
132
125
|
|
126
|
+
if @reflection.options[:limit]
|
127
|
+
count = [ @reflection.options[:limit], count ].min
|
128
|
+
end
|
129
|
+
|
133
130
|
return count
|
134
131
|
end
|
135
132
|
|
@@ -138,22 +135,22 @@ module ActiveRecord
|
|
138
135
|
end
|
139
136
|
|
140
137
|
def cached_counter_attribute_name
|
141
|
-
"#{@
|
138
|
+
"#{@reflection.name}_count"
|
142
139
|
end
|
143
140
|
|
144
141
|
def insert_record(record)
|
145
|
-
record
|
142
|
+
set_belongs_to_association_for(record)
|
146
143
|
record.save
|
147
144
|
end
|
148
145
|
|
149
146
|
def delete_records(records)
|
150
|
-
if @options[:dependent]
|
147
|
+
if @reflection.options[:dependent]
|
151
148
|
records.each { |r| r.destroy }
|
152
149
|
else
|
153
150
|
ids = quoted_record_ids(records)
|
154
|
-
@
|
155
|
-
"#{@
|
156
|
-
"#{@
|
151
|
+
@reflection.klass.update_all(
|
152
|
+
"#{@reflection.primary_key_name} = NULL",
|
153
|
+
"#{@reflection.primary_key_name} = #{@owner.quoted_id} AND #{@reflection.klass.primary_key} IN (#{ids})"
|
157
154
|
)
|
158
155
|
end
|
159
156
|
end
|
@@ -163,21 +160,29 @@ module ActiveRecord
|
|
163
160
|
end
|
164
161
|
|
165
162
|
def construct_sql
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
163
|
+
case
|
164
|
+
when @reflection.options[:finder_sql]
|
165
|
+
@finder_sql = interpolate_sql(@reflection.options[:finder_sql])
|
166
|
+
|
167
|
+
when @reflection.options[:as]
|
168
|
+
@finder_sql =
|
169
|
+
"#{@reflection.klass.table_name}.#{@reflection.options[:as]}_id = #{@owner.quoted_id} AND " +
|
170
|
+
"#{@reflection.klass.table_name}.#{@reflection.options[:as]}_type = #{@owner.class.quote @owner.class.base_class.name.to_s}"
|
171
|
+
@finder_sql << " AND (#{conditions})" if conditions
|
172
|
+
|
173
|
+
else
|
174
|
+
@finder_sql = "#{@reflection.klass.table_name}.#{@reflection.primary_key_name} = #{@owner.quoted_id}"
|
175
|
+
@finder_sql << " AND (#{conditions})" if conditions
|
171
176
|
end
|
172
177
|
|
173
|
-
if @options[:counter_sql]
|
174
|
-
@counter_sql = interpolate_sql(@options[:counter_sql])
|
175
|
-
elsif @options[:finder_sql]
|
176
|
-
|
177
|
-
@counter_sql =
|
178
|
+
if @reflection.options[:counter_sql]
|
179
|
+
@counter_sql = interpolate_sql(@reflection.options[:counter_sql])
|
180
|
+
elsif @reflection.options[:finder_sql]
|
181
|
+
# replace the SELECT clause with COUNT(*), preserving any hints within /* ... */
|
182
|
+
@reflection.options[:counter_sql] = @reflection.options[:finder_sql].sub(/SELECT (\/\*.*?\*\/ )?(.*)\bFROM\b/im) { "SELECT #{$1}COUNT(*) FROM" }
|
183
|
+
@counter_sql = interpolate_sql(@reflection.options[:counter_sql])
|
178
184
|
else
|
179
|
-
@counter_sql =
|
180
|
-
@counter_sql << " AND (#{interpolate_sql(@conditions)})" if @conditions
|
185
|
+
@counter_sql = @finder_sql
|
181
186
|
end
|
182
187
|
end
|
183
188
|
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Associations
|
3
|
+
class HasManyThroughAssociation < AssociationProxy #:nodoc:
|
4
|
+
def initialize(owner, reflection)
|
5
|
+
super
|
6
|
+
reflection.check_validity!
|
7
|
+
@finder_sql = construct_conditions
|
8
|
+
construct_sql
|
9
|
+
end
|
10
|
+
|
11
|
+
|
12
|
+
def find(*args)
|
13
|
+
options = Base.send(:extract_options_from_args!, args)
|
14
|
+
|
15
|
+
conditions = "#{@finder_sql}"
|
16
|
+
if sanitized_conditions = sanitize_sql(options[:conditions])
|
17
|
+
conditions << " AND (#{sanitized_conditions})"
|
18
|
+
end
|
19
|
+
options[:conditions] = conditions
|
20
|
+
|
21
|
+
if options[:order] && @reflection.options[:order]
|
22
|
+
options[:order] = "#{options[:order]}, #{@reflection.options[:order]}"
|
23
|
+
elsif @reflection.options[:order]
|
24
|
+
options[:order] = @reflection.options[:order]
|
25
|
+
end
|
26
|
+
|
27
|
+
options[:select] = construct_select(options[:select])
|
28
|
+
options[:from] ||= construct_from
|
29
|
+
options[:joins] = construct_joins(options[:joins])
|
30
|
+
options[:include] = @reflection.source_reflection.options[:include] if options[:include].nil?
|
31
|
+
|
32
|
+
merge_options_from_reflection!(options)
|
33
|
+
|
34
|
+
# Pass through args exactly as we received them.
|
35
|
+
args << options
|
36
|
+
@reflection.klass.find(*args)
|
37
|
+
end
|
38
|
+
|
39
|
+
def reset
|
40
|
+
@target = []
|
41
|
+
@loaded = false
|
42
|
+
end
|
43
|
+
|
44
|
+
protected
|
45
|
+
def method_missing(method, *args, &block)
|
46
|
+
if @target.respond_to?(method) || (!@reflection.klass.respond_to?(method) && Class.respond_to?(method))
|
47
|
+
super
|
48
|
+
else
|
49
|
+
@reflection.klass.with_scope(construct_scope) { @reflection.klass.send(method, *args, &block) }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def find_target
|
54
|
+
@reflection.klass.find(:all,
|
55
|
+
:select => construct_select,
|
56
|
+
:conditions => construct_conditions,
|
57
|
+
:from => construct_from,
|
58
|
+
:joins => construct_joins,
|
59
|
+
:order => @reflection.options[:order],
|
60
|
+
:limit => @reflection.options[:limit],
|
61
|
+
:group => @reflection.options[:group],
|
62
|
+
:include => @reflection.options[:include] || @reflection.source_reflection.options[:include]
|
63
|
+
)
|
64
|
+
end
|
65
|
+
|
66
|
+
def construct_conditions
|
67
|
+
conditions = if @reflection.through_reflection.options[:as]
|
68
|
+
"#{@reflection.through_reflection.table_name}.#{@reflection.through_reflection.options[:as]}_id = #{@owner.quoted_id} " +
|
69
|
+
"AND #{@reflection.through_reflection.table_name}.#{@reflection.through_reflection.options[:as]}_type = #{@owner.class.quote @owner.class.base_class.name.to_s}"
|
70
|
+
else
|
71
|
+
case @reflection.source_reflection.macro
|
72
|
+
when :belongs_to, :has_many
|
73
|
+
"#{@reflection.through_reflection.table_name}.#{@reflection.through_reflection.primary_key_name} = #{@owner.quoted_id}"
|
74
|
+
else
|
75
|
+
raise ActiveRecordError, "Invalid source reflection macro :#{@reflection.source_reflection.macro} for has_many #{@reflection.name}, :through => #{@reflection.through_reflection.name}. Use :source to specify the source reflection."
|
76
|
+
end
|
77
|
+
end
|
78
|
+
conditions << " AND (#{sql_conditions})" if sql_conditions
|
79
|
+
|
80
|
+
return conditions
|
81
|
+
end
|
82
|
+
|
83
|
+
def construct_from
|
84
|
+
@reflection.table_name
|
85
|
+
end
|
86
|
+
|
87
|
+
def construct_select(custom_select = nil)
|
88
|
+
selected = custom_select || @reflection.options[:select] || "#{@reflection.table_name}.*"
|
89
|
+
end
|
90
|
+
|
91
|
+
def construct_joins(custom_joins = nil)
|
92
|
+
if @reflection.through_reflection.options[:as] || @reflection.source_reflection.macro == :belongs_to
|
93
|
+
reflection_primary_key = @reflection.klass.primary_key
|
94
|
+
source_primary_key = @reflection.source_reflection.primary_key_name
|
95
|
+
else
|
96
|
+
reflection_primary_key = @reflection.source_reflection.primary_key_name
|
97
|
+
source_primary_key = @reflection.klass.primary_key
|
98
|
+
end
|
99
|
+
|
100
|
+
"INNER JOIN %s ON %s.%s = %s.%s #{@reflection.options[:joins]} #{custom_joins}" % [
|
101
|
+
@reflection.through_reflection.table_name,
|
102
|
+
@reflection.table_name, reflection_primary_key,
|
103
|
+
@reflection.through_reflection.table_name, source_primary_key
|
104
|
+
]
|
105
|
+
end
|
106
|
+
|
107
|
+
def construct_scope
|
108
|
+
{
|
109
|
+
:find => { :from => construct_from, :conditions => construct_conditions, :joins => construct_joins, :select => construct_select },
|
110
|
+
:create => { @reflection.primary_key_name => @owner.id }
|
111
|
+
}
|
112
|
+
end
|
113
|
+
|
114
|
+
def construct_sql
|
115
|
+
case
|
116
|
+
when @reflection.options[:finder_sql]
|
117
|
+
@finder_sql = interpolate_sql(@reflection.options[:finder_sql])
|
118
|
+
|
119
|
+
@finder_sql = "#{@reflection.klass.table_name}.#{@reflection.primary_key_name} = #{@owner.quoted_id}"
|
120
|
+
@finder_sql << " AND (#{conditions})" if conditions
|
121
|
+
end
|
122
|
+
|
123
|
+
if @reflection.options[:counter_sql]
|
124
|
+
@counter_sql = interpolate_sql(@reflection.options[:counter_sql])
|
125
|
+
elsif @reflection.options[:finder_sql]
|
126
|
+
# replace the SELECT clause with COUNT(*), preserving any hints within /* ... */
|
127
|
+
@reflection.options[:counter_sql] = @reflection.options[:finder_sql].sub(/SELECT (\/\*.*?\*\/ )?(.*)\bFROM\b/im) { "SELECT #{$1}COUNT(*) FROM" }
|
128
|
+
@counter_sql = interpolate_sql(@reflection.options[:counter_sql])
|
129
|
+
else
|
130
|
+
@counter_sql = @finder_sql
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def conditions
|
135
|
+
@conditions ||= [
|
136
|
+
(interpolate_sql(@reflection.active_record.send(:sanitize_sql, @reflection.options[:conditions])) if @reflection.options[:conditions]),
|
137
|
+
(interpolate_sql(@reflection.active_record.send(:sanitize_sql, @reflection.through_reflection.options[:conditions])) if @reflection.through_reflection.options[:conditions])
|
138
|
+
].compact.collect { |condition| "(#{condition})" }.join(' AND ') unless (!@reflection.options[:conditions] && !@reflection.through_reflection.options[:conditions])
|
139
|
+
end
|
140
|
+
|
141
|
+
alias_method :sql_conditions, :conditions
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
@@ -1,9 +1,8 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module Associations
|
3
3
|
class HasOneAssociation < BelongsToAssociation #:nodoc:
|
4
|
-
def initialize(owner,
|
4
|
+
def initialize(owner, reflection)
|
5
5
|
super
|
6
|
-
|
7
6
|
construct_sql
|
8
7
|
end
|
9
8
|
|
@@ -14,12 +13,12 @@ module ActiveRecord
|
|
14
13
|
end
|
15
14
|
|
16
15
|
def build(attributes = {}, replace_existing = true)
|
17
|
-
record = @
|
16
|
+
record = @reflection.klass.new(attributes)
|
18
17
|
|
19
18
|
if replace_existing
|
20
19
|
replace(record, true)
|
21
20
|
else
|
22
|
-
record[@
|
21
|
+
record[@reflection.primary_key_name] = @owner.id unless @owner.new_record?
|
23
22
|
self.target = record
|
24
23
|
end
|
25
24
|
|
@@ -28,13 +27,14 @@ module ActiveRecord
|
|
28
27
|
|
29
28
|
def replace(obj, dont_save = false)
|
30
29
|
load_target
|
30
|
+
|
31
31
|
unless @target.nil?
|
32
32
|
if dependent? && !dont_save && @target != obj
|
33
33
|
@target.destroy unless @target.new_record?
|
34
34
|
@owner.clear_association_cache
|
35
35
|
else
|
36
|
-
@target[@
|
37
|
-
@target.save unless @owner.new_record?
|
36
|
+
@target[@reflection.primary_key_name] = nil
|
37
|
+
@target.save unless @owner.new_record? || @target.new_record?
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
@@ -42,12 +42,12 @@ module ActiveRecord
|
|
42
42
|
@target = nil
|
43
43
|
else
|
44
44
|
raise_on_type_mismatch(obj)
|
45
|
-
|
46
|
-
obj[@association_class_primary_key_name] = @owner.id unless @owner.new_record?
|
45
|
+
set_belongs_to_association_for(obj)
|
47
46
|
@target = (AssociationProxy === obj ? obj.target : obj)
|
48
47
|
end
|
49
48
|
|
50
49
|
@loaded = true
|
50
|
+
|
51
51
|
unless @owner.new_record? or obj.nil? or dont_save
|
52
52
|
return (obj.save ? self : false)
|
53
53
|
else
|
@@ -57,17 +57,23 @@ module ActiveRecord
|
|
57
57
|
|
58
58
|
private
|
59
59
|
def find_target
|
60
|
-
@
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
60
|
+
@reflection.klass.find(:first,
|
61
|
+
:conditions => @finder_sql,
|
62
|
+
:order => @reflection.options[:order],
|
63
|
+
:include => @reflection.options[:include]
|
64
|
+
)
|
65
65
|
end
|
66
66
|
|
67
67
|
def construct_sql
|
68
|
-
|
69
|
-
|
70
|
-
|
68
|
+
case
|
69
|
+
when @reflection.options[:as]
|
70
|
+
@finder_sql =
|
71
|
+
"#{@reflection.klass.table_name}.#{@reflection.options[:as]}_id = #{@owner.quoted_id} AND " +
|
72
|
+
"#{@reflection.klass.table_name}.#{@reflection.options[:as]}_type = #{@owner.class.quote @owner.class.base_class.name.to_s}"
|
73
|
+
else
|
74
|
+
@finder_sql = "#{@reflection.table_name}.#{@reflection.primary_key_name} = #{@owner.quoted_id}"
|
75
|
+
end
|
76
|
+
@finder_sql << " AND (#{conditions})" if conditions
|
71
77
|
end
|
72
78
|
end
|
73
79
|
end
|