activerecord 3.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (93) hide show
  1. data/CHANGELOG +6023 -0
  2. data/README.rdoc +222 -0
  3. data/examples/associations.png +0 -0
  4. data/examples/performance.rb +162 -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 +403 -0
  9. data/lib/active_record/associations.rb +2254 -0
  10. data/lib/active_record/associations/association_collection.rb +562 -0
  11. data/lib/active_record/associations/association_proxy.rb +295 -0
  12. data/lib/active_record/associations/belongs_to_association.rb +91 -0
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +78 -0
  14. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +137 -0
  15. data/lib/active_record/associations/has_many_association.rb +128 -0
  16. data/lib/active_record/associations/has_many_through_association.rb +116 -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 +33 -0
  22. data/lib/active_record/attribute_methods/dirty.rb +95 -0
  23. data/lib/active_record/attribute_methods/primary_key.rb +50 -0
  24. data/lib/active_record/attribute_methods/query.rb +39 -0
  25. data/lib/active_record/attribute_methods/read.rb +116 -0
  26. data/lib/active_record/attribute_methods/time_zone_conversion.rb +61 -0
  27. data/lib/active_record/attribute_methods/write.rb +37 -0
  28. data/lib/active_record/autosave_association.rb +369 -0
  29. data/lib/active_record/base.rb +1867 -0
  30. data/lib/active_record/callbacks.rb +288 -0
  31. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +365 -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 +329 -0
  35. data/lib/active_record/connection_adapters/abstract/query_cache.rb +81 -0
  36. data/lib/active_record/connection_adapters/abstract/quoting.rb +72 -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 +543 -0
  39. data/lib/active_record/connection_adapters/abstract_adapter.rb +212 -0
  40. data/lib/active_record/connection_adapters/mysql_adapter.rb +643 -0
  41. data/lib/active_record/connection_adapters/postgresql_adapter.rb +1030 -0
  42. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +53 -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 +53 -0
  46. data/lib/active_record/dynamic_scope_match.rb +32 -0
  47. data/lib/active_record/errors.rb +172 -0
  48. data/lib/active_record/fixtures.rb +1008 -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 +417 -0
  56. data/lib/active_record/observer.rb +140 -0
  57. data/lib/active_record/persistence.rb +291 -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 +403 -0
  63. data/lib/active_record/relation.rb +393 -0
  64. data/lib/active_record/relation/batches.rb +89 -0
  65. data/lib/active_record/relation/calculations.rb +286 -0
  66. data/lib/active_record/relation/finder_methods.rb +355 -0
  67. data/lib/active_record/relation/predicate_builder.rb +41 -0
  68. data/lib/active_record/relation/query_methods.rb +261 -0
  69. data/lib/active_record/relation/spawn_methods.rb +112 -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 +356 -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 +185 -0
  81. data/lib/active_record/version.rb +9 -0
  82. data/lib/rails/generators/active_record.rb +27 -0
  83. data/lib/rails/generators/active_record/migration/migration_generator.rb +25 -0
  84. data/lib/rails/generators/active_record/migration/templates/migration.rb +17 -0
  85. data/lib/rails/generators/active_record/model/model_generator.rb +38 -0
  86. data/lib/rails/generators/active_record/model/templates/migration.rb +16 -0
  87. data/lib/rails/generators/active_record/model/templates/model.rb +5 -0
  88. data/lib/rails/generators/active_record/model/templates/module.rb +5 -0
  89. data/lib/rails/generators/active_record/observer/observer_generator.rb +15 -0
  90. data/lib/rails/generators/active_record/observer/templates/observer.rb +2 -0
  91. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +24 -0
  92. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +16 -0
  93. metadata +224 -0
