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
@@ -1,4 +1,3 @@
1
- require 'active_support/core_ext/object/blank'
2
1
  require 'date'
3
2
  require 'set'
4
3
  require 'bigdecimal'
@@ -6,44 +5,74 @@ require 'bigdecimal/util'
6
5
 
7
6
  module ActiveRecord
8
7
  module ConnectionAdapters #:nodoc:
9
- class IndexDefinition < Struct.new(:table, :name, :unique, :columns, :lengths) #:nodoc:
8
+ # Abstract representation of an index definition on a table. Instances of
9
+ # this type are typically created and returned by methods in database
10
+ # adapters. e.g. ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter#indexes
11
+ class IndexDefinition < Struct.new(:table, :name, :unique, :columns, :lengths, :orders, :where, :type, :using) #:nodoc:
10
12
  end
11
13
 
12
14
  # Abstract representation of a column definition. Instances of this type
13
15
  # are typically created by methods in TableDefinition, and added to the
14
16
  # +columns+ attribute of said TableDefinition object, in order to be used
15
17
  # for generating a number of table creation or table changing SQL statements.
16
- class ColumnDefinition < Struct.new(:base, :name, :type, :limit, :precision, :scale, :default, :null) #:nodoc:
18
+ class ColumnDefinition < Struct.new(:name, :type, :limit, :precision, :scale, :default, :null, :first, :after, :primary_key, :sql_type, :cast_type) #:nodoc:
17
19
 
18
- def string_to_binary(value)
19
- value
20
+ def primary_key?
21
+ primary_key || type.to_sym == :primary_key
22
+ end
23
+ end
24
+
25
+ class ChangeColumnDefinition < Struct.new(:column, :type, :options) #:nodoc:
26
+ end
27
+
28
+ class ForeignKeyDefinition < Struct.new(:from_table, :to_table, :options) #:nodoc:
29
+ def name
30
+ options[:name]
31
+ end
32
+
33
+ def column
34
+ options[:column]
35
+ end
36
+
37
+ def primary_key
38
+ options[:primary_key] || default_primary_key
39
+ end
40
+
41
+ def on_delete
42
+ options[:on_delete]
20
43
  end
21
44
 
22
- def sql_type
23
- base.type_to_sql(type.to_sym, limit, precision, scale) rescue type
45
+ def on_update
46
+ options[:on_update]
24
47
  end
25
48
 
26
- def to_sql
27
- column_sql = "#{base.quote_column_name(name)} #{sql_type}"
28
- column_options = {}
29
- column_options[:null] = null unless null.nil?
30
- column_options[:default] = default unless default.nil?
31
- add_column_options!(column_sql, column_options) unless type.to_sym == :primary_key
32
- column_sql
49
+ def custom_primary_key?
50
+ options[:primary_key] != default_primary_key
33
51
  end
34
52
 
35
53
  private
54
+ def default_primary_key
55
+ "id"
56
+ end
57
+ end
36
58
 
37
- def add_column_options!(sql, options)
38
- base.add_column_options!(sql, options.merge(:column => self))
39
- end
59
+ module TimestampDefaultDeprecation # :nodoc:
60
+ def emit_warning_if_null_unspecified(sym, options)
61
+ return if options.key?(:null)
62
+
63
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
64
+ `##{sym}` was called without specifying an option for `null`. In Rails 5,
65
+ this behavior will change to `null: false`. You should manually specify
66
+ `null: true` to prevent the behavior of your existing migrations from changing.
67
+ MSG
68
+ end
40
69
  end
41
70
 
42
71
  # Represents the schema of an SQL table in an abstract way. This class
43
72
  # provides methods for manipulating the schema representation.
44
73
  #
45
- # Inside migration files, the +t+ object in +create_table+ and
46
- # +change_table+ is actually of this type:
74
+ # Inside migration files, the +t+ object in +create_table+
75
+ # is actually of this type:
47
76
  #
48
77
  # class SomeMigration < ActiveRecord::Migration
49
78
  # def up
@@ -60,33 +89,35 @@ module ActiveRecord
60
89
  # The table definitions
61
90
  # The Columns are stored as a ColumnDefinition in the +columns+ attribute.
62
91
  class TableDefinition
92
+ include TimestampDefaultDeprecation
93
+
63
94
  # An array of ColumnDefinition objects, representing the column changes
64
95
  # that have been defined.
