omg-peanuts 1.0 → 2.0.0

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