activerecord 3.1.10 → 4.2.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (237) hide show
  1. checksums.yaml +6 -6
  2. data/CHANGELOG.md +1837 -338
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +39 -43
  5. data/examples/performance.rb +51 -20
  6. data/examples/simple.rb +4 -4
  7. data/lib/active_record/aggregations.rb +57 -43
  8. data/lib/active_record/association_relation.rb +35 -0
  9. data/lib/active_record/associations/alias_tracker.rb +47 -39
  10. data/lib/active_record/associations/association.rb +71 -85
  11. data/lib/active_record/associations/association_scope.rb +138 -89
  12. data/lib/active_record/associations/belongs_to_association.rb +65 -25
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +9 -3
  14. data/lib/active_record/associations/builder/association.rb +125 -29
  15. data/lib/active_record/associations/builder/belongs_to.rb +91 -60
  16. data/lib/active_record/associations/builder/collection_association.rb +69 -49
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +113 -42
  18. data/lib/active_record/associations/builder/has_many.rb +8 -64
  19. data/lib/active_record/associations/builder/has_one.rb +12 -52
  20. data/lib/active_record/associations/builder/singular_association.rb +22 -29
  21. data/lib/active_record/associations/collection_association.rb +294 -187
  22. data/lib/active_record/associations/collection_proxy.rb +961 -94
  23. data/lib/active_record/associations/foreign_association.rb +11 -0
  24. data/lib/active_record/associations/has_many_association.rb +118 -23
  25. data/lib/active_record/associations/has_many_through_association.rb +115 -45
  26. data/lib/active_record/associations/has_one_association.rb +57 -24
  27. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  28. data/lib/active_record/associations/join_dependency/join_association.rb +76 -102
  29. data/lib/active_record/associations/join_dependency/join_base.rb +7 -9
  30. data/lib/active_record/associations/join_dependency/join_part.rb +30 -37
  31. data/lib/active_record/associations/join_dependency.rb +230 -156
  32. data/lib/active_record/associations/preloader/association.rb +96 -55
  33. data/lib/active_record/associations/preloader/collection_association.rb +3 -3
  34. data/lib/active_record/associations/preloader/has_many_through.rb +7 -3
  35. data/lib/active_record/associations/preloader/has_one.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 +61 -32
  38. data/lib/active_record/associations/preloader.rb +113 -87
  39. data/lib/active_record/associations/singular_association.rb +29 -13
  40. data/lib/active_record/associations/through_association.rb +37 -19
  41. data/lib/active_record/associations.rb +505 -371
  42. data/lib/active_record/attribute.rb +163 -0
  43. data/lib/active_record/attribute_assignment.rb +212 -0
  44. data/lib/active_record/attribute_decorators.rb +66 -0
  45. data/lib/active_record/attribute_methods/before_type_cast.rb +52 -7
  46. data/lib/active_record/attribute_methods/dirty.rb +141 -51
  47. data/lib/active_record/attribute_methods/primary_key.rb +87 -36
  48. data/lib/active_record/attribute_methods/query.rb +5 -4
  49. data/lib/active_record/attribute_methods/read.rb +74 -117
  50. data/lib/active_record/attribute_methods/serialization.rb +70 -0
  51. data/lib/active_record/attribute_methods/time_zone_conversion.rb +49 -47
  52. data/lib/active_record/attribute_methods/write.rb +60 -21
  53. data/lib/active_record/attribute_methods.rb +409 -48
  54. data/lib/active_record/attribute_set/builder.rb +106 -0
  55. data/lib/active_record/attribute_set.rb +81 -0
  56. data/lib/active_record/attributes.rb +147 -0
  57. data/lib/active_record/autosave_association.rb +279 -232
  58. data/lib/active_record/base.rb +84 -1969
  59. data/lib/active_record/callbacks.rb +66 -28
  60. data/lib/active_record/coders/json.rb +13 -0
  61. data/lib/active_record/coders/yaml_column.rb +18 -21
  62. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +422 -243
  63. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  64. data/lib/active_record/connection_adapters/abstract/database_statements.rb +170 -194
  65. data/lib/active_record/connection_adapters/abstract/query_cache.rb +32 -19
  66. data/lib/active_record/connection_adapters/abstract/quoting.rb +79 -57
  67. data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
  68. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +125 -0
  69. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +273 -170
  70. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +50 -0
  71. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +731 -254
  72. data/lib/active_record/connection_adapters/abstract/transaction.rb +215 -0
  73. data/lib/active_record/connection_adapters/abstract_adapter.rb +339 -95
  74. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +946 -0
  75. data/lib/active_record/connection_adapters/column.rb +33 -221
  76. data/lib/active_record/connection_adapters/connection_specification.rb +275 -0
  77. data/lib/active_record/connection_adapters/mysql2_adapter.rb +140 -602
  78. data/lib/active_record/connection_adapters/mysql_adapter.rb +254 -756
  79. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +93 -0
  80. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  81. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +232 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +19 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +36 -0
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +108 -0
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +596 -0
  112. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  113. data/lib/active_record/connection_adapters/postgresql_adapter.rb +445 -902
  114. data/lib/active_record/connection_adapters/schema_cache.rb +94 -0
  115. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +578 -25
  116. data/lib/active_record/connection_handling.rb +132 -0
  117. data/lib/active_record/core.rb +579 -0
  118. data/lib/active_record/counter_cache.rb +159 -102
  119. data/lib/active_record/dynamic_matchers.rb +140 -0
  120. data/lib/active_record/enum.rb +197 -0
  121. data/lib/active_record/errors.rb +102 -34
  122. data/lib/active_record/explain.rb +38 -0
  123. data/lib/active_record/explain_registry.rb +30 -0
  124. data/lib/active_record/explain_subscriber.rb +29 -0
  125. data/lib/active_record/fixture_set/file.rb +56 -0
  126. data/lib/active_record/fixtures.rb +318 -260
  127. data/lib/active_record/gem_version.rb +15 -0
  128. data/lib/active_record/inheritance.rb +247 -0
  129. data/lib/active_record/integration.rb +113 -0
  130. data/lib/active_record/legacy_yaml_adapter.rb +30 -0
  131. data/lib/active_record/locale/en.yml +8 -1
  132. data/lib/active_record/locking/optimistic.rb +80 -52
  133. data/lib/active_record/locking/pessimistic.rb +27 -5
  134. data/lib/active_record/log_subscriber.rb +25 -18
  135. data/lib/active_record/migration/command_recorder.rb +130 -38
  136. data/lib/active_record/migration/join_table.rb +15 -0
  137. data/lib/active_record/migration.rb +532 -201
  138. data/lib/active_record/model_schema.rb +342 -0
  139. data/lib/active_record/nested_attributes.rb +229 -139
  140. data/lib/active_record/no_touching.rb +52 -0
  141. data/lib/active_record/null_relation.rb +81 -0
  142. data/lib/active_record/persistence.rb +304 -99
  143. data/lib/active_record/query_cache.rb +25 -43
  144. data/lib/active_record/querying.rb +68 -0
  145. data/lib/active_record/railtie.rb +86 -45
  146. data/lib/active_record/railties/console_sandbox.rb +3 -4
  147. data/lib/active_record/railties/controller_runtime.rb +7 -4
  148. data/lib/active_record/railties/databases.rake +198 -377
  149. data/lib/active_record/railties/jdbcmysql_error.rb +2 -2
  150. data/lib/active_record/readonly_attributes.rb +23 -0
  151. data/lib/active_record/reflection.rb +516 -165
  152. data/lib/active_record/relation/batches.rb +96 -45
  153. data/lib/active_record/relation/calculations.rb +221 -144
  154. data/lib/active_record/relation/delegation.rb +140 -0
  155. data/lib/active_record/relation/finder_methods.rb +362 -243
  156. data/lib/active_record/relation/merger.rb +193 -0
  157. data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
  158. data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
  159. data/lib/active_record/relation/predicate_builder.rb +135 -41
  160. data/lib/active_record/relation/query_methods.rb +982 -155
  161. data/lib/active_record/relation/spawn_methods.rb +50 -110
  162. data/lib/active_record/relation.rb +371 -180
  163. data/lib/active_record/result.rb +109 -12
  164. data/lib/active_record/runtime_registry.rb +22 -0
  165. data/lib/active_record/sanitization.rb +191 -0
  166. data/lib/active_record/schema.rb +19 -13
  167. data/lib/active_record/schema_dumper.rb +111 -61
  168. data/lib/active_record/schema_migration.rb +53 -0
  169. data/lib/active_record/scoping/default.rb +135 -0
  170. data/lib/active_record/scoping/named.rb +164 -0
  171. data/lib/active_record/scoping.rb +87 -0
  172. data/lib/active_record/serialization.rb +7 -45
  173. data/lib/active_record/serializers/xml_serializer.rb +14 -65
  174. data/lib/active_record/statement_cache.rb +111 -0
  175. data/lib/active_record/store.rb +205 -0
  176. data/lib/active_record/tasks/database_tasks.rb +299 -0
  177. data/lib/active_record/tasks/mysql_database_tasks.rb +159 -0
  178. data/lib/active_record/tasks/postgresql_database_tasks.rb +101 -0
  179. data/lib/active_record/tasks/sqlite_database_tasks.rb +55 -0
  180. data/lib/active_record/timestamp.rb +35 -14
  181. data/lib/active_record/transactions.rb +141 -74
  182. data/lib/active_record/translation.rb +22 -0
  183. data/lib/active_record/type/big_integer.rb +13 -0
  184. data/lib/active_record/type/binary.rb +50 -0
  185. data/lib/active_record/type/boolean.rb +31 -0
  186. data/lib/active_record/type/date.rb +50 -0
  187. data/lib/active_record/type/date_time.rb +54 -0
  188. data/lib/active_record/type/decimal.rb +64 -0
  189. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  190. data/lib/active_record/type/decorator.rb +14 -0
  191. data/lib/active_record/type/float.rb +19 -0
  192. data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
  193. data/lib/active_record/type/integer.rb +59 -0
  194. data/lib/active_record/type/mutable.rb +16 -0
  195. data/lib/active_record/type/numeric.rb +36 -0
  196. data/lib/active_record/type/serialized.rb +62 -0
  197. data/lib/active_record/type/string.rb +40 -0
  198. data/lib/active_record/type/text.rb +11 -0
  199. data/lib/active_record/type/time.rb +26 -0
  200. data/lib/active_record/type/time_value.rb +38 -0
  201. data/lib/active_record/type/type_map.rb +64 -0
  202. data/lib/active_record/type/unsigned_integer.rb +15 -0
  203. data/lib/active_record/type/value.rb +110 -0
  204. data/lib/active_record/type.rb +23 -0
  205. data/lib/active_record/validations/associated.rb +27 -18
  206. data/lib/active_record/validations/presence.rb +67 -0
  207. data/lib/active_record/validations/uniqueness.rb +125 -66
  208. data/lib/active_record/validations.rb +37 -30
  209. data/lib/active_record/version.rb +5 -7
  210. data/lib/active_record.rb +80 -25
  211. data/lib/rails/generators/active_record/migration/migration_generator.rb +54 -9
  212. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +19 -0
  213. data/lib/rails/generators/active_record/migration/templates/migration.rb +25 -11
  214. data/lib/rails/generators/active_record/migration.rb +11 -8
  215. data/lib/rails/generators/active_record/model/model_generator.rb +17 -4
  216. data/lib/rails/generators/active_record/model/templates/model.rb +5 -2
  217. data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
  218. data/lib/rails/generators/active_record.rb +3 -11
  219. metadata +132 -53
  220. data/examples/associations.png +0 -0
  221. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -62
  222. data/lib/active_record/associations/join_helper.rb +0 -55
  223. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  224. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -135
  225. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -556
  226. data/lib/active_record/dynamic_finder_match.rb +0 -56
  227. data/lib/active_record/dynamic_scope_match.rb +0 -23
  228. data/lib/active_record/identity_map.rb +0 -163
  229. data/lib/active_record/named_scope.rb +0 -200
  230. data/lib/active_record/observer.rb +0 -121
  231. data/lib/active_record/session_store.rb +0 -358
  232. data/lib/active_record/test_case.rb +0 -69
  233. data/lib/rails/generators/active_record/model/templates/migration.rb +0 -17
  234. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  235. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  236. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  237. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -16
