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,163 +0,0 @@
1
- module ActiveRecord
2
- # = Active Record Identity Map
3
- #
4
- # Ensures that each object gets loaded only once by keeping every loaded
5
- # object in a map. Looks up objects using the map when referring to them.
6
- #
7
- # More information on Identity Map pattern:
8
- # http://www.martinfowler.com/eaaCatalog/identityMap.html
9
- #
10
- # == Configuration
11
- #
12
- # In order to enable IdentityMap, set <tt>config.active_record.identity_map = true</tt>
13
- # in your <tt>config/application.rb</tt> file.
14
- #
15
- # IdentityMap is disabled by default and still in development (i.e. use it with care).
16
- #
17
- # == Associations
18
- #
19
- # Active Record Identity Map does not track associations yet. For example:
20
- #
21
- # comment = @post.comments.first
22
- # comment.post = nil
23
- # @post.comments.include?(comment) #=> true
24
- #
25
- # Ideally, the example above would return false, removing the comment object from the
26
- # post association when the association is nullified. This may cause side effects, as
27
- # in the situation below, if Identity Map is enabled:
28
- #
29
- # Post.has_many :comments, :dependent => :destroy
30
- #
31
- # comment = @post.comments.first
32
- # comment.post = nil
33
- # comment.save
34
- # Post.destroy(@post.id)
35
- #
36
- # Without using Identity Map, the code above will destroy the @post object leaving
37
- # the comment object intact. However, once we enable Identity Map, the post loaded
38
- # by Post.destroy is exactly the same object as the object @post. As the object @post
39
- # still has the comment object in @post.comments, once Identity Map is enabled, the
40
- # comment object will be accidently removed.
41
- #
42
- # This inconsistency is meant to be fixed in future Rails releases.
43
- #
44
- module IdentityMap
45
-
46
- class << self
47
- def enabled=(flag)
48
- Thread.current[:identity_map_enabled] = flag
49
- end
50
-
51
- def enabled
52
- Thread.current[:identity_map_enabled]
53
- end
54
- alias enabled? enabled
55
-
56
- def repository
57
- Thread.current[:identity_map] ||= Hash.new { |h,k| h[k] = {} }
58
- end
59
-
60
- def use
61
- old, self.enabled = enabled, true
62
-
63
- yield if block_given?
64
- ensure
65
- self.enabled = old
66
- clear
67
- end
68
-
69
- def without
70
- old, self.enabled = enabled, false
71
-
72
- yield if block_given?
73
- ensure
74
- self.enabled = old
75
- end
76
-
77
- def get(klass, primary_key)
78
- record = repository[klass.symbolized_sti_name][primary_key]
79
-
80
- if record.is_a?(klass)
81
- ActiveSupport::Notifications.instrument("identity.active_record",
82
- :line => "From Identity Map (id: #{primary_key})",
83
- :name => "#{klass} Loaded",
84
- :connection_id => object_id)
85
-
86
- record
87
- else
88
- nil
89
- end
90
- end
91
-
92
- def add(record)
93
- repository[record.class.symbolized_sti_name][record.id] = record if contain_all_columns?(record)
94
- end
95
-
96
- def remove(record)
97
- repository[record.class.symbolized_sti_name].delete(record.id)
98
- end
99
-
100
- def remove_by_id(symbolized_sti_name, id)
101
- repository[symbolized_sti_name].delete(id)
102
- end
103
-
104
- def clear
105
- repository.clear
106
- end
107
-
108
- private
109
-
110
- def contain_all_columns?(record)
111
- (record.class.column_names - record.attribute_names).empty?
112
- end
113
- end
114
-
115
- # Reinitialize an Identity Map model object from +coder+.
116
- # +coder+ must contain the attributes necessary for initializing an empty
117
- # model object.
118
- def reinit_with(coder)
119
- @attributes_cache = {}
120
- dirty = @changed_attributes.keys
121
- @attributes.update(coder['attributes'].except(*dirty))
122
- @changed_attributes.update(coder['attributes'].slice(*dirty))
123
- @changed_attributes.delete_if{|k,v| v.eql? @attributes[k]}
124
-
125
- set_serialized_attributes
126
-
127
- run_callbacks :find
128
-
129
- self
130
- end
131
-
132
- class Middleware
133
- class Body #:nodoc:
134
- def initialize(target, original)
135
- @target = target
136
- @original = original
137
- end
138
-
139
- def each(&block)
140
- @target.each(&block)
141
- end
142
-
143
- def close
144
- @target.close if @target.respond_to?(:close)
145
- ensure
146
- IdentityMap.enabled = @original
147
- IdentityMap.clear
148
- end
149
- end
150
-
151
- def initialize(app)
152
- @app = app
153
- end
154
-
155
- def call(env)
156
- enabled = IdentityMap.enabled
157
- IdentityMap.enabled = true
158
- status, headers, body = @app.call(env)
159
- [status, headers, Body.new(body, enabled)]
160
- end
161
- end
162
- end
163
- end
@@ -1,200 +0,0 @@
1
- require 'active_support/core_ext/array'
2
- require 'active_support/core_ext/hash/except'
3
- require 'active_support/core_ext/kernel/singleton_class'
4
- require 'active_support/core_ext/object/blank'
5
- require 'active_support/core_ext/class/attribute'
6
-
7
- module ActiveRecord
8
- # = Active Record Named \Scopes
9
- module NamedScope
10
- extend ActiveSupport::Concern
11
-
12
- module ClassMethods
13
- # Returns an anonymous \scope.
14
- #
15
- # posts = Post.scoped
16
- # posts.size # Fires "select count(*) from posts" and returns the count
17
- # posts.each {|p| puts p.name } # Fires "select * from posts" and loads post objects
18
- #
19
- # fruits = Fruit.scoped
20
- # fruits = fruits.where(:colour => 'red') if options[:red_only]
21
- # fruits = fruits.limit(10) if limited?
22
- #
23
- # Anonymous \scopes tend to be useful when procedurally generating complex
24
- # queries, where passing intermediate values (\scopes) around as first-class
25
- # objects is convenient.
26
- #
27
- # You can define a \scope that applies to all finders using
28
- # ActiveRecord::Base.default_scope.
29
- def scoped(options = nil)
30
- if options
31
- scoped.apply_finder_options(options)
32
- else
33
- if current_scope
34
- current_scope.clone
35
- else
36
- scope = relation.clone
37
- scope.default_scoped = true
38
- scope
39
- end
40
- end
41
- end
42
-
43
- ##
44
- # Collects attributes from scopes that should be applied when creating
45
- # an AR instance for the particular class this is called on.
46
- def scope_attributes # :nodoc:
47
- if current_scope
48
- current_scope.scope_for_create
49
- else
50
- scope = relation.clone
51
- scope.default_scoped = true
52
- scope.scope_for_create
53
- end
54
- end
55
-
56
- ##
57
- # Are there default attributes associated with this scope?
58
- def scope_attributes? # :nodoc:
59
- current_scope || default_scopes.any?
60
- end
61
-
62
- # Adds a class method for retrieving and querying objects. A \scope represents a narrowing of a database query,
63
- # such as <tt>where(:color => :red).select('shirts.*').includes(:washing_instructions)</tt>.
64
- #
65
- # class Shirt < ActiveRecord::Base
66
- # scope :red, where(:color => 'red')
67
- # scope :dry_clean_only, joins(:washing_instructions).where('washing_instructions.dry_clean_only = ?', true)
68
- # end
69
- #
70
- # The above calls to <tt>scope</tt> define class methods Shirt.red and Shirt.dry_clean_only. Shirt.red,
71
- # in effect, represents the query <tt>Shirt.where(:color => 'red')</tt>.
72
- #
73
- # Note that this is simply 'syntactic sugar' for defining an actual class method:
74
- #
75
- # class Shirt < ActiveRecord::Base
76
- # def self.red
77
- # where(:color => 'red')
78
- # end
79
- # end
80
- #
81
- # Unlike <tt>Shirt.find(...)</tt>, however, the object returned by Shirt.red is not an Array; it
82
- # resembles the association object constructed by a <tt>has_many</tt> declaration. For instance,
83
- # you can invoke <tt>Shirt.red.first</tt>, <tt>Shirt.red.count</tt>, <tt>Shirt.red.where(:size => 'small')</tt>.
84
- # Also, just as with the association objects, named \scopes act like an Array, implementing Enumerable;
85
- # <tt>Shirt.red.each(&block)</tt>, <tt>Shirt.red.first</tt>, and <tt>Shirt.red.inject(memo, &block)</tt>
86
- # all behave as if Shirt.red really was an Array.
87
- #
88
- # These named \scopes are composable. For instance, <tt>Shirt.red.dry_clean_only</tt> will produce
89
- # all shirts that are both red and dry clean only.
90
- # Nested finds and calculations also work with these compositions: <tt>Shirt.red.dry_clean_only.count</tt>
91
- # returns the number of garments for which these criteria obtain. Similarly with
92
- # <tt>Shirt.red.dry_clean_only.average(:thread_count)</tt>.
93
- #
94
- # All \scopes are available as class methods on the ActiveRecord::Base descendant upon which
95
- # the \scopes were defined. But they are also available to <tt>has_many</tt> associations. If,
96
- #
97
- # class Person < ActiveRecord::Base
98
- # has_many :shirts
99
- # end
100
- #
101
- # then <tt>elton.shirts.red.dry_clean_only</tt> will return all of Elton's red, dry clean
102
- # only shirts.
103
- #
104
- # Named \scopes can also be procedural:
105
- #
106
- # class Shirt < ActiveRecord::Base
107
- # scope :colored, lambda { |color| where(:color => color) }
108
- # end
109
- #
110
- # In this example, <tt>Shirt.colored('puce')</tt> finds all puce shirts.
111
- #
112
- # On Ruby 1.9 you can use the 'stabby lambda' syntax:
113
- #
114
- # scope :colored, ->(color) { where(:color => color) }
115
- #
116
- # Note that scopes defined with \scope will be evaluated when they are defined, rather than
117
- # when they are used. For example, the following would be incorrect:
118
- #
119
- # class Post < ActiveRecord::Base
120
- # scope :recent, where('published_at >= ?', Time.now - 1.week)
121
- # end
122
- #
123
- # The example above would be 'frozen' to the <tt>Time.now</tt> value when the <tt>Post</tt>
124
- # class was defined, and so the resultant SQL query would always be the same. The correct
125
- # way to do this would be via a lambda, which will re-evaluate the scope each time
126
- # it is called:
127
- #
128
- # class Post < ActiveRecord::Base
129
- # scope :recent, lambda { where('published_at >= ?', Time.now - 1.week) }
130
- # end
131
- #
132
- # Named \scopes can also have extensions, just as with <tt>has_many</tt> declarations:
133
- #
134
- # class Shirt < ActiveRecord::Base
135
- # scope :red, where(:color => 'red') do
136
- # def dom_id
137
- # 'red_shirts'
138
- # end
139
- # end
140
- # end
141
- #
142
- # Scopes can also be used while creating/building a record.
143
- #
144
- # class Article < ActiveRecord::Base
145
- # scope :published, where(:published => true)
146
- # end
147
- #
148
- # Article.published.new.published # => true
149
- # Article.published.create.published # => true
150
- #
151
- # Class methods on your model are automatically available
152
- # on scopes. Assuming the following setup:
153
- #
154
- # class Article < ActiveRecord::Base
155
- # scope :published, where(:published => true)
156
- # scope :featured, where(:featured => true)
157
- #
158
- # def self.latest_article
159
- # order('published_at desc').first
160
- # end
161
- #
162
- # def self.titles
163
- # map(&:title)
164
- # end
165
- #
166
- # end
167
- #
168
- # We are able to call the methods like this:
169
- #
170
- # Article.published.featured.latest_article
171
- # Article.featured.titles
172
-
173
- def scope(name, scope_options = {})
174
- name = name.to_sym
175
- valid_scope_name?(name)
176
- extension = Module.new(&Proc.new) if block_given?
177
-
178
- scope_proc = lambda do |*args|
179
- options = scope_options.respond_to?(:call) ? unscoped { scope_options.call(*args) } : scope_options
180
- options = scoped.apply_finder_options(options) if options.is_a?(Hash)
181
-
182
- relation = scoped.merge(options)
183
-
184
- extension ? relation.extending(extension) : relation
185
- end
186
-
187
- singleton_class.send(:redefine_method, name, &scope_proc)
188
- end
189
-
190
- protected
191
-
192
- def valid_scope_name?(name)
193
- if respond_to?(name, true)
194
- logger.warn "Creating scope :#{name}. " \
195
- "Overwriting existing method #{self.name}.#{name}."
196
- end
197
- end
198
- end
199
- end
200
- end
@@ -1,121 +0,0 @@
1
- require 'active_support/core_ext/class/attribute'
2
-
3
- module ActiveRecord
4
- # = Active Record Observer
5
- #
6
- # Observer classes respond to life cycle callbacks to implement trigger-like
7
- # behavior outside the original class. This is a great way to reduce the
8
- # clutter that normally comes when the model class is burdened with
9
- # functionality that doesn't pertain to the core responsibility of the
10
- # class. Example:
11
- #
12
- # class CommentObserver < ActiveRecord::Observer
13
- # def after_save(comment)
14
- # Notifications.comment("admin@do.com", "New comment was posted", comment).deliver
15
- # end
16
- # end
17
- #
18
- # This Observer sends an email when a Comment#save is finished.
19
- #
20
- # class ContactObserver < ActiveRecord::Observer
21
- # def after_create(contact)
22
- # contact.logger.info('New contact added!')
23
- # end
24
- #
25
- # def after_destroy(contact)
26
- # contact.logger.warn("Contact with an id of #{contact.id} was destroyed!")
27
- # end
28
- # end
29
- #
30
- # This Observer uses logger to log when specific callbacks are triggered.
31
- #
32
- # == Observing a class that can't be inferred
33
- #
34
- # Observers will by default be mapped to the class with which they share a name. So CommentObserver will
35
- # be tied to observing Comment, ProductManagerObserver to ProductManager, and so on. If you want to name your observer
36
- # differently than the class you're interested in observing, you can use the Observer.observe class method which takes
37
- # either the concrete class (Product) or a symbol for that class (:product):
38
- #
39
- # class AuditObserver < ActiveRecord::Observer
40
- # observe :account
41
- #
42
- # def after_update(account)
43
- # AuditTrail.new(account, "UPDATED")
44
- # end
45
- # end
46
- #
47
- # If the audit observer needs to watch more than one kind of object, this can be specified with multiple arguments:
48
- #
49
- # class AuditObserver < ActiveRecord::Observer
50
- # observe :account, :balance
51
- #
52
- # def after_update(record)
53
- # AuditTrail.new(record, "UPDATED")
54
- # end
55
- # end
56
- #
57
- # The AuditObserver will now act on both updates to Account and Balance by treating them both as records.
58
- #
59
- # == Available callback methods
60
- #
61
- # The observer can implement callback methods for each of the methods described in the Callbacks module.
62
- #
63
- # == Storing Observers in Rails
64
- #
65
- # If you're using Active Record within Rails, observer classes are usually stored in app/models with the
66
- # naming convention of app/models/audit_observer.rb.
67
- #
68
- # == Configuration
69
- #
70
- # In order to activate an observer, list it in the <tt>config.active_record.observers</tt> configuration
71
- # setting in your <tt>config/application.rb</tt> file.
72
- #
73
- # config.active_record.observers = :comment_observer, :signup_observer
74
- #
75
- # Observers will not be invoked unless you define these in your application configuration.
76
- #
77
- # == Loading
78
- #
79
- # Observers register themselves in the model class they observe, since it is the class that
80
- # notifies them of events when they occur. As a side-effect, when an observer is loaded its
81
- # corresponding model class is loaded.
82
- #
83
- # Up to (and including) Rails 2.0.2 observers were instantiated between plugins and
84
- # application initializers. Now observers are loaded after application initializers,
85
- # so observed models can make use of extensions.
86
- #
87
- # If by any chance you are using observed models in the initialization you can still
88
- # load their observers by calling <tt>ModelObserver.instance</tt> before. Observers are
89
- # singletons and that call instantiates and registers them.
90
- #
91
- class Observer < ActiveModel::Observer
92
-
93
- protected
94
-
95
- def observed_classes
96
- klasses = super
97
- klasses + klasses.map { |klass| klass.descendants }.flatten
98
- end
99
-
100
- def add_observer!(klass)
101
- super
102
- define_callbacks klass
103
- end
104
-
105
- def define_callbacks(klass)
106
- observer = self
107
- observer_name = observer.class.name.underscore.gsub('/', '__')
108
-
109
- ActiveRecord::Callbacks::CALLBACKS.each do |callback|
110
- next unless respond_to?(callback)
111
- callback_meth = :"_notify_#{observer_name}_for_#{callback}"
112
- unless klass.respond_to?(callback_meth)
113
- klass.send(:define_method, callback_meth) do |&block|
114
- observer.update(callback, self, &block)
115
- end
116
- klass.send(callback, callback_meth)
117
- end
118
- end
119
- end
120
- end
121
- end