activerecord 3.0.4 → 3.0.5.rc1

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 CHANGED
@@ -1,4 +1,36 @@
1
- *Rails 3.0.4 (unreleased)*
1
+ *Rails 3.0.5 (unreleased)*
2
+
3
+ * Model.where(:column => 1).where(:column => 2) will always produce an AND
4
+ query.
5
+
6
+ [Aaron Patterson]
7
+
8
+ * Deprecated support for interpolated association conditions in the form of :conditions => 'foo = #{bar}'.
9
+
10
+ Instead, you should use a proc, like so:
11
+
12
+ Before:
13
+
14
+ has_many :things, :conditions => 'foo = #{bar}'
15
+
16
+ After:
17
+
18
+ has_many :things, :conditions => proc { "foo = #{bar}" }
19
+
20
+ Inside the proc, 'self' is the object which is the owner of the association, unless you are
21
+ eager loading the association, in which case 'self' is the class which the association is within.
22
+
23
+ You can have any "normal" conditions inside the proc, so the following will work too:
24
+
25
+ has_many :things, :conditions => proc { ["foo = ?", bar] }
26
+
27
+ Previously :insert_sql and :delete_sql on has_and_belongs_to_many association allowed you to call
28
+ 'record' to get the record being inserted or deleted. This is now passed as an argument to
29
+ the proc.
30
+
31
+ [Jon Leighton]
32
+
33
+ *Rails 3.0.4*
2
34
 
3
35
  * Added deprecation warning for has_and_belongs_to_many associations where the join table has
4
36
  additional attributes other than the keys. Access to these attributes is removed in 3.1.
@@ -387,15 +387,27 @@ module ActiveRecord
387
387
  end
388
388
  end
389
389
 
390
-
391
- def interpolate_sql_for_preload(sql)
392
- instance_eval("%@#{sql.gsub('@', '\@')}@", __FILE__, __LINE__)
390
+ def process_conditions_for_preload(conditions, klass = self)
391
+ sanitized = klass.send(:sanitize_sql, conditions)
392
+
393
+ if sanitized =~ /\#\{.*\}/
394
+ ActiveSupport::Deprecation.warn(
395
+ 'String-based interpolation of association conditions is deprecated. Please use a ' \
396
+ 'proc instead. So, for example, has_many :older_friends, :conditions => \'age > #{age}\' ' \
397
+ 'should be changed to has_many :older_friends, :conditions => proc { "age > #{age}" }.'
398
+ )
399
+ instance_eval("%@#{sanitized.gsub('@', '\@')}@", __FILE__, __LINE__)
400
+ elsif conditions.respond_to?(:to_proc)
401
+ klass.send(:sanitize_sql, instance_eval(&conditions))
402
+ else
403
+ sanitized
404
+ end
393
405
  end
394
406
 
395
407
  def append_conditions(reflection, preload_options)
396
408
  sql = ""
397
- sql << " AND (#{interpolate_sql_for_preload(reflection.sanitized_conditions)})" if reflection.sanitized_conditions
398
- sql << " AND (#{sanitize_sql preload_options[:conditions]})" if preload_options[:conditions]
409
+ sql << " AND (#{process_conditions_for_preload(reflection.options[:conditions], reflection.klass)})" if reflection.options[:conditions]
410
+ sql << " AND (#{process_conditions_for_preload(preload_options[:conditions])})" if preload_options[:conditions]
399
411
  sql
400
412
  end
401
413
 
@@ -1416,8 +1416,8 @@ module ActiveRecord
1416
1416
  include Module.new {
1417
1417
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
1418
1418
  def destroy # def destroy
1419
- #{reflection.name}.clear # posts.clear
1420
1419
  super # super
1420
+ #{reflection.name}.clear # posts.clear
1421
1421
  end # end
1422
1422
  RUBY
1423
1423
  }
@@ -2221,7 +2221,7 @@ module ActiveRecord
2221
2221
 
2222
2222
  [through_reflection, reflection].each do |ref|
