activerecord 5.2.3

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 (244) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +937 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +217 -0
  5. data/examples/performance.rb +185 -0
  6. data/examples/simple.rb +15 -0
  7. data/lib/active_record.rb +188 -0
  8. data/lib/active_record/aggregations.rb +283 -0
  9. data/lib/active_record/association_relation.rb +40 -0
  10. data/lib/active_record/associations.rb +1860 -0
  11. data/lib/active_record/associations/alias_tracker.rb +81 -0
  12. data/lib/active_record/associations/association.rb +299 -0
  13. data/lib/active_record/associations/association_scope.rb +168 -0
  14. data/lib/active_record/associations/belongs_to_association.rb +130 -0
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +40 -0
  16. data/lib/active_record/associations/builder/association.rb +140 -0
  17. data/lib/active_record/associations/builder/belongs_to.rb +163 -0
  18. data/lib/active_record/associations/builder/collection_association.rb +82 -0
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +135 -0
  20. data/lib/active_record/associations/builder/has_many.rb +17 -0
  21. data/lib/active_record/associations/builder/has_one.rb +30 -0
  22. data/lib/active_record/associations/builder/singular_association.rb +42 -0
  23. data/lib/active_record/associations/collection_association.rb +513 -0
  24. data/lib/active_record/associations/collection_proxy.rb +1131 -0
  25. data/lib/active_record/associations/foreign_association.rb +13 -0
  26. data/lib/active_record/associations/has_many_association.rb +144 -0
  27. data/lib/active_record/associations/has_many_through_association.rb +227 -0
  28. data/lib/active_record/associations/has_one_association.rb +120 -0
  29. data/lib/active_record/associations/has_one_through_association.rb +45 -0
  30. data/lib/active_record/associations/join_dependency.rb +262 -0
  31. data/lib/active_record/associations/join_dependency/join_association.rb +60 -0
  32. data/lib/active_record/associations/join_dependency/join_base.rb +23 -0
  33. data/lib/active_record/associations/join_dependency/join_part.rb +71 -0
  34. data/lib/active_record/associations/preloader.rb +193 -0
  35. data/lib/active_record/associations/preloader/association.rb +131 -0
  36. data/lib/active_record/associations/preloader/through_association.rb +107 -0
  37. data/lib/active_record/associations/singular_association.rb +73 -0
  38. data/lib/active_record/associations/through_association.rb +121 -0
  39. data/lib/active_record/attribute_assignment.rb +88 -0
  40. data/lib/active_record/attribute_decorators.rb +90 -0
  41. data/lib/active_record/attribute_methods.rb +492 -0
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +78 -0
  43. data/lib/active_record/attribute_methods/dirty.rb +150 -0
  44. data/lib/active_record/attribute_methods/primary_key.rb +143 -0
  45. data/lib/active_record/attribute_methods/query.rb +42 -0
  46. data/lib/active_record/attribute_methods/read.rb +85 -0
  47. data/lib/active_record/attribute_methods/serialization.rb +90 -0
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +91 -0
  49. data/lib/active_record/attribute_methods/write.rb +68 -0
  50. data/lib/active_record/attributes.rb +266 -0
  51. data/lib/active_record/autosave_association.rb +498 -0
  52. data/lib/active_record/base.rb +329 -0
  53. data/lib/active_record/callbacks.rb +353 -0
  54. data/lib/active_record/coders/json.rb +15 -0
  55. data/lib/active_record/coders/yaml_column.rb +50 -0
  56. data/lib/active_record/collection_cache_key.rb +53 -0
  57. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +1068 -0
  58. data/lib/active_record/connection_adapters/abstract/database_limits.rb +72 -0
  59. data/lib/active_record/connection_adapters/abstract/database_statements.rb +540 -0
  60. data/lib/active_record/connection_adapters/abstract/query_cache.rb +145 -0
  61. data/lib/active_record/connection_adapters/abstract/quoting.rb +200 -0
  62. data/lib/active_record/connection_adapters/abstract/savepoints.rb +23 -0
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +146 -0
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +685 -0
  65. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +95 -0
  66. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1396 -0
  67. data/lib/active_record/connection_adapters/abstract/transaction.rb +283 -0
  68. data/lib/active_record/connection_adapters/abstract_adapter.rb +628 -0
  69. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +887 -0
  70. data/lib/active_record/connection_adapters/column.rb +91 -0
  71. data/lib/active_record/connection_adapters/connection_specification.rb +287 -0
  72. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +33 -0
  73. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  74. data/lib/active_record/connection_adapters/mysql/database_statements.rb +140 -0
  75. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  76. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +73 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +87 -0
  79. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +80 -0
  80. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +148 -0
  81. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +35 -0
  82. data/lib/active_record/connection_adapters/mysql2_adapter.rb +129 -0
  83. data/lib/active_record/connection_adapters/postgresql/column.rb +44 -0
  84. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +163 -0
  85. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid.rb +34 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +92 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +56 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +50 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +23 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +21 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +71 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +41 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +65 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +97 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +111 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +23 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
  109. data/lib/active_record/connection_adapters/postgresql/quoting.rb +168 -0
  110. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +43 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +65 -0
  112. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +206 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  114. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +774 -0
  115. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +39 -0
  116. data/lib/active_record/connection_adapters/postgresql/utils.rb +81 -0
  117. data/lib/active_record/connection_adapters/postgresql_adapter.rb +863 -0
  118. data/lib/active_record/connection_adapters/schema_cache.rb +118 -0
  119. data/lib/active_record/connection_adapters/sql_type_metadata.rb +34 -0
  120. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  121. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +67 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  125. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +106 -0
  126. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +573 -0
  127. data/lib/active_record/connection_adapters/statement_pool.rb +61 -0
  128. data/lib/active_record/connection_handling.rb +145 -0
  129. data/lib/active_record/core.rb +559 -0
  130. data/lib/active_record/counter_cache.rb +218 -0
  131. data/lib/active_record/define_callbacks.rb +22 -0
  132. data/lib/active_record/dynamic_matchers.rb +122 -0
  133. data/lib/active_record/enum.rb +244 -0
  134. data/lib/active_record/errors.rb +380 -0
  135. data/lib/active_record/explain.rb +50 -0
  136. data/lib/active_record/explain_registry.rb +32 -0
  137. data/lib/active_record/explain_subscriber.rb +34 -0
  138. data/lib/active_record/fixture_set/file.rb +82 -0
  139. data/lib/active_record/fixtures.rb +1065 -0
  140. data/lib/active_record/gem_version.rb +17 -0
  141. data/lib/active_record/inheritance.rb +283 -0
  142. data/lib/active_record/integration.rb +155 -0
  143. data/lib/active_record/internal_metadata.rb +45 -0
  144. data/lib/active_record/legacy_yaml_adapter.rb +48 -0
  145. data/lib/active_record/locale/en.yml +48 -0
  146. data/lib/active_record/locking/optimistic.rb +198 -0
  147. data/lib/active_record/locking/pessimistic.rb +89 -0
  148. data/lib/active_record/log_subscriber.rb +137 -0
  149. data/lib/active_record/migration.rb +1378 -0
  150. data/lib/active_record/migration/command_recorder.rb +240 -0
  151. data/lib/active_record/migration/compatibility.rb +217 -0
  152. data/lib/active_record/migration/join_table.rb +17 -0
  153. data/lib/active_record/model_schema.rb +521 -0
  154. data/lib/active_record/nested_attributes.rb +600 -0
  155. data/lib/active_record/no_touching.rb +58 -0
  156. data/lib/active_record/null_relation.rb +68 -0
  157. data/lib/active_record/persistence.rb +763 -0
  158. data/lib/active_record/query_cache.rb +45 -0
  159. data/lib/active_record/querying.rb +70 -0
  160. data/lib/active_record/railtie.rb +226 -0
  161. data/lib/active_record/railties/console_sandbox.rb +7 -0
  162. data/lib/active_record/railties/controller_runtime.rb +56 -0
  163. data/lib/active_record/railties/databases.rake +377 -0
  164. data/lib/active_record/readonly_attributes.rb +24 -0
  165. data/lib/active_record/reflection.rb +1044 -0
  166. data/lib/active_record/relation.rb +629 -0
  167. data/lib/active_record/relation/batches.rb +287 -0
  168. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  169. data/lib/active_record/relation/calculations.rb +417 -0
  170. data/lib/active_record/relation/delegation.rb +147 -0
  171. data/lib/active_record/relation/finder_methods.rb +565 -0
  172. data/lib/active_record/relation/from_clause.rb +26 -0
  173. data/lib/active_record/relation/merger.rb +193 -0
  174. data/lib/active_record/relation/predicate_builder.rb +152 -0
  175. data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
  176. data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
  177. data/lib/active_record/relation/predicate_builder/base_handler.rb +19 -0
  178. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +20 -0
  179. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
  180. data/lib/active_record/relation/predicate_builder/range_handler.rb +42 -0
  181. data/lib/active_record/relation/predicate_builder/relation_handler.rb +19 -0
  182. data/lib/active_record/relation/query_attribute.rb +45 -0
  183. data/lib/active_record/relation/query_methods.rb +1231 -0
  184. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  185. data/lib/active_record/relation/spawn_methods.rb +77 -0
  186. data/lib/active_record/relation/where_clause.rb +186 -0
  187. data/lib/active_record/relation/where_clause_factory.rb +34 -0
  188. data/lib/active_record/result.rb +149 -0
  189. data/lib/active_record/runtime_registry.rb +24 -0
  190. data/lib/active_record/sanitization.rb +222 -0
  191. data/lib/active_record/schema.rb +70 -0
  192. data/lib/active_record/schema_dumper.rb +255 -0
  193. data/lib/active_record/schema_migration.rb +56 -0
  194. data/lib/active_record/scoping.rb +106 -0
  195. data/lib/active_record/scoping/default.rb +152 -0
  196. data/lib/active_record/scoping/named.rb +213 -0
  197. data/lib/active_record/secure_token.rb +40 -0
  198. data/lib/active_record/serialization.rb +22 -0
  199. data/lib/active_record/statement_cache.rb +121 -0
  200. data/lib/active_record/store.rb +211 -0
  201. data/lib/active_record/suppressor.rb +61 -0
  202. data/lib/active_record/table_metadata.rb +82 -0
  203. data/lib/active_record/tasks/database_tasks.rb +337 -0
  204. data/lib/active_record/tasks/mysql_database_tasks.rb +115 -0
  205. data/lib/active_record/tasks/postgresql_database_tasks.rb +143 -0
  206. data/lib/active_record/tasks/sqlite_database_tasks.rb +83 -0
  207. data/lib/active_record/timestamp.rb +153 -0
  208. data/lib/active_record/touch_later.rb +64 -0
  209. data/lib/active_record/transactions.rb +502 -0
  210. data/lib/active_record/translation.rb +24 -0
  211. data/lib/active_record/type.rb +79 -0
  212. data/lib/active_record/type/adapter_specific_registry.rb +136 -0
  213. data/lib/active_record/type/date.rb +9 -0
  214. data/lib/active_record/type/date_time.rb +9 -0
  215. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  216. data/lib/active_record/type/hash_lookup_type_map.rb +25 -0
  217. data/lib/active_record/type/internal/timezone.rb +17 -0
  218. data/lib/active_record/type/json.rb +30 -0
  219. data/lib/active_record/type/serialized.rb +71 -0
  220. data/lib/active_record/type/text.rb +11 -0
  221. data/lib/active_record/type/time.rb +21 -0
  222. data/lib/active_record/type/type_map.rb +62 -0
  223. data/lib/active_record/type/unsigned_integer.rb +17 -0
  224. data/lib/active_record/type_caster.rb +9 -0
  225. data/lib/active_record/type_caster/connection.rb +33 -0
  226. data/lib/active_record/type_caster/map.rb +23 -0
  227. data/lib/active_record/validations.rb +93 -0
  228. data/lib/active_record/validations/absence.rb +25 -0
  229. data/lib/active_record/validations/associated.rb +60 -0
  230. data/lib/active_record/validations/length.rb +26 -0
  231. data/lib/active_record/validations/presence.rb +68 -0
  232. data/lib/active_record/validations/uniqueness.rb +238 -0
  233. data/lib/active_record/version.rb +10 -0
  234. data/lib/rails/generators/active_record.rb +19 -0
  235. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  236. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  237. data/lib/rails/generators/active_record/migration.rb +35 -0
  238. data/lib/rails/generators/active_record/migration/migration_generator.rb +78 -0
  239. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
  240. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +46 -0
  241. data/lib/rails/generators/active_record/model/model_generator.rb +48 -0
  242. data/lib/rails/generators/active_record/model/templates/model.rb.tt +13 -0
  243. data/lib/rails/generators/active_record/model/templates/module.rb.tt +7 -0
  244. metadata +333 -0
