activerecord 3.0.0.rc → 3.0.0.rc2
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 +6 -1
- data/README.rdoc +9 -9
- data/lib/active_record/aggregations.rb +64 -51
- data/lib/active_record/association_preload.rb +11 -9
- data/lib/active_record/associations.rb +300 -204
- data/lib/active_record/associations/association_collection.rb +7 -2
- data/lib/active_record/associations/belongs_to_association.rb +9 -5
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +7 -6
- data/lib/active_record/associations/has_many_association.rb +6 -6
- data/lib/active_record/associations/has_many_through_association.rb +4 -3
- data/lib/active_record/associations/has_one_association.rb +7 -7
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +2 -1
- data/lib/active_record/attribute_methods/write.rb +2 -2
- data/lib/active_record/autosave_association.rb +54 -72
- data/lib/active_record/base.rb +167 -108
- data/lib/active_record/callbacks.rb +43 -35
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +8 -11
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +0 -8
- data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +8 -6
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +5 -3
- data/lib/active_record/connection_adapters/mysql_adapter.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +9 -5
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +5 -5
- data/lib/active_record/dynamic_finder_match.rb +3 -3
- data/lib/active_record/dynamic_scope_match.rb +1 -1
- data/lib/active_record/errors.rb +9 -5
- data/lib/active_record/fixtures.rb +36 -22
- data/lib/active_record/locale/en.yml +2 -2
- data/lib/active_record/migration.rb +36 -36
- data/lib/active_record/named_scope.rb +23 -11
- data/lib/active_record/nested_attributes.rb +3 -3
- data/lib/active_record/observer.rb +3 -3
- data/lib/active_record/persistence.rb +44 -29
- data/lib/active_record/railtie.rb +5 -8
- data/lib/active_record/railties/databases.rake +1 -1
- data/lib/active_record/reflection.rb +52 -52
- data/lib/active_record/relation.rb +26 -19
- data/lib/active_record/relation/batches.rb +4 -4
- data/lib/active_record/relation/calculations.rb +58 -34
- data/lib/active_record/relation/finder_methods.rb +21 -12
- data/lib/active_record/relation/query_methods.rb +26 -31
- data/lib/active_record/relation/spawn_methods.rb +17 -5
- data/lib/active_record/schema.rb +1 -1
- data/lib/active_record/schema_dumper.rb +12 -12
- data/lib/active_record/serialization.rb +1 -1
- data/lib/active_record/serializers/xml_serializer.rb +1 -1
- data/lib/active_record/session_store.rb +9 -9
- data/lib/active_record/test_case.rb +2 -2
- data/lib/active_record/timestamp.rb +31 -32
- data/lib/active_record/validations/associated.rb +4 -3
- data/lib/active_record/validations/uniqueness.rb +15 -11
- data/lib/active_record/version.rb +1 -1
- metadata +17 -16
@@ -21,23 +21,28 @@ module ActiveRecord
|
|
21
21
|
#
|
22
22
|
# ==== Parameters
|
23
23
|
#
|
24
|
-
# * <tt>:conditions</tt> - An SQL fragment like "administrator = 1", <tt>[ "user_name = ?", username ]</tt>,
|
24
|
+
# * <tt>:conditions</tt> - An SQL fragment like "administrator = 1", <tt>[ "user_name = ?", username ]</tt>,
|
25
|
+
# or <tt>["user_name = :user_name", { :user_name => user_name }]</tt>. See conditions in the intro.
|
25
26
|
# * <tt>:order</tt> - An SQL fragment like "created_at DESC, name".
|
26
27
|
# * <tt>:group</tt> - An attribute name by which the result should be grouped. Uses the <tt>GROUP BY</tt> SQL-clause.
|
27
|
-
# * <tt>:having</tt> - Combined with +:group+ this can be used to filter the records that a
|
28
|
+
# * <tt>:having</tt> - Combined with +:group+ this can be used to filter the records that a
|
29
|
+
# <tt>GROUP BY</tt> returns. Uses the <tt>HAVING</tt> SQL-clause.
|
28
30
|
# * <tt>:limit</tt> - An integer determining the limit on the number of rows that should be returned.
|
29
|
-
# * <tt>:offset</tt> - An integer determining the offset from where the rows should be fetched. So at 5,
|
31
|
+
# * <tt>:offset</tt> - An integer determining the offset from where the rows should be fetched. So at 5,
|
32
|
+
# it would skip rows 0 through 4.
|
30
33
|
# * <tt>:joins</tt> - Either an SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id" (rarely needed),
|
31
|
-
# named associations in the same form used for the <tt>:include</tt> option, which will perform an
|
34
|
+
# named associations in the same form used for the <tt>:include</tt> option, which will perform an
|
35
|
+
# <tt>INNER JOIN</tt> on the associated table(s),
|
32
36
|
# or an array containing a mixture of both strings and named associations.
|
33
|
-
# If the value is a string, then the records will be returned read-only since they will
|
37
|
+
# If the value is a string, then the records will be returned read-only since they will
|
38
|
+
# have attributes that do not correspond to the table's columns.
|
34
39
|
# Pass <tt>:readonly => false</tt> to override.
|
35
40
|
# * <tt>:include</tt> - Names associations that should be loaded alongside. The symbols named refer
|
36
41
|
# to already defined associations. See eager loading under Associations.
|
37
|
-
# * <tt>:select</tt> - By default, this is "*" as in "SELECT * FROM", but can be changed if you,
|
38
|
-
# include the joined columns. Takes a string with the SELECT SQL fragment (e.g. "id, name").
|
39
|
-
# * <tt>:from</tt> - By default, this is the table name of the class, but can be changed
|
40
|
-
# of a database view).
|
42
|
+
# * <tt>:select</tt> - By default, this is "*" as in "SELECT * FROM", but can be changed if you,
|
43
|
+
# for example, want to do a join but not include the joined columns. Takes a string with the SELECT SQL fragment (e.g. "id, name").
|
44
|
+
# * <tt>:from</tt> - By default, this is the table name of the class, but can be changed
|
45
|
+
# to an alternate table name (or even the name of a database view).
|
41
46
|
# * <tt>:readonly</tt> - Mark the returned records read-only so they cannot be saved or updated.
|
42
47
|
# * <tt>:lock</tt> - An SQL fragment like "FOR UPDATE" or "LOCK IN SHARE MODE".
|
43
48
|
# <tt>:lock => true</tt> gives connection's default exclusive lock, usually "FOR UPDATE".
|
@@ -164,6 +169,8 @@ module ActiveRecord
|
|
164
169
|
# Person.exists?(['name LIKE ?', "%#{query}%"])
|
165
170
|
# Person.exists?
|
166
171
|
def exists?(id = nil)
|
172
|
+
id = id.id if ActiveRecord::Base === id
|
173
|
+
|
167
174
|
case id
|
168
175
|
when Array, Hash
|
169
176
|
where(id).exists?
|
@@ -279,10 +286,12 @@ module ActiveRecord
|
|
279
286
|
end
|
280
287
|
|
281
288
|
def find_one(id)
|
289
|
+
id = id.id if ActiveRecord::Base === id
|
290
|
+
|
282
291
|
record = where(primary_key.eq(id)).first
|
283
292
|
|
284
293
|
unless record
|
285
|
-
conditions = arel.
|
294
|
+
conditions = arel.wheres.map { |x| x.value }.join(', ')
|
286
295
|
conditions = " [WHERE #{conditions}]" if conditions.present?
|
287
296
|
raise RecordNotFound, "Couldn't find #{@klass.name} with ID=#{id}#{conditions}"
|
288
297
|
end
|
@@ -308,7 +317,7 @@ module ActiveRecord
|
|
308
317
|
if result.size == expected_size
|
309
318
|
result
|
310
319
|
else
|
311
|
-
conditions = arel.
|
320
|
+
conditions = arel.wheres.map { |x| x.value }.join(', ')
|
312
321
|
conditions = " [WHERE #{conditions}]" if conditions.present?
|
313
322
|
|
314
323
|
error = "Couldn't find all #{@klass.name.pluralize} with IDs "
|
@@ -339,7 +348,7 @@ module ActiveRecord
|
|
339
348
|
end
|
340
349
|
|
341
350
|
def using_limitable_reflections?(reflections)
|
342
|
-
reflections.none?
|
351
|
+
reflections.none? { |r| r.collection? }
|
343
352
|
end
|
344
353
|
|
345
354
|
end
|
@@ -11,7 +11,7 @@ module ActiveRecord
|
|
11
11
|
|
12
12
|
def includes(*args)
|
13
13
|
args.reject! { |a| a.blank? }
|
14
|
-
clone.tap {|r| r.includes_values
|
14
|
+
clone.tap {|r| r.includes_values = (r.includes_values + args).flatten.uniq if args.present? }
|
15
15
|
end
|
16
16
|
|
17
17
|
def eager_load(*args)
|
@@ -31,7 +31,7 @@ module ActiveRecord
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def group(*args)
|
34
|
-
clone.tap {|r| r.group_values += args if args.present? }
|
34
|
+
clone.tap {|r| r.group_values += args.flatten if args.present? }
|
35
35
|
end
|
36
36
|
|
37
37
|
def order(*args)
|
@@ -47,9 +47,11 @@ module ActiveRecord
|
|
47
47
|
clone.tap {|r| r.joins_values += args if args.present? }
|
48
48
|
end
|
49
49
|
|
50
|
-
def where(*
|
51
|
-
value = build_where(
|
52
|
-
|
50
|
+
def where(opts, *rest)
|
51
|
+
value = build_where(opts, rest)
|
52
|
+
copy = clone
|
53
|
+
copy.where_values += Array.wrap(value) if value
|
54
|
+
copy
|
53
55
|
end
|
54
56
|
|
55
57
|
def having(*args)
|
@@ -58,7 +60,9 @@ module ActiveRecord
|
|
58
60
|
end
|
59
61
|
|
60
62
|
def limit(value = true)
|
61
|
-
|
63
|
+
copy = clone
|
64
|
+
copy.limit_value = value
|
65
|
+
copy
|
62
66
|
end
|
63
67
|
|
64
68
|
def offset(value = true)
|
@@ -117,8 +121,10 @@ module ActiveRecord
|
|
117
121
|
when Hash, Array, Symbol
|
118
122
|
if array_of_strings?(join)
|
119
123
|
join_string = join.join(' ')
|
120
|
-
arel = arel.join(join_string)
|
124
|
+
arel = arel.join(Arel::SqlLiteral.new(join_string))
|
121
125
|
end
|
126
|
+
when String
|
127
|
+
arel = arel.join(Arel::SqlLiteral.new(join))
|
122
128
|
else
|
123
129
|
arel = arel.join(join)
|
124
130
|
end
|
@@ -129,11 +135,9 @@ module ActiveRecord
|
|
129
135
|
def build_arel
|
130
136
|
arel = table
|
131
137
|
|
132
|
-
arel = build_joins(arel, @joins_values)
|
133
|
-
|
134
|
-
@where_values.uniq.each do |where|
|
135
|
-
next if where.blank?
|
138
|
+
arel = build_joins(arel, @joins_values) unless @joins_values.empty?
|
136
139
|
|
140
|
+
(@where_values - ['']).uniq.each do |where|
|
137
141
|
case where
|
138
142
|
when Arel::SqlLiteral
|
139
143
|
arel = arel.where(where)
|
@@ -143,36 +147,27 @@ module ActiveRecord
|
|
143
147
|
end
|
144
148
|
end
|
145
149
|
|
146
|
-
arel = arel.having(*@having_values.uniq.select{|h| h.present?})
|
150
|
+
arel = arel.having(*@having_values.uniq.select{|h| h.present?}) unless @having_values.empty?
|
147
151
|
|
148
|
-
arel = arel.take(@limit_value) if @limit_value
|
149
|
-
arel = arel.skip(@offset_value) if @offset_value
|
152
|
+
arel = arel.take(@limit_value) if @limit_value
|
153
|
+
arel = arel.skip(@offset_value) if @offset_value
|
150
154
|
|
151
|
-
arel = arel.group(*@group_values.uniq.select{|g| g.present?})
|
155
|
+
arel = arel.group(*@group_values.uniq.select{|g| g.present?}) unless @group_values.empty?
|
152
156
|
|
153
|
-
arel = arel.order(*@order_values.uniq.select{|o| o.present?})
|
157
|
+
arel = arel.order(*@order_values.uniq.select{|o| o.present?}) unless @order_values.empty?
|
154
158
|
|
155
159
|
arel = build_select(arel, @select_values.uniq)
|
156
160
|
|
157
|
-
arel = arel.from(@from_value) if @from_value
|
158
|
-
|
159
|
-
case @lock_value
|
160
|
-
when TrueClass
|
161
|
-
arel = arel.lock
|
162
|
-
when String
|
163
|
-
arel = arel.lock(@lock_value)
|
164
|
-
end if @lock_value.present?
|
161
|
+
arel = arel.from(@from_value) if @from_value
|
162
|
+
arel = arel.lock(@lock_value) if @lock_value
|
165
163
|
|
166
164
|
arel
|
167
165
|
end
|
168
166
|
|
169
|
-
def build_where(
|
170
|
-
return if args.blank?
|
171
|
-
|
172
|
-
opts = args.first
|
167
|
+
def build_where(opts, other = [])
|
173
168
|
case opts
|
174
169
|
when String, Array
|
175
|
-
@klass.send(:sanitize_sql,
|
170
|
+
@klass.send(:sanitize_sql, other.empty? ? opts : ([opts] + other))
|
176
171
|
when Hash
|
177
172
|
attributes = @klass.send(:expand_hash_conditions_for_aggregates, opts)
|
178
173
|
PredicateBuilder.new(table.engine).build_from_hash(attributes, table)
|
@@ -226,7 +221,7 @@ module ActiveRecord
|
|
226
221
|
end
|
227
222
|
|
228
223
|
def build_select(arel, selects)
|
229
|
-
|
224
|
+
unless selects.empty?
|
230
225
|
@implicit_readonly = false
|
231
226
|
# TODO: fix this ugly hack, we should refactor the callers to get an ARel compatible array.
|
232
227
|
# Before this change we were passing to ARel the last element only, and ARel is capable of handling an array
|
@@ -236,7 +231,7 @@ module ActiveRecord
|
|
236
231
|
arel.project(selects.last)
|
237
232
|
end
|
238
233
|
else
|
239
|
-
arel.project(@klass.quoted_table_name + '.*')
|
234
|
+
arel.project(Arel::SqlLiteral.new(@klass.quoted_table_name + '.*'))
|
240
235
|
end
|
241
236
|
end
|
242
237
|
|
@@ -8,7 +8,13 @@ module ActiveRecord
|
|
8
8
|
|
9
9
|
((Relation::ASSOCIATION_METHODS + Relation::MULTI_VALUE_METHODS) - [:joins, :where]).each do |method|
|
10
10
|
value = r.send(:"#{method}_values")
|
11
|
-
|
11
|
+
if value.present?
|
12
|
+
if method == :includes
|
13
|
+
merged_relation = merged_relation.includes(value)
|
14
|
+
else
|
15
|
+
merged_relation.send(:"#{method}_values=", value)
|
16
|
+
end
|
17
|
+
end
|
12
18
|
end
|
13
19
|
|
14
20
|
merged_relation = merged_relation.joins(r.joins_values)
|
@@ -16,8 +22,10 @@ module ActiveRecord
|
|
16
22
|
merged_wheres = @where_values
|
17
23
|
|
18
24
|
r.where_values.each do |w|
|
19
|
-
if w.
|
20
|
-
merged_wheres = merged_wheres.reject {|p|
|
25
|
+
if w.respond_to?(:operator) && w.operator == :==
|
26
|
+
merged_wheres = merged_wheres.reject { |p|
|
27
|
+
p.respond_to?(:operator) && p.operator == :== && p.operand1.name == w.operand1.name
|
28
|
+
}
|
21
29
|
end
|
22
30
|
|
23
31
|
merged_wheres += [w]
|
@@ -80,10 +88,14 @@ module ActiveRecord
|
|
80
88
|
|
81
89
|
options.assert_valid_keys(VALID_FIND_OPTIONS)
|
82
90
|
|
83
|
-
[:joins, :select, :group, :having, :limit, :offset, :from, :lock
|
84
|
-
|
91
|
+
[:joins, :select, :group, :having, :limit, :offset, :from, :lock].each do |finder|
|
92
|
+
if value = options[finder]
|
93
|
+
relation = relation.send(finder, value)
|
94
|
+
end
|
85
95
|
end
|
86
96
|
|
97
|
+
relation = relation.readonly(options[:readonly]) if options.key? :readonly
|
98
|
+
|
87
99
|
# Give precedence to newly-applied orders and groups to play nicely with with_scope
|
88
100
|
[:group, :order].each do |finder|
|
89
101
|
relation.send("#{finder}_values=", Array.wrap(options[finder]) + relation.send("#{finder}_values")) if options.has_key?(finder)
|
data/lib/active_record/schema.rb
CHANGED
@@ -2,7 +2,7 @@ require 'active_support/core_ext/object/blank'
|
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
# = Active Record Schema
|
5
|
-
#
|
5
|
+
#
|
6
6
|
# Allows programmers to programmatically define a schema in a portable
|
7
7
|
# DSL. This means you can define tables, indexes, etc. without using SQL
|
8
8
|
# directly, so your applications can more easily support multiple
|
@@ -8,13 +8,13 @@ module ActiveRecord
|
|
8
8
|
# output format (i.e., ActiveRecord::Schema).
|
9
9
|
class SchemaDumper #:nodoc:
|
10
10
|
private_class_method :new
|
11
|
-
|
11
|
+
|
12
12
|
##
|
13
13
|
# :singleton-method:
|
14
|
-
# A list of tables which should not be dumped to the schema.
|
14
|
+
# A list of tables which should not be dumped to the schema.
|
15
15
|
# Acceptable values are strings as well as regexp.
|
16
16
|
# This setting is only used if ActiveRecord::Base.schema_format == :ruby
|
17
|
-
cattr_accessor :ignore_tables
|
17
|
+
cattr_accessor :ignore_tables
|
18
18
|
@@ignore_tables = []
|
19
19
|
|
20
20
|
def self.dump(connection=ActiveRecord::Base.connection, stream=STDOUT)
|
@@ -41,11 +41,11 @@ module ActiveRecord
|
|
41
41
|
define_params = @version ? ":version => #{@version}" : ""
|
42
42
|
|
43
43
|
stream.puts <<HEADER
|
44
|
-
# This file is auto-generated from the current state of the database. Instead
|
44
|
+
# This file is auto-generated from the current state of the database. Instead
|
45
45
|
# of editing this file, please use the migrations feature of Active Record to
|
46
46
|
# incrementally modify your database, and then regenerate this schema definition.
|
47
47
|
#
|
48
|
-
# Note that this schema.rb definition is the authoritative source for your
|
48
|
+
# Note that this schema.rb definition is the authoritative source for your
|
49
49
|
# database schema. If you need to create the application database on another
|
50
50
|
# system, you should be using db:schema:load, not running all the migrations
|
51
51
|
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
|
@@ -71,7 +71,7 @@ HEADER
|
|
71
71
|
else
|
72
72
|
raise StandardError, 'ActiveRecord::SchemaDumper.ignore_tables accepts an array of String and / or Regexp values.'
|
73
73
|
end
|
74
|
-
end
|
74
|
+
end
|
75
75
|
table(tbl, stream)
|
76
76
|
end
|
77
77
|
end
|
@@ -87,7 +87,7 @@ HEADER
|
|
87
87
|
elsif @connection.respond_to?(:primary_key)
|
88
88
|
pk = @connection.primary_key(table)
|
89
89
|
end
|
90
|
-
|
90
|
+
|
91
91
|
tbl.print " create_table #{table.inspect}"
|
92
92
|
if columns.detect { |c| c.name == pk }
|
93
93
|
if pk != 'id'
|
@@ -105,7 +105,7 @@ HEADER
|
|
105
105
|
next if column.name == pk
|
106
106
|
spec = {}
|
107
107
|
spec[:name] = column.name.inspect
|
108
|
-
|
108
|
+
|
109
109
|
# AR has an optimisation which handles zero-scale decimals as integers. This
|
110
110
|
# code ensures that the dumper still dumps the column as a decimal.
|
111
111
|
spec[:type] = if column.type == :integer && [/^numeric/, /^decimal/].any? { |e| e.match(column.sql_type) }
|
@@ -123,7 +123,7 @@ HEADER
|
|
123
123
|
end.compact
|
124
124
|
|
125
125
|
# find all migration keys used in this table
|
126
|
-
keys = [:name, :limit, :precision, :scale, :default, :null] & column_specs.map
|
126
|
+
keys = [:name, :limit, :precision, :scale, :default, :null] & column_specs.map{ |k| k.keys }.flatten
|
127
127
|
|
128
128
|
# figure out the lengths for each column based on above keys
|
129
129
|
lengths = keys.map{ |key| column_specs.map{ |spec| spec[key] ? spec[key].length + 2 : 0 }.max }
|
@@ -148,7 +148,7 @@ HEADER
|
|
148
148
|
|
149
149
|
tbl.puts " end"
|
150
150
|
tbl.puts
|
151
|
-
|
151
|
+
|
152
152
|
indexes(table, tbl)
|
153
153
|
|
154
154
|
tbl.rewind
|
@@ -158,7 +158,7 @@ HEADER
|
|
158
158
|
stream.puts "# #{e.message}"
|
159
159
|
stream.puts
|
160
160
|
end
|
161
|
-
|
161
|
+
|
162
162
|
stream
|
163
163
|
end
|
164
164
|
|
@@ -172,7 +172,7 @@ HEADER
|
|
172
172
|
value.inspect
|
173
173
|
end
|
174
174
|
end
|
175
|
-
|
175
|
+
|
176
176
|
def indexes(table, stream)
|
177
177
|
if (indexes = @connection.indexes(table)).any?
|
178
178
|
add_index_statements = indexes.map do |index|
|
@@ -23,7 +23,7 @@ module ActiveRecord #:nodoc:
|
|
23
23
|
|
24
24
|
private
|
25
25
|
# Add associations specified via the <tt>:includes</tt> option.
|
26
|
-
#
|
26
|
+
#
|
27
27
|
# Expects a block that takes as arguments:
|
28
28
|
# +association+ - name of the association
|
29
29
|
# +records+ - the association record(s) to be serialized
|
@@ -80,7 +80,7 @@ module ActiveRecord #:nodoc:
|
|
80
80
|
# closure created by a Proc, to_xml can be used to add elements that normally fall
|
81
81
|
# outside of the scope of the model -- for example, generating and appending URLs
|
82
82
|
# associated with models.
|
83
|
-
#
|
83
|
+
#
|
84
84
|
# proc = Proc.new { |options, record| options[:builder].tag!('name-reverse', record.name.reverse) }
|
85
85
|
# firm.to_xml :procs => [ proc ]
|
86
86
|
#
|
@@ -9,7 +9,7 @@ module ActiveRecord
|
|
9
9
|
# +id+ (numeric primary key),
|
10
10
|
# +session_id+ (text, or longtext if your session data exceeds 65K), and
|
11
11
|
# +data+ (text or longtext; careful if your session data exceeds 65KB).
|
12
|
-
#
|
12
|
+
#
|
13
13
|
# The +session_id+ column should always be indexed for speedy lookups.
|
14
14
|
# Session data is marshaled to the +data+ column in Base64 format.
|
15
15
|
# If the data you write is larger than the column's size limit,
|
@@ -59,17 +59,15 @@ module ActiveRecord
|
|
59
59
|
end
|
60
60
|
|
61
61
|
def drop_table!
|
62
|
-
connection.
|
62
|
+
connection.drop_table table_name
|
63
63
|
end
|
64
64
|
|
65
65
|
def create_table!
|
66
|
-
connection.
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
)
|
72
|
-
end_sql
|
66
|
+
connection.create_table(table_name) do |t|
|
67
|
+
t.string session_id_column, :limit => 255
|
68
|
+
t.text data_column_name
|
69
|
+
end
|
70
|
+
connection.add_index table_name, session_id_column, :unique => true
|
73
71
|
end
|
74
72
|
end
|
75
73
|
|
@@ -205,6 +203,7 @@ module ActiveRecord
|
|
205
203
|
class << self
|
206
204
|
alias :data_column_name :data_column
|
207
205
|
|
206
|
+
remove_method :connection
|
208
207
|
def connection
|
209
208
|
@@connection ||= ActiveRecord::Base.connection
|
210
209
|
end
|
@@ -293,6 +292,7 @@ module ActiveRecord
|
|
293
292
|
private
|
294
293
|
def get_session(env, sid)
|
295
294
|
Base.silence do
|
295
|
+
sid ||= generate_sid
|
296
296
|
session = find_session(sid)
|
297
297
|
env[SESSION_RECORD_KEY] = session
|
298
298
|
[sid, session.data]
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
# = Active Record Test Case
|
3
|
-
#
|
3
|
+
#
|
4
4
|
# Defines some test assertions to test against SQL queries.
|
5
5
|
class TestCase < ActiveSupport::TestCase #:nodoc:
|
6
6
|
def assert_date_from_db(expected, actual, message = nil)
|
@@ -21,7 +21,7 @@ module ActiveRecord
|
|
21
21
|
patterns_to_match.each do |pattern|
|
22
22
|
failed_patterns << pattern unless $queries_executed.any?{ |sql| pattern === sql }
|
23
23
|
end
|
24
|
-
assert failed_patterns.empty?, "Query pattern(s) #{failed_patterns.map
|
24
|
+
assert failed_patterns.empty?, "Query pattern(s) #{failed_patterns.map{ |p| p.inspect }.join(', ')} not found.#{$queries_executed.size == 0 ? '' : "\nQueries:\n#{$queries_executed.join("\n")}"}"
|
25
25
|
end
|
26
26
|
|
27
27
|
def assert_queries(num = 1)
|
@@ -1,8 +1,8 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
# = Active Record Timestamp
|
3
|
-
#
|
3
|
+
#
|
4
4
|
# Active Record automatically timestamps create and update operations if the
|
5
|
-
# table has fields named <tt>created_at/created_on</tt> or
|
5
|
+
# table has fields named <tt>created_at/created_on</tt> or
|
6
6
|
# <tt>updated_at/updated_on</tt>.
|
7
7
|
#
|
8
8
|
# Timestamping can be turned off by setting:
|
@@ -12,6 +12,19 @@ module ActiveRecord
|
|
12
12
|
# Timestamps are in the local timezone by default but you can use UTC by setting:
|
13
13
|
#
|
14
14
|
# <tt>ActiveRecord::Base.default_timezone = :utc</tt>
|
15
|
+
#
|
16
|
+
# == Time Zone aware attributes
|
17
|
+
#
|
18
|
+
# By default, ActiveRecord::Base keeps all the datetime columns time zone aware by executing following code.
|
19
|
+
#
|
20
|
+
# ActiveRecord::Base.time_zone_aware_attributes = true
|
21
|
+
#
|
22
|
+
# This feature can easily be turned off by assigning value <tt>false</tt> .
|
23
|
+
#
|
24
|
+
# If your attributes are time zone aware and you desire to skip time zone conversion for certain
|
25
|
+
# attributes then you can do following:
|
26
|
+
#
|
27
|
+
# Topic.skip_time_zone_conversion_for_attributes = [:written_on]
|
15
28
|
module Timestamp
|
16
29
|
extend ActiveSupport::Concern
|
17
30
|
|
@@ -19,19 +32,6 @@ module ActiveRecord
|
|
19
32
|
class_inheritable_accessor :record_timestamps, :instance_writer => false
|
20
33
|
self.record_timestamps = true
|
21
34
|
end
|
22
|
-
|
23
|
-
# Saves the record with the updated_at/on attributes set to the current time.
|
24
|
-
# Please note that no validation is performed and no callbacks are executed.
|
25
|
-
# If an attribute name is passed, that attribute is updated along with
|
26
|
-
# updated_at/on attributes.
|
27
|
-
#
|
28
|
-
# Examples:
|
29
|
-
#
|
30
|
-
# product.touch # updates updated_at/on
|
31
|
-
# product.touch(:designed_at) # updates the designed_at attribute and updated_at/on
|
32
|
-
def touch(attribute = nil)
|
33
|
-
update_attribute(attribute, current_time_from_proper_timezone)
|
34
|
-
end
|
35
35
|
|
36
36
|
private
|
37
37
|
|
@@ -39,34 +39,33 @@ module ActiveRecord
|
|
39
39
|
if record_timestamps
|
40
40
|
current_time = current_time_from_proper_timezone
|
41
41
|
|
42
|
-
|
42
|
+
all_timestamp_attributes.each do |column|
|
43
43
|
write_attribute(column.to_s, current_time) if respond_to?(column) && self.send(column).nil?
|
44
44
|
end
|
45
|
-
|
46
|
-
timestamp_attributes_for_update_in_model.each do |column|
|
47
|
-
write_attribute(column.to_s, current_time) if self.send(column).nil?
|
48
|
-
end
|
49
45
|
end
|
50
46
|
|
51
47
|
super
|
52
48
|
end
|
53
49
|
|
54
50
|
def update(*args) #:nodoc:
|
55
|
-
|
51
|
+
if should_record_timestamps?
|
52
|
+
current_time = current_time_from_proper_timezone
|
53
|
+
|
54
|
+
timestamp_attributes_for_update_in_model.each do |column|
|
55
|
+
column = column.to_s
|
56
|
+
next if attribute_changed?(column)
|
57
|
+
write_attribute(column, current_time)
|
58
|
+
end
|
59
|
+
end
|
56
60
|
super
|
57
61
|
end
|
58
62
|
|
59
|
-
def
|
60
|
-
|
61
|
-
current_time = current_time_from_proper_timezone
|
62
|
-
timestamp_attributes_for_update_in_model.inject({}) do |hash, column|
|
63
|
-
hash[column.to_s] = write_attribute(column.to_s, current_time)
|
64
|
-
hash
|
65
|
-
end
|
63
|
+
def should_record_timestamps?
|
64
|
+
record_timestamps && !partial_updates? || changed?
|
66
65
|
end
|
67
66
|
|
68
|
-
def timestamp_attributes_for_update_in_model
|
69
|
-
timestamp_attributes_for_update.select { |
|
67
|
+
def timestamp_attributes_for_update_in_model
|
68
|
+
timestamp_attributes_for_update.select { |c| respond_to?(c) }
|
70
69
|
end
|
71
70
|
|
72
71
|
def timestamp_attributes_for_update #:nodoc:
|
@@ -78,9 +77,9 @@ module ActiveRecord
|
|
78
77
|
end
|
79
78
|
|
80
79
|
def all_timestamp_attributes #:nodoc:
|
81
|
-
|
80
|
+
timestamp_attributes_for_create + timestamp_attributes_for_update
|
82
81
|
end
|
83
|
-
|
82
|
+
|
84
83
|
def current_time_from_proper_timezone #:nodoc:
|
85
84
|
self.class.default_timezone == :utc ? Time.now.utc : Time.now
|
86
85
|
end
|