activerecord 1.14.4 → 1.15.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 +400 -1
- data/README +2 -2
- data/RUNNING_UNIT_TESTS +21 -3
- data/Rakefile +55 -10
- data/lib/active_record.rb +10 -4
- data/lib/active_record/acts/list.rb +15 -4
- data/lib/active_record/acts/nested_set.rb +11 -12
- data/lib/active_record/acts/tree.rb +13 -14
- data/lib/active_record/aggregations.rb +46 -22
- data/lib/active_record/associations.rb +213 -162
- data/lib/active_record/associations/association_collection.rb +45 -15
- data/lib/active_record/associations/association_proxy.rb +32 -13
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +18 -18
- data/lib/active_record/associations/has_many_association.rb +37 -17
- data/lib/active_record/associations/has_many_through_association.rb +120 -30
- data/lib/active_record/associations/has_one_association.rb +1 -1
- data/lib/active_record/attribute_methods.rb +75 -0
- data/lib/active_record/base.rb +282 -203
- data/lib/active_record/calculations.rb +95 -54
- data/lib/active_record/callbacks.rb +13 -24
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +12 -1
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb.rej +21 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +30 -4
- data/lib/active_record/connection_adapters/abstract/quoting.rb +16 -9
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +121 -37
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +55 -23
- data/lib/active_record/connection_adapters/abstract_adapter.rb +8 -0
- data/lib/active_record/connection_adapters/db2_adapter.rb +1 -11
- data/lib/active_record/connection_adapters/firebird_adapter.rb +364 -50
- data/lib/active_record/connection_adapters/frontbase_adapter.rb +861 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +86 -33
- data/lib/active_record/connection_adapters/openbase_adapter.rb +4 -3
- data/lib/active_record/connection_adapters/oracle_adapter.rb +151 -127
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +125 -48
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +38 -10
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +183 -155
- data/lib/active_record/connection_adapters/sybase_adapter.rb +190 -212
- data/lib/active_record/deprecated_associations.rb +24 -10
- data/lib/active_record/deprecated_finders.rb +4 -1
- data/lib/active_record/fixtures.rb +37 -23
- data/lib/active_record/locking/optimistic.rb +106 -0
- data/lib/active_record/locking/pessimistic.rb +77 -0
- data/lib/active_record/migration.rb +8 -5
- data/lib/active_record/observer.rb +73 -34
- data/lib/active_record/reflection.rb +21 -7
- data/lib/active_record/schema_dumper.rb +33 -5
- data/lib/active_record/timestamp.rb +23 -34
- data/lib/active_record/transactions.rb +37 -30
- data/lib/active_record/validations.rb +46 -30
- data/lib/active_record/vendor/mysql.rb +20 -5
- data/lib/active_record/version.rb +2 -2
- data/lib/active_record/wrappings.rb +1 -2
- data/lib/active_record/xml_serialization.rb +308 -0
- data/test/aaa_create_tables_test.rb +5 -1
- data/test/abstract_unit.rb +18 -8
- data/test/{active_schema_mysql.rb → active_schema_test_mysql.rb} +2 -2
- data/test/adapter_test.rb +9 -7
- data/test/adapter_test_sqlserver.rb +81 -0
- data/test/aggregations_test.rb +29 -0
- data/test/{association_callbacks_test.rb → associations/callbacks_test.rb} +10 -8
- data/test/{associations_cascaded_eager_loading_test.rb → associations/cascaded_eager_loading_test.rb} +35 -3
- data/test/{associations_go_eager_test.rb → associations/eager_test.rb} +36 -2
- data/test/{associations_extensions_test.rb → associations/extension_test.rb} +5 -0
- data/test/{associations_join_model_test.rb → associations/join_model_test.rb} +118 -8
- data/test/associations_test.rb +339 -45
- data/test/attribute_methods_test.rb +49 -0
- data/test/base_test.rb +321 -67
- data/test/calculations_test.rb +48 -10
- data/test/callbacks_test.rb +13 -0
- data/test/connection_test_firebird.rb +8 -0
- data/test/connections/native_db2/connection.rb +18 -17
- data/test/connections/native_firebird/connection.rb +19 -17
- data/test/connections/native_frontbase/connection.rb +27 -0
- data/test/connections/native_mysql/connection.rb +18 -15
- data/test/connections/native_openbase/connection.rb +14 -15
- data/test/connections/native_oracle/connection.rb +16 -12
- data/test/connections/native_postgresql/connection.rb +16 -17
- data/test/connections/native_sqlite/connection.rb +3 -6
- data/test/connections/native_sqlite3/connection.rb +3 -6
- data/test/connections/native_sqlserver/connection.rb +16 -17
- data/test/connections/native_sqlserver_odbc/connection.rb +18 -19
- data/test/connections/native_sybase/connection.rb +16 -17
- data/test/datatype_test_postgresql.rb +52 -0
- data/test/defaults_test.rb +52 -10
- data/test/deprecated_associations_test.rb +151 -107
- data/test/deprecated_finder_test.rb +83 -66
- data/test/empty_date_time_test.rb +25 -0
- data/test/finder_test.rb +118 -11
- data/test/fixtures/accounts.yml +6 -1
- data/test/fixtures/author.rb +27 -4
- data/test/fixtures/categorizations.yml +8 -2
- data/test/fixtures/category.rb +1 -2
- data/test/fixtures/comments.yml +0 -6
- data/test/fixtures/companies.yml +6 -1
- data/test/fixtures/company.rb +23 -1
- data/test/fixtures/company_in_module.rb +8 -10
- data/test/fixtures/customer.rb +2 -2
- data/test/fixtures/customers.yml +9 -0
- data/test/fixtures/db_definitions/db2.drop.sql +1 -0
- data/test/fixtures/db_definitions/db2.sql +9 -0
- data/test/fixtures/db_definitions/firebird.drop.sql +3 -0
- data/test/fixtures/db_definitions/firebird.sql +13 -1
- data/test/fixtures/db_definitions/frontbase.drop.sql +31 -0
- data/test/fixtures/db_definitions/frontbase.sql +262 -0
- data/test/fixtures/db_definitions/frontbase2.drop.sql +1 -0
- data/test/fixtures/db_definitions/frontbase2.sql +4 -0
- data/test/fixtures/db_definitions/mysql.drop.sql +1 -0
- data/test/fixtures/db_definitions/mysql.sql +23 -14
- data/test/fixtures/db_definitions/openbase.sql +13 -1
- data/test/fixtures/db_definitions/oracle.drop.sql +2 -0
- data/test/fixtures/db_definitions/oracle.sql +29 -2
- data/test/fixtures/db_definitions/postgresql.drop.sql +3 -1
- data/test/fixtures/db_definitions/postgresql.sql +13 -3
- data/test/fixtures/db_definitions/schema.rb +29 -1
- data/test/fixtures/db_definitions/sqlite.drop.sql +1 -0
- data/test/fixtures/db_definitions/sqlite.sql +12 -3
- data/test/fixtures/db_definitions/sqlserver.drop.sql +3 -0
- data/test/fixtures/db_definitions/sqlserver.sql +35 -0
- data/test/fixtures/db_definitions/sybase.drop.sql +2 -0
- data/test/fixtures/db_definitions/sybase.sql +13 -4
- data/test/fixtures/developer.rb +12 -0
- data/test/fixtures/edge.rb +5 -0
- data/test/fixtures/edges.yml +6 -0
- data/test/fixtures/funny_jokes.yml +3 -7
- data/test/fixtures/migrations_with_decimal/1_give_me_big_numbers.rb +15 -0
- data/test/fixtures/migrations_with_missing_versions/1000_people_have_middle_names.rb +9 -0
- data/test/fixtures/migrations_with_missing_versions/1_people_have_last_names.rb +9 -0
- data/test/fixtures/migrations_with_missing_versions/3_we_need_reminders.rb +12 -0
- data/test/fixtures/migrations_with_missing_versions/4_innocent_jointable.rb +12 -0
- data/test/fixtures/mixin.rb +15 -0
- data/test/fixtures/mixins.yml +38 -0
- data/test/fixtures/post.rb +3 -2
- data/test/fixtures/project.rb +3 -1
- data/test/fixtures/topic.rb +6 -1
- data/test/fixtures/topics.yml +4 -4
- data/test/fixtures/vertex.rb +9 -0
- data/test/fixtures/vertices.yml +4 -0
- data/test/fixtures_test.rb +45 -0
- data/test/inheritance_test.rb +67 -6
- data/test/lifecycle_test.rb +40 -19
- data/test/locking_test.rb +170 -26
- data/test/method_scoping_test.rb +2 -2
- data/test/migration_test.rb +387 -110
- data/test/migration_test_firebird.rb +124 -0
- data/test/mixin_nested_set_test.rb +14 -2
- data/test/mixin_test.rb +56 -18
- data/test/modules_test.rb +8 -2
- data/test/multiple_db_test.rb +2 -2
- data/test/pk_test.rb +1 -0
- data/test/reflection_test.rb +8 -2
- data/test/schema_authorization_test_postgresql.rb +75 -0
- data/test/schema_dumper_test.rb +40 -4
- data/test/table_name_test_sqlserver.rb +23 -0
- data/test/threaded_connections_test.rb +19 -16
- data/test/transactions_test.rb +86 -72
- data/test/validations_test.rb +126 -56
- data/test/xml_serialization_test.rb +125 -0
- metadata +45 -11
- data/lib/active_record/locking.rb +0 -79
@@ -1,6 +1,6 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module Calculations #:nodoc:
|
3
|
-
CALCULATIONS_OPTIONS = [:conditions, :joins, :order, :select, :group, :having, :distinct, :limit, :offset]
|
3
|
+
CALCULATIONS_OPTIONS = [:conditions, :joins, :order, :select, :group, :having, :distinct, :limit, :offset, :include]
|
4
4
|
def self.included(base)
|
5
5
|
base.extend(ClassMethods)
|
6
6
|
end
|
@@ -9,7 +9,7 @@ module ActiveRecord
|
|
9
9
|
# Count operates using three different approaches.
|
10
10
|
#
|
11
11
|
# * Count all: By not passing any parameters to count, it will return a count of all the rows for the model.
|
12
|
-
# * Count by conditions or joins:
|
12
|
+
# * Count by conditions or joins: This API has been deprecated and will be removed in Rails 2.0
|
13
13
|
# * Count using options will find the row count matched by the options used.
|
14
14
|
#
|
15
15
|
# The last approach, count using options, accepts an option hash as the only parameter. The options are:
|
@@ -29,7 +29,7 @@ module ActiveRecord
|
|
29
29
|
# Examples for counting all:
|
30
30
|
# Person.count # returns the total count of all people
|
31
31
|
#
|
32
|
-
# Examples for count by +conditions+ and +joins+ (
|
32
|
+
# Examples for count by +conditions+ and +joins+ (this has been deprecated):
|
33
33
|
# Person.count("age > 26") # returns the number of people older than 26
|
34
34
|
# Person.find("age > 26 AND job.salary > 60000", "LEFT JOIN jobs on jobs.person_id = person.id") # returns the total number of rows matching the conditions and joins fetched by SELECT COUNT(*).
|
35
35
|
#
|
@@ -42,29 +42,7 @@ module ActiveRecord
|
|
42
42
|
#
|
43
43
|
# Note: Person.count(:all) will not work because it will use :all as the condition. Use Person.count instead.
|
44
44
|
def count(*args)
|
45
|
-
|
46
|
-
column_name = :all
|
47
|
-
# For backwards compatibility, we need to handle both count(conditions=nil, joins=nil) or count(options={}) or count(column_name=:all, options={}).
|
48
|
-
if args.size >= 0 && args.size <= 2
|
49
|
-
if args.first.is_a?(Hash)
|
50
|
-
options = args.first
|
51
|
-
elsif args[1].is_a?(Hash)
|
52
|
-
options = args[1]
|
53
|
-
column_name = args.first
|
54
|
-
else
|
55
|
-
# Handle legacy paramter options: def count(conditions=nil, joins=nil)
|
56
|
-
options.merge!(:conditions => args[0]) if args.length > 0
|
57
|
-
options.merge!(:joins => args[1]) if args.length > 1
|
58
|
-
end
|
59
|
-
else
|
60
|
-
raise(ArgumentError, "Unexpected parameters passed to count(*args): expected either count(conditions=nil, joins=nil) or count(options={})")
|
61
|
-
end
|
62
|
-
|
63
|
-
if options[:include] || scope(:find, :include)
|
64
|
-
count_with_associations(options)
|
65
|
-
else
|
66
|
-
calculate(:count, column_name, options)
|
67
|
-
end
|
45
|
+
calculate(:count, *construct_count_options_from_legacy_args(*args))
|
68
46
|
end
|
69
47
|
|
70
48
|
# Calculates average value on a given column. The value is returned as a float. See #calculate for examples with options.
|
@@ -136,44 +114,115 @@ module ActiveRecord
|
|
136
114
|
column_name = options[:select] if options[:select]
|
137
115
|
column_name = '*' if column_name == :all
|
138
116
|
column = column_for column_name
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
117
|
+
catch :invalid_query do
|
118
|
+
if options[:group]
|
119
|
+
return execute_grouped_calculation(operation, column_name, column, options)
|
120
|
+
else
|
121
|
+
return execute_simple_calculation(operation, column_name, column, options)
|
122
|
+
end
|
145
123
|
end
|
124
|
+
0
|
146
125
|
end
|
147
126
|
|
148
127
|
protected
|
149
|
-
def
|
150
|
-
|
151
|
-
|
128
|
+
def construct_count_options_from_legacy_args(*args)
|
129
|
+
options = {}
|
130
|
+
column_name = :all
|
131
|
+
|
132
|
+
# We need to handle
|
133
|
+
# count()
|
134
|
+
# count(options={})
|
135
|
+
# count(column_name=:all, options={})
|
136
|
+
# count(conditions=nil, joins=nil) # deprecated
|
137
|
+
if args.size > 2
|
138
|
+
raise ArgumentError, "Unexpected parameters passed to count(options={}): #{args.inspect}"
|
139
|
+
elsif args.size > 0
|
140
|
+
if args[0].is_a?(Hash)
|
141
|
+
options = args[0]
|
142
|
+
elsif args[1].is_a?(Hash)
|
143
|
+
column_name, options = args
|
144
|
+
else
|
145
|
+
# Deprecated count(conditions, joins=nil)
|
146
|
+
ActiveSupport::Deprecation.warn(
|
147
|
+
"You called count(#{args[0].inspect}, #{args[1].inspect}), which is a deprecated API call. " +
|
148
|
+
"Instead you should use count(column_name, options). Passing the conditions and joins as " +
|
149
|
+
"string parameters will be removed in Rails 2.0.", caller(2)
|
150
|
+
)
|
151
|
+
options.merge!(:conditions => args[0])
|
152
|
+
options.merge!(:joins => args[1]) if args[1]
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
[column_name, options]
|
157
|
+
end
|
158
|
+
|
159
|
+
def construct_calculation_sql(operation, column_name, options) #:nodoc:
|
160
|
+
operation = operation.to_s.downcase
|
161
|
+
options = options.symbolize_keys
|
162
|
+
|
163
|
+
scope = scope(:find)
|
164
|
+
merged_includes = merge_includes(scope ? scope[:include] : [], options[:include])
|
165
|
+
aggregate_alias = column_alias_for(operation, column_name)
|
166
|
+
use_workaround = !Base.connection.supports_count_distinct? && options[:distinct] && operation.to_s.downcase == 'count'
|
167
|
+
join_dependency = nil
|
168
|
+
|
169
|
+
if merged_includes.any? && operation.to_s.downcase == 'count'
|
170
|
+
options[:distinct] = true
|
171
|
+
column_name = options[:select] || [table_name, primary_key] * '.'
|
172
|
+
end
|
173
|
+
|
174
|
+
sql = "SELECT #{operation}(#{'DISTINCT ' if options[:distinct]}#{column_name}) AS #{aggregate_alias}"
|
175
|
+
|
176
|
+
# A (slower) workaround if we're using a backend, like sqlite, that doesn't support COUNT DISTINCT.
|
177
|
+
sql = "SELECT COUNT(*) AS #{aggregate_alias}" if use_workaround
|
178
|
+
|
152
179
|
sql << ", #{options[:group_field]} AS #{options[:group_alias]}" if options[:group]
|
180
|
+
sql << " FROM (SELECT DISTINCT #{column_name}" if use_workaround
|
153
181
|
sql << " FROM #{table_name} "
|
182
|
+
if merged_includes.any?
|
183
|
+
join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(self, merged_includes, options[:joins])
|
184
|
+
sql << join_dependency.join_associations.collect{|join| join.association_join }.join
|
185
|
+
end
|
154
186
|
add_joins!(sql, options, scope)
|
155
187
|
add_conditions!(sql, options[:conditions], scope)
|
156
|
-
sql
|
157
|
-
|
158
|
-
|
159
|
-
|
188
|
+
add_limited_ids_condition!(sql, options, join_dependency) if join_dependency && !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit])
|
189
|
+
|
190
|
+
if options[:group]
|
191
|
+
group_key = Base.connection.adapter_name == 'FrontBase' ? :group_alias : :group_field
|
192
|
+
sql << " GROUP BY #{options[group_key]} "
|
193
|
+
end
|
194
|
+
|
195
|
+
if options[:group] && options[:having]
|
196
|
+
# FrontBase requires identifiers in the HAVING clause and chokes on function calls
|
197
|
+
if Base.connection.adapter_name == 'FrontBase'
|
198
|
+
options[:having].downcase!
|
199
|
+
options[:having].gsub!(/#{operation}\s*\(\s*#{column_name}\s*\)/, aggregate_alias)
|
200
|
+
end
|
201
|
+
|
202
|
+
sql << " HAVING #{options[:having]} "
|
203
|
+
end
|
204
|
+
|
205
|
+
sql << " ORDER BY #{options[:order]} " if options[:order]
|
206
|
+
add_limit!(sql, options, scope)
|
207
|
+
sql << ')' if use_workaround
|
160
208
|
sql
|
161
209
|
end
|
162
210
|
|
163
|
-
def execute_simple_calculation(operation, column_name, column,
|
164
|
-
value
|
211
|
+
def execute_simple_calculation(operation, column_name, column, options) #:nodoc:
|
212
|
+
value = connection.select_value(construct_calculation_sql(operation, column_name, options))
|
165
213
|
type_cast_calculated_value(value, column, operation)
|
166
214
|
end
|
167
215
|
|
168
|
-
def execute_grouped_calculation(operation, column_name, column,
|
216
|
+
def execute_grouped_calculation(operation, column_name, column, options) #:nodoc:
|
169
217
|
group_attr = options[:group].to_s
|
170
218
|
association = reflect_on_association(group_attr.to_sym)
|
171
219
|
associated = association && association.macro == :belongs_to # only count belongs_to associations
|
172
220
|
group_field = (associated ? "#{options[:group]}_id" : options[:group]).to_s
|
173
221
|
group_alias = column_alias_for(group_field)
|
174
222
|
group_column = column_for group_field
|
175
|
-
sql = construct_calculation_sql(
|
223
|
+
sql = construct_calculation_sql(operation, column_name, options.merge(:group_field => group_field, :group_alias => group_alias))
|
176
224
|
calculated_data = connection.select_all(sql)
|
225
|
+
aggregate_alias = column_alias_for(operation, column_name)
|
177
226
|
|
178
227
|
if association
|
179
228
|
key_ids = calculated_data.collect { |row| row[group_alias] }
|
@@ -181,7 +230,7 @@ module ActiveRecord
|
|
181
230
|
key_records = key_records.inject({}) { |hsh, r| hsh.merge(r.id => r) }
|
182
231
|
end
|
183
232
|
|
184
|
-
calculated_data.inject(OrderedHash.new) do |all, row|
|
233
|
+
calculated_data.inject(ActiveSupport::OrderedHash.new) do |all, row|
|
185
234
|
key = associated ? key_records[row[group_alias].to_i] : type_cast_calculated_value(row[group_alias], group_column)
|
186
235
|
value = row[aggregate_alias]
|
187
236
|
all << [key, type_cast_calculated_value(value, column, operation)]
|
@@ -190,15 +239,7 @@ module ActiveRecord
|
|
190
239
|
|
191
240
|
private
|
192
241
|
def validate_calculation_options(operation, options = {})
|
193
|
-
|
194
|
-
options.assert_valid_keys(CALCULATIONS_OPTIONS + [:include])
|
195
|
-
else
|
196
|
-
options.assert_valid_keys(CALCULATIONS_OPTIONS)
|
197
|
-
end
|
198
|
-
end
|
199
|
-
|
200
|
-
def select_aggregate(operation, column_name, options)
|
201
|
-
"#{operation}(#{'DISTINCT ' if options[:distinct]}#{column_name})"
|
242
|
+
options.assert_valid_keys(CALCULATIONS_OPTIONS)
|
202
243
|
end
|
203
244
|
|
204
245
|
# converts a given key to the value that the database adapter returns as
|
@@ -208,7 +249,7 @@ module ActiveRecord
|
|
208
249
|
# count(distinct users.id) #=> count_distinct_users_id
|
209
250
|
# count(*) #=> count_all
|
210
251
|
def column_alias_for(*keys)
|
211
|
-
keys.join(' ').downcase.gsub(/\*/, 'all').gsub(/\W+/, ' ').strip.gsub(/ +/, '_')
|
252
|
+
connection.table_alias_for(keys.join(' ').downcase.gsub(/\*/, 'all').gsub(/\W+/, ' ').strip.gsub(/ +/, '_'))
|
212
253
|
end
|
213
254
|
|
214
255
|
def column_for(field)
|
@@ -158,6 +158,12 @@ module ActiveRecord
|
|
158
158
|
# after_initialize will only be run if an explicit implementation is defined (<tt>def after_find</tt>). In that case, all of the
|
159
159
|
# callback types will be called.
|
160
160
|
#
|
161
|
+
# == before_validation* returning statements
|
162
|
+
#
|
163
|
+
# If the returning value of a before_validation callback can be evaluated to false, the process will be aborted and Base#save will return false.
|
164
|
+
# If Base#save! is called it will raise a RecordNotSave error.
|
165
|
+
# Nothing will be appended to the errors object.
|
166
|
+
#
|
161
167
|
# == Cancelling callbacks
|
162
168
|
#
|
163
169
|
# If a before_* callback returns false, all the later callbacks and the associated action are cancelled. If an after_* callback returns
|
@@ -170,34 +176,17 @@ module ActiveRecord
|
|
170
176
|
after_validation_on_update before_destroy after_destroy
|
171
177
|
)
|
172
178
|
|
173
|
-
def self.
|
174
|
-
super
|
175
|
-
|
179
|
+
def self.included(base) #:nodoc:
|
176
180
|
base.extend(ClassMethods)
|
177
181
|
base.class_eval do
|
178
182
|
class << self
|
179
183
|
include Observable
|
180
|
-
|
181
|
-
alias_method :instantiate, :instantiate_with_callbacks
|
184
|
+
alias_method_chain :instantiate, :callbacks
|
182
185
|
end
|
183
186
|
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
alias_method :create_or_update_without_callbacks, :create_or_update
|
188
|
-
alias_method :create_or_update, :create_or_update_with_callbacks
|
189
|
-
|
190
|
-
alias_method :valid_without_callbacks, :valid?
|
191
|
-
alias_method :valid?, :valid_with_callbacks
|
192
|
-
|
193
|
-
alias_method :create_without_callbacks, :create
|
194
|
-
alias_method :create, :create_with_callbacks
|
195
|
-
|
196
|
-
alias_method :update_without_callbacks, :update
|
197
|
-
alias_method :update, :update_with_callbacks
|
198
|
-
|
199
|
-
alias_method :destroy_without_callbacks, :destroy
|
200
|
-
alias_method :destroy, :destroy_with_callbacks
|
187
|
+
[:initialize, :create_or_update, :valid?, :create, :update, :destroy].each do |method|
|
188
|
+
alias_method_chain method, :callbacks
|
189
|
+
end
|
201
190
|
end
|
202
191
|
|
203
192
|
CALLBACKS.each do |method|
|
@@ -302,12 +291,12 @@ module ActiveRecord
|
|
302
291
|
# existing objects that have a record.
|
303
292
|
def after_validation_on_update() end
|
304
293
|
|
305
|
-
def valid_with_callbacks #:nodoc:
|
294
|
+
def valid_with_callbacks? #:nodoc:
|
306
295
|
return false if callback(:before_validation) == false
|
307
296
|
if new_record? then result = callback(:before_validation_on_create) else result = callback(:before_validation_on_update) end
|
308
297
|
return false if result == false
|
309
298
|
|
310
|
-
result = valid_without_callbacks
|
299
|
+
result = valid_without_callbacks?
|
311
300
|
|
312
301
|
callback(:after_validation)
|
313
302
|
if new_record? then callback(:after_validation_on_create) else callback(:after_validation_on_update) end
|
@@ -86,6 +86,16 @@ module ActiveRecord
|
|
86
86
|
conn.disconnect!
|
87
87
|
end
|
88
88
|
end
|
89
|
+
|
90
|
+
# Clears the cache which maps classes
|
91
|
+
def clear_reloadable_connections!
|
92
|
+
@@active_connections.each do |name, conn|
|
93
|
+
if conn.requires_reloading?
|
94
|
+
conn.disconnect!
|
95
|
+
@@active_connections.delete(name)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
89
99
|
|
90
100
|
# Verify active connections.
|
91
101
|
def verify_active_connections! #:nodoc:
|
@@ -248,7 +258,8 @@ module ActiveRecord
|
|
248
258
|
if spec.kind_of?(ActiveRecord::ConnectionAdapters::AbstractAdapter)
|
249
259
|
active_connections[name] = spec
|
250
260
|
elsif spec.kind_of?(ConnectionSpecification)
|
251
|
-
|
261
|
+
config = spec.config.reverse_merge(:allow_concurrency => @@allow_concurrency)
|
262
|
+
self.connection = self.send(spec.adapter_method, config)
|
252
263
|
elsif spec.nil?
|
253
264
|
raise ConnectionNotEstablished
|
254
265
|
else
|
@@ -0,0 +1,21 @@
|
|
1
|
+
***************
|
2
|
+
*** 90,97 ****
|
3
|
+
# Clears the cache which maps classes
|
4
|
+
def clear_reloadable_connections!
|
5
|
+
@@active_connections.each do |name, conn|
|
6
|
+
- conn.disconnect! if conn.supports_reloading?
|
7
|
+
- @@active_connections.delete(name)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
--- 90,99 ----
|
12
|
+
# Clears the cache which maps classes
|
13
|
+
def clear_reloadable_connections!
|
14
|
+
@@active_connections.each do |name, conn|
|
15
|
+
+ if conn.requires_reloading?
|
16
|
+
+ conn.disconnect!
|
17
|
+
+ @@active_connections.delete(name)
|
18
|
+
+ end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
@@ -4,11 +4,14 @@ module ActiveRecord
|
|
4
4
|
# Returns an array of record hashes with the column names as keys and
|
5
5
|
# column values as values.
|
6
6
|
def select_all(sql, name = nil)
|
7
|
+
select(sql, name)
|
7
8
|
end
|
8
9
|
|
9
10
|
# Returns a record hash with the column names as keys and column values
|
10
11
|
# as values.
|
11
12
|
def select_one(sql, name = nil)
|
13
|
+
result = select(sql, name)
|
14
|
+
result.first if result
|
12
15
|
end
|
13
16
|
|
14
17
|
# Returns a single value from a record
|
@@ -25,19 +28,24 @@ module ActiveRecord
|
|
25
28
|
end
|
26
29
|
|
27
30
|
# Executes the SQL statement in the context of this connection.
|
28
|
-
# This abstract method raises a NotImplementedError.
|
29
31
|
def execute(sql, name = nil)
|
30
32
|
raise NotImplementedError, "execute is an abstract method"
|
31
33
|
end
|
32
34
|
|
33
35
|
# Returns the last auto-generated ID from the affected table.
|
34
|
-
def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
|
36
|
+
def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
|
37
|
+
raise NotImplementedError, "insert is an abstract method"
|
38
|
+
end
|
35
39
|
|
36
40
|
# Executes the update statement and returns the number of rows affected.
|
37
|
-
def update(sql, name = nil)
|
41
|
+
def update(sql, name = nil)
|
42
|
+
execute(sql, name)
|
43
|
+
end
|
38
44
|
|
39
45
|
# Executes the delete statement and returns the number of rows affected.
|
40
|
-
def delete(sql, name = nil)
|
46
|
+
def delete(sql, name = nil)
|
47
|
+
update(sql, name)
|
48
|
+
end
|
41
49
|
|
42
50
|
# Wrap a block in a transaction. Returns result of block.
|
43
51
|
def transaction(start_db_transaction = true)
|
@@ -91,6 +99,17 @@ module ActiveRecord
|
|
91
99
|
end
|
92
100
|
end
|
93
101
|
|
102
|
+
# Appends a locking clause to a SQL statement. *Modifies the +sql+ parameter*.
|
103
|
+
# # SELECT * FROM suppliers FOR UPDATE
|
104
|
+
# add_lock! 'SELECT * FROM suppliers', :lock => true
|
105
|
+
# add_lock! 'SELECT * FROM suppliers', :lock => ' FOR UPDATE'
|
106
|
+
def add_lock!(sql, options)
|
107
|
+
case lock = options[:lock]
|
108
|
+
when true: sql << ' FOR UPDATE'
|
109
|
+
when String: sql << " #{lock}"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
94
113
|
def default_sequence_name(table, column)
|
95
114
|
nil
|
96
115
|
end
|
@@ -99,6 +118,13 @@ module ActiveRecord
|
|
99
118
|
def reset_sequence!(table, column, sequence = nil)
|
100
119
|
# Do nothing by default. Implement for PostgreSQL, Oracle, ...
|
101
120
|
end
|
121
|
+
|
122
|
+
protected
|
123
|
+
# Returns an array of record hashes with the column names as keys and
|
124
|
+
# column values as values.
|
125
|
+
def select(sql, name = nil)
|
126
|
+
raise NotImplementedError, "select is an abstract method"
|
127
|
+
end
|
102
128
|
end
|
103
129
|
end
|
104
130
|
end
|
@@ -4,22 +4,29 @@ module ActiveRecord
|
|
4
4
|
# Quotes the column value to help prevent
|
5
5
|
# {SQL injection attacks}[http://en.wikipedia.org/wiki/SQL_injection].
|
6
6
|
def quote(value, column = nil)
|
7
|
+
# records are quoted as their primary key
|
8
|
+
return value.quoted_id if value.respond_to?(:quoted_id)
|
9
|
+
|
7
10
|
case value
|
8
|
-
when String
|
11
|
+
when String, ActiveSupport::Multibyte::Chars
|
12
|
+
value = value.to_s
|
9
13
|
if column && column.type == :binary && column.class.respond_to?(:string_to_binary)
|
10
14
|
"'#{quote_string(column.class.string_to_binary(value))}'" # ' (for ruby-mode)
|
11
|
-
elsif column && [:integer, :float].include?(column.type)
|
15
|
+
elsif column && [:integer, :float].include?(column.type)
|
16
|
+
value = column.type == :integer ? value.to_i : value.to_f
|
12
17
|
value.to_s
|
13
18
|
else
|
14
19
|
"'#{quote_string(value)}'" # ' (for ruby-mode)
|
15
20
|
end
|
16
|
-
when NilClass
|
17
|
-
when TrueClass
|
18
|
-
when FalseClass
|
19
|
-
when Float, Fixnum, Bignum
|
20
|
-
|
21
|
-
when
|
22
|
-
|
21
|
+
when NilClass then "NULL"
|
22
|
+
when TrueClass then (column && column.type == :integer ? '1' : quoted_true)
|
23
|
+
when FalseClass then (column && column.type == :integer ? '0' : quoted_false)
|
24
|
+
when Float, Fixnum, Bignum then value.to_s
|
25
|
+
# BigDecimals need to be output in a non-normalized form and quoted.
|
26
|
+
when BigDecimal then value.to_s('F')
|
27
|
+
when Date then "'#{value.to_s}'"
|
28
|
+
when Time, DateTime then "'#{quoted_date(value)}'"
|
29
|
+
else "'#{quote_string(value.to_yaml)}'"
|
23
30
|
end
|
24
31
|
end
|
25
32
|
|