Permalink
Browse files

Added association inclusion in to_xml [DHH]

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@3831 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
  • Loading branch information...
1 parent 0c6d178 commit db37c0c95fea1ddb3a34665f82d3cba9bced49f8 @dhh dhh committed Mar 10, 2006
Showing with 111 additions and 15 deletions.
  1. +27 −0 activerecord/CHANGELOG
  2. +41 −10 activerecord/lib/active_record/base.rb
  3. +43 −5 activerecord/test/base_test.rb
View
@@ -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]
@@ -1418,8 +1418,24 @@ def attributes=(new_attributes)
# 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 @@ def readonly!
# 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_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_to_be_xmled.to_xml(options)
+ attributes_for_xml.to_xml(options)
end
private
@@ -1155,12 +1155,12 @@ def test_assert_queries
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 @@ def test_to_xml
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.

0 comments on commit db37c0c

Please sign in to comment.