activerecord 3.1.10 → 4.2.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (237) hide show
  1. checksums.yaml +6 -6
  2. data/CHANGELOG.md +1837 -338
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +39 -43
  5. data/examples/performance.rb +51 -20
  6. data/examples/simple.rb +4 -4
  7. data/lib/active_record/aggregations.rb +57 -43
  8. data/lib/active_record/association_relation.rb +35 -0
  9. data/lib/active_record/associations/alias_tracker.rb +47 -39
  10. data/lib/active_record/associations/association.rb +71 -85
  11. data/lib/active_record/associations/association_scope.rb +138 -89
  12. data/lib/active_record/associations/belongs_to_association.rb +65 -25
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +9 -3
  14. data/lib/active_record/associations/builder/association.rb +125 -29
  15. data/lib/active_record/associations/builder/belongs_to.rb +91 -60
  16. data/lib/active_record/associations/builder/collection_association.rb +69 -49
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +113 -42
  18. data/lib/active_record/associations/builder/has_many.rb +8 -64
  19. data/lib/active_record/associations/builder/has_one.rb +12 -52
  20. data/lib/active_record/associations/builder/singular_association.rb +22 -29
  21. data/lib/active_record/associations/collection_association.rb +294 -187
  22. data/lib/active_record/associations/collection_proxy.rb +961 -94
  23. data/lib/active_record/associations/foreign_association.rb +11 -0
  24. data/lib/active_record/associations/has_many_association.rb +118 -23
  25. data/lib/active_record/associations/has_many_through_association.rb +115 -45
  26. data/lib/active_record/associations/has_one_association.rb +57 -24
  27. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  28. data/lib/active_record/associations/join_dependency/join_association.rb +76 -102
  29. data/lib/active_record/associations/join_dependency/join_base.rb +7 -9
  30. data/lib/active_record/associations/join_dependency/join_part.rb +30 -37
  31. data/lib/active_record/associations/join_dependency.rb +230 -156
  32. data/lib/active_record/associations/preloader/association.rb +96 -55
  33. data/lib/active_record/associations/preloader/collection_association.rb +3 -3
  34. data/lib/active_record/associations/preloader/has_many_through.rb +7 -3
  35. data/lib/active_record/associations/preloader/has_one.rb +1 -1
  36. data/lib/active_record/associations/preloader/singular_association.rb +3 -3
  37. data/lib/active_record/associations/preloader/through_association.rb +61 -32
  38. data/lib/active_record/associations/preloader.rb +113 -87
  39. data/lib/active_record/associations/singular_association.rb +29 -13
  40. data/lib/active_record/associations/through_association.rb +37 -19
  41. data/lib/active_record/associations.rb +505 -371
  42. data/lib/active_record/attribute.rb +163 -0
  43. data/lib/active_record/attribute_assignment.rb +212 -0
  44. data/lib/active_record/attribute_decorators.rb +66 -0
  45. data/lib/active_record/attribute_methods/before_type_cast.rb +52 -7
  46. data/lib/active_record/attribute_methods/dirty.rb +141 -51
  47. data/lib/active_record/attribute_methods/primary_key.rb +87 -36
  48. data/lib/active_record/attribute_methods/query.rb +5 -4
  49. data/lib/active_record/attribute_methods/read.rb +74 -117
  50. data/lib/active_record/attribute_methods/serialization.rb +70 -0
  51. data/lib/active_record/attribute_methods/time_zone_conversion.rb +49 -47
  52. data/lib/active_record/attribute_methods/write.rb +60 -21
  53. data/lib/active_record/attribute_methods.rb +409 -48
  54. data/lib/active_record/attribute_set/builder.rb +106 -0
  55. data/lib/active_record/attribute_set.rb +81 -0
  56. data/lib/active_record/attributes.rb +147 -0
  57. data/lib/active_record/autosave_association.rb +279 -232
  58. data/lib/active_record/base.rb +84 -1969
  59. data/lib/active_record/callbacks.rb +66 -28
  60. data/lib/active_record/coders/json.rb +13 -0
  61. data/lib/active_record/coders/yaml_column.rb +18 -21
  62. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +422 -243
  63. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  64. data/lib/active_record/connection_adapters/abstract/database_statements.rb +170 -194
  65. data/lib/active_record/connection_adapters/abstract/query_cache.rb +32 -19
  66. data/lib/active_record/connection_adapters/abstract/quoting.rb +79 -57
  67. data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
  68. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +125 -0
  69. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +273 -170
  70. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +50 -0
  71. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +731 -254
  72. data/lib/active_record/connection_adapters/abstract/transaction.rb +215 -0
  73. data/lib/active_record/connection_adapters/abstract_adapter.rb +339 -95
  74. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +946 -0
  75. data/lib/active_record/connection_adapters/column.rb +33 -221
  76. data/lib/active_record/connection_adapters/connection_specification.rb +275 -0
  77. data/lib/active_record/connection_adapters/mysql2_adapter.rb +140 -602
  78. data/lib/active_record/connection_adapters/mysql_adapter.rb +254 -756
  79. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +93 -0
  80. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  81. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +232 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +19 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +36 -0
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +108 -0
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +596 -0
  112. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  113. data/lib/active_record/connection_adapters/postgresql_adapter.rb +445 -902
  114. data/lib/active_record/connection_adapters/schema_cache.rb +94 -0
  115. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +578 -25
  116. data/lib/active_record/connection_handling.rb +132 -0
  117. data/lib/active_record/core.rb +579 -0
  118. data/lib/active_record/counter_cache.rb +159 -102
  119. data/lib/active_record/dynamic_matchers.rb +140 -0
  120. data/lib/active_record/enum.rb +197 -0
  121. data/lib/active_record/errors.rb +102 -34
  122. data/lib/active_record/explain.rb +38 -0
  123. data/lib/active_record/explain_registry.rb +30 -0
  124. data/lib/active_record/explain_subscriber.rb +29 -0
  125. data/lib/active_record/fixture_set/file.rb +56 -0
  126. data/lib/active_record/fixtures.rb +318 -260
  127. data/lib/active_record/gem_version.rb +15 -0
  128. data/lib/active_record/inheritance.rb +247 -0
  129. data/lib/active_record/integration.rb +113 -0
  130. data/lib/active_record/legacy_yaml_adapter.rb +30 -0
  131. data/lib/active_record/locale/en.yml +8 -1
  132. data/lib/active_record/locking/optimistic.rb +80 -52
  133. data/lib/active_record/locking/pessimistic.rb +27 -5
  134. data/lib/active_record/log_subscriber.rb +25 -18
  135. data/lib/active_record/migration/command_recorder.rb +130 -38
  136. data/lib/active_record/migration/join_table.rb +15 -0
  137. data/lib/active_record/migration.rb +532 -201
  138. data/lib/active_record/model_schema.rb +342 -0
  139. data/lib/active_record/nested_attributes.rb +229 -139
  140. data/lib/active_record/no_touching.rb +52 -0
  141. data/lib/active_record/null_relation.rb +81 -0
  142. data/lib/active_record/persistence.rb +304 -99
  143. data/lib/active_record/query_cache.rb +25 -43
  144. data/lib/active_record/querying.rb +68 -0
  145. data/lib/active_record/railtie.rb +86 -45
  146. data/lib/active_record/railties/console_sandbox.rb +3 -4
  147. data/lib/active_record/railties/controller_runtime.rb +7 -4
  148. data/lib/active_record/railties/databases.rake +198 -377
  149. data/lib/active_record/railties/jdbcmysql_error.rb +2 -2
  150. data/lib/active_record/readonly_attributes.rb +23 -0
  151. data/lib/active_record/reflection.rb +516 -165
  152. data/lib/active_record/relation/batches.rb +96 -45
  153. data/lib/active_record/relation/calculations.rb +221 -144
  154. data/lib/active_record/relation/delegation.rb +140 -0
  155. data/lib/active_record/relation/finder_methods.rb +362 -243
  156. data/lib/active_record/relation/merger.rb +193 -0
  157. data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
  158. data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
  159. data/lib/active_record/relation/predicate_builder.rb +135 -41
  160. data/lib/active_record/relation/query_methods.rb +982 -155
  161. data/lib/active_record/relation/spawn_methods.rb +50 -110
  162. data/lib/active_record/relation.rb +371 -180
  163. data/lib/active_record/result.rb +109 -12
  164. data/lib/active_record/runtime_registry.rb +22 -0
  165. data/lib/active_record/sanitization.rb +191 -0
  166. data/lib/active_record/schema.rb +19 -13
  167. data/lib/active_record/schema_dumper.rb +111 -61
  168. data/lib/active_record/schema_migration.rb +53 -0
  169. data/lib/active_record/scoping/default.rb +135 -0
  170. data/lib/active_record/scoping/named.rb +164 -0
  171. data/lib/active_record/scoping.rb +87 -0
  172. data/lib/active_record/serialization.rb +7 -45
  173. data/lib/active_record/serializers/xml_serializer.rb +14 -65
  174. data/lib/active_record/statement_cache.rb +111 -0
  175. data/lib/active_record/store.rb +205 -0
  176. data/lib/active_record/tasks/database_tasks.rb +299 -0
  177. data/lib/active_record/tasks/mysql_database_tasks.rb +159 -0
  178. data/lib/active_record/tasks/postgresql_database_tasks.rb +101 -0
  179. data/lib/active_record/tasks/sqlite_database_tasks.rb +55 -0
  180. data/lib/active_record/timestamp.rb +35 -14
  181. data/lib/active_record/transactions.rb +141 -74
  182. data/lib/active_record/translation.rb +22 -0
  183. data/lib/active_record/type/big_integer.rb +13 -0
  184. data/lib/active_record/type/binary.rb +50 -0
  185. data/lib/active_record/type/boolean.rb +31 -0
  186. data/lib/active_record/type/date.rb +50 -0
  187. data/lib/active_record/type/date_time.rb +54 -0
  188. data/lib/active_record/type/decimal.rb +64 -0
  189. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  190. data/lib/active_record/type/decorator.rb +14 -0
  191. data/lib/active_record/type/float.rb +19 -0
  192. data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
  193. data/lib/active_record/type/integer.rb +59 -0
  194. data/lib/active_record/type/mutable.rb +16 -0
  195. data/lib/active_record/type/numeric.rb +36 -0
  196. data/lib/active_record/type/serialized.rb +62 -0
  197. data/lib/active_record/type/string.rb +40 -0
  198. data/lib/active_record/type/text.rb +11 -0
  199. data/lib/active_record/type/time.rb +26 -0
  200. data/lib/active_record/type/time_value.rb +38 -0
  201. data/lib/active_record/type/type_map.rb +64 -0
  202. data/lib/active_record/type/unsigned_integer.rb +15 -0
  203. data/lib/active_record/type/value.rb +110 -0
  204. data/lib/active_record/type.rb +23 -0
  205. data/lib/active_record/validations/associated.rb +27 -18
  206. data/lib/active_record/validations/presence.rb +67 -0
  207. data/lib/active_record/validations/uniqueness.rb +125 -66
  208. data/lib/active_record/validations.rb +37 -30
  209. data/lib/active_record/version.rb +5 -7
  210. data/lib/active_record.rb +80 -25
  211. data/lib/rails/generators/active_record/migration/migration_generator.rb +54 -9
  212. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +19 -0
  213. data/lib/rails/generators/active_record/migration/templates/migration.rb +25 -11
  214. data/lib/rails/generators/active_record/migration.rb +11 -8
  215. data/lib/rails/generators/active_record/model/model_generator.rb +17 -4
  216. data/lib/rails/generators/active_record/model/templates/model.rb +5 -2
  217. data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
  218. data/lib/rails/generators/active_record.rb +3 -11
  219. metadata +132 -53
  220. data/examples/associations.png +0 -0
  221. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -62
  222. data/lib/active_record/associations/join_helper.rb +0 -55
  223. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  224. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -135
  225. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -556
  226. data/lib/active_record/dynamic_finder_match.rb +0 -56
  227. data/lib/active_record/dynamic_scope_match.rb +0 -23
  228. data/lib/active_record/identity_map.rb +0 -163
  229. data/lib/active_record/named_scope.rb +0 -200
  230. data/lib/active_record/observer.rb +0 -121
  231. data/lib/active_record/session_store.rb +0 -358
  232. data/lib/active_record/test_case.rb +0 -69
  233. data/lib/rails/generators/active_record/model/templates/migration.rb +0 -17
  234. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  235. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  236. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  237. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -16
@@ -1,32 +1,27 @@
1
- begin
2
- require 'psych'
3
- rescue LoadError
4
- end
5
-
6
1
  require 'yaml'
7
2
  require 'set'
8
3
  require 'active_support/benchmarkable'
9
4
  require 'active_support/dependencies'
10
5
  require 'active_support/descendants_tracker'
11
6
  require 'active_support/time'
12
- require 'active_support/core_ext/class/attribute'
13
- require 'active_support/core_ext/class/attribute_accessors'
7
+ require 'active_support/core_ext/module/attribute_accessors'
14
8
  require 'active_support/core_ext/class/delegating_attributes'
15
- require 'active_support/core_ext/class/attribute'
16
9
  require 'active_support/core_ext/array/extract_options'