@@ -3,18 +3,18 @@ module ActiveRecord
3
3
  # Locking::Pessimistic provides support for row-level locking using
4
4
  # SELECT ... FOR UPDATE and other lock types.
5
5
  #
6
- # Pass <tt>:lock => true</tt> to <tt>ActiveRecord::Base.find</tt> to obtain an exclusive
6
+ # Chain <tt>ActiveRecord::Base#find</tt> to <tt>ActiveRecord::QueryMethods#lock</tt> to obtain an exclusive
7
7
  # lock on the selected rows:
8
8
  # # select * from accounts where id=1 for update
9
- # Account.find(1, :lock => true)
9
+ # Account.lock.find(1)
10
10
  #
11
- # Pass <tt>:lock => 'some locking clause'</tt> to give a database-specific locking clause
11
+ # Call <tt>lock('some locking clause')</tt> to use a database-specific locking clause
12
12
  # of your own such as 'LOCK IN SHARE MODE' or 'FOR UPDATE NOWAIT'. Example:
13
13
  #
14
14
  # Account.transaction do
15
15
  # # select * from accounts where name = 'shugo' limit 1 for update
16
16
  # shugo = Account.where("name = 'shugo'").lock(true).first
17
- # yuko = Account.where("name = 'shugo'").lock(true).first
17
+ # yuko = Account.where("name = 'yuko'").lock(true).first
18
18
  # shugo.balance -= 100
