activerecord 4.0.4 → 4.1.16

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 (143) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1632 -1797
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +2 -2
  5. data/examples/performance.rb +30 -18
  6. data/examples/simple.rb +4 -4
  7. data/lib/active_record/aggregations.rb +2 -1
  8. data/lib/active_record/association_relation.rb +4 -0
  9. data/lib/active_record/associations/alias_tracker.rb +49 -29
  10. data/lib/active_record/associations/association.rb +9 -17
  11. data/lib/active_record/associations/association_scope.rb +59 -49
  12. data/lib/active_record/associations/belongs_to_association.rb +34 -25
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +6 -1
  14. data/lib/active_record/associations/builder/association.rb +84 -54
  15. data/lib/active_record/associations/builder/belongs_to.rb +90 -58
  16. data/lib/active_record/associations/builder/collection_association.rb +47 -45
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +119 -25
  18. data/lib/active_record/associations/builder/has_many.rb +3 -3
  19. data/lib/active_record/associations/builder/has_one.rb +5 -7
  20. data/lib/active_record/associations/builder/singular_association.rb +6 -7
  21. data/lib/active_record/associations/collection_association.rb +121 -111
  22. data/lib/active_record/associations/collection_proxy.rb +73 -18
  23. data/lib/active_record/associations/has_many_association.rb +14 -11
  24. data/lib/active_record/associations/has_many_through_association.rb +33 -6
  25. data/lib/active_record/associations/has_one_association.rb +1 -1
  26. data/lib/active_record/associations/join_dependency/join_association.rb +46 -104
  27. data/lib/active_record/associations/join_dependency/join_base.rb +6 -8
  28. data/lib/active_record/associations/join_dependency/join_part.rb +18 -37
  29. data/lib/active_record/associations/join_dependency.rb +208 -168
  30. data/lib/active_record/associations/preloader/association.rb +69 -27
  31. data/lib/active_record/associations/preloader/collection_association.rb +2 -2
  32. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  33. data/lib/active_record/associations/preloader/singular_association.rb +3 -3
  34. data/lib/active_record/associations/preloader/through_association.rb +58 -26
  35. data/lib/active_record/associations/preloader.rb +63 -49
  36. data/lib/active_record/associations/singular_association.rb +6 -5
  37. data/lib/active_record/associations/through_association.rb +30 -9
  38. data/lib/active_record/associations.rb +116 -42
  39. data/lib/active_record/attribute_assignment.rb +6 -3
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +2 -1
  41. data/lib/active_record/attribute_methods/dirty.rb +35 -26
  42. data/lib/active_record/attribute_methods/primary_key.rb +8 -1
  43. data/lib/active_record/attribute_methods/read.rb +56 -29
  44. data/lib/active_record/attribute_methods/serialization.rb +44 -12
  45. data/lib/active_record/attribute_methods/time_zone_conversion.rb +13 -1
  46. data/lib/active_record/attribute_methods/write.rb +59 -26
  47. data/lib/active_record/attribute_methods.rb +82 -43
  48. data/lib/active_record/autosave_association.rb +209 -194
  49. data/lib/active_record/base.rb +6 -2
  50. data/lib/active_record/callbacks.rb +2 -2
  51. data/lib/active_record/coders/json.rb +13 -0
  52. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +5 -10
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +14 -24
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +13 -13
  55. data/lib/active_record/connection_adapters/abstract/quoting.rb +6 -3
  56. data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
  57. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +90 -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 +45 -70
  60. data/lib/active_record/connection_adapters/abstract/transaction.rb +1 -0
  61. data/lib/active_record/connection_adapters/abstract_adapter.rb +28 -96
  62. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +74 -66
  63. data/lib/active_record/connection_adapters/column.rb +1 -35
  64. data/lib/active_record/connection_adapters/connection_specification.rb +231 -43
  65. data/lib/active_record/connection_adapters/mysql2_adapter.rb +10 -5
  66. data/lib/active_record/connection_adapters/mysql_adapter.rb +24 -17
  67. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +22 -15
  68. data/lib/active_record/connection_adapters/postgresql/cast.rb +12 -4
  69. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +18 -44
  70. data/lib/active_record/connection_adapters/postgresql/oid.rb +38 -14
  71. data/lib/active_record/connection_adapters/postgresql/quoting.rb +37 -12
  72. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +20 -11
  73. data/lib/active_record/connection_adapters/postgresql_adapter.rb +98 -52
  74. data/lib/active_record/connection_adapters/schema_cache.rb +8 -29
  75. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +55 -60
  76. data/lib/active_record/connection_handling.rb +39 -5
  77. data/lib/active_record/core.rb +38 -54
  78. data/lib/active_record/counter_cache.rb +9 -10
  79. data/lib/active_record/dynamic_matchers.rb +6 -2
  80. data/lib/active_record/enum.rb +199 -0
  81. data/lib/active_record/errors.rb +22 -5
  82. data/lib/active_record/fixture_set/file.rb +2 -1
  83. data/lib/active_record/fixtures.rb +173 -76
  84. data/lib/active_record/gem_version.rb +15 -0
  85. data/lib/active_record/inheritance.rb +23 -9
  86. data/lib/active_record/integration.rb +54 -1
  87. data/lib/active_record/locking/optimistic.rb +7 -2
  88. data/lib/active_record/locking/pessimistic.rb +1 -1
  89. data/lib/active_record/log_subscriber.rb +6 -13
  90. data/lib/active_record/migration/command_recorder.rb +8 -2
  91. data/lib/active_record/migration.rb +91 -56
  92. data/lib/active_record/model_schema.rb +7 -14
  93. data/lib/active_record/nested_attributes.rb +25 -13
  94. data/lib/active_record/no_touching.rb +52 -0
  95. data/lib/active_record/null_relation.rb +26 -6
  96. data/lib/active_record/persistence.rb +23 -29
  97. data/lib/active_record/querying.rb +15 -12
  98. data/lib/active_record/railtie.rb +12 -61
  99. data/lib/active_record/railties/databases.rake +37 -56
  100. data/lib/active_record/readonly_attributes.rb +0 -6
  101. data/lib/active_record/reflection.rb +230 -79
  102. data/lib/active_record/relation/batches.rb +74 -24
  103. data/lib/active_record/relation/calculations.rb +52 -48
  104. data/lib/active_record/relation/delegation.rb +54 -39
  105. data/lib/active_record/relation/finder_methods.rb +210 -67
  106. data/lib/active_record/relation/merger.rb +15 -12
  107. data/lib/active_record/relation/predicate_builder/array_handler.rb +29 -0
  108. data/lib/active_record/relation/predicate_builder/relation_handler.rb +17 -0
  109. data/lib/active_record/relation/predicate_builder.rb +81 -40
  110. data/lib/active_record/relation/query_methods.rb +185 -108
  111. data/lib/active_record/relation/spawn_methods.rb +8 -5
  112. data/lib/active_record/relation.rb +79 -84
  113. data/lib/active_record/result.rb +45 -6
  114. data/lib/active_record/runtime_registry.rb +5 -0
  115. data/lib/active_record/sanitization.rb +4 -4
  116. data/lib/active_record/schema_dumper.rb +18 -6
  117. data/lib/active_record/schema_migration.rb +31 -18
  118. data/lib/active_record/scoping/default.rb +5 -18
  119. data/lib/active_record/scoping/named.rb +14 -29
  120. data/lib/active_record/scoping.rb +5 -0
  121. data/lib/active_record/store.rb +67 -18
  122. data/lib/active_record/tasks/database_tasks.rb +66 -26
  123. data/lib/active_record/tasks/mysql_database_tasks.rb +16 -10
  124. data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
  125. data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
  126. data/lib/active_record/timestamp.rb +6 -6
  127. data/lib/active_record/transactions.rb +10 -12
  128. data/lib/active_record/validations/presence.rb +1 -1
  129. data/lib/active_record/validations/uniqueness.rb +19 -9
  130. data/lib/active_record/version.rb +4 -7
  131. data/lib/active_record.rb +5 -7
  132. data/lib/rails/generators/active_record/migration/migration_generator.rb +4 -0
  133. data/lib/rails/generators/active_record/migration.rb +18 -0
  134. data/lib/rails/generators/active_record/model/model_generator.rb +4 -0
  135. data/lib/rails/generators/active_record.rb +2 -8
  136. metadata +18 -30
  137. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -65
  138. data/lib/active_record/associations/join_helper.rb +0 -45
  139. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  140. data/lib/active_record/tasks/firebird_database_tasks.rb +0 -56
  141. data/lib/active_record/tasks/oracle_database_tasks.rb +0 -45
  142. data/lib/active_record/tasks/sqlserver_database_tasks.rb +0 -48
  143. data/lib/active_record/test_case.rb +0 -96
