openwferu-extras 0.9.15 → 0.9.16

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,286 @@
1
+ #
2
+ #--
3
+ # Copyright (c) 2007, John Mettraux, OpenWFE.org
4
+ # All rights reserved.
5
+ #
6
+ # Redistribution and use in source and binary forms, with or without
7
+ # modification, are permitted provided that the following conditions are met:
8
+ #
9
+ # . Redistributions of source code must retain the above copyright notice, this
10
+ # list of conditions and the following disclaimer.
11
+ #
12
+ # . Redistributions in binary form must reproduce the above copyright notice,
13
+ # this list of conditions and the following disclaimer in the documentation
14
+ # and/or other materials provided with the distribution.
15
+ #
16
+ # . Neither the name of the "OpenWFE" nor the names of its contributors may be
17
+ # used to endorse or promote products derived from this software without
18
+ # specific prior written permission.
19
+ #
20
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30
+ # POSSIBILITY OF SUCH DAMAGE.
31
+ #++
32
+ #
33
+
34
+ #
35
+ # "made in Japan"
36
+ #
37
+ # John Mettraux at openwfe.org
38
+ #
39
+
40
+ #
41
+ # this participant requires atom-tools from
42
+ #
43
+ # http://code.necronomicorp.com/trac/atom-tools
44
+ #
45
+ # atom-tools' license is X11/MIT
46
+ #
47
+
48
+ require 'yaml'
49
+ require 'rexml/document'
50
+
51
+ require 'openwfe/participants/participants'
52
+
53
+ require 'rubygems'
54
+
55
+ begin
56
+ require 'atom/entry'
57
+ require 'atom/collection'
58
+ rescue LoadError
59
+ #
60
+ # soft dependency on 'atom-tools'
61
+ #
62
+ puts
63
+ puts
64
+ puts "'atom/collection' is missing. You can install with :"
65
+ puts
66
+ puts " [sudo] gem install atom-tools"
67
+ puts
68
+ puts
69
+ exit 1
70
+ end
71
+
72
+ require 'openwfe/participants/participant'
73
+ require 'openwfe/participants/participants'
74
+
75
+
76
+ module OpenWFE::Extras
77
+
78
+ #
79
+ # This participants posts (as in HTTP POST) a workitem
80
+ # to an AtomPub enabled resource.
81
+ #
82
+ # target_uri = "https://openwferu.wordpress.com/wp-app.php/posts"
83
+ #
84
+ # params = {}
85
+ # params[:username] = 'jmettraux'
86
+ # params[:password] = ENV['WORDPRESS_PASSWORD']
87
+ # params[:categories] = 'openwferu, test'
88
+ #
89
+ # engine.register_participant(
90
+ # "app", OpenWFE::Extras::AtomPubParticipant.new target_uri, params)
91
+ #
92
+ # This base implementation dumps workitem as YAML in the entry content.
93
+ #
94
+ # See BlogParticipant for a human-oriented blog posting participant.
95
+ #
96
+ class AtomPubParticipant
97
+ include OpenWFE::LocalParticipant
98
+
99
+ #
100
+ # The URI to post to
101
+ #
102
+ attr_accessor :target_uri
103
+
104
+ attr_accessor :author_name, :author_uri
105
+
106
+
107
+ def initialize (target_uri, params)
108
+
109
+ @target_uri = target_uri
110
+
111
+ @username = params[:username]
112
+ @password = params[:password]
113
+
114
+ @author_name = \
115
+ params[:author_name] || self.class.name
116
+ @author_uri = \
117
+ params[:author_uri] || "http://openwferu.rubyforge.org"
118
+
119
+
120
+ @categories = params[:categories] || []
121
+ @categories = @categories.split(",") if @categories.is_a?(String)
122
+ @categories = Array(@categories)
123
+ end
124
+
125
+ #
126
+ # The incoming workitem will generate an atom entry that will
127
+ # get posted to the target URI.
128
+ #
129
+ # This consume() method returns the URI (as a String) where the
130
+ # just uploaded post can be edited.
131
+ #
132
+ def consume (workitem)
133
+
134
+ entry = Atom::Entry.new
135
+ entry.updated! # set updated time to now
136
+
137
+ render_author entry, workitem
138
+ render_categories entry, workitem
139
+ render_content entry, workitem
140
+
141
+ h = Atom::HTTP.new
142
+ h.user = @username
143
+ h.pass = @password
144
+ h.always_auth = :basic
145
+
146
+ res = Atom::Collection.new(@target_uri, h).post!(entry)
147
+
148
+ # initial implementation
149
+ # don't catch an error, let the process fail
150
+
151
+ #res.read_body
152
+ extract_new_link res
153
+ end
154
+
155
+ protected
156
+
157
+ #
158
+ # This base implementation simply uses a YAML dump of the workitem
159
+ # as the post content (with a content type of 'html').
160
+ #
161
+ def render_content (entry, workitem)
162
+
163
+ entry.title = \
164
+ workitem.participant_name + " " +
165
+ workitem.fei.expression_id + " " +
166
+ workitem.fei.workflow_instance_id
167
+
168
+ entry.content = workitem.to_yaml
169
+ entry.content["type"] = "html"
170
+ end
171
+
172
+ #
173
+ # This default implementation simply builds a single author
174
+ # out of the :author_name, :author_uri passed as initialization
175
+ # params.
176
+ #
177
+ def render_author (entry, workitem)
178
+
179
+ author = Atom::Author.new
180
+ author.name = author_name
181
+ author.uri = author_uri
182
+
183
+ entry.authors << author
184
+ end
185
+
186
+ #
187
+ # This base implementations simply adds the categories listed
188
+ # in the :categories initialization parameter.
189
+ # The target_uri is used as the scheme for the categories.
190
+ #
191
+ # You can override this method to add extra categories or to
192
+ # have completely different categories.
193
+ #
194
+ def render_categories (entry, workitem)
195
+
196
+ @categories.each do |s|
197
+
198
+ c = Atom::Category.new
199
+
200
+ c["scheme"] = @target_uri
201
+ c["term"] = s.strip
202
+
203
+ entry.categories << c
204
+ end
205
+ end
206
+
207
+ #
208
+ # Extracts the link of the newly created resource (newly posted blog
209
+ # entry), and returns it as a String.
210
+ #
211
+ def extract_new_link (response)
212
+
213
+ doc = REXML::Document.new response.read_body
214
+
215
+ #REXML::XPath.first(doc.root, "//link[@rel='edit']")
216
+ #
217
+ # doesn't work :(
218
+
219
+ REXML::XPath.first(doc.root, "//link[2]").attribute('href')
220
+ #
221
+ # will break if the order changes :(
222
+ end
223
+ end
224
+
225
+ #
226
+ # A participant that blogs.
227
+ #
228
+ # require 'openwfe/extras/participants/atompub_participants'
229
+ # include OpenWFE::Extras
230
+ #
231
+ # target_uri = "https://openwferu.wordpress.com/wp-app.php/posts"
232
+ #
233
+ # params = {}
234
+ # params[:username] = 'jeff'
235
+ # params[:password] = 'whatever'
236
+ #
237
+ # params[:categories] = 'openwferu, test'
238
+ #
239
+ # #params[:title_field] = "title"
240
+ # #
241
+ # # which workitem field will hold the post title ?
242
+ # # by default, it's "title"
243
+ #
244
+ # engine.register_participant "blogger", BlogParticipant.new(target_uri, params) do
245
+ # """
246
+ # paragraph 0
247
+ #
248
+ # paragraph 1 : ${f:message}
249
+ #
250
+ # paragraph 2
251
+ # """
252
+ # end
253
+ #
254
+ # This participant takes its template and the workitem it receives to
255
+ # publish a blog entry.
256
+ #
257
+ # The template can be specified as a block (as in the previous example)
258
+ # or via the :template parameter.
259
+ #
260
+ class BlogParticipant < AtomPubParticipant
261
+ include OpenWFE::TemplateMixin
262
+
263
+ def initialize (target_uri, params, &block)
264
+
265
+ super
266
+
267
+ @template = params[:template]
268
+ @block_template = block
269
+
270
+ @content_type = params[:content_type] || "html"
271
+
272
+ @title_field = params[:title_field] || "title"
273
+ end
274
+
275
+ protected
276
+
277
+ def render_content (entry, workitem)
278
+
279
+ entry.title = workitem.attributes[@title_field].to_s
280
+
281
+ entry.content = eval_template workitem
282
+ entry.content["type"] = @content_type
283
+ end
284
+ end
285
+ end
286
+
@@ -59,15 +59,11 @@ module Extras
59
59
  # An example :
