peanuts 1.0 → 2.0.7

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.
@@ -1,49 +1,86 @@
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 :local_name, :namespace_uri, :prefix, :options
9
7
 
10
- def initialize(xmlname, options)
11
- @xmlname, @xmlns, @options = xmlname.to_s, options.delete(:xmlns), options
12
- end
8
+ def initialize(local_name, options)
9
+ @local_name, @namespace_uri, @prefix, @options = local_name.to_s, options.delete(:ns), options.delete(:prefix), options
10
+ end
11
+
12
+ def matches?(reader)
13
+ node_type == reader.node_type &&
14
+ local_name == reader.local_name &&
15
+ namespace_uri == reader.namespace_uri
13
16
  end
14
17
 
18
+ def self.node_type(node_type)
19
+ define_method(:node_type) { node_type }
20
+ end
21
+ end
22
+
23
+ module Mappings # :nodoc:
15
24
  class Root < Mapping
16
- def initialize(xmlname, options = {})
17
- super
25
+ node_type :element
26
+
27
+ def write(writer, &block)
28
+ writer.write(node_type, local_name, namespace_uri, prefix, &block)
18
29
  end
19
30
  end
20
31
 
21
32
  class MemberMapping < Mapping
22
- include XmlBackend
23
-
24
33
  attr_reader :name, :type, :converter
25
34
 
26
35
  def initialize(name, type, options)
27
- super(options.delete(:xmlname) || name, options)
28
- case type
36
+ @bare_name = name.to_s.sub(/\?\z/, '')
37
+
38
+ super(options.delete(:name) || @bare_name, options)
39
+
40
+ @type = type
41
+ @converter = case type
42
+ when Symbol
43
+ Converter.create!(type, options)
44
+ when Class
45
+ if type < Converter
46
+ @type = nil
47
+ Converter.create!(type, options)
48
+ end
29
49
  when Array
30
- raise ArgumentError, "invalid value for type: #{type}" if type.length != 1
50
+ raise ArgumentError, "invalid value for type: #{type.inspect}" if type.length != 1
31
51
  options[:item_type] = type.first
32
- @converter = Converter.create!(:list, options)
33
- when Class
34
- options[:object_type] = type
52
+ Converter.create!(:list, options)
35
53
  else
36
- @converter = Converter.create!(type, options)
54
+ raise ArgumentError, "invalid value for type: #{type.inspect}"
37
55
  end
38
- @name, @setter, @type = name.to_sym, :"#{name}=", type
56
+ @name, @setter = name.to_sym, :"#{@bare_name}="
39
57
  end
40
58
 
41
- def to_xml(nut, node)
42
- setxml(node, get(nut))
59
+ def define_accessors(type)
60
+ raise ArgumentError, "#{name}: method already defined or reserved" if type.method_defined?(name)
61
+ raise ArgumentError, "#{@setter}: method already defined or reserved" if type.method_defined?(@setter)
62
+
63
+ ivar = :"@#{@bare_name}"
64
+ raise ArgumentError, "#{ivar}: instance variable already defined" if type.instance_variable_defined?(ivar)
65
+
66
+ type.send(:define_method, name) do
67
+ instance_variable_get(ivar)
68
+ end
69
+ type.send(:define_method, @setter) do |value|
70
+ instance_variable_set(ivar, value)
71
+ end
43
72
  end
44
73
 
45
- def from_xml(nut, node)
46
- set(nut, getxml(node))
74
+ def read(nut, reader)
75
+ set(nut, read_it(reader, get(nut)))
76
+ end
77
+
78
+ def write(nut, writer)
79
+ write_it(writer, get(nut))
80
+ end
81
+
82
+ def clear(nut)
83
+ set(nut, nil)
47
84
  end
48
85
 
49
86
  private
@@ -55,99 +92,117 @@ module Peanuts
55
92
  nut.send(@setter, value)
56
93
  end
57
94
 
58
- def toxml(value)
95
+ def to_xml(value)
59
96
  @converter ? @converter.to_xml(value) : value
60
97
  end
61
98
 
62
- def froxml(text)
99
+ def from_xml(text)
63
100
  @converter ? @converter.from_xml(text) : text