17
10
  require 'active_support/core_ext/hash/deep_merge'
18
- require 'active_support/core_ext/hash/indifferent_access'
19
11
  require 'active_support/core_ext/hash/slice'
12
+ require 'active_support/core_ext/hash/transform_values'
20
13
  require 'active_support/core_ext/string/behavior'
21
14
  require 'active_support/core_ext/kernel/singleton_class'
22
- require 'active_support/core_ext/module/delegation'
23
15
  require 'active_support/core_ext/module/introspection'
24
16
  require 'active_support/core_ext/object/duplicable'
25
- require 'active_support/core_ext/object/blank'
26
- require 'active_support/deprecation'
17
+ require 'active_support/core_ext/class/subclasses'
27
18
  require 'arel'
19
+ require 'active_record/attribute_decorators'
28
20
  require 'active_record/errors'
29
21
  require 'active_record/log_subscriber'
22
+ require 'active_record/explain_subscriber'
23
+ require 'active_record/relation/delegation'
24
+ require 'active_record/attributes'
30
25
 
31
26
  module ActiveRecord #:nodoc:
32
27
  # = Active Record
@@ -45,7 +40,7 @@ module ActiveRecord #:nodoc:
45
40
  # method is especially useful when you're receiving the data from somewhere else, like an
46
41
  # HTTP request. It works like this:
47
42
  #
48
- # user = User.new(:name => "David", :occupation => "Code Artist")
43
+ # user = User.new(name: "David", occupation: "Code Artist")
49
44
  # user.name # => "David"
50
45
  #
51
46
  # You can also use block initialization:
@@ -78,7 +73,7 @@ module ActiveRecord #:nodoc:
78
73
  # end
79
74
  #
80
75
  # def self.authenticate_safely_simply(user_name, password)
81
- # where(:user_name => user_name, :password => password).first
76
+ # where(user_name: user_name, password: password).first
82
77
  # end
83
78
  # end
84
79
  #
@@ -96,27 +91,27 @@ module ActiveRecord #:nodoc:
96
91
  #
97
92
  # Company.where(
98
93
  # "id = :id AND name = :name AND division = :division AND created_at > :accounting_date",
99
- # { :id => 3, :name => "37signals", :division => "First", :accounting_date => '2005-01-01' }
94
+ # { id: 3, name: "37signals", division: "First", accounting_date: '2005-01-01' }
100
95
  # ).first
101
96
  #
102
97
  # Similarly, a simple hash without a statement will generate conditions based on equality with the SQL AND
103
98
  # operator. For instance:
104
99
  #
105
- # Student.where(:first_name => "Harvey", :status => 1)
100
+ # Student.where(first_name: "Harvey", status: 1)
106
101
  # Student.where(params[:student])
107
102
  #
108
103
  # A range may be used in the hash to use the SQL BETWEEN operator:
109
104
  #
110
- # Student.where(:grade => 9..12)
105
+ # Student.where(grade: 9..12)
111
106
  #
112
107
  # An array may be used in the hash to use the SQL IN operator:
113
108
  #
114
- # Student.where(:grade => [9,11,12])
109
+ # Student.where(grade: [9,11,12])
115
110
  #
116
111
  # When joining tables, nested hashes or keys written in the form 'table_name.column_name'
117
112
  # can be used to qualify the table name of a particular condition. For instance:
118
113
  #
119
- # Student.joins(:schools).where(:schools => { :category => 'public' })
114
+ # Student.joins(:schools).where(schools: { category: 'public' })
120
115
  # Student.joins(:schools).where('schools.category' => 'public' )
121
116
  #
122
117
  # == Overwriting default accessors
@@ -124,36 +119,36 @@ module ActiveRecord #:nodoc:
124
119
  # All column values are automatically available through basic accessors on the Active Record
125
120
  # object, but sometimes you want to specialize this behavior. This can be done by overwriting
126
121
  # the default accessors (using the same name as the attribute) and calling
127
- # <tt>read_attribute(attr_name)</tt> and <tt>write_attribute(attr_name, value)</tt> to actually
128
- # change things.
122
+ # +super+ to actually change things.
129
123
  #
130
124
  # class Song < ActiveRecord::Base
131
125
  # # Uses an integer of seconds to hold the length of the song
132
126
  #
133
127
  # def length=(minutes)
134
- # write_attribute(:length, minutes.to_i * 60)
128
+ # super(minutes.to_i * 60)
135
129
  # end
136
130
  #
137
131
  # def length
138
- # read_attribute(:length) / 60
132
+ # super / 60
139
133
  # end
140
134
  # end
141
135
  #
142
136
  # You can alternatively use <tt>self[:attribute]=(value)</tt> and <tt>self[:attribute]</tt>
143
- # instead of <tt>write_attribute(:attribute, value)</tt> and <tt>read_attribute(:attribute)</tt>.
137
+ # or <tt>write_attribute(:attribute, value)</tt> and <tt>read_attribute(:attribute)</tt>.
144
138
  #
145
139
  # == Attribute query methods
146
140
  #
147
141
  # In addition to the basic accessors, query methods are also automatically available on the Active Record object.
148
142
  # Query methods allow you to test whether an attribute value is present.
143
+ # For numeric values, present is defined as non-zero.
149
144
  #
150
145
  # For example, an Active Record User with the <tt>name</tt> attribute has a <tt>name?</tt> method that you can call
151
146
  # to determine whether the user has a name:
152
147
  #
153
- # user = User.new(:name => "David")
148
+ # user = User.new(name: "David")
154
149
  # user.name? # => true
155
150
  #
156
- # anonymous = User.new(:name => "")
151
+ # anonymous = User.new(name: "")
157
152
  # anonymous.name? # => false
158
153
  #
159
154
  # == Accessing attributes before they have been typecasted
@@ -169,59 +164,24 @@ module ActiveRecord #:nodoc:
169
164
  #
170
165
  # == Dynamic attribute-based finders
171
166
  #
172
- # Dynamic attribute-based finders are a cleaner way of getting (and/or creating) objects
167
+ # Dynamic attribute-based finders are a mildly deprecated way of getting (and/or creating) objects
173
168
  # by simple queries without turning to SQL. They work by appending the name of an attribute
174
- # to <tt>find_by_</tt>, <tt>find_last_by_</tt>, or <tt>find_all_by_</tt> and thus produces finders
175
- # like <tt>Person.find_by_user_name</tt>, <tt>Person.find_all_by_last_name</tt>, and
176
- # <tt>Payment.find_by_transaction_id</tt>. Instead of writing
177
- # <tt>Person.where(:user_name => user_name).first</tt>, you just do <tt>Person.find_by_user_name(user_name)</tt>.
178
- # And instead of writing <tt>Person.where(:last_name => last_name).all</tt>, you just do
179
- # <tt>Person.find_all_by_last_name(last_name)</tt>.
169
+ # to <tt>find_by_</tt> like <tt>Person.find_by_user_name</tt>.
170
+ # Instead of writing <tt>Person.find_by(user_name: user_name)</tt>, you can use
171
+ # <tt>Person.find_by_user_name(user_name)</tt>.
172
+ #
173
+ # It's possible to add an exclamation point (!) on the end of the dynamic finders to get them to raise an
174
+ # <tt>ActiveRecord::RecordNotFound</tt> error if they do not return any records,
175
+ # like <tt>Person.find_by_last_name!</tt>.
180
176
  #
181
177
  # It's also possible to use multiple attributes in the same find by separating them with "_and_".
182
178
  #
183
- # Person.where(:user_name => user_name, :password => password).first
179
+ # Person.find_by(user_name: user_name, password: password)
184
180
  # Person.find_by_user_name_and_password(user_name, password) # with dynamic finder
185
181
  #
186
182
  # It's even possible to call these dynamic finder methods on relations and named scopes.
187
183
  #
188
- # Payment.order("created_on").find_all_by_amount(50)
189
- # Payment.pending.find_last_by_amount(100)
190
- #
191
- # The same dynamic finder style can be used to create the object if it doesn't already exist.
192
- # This dynamic finder is called with <tt>find_or_create_by_</tt> and will return the object if
193
- # it already exists and otherwise creates it, then returns it. Protected attributes won't be set
194
- # unless they are given in a block.
195
- #
196
- # # No 'Summer' tag exists
197
- # Tag.find_or_create_by_name("Summer") # equal to Tag.create(:name => "Summer")
198
- #
199
- # # Now the 'Summer' tag does exist
200
- # Tag.find_or_create_by_name("Summer") # equal to Tag.find_by_name("Summer")
201
- #
202
- # # Now 'Bob' exist and is an 'admin'
203
- # User.find_or_create_by_name('Bob', :age => 40) { |u| u.admin = true }
204
- #
205
- # Use the <tt>find_or_initialize_by_</tt> finder if you want to return a new record without
206
- # saving it first. Protected attributes won't be set unless they are given in a block.
207
- #
208
- # # No 'Winter' tag exists
209
- # winter = Tag.find_or_initialize_by_name("Winter")
210
- # winter.persisted? # false
211
- #
212
- # To find by a subset of the attributes to be used for instantiating a new object, pass a hash instead of
213
- # a list of parameters.
214
- #
215
- # Tag.find_or_create_by_name(:name => "rails", :creator => current_user)
216
- #
217
- # That will either find an existing tag named "rails", or create a new one while setting the
218
- # user that created it.
219
- #
220
- # Just like <tt>find_by_*</tt>, you can also use <tt>scoped_by_*</tt> to retrieve data. The good thing about
221
- # using this feature is that the very first time result is returned using <tt>method_missing</tt> technique
222
- # but after that the method is declared on the class. Henceforth <tt>method_missing</tt> will not be hit.
223
- #
224
- # User.scoped_by_user_name('David')
184
+ # Payment.order("created_on").find_by_amount(50)
225
185
  #
226
186
  # == Saving arrays, hashes, and other non-mappable objects in text columns
227
187
  #
@@ -234,7 +194,7 @@ module ActiveRecord #:nodoc:
234
194
  # serialize :preferences
235
195
  # end
236
196
  #
237
- # user = User.create(:preferences => { "background" => "black", "display" => large })
197
+ # user = User.create(preferences: { "background" => "black", "display" => large })
238
198
  # User.find(user.id).preferences # => { "background" => "black", "display" => large }
239
199
  #
240
200
  # You can also specify a class option as the second parameter that'll raise an exception
@@ -244,7 +204,7 @@ module ActiveRecord #:nodoc:
244
204
  # serialize :preferences, Hash
245
205
  # end
246
206
  #
247
- # user = User.create(:preferences => %w( one two three ))
207
+ # user = User.create(preferences: %w( one two three ))
248
208
  # User.find(user.id).preferences # raises SerializationTypeMismatch
249
209
  #
250
210
  # When you specify a class option, the default value for that attribute will be a new
@@ -260,25 +220,9 @@ module ActiveRecord #:nodoc:
260
220
  #
261
221
  # == Single table inheritance
262
222
  #
263
- # Active Record allows inheritance by storing the name of the class in a column that by
264
- # default is named "type" (can be changed by overwriting <tt>Base.inheritance_column</tt>).
265
- # This means that an inheritance looking like this:
266
- #
267
- # class Company < ActiveRecord::Base; end
268
- # class Firm < Company; end
269
- # class Client < Company; end
270
- # class PriorityClient < Client; end
271
- #
272
- # When you do <tt>Firm.create(:name => "37signals")</tt>, this record will be saved in
273
- # the companies table with type = "Firm". You can then fetch this row again using
274
- # <tt>Company.where(:name => '37signals').first</tt> and it will return a Firm object.
275
- #
276
- # If you don't have a type column defined in your table, single-table inheritance won't
277
- # be triggered. In that case, it'll work just like normal subclasses with no special magic
278
- # for differentiating between them or reloading the right type with find.
279
- #
280
- # Note, all the attributes for all the cases are kept in the same table. Read more:
281
- # http://www.martinfowler.com/eaaCatalog/singleTableInheritance.html
223
+ # Active Record allows inheritance by storing the name of the class in a
224
+ # column that is named "type" by default. See ActiveRecord::Inheritance for
225
+ # more details.
282
226
  #
283
227
  # == Connection to multiple databases in different models
284
228
  #
@@ -302,1900 +246,71 @@ module ActiveRecord #:nodoc:
302
246
  # (or a bad spelling of an existing one).
303
247
  # * AssociationTypeMismatch - The object assigned to the association wasn't of the type
304
248
  # specified in the association definition.
305
- # * SerializationTypeMismatch - The serialized object wasn't of the class specified as the second parameter.
306
- # * ConnectionNotEstablished+ - No connection has been established. Use <tt>establish_connection</tt>
249
+ # * AttributeAssignmentError - An error occurred while doing a mass assignment through the
250
+ # <tt>attributes=</tt> method.
251
+ # You can inspect the +attribute+ property of the exception object to determine which attribute
252
+ # triggered the error.
253
+ # * ConnectionNotEstablished - No connection has been established. Use <tt>establish_connection</tt>
307
254
  # before querying.