@@ -0,0 +1,41 @@
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
28
+ attribute.in(values)
29
+ when Range, Arel::Relation
30
+ attribute.in(value)
31
+ else
32
+ attribute.eq(value)
33
+ end
34
+ end
35
+ end
36
+
37
+ predicates.flatten
38
+ end
39
+
40
+ end
41
+ end
@@ -0,0 +1,261 @@
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, :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
+ clone.tap {|r| r.includes_values = (r.includes_values + args).flatten.uniq if args.present? }
15
+ end
16
+
17
+ def eager_load(*args)
18
+ clone.tap {|r| r.eager_load_values += args if args.present? }
19
+ end
20
+
21
+ def preload(*args)
22
+ clone.tap {|r| r.preload_values += args if args.present? }
23
+ end
24
+
25
+ def select(*args)
26
+ if block_given?
27
+ to_a.select {|*block_args| yield(*block_args) }
28
+ else
29
+ clone.tap {|r| r.select_values += args if args.present? }
30
+ end
31
+ end
32
+
33
+ def group(*args)
34
+ clone.tap {|r| r.group_values += args.flatten if args.present? }
35
+ end
36
+
37
+ def order(*args)
38
+ clone.tap {|r| r.order_values += args if args.present? }
39
+ end
40
+
41
+ def reorder(*args)
42
+ clone.tap {|r| r.order_values = args if args.present? }
43
+ end
44
+
45
+ def joins(*args)
46
+ args.flatten!
47
+ clone.tap {|r| r.joins_values += args if args.present? }
48
+ end
49
+
50
+ def where(opts, *rest)
51
+ value = build_where(opts, rest)
52
+ copy = clone
53
+ copy.where_values += Array.wrap(value) if value
54
+ copy
55
+ end
56
+
57
+ def having(*args)
58
+ value = build_where(*args)
59
+ clone.tap {|r| r.having_values += Array.wrap(value) if value.present? }
60
+ end
61
+
62
+ def limit(value = true)
63
+ copy = clone
64
+ copy.limit_value = value
65
+ copy
66
+ end
67
+
68
+ def offset(value = true)
69
+ clone.tap {|r| r.offset_value = value }
70
+ end
71
+
72
+ def lock(locks = true)
73
+ case locks
74
+ when String, TrueClass, NilClass
75
+ clone.tap {|r| r.lock_value = locks || true }
76
+ else
77
+ clone.tap {|r| r.lock_value = false }
78
+ end
79
+ end
80
+
81
+ def readonly(value = true)
82
+ clone.tap {|r| r.readonly_value = value }
83
+ end
84
+
85
+ def create_with(value = true)
86
+ clone.tap {|r| r.create_with_value = value }
87
+ end
88
+
89
+ def from(value = true)
90
+ clone.tap {|r| r.from_value = value }
91
+ end
92
+
93
+ def extending(*modules, &block)
94
+ modules << Module.new(&block) if block_given?
95
+ clone.tap {|r| r.send(:apply_modules, *modules) }
96
+ end
97
+
98
+ def reverse_order
99
+ order_clause = arel.order_clauses.join(', ')
100
+ relation = except(:order)
101
+
102
+ order = order_clause.blank? ?
103
+ "#{@klass.table_name}.#{@klass.primary_key} DESC" :
104
+ reverse_sql_order(order_clause)
105
+
106
+ relation.order Arel::SqlLiteral.new order
107
+ end
108
+
109
+ def arel
110
+ @arel ||= build_arel
111
+ end
112
+
113
+ def custom_join_sql(*joins)
114
+ arel = table
115
+ joins.each do |join|
116
+ next if join.blank?
117
+
118
+ @implicit_readonly = true
119
+
120
+ case join
121
+ when Hash, Array, Symbol
122
+ if array_of_strings?(join)
123
+ join_string = join.join(' ')
124
+ arel = arel.join(Arel::SqlLiteral.new(join_string))
125
+ end
126
+ when String
127
+ arel = arel.join(Arel::SqlLiteral.new(join))
128
+ else
129
+ arel = arel.join(join)
130
+ end
131
+ end
132
+ arel.joins(arel)
133
+ end
134
+
135
+ def build_arel
136
+ arel = table
137
+
138
+ arel = build_joins(arel, @joins_values) unless @joins_values.empty?
139
+
140
+ (@where_values - ['']).uniq.each do |where|
141
+ case where
142
+ when Arel::SqlLiteral
143
+ arel = arel.where(where)
144
+ else
145
+ sql = where.is_a?(String) ? where : where.to_sql
146
+ arel = arel.where(Arel::SqlLiteral.new("(#{sql})"))
147
+ end
148
+ end
149
+
150
+ arel = arel.having(*@having_values.uniq.select{|h| h.present?}) unless @having_values.empty?
151
+
152
+ arel = arel.take(@limit_value) if @limit_value
153
+ arel = arel.skip(@offset_value) if @offset_value
154
+
155
+ arel = arel.group(*@group_values.uniq.select{|g| g.present?}) unless @group_values.empty?
156
+
157
+ arel = arel.order(*@order_values.uniq.select{|o| o.present?}) unless @order_values.empty?
158
+
159
+ arel = build_select(arel, @select_values.uniq)
160
+
161
+ arel = arel.from(@from_value) if @from_value
162
+ arel = arel.lock(@lock_value) if @lock_value
163
+
164
+ arel
165
+ end
166
+
167
+ def build_where(opts, other = [])
168
+ case opts
169
+ when String, Array
170
+ @klass.send(:sanitize_sql, other.empty? ? opts : ([opts] + other))
171
+ when Hash
172
+ attributes = @klass.send(:expand_hash_conditions_for_aggregates, opts)
173
+ PredicateBuilder.new(table.engine).build_from_hash(attributes, table)
174
+ else
175
+ opts
176
+ end
177
+ end
178
+
179
+ private
180
+
181
+ def build_joins(relation, joins)
182
+ joined_associations = []
183
+ association_joins = []
184
+
185
+ joins = @joins_values.map {|j| j.respond_to?(:strip) ? j.strip : j}.uniq
186
+
187
+ joins.each do |join|
188
+ association_joins << join if [Hash, Array, Symbol].include?(join.class) && !array_of_strings?(join)
189
+ end
190
+
191
+ stashed_association_joins = joins.grep(ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation)
192
+
193
+ non_association_joins = (joins - association_joins - stashed_association_joins)
194
+ custom_joins = custom_join_sql(*non_association_joins)
195
+
196
+ join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, association_joins, custom_joins)
197
+
198
+ join_dependency.graft(*stashed_association_joins)
199
+
200
+ @implicit_readonly = true unless association_joins.empty? && stashed_association_joins.empty?
201
+
202
+ to_join = []
203
+
204
+ join_dependency.join_associations.each do |association|
205
+ if (association_relation = association.relation).is_a?(Array)
206
+ to_join << [association_relation.first, association.join_class, association.association_join.first]
207
+ to_join << [association_relation.last, association.join_class, association.association_join.last]
208
+ else
209
+ to_join << [association_relation, association.join_class, association.association_join]
210
+ end
211
+ end
212
+
213
+ to_join.each do |tj|
214
+ unless joined_associations.detect {|ja| ja[0] == tj[0] && ja[1] == tj[1] && ja[2] == tj[2] }
215
+ joined_associations << tj
216
+ relation = relation.join(tj[0], tj[1]).on(*tj[2])
217
+ end
218
+ end
219
+
220
+ relation.join(custom_joins)
221
+ end
222
+
223
+ def build_select(arel, selects)
224
+ unless selects.empty?
225
+ @implicit_readonly = false
226
+ # TODO: fix this ugly hack, we should refactor the callers to get an ARel compatible array.
227
+ # Before this change we were passing to ARel the last element only, and ARel is capable of handling an array
228
+ if selects.all? {|s| s.is_a?(String) || !s.is_a?(Arel::Expression) } && !(selects.last =~ /^COUNT\(/)
229
+ arel.project(*selects)
230
+ else
231
+ arel.project(selects.last)
232
+ end
233
+ else
234
+ arel.project(Arel::SqlLiteral.new(@klass.quoted_table_name + '.*'))
235
+ end
236
+ end
237
+
238
+ def apply_modules(modules)
239
+ values = Array.wrap(modules)
240
+ @extensions += values if values.present?
241
+ values.each {|extension| extend(extension) }
242
+ end
243
+
244
+ def reverse_sql_order(order_query)
245
+ order_query.to_s.split(/,/).each { |s|
246
+ if s.match(/\s(asc|ASC)$/)
247
+ s.gsub!(/\s(asc|ASC)$/, ' DESC')
248
+ elsif s.match(/\s(desc|DESC)$/)
249
+ s.gsub!(/\s(desc|DESC)$/, ' ASC')
250
+ else
251
+ s.concat(' DESC')
252
+ end
253
+ }.join(',')
254
+ end
255
+
256
+ def array_of_strings?(o)
257
+ o.is_a?(Array) && o.all?{|obj| obj.is_a?(String)}
258
+ end
259
+
260
+ end
261
+ end
@@ -0,0 +1,112 @@
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
+
9
+ ((Relation::ASSOCIATION_METHODS + Relation::MULTI_VALUE_METHODS) - [:joins, :where]).each do |method|
10
+ value = r.send(:"#{method}_values")
11
+ unless value.empty?
12
+ if method == :includes
13
+ merged_relation = merged_relation.includes(value)
14
+ else
15
+ merged_relation.send(:"#{method}_values=", value)
16
+ end
17
+ end
18
+ end
19
+
20
+ merged_relation = merged_relation.joins(r.joins_values)
21
+
22
+ merged_wheres = @where_values
23
+
24
+ r.where_values.each do |w|
25
+ if w.respond_to?(:operator) && w.operator == :==
26
+ merged_wheres = merged_wheres.reject { |p|
27
+ p.respond_to?(:operator) && p.operator == :== && p.operand1.name == w.operand1.name
28
+ }
29
+ end
30
+
31
+ merged_wheres += [w]
32
+ end
33
+
34
+ merged_relation.where_values = merged_wheres
35
+
36
+ Relation::SINGLE_VALUE_METHODS.reject {|m| m == :lock}.each do |method|
37
+ unless (value = r.send(:"#{method}_value")).nil?
38
+ merged_relation.send(:"#{method}_value=", value)
39
+ end
40
+ end
41
+
42
+ merged_relation.lock_value = r.lock_value unless merged_relation.lock_value
43
+
44
+ # Apply scope extension modules
45
+ merged_relation.send :apply_modules, r.extensions
46
+
47
+ merged_relation
48
+ end
49
+
50
+ alias :& :merge
51
+
52
+ def except(*skips)
53
+ result = self.class.new(@klass, table)
54
+
55
+ (Relation::ASSOCIATION_METHODS + Relation::MULTI_VALUE_METHODS).each do |method|
56
+ result.send(:"#{method}_values=", send(:"#{method}_values")) unless skips.include?(method)
57
+ end
58
+
59
+ Relation::SINGLE_VALUE_METHODS.each do |method|
60
+ result.send(:"#{method}_value=", send(:"#{method}_value")) unless skips.include?(method)
61
+ end
62
+
63
+ result
64
+ end
65
+
66
+ def only(*onlies)
67
+ result = self.class.new(@klass, table)
68
+
69
+ onlies.each do |only|
70
+ if (Relation::ASSOCIATION_METHODS + Relation::MULTI_VALUE_METHODS).include?(only)
71
+ result.send(:"#{only}_values=", send(:"#{only}_values"))
72
+ elsif Relation::SINGLE_VALUE_METHODS.include?(only)
73
+ result.send(:"#{only}_value=", send(:"#{only}_value"))
74
+ else
75
+ raise "Invalid argument : #{only}"
76
+ end
77
+ end
78
+
79
+ result
80
+ end
81
+
82
+ VALID_FIND_OPTIONS = [ :conditions, :include, :joins, :limit, :offset, :extend,
83
+ :order, :select, :readonly, :group, :having, :from, :lock ]
84
+
85
+ def apply_finder_options(options)
86
+ relation = clone
87
+ return relation unless options
88
+
89
+ options.assert_valid_keys(VALID_FIND_OPTIONS)
90
+
91
+ [:joins, :select, :group, :having, :limit, :offset, :from, :lock].each do |finder|
92
+ if value = options[finder]
93
+ relation = relation.send(finder, value)
94
+ end
95
+ end
96
+
97
+ relation = relation.readonly(options[:readonly]) if options.key? :readonly
98
+
99
+ # Give precedence to newly-applied orders and groups to play nicely with with_scope
100
+ [:group, :order].each do |finder|
101
+ relation.send("#{finder}_values=", Array.wrap(options[finder]) + relation.send("#{finder}_values")) if options.has_key?(finder)
102
+ end
103
+
104
+ relation = relation.where(options[:conditions]) if options.has_key?(:conditions)
105
+ relation = relation.includes(options[:include]) if options.has_key?(:include)
106
+ relation = relation.extending(options[:extend]) if options.has_key?(:extend)
107
+
108
+ relation
109
+ end
110
+
111
+ end
112
+ end
@@ -0,0 +1,59 @@
1
+ require 'active_support/core_ext/object/blank'
2
+
3
+ module ActiveRecord
4
+ # = Active Record Schema
5
+ #
6
+ # Allows programmers to programmatically define a schema in a portable
7
+ # DSL. This means you can define tables, indexes, etc. without using SQL
8
+ # directly, so your applications can more easily support multiple
9
+ # databases.
10
+ #
11
+ # Usage:
12
+ #
13
+ # ActiveRecord::Schema.define do
14
+ # create_table :authors do |t|
15
+ # t.string :name, :null => false
16
+ # end
17
+ #
18
+ # add_index :authors, :name, :unique
19
+ #
20
+ # create_table :posts do |t|
21
+ # t.integer :author_id, :null => false
22
+ # t.string :subject
23
+ # t.text :body
24
+ # t.boolean :private, :default => false
25
+ # end
26
+ #
27
+ # add_index :posts, :author_id
28
+ # end
29
+ #
30
+ # ActiveRecord::Schema is only supported by database adapters that also
31
+ # support migrations, the two features being very similar.
32
+ class Schema < Migration
33
+ private_class_method :new
34
+
35
+ def self.migrations_path
36
+ ActiveRecord::Migrator.migrations_path
37
+ end
38
+
39
+ # Eval the given block. All methods available to the current connection
40
+ # adapter are available within the block, so you can easily use the
41
+ # database definition DSL to build up your schema (+create_table+,
42
+ # +add_index+, etc.).
43
+ #
44
+ # The +info+ hash is optional, and if given is used to define metadata
45
+ # about the current schema (currently, only the schema's version):
46
+ #
47
+ # ActiveRecord::Schema.define(:version => 20380119000001) do
48
+ # ...
49
+ # end
50
+ def self.define(info={}, &block)
51
+ instance_eval(&block)
52
+
53
+ unless info[:version].blank?
54
+ initialize_schema_migrations_table
55
+ assume_migrated_upto_version(info[:version], migrations_path)
56
+ end
57
+ end
58
+ end
59
+ end