require_relative './spec_helper'
describe ROXML do
describe "::VERSION" do
it "should be equal to the VERSION file contents" do
expect(ROXML::VERSION).to eq(File.read('VERSION'))
end
end
describe "#from_xml" do
shared_examples_for "from_xml call" do
it "should fetch values" do
book = BookWithContributors.from_xml(@path)
expect(book.title).to eq("Programming Ruby - 2nd Edition")
expect(book.contributors.map(&:name)).to eq(["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
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, :as => DescriptionReadonly
xml_reader :contributions, :as => [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, :as => DescriptionReadonly, :frozen => true
xml_reader :contributions, :as => [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
expect do
Class.new do
include ROXML
xml_reader :id
xml_accessor :id
end
end.to 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, :as => 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 #from_xml defined on the object" do
class BookWithOctalPagesType
include ROXML
xml_accessor :pages, :as => OctalInteger, :required => true
end
it "should apply filtering on input" do
book = BookWithOctalPagesType.from_xml(@book_with_octal_pages_xml)
expect(book.pages).to eq(@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
expect(@frozen.writable.frozen?).to be_falsey
expect(@frozen.readonly.frozen?).to be_truthy
expect(@unfrozen.writable.frozen?).to be_falsey
expect(@unfrozen.readonly.frozen?).to be_truthy
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
expect(@frozen.all?(&:frozen?)).to be_truthy
expect(@unfrozen.any?(&:frozen?)).to be_falsey
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, :as => [Contributor]
end
it "should look for elements :in the plural of name" do
book = BookWithContributors.from_xml(%{
David Thomas
Andrew Hunt
Chad Fowler
})
expect(book.contributors.map(&:name).sort).to eq(["David Thomas","Andrew Hunt","Chad Fowler"].sort)
end
end
end
describe "hash reference" do
class DictionaryOfGuardedNames
include ROXML
xml_name :dictionary
xml_reader :definitions, :as => {:key => :name,
:value => :content}, :in => :definitions
end
class DictionaryOfGuardedNamesReadonly
include ROXML
xml_name :dictionary
xml_reader :definitions, :as => {: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
expect(@frozen.keys.all?(&:frozen?)).to be_truthy
expect(@unfrozen.keys.all?(&:frozen?)).to be_truthy
end
it "should apply :frozen to the constituent values" do
expect(@frozen.values.all?(&:frozen?)).to be_truthy
expect(@unfrozen.values.any?(&:frozen?)).to be_falsey
end
end
end
describe ROXML, "inheritance" do
class Book
include ROXML
xml_accessor :isbn, :from => '@ISBN'
xml_reader :title
xml_reader :description, :cdata => true
xml_reader :author
xml_accessor :pages, :from => '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, :as => 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
expect(@child.isbn).to eq("0201710897")
expect(@child.title).to eq("The PickAxe")
expect(@child.description).to eq("Probably the best Ruby book out there")
expect(@child.author).to eq('David Thomas, Andrew Hunt, Dave Thomas')
expect(@child.pages).to eq(nil)
end
it "should not include its child's attributes" do
expect(@parent).to_not respond_to(:depth)
end
end
describe "child" do
it "should include its parent's attributes" do
expect(@child.isbn).to eq(@parent.isbn)
expect(@child.title).to eq(@parent.title)
expect(@child.description).to eq(@parent.description)
expect(@child.author).to eq(@parent.author)
expect(@child.pages).to eq(@parent.pages)
end
it "should include its attributes" do
expect(@child.depth.to_s).to eq('11.3 meters')
end
it "should include parent's attributes added after declaration" do
Book.class_eval do
xml_reader :publisher, :required => true
end
book = InheritedBookWithDepth.from_xml(@book_xml)
expect(book.publisher).to eq("Pragmattic Programmers")
end
end
end