omg-peanuts 2.0.0 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -47,18 +47,21 @@ Please report via Github issue tracking.
47
47
 
48
48
  root 'kitteh', :xmlns => :lol
49
49
 
50
- attribute :has_tail, :boolean, :xmlname => 'has-tail', :xmlns => 'urn:x-lol:kthnx'
50
+ attribute :has_tail, :boolean, :xmlname => 'has-tail', :xmlns => :kthnx
51
51
  attribute :ears, :integer
52
52
 
53
53
  element :ration, [:string], :xmlname => :eats, :xmlns => :kthnx
54
- element :name, :string, :xmlns => 'urn:x-lol:kthnx'
55
- elements :paws, :string, :xmlname => :paw
54
+ element :name, :xmlns => 'urn:x-lol:kthnx'
55
+ elements :paws, :xmlname => :paw
56
56
 
57
- element :friends, :xmlname => :pals do # anonymous class definition follows within block
58
- elements :names, :string, :xmlname => :friend, :xmlname => :pal
57
+ element :friends, :xmlname => :pals do
58
+ elements :names, :xmlname => :pal
59
59
  end
60
60
 
61
61
  element :cheezburger, Cheezburger
62
+ element :moar_cheezburgers do
63
+ elements :cheezburger, Cheezburger
64
+ end
62
65
  end
63
66
 
64
67
  xml_fragment = <<-EOS
@@ -83,7 +86,7 @@ Please report via Github issue tracking.
83
86
  <cheezburger weight='2' />
84
87
  </kitteh>
85
88
  EOS
86
- cat = Cat.parse(xml_fragment)
89
+ cat = Cat.restore_from(xml_fragment)
87
90
 
88
91
  assert_equal 'Silly Tom', cat.name
89
92
  assert_equal %w(tigers lions), cat.ration
@@ -94,12 +97,13 @@ Please report via Github issue tracking.
94
97
  assert_kind_of Cheezburger, cat.cheezburger
95
98
  assert_equal 2, cat.cheezburger.weight
96
99
  ...
97
- puts cat.build
100
+ puts cat.save_to(:string)
98
101
 
99
102
 
100
103
  === See also
101
- * http://github.com/omg/threadpool -- Thread pool implementation
104
+ * http://github.com/omg/quack -- ... here is my handle, here is my spout
102
105
  * http://github.com/omg/statelogic -- A simple state machine for ActiveRecord
106
+ * http://github.com/omg/threadpool -- Thread pool implementation
103
107
 
104
108
 
105
109
  Copyright (c) 2009 Igor Gunko, released under the MIT license
data/Rakefile CHANGED
@@ -5,7 +5,7 @@ require 'rake'
5
5
  require 'rake/clean'
6
6
  require 'rake/gempackagetask'
7
7
  require 'rake/rdoctask'
8
- require 'rake/testtask'
8
+ require 'spec/rake/spectask'
9
9
 
10
10
  Rake::GemPackageTask.new(Gem::Specification.load('peanuts.gemspec')) do |p|
11
11
  p.need_tar = true
@@ -21,6 +21,9 @@ Rake::RDocTask.new do |rdoc|
21
21
  rdoc.options << '--line-numbers' << '--inline-source'
22
22
  end
23
23
 
24
- Rake::TestTask.new do |t|
25
- t.test_files = FileList['test/**/*.rb']
24
+ desc 'Run specs'
25
+ task :test => :spec
26
+
27
+ Spec::Rake::SpecTask.new do |t|
28
+ t.spec_files = FileList['spec/**/*.rb']
26
29
  end
@@ -83,8 +83,8 @@ module Peanuts
83
83
  def to_xml(flag)
84
84
  return nil if flag.nil?
85
85
  string = case @format
86
- when :true_false then flag ? 'true' : 'false'
87
- when :yes_no then flag ? 'yes' : 'no'
86
+ when :true_false, :truefalse then flag ? 'true' : 'false'
87
+ when :yes_no, :yesno then flag ? 'yes' : 'no'
88
88
  when :numeric then flag ? '0' : '1'
89
89
  end
90
90
  super(string)
@@ -4,30 +4,21 @@ module Peanuts
4
4
  class Mapper
5
5
  include Enumerable
6
6
 
7
- attr_reader :root, :namespaces, :nscontext, :container
7
+ attr_reader :root, :namespaces, :ns_context, :default_ns
8
8
  attr_accessor :schema
9
9
 
10
- def initialize
10
+ def initialize(ns_context = nil, default_ns = nil)
11
+ @ns_context, @default_ns = ns_context, default_ns
11
12
  @mappings, @footprints = [], {}
12
- @namespaces = Hash.new do |h, k|
13
- nscontext && nscontext[k] || raise(IndexError)
14
- end
13
+ @namespaces = ns_context ? Hash.new {|h, k| ns_context[k] || raise(IndexError) } : {}
15
14
  end
16
15
 
17
16
  def root=(root)
18
17
  raise 'root already defined' if @root
19
- raise 'root in nested scopes not supported' if nested?
18
+ # TODO raise 'root in nested scopes not supported' if nested?
20
19
  @root = root
21
20
  end
22
21
 
23
- def set_context(container, nscontext)
24
- @container, @nscontext = container, nscontext
25
- end
26
-
27
- def nested?
28
- !!container
29
- end
30
-
31
22
  def each(&block)
