activerecord 4.0.0.beta1 → 4.0.0.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.

Files changed (107) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +573 -30
  3. data/README.rdoc +3 -3
  4. data/lib/active_record.rb +8 -2
  5. data/lib/active_record/associations.rb +16 -9
  6. data/lib/active_record/associations/association.rb +8 -6
  7. data/lib/active_record/associations/association_scope.rb +2 -1
  8. data/lib/active_record/associations/belongs_to_association.rb +2 -2
  9. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
  10. data/lib/active_record/associations/builder/belongs_to.rb +37 -5
  11. data/lib/active_record/associations/collection_association.rb +38 -14
  12. data/lib/active_record/associations/collection_proxy.rb +18 -15
  13. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +3 -3
  14. data/lib/active_record/associations/has_many_association.rb +4 -3
  15. data/lib/active_record/associations/has_many_through_association.rb +1 -1
  16. data/lib/active_record/associations/has_one_association.rb +1 -1
  17. data/lib/active_record/associations/join_dependency.rb +29 -8
  18. data/lib/active_record/associations/join_dependency/join_association.rb +26 -6
  19. data/lib/active_record/associations/join_dependency/join_base.rb +2 -2
  20. data/lib/active_record/associations/join_dependency/join_part.rb +6 -6
  21. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +1 -1
  22. data/lib/active_record/associations/preloader/has_many_through.rb +6 -2
  23. data/lib/active_record/associations/through_association.rb +1 -1
  24. data/lib/active_record/attribute_assignment.rb +5 -5
  25. data/lib/active_record/attribute_methods.rb +20 -5
  26. data/lib/active_record/attribute_methods/dirty.rb +5 -1
  27. data/lib/active_record/attribute_methods/primary_key.rb +1 -1
  28. data/lib/active_record/attribute_methods/serialization.rb +9 -2
  29. data/lib/active_record/autosave_association.rb +19 -5
  30. data/lib/active_record/base.rb +3 -3
  31. data/lib/active_record/callbacks.rb +1 -1
  32. data/lib/active_record/coders/yaml_column.rb +8 -13
  33. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +3 -9
  34. data/lib/active_record/connection_adapters/abstract/database_limits.rb +1 -1
  35. data/lib/active_record/connection_adapters/abstract/database_statements.rb +2 -1
  36. data/lib/active_record/connection_adapters/abstract/quoting.rb +2 -8
  37. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +60 -61
  38. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +13 -2
  39. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +291 -153
  40. data/lib/active_record/connection_adapters/abstract/transaction.rb +1 -1
  41. data/lib/active_record/connection_adapters/abstract_adapter.rb +92 -1
  42. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +55 -29
  43. data/lib/active_record/connection_adapters/column.rb +4 -4
  44. data/lib/active_record/connection_adapters/connection_specification.rb +2 -2
  45. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -3
  46. data/lib/active_record/connection_adapters/mysql_adapter.rb +5 -5
  47. data/lib/active_record/connection_adapters/postgresql/cast.rb +22 -2
  48. data/lib/active_record/connection_adapters/postgresql/oid.rb +25 -6
  49. data/lib/active_record/connection_adapters/postgresql/quoting.rb +26 -13
  50. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +50 -9
  51. data/lib/active_record/connection_adapters/postgresql_adapter.rb +53 -24
  52. data/lib/active_record/connection_adapters/schema_cache.rb +35 -7
  53. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +13 -5
  54. data/lib/active_record/connection_handling.rb +7 -7
  55. data/lib/active_record/core.rb +43 -8
  56. data/lib/active_record/counter_cache.rb +2 -1
  57. data/lib/active_record/errors.rb +11 -10
  58. data/lib/active_record/explain.rb +9 -7
  59. data/lib/active_record/explain_registry.rb +30 -0
  60. data/lib/active_record/explain_subscriber.rb +3 -2
  61. data/lib/active_record/fixture_set/file.rb +1 -2
  62. data/lib/active_record/fixtures.rb +13 -7
  63. data/lib/active_record/inheritance.rb +12 -4
  64. data/lib/active_record/integration.rb +3 -3
  65. data/lib/active_record/locking/optimistic.rb +2 -2
  66. data/lib/active_record/log_subscriber.rb +2 -2
  67. data/lib/active_record/migration.rb +69 -21
  68. data/lib/active_record/model_schema.rb +1 -1
  69. data/lib/active_record/nested_attributes.rb +98 -46
  70. data/lib/active_record/persistence.rb +3 -3
  71. data/lib/active_record/querying.rb +1 -1
  72. data/lib/active_record/railtie.rb +18 -4
  73. data/lib/active_record/railties/console_sandbox.rb +3 -2
  74. data/lib/active_record/railties/controller_runtime.rb +2 -1
  75. data/lib/active_record/railties/databases.rake +38 -80
  76. data/lib/active_record/reflection.rb +36 -3
  77. data/lib/active_record/relation.rb +18 -8
  78. data/lib/active_record/relation/calculations.rb +10 -5
  79. data/lib/active_record/relation/delegation.rb +3 -5
  80. data/lib/active_record/relation/finder_methods.rb +27 -14
  81. data/lib/active_record/relation/merger.rb +30 -2
  82. data/lib/active_record/relation/predicate_builder.rb +1 -6
  83. data/lib/active_record/relation/query_methods.rb +113 -16
  84. data/lib/active_record/runtime_registry.rb +17 -0
  85. data/lib/active_record/schema_dumper.rb +5 -1
  86. data/lib/active_record/schema_migration.rb +8 -5
  87. data/lib/active_record/scoping.rb +56 -2
  88. data/lib/active_record/scoping/default.rb +12 -11
  89. data/lib/active_record/scoping/named.rb +7 -3
  90. data/lib/active_record/serialization.rb +1 -1
  91. data/lib/active_record/statement_cache.rb +26 -0
  92. data/lib/active_record/tasks/database_tasks.rb +55 -10
  93. data/lib/active_record/tasks/firebird_database_tasks.rb +56 -0
  94. data/lib/active_record/tasks/mysql_database_tasks.rb +7 -2
  95. data/lib/active_record/tasks/oracle_database_tasks.rb +45 -0
  96. data/lib/active_record/tasks/sqlserver_database_tasks.rb +48 -0
  97. data/lib/active_record/timestamp.rb +6 -0
  98. data/lib/active_record/transactions.rb +7 -3
  99. data/lib/active_record/validations.rb +1 -2
  100. data/lib/active_record/validations/uniqueness.rb +7 -3
  101. data/lib/active_record/version.rb +7 -6
  102. data/lib/rails/generators/active_record/migration/migration_generator.rb +9 -2
  103. data/lib/rails/generators/active_record/{model/templates/migration.rb → migration/templates/create_table_migration.rb} +4 -0
  104. data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
  105. data/lib/rails/generators/active_record/model/templates/model.rb +4 -1
  106. metadata +17 -12
  107. data/examples/associations.png +0 -0
