pipa-xmlnuts 0.1 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
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