Empact-roxml 2.1 → 2.2

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.
@@ -0,0 +1,5 @@
1
+ require File.join(File.dirname(__FILE__), 'array/conversions')
2
+
3
+ class Array #:nodoc:
4
+ include ROXML::CoreExtensions::Array::Conversions
5
+ end
@@ -0,0 +1,27 @@
1
+ require File.join(File.dirname(__FILE__), '../deprecation')
2
+
3
+ module ROXML
4
+ module CoreExtensions
5
+ module Array #:nodoc:
6
+ module Conversions
7
+ # Translates an array into a hash, where each element of the array is
8
+ # an array with 2 elements:
9
+ #
10
+ # >> [[:key, :value], [1, 2], ['key', 'value']].to_h
11
+ # => {:key => :value, 1 => 2, 'key' => 'value'}
12
+ #
13
+ def to_hash
14
+ inject({}) do |result, (k, v)|
15
+ result[k] = v
16
+ result
17
+ end
18
+ end
19
+
20
+ def to_h #:nodoc:
21
+ ActiveSupport::Deprecation.warn "Please use #to_hash instead"
22
+ to_hash
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,15 @@
1
+ require 'active_support'
2
+ require 'active_support/version'
3
+
4
+ module ActiveSupport # :nodoc:all
5
+ if VERSION::MAJOR <= 2 && VERSION::MINOR <= 1
6
+ module Deprecation
7
+ class << self
8
+ def deprecation_message(callstack, message = nil)
9
+ message ||= "You are using deprecated behavior which will be removed from the next major or minor release."
10
+ "DEPRECATION WARNING: #{message}. #{deprecation_caller_message(callstack)}"
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,22 @@
1
+ require File.join(File.dirname(__FILE__), 'string/conversions')
2
+ require File.join(File.dirname(__FILE__), 'string/iterators')
3
+
4
+ class String #:nodoc:
5
+ include ROXML::CoreExtensions::String::Conversions
6
+ include ROXML::CoreExtensions::String::Iterators
7
+ end
8
+
9
+ require File.join(File.dirname(__FILE__), 'deprecation')
10
+ class Object #:nodoc:
11
+ # Deprecated in favor of explicit #to_s.to_utf
12
+ def to_utf
13
+ ActiveSupport::Deprecation.warn "This method will be removed from Object please use String#to_utf instead via explicit #to_s"
14
+ to_s.to_utf
15
+ end
16
+
17
+ # Deprecated in favor of explicit #to_s.to_latin
18
+ def to_latin
19
+ ActiveSupport::Deprecation.warn "This method will be removed from Object please use String#to_latin instead via explicit #to_s"
20
+ to_s.to_latin
21
+ end
22
+ end
@@ -0,0 +1,44 @@
1
+ module ROXML
2
+ module CoreExtensions #:nodoc:
3
+ module String
4
+ # Extension of String class to handle conversion from/to
5
+ # UTF-8/ISO-8869-1
6
+ module Conversions
7
+ require 'iconv'
8
+
9
+ #
10
+ # Return an utf-8 representation of this string.
11
+ #
12
+ def to_utf
13
+ begin
14
+ Iconv.new("utf-8", "iso-8859-1").iconv(to_s)
15
+ rescue Iconv::IllegalSequence
16
+ STDERR << "!! Failed converting from UTF-8 -> ISO-8859-1 (#{self}). Already the right charset?"
17
+ self
18
+ end
19
+ end
20
+
21
+ #
22
+ # Convert this string to iso-8850-1
23
+ #
24
+ def to_latin
25
+ begin
26
+ Iconv.new("iso-8859-1", "utf-8").iconv(to_s)
27
+ rescue Iconv::IllegalSequence
28
+ STDERR << "!! Failed converting from ISO-8859-1 -> UTF-8 (#{self}). Already the right charset?"
29
+ self
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ class Object
38
+ end
39
+
40
+ class String
41
+ def between(separator, &block)
42
+ split(separator).collect(&block).join(separator)
43
+ end
44
+ end
@@ -0,0 +1,12 @@
1
+ module ROXML
2
+ module CoreExtensions
3
+ module String #:nodoc:
4
+ module Iterators
5
+ # Allows you to iterate over and modify the sub-strings between _separator_. Returns the joined result of the modification.
6
+ def between(separator, &block)
7
+ split(separator).collect(&block).join(separator)
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
data/lib/roxml/options.rb CHANGED
@@ -2,7 +2,7 @@ module ROXML
2
2
  HASH_KEYS = [:attrs, :key, :value].freeze
