pipa-xmlnuts 0.1 → 0.2

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.
data/README.rdoc CHANGED
@@ -1,24 +1,23 @@
1
1
  == Introduction
2
2
  XmlNuts is an library that allows for bidirectional mapping between Ruby objects and XML.
3
3
 
4
+ Released under the MIT license.
5
+
4
6
  == Features
5
- - Type awareness (extensible)
6
- - XML namespaces support
7
- - Pluggable backends to work with different XML APIs (only REXML implemented so far)
7
+ - Type awareness (extensible).
8
+ - XML namespaces support.
9
+ - Pluggable backends to work with different XML APIs (REXML implemented so far).
8
10
 
9
11
  == Installation
10
- gem install pipa-xmlnuts --source http://gems.github.com
12
+ gem install pipa-xmlnuts --source http://gems.github.com
11
13
 
12
14
  == Usage
13
- TODO
14
- Please see example below.
15
+ Please see an example below.
16
+ See also +XmlNuts::Nut+, +XmlNuts::Nut::ClassMethods+
15
17
 
16
- == Issue tracking
18
+ == Reporting bugs
17
19
  Please file bugs and feature requests at http://pipa.lighthouseapp.com/projects/21756-xmlnuts
18
20
 
19
- == License
20
- Released under the MIT license (included).
21
-
22
21
  == Example
23
22
  class Cheezburger
24
23
  include XmlNuts::Nut
@@ -37,7 +36,7 @@ Released under the MIT license (included).
37
36
  attribute :ears, :integer
38
37
 
39
38
  element :ration, [:string], :xmlname => :eats, :xmlns => :kthnx
40
- element :name, :string, :whitespace => :collapse, :xmlns => 'urn:x-lol:kthnx'
39
+ element :name, :string, :xmlns => 'urn:x-lol:kthnx'
41
40
  elements :paws, :string, :xmlname => :paw
42
41
 
43
42
  element :friends, :xmlname => :pals do # anonymous class definition follows within block
@@ -47,75 +46,37 @@ Released under the MIT license (included).
47
46
  element :cheezburger, Cheezburger
48
47
  end
49
48
 
