square-activerecord 3.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. data/CHANGELOG +6140 -0
  2. data/README.rdoc +222 -0
  3. data/examples/associations.png +0 -0
  4. data/examples/performance.rb +179 -0
  5. data/examples/simple.rb +14 -0
  6. data/lib/active_record.rb +124 -0
  7. data/lib/active_record/aggregations.rb +277 -0
  8. data/lib/active_record/association_preload.rb +430 -0
  9. data/lib/active_record/associations.rb +2307 -0
  10. data/lib/active_record/associations/association_collection.rb +572 -0
  11. data/lib/active_record/associations/association_proxy.rb +299 -0
  12. data/lib/active_record/associations/belongs_to_association.rb +91 -0
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +82 -0
  14. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +143 -0
  15. data/lib/active_record/associations/has_many_association.rb +128 -0
  16. data/lib/active_record/associations/has_many_through_association.rb +115 -0
  17. data/lib/active_record/associations/has_one_association.rb +143 -0
  18. data/lib/active_record/associations/has_one_through_association.rb +40 -0
  19. data/lib/active_record/associations/through_association_scope.rb +154 -0
  20. data/lib/active_record/attribute_methods.rb +60 -0
  21. data/lib/active_record/attribute_methods/before_type_cast.rb +30 -0
  22. data/lib/active_record/attribute_methods/dirty.rb +95 -0
  23. data/lib/active_record/attribute_methods/primary_key.rb +56 -0
  24. data/lib/active_record/attribute_methods/query.rb +39 -0
  25. data/lib/active_record/attribute_methods/read.rb +145 -0
  26. data/lib/active_record/attribute_methods/time_zone_conversion.rb +64 -0
  27. data/lib/active_record/attribute_methods/write.rb +43 -0
  28. data/lib/active_record/autosave_association.rb +369 -0
  29. data/lib/active_record/base.rb +1904 -0
  30. data/lib/active_record/callbacks.rb +284 -0
  31. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +364 -0
  32. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +113 -0
  33. data/lib/active_record/connection_adapters/abstract/database_limits.rb +57 -0
  34. data/lib/active_record/connection_adapters/abstract/database_statements.rb +333 -0
  35. data/lib/active_record/connection_adapters/abstract/query_cache.rb +81 -0
  36. data/lib/active_record/connection_adapters/abstract/quoting.rb +73 -0
  37. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +739 -0
  38. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +539 -0
  39. data/lib/active_record/connection_adapters/abstract_adapter.rb +217 -0
  40. data/lib/active_record/connection_adapters/mysql_adapter.rb +657 -0
  41. data/lib/active_record/connection_adapters/postgresql_adapter.rb +1031 -0
  42. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -0
  43. data/lib/active_record/connection_adapters/sqlite_adapter.rb +401 -0
  44. data/lib/active_record/counter_cache.rb +115 -0
  45. data/lib/active_record/dynamic_finder_match.rb +56 -0
  46. data/lib/active_record/dynamic_scope_match.rb +23 -0
  47. data/lib/active_record/errors.rb +172 -0
  48. data/lib/active_record/fixtures.rb +1006 -0
  49. data/lib/active_record/locale/en.yml +40 -0
  50. data/lib/active_record/locking/optimistic.rb +172 -0
  51. data/lib/active_record/locking/pessimistic.rb +55 -0
  52. data/lib/active_record/log_subscriber.rb +48 -0
  53. data/lib/active_record/migration.rb +617 -0
  54. data/lib/active_record/named_scope.rb +138 -0
  55. data/lib/active_record/nested_attributes.rb +419 -0
  56. data/lib/active_record/observer.rb +125 -0
  57. data/lib/active_record/persistence.rb +290 -0
  58. data/lib/active_record/query_cache.rb +36 -0
  59. data/lib/active_record/railtie.rb +91 -0
  60. data/lib/active_record/railties/controller_runtime.rb +38 -0
  61. data/lib/active_record/railties/databases.rake +512 -0
  62. data/lib/active_record/reflection.rb +411 -0
  63. data/lib/active_record/relation.rb +394 -0
  64. data/lib/active_record/relation/batches.rb +89 -0
  65. data/lib/active_record/relation/calculations.rb +295 -0
  66. data/lib/active_record/relation/finder_methods.rb +363 -0
  67. data/lib/active_record/relation/predicate_builder.rb +48 -0
  68. data/lib/active_record/relation/query_methods.rb +303 -0
  69. data/lib/active_record/relation/spawn_methods.rb +132 -0
  70. data/lib/active_record/schema.rb +59 -0
  71. data/lib/active_record/schema_dumper.rb +195 -0
  72. data/lib/active_record/serialization.rb +60 -0
  73. data/lib/active_record/serializers/xml_serializer.rb +244 -0
  74. data/lib/active_record/session_store.rb +340 -0
  75. data/lib/active_record/test_case.rb +67 -0
  76. data/lib/active_record/timestamp.rb +88 -0
  77. data/lib/active_record/transactions.rb +359 -0
  78. data/lib/active_record/validations.rb +84 -0
  79. data/lib/active_record/validations/associated.rb +48 -0
  80. data/lib/active_record/validations/uniqueness.rb +190 -0
  81. data/lib/active_record/version.rb +10 -0
  82. data/lib/rails/generators/active_record.rb +19 -0
  83. data/lib/rails/generators/active_record/migration.rb +15 -0
  84. data/lib/rails/generators/active_record/migration/migration_generator.rb +25 -0
  85. data/lib/rails/generators/active_record/migration/templates/migration.rb +17 -0
  86. data/lib/rails/generators/active_record/model/model_generator.rb +38 -0
  87. data/lib/rails/generators/active_record/model/templates/migration.rb +16 -0
  88. data/lib/rails/generators/active_record/model/templates/model.rb +5 -0
  89. data/lib/rails/generators/active_record/model/templates/module.rb +5 -0
  90. data/lib/rails/generators/active_record/observer/observer_generator.rb +15 -0
  91. data/lib/rails/generators/active_record/observer/templates/observer.rb +2 -0
  92. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +24 -0
  93. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +16 -0
  94. metadata +223 -0
