activerecord 3.0.0.beta2 → 3.0.0.beta3

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.

Files changed (28) hide show
  1. data/CHANGELOG +11 -1
  2. data/lib/active_record/associations.rb +26 -54
  3. data/lib/active_record/associations/association_collection.rb +13 -2
  4. data/lib/active_record/associations/association_proxy.rb +3 -1
  5. data/lib/active_record/associations/through_association_scope.rb +1 -1
  6. data/lib/active_record/attribute_methods/dirty.rb +6 -0
  7. data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -4
  8. data/lib/active_record/base.rb +66 -62
  9. data/lib/active_record/callbacks.rb +4 -2
  10. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -3
  11. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +5 -3
  12. data/lib/active_record/connection_adapters/abstract_adapter.rb +1 -0
  13. data/lib/active_record/connection_adapters/postgresql_adapter.rb +45 -45
  14. data/lib/active_record/migration.rb +1 -1
  15. data/lib/active_record/named_scope.rb +26 -109
  16. data/lib/active_record/railties/databases.rake +3 -7
  17. data/lib/active_record/reflection.rb +11 -0
  18. data/lib/active_record/relation.rb +20 -1
  19. data/lib/active_record/relation/finder_methods.rb +18 -2
  20. data/lib/active_record/relation/predicate_builder.rb +4 -2
  21. data/lib/active_record/relation/query_methods.rb +26 -8
  22. data/lib/active_record/relation/spawn_methods.rb +9 -6
  23. data/lib/active_record/serializers/xml_serializer.rb +2 -1
  24. data/lib/active_record/validations/uniqueness.rb +3 -1
  25. data/lib/active_record/version.rb +1 -1
  26. data/lib/rails/generators/active_record/model/model_generator.rb +5 -0
  27. data/lib/rails/generators/active_record/model/templates/module.rb +5 -0
  28. metadata +8 -7
data/CHANGELOG CHANGED
@@ -1,4 +1,14 @@
1
- *Rails 3.0.0 [Beta 2] (pending)*
1
+ *Rails 3.0.0 [beta 3] (April 13th, 2010)*
2
+
3
+ * Add Relation extensions. [Pratik Naik]
4
+
5
+ users = User.where(:admin => true).extending(User::AdminPowers)
6
+
7
+ latest_users = User.order('created_at DESC') do
8
+ def posts_count
9
+ Post.count(:user_id => to_a.map(&:id))
10
+ end
11
+ end
2
12
 
3
13
  * To prefix the table names of all models in a module, define self.table_name_prefix on the module. #4032 [Andrew White]
4
14
 
@@ -1,5 +1,6 @@
1
- require 'active_support/core_ext/module/delegation'
1
+ require 'active_support/core_ext/array/wrap'
2
2
  require 'active_support/core_ext/enumerable'
3
+ require 'active_support/core_ext/module/delegation'
3
4
  require 'active_support/core_ext/object/blank'
4
5
 
5
6
  module ActiveRecord
@@ -1495,14 +1496,6 @@ module ActiveRecord
1495
1496
  # finder conditions.
1496
1497
  def configure_dependency_for_has_many(reflection, extra_conditions = nil)
1497
1498
  if reflection.options.include?(:dependent)
1498
- # Add polymorphic type if the :as option is present
1499
- dependent_conditions = []
1500
- dependent_conditions << "#{reflection.primary_key_name} = \#{record.#{reflection.name}.send(:owner_quoted_id)}"
1501
- dependent_conditions << "#{reflection.options[:as]}_type = '#{base_class.name}'" if reflection.options[:as]
1502
- dependent_conditions << sanitize_sql(reflection.options[:conditions], reflection.table_name) if reflection.options[:conditions]
1503
- dependent_conditions << extra_conditions if extra_conditions
1504
- dependent_conditions = dependent_conditions.collect {|where| "(#{where})" }.join(" AND ")
1505
- dependent_conditions = dependent_conditions.gsub('@', '\@')
1506
1499
  case reflection.options[:dependent]
1507
1500
  when :destroy
1508
1501
  method_name = "has_many_dependent_destroy_for_#{reflection.name}".to_sym