50
- class ParsingTest < Test::Unit::TestCase
51
- def setup
52
- @xml_fragment = <<-EOS
53
- <kitteh xmlns='urn:x-lol' xmlns:kthnx='urn:x-lol:kthnx' ears=' 2 ' kthnx:has-tail=' yes '>
54
- <name xmlns='urn:x-lol:kthnx'>
55
- Silly
56
- Tom
57
- Писда
58
- </name>
59
- <kthnx:eats>
60
- tigers
61
- lions
62
- </kthnx:eats>
63
- <pals>
64
- <pal>Chrissy</pal>
65
- <pal>Missy</pal>
66
- <pal>Sissy</pal>
67
- </pals>
68
- <paw> one</paw>
69
- <paw> two </paw>
70
- <paw>three</paw>
71
- <paw>four</paw>
72
- <cheezburger weight='2' />
73
- </kitteh>
74
- EOS
75
- @cat = Cat.parse(@xml_fragment)
76
- end
77
-
78
- context "A cat" do
79
- should 'be named Silly Tom' do
80
- assert_equal 'Silly Tom Писда', @cat.name
81
- end
82
-
83
- should 'eat tigers and lions' do
84
- assert_equal %w(tigers lions), @cat.ration
85
- end
86
-
87
- should 'be a friend of Chrissy, Missy & Sissy' do
88
- assert_equal ['Chrissy', 'Missy', 'Sissy'], @cat.friends.names
89
- end
90
-
91
- should 'have 2 ears' do
92
- assert_equal 2, @cat.ears
93
- end
94
-
95
- should 'have tail' do
96
- assert_equal true, @cat.has_tail
97
- end
98
-
99
- should 'have four paws' do
100
- assert_not_nil @cat.paws
101
- assert_equal 4, @cat.paws.length
102
- assert_equal %w(one two three four), @cat.paws
103
- end
104
-
105
- should 'has cheezburger' do
106
- assert_kind_of Cheezburger, @cat.cheezburger
107
- end
108
- end
109
-
110
- context 'A cheezburger' do
111
- setup do
112
- @burger = @cat.cheezburger
113
- end
114
-
115
- should 'weigh 2 pounds' do
116
- assert_equal 2, @burger.weight
117
- end
118
- end
119
- end
49
+ xml_fragment = <<-EOS
50
+ <kitteh xmlns='urn:x-lol' xmlns:kthnx='urn:x-lol:kthnx' ears=' 2 ' kthnx:has-tail=' yes '>
51
+ <name xmlns='urn:x-lol:kthnx'>
52
+ Silly
53
+ Tom
54
+ </name>
55
+ <kthnx:eats>
56
+ tigers
57
+ lions
58
+ </kthnx:eats>
59
+ <pals>
60
+ <pal>Chrissy</pal>
61
+ <pal>Missy</pal>
62
+ <pal>Sissy</pal>
63
+ </pals>
64
+ <paw> one</paw>
65
+ <paw> two </paw>
66
+ <paw>three</paw>
67
+ <paw>four</paw>
68
+ <cheezburger weight='2' />
69
+ </kitteh>
70
+ EOS
71
+ cat = Cat.parse(xml_fragment)
72
+
73
+ assert_equal 'Silly Tom', cat.name
74
+ assert_equal %w(tigers lions), cat.ration
75
+ assert_equal ['Chrissy', 'Missy', 'Sissy'], cat.friends.names
76
+ assert_equal 2, cat.ears
77
+ assert_equal true, cat.has_tail
78
+ assert_equal %w(one two three four), cat.paws
79
+ assert_kind_of Cheezburger, cat.cheezburger
80
+ assert_equal 2, cat.cheezburger.weight
120
81
  ...
121
- puts @cat.build
82
+ puts cat.build
@@ -1,6 +1,13 @@
1
- require 'time'
2
-
3
1
  module XmlNuts
2
+ autoload :Time, 'time'
3
+ autoload :BigDecimal, 'bigdecimal'
4
+
5
+ # === Currently supported types:
6
+ # string:: see +Convert_string+
7
+ # boolean:: see +Convert_boolean+, +Convert_yesno+
8
+ # numeric:: see +Convert_integer+, +Convert_decimal+, +Convert_float+
9
+ # date & time:: see +Convert_datetime+
10
+ # lists:: see +Convert_list+
4
11
  module Converter
5
12
  def self.lookup(type)
6
13
  lookup!(type)
@@ -24,9 +31,19 @@ module XmlNuts
24
31
  lookup!(type).new(options)
25
32
  end
26
33
 
34
+ # Who could have thought... a string.
35
+ #
36
+ # Specifier:: <tt>:string</tt>
37
+ #
38
+ # ==== Options:
39
+ # [<tt>:whitespace => :collapse</tt>]
40
+ # Whitespace handling behavior.
41
+ # [<tt>:trim</tt>] Trim whitespace from both ends.
42
+ # [<tt>:collapse</tt>] Collapse consecutive whitespace + trim as well.
43
+ # [<tt>:preserve</tt>] Keep'em all.
27
44
  class Convert_string
28
45
  def initialize(options)
29
- @whitespace = options[:whitespace] || :trim
46
+ @whitespace = options[:whitespace] || :collapse
30
47
  end
31
48
 
32
49
  def to_xml(string)
@@ -36,13 +53,26 @@ module XmlNuts
36
53
  def from_xml(string)
37
54
  return nil unless string
38
55
  string = case @whitespace
39
- when :trim then string.gsub(/(\A\s*)|(\s*\Z)/, '')
56
+ when :trim then string.gsub(/\A\s*|\s*\Z/, '')
40
57
  when :preserve then string
41
58
  when :collapse then string.gsub(/\s+/, ' ').gsub(/\A\s*|\s*\Z|\s*(?=\s)/, '')
