Narnach-libxml_rails 0.0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3 @@
1
+ v0.0.2. Port ActiveSupport Test::Unit tests to RSpec.
2
+
3
+ v0.0.1. Initial release
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Brandon Mitchell
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,40 @@
1
+ = libxml_rails
2
+
3
+ libxml_rails is a gem/rails plugin by Brandon Mitchell. It is designed to replace the default XML processor in Rails (XmlSimple) with the C-based libxml. It requires the libxml-ruby gem.
4
+
5
+ This fork is maintained by Wes 'Narnach' Oldenbeuving.
6
+
7
+ Changes from the Brandon's version are:
8
+ * Don't rely on echoe to build the gem.
9
+ * Added Hash#to_xml_with_libxml and Array#to_xml_with_libxml
10
+ * ActiveResource::Base#to_xml uses libxml.
11
+ * ActiveResource::Formats::XmlFormat#encode uses libxml
12
+
13
+ === Installation
14
+
15
+ libxml_rails can be installed as a rails plugin or as a stand-alone gem.
16
+
17
+ === Gem Installation
18
+
19
+ From PROJECT_ROOT:
20
+
21
+ gem install libxml-ruby
22
+ rake install
23
+
24
+ === Plugin Installation
25
+
26
+ From RAILS_ROOT:
27
+
28
+ script/plugin install git://github.com/Narnach/libxml_rails.git
29
+
30
+ Add the following to the configuration block in config/environment.rb:
31
+
32
+ config.gem 'libxml-ruby'
33
+
34
+ === Gem Installation
35
+
36
+ Add the following to the configuration block in config/environment.rb:
37
+
38
+ config.gem 'Narnach-libxml_rails', :source => 'http://gems.github.com'
39
+
40
+ Copyright (c) 2008 Brandon Mitchell, released under the MIT license
@@ -0,0 +1,28 @@
1
+ require "rake"
2
+ require "rake/clean"
3
+ require "rake/gempackagetask"
4
+ require 'rubygems'
5
+
6
+ ################################################################################
7
+ ### Gem
8
+ ################################################################################
9
+
10
+ begin
11
+ # Parse gemspec using the github safety level.
12
+ file = Dir['*.gemspec'].first
13
+ data = File.read(file)
14
+ spec = nil
15
+ Thread.new { spec = eval("$SAFE = 3\n%s" % data)}.join
16
+
17
+ # Create the gem tasks
18
+ Rake::GemPackageTask.new(spec) do |package|
19
+ package.gem_spec = spec
20
+ end
21
+ rescue Exception => e
22
+ printf "WARNING: Error caught (%s): %s\n%s", e.class.name, e.message, e.backtrace[0...5].map {|l| ' %s' % l}.join("\n")
23
+ end
24
+
25
+ desc 'Package and install the gem for the current version'
26
+ task :install => :gem do
27
+ system "sudo gem install -l pkg/%s-%s.gem" % [spec.name, spec.version]
28
+ end
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'lib', 'libxml_rails'))
@@ -0,0 +1,91 @@
1
+ module Bitbckt #:nodoc:
2
+ module CoreExtensions #:nodoc:
3
+ module Hash #:nodoc:
4
+ module Conversions
5
+
6
+ def self.included(klass)
7
+ klass.extend(ClassMethods)
8
+ end
9
+
10
+ module ClassMethods
11
+ def from_xml(xml)
12
+ result = LibXML::XML::Parser.string(xml).parse
13
+ if result.root.attributes['type'] == 'array'
14
+ { result.root.name.to_s.gsub(/-/,'_') => array_from_node(result.root) }
15
+ else
16
+ { result.root.name.to_s.gsub(/-/,'_') => xml_node_to_hash(result.root) }
17
+ end
18
+ end
19
+
20
+ private
21
+ def xml_node_to_hash(node)
22
+ # If we are at an XML::Node, build the Hash
23
+ if node.element?
24
+ result_hash = {}
25
+ if node.children?
26
+
27
+ node.each_child do |child|
28
+ next if child.empty?
29
+
30
+ if child.attributes['type'] == 'array'
31
+ result = array_from_node(child)
32
+ elsif child.attributes['type'] == 'string' and child.children.empty?
33
+ # NOTE: Odd issue with libxml - XML::Node#empty? returns false and
34
+ # XML::Node#children? returns true when the Node contains just
35
+ # an empty string (#children == []), but has attributes.
36
+ result = ''
37
+ elsif child.attributes['type'] == 'file'
38
+ result = Object::Hash::XML_PARSING['file'].call(child.content.to_s, child.attributes)
39
+ elsif Object::Hash::XML_PARSING.include? child.attributes['type'] and not child.content.to_s.chomp.blank?
40
+ result = Object::Hash::XML_PARSING[child.attributes['type']].call(child.content.to_s)
41
+ else
42
+ result = xml_node_to_hash(child)
43
+ result['type'] = child.attributes['type'] if child.attributes['type'] and result
44
+ end
45
+
46
+ return result if child.name == 'text'
47
+
48
+ key = child.name.gsub(/-/, '_')
49
+ if result_hash[key]
50
+ if result_hash[key].is_a?(Object::Array)
51
+ result_hash[key] << result
52
+ else
53
+ result_hash[key] = [result_hash[key]] << result
54
+ end
55
+ else
56
+ result_hash[key] = result
57
+ end
58
+ end
59
+
60
+ else
61
+ if node.attributes['type'] || node.attributes.length == 0 || node.attributes['nil'] == 'true'
62
+ result_hash = nil
63
+ else
64
+ node.each_attr do |attribute|
65
+ result_hash[attribute.name] = attribute.value.to_s
66
+ end
67
+ end
68
+ end
69
+
70
+ return result_hash
71
+ else
72
+ return node.content.to_s
73
+ end
74
+ end
75
+
76
+ def array_from_node(node)
77
+ returning([]) do |collection|
78
+ node.each_child do |child|
79
+ next if child.empty?
80
+ collection << xml_node_to_hash(child)
81
+ end
82
+ end
83
+ end
84
+ end
85
+
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ Hash.class_eval { include Bitbckt::CoreExtensions::Hash::Conversions }
@@ -0,0 +1,6 @@
1
+ require 'rubygems'
2
+ require 'active_support'
3
+ require 'libxml'
4
+ require 'bitbckt/core_ext/hash/conversions'
5
+ require 'narnach/core_ext/hash/conversions'
6
+ require 'narnach/core_ext/array/conversions'
@@ -0,0 +1,2 @@
1
+ # Convenience file to make it easy to load 'libxml_rails/active_resource'
2
+ require 'narnach/rails_ext/active_resource'
@@ -0,0 +1,64 @@
1
+ module Narnach #:nodoc:
2
+ module CoreExtensions #:nodoc:
3
+ module Array #:nodoc:
4
+ module Conversions
5
+ # Version of #to_xml copy-adjusted from ActiveSupport to work with libxml.
6
+ #
7
+ # Options not supported:
8
+ # - :indent
9
+ def to_xml_with_libxml(options = {})
10
+ raise "Not all elements respond to to_xml" unless all? { |e| e.respond_to? :to_xml_with_libxml }
11
+ options.reverse_merge!({:to_string => true, :skip_instruct => false })
12
+ dasherize = !options.has_key?(:dasherize) || options[:dasherize]
13
+ options[:root] ||= all? { |e| e.is_a?(first.class) && first.class.to_s != "Hash" } ? first.class.to_s.underscore.pluralize : "records"
14
+ options[:root] = options[:root].to_s.dasherize if dasherize
15
+ options[:children] ||= options[:root].singularize
16
+ to_string = options.delete(:to_string)
17
+ skip_instruct = options.delete(:skip_instruct)
18
+ root = options.delete(:root).to_s
19
+ doc = nil
20
+ if skip_instruct
21
+ node = LibXML::XML::Node.new(root)
22
+ doc = node
23
+ case options[:builder]
24
+ when LibXML::XML::Document
25
+ options[:builder].root << node
26
+ when nil
27
+ options[:builder] = node
28
+ else
29
+ options[:builder] << node
30
+ end
31
+ else
32
+ options[:builder] = LibXML::XML::Document.new
33
+ options[:builder].encoding = 'UTF-8'
34
+ options[:builder].root = LibXML::XML::Node.new(root)
35
+ doc = options[:builder].root
36
+ end
37
+ doc['type'] = 'array' unless options[:skip_types]
38
+
39
+
40
+ children = options.delete(:children)
41
+ opts = options.merge({ :root => children })
42
+
43
+ unless empty?
44
+ yield doc if block_given?
45
+ each do |e|
46
+ e.to_xml_with_libxml(opts.merge!({ :skip_instruct => true, :to_string => false, :builder => doc }))
47
+ end
48
+ end
49
+ if to_string
50
+ string = options[:builder].to_s
51
+ string << "\n" if skip_instruct # Emulate Builder behaviour
52
+ return string
53
+ else
54
+ return options[:builder]
55
+ end
56
+ end # to_xml_with_libxml
57
+ end # module Conversions
58
+ end # module Hash
59
+ end # module CoreExtensions
60
+ end # module Narnach
61
+
62
+ class Array
63
+ include Narnach::CoreExtensions::Array::Conversions
64
+ end
@@ -0,0 +1,91 @@
1
+ module Narnach #:nodoc:
2
+ module CoreExtensions #:nodoc:
3
+ module Hash #:nodoc:
4
+ module Conversions
5
+ XML_TYPE_NAMES = ActiveSupport::CoreExtensions::Hash::Conversions::XML_TYPE_NAMES unless defined?(XML_TYPE_NAMES)
6
+ XML_FORMATTING = ActiveSupport::CoreExtensions::Hash::Conversions::XML_FORMATTING unless defined?(XML_FORMATTING)
7
+
8
+ # Version of #to_xml copy-adjusted from ActiveSupport to work with libxml.
9
+ #
10
+ # Options supported in XMLBuilder that are/will be supported here:
11
+ # - :root, name of root node. Default to hash
12
+ # - :children, name of child nodes. Default to singular of root.
13
+ # - :skip_types
14
+ #
15
+ # Options supported in a different way:
16
+ # - :builder. Holds the active LibXML::XML::Document or LibXML::XML::Node.
17
+ # - :skip_instruct. Determine if the document encoding needs to be set. When false, a new Document is created and used as :builder. When true a new Node is created when no :builder is specified.
18
+ # - :dasherize, dasherize the node names or keep them as-is.
19
+ #
20
+ # Options known but not (yet) supported:
21
+ # - :indent, default to level 2
22
+ #
23
+ # Uses Builder's String#to_xs for escaping
24
+ def to_xml_with_libxml(options = {})
25
+ options.reverse_merge!({:root => "hash", :to_string => true })
26
+ dasherize = !options.has_key?(:dasherize) || options[:dasherize]
27
+ root = dasherize ? options[:root].to_s.dasherize : options[:root].to_s
28
+ to_string = options.delete(:to_string)
29
+ skip_instruct = options.delete(:skip_instruct)
30
+ doc = nil
31
+ if skip_instruct
32
+ node = LibXML::XML::Node.new(root)
33
+ doc = node
34
+ case options[:builder]
35
+ when LibXML::XML::Document
36
+ options[:builder].root << node
37
+ when nil
38
+ options[:builder] = node
39
+ else
40
+ options[:builder] << node
41
+ end
42
+ else
43
+ options[:builder] = LibXML::XML::Document.new
44
+ options[:builder].encoding = 'UTF-8'
45
+ options[:builder].root = LibXML::XML::Node.new(root)
46
+ doc = options[:builder].root
47
+ end
48
+ self.each do |key, value|
49
+ case value
50
+ when ::Hash
51
+ value.to_xml_with_libxml(options.merge({ :root => key, :skip_instruct => true, :to_string => false, :builder => doc }))
52
+ when ::Array
53
+ value.to_xml_with_libxml(options.merge({ :root => key, :children => key.to_s.singularize, :skip_instruct => true, :to_string => false, :builder => doc}))
54
+ # when ::Method, ::Proc
55
+ else
56
+ if value.respond_to?(:to_xml_with_libxml)
57
+ value.to_xml_with_libxml(options.merge({ :root => key, :skip_instruct => true, :to_string => false, :builder => doc }))
58
+ else
59
+ type_name = XML_TYPE_NAMES[value.class.name]
60
+ key = dasherize ? key.to_s.dasherize : key.to_s
61
+ attributes = options[:skip_types] || value.nil? || type_name.nil? ? { } : { :type => type_name }
62
+ if value.nil?
63
+ attributes[:nil] = true
64
+ end
65
+
66
+ content = XML_FORMATTING[type_name] ? XML_FORMATTING[type_name].call(value) : value
67
+ child = LibXML::XML::Node.new(key, content.to_s.to_xs)
68
+ attributes.stringify_keys.each do |akey, avalue|
69
+ child[akey] = String(avalue).to_s.to_xs
70
+ end
71
+ doc << child
72
+ end # if
73
+ end # case
74
+ end # each
75
+ yield options[:builder] if block_given?
76
+ if to_string
77
+ string = options[:builder].to_s
78
+ string << "\n" if skip_instruct # Emulate Builder behaviour
79
+ return string
80
+ else
81
+ return options[:builder]
82
+ end
83
+ end # to_xml_with_libxml
84
+ end # module Conversions
85
+ end # module Hash
86
+ end # module CoreExtensions
87
+ end # module Narnach
88
+
89
+ class Hash
90
+ include Narnach::CoreExtensions::Hash::Conversions
91
+ end
@@ -0,0 +1,64 @@
1
+ gem 'activeresource'
2
+ require 'active_resource'
3
+ require 'active_resource/formats/xml_format'
4
+
5
+ module Narnach
6
+ module RailsExt
7
+ module ActiveResource
8
+ module Base
9
+ # A method to convert the the resource to an XML string.
10
+ #
11
+ # This is a copy-adjusted version of ActiveResource's #to_xml, but
12
+ # it uses libxml for serialization instead of builder.
13
+ #
14
+ # ==== Options
15
+ # The +options+ parameter is handed off to the +to_xml_with_libxml+ method on each
16
+ # attribute, so it has the same options as the +to_xml_with_libxml+ methods in
17
+ # Active Support.
18
+ #
19
+ # * <tt>:dasherize</tt> - Boolean option to determine whether or not element names should
20
+ # replace underscores with dashes (default is <tt>false</tt>).
21
+ # * <tt>:skip_instruct</tt> - Toggle skipping the +instruct!+ call on the XML builder
22
+ # that generates the XML declaration (default is <tt>false</tt>).
23
+ #
24
+ # ==== Examples
25
+ # my_group = SubsidiaryGroup.find(:first)
26
+ # my_group.to_xml
27
+ # # => <?xml version="1.0" encoding="UTF-8"?>
28
+ # # <subsidiary_group> [...] </subsidiary_group>
29
+ #
30
+ # my_group.to_xml(:dasherize => true)
31
+ # # => <?xml version="1.0" encoding="UTF-8"?>
32
+ # # <subsidiary-group> [...] </subsidiary-group>
33
+ #
34
+ # my_group.to_xml(:skip_instruct => true)
35
+ # # => <subsidiary_group> [...] </subsidiary_group>
36
+ def to_xml(options={})
37
+ attributes.to_xml_with_libxml({:root => self.class.element_name}.merge(options))
38
+ end
39
+ end
40
+
41
+ module Formats
42
+ module XmlFormat
43
+ def encode(hash, options={})
44
+ hash.to_xml_with_libxml(options)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ module ActiveResource
53
+ module Formats
54
+ XmlFormat.class_eval do
55
+ remove_method :encode
56
+ include Narnach::RailsExt::ActiveResource::Formats::XmlFormat
57
+ end
58
+ end
59
+
60
+ Base.class_eval do
61
+ remove_method :to_xml
62
+ include Narnach::RailsExt::ActiveResource::Base
63
+ end
64
+ end
@@ -0,0 +1,34 @@
1
+ Gem::Specification.new do |s|
2
+ # Project
3
+ s.name = 'libxml_rails'
4
+ s.description = "Libxml_rails replaces ActiveSupport's XmlSimple XML parsing with libxml-ruby. Original gem by Brandon Mitchell, this fork is by Wes 'Narnach' Oldenbeuving."
5
+ s.summary = s.description
6
+ s.version = '0.0.2.5'
7
+ s.date = '2009-01-06'
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Brandon Mitchell", "Wes Oldenbeuving"]
10
+ s.email = "narnach@gmail.com"
11
+ s.homepage = "http://www.github.com/Narnach/libxml_rails"
12
+
13
+ # Files
14
+ root_files = %w[CHANGELOG init.rb libxml_rails.gemspec MIT-LICENSE Rakefile README.rdoc]
15
+ lib_files = %w[bitbckt/core_ext/hash/conversions narnach/core_ext/hash/conversions narnach/core_ext/array/conversions narnach/rails_ext/active_resource libxml_rails libxml_rails/active_resource]
16
+ spec_files = %w[from_xml] + lib_files.select{|f| f[0...7] == 'narnach'}
17
+ other_files = %w[spec/spec_helper.rb]
18
+ s.require_path = "lib"
19
+ s.executables = []
20
+ s.test_files = spec_files.map {|f| 'spec/%s_spec.rb' % f}
21
+ s.files = root_files + s.test_files + other_files + lib_files.map {|f| 'lib/%s.rb' % f}
22
+
23
+ # rdoc
24
+ s.has_rdoc = true
25
+ s.extra_rdoc_files = %w[ README.rdoc MIT-LICENSE]
26
+ s.rdoc_options << '--inline-source' << '--line-numbers' << '--main' << 'README.rdoc'
27
+
28
+ # Dependencies
29
+ s.add_dependency 'activesupport', '>= 2.1.0'
30
+ s.add_dependency 'libxml-ruby', '>= 0.8.3'
31
+
32
+ # Requirements
33
+ s.required_ruby_version = ">= 1.8.0"
34
+ end
@@ -0,0 +1,328 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe Hash, '.from_xml' do
4
+
5
+ before(:each) do
6
+ @xml_options = { :root => :person, :skip_instruct => true, :indent => 0 }
7
+ end
8
+
9
+ it 'parses a single record' do
10
+ topic_xml = <<-EOT
11
+ <topic>
12
+ <title>The First Topic</title>
13
+ <author-name>David</author-name>
14
+ <id type="integer">1</id>
15
+ <approved type="boolean"> true </approved>
16
+ <replies-count type="integer">0</replies-count>
17
+ <replies-close-in type="integer">2592000000</replies-close-in>
18
+ <written-on type="date">2003-07-16</written-on>
19
+ <viewed-at type="datetime">2003-07-16T09:28:00+0000</viewed-at>
20
+ <content type="yaml">--- \n1: should be an integer\n:message: Have a nice day\narray: \n- should-have-dashes: true\n should_have_underscores: true\n</content>
21
+ <author-email-address>david@loudthinking.com</author-email-address>
22
+ <parent-id></parent-id>
23
+ <ad-revenue type="decimal">1.5</ad-revenue>
24
+ <optimum-viewing-angle type="float">135</optimum-viewing-angle>
25
+ <resident type="symbol">yes</resident>
26
+ </topic>
27
+ EOT
28
+
29
+ expected_topic_hash = {
30
+ :title => "The First Topic",
31
+ :author_name => "David",
32
+ :id => 1,
33
+ :approved => true,
34
+ :replies_count => 0,
35
+ :replies_close_in => 2592000000,
36
+ :written_on => Date.new(2003, 7, 16),
37
+ :viewed_at => Time.utc(2003, 7, 16, 9, 28),
38
+ :content => { :message => "Have a nice day", 1 => "should be an integer", "array" => [{ "should-have-dashes" => true, "should_have_underscores" => true }] },
39
+ :author_email_address => "david@loudthinking.com",
40
+ :parent_id => nil,
41
+ :ad_revenue => BigDecimal("1.50"),
42
+ :optimum_viewing_angle => 135.0,
43
+ :resident => :yes
44
+ }.stringify_keys
45
+
46
+ Hash.from_xml(topic_xml)["topic"].should == expected_topic_hash
47
+ end
48
+
49
+ it 'parses a single record with nil values' do
50
+ topic_xml = <<-EOT
51
+ <topic>
52
+ <title></title>
53
+ <id type="integer"></id>
54
+ <approved type="boolean"></approved>
55
+ <written-on type="date"></written-on>
56
+ <viewed-at type="datetime"></viewed-at>
57
+ <content type="yaml"></content>
58
+ <parent-id></parent-id>
59
+ </topic>
60
+ EOT
61
+
62
+ expected_topic_hash = {
63
+ :title => nil,
64
+ :id => nil,
65
+ :approved => nil,
66
+ :written_on => nil,
67
+ :viewed_at => nil,
68
+ :content => nil,
69
+ :parent_id => nil
70
+ }.stringify_keys
71
+
72
+ Hash.from_xml(topic_xml)["topic"].should == expected_topic_hash
73
+ end
74
+
75
+ it 'parses multiple records' do
76
+ topics_xml = <<-EOT
77
+ <topics type="array">
78
+ <topic>
79
+ <title>The First Topic</title>
80
+ <author-name>David</author-name>
81
+ <id type="integer">1</id>
82
+ <approved type="boolean">false</approved>
83
+ <replies-count type="integer">0</replies-count>
84
+ <replies-close-in type="integer">2592000000</replies-close-in>
85
+ <written-on type="date">2003-07-16</written-on>
86
+ <viewed-at type="datetime">2003-07-16T09:28:00+0000</viewed-at>
87
+ <content>Have a nice day</content>
88
+ <author-email-address>david@loudthinking.com</author-email-address>
89
+ <parent-id nil="true"></parent-id>
90
+ </topic>
91
+ <topic>
92
+ <title>The Second Topic</title>
93
+ <author-name>Jason</author-name>
94
+ <id type="integer">1</id>
95
+ <approved type="boolean">false</approved>
96
+ <replies-count type="integer">0</replies-count>
97
+ <replies-close-in type="integer">2592000000</replies-close-in>
98
+ <written-on type="date">2003-07-16</written-on>
99
+ <viewed-at type="datetime">2003-07-16T09:28:00+0000</viewed-at>
100
+ <content>Have a nice day</content>
101
+ <author-email-address>david@loudthinking.com</author-email-address>
102
+ <parent-id></parent-id>
103
+ </topic>
104
+ </topics>
105
+ EOT
106
+
107
+ expected_topic_hash = {
108
+ :title => "The First Topic",
109
+ :author_name => "David",
110
+ :id => 1,
111
+ :approved => false,
112
+ :replies_count => 0,
113
+ :replies_close_in => 2592000000,
114
+ :written_on => Date.new(2003, 7, 16),
115
+ :viewed_at => Time.utc(2003, 7, 16, 9, 28),
116
+ :content => "Have a nice day",
117
+ :author_email_address => "david@loudthinking.com",
118
+ :parent_id => nil
119
+ }.stringify_keys
120
+
121
+ Hash.from_xml(topics_xml)["topics"].first.should == expected_topic_hash
122
+ end
123
+
124
+ it 'parses a single record with attributes *other* than "type"' do
125
+ topic_xml = <<-EOT
126
+ <rsp stat="ok">
127
+ <photos page="1" pages="1" perpage="100" total="16">
128
+ <photo id="175756086" owner="55569174@N00" secret="0279bf37a1" server="76" title="Colored Pencil PhotoBooth Fun" ispublic="1" isfriend="0" isfamily="0"/>
129
+ </photos>
130
+ </rsp>
131
+ EOT
132
+
133
+ expected_topic_hash = {
134
+ :id => "175756086",
135
+ :owner => "55569174@N00",
136
+ :secret => "0279bf37a1",
137
+ :server => "76",
138
+ :title => "Colored Pencil PhotoBooth Fun",
139
+ :ispublic => "1",
140
+ :isfriend => "0",
141
+ :isfamily => "0",
142
+ }.stringify_keys
143
+
144
+ Hash.from_xml(topic_xml)["rsp"]["photos"]["photo"].should == expected_topic_hash
145
+ end
146
+
147
+ it 'parses an empty array' do
148
+ blog_xml = <<-XML
149
+ <blog>
150
+ <posts type="array"></posts>
151
+ </blog>
152
+ XML
153
+ expected_blog_hash = {"blog" => {"posts" => []}}
154
+ Hash.from_xml(blog_xml).should == expected_blog_hash
155
+ end
156
+
157
+ it 'parses an empty array with whitespace' do
158
+ blog_xml = <<-XML
159
+ <blog>
160
+ <posts type="array">
161
+ </posts>
162
+ </blog>
163
+ XML
164
+ expected_blog_hash = {"blog" => {"posts" => []}}
165
+ Hash.from_xml(blog_xml).should == expected_blog_hash
166
+ end
167
+
168
+ it 'parses an array with one entry' do
169
+ blog_xml = <<-XML
170
+ <blog>
171
+ <posts type="array">
172
+ <post>a post</post>
173
+ </posts>
174
+ </blog>
175
+ XML
176
+ expected_blog_hash = {"blog" => {"posts" => ["a post"]}}
177
+ Hash.from_xml(blog_xml).should == expected_blog_hash
178
+ end
179
+
180
+ it 'parses an array with multiple entries' do
181
+ blog_xml = <<-XML
182
+ <blog>
183
+ <posts type="array">
184
+ <post>a post</post>
185
+ <post>another post</post>
186
+ </posts>
187
+ </blog>
188
+ XML
189
+ expected_blog_hash = {"blog" => {"posts" => ["a post", "another post"]}}
190
+ Hash.from_xml(blog_xml).should == expected_blog_hash
191
+ end
192
+
193
+ it 'parses a file' do
194
+ blog_xml = <<-XML
195
+ <blog>
196
+ <logo type="file" name="logo.png" content_type="image/png">
197
+ </logo>
198
+ </blog>
199
+ XML
200
+ hash = Hash.from_xml(blog_xml)
201
+ hash.has_key?('blog').should be_true
202
+ hash['blog'].has_key?('logo').should be_true
203
+
204
+ file = hash['blog']['logo']
205
+ file.original_filename.should == 'logo.png'
206
+ file.content_type.should == 'image/png'
207
+ end
208
+
209
+ it 'parses a file with defaults' do
210
+ blog_xml = <<-XML
211
+ <blog>
212
+ <logo type="file">
213
+ </logo>
214
+ </blog>
215
+ XML
216
+ file = Hash.from_xml(blog_xml)['blog']['logo']
217
+ file.original_filename.should == 'untitled'
218
+ file.content_type.should == 'application/octet-stream'
219
+ end
220
+
221
+ it 'parses XSD-like types' do
222
+ bacon_xml = <<-EOT
223
+ <bacon>
224
+ <weight type="double">0.5</weight>
225
+ <price type="decimal">12.50</price>
226
+ <chunky type="boolean"> 1 </chunky>
227
+ <expires-at type="dateTime">2007-12-25T12:34:56+0000</expires-at>
228
+ <notes type="string"></notes>
229
+ <illustration type="base64Binary">YmFiZS5wbmc=</illustration>
230
+ </bacon>
231
+ EOT
232
+
233
+ expected_bacon_hash = {
234
+ :weight => 0.5,
235
+ :chunky => true,
236
+ :price => BigDecimal("12.50"),
237
+ :expires_at => Time.utc(2007,12,25,12,34,56),
238
+ :notes => "",
239
+ :illustration => "babe.png"
240
+ }.stringify_keys
241
+
242
+ Hash.from_xml(bacon_xml)["bacon"].should == expected_bacon_hash
243
+ end
244
+
245
+ it 'trickles the "type" attribute through, when unknown' do
246
+ product_xml = <<-EOT
247
+ <product>
248
+ <weight type="double">0.5</weight>
249
+ <image type="ProductImage"><filename>image.gif</filename></image>
250
+
251
+ </product>
252
+ EOT
253
+
254
+ expected_product_hash = {
255
+ :weight => 0.5,
256
+ :image => {'type' => 'ProductImage', 'filename' => 'image.gif' },
257
+ }.stringify_keys
258
+
259
+ Hash.from_xml(product_xml)["product"].should == expected_product_hash
260
+ end
261
+
262
+ it 'unescapes text nodes' do
263
+ xml_string = '<person><bare-string>First &amp; Last Name</bare-string><pre-escaped-string>First &amp;amp; Last Name</pre-escaped-string></person>'
264
+ expected_hash = {
265
+ :bare_string => 'First & Last Name',
266
+ :pre_escaped_string => 'First &amp; Last Name'
267
+ }.stringify_keys
268
+ Hash.from_xml(xml_string)['person'].should == expected_hash
269
+ end
270
+
271
+ it 'parses Hash.to_xml output' do
272
+ hash = {
273
+ :bare_string => 'First & Last Name',
274
+ :pre_escaped_string => 'First &amp; Last Name'
275
+ }.stringify_keys
276
+
277
+ Hash.from_xml(hash.to_xml(@xml_options))['person'].should == hash
278
+ end
279
+
280
+ it 'parses a datetime with UTC time' do
281
+ alert_xml = <<-XML
282
+ <alert>
283
+ <alert_at type="datetime">2008-02-10T15:30:45Z</alert_at>
284
+ </alert>
285
+ XML
286
+ alert_at = Hash.from_xml(alert_xml)['alert']['alert_at']
287
+ alert_at.utc?.should be_true
288
+ alert_at.should == Time.utc(2008, 2, 10, 15, 30, 45)
289
+ end
290
+
291
+ it 'parses a datetime with non-UTC time' do
292
+ alert_xml = <<-XML
293
+ <alert>
294
+ <alert_at type="datetime">2008-02-10T10:30:45-05:00</alert_at>
295
+ </alert>
296
+ XML
297
+ alert_at = Hash.from_xml(alert_xml)['alert']['alert_at']
298
+ alert_at.utc?.should be_true
299
+ alert_at.should == Time.utc(2008, 2, 10, 15, 30, 45)
300
+ end
301
+
302
+ it 'parses a datetime with a far-future date' do
303
+ alert_xml = <<-XML
304
+ <alert>
305
+ <alert_at type="datetime">2050-02-10T15:30:45Z</alert_at>
306
+ </alert>
307
+ XML
308
+ alert_at = Hash.from_xml(alert_xml)['alert']['alert_at']
309
+ alert_at.utc?.should be_true
310
+ alert_at.year.should == 2050
311
+ alert_at.month.should == 2
312
+ alert_at.day.should == 10
313
+ alert_at.hour.should == 15
314
+ alert_at.min.should == 30
315
+ alert_at.sec.should == 45
316
+ end
317
+
318
+ it 'undasherizes the root node name' do
319
+ xml = <<-EOT
320
+ <?xml version="1.0" encoding="UTF-8"?>
321
+ <with-dash>
322
+ <another-dash>text</another-dash>
323
+ </with-dash>
324
+ EOT
325
+ expected_hash = { "with_dash" => {"another_dash"=>"text"} }
326
+ Hash.from_xml(xml).should == expected_hash
327
+ end
328
+ end
@@ -0,0 +1,73 @@
1
+ require File.dirname(__FILE__) + '/../../../spec_helper'
2
+ require 'narnach/core_ext/array/conversions'
3
+
4
+ describe Array, '#to_xml' do
5
+ describe 'ActiveSupport tests' do
6
+ before(:each) do
7
+ @builder_xml_options = {:skip_instruct => true }
8
+ @libxml_options = {:skip_instruct => true }
9
+ end
10
+
11
+ def compare_with_rails_for(hash, options = {})
12
+ builder_xml = hash.to_xml(@builder_xml_options.merge(options))
13
+ libxml_xml = hash.to_xml_with_libxml(@libxml_options.merge(options))
14
+ libxml_xml.should == builder_xml
15
+ end
16
+
17
+ it 'should pass test_to_xml' do
18
+ compare_with_rails_for([
19
+ { :name => "David", :age => 26, :age_in_millis => 820497600000 },
20
+ { :name => "Jason", :age => 31, :age_in_millis => BigDecimal.new('1.0') }
21
+ ])
22
+ end
23
+
24
+ it 'should pass test_to_xml_with_dedicated_name' do
25
+ compare_with_rails_for([
26
+ { :name => "David", :age => 26, :age_in_millis => 820497600000 }, { :name => "Jason", :age => 31 }
27
+ ],{:root => "people"})
28
+ end
29
+
30
+ it 'should pass test_to_xml_with_options' do
31
+ compare_with_rails_for([
32
+ { :name => "David", :street_address => "Paulina" }, { :name => "Jason", :street_address => "Evergreen" }
33
+ ], {:skip_types => true})
34
+ end
35
+
36
+ it 'should pass test_to_xml_with_dasherize_false' do
37
+ compare_with_rails_for([
38
+ { :name => "David", :street_address => "Paulina" }, { :name => "Jason", :street_address => "Evergreen" }
39
+ ], {:skip_types => true, :dasherize => false})
40
+ end
41
+
42
+ it 'should pass test_to_xml_with_dasherize_true' do
43
+ compare_with_rails_for([
44
+ { :name => "David", :street_address => "Paulina" }, { :name => "Jason", :street_address => "Evergreen" }
45
+ ], {:skip_types => true, :dasherize => true})
46
+ end
47
+
48
+ it 'should pass test_to_with_instruct' do
49
+ compare_with_rails_for([
50
+ { :name => "David", :age => 26, :age_in_millis => 820497600000 },
51
+ { :name => "Jason", :age => 31, :age_in_millis => BigDecimal.new('1.0') }
52
+ ], {:skip_instruct => false})
53
+ end
54
+
55
+ it 'should pass test_to_xml_with_block' do
56
+ ary = [
57
+ { :name => "David", :age => 26, :age_in_millis => 820497600000 },
58
+ { :name => "Jason", :age => 31, :age_in_millis => BigDecimal.new('1.0') }
59
+ ]
60
+ builder_xml = ary.to_xml(@builder_xml_options) do |builder|
61
+ builder.count 2
62
+ end
63
+ libxml_xml = ary.to_xml_with_libxml(@libxml_options) do |libxml|
64
+ libxml << LibXML::XML::Node.new('count', 2)
65
+ end
66
+ libxml_xml.should == builder_xml
67
+ end
68
+
69
+ it 'should pass test_to_xml_with_empty' do
70
+ compare_with_rails_for([])
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,154 @@
1
+ require File.dirname(__FILE__) + '/../../../spec_helper'
2
+
3
+ describe Hash, '#from_xml' do
4
+ it "should undasherize the Hash keys" do
5
+ xml = <<-XML
6
+ <?xml version="1.0" encoding="UTF-8"?>
7
+ <post>
8
+ <post-id type="integer">1</post-id>
9
+ </post>
10
+ XML
11
+ Hash.from_xml(xml).should == {'post' => {'post_id' => 1}}
12
+ end
13
+
14
+ # # libxml will not break, but it takes about 140MB and 60 seconds on a 2.4GHz Core2 Duo Macbook to succeed.
15
+ # # It would be nice if entity expansion could be disabled if you don't plan to use it.
16
+ # it 'should pass test_expansion_count_is_limited' do
17
+ # attack_xml = <<-EOT
18
+ # <?xml version="1.0" encoding="UTF-8"?>
19
+ # <!DOCTYPE member [
20
+ # <!ENTITY a "&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;">
21
+ # <!ENTITY b "&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;">
22
+ # <!ENTITY c "&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;">
23
+ # <!ENTITY d "&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;">
24
+ # <!ENTITY e "&f;&f;&f;&f;&f;&f;&f;&f;&f;&f;">
25
+ # <!ENTITY f "&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;">
26
+ # <!ENTITY g "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx">
27
+ # ]>
28
+ # <member>
29
+ # &a;
30
+ # </member>
31
+ # EOT
32
+ # lambda {
33
+ # Hash.from_xml(attack_xml)
34
+ # }.should_not raise_error
35
+ # end
36
+ end
37
+
38
+ class IWriteMyOwnXML
39
+ def to_xml(options = {})
40
+ options[:indent] ||= 2
41
+ xml = options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent])
42
+ xml.instruct! unless options[:skip_instruct]
43
+ xml.level_one do
44
+ xml.tag!(:second_level, 'content')
45
+ end
46
+ end
47
+
48
+ def to_xml_with_libxml(options = {})
49
+ {:second_level => 'content'}.to_xml_with_libxml(options.merge(:root => 'level_one', :dasherize => false))
50
+ end
51
+ end
52
+
53
+ describe Hash, "#to_xml_with_libxml" do
54
+ it "should convert a Hash with one element" do
55
+ hsh = {:one => 1}
56
+ libxml_xml = hsh.to_xml_with_libxml.to_s
57
+ builder_xml = hsh.to_xml
58
+ libxml_xml.should == builder_xml
59
+ end
60
+
61
+ it "should convert a Hash with nested Hash" do
62
+ hsh = {:one => 1, 'two' => { :three => 3}}
63
+ libxml_xml = hsh.to_xml_with_libxml.to_s
64
+ builder_xml = hsh.to_xml
65
+ libxml_xml.should == builder_xml
66
+ end
67
+
68
+ # These tests are adapted from the Rails ActiveSupport tests, so test to_xml_with_libxml in the same situations as to_xml
69
+ describe 'ActiveSupport tests' do
70
+ before(:each) do
71
+ @builder_xml_options = { :root => :person, :skip_instruct => true }
72
+ @libxml_options = { :root => :person, :skip_instruct => true }
73
+ end
74
+
75
+ def compare_with_rails_for(hash, custom_builder_options={}, custom_libxml_options={})
76
+ builder_xml = hash.to_xml(@builder_xml_options.merge(custom_builder_options))
77
+ libxml_xml = hash.to_xml_with_libxml(@libxml_options.merge(custom_libxml_options))
78
+ libxml_xml.should == builder_xml
79
+ end
80
+
81
+ it 'should pass test_one_level' do
82
+ compare_with_rails_for({ :name => "David", :street => "Paulina" })
83
+ end
84
+
85
+ it 'should pass test_one_level_dasherize_false' do
86
+ compare_with_rails_for({ :name => "David", :street_name => "Paulina" }, {:dasherize => false}, {:dasherize => false})
87
+ end
88
+
89
+ it 'should pass test_one_level_dasherize_true' do
90
+ compare_with_rails_for({ :name => "David", :street_name => "Paulina" }, {:dasherize => true}, {:dasherize => true})
91
+ end
92
+
93
+ it 'should pass test_one_level_with_types' do
94
+ compare_with_rails_for({ :name => "David", :street => "Paulina", :age => 26, :age_in_millis => 820497600000, :moved_on => Date.new(2005, 11, 15), :resident => :yes })
95
+ end
96
+
97
+ it 'should pass test_one_level_with_nils' do
98
+ compare_with_rails_for({ :name => "David", :street => "Paulina", :age => nil })
99
+ end
100
+
101
+ it 'should pass test_one_level_with_skipping_types' do
102
+ compare_with_rails_for({ :name => "David", :street => "Paulina", :age => nil }, {:skip_types => true}, {:skip_types => true})
103
+ end
104
+
105
+ it 'should pass test_one_level_with_yielding' do
106
+ hash = { :name => "David", :street => "Paulina" }
107
+ builder_xml = hash.to_xml(@builder_xml_options) do |x|
108
+ x.creator("Rails")
109
+ end
110
+ libxml_xml = hash.to_xml_with_libxml(@libxml_options) do |x|
111
+ x << LibXML::XML::Node.new('creator', 'Rails')
112
+ end
113
+ libxml_xml.should == builder_xml
114
+ end
115
+
116
+ it 'should pass test_two_levels' do
117
+ compare_with_rails_for({ :name => "David", :address => { :street => "Paulina" } })
118
+ end
119
+
120
+ it 'should pass test_two_levels_with_second_level_overriding_to_xml' do
121
+ compare_with_rails_for({ :name => "David", :address => { :street => "Paulina" }, :child => IWriteMyOwnXML.new })
122
+ end
123
+
124
+ it 'should pass test_two_levels_with_array' do
125
+ compare_with_rails_for({ :name => "David", :addresses => [{ :street => "Paulina" }, { :street => "Evergreen" }] })
126
+ end
127
+
128
+ it 'should test_three_levels_with_array' do
129
+ compare_with_rails_for({ :name => "David", :addresses => [{ :streets => [ { :name => "Paulina" }, { :name => "Paulina" } ] } ] })
130
+ end
131
+
132
+ # The XML builder seems to fail miserably when trying to tag something
133
+ # with the same name as a Kernel method (throw, test, loop, select ...)
134
+ it 'should pass test_kernel_method_names_to_xml' do
135
+ compare_with_rails_for({ :throw => { :ball => 'red' } })
136
+ end
137
+
138
+ it 'should pass test_escaping_to_xml' do
139
+ compare_with_rails_for({
140
+ :bare_string => 'First & Last Name',
141
+ :pre_escaped_string => 'First &amp; Last Name'
142
+ }.stringify_keys)
143
+ end
144
+
145
+ it 'should pass test_roundtrip_to_xml_from_xml' do
146
+ hash = {
147
+ :bare_string => 'First & Last Name',
148
+ :pre_escaped_string => 'First &amp; Last Name'
149
+ }.stringify_keys
150
+
151
+ Hash.from_xml(hash.to_xml_with_libxml(@libxml_options))['person'].should == hash
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,16 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+ require 'libxml_rails/active_resource'
3
+
4
+ class SomePerson < ActiveResource::Base
5
+ self.site = 'http://example.org'
6
+ end
7
+
8
+ describe ActiveResource, '#to_xml' do
9
+ before(:each) do
10
+ @sp = SomePerson.new(:name => 'Wes', :last_name => 'Oldenbeuving', :age => 22)
11
+ end
12
+
13
+ it "should return the resource as XML" do
14
+ @sp.to_xml(:libxml=>false).should == @sp.to_xml(:libxml=>true)
15
+ end
16
+ end
@@ -0,0 +1,4 @@
1
+ lib_dir = File.expand_path(File.join(File.dirname(__FILE__),'..','lib'))
2
+ $LOAD_PATH.unshift(lib_dir)
3
+
4
+ require 'libxml_rails'
metadata ADDED
@@ -0,0 +1,94 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: Narnach-libxml_rails
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2.5
5
+ platform: ruby
6
+ authors:
7
+ - Brandon Mitchell
8
+ - Wes Oldenbeuving
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2009-01-06 00:00:00 -08:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: activesupport
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 2.1.0
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: libxml-ruby
27
+ version_requirement:
28
+ version_requirements: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 0.8.3
33
+ version:
34
+ description: Libxml_rails replaces ActiveSupport's XmlSimple XML parsing with libxml-ruby. Original gem by Brandon Mitchell, this fork is by Wes 'Narnach' Oldenbeuving.
35
+ email: narnach@gmail.com
36
+ executables: []
37
+
38
+ extensions: []
39
+
40
+ extra_rdoc_files:
41
+ - README.rdoc
42
+ - MIT-LICENSE
43
+ files:
44
+ - CHANGELOG
45
+ - init.rb
46
+ - libxml_rails.gemspec
47
+ - MIT-LICENSE
48
+ - Rakefile
49
+ - README.rdoc
50
+ - spec/from_xml_spec.rb
51
+ - spec/narnach/core_ext/hash/conversions_spec.rb
52
+ - spec/narnach/core_ext/array/conversions_spec.rb
53
+ - spec/narnach/rails_ext/active_resource_spec.rb
54
+ - spec/spec_helper.rb
55
+ - lib/bitbckt/core_ext/hash/conversions.rb
56
+ - lib/narnach/core_ext/hash/conversions.rb
57
+ - lib/narnach/core_ext/array/conversions.rb
58
+ - lib/narnach/rails_ext/active_resource.rb
59
+ - lib/libxml_rails.rb
60
+ - lib/libxml_rails/active_resource.rb
61
+ has_rdoc: true
62
+ homepage: http://www.github.com/Narnach/libxml_rails
63
+ post_install_message:
64
+ rdoc_options:
65
+ - --inline-source
66
+ - --line-numbers
67
+ - --main
68
+ - README.rdoc
69
+ require_paths:
70
+ - lib
71
+ required_ruby_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: 1.8.0
76
+ version:
77
+ required_rubygems_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: "0"
82
+ version:
83
+ requirements: []
84
+
85
+ rubyforge_project:
86
+ rubygems_version: 1.2.0
87
+ signing_key:
88
+ specification_version: 2
89
+ summary: Libxml_rails replaces ActiveSupport's XmlSimple XML parsing with libxml-ruby. Original gem by Brandon Mitchell, this fork is by Wes 'Narnach' Oldenbeuving.
90
+ test_files:
91
+ - spec/from_xml_spec.rb
92
+ - spec/narnach/core_ext/hash/conversions_spec.rb
93
+ - spec/narnach/core_ext/array/conversions_spec.rb
94
+ - spec/narnach/rails_ext/active_resource_spec.rb