@@ -83,16 +83,16 @@ module ActiveRecord
83
83
  # # #<Pet id: 3, name: "Choo-Choo">
84
84
  # # ]
85
85
  #
86
- # Be careful because this also means youre initializing a model
87
- # object with only the fields that youve selected. If you attempt
88
- # to access a field that is not in the initialized record youll
86
+ # Be careful because this also means you're initializing a model
87
+ # object with only the fields that you've selected. If you attempt
88
+ # to access a field that is not in the initialized record you'll
89
89
  # receive:
90
90
  #
91
91
  # person.pets.select(:name).first.person_id
92
92
  # # => ActiveModel::MissingAttributeError: missing attribute: person_id
93
93
  #
94
94
  # *Second:* You can pass a block so it can be used just like Array#select.
95
- # This build an array of objects from the database for the scope,
95
+ # This builds an array of objects from the database for the scope,
96
96
  # converting them into an array and iterating through them using
97
97
  # Array#select.
98
98
  #
@@ -228,6 +228,7 @@ module ActiveRecord
228
228
  def build(attributes = {}, &block)
229
229
  @association.build(attributes, &block)
230
230
  end
231
+ alias_method :new, :build
231
232
 
232
233
  # Returns a new object of the collection type that has been instantiated with
233
234
  # attributes, linked to this object and that has already been saved (if it
