omg-peanuts 1.0 → 2.0.0

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,4 +1,6 @@
1
1
  === Introduction
2
+ *This is currently undergoing some rewrite. 2.x branch will introduce minor api incompatibilities.*
3
+
2
4
  Peanuts is an library that allows for bidirectional mapping between Ruby objects and XML.
3
5
 
4
6
  Released under the MIT license.
@@ -9,6 +11,10 @@ Released under the MIT license.
9
11
  - Pluggable backends to work with different XML APIs (REXML implemented so far).
10
12
 
11
13
  === Installation
14
+ Stable branch 1.x available from Gemcutter
15
+ gem install peanuts --source http://gemcutter.org
16
+
17
+ Development branch 2.x available from Github. buggy/broken/missing docs
12
18
  gem install omg-peanuts --source http://gems.github.com
13
19
 
14
20
  === Usage
@@ -96,9 +102,4 @@ Please report via Github issue tracking.
96
102
  * http://github.com/omg/statelogic -- A simple state machine for ActiveRecord
97
103
 
98
104
 
99
-
100
- Free hint: If you liek mudkipz^W^Wfeel like generous today you can tip me at http://tipjoy.com/u/pisuka
101
-
102
-
103
105
  Copyright (c) 2009 Igor Gunko, released under the MIT license
104
-
@@ -0,0 +1,110 @@
1
+ require 'enumerator'
2
+
3
+ module Peanuts
4
+ class Mapper
5
+ include Enumerable
6
+
7
+ attr_reader :root, :namespaces, :nscontext, :container
8
+ attr_accessor :schema
9
+
10
+ def initialize
11
+ @mappings, @footprints = [], {}
12
+ @namespaces = Hash.new do |h, k|
13
+ nscontext && nscontext[k] || raise(IndexError)
14
+ end
15
+ end
16
+
17
+ def root=(root)
18
+ raise 'root already defined' if @root
19
+ raise 'root in nested scopes not supported' if nested?
20
+ @root = root
21
+ end
22
+
23
+ def set_context(container, nscontext)
24
+ @container, @nscontext = container, nscontext
25
+ end
26
+
27
+ def nested?
28
+ !!container
29
+ end
30
+
31
+ def each(&block)
32
+ @mappings.each(&block)
33
+ end
34
+
35
+ def <<(mapping)
36
+ fp = MappingFootprint.new(mapping)
37
+ raise "mapping already defined for #{fp}" if @footprints.include?(fp)
38
+ @mappings << (@footprints[fp] = mapping)
39
+ end
40
+
41
+ def parse(nut, reader)
42
+ rdfp = ReaderFootprint.new(reader)
43
+ reader.each do
44
+ m = @footprints[rdfp]
45
+ m.from_xml(nut, reader) if m
46
+ end
47
+ nut
48
+ end
49
+
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
59
+ end
60
+
61
+ def clear(nut)
62
+ @mappings.each {|m| m.clear(nut) }
63
+ end
64
+
65
+ private
66
+ def _save(nut, writer)
67
+ @mappings.each {|m| m.to_xml(nut, writer) }
68
+ end
69
+
70
+ class Footprint
71
+ def ==(other)
72
+ self.equal?(other) || other && node_type == other.node_type && name == other.name && ns == other.ns
73
+ end
74
+
75
+ alias eql? ==
76
+
77
+ def hash
78
+ node_type.hash ^ name.hash ^ ns.hash
79
+ end
80
+
81
+ def to_s
82
+ "#{node_type}(#{name}, #{ns})"
83
+ end
84
+ end
85
+
86
+ class MappingFootprint < Footprint
87
+ extend Forwardable
88
+
89
+ def initialize(mapping)
90
+ @mapping = mapping
91
+ end
92
+
93
+ def_delegator :@mapping, :node_type
94
+ def_delegator :@mapping, :xmlname, :name
95
+ def_delegator :@mapping, :xmlns, :ns
96
+ end
97
+
98
+ class ReaderFootprint < Footprint
99
+ extend Forwardable
100
+
101
+ def initialize(reader)
102
+ @reader = reader
103
+ end
104
+
105
+ def_delegator :@reader, :node_type
106
+ def_delegator :@reader, :local_name, :name
107
+ def_delegator :@reader, :namespace_uri, :ns
108
+ end
109
+ end
110
+ end
@@ -1,26 +1,36 @@
1
- require 'enumerator'
2
- require 'peanuts/backend'
1
+ require 'forwardable'
3
2
  require 'peanuts/converters'