2223
2223
  if ref && ref.options[:conditions]
2224
- @join << interpolate_sql(sanitize_sql(ref.options[:conditions], aliased_table_name))
2224
+ @join << process_conditions(ref.options[:conditions], aliased_table_name)
2225
2225
  end
2226
2226
  end
2227
2227
 
@@ -2282,8 +2282,21 @@ module ActiveRecord
2282
2282
  table_alias_for table_name, @aliased_table_name
2283
2283
  end
2284
2284
 
2285
- def interpolate_sql(sql)
2286
- instance_eval("%@#{sql.gsub('@', '\@')}@", __FILE__, __LINE__)
2285
+ def process_conditions(conditions, table_name)
2286
+ sanitized = sanitize_sql(conditions, table_name)
2287
+
2288
+ if sanitized =~ /\#\{.*\}/
2289
+ ActiveSupport::Deprecation.warn(
2290
+ 'String-based interpolation of association conditions is deprecated. Please use a ' \
2291
+ 'proc instead. So, for example, has_many :older_friends, :conditions => \'age > #{age}\' ' \
2292
+ 'should be changed to has_many :older_friends, :conditions => proc { "age > #{age}" }.'
2293
+ )
2294
+ instance_eval("%@#{sanitized.gsub('@', '\@')}@", __FILE__, __LINE__)
2295
+ elsif conditions.respond_to?(:to_proc)
2296
+ conditions = sanitize_sql(instance_eval(&conditions), table_name)
2297
+ else
2298
+ sanitized
2299
+ end
2287
2300
  end
2288
2301
  end
2289
2302
  end
@@ -185,9 +185,11 @@ module ActiveRecord
185
185
  def count(column_name = nil, options = {})
186
186
  column_name, options = nil, column_name if column_name.is_a?(Hash)
187
187
 
188
- if @reflection.options[:counter_sql] && !options.blank?
189
- raise ArgumentError, "If finder_sql/counter_sql is used then options cannot be passed"
190
- elsif @reflection.options[:counter_sql]
188
+ if @reflection.options[:finder_sql] || @reflection.options[:counter_sql]
189
+ unless options.blank?
190
+ raise ArgumentError, "If finder_sql/counter_sql is used then options cannot be passed"
191
+ end
192
+
191
193
  @reflection.klass.count_by_sql(@counter_sql)
192
194
  else
193
195
 
@@ -379,11 +381,10 @@ module ActiveRecord
379
381
 
380
382
  def construct_counter_sql
381
383
  if @reflection.options[:counter_sql]
382
- @counter_sql = interpolate_sql(@reflection.options[:counter_sql])
384
+ @counter_sql = interpolate_and_sanitize_sql(@reflection.options[:counter_sql])
383
385
  elsif @reflection.options[:finder_sql]
384
386
  # replace the SELECT clause with COUNT(*), preserving any hints within /* ... */
385
- @reflection.options[:counter_sql] = @reflection.options[:finder_sql].sub(/SELECT\b(\/\*.*?\*\/ )?(.*)\bFROM\b/im) { "SELECT #{$1}COUNT(*) FROM" }
386
- @counter_sql = interpolate_sql(@reflection.options[:counter_sql])
387
+ @counter_sql = interpolate_and_sanitize_sql(@reflection.options[:finder_sql]).sub(/SELECT\b(\/\*.*?\*\/ )?(.*)\bFROM\b/im) { "SELECT #{$1}COUNT(*) FROM" }
387
388
  else
388
389
  @counter_sql = @finder_sql
389
390
  end
@@ -102,7 +102,7 @@ module ActiveRecord
102
102
  # Returns the SQL string that corresponds to the <tt>:conditions</tt>
103
103
  # option of the macro, if given, or +nil+ otherwise.
104
104
  def conditions
105
- @conditions ||= interpolate_sql(@reflection.sanitized_conditions) if @reflection.sanitized_conditions
105
+ @conditions ||= @reflection.options[:conditions] && interpolate_and_sanitize_sql(@reflection.options[:conditions])
106
106
  end
