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,130 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Associations
5
+ # = Active Record Belongs To Association
6
+ class BelongsToAssociation < SingularAssociation #:nodoc:
7
+ def handle_dependency
8
+ return unless load_target
9
+
10
+ case options[:dependent]
11
+ when :destroy
12
+ target.destroy
13
+ raise ActiveRecord::Rollback unless target.destroyed?
14
+ else
15
+ target.send(options[:dependent])
16
+ end
17
+ end
18
+
19
+ def replace(record)
20
+ if record
21
+ raise_on_type_mismatch!(record)
22
+ update_counters_on_replace(record)
23
+ set_inverse_instance(record)
24
+ @updated = true
25
+ else
26
+ decrement_counters
27
+ end
28
+
29
+ replace_keys(record)
30
+
31
+ self.target = record
32
+ end
33
+
34
+ def inversed_from(record)
35
+ replace_keys(record)
36
+ super
37
+ end
38
+
39
+ def default(&block)
40
+ writer(owner.instance_exec(&block)) if reader.nil?
41
+ end
42
+
43
+ def reset
44
+ super
45
+ @updated = false
46
+ end
47
+
48
+ def updated?
49
+ @updated
50
+ end
51
+
52
+ def decrement_counters # :nodoc:
53
+ update_counters(-1)
54
+ end
55
+
56
+ def increment_counters # :nodoc:
57
+ update_counters(1)
58
+ end
59
+
60
+ def target_changed?
61
+ owner.saved_change_to_attribute?(reflection.foreign_key)
62
+ end
63
+
64
+ private
65
+
66
+ def update_counters(by)
67
+ if require_counter_update? && foreign_key_present?
68
+ if target && !stale_target?
69
+ target.increment!(reflection.counter_cache_column, by, touch: reflection.options[:touch])
70
+ else
71
+ klass.update_counters(target_id, reflection.counter_cache_column => by, touch: reflection.options[:touch])
72
+ end
73
+ end
74
+ end
75
+
76
+ def find_target?
77
+ !loaded? && foreign_key_present? && klass
78
+ end
79
+
80
+ def require_counter_update?
81
+ reflection.counter_cache_column && owner.persisted?
82
+ end
83
+
84
+ def update_counters_on_replace(record)
85
+ if require_counter_update? && different_target?(record)
86
+ owner.instance_variable_set :@_after_replace_counter_called, true
87
+ record.increment!(reflection.counter_cache_column, touch: reflection.options[:touch])
88
+ decrement_counters
89
+ end
90
+ end
91
+
92
+ # Checks whether record is different to the current target, without loading it
93
+ def different_target?(record)
94
+ record._read_attribute(primary_key(record)) != owner._read_attribute(reflection.foreign_key)
95
+ end
96
+
97
+ def replace_keys(record)
98
+ owner[reflection.foreign_key] = record ? record._read_attribute(primary_key(record)) : nil
99
+ end
100
+
101
+ def primary_key(record)
102
+ reflection.association_primary_key(record.class)
103
+ end
104
+
105
+ def foreign_key_present?
106
+ owner._read_attribute(reflection.foreign_key)
107
+ end
108
+
109
+ # NOTE - for now, we're only supporting inverse setting from belongs_to back onto
110
+ # has_one associations.
111
+ def invertible_for?(record)
112
+ inverse = inverse_reflection_for(record)
113
+ inverse && inverse.has_one?
114
+ end
115
+
116
+ def target_id
117
+ if options[:primary_key]
118
+ owner.send(reflection.name).try(:id)
119
+ else
120
+ owner._read_attribute(reflection.foreign_key)
121
+ end
122
+ end
123
+
124
+ def stale_state
125
+ result = owner._read_attribute(reflection.foreign_key) { |n| owner.send(:missing_attribute, n, caller) }
126
+ result && result.to_s
127
+ end
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Associations
5
+ # = Active Record Belongs To Polymorphic Association
6
+ class BelongsToPolymorphicAssociation < BelongsToAssociation #:nodoc:
7
+ def klass
8
+ type = owner[reflection.foreign_type]
9
+ type.presence && type.constantize
10
+ end
11
+
12
+ def target_changed?
13
+ super || owner.saved_change_to_attribute?(reflection.foreign_type)
14
+ end
15
+
16
+ private
17
+ def replace_keys(record)
18
+ super
19
+ owner[reflection.foreign_type] = record ? record.class.polymorphic_name : nil
20
+ end
21
+
22
+ def different_target?(record)
23
+ super || record.class != klass
24
+ end
25
+
26
+ def inverse_reflection_for(record)
27
+ reflection.polymorphic_inverse_of(record.class)
28
+ end
29
+
30
+ def raise_on_type_mismatch!(record)
31
+ # A polymorphic association cannot have a type mismatch, by definition
32
+ end
33
+
34
+ def stale_state
35
+ foreign_key = super
36
+ foreign_key && [foreign_key.to_s, owner[reflection.foreign_type].to_s]
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,140 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This is the parent Association class which defines the variables
4
+ # used by all associations.
5
+ #
6
+ # The hierarchy is defined as follows:
7
+ # Association
8
+ # - SingularAssociation
9
+ # - BelongsToAssociation
10
+ # - HasOneAssociation
11
+ # - CollectionAssociation
12
+ # - HasManyAssociation
13
+
14
+ module ActiveRecord::Associations::Builder # :nodoc:
15
+ class Association #:nodoc:
16
+ class << self
17
+ attr_accessor :extensions
18
+ end
19
+ self.extensions = []
20
+
21
+ VALID_OPTIONS = [:class_name, :anonymous_class, :foreign_key, :validate] # :nodoc:
22
+
23
+ def self.build(model, name, scope, options, &block)
24
+ if model.dangerous_attribute_method?(name)
25
+ raise ArgumentError, "You tried to define an association named #{name} on the model #{model.name}, but " \
26
+ "this will conflict with a method #{name} already defined by Active Record. " \
27
+ "Please choose a different association name."
28
+ end
29
+
30
+ extension = define_extensions model, name, &block
31
+ reflection = create_reflection model, name, scope, options, extension
32
+ define_accessors model, reflection
33
+ define_callbacks model, reflection
34
+ define_validations model, reflection
35
+ reflection
36
+ end
37
+
38
+ def self.create_reflection(model, name, scope, options, extension = nil)
39
+ raise ArgumentError, "association names must be a Symbol" unless name.kind_of?(Symbol)
40
+
41
+ validate_options(options)
42
+
43
+ scope = build_scope(scope, extension)
44
+
45
+ ActiveRecord::Reflection.create(macro, name, scope, options, model)
46
+ end
47
+
48
+ def self.build_scope(scope, extension)
49
+ new_scope = scope
50
+
51
+ if scope && scope.arity == 0
52
+ new_scope = proc { instance_exec(&scope) }
53
+ end
54
+
55
+ if extension
56
+ new_scope = wrap_scope new_scope, extension
57
+ end
58
+
59
+ new_scope
60
+ end
61
+
62
+ def self.wrap_scope(scope, extension)
63
+ scope
64
+ end
65
+
66
+ def self.macro
67
+ raise NotImplementedError
68
+ end
69
+
70
+ def self.valid_options(options)
71
+ VALID_OPTIONS + Association.extensions.flat_map(&:valid_options)
72
+ end
73
+
74
+ def self.validate_options(options)
75
+ options.assert_valid_keys(valid_options(options))
76
+ end
77
+
78
+ def self.define_extensions(model, name)
79
+ end
80
+
81
+ def self.define_callbacks(model, reflection)
82
+ if dependent = reflection.options[:dependent]
83
+ check_dependent_options(dependent)
84
+ add_destroy_callbacks(model, reflection)
85
+ end
86
+
87
+ Association.extensions.each do |extension|
88
+ extension.build model, reflection
89
+ end
90
+ end
91
+
92
+ # Defines the setter and getter methods for the association
93
+ # class Post < ActiveRecord::Base
94
+ # has_many :comments
95
+ # end
96
+ #
97
+ # Post.first.comments and Post.first.comments= methods are defined by this method...
98
+ def self.define_accessors(model, reflection)
99
+ mixin = model.generated_association_methods
100
+ name = reflection.name
101
+ define_readers(mixin, name)
102
+ define_writers(mixin, name)
103
+ end
104
+
105
+ def self.define_readers(mixin, name)
106
+ mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
107
+ def #{name}
108
+ association(:#{name}).reader
109
+ end
110
+ CODE
111
+ end
112
+
113
+ def self.define_writers(mixin, name)
114
+ mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
115
+ def #{name}=(value)
116
+ association(:#{name}).writer(value)
117
+ end
118
+ CODE
119
+ end
120
+
121
+ def self.define_validations(model, reflection)
122
+ # noop
123
+ end
124
+
125
+ def self.valid_dependent_options
126
+ raise NotImplementedError
127
+ end
128
+
129
+ def self.check_dependent_options(dependent)
130
+ unless valid_dependent_options.include? dependent
131
+ raise ArgumentError, "The :dependent option must be one of #{valid_dependent_options}, but is :#{dependent}"
132
+ end
133
+ end
134
+
135
+ def self.add_destroy_callbacks(model, reflection)
136
+ name = reflection.name
137
+ model.before_destroy lambda { |o| o.association(name).handle_dependency }
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,163 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord::Associations::Builder # :nodoc:
4
+ class BelongsTo < SingularAssociation #:nodoc:
5
+ def self.macro
6
+ :belongs_to
7
+ end
8
+
9
+ def self.valid_options(options)
10
+ super + [:polymorphic, :touch, :counter_cache, :optional, :default]
11
+ end
12
+
13
+ def self.valid_dependent_options
14
+ [:destroy, :delete]
15
+ end
16
+
17
+ def self.define_callbacks(model, reflection)
18
+ super
19
+ add_counter_cache_callbacks(model, reflection) if reflection.options[:counter_cache]
20
+ add_touch_callbacks(model, reflection) if reflection.options[:touch]
21
+ add_default_callbacks(model, reflection) if reflection.options[:default]
22
+ end
23
+
24
+ def self.define_accessors(mixin, reflection)
25
+ super
26
+ add_counter_cache_methods mixin
27
+ end
28
+
29
+ def self.add_counter_cache_methods(mixin)
30
+ return if mixin.method_defined? :belongs_to_counter_cache_after_update
31
+
32
+ mixin.class_eval do
33
+ def belongs_to_counter_cache_after_update(reflection)
34
+ foreign_key = reflection.foreign_key
35
+ cache_column = reflection.counter_cache_column
36
+
37
+ if (@_after_replace_counter_called ||= false)
38
+ @_after_replace_counter_called = false
39
+ elsif association(reflection.name).target_changed?
40
+ if reflection.polymorphic?
41
+ model = attribute_in_database(reflection.foreign_type).try(:constantize)
42
+ model_was = attribute_before_last_save(reflection.foreign_type).try(:constantize)
43
+ else
44
+ model = reflection.klass
45
+ model_was = reflection.klass
46
+ end
47
+
48
+ foreign_key_was = attribute_before_last_save foreign_key
49
+ foreign_key = attribute_in_database foreign_key
50
+
51
+ if foreign_key && model.respond_to?(:increment_counter)
52
+ foreign_key = counter_cache_target(reflection, model, foreign_key)
53
+ model.increment_counter(cache_column, foreign_key)
54
+ end
55
+
56
+ if foreign_key_was && model_was.respond_to?(:decrement_counter)
57
+ foreign_key_was = counter_cache_target(reflection, model_was, foreign_key_was)
58
+ model_was.decrement_counter(cache_column, foreign_key_was)
59
+ end
60
+ end
61
+ end
62
+
63
+ private
64
+ def counter_cache_target(reflection, model, foreign_key)
65
+ primary_key = reflection.association_primary_key(model)
66
+ model.unscoped.where!(primary_key => foreign_key)
67
+ end
68
+ end
69
+ end
70
+
71
+ def self.add_counter_cache_callbacks(model, reflection)
72
+ cache_column = reflection.counter_cache_column
73
+
74
+ model.after_update lambda { |record|
75
+ record.belongs_to_counter_cache_after_update(reflection)
76
+ }
77
+
78
+ klass = reflection.class_name.safe_constantize
79
+ klass.attr_readonly cache_column if klass && klass.respond_to?(:attr_readonly)
80
+ end
81
+
82
+ def self.touch_record(o, changes, foreign_key, name, touch, touch_method) # :nodoc:
83
+ old_foreign_id = changes[foreign_key] && changes[foreign_key].first
84
+
85
+ if old_foreign_id
86
+ association = o.association(name)
87
+ reflection = association.reflection
88
+ if reflection.polymorphic?
89
+ foreign_type = reflection.foreign_type
90
+ klass = changes[foreign_type] && changes[foreign_type].first || o.public_send(foreign_type)
91
+ klass = klass.constantize
92
+ else
93
+ klass = association.klass
94
+ end
95
+ primary_key = reflection.association_primary_key(klass)
96
+ old_record = klass.find_by(primary_key => old_foreign_id)
97
+
98
+ if old_record
99
+ if touch != true
100
+ old_record.send(touch_method, touch)
101
+ else
102
+ old_record.send(touch_method)
103
+ end
104
+ end
105
+ end
106
+
107
+ record = o.send name
108
+ if record && record.persisted?
109
+ if touch != true
110
+ record.send(touch_method, touch)
111
+ else
112
+ record.send(touch_method)
113
+ end
114
+ end
115
+ end
116
+
117
+ def self.add_touch_callbacks(model, reflection)
118
+ foreign_key = reflection.foreign_key
119
+ n = reflection.name
120
+ touch = reflection.options[:touch]
121
+
122
+ callback = lambda { |changes_method| lambda { |record|
123
+ BelongsTo.touch_record(record, record.send(changes_method), foreign_key, n, touch, belongs_to_touch_method)
124
+ }}
125
+
126
+ unless reflection.counter_cache_column
127
+ model.after_create callback.(:saved_changes), if: :saved_changes?
128
+ model.after_destroy callback.(:changes_to_save)
129
+ end
130
+
131
+ model.after_update callback.(:saved_changes), if: :saved_changes?
132
+ model.after_touch callback.(:changes_to_save)
133
+ end
134
+
135
+ def self.add_default_callbacks(model, reflection)
136
+ model.before_validation lambda { |o|
137
+ o.association(reflection.name).default(&reflection.options[:default])
138
+ }
139
+ end
140
+
141
+ def self.add_destroy_callbacks(model, reflection)
142
+ model.after_destroy lambda { |o| o.association(reflection.name).handle_dependency }
143
+ end
144
+
145
+ def self.define_validations(model, reflection)
146
+ if reflection.options.key?(:required)
147
+ reflection.options[:optional] = !reflection.options.delete(:required)
148
+ end
149
+
150
+ if reflection.options[:optional].nil?
151
+ required = model.belongs_to_required_by_default
152
+ else
153
+ required = !reflection.options[:optional]
154
+ end
155
+
156
+ super
157
+
158
+ if required
159
+ model.validates_presence_of reflection.name, message: :required
160
+ end
161
+ end
162
+ end
163
+ end