65
- attr_accessor :columns
66
-
67
- def initialize(base)
68
- @columns = []
69
- @base = base
96
+ attr_accessor :indexes
97
+ attr_reader :name, :temporary, :options, :as, :foreign_keys
98
+
99
+ def initialize(types, name, temporary, options, as = nil)
100
+ @columns_hash = {}
101
+ @indexes = {}
102
+ @foreign_keys = []
103
+ @native = types
104
+ @temporary = temporary
105
+ @options = options
106
+ @as = as
107
+ @name = name
70
108
  end
71
109
 
72
- def xml(*args)
73
- raise NotImplementedError unless %w{
74
- sqlite mysql mysql2
75
- }.include? @base.adapter_name.downcase
76
-
77
- options = args.extract_options!
78
- column(args[0], :text, options)
79
- end
110
+ def columns; @columns_hash.values; end
80
111
 
81
112
  # Appends a primary key definition to the table definition.
82
113
  # Can be called multiple times, but this is probably not a good idea.
83
- def primary_key(name)
84
- column(name, :primary_key)
114
+ def primary_key(name, type = :primary_key, options = {})
115
+ column(name, type, options.merge(:primary_key => true))
85
116
  end
86
117
 
87
118
  # Returns a ColumnDefinition for the column with name +name+.
88
119
  def [](name)
89
- @columns.find {|column| column.name.to_s == name.to_s}
120
+ @columns_hash[name.to_s]
90
121
  end
91
122
 
92
123
  # Instantiates a new column for the table.
@@ -94,8 +125,8 @@ module ActiveRecord
94
125
  # which is one of the following:
95
126
  # <tt>:primary_key</tt>, <tt>:string</tt>, <tt>:text</tt>,
96
127
  # <tt>:integer</tt>, <tt>:float</tt>, <tt>:decimal</tt>,
97
- # <tt>:datetime</tt>, <tt>:timestamp</tt>, <tt>:time</tt>,
98
- # <tt>:date</tt>, <tt>:binary</tt>, <tt>:boolean</tt>.
128
+ # <tt>:datetime</tt>, <tt>:time</tt>, <tt>:date</tt>,
129
+ # <tt>:binary</tt>, <tt>:boolean</tt>.
99
130
  #
100
131
  # You may use a type not in this list as long as it is supported by your
101
132
  # database (for example, "polygon" in MySQL), but this will not be database
@@ -114,9 +145,11 @@ module ActiveRecord
114
145
  # Specifies the precision for a <tt>:decimal</tt> column.
115
146
  # * <tt>:scale</tt> -
116
147
  # Specifies the scale for a <tt>:decimal</tt> column.
148
+ # * <tt>:index</tt> -
149
+ # Create an index for the column. Can be either <tt>true</tt> or an options hash.
117
150
  #
118
- # For clarity's sake: the precision is the number of significant digits,
119
- # while the scale is the number of digits that can be stored following
151
+ # Note: The precision is the total number of significant digits
152
+ # and the scale is the number of digits that can be stored following
120
153
  # the decimal point. For example, the number 123.45 has a precision of 5
121
154
  # and a scale of 2. A decimal with a precision of 5 and a scale of 2 can
122
155
  # range from -999.99 to 999.99.
@@ -138,17 +171,8 @@ module ActiveRecord
138
171
  # Default is (38,0).
139
172
  # * DB2: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..62].
140
173
  # Default unknown.
141
- # * Firebird: <tt>:precision</tt> [1..18], <tt>:scale</tt> [0..18].
142
- # Default (9,0). Internal types NUMERIC and DECIMAL have different
143
- # storage rules, decimal being better.
144
- # * FrontBase?: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
145
- # Default (38,0). WARNING Max <tt>:precision</tt>/<tt>:scale</tt> for
146
- # NUMERIC is 19, and DECIMAL is 38.
147
174
  # * SqlServer?: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
148
175
  # Default (38,0).
149
- # * Sybase: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
150
- # Default (38,0).
151
- # * OpenBase?: Documentation unclear. Claims storage in <tt>double</tt>.
152
176
  #
153
177
  # This method returns <tt>self</tt>.
154
178
  #
@@ -157,21 +181,21 @@ module ActiveRecord
157
181
  # td.column(:granted, :boolean)
158
182
  # # granted BOOLEAN
159
183
  #