4
3
 
5
4
  module Peanuts
6
- module Mappings
7
- class Mapping
8
- attr_reader :xmlname, :xmlns, :options
5
+ class Mapping
6
+ attr_reader :xmlname, :xmlns, :prefix, :options
7
+
8
+ def initialize(xmlname, options)
9
+ @xmlname, @xmlns, @prefix, @options = xmlname.to_s, options.delete(:xmlns), options.delete(:prefix), options
10
+ end
11
+
12
+ def node_type
13
+ self.class.node_type
14
+ end
9
15
 
10
- def initialize(xmlname, options)
11
- @xmlname, @xmlns, @options = xmlname.to_s, options.delete(:xmlns), options
16
+ class << self
17
+ def node_type(node_type = nil)
18
+ @node_type = node_type if node_type
19
+ @node_type
12
20
  end
13
21
  end
22
+ end
14
23
 
24
+ module Mappings
15
25
  class Root < Mapping
16
- def initialize(xmlname, options = {})
17
- super
26
+ node_type :element
27
+
28
+ def to_xml(writer, &block)
29
+ writer.write(node_type, xmlname, xmlns, prefix, &block)
18
30
  end
19
31
  end
20
32
 
21
33
  class MemberMapping < Mapping
22
- include XmlBackend
23
-
24
34
  attr_reader :name, :type, :converter
25
35
 
26
36
  def initialize(name, type, options)
@@ -43,7 +53,11 @@ module Peanuts
43
53
  end
44
54
 
45
55
  def from_xml(nut, node)
46
- set(nut, getxml(node))
56
+ set(nut, getxml2(node, get(nut)))
57
+ end
58
+
59
+ def clear(nut)
60
+ set(nut, nil)
47
61
  end
48
62
 
49
63
  private
@@ -63,54 +77,60 @@ module Peanuts
63
77
  @converter ? @converter.from_xml(text) : text
64
78
  end
65
79
 
66
- def each_element(node, &block)
67
- node && backend.each_element(node, xmlname, xmlns, &block)
68
- nil
69
- end
70
-
71
- def add_element(node, value = nil)
72
- backend.add_element(node, xmlname, xmlns, value)
80
+ def getxml2(node, acc)
81
+ getxml(node)
73
82
  end
74
83
 
75
- def value(node)
76
- backend.value(node)
84
+ def parse(events)
85
+ type.send(:_restore, events)
77
86
  end
78
87
 
79
- def parse(node)
80
- type.parse_node(type.new, node)
81
- end
82
-
83
- def build(node, nut, dest_node)
84
- nut && type.build_node(nut, dest_node)
88
+ def build(nut, writer)
89
+ type.send(:_save, nut, writer)
85
90
  end
86
91
  end
87
92
 
88
93
  class ElementValue < MemberMapping
94
+ node_type :element
95
+
89
96
  private
90
97
  def getxml(node)
91
- each_element(node) {|e| return froxml(value(e)) }
98
+ froxml(node.value)
92
99
  end
93
100
 
94
- def setxml(node, value)
95
- add_element(node, toxml(value))
101
+ def setxml(writer, value)
102
+ writer.write(node_type, xmlname, xmlns, prefix) do |w|
103
+ w.value = toxml(value)
104
+ end
96
105
  end
97
106
  end
98
107
 
99
108
  class Element < MemberMapping
109
+ node_type :element
110
+
100
111
  private
101
112
  def getxml(node)
102
- each_element(node) {|e| return parse(e) }
113
+ parse(node)
103
114
  end
104
115
 
105
- def setxml(node, value)
106
- build(node, value, add_element(node))
116
+ def setxml(writer, value)
117
+ writer.write(node_type, xmlname, xmlns, prefix) do |w|
118
+ build(value, w)
119
+ end
107
120
  end
108
121
  end
109
122
 
110
123
  class Attribute < MemberMapping
124
+ node_type :attribute
125
+
126
+ def initialize(name, type, options)
127
+ super
128
+ raise ArgumentError, 'a namespaced attribute must have namespace prefix' if xmlns && !prefix
129
+ end
130
+
111
131
  private
112
132
  def getxml(node)