60
60
  #
61
61
  # class TestDefinition0 < ProcessDefinition
62
- # def make
63
- # process_definition :name => "test0", :revision => "0" do
64
- # sequence do
65
- # set :field => "weather", :value => "cloudy"
66
- # set :field => "month", :value => "may"
67
- # decision
68
- # _print "${f:take_umbrella?}"
69
- # end
70
- # end
62
+ # sequence do
63
+ # set :field => "weather", :value => "cloudy"
64
+ # set :field => "month", :value => "may"
65
+ # decision
66
+ # _print "${f:take_umbrella?}"
71
67
  # end
72
68
  # end
73
69
  #
@@ -50,6 +50,63 @@ include OpenWFE
50
50
  module OpenWFE
51
51
  module Extras
52
52
 
53
+ #
54
+ # A regexp for checking if a string is a numeric Ruby range
55
+ #
56
+ RUBY_NUMERIC_RANGE_REGEXP = Regexp.compile(
57
+ "^\\d+(\\.\\d+)?\\.{2,3}\\d+(\\.\\d+)?$")
58
+
59
+ #
60
+ # A regexp for checking if a string is an alpha Ruby range
61
+ #
62
+ RUBY_ALPHA_RANGE_REGEXP = Regexp.compile(
63
+ "^([A-Za-z])(\\.{2,3})([A-Za-z])$")
64
+
65
+ #
66
+ # If the string contains a Ruby range definition
67
+ # (ie something like "93.0..94.5" or "56..72"), it will return
68
+ # the Range instance.
69
+ # Will return nil else.
70
+ #
71
+ # The Ruby range returned (if any) will accept String or Numeric,
72
+ # ie (4..6).include?("5") will yield true.
73
+ #
74
+ def to_ruby_range (s)
75
+
76
+ range = if RUBY_NUMERIC_RANGE_REGEXP.match(s)
77
+
78
+ eval s
79
+ else
80
+
81
+ m = RUBY_ALPHA_RANGE_REGEXP.match(s)
82
+
83
+ if m
84
+ eval "'#{m[1]}'#{m[2]}'#{m[3]}'"
85
+ else
86
+ nil
87
+ end
88
+ end
89
+
90
+ class << range
91
+
92
+ alias :old_include? :include?
93
+
94
+ def include? (elt)
95
+
96
+ elt = if first.is_a?(Numeric)
97
+ Float(elt)
98
+ else
99
+ elt
100
+ end
101
+
102
+ old_include?(elt)
103
+ end
104
+
105
+ end if range
106
+
107
+ range
108
+ end
109
+
53
110
  #
