activerecord 4.0.13 → 4.1.0.beta1

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 (135) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +745 -2700
  3. data/README.rdoc +2 -2
  4. data/examples/performance.rb +30 -18
  5. data/examples/simple.rb +4 -4
  6. data/lib/active_record.rb +2 -6
  7. data/lib/active_record/aggregations.rb +2 -1
  8. data/lib/active_record/association_relation.rb +0 -4
  9. data/lib/active_record/associations.rb +87 -43
  10. data/lib/active_record/associations/alias_tracker.rb +1 -3
  11. data/lib/active_record/associations/association.rb +8 -16
  12. data/lib/active_record/associations/association_scope.rb +5 -16
  13. data/lib/active_record/associations/belongs_to_association.rb +34 -25
  14. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
  15. data/lib/active_record/associations/builder/association.rb +78 -54
  16. data/lib/active_record/associations/builder/belongs_to.rb +91 -58
  17. data/lib/active_record/associations/builder/collection_association.rb +47 -45
  18. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +107 -25
  19. data/lib/active_record/associations/builder/has_many.rb +2 -2
  20. data/lib/active_record/associations/builder/has_one.rb +5 -7
  21. data/lib/active_record/associations/builder/singular_association.rb +6 -7
  22. data/lib/active_record/associations/collection_association.rb +68 -105
  23. data/lib/active_record/associations/collection_proxy.rb +12 -15
  24. data/lib/active_record/associations/has_many_association.rb +11 -9
  25. data/lib/active_record/associations/has_many_through_association.rb +16 -12
  26. data/lib/active_record/associations/has_one_association.rb +1 -1
  27. data/lib/active_record/associations/join_dependency.rb +204 -165
  28. data/lib/active_record/associations/join_dependency/join_association.rb +43 -101
  29. data/lib/active_record/associations/join_dependency/join_base.rb +6 -8
  30. data/lib/active_record/associations/join_dependency/join_part.rb +18 -37
  31. data/lib/active_record/associations/join_helper.rb +2 -11
  32. data/lib/active_record/associations/preloader.rb +89 -34
  33. data/lib/active_record/associations/preloader/association.rb +43 -25
  34. data/lib/active_record/associations/preloader/collection_association.rb +2 -2
  35. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  36. data/lib/active_record/associations/preloader/singular_association.rb +3 -3
  37. data/lib/active_record/associations/preloader/through_association.rb +58 -26
  38. data/lib/active_record/associations/singular_association.rb +6 -5
  39. data/lib/active_record/associations/through_association.rb +2 -2
  40. data/lib/active_record/attribute_assignment.rb +5 -2
  41. data/lib/active_record/attribute_methods.rb +45 -40
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +2 -1
  43. data/lib/active_record/attribute_methods/dirty.rb +8 -22
  44. data/lib/active_record/attribute_methods/primary_key.rb +1 -7
  45. data/lib/active_record/attribute_methods/read.rb +55 -28
  46. data/lib/active_record/attribute_methods/serialization.rb +12 -33
  47. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -13
  48. data/lib/active_record/attribute_methods/write.rb +37 -12
  49. data/lib/active_record/autosave_association.rb +207 -207
  50. data/lib/active_record/base.rb +5 -1
  51. data/lib/active_record/callbacks.rb +2 -2
  52. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +2 -7
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +11 -22
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +12 -14
  55. data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -5
  56. data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
  57. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +84 -0
  58. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +9 -8
  59. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +52 -83
  60. data/lib/active_record/connection_adapters/abstract/transaction.rb +0 -5
  61. data/lib/active_record/connection_adapters/abstract_adapter.rb +14 -97
  62. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +58 -60
  63. data/lib/active_record/connection_adapters/column.rb +1 -35
  64. data/lib/active_record/connection_adapters/connection_specification.rb +2 -2
  65. data/lib/active_record/connection_adapters/mysql2_adapter.rb +3 -4
  66. data/lib/active_record/connection_adapters/mysql_adapter.rb +16 -15
  67. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +24 -18
  68. data/lib/active_record/connection_adapters/postgresql/cast.rb +20 -16
  69. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +23 -43
  70. data/lib/active_record/connection_adapters/postgresql/oid.rb +19 -12
  71. data/lib/active_record/connection_adapters/postgresql/quoting.rb +28 -23
  72. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +8 -30
  73. data/lib/active_record/connection_adapters/postgresql_adapter.rb +92 -75
  74. data/lib/active_record/connection_adapters/schema_cache.rb +8 -29
  75. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +31 -64
  76. data/lib/active_record/connection_handling.rb +2 -2
  77. data/lib/active_record/core.rb +22 -43
  78. data/lib/active_record/counter_cache.rb +7 -7
  79. data/lib/active_record/enum.rb +100 -0
  80. data/lib/active_record/errors.rb +10 -5
  81. data/lib/active_record/fixture_set/file.rb +2 -1
  82. data/lib/active_record/fixtures.rb +171 -74
  83. data/lib/active_record/inheritance.rb +16 -22
  84. data/lib/active_record/integration.rb +52 -1
  85. data/lib/active_record/locking/optimistic.rb +7 -2
  86. data/lib/active_record/locking/pessimistic.rb +1 -1
  87. data/lib/active_record/log_subscriber.rb +5 -12
  88. data/lib/active_record/migration.rb +62 -46
  89. data/lib/active_record/migration/command_recorder.rb +7 -13
  90. data/lib/active_record/model_schema.rb +7 -14
  91. data/lib/active_record/nested_attributes.rb +10 -8
  92. data/lib/active_record/no_touching.rb +52 -0
  93. data/lib/active_record/null_relation.rb +3 -3
  94. data/lib/active_record/persistence.rb +16 -34
  95. data/lib/active_record/querying.rb +14 -12
  96. data/lib/active_record/railtie.rb +0 -50
  97. data/lib/active_record/railties/databases.rake +12 -15
  98. data/lib/active_record/readonly_attributes.rb +0 -6
  99. data/lib/active_record/reflection.rb +189 -75
  100. data/lib/active_record/relation.rb +69 -94
  101. data/lib/active_record/relation/batches.rb +57 -23
  102. data/lib/active_record/relation/calculations.rb +36 -43
  103. data/lib/active_record/relation/delegation.rb +54 -39
  104. data/lib/active_record/relation/finder_methods.rb +107 -62
  105. data/lib/active_record/relation/merger.rb +7 -20
  106. data/lib/active_record/relation/predicate_builder.rb +57 -38
  107. data/lib/active_record/relation/predicate_builder/array_handler.rb +29 -0
  108. data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
  109. data/lib/active_record/relation/query_methods.rb +110 -98
  110. data/lib/active_record/relation/spawn_methods.rb +1 -2
  111. data/lib/active_record/result.rb +45 -6
  112. data/lib/active_record/runtime_registry.rb +5 -0
  113. data/lib/active_record/sanitization.rb +6 -8
  114. data/lib/active_record/schema_dumper.rb +16 -5
  115. data/lib/active_record/schema_migration.rb +24 -25
  116. data/lib/active_record/scoping/default.rb +5 -18
  117. data/lib/active_record/scoping/named.rb +8 -29
  118. data/lib/active_record/store.rb +56 -28
  119. data/lib/active_record/tasks/database_tasks.rb +8 -4
  120. data/lib/active_record/timestamp.rb +4 -4
  121. data/lib/active_record/transactions.rb +8 -10
  122. data/lib/active_record/validations/presence.rb +1 -1
  123. data/lib/active_record/validations/uniqueness.rb +1 -6
  124. data/lib/active_record/version.rb +1 -1
  125. data/lib/rails/generators/active_record.rb +2 -8
  126. data/lib/rails/generators/active_record/migration.rb +18 -0
  127. data/lib/rails/generators/active_record/migration/migration_generator.rb +4 -0
  128. data/lib/rails/generators/active_record/model/model_generator.rb +4 -0
  129. metadata +32 -45
  130. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -65
  131. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  132. data/lib/active_record/tasks/firebird_database_tasks.rb +0 -56
  133. data/lib/active_record/tasks/oracle_database_tasks.rb +0 -45
  134. data/lib/active_record/tasks/sqlserver_database_tasks.rb +0 -48
  135. data/lib/active_record/test_case.rb +0 -102