32
23
  @mappings.each(&block)
33
24
  end
@@ -38,24 +29,17 @@ module Peanuts
38
29
  @mappings << (@footprints[fp] = mapping)
39
30
  end
40
31
 
41
- def parse(nut, reader)
32
+ def restore(nut, reader)
42
33
  rdfp = ReaderFootprint.new(reader)
43
34
  reader.each do
44
35
  m = @footprints[rdfp]
45
- m.from_xml(nut, reader) if m
36
+ m.restore(nut, reader) if m
46
37
  end
47
38
  nut
48
39
  end
49
40
 
50
- def build(nut, writer)
51
- if @root
52
- @root.to_xml(writer) do
53
- _save(nut, writer)
54
- end
55
- else
56
- _save(nut, writer)
57
- end
58
- writer.result
41
+ def save(nut, writer)
42
+ @root ? @root.save(writer) { _save(nut, writer) } : _save(nut, writer)
59
43
  end
60
44
 
61
45
  def clear(nut)
@@ -64,7 +48,7 @@ module Peanuts
64
48
 
65
49
  private
66
50
  def _save(nut, writer)
67
- @mappings.each {|m| m.to_xml(nut, writer) }
51
+ @mappings.each {|m| m.save(nut, writer) } if nut
68
52
  end
69
53
 
70
54
  class Footprint
@@ -25,7 +25,7 @@ module Peanuts
25
25
  class Root < Mapping
26
26
  node_type :element
27
27
 
28
- def to_xml(writer, &block)
28
+ def save(writer, &block)
29
29
  writer.write(node_type, xmlname, xmlns, prefix, &block)
30
30
  end
31
31
  end
@@ -37,7 +37,7 @@ module Peanuts
37
37
  super(options.delete(:xmlname) || name, options)
38
38
  case type
39
39
  when Array
40
- raise ArgumentError, "invalid value for type: #{type}" if type.length != 1
40
+ raise ArgumentError, "invalid value for type: #{type.inspect}" if type.length != 1
41
41
  options[:item_type] = type.first
42
42
  @converter = Converter.create!(:list, options)
43
43
  when Class
@@ -48,12 +48,12 @@ module Peanuts
48
48
  @name, @setter, @type = name.to_sym, :"#{name}=", type
49
49
  end
50
50
 
51
- def to_xml(nut, node)
52
- setxml(node, get(nut))
51
+ def save(nut, writer)
52
+ save_value(writer, get(nut))
53
53
  end
54
54
 
55
- def from_xml(nut, node)
56
- set(nut, getxml2(node, get(nut)))
55
+ def restore(nut, reader)
56
+ set(nut, restore_value(reader, get(nut)))
57
57
  end
58
58
 
59
59
  def clear(nut)
@@ -69,99 +69,110 @@ module Peanuts
69
69
  nut.send(@setter, value)
70
70
  end
71
71
 
72
- def toxml(value)
72
+ def to_xml(value)
73
73
  @converter ? @converter.to_xml(value) : value
74
74
  end
75
75
 
76
- def froxml(text)
76
+ def from_xml(text)
77
77
  @converter ? @converter.from_xml(text) : text
78
78
  end
79
79
 
80
- def getxml2(node, acc)
81
- getxml(node)
80
+ def write(writer, &block)
81
+ writer.write(node_type, xmlname, xmlns, prefix, &block)
82
82
  end
83
83
 
84
- def parse(events)
84
+ def restore_object(events)
85
85
  type.send(:_restore, events)
86
86
  end
87
87
 
88
- def build(nut, writer)
88
+ def save_object(nut, writer)
89
89
  type.send(:_save, nut, writer)
90
90
  end
91
91
  end
92
92
 
93
- class ElementValue < MemberMapping
94
- node_type :element
95
-
93
+ module SingleMapping
96
94
  private
97
- def getxml(node)
98
- froxml(node.value)
95
+ def restore_value(reader, acc)
96
+ read_value(reader)
99
97
  end
100
98
 
101
- def setxml(writer, value)
102
- writer.write(node_type, xmlname, xmlns, prefix) do |w|
103
- w.value = toxml(value)
104
- end
99
+ def save_value(writer, value)
100
+ write(writer) {|w| write_value(w, value) }
105
101
  end
106
102
  end
107
103
 
108
- class Element < MemberMapping
109
- node_type :element
110
-
111
- private
112
- def getxml(node)
113
- parse(node)
104
+ module MultiMapping
105
+ def restore_value(reader, acc)
106
+ (acc || []) << read_value(reader)
114
107
  end
115
108
 
116
- def setxml(writer, value)
117
- writer.write(node_type, xmlname, xmlns, prefix) do |w|
118
- build(value, w)
109
+ def save_value(writer, values)
110
+ for value in values
111
+ write(writer) {|w| write_value(w, value) }
119
112
  end
120
113
  end
121
114
  end
122
115
 
123
- class Attribute < MemberMapping
124
- node_type :attribute
116
+ module ValueMapping
117
+ private
118
+ def read_value(reader)
119
+ from_xml(reader.value)
120
+ end
125
121
 
126
- def initialize(name, type, options)
127
- super
128
- raise ArgumentError, 'a namespaced attribute must have namespace prefix' if xmlns && !prefix
122
+ def write_value(writer, value)
123
+ writer.value = to_xml(value)
129
124
  end