3
3
  TYPE_KEYS = [:attr, :text, :hash, :content].freeze
4
4
 
5
- class HashDesc # ::nodoc::
5
+ class HashDesc # :nodoc:
6
6
  attr_reader :key, :value, :wrapper
7
7
 
8
8
  def initialize(opts, wrapper)
@@ -78,25 +78,24 @@ module ROXML
78
78
  end
79
79
  end
80
80
 
81
- class Opts # ::nodoc::
82
- attr_reader :type, :hash
81
+ class Opts # :nodoc:
82
+ attr_reader :name, :type, :hash, :blocks
83
83
 
84
- def initialize(sym, *args)
84
+ def initialize(sym, *args, &block)
85
85
  @opts = extract_options!(args)
86
86
 
87
87
  @opts.reverse_merge!(:from => sym.to_s, :as => [], :else => nil, :in => nil)
88
88
  @opts[:as] = [*@opts[:as]]
89
89
  @type = extract_type(args)
90
+ @blocks = collect_blocks(block, @opts[:as])
90
91
 
91
92
  @name = @opts[:from].to_s
92
- end
93
-
94
- def name=(n)
95
- @name = n.to_s
96
- end
93
+ @name = @name.singularize if hash? || array?
94
+ if hash? && (hash.key.name? || hash.value.name?)
95
+ @name = '*'
96
+ end
97
97
 
98
- def name
99
- enumerable? ? @name.singularize : @name
98
+ raise ArgumentError, "Can't specify both :else default and :required" if required? && default
100
99
  end
101
100
 
102
101
  def hash
@@ -107,10 +106,6 @@ module ROXML
107
106
  @opts[:else]
108
107
  end
109
108
 
110
- def enumerable?
111
- hash? || array?
112
- end
113
-
114
109
  def hash?
115
110
  @type == :hash
116
111
  end
@@ -131,7 +126,24 @@ module ROXML
131
126
  @opts[:in]
132
127
  end
133
128
 
129
+ def required?
130
+ @opts[:required]
131
+ end
132
+
134
133
  private
134
+ BLOCK_SHORTHANDS = {
135
+ :integer => lambda {|val| Integer(val) },
136
+ Integer => lambda {|val| Integer(val) },
137
+ :float => lambda {|val| Float(val) },
138
+ Float => lambda {|val| Float(val) }
139
+ }
140
+
141
+ def collect_blocks(block, as)
142
+ shorthands = as & BLOCK_SHORTHANDS.keys
143
+ raise ArgumentError, "multiple block shorthands supplied #{shorthands.map(&:to_s).join(', ')}" if shorthands.size > 1
144
+ [BLOCK_SHORTHANDS[shorthands.first], block].compact
145
+ end
146
+
135
147
  def extract_options!(args)
136
148
  opts = args.extract_options!
137
149
  unless (opts.keys & HASH_KEYS).empty?
data/lib/roxml/xml.rb CHANGED
@@ -2,26 +2,27 @@ module ROXML
2
2
  unless const_defined? 'XML_PARSER'
3
3
  begin
4
4
  require 'libxml'
5
- XML_PARSER = 'libxml'
5
+ XML_PARSER = 'libxml' # :nodoc:
6
6
  rescue LoadError
7
- XML_PARSER = 'rexml'
7
+ XML_PARSER = 'rexml' # :nodoc:
8
8
  end
9
9
  end
10
10
  require File.join(File.dirname(__FILE__), 'xml', XML_PARSER)
11
11
 
12
+ class RequiredElementMissing < Exception # :nodoc:
13
+ end
14
+
12
15
  #
13
16
  # Internal base class that represents an XML - Class binding.
14
17
  #
15
- class XMLRef # ::nodoc::
16
- attr_reader :accessor, :name, :array, :default, :block, :wrapper
18
+ class XMLRef # :nodoc:
19
+ attr_reader :accessor
20
+ delegate :name, :required?, :array?, :default, :wrapper, :blocks, :to => :opts
21
+ alias_method :xpath_name, :name
17
22
 
18
- def initialize(accessor, args, &block)
23
+ def initialize(accessor, args)
19
24
  @accessor = accessor
20
- @array = args.array?
21
- @name = args.name
22
- @default = args.default
23
- @block = block
24
- @wrapper = args.wrapper
25
+ @opts = args
25
26
  end
26
27
 
27
28
  # Reads data from the XML element and populates the instance