@@ -3,6 +3,7 @@ module ActiveRecord
3
3
  class Preloader
4
4
  class Association #:nodoc:
5
5
  attr_reader :owners, :reflection, :preload_scope, :model, :klass
6
+ attr_reader :preloaded_records
6
7
 
7
8
  def initialize(klass, owners, reflection, preload_scope)
8
9
  @klass = klass
@@ -12,15 +13,14 @@ module ActiveRecord
12
13
  @model = owners.first && owners.first.class
13
14
  @scope = nil
14
15
  @owners_by_key = nil
16
+ @preloaded_records = []
15
17
  end
16
18
 
17
- def run
18
- unless owners.first.association(reflection.name).loaded?
19
- preload
20
- end
19
+ def run(preloader)
20
+ preload(preloader)
21
21
  end
22
22
 
23
- def preload
23
+ def preload(preloader)
24
24
  raise NotImplementedError
25
25
  end
26
26
 
@@ -29,6 +29,10 @@ module ActiveRecord
29
29
  end
30
30
 
31
31
  def records_for(ids)
32
+ query_scope(ids)
33
+ end
34
+
35
+ def query_scope(ids)
32
36
  scope.where(association_key.in(ids))
33
37
  end
34
38
 
@@ -52,12 +56,9 @@ module ActiveRecord
52
56
  raise NotImplementedError