125
+ end
130
126
 
127
+ module ObjectMapping
131
128
  private
132
- def getxml(node)
133
- froxml(node.value)
129
+ def read_value(reader)
130
+ restore_object(reader)
134
131
  end
135
132
 
136
- def setxml(node, value)
137
- backend.set_attribute(node, xmlname, xmlns, toxml(value))
133
+ def write_value(writer, value)
134
+ save_object(value, writer)
138
135
  end
139
136
  end
140
137
 
141
- class ElementValues < MemberMapping
138
+ class ElementValue < MemberMapping
139
+ include SingleMapping
140
+ include ValueMapping
141
+
142
142
  node_type :element
143
+ end
143
144
 
144
- private
145
- def getxml2(node, acc)
146
- (acc || []) << froxml(node.value)
147
- end
145
+ class Element < MemberMapping
146
+ include SingleMapping
147
+ include ObjectMapping
148
+
149
+ node_type :element
150
+ end
148
151
 
149
- def setxml(node, values)
150
- values.each {|v| add_element(node, toxml(v)) } if values
152
+ class Attribute < MemberMapping
153
+ include SingleMapping
154
+ include ValueMapping
155
+
156
+ node_type :attribute
157
+
158
+ def initialize(name, type, options)
159
+ super
160
+ raise ArgumentError, 'a namespaced attribute must have namespace prefix' if xmlns && !prefix
151
161
  end
152
162
  end
153
163
 
154
- class Elements < MemberMapping
164
+ class ElementValues < MemberMapping
165
+ include MultiMapping
166
+ include ValueMapping
167
+
155
168
  node_type :element
169
+ end
156
170
 
157
- private
158
- def getxml2(node, acc)
159
- (acc || []) << parse(node)
160
- end
171
+ class Elements < MemberMapping
172
+ include MultiMapping
173
+ include ObjectMapping
161
174
 
162
- def setxml(node, elements)
163
- elements.each {|e| build(node, e, add_element(node)) } if elements
164
- end
175
+ node_type :element
165
176
  end
166
177
  end
167
178
  end
@@ -1,27 +1,28 @@
1
1
  require 'peanuts/mappings'
2
2
  require 'peanuts/mapper'
3
- require 'peanuts/xml/reader'
4
3
 
5
4
  module Peanuts #:nodoc:
6
- # See also +ClassMethods+
7
5
  def self.included(other) #:nodoc:
8
- other.extend(ClassMethods)
6
+ init(other)
7
+ end
8
+
9
+ def self.init(cls, ns_context = nil, default_ns = nil, &block) #:nodoc:
10
+ cls.instance_eval do
11
+ include InstanceMethods
12
+ extend ClassMethods
13
+ @mapper = Mapper.new(ns_context, default_ns)
14
+ instance_eval(&block) if block_given?
15
+ end
9
16
  end
10
17
 
11
- # See also +PeaNuts+.
18
+ # See also +InstanceMethods+.
12
19
  module ClassMethods
13
20
  include Mappings
14
21
 
15
- def self.extended(other)
16
- other.instance_eval do
17
- @mappings = Mapper.new
18
- end
19
- end
20
-
21
- # mappings -> Mapper
22
+ # mapper -> Mapper
22
23
  #
23
- # Returns all mappings defined on a class.
24
- attr_reader :mappings
24
+ # Returns the mapper for the class.
25
+ attr_reader :mapper
25
26
 
26
27
  # namespaces(hash) -> Hash
27
28
  # namespaces -> Hash
@@ -36,8 +37,8 @@ module Peanuts #:nodoc:
36
37
  # namespaces :lol => 'urn:lol', ...
37
38
  # ...
38
39
  # end
39
- def namespaces(mappings = nil)
40
- mappings ? @mappings.namespaces.update(mappings) : @mappings.namespaces
40
+ def namespaces(map = nil)
41
+ map ? mapper.namespaces.update(map) : mapper.namespaces
41
42
  end
42
43
 
43
44
  # root(xmlname[, :xmlns => ...]) -> Mappings::Root
@@ -58,8 +59,8 @@ module Peanuts #:nodoc:
58
59
  # ...
59
60
  # end
60
61
  def root(xmlname = nil, options = {})
61
- @mappings.root = Root.new(xmlname, prepare_options(options)) if xmlname
62
- @mappings.root
62
+ mapper.root = Root.new(xmlname, prepare_options(options)) if xmlname
63
+ mapper.root
63
64
  end
64
65
 
65
66
  # element(name, [type[, options]]) -> Mappings::Element or Mappings::ElementValue
@@ -81,10 +82,10 @@ module Peanuts #:nodoc:
81
82
  # element :cheeseburger, Cheeseburger, :xmlname => :cheezburger
82
83
  # ...
83
84
  # end
84
- def element(name, type = :string, options = {}, &block)
85
- prepare_args(type, options, block) do |type, options|
85
+ def element(name, *args, &block)
86
+ prepare_args(args, block) do |type, options|
86
87
  define_accessor name
87
- (@mappings << (type.is_a?(Class) ? Element : ElementValue).new(name, type, options)).last
88
+ (mapper << (type.is_a?(Class) ? Element : ElementValue).new(name, type, options)).last
88
89
  end
89
90
  end
90
91
 
@@ -107,10 +108,10 @@ module Peanuts #:nodoc:
107
108
  # elements :cheeseburgers, Cheeseburger, :xmlname => :cheezburger
