activerecord 1.0.0 → 4.0.0

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 (255) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +2102 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +213 -0
  5. data/examples/performance.rb +172 -0
  6. data/examples/simple.rb +14 -0
  7. data/lib/active_record/aggregations.rb +180 -84
  8. data/lib/active_record/associations/alias_tracker.rb +76 -0
  9. data/lib/active_record/associations/association.rb +248 -0
  10. data/lib/active_record/associations/association_scope.rb +135 -0
  11. data/lib/active_record/associations/belongs_to_association.rb +92 -0
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +35 -0
  13. data/lib/active_record/associations/builder/association.rb +108 -0
  14. data/lib/active_record/associations/builder/belongs_to.rb +98 -0
  15. data/lib/active_record/associations/builder/collection_association.rb +89 -0
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +39 -0
  17. data/lib/active_record/associations/builder/has_many.rb +15 -0
  18. data/lib/active_record/associations/builder/has_one.rb +25 -0
  19. data/lib/active_record/associations/builder/singular_association.rb +32 -0
  20. data/lib/active_record/associations/collection_association.rb +608 -0
  21. data/lib/active_record/associations/collection_proxy.rb +986 -0
  22. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +58 -39
  23. data/lib/active_record/associations/has_many_association.rb +116 -85
  24. data/lib/active_record/associations/has_many_through_association.rb +197 -0
  25. data/lib/active_record/associations/has_one_association.rb +102 -0
  26. data/lib/active_record/associations/has_one_through_association.rb +36 -0
  27. data/lib/active_record/associations/join_dependency/join_association.rb +174 -0
  28. data/lib/active_record/associations/join_dependency/join_base.rb +24 -0
  29. data/lib/active_record/associations/join_dependency/join_part.rb +78 -0
  30. data/lib/active_record/associations/join_dependency.rb +235 -0
  31. data/lib/active_record/associations/join_helper.rb +45 -0
  32. data/lib/active_record/associations/preloader/association.rb +121 -0
  33. data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
  34. data/lib/active_record/associations/preloader/collection_association.rb +24 -0
  35. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +60 -0
  36. data/lib/active_record/associations/preloader/has_many.rb +17 -0
  37. data/lib/active_record/associations/preloader/has_many_through.rb +19 -0
  38. data/lib/active_record/associations/preloader/has_one.rb +23 -0
  39. data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
  40. data/lib/active_record/associations/preloader/singular_association.rb +21 -0
  41. data/lib/active_record/associations/preloader/through_association.rb +63 -0
  42. data/lib/active_record/associations/preloader.rb +178 -0
  43. data/lib/active_record/associations/singular_association.rb +64 -0
  44. data/lib/active_record/associations/through_association.rb +87 -0
  45. data/lib/active_record/associations.rb +1437 -431
  46. data/lib/active_record/attribute_assignment.rb +201 -0
  47. data/lib/active_record/attribute_methods/before_type_cast.rb +70 -0
  48. data/lib/active_record/attribute_methods/dirty.rb +118 -0
  49. data/lib/active_record/attribute_methods/primary_key.rb +122 -0
  50. data/lib/active_record/attribute_methods/query.rb +40 -0
  51. data/lib/active_record/attribute_methods/read.rb +107 -0
  52. data/lib/active_record/attribute_methods/serialization.rb +162 -0
  53. data/lib/active_record/attribute_methods/time_zone_conversion.rb +59 -0
  54. data/lib/active_record/attribute_methods/write.rb +63 -0
  55. data/lib/active_record/attribute_methods.rb +393 -0
  56. data/lib/active_record/autosave_association.rb +426 -0
  57. data/lib/active_record/base.rb +268 -930
  58. data/lib/active_record/callbacks.rb +203 -230
  59. data/lib/active_record/coders/yaml_column.rb +38 -0
  60. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +638 -0
  61. data/lib/active_record/connection_adapters/abstract/database_limits.rb +67 -0
  62. data/lib/active_record/connection_adapters/abstract/database_statements.rb +390 -0
  63. data/lib/active_record/connection_adapters/abstract/query_cache.rb +95 -0
  64. data/lib/active_record/connection_adapters/abstract/quoting.rb +129 -0
  65. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +501 -0
  66. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +70 -0
  67. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +873 -0
  68. data/lib/active_record/connection_adapters/abstract/transaction.rb +203 -0
  69. data/lib/active_record/connection_adapters/abstract_adapter.rb +389 -275
  70. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +782 -0
  71. data/lib/active_record/connection_adapters/column.rb +318 -0
  72. data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
  73. data/lib/active_record/connection_adapters/mysql2_adapter.rb +273 -0
  74. data/lib/active_record/connection_adapters/mysql_adapter.rb +517 -90
  75. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +97 -0
  76. data/lib/active_record/connection_adapters/postgresql/cast.rb +152 -0
  77. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +242 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid.rb +366 -0
  79. data/lib/active_record/connection_adapters/postgresql/quoting.rb +171 -0
  80. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  81. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +489 -0
  82. data/lib/active_record/connection_adapters/postgresql_adapter.rb +911 -138
  83. data/lib/active_record/connection_adapters/schema_cache.rb +129 -0
  84. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +624 -0
  85. data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
  86. data/lib/active_record/connection_handling.rb +98 -0
  87. data/lib/active_record/core.rb +463 -0
  88. data/lib/active_record/counter_cache.rb +122 -0
  89. data/lib/active_record/dynamic_matchers.rb +131 -0
  90. data/lib/active_record/errors.rb +213 -0
  91. data/lib/active_record/explain.rb +38 -0
  92. data/lib/active_record/explain_registry.rb +30 -0
  93. data/lib/active_record/explain_subscriber.rb +29 -0
  94. data/lib/active_record/fixture_set/file.rb +55 -0
  95. data/lib/active_record/fixtures.rb +892 -138
  96. data/lib/active_record/inheritance.rb +200 -0
  97. data/lib/active_record/integration.rb +60 -0
  98. data/lib/active_record/locale/en.yml +47 -0
  99. data/lib/active_record/locking/optimistic.rb +181 -0
  100. data/lib/active_record/locking/pessimistic.rb +77 -0
  101. data/lib/active_record/log_subscriber.rb +82 -0
  102. data/lib/active_record/migration/command_recorder.rb +164 -0
  103. data/lib/active_record/migration/join_table.rb +15 -0
  104. data/lib/active_record/migration.rb +1015 -0
  105. data/lib/active_record/model_schema.rb +345 -0
  106. data/lib/active_record/nested_attributes.rb +546 -0
  107. data/lib/active_record/null_relation.rb +65 -0
  108. data/lib/active_record/persistence.rb +509 -0
  109. data/lib/active_record/query_cache.rb +56 -0
  110. data/lib/active_record/querying.rb +62 -0
  111. data/lib/active_record/railtie.rb +205 -0
  112. data/lib/active_record/railties/console_sandbox.rb +5 -0
  113. data/lib/active_record/railties/controller_runtime.rb +50 -0
  114. data/lib/active_record/railties/databases.rake +402 -0
  115. data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
  116. data/lib/active_record/readonly_attributes.rb +30 -0
  117. data/lib/active_record/reflection.rb +544 -87
  118. data/lib/active_record/relation/batches.rb +93 -0
  119. data/lib/active_record/relation/calculations.rb +399 -0
  120. data/lib/active_record/relation/delegation.rb +125 -0
  121. data/lib/active_record/relation/finder_methods.rb +349 -0
  122. data/lib/active_record/relation/merger.rb +161 -0
  123. data/lib/active_record/relation/predicate_builder.rb +106 -0
  124. data/lib/active_record/relation/query_methods.rb +1044 -0
  125. data/lib/active_record/relation/spawn_methods.rb +73 -0
  126. data/lib/active_record/relation.rb +655 -0
  127. data/lib/active_record/result.rb +67 -0
  128. data/lib/active_record/runtime_registry.rb +17 -0
  129. data/lib/active_record/sanitization.rb +168 -0
  130. data/lib/active_record/schema.rb +65 -0
  131. data/lib/active_record/schema_dumper.rb +204 -0
  132. data/lib/active_record/schema_migration.rb +39 -0
  133. data/lib/active_record/scoping/default.rb +146 -0
  134. data/lib/active_record/scoping/named.rb +175 -0
  135. data/lib/active_record/scoping.rb +82 -0
  136. data/lib/active_record/serialization.rb +22 -0
  137. data/lib/active_record/serializers/xml_serializer.rb +197 -0
  138. data/lib/active_record/statement_cache.rb +26 -0
  139. data/lib/active_record/store.rb +156 -0
  140. data/lib/active_record/tasks/database_tasks.rb +203 -0
  141. data/lib/active_record/tasks/firebird_database_tasks.rb +56 -0
  142. data/lib/active_record/tasks/mysql_database_tasks.rb +143 -0
  143. data/lib/active_record/tasks/oracle_database_tasks.rb +45 -0
  144. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  145. data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
  146. data/lib/active_record/tasks/sqlserver_database_tasks.rb +48 -0
  147. data/lib/active_record/test_case.rb +96 -0
  148. data/lib/active_record/timestamp.rb +119 -0
  149. data/lib/active_record/transactions.rb +366 -69
  150. data/lib/active_record/translation.rb +22 -0
  151. data/lib/active_record/validations/associated.rb +49 -0
  152. data/lib/active_record/validations/presence.rb +65 -0
  153. data/lib/active_record/validations/uniqueness.rb +225 -0
  154. data/lib/active_record/validations.rb +64 -185
  155. data/lib/active_record/version.rb +11 -0
  156. data/lib/active_record.rb +149 -24
  157. data/lib/rails/generators/active_record/migration/migration_generator.rb +62 -0
  158. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +19 -0
  159. data/lib/rails/generators/active_record/migration/templates/migration.rb +39 -0
  160. data/lib/rails/generators/active_record/model/model_generator.rb +48 -0
  161. data/lib/rails/generators/active_record/model/templates/model.rb +10 -0
  162. data/lib/rails/generators/active_record/model/templates/module.rb +7 -0
  163. data/lib/rails/generators/active_record.rb +23 -0
  164. metadata +261 -161
  165. data/CHANGELOG +0 -581
  166. data/README +0 -361
  167. data/RUNNING_UNIT_TESTS +0 -36
  168. data/dev-utils/eval_debugger.rb +0 -9
  169. data/examples/associations.png +0 -0
  170. data/examples/associations.rb +0 -87
  171. data/examples/shared_setup.rb +0 -15
  172. data/examples/validation.rb +0 -88
  173. data/install.rb +0 -60
  174. data/lib/active_record/associations/association_collection.rb +0 -70
  175. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -107
  176. data/lib/active_record/deprecated_associations.rb +0 -70
  177. data/lib/active_record/observer.rb +0 -71
  178. data/lib/active_record/support/class_attribute_accessors.rb +0 -43
  179. data/lib/active_record/support/class_inheritable_attributes.rb +0 -37
  180. data/lib/active_record/support/clean_logger.rb +0 -10
  181. data/lib/active_record/support/inflector.rb +0 -70
  182. data/lib/active_record/vendor/mysql.rb +0 -1117
  183. data/lib/active_record/vendor/simple.rb +0 -702
  184. data/lib/active_record/wrappers/yaml_wrapper.rb +0 -15
  185. data/lib/active_record/wrappings.rb +0 -59
  186. data/rakefile +0 -122
  187. data/test/abstract_unit.rb +0 -16
  188. data/test/aggregations_test.rb +0 -34
  189. data/test/all.sh +0 -8
  190. data/test/associations_test.rb +0 -477
  191. data/test/base_test.rb +0 -513
  192. data/test/class_inheritable_attributes_test.rb +0 -33
  193. data/test/connections/native_mysql/connection.rb +0 -24
  194. data/test/connections/native_postgresql/connection.rb +0 -24
  195. data/test/connections/native_sqlite/connection.rb +0 -24
  196. data/test/deprecated_associations_test.rb +0 -336
  197. data/test/finder_test.rb +0 -67
  198. data/test/fixtures/accounts/signals37 +0 -3
  199. data/test/fixtures/accounts/unknown +0 -2
  200. data/test/fixtures/auto_id.rb +0 -4
  201. data/test/fixtures/column_name.rb +0 -3
  202. data/test/fixtures/companies/first_client +0 -6
  203. data/test/fixtures/companies/first_firm +0 -4
  204. data/test/fixtures/companies/second_client +0 -6
  205. data/test/fixtures/company.rb +0 -37
  206. data/test/fixtures/company_in_module.rb +0 -33
  207. data/test/fixtures/course.rb +0 -3
  208. data/test/fixtures/courses/java +0 -2
  209. data/test/fixtures/courses/ruby +0 -2
  210. data/test/fixtures/customer.rb +0 -30
  211. data/test/fixtures/customers/david +0 -6
  212. data/test/fixtures/db_definitions/mysql.sql +0 -96
  213. data/test/fixtures/db_definitions/mysql2.sql +0 -4
  214. data/test/fixtures/db_definitions/postgresql.sql +0 -113
  215. data/test/fixtures/db_definitions/postgresql2.sql +0 -4
  216. data/test/fixtures/db_definitions/sqlite.sql +0 -85
  217. data/test/fixtures/db_definitions/sqlite2.sql +0 -4
  218. data/test/fixtures/default.rb +0 -2
  219. data/test/fixtures/developer.rb +0 -8
  220. data/test/fixtures/developers/david +0 -2
  221. data/test/fixtures/developers/jamis +0 -2
  222. data/test/fixtures/developers_projects/david_action_controller +0 -2
  223. data/test/fixtures/developers_projects/david_active_record +0 -2
  224. data/test/fixtures/developers_projects/jamis_active_record +0 -2
  225. data/test/fixtures/entrant.rb +0 -3
  226. data/test/fixtures/entrants/first +0 -3
  227. data/test/fixtures/entrants/second +0 -3
  228. data/test/fixtures/entrants/third +0 -3
  229. data/test/fixtures/fixture_database.sqlite +0 -0
  230. data/test/fixtures/fixture_database_2.sqlite +0 -0
  231. data/test/fixtures/movie.rb +0 -5
  232. data/test/fixtures/movies/first +0 -2
  233. data/test/fixtures/movies/second +0 -2
  234. data/test/fixtures/project.rb +0 -3
  235. data/test/fixtures/projects/action_controller +0 -2
  236. data/test/fixtures/projects/active_record +0 -2
  237. data/test/fixtures/reply.rb +0 -21
  238. data/test/fixtures/subscriber.rb +0 -5
  239. data/test/fixtures/subscribers/first +0 -2
  240. data/test/fixtures/subscribers/second +0 -2
  241. data/test/fixtures/topic.rb +0 -20
  242. data/test/fixtures/topics/first +0 -9
  243. data/test/fixtures/topics/second +0 -8
  244. data/test/fixtures_test.rb +0 -20
  245. data/test/inflector_test.rb +0 -104
  246. data/test/inheritance_test.rb +0 -125
  247. data/test/lifecycle_test.rb +0 -110
  248. data/test/modules_test.rb +0 -21
  249. data/test/multiple_db_test.rb +0 -46
  250. data/test/pk_test.rb +0 -57
  251. data/test/reflection_test.rb +0 -78
  252. data/test/thread_safety_test.rb +0 -33
  253. data/test/transactions_test.rb +0 -83
  254. data/test/unconnected_test.rb +0 -24
  255. data/test/validations_test.rb +0 -126