53
57
  end
54
58
 
55
- # We're converting to a string here because postgres will return the aliased association
56
- # key in a habtm as a string (for whatever reason)
57
59
  def owners_by_key
58
60
  @owners_by_key ||= owners.group_by do |owner|
59
- key = owner[owner_key_name]
60
- key && key.to_s
61
+ owner[owner_key_name]
61
62
  end
62
63
  end
63
64
 
@@ -67,38 +68,47 @@ module ActiveRecord
67
68
 
68
69
  private
69
70
 
70
- def associated_records_by_owner
71
+ def associated_records_by_owner(preloader)
71
72
  owners_map = owners_by_key
72
73
  owner_keys = owners_map.keys.compact
73
74
 
74
- if klass.nil? || owner_keys.empty?
75
- records = []
76
- else
75
+ # Each record may have multiple owners, and vice-versa
76
+ records_by_owner = owners.each_with_object({}) do |owner,h|
77
+ h[owner] = []
78
+ end
79
+
80
+ if owner_keys.any?
77
81
  # Some databases impose a limit on the number of ids in a list (in Oracle it's 1000)
78
82
  # Make several smaller queries if necessary or make one query if the adapter supports it
79
83
  sliced = owner_keys.each_slice(klass.connection.in_clause_length || owner_keys.size)
80
- records = sliced.map { |slice| records_for(slice).to_a }.flatten
81
- end
82
-
83
- # Each record may have multiple owners, and vice-versa
84
- records_by_owner = Hash[owners.map { |owner| [owner, []] }]
85
- records.each do |record|
86
- owner_key = record[association_key_name].to_s
87
84
 
88
- owners_map[owner_key].each do |owner|
89
- records_by_owner[owner] << record
85
+ records = load_slices sliced
86
+ records.each do |record, owner_key|
87
+ owners_map[owner_key].each do |owner|
88
+ records_by_owner[owner] << record
89
+ end
90
90
  end
91
91
  end
92
+
92
93
  records_by_owner
93
94
  end
94
95
 
96
+ def load_slices(slices)
97
+ @preloaded_records = slices.flat_map { |slice|
98
+ records_for(slice)
99
+ }
100
+
101
+ @preloaded_records.map { |record|
102
+ [record, record[association_key_name]]
103
+ }
104
+ end
105
+
95
106
  def reflection_scope
96
107
  @reflection_scope ||= reflection.scope ? klass.unscoped.instance_exec(nil, &reflection.scope) : klass.unscoped
97
108
  end
98
109
 
99
110
  def build_scope
100
111
  scope = klass.unscoped
101
- scope.default_scoped = true
102
112
 
103
113
  values = reflection_scope.values
104
114
  preload_values = preload_scope.values