@@ -36,9 +37,31 @@ module ROXML
36
37
  false
37
38
  end
38
39
 
40
+ def update_xml(xml, value)
41
+ returning wrap(xml) do |xml|
42
+ write_xml(xml, value)
43
+ end
44
+ end
45
+
46
+ def value(xml)
47
+ value = fetch_value(xml)
48
+ if value.blank?
49
+ raise RequiredElementMissing if required?
50
+ value = default
51
+ end
52
+ apply_blocks(value)
53
+ end
54
+
39
55
  private
56
+ attr_reader :opts
57
+
58
+ def apply_blocks(val)
59
+ blocks.each {|block| val = block[*val] } unless blocks.empty?
60
+ val
61
+ end
62
+
40
63
  def xpath
41
- wrapper ? "#{wrapper}#{xpath_separator}#{name}" : name.to_s
64
+ wrapper ? "#{wrapper}/#{xpath_name}" : xpath_name.to_s
42
65
  end
43
66
 
44
67
  def wrap(xml)
@@ -52,23 +75,21 @@ module ROXML
52
75
  # <element attribute="XMLAttributeRef">
53
76
  # XMLTextRef
54
77
  # </element>
55
- class XMLAttributeRef < XMLRef # ::nodoc::
78
+ class XMLAttributeRef < XMLRef # :nodoc:
79
+ private
56
80
  # Updates the attribute in the given XML block to
57
81
  # the value provided.
58
- def update_xml(xml, value)
59
- xml.attributes[name] = value.to_utf
60
- xml
82
+ def write_xml(xml, value)
83
+ xml.attributes[name] = value.to_s.to_utf
61
84
  end
62
85
 
63
- def value(xml)
64
- parent = wrap(xml)
65
- val = xml.attributes[name] || default
66
- block ? block.call(val) : val
86
+ def fetch_value(xml)
87
+ attr = xml.search(xpath).first
88
+ attr && attr.value
67
89
  end
68
90
 
69
- private
70
- def xpath_separator
71
- '@'
91
+ def xpath_name
92
+ "@#{name}"
72
93
  end
73
94
  end
74
95
 
@@ -78,165 +99,117 @@ module ROXML
78
99
  # <element attribute="XMLAttributeRef">
79
100
  # XMLTextRef
80
101
  # </element>
81
- class XMLTextRef < XMLRef # ::nodoc::
82
- attr_reader :cdata, :content
102
+ class XMLTextRef < XMLRef # :nodoc:
103
+ delegate :cdata?, :content?, :to => :opts
83
104
 
84
- def initialize(accessor, args, &block)
85
- super(accessor, args, &block)
86
- @content = args.content?
87
- @cdata = args.cdata?
105
+ def name?
106
+ name == '*'
88
107
  end
89
108
 
109
+ private
90
110
  # Updates the text in the given _xml_ block to
91
111
  # the _value_ provided.
92
- def update_xml(xml, value)
93
- parent = wrap(xml)
94
- if content
95
- add(parent, value)
112
+ def write_xml(xml, value)
113
+ if content?
114
+ add(xml, value)
96
115
  elsif name?
97
- parent.name = value
98
- elsif array
116
+ xml.name = value
117
+ elsif array?
99
118
  value.each do |v|
100
- add(parent.child_add(XML::Node.new_element(name)), v)
119
+ add(xml.child_add(XML::Node.new_element(name)), v)
101
120
  end
102
121
  else
103
- add(parent.child_add(XML::Node.new_element(name)), value)
122
+ add(xml.child_add(XML::Node.new_element(name)), value)
104
123
  end
105
- xml
106
124
  end
107
125
 
108
- def value(xml)
109
- val = if content
126
+ def fetch_value(xml)
127
+ if content?
110
128
  xml.content.strip
111
129
  elsif name?
112
130
  xml.name
113
- elsif array
114
- arr = xml.search(xpath).collect do |e|
131
+ elsif array?
132
+ xml.search(xpath).collect do |e|
115
133
  e.content.strip.to_latin if e.content
116
134
  end
117
- arr unless arr.empty?
118
135
  else
119
136
  child = xml.search(name).first
120
137
  child.content if child
121
138
  end
122
- val = default unless val && !val.blank?
123
- block ? block.call(val) : val
124
- end
125
-
126
- def name?
127
- name == '*'
128
- end
129
-
130
- private
131
- def xpath_separator
132
- '/'
133
139
  end
134
140
 