42
59
  end
43
60
  end
44
61
  end
45
62
 
63
+ # An XSD boolean.
64
+ #
65
+ # Specifier:: <tt>:boolean</tt>
66
+ #
67
+ # ==== Options:
68
+ # [<tt>:format => :true_false</tt>]
69
+ # Format variation.
70
+ # [<tt>:true_false</tt>] <tt>true/false</tt>
71
+ # [<tt>:yes_no</tt>] <tt>yes/no</tt>
72
+ # [<tt>:numeric</tt>] <tt>0/1</tt>
73
+ # In addition supports all options of +Convert_string+.
74
+ #
75
+ # See also +Convert_yesno+.
46
76
  class Convert_boolean < Convert_string
47
77
  def initialize(options)
48
78
  super
@@ -52,16 +82,17 @@ module XmlNuts
52
82
 
53
83
  def to_xml(flag)
54
84
  return nil if flag.nil?
55
- case @format
56
- when :truefalse then flag ? 'true' : 'false'
57
- when :yesno then flag ? 'yes' : 'no'
85
+ string = case @format
86
+ when :true_false then flag ? 'true' : 'false'
87
+ when :yes_no then flag ? 'yes' : 'no'
58
88
  when :numeric then flag ? '0' : '1'
59
89
  end
90
+ super(string)
60
91
  end
61
92
 
62
93
  def from_xml(string)
63
- return nil unless string
64
94
  case string = super(string)
95
+ when nil then nil
65
96
  when '1', 'true', 'yes' then true
66
97
  when '0', 'false', 'no' then false
67
98
  else
@@ -70,20 +101,88 @@ module XmlNuts
70
101
  end
71
102
  end
72
103
 
104
+ # The same as +Convert_boolean+ but with the <tt>:yes_no</tt> default format.
105
+ #
106
+ # Specifier:: <tt>:yesno</tt>
107
+ class Convert_yesno < Convert_boolean
108
+ def initialize(options)
109
+ options[:format] ||= :yes_no
110
+ super
111
+ end
112
+ end
113
+
114
+ # An integer.
115
+ #
116
+ # Specifier:: <tt>:integer</tt>
117
+ #
118
+ # ==== Options
119
+ # Accepts all options of +Convert_string+.
73
120
  class Convert_integer < Convert_string
74
121
  def initialize(options)
75
122
  super
76
123
  end
77
124
 
78
125
  def to_xml(int)
79
- int.to_s
126
+ super(int.to_s)
127
+ end
128
+
129
+ def from_xml(string)
130
+ (string = super(string)) && Integer(string)
131
+ end
132
+ end
133
+
134
+ # A decimal.
135
+ #
136
+ # Specifier:: <tt>:decimal</tt>
137
+ # Ruby type:: +BigDecimal+
138
+ #
139
+ # ==== Options
140
+ # Accepts all options of +Convert_string+.
141
+ class Convert_decimal < Convert_string
142
+ def initialize(options)
143
+ super
144
+ end
145
+
146
+ def to_xml(int)
147
+ super(int && int.to_s('F'))
148
+ end
149
+
150
+ def from_xml(string)
151
+ (string = super(string)) && BigDecimal.new(string)
152
+ end
153
+ end
154
+
155
+ # A float.
156
+ #
157
+ # Specifier:: <tt>:float</tt>
158
+ #
159
+ # ==== Options
160
+ # [<tt>:precision</tt>] Floating point precision.
161
+ #
162
+ # In addition accepts all options of +Convert_string+.
163
+ class Convert_float < Convert_string
164
+ def initialize(options)
165
+ super
166
+ @precision = options[:precision]
167
+ @format = @precision ? "%f.#{@precision}" : '%f'
168
+ end
169
+
170
+ def to_xml(int)
171
+ super(int && sprintf(@format, int))
80
172
  end
81
173
 
82
174
  def from_xml(string)
83
- string && Integer(super(string))
175
+ (string = super(string)) && string.to_f
84
176
  end