@@ -1511,51 +1504,30 @@ module ActiveRecord
1511
1504
  end
1512
1505
  before_destroy method_name
1513
1506
  when :delete_all
1514
- # before_destroy do |record|
1515
- # self.class.send(:delete_all_has_many_dependencies,
1516
- # record,
1517
- # "posts",
1518
- # Post,
1519
- # %@...@) # this is a string literal like %(...)
1520
- # end
1521
- # end
1522
- module_eval <<-CALLBACK
1523
- before_destroy do |record|
1524
- self.class.send(:delete_all_has_many_dependencies,
1525
- record,
1526
- "#{reflection.name}",
1527
- #{reflection.class_name},
1528
- %@#{dependent_conditions}@)
1529
- end
1530
- CALLBACK
1507
+ before_destroy do |record|
1508
+ self.class.send(:delete_all_has_many_dependencies,
1509
+ record,
1510
+ reflection.name,
1511
+ reflection.klass,
1512
+ reflection.dependent_conditions(record, self.class, extra_conditions))
1513
+ end
1531
1514
  when :nullify
1532
- # before_destroy do |record|
1533
- # self.class.send(:nullify_has_many_dependencies,
1534
- # record,
1535
- # "posts",
1536
- # Post,
1537
- # "user_id",
1538
- # %@...@) # this is a string literal like %(...)
1539
- # end
1540
- # end
1541
- module_eval <<-CALLBACK
1542
- before_destroy do |record|
1543
- self.class.send(:nullify_has_many_dependencies,
1544
- record,
1545
- "#{reflection.name}",
1546
- #{reflection.class_name},
1547
- "#{reflection.primary_key_name}",
1548
- %@#{dependent_conditions}@)
1549
- end
1550
- CALLBACK
1551
- when :restrict
1552
- method_name = "has_many_dependent_restrict_for_#{reflection.name}".to_sym
1553
- define_method(method_name) do
1554
- unless send(reflection.name).empty?
1555
- raise DeleteRestrictionError.new(reflection)
1556
- end
1515
+ before_destroy do |record|
1516
+ self.class.send(:nullify_has_many_dependencies,
1517
+ record,
1518
+ reflection.name,
1519
+ reflection.klass,
1520
+ reflection.primary_key_name,
1521
+ reflection.dependent_conditions(record, self.class, extra_conditions))
1522
+ end
1523
+ when :restrict
1524
+ method_name = "has_many_dependent_restrict_for_#{reflection.name}".to_sym
1525
+ define_method(method_name) do
1526
+ unless send(reflection.name).empty?
1527
+ raise DeleteRestrictionError.new(reflection)
1557
1528
  end
1558
- before_destroy method_name
1529
+ end
1530
+ before_destroy method_name
1559
1531
  else
1560
1532
  raise ArgumentError, "The :dependent option expects either :destroy, :delete_all, :nullify or :restrict (#{reflection.options[:dependent].inspect})"
1561
1533
  end
@@ -1736,9 +1708,9 @@ module ActiveRecord
1736
1708
  silence_warnings do
1737
1709
  self.parent.const_set(extension_module_name, Module.new(&block_extension))
1738
1710
  end
1739
- Array(extensions).push("#{self.parent}::#{extension_module_name}".constantize)
1711
+ Array.wrap(extensions).push("#{self.parent}::#{extension_module_name}".constantize)
1740
1712
  else
1741
- Array(extensions)
1713
+ Array.wrap(extensions)
1742
1714
  end
1743
1715
  end
1744
1716
 
@@ -1,4 +1,5 @@
1
1
  require 'set'
2
+ require 'active_support/core_ext/array/wrap'
2
3
 
3
4
  module ActiveRecord
4
5
  module Associations
@@ -98,13 +99,14 @@ module ActiveRecord
98
99
  if @target.is_a?(Array)
99
100
  @target.to_ary
100
101
  else
101
- Array(@target)
102
+ Array.wrap(@target)
102
103
  end
103
104
  end
104
105
  alias_method :to_a, :to_ary
105
106
 
106
107
  def reset
107
108
  reset_target!
109
+ reset_named_scopes_cache!
108
110
  @loaded = false
109
111
  end
110
112
 
@@ -146,7 +148,7 @@ module ActiveRecord
146
148
  # has_many :books
147
149
  # end
148
150
  #
149
- # Author.find(:first).books.transaction do
151
+ # Author.first.books.transaction do
150
152
  # # same effect as calling Book.transaction
151
153
  # end
152
154
  def transaction(*args)
@@ -162,6 +164,7 @@ module ActiveRecord
162
164
  load_target
163
165
  delete(@target)
164
166
  reset_target!
167
+ reset_named_scopes_cache!
165
168
  end
166
169
 
167
170
  # Calculate sum using SQL, not Enumerable
@@ -250,6 +253,7 @@ module ActiveRecord
250
253
  load_target
251
254
  destroy(@target)
252
255
  reset_target!
256
+ reset_named_scopes_cache!
253
257
  end
254
258
 
255
259
  def create(attrs = {})
@@ -405,6 +409,9 @@ module ActiveRecord
405
409
  else
406
410
  super
407
411
  end
412
+ elsif @reflection.klass.scopes[method]
413
+ @_named_scopes_cache ||= {}
414
+ @_named_scopes_cache[method] ||= with_scope(construct_scope) { @reflection.klass.send(method, *args) }
408
415
  else
409
416
  with_scope(construct_scope) do
410
417
  if block_given?
@@ -425,6 +432,10 @@ module ActiveRecord
425
432
  @target = Array.new
426
433
  end
427
434
 
435
+ def reset_named_scopes_cache!
436
+ @_named_scopes_cache = {}
437
+ end
438
+
428
439
  def find_target
429
440
  records =
430
441
  if @reflection.options[:finder_sql]
@@ -1,3 +1,5 @@
1
+ require 'active_support/core_ext/array/wrap'
2
+
1
3
  module ActiveRecord
2
4
  module Associations
3
5
  # This is the root class of all association proxies:
@@ -55,7 +57,7 @@ module ActiveRecord
55
57
  @owner, @reflection = owner, reflection
56
58
  @updated = false
57
59
  reflection.check_validity!
58
- Array(reflection.options[:extend]).each { |ext| proxy_extend(ext) }
60
+ Array.wrap(reflection.options[:extend]).each { |ext| proxy_extend(ext) }
59
61
  reset
60
62
  end
61
63
 
@@ -135,7 +135,7 @@ module ActiveRecord
135
135
  def build_through_conditions
136
136
  conditions = @reflection.through_reflection.options[:conditions]
137
137
  if conditions.is_a?(Hash)
138
- interpolate_sql(sanitize_sql(conditions)).gsub(
138
+ interpolate_sql(@reflection.through_reflection.klass.send(:sanitize_sql, conditions)).gsub(
139
139
  @reflection.quoted_table_name,
140
140
  @reflection.through_reflection.quoted_table_name)
141
141
  elsif conditions
@@ -52,6 +52,8 @@ module ActiveRecord
52
52
  @changed_attributes.delete(attr) unless field_changed?(attr, old, value)
53
53
  else
54
54
  old = clone_attribute_value(:read_attribute, attr)
55
+ # Save Time objects as TimeWithZone if time_zone_aware_attributes == true
56
+ old = old.in_time_zone if clone_with_time_zone_conversion_attribute?(attr, old)
55
57
  @changed_attributes[attr] = old if field_changed?(attr, old, value)
56
58
  end
57
59
 
@@ -84,6 +86,10 @@ module ActiveRecord
84
86
 
85
87
  old != value
86
88
  end
89
+
90
+ def clone_with_time_zone_conversion_attribute?(attr, old)
91
+ old.class.name == "Time" && time_zone_aware_attributes && !skip_time_zone_conversion_for_attributes.include?(attr.to_sym)
92
+ end
87
93
  end
88
94
  end
89
95
  end
@@ -17,7 +17,7 @@ module ActiveRecord
17
17
  # This enhanced read method automatically converts the UTC time stored in the database to the time zone stored in Time.zone.
18
18
  def define_method_attribute(attr_name)
19
19
  if create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name])
20
- method_body = <<-EOV
20
+ method_body, line = <<-EOV, __LINE__ + 1
21
21
  def #{attr_name}(reload = false)
22
22
  cached = @attributes_cache['#{attr_name}']
23
23
  return cached if cached && !reload
@@ -25,7 +25,7 @@ module ActiveRecord
25
25
  @attributes_cache['#{attr_name}'] = time.acts_like?(:time) ? time.in_time_zone : time
26
26
  end
27
27
  EOV
28
- generated_attribute_methods.module_eval(method_body, __FILE__, __LINE__)
28
+ generated_attribute_methods.module_eval(method_body, __FILE__, line)
29
29
  else
30
30
  super
31
31
  end
@@ -35,7 +35,7 @@ module ActiveRecord
35
35
  # This enhanced write method will automatically convert the time passed to it to the zone stored in Time.zone.
36
36
  def define_method_attribute=(attr_name)
37
37
  if create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name])