113
- froxml(backend.attribute(node, xmlname, xmlns))
133
+ froxml(node.value)
114
134
  end
115
135
 
116
136
  def setxml(node, value)
@@ -119,31 +139,24 @@ module Peanuts
119
139
  end
120
140
 
121
141
  class ElementValues < MemberMapping
122
- private
123
- def each_value(node)
124
- each_element(node) {|x| yield froxml(value(x)) }
125
- end
142
+ node_type :element
126
143
 
127
- def getxml(node)
128
- enum_for(:each_value, node).to_a
144
+ private
145
+ def getxml2(node, acc)
146
+ (acc || []) << froxml(node.value)
129
147
  end
130
148
 
131
149
  def setxml(node, values)
132
- unless node
133
- raise 'fuck'
134
- end
135
150
  values.each {|v| add_element(node, toxml(v)) } if values
136
151
  end
137
152
  end
138
153
 
139
154
  class Elements < MemberMapping
140
- private
141
- def each_object(node)
142
- each_element(node) {|e| yield parse(e) }
143
- end
155
+ node_type :element
144
156
 
145
- def getxml(node)
146
- enum_for(:each_object, node)
157
+ private
158
+ def getxml2(node, acc)
159
+ (acc || []) << parse(node)
147
160
  end
148
161
 
149
162
  def setxml(node, elements)
data/lib/peanuts/nuts.rb CHANGED
@@ -1,4 +1,6 @@
1
1
  require 'peanuts/mappings'
2
+ require 'peanuts/mapper'
3
+ require 'peanuts/xml/reader'
2
4
 
3
5
  module Peanuts #:nodoc:
4
6
  # See also +ClassMethods+
@@ -8,9 +10,19 @@ module Peanuts #:nodoc:
8
10
 
9
11
  # See also +PeaNuts+.
10
12
  module ClassMethods
11
- include XmlBackend
12
13
  include Mappings
13
14
 
15
+ def self.extended(other)
16
+ other.instance_eval do
17
+ @mappings = Mapper.new
18
+ end
19
+ end
20
+
21
+ # mappings -> Mapper
22
+ #
23
+ # Returns all mappings defined on a class.
24
+ attr_reader :mappings
25
+
14
26
  # namespaces(hash) -> Hash
15
27
  # namespaces -> Hash
16
28
  #
@@ -20,13 +32,12 @@ module Peanuts #:nodoc:
20
32
  #
21
33
  # === Example:
22
34
  # class Cat
23
- # include PeaNuts
35
+ # include Peanuts
24
36
  # namespaces :lol => 'urn:lol', ...
25
37
  # ...
26
38
  # end
27
39
  def namespaces(mappings = nil)
28
- @namespaces ||= {}
29
- mappings ? @namespaces.update(mappings) : @namespaces
40
+ mappings ? @mappings.namespaces.update(mappings) : @mappings.namespaces
30
41
  end
31
42
 
32
43
  # root(xmlname[, :xmlns => ...]) -> Mappings::Root
@@ -47,8 +58,8 @@ module Peanuts #:nodoc:
47
58
  # ...
48
59
  # end
49
60
  def root(xmlname = nil, options = {})
50
- @root = Root.new(xmlname, prepare_options(options)) if xmlname
51
- @root ||= Root.new('root')
61
+ @mappings.root = Root.new(xmlname, prepare_options(options)) if xmlname
62
+ @mappings.root
52
63
  end
53
64
 
54
65
  # element(name, [type[, options]]) -> Mappings::Element or Mappings::ElementValue
@@ -71,9 +82,10 @@ module Peanuts #:nodoc:
71
82
  # ...
72
83
  # end
73
84
  def element(name, type = :string, options = {}, &block)
74
- type, options = prepare_args(type, options, &block)
75
- define_accessor name
76
- (mappings << (type.is_a?(Class) ? Element : ElementValue).new(name, type, prepare_options(options))).last
85
+ prepare_args(type, options, block) do |type, options|
86
+ define_accessor name
87
+ (@mappings << (type.is_a?(Class) ? Element : ElementValue).new(name, type, options)).last
88
+ end
77
89
  end
78
90
 
79
91
  # elements(name, [type[, options]]) -> Mappings::Element or Mappings::ElementValue
@@ -96,9 +108,10 @@ module Peanuts #:nodoc:
96
108
  # ...