85
177
  end
86
178
 
179
+ # An XSD datetime.
180
+ #
181
+ # Specifier:: <tt>:datetime</tt>
182
+ # Ruby type:: +Time+
183
+ #
184
+ # ==== Options
185
+ # Accepts all options of +Convert_string+.
87
186
  class Convert_datetime < Convert_string
88
187
  def initialize(options)
89
188
  super
@@ -91,22 +190,30 @@ module XmlNuts
91
190
  end
92
191
 
93
192
  def to_xml(time)
94
- time && time.xmlschema(@fraction_digits)
193
+ super(time && time.xmlschema(@fraction_digits))
95
194
  end
96
195
 
97
196
  def from_xml(string)
98
- string && Time.parse(super(string, options))
197
+ (string = super(string)) && Time.parse(string)
99
198
  end
100
199
  end
101
200
 
201
+ # An XSD whitespace-separated list.
202
+ #
203
+ # Specifier:: <tt>:list, :item_type => <em>simple type specifier</em></tt>
204
+ # Alternative specifier:: <tt>[<em>simple type specifier</em>]</tt>
205
+ # Ruby type:: +Array+ of <tt><em>simple type</em></tt>
206
+ #
207
+ # ==== Options
208
+ # All options will be passed to the underlying type converter.
102
209
  class Convert_list
103
210
  def initialize(options)
104
211
  @item_type = options[:item_type] || :string
105
212
  @item_converter = Converter.create!(@item_type, options)
106
213
  end
107
214
 
108
- def to_xml(array)
109
- array && array.map {|x| @item_converter.to_xml(x) } * ' '
215
+ def to_xml(items)
216
+ items && items.map {|x| @item_converter.to_xml(x) } * ' '
110
217
  end
111
218
 
112
219
  def from_xml(string)
data/lib/xmlnuts/nuts.rb CHANGED
@@ -3,13 +3,11 @@ require 'xmlnuts/mappings'
3
3
  module XmlNuts #:nodoc:
4
4
  # See also +ClassMethods+
5
5
  module Nut
6
- include XmlBackend
7
-
8
6
  def self.included(other) #:nodoc:
9
7
  other.extend(ClassMethods)
10
8
  end
11
9
 
12
- # See also Nut
10
+ # See also +Nut+.
13
11
  module ClassMethods
14
12
  include XmlBackend
15
13
  include Mappings
@@ -39,8 +37,8 @@ module XmlNuts #:nodoc:
39
37
  # TODO: moar details
40
38
  #
41
39
  # === Arguments
42
- # +xmlname+:: Element name
43
- # +options+:: +:xmlns+ => Element namespace
40
+ # [+xmlname+] Element name
41
+ # [+options+] <tt>:xmlns => <tt> Element namespace
44
42
  #
45
43
  # === Example:
46
44
  # class Cat
@@ -60,10 +58,10 @@ module XmlNuts #:nodoc:
60
58
  # Defines single-element mapping.
61
59
  #
62
60
  # === Arguments
63
- # +name+:: Accessor name
64
- # +type+:: Element type. +:string+ assumed if omitted (see +Converter+).
65
- # +options+:: +:xmlname+, +:xmlns+, converter options (see +Converter+).
66
- # +block+:: An anonymous class definition.
61
+ # [+name+] Accessor name
62
+ # [+type+] Element type. <tt>:string</tt> assumed if omitted (see +Converter+).
63
+ # [+options+] <tt>:xmlname</tt>, <tt>:xmlns</tt>, converter options (see +Converter+).
64
+ # [+block+] An anonymous class definition.
67
65
  #
68
66
  # === Example:
69
67
  # class Cat
@@ -85,10 +83,10 @@ module XmlNuts #:nodoc:
85
83
  # Defines multiple elements mapping.
86
84
  #
87
85
  # === Arguments