54
111
  # A 'CsvTable' is called a 'decision table' in OpenWFEja (the initial
55
112
  # Java implementation of OpenWFE).
@@ -128,6 +185,15 @@ module Extras
128
185
  # Such comparisons are done after the elements are transformed to float
129
186
  # numbers. By default, non-numeric arguments will get compared as Strings.
130
187
  #
188
+ # Ruby ranges are also accepted in cells.
189
+ #
190
+ # in:f0,out:result
191
+ # ,
192
+ # 0..32,low
193
+ # 33..66,medium
194
+ # 67..100,high
195
+ #
196
+ # will set the field 'result' to 'low' for f0 => 24
131
197
  #
132
198
  # Disclaimer : the decision / CSV table system is no replacement for
133
199
  # full rule engines with forward and backward chaining, RETE implementation
@@ -141,21 +207,32 @@ module Extras
141
207
  #
142
208
  # Currently, two options are supported, "ignorecase" and "through".
143
209
  #
144
- # "ignorecase", if found by the CsvTable will make any match (in the "in"
145
- # columns) case unsensitive.
210
+ # * "ignorecase", if found by the CsvTable will make any match (in the "in"
211
+ # columns) case unsensitive.
212
+ #
213
+ # * "through", will make sure that EVERY row is evaluated and potentially
214
+ # applied. The default behaviour (without "through"), is to stop the
215
+ # evaluation after applying the results of the first matching row.
216
+ #
217
+ # * "accumulate", behaves as with "through" set but instead of overriding
218
+ # values each time a match is found, will gather them in an array.
146
219
  #
