omf_sfa 0.2.6 → 0.2.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/bin/brite2rspec.rb +0 -0
- data/lib/omf-sfa/am.rb +4 -1
- data/lib/omf-sfa/am/am-rest/promise_handler.rb +119 -0
- data/lib/omf-sfa/am/am-rest/rest_handler.rb +301 -45
- data/lib/omf-sfa/resource.rb +3 -1
- data/lib/omf-sfa/resource/gurn.rb +5 -3
- data/lib/omf-sfa/resource/node.rb +1 -1
- data/lib/omf-sfa/resource/oproperty.rb +13 -14
- data/lib/omf-sfa/resource/oresource.rb +14 -5
- data/lib/omf-sfa/resource/sfa_base.rb +3 -1
- data/lib/omf-sfa/util/graph_json.rb +34 -14
- data/lib/omf-sfa/util/promise.rb +325 -0
- data/lib/omf-sfa/version.rb +1 -2
- data/omf_sfa.gemspec +1 -1
- metadata +64 -117
data/lib/omf-sfa/resource.rb
CHANGED
@@ -3,6 +3,8 @@ require 'omf_base/lobject'
|
|
3
3
|
|
4
4
|
module OMF::SFA::Resource
|
5
5
|
#
|
6
|
+
class GurnMalformedException < ResourceException; end
|
7
|
+
|
6
8
|
class GURN #< OMF::Base::MObject
|
7
9
|
|
8
10
|
@@def_domain = "urn:publicid:IDN+acme.org"
|
@@ -19,7 +21,7 @@ module OMF::SFA::Resource
|
|
19
21
|
def self.create(name, opts = {})
|
20
22
|
return name if name.kind_of? self
|
21
23
|
unless name
|
22
|
-
raise "No name given"
|
24
|
+
raise GurnMalformedException.new "No name given"
|
23
25
|
end
|
24
26
|
#puts "GUID: #{name}###{opts}"
|
25
27
|
|
@@ -58,11 +60,11 @@ module OMF::SFA::Resource
|
|
58
60
|
prefix, name = a
|
59
61
|
type = nil
|
60
62
|
else
|
61
|
-
raise "unknown format '#{urn_str}' for GURN (#{a.inspect})."
|
63
|
+
raise GurnMalformedException.new "unknown format '#{urn_str}' for GURN (#{a.inspect})."
|
62
64
|
end
|
63
65
|
@@name2obj[urn_str] = self.new(name, type, prefix)
|
64
66
|
else
|
65
|
-
raise "unknown format '#{urn_str}' for GURN - expected it to start with 'urn:publicid:IDN'."
|
67
|
+
raise GurnMalformedException.new "unknown format '#{urn_str}' for GURN - expected it to start with 'urn:publicid:IDN'."
|
66
68
|
end
|
67
69
|
end
|
68
70
|
|
@@ -14,7 +14,7 @@ module OMF::SFA::Resource
|
|
14
14
|
oproperty :available, Boolean, :default => true
|
15
15
|
oproperty :sliver_type, SliverType, :required => false # Is this required?
|
16
16
|
oproperty :interfaces, :interface, :functional => false, :inverse => :node
|
17
|
-
oproperty :exclusive, Boolean, :default =>
|
17
|
+
oproperty :exclusive, Boolean, :default => false
|
18
18
|
oproperty :services, OMF::SFA::Resource::AbstractService, functional: false
|
19
19
|
|
20
20
|
#belongs_to :sliver
|
@@ -62,7 +62,7 @@ module OMF::SFA::Resource
|
|
62
62
|
if l = opts[:limit]
|
63
63
|
q += " LIMIT #{l} OFFSET #{opts[:offset] || 0}"
|
64
64
|
end
|
65
|
-
|
65
|
+
debug "prop_all q: #{q}"
|
66
66
|
res = repository(:default).adapter.select(q)
|
67
67
|
ores = res.map do |qr|
|
68
68
|
if resource_class
|
@@ -93,7 +93,9 @@ module OMF::SFA::Resource
|
|
93
93
|
# TODO: THIS IS REALLY BROKEN! Need to define what a query is in this context
|
94
94
|
#
|
95
95
|
def self._build_query(query, resource_class = nil)
|
96
|
+
#puts ">>>QUERY>>> #{query}"
|
96
97
|
i = 0
|
98
|
+
resource = nil
|
97
99
|
where = query.map do |pn, v|
|
98
100
|
tbl = "p#{i}"
|
99
101
|
case pn.to_sym
|
@@ -101,34 +103,31 @@ module OMF::SFA::Resource
|
|
101
103
|
unless v.is_a? OResource
|
102
104
|
raise "Expected type OResource for :resource, but got '#{v}::#{v.class}'"
|
103
105
|
end
|
104
|
-
|
105
|
-
|
106
|
-
"#{tbl}.
|
106
|
+
resource = v
|
107
|
+
"r.id = #{resource.id}"
|
108
|
+
#"#{tbl}.o_resource_id = #{v.id}"
|
109
|
+
# resource property 'name' is not stored as a property but as a column in the resource
|
110
|
+
# when :name
|
111
|
+
# "r.name = '#{v}'"
|
107
112
|
else
|
108
113
|
h = _analyse_value(v)
|
109
114
|
i += 1
|
110
115
|
if (val = h[:v]).is_a? String
|
111
116
|
val = "'#{val}'"
|
112
117
|
end
|
113
|
-
|
118
|
+
op = (h[:t] == 's' ? 'LIKE' : '=')
|
119
|
+
# TODO: Would also need to add a where clause "#{tbl}.name = #{pn} "
|
120
|
+
"#{tbl}.#{h[:f]} #{op} #{val}"
|
114
121
|
end
|
115
122
|
end
|
116
123
|
|
117
|
-
# TODO: Hack!!!
|
118
|
-
unless i <= 1
|
119
|
-
raise "Are you using the query facility on OProperties correctly? (#{i})"
|
120
|
-
else
|
121
|
-
i = 1
|
122
|
-
end
|
123
|
-
|
124
124
|
i.times do |j|
|
125
125
|
where << "r.id = p#{j}.o_resource_id"
|
126
126
|
end
|
127
127
|
where << "r.type = '#{resource_class}'" if resource_class
|
128
|
-
|
129
128
|
table = storage_names[:default]
|
130
129
|
from = i.times.map {|j| "#{table} AS p#{j}" }
|
131
|
-
from << "omf_sfa_resource_o_resources AS r" # TODO: Shouldn't hard-code that
|
130
|
+
from << "omf_sfa_resource_o_resources AS r" # TODO: Shouldn't hard-code that (why not?)
|
132
131
|
[from, where]
|
133
132
|
end
|
134
133
|
|
@@ -177,7 +177,7 @@ module OMF::SFA::Resource
|
|
177
177
|
elsif v.respond_to?(m = "#{rev_m}=".to_sym)
|
178
178
|
v.send(m, self)
|
179
179
|
else
|
180
|
-
raise "Can't find any setter '#{rev_m}' on '#{v}'"
|
180
|
+
raise "Can't find any setter '#{rev_m}' on '#{v}:#{v.class}'"
|
181
181
|
end
|
182
182
|
else
|
183
183
|
# TODO: should remove this one form the reverse side
|
@@ -489,6 +489,9 @@ module OMF::SFA::Resource
|
|
489
489
|
op.each do |k, v|
|
490
490
|
k = k.to_sym
|
491
491
|
unless (value = send(k)).nil?
|
492
|
+
if value.is_a?(OMF::SFA::Util::Promise) && value.resolved?
|
493
|
+
value = value.value
|
494
|
+
end
|
492
495
|
#puts "OPROPS_TO_HAHS(#{k}): #{value}::#{value.class}--#{oproperty_get(k)}"
|
493
496
|
#puts "OPROPS_TO_HAHS(#{k}): #{opts[:level]} >= #{opts[:max_level]}"
|
494
497
|
if value.is_a? OResource
|
@@ -501,13 +504,19 @@ module OMF::SFA::Resource
|
|
501
504
|
end
|
502
505
|
if value.kind_of? Array
|
503
506
|
next if value.empty?
|
504
|
-
opts = opts.merge(level: opts[:level] + 1)
|
505
507
|
value = value.collect do |e|
|
506
|
-
#
|
507
|
-
|
508
|
+
#puts "LEVLE: #{opts[:level]} - #{opts[:max_level]}"
|
509
|
+
if e.kind_of? OResource
|
510
|
+
if opts[:level] >= opts[:max_level]
|
511
|
+
e.href
|
512
|
+
else
|
513
|
+
e.to_hash(objs, opts.merge(level: opts[:level] + 2))
|
514
|
+
end
|
515
|
+
else
|
516
|
+
e
|
517
|
+
end
|
508
518
|
end
|
509
519
|
end
|
510
|
-
|
511
520
|
h[k] = value
|
512
521
|
end
|
513
522
|
end
|
@@ -14,6 +14,7 @@ module OMF::SFA
|
|
14
14
|
module Base
|
15
15
|
|
16
16
|
SFA_NAMESPACE_URI = "http://www.geni.net/resources/rspec/3"
|
17
|
+
OMF_NAMESPACE_URI = "http://schema.mytestbed.net/rspec/3"
|
17
18
|
|
18
19
|
module ClassMethods
|
19
20
|
|
@@ -62,6 +63,7 @@ module OMF::SFA
|
|
62
63
|
def sfa_add_namespaces_to_document(doc)
|
63
64
|
root = doc.root
|
64
65
|
root.add_namespace(nil, SFA_NAMESPACE_URI)
|
66
|
+
root.add_namespace('omf', OMF_NAMESPACE_URI)
|
65
67
|
@@sfa_namespaces.each do |name, opts|
|
66
68
|
root.add_namespace(name.to_s, opts[:urn]) #'omf', 'http://tenderlovemaking.com')
|
67
69
|
end
|
@@ -478,7 +480,7 @@ module OMF::SFA
|
|
478
480
|
end
|
479
481
|
|
480
482
|
unless opts[:suppress_uuid] || self.class.sfa_suppress_uuid?
|
481
|
-
new_element.set_attribute('uuid', self.uuid) #if detail_level > 0
|
483
|
+
new_element.set_attribute('omf:uuid', self.uuid) #if detail_level > 0
|
482
484
|
end
|
483
485
|
|
484
486
|
#if href = self.href(opts)
|
@@ -12,7 +12,7 @@ module OMF::SFA::Util
|
|
12
12
|
end
|
13
13
|
|
14
14
|
|
15
|
-
# This class handles the
|
15
|
+
# This class handles the conversion between GraphJson
|
16
16
|
# and a set of resources.
|
17
17
|
#
|
18
18
|
# Usage:
|
@@ -25,6 +25,17 @@ module OMF::SFA::Util
|
|
25
25
|
self.new.parse(JSON.parse(content, symbolize_names: true), opts)
|
26
26
|
end
|
27
27
|
|
28
|
+
# Parse a hash following the GraphJSON format and return a
|
29
|
+
# list od resources.
|
30
|
+
#
|
31
|
+
# * opts
|
32
|
+
# - :node_services Services to add to each node (default: [])
|
33
|
+
# - :create_new_uuids Don't use '_id' for UUID, create new ones
|
34
|
+
#
|
35
|
+
def self.parse(descr_hash, opts = {})
|
36
|
+
self.new.parse(descr_hash, opts)
|
37
|
+
end
|
38
|
+
|
28
39
|
# Parse a hash following the GraphJSON format and return a
|
29
40
|
# list od resources.
|
30
41
|
#
|
@@ -68,12 +79,17 @@ module OMF::SFA::Util
|
|
68
79
|
@defaults = {node: {}, interface: {}, network: {}}
|
69
80
|
(graph[:defaults] || {}).each do |type, h|
|
70
81
|
type_def = @defaults[type.to_sym] ||= {}
|
71
|
-
h.each do |name,
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
82
|
+
h.each do |name, vh|
|
83
|
+
puts "DEFAULTS>>> #{type} - #{name} => #{vh}"
|
84
|
+
if ref_id = vh[:ref]
|
85
|
+
unless ref = @id2descr[ref_id]
|
86
|
+
raise GraphJSONException.new "Defaults refer to unknown id '#{id}' - #{@id2descr.keys}"
|
87
|
+
end
|
88
|
+
unless val = ref[name]
|
89
|
+
raise GraphJSONException.new "Defaults refer to unspecified property '#{name}' in '#{ref_id}' - #{ref}"
|
90
|
+
end
|
91
|
+
elsif (val = vh[:value]).nil?
|
92
|
+
raise GraphJSONException.new "Default '#{name}' for '#{type}' has no value defined - #{vh}"
|
77
93
|
end
|
78
94
|
type_def[name] = val
|
79
95
|
#puts "#{type}::#{name} ===> #{val}"
|
@@ -105,7 +121,7 @@ module OMF::SFA::Util
|
|
105
121
|
end
|
106
122
|
opts[:uuid] = id unless @create_new_uuids
|
107
123
|
opts[:name] = node[:name]
|
108
|
-
parse_value(node, :
|
124
|
+
parse_value(node, :am_urn, el_defaults, opts, true, :component_manager)
|
109
125
|
parse_value(node, :urn, el_defaults, opts, false)
|
110
126
|
opts[:sliver_type] = parse_sliver_type(node, el_defaults)
|
111
127
|
|
@@ -145,6 +161,7 @@ module OMF::SFA::Util
|
|
145
161
|
next unless n_descr[:_type] == "node"
|
146
162
|
node_id = n_descr[:_id]
|
147
163
|
if_a = n_descr[:__ifs]
|
164
|
+
next unless if_a
|
148
165
|
|
149
166
|
# Add names to all interfaces
|
150
167
|
names = if_a.map {|ifd| ifd[:name] }.compact
|
@@ -226,23 +243,26 @@ module OMF::SFA::Util
|
|
226
243
|
|
227
244
|
def parse_sliver_type(node, el_defaults)
|
228
245
|
sliver_type = parse_value(node, :sliver_type, el_defaults, nil, true)
|
229
|
-
disk_image_url = parse_value(node, :disk_image, el_defaults, nil,
|
230
|
-
id = "#{sliver_type}-#{disk_image_url}"
|
246
|
+
disk_image_url = parse_value(node, :disk_image, el_defaults, nil, false)
|
247
|
+
id = "#{sliver_type}-#{disk_image_url || '__none'}"
|
231
248
|
unless st_res = @sliver_types[id]
|
232
|
-
|
233
|
-
|
249
|
+
opts = {name: sliver_type}
|
250
|
+
if disk_image_url
|
251
|
+
opts[:disk_image] = DiskImage.create(url: disk_image_url)
|
252
|
+
end
|
253
|
+
st_res = @sliver_types[id] = SliverType.create(opts)
|
234
254
|
end
|
235
255
|
st_res
|
236
256
|
end
|
237
257
|
|
238
258
|
|
239
|
-
def parse_value(el, name, defaults, opts, is_mandatory = false)
|
259
|
+
def parse_value(el, name, defaults, opts, is_mandatory = false, opts_name = nil)
|
240
260
|
val = el[name] || defaults[name]
|
241
261
|
if is_mandatory && val.nil?
|
242
262
|
raise GraphJSONException.new "Can't find value for mandatory property '#{name}' in '#{el}'"
|
243
263
|
end
|
244
264
|
if opts && !val.nil?
|
245
|
-
opts[name.to_sym] = val
|
265
|
+
opts[(opts_name || name).to_sym] = val
|
246
266
|
end
|
247
267
|
val
|
248
268
|
end
|
@@ -0,0 +1,325 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
|
4
|
+
module OMF::SFA
|
5
|
+
module Util
|
6
|
+
class UtilException < Exception; end
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
module OMF::SFA::Util
|
11
|
+
|
12
|
+
class PromiseException < UtilException
|
13
|
+
attr_reader :promise
|
14
|
+
|
15
|
+
def initialize(promise)
|
16
|
+
@promise = promise
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class PromiseUnresolvedException < PromiseException
|
21
|
+
attr_reader :promise, :uuid
|
22
|
+
|
23
|
+
def initialize(promise)
|
24
|
+
@promise = promise
|
25
|
+
end
|
26
|
+
|
27
|
+
def uuid
|
28
|
+
@uuid ||= UUIDTools::UUID.random_create
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class PromiseAlreadySetException < PromiseException; end
|
33
|
+
|
34
|
+
class Promise < OMF::Base::LObject
|
35
|
+
|
36
|
+
# Returns a promise which fires when all dependencies
|
37
|
+
def self.all(*promises)
|
38
|
+
count = promises.length
|
39
|
+
results = []
|
40
|
+
new_promise = self.new()
|
41
|
+
if count == 0
|
42
|
+
new_promise.resolve(results)
|
43
|
+
else
|
44
|
+
already_rejected = false
|
45
|
+
promises.each_with_index do |p, i|
|
46
|
+
p.on_success do |v|
|
47
|
+
results[i] = v
|
48
|
+
if (count -= 1) <= 0
|
49
|
+
new_promise.resolve(results)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
p.on_error do |err_code, msg|
|
53
|
+
unless already_rejected
|
54
|
+
already_rejected = true
|
55
|
+
new_promise.reject(err_code, msg)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
new_promise
|
61
|
+
end
|
62
|
+
|
63
|
+
def value(exception_on_unresolved = PromiseUnresolvedException)
|
64
|
+
if @status == :resolved
|
65
|
+
@value
|
66
|
+
else
|
67
|
+
ex = exception_on_unresolved
|
68
|
+
raise ex.is_a?(PromiseException) ? ex.new(self) : ex
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def error_msg(exception_on_unresolved = PromiseUnresolvedException)
|
73
|
+
if @status == :rejected
|
74
|
+
@value
|
75
|
+
else
|
76
|
+
ex = exception_on_unresolved
|
77
|
+
raise ex.is_a?(PromiseException) ? ex.new(self) : ex
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def error_code(exception_on_unresolved = PromiseUnresolvedException)
|
82
|
+
if @status == :rejected
|
83
|
+
@err_code
|
84
|
+
else
|
85
|
+
ex = exception_on_unresolved
|
86
|
+
raise ex.is_a?(PromiseException) ? ex.new(self) : ex
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Resolve the promise.
|
91
|
+
#
|
92
|
+
# @param [Object] value of promise
|
93
|
+
#
|
94
|
+
def resolve(value)
|
95
|
+
raise PromiseAlreadySetException.new(self) unless @status == :pending
|
96
|
+
|
97
|
+
#puts "--------RESOLVE-#{@name}-#{@status}(#{@resolved_handlers.inspect}>>> "
|
98
|
+
if value.is_a? self.class
|
99
|
+
@status = :proxy
|
100
|
+
@proxy = value
|
101
|
+
@proxy.on_success {|v| _resolve(v)}
|
102
|
+
@proxy.on_error {|e, m| _reject(e, m)}
|
103
|
+
else
|
104
|
+
_resolve(value)
|
105
|
+
end
|
106
|
+
self
|
107
|
+
end
|
108
|
+
|
109
|
+
# Resolve the promise.
|
110
|
+
#
|
111
|
+
# @param [Object] Reject message
|
112
|
+
#
|
113
|
+
def reject(err_code, msg = nil)
|
114
|
+
unless msg
|
115
|
+
# no error code
|
116
|
+
msg = err_code
|
117
|
+
err_code = 999
|
118
|
+
end
|
119
|
+
unless @status == :pending
|
120
|
+
warn "No longer pending - #{self.inspect} - #{@value}"
|
121
|
+
raise PromiseAlreadySetException.new(self)
|
122
|
+
end
|
123
|
+
_reject(err_code, msg)
|
124
|
+
self
|
125
|
+
end
|
126
|
+
|
127
|
+
# Call block to allow modifications to the 'resolve' value
|
128
|
+
#
|
129
|
+
def filter(&block)
|
130
|
+
if @filter_block
|
131
|
+
raise "Can only define one filter block - previous #{@filter_block}"
|
132
|
+
end
|
133
|
+
@filter_block = block
|
134
|
+
if @status == :resolved
|
135
|
+
unless @resolved_handlers.empty?
|
136
|
+
warn "Attached filter after promise was already resolved and reported to 'on_success'"
|
137
|
+
end
|
138
|
+
_resolve(@value)
|
139
|
+
end
|
140
|
+
self
|
141
|
+
end
|
142
|
+
|
143
|
+
# To track the progress towards resolving or rejecting the promise
|
144
|
+
# the various tasks can report on their progress through this method.
|
145
|
+
# Calling it with an empty message will return an array of progress
|
146
|
+
# messages
|
147
|
+
#
|
148
|
+
def progress(msg = nil, timestamp = nil)
|
149
|
+
if msg
|
150
|
+
@progress << [timestamp ||= Time.now, msg]
|
151
|
+
if @progress_handlers
|
152
|
+
_call('on_progress', @progress_handlers, [timestamp, " #{msg}"], false)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
@progress
|
156
|
+
end
|
157
|
+
|
158
|
+
# Register block to call on success, or other promise to
|
159
|
+
# 'upcall' on success.
|
160
|
+
#
|
161
|
+
def on_success(other_promise = nil, &block)
|
162
|
+
if other_promise
|
163
|
+
raise "can't have block as well" if block
|
164
|
+
block = lambda {|v| other_promise.resolve(v) }
|
165
|
+
end
|
166
|
+
if @status == :resolved
|
167
|
+
_call('on_success', [block], [@value])
|
168
|
+
else
|
169
|
+
@resolved_handlers << block
|
170
|
+
end
|
171
|
+
self
|
172
|
+
end
|
173
|
+
|
174
|
+
# Register block to call on error, or other promise to
|
175
|
+
# 'upcall' on error.
|
176
|
+
#
|
177
|
+
def on_error(other_promise = nil, &block)
|
178
|
+
if other_promise
|
179
|
+
raise "can't have block as well" if block
|
180
|
+
block = lambda {|c, m| other_promise.reject(c, m) }
|
181
|
+
end
|
182
|
+
if @status == :rejected
|
183
|
+
_call('on_error', [block], [@err_code, @value])
|
184
|
+
else
|
185
|
+
@rejected_handlers << block
|
186
|
+
end
|
187
|
+
self
|
188
|
+
end
|
189
|
+
|
190
|
+
# Register block to call whenever this promise transitions
|
191
|
+
# out of 'pending'.
|
192
|
+
#
|
193
|
+
def on_always(&block)
|
194
|
+
if @status == :pending
|
195
|
+
@always_handlers << block
|
196
|
+
else
|
197
|
+
_call('on_always', [block], nil, false)
|
198
|
+
end
|
199
|
+
self
|
200
|
+
end
|
201
|
+
|
202
|
+
# Register block to call whenever a new progress message is being reported
|
203
|
+
#
|
204
|
+
def on_progress(other_promise = nil, prefix = nil, &block)
|
205
|
+
if other_promise
|
206
|
+
raise "can't have block as well" if block
|
207
|
+
block = lambda do |ts, m|
|
208
|
+
m = " #{prefix ? "[#{prefix}] " : ''}#{m}"
|
209
|
+
other_promise.progress(m, ts)
|
210
|
+
end
|
211
|
+
end
|
212
|
+
@progress.each do |ts, m|
|
213
|
+
#_call('on_progress', [block], [ts, " #{prefix ? "#{prefix}: " : ''}#{m}"], false)
|
214
|
+
_call('on_progress', [block], [ts, m], false)
|
215
|
+
end
|
216
|
+
(@progress_handlers ||= []) << block
|
217
|
+
self
|
218
|
+
end
|
219
|
+
|
220
|
+
def resolved?
|
221
|
+
(@status == :proxy) ? @proxy.resolved? : (@status == :resolved)
|
222
|
+
end
|
223
|
+
|
224
|
+
def pending?
|
225
|
+
(@status == :proxy) ? @proxy.pending? : (@status == :pending)
|
226
|
+
end
|
227
|
+
|
228
|
+
def status
|
229
|
+
(@status == :proxy) ? @proxy.status : @status
|
230
|
+
end
|
231
|
+
|
232
|
+
def to_html
|
233
|
+
case @status
|
234
|
+
when :pending
|
235
|
+
'.... pending'
|
236
|
+
when :resolved
|
237
|
+
@value.to_s
|
238
|
+
else
|
239
|
+
"ERROR(#{@err_code}: #{@value}"
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
# def to_json(*a)
|
244
|
+
# to_str().to_json(*a)
|
245
|
+
# end
|
246
|
+
|
247
|
+
def to_json(*a)
|
248
|
+
puts ">>> JSONIFY PROMISE - #{self.to_s} - #{@value}"
|
249
|
+
if @status == :resolved
|
250
|
+
@value.to_json(*a)
|
251
|
+
else
|
252
|
+
raise PromiseUnresolvedException.new(self)
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
def to_s
|
257
|
+
"#<#{self.class}:#{@name}-#{@status}>"
|
258
|
+
end
|
259
|
+
|
260
|
+
|
261
|
+
attr_reader :name
|
262
|
+
|
263
|
+
def initialize(name = nil)
|
264
|
+
@pretty_name = name
|
265
|
+
@name = "p#{hash()}"
|
266
|
+
@name += "-#{name}" if name
|
267
|
+
@status = :pending
|
268
|
+
@resolved_handlers = []
|
269
|
+
@rejected_handlers = []
|
270
|
+
@always_handlers = []
|
271
|
+
@progress = []
|
272
|
+
@progress_handlers = nil
|
273
|
+
progress "#{@pretty_name} started" if @pretty_name
|
274
|
+
end
|
275
|
+
|
276
|
+
private
|
277
|
+
|
278
|
+
def _resolve(value)
|
279
|
+
@status = :resolved
|
280
|
+
value = @filter_block.call(value) if @filter_block
|
281
|
+
@value = value
|
282
|
+
progress("#{@pretty_name} resolved") if @pretty_name
|
283
|
+
_call('on_success', @resolved_handlers, [value])
|
284
|
+
value
|
285
|
+
end
|
286
|
+
|
287
|
+
def _reject(err_code, msg)
|
288
|
+
@status = :rejected
|
289
|
+
#puts ">>>> REJECT MSG - #{msg.inspect} -- #{msg.respond_to? :pretty_print}"
|
290
|
+
if msg.respond_to? :pretty_print
|
291
|
+
msg = msg.pretty_print
|
292
|
+
end
|
293
|
+
@value = msg
|
294
|
+
@err_code = err_code
|
295
|
+
progress("#{@pretty_name} rejected") if @pretty_name
|
296
|
+
_call('on_error', @rejected_handlers, [err_code, msg])
|
297
|
+
nil
|
298
|
+
end
|
299
|
+
|
300
|
+
def _call(name, blocks, args, call_always_on_as_well = true)
|
301
|
+
blocks.each do |block|
|
302
|
+
begin
|
303
|
+
block.call(*args)
|
304
|
+
rescue Exception => ex
|
305
|
+
warn "(#{@name}) Exception while calling '#{name}' - #{ex} - #{block} - #{args}"
|
306
|
+
debug ex.backtrace.join("\n\t")
|
307
|
+
if name == 'on_success'
|
308
|
+
#return _call('on_error', @rejected_handlers, [-1, ex])
|
309
|
+
# TODO: Clarify this. Currently we could have multiple
|
310
|
+
# 'on_success' handlers, but the current logic will call
|
311
|
+
# all 'on_error' handles if one 'on_success' throws an
|
312
|
+
# exception and turns this promise into 'rejected'
|
313
|
+
return _reject(-1, ex)
|
314
|
+
end
|
315
|
+
end
|
316
|
+
end
|
317
|
+
if call_always_on_as_well
|
318
|
+
_call('on_always', @always_handlers, nil, false)
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
|
323
|
+
|
324
|
+
end
|
325
|
+
end
|