@@ -303,7 +304,7 @@ module ActiveRecord
303
304
  @association.concat(*records)
304
305
  end
305
306
 
306
- # Replace this collection with +other_array+. This will perform a diff
307
+ # Replaces this collection with +other_array+. This will perform a diff
307
308
  # and delete/add only records that have changed.
308
309
  #
309
310
  # class Person < ActiveRecord::Base
@@ -649,11 +650,12 @@ module ActiveRecord
649
650
  # # #<Pet name: "Fancy-Fancy">
650
651
  # # ]
651
652
  #
652
- # person.pets.select(:name).uniq
653
+ # person.pets.select(:name).distinct
653
654
  # # => [#<Pet name: "Fancy-Fancy">]
654
- def uniq
655
- @association.uniq
655
+ def distinct
656
+ @association.distinct
656
657
  end
658
+ alias uniq distinct
657
659
 
658
660
  # Count all records using SQL.
659
661
  #
@@ -831,8 +833,6 @@ module ActiveRecord
831
833
  @association.include?(record)
832
834
  end
833
835
 
834
- alias_method :new, :build
835
-
836
836
  def proxy_association
837
837
  @association
838
838
  end
@@ -847,10 +847,8 @@ module ActiveRecord
847
847
 
848
848
  # Returns a <tt>Relation</tt> object for the records in this association
849
849
  def scope
850
- association = @association
851
-
852
- @association.scope.extending! do
853
- define_method(:proxy_association) { association }
850
+ @association.scope.tap do |scope|
851
+ scope.proxy_association = @association
854
852
  end
855
853
  end
856
854
 
@@ -924,7 +922,7 @@ module ActiveRecord
924
922
  alias_method :to_a, :to_ary
925
923
 
926
924
  # Adds one or more +records+ to the collection by setting their foreign keys
927
- # to the associations primary key. Returns +self+, so several appends may be
925
+ # to the association's primary key. Returns +self+, so several appends may be
928
926
  # chained together.
929
927
  #
930
928
  # class Person < ActiveRecord::Base
@@ -947,6 +945,11 @@ module ActiveRecord
947
945
  proxy_association.concat(records) && self
948
946
  end
949
947
  alias_method :push, :<<
948
+ alias_method :append, :<<
949
+
950
+ def prepend(*args)
951
+ raise NoMethodError, "prepend on association is not defined. Please use << or append"
952
+ end
950
953
 
951
954
  # Equivalent to +delete_all+. The difference is that returns +self+, instead
952
955
  # of an array with the deleted objects, so methods can be chained. See
@@ -26,7 +26,7 @@ module ActiveRecord
26
26
  join_table[reflection.association_foreign_key] => record.id
27
27
  )
28
28
 
29
- owner.connection.insert stmt
29
+ owner.class.connection.insert stmt
30
30
  end
31
31
 
32
32
  record
@@ -41,7 +41,7 @@ module ActiveRecord
41
41
  def delete_records(records, method)
42
42
  if sql = options[:delete_sql]
43
43
  records = load_target if records == :all
44
- records.each { |record| owner.connection.delete(interpolate(sql, record)) }
44
+ records.each { |record| owner.class.connection.delete(interpolate(sql, record)) }
45
45
  else
46
46
  relation = join_table
47
47
  condition = relation[reflection.foreign_key].eq(owner.id)
