aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/CHANGELOG2
-rwxr-xr-xactiverecord/lib/active_record/associations.rb7
-rw-r--r--activerecord/lib/active_record/associations/association_collection.rb23
-rw-r--r--activerecord/lib/active_record/associations/association_proxy.rb4
-rwxr-xr-xactiverecord/lib/active_record/base.rb51
-rw-r--r--activerecord/lib/active_record/callbacks.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb3
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb6
-rwxr-xr-xactiverecord/lib/active_record/connection_adapters/abstract_adapter.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb69
-rw-r--r--activerecord/lib/active_record/nested_attributes.rb14
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb9
-rw-r--r--activerecord/lib/active_record/serializers/xml_serializer.rb3
-rw-r--r--activerecord/lib/active_record/validations/uniqueness.rb4
-rw-r--r--activerecord/lib/rails/generators/active_record/model/model_generator.rb5
-rw-r--r--activerecord/lib/rails/generators/active_record/model/templates/module.rb5
-rwxr-xr-xactiverecord/test/cases/base_test.rb70
-rw-r--r--activerecord/test/cases/datatype_test_postgresql.rb2
-rw-r--r--activerecord/test/cases/modules_test.rb33
-rw-r--r--activerecord/test/cases/nested_attributes_test.rb12
-rw-r--r--activerecord/test/cases/schema_test_postgresql.rb15
-rw-r--r--activerecord/test/fixtures/collections.yml3
-rw-r--r--activerecord/test/fixtures/products.yml4
-rw-r--r--activerecord/test/fixtures/variants.yml4
-rw-r--r--activerecord/test/models/shop.rb12
-rw-r--r--activerecord/test/schema/schema.rb14
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