activerecord 3.0.0.beta → 3.0.0.beta2

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 (53) hide show
  1. data/CHANGELOG +8 -1
  2. data/lib/active_record.rb +9 -6
  3. data/lib/active_record/aggregations.rb +5 -0
  4. data/lib/active_record/association_preload.rb +7 -2
  5. data/lib/active_record/associations.rb +74 -54
  6. data/lib/active_record/associations/association_collection.rb +1 -0
  7. data/lib/active_record/associations/association_proxy.rb +2 -1
  8. data/lib/active_record/associations/has_many_association.rb +4 -0
  9. data/lib/active_record/associations/has_many_through_association.rb +1 -0
  10. data/lib/active_record/attribute_methods/dirty.rb +11 -9
  11. data/lib/active_record/attribute_methods/primary_key.rb +6 -0
  12. data/lib/active_record/attribute_methods/query.rb +2 -0
  13. data/lib/active_record/base.rb +57 -212
  14. data/lib/active_record/callbacks.rb +10 -0
  15. data/lib/active_record/connection_adapters/abstract/database_statements.rb +24 -1
  16. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -0
  17. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +10 -5
  18. data/lib/active_record/connection_adapters/abstract_adapter.rb +1 -0
  19. data/lib/active_record/connection_adapters/mysql_adapter.rb +22 -5
  20. data/lib/active_record/connection_adapters/postgresql_adapter.rb +34 -8
  21. data/lib/active_record/dynamic_finder_match.rb +3 -0
  22. data/lib/active_record/errors.rb +165 -0
  23. data/lib/active_record/fixtures.rb +1 -0
  24. data/lib/active_record/migration.rb +8 -6
  25. data/lib/active_record/named_scope.rb +14 -5
  26. data/lib/active_record/nested_attributes.rb +6 -2
  27. data/lib/active_record/query_cache.rb +2 -0
  28. data/lib/active_record/railtie.rb +30 -19
  29. data/lib/active_record/railties/databases.rake +13 -7
  30. data/lib/active_record/railties/{subscriber.rb → log_subscriber.rb} +7 -2
  31. data/lib/active_record/reflection.rb +5 -3
  32. data/lib/active_record/relation.rb +13 -2
  33. data/lib/active_record/relation/batches.rb +84 -0
  34. data/lib/active_record/relation/calculations.rb +2 -0
  35. data/lib/active_record/relation/finder_methods.rb +13 -2
  36. data/lib/active_record/relation/predicate_builder.rb +2 -7
  37. data/lib/active_record/relation/query_methods.rb +20 -27
  38. data/lib/active_record/relation/spawn_methods.rb +18 -28
  39. data/lib/active_record/schema.rb +2 -0
  40. data/lib/active_record/validations/uniqueness.rb +2 -4
  41. data/lib/active_record/version.rb +3 -2
  42. data/lib/{generators → rails/generators}/active_record.rb +0 -0
  43. data/lib/{generators → rails/generators}/active_record/migration/migration_generator.rb +1 -1
  44. data/lib/{generators → rails/generators}/active_record/migration/templates/migration.rb +0 -0
  45. data/lib/{generators → rails/generators}/active_record/model/model_generator.rb +1 -1
  46. data/lib/{generators → rails/generators}/active_record/model/templates/migration.rb +0 -0
  47. data/lib/{generators → rails/generators}/active_record/model/templates/model.rb +0 -0
  48. data/lib/{generators → rails/generators}/active_record/observer/observer_generator.rb +1 -1
  49. data/lib/{generators → rails/generators}/active_record/observer/templates/observer.rb +0 -0
  50. data/lib/{generators → rails/generators}/active_record/session_migration/session_migration_generator.rb +1 -1
  51. data/lib/{generators → rails/generators}/active_record/session_migration/templates/migration.rb +0 -0
  52. metadata +61 -34
  53. data/lib/active_record/batches.rb +0 -79
@@ -205,6 +205,16 @@ module ActiveRecord
205
205
  # including <tt>after_*</tt> hooks. Note, however, that in that case the client
