diff options
Diffstat (limited to 'activerecord/lib')
23 files changed, 295 insertions, 148 deletions
diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb index 7a2c5c8bf2..cbac2ef3c6 100644 --- a/activerecord/lib/active_record.rb +++ b/activerecord/lib/active_record.rb @@ -45,6 +45,7 @@ module ActiveRecord autoload :Migrator, 'active_record/migration' autoload :ModelSchema autoload :NestedAttributes + autoload :NoTouching autoload :Persistence autoload :QueryCache autoload :Querying 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 56331bbb0b..31b8d27892 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -163,7 +163,7 @@ module ActiveRecord delete_through_records(records) - if source_reflection.options[:counter_cache] + if source_reflection.options[:counter_cache] && method != :destroy counter = source_reflection.counter_cache_column klass.decrement_counter counter, records.map(&:id) end diff --git a/activerecord/lib/active_record/associations/has_one_association.rb b/activerecord/lib/active_record/associations/has_one_association.rb index 0008600418..944caacab6 100644 --- a/activerecord/lib/active_record/associations/has_one_association.rb +++ b/activerecord/lib/active_record/associations/has_one_association.rb @@ -26,11 +26,13 @@ module ActiveRecord load_target return self.target if !(target || record) - if (target != record) || record.changed? + + assigning_another_record = target != record + if assigning_another_record || record.changed? save &&= owner.persisted? transaction_if(save) do - remove_target!(options[:dependent]) if target && !target.destroyed? + remove_target!(options[:dependent]) if target && !target.destroyed? && assigning_another_record if record set_owner_attributes(record) diff --git a/activerecord/lib/active_record/attribute_methods/serialization.rb b/activerecord/lib/active_record/attribute_methods/serialization.rb index 5701804168..d484659190 100644 --- a/activerecord/lib/active_record/attribute_methods/serialization.rb +++ b/activerecord/lib/active_record/attribute_methods/serialization.rb @@ -24,6 +24,9 @@ module ActiveRecord # serialized object must be of that class on retrieval or # <tt>SerializationTypeMismatch</tt> will be raised. # + # A notable side effect of serialized attributes is that the model will + # be updated on every save, even if it is not dirty. + # # ==== Parameters # # * +attr_name+ - The field name that should be serialized. diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 69a9eabefb..e05e22ebb0 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -295,6 +295,7 @@ module ActiveRecord #:nodoc: extend Delegation::DelegateCache include Persistence + include NoTouching include ReadonlyAttributes include ModelSchema include Inheritance diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb new file mode 100644 index 0000000000..7c330a2f25 --- /dev/null +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb @@ -0,0 +1,83 @@ +module ActiveRecord + module ConnectionAdapters + class AbstractAdapter + class SchemaCreation # :nodoc: + def initialize(conn) + @conn = conn + @cache = {} + end + + def accept(o) + m = @cache[o.class] ||= "visit_#{o.class.name.split('::').last}" + send m, o + end + + def visit_AddColumn(o) + sql_type = type_to_sql(o.type.to_sym, o.limit, o.precision, o.scale) + sql = "ADD #{quote_column_name(o.name)} #{sql_type}" + add_column_options!(sql, column_options(o)) + end + + private + + def visit_AlterTable(o) + sql = "ALTER TABLE #{quote_table_name(o.name)} " + sql << o.adds.map { |col| visit_AddColumn col }.join(' ') + end + + def visit_ColumnDefinition(o) + sql_type = type_to_sql(o.type.to_sym, o.limit, o.precision, o.scale) + column_sql = "#{quote_column_name(o.name)} #{sql_type}" + add_column_options!(column_sql, column_options(o)) unless o.primary_key? + column_sql + end + + def visit_TableDefinition(o) + create_sql = "CREATE#{' TEMPORARY' if o.temporary} TABLE " + create_sql << "#{quote_table_name(o.name)} (" + create_sql << o.columns.map { |c| accept c }.join(', ') + create_sql << ") #{o.options}" + create_sql + end + + def column_options(o) + column_options = {} + column_options[:null] = o.null unless o.null.nil? + column_options[:default] = o.default unless o.default.nil? + column_options[:column] = o + column_options[:first] = o.first + column_options[:after] = o.after + column_options + end + + def quote_column_name(name) + @conn.quote_column_name name + end + + def quote_table_name(name) + @conn.quote_table_name name + end + + def type_to_sql(type, limit, precision, scale) + @conn.type_to_sql type.to_sym, limit, precision, scale + end + + def add_column_options!(sql, options) + sql << " DEFAULT #{@conn.quote(options[:default], options[:column])}" if options_include_default?(options) + # must explicitly check for :null to allow change_column to work on migrations + if options[:null] == false + sql << " NOT NULL" + end + if options[:auto_increment] == true + sql << " AUTO_INCREMENT" + end + sql + end + + def options_include_default?(options) + options.include?(:default) && !(options[:null] == false && options[:default].nil?) + end + end + 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 cbe563676b..8aa1ce5c04 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -4,6 +4,7 @@ require 'bigdecimal/util' require 'active_support/core_ext/benchmark' require 'active_record/connection_adapters/schema_cache' require 'active_record/connection_adapters/abstract/schema_dumper' +require 'active_record/connection_adapters/abstract/schema_creation' require 'monitor' module ActiveRecord @@ -16,6 +17,7 @@ module ActiveRecord autoload_at 'active_record/connection_adapters/abstract/schema_definitions' do autoload :IndexDefinition autoload :ColumnDefinition + autoload :ChangeColumnDefinition autoload :TableDefinition autoload :Table autoload :AlterTable @@ -40,6 +42,7 @@ module ActiveRecord autoload :ClosedTransaction autoload :RealTransaction autoload :SavepointTransaction + autoload :TransactionState end # Active Record supports multiple database systems. AbstractAdapter and @@ -105,84 +108,6 @@ module ActiveRecord true end - class SchemaCreation - def initialize(conn) - @conn = conn - @cache = {} - end - - def accept(o) - m = @cache[o.class] ||= "visit_#{o.class.name.split('::').last}" - send m, o - end - - def visit_AddColumn(o) - sql_type = type_to_sql(o.type.to_sym, o.limit, o.precision, o.scale) - sql = "ADD #{quote_column_name(o.name)} #{sql_type}" - add_column_options!(sql, column_options(o)) - end - - private - - def visit_AlterTable(o) - sql = "ALTER TABLE #{quote_table_name(o.name)} " - sql << o.adds.map { |col| visit_AddColumn col }.join(' ') - end - - def visit_ColumnDefinition(o) - sql_type = type_to_sql(o.type.to_sym, o.limit, o.precision, o.scale) - column_sql = "#{quote_column_name(o.name)} #{sql_type}" - add_column_options!(column_sql, column_options(o)) unless o.primary_key? - column_sql - end - - def visit_TableDefinition(o) - create_sql = "CREATE#{' TEMPORARY' if o.temporary} TABLE " - create_sql << "#{quote_table_name(o.name)} (" - create_sql << o.columns.map { |c| accept c }.join(', ') - create_sql << ") #{o.options}" - create_sql - end - - def column_options(o) - column_options = {} - column_options[:null] = o.null unless o.null.nil? - column_options[:default] = o.default unless o.default.nil? - column_options[:column] = o - column_options[:first] = o.first - column_options[:after] = o.after - column_options - end - - def quote_column_name(name) - @conn.quote_column_name name - end - - def quote_table_name(name) - @conn.quote_table_name name - end - - def type_to_sql(type, limit, precision, scale) - @conn.type_to_sql type.to_sym, limit, precision, scale - end - - def add_column_options!(sql, options) - sql << " DEFAULT #{@conn.quote(options[:default], options[:column])}" if options_include_default?(options) - # must explicitly check for :null to allow change_column to work on migrations - if options[:null] == false - sql << " NOT NULL" - end - if options[:auto_increment] == true - sql << " AUTO_INCREMENT" - end - sql - end - - def options_include_default?(options) - options.include?(:default) && !(options[:null] == false && options[:default].nil?) - end - end - def schema_creation SchemaCreation.new self end diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb index 138ab811dc..dcbc3466b2 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -206,6 +206,12 @@ module ActiveRecord true end + def type_cast(value, column) + return super unless value == true || value == false + + value ? 1 : 0 + end + # MySQL 4 technically support transaction isolation, but it is affected by a bug # where the transaction level gets persisted for the whole session: # @@ -314,27 +320,19 @@ module ActiveRecord def begin_db_transaction execute "BEGIN" - rescue - # Transactions aren't supported end def begin_isolated_db_transaction(isolation) execute "SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}" begin_db_transaction - rescue - # Transactions aren't supported end def commit_db_transaction #:nodoc: execute "COMMIT" - rescue - # Transactions aren't supported end def rollback_db_transaction #:nodoc: execute "ROLLBACK" - rescue - # Transactions aren't supported end # In the simple case, MySQL allows us to place JOINs directly into the UPDATE diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index 88c9494fc6..c4dcba0501 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -160,12 +160,6 @@ module ActiveRecord # QUOTING ================================================== - def type_cast(value, column) - return super unless value == true || value == false - - value ? 1 : 0 - end - def quote_string(string) #:nodoc: @connection.quote(string) end @@ -468,15 +462,17 @@ module ActiveRecord def begin_db_transaction #:nodoc: exec_query "BEGIN" - rescue Mysql::Error - # Transactions aren't supported end private def exec_stmt(sql, name, binds) cache = {} - log(sql, name, binds) do + type_casted_binds = binds.map { |col, val| + [col, type_cast(val, col)] + } + + log(sql, name, type_casted_binds) do if binds.empty? stmt = @connection.prepare(sql) else @@ -487,7 +483,7 @@ module ActiveRecord end begin - stmt.execute(*binds.map { |col, val| type_cast(val, col) }) + stmt.execute(*type_casted_binds.map { |_, val| val }) rescue Mysql::Error => e # Older versions of MySQL leave the prepared statement in a bad # place when an error occurs. To support older mysql versions, we diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb index e9daa5d7ff..c1f978a081 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb @@ -86,8 +86,11 @@ module ActiveRecord case value when Range - return super(value, column) unless /range$/ =~ column.sql_type - PostgreSQLColumn.range_to_string(value) + if /range$/ =~ column.sql_type + PostgreSQLColumn.range_to_string(value) + else + super(value, column) + end when NilClass if column.array && array_member 'NULL' @@ -101,12 +104,21 @@ module ActiveRecord when 'point' then PostgreSQLColumn.point_to_string(value) when 'json' then PostgreSQLColumn.json_to_string(value) else - return super(value, column) unless column.array - PostgreSQLColumn.array_to_string(value, column, self) + if column.array + PostgreSQLColumn.array_to_string(value, column, self) + else + super(value, column) + end end when String - return super(value, column) unless 'bytea' == column.sql_type - { :value => value, :format => 1 } + if 'bytea' == column.sql_type + # Return a bind param hash with format as binary. + # See http://deveiate.org/code/pg/PGconn.html#method-i-exec_prepared-doc + # for more information + { value: value, format: 1 } + else + super(value, column) + end when Hash case column.sql_type when 'hstore' then PostgreSQLColumn.hstore_to_string(value) @@ -114,8 +126,11 @@ module ActiveRecord else super(value, column) end when IPAddr - return super(value, column) unless ['inet','cidr'].include? column.sql_type - PostgreSQLColumn.cidr_to_string(value) + if %w(inet cidr).include? column.sql_type + PostgreSQLColumn.cidr_to_string(value) + else + super(value, column) + end else super(value, column) end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 3668aecd4b..adeb57d913 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -631,7 +631,6 @@ module ActiveRecord true end - # Returns true. def supports_explain? true end @@ -783,11 +782,12 @@ module ActiveRecord def exec_cache(sql, name, binds) stmt_key = prepare_statement(sql) + type_casted_binds = binds.map { |col, val| + [col, type_cast(val, col)] + } - log(sql, name, binds, stmt_key) do - @connection.send_query_prepared(stmt_key, binds.map { |col, val| - type_cast(val, col) - }) + log(sql, name, type_casted_binds, stmt_key) do + @connection.send_query_prepared(stmt_key, type_casted_binds.map { |_, val| val }) @connection.block @connection.get_last_result end diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb index e5ad08b6b0..2cf1015f2c 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb @@ -141,14 +141,12 @@ module ActiveRecord 'SQLite' end - # Returns true def supports_ddl_transactions? true end - # Returns true if SQLite version is '3.6.8' or greater, false otherwise. def supports_savepoints? - sqlite_version >= '3.6.8' + true end # Returns true, since this connection adapter supports prepared statement @@ -162,7 +160,6 @@ module ActiveRecord true end - # Returns true. def supports_primary_key? #:nodoc: true end @@ -171,7 +168,6 @@ module ActiveRecord true end - # Returns true def supports_add_column? true end @@ -193,11 +189,6 @@ module ActiveRecord @statements.clear end - # Returns true - def supports_count_distinct? #:nodoc: - true - end - def supports_index_sort_order? true end @@ -218,7 +209,6 @@ module ActiveRecord @connection.encoding.to_s end - # Returns true. def supports_explain? true end @@ -291,8 +281,11 @@ module ActiveRecord end def exec_query(sql, name = nil, binds = []) - log(sql, name, binds) do + type_casted_binds = binds.map { |col, val| + [col, type_cast(val, col)] + } + log(sql, name, type_casted_binds) do # Don't cache statements if they are not prepared if without_prepared_statement?(binds) stmt = @connection.prepare(sql) @@ -307,9 +300,7 @@ module ActiveRecord stmt = cache[:stmt] cols = cache[:cols] ||= stmt.columns stmt.reset! - stmt.bind_params binds.map { |col, val| - type_cast(val, col) - } + stmt.bind_params type_casted_binds.map { |_, val| val } end ActiveRecord::Result.new(cols, stmt.to_a) diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb index d93c26c130..96b5686ae0 100644 --- a/activerecord/lib/active_record/core.rb +++ b/activerecord/lib/active_record/core.rb @@ -109,7 +109,7 @@ module ActiveRecord elsif abstract_class? "#{super}(abstract)" elsif !connected? - "#{super}(no database connection)" + "#{super} (call '#{super}.connection' to establish a connection)" elsif table_exists? attr_list = columns.map { |c| "#{c.name}: #{c.type}" } * ', ' "#{super}(#{attr_list})" diff --git a/activerecord/lib/active_record/integration.rb b/activerecord/lib/active_record/integration.rb index f88c8e30e6..27576b1e61 100644 --- a/activerecord/lib/active_record/integration.rb +++ b/activerecord/lib/active_record/integration.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/string/filters' + module ActiveRecord module Integration extend ActiveSupport::Concern @@ -65,5 +67,45 @@ module ActiveRecord "#{self.class.model_name.cache_key}/#{id}" end end + + module ClassMethods + # Defines your model's +to_param+ method to generate "pretty" URLs + # using +method_name+, which can be any attribute or method that + # responds to +to_s+. + # + # class User < ActiveRecord::Base + # to_param :name + # end + # + # user = User.find_by(name: 'Fancy Pants') + # user.id # => 123 + # user_path(user) # => "/users/123-fancy-pants" + # + # Values longer than 20 characters will be truncated. The value + # is truncated word by word. + # + # user = User.find_by(name: 'David HeinemeierHansson') + # user.id # => 125 + # user_path(user) # => "/users/125-david" + # + # Because the generated param begins with the record's +id+, it is + # suitable for passing to +find+. In a controller, for example: + # + # params[:id] # => "123-fancy-pants" + # User.find(params[:id]).id # => 123 + def to_param(method_name = nil) + if method_name.nil? + super() + else + define_method :to_param do + if (default = super()) && (result = send(method_name).to_s).present? + "#{default}-#{result.squish.truncate(20, separator: /\s/, omission: nil).parameterize}" + else + default + end + end + end + end + end end end diff --git a/activerecord/lib/active_record/log_subscriber.rb b/activerecord/lib/active_record/log_subscriber.rb index 927fbab8f0..654ef21b07 100644 --- a/activerecord/lib/active_record/log_subscriber.rb +++ b/activerecord/lib/active_record/log_subscriber.rb @@ -23,6 +23,8 @@ module ActiveRecord def render_bind(column, value) if column if column.binary? + # This specifically deals with the PG adapter that casts bytea columns into a Hash. + value = value[:value] if value.is_a?(Hash) value = "<#{value.bytesize} bytes of binary data>" end diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 5224a6b67c..d010f23517 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -32,7 +32,11 @@ module ActiveRecord class PendingMigrationError < ActiveRecordError#:nodoc: def initialize - super("Migrations are pending; run 'bin/rake db:migrate RAILS_ENV=#{::Rails.env}' to resolve this issue.") + if defined?(Rails) + super("Migrations are pending; run 'bin/rake db:migrate RAILS_ENV=#{::Rails.env}' to resolve this issue.") + else + super("Migrations are pending; run 'bin/rake db:migrate' to resolve this issue.") + end end end diff --git a/activerecord/lib/active_record/no_touching.rb b/activerecord/lib/active_record/no_touching.rb new file mode 100644 index 0000000000..dbf4564ae5 --- /dev/null +++ b/activerecord/lib/active_record/no_touching.rb @@ -0,0 +1,52 @@ +module ActiveRecord + # = Active Record No Touching + module NoTouching + extend ActiveSupport::Concern + + module ClassMethods + # Lets you selectively disable calls to `touch` for the + # duration of a block. + # + # ==== Examples + # ActiveRecord::Base.no_touching do + # Project.first.touch # does nothing + # Message.first.touch # does nothing + # end + # + # Project.no_touching do + # Project.first.touch # does nothing + # Message.first.touch # works, but does not touch the associated project + # end + # + def no_touching(&block) + NoTouching.apply_to(self, &block) + end + end + + class << self + def apply_to(klass) #:nodoc: + klasses.push(klass) + yield + ensure + klasses.pop + end + + def applied_to?(klass) #:nodoc: + klasses.any? { |k| k >= klass } + end + + private + def klasses + Thread.current[:no_touching_classes] ||= [] + end + end + + def no_touching? + NoTouching.applied_to?(self.class) + end + + def touch(*) + super unless no_touching? + end + end +end diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 52b3d3e5e6..3ca66223b6 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -8,31 +8,47 @@ db_namespace = namespace :db do namespace :create do task :all => :load_config do - ActiveRecord::Tasks::DatabaseTasks.create_all + begin + ActiveRecord::Tasks::DatabaseTasks.create_all + rescue + exit(1) + end end end desc 'Create the database from DATABASE_URL or config/database.yml for the current Rails.env (use db:create:all to create all databases in the config)' task :create => [:load_config] do - if ENV['DATABASE_URL'] - ActiveRecord::Tasks::DatabaseTasks.create_database_url - else - ActiveRecord::Tasks::DatabaseTasks.create_current + begin + if ENV['DATABASE_URL'] + ActiveRecord::Tasks::DatabaseTasks.create_database_url + else + ActiveRecord::Tasks::DatabaseTasks.create_current + end + rescue + exit(1) end end namespace :drop do task :all => :load_config do - ActiveRecord::Tasks::DatabaseTasks.drop_all + begin + ActiveRecord::Tasks::DatabaseTasks.drop_all + rescue + exit(1) + end end end desc 'Drops the database using DATABASE_URL or the current Rails.env (use db:drop:all to drop all databases)' task :drop => [:load_config] do - if ENV['DATABASE_URL'] - ActiveRecord::Tasks::DatabaseTasks.drop_database_url - else - ActiveRecord::Tasks::DatabaseTasks.drop_current + begin + if ENV['DATABASE_URL'] + ActiveRecord::Tasks::DatabaseTasks.drop_database_url + else + ActiveRecord::Tasks::DatabaseTasks.drop_current + end + rescue + exit(1) end end diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 60f2726a6e..ff088818ee 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -244,7 +244,13 @@ module ActiveRecord def empty? return @records.empty? if loaded? - limit_value == 0 ? true : !exists? + if limit_value == 0 + true + else + # FIXME: This count is not compatible with #select('authors.*') or other select narrows + c = count + c.respond_to?(:zero?) ? c.zero? : c.empty? + end end # Returns true if there are any records. diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 3a02bf90e9..d91d6367a3 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -6,11 +6,12 @@ module ActiveRecord # If no record can be found for all of the listed ids, then RecordNotFound will be raised. If the primary key # is an integer, find by id coerces its arguments using +to_i+. # - # Person.find(1) # returns the object for ID = 1 - # Person.find("1") # returns the object for ID = 1 - # Person.find(1, 2, 6) # returns an array for objects with IDs in (1, 2, 6) - # Person.find([7, 17]) # returns an array for objects with IDs in (7, 17) - # Person.find([1]) # returns an array for the object with ID = 1 + # Person.find(1) # returns the object for ID = 1 + # Person.find("1") # returns the object for ID = 1 + # Person.find("31-sarah") # returns the object for ID = 31 + # Person.find(1, 2, 6) # returns an array for objects with IDs in (1, 2, 6) + # Person.find([7, 17]) # returns an array for objects with IDs in (7, 17) + # Person.find([1]) # returns an array for the object with ID = 1 # Person.where("administrator = 1").order("created_on DESC").find(1) # # <tt>ActiveRecord::RecordNotFound</tt> will be raised if one or more ids are not found. diff --git a/activerecord/lib/active_record/result.rb b/activerecord/lib/active_record/result.rb index 1dc3bf3f12..469451e2f4 100644 --- a/activerecord/lib/active_record/result.rb +++ b/activerecord/lib/active_record/result.rb @@ -83,9 +83,10 @@ module ActiveRecord end def initialize_copy(other) - @columns = columns.dup - @rows = rows.dup - @hash_rows = nil + @columns = columns.dup + @rows = rows.dup + @column_types = column_types.dup + @hash_rows = nil end private diff --git a/activerecord/lib/active_record/store.rb b/activerecord/lib/active_record/store.rb index b841b977fc..186a737561 100644 --- a/activerecord/lib/active_record/store.rb +++ b/activerecord/lib/active_record/store.rb @@ -15,6 +15,11 @@ module ActiveRecord # You can set custom coder to encode/decode your serialized attributes to/from different formats. # JSON, YAML, Marshal are supported out of the box. Generally it can be any wrapper that provides +load+ and +dump+. # + # NOTE - If you are using PostgreSQL specific columns like +hstore+ or +json+ there is no need for + # the serialization provided by +store+. Simply use +store_accessor+ instead to generate + # the accessor methods. Be aware that these columns use a string keyed hash and do not allow access + # using a symbol. + # # Examples: # # class User < ActiveRecord::Base diff --git a/activerecord/lib/active_record/tasks/database_tasks.rb b/activerecord/lib/active_record/tasks/database_tasks.rb index be7d496d15..0b6a49ac85 100644 --- a/activerecord/lib/active_record/tasks/database_tasks.rb +++ b/activerecord/lib/active_record/tasks/database_tasks.rb @@ -69,9 +69,11 @@ module ActiveRecord class_for_adapter(configuration['adapter']).new(*arguments).create rescue DatabaseAlreadyExists $stderr.puts "#{configuration['database']} already exists" + raise rescue Exception => error $stderr.puts error, *(error.backtrace) $stderr.puts "Couldn't create database for #{configuration.inspect}" + raise end def create_all @@ -95,6 +97,7 @@ module ActiveRecord rescue Exception => error $stderr.puts error, *(error.backtrace) $stderr.puts "Couldn't drop #{configuration['database']}" + raise end def drop_all |