108
109
  # ...
109
110
  # end
110
- def elements(name, type = :string, options = {}, &block)
111
- prepare_args(type, options, block) do |type, options|
111
+ def elements(name, *args, &block)
112
+ prepare_args(args, block) do |type, options|
112
113
  define_accessor name
113
- (@mappings << (type.is_a?(Class) ? Elements : ElementValues).new(name, type, options)).last
114
+ (mapper << (type.is_a?(Class) ? Elements : ElementValues).new(name, type, options)).last
114
115
  end
115
116
  end
116
117
 
@@ -131,14 +132,16 @@ module Peanuts #:nodoc:
131
132
  # element :cheeseburger, Cheeseburger, :xmlname => :cheezburger
132
133
  # ...
133
134
  # end
134
- def attribute(name, type = :string, options = {})
135
- define_accessor name
136
- @mappings << Attribute.new(name, type, prepare_options({:xmlns => nil}.merge(options)))
135
+ def attribute(name, *args)
136
+ prepare_args(args, nil, :xmlns => nil) do |type, options|
137
+ define_accessor name
138
+ mapper << Attribute.new(name, type, options)
139
+ end
137
140
  end
138
141
 
139
142
  def schema(schema = nil)
140
- @mappings.schema = schema if schema
141
- @mappings.schema
143
+ mapper.schema = schema if schema
144
+ mapper.schema
142
145
  end
143
146
 
144
147
  def restore(reader)
@@ -146,70 +149,49 @@ module Peanuts #:nodoc:
146
149
  e && _restore(e)
147
150
  end
148
151
 
149
- def restore_from(source_or_type = nil, source = nil, options = {})
150
- _source_or_dest(source_or_type, source) do |source_type, source|
152
+ def restore_from(*args)
153
+ _source_or_dest(*args) do |source_type, source, options|
151
154
  restore(XML::Reader.new(source, source_type, options))
152
155
  end
153
156
  end
154
157
 
155
158
  def save(nut, writer)
156
159
  _save(nut, writer)
160
+ writer.result
157
161
  end
158
162
 
159
- def build(nut, result = :string, options = {})
160
- options, result = result, :string if result.is_a?(Hash)
161
- options[:xmlname] ||= root.xmlname
162
- options[:xmlns_prefix] = namespaces.invert[options[:xmlns] ||= root.xmlns]
163
- backend.build(result, options) {|node| build_node(nut, node) }
164
- end
165
-
166
- def build_node(nut, node) #:nodoc:
167
- backend.add_namespaces(node, namespaces)
168
- callem(:to_xml, nut, node)
169
- node
170
- end
171
-
172
- def parse_events(nut, events) #:nodoc:
173
- @mappings.parse(nut, events)
174
- end
175
-
176
- def _source_or_dest(a, b)
177
- a, b = :string, a unless a.is_a?(Symbol)
178
- yield a, b
163
+ def _source_or_dest(*args)
164
+ options = args.last.is_a?(Hash) ? args.pop : {}
165
+ type, source = *args
166
+ type, source = :string, type unless type.is_a?(Symbol)
167
+ yield type, source, options
179
168
  end
180
169
 
181
170
  private
182
- def _restore(events)
183
- nut = new
184
- @mappings.parse(nut, events)
171
+ def _restore(reader)
172
+ mapper.restore(nut = new, reader)
185
173
  nut
186
174
  end
187
175
 
188
- def _save(nut, events)
189
- @mappings.build(nut, events)
176
+ def _save(nut, writer)
177
+ mapper.save(nut, writer)
190
178
  end
191
179
 
192
- def prepare_args(type, options, blk)
193
- if blk
194
- options = prepare_options(type) if type.is_a?(Hash)
195
- type = Class.new
196
- yield(type, options).tap do |m|
197
- type.instance_eval do
198
- include Peanuts
199
- mappings.set_context(m, namespaces)
200
- instance_eval(&blk)
201
- end
202
- end
203
- else
204
- options = prepare_options(options)
205
- yield type, options
206
- end
180
+ def prepare_args(args, blk, defopt = nil)
181
+ type, options = *args
182
+ type, options = (blk ? Class.new : :string), type if type.nil? || type.is_a?(Hash)
183
+ options ||= {}
184
+ options = defopt.merge(options) if defopt
185
+ options = prepare_options(options)
186
+ m = yield(type, options)
187
+ Peanuts.init(type, mapper.namespaces, m.xmlns, &blk) if blk
188
+ m
207
189
  end
208
190
 
209
191
  def prepare_options(options)
210
- ns = options.fetch(:xmlns) {|k| options[k] = root && root.xmlns || @mappings.container && @mappings.container.xmlns }
192
+ ns = options.fetch(:xmlns) {|k| options[k] = root && root.xmlns || mapper.default_ns }
211
193
  if ns.is_a?(Symbol)
212
- raise ArgumentError, "undefined prefix: #{ns}" unless options[:xmlns] = namespaces[ns]
194
+ raise ArgumentError, "undefined prefix: #{ns}" unless options[:xmlns] = mapper.namespaces[ns]
213
195
  options[:prefix] = ns
214
196
  end
215
197
  options
@@ -217,47 +199,50 @@ module Peanuts #:nodoc:
217
199
 
218
200
  def define_accessor(name)