@@ -106,14 +116,22 @@ module ActiveRecord
106
116
  scope.where_values = Array(values[:where]) + Array(preload_values[:where])
107
117
  scope.references_values = Array(values[:references]) + Array(preload_values[:references])
108
118
 
109
- scope._select! preload_values[:select] || values[:select] || table[Arel.star]
119
+ scope.select! preload_values[:select] || values[:select] || table[Arel.star]
110
120
  scope.includes! preload_values[:includes] || values[:includes]
111
121
 
122
+ if preload_values.key? :order
123
+ scope.order! preload_values[:order]
124
+ else
125
+ if values.key? :order
126
+ scope.order! values[:order]
127
+ end
128
+ end
129
+
112
130
  if options[:as]
113
131
  scope.where!(klass.table_name => { reflection.type => model.base_class.sti_name })
114
132
  end
115
133
 
116
- scope
134
+ klass.default_scoped.merge(scope)
117
135
  end
118
136
  end
119
137
  end
@@ -9,8 +9,8 @@ module ActiveRecord
9
9
  super.order(preload_scope.values[:order] || reflection_scope.values[:order])
10
10
  end
11
11
 
12
- def preload
13
- associated_records_by_owner.each do |owner, records|
12
+ def preload(preloader)
13
+ associated_records_by_owner(preloader).each do |owner, records|
14
14
  association = owner.association(reflection.name)
15
15
  association.loaded!
16
16
  association.target.concat(records)
@@ -4,7 +4,7 @@ module ActiveRecord
4
4
  class HasManyThrough < CollectionAssociation #:nodoc:
5
5
  include ThroughAssociation
6
6
 
7
- def associated_records_by_owner
7
+ def associated_records_by_owner(preloader)
8
8
  records_by_owner = super
9
9
 
10
10
  if reflection_scope.distinct_value
@@ -5,13 +5,13 @@ module ActiveRecord
5
5
 
6
6
  private
7
7
 
8
- def preload
9
- associated_records_by_owner.each do |owner, associated_records|
8
+ def preload(preloader)
9
+ associated_records_by_owner(preloader).each do |owner, associated_records|
10
10
  record = associated_records.first
11
11
 
12
12
  association = owner.association(reflection.name)
13
13
  association.target = record
14
- association.set_inverse_instance(record)
14
+ association.set_inverse_instance(record) if record
15
15
  end
16
16
  end
17
17
 
@@ -2,7 +2,6 @@ module ActiveRecord
2
2
  module Associations
3
3
  class Preloader
4
4
  module ThroughAssociation #:nodoc:
5
-
6
5
  def through_reflection
7
6
  reflection.through_reflection
8
7
  end
@@ -11,51 +10,84 @@ module ActiveRecord
11
10
  reflection.source_reflection
12
11
  end
13
12
 
14
- def associated_records_by_owner
15
- through_records = through_records_by_owner
13
+ def associated_records_by_owner(preloader)
14
+ preloader.preload(owners,
15
+ through_reflection.name,
16
+ through_scope)
16
17
 
17
- Preloader.new(through_records.values.flatten, source_reflection.name, reflection_scope).run
18
+ through_records = owners.map do |owner|
19
+ association = owner.association through_reflection.name
18
20
 
19
- through_records.each do |owner, records|
20
- records.map! { |r| r.send(source_reflection.name) }.flatten!
21
- records.compact!
21
+ [owner, Array(association.reader)]
22
22
  end
23
- end
24
23
 
25
- private
24
+ reset_association owners, through_reflection.name
25
+
26
+ middle_records = through_records.map { |(_,rec)| rec }.flatten
27
+
28
+ preloaders = preloader.preload(middle_records,
29
+ source_reflection.name,
30
+ reflection_scope)
26
31
 
27
- def through_records_by_owner
28
- Preloader.new(owners, through_reflection.name, through_scope).run
32
+ @preloaded_records = preloaders.flat_map(&:preloaded_records)
33
+
34
+ middle_to_pl = preloaders.each_with_object({}) do |pl,h|
35
+ pl.owners.each { |middle|
36
+ h[middle] = pl
37
+ }
38
+ end
39
+
40
+ record_offset = {}
41
+ @preloaded_records.each_with_index do |record,i|
42
+ record_offset[record] = i
43
+ end
29
44
 
