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,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