openwferu 0.9.15 → 0.9.16
Sign up to get free protection for your applications and to get access to all the features.
- data/README.txt +4 -2
- data/lib/openwfe/engine/engine.rb +143 -25
- data/lib/openwfe/engine/file_persisted_engine.rb +3 -3
- data/lib/openwfe/expool/errorjournal.rb +48 -25
- data/lib/openwfe/expool/expressionpool.rb +77 -106
- data/lib/openwfe/expool/expstorage.rb +6 -5
- data/lib/openwfe/expool/threadedexpstorage.rb +190 -0
- data/lib/openwfe/expool/yamlexpstorage.rb +5 -150
- data/lib/openwfe/expressions/condition.rb +6 -6
- data/lib/openwfe/expressions/environment.rb +7 -2
- data/lib/openwfe/expressions/expressionmap.rb +14 -0
- data/lib/openwfe/expressions/fe_command.rb +2 -1
- data/lib/openwfe/expressions/fe_concurrence.rb +40 -18
- data/lib/openwfe/expressions/fe_cron.rb +14 -11
- data/lib/openwfe/expressions/fe_cursor.rb +2 -3
- data/lib/openwfe/expressions/fe_define.rb +34 -31
- data/lib/openwfe/expressions/fe_equals.rb +11 -21
- data/lib/openwfe/expressions/fe_filter_definition.rb +0 -2
- data/lib/openwfe/expressions/fe_fqv.rb +1 -3
- data/lib/openwfe/expressions/fe_if.rb +37 -12
- data/lib/openwfe/expressions/fe_iterator.rb +1 -1
- data/lib/openwfe/expressions/fe_listen.rb +147 -28
- data/lib/openwfe/expressions/fe_losfor.rb +13 -1
- data/lib/openwfe/expressions/fe_misc.rb +70 -11
- data/lib/openwfe/expressions/fe_participant.rb +3 -3
- data/lib/openwfe/expressions/fe_reserve.rb +1 -1
- data/lib/openwfe/expressions/fe_save.rb +11 -12
- data/lib/openwfe/expressions/fe_sequence.rb +22 -29
- data/lib/openwfe/expressions/fe_sleep.rb +11 -7
- data/lib/openwfe/expressions/fe_subprocess.rb +24 -10
- data/lib/openwfe/expressions/fe_value.rb +35 -15
- data/lib/openwfe/expressions/fe_when.rb +2 -4
- data/lib/openwfe/expressions/flowexpression.rb +73 -37
- data/lib/openwfe/expressions/merge.rb +2 -4
- data/lib/openwfe/expressions/raw.rb +40 -31
- data/lib/openwfe/expressions/raw_prog.rb +18 -9
- data/lib/openwfe/expressions/raw_xml.rb +1 -8
- data/lib/openwfe/expressions/simplerep.rb +27 -5
- data/lib/openwfe/expressions/time.rb +45 -15
- data/lib/openwfe/expressions/timeout.rb +2 -1
- data/lib/openwfe/expressions/wtemplate.rb +2 -2
- data/lib/openwfe/flowexpressionid.rb +62 -16
- data/lib/openwfe/listeners/listener.rb +28 -37
- data/lib/openwfe/listeners/listeners.rb +1 -1
- data/lib/openwfe/listeners/socketlisteners.rb +7 -15
- data/lib/openwfe/logging.rb +5 -4
- data/lib/openwfe/{rest → orest}/controlclient.rb +3 -5
- data/lib/openwfe/{rest → orest}/definitions.rb +0 -2
- data/lib/openwfe/{rest → orest}/exception.rb +0 -0
- data/lib/openwfe/{rest → orest}/oldrestservlet.rb +1 -1
- data/lib/openwfe/{rest → orest}/osocket.rb +1 -1
- data/lib/openwfe/{rest → orest}/restclient.rb +0 -2
- data/lib/openwfe/orest/workitem.rb +206 -0
- data/lib/openwfe/{rest → orest}/worklistclient.rb +15 -5
- data/lib/openwfe/{rest → orest}/xmlcodec.rb +4 -1
- data/lib/openwfe/participants/enoparticipants.rb +4 -14
- data/lib/openwfe/participants/participantmap.rb +16 -12
- data/lib/openwfe/participants/participants.rb +46 -1
- data/lib/openwfe/participants/socketparticipants.rb +1 -6
- data/lib/openwfe/service.rb +15 -6
- data/lib/openwfe/storage/yamlcustom.rb +3 -0
- data/lib/openwfe/storage/yamlfilestorage.rb +3 -1
- data/lib/openwfe/util/dollar.rb +21 -14
- data/lib/openwfe/util/lru.rb +29 -10
- data/lib/openwfe/util/observable.rb +4 -1
- data/lib/openwfe/util/otime.rb +3 -0
- data/lib/openwfe/util/scheduler.rb +346 -114
- data/lib/openwfe/utils.rb +67 -13
- data/lib/openwfe/version.rb +1 -1
- data/lib/openwfe/workitem.rb +22 -165
- data/lib/openwfe/worklist/oldrest.rb +2 -2
- data/test/bm/bm_1_xml_vs_prog.rb +56 -0
- data/test/{ft_26_load.rb → bm/ft_26_load.rb} +0 -0
- data/test/{ft_26b_load.rb → bm/ft_26b_load.rb} +0 -0
- data/test/{ft_26c_load.rb → bm/ft_26c_load.rb} +16 -4
- data/test/clone_test.rb +62 -0
- data/test/cron_test.rb +56 -1
- data/test/cronline_test.rb +17 -8
- data/test/description_test.rb +57 -0
- data/test/dollar_test.rb +17 -6
- data/test/eno_test.rb +22 -9
- data/test/fe_lookup_att_test.rb +50 -0
- data/test/fei_test.rb +18 -9
- data/test/flowtestbase.rb +24 -2
- data/test/ft_0.rb +10 -12
- data/test/ft_0e_multibody.rb +34 -0
- data/test/ft_10_loop.rb +4 -6
- data/test/ft_11_ppd.rb +5 -20
- data/test/ft_14b_subprocess.rb +2 -2
- data/test/ft_15_iterator.rb +56 -4
- data/test/ft_15b_iterator.rb +48 -0
- data/test/ft_16_fqv.rb +18 -3
- data/test/ft_23c_wait.rb +7 -5
- data/test/ft_25_cancel.rb +5 -3
- data/test/ft_27_getflowpos.rb +14 -11
- data/test/ft_28_fileparticipant.rb +3 -4
- data/test/ft_2_concurrence.rb +8 -12
- data/test/ft_2b_concurrence.rb +3 -2
- data/test/ft_30_socketlistener.rb +5 -6
- data/test/ft_32c_journal.rb +2 -2
- data/test/ft_32d_journal.rb +5 -6
- data/test/ft_33_description.rb +8 -3
- data/test/ft_34_cancelwfid.rb +3 -3
- data/test/ft_38_tag.rb +7 -10
- data/test/ft_39_reserve.rb +4 -2
- data/test/ft_3_equals.rb +18 -13
- data/test/ft_44b_restore.rb +2 -6
- data/test/ft_49_condition.rb +16 -12
- data/test/ft_4_misc.rb +51 -12
- data/test/ft_50_xml_attribute.rb +1 -1
- data/test/ft_54_listen.rb +96 -10
- data/test/ft_54b_listen.rb +68 -0
- data/test/ft_55_ptimeout.rb +1 -2
- data/test/ft_58_ejournal.rb +8 -33
- data/test/ft_59_ps.rb +2 -3
- data/test/ft_59b_ps_for_pat.rb +59 -0
- data/test/ft_5_time.rb +45 -4
- data/test/ft_60_ecancel.rb +7 -7
- data/test/ft_61_elsub.rb +1 -1
- data/test/ft_64_alias.rb +1 -1
- data/test/ft_67_schedlaunch.rb +29 -16
- data/test/ft_69_cancelmissing.rb +1 -1
- data/test/ft_6_lambda.rb +8 -6
- data/test/ft_70_lookupvar.rb +2 -2
- data/test/ft_71_log.rb +60 -0
- data/test/ft_72_lookup_processes.rb +79 -0
- data/test/ft_73_cancel_sub.rb +144 -0
- data/test/ft_74_block_and_workitem_dup.rb +63 -0
- data/test/ft_75_ruby_attributes.rb +87 -0
- data/test/ft_76_merge_isolate.rb +90 -0
- data/test/ft_7_lose.rb +2 -1
- data/test/ft_tests.rb +9 -0
- data/test/lookup_att_test.rb +90 -0
- data/test/misc_test.rb +33 -50
- data/test/orest_test.rb +1 -1
- data/test/participant_test.rb +32 -8
- data/test/pending.rb +6 -7
- data/test/rake_ltest.rb +3 -0
- data/test/rake_qtest.rb +4 -1
- data/test/raw_prog_test.rb +1 -1
- data/test/restart_cron_test.rb +6 -6
- data/test/restart_sleep_test.rb +8 -8
- data/test/ruby_procdef_test.rb +2 -2
- data/test/rutest_utils.rb +9 -3
- data/test/scheduler_1_test.rb +88 -0
- data/test/scheduler_test.rb +49 -4
- data/test/sec_test.rb +18 -11
- metadata +51 -34
- data/test/cron_test_2.rb +0 -50
@@ -40,6 +40,7 @@
|
|
40
40
|
require 'yaml'
|
41
41
|
|
42
42
|
require 'openwfe/utils'
|
43
|
+
require 'openwfe/util/dollar'
|
43
44
|
require 'openwfe/participants/participant'
|
44
45
|
|
45
46
|
|
@@ -169,7 +170,8 @@ module OpenWFE
|
|
169
170
|
|
170
171
|
result = call_block @block, workitem
|
171
172
|
|
172
|
-
workitem.set_result(result)
|
173
|
+
workitem.set_result(result) \
|
174
|
+
if result and result != workitem
|
173
175
|
|
174
176
|
reply_to_engine(workitem) \
|
175
177
|
if workitem.kind_of? InFlowWorkItem
|
@@ -225,6 +227,9 @@ module OpenWFE
|
|
225
227
|
# The NoOperationParticipant immediately replies to the engine upon
|
226
228
|
# receiving a workitem.
|
227
229
|
#
|
230
|
+
# Is used in testing. Could also be useful during the 'development'
|
231
|
+
# phase of a business process, as an empty placeholder.
|
232
|
+
#
|
228
233
|
class NoOperationParticipant
|
229
234
|
include LocalParticipant
|
230
235
|
|
@@ -232,6 +237,7 @@ module OpenWFE
|
|
232
237
|
# Simply discards the incoming workitem
|
233
238
|
#
|
234
239
|
def consume (workitem)
|
240
|
+
|
235
241
|
reply_to_engine workitem
|
236
242
|
end
|
237
243
|
end
|
@@ -351,5 +357,44 @@ module OpenWFE
|
|
351
357
|
end
|
352
358
|
end
|
353
359
|
|
360
|
+
#
|
361
|
+
# This mixin provides an eval_template() method. This method assumes
|
362
|
+
# the target class has a @block_template and a @template, it also
|
363
|
+
# assumes the class includes the module LocalParticipant.
|
364
|
+
#
|
365
|
+
# This mixin is used for example in the MailParticipant class.
|
366
|
+
#
|
367
|
+
module TemplateMixin
|
368
|
+
|
369
|
+
#
|
370
|
+
# Given a workitem, expands the template and returns it as a String.
|
371
|
+
#
|
372
|
+
def eval_template (workitem)
|
373
|
+
|
374
|
+
fe = get_flow_expression workitem
|
375
|
+
|
376
|
+
template = if @block_template
|
377
|
+
|
378
|
+
call_block @block_template, workitem
|
379
|
+
|
380
|
+
elsif @template
|
381
|
+
|
382
|
+
template = if @template.kind_of?(File)
|
383
|
+
@template.readlines
|
384
|
+
else
|
385
|
+
@template.to_s
|
386
|
+
end
|
387
|
+
|
388
|
+
else
|
389
|
+
|
390
|
+
nil
|
391
|
+
end
|
392
|
+
|
393
|
+
return "(no template given)" unless template
|
394
|
+
|
395
|
+
OpenWFE::dosub template, fe, workitem
|
396
|
+
end
|
397
|
+
end
|
398
|
+
|
354
399
|
end
|
355
400
|
|
@@ -30,8 +30,6 @@
|
|
30
30
|
# POSSIBILITY OF SUCH DAMAGE.
|
31
31
|
#++
|
32
32
|
#
|
33
|
-
# $Id: definitions.rb 2725 2006-06-02 13:26:32Z jmettraux $
|
34
|
-
#
|
35
33
|
|
36
34
|
#
|
37
35
|
# "made in Japan"
|
@@ -43,13 +41,10 @@ require 'yaml'
|
|
43
41
|
require 'socket'
|
44
42
|
require 'timeout'
|
45
43
|
|
46
|
-
require 'openwfe/
|
44
|
+
require 'openwfe/orest/xmlcodec'
|
47
45
|
require 'openwfe/participants/participant'
|
48
46
|
|
49
47
|
|
50
|
-
#
|
51
|
-
# some base participant implementations
|
52
|
-
#
|
53
48
|
module OpenWFE
|
54
49
|
|
55
50
|
#
|
data/lib/openwfe/service.rb
CHANGED
@@ -30,8 +30,6 @@
|
|
30
30
|
# POSSIBILITY OF SUCH DAMAGE.
|
31
31
|
#++
|
32
32
|
#
|
33
|
-
# $Id: definitions.rb 2725 2006-06-02 13:26:32Z jmettraux $
|
34
|
-
#
|
35
33
|
|
36
34
|
#
|
37
35
|
# "made in Japan"
|
@@ -45,11 +43,18 @@ require 'openwfe/contextual'
|
|
45
43
|
|
46
44
|
module OpenWFE
|
47
45
|
|
46
|
+
#
|
47
|
+
# Most of the functionalities of an OpenWFEru service are implemented
|
48
|
+
# here as a mixin. It's then easy to either extend Service or include
|
49
|
+
# ServiceMixin, to compose an OpenWFEru service class.
|
50
|
+
#
|
48
51
|
module ServiceMixin
|
49
52
|
include Contextual, Logging
|
50
53
|
|
51
|
-
|
52
|
-
|
54
|
+
#
|
55
|
+
# The name of the service
|
56
|
+
#
|
57
|
+
attr_accessor :service_name
|
53
58
|
|
54
59
|
#
|
55
60
|
# Inits the service by setting the service name and the application
|
@@ -74,14 +79,18 @@ module OpenWFE
|
|
74
79
|
end
|
75
80
|
end
|
76
81
|
|
82
|
+
#
|
83
|
+
# A service has a service_name and a link to its application_context.
|
84
|
+
# Via the application_context, it can lookup other services (if it
|
85
|
+
# knows their names).
|
86
|
+
#
|
77
87
|
class Service
|
78
88
|
include ServiceMixin
|
79
89
|
|
80
90
|
def initialize (service_name, application_context)
|
81
91
|
|
82
|
-
service_init
|
92
|
+
service_init service_name, application_context
|
83
93
|
end
|
84
|
-
|
85
94
|
end
|
86
95
|
|
87
96
|
#def register (service)
|
@@ -82,6 +82,7 @@ module OpenWFE
|
|
82
82
|
#end
|
83
83
|
|
84
84
|
def to_yaml (opts={})
|
85
|
+
|
85
86
|
YAML::quick_emit(self.object_id, opts) do |out|
|
86
87
|
out.map(taguri) do |map|
|
87
88
|
map.add("s", to_s)
|
@@ -90,6 +91,7 @@ module OpenWFE
|
|
90
91
|
end
|
91
92
|
|
92
93
|
def FlowExpressionId.yaml_new (klass, tag, val)
|
94
|
+
|
93
95
|
s = val["s"]
|
94
96
|
begin
|
95
97
|
FlowExpressionId.to_fei(s)
|
@@ -103,6 +105,7 @@ module OpenWFE
|
|
103
105
|
# opening for tuning yaml persistence
|
104
106
|
#
|
105
107
|
class XmlRawExpression
|
108
|
+
|
106
109
|
def to_yaml_properties
|
107
110
|
l = super()
|
108
111
|
l.delete("@raw_representation")
|
@@ -84,6 +84,8 @@ module OpenWFE
|
|
84
84
|
def []= (fei, object)
|
85
85
|
synchronize do
|
86
86
|
|
87
|
+
#linfo { "[]= #{fei}" }
|
88
|
+
|
87
89
|
fei_path = compute_file_path(fei)
|
88
90
|
|
89
91
|
fei_parent_path = File.dirname(fei_path)
|
@@ -195,7 +197,7 @@ module OpenWFE
|
|
195
197
|
#
|
196
198
|
def each_object_path (&block)
|
197
199
|
|
198
|
-
return unless block
|
200
|
+
#return unless block
|
199
201
|
|
200
202
|
synchronize do
|
201
203
|
Find.find(@basepath) do |path|
|
data/lib/openwfe/util/dollar.rb
CHANGED
@@ -58,6 +58,8 @@ module OpenWFE
|
|
58
58
|
#
|
59
59
|
def OpenWFE.dsub (text, dict)
|
60
60
|
|
61
|
+
text = text.to_s
|
62
|
+
|
61
63
|
#puts "### text is >#{text}<"
|
62
64
|
#puts "### dict is of class #{dict.class.name}"
|
63
65
|
|
@@ -89,13 +91,13 @@ module OpenWFE
|
|
89
91
|
|
90
92
|
#puts "### value 0 is '#{value}'"
|
91
93
|
|
92
|
-
if value
|
93
|
-
value
|
94
|
+
value = if value
|
95
|
+
value.to_s
|
94
96
|
else
|
95
|
-
if dict.has_key?
|
96
|
-
|
97
|
+
if dict.has_key?(key)
|
98
|
+
"false"
|
97
99
|
else
|
98
|
-
|
100
|
+
""
|
99
101
|
end
|
100
102
|
end
|
101
103
|
|
@@ -104,16 +106,17 @@ module OpenWFE
|
|
104
106
|
#puts "pre is >#{text[0..i-1]}<"
|
105
107
|
#puts "post is >#{text[j+1..-1]}<"
|
106
108
|
|
107
|
-
pre =
|
108
|
-
|
109
|
-
|
109
|
+
pre = if i > 0
|
110
|
+
text[0..i-1]
|
111
|
+
else
|
112
|
+
""
|
110
113
|
end
|
111
114
|
|
112
|
-
|
115
|
+
dsub("#{pre}#{value}#{text[j+1..-1]}", dict)
|
113
116
|
end
|
114
117
|
|
115
118
|
def OpenWFE.unescape (text)
|
116
|
-
|
119
|
+
text.gsub("\\\\\\$\\{", "\\${")
|
117
120
|
end
|
118
121
|
|
119
122
|
#
|
@@ -121,9 +124,13 @@ module OpenWFE
|
|
121
124
|
# a flow expression and a workitem (fields and variables).
|
122
125
|
#
|
123
126
|
def OpenWFE.dosub (text, flow_expression, workitem)
|
124
|
-
|
127
|
+
dsub(text, FlowDict.new(flow_expression, workitem))
|
125
128
|
end
|
126
129
|
|
130
|
+
#
|
131
|
+
# Wrapping a process expression and the current workitem as a
|
132
|
+
# Hash object ready for lookup at substitution time.
|
133
|
+
#
|
127
134
|
class FlowDict < Hash
|
128
135
|
|
129
136
|
def initialize (flow_expression, workitem)
|
@@ -149,7 +156,7 @@ module OpenWFE
|
|
149
156
|
return call_ruby(k) if p == 'r'
|
150
157
|
# TODO : implement constant lookup
|
151
158
|
|
152
|
-
|
159
|
+
@workitem.lookup_attribute(key)
|
153
160
|
end
|
154
161
|
|
155
162
|
def has_key? (key)
|
@@ -168,7 +175,7 @@ module OpenWFE
|
|
168
175
|
return true if p == 'r'
|
169
176
|
# TODO : implement constant lookup
|
170
177
|
|
171
|
-
|
178
|
+
@workitem.has_attribute?(key)
|
172
179
|
end
|
173
180
|
|
174
181
|
protected
|
@@ -176,7 +183,7 @@ module OpenWFE
|
|
176
183
|
def extract_prefix (key)
|
177
184
|
i = key.index(':')
|
178
185
|
return 'v', key if not i
|
179
|
-
|
186
|
+
[ key[0..0], key[i+1..-1] ]
|
180
187
|
end
|
181
188
|
|
182
189
|
def call_function (function_name)
|
data/lib/openwfe/util/lru.rb
CHANGED
@@ -64,33 +64,41 @@ module OpenWFE
|
|
64
64
|
end
|
65
65
|
|
66
66
|
def maxsize= (newsize)
|
67
|
+
|
67
68
|
remove_lru() while @hash.size > newsize
|
68
69
|
@maxsize = newsize
|
69
70
|
end
|
70
71
|
|
71
72
|
def maxsize
|
72
|
-
|
73
|
+
|
74
|
+
@maxsize
|
73
75
|
end
|
74
76
|
|
75
77
|
def size
|
76
|
-
|
78
|
+
@lru_keys.size
|
77
79
|
end
|
78
80
|
alias :length :size
|
79
81
|
|
80
82
|
def keys
|
81
|
-
|
83
|
+
|
84
|
+
@hash.keys
|
82
85
|
end
|
86
|
+
|
83
87
|
def values
|
84
|
-
|
88
|
+
|
89
|
+
@hash.values
|
85
90
|
end
|
86
91
|
|
87
92
|
def clear
|
93
|
+
|
88
94
|
@hash.clear
|
89
95
|
@lru_keys.clear
|
90
96
|
end
|
91
97
|
|
92
98
|
def each (&block)
|
99
|
+
|
93
100
|
return unless block
|
101
|
+
|
94
102
|
@hash.each do |k, v|
|
95
103
|
block.call(k, v)
|
96
104
|
end
|
@@ -100,31 +108,39 @@ module OpenWFE
|
|
100
108
|
# Returns the keys with the lru in front.
|
101
109
|
#
|
102
110
|
def ordered_keys
|
103
|
-
|
111
|
+
|
112
|
+
@lru_keys
|
104
113
|
end
|
105
114
|
|
106
115
|
def [] (key)
|
116
|
+
|
107
117
|
value = @hash[key]
|
108
118
|
return nil unless value
|
109
119
|
touch(key)
|
110
|
-
|
120
|
+
|
121
|
+
value
|
111
122
|
end
|
112
123
|
|
113
124
|
def []= (key, value)
|
125
|
+
|
114
126
|
remove_lru() while @hash.size >= @maxsize
|
115
127
|
@hash[key] = value
|
116
128
|
touch(key)
|
117
|
-
|
129
|
+
|
130
|
+
value
|
118
131
|
end
|
119
132
|
|
120
133
|
def delete (key)
|
134
|
+
|
121
135
|
value = @hash.delete(key)
|
122
136
|
@lru_keys.delete(key)
|
123
|
-
|
137
|
+
|
138
|
+
value
|
124
139
|
end
|
125
140
|
|
126
141
|
def include? (key)
|
127
|
-
|
142
|
+
|
143
|
+
@hash.include?(key)
|
128
144
|
end
|
129
145
|
|
130
146
|
protected
|
@@ -134,6 +150,7 @@ module OpenWFE
|
|
134
150
|
# The bottom being the lru place.
|
135
151
|
#
|
136
152
|
def touch (key)
|
153
|
+
|
137
154
|
@lru_keys.delete(key)
|
138
155
|
@lru_keys << key
|
139
156
|
end
|
@@ -143,9 +160,11 @@ module OpenWFE
|
|
143
160
|
# Returns nil if the cache is currently empty.
|
144
161
|
#
|
145
162
|
def remove_lru ()
|
163
|
+
|
146
164
|
return nil if @lru_keys.size < 1
|
147
165
|
key = @lru_keys.delete_at(0)
|
148
|
-
|
166
|
+
|
167
|
+
@hash.delete(key)
|
149
168
|
end
|
150
169
|
end
|
151
170
|
end
|
@@ -57,6 +57,7 @@ module OpenWFE
|
|
57
57
|
# be useful when removing the observer.
|
58
58
|
#
|
59
59
|
def add_observer (channel, observer=nil, &callback)
|
60
|
+
|
60
61
|
observer = callback unless observer
|
61
62
|
(@observers[channel] ||= []) << observer
|
62
63
|
observer
|
@@ -105,6 +106,8 @@ module OpenWFE
|
|
105
106
|
|
106
107
|
def do_notify (target_channel, channel, *args)
|
107
108
|
|
109
|
+
#ldebug { "do_notify() @observers.size is #{@observers.size}" }
|
110
|
+
|
108
111
|
if target_channel.is_a?(String)
|
109
112
|
|
110
113
|
observers = []
|
@@ -132,7 +135,7 @@ module OpenWFE
|
|
132
135
|
obs.call channel, *args
|
133
136
|
end
|
134
137
|
|
135
|
-
observers.size > 0
|
138
|
+
(observers.size > 0)
|
136
139
|
#
|
137
140
|
# returns true if at least one observer was called
|
138
141
|
end
|