147
- # "through", will make sure that EVERY row is evaluated and potentially
148
- # applied. The default behaviour (without "through"), is to stop the
149
- # evaluation after applying the results of the first matching row.
220
+ # accumulate
221
+ # in:f0,out:result
222
+ # ,
223
+ # ,normal
224
+ # >10,large
225
+ # >100,xl
150
226
  #
227
+ # will yield { result => [ 'normal', 'large' ]} for f0 => 56
151
228
  #
152
229
  # CSV Tables are available to workflows as CsvParticipant.
153
230
  #
154
231
  #
155
232
  # See also :
156
233
  #
157
- # http://jmettraux.wordpress.com/2007/02/11/ruby-decision-tables/
158
- # http://rubyforge.org/viewvc/trunk/openwfe-ruby/test/extras/csv_test.rb?root=openwferu&view=co
234
+ # * http://jmettraux.wordpress.com/2007/02/11/ruby-decision-tables/
235
+ # * http://rubyforge.org/viewvc/trunk/openwfe-ruby/test/extras/csv_test.rb?root=openwferu&view=co
159
236
  #
160
237
  class CsvTable
161
238
 
@@ -163,7 +240,8 @@ module Extras
163
240
  :first_match,
164
241
  :ignore_case,
165
242
  :header,
166
- :rows
243
+ :rows,
244
+ :accumulate
167
245
 
168
246
  #
169
247
  # The constructor for CsvTable, you can pass a String, an Array
@@ -174,6 +252,7 @@ module Extras
174
252
 
175
253
  @first_match = true
176
254
  @ignore_case = false
255
+ @accumulate = false
177
256
 
178
257
  @header = nil
179
258
  @rows = []
@@ -201,7 +280,7 @@ module Extras
201
280
  @rows.each do |row|
202
281
 
203
282
  if matches?(row, flow_expression, workitem)
204
- apply(row, flow_expression, workitem)
283
+ apply row, flow_expression, workitem
205
284
  break if @first_match
206
285
  end
207
286
  end
@@ -253,7 +332,7 @@ module Extras
253
332
 
254
333
  def matches? (row, fexp, wi)
255
334
 
256
- return false if empty_row? row
335
+ return false if empty_row?(row)
257
336
 
258
337
  #puts
259
338
  #puts "__row match ?"
@@ -279,9 +358,16 @@ module Extras
279
358
  #puts "__does '#{value}' match '#{cell}' ?"
280
359
 
281
360
  b = if cell[0, 1] == '<' or cell[0, 1] == '>'
282
- numeric_compare(value, cell)
361
+
362
+ numeric_compare value, cell
283
363
  else
284
- regex_compare(value, cell)
364
+
365
+ range = to_ruby_range cell
366
+ if range
367
+ range.include?(value)
368
+ else
369
+ regex_compare value, cell
370
+ end
285
371
  end
286
372
 
287
373
  return false unless b
@@ -326,16 +412,17 @@ module Extras
326
412
  begin
327
413
  return OpenWFE::eval_safely(s, 4)
328
414
  rescue Exception => e
329
- return false
330
415
  end
416
+
417
+ false
331
418
  end
332
419
 
333
420
  def narrow (s)
334
421
  begin
335
422
  return Float(s)
336
423
  rescue Exception => e
337
- return s
338
424
  end
425
+ s
339
426
  end
340
427
 
341
428
  def resolve_in_header (in_header)
@@ -362,6 +449,7 @@ module Extras
362
449
 
363
450
  value = OpenWFE::dosub(value, fexp, wi)
364
451
 
452
+ #puts "___ value.class:#{value.class}"
365
453
  #puts "___ value:'#{value}'"