@@ -0,0 +1,299 @@
1
+ require 'active_support/core_ext/array/wrap'
2
+
3
+ module ActiveRecord
4
+ module Associations
5
+ # = Active Record Associations
6
+ #
7
+ # This is the root class of all association proxies:
8
+ #
9
+ # AssociationProxy
10
+ # BelongsToAssociation
11
+ # HasOneAssociation
12
+ # BelongsToPolymorphicAssociation
13
+ # AssociationCollection
14
+ # HasAndBelongsToManyAssociation
15
+ # HasManyAssociation
16
+ # HasManyThroughAssociation
17
+ # HasOneThroughAssociation
18
+ #
19
+ # Association proxies in Active Record are middlemen between the object that
20
+ # holds the association, known as the <tt>@owner</tt>, and the actual associated
21
+ # object, known as the <tt>@target</tt>. The kind of association any proxy is
22
+ # about is available in <tt>@reflection</tt>. That's an instance of the class
23
+ # ActiveRecord::Reflection::AssociationReflection.
24
+ #
25
+ # For example, given
26
+ #
27
+ # class Blog < ActiveRecord::Base
28
+ # has_many :posts
29
+ # end
30
+ #
31
+ # blog = Blog.find(:first)
32
+ #
33
+ # the association proxy in <tt>blog.posts</tt> has the object in +blog+ as
34
+ # <tt>@owner</tt>, the collection of its posts as <tt>@target</tt>, and
35
+ # the <tt>@reflection</tt> object represents a <tt>:has_many</tt> macro.
36
+ #
37
+ # This class has most of the basic instance methods removed, and delegates
38
+ # unknown methods to <tt>@target</tt> via <tt>method_missing</tt>. As a
39
+ # corner case, it even removes the +class+ method and that's why you get
40
+ #
41
+ # blog.posts.class # => Array
42
+ #
43
+ # though the object behind <tt>blog.posts</tt> is not an Array, but an
44
+ # ActiveRecord::Associations::HasManyAssociation.
45
+ #
46
+ # The <tt>@target</tt> object is not \loaded until needed. For example,
47
+ #
48
+ # blog.posts.count
49
+ #
50
+ # is computed directly through SQL and does not trigger by itself the
51
+ # instantiation of the actual post records.
52
+ class AssociationProxy #:nodoc:
53
+ alias_method :proxy_respond_to?, :respond_to?
54
+ alias_method :proxy_extend, :extend
55
+ delegate :to_param, :to => :proxy_target
56
+ instance_methods.each { |m| undef_method m unless m.to_s =~ /^(?:nil\?|send|object_id|to_a)$|^__|^respond_to_missing|proxy_/ }
57
+
58
+ def initialize(owner, reflection)
59
+ @owner, @reflection = owner, reflection
60
+ @updated = false
61
+ reflection.check_validity!
62
+ Array.wrap(reflection.options[:extend]).each { |ext| proxy_extend(ext) }
63
+ reset
64
+ end
65
+
66
+ # Returns the owner of the proxy.
67
+ def proxy_owner
68
+ @owner
69
+ end
70
+
71
+ # Returns the reflection object that represents the association handled
72
+ # by the proxy.
73
+ def proxy_reflection
74
+ @reflection
75
+ end
76
+
77
+ # Returns the \target of the proxy, same as +target+.
78
+ def proxy_target
79
+ @target
80
+ end
81
+
82
+ # Does the proxy or its \target respond to +symbol+?
83
+ def respond_to?(*args)
84
+ proxy_respond_to?(*args) || (load_target && @target.respond_to?(*args))
85
+ end
86
+
87
+ # Forwards <tt>===</tt> explicitly to the \target because the instance method
88
+ # removal above doesn't catch it. Loads the \target if needed.
89
+ def ===(other)
90
+ load_target
91
+ other === @target
92
+ end
93
+
94
+ # Returns the name of the table of the related class:
95
+ #
96
+ # post.comments.aliased_table_name # => "comments"
97
+ #
98
+ def aliased_table_name
99
+ @reflection.klass.table_name
100
+ end
101
+
102
+ # Returns the SQL string that corresponds to the <tt>:conditions</tt>
103
+ # option of the macro, if given, or +nil+ otherwise.
104
+ def conditions
105
+ @conditions ||= interpolate_sanitized_sql(@reflection.sanitized_conditions) if @reflection.sanitized_conditions
106
+ end
107
+ alias :sql_conditions :conditions
108
+
109
+ # Resets the \loaded flag to +false+ and sets the \target to +nil+.
110
+ def reset
111
+ @loaded = false
112
+ @target = nil
113
+ end
114
+
115
+ # Reloads the \target and returns +self+ on success.
116
+ def reload
117
+ reset
118
+ load_target
119
+ self unless @target.nil?
120
+ end
121
+
122
+ # Has the \target been already \loaded?
123
+ def loaded?
124
+ @loaded
125
+ end
126
+
127
+ # Asserts the \target has been loaded setting the \loaded flag to +true+.
128
+ def loaded
129
+ @loaded = true
130
+ end
131
+
132
+ # Returns the target of this proxy, same as +proxy_target+.
133
+ def target
134
+ @target
135
+ end
136
+
137
+ # Sets the target of this proxy to <tt>\target</tt>, and the \loaded flag to +true+.
138
+ def target=(target)
139
+ @target = target
140
+ loaded
141
+ end
142
+
143
+ # Forwards the call to the target. Loads the \target if needed.
144
+ def inspect
145
+ load_target
146
+ @target.inspect
147
+ end
148
+
149
+ def send(method, *args)
150
+ if proxy_respond_to?(method)
151
+ super
152
+ else
153
+ load_target
154
+ @target.send(method, *args)
155
+ end
156
+ end
157
+
158
+ protected
159
+ # Does the association have a <tt>:dependent</tt> option?
160
+ def dependent?
161
+ @reflection.options[:dependent]
162
+ end
163
+
164
+ def interpolate_sanitized_sql(sql, record = nil, sanitize_klass = @reflection.klass)
165
+ @owner.send(:interpolate_sanitized_sql, sql, record, sanitize_klass)
166
+ end
167
+
168
+ def interpolate_and_sanitize_sql(sql, record = nil, sanitize_klass = @reflection.klass)
169
+ @owner.send(:interpolate_and_sanitize_sql, sql, record, sanitize_klass)
170
+ end
171
+
172
+ # Forwards the call to the reflection class.
173
+ def sanitize_sql(sql, table_name = @reflection.klass.table_name)
174
+ @reflection.klass.send(:sanitize_sql, sql, table_name)
175
+ end
176
+
177
+ # Assigns the ID of the owner to the corresponding foreign key in +record+.
178
+ # If the association is polymorphic the type of the owner is also set.
179
+ def set_belongs_to_association_for(record)
180
+ if @reflection.options[:as]
181
+ record["#{@reflection.options[:as]}_id"] = @owner.id unless @owner.new_record?
182
+ record["#{@reflection.options[:as]}_type"] = @owner.class.base_class.name.to_s
183
+ else
184
+ unless @owner.new_record?
185
+ primary_key = @reflection.options[:primary_key] || :id
186
+ record[@reflection.primary_key_name] = @owner.send(primary_key)
187
+ end
188
+ end
189
+ end
190
+
191
+ # Merges into +options+ the ones coming from the reflection.
192
+ def merge_options_from_reflection!(options)
193
+ options.reverse_merge!(
194
+ :group => @reflection.options[:group],
195
+ :having => @reflection.options[:having],
196
+ :limit => @reflection.options[:limit],
197
+ :offset => @reflection.options[:offset],
198
+ :joins => @reflection.options[:joins],
199
+ :include => @reflection.options[:include],
200
+ :select => @reflection.options[:select],
201
+ :readonly => @reflection.options[:readonly]
202
+ )
203
+ end
204
+
205
+ # Forwards +with_scope+ to the reflection.
206
+ def with_scope(*args, &block)
207
+ @reflection.klass.send :with_scope, *args, &block
208
+ end
209
+
210
+ private
211
+ # Forwards any missing method call to the \target.
212
+ def method_missing(method, *args)
213
+ if load_target
214
+ unless @target.respond_to?(method)
215
+ message = "undefined method `#{method.to_s}' for \"#{@target}\":#{@target.class.to_s}"
216
+ raise NoMethodError, message
217
+ end
218
+
219
+ if block_given?
220
+ @target.send(method, *args) { |*block_args| yield(*block_args) }
221
+ else
222
+ @target.send(method, *args)
223
+ end
224
+ end
225
+ end
226
+
227
+ # Loads the \target if needed and returns it.
228
+ #
229
+ # This method is abstract in the sense that it relies on +find_target+,
230
+ # which is expected to be provided by descendants.
231
+ #
232
+ # If the \target is already \loaded it is just returned. Thus, you can call
233
+ # +load_target+ unconditionally to get the \target.
234
+ #
235
+ # ActiveRecord::RecordNotFound is rescued within the method, and it is
236
+ # not reraised. The proxy is \reset and +nil+ is the return value.
237
+ def load_target
238
+ return nil unless defined?(@loaded)
239
+
240
+ if !loaded? and (!@owner.new_record? || foreign_key_present)
241
+ @target = find_target
242
+ end
243
+
244
+ @loaded = true
245
+ @target
246
+ rescue ActiveRecord::RecordNotFound
247
+ reset
248
+ end
249
+
250
+ # Can be overwritten by associations that might have the foreign key
251
+ # available for an association without having the object itself (and
252
+ # still being a new record). Currently, only +belongs_to+ presents
253
+ # this scenario (both vanilla and polymorphic).
254
+ def foreign_key_present
255
+ false
256
+ end
257
+
258
+ # Raises ActiveRecord::AssociationTypeMismatch unless +record+ is of
259
+ # the kind of the class of the associated objects. Meant to be used as
260
+ # a sanity check when you are about to assign an associated record.
261
+ def raise_on_type_mismatch(record)
262
+ unless record.is_a?(@reflection.klass) || record.is_a?(@reflection.class_name.constantize)
263
+ message = "#{@reflection.class_name}(##{@reflection.klass.object_id}) expected, got #{record.class}(##{record.class.object_id})"
264
+ raise ActiveRecord::AssociationTypeMismatch, message
265
+ end
266
+ end
267
+
268
+ if RUBY_VERSION < '1.9.2'
269
+ # Array#flatten has problems with recursive arrays before Ruby 1.9.2.
270
+ # Going one level deeper solves the majority of the problems.
271
+ def flatten_deeper(array)
272
+ array.collect { |element| (element.respond_to?(:flatten) && !element.is_a?(Hash)) ? element.flatten : element }.flatten
273
+ end
274
+ else
275
+ def flatten_deeper(array)
276
+ array.flatten
277
+ end
278
+ end
279
+
280
+ # Returns the ID of the owner, quoted if needed.
281
+ def owner_quoted_id
282
+ @owner.quoted_id
283
+ end
284
+
285
+ def set_inverse_instance(record, instance)
286
+ return if record.nil? || !we_can_set_the_inverse_on_this?(record)
287
+ inverse_relationship = @reflection.inverse_of
288
+ unless inverse_relationship.nil?
289
+ record.send(:"set_#{inverse_relationship.name}_target", instance)
290
+ end
291
+ end
292
+
293
+ # Override in subclasses
294
+ def we_can_set_the_inverse_on_this?(record)
295
+ false
296
+ end
297
+ end
298
+ end
299
+ end
@@ -0,0 +1,91 @@
1
+ module ActiveRecord
2
+ # = Active Record Belongs To Associations
3
+ module Associations
4
+ class BelongsToAssociation < AssociationProxy #:nodoc:
5
+ def create(attributes = {})
6
+ replace(@reflection.create_association(attributes))
7
+ end
8
+
9
+ def build(attributes = {})
10
+ replace(@reflection.build_association(attributes))
11
+ end
12
+
13
+ def replace(record)
14
+ counter_cache_name = @reflection.counter_cache_column
15
+
16
+ if record.nil?
17
+ if counter_cache_name && !@owner.new_record?
18
+ @reflection.klass.decrement_counter(counter_cache_name, previous_record_id) if @owner[@reflection.primary_key_name]
19
+ end
20
+
21
+ @target = @owner[@reflection.primary_key_name] = nil
22
+ else
23
+ raise_on_type_mismatch(record)
24
+
25
+ if counter_cache_name && !@owner.new_record? && record.id != @owner[@reflection.primary_key_name]
26
+ @reflection.klass.increment_counter(counter_cache_name, record.id)
27
+ @reflection.klass.decrement_counter(counter_cache_name, @owner[@reflection.primary_key_name]) if @owner[@reflection.primary_key_name]
28
+ end
29
+
30
+ @target = (AssociationProxy === record ? record.target : record)
31
+ @owner[@reflection.primary_key_name] = record_id(record) unless record.new_record?
32
+ @updated = true
33
+ end
34
+
35
+ set_inverse_instance(record, @owner)
36
+
37
+ loaded
38
+ record
39
+ end
40
+
41
+ def updated?
42
+ @updated
43
+ end
44
+
45
+ private
46
+ def find_target
47
+ find_method = if @reflection.options[:primary_key]
48
+ "find_by_#{@reflection.options[:primary_key]}"
49
+ else
50
+ "find"
51
+ end
52
+
53
+ options = @reflection.options.dup
54
+ (options.keys - [:select, :include, :readonly]).each do |key|
55
+ options.delete key
56
+ end
57
+ options[:conditions] = conditions
58
+
59
+ the_target = @reflection.klass.send(find_method,
60
+ @owner[@reflection.primary_key_name],
61
+ options
62
+ ) if @owner[@reflection.primary_key_name]
63
+ set_inverse_instance(the_target, @owner)
64
+ the_target
65
+ end
66
+
67
+ def foreign_key_present
68
+ !@owner[@reflection.primary_key_name].nil?
69
+ end
70
+
71
+ # NOTE - for now, we're only supporting inverse setting from belongs_to back onto
72
+ # has_one associations.
73
+ def we_can_set_the_inverse_on_this?(record)
74
+ @reflection.has_inverse? && @reflection.inverse_of.macro == :has_one
75
+ end
76
+
77
+ def record_id(record)
78
+ record.send(@reflection.options[:primary_key] || :id)
79
+ end
80
+
81
+ def previous_record_id
82
+ @previous_record_id ||= if @reflection.options[:primary_key]
83
+ previous_record = @owner.send(@reflection.name)
84
+ previous_record.nil? ? nil : previous_record.id
85
+ else
86
+ @owner[@reflection.primary_key_name]
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,82 @@
1
+ module ActiveRecord
2
+ # = Active Record Belongs To Polymorphic Association
3
+ module Associations
4
+ class BelongsToPolymorphicAssociation < AssociationProxy #:nodoc:
5
+ def replace(record)
6
+ if record.nil?
7
+ @target = @owner[@reflection.primary_key_name] = @owner[@reflection.options[:foreign_type]] = nil
8
+ else
9
+ @target = (AssociationProxy === record ? record.target : record)
10
+
11
+ @owner[@reflection.primary_key_name] = record_id(record)
12
+ @owner[@reflection.options[:foreign_type]] = record.class.base_class.name.to_s
13
+
14
+ @updated = true
15
+ end
16
+
17
+ set_inverse_instance(record, @owner)
18
+ loaded
19
+ record
20
+ end
21
+
22
+ def updated?
23
+ @updated
24
+ end
25
+
26
+ def conditions
27
+ @conditions ||= @reflection.options[:conditions] && interpolate_and_sanitize_sql(@reflection.options[:conditions], nil, association_class)
28
+ end
29
+
30
+ private
31
+
32
+ # NOTE - for now, we're only supporting inverse setting from belongs_to back onto
33
+ # has_one associations.
34
+ def we_can_set_the_inverse_on_this?(record)
35
+ if @reflection.has_inverse?
36
+ inverse_association = @reflection.polymorphic_inverse_of(record.class)
37
+ inverse_association && inverse_association.macro == :has_one
38
+ else
39
+ false
40
+ end
41
+ end
42
+
43
+ def set_inverse_instance(record, instance)
44
+ return if record.nil? || !we_can_set_the_inverse_on_this?(record)
45
+ inverse_relationship = @reflection.polymorphic_inverse_of(record.class)
46
+ unless inverse_relationship.nil?
47
+ record.send(:"set_#{inverse_relationship.name}_target", instance)
48
+ end
49
+ end
50
+
51
+ def find_target
52
+ return nil if association_class.nil?
53
+
54
+ target =
55
+ if @reflection.options[:conditions]
56
+ association_class.find(
57
+ @owner[@reflection.primary_key_name],
58
+ :select => @reflection.options[:select],
59
+ :conditions => conditions,
60
+ :include => @reflection.options[:include]
61
+ )
62
+ else
63
+ association_class.find(@owner[@reflection.primary_key_name], :select => @reflection.options[:select], :include => @reflection.options[:include])
64
+ end
65
+ set_inverse_instance(target, @owner)
66
+ target
67
+ end
68
+
69
+ def foreign_key_present
70
+ !@owner[@reflection.primary_key_name].nil?
71
+ end
72
+
73
+ def record_id(record)
74
+ record.send(@reflection.options[:primary_key] || :id)
75
+ end
76
+
77
+ def association_class
78
+ @owner[@reflection.options[:foreign_type]] ? @owner[@reflection.options[:foreign_type]].constantize : nil
79
+ end
80
+ end
81
+ end
82
+ end