aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/CHANGELOG19
-rw-r--r--activerecord/lib/active_record/associations/belongs_to_association.rb2
-rw-r--r--activerecord/lib/active_record/attribute_methods/primary_key.rb2
-rw-r--r--activerecord/lib/active_record/base.rb26
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb26
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb5
-rw-r--r--activerecord/lib/active_record/migration.rb2
-rw-r--r--activerecord/lib/active_record/nested_attributes.rb2
-rw-r--r--activerecord/lib/active_record/relation.rb12
-rw-r--r--activerecord/lib/active_record/relation/batches.rb7
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb6
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb6
-rw-r--r--activerecord/lib/active_record/serialization.rb40
-rw-r--r--activerecord/lib/active_record/serializers/xml_serializer.rb42
-rw-r--r--activerecord/lib/active_record/session_store.rb28
-rw-r--r--activerecord/test/cases/adapter_test.rb20
-rw-r--r--activerecord/test/cases/adapters/mysql/connection_test.rb10
-rw-r--r--activerecord/test/cases/adapters/postgresql/connection_test.rb14
-rw-r--r--activerecord/test/cases/adapters/sqlite3/quoting_test.rb2
-rw-r--r--activerecord/test/cases/associations/belongs_to_associations_test.rb9
-rw-r--r--activerecord/test/cases/associations/eager_test.rb20
-rw-r--r--activerecord/test/cases/base_test.rb40
-rw-r--r--activerecord/test/cases/batches_test.rb16
-rw-r--r--activerecord/test/cases/connection_management_test.rb34
-rw-r--r--activerecord/test/cases/finder_test.rb25
-rw-r--r--activerecord/test/cases/log_subscriber_test.rb8
-rw-r--r--activerecord/test/cases/named_scope_test.rb20
-rw-r--r--activerecord/test/cases/nested_attributes_test.rb5
-rw-r--r--activerecord/test/cases/primary_keys_test.rb2
-rw-r--r--activerecord/test/cases/serialization_test.rb142
-rw-r--r--activerecord/test/cases/xml_serialization_test.rb134
-rw-r--r--activerecord/test/models/comment.rb5
-rw-r--r--activerecord/test/schema/schema.rb1
33 files changed, 454 insertions, 278 deletions
diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG
index 4113a16c12..d24ca2716e 100644
--- a/activerecord/CHANGELOG
+++ b/activerecord/CHANGELOG
@@ -1,3 +1,22 @@
+*Rails 3.2.0 (unreleased)*
+
+* ActiveRecord::Base.establish_connection now takes a string that contains
+a URI that specifies the connection configuration. For example:
+
+ ActiveRecord::Base.establish_connection 'postgres://localhost/foo'
+
+* Active Record's dynamic finder will now raise the error if you passing in less number of arguments than what you call in method signature.
+
+ So if you were doing this and expecting the second argument to be nil:
+
+ User.find_by_username_and_group("sikachu")
+
+ You'll now get `ArgumentError: wrong number of arguments (1 for 2).` You'll then have to do this:
+
+ User.find_by_username_and_group("sikachu", nil)
+
+ [Prem Sichanugrist]
+
*Rails 3.1.0 (unreleased)*
* ActiveRecord::MacroReflection::AssociationReflection#build_record has a new method signature.
diff --git a/activerecord/lib/active_record/associations/belongs_to_association.rb b/activerecord/lib/active_record/associations/belongs_to_association.rb
index c263edd2c6..adb6af7165 100644
--- a/activerecord/lib/active_record/associations/belongs_to_association.rb
+++ b/activerecord/lib/active_record/associations/belongs_to_association.rb
@@ -37,7 +37,7 @@ module ActiveRecord
# Checks whether record is different to the current target, without loading it
def different_target?(record)
record.nil? && owner[reflection.foreign_key] ||
- record.id != owner[reflection.foreign_key]
+ record && record.id != owner[reflection.foreign_key]
end
def replace_keys(record)
diff --git a/activerecord/lib/active_record/attribute_methods/primary_key.rb b/activerecord/lib/active_record/attribute_methods/primary_key.rb
index e3f221c773..8bd898d126 100644
--- a/activerecord/lib/active_record/attribute_methods/primary_key.rb
+++ b/activerecord/lib/active_record/attribute_methods/primary_key.rb
@@ -7,7 +7,7 @@ module ActiveRecord
# the record is not persisted? or has just been destroyed.
def to_key
key = send(self.class.primary_key)
- [key] if key
+ persisted? && key ? [key] : nil
end
module ClassMethods
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index daa1b2fcd6..99ed273a8a 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -1052,20 +1052,15 @@ module ActiveRecord #:nodoc:
# Each dynamic finder using <tt>scoped_by_*</tt> is also defined in the class after it
# is first invoked, so that future attempts to use it do not run through method_missing.
def method_missing(method_id, *arguments, &block)
- if match = DynamicFinderMatch.match(method_id)
+ if match = (DynamicFinderMatch.match(method_id) || DynamicScopeMatch.match(method_id))
attribute_names = match.attribute_names
super unless all_attributes_exists?(attribute_names)
- if match.finder?
- options = arguments.extract_options!
- relation = options.any? ? scoped(options) : scoped
- relation.send :find_by_attributes, match, attribute_names, *arguments, &block
- elsif match.instantiator?
- scoped.send :find_or_instantiator_by_attributes, match, attribute_names, *arguments, &block
+ if arguments.size < attribute_names.size
+ method_trace = "#{__FILE__}:#{__LINE__}:in `#{method_id}'"
+ backtrace = [method_trace] + caller
+ raise ArgumentError, "wrong number of arguments (#{arguments.size} for #{attribute_names.size})", backtrace
end
- elsif match = DynamicScopeMatch.match(method_id)
- attribute_names = match.attribute_names
- super unless all_attributes_exists?(attribute_names)
- if match.scope?
+ if match.respond_to?(:scope?) && match.scope?
self.class_eval <<-METHOD, __FILE__, __LINE__ + 1
def self.#{method_id}(*args) # def self.scoped_by_user_name_and_password(*args)
attributes = Hash[[:#{attribute_names.join(',:')}].zip(args)] # attributes = Hash[[:user_name, :password].zip(args)]
@@ -1074,6 +1069,12 @@ module ActiveRecord #:nodoc:
end # end
METHOD
send(method_id, *arguments)
+ elsif match.finder?
+ options = arguments.extract_options!
+ relation = options.any? ? scoped(options) : scoped
+ relation.send :find_by_attributes, match, attribute_names, *arguments, &block
+ elsif match.instantiator?
+ scoped.send :find_or_instantiator_by_attributes, match, attribute_names, *arguments, &block
end
else
super
@@ -1638,7 +1639,8 @@ MSG
when new_record?
"#{self.class.model_name.cache_key}/new"
when timestamp = self[:updated_at]
- "#{self.class.model_name.cache_key}/#{id}-#{timestamp.to_s(:number)}"
+ timestamp = timestamp.utc.to_s(:number)
+ "#{self.class.model_name.cache_key}/#{id}-#{timestamp}"
else
"#{self.class.model_name.cache_key}/#{id}"
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb
index bcd3abc08d..123b3654e6 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb
@@ -46,6 +46,12 @@ module ActiveRecord
# "database" => "path/to/dbfile"
# )
#
+ # Or a URL:
+ #
+ # ActiveRecord::Base.establish_connection(
+ # "postgres://myuser:mypass@localhost/somedatabase"
+ # )
+ #
# The exceptions AdapterNotSpecified, AdapterNotFound and ArgumentError
# may be returned on an error.
def self.establish_connection(spec = nil)
@@ -58,6 +64,8 @@ module ActiveRecord
when Symbol, String
if configuration = configurations[spec.to_s]
establish_connection(configuration)
+ elsif spec.is_a?(String) && hash = connection_url_to_hash(spec)
+ establish_connection(hash)
else
raise AdapterNotSpecified, "#{spec} database is not configured"
end
@@ -81,6 +89,24 @@ module ActiveRecord
end
end
+ def self.connection_url_to_hash(url) # :nodoc:
+ config = URI.parse url
+ adapter = config.scheme
+ adapter = "postgresql" if adapter == "postgres"
+ spec = { :adapter => adapter,
+ :username => config.user,
+ :password => config.password,
+ :port => config.port,
+ :database => config.path.sub(%r{^/},""),
+ :host => config.host }
+ spec.reject!{ |_,value| !value }
+ if config.query
+ options = Hash[config.query.split("&").map{ |pair| pair.split("=") }].symbolize_keys
+ spec.merge!(options)
+ end
+ spec
+ end
+
class << self
# Returns the connection currently associated with the class. This can
# also be used to "borrow" the connection to do database work unrelated
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
index 724b2e6d9c..e2a0f63393 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
@@ -157,6 +157,11 @@ module ActiveRecord
end
end
+ def type_cast(value, column) # :nodoc:
+ return super unless BigDecimal === value
+
+ value.to_f
+ end
# DATABASE STATEMENTS ======================================
diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb
index c459846264..507f345ef5 100644
--- a/activerecord/lib/active_record/migration.rb
+++ b/activerecord/lib/active_record/migration.rb
@@ -1,3 +1,5 @@
+require "active_support/core_ext/module/delegation"
+require "active_support/core_ext/class/attribute_accessors"
require "active_support/core_ext/array/wrap"
module ActiveRecord
diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb
index 53617059d0..2dbebfcaf8 100644
--- a/activerecord/lib/active_record/nested_attributes.rb
+++ b/activerecord/lib/active_record/nested_attributes.rb
@@ -383,7 +383,7 @@ module ActiveRecord
attributes_collection = if keys.include?('id') || keys.include?(:id)
Array.wrap(attributes_collection)
else
- attributes_collection.sort_by { |i, _| i.to_i }.map { |_, attributes| attributes }
+ attributes_collection.values
end
end
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index 6258a16d0f..2d0861d5c9 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -408,7 +408,17 @@ module ActiveRecord
end
def eager_loading?
- @should_eager_load ||= (@eager_load_values.any? || (@includes_values.any? && references_eager_loaded_tables?))
+ @should_eager_load ||=
+ @eager_load_values.any? ||
+ @includes_values.any? && (joined_includes_values.any? || references_eager_loaded_tables?)
+ end
+
+ # Joins that are also marked for preloading. In which case we should just eager load them.
+ # Note that this is a naive implementation because we could have strings and symbols which
+ # represent the same association, but that aren't matched by this. Also, we could have
+ # nested hashes which partially match, e.g. { :a => :b } & { :a => [:b, :c] }
+ def joined_includes_values
+ @includes_values & @joins_values
end
def ==(other)
diff --git a/activerecord/lib/active_record/relation/batches.rb b/activerecord/lib/active_record/relation/batches.rb
index 46ab67d1cf..ec1176e3dd 100644
--- a/activerecord/lib/active_record/relation/batches.rb
+++ b/activerecord/lib/active_record/relation/batches.rb
@@ -66,11 +66,14 @@ module ActiveRecord
records = relation.where(table[primary_key].gteq(start)).all
while records.any?
+ records_size = records.size
+ primary_key_offset = records.last.id
+
yield records
- break if records.size < batch_size
+ break if records_size < batch_size
- if primary_key_offset = records.last.id
+ if primary_key_offset
records = relation.where(table[primary_key].gt(primary_key_offset)).to_a
else
raise "Primary key not included in the custom select clause"
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index 422d89060b..8cef4e5554 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -193,8 +193,8 @@ module ActiveRecord
else
relation = relation.where(table[primary_key].eq(id)) if id
end
-
- connection.select_value(relation.to_sql) ? true : false
+
+ connection.select_value(relation.to_sql, "#{name} Exists") ? true : false
end
protected
@@ -243,7 +243,7 @@ module ActiveRecord
end
def construct_limited_ids_condition(relation)
- orders = relation.order_values
+ orders = relation.order_values.map { |val| val.presence }.compact
values = @klass.connection.distinct("#{@klass.connection.quote_table_name table_name}.#{primary_key}", orders)
relation = relation.dup
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index d17861f407..8bd4732c0c 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -96,11 +96,11 @@ module ActiveRecord
relation
end
- def having(*args)
- return self if args.blank?
+ def having(opts, *rest)
+ return self if opts.blank?
relation = clone
- relation.having_values += build_where(*args)
+ relation.having_values += build_where(opts, rest)
relation
end
diff --git a/activerecord/lib/active_record/serialization.rb b/activerecord/lib/active_record/serialization.rb
index e3185a9c5a..5ad40d8cd9 100644
--- a/activerecord/lib/active_record/serialization.rb
+++ b/activerecord/lib/active_record/serialization.rb
@@ -10,46 +10,8 @@ module ActiveRecord #:nodoc:
options[:except] = Array.wrap(options[:except]).map { |n| n.to_s }
options[:except] |= Array.wrap(self.class.inheritance_column)
- hash = super(options)
-
- serializable_add_includes(options) do |association, records, opts|
- hash[association] = records.is_a?(Enumerable) ?
- records.map { |r| r.serializable_hash(opts) } :
- records.serializable_hash(opts)
- end
-
- hash
+ super(options)
end
-
- private
- # Add associations specified via the <tt>:include</tt> option.
- #
- # Expects a block that takes as arguments:
- # +association+ - name of the association
- # +records+ - the association record(s) to be serialized
- # +opts+ - options for the association records
- def serializable_add_includes(options = {})
- return unless include_associations = options.delete(:include)
-
- include_has_options = include_associations.is_a?(Hash)
- associations = include_has_options ? include_associations.keys : Array.wrap(include_associations)
-
- associations.each do |association|
- records = case self.class.reflect_on_association(association).macro
- when :has_many, :has_and_belongs_to_many
- send(association).to_a
- when :has_one, :belongs_to
- send(association)
- end
-
- if records
- association_options = include_has_options ? include_associations[association] : {}
- yield(association, records, association_options)
- end
- end
-
- options[:include] = include_associations
- end
end
end
diff --git a/activerecord/lib/active_record/serializers/xml_serializer.rb b/activerecord/lib/active_record/serializers/xml_serializer.rb
index f8e6cf958c..cbfa1ad609 100644
--- a/activerecord/lib/active_record/serializers/xml_serializer.rb
+++ b/activerecord/lib/active_record/serializers/xml_serializer.rb
@@ -182,48 +182,6 @@ module ActiveRecord #:nodoc:
options[:except] |= Array.wrap(@serializable.class.inheritance_column)
end
- def add_extra_behavior
- add_includes
- end
-
- def add_includes
- procs = options.delete(:procs)
- @serializable.send(:serializable_add_includes, options) do |association, records, opts|
- add_associations(association, records, opts)
- end
- options[:procs] = procs
- end
-
- # TODO This can likely be cleaned up to simple use ActiveSupport::XmlMini.to_tag as well.
- def add_associations(association, records, opts)
- association_name = association.to_s.singularize
- merged_options = options.merge(opts).merge!(:root => association_name, :skip_instruct => true)
-
- if records.is_a?(Enumerable)
- tag = ActiveSupport::XmlMini.rename_key(association.to_s, options)
- type = options[:skip_types] ? { } : {:type => "array"}
-
- if records.empty?
- @builder.tag!(tag, type)
- else
- @builder.tag!(tag, type) do
- records.each do |record|
- if options[:skip_types]
- record_type = {}
- else
- record_class = (record.class.to_s.underscore == association_name) ? nil : record.class.name
- record_type = {:type => record_class}
- end
-
- record.to_xml merged_options.merge(record_type)
- end
- end
- end
- elsif record = @serializable.send(association)
- record.to_xml(merged_options)
- end
- end
-
class Attribute < ActiveModel::Serializers::Xml::Serializer::Attribute #:nodoc:
def compute_type
klass = @serializable.class
diff --git a/activerecord/lib/active_record/session_store.rb b/activerecord/lib/active_record/session_store.rb
index 929559c3ba..76c37cc367 100644
--- a/activerecord/lib/active_record/session_store.rb
+++ b/activerecord/lib/active_record/session_store.rb
@@ -183,11 +183,6 @@ module ActiveRecord
##
# :singleton-method:
- # Use the ActiveRecord::Base.connection by default.
- cattr_accessor :connection
-
- ##
- # :singleton-method:
# The table name defaults to 'sessions'.
cattr_accessor :table_name
@@table_name = 'sessions'
@@ -206,10 +201,19 @@ module ActiveRecord
class << self
alias :data_column_name :data_column
+
+ # Use the ActiveRecord::Base.connection by default.
+ attr_writer :connection
+
+ # Use the ActiveRecord::Base.connection_pool by default.
+ attr_writer :connection_pool
- remove_method :connection
def connection
- @@connection ||= ActiveRecord::Base.connection
+ @connection ||= ActiveRecord::Base.connection
+ end
+
+ def connection_pool
+ @connection_pool ||= ActiveRecord::Base.connection_pool
end
# Look up a session by id and unmarshal its data if found.
@@ -219,6 +223,8 @@ module ActiveRecord
end
end
end
+
+ delegate :connection, :connection=, :connection_pool, :connection_pool=, :to => self
attr_reader :session_id, :new_record
alias :new_record? :new_record
@@ -297,8 +303,12 @@ module ActiveRecord
private
def get_session(env, sid)
Base.silence do
- sid ||= generate_sid
- session = find_session(sid)
+ unless sid and session = @@session_class.find_by_session_id(sid)
+ # If the sid was nil or if there is no pre-existing session under the sid,
+ # force the generation of a new sid and associate a new session associated with the new sid
+ sid = generate_sid
+ session = @@session_class.new(:session_id => sid, :data => {})
+ end
env[SESSION_RECORD_KEY] = session
[sid, session.data]
end
diff --git a/activerecord/test/cases/adapter_test.rb b/activerecord/test/cases/adapter_test.rb
index 07de544868..3942e7bb41 100644
--- a/activerecord/test/cases/adapter_test.rb
+++ b/activerecord/test/cases/adapter_test.rb
@@ -81,12 +81,6 @@ class AdapterTest < ActiveRecord::TestCase
end
end
- if current_adapter?(:PostgreSQLAdapter)
- def test_encoding
- assert_not_nil @connection.encoding
- end
- end
-
def test_table_alias
def @connection.test_table_alias_length() 10; end
class << @connection
@@ -146,4 +140,18 @@ class AdapterTest < ActiveRecord::TestCase
end
end
end
+
+ def test_disable_referential_integrity
+ assert_nothing_raised do
+ @connection.disable_referential_integrity do
+ # Oracle adapter uses prefetched primary key values from sequence and passes them to connection adapter insert method
+ if @connection.prefetch_primary_key?
+ id_value = @connection.next_sequence_value(@connection.default_sequence_name("fk_test_has_fk", "id"))
+ @connection.execute "INSERT INTO fk_test_has_fk (id, fk_id) VALUES (#{id_value},0)"
+ else
+ @connection.execute "INSERT INTO fk_test_has_fk (fk_id) VALUES (0)"
+ end
+ end
+ end
+ end
end
diff --git a/activerecord/test/cases/adapters/mysql/connection_test.rb b/activerecord/test/cases/adapters/mysql/connection_test.rb
index eee771ecff..2a89430da9 100644
--- a/activerecord/test/cases/adapters/mysql/connection_test.rb
+++ b/activerecord/test/cases/adapters/mysql/connection_test.rb
@@ -13,6 +13,16 @@ class MysqlConnectionTest < ActiveRecord::TestCase
end
end
+ def test_connect_with_url
+ run_without_connection do |orig|
+ ar_config = ARTest.connection_config['arunit']
+ url = "mysql://#{ar_config["username"]}@localhost/#{ar_config["database"]}"
+ klass = Class.new(ActiveRecord::Base)
+ klass.establish_connection(url)
+ assert_equal ar_config['database'], klass.connection.current_database
+ end
+ end
+
def test_mysql_reconnect_attribute_after_connection_with_reconnect_false
run_without_connection do |orig_connection|
ActiveRecord::Base.establish_connection(orig_connection.merge({:reconnect => false}))
diff --git a/activerecord/test/cases/adapters/postgresql/connection_test.rb b/activerecord/test/cases/adapters/postgresql/connection_test.rb
new file mode 100644
index 0000000000..21b97b3b39
--- /dev/null
+++ b/activerecord/test/cases/adapters/postgresql/connection_test.rb
@@ -0,0 +1,14 @@
+require "cases/helper"
+
+module ActiveRecord
+ class PostgresqlConnectionTest < ActiveRecord::TestCase
+ def setup
+ super
+ @connection = ActiveRecord::Base.connection
+ end
+
+ def test_encoding
+ assert_not_nil @connection.encoding
+ end
+ end
+end
diff --git a/activerecord/test/cases/adapters/sqlite3/quoting_test.rb b/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
index 0d9db92447..e0152e7ccf 100644
--- a/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
@@ -67,7 +67,7 @@ module ActiveRecord
def test_type_cast_bigdecimal
bd = BigDecimal.new '10.0'
- assert_equal bd.to_s('F'), @conn.type_cast(bd, nil)
+ assert_equal bd.to_f, @conn.type_cast(bd, nil)
end
def test_type_cast_unknown
diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb
index 4190f6d76d..818902beb5 100644
--- a/activerecord/test/cases/associations/belongs_to_associations_test.rb
+++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb
@@ -681,4 +681,13 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
firm = client.create_firm! :name => "baa"
assert_equal firm.id, client.client_of
end
+
+ def test_self_referential_belongs_to_with_counter_cache_assigning_nil
+ comment = Comment.create! :post => posts(:thinking), :body => "fuu"
+ comment.parent = nil
+ comment.save!
+
+ assert_equal nil, comment.reload.parent
+ assert_equal 0, comments(:greetings).reload.children_count
+ end
end
diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb
index 325fc58958..c6e451fc57 100644
--- a/activerecord/test/cases/associations/eager_test.rb
+++ b/activerecord/test/cases/associations/eager_test.rb
@@ -1040,4 +1040,24 @@ class EagerAssociationTest < ActiveRecord::TestCase
}
assert_no_queries { assert_equal groucho, sponsor.thing }
end
+
+ def test_joins_with_includes_should_preload_via_joins
+ post = assert_queries(1) { Post.includes(:comments).joins(:comments).order('posts.id desc').to_a.first }
+
+ assert_queries(0) do
+ assert_not_equal 0, post.comments.to_a.count
+ end
+ end
+
+ def test_join_eager_with_empty_order_should_generate_valid_sql
+ assert_nothing_raised(ActiveRecord::StatementInvalid) do
+ Post.includes(:comments).order("").where(:comments => {:body => "Thank you for the welcome"}).first
+ end
+ end
+
+ def test_join_eager_with_nil_order_should_generate_valid_sql
+ assert_nothing_raised(ActiveRecord::StatementInvalid) do
+ Post.includes(:comments).order(nil).where(:comments => {:body => "Thank you for the welcome"}).first
+ end
+ end
end
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index 9feb7b010f..607c87beec 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -488,11 +488,11 @@ class BasicsTest < ActiveRecord::TestCase
def test_hashing
assert_equal [ Topic.find(1) ], [ Topic.find(2).topic ] & [ Topic.find(1) ]
end
-
+
def test_comparison
topic_1 = Topic.create!
topic_2 = Topic.create!
-
+
assert_equal [topic_2, topic_1].sort, [topic_1, topic_2]
end
@@ -1113,6 +1113,17 @@ class BasicsTest < ActiveRecord::TestCase
self.table_name = 'numeric_data'
end
+ def test_big_decimal_conditions
+ m = NumericData.new(
+ :bank_balance => 1586.43,
+ :big_bank_balance => BigDecimal("1000234000567.95"),
+ :world_population => 6000000000,
+ :my_house_population => 3
+ )
+ assert m.save
+ assert_equal 0, NumericData.where("bank_balance > ?", 2000.0).count
+ end
+
def test_numeric_fields
m = NumericData.new(
:bank_balance => 1586.43,
@@ -1835,4 +1846,29 @@ class BasicsTest < ActiveRecord::TestCase
def test_attribtue_names_on_abstract_class
assert_equal [], AbstractCompany.attribute_names
end
+
+ def test_cache_key_for_existing_record_is_not_timezone_dependent
+ ActiveRecord::Base.time_zone_aware_attributes = true
+
+ Time.zone = "UTC"
+ utc_key = Developer.first.cache_key
+
+ Time.zone = "EST"
+ est_key = Developer.first.cache_key
+
+ assert_equal utc_key, est_key
+ ensure
+ ActiveRecord::Base.time_zone_aware_attributes = false
+ end
+
+ def test_cache_key_format_for_existing_record_with_updated_at
+ dev = Developer.first
+ assert_equal "developers/#{dev.id}-#{dev.updated_at.utc.to_s(:number)}", dev.cache_key
+ end
+
+ def test_cache_key_format_for_existing_record_with_nil_updated_at
+ dev = Developer.first
+ dev.update_attribute(:updated_at, nil)
+ assert_match /\/#{dev.id}$/, dev.cache_key
+ end
end
diff --git a/activerecord/test/cases/batches_test.rb b/activerecord/test/cases/batches_test.rb
index 50ddf6c757..a35baee4ed 100644
--- a/activerecord/test/cases/batches_test.rb
+++ b/activerecord/test/cases/batches_test.rb
@@ -100,4 +100,20 @@ class EachTest < ActiveRecord::TestCase
end
end
end
+
+ def test_find_in_batches_should_not_use_records_after_yielding_them_in_case_original_array_is_modified
+ not_a_post = "not a post"
+ not_a_post.stubs(:id).raises(StandardError, "not_a_post had #id called on it")
+
+ assert_nothing_raised do
+ Post.find_in_batches(:batch_size => 1) do |batch|
+ assert_kind_of Array, batch
+ assert_kind_of Post, batch.first
+
+ batch.map! { not_a_post }
+ end
+ end
+
+ end
+
end
diff --git a/activerecord/test/cases/connection_management_test.rb b/activerecord/test/cases/connection_management_test.rb
index a1d1177289..f554ceef35 100644
--- a/activerecord/test/cases/connection_management_test.rb
+++ b/activerecord/test/cases/connection_management_test.rb
@@ -25,6 +25,40 @@ module ActiveRecord
assert ActiveRecord::Base.connection_handler.active_connections?
end
+ class FakeBase < ActiveRecord::Base
+ def self.establish_connection spec
+ String === spec ? super : spec
+ end
+ end
+
+ def test_url_host_no_db
+ spec = FakeBase.establish_connection 'postgres://foo?encoding=utf8'
+ assert_equal({
+ :adapter => "postgresql",
+ :database => "",
+ :host => "foo",
+ :encoding => "utf8" }, spec)
+ end
+
+ def test_url_host_db
+ spec = FakeBase.establish_connection 'postgres://foo/bar?encoding=utf8'
+ assert_equal({
+ :adapter => "postgresql",
+ :database => "bar",
+ :host => "foo",
+ :encoding => "utf8" }, spec)
+ end
+
+ def test_url_port
+ spec = FakeBase.establish_connection 'postgres://foo:123?encoding=utf8'
+ assert_equal({
+ :adapter => "postgresql",
+ :database => "",
+ :port => 123,
+ :host => "foo",
+ :encoding => "utf8" }, spec)
+ end
+
def test_app_delegation
manager = ConnectionManagement.new(@app)
diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb
index 4e75eafe3d..5dc5f99582 100644
--- a/activerecord/test/cases/finder_test.rb
+++ b/activerecord/test/cases/finder_test.rb
@@ -140,23 +140,30 @@ class FinderTest < ActiveRecord::TestCase
def test_find_with_group
- developers = Developer.find(:all, :group => "salary", :select => "salary")
+ developers = Developer.find(:all, :group => "salary", :select => "salary")
assert_equal 4, developers.size
assert_equal 4, developers.map(&:salary).uniq.size
end
def test_find_with_group_and_having
- developers = Developer.find(:all, :group => "salary", :having => "sum(salary) > 10000", :select => "salary")
+ developers = Developer.find(:all, :group => "salary", :having => "sum(salary) > 10000", :select => "salary")
assert_equal 3, developers.size
assert_equal 3, developers.map(&:salary).uniq.size
- assert developers.all? { |developer| developer.salary > 10000 }
+ assert developers.all? { |developer| developer.salary > 10000 }
end
def test_find_with_group_and_sanitized_having
- developers = Developer.find(:all, :group => "salary", :having => ["sum(salary) > ?", 10000], :select => "salary")
+ developers = Developer.find(:all, :group => "salary", :having => ["sum(salary) > ?", 10000], :select => "salary")
assert_equal 3, developers.size
assert_equal 3, developers.map(&:salary).uniq.size
- assert developers.all? { |developer| developer.salary > 10000 }
+ assert developers.all? { |developer| developer.salary > 10000 }
+ end
+
+ def test_find_with_group_and_sanitized_having_method
+ developers = Developer.group(:salary).having("sum(salary) > ?", 10000).select('salary').all
+ assert_equal 3, developers.size
+ assert_equal 3, developers.map(&:salary).uniq.size
+ assert developers.all? { |developer| developer.salary > 10000 }
end
def test_find_with_entire_select_statement
@@ -659,6 +666,10 @@ class FinderTest < ActiveRecord::TestCase
assert_nil Topic.find_by_title_and_author_name("The First Topic", "Mary")
end
+ def test_find_by_two_attributes_but_passing_only_one
+ assert_raise(ArgumentError) { Topic.find_by_title_and_author_name("The First Topic") }
+ end
+
def test_find_last_by_one_attribute
assert_equal Topic.last, Topic.find_last_by_title(Topic.last.title)
assert_nil Topic.find_last_by_title("A title with no matches")
@@ -940,6 +951,10 @@ class FinderTest < ActiveRecord::TestCase
assert !another.persisted?
end
+ def test_find_or_initialize_from_two_attributes_but_passing_only_one
+ assert_raise(ArgumentError) { Topic.find_or_initialize_by_title_and_author_name("Another topic") }
+ end
+
def test_find_or_initialize_from_one_aggregate_attribute_and_one_not
new_customer = Customer.find_or_initialize_by_balance_and_name(Money.new(123), "Elizabeth")
assert_equal 123, new_customer.balance.amount
diff --git a/activerecord/test/cases/log_subscriber_test.rb b/activerecord/test/cases/log_subscriber_test.rb
index c6c6079490..9e8475465e 100644
--- a/activerecord/test/cases/log_subscriber_test.rb
+++ b/activerecord/test/cases/log_subscriber_test.rb
@@ -63,6 +63,14 @@ class LogSubscriberTest < ActiveRecord::TestCase
assert_match(/SELECT .*?FROM .?developers.?/i, @logger.logged(:debug).last)
end
+ def test_exists_query_logging
+ Developer.exists? 1
+ wait
+ assert_equal 1, @logger.logged(:debug).size
+ assert_match(/Developer Exists/, @logger.logged(:debug).last)
+ assert_match(/SELECT .*?FROM .?developers.?/i, @logger.logged(:debug).last)
+ end
+
def test_cached_queries
ActiveRecord::Base.cache do
Developer.all
diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb
index 2afe3c8f32..ed0240cada 100644
--- a/activerecord/test/cases/named_scope_test.rb
+++ b/activerecord/test/cases/named_scope_test.rb
@@ -497,14 +497,24 @@ end
class DynamicScopeTest < ActiveRecord::TestCase
fixtures :posts
+ def setup
+ @test_klass = Class.new(Post) do
+ def self.name; "Post"; end
+ end
+ end
+
def test_dynamic_scope
- assert_equal Post.scoped_by_author_id(1).find(1), Post.find(1)
- assert_equal Post.scoped_by_author_id_and_title(1, "Welcome to the weblog").first, Post.find(:first, :conditions => { :author_id => 1, :title => "Welcome to the weblog"})
+ assert_equal @test_klass.scoped_by_author_id(1).find(1), @test_klass.find(1)
+ assert_equal @test_klass.scoped_by_author_id_and_title(1, "Welcome to the weblog").first, @test_klass.find(:first, :conditions => { :author_id => 1, :title => "Welcome to the weblog"})
end
def test_dynamic_scope_should_create_methods_after_hitting_method_missing
- assert_blank Developer.methods.grep(/scoped_by_created_at/)
- Developer.scoped_by_created_at(nil)
- assert_present Developer.methods.grep(/scoped_by_created_at/)
+ assert_blank @test_klass.methods.grep(/scoped_by_type/)
+ @test_klass.scoped_by_type(nil)
+ assert_present @test_klass.methods.grep(/scoped_by_type/)
+ end
+
+ def test_dynamic_scope_with_less_number_of_arguments
+ assert_raise(ArgumentError){ @test_klass.scoped_by_author_id_and_title(1) }
end
end
diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb
index 6568eb1d18..67a9ed6cd8 100644
--- a/activerecord/test/cases/nested_attributes_test.rb
+++ b/activerecord/test/cases/nested_attributes_test.rb
@@ -755,6 +755,11 @@ module NestedAttributesOnACollectionAssociationTests
Interest.reflect_on_association(:man).options[:inverse_of] = :interests
end
+ def test_can_use_symbols_as_object_identifier
+ @pirate.attributes = { :parrots_attributes => { :foo => { :name => 'Lovely Day' }, :bar => { :name => 'Blown Away' } } }
+ assert_nothing_raised(NoMethodError) { @pirate.save! }
+ end
+
private
def association_setter
diff --git a/activerecord/test/cases/primary_keys_test.rb b/activerecord/test/cases/primary_keys_test.rb
index 05a41d8a0a..7e3da145e5 100644
--- a/activerecord/test/cases/primary_keys_test.rb
+++ b/activerecord/test/cases/primary_keys_test.rb
@@ -26,7 +26,7 @@ class PrimaryKeysTest < ActiveRecord::TestCase
def test_to_key_with_primary_key_after_destroy
topic = Topic.find(1)
topic.destroy
- assert_equal [1], topic.to_key
+ assert_equal nil, topic.to_key
end
def test_integer_key
diff --git a/activerecord/test/cases/serialization_test.rb b/activerecord/test/cases/serialization_test.rb
index 677d659f39..382d659289 100644
--- a/activerecord/test/cases/serialization_test.rb
+++ b/activerecord/test/cases/serialization_test.rb
@@ -1,13 +1,8 @@
require "cases/helper"
require 'models/contact'
require 'models/topic'
-require 'models/reply'
-require 'models/company'
class SerializationTest < ActiveRecord::TestCase
-
- fixtures :topics, :companies, :accounts
-
FORMATS = [ :xml, :json ]
def setup
@@ -19,8 +14,6 @@ class SerializationTest < ActiveRecord::TestCase
:awesome => false,
:preferences => { :gem => '<strong>ruby</strong>' }
}
-
- @contact = Contact.new(@contact_attributes)
end
def test_serialized_init_with
@@ -29,134 +22,6 @@ class SerializationTest < ActiveRecord::TestCase
assert_equal 'foo', topic.content
end
- def test_to_xml
- xml = REXML::Document.new(topics(:first).to_xml(:indent => 0))
- bonus_time_in_current_timezone = topics(:first).bonus_time.xmlschema
- written_on_in_current_timezone = topics(:first).written_on.xmlschema
- last_read_in_current_timezone = topics(:first).last_read.xmlschema
-
- assert_equal "topic", xml.root.name
- assert_equal "The First Topic" , xml.elements["//title"].text
- assert_equal "David" , xml.elements["//author-name"].text
- assert_match "Have a nice day", xml.elements["//content"].text
-
- assert_equal "1", xml.elements["//id"].text
- assert_equal "integer" , xml.elements["//id"].attributes['type']
-
- assert_equal "1", xml.elements["//replies-count"].text
- assert_equal "integer" , xml.elements["//replies-count"].attributes['type']
-
- assert_equal written_on_in_current_timezone, xml.elements["//written-on"].text
- assert_equal "datetime" , xml.elements["//written-on"].attributes['type']
-
- assert_equal "david@loudthinking.com", xml.elements["//author-email-address"].text
-
- assert_equal nil, xml.elements["//parent-id"].text
- assert_equal "integer", xml.elements["//parent-id"].attributes['type']
- assert_equal "true", xml.elements["//parent-id"].attributes['nil']
-
- if current_adapter?(:SybaseAdapter)
- assert_equal last_read_in_current_timezone, xml.elements["//last-read"].text
- assert_equal "datetime" , xml.elements["//last-read"].attributes['type']
- else
- # Oracle enhanced adapter allows to define Date attributes in model class (see topic.rb)
- assert_equal "2004-04-15", xml.elements["//last-read"].text
- assert_equal "date" , xml.elements["//last-read"].attributes['type']
- end
-
- # Oracle and DB2 don't have true boolean or time-only fields
- unless current_adapter?(:OracleAdapter, :DB2Adapter)
- assert_equal "false", xml.elements["//approved"].text
- assert_equal "boolean" , xml.elements["//approved"].attributes['type']
-
- assert_equal bonus_time_in_current_timezone, xml.elements["//bonus-time"].text
- assert_equal "datetime" , xml.elements["//bonus-time"].attributes['type']
- end
- end
-
- def test_to_xml_skipping_attributes
- xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :except => [:title, :replies_count])
- assert_equal "<topic>", xml.first(7)
- assert !xml.include?(%(<title>The First Topic</title>))
- assert xml.include?(%(<author-name>David</author-name>))
-
- xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :except => [:title, :author_name, :replies_count])
- assert !xml.include?(%(<title>The First Topic</title>))
- assert !xml.include?(%(<author-name>David</author-name>))
- end
-
- def test_to_xml_including_has_many_association
- xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :include => :replies, :except => :replies_count)
- assert_equal "<topic>", xml.first(7)
- assert xml.include?(%(<replies type="array"><reply>))
- assert xml.include?(%(<title>The Second Topic of the day</title>))
- end
-
- def test_array_to_xml_including_has_many_association
- xml = [ topics(:first), topics(:second) ].to_xml(:indent => 0, :skip_instruct => true, :include => :replies)
- assert xml.include?(%(<replies type="array"><reply>))
- end
-
- def test_array_to_xml_including_methods
- xml = [ topics(:first), topics(:second) ].to_xml(:indent => 0, :skip_instruct => true, :methods => [ :topic_id ])
- assert xml.include?(%(<topic-id type="integer">#{topics(:first).topic_id}</topic-id>)), xml
- assert xml.include?(%(<topic-id type="integer">#{topics(:second).topic_id}</topic-id>)), xml
- end
-
- def test_array_to_xml_including_has_one_association
- xml = [ companies(:first_firm), companies(:rails_core) ].to_xml(:indent => 0, :skip_instruct => true, :include => :account)
- assert xml.include?(companies(:first_firm).account.to_xml(:indent => 0, :skip_instruct => true))
- assert xml.include?(companies(:rails_core).account.to_xml(:indent => 0, :skip_instruct => true))
- end
-
- def test_array_to_xml_including_belongs_to_association
- xml = [ companies(:first_client), companies(:second_client), companies(:another_client) ].to_xml(:indent => 0, :skip_instruct => true, :include => :firm)
- assert xml.include?(companies(:first_client).to_xml(:indent => 0, :skip_instruct => true))
- assert xml.include?(companies(:second_client).firm.to_xml(:indent => 0, :skip_instruct => true))
- assert xml.include?(companies(:another_client).firm.to_xml(:indent => 0, :skip_instruct => true))
- end
-
- def test_to_xml_including_belongs_to_association
- xml = companies(:first_client).to_xml(:indent => 0, :skip_instruct => true, :include => :firm)
- assert !xml.include?("<firm>")
-
- xml = companies(:second_client).to_xml(:indent => 0, :skip_instruct => true, :include => :firm)
- assert xml.include?("<firm>")
- end
-
- def test_to_xml_including_multiple_associations
- xml = companies(:first_firm).to_xml(:indent => 0, :skip_instruct => true, :include => [ :clients, :account ])
- assert_equal "<firm>", xml.first(6)
- assert xml.include?(%(<account>))
- assert xml.include?(%(<clients type="array"><client>))
- end
-
- def test_to_xml_including_multiple_associations_with_options
- xml = companies(:first_firm).to_xml(
- :indent => 0, :skip_instruct => true,
- :include => { :clients => { :only => :name } }
- )
-
- assert_equal "<firm>", xml.first(6)
- assert xml.include?(%(<client><name>Summit</name></client>))
- assert xml.include?(%(<clients type="array"><client>))
- end
-
- def test_to_xml_including_methods
- xml = Company.new.to_xml(:methods => :arbitrary_method, :skip_instruct => true)
- assert_equal "<company>", xml.first(9)
- assert xml.include?(%(<arbitrary-method>I am Jack's profound disappointment</arbitrary-method>))
- end
-
- def test_to_xml_with_block
- value = "Rockin' the block"
- xml = Company.new.to_xml(:skip_instruct => true) do |_xml|
- _xml.tag! "arbitrary-element", value
- end
- assert_equal "<company>", xml.first(9)
- assert xml.include?(%(<arbitrary-element>#{value}</arbitrary-element>))
- end
-
def test_serialize_should_be_reversible
for format in FORMATS
@serialized = Contact.new.send("to_#{format}")
@@ -184,11 +49,4 @@ class SerializationTest < ActiveRecord::TestCase
assert_equal @contact_attributes[:awesome], contact.awesome, "For #{format}"
end
end
-
- def test_serialize_should_xml_skip_instruct_for_included_records
- @contact.alternative = Contact.new(:name => 'Copa Cabana')
- @serialized = @contact.to_xml(:include => [ :alternative ])
- assert_equal @serialized.index('<?xml '), 0
- assert_nil @serialized.index('<?xml ', 1)
- end
end
diff --git a/activerecord/test/cases/xml_serialization_test.rb b/activerecord/test/cases/xml_serialization_test.rb
index 756c8a32eb..88751a72f9 100644
--- a/activerecord/test/cases/xml_serialization_test.rb
+++ b/activerecord/test/cases/xml_serialization_test.rb
@@ -5,6 +5,9 @@ require 'models/author'
require 'models/comment'
require 'models/company_in_module'
require 'models/toy'
+require 'models/topic'
+require 'models/reply'
+require 'models/company'
class XmlSerializationTest < ActiveRecord::TestCase
def test_should_serialize_default_root
@@ -50,6 +53,23 @@ class XmlSerializationTest < ActiveRecord::TestCase
end
assert_match %r{<creator>David</creator>}, @xml
end
+
+ def test_to_xml_with_block
+ value = "Rockin' the block"
+ xml = Contact.new.to_xml(:skip_instruct => true) do |_xml|
+ _xml.tag! "arbitrary-element", value
+ end
+ assert_equal "<contact>", xml.first(9)
+ assert xml.include?(%(<arbitrary-element>#{value}</arbitrary-element>))
+ end
+
+ def test_should_skip_instruct_for_included_records
+ @contact = Contact.new
+ @contact.alternative = Contact.new(:name => 'Copa Cabana')
+ @xml = @contact.to_xml(:include => [ :alternative ])
+ assert_equal @xml.index('<?xml '), 0
+ assert_nil @xml.index('<?xml ', 1)
+ end
end
class DefaultXmlSerializationTest < ActiveRecord::TestCase
@@ -148,7 +168,63 @@ class NilXmlSerializationTest < ActiveRecord::TestCase
end
class DatabaseConnectedXmlSerializationTest < ActiveRecord::TestCase
- fixtures :authors, :posts, :projects
+ fixtures :topics, :companies, :accounts, :authors, :posts, :projects
+
+ def test_to_xml
+ xml = REXML::Document.new(topics(:first).to_xml(:indent => 0))
+ bonus_time_in_current_timezone = topics(:first).bonus_time.xmlschema
+ written_on_in_current_timezone = topics(:first).written_on.xmlschema
+ last_read_in_current_timezone = topics(:first).last_read.xmlschema
+
+ assert_equal "topic", xml.root.name
+ assert_equal "The First Topic" , xml.elements["//title"].text
+ assert_equal "David" , xml.elements["//author-name"].text
+ assert_match "Have a nice day", xml.elements["//content"].text
+
+ assert_equal "1", xml.elements["//id"].text
+ assert_equal "integer" , xml.elements["//id"].attributes['type']
+
+ assert_equal "1", xml.elements["//replies-count"].text
+ assert_equal "integer" , xml.elements["//replies-count"].attributes['type']
+
+ assert_equal written_on_in_current_timezone, xml.elements["//written-on"].text
+ assert_equal "datetime" , xml.elements["//written-on"].attributes['type']
+
+ assert_equal "david@loudthinking.com", xml.elements["//author-email-address"].text
+
+ assert_equal nil, xml.elements["//parent-id"].text
+ assert_equal "integer", xml.elements["//parent-id"].attributes['type']
+ assert_equal "true", xml.elements["//parent-id"].attributes['nil']
+
+ if current_adapter?(:SybaseAdapter)
+ assert_equal last_read_in_current_timezone, xml.elements["//last-read"].text
+ assert_equal "datetime" , xml.elements["//last-read"].attributes['type']
+ else
+ # Oracle enhanced adapter allows to define Date attributes in model class (see topic.rb)
+ assert_equal "2004-04-15", xml.elements["//last-read"].text
+ assert_equal "date" , xml.elements["//last-read"].attributes['type']
+ end
+
+ # Oracle and DB2 don't have true boolean or time-only fields
+ unless current_adapter?(:OracleAdapter, :DB2Adapter)
+ assert_equal "false", xml.elements["//approved"].text
+ assert_equal "boolean" , xml.elements["//approved"].attributes['type']
+
+ assert_equal bonus_time_in_current_timezone, xml.elements["//bonus-time"].text
+ assert_equal "datetime" , xml.elements["//bonus-time"].attributes['type']
+ end
+ end
+
+ def test_except_option
+ xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :except => [:title, :replies_count])
+ assert_equal "<topic>", xml.first(7)
+ assert !xml.include?(%(<title>The First Topic</title>))
+ assert xml.include?(%(<author-name>David</author-name>))
+
+ xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :except => [:title, :author_name, :replies_count])
+ assert !xml.include?(%(<title>The First Topic</title>))
+ assert !xml.include?(%(<author-name>David</author-name>))
+ end
# to_xml used to mess with the hash the user provided which
# caused the builder to be reused. This meant the document kept
@@ -184,6 +260,39 @@ class DatabaseConnectedXmlSerializationTest < ActiveRecord::TestCase
assert_match %r{<hello-post>}, xml
end
+ def test_including_has_many_association
+ xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :include => :replies, :except => :replies_count)
+ assert_equal "<topic>", xml.first(7)
+ assert xml.include?(%(<replies type="array"><reply>))
+ assert xml.include?(%(<title>The Second Topic of the day</title>))
+ end
+
+ def test_including_belongs_to_association
+ xml = companies(:first_client).to_xml(:indent => 0, :skip_instruct => true, :include => :firm)
+ assert !xml.include?("<firm>")
+
+ xml = companies(:second_client).to_xml(:indent => 0, :skip_instruct => true, :include => :firm)
+ assert xml.include?("<firm>")
+ end
+
+ def test_including_multiple_associations
+ xml = companies(:first_firm).to_xml(:indent => 0, :skip_instruct => true, :include => [ :clients, :account ])
+ assert_equal "<firm>", xml.first(6)
+ assert xml.include?(%(<account>))
+ assert xml.include?(%(<clients type="array"><client>))
+ end
+
+ def test_including_association_with_options
+ xml = companies(:first_firm).to_xml(
+ :indent => 0, :skip_instruct => true,
+ :include => { :clients => { :only => :name } }
+ )
+
+ assert_equal "<firm>", xml.first(6)
+ assert xml.include?(%(<client><name>Summit</name></client>))
+ assert xml.include?(%(<clients type="array"><client>))
+ end
+
def test_methods_are_called_on_object
xml = authors(:david).to_xml :methods => :label, :indent => 0
assert_match %r{<label>.*</label>}, xml
@@ -265,4 +374,27 @@ class DatabaseConnectedXmlSerializationTest < ActiveRecord::TestCase
assert_equal array.size, array.select { |author| author.has_key? 'firstname' }.size
end
+ def test_array_to_xml_including_has_many_association
+ xml = [ topics(:first), topics(:second) ].to_xml(:indent => 0, :skip_instruct => true, :include => :replies)
+ assert xml.include?(%(<replies type="array"><reply>))
+ end
+
+ def test_array_to_xml_including_methods
+ xml = [ topics(:first), topics(:second) ].to_xml(:indent => 0, :skip_instruct => true, :methods => [ :topic_id ])
+ assert xml.include?(%(<topic-id type="integer">#{topics(:first).topic_id}</topic-id>)), xml
+ assert xml.include?(%(<topic-id type="integer">#{topics(:second).topic_id}</topic-id>)), xml
+ end
+
+ def test_array_to_xml_including_has_one_association
+ xml = [ companies(:first_firm), companies(:rails_core) ].to_xml(:indent => 0, :skip_instruct => true, :include => :account)
+ assert xml.include?(companies(:first_firm).account.to_xml(:indent => 0, :skip_instruct => true))
+ assert xml.include?(companies(:rails_core).account.to_xml(:indent => 0, :skip_instruct => true))
+ end
+
+ def test_array_to_xml_including_belongs_to_association
+ xml = [ companies(:first_client), companies(:second_client), companies(:another_client) ].to_xml(:indent => 0, :skip_instruct => true, :include => :firm)
+ assert xml.include?(companies(:first_client).to_xml(:indent => 0, :skip_instruct => true))
+ assert xml.include?(companies(:second_client).firm.to_xml(:indent => 0, :skip_instruct => true))
+ assert xml.include?(companies(:another_client).firm.to_xml(:indent => 0, :skip_instruct => true))
+ end
end
diff --git a/activerecord/test/models/comment.rb b/activerecord/test/models/comment.rb
index 43650c0427..88b139d931 100644
--- a/activerecord/test/models/comment.rb
+++ b/activerecord/test/models/comment.rb
@@ -7,10 +7,13 @@ class Comment < ActiveRecord::Base
:joins => :post,
:conditions => { "posts.author_id" => 1 }
scope :created
-
+
belongs_to :post, :counter_cache => true
has_many :ratings
+ has_many :children, :class_name => 'Comment', :foreign_key => :parent_id
+ belongs_to :parent, :class_name => 'Comment', :counter_cache => :children_count
+
def self.what_are_you
'a comment...'
end
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index e6cd274586..64e0452100 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -155,6 +155,7 @@ ActiveRecord::Schema.define do
end
t.string :type
t.integer :taggings_count, :default => 0
+ t.integer :children_count, :default => 0
end
create_table :companies, :force => true do |t|