64
101
  end
65
102
 
66
- def each_element(node, &block)
67
- node && backend.each_element(node, xmlname, xmlns, &block)
68
- nil
103
+ def write_node(writer, &block)
104
+ writer.write(node_type, local_name, namespace_uri, prefix, &block)
69
105
  end
106
+ end
70
107
 
71
- def add_element(node, value = nil)
72
- backend.add_element(node, xmlname, xmlns, value)
108
+ module SingleMapping
109
+ private
110
+ def read_it(reader, acc)
111
+ read_value(reader)
73
112
  end
74
113
 
75
- def value(node)
76
- backend.value(node)
114
+ def write_it(writer, value)
115
+ write_node(writer) {|w| write_value(w, value) } if value
77
116
  end
117
+ end
78
118
 
79
- def parse(node)
80
- type.parse_node(type.new, node)
119
+ module MultiMapping
120
+ def read_it(reader, acc)
121
+ (acc || []) << read_value(reader)
81
122
  end
82
123
 
83
- def build(node, nut, dest_node)
84
- nut && type.build_node(nut, dest_node)
124
+ def write_it(writer, values)
125
+ values.each {|value| write_node(writer) {|w| write_value(w, value) } } if values
85
126
  end
86
127
  end
87
128
 
88
- class ElementValue < MemberMapping
129
+ module ValueMapping
89
130
  private
90
- def getxml(node)
91
- each_element(node) {|e| return froxml(value(e)) }
131
+ def read_value(reader)
132
+ from_xml(reader.value)
92
133
  end
93
134
 
94
- def setxml(node, value)
95
- add_element(node, toxml(value))
135
+ def write_value(writer, value)
136
+ writer.write_value(to_xml(value))
96
137
  end
97
138
  end
98
139
 
99
- class Element < MemberMapping
140
+ module ObjectMapping
100
141
  private
101
- def getxml(node)
102
- each_element(node) {|e| return parse(e) }
142
+ def read_value(reader)
143
+ type.mapper.read(type.new, reader)
103
144
  end
104
145
 
105
- def setxml(node, value)
106
- build(node, value, add_element(node))
146
+ def write_value(writer, value)
147
+ type.mapper.write_children(value, writer)
107
148
  end
108
149
  end
109
150
 
151
+ class ElementValue < MemberMapping
152
+ include SingleMapping
153
+ include ValueMapping
154
+
155
+ node_type :element
156
+ end
157
+
158
+ class Element < MemberMapping
159
+ include SingleMapping
160
+ include ObjectMapping
161
+
162
+ node_type :element
163
+ end
164
+
110
165
  class Attribute < MemberMapping
111
- private
112
- def getxml(node)
113
- froxml(backend.attribute(node, xmlname, xmlns))
114
- end
166
+ include SingleMapping
167
+ include ValueMapping
115
168
 
116
- def setxml(node, value)
117
- backend.set_attribute(node, xmlname, xmlns, toxml(value))
169
+ node_type :attribute
170
+
171
+ def initialize(name, type, options)
172
+ super
173
+ raise ArgumentError, 'a namespaced attribute must have namespace prefix' if namespace_uri && !prefix
118
174
  end
119
175
  end
120
176
 
121
177
  class ElementValues < MemberMapping
122
- private
123
- def each_value(node)
124
- each_element(node) {|x| yield froxml(value(x)) }
125
- end
126
-
127
- def getxml(node)
128
- enum_for(:each_value, node).to_a
129
- end
178
+ include MultiMapping
179
+ include ValueMapping
130
180
 
131
- def setxml(node, values)
132
- unless node
133
- raise 'fuck'
134
- end
135
- values.each {|v| add_element(node, toxml(v)) } if values
136
- end
181
+ node_type :element
137
182
  end
138
183
 
139
184
  class Elements < MemberMapping
140
- private
141
- def each_object(node)
142
- each_element(node) {|e| yield parse(e) }
185
+ include MultiMapping
186
+ include ObjectMapping
187
+
188
+ node_type :element
189
+ end
190
+
191
+ class ShallowElement < Element
192
+ def read(nut, reader)
193
+ type.mapper.read(nut, reader)
194
+ end
195
+
196
+ def write(nut, writer)
197
+ write_node(writer) {|w| type.mapper.write_children(nut, w) }
143
198
  end
