/address_book_transformer.rb Secret
Created
April 7, 2010 13:46
Star
You must be signed in to star a gist
Solution to RPCFN #8: http://rubylearning.com/blog/2010/04/07/rpcfn-xml-transformer-8, to run do "ruby transformer_test.rb"
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class AddressBookTransformer < Transformer | |
transform_with do | |
person "//address_book/contact/name" do | |
first_name "first" | |
last_name "last" | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class EmployeesTransformer < Transformer | |
transform_with do | |
person "//employees/employee" do | |
first_name "first_name" | |
last_name "surname" | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class PeopleTransformer < Transformer | |
transform_with do | |
person "//people/person" do | |
first_name "first_name" | |
last_name "last_name" | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Person | |
attr_accessor :first_name | |
attr_accessor :last_name | |
def initialize(attributes={}) | |
attributes.each do |k,v| | |
send("#{k}=", v) | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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 file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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