308
- # * RecordNotFound - No record responded to the +find+ method. Either the row with the given ID doesn't exist
309
- # or the row didn't meet the additional restrictions. Some +find+ calls do not raise this exception to signal
310
- # nothing was found, please check its documentation for further details.
311
- # * StatementInvalid - The database server rejected the SQL statement. The precise error is added in the message.
312
255
  # * MultiparameterAssignmentErrors - Collection of errors that occurred during a mass assignment using the
313
256
  # <tt>attributes=</tt> method. The +errors+ property of this exception contains an array of
314
257
  # AttributeAssignmentError
315
258
  # objects that should be inspected to determine which attributes triggered the errors.
316
- # * AttributeAssignmentError - An error occurred while doing a mass assignment through the
317
- # <tt>attributes=</tt> method.
318
- # You can inspect the +attribute+ property of the exception object to determine which attribute
319
- # triggered the error.
259
+ # * RecordInvalid - raised by save! and create! when the record is invalid.
260
+ # * RecordNotFound - No record responded to the +find+ method. Either the row with the given ID doesn't exist
261
+ # or the row didn't meet the additional restrictions. Some +find+ calls do not raise this exception to signal
262
+ # nothing was found, please check its documentation for further details.
263
+ # * SerializationTypeMismatch - The serialized object wasn't of the class specified as the second parameter.
264
+ # * StatementInvalid - The database server rejected the SQL statement. The precise error is added in the message.
320
265
  #
321
266
  # *Note*: The attributes listed are class-level attributes (accessible from both the class and instance level).
322
267
  # So it's possible to assign a logger to the class through <tt>Base.logger=</tt> which will then be used by all
323
268
  # instances in the current object space.
324
269
  class Base