@@ -0,0 +1,655 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module ActiveRecord
4
+ # = Active Record Relation
5
+ class Relation
6
+ JoinOperation = Struct.new(:relation, :join_class, :on)
7
+
8
+ MULTI_VALUE_METHODS = [:includes, :eager_load, :preload, :select, :group,
9
+ :order, :joins, :where, :having, :bind, :references,
10
+ :extending]
11
+
12
+ SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :from, :reordering,
13
+ :reverse_order, :distinct, :create_with, :uniq]
14
+
15
+ VALUE_METHODS = MULTI_VALUE_METHODS + SINGLE_VALUE_METHODS
16
+
17
+ include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches, Explain, Delegation
18
+
19
+ attr_reader :table, :klass, :loaded
20
+ attr_accessor :default_scoped, :proxy_association
21
+ alias :model :klass
22
+ alias :loaded? :loaded
23
+ alias :default_scoped? :default_scoped
24
+
25
+ def initialize(klass, table, values = {})
26
+ @klass = klass
27
+ @table = table
28
+ @values = values
29
+ @implicit_readonly = nil
30
+ @loaded = false
31
+ @default_scoped = false
32
+ end
33
+
34
+ def initialize_copy(other)
35
+ # This method is a hot spot, so for now, use Hash[] to dup the hash.
36
+ # https://bugs.ruby-lang.org/issues/7166
37
+ @values = Hash[@values]
38
+ @values[:bind] = @values[:bind].dup if @values.key? :bind
39
+ reset
40
+ end
41
+
42
+ def insert(values)
43
+ primary_key_value = nil
44
+
45
+ if primary_key && Hash === values
46
+ primary_key_value = values[values.keys.find { |k|
47
+ k.name == primary_key
48
+ }]
49
+
50
+ if !primary_key_value && connection.prefetch_primary_key?(klass.table_name)
51
+ primary_key_value = connection.next_sequence_value(klass.sequence_name)
52
+ values[klass.arel_table[klass.primary_key]] = primary_key_value
53
+ end
54
+ end
55
+
56
+ im = arel.create_insert
57
+ im.into @table
58
+
59
+ conn = @klass.connection
60
+
61
+ substitutes = values.sort_by { |arel_attr,_| arel_attr.name }
62
+ binds = substitutes.map do |arel_attr, value|
63
+ [@klass.columns_hash[arel_attr.name], value]
64
+ end
65
+
66
+ substitutes.each_with_index do |tuple, i|
67
+ tuple[1] = conn.substitute_at(binds[i][0], i)
68
+ end
69
+
70
+ if values.empty? # empty insert
71
+ im.values = Arel.sql(connection.empty_insert_statement_value)
72
+ else
73
+ im.insert substitutes
74
+ end
75
+
76
+ conn.insert(
77
+ im,
78
+ 'SQL',
79
+ primary_key,
80
+ primary_key_value,
81
+ nil,
82
+ binds)
83
+ end
84
+
85
+ # Initializes new record from relation while maintaining the current
86
+ # scope.
87
+ #
88
+ # Expects arguments in the same format as +Base.new+.
89
+ #
90
+ # users = User.where(name: 'DHH')
91
+ # user = users.new # => #<User id: nil, name: "DHH", created_at: nil, updated_at: nil>
92
+ #
93
+ # You can also pass a block to new with the new record as argument:
94
+ #
95
+ # user = users.new { |user| user.name = 'Oscar' }
96
+ # user.name # => Oscar
97
+ def new(*args, &block)
98
+ scoping { @klass.new(*args, &block) }
99
+ end
100
+
101
+ alias build new
102
+
103
+ # Tries to create a new record with the same scoped attributes
104
+ # defined in the relation. Returns the initialized object if validation fails.
105
+ #
106
+ # Expects arguments in the same format as +Base.create+.
107
+ #
108
+ # ==== Examples
109
+ # users = User.where(name: 'Oscar')
110
+ # users.create # #<User id: 3, name: "oscar", ...>
111
+ #
112
+ # users.create(name: 'fxn')
113
+ # users.create # #<User id: 4, name: "fxn", ...>
114
+ #
115
+ # users.create { |user| user.name = 'tenderlove' }
116
+ # # #<User id: 5, name: "tenderlove", ...>
117
+ #
118
+ # users.create(name: nil) # validation on name
119
+ # # #<User id: nil, name: nil, ...>
120
+ def create(*args, &block)
121
+ scoping { @klass.create(*args, &block) }
122
+ end
123
+
124
+ # Similar to #create, but calls +create!+ on the base class. Raises
125
+ # an exception if a validation error occurs.
126
+ #
127
+ # Expects arguments in the same format as <tt>Base.create!</tt>.
128
+ def create!(*args, &block)
129
+ scoping { @klass.create!(*args, &block) }
130
+ end
131
+
132
+ def first_or_create(attributes = nil, &block) # :nodoc:
133
+ first || create(attributes, &block)
134
+ end
135
+
136
+ def first_or_create!(attributes = nil, &block) # :nodoc:
137
+ first || create!(attributes, &block)
138
+ end
139
+
140
+ def first_or_initialize(attributes = nil, &block) # :nodoc:
141
+ first || new(attributes, &block)
142
+ end
143
+
144
+ # Finds the first record with the given attributes, or creates a record
145
+ # with the attributes if one is not found:
146
+ #
147
+ # # Find the first user named "Penélope" or create a new one.
148
+ # User.find_or_create_by(first_name: 'Penélope')
149
+ # # => #<User id: 1, first_name: "Penélope", last_name: nil>
150
+ #
151
+ # # Find the first user named "Penélope" or create a new one.
152
+ # # We already have one so the existing record will be returned.
153
+ # User.find_or_create_by(first_name: 'Penélope')
154
+ # # => #<User id: 1, first_name: "Penélope", last_name: nil>
155
+ #
156
+ # # Find the first user named "Scarlett" or create a new one with
157
+ # # a particular last name.
158
+ # User.create_with(last_name: 'Johansson').find_or_create_by(first_name: 'Scarlett')
159
+ # # => #<User id: 2, first_name: "Scarlett", last_name: "Johansson">
160
+ #
161
+ # This method accepts a block, which is passed down to +create+. The last example
162
+ # above can be alternatively written this way:
163
+ #
164
+ # # Find the first user named "Scarlett" or create a new one with a
165
+ # # different last name.
166
+ # User.find_or_create_by(first_name: 'Scarlett') do |user|
167
+ # user.last_name = 'Johansson'
168
+ # end
169
+ # # => #<User id: 2, first_name: "Scarlett", last_name: "Johansson">
170
+ #
171
+ # This method always returns a record, but if creation was attempted and
172
+ # failed due to validation errors it won't be persisted, you get what
173
+ # +create+ returns in such situation.
174
+ #
175
+ # Please note *this method is not atomic*, it runs first a SELECT, and if
176
+ # there are no results an INSERT is attempted. If there are other threads
177
+ # or processes there is a race condition between both calls and it could
178
+ # be the case that you end up with two similar records.
179
+ #
180
+ # Whether that is a problem or not depends on the logic of the
181
+ # application, but in the particular case in which rows have a UNIQUE
182
+ # constraint an exception may be raised, just retry:
183
+ #
184
+ # begin
185
+ # CreditAccount.find_or_create_by(user_id: user.id)
186
+ # rescue ActiveRecord::RecordNotUnique
187
+ # retry
188
+ # end
189
+ #
190
+ def find_or_create_by(attributes, &block)
191
+ find_by(attributes) || create(attributes, &block)
192
+ end
193
+
194
+ # Like <tt>find_or_create_by</tt>, but calls <tt>create!</tt> so an exception
195
+ # is raised if the created record is invalid.
196
+ def find_or_create_by!(attributes, &block)
197
+ find_by(attributes) || create!(attributes, &block)
198
+ end
199
+
200
+ # Like <tt>find_or_create_by</tt>, but calls <tt>new</tt> instead of <tt>create</tt>.
201
+ def find_or_initialize_by(attributes, &block)
202
+ find_by(attributes) || new(attributes, &block)
203
+ end
204
+
205
+ # Runs EXPLAIN on the query or queries triggered by this relation and
206
+ # returns the result as a string. The string is formatted imitating the
207
+ # ones printed by the database shell.
208
+ #
209
+ # Note that this method actually runs the queries, since the results of some
210
+ # are needed by the next ones when eager loading is going on.
211
+ #
212
+ # Please see further details in the
213
+ # {Active Record Query Interface guide}[http://guides.rubyonrails.org/active_record_querying.html#running-explain].
214
+ def explain
215
+ exec_explain(collecting_queries_for_explain { exec_queries })
216
+ end
217
+
218
+ # Converts relation objects to Array.
219
+ def to_a
220
+ load
221
+ @records
222
+ end
223
+
224
+ def as_json(options = nil) #:nodoc:
225
+ to_a.as_json(options)
226
+ end
227
+
228
+ # Returns size of the records.
229
+ def size
230
+ loaded? ? @records.length : count
231
+ end
232
+
233
+ # Returns true if there are no records.
234
+ def empty?
235
+ return @records.empty? if loaded?
236
+
237
+ c = count
238
+ c.respond_to?(:zero?) ? c.zero? : c.empty?
239
+ end
240
+
241
+ # Returns true if there are any records.
242
+ def any?
243
+ if block_given?
244
+ to_a.any? { |*block_args| yield(*block_args) }
245
+ else
246
+ !empty?
247
+ end
248
+ end
249
+
250
+ # Returns true if there is more than one record.
251
+ def many?
252
+ if block_given?
253
+ to_a.many? { |*block_args| yield(*block_args) }
254
+ else
255
+ limit_value ? to_a.many? : size > 1
256
+ end
257
+ end
258
+
259
+ # Scope all queries to the current scope.
260
+ #
261
+ # Comment.where(post_id: 1).scoping do
262
+ # Comment.first
263
+ # end
264
+ # # => SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = 1 ORDER BY "comments"."id" ASC LIMIT 1
265
+ #
266
+ # Please check unscoped if you want to remove all previous scopes (including
267
+ # the default_scope) during the execution of a block.
268
+ def scoping
269
+ previous, klass.current_scope = klass.current_scope, self
270
+ yield
271
+ ensure
272
+ klass.current_scope = previous
273
+ end
274
+
275
+ # Updates all records with details given if they match a set of conditions supplied, limits and order can
276
+ # also be supplied. This method constructs a single SQL UPDATE statement and sends it straight to the
277
+ # database. It does not instantiate the involved models and it does not trigger Active Record callbacks
278
+ # or validations.
279
+ #
280
+ # ==== Parameters
281
+ #
282
+ # * +updates+ - A string, array, or hash representing the SET part of an SQL statement.
283
+ #
284
+ # ==== Examples
285
+ #
286
+ # # Update all customers with the given attributes
287
+ # Customer.update_all wants_email: true
288
+ #
289
+ # # Update all books with 'Rails' in their title
290
+ # Book.where('title LIKE ?', '%Rails%').update_all(author: 'David')
291
+ #
292
+ # # Update all books that match conditions, but limit it to 5 ordered by date
293
+ # Book.where('title LIKE ?', '%Rails%').order(:created_at).limit(5).update_all(author: 'David')
294
+ def update_all(updates)
295
+ raise ArgumentError, "Empty list of attributes to change" if updates.blank?
296
+
297
+ stmt = Arel::UpdateManager.new(arel.engine)
298
+
299
+ stmt.set Arel.sql(@klass.send(:sanitize_sql_for_assignment, updates))
300
+ stmt.table(table)
301
+ stmt.key = table[primary_key]
302
+
303
+ if with_default_scope.joins_values.any?
304
+ @klass.connection.join_to_update(stmt, arel)
305
+ else
306
+ stmt.take(arel.limit)
307
+ stmt.order(*arel.orders)
308
+ stmt.wheres = arel.constraints
309
+ end
310
+
311
+ @klass.connection.update stmt, 'SQL', bind_values
312
+ end
313
+
314
+ # Updates an object (or multiple objects) and saves it to the database, if validations pass.
315
+ # The resulting object is returned whether the object was saved successfully to the database or not.
316
+ #
317
+ # ==== Parameters
318
+ #
319
+ # * +id+ - This should be the id or an array of ids to be updated.
320
+ # * +attributes+ - This should be a hash of attributes or an array of hashes.
321
+ #
322
+ # ==== Examples
323
+ #
324
+ # # Updates one record
325
+ # Person.update(15, user_name: 'Samuel', group: 'expert')
326
+ #
327
+ # # Updates multiple records
328
+ # people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy" } }
329
+ # Person.update(people.keys, people.values)
330
+ def update(id, attributes)
331
+ if id.is_a?(Array)
332
+ id.map.with_index { |one_id, idx| update(one_id, attributes[idx]) }
333
+ else
334
+ object = find(id)
335
+ object.update(attributes)
336
+ object
337
+ end
338
+ end
339
+
340
+ # Destroys the records matching +conditions+ by instantiating each
341
+ # record and calling its +destroy+ method. Each object's callbacks are
342
+ # executed (including <tt>:dependent</tt> association options). Returns the
343
+ # collection of objects that were destroyed; each will be frozen, to
344
+ # reflect that no changes should be made (since they can't be persisted).
345
+ #
346
+ # Note: Instantiation, callback execution, and deletion of each
347
+ # record can be time consuming when you're removing many records at
348
+ # once. It generates at least one SQL +DELETE+ query per record (or
349
+ # possibly more, to enforce your callbacks). If you want to delete many
350
+ # rows quickly, without concern for their associations or callbacks, use
351
+ # +delete_all+ instead.
352
+ #
353
+ # ==== Parameters
354
+ #
355
+ # * +conditions+ - A string, array, or hash that specifies which records
356
+ # to destroy. If omitted, all records are destroyed. See the
357
+ # Conditions section in the introduction to ActiveRecord::Base for
358
+ # more information.
359
+ #
360
+ # ==== Examples
361
+ #
362
+ # Person.destroy_all("last_login < '2004-04-04'")
363
+ # Person.destroy_all(status: "inactive")
364
+ # Person.where(age: 0..18).destroy_all
365
+ def destroy_all(conditions = nil)
366
+ if conditions
367
+ where(conditions).destroy_all
368
+ else
369
+ to_a.each {|object| object.destroy }.tap { reset }
370
+ end
371
+ end
372
+
373
+ # Destroy an object (or multiple objects) that has the given id. The object is instantiated first,
374
+ # therefore all callbacks and filters are fired off before the object is deleted. This method is
375
+ # less efficient than ActiveRecord#delete but allows cleanup methods and other actions to be run.
376
+ #
377
+ # This essentially finds the object (or multiple objects) with the given id, creates a new object
378
+ # from the attributes, and then calls destroy on it.
379
+ #
380
+ # ==== Parameters
381
+ #
382
+ # * +id+ - Can be either an Integer or an Array of Integers.
383
+ #
384
+ # ==== Examples
385
+ #
386
+ # # Destroy a single object
387
+ # Todo.destroy(1)
388
+ #
389
+ # # Destroy multiple objects
390
+ # todos = [1,2,3]
391
+ # Todo.destroy(todos)
392
+ def destroy(id)
393
+ if id.is_a?(Array)
394
+ id.map { |one_id| destroy(one_id) }
395
+ else
396
+ find(id).destroy
397
+ end
398
+ end
399
+
400
+ # Deletes the records matching +conditions+ without instantiating the records
401
+ # first, and hence not calling the +destroy+ method nor invoking callbacks. This
402
+ # is a single SQL DELETE statement that goes straight to the database, much more
403
+ # efficient than +destroy_all+. Be careful with relations though, in particular
404
+ # <tt>:dependent</tt> rules defined on associations are not honored. Returns the
405
+ # number of rows affected.
406
+ #
407
+ # Post.delete_all("person_id = 5 AND (category = 'Something' OR category = 'Else')")
408
+ # Post.delete_all(["person_id = ? AND (category = ? OR category = ?)", 5, 'Something', 'Else'])
409
+ # Post.where(person_id: 5).where(category: ['Something', 'Else']).delete_all
410
+ #
411
+ # Both calls delete the affected posts all at once with a single DELETE statement.
412
+ # If you need to destroy dependent associations or call your <tt>before_*</tt> or
413
+ # +after_destroy+ callbacks, use the +destroy_all+ method instead.
414
+ #
415
+ # If a limit scope is supplied, +delete_all+ raises an ActiveRecord error:
416
+ #
417
+ # Post.limit(100).delete_all
418
+ # # => ActiveRecord::ActiveRecordError: delete_all doesn't support limit scope
419
+ def delete_all(conditions = nil)
420
+ raise ActiveRecordError.new("delete_all doesn't support limit scope") if self.limit_value
421
+
422
+ if conditions
423
+ where(conditions).delete_all
424
+ else
425
+ stmt = Arel::DeleteManager.new(arel.engine)
426
+ stmt.from(table)
427
+
428
+ if with_default_scope.joins_values.any?
429
+ @klass.connection.join_to_delete(stmt, arel, table[primary_key])
430
+ else
431
+ stmt.wheres = arel.constraints
432
+ end
433
+
434
+ affected = @klass.connection.delete(stmt, 'SQL', bind_values)
435
+
436
+ reset
437
+ affected
438
+ end
439
+ end
440
+
441
+ # Deletes the row with a primary key matching the +id+ argument, using a
442
+ # SQL +DELETE+ statement, and returns the number of rows deleted. Active
443
+ # Record objects are not instantiated, so the object's callbacks are not
444
+ # executed, including any <tt>:dependent</tt> association options.
445
+ #
446
+ # You can delete multiple rows at once by passing an Array of <tt>id</tt>s.
447
+ #
448
+ # Note: Although it is often much faster than the alternative,
449
+ # <tt>#destroy</tt>, skipping callbacks might bypass business logic in
450
+ # your application that ensures referential integrity or performs other
451
+ # essential jobs.
452
+ #
453
+ # ==== Examples
454
+ #
455
+ # # Delete a single row
456
+ # Todo.delete(1)
457
+ #
458
+ # # Delete multiple rows
459
+ # Todo.delete([2,3,4])
460
+ def delete(id_or_array)
461
+ where(primary_key => id_or_array).delete_all
462
+ end
463
+
464
+ # Causes the records to be loaded from the database if they have not
465
+ # been loaded already. You can use this if for some reason you need
466
+ # to explicitly load some records before actually using them. The
467
+ # return value is the relation itself, not the records.
468
+ #
469
+ # Post.where(published: true).load # => #<ActiveRecord::Relation>
470
+ def load
471
+ exec_queries unless loaded?
472
+
473
+ self
474
+ end
475
+
476
+ # Forces reloading of relation.
477
+ def reload
478
+ reset
479
+ load
480
+ end
481
+
482
+ def reset
483
+ @first = @last = @to_sql = @order_clause = @scope_for_create = @arel = @loaded = nil
484
+ @should_eager_load = @join_dependency = nil
485
+ @records = []
486
+ self
487
+ end
488
+
489
+ # Returns sql statement for the relation.
490
+ #
491
+ # User.where(name: 'Oscar').to_sql
492
+ # # => SELECT "users".* FROM "users" WHERE "users"."name" = 'Oscar'
493
+ def to_sql
494
+ @to_sql ||= klass.connection.to_sql(arel, bind_values.dup)
495
+ end
496
+
497
+ # Returns a hash of where conditions.
498
+ #
499
+ # User.where(name: 'Oscar').where_values_hash
500
+ # # => {name: "Oscar"}
501
+ def where_values_hash
502
+ equalities = with_default_scope.where_values.grep(Arel::Nodes::Equality).find_all { |node|
503
+ node.left.relation.name == table_name
504
+ }
505
+
506
+ binds = Hash[bind_values.find_all(&:first).map { |column, v| [column.name, v] }]
507
+
508
+ Hash[equalities.map { |where|
509
+ name = where.left.name
510
+ [name, binds.fetch(name.to_s) { where.right }]
511
+ }]
512
+ end
513
+
514
+ def scope_for_create
515
+ @scope_for_create ||= where_values_hash.merge(create_with_value)
516
+ end
517
+
518
+ # Returns true if relation needs eager loading.
519
+ def eager_loading?
520
+ @should_eager_load ||=
521
+ eager_load_values.any? ||
522
+ includes_values.any? && (joined_includes_values.any? || references_eager_loaded_tables?)
523
+ end
524
+
525
+ # Joins that are also marked for preloading. In which case we should just eager load them.
526
+ # Note that this is a naive implementation because we could have strings and symbols which
527
+ # represent the same association, but that aren't matched by this. Also, we could have
528
+ # nested hashes which partially match, e.g. { a: :b } & { a: [:b, :c] }
529
+ def joined_includes_values
530
+ includes_values & joins_values
531
+ end
532
+
533
+ # +uniq+ and +uniq!+ are silently deprecated. +uniq_value+ delegates to +distinct_value+
534
+ # to maintain backwards compatibility. Use +distinct_value+ instead.
535
+ def uniq_value
536
+ distinct_value
537
+ end
538
+
539
+ # Compares two relations for equality.
540
+ def ==(other)
541
+ case other
542
+ when Relation
543
+ other.to_sql == to_sql
544
+ when Array
545
+ to_a == other
546
+ end
547
+ end
548
+
549
+ def pretty_print(q)
550
+ q.pp(self.to_a)
551
+ end
552
+
553
+ def with_default_scope #:nodoc:
554
+ if default_scoped? && default_scope = klass.send(:build_default_scope)
555
+ default_scope = default_scope.merge(self)
556
+ default_scope.default_scoped = false
557
+ default_scope
558
+ else
559
+ self
560
+ end
561
+ end
562
+
563
+ # Returns true if relation is blank.
564
+ def blank?
565
+ to_a.blank?
566
+ end
567
+
568
+ def values
569
+ Hash[@values]
570
+ end
571
+
572
+ def inspect
573
+ entries = to_a.take([limit_value, 11].compact.min).map!(&:inspect)
574
+ entries[10] = '...' if entries.size == 11
575
+
576
+ "#<#{self.class.name} [#{entries.join(', ')}]>"
577
+ end
578
+
579
+ private
580
+
581
+ def exec_queries
582
+ default_scoped = with_default_scope
583
+
584
+ if default_scoped.equal?(self)
585
+ @records = eager_loading? ? find_with_associations : @klass.find_by_sql(arel, bind_values)
586
+
587
+ preload = preload_values
588
+ preload += includes_values unless eager_loading?
589
+ preload.each do |associations|
590
+ ActiveRecord::Associations::Preloader.new(@records, associations).run
591
+ end
592
+
593
+ # @readonly_value is true only if set explicitly. @implicit_readonly is true if there
594
+ # are JOINS and no explicit SELECT.
595
+ readonly = readonly_value.nil? ? @implicit_readonly : readonly_value
596
+ @records.each { |record| record.readonly! } if readonly
597
+ else
598
+ @records = default_scoped.to_a
599
+ end
600
+
601
+ @loaded = true
602
+ @records
603
+ end
604
+
605
+ def references_eager_loaded_tables?
606
+ joined_tables = arel.join_sources.map do |join|
607
+ if join.is_a?(Arel::Nodes::StringJoin)
608
+ tables_in_string(join.left)
609
+ else
610
+ [join.left.table_name, join.left.table_alias]
611
+ end
612
+ end
613
+
614
+ joined_tables += [table.name, table.table_alias]
615
+
616
+ # always convert table names to downcase as in Oracle quoted table names are in uppercase
617
+ joined_tables = joined_tables.flatten.compact.map { |t| t.downcase }.uniq
618
+ string_tables = tables_in_string(to_sql)
619
+
620
+ if (references_values - joined_tables).any?
621
+ true
622
+ elsif !ActiveRecord::Base.disable_implicit_join_references &&
623
+ (string_tables - joined_tables).any?
624
+ ActiveSupport::Deprecation.warn(
625
+ "It looks like you are eager loading table(s) (one of: #{string_tables.join(', ')}) " \
626
+ "that are referenced in a string SQL snippet. For example: \n" \
627
+ "\n" \
628
+ " Post.includes(:comments).where(\"comments.title = 'foo'\")\n" \
629
+ "\n" \
630
+ "Currently, Active Record recognizes the table in the string, and knows to JOIN the " \
631
+ "comments table to the query, rather than loading comments in a separate query. " \
632
+ "However, doing this without writing a full-blown SQL parser is inherently flawed. " \
633
+ "Since we don't want to write an SQL parser, we are removing this functionality. " \
634
+ "From now on, you must explicitly tell Active Record when you are referencing a table " \
635
+ "from a string:\n" \
636
+ "\n" \
637
+ " Post.includes(:comments).where(\"comments.title = 'foo'\").references(:comments)\n" \
638
+ "\n" \
639
+ "If you don't rely on implicit join references you can disable the feature entirely " \
640
+ "by setting `config.active_record.disable_implicit_join_references = true`."
641
+ )
642
+ true
643
+ else
644
+ false
645
+ end
646
+ end
647
+
648
+ def tables_in_string(string)
649
+ return [] if string.blank?
650
+ # always convert table names to downcase as in Oracle quoted table names are in uppercase
651
+ # ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries
652
+ string.scan(/([a-zA-Z_][.\w]+).?\./).flatten.map{ |s| s.downcase }.uniq - ['raw_sql_']
653
+ end
654
+ end
655
+ end