@@ -0,0 +1,685 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters #:nodoc:
5
+ # Abstract representation of an index definition on a table. Instances of
6
+ # this type are typically created and returned by methods in database
7
+ # adapters. e.g. ActiveRecord::ConnectionAdapters::MySQL::SchemaStatements#indexes
8
+ class IndexDefinition # :nodoc:
9
+ attr_reader :table, :name, :unique, :columns, :lengths, :orders, :opclasses, :where, :type, :using, :comment
10
+
11
+ def initialize(
12
+ table, name,
13
+ unique = false,
14
+ columns = [],
15
+ lengths: {},
16
+ orders: {},
17
+ opclasses: {},
18
+ where: nil,
19
+ type: nil,
20
+ using: nil,
21
+ comment: nil
22
+ )
23
+ @table = table
24
+ @name = name
25
+ @unique = unique
26
+ @columns = columns
27
+ @lengths = concise_options(lengths)
28
+ @orders = concise_options(orders)
29
+ @opclasses = concise_options(opclasses)
30
+ @where = where
31
+ @type = type
32
+ @using = using
33
+ @comment = comment
34
+ end
35
+
36
+ private
37
+ def concise_options(options)
38
+ if columns.size == options.size && options.values.uniq.size == 1
39
+ options.values.first
40
+ else
41
+ options
42
+ end
43
+ end
44
+ end
45
+
46
+ # Abstract representation of a column definition. Instances of this type
47
+ # are typically created by methods in TableDefinition, and added to the
48
+ # +columns+ attribute of said TableDefinition object, in order to be used
49
+ # for generating a number of table creation or table changing SQL statements.
50
+ ColumnDefinition = Struct.new(:name, :type, :options, :sql_type) do # :nodoc:
51
+ def primary_key?
52
+ options[:primary_key]
53
+ end
54
+
55
+ [:limit, :precision, :scale, :default, :null, :collation, :comment].each do |option_name|
56
+ module_eval <<-CODE, __FILE__, __LINE__ + 1
57
+ def #{option_name}
58
+ options[:#{option_name}]
59
+ end
60
+
61
+ def #{option_name}=(value)
62
+ options[:#{option_name}] = value
63
+ end
64
+ CODE
65
+ end
66
+ end
67
+
68
+ AddColumnDefinition = Struct.new(:column) # :nodoc:
69
+
70
+ ChangeColumnDefinition = Struct.new(:column, :name) #:nodoc:
71
+
72
+ PrimaryKeyDefinition = Struct.new(:name) # :nodoc:
73
+
74
+ ForeignKeyDefinition = Struct.new(:from_table, :to_table, :options) do #:nodoc:
75
+ def name
76
+ options[:name]
77
+ end
78
+
79
+ def column
80
+ options[:column]
81
+ end
82
+
83
+ def primary_key
84
+ options[:primary_key] || default_primary_key
85
+ end
86
+
87
+ def on_delete
88
+ options[:on_delete]
89
+ end
90
+
91
+ def on_update
92
+ options[:on_update]
93
+ end
94
+
95
+ def custom_primary_key?
96
+ options[:primary_key] != default_primary_key
97
+ end
98
+
99
+ def validate?
100
+ options.fetch(:validate, true)
101
+ end
102
+ alias validated? validate?
103
+
104
+ def defined_for?(to_table_ord = nil, to_table: nil, **options)
105
+ if to_table_ord
106
+ self.to_table == to_table_ord.to_s
107
+ else
108
+ (to_table.nil? || to_table.to_s == self.to_table) &&
109
+ options.all? { |k, v| self.options[k].to_s == v.to_s }
110
+ end
111
+ end
112
+
113
+ private
114
+ def default_primary_key
115
+ "id"
116
+ end
117
+ end
118
+
119
+ class ReferenceDefinition # :nodoc:
120
+ def initialize(
121
+ name,
122
+ polymorphic: false,
123
+ index: true,
124
+ foreign_key: false,
125
+ type: :bigint,
126
+ **options
127
+ )
128
+ @name = name
129
+ @polymorphic = polymorphic
130
+ @index = index
131
+ @foreign_key = foreign_key
132
+ @type = type
133
+ @options = options
134
+
135
+ if polymorphic && foreign_key
136
+ raise ArgumentError, "Cannot add a foreign key to a polymorphic relation"
137
+ end
138
+ end
139
+
140
+ def add_to(table)
141
+ columns.each do |column_options|
142
+ table.column(*column_options)
143
+ end
144
+
145
+ if index
146
+ table.index(column_names, index_options)
147
+ end
148
+
149
+ if foreign_key
150
+ table.foreign_key(foreign_table_name, foreign_key_options)
151
+ end
152
+ end
153
+
154
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
155
+ # Workaround for Ruby 2.2 "private attribute?" warning.
156
+ protected
157
+
158
+ attr_reader :name, :polymorphic, :index, :foreign_key, :type, :options
159
+
160
+ private
161
+
162
+ def as_options(value)
163
+ value.is_a?(Hash) ? value : {}
164
+ end
165
+
166
+ def polymorphic_options
167
+ as_options(polymorphic).merge(options.slice(:null, :first, :after))
168
+ end
169
+
170
+ def index_options
171
+ as_options(index)
172
+ end
173
+
174
+ def foreign_key_options
175
+ as_options(foreign_key).merge(column: column_name)
176
+ end
177
+
178
+ def columns
179
+ result = [[column_name, type, options]]
180
+ if polymorphic
181
+ result.unshift(["#{name}_type", :string, polymorphic_options])
182
+ end
183
+ result
184
+ end
185
+
186
+ def column_name
187
+ "#{name}_id"
188
+ end
189
+
190
+ def column_names
191
+ columns.map(&:first)
192
+ end
193
+
194
+ def foreign_table_name
195
+ foreign_key_options.fetch(:to_table) do
196
+ Base.pluralize_table_names ? name.to_s.pluralize : name
197
+ end
198
+ end
199
+ end
200
+
201
+ module ColumnMethods
202
+ # Appends a primary key definition to the table definition.
203
+ # Can be called multiple times, but this is probably not a good idea.
204
+ def primary_key(name, type = :primary_key, **options)
205
+ column(name, type, options.merge(primary_key: true))
206
+ end
207
+
208
+ # Appends a column or columns of a specified type.
209
+ #
210
+ # t.string(:goat)
211
+ # t.string(:goat, :sheep)
212
+ #
213
+ # See TableDefinition#column
214
+ [
215
+ :bigint,
216
+ :binary,
217
+ :boolean,
218
+ :date,
219
+ :datetime,
220
+ :decimal,
221
+ :float,
222
+ :integer,
223
+ :json,
224
+ :string,
225
+ :text,
226
+ :time,
227
+ :timestamp,
228
+ :virtual,
229
+ ].each do |column_type|
230
+ module_eval <<-CODE, __FILE__, __LINE__ + 1
231
+ def #{column_type}(*args, **options)
232
+ args.each { |name| column(name, :#{column_type}, options) }
233
+ end
234
+ CODE
235
+ end
236
+ alias_method :numeric, :decimal
237
+ end
238
+
239
+ # Represents the schema of an SQL table in an abstract way. This class
240
+ # provides methods for manipulating the schema representation.
241
+ #
242
+ # Inside migration files, the +t+ object in {create_table}[rdoc-ref:SchemaStatements#create_table]
243
+ # is actually of this type:
244
+ #
245
+ # class SomeMigration < ActiveRecord::Migration[5.0]
246
+ # def up
247
+ # create_table :foo do |t|
248
+ # puts t.class # => "ActiveRecord::ConnectionAdapters::TableDefinition"
249
+ # end
250
+ # end
251
+ #
252
+ # def down
253
+ # ...
254
+ # end
255
+ # end
256
+ #
257
+ class TableDefinition
258
+ include ColumnMethods
259
+
260
+ attr_accessor :indexes
261
+ attr_reader :name, :temporary, :options, :as, :foreign_keys, :comment
262
+
263
+ def initialize(name, temporary = false, options = nil, as = nil, comment: nil)
264
+ @columns_hash = {}
265
+ @indexes = []
266
+ @foreign_keys = []
267
+ @primary_keys = nil
268
+ @temporary = temporary
269
+ @options = options
270
+ @as = as
271
+ @name = name
272
+ @comment = comment
273
+ end
274
+
275
+ def primary_keys(name = nil) # :nodoc:
276
+ @primary_keys = PrimaryKeyDefinition.new(name) if name
277
+ @primary_keys
278
+ end
279
+
280
+ # Returns an array of ColumnDefinition objects for the columns of the table.
281
+ def columns; @columns_hash.values; end
282
+
283
+ # Returns a ColumnDefinition for the column with name +name+.
284
+ def [](name)
285
+ @columns_hash[name.to_s]
286
+ end
287
+
288
+ # Instantiates a new column for the table.
289
+ # See {connection.add_column}[rdoc-ref:ConnectionAdapters::SchemaStatements#add_column]
290
+ # for available options.
291
+ #
292
+ # Additional options are:
293
+ # * <tt>:index</tt> -
294
+ # Create an index for the column. Can be either <tt>true</tt> or an options hash.
295
+ #
296
+ # This method returns <tt>self</tt>.
297
+ #
298
+ # == Examples
299
+ #
300
+ # # Assuming +td+ is an instance of TableDefinition
301
+ # td.column(:granted, :boolean, index: true)
302
+ #
303
+ # == Short-hand examples
304
+ #
305
+ # Instead of calling #column directly, you can also work with the short-hand definitions for the default types.
306
+ # They use the type as the method name instead of as a parameter and allow for multiple columns to be defined
307
+ # in a single statement.
308
+ #
309
+ # What can be written like this with the regular calls to column:
310
+ #
311
+ # create_table :products do |t|
312
+ # t.column :shop_id, :integer
313
+ # t.column :creator_id, :integer
314
+ # t.column :item_number, :string
315
+ # t.column :name, :string, default: "Untitled"
316
+ # t.column :value, :string, default: "Untitled"
317
+ # t.column :created_at, :datetime
318
+ # t.column :updated_at, :datetime
319
+ # end
320
+ # add_index :products, :item_number
321
+ #
322
+ # can also be written as follows using the short-hand:
323
+ #
324
+ # create_table :products do |t|
325
+ # t.integer :shop_id, :creator_id
326
+ # t.string :item_number, index: true
327
+ # t.string :name, :value, default: "Untitled"
328
+ # t.timestamps null: false
329
+ # end
330
+ #
331
+ # There's a short-hand method for each of the type values declared at the top. And then there's
332
+ # TableDefinition#timestamps that'll add +created_at+ and +updated_at+ as datetimes.
333
+ #
334
+ # TableDefinition#references will add an appropriately-named _id column, plus a corresponding _type
335
+ # column if the <tt>:polymorphic</tt> option is supplied. If <tt>:polymorphic</tt> is a hash of
336
+ # options, these will be used when creating the <tt>_type</tt> column. The <tt>:index</tt> option
337
+ # will also create an index, similar to calling {add_index}[rdoc-ref:ConnectionAdapters::SchemaStatements#add_index].
338
+ # So what can be written like this:
339
+ #
340
+ # create_table :taggings do |t|
341
+ # t.integer :tag_id, :tagger_id, :taggable_id
342
+ # t.string :tagger_type
343
+ # t.string :taggable_type, default: 'Photo'
344
+ # end
345
+ # add_index :taggings, :tag_id, name: 'index_taggings_on_tag_id'
346
+ # add_index :taggings, [:tagger_id, :tagger_type]
347
+ #
348
+ # Can also be written as follows using references:
349
+ #
350
+ # create_table :taggings do |t|
351
+ # t.references :tag, index: { name: 'index_taggings_on_tag_id' }
352
+ # t.references :tagger, polymorphic: true, index: true
353
+ # t.references :taggable, polymorphic: { default: 'Photo' }
354
+ # end
355
+ def column(name, type, options = {})
356
+ name = name.to_s
357
+ type = type.to_sym if type
358
+ options = options.dup
359
+
360
+ if @columns_hash[name] && @columns_hash[name].primary_key?
361
+ raise ArgumentError, "you can't redefine the primary key column '#{name}'. To define a custom primary key, pass { id: false } to create_table."
362
+ end
363
+
364
+ index_options = options.delete(:index)
365
+ index(name, index_options.is_a?(Hash) ? index_options : {}) if index_options
366
+ @columns_hash[name] = new_column_definition(name, type, options)
367
+ self
368
+ end
369
+
370
+ # remove the column +name+ from the table.
371
+ # remove_column(:account_id)
372
+ def remove_column(name)
373
+ @columns_hash.delete name.to_s
374
+ end
375
+
376
+ # Adds index options to the indexes hash, keyed by column name
377
+ # This is primarily used to track indexes that need to be created after the table
378
+ #
379
+ # index(:account_id, name: 'index_projects_on_account_id')
380
+ def index(column_name, options = {})
381
+ indexes << [column_name, options]
382
+ end
383
+
384
+ def foreign_key(table_name, options = {}) # :nodoc:
385
+ table_name_prefix = ActiveRecord::Base.table_name_prefix
386
+ table_name_suffix = ActiveRecord::Base.table_name_suffix
387
+ table_name = "#{table_name_prefix}#{table_name}#{table_name_suffix}"
388
+ foreign_keys.push([table_name, options])
389
+ end
390
+
391
+ # Appends <tt>:datetime</tt> columns <tt>:created_at</tt> and
392
+ # <tt>:updated_at</tt> to the table. See {connection.add_timestamps}[rdoc-ref:SchemaStatements#add_timestamps]
393
+ #
394
+ # t.timestamps null: false
395
+ def timestamps(**options)
396
+ options[:null] = false if options[:null].nil?
397
+
398
+ column(:created_at, :datetime, options)
399
+ column(:updated_at, :datetime, options)
400
+ end
401
+
402
+ # Adds a reference.
403
+ #
404
+ # t.references(:user)
405
+ # t.belongs_to(:supplier, foreign_key: true)
406
+ #
407
+ # See {connection.add_reference}[rdoc-ref:SchemaStatements#add_reference] for details of the options you can use.
408
+ def references(*args, **options)
409
+ args.each do |ref_name|
410
+ ReferenceDefinition.new(ref_name, options).add_to(self)
411
+ end
412
+ end
413
+ alias :belongs_to :references
414
+
415
+ def new_column_definition(name, type, **options) # :nodoc:
416
+ if integer_like_primary_key?(type, options)
417
+ type = integer_like_primary_key_type(type, options)
418
+ end
419
+ type = aliased_types(type.to_s, type)
420
+ options[:primary_key] ||= type == :primary_key
421
+ options[:null] = false if options[:primary_key]
422
+ create_column_definition(name, type, options)
423
+ end
424
+
425
+ private
426
+ def create_column_definition(name, type, options)
427
+ ColumnDefinition.new(name, type, options)
428
+ end
429
+
430
+ def aliased_types(name, fallback)
431
+ "timestamp" == name ? :datetime : fallback
432
+ end
433
+
434
+ def integer_like_primary_key?(type, options)
435
+ options[:primary_key] && [:integer, :bigint].include?(type) && !options.key?(:default)
436
+ end
437
+
438
+ def integer_like_primary_key_type(type, options)
439
+ type
440
+ end
441
+ end
442
+
443
+ class AlterTable # :nodoc:
444
+ attr_reader :adds
445
+ attr_reader :foreign_key_adds
446
+ attr_reader :foreign_key_drops
447
+
448
+ def initialize(td)
449
+ @td = td
450
+ @adds = []
451
+ @foreign_key_adds = []
452
+ @foreign_key_drops = []
453
+ end
454
+
455
+ def name; @td.name; end
456
+
457
+ def add_foreign_key(to_table, options)
458
+ @foreign_key_adds << ForeignKeyDefinition.new(name, to_table, options)
459
+ end
460
+
461
+ def drop_foreign_key(name)
462
+ @foreign_key_drops << name
463
+ end
464
+
465
+ def add_column(name, type, options)
466
+ name = name.to_s
467
+ type = type.to_sym
468
+ @adds << AddColumnDefinition.new(@td.new_column_definition(name, type, options))
469
+ end
470
+ end
471
+
472
+ # Represents an SQL table in an abstract way for updating a table.
473
+ # Also see TableDefinition and {connection.create_table}[rdoc-ref:SchemaStatements#create_table]
474
+ #
475
+ # Available transformations are:
476
+ #
477
+ # change_table :table do |t|
478
+ # t.primary_key
479
+ # t.column
480
+ # t.index
481
+ # t.rename_index
482
+ # t.timestamps
483
+ # t.change
484
+ # t.change_default
485
+ # t.rename
486
+ # t.references
487
+ # t.belongs_to
488
+ # t.string
489
+ # t.text
490
+ # t.integer
491
+ # t.bigint
492
+ # t.float
493
+ # t.decimal
494
+ # t.numeric
495
+ # t.datetime
496
+ # t.timestamp
497
+ # t.time
498
+ # t.date
499
+ # t.binary
500
+ # t.boolean
501
+ # t.foreign_key
502
+ # t.json
503
+ # t.virtual
504
+ # t.remove
505
+ # t.remove_references
506
+ # t.remove_belongs_to
507
+ # t.remove_index
508
+ # t.remove_timestamps
509
+ # end
510
+ #
511
+ class Table
512
+ include ColumnMethods
513
+
514
+ attr_reader :name
515
+
516
+ def initialize(table_name, base)
517
+ @name = table_name
518
+ @base = base
519
+ end
520
+
521
+ # Adds a new column to the named table.
522
+ #
523
+ # t.column(:name, :string)
524
+ #
525
+ # See TableDefinition#column for details of the options you can use.
526
+ def column(column_name, type, options = {})
527
+ @base.add_column(name, column_name, type, options)
528
+ end
529
+
530
+ # Checks to see if a column exists.
531
+ #
532
+ # t.string(:name) unless t.column_exists?(:name, :string)
533
+ #
534
+ # See {connection.column_exists?}[rdoc-ref:SchemaStatements#column_exists?]
535
+ def column_exists?(column_name, type = nil, options = {})
536
+ @base.column_exists?(name, column_name, type, options)
537
+ end
538
+
539
+ # Adds a new index to the table. +column_name+ can be a single Symbol, or
540
+ # an Array of Symbols.
541
+ #
542
+ # t.index(:name)
543
+ # t.index([:branch_id, :party_id], unique: true)
544
+ # t.index([:branch_id, :party_id], unique: true, name: 'by_branch_party')
545
+ #
546
+ # See {connection.add_index}[rdoc-ref:SchemaStatements#add_index] for details of the options you can use.
547
+ def index(column_name, options = {})
548
+ @base.add_index(name, column_name, options)
549
+ end
550
+
551
+ # Checks to see if an index exists.
552
+ #
553
+ # unless t.index_exists?(:branch_id)
554
+ # t.index(:branch_id)
555
+ # end
556
+ #
557
+ # See {connection.index_exists?}[rdoc-ref:SchemaStatements#index_exists?]
558
+ def index_exists?(column_name, options = {})
559
+ @base.index_exists?(name, column_name, options)
560
+ end
561
+
562
+ # Renames the given index on the table.
563
+ #
564
+ # t.rename_index(:user_id, :account_id)
565
+ #
566
+ # See {connection.rename_index}[rdoc-ref:SchemaStatements#rename_index]
567
+ def rename_index(index_name, new_index_name)
568
+ @base.rename_index(name, index_name, new_index_name)
569
+ end
570
+
571
+ # Adds timestamps (+created_at+ and +updated_at+) columns to the table.
572
+ #
573
+ # t.timestamps(null: false)
574
+ #
575
+ # See {connection.add_timestamps}[rdoc-ref:SchemaStatements#add_timestamps]
576
+ def timestamps(options = {})
577
+ @base.add_timestamps(name, options)
578
+ end
579
+
580
+ # Changes the column's definition according to the new options.
581
+ #
582
+ # t.change(:name, :string, limit: 80)
583
+ # t.change(:description, :text)
584
+ #
585
+ # See TableDefinition#column for details of the options you can use.
586
+ def change(column_name, type, options = {})
587
+ @base.change_column(name, column_name, type, options)
588
+ end
589
+
590
+ # Sets a new default value for a column.
591
+ #
592
+ # t.change_default(:qualification, 'new')
593
+ # t.change_default(:authorized, 1)
594
+ # t.change_default(:status, from: nil, to: "draft")
595
+ #
596
+ # See {connection.change_column_default}[rdoc-ref:SchemaStatements#change_column_default]
597
+ def change_default(column_name, default_or_changes)
598
+ @base.change_column_default(name, column_name, default_or_changes)
599
+ end
600
+
601
+ # Removes the column(s) from the table definition.
602
+ #
603
+ # t.remove(:qualification)
604
+ # t.remove(:qualification, :experience)
605
+ #
606
+ # See {connection.remove_columns}[rdoc-ref:SchemaStatements#remove_columns]
607
+ def remove(*column_names)
608
+ @base.remove_columns(name, *column_names)
609
+ end
610
+
611
+ # Removes the given index from the table.
612
+ #
613
+ # t.remove_index(:branch_id)
614
+ # t.remove_index(column: [:branch_id, :party_id])
615
+ # t.remove_index(name: :by_branch_party)
616
+ #
617
+ # See {connection.remove_index}[rdoc-ref:SchemaStatements#remove_index]
618
+ def remove_index(options = {})
619
+ @base.remove_index(name, options)
620
+ end
621
+
622
+ # Removes the timestamp columns (+created_at+ and +updated_at+) from the table.
623
+ #
624
+ # t.remove_timestamps
625
+ #
626
+ # See {connection.remove_timestamps}[rdoc-ref:SchemaStatements#remove_timestamps]
627
+ def remove_timestamps(options = {})
628
+ @base.remove_timestamps(name, options)
629
+ end
630
+
631
+ # Renames a column.
632
+ #
633
+ # t.rename(:description, :name)
634
+ #
635
+ # See {connection.rename_column}[rdoc-ref:SchemaStatements#rename_column]
636
+ def rename(column_name, new_column_name)
637
+ @base.rename_column(name, column_name, new_column_name)
638
+ end
639
+
640
+ # Adds a reference.
641
+ #
642
+ # t.references(:user)
643
+ # t.belongs_to(:supplier, foreign_key: true)
644
+ #
645
+ # See {connection.add_reference}[rdoc-ref:SchemaStatements#add_reference] for details of the options you can use.
646
+ def references(*args, **options)
647
+ args.each do |ref_name|
648
+ @base.add_reference(name, ref_name, options)
649
+ end
650
+ end
651
+ alias :belongs_to :references
652
+
653
+ # Removes a reference. Optionally removes a +type+ column.
654
+ #
655
+ # t.remove_references(:user)
656
+ # t.remove_belongs_to(:supplier, polymorphic: true)
657
+ #
658
+ # See {connection.remove_reference}[rdoc-ref:SchemaStatements#remove_reference]
659
+ def remove_references(*args, **options)
660
+ args.each do |ref_name|
661
+ @base.remove_reference(name, ref_name, options)
662
+ end
663
+ end
664
+ alias :remove_belongs_to :remove_references
665
+
666
+ # Adds a foreign key.
667
+ #
668
+ # t.foreign_key(:authors)
669
+ #
670
+ # See {connection.add_foreign_key}[rdoc-ref:SchemaStatements#add_foreign_key]
671
+ def foreign_key(*args)
672
+ @base.add_foreign_key(name, *args)
673
+ end
674
+
675
+ # Checks to see if a foreign key exists.
676
+ #
677
+ # t.foreign_key(:authors) unless t.foreign_key_exists?(:authors)
678
+ #
679
+ # See {connection.foreign_key_exists?}[rdoc-ref:SchemaStatements#foreign_key_exists?]
680
+ def foreign_key_exists?(*args)
681
+ @base.foreign_key_exists?(name, *args)
682
+ end
683
+ end
684
+ end
685
+ end