Skip to content

Commit

Permalink
Merge pull request #7 from x12ruby/use_nokogiri
Browse files Browse the repository at this point in the history
Use nokogiri
  • Loading branch information
swalberg committed Jan 17, 2014
2 parents 728ee7f + a06950d commit 50b4072
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 36 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
.bundle
Gemfile.lock
pkg/*
.DS_Store
2 changes: 2 additions & 0 deletions lib/x12.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
# $Id: X12.rb 91 2009-05-13 22:11:10Z ikk $
#
# Package implementing direct manipulation of X12 structures using Ruby syntax.
require 'nokogiri'

require "x12/version"
require 'x12/base'
require 'x12/empty'
Expand Down
8 changes: 0 additions & 8 deletions lib/x12/parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,6 @@
#++
#

require "rexml/document"

require 'pp'

module X12

# $Id: Parser.rb 89 2009-05-13 19:36:20Z ikk $
Expand Down Expand Up @@ -73,7 +69,6 @@ def initialize(file_name)

# Populate fields in all segments found in all the loops
@x12_definition[X12::Loop].each_pair{|k, v|
#puts "Populating definitions for loop #{k}"
process_loop(v)
} if @x12_definition[X12::Loop]

Expand All @@ -88,13 +83,11 @@ def initialize(file_name)
}
end

#puts PP.pp(self, '')
end # initialize

# Parse a loop of a given name out of a string. Throws an exception if the loop name is not defined.
def parse(loop_name, str)
loop = @x12_definition[X12::Loop][loop_name]
#puts "Loops to parse #{@x12_definition[X12::Loop].keys}"
throw Exception.new("Cannot find a definition for loop #{loop_name}") unless loop
loop = loop.dup
loop.parse(str)
Expand Down Expand Up @@ -128,7 +121,6 @@ def process_loop(loop)

# Instantiate segment's fields as previously defined
def process_segment(segment)
#puts "Trying to process segment #{segment.inspect}"
unless @x12_definition[X12::Segment] && @x12_definition[X12::Segment][segment.name]
# Try to find it in a separate file if missing from the @x12_definition structure
initialize(File.join(@dir_name, segment.name+'.xml'))
Expand Down
37 changes: 21 additions & 16 deletions lib/x12/xmldefinitions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@
#++
#

require "rexml/document"

module X12

# $Id: XMLDefinitions.rb 90 2009-05-13 19:51:27Z ikk $
Expand All @@ -34,10 +32,9 @@ class XMLDefinitions < Hash

# Parse definitions out of XML file
def initialize(str)
doc = REXML::Document.new(str)
doc = Nokogiri.XML(str)
definitions = doc.root.name =~ /^Definition$/i ? doc.root.elements.to_a : [doc.root]
definitions.each { |element|
#puts element.name
syntax_element = case element.name
when /table/i
parse_table(element)
Expand Down Expand Up @@ -104,13 +101,21 @@ def parse_int(s)
end #parse_int

def parse_attributes(e)
throw Exception.new("No name attribute found for : #{e.inspect}") unless name = e.attributes["name"]
throw Exception.new("Cannot parse attribute 'min' for: #{e.inspect}") unless min = parse_int(e.attributes["min"])
throw Exception.new("Cannot parse attribute 'max' for: #{e.inspect}") unless max = parse_int(e.attributes["max"])
throw Exception.new("Cannot parse attribute 'type' for: #{e.inspect}") unless type = parse_type(e.attributes["type"])
throw Exception.new("Cannot parse attribute 'required' for: #{e.inspect}") if (required = parse_boolean(e.attributes["required"])).nil?

validation = e.attributes["validation"]
throw Exception.new("No name attribute found for : #{e.inspect}") unless name = e.attributes["name"].content
min = parse_int(e.attributes.has_key?("min") ? e.attributes["min"].content : nil )
throw Exception.new("Cannot parse attribute 'min' for: #{e.inspect}") unless min

max = parse_int(e.attributes.has_key?("max") ? e.attributes["max"].content : nil )
throw Exception.new("Cannot parse attribute 'max' for: #{e.inspect}") unless max

type = parse_type(e.attributes.has_key?("type") ? e.attributes["type"].content : nil )
throw Exception.new("Cannot parse attribute 'type' for: #{e.inspect}") unless type

required = parse_boolean(e.attributes.has_key?("required") ? e.attributes["required"].content : nil )
throw Exception.new("Cannot parse attribute 'required' for: #{e.inspect}") if required.nil?

validation = parse_boolean(e.attributes.has_key?("validation") ? e.attributes["validation"].content : nil )

min = 1 if required and min < 1
max = 999999 if max == 0

Expand All @@ -124,7 +129,7 @@ def parse_field(e)
# double quotes
const_field = e.attributes["const"]
if(const_field)
type = "\"#{const_field}\""
type = "\"#{const_field.content}\""
end

Field.new(name, type, required, min, max, validation)
Expand All @@ -133,8 +138,8 @@ def parse_field(e)
def parse_table(e)
name, min, max, type, required, validation = parse_attributes(e)

content = e.get_elements("Entry").inject({}) {|t, entry|
t[entry.attributes["name"]] = entry.attributes["value"]
content = e.search("Entry").inject({}) {|t, entry|
t[entry.attributes["name"].content] = entry.attributes["value"].content
t
}
Table.new(name, content)
Expand All @@ -143,7 +148,7 @@ def parse_table(e)
def parse_segment(e)
name, min, max, type, required, validation = parse_attributes(e)

fields = e.get_elements("Field").inject([]) {|f, field|
fields = e.search("Field").inject([]) {|f, field|
f << parse_field(field)
}
Segment.new(name, fields, Range.new(min, max))
Expand All @@ -152,7 +157,7 @@ def parse_segment(e)
def parse_composite(e)
name, min, max, type, required, validation = parse_attributes(e)

fields = e.get_elements("Field").inject([]) {|f, field|
fields = e.search("Field").inject([]) {|f, field|
f << parse_field(field)
}
Composite.new(name, fields)
Expand Down
92 changes: 80 additions & 12 deletions spec/classes/xml_definitions_spec.rb
Original file line number Diff line number Diff line change
@@ -1,29 +1,97 @@
require 'spec_helper'
describe X12::XMLDefinitions do
describe "#initialize" do
context "a simple document with an embedded loop" do
let(:something) { double(name: :something) }

it "parses the next tag" do
obj = X12::XMLDefinitions.new('<Definition><Loop name="test" comment="test"></Loop></Definition>')
context "where the root is a definition" do
it "parses the loop element" do
described_class.any_instance.should_receive(:parse_loop).and_return(something)
described_class.new('<Definition><Loop name="test" comment="test"></Loop></Definition>')
end
end

context "where the root is not a definition" do

it "parses the loop element" do
described_class.any_instance.should_receive(:parse_loop).and_return(something)
described_class.new('<Loop name="test" comment="test"></Loop>')
end
end

context "a loop" do

subject { described_class.new('<Definition><Loop name="test" comment="test"></Loop></Definition>') }

obj.keys.size.should == 1
context "containing another loop" do
subject { described_class.new('<Loop name="outer"><Loop name="inner" comment="test"></Loop></Loop>') }

it "parses the outer loop" do
expect(subject[X12::Loop].keys).to eq [ "outer" ]
end

it "parses the inner loop" do
inner = subject[X12::Loop]["outer"].nodes
expect(inner.first).to be_an_instance_of X12::Loop
end
end

context "containing a segment" do
subject { described_class.new('<Loop name="outer"><Segment name="inner" comment="test" /></Loop>') }

it "parses the outer loop" do
expect(subject[X12::Loop].keys).to eq [ "outer" ]
end

it "parses the inner loop" do
inner = subject[X12::Loop]["outer"].nodes
expect(inner.first.class).to eq X12::Segment
end
end

end
end

describe "#loop" do
let(:loop) { REXML::Document.new('<loop name="test" comment="test"></loop>') }
context "a table" do
subject { described_class.new('<Table name="test"><Entry name="A" value="something"/><Entry name="B" value="another" /></Table>') }

it "parses the table" do
expect(subject[X12::Table].keys).to eq [ "test" ]
end

it "parses the entries" do
table = subject[X12::Table]["test"]

expect(table).to eq({"A" => "something", "B" => "another"})
end
end

subject { X12::XMLDefinitions.new("<Definition />") }
context "a segment" do
subject { described_class.new('<Segment name="ST" required="y" max="1"><Field name="field1" const="270" comment="field"/></Segment>') }

it "parses the parameters out of a loop" do
#parse = subject.loop(loop)
it "parses the segment" do
expect(subject[X12::Segment].keys).to eq [ "ST" ]
end

#parse.class.should == Loop
it "parses the fields" do
segment = subject[X12::Segment]["ST"]

expect(segment.nodes.map(&:class)).to eq [ X12::Field ]
end
end

end
context "a composite" do
subject { described_class.new('<Composite name="C043" comment="foo"><Field name="field1" min="1" max="30" comment="fieldcomment"/></Composite>') }

it "parses the composite" do
expect(subject[X12::Composite].keys).to eq [ "C043" ]
end

it "parses the fields" do

composite = subject[X12::Composite]["C043"]

expect(composite.nodes.map(&:class)).to eq [ X12::Field ]
end
end

end
end
3 changes: 3 additions & 0 deletions x12.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@ Gem::Specification.new do |s|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
s.require_paths = ["lib"]

s.add_dependency 'nokogiri'

s.add_development_dependency('rdoc')
s.add_development_dependency('rspec')
s.add_development_dependency('awesome_print')
s.add_development_dependency('rake')
s.add_development_dependency('byebug') if RUBY_VERSION =~ /^2/
end

0 comments on commit 50b4072

Please sign in to comment.