@@ -0,0 +1,199 @@
1
+ require 'active_support/core_ext/object/deep_dup'
2
+
3
+ module ActiveRecord
4
+ # Declare an enum attribute where the values map to integers in the database,
5
+ # but can be queried by name. Example:
6
+ #
7
+ # class Conversation < ActiveRecord::Base
8
+ # enum status: [ :active, :archived ]
9
+ # end
10
+ #
11
+ # # conversation.update! status: 0
12
+ # conversation.active!
13
+ # conversation.active? # => true
14
+ # conversation.status # => "active"
15
+ #
16
+ # # conversation.update! status: 1
17
+ # conversation.archived!
18
+ # conversation.archived? # => true
19
+ # conversation.status # => "archived"
20
+ #
21
+ # # conversation.update! status: 1
22
+ # conversation.status = "archived"
23
+ #
24
+ # # conversation.update! status: nil
25
+ # conversation.status = nil
26
+ # conversation.status.nil? # => true
27
+ # conversation.status # => nil
28
+ #
29
+ # Scopes based on the allowed values of the enum field will be provided
30
+ # as well. With the above example, it will create an +active+ and +archived+
31
+ # scope.
32
+ #
33
+ # You can set the default value from the database declaration, like:
34
+ #
35
+ # create_table :conversations do |t|
36
+ # t.column :status, :integer, default: 0
37
+ # end
38
+ #
39
+ # Good practice is to let the first declared status be the default.
40
+ #
41
+ # Finally, it's also possible to explicitly map the relation between attribute and
42
+ # database integer with a +Hash+:
43
+ #
44
+ # class Conversation < ActiveRecord::Base
45
+ # enum status: { active: 0, archived: 1 }
46
+ # end
47
+ #
48
+ # Note that when an +Array+ is used, the implicit mapping from the values to database
49
+ # integers is derived from the order the values appear in the array. In the example,
50
+ # <tt>:active</tt> is mapped to +0+ as it's the first element, and <tt>:archived</tt>
51
+ # is mapped to +1+. In general, the +i+-th element is mapped to <tt>i-1</tt> in the
52
+ # database.
53
+ #
54
+ # Therefore, once a value is added to the enum array, its position in the array must
55
+ # be maintained, and new values should only be added to the end of the array. To
56
+ # remove unused values, the explicit +Hash+ syntax should be used.
57
+ #
58
+ # In rare circumstances you might need to access the mapping directly.
59
+ # The mappings are exposed through a class method with the pluralized attribute
60
+ # name:
61
+ #
62
+ # Conversation.statuses # => { "active" => 0, "archived" => 1 }
63
+ #
64
+ # Use that class method when you need to know the ordinal value of an enum:
65
+ #
66
+ # Conversation.where("status <> ?", Conversation.statuses[:archived])
67
+ #
68
+ # Where conditions on an enum attribute must use the ordinal value of an enum.
69
+ module Enum
70
+ def self.extended(base) # :nodoc:
71
+ base.class_attribute(:defined_enums, instance_writer: false)
72
+ base.defined_enums = {}
73
+ end
74
+
75
+ def inherited(base) # :nodoc:
76
+ base.defined_enums = defined_enums.deep_dup
77
+ super
78
+ end
79
+
80
+ def enum(definitions)
81
+ klass = self
82
+ definitions.each do |name, values|
83
+ # statuses = { }
84
+ enum_values = ActiveSupport::HashWithIndifferentAccess.new
85
+ name = name.to_sym
86
+
87
+ # def self.statuses statuses end
88
+ detect_enum_conflict!(name, name.to_s.pluralize, true)
89
+ klass.singleton_class.send(:define_method, name.to_s.pluralize) { enum_values }
90
+
91
+ _enum_methods_module.module_eval do
92
+ # def status=(value) self[:status] = statuses[value] end
93
+ klass.send(:detect_enum_conflict!, name, "#{name}=")
94
+ define_method("#{name}=") { |value|
95
+ if enum_values.has_key?(value) || value.blank?
96
+ self[name] = enum_values[value]
97
+ elsif enum_values.has_value?(value)
98
+ # Assigning a value directly is not a end-user feature, hence it's not documented.
99
+ # This is used internally to make building objects from the generated scopes work
100
+ # as expected, i.e. +Conversation.archived.build.archived?+ should be true.
101
+ self[name] = value
102
+ else
103
+ raise ArgumentError, "'#{value}' is not a valid #{name}"
104
+ end
105
+ }
106
+
107
+ # def status() statuses.key self[:status] end
108
+ klass.send(:detect_enum_conflict!, name, name)
109
+ define_method(name) { enum_values.key self[name] }
110
+
111
+ # def status_before_type_cast() statuses.key self[:status] end
112
+ klass.send(:detect_enum_conflict!, name, "#{name}_before_type_cast")
113
+ define_method("#{name}_before_type_cast") { enum_values.key self[name] }
114
+
115
+ pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index
116
+ pairs.each do |value, i|
117
+ enum_values[value] = i
118
+
119
+ # def active?() status == 0 end
120
+ klass.send(:detect_enum_conflict!, name, "#{value}?")
121
+ define_method("#{value}?") { self[name] == i }
122
+
123
+ # def active!() update! status: :active end
124
+ klass.send(:detect_enum_conflict!, name, "#{value}!")
125
+ define_method("#{value}!") { update! name => value }
126
+
127
+ # scope :active, -> { where status: 0 }
128
+ klass.send(:detect_enum_conflict!, name, value, true)
129
+ klass.scope value, -> { klass.where name => i }
130
+ end
131
+ end
132
+ defined_enums[name.to_s] = enum_values
133
+ end
134
+ end
135
+
136
+ private
137
+ def _enum_methods_module
138
+ @_enum_methods_module ||= begin
139
+ mod = Module.new do
140
+ private
141
+ def save_changed_attribute(attr_name, value)
142
+ if (mapping = self.class.defined_enums[attr_name.to_s])
143
+ if attribute_changed?(attr_name)
144
+ old = changed_attributes[attr_name]
145
+
146
+ if mapping[old] == value
147
+ changed_attributes.delete(attr_name)
148
+ end
149
+ else
150
+ old = clone_attribute_value(:read_attribute, attr_name)
151
+
152
+ if old != value
153
+ changed_attributes[attr_name] = mapping.key old
154
+ end
155
+ end
156
+ else
157
+ super
158
+ end
159
+ end
160
+ end
161
+ include mod
162
+ mod
163
+ end
164
+ end
165
+
166
+ ENUM_CONFLICT_MESSAGE = \
167
+ "You tried to define an enum named \"%{enum}\" on the model \"%{klass}\", but " \
168
+ "this will generate a %{type} method \"%{method}\", which is already defined " \
169
+ "by %{source}."
170
+
171
+ def detect_enum_conflict!(enum_name, method_name, klass_method = false)
172
+ if klass_method && dangerous_class_method?(method_name)
173
+ raise ArgumentError, ENUM_CONFLICT_MESSAGE % {
174
+ enum: enum_name,
175
+ klass: self.name,
176
+ type: 'class',
177
+ method: method_name,
178
+ source: 'Active Record'
179
+ }
180
+ elsif !klass_method && dangerous_attribute_method?(method_name)
181
+ raise ArgumentError, ENUM_CONFLICT_MESSAGE % {
182
+ enum: enum_name,
183
+ klass: self.name,
184
+ type: 'instance',
185
+ method: method_name,
186
+ source: 'Active Record'
187
+ }
188
+ elsif !klass_method && method_defined_within?(method_name, _enum_methods_module, Module)
189
+ raise ArgumentError, ENUM_CONFLICT_MESSAGE % {
190
+ enum: enum_name,
191
+ klass: self.name,
192
+ type: 'instance',
193
+ method: method_name,
194
+ source: 'another enum'
195
+ }
196
+ end
197
+ end
198
+ end
199
+ end
@@ -69,10 +69,6 @@ module ActiveRecord
69
69
  end