19
19
  # shugo.save!
20
20
  # yuko.balance += 100
@@ -26,7 +26,7 @@ module ActiveRecord
26
26
  #
27
27
  # Account.transaction do
28
28
  # # select * from accounts where ...
29
- # accounts = Account.where(...).all
29
+ # accounts = Account.where(...)
30
30
  # account1 = accounts.detect { |account| ... }
31
31
  # account2 = accounts.detect { |account| ... }
32
32
  # # select * from accounts where id=? for update
@@ -38,6 +38,18 @@ module ActiveRecord
38
38
  # account2.save!
39
39
  # end
40
40
  #
41
+ # You can start a transaction and acquire the lock in one go by calling
42
+ # <tt>with_lock</tt> with a block. The block is called from within
43
+ # a transaction, the object is already locked. Example:
44
+ #
45
+ # account = Account.first
46
+ # account.with_lock do
47
+ # # This block is called within a transaction,
48
+ # # account is already locked.
49
+ # account.balance -= 100
50
+ # account.save!
51
+ # end
52
+ #
41
53
  # Database-specific information on row locking:
42
54
  # MySQL: http://dev.mysql.com/doc/refman/5.1/en/innodb-locking-reads.html
43
55
  # PostgreSQL: http://www.postgresql.org/docs/current/interactive/sql-select.html#SQL-FOR-UPDATE-SHARE