366
454
  #puts "___ value:'"+value+"'"
367
455
 
@@ -369,6 +457,9 @@ module Extras
369
457
 
370
458
  #puts "___ t:'#{type}' target:'#{target}'"
371
459
 
460
+ value = accumulate_values(type, target, value, wi, fexp) \
461
+ if @accumulate
462
+
372
463
  if type == "v"
373
464
  fexp.set_variable(target, value) if fexp
374
465
  elsif type == "f"
@@ -379,6 +470,42 @@ module Extras
379
470
  end
380
471
  end
381
472
 
473
+ #
474
+ # 'accumulate' is on, this method got called to compute the
475
+ # new value.
476
+ #
477
+ # If it finds nothing in the target field, the new value is
478
+ # value.
479
+ # If there is already a value and its an array, the new value
480
+ # will be current_array + value.
481
+ # Else the two values (current and new) are combined into an array.
482
+ #
483
+ # Sorry, if you want f([ x ], y) -> [[ x ], y]... It's not
484
+ # implemented like that.
485
+ #
486
+ def accumulate_values (type, target, value, workitem, fexp)
487
+
488
+ current_value = case type
489
+ when 'v'
490
+ if fexp
491
+ fexp.lookup_variable target
492
+ else
493
+ nil
494
+ end
495
+ when 'f'
496
+ workitem.lookup_attribute target
497
+ when 'r'
498
+ nil
499
+ end
500
+
501
+ return value unless current_value
502
+
503
+ return current_value + Array(value) \
504
+ if current_value.is_a?(Array)
505
+
506
+ [ current_value, value ]
507
+ end
508
+
382
509
  def parse_header_row (row)
383
510
 
384
511
  row.each_with_index do |cell, icol|
@@ -398,6 +525,12 @@ module Extras
398
525
  next
399
526
  end
400
527
 
528
+ if s == "accumulate"
529
+ @first_match = false
530
+ @accumulate = true
531
+ next
532
+ end
533
+
401
534
  if OpenWFE::starts_with(cell, "in:") or OpenWFE::starts_with(cell, "out:")
402
535
  @header = Header.new unless @header
403
536
  @header.add cell, icol
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.4
3
3
  specification_version: 1
4
4
  name: openwferu-extras
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.9.15
7
- date: 2007-09-24 00:00:00 +09:00
6
+ version: 0.9.16
7
+ date: 2007-11-19 00:00:00 +09:00
8
8
  summary: OpenWFEru extras (sqs, csv, ...)
9
9
  require_paths:
10
10
  - lib
@@ -29,15 +29,23 @@ post_install_message:
29
29
  authors:
30
30
  - John Mettraux
31
31
  files:
32
+ - lib/openwfe/extras/engine
33
+ - lib/openwfe/extras/engine/db_persisted_engine.rb
34
+ - lib/openwfe/extras/expool
35
+ - lib/openwfe/extras/expool/dberrorjournal.rb
36
+ - lib/openwfe/extras/expool/dbexpstorage.rb
32
37
  - lib/openwfe/extras/listeners
33
- - lib/openwfe/extras/participants
34
- - lib/openwfe/extras/util
35
38
  - lib/openwfe/extras/listeners/sqslisteners.rb
39
+ - lib/openwfe/extras/misc
40
+ - lib/openwfe/extras/misc/activityfeed.rb
41
+ - lib/openwfe/extras/participants
36
42
  - lib/openwfe/extras/participants/activeparticipants.rb
37
- - lib/openwfe/extras/participants/atomparticipants.rb
43
+ - lib/openwfe/extras/participants/atomfeed_participants.rb
44
+ - lib/openwfe/extras/participants/atompub_participants.rb
38
45
  - lib/openwfe/extras/participants/csvparticipants.rb
39
46
  - lib/openwfe/extras/participants/sqsparticipants.rb
40
47
  - lib/openwfe/extras/participants/twitterparticipants.rb
48
+ - lib/openwfe/extras/util
41
49
  - lib/openwfe/extras/util/csvtable.rb
42
50
  - lib/openwfe/extras/util/sqs.rb
43
51
  test_files: []