206
206
  # needs to be aware of it because an ordinary +save+ will raise such exception
207
207
  # instead of quietly returning +false+.
208
+ #
209
+ # == Debugging callbacks
210
+ #
211
+ # To list the methods and procs registered with a particular callback, append <tt>_callback_chain</tt> to the callback name that you wish to list and send that to your class from the Rails console:
212
+ #
213
+ # >> Topic.after_save_callback_chain
214
+ # => [#<ActiveSupport::Callbacks::Callback:0x3f6a448
215
+ # @method=#<Proc:0x03f9a42c@/Users/foo/bar/app/models/topic.rb:43>, kind:after_save, identifiernil,
216
+ # options{}]
217
+ #
208
218
  module Callbacks
209
219
  extend ActiveSupport::Concern
210
220
 
@@ -113,7 +113,7 @@ module ActiveRecord
113
113
  def transaction(options = {})
114
114
  options.assert_valid_keys :requires_new, :joinable
115
115
 
116
- last_transaction_joinable = @transaction_joinable
116
+ last_transaction_joinable = defined?(@transaction_joinable) ? @transaction_joinable : nil
117
117
  if options.has_key?(:joinable)
118
118
  @transaction_joinable = options[:joinable]
119
119
  else
@@ -181,6 +181,29 @@ module ActiveRecord
181
181
  # done if the transaction block raises an exception or returns false.
182
182
  def rollback_db_transaction() end
183
183
 
184
+ # Appends +LIMIT+ and +OFFSET+ options to an SQL statement, or some SQL
185
+ # fragment that has the same semantics as LIMIT and OFFSET.
186
+ #
187
+ # +options+ must be a Hash which contains a +:limit+ option
188
+ # and an +:offset+ option.
189
+ #
190
+ # This method *modifies* the +sql+ parameter.
191
+ #
192
+ # ===== Examples
193
+ # add_limit_offset!('SELECT * FROM suppliers', {:limit => 10, :offset => 50})
194
+ # generates
195
+ # SELECT * FROM suppliers LIMIT 10 OFFSET 50
196
+
197
+ def add_limit_offset!(sql, options)
198
+ if limit = options[:limit]
199
+ sql << " LIMIT #{sanitize_limit(limit)}"
200
+ end
201
+ if offset = options[:offset]
202
+ sql << " OFFSET #{offset.to_i}"
203
+ end
204
+ sql
205
+ end
206
+
184
207
  def default_sequence_name(table, column)
185
208
  nil
186
209
  end
@@ -1,3 +1,5 @@
1
+ require 'active_support/core_ext/object/duplicable'
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters # :nodoc:
3
5
  module QueryCache
@@ -1,3 +1,4 @@
1
+ require 'active_support/core_ext/object/blank'
1
2
  require 'date'
2
3
  require 'set'
3
4
  require 'bigdecimal'
@@ -22,7 +23,8 @@ module ActiveRecord
22
23
  #
23
24
  # +name+ is the column's name, such as <tt>supplier_id</tt> in <tt>supplier_id int(11)</tt>.
24
25
  # +default+ is the type-casted default value, such as +new+ in <tt>sales_stage varchar(20) default 'new'</tt>.
25
- # +sql_type+ is only used to extract the column's length, if necessary. For example +60+ in <tt>company_name varchar(60)</tt>.
26
+ # +sql_type+ is used to extract the column's length, if necessary. For example +60+ in <tt>company_name varchar(60)</tt>.
27
+ # It will be mapped to one of the standard Rails SQL types in the <tt>type</tt> attribute.
26
28
  # +null+ determines if this column allows +NULL+ values.
27
29
  def initialize(name, default, sql_type = nil, null = true)
28
30
  @name, @sql_type, @null = name, sql_type, null
@@ -319,16 +321,19 @@ module ActiveRecord
319
321
  def method_missing(symbol, *args)
320
322
  if symbol.to_s == 'xml'
321
323
  xml_column_fallback(args)
324
+ else
325
+ super
322
326
  end
323
327
  end
324
328
 
325
329
  def xml_column_fallback(*args)
326
330
  case @base.adapter_name.downcase
327
- when 'sqlite', 'mysql'
328
- options = args.extract_options!
329
- column(args[0], :text, options)
330
- end
331
+ when 'sqlite', 'mysql'
332
+ options = args.extract_options!
333
+ column(args[0], :text, options)
331
334
  end
335
+ end
336
+
332
337
  # Appends a primary key definition to the table definition.
333
338
  # Can be called multiple times, but this is probably not a good idea.
334
339
  def primary_key(name)
@@ -37,6 +37,7 @@ module ActiveRecord
37
37
  @@row_even = true
38
38
 
39
39
  def initialize(connection, logger = nil) #:nodoc:
40
+ @active = nil
40
41
  @connection, @logger = connection, logger
41
42
  @runtime = 0
42
43
  @query_cache_enabled = false
@@ -1,5 +1,6 @@
1
1
  require 'active_record/connection_adapters/abstract_adapter'
2
2
  require 'active_support/core_ext/kernel/requires'
3
+ require 'active_support/core_ext/object/blank'
3
4
  require 'set'
4
5
 
5
6
  module MysqlCompat #:nodoc:
@@ -61,7 +62,7 @@ module ActiveRecord
61
62
  begin
62
63
  require_library_or_gem('mysql')
63
64
  rescue LoadError
64
- $stderr.puts '!!! The bundled mysql.rb driver has been removed from Rails 2.2. Please install the mysql gem and try again: gem install mysql.'
65
+ $stderr.puts '!!! Please install the mysql gem and try again: gem install mysql.'
65
66
  raise
66
67
  end
67
68
  end
@@ -321,7 +322,11 @@ module ActiveRecord
321
322
 
322
323
  # Executes a SQL query and returns a MySQL::Result object. Note that you have to free the Result object after you're done using it.
323
324
  def execute(sql, name = nil) #:nodoc:
324
- log(sql, name) { @connection.query(sql) }
325
+ if name == :skip_logging
326
+ @connection.query(sql)
327
+ else
328
+ log(sql, name) { @connection.query(sql) }
329
+ end
325
330
  rescue ActiveRecord::StatementInvalid => exception
326
331
  if exception.message.split(":").first =~ /Packets out of order/
327
332
  raise ActiveRecord::StatementInvalid, "'Packets out of order' error was received from the database. Please update your mysql bindings (gem install mysql) and read http://dev.mysql.com/doc/mysql/en/password-hashing.html for more information. If you're on Windows, use the Instant Rails installer to get the updated mysql bindings."
@@ -371,6 +376,18 @@ module ActiveRecord
371
376
  execute("RELEASE SAVEPOINT #{current_savepoint_name}")
372
377
  end
373
378
 
379
+ def add_limit_offset!(sql, options) #:nodoc:
380
+ limit, offset = options[:limit], options[:offset]
381
+ if limit && offset
382
+ sql << " LIMIT #{offset.to_i}, #{sanitize_limit(limit)}"
383
+ elsif limit
384
+ sql << " LIMIT #{sanitize_limit(limit)}"
385
+ elsif offset
386
+ sql << " OFFSET #{offset.to_i}"
387
+ end
388
+ sql
389
+ end
390
+
374
391
  # SCHEMA STATEMENTS ========================================
375
392
 
376
393
  def structure_dump #:nodoc:
@@ -456,7 +473,7 @@ module ActiveRecord
456
473
  def columns(table_name, name = nil)#:nodoc:
457
474
  sql = "SHOW FIELDS FROM #{quote_table_name(table_name)}"
458
475
  columns = []
459
- result = execute(sql, name)
476
+ result = execute(sql, :skip_logging)
460
477
  result.each { |field| columns << MysqlColumn.new(field[0], field[4], field[1], field[2] == "YES") }
461
478
  result.free
462
479
  columns
@@ -616,11 +633,11 @@ module ActiveRecord
616
633
 
617
634
  def configure_connection
618
635
  encoding = @config[:encoding]