@@ -50,6 +62,16 @@ module ActiveRecord
50
62
  reload(:lock => lock) if persisted?
51
63
  self
52
64
  end
65
+
66
+ # Wraps the passed block in a transaction, locking the object
67
+ # before yielding. You can pass the SQL locking clause
68
+ # as argument (see <tt>lock!</tt>).
69
+ def with_lock(lock = true)
70
+ transaction do
71
+ lock!(lock)
72
+ yield
73
+ end
74
+ end
53
75
  end
54
76
  end
55
77
  end
@@ -1,11 +1,13 @@
1
1
  module ActiveRecord
2
2
  class LogSubscriber < ActiveSupport::LogSubscriber
3
+ IGNORE_PAYLOAD_NAMES = ["SCHEMA", "EXPLAIN"]
4
+
3
5
  def self.runtime=(value)
4
- Thread.current["active_record_sql_runtime"] = value
6
+ ActiveRecord::RuntimeRegistry.sql_runtime = value
5
7
  end
6
8
 
7
9
  def self.runtime
8
- Thread.current["active_record_sql_runtime"] ||= 0
10
+ ActiveRecord::RuntimeRegistry.sql_runtime ||= 0
9
11
  end
10
12
 
11
13
  def self.reset_runtime
@@ -15,7 +17,21 @@ module ActiveRecord
15
17
 