219
201
  if method_defined?(name) || method_defined?("#{name}=")
220
- raise ArgumentError, "#{name}: name already defined or reserved"
202
+ raise ArgumentError, "#{name.inspect}: name already defined or reserved"
221
203
  end
222
204
  attr_accessor name
223
205
  end
224
206
  end
225
207
 
226
- def parse(source, options = {})
227
- backend.parse(source, options) {|node| parse_node(node) }
228
- end
208
+ # See also +ClassMethods+
209
+ module InstanceMethods
210
+ def parse(source, options = {})
211
+ backend.parse(source, options) {|node| parse_node(node) }
212
+ end
229
213
 
230
- def save(writer)
231
- self.class.save(self, writer)
232
- end
214
+ def save(writer)
215
+ self.class.save(self, writer)
216
+ end
233
217
 
234
- # build([options]) -> root element or string
235
- # build([options]) -> root element or string
236
- # build(destination[, options]) -> destination
237
- #
238
- # Defines attribute mapping.
239
- #
240
- # === Arguments
241
- # [+destination+]
242
- # Can be given a symbol a backend-specific object, an instance of String or IO classes.
243
- # [<tt>:string</tt>] will return an XML string.
244
- # [<tt>:document</tt>] will return a backend specific document object.
245
- # [<tt>:object</tt>] will return a backend specific object. New document will be created.
246
- # [an instance of +String+] the contents of the string will be replaced with the generated XML.
247
- # [an instance of +IO+] the IO will be written to.
248
- # [+options+] Backend-specific options
249
- #
250
- # === Example:
251
- # cat = Cat.new
252
- # cat.name = 'Pussy'
253
- # puts cat.build
254
- # ...
255
- # doc = REXML::Document.new
256
- # cat.build(doc)
257
- # puts doc.to_s
258
- def save_to(dest_or_type = :string, dest = nil, options = {})
259
- self.class._source_or_dest(dest_or_type, dest) do |dest_type, dest|
260
- save(XML::Writer.new(dest, dest_type, options))
218
+ # build([options]) -> root element or string
219
+ # build([options]) -> root element or string
220
+ # build(destination[, options]) -> destination
221
+ #
222
+ # Defines attribute mapping.
223
+ #
224
+ # === Arguments
225
+ # [+destination+]
226
+ # Can be given a symbol a backend-specific object, an instance of String or IO classes.
227
+ # [<tt>:string</tt>] will return an XML string.
228
+ # [<tt>:document</tt>] will return a backend specific document object.
229
+ # [<tt>:object</tt>] will return a backend specific object. New document will be created.
230
+ # [an instance of +String+] the contents of the string will be replaced with the generated XML.
231
+ # [an instance of +IO+] the IO will be written to.
232
+ # [+options+] Backend-specific options
233
+ #
234
+ # === Example:
235
+ # cat = Cat.new
236
+ # cat.name = 'Pussy'
237
+ # puts cat.save_to(:string)
238
+ # ...
239
+ # doc = LibXML::XML::Document.new
240
+ # cat.save_to(doc)
241
+ # puts doc.to_s
242
+ def save_to(*args)
243
+ self.class._source_or_dest(*args) do |dest_type, dest, options|
244
+ save(XML::Writer.new(dest, dest_type, options))
245
+ end
261
246
  end
262
247
  end
263
248
  end
@@ -1 +1,78 @@
1
- require 'peanuts/xml/reader'
1
+ require 'enumerator'
2
+
3
+ class Hash
4
+ def from_namespace(ns)
5
+ rx = /^#{Regexp.quote(ns.to_s)}_(.*)$/
6
+ inject({}) do |a, p|
7
+ a[$1.to_sym] = p[1] if p[0].to_s =~ rx
8
+ a
9
+ end
10
+ end
11
+
12
+ def from_namespace!(ns)
13
+ h = from_namespace(ns)
14
+ h.each_key {|k| delete(:"#{ns}_#{k}") }
15
+ h
16
+ end
17
+ end
18
+
19
+ module Peanuts
20
+ module XML
21
+ autoload :LibXML, 'peanuts/xml/libxml'
22
+
23
+ def self.default
24
+ @@default ||= LibXML
25
+ end
26
+
27
+ def self.schema(schema_type, source, source_type = :string)
28
+ default.schema(schema_type, source, source_type)
29
+ end
30
+
31
+ def self.method_missing(method, *args, &block)
32
+ case method.to_s
33
+ when /^(.*)_schema_from_(.*)$/
34
+ XML.schema($1.to_sym, args.first, $2.to_sym)
35
+ else
36
+ super
37
+ end
38
+ end
39
+
40
+ class Reader
41
+ include Enumerable
42
+
43
+ def self.new(*args, &block)
44
+ cls = self == Reader ? XML.default::Reader : self
45
+ obj = cls.allocate
46
+ obj.send(:initialize, *args, &block)
47
+ obj
48
+ end
49
+
50
+ def self.method_missing(method, *args, &block)
51
+ case method.to_s
52
+ when /^from_(.*)$/
53
+ new(args.first, $1.to_sym, &block)
54
+ else
55
+ super
56
+ end
57
+ end
58
+ end
59
+
60
+ class Writer
61
+ def self.new(*args, &block)
62
+ cls = self == Writer ? XML.default::Writer : self
63
+ obj = cls.allocate
64
+ obj.send(:initialize, *args, &block)
65
+ obj
66
+ end
67
+
68
+ def self.method_missing(method, *args, &block)
69
+ case method.to_s
70
+ when /^from_(.*)$/
71
+ new(args.first, $1.to_sym, &block)
72
+ else
73
+ super
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -1,6 +1,6 @@
1
1
  require 'libxml'