619
- execute("SET NAMES '#{encoding}'") if encoding
636
+ execute("SET NAMES '#{encoding}'", :skip_logging) if encoding
620
637
 
621
638
  # By default, MySQL 'where id is null' selects the last inserted id.
622
639
  # Turn this off. http://dev.rubyonrails.org/ticket/6778
623
- execute("SET SQL_AUTO_IS_NULL=0")
640
+ execute("SET SQL_AUTO_IS_NULL=0", :skip_logging)
624
641
  end
625
642
 
626
643
  def select(sql, name = nil)
@@ -1,5 +1,6 @@
1
1
  require 'active_record/connection_adapters/abstract_adapter'
2
2
  require 'active_support/core_ext/kernel/requires'
3
+ require 'active_support/core_ext/object/blank'
3
4
 
4
5
  begin
5
6
  require_library_or_gem 'pg'
@@ -113,6 +114,12 @@ module ActiveRecord
113
114
  # Object identifier types
114
115
  when /^oid$/
115
116
  :integer
117
+ # UUID type
118
+ when /^uuid$/
119
+ :string
120
+ # Small and big integer types
121
+ when /^(?:small|big)int$/
122
+ :integer
116
123
  # Pass through all types that are not specific to PostgreSQL.
117
124
  else
118
125
  super
@@ -299,7 +306,7 @@ module ActiveRecord
299
306
  # QUOTING ==================================================
300
307
 
301
308
  # Escapes binary strings for bytea input to the database.
302
- def escape_bytea(value)
309
+ def escape_bytea(original_value)
303
310
  if @connection.respond_to?(:escape_bytea)
304
311
  self.class.instance_eval do
305
312
  define_method(:escape_bytea) do |value|
@@ -323,13 +330,13 @@ module ActiveRecord
323
330
  end
324
331
  end
325
332
  end
326
- escape_bytea(value)
333
+ escape_bytea(original_value)
327
334
  end
328
335
 
329
336
  # Unescapes bytea output from a database to the binary string it represents.
330
337
  # NOTE: This is NOT an inverse of escape_bytea! This is only to be used
331
338
  # on escaped binary output from database drive.
332
- def unescape_bytea(value)
339
+ def unescape_bytea(original_value)
333
340
  # In each case, check if the value actually is escaped PostgreSQL bytea output
334
341
  # or an unescaped Active Record attribute that was just written.
335
342
  if PGconn.respond_to?(:unescape_bytea)
@@ -369,7 +376,7 @@ module ActiveRecord
369
376
  end
370
377
  end
371
378
  end
372
- unescape_bytea(value)
379
+ unescape_bytea(original_value)
373
380
  end
374
381
 
375
382
  # Quotes PostgreSQL-specific data types for SQL input.
@@ -394,7 +401,7 @@ module ActiveRecord
394
401
  end
395
402
 
396
403
  # Quotes strings for use in SQL input in the postgres driver for better performance.
397
- def quote_string(s) #:nodoc:
404
+ def quote_string(original_value) #:nodoc:
398
405
  if @connection.respond_to?(:escape)
399
406
  self.class.instance_eval do
400
407
  define_method(:quote_string) do |s|
@@ -414,7 +421,7 @@ module ActiveRecord
414
421
  remove_method(:quote_string)
415
422
  end
416
423
  end
417
- quote_string(s)
424
+ quote_string(original_value)
418
425
  end
419
426
 
420
427
  # Checks the following cases:
@@ -651,14 +658,33 @@ module ActiveRecord
651
658
  end
652
659
  end
653
660
 
661
+ # Creates a schema for the given user
662
+ #
663
+ # Example:
664
+ # create_schema('products', 'postgres')
665
+ def create_schema(schema_name, pg_username)
666
+ execute("CREATE SCHEMA \"#{schema_name}\" AUTHORIZATION \"#{pg_username}\"")
667
+ end
668
+
669
+ # Drops a schema
670
+ #
671
+ # Example:
672
+ # drop_schema('products')
673
+ def drop_schema(schema_name)
674
+ execute("DROP SCHEMA \"#{schema_name}\"")
675
+ end
676
+
677
+ # Returns an array of all schemas in the database
678
+ def all_schemas
679
+ query('SELECT schema_name FROM information_schema.schemata').flatten
680
+ end
654
681
 