16
18
  def initialize
17
19
  super
18
- @odd_or_even = false
20
+ @odd = false
21
+ end
22
+
23
+ def render_bind(column, value)
24
+ if column
25
+ if column.binary?
26
+ # This specifically deals with the PG adapter that casts bytea columns into a Hash.
27
+ value = value[:value] if value.is_a?(Hash)
28
+ value = value ? "<#{value.bytesize} bytes of binary data>" : "<NULL binary data>"
29
+ end
30
+
31
+ [column.name, value]
32
+ else
33
+ [nil, value]
34
+ end
19
35
  end
20
36
 
21
37
  def sql(event)
@@ -24,15 +40,15 @@ module ActiveRecord
24
40
 
25
41
  payload = event.payload
26
42
 
27
- return if 'SCHEMA' == payload[:name]
43
+ return if IGNORE_PAYLOAD_NAMES.include?(payload[:name])
28
44
 
29
- name = '%s (%.1fms)' % [payload[:name], event.duration]
30
- sql = payload[:sql].squeeze(' ')
31
- binds = nil
45
+ name = "#{payload[:name]} (#{event.duration.round(1)}ms)"
46
+ sql = payload[:sql]
47
+ binds = nil
32
48
 
33
49
  unless (payload[:binds] || []).empty?
34
50
  binds = " " + payload[:binds].map { |col,v|
35
- [col.name, v]
51
+ render_bind(col, v)
36
52
  }.inspect
37
53
  end
38
54
 
@@ -46,17 +62,8 @@ module ActiveRecord
46
62
  debug " #{name} #{sql}#{binds}"
47
63
  end
48
64
 
49
- def identity(event)
50
- return unless logger.debug?
51
-
52
- name = color(event.payload[:name], odd? ? CYAN : MAGENTA, true)
53
- line = odd? ? color(event.payload[:line], nil, true) : event.payload[:line]
54
-
55
- debug " #{name} #{line}"
56
- end
57
-
58
65
  def odd?
59
- @odd_or_even = !@odd_or_even
66
+ @odd = !@odd
60
67
  end
61
68
 
62
69
  def logger
@@ -1,73 +1,135 @@
1
1
  module ActiveRecord
2
2
  class Migration
3
- # ActiveRecord::Migration::CommandRecorder records commands done during
4
- # a migration and knows how to reverse those commands. The CommandRecorder
3
+ # <tt>ActiveRecord::Migration::CommandRecorder</tt> records commands done during
4
+ # a migration and knows how to reverse those commands. The CommandRecorder
5
5
  # knows how to invert the following commands:
6
6
  #
7
7
  # * add_column
8
8
  # * add_index
9
- # * add_timestamp
9
+ # * add_timestamps
10
10
  # * create_table
11
+ # * create_join_table
11
12
  # * remove_timestamps
12
13
  # * rename_column
13
14
  # * rename_index
14
15
  # * rename_table
15
16
  class CommandRecorder
16
- attr_accessor :commands, :delegate
17
+ include JoinTable
18
+
19
+ attr_accessor :commands, :delegate, :reverting
17
20
 
18
21
  def initialize(delegate = nil)
19
22
  @commands = []
20
23
  @delegate = delegate
24
+ @reverting = false
25
+ end
26
+
27
+ # While executing the given block, the recorded will be in reverting mode.
28
+ # All commands recorded will end up being recorded reverted
29
+ # and in reverse order.
30
+ # For example:
31
+ #
32
+ # recorder.revert{ recorder.record(:rename_table, [:old, :new]) }
33
+ # # same effect as recorder.record(:rename_table, [:new, :old])
34
+ def revert
35
+ @reverting = !@reverting
36
+ previous = @commands
37
+ @commands = []
38
+ yield
39
+ ensure
40
+ @commands = previous.concat(@commands.reverse)
41
+ @reverting = !@reverting
21
42
  end