88
- # +name+:: Accessor name
89
- # +type+:: Element type. +:string+ assumed if omitted (see +Converter+).
90
- # +options+:: +:xmlname+, +:xmlns+, converter options (see +Converter+).
91
- # +block+:: An anonymous class definition.
86
+ # [+name+] Accessor name
87
+ # [+type+] Element type. <tt>:string</tt> assumed if omitted (see +Converter+).
88
+ # [+options+] <tt>:xmlname</tt>, <tt>:xmlns</tt>, converter options (see +Converter+).
89
+ # [+block+] An anonymous class definition.
92
90
  #
93
91
  # === Example:
94
92
  # class RichCat
@@ -109,9 +107,9 @@ module XmlNuts #:nodoc:
109
107
  # Defines attribute mapping.
110
108
  #
111
109
  # === Arguments
112
- # +name+:: Accessor name
113
- # +type+:: Element type. +:string+ assumed if omitted (see +Converter+).
114
- # +options+:: +:xmlname+, +:xmlns+, converter options (see +Converter+).
110
+ # [+name+] Accessor name
111
+ # [+type+] Element type. <tt>:string</tt> assumed if omitted (see +Converter+).
112
+ # [+options+] <tt>:xmlname</tt>, <tt>:xmlns</tt>, converter options (see +Converter+).
115
113
  #
116
114
  # === Example:
117
115
  # class Cat
@@ -128,13 +126,20 @@ module XmlNuts #:nodoc:
128
126
 
129
127
  # mappings -> Array
130
128
  #
131
- # Returns all XmlNuts mappings defined on a class.
129
+ # Returns all mappings defined on a class.
132
130
  def mappings
133
131
  @mappings ||= []
134
132
  end
135
133
 
136
134
  def parse(source, options = {})
137
- backend.parse(source, options) {|node| new.parse_node(node) }
135
+ backend.parse(source, options) {|node| parse_node(new, node) }
136
+ end
137
+
138
+ def build(nut, result = :string, options = {})
139
+ options, result = result, :string if result.is_a?(Hash)
140
+ options[:xmlname] ||= root.xmlname
141
+ options[:xmlns_prefix] = namespaces.invert[options[:xmlns] ||= root.xmlns]
142
+ backend.build(result, options) {|node| build_node(nut, node) }
138
143
  end
139
144
 
140
145
  def build_node(nut, node) #:nodoc:
@@ -192,15 +197,14 @@ module XmlNuts #:nodoc:
192
197
  # Defines attribute mapping.
193
198
  #
194
199
  # === Arguments
195
- # +destination+:: Can be given a symbol a backend-specific object,
196
- # an instance of String or IO classes.
197
- # - +:string+ -> will return an XML string.
198
- # - +:document+ -> will return a backend specific document object.
199
- # - +:object+ -> will return a backend specific object. New document will be created.
200
- # - an instance of +String+: the contents of the string will be replaced with
201
- # the generated XML.
202
- # - an instance of +IO+: the IO will be written to.
203
- # +options+:: Backend-specific options
200
+ # [+destination+]
201
+ # Can be given a symbol a backend-specific object, an instance of String or IO classes.
202
+ # [<tt>:string</tt>] will return an XML string.
203
+ # [<tt>:document</tt>] will return a backend specific document object.
204
+ # [<tt>:object</tt>] will return a backend specific object. New document will be created.
205
+ # [an instance of +String+] the contents of the string will be replaced with the generated XML.
206
+ # [an instance of +IO+] the IO will be written to.
207
+ # [+options+] Backend-specific options
204
208
  #
205
209
  # === Example:
206
210
  # cat = Cat.new
@@ -211,19 +215,7 @@ module XmlNuts #:nodoc:
211
215
  # cat.build(doc)
212
216
  # puts doc.to_s
213
217
  def build(result = :string, options = {})