@@ -53,7 +53,7 @@ module ActiveRecord
53
53
  )
54
54
  end
55
55
 
56
- owner.connection.delete(relation.where(condition).compile_delete)
56
+ owner.class.connection.delete(relation.where(condition).compile_delete)
57
57
  end
58
58
  end
59
59
 
@@ -22,10 +22,11 @@ module ActiveRecord
22
22
  else
23
23
  if options[:dependent] == :destroy
24
24
  # No point in executing the counter update since we're going to destroy the parent anyway
25
- load_target.each(&:mark_for_destruction)
25
+ load_target.each { |t| t.destroyed_by_association = reflection }
26
+ destroy_all
27
+ else
28
+ delete_all
26
29
  end
27
-
28
- delete_all
29
30
  end
30
31
  end
31
32
 
@@ -29,7 +29,7 @@ module ActiveRecord
29
29
  def concat(*records)
30
30
  unless owner.new_record?
31
31
  records.flatten.each do |record|
32
- raise_on_type_mismatch(record)
32
+ raise_on_type_mismatch!(record)
33
33
  record.save! if record.new_record?
34
34
  end
35
35
  end
@@ -22,7 +22,7 @@ module ActiveRecord
22
22
  end
23
23
 
24
24
  def replace(record, save = true)
25
- raise_on_type_mismatch(record) if record
25
+ raise_on_type_mismatch!(record) if record
26
26
  load_target
27
27
 
28
28
  # If target and record are nil, or target is equal to record,
@@ -5,10 +5,31 @@ module ActiveRecord
5
5
  autoload :JoinBase, 'active_record/associations/join_dependency/join_base'
6
6
  autoload :JoinAssociation, 'active_record/associations/join_dependency/join_association'
7
7
 
8
- attr_reader :join_parts, :reflections, :alias_tracker, :active_record
9
-
8
+ attr_reader :join_parts, :reflections, :alias_tracker, :base_klass
9
+
10
+ # base is the base class on which operation is taking place.
11
+ # associations is the list of associations which are joined using hash, symbol or array.
12
+ # joins is the list of all string join commnads and arel nodes.
13
+ #
14
+ # Example :
15
+ #
16
+ # class Physician < ActiveRecord::Base
17
+ # has_many :appointments
18
+ # has_many :patients, through: :appointments
19
+ # end
20
+ #
21
+ # If I execute `@physician.patients.to_a` then
22
+ # base #=> Physician
23
+ # associations #=> []
24
+ # joins #=> [#<Arel::Nodes::InnerJoin: ...]
25
+ #
26
+ # However if I execute `Physician.joins(:appointments).to_a` then
27
+ # base #=> Physician
28
+ # associations #=> [:appointments]
29
+ # joins #=> []
30
+ #
10
31
  def initialize(base, associations, joins)
11
- @active_record = base
32
+ @base_klass = base
12
33
  @table_joins = joins
13
34
  @join_parts = [JoinBase.new(base)]
14
35
  @associations = {}
@@ -54,10 +75,12 @@ module ActiveRecord
54
75
  parent
55
76
  }.uniq
56
77
 
57
- remove_duplicate_results!(active_record, records, @associations)
78
+ remove_duplicate_results!(base_klass, records, @associations)
58
79
  records
59
80
  end
60
81
 
82
+ protected
83
+
61
84
  def remove_duplicate_results!(base, records, associations)
62
85
  case associations
63
86
  when Symbol, String
@@ -88,8 +111,6 @@ module ActiveRecord
88
111
  end
89
112
  end
90
113
 
91
- protected
92
-
93
114
  def cache_joined_association(association)
94
115
  associations = []
95
116
  parent = association.parent
@@ -108,8 +129,8 @@ module ActiveRecord
108
129
  parent ||= join_parts.last
109
130
  case associations
110
131
  when Symbol, String