30
- Hash[owners.map do |owner|
31
- through_records = Array.wrap(owner.send(through_reflection.name))
45
+ through_records.each_with_object({}) { |(lhs,center),records_by_owner|
46
+ pl_to_middle = center.group_by { |record| middle_to_pl[record] }
32
47
 
33
- # Dont cache the association - we would only be caching a subset
34
- if (through_scope != through_reflection.klass.unscoped) ||
35
- (reflection.options[:source_type] && through_reflection.collection?)
36
- owner.association(through_reflection.name).reset
48
+ records_by_owner[lhs] = pl_to_middle.flat_map do |pl, middles|
49
+ rhs_records = middles.flat_map { |r|
50
+ association = r.association source_reflection.name
51
+
52
+ association.reader
53
+ }.compact
54
+
55
+ rhs_records.sort_by { |rhs| record_offset[rhs] }
37
56
  end
57
+ }
58
+ end
59
+
60
+ private
61
+
62
+ def reset_association(owners, association_name)
63
+ should_reset = (through_scope != through_reflection.klass.unscoped) ||
64
+ (reflection.options[:source_type] && through_reflection.collection?)
38
65
 
39
- [owner, through_records]
40
- end]
66
+ # Dont cache the association - we would only be caching a subset
67
+ if should_reset
68
+ owners.each { |owner|
69
+ owner.association(association_name).reset
70
+ }
71
+ end
41
72
  end
42
73
 
74
+
43
75
  def through_scope
44
- through_scope = through_reflection.klass.unscoped
76
+ scope = through_reflection.klass.unscoped
45
77
 
46
78
  if options[:source_type]
47
- through_scope.where! reflection.foreign_type => options[:source_type]
79
+ scope.where! reflection.foreign_type => options[:source_type]
48
80
  else
49
81
  unless reflection_scope.where_values.empty?
50
- through_scope.includes_values = Array(reflection_scope.values[:includes] || options[:source])
51
- through_scope.where_values = reflection_scope.values[:where]
82
+ scope.includes_values = Array(reflection_scope.values[:includes] || options[:source])
83
+ scope.where_values = reflection_scope.values[:where]
52
84
  end
53
85
 
54
- through_scope.references! reflection_scope.values[:references]
55
- through_scope.order! reflection_scope.values[:order] if through_scope.eager_loading?
86
+ scope.references! reflection_scope.values[:references]
87
+ scope.order! reflection_scope.values[:order] if scope.eager_loading?
56
88
  end
57
89
 
58
- through_scope
90
+ scope
59
91
  end
60
92
  end
61
93
  end
@@ -18,11 +18,11 @@ module ActiveRecord
18
18
  end
19
19
 
20
20
  def create(attributes = {}, &block)
21
- _create_record(attributes, &block)
21
+ create_record(attributes, &block)
22
22
  end
23
23
 
24
24
  def create!(attributes = {}, &block)
25
- _create_record(attributes, true, &block)
25
+ create_record(attributes, true, &block)
26
26
  end
27
27
 
28
28
  def build(attributes = {})
@@ -39,10 +39,11 @@ module ActiveRecord
39
39
  end
40
40
 
41
41
  def find_target
42
- scope.take.tap { |record| set_inverse_instance(record) }
42
+ if record = scope.first
43
+ set_inverse_instance record
44
+ end
43
45
  end
44
46
 
45
- # Implemented by subclasses
46
47
  def replace(record)
47
48
  raise NotImplementedError, "Subclasses must implement a replace(record) method"
48
49
  end
@@ -51,7 +52,7 @@ module ActiveRecord
51
52
  replace(record)
52
53
  end
53
54
 
54
- def _create_record(attributes, raise_error = false)
55
+ def create_record(attributes, raise_error = false)
55
56
  record = build_record(attributes)
56
57
  yield(record) if block_given?
57
58
  saved = record.save
@@ -13,9 +13,9 @@ module ActiveRecord
13
13
  # 2. To get the type conditions for any STI models in the chain
14
14
  def target_scope
15
15
  scope = super