160
- # td.column(:picture, :binary, :limit => 2.megabytes)
184
+ # td.column(:picture, :binary, limit: 2.megabytes)
161
185
  # # => picture BLOB(2097152)
162
186
  #
163
- # td.column(:sales_stage, :string, :limit => 20, :default => 'new', :null => false)
187
+ # td.column(:sales_stage, :string, limit: 20, default: 'new', null: false)
164
188
  # # => sales_stage VARCHAR(20) DEFAULT 'new' NOT NULL
165
189
  #
166
- # td.column(:bill_gates_money, :decimal, :precision => 15, :scale => 2)
190
+ # td.column(:bill_gates_money, :decimal, precision: 15, scale: 2)
167
191
  # # => bill_gates_money DECIMAL(15,2)
168
192
  #
169
- # td.column(:sensor_reading, :decimal, :precision => 30, :scale => 20)
193
+ # td.column(:sensor_reading, :decimal, precision: 30, scale: 20)
170
194
  # # => sensor_reading DECIMAL(30,20)
171
195
  #
172
196
  # # While <tt>:scale</tt> defaults to zero on most databases, it
173
197
  # # probably wouldn't hurt to include it.
174
- # td.column(:huge_integer, :decimal, :precision => 30)
198
+ # td.column(:huge_integer, :decimal, precision: 30)
175
199
  # # => huge_integer DECIMAL(30)
176
200
  #
177
201
  # # Defines a column with a database-specific type.
@@ -186,21 +210,24 @@ module ActiveRecord
186
210
  #
187
211
  # What can be written like this with the regular calls to column:
188
212
  #
189
- # create_table "products", :force => true do |t|
190
- # t.column "shop_id", :integer
191
- # t.column "creator_id", :integer
192
- # t.column "name", :string, :default => "Untitled"
193
- # t.column "value", :string, :default => "Untitled"
194
- # t.column "created_at", :datetime
195
- # t.column "updated_at", :datetime
213
+ # create_table :products do |t|
214
+ # t.column :shop_id, :integer
215
+ # t.column :creator_id, :integer
216
+ # t.column :item_number, :string
217
+ # t.column :name, :string, default: "Untitled"
218
+ # t.column :value, :string, default: "Untitled"
219
+ # t.column :created_at, :datetime
220
+ # t.column :updated_at, :datetime
196
221
  # end
222
+ # add_index :products, :item_number
197
223
  #
198
- # Can also be written as follows using the short-hand:
224
+ # can also be written as follows using the short-hand:
199
225
  #
200
226
  # create_table :products do |t|
201
227
  # t.integer :shop_id, :creator_id
202
- # t.string :name, :value, :default => "Untitled"
203
- # t.timestamps
228
+ # t.string :item_number, index: true
229
+ # t.string :name, :value, default: "Untitled"
230
+ # t.timestamps null: false
204
231
  # end
205
232
  #
206
233
  # There's a short-hand method for each of the type values declared at the top. And then there's
@@ -208,76 +235,162 @@ module ActiveRecord
208
235
  #
209
236
  # TableDefinition#references will add an appropriately-named _id column, plus a corresponding _type
210
237
  # column if the <tt>:polymorphic</tt> option is supplied. If <tt>:polymorphic</tt> is a hash of
211
- # options, these will be used when creating the <tt>_type</tt> column. So what can be written like this:
238
+ # options, these will be used when creating the <tt>_type</tt> column. The <tt>:index</tt> option
239
+ # will also create an index, similar to calling <tt>add_index</tt>. So what can be written like this:
212
240
  #
213
241
  # create_table :taggings do |t|
214
242
  # t.integer :tag_id, :tagger_id, :taggable_id
215
243
  # t.string :tagger_type
216
- # t.string :taggable_type, :default => 'Photo'
244
+ # t.string :taggable_type, default: 'Photo'
217
245
  # end
246
+ # add_index :taggings, :tag_id, name: 'index_taggings_on_tag_id'
247
+ # add_index :taggings, [:tagger_id, :tagger_type]
218
248
  #
219
249
  # Can also be written as follows using references:
220
250
  #
221
251
  # create_table :taggings do |t|
222
- # t.references :tag
223
- # t.references :tagger, :polymorphic => true
224
- # t.references :taggable, :polymorphic => { :default => 'Photo' }
252
+ # t.references :tag, index: { name: 'index_taggings_on_tag_id' }
253
+ # t.references :tagger, polymorphic: true, index: true
254
+ # t.references :taggable, polymorphic: { default: 'Photo' }
225
255
  # end
