diff options
author | Xavier Noria <fxn@hashref.com> | 2010-03-31 07:47:58 -0700 |
---|---|---|
committer | Xavier Noria <fxn@hashref.com> | 2010-03-31 07:47:58 -0700 |
commit | 824fa10f4d1306cc1e310a7d5de7e95cfb07d6f8 (patch) | |
tree | 1967c2a945d6e131d467fb4f0178a0f60e01ae8a /activerecord | |
parent | 1ed1652bef981d9402797b6cb9f0920a40eea21a (diff) | |
parent | db28d407f76a790a31e27bf51560e23425dd6944 (diff) | |
download | rails-824fa10f4d1306cc1e310a7d5de7e95cfb07d6f8.tar.gz rails-824fa10f4d1306cc1e310a7d5de7e95cfb07d6f8.tar.bz2 rails-824fa10f4d1306cc1e310a7d5de7e95cfb07d6f8.zip |
Merge commit 'rails/master'
Diffstat (limited to 'activerecord')
37 files changed, 270 insertions, 64 deletions
diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index 214ec8c1d8..e379f4f967 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,9 +1,11 @@ -*Edge* +*Rails 3.0.0 [Beta 2] (pending)* + +* To prefix the table names of all models in a module, define self.table_name_prefix on the module. #4032 [Andrew White] * Silenced "SHOW FIELDS" and "SET SQL_AUTO_IS_NULL=0" statements from the MySQL driver to improve log signal to noise ration in development [DHH] -*Rails 3.0 [Beta] (February 4th, 2010)* +*Rails 3.0.0 [Beta 1] (February 4th, 2010)* * PostgreSQLAdapter: set time_zone to UTC when Base.default_timezone == :utc so that Postgres doesn't incorrectly offset-adjust values inserted into TIMESTAMP WITH TIME ZONE columns. #3777 [Jack Christensen] diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb index 5942640c85..8a1aa50e24 100644 --- a/activerecord/lib/active_record.rb +++ b/activerecord/lib/active_record.rb @@ -111,10 +111,10 @@ module ActiveRecord autoload :TestCase autoload :TestFixtures, 'active_record/fixtures' +end - base_hook do - Arel::Table.engine = Arel::Sql::Engine.new(self) - end +ActiveSupport.on_load(:active_record) do + Arel::Table.engine = Arel::Sql::Engine.new(self) end I18n.load_path << File.dirname(__FILE__) + '/active_record/locale/en.yml'
\ No newline at end of file diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb index 6725d4e88b..95bbaf00cf 100644 --- a/activerecord/lib/active_record/association_preload.rb +++ b/activerecord/lib/active_record/association_preload.rb @@ -159,6 +159,11 @@ module ActiveRecord association_proxy.__send__(:set_inverse_instance, associated_record, mapped_record) end end + + id_to_record_map.each do |id, records| + next if seen_keys.include?(id.to_s) + records.each {|record| record.send("set_#{reflection_name}_target", nil) } + end end # Given a collection of ActiveRecord objects, constructs a Hash which maps @@ -324,7 +329,7 @@ module ActiveRecord klass = klass_name.constantize table_name = klass.quoted_table_name - primary_key = klass.primary_key + primary_key = reflection.options[:primary_key] || klass.primary_key column_type = klass.columns.detect{|c| c.name == primary_key}.type ids = id_map.keys.map do |id| diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index d623ddb915..7406daf837 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1,5 +1,6 @@ require 'active_support/core_ext/module/delegation' require 'active_support/core_ext/enumerable' +require 'active_support/core_ext/object/blank' module ActiveRecord class InverseOfAssociationNotFoundError < ActiveRecordError #:nodoc: @@ -85,6 +86,15 @@ module ActiveRecord end end + # This error is raised when trying to destroy a parent instance in a N:1, 1:1 assosications + # (has_many, has_one) when there is at least 1 child assosociated instance. + # ex: if @project.tasks.size > 0, DeleteRestrictionError will be raised when trying to destroy @project + class DeleteRestrictionError < ActiveRecordError #:nodoc: + def initialize(reflection) + super("Cannot delete record because of dependent #{reflection.name}") + end + end + # See ActiveRecord::Associations::ClassMethods for documentation. module Associations # :nodoc: extend ActiveSupport::Concern @@ -830,6 +840,8 @@ module ActiveRecord # objects are deleted *without* calling their +destroy+ method. If set to <tt>:nullify</tt> all associated # objects' foreign keys are set to +NULL+ *without* calling their +save+ callbacks. *Warning:* This option is ignored when also using # the <tt>:through</tt> option. + # the <tt>:through</tt> option. If set to <tt>:restrict</tt> + # this object cannot be deleted if it has any associated object. # [:finder_sql] # Specify a complete SQL statement to fetch the association. This is a good way to go for complex # associations that depend on multiple tables. Note: When this option is used, +find_in_collection+ is _not_ added. @@ -1468,9 +1480,15 @@ module ActiveRecord # Creates before_destroy callback methods that nullify, delete or destroy # has_many associated objects, according to the defined :dependent rule. + # If the association is marked as :dependent => :restrict, create a callback + # that prevents deleting entirely. # # See HasManyAssociation#delete_records. Dependent associations # delete children, otherwise foreign key is set to NULL. + # See HasManyAssociation#delete_records. Dependent associations + # delete children if the option is set to :destroy or :delete_all, set the + # foreign key to NULL if the option is set to :nullify, and do not touch the + # child records if the option is set to :restrict. # # The +extra_conditions+ parameter, which is not used within the main # Active Record codebase, is meant to allow plugins to define extra @@ -1530,14 +1548,24 @@ module ActiveRecord %@#{dependent_conditions}@) end CALLBACK + when :restrict + method_name = "has_many_dependent_restrict_for_#{reflection.name}".to_sym + define_method(method_name) do + unless send(reflection.name).empty? + raise DeleteRestrictionError.new(reflection) + end + end + before_destroy method_name else - raise ArgumentError, "The :dependent option expects either :destroy, :delete_all, or :nullify (#{reflection.options[:dependent].inspect})" + raise ArgumentError, "The :dependent option expects either :destroy, :delete_all, :nullify or :restrict (#{reflection.options[:dependent].inspect})" end end end # Creates before_destroy callback methods that nullify, delete or destroy # has_one associated objects, according to the defined :dependent rule. + # If the association is marked as :dependent => :restrict, create a callback + # that prevents deleting entirely. def configure_dependency_for_has_one(reflection) if reflection.options.include?(:dependent) name = reflection.options[:dependent] @@ -1558,8 +1586,16 @@ module ActiveRecord association.update_attribute(#{reflection.primary_key_name.inspect}, nil) if association end eoruby + when :restrict + method_name = "has_one_dependent_restrict_for_#{reflection.name}".to_sym + define_method(method_name) do + unless send(reflection.name).nil? + raise DeleteRestrictionError.new(reflection) + end + end + before_destroy method_name else - raise ArgumentError, "The :dependent option expects either :destroy, :delete or :nullify (#{reflection.options[:dependent].inspect})" + raise ArgumentError, "The :dependent option expects either :destroy, :delete, :nullify or :restrict (#{reflection.options[:dependent].inspect})" end before_destroy method_name @@ -1995,7 +2031,7 @@ module ActiveRecord [aliased_table[foreign_key].eq(parent_table[reflection.options[:primary_key] || parent.primary_key])] end when :belongs_to - [aliased_table[reflection.klass.primary_key].eq(parent_table[options[:foreign_key] || reflection.primary_key_name])] + [aliased_table[options[:primary_key] || reflection.klass.primary_key].eq(parent_table[options[:foreign_key] || reflection.primary_key_name])] end unless klass.descends_from_active_record? 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 bd2acd4340..5338bb099d 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,5 @@ require "active_record/associations/through_association_scope" +require 'active_support/core_ext/object/blank' module ActiveRecord module Associations diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb index a8698a2f5a..435aea9b09 100644 --- a/activerecord/lib/active_record/attribute_methods/dirty.rb +++ b/activerecord/lib/active_record/attribute_methods/dirty.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/object/blank' + module ActiveRecord module AttributeMethods module Dirty diff --git a/activerecord/lib/active_record/attribute_methods/primary_key.rb b/activerecord/lib/active_record/attribute_methods/primary_key.rb index 095814b635..411330dda2 100644 --- a/activerecord/lib/active_record/attribute_methods/primary_key.rb +++ b/activerecord/lib/active_record/attribute_methods/primary_key.rb @@ -3,6 +3,12 @@ module ActiveRecord module PrimaryKey extend ActiveSupport::Concern + # Returns this record's primary key value wrapped in an Array + # or nil if the record is a new_record? + def to_key + new_record? ? nil : [ send(self.class.primary_key) ] + end + module ClassMethods # Defines the primary key field -- can be overridden in subclasses. Overwriting will negate any effect of the # primary_key_prefix_type setting, though. @@ -39,22 +45,6 @@ module ActiveRecord end alias :primary_key= :set_primary_key end - - module InstanceMethods - - # Returns this record's primary key value wrapped in an Array - # or nil if the record is a new_record? - # This is done to comply with the AMo interface that expects - # every AMo compliant object to respond_to?(:to_key) and return - # an Enumerable object from that call, or nil if new_record? - # This method also takes custom primary keys specified via - # the +set_primary_key+ into account. - def to_key - new_record? ? nil : [ self.primary_key ] - end - - end - end end end diff --git a/activerecord/lib/active_record/attribute_methods/query.rb b/activerecord/lib/active_record/attribute_methods/query.rb index a949d80120..948809c65a 100644 --- a/activerecord/lib/active_record/attribute_methods/query.rb +++ b/activerecord/lib/active_record/attribute_methods/query.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/object/blank' + module ActiveRecord module AttributeMethods module Query diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 1c93346a52..37b379af9e 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -13,6 +13,8 @@ require 'active_support/core_ext/hash/slice' require 'active_support/core_ext/string/behavior' require 'active_support/core_ext/object/singleton_class' require 'active_support/core_ext/module/delegation' +require 'active_support/core_ext/object/duplicable' +require 'active_support/core_ext/object/blank' require 'arel' require 'active_record/errors' @@ -336,6 +338,9 @@ module ActiveRecord #:nodoc: # Accessor for the name of the prefix string to prepend to every table name. So if set to "basecamp_", all # table names will be named like "basecamp_projects", "basecamp_people", etc. This is a convenient way of creating a namespace # for tables in a shared database. By default, the prefix is the empty string. + # + # If you are organising your models within modules you can add a prefix to the models within a namespace by defining + # a singleton method in the parent module called table_name_prefix which returns your chosen prefix. cattr_accessor :table_name_prefix, :instance_writer => false @@table_name_prefix = "" @@ -763,7 +768,7 @@ module ActiveRecord #:nodoc: contained = contained.singularize if parent.pluralize_table_names contained << '_' end - name = "#{table_name_prefix}#{contained}#{undecorated_table_name(base.name)}#{table_name_suffix}" + name = "#{full_table_name_prefix}#{contained}#{undecorated_table_name(base.name)}#{table_name_suffix}" end @quoted_table_name = nil @@ -771,6 +776,10 @@ module ActiveRecord #:nodoc: name end + def full_table_name_prefix #:nodoc: + (parents.detect{ |p| p.respond_to?(:table_name_prefix) } || self).table_name_prefix + end + # Defines the column name for use with single table inheritance # -- can be set in subclasses like so: self.inheritance_column = "type_id" def inheritance_column @@ -1288,7 +1297,7 @@ module ActiveRecord #:nodoc: # <tt>options</tt> argument is the same as in find. # # class Person < ActiveRecord::Base - # default_scope :order => 'last_name, first_name' + # default_scope order('last_name, first_name') # end def default_scope(options = {}) self.default_scoping << construct_finder_arel(options) @@ -2214,6 +2223,7 @@ module ActiveRecord #:nodoc: extend QueryCache::ClassMethods extend ActiveSupport::Benchmarkable + include ActiveModel::Conversion include Validations include Locking::Optimistic, Locking::Pessimistic include AttributeMethods @@ -2223,12 +2233,10 @@ module ActiveRecord #:nodoc: include AttributeMethods::Dirty include Callbacks, ActiveModel::Observing, Timestamp include Associations, AssociationPreload, NamedScope - include ActiveModel::Conversion # AutosaveAssociation needs to be included before Transactions, because we want # #save_with_autosave_associations to be wrapped inside a transaction. include AutosaveAssociation, NestedAttributes - include Aggregations, Transactions, Reflection, Serialization NilClass.add_whiner(self) if NilClass.respond_to?(:add_whiner) @@ -2237,4 +2245,4 @@ end # TODO: Remove this and make it work with LAZY flag require 'active_record/connection_adapters/abstract_adapter' -ActiveRecord.run_base_hooks(ActiveRecord::Base) +ActiveSupport.run_load_hooks(:active_record, ActiveRecord::Base) diff --git a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb index 020acbbe5a..1e095110f2 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/object/duplicable' + module ActiveRecord module ConnectionAdapters # :nodoc: module QueryCache diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb index 64faaef4a0..046825d43f 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -1,3 +1,4 @@ +require 'active_support/core_ext/object/blank' require 'date' require 'set' require 'bigdecimal' diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index 521bd810d0..55d9d20bb5 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -1,5 +1,6 @@ require 'active_record/connection_adapters/abstract_adapter' require 'active_support/core_ext/kernel/requires' +require 'active_support/core_ext/object/blank' require 'set' module MysqlCompat #:nodoc: diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 31d5266da8..a6042e1382 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -1,5 +1,6 @@ require 'active_record/connection_adapters/abstract_adapter' require 'active_support/core_ext/kernel/requires' +require 'active_support/core_ext/object/blank' begin require_library_or_gem 'pg' @@ -299,7 +300,7 @@ module ActiveRecord # QUOTING ================================================== # Escapes binary strings for bytea input to the database. - def escape_bytea(value) + def escape_bytea(original_value) if @connection.respond_to?(:escape_bytea) self.class.instance_eval do define_method(:escape_bytea) do |value| @@ -323,13 +324,13 @@ module ActiveRecord end end end - escape_bytea(value) + escape_bytea(original_value) end # Unescapes bytea output from a database to the binary string it represents. # NOTE: This is NOT an inverse of escape_bytea! This is only to be used # on escaped binary output from database drive. - def unescape_bytea(value) + def unescape_bytea(original_value) # In each case, check if the value actually is escaped PostgreSQL bytea output # or an unescaped Active Record attribute that was just written. if PGconn.respond_to?(:unescape_bytea) @@ -369,7 +370,7 @@ module ActiveRecord end end end - unescape_bytea(value) + unescape_bytea(original_value) end # Quotes PostgreSQL-specific data types for SQL input. @@ -394,7 +395,7 @@ module ActiveRecord end # Quotes strings for use in SQL input in the postgres driver for better performance. - def quote_string(s) #:nodoc: + def quote_string(original_value) #:nodoc: if @connection.respond_to?(:escape) self.class.instance_eval do define_method(:quote_string) do |s| @@ -414,7 +415,7 @@ module ActiveRecord remove_method(:quote_string) end end - quote_string(s) + quote_string(original_value) end # Checks the following cases: diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index 79f70f07cd..22f0e60083 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -4,6 +4,7 @@ require 'csv' require 'zlib' require 'active_support/dependencies' require 'active_support/core_ext/logger' +require 'active_support/core_ext/object/blank' if RUBY_VERSION < '1.9' module YAML #:nodoc: diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 394e1587e1..9abf979cd0 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -1,6 +1,7 @@ require 'active_support/core_ext/array' require 'active_support/core_ext/hash/except' require 'active_support/core_ext/object/singleton_class' +require 'active_support/core_ext/object/blank' module ActiveRecord module NamedScope @@ -101,7 +102,8 @@ module ActiveRecord name = name.to_sym if !scopes[name] && respond_to?(name, true) - raise ArgumentError, "Cannot define scope :#{name} because #{self.name}.#{name} method already exists." + logger.warn "Creating scope :#{name}. " \ + "Overwriting existing method #{self.name}.#{name}." end scopes[name] = lambda do |parent_scope, *args| @@ -165,7 +167,14 @@ module ActiveRecord end def ==(other) - other.respond_to?(:to_ary) ? to_a == other.to_a : false + case other + when Scope + to_sql == other.to_sql + when Relation + other == self + when Array + to_a == other.to_a + end end private diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index 76ec7eb681..ee2d420194 100644 --- a/activerecord/lib/active_record/nested_attributes.rb +++ b/activerecord/lib/active_record/nested_attributes.rb @@ -1,5 +1,6 @@ require 'active_support/core_ext/hash/except' require 'active_support/core_ext/object/try' +require 'active_support/core_ext/object/blank' module ActiveRecord module NestedAttributes #:nodoc: diff --git a/activerecord/lib/active_record/query_cache.rb b/activerecord/lib/active_record/query_cache.rb index eb92bc2545..093c6c1e55 100644 --- a/activerecord/lib/active_record/query_cache.rb +++ b/activerecord/lib/active_record/query_cache.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/object/blank' + module ActiveRecord class QueryCache module ClassMethods diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index de47461c73..04c4a9c153 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -23,18 +23,18 @@ module ActiveRecord log_subscriber :active_record, ActiveRecord::Railties::LogSubscriber.new initializer "active_record.initialize_timezone" do - ActiveRecord.base_hook do + ActiveSupport.on_load(:active_record) do self.time_zone_aware_attributes = true self.default_timezone = :utc end end initializer "active_record.logger" do - ActiveRecord.base_hook { self.logger ||= ::Rails.logger } + ActiveSupport.on_load(:active_record) { self.logger ||= ::Rails.logger } end initializer "active_record.set_configs" do |app| - ActiveRecord.base_hook do + ActiveSupport.on_load(:active_record) do app.config.active_record.each do |k,v| send "#{k}=", v end @@ -44,7 +44,7 @@ module ActiveRecord # This sets the database configuration from Configuration#database_configuration # and then establishes the connection. initializer "active_record.initialize_database" do |app| - ActiveRecord.base_hook do + ActiveSupport.on_load(:active_record) do self.configurations = app.config.database_configuration establish_connection end @@ -53,7 +53,7 @@ module ActiveRecord # Expose database runtime to controller for logging. initializer "active_record.log_runtime" do |app| require "active_record/railties/controller_runtime" - ActionController.base_hook do + ActiveSupport.on_load(:action_controller) do include ActiveRecord::Railties::ControllerRuntime end end @@ -71,9 +71,9 @@ module ActiveRecord end initializer "active_record.load_observers" do - ActiveRecord.base_hook { instantiate_observers } + ActiveSupport.on_load(:active_record) { instantiate_observers } - ActiveRecord.base_hook do + ActiveSupport.on_load(:active_record) do ActionDispatch::Callbacks.to_prepare(:activerecord_instantiate_observers) do ActiveRecord::Base.instantiate_observers end @@ -81,7 +81,7 @@ module ActiveRecord end initializer "active_record.set_dispatch_hooks", :before => :set_clear_dependencies_hook do |app| - ActiveRecord.base_hook do + ActiveSupport.on_load(:active_record) do unless app.config.cache_classes ActionDispatch::Callbacks.after do ActiveRecord::Base.reset_subclasses diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index a20c152eeb..8577ec58f7 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/object/blank' + module ActiveRecord class Relation JoinOperation = Struct.new(:relation, :join_class, :on) diff --git a/activerecord/lib/active_record/relation/batches.rb b/activerecord/lib/active_record/relation/batches.rb index 4a260d4caa..1c61e7d450 100644 --- a/activerecord/lib/active_record/relation/batches.rb +++ b/activerecord/lib/active_record/relation/batches.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/object/blank' + module ActiveRecord module Batches # :nodoc: # Yields each record that was found by the find +options+. The find is @@ -48,6 +50,10 @@ module ActiveRecord def find_in_batches(options = {}) relation = self + if orders.present? || taken.present? + ActiveRecord::Base.logger.warn("Scoped order and limit are ignored, it's forced to be batch order and batch size") + end + if (finder_options = options.except(:start, :batch_size)).present? raise "You can't specify an order, it's forced to be #{batch_order}" if options[:order].present? raise "You can't specify a limit, it's forced to be the batch_size" if options[:limit].present? @@ -75,4 +81,4 @@ module ActiveRecord "#{@klass.table_name}.#{@klass.primary_key} ASC" end end -end
\ No newline at end of file +end diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index e77424a64b..a5ea6e7e3a 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/object/blank' + module ActiveRecord module Calculations # Count operates using three different approaches. diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index c1cce679b6..37aaac0894 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/object/blank' + module ActiveRecord module FinderMethods # Find operates with four different retrieval approaches: diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb index 7e83eccbb5..711df16bf1 100644 --- a/activerecord/lib/active_record/relation/predicate_builder.rb +++ b/activerecord/lib/active_record/relation/predicate_builder.rb @@ -27,12 +27,7 @@ module ActiveRecord values = value.to_a attribute.in(values) when Range - # TODO : Arel should handle ranges with excluded end. - if value.exclude_end? - [attribute.gteq(value.begin), attribute.lt(value.end)] - else - attribute.in(value) - end + attribute.in(value) else attribute.eq(value) end diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 0250e739b8..e224781016 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/object/blank' + module ActiveRecord module QueryMethods extend ActiveSupport::Concern diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb index 2841ff1239..a17de1bdbb 100644 --- a/activerecord/lib/active_record/relation/spawn_methods.rb +++ b/activerecord/lib/active_record/relation/spawn_methods.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/object/blank' + module ActiveRecord module SpawnMethods def merge(r) diff --git a/activerecord/lib/active_record/schema.rb b/activerecord/lib/active_record/schema.rb index a996a0ebac..a833356d15 100644 --- a/activerecord/lib/active_record/schema.rb +++ b/activerecord/lib/active_record/schema.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/object/blank' + module ActiveRecord # Allows programmers to programmatically define a schema in a portable # DSL. This means you can define tables, indexes, etc. without using SQL diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb index e28808df98..4806fa0ecc 100644 --- a/activerecord/lib/active_record/validations/uniqueness.rb +++ b/activerecord/lib/active_record/validations/uniqueness.rb @@ -67,13 +67,11 @@ module ActiveRecord if value.nil? || (options[:case_sensitive] || !column.text?) sql = "#{sql_attribute} #{operator}" - params = [value] else - sql = "LOWER(#{sql_attribute}) #{operator}" - params = [value.mb_chars.downcase] + sql = "LOWER(#{sql_attribute}) = LOWER(?)" end - [sql, params] + [sql, [value]] end end diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb index 41a23d7f61..62121b93cb 100644 --- a/activerecord/test/cases/associations/belongs_to_associations_test.rb +++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb @@ -32,6 +32,17 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase assert_equal companies(:first_firm).name, client.firm_with_primary_key.name end + def test_belongs_to_with_primary_key_joins_on_correct_column + sql = Client.joins(:firm_with_primary_key).to_sql + if current_adapter?(:MysqlAdapter) + assert_no_match /`firm_with_primary_keys_companies`\.`id`/, sql + assert_match /`firm_with_primary_keys_companies`\.`name`/, sql + else + assert_no_match /"firm_with_primary_keys_companies"\."id"/, sql + assert_match /"firm_with_primary_keys_companies"\."name"/, sql + end + end + def test_proxy_assignment account = Account.find(1) assert_nothing_raised { account.firm = account.firm } @@ -61,6 +72,13 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase assert_equal apple.name, citibank.firm_name end + def test_eager_loading_with_primary_key + apple = Firm.create("name" => "Apple") + citibank = 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 + def test_no_unexpected_aliasing first_firm = companies(:first_firm) another_firm = companies(:another_firm) @@ -439,9 +457,15 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase assert_equal [author_address.id], AuthorAddress.destroyed_author_address_ids end - def test_invalid_belongs_to_dependent_option_raises_exception + def test_invalid_belongs_to_dependent_option_nullify_raises_exception assert_raise ArgumentError do Author.belongs_to :special_author_address, :dependent => :nullify end end + + def test_invalid_belongs_to_dependent_option_restrict_raises_exception + assert_raise ArgumentError do + Author.belongs_to :special_author_address, :dependent => :restrict + end + end end diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb index 42a891bc3b..79e5ecf4ce 100644 --- a/activerecord/test/cases/associations/eager_test.rb +++ b/activerecord/test/cases/associations/eager_test.rb @@ -833,4 +833,10 @@ class EagerAssociationTest < ActiveRecord::TestCase end end + def test_preloading_empty_polymorphic_parent + t = Tagging.create!(:taggable_type => 'Post', :taggable_id => Post.maximum(:id) + 1, :tag => tags(:general)) + + assert_queries(2) { @tagging = Tagging.preload(:taggable).find(t.id) } + assert_no_queries { assert ! @tagging.taggable } + end end diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 54624e79ce..c1e539d573 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -836,6 +836,14 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert_equal num_accounts, Account.count end + def test_restrict + firm = RestrictedFirm.new(:name => 'restrict') + firm.save! + child_firm = firm.companies.create(:name => 'child') + assert !firm.companies.empty? + assert_raise(ActiveRecord::DeleteRestrictionError) { firm.destroy } + end + def test_included_in_collection assert companies(:first_firm).clients.include?(Client.find(2)) end diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb index 7372f2da1b..8f5540950e 100644 --- a/activerecord/test/cases/associations/has_one_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_associations_test.rb @@ -177,7 +177,15 @@ class HasOneAssociationsTest < ActiveRecord::TestCase assert_nothing_raised { firm.destroy } end - def test_succesful_build_association + def test_dependence_with_restrict + firm = RestrictedFirm.new(:name => 'restrict') + firm.save! + account = firm.create_account(:credit_limit => 10) + assert !firm.account.nil? + assert_raise(ActiveRecord::DeleteRestrictionError) { firm.destroy } + end + + def test_successful_build_association firm = Firm.new("name" => "GlobalMegaCorp") firm.save diff --git a/activerecord/test/cases/batches_test.rb b/activerecord/test/cases/batches_test.rb index e417d8a803..83deabb5b7 100644 --- a/activerecord/test/cases/batches_test.rb +++ b/activerecord/test/cases/batches_test.rb @@ -8,7 +8,7 @@ class EachTest < ActiveRecord::TestCase @posts = Post.order("id asc") @total = Post.count end - + def test_each_should_excecute_one_query_per_batch assert_queries(Post.count + 1) do Post.find_each(:batch_size => 1) do |post| @@ -28,7 +28,17 @@ class EachTest < ActiveRecord::TestCase Post.find_each(:limit => 1) { |post| post } end end - + + def test_warn_if_limit_scope_is_set + ActiveRecord::Base.logger.expects(:warn) + Post.limit(1).find_each { |post| post } + end + + def test_warn_if_order_scope_is_set + ActiveRecord::Base.logger.expects(:warn) + Post.order("title").find_each { |post| post } + end + def test_find_in_batches_should_return_batches assert_queries(Post.count + 1) do Post.find_in_batches(:batch_size => 1) do |batch| @@ -58,4 +68,4 @@ class EachTest < ActiveRecord::TestCase Post.find_in_batches(:batch_size => post_count + 1) {|batch| assert_kind_of Array, batch } end end -end
\ No newline at end of file +end diff --git a/activerecord/test/cases/modules_test.rb b/activerecord/test/cases/modules_test.rb index d781a229f4..7209966bf8 100644 --- a/activerecord/test/cases/modules_test.rb +++ b/activerecord/test/cases/modules_test.rb @@ -82,4 +82,32 @@ class ModulesTest < ActiveRecord::TestCase end end end + + def test_module_table_name_prefix + assert_equal 'prefixed_companies', MyApplication::Business::Prefixed::Company.table_name, 'inferred table_name for ActiveRecord model in module with table_name_prefix' + assert_equal 'prefixed_companies', MyApplication::Business::Prefixed::Nested::Company.table_name, 'table_name for ActiveRecord model in nested module with a parent table_name_prefix' + assert_equal 'companies', MyApplication::Business::Prefixed::Firm.table_name, 'explicit table_name for ActiveRecord model in module with table_name_prefix should not be prefixed' + end + + def test_module_table_name_prefix_with_global_prefix + classes = [ MyApplication::Business::Company, + MyApplication::Business::Firm, + MyApplication::Business::Client, + MyApplication::Business::Client::Contact, + MyApplication::Business::Developer, + MyApplication::Business::Project, + MyApplication::Business::Prefixed::Company, + MyApplication::Business::Prefixed::Nested::Company, + MyApplication::Billing::Account ] + + ActiveRecord::Base.table_name_prefix = 'global_' + classes.each(&:reset_table_name) + assert_equal 'global_companies', MyApplication::Business::Company.table_name, 'inferred table_name for ActiveRecord model in module without table_name_prefix' + assert_equal 'prefixed_companies', MyApplication::Business::Prefixed::Company.table_name, 'inferred table_name for ActiveRecord model in module with table_name_prefix' + assert_equal 'prefixed_companies', MyApplication::Business::Prefixed::Nested::Company.table_name, 'table_name for ActiveRecord model in nested module with a parent table_name_prefix' + assert_equal 'companies', MyApplication::Business::Prefixed::Firm.table_name, 'explicit table_name for ActiveRecord model in module with table_name_prefix should not be prefixed' + ensure + ActiveRecord::Base.table_name_prefix = '' + classes.each(&:reset_table_name) + end end diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb index 6c2b4fa3a7..2396ca10b0 100644 --- a/activerecord/test/cases/named_scope_test.rb +++ b/activerecord/test/cases/named_scope_test.rb @@ -371,8 +371,21 @@ class NamedScopeTest < ActiveRecord::TestCase end def test_named_scopes_with_reserved_names - [:where, :with_scope].each do |protected_method| - assert_raises(ArgumentError) { Topic.scope protected_method } + class << Topic + def public_method; end + public :public_method + + def protected_method; end + protected :protected_method + + def private_method; end + private :private_method + end + + [:public_method, :protected_method, :private_method].each do |reserved_method| + assert Topic.respond_to?(reserved_method, true) + ActiveRecord::Base.logger.expects(:warn) + Topic.scope(reserved_method) end end @@ -394,6 +407,12 @@ class NamedScopeTest < ActiveRecord::TestCase assert_equal topics(:second), approved[0] assert approved.loaded? end + + def test_nested_named_scopes_queries_size + assert_queries(1) do + Topic.approved.by_lifo.replied.written_before(Time.now).all + end + end end class DynamicScopeMatchTest < ActiveRecord::TestCase diff --git a/activerecord/test/cases/pk_test.rb b/activerecord/test/cases/pk_test.rb index 5bba1d5625..33ad5992de 100644 --- a/activerecord/test/cases/pk_test.rb +++ b/activerecord/test/cases/pk_test.rb @@ -23,6 +23,12 @@ class PrimaryKeysTest < ActiveRecord::TestCase assert_equal keyboard.to_key, [keyboard.id] end + def test_to_key_with_primary_key_after_destroy + topic = Topic.find(1) + topic.destroy + assert_equal topic.to_key, [1] + end + def test_integer_key topic = Topic.find(1) assert_equal(topics(:first).author_name, topic.author_name) diff --git a/activerecord/test/models/company.rb b/activerecord/test/models/company.rb index f31d5f87e5..be6dd71e3b 100644 --- a/activerecord/test/models/company.rb +++ b/activerecord/test/models/company.rb @@ -95,6 +95,11 @@ class DependentFirm < Company has_many :companies, :foreign_key => 'client_of', :dependent => :nullify end +class RestrictedFirm < Company + has_one :account, :foreign_key => "firm_id", :dependent => :restrict, :order => "id" + has_many :companies, :foreign_key => 'client_of', :order => "id", :dependent => :restrict +end + class Client < Company belongs_to :firm, :foreign_key => "client_of" belongs_to :firm_with_basic_id, :class_name => "Firm", :foreign_key => "firm_id" diff --git a/activerecord/test/models/company_in_module.rb b/activerecord/test/models/company_in_module.rb index cdda7a44d4..83d71b6909 100644 --- a/activerecord/test/models/company_in_module.rb +++ b/activerecord/test/models/company_in_module.rb @@ -32,6 +32,23 @@ module MyApplication has_and_belongs_to_many :developers end + module Prefixed + def self.table_name_prefix + 'prefixed_' + end + + class Company < ActiveRecord::Base + end + + class Firm < Company + self.table_name = 'companies' + end + + module Nested + class Company < ActiveRecord::Base + end + end + end end module Billing |