Recharge 1.1.1 → 1.1.3
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/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