325
- ##
326
- # :singleton-method:
327
- # Accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class,
328
- # which is then passed on to any new database connections made and which can be retrieved on both
329
- # a class and instance level by calling +logger+.
330
- cattr_accessor :logger, :instance_writer => false
331
-
332
- ##
333
- # :singleton-method:
334
- # Contains the database configuration - as is typically stored in config/database.yml -
335
- # as a Hash.
336
- #
337
- # For example, the following database.yml...
338
- #
339
- # development:
340
- # adapter: sqlite3
341
- # database: db/development.sqlite3
342
- #
343
- # production:
344
- # adapter: sqlite3
345
- # database: db/production.sqlite3
346
- #
347
- # ...would result in ActiveRecord::Base.configurations to look like this:
348
- #
349
- # {
350
- # 'development' => {
351
- # 'adapter' => 'sqlite3',
352
- # 'database' => 'db/development.sqlite3'
353
- # },
354
- # 'production' => {
355
- # 'adapter' => 'sqlite3',
356
- # 'database' => 'db/production.sqlite3'
357
- # }
358
- # }
359
- cattr_accessor :configurations, :instance_writer => false
360
- @@configurations = {}
361
-
362
- ##
363
- # :singleton-method:
364
- # Accessor for the prefix type that will be prepended to every primary key column name.
365
- # The options are :table_name and :table_name_with_underscore. If the first is specified,
366
- # the Product class will look for "productid" instead of "id" as the primary column. If the
367
- # latter is specified, the Product class will look for "product_id" instead of "id". Remember
368
- # that this is a global setting for all Active Records.
369
- cattr_accessor :primary_key_prefix_type, :instance_writer => false
370
- @@primary_key_prefix_type = nil
371
-
372
- ##
373
- # :singleton-method:
374
- # Accessor for the name of the prefix string to prepend to every table name. So if set
375
- # to "basecamp_", all table names will be named like "basecamp_projects", "basecamp_people",
376
- # etc. This is a convenient way of creating a namespace for tables in a shared database.
377
- # By default, the prefix is the empty string.
378
- #
379
- # If you are organising your models within modules you can add a prefix to the models within
380
- # a namespace by defining a singleton method in the parent module called table_name_prefix which
381
- # returns your chosen prefix.
382
- class_attribute :table_name_prefix, :instance_writer => false
383
- self.table_name_prefix = ""
384
-
385
- ##
386
- # :singleton-method:
387
- # Works like +table_name_prefix+, but appends instead of prepends (set to "_basecamp" gives "projects_basecamp",
388
- # "people_basecamp"). By default, the suffix is the empty string.
389
- class_attribute :table_name_suffix, :instance_writer => false
390
- self.table_name_suffix = ""
391
-
392
- ##
393
- # :singleton-method:
394
- # Indicates whether table names should be the pluralized versions of the corresponding class names.
395
- # If true, the default table name for a Product class will be +products+. If false, it would just be +product+.
396
- # See table_name for the full rules on table/class naming. This is true, by default.
397
- class_attribute :pluralize_table_names, :instance_writer => false
398
- self.pluralize_table_names = true
399
-
400
- ##
401
- # :singleton-method:
402
- # Determines whether to use Time.local (using :local) or Time.utc (using :utc) when pulling
403
- # dates and times from the database. This is set to :local by default.
404
- cattr_accessor :default_timezone, :instance_writer => false
405
- @@default_timezone = :local
406
-
407
- ##
408
- # :singleton-method:
409
- # Specifies the format to use when dumping the database schema with Rails'
410
- # Rakefile. If :sql, the schema is dumped as (potentially database-
411
- # specific) SQL statements. If :ruby, the schema is dumped as an
412
- # ActiveRecord::Schema file which can be loaded into any database that
413
- # supports migrations. Use :ruby if you want to have different database
414
- # adapters for, e.g., your development and test environments.
415
- cattr_accessor :schema_format , :instance_writer => false
416
- @@schema_format = :ruby
417
-
418
- ##
419
- # :singleton-method:
420
- # Specify whether or not to use timestamps for migration versions
421
- cattr_accessor :timestamped_migrations , :instance_writer => false
422
- @@timestamped_migrations = true
423
-
424
- # Determine whether to store the full constant name including namespace when using STI
425
- class_attribute :store_full_sti_class
426
- self.store_full_sti_class = true
427
-
428
- # Stores the default scope for the class
429
- class_attribute :default_scopes, :instance_writer => false
430
- self.default_scopes = []
431
-
432
- # Returns a hash of all the attributes that have been specified for serialization as
433
- # keys and their class restriction as values.
434
- class_attribute :serialized_attributes
435
- self.serialized_attributes = {}
436
-
437
- class_attribute :_attr_readonly, :instance_writer => false
438
- self._attr_readonly = []
439
-
440
- class << self # Class methods
441
- delegate :find, :first, :first!, :last, :last!, :all, :exists?, :any?, :many?, :to => :scoped
442
- delegate :destroy, :destroy_all, :delete, :delete_all, :update, :update_all, :to => :scoped
443
- delegate :find_each, :find_in_batches, :to => :scoped
444
- delegate :select, :group, :order, :except, :reorder, :limit, :offset, :joins, :where, :preload, :eager_load, :includes, :from, :lock, :readonly, :having, :create_with, :to => :scoped
445
- delegate :count, :average, :minimum, :maximum, :sum, :calculate, :to => :scoped
446
-
447
- # Executes a custom SQL query against your database and returns all the results. The results will
448
- # be returned as an array with columns requested encapsulated as attributes of the model you call
449
- # this method from. If you call <tt>Product.find_by_sql</tt> then the results will be returned in
450
- # a Product object with the attributes you specified in the SQL query.
451
- #
452
- # If you call a complicated SQL query which spans multiple tables the columns specified by the
453
- # SELECT will be attributes of the model, whether or not they are columns of the corresponding
454
- # table.
455
- #
456
- # The +sql+ parameter is a full SQL query as a string. It will be called as is, there will be
457
- # no database agnostic conversions performed. This should be a last resort because using, for example,
458
- # MySQL specific terms will lock you to using that particular database engine or require you to
459
- # change your call if you switch engines.
460
- #
461
- # ==== Examples
462
- # # A simple SQL query spanning multiple tables
463
- # Post.find_by_sql "SELECT p.title, c.author FROM posts p, comments c WHERE p.id = c.post_id"
464
- # > [#<Post:0x36bff9c @attributes={"title"=>"Ruby Meetup", "first_name"=>"Quentin"}>, ...]
465
- #
466
- # # You can use the same string replacement techniques as you can with ActiveRecord#find
467
- # Post.find_by_sql ["SELECT title FROM posts WHERE author = ? AND created > ?", author_id, start_date]
468
- # > [#<Post:0x36bff9c @attributes={"title"=>"The Cheap Man Buys Twice"}>, ...]
469
- def find_by_sql(sql, binds = [])
470
- connection.select_all(sanitize_sql(sql), "#{name} Load", binds).collect! { |record| instantiate(record) }
471
- end
472
-
473
- # Creates an object (or multiple objects) and saves it to the database, if validations pass.
474
- # The resulting object is returned whether the object was saved successfully to the database or not.
475
- #
476
- # The +attributes+ parameter can be either be a Hash or an Array of Hashes. These Hashes describe the
477
- # attributes on the objects that are to be created.
478
- #
479
- # +create+ respects mass-assignment security and accepts either +:as+ or +:without_protection+ options
480
- # in the +options+ parameter.
481
- #
482
- # ==== Examples
483
- # # Create a single new object
484
- # User.create(:first_name => 'Jamie')
485
- #
486
- # # Create a single new object using the :admin mass-assignment security role
487
- # User.create({ :first_name => 'Jamie', :is_admin => true }, :as => :admin)
488
- #
489
- # # Create a single new object bypassing mass-assignment security
490
- # User.create({ :first_name => 'Jamie', :is_admin => true }, :without_protection => true)
491
- #
492
- # # Create an Array of new objects
493
- # User.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }])
494
- #
495
- # # Create a single object and pass it into a block to set other attributes.
496
- # User.create(:first_name => 'Jamie') do |u|
497
- # u.is_admin = false
498
- # end
499
- #
500
- # # Creating an Array of new objects using a block, where the block is executed for each object:
501
- # User.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }]) do |u|
502
- # u.is_admin = false
503
- # end
504
- def create(attributes = nil, options = {}, &block)
505
- if attributes.is_a?(Array)
506
- attributes.collect { |attr| create(attr, options, &block) }
507
- else
508
- object = new(attributes, options)
509
- yield(object) if block_given?
510
- object.save
511
- object
512
- end
513
- end
514
-
515
- # Returns the result of an SQL statement that should only include a COUNT(*) in the SELECT part.
516
- # The use of this method should be restricted to complicated SQL queries that can't be executed
517
- # using the ActiveRecord::Calculations class methods. Look into those before using this.
518
- #
519
- # ==== Parameters
520
- #
521
- # * +sql+ - An SQL statement which should return a count query from the database, see the example below.
522
- #
523
- # ==== Examples
524
- #
525
- # Product.count_by_sql "SELECT COUNT(*) FROM sales s, customers c WHERE s.customer_id = c.id"
526
- def count_by_sql(sql)
527
- sql = sanitize_conditions(sql)
528
- connection.select_value(sql, "#{name} Count").to_i
529
- end
530
-
531
- # Attributes listed as readonly will be used to create a new record but update operations will
532
- # ignore these fields.
533
- def attr_readonly(*attributes)
534
- self._attr_readonly = Set.new(attributes.map { |a| a.to_s }) + (self._attr_readonly || [])
535
- end
536
-
537
- # Returns an array of all the attributes that have been specified as readonly.
538
- def readonly_attributes
539
- self._attr_readonly
540
- end
541
-
542
- # If you have an attribute that needs to be saved to the database as an object, and retrieved as the same object,
543
- # then specify the name of that attribute using this method and it will be handled automatically.
544
- # The serialization is done through YAML. If +class_name+ is specified, the serialized object must be of that
545
- # class on retrieval or SerializationTypeMismatch will be raised.
546
- #
547
- # ==== Parameters
548
- #
549
- # * +attr_name+ - The field name that should be serialized.
550
- # * +class_name+ - Optional, class name that the object type should be equal to.
551
- #
552
- # ==== Example
553
- # # Serialize a preferences attribute
554
- # class User < ActiveRecord::Base
555
- # serialize :preferences
556
- # end
557
- def serialize(attr_name, class_name = Object)
558
- coder = if [:load, :dump].all? { |x| class_name.respond_to?(x) }
559
- class_name
560
- else
561
- Coders::YAMLColumn.new(class_name)
562
- end
563
-
564
- # merge new serialized attribute and create new hash to ensure that each class in inheritance hierarchy
565
- # has its own hash of own serialized attributes
566
- self.serialized_attributes = serialized_attributes.merge(attr_name.to_s => coder)
567
- end
568
-
569
- # Guesses the table name (in forced lower-case) based on the name of the class in the
570
- # inheritance hierarchy descending directly from ActiveRecord::Base. So if the hierarchy
571
- # looks like: Reply < Message < ActiveRecord::Base, then Message is used
572
- # to guess the table name even when called on Reply. The rules used to do the guess
573
- # are handled by the Inflector class in Active Support, which knows almost all common
574
- # English inflections. You can add new inflections in config/initializers/inflections.rb.
575
- #
576
- # Nested classes are given table names prefixed by the singular form of
577
- # the parent's table name. Enclosing modules are not considered.
578
- #
579
- # ==== Examples
580
- #
581
- # class Invoice < ActiveRecord::Base
582
- # end
583
- #
584
- # file class table_name
585
- # invoice.rb Invoice invoices
586
- #
587
- # class Invoice < ActiveRecord::Base
588
- # class Lineitem < ActiveRecord::Base
589
- # end
590
- # end
591
- #
592
- # file class table_name
593
- # invoice.rb Invoice::Lineitem invoice_lineitems
594
- #
595
- # module Invoice
596
- # class Lineitem < ActiveRecord::Base
597
- # end
598
- # end
599
- #
600
- # file class table_name
601
- # invoice/lineitem.rb Invoice::Lineitem lineitems
602
- #
603
- # Additionally, the class-level +table_name_prefix+ is prepended and the
604
- # +table_name_suffix+ is appended. So if you have "myapp_" as a prefix,
605
- # the table name guess for an Invoice class becomes "myapp_invoices".
606
- # Invoice::Lineitem becomes "myapp_invoice_lineitems".
607
- #
608
- # You can also overwrite this class method to allow for unguessable
609
- # links, such as a Mouse class with a link to a "mice" table. Example:
610
- #
611
- # class Mouse < ActiveRecord::Base
612
- # set_table_name "mice"
613
- # end
614
- def table_name
615
- reset_table_name
616
- end
617
-
618
- # Returns a quoted version of the table name, used to construct SQL statements.
619
- def quoted_table_name
620
- @quoted_table_name ||= connection.quote_table_name(table_name)
621
- end
622
-
623
- # Computes the table name, (re)sets it internally, and returns it.
624
- def reset_table_name #:nodoc:
625
- return if abstract_class?
626
-
627
- self.table_name = compute_table_name
628
- end
629
-
630
- def full_table_name_prefix #:nodoc:
631
- (parents.detect{ |p| p.respond_to?(:table_name_prefix) } || self).table_name_prefix
632
- end
633
-
634
- # Defines the column name for use with single table inheritance. Use
635
- # <tt>set_inheritance_column</tt> to set a different value.
636
- def inheritance_column
637
- @inheritance_column ||= "type"
638
- end
639
-
640
- # Lazy-set the sequence name to the connection's default. This method
641
- # is only ever called once since set_sequence_name overrides it.
642
- def sequence_name #:nodoc:
643
- reset_sequence_name
644
- end
645
-
646
- def reset_sequence_name #:nodoc:
647
- default = connection.default_sequence_name(table_name, primary_key)
648
- set_sequence_name(default)
649
- default
650
- end
651
-
652
- # Sets the table name. If the value is nil or false then the value returned by the given
653
- # block is used.
654
- #
655
- # class Project < ActiveRecord::Base
656
- # set_table_name "project"
657
- # end
658
- def set_table_name(value = nil, &block)
659
- @quoted_table_name = nil
660
- define_attr_method :table_name, value, &block
661
- @arel_table = nil
662
-
663
- @arel_table = Arel::Table.new(table_name, arel_engine)
664
- @relation = Relation.new(self, arel_table)
665
- end
666
- alias :table_name= :set_table_name
667
-
668
- # Sets the name of the inheritance column to use to the given value,
669
- # or (if the value # is nil or false) to the value returned by the
670
- # given block.
671
- #
672
- # class Project < ActiveRecord::Base
673
- # set_inheritance_column do
674
- # original_inheritance_column + "_id"
675
- # end
676
- # end
677
- def set_inheritance_column(value = nil, &block)
678
- define_attr_method :inheritance_column, value, &block
679
- end
680
- alias :inheritance_column= :set_inheritance_column
681
-
682
- # Sets the name of the sequence to use when generating ids to the given
683
- # value, or (if the value is nil or false) to the value returned by the
684
- # given block. This is required for Oracle and is useful for any
685
- # database which relies on sequences for primary key generation.
686
- #
687
- # If a sequence name is not explicitly set when using Oracle or Firebird,
688
- # it will default to the commonly used pattern of: #{table_name}_seq
689
- #
690
- # If a sequence name is not explicitly set when using PostgreSQL, it
691
- # will discover the sequence corresponding to your primary key for you.
692
- #
693
- # class Project < ActiveRecord::Base
694
- # set_sequence_name "projectseq" # default would have been "project_seq"
695
- # end
696
- def set_sequence_name(value = nil, &block)
697
- define_attr_method :sequence_name, value, &block
698
- end
699
- alias :sequence_name= :set_sequence_name
700
-
701
- # Indicates whether the table associated with this class exists
702
- def table_exists?
703
- connection.table_exists?(table_name)
704
- end
705
-
706
- # Returns an array of column objects for the table associated with this class.
707
- def columns
708
- if defined?(@primary_key)
709
- connection_pool.primary_keys[table_name] ||= primary_key
710
- end
711
-
712
- connection_pool.columns[table_name]
713
- end
714
-
715
- # Returns a hash of column objects for the table associated with this class.
716
- def columns_hash
717
- connection_pool.columns_hash[table_name]
718
- end
719
-
720
- # Returns a hash where the keys are column names and the values are
721
- # default values when instantiating the AR object for this table.
722
- def column_defaults
723
- connection_pool.column_defaults[table_name]
724
- end
725
-
726
- # Returns an array of column names as strings.
727
- def column_names
728
- @column_names ||= columns.map { |column| column.name }
729
- end
730
-
731
- # Returns an array of column objects where the primary id, all columns ending in "_id" or "_count",
732
- # and columns used for single table inheritance have been removed.
733
- def content_columns
734
- @content_columns ||= columns.reject { |c| c.primary || c.name =~ /(_id|_count)$/ || c.name == inheritance_column }
735
- end
736
-
737
- # Returns a hash of all the methods added to query each of the columns in the table with the name of the method as the key
738
- # and true as the value. This makes it possible to do O(1) lookups in respond_to? to check if a given method for attribute
739
- # is available.
740
- def column_methods_hash #:nodoc:
741
- @dynamic_methods_hash ||= column_names.inject(Hash.new(false)) do |methods, attr|
742
- attr_name = attr.to_s
743
- methods[attr.to_sym] = attr_name
744
- methods["#{attr}=".to_sym] = attr_name
745
- methods["#{attr}?".to_sym] = attr_name
746
- methods["#{attr}_before_type_cast".to_sym] = attr_name
747
- methods
748
- end
749
- end
750
-
751
- # Resets all the cached information about columns, which will cause them
752
- # to be reloaded on the next request.
753
- #
754
- # The most common usage pattern for this method is probably in a migration,
755
- # when just after creating a table you want to populate it with some default
756
- # values, eg:
757
- #
758
- # class CreateJobLevels < ActiveRecord::Migration
759
- # def up
760
- # create_table :job_levels do |t|
761
- # t.integer :id
762
- # t.string :name
763
- #
764
- # t.timestamps
765
- # end
766
- #
767
- # JobLevel.reset_column_information
768
- # %w{assistant executive manager director}.each do |type|
769
- # JobLevel.create(:name => type)
770
- # end
771
- # end
772
- #
773
- # def down
774
- # drop_table :job_levels
775
- # end
776
- # end
777
- def reset_column_information
778
- connection.clear_cache!
779
- undefine_attribute_methods
780
- connection_pool.clear_table_cache!(table_name) if table_exists?
781
-
782
- @column_names = @content_columns = @dynamic_methods_hash = @inheritance_column = nil
783
- @arel_engine = @relation = nil
784
- end
785
-
786
- def clear_cache! # :nodoc:
787
- connection_pool.clear_cache!
788
- end
789
-
790
- def attribute_method?(attribute)
791
- super || (table_exists? && column_names.include?(attribute.to_s.sub(/=$/, '')))
792
- end
793
-
794
- # Returns an array of column names as strings if it's not
795
- # an abstract class and table exists.
796
- # Otherwise it returns an empty array.
797
- def attribute_names
798
- @attribute_names ||= if !abstract_class? && table_exists?
799
- column_names
800
- else
801
- []
802
- end
803
- end
804
-
805
- # Set the lookup ancestors for ActiveModel.
806
- def lookup_ancestors #:nodoc:
807
- klass = self
808
- classes = [klass]
809
- return classes if klass == ActiveRecord::Base
810
-
811
- while klass != klass.base_class
812
- classes << klass = klass.superclass
813
- end
814
- classes
815
- end
816
-
817
- # Set the i18n scope to overwrite ActiveModel.
818
- def i18n_scope #:nodoc:
819
- :activerecord
820
- end
821
-
822
- # True if this isn't a concrete subclass needing a STI type condition.
823
- def descends_from_active_record?
824
- if superclass.abstract_class?
825
- superclass.descends_from_active_record?
826
- else
827
- superclass == Base || !columns_hash.include?(inheritance_column)
828
- end
829
- end
830
-
831
- def finder_needs_type_condition? #:nodoc:
832
- # This is like this because benchmarking justifies the strange :false stuff
833
- :true == (@finder_needs_type_condition ||= descends_from_active_record? ? :false : :true)
834
- end
835
-
836
- # Returns a string like 'Post(id:integer, title:string, body:text)'
837
- def inspect
838
- if self == Base
839
- super
840
- elsif abstract_class?
841
- "#{super}(abstract)"
842
- elsif table_exists?
843
- attr_list = columns.map { |c| "#{c.name}: #{c.type}" } * ', '
844
- "#{super}(#{attr_list})"
845
- else
846
- "#{super}(Table doesn't exist)"
847
- end
848
- end
849
-
850
- def quote_value(value, column = nil) #:nodoc:
851
- connection.quote(value,column)
852
- end
853
-
854
- # Used to sanitize objects before they're used in an SQL SELECT statement. Delegates to <tt>connection.quote</tt>.
855
- def sanitize(object) #:nodoc:
856
- connection.quote(object)
857
- end
858
-
859
- # Overwrite the default class equality method to provide support for association proxies.
860
- def ===(object)
861
- object.is_a?(self)
862
- end
863
-
864
- def symbolized_base_class
865
- @symbolized_base_class ||= base_class.to_s.to_sym
866
- end
867
-
868
- def symbolized_sti_name
869
- @symbolized_sti_name ||= sti_name.present? ? sti_name.to_sym : symbolized_base_class
870
- end
871
-
872
- # Returns the base AR subclass that this class descends from. If A
873
- # extends AR::Base, A.base_class will return A. If B descends from A
874
- # through some arbitrarily deep hierarchy, B.base_class will return A.
875
- #
876
- # If B < A and C < B and if A is an abstract_class then both B.base_class
877
- # and C.base_class would return B as the answer since A is an abstract_class.
878
- def base_class
879
- class_of_active_record_descendant(self)
880
- end
881
-
882
- # Set this to true if this is an abstract class (see <tt>abstract_class?</tt>).
883
- attr_accessor :abstract_class
884
-
885
- # Returns whether this class is an abstract class or not.
886
- def abstract_class?
887
- defined?(@abstract_class) && @abstract_class == true
888
- end
889
-
890
- def respond_to?(method_id, include_private = false)
891
- if match = DynamicFinderMatch.match(method_id)
892
- return true if all_attributes_exists?(match.attribute_names)
893
- elsif match = DynamicScopeMatch.match(method_id)
894
- return true if all_attributes_exists?(match.attribute_names)
895
- end
896
-
897
- super
898
- end
899
-
900
- def sti_name
901
- store_full_sti_class ? name : name.demodulize
902
- end
903
-
904
- def arel_table
905
- @arel_table ||= Arel::Table.new(table_name, arel_engine)
906
- end
907
-
908
- def arel_engine
909
- @arel_engine ||= begin
910
- if self == ActiveRecord::Base
911
- ActiveRecord::Base
912
- else
913
- connection_handler.connection_pools[name] ? self : superclass.arel_engine
914
- end
915
- end
916
- end
917
-
918
- # Returns a scope for this class without taking into account the default_scope.
919
- #
920
- # class Post < ActiveRecord::Base
921
- # def self.default_scope
922
- # where :published => true
923
- # end
924
- # end
925
- #
926
- # Post.all # Fires "SELECT * FROM posts WHERE published = true"
927
- # Post.unscoped.all # Fires "SELECT * FROM posts"
928
- #
929
- # This method also accepts a block meaning that all queries inside the block will
930
- # not use the default_scope:
931
- #
932
- # Post.unscoped {
933
- # Post.limit(10) # Fires "SELECT * FROM posts LIMIT 10"
934
- # }
935
- #
936
- # It is recommended to use block form of unscoped because chaining unscoped with <tt>scope</tt>
937
- # does not work. Assuming that <tt>published</tt> is a <tt>scope</tt> following two statements are same.
938
- #
939
- # Post.unscoped.published
940
- # Post.published
941
- def unscoped #:nodoc:
942
- block_given? ? relation.scoping { yield } : relation
943
- end
944
-
945
- def before_remove_const #:nodoc:
946
- self.current_scope = nil
947
- end
948
-
949
- # Finder methods must instantiate through this method to work with the
950
- # single-table inheritance model that makes it possible to create
951
- # objects of different types from the same table.
952
- def instantiate(record)
953
- sti_class = find_sti_class(record[inheritance_column])
954
- record_id = sti_class.primary_key && record[sti_class.primary_key]
955
-
956
- if ActiveRecord::IdentityMap.enabled? && record_id
957
- instance = use_identity_map(sti_class, record_id, record)
958
- else
959
- instance = sti_class.allocate.init_with('attributes' => record)
960
- end
961
-
962
- instance
963
- end
964
-
965
- private
966
-
967
- def use_identity_map(sti_class, record_id, record)
968
- if (column = sti_class.columns_hash[sti_class.primary_key]) && column.number?
969
- record_id = record_id.to_i
970
- end
971
-
972
- if instance = IdentityMap.get(sti_class, record_id)
973
- instance.reinit_with('attributes' => record)
974
- else
975
- instance = sti_class.allocate.init_with('attributes' => record)
976
- IdentityMap.add(instance)
977
- end
978
-
979
- instance
980
- end
981
-
982
- def relation #:nodoc:
983
- @relation ||= Relation.new(self, arel_table)
984
-
985
- if finder_needs_type_condition?
986
- @relation.where(type_condition).create_with(inheritance_column.to_sym => sti_name)
987
- else
988
- @relation
989
- end
990
- end
991
-
992
- def find_sti_class(type_name)
993
- if type_name.blank? || !columns_hash.include?(inheritance_column)
994
- self
995
- else
996
- begin
997
- if store_full_sti_class
998
- ActiveSupport::Dependencies.constantize(type_name)
999
- else
1000
- compute_type(type_name)
1001
- end
1002
- rescue NameError
1003
- raise SubclassNotFound,
1004
- "The single-table inheritance mechanism failed to locate the subclass: '#{type_name}'. " +
1005
- "This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. " +
1006
- "Please rename this column if you didn't intend it to be used for storing the inheritance class " +
1007
- "or overwrite #{name}.inheritance_column to use another column for that information."
1008
- end
1009
- end
1010
- end
1011
-
1012
- def construct_finder_arel(options = {}, scope = nil)
1013
- relation = options.is_a?(Hash) ? unscoped.apply_finder_options(options) : options
1014
- relation = scope.merge(relation) if scope
1015
- relation
1016
- end
1017
-
1018
- def type_condition(table = arel_table)
1019
- sti_column = table[inheritance_column.to_sym]
1020
- sti_names = ([self] + descendants).map { |model| model.sti_name }
1021
-
1022
- sti_column.in(sti_names)
1023
- end
1024
-
1025
- # Guesses the table name, but does not decorate it with prefix and suffix information.
1026
- def undecorated_table_name(class_name = base_class.name)
1027
- table_name = class_name.to_s.demodulize.underscore
1028
- table_name = table_name.pluralize if pluralize_table_names
1029
- table_name
1030
- end
1031
-
1032
- # Computes and returns a table name according to default conventions.
1033
- def compute_table_name
1034
- base = base_class
1035
- if self == base
1036
- # Nested classes are prefixed with singular parent table name.
1037
- if parent < ActiveRecord::Base && !parent.abstract_class?
1038
- contained = parent.table_name
1039
- contained = contained.singularize if parent.pluralize_table_names
1040
- contained += '_'
1041
- end
1042
- "#{full_table_name_prefix}#{contained}#{undecorated_table_name(name)}#{table_name_suffix}"
1043
- else
1044
- # STI subclasses always use their superclass' table.
1045
- base.table_name
1046
- end
1047
- end
1048
-
1049
- # Enables dynamic finders like <tt>User.find_by_user_name(user_name)</tt> and
1050
- # <tt>User.scoped_by_user_name(user_name). Refer to Dynamic attribute-based finders
1051
- # section at the top of this file for more detailed information.
1052
- #
1053
- # It's even possible to use all the additional parameters to +find+. For example, the
1054
- # full interface for +find_all_by_amount+ is actually <tt>find_all_by_amount(amount, options)</tt>.
1055
- #
1056
- # Each dynamic finder using <tt>scoped_by_*</tt> is also defined in the class after it
1057
- # is first invoked, so that future attempts to use it do not run through method_missing.
1058
- def method_missing(method_id, *arguments, &block)
1059
- if match = DynamicFinderMatch.match(method_id)
1060
- attribute_names = match.attribute_names
1061
- super unless all_attributes_exists?(attribute_names)
1062
- if !arguments.first.is_a?(Hash) && arguments.size < attribute_names.size
1063
- ActiveSupport::Deprecation.warn(<<-eowarn)
1064
- Calling dynamic finder with less number of arguments than the number of attributes in the method name is deprecated and will raise an ArgumentError in the next version of Rails. Please pass `nil' explicitly to the arguments that are left out.
1065
- eowarn
1066
- end
1067
- if match.finder?
1068
- options = if arguments.length > attribute_names.size
1069
- arguments.extract_options!
1070
- else
1071
- {}
1072
- end
1073
- relation = options.any? ? scoped(options) : scoped
1074
- relation.send :find_by_attributes, match, attribute_names, *arguments
1075
- elsif match.instantiator?
1076
- scoped.send :find_or_instantiator_by_attributes, match, attribute_names, *arguments, &block
1077
- end
1078
- elsif match = DynamicScopeMatch.match(method_id)
1079
- attribute_names = match.attribute_names
1080
- super unless all_attributes_exists?(attribute_names)
1081
- if arguments.size < attribute_names.size
1082
- ActiveSupport::Deprecation.warn(
1083
- "Calling dynamic scope with less number of arguments than the number of attributes in " \
1084
- "the method name is deprecated and will raise an ArgumentError in the next version of Rails. " \
1085
- "Please pass `nil' explicitly to the arguments that are left out."
1086
- )
1087
- end
1088
- if match.scope?
1089
- self.class_eval <<-METHOD, __FILE__, __LINE__ + 1
1090
- def self.#{method_id}(*args) # def self.scoped_by_user_name_and_password(*args)
1091
- attributes = Hash[[:#{attribute_names.join(',:')}].zip(args)] # attributes = Hash[[:user_name, :password].zip(args)]
1092
- #
1093
- scoped(:conditions => attributes) # scoped(:conditions => attributes)
1094
- end # end
1095
- METHOD
1096
- send(method_id, *arguments)
1097
- end
1098
- else
1099
- super
1100
- end
1101
- end
1102
-
1103
- # Similar in purpose to +expand_hash_conditions_for_aggregates+.
1104
- def expand_attribute_names_for_aggregates(attribute_names)
1105
- attribute_names.map { |attribute_name|
1106
- unless (aggregation = reflect_on_aggregation(attribute_name.to_sym)).nil?
1107
- aggregate_mapping(aggregation).map do |field_attr, _|
1108
- field_attr.to_sym
1109
- end
1110
- else
1111
- attribute_name.to_sym
1112
- end
1113
- }.flatten
1114
- end
1115
-
1116
- def all_attributes_exists?(attribute_names)
1117
- (expand_attribute_names_for_aggregates(attribute_names) -
1118
- column_methods_hash.keys).empty?
1119
- end
1120
-
1121
- protected
1122
- # with_scope lets you apply options to inner block incrementally. It takes a hash and the keys must be
1123
- # <tt>:find</tt> or <tt>:create</tt>. <tt>:find</tt> parameter is <tt>Relation</tt> while
1124
- # <tt>:create</tt> parameters are an attributes hash.
1125
- #
1126
- # class Article < ActiveRecord::Base
1127
- # def self.create_with_scope
1128
- # with_scope(:find => where(:blog_id => 1), :create => { :blog_id => 1 }) do
1129
- # find(1) # => SELECT * from articles WHERE blog_id = 1 AND id = 1
1130
- # a = create(1)
1131
- # a.blog_id # => 1
1132
- # end
1133
- # end
1134
- # end
1135
- #
1136
- # In nested scopings, all previous parameters are overwritten by the innermost rule, with the exception of
1137
- # <tt>where</tt>, <tt>includes</tt>, and <tt>joins</tt> operations in <tt>Relation</tt>, which are merged.
1138
- #
1139
- # <tt>joins</tt> operations are uniqued so multiple scopes can join in the same table without table aliasing
1140
- # problems. If you need to join multiple tables, but still want one of the tables to be uniqued, use the
1141
- # array of strings format for your joins.
1142
- #
1143
- # class Article < ActiveRecord::Base
1144
- # def self.find_with_scope
1145
- # with_scope(:find => where(:blog_id => 1).limit(1), :create => { :blog_id => 1 }) do
1146
- # with_scope(:find => limit(10)) do
1147
- # all # => SELECT * from articles WHERE blog_id = 1 LIMIT 10
1148
- # end
1149
- # with_scope(:find => where(:author_id => 3)) do
1150
- # all # => SELECT * from articles WHERE blog_id = 1 AND author_id = 3 LIMIT 1
1151
- # end
1152
- # end
1153
- # end
1154
- # end
1155
- #
1156
- # You can ignore any previous scopings by using the <tt>with_exclusive_scope</tt> method.
1157
- #
1158
- # class Article < ActiveRecord::Base
1159
- # def self.find_with_exclusive_scope
1160
- # with_scope(:find => where(:blog_id => 1).limit(1)) do
1161
- # with_exclusive_scope(:find => limit(10)) do
1162
- # all # => SELECT * from articles LIMIT 10
1163
- # end
1164
- # end
1165
- # end
1166
- # end
1167
- #
1168
- # *Note*: the +:find+ scope also has effect on update and deletion methods, like +update_all+ and +delete_all+.
1169
- def with_scope(scope = {}, action = :merge, &block)
1170
- # If another Active Record class has been passed in, get its current scope
1171
- scope = scope.current_scope if !scope.is_a?(Relation) && scope.respond_to?(:current_scope)
1172
-
1173
- previous_scope = self.current_scope
1174
-
1175
- if scope.is_a?(Hash)
1176
- # Dup first and second level of hash (method and params).
1177
- scope = scope.dup
1178
- scope.each do |method, params|
1179
- scope[method] = params.dup unless params == true
1180
- end
1181
-
1182
- scope.assert_valid_keys([ :find, :create ])
1183
- relation = construct_finder_arel(scope[:find] || {})
1184
- relation.default_scoped = true unless action == :overwrite
1185
-
1186
- if previous_scope && previous_scope.create_with_value && scope[:create]
1187
- scope_for_create = if action == :merge
1188
- previous_scope.create_with_value.merge(scope[:create])
1189
- else
1190
- scope[:create]
1191
- end
1192
-
1193
- relation = relation.create_with(scope_for_create)
1194
- else
1195
- scope_for_create = scope[:create]
1196
- scope_for_create ||= previous_scope.create_with_value if previous_scope
1197
- relation = relation.create_with(scope_for_create) if scope_for_create
1198
- end
1199
-
1200
- scope = relation
1201
- end
1202
-
1203
- scope = previous_scope.merge(scope) if previous_scope && action == :merge
1204
-
1205
- self.current_scope = scope
1206
- begin
1207
- yield
1208
- ensure
1209
- self.current_scope = previous_scope
1210
- end
1211
- end
1212
-
1213
- # Works like with_scope, but discards any nested properties.
1214
- def with_exclusive_scope(method_scoping = {}, &block)
1215
- if method_scoping.values.any? { |e| e.is_a?(ActiveRecord::Relation) }
1216
- raise ArgumentError, <<-MSG
1217
- New finder API can not be used with_exclusive_scope. You can either call unscoped to get an anonymous scope not bound to the default_scope:
1218
-
1219
- User.unscoped.where(:active => true)
1220
-
1221
- Or call unscoped with a block:
1222
-
1223
- User.unscoped do
1224
- User.where(:active => true).all
1225
- end
1226
-
1227
- MSG
1228
- end
1229
- with_scope(method_scoping, :overwrite, &block)
1230
- end
1231
-
1232
- def current_scope #:nodoc:
1233
- Thread.current["#{self}_current_scope"]
1234
- end
1235
-
1236
- def current_scope=(scope) #:nodoc:
1237
- Thread.current["#{self}_current_scope"] = scope
1238
- end
1239
-
1240
- # Use this macro in your model to set a default scope for all operations on
1241
- # the model.
1242
- #
1243
- # class Article < ActiveRecord::Base
1244
- # default_scope where(:published => true)
1245
- # end
1246
- #
1247
- # Article.all # => SELECT * FROM articles WHERE published = true
1248
- #
1249
- # The <tt>default_scope</tt> is also applied while creating/building a record. It is not
1250
- # applied while updating a record.
1251
- #
1252
- # Article.new.published # => true
1253
- # Article.create.published # => true
1254
- #
1255
- # You can also use <tt>default_scope</tt> with a block, in order to have it lazily evaluated:
1256
- #
1257
- # class Article < ActiveRecord::Base
1258
- # default_scope { where(:published_at => Time.now - 1.week) }
1259
- # end
1260
- #
1261
- # (You can also pass any object which responds to <tt>call</tt> to the <tt>default_scope</tt>
1262
- # macro, and it will be called when building the default scope.)
1263
- #
1264
- # If you use multiple <tt>default_scope</tt> declarations in your model then they will
1265
- # be merged together:
1266
- #
1267
- # class Article < ActiveRecord::Base
1268
- # default_scope where(:published => true)
1269
- # default_scope where(:rating => 'G')
1270
- # end
1271
- #
1272
- # Article.all # => SELECT * FROM articles WHERE published = true AND rating = 'G'
1273
- #
1274
- # This is also the case with inheritance and module includes where the parent or module
1275
- # defines a <tt>default_scope</tt> and the child or including class defines a second one.
1276
- #
1277
- # If you need to do more complex things with a default scope, you can alternatively
1278
- # define it as a class method:
1279
- #
1280
- # class Article < ActiveRecord::Base
1281
- # def self.default_scope
1282
- # # Should return a scope, you can call 'super' here etc.
1283
- # end
1284
- # end
1285
- def default_scope(scope = {})
1286
- scope = Proc.new if block_given?
1287
- self.default_scopes = default_scopes + [scope]
1288
- end
1289
-
1290
- def build_default_scope #:nodoc:
1291
- if method(:default_scope).owner != Base.singleton_class
1292
- evaluate_default_scope { default_scope }
1293
- elsif default_scopes.any?
1294
- evaluate_default_scope do
1295
- default_scopes.inject(relation) do |default_scope, scope|
1296
- if scope.is_a?(Hash)
1297
- default_scope.apply_finder_options(scope)
1298
- elsif !scope.is_a?(Relation) && scope.respond_to?(:call)
1299
- default_scope.merge(scope.call)
1300
- else
1301
- default_scope.merge(scope)
1302
- end
1303
- end
1304
- end
1305
- end
1306
- end
1307
-
1308
- def ignore_default_scope? #:nodoc:
1309
- Thread.current["#{self}_ignore_default_scope"]
1310
- end
1311
-
1312
- def ignore_default_scope=(ignore) #:nodoc:
1313
- Thread.current["#{self}_ignore_default_scope"] = ignore
1314
- end
1315
-
1316
- # The ignore_default_scope flag is used to prevent an infinite recursion situation where
1317
- # a default scope references a scope which has a default scope which references a scope...
1318
- def evaluate_default_scope
1319
- return if ignore_default_scope?
1320
-
1321
- begin
1322
- self.ignore_default_scope = true
1323
- yield
1324
- ensure
1325
- self.ignore_default_scope = false
1326
- end
1327
- end
1328
-
1329
- # Returns the class type of the record using the current module as a prefix. So descendants of
1330
- # MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
1331
- def compute_type(type_name)
1332
- if type_name.match(/^::/)
1333
- # If the type is prefixed with a scope operator then we assume that
1334
- # the type_name is an absolute reference.
1335
- ActiveSupport::Dependencies.constantize(type_name)
1336
- else
1337
- # Build a list of candidates to search for
1338
- candidates = []
1339
- name.scan(/::|$/) { candidates.unshift "#{$`}::#{type_name}" }
1340
- candidates << type_name
1341
-
1342
- candidates.each do |candidate|
1343
- begin
1344
- constant = ActiveSupport::Dependencies.constantize(candidate)
1345
- return constant if candidate == constant.to_s
1346
- rescue NameError => e
1347
- # We don't want to swallow NoMethodError < NameError errors
1348
- raise e unless e.instance_of?(NameError)
1349
- end
1350
- end
1351
-
1352
- raise NameError, "uninitialized constant #{candidates.first}"
1353
- end
1354
- end
1355
-
1356
- # Returns the class descending directly from ActiveRecord::Base or an
1357
- # abstract class, if any, in the inheritance hierarchy.
1358
- def class_of_active_record_descendant(klass)
1359
- if klass.superclass == Base || klass.superclass.abstract_class?
1360
- klass
1361
- elsif klass.superclass.nil?
1362
- raise ActiveRecordError, "#{name} doesn't belong in a hierarchy descending from ActiveRecord"
1363
- else
1364
- class_of_active_record_descendant(klass.superclass)
1365
- end
1366
- end
1367
-
1368
- # Accepts an array, hash, or string of SQL conditions and sanitizes
1369
- # them into a valid SQL fragment for a WHERE clause.
1370
- # ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'"
1371
- # { :name => "foo'bar", :group_id => 4 } returns "name='foo''bar' and group_id='4'"
1372
- # "name='foo''bar' and group_id='4'" returns "name='foo''bar' and group_id='4'"
1373
- def sanitize_sql_for_conditions(condition, table_name = self.table_name)
1374
- return nil if condition.blank?
1375
-
1376
- case condition
1377
- when Array; sanitize_sql_array(condition)
1378
- when Hash; sanitize_sql_hash_for_conditions(condition, table_name)
1379
- else condition
1380
- end
1381
- end
1382
- alias_method :sanitize_sql, :sanitize_sql_for_conditions
1383
-
1384
- # Accepts an array, hash, or string of SQL conditions and sanitizes
1385
- # them into a valid SQL fragment for a SET clause.
1386
- # { :name => nil, :group_id => 4 } returns "name = NULL , group_id='4'"
1387
- def sanitize_sql_for_assignment(assignments)
1388
- case assignments
1389
- when Array; sanitize_sql_array(assignments)
1390
- when Hash; sanitize_sql_hash_for_assignment(assignments)
1391
- else assignments
1392
- end
1393
- end
1394
-
1395
- def aggregate_mapping(reflection)
1396
- mapping = reflection.options[:mapping] || [reflection.name, reflection.name]
1397
- mapping.first.is_a?(Array) ? mapping : [mapping]
1398
- end
1399
-
1400
- # Accepts a hash of SQL conditions and replaces those attributes
1401
- # that correspond to a +composed_of+ relationship with their expanded
1402
- # aggregate attribute values.
1403
- # Given:
1404
- # class Person < ActiveRecord::Base
1405
- # composed_of :address, :class_name => "Address",
1406
- # :mapping => [%w(address_street street), %w(address_city city)]
1407
- # end
1408
- # Then:
1409
- # { :address => Address.new("813 abc st.", "chicago") }
1410
- # # => { :address_street => "813 abc st.", :address_city => "chicago" }
1411
- def expand_hash_conditions_for_aggregates(attrs)
1412
- expanded_attrs = {}
1413
- attrs.each do |attr, value|
1414
- unless (aggregation = reflect_on_aggregation(attr.to_sym)).nil?
1415
- mapping = aggregate_mapping(aggregation)
1416
- mapping.each do |field_attr, aggregate_attr|
1417
- if mapping.size == 1 && !value.respond_to?(aggregate_attr)
1418
- expanded_attrs[field_attr] = value
1419
- else
1420
- expanded_attrs[field_attr] = value.send(aggregate_attr)
1421
- end
1422
- end
1423
- else
1424
- expanded_attrs[attr] = value
1425
- end
1426
- end
1427
- expanded_attrs
1428
- end
1429
-
1430
- # Sanitizes a hash of attribute/value pairs into SQL conditions for a WHERE clause.
1431
- # { :name => "foo'bar", :group_id => 4 }
1432
- # # => "name='foo''bar' and group_id= 4"
1433
- # { :status => nil, :group_id => [1,2,3] }
1434
- # # => "status IS NULL and group_id IN (1,2,3)"
1435
- # { :age => 13..18 }
1436
- # # => "age BETWEEN 13 AND 18"
1437
- # { 'other_records.id' => 7 }
1438
- # # => "`other_records`.`id` = 7"
1439
- # { :other_records => { :id => 7 } }
1440
- # # => "`other_records`.`id` = 7"
1441
- # And for value objects on a composed_of relationship:
1442
- # { :address => Address.new("123 abc st.", "chicago") }
1443
- # # => "address_street='123 abc st.' and address_city='chicago'"
1444
- def sanitize_sql_hash_for_conditions(attrs, default_table_name = self.table_name)
1445
- attrs = expand_hash_conditions_for_aggregates(attrs)
1446
-
1447
- table = Arel::Table.new(table_name).alias(default_table_name)
1448
- PredicateBuilder.build_from_hash(arel_engine, attrs, table).map { |b|
1449
- connection.visitor.accept b
1450
- }.join(' AND ')
1451
- end
1452
- alias_method :sanitize_sql_hash, :sanitize_sql_hash_for_conditions
1453
-
1454
- # Sanitizes a hash of attribute/value pairs into SQL conditions for a SET clause.
1455
- # { :status => nil, :group_id => 1 }
1456
- # # => "status = NULL , group_id = 1"
1457
- def sanitize_sql_hash_for_assignment(attrs)
1458
- attrs.map do |attr, value|
1459
- "#{connection.quote_column_name(attr)} = #{quote_bound_value(value)}"
1460
- end.join(', ')
1461
- end
1462
-
1463
- # Accepts an array of conditions. The array has each value
1464
- # sanitized and interpolated into the SQL statement.
1465
- # ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'"
1466
- def sanitize_sql_array(ary)
1467
- statement, *values = ary
1468
- if values.first.is_a?(Hash) && statement =~ /:\w+/
1469
- replace_named_bind_variables(statement, values.first)
1470
- elsif statement.include?('?')
1471
- replace_bind_variables(statement, values)
1472
- elsif statement.blank?
1473
- statement
1474
- else
1475
- statement % values.collect { |value| connection.quote_string(value.to_s) }
1476
- end
1477
- end
1478
-
1479
- alias_method :sanitize_conditions, :sanitize_sql
1480
-
1481
- def replace_bind_variables(statement, values) #:nodoc:
1482
- raise_if_bind_arity_mismatch(statement, statement.count('?'), values.size)
1483
- bound = values.dup
1484
- c = connection
1485
- statement.gsub('?') { quote_bound_value(bound.shift, c) }
1486
- end
1487
-
1488
- def replace_named_bind_variables(statement, bind_vars) #:nodoc:
1489
- statement.gsub(/(:?):([a-zA-Z]\w*)/) do
1490
- if $1 == ':' # skip postgresql casts
1491
- $& # return the whole match
1492
- elsif bind_vars.include?(match = $2.to_sym)
1493
- quote_bound_value(bind_vars[match])
1494
- else
1495
- raise PreparedStatementInvalid, "missing value for :#{match} in #{statement}"
1496
- end
1497
- end
1498
- end
1499
-
1500
- def expand_range_bind_variables(bind_vars) #:nodoc:
1501
- expanded = []
1502
-
1503
- bind_vars.each do |var|
1504
- next if var.is_a?(Hash)
1505
-
1506
- if var.is_a?(Range)
1507
- expanded << var.first
1508
- expanded << var.last
1509
- else
1510
- expanded << var
1511
- end
1512
- end
1513
-
1514
- expanded
1515
- end
1516
-
1517
- def quote_bound_value(value, c = connection) #:nodoc:
1518
- if value.respond_to?(:map) && !value.acts_like?(:string)
1519
- if value.respond_to?(:empty?) && value.empty?
1520
- c.quote(nil)
1521
- else
1522
- value.map { |v| c.quote(v) }.join(',')
1523
- end
1524
- else
1525
- c.quote(value)
1526
- end
1527
- end
1528
-
1529
- def raise_if_bind_arity_mismatch(statement, expected, provided) #:nodoc:
1530
- unless expected == provided
1531
- raise PreparedStatementInvalid, "wrong number of bind variables (#{provided} for #{expected}) in: #{statement}"
1532
- end
1533
- end
1534
-
1535
- def encode_quoted_value(value) #:nodoc:
1536
- quoted_value = connection.quote(value)
1537
- quoted_value = "'#{quoted_value[1..-2].gsub(/\'/, "\\\\'")}'" if quoted_value.include?("\\\'") # (for ruby mode) "
1538
- quoted_value
1539
- end
1540
- end
1541
-
1542
- public
1543
- # New objects can be instantiated as either empty (pass no construction parameter) or pre-set with
1544
- # attributes but not yet saved (pass a hash with key names matching the associated table column names).
1545
- # In both instances, valid attribute keys are determined by the column names of the associated table --
1546
- # hence you can't have attributes that aren't part of the table columns.
1547
- #
1548
- # +initialize+ respects mass-assignment security and accepts either +:as+ or +:without_protection+ options
1549
- # in the +options+ parameter.
1550
- #
1551
- # ==== Examples
1552
- # # Instantiates a single new object
1553
- # User.new(:first_name => 'Jamie')
1554
- #
1555
- # # Instantiates a single new object using the :admin mass-assignment security role
1556
- # User.new({ :first_name => 'Jamie', :is_admin => true }, :as => :admin)
1557
- #
1558
- # # Instantiates a single new object bypassing mass-assignment security
1559
- # User.new({ :first_name => 'Jamie', :is_admin => true }, :without_protection => true)
1560
- def initialize(attributes = nil, options = {})
1561
- @attributes = attributes_from_column_definition
1562
- @association_cache = {}
1563
- @aggregation_cache = {}
1564
- @attributes_cache = {}
1565
- @new_record = true
1566
- @readonly = false
1567
- @destroyed = false
1568
- @marked_for_destruction = false
1569
- @previously_changed = {}
1570
- @changed_attributes = {}
1571
- @relation = nil
1572
-
1573
- ensure_proper_type
1574
- set_serialized_attributes
1575
-
1576
- populate_with_current_scope_attributes
1577
-
1578
- assign_attributes(attributes, options) if attributes
1579
-
1580
- yield self if block_given?
1581
- run_callbacks :initialize
1582
- end
1583
-
1584
- # Populate +coder+ with attributes about this record that should be
1585
- # serialized. The structure of +coder+ defined in this method is
1586
- # guaranteed to match the structure of +coder+ passed to the +init_with+
1587
- # method.
1588
- #
1589
- # Example:
1590
- #
1591
- # class Post < ActiveRecord::Base
1592
- # end
1593
- # coder = {}
1594
- # Post.new.encode_with(coder)
1595
- # coder # => { 'id' => nil, ... }
1596
- def encode_with(coder)
1597
- coder['attributes'] = attributes
1598
- end
1599
-
1600
- # Initialize an empty model object from +coder+. +coder+ must contain
1601
- # the attributes necessary for initializing an empty model object. For
1602
- # example:
1603
- #
1604
- # class Post < ActiveRecord::Base
1605
- # end
1606
- #
1607
- # post = Post.allocate
1608
- # post.init_with('attributes' => { 'title' => 'hello world' })
1609
- # post.title # => 'hello world'
1610
- def init_with(coder)
1611
- @attributes = coder['attributes']
1612
- @relation = nil
1613
-
1614
- set_serialized_attributes
1615
-
1616
- @attributes_cache, @previously_changed, @changed_attributes = {}, {}, {}
1617
- @association_cache = {}
1618
- @aggregation_cache = {}
1619
- @readonly = @destroyed = @marked_for_destruction = false
1620
- @new_record = false
1621
- run_callbacks :find
1622
- run_callbacks :initialize
1623
-
1624
- self
1625
- end
1626
-
1627
- # Returns a String, which Action Pack uses for constructing an URL to this
1628
- # object. The default implementation returns this record's id as a String,
1629
- # or nil if this record's unsaved.
1630
- #
1631
- # For example, suppose that you have a User model, and that you have a
1632
- # <tt>resources :users</tt> route. Normally, +user_path+ will
1633
- # construct a path with the user object's 'id' in it:
1634
- #
1635
- # user = User.find_by_name('Phusion')
1636
- # user_path(user) # => "/users/1"
1637
- #
1638
- # You can override +to_param+ in your model to make +user_path+ construct
1639
- # a path using the user's name instead of the user's id:
1640
- #
1641
- # class User < ActiveRecord::Base
1642
- # def to_param # overridden
1643
- # name
1644
- # end
1645
- # end
1646
- #
1647
- # user = User.find_by_name('Phusion')
1648
- # user_path(user) # => "/users/Phusion"
1649
- def to_param
1650
- # We can't use alias_method here, because method 'id' optimizes itself on the fly.
1651
- id && id.to_s # Be sure to stringify the id for routes
1652
- end
1653
-
1654
- # Returns a cache key that can be used to identify this record.
1655
- #
1656
- # ==== Examples
1657
- #
1658
- # Product.new.cache_key # => "products/new"
1659
- # Product.find(5).cache_key # => "products/5" (updated_at not available)
1660
- # Person.find(5).cache_key # => "people/5-20071224150000" (updated_at available)
1661
- def cache_key
1662
- case
1663
- when new_record?
1664
- "#{self.class.model_name.cache_key}/new"
1665
- when timestamp = self[:updated_at]
1666
- timestamp = timestamp.utc.to_s(:number)
1667
- "#{self.class.model_name.cache_key}/#{id}-#{timestamp}"
1668
- else
1669
- "#{self.class.model_name.cache_key}/#{id}"
1670
- end
1671
- end
1672
-
1673
- def quoted_id #:nodoc:
1674
- quote_value(id, column_for_attribute(self.class.primary_key))
1675
- end
1676
-
1677
- # Returns true if the given attribute is in the attributes hash
1678
- def has_attribute?(attr_name)
1679
- @attributes.has_key?(attr_name.to_s)
1680
- end
1681
-
1682
- # Returns an array of names for the attributes available on this object.
1683
- def attribute_names
1684
- @attributes.keys
1685
- end
1686
-
1687
- # Allows you to set all the attributes at once by passing in a hash with keys
1688
- # matching the attribute names (which again matches the column names).
1689
- #
1690
- # If any attributes are protected by either +attr_protected+ or
1691
- # +attr_accessible+ then only settable attributes will be assigned.
1692
- #
1693
- # The +guard_protected_attributes+ argument is now deprecated, use
1694
- # the +assign_attributes+ method if you want to bypass mass-assignment security.
1695
- #
1696
- # class User < ActiveRecord::Base
1697
- # attr_protected :is_admin
1698
- # end
1699
- #
1700
- # user = User.new
1701
- # user.attributes = { :username => 'Phusion', :is_admin => true }
1702
- # user.username # => "Phusion"
1703
- # user.is_admin? # => false
1704
- def attributes=(new_attributes, guard_protected_attributes = nil)
1705
- unless guard_protected_attributes.nil?
1706
- message = "the use of 'guard_protected_attributes' will be removed from the next minor release of rails, " +
1707
- "if you want to bypass mass-assignment security then look into using assign_attributes"
1708
- ActiveSupport::Deprecation.warn(message)
1709
- end
1710
-
1711
- return unless new_attributes.is_a?(Hash)
1712
-
1713
- if guard_protected_attributes == false
1714
- assign_attributes(new_attributes, :without_protection => true)
1715
- else
1716
- assign_attributes(new_attributes)
1717
- end
1718
- end
1719
-
1720
- # Allows you to set all the attributes for a particular mass-assignment
1721
- # security role by passing in a hash of attributes with keys matching
1722
- # the attribute names (which again matches the column names) and the role
1723
- # name using the :as option.
1724
- #
1725
- # To bypass mass-assignment security you can use the :without_protection => true
1726
- # option.
1727
- #
1728
- # class User < ActiveRecord::Base
1729
- # attr_accessible :name
1730
- # attr_accessible :name, :is_admin, :as => :admin
1731
- # end
1732
- #
1733
- # user = User.new
1734
- # user.assign_attributes({ :name => 'Josh', :is_admin => true })
1735
- # user.name # => "Josh"
1736
- # user.is_admin? # => false
1737
- #
1738
- # user = User.new
1739
- # user.assign_attributes({ :name => 'Josh', :is_admin => true }, :as => :admin)
1740
- # user.name # => "Josh"
1741
- # user.is_admin? # => true
1742
- #
1743
- # user = User.new
1744
- # user.assign_attributes({ :name => 'Josh', :is_admin => true }, :without_protection => true)
1745
- # user.name # => "Josh"
1746
- # user.is_admin? # => true
1747
- def assign_attributes(new_attributes, options = {})
1748
- return unless new_attributes
1749
-
1750
- attributes = new_attributes.stringify_keys
1751
- multi_parameter_attributes = []
1752
- @mass_assignment_options = options
1753
-
1754
- unless options[:without_protection]
1755
- attributes = sanitize_for_mass_assignment(attributes, mass_assignment_role)
1756
- end
1757
-
1758
- attributes.each do |k, v|
1759
- if k.include?("(")
1760
- multi_parameter_attributes << [ k, v ]
1761
- elsif respond_to?("#{k}=")
1762
- send("#{k}=", v)
1763
- else
1764
- raise(UnknownAttributeError, "unknown attribute: #{k}")
1765
- end
1766
- end
1767
-
1768
- @mass_assignment_options = nil
1769
- assign_multiparameter_attributes(multi_parameter_attributes)
1770
- end
1771
-
1772
- # Returns a hash of all the attributes with their names as keys and the values of the attributes as values.
1773
- def attributes
1774
- Hash[@attributes.map { |name, _| [name, read_attribute(name)] }]
1775
- end
1776
-
1777
- # Returns an <tt>#inspect</tt>-like string for the value of the
1778
- # attribute +attr_name+. String attributes are truncated upto 50
1779
- # characters, and Date and Time attributes are returned in the
1780
- # <tt>:db</tt> format. Other attributes return the value of
1781
- # <tt>#inspect</tt> without modification.
1782
- #
1783
- # person = Person.create!(:name => "David Heinemeier Hansson " * 3)
1784
- #
1785
- # person.attribute_for_inspect(:name)
1786
- # # => '"David Heinemeier Hansson David Heinemeier Hansson D..."'
1787
- #
1788
- # person.attribute_for_inspect(:created_at)
1789
- # # => '"2009-01-12 04:48:57"'
1790
- def attribute_for_inspect(attr_name)
1791
- value = read_attribute(attr_name)
1792
-
1793
- if value.is_a?(String) && value.length > 50
1794
- "#{value[0..50]}...".inspect
1795
- elsif value.is_a?(Date) || value.is_a?(Time)
1796
- %("#{value.to_s(:db)}")
1797
- else
1798
- value.inspect
1799
- end
1800
- end
1801
-
1802
- # Returns true if the specified +attribute+ has been set by the user or by a database load and is neither
1803
- # nil nor empty? (the latter only applies to objects that respond to empty?, most notably Strings).
1804
- def attribute_present?(attribute)
1805
- !_read_attribute(attribute).blank?
1806
- end
1807
-
1808
- # Returns the column object for the named attribute.
1809
- def column_for_attribute(name)
1810
- self.class.columns_hash[name.to_s]
1811
- end
1812
-
1813
- # Returns true if +comparison_object+ is the same exact object, or +comparison_object+
1814
- # is of the same type and +self+ has an ID and it is equal to +comparison_object.id+.
1815
- #
1816
- # Note that new records are different from any other record by definition, unless the
1817
- # other record is the receiver itself. Besides, if you fetch existing records with
1818
- # +select+ and leave the ID out, you're on your own, this predicate will return false.
1819
- #
1820
- # Note also that destroying a record preserves its ID in the model instance, so deleted
1821
- # models are still comparable.
1822
- def ==(comparison_object)
1823
- super ||
1824
- comparison_object.instance_of?(self.class) &&
1825
- id.present? &&
1826
- comparison_object.id == id
1827
- end
1828
- alias :eql? :==
1829
-
1830
- # Delegates to id in order to allow two records of the same type and id to work with something like:
1831
- # [ Person.find(1), Person.find(2), Person.find(3) ] & [ Person.find(1), Person.find(4) ] # => [ Person.find(1) ]
1832
- def hash
1833
- id.hash
1834
- end
1835
-
1836
- # Freeze the attributes hash such that associations are still accessible, even on destroyed records.
1837
- def freeze
1838
- @attributes.freeze; self
1839
- end
1840
-
1841
- # Returns +true+ if the attributes hash has been frozen.
1842
- def frozen?
1843
- @attributes.frozen?
1844
- end
1845
-
1846
- # Allows sort on objects
1847
- def <=>(other_object)
1848
- if other_object.is_a?(self.class)
1849
- self.to_key <=> other_object.to_key
1850
- else
1851
- nil
1852
- end
1853
- end
1854
-
1855
- # Backport dup from 1.9 so that initialize_dup() gets called
1856
- unless Object.respond_to?(:initialize_dup)
1857
- def dup # :nodoc:
1858
- copy = super
1859
- copy.initialize_dup(self)
1860
- copy
1861
- end
1862
- end
1863
-
1864
- # Duped objects have no id assigned and are treated as new records. Note
1865
- # that this is a "shallow" copy as it copies the object's attributes
1866
- # only, not its associations. The extent of a "deep" copy is application
1867
- # specific and is therefore left to the application to implement according
1868
- # to its need.
1869
- # The dup method does not preserve the timestamps (created|updated)_(at|on).
1870
- def initialize_dup(other)
1871
- cloned_attributes = other.clone_attributes(:read_attribute_before_type_cast)
1872
- cloned_attributes.delete(self.class.primary_key)
1873
-
1874
- @attributes = cloned_attributes
1875
-
1876
- _run_after_initialize_callbacks if respond_to?(:_run_after_initialize_callbacks)
1877
-
1878
- @changed_attributes = {}
1879
- attributes_from_column_definition.each do |attr, orig_value|
1880
- @changed_attributes[attr] = orig_value if field_changed?(attr, orig_value, @attributes[attr])
1881
- end
1882
-
1883
- @aggregation_cache = {}
1884
- @association_cache = {}
1885
- @attributes_cache = {}
1886
- @new_record = true
1887
-
1888
- ensure_proper_type
1889
- populate_with_current_scope_attributes
1890
- clear_timestamp_attributes
1891
- end
1892
-
1893
- # Returns +true+ if the record is read only. Records loaded through joins with piggy-back
1894
- # attributes will be marked as read only since they cannot be saved.
1895
- def readonly?
1896
- @readonly
1897
- end
1898
-
1899
- # Marks this record as read only.
1900
- def readonly!
1901
- @readonly = true
1902
- end
1903
-
1904
- # Returns the contents of the record as a nicely formatted string.
1905
- def inspect
1906
- attributes_as_nice_string = self.class.column_names.collect { |name|
1907
- if has_attribute?(name)
1908
- "#{name}: #{attribute_for_inspect(name)}"
1909
- end
1910
- }.compact.join(", ")
1911
- "#<#{self.class} #{attributes_as_nice_string}>"
1912
- end
1913
-
1914
- protected
1915
- def clone_attributes(reader_method = :read_attribute, attributes = {})
1916
- attribute_names.each do |name|
1917
- attributes[name] = clone_attribute_value(reader_method, name)
1918
- end
1919
- attributes
1920
- end
1921
-
1922
- def clone_attribute_value(reader_method, attribute_name)
1923
- value = send(reader_method, attribute_name)
1924
- value.duplicable? ? value.clone : value
1925
- rescue TypeError, NoMethodError
1926
- value
1927
- end
1928
-
1929
- def mass_assignment_options
1930
- @mass_assignment_options ||= {}
1931
- end
1932
-
1933
- def mass_assignment_role
1934
- mass_assignment_options[:as] || :default
1935
- end
1936
-
1937
- private
1938
-
1939
- # Under Ruby 1.9, Array#flatten will call #to_ary (recursively) on each of the elements
1940
- # of the array, and then rescues from the possible NoMethodError. If those elements are
1941
- # ActiveRecord::Base's, then this triggers the various method_missing's that we have,
1942
- # which significantly impacts upon performance.
1943
- #
1944
- # So we can avoid the method_missing hit by explicitly defining #to_ary as nil here.
1945
- #
1946
- # See also http://tenderlovemaking.com/2011/06/28/til-its-ok-to-return-nil-from-to_ary/
1947
- def to_ary # :nodoc:
1948
- nil
1949
- end
1950
-
1951
- def set_serialized_attributes
1952
- sattrs = self.class.serialized_attributes
1953
-
1954
- sattrs.each do |key, coder|
1955
- @attributes[key] = coder.load @attributes[key] if @attributes.key?(key)
1956
- end
1957
- end
1958
-
1959
- # Sets the attribute used for single table inheritance to this class name if this is not the
1960
- # ActiveRecord::Base descendant.
1961
- # Considering the hierarchy Reply < Message < ActiveRecord::Base, this makes it possible to
1962
- # do Reply.new without having to set <tt>Reply[Reply.inheritance_column] = "Reply"</tt> yourself.
1963
- # No such attribute would be set for objects of the Message class in that example.
1964
- def ensure_proper_type
1965
- klass = self.class
1966
- if klass.finder_needs_type_condition?
1967
- write_attribute(klass.inheritance_column, klass.sti_name)
1968
- end
1969
- end
1970
-
1971
- # The primary key and inheritance column can never be set by mass-assignment for security reasons.
1972
- def self.attributes_protected_by_default
1973
- default = [ primary_key, inheritance_column ]
1974
- default << 'id' unless primary_key.eql? 'id'
1975
- default
1976
- end
1977
-
1978
- # Returns a copy of the attributes hash where all the values have been safely quoted for use in
1979
- # an Arel insert/update method.
1980
- def arel_attributes_values(include_primary_key = true, include_readonly_attributes = true, attribute_names = @attributes.keys)
1981
- attrs = {}
1982
- klass = self.class
1983
- arel_table = klass.arel_table
1984
-
1985
- attribute_names.each do |name|
1986
- if (column = column_for_attribute(name)) && (include_primary_key || !column.primary)
1987
-
1988
- if include_readonly_attributes || (!include_readonly_attributes && !self.class.readonly_attributes.include?(name))
1989
-
1990
- value = if coder = klass.serialized_attributes[name]
1991
- coder.dump @attributes[name]
1992
- else
1993
- # FIXME: we need @attributes to be used consistently.
1994
- # If the values stored in @attributes were already type
1995
- # casted, this code could be simplified
1996
- read_attribute(name)
1997
- end
1998
-
1999
- attrs[arel_table[name]] = value
2000
- end
2001
- end
2002
- end
2003
- attrs
2004
- end
2005
-
2006
- # Quote strings appropriately for SQL statements.
2007
- def quote_value(value, column = nil)
2008
- self.class.connection.quote(value, column)
2009
- end
2010
-
2011
- # Instantiates objects for all attribute classes that needs more than one constructor parameter. This is done
2012
- # by calling new on the column type or aggregation type (through composed_of) object with these parameters.
2013
- # So having the pairs written_on(1) = "2004", written_on(2) = "6", written_on(3) = "24", will instantiate
2014
- # written_on (a date type) with Date.new("2004", "6", "24"). You can also specify a typecast character in the
2015
- # parentheses to have the parameters typecasted before they're used in the constructor. Use i for Fixnum,
2016
- # f for Float, s for String, and a for Array. If all the values for a given attribute are empty, the
2017
- # attribute will be set to nil.
2018
- def assign_multiparameter_attributes(pairs)
2019
- execute_callstack_for_multiparameter_attributes(
2020
- extract_callstack_for_multiparameter_attributes(pairs)
2021
- )
2022
- end
2023
-
2024
- def instantiate_time_object(name, values)
2025
- if self.class.send(:create_time_zone_conversion_attribute?, name, column_for_attribute(name))
2026
- Time.zone.local(*values)
2027
- else
2028
- Time.time_with_datetime_fallback(@@default_timezone, *values)
2029
- end
2030
- end
2031
-
2032
- def execute_callstack_for_multiparameter_attributes(callstack)
2033
- errors = []
2034
- callstack.each do |name, values_with_empty_parameters|
2035
- begin
2036
- send(name + "=", read_value_from_parameter(name, values_with_empty_parameters))
2037
- rescue => ex
2038
- errors << AttributeAssignmentError.new("error on assignment #{values_with_empty_parameters.values.inspect} to #{name}", ex, name)
2039
- end
2040
- end
2041
- unless errors.empty?
2042
- raise MultiparameterAssignmentErrors.new(errors), "#{errors.size} error(s) on assignment of multiparameter attributes"
2043
- end
2044
- end
2045
-
2046
- def read_value_from_parameter(name, values_hash_from_param)
2047
- klass = (self.class.reflect_on_aggregation(name.to_sym) || column_for_attribute(name)).klass
2048
- if values_hash_from_param.values.all?{|v|v.nil?}
2049
- nil
2050
- elsif klass == Time
2051
- read_time_parameter_value(name, values_hash_from_param)
2052
- elsif klass == Date
2053
- read_date_parameter_value(name, values_hash_from_param)
2054
- else
2055
- read_other_parameter_value(klass, name, values_hash_from_param)
2056
- end
2057
- end
2058
-
2059
- def read_time_parameter_value(name, values_hash_from_param)
2060
- # If Date bits were not provided, error
2061
- raise "Missing Parameter" if [1,2,3].any?{|position| !values_hash_from_param.has_key?(position)}
2062
- max_position = extract_max_param_for_multiparameter_attributes(values_hash_from_param, 6)
2063
- set_values = (1..max_position).collect{|position| values_hash_from_param[position] }
2064
- # If Date bits were provided but blank, then default to 1
2065
- # If Time bits are not there, then default to 0
2066
- [1,1,1,0,0,0].each_with_index{|v,i| set_values[i] = set_values[i].blank? ? v : set_values[i]}
2067
- instantiate_time_object(name, set_values)
2068
- end
2069
-
2070
- def read_date_parameter_value(name, values_hash_from_param)
2071
- set_values = (1..3).collect{|position| values_hash_from_param[position].blank? ? 1 : values_hash_from_param[position]}
2072
- begin
2073
- Date.new(*set_values)
2074
- rescue ArgumentError => ex # if Date.new raises an exception on an invalid date
2075
- instantiate_time_object(name, set_values).to_date # we instantiate Time object and convert it back to a date thus using Time's logic in handling invalid dates
2076
- end
2077
- end
2078
-
2079
- def read_other_parameter_value(klass, name, values_hash_from_param)
2080
- max_position = extract_max_param_for_multiparameter_attributes(values_hash_from_param)
2081
- values = (1..max_position).collect do |position|
2082
- raise "Missing Parameter" if !values_hash_from_param.has_key?(position)
2083
- values_hash_from_param[position]
2084
- end
2085
- klass.new(*values)
2086
- end
2087
-
2088
- def extract_max_param_for_multiparameter_attributes(values_hash_from_param, upper_cap = 100)
2089
- [values_hash_from_param.keys.max,upper_cap].min
2090
- end
2091
-
2092
- def extract_callstack_for_multiparameter_attributes(pairs)
2093
- attributes = { }
2094
-
2095
- pairs.each do |pair|
2096
- multiparameter_name, value = pair
2097
- attribute_name = multiparameter_name.split("(").first
2098
- attributes[attribute_name] = {} unless attributes.include?(attribute_name)
2099
-
2100
- parameter_value = value.empty? ? nil : type_cast_attribute_value(multiparameter_name, value)
2101
- attributes[attribute_name][find_parameter_position(multiparameter_name)] ||= parameter_value
2102
- end
2103
-
2104
- attributes
2105
- end
2106
-
2107
- def type_cast_attribute_value(multiparameter_name, value)
2108
- multiparameter_name =~ /\([0-9]*([if])\)/ ? value.send("to_" + $1) : value
2109
- end
2110
-
2111
- def find_parameter_position(multiparameter_name)
2112
- multiparameter_name.scan(/\(([0-9]*).*\)/).first.first.to_i
2113
- end
2114
-
2115
- # Returns a comma-separated pair list, like "key1 = val1, key2 = val2".
2116
- def comma_pair_list(hash)
2117
- hash.map { |k,v| "#{k} = #{v}" }.join(", ")
2118
- end
2119
-
2120
- def quote_columns(quoter, hash)
2121
- Hash[hash.map { |name, value| [quoter.quote_column_name(name), value] }]
2122
- end
2123
-
2124
- def quoted_comma_pair_list(quoter, hash)
2125
- comma_pair_list(quote_columns(quoter, hash))
2126
- end
2127
-
2128
- def convert_number_column_value(value)
2129
- if value == false
2130
- 0
2131
- elsif value == true
2132
- 1
2133
- elsif value.is_a?(String) && value.blank?
2134
- nil
2135
- else
2136
- value
2137
- end
2138
- end
2139
-
2140
- def populate_with_current_scope_attributes
2141
- return unless self.class.scope_attributes?
2142
-
2143
- self.class.scope_attributes.each do |att,value|
2144
- send("#{att}=", value) if respond_to?("#{att}=")
2145
- end
2146
- end
2147
-
2148
- # Clear attributes and changed_attributes
2149
- def clear_timestamp_attributes
2150
- all_timestamp_attributes_in_model.each do |attribute_name|
2151
- self[attribute_name] = nil
2152
- changed_attributes.delete(attribute_name)
2153
- end
2154
- end
2155
- end
2156
-
2157
- Base.class_eval do
2158
- include ActiveRecord::Persistence
2159
270
  extend ActiveModel::Naming
2160
- extend QueryCache::ClassMethods
271
+
2161
272
  extend ActiveSupport::Benchmarkable
2162
273
  extend ActiveSupport::DescendantsTracker
2163
274
 
275
+ extend ConnectionHandling
276
+ extend QueryCache::ClassMethods
277
+ extend Querying
278
+ extend Translation
279
+ extend DynamicMatchers
280
+ extend Explain
281
+ extend Enum
282
+ extend Delegation::DelegateCache
283
+
284
+ include Core
285
+ include Persistence
286
+ include ReadonlyAttributes
287
+ include ModelSchema
288
+ include Inheritance
289
+ include Scoping
290
+ include Sanitization
291
+ include AttributeAssignment
2164
292
  include ActiveModel::Conversion
293
+ include Integration
2165
294
  include Validations
2166
- extend CounterCache
2167
- include Locking::Optimistic, Locking::Pessimistic
295
+ include CounterCache
296
+ include Attributes
297
+ include AttributeDecorators
298
+ include Locking::Optimistic
299
+ include Locking::Pessimistic
2168
300
  include AttributeMethods
2169
- include AttributeMethods::Read, AttributeMethods::Write, AttributeMethods::BeforeTypeCast, AttributeMethods::Query
2170
- include AttributeMethods::PrimaryKey
2171
- include AttributeMethods::TimeZoneConversion
2172
- include AttributeMethods::Dirty
2173
- include ActiveModel::MassAssignmentSecurity
2174
- include Callbacks, ActiveModel::Observing, Timestamp
2175
- include Associations, NamedScope
2176
- include IdentityMap
301
+ include Callbacks
302
+ include Timestamp
303
+ include Associations
2177
304
  include ActiveModel::SecurePassword
2178
-
2179
- # AutosaveAssociation needs to be included before Transactions, because we want
2180
- # #save_with_autosave_associations to be wrapped inside a transaction.
2181
- include AutosaveAssociation, NestedAttributes
2182
- include Aggregations, Transactions, Reflection, Serialization
2183
-
2184
- # Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
2185
- # "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)).
2186
- # (Alias for the protected read_attribute method).
2187
- def [](attr_name)
2188
- read_attribute(attr_name)
2189
- end
2190
-
2191
- # Updates the attribute identified by <tt>attr_name</tt> with the specified +value+.
2192
- # (Alias for the protected write_attribute method).
2193
- def []=(attr_name, value)
2194
- write_attribute(attr_name, value)
2195
- end
305
+ include AutosaveAssociation
306
+ include NestedAttributes
307
+ include Aggregations
308
+ include Transactions
309
+ include NoTouching
310
+ include Reflection
311
+ include Serialization
312
+ include Store
2196
313
  end
2197
- end
2198
314
 
2199
- # TODO: Remove this and make it work with LAZY flag
2200
- require 'active_record/connection_adapters/abstract_adapter'
2201
- ActiveSupport.run_load_hooks(:active_record, ActiveRecord::Base)
315
+ ActiveSupport.run_load_hooks(:active_record, Base)
316
+ end