omg-peanuts 2.0.0 → 2.0.1

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.
@@ -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