2
2
  require 'forwardable'
3
- require 'peanuts/xml/reader'
3
+ require 'peanuts/xml'
4
4
 
5
5
  module Peanuts
6
6
  module XML
@@ -14,7 +14,7 @@ module Peanuts
14
14
  when :io
15
15
  dest
16
16
  when :document
17
- dest || LibXML::XML::Document.new
17
+ dest || ::LibXML::XML::Document.new
18
18
  else
19
19
  raise ArgumentError, "unrecognized destination type #{dest_type.inspect}"
20
20
  end
@@ -24,31 +24,31 @@ module Peanuts
24
24
  @dest
25
25
  end
26
26
 
27
- attr_writer :value
28
-
29
- def clear
30
- @node_type = @local_name = @namespace_uri = @prefix = @value = nil
27
+ def value=(value)
28
+ case @node
29
+ when ::LibXML::XML::Attr
30
+ @node.value = value || ''
31
+ else
32
+ @node.content = value || ''
33
+ end
31
34
  end
32
35
 
33
36
  def write(node_type, local_name = nil, namespace_uri = nil, prefix = nil)
34
- if @node_type
35
- mknode
37
+ case node_type
38
+ when :element
39
+ @node = ::LibXML::XML::Node.new(local_name)
40
+ @parent << @node if @parent
41
+ @node.namespaces.namespace = mkns(@node, namespace_uri, prefix) if namespace_uri
42
+ when :attribute
43
+ @node = ::LibXML::XML::Attr.new(@parent, local_name, '', namespace_uri && mkns(@parent, namespace_uri, prefix))
36
44
  else
37
- @node = nil
45
+ raise "unsupported node type #{node_type.inspect}"
38
46
  end
39
47
 
40
- @node_type = node_type
41
- @local_name, @namespace_uri, @prefix = local_name, namespace_uri, prefix && prefix.to_s
42
-
43
48
  exparent, @parent = @parent, @node
44
49
 
45
50
  yield self
46
51
 
47
- if @node_type
48
- mknode
49
- end
50
- @node = nil
51
-
52
52
  if exparent.nil?
53
53
  case @dest_type
54
54
  when :string, :io
@@ -62,22 +62,11 @@ module Peanuts
62
62
  end
63
63
 
64
64
  private
65
- def mknode
66
- @node = case @node_type
67
- when :element
68
- n = ::LibXML::XML::Node.new(@local_name, @value)
69
- if @namespace_uri
70
- ns = @parent && @parent.namespaces.find_by_href(@namespace_uri)
71
- n.namespaces.namespace = ns && ns.prefix == @prefix ? ns : ::LibXML::XML::Namespace.new(n, @prefix, @namespace_uri)
72
- end
73
- @parent << n if @parent
74
- n
75
- when :attribute
76
- ::LibXML::XML::Attr.new(@parent, @local_name, @value, @namespace_uri)
77
- else
78
- raise "unsupported node type #{@node_type.inspect}"
79
- end
80
- clear
65
+ def mkns(node, namespace_uri, prefix)
66
+ prefix = prefix && prefix.to_s
67
+ ns = node && node.namespaces.find_by_href(namespace_uri)
68
+ ns = ::LibXML::XML::Namespace.new(node, prefix, namespace_uri) unless ns && ns.prefix == prefix
69
+ ns
81
70
  end
82
71
  end
83
72
 