214
- options, result = result, :string if result.is_a?(Hash)
215
- root = self.class.root
216
- options[:xmlname] ||= root.xmlname
217
- options[:xmlns_prefix] = self.class.namespaces.invert[options[:xmlns] ||= root.xmlns]
218
- backend.build(result, options) {|node| build_node(node) }
219
- end
220
-
221
- def parse_node(node) #:nodoc:
222
- self.class.parse_node(self, node)
223
- end
224
-
225
- def build_node(node) #:nodoc:
226
- self.class.build_node(self, node)
218
+ self.class.build(self, result, options)
227
219
  end
228
220
  end
229
221
  end
data/test/parsing_test.rb CHANGED
@@ -4,10 +4,12 @@ require 'test/unit'
4
4
  require 'shoulda'
5
5
  require 'lib/xmlnuts'
6
6
 
7
+
7
8
  class Cheezburger
8
9
  include XmlNuts::Nut
9
10
 
10
- attribute :weight, :integer
11
+ attribute :weight, :float
12
+ attribute :price, :decimal
11
13
  end
12
14
 
13
15
  class Cat
@@ -21,7 +23,7 @@ class Cat
21
23
  attribute :ears, :integer
22
24
 
23
25
  element :ration, [:string], :xmlname => :eats, :xmlns => :kthnx
24
- element :name, :string, :whitespace => :collapse, :xmlns => 'urn:x-lol:kthnx'
26
+ element :name, :string, :xmlns => 'urn:x-lol:kthnx'
25
27
  elements :paws, :string, :xmlname => :paw
26
28
 
27
29
  element :friends, :xmlname => :pals do
@@ -38,7 +40,6 @@ class ParsingTest < Test::Unit::TestCase
38
40
  <name xmlns='urn:x-lol:kthnx'>
39
41
  Silly
40
42
  Tom
41
- Писта
42
43
  </name>
43
44
  <kthnx:eats>
44
45
  tigers
@@ -53,7 +54,7 @@ class ParsingTest < Test::Unit::TestCase
53
54
  <paw> two </paw>
54
55
  <paw>three</paw>
55
56
  <paw>four</paw>
56
- <cheezburger weight='2' />
57
+ <cheezburger price='2.05' weight='14.5547' />
57
58
  </kitteh>
58
59
  EOS
59
60
  @cat = Cat.parse(@xml_fragment)
@@ -61,7 +62,7 @@ class ParsingTest < Test::Unit::TestCase
61
62
 
62
63
  context "A cat" do
63
64
  should 'be named Silly Tom' do
64
- assert_equal 'Silly Tom Писта', @cat.name
65
+ assert_equal 'Silly Tom', @cat.name
65
66
  end
66
67
 
67
68
  should 'eat tigers and lions' do
@@ -77,12 +78,10 @@ class ParsingTest < Test::Unit::TestCase
77
78
  end
78
79
 
79
80
  should 'have tail' do
80
- assert_equal true, @cat.has_tail
81
+ assert @cat.has_tail
81
82
  end
82
83
 
83
84
  should 'have four paws' do
84
- assert_not_nil @cat.paws
85
- assert_equal 4, @cat.paws.length
86
85
  assert_equal %w(one two three four), @cat.paws
87
86
  end
88
87
 
@@ -96,8 +95,12 @@ class ParsingTest < Test::Unit::TestCase
96
95
  @burger = @cat.cheezburger
97
96
  end
98
97
 
99
- should 'weigh 2 pounds' do
100
- assert_equal 2, @burger.weight
98
+ should 'weigh 14.5547 pounds' do
99
+ assert_equal 14.5547, @burger.weight
100
+ end
101
+
102
+ should 'cost $2.05' do
103
+ assert_equal BigDecimal('2.05'), @burger.price
101
104
  end
102
105
  end
103
106
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pipa-xmlnuts
3
3
  version: !ruby/object:Gem::Version
4
- version: "0.1"
4
+ version: "0.2"
5
5
  platform: ruby
6
6
  authors:
7
7
  - Igor Gunko
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-12-16 00:00:00 -08:00
12
+ date: 2008-12-17 00:00:00 -08:00
13
13
  default_executable:
14
14
  dependencies: []
15
15