require File.dirname(__FILE__) + '/spec_helper.rb'
describe ROXML, "#from_xml" do
describe "from_xml call", :shared => true do
it "should fetch values" do
book = BookWithContributors.from_xml(@path)
book.title.should == "Programming Ruby - 2nd Edition"
book.contributors.map(&:name).should == ["David Thomas","Andrew Hunt","Chad Fowler"]
end
end
context "called with PathName" do
before do
@path = Pathname.new(fixture_path(:book_with_contributors))
end
it_should_behave_like "from_xml call"
end
context "called with File" do
before do
@path = File.new(fixture_path(:book_with_contributors))
end
it_should_behave_like "from_xml call"
end
context "called with URI" do
before do
require 'uri'
@path = URI.parse("file://#{File.expand_path(File.expand_path(fixture_path(:book_with_contributors)))}")
end
it_should_behave_like "from_xml call"
end
end
describe ROXML, "#xml" do
class DescriptionReadonly
include ROXML
xml_reader :writable, :from => :content
xml_reader :readonly, :from => :content, :frozen => true
end
class Contributor
include ROXML
xml_reader :role, :from => :attr
xml_reader :name
end
class BookWithContributions
include ROXML
xml_name :book
xml_reader :isbn, :from => :attr
xml_reader :title
xml_reader :description, DescriptionReadonly
xml_reader :contributions, [Contributor], :from => 'contributor', :in => "contributions"
end
class BookWithContributionsReadonly
include ROXML
xml_name :book
xml_reader :isbn, :from => :attr, :frozen => true
xml_reader :title, :frozen => true
xml_reader :description, DescriptionReadonly, :frozen => true
xml_reader :contributions, [Contributor], :from => 'contributor', :in => "contributions", :frozen => true
end
before do
@writable = BookWithContributions.from_xml(fixture(:book_with_contributions))
@readonly = BookWithContributionsReadonly.from_xml(fixture(:book_with_contributions))
end
it "should raise on duplicate accessor name" do
proc do
Class.new do
include ROXML
xml_reader :id
xml_accessor :id
end
end.should raise_error(RuntimeError)
end
class OctalInteger
def self.from_xml(val)
new(Integer(val.content))
end
def initialize(value)
@val = value
end
def ==(other)
@val == other
end
def to_xml
sprintf("%#o", @val)
end
end
describe "overriding output" do
class BookWithOctalPages
include ROXML
xml_accessor :pages_with_as, :as => Integer, :to_xml => proc {|val| sprintf("%#o", val) }, :required => true
xml_accessor :pages_with_type, OctalInteger, :required => true
end
# to_xml_test :book_with_octal_pages
describe "with :to_xml option" do
it "should output with to_xml filtering"
end
describe "with #to_xml on the object" do
it "should output with to_xml filtering"
end
end
describe "overriding input" do
before do
@book_with_octal_pages_xml = %{
0357
}
@expected_pages = 239
end
describe "with :as block shorthand" do
class BookWithOctalPagesBlockShorthand
include ROXML
xml_accessor :pages, :as => Integer, :required => true
end
it "should apply filtering on input" do
book = BookWithOctalPagesBlockShorthand.from_xml(@book_with_octal_pages_xml)
book.pages.should == @expected_pages
end
end
describe "with #from_xml defined on the object" do
class BookWithOctalPagesType
include ROXML
xml_accessor :pages, OctalInteger, :required => true
end
it "should apply filtering on input" do
book = BookWithOctalPagesType.from_xml(@book_with_octal_pages_xml)
book.pages.should == @expected_pages
end
end
end
describe "attribute reference" do
before do
@frozen = @readonly.isbn
@unfrozen = @writable.isbn
end
it_should_behave_like "freezable xml reference"
end
describe "text reference" do
before do
@frozen = @readonly.title
@unfrozen = @writable.title
end
it_should_behave_like "freezable xml reference"
end
describe "object reference" do
before do
@frozen = @readonly.description
@unfrozen = @writable.description
end
it_should_behave_like "freezable xml reference"
describe "indirect reference via an object" do
it "does not inherit the frozen status from its parent" do
@frozen.writable.frozen?.should be_false
@frozen.readonly.frozen?.should be_true
@unfrozen.writable.frozen?.should be_false
@unfrozen.readonly.frozen?.should be_true
end
end
end
describe "array reference" do
before do
@frozen = @readonly.contributions
@unfrozen = @writable.contributions
end
it_should_behave_like "freezable xml reference"
it "should apply :frozen to the constituent elements" do
@frozen.all?(&:frozen?).should be_true
@unfrozen.any?(&:frozen?).should be_false
end
context "no elements are present in root, no :in is specified" do
class BookWithContributors
include ROXML
xml_name :book
xml_reader :isbn, :from => :attr
xml_reader :title
xml_reader :description
xml_reader :contributors, [Contributor]
end
it "should look for elements :in the plural of name" do
book = BookWithContributors.from_xml(%{
David Thomas
Andrew Hunt
Chad Fowler
})
book.contributors.map(&:name).sort.should == ["David Thomas","Andrew Hunt","Chad Fowler"].sort
end
end
end
describe "hash reference" do
class DictionaryOfGuardedNames
include ROXML
xml_name :dictionary
xml_reader :definitions, {:key => :name,
:value => :content}, :in => :definitions
end
class DictionaryOfGuardedNamesReadonly
include ROXML
xml_name :dictionary
xml_reader :definitions, {:key => :name,
:value => :content}, :in => :definitions, :frozen => true
end
before do
@frozen = DictionaryOfGuardedNamesReadonly.from_xml(fixture(:dictionary_of_guarded_names)).definitions
@unfrozen = DictionaryOfGuardedNames.from_xml(fixture(:dictionary_of_guarded_names)).definitions
end
it_should_behave_like "freezable xml reference"
it "should have frozen keys, as with all hashes" do
@frozen.keys.all?(&:frozen?).should be_true
@unfrozen.keys.all?(&:frozen?).should be_true
end
it "should apply :frozen to the constituent values" do
@frozen.values.all?(&:frozen?).should be_true
@unfrozen.values.any?(&:frozen?).should be_false
end
end
end
describe ROXML, "inheritance" do
class Book
include ROXML
xml_accessor :isbn, :attr => 'ISBN'
xml_reader :title
xml_reader :description, :as => :cdata
xml_reader :author
xml_accessor :pages, :text => 'pagecount', :as => Integer
end
class Measurement
include ROXML
xml_reader :units, :from => :attr
xml_reader :value, :from => :content, :as => Float
def initialize(value = 0, units = 'pixels')
@value = Float(value)
@units = units.to_s
normalize_hundredths
end
def to_s
"#{value} #{units}"
end
def ==(other)
other.units == @units && other.value == @value
end
private
def after_parse
normalize_hundredths
end
def normalize_hundredths
if @units.starts_with? 'hundredths-'
@value /= 100
@units = @units.split('hundredths-')[1]
end
end
end
class InheritedBookWithDepth < Book
xml_reader :depth, Measurement
end
before do
@book_xml = %{
The PickAxe
David Thomas, Andrew Hunt, Dave Thomas
1130
Pragmattic Programmers
}
@parent = Book.from_xml(@book_xml)
@child = InheritedBookWithDepth.from_xml(@book_xml)
end
describe "parent" do
it "should include its attributes" do
@child.isbn.should == "0201710897"
@child.title.should == "The PickAxe"
@child.description.should == "Probably the best Ruby book out there"
@child.author.should == 'David Thomas, Andrew Hunt, Dave Thomas'
@child.pages.should == nil
end
it "should not include its child's attributes" do
@parent.should_not respond_to(:depth)
end
end
describe "child" do
it "should include its parent's attributes" do
@child.isbn.should == @parent.isbn
@child.title.should == @parent.title
@child.description.should == @parent.description
@child.author.should == @parent.author
@child.pages.should == @parent.pages
end
it "should include its attributes" do
@child.depth.to_s.should == '11.3 meters'
end
it "should include parent's attributes added after declaration" do
Book.class_eval do
xml_reader :publisher, :require => true
end
book = InheritedBookWithDepth.from_xml(@book_xml)
book.publisher.should == "Pragmattic Programmers"
end
end
end