Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove ability to parse symbols and yaml #34

Merged
merged 6 commits into from
Jan 11, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ Ideally, a bug report should include a pull request with failing specs.
7. Run `open coverage/index.html`. If your changes are not completely covered
by your tests, return to step 3.
8. Add documentation for your feature or bug fix.
9. Run `bundle exec rake yard`. If your changes are not 100% documented, go
9. Run `bundle exec rake doc:yard`. If your changes are not 100% documented, go
back to step 8.
10. Add, commit, and push your changes.
11. [Submit a pull request.][pr]
Expand Down
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,20 @@ use it like so:
```ruby
require 'multi_xml'

MultiXml.parser = :ox MultiXml.parser = MultiXml::Parsers::Ox # Same as above
MultiXml.parser = :ox
MultiXml.parser = MultiXml::Parsers::Ox # Same as above
MultiXml.parse('<tag>This is the contents</tag>') # Parsed using Ox

MultiXml.parser = :libxml MultiXml.parser = MultiXml::Parsers::Libxml # Same as above
MultiXml.parser = :libxml
MultiXml.parser = MultiXml::Parsers::Libxml # Same as above
MultiXml.parse('<tag>This is the contents</tag>') # Parsed using LibXML

MultiXml.parser = :nokogiri MultiXml.parser = MultiXml::Parsers::Nokogiri # Same as above
MultiXml.parser = :nokogiri
MultiXml.parser = MultiXml::Parsers::Nokogiri # Same as above
MultiXml.parse('<tag>This is the contents</tag>') # Parsed using Nokogiri

MultiXml.parser = :rexml MultiXml.parser = MultiXml::Parsers::Rexml # Same as above
MultiXml.parser = :rexml
MultiXml.parser = MultiXml::Parsers::Rexml # Same as above
MultiXml.parse('<tag>This is the contents</tag>') # Parsed using REXML
```
The `parser` setter takes either a symbol or a class (to allow for custom XML
Expand Down
2 changes: 1 addition & 1 deletion Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ task :default => :spec
namespace :doc do
require 'yard'
YARD::Rake::YardocTask.new do |task|
task.files = ['LICENSE.md', 'lib/**/*.rb']
task.files = ['lib/**/*.rb', '-', 'LICENSE.md']
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was getting warnings:

[warn]: Syntax error in `license.md`:(1,18): syntax error, unexpected tinteger, expecting $end
[warn]: ParserSyntaxError: syntax error in `LICENSE.md`:(1,18): syntax error, unexpected tINTEGER, expecting $end
[warn]: Stack trace:
    /Users/nate/.rvm/gems/ruby-1.9.3-p327/gems/yard-0.8.3/lib/yard/parser/ruby/ruby_parser.rb:537:in `on_parse_error'
    /Users/nate/.rvm/gems/ruby-1.9.3-p327/gems/yard-0.8.3/lib/yard/parser/ruby/ruby_parser.rb:50:in `parse'
    /Users/nate/.rvm/gems/ruby-1.9.3-p327/gems/yard-0.8.3/lib/yard/parser/ruby/ruby_parser.rb:50:in `parse'
    /Users/nate/.rvm/gems/ruby-1.9.3-p327/gems/yard-0.8.3/lib/yard/parser/ruby/ruby_parser.rb:15:in `parse'
    /Users/nate/.rvm/gems/ruby-1.9.3-p327/gems/yard-0.8.3/lib/yard/parser/source_parser.rb:439:in `parse'
    /Users/nate/.rvm/gems/ruby-1.9.3-p327/gems/yard-0.8.3/lib/yard/parser/source_parser.rb:44:in `block in parse'

The code says it should be formatted like this.

task.options = [
'--no-private',
'--protected',
Expand Down
29 changes: 23 additions & 6 deletions lib/multi_xml.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@

module MultiXml
class ParseError < StandardError; end
class DisallowedTypeError < StandardError
def initialize(type)
super "Disallowed type attribute: #{type.inspect}"
end
end

REQUIREMENT_MAP = [
['ox', :ox],
Expand Down Expand Up @@ -54,6 +59,8 @@ class ParseError < StandardError; end
'Hash' => 'hash'
} unless defined?(TYPE_NAMES)

DISALLOWED_XML_TYPES = %w(symbol yaml)

class << self
# Get the current parser class.
def parser
Expand Down Expand Up @@ -105,6 +112,8 @@ def parser=(new_parser)
# <b>Options</b>
#
# <tt>:symbolize_keys</tt> :: If true, will use symbols instead of strings for the keys.
#
# <tt>:disallowed_types</tt> :: Types to disallow from being typecasted. Defaults to `['yaml', 'symbol']`. Use `[]` to allow all types.
def parse(xml, options={})
xml ||= ''

Expand All @@ -116,7 +125,9 @@ def parse(xml, options={})
return {} if char.nil?
xml.ungetc(char)

hash = typecast_xml_value(undasherize_keys(parser.parse(xml))) || {}
hash = typecast_xml_value(undasherize_keys(parser.parse(xml)), options[:disallowed_types]) || {}
rescue DisallowedTypeError
raise
rescue parser.parse_error => error
raise ParseError, error.to_s, error.backtrace
end
Expand Down Expand Up @@ -191,9 +202,15 @@ def undasherize_keys(params)
end
end

def typecast_xml_value(value)
def typecast_xml_value(value, disallowed_types=nil)
disallowed_types ||= DISALLOWED_XML_TYPES

case value
when Hash
if value.include?('type') && !value['type'].is_a?(Hash) && disallowed_types.include?(value['type'])
raise DisallowedTypeError, value['type']
end

if value['type'] == 'array'

# this commented-out suggestion helps to avoid the multiple attribute
Expand All @@ -216,9 +233,9 @@ def typecast_xml_value(value)
else
case entries
when Array
entries.map {|entry| typecast_xml_value(entry)}
entries.map {|entry| typecast_xml_value(entry, disallowed_types)}
when Hash
[typecast_xml_value(entries)]
[typecast_xml_value(entries, disallowed_types)]
else
raise "can't typecast #{entries.class.name}: #{entries.inspect}"
end
Expand Down Expand Up @@ -252,7 +269,7 @@ def typecast_xml_value(value)
nil
else
xml_value = value.inject({}) do |hash, (k, v)|
hash[k] = typecast_xml_value(v)
hash[k] = typecast_xml_value(v, disallowed_types)
hash
end

Expand All @@ -261,7 +278,7 @@ def typecast_xml_value(value)
xml_value['file'].is_a?(StringIO) ? xml_value['file'] : xml_value
end
when Array
value.map!{|i| typecast_xml_value(i)}
value.map!{|i| typecast_xml_value(i, disallowed_types)}
value.length > 1 ? value : value.first
when String
value
Expand Down
40 changes: 35 additions & 5 deletions spec/parser_shared_example.rb
Original file line number Diff line number Diff line change
Expand Up @@ -323,12 +323,26 @@
@xml = "<tag type=\"yaml\">--- \n1: returns an integer\n:message: Have a nice day\narray: \n- has-dashes: true\n has_underscores: true\n</tag>"
end

it "returns a Hash" do
expect(MultiXml.parse(@xml)['tag']).to be_a(Hash)
it "raises MultiXML::DisallowedTypeError by default" do
expect{ MultiXml.parse(@xml)['tag'] }.to raise_error(MultiXml::DisallowedTypeError)
end

it "returns the correctly parsed YAML" do
expect(MultiXml.parse(@xml)['tag']).to eq({:message => "Have a nice day", 1 => "returns an integer", "array" => [{"has-dashes" => true, "has_underscores" => true}]})
it "returns the correctly parsed YAML when the type is allowed" do
expect(MultiXml.parse(@xml, :disallowed_types => [])['tag']).to eq({:message => "Have a nice day", 1 => "returns an integer", "array" => [{"has-dashes" => true, "has_underscores" => true}]})
end
end

context "with an attribute type=\"symbol\"" do
before do
@xml = "<tag type=\"symbol\">my_symbol</tag>"
end

it "raises MultiXML::DisallowedTypeError" do
expect{ MultiXml.parse(@xml)['tag'] }.to raise_error(MultiXml::DisallowedTypeError)
end

it "returns the correctly parsed Symbol when the type is allowed" do
expect(MultiXml.parse(@xml, :disallowed_types => [])['tag']).to eq(:my_symbol)
end
end

Expand Down Expand Up @@ -418,7 +432,7 @@
end
end

%w(integer boolean date datetime yaml file).each do |type|
%w(integer boolean date datetime file).each do |type|
context "with an empty attribute type=\"#{type}\"" do
before do
@xml = "<tag type=\"#{type}\"/>"
Expand All @@ -430,6 +444,22 @@
end
end

%w{yaml symbol}.each do |type|
context "with an empty attribute type=\"#{type}\"" do
before do
@xml = "<tag type=\"#{type}\"/>"
end

it "raises MultiXml::DisallowedTypeError by default" do
expect{ MultiXml.parse(@xml)['tag']}.to raise_error(MultiXml::DisallowedTypeError)
end

it "returns nil when the type is allowed" do
expect(MultiXml.parse(@xml, :disallowed_types => [])['tag']).to be_nil
end
end
end

context "with an empty attribute type=\"array\"" do
before do
@xml = '<tag type="array"/>'
Expand Down