111
- reflection = parent.reflections[associations.to_s.intern] or
112
- raise ConfigurationError, "Association named '#{ associations }' was not found; perhaps you misspelled it?"
132
+ reflection = parent.reflections[associations.intern] or
133
+ raise ConfigurationError, "Association named '#{ associations }' was not found on #{ parent.base_klass.name }; perhaps you misspelled it?"
113
134
  unless join_association = find_join_association(reflection, parent)
114
135
  @reflections << reflection
115
136
  join_association = build_join_association(reflection, parent)
@@ -55,14 +55,19 @@ module ActiveRecord
55
55
 
56
56
  def find_parent_in(other_join_dependency)
57
57
  other_join_dependency.join_parts.detect do |join_part|
58
- parent == join_part
58
+ case parent
59
+ when JoinBase
60
+ parent.base_klass == join_part.base_klass
61
+ else
62
+ parent == join_part
63
+ end
59
64
  end
60
65
  end
61
66
 
62
- def join_to(relation)
67
+ def join_to(manager)
63
68
  tables = @tables.dup
64
69
  foreign_table = parent_table
65
- foreign_klass = parent.active_record
70
+ foreign_klass = parent.base_klass
66
71
 
67
72
  # The chain starts with the target table, but we want to end with it here (makes
68
73
  # more sense in this context), so we reverse
@@ -75,7 +80,7 @@ module ActiveRecord
75
80
  foreign_key = reflection.foreign_key
76
81
  when :has_and_belongs_to_many
77
82
  # Join the join table first...