70
70
  end
71
71
 
72
- # Raised when SQL statement is invalid and the application gets a blank result.
73
- class ThrowResult < ActiveRecordError
74
- end
75
-
76
72
  # Defunct wrapper class kept for compatibility.
77
73
  # +StatementInvalid+ wraps the original exception now.
78
74
  class WrappedDatabaseException < StatementInvalid
@@ -98,6 +94,18 @@ module ActiveRecord
98
94
  class PreparedStatementInvalid < ActiveRecordError
99
95
  end
100
96
 
97
+ # Raised when a given database does not exist
98
+ class NoDatabaseError < ActiveRecordError
99
+ def initialize(message)
100
+ super extend_message(message)
101
+ end
102
+
103
+ # can be over written to add additional error information.
104
+ def extend_message(message)
105
+ message
106
+ end
107
+ end
108
+
101
109
  # Raised on attempt to save stale record. Record is stale when it's being saved in another query after
102
110
  # instantiation, for example, when two users edit the same wiki page and one starts editing and saves
103
111
  # the page before the other.
@@ -159,6 +167,15 @@ module ActiveRecord
159
167
 
160
168
  # Raised when unknown attributes are supplied via mass assignment.
161
169
  class UnknownAttributeError < NoMethodError
170
+
171
+ attr_reader :record, :attribute
172
+
173
+ def initialize(record, attribute)
174
+ @record = record
175
+ @attribute = attribute.to_s
176
+ super("unknown attribute: #{attribute}")
177
+ end
178
+
162
179
  end
163
180
 
164
181
  # Raised when an error occurred while doing a mass assignment to an attribute through the
@@ -183,7 +200,7 @@ module ActiveRecord
183
200
  end
184
201
  end
185
202
 
186
- # Raised when a primary key is needed, but there is not one specified in the schema or model.
203
+ # Raised when a primary key is needed, but not specified in the schema or model.
187
204
  class UnknownPrimaryKey < ActiveRecordError
188
205
  attr_reader :model
189
206
 
@@ -38,7 +38,8 @@ module ActiveRecord
38
38
  end
39
39
 
40
40
  def render(content)
41
- ERB.new(content).result
41
+ context = ActiveRecord::FixtureSet::RenderContext.create_subclass.new
42
+ ERB.new(content).result(context.get_binding)
42
43
  end
43
44
 
44
45
  # Validate our unmarshalled data.