226
256
  def column(name, type, options = {})
227
- column = self[name] || ColumnDefinition.new(@base, name, type)
228
- if options[:limit]
229
- column.limit = options[:limit]
230
- elsif native[type.to_sym].is_a?(Hash)
231
- column.limit = native[type.to_sym][:limit]
257
+ name = name.to_s
258
+ type = type.to_sym
259
+ options = options.dup
260
+
261
+ if @columns_hash[name] && @columns_hash[name].primary_key?
262
+ raise ArgumentError, "you can't redefine the primary key column '#{name}'. To define a custom primary key, pass { id: false } to create_table."
232
263
  end
233
- column.precision = options[:precision]
234
- column.scale = options[:scale]
235
- column.default = options[:default]
236
- column.null = options[:null]
237
- @columns << column unless @columns.include? column
264
+
265
+ index_options = options.delete(:index)
266
+ index(name, index_options.is_a?(Hash) ? index_options : {}) if index_options
267
+ @columns_hash[name] = new_column_definition(name, type, options)
238
268
  self
239
269
  end
240
270
 
241
- %w( string text integer float decimal datetime timestamp time date binary boolean ).each do |column_type|
242
- class_eval <<-EOV, __FILE__, __LINE__ + 1
243
- def #{column_type}(*args) # def string(*args)
244
- options = args.extract_options! # options = args.extract_options!
245
- column_names = args # column_names = args
246
- #
247
- column_names.each { |name| column(name, '#{column_type}', options) } # column_names.each { |name| column(name, 'string', options) }
248
- end # end
249
- EOV
271
+ def remove_column(name)
272
+ @columns_hash.delete name.to_s
273
+ end
274
+
275
+ [:string, :text, :integer, :bigint, :float, :decimal, :datetime, :timestamp, :time, :date, :binary, :boolean].each do |column_type|
276
+ define_method column_type do |*args|
277
+ options = args.extract_options!
278
+ column_names = args
279
+ column_names.each { |name| column(name, column_type, options) }
280
+ end
281
+ end
282
+
283
+ # Adds index options to the indexes hash, keyed by column name
284
+ # This is primarily used to track indexes that need to be created after the table
285
+ #
286
+ # index(:account_id, name: 'index_projects_on_account_id')
287
+ def index(column_name, options = {})
288
+ indexes[column_name] = options
289
+ end
290
+
291
+ def foreign_key(table_name, options = {}) # :nodoc:
292
+ foreign_keys.push([table_name, options])
250
293
  end
251
294
 
252
295
  # Appends <tt>:datetime</tt> columns <tt>:created_at</tt> and
253
- # <tt>:updated_at</tt> to the table.
296
+ # <tt>:updated_at</tt> to the table. See SchemaStatements#add_timestamps
297
+ #
298
+ # t.timestamps null: false
254
299
  def timestamps(*args)
255
300
  options = args.extract_options!
301
+ emit_warning_if_null_unspecified(:timestamps, options)
256
302
  column(:created_at, :datetime, options)
257
303
  column(:updated_at, :datetime, options)
258
304
  end
259
305
 
306
+ # Adds a reference.
307
+ #
308
+ # t.references(:user)
309
+ # t.belongs_to(:supplier, foreign_key: true)
310
+ #
311
+ # See SchemaStatements#add_reference for details of the options you can use.
260
312
  def references(*args)
261
313
  options = args.extract_options!
262
314
  polymorphic = options.delete(:polymorphic)
315
+ index_options = options.delete(:index)
316
+ foreign_key_options = options.delete(:foreign_key)
317
+ type = options.delete(:type) || :integer
318
+
319
+ if polymorphic && foreign_key_options
320
+ raise ArgumentError, "Cannot add a foreign key on a polymorphic relation"
321
+ end
322
+
263
323
  args.each do |col|
