Skip to content

Instantly share code, notes, and snippets.

@pjb3
Created April 7, 2010 13:46
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save pjb3/9575443f97f8f09bf104 to your computer and use it in GitHub Desktop.
Solution to RPCFN #8: http://rubylearning.com/blog/2010/04/07/rpcfn-xml-transformer-8, to run do "ruby transformer_test.rb"
class AddressBookTransformer < Transformer
transform_with do
person "//address_book/contact/name" do
first_name "first"
last_name "last"
end
end
end
class EmployeesTransformer < Transformer
transform_with do
person "//employees/employee" do
first_name "first_name"
last_name "surname"
end
end
end
class People
def initialize(people=[])
@people = people
end
def <<(person)
@people << person
end
def empty?
@people.empty?
end
def to_xml
Nokogiri::XML::Builder.new do |xml|
xml.people do
@people.each do |p|
xml.person do
xml.name do
xml.first p.first_name
xml.last p.last_name
end
end
end
end
end.to_xml
end
end
class PeopleTransformer < Transformer
transform_with do
person "//people/person" do
first_name "first_name"
last_name "last_name"
end
end
end
class Person
attr_accessor :first_name
attr_accessor :last_name
def initialize(attributes={})
attributes.each do |k,v|
send("#{k}=", v)
end
end
end
<?xml version="1.0"?>
<people>
<person>
<name>
<first>Winnie</first>
<last>the Pooh</last>
</name>
</person>
<person>
<name>
<first>Jamie</first>
<last>the Weeh</last>
</name>
</person>
</people>
<?xml version="1.0"?>
<people>
<person>
<first_name>Winnie</first_name>
<last_name>the Pooh</last_name>
</person>
<person>
<first_name>Jamie</first_name>
<last_name>the Weeh</last_name>
</person>
</people>
<?xml version="1.0"?>
<address_book>
<contact>
<name>
<first>Winnie</first>
<last>the Pooh</last>
</name>
</contact>
<contact>
<name>
<first>Jamie</first>
<last>the Weeh</last>
</name>
</contact>
</address_book>
<?xml version="1.0"?>
<employees>
<employee>
<first_name>Winnie</first_name>
<surname>the Pooh</surname>
</employee>
<employee>
<first_name>Jamie</first_name>
<surname>the Weeh</surname>
</employee>
</employees>
# This class is kind of big and ugly, but it's let's us
# use a DSL to define transformers in subclasses
class Transformer
def self.transform_with
yield if block_given?
end
class << self
attr_accessor :person_xpath_query
attr_accessor :first_name_xpath_query
attr_accessor :last_name_xpath_query
end
def self.person(person_xpath_query)
self.person_xpath_query = person_xpath_query
yield if block_given?
end
def self.first_name(first_name_xpath_query)
self.first_name_xpath_query = first_name_xpath_query
end
def self.last_name(last_name_xpath_query)
self.last_name_xpath_query = last_name_xpath_query
end
def self.inherited(transformer)
(@transformers ||= []) << transformer
end
def self.transform(file_name)
doc = Nokogiri::XML(open(file_name))
@transformers.each do |transformer|
people = transformer.new(doc).transform
return people if people
end
nil
end
def initialize(file_name_or_doc)
@doc = if file_name_or_doc.is_a?(Nokogiri::XML::Document)
file_name_or_doc
else
Nokogiri::XML(open(file_name_or_doc))
end
end
def transform
person_nodes = @doc.xpath(self.class.person_xpath_query)
unless person_nodes.empty?
People.new(person_nodes.map do |node|
Person.new(
:first_name => node.xpath(self.class.first_name_xpath_query).first.content,
:last_name => node.xpath(self.class.last_name_xpath_query).first.content)
end).to_xml
end
end
end
require 'rubygems'
require 'nokogiri'
require 'transformer'
require 'people_transformer'
require 'address_book_transformer'
require 'employees_transformer'
require 'people'
require 'person'
require 'test/unit'
# You can call this API one of two ways. First, if you call
# Transformer.transform(file_name), it will try all transformers
# until it finds one that works. If you know ahead of time which
# transformer you want to use for a file, you can call
# PersonTransformer.new(file_name).transform, which will only try
# that specific transformer
class TransformerTest < Test::Unit::TestCase
def test_transform_people_with_transformer
assert_equal File.read("result.xml").chomp,
Transformer.transform("source1.xml").chomp
end
def test_transform_address_book_with_transformer
assert_equal File.read("result.xml").chomp,
Transformer.transform("source2.xml").chomp
end
def test_transform_employees_with_transformer
assert_equal File.read("result.xml").chomp,
Transformer.transform("source3.xml").chomp
end
def test_transform_people_with_people_transformer
assert_equal File.read("result.xml").chomp,
PeopleTransformer.new("source1.xml").transform.chomp
end
def test_transform_address_book_with_address_book_transformer
assert_equal File.read("result.xml").chomp,
AddressBookTransformer.new("source2.xml").transform.chomp
end
def test_transform_employees_with_employees_transformer
assert_equal File.read("result.xml").chomp,
EmployeesTransformer.new("source3.xml").transform.chomp
end
def test_transform_people_with_address_book_transformer
assert_nil AddressBookTransformer.new("source1.xml").transform
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment