activerecord 2.3.2 → 2.3.3
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 +11 -0
- data/Rakefile +10 -10
- data/lib/active_record/associations.rb +67 -30
- data/lib/active_record/associations/association_collection.rb +9 -5
- data/lib/active_record/associations/association_proxy.rb +2 -2
- data/lib/active_record/associations/belongs_to_association.rb +22 -4
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +5 -1
- data/lib/active_record/associations/has_one_through_association.rb +8 -8
- data/lib/active_record/autosave_association.rb +11 -6
- data/lib/active_record/base.rb +16 -21
- data/lib/active_record/batches.rb +23 -15
- data/lib/active_record/calculations.rb +5 -13
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +66 -22
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +3 -0
- data/lib/active_record/fixtures.rb +5 -4
- data/lib/active_record/named_scope.rb +2 -2
- data/lib/active_record/schema_dumper.rb +5 -1
- data/lib/active_record/serialization.rb +3 -2
- data/lib/active_record/serializers/json_serializer.rb +8 -18
- data/lib/active_record/session_store.rb +9 -1
- data/lib/active_record/timestamp.rb +39 -9
- data/lib/active_record/validations.rb +1 -1
- data/lib/active_record/version.rb +1 -1
- data/test/cases/associations/belongs_to_associations_test.rb +102 -4
- data/test/cases/associations/eager_test.rb +12 -0
- data/test/cases/associations/has_many_associations_test.rb +6 -0
- data/test/cases/associations/has_one_through_associations_test.rb +8 -1
- data/test/cases/associations/inner_join_association_test.rb +5 -0
- data/test/cases/autosave_association_test.rb +22 -0
- data/test/cases/base_test.rb +2 -2
- data/test/cases/calculations_test.rb +8 -14
- data/test/cases/copy_table_test_sqlite.rb +5 -5
- data/test/cases/finder_test.rb +6 -0
- data/test/cases/fixtures_test.rb +5 -0
- data/test/cases/helper.rb +1 -2
- data/test/cases/json_serialization_test.rb +57 -57
- data/test/cases/method_scoping_test.rb +13 -3
- data/test/cases/reflection_test.rb +5 -5
- data/test/cases/schema_dumper_test.rb +17 -7
- data/test/cases/schema_test_postgresql.rb +76 -0
- data/test/cases/timestamp_test.rb +75 -0
- data/test/debug.log +415 -0
- data/test/fixtures/fixture_database.sqlite3 +0 -0
- data/test/fixtures/fixture_database_2.sqlite3 +0 -0
- data/test/models/author.rb +4 -0
- data/test/models/company.rb +7 -7
- data/test/models/developer.rb +10 -0
- data/test/models/essay.rb +3 -0
- data/test/models/pet.rb +1 -1
- data/test/models/project.rb +1 -1
- data/test/models/reply.rb +2 -1
- data/test/models/topic.rb +1 -0
- data/test/models/toy.rb +2 -0
- data/test/schema/schema.rb +15 -0
- metadata +6 -3
@@ -4,10 +4,12 @@ module ActiveRecord
|
|
4
4
|
base.extend(ClassMethods)
|
5
5
|
end
|
6
6
|
|
7
|
-
# When processing large numbers of records, it's often a good idea to do
|
7
|
+
# When processing large numbers of records, it's often a good idea to do
|
8
|
+
# so in batches to prevent memory ballooning.
|
8
9
|
module ClassMethods
|
9
|
-
# Yields each record that was found by the find +options+. The find is
|
10
|
-
# with a batch size of 1000 (or as
|
10
|
+
# Yields each record that was found by the find +options+. The find is
|
11
|
+
# performed by find_in_batches with a batch size of 1000 (or as
|
12
|
+
# specified by the <tt>:batch_size</tt> option).
|
11
13
|
#
|
12
14
|
# Example:
|
13
15
|
#
|
@@ -15,9 +17,10 @@ module ActiveRecord
|
|
15
17
|
# person.party_all_night!
|
16
18
|
# end
|
17
19
|
#
|
18
|
-
# Note: This method is only intended to use for batch processing of
|
19
|
-
#
|
20
|
-
#
|
20
|
+
# Note: This method is only intended to use for batch processing of
|
21
|
+
# large amounts of records that wouldn't fit in memory all at once. If
|
22
|
+
# you just need to loop over less than 1000 records, it's probably
|
23
|
+
# better just to use the regular find methods.
|
21
24
|
def find_each(options = {})
|
22
25
|
find_in_batches(options) do |records|
|
23
26
|
records.each { |record| yield record }
|
@@ -26,17 +29,22 @@ module ActiveRecord
|
|
26
29
|
self
|
27
30
|
end
|
28
31
|
|
29
|
-
# Yields each batch of records that was found by the find +options+ as
|
30
|
-
#
|
32
|
+
# Yields each batch of records that was found by the find +options+ as
|
33
|
+
# an array. The size of each batch is set by the <tt>:batch_size</tt>
|
34
|
+
# option; the default is 1000.
|
31
35
|
#
|
32
|
-
# You can control the starting point for the batch processing by
|
33
|
-
#
|
34
|
-
#
|
35
|
-
# worker
|
36
|
+
# You can control the starting point for the batch processing by
|
37
|
+
# supplying the <tt>:start</tt> option. This is especially useful if you
|
38
|
+
# want multiple workers dealing with the same processing queue. You can
|
39
|
+
# make worker 1 handle all the records between id 0 and 10,000 and
|
40
|
+
# worker 2 handle from 10,000 and beyond (by setting the <tt>:start</tt>
|
41
|
+
# option on that worker).
|
36
42
|
#
|
37
|
-
# It's not possible to set the order. That is automatically set to
|
38
|
-
#
|
39
|
-
#
|
43
|
+
# It's not possible to set the order. That is automatically set to
|
44
|
+
# ascending on the primary key ("id ASC") to make the batch ordering
|
45
|
+
# work. This also mean that this method only works with integer-based
|
46
|
+
# primary keys. You can't set the limit either, that's used to control
|
47
|
+
# the the batch sizes.
|
40
48
|
#
|
41
49
|
# Example:
|
42
50
|
#
|
@@ -141,30 +141,22 @@ module ActiveRecord
|
|
141
141
|
def construct_count_options_from_args(*args)
|
142
142
|
options = {}
|
143
143
|
column_name = :all
|
144
|
-
|
144
|
+
|
145
145
|
# We need to handle
|
146
146
|
# count()
|
147
147
|
# count(:column_name=:all)
|
148
148
|
# count(options={})
|
149
149
|
# count(column_name=:all, options={})
|
150
|
-
# selects specified by scopes
|
151
150
|
case args.size
|
152
|
-
when 0
|
153
|
-
column_name = scope(:find)[:select] if scope(:find)
|
154
151
|
when 1
|
155
|
-
|
156
|
-
column_name = scope(:find)[:select] if scope(:find)
|
157
|
-
options = args[0]
|
158
|
-
else
|
159
|
-
column_name = args[0]
|
160
|
-
end
|
152
|
+
args[0].is_a?(Hash) ? options = args[0] : column_name = args[0]
|
161
153
|
when 2
|
162
154
|
column_name, options = args
|
163
155
|
else
|
164
156
|
raise ArgumentError, "Unexpected parameters passed to count(): #{args.inspect}"
|
165
|
-
end
|
166
|
-
|
167
|
-
[column_name
|
157
|
+
end if args.size > 0
|
158
|
+
|
159
|
+
[column_name, options]
|
168
160
|
end
|
169
161
|
|
170
162
|
def construct_calculation_sql(operation, column_name, options) #:nodoc:
|
@@ -287,7 +287,13 @@ module ActiveRecord
|
|
287
287
|
|
288
288
|
# Escapes binary strings for bytea input to the database.
|
289
289
|
def escape_bytea(value)
|
290
|
-
if
|
290
|
+
if @connection.respond_to?(:escape_bytea)
|
291
|
+
self.class.instance_eval do
|
292
|
+
define_method(:escape_bytea) do |value|
|
293
|
+
@connection.escape_bytea(value) if value
|
294
|
+
end
|
295
|
+
end
|
296
|
+
elsif PGconn.respond_to?(:escape_bytea)
|
291
297
|
self.class.instance_eval do
|
292
298
|
define_method(:escape_bytea) do |value|
|
293
299
|
PGconn.escape_bytea(value) if value
|
@@ -376,7 +382,13 @@ module ActiveRecord
|
|
376
382
|
|
377
383
|
# Quotes strings for use in SQL input in the postgres driver for better performance.
|
378
384
|
def quote_string(s) #:nodoc:
|
379
|
-
if
|
385
|
+
if @connection.respond_to?(:escape)
|
386
|
+
self.class.instance_eval do
|
387
|
+
define_method(:quote_string) do |s|
|
388
|
+
@connection.escape(s)
|
389
|
+
end
|
390
|
+
end
|
391
|
+
elsif PGconn.respond_to?(:escape)
|
380
392
|
self.class.instance_eval do
|
381
393
|
define_method(:quote_string) do |s|
|
382
394
|
PGconn.escape(s)
|
@@ -392,9 +404,28 @@ module ActiveRecord
|
|
392
404
|
quote_string(s)
|
393
405
|
end
|
394
406
|
|
407
|
+
# Checks the following cases:
|
408
|
+
#
|
409
|
+
# - table_name
|
410
|
+
# - "table.name"
|
411
|
+
# - schema_name.table_name
|
412
|
+
# - schema_name."table.name"
|
413
|
+
# - "schema.name".table_name
|
414
|
+
# - "schema.name"."table.name"
|
415
|
+
def quote_table_name(name)
|
416
|
+
schema, name_part = extract_pg_identifier_from_name(name.to_s)
|
417
|
+
|
418
|
+
unless name_part
|
419
|
+
quote_column_name(schema)
|
420
|
+
else
|
421
|
+
table_name, name_part = extract_pg_identifier_from_name(name_part)
|
422
|
+
"#{quote_column_name(schema)}.#{quote_column_name(table_name)}"
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
395
426
|
# Quotes column names for use in SQL queries.
|
396
427
|
def quote_column_name(name) #:nodoc:
|
397
|
-
|
428
|
+
PGconn.quote_ident(name.to_s)
|
398
429
|
end
|
399
430
|
|
400
431
|
# Quote date/time values for use in SQL input. Includes microseconds
|
@@ -621,33 +652,36 @@ module ActiveRecord
|
|
621
652
|
def indexes(table_name, name = nil)
|
622
653
|
schemas = schema_search_path.split(/,/).map { |p| quote(p) }.join(',')
|
623
654
|
result = query(<<-SQL, name)
|
624
|
-
SELECT distinct i.relname, d.indisunique,
|
625
|
-
FROM pg_class t, pg_class i, pg_index d
|
655
|
+
SELECT distinct i.relname, d.indisunique, d.indkey, t.oid
|
656
|
+
FROM pg_class t, pg_class i, pg_index d
|
626
657
|
WHERE i.relkind = 'i'
|
627
658
|
AND d.indexrelid = i.oid
|
628
659
|
AND d.indisprimary = 'f'
|
629
660
|
AND t.oid = d.indrelid
|
630
661
|
AND t.relname = '#{table_name}'
|
631
662
|
AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname IN (#{schemas}) )
|
632
|
-
AND a.attrelid = t.oid
|
633
|
-
AND ( d.indkey[0]=a.attnum OR d.indkey[1]=a.attnum
|
634
|
-
OR d.indkey[2]=a.attnum OR d.indkey[3]=a.attnum
|
635
|
-
OR d.indkey[4]=a.attnum OR d.indkey[5]=a.attnum
|
636
|
-
OR d.indkey[6]=a.attnum OR d.indkey[7]=a.attnum
|
637
|
-
OR d.indkey[8]=a.attnum OR d.indkey[9]=a.attnum )
|
638
663
|
ORDER BY i.relname
|
639
664
|
SQL
|
640
665
|
|
641
|
-
|
666
|
+
|
642
667
|
indexes = []
|
643
668
|
|
644
|
-
result.
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
669
|
+
indexes = result.map do |row|
|
670
|
+
index_name = row[0]
|
671
|
+
unique = row[1] == 't'
|
672
|
+
indkey = row[2].split(" ")
|
673
|
+
oid = row[3]
|
674
|
+
|
675
|
+
columns = query(<<-SQL, "Columns for index #{row[0]} on #{table_name}").inject({}) {|attlist, r| attlist[r[1]] = r[0]; attlist}
|
676
|
+
SELECT a.attname, a.attnum
|
677
|
+
FROM pg_attribute a
|
678
|
+
WHERE a.attrelid = #{oid}
|
679
|
+
AND a.attnum IN (#{indkey.join(",")})
|
680
|
+
SQL
|
681
|
+
|
682
|
+
column_names = indkey.map {|attnum| columns[attnum] }
|
683
|
+
IndexDefinition.new(table_name, index_name, unique, column_names)
|
649
684
|
|
650
|
-
indexes.last.columns << row[2]
|
651
685
|
end
|
652
686
|
|
653
687
|
indexes
|
@@ -745,7 +779,7 @@ module ActiveRecord
|
|
745
779
|
AND attr.attrelid = cons.conrelid
|
746
780
|
AND attr.attnum = cons.conkey[1]
|
747
781
|
AND cons.contype = 'p'
|
748
|
-
AND dep.refobjid = '#{table}'::regclass
|
782
|
+
AND dep.refobjid = '#{quote_table_name(table)}'::regclass
|
749
783
|
end_sql
|
750
784
|
|
751
785
|
if result.nil? or result.empty?
|
@@ -764,7 +798,7 @@ module ActiveRecord
|
|
764
798
|
JOIN pg_attribute attr ON (t.oid = attrelid)
|
765
799
|
JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
|
766
800
|
JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
|
767
|
-
WHERE t.oid = '#{table}'::regclass
|
801
|
+
WHERE t.oid = '#{quote_table_name(table)}'::regclass
|
768
802
|
AND cons.contype = 'p'
|
769
803
|
AND def.adsrc ~* 'nextval'
|
770
804
|
end_sql
|
@@ -839,7 +873,7 @@ module ActiveRecord
|
|
839
873
|
|
840
874
|
# Drops an index from a table.
|
841
875
|
def remove_index(table_name, options = {})
|
842
|
-
execute "DROP INDEX #{index_name(table_name, options)}"
|
876
|
+
execute "DROP INDEX #{quote_table_name(index_name(table_name, options))}"
|
843
877
|
end
|
844
878
|
|
845
879
|
# Maps logical Rails types to PostgreSQL-specific data types.
|
@@ -1040,11 +1074,21 @@ module ActiveRecord
|
|
1040
1074
|
SELECT a.attname, format_type(a.atttypid, a.atttypmod), d.adsrc, a.attnotnull
|
1041
1075
|
FROM pg_attribute a LEFT JOIN pg_attrdef d
|
1042
1076
|
ON a.attrelid = d.adrelid AND a.attnum = d.adnum
|
1043
|
-
WHERE a.attrelid = '#{table_name}'::regclass
|
1077
|
+
WHERE a.attrelid = '#{quote_table_name(table_name)}'::regclass
|
1044
1078
|
AND a.attnum > 0 AND NOT a.attisdropped
|
1045
1079
|
ORDER BY a.attnum
|
1046
1080
|
end_sql
|
1047
1081
|
end
|
1082
|
+
|
1083
|
+
def extract_pg_identifier_from_name(name)
|
1084
|
+
match_data = name[0,1] == '"' ? name.match(/\"([^\"]+)\"/) : name.match(/([^\.]+)/)
|
1085
|
+
|
1086
|
+
if match_data
|
1087
|
+
rest = name[match_data[0].length..-1]
|
1088
|
+
rest = rest[1..-1] if rest[0,1] == "."
|
1089
|
+
[match_data[1], (rest.length > 0 ? rest : nil)]
|
1090
|
+
end
|
1091
|
+
end
|
1048
1092
|
end
|
1049
1093
|
end
|
1050
1094
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# encoding: binary
|
1
2
|
require 'active_record/connection_adapters/abstract_adapter'
|
2
3
|
|
3
4
|
module ActiveRecord
|
@@ -46,6 +47,7 @@ module ActiveRecord
|
|
46
47
|
class SQLiteColumn < Column #:nodoc:
|
47
48
|
class << self
|
48
49
|
def string_to_binary(value)
|
50
|
+
value = value.dup.force_encoding(Encoding::BINARY) if value.respond_to?(:force_encoding)
|
49
51
|
value.gsub(/\0|\%/n) do |b|
|
50
52
|
case b
|
51
53
|
when "\0" then "%00"
|
@@ -55,6 +57,7 @@ module ActiveRecord
|
|
55
57
|
end
|
56
58
|
|
57
59
|
def binary_to_string(value)
|
60
|
+
value = value.dup.force_encoding(Encoding::BINARY) if value.respond_to?(:force_encoding)
|
58
61
|
value.gsub(/%00|%25/n) do |b|
|
59
62
|
case b
|
60
63
|
when "%00" then "\0"
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'erb'
|
2
2
|
require 'yaml'
|
3
3
|
require 'csv'
|
4
|
+
require 'zlib'
|
4
5
|
require 'active_support/dependencies'
|
5
6
|
require 'active_support/test_case'
|
6
7
|
|
@@ -433,6 +434,7 @@ end
|
|
433
434
|
# Any fixture labeled "DEFAULTS" is safely ignored.
|
434
435
|
|
435
436
|
class Fixtures < (RUBY_VERSION < '1.9' ? YAML::Omap : Hash)
|
437
|
+
MAX_ID = 2 ** 31 - 1
|
436
438
|
DEFAULT_FILTER_RE = /\.ya?ml$/
|
437
439
|
|
438
440
|
@@all_cached_fixtures = {}
|
@@ -524,11 +526,10 @@ class Fixtures < (RUBY_VERSION < '1.9' ? YAML::Omap : Hash)
|
|
524
526
|
cached_fixtures(connection, table_names)
|
525
527
|
end
|
526
528
|
|
527
|
-
# Returns a consistent identifier for +label+.
|
528
|
-
#
|
529
|
-
# label, assuming the same OS, platform, and version of Ruby.
|
529
|
+
# Returns a consistent, platform-independent identifier for +label+.
|
530
|
+
# Identifiers are positive integers less than 2^32.
|
530
531
|
def self.identify(label)
|
531
|
-
label.to_s
|
532
|
+
Zlib.crc32(label.to_s) % MAX_ID
|
532
533
|
end
|
533
534
|
|
534
535
|
attr_reader :table_name, :name
|
@@ -114,7 +114,7 @@ module ActiveRecord
|
|
114
114
|
end
|
115
115
|
end
|
116
116
|
|
117
|
-
delegate :scopes, :with_scope, :to => :proxy_scope
|
117
|
+
delegate :scopes, :with_scope, :scoped_methods, :to => :proxy_scope
|
118
118
|
|
119
119
|
def initialize(proxy_scope, options, &block)
|
120
120
|
options ||= {}
|
@@ -178,7 +178,7 @@ module ActiveRecord
|
|
178
178
|
else
|
179
179
|
with_scope({:find => proxy_options, :create => proxy_options[:conditions].is_a?(Hash) ? proxy_options[:conditions] : {}}, :reverse_merge) do
|
180
180
|
method = :new if method == :build
|
181
|
-
if current_scoped_methods_when_defined
|
181
|
+
if current_scoped_methods_when_defined && !scoped_methods.include?(current_scoped_methods_when_defined)
|
182
182
|
with_scope current_scoped_methods_when_defined do
|
183
183
|
proxy_scope.send(method, *args, &block)
|
184
184
|
end
|
@@ -78,11 +78,14 @@ HEADER
|
|
78
78
|
begin
|
79
79
|
tbl = StringIO.new
|
80
80
|
|
81
|
+
# first dump primary key column
|
81
82
|
if @connection.respond_to?(:pk_and_sequence_for)
|
82
83
|
pk, pk_seq = @connection.pk_and_sequence_for(table)
|
84
|
+
elsif @connection.respond_to?(:primary_key)
|
85
|
+
pk = @connection.primary_key(table)
|
83
86
|
end
|
84
87
|
pk ||= 'id'
|
85
|
-
|
88
|
+
|
86
89
|
tbl.print " create_table #{table.inspect}"
|
87
90
|
if columns.detect { |c| c.name == pk }
|
88
91
|
if pk != 'id'
|
@@ -94,6 +97,7 @@ HEADER
|
|
94
97
|
tbl.print ", :force => true"
|
95
98
|
tbl.puts " do |t|"
|
96
99
|
|
100
|
+
# then dump all non-primary key columns
|
97
101
|
column_specs = columns.map do |column|
|
98
102
|
raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" if @types[column.type].nil?
|
99
103
|
next if column.name == pk
|
@@ -5,8 +5,9 @@ module ActiveRecord #:nodoc:
|
|
5
5
|
class Serializer #:nodoc:
|
6
6
|
attr_reader :options
|
7
7
|
|
8
|
-
def initialize(record, options =
|
9
|
-
@record
|
8
|
+
def initialize(record, options = nil)
|
9
|
+
@record = record
|
10
|
+
@options = options ? options.dup : {}
|
10
11
|
end
|
11
12
|
|
12
13
|
# To replicate the behavior in ActiveRecord#attributes,
|
@@ -1,8 +1,10 @@
|
|
1
|
+
require 'active_support/json'
|
2
|
+
require 'active_support/core_ext/module/model_naming'
|
3
|
+
|
1
4
|
module ActiveRecord #:nodoc:
|
2
5
|
module Serialization
|
3
6
|
def self.included(base)
|
4
7
|
base.cattr_accessor :include_root_in_json, :instance_writer => false
|
5
|
-
base.extend ClassMethods
|
6
8
|
end
|
7
9
|
|
8
10
|
# Returns a JSON string representing the model. Some configuration is
|
@@ -72,28 +74,16 @@ module ActiveRecord #:nodoc:
|
|
72
74
|
# {"comments": [{"body": "Don't think too hard"}],
|
73
75
|
# "title": "So I was thinking"}]}
|
74
76
|
def to_json(options = {})
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
JsonSerializer.new(self, options).to_s
|
79
|
-
end
|
77
|
+
hash = Serializer.new(self, options).serializable_record
|
78
|
+
hash = { self.class.model_name.element => hash } if include_root_in_json
|
79
|
+
ActiveSupport::JSON.encode(hash)
|
80
80
|
end
|
81
81
|
|
82
|
+
def as_json(options = nil) self end #:nodoc:
|
83
|
+
|
82
84
|
def from_json(json)
|
83
85
|
self.attributes = ActiveSupport::JSON.decode(json)
|
84
86
|
self
|
85
87
|
end
|
86
|
-
|
87
|
-
class JsonSerializer < ActiveRecord::Serialization::Serializer #:nodoc:
|
88
|
-
def serialize
|
89
|
-
serializable_record.to_json
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
module ClassMethods
|
94
|
-
def json_class_name
|
95
|
-
@json_class_name ||= name.demodulize.underscore.inspect
|
96
|
-
end
|
97
|
-
end
|
98
88
|
end
|
99
89
|
end
|
@@ -295,7 +295,7 @@ module ActiveRecord
|
|
295
295
|
|
296
296
|
def set_session(env, sid, session_data)
|
297
297
|
Base.silence do
|
298
|
-
record = env
|
298
|
+
record = get_session_model(env, sid)
|
299
299
|
record.data = session_data
|
300
300
|
return false unless record.save
|
301
301
|
|
@@ -309,6 +309,14 @@ module ActiveRecord
|
|
309
309
|
|
310
310
|
return true
|
311
311
|
end
|
312
|
+
|
313
|
+
def get_session_model(env, sid)
|
314
|
+
if env[ENV_SESSION_OPTIONS_KEY][:id].nil?
|
315
|
+
env[SESSION_RECORD_KEY] = find_session(sid)
|
316
|
+
else
|
317
|
+
env[SESSION_RECORD_KEY] ||= find_session(sid)
|
318
|
+
end
|
319
|
+
end
|
312
320
|
|
313
321
|
def find_session(id)
|
314
322
|
@@session_class.find_by_session_id(id) ||
|
@@ -15,27 +15,57 @@ module ActiveRecord
|
|
15
15
|
base.class_inheritable_accessor :record_timestamps, :instance_writer => false
|
16
16
|
base.record_timestamps = true
|
17
17
|
end
|
18
|
+
|
19
|
+
# Saves the record with the updated_at/on attributes set to the current time.
|
20
|
+
# If the save fails because of validation errors, an ActiveRecord::RecordInvalid exception is raised.
|
21
|
+
# If an attribute name is passed, that attribute is used for the touch instead of the updated_at/on attributes.
|
22
|
+
#
|
23
|
+
# Examples:
|
24
|
+
#
|
25
|
+
# product.touch # updates updated_at
|
26
|
+
# product.touch(:designed_at) # updates the designed_at attribute
|
27
|
+
def touch(attribute = nil)
|
28
|
+
current_time = current_time_from_proper_timezone
|
29
|
+
|
30
|
+
if attribute
|
31
|
+
write_attribute(attribute, current_time)
|
32
|
+
else
|
33
|
+
write_attribute('updated_at', current_time) if respond_to?(:updated_at)
|
34
|
+
write_attribute('updated_on', current_time) if respond_to?(:updated_on)
|
35
|
+
end
|
36
|
+
|
37
|
+
save!
|
38
|
+
end
|
39
|
+
|
18
40
|
|
19
41
|
private
|
20
42
|
def create_with_timestamps #:nodoc:
|
21
43
|
if record_timestamps
|
22
|
-
|
23
|
-
write_attribute('created_at', t) if respond_to?(:created_at) && created_at.nil?
|
24
|
-
write_attribute('created_on', t) if respond_to?(:created_on) && created_on.nil?
|
44
|
+
current_time = current_time_from_proper_timezone
|
25
45
|
|
26
|
-
write_attribute('
|
27
|
-
write_attribute('
|
46
|
+
write_attribute('created_at', current_time) if respond_to?(:created_at) && created_at.nil?
|
47
|
+
write_attribute('created_on', current_time) if respond_to?(:created_on) && created_on.nil?
|
48
|
+
|
49
|
+
write_attribute('updated_at', current_time) if respond_to?(:updated_at) && updated_at.nil?
|
50
|
+
write_attribute('updated_on', current_time) if respond_to?(:updated_on) && updated_on.nil?
|
28
51
|
end
|
52
|
+
|
29
53
|
create_without_timestamps
|
30
54
|
end
|
31
55
|
|
32
56
|
def update_with_timestamps(*args) #:nodoc:
|
33
57
|
if record_timestamps && (!partial_updates? || changed?)
|
34
|
-
|
35
|
-
|
36
|
-
write_attribute('
|
58
|
+
current_time = current_time_from_proper_timezone
|
59
|
+
|
60
|
+
write_attribute('updated_at', current_time) if respond_to?(:updated_at)
|
61
|
+
write_attribute('updated_on', current_time) if respond_to?(:updated_on)
|
37
62
|
end
|
63
|
+
|
38
64
|
update_without_timestamps(*args)
|
39
65
|
end
|
66
|
+
|
67
|
+
def current_time_from_proper_timezone
|
68
|
+
self.class.default_timezone == :utc ? Time.now.utc : Time.now
|
69
|
+
end
|
40
70
|
end
|
41
|
-
end
|
71
|
+
end
|