Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Implemented XML parsing.

  • Loading branch information...
commit 4713de129cf7e9aa62feaa6a8be1e37c207b4f37 1 parent c1628a0
@tylerhunt authored
View
4 README.rdoc
@@ -1,6 +1,6 @@
-= relief
+= Relief
-Description goes here.
+An XML to Hash Ruby parser DSL.
== Copyright
View
4 lib/relief.rb
@@ -0,0 +1,4 @@
+module Relief
+ autoload :Parser, 'lib/relief/parser'
+ autoload :Element, 'lib/relief/element'
+end
View
61 lib/relief/element.rb
@@ -0,0 +1,61 @@
+module Relief
+ class Element
+ attr_reader :name, :options, :children
+
+ def initialize(name, options, &block)
+ @name = name
+ @options = options
+ @children = {}
+
+ instance_eval(&block) if block_given?
+ end
+
+ def parse(document)
+ @children.inject({}) do |values, child|
+ name, element = child
+
+ values[name] = begin
+ target = (document / element.xpath)
+
+ parse_node = lambda { |target|
+ element.children.any? ? element.parse(target) : target.to_s
+ }
+
+ if element.options[:collection]
+ target.collect { |child| parse_node.call(child) }
+ else
+ parse_node.call(target)
+ end
+ end
+
+ values
+ end
+ end
+
+ def element(name, options={}, &block)
+ options[:xpath] ||= name if name =~ %r([/.])
+ name = options[:as].to_sym if options.has_key?(:as)
+ @children[name] ||= self.class.new(name, options, &block)
+ end
+
+ def elements(name, options={}, &block)
+ element(name, options.merge(:collection => true), &block)
+ end
+
+ def attribute(name, options={}, &block)
+ element(name, options.merge(:attribute => true), &block)
+ end
+
+ def xpath
+ if options.has_key?(:xpath)
+ options[:xpath]
+ elsif @children.empty?
+ attribute = @options[:attribute]
+ attribute = name if attribute == true
+ !attribute ? "#{name}/text()" : "@#{attribute}"
+ else
+ name.to_s
+ end
+ end
+ end
+end
View
20 lib/relief/parser.rb
@@ -0,0 +1,20 @@
+gem 'nokogiri', '~> 1.2.3'
+require 'nokogiri'
+
+module Relief
+ class Parser
+ attr_reader :root
+
+ def initialize(name, options={}, &block)
+ @root = Element.new(name, options, &block)
+ end
+
+ def parse(document)
+ unless document.is_a?(Nokogiri::XML::NodeSet)
+ document = Nokogiri::XML(document)
+ end
+
+ @root.parse(document)
+ end
+ end
+end
View
126 spec/parser_spec.rb
@@ -0,0 +1,126 @@
+require File.join(File.dirname(__FILE__), 'spec_helper')
+
+describe Relief::Parser do
+ it "parses elements" do
+ parser = Relief::Parser.new(:photo) do
+ element :name
+ element :url
+ end
+
+ photo = parser.parse(<<-XML)
+ <?xml version="1.0" encoding="UTF-8"?>
+ <photo>
+ <name>Cucumbers</name>
+ <url>/photos/cucumbers.jpg</url>
+ </photo>
+ XML
+
+ photo.should == { :name => 'Cucumbers', :url => '/photos/cucumbers.jpg' }
+ end
+
+ it "parses collections of elements" do
+ parser = Relief::Parser.new(:photos) do
+ elements :photo do
+ element :name
+ element :url
+ end
+ end
+
+ photos = parser.parse(<<-XML)
+ <?xml version="1.0" encoding="UTF-8"?>
+ <photos>
+ <photo>
+ <name>Cucumbers</name>
+ <url>/photos/cucumbers.jpg</url>
+ </photo>
+ <photo>
+ <name>Lemons</name>
+ <url>/photos/lemons.jpg</url>
+ </photo>
+ </photos>
+ XML
+
+ photos.should == {
+ :photo => [
+ { :name => 'Cucumbers', :url => '/photos/cucumbers.jpg' },
+ { :name => 'Lemons', :url => '/photos/lemons.jpg' }
+ ]
+ }
+ end
+
+ it "parses attributes" do
+ parser = Relief::Parser.new(:photos) do
+ elements :photo do
+ attribute :name
+ attribute :url
+ end
+ end
+
+ photos = parser.parse(<<-XML)
+ <?xml version="1.0" encoding="UTF-8"?>
+ <photos>
+ <photo name="Cucumbers" url="/photos/cucumbers.jpg" />
+ <photo name="Lemons" url="/photos/lemons.jpg" />
+ </photos>
+ XML
+
+ photos.should == {
+ :photo => [
+ { :name => 'Cucumbers', :url => '/photos/cucumbers.jpg' },
+ { :name => 'Lemons', :url => '/photos/lemons.jpg' }
+ ]
+ }
+ end
+
+ it "parses elements by XPath" do
+ parser = Relief::Parser.new(:photos) do
+ elements '//photo', :as => :photo do
+ element 'name/text()', :as => :name
+ element 'url/text()', :as => :url
+ end
+ end
+
+ photos = parser.parse(<<-XML)
+ <?xml version="1.0" encoding="UTF-8"?>
+ <photos>
+ <photo>
+ <name>Cucumbers</name>
+ <url>/photos/cucumbers.jpg</url>
+ </photo>
+ <photo>
+ <name>Lemons</name>
+ <url>/photos/lemons.jpg</url>
+ </photo>
+ </photos>
+ XML
+
+ photos.should == {
+ :photo => [
+ { :name => 'Cucumbers', :url => '/photos/cucumbers.jpg' },
+ { :name => 'Lemons', :url => '/photos/lemons.jpg' }
+ ]
+ }
+ end
+
+ it "parses elements by nested XPath" do
+ parser = Relief::Parser.new(:photos) do
+ elements '//name/text()', :as => :name
+ end
+
+ photos = parser.parse(<<-XML)
+ <?xml version="1.0" encoding="UTF-8"?>
+ <photos>
+ <photo>
+ <name>Cucumbers</name>
+ <url>/photos/cucumbers.jpg</url>
+ </photo>
+ <photo>
+ <name>Lemons</name>
+ <url>/photos/lemons.jpg</url>
+ </photo>
+ </photos>
+ XML
+
+ photos.should == { :name => ['Cucumbers', 'Lemons'] }
+ end
+end
View
7 spec/relief_spec.rb
@@ -1,7 +0,0 @@
-require 'spec_helper'
-
-describe "Relief" do
- it "fails" do
- fail "hey buddy, you should probably rename this file and start specing for real"
- end
-end
View
11 spec/spec_helper.rb
@@ -1,9 +1,8 @@
+$:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
+
+require 'rubygems'
+
+gem 'rspec', '~> 1.2.4'
require 'spec'
-$LOAD_PATH.unshift(File.dirname(__FILE__))
-$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
require 'relief'
-
-Spec::Runner.configure do |config|
-
-end
Please sign in to comment.
Something went wrong with that request. Please try again.