16
- chain[1..-1].each do |reflection|
16
+ chain.drop(1).each do |reflection|
17
17
  scope.merge!(
18
- reflection.klass.all.with_default_scope.
18
+ reflection.klass.all.
19
19
  except(:select, :create_with, :includes, :preload, :joins, :eager_load)
20
20
  )
21
21
  end
@@ -1,8 +1,8 @@
1
+ require 'active_model/forbidden_attributes_protection'
1
2
 
2
3
  module ActiveRecord
3
4
  module AttributeAssignment
4
5
  extend ActiveSupport::Concern
5
- include ActiveModel::DeprecatedMassAssignmentSecurity
6
6
  include ActiveModel::ForbiddenAttributesProtection
7
7
 
8
8
  # Allows you to set all the attributes by passing in a hash of attributes with
@@ -12,6 +12,9 @@ module ActiveRecord
12
12
  # of this method is +false+ an <tt>ActiveModel::ForbiddenAttributesError</tt>
13
13
  # exception is raised.
14
14
  def assign_attributes(new_attributes)
15
+ if !new_attributes.respond_to?(:stringify_keys)
16
+ raise ArgumentError, "When assigning attributes, you must pass a hash as an argument."
17
+ end
15
18
  return if new_attributes.blank?
16
19
 
17
20
  attributes = new_attributes.stringify_keys
@@ -44,7 +47,7 @@ module ActiveRecord
44
47
  if respond_to?("#{k}=")
45
48
  raise
46
49
  else
47
- raise UnknownAttributeError, "unknown attribute: #{k}"
50
+ raise UnknownAttributeError.new(self, k)
48
51
  end
49
52
  end
50
53
 
@@ -1,5 +1,6 @@
1
1
  require 'active_support/core_ext/enumerable'
2
2
  require 'mutex_m'
3
+ require 'thread_safe'
3
4
 
4
5
  module ActiveRecord
5
6
  # = Active Record Attribute Methods
@@ -19,6 +20,35 @@ module ActiveRecord
19
20
  include Serialization
20
21
  end
21
22
 
23
+ AttrNames = Module.new {
24
+ def self.set_name_cache(name, value)
25
+ const_name = "ATTR_#{name}"
26
+ unless const_defined? const_name
27
+ const_set const_name, value.dup.freeze
28
+ end
29
+ end
30
+ }
31
+
32
+ class AttributeMethodCache
33
+ def initialize
34
+ @module = Module.new
35
+ @method_cache = ThreadSafe::Cache.new
36
+ end
37
+
38
+ def [](name)
39
+ @method_cache.compute_if_absent(name) do
40
+ safe_name = name.unpack('h*').first
41
+ temp_method = "__temp__#{safe_name}"
42
+ ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
43
+ @module.module_eval method_body(temp_method, safe_name), __FILE__, __LINE__
44
+ @module.instance_method temp_method
45
+ end
46
+ end
47
+
48
+ private
49
+ def method_body; raise NotImplementedError; end
50
+ end
51
+
22
52
  module ClassMethods
23
53
  def inherited(child_class) #:nodoc:
24
54
  child_class.initialize_generated_modules
@@ -26,18 +56,7 @@ module ActiveRecord
26
56
  end
27
57
 
28
58
  def initialize_generated_modules # :nodoc:
29
- @generated_attribute_methods = Module.new {
30
- extend Mutex_m
31
-
32
- const_set :AttrNames, Module.new {
33
- def self.set_name_cache(name, value)
34
- const_name = "ATTR_#{name}"
35
- unless const_defined? const_name
36
- const_set const_name, value.dup.freeze
37
- end
38
- end
39
- }
40
- }
59
+ @generated_attribute_methods = Module.new { extend Mutex_m }
41
60
  @attribute_methods_generated = false
42
61
  include @generated_attribute_methods
43
62
  end
@@ -87,8 +106,7 @@ module ActiveRecord
87
106
  else
88
107
  # If B < A and A defines its own attribute method, then we don't want to overwrite that.
89
108
  defined = method_defined_within?(method_name, superclass, superclass.generated_attribute_methods)
