diff options
Diffstat (limited to 'activerecord/lib')
18 files changed, 106 insertions, 89 deletions
diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb index 007e3bc555..44c4436e95 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -1,4 +1,3 @@ - module ActiveRecord # = Active Record Has Many Through Association module Associations diff --git a/activerecord/lib/active_record/associations/has_one_association.rb b/activerecord/lib/active_record/associations/has_one_association.rb index 944caacab6..e6095d84dc 100644 --- a/activerecord/lib/active_record/associations/has_one_association.rb +++ b/activerecord/lib/active_record/associations/has_one_association.rb @@ -1,4 +1,3 @@ - module ActiveRecord # = Active Record Belongs To Has One Association module Associations diff --git a/activerecord/lib/active_record/associations/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/join_dependency/join_association.rb index a0e83c0a02..719eff9acc 100644 --- a/activerecord/lib/active_record/associations/join_dependency/join_association.rb +++ b/activerecord/lib/active_record/associations/join_dependency/join_association.rb @@ -95,7 +95,7 @@ module ActiveRecord # end # # If I execute `Physician.joins(:appointments).to_a` then - # reflection # => #<ActiveRecord::Reflection::AssociationReflection @macro=:has_many ...> + # reflection # => #<ActiveRecord::Reflection::HasManyReflection ...> # table # => #<Arel::Table @name="appointments" ...> # key # => physician_id # foreign_table # => #<Arel::Table @name="physicians" ...> diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 64cc5b68cc..f978fbd0a4 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -141,6 +141,7 @@ module ActiveRecord #:nodoc: # # In addition to the basic accessors, query methods are also automatically available on the Active Record object. # Query methods allow you to test whether an attribute value is present. + # For numeric values, present is defined as non-zero. # # For example, an Active Record User with the <tt>name</tt> attribute has a <tt>name?</tt> method that you can call # to determine whether the user has a name: diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb index e8ce00d92b..98e96099cb 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb @@ -203,62 +203,30 @@ module ActiveRecord if options[:isolation] raise ActiveRecord::TransactionIsolationError, "cannot set isolation when joining a transaction" end - yield else - within_new_transaction(options) { yield } + transaction_manager.within_new_transaction(options) { yield } end rescue ActiveRecord::Rollback # rollbacks are silently swallowed end - def within_new_transaction(options = {}) #:nodoc: - transaction = begin_transaction(options) - yield - rescue Exception => error - rollback_transaction if transaction - raise - ensure - begin - commit_transaction unless error - rescue Exception - rollback_transaction - raise - end - end - - def open_transactions - @transaction.number - end + attr_reader :transaction_manager #:nodoc: - def current_transaction #:nodoc: - @transaction - end + delegate :within_new_transaction, :open_transactions, :current_transaction, :begin_transaction, :commit_transaction, :rollback_transaction, to: :transaction_manager def transaction_open? - @transaction.open? - end - - def begin_transaction(options = {}) #:nodoc: - @transaction = @transaction.begin(options) - end - - def commit_transaction #:nodoc: - @transaction = @transaction.commit - end - - def rollback_transaction #:nodoc: - @transaction = @transaction.rollback + current_transaction.open? end def reset_transaction #:nodoc: - @transaction = ClosedTransaction.new(self) + @transaction_manager = TransactionManager.new(self) end # Register a record with the current transaction so that its after_commit and after_rollback callbacks # can be called. def add_transaction_record(record) - @transaction.add_record(record) + current_transaction.add_record(record) end # Begins the transaction (and turns off auto-committing). diff --git a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb index bc4884b538..3a266512a9 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb @@ -1,5 +1,59 @@ module ActiveRecord module ConnectionAdapters + class TransactionManager #:nodoc: + def initialize(connection) + @stack = [] + @connection = connection + end + + def begin_transaction(options = {}) + transaction_class = @stack.empty? ? RealTransaction : SavepointTransaction + transaction = transaction_class.new(@connection, current_transaction, options) + + @stack.push(transaction) + transaction + end + + def commit_transaction + @stack.pop.commit + end + + def rollback_transaction + @stack.pop.rollback + end + + def within_new_transaction(options = {}) + transaction = begin_transaction options + yield + rescue Exception => error + transaction.rollback if transaction + raise + ensure + begin + transaction.commit unless error + rescue Exception + transaction.rollback + raise + ensure + @stack.pop if transaction + end + end + + def open_transactions + @stack.size + end + + def current_transaction + @stack.last || closed_transaction + end + + private + + def closed_transaction + @closed_transaction ||= ClosedTransaction.new(@connection) + end + end + class Transaction #:nodoc: attr_reader :connection @@ -11,6 +65,10 @@ module ActiveRecord def state @state end + + def savepoint_name + nil + end end class TransactionState @@ -78,45 +136,28 @@ module ActiveRecord @parent = parent @records = [] - @finishing = false @joinable = options.fetch(:joinable, true) end - # This state is necessary so that we correctly handle stuff that might - # happen in a commit/rollback. But it's kinda distasteful. Maybe we can - # find a better way to structure it in the future. - def finishing? - @finishing - end def joinable? - @joinable && !finishing? + @joinable end def number - if finishing? - parent.number - else - parent.number + 1 - end + parent.number + 1 end def begin(options = {}) - if finishing? - parent.begin - else - SavepointTransaction.new(connection, self, options) - end + SavepointTransaction.new(connection, self, options) end def rollback - @finishing = true perform_rollback parent end def commit - @finishing = true perform_commit parent end @@ -183,24 +224,29 @@ module ActiveRecord end class SavepointTransaction < OpenTransaction #:nodoc: + attr_reader :savepoint_name + def initialize(connection, parent, options = {}) if options[:isolation] raise ActiveRecord::TransactionIsolationError, "cannot set transaction isolation in a nested transaction" end super - connection.create_savepoint + + # Savepoint name only counts the Savepoint transactions, so we need to subtract 1 + @savepoint_name = "active_record_#{number - 1}" + connection.create_savepoint(@savepoint_name) end def perform_rollback - connection.rollback_to_savepoint + connection.rollback_to_savepoint(@savepoint_name) rollback_records end def perform_commit @state.set_state(:committed) @state.parent = parent.state - connection.release_savepoint + connection.release_savepoint(@savepoint_name) end end end diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index f8c054eb69..99c728814a 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -45,6 +45,7 @@ module ActiveRecord end autoload_at 'active_record/connection_adapters/abstract/transaction' do + autoload :TransactionManager autoload :ClosedTransaction autoload :RealTransaction autoload :SavepointTransaction @@ -357,7 +358,7 @@ module ActiveRecord end def current_savepoint_name - "active_record_#{open_transactions}" + current_transaction.savepoint_name end # Check the connection back in to the connection pool diff --git a/activerecord/lib/active_record/connection_adapters/schema_cache.rb b/activerecord/lib/active_record/connection_adapters/schema_cache.rb index 4d8afcf16a..3116bed596 100644 --- a/activerecord/lib/active_record/connection_adapters/schema_cache.rb +++ b/activerecord/lib/active_record/connection_adapters/schema_cache.rb @@ -1,4 +1,3 @@ - module ActiveRecord module ConnectionAdapters class SchemaCache diff --git a/activerecord/lib/active_record/migration/command_recorder.rb b/activerecord/lib/active_record/migration/command_recorder.rb index f833caaab6..36256415df 100644 --- a/activerecord/lib/active_record/migration/command_recorder.rb +++ b/activerecord/lib/active_record/migration/command_recorder.rb @@ -87,7 +87,7 @@ module ActiveRecord alias :add_belongs_to :add_reference alias :remove_belongs_to :remove_reference - def change_table(table_name, options = {}) + def change_table(table_name, options = {}) # :nodoc: yield delegate.update_table_definition(table_name, self) end diff --git a/activerecord/lib/active_record/query_cache.rb b/activerecord/lib/active_record/query_cache.rb index 16ad942912..dcb2bd3d84 100644 --- a/activerecord/lib/active_record/query_cache.rb +++ b/activerecord/lib/active_record/query_cache.rb @@ -1,4 +1,3 @@ - module ActiveRecord # = Active Record Query Cache class QueryCache diff --git a/activerecord/lib/active_record/readonly_attributes.rb b/activerecord/lib/active_record/readonly_attributes.rb index b3ddfd63d4..85bbac43e4 100644 --- a/activerecord/lib/active_record/readonly_attributes.rb +++ b/activerecord/lib/active_record/readonly_attributes.rb @@ -1,4 +1,3 @@ - module ActiveRecord module ReadonlyAttributes extend ActiveSupport::Concern diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index 86eef9ca36..1672128aa3 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -167,9 +167,12 @@ module ActiveRecord # AggregateReflection and AssociationReflection are returned by the Reflection::ClassMethods. # # MacroReflection - # AggregateReflection # AssociationReflection - # ThroughReflection + # AggregateReflection + # HasManyReflection + # HasOneReflection + # BelongsToReflection + # ThroughReflection class MacroReflection < AbstractReflection # Returns the name of the macro. # @@ -647,7 +650,7 @@ Joining, Preloading and eager loading of these associations is deprecated and wi # # tags_reflection = Post.reflect_on_association(:tags) # tags_reflection.source_reflection - # # => <ActiveRecord::Reflection::AssociationReflection: @macro=:belongs_to, @name=:tag, @active_record=Tagging, @plural_name="tags"> + # # => <ActiveRecord::Reflection::BelongsToReflection: @name=:tag, @active_record=Tagging, @plural_name="tags"> # def source_reflection through_reflection.klass._reflect_on_association(source_reflection_name) @@ -663,7 +666,7 @@ Joining, Preloading and eager loading of these associations is deprecated and wi # # tags_reflection = Post.reflect_on_association(:tags) # tags_reflection.through_reflection - # # => <ActiveRecord::Reflection::AssociationReflection: @macro=:has_many, @name=:taggings, @active_record=Post, @plural_name="taggings"> + # # => <ActiveRecord::Reflection::HasManyReflection: @name=:taggings, @active_record=Post, @plural_name="taggings"> # def through_reflection active_record._reflect_on_association(options[:through]) @@ -683,8 +686,8 @@ Joining, Preloading and eager loading of these associations is deprecated and wi # # tags_reflection = Post.reflect_on_association(:tags) # tags_reflection.chain - # # => [<ActiveRecord::Reflection::ThroughReflection: @macro=:has_many, @name=:tags, @options={:through=>:taggings}, @active_record=Post>, - # <ActiveRecord::Reflection::AssociationReflection: @macro=:has_many, @name=:taggings, @options={}, @active_record=Post>] + # # => [<ActiveRecord::Reflection::ThroughReflection: @delegate_reflection=#<ActiveRecord::Reflection::HasManyReflection: @name=:tags...>, + # <ActiveRecord::Reflection::HasManyReflection: @name=:taggings, @options={}, @active_record=Post>] # def chain @chain ||= begin diff --git a/activerecord/lib/active_record/relation/batches.rb b/activerecord/lib/active_record/relation/batches.rb index 29fc150b3d..b069cdce7c 100644 --- a/activerecord/lib/active_record/relation/batches.rb +++ b/activerecord/lib/active_record/relation/batches.rb @@ -1,4 +1,3 @@ - module ActiveRecord module Batches # Looping through a collection of records from the database diff --git a/activerecord/lib/active_record/schema.rb b/activerecord/lib/active_record/schema.rb index 4bfd0167a4..0a5546a760 100644 --- a/activerecord/lib/active_record/schema.rb +++ b/activerecord/lib/active_record/schema.rb @@ -1,4 +1,3 @@ - module ActiveRecord # = Active Record Schema # diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb index a94364bde1..fae6427ea1 100644 --- a/activerecord/lib/active_record/schema_dumper.rb +++ b/activerecord/lib/active_record/schema_dumper.rb @@ -92,16 +92,9 @@ HEADER def tables(stream) sorted_tables = @connection.tables.sort - sorted_tables.each do |tbl| - next if ['schema_migrations', ignore_tables].flatten.any? do |ignored| - case ignored - when String; remove_prefix_and_suffix(tbl) == ignored - when Regexp; remove_prefix_and_suffix(tbl) =~ ignored - else - raise StandardError, 'ActiveRecord::SchemaDumper.ignore_tables accepts an array of String and / or Regexp values.' - end - end - table(tbl, stream) + + sorted_tables.each do |table_name| + table(table_name, stream) unless ignored?(table_name) end # dump foreign keys at the end to make sure all dependent tables exist. @@ -254,5 +247,16 @@ HEADER def remove_prefix_and_suffix(table) table.gsub(/^(#{@options[:table_name_prefix]})(.+)(#{@options[:table_name_suffix]})$/, "\\2") end + + def ignored?(table_name) + ['schema_migrations', ignore_tables].flatten.any? do |ignored| + case ignored + when String; remove_prefix_and_suffix(table_name) == ignored + when Regexp; remove_prefix_and_suffix(table_name) =~ ignored + else + raise StandardError, 'ActiveRecord::SchemaDumper.ignore_tables accepts an array of String and / or Regexp values.' + end + end + end end end diff --git a/activerecord/lib/active_record/tasks/database_tasks.rb b/activerecord/lib/active_record/tasks/database_tasks.rb index 9bc23b5ec7..b7315ed4b3 100644 --- a/activerecord/lib/active_record/tasks/database_tasks.rb +++ b/activerecord/lib/active_record/tasks/database_tasks.rb @@ -110,6 +110,8 @@ module ActiveRecord def drop(*arguments) configuration = arguments.first class_for_adapter(configuration['adapter']).new(*arguments).drop + rescue ActiveRecord::NoDatabaseError + $stderr.puts "Database '#{configuration['database']}' does not exist" rescue Exception => error $stderr.puts error, *(error.backtrace) $stderr.puts "Couldn't drop #{configuration['database']}" diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb index e2e37e7c00..ddf3e1804c 100644 --- a/activerecord/lib/active_record/timestamp.rb +++ b/activerecord/lib/active_record/timestamp.rb @@ -1,4 +1,3 @@ - module ActiveRecord # = Active Record Timestamp # diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb index 2a34969a8c..2dba4c7b94 100644 --- a/activerecord/lib/active_record/validations/uniqueness.rb +++ b/activerecord/lib/active_record/validations/uniqueness.rb @@ -152,7 +152,7 @@ module ActiveRecord # or <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method, # proc or string should return or evaluate to a +true+ or +false+ value. # * <tt>:unless</tt> - Specifies a method, proc or string to call to - # determine if the validation should ot occur (e.g. <tt>unless: :skip_validation</tt>, + # determine if the validation should not occur (e.g. <tt>unless: :skip_validation</tt>, # or <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>). The # method, proc or string should return or evaluate to a +true+ or +false+ # value. |