97
109
  # end
98
110
  def elements(name, type = :string, options = {}, &block)
99
- type, options = prepare_args(type, options, &block)
100
- define_accessor name
101
- (mappings << (type.is_a?(Class) ? Elements : ElementValues).new(name, type, prepare_options(options))).last
111
+ prepare_args(type, options, block) do |type, options|
112
+ define_accessor name
113
+ (@mappings << (type.is_a?(Class) ? Elements : ElementValues).new(name, type, options)).last
114
+ end
102
115
  end
103
116
 
104
117
  # attribute(name, [type[, options]]) -> Mappings::Attribute or Mappings::AttributeValue
@@ -120,18 +133,27 @@ module Peanuts #:nodoc:
120
133
  # end
121
134
  def attribute(name, type = :string, options = {})
122
135
  define_accessor name
123
- mappings << Attribute.new(name, type, prepare_options(options))
136
+ @mappings << Attribute.new(name, type, prepare_options({:xmlns => nil}.merge(options)))
124
137
  end
125
138
 
126
- # mappings -> Array
127
- #
128
- # Returns all mappings defined on a class.
129
- def mappings
130
- @mappings ||= []
139
+ def schema(schema = nil)
140
+ @mappings.schema = schema if schema
141
+ @mappings.schema
142
+ end
143
+
144
+ def restore(reader)
145
+ e = reader.find_element
146
+ e && _restore(e)
131
147
  end
132
148
 
133
- def parse(source, options = {})
134
- backend.parse(source, options) {|node| parse_node(new, node) }
149
+ def restore_from(source_or_type = nil, source = nil, options = {})
150
+ _source_or_dest(source_or_type, source) do |source_type, source|
151
+ restore(XML::Reader.new(source, source_type, options))
152
+ end
153
+ end
154
+
155
+ def save(nut, writer)
156
+ _save(nut, writer)
135
157
  end
136
158
 
137
159
  def build(nut, result = :string, options = {})
@@ -147,28 +169,48 @@ module Peanuts #:nodoc:
147
169
  node
148
170
  end
149
171
 
150
- def parse_node(nut, node) #:nodoc:
151
- callem(:from_xml, nut, node)
152
- nut
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
153
179
  end
154
180
 
155
181
  private
156
- def prepare_args(type, options, &block)
157
- if block_given?
158
- options = type if type.is_a?(Hash)
182
+ def _restore(events)
183
+ nut = new
184
+ @mappings.parse(nut, events)
185
+ nut
186
+ end
187
+
188
+ def _save(nut, events)
189
+ @mappings.build(nut, events)
190
+ end
191
+
192
+ def prepare_args(type, options, blk)
193
+ if blk
194
+ options = prepare_options(type) if type.is_a?(Hash)
159
195
  type = Class.new
160
- type.class_eval do
161
- include Peanuts
162
- class_eval(&block)
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
163
202
  end
203
+ else
204
+ options = prepare_options(options)
205
+ yield type, options
164
206
  end
165
- return type, prepare_options(options)
166
207
  end
167
208
 
168
209
  def prepare_options(options)
169
- ns = options[:xmlns]
210
+ ns = options.fetch(:xmlns) {|k| options[k] = root && root.xmlns || @mappings.container && @mappings.container.xmlns }
170
211
  if ns.is_a?(Symbol)
171
212
  raise ArgumentError, "undefined prefix: #{ns}" unless options[:xmlns] = namespaces[ns]
213
+ options[:prefix] = ns
172
214
  end
173
215
  options
174
216
  end
@@ -179,16 +221,16 @@ module Peanuts #:nodoc:
179
221
  end
180
222
  attr_accessor name
181
223
  end
182
-
183
- def callem(method, *args)
184
- mappings.each {|m| m.send(method, *args) }
185
- end
186
224
  end
187
225
 
188
226
  def parse(source, options = {})
189
227
  backend.parse(source, options) {|node| parse_node(node) }
190
228
  end
191
229
 
230
+ def save(writer)
231
+ self.class.save(self, writer)
232
+ end
233
+
192
234
  # build([options]) -> root element or string
193
235
  # build([options]) -> root element or string
194
236
  # build(destination[, options]) -> destination
@@ -213,7 +255,9 @@ module Peanuts #:nodoc:
213
255
  # doc = REXML::Document.new
214
256
  # cat.build(doc)