38
- method_body = <<-EOV
38
+ method_body, line = <<-EOV, __LINE__ + 1
39
39
  def #{attr_name}=(time)
40
40
  unless time.acts_like?(:time)
41
41
  time = time.is_a?(String) ? Time.zone.parse(time) : time.to_time rescue time
@@ -44,7 +44,7 @@ module ActiveRecord
44
44
  write_attribute(:#{attr_name}, time)
45
45
  end
46
46
  EOV
47
- generated_attribute_methods.module_eval(method_body, __FILE__, __LINE__)
47
+ generated_attribute_methods.module_eval(method_body, __FILE__, line)
48
48
  else
49
49
  super
50
50
  end
@@ -3,6 +3,7 @@ require 'set'
3
3
  require 'active_support/benchmarkable'
4
4
  require 'active_support/dependencies'
5
5
  require 'active_support/time'
6
+ require 'active_support/core_ext/class/attribute'
6
7
  require 'active_support/core_ext/class/attribute_accessors'
7
8
  require 'active_support/core_ext/class/delegating_attributes'
8
9
  require 'active_support/core_ext/class/inheritable_attributes'
@@ -11,7 +12,7 @@ require 'active_support/core_ext/hash/deep_merge'
11
12
  require 'active_support/core_ext/hash/indifferent_access'
12
13
  require 'active_support/core_ext/hash/slice'
13
14
  require 'active_support/core_ext/string/behavior'
14
- require 'active_support/core_ext/object/singleton_class'
15
+ require 'active_support/core_ext/kernel/singleton_class'
15
16
  require 'active_support/core_ext/module/delegation'
16
17
  require 'active_support/core_ext/object/duplicable'
17
18
  require 'active_support/core_ext/object/blank'
@@ -56,15 +57,15 @@ module ActiveRecord #:nodoc:
56
57
  #
57
58
  # class User < ActiveRecord::Base
58
59
  # def self.authenticate_unsafely(user_name, password)
59
- # find(:first, :conditions => "user_name = '#{user_name}' AND password = '#{password}'")
60
+ # where("user_name = '#{user_name}' AND password = '#{password}'").first
60
61
  # end
61
62
  #
62
63
  # def self.authenticate_safely(user_name, password)
63
- # find(:first, :conditions => [ "user_name = ? AND password = ?", user_name, password ])
64
+ # where("user_name = ? AND password = ?", user_name, password).first
64
65
  # end
65
66
  #
66
67
  # def self.authenticate_safely_simply(user_name, password)
67
- # find(:first, :conditions => { :user_name => user_name, :password => password })
68
+ # where(:user_name => user_name, :password => password).first
68
69
  # end
69
70
  # end
70
71
  #
@@ -77,30 +78,30 @@ module ActiveRecord #:nodoc:
77
78
  # question mark is supposed to represent. In those cases, you can resort to named bind variables instead. That's done by replacing
78
79
  # the question marks with symbols and supplying a hash with values for the matching symbol keys:
79
80
  #
80
- # Company.find(:first, :conditions => [
81
+ # Company.where(
81
82
  # "id = :id AND name = :name AND division = :division AND created_at > :accounting_date",
82
83
  # { :id => 3, :name => "37signals", :division => "First", :accounting_date => '2005-01-01' }
83
- # ])
84
+ # ).first
84
85
  #
85
86
  # Similarly, a simple hash without a statement will generate conditions based on equality with the SQL AND
86
87
  # operator. For instance:
87
88
  #
88
- # Student.find(:all, :conditions => { :first_name => "Harvey", :status => 1 })
89
- # Student.find(:all, :conditions => params[:student])
89
+ # Student.where(:first_name => "Harvey", :status => 1)
90
+ # Student.where(params[:student])
90
91
  #
91
92
  # A range may be used in the hash to use the SQL BETWEEN operator:
92
93
  #
93
- # Student.find(:all, :conditions => { :grade => 9..12 })
94
+ # Student.where(:grade => 9..12)
94
95
  #
95
96
  # An array may be used in the hash to use the SQL IN operator:
96
97
  #
97
- # Student.find(:all, :conditions => { :grade => [9,11,12] })
98
+ # Student.where(:grade => [9,11,12])
98
99
  #
99
100
  # When joining tables, nested hashes or keys written in the form 'table_name.column_name' can be used to qualify the table name of a
100
101
  # particular condition. For instance:
101
102
  #
102
- # Student.find(:all, :conditions => { :schools => { :type => 'public' }}, :joins => :schools)
103
- # Student.find(:all, :conditions => { 'schools.type' => 'public' }, :joins => :schools)
103
+ # Student.joins(:schools).where(:schools => { :type => 'public' })
104
+ # Student.joins(:schools).where('schools.type' => 'public' )
104
105
  #
105
106
  # == Overwriting default accessors
106
107
  #
@@ -153,18 +154,18 @@ module ActiveRecord #:nodoc:
153
154
  # Dynamic attribute-based finders are a cleaner way of getting (and/or creating) objects by simple queries without turning to SQL. They work by
154
155
  # appending the name of an attribute to <tt>find_by_</tt>, <tt>find_last_by_</tt>, or <tt>find_all_by_</tt>, so you get finders like <tt>Person.find_by_user_name</tt>,
155
156
  # <tt>Person.find_all_by_last_name</tt>, and <tt>Payment.find_by_transaction_id</tt>. So instead of writing
156
- # <tt>Person.find(:first, :conditions => ["user_name = ?", user_name])</tt>, you just do <tt>Person.find_by_user_name(user_name)</tt>.
157
- # And instead of writing <tt>Person.find(:all, :conditions => ["last_name = ?", last_name])</tt>, you just do <tt>Person.find_all_by_last_name(last_name)</tt>.
157
+ # <tt>Person.where(:user_name => user_name).first</tt>, you just do <tt>Person.find_by_user_name(user_name)</tt>.
158
+ # And instead of writing <tt>Person.where(:last_name => last_name).all</tt>, you just do <tt>Person.find_all_by_last_name(last_name)</tt>.
158
159
  #
159
160
  # It's also possible to use multiple attributes in the same find by separating them with "_and_", so you get finders like
160
161
  # <tt>Person.find_by_user_name_and_password</tt> or even <tt>Payment.find_by_purchaser_and_state_and_country</tt>. So instead of writing
161
- # <tt>Person.find(:first, :conditions => ["user_name = ? AND password = ?", user_name, password])</tt>, you just do
162
+ # <tt>Person.where(:user_name => user_name, :password => password).first</tt>, you just do
162
163
  # <tt>Person.find_by_user_name_and_password(user_name, password)</tt>.
163
164
  #
164
- # It's even possible to use all the additional parameters to find. For example, the full interface for <tt>Payment.find_all_by_amount</tt>
165
- # is actually <tt>Payment.find_all_by_amount(amount, options)</tt>. And the full interface to <tt>Person.find_by_user_name</tt> is
166
- # actually <tt>Person.find_by_user_name(user_name, options)</tt>. So you could call <tt>Payment.find_all_by_amount(50, :order => "created_on")</tt>.
167
- # Also you may call <tt>Payment.find_last_by_amount(amount, options)</tt> returning the last record matching that amount and options.
165
+ # It's even possible to call these dynamic finder methods on relations and named scopes. For example :
166
+ #
167
+ # Payment.order("created_on").find_all_by_amount(50)
168
+ # Payment.pending.find_last_by_amount(100)
168
169
  #
169
170
  # The same dynamic finder style can be used to create the object if it doesn't already exist. This dynamic finder is called with
170
171
  # <tt>find_or_create_by_</tt> and will return the object if it already exists and otherwise creates it, then returns it. Protected attributes won't be set unless they are given in a block. For example:
@@ -224,7 +225,7 @@ module ActiveRecord #:nodoc:
224
225
  # class PriorityClient < Client; end
225
226
  #
226
227
  # When you do <tt>Firm.create(:name => "37signals")</tt>, this record will be saved in the companies table with type = "Firm". You can then
227
- # fetch this row again using <tt>Company.find(:first, "name = '37signals'")</tt> and it will return a Firm object.
228
+ # fetch this row again using <tt>Company.where(:name => '37signals').first</tt> and it will return a Firm object.
228
229
  #
229
230
  # If you don't have a type column defined in your table, single-table inheritance won't be triggered. In that case, it'll work just
230
231
  # like normal subclasses with no special magic for differentiating between them or reloading the right type with find.
@@ -341,15 +342,15 @@ module ActiveRecord #:nodoc:
341
342
  #
342
343
  # If you are organising your models within modules you can add a prefix to the models within a namespace by defining
343
344
  # a singleton method in the parent module called table_name_prefix which returns your chosen prefix.
344
- cattr_accessor :table_name_prefix, :instance_writer => false
345
- @@table_name_prefix = ""
345
+ class_attribute :table_name_prefix, :instance_writer => false
346
+ self.table_name_prefix = ""
346
347
 
347
348
  ##
348
349
  # :singleton-method:
349
350
  # Works like +table_name_prefix+, but appends instead of prepends (set to "_basecamp" gives "projects_basecamp",
350
351
  # "people_basecamp"). By default, the suffix is the empty string.
351
- cattr_accessor :table_name_suffix, :instance_writer => false
352
- @@table_name_suffix = ""
352
+ class_attribute :table_name_suffix, :instance_writer => false
353
+ self.table_name_suffix = ""
353
354
 
354
355
  ##
355
356
  # :singleton-method:
@@ -379,7 +380,7 @@ module ActiveRecord #:nodoc:
379
380
 
380
381
  ##
381
382
  # :singleton-method:
382
- # Specify whether or not to use timestamps for migration numbers
383
+ # Specify whether or not to use timestamps for migration versions
383
384
  cattr_accessor :timestamped_migrations , :instance_writer => false
384
385
  @@timestamped_migrations = true
385
386
 
@@ -1079,16 +1080,6 @@ module ActiveRecord #:nodoc:
1079
1080
  end
1080
1081
  end
1081
1082
 
1082
- # Nest the type name in the same module as this class.
1083
- # Bar is "MyApp::Business::Bar" relative to MyApp::Business::Foo
1084
- def type_name_with_module(type_name)
1085
- if store_full_sti_class
1086
- type_name
1087
- else
1088
- (/^::/ =~ type_name) ? type_name : "#{parent.name}::#{type_name}"
1089
- end
1090
- end
1091
-
1092
1083
  def construct_finder_arel(options = {}, scope = nil)
1093
1084
  relation = options.is_a?(Hash) ? unscoped.apply_finder_options(options) : unscoped.merge(options)
1094
1085
  relation = scope.merge(relation) if scope
@@ -1117,10 +1108,6 @@ module ActiveRecord #:nodoc:
1117
1108
  # It's even possible to use all the additional parameters to +find+. For example, the full interface for +find_all_by_amount+
1118
1109
  # is actually <tt>find_all_by_amount(amount, options)</tt>.
1119
1110
  #
1120
- # Also enables dynamic scopes like scoped_by_user_name(user_name) and scoped_by_user_name_and_password(user_name, password) that
1121
- # are turned into scoped(:conditions => ["user_name = ?", user_name]) and scoped(:conditions => ["user_name = ? AND password = ?", user_name, password])
1122
- # respectively.
1123
- #
1124
1111
  # Each dynamic finder, scope or initializer/creator is also defined in the class after it is first invoked, so that future
1125
1112
  # attempts to use it do not run through method_missing.
1126
1113
  def method_missing(method_id, *arguments, &block)
@@ -1138,7 +1125,7 @@ module ActiveRecord #:nodoc:
1138
1125
  attribute_names = match.attribute_names
1139
1126
  super unless all_attributes_exists?(attribute_names)
1140
1127
  if match.scope?
1141
- self.class_eval %{
1128
+ self.class_eval <<-METHOD, __FILE__, __LINE__ + 1
1142
1129
  def self.#{method_id}(*args) # def self.scoped_by_user_name_and_password(*args)
1143
1130
  options = args.extract_options! # options = args.extract_options!
1144
1131
  attributes = construct_attributes_from_arguments( # attributes = construct_attributes_from_arguments(
@@ -1147,7 +1134,7 @@ module ActiveRecord #:nodoc:
1147
1134
  #
1148
1135
  scoped(:conditions => attributes) # scoped(:conditions => attributes)
1149
1136
  end # end
1150
- }, __FILE__, __LINE__
1137
+ METHOD
1151
1138
  send(method_id, *arguments)
1152
1139
  end
1153
1140
  else
@@ -1196,12 +1183,12 @@ module ActiveRecord #:nodoc:
1196
1183
 
1197
1184
  protected
1198
1185
  # Scope parameters to method calls within the block. Takes a hash of method_name => parameters hash.
1199
- # method_name may be <tt>:find</tt> or <tt>:create</tt>. <tt>:find</tt> parameters may include the <tt>:conditions</tt>, <tt>:joins</tt>,
1200
- # <tt>:include</tt>, <tt>:offset</tt>, <tt>:limit</tt>, and <tt>:readonly</tt> options. <tt>:create</tt> parameters are an attributes hash.
1186
+ # method_name may be <tt>:find</tt> or <tt>:create</tt>. <tt>:find</tt> parameter is <tt>Relation</tt> while
1187
+ # <tt>:create</tt> parameters are an attributes hash.
1201
1188
  #
1202
1189
  # class Article < ActiveRecord::Base
1203
1190
  # def self.create_with_scope
1204
- # with_scope(:find => { :conditions => "blog_id = 1" }, :create => { :blog_id => 1 }) do
1191
+ # with_scope(:find => where(:blog_id => 1), :create => { :blog_id => 1 }) do
1205
1192
  # find(1) # => SELECT * from articles WHERE blog_id = 1 AND id = 1
1206
1193
  # a = create(1)
1207
1194
  # a.blog_id # => 1
@@ -1210,20 +1197,20 @@ module ActiveRecord #:nodoc:
1210
1197
  # end
1211
1198
  #
1212
1199
  # In nested scopings, all previous parameters are overwritten by the innermost rule, with the exception of
1213
- # <tt>:conditions</tt>, <tt>:include</tt>, and <tt>:joins</tt> options in <tt>:find</tt>, which are merged.
1200
+ # <tt>where</tt>, <tt>includes</tt>, and <tt>joins</tt> operations in <tt>Relation</tt>, which are merged.
1214
1201
  #
1215
- # <tt>:joins</tt> options are uniqued so multiple scopes can join in the same table without table aliasing
1202
+ # <tt>joins</tt> operations are uniqued so multiple scopes can join in the same table without table aliasing
1216
1203
  # problems. If you need to join multiple tables, but still want one of the tables to be uniqued, use the
1217
1204
  # array of strings format for your joins.
1218
1205
  #
1219
1206
  # class Article < ActiveRecord::Base
1220
1207
  # def self.find_with_scope
1221
- # with_scope(:find => { :conditions => "blog_id = 1", :limit => 1 }, :create => { :blog_id => 1 }) do
1222
- # with_scope(:find => { :limit => 10 }) do
1223
- # find(:all) # => SELECT * from articles WHERE blog_id = 1 LIMIT 10
1208
+ # with_scope(:find => where(:blog_id => 1).limit(1), :create => { :blog_id => 1 }) do
1209
+ # with_scope(:find => limit(10)) do
1210
+ # all # => SELECT * from articles WHERE blog_id = 1 LIMIT 10
1224
1211
  # end
1225
- # with_scope(:find => { :conditions => "author_id = 3" }) do
1226
- # find(:all) # => SELECT * from articles WHERE blog_id = 1 AND author_id = 3 LIMIT 1
1212
+ # with_scope(:find => where(:author_id => 3)) do
1213
+ # all # => SELECT * from articles WHERE blog_id = 1 AND author_id = 3 LIMIT 1
1227
1214
  # end
1228
1215
  # end
1229
1216
  # end
@@ -1233,9 +1220,9 @@ module ActiveRecord #:nodoc:
1233
1220
  #
1234
1221
  # class Article < ActiveRecord::Base
1235
1222
  # def self.find_with_exclusive_scope
1236
- # with_scope(:find => { :conditions => "blog_id = 1", :limit => 1 }) do
1237
- # with_exclusive_scope(:find => { :limit => 10 })
1238
- # find(:all) # => SELECT * from articles LIMIT 10
1223
+ # with_scope(:find => where(:blog_id => 1).limit(1)) do
1224
+ # with_exclusive_scope(:find => limit(10))
1225
+ # all # => SELECT * from articles LIMIT 10
1239
1226
  # end
1240
1227
  # end
1241
1228
  # end
@@ -1288,11 +1275,15 @@ module ActiveRecord #:nodoc:
1288
1275
  with_scope(method_scoping, :overwrite, &block)
1289
1276
  end
1290
1277
 
1291
- def subclasses #:nodoc:
1278
+ # ActiveRecord::Base utilizes the inherited hook to know about new subclasses.
1279
+ # You can access the list of currently loaded ActiveRecord::Base subclasses using this accessor.
1280
+ def subclasses
1292
1281
  @@subclasses[self] ||= []
1293
1282
  @@subclasses[self] + extra = @@subclasses[self].inject([]) {|list, subclass| list + subclass.subclasses }
1294
1283
  end
1295
1284
 
1285
+ public :subclasses
1286
+
1296
1287
  # Sets the default options for the model. The format of the
1297
1288
  # <tt>options</tt> argument is the same as in find.
1298
1289
  #
@@ -1315,13 +1306,26 @@ module ActiveRecord #:nodoc:
1315
1306
  # Returns the class type of the record using the current module as a prefix. So descendants of
1316
1307
  # MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
1317
1308
  def compute_type(type_name)
1318
- modularized_name = type_name_with_module(type_name)
1319
- silence_warnings do
1320
- begin
1321
- class_eval(modularized_name, __FILE__, __LINE__)
1322
- rescue NameError
1323
- class_eval(type_name, __FILE__, __LINE__)
1309
+ if type_name.match(/^::/)
1310
+ # If the type is prefixed with a scope operator then we assume that
1311
+ # the type_name is an absolute reference.
1312
+ type_name.constantize
1313
+ else
1314
+ # Build a list of candidates to search for
1315
+ candidates = []
1316
+ name.scan(/::|$/) { candidates.unshift "#{$`}::#{type_name}" }
1317
+ candidates << type_name
1318
+
1319
+ candidates.each do |candidate|
1320
+ begin
1321
+ constant = candidate.constantize
1322
+ return constant if candidate == constant.to_s
1323
+ rescue NameError
1324
+ rescue ArgumentError
1325
+ end
1324
1326
  end
1327
+
1328
+ raise NameError, "uninitialized constant #{candidates.first}"
1325
1329
  end
1326
1330
  end
1327
1331
 
@@ -1572,7 +1576,7 @@ module ActiveRecord #:nodoc:
1572
1576
  # or nil if this record's unsaved.
1573
1577
  #
1574
1578
  # For example, suppose that you have a User model, and that you have a
1575
- # <tt>map.resources :users</tt> route. Normally, +user_path+ will
1579
+ # <tt>resources :users</tt> route. Normally, +user_path+ will
1576
1580
  # construct a path with the user object's 'id' in it:
1577
1581
  #
1578
1582
  # user = User.find_by_name('Phusion')