activerecord 3.0.0.beta4 → 3.0.0.rc
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 +267 -254
- data/README.rdoc +222 -0
- data/examples/performance.rb +9 -9
- data/lib/active_record/aggregations.rb +3 -4
- data/lib/active_record/association_preload.rb +15 -10
- data/lib/active_record/associations.rb +54 -37
- data/lib/active_record/associations/association_collection.rb +43 -17
- data/lib/active_record/associations/association_proxy.rb +2 -0
- data/lib/active_record/associations/belongs_to_association.rb +1 -0
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +22 -7
- data/lib/active_record/associations/has_many_association.rb +6 -1
- data/lib/active_record/associations/has_many_through_association.rb +1 -0
- data/lib/active_record/associations/has_one_association.rb +1 -0
- data/lib/active_record/associations/has_one_through_association.rb +1 -0
- data/lib/active_record/associations/through_association_scope.rb +3 -2
- data/lib/active_record/attribute_methods.rb +1 -0
- data/lib/active_record/autosave_association.rb +4 -6
- data/lib/active_record/base.rb +106 -240
- data/lib/active_record/callbacks.rb +4 -25
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +22 -29
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +2 -8
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +10 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +56 -7
- data/lib/active_record/connection_adapters/abstract_adapter.rb +10 -18
- data/lib/active_record/connection_adapters/mysql_adapter.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +65 -69
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +19 -6
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +20 -46
- data/lib/active_record/counter_cache.rb +14 -4
- data/lib/active_record/dynamic_finder_match.rb +9 -0
- data/lib/active_record/dynamic_scope_match.rb +7 -0
- data/lib/active_record/errors.rb +3 -0
- data/lib/active_record/fixtures.rb +5 -6
- data/lib/active_record/locale/en.yml +1 -1
- data/lib/active_record/locking/optimistic.rb +1 -0
- data/lib/active_record/log_subscriber.rb +48 -0
- data/lib/active_record/migration.rb +64 -37
- data/lib/active_record/named_scope.rb +33 -19
- data/lib/active_record/nested_attributes.rb +17 -13
- data/lib/active_record/observer.rb +13 -6
- data/lib/active_record/persistence.rb +55 -22
- data/lib/active_record/query_cache.rb +1 -0
- data/lib/active_record/railtie.rb +14 -8
- data/lib/active_record/railties/controller_runtime.rb +2 -2
- data/lib/active_record/railties/databases.rake +63 -33
- data/lib/active_record/reflection.rb +46 -28
- data/lib/active_record/relation.rb +38 -24
- data/lib/active_record/relation/finder_methods.rb +5 -5
- data/lib/active_record/relation/predicate_builder.rb +2 -4
- data/lib/active_record/relation/query_methods.rb +134 -115
- data/lib/active_record/relation/spawn_methods.rb +1 -1
- data/lib/active_record/schema.rb +2 -0
- data/lib/active_record/schema_dumper.rb +15 -12
- data/lib/active_record/serialization.rb +2 -0
- data/lib/active_record/session_store.rb +93 -79
- data/lib/active_record/test_case.rb +3 -0
- data/lib/active_record/timestamp.rb +49 -29
- data/lib/active_record/transactions.rb +5 -2
- data/lib/active_record/validations.rb +5 -2
- data/lib/active_record/validations/associated.rb +1 -1
- data/lib/active_record/validations/uniqueness.rb +1 -1
- data/lib/active_record/version.rb +1 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb +12 -6
- data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
- metadata +27 -14
- data/README +0 -351
- data/lib/active_record/railties/log_subscriber.rb +0 -32
@@ -6,7 +6,7 @@ module ActiveRecord
|
|
6
6
|
merged_relation = clone
|
7
7
|
return merged_relation unless r
|
8
8
|
|
9
|
-
(Relation::ASSOCIATION_METHODS + Relation::MULTI_VALUE_METHODS)
|
9
|
+
((Relation::ASSOCIATION_METHODS + Relation::MULTI_VALUE_METHODS) - [:joins, :where]).each do |method|
|
10
10
|
value = r.send(:"#{method}_values")
|
11
11
|
merged_relation.send(:"#{method}_values=", value) if value.present?
|
12
12
|
end
|
data/lib/active_record/schema.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'active_support/core_ext/object/blank'
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
|
+
# = Active Record Schema
|
5
|
+
#
|
4
6
|
# Allows programmers to programmatically define a schema in a portable
|
5
7
|
# DSL. This means you can define tables, indexes, etc. without using SQL
|
6
8
|
# directly, so your applications can more easily support multiple
|
@@ -2,6 +2,8 @@ require 'stringio'
|
|
2
2
|
require 'active_support/core_ext/big_decimal'
|
3
3
|
|
4
4
|
module ActiveRecord
|
5
|
+
# = Active Record Schema Dumper
|
6
|
+
#
|
5
7
|
# This class is used to dump the database schema for some connection to some
|
6
8
|
# output format (i.e., ActiveRecord::Schema).
|
7
9
|
class SchemaDumper #:nodoc:
|
@@ -39,13 +41,14 @@ module ActiveRecord
|
|
39
41
|
define_params = @version ? ":version => #{@version}" : ""
|
40
42
|
|
41
43
|
stream.puts <<HEADER
|
42
|
-
# This file is auto-generated from the current state of the database. Instead
|
43
|
-
# please use the migrations feature of Active Record to
|
44
|
-
# then regenerate this schema definition.
|
44
|
+
# This file is auto-generated from the current state of the database. Instead
|
45
|
+
# of editing this file, please use the migrations feature of Active Record to
|
46
|
+
# incrementally modify your database, and then regenerate this schema definition.
|
45
47
|
#
|
46
|
-
# Note that this schema.rb definition is the authoritative source for your
|
47
|
-
# to create the application database on another
|
48
|
-
#
|
48
|
+
# Note that this schema.rb definition is the authoritative source for your
|
49
|
+
# database schema. If you need to create the application database on another
|
50
|
+
# system, you should be using db:schema:load, not running all the migrations
|
51
|
+
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
|
49
52
|
# you'll amass, the slower it'll run and the greater likelihood for issues).
|
50
53
|
#
|
51
54
|
# It's strongly recommended to check this file into your version control system.
|
@@ -173,15 +176,15 @@ HEADER
|
|
173
176
|
def indexes(table, stream)
|
174
177
|
if (indexes = @connection.indexes(table)).any?
|
175
178
|
add_index_statements = indexes.map do |index|
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
179
|
+
statement_parts = [ ('add_index ' + index.table.inspect) ]
|
180
|
+
statement_parts << index.columns.inspect
|
181
|
+
statement_parts << (':name => ' + index.name.inspect)
|
182
|
+
statement_parts << ':unique => true' if index.unique
|
180
183
|
|
181
184
|
index_lengths = index.lengths.compact if index.lengths.is_a?(Array)
|
182
|
-
|
185
|
+
statement_parts << (':length => ' + Hash[*index.columns.zip(index.lengths).flatten].inspect) if index_lengths.present?
|
183
186
|
|
184
|
-
' ' +
|
187
|
+
' ' + statement_parts.join(', ')
|
185
188
|
end
|
186
189
|
|
187
190
|
stream.puts add_index_statements.sort.join("\n")
|
@@ -1,4 +1,5 @@
|
|
1
1
|
module ActiveRecord #:nodoc:
|
2
|
+
# = Active Record Serialization
|
2
3
|
module Serialization
|
3
4
|
extend ActiveSupport::Concern
|
4
5
|
include ActiveModel::Serializers::JSON
|
@@ -22,6 +23,7 @@ module ActiveRecord #:nodoc:
|
|
22
23
|
|
23
24
|
private
|
24
25
|
# Add associations specified via the <tt>:includes</tt> option.
|
26
|
+
#
|
25
27
|
# Expects a block that takes as arguments:
|
26
28
|
# +association+ - name of the association
|
27
29
|
# +records+ - the association record(s) to be serialized
|
@@ -1,4 +1,6 @@
|
|
1
1
|
module ActiveRecord
|
2
|
+
# = Active Record Session Store
|
3
|
+
#
|
2
4
|
# A session store backed by an Active Record class. A default class is
|
3
5
|
# provided, but any object duck-typing to an Active Record Session class
|
4
6
|
# with text +session_id+ and +data+ attributes is sufficient.
|
@@ -7,16 +9,19 @@ module ActiveRecord
|
|
7
9
|
# +id+ (numeric primary key),
|
8
10
|
# +session_id+ (text, or longtext if your session data exceeds 65K), and
|
9
11
|
# +data+ (text or longtext; careful if your session data exceeds 65KB).
|
12
|
+
#
|
10
13
|
# The +session_id+ column should always be indexed for speedy lookups.
|
11
14
|
# Session data is marshaled to the +data+ column in Base64 format.
|
12
15
|
# If the data you write is larger than the column's size limit,
|
13
16
|
# ActionController::SessionOverflowError will be raised.
|
14
17
|
#
|
15
18
|
# You may configure the table name, primary key, and data column.
|
16
|
-
# For example, at the end of <tt>config/
|
19
|
+
# For example, at the end of <tt>config/application.rb</tt>:
|
20
|
+
#
|
17
21
|
# ActiveRecord::SessionStore::Session.table_name = 'legacy_session_table'
|
18
22
|
# ActiveRecord::SessionStore::Session.primary_key = 'session_id'
|
19
23
|
# ActiveRecord::SessionStore::Session.data_column_name = 'legacy_session_data'
|
24
|
+
#
|
20
25
|
# Note that setting the primary key to the +session_id+ frees you from
|
21
26
|
# having a separate +id+ column if you don't want it. However, you must
|
22
27
|
# set <tt>session.model.id = session.session_id</tt> by hand! A before filter
|
@@ -29,8 +34,11 @@ module ActiveRecord
|
|
29
34
|
# You may provide your own session class implementation, whether a
|
30
35
|
# feature-packed Active Record or a bare-metal high-performance SQL
|
31
36
|
# store, by setting
|
37
|
+
#
|
32
38
|
# ActiveRecord::SessionStore.session_class = MySessionClass
|
39
|
+
#
|
33
40
|
# You must implement these methods:
|
41
|
+
#
|
34
42
|
# self.find_by_session_id(session_id)
|
35
43
|
# initialize(hash_of_session_id_and_data)
|
36
44
|
# attr_reader :session_id
|
@@ -41,8 +49,34 @@ module ActiveRecord
|
|
41
49
|
# The example SqlBypass class is a generic SQL session store. You may
|
42
50
|
# use it as a basis for high-performance database-specific stores.
|
43
51
|
class SessionStore < ActionDispatch::Session::AbstractStore
|
52
|
+
module ClassMethods # :nodoc:
|
53
|
+
def marshal(data)
|
54
|
+
ActiveSupport::Base64.encode64(Marshal.dump(data)) if data
|
55
|
+
end
|
56
|
+
|
57
|
+
def unmarshal(data)
|
58
|
+
Marshal.load(ActiveSupport::Base64.decode64(data)) if data
|
59
|
+
end
|
60
|
+
|
61
|
+
def drop_table!
|
62
|
+
connection.execute "DROP TABLE #{table_name}"
|
63
|
+
end
|
64
|
+
|
65
|
+
def create_table!
|
66
|
+
connection.execute <<-end_sql
|
67
|
+
CREATE TABLE #{table_name} (
|
68
|
+
id #{connection.type_to_sql(:primary_key)},
|
69
|
+
#{connection.quote_column_name(session_id_column)} VARCHAR(255) UNIQUE,
|
70
|
+
#{connection.quote_column_name(data_column_name)} TEXT
|
71
|
+
)
|
72
|
+
end_sql
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
44
76
|
# The default Active Record class.
|
45
77
|
class Session < ActiveRecord::Base
|
78
|
+
extend ClassMethods
|
79
|
+
|
46
80
|
##
|
47
81
|
# :singleton-method:
|
48
82
|
# Customizable data column name. Defaults to 'data'.
|
@@ -54,7 +88,7 @@ module ActiveRecord
|
|
54
88
|
|
55
89
|
class << self
|
56
90
|
def data_column_size_limit
|
57
|
-
@data_column_size_limit ||= columns_hash[
|
91
|
+
@data_column_size_limit ||= columns_hash[data_column_name].limit
|
58
92
|
end
|
59
93
|
|
60
94
|
# Hook to set up sessid compatibility.
|
@@ -63,29 +97,11 @@ module ActiveRecord
|
|
63
97
|
find_by_session_id(session_id)
|
64
98
|
end
|
65
99
|
|
66
|
-
def marshal(data)
|
67
|
-
ActiveSupport::Base64.encode64(Marshal.dump(data)) if data
|
68
|
-
end
|
69
|
-
|
70
|
-
def unmarshal(data)
|
71
|
-
Marshal.load(ActiveSupport::Base64.decode64(data)) if data
|
72
|
-
end
|
73
|
-
|
74
|
-
def create_table!
|
75
|
-
connection.execute <<-end_sql
|
76
|
-
CREATE TABLE #{table_name} (
|
77
|
-
id INTEGER PRIMARY KEY,
|
78
|
-
#{connection.quote_column_name('session_id')} TEXT UNIQUE,
|
79
|
-
#{connection.quote_column_name(@@data_column_name)} TEXT(255)
|
80
|
-
)
|
81
|
-
end_sql
|
82
|
-
end
|
83
|
-
|
84
|
-
def drop_table!
|
85
|
-
connection.execute "DROP TABLE #{table_name}"
|
86
|
-
end
|
87
|
-
|
88
100
|
private
|
101
|
+
def session_id_column
|
102
|
+
'session_id'
|
103
|
+
end
|
104
|
+
|
89
105
|
# Compatibility with tables using sessid instead of session_id.
|
90
106
|
def setup_sessid_compatibility!
|
91
107
|
# Reset column info since it may be stale.
|
@@ -98,6 +114,8 @@ module ActiveRecord
|
|
98
114
|
define_method(:session_id) { sessid }
|
99
115
|
define_method(:session_id=) { |session_id| self.sessid = session_id }
|
100
116
|
else
|
117
|
+
class << self; remove_method :find_by_session_id; end
|
118
|
+
|
101
119
|
def self.find_by_session_id(session_id)
|
102
120
|
find :first, :conditions => {:session_id=>session_id}
|
103
121
|
end
|
@@ -105,6 +123,11 @@ module ActiveRecord
|
|
105
123
|
end
|
106
124
|
end
|
107
125
|
|
126
|
+
def initialize(attributes = nil)
|
127
|
+
@data = nil
|
128
|
+
super
|
129
|
+
end
|
130
|
+
|
108
131
|
# Lazy-unmarshal session state.
|
109
132
|
def data
|
110
133
|
@data ||= self.class.unmarshal(read_attribute(@@data_column_name)) || {}
|
@@ -114,22 +137,22 @@ module ActiveRecord
|
|
114
137
|
|
115
138
|
# Has the session been loaded yet?
|
116
139
|
def loaded?
|
117
|
-
|
140
|
+
@data
|
118
141
|
end
|
119
142
|
|
120
143
|
private
|
121
144
|
def marshal_data!
|
122
|
-
return false
|
123
|
-
write_attribute(@@data_column_name, self.class.marshal(
|
145
|
+
return false unless loaded?
|
146
|
+
write_attribute(@@data_column_name, self.class.marshal(data))
|
124
147
|
end
|
125
148
|
|
126
149
|
# Ensures that the data about to be stored in the database is not
|
127
150
|
# larger than the data storage column. Raises
|
128
151
|
# ActionController::SessionOverflowError.
|
129
152
|
def raise_on_session_data_overflow!
|
130
|
-
return false
|
153
|
+
return false unless loaded?
|
131
154
|
limit = self.class.data_column_size_limit
|
132
|
-
if
|
155
|
+
if limit and read_attribute(@@data_column_name).size > limit
|
133
156
|
raise ActionController::SessionOverflowError
|
134
157
|
end
|
135
158
|
end
|
@@ -154,6 +177,8 @@ module ActiveRecord
|
|
154
177
|
# binary session data in a +text+ column. For higher performance,
|
155
178
|
# store in a +blob+ column instead and forgo the Base64 encoding.
|
156
179
|
class SqlBypass
|
180
|
+
extend ClassMethods
|
181
|
+
|
157
182
|
##
|
158
183
|
# :singleton-method:
|
159
184
|
# Use the ActiveRecord::Base.connection by default.
|
@@ -178,6 +203,8 @@ module ActiveRecord
|
|
178
203
|
@@data_column = 'data'
|
179
204
|
|
180
205
|
class << self
|
206
|
+
alias :data_column_name :data_column
|
207
|
+
|
181
208
|
def connection
|
182
209
|
@@connection ||= ActiveRecord::Base.connection
|
183
210
|
end
|
@@ -188,43 +215,21 @@ module ActiveRecord
|
|
188
215
|
new(:session_id => session_id, :marshaled_data => record['data'])
|
189
216
|
end
|
190
217
|
end
|
191
|
-
|
192
|
-
def marshal(data)
|
193
|
-
ActiveSupport::Base64.encode64(Marshal.dump(data)) if data
|
194
|
-
end
|
195
|
-
|
196
|
-
def unmarshal(data)
|
197
|
-
Marshal.load(ActiveSupport::Base64.decode64(data)) if data
|
198
|
-
end
|
199
|
-
|
200
|
-
def create_table!
|
201
|
-
@@connection.execute <<-end_sql
|
202
|
-
CREATE TABLE #{table_name} (
|
203
|
-
id INTEGER PRIMARY KEY,
|
204
|
-
#{@@connection.quote_column_name(session_id_column)} TEXT UNIQUE,
|
205
|
-
#{@@connection.quote_column_name(data_column)} TEXT
|
206
|
-
)
|
207
|
-
end_sql
|
208
|
-
end
|
209
|
-
|
210
|
-
def drop_table!
|
211
|
-
@@connection.execute "DROP TABLE #{table_name}"
|
212
|
-
end
|
213
218
|
end
|
214
219
|
|
215
|
-
attr_reader :session_id
|
220
|
+
attr_reader :session_id, :new_record
|
221
|
+
alias :new_record? :new_record
|
222
|
+
|
216
223
|
attr_writer :data
|
217
224
|
|
218
225
|
# Look for normal and marshaled data, self.find_by_session_id's way of
|
219
226
|
# telling us to postpone unmarshaling until the data is requested.
|
220
227
|
# We need to handle a normal data attribute in case of a new record.
|
221
228
|
def initialize(attributes)
|
222
|
-
@session_id
|
223
|
-
@
|
224
|
-
|
225
|
-
|
226
|
-
def new_record?
|
227
|
-
@new_record
|
229
|
+
@session_id = attributes[:session_id]
|
230
|
+
@data = attributes[:data]
|
231
|
+
@marshaled_data = attributes[:marshaled_data]
|
232
|
+
@new_record = @marshaled_data.nil?
|
228
233
|
end
|
229
234
|
|
230
235
|
# Lazy-unmarshal session state.
|
@@ -240,39 +245,41 @@ module ActiveRecord
|
|
240
245
|
end
|
241
246
|
|
242
247
|
def loaded?
|
243
|
-
|
248
|
+
@data
|
244
249
|
end
|
245
250
|
|
246
251
|
def save
|
247
|
-
return false
|
252
|
+
return false unless loaded?
|
248
253
|
marshaled_data = self.class.marshal(data)
|
254
|
+
connect = connection
|
249
255
|
|
250
256
|
if @new_record
|
251
257
|
@new_record = false
|
252
|
-
|
253
|
-
INSERT INTO #{
|
254
|
-
#{
|
255
|
-
#{
|
258
|
+
connect.update <<-end_sql, 'Create session'
|
259
|
+
INSERT INTO #{table_name} (
|
260
|
+
#{connect.quote_column_name(session_id_column)},
|
261
|
+
#{connect.quote_column_name(data_column)} )
|
256
262
|
VALUES (
|
257
|
-
#{
|
258
|
-
#{
|
263
|
+
#{connect.quote(session_id)},
|
264
|
+
#{connect.quote(marshaled_data)} )
|
259
265
|
end_sql
|
260
266
|
else
|
261
|
-
|
262
|
-
UPDATE #{
|
263
|
-
SET #{
|
264
|
-
WHERE #{
|
267
|
+
connect.update <<-end_sql, 'Update session'
|
268
|
+
UPDATE #{table_name}
|
269
|
+
SET #{connect.quote_column_name(data_column)}=#{connect.quote(marshaled_data)}
|
270
|
+
WHERE #{connect.quote_column_name(session_id_column)}=#{connect.quote(session_id)}
|
265
271
|
end_sql
|
266
272
|
end
|
267
273
|
end
|
268
274
|
|
269
275
|
def destroy
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
+
return if @new_record
|
277
|
+
|
278
|
+
connect = connection
|
279
|
+
connect.delete <<-end_sql, 'Destroy session'
|
280
|
+
DELETE FROM #{table_name}
|
281
|
+
WHERE #{connect.quote_column_name(session_id_column)}=#{connect.quote(session_id)}
|
282
|
+
end_sql
|
276
283
|
end
|
277
284
|
end
|
278
285
|
|
@@ -281,12 +288,11 @@ module ActiveRecord
|
|
281
288
|
cattr_accessor :session_class
|
282
289
|
self.session_class = Session
|
283
290
|
|
284
|
-
SESSION_RECORD_KEY = 'rack.session.record'
|
291
|
+
SESSION_RECORD_KEY = 'rack.session.record'
|
285
292
|
|
286
293
|
private
|
287
294
|
def get_session(env, sid)
|
288
295
|
Base.silence do
|
289
|
-
sid ||= generate_sid
|
290
296
|
session = find_session(sid)
|
291
297
|
env[SESSION_RECORD_KEY] = session
|
292
298
|
[sid, session.data]
|
@@ -309,7 +315,15 @@ module ActiveRecord
|
|
309
315
|
|
310
316
|
sid
|
311
317
|
end
|
312
|
-
|
318
|
+
|
319
|
+
def destroy(env)
|
320
|
+
if sid = current_session_id(env)
|
321
|
+
Base.silence do
|
322
|
+
get_session_model(env, sid).destroy
|
323
|
+
end
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
313
327
|
def get_session_model(env, sid)
|
314
328
|
if env[ENV_SESSION_OPTIONS_KEY][:id].nil?
|
315
329
|
env[SESSION_RECORD_KEY] = find_session(sid)
|
@@ -1,4 +1,7 @@
|
|
1
1
|
module ActiveRecord
|
2
|
+
# = Active Record Test Case
|
3
|
+
#
|
4
|
+
# Defines some test assertions to test against SQL queries.
|
2
5
|
class TestCase < ActiveSupport::TestCase #:nodoc:
|
3
6
|
def assert_date_from_db(expected, actual, message = nil)
|
4
7
|
# SybaseAdapter doesn't have a separate column type just for dates,
|
@@ -1,11 +1,16 @@
|
|
1
1
|
module ActiveRecord
|
2
|
-
# Active Record
|
3
|
-
#
|
2
|
+
# = Active Record Timestamp
|
3
|
+
#
|
4
|
+
# Active Record automatically timestamps create and update operations if the
|
5
|
+
# table has fields named <tt>created_at/created_on</tt> or
|
6
|
+
# <tt>updated_at/updated_on</tt>.
|
7
|
+
#
|
8
|
+
# Timestamping can be turned off by setting:
|
4
9
|
#
|
5
|
-
# Timestamping can be turned off by setting
|
6
10
|
# <tt>ActiveRecord::Base.record_timestamps = false</tt>
|
7
11
|
#
|
8
|
-
# Timestamps are in the local timezone by default but you can use UTC by setting
|
12
|
+
# Timestamps are in the local timezone by default but you can use UTC by setting:
|
13
|
+
#
|
9
14
|
# <tt>ActiveRecord::Base.default_timezone = :utc</tt>
|
10
15
|
module Timestamp
|
11
16
|
extend ActiveSupport::Concern
|
@@ -16,54 +21,69 @@ module ActiveRecord
|
|
16
21
|
end
|
17
22
|
|
18
23
|
# Saves the record with the updated_at/on attributes set to the current time.
|
19
|
-
#
|
20
|
-
# If an attribute name is passed, that attribute is
|
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.
|
21
27
|
#
|
22
28
|
# Examples:
|
23
29
|
#
|
24
|
-
# product.touch # updates updated_at
|
25
|
-
# product.touch(:designed_at) # updates the designed_at attribute
|
30
|
+
# product.touch # updates updated_at/on
|
31
|
+
# product.touch(:designed_at) # updates the designed_at attribute and updated_at/on
|
26
32
|
def touch(attribute = nil)
|
27
|
-
|
28
|
-
|
29
|
-
if attribute
|
30
|
-
write_attribute(attribute, current_time)
|
31
|
-
else
|
32
|
-
write_attribute('updated_at', current_time) if respond_to?(:updated_at)
|
33
|
-
write_attribute('updated_on', current_time) if respond_to?(:updated_on)
|
34
|
-
end
|
35
|
-
|
36
|
-
save!
|
33
|
+
update_attribute(attribute, current_time_from_proper_timezone)
|
37
34
|
end
|
38
35
|
|
39
36
|
private
|
37
|
+
|
40
38
|
def create #:nodoc:
|
41
39
|
if record_timestamps
|
42
40
|
current_time = current_time_from_proper_timezone
|
43
41
|
|
44
|
-
|
45
|
-
|
42
|
+
timestamp_attributes_for_create.each do |column|
|
43
|
+
write_attribute(column.to_s, current_time) if respond_to?(column) && self.send(column).nil?
|
44
|
+
end
|
46
45
|
|
47
|
-
|
48
|
-
|
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
49
|
end
|
50
50
|
|
51
51
|
super
|
52
52
|
end
|
53
53
|
|
54
54
|
def update(*args) #:nodoc:
|
55
|
-
if
|
56
|
-
|
55
|
+
record_update_timestamps if !partial_updates? || changed?
|
56
|
+
super
|
57
|
+
end
|
57
58
|
|
58
|
-
|
59
|
-
|
59
|
+
def record_update_timestamps #:nodoc:
|
60
|
+
return unless record_timestamps
|
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
|
60
65
|
end
|
66
|
+
end
|
61
67
|
|
62
|
-
|
68
|
+
def timestamp_attributes_for_update_in_model #:nodoc:
|
69
|
+
timestamp_attributes_for_update.select { |elem| respond_to?(elem) }
|
70
|
+
end
|
71
|
+
|
72
|
+
def timestamp_attributes_for_update #:nodoc:
|
73
|
+
[:updated_at, :updated_on]
|
74
|
+
end
|
75
|
+
|
76
|
+
def timestamp_attributes_for_create #:nodoc:
|
77
|
+
[:created_at, :created_on]
|
78
|
+
end
|
79
|
+
|
80
|
+
def all_timestamp_attributes #:nodoc:
|
81
|
+
timestamp_attributes_for_update + timestamp_attributes_for_create
|
63
82
|
end
|
64
83
|
|
65
|
-
def current_time_from_proper_timezone
|
84
|
+
def current_time_from_proper_timezone #:nodoc:
|
66
85
|
self.class.default_timezone == :utc ? Time.now.utc : Time.now
|
67
86
|
end
|
68
87
|
end
|
69
|
-
end
|
88
|
+
end
|
89
|
+
|