diff options
Diffstat (limited to 'activerecord')
15 files changed, 49 insertions, 691 deletions
diff --git a/activerecord/activerecord.gemspec b/activerecord/activerecord.gemspec index a3c0acb370..dd9dc288cd 100644 --- a/activerecord/activerecord.gemspec +++ b/activerecord/activerecord.gemspec @@ -23,6 +23,6 @@ Gem::Specification.new do |s| s.add_dependency('activesupport', version) s.add_dependency('activemodel', version) - s.add_dependency('arel', '~> 0.4.0') + s.add_dependency('arel', '~> 1.0.0.rc1') s.add_dependency('tzinfo', '~> 0.3.23') end diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb index 5ac89a93c2..63224377a3 100644 --- a/activerecord/lib/active_record/association_preload.rb +++ b/activerecord/lib/active_record/association_preload.rb @@ -215,7 +215,7 @@ module ActiveRecord source = reflection.source_reflection.name through_records.first.class.preload_associations(through_records, source) if through_reflection.macro == :belongs_to - rev_id_to_record_map, rev_ids = construct_id_map(records, through_primary_key) + rev_id_to_record_map = construct_id_map(records, through_primary_key).first rev_primary_key = through_reflection.klass.primary_key through_records.each do |through_record| add_preloaded_record_to_collection(rev_id_to_record_map[through_record[rev_primary_key].to_s], @@ -243,7 +243,6 @@ module ActiveRecord if options[:through] through_records = preload_through_records(records, reflection, options[:through]) - through_reflection = reflections[options[:through]] unless through_records.empty? source = reflection.source_reflection.name through_records.first.class.preload_associations(through_records, source, options) @@ -261,7 +260,6 @@ module ActiveRecord def preload_through_records(records, reflection, through_association) through_reflection = reflections[through_association] - through_primary_key = through_reflection.primary_key_name through_records = [] if reflection.options[:source_type] @@ -372,7 +370,7 @@ module ActiveRecord conditions << append_conditions(reflection, preload_options) find_options = { - :select => preload_options[:select] || options[:select] || "#{table_name}.*", + :select => preload_options[:select] || options[:select] || Arel::SqlLiteral.new("#{table_name}.*"), :include => preload_options[:include] || options[:include], :conditions => [conditions, ids], :joins => options[:joins], diff --git a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb index 862a19587d..e983f86f9e 100644 --- a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb +++ b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb @@ -24,7 +24,7 @@ module ActiveRecord protected def construct_find_options!(options) - options[:joins] = @join_sql + options[:joins] = Arel::SqlLiteral.new @join_sql options[:readonly] = finding_with_ambiguous_select?(options[:select] || @reflection.options[:select]) options[:select] ||= (@reflection.options[:select] || '*') end diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index bbb44f0e17..4bbf5cd3d1 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -32,7 +32,7 @@ module ActiveRecord #:nodoc: # Active Record objects. The mapping that binds a given Active Record class to a certain # database table will happen automatically in most common cases, but can be overwritten for the uncommon ones. # - # See the mapping rules in table_name and the full example in link:files/README.html for more insight. + # See the mapping rules in table_name and the full example in link:files/activerecord/README_rdoc.html for more insight. # # == Creation # diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb deleted file mode 100644 index b5bf7f46ef..0000000000 --- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb +++ /dev/null @@ -1,644 +0,0 @@ -# encoding: utf-8 - -require 'mysql2' unless defined? Mysql2 - -module ActiveRecord - class Base - def self.mysql2_connection(config) - config[:username] = 'root' if config[:username].nil? - - if Mysql2::Client.const_defined? :FOUND_ROWS - config[:flags] = Mysql2::Client::FOUND_ROWS - end - - client = Mysql2::Client.new(config.symbolize_keys) - options = [config[:host], config[:username], config[:password], config[:database], config[:port], config[:socket], 0] - ConnectionAdapters::Mysql2Adapter.new(client, logger, options, config) - end - end - - module ConnectionAdapters - class Mysql2Column < Column - BOOL = "tinyint(1)" - def extract_default(default) - if sql_type =~ /blob/i || type == :text - if default.blank? - return null ? nil : '' - else - raise ArgumentError, "#{type} columns cannot have a default value: #{default.inspect}" - end - elsif missing_default_forged_as_empty_string?(default) - nil - else - super - end - end - - def has_default? - return false if sql_type =~ /blob/i || type == :text #mysql forbids defaults on blob and text columns - super - end - - # Returns the Ruby class that corresponds to the abstract data type. - def klass - case type - when :integer then Fixnum - when :float then Float - when :decimal then BigDecimal - when :datetime then Time - when :date then Date - when :timestamp then Time - when :time then Time - when :text, :string then String - when :binary then String - when :boolean then Object - end - end - - def type_cast(value) - return nil if value.nil? - case type - when :string then value - when :text then value - when :integer then value.to_i rescue value ? 1 : 0 - when :float then value.to_f # returns self if it's already a Float - when :decimal then self.class.value_to_decimal(value) - when :datetime, :timestamp then value.class == Time ? value : self.class.string_to_time(value) - when :time then value.class == Time ? value : self.class.string_to_dummy_time(value) - when :date then value.class == Date ? value : self.class.string_to_date(value) - when :binary then value - when :boolean then self.class.value_to_boolean(value) - else value - end - end - - def type_cast_code(var_name) - case type - when :string then nil - when :text then nil - when :integer then "#{var_name}.to_i rescue #{var_name} ? 1 : 0" - when :float then "#{var_name}.to_f" - when :decimal then "#{self.class.name}.value_to_decimal(#{var_name})" - when :datetime, :timestamp then "#{var_name}.class == Time ? #{var_name} : #{self.class.name}.string_to_time(#{var_name})" - when :time then "#{var_name}.class == Time ? #{var_name} : #{self.class.name}.string_to_dummy_time(#{var_name})" - when :date then "#{var_name}.class == Date ? #{var_name} : #{self.class.name}.string_to_date(#{var_name})" - when :binary then nil - when :boolean then "#{self.class.name}.value_to_boolean(#{var_name})" - else nil - end - end - - private - def simplified_type(field_type) - return :boolean if Mysql2Adapter.emulate_booleans && field_type.downcase.index(BOOL) - return :string if field_type =~ /enum/i or field_type =~ /set/i - return :integer if field_type =~ /year/i - return :binary if field_type =~ /bit/i - super - end - - def extract_limit(sql_type) - case sql_type - when /blob|text/i - case sql_type - when /tiny/i - 255 - when /medium/i - 16777215 - when /long/i - 2147483647 # mysql only allows 2^31-1, not 2^32-1, somewhat inconsistently with the tiny/medium/normal cases - else - super # we could return 65535 here, but we leave it undecorated by default - end - when /^bigint/i; 8 - when /^int/i; 4 - when /^mediumint/i; 3 - when /^smallint/i; 2 - when /^tinyint/i; 1 - else - super - end - end - - # MySQL misreports NOT NULL column default when none is given. - # We can't detect this for columns which may have a legitimate '' - # default (string) but we can for others (integer, datetime, boolean, - # and the rest). - # - # Test whether the column has default '', is not null, and is not - # a type allowing default ''. - def missing_default_forged_as_empty_string?(default) - type != :string && !null && default == '' - end - end - - class Mysql2Adapter < AbstractAdapter - cattr_accessor :emulate_booleans - self.emulate_booleans = true - - ADAPTER_NAME = 'Mysql2' - PRIMARY = "PRIMARY" - - LOST_CONNECTION_ERROR_MESSAGES = [ - "Server shutdown in progress", - "Broken pipe", - "Lost connection to MySQL server during query", - "MySQL server has gone away" ] - - QUOTED_TRUE, QUOTED_FALSE = '1', '0' - - NATIVE_DATABASE_TYPES = { - :primary_key => "int(11) DEFAULT NULL auto_increment PRIMARY KEY", - :string => { :name => "varchar", :limit => 255 }, - :text => { :name => "text" }, - :integer => { :name => "int", :limit => 4 }, - :float => { :name => "float" }, - :decimal => { :name => "decimal" }, - :datetime => { :name => "datetime" }, - :timestamp => { :name => "datetime" }, - :time => { :name => "time" }, - :date => { :name => "date" }, - :binary => { :name => "blob" }, - :boolean => { :name => "tinyint", :limit => 1 } - } - - def initialize(connection, logger, connection_options, config) - super(connection, logger) - @connection_options, @config = connection_options, config - @quoted_column_names, @quoted_table_names = {}, {} - configure_connection - end - - def adapter_name - ADAPTER_NAME - end - - def supports_migrations? - true - end - - def supports_primary_key? - true - end - - def supports_savepoints? - true - end - - def native_database_types - NATIVE_DATABASE_TYPES - end - - # QUOTING ================================================== - - def quote(value, column = nil) - if value.kind_of?(String) && column && column.type == :binary && column.class.respond_to?(:string_to_binary) - s = column.class.string_to_binary(value).unpack("H*")[0] - "x'#{s}'" - elsif value.kind_of?(BigDecimal) - value.to_s("F") - else - super - end - end - - def quote_column_name(name) #:nodoc: - @quoted_column_names[name] ||= "`#{name}`" - end - - def quote_table_name(name) #:nodoc: - @quoted_table_names[name] ||= quote_column_name(name).gsub('.', '`.`') - end - - def quote_string(string) - @connection.escape(string) - end - - def quoted_true - QUOTED_TRUE - end - - def quoted_false - QUOTED_FALSE - end - - # REFERENTIAL INTEGRITY ==================================== - - def disable_referential_integrity(&block) #:nodoc: - old = select_value("SELECT @@FOREIGN_KEY_CHECKS") - - begin - update("SET FOREIGN_KEY_CHECKS = 0") - yield - ensure - update("SET FOREIGN_KEY_CHECKS = #{old}") - end - end - - # CONNECTION MANAGEMENT ==================================== - - def active? - return false unless @connection - @connection.query 'select 1' - true - rescue Mysql2::Error - false - end - - def reconnect! - disconnect! - connect - end - - # this is set to true in 2.3, but we don't want it to be - def requires_reloading? - false - end - - def disconnect! - unless @connection.nil? - @connection.close - @connection = nil - end - end - - def reset! - disconnect! - connect - end - - # DATABASE STATEMENTS ====================================== - - # FIXME: re-enable the following once a "better" query_cache solution is in core - # - # The overrides below perform much better than the originals in AbstractAdapter - # because we're able to take advantage of mysql2's lazy-loading capabilities - # - # # Returns a record hash with the column names as keys and column values - # # as values. - # def select_one(sql, name = nil) - # result = execute(sql, name) - # result.each(:as => :hash) do |r| - # return r - # end - # end - # - # # Returns a single value from a record - # def select_value(sql, name = nil) - # result = execute(sql, name) - # if first = result.first - # first.first - # end - # end - # - # # Returns an array of the values of the first column in a select: - # # select_values("SELECT id FROM companies LIMIT 3") => [1,2,3] - # def select_values(sql, name = nil) - # execute(sql, name).map { |row| row.first } - # end - - # Returns an array of arrays containing the field values. - # Order is the same as that returned by +columns+. - def select_rows(sql, name = nil) - execute(sql, name).to_a - end - - # Executes the SQL statement in the context of this connection. - def execute(sql, name = nil) - # make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been - # made since we established the connection - @connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone - if name == :skip_logging - @connection.query(sql) - else - log(sql, name) { @connection.query(sql) } - end - rescue ActiveRecord::StatementInvalid => exception - if exception.message.split(":").first =~ /Packets out of order/ - 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." - else - raise - end - end - - def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) - super - id_value || @connection.last_id - end - alias :create :insert_sql - - def update_sql(sql, name = nil) - super - @connection.affected_rows - end - - def begin_db_transaction - execute "BEGIN" - rescue Exception - # Transactions aren't supported - end - - def commit_db_transaction - execute "COMMIT" - rescue Exception - # Transactions aren't supported - end - - def rollback_db_transaction - execute "ROLLBACK" - rescue Exception - # Transactions aren't supported - end - - def create_savepoint - execute("SAVEPOINT #{current_savepoint_name}") - end - - def rollback_to_savepoint - execute("ROLLBACK TO SAVEPOINT #{current_savepoint_name}") - end - - def release_savepoint - execute("RELEASE SAVEPOINT #{current_savepoint_name}") - end - - def add_limit_offset!(sql, options) - limit, offset = options[:limit], options[:offset] - if limit && offset - sql << " LIMIT #{offset.to_i}, #{sanitize_limit(limit)}" - elsif limit - sql << " LIMIT #{sanitize_limit(limit)}" - elsif offset - sql << " OFFSET #{offset.to_i}" - end - sql - end - - # SCHEMA STATEMENTS ======================================== - - def structure_dump - if supports_views? - sql = "SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'" - else - sql = "SHOW TABLES" - end - - select_all(sql).inject("") do |structure, table| - table.delete('Table_type') - structure += select_one("SHOW CREATE TABLE #{quote_table_name(table.to_a.first.last)}")["Create Table"] + ";\n\n" - end - end - - def recreate_database(name, options = {}) - drop_database(name) - create_database(name, options) - end - - # Create a new MySQL database with optional <tt>:charset</tt> and <tt>:collation</tt>. - # Charset defaults to utf8. - # - # Example: - # create_database 'charset_test', :charset => 'latin1', :collation => 'latin1_bin' - # create_database 'matt_development' - # create_database 'matt_development', :charset => :big5 - def create_database(name, options = {}) - if options[:collation] - execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}` COLLATE `#{options[:collation]}`" - else - execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}`" - end - end - - def drop_database(name) #:nodoc: - execute "DROP DATABASE IF EXISTS `#{name}`" - end - - def current_database - select_value 'SELECT DATABASE() as db' - end - - # Returns the database character set. - def charset - show_variable 'character_set_database' - end - - # Returns the database collation strategy. - def collation - show_variable 'collation_database' - end - - def tables(name = nil) - tables = [] - execute("SHOW TABLES", name).each do |field| - tables << field.first - end - tables - end - - def drop_table(table_name, options = {}) - super(table_name, options) - end - - def indexes(table_name, name = nil) - indexes = [] - current_index = nil - result = execute("SHOW KEYS FROM #{quote_table_name(table_name)}", name) - result.each(:symbolize_keys => true, :as => :hash) do |row| - if current_index != row[:Key_name] - next if row[:Key_name] == PRIMARY # skip the primary key - current_index = row[:Key_name] - indexes << IndexDefinition.new(row[:Table], row[:Key_name], row[:Non_unique] == 0, []) - end - - indexes.last.columns << row[:Column_name] - end - indexes - end - - def columns(table_name, name = nil) - sql = "SHOW FIELDS FROM #{quote_table_name(table_name)}" - columns = [] - result = execute(sql, :skip_logging) - result.each(:symbolize_keys => true, :as => :hash) { |field| - columns << Mysql2Column.new(field[:Field], field[:Default], field[:Type], field[:Null] == "YES") - } - columns - end - - def create_table(table_name, options = {}) - super(table_name, options.reverse_merge(:options => "ENGINE=InnoDB")) - end - - def rename_table(table_name, new_name) - execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}" - end - - def add_column(table_name, column_name, type, options = {}) - add_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}" - add_column_options!(add_column_sql, options) - add_column_position!(add_column_sql, options) - execute(add_column_sql) - end - - def change_column_default(table_name, column_name, default) - column = column_for(table_name, column_name) - change_column table_name, column_name, column.sql_type, :default => default - end - - def change_column_null(table_name, column_name, null, default = nil) - column = column_for(table_name, column_name) - - unless null || default.nil? - execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL") - end - - change_column table_name, column_name, column.sql_type, :null => null - end - - def change_column(table_name, column_name, type, options = {}) - column = column_for(table_name, column_name) - - unless options_include_default?(options) - options[:default] = column.default - end - - unless options.has_key?(:null) - options[:null] = column.null - end - - change_column_sql = "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}" - add_column_options!(change_column_sql, options) - add_column_position!(change_column_sql, options) - execute(change_column_sql) - end - - def rename_column(table_name, column_name, new_column_name) - options = {} - if column = columns(table_name).find { |c| c.name == column_name.to_s } - options[:default] = column.default - options[:null] = column.null - else - raise ActiveRecordError, "No such column: #{table_name}.#{column_name}" - end - current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'")["Type"] - rename_column_sql = "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(new_column_name)} #{current_type}" - add_column_options!(rename_column_sql, options) - execute(rename_column_sql) - end - - # Maps logical Rails types to MySQL-specific data types. - def type_to_sql(type, limit = nil, precision = nil, scale = nil) - return super unless type.to_s == 'integer' - - case limit - when 1; 'tinyint' - when 2; 'smallint' - when 3; 'mediumint' - when nil, 4, 11; 'int(11)' # compatibility with MySQL default - when 5..8; 'bigint' - else raise(ActiveRecordError, "No integer type has byte size #{limit}") - end - end - - def add_column_position!(sql, options) - if options[:first] - sql << " FIRST" - elsif options[:after] - sql << " AFTER #{quote_column_name(options[:after])}" - end - end - - def show_variable(name) - variables = select_all("SHOW VARIABLES LIKE '#{name}'") - variables.first['Value'] unless variables.empty? - end - - def pk_and_sequence_for(table) - keys = [] - result = execute("describe #{quote_table_name(table)}") - result.each(:symbolize_keys => true, :as => :hash) do |row| - keys << row[:Field] if row[:Key] == "PRI" - end - keys.length == 1 ? [keys.first, nil] : nil - end - - # Returns just a table's primary key - def primary_key(table) - pk_and_sequence = pk_and_sequence_for(table) - pk_and_sequence && pk_and_sequence.first - end - - def case_sensitive_equality_operator - "= BINARY" - end - - def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key) - where_sql - end - - protected - def quoted_columns_for_index(column_names, options = {}) - length = options[:length] if options.is_a?(Hash) - - quoted_column_names = case length - when Hash - column_names.map {|name| length[name] ? "#{quote_column_name(name)}(#{length[name]})" : quote_column_name(name) } - when Fixnum - column_names.map {|name| "#{quote_column_name(name)}(#{length})"} - else - column_names.map {|name| quote_column_name(name) } - end - end - - def translate_exception(exception, message) - return super unless exception.respond_to?(:error_number) - - case exception.error_number - when 1062 - RecordNotUnique.new(message, exception) - when 1452 - InvalidForeignKey.new(message, exception) - else - super - end - end - - private - def connect - @connection = Mysql2::Client.new(@config) - configure_connection - end - - def configure_connection - @connection.query_options.merge!(:as => :array) - encoding = @config[:encoding] - execute("SET NAMES '#{encoding}'", :skip_logging) if encoding - - # By default, MySQL 'where id is null' selects the last inserted id. - # Turn this off. http://dev.rubyonrails.org/ticket/6778 - execute("SET SQL_AUTO_IS_NULL=0", :skip_logging) - end - - # Returns an array of record hashes with the column names as keys and - # column values as values. - def select(sql, name = nil) - execute(sql, name).each(:as => :hash) - end - - def supports_views? - version[0] >= 5 - end - - def version - @version ||= @connection.info[:version].scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map { |v| v.to_i } - end - - def column_for(table_name, column_name) - unless column = columns(table_name).find { |c| c.name == column_name.to_s } - raise "No such column: #{table_name}.#{column_name}" - end - column - end - end - end -end diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index c342824712..1ce86da2fa 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -218,18 +218,20 @@ module ActiveRecord # @brake.touch #=> will also invoke @brake.car.touch and @brake.car.corporation.touch def touch(name = nil) attributes = timestamp_attributes_for_update_in_model - attributes << name if name + unless attributes.blank? + attributes << name if name - current_time = current_time_from_proper_timezone - changes = {} + current_time = current_time_from_proper_timezone + changes = {} - attributes.each do |column| - changes[column.to_s] = write_attribute(column.to_s, current_time) - end + attributes.each do |column| + changes[column.to_s] = write_attribute(column.to_s, current_time) + end - @changed_attributes.except!(*changes.keys) - primary_key = self.class.primary_key - self.class.update_all(changes, { primary_key => self[primary_key] }) == 1 + @changed_attributes.except!(*changes.keys) + primary_key = self.class.primary_key + self.class.update_all(changes, { primary_key => self[primary_key] }) == 1 + end end private diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 03b06205d4..b7ed81c300 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -154,7 +154,7 @@ module ActiveRecord else # Apply limit and order only if they're both present if @limit_value.present? == @order_values.present? - arel.update(@klass.send(:sanitize_sql_for_assignment, updates)) + arel.update(Arel::SqlLiteral.new(@klass.send(:sanitize_sql_for_assignment, updates))) else except(:limit, :order).update_all(updates) end diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 0b5e9b4fb2..8ccc62c9d1 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -121,8 +121,10 @@ module ActiveRecord when Hash, Array, Symbol if array_of_strings?(join) join_string = join.join(' ') - arel = arel.join(join_string) + arel = arel.join(Arel::SqlLiteral.new(join_string)) end + when String + arel = arel.join(Arel::SqlLiteral.new(join)) else arel = arel.join(join) end diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb index c6ff4b39fa..a7583f06cc 100644 --- a/activerecord/lib/active_record/timestamp.rb +++ b/activerecord/lib/active_record/timestamp.rb @@ -61,7 +61,7 @@ module ActiveRecord end def should_record_timestamps? - record_timestamps && !partial_updates? || changed? + record_timestamps && (!partial_updates? || changed?) end def timestamp_attributes_for_update_in_model diff --git a/activerecord/lib/active_record/version.rb b/activerecord/lib/active_record/version.rb index a467ffa960..89eba15be1 100644 --- a/activerecord/lib/active_record/version.rb +++ b/activerecord/lib/active_record/version.rb @@ -1,9 +1,9 @@ module ActiveRecord module VERSION #:nodoc: MAJOR = 3 - MINOR = 0 + MINOR = 1 TINY = 0 - BUILD = "rc" + BUILD = "beta" STRING = [MAJOR, MINOR, TINY, BUILD].join('.') end diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb index 28234ebe49..742513230e 100644 --- a/activerecord/test/cases/associations/belongs_to_associations_test.rb +++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb @@ -75,8 +75,8 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase end def test_eager_loading_with_primary_key - apple = Firm.create("name" => "Apple") - citibank = Client.create("name" => "Citibank", :firm_name => "Apple") + Firm.create("name" => "Apple") + Client.create("name" => "Citibank", :firm_name => "Apple") citibank_result = Client.find(:first, :conditions => {:name => "Citibank"}, :include => :firm_with_primary_key) assert_not_nil citibank_result.instance_variable_get("@firm_with_primary_key") end diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb index ed7d9a782c..c726fedd13 100644 --- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb @@ -109,7 +109,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase record = con.select_rows(sql).last assert_not_nil record[2] assert_not_nil record[3] - if current_adapter?(:Mysql2Adapter) + if current_adapter?(:Mysql2Adapter, :OracleAdapter) assert_match %r{\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}}, record[2].to_s(:db) assert_match %r{\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}}, record[3].to_s(:db) else diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb index a698f23fb6..9b3cc47c79 100644 --- a/activerecord/test/cases/attribute_methods_test.rb +++ b/activerecord/test/cases/attribute_methods_test.rb @@ -120,9 +120,9 @@ class AttributeMethodsTest < ActiveRecord::TestCase assert_equal developer.created_at_before_type_cast, "345643456" assert_equal developer.created_at, nil - developer.created_at = "2010-03-21T21:23:32+01:00" - assert_equal developer.created_at_before_type_cast, "2010-03-21T21:23:32+01:00" - assert_equal developer.created_at, Time.parse("2010-03-21T21:23:32+01:00") + developer.created_at = "2010-03-21 21:23:32" + assert_equal developer.created_at_before_type_cast, "2010-03-21 21:23:32" + assert_equal developer.created_at, Time.parse("2010-03-21 21:23:32") end end end diff --git a/activerecord/test/cases/timestamp_test.rb b/activerecord/test/cases/timestamp_test.rb index fce27d8972..db7accfc40 100644 --- a/activerecord/test/cases/timestamp_test.rb +++ b/activerecord/test/cases/timestamp_test.rb @@ -3,13 +3,15 @@ require 'models/developer' require 'models/owner' require 'models/pet' require 'models/toy' +require 'models/car' class TimestampTest < ActiveRecord::TestCase - fixtures :developers, :owners, :pets, :toys + fixtures :developers, :owners, :pets, :toys, :cars def setup @developer = Developer.first @previously_updated_at = @developer.updated_at + @car = Car.first end def test_saving_a_changed_record_updates_its_timestamp @@ -38,6 +40,16 @@ class TimestampTest < ActiveRecord::TestCase assert_equal previous_salary, @developer.salary end + def test_saving_when_record_timestamps_is_false_doesnt_update_its_timestamp + Developer.record_timestamps = false + @developer.name = "John Smith" + @developer.save! + + assert_equal @previously_updated_at, @developer.updated_at + ensure + Developer.record_timestamps = true + end + def test_touching_a_different_attribute previously_created_at = @developer.created_at @developer.touch(:created_at) @@ -48,6 +60,10 @@ class TimestampTest < ActiveRecord::TestCase assert_not_equal @previously_updated_at, @developer.updated_at end + def test_touch_a_record_without_timestamps + assert_nothing_raised { @car.touch } + end + def test_saving_a_record_with_a_belongs_to_that_specifies_touching_the_parent_should_update_the_parent_updated_at pet = Pet.first owner = pet.owner diff --git a/activerecord/test/connections/native_oracle/connection.rb b/activerecord/test/connections/native_oracle/connection.rb index 00164466f2..9a717018b2 100644 --- a/activerecord/test/connections/native_oracle/connection.rb +++ b/activerecord/test/connections/native_oracle/connection.rb @@ -8,32 +8,22 @@ print "Using Oracle\n" require_dependency 'models/course' require 'logger' -# ActiveRecord::Base.logger = Logger.new STDOUT -# ActiveRecord::Base.logger.level = Logger::WARN ActiveRecord::Base.logger = Logger.new("debug.log") # Set these to your database connection strings -db = ENV['ARUNIT_DB_NAME'] = 'orcl' +ENV['ARUNIT_DB_NAME'] ||= 'orcl' ActiveRecord::Base.configurations = { 'arunit' => { :adapter => 'oracle_enhanced', - :database => db, - :host => "localhost", # used just by JRuby to construct JDBC connect string - # :adapter => "jdbc", - # :driver => "oracle.jdbc.driver.OracleDriver", - # :url => "jdbc:oracle:thin:@localhost:1521:#{db}", + :database => ENV['ARUNIT_DB_NAME'], :username => 'arunit', :password => 'arunit', :emulate_oracle_adapter => true }, 'arunit2' => { :adapter => 'oracle_enhanced', - :database => db, - :host => "localhost", # used just by JRuby to construct JDBC connect string - # :adapter => "jdbc", - # :driver => "oracle.jdbc.driver.OracleDriver", - # :url => "jdbc:oracle:thin:@localhost:1521:#{db}", + :database => ENV['ARUNIT_DB_NAME'], :username => 'arunit2', :password => 'arunit2', :emulate_oracle_adapter => true @@ -43,9 +33,6 @@ ActiveRecord::Base.configurations = { ActiveRecord::Base.establish_connection 'arunit' Course.establish_connection 'arunit2' -# ActiveRecord::Base.connection.execute %q{alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS'} -# ActiveRecord::Base.connection.execute %q{alter session set nls_timestamp_format = 'YYYY-MM-DD HH24:MI:SS'} rescue nil - # for assert_queries test helper ActiveRecord::Base.connection.class.class_eval do IGNORED_SELECT_SQL = [/^select .*nextval/i, /^SAVEPOINT/, /^ROLLBACK TO/, /^\s*select .* from ((all|user)_tab_columns|(all|user)_triggers|(all|user)_constraints)/im] @@ -58,6 +45,3 @@ ActiveRecord::Base.connection.class.class_eval do alias_method_chain :select, :query_record end - -# For JRuby Set default $KCODE to UTF8 -$KCODE = "UTF8" if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby' |