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,48 @@
1
+ module ActiveRecord
2
+ class PredicateBuilder
3
+
4
+ def initialize(engine)
5
+ @engine = engine
6
+ end
7
+
8
+ def build_from_hash(attributes, default_table)
9
+ predicates = attributes.map do |column, value|
10
+ table = default_table
11
+
12
+ if value.is_a?(Hash)
13
+ table = Arel::Table.new(column, :engine => @engine)
14
+ build_from_hash(value, table)
15
+ else
16
+ column = column.to_s
17
+
18
+ if column.include?('.')
19
+ table_name, column = column.split('.', 2)
20
+ table = Arel::Table.new(table_name, :engine => @engine)
21
+ end
22
+
23
+ attribute = table[column] || Arel::Attribute.new(table, column)
24
+
25
+ case value
26
+ when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::Relation
27
+ values = value.to_a.map { |x|
28
+ x.is_a?(ActiveRecord::Base) ? x.id : x
29
+ }
30
+ attribute.in(values)
31
+ when Range, Arel::Relation
32
+ attribute.in(value)
33
+ when ActiveRecord::Base
34
+ attribute.eq(value.id)
35
+ when Class
36
+ # FIXME: I think we need to deprecate this behavior
37
+ attribute.eq(value.name)
38
+ else
39
+ attribute.eq(value)
40
+ end
41
+ end
42
+ end
43
+
44
+ predicates.flatten
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,303 @@
1
+ require 'active_support/core_ext/array/wrap'
2
+ require 'active_support/core_ext/object/blank'
3
+
4
+ module ActiveRecord
5
+ module QueryMethods
6
+ extend ActiveSupport::Concern
7
+
8
+ attr_accessor :includes_values, :eager_load_values, :preload_values,
9
+ :select_values, :group_values, :order_values, :reorder_flag, :joins_values, :where_values, :having_values,
10
+ :limit_value, :offset_value, :lock_value, :readonly_value, :create_with_value, :from_value
11
+
12
+ def includes(*args)
13
+ args.reject! {|a| a.blank? }
14
+
15
+ return clone if args.empty?
16
+
17
+ relation = clone
18
+ relation.includes_values = (relation.includes_values + args).flatten.uniq
19
+ relation
20
+ end
21
+
22
+ def eager_load(*args)
23
+ relation = clone
24
+ relation.eager_load_values += args unless args.blank?
25
+ relation
26
+ end
27
+
28
+ def preload(*args)
29
+ relation = clone
30
+ relation.preload_values += args unless args.blank?
31
+ relation
32
+ end
33
+
34
+ def select(value = Proc.new)
35
+ if block_given?
36
+ to_a.select {|*block_args| value.call(*block_args) }
37
+ else
38
+ relation = clone
39
+ relation.select_values += Array.wrap(value)
40
+ relation
41
+ end
42
+ end
43
+
44
+ def group(*args)
45
+ relation = clone
46
+ relation.group_values += args.flatten unless args.blank?
47
+ relation
48
+ end
49
+
50
+ def order(*args)
51
+ relation = clone
52
+ relation.order_values += args.flatten unless args.blank?
53
+ relation
54
+ end
55
+
56
+ def reorder(*args)
57
+ relation = clone
58
+ unless args.blank?
59
+ relation.order_values = args
60
+ relation.reorder_flag = true
61
+ end
62
+ relation
63
+ end
64
+
65
+ def joins(*args)
66
+ relation = clone
67
+
68
+ args.flatten!
69
+ relation.joins_values += args unless args.blank?
70
+
71
+ relation
72
+ end
73
+
74
+ def where(opts, *rest)
75
+ relation = clone
76
+ relation.where_values += build_where(opts, rest) unless opts.blank?
77
+ relation
78
+ end
79
+
80
+ def having(*args)
81
+ relation = clone
82
+ relation.having_values += build_where(*args) unless args.blank?
83
+ relation
84
+ end
85
+
86
+ def limit(value)
87
+ relation = clone
88
+ relation.limit_value = value
89
+ relation
90
+ end
91
+
92
+ def offset(value)
93
+ relation = clone
94
+ relation.offset_value = value
95
+ relation
96
+ end
97
+
98
+ def lock(locks = true)
99
+ relation = clone
100
+
101
+ case locks
102
+ when String, TrueClass, NilClass
103
+ relation.lock_value = locks || true
104
+ else
105
+ relation.lock_value = false
106
+ end
107
+
108
+ relation
109
+ end
110
+
111
+ def readonly(value = true)
112
+ relation = clone
113
+ relation.readonly_value = value
114
+ relation
115
+ end
116
+
117
+ def create_with(value)
118
+ relation = clone
119
+ relation.create_with_value = value
120
+ relation
121
+ end
122
+
123
+ def from(value)
124
+ relation = clone
125
+ relation.from_value = value
126
+ relation
127
+ end
128
+
129
+ def extending(*modules, &block)
130
+ modules << Module.new(&block) if block_given?
131
+
132
+ relation = clone
133
+ relation.send(:apply_modules, modules.flatten)
134
+ relation
135
+ end
136
+
137
+ def reverse_order
138
+ order_clause = arel.order_clauses.join(', ')
139
+ relation = except(:order)
140
+
141
+ order = order_clause.blank? ?
142
+ "#{@klass.table_name}.#{@klass.primary_key} DESC" :
143
+ reverse_sql_order(order_clause)
144
+
145
+ relation.order(Arel.sql(order))
146
+ end
147
+
148
+ def arel
149
+ @arel ||= build_arel
150
+ end
151
+
152
+ def custom_join_sql(*joins)
153
+ arel = table.select_manager
154
+
155
+ joins.each do |join|
156
+ next if join.blank?
157
+
158
+ @implicit_readonly = true
159
+
160
+ case join
161
+ when Array
162
+ join = Arel.sql(join.join(' ')) if array_of_strings?(join)
163
+ when String
164
+ join = Arel.sql(join)
165
+ end
166
+
167
+ arel.join(join)
168
+ end
169
+
170
+ arel.join_sql
171
+ end
172
+
173
+ def build_arel
174
+ arel = table
175
+
176
+ arel = build_joins(arel, @joins_values) unless @joins_values.empty?
177
+
178
+ arel = collapse_wheres(arel, (@where_values - ['']).uniq)
179
+
180
+ arel = arel.having(*@having_values.uniq.reject{|h| h.blank?}) unless @having_values.empty?
181
+
182
+ arel = arel.take(connection.sanitize_limit(@limit_value)) if @limit_value
183
+ arel = arel.skip(@offset_value) if @offset_value
184
+
185
+ arel = arel.group(*@group_values.uniq.reject{|g| g.blank?}) unless @group_values.empty?
186
+
187
+ arel = arel.order(*@order_values.uniq.reject{|o| o.blank?}) unless @order_values.empty?
188
+
189
+ arel = build_select(arel, @select_values.uniq)
190
+
191
+ arel = arel.from(@from_value) if @from_value
192
+ arel = arel.lock(@lock_value) if @lock_value
193
+
194
+ arel
195
+ end
196
+
197
+ private
198
+
199
+ def collapse_wheres(arel, wheres)
200
+ equalities = wheres.grep(Arel::Nodes::Equality)
201
+
202
+ groups = equalities.group_by do |equality|
203
+ equality.left
204
+ end
205
+
206
+ groups.each do |_, eqls|
207
+ test = eqls.inject(eqls.shift) do |memo, expr|
208
+ memo.and(expr)
209
+ end
210
+ arel = arel.where(test)
211
+ end
212
+
213
+ (wheres - equalities).each do |where|
214
+ where = Arel.sql(where) if String === where
215
+ arel = arel.where(Arel::Nodes::Grouping.new(where))
216
+ end
217
+ arel
218
+ end
219
+
220
+ def build_where(opts, other = [])
221
+ case opts
222
+ when String, Array
223
+ [@klass.send(:sanitize_sql, other.empty? ? opts : ([opts] + other))]
224
+ when Hash
225
+ attributes = @klass.send(:expand_hash_conditions_for_aggregates, opts)
226
+ PredicateBuilder.new(table.engine).build_from_hash(attributes, table)
227
+ else
228
+ [opts]
229
+ end
230
+ end
231
+
232
+ def build_joins(relation, joins)
233
+ association_joins = []
234
+
235
+ joins = @joins_values.map {|j| j.respond_to?(:strip) ? j.strip : j}.uniq
236
+
237
+ joins.each do |join|
238
+ association_joins << join if [Hash, Array, Symbol].include?(join.class) && !array_of_strings?(join)
239
+ end
240
+
241
+ stashed_association_joins = joins.grep(ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation)
242
+
243
+ non_association_joins = (joins - association_joins - stashed_association_joins)
244
+ custom_joins = custom_join_sql(*non_association_joins)
245
+
246
+ join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, association_joins, custom_joins)
247
+
248
+ join_dependency.graft(*stashed_association_joins)
249
+
250
+ @implicit_readonly = true unless association_joins.empty? && stashed_association_joins.empty?
251
+
252
+ to_join = []
253
+
254
+ join_dependency.join_associations.each do |association|
255
+ if (association_relation = association.relation).is_a?(Array)
256
+ to_join << [association_relation.first, association.join_type, association.association_join.first]
257
+ to_join << [association_relation.last, association.join_type, association.association_join.last]
258
+ else
259
+ to_join << [association_relation, association.join_type, association.association_join]
260
+ end
261
+ end
262
+
263
+ to_join.uniq.each do |left, join_type, right|
264
+ relation = relation.join(left, join_type).on(*right)
265
+ end
266
+
267
+ relation.join(custom_joins)
268
+ end
269
+
270
+ def build_select(arel, selects)
271
+ unless selects.empty?
272
+ @implicit_readonly = false
273
+ arel.project(*selects)
274
+ else
275
+ arel.project(Arel::SqlLiteral.new(@klass.quoted_table_name + '.*'))
276
+ end
277
+ end
278
+
279
+ def apply_modules(modules)
280
+ unless modules.empty?
281
+ @extensions += modules
282
+ modules.each {|extension| extend(extension) }
283
+ end
284
+ end
285
+
286
+ def reverse_sql_order(order_query)
287
+ order_query.to_s.split(/,/).each { |s|
288
+ if s.match(/\s(asc|ASC)$/)
289
+ s.gsub!(/\s(asc|ASC)$/, ' DESC')
290
+ elsif s.match(/\s(desc|DESC)$/)
291
+ s.gsub!(/\s(desc|DESC)$/, ' ASC')
292
+ else
293
+ s.concat(' DESC')
294
+ end
295
+ }.join(',')
296
+ end
297
+
298
+ def array_of_strings?(o)
299
+ o.is_a?(Array) && o.all?{|obj| obj.is_a?(String)}
300
+ end
301
+
302
+ end
303
+ end
@@ -0,0 +1,132 @@
1
+ require 'active_support/core_ext/object/blank'
2
+
3
+ module ActiveRecord
4
+ module SpawnMethods
5
+ def merge(r)
6
+ merged_relation = clone
7
+ return merged_relation unless r
8
+ return to_a & r if r.is_a?(Array)
9
+
10
+ Relation::ASSOCIATION_METHODS.each do |method|
11
+ value = r.send(:"#{method}_values")
12
+
13
+ unless value.empty?
14
+ if method == :includes
15
+ merged_relation = merged_relation.includes(value)
16
+ else
17
+ merged_relation.send(:"#{method}_values=", value)
18
+ end
19
+ end
20
+ end
21
+
22
+ (Relation::MULTI_VALUE_METHODS - [:joins, :where, :order]).each do |method|
23
+ value = r.send(:"#{method}_values")
24
+ merged_relation.send(:"#{method}_values=", merged_relation.send(:"#{method}_values") + value) if value.present?
25
+ end
26
+
27
+ order_value = r.order_values
28
+ if order_value.present?
29
+ if r.reorder_flag
30
+ merged_relation.order_values = order_value
31
+ else
32
+ merged_relation.order_values = merged_relation.order_values + order_value
33
+ end
34
+ end
35
+
36
+ merged_relation = merged_relation.joins(r.joins_values)
37
+
38
+ merged_wheres = @where_values + r.where_values
39
+
40
+ unless @where_values.empty?
41
+ # Remove duplicates, last one wins.
42
+ seen = Hash.new { |h,table| h[table] = {} }
43
+ merged_wheres = merged_wheres.reverse.reject { |w|
44
+ nuke = false
45
+ if w.respond_to?(:operator) && w.operator == :==
46
+ name = w.left.name
47
+ table = w.left.relation.name
48
+ nuke = seen[table][name]
49
+ seen[table][name] = true
50
+ end
51
+ nuke
52
+ }.reverse
53
+ end
54
+
55
+ merged_relation.where_values = merged_wheres
56
+
57
+ Relation::SINGLE_VALUE_METHODS.reject {|m| m == :lock}.each do |method|
58
+ value = r.send(:"#{method}_value")
59
+ merged_relation.send(:"#{method}_value=", value) unless value.nil?
60
+ end
61
+
62
+ merged_relation.lock_value = r.lock_value unless merged_relation.lock_value
63
+
64
+ # Apply scope extension modules
65
+ merged_relation.send :apply_modules, r.extensions
66
+
67
+ merged_relation
68
+ end
69
+
70
+ def &(r)
71
+ ActiveSupport::Deprecation.warn "Using & to merge relations has been deprecated and will be removed in Rails 3.1. Please use the relation's merge method, instead"
72
+ merge(r)
73
+ end
74
+
75
+ def except(*skips)
76
+ result = self.class.new(@klass, table)
77
+
78
+ ((Relation::ASSOCIATION_METHODS + Relation::MULTI_VALUE_METHODS) - skips).each do |method|
79
+ result.send(:"#{method}_values=", send(:"#{method}_values"))
80
+ end
81
+
82
+ (Relation::SINGLE_VALUE_METHODS - skips).each do |method|
83
+ result.send(:"#{method}_value=", send(:"#{method}_value"))
84
+ end
85
+
86
+ # Apply scope extension modules
87
+ result.send(:apply_modules, extensions)
88
+
89
+ result
90
+ end
91
+
92
+ def only(*onlies)
93
+ result = self.class.new(@klass, table)
94
+
95
+ ((Relation::ASSOCIATION_METHODS + Relation::MULTI_VALUE_METHODS) & onlies).each do |method|
96
+ result.send(:"#{method}_values=", send(:"#{method}_values"))
97
+ end
98
+
99
+ (Relation::SINGLE_VALUE_METHODS & onlies).each do |method|
100
+ result.send(:"#{method}_value=", send(:"#{method}_value"))
101
+ end
102
+
103
+ # Apply scope extension modules
104
+ result.send(:apply_modules, extensions)
105
+
106
+ result
107
+ end
108
+
109
+ VALID_FIND_OPTIONS = [ :conditions, :include, :joins, :limit, :offset, :extend,
110
+ :order, :select, :readonly, :group, :having, :from, :lock ]
111
+
112
+ def apply_finder_options(options)
113
+ relation = clone
114
+ return relation unless options
115
+
116
+ options.assert_valid_keys(VALID_FIND_OPTIONS)
117
+ finders = options.dup
118
+ finders.delete_if { |key, value| value.nil? }
119
+
120
+ ([:joins, :select, :group, :order, :having, :limit, :offset, :from, :lock, :readonly] & finders.keys).each do |finder|
121
+ relation = relation.send(finder, finders[finder])
122
+ end
123
+
124
+ relation = relation.where(finders[:conditions]) if options.has_key?(:conditions)
125
+ relation = relation.includes(finders[:include]) if options.has_key?(:include)
126
+ relation = relation.extending(finders[:extend]) if options.has_key?(:extend)
127
+
128
+ relation
129
+ end
130
+
131
+ end
132
+ end