90
- base_defined = Base.method_defined?(method_name) || Base.private_method_defined?(method_name)
91
- defined && !base_defined || super
109
+ defined && !ActiveRecord::Base.method_defined?(method_name) || super
92
110
  end
93
111
  end
94
112
 
@@ -161,7 +179,6 @@ module ActiveRecord
161
179
  # this is probably horribly slow, but should only happen at most once for a given AR class
162
180
  attribute_method.bind(self).call(*args, &block)
163
181
  else
164
- return super unless respond_to_missing?(method, true)
165
182
  send(method, *args, &block)
166
183
  end
167
184
  else
@@ -169,20 +186,6 @@ module ActiveRecord
169
186
  end
170
187
  end
171
188
 
172
- def attribute_missing(match, *args, &block) # :nodoc:
173
- if self.class.columns_hash[match.attr_name]
174
- ActiveSupport::Deprecation.warn(
175
- "The method `#{match.method_name}', matching the attribute `#{match.attr_name}' has " \
176
- "dispatched through method_missing. This shouldn't happen, because `#{match.attr_name}' " \
177
- "is a column of the table. If this error has happened through normal usage of Active " \
178
- "Record (rather than through your own code or external libraries), please report it as " \
179
- "a bug."
180
- )
181
- end
182
-
183
- super
184
- end
185
-
186
189
  # A Person object with a name attribute can ask <tt>person.respond_to?(:name)</tt>,
187
190
  # <tt>person.respond_to?(:name=)</tt>, and <tt>person.respond_to?(:name?)</tt>
188
191
  # which will all return +true+. It also define the attribute methods if they have
@@ -211,7 +214,7 @@ module ActiveRecord
211
214
  # For queries selecting a subset of columns, return false for unselected columns.
212
215
  # We check defined?(@attributes) not to issue warnings if called on objects that
213
216
  # have been allocated but not yet initialized.
214
- if defined?(@attributes) && @attributes.present? && self.class.column_names.include?(name)
217
+ if defined?(@attributes) && @attributes.any? && self.class.column_names.include?(name)
215
218
  return has_attribute?(name)
216
219
  end
217
220
 
@@ -257,31 +260,33 @@ module ActiveRecord
257
260
  }
258
261
  end
259
262
 
260
- # Placeholder so it can be overriden when needed by serialization
261
- def attributes_for_coder # :nodoc:
262
- attributes
263
- end
264
-
265
263
  # Returns an <tt>#inspect</tt>-like string for the value of the
266
264
  # attribute +attr_name+. String attributes are truncated upto 50
267
- # characters, and Date and Time attributes are returned in the
268
- # <tt>:db</tt> format. Other attributes return the value of
269
- # <tt>#inspect</tt> without modification.
265
+ # characters, Date and Time attributes are returned in the
266
+ # <tt>:db</tt> format, Array attributes are truncated upto 10 values.
267
+ # Other attributes return the value of <tt>#inspect</tt> without
268
+ # modification.
270
269
  #
271
270
  # person = Person.create!(name: 'David Heinemeier Hansson ' * 3)
272
271
  #
273
272
  # person.attribute_for_inspect(:name)
274
- # # => "\"David Heinemeier Hansson David Heinemeier Hansson D...\""
273
+ # # => "\"David Heinemeier Hansson David Heinemeier Hansson ...\""
275
274
  #
276
275
  # person.attribute_for_inspect(:created_at)
277
276
  # # => "\"2012-10-22 00:15:07\""
277
+ #
278
+ # person.attribute_for_inspect(:tag_ids)
279
+ # # => "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...]"
278
280
  def attribute_for_inspect(attr_name)
279
281
  value = read_attribute(attr_name)
280
282
 
281
283
  if value.is_a?(String) && value.length > 50
282
- "#{value[0..50]}...".inspect
284
+ "#{value[0, 50]}...".inspect
283
285
  elsif value.is_a?(Date) || value.is_a?(Time)
284
286
  %("#{value.to_s(:db)}")
287
+ elsif value.is_a?(Array) && value.size > 10
288
+ inspected = value.first(10).inspect
289
+ %(#{inspected[0...-1]}, ...])
285
290
  else
286
291
  value.inspect
287
292
  end