activerecord 4.2.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 (221) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1372 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +218 -0
  5. data/examples/performance.rb +184 -0
  6. data/examples/simple.rb +14 -0
  7. data/lib/active_record.rb +173 -0
  8. data/lib/active_record/aggregations.rb +266 -0
  9. data/lib/active_record/association_relation.rb +22 -0
  10. data/lib/active_record/associations.rb +1724 -0
  11. data/lib/active_record/associations/alias_tracker.rb +87 -0
  12. data/lib/active_record/associations/association.rb +253 -0
  13. data/lib/active_record/associations/association_scope.rb +194 -0
  14. data/lib/active_record/associations/belongs_to_association.rb +111 -0
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +40 -0
  16. data/lib/active_record/associations/builder/association.rb +149 -0
  17. data/lib/active_record/associations/builder/belongs_to.rb +116 -0
  18. data/lib/active_record/associations/builder/collection_association.rb +91 -0
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +124 -0
  20. data/lib/active_record/associations/builder/has_many.rb +15 -0
  21. data/lib/active_record/associations/builder/has_one.rb +23 -0
  22. data/lib/active_record/associations/builder/singular_association.rb +38 -0
  23. data/lib/active_record/associations/collection_association.rb +634 -0
  24. data/lib/active_record/associations/collection_proxy.rb +1027 -0
  25. data/lib/active_record/associations/has_many_association.rb +184 -0
  26. data/lib/active_record/associations/has_many_through_association.rb +238 -0
  27. data/lib/active_record/associations/has_one_association.rb +105 -0
  28. data/lib/active_record/associations/has_one_through_association.rb +36 -0
  29. data/lib/active_record/associations/join_dependency.rb +282 -0
  30. data/lib/active_record/associations/join_dependency/join_association.rb +122 -0
  31. data/lib/active_record/associations/join_dependency/join_base.rb +22 -0
  32. data/lib/active_record/associations/join_dependency/join_part.rb +71 -0
  33. data/lib/active_record/associations/preloader.rb +203 -0
  34. data/lib/active_record/associations/preloader/association.rb +162 -0
  35. data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
  36. data/lib/active_record/associations/preloader/collection_association.rb +24 -0
  37. data/lib/active_record/associations/preloader/has_many.rb +17 -0
  38. data/lib/active_record/associations/preloader/has_many_through.rb +19 -0
  39. data/lib/active_record/associations/preloader/has_one.rb +23 -0
  40. data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
  41. data/lib/active_record/associations/preloader/singular_association.rb +21 -0
  42. data/lib/active_record/associations/preloader/through_association.rb +96 -0
  43. data/lib/active_record/associations/singular_association.rb +86 -0
  44. data/lib/active_record/associations/through_association.rb +96 -0
  45. data/lib/active_record/attribute.rb +149 -0
  46. data/lib/active_record/attribute_assignment.rb +212 -0
  47. data/lib/active_record/attribute_decorators.rb +66 -0
  48. data/lib/active_record/attribute_methods.rb +439 -0
  49. data/lib/active_record/attribute_methods/before_type_cast.rb +71 -0
  50. data/lib/active_record/attribute_methods/dirty.rb +181 -0
  51. data/lib/active_record/attribute_methods/primary_key.rb +128 -0
  52. data/lib/active_record/attribute_methods/query.rb +40 -0
  53. data/lib/active_record/attribute_methods/read.rb +103 -0
  54. data/lib/active_record/attribute_methods/serialization.rb +70 -0
  55. data/lib/active_record/attribute_methods/time_zone_conversion.rb +65 -0
  56. data/lib/active_record/attribute_methods/write.rb +83 -0
  57. data/lib/active_record/attribute_set.rb +77 -0
  58. data/lib/active_record/attribute_set/builder.rb +86 -0
  59. data/lib/active_record/attributes.rb +139 -0
  60. data/lib/active_record/autosave_association.rb +439 -0
  61. data/lib/active_record/base.rb +317 -0
  62. data/lib/active_record/callbacks.rb +313 -0
  63. data/lib/active_record/coders/json.rb +13 -0
  64. data/lib/active_record/coders/yaml_column.rb +38 -0
  65. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +659 -0
  66. data/lib/active_record/connection_adapters/abstract/database_limits.rb +67 -0
  67. data/lib/active_record/connection_adapters/abstract/database_statements.rb +373 -0
  68. data/lib/active_record/connection_adapters/abstract/query_cache.rb +95 -0
  69. data/lib/active_record/connection_adapters/abstract/quoting.rb +133 -0
  70. data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
  71. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +125 -0
  72. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +574 -0
  73. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +50 -0
  74. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +991 -0
  75. data/lib/active_record/connection_adapters/abstract/transaction.rb +219 -0
  76. data/lib/active_record/connection_adapters/abstract_adapter.rb +487 -0
  77. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +883 -0
  78. data/lib/active_record/connection_adapters/column.rb +82 -0
  79. data/lib/active_record/connection_adapters/connection_specification.rb +275 -0
  80. data/lib/active_record/connection_adapters/mysql2_adapter.rb +282 -0
  81. data/lib/active_record/connection_adapters/mysql_adapter.rb +491 -0
  82. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +93 -0
  83. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  84. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +232 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid.rb +36 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +99 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +14 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +27 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +97 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  109. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  110. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  111. data/lib/active_record/connection_adapters/postgresql/quoting.rb +108 -0
  112. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
  114. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +588 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +754 -0
  117. data/lib/active_record/connection_adapters/schema_cache.rb +94 -0
  118. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +628 -0
  119. data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
  120. data/lib/active_record/connection_handling.rb +132 -0
  121. data/lib/active_record/core.rb +566 -0
  122. data/lib/active_record/counter_cache.rb +175 -0
  123. data/lib/active_record/dynamic_matchers.rb +140 -0
  124. data/lib/active_record/enum.rb +198 -0
  125. data/lib/active_record/errors.rb +252 -0
  126. data/lib/active_record/explain.rb +38 -0
  127. data/lib/active_record/explain_registry.rb +30 -0
  128. data/lib/active_record/explain_subscriber.rb +29 -0
  129. data/lib/active_record/fixture_set/file.rb +56 -0
  130. data/lib/active_record/fixtures.rb +1007 -0
  131. data/lib/active_record/gem_version.rb +15 -0
  132. data/lib/active_record/inheritance.rb +247 -0
  133. data/lib/active_record/integration.rb +113 -0
  134. data/lib/active_record/locale/en.yml +47 -0
  135. data/lib/active_record/locking/optimistic.rb +204 -0
  136. data/lib/active_record/locking/pessimistic.rb +77 -0
  137. data/lib/active_record/log_subscriber.rb +75 -0
  138. data/lib/active_record/migration.rb +1051 -0
  139. data/lib/active_record/migration/command_recorder.rb +197 -0
  140. data/lib/active_record/migration/join_table.rb +15 -0
  141. data/lib/active_record/model_schema.rb +340 -0
  142. data/lib/active_record/nested_attributes.rb +548 -0
  143. data/lib/active_record/no_touching.rb +52 -0
  144. data/lib/active_record/null_relation.rb +81 -0
  145. data/lib/active_record/persistence.rb +532 -0
  146. data/lib/active_record/query_cache.rb +56 -0
  147. data/lib/active_record/querying.rb +68 -0
  148. data/lib/active_record/railtie.rb +162 -0
  149. data/lib/active_record/railties/console_sandbox.rb +5 -0
  150. data/lib/active_record/railties/controller_runtime.rb +50 -0
  151. data/lib/active_record/railties/databases.rake +391 -0
  152. data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
  153. data/lib/active_record/readonly_attributes.rb +23 -0
  154. data/lib/active_record/reflection.rb +881 -0
  155. data/lib/active_record/relation.rb +681 -0
  156. data/lib/active_record/relation/batches.rb +138 -0
  157. data/lib/active_record/relation/calculations.rb +403 -0
  158. data/lib/active_record/relation/delegation.rb +140 -0
  159. data/lib/active_record/relation/finder_methods.rb +528 -0
  160. data/lib/active_record/relation/merger.rb +170 -0
  161. data/lib/active_record/relation/predicate_builder.rb +126 -0
  162. data/lib/active_record/relation/predicate_builder/array_handler.rb +47 -0
  163. data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
  164. data/lib/active_record/relation/query_methods.rb +1176 -0
  165. data/lib/active_record/relation/spawn_methods.rb +75 -0
  166. data/lib/active_record/result.rb +131 -0
  167. data/lib/active_record/runtime_registry.rb +22 -0
  168. data/lib/active_record/sanitization.rb +191 -0
  169. data/lib/active_record/schema.rb +64 -0
  170. data/lib/active_record/schema_dumper.rb +251 -0
  171. data/lib/active_record/schema_migration.rb +56 -0
  172. data/lib/active_record/scoping.rb +87 -0
  173. data/lib/active_record/scoping/default.rb +134 -0
  174. data/lib/active_record/scoping/named.rb +164 -0
  175. data/lib/active_record/serialization.rb +22 -0
  176. data/lib/active_record/serializers/xml_serializer.rb +193 -0
  177. data/lib/active_record/statement_cache.rb +111 -0
  178. data/lib/active_record/store.rb +205 -0
  179. data/lib/active_record/tasks/database_tasks.rb +296 -0
  180. data/lib/active_record/tasks/mysql_database_tasks.rb +145 -0
  181. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  182. data/lib/active_record/tasks/sqlite_database_tasks.rb +55 -0
  183. data/lib/active_record/timestamp.rb +121 -0
  184. data/lib/active_record/transactions.rb +417 -0
  185. data/lib/active_record/translation.rb +22 -0
  186. data/lib/active_record/type.rb +23 -0
  187. data/lib/active_record/type/big_integer.rb +13 -0
  188. data/lib/active_record/type/binary.rb +50 -0
  189. data/lib/active_record/type/boolean.rb +30 -0
  190. data/lib/active_record/type/date.rb +46 -0
  191. data/lib/active_record/type/date_time.rb +43 -0
  192. data/lib/active_record/type/decimal.rb +40 -0
  193. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  194. data/lib/active_record/type/decorator.rb +14 -0
  195. data/lib/active_record/type/float.rb +19 -0
  196. data/lib/active_record/type/hash_lookup_type_map.rb +17 -0
  197. data/lib/active_record/type/integer.rb +55 -0
  198. data/lib/active_record/type/mutable.rb +16 -0
  199. data/lib/active_record/type/numeric.rb +36 -0
  200. data/lib/active_record/type/serialized.rb +56 -0
  201. data/lib/active_record/type/string.rb +36 -0
  202. data/lib/active_record/type/text.rb +11 -0
  203. data/lib/active_record/type/time.rb +26 -0
  204. data/lib/active_record/type/time_value.rb +38 -0
  205. data/lib/active_record/type/type_map.rb +64 -0
  206. data/lib/active_record/type/unsigned_integer.rb +15 -0
  207. data/lib/active_record/type/value.rb +101 -0
  208. data/lib/active_record/validations.rb +90 -0
  209. data/lib/active_record/validations/associated.rb +51 -0
  210. data/lib/active_record/validations/presence.rb +67 -0
  211. data/lib/active_record/validations/uniqueness.rb +229 -0
  212. data/lib/active_record/version.rb +8 -0
  213. data/lib/rails/generators/active_record.rb +17 -0
  214. data/lib/rails/generators/active_record/migration.rb +18 -0
  215. data/lib/rails/generators/active_record/migration/migration_generator.rb +70 -0
  216. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +22 -0
  217. data/lib/rails/generators/active_record/migration/templates/migration.rb +45 -0
  218. data/lib/rails/generators/active_record/model/model_generator.rb +52 -0
  219. data/lib/rails/generators/active_record/model/templates/model.rb +10 -0
  220. data/lib/rails/generators/active_record/model/templates/module.rb +7 -0
  221. metadata +309 -0