@@ -0,0 +1,142 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
2
+
3
+ require 'bigdecimal'
4
+ require 'test/unit'
5
+ require 'rubygems'
6
+ require 'shoulda'
7
+ require 'peanuts'
8
+
9
+
10
+ class Cheezburger
11
+ include Peanuts
12
+
13
+ attribute :weight, :float
14
+ attribute :price, :decimal
15
+
16
+ def initialize(weight = nil, price = nil)
17
+ @weight, @price = weight, price
18
+ end
19
+
20
+ def eql?(other)
21
+ other && weight == other.weight && price == other.price
22
+ end
23
+
24
+ alias == eql?
25
+ end
26
+
27
+ class Cat
28
+ include Peanuts
29
+
30
+ namespaces :lol => 'urn:x-lol', :kthnx => 'urn:x-lol:kthnx'
31
+
32
+ root 'kitteh', :xmlns => :lol
33
+
34
+ attribute :has_tail, :boolean, :xmlname => 'has-tail', :xmlns => :kthnx
35
+ attribute :ears, :integer
36
+
37
+ element :ration, [:string], :xmlname => :eats, :xmlns => :kthnx
38
+ element :name, :xmlns => 'urn:x-lol:kthnx'
39
+ elements :paws, :xmlname => :paw
40
+
41
+ element :friends, :xmlname => :pals do
42
+ elements :names, :xmlname => :pal
43
+ end
44
+
45
+ element :cheezburger, Cheezburger
46
+ element :moar_cheezburgers do
47
+ elements :cheezburger, Cheezburger
48
+ end
49
+ end
50
+
51
+ shared_examples_for 'my cat' do
52
+ it 'should be named Silly Tom' do
53
+ @cat.name.should eql 'Silly Tom'
54
+ end
55
+
56
+ it 'should eat tigers and lions' do
57
+ @cat.ration.should eql %w(tigers lions)
58
+ end
59
+
60
+ it 'should be a friend of Chrissy, Missy & Sissy' do
61
+ @cat.friends.names.should eql ['Chrissy', 'Missy', 'Sissy']
62
+ end
63
+
64
+ it 'should have 2 ears' do
65
+ @cat.ears.should eql 2
66
+ end
67
+
68
+ it 'should have a tail' do
69
+ @cat.has_tail.should be_true
70
+ end
71
+
72
+ it 'should have four paws' do
73
+ @cat.paws.should eql %w(one two three four)
74
+ end
75
+
76
+ it 'should has cheezburger' do
77
+ @cat.cheezburger.should be_kind_of Cheezburger
78
+ end
79
+
80
+ it 'should has 2 moar good cheezburgerz' do
81
+ @cat.moar_cheezburgers.cheezburger.should eql [
82
+ Cheezburger.new(685.940, BigDecimal('19')),
83
+ Cheezburger.new(9356.7, BigDecimal('7.40'))]
84
+ end
85
+ end
86
+
87
+ shared_examples_for 'my cheezburger' do
88
+ it 'should weigh 14.5547 pounds' do
89
+ @cheezburger.weight.should eql 14.5547
90
+ end
91
+
92
+ it 'should cost $2.05' do
93
+ @cheezburger.price.should eql BigDecimal('2.05')
94
+ end
95
+ end
96
+
97
+ shared_examples_for 'sample kitteh' do
98
+ before :all do
99
+ @xml_fragment = <<-EOS
100
+ <kitteh xmlns='urn:x-lol' xmlns:kthnx='urn:x-lol:kthnx' ears=' 2 ' kthnx:has-tail=' yes '>
101
+ <name xmlns='urn:x-lol:kthnx'>
102
+ Silly
103
+ Tom
104
+ </name>
105
+ <kthnx:eats>
106
+ tigers
107
+ lions
108
+ </kthnx:eats>
109
+ <pals>
110
+ <pal>Chrissy</pal>
111
+ <pal>Missy</pal>
112
+ <pal>Sissy</pal>
113
+ </pals>
114
+ <paw> one</paw>
115
+ <paw> two </paw>
116
+ <paw>three</paw>
117
+ <paw>four</paw>
118
+ <cheezburger price='2.05' weight='14.5547' />
119
+ <moar_cheezburgers>
120
+ <cheezburger price='19' weight='685.940' />
121
+ <cheezburger price='7.40' weight='9356.7' />
122
+ </moar_cheezburgers>
123
+ </kitteh>
124
+ EOS
125
+ @cat = Cat.restore_from(@xml_fragment)
126
+ @cheezburger = @cat.cheezburger
127
+ end
128
+
129
+ it_should_behave_like 'my cat', 'my cheezburger'
130
+ end
131
+
132
+ describe 'My cat' do
133
+ it_should_behave_like 'sample kitteh'
134
+ end
135
+
136
+ describe 'My cat saved and restored' do
137
+ it_should_behave_like 'sample kitteh'
138
+
139
+ before :all do
140
+ @cat = Cat.restore_from(@cat.save_to(:string))
141
+ end
142
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: omg-peanuts
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.0.1
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: 2009-09-24 00:00:00 -07:00
12
+ date: 2009-09-25 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -23,14 +23,14 @@ dependencies:
23
23
  version: 1.1.3
24
24
  version:
25
25
  - !ruby/object:Gem::Dependency
26
- name: thoughtbot-shoulda
26
+ name: rspec
27
27
  type: :development
28
28
  version_requirement:
29
29
  version_requirements: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: 2.0.6
33
+ version: 1.2.8
34
34
  version:
35
35
  description: Peanuts is an XML to Ruby and back again mapping library.
36
36
  email: tekmon@gmail.com
@@ -52,7 +52,6 @@ files:
52
52
  - lib/peanuts/converters.rb
53
53
  - lib/peanuts/mapper.rb
54
54
  - lib/peanuts/xml.rb
55
- - lib/peanuts/xml/reader.rb
56
55
  - lib/peanuts/xml/libxml.rb
57
56
  has_rdoc: true
58
57
  homepage: http://github.com/omg/peanuts
@@ -84,4 +83,4 @@ signing_key:
84
83
  specification_version: 2
85
84
  summary: Making XML <-> Ruby binding easy
86
85
  test_files:
