square-activerecord 3.0.7

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.
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,411 @@
1
+ module ActiveRecord
2
+ # = Active Record Reflection
3
+ module Reflection # :nodoc:
4
+ extend ActiveSupport::Concern
5
+
6
+ # Reflection enables to interrogate Active Record classes and objects
7
+ # about their associations and aggregations. This information can,
8
+ # for example, be used in a form builder that takes an Active Record object
9
+ # and creates input fields for all of the attributes depending on their type
10
+ # and displays the associations to other objects.
11
+ #
12
+ # MacroReflection class has info for AggregateReflection and AssociationReflection
13
+ # classes.
14
+ module ClassMethods
15
+ def create_reflection(macro, name, options, active_record)
16
+ case macro
17
+ when :has_many, :belongs_to, :has_one, :has_and_belongs_to_many
18
+ klass = options[:through] ? ThroughReflection : AssociationReflection
19
+ reflection = klass.new(macro, name, options, active_record)
20
+ when :composed_of
21
+ reflection = AggregateReflection.new(macro, name, options, active_record)
22
+ end
23
+ write_inheritable_hash :reflections, name => reflection
24
+ reflection
25
+ end
26
+
27
+ # Returns a hash containing all AssociationReflection objects for the current class.
28
+ # Example:
29
+ #
30
+ # Invoice.reflections
31
+ # Account.reflections
32
+ #
33
+ def reflections
34
+ read_inheritable_attribute(:reflections) || write_inheritable_attribute(:reflections, {})
35
+ end
36
+
37
+ # Returns an array of AggregateReflection objects for all the aggregations in the class.
38
+ def reflect_on_all_aggregations
39
+ reflections.values.select { |reflection| reflection.is_a?(AggregateReflection) }
40
+ end
41
+
42
+ # Returns the AggregateReflection object for the named +aggregation+ (use the symbol).
43
+ #
44
+ # Account.reflect_on_aggregation(:balance) # => the balance AggregateReflection
45
+ #
46
+ def reflect_on_aggregation(aggregation)
47
+ reflections[aggregation].is_a?(AggregateReflection) ? reflections[aggregation] : nil
48
+ end
49
+
50
+ # Returns an array of AssociationReflection objects for all the
51
+ # associations in the class. If you only want to reflect on a certain
52
+ # association type, pass in the symbol (<tt>:has_many</tt>, <tt>:has_one</tt>,
53
+ # <tt>:belongs_to</tt>) as the first parameter.
54
+ #
55
+ # Example:
56
+ #
57
+ # Account.reflect_on_all_associations # returns an array of all associations
58
+ # Account.reflect_on_all_associations(:has_many) # returns an array of all has_many associations
59
+ #
60
+ def reflect_on_all_associations(macro = nil)
61
+ association_reflections = reflections.values.select { |reflection| reflection.is_a?(AssociationReflection) }
62
+ macro ? association_reflections.select { |reflection| reflection.macro == macro } : association_reflections
63
+ end
64
+
65
+ # Returns the AssociationReflection object for the +association+ (use the symbol).
66
+ #
67
+ # Account.reflect_on_association(:owner) # returns the owner AssociationReflection
68
+ # Invoice.reflect_on_association(:line_items).macro # returns :has_many
69
+ #
70
+ def reflect_on_association(association)
71
+ reflections[association].is_a?(AssociationReflection) ? reflections[association] : nil
72
+ end
73
+
74
+ # Returns an array of AssociationReflection objects for all associations which have <tt>:autosave</tt> enabled.
75
+ def reflect_on_all_autosave_associations
76
+ reflections.values.select { |reflection| reflection.options[:autosave] }
77
+ end
78
+ end
79
+
80
+
81
+ # Abstract base class for AggregateReflection and AssociationReflection. Objects of
82
+ # AggregateReflection and AssociationReflection are returned by the Reflection::ClassMethods.
83
+ class MacroReflection
84
+ attr_reader :active_record
85
+
86
+ def initialize(macro, name, options, active_record)
87
+ @macro, @name, @options, @active_record = macro, name, options, active_record
88
+ end
89
+
90
+ # Returns the name of the macro.
91
+ #
92
+ # <tt>composed_of :balance, :class_name => 'Money'</tt> returns <tt>:balance</tt>
93
+ # <tt>has_many :clients</tt> returns <tt>:clients</tt>
94
+ attr_reader :name
95
+
96
+ # Returns the macro type.
97
+ #
98
+ # <tt>composed_of :balance, :class_name => 'Money'</tt> returns <tt>:composed_of</tt>
99
+ # <tt>has_many :clients</tt> returns <tt>:has_many</tt>
100
+ attr_reader :macro
101
+
102
+ # Returns the hash of options used for the macro.
103
+ #
104
+ # <tt>composed_of :balance, :class_name => 'Money'</tt> returns <tt>{ :class_name => "Money" }</tt>
105
+ # <tt>has_many :clients</tt> returns +{}+
106
+ attr_reader :options
107
+
108
+ # Returns the class for the macro.
109
+ #
110
+ # <tt>composed_of :balance, :class_name => 'Money'</tt> returns the Money class
111
+ # <tt>has_many :clients</tt> returns the Client class
112
+ def klass
113
+ @klass ||= class_name.constantize
114
+ end
115
+
116
+ # Returns the class name for the macro.
117
+ #
118
+ # <tt>composed_of :balance, :class_name => 'Money'</tt> returns <tt>'Money'</tt>
119
+ # <tt>has_many :clients</tt> returns <tt>'Client'</tt>
120
+ def class_name
121
+ @class_name ||= options[:class_name] || derive_class_name
122
+ end
123
+
124
+ # Returns +true+ if +self+ and +other_aggregation+ have the same +name+ attribute, +active_record+ attribute,
125
+ # and +other_aggregation+ has an options hash assigned to it.
126
+ def ==(other_aggregation)
127
+ other_aggregation.kind_of?(self.class) && name == other_aggregation.name && other_aggregation.options && active_record == other_aggregation.active_record
128
+ end
129
+
130
+ def sanitized_conditions #:nodoc:
131
+ @sanitized_conditions ||= klass.send(:sanitize_sql, options[:conditions]) if options[:conditions]
132
+ end
133
+
134
+ private
135
+ def derive_class_name
136
+ name.to_s.camelize
137
+ end
138
+ end
139
+
140
+
141
+ # Holds all the meta-data about an aggregation as it was specified in the
142
+ # Active Record class.
143
+ class AggregateReflection < MacroReflection #:nodoc:
144
+ end
145
+
146
+ # Holds all the meta-data about an association as it was specified in the
147
+ # Active Record class.
148
+ class AssociationReflection < MacroReflection #:nodoc:
149
+ # Returns the target association's class.
150
+ #
151
+ # class Author < ActiveRecord::Base
152
+ # has_many :books
153
+ # end
154
+ #
155
+ # Author.reflect_on_association(:books).klass
156
+ # # => Book
157
+ #
158
+ # <b>Note:</b> Do not call +klass.new+ or +klass.create+ to instantiate
159
+ # a new association object. Use +build_association+ or +create_association+
160
+ # instead. This allows plugins to hook into association object creation.
161
+ def klass
162
+ @klass ||= active_record.send(:compute_type, class_name)
163
+ end
164
+
165
+ def initialize(macro, name, options, active_record)
166
+ super
167
+ @collection = [:has_many, :has_and_belongs_to_many].include?(macro)
168
+ end
169
+
170
+ # Returns a new, unsaved instance of the associated class. +options+ will
171
+ # be passed to the class's constructor.
172
+ def build_association(*options)
173
+ klass.new(*options)
174
+ end
175
+
176
+ # Creates a new instance of the associated class, and immediately saves it
177
+ # with ActiveRecord::Base#save. +options+ will be passed to the class's
178
+ # creation method. Returns the newly created object.
179
+ def create_association(*options)
180
+ klass.create(*options)
181
+ end
182
+
183
+ # Creates a new instance of the associated class, and immediately saves it
184
+ # with ActiveRecord::Base#save!. +options+ will be passed to the class's
185
+ # creation method. If the created record doesn't pass validations, then an
186
+ # exception will be raised.
187
+ #
188
+ # Returns the newly created object.
189
+ def create_association!(*options)
190
+ klass.create!(*options)
191
+ end
192
+
193
+ def table_name
194
+ @table_name ||= klass.table_name
195
+ end
196
+
197
+ def quoted_table_name
198
+ @quoted_table_name ||= klass.quoted_table_name
199
+ end
200
+
201
+ def primary_key_name
202
+ @primary_key_name ||= options[:foreign_key] || derive_primary_key_name
203
+ end
204
+
205
+ def primary_key_column
206
+ @primary_key_column ||= klass.columns.find { |c| c.name == klass.primary_key }
207
+ end
208
+
209
+ def association_foreign_key
210
+ @association_foreign_key ||= options[:association_foreign_key] || class_name.foreign_key
211
+ end
212
+
213
+ def association_primary_key
214
+ @association_primary_key ||= options[:primary_key] || klass.primary_key
215
+ end
216
+
217
+ def active_record_primary_key
218
+ @active_record_primary_key ||= options[:primary_key] || active_record.primary_key
219
+ end
220
+
221
+ def counter_cache_column
222
+ if options[:counter_cache] == true
223
+ "#{active_record.name.demodulize.underscore.pluralize}_count"
224
+ elsif options[:counter_cache]
225
+ options[:counter_cache]
226
+ end
227
+ end
228
+
229
+ def columns(tbl_name, log_msg)
230
+ @columns ||= klass.connection.columns(tbl_name, log_msg)
231
+ end
232
+
233
+ def reset_column_information
234
+ @columns = nil
235
+ end
236
+
237
+ def check_validity!
238
+ check_validity_of_inverse!
239
+ end
240
+
241
+ def check_validity_of_inverse!
242
+ unless options[:polymorphic]
243
+ if has_inverse? && inverse_of.nil?
244
+ raise InverseOfAssociationNotFoundError.new(self)
245
+ end
246
+ end
247
+ end
248
+
249
+ def through_reflection
250
+ false
251
+ end
252
+
253
+ def through_reflection_primary_key_name
254
+ end
255
+
256
+ def source_reflection
257
+ nil
258
+ end
259
+
260
+ def has_inverse?
261
+ !@options[:inverse_of].nil?
262
+ end
263
+
264
+ def inverse_of
265
+ if has_inverse?
266
+ @inverse_of ||= klass.reflect_on_association(options[:inverse_of])
267
+ end
268
+ end
269
+
270
+ def polymorphic_inverse_of(associated_class)
271
+ if has_inverse?
272
+ if inverse_relationship = associated_class.reflect_on_association(options[:inverse_of])
273
+ inverse_relationship
274
+ else
275
+ raise InverseOfAssociationNotFoundError.new(self, associated_class)
276
+ end
277
+ end
278
+ end
279
+
280
+ # Returns whether or not this association reflection is for a collection
281
+ # association. Returns +true+ if the +macro+ is either +has_many+ or
282
+ # +has_and_belongs_to_many+, +false+ otherwise.
283
+ def collection?
284
+ @collection
285
+ end
286
+
287
+ # Returns whether or not the association should be validated as part of
288
+ # the parent's validation.
289
+ #
290
+ # Unless you explicitly disable validation with
291
+ # <tt>:validate => false</tt>, validation will take place when:
292
+ #
293
+ # * you explicitly enable validation; <tt>:validate => true</tt>
294
+ # * you use autosave; <tt>:autosave => true</tt>
295
+ # * the association is a +has_many+ association
296
+ def validate?
297
+ !options[:validate].nil? ? options[:validate] : (options[:autosave] == true || macro == :has_many)
298
+ end
299
+
300
+ def dependent_conditions(record, base_class, extra_conditions)
301
+ dependent_conditions = []
302
+ dependent_conditions << "#{primary_key_name} = #{record.send(name).send(:owner_quoted_id)}"
303
+ dependent_conditions << "#{options[:as]}_type = '#{base_class.name}'" if options[:as]
304
+ dependent_conditions << klass.send(:sanitize_sql, options[:conditions]) if options[:conditions]
305
+ dependent_conditions << extra_conditions if extra_conditions
306
+ dependent_conditions = dependent_conditions.collect {|where| "(#{where})" }.join(" AND ")
307
+ dependent_conditions = dependent_conditions.gsub('@', '\@')
308
+ dependent_conditions
309
+ end
310
+
311
+ # Returns +true+ if +self+ is a +belongs_to+ reflection.
312
+ def belongs_to?
313
+ macro == :belongs_to
314
+ end
315
+
316
+ private
317
+ def derive_class_name
318
+ class_name = name.to_s.camelize
319
+ class_name = class_name.singularize if collection?
320
+ class_name
321
+ end
322
+
323
+ def derive_primary_key_name
324
+ if belongs_to?
325
+ "#{name}_id"
326
+ elsif options[:as]
327
+ "#{options[:as]}_id"
328
+ else
329
+ active_record.name.foreign_key
330
+ end
331
+ end
332
+ end
333
+
334
+ # Holds all the meta-data about a :through association as it was specified
335
+ # in the Active Record class.
336
+ class ThroughReflection < AssociationReflection #:nodoc:
337
+ # Gets the source of the through reflection. It checks both a singularized
338
+ # and pluralized form for <tt>:belongs_to</tt> or <tt>:has_many</tt>.
339
+ #
340
+ # class Post < ActiveRecord::Base
341
+ # has_many :taggings
342
+ # has_many :tags, :through => :taggings
343
+ # end
344
+ #
345
+ def source_reflection
346
+ @source_reflection ||= source_reflection_names.collect { |name| through_reflection.klass.reflect_on_association(name) }.compact.first
347
+ end
348
+
349
+ # Returns the AssociationReflection object specified in the <tt>:through</tt> option
350
+ # of a HasManyThrough or HasOneThrough association.
351
+ #
352
+ # class Post < ActiveRecord::Base
353
+ # has_many :taggings
354
+ # has_many :tags, :through => :taggings
355
+ # end
356
+ #
357
+ # tags_reflection = Post.reflect_on_association(:tags)
358
+ # taggings_reflection = tags_reflection.through_reflection
359
+ #
360
+ def through_reflection
361
+ @through_reflection ||= active_record.reflect_on_association(options[:through])
362
+ end
363
+
364
+ # Gets an array of possible <tt>:through</tt> source reflection names:
365
+ #
366
+ # [:singularized, :pluralized]
367
+ #
368
+ def source_reflection_names
369
+ @source_reflection_names ||= (options[:source] ? [options[:source]] : [name.to_s.singularize, name]).collect { |n| n.to_sym }
370
+ end
371
+
372
+ def check_validity!
373
+ if through_reflection.nil?
374
+ raise HasManyThroughAssociationNotFoundError.new(active_record.name, self)
375
+ end
376
+
377
+ if source_reflection.nil?
378
+ raise HasManyThroughSourceAssociationNotFoundError.new(self)
379
+ end
380
+
381
+ if options[:source_type] && source_reflection.options[:polymorphic].nil?
382
+ raise HasManyThroughAssociationPointlessSourceTypeError.new(active_record.name, self, source_reflection)
383
+ end
384
+
385
+ if source_reflection.options[:polymorphic] && options[:source_type].nil?
386
+ raise HasManyThroughAssociationPolymorphicError.new(active_record.name, self, source_reflection)
387
+ end
388
+
389
+ unless [:belongs_to, :has_many, :has_one].include?(source_reflection.macro) && source_reflection.options[:through].nil?
390
+ raise HasManyThroughSourceAssociationMacroError.new(self)
391
+ end
392
+
393
+ check_validity_of_inverse!
394
+ end
395
+
396
+ def through_reflection_primary_key
397
+ through_reflection.belongs_to? ? through_reflection.klass.primary_key : through_reflection.primary_key_name
398
+ end
399
+
400
+ def through_reflection_primary_key_name
401
+ through_reflection.primary_key_name if through_reflection.belongs_to?
402
+ end
403
+
404
+ private
405
+ def derive_class_name
406
+ # get the class_name of the belongs_to association of the through reflection
407
+ options[:source_type] || source_reflection.class_name
408
+ end
409
+ end
410
+ end
411
+ end
@@ -0,0 +1,394 @@
1
+ require 'active_support/core_ext/object/blank'
2
+
3
+ module ActiveRecord
4
+ # = Active Record Relation
5
+ class Relation
6
+ JoinOperation = Struct.new(:relation, :join_class, :on)
7
+ ASSOCIATION_METHODS = [:includes, :eager_load, :preload]
8
+ MULTI_VALUE_METHODS = [:select, :group, :order, :joins, :where, :having]
9
+ SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :create_with, :from]
10
+
11
+ include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches
12
+
13
+ delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to => :to_a
14
+ delegate :insert, :to => :arel
15
+
16
+ attr_reader :table, :klass, :loaded
17
+ attr_accessor :extensions
18
+ alias :loaded? :loaded
19
+
20
+ def initialize(klass, table)
21
+ @klass, @table = klass, table
22
+
23
+ @implicit_readonly = nil
24
+ @loaded = false
25
+
26
+ SINGLE_VALUE_METHODS.each {|v| instance_variable_set(:"@#{v}_value", nil)}
27
+ (ASSOCIATION_METHODS + MULTI_VALUE_METHODS).each {|v| instance_variable_set(:"@#{v}_values", [])}
28
+ @extensions = []
29
+ end
30
+
31
+ def new(*args, &block)
32
+ scoping { @klass.new(*args, &block) }
33
+ end
34
+
35
+ def initialize_copy(other)
36
+ reset
37
+ end
38
+
39
+ alias build new
40
+
41
+ def create(*args, &block)
42
+ scoping { @klass.create(*args, &block) }
43
+ end
44
+
45
+ def create!(*args, &block)
46
+ scoping { @klass.create!(*args, &block) }
47
+ end
48
+
49
+ def respond_to?(method, include_private = false)
50
+ return true if arel.respond_to?(method, include_private) || Array.method_defined?(method) || @klass.respond_to?(method, include_private)
51
+
52
+ if match = DynamicFinderMatch.match(method)
53
+ return true if @klass.send(:all_attributes_exists?, match.attribute_names)
54
+ elsif match = DynamicScopeMatch.match(method)
55
+ return true if @klass.send(:all_attributes_exists?, match.attribute_names)
56
+ else
57
+ super
58
+ end
59
+ end
60
+
61
+ def to_a
62
+ return @records if loaded?
63
+
64
+ @records = eager_loading? ? find_with_associations : @klass.find_by_sql(arel.to_sql)
65
+
66
+ preload = @preload_values
67
+ preload += @includes_values unless eager_loading?
68
+ preload.each {|associations| @klass.send(:preload_associations, @records, associations) }
69
+
70
+ # @readonly_value is true only if set explicitly. @implicit_readonly is true if there
71
+ # are JOINS and no explicit SELECT.
72
+ readonly = @readonly_value.nil? ? @implicit_readonly : @readonly_value
73
+ @records.each { |record| record.readonly! } if readonly
74
+
75
+ @loaded = true
76
+ @records
77
+ end
78
+
79
+ def as_json(options = nil) #:nodoc:
80
+ to_a.as_json(options)
81
+ end
82
+
83
+ # Returns size of the records.
84
+ def size
85
+ loaded? ? @records.length : count
86
+ end
87
+
88
+ # Returns true if there are no records.
89
+ def empty?
90
+ return @records.empty? if loaded?
91
+
92
+ c = count
93
+ c.respond_to?(:zero?) ? c.zero? : c.empty?
94
+ end
95
+
96
+ def any?
97
+ if block_given?
98
+ to_a.any? { |*block_args| yield(*block_args) }
99
+ else
100
+ !empty?
101
+ end
102
+ end
103
+
104
+ def many?
105
+ if block_given?
106
+ to_a.many? { |*block_args| yield(*block_args) }
107
+ else
108
+ @limit_value ? to_a.many? : size > 1
109
+ end
110
+ end
111
+
112
+ # Scope all queries to the current scope.
113
+ #
114
+ # ==== Example
115
+ #
116
+ # Comment.where(:post_id => 1).scoping do
117
+ # Comment.first # SELECT * FROM comments WHERE post_id = 1
118
+ # end
119
+ #
120
+ # Please check unscoped if you want to remove all previous scopes (including
121
+ # the default_scope) during the execution of a block.
122
+ def scoping
123
+ @klass.scoped_methods << self
124
+ begin
125
+ yield
126
+ ensure
127
+ @klass.scoped_methods.pop
128
+ end
129
+ end
130
+
131
+ # Updates all records with details given if they match a set of conditions supplied, limits and order can
132
+ # also be supplied. This method constructs a single SQL UPDATE statement and sends it straight to the
133
+ # database. It does not instantiate the involved models and it does not trigger Active Record callbacks
134
+ # or validations.
135
+ #
136
+ # ==== Parameters
137
+ #
138
+ # * +updates+ - A string, array, or hash representing the SET part of an SQL statement.
139
+ # * +conditions+ - A string, array, or hash representing the WHERE part of an SQL statement.
140
+ # See conditions in the intro.
141
+ # * +options+ - Additional options are <tt>:limit</tt> and <tt>:order</tt>, see the examples for usage.
142
+ #
143
+ # ==== Examples
144
+ #
145
+ # # Update all customers with the given attributes
146
+ # Customer.update_all :wants_email => true
147
+ #
148
+ # # Update all books with 'Rails' in their title
149
+ # Book.update_all "author = 'David'", "title LIKE '%Rails%'"
150
+ #
151
+ # # Update all avatars migrated more than a week ago
152
+ # Avatar.update_all ['migrated_at = ?', Time.now.utc], ['migrated_at > ?', 1.week.ago]
153
+ #
154
+ # # Update all books that match conditions, but limit it to 5 ordered by date
155
+ # Book.update_all "author = 'David'", "title LIKE '%Rails%'", :order => 'created_at', :limit => 5
156
+ def update_all(updates, conditions = nil, options = {})
157
+ if conditions || options.present?
158
+ where(conditions).apply_finder_options(options.slice(:limit, :order)).update_all(updates)
159
+ else
160
+ # Apply limit and order only if they're both present
161
+ if @limit_value.present? == @order_values.present?
162
+ arel.update(Arel::SqlLiteral.new(@klass.send(:sanitize_sql_for_assignment, updates)))
163
+ else
164
+ except(:limit, :order).update_all(updates)
165
+ end
166
+ end
167
+ end
168
+
169
+ # Updates an object (or multiple objects) and saves it to the database, if validations pass.
170
+ # The resulting object is returned whether the object was saved successfully to the database or not.
171
+ #
172
+ # ==== Parameters
173
+ #
174
+ # * +id+ - This should be the id or an array of ids to be updated.
175
+ # * +attributes+ - This should be a hash of attributes or an array of hashes.
176
+ #
177
+ # ==== Examples
178
+ #
179
+ # # Updates one record
180
+ # Person.update(15, :user_name => 'Samuel', :group => 'expert')
181
+ #
182
+ # # Updates multiple records
183
+ # people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy" } }
184
+ # Person.update(people.keys, people.values)
185
+ def update(id, attributes)
186
+ if id.is_a?(Array)
187
+ idx = -1
188
+ id.collect { |one_id| idx += 1; update(one_id, attributes[idx]) }
189
+ else
190
+ object = find(id)
191
+ object.update_attributes(attributes)
192
+ object
193
+ end
194
+ end
195
+
196
+ # Destroys the records matching +conditions+ by instantiating each
197
+ # record and calling its +destroy+ method. Each object's callbacks are
198
+ # executed (including <tt>:dependent</tt> association options and
199
+ # +before_destroy+/+after_destroy+ Observer methods). Returns the
200
+ # collection of objects that were destroyed; each will be frozen, to
201
+ # reflect that no changes should be made (since they can't be
202
+ # persisted).
203
+ #
204
+ # Note: Instantiation, callback execution, and deletion of each
205
+ # record can be time consuming when you're removing many records at
206
+ # once. It generates at least one SQL +DELETE+ query per record (or
207
+ # possibly more, to enforce your callbacks). If you want to delete many
208
+ # rows quickly, without concern for their associations or callbacks, use
209
+ # +delete_all+ instead.
210
+ #
211
+ # ==== Parameters
212
+ #
213
+ # * +conditions+ - A string, array, or hash that specifies which records
214
+ # to destroy. If omitted, all records are destroyed. See the
215
+ # Conditions section in the introduction to ActiveRecord::Base for
216
+ # more information.
217
+ #
218
+ # ==== Examples
219
+ #
220
+ # Person.destroy_all("last_login < '2004-04-04'")
221
+ # Person.destroy_all(:status => "inactive")
222
+ def destroy_all(conditions = nil)
223
+ if conditions
224
+ where(conditions).destroy_all
225
+ else
226
+ to_a.each {|object| object.destroy }.tap { reset }
227
+ end
228
+ end
229
+
230
+ # Destroy an object (or multiple objects) that has the given id, the object is instantiated first,
231
+ # therefore all callbacks and filters are fired off before the object is deleted. This method is
232
+ # less efficient than ActiveRecord#delete but allows cleanup methods and other actions to be run.
233
+ #
234
+ # This essentially finds the object (or multiple objects) with the given id, creates a new object
235
+ # from the attributes, and then calls destroy on it.
236
+ #
237
+ # ==== Parameters
238
+ #
239
+ # * +id+ - Can be either an Integer or an Array of Integers.
240
+ #
241
+ # ==== Examples
242
+ #
243
+ # # Destroy a single object
244
+ # Todo.destroy(1)
245
+ #
246
+ # # Destroy multiple objects
247
+ # todos = [1,2,3]
248
+ # Todo.destroy(todos)
249
+ def destroy(id)
250
+ if id.is_a?(Array)
251
+ id.map { |one_id| destroy(one_id) }
252
+ else
253
+ find(id).destroy
254
+ end
255
+ end
256
+
257
+ # Deletes the records matching +conditions+ without instantiating the records first, and hence not
258
+ # calling the +destroy+ method nor invoking callbacks. This is a single SQL DELETE statement that
259
+ # goes straight to the database, much more efficient than +destroy_all+. Be careful with relations
260
+ # though, in particular <tt>:dependent</tt> rules defined on associations are not honored. Returns
261
+ # the number of rows affected.
262
+ #
263
+ # ==== Parameters
264
+ #
265
+ # * +conditions+ - Conditions are specified the same way as with +find+ method.
266
+ #
267
+ # ==== Example
268
+ #
269
+ # Post.delete_all("person_id = 5 AND (category = 'Something' OR category = 'Else')")
270
+ # Post.delete_all(["person_id = ? AND (category = ? OR category = ?)", 5, 'Something', 'Else'])
271
+ #
272
+ # Both calls delete the affected posts all at once with a single DELETE statement.
273
+ # If you need to destroy dependent associations or call your <tt>before_*</tt> or
274
+ # +after_destroy+ callbacks, use the +destroy_all+ method instead.
275
+ def delete_all(conditions = nil)
276
+ conditions ? where(conditions).delete_all : arel.delete.tap { reset }
277
+ end
278
+
279
+ # Deletes the row with a primary key matching the +id+ argument, using a
280
+ # SQL +DELETE+ statement, and returns the number of rows deleted. Active
281
+ # Record objects are not instantiated, so the object's callbacks are not
282
+ # executed, including any <tt>:dependent</tt> association options or
283
+ # Observer methods.
284
+ #
285
+ # You can delete multiple rows at once by passing an Array of <tt>id</tt>s.
286
+ #
287
+ # Note: Although it is often much faster than the alternative,
288
+ # <tt>#destroy</tt>, skipping callbacks might bypass business logic in
289
+ # your application that ensures referential integrity or performs other
290
+ # essential jobs.
291
+ #
292
+ # ==== Examples
293
+ #
294
+ # # Delete a single row
295
+ # Todo.delete(1)
296
+ #
297
+ # # Delete multiple rows
298
+ # Todo.delete([2,3,4])
299
+ def delete(id_or_array)
300
+ where(@klass.primary_key => id_or_array).delete_all
301
+ end
302
+
303
+ def reload
304
+ reset
305
+ to_a # force reload
306
+ self
307
+ end
308
+
309
+ def reset
310
+ @first = @last = @to_sql = @order_clause = @scope_for_create = @arel = @loaded = nil
311
+ @should_eager_load = @join_dependency = nil
312
+ @records = []
313
+ self
314
+ end
315
+
316
+ def primary_key
317
+ @primary_key ||= table[@klass.primary_key]
318
+ end
319
+
320
+ def to_sql
321
+ @to_sql ||= arel.to_sql
322
+ end
323
+
324
+ def where_values_hash
325
+ Hash[@where_values.find_all { |w|
326
+ w.respond_to?(:operator) && w.operator == :== && w.left.relation.name == table_name
327
+ }.map { |where|
328
+ [
329
+ where.left.name,
330
+ where.right.respond_to?(:value) ? where.right.value : where.right
331
+ ]
332
+ }]
333
+ end
334
+
335
+ def scope_for_create
336
+ @scope_for_create ||= begin
337
+ if @create_with_value
338
+ @create_with_value.reverse_merge(where_values_hash)
339
+ else
340
+ where_values_hash
341
+ end
342
+ end
343
+ end
344
+
345
+ def eager_loading?
346
+ @should_eager_load ||= (@eager_load_values.any? || (@includes_values.any? && references_eager_loaded_tables?))
347
+ end
348
+
349
+ def ==(other)
350
+ case other
351
+ when Relation
352
+ other.to_sql == to_sql
353
+ when Array
354
+ to_a == other.to_a
355
+ end
356
+ end
357
+
358
+ def inspect
359
+ to_a.inspect
360
+ end
361
+
362
+ protected
363
+
364
+ def method_missing(method, *args, &block)
365
+ if Array.method_defined?(method)
366
+ to_a.send(method, *args, &block)
367
+ elsif @klass.scopes[method]
368
+ merge(@klass.send(method, *args, &block))
369
+ elsif @klass.respond_to?(method)
370
+ scoping { @klass.send(method, *args, &block) }
371
+ elsif arel.respond_to?(method)
372
+ arel.send(method, *args, &block)
373
+ else
374
+ super
375
+ end
376
+ end
377
+
378
+ private
379
+
380
+ def references_eager_loaded_tables?
381
+ # always convert table names to downcase as in Oracle quoted table names are in uppercase
382
+ joined_tables = (tables_in_string(arel.join_sql) + [table.name, table.table_alias]).compact.map{ |t| t.downcase }.uniq
383
+ (tables_in_string(to_sql) - joined_tables).any?
384
+ end
385
+
386
+ def tables_in_string(string)
387
+ return [] if string.blank?
388
+ # always convert table names to downcase as in Oracle quoted table names are in uppercase
389
+ # ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries
390
+ string.scan(/([a-zA-Z_][\.\w]+).?\./).flatten.map{ |s| s.downcase }.uniq - ['raw_sql_']
391
+ end
392
+
393
+ end
394
+ end