144
199
 
145
- def getxml(node)
146
- enum_for(:each_object, node)
200
+ def clear(nut)
201
+ type.mapper.clear(nut)
147
202
  end
148
203
 
149
- def setxml(node, elements)
150
- elements.each {|e| build(node, e, add_element(node)) } if elements
204
+ def define_accessors(type)
205
+ self.type.mapper.define_accessors(type)
151
206
  end
152
207
  end
153
208
  end
@@ -0,0 +1,231 @@
1
+ require 'libxml'
2
+ require 'forwardable'
3
+ require 'uri'
4
+ require 'stringio'
5
+ require 'peanuts/xml'
6
+
7
+ module Peanuts
8
+ module XML
9
+ module LibXML
10
+ def self.libxml_opt(options, default = {})
11
+ h = default.merge(options)
12
+ h.update(h.from_namespace!(:libxml))
13
+ end
14
+
15
+ class Writer < Peanuts::XML::Writer
16
+ DEFAULT_OPTIONS = {}
17
+
18
+ def initialize(dest, options = {})
19
+ @dest = case dest
20
+ when :string
21
+ ''
22
+ when :document
23
+ ::LibXML::XML::Document.new
24
+ when ::LibXML::XML::Document
25
+ dest
26
+ else
27
+ raise ArgumentError, "unsupported destination #{dest.inspect}" unless dest.respond_to?(:<<)
28
+ dest
29
+ end
30
+ @options = LibXML.libxml_opt(options, DEFAULT_OPTIONS)
31
+ end
32
+
33
+ def result
34
+ @dest
35
+ end
36
+
37
+ def write_value(value)
38
+ case @node
39
+ when ::LibXML::XML::Attr
40
+ @node.value = value || ''
41
+ else
42
+ @node.content = value || ''
43
+ end
44
+ end
45
+
46
+ def write_namespaces(namespaces)
47
+ for prefix, uri in namespaces
48
+ mkns(@node, uri, prefix)
49
+ end
50
+ end
51
+
52
+ def write(node_type, local_name = nil, namespace_uri = nil, prefix = nil)
53
+ case node_type
54
+ when :element
55
+ @node = ::LibXML::XML::Node.new(local_name)
56
+ @parent << @node if @parent
57
+ if namespace_uri || prefix
58
+ unless namespace_uri
59
+ defns = @node.namespaces.default
60
+ namespace_uri = defns ? defns.href : ''
61
+ end
62
+ @node.namespaces.namespace = mkns(@node, namespace_uri, prefix)
63
+ end
64
+ when :attribute
65
+ @node = ::LibXML::XML::Attr.new(@parent, local_name, '', namespace_uri && mkns(@parent, namespace_uri, prefix))
66
+ else
67
+ raise "unsupported node type #{node_type.inspect}"
68
+ end
69
+
70
+ exparent, @parent = @parent, @node
71
+
72
+ yield self
73
+
74
+ if exparent.nil?
75
+ case @dest
76
+ when ::LibXML::XML::Document
77
+ @dest.root = @parent
78
+ else
79
+ @dest << @parent.to_s(@options)
80
+ end
81
+ end
82
+
83
+ @parent = exparent
84
+ end
85
+
86
+ private
87
+ def mkns(node, namespace_uri, prefix)
88
+ prefix = nil if prefix && (prefix = prefix.to_s).empty?
89
+ newns = proc { ::LibXML::XML::Namespace.new(node, prefix, namespace_uri) }
90
+ node.namespaces.find(newns) {|ns| ns.prefix == prefix && ns.href == namespace_uri }
91
+ end
92
+ end
93
+
94
+ class Reader < Peanuts::XML::Reader
95
+ extend Forwardable
96
+
97
+ SCHEMAS = {:xml_schema => :schema, :relax_ng => :relax_ng}
98
+
99
+ RD = ::LibXML::XML::Reader
100
+
101
+ NODE_TYPES = [
102
+ nil,
103
+ :element,
104
+ :attribute,
105
+ :text,
106
+ :cdata,
107
+ :entity_reference,
108
+ :entity,
109
+ :processing_instruction,
110
+ :comment,
111
+ :document,
112
+ :document_type,
113
+ :document_fragment,
114
+ :notation,
115
+ :whitespace,
116
+ :significant_whitespace,
117
+ :end_element,
118
+ :end_entity,
119
+ :xml_declaration
120
+ ].freeze
121
+
122
+ DEFAULT_OPTIONS = {}
123
+
124
+ def initialize(source, options = {})
125
+ super()
126
+ options = options.dup
127
+ @schema = options.delete(:schema)
128
+ options = LibXML.libxml_opt(options, DEFAULT_OPTIONS)
129
+ @reader = case source
130
+ when String
131
+ RD.string(source.to_s, options)
132
+ when URI
133
+ RD.file(source.to_s, options)
134
+ when ::LibXML::XML::Document
135
+ RD.document(source)
136
+ else
137
+ raise "unsupported source #{source.inspect}" unless source.respond_to?(:read)
138
+ RD.io(source, options)
139
+ end
140
+ @reader.send("#{SCHEMAS[schema.type]}_validate", schema.schema) if @schema
141
+ end
142
+
143
+ def close
144
+ @reader.close
145
+ end
146
+
147
+ def_delegators :@reader, :name, :local_name, :namespace_uri, :depth
148
+
149
+ def node_type
150
+ NODE_TYPES[@reader.node_type]
151
+ end
152
+
153
+ def value
154
+ case @reader.node_type
155
+ when RD::TYPE_ELEMENT
156
+ @reader.read_string
157
+ else
158
+ @reader.has_value? ? @reader.value : nil
159
+ end
160
+ end
161
+
162
+ def each
163
+ depth = self.depth
164
+ if read
165
+ while self.depth > depth
166
+ yield self
167
+ break unless next_sibling
168
+ end
169
+ end
170
+ end
171
+
172
+ def find_element
173
+ until @reader.node_type == RD::TYPE_ELEMENT
174
+ return nil unless read
175
+ end
176
+ self
177
+ end
178
+
179
+ private
180
+ def read
181
+ case @reader.node_type
182
+ when RD::TYPE_ATTRIBUTE
183
+ @reader.move_to_next_attribute > 0 || @reader.read
184
+ else
185
+ @reader.move_to_first_attribute > 0 || @reader.read
186
+ end
187
+ end
188
+
189
+ def next_sibling
190
+ case @reader.node_type
191
+ when RD::TYPE_ATTRIBUTE
192
+ @reader.move_to_next_attribute > 0 || @reader.read
193
+ else
194
+ @reader.next > 0
195
+ end
196
+ end
197
+ end
198
+
199
+ def self.schema(schema_type, source)
200
+ schema_class = case schema_type
201
+ when :xml_schema
202
+ ::LibXML::XML::Schema
203
+ when :relax_ng
204
+ ::LibXML::XML::RelaxNG
205
+ else
206
+ raise ArgumentError, "unrecognized schema type #{schema_type}"
207
+ end
208
+ schema = case source
209
+ when IO
210
+ schema_class.string(source.read)
211
+ when URI
212
+ schema_class.new(source.to_s)
213
+ when ::LibXML::XML::Document
214
+ schema_class.document(source)
215
+ else
216
+ schema_class.string(source)
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,60 @@
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)
28
+ default.schema(schema_type, source)
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)
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
+ end
50
+
51
+ class Writer
52
+ def self.new(*args, &block)
53
+ cls = self == Writer ? XML.default::Writer : self
54
+ obj = cls.allocate
55
+ obj.send(:initialize, *args, &block)
56
+ obj
57
+ end
58
+ end
59
+ end
60
+ end
data/lib/peanuts.rb CHANGED
@@ -1 +1,7 @@
1
- require 'peanuts/nuts'
1
+ require 'peanuts/mappable'
2
+
3
+ module Peanuts #:nodoc:
4
+ def self.included(other)
5
+ other.send(:include, MappableObject)
6
+ end
7
+ end