107
107
  alias :sql_conditions :conditions
108
108
 
@@ -161,8 +161,8 @@ module ActiveRecord
161
161
  @reflection.options[:dependent]
162
162
  end
163
163
 
164
- def interpolate_sql(sql, record = nil)
165
- @owner.send(:interpolate_sql, sql, record)
164
+ def interpolate_and_sanitize_sql(sql, record = nil, sanitize_klass = @reflection.klass)
165
+ @owner.send(:interpolate_and_sanitize_sql, sql, record, sanitize_klass)
166
166
  end
167
167
 
168
168
  # Forwards the call to the reflection class.
@@ -24,7 +24,7 @@ module ActiveRecord
24
24
  end
25
25
 
26
26
  def conditions
27
- @conditions ||= interpolate_sql(association_class.send(:sanitize_sql, @reflection.options[:conditions])) if @reflection.options[:conditions]
27
+ @conditions ||= @reflection.options[:conditions] && interpolate_and_sanitize_sql(@reflection.options[:conditions], nil, association_class)
28
28
  end
29
29
 
30
30
  private
@@ -52,7 +52,7 @@ module ActiveRecord
52
52
  end
53
53
 
54
54
  if @reflection.options[:insert_sql]
55
- @owner.connection.insert(interpolate_sql(@reflection.options[:insert_sql], record))
55
+ @owner.connection.insert(interpolate_and_sanitize_sql(@reflection.options[:insert_sql], record))
56
56
  else
57
57
  relation = Arel::Table.new(@reflection.options[:join_table])
58
58
  timestamps = record_timestamp_columns(record)
@@ -81,7 +81,7 @@ module ActiveRecord
81
81
 
82
82
  def delete_records(records)
83
83
  if sql = @reflection.options[:delete_sql]
84
- records.each { |record| @owner.connection.delete(interpolate_sql(sql, record)) }
84
+ records.each { |record| @owner.connection.delete(interpolate_and_sanitize_sql(sql, record)) }
85
85
  else
86
86
  relation = Arel::Table.new(@reflection.options[:join_table])
87
87
  relation.where(relation[@reflection.primary_key_name].eq(@owner.id).
@@ -92,7 +92,7 @@ module ActiveRecord
92
92
 
93
93
  def construct_sql
94
94
  if @reflection.options[:finder_sql]
95
- @finder_sql = interpolate_sql(@reflection.options[:finder_sql])
95
+ @finder_sql = interpolate_and_sanitize_sql(@reflection.options[:finder_sql])
96
96
  else
97
97
  @finder_sql = "#{@owner.connection.quote_table_name @reflection.options[:join_table]}.#{@reflection.primary_key_name} = #{owner_quoted_id} "
98
98
  @finder_sql << " AND (#{conditions})" if conditions
@@ -35,7 +35,7 @@ module ActiveRecord
35
35
  def count_records
36
36
  count = if has_cached_counter?
37
37
  @owner.send(:read_attribute, cached_counter_attribute_name)
38
- elsif @reflection.options[:counter_sql]
38
+ elsif @reflection.options[:finder_sql] || @reflection.options[:counter_sql]
39
39
  @reflection.klass.count_by_sql(@counter_sql)
40
40
  else
41
41
  @reflection.klass.count(:conditions => @counter_sql, :include => @reflection.options[:include])
@@ -90,7 +90,7 @@ module ActiveRecord
90
90
  def construct_sql
91
91
  case
92
92
  when @reflection.options[:finder_sql]
93
- @finder_sql = interpolate_sql(@reflection.options[:finder_sql])
93
+ @finder_sql = interpolate_and_sanitize_sql(@reflection.options[:finder_sql])
94
94
 
95
95
  when @reflection.options[:as]
96
96
  @finder_sql =
@@ -87,7 +87,7 @@ module ActiveRecord
87
87
  def construct_sql
88
88
  case
89
89
  when @reflection.options[:finder_sql]
90
- @finder_sql = interpolate_sql(@reflection.options[:finder_sql])
90
+ @finder_sql = interpolate_and_sanitize_sql(@reflection.options[:finder_sql])
91
91
 
92
92
  @finder_sql = "#{@reflection.quoted_table_name}.#{@reflection.primary_key_name} = #{owner_quoted_id}"
93
93
  @finder_sql << " AND (#{conditions})" if conditions
@@ -123,7 +123,7 @@ module ActiveRecord
123
123
  all = []
124
124
 
125
125
  [association_conditions, source_conditions].each do |conditions|
126
- all << interpolate_sql(sanitize_sql(conditions)) if conditions
126
+ all << interpolate_and_sanitize_sql(conditions) if conditions
127
127
  end
128
128
 
129
129
  all << through_conditions if through_conditions
@@ -136,11 +136,11 @@ module ActiveRecord
136
136
  def build_through_conditions
137
137
  conditions = @reflection.through_reflection.options[:conditions]
138
138
  if conditions.is_a?(Hash)
139
- interpolate_sql(@reflection.through_reflection.klass.send(:sanitize_sql, conditions)).gsub(
139
+ interpolate_and_sanitize_sql(conditions, nil, @reflection.through_reflection.klass).gsub(
140
140
  @reflection.quoted_table_name,
141
141
  @reflection.through_reflection.quoted_table_name)
142
142
  elsif conditions
143
- interpolate_sql(sanitize_sql(conditions))
143
+ interpolate_and_sanitize_sql(conditions)
144
144
  end
145
145
  end
146
146
 
@@ -37,12 +37,13 @@ module ActiveRecord
37
37
  def define_method_attribute=(attr_name)
38
38
  if create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name])