264
- column("#{col}_id", :integer, options)
265
- column("#{col}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) unless polymorphic.nil?
324
+ column("#{col}_id", type, options)
325
+ column("#{col}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) if polymorphic
326
+ index(polymorphic ? %w(type id).map { |t| "#{col}_#{t}" } : "#{col}_id", index_options.is_a?(Hash) ? index_options : {}) if index_options
327
+ if foreign_key_options
328
+ to_table = Base.pluralize_table_names ? col.to_s.pluralize : col.to_s
329
+ foreign_key(to_table, foreign_key_options.is_a?(Hash) ? foreign_key_options : {})
330
+ end
266
331
  end
267
332
  end
268
333
  alias :belongs_to :references
269
334
 
270
- # Returns a String whose contents are the column definitions
271
- # concatenated together. This string can then be prepended and appended to
272
- # to generate the final SQL to create the table.
273
- def to_sql
274
- @columns.map { |c| c.to_sql } * ', '
335
+ def new_column_definition(name, type, options) # :nodoc:
336
+ type = aliased_types(type.to_s, type)
337
+ column = create_column_definition name, type
338
+ limit = options.fetch(:limit) do
339
+ native[type][:limit] if native[type].is_a?(Hash)
340
+ end
341
+
342
+ column.limit = limit
343
+ column.precision = options[:precision]
344
+ column.scale = options[:scale]
345
+ column.default = options[:default]
346
+ column.null = options[:null]
347
+ column.first = options[:first]
348
+ column.after = options[:after]
349
+ column.primary_key = type == :primary_key || options[:primary_key]
350
+ column
275
351
  end
276
352
 
277
353
  private
278
- def native
279
- @base.native_database_types
280
- end
354
+ def create_column_definition(name, type)
355
+ ColumnDefinition.new name, type
356
+ end
357
+
358
+ def native
359
+ @native
360
+ end
361
+
362
+ def aliased_types(name, fallback)
363
+ 'timestamp' == name ? :datetime : fallback
364
+ end
365
+ end
366
+
367
+ class AlterTable # :nodoc:
368
+ attr_reader :adds
369
+ attr_reader :foreign_key_adds
370
+ attr_reader :foreign_key_drops
371
+
372
+ def initialize(td)
373
+ @td = td
374
+ @adds = []
375
+ @foreign_key_adds = []
376
+ @foreign_key_drops = []
377
+ end
378
+
379
+ def name; @td.name; end
380
+
381
+ def add_foreign_key(to_table, options)
382
+ @foreign_key_adds << ForeignKeyDefinition.new(name, to_table, options)
383
+ end
384
+
385
+ def drop_foreign_key(name)
386
+ @foreign_key_drops << name
387
+ end
388
+
389
+ def add_column(name, type, options)
390
+ name = name.to_s
391
+ type = type.to_sym
392
+ @adds << @td.new_column_definition(name, type, options)
393
+ end
281
394
  end
282
395
 
283
396
  # Represents an SQL table in an abstract way for updating a table.
@@ -288,6 +401,7 @@ module ActiveRecord
288
401
  # change_table :table do |t|
289
402
  # t.column
290
403
  # t.index
404
+ # t.rename_index
291
405
  # t.timestamps
292
406
  # t.change
293
407
  # t.change_default
@@ -313,162 +427,152 @@ module ActiveRecord
313
427
  # end
314
428
  #
315
429
  class Table
430
+ attr_reader :name
431
+
316
432
  def initialize(table_name, base)
317
- @table_name = table_name
433
+ @name = table_name
318
434
  @base = base
319
435
  end
320
436
 
321
437
  # Adds a new column to the named table.
322
438
  # See TableDefinition#column for details of the options you can use.
323
- # ===== Example
439
+ #
324
440
  # ====== Creating a simple column
325
441
  # t.column(:name, :string)
326
442
  def column(column_name, type, options = {})
327
- @base.add_column(@table_name, column_name, type, options)
443
+ @base.add_column(name, column_name, type, options)
328
444
  end
329
445
 
330
446
  # Checks to see if a column exists. See SchemaStatements#column_exists?
331
447
  def column_exists?(column_name, type = nil, options = {})
332
- @base.column_exists?(@table_name, column_name, type, options)
448
+ @base.column_exists?(name, column_name, type, options)
333
449
  end
334
450
 
335
451
  # Adds a new index to the table. +column_name+ can be a single Symbol, or
336
452
  # an Array of Symbols. See SchemaStatements#add_index
337
453
  #
338
- # ===== Examples
339
454
  # ====== Creating a simple index
340
455
  # t.index(:name)
341
456
  # ====== Creating a unique index
342
- # t.index([:branch_id, :party_id], :unique => true)
457
+ # t.index([:branch_id, :party_id], unique: true)
343
458
  # ====== Creating a named index
344
- # t.index([:branch_id, :party_id], :unique => true, :name => 'by_branch_party')
459
+ # t.index([:branch_id, :party_id], unique: true, name: 'by_branch_party')
345
460
  def index(column_name, options = {})
346
- @base.add_index(@table_name, column_name, options)
461
+ @base.add_index(name, column_name, options)
347
462
  end
348
463
 
349
464
  # Checks to see if an index exists. See SchemaStatements#index_exists?
350
465
  def index_exists?(column_name, options = {})
351
- @base.index_exists?(@table_name, column_name, options)
466
+ @base.index_exists?(name, column_name, options)
467
+ end
468
+
469
+ # Renames the given index on the table.
470
+ #
471
+ # t.rename_index(:user_id, :account_id)
472
+ def rename_index(index_name, new_index_name)
473
+ @base.rename_index(name, index_name, new_index_name)
352
474
  end
353
475
 
354
476
  # Adds timestamps (+created_at+ and +updated_at+) columns to the table. See SchemaStatements#add_timestamps
355
- # ===== Example
356
- # t.timestamps
357
- def timestamps
358
- @base.add_timestamps(@table_name)
477
+ #
478
+ # t.timestamps null: false
479
+ def timestamps(options = {})
480
+ @base.add_timestamps(name, options)
359
481
  end
360
482
 
361
483
  # Changes the column's definition according to the new options.
362
484
  # See TableDefinition#column for details of the options you can use.
363
- # ===== Examples
364
- # t.change(:name, :string, :limit => 80)
485
+ #
486
+ # t.change(:name, :string, limit: 80)
365
487
  # t.change(:description, :text)
366
488
  def change(column_name, type, options = {})
367
- @base.change_column(@table_name, column_name, type, options)
489
+ @base.change_column(name, column_name, type, options)
368
490
  end
369
491
 
370
492
  # Sets a new default value for a column. See SchemaStatements#change_column_default
371
- # ===== Examples
493
+ #
372
494
  # t.change_default(:qualification, 'new')
373
495
  # t.change_default(:authorized, 1)
374
496
  def change_default(column_name, default)
375
- @base.change_column_default(@table_name, column_name, default)
497
+ @base.change_column_default(name, column_name, default)
376
498
  end
377
499
 
378
500
  # Removes the column(s) from the table definition.
379
- # ===== Examples
501
+ #
380
502
  # t.remove(:qualification)
381
503
  # t.remove(:qualification, :experience)
382
504
  def remove(*column_names)
383
- @base.remove_column(@table_name, column_names)
505
+ @base.remove_columns(name, *column_names)
384
506
  end
385
507
 
386
508
  # Removes the given index from the table.
387
509
  #
388
- # ===== Examples
389
510
  # ====== Remove the index_table_name_on_column in the table_name table
390
511
  # t.remove_index :column
391
512
  # ====== Remove the index named index_table_name_on_branch_id in the table_name table
392
- # t.remove_index :column => :branch_id
513
+ # t.remove_index column: :branch_id
393
514
  # ====== Remove the index named index_table_name_on_branch_id_and_party_id in the table_name table
394
- # t.remove_index :column => [:branch_id, :party_id]
515
+ # t.remove_index column: [:branch_id, :party_id]
395
516
  # ====== Remove the index named by_branch_party in the table_name table
396
- # t.remove_index :name => :by_branch_party
517
+ # t.remove_index name: :by_branch_party
397
518
  def remove_index(options = {})
398
- @base.remove_index(@table_name, options)
519
+ @base.remove_index(name, options)
399
520
  end
400
521
 
401
522
  # Removes the timestamp columns (+created_at+ and +updated_at+) from the table.
402
- # ===== Example
523
+ #
403
524
  # t.remove_timestamps
404
- def remove_timestamps
405
- @base.remove_timestamps(@table_name)
525
+ def remove_timestamps(options = {})
526
+ @base.remove_timestamps(name, options)
406
527
  end
407
528
 
408
529
  # Renames a column.
409
- # ===== Example
530
+ #
410
531
  # t.rename(:description, :name)
411
532
  def rename(column_name, new_column_name)
412
- @base.rename_column(@table_name, column_name, new_column_name)
533
+ @base.rename_column(name, column_name, new_column_name)
413
534
  end
414
535
 
415
- # Adds a reference. Optionally adds a +type+ column, if <tt>:polymorphic</tt> option is provided.
416
- # <tt>references</tt> and <tt>belongs_to</tt> are acceptable.
417
- # ===== Examples
418
- # t.references(:goat)
419
- # t.references(:goat, :polymorphic => true)
420
- # t.belongs_to(:goat)
536
+ # Adds a reference.
537
+ #
538
+ # t.references(:user)
539
+ # t.belongs_to(:supplier, foreign_key: true)
540
+ #
541
+ # See SchemaStatements#add_reference for details of the options you can use.
421
542
  def references(*args)
422
543
  options = args.extract_options!
423
- polymorphic = options.delete(:polymorphic)
424
- args.each do |col|
425
- @base.add_column(@table_name, "#{col}_id", :integer, options)
426
- @base.add_column(@table_name, "#{col}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) unless polymorphic.nil?
544
+ args.each do |ref_name|
545
+ @base.add_reference(name, ref_name, options)
427
546
  end
428
547
  end
429
548
  alias :belongs_to :references
430
549
 
431
550
  # Removes a reference. Optionally removes a +type+ column.
432
551
  # <tt>remove_references</tt> and <tt>remove_belongs_to</tt> are acceptable.
433
- # ===== Examples
434
- # t.remove_references(:goat)
435
- # t.remove_references(:goat, :polymorphic => true)
436
- # t.remove_belongs_to(:goat)
552
+ #
553
+ # t.remove_references(:user)
554
+ # t.remove_belongs_to(:supplier, polymorphic: true)
555
+ #
556
+ # See SchemaStatements#remove_reference
437
557
  def remove_references(*args)
438
558
  options = args.extract_options!
439
- polymorphic = options.delete(:polymorphic)
440
- args.each do |col|
441
- @base.remove_column(@table_name, "#{col}_id")
442
- @base.remove_column(@table_name, "#{col}_type") unless polymorphic.nil?
559
+ args.each do |ref_name|
560
+ @base.remove_reference(name, ref_name, options)
443
561
  end
444
562
  end
445
- alias :remove_belongs_to :remove_references
563
+ alias :remove_belongs_to :remove_references
446
564
 
447
565
  # Adds a column or columns of a specified type
448
- # ===== Examples
566
+ #
449
567
  # t.string(:goat)
450
568
  # t.string(:goat, :sheep)
451
- %w( string text integer float decimal datetime timestamp time date binary boolean ).each do |column_type|
452
- class_eval <<-EOV, __FILE__, __LINE__ + 1
453
- def #{column_type}(*args) # def string(*args)
454
- options = args.extract_options! # options = args.extract_options!
455
- column_names = args # column_names = args
456
- #
457
- column_names.each do |name| # column_names.each do |name|
458
- column = ColumnDefinition.new(@base, name, '#{column_type}') # column = ColumnDefinition.new(@base, name, 'string')
459
- if options[:limit] # if options[:limit]
460
- column.limit = options[:limit] # column.limit = options[:limit]
461
- elsif native['#{column_type}'.to_sym].is_a?(Hash) # elsif native['string'.to_sym].is_a?(Hash)
462
- column.limit = native['#{column_type}'.to_sym][:limit] # column.limit = native['string'.to_sym][:limit]
463
- end # end
464
- column.precision = options[:precision] # column.precision = options[:precision]
465
- column.scale = options[:scale] # column.scale = options[:scale]
466
- column.default = options[:default] # column.default = options[:default]
467
- column.null = options[:null] # column.null = options[:null]
468
- @base.add_column(@table_name, name, column.sql_type, options) # @base.add_column(@table_name, name, column.sql_type, options)
469
- end # end
470
- end # end
471
- EOV
569
+ [:string, :text, :integer, :float, :decimal, :datetime, :timestamp, :time, :date, :binary, :boolean].each do |column_type|
570
+ define_method column_type do |*args|
571
+ options = args.extract_options!
572
+ args.each do |column_name|
573
+ @base.add_column(name, column_name, column_type, options)
574
+ end
575
+ end
472
576
  end
473
577
 
474
578
  private
@@ -476,6 +580,5 @@ module ActiveRecord
476
580
  @base.native_database_types
477
581
  end
478
582
  end
479
-
480
583
  end
481
584
  end