activerecord 1.12.2 → 1.13.0
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 +92 -0
- data/README +9 -9
- data/lib/active_record/acts/list.rb +1 -1
- data/lib/active_record/acts/nested_set.rb +13 -13
- data/lib/active_record/acts/tree.rb +7 -6
- data/lib/active_record/aggregations.rb +4 -4
- data/lib/active_record/associations.rb +82 -21
- data/lib/active_record/associations/association_collection.rb +0 -8
- data/lib/active_record/associations/association_proxy.rb +5 -2
- data/lib/active_record/associations/belongs_to_association.rb +6 -2
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +11 -1
- data/lib/active_record/associations/has_many_association.rb +34 -5
- data/lib/active_record/associations/has_one_association.rb +1 -1
- data/lib/active_record/base.rb +144 -59
- data/lib/active_record/callbacks.rb +6 -6
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +3 -2
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +4 -4
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +10 -8
- data/lib/active_record/connection_adapters/abstract_adapter.rb +1 -1
- data/lib/active_record/connection_adapters/db2_adapter.rb +17 -1
- data/lib/active_record/connection_adapters/oci_adapter.rb +322 -185
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +9 -8
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +107 -14
- data/lib/active_record/fixtures.rb +10 -8
- data/lib/active_record/migration.rb +20 -6
- data/lib/active_record/observer.rb +4 -3
- data/lib/active_record/reflection.rb +5 -5
- data/lib/active_record/transactions.rb +2 -2
- data/lib/active_record/validations.rb +70 -40
- data/lib/active_record/vendor/mysql411.rb +9 -13
- data/lib/active_record/version.rb +2 -2
- data/rakefile +1 -1
- data/test/abstract_unit.rb +5 -0
- data/test/ar_schema_test.rb +1 -1
- data/test/associations_extensions_test.rb +37 -0
- data/test/associations_go_eager_test.rb +25 -0
- data/test/associations_test.rb +14 -6
- data/test/base_test.rb +63 -45
- data/test/connections/native_sqlite/connection.rb +2 -2
- data/test/connections/native_sqlite3/connection.rb +2 -2
- data/test/connections/native_sqlite3/in_memory_connection.rb +1 -1
- data/test/debug.log +2857 -0
- data/test/deprecated_finder_test.rb +3 -9
- data/test/finder_test.rb +27 -13
- data/test/fixtures/author.rb +4 -0
- data/test/fixtures/comment.rb +4 -8
- data/test/fixtures/db_definitions/create_oracle_db.bat +0 -5
- data/test/fixtures/db_definitions/create_oracle_db.sh +0 -5
- data/test/fixtures/db_definitions/db2.drop.sql +0 -1
- data/test/fixtures/db_definitions/oci.sql +2 -0
- data/test/fixtures/db_definitions/sqlserver.drop.sql +1 -1
- data/test/fixtures/developer.rb +18 -1
- data/test/fixtures/fixture_database.sqlite +0 -0
- data/test/fixtures/fixture_database_2.sqlite +0 -0
- data/test/fixtures/migrations_with_duplicate/1_people_have_last_names.rb +9 -0
- data/test/fixtures/migrations_with_duplicate/2_we_need_reminders.rb +12 -0
- data/test/fixtures/migrations_with_duplicate/3_foo.rb +7 -0
- data/test/fixtures/migrations_with_duplicate/3_innocent_jointable.rb +12 -0
- data/test/fixtures/post.rb +18 -4
- data/test/fixtures/reply.rb +3 -1
- data/test/inheritance_test.rb +3 -2
- data/test/{conditions_scoping_test.rb → method_scoping_test.rb} +55 -10
- data/test/migration_test.rb +68 -31
- data/test/readonly_test.rb +65 -5
- data/test/validations_test.rb +10 -2
- metadata +13 -4
@@ -124,14 +124,6 @@ module ActiveRecord
|
|
124
124
|
end
|
125
125
|
|
126
126
|
private
|
127
|
-
def method_missing(method, *args, &block)
|
128
|
-
if @target.respond_to?(method) or (not @association_class.respond_to?(method) and Class.respond_to?(method))
|
129
|
-
super
|
130
|
-
else
|
131
|
-
@association_class.constrain(:conditions => @finder_sql, :joins => @join_sql) { @association_class.send(method, *args, &block) }
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
127
|
def raise_on_type_mismatch(record)
|
136
128
|
raise ActiveRecord::AssociationTypeMismatch, "#{@association_class} expected, got #{record.class}" unless record.is_a?(@association_class)
|
137
129
|
end
|
@@ -2,7 +2,8 @@ module ActiveRecord
|
|
2
2
|
module Associations
|
3
3
|
class AssociationProxy #:nodoc:
|
4
4
|
alias_method :proxy_respond_to?, :respond_to?
|
5
|
-
|
5
|
+
alias_method :proxy_extend, :extend
|
6
|
+
instance_methods.each { |m| undef_method m unless m =~ /(^__|^nil\?|^proxy_respond_to\?|^proxy_extend|^send)/ }
|
6
7
|
|
7
8
|
def initialize(owner, association_name, association_class_name, association_class_primary_key_name, options)
|
8
9
|
@owner = owner
|
@@ -11,6 +12,8 @@ module ActiveRecord
|
|
11
12
|
@association_class = eval(association_class_name, nil, __FILE__, __LINE__)
|
12
13
|
@association_class_primary_key_name = association_class_primary_key_name
|
13
14
|
|
15
|
+
proxy_extend(options[:extend]) if options[:extend]
|
16
|
+
|
14
17
|
reset
|
15
18
|
end
|
16
19
|
|
@@ -95,4 +98,4 @@ module ActiveRecord
|
|
95
98
|
end
|
96
99
|
end
|
97
100
|
end
|
98
|
-
end
|
101
|
+
end
|
@@ -49,9 +49,13 @@ module ActiveRecord
|
|
49
49
|
private
|
50
50
|
def find_target
|
51
51
|
if @options[:conditions]
|
52
|
-
@association_class.
|
52
|
+
@association_class.find(
|
53
|
+
@owner[@association_class_primary_key_name],
|
54
|
+
:conditions => interpolate_sql(@options[:conditions]),
|
55
|
+
:include => @options[:include]
|
56
|
+
)
|
53
57
|
else
|
54
|
-
@association_class.find(@owner[@association_class_primary_key_name])
|
58
|
+
@association_class.find(@owner[@association_class_primary_key_name], :include => @options[:include])
|
55
59
|
end
|
56
60
|
end
|
57
61
|
|
@@ -76,11 +76,21 @@ module ActiveRecord
|
|
76
76
|
end
|
77
77
|
|
78
78
|
protected
|
79
|
+
def method_missing(method, *args, &block)
|
80
|
+
if @target.respond_to?(method) || (!@association_class.respond_to?(method) && Class.respond_to?(method))
|
81
|
+
super
|
82
|
+
else
|
83
|
+
@association_class.with_scope(:find => { :conditions => @finder_sql, :joins => @join_sql, :readonly => false }) do
|
84
|
+
@association_class.send(method, *args, &block)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
79
89
|
def find_target
|
80
90
|
if @options[:finder_sql]
|
81
91
|
records = @association_class.find_by_sql(@finder_sql)
|
82
92
|
else
|
83
|
-
records = find(:all)
|
93
|
+
records = find(:all, :include => @options[:include])
|
84
94
|
end
|
85
95
|
|
86
96
|
@options[:uniq] ? uniq(records) : records
|
@@ -23,12 +23,12 @@ module ActiveRecord
|
|
23
23
|
# DEPRECATED.
|
24
24
|
def find_all(runtime_conditions = nil, orderings = nil, limit = nil, joins = nil)
|
25
25
|
if @options[:finder_sql]
|
26
|
-
|
26
|
+
@association_class.find_by_sql(@finder_sql)
|
27
27
|
else
|
28
|
-
|
29
|
-
|
28
|
+
conditions = @finder_sql
|
29
|
+
conditions += " AND (#{sanitize_sql(runtime_conditions)})" if runtime_conditions
|
30
30
|
orderings ||= @options[:order]
|
31
|
-
|
31
|
+
@association_class.find_all(conditions, orderings, limit, joins)
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
@@ -85,8 +85,37 @@ module ActiveRecord
|
|
85
85
|
end
|
86
86
|
|
87
87
|
protected
|
88
|
+
def method_missing(method, *args, &block)
|
89
|
+
if @target.respond_to?(method) || (!@association_class.respond_to?(method) && Class.respond_to?(method))
|
90
|
+
super
|
91
|
+
else
|
92
|
+
@association_class.with_scope(
|
93
|
+
:find => {
|
94
|
+
:conditions => @finder_sql,
|
95
|
+
:joins => @join_sql,
|
96
|
+
:readonly => false
|
97
|
+
},
|
98
|
+
:create => {
|
99
|
+
@association_class_primary_key_name => @owner.id
|
100
|
+
}
|
101
|
+
) do
|
102
|
+
@association_class.send(method, *args, &block)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
88
107
|
def find_target
|
89
|
-
|
108
|
+
if @options[:finder_sql]
|
109
|
+
@association_class.find_by_sql(@finder_sql)
|
110
|
+
else
|
111
|
+
@association_class.find(:all,
|
112
|
+
:conditions => @finder_sql,
|
113
|
+
:order => @options[:order],
|
114
|
+
:limit => @options[:limit],
|
115
|
+
:joins => @options[:joins],
|
116
|
+
:include => @options[:include]
|
117
|
+
)
|
118
|
+
end
|
90
119
|
end
|
91
120
|
|
92
121
|
def count_records
|
@@ -57,7 +57,7 @@ module ActiveRecord
|
|
57
57
|
|
58
58
|
private
|
59
59
|
def find_target
|
60
|
-
@association_class.find(:first, :conditions => @finder_sql, :order => @options[:order])
|
60
|
+
@association_class.find(:first, :conditions => @finder_sql, :order => @options[:order], :include => @options[:include])
|
61
61
|
end
|
62
62
|
|
63
63
|
def target_obsolete?
|
data/lib/active_record/base.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'yaml'
|
2
|
+
require 'set'
|
2
3
|
require 'active_record/deprecated_finders'
|
3
4
|
|
4
5
|
module ActiveRecord #:nodoc:
|
@@ -139,7 +140,7 @@ module ActiveRecord #:nodoc:
|
|
139
140
|
#
|
140
141
|
# == Dynamic attribute-based finders
|
141
142
|
#
|
142
|
-
# Dynamic attribute-based finders are a cleaner way of getting objects by simple queries without turning to SQL. They work by
|
143
|
+
# Dynamic attribute-based finders are a cleaner way of getting (and/or creating) objects by simple queries without turning to SQL. They work by
|
143
144
|
# appending the name of an attribute to <tt>find_by_</tt> or <tt>find_all_by_</tt>, so you get finders like Person.find_by_user_name,
|
144
145
|
# Person.find_all_by_last_name, Payment.find_by_transaction_id. So instead of writing
|
145
146
|
# <tt>Person.find(:first, ["user_name = ?", user_name])</tt>, you just do <tt>Person.find_by_user_name(user_name)</tt>.
|
@@ -154,6 +155,15 @@ module ActiveRecord #:nodoc:
|
|
154
155
|
# is actually Payment.find_all_by_amount(amount, options). And the full interface to Person.find_by_user_name is
|
155
156
|
# actually Person.find_by_user_name(user_name, options). So you could call <tt>Payment.find_all_by_amount(50, :order => "created_on")</tt>.
|
156
157
|
#
|
158
|
+
# The same dynamic finder style can be used to create the object if it doesn't already exist. This dynamic finder is called with
|
159
|
+
# <tt>find_or_create_by_</tt> and will return the object if it already exists and otherwise creates it, then returns it. Example:
|
160
|
+
#
|
161
|
+
# # No 'Summer' tag exists
|
162
|
+
# Tag.find_or_create_by_name("Summer") # equal to Tag.create(:name => "Summer")
|
163
|
+
#
|
164
|
+
# # Now the 'Summer' tag does exist
|
165
|
+
# Tag.find_or_create_by_name("Summer") # equal to Tag.find_by_name("Summer")
|
166
|
+
#
|
157
167
|
# == Saving arrays, hashes, and other non-mappable objects in text columns
|
158
168
|
#
|
159
169
|
# Active Record can serialize any object in text columns using YAML. To do so, you must specify this with a call to the class method +serialize+.
|
@@ -384,9 +394,14 @@ module ActiveRecord #:nodoc:
|
|
384
394
|
def find(*args)
|
385
395
|
options = extract_options_from_args!(args)
|
386
396
|
|
387
|
-
#
|
388
|
-
if
|
389
|
-
|
397
|
+
# Inherit :readonly from finder scope if set. Otherwise,
|
398
|
+
# if :joins is not blank then :readonly defaults to true.
|
399
|
+
unless options.has_key?(:readonly)
|
400
|
+
if scoped?(:find, :readonly)
|
401
|
+
options[:readonly] = scope(:find, :readonly)
|
402
|
+
elsif !options[:joins].blank?
|
403
|
+
options[:readonly] = true
|
404
|
+
end
|
390
405
|
end
|
391
406
|
|
392
407
|
case args.first
|
@@ -445,6 +460,8 @@ module ActiveRecord #:nodoc:
|
|
445
460
|
if attributes.is_a?(Array)
|
446
461
|
attributes.collect { |attr| create(attr) }
|
447
462
|
else
|
463
|
+
attributes.reverse_merge!(scope(:create)) if scoped?(:create)
|
464
|
+
|
448
465
|
object = new(attributes)
|
449
466
|
object.save
|
450
467
|
object
|
@@ -702,6 +719,22 @@ module ActiveRecord #:nodoc:
|
|
702
719
|
class_name
|
703
720
|
end
|
704
721
|
|
722
|
+
# Indicates whether the table associated with this class exists
|
723
|
+
def table_exists?
|
724
|
+
if connection.respond_to?(:tables)
|
725
|
+
connection.tables.include? table_name
|
726
|
+
else
|
727
|
+
# if the connection adapter hasn't implemented tables, there are two crude tests that can be
|
728
|
+
# used - see if getting column info raises an error, or if the number of columns returned is zero
|
729
|
+
begin
|
730
|
+
reset_column_information
|
731
|
+
columns.size > 0
|
732
|
+
rescue ActiveRecord::StatementInvalid
|
733
|
+
false
|
734
|
+
end
|
735
|
+
end
|
736
|
+
end
|
737
|
+
|
705
738
|
# Returns an array of column objects for the table associated with this class.
|
706
739
|
def columns
|
707
740
|
unless @columns
|
@@ -740,15 +773,14 @@ module ActiveRecord #:nodoc:
|
|
740
773
|
end
|
741
774
|
end
|
742
775
|
|
743
|
-
|
744
776
|
# Contains the names of the generated reader methods.
|
745
777
|
def read_methods
|
746
|
-
@read_methods ||=
|
778
|
+
@read_methods ||= Set.new
|
747
779
|
end
|
748
|
-
|
780
|
+
|
749
781
|
# Resets all the cached information about columns, which will cause them to be reloaded on the next request.
|
750
782
|
def reset_column_information
|
751
|
-
read_methods.
|
783
|
+
read_methods.each { |name| undef_method(name) }
|
752
784
|
@column_names = @columns = @columns_hash = @content_columns = @dynamic_methods_hash = @read_methods = nil
|
753
785
|
end
|
754
786
|
|
@@ -807,20 +839,37 @@ module ActiveRecord #:nodoc:
|
|
807
839
|
ensure
|
808
840
|
logger.level = old_logger_level if logger
|
809
841
|
end
|
810
|
-
|
811
|
-
#
|
812
|
-
#
|
842
|
+
|
843
|
+
# Scope parameters to method calls within the block. Takes a hash of method_name => parameters hash.
|
844
|
+
# method_name may be :find or :create.
|
845
|
+
# :find parameters may include the <tt>:conditions</tt>, <tt>:joins</tt>,
|
846
|
+
# <tt>:offset</tt>, <tt>:limit</tt>, and <tt>:readonly</tt> options.
|
847
|
+
# :create parameters are an attributes hash.
|
813
848
|
#
|
814
|
-
# Article.
|
849
|
+
# Article.with_scope(:find => { :conditions => "blog_id = 1" }, :create => { :blog_id => 1 }) do
|
815
850
|
# Article.find(1) # => SELECT * from articles WHERE blog_id = 1 AND id = 1
|
851
|
+
# a = Article.create(1)
|
852
|
+
# a.blog_id == 1
|
816
853
|
# end
|
817
|
-
def
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
854
|
+
def with_scope(method_scoping = {})
|
855
|
+
# Dup first and second level of hash (method and params).
|
856
|
+
method_scoping = method_scoping.inject({}) do |hash, (method, params)|
|
857
|
+
hash[method] = params.dup
|
858
|
+
hash
|
859
|
+
end
|
860
|
+
|
861
|
+
method_scoping.assert_valid_keys [:find, :create]
|
862
|
+
if f = method_scoping[:find]
|
863
|
+
f.assert_valid_keys [:conditions, :joins, :offset, :limit, :readonly]
|
864
|
+
f[:readonly] = true if !f[:joins].blank? && !f.has_key?(:readonly)
|
823
865
|
end
|
866
|
+
|
867
|
+
raise ArgumentError, "Nested scopes are not yet supported: #{scoped_methods.inspect}" unless scoped_methods.nil?
|
868
|
+
|
869
|
+
self.scoped_methods = method_scoping
|
870
|
+
yield
|
871
|
+
ensure
|
872
|
+
self.scoped_methods = nil
|
824
873
|
end
|
825
874
|
|
826
875
|
# Overwrite the default class equality method to provide support for association proxies.
|
@@ -883,21 +932,23 @@ module ActiveRecord #:nodoc:
|
|
883
932
|
end
|
884
933
|
|
885
934
|
def add_limit!(sql, options)
|
935
|
+
options[:limit] ||= scope(:find, :limit)
|
936
|
+
options[:offset] ||= scope(:find, :offset)
|
886
937
|
connection.add_limit_offset!(sql, options)
|
887
938
|
end
|
888
|
-
|
939
|
+
|
889
940
|
def add_joins!(sql, options)
|
890
|
-
join =
|
941
|
+
join = scope(:find, :joins) || options[:joins]
|
891
942
|
sql << " #{join} " if join
|
892
943
|
end
|
893
|
-
|
944
|
+
|
894
945
|
# Adds a sanitized version of +conditions+ to the +sql+ string. Note that the passed-in +sql+ string is changed.
|
895
946
|
def add_conditions!(sql, conditions)
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
sql << "WHERE (#{
|
947
|
+
segments = [scope(:find, :conditions)]
|
948
|
+
segments << sanitize_sql(conditions) unless conditions.nil?
|
949
|
+
segments << type_condition unless descends_from_active_record?
|
950
|
+
segments.compact!
|
951
|
+
sql << "WHERE (#{segments.join(") AND (")}) " unless segments.empty?
|
901
952
|
end
|
902
953
|
|
903
954
|
def type_condition
|
@@ -922,27 +973,54 @@ module ActiveRecord #:nodoc:
|
|
922
973
|
# It's even possible to use all the additional parameters to find. For example, the full interface for find_all_by_amount
|
923
974
|
# is actually find_all_by_amount(amount, options).
|
924
975
|
def method_missing(method_id, *arguments)
|
925
|
-
|
976
|
+
if match = /find_(all_by|by)_([_a-zA-Z]\w*)/.match(method_id.to_s)
|
977
|
+
finder = determine_finder(match)
|
926
978
|
|
927
|
-
|
928
|
-
|
929
|
-
attributes = md.captures.last.split('_and_')
|
930
|
-
attributes.each { |attr_name| super unless column_methods_hash.include?(attr_name.to_sym) }
|
979
|
+
attribute_names = extract_attribute_names_from_match(match)
|
980
|
+
super unless all_attributes_exists?(attribute_names)
|
931
981
|
|
932
|
-
|
933
|
-
conditions = attributes.collect { |attr_name| attr_index += 1; "#{table_name}.#{attr_name} #{attribute_condition(arguments[attr_index])} " }.join(" AND ")
|
982
|
+
conditions = construct_conditions_from_arguments(attribute_names, arguments)
|
934
983
|
|
935
|
-
if arguments[
|
936
|
-
find(finder, { :conditions =>
|
984
|
+
if arguments[attribute_names.length].is_a?(Hash)
|
985
|
+
find(finder, { :conditions => conditions }.update(arguments[attribute_names.length]))
|
937
986
|
else
|
938
|
-
# deprecated API
|
939
|
-
send("find_#{finder}", [conditions, *arguments[0...attributes.length]], *arguments[attributes.length..-1])
|
987
|
+
send("find_#{finder}", conditions, *arguments[attribute_names.length..-1]) # deprecated API
|
940
988
|
end
|
989
|
+
elsif match = /find_or_create_by_([_a-zA-Z]\w*)/.match(method_id.to_s)
|
990
|
+
attribute_names = extract_attribute_names_from_match(match)
|
991
|
+
super unless all_attributes_exists?(attribute_names)
|
992
|
+
|
993
|
+
find(:first, :conditions => construct_conditions_from_arguments(attribute_names, arguments)) ||
|
994
|
+
create(construct_attributes_from_arguments(attribute_names, arguments))
|
941
995
|
else
|
942
996
|
super
|
943
997
|
end
|
944
998
|
end
|
945
999
|
|
1000
|
+
def determine_finder(match)
|
1001
|
+
match.captures.first == 'all_by' ? :all : :first
|
1002
|
+
end
|
1003
|
+
|
1004
|
+
def extract_attribute_names_from_match(match)
|
1005
|
+
match.captures.last.split('_and_')
|
1006
|
+
end
|
1007
|
+
|
1008
|
+
def construct_conditions_from_arguments(attribute_names, arguments)
|
1009
|
+
conditions = []
|
1010
|
+
attribute_names.each_with_index { |name, idx| conditions << "#{table_name}.#{name} #{attribute_condition(arguments[idx])} " }
|
1011
|
+
[ conditions.join(" AND "), *arguments[0...attribute_names.length] ]
|
1012
|
+
end
|
1013
|
+
|
1014
|
+
def construct_attributes_from_arguments(attribute_names, arguments)
|
1015
|
+
attributes = {}
|
1016
|
+
attribute_names.each_with_index { |name, idx| attributes[name] = arguments[idx] }
|
1017
|
+
attributes
|
1018
|
+
end
|
1019
|
+
|
1020
|
+
def all_attributes_exists?(attribute_names)
|
1021
|
+
attribute_names.all? { |name| column_methods_hash.include?(name.to_sym) }
|
1022
|
+
end
|
1023
|
+
|
946
1024
|
def attribute_condition(argument)
|
947
1025
|
case argument
|
948
1026
|
when nil then "IS ?"
|
@@ -986,29 +1064,37 @@ module ActiveRecord #:nodoc:
|
|
986
1064
|
@@subclasses[self] ||= []
|
987
1065
|
@@subclasses[self] + extra = @@subclasses[self].inject([]) {|list, subclass| list + subclass.subclasses }
|
988
1066
|
end
|
989
|
-
|
990
|
-
|
1067
|
+
|
1068
|
+
# Test whether the given method and optional key are scoped.
|
1069
|
+
def scoped?(method, key = nil)
|
1070
|
+
scoped_methods and scoped_methods.has_key?(method) and (key.nil? or scope(method).has_key?(key))
|
1071
|
+
end
|
1072
|
+
|
1073
|
+
# Retrieve the scope for the given method and optional key.
|
1074
|
+
def scope(method, key = nil)
|
1075
|
+
if scoped_methods and scope = scoped_methods[method]
|
1076
|
+
key ? scope[key] : scope
|
1077
|
+
end
|
1078
|
+
end
|
1079
|
+
|
1080
|
+
def scoped_methods
|
991
1081
|
if allow_concurrency
|
992
|
-
Thread.current[:
|
993
|
-
Thread.current[:
|
1082
|
+
Thread.current[:scoped_methods] ||= {}
|
1083
|
+
Thread.current[:scoped_methods][self] ||= nil
|
994
1084
|
else
|
995
|
-
@
|
1085
|
+
@scoped_methods ||= nil
|
996
1086
|
end
|
997
1087
|
end
|
998
|
-
# backwards compatibility
|
999
|
-
alias_method :scope_constrains, :scope_constraints
|
1000
1088
|
|
1001
|
-
def
|
1089
|
+
def scoped_methods=(value)
|
1002
1090
|
if allow_concurrency
|
1003
|
-
Thread.current[:
|
1004
|
-
Thread.current[:
|
1091
|
+
Thread.current[:scoped_methods] ||= {}
|
1092
|
+
Thread.current[:scoped_methods][self] = value
|
1005
1093
|
else
|
1006
|
-
@
|
1094
|
+
@scoped_methods = value
|
1007
1095
|
end
|
1008
1096
|
end
|
1009
|
-
|
1010
|
-
alias_method :scope_constrains=, :scope_constraints=
|
1011
|
-
|
1097
|
+
|
1012
1098
|
# Returns the class type of the record using the current module as a prefix. So descendents of
|
1013
1099
|
# MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
|
1014
1100
|
def compute_type(type_name)
|
@@ -1108,14 +1194,13 @@ module ActiveRecord #:nodoc:
|
|
1108
1194
|
yield self if block_given?
|
1109
1195
|
end
|
1110
1196
|
|
1111
|
-
#
|
1112
|
-
#
|
1197
|
+
# A model instance's primary key is always available as model.id
|
1198
|
+
# whether you name it the default 'id' or set it to something else.
|
1113
1199
|
def id
|
1114
1200
|
attr_name = self.class.primary_key
|
1115
|
-
column
|
1116
|
-
raise ActiveRecordError, "No such primary key column #{attr_name} for table #{table_name}" if column.nil?
|
1201
|
+
column = column_for_attribute(attr_name)
|
1117
1202
|
define_read_method(:id, attr_name, column) if self.class.generate_read_methods
|
1118
|
-
(
|
1203
|
+
read_attribute(attr_name)
|
1119
1204
|
end
|
1120
1205
|
|
1121
1206
|
# Enables Active Record objects to be used as URL parameters in Action Pack automatically.
|
@@ -1447,17 +1532,17 @@ module ActiveRecord #:nodoc:
|
|
1447
1532
|
end
|
1448
1533
|
end
|
1449
1534
|
|
1450
|
-
# Define
|
1535
|
+
# Define an attribute reader method. Cope with nil column.
|
1451
1536
|
def define_read_method(symbol, attr_name, column)
|
1452
|
-
cast_code = column.type_cast_code('v')
|
1537
|
+
cast_code = column.type_cast_code('v') if column
|
1453
1538
|
access_code = cast_code ? "(v=@attributes['#{attr_name}']) && #{cast_code}" : "@attributes['#{attr_name}']"
|
1454
1539
|
|
1455
1540
|
unless attr_name.to_s == self.class.primary_key.to_s
|
1456
1541
|
access_code = access_code.insert(0, "raise NoMethodError, 'missing attribute: #{attr_name}', caller unless @attributes.has_key?('#{attr_name}'); ")
|
1542
|
+
self.class.read_methods << attr_name
|
1457
1543
|
end
|
1458
1544
|
|
1459
1545
|
self.class.class_eval("def #{symbol}; #{access_code}; end")
|
1460
|
-
self.class.read_methods[attr_name] = true unless symbol == :id
|
1461
1546
|
end
|
1462
1547
|
|
1463
1548
|
# Returns true if the attribute is of a text column and marked for serialization.
|