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,200 @@
1
+ require 'active_support/core_ext/hash/indifferent_access'
2
+
3
+ module ActiveRecord
4
+ module Inheritance
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ # Determine whether to store the full constant name including namespace when using STI
9
+ class_attribute :store_full_sti_class, instance_writer: false
10
+ self.store_full_sti_class = true
11
+ end
12
+
13
+ module ClassMethods
14
+ # Determines if one of the attributes passed in is the inheritance column,
15
+ # and if the inheritance column is attr accessible, it initializes an
16
+ # instance of the given subclass instead of the base class
17
+ def new(*args, &block)
18
+ if abstract_class? || self == Base
19
+ raise NotImplementedError, "#{self} is an abstract class and can not be instantiated."
20
+ end
21
+ if (attrs = args.first).is_a?(Hash)
22
+ if subclass = subclass_from_attrs(attrs)
23
+ return subclass.new(*args, &block)
24
+ end
25
+ end
26
+ # Delegate to the original .new
27
+ super
28
+ end
29
+
30
+ # True if this isn't a concrete subclass needing a STI type condition.
31
+ def descends_from_active_record?
32
+ if self == Base
33
+ false
34
+ elsif superclass.abstract_class?
35
+ superclass.descends_from_active_record?
36
+ else
37
+ superclass == Base || !columns_hash.include?(inheritance_column)
38
+ end
39
+ end
40
+
41
+ def finder_needs_type_condition? #:nodoc:
42
+ # This is like this because benchmarking justifies the strange :false stuff
43
+ :true == (@finder_needs_type_condition ||= descends_from_active_record? ? :false : :true)
44
+ end
45
+
46
+ def symbolized_base_class
47
+ @symbolized_base_class ||= base_class.to_s.to_sym
48
+ end
49
+
50
+ def symbolized_sti_name
51
+ @symbolized_sti_name ||= sti_name.present? ? sti_name.to_sym : symbolized_base_class
52
+ end
53
+
54
+ # Returns the class descending directly from ActiveRecord::Base, or
55
+ # an abstract class, if any, in the inheritance hierarchy.
56
+ #
57
+ # If A extends AR::Base, A.base_class will return A. If B descends from A
58
+ # through some arbitrarily deep hierarchy, B.base_class will return A.
59
+ #
60
+ # If B < A and C < B and if A is an abstract_class then both B.base_class
61
+ # and C.base_class would return B as the answer since A is an abstract_class.
62
+ def base_class
63
+ unless self < Base
64
+ raise ActiveRecordError, "#{name} doesn't belong in a hierarchy descending from ActiveRecord"
65
+ end
66
+
67
+ if superclass == Base || superclass.abstract_class?
68
+ self
69
+ else
70
+ superclass.base_class
71
+ end
72
+ end
73
+
74
+ # Set this to true if this is an abstract class (see <tt>abstract_class?</tt>).
75
+ # If you are using inheritance with ActiveRecord and don't want child classes
76
+ # to utilize the implied STI table name of the parent class, this will need to be true.
77
+ # For example, given the following:
78
+ #
79
+ # class SuperClass < ActiveRecord::Base
80
+ # self.abstract_class = true
81
+ # end
82
+ # class Child < SuperClass
83
+ # self.table_name = 'the_table_i_really_want'
84
+ # end
85
+ #
86
+ #
87
+ # <tt>self.abstract_class = true</tt> is required to make <tt>Child<.find,.create, or any Arel method></tt> use <tt>the_table_i_really_want</tt> instead of a table called <tt>super_classes</tt>
88
+ #
89
+ attr_accessor :abstract_class
90
+
91
+ # Returns whether this class is an abstract class or not.
92
+ def abstract_class?
93
+ defined?(@abstract_class) && @abstract_class == true
94
+ end
95
+
96
+ def sti_name
97
+ store_full_sti_class ? name : name.demodulize
98
+ end
99
+
100
+ protected
101
+
102
+ # Returns the class type of the record using the current module as a prefix. So descendants of
103
+ # MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
104
+ def compute_type(type_name)
105
+ if type_name.match(/^::/)
106
+ # If the type is prefixed with a scope operator then we assume that
107
+ # the type_name is an absolute reference.
108
+ ActiveSupport::Dependencies.constantize(type_name)
109
+ else
110
+ # Build a list of candidates to search for
111
+ candidates = []
112
+ name.scan(/::|$/) { candidates.unshift "#{$`}::#{type_name}" }
113
+ candidates << type_name
114
+
115
+ candidates.each do |candidate|
116
+ begin
117
+ constant = ActiveSupport::Dependencies.constantize(candidate)
118
+ return constant if candidate == constant.to_s
119
+ rescue NameError => e
120
+ # We don't want to swallow NoMethodError < NameError errors
121
+ raise e unless e.instance_of?(NameError)
122
+ end
123
+ end
124
+
125
+ raise NameError, "uninitialized constant #{candidates.first}"
126
+ end
127
+ end
128
+
129
+ private
130
+
131
+ # Called by +instantiate+ to decide which class to use for a new
132
+ # record instance. For single-table inheritance, we check the record
133
+ # for a +type+ column and return the corresponding class.
134
+ def discriminate_class_for_record(record)
135
+ if using_single_table_inheritance?(record)
136
+ find_sti_class(record[inheritance_column])
137
+ else
138
+ super
139
+ end
140
+ end
141
+
142
+ def using_single_table_inheritance?(record)
143
+ record[inheritance_column].present? && columns_hash.include?(inheritance_column)
144
+ end
145
+
146
+ def find_sti_class(type_name)
147
+ if store_full_sti_class
148
+ ActiveSupport::Dependencies.constantize(type_name)
149
+ else
150
+ compute_type(type_name)
151
+ end
152
+ rescue NameError
153
+ raise SubclassNotFound,
154
+ "The single-table inheritance mechanism failed to locate the subclass: '#{type_name}'. " +
155
+ "This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. " +
156
+ "Please rename this column if you didn't intend it to be used for storing the inheritance class " +
157
+ "or overwrite #{name}.inheritance_column to use another column for that information."
158
+ end
159
+
160
+ def type_condition(table = arel_table)
161
+ sti_column = table[inheritance_column.to_sym]
162
+ sti_names = ([self] + descendants).map { |model| model.sti_name }
163
+
164
+ sti_column.in(sti_names)
165
+ end
166
+
167
+ # Detect the subclass from the inheritance column of attrs. If the inheritance column value
168
+ # is not self or a valid subclass, raises ActiveRecord::SubclassNotFound
169
+ # If this is a StrongParameters hash, and access to inheritance_column is not permitted,
170
+ # this will ignore the inheritance column and return nil
171
+ def subclass_from_attrs(attrs)
172
+ subclass_name = attrs.with_indifferent_access[inheritance_column]
173
+
174
+ if subclass_name.present? && subclass_name != self.name
175
+ subclass = subclass_name.safe_constantize
176
+
177
+ unless descendants.include?(subclass)
178
+ raise ActiveRecord::SubclassNotFound.new("Invalid single-table inheritance type: #{subclass_name} is not a subclass of #{name}")
179
+ end
180
+
181
+ subclass
182
+ end
183
+ end
184
+ end
185
+
186
+ private
187
+
188
+ # Sets the attribute used for single table inheritance to this class name if this is not the
189
+ # ActiveRecord::Base descendant.
190
+ # Considering the hierarchy Reply < Message < ActiveRecord::Base, this makes it possible to
191
+ # do Reply.new without having to set <tt>Reply[Reply.inheritance_column] = "Reply"</tt> yourself.
192
+ # No such attribute would be set for objects of the Message class in that example.
193
+ def ensure_proper_type
194
+ klass = self.class
195
+ if klass.finder_needs_type_condition?
196
+ write_attribute(klass.inheritance_column, klass.sti_name)
197
+ end
198
+ end
199
+ end
200
+ end
@@ -0,0 +1,60 @@
1
+ module ActiveRecord
2
+ module Integration
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ ##
7
+ # :singleton-method:
8
+ # Indicates the format used to generate the timestamp in the cache key.
9
+ # Accepts any of the symbols in <tt>Time::DATE_FORMATS</tt>.
10
+ #
11
+ # This is +:nsec+, by default.
12
+ class_attribute :cache_timestamp_format, :instance_writer => false
13
+ self.cache_timestamp_format = :nsec
14
+ end
15
+
16
+ # Returns a String, which Action Pack uses for constructing an URL to this
17
+ # object. The default implementation returns this record's id as a String,
18
+ # or nil if this record's unsaved.
19
+ #
20
+ # For example, suppose that you have a User model, and that you have a
21
+ # <tt>resources :users</tt> route. Normally, +user_path+ will
22
+ # construct a path with the user object's 'id' in it:
23
+ #
24
+ # user = User.find_by(name: 'Phusion')
25
+ # user_path(user) # => "/users/1"
26
+ #
27
+ # You can override +to_param+ in your model to make +user_path+ construct
28
+ # a path using the user's name instead of the user's id:
29
+ #
30
+ # class User < ActiveRecord::Base
31
+ # def to_param # overridden
32
+ # name
33
+ # end
34
+ # end
35
+ #
36
+ # user = User.find_by(name: 'Phusion')
37
+ # user_path(user) # => "/users/Phusion"
38
+ def to_param
39
+ # We can't use alias_method here, because method 'id' optimizes itself on the fly.
40
+ id && id.to_s # Be sure to stringify the id for routes
41
+ end
42
+
43
+ # Returns a cache key that can be used to identify this record.
44
+ #
45
+ # Product.new.cache_key # => "products/new"
46
+ # Product.find(5).cache_key # => "products/5" (updated_at not available)
47
+ # Person.find(5).cache_key # => "people/5-20071224150000" (updated_at available)
48
+ def cache_key
49
+ case
50
+ when new_record?
51
+ "#{self.class.model_name.cache_key}/new"
52
+ when timestamp = max_updated_column_timestamp
53
+ timestamp = timestamp.utc.to_s(cache_timestamp_format)
54
+ "#{self.class.model_name.cache_key}/#{id}-#{timestamp}"
55
+ else
56
+ "#{self.class.model_name.cache_key}/#{id}"
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,47 @@
1
+ en:
2
+ # Attributes names common to most models
3
+ #attributes:
4
+ #created_at: "Created at"
5
+ #updated_at: "Updated at"
6
+
7
+ # Default error messages
8
+ errors:
9
+ messages:
10
+ taken: "has already been taken"
11
+
12
+ # Active Record models configuration
13
+ activerecord:
14
+ errors:
15
+ messages:
16
+ record_invalid: "Validation failed: %{errors}"
17
+ restrict_dependent_destroy:
18
+ one: "Cannot delete record because a dependent %{record} exists"
19
+ many: "Cannot delete record because dependent %{record} exist"
20
+ # Append your own errors here or at the model/attributes scope.
21
+
22
+ # You can define own errors for models or model attributes.
23
+ # The values :model, :attribute and :value are always available for interpolation.
24
+ #
25
+ # For example,
26
+ # models:
27
+ # user:
28
+ # blank: "This is a custom blank message for %{model}: %{attribute}"
29
+ # attributes:
30
+ # login:
31
+ # blank: "This is a custom blank message for User login"
32
+ # Will define custom blank validation message for User model and
33
+ # custom blank validation message for login attribute of User model.
34
+ #models:
35
+
36
+ # Translate model names. Used in Model.human_name().
37
+ #models:
38
+ # For example,
39
+ # user: "Dude"
40
+ # will translate User model name to "Dude"
41
+
42
+ # Translate model attribute names. Used in Model.human_attribute_name(attribute).
43
+ #attributes:
44
+ # For example,
45
+ # user:
46
+ # login: "Handle"
47
+ # will translate User attribute "login" as "Handle"
@@ -0,0 +1,181 @@
1
+ module ActiveRecord
2
+ module Locking
3
+ # == What is Optimistic Locking
4
+ #
5
+ # Optimistic locking allows multiple users to access the same record for edits, and assumes a minimum of
6
+ # conflicts with the data. It does this by checking whether another process has made changes to a record since
7
+ # it was opened, an <tt>ActiveRecord::StaleObjectError</tt> exception is thrown if that has occurred
8
+ # and the update is ignored.
9
+ #
10
+ # Check out <tt>ActiveRecord::Locking::Pessimistic</tt> for an alternative.
11
+ #
12
+ # == Usage
13
+ #
14
+ # Active Records support optimistic locking if the field +lock_version+ is present. Each update to the
15
+ # record increments the +lock_version+ column and the locking facilities ensure that records instantiated twice
16
+ # will let the last one saved raise a +StaleObjectError+ if the first was also updated. Example:
17
+ #
18
+ # p1 = Person.find(1)
19
+ # p2 = Person.find(1)
20
+ #
21
+ # p1.first_name = "Michael"
22
+ # p1.save
23
+ #
24
+ # p2.first_name = "should fail"
25
+ # p2.save # Raises a ActiveRecord::StaleObjectError
26
+ #
27
+ # Optimistic locking will also check for stale data when objects are destroyed. Example:
28
+ #
29
+ # p1 = Person.find(1)
30
+ # p2 = Person.find(1)
31
+ #
32
+ # p1.first_name = "Michael"
33
+ # p1.save
34
+ #
35
+ # p2.destroy # Raises a ActiveRecord::StaleObjectError
36
+ #
37
+ # You're then responsible for dealing with the conflict by rescuing the exception and either rolling back, merging,
38
+ # or otherwise apply the business logic needed to resolve the conflict.
39
+ #
40
+ # This locking mechanism will function inside a single Ruby process. To make it work across all
41
+ # web requests, the recommended approach is to add +lock_version+ as a hidden field to your form.
42
+ #
43
+ # This behavior can be turned off by setting <tt>ActiveRecord::Base.lock_optimistically = false</tt>.
44
+ # To override the name of the +lock_version+ column, set the <tt>locking_column</tt> class attribute:
45
+ #
46
+ # class Person < ActiveRecord::Base
47
+ # self.locking_column = :lock_person
48
+ # end
49
+ #
50
+ module Optimistic
51
+ extend ActiveSupport::Concern
52
+
53
+ included do
54
+ class_attribute :lock_optimistically, instance_writer: false
55
+ self.lock_optimistically = true
56
+ end
57
+
58
+ def locking_enabled? #:nodoc:
59
+ self.class.locking_enabled?
60
+ end
61
+
62
+ private
63
+ def increment_lock
64
+ lock_col = self.class.locking_column
65
+ previous_lock_value = send(lock_col).to_i
66
+ send(lock_col + '=', previous_lock_value + 1)
67
+ end
68
+
69
+ def update_record(attribute_names = @attributes.keys) #:nodoc:
70
+ return super unless locking_enabled?
71
+ return 0 if attribute_names.empty?
72
+
73
+ lock_col = self.class.locking_column
74
+ previous_lock_value = send(lock_col).to_i
75
+ increment_lock
76
+
77
+ attribute_names += [lock_col]
78
+ attribute_names.uniq!
79
+
80
+ begin
81
+ relation = self.class.unscoped
82
+
83
+ stmt = relation.where(
84
+ relation.table[self.class.primary_key].eq(id).and(
85
+ relation.table[lock_col].eq(self.class.quote_value(previous_lock_value))
86
+ )
87
+ ).arel.compile_update(arel_attributes_with_values_for_update(attribute_names))
88
+
89
+ affected_rows = self.class.connection.update stmt
90
+
91
+ unless affected_rows == 1
92
+ raise ActiveRecord::StaleObjectError.new(self, "update")
93
+ end
94
+
95
+ affected_rows
96
+
97
+ # If something went wrong, revert the version.
98
+ rescue Exception
99
+ send(lock_col + '=', previous_lock_value)
100
+ raise
101
+ end
102
+ end
103
+
104
+ def destroy_row
105
+ affected_rows = super
106
+
107
+ if locking_enabled? && affected_rows != 1
108
+ raise ActiveRecord::StaleObjectError.new(self, "destroy")
109
+ end
110
+
111
+ affected_rows
112
+ end
113
+
114
+ def relation_for_destroy
115
+ relation = super
116
+
117
+ if locking_enabled?
118
+ column_name = self.class.locking_column
119
+ column = self.class.columns_hash[column_name]
120
+ substitute = self.class.connection.substitute_at(column, relation.bind_values.length)
121
+
122
+ relation = relation.where(self.class.arel_table[column_name].eq(substitute))
123
+ relation.bind_values << [column, self[column_name].to_i]
124
+ end
125
+
126
+ relation
127
+ end
128
+
129
+ module ClassMethods
130
+ DEFAULT_LOCKING_COLUMN = 'lock_version'
131
+
132
+ # Returns true if the +lock_optimistically+ flag is set to true
133
+ # (which it is, by default) and the table includes the
134
+ # +locking_column+ column (defaults to +lock_version+).
135
+ def locking_enabled?
136
+ lock_optimistically && columns_hash[locking_column]
137
+ end
138
+
139
+ # Set the column to use for optimistic locking. Defaults to +lock_version+.
140
+ def locking_column=(value)
141
+ @locking_column = value.to_s
142
+ end
143
+
144
+ # The version column used for optimistic locking. Defaults to +lock_version+.
145
+ def locking_column
146
+ reset_locking_column unless defined?(@locking_column)
147
+ @locking_column
148
+ end
149
+
150
+ # Quote the column name used for optimistic locking.
151
+ def quoted_locking_column
152
+ connection.quote_column_name(locking_column)
153
+ end
154
+
155
+ # Reset the column used for optimistic locking back to the +lock_version+ default.
156
+ def reset_locking_column
157
+ self.locking_column = DEFAULT_LOCKING_COLUMN
158
+ end
159
+
160
+ # Make sure the lock version column gets updated when counters are
161
+ # updated.
162
+ def update_counters(id, counters)
163
+ counters = counters.merge(locking_column => 1) if locking_enabled?
164
+ super
165
+ end
166
+
167
+ def column_defaults
168
+ @column_defaults ||= begin
169
+ defaults = super
170
+
171
+ if defaults.key?(locking_column) && lock_optimistically
172
+ defaults[locking_column] ||= 0
173
+ end
174
+
175
+ defaults
176
+ end
177
+ end
178
+ end
179
+ end
180
+ end
181
+ end
@@ -0,0 +1,77 @@
1
+ module ActiveRecord
2
+ module Locking
3
+ # Locking::Pessimistic provides support for row-level locking using
4
+ # SELECT ... FOR UPDATE and other lock types.
5
+ #
6
+ # Pass <tt>lock: true</tt> to <tt>ActiveRecord::Base.find</tt> to obtain an exclusive
7
+ # lock on the selected rows:
8
+ # # select * from accounts where id=1 for update
9
+ # Account.find(1, lock: true)
10
+ #
11
+ # Pass <tt>lock: 'some locking clause'</tt> to give a database-specific locking clause
12
+ # of your own such as 'LOCK IN SHARE MODE' or 'FOR UPDATE NOWAIT'. Example:
13
+ #
14
+ # Account.transaction do
15
+ # # select * from accounts where name = 'shugo' limit 1 for update
16
+ # shugo = Account.where("name = 'shugo'").lock(true).first
17
+ # yuko = Account.where("name = 'yuko'").lock(true).first
18
+ # shugo.balance -= 100
19
+ # shugo.save!
20
+ # yuko.balance += 100
21
+ # yuko.save!
22
+ # end
23
+ #
24
+ # You can also use <tt>ActiveRecord::Base#lock!</tt> method to lock one record by id.
25
+ # This may be better if you don't need to lock every row. Example:
26
+ #
27
+ # Account.transaction do
28
+ # # select * from accounts where ...
29
+ # accounts = Account.where(...)
30
+ # account1 = accounts.detect { |account| ... }
31
+ # account2 = accounts.detect { |account| ... }
32
+ # # select * from accounts where id=? for update
33
+ # account1.lock!
34
+ # account2.lock!
35
+ # account1.balance -= 100
36
+ # account1.save!
37
+ # account2.balance += 100
38
+ # account2.save!
39
+ # end
40
+ #
41
+ # You can start a transaction and acquire the lock in one go by calling
42
+ # <tt>with_lock</tt> with a block. The block is called from within
43
+ # a transaction, the object is already locked. Example:
44
+ #
45
+ # account = Account.first
46
+ # account.with_lock do
47
+ # # This block is called within a transaction,
48
+ # # account is already locked.
49
+ # account.balance -= 100
50
+ # account.save!
51
+ # end
52
+ #
53
+ # Database-specific information on row locking:
54
+ # MySQL: http://dev.mysql.com/doc/refman/5.1/en/innodb-locking-reads.html
55
+ # PostgreSQL: http://www.postgresql.org/docs/current/interactive/sql-select.html#SQL-FOR-UPDATE-SHARE
56
+ module Pessimistic
57
+ # Obtain a row lock on this record. Reloads the record to obtain the requested
58
+ # lock. Pass an SQL locking clause to append the end of the SELECT statement
59
+ # or pass true for "FOR UPDATE" (the default, an exclusive row lock). Returns
60
+ # the locked record.
61
+ def lock!(lock = true)
62
+ reload(:lock => lock) if persisted?
63
+ self
64
+ end
65
+
66
+ # Wraps the passed block in a transaction, locking the object
67
+ # before yielding. You pass can the SQL locking clause
68
+ # as argument (see <tt>lock!</tt>).
69
+ def with_lock(lock = true)
70
+ transaction do
71
+ lock!(lock)
72
+ yield
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,82 @@
1
+ module ActiveRecord
2
+ class LogSubscriber < ActiveSupport::LogSubscriber
3
+ IGNORE_PAYLOAD_NAMES = ["SCHEMA", "EXPLAIN"]
4
+
5
+ def self.runtime=(value)
6
+ ActiveRecord::RuntimeRegistry.sql_runtime = value
7
+ end
8
+
9
+ def self.runtime
10
+ ActiveRecord::RuntimeRegistry.sql_runtime ||= 0
11
+ end
12
+
13
+ def self.reset_runtime
14
+ rt, self.runtime = runtime, 0
15
+ rt
16
+ end
17
+
18
+ def initialize
19
+ super
20
+ @odd_or_even = false
21
+ end
22
+
23
+ def render_bind(column, value)
24
+ if column
25
+ if column.binary?
26
+ value = "<#{value.bytesize} bytes of binary data>"
27
+ end
28
+
29
+ [column.name, value]
30
+ else
31
+ [nil, value]
32
+ end
33
+ end
34
+
35
+ def sql(event)
36
+ self.class.runtime += event.duration
37
+ return unless logger.debug?
38
+
39
+ payload = event.payload
40
+
41
+ return if IGNORE_PAYLOAD_NAMES.include?(payload[:name])
42
+
43
+ name = "#{payload[:name]} (#{event.duration.round(1)}ms)"
44
+ sql = payload[:sql].squeeze(' ')
45
+ binds = nil
46
+
47
+ unless (payload[:binds] || []).empty?
48
+ binds = " " + payload[:binds].map { |col,v|
49
+ render_bind(col, v)
50
+ }.inspect
51
+ end
52
+
53
+ if odd?
54
+ name = color(name, CYAN, true)
55
+ sql = color(sql, nil, true)
56
+ else
57
+ name = color(name, MAGENTA, true)
58
+ end
59
+
60
+ debug " #{name} #{sql}#{binds}"
61
+ end
62
+
63
+ def identity(event)
64
+ return unless logger.debug?
65
+
66
+ name = color(event.payload[:name], odd? ? CYAN : MAGENTA, true)
67
+ line = odd? ? color(event.payload[:line], nil, true) : event.payload[:line]
68
+
69
+ debug " #{name} #{line}"
70
+ end
71
+
72
+ def odd?
73
+ @odd_or_even = !@odd_or_even
74
+ end
75
+
76
+ def logger
77
+ ActiveRecord::Base.logger
78
+ end
79
+ end
80
+ end
81
+
82
+ ActiveRecord::LogSubscriber.attach_to :active_record