pipa-xmlnuts 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Igor Gunko
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.
data/README ADDED
@@ -0,0 +1,5 @@
1
+ == xmlnuts
2
+
3
+
4
+
5
+ Released under the MIT license (included)
data/lib/xmlnuts.rb ADDED
@@ -0,0 +1 @@
1
+ require 'xmlnuts/nuts'
@@ -0,0 +1,115 @@
1
+ module XmlNuts
2
+ module Converters
3
+ def self.lookup(type)
4
+ lookup!(type)
5
+ rescue ArgumentError
6
+ nil
7
+ end
8
+
9
+ def self.lookup!(type)
10
+ const_get("Convert_#{type}")
11
+ rescue NameError
12
+ raise ArgumentError, "converter not found for #{type}"
13
+ end
14
+
15
+ def self.create(type, options)
16
+ create!(type, options)
17
+ rescue ArgumentError
18
+ nil
19
+ end
20
+
21
+ def self.create!(type, options)
22
+ lookup!(type).new(options)
23
+ end
24
+
25
+ class Convert_string #:nodoc:
26
+ def initialize(options)
27
+ @whitespace = options[:whitespace] || :trim
28
+ end
29
+
30
+ def to_xml(string)
31
+ string
32
+ end
33
+
34
+ def from_xml(string)
35
+ return nil unless string
36
+ string = case @whitespace
37
+ when :trim then string.strip
38
+ when :preserve then string
39
+ when :collapse then string.gsub(/\s+/, ' ').strip
40
+ end
41
+ end
42
+ end
43
+
44
+ class Convert_boolean < Convert_string #:nodoc:
45
+ def initialize(options)
46
+ super
47
+ @format = options[:format] || :truefalse
48
+ raise ArgumentError, "unrecognized format #{@format}" unless [:truefalse, :yesno, :numeric].include?(@format)
49
+ end
50
+
51
+ def to_xml(flag)
52
+ return nil if flag.nil?
53
+ case @format
54
+ when :truefalse then flag ? 'true' : 'false'
55
+ when :yesno then flag ? 'yes' : 'no'
56
+ when :numeric then flag ? '0' : '1'
57
+ end
58
+ end
59
+
60
+ def from_xml(string)
61
+ return nil unless string
62
+ case string = super(string)
63
+ when '1', 'true', 'yes' then true
64
+ when '0', 'false', 'no' then false
65
+ else
66
+ raise ArgumentError, "invalid value for boolean: #{string.inspect}"
67
+ end
68
+ end
69
+ end
70
+
71
+ class Convert_integer < Convert_string #:nodoc:
72
+ def initialize(options)
73
+ super
74
+ end
75
+
76
+ def to_xml(int)
77
+ int.to_s
78
+ end
79
+
80
+ def from_xml(string)
81
+ string && Integer(super(string))
82
+ end
83
+ end
84
+
85
+ class Convert_datetime < Convert_string #:nodoc:
86
+ def initialize(options)
87
+ super
88
+ @fraction_digits = options[:fraction_digits] || 0
89
+ end
90
+
91
+ def to_xml(time)
92
+ time && time.xmlschema(@fraction_digits)
93
+ end
94
+
95
+ def self.from_xml(string)
96
+ string && Time.parse(super(string, options))
97
+ end
98
+ end
99
+
100
+ class Convert_list #:nodoc:
101
+ def initialize(options)
102
+ @item_type = options[:item_type] || :string
103
+ @item_converter = Converters.create!(@item_type, options)
104
+ end
105
+
106
+ def to_xml(array)
107
+ array.map {|x| @item_converter.to_xml(x) } * ' '
108
+ end
109
+
110
+ def from_xml(string)
111
+ string && string.split.map! {|x| @item_converter.from_xml(x)}
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,137 @@
1
+ module XmlNuts
2
+ class Mapping
3
+ attr_reader :name, :xmlname, :type, :options, :converter
4
+
5
+ def initialize(name, type, options)
6
+ @name, @xmlname, @type, @options = name.to_sym, (options.delete(:xmlname) || name).to_s, type, options
7
+ @setter = :"#{name}="
8
+ @converter = Converters.create(type, options)
9
+ end
10
+
11
+ def to_xml(nut, node)
12
+ setxml(node, toxml(get(nut)))
13
+ end
14
+
15
+ def from_xml(nut, node)
16
+ set(nut, froxml(getxml(node)))
17
+ end
18
+
19
+ private
20
+ def get(nut) #:doc:
21
+ nut.send(name)
22
+ end
23
+
24
+ def set(nut, value) #:doc:
25
+ nut.send(@setter, value)
26
+ end
27
+
28
+ def toxml(value) #:doc:
29
+ value
30
+ end
31
+
32
+ def froxml(text) #:doc:
33
+ text
34
+ end
35
+ end
36
+
37
+ class PrimitiveMapping < Mapping
38
+ def initialize(name, type, options)
39
+ if type.is_a?(Array)
40
+ raise ArgumentError, "invalid value for type: #{type}" if type.length != 1
41
+ type, options[:item_type] = :list, type.first
42
+ end
43
+ super
44
+ raise ArgumentError, "converter absent for type #{type.inspect}" unless converter
45
+ end
46
+
47
+ private
48
+ def toxml(value) #:doc:
49
+ converter.to_xml(value)
50
+ end
51
+
52
+ def froxml(text) #:doc:
53
+ converter.from_xml(text)
54
+ end
55
+ end
56
+
57
+ class ElementMapping < PrimitiveMapping
58
+ private
59
+ def getxml(node) #:doc:
60
+ (e = node.elements[xmlname]) && e.text
61
+ end
62
+
63
+ def setxml(node, value) #:doc:
64
+ (node.elements[xmlname] ||= REXML::Element.new(xmlname)).text = value
65
+ end
66
+ end
67
+
68
+ class AttributeMapping < PrimitiveMapping
69
+ private
70
+ def getxml(node) #:doc:
71
+ node.attributes[xmlname]
72
+ end
73
+
74
+ def setxml(node, value) #:doc:
75
+ node.add_attribute(xmlname, value)
76
+ end
77
+ end
78
+
79
+ module NestedMany
80
+ private
81
+ def toxml(nested_nuts) #:doc:
82
+ nested_nuts && nested_nuts.map {|x| super(x) }
83
+ end
84
+
85
+ def froxml(nested_nodes) #:doc:
86
+ nested_nodes && nested_nodes.map {|x| super(x) }
87
+ end
88
+ end
89
+
90
+ class ElementsMapping < PrimitiveMapping
91
+ include NestedMany
92
+
93
+ private
94
+ def getxml(node) #:doc:
95
+ (e = node.get_elements(xmlname)) && e.map {|x| x.text }
96
+ end
97
+
98
+ def setxml(node, values) #:doc:
99
+ values.each {|x| node.add_element(xmlname).text = x } if values
100
+ end
101
+ end
102
+
103
+ class NestedMapping < Mapping
104
+ private
105
+ def toxml(nested_nut) #:doc:
106
+ type.build_node(nested_nut, Element.new(xmlname))
107
+ end
108
+
109
+ def froxml(nested_node) #:doc:
110
+ type.parse_node(type.new, nested_node)
111
+ end
112
+ end
113
+
114
+ class NestedOneMapping < NestedMapping
115
+ private
116
+ def getxml(node) #:doc:
117
+ node.elements[xmlname]
118
+ end
119
+
120
+ def setxml(node, nested_node) #:doc:
121
+ node.elements << nested_node if nested_node
122
+ end
123
+ end
124
+
125
+ class NestedManyMapping < NestedMapping
126
+ include NestedMany
127
+
128
+ private
129
+ def getxml(node) #:doc:
130
+ node.get_elements(xmlname)
131
+ end
132
+
133
+ def setxml(node, nested_nodes) #:doc:
134
+ nested_nodes.each {|x| node.add_element(x) }
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,72 @@
1
+ require 'rexml/document'
2
+ require 'time'
3
+ require 'xmlnuts/converters'
4
+ require 'xmlnuts/mappings'
5
+
6
+ module XmlNuts #:nodoc:
7
+ module Nut
8
+ def self.included(other) #:nodoc:
9
+ other.extend(ClassMethods)
10
+ end
11
+
12
+ module ClassMethods
13
+ def element(name, type = :string, options = {})
14
+ mappings << (type.is_a?(Class) ? NestedOneMapping : ElementMapping).new(name, type, options)
15
+ attr_accessor name
16
+ end
17
+
18
+ def elements(name, type = :string, options = {})
19
+ mappings << (type.is_a?(Class) ? NestedManyMapping : ElementsMapping).new(name, type, options)
20
+ attr_accessor name
21
+ end
22
+
23
+ def attribute(name, type = :string, options = {})
24
+ mappings << AttributeMapping.new(name, type, options)
25
+ attr_accessor name
26
+ end
27
+
28
+ def mappings
29
+ @mappings ||= []
30
+ end
31
+
32
+ def build(nut, destination = nil)
33
+ case destination
34
+ when nil
35
+ destination = REXML::Document.new
36
+ e = destination.add_element('root')
37
+ build_node(nut, e)
38
+ when REXML::Node
39
+ build_node(nut, destination)
40
+ end
41
+ destination
42
+ end
43
+
44
+ def parse(source)
45
+ case source
46
+ when nil
47
+ nil
48
+ when REXML::Node
49
+ parse_node(new, source)
50
+ when String
51
+ doc = REXML::Document.new(source)
52
+ (root = doc.root) ? parse_node(new, root) : nil
53
+ end
54
+ end
55
+
56
+ def build_node(nut, node)
57
+ callem(:to_xml, nut, node)
58
+ node
59
+ end
60
+
61
+ def parse_node(nut, node)
62
+ callem(:from_xml ,nut, node)
63
+ nut
64
+ end
65
+
66
+ private
67
+ def callem(method, nut, node)
68
+ mappings.each {|m| m.send(method, nut, node) }
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,85 @@
1
+ #$:.unshift File.join(File.dirname(__FILE__),'..','lib')
2
+
3
+ require 'test/unit'
4
+ require 'shoulda'
5
+ require 'lib/xmlnuts'
6
+
7
+ class Cheezburger
8
+ include XmlNuts::Nut
9
+
10
+ attribute :weight, :integer
11
+ end
12
+
13
+ class Pet
14
+ include XmlNuts::Nut
15
+
16
+ element :eats, [:string], :xmlname => :ration
17
+ element :species, :string, :whitespace => :collapse
18
+ elements :paws, :string, :xmlname => :paw
19
+
20
+ attribute :has_tail, :boolean, :xmlname => 'has-tail'
21
+ attribute :height, :integer
22
+
23
+ element :cheezburger, Cheezburger
24
+ end
25
+
26
+ class ParsingTest < Test::Unit::TestCase
27
+ context "Old McDonald's pet" do
28
+ setup do
29
+ @xml_fragment = <<-EOS
30
+ <mypet height=' 12 ' has-tail=' yes '>
31
+ <species> silly
32
+ mouse
33
+ </species>
34
+ <ration> tigers
35
+ lions
36
+ </ration>
37
+ <paw> one</paw>
38
+ <paw> two </paw>
39
+ <paw>three</paw>
40
+ <paw>four</paw>
41
+ <cheezburger weight='2'>
42
+ </cheezburger>
43
+ <cub age='4'>
44
+ </cub>
45
+ </mypet>
46
+ EOS
47
+ @pet = Pet.parse(@xml_fragment)
48
+ end
49
+
50
+ should 'be a silly mouse' do
51
+ assert_equal 'silly mouse', @pet.species
52
+ end
53
+
54
+ should 'eat tigers and lions' do
55
+ assert_equal ['tigers', 'lions'], @pet.eats
56
+ end
57
+
58
+ should 'be 12 meters tall' do
59
+ assert_equal 12, @pet.height
60
+ end
61
+
62
+ should 'have tail' do
63
+ assert_equal true, @pet.has_tail
64
+ end
65
+
66
+ should 'have four paws' do
67
+ assert_not_nil @pet.paws
68
+ assert_equal 4, @pet.paws.length
69
+ assert_equal %w(one two three four), @pet.paws
70
+ end
71
+
72
+ context 'should has cheezburger' do
73
+ setup do
74
+ assert_not_nil @burger = @pet.cheezburger
75
+ assert_kind_of Cheezburger, @pet.cheezburger
76
+ end
77
+
78
+ context 'that' do
79
+ should 'weigh 2 kg' do
80
+ assert_equal 2, @burger.weight
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pipa-xmlnuts
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Igor Gunko
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-12-14 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: XmlNuts is an XML to ruby object and back again mapping library.
17
+ email: tekmon@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README
24
+ - MIT-LICENSE
25
+ files:
26
+ - README
27
+ - MIT-LICENSE
28
+ - lib/xmlnuts.rb
29
+ - lib/xmlnuts/nuts.rb
30
+ - lib/xmlnuts/mappings.rb
31
+ - lib/xmlnuts/converters.rb
32
+ has_rdoc: true
33
+ homepage: http://github.com/pipa/xmlnuts
34
+ post_install_message:
35
+ rdoc_options:
36
+ - --line-numbers
37
+ - --inline-source
38
+ - --main
39
+ - README
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: "0"
47
+ version:
48
+ required_rubygems_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: "0"
53
+ version:
54
+ requirements: []
55
+
56
+ rubyforge_project:
57
+ rubygems_version: 1.2.0
58
+ signing_key:
59
+ specification_version: 2
60
+ summary: Making xml<->ruby binding easy
61
+ test_files:
62
+ - test/parsing_test.rb