Added association inclusion in to_xml [DHH]

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@3831 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
This commit is contained in:
David Heinemeier Hansson
2006-03-10 00:25:29 +00:00
parent 0c6d178552
commit db37c0c95f
3 changed files with 111 additions and 15 deletions

View File

@@ -36,6 +36,33 @@
<parent-id></parent-id>
<last-read type="date">2004-04-15</last-read>
</topic>
You can even do load first-level associations as part of the document:
firm.to_xml :include => [ :account, :clients ]
...that'll return something like:
<?xml version="1.0" encoding="UTF-8"?>
<firm>
<id type="integer">1</id>
<rating type="integer">1</rating>
<name>37signals</name>
<clients>
<client>
<rating type="integer">1</rating>
<name>Summit</name>
</client>
<client>
<rating type="integer">1</rating>
<name>Microsoft</name>
</client>
</clients>
<account>
<id type="integer">1</id>
<credit-limit type="integer">50</credit-limit>
</account>
</firm>
* Allow :counter_cache to take a column name for custom counter cache columns [Jamis Buck]

View File

@@ -1418,8 +1418,24 @@ module ActiveRecord #:nodoc:
# Returns a hash of all the attributes with their names as keys and clones of their objects as values.
def attributes
clone_attributes :read_attribute
def attributes(options = nil)
attributes = clone_attributes :read_attribute
if options.nil?
attributes
else
if except = options[:except]
except = Array(except).collect { |attribute| attribute.to_s }
except.each { |attribute_name| attributes.delete(attribute_name) }
attributes
elsif only = options[:only]
only = Array(only).collect { |attribute| attribute.to_s }
attributes.delete_if { |key, value| !only.include?(key) }
attributes
else
raise ArgumentError, "Options does not specify :except or :only (#{options.keys.inspect})"
end
end
end
# Returns a hash of cloned attributes before typecasting and deserialization.
@@ -1506,16 +1522,31 @@ module ActiveRecord #:nodoc:
# Turns this record into XML
def to_xml(options = {})
options[:skip_attributes] = Array(options[:skip_attributes])
options[:skip_attributes] << :type
options[:skip_attributes].collect! { |attribute| attribute.to_s }
attributes_to_be_xmled = attributes
options[:skip_attributes].each { |attribute_name| attributes_to_be_xmled.delete(attribute_name) }
options[:root] ||= self.class.to_s.underscore
options[:except] = Array(options[:except]) << self.class.inheritance_column unless options[:only]
only_or_except = { :only => options[:only], :except => options[:except] }
attributes_to_be_xmled.to_xml(options)
attributes_for_xml = attributes(only_or_except)
if include_associations = options.delete(:include)
for association in Array(include_associations)
case self.class.reflect_on_association(association).macro
when :has_many, :has_and_belongs_to_many
records = send(association).to_a
unless records.empty?
attributes_for_xml[association] = records.collect do |record|
record.attributes(only_or_except)
end
end
when :has_one, :belongs_to
if record = send(association)
attributes_for_xml[association] = record.attributes(only_or_except)
end
end
end
end
attributes_for_xml.to_xml(options)
end
private

View File

@@ -1155,12 +1155,12 @@ class BasicsTest < Test::Unit::TestCase
end
def test_to_xml
xml = Topic.find(:first).to_xml(:indent => 0, :skip_instruct => true)
xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true)
assert_equal "<topic>", xml.first(7)
assert xml.include?(%(<title>The First Topic</title>))
assert xml.include?(%(<author-name>David</author-name>))
assert xml.include?(%(<id type="integer">1</id>))
assert xml.include?(%(<approved type="boolean">false</approved>))
assert xml.include?(%(<approved type="boolean">false</approved>)), "Approved should be a boolean"
assert xml.include?(%(<replies-count type="integer">0</replies-count>))
assert xml.include?(%(<bonus-time type="datetime">2000-01-01 08:28:00</bonus-time>))
assert xml.include?(%(<written-on type="datetime">2003-07-16 09:28:00</written-on>))
@@ -1171,16 +1171,54 @@ class BasicsTest < Test::Unit::TestCase
end
def test_to_xml_skipping_attributes
xml = Topic.find(:first).to_xml(:indent => 0, :skip_instruct => true, :skip_attributes => :title)
breakpoint
xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :except => :title)
assert_equal "<topic>", xml.first(7)
assert !xml.include?(%(<title>The First Topic</title>))
assert xml.include?(%(<author-name>David</author-name>))
xml = Topic.find(:first).to_xml(:indent => 0, :skip_instruct => true, :skip_attributes => [ :title, :author_name ])
xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :except => [ :title, :author_name ])
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)
assert_equal "<topic>", xml.first(7)
assert xml.include?(%(<replies><reply>))
assert xml.include?(%(<title>The Second Topic's of the day</title>))
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><client>))
end
def test_except_attributes
assert_equal(
%w( author_name type id approved replies_count bonus_time written_on content author_email_address parent_id last_read),
topics(:first).attributes(:except => :title).keys
)
assert_equal(
%w( replies_count bonus_time written_on content author_email_address parent_id last_read),
topics(:first).attributes(:except => [ :title, :id, :type, :approved, :author_name ]).keys
)
end
def test_include_attributes
assert_equal(%w( title ), topics(:first).attributes(:only => :title).keys)
assert_equal(%w( title author_name type id approved ), topics(:first).attributes(:only => [ :title, :id, :type, :approved, :author_name ]).keys)
end
# FIXME: this test ought to run, but it needs to run sandboxed so that it
# doesn't b0rk the current test environment by undefing everything.