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.
- data/CHANGELOG +11 -1
- data/lib/active_record/associations.rb +26 -54
- data/lib/active_record/associations/association_collection.rb +13 -2
- data/lib/active_record/associations/association_proxy.rb +3 -1
- data/lib/active_record/associations/through_association_scope.rb +1 -1
- data/lib/active_record/attribute_methods/dirty.rb +6 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -4
- data/lib/active_record/base.rb +66 -62
- data/lib/active_record/callbacks.rb +4 -2
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +5 -3
- data/lib/active_record/connection_adapters/abstract_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +45 -45
- data/lib/active_record/migration.rb +1 -1
- data/lib/active_record/named_scope.rb +26 -109
- data/lib/active_record/railties/databases.rake +3 -7
- data/lib/active_record/reflection.rb +11 -0
- data/lib/active_record/relation.rb +20 -1
- data/lib/active_record/relation/finder_methods.rb +18 -2
- data/lib/active_record/relation/predicate_builder.rb +4 -2
- data/lib/active_record/relation/query_methods.rb +26 -8
- data/lib/active_record/relation/spawn_methods.rb +9 -6
- data/lib/active_record/serializers/xml_serializer.rb +2 -1
- data/lib/active_record/validations/uniqueness.rb +3 -1
- data/lib/active_record/version.rb +1 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +5 -0
- data/lib/rails/generators/active_record/model/templates/module.rb +5 -0
- metadata +8 -7
data/CHANGELOG
CHANGED
@@ -1,4 +1,14 @@
|
|
1
|
-
*Rails 3.0.0 [
|
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/
|
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
|
-
|
1515
|
-
|
1516
|
-
|
1517
|
-
|
1518
|
-
|
1519
|
-
|
1520
|
-
|
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
|
-
|
1533
|
-
|
1534
|
-
|
1535
|
-
|
1536
|
-
|
1537
|
-
|
1538
|
-
|
1539
|
-
|
1540
|
-
|
1541
|
-
|
1542
|
-
|
1543
|
-
|
1544
|
-
|
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
|
-
|
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.
|
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
|
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__,
|
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__,
|
47
|
+
generated_attribute_methods.module_eval(method_body, __FILE__, line)
|
48
48
|
else
|
49
49
|
super
|
50
50
|
end
|
data/lib/active_record/base.rb
CHANGED
@@ -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/
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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.
|
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.
|
89
|
-
# 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.
|
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.
|
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.
|
103
|
-
# Student.
|
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.
|
157
|
-
# And instead of writing <tt>Person.
|
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.
|
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
|
165
|
-
#
|
166
|
-
#
|
167
|
-
#
|
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.
|
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
|
-
|
345
|
-
|
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
|
-
|
352
|
-
|
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
|
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
|
-
|
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>
|
1200
|
-
# <tt>:
|
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 =>
|
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
|
1200
|
+
# <tt>where</tt>, <tt>includes</tt>, and <tt>joins</tt> operations in <tt>Relation</tt>, which are merged.
|
1214
1201
|
#
|
1215
|
-
# <tt
|
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 =>
|
1222
|
-
# with_scope(:find =>
|
1223
|
-
#
|
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 =>
|
1226
|
-
#
|
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 =>
|
1237
|
-
# with_exclusive_scope(:find =>
|
1238
|
-
#
|
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
|
-
|
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
|
-
|
1319
|
-
|
1320
|
-
|
1321
|
-
|
1322
|
-
|
1323
|
-
|
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>
|
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')
|