Recharge 1.1.1 → 1.1.3
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/recharge.rb +2 -0
- metadata +3 -8
- data/lib/happymapper.rb +0 -315
- data/lib/happymapper/attribute.rb +0 -3
- data/lib/happymapper/element.rb +0 -3
- data/lib/happymapper/item.rb +0 -179
- data/lib/happymapper/version.rb +0 -3
data/lib/recharge.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: Recharge
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 21
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 1
|
8
8
|
- 1
|
9
|
-
-
|
10
|
-
version: 1.1.
|
9
|
+
- 3
|
10
|
+
version: 1.1.3
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Recharge
|
@@ -42,11 +42,6 @@ extensions: []
|
|
42
42
|
extra_rdoc_files: []
|
43
43
|
|
44
44
|
files:
|
45
|
-
- lib/happymapper/attribute.rb
|
46
|
-
- lib/happymapper/element.rb
|
47
|
-
- lib/happymapper/item.rb
|
48
|
-
- lib/happymapper/version.rb
|
49
|
-
- lib/happymapper.rb
|
50
45
|
- lib/recharge/base.rb
|
51
46
|
- lib/recharge/charge.rb
|
52
47
|
- lib/recharge/client.rb
|
data/lib/happymapper.rb
DELETED
@@ -1,315 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'date'
|
3
|
-
require 'time'
|
4
|
-
require 'xml'
|
5
|
-
|
6
|
-
class Boolean; end
|
7
|
-
|
8
|
-
module HappyMapper
|
9
|
-
|
10
|
-
DEFAULT_NS = "happymapper"
|
11
|
-
|
12
|
-
def self.included(base)
|
13
|
-
base.instance_variable_set("@attributes", {})
|
14
|
-
base.instance_variable_set("@elements", {})
|
15
|
-
base.instance_variable_set("@registered_namespaces", {})
|
16
|
-
|
17
|
-
base.extend ClassMethods
|
18
|
-
end
|
19
|
-
|
20
|
-
module ClassMethods
|
21
|
-
def attribute(name, type, options={})
|
22
|
-
attribute = Attribute.new(name, type, options)
|
23
|
-
@attributes[to_s] ||= []
|
24
|
-
@attributes[to_s] << attribute
|
25
|
-
attr_accessor attribute.method_name.intern
|
26
|
-
end
|
27
|
-
|
28
|
-
def attributes
|
29
|
-
@attributes[to_s] || []
|
30
|
-
end
|
31
|
-
|
32
|
-
def element(name, type, options={})
|
33
|
-
element = Element.new(name, type, options)
|
34
|
-
@elements[to_s] ||= []
|
35
|
-
@elements[to_s] << element
|
36
|
-
attr_accessor element.method_name.intern
|
37
|
-
end
|
38
|
-
|
39
|
-
def content(name)
|
40
|
-
@content = name
|
41
|
-
attr_accessor name
|
42
|
-
end
|
43
|
-
|
44
|
-
def after_parse_callbacks
|
45
|
-
@after_parse_callbacks ||= []
|
46
|
-
end
|
47
|
-
|
48
|
-
def after_parse(&block)
|
49
|
-
after_parse_callbacks.push(block)
|
50
|
-
end
|
51
|
-
|
52
|
-
def elements
|
53
|
-
@elements[to_s] || []
|
54
|
-
end
|
55
|
-
|
56
|
-
def has_one(name, type, options={})
|
57
|
-
element name, type, {:single => true}.merge(options)
|
58
|
-
end
|
59
|
-
|
60
|
-
def has_many(name, type, options={})
|
61
|
-
element name, type, {:single => false}.merge(options)
|
62
|
-
end
|
63
|
-
|
64
|
-
# Specify a namespace if a node and all its children are all namespaced
|
65
|
-
# elements. This is simpler than passing the :namespace option to each
|
66
|
-
# defined element.
|
67
|
-
def namespace(namespace = nil)
|
68
|
-
@namespace = namespace if namespace
|
69
|
-
@namespace
|
70
|
-
end
|
71
|
-
|
72
|
-
def register_namespace(namespace, ns)
|
73
|
-
@registered_namespaces.merge!(namespace => ns)
|
74
|
-
end
|
75
|
-
|
76
|
-
def tag(new_tag_name)
|
77
|
-
@tag_name = new_tag_name.to_s
|
78
|
-
end
|
79
|
-
|
80
|
-
def tag_name
|
81
|
-
@tag_name ||= to_s.split('::')[-1].downcase
|
82
|
-
end
|
83
|
-
|
84
|
-
def parse(xml, options = {})
|
85
|
-
if xml.is_a?(XML::Node)
|
86
|
-
node = xml
|
87
|
-
else
|
88
|
-
if xml.is_a?(XML::Document)
|
89
|
-
node = xml.root
|
90
|
-
else
|
91
|
-
node = XML::Parser.string(xml).parse.root
|
92
|
-
end
|
93
|
-
|
94
|
-
root = node.name == tag_name
|
95
|
-
end
|
96
|
-
|
97
|
-
namespace = @namespace || (node.namespaces && node.namespaces.default)
|
98
|
-
namespace = "#{DEFAULT_NS}:#{namespace}" if namespace
|
99
|
-
|
100
|
-
xpath = root ? '/' : './/'
|
101
|
-
xpath += "#{DEFAULT_NS}:" if namespace
|
102
|
-
xpath += tag_name
|
103
|
-
|
104
|
-
nodes = node.find(xpath, Array(namespace))
|
105
|
-
collection = nodes.collect do |n|
|
106
|
-
obj = new
|
107
|
-
|
108
|
-
attributes.each do |attr|
|
109
|
-
obj.send("#{attr.method_name}=",
|
110
|
-
attr.from_xml_node(n, namespace))
|
111
|
-
end
|
112
|
-
|
113
|
-
elements.each do |elem|
|
114
|
-
obj.send("#{elem.method_name}=",
|
115
|
-
elem.from_xml_node(n, namespace))
|
116
|
-
end
|
117
|
-
|
118
|
-
obj.send("#{@content}=", n.content) if @content
|
119
|
-
|
120
|
-
obj.class.after_parse_callbacks.each { |callback| callback.call(obj) }
|
121
|
-
|
122
|
-
obj
|
123
|
-
end
|
124
|
-
|
125
|
-
# per http://libxml.rubyforge.org/rdoc/classes/LibXML/XML/Document.html#M000354
|
126
|
-
nodes = nil
|
127
|
-
|
128
|
-
if options[:single] || root
|
129
|
-
collection.first
|
130
|
-
else
|
131
|
-
collection
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
end
|
136
|
-
|
137
|
-
#
|
138
|
-
# Create an xml representation of the specified class based on defined
|
139
|
-
# HappyMapper elements and attributes. The method is defined in a way
|
140
|
-
# that it can be called recursively by classes that are also HappyMapper
|
141
|
-
# classes, allowg for the composition of classes.
|
142
|
-
#
|
143
|
-
def to_xml(parent_node = nil, default_namespace = nil)
|
144
|
-
|
145
|
-
#
|
146
|
-
# Create a tag that uses the tag name of the class that has no contents
|
147
|
-
# but has the specified namespace or uses the default namespace
|
148
|
-
#
|
149
|
-
current_node = XML::Node.new(self.class.tag_name)
|
150
|
-
|
151
|
-
|
152
|
-
if parent_node
|
153
|
-
#
|
154
|
-
# if #to_xml has been called with a parent_node that means this method
|
155
|
-
# is being called recursively (or a special case) and we want to return
|
156
|
-
# the parent_node with the new node as a child
|
157
|
-
#
|
158
|
-
parent_node << current_node
|
159
|
-
else
|
160
|
-
#
|
161
|
-
# If #to_xml has been called without a Node (and namespace) that
|
162
|
-
# means we want to return an xml document
|
163
|
-
#
|
164
|
-
write_out_to_xml = true
|
165
|
-
end
|
166
|
-
|
167
|
-
#
|
168
|
-
# Add all the registered namespaces to the current node and the current node's
|
169
|
-
# root element. Without adding it to the root element it is not possible to
|
170
|
-
# parse or use xpath to find elements.
|
171
|
-
#
|
172
|
-
if self.class.instance_variable_get('@registered_namespaces')
|
173
|
-
|
174
|
-
# Given a node, continue moving up to parents until there are no more parents
|
175
|
-
find_root_node = lambda {|node| while node.parent? ; node = node.parent ; end ; node }
|
176
|
-
root_node = find_root_node.call(current_node)
|
177
|
-
|
178
|
-
# Add the registered namespace to the found root node only if it does not already have one defined
|
179
|
-
self.class.instance_variable_get('@registered_namespaces').each_pair do |prefix,href|
|
180
|
-
XML::Namespace.new(current_node,prefix,href)
|
181
|
-
XML::Namespace.new(root_node,prefix,href) unless root_node.namespaces.find_by_prefix(prefix)
|
182
|
-
end
|
183
|
-
end
|
184
|
-
|
185
|
-
#
|
186
|
-
# Determine the tag namespace if one has been specified. This value takes
|
187
|
-
# precendence over one that is handed down to composed sub-classes.
|
188
|
-
#
|
189
|
-
tag_namespace = current_node.namespaces.find_by_prefix(self.class.namespace) || default_namespace
|
190
|
-
|
191
|
-
# Set the namespace of the current node to the specified namespace
|
192
|
-
current_node.namespaces.namespace = tag_namespace if tag_namespace
|
193
|
-
|
194
|
-
#
|
195
|
-
# Add all the attribute tags to the current node with their namespace, if one
|
196
|
-
# is defined, or the namespace handed down to the node.
|
197
|
-
#
|
198
|
-
self.class.attributes.each do |attribute|
|
199
|
-
attribute_namespace = current_node.namespaces.find_by_prefix(attribute.options[:namespace]) || default_namespace
|
200
|
-
|
201
|
-
value = send(attribute.method_name)
|
202
|
-
|
203
|
-
#
|
204
|
-
# If the attribute has a :on_save attribute defined that is a proc or
|
205
|
-
# a defined method, then call those with the current value.
|
206
|
-
#
|
207
|
-
if on_save_operation = attribute.options[:on_save]
|
208
|
-
if on_save_operation.is_a?(Proc)
|
209
|
-
value = on_save_operation.call(value)
|
210
|
-
elsif respond_to?(on_save_operation)
|
211
|
-
value = send(on_save_operation,value)
|
212
|
-
end
|
213
|
-
end
|
214
|
-
|
215
|
-
current_node[ "#{attribute_namespace ? "#{attribute_namespace.prefix}:" : ""}#{attribute.tag}" ] = value
|
216
|
-
end
|
217
|
-
|
218
|
-
#
|
219
|
-
# All all the elements defined (e.g. has_one, has_many, element) ...
|
220
|
-
#
|
221
|
-
self.class.elements.each do |element|
|
222
|
-
|
223
|
-
tag = element.tag || element.name
|
224
|
-
|
225
|
-
element_namespace = current_node.namespaces.find_by_prefix(element.options[:namespace]) || tag_namespace
|
226
|
-
|
227
|
-
value = send(element.name)
|
228
|
-
|
229
|
-
#
|
230
|
-
# If the element defines an :on_save lambda/proc then we will call that
|
231
|
-
# operation on the specified value. This allows for operations to be
|
232
|
-
# performed to convert the value to a specific value to be saved to the xml.
|
233
|
-
#
|
234
|
-
if on_save_operation = element.options[:on_save]
|
235
|
-
if on_save_operation.is_a?(Proc)
|
236
|
-
value = on_save_operation.call(value)
|
237
|
-
elsif respond_to?(on_save_operation)
|
238
|
-
value = send(on_save_operation,value)
|
239
|
-
end
|
240
|
-
end
|
241
|
-
|
242
|
-
#
|
243
|
-
# Normally a nil value would be ignored, however if specified then
|
244
|
-
# an empty element will be written to the xml
|
245
|
-
#
|
246
|
-
if value.nil? && element.options[:state_when_nil]
|
247
|
-
current_node << XML::Node.new(tag,nil,element_namespace)
|
248
|
-
end
|
249
|
-
|
250
|
-
#
|
251
|
-
# To allow for us to treat both groups of items and singular items
|
252
|
-
# equally we wrap the value and treat it as an array.
|
253
|
-
#
|
254
|
-
if value.nil?
|
255
|
-
values = []
|
256
|
-
elsif value.respond_to?(:to_ary) && !element.options[:single]
|
257
|
-
values = value.to_ary
|
258
|
-
else
|
259
|
-
values = [value]
|
260
|
-
end
|
261
|
-
|
262
|
-
|
263
|
-
values.each do |item|
|
264
|
-
|
265
|
-
if item.is_a?(HappyMapper)
|
266
|
-
|
267
|
-
#
|
268
|
-
# Other HappyMapper items that are convertable should not be called
|
269
|
-
# with the current node and the namespace defined for the element.
|
270
|
-
#
|
271
|
-
item.to_xml(current_node,element_namespace)
|
272
|
-
|
273
|
-
elsif item
|
274
|
-
|
275
|
-
#
|
276
|
-
# When a value exists we should append the value for the tag
|
277
|
-
#
|
278
|
-
current_node << XML::Node.new(tag,item.to_s,element_namespace)
|
279
|
-
|
280
|
-
else
|
281
|
-
|
282
|
-
#
|
283
|
-
# Normally a nil value would be ignored, however if specified then
|
284
|
-
# an empty element will be written to the xml
|
285
|
-
#
|
286
|
-
current_node << XML.Node.new(tag,nil,element_namespace) if element.options[:state_when_nil]
|
287
|
-
|
288
|
-
end
|
289
|
-
|
290
|
-
end
|
291
|
-
|
292
|
-
end
|
293
|
-
|
294
|
-
|
295
|
-
#
|
296
|
-
# Generate xml from a document if no node was passed as a parameter. Otherwise
|
297
|
-
# this method is being called recursively (or special case) and we should
|
298
|
-
# return the node with this node attached as a child.
|
299
|
-
#
|
300
|
-
if write_out_to_xml
|
301
|
-
document = XML::Document.new
|
302
|
-
document.root = current_node
|
303
|
-
document.to_s
|
304
|
-
else
|
305
|
-
parent_node
|
306
|
-
end
|
307
|
-
|
308
|
-
end
|
309
|
-
|
310
|
-
|
311
|
-
end
|
312
|
-
|
313
|
-
require File.dirname(__FILE__) + '/happymapper/item'
|
314
|
-
require File.dirname(__FILE__) + '/happymapper/attribute'
|
315
|
-
require File.dirname(__FILE__) + '/happymapper/element'
|
data/lib/happymapper/element.rb
DELETED
data/lib/happymapper/item.rb
DELETED
@@ -1,179 +0,0 @@
|
|
1
|
-
module HappyMapper
|
2
|
-
class Item
|
3
|
-
attr_accessor :name, :type, :tag, :options, :namespace
|
4
|
-
|
5
|
-
Types = [String, Float, Time, Date, DateTime, Integer, Boolean]
|
6
|
-
|
7
|
-
# options:
|
8
|
-
# :deep => Boolean False to only parse element's children, True to include
|
9
|
-
# grandchildren and all others down the chain (// in expath)
|
10
|
-
# :namespace => String Element's namespace if it's not the global or inherited
|
11
|
-
# default
|
12
|
-
# :parser => Symbol Class method to use for type coercion.
|
13
|
-
# :raw => Boolean Use raw node value (inc. tags) when parsing.
|
14
|
-
# :single => Boolean False if object should be collection, True for single object
|
15
|
-
# :tag => String Element name if it doesn't match the specified name.
|
16
|
-
def initialize(name, type, o={})
|
17
|
-
self.name = name.to_s
|
18
|
-
self.type = type
|
19
|
-
self.tag = o.delete(:tag) || name.to_s
|
20
|
-
self.options = o
|
21
|
-
|
22
|
-
@xml_type = self.class.to_s.split('::').last.downcase
|
23
|
-
end
|
24
|
-
|
25
|
-
def constant
|
26
|
-
@constant ||= constantize(type)
|
27
|
-
end
|
28
|
-
|
29
|
-
def from_xml_node(node, namespace)
|
30
|
-
if primitive?
|
31
|
-
find(node, namespace) do |n|
|
32
|
-
if n.respond_to?(:content)
|
33
|
-
typecast(n.content)
|
34
|
-
else
|
35
|
-
typecast(n.to_s)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
else
|
39
|
-
if options[:parser]
|
40
|
-
find(node, namespace) do |n|
|
41
|
-
if n.respond_to?(:content) && !options[:raw]
|
42
|
-
value = n.content
|
43
|
-
else
|
44
|
-
value = n.to_s
|
45
|
-
end
|
46
|
-
|
47
|
-
begin
|
48
|
-
constant.send(options[:parser].to_sym, value)
|
49
|
-
rescue
|
50
|
-
nil
|
51
|
-
end
|
52
|
-
end
|
53
|
-
else
|
54
|
-
constant.parse(node, options)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
def xpath(namespace = self.namespace)
|
60
|
-
xpath = ''
|
61
|
-
xpath += './/' if options[:deep]
|
62
|
-
xpath += "#{DEFAULT_NS}:" if namespace
|
63
|
-
xpath += tag
|
64
|
-
# puts "xpath: #{xpath}"
|
65
|
-
xpath
|
66
|
-
end
|
67
|
-
|
68
|
-
def primitive?
|
69
|
-
Types.include?(constant)
|
70
|
-
end
|
71
|
-
|
72
|
-
def element?
|
73
|
-
@xml_type == 'element'
|
74
|
-
end
|
75
|
-
|
76
|
-
def attribute?
|
77
|
-
!element?
|
78
|
-
end
|
79
|
-
|
80
|
-
def method_name
|
81
|
-
@method_name ||= name.tr('-', '_')
|
82
|
-
end
|
83
|
-
|
84
|
-
def typecast(value)
|
85
|
-
return value if value.kind_of?(constant) || value.nil?
|
86
|
-
begin
|
87
|
-
if constant == String then value.to_s
|
88
|
-
elsif constant == Float then value.to_f
|
89
|
-
elsif constant == Time then Time.parse(value.to_s)
|
90
|
-
elsif constant == Date then Date.parse(value.to_s)
|
91
|
-
elsif constant == DateTime then DateTime.parse(value.to_s)
|
92
|
-
elsif constant == Boolean then ['true', 't', '1'].include?(value.to_s.downcase)
|
93
|
-
elsif constant == Integer
|
94
|
-
# ganked from datamapper
|
95
|
-
value_to_i = value.to_i
|
96
|
-
if value_to_i == 0 && value != '0'
|
97
|
-
value_to_s = value.to_s
|
98
|
-
begin
|
99
|
-
Integer(value_to_s =~ /^(\d+)/ ? $1 : value_to_s)
|
100
|
-
rescue ArgumentError
|
101
|
-
nil
|
102
|
-
end
|
103
|
-
else
|
104
|
-
value_to_i
|
105
|
-
end
|
106
|
-
else
|
107
|
-
value
|
108
|
-
end
|
109
|
-
rescue
|
110
|
-
value
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
private
|
115
|
-
def constantize(type)
|
116
|
-
if type.is_a?(String)
|
117
|
-
names = type.split('::')
|
118
|
-
constant = Object
|
119
|
-
names.each do |name|
|
120
|
-
constant = constant.const_defined?(name) ?
|
121
|
-
constant.const_get(name) :
|
122
|
-
constant.const_missing(name)
|
123
|
-
end
|
124
|
-
constant
|
125
|
-
else
|
126
|
-
type
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
def find(node, namespace, &block)
|
131
|
-
if options[:namespace] == false
|
132
|
-
namespace = nil
|
133
|
-
elsif options[:namespace]
|
134
|
-
# from an element definition
|
135
|
-
namespace = "#{DEFAULT_NS}:#{options[:namespace]}"
|
136
|
-
elsif self.namespace
|
137
|
-
# this node has a custom namespace (that is present in the doc)
|
138
|
-
namespace = "#{DEFAULT_NS}:#{self.namespace}"
|
139
|
-
end
|
140
|
-
|
141
|
-
if element?
|
142
|
-
if(options[:single].nil? || options[:single])
|
143
|
-
result = node.find_first(xpath(namespace), namespace)
|
144
|
-
else
|
145
|
-
result = node.find(xpath(namespace))
|
146
|
-
end
|
147
|
-
# puts "vfxn: #{xpath} #{result.inspect}"
|
148
|
-
if result
|
149
|
-
if(options[:single].nil? || options[:single])
|
150
|
-
value = yield(result)
|
151
|
-
else
|
152
|
-
value = []
|
153
|
-
|
154
|
-
result.each do |res|
|
155
|
-
value << yield(res)
|
156
|
-
end
|
157
|
-
end
|
158
|
-
if options[:attributes].is_a?(Hash)
|
159
|
-
result.attributes.each do |xml_attribute|
|
160
|
-
if attribute_options = options[:attributes][xml_attribute.name.to_sym]
|
161
|
-
attribute_value = Attribute.new(xml_attribute.name.to_sym, *attribute_options).from_xml_node(result, namespace)
|
162
|
-
result.instance_eval <<-EOV
|
163
|
-
def value.#{xml_attribute.name}
|
164
|
-
#{attribute_value.inspect}
|
165
|
-
end
|
166
|
-
EOV
|
167
|
-
end
|
168
|
-
end
|
169
|
-
end
|
170
|
-
value
|
171
|
-
else
|
172
|
-
nil
|
173
|
-
end
|
174
|
-
else
|
175
|
-
yield(node[tag])
|
176
|
-
end
|
177
|
-
end
|
178
|
-
end
|
179
|
-
end
|
data/lib/happymapper/version.rb
DELETED