78
- relation.from(join(
83
+ manager.from(join(
79
84
  table,
80
85
  table[reflection.foreign_key].
81
86
  eq(foreign_table[reflection.active_record_primary_key])
@@ -109,15 +114,30 @@ module ActiveRecord
109
114
  constraint = constraint.and(item.arel.constraints) unless item.arel.constraints.empty?
110
115
  end
111
116
 
112
- relation.from(join(table, constraint))
117
+ manager.from(join(table, constraint))
113
118
 
114
119
  # The current table in this iteration becomes the foreign table in the next
115
120
  foreign_table, foreign_klass = table, reflection.klass
116
121
  end
117
122
 
118
- relation
123
+ manager
119
124
  end
120
125
 
126
+ # Builds equality condition.
127
+ #
128
+ # Example:
129
+ #
130
+ # class Physician < ActiveRecord::Base
131
+ # has_many :appointments
132
+ # end
133
+ #
134
+ # If I execute `Physician.joins(:appointments).to_a` then
135
+ # reflection #=> #<ActiveRecord::Reflection::AssociationReflection @macro=:has_many ...>
136
+ # table #=> #<Arel::Table @name="appointments" ...>
137
+ # key #=> physician_id
138
+ # foreign_table #=> #<Arel::Table @name="physicians" ...>
139
+ # foreign_key #=> id
140
+ #
121
141
  def build_constraint(reflection, table, key, foreign_table, foreign_key)
122
142
  constraint = table[key].eq(foreign_table[foreign_key])
123
143
 
@@ -4,7 +4,7 @@ module ActiveRecord
4
4
  class JoinBase < JoinPart # :nodoc:
5
5
  def ==(other)
6
6
  other.class == self.class &&
7
- other.active_record == active_record
7
+ other.base_klass == base_klass
8
8
  end
9
9
 
10
10
  def aliased_prefix
@@ -16,7 +16,7 @@ module ActiveRecord
16
16
  end
17
17
 
18
18
  def aliased_table_name
19
- active_record.table_name
19
+ base_klass.table_name
20
20
  end
21
21
  end
22
22
  end
@@ -1,7 +1,7 @@
1
1
  module ActiveRecord
2
2
  module Associations
3
3
  class JoinDependency # :nodoc:
4
- # A JoinPart represents a part of a JoinDependency. It is an abstract class, inherited
4
+ # A JoinPart represents a part of a JoinDependency. It is inherited
5
5
  # by JoinBase and JoinAssociation. A JoinBase represents the Active Record which
6
6
  # everything else is being joined onto. A JoinAssociation represents an association which
7
7
  # is joining to the base. A JoinAssociation may result in more than one actual join
@@ -11,12 +11,12 @@ module ActiveRecord
11
11
  # The Active Record class which this join part is associated 'about'; for a JoinBase
12
12
  # this is the actual base model, for a JoinAssociation this is the target model of the
13
13
  # association.
14
- attr_reader :active_record
14
+ attr_reader :base_klass
15
15
 
16
- delegate :table_name, :column_names, :primary_key, :reflections, :arel_engine, :to => :active_record
16
+ delegate :table_name, :column_names, :primary_key, :reflections, :arel_engine, :to => :base_klass
17
17
 
18
- def initialize(active_record)
19
- @active_record = active_record
18
+ def initialize(base_klass)
19
+ @base_klass = base_klass
20
20
  @cached_record = {}
21
21
  @column_names_with_alias = nil
22
22
  end
@@ -70,7 +70,7 @@ module ActiveRecord
70
70
  end
71
71
 
72
72
  def instantiate(row)
73
- @cached_record[record_id(row)] ||= active_record.send(:instantiate, extract_record(row))
73
+ @cached_record[record_id(row)] ||= base_klass.instantiate(extract_record(row))
74
74
  end
75
75
  end
76
76
  end
@@ -35,7 +35,7 @@ module ActiveRecord
35
35
  # record
36
36
  def associated_records_by_owner
37
37
  records = {}
38
- super.each do |owner_key, rows|
38
+ super.each_value do |rows|
39
39
  rows.map! { |row| records[row[klass.primary_key]] ||= klass.instantiate(row) }
40
40
  end
41
41
  end
@@ -5,9 +5,13 @@ module ActiveRecord
5
5
  include ThroughAssociation
6
6
 
7
7
  def associated_records_by_owner
8
- super.each do |owner, records|
9
- records.uniq! if reflection_scope.uniq_value
8
+ records_by_owner = super
9
+
10
+ if reflection_scope.distinct_value
11
+ records_by_owner.each_value { |records| records.uniq! }
10
12
  end
13
+
14
+ records_by_owner
11
15
  end
12
16
  end
13
17
  end
@@ -14,7 +14,7 @@ module ActiveRecord
14
14
  def target_scope
15
15
  scope = super
16
16
  chain[1..-1].each do |reflection|
17
- scope = scope.merge(
17
+ scope.merge!(
18
18
  reflection.klass.all.with_default_scope.
19
19
  except(:select, :create_with, :includes, :preload, :joins, :eager_load)
20
20
  )
@@ -81,7 +81,7 @@ module ActiveRecord
81
81
  end
82
82
 
83
83
  def extract_callstack_for_multiparameter_attributes(pairs)
84
- attributes = { }
84
+ attributes = {}
85
85
 
86
86
  pairs.each do |(multiparameter_name, value)|
87
87
  attribute_name = multiparameter_name.split("(").first
@@ -146,7 +146,7 @@ module ActiveRecord
146
146
  end
147
147
  else
148
148
  # else column is a timestamp, so if Date bits were not provided, error
149
- validate_missing_parameters!([1,2,3])
149
+ validate_required_parameters!([1,2,3])
150
150
 
151
151
  # If Date bits were provided but blank, then return nil
152
152
  return if blank_date_parameter?
@@ -172,14 +172,14 @@ module ActiveRecord
172
172
  def read_other(klass)
173
173
  max_position = extract_max_param
174
174
  positions = (1..max_position)
175
- validate_missing_parameters!(positions)
175
+ validate_required_parameters!(positions)
176
176
 
177
177
  set_values = values.values_at(*positions)
178
178
  klass.new(*set_values)
179
179
  end
180
180
 
181
181
  # Checks whether some blank date parameter exists. Note that this is different
182
- # than the validate_missing_parameters! method, since it just checks for blank
182
+ # than the validate_required_parameters! method, since it just checks for blank
183
183
  # positions instead of missing ones, and does not raise in case one blank position
184
184
  # exists. The caller is responsible to handle the case of this returning true.
185
185
  def blank_date_parameter?
@@ -187,7 +187,7 @@ module ActiveRecord
187
187
  end
188
188
 
189
189
  # If some position is not provided, it errors out a missing parameter exception.
190
- def validate_missing_parameters!(positions)
190
+ def validate_required_parameters!(positions)
191
191
  if missing_parameter = positions.detect { |position| !values.key?(position) }
192
192
  raise ArgumentError.new("Missing Parameter - #{name}(#{missing_parameter})")
193
193
  end
@@ -21,7 +21,7 @@ module ActiveRecord
21
21
  # Generates all the attribute related methods for columns in the database
22
22
  # accessors, mutators and query methods.
23
23
  def define_attribute_methods # :nodoc:
24
- # Use a mutex; we don't want two thread simaltaneously trying to define
24
+ # Use a mutex; we don't want two thread simultaneously trying to define
25
25
  # attribute methods.
26
26
  @attribute_methods_mutex.synchronize do
27
27
  return if attribute_methods_generated?
@@ -56,7 +56,7 @@ module ActiveRecord
56
56
  # # => false
57
57
  def instance_method_already_implemented?(method_name)
58
58
  if dangerous_attribute_method?(method_name)
59
- raise DangerousAttributeError, "#{method_name} is defined by ActiveRecord"
59
+ raise DangerousAttributeError, "#{method_name} is defined by Active Record"
60
60
  end
61
61
 
62
62
  if superclass == Base
@@ -163,8 +163,22 @@ module ActiveRecord
163
163
  # person.respond_to('age?') # => true
164
164
  # person.respond_to(:nothing) # => false
165
165
  def respond_to?(name, include_private = false)
166
+ name = name.to_s
166
167
  self.class.define_attribute_methods unless self.class.attribute_methods_generated?
167
- super
168
+ result = super
169
+
170
+ # If the result is false the answer is false.
171
+ return false unless result
172
+
173
+ # If the result is true then check for the select case.
174
+ # For queries selecting a subset of columns, return false for unselected columns.
175
+ # We check defined?(@attributes) not to issue warnings if called on objects that
176
+ # have been allocated but not yet initialized.
177
+ if defined?(@attributes) && @attributes.present? && self.class.column_names.include?(name)
178
+ return has_attribute?(name)
179
+ end
180
+
181
+ return true
168
182
  end
169
183
 
170
184
  # Returns +true+ if the given attribute is in the attributes hash, otherwise +false+.
@@ -328,13 +342,14 @@ module ActiveRecord
328
342
  end
329
343
 
330
344
  def attribute_method?(attr_name) # :nodoc:
345
+ # We check defined? because Syck calls respond_to? before actually calling initialize.
331
346
  defined?(@attributes) && @attributes.include?(attr_name)
332
347
  end
333
348
 
334
349
  private
335
350
 
336
351
  # Returns a Hash of the Arel::Attributes and attribute values that have been
337
- # type casted for use in an Arel insert/update method.
352
+ # typecasted for use in an Arel insert/update method.
338
353
  def arel_attributes_with_values(attribute_names)
339
354
  attrs = {}
340
355
  arel_table = self.class.arel_table
@@ -348,7 +363,7 @@ module ActiveRecord
348
363
  # Filters the primary keys and readonly attributes from the attribute names.
349
364
  def attributes_for_update(attribute_names)
350
365
  attribute_names.select do |name|
351
- column_for_attribute(name) && !pk_attribute?(name) && !readonly_attribute?(name)
366
+ column_for_attribute(name) && !readonly_attribute?(name)
352
367
  end
353
368
  end
354
369