Hello Raphael, welcome back.
Back a million year ago, when ruote was OpenWFE, a Java workflow engine, participants could be tied to filters. Those filters controlled which fields went to the real participant and then handled the merge when the workitem came back.
When I moved to Ruby, I wanted (maybe still want (https://github.com/jmettraux/ruote/blob/7675a47da31485fffd6d160ab2f5592a08a0affe/TODO.txt#L290-291)) to have this feature in ruote, but I realized people built very smart participant implementations, very quickly (it's Ruby), and they all had slightly different needs.
Now I realize you want something a bit different, in fact you want this filtering but you also want the "subprocess signature enforcement", am I right ?
> so to summarize:
>
> 1. How does one validate workitem fields in ruote (e.g. the initial workitem
> fields)?
Ah validation at launch time... I've been resisting this feature for a while :
http://groups.google.com/group/openwferu-users/browse_thread/thread/4ea83ccfbc1caa87
http://groups.google.com/group/openwferu-users/msg/8504cf9e065a4607
I guess the launch call would immediately fail. Patrice, in that thread, really wants the process to fail immediately (not asynchronously).
What is your take ?
> 2. How would a sub-process define the fields it expects and how would the
> parent process do the mapping?
Via a participant is right.
What happens when the expected fields are not here ? Error ?
> I could be that I'm completely missing the 'philosophy' being ruote, please
> let me know if that's the case...
The philosphy on that, is more like trying to delay the implementation decision until it's really needed.
So let's look at the current 'way' :
---8<---
Ruote.define do
define 'y' do
filterTwoIn
participany 'z'
filterTwoOut
end
sequence do
filterOneIn
participant 'x'
subprocess 'y'
filterOneOut
end
end
--->8---
Quite verbose.
One thing we could do is introduce a :filter attribute (on any expression), that would trigger a participant upon applying an yet another upon leaving (replying to the engine).
---8<---
sequence :filter => 'one' do
# ...
end
class LevelOneFilter
def consume(workitem)
# filter (and keep original)
end
def on_reply(workitem)
# unfilter (merge incoming and original)
end
end
engine.register_participant 'one', LevelOneFilter
--->8---
Maybe it's "perverting" participants a bit too much, but it could shorten our process definitions :
---8<---
Ruote.define do
define 'y' do
participany 'z', :filter => 'two'
end
sequence :filter => 'one' do
participant 'x'
subprocess 'y'
end
end
--->8---
What do you think ?
Best regards,
--
John Mettraux - http://jmettraux.wordpress.com
--
you received this message because you are subscribed to the "ruote users" group.
to post : send email to openwfe...@googlegroups.com
to unsubscribe : send email to openwferu-use...@googlegroups.com
more options : http://groups.google.com/group/openwferu-users?hl=en
Hello Raphael,
In that case, the check_and_launch approach I describe is handy, but it's not applicable to subprocesses.
> > Via a participant is right.
> > What happens when the expected fields are not here ? Error ?
>
> Yes, it's a contract violation.
OK.
> The filtering mechanism you describe could help 'reuse' the contract
> definitions for multiple sub-processes but we would want the definition to
> be explicit in the workflow rather than embedded in a participant.
what should it look like ?
---8<---
define 'x' do
signature do
server_id :required => true, :type => String
#...
end
# body...
end
--->8---
?
> The filtering would give some of the scoping too. I'm wondering: where would
> the fields "filtered out" be stored so that they can be merged back after the
> participant replies?
Each expression stores the workitem ('applied_workitem' field) upon getting applied, so the storage already occurs.
So we have filters and signatures... Filters have a scope, signatures behave more like checkpoints.
I think I'd like to implement the filter feature. The signature one needs refining.
Cheers,
Hello Raphael,
in this example, shouldn't the filter be replaced with a signature ? (ie defined in the subprocess ?)
> where anything not explicitly listed in the filters list is not given to the
> subprocess (in) or merged back into the workitem (out).
>
> I guess if you provide the ability to filter using a participant this could
> then be implemented doing something like:
>
> ---8<---
> mappings :input => ...., :output => ...
> a_subprocess :filter => :mappings
> --->8---
>
> with the right implementation for the mappings participant. Maybe the former
> syntax then just becomes a shortcut for the later.
I like the inlining idea.
I'd like to use :filter (and :signature maybe) both with participants and { :in => [], :out => [] } one-liners.
Ruote will lookup the name given to it, it could map to a participant, or a process variable or an engine variable (for filters/signatures shared by the whole engine).
Participants are powerful because you have the whole Ruby power at your disposal for filtering/merging/enforcing.
Inlines are quick are limited to a process instance (except when they're bound in engine variables).
One thing I don't like about the DSL behind the inlines and theFieldsValidationParticipant is that it's Ruby-bound. I'd prefer to limit ourselves to the JSON schema (sorry no DateTime or Hash<Hash<String, String>, Fixnum> recursive fun). I don't want to tie the process definitions to Ruby.
I'd like to avoid implementing (and/or maintaining) a complete type system in ruote.
I will look at the expanded version of the "inlines".
Give me a bit of time to come up with something we can refine further.
We also have to be clear and differentiate transformations from validations.
:type => 'String', :default => 'run'
is a transformation, while
:type => 'Array<String>', :required => true
is a validation.
Sorry if I drag you in too general a discussion, but I'd like to discuss that with sufficient altitude in the point of view.
Understood. Transformation it seems.
> > I'd like to use :filter (and :signature maybe) both with participants and {
> > :in => [], :out => [] } one-liners.
>
> I'm missing something: how would you use :filter with :in, :out ?
For now, I'm thinking something like :
---8<---
engine.variables['filter1'] = { :in => [], :out => [] }
# ...
Ruote.define do
x :filter => { :in => [], :out => [] }
set 'v:filter0' => { :in => [], :out => [] }
x :filter => 'filter0'
x :filter => 'filter1'
x :filter => 'participant_name'
end
--->8---
> > One thing I don't like about the DSL behind the inlines and
> > theFieldsValidationParticipant is >that it's Ruby-bound. I'd prefer to limit
> > ourselves to the JSON schema (sorry no DateTime or >Hash<Hash<String,
> > String>, Fixnum> recursive fun). I don't want to tie the process definitions
> > to Ruby.
> >
> > I'd like to avoid implementing (and/or maintaining) a complete type system
> > in ruote.
>
> That makes a lot of sense, I wasn't sure where to set the limit but thinking
> in terms of JSON helps a lot.
Maybe we could have a look at things like JSONpath or equivalents.
> > We also have to be clear and differentiate transformations from
> > validations.
> >
> > :type => 'String', :default => 'run'
> >
> > is a transformation, while
> >
> > :type => 'Array<String>', :required => true
> >
> > is a validation.
>
> I am not sure it's that clear, the point being that if a field has a default
> value then it doesn't matter that it is required. So a field is either
> required (and it's an error if the value is missing) or has a default value
> (used if the value is missing). But you're right this wasn't meant to be
> pure validation (so the name isn't great), this was more meant to describe
> what the interface of the sub-process is.
Understood.
Best regards,
Hello Raphael,
I've started work on the 'filter' :
https://github.com/jmettraux/ruote/commit/afc772cbe022a617a7846615aeddab1af016d847
For now, a filter looks like :
[
{ 'field' => 'x', 'default' => 1 },
{ 'field' => 'y', 'remove' => true }
]
I was wondering about an alternate (transposed) variant for the filters :
{
'require' => [ 'x', 'y', 'z' ],
'remove' => [ 'a', 'b' ],
'copy' => { 'c' => 'd', 'e' => 'f' },
'default' => { 'g' => {}, 'h' => "twelve" }
}
The two formats could coexist (though since order matters, we couldn't have strict equality...). Well, for now I'm going with the first version (list of rules).
I think I'll go on with mixing validation and transformation. It's OK. When I look at your filter at
https://gist.github.com/799298
I wonder if I should use a field_error like you do and place the process instance in error or simply raise an error on the first error encountered when filtering.
I also found
http://json-schema.org/
http://tools.ietf.org/html/draft-zyp-json-schema-03
and two ruby implementations
https://github.com/Constellation/ruby-jsonchema
https://github.com/hoxworth/json-schema (active)
Looking for it, at least as an inspiration, for the validation aspect of filters. (of course, your gist is in as well).
Cheers,
it's progressing well.
To get an overview of the filtering (validations + transformations) capabilities :
For now, the filter are only "one-way", via the 'filter' expression :
I will start working on the :filter attribute discussed in this thread soon.
Comments are welcome, especially about validation errors stacking and so.
Hello,
just pushed a change that lets you do things like
filter :fields => "a, b, c", :type => 'string'
filter :fields => %w[ a b c ], :type => 'string'
filter :fields => '/^user_/', :type => 'string'
filter :fields => '/^x_(.+)$/', :copy_to => 'y_\1'
...
https://github.com/jmettraux/ruote/commit/f496d53ba0962c3f3048b09b5015e19fe07cc928
Best regards,
Hello Raphael,
> Stepping back a little though there is a little bit of confusion in my mind
> about what this does in relation to what we had discussed. Specifically what
> I had understood what that you were going to work on a way to filter and/or
> transform the workitem fields being "given to" and/or "retrieved from" a
> participant. That is a participant would have an implicit contract/interface
> and the filters could be used to "force" the workitem that it receives to
> abide that contract. So you could have a filter that does:
>
> some_participant :filter => { :in => { 'global_field' => 'a_field' }, :out
> => { 'a_field' => 'a_result' } }
>
> This would have filtered out all workitem fields other than 'global_field'
> from the workitem that 'some_participant' consumes and would have renamed
> the 'global_field' workitem field to 'a_field' as far as 'some_participant'
> is concerned. Once 'some_participant' replies the initial workitem fields
> would be "restored" and the workitem field 'a_field' would now be named
> 'a_result' for the next expression to pick up.
For now, it's limited to
---8<---
sequence do
filter :field => 'global_field', :move_to => 'a_field'
some_participant
filter :field => 'a_field', :move_to => 'a_result'
end
--->8---
I haven't had the time to implement the :filter as an attribute now, I only did the 'filter' as an expression.
http://groups.google.com/group/openwferu-users/msg/de967598edea5b5d
---8<---
some_participant
:in => { :field => 'global_field', :move_to => 'a_field' },
:out => { :field => 'a_field', :move_to => 'a_result' }
# or
some_participant
:in => { :f => 'global_field', :mv_to => 'a_field' },
:out => { :f => 'a_field', :mv_to => 'a_result' }
--->8---
> Validation would have been nice to be built in but could have been done
> through participants (like my weak attempt with the
> FieldDefinitionsParticipant). However it seems that you went the other route
> though were filters really only validate but don't filter nor transform at
> the moment.
filtering :
transform :
I also not had the time to document transformations via the 'filter' expression yet :
> While it's great to have validation built-in into Ruote I can't
> help but think that this should not be the job of filters, maybe a validate
> expression would be more appropriate.
'validate' vs 'filter', makes sense. I mixed both to be able to do things like :
('and' and 'or')
> Obviously I could be completely off-base and miss the grand vision behind
> the current design :) Anyway I would appreciate it if you could share some
> of the rationale behind the current choices you made and where actual
> filtering and transforming fit.
I have the impression I'm not too far off the mark, I just need some time to do the :filter attribute (now that I have the filter mecha shared by the filter expression).
Wdyt ?
Hello Raphael,
sorry for the late reply : the :filter attribute is in.
It only understands filter passed via variables for now (I'll add filter participants) later.
Please tell me if it fits your bill and how I could improve it.
> I also like the fact that the filter mechanism is easy to use (via
> Ruote::Filter) since I'm thinking our participants will use this directly to
> validate the incoming workitem (don't know if it's overkill but at some
> level this is nicely independent of everything else and could even live in
> its own gem, rufus-validate anyone?)
+1 for using Ruote::Filter from your participants.
rufus-validate : why not, thinking about it, maybe after I release ruote 2.2.0
Cheers,
Hello,
I've added participants as valid filters for the :filter attribute
https://github.com/jmettraux/ruote/commit/ceae194e14447ce857bce996fcac7913adb9b30c
https://github.com/jmettraux/ruote/commit/4e31fb648e963893389351f0fa7aa3e328bdbf8e
I think I'm done with the filters (expression and attribute) for now.
It's documented at
http://ruote.rubyforge.org/exp/filter.html
http://ruote.rubyforge.org/common_attributes.html#filter
I haven't implemented the 'record' feature found in the 'filter' expression for the :filter attribute. It doesn't seem necessary, in other words, validation filters are probably not necessary for filtered sections.
I hope to release 2.2.0 very soon.
Best regards,
--
Raphael.