22
43
 
23
- # record +command+. +command+ should be a method name and arguments.
44
+ # record +command+. +command+ should be a method name and arguments.
24
45
  # For example:
25
46
  #
26
- # recorder.record(:method_name, [:arg1, arg2])
27
- def record(*command)
28
- @commands << command
47
+ # recorder.record(:method_name, [:arg1, :arg2])
48
+ def record(*command, &block)
49
+ if @reverting
50
+ @commands << inverse_of(*command, &block)
51
+ else
52
+ @commands << (command << block)
53
+ end
29
54
  end
30
55
 
31
- # Returns a list that represents commands that are the inverse of the
32
- # commands stored in +commands+. For example:
56
+ # Returns the inverse of the given command. For example:
33
57
  #
34
- # recorder.record(:rename_table, [:old, :new])
35
- # recorder.inverse # => [:rename_table, [:new, :old]]
58
+ # recorder.inverse_of(:rename_table, [:old, :new])
59
+ # # => [:rename_table, [:new, :old]]
36
60
  #
37
- # This method will raise an IrreversibleMigration exception if it cannot
38
- # invert the +commands+.
39
- def inverse
40
- @commands.reverse.map { |name, args|
41
- method = :"invert_#{name}"
42
- raise IrreversibleMigration unless respond_to?(method, true)
43
- send(method, args)
44
- }
61
+ # This method will raise an +IrreversibleMigration+ exception if it cannot
62
+ # invert the +command+.
63
+ def inverse_of(command, args, &block)
64
+ method = :"invert_#{command}"
65
+ raise IrreversibleMigration unless respond_to?(method, true)
66
+ send(method, args, &block)
45
67
  end
46
68
 
47
69
  def respond_to?(*args) # :nodoc:
48
70
  super || delegate.respond_to?(*args)
49
71
  end
50
72
 
51
- [:create_table, :change_table, :rename_table, :add_column, :remove_column, :rename_index, :rename_column, :add_index, :remove_index, :add_timestamps, :remove_timestamps, :change_column, :change_column_default].each do |method|
73
+ [:create_table, :create_join_table, :rename_table, :add_column, :remove_column,
74
+ :rename_index, :rename_column, :add_index, :remove_index, :add_timestamps, :remove_timestamps,
75
+ :change_column_default, :add_reference, :remove_reference, :transaction,
76
+ :drop_join_table, :drop_table, :execute_block, :enable_extension,
77
+ :change_column, :execute, :remove_columns, :change_column_null,
78
+ :add_foreign_key, :remove_foreign_key
79
+ # irreversible methods need to be here too
80
+ ].each do |method|
52
81
  class_eval <<-EOV, __FILE__, __LINE__ + 1
53
- def #{method}(*args) # def create_table(*args)
54
- record(:"#{method}", args) # record(:create_table, args)
55
- end # end
82
+ def #{method}(*args, &block) # def create_table(*args, &block)
83
+ record(:"#{method}", args, &block) # record(:create_table, args, &block)
84
+ end # end
56
85
  EOV
57
86
  end
87
+ alias :add_belongs_to :add_reference
88
+ alias :remove_belongs_to :remove_reference
89
+
90
+ def change_table(table_name, options = {}) # :nodoc:
91
+ yield delegate.update_table_definition(table_name, self)
92
+ end
58
93
 
59
94
  private
60
95
 
