diff options
Diffstat (limited to 'activerecord')
26 files changed, 287 insertions, 94 deletions
diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index e0625c3dbb..8ecef6574f 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,4 +1,4 @@ -*Rails 3.0.0 [Beta 2] (pending)* +*Rails 3.0.0 [beta 3] (April 13th, 2010)* * Add Relation extensions. [Pratik Naik] diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 20a8754b7c..d94cc03938 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/array/wrap' require 'active_support/core_ext/enumerable' +require 'active_support/core_ext/module/delegation' require 'active_support/core_ext/object/blank' module ActiveRecord @@ -1707,9 +1708,9 @@ module ActiveRecord silence_warnings do self.parent.const_set(extension_module_name, Module.new(&block_extension)) end - Array(extensions).push("#{self.parent}::#{extension_module_name}".constantize) + Array.wrap(extensions).push("#{self.parent}::#{extension_module_name}".constantize) else - Array(extensions) + Array.wrap(extensions) end end diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index b808f8c306..0dfd966466 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -1,4 +1,5 @@ require 'set' +require 'active_support/core_ext/array/wrap' module ActiveRecord module Associations @@ -98,7 +99,7 @@ module ActiveRecord if @target.is_a?(Array) @target.to_ary else - Array(@target) + Array.wrap(@target) end end alias_method :to_a, :to_ary @@ -450,6 +451,16 @@ module ActiveRecord records end + def add_record_to_target_with_callbacks(record) + callback(:before_add, record) + yield(record) if block_given? + @target ||= [] unless loaded? + @target << record unless @reflection.options[:uniq] && @target.include?(record) + callback(:after_add, record) + set_inverse_instance(record, @owner) + record + end + private def create_record(attrs) attrs.update(@reflection.options[:conditions]) if @reflection.options[:conditions].is_a?(Hash) @@ -474,16 +485,6 @@ module ActiveRecord end end - def add_record_to_target_with_callbacks(record) - callback(:before_add, record) - yield(record) if block_given? - @target ||= [] unless loaded? - @target << record unless @reflection.options[:uniq] && @target.include?(record) - callback(:after_add, record) - set_inverse_instance(record, @owner) - record - end - def remove_records(*records) records = flatten_deeper(records) records.each { |record| raise_on_type_mismatch(record) } diff --git a/activerecord/lib/active_record/associations/association_proxy.rb b/activerecord/lib/active_record/associations/association_proxy.rb index 4fb1df3ab9..b9d0fe3abe 100644 --- a/activerecord/lib/active_record/associations/association_proxy.rb +++ b/activerecord/lib/active_record/associations/association_proxy.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/array/wrap' + module ActiveRecord module Associations # This is the root class of all association proxies: @@ -55,7 +57,7 @@ module ActiveRecord @owner, @reflection = owner, reflection @updated = false reflection.check_validity! - Array(reflection.options[:extend]).each { |ext| proxy_extend(ext) } + Array.wrap(reflection.options[:extend]).each { |ext| proxy_extend(ext) } reset end diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index d544c48a4c..fd24dcddc3 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -3,6 +3,7 @@ require 'set' require 'active_support/benchmarkable' require 'active_support/dependencies' require 'active_support/time' +require 'active_support/core_ext/class/attribute' require 'active_support/core_ext/class/attribute_accessors' require 'active_support/core_ext/class/delegating_attributes' require 'active_support/core_ext/class/inheritable_attributes' @@ -341,15 +342,15 @@ module ActiveRecord #:nodoc: # # 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 = "" + class_attribute :table_name_prefix, :instance_writer => false + self.table_name_prefix = "" ## # :singleton-method: # Works like +table_name_prefix+, but appends instead of prepends (set to "_basecamp" gives "projects_basecamp", # "people_basecamp"). By default, the suffix is the empty string. - cattr_accessor :table_name_suffix, :instance_writer => false - @@table_name_suffix = "" + class_attribute :table_name_suffix, :instance_writer => false + self.table_name_suffix = "" ## # :singleton-method: @@ -1079,16 +1080,6 @@ module ActiveRecord #:nodoc: end end - # Nest the type name in the same module as this class. - # Bar is "MyApp::Business::Bar" relative to MyApp::Business::Foo - def type_name_with_module(type_name) - if store_full_sti_class - type_name - else - (/^::/ =~ type_name) ? type_name : "#{parent.name}::#{type_name}" - end - end - def construct_finder_arel(options = {}, scope = nil) relation = options.is_a?(Hash) ? unscoped.apply_finder_options(options) : unscoped.merge(options) relation = scope.merge(relation) if scope @@ -1284,11 +1275,14 @@ module ActiveRecord #:nodoc: with_scope(method_scoping, :overwrite, &block) end - def subclasses #:nodoc: + # Returns a list of all subclasses of this class, meaning all descendants. + def subclasses @@subclasses[self] ||= [] - @@subclasses[self] + extra = @@subclasses[self].inject([]) {|list, subclass| list + subclass.subclasses } + @@subclasses[self] + @@subclasses[self].inject([]) {|list, subclass| list + subclass.subclasses } end + public :subclasses + # Sets the default options for the model. The format of the # <tt>options</tt> argument is the same as in find. # @@ -1311,13 +1305,26 @@ module ActiveRecord #:nodoc: # Returns the class type of the record using the current module as a prefix. So descendants of # MyApp::Business::Account would appear as MyApp::Business::AccountSubclass. def compute_type(type_name) - modularized_name = type_name_with_module(type_name) - silence_warnings do - begin - class_eval(modularized_name, __FILE__) - rescue NameError - class_eval(type_name, __FILE__) + if type_name.match(/^::/) + # If the type is prefixed with a scope operator then we assume that + # the type_name is an absolute reference. + type_name.constantize + else + # Build a list of candidates to search for + candidates = [] + name.scan(/::|$/) { candidates.unshift "#{$`}::#{type_name}" } + candidates << type_name + + candidates.each do |candidate| + begin + constant = candidate.constantize + return constant if candidate == constant.to_s + rescue NameError + rescue ArgumentError + end end + + raise NameError, "uninitialized constant #{candidates.first}" end end diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb index add5d99ca6..98c14e6eb0 100644 --- a/activerecord/lib/active_record/callbacks.rb +++ b/activerecord/lib/active_record/callbacks.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/array/wrap' + module ActiveRecord # Callbacks are hooks into the lifecycle of an Active Record object that allow you to trigger logic # before or after an alteration of the object state. This can be used to make sure that associated and @@ -250,7 +252,7 @@ module ActiveRecord def before_validation(*args, &block) options = args.last if options.is_a?(Hash) && options[:on] - options[:if] = Array(options[:if]) + options[:if] = Array.wrap(options[:if]) options[:if] << "@_on_validate == :#{options[:on]}" end set_callback(:validation, :before, *args, &block) @@ -259,7 +261,7 @@ module ActiveRecord def after_validation(*args, &block) options = args.extract_options! options[:prepend] = true - options[:if] = Array(options[:if]) + options[:if] = Array.wrap(options[:if]) options[:if] << "!halted && value != false" options[:if] << "@_on_validate == :#{options[:on]}" if options[:on] set_callback(:validation, :after, *(args << options), &block) 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 e5d100b51b..3c560903f7 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb @@ -32,7 +32,6 @@ module ActiveRecord # Enable the query cache within the block. def cache old, @query_cache_enabled = @query_cache_enabled, true - @query_cache ||= {} yield ensure clear_query_cache @@ -54,7 +53,7 @@ module ActiveRecord # the same SQL query and repeatedly return the same result each time, silently # undermining the randomness you were expecting. def clear_query_cache - @query_cache.clear if @query_cache + @query_cache.clear end def select_all_with_query_cache(*args) diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index 6d4ab501fa..e8cba1bd41 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/array/wrap' + module ActiveRecord module ConnectionAdapters # :nodoc: module SchemaStatements @@ -267,7 +269,7 @@ module ActiveRecord # generates # CREATE UNIQUE INDEX by_branch_party ON accounts(branch_id, party_id) def add_index(table_name, column_name, options = {}) - column_names = Array(column_name) + column_names = Array.wrap(column_name) index_name = index_name(table_name, :column => column_names) if Hash === options # legacy support, since this param was a string @@ -297,7 +299,7 @@ module ActiveRecord def index_name(table_name, options) #:nodoc: if Hash === options # legacy support if options[:column] - "index_#{table_name}_on_#{Array(options[:column]) * '_and_'}" + "index_#{table_name}_on_#{Array.wrap(options[:column]) * '_and_'}" elsif options[:name] options[:name] else diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index 6ffffc8654..578297029b 100755 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -41,6 +41,7 @@ module ActiveRecord @connection, @logger = connection, logger @runtime = 0 @query_cache_enabled = false + @query_cache = {} end # Returns the human-readable name of the adapter. Use mixed case - one diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 68ee88bba4..ceb1adc9e0 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -54,6 +54,12 @@ module ActiveRecord super(name, self.class.extract_value_from_default(default), sql_type, null) end + # :stopdoc: + class << self + attr_accessor :money_precision + end + # :startdoc: + private def extract_limit(sql_type) case sql_type @@ -71,9 +77,11 @@ module ActiveRecord # Extracts the precision from PostgreSQL-specific data types. def extract_precision(sql_type) - # Actual code is defined dynamically in PostgreSQLAdapter.connect - # depending on the server specifics - super + if sql_type == 'money' + self.class.money_precision + else + super + end end # Maps PostgreSQL-specific data types to logical Rails types. @@ -83,18 +91,18 @@ module ActiveRecord when /^(?:real|double precision)$/ :float # Monetary types - when /^money$/ + when 'money' :decimal # Character types when /^(?:character varying|bpchar)(?:\(\d+\))?$/ :string # Binary data types - when /^bytea$/ + when 'bytea' :binary # Date/time types when /^timestamp with(?:out)? time zone$/ :datetime - when /^interval$/ + when 'interval' :string # Geometric types when /^(?:point|line|lseg|box|"?path"?|polygon|circle)$/ @@ -106,16 +114,16 @@ module ActiveRecord when /^bit(?: varying)?(?:\(\d+\))?$/ :string # XML type - when /^xml$/ + when 'xml' :xml # Arrays when /^\D+\[\]$/ :string # Object identifier types - when /^oid$/ + when 'oid' :integer # UUID type - when /^uuid$/ + when 'uuid' :string # Small and big integer types when /^(?:small|big)int$/ @@ -383,9 +391,9 @@ module ActiveRecord def quote(value, column = nil) #:nodoc: if value.kind_of?(String) && column && column.type == :binary "#{quoted_string_prefix}'#{escape_bytea(value)}'" - elsif value.kind_of?(String) && column && column.sql_type =~ /^xml$/ + elsif value.kind_of?(String) && column && column.sql_type == 'xml' "xml E'#{quote_string(value)}'" - elsif value.kind_of?(Numeric) && column && column.sql_type =~ /^money$/ + elsif value.kind_of?(Numeric) && column && column.sql_type == 'money' # Not truly string input, so doesn't require (or allow) escape string syntax. "'#{value.to_s}'" elsif value.kind_of?(String) && column && column.sql_type =~ /^bit/ @@ -662,8 +670,30 @@ module ActiveRecord def tables(name = nil) query(<<-SQL, name).map { |row| row[0] } SELECT tablename + FROM pg_tables + WHERE schemaname = ANY (current_schemas(false)) + SQL + end + + def table_exists?(name) + name = name.to_s + schema, table = name.split('.', 2) + + unless table # A table was provided without a schema + table = schema + schema = nil + end + + if name =~ /^"/ # Handle quoted table names + table = name + schema = nil + end + + query(<<-SQL).first[0].to_i > 0 + SELECT COUNT(*) FROM pg_tables - WHERE schemaname = ANY (current_schemas(false)) + WHERE tablename = '#{table.gsub(/(^"|"$)/,'')}' + #{schema ? "AND schemaname = '#{schema}'" : ''} SQL end @@ -925,7 +955,7 @@ module ActiveRecord # Construct a clean list of column names from the ORDER BY clause, removing # any ASC/DESC modifiers order_columns = order_by.split(',').collect { |s| s.split.first } - order_columns.delete_if &:blank? + order_columns.delete_if(&:blank?) order_columns = order_columns.zip((0...order_columns.size).to_a).map { |s,i| "#{s} AS alias_#{i}" } # Return a DISTINCT ON() clause that's distinct on the columns we want but includes @@ -989,17 +1019,8 @@ module ActiveRecord # Money type has a fixed precision of 10 in PostgreSQL 8.2 and below, and as of # PostgreSQL 8.3 it has a fixed precision of 19. PostgreSQLColumn.extract_precision # should know about this but can't detect it there, so deal with it here. - money_precision = (postgresql_version >= 80300) ? 19 : 10 - PostgreSQLColumn.module_eval(<<-end_eval) - def extract_precision(sql_type) # def extract_precision(sql_type) - if sql_type =~ /^money$/ # if sql_type =~ /^money$/ - #{money_precision} # 19 - else # else - super # super - end # end - end # end - end_eval - + PostgreSQLColumn.money_precision = + (postgresql_version >= 80300) ? 19 : 10 configure_connection end diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index ee2d420194..70a460d41d 100644 --- a/activerecord/lib/active_record/nested_attributes.rb +++ b/activerecord/lib/active_record/nested_attributes.rb @@ -348,14 +348,24 @@ module ActiveRecord attributes_collection = attributes_collection.sort_by { |index, _| index.to_i }.map { |_, attributes| attributes } end + association = send(association_name) + + existing_records = if association.loaded? + association.to_a + else + attribute_ids = attributes_collection.map {|a| a['id'] || a[:id] }.compact + attribute_ids.present? ? association.all(:conditions => {:id => attribute_ids}) : [] + end + attributes_collection.each do |attributes| attributes = attributes.with_indifferent_access if attributes['id'].blank? unless reject_new_record?(association_name, attributes) - send(association_name).build(attributes.except(*UNASSIGNABLE_KEYS)) + association.build(attributes.except(*UNASSIGNABLE_KEYS)) end - elsif existing_record = send(association_name).detect { |record| record.id.to_s == attributes['id'].to_s } + elsif existing_record = existing_records.detect { |record| record.id.to_s == attributes['id'].to_s } + association.send(:add_record_to_target_with_callbacks, existing_record) unless association.loaded? assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy]) else raise_nested_attributes_record_not_found(association_name, attributes['id']) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index b5e8b7570a..09332418d5 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -1,3 +1,4 @@ +require 'active_support/core_ext/array/wrap' require 'active_support/core_ext/object/blank' module ActiveRecord @@ -9,7 +10,7 @@ module ActiveRecord attr_accessor :"#{query_method}_values" next if [:where, :having].include?(query_method) - class_eval <<-CEVAL + class_eval <<-CEVAL, __FILE__ def #{query_method}(*args, &block) new_relation = clone new_relation.send(:apply_modules, Module.new(&block)) if block_given? @@ -21,12 +22,12 @@ module ActiveRecord end [:where, :having].each do |query_method| - class_eval <<-CEVAL + class_eval <<-CEVAL, __FILE__ def #{query_method}(*args, &block) new_relation = clone new_relation.send(:apply_modules, Module.new(&block)) if block_given? value = build_where(*args) - new_relation.#{query_method}_values += [*value] if value.present? + new_relation.#{query_method}_values += Array.wrap(value) if value.present? new_relation end CEVAL @@ -35,7 +36,7 @@ module ActiveRecord ActiveRecord::Relation::SINGLE_VALUE_METHODS.each do |query_method| attr_accessor :"#{query_method}_value" - class_eval <<-CEVAL + class_eval <<-CEVAL, __FILE__ def #{query_method}(value = true, &block) new_relation = clone new_relation.send(:apply_modules, Module.new(&block)) if block_given? diff --git a/activerecord/lib/active_record/serializers/xml_serializer.rb b/activerecord/lib/active_record/serializers/xml_serializer.rb index b19920741e..2e85959b1e 100644 --- a/activerecord/lib/active_record/serializers/xml_serializer.rb +++ b/activerecord/lib/active_record/serializers/xml_serializer.rb @@ -1,3 +1,4 @@ +require 'active_support/core_ext/array/wrap' require 'active_support/core_ext/hash/conversions' module ActiveRecord #:nodoc: @@ -186,7 +187,7 @@ module ActiveRecord #:nodoc: end def serializable_method_attributes - Array(options[:methods]).inject([]) do |method_attributes, name| + Array.wrap(options[:methods]).inject([]) do |method_attributes, name| method_attributes << MethodAttribute.new(name.to_s, @serializable) if @serializable.respond_to?(name.to_s) method_attributes end diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb index 4806fa0ecc..6283bdd0d6 100644 --- a/activerecord/lib/active_record/validations/uniqueness.rb +++ b/activerecord/lib/active_record/validations/uniqueness.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/array/wrap' + module ActiveRecord module Validations class UniquenessValidator < ActiveModel::EachValidator @@ -19,7 +21,7 @@ module ActiveRecord relation = table.where(sql, *params) - Array(options[:scope]).each do |scope_item| + Array.wrap(options[:scope]).each do |scope_item| scope_value = record.send(scope_item) relation = relation.where(scope_item => scope_value) end diff --git a/activerecord/lib/rails/generators/active_record/model/model_generator.rb b/activerecord/lib/rails/generators/active_record/model/model_generator.rb index 3e72fbeca8..539c2517ee 100644 --- a/activerecord/lib/rails/generators/active_record/model/model_generator.rb +++ b/activerecord/lib/rails/generators/active_record/model/model_generator.rb @@ -20,6 +20,11 @@ module ActiveRecord template 'model.rb', File.join('app/models', class_path, "#{file_name}.rb") end + def create_module_file + return if class_path.empty? + template 'module.rb', File.join('app/models', "#{class_path.join('/')}.rb") + end + hook_for :test_framework protected diff --git a/activerecord/lib/rails/generators/active_record/model/templates/module.rb b/activerecord/lib/rails/generators/active_record/model/templates/module.rb new file mode 100644 index 0000000000..bb4220f038 --- /dev/null +++ b/activerecord/lib/rails/generators/active_record/model/templates/module.rb @@ -0,0 +1,5 @@ +module <%= class_path.map(&:camelize).join('::') %> + def self.table_name_prefix + '<%= class_path.join('_') %>_' + end +end diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 8774ed58aa..1c3655b587 100755 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -579,9 +579,9 @@ class BasicsTest < ActiveRecord::TestCase assert_equal(topics(:second).title, topics.first.title) end - def test_table_name_guesses - classes = [Category, Smarts, CreditCard, CreditCard::PinNumber, CreditCard::PinNumber::CvvCode, CreditCard::SubPinNumber, CreditCard::Brand, MasterCreditCard] + GUESSED_CLASSES = [Category, Smarts, CreditCard, CreditCard::PinNumber, CreditCard::PinNumber::CvvCode, CreditCard::SubPinNumber, CreditCard::Brand, MasterCreditCard] + def test_table_name_guesses assert_equal "topics", Topic.table_name assert_equal "categories", Category.table_name @@ -592,9 +592,13 @@ class BasicsTest < ActiveRecord::TestCase assert_equal "credit_card_pin_numbers", CreditCard::SubPinNumber.table_name assert_equal "categories", CreditCard::Brand.table_name assert_equal "master_credit_cards", MasterCreditCard.table_name + ensure + GUESSED_CLASSES.each(&:reset_table_name) + end + def test_singular_table_name_guesses ActiveRecord::Base.pluralize_table_names = false - classes.each(&:reset_table_name) + GUESSED_CLASSES.each(&:reset_table_name) assert_equal "category", Category.table_name assert_equal "smarts", Smarts.table_name @@ -604,10 +608,12 @@ class BasicsTest < ActiveRecord::TestCase assert_equal "credit_card_pin_number", CreditCard::SubPinNumber.table_name assert_equal "category", CreditCard::Brand.table_name assert_equal "master_credit_card", MasterCreditCard.table_name - + ensure ActiveRecord::Base.pluralize_table_names = true - classes.each(&:reset_table_name) + GUESSED_CLASSES.each(&:reset_table_name) + end + def test_table_name_guesses_with_prefixes_and_suffixes ActiveRecord::Base.table_name_prefix = "test_" Category.reset_table_name assert_equal "test_categories", Category.table_name @@ -620,8 +626,15 @@ class BasicsTest < ActiveRecord::TestCase ActiveRecord::Base.table_name_suffix = "" Category.reset_table_name assert_equal "categories", Category.table_name + ensure + ActiveRecord::Base.table_name_prefix = "" + ActiveRecord::Base.table_name_suffix = "" + GUESSED_CLASSES.each(&:reset_table_name) + end + def test_singular_table_name_guesses_with_prefixes_and_suffixes ActiveRecord::Base.pluralize_table_names = false + ActiveRecord::Base.table_name_prefix = "test_" Category.reset_table_name assert_equal "test_category", Category.table_name @@ -634,9 +647,40 @@ class BasicsTest < ActiveRecord::TestCase ActiveRecord::Base.table_name_suffix = "" Category.reset_table_name assert_equal "category", Category.table_name - + ensure ActiveRecord::Base.pluralize_table_names = true - classes.each(&:reset_table_name) + ActiveRecord::Base.table_name_prefix = "" + ActiveRecord::Base.table_name_suffix = "" + GUESSED_CLASSES.each(&:reset_table_name) + end + + def test_table_name_guesses_with_inherited_prefixes_and_suffixes + GUESSED_CLASSES.each(&:reset_table_name) + + CreditCard.table_name_prefix = "test_" + CreditCard.reset_table_name + Category.reset_table_name + assert_equal "test_credit_cards", CreditCard.table_name + assert_equal "categories", Category.table_name + CreditCard.table_name_suffix = "_test" + CreditCard.reset_table_name + Category.reset_table_name + assert_equal "test_credit_cards_test", CreditCard.table_name + assert_equal "categories", Category.table_name + CreditCard.table_name_prefix = "" + CreditCard.reset_table_name + Category.reset_table_name + assert_equal "credit_cards_test", CreditCard.table_name + assert_equal "categories", Category.table_name + CreditCard.table_name_suffix = "" + CreditCard.reset_table_name + Category.reset_table_name + assert_equal "credit_cards", CreditCard.table_name + assert_equal "categories", Category.table_name + ensure + CreditCard.table_name_prefix = "" + CreditCard.table_name_suffix = "" + GUESSED_CLASSES.each(&:reset_table_name) end def test_destroy_all @@ -2009,6 +2053,10 @@ class BasicsTest < ActiveRecord::TestCase assert !SubStiPost.descends_from_active_record? end + def test_base_subclasses_is_public_method + assert ActiveRecord::Base.public_methods.include?("subclasses") + end + def test_find_on_abstract_base_class_doesnt_use_type_condition old_class = LooseDescendant Object.send :remove_const, :LooseDescendant @@ -2157,14 +2205,6 @@ class BasicsTest < ActiveRecord::TestCase assert xml.include?(%(<arbitrary-element>#{value}</arbitrary-element>)) end - def test_type_name_with_module_should_handle_beginning - ActiveRecord::Base.store_full_sti_class = false - assert_equal 'ActiveRecord::Person', ActiveRecord::Base.send(:type_name_with_module, 'Person') - assert_equal '::Person', ActiveRecord::Base.send(:type_name_with_module, '::Person') - ensure - ActiveRecord::Base.store_full_sti_class = true - end - def test_to_param_should_return_string assert_kind_of String, Client.find(:first).to_param end diff --git a/activerecord/test/cases/datatype_test_postgresql.rb b/activerecord/test/cases/datatype_test_postgresql.rb index 9454b6e059..3c2d9fb7bd 100644 --- a/activerecord/test/cases/datatype_test_postgresql.rb +++ b/activerecord/test/cases/datatype_test_postgresql.rb @@ -97,7 +97,7 @@ class PostgresqlDataTypeTest < ActiveRecord::TestCase def test_money_values assert_equal 567.89, @first_money.wealth - assert_equal -567.89, @second_money.wealth + assert_equal(-567.89, @second_money.wealth) end def test_number_values diff --git a/activerecord/test/cases/modules_test.rb b/activerecord/test/cases/modules_test.rb index 7209966bf8..c924c3dfad 100644 --- a/activerecord/test/cases/modules_test.rb +++ b/activerecord/test/cases/modules_test.rb @@ -1,8 +1,9 @@ require "cases/helper" require 'models/company_in_module' +require 'models/shop' class ModulesTest < ActiveRecord::TestCase - fixtures :accounts, :companies, :projects, :developers + fixtures :accounts, :companies, :projects, :developers, :collections, :products, :variants def setup # need to make sure Object::Firm and Object::Client are not defined, @@ -110,4 +111,34 @@ class ModulesTest < ActiveRecord::TestCase ActiveRecord::Base.table_name_prefix = '' classes.each(&:reset_table_name) end + + def test_compute_type_can_infer_class_name_of_sibling_inside_module + old = ActiveRecord::Base.store_full_sti_class + ActiveRecord::Base.store_full_sti_class = true + assert_equal MyApplication::Business::Firm, MyApplication::Business::Client.send(:compute_type, "Firm") + ensure + ActiveRecord::Base.store_full_sti_class = old + end + + def test_nested_models_should_not_raise_exception_when_using_delete_all_dependency_on_association + old = ActiveRecord::Base.store_full_sti_class + ActiveRecord::Base.store_full_sti_class = true + + collection = Shop::Collection.find(:first) + assert !collection.products.empty?, "Collection should have products" + assert_nothing_raised { collection.destroy } + ensure + ActiveRecord::Base.store_full_sti_class = old + end + + def test_nested_models_should_not_raise_exception_when_using_nullify_dependency_on_association + old = ActiveRecord::Base.store_full_sti_class + ActiveRecord::Base.store_full_sti_class = true + + product = Shop::Product.find(:first) + assert !product.variants.empty?, "Product should have variants" + assert_nothing_raised { product.destroy } + ensure + ActiveRecord::Base.store_full_sti_class = old + end end diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb index 7ca9c416cb..eae8ae7e39 100644 --- a/activerecord/test/cases/nested_attributes_test.rb +++ b/activerecord/test/cases/nested_attributes_test.rb @@ -453,6 +453,16 @@ module NestedAttributesOnACollectionAssociationTests assert_equal 'Privateers Greed', @pirate.send(@association_name).last.name end + def test_should_not_load_association_when_updating_existing_records + @pirate.reload + @pirate.send(association_setter, [{ :id => @child_1.id, :name => 'Grace OMalley' }]) + assert ! @pirate.send(@association_name).loaded? + + @pirate.save + assert ! @pirate.send(@association_name).loaded? + assert_equal 'Grace OMalley', @child_1.reload.name + end + def test_should_take_a_hash_with_composite_id_keys_and_assign_the_attributes_to_the_associated_models @child_1.stubs(:id).returns('ABC1X') @child_2.stubs(:id).returns('ABC2X') @@ -507,7 +517,7 @@ module NestedAttributesOnACollectionAssociationTests def test_should_ignore_new_associated_records_if_a_reject_if_proc_returns_false @alternate_params[association_getter]['baz'] = {} - assert_no_difference("@pirate.send(@association_name).length") do + assert_no_difference("@pirate.send(@association_name).count") do @pirate.attributes = @alternate_params end end diff --git a/activerecord/test/cases/schema_test_postgresql.rb b/activerecord/test/cases/schema_test_postgresql.rb index a294848fa3..3ed73786a7 100644 --- a/activerecord/test/cases/schema_test_postgresql.rb +++ b/activerecord/test/cases/schema_test_postgresql.rb @@ -52,6 +52,21 @@ class SchemaTest < ActiveRecord::TestCase @connection.execute "DROP SCHEMA #{SCHEMA_NAME} CASCADE" end + def test_table_exists? + [Thing1, Thing2, Thing3, Thing4].each do |klass| + name = klass.table_name + assert @connection.table_exists?(name), "'#{name}' table should exist" + end + end + + def test_table_exists_wrong_schema + assert(!@connection.table_exists?("foo.things"), "table should not exist") + end + + def test_table_exists_quoted_table + assert(@connection.table_exists?('"things.table"'), "table should exist") + end + def test_with_schema_prefixed_table_name assert_nothing_raised do assert_equal COLUMNS, columns("#{SCHEMA_NAME}.#{TABLE_NAME}") diff --git a/activerecord/test/fixtures/collections.yml b/activerecord/test/fixtures/collections.yml new file mode 100644 index 0000000000..ad0fd26554 --- /dev/null +++ b/activerecord/test/fixtures/collections.yml @@ -0,0 +1,3 @@ +collection_1: + id: 1 + name: Collection diff --git a/activerecord/test/fixtures/products.yml b/activerecord/test/fixtures/products.yml new file mode 100644 index 0000000000..8a197fb038 --- /dev/null +++ b/activerecord/test/fixtures/products.yml @@ -0,0 +1,4 @@ +product_1: + id: 1 + collection_id: 1 + name: Product diff --git a/activerecord/test/fixtures/variants.yml b/activerecord/test/fixtures/variants.yml new file mode 100644 index 0000000000..06be30727b --- /dev/null +++ b/activerecord/test/fixtures/variants.yml @@ -0,0 +1,4 @@ +variant_1: + id: 1 + product_id: 1 + name: Variant diff --git a/activerecord/test/models/shop.rb b/activerecord/test/models/shop.rb new file mode 100644 index 0000000000..b232185693 --- /dev/null +++ b/activerecord/test/models/shop.rb @@ -0,0 +1,12 @@ +module Shop + class Collection < ActiveRecord::Base + has_many :products, :dependent => :nullify + end + + class Product < ActiveRecord::Base + has_many :variants, :dependent => :delete_all + end + + class Variant < ActiveRecord::Base + end +end diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index bec4291457..7a0cf550e0 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -99,6 +99,10 @@ ActiveRecord::Schema.define do t.string :name end + create_table :collections, :force => true do |t| + t.string :name + end + create_table :colnametests, :force => true do |t| t.integer :references, :null => false end @@ -394,6 +398,11 @@ ActiveRecord::Schema.define do t.integer :price end + create_table :products, :force => true do |t| + t.references :collection + t.string :name + end + create_table :projects, :force => true do |t| t.string :name t.string :type @@ -499,6 +508,11 @@ ActiveRecord::Schema.define do t.column :looter_type, :string end + create_table :variants, :force => true do |t| + t.references :product + t.string :name + end + create_table :vertices, :force => true do |t| t.column :label, :string end |