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,15 @@
1
+ module ActiveRecord
2
+ # Returns the version of the currently loaded Active Record as a <tt>Gem::Version</tt>
3
+ def self.gem_version
4
+ Gem::Version.new VERSION::STRING
5
+ end
6
+
7
+ module VERSION
8
+ MAJOR = 4
9
+ MINOR = 2
10
+ TINY = 0
11
+ PRE = nil
12
+
13
+ STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
14
+ end
15
+ end
@@ -0,0 +1,247 @@
1
+ require 'active_support/core_ext/hash/indifferent_access'
2
+
3
+ module ActiveRecord
4
+ # == Single table inheritance
5
+ #
6
+ # Active Record allows inheritance by storing the name of the class in a column that by
7
+ # default is named "type" (can be changed by overwriting <tt>Base.inheritance_column</tt>).
8
+ # This means that an inheritance looking like this:
9
+ #
10
+ # class Company < ActiveRecord::Base; end
11
+ # class Firm < Company; end
12
+ # class Client < Company; end
13
+ # class PriorityClient < Client; end
14
+ #
15
+ # When you do <tt>Firm.create(name: "37signals")</tt>, this record will be saved in
16
+ # the companies table with type = "Firm". You can then fetch this row again using
17
+ # <tt>Company.where(name: '37signals').first</tt> and it will return a Firm object.
18
+ #
19
+ # Be aware that because the type column is an attribute on the record every new
20
+ # subclass will instantly be marked as dirty and the type column will be included
21
+ # in the list of changed attributes on the record. This is different from non
22
+ # STI classes:
23
+ #
24
+ # Company.new.changed? # => false
25
+ # Firm.new.changed? # => true
26
+ # Firm.new.changes # => {"type"=>["","Firm"]}
27
+ #
28
+ # If you don't have a type column defined in your table, single-table inheritance won't
29
+ # be triggered. In that case, it'll work just like normal subclasses with no special magic
30
+ # for differentiating between them or reloading the right type with find.
31
+ #
32
+ # Note, all the attributes for all the cases are kept in the same table. Read more:
33
+ # http://www.martinfowler.com/eaaCatalog/singleTableInheritance.html
34
+ #
35
+ module Inheritance
36
+ extend ActiveSupport::Concern
37
+
38
+ included do
39
+ # Determines whether to store the full constant name including namespace when using STI.
40
+ class_attribute :store_full_sti_class, instance_writer: false
41
+ self.store_full_sti_class = true
42
+ end
43
+
44
+ module ClassMethods
45
+ # Determines if one of the attributes passed in is the inheritance column,
46
+ # and if the inheritance column is attr accessible, it initializes an
47
+ # instance of the given subclass instead of the base class.
48
+ def new(*args, &block)
49
+ if abstract_class? || self == Base
50
+ raise NotImplementedError, "#{self} is an abstract class and cannot be instantiated."
51
+ end
52
+
53
+ attrs = args.first
54
+ if subclass_from_attributes?(attrs)
55
+ subclass = subclass_from_attributes(attrs)
56
+ end
57
+
58
+ if subclass
59
+ subclass.new(*args, &block)
60
+ else
61
+ super
62
+ end
63
+ end
64
+
65
+ # Returns +true+ if this does not need STI type condition. Returns
66
+ # +false+ if STI type condition needs to be applied.
67
+ def descends_from_active_record?
68
+ if self == Base
69
+ false
70
+ elsif superclass.abstract_class?
71
+ superclass.descends_from_active_record?
72
+ else
73
+ superclass == Base || !columns_hash.include?(inheritance_column)
74
+ end
75
+ end
76
+
77
+ def finder_needs_type_condition? #:nodoc:
78
+ # This is like this because benchmarking justifies the strange :false stuff
79
+ :true == (@finder_needs_type_condition ||= descends_from_active_record? ? :false : :true)
80
+ end
81
+
82
+ def symbolized_base_class
83
+ ActiveSupport::Deprecation.warn('`ActiveRecord::Base.symbolized_base_class` is deprecated and will be removed without replacement.')
84
+ @symbolized_base_class ||= base_class.to_s.to_sym
85
+ end
86
+
87
+ def symbolized_sti_name
88
+ ActiveSupport::Deprecation.warn('`ActiveRecord::Base.symbolized_sti_name` is deprecated and will be removed without replacement.')
89
+ @symbolized_sti_name ||= sti_name.present? ? sti_name.to_sym : symbolized_base_class
90
+ end
91
+
92
+ # Returns the class descending directly from ActiveRecord::Base, or
93
+ # an abstract class, if any, in the inheritance hierarchy.
94
+ #
95
+ # If A extends AR::Base, A.base_class will return A. If B descends from A
96
+ # through some arbitrarily deep hierarchy, B.base_class will return A.
97
+ #
98
+ # If B < A and C < B and if A is an abstract_class then both B.base_class
99
+ # and C.base_class would return B as the answer since A is an abstract_class.
100
+ def base_class
101
+ unless self < Base
102
+ raise ActiveRecordError, "#{name} doesn't belong in a hierarchy descending from ActiveRecord"
103
+ end
104
+
105
+ if superclass == Base || superclass.abstract_class?
106
+ self
107
+ else
108
+ superclass.base_class
109
+ end
110
+ end
111
+
112
+ # Set this to true if this is an abstract class (see <tt>abstract_class?</tt>).
113
+ # If you are using inheritance with ActiveRecord and don't want child classes
114
+ # to utilize the implied STI table name of the parent class, this will need to be true.
115
+ # For example, given the following:
116
+ #
117
+ # class SuperClass < ActiveRecord::Base
118
+ # self.abstract_class = true
119
+ # end
120
+ # class Child < SuperClass
121
+ # self.table_name = 'the_table_i_really_want'
122
+ # end
123
+ #
124
+ #
125
+ # <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>
126
+ #
127
+ attr_accessor :abstract_class
128
+
129
+ # Returns whether this class is an abstract class or not.
130
+ def abstract_class?
131
+ defined?(@abstract_class) && @abstract_class == true
132
+ end
133
+
134
+ def sti_name
135
+ store_full_sti_class ? name : name.demodulize
136
+ end
137
+
138
+ protected
139
+
140
+ # Returns the class type of the record using the current module as a prefix. So descendants of
141
+ # MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
142
+ def compute_type(type_name)
143
+ if type_name.match(/^::/)
144
+ # If the type is prefixed with a scope operator then we assume that
145
+ # the type_name is an absolute reference.
146
+ ActiveSupport::Dependencies.constantize(type_name)
147
+ else
148
+ # Build a list of candidates to search for
149
+ candidates = []
150
+ name.scan(/::|$/) { candidates.unshift "#{$`}::#{type_name}" }
151
+ candidates << type_name
152
+
153
+ candidates.each do |candidate|
154
+ constant = ActiveSupport::Dependencies.safe_constantize(candidate)
155
+ return constant if candidate == constant.to_s
156
+ end
157
+
158
+ raise NameError.new("uninitialized constant #{candidates.first}", candidates.first)
159
+ end
160
+ end
161
+
162
+ private
163
+
164
+ # Called by +instantiate+ to decide which class to use for a new
165
+ # record instance. For single-table inheritance, we check the record
166
+ # for a +type+ column and return the corresponding class.
167
+ def discriminate_class_for_record(record)
168
+ if using_single_table_inheritance?(record)
169
+ find_sti_class(record[inheritance_column])
170
+ else
171
+ super
172
+ end
173
+ end
174
+
175
+ def using_single_table_inheritance?(record)
176
+ record[inheritance_column].present? && columns_hash.include?(inheritance_column)
177
+ end
178
+
179
+ def find_sti_class(type_name)
180
+ if store_full_sti_class
181
+ ActiveSupport::Dependencies.constantize(type_name)
182
+ else
183
+ compute_type(type_name)
184
+ end
185
+ rescue NameError
186
+ raise SubclassNotFound,
187
+ "The single-table inheritance mechanism failed to locate the subclass: '#{type_name}'. " +
188
+ "This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. " +
189
+ "Please rename this column if you didn't intend it to be used for storing the inheritance class " +
190
+ "or overwrite #{name}.inheritance_column to use another column for that information."
191
+ end
192
+
193
+ def type_condition(table = arel_table)
194
+ sti_column = table[inheritance_column]
195
+ sti_names = ([self] + descendants).map { |model| model.sti_name }
196
+
197
+ sti_column.in(sti_names)
198
+ end
199
+
200
+ # Detect the subclass from the inheritance column of attrs. If the inheritance column value
201
+ # is not self or a valid subclass, raises ActiveRecord::SubclassNotFound
202
+ # If this is a StrongParameters hash, and access to inheritance_column is not permitted,
203
+ # this will ignore the inheritance column and return nil
204
+ def subclass_from_attributes?(attrs)
205
+ columns_hash.include?(inheritance_column) && attrs.is_a?(Hash)
206
+ end
207
+
208
+ def subclass_from_attributes(attrs)
209
+ subclass_name = attrs.with_indifferent_access[inheritance_column]
210
+
211
+ if subclass_name.present? && subclass_name != self.name
212
+ subclass = subclass_name.safe_constantize
213
+
214
+ unless descendants.include?(subclass)
215
+ raise ActiveRecord::SubclassNotFound.new("Invalid single-table inheritance type: #{subclass_name} is not a subclass of #{name}")
216
+ end
217
+
218
+ subclass
219
+ end
220
+ end
221
+ end
222
+
223
+ def initialize_dup(other)
224
+ super
225
+ ensure_proper_type
226
+ end
227
+
228
+ private
229
+
230
+ def initialize_internals_callback
231
+ super
232
+ ensure_proper_type
233
+ end
234
+
235
+ # Sets the attribute used for single table inheritance to this class name if this is not the
236
+ # ActiveRecord::Base descendant.
237
+ # Considering the hierarchy Reply < Message < ActiveRecord::Base, this makes it possible to
238
+ # do Reply.new without having to set <tt>Reply[Reply.inheritance_column] = "Reply"</tt> yourself.
239
+ # No such attribute would be set for objects of the Message class in that example.
240
+ def ensure_proper_type
241
+ klass = self.class
242
+ if klass.finder_needs_type_condition?
243
+ write_attribute(klass.inheritance_column, klass.sti_name)
244
+ end
245
+ end
246
+ end
247
+ end
@@ -0,0 +1,113 @@
1
+ require 'active_support/core_ext/string/filters'
2
+
3
+ module ActiveRecord
4
+ module Integration
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ ##
9
+ # :singleton-method:
10
+ # Indicates the format used to generate the timestamp in the cache key.
11
+ # Accepts any of the symbols in <tt>Time::DATE_FORMATS</tt>.
12
+ #
13
+ # This is +:nsec+, by default.
14
+ class_attribute :cache_timestamp_format, :instance_writer => false
15
+ self.cache_timestamp_format = :nsec
16
+ end
17
+
18
+ # Returns a String, which Action Pack uses for constructing an URL to this
19
+ # object. The default implementation returns this record's id as a String,
20
+ # or nil if this record's unsaved.
21
+ #
22
+ # For example, suppose that you have a User model, and that you have a
23
+ # <tt>resources :users</tt> route. Normally, +user_path+ will
24
+ # construct a path with the user object's 'id' in it:
25
+ #
26
+ # user = User.find_by(name: 'Phusion')
27
+ # user_path(user) # => "/users/1"
28
+ #
29
+ # You can override +to_param+ in your model to make +user_path+ construct
30
+ # a path using the user's name instead of the user's id:
31
+ #
32
+ # class User < ActiveRecord::Base
33
+ # def to_param # overridden
34
+ # name
35
+ # end
36
+ # end
37
+ #
38
+ # user = User.find_by(name: 'Phusion')
39
+ # user_path(user) # => "/users/Phusion"
40
+ def to_param
41
+ # We can't use alias_method here, because method 'id' optimizes itself on the fly.
42
+ id && id.to_s # Be sure to stringify the id for routes
43
+ end
44
+
45
+ # Returns a cache key that can be used to identify this record.
46
+ #
47
+ # Product.new.cache_key # => "products/new"
48
+ # Product.find(5).cache_key # => "products/5" (updated_at not available)
49
+ # Person.find(5).cache_key # => "people/5-20071224150000" (updated_at available)
50
+ #
51
+ # You can also pass a list of named timestamps, and the newest in the list will be
52
+ # used to generate the key:
53
+ #
54
+ # Person.find(5).cache_key(:updated_at, :last_reviewed_at)
55
+ def cache_key(*timestamp_names)
56
+ case
57
+ when new_record?
58
+ "#{model_name.cache_key}/new"
59
+ when timestamp_names.any?
60
+ timestamp = max_updated_column_timestamp(timestamp_names)
61
+ timestamp = timestamp.utc.to_s(cache_timestamp_format)
62
+ "#{model_name.cache_key}/#{id}-#{timestamp}"
63
+ when timestamp = max_updated_column_timestamp
64
+ timestamp = timestamp.utc.to_s(cache_timestamp_format)
65
+ "#{model_name.cache_key}/#{id}-#{timestamp}"
66
+ else
67
+ "#{model_name.cache_key}/#{id}"
68
+ end
69
+ end
70
+
71
+ module ClassMethods
72
+ # Defines your model's +to_param+ method to generate "pretty" URLs
73
+ # using +method_name+, which can be any attribute or method that
74
+ # responds to +to_s+.
75
+ #
76
+ # class User < ActiveRecord::Base
77
+ # to_param :name
78
+ # end
79
+ #
80
+ # user = User.find_by(name: 'Fancy Pants')
81
+ # user.id # => 123
82
+ # user_path(user) # => "/users/123-fancy-pants"
83
+ #
84
+ # Values longer than 20 characters will be truncated. The value
85
+ # is truncated word by word.
86
+ #
87
+ # user = User.find_by(name: 'David HeinemeierHansson')
88
+ # user.id # => 125
89
+ # user_path(user) # => "/users/125-david"
90
+ #
91
+ # Because the generated param begins with the record's +id+, it is
92
+ # suitable for passing to +find+. In a controller, for example:
93
+ #
94
+ # params[:id] # => "123-fancy-pants"
95
+ # User.find(params[:id]).id # => 123
96
+ def to_param(method_name = nil)
97
+ if method_name.nil?
98
+ super()
99
+ else
100
+ define_method :to_param do
101
+ if (default = super()) &&
102
+ (result = send(method_name).to_s).present? &&
103
+ (param = result.squish.truncate(20, separator: /\s/, omission: nil).parameterize).present?
104
+ "#{default}-#{param}"
105
+ else
106
+ default
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
113
+ 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,204 @@
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 = self.attribute_names) #: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, column_for_attribute(lock_col)))
86
+ )
87
+ ).arel.compile_update(
88
+ arel_attributes_with_values_for_update(attribute_names),
89
+ self.class.primary_key
90
+ )
91
+
92
+ affected_rows = self.class.connection.update stmt
93
+
94
+ unless affected_rows == 1
95
+ raise ActiveRecord::StaleObjectError.new(self, "update")
96
+ end
97
+
98
+ affected_rows
99
+
100
+ # If something went wrong, revert the version.
101
+ rescue Exception
102
+ send(lock_col + '=', previous_lock_value)
103
+ raise
104
+ end
105
+ end
106
+
107
+ def destroy_row
108
+ affected_rows = super
109
+
110
+ if locking_enabled? && affected_rows != 1
111
+ raise ActiveRecord::StaleObjectError.new(self, "destroy")
112
+ end
113
+
114
+ affected_rows
115
+ end
116
+
117
+ def relation_for_destroy
118
+ relation = super
119
+
120
+ if locking_enabled?
121
+ column_name = self.class.locking_column
122
+ column = self.class.columns_hash[column_name]
123
+ substitute = self.class.connection.substitute_at(column)
124
+
125
+ relation = relation.where(self.class.arel_table[column_name].eq(substitute))
126
+ relation.bind_values << [column, self[column_name].to_i]
127
+ end
128
+
129
+ relation
130
+ end
131
+
132
+ module ClassMethods
133
+ DEFAULT_LOCKING_COLUMN = 'lock_version'
134
+
135
+ # Returns true if the +lock_optimistically+ flag is set to true
136
+ # (which it is, by default) and the table includes the
137
+ # +locking_column+ column (defaults to +lock_version+).
138
+ def locking_enabled?
139
+ lock_optimistically && columns_hash[locking_column]
140
+ end
141
+
142
+ # Set the column to use for optimistic locking. Defaults to +lock_version+.
143
+ def locking_column=(value)
144
+ clear_caches_calculated_from_columns
145
+ @locking_column = value.to_s
146
+ end
147
+
148
+ # The version column used for optimistic locking. Defaults to +lock_version+.
149
+ def locking_column
150
+ reset_locking_column unless defined?(@locking_column)
151
+ @locking_column
152
+ end
153
+
154
+ # Reset the column used for optimistic locking back to the +lock_version+ default.
155
+ def reset_locking_column
156
+ self.locking_column = DEFAULT_LOCKING_COLUMN
157
+ end
158
+
159
+ # Make sure the lock version column gets updated when counters are
160
+ # updated.
161
+ def update_counters(id, counters)
162
+ counters = counters.merge(locking_column => 1) if locking_enabled?
163
+ super
164
+ end
165
+
166
+ private
167
+
168
+ # We need to apply this decorator here, rather than on module inclusion. The closure
169
+ # created by the matcher would otherwise evaluate for `ActiveRecord::Base`, not the
170
+ # sub class being decorated. As such, changes to `lock_optimistically`, or
171
+ # `locking_column` would not be picked up.
172
+ def inherited(subclass)
173
+ subclass.class_eval do
174
+ is_lock_column = ->(name, _) { lock_optimistically && name == locking_column }
175
+ decorate_matching_attribute_types(is_lock_column, :_optimistic_locking) do |type|
176
+ LockingType.new(type)
177
+ end
178
+ end
179
+ super
180
+ end
181
+ end
182
+ end
183
+
184
+ class LockingType < SimpleDelegator # :nodoc:
185
+ def type_cast_from_database(value)
186
+ # `nil` *should* be changed to 0
187
+ super.to_i
188
+ end
189
+
190
+ def changed?(old_value, *)
191
+ # Ensure we save if the default was `nil`
192
+ super || old_value == 0
193
+ end
194
+
195
+ def init_with(coder)
196
+ __setobj__(coder['subtype'])
197
+ end
198
+
199
+ def encode_with(coder)
200
+ coder['subtype'] = __getobj__
201
+ end
202
+ end
203
+ end
204
+ end