39
39
  method_body, line = <<-EOV, __LINE__ + 1
40
- def #{attr_name}=(time)
40
+ def #{attr_name}=(original_time)
41
+ time = original_time.dup unless original_time.nil?
41
42
  unless time.acts_like?(:time)
42
43
  time = time.is_a?(String) ? Time.zone.parse(time) : time.to_time rescue time
43
44
  end
44
45
  time = time.in_time_zone rescue nil if time
45
- write_attribute(:#{attr_name}, time)
46
+ write_attribute(:#{attr_name}, (time || original_time))
46
47
  end
47
48
  EOV
48
49
  generated_attribute_methods.module_eval(method_body, __FILE__, line)
@@ -879,8 +879,8 @@ module ActiveRecord #:nodoc:
879
879
  # It is recommended to use block form of unscoped because chaining unscoped with <tt>named_scope</tt>
880
880
  # does not work. Assuming that <tt>published</tt> is a <tt>named_scope</tt> following two statements are same.
881
881
  #
882
- # Post.unscoped.published
883
- # Post.published
882
+ # Post.unscoped.published
883
+ # Post.published
884
884
  def unscoped #:nodoc:
885
885
  block_given? ? relation.scoping { yield } : relation
886
886
  end
@@ -1606,7 +1606,7 @@ MSG
1606
1606
  self.class.columns_hash[name.to_s]
1607
1607
  end
1608
1608
 
1609
- # Returns true if +comparison_object+ is the same exact object, or +comparison_object+
1609
+ # Returns true if +comparison_object+ is the same exact object, or +comparison_object+
1610
1610
  # is of the same type and +self+ has an ID and it is equal to +comparison_object.id+.
1611
1611
  #
1612
1612
  # Note that new records are different from any other record by definition, unless the
@@ -1730,10 +1730,21 @@ MSG
1730
1730
  self.class.connection.quote(value, column)
1731
1731
  end
1732
1732
 
1733
- # Interpolate custom SQL string in instance context.
1734
- # Optional record argument is meant for custom insert_sql.
1735
- def interpolate_sql(sql, record = nil)
1736
- instance_eval("%@#{sql.gsub('@', '\@')}@", __FILE__, __LINE__)
1733
+ def interpolate_and_sanitize_sql(sql, record = nil, sanitize_klass = self.class)
1734
+ sanitized = sanitize_klass.send(:sanitize_sql, sql)
1735
+
1736
+ if sanitized =~ /\#\{.*\}/
1737
+ ActiveSupport::Deprecation.warn(
1738
+ 'String-based interpolation of association conditions is deprecated. Please use a ' \
1739
+ 'proc instead. So, for example, has_many :older_friends, :conditions => \'age > #{age}\' ' \
1740
+ 'should be changed to has_many :older_friends, :conditions => proc { "age > #{age}" }.'
1741
+ )
1742
+ instance_eval("%@#{sanitized.gsub('@', '\@')}@", __FILE__, __LINE__)
1743
+ elsif sql.respond_to?(:to_proc)
1744
+ sanitize_klass.send(:sanitize_sql, instance_exec(record, &sql))
1745
+ else
1746
+ sanitized
1747
+ end
1737
1748
  end
1738
1749
 
1739
1750
  # Instantiates objects for all attribute classes that needs more than one constructor parameter. This is done
@@ -253,13 +253,17 @@ module ActiveRecord
253
253
 
254
254
  # Sanitizes the given LIMIT parameter in order to prevent SQL injection.
255
255
  #
256
- # +limit+ may be anything that can evaluate to a string via #to_s. It
257
- # should look like an integer, or a comma-delimited list of integers.
256
+ # The +limit+ may be anything that can evaluate to a string via #to_s. It
257
+ # should look like an integer, or a comma-delimited list of integers, or
258
+ # an Arel SQL literal.
258
259
  #
260
+ # Returns Integer and Arel::Nodes::SqlLiteral limits as is.
259
261
  # Returns the sanitized limit parameter, either as an integer, or as a
260
262
  # string which contains a comma-delimited list of integers.
261
263
  def sanitize_limit(limit)
262
- if limit.to_s =~ /,/
264
+ if limit.is_a?(Integer) || limit.is_a?(Arel::Nodes::SqlLiteral)
265
+ limit
266
+ elsif limit.to_s =~ /,/
263
267
  Arel.sql limit.to_s.split(',').map{ |i| Integer(i) }.join(',')
264
268
  else
265
269
  Integer(limit)
@@ -40,7 +40,7 @@ module ActiveRecord
40
40
  #
41
41
  # Database-specific information on row locking:
42
42
  # MySQL: http://dev.mysql.com/doc/refman/5.1/en/innodb-locking-reads.html
43
- # PostgreSQL: http://www.postgresql.org/docs/8.1/interactive/sql-select.html#SQL-FOR-UPDATE-SHARE
43
+ # PostgreSQL: http://www.postgresql.org/docs/current/interactive/sql-select.html#SQL-FOR-UPDATE-SHARE
44
44
  module Pessimistic
45
45
  # Obtain a row lock on this record. Reloads the record to obtain the requested
46
46
  # lock. Pass an SQL locking clause to append the end of the SELECT statement
@@ -108,10 +108,17 @@ module ActiveRecord
108
108
 
109
109
  def define_callbacks(klass)
110
110
  observer = self
111
+ observer_name = observer.class.name.underscore.gsub('/', '__')
111
112
 
112
113
  ActiveRecord::Callbacks::CALLBACKS.each do |callback|
113
114
  next unless respond_to?(callback)
114
- klass.send(callback){|record| observer.send(callback, record)}
115
+ callback_meth = :"_notify_#{observer_name}_for_#{callback}"
116
+ unless klass.respond_to?(callback_meth)
117
+ klass.send(:define_method, callback_meth) do
118
+ observer.send(callback, self)
119
+ end
120
+ klass.send(callback, callback_meth)
121
+ end
115
122
  end
116
123
  end
117
124
  end
@@ -276,7 +276,7 @@ module ActiveRecord
276
276
  case operation
277
277
  when 'count' then value.to_i
278
278
  when 'sum' then type_cast_using_column(value || '0', column)
279
- when 'average' then value.try(:to_d)
279
+ when 'average' then value.respond_to?(:to_d) ? value.to_d : value
280
280
  else type_cast_using_column(value, column)
281
281
  end
282
282
  end
@@ -206,7 +206,7 @@ module ActiveRecord
206
206
 
207
207
  groups.each do |_, eqls|
208
208
  test = eqls.inject(eqls.shift) do |memo, expr|
209
- memo.or(expr)
209
+ memo.and(expr)
210
210
  end
211
211
  arel = arel.where(test)
212
212
  end
@@ -67,7 +67,10 @@ module ActiveRecord
67
67
  merged_relation
68
68
  end
69
69
 
70
- alias :& :merge
70
+ def &(r)
71
+ ActiveSupport::Deprecation.warn "Using & to merge relations has been deprecated and will be removed in Rails 3.1. Please use the relation's merge method, instead"
72
+ merge(r)
73
+ end
71
74
 
72
75
  def except(*skips)
73
76
  result = self.class.new(@klass, table)
@@ -2,8 +2,8 @@ module ActiveRecord
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 3
4
4
  MINOR = 0
5
- TINY = 4
6
- PRE = nil
5
+ TINY = 5
6
+ PRE = "rc1"
7
7
 
8
8
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
9
9
  end
metadata CHANGED
@@ -1,13 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord
3
3
  version: !ruby/object:Gem::Version
4
- hash: 15
5
- prerelease: false
4
+ hash: 15424095
5
+ prerelease: 6
6
6
  segments:
7
7
  - 3
8
8
  - 0
9
- - 4
10
- version: 3.0.4
9
+ - 5
10
+ - rc
11
+ - 1
12
+ version: 3.0.5.rc1
11
13
  platform: ruby
12
14
  authors:
13
15
  - David Heinemeier Hansson
@@ -15,7 +17,7 @@ autorequire:
15
17
  bindir: bin
16
18
  cert_chain: []
17
19
 
18
- date: 2011-02-09 00:00:00 +13:00
20
+ date: 2011-02-22 00:00:00 -08:00
19
21
  default_executable:
20
22
  dependencies:
21
23
  - !ruby/object:Gem::Dependency
@@ -26,12 +28,14 @@ dependencies:
26
28
  requirements:
27
29
  - - "="
28
30
  - !ruby/object:Gem::Version
29
- hash: 15
31
+ hash: 15424095
30
32
  segments:
31
33
  - 3
32
34
  - 0
33
- - 4
34
- version: 3.0.4
35
+ - 5
36
+ - rc
37
+ - 1
38
+ version: 3.0.5.rc1
35
39
  type: :runtime
36
40
  version_requirements: *id001
37
41
  - !ruby/object:Gem::Dependency
@@ -42,12 +46,14 @@ dependencies:
42
46
  requirements:
43
47
  - - "="
44
48
  - !ruby/object:Gem::Version
45
- hash: 15
49
+ hash: 15424095
46
50
  segments:
47
51
  - 3
48
52
  - 0
49
- - 4
50
- version: 3.0.4
53
+ - 5
54
+ - rc
55
+ - 1
56
+ version: 3.0.5.rc1
51
57
  type: :runtime
52
58
  version_requirements: *id002
53
59
  - !ruby/object:Gem::Dependency
@@ -208,16 +214,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
208
214
  required_rubygems_version: !ruby/object:Gem::Requirement
209
215
  none: false
210
216
  requirements:
211
- - - ">="
217
+ - - ">"
212
218
  - !ruby/object:Gem::Version
213
- hash: 3
219
+ hash: 25
214
220
  segments:
215
- - 0
216
- version: "0"
221
+ - 1
222
+ - 3
223
+ - 1
224
+ version: 1.3.1
217
225
  requirements: []
218
226
 
219
227
  rubyforge_project: activerecord
220
- rubygems_version: 1.3.7
228
+ rubygems_version: 1.5.2
221
229
  signing_key:
222
230
  specification_version: 3
223
231
  summary: Object-relational mapper framework (part of Rails).