135
141
  def add(dest, value)
136
- if cdata
137
- dest.child_add(XML::Node.new_cdata(value.to_utf))
142
+ if cdata?
143
+ dest.child_add(XML::Node.new_cdata(value.to_s.to_utf))
138
144
  else
139
- dest.content = value.to_utf
145
+ dest.content = value.to_s.to_utf
140
146
  end
141
147
  end
142
148
  end
143
149
 
144
- class XMLHashRef < XMLTextRef # ::nodoc::
145
- attr_reader :hash
146
-
147
- def initialize(accessor, args, &block)
148
- super(accessor, args, &block)
149
- @hash = args.hash
150
- if @hash.key.name? || @hash.value.name?
151
- @name = '*'
152
- end
153
- end
150
+ class XMLHashRef < XMLTextRef # :nodoc:
151
+ delegate :hash, :to => :opts
154
152
 
153
+ private
155
154
  # Updates the composed XML object in the given XML block to
156
155
  # the value provided.
157
- def update_xml(xml, value)
158
- parent = wrap(xml)
156
+ def write_xml(xml, value)
159
157
  value.each_pair do |k, v|
160
- node = add_node(parent)
158
+ node = xml.child_add(XML::Node.new_element(hash.wrapper))
161
159
  hash.key.update_xml(node, k)
162
160
  hash.value.update_xml(node, v)
163
161
  end
164
- xml
165
162
  end
166
163
 
167
- def value(xml)
168
- vals = xml.search(xpath).collect do |e|
169
- [@hash.key.value(e), @hash.value.value(e)]
170
- end
171
- if block
172
- vals.collect! do |(key, val)|
173
- block.call(key, val)
174
- end
164
+ def fetch_value(xml)
165
+ xml.search(xpath).collect do |e|
166
+ [hash.key.value(e), hash.value.value(e)]
175
167
  end
176
- vals.to_h
177
168
  end
178
169
 
179
- private
180
- def add_node(xml)
181
- xml.child_add(XML::Node.new_element(hash.wrapper))
170
+ def apply_blocks(vals)
171
+ unless blocks.empty?
172
+ vals.collect! do |kvp|
173
+ super(kvp)
174
+ end
175
+ end
176
+ vals.to_hash
182
177
  end
183
178
  end
184
179
 
185
- class XMLObjectRef < XMLTextRef # ::nodoc::
186
- attr_reader :klass
187
-
188
- def initialize(accessor, args, &block)
189
- super(accessor, args, &block)
190
- @klass = args.type
191
- end
180
+ class XMLObjectRef < XMLTextRef # :nodoc:
181
+ delegate :type, :to => :opts
192
182
 
183
+ private
193
184
  # Updates the composed XML object in the given XML block to
194
185
  # the value provided.
195
- def update_xml(xml, value)
196
- parent = wrap(xml)
197
- unless array
198
- parent.child_add(value.to_xml(name))
186
+ def write_xml(xml, value)
187
+ unless array?
188
+ xml.child_add(value.to_xml(name))
199
189
  else
200
190
  value.each do |v|
201
- parent.child_add(v.to_xml(name))
191
+ xml.child_add(v.to_xml(name))
202
192
  end
203
193
  end
204
- xml
205
194
  end
206
195
 
207
- def value(xml)
208
- val = unless array
196
+ def fetch_value(xml)
197
+ unless array?
209
198
  if child = xml.search(xpath).first
210
199
  instantiate(child)
211
200
  end
212
201
  else
213
- arr = xml.search(xpath).collect do |e|
202
+ xml.search(xpath).collect do |e|
214
203
  instantiate(e)
215
204
  end
216
- arr unless arr.empty?
217
- end || default
218
- block ? block.call(val) : val
205
+ end
219
206
  end
220
207
 
221
- private
222
208
  def instantiate(elem)
223
- if klass.respond_to? :parse
224
- klass.parse(elem)
209
+ if type.respond_to? :from_xml
210
+ type.from_xml(elem)
225
211
  else
226
- klass.new(elem)
227
- end
228
- end
229
- end
230
-
231
- #
232
- # Returns an XML::Node representing this object.
233
- #
234
- def to_xml(name = nil)
235
- returning XML::Node.new_element(name || tag_name) do |root|
236
- tag_refs.each do |ref|
237
- if v = __send__(ref.accessor)
238
- ref.update_xml(root, v)
239
- end
212
+ type.new(elem)
240
213
  end
241
214
  end
242
215
  end