openwferu-extras 0.9.15 → 0.9.16

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.
@@ -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: []