87
- - test/parsing_test.rb
86
+ - spec/cat_spec.rb
@@ -1,78 +0,0 @@
1
- require 'enumerator'
2
-
3
- class Hash
4
- def from_namespace(ns)
5
- rx = /^#{Regexp.quote(ns.to_s)}_(.*)$/
6
- inject({}) do |a, p|
7
- a[$1.to_sym] = p[1] if p[0].to_s =~ rx
8
- a
9
- end
10
- end
11
-
12
- def from_namespace!(ns)
13
- h = from_namespace(ns)
14
- h.each_key {|k| delete(:"#{ns}_#{k}") }
15
- h
16
- end
17
- end
18
-
19
- module Peanuts
20
- module XML
21
- autoload :LibXML, 'peanuts/xml/libxml'
22
-
23
- def self.default
24
- @@default ||= LibXML
25
- end
26
-
27
- def self.schema(schema_type, source, source_type = :string)
28
- default.schema(schema_type, source, source_type)
29
- end
30
-
31
- def self.method_missing(method, *args, &block)
32
- case method.to_s
33
- when /^(.*)_schema_from_(.*)$/
34
- XML.schema($1.to_sym, args.first, $2.to_sym)
35
- else
36
- super
37
- end
38
- end
39
-
40
- class Reader
41
- include Enumerable
42
-
43
- def self.new(*args, &block)
44
- cls = self == Reader ? XML.default::Reader : self
45
- obj = cls.allocate
46
- obj.send(:initialize, *args, &block)
47
- obj
48
- end
49
-
50
- def self.method_missing(method, *args, &block)
51
- case method.to_s
52
- when /^from_(.*)$/
53
- new(args.first, $1.to_sym, &block)
54
- else
55
- super
56
- end
57
- end
58
- end
59
-
60
- class Writer
61
- def self.new(*args, &block)
62
- cls = self == Writer ? XML.default::Writer : self
63
- obj = cls.allocate
64
- obj.send(:initialize, *args, &block)
65
- obj
66
- end
67
-
68
- def self.method_missing(method, *args, &block)
69
- case method.to_s
70
- when /^from_(.*)$/
71
- new(args.first, $1.to_sym, &block)
72
- else
73
- super
74
- end
75
- end
76
- end
77
- end
78
- end
@@ -1,115 +0,0 @@
1
- #$:.unshift File.join(File.dirname(__FILE__),'..','lib')
2
-
3
- require 'bigdecimal'
4
- require 'test/unit'
5
- require 'rubygems'
6
- require 'shoulda'
7
- require 'lib/peanuts'
8
-
9
-
10
- class Cheezburger
11
- include Peanuts
12
-
13
- attribute :weight, :float
14
- attribute :price, :decimal
15
- end
16
-
17
- class Cat
18
- include Peanuts
19
-
20
- namespaces :lol => 'urn:x-lol', :kthnx => 'urn:x-lol:kthnx'
21
-
22
- root 'kitteh', :xmlns => :lol
23
-
24
- attribute :has_tail, :boolean, :xmlname => 'has-tail', :xmlns => :kthnx
25
- attribute :ears, :integer
26
-
27
- element :ration, [:string], :xmlname => :eats, :xmlns => :kthnx
28
- element :name, :string, :xmlns => 'urn:x-lol:kthnx'
29
- elements :paws, :string, :xmlname => :paw
30
-
31
- element :friends, :xmlname => :pals do
32
- elements :names, :string, :xmlname => :pal
33
- end
34
-
35
- element :cheezburger, Cheezburger
36
- element :moar_cheezburgers do
37
- elements :cheezburger, Cheezburger
38
- end
39
- end
40
-
41
- class ParsingTest < Test::Unit::TestCase
42
- def setup
43
- @xml_fragment = <<-EOS
44
- <kitteh xmlns='urn:x-lol' xmlns:kthnx='urn:x-lol:kthnx' ears=' 2 ' kthnx:has-tail=' yes '>
45
- <name xmlns='urn:x-lol:kthnx'>
46
- Silly
47
- Tom
48
- </name>
49
- <kthnx:eats>
50
- tigers
51
- lions
52
- </kthnx:eats>
53
- <pals>
54
- <pal>Chrissy</pal>
55
- <pal>Missy</pal>
56
- <pal>Sissy</pal>
57
- </pals>
58
- <paw> one</paw>
59
- <paw> two </paw>
60
- <paw>three</paw>
61
- <paw>four</paw>
62
- <cheezburger price='2.05' weight='14.5547' />
63
- <moar_cheezburgers>
64
- <cheezburger price='19' weight='685.940' />
65
- <cheezburger price='7.40' weight='9356.7' />
66
- </moar_cheezburgers>
67
- </kitteh>
68
- EOS
69
- @cat = Cat.restore_from(@xml_fragment)
70
- end
71
-
72
- context "A cat" do
73
- should 'be named Silly Tom' do
74
- assert_equal 'Silly Tom', @cat.name
75
- end
76
-
77
- should 'eat tigers and lions' do
78
- assert_equal %w(tigers lions), @cat.ration
79
- end
80
-
81
- should 'be a friend of Chrissy, Missy & Sissy' do
82
- assert_equal ['Chrissy', 'Missy', 'Sissy'], @cat.friends.names
83
- end
84
-
85
- should 'have 2 ears' do
86
- assert_equal 2, @cat.ears
87
- end
88
-
89
- should 'have a tail' do
90
- assert @cat.has_tail
91
- end
92
-
93
- should 'have four paws' do
94
- assert_equal %w(one two three four), @cat.paws
95
- end
96
-
97
- should 'has cheezburger' do
98
- assert_kind_of Cheezburger, @cat.cheezburger
99
- end
100
- end
101
-
102
- context 'A cheezburger' do
103
- setup do
104
- @burger = @cat.cheezburger
105
- end
106
-
107
- should 'weigh 14.5547 pounds' do
108
- assert_equal 14.5547, @burger.weight
109
- end
110
-
111
- should 'cost $2.05' do
112
- assert_equal BigDecimal('2.05'), @burger.price
113
- end
114
- end
115
- end