@@ -0,0 +1,71 @@
1
+ module ActiveRecord
2
+ module AttributeMethods
3
+ # = Active Record Attribute Methods Before Type Cast
4
+ #
5
+ # <tt>ActiveRecord::AttributeMethods::BeforeTypeCast</tt> provides a way to
6
+ # read the value of the attributes before typecasting and deserialization.
7
+ #
8
+ # class Task < ActiveRecord::Base
9
+ # end
10
+ #
11
+ # task = Task.new(id: '1', completed_on: '2012-10-21')
12
+ # task.id # => 1
13
+ # task.completed_on # => Sun, 21 Oct 2012
14
+ #
15
+ # task.attributes_before_type_cast
16
+ # # => {"id"=>"1", "completed_on"=>"2012-10-21", ... }
17
+ # task.read_attribute_before_type_cast('id') # => "1"
18
+ # task.read_attribute_before_type_cast('completed_on') # => "2012-10-21"
19
+ #
20
+ # In addition to #read_attribute_before_type_cast and #attributes_before_type_cast,
21
+ # it declares a method for all attributes with the <tt>*_before_type_cast</tt>
22
+ # suffix.
23
+ #
24
+ # task.id_before_type_cast # => "1"
25
+ # task.completed_on_before_type_cast # => "2012-10-21"
26
+ module BeforeTypeCast
27
+ extend ActiveSupport::Concern
28
+
29
+ included do
30
+ attribute_method_suffix "_before_type_cast"
31
+ end
32
+
33
+ # Returns the value of the attribute identified by +attr_name+ before
34
+ # typecasting and deserialization.
35
+ #
36
+ # class Task < ActiveRecord::Base
37
+ # end
38
+ #
39
+ # task = Task.new(id: '1', completed_on: '2012-10-21')
40
+ # task.read_attribute('id') # => 1
41
+ # task.read_attribute_before_type_cast('id') # => '1'
42
+ # task.read_attribute('completed_on') # => Sun, 21 Oct 2012
43
+ # task.read_attribute_before_type_cast('completed_on') # => "2012-10-21"
44
+ # task.read_attribute_before_type_cast(:completed_on) # => "2012-10-21"
45
+ def read_attribute_before_type_cast(attr_name)
46
+ @attributes[attr_name.to_s].value_before_type_cast
47
+ end
48
+
49
+ # Returns a hash of attributes before typecasting and deserialization.
50
+ #
51
+ # class Task < ActiveRecord::Base
52
+ # end
53
+ #
54
+ # task = Task.new(title: nil, is_done: true, completed_on: '2012-10-21')
55
+ # task.attributes
56
+ # # => {"id"=>nil, "title"=>nil, "is_done"=>true, "completed_on"=>Sun, 21 Oct 2012, "created_at"=>nil, "updated_at"=>nil}
57
+ # task.attributes_before_type_cast
58
+ # # => {"id"=>nil, "title"=>nil, "is_done"=>true, "completed_on"=>"2012-10-21", "created_at"=>nil, "updated_at"=>nil}
59
+ def attributes_before_type_cast
60
+ @attributes.values_before_type_cast
61
+ end
62
+
63
+ private
64
+
65
+ # Handle *_before_type_cast for method_missing.
66
+ def attribute_before_type_cast(attribute_name)
67
+ read_attribute_before_type_cast(attribute_name)
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,181 @@
1
+ require 'active_support/core_ext/module/attribute_accessors'
2
+
3
+ module ActiveRecord
4
+ module AttributeMethods
5
+ module Dirty # :nodoc:
6
+ extend ActiveSupport::Concern
7
+
8
+ include ActiveModel::Dirty
9
+
10
+ included do
11
+ if self < ::ActiveRecord::Timestamp
12
+ raise "You cannot include Dirty after Timestamp"
13
+ end
14
+
15
+ class_attribute :partial_writes, instance_writer: false
16
+ self.partial_writes = true
17
+ end
18
+
19
+ # Attempts to +save+ the record and clears changed attributes if successful.
20
+ def save(*)
21
+ if status = super
22
+ changes_applied
23
+ end
24
+ status
25
+ end
26
+
27
+ # Attempts to <tt>save!</tt> the record and clears changed attributes if successful.
28
+ def save!(*)
29
+ super.tap do
30
+ changes_applied
31
+ end
32
+ end
33
+
34
+ # <tt>reload</tt> the record and clears changed attributes.
35
+ def reload(*)
36
+ super.tap do
37
+ clear_changes_information
38
+ end
39
+ end
40
+
41
+ def initialize_dup(other) # :nodoc:
42
+ super
43
+ calculate_changes_from_defaults
44
+ end
45
+
46
+ def changes_applied
47
+ super
48
+ store_original_raw_attributes
49
+ end
50
+
51
+ def clear_changes_information
52
+ super
53
+ original_raw_attributes.clear
54
+ end
55
+
56
+ def changed_attributes
57
+ # This should only be set by methods which will call changed_attributes
58
+ # multiple times when it is known that the computed value cannot change.
59
+ if defined?(@cached_changed_attributes)
60
+ @cached_changed_attributes
61
+ else
62
+ super.reverse_merge(attributes_changed_in_place).freeze
63
+ end
64
+ end
65
+
66
+ def changes
67
+ cache_changed_attributes do
68
+ super
69
+ end
70
+ end
71
+
72
+ def attribute_changed_in_place?(attr_name)
73
+ old_value = original_raw_attribute(attr_name)
74
+ @attributes[attr_name].changed_in_place_from?(old_value)
75
+ end
76
+
77
+ private
78
+
79
+ def calculate_changes_from_defaults
80
+ @changed_attributes = nil
81
+ self.class.column_defaults.each do |attr, orig_value|
82
+ set_attribute_was(attr, orig_value) if _field_changed?(attr, orig_value)
83
+ end
84
+ end
85
+
86
+ # Wrap write_attribute to remember original attribute value.
87
+ def write_attribute(attr, value)
88
+ attr = attr.to_s
89
+
90
+ old_value = old_attribute_value(attr)
91
+
92
+ result = super
93
+ store_original_raw_attribute(attr)
94
+ save_changed_attribute(attr, old_value)
95
+ result
96
+ end
97
+
98
+ def raw_write_attribute(attr, value)
99
+ attr = attr.to_s
100
+
101
+ result = super
102
+ original_raw_attributes[attr] = value
103
+ result
104
+ end
105
+
106
+ def save_changed_attribute(attr, old_value)
107
+ if attribute_changed?(attr)
108
+ clear_attribute_changes(attr) unless _field_changed?(attr, old_value)
109
+ else
110
+ set_attribute_was(attr, old_value) if _field_changed?(attr, old_value)
111
+ end
112
+ end
113
+
114
+ def old_attribute_value(attr)
115
+ if attribute_changed?(attr)
116
+ changed_attributes[attr]
117
+ else
118
+ clone_attribute_value(:_read_attribute, attr)
119
+ end
120
+ end
121
+
122
+ def _update_record(*)
123
+ partial_writes? ? super(keys_for_partial_write) : super
124
+ end
125
+
126
+ def _create_record(*)
127
+ partial_writes? ? super(keys_for_partial_write) : super
128
+ end
129
+
130
+ # Serialized attributes should always be written in case they've been
131
+ # changed in place.
132
+ def keys_for_partial_write
133
+ changed
134
+ end
135
+
136
+ def _field_changed?(attr, old_value)
137
+ @attributes[attr].changed_from?(old_value)
138
+ end
139
+
140
+ def attributes_changed_in_place
141
+ changed_in_place.each_with_object({}) do |attr_name, h|
142
+ orig = @attributes[attr_name].original_value
143
+ h[attr_name] = orig
144
+ end
145
+ end
146
+
147
+ def changed_in_place
148
+ self.class.attribute_names.select do |attr_name|
149
+ attribute_changed_in_place?(attr_name)
150
+ end
151
+ end
152
+
153
+ def original_raw_attribute(attr_name)
154
+ original_raw_attributes.fetch(attr_name) do
155
+ read_attribute_before_type_cast(attr_name)
156
+ end
157
+ end
158
+
159
+ def original_raw_attributes
160
+ @original_raw_attributes ||= {}
161
+ end
162
+
163
+ def store_original_raw_attribute(attr_name)
164
+ original_raw_attributes[attr_name] = @attributes[attr_name].value_for_database
165
+ end
166
+
167
+ def store_original_raw_attributes
168
+ attribute_names.each do |attr|
169
+ store_original_raw_attribute(attr)
170
+ end
171
+ end
172
+
173
+ def cache_changed_attributes
174
+ @cached_changed_attributes = changed_attributes
175
+ yield
176
+ ensure
177
+ remove_instance_variable(:@cached_changed_attributes)
178
+ end
179
+ end
180
+ end
181
+ end
@@ -0,0 +1,128 @@
1
+ require 'set'
2
+
3
+ module ActiveRecord
4
+ module AttributeMethods
5
+ module PrimaryKey
6
+ extend ActiveSupport::Concern
7
+
8
+ # Returns this record's primary key value wrapped in an Array if one is
9
+ # available.
10
+ def to_key
11
+ sync_with_transaction_state
12
+ key = self.id
13
+ [key] if key
14
+ end
15
+
16
+ # Returns the primary key value.
17
+ def id
18
+ if pk = self.class.primary_key
19
+ sync_with_transaction_state
20
+ _read_attribute(pk)
21
+ end
22
+ end
23
+
24
+ # Sets the primary key value.
25
+ def id=(value)
26
+ sync_with_transaction_state
27
+ write_attribute(self.class.primary_key, value) if self.class.primary_key
28
+ end
29
+
30
+ # Queries the primary key value.
31
+ def id?
32
+ sync_with_transaction_state
33
+ query_attribute(self.class.primary_key)
34
+ end
35
+
36
+ # Returns the primary key value before type cast.
37
+ def id_before_type_cast
38
+ sync_with_transaction_state
39
+ read_attribute_before_type_cast(self.class.primary_key)
40
+ end
41
+
42
+ # Returns the primary key previous value.
43
+ def id_was
44
+ sync_with_transaction_state
45
+ attribute_was(self.class.primary_key)
46
+ end
47
+
48
+ protected
49
+
50
+ def attribute_method?(attr_name)
51
+ attr_name == 'id' || super
52
+ end
53
+
54
+ module ClassMethods
55
+ def define_method_attribute(attr_name)
56
+ super
57
+
58
+ if attr_name == primary_key && attr_name != 'id'
59
+ generated_attribute_methods.send(:alias_method, :id, primary_key)
60
+ end
61
+ end
62
+
63
+ ID_ATTRIBUTE_METHODS = %w(id id= id? id_before_type_cast id_was).to_set
64
+
65
+ def dangerous_attribute_method?(method_name)
66
+ super && !ID_ATTRIBUTE_METHODS.include?(method_name)
67
+ end
68
+
69
+ # Defines the primary key field -- can be overridden in subclasses.
70
+ # Overwriting will negate any effect of the +primary_key_prefix_type+
71
+ # setting, though.
72
+ def primary_key
73
+ @primary_key = reset_primary_key unless defined? @primary_key
74
+ @primary_key
75
+ end
76
+
77
+ # Returns a quoted version of the primary key name, used to construct
78
+ # SQL statements.
79
+ def quoted_primary_key
80
+ @quoted_primary_key ||= connection.quote_column_name(primary_key)
81
+ end
82
+
83
+ def reset_primary_key #:nodoc:
84
+ if self == base_class
85
+ self.primary_key = get_primary_key(base_class.name)
86
+ else
87
+ self.primary_key = base_class.primary_key
88
+ end
89
+ end
90
+
91
+ def get_primary_key(base_name) #:nodoc:
92
+ if base_name && primary_key_prefix_type == :table_name
93
+ base_name.foreign_key(false)
94
+ elsif base_name && primary_key_prefix_type == :table_name_with_underscore
95
+ base_name.foreign_key
96
+ else
97
+ if ActiveRecord::Base != self && table_exists?
98
+ connection.schema_cache.primary_keys(table_name)
99
+ else
100
+ 'id'
101
+ end
102
+ end
103
+ end
104
+
105
+ # Sets the name of the primary key column.
106
+ #
107
+ # class Project < ActiveRecord::Base
108
+ # self.primary_key = 'sysid'
109
+ # end
110
+ #
111
+ # You can also define the +primary_key+ method yourself:
112
+ #
113
+ # class Project < ActiveRecord::Base
114
+ # def self.primary_key
115
+ # 'foo_' + super
116
+ # end
117
+ # end
118
+ #
119
+ # Project.primary_key # => "foo_id"
120
+ def primary_key=(value)
121
+ @primary_key = value && value.to_s
122
+ @quoted_primary_key = nil
123
+ @attributes_builder = nil
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,40 @@
1
+ module ActiveRecord
2
+ module AttributeMethods
3
+ module Query
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ attribute_method_suffix "?"
8
+ end
9
+
10
+ def query_attribute(attr_name)
11
+ value = self[attr_name]
12
+
13
+ case value
14
+ when true then true
15
+ when false, nil then false
16
+ else
17
+ column = self.class.columns_hash[attr_name]
18
+ if column.nil?
19
+ if Numeric === value || value !~ /[^0-9]/
20
+ !value.to_i.zero?
21
+ else
22
+ return false if ActiveRecord::ConnectionAdapters::Column::FALSE_VALUES.include?(value)
23
+ !value.blank?
24
+ end
25
+ elsif column.number?
26
+ !value.zero?
27
+ else
28
+ !value.blank?
29
+ end
30
+ end
31
+ end
32
+
33
+ private
34
+ # Handle *? for method_missing.
35
+ def attribute?(attribute_name)
36
+ query_attribute(attribute_name)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,103 @@
1
+ require 'active_support/core_ext/module/method_transplanting'
2
+
3
+ module ActiveRecord
4
+ module AttributeMethods
5
+ module Read
6
+ ReaderMethodCache = Class.new(AttributeMethodCache) {
7
+ private
8
+ # We want to generate the methods via module_eval rather than
9
+ # define_method, because define_method is slower on dispatch.
10
+ # Evaluating many similar methods may use more memory as the instruction
11
+ # sequences are duplicated and cached (in MRI). define_method may
12
+ # be slower on dispatch, but if you're careful about the closure
13
+ # created, then define_method will consume much less memory.
14
+ #
15
+ # But sometimes the database might return columns with
16
+ # characters that are not allowed in normal method names (like
17
+ # 'my_column(omg)'. So to work around this we first define with
18
+ # the __temp__ identifier, and then use alias method to rename
19
+ # it to what we want.
20
+ #
21
+ # We are also defining a constant to hold the frozen string of
22
+ # the attribute name. Using a constant means that we do not have
23
+ # to allocate an object on each call to the attribute method.
24
+ # Making it frozen means that it doesn't get duped when used to
25
+ # key the @attributes in read_attribute.
26
+ def method_body(method_name, const_name)
27
+ <<-EOMETHOD
28
+ def #{method_name}
29
+ name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{const_name}
30
+ _read_attribute(name) { |n| missing_attribute(n, caller) }
31
+ end
32
+ EOMETHOD
33
+ end
34
+ }.new
35
+
36
+ extend ActiveSupport::Concern
37
+
38
+ module ClassMethods
39
+ [:cache_attributes, :cached_attributes, :cache_attribute?].each do |method_name|
40
+ define_method method_name do |*|
41
+ cached_attributes_deprecation_warning(method_name)
42
+ true
43
+ end
44
+ end
45
+
46
+ protected
47
+
48
+ def cached_attributes_deprecation_warning(method_name)
49
+ ActiveSupport::Deprecation.warn "Calling `#{method_name}` is no longer necessary. All attributes are cached."
50
+ end
51
+
52
+ if Module.methods_transplantable?
53
+ def define_method_attribute(name)
54
+ method = ReaderMethodCache[name]
55
+ generated_attribute_methods.module_eval { define_method name, method }
56
+ end
57
+ else
58
+ def define_method_attribute(name)
59
+ safe_name = name.unpack('h*').first
60
+ temp_method = "__temp__#{safe_name}"
61
+
62
+ ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
63
+
64
+ generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
65
+ def #{temp_method}
66
+ name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{safe_name}
67
+ _read_attribute(name) { |n| missing_attribute(n, caller) }
68
+ end
69
+ STR
70
+
71
+ generated_attribute_methods.module_eval do
72
+ alias_method name, temp_method
73
+ undef_method temp_method
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ ID = 'id'.freeze
80
+
81
+ # Returns the value of the attribute identified by <tt>attr_name</tt> after
82
+ # it has been typecast (for example, "2004-12-12" in a date column is cast
83
+ # to a date object, like Date.new(2004, 12, 12)).
84
+ def read_attribute(attr_name, &block)
85
+ name = attr_name.to_s
86
+ name = self.class.primary_key if name == ID
87
+ _read_attribute(name, &block)
88
+ end
89
+
90
+ # This method exists to avoid the expensive primary_key check internally, without
91
+ # breaking compatibility with the read_attribute API
92
+ def _read_attribute(attr_name) # :nodoc:
93
+ @attributes.fetch_value(attr_name.to_s) { |n| yield n if block_given? }
94
+ end
95
+
96
+ private
97
+
98
+ def attribute(attribute_name)
99
+ _read_attribute(attribute_name)
100
+ end
101
+ end
102
+ end
103
+ end