61
- def invert_create_table(args)
62
- [:drop_table, args]
96
+ module StraightReversions
97
+ private
98
+ { transaction: :transaction,
99
+ execute_block: :execute_block,
100
+ create_table: :drop_table,
101
+ create_join_table: :drop_join_table,
102
+ add_column: :remove_column,
103
+ add_timestamps: :remove_timestamps,
104
+ add_reference: :remove_reference,
105
+ enable_extension: :disable_extension
106
+ }.each do |cmd, inv|
107
+ [[inv, cmd], [cmd, inv]].uniq.each do |method, inverse|
108
+ class_eval <<-EOV, __FILE__, __LINE__ + 1
109
+ def invert_#{method}(args, &block) # def invert_create_table(args, &block)
110
+ [:#{inverse}, args, block] # [:drop_table, args, block]
111
+ end # end
112
+ EOV
113
+ end
114
+ end
115
+ end
116
+
117
+ include StraightReversions
118
+
119
+ def invert_drop_table(args, &block)
120
+ if args.size == 1 && block == nil
121
+ raise ActiveRecord::IrreversibleMigration, "To avoid mistakes, drop_table is only reversible if given options or a block (can be empty)."
122
+ end
123
+ super
63
124
  end
64
125
 
65
126
  def invert_rename_table(args)
66
127
  [:rename_table, args.reverse]
67
128
  end
68
129
 
69
- def invert_add_column(args)
70
- [:remove_column, args.first(2)]
130
+ def invert_remove_column(args)
131
+ raise ActiveRecord::IrreversibleMigration, "remove_column is only reversible if given a type." if args.size <= 2
132
+ super
71
133
  end
72
134
 
73
135
  def invert_rename_index(args)
@@ -80,26 +142,56 @@ module ActiveRecord
80
142
 
81
143
  def invert_add_index(args)
82
144
  table, columns, options = *args
83
- index_name = options.try(:[], :name)
84
- options_hash = index_name ? {:name => index_name} : {:column => columns}
145
+ options ||= {}
146
+
147
+ index_name = options[:name]
148
+ options_hash = index_name ? { name: index_name } : { column: columns }
149
+
85
150
  [:remove_index, [table, options_hash]]
86
151
  end
87
152
 
88
- def invert_remove_timestamps(args)
89
- [:add_timestamps, args]
153
+ def invert_remove_index(args)
154
+ table, options = *args
155
+
156
+ unless options && options.is_a?(Hash) && options[:column]
157
+ raise ActiveRecord::IrreversibleMigration, "remove_index is only reversible if given a :column option."
158
+ end
159
+
160
+ options = options.dup
161
+ [:add_index, [table, options.delete(:column), options]]
162
+ end
163
+
164
+ alias :invert_add_belongs_to :invert_add_reference
165
+ alias :invert_remove_belongs_to :invert_remove_reference
166
+
167
+ def invert_change_column_null(args)
168
+ args[2] = !args[2]
169
+ [:change_column_null, args]
90
170
  end
91
171
 
92
- def invert_add_timestamps(args)
93
- [:remove_timestamps, args]
172
+ def invert_add_foreign_key(args)
173
+ from_table, to_table, add_options = args
174
+ add_options ||= {}
175
+
176
+ if add_options[:name]
177
+ options = { name: add_options[:name] }
178
+ elsif add_options[:column]
179
+ options = { column: add_options[:column] }
180
+ else
181
+ options = to_table
182
+ end
183
+
184
+ [:remove_foreign_key, [from_table, options]]
94
185
  end
95
186
 
96
187
  # Forwards any missing method call to the \target.
97
188
  def method_missing(method, *args, &block)
98
- @delegate.send(method, *args, &block)
99
- rescue NoMethodError => e
100
- raise e, e.message.sub(/ for #<.*$/, " via proxy for #{@delegate}")
189
+ if @delegate.respond_to?(method)
190
+ @delegate.send(method, *args, &block)
191
+ else
192
+ super
193
+ end
101
194
  end
102
-
103
195
  end
104
196
  end
105
197
  end
@@ -0,0 +1,15 @@
1
+ module ActiveRecord
2
+ class Migration
3
+ module JoinTable #:nodoc:
4
+ private
5
+
6
+ def find_join_table_name(table_1, table_2, options = {})
7
+ options.delete(:table_name) || join_table_name(table_1, table_2)
8
+ end
9
+
10
+ def join_table_name(table_1, table_2)
11
+ ModelSchema.derive_join_table_name(table_1, table_2).to_sym
12
+ end
13
+ end
14
+ end
15
+ end