655
682
  # Returns the list of all tables in the schema search path or a specified schema.
656
683
  def tables(name = nil)
657
- schemas = schema_search_path.split(/,/).map { |p| quote(p) }.join(',')
658
684
  query(<<-SQL, name).map { |row| row[0] }
659
685
  SELECT tablename
660
686
  FROM pg_tables
661
- WHERE schemaname IN (#{schemas})
687
+ WHERE schemaname = ANY (current_schemas(false))
662
688
  SQL
663
689
  end
664
690
 
@@ -7,6 +7,9 @@ module ActiveRecord
7
7
 
8
8
  def initialize(method)
9
9
  @finder = :first
10
+ @bang = false
11
+ @instantiator = nil
12
+
10
13
  case method.to_s
11
14
  when /^find_(all_by|last_by|by)_([_a-zA-Z]\w*)$/
12
15
  @finder = :last if $1 == 'last_by'
@@ -0,0 +1,165 @@
1
+ module ActiveRecord
2
+ # Generic Active Record exception class.
3
+ class ActiveRecordError < StandardError
4
+ end
5
+
6
+ # Raised when the single-table inheritance mechanism fails to locate the subclass
7
+ # (for example due to improper usage of column that +inheritance_column+ points to).
8
+ class SubclassNotFound < ActiveRecordError #:nodoc:
9
+ end
10
+
11
+ # Raised when an object assigned to an association has an incorrect type.
12
+ #
13
+ # class Ticket < ActiveRecord::Base
14
+ # has_many :patches
15
+ # end
16
+ #
17
+ # class Patch < ActiveRecord::Base
18
+ # belongs_to :ticket
19
+ # end
20
+ #
21
+ # # Comments are not patches, this assignment raises AssociationTypeMismatch.
22
+ # @ticket.patches << Comment.new(:content => "Please attach tests to your patch.")
23
+ class AssociationTypeMismatch < ActiveRecordError
24
+ end
25
+
26
+ # Raised when unserialized object's type mismatches one specified for serializable field.
27
+ class SerializationTypeMismatch < ActiveRecordError
28
+ end
29
+
30
+ # Raised when adapter not specified on connection (or configuration file <tt>config/database.yml</tt> misses adapter field).
31
+ class AdapterNotSpecified < ActiveRecordError
32
+ end
33
+
34
+ # Raised when Active Record cannot find database adapter specified in <tt>config/database.yml</tt> or programmatically.
35
+ class AdapterNotFound < ActiveRecordError
36
+ end
37
+
38
+ # Raised when connection to the database could not been established (for example when <tt>connection=</tt> is given a nil object).
39
+ class ConnectionNotEstablished < ActiveRecordError
40
+ end
41
+
42
+ # Raised when Active Record cannot find record by given id or set of ids.
43
+ class RecordNotFound < ActiveRecordError
44
+ end
45
+
46
+ # Raised by ActiveRecord::Base.save! and ActiveRecord::Base.create! methods when record cannot be
47
+ # saved because record is invalid.
48
+ class RecordNotSaved < ActiveRecordError
49
+ end
50
+
51
+ # Raised when SQL statement cannot be executed by the database (for example, it's often the case for MySQL when Ruby driver used is too old).
52
+ class StatementInvalid < ActiveRecordError
53
+ end
54
+
55
+ # Raised when SQL statement is invalid and the application gets a blank result.
56
+ class ThrowResult < ActiveRecordError
57
+ end
58
+
59
+ # Parent class for all specific exceptions which wrap database driver exceptions
60
+ # provides access to the original exception also.
61
+ class WrappedDatabaseException < StatementInvalid
62
+ attr_reader :original_exception
63
+
64
+ def initialize(message, original_exception)
65
+ super(message)
66
+ @original_exception = original_exception
67
+ end
68
+ end
69
+
70
+ # Raised when a record cannot be inserted because it would violate a uniqueness constraint.
71
+ class RecordNotUnique < WrappedDatabaseException
72
+ end
73
+
74
+ # Raised when a record cannot be inserted or updated because it references a non-existent record.
75
+ class InvalidForeignKey < WrappedDatabaseException
76
+ end
77
+
78
+ # Raised when number of bind variables in statement given to <tt>:condition</tt> key (for example, when using +find+ method)
79
+ # does not match number of expected variables.
80
+ #
81
+ # For example, in
82
+ #
83
+ # Location.find :all, :conditions => ["lat = ? AND lng = ?", 53.7362]
84
+ #
85
+ # two placeholders are given but only one variable to fill them.
86
+ class PreparedStatementInvalid < ActiveRecordError
87
+ end
88
+
89
+ # Raised on attempt to save stale record. Record is stale when it's being saved in another query after
90
+ # instantiation, for example, when two users edit the same wiki page and one starts editing and saves
91
+ # the page before the other.
92
+ #
93
+ # Read more about optimistic locking in ActiveRecord::Locking module RDoc.
94
+ class StaleObjectError < ActiveRecordError
95
+ end
96
+
97
+ # Raised when association is being configured improperly or
98
+ # user tries to use offset and limit together with has_many or has_and_belongs_to_many associations.
99
+ class ConfigurationError < ActiveRecordError
100
+ end
101
+
102
+ # Raised on attempt to update record that is instantiated as read only.
103
+ class ReadOnlyRecord < ActiveRecordError
104
+ end
105
+
106
+ # ActiveRecord::Transactions::ClassMethods.transaction uses this exception
107
+ # to distinguish a deliberate rollback from other exceptional situations.
108
+ # Normally, raising an exception will cause the +transaction+ method to rollback
109
+ # the database transaction *and* pass on the exception. But if you raise an
110
+ # ActiveRecord::Rollback exception, then the database transaction will be rolled back,
111
+ # without passing on the exception.
112
+ #
113
+ # For example, you could do this in your controller to rollback a transaction:
114
+ #
115
+ # class BooksController < ActionController::Base
116
+ # def create
117
+ # Book.transaction do
118
+ # book = Book.new(params[:book])
119
+ # book.save!
120
+ # if today_is_friday?
121
+ # # The system must fail on Friday so that our support department
122
+ # # won't be out of job. We silently rollback this transaction
123
+ # # without telling the user.
124
+ # raise ActiveRecord::Rollback, "Call tech support!"
125
+ # end
126
+ # end
127
+ # # ActiveRecord::Rollback is the only exception that won't be passed on
128
+ # # by ActiveRecord::Base.transaction, so this line will still be reached
129
+ # # even on Friday.
130
+ # redirect_to root_url
131
+ # end
132
+ # end
133
+ class Rollback < ActiveRecordError
134
+ end
135
+
136
+ # Raised when attribute has a name reserved by Active Record (when attribute has name of one of Active Record instance methods).
137
+ class DangerousAttributeError < ActiveRecordError
138
+ end
139
+
140
+ # Raised when unknown attributes are supplied via mass assignment.
141
+ class UnknownAttributeError < NoMethodError
142
+ end
143
+
144
+ # Raised when an error occurred while doing a mass assignment to an attribute through the
145
+ # <tt>attributes=</tt> method. The exception has an +attribute+ property that is the name of the
146
+ # offending attribute.
147
+ class AttributeAssignmentError < ActiveRecordError
148
+ attr_reader :exception, :attribute
149
+ def initialize(message, exception, attribute)
150
+ @exception = exception
151
+ @attribute = attribute
152
+ @message = message
153
+ end
154
+ end
155
+
156
+ # Raised when there are multiple errors while doing a mass assignment through the +attributes+
157
+ # method. The exception has an +errors+ property that contains an array of AttributeAssignmentError
158
+ # objects, each corresponding to the error while assigning to an attribute.
159
+ class MultiparameterAssignmentErrors < ActiveRecordError
160
+ attr_reader :errors
161
+ def initialize(errors)
162
+ @errors = errors
163
+ end
164
+ end
165
+ end