aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
authorDavid Heinemeier Hansson <david@loudthinking.com>2006-03-10 00:25:29 +0000
committerDavid Heinemeier Hansson <david@loudthinking.com>2006-03-10 00:25:29 +0000
commitdb37c0c95fea1ddb3a34665f82d3cba9bced49f8 (patch)
treeebc836f012aa248adfe9a9b7d8dd794bf777d92b /activerecord
parent0c6d1785529ab2f9c86908f03a10c871512311e5 (diff)
downloadrails-db37c0c95fea1ddb3a34665f82d3cba9bced49f8.tar.gz
rails-db37c0c95fea1ddb3a34665f82d3cba9bced49f8.tar.bz2
rails-db37c0c95fea1ddb3a34665f82d3cba9bced49f8.zip
Added association inclusion in to_xml [DHH]
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@3831 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/CHANGELOG27
-rwxr-xr-xactiverecord/lib/active_record/base.rb51
-rwxr-xr-xactiverecord/test/base_test.rb48
3 files changed, 111 insertions, 15 deletions
diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG
index c3fdbe38c6..5cde7f465b 100644
--- a/activerecord/CHANGELOG
+++ b/activerecord/CHANGELOG
@@ -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]
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 61f7e94d89..c84f1c7778 100755
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -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_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
diff --git a/activerecord/test/base_test.rb b/activerecord/test/base_test.rb
index d82bde433d..3ad2d6e960 100755
--- a/activerecord/test/base_test.rb
+++ b/activerecord/test/base_test.rb
@@ -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.