215
257
  # puts doc.to_s
216
- def build(result = :string, options = {})
217
- self.class.build(self, result, options)
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))
261
+ end
218
262
  end
219
263
  end
@@ -0,0 +1,231 @@
1
+ require 'libxml'
2
+ require 'forwardable'
3
+ require 'peanuts/xml/reader'
4
+
5
+ module Peanuts
6
+ module XML
7
+ module LibXML
8
+ class Writer < Peanuts::XML::Writer
9
+ def initialize(dest, dest_type, options = {})
10
+ @dest_type = dest_type
11
+ @dest = case dest_type
12
+ when :string
13
+ dest || ''
14
+ when :io
15
+ dest
16
+ when :document
17
+ dest || LibXML::XML::Document.new
18
+ else
19
+ raise ArgumentError, "unrecognized destination type #{dest_type.inspect}"
20
+ end
21
+ end
22
+
23
+ def result
24
+ @dest
25
+ end
26
+
27
+ attr_writer :value
28
+
29
+ def clear
30
+ @node_type = @local_name = @namespace_uri = @prefix = @value = nil
31
+ end
32
+
33
+ def write(node_type, local_name = nil, namespace_uri = nil, prefix = nil)
34
+ if @node_type
35
+ mknode
36
+ else
37
+ @node = nil
38
+ end
39
+
40
+ @node_type = node_type
41
+ @local_name, @namespace_uri, @prefix = local_name, namespace_uri, prefix && prefix.to_s
42
+
43
+ exparent, @parent = @parent, @node
44
+
45
+ yield self
46
+
47
+ if @node_type
48
+ mknode
49
+ end
50
+ @node = nil
51
+
52
+ if exparent.nil?
53
+ case @dest_type
54
+ when :string, :io
55
+ @dest << @parent.to_s
56
+ when :document
57
+ @dest.root = @parent
58
+ end
59
+ end
60
+
61
+ @parent = exparent
62
+ end
63
+
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
81
+ end
82
+ end
83
+
84
+ class Reader < Peanuts::XML::Reader
85
+ extend Forwardable
86
+
87
+ SCHEMAS = {:xml_schema => :schema, :relax_ng => :relax_ng}
88
+
89
+ RD = ::LibXML::XML::Reader
90
+
91
+ NODE_TYPES = [
92
+ nil,
93
+ :element,
94
+ :attribute,
95
+ :text,
96
+ :cdata,
97
+ :entity_reference,
98
+ :entity,
99
+ :processing_instruction,
100
+ :comment,
101
+ :document,
102
+ :document_type,
103
+ :document_fragment,
104
+ :notation,
105
+ :whitespace,
106
+ :significant_whitespace,
107
+ :end_element,
108
+ :end_entity,
109
+ :xml_declaration
110
+ ].freeze
111
+
112
+ DEFAULT_PARSER_OPTIONS = {
113
+ :libxml_encoding => ::LibXML::XML::Encoding::UTF_8,
114
+ :libxml_options => ::LibXML::XML::Parser::Options::NOENT
115
+ }
116
+
117
+ def initialize(source, source_type, options = {})
118
+ super()
119
+ options = options.dup
120
+ @schema = options.delete(:schema)
121
+ @reader = case source_type
122
+ when :string
123
+ RD.string(source, parser_opt(options))
124
+ when :io
125
+ RD.io(source, parser_opt(options))
126
+ when :uri
127
+ RD.file(source, parser_opt(options))
128
+ when :document
129
+ RD.document(source)
130
+ else
131
+ raise ArgumentError, "unrecognized source type #{source_type}"
132
+ end
133
+ @reader.send("#{SCHEMAS[schema.type]}_validate", schema.schema) if @schema
134
+ end
135
+
136
+ def close
137
+ @reader.close
138
+ end
139
+
140
+ def_delegators :@reader, :name, :local_name, :namespace_uri, :depth
141
+
142
+ def node_type
143
+ NODE_TYPES[@reader.node_type]
144
+ end
145
+
146
+ def value
147
+ case @reader.node_type
148
+ when RD::TYPE_ELEMENT
149
+ @reader.read_string
150
+ else
151
+ @reader.has_value? ? @reader.value : nil
152
+ end
153
+ end
154
+
155
+ def each
156
+ depth = self.depth
157
+ if read
158
+ while self.depth > depth
159
+ yield self
160
+ break unless next_sibling
161
+ end
162
+ end
163
+ end
164
+
165
+ def find_element
166
+ until @reader.node_type == RD::TYPE_ELEMENT
167
+ return nil unless read
168
+ end
169
+ self
170
+ end
171
+
172
+ private
173
+ def parser_opt(options)
174
+ h = DEFAULT_PARSER_OPTIONS.merge(options)
175
+ h.merge(h.from_namespace!(:libxml))
176
+ end
177
+
178
+ def read
179
+ case @reader.node_type
180
+ when RD::TYPE_ATTRIBUTE
181
+ @reader.move_to_next_attribute > 0 || @reader.read
182
+ else
183
+ @reader.move_to_first_attribute > 0 || @reader.read
184
+ end
185
+ end
186
+
187
+ def next_sibling
188
+ case @reader.node_type
189
+ when RD::TYPE_ATTRIBUTE
190
+ @reader.move_to_next_attribute > 0 || @reader.read
191
+ else
192
+ @reader.next > 0
193
+ end
194
+ end
195
+ end
196
+
197
+ def self.schema(schema_type, source, source_type = :string)
198
+ schema_class = case schema_type
199
+ when :xml_schema
200
+ ::LibXML::XML::Schema
201
+ when :relax_ng
202
+ ::LibXML::XML::RelaxNG
203
+ else
204
+ raise ArgumentError, "unrecognized schema type #{schema_type}"
205
+ end
206
+ schema = case source_type
207
+ when :string
208
+ schema_class.string(source)
209
+ when :io
210
+ schema_class.string(source.read)
211
+ when :uri
212
+ schema_class.new(source)
213
+ when :document
214
+ schema_class.document(source)
215
+ else
216
+ raise ArgumentError, "unrecognized source type #{source_type}"
217
+ end
218
+
219
+ Schema.new(schema_type, schema)
220
+ end
221
+
222
+ class Schema
223
+ attr_reader :type, :handle
224
+
225
+ def initialize(type, handle)
226
+ @type, @handle = type, handle
227
+ end
228
+ end
229
+ end
230
+ end
231
+ end
@@ -0,0 +1,78 @@
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
@@ -0,0 +1 @@
1
+ require 'peanuts/xml/reader'
data/lib/peanuts.rb CHANGED
@@ -1 +1,2 @@
1
1
  require 'peanuts/nuts'
2
+ require 'peanuts/xml'
data/test/parsing_test.rb CHANGED
@@ -21,7 +21,7 @@ class Cat
21
21
 
22
22
  root 'kitteh', :xmlns => :lol
23
23
 
24
- attribute :has_tail, :boolean, :xmlname => 'has-tail', :xmlns => 'urn:x-lol:kthnx'
24
+ attribute :has_tail, :boolean, :xmlname => 'has-tail', :xmlns => :kthnx
25
25
  attribute :ears, :integer
26
26
 
27
27
  element :ration, [:string], :xmlname => :eats, :xmlns => :kthnx
@@ -66,7 +66,7 @@ class ParsingTest < Test::Unit::TestCase
66
66
  </moar_cheezburgers>
67
67
  </kitteh>
68
68
  EOS
69
- @cat = Cat.parse(@xml_fragment)
69
+ @cat = Cat.restore_from(@xml_fragment)
70
70
  end
71
71
 
72
72
  context "A cat" do
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: "1.0"
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Igor Gunko
@@ -9,9 +9,19 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-06-21 00:00:00 -07:00
12
+ date: 2009-09-24 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: libxml-ruby
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.1.3
24
+ version:
15
25
  - !ruby/object:Gem::Dependency
16
26
  name: thoughtbot-shoulda
17
27
  type: :development
@@ -40,10 +50,13 @@ files:
40
50
  - lib/peanuts/nuts.rb
41
51
  - lib/peanuts/mappings.rb
42
52
  - lib/peanuts/converters.rb
43
- - lib/peanuts/backend.rb
44
- - lib/peanuts/rexml.rb
53
+ - lib/peanuts/mapper.rb
54
+ - lib/peanuts/xml.rb
55
+ - lib/peanuts/xml/reader.rb
56
+ - lib/peanuts/xml/libxml.rb
45
57
  has_rdoc: true
46
58
  homepage: http://github.com/omg/peanuts
59
+ licenses:
47
60
  post_install_message:
48
61
  rdoc_options:
49
62
  - --line-numbers
@@ -66,7 +79,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
66
79
  requirements: []
67
80
 
68
81
  rubyforge_project:
69
- rubygems_version: 1.2.0
82
+ rubygems_version: 1.3.5
70
83
  signing_key:
71
84
  specification_version: 2
72
85
  summary: Making XML <-> Ruby binding easy
@@ -1,38 +0,0 @@
1
- require 'monitor'
2
-
3
- module Peanuts
4
- module XmlBackend
5
- extend MonitorMixin
6
-
7
- autoload :REXMLBackend, 'peanuts/rexml'
8
-
9
- def self.default
10
- synchronize do
11
- unless defined? @@default
12
- @@default = REXMLBackend.new
13
- def self.default #:nodoc:
14
- @@default
15
- end
16
- @@default
17
- end
18
- end
19
- end
20
-
21
- def self.default=(backend)
22
- @@default = backend
23
- end
24
-
25
- def self.current
26
- Thread.current[XmlBackend.name] || default
27
- end
28
-
29
- def self.current=(backend)
30
- Thread.current[XmlBackend.name] = backend
31
- end
32
-
33
- private
34
- def backend #:doc:
35
- XmlBackend.current
36
- end
37
- end
38
- end
data/lib/peanuts/rexml.rb DELETED
@@ -1,98 +0,0 @@
1
- require 'rexml/document'
2
- require 'peanuts/backend'
3
-
4
- class Peanuts::XmlBackend::REXMLBackend #:nodoc:
5
- def parse(source, options)
6
- case source
7
- when nil
8
- return nil
9
- when REXML::Document
10
- node = source.root
11
- when REXML::Node
12
- node = source
13
- when String, IO
14
- node = REXML::Document.new(source).root
15
- else
16
- raise ArgumentError, 'invalid source'
17
- end
18
- node && yield(node)
19
- end
20
-
21
- def build(result, options)
22
- case result
23
- when :string, :document, :object, String, IO
24
- doc = REXML::Document.new
25
- when REXML::Document
26
- doc = result
27
- when REXML::Node
28
- node, doc = result, result.document
29
- else
30
- raise ArgumentError, 'invalid destination'
31
- end
32
- node ||= doc.root
33
- unless node
34
- name, ns, prefix = options[:xmlname], options[:xmlns], options[:xmlns_prefix]
35
- name, ns = "#{prefix}:#{name}", nil if prefix
36
- node = add_element(doc, name, ns, nil)
37
- end
38
-
39
- yield node
40
-
41
- case result
42
- when :string
43
- doc.to_s
44
- when String
45
- result.replace(doc.to_s)
46
- when IO
47
- doc.write(result)
48
- result
49
- when REXML::Document, :document
50
- doc
51
- when REXML::Node, :object
52
- node
53
- end
54
- end
55
-
56
- def add_namespaces(context, namespaces)
57
- namespaces.each {|prefix, uri| context.add_namespace(prefix.to_s, uri) }
58
- end
59
-
60
- def each_element(context, name, ns, &block)
61
- ns = context.namespace unless ns
62
- REXML::XPath.each(context, "ns:#{name}", 'ns' => ns, &block)
63
- end
64
-
65
- def value(node)
66
- node.text
67
- end
68
-
69
- def attribute(context, name, ns)
70
- name, ns = to_prefixed_name(context, name, ns, true)
71
- context.attributes[name]
72
- end
73
-
74
- def set_attribute(context, name, ns, text)
75
- name, ns = to_prefixed_name(context, name, ns, true)
76
- context.add_attribute(name, text)
77
- end
78
-
79
- def add_element(context, name, ns, text)
80
- name, ns = to_prefixed_name(context, name, ns, false)
81
- elem = context.add_element(name)
82
- elem.add_namespace(ns) if ns
83
- elem.text = text if text
84
- elem
85
- end
86
-
87
- private
88
- def to_prefixed_name(context, name, ns, prefix_required)
89
- if ns
90
- if prefix = context.namespaces.invert[ns]
91
- name, ns = "#{prefix}:#{name}", nil
92
- else
93
- raise ArgumentError, "no prefix defined for #{ns}" if prefix_required
94
- end
95
- end
96
- return name, ns
97
- end
98
- end