geekq / workflow Goto Github PK
View Code? Open in Web Editor NEWRuby finite-state-machine-inspired API for modeling workflow
License: MIT License
Ruby finite-state-machine-inspired API for modeling workflow
License: MIT License
ruby -e "puts RUBY_VERSION; require 'workflow'"
1.9.1
/usr/local/lib/ruby/gems/1.9.1/gems/workflow-0.6.0/lib/workflow.rb: /usr/local/lib/ruby/gems/1.9.1/gems/workflow-0.6.0/lib/workflow.rb:182: Invalid return (SyntaxError)
Using workflow in an ActiveRecord model, when an event is raised the workflow state is updated, however the method model (with the same name) is not invoked.
I use RVM with ruby-1.9.2-p320 and rails 3.2.12.
My model :
class Assembly < ActiveRecord::Base
include Workflow
...
workflow do
state :fresh_aln_assembly do
event :submit_aln_transcripts_on_dataset, :transitions_to => :aln_transcripts_on_dataset
end
state :aln_transcripts_on_dataset do
event :aln_transcripts_on_dataset_check, :transitions_to => :aln_transcripts_on_dataset_checking
end
etc...
...
def aln_transcripts_on_dataset
do some stuff
end
...
Could you modify workflow to call on_*_entry callbacks before saving ActiveRecord models? That way the callbacks can modify object state without having to call save() a second time, improving usability and performance.
Perhaps this is intended but it's definitely not something I want in my app. Any state transition saves the object without validation because it uses update_attribute. This is true even if the object is not yet persisted.
If for example in your controller you have:
@user = User.new
@user.email = "someinvalidemail"
@user.registered!
if @user.save
..
The user will be saved with the invalid email. You will have invalid data the bug will be hard to find. This could manifest in some terribly more subtle ways if you've modified an object and then call a transition method. I prefer validation be performed, as sometimes validation could depend on state, so I'm trying out this monkey patch:
module Workflow
module ActiveRecordInstanceMethods
def persist_workflow_state(new_value)
send "#{self.class.workflow_column}=".to_sym, new_value
save!
end
end
end
It's probably better to return false and halt in the caller however. I was going to hack up a new version but the gemspec is not checked in and I'm not all that familiar with publishing gems, and this makes it hard to develop directly on a path with bundler.
Great job on this -- it is working really well for me. But to my question / issue: I am trying to externalize my workflows to my lib so I can re-use them. For example, a lot of my models use a common state of new, pending, active, etc. So what I am trying to do is define a couple of common workflows that a class can mix in. In my lib, I have myco/workflows/life_cycle, for example. In that module, I have a constant for the possible states (array) and the "workflow do..." logic. But, I can't seem to get everything to pick up my method. Any suggestions or tips? I've tried doing it as instance methods, class methods, and both, and I tried creating a method to wrap the "workflow do..." in then invoking that in my class def, but no luck. FYI, when I move the logic back in to my class, it works fine. Any thoughts are appreciated, and I'm happy to contribute my library of what I think are practical, common workflows.
I need to change the state of an object manually without triggering the state machine and any hooks. e.g. model.current_state = :closed
This is because I need to set up an initial state that can be any of the possible states.
Is this possible? How?
Thanks
The README does a great job with describing Workflow but one important detail appears to be missing. How to define which state is the initial state for the object. The example of article = Article.new; article.new? => true implies that if the state is named with :new, that is the initial state. Could this be stated explicitly in the docs, and what happens if the workflow does not define a state called :new?
Hello,
I'm having an interesting problem:
I have a request order with many sales. I want to transit the ro (request order) to 'completed' once the sum of all the sales' items equal to the ro items.
So each time I transit a sale to 'completed', I want to invoke a transition ('sell_partially') on the ro to control if all requested items have been sold.
My issue is:
Since I'm STILL in the sale's transition to 'completed', this very sale is NOT selected when the ro method iterating over all the related sales to control if the ro should be transited to 'completed' therefore it will wrongly assess that the ro is not yet completed and the ro will never be marked as 'completed' even though it is...
I could call it from the sales controller after having transited the sale but I feel it's not its business and this logic should belong to ro...
Anybody has a similar problem or suggestion?
Thanks in advance!
Gam
PS: Is there a way of nicely add this scopes to all the classes that uses Workflow:
workflow_spec.states.keys.each do |state|
named_scope state, :conditions => ['workflow_state = ?', state.to_s]
end
I have some classes which are inherited, however the initial state on the parent class is not the default state on the child class.
This should be configurable or default.
class Animal < AR::Base
include Workflow
workflow do
state :conceived do
event :birth, :transition_to => :born
end
state :born do
end
end
end
class Cat < Animal
include Workflow
workflow do
state :upset do
event :scratch, :transition_to => :hiding
end
state :hiding do
end
end
end
As you can see, my Cat should still start at :conceived, in fact all classes derived from Animal should.
Obviously this is a very simplified example, to illustrate a point!
Hi,
We'd like to use workflow to create a wizard. Is there any way to get the current step and the total number of steps in the workflow?
Thanks
If I use the helper halt! (with AR) within a event handler method (here 'load') where there is an transaction, halt! exception doesn't rollback the transaction leaving DB in a unsafe state.
In the following example, if I replace the halt! by raise, the rollback occurs properly.
Am I doing something wrong? or there is a bug somewhere...
def load
Shipment.transaction do
shipping_items.each do |si|
si.prepare_for_shipment
si.unit_price = 99.9
si.save!
end
# raise # rollback properly
halt! 'An error occured' # raise exception but doesn't rollback properly
end
end
PS: I checked the code but being new to Ruby, I'm not sure... maybe it's due to the exception type raised (TransitionHalted)?!?
I have workflow_column defined on a parent active record model, but it appears to not descend down to the child model. I accordingly receive undefined method for the default setter (workflow_state=)
I'll investigate a bit more in the morning. tired
since there is no forum, I use the issue tracker for asking a question.
I would like to know if the workflow fit with my requirement
I have n tasks to complete. A same task need to be parallelised and done by k non-experts , then to aggregated by a reviewer. I would like to design this kind of workflow though the use of this workflow gem as a potential help but I am not sure to understand how to design the parallelisation step.
To get more insights. I need to design a "task assigner" method so that when a non-experts wants a task he can choose on the available tasks that are still in the "parallelised" state ( less than k non-experts have completed the task)
nicolas
I'm wondering if it makes sense to have a way of defining a global transition that applies to all states? For example, if we have a long process with a lot of steps, it would be good to declare a global 'stop' transition that will go to :cancelled state.
I see a potential issue as we could then transit from :cancelled to :cancelled which is not good.
Anyone has a better idea?
It would be nice if the "workflow" method accepted a Specification OR a block, or possibly both. I am wrapping your state machine DSL with a proxy to enable my own DSL more specific to my problem domain. To do that, I wanted to have a WorkflowProxy that would intercept my own methods and convert them to method calls to your DSL, which is the Specification object. To do this I instantiate a Specification and my proxy uses method missing to pass method calls through, but can define custom methods to. In any case, I had to fork the project to allow this to work. I would submit a pull request, but I suspect my implementation may not be the right ultimate solution. (http://github.com/PlasticLizard/workflow)
Is there any problem with setting the initial state upon object instantiation? currently...
> d = MyModel.new
=> <MyModel....>
> d.state
=> nil
> d.valid?
=> false
> d.state
=> "created"
preferred way
> d = MyModel.new
=> <MyModel...>
> d.state
=> "created"
Is there a problem with calling write_initial_state
as a callback for after_initialize
?
After creating new record I get the following error
"can not update on a new record object"
My versions of ruby and rails:
ruby 2.0.0p0 (2013-02-24 revision 39474) [x86_64-linux]
Rails 3.2.12
My logs:
activerecord (3.2.12) lib/active_record/persistence.rb:195:in update_column'
workflow (1.0.0) lib/workflow.rb:348:inpersist_workflow_state'
workflow (1.0.0) lib/workflow.rb:214:in process_event!'
workflow (1.0.0) lib/workflow.rb:154:inblock (4 levels) in workflow'
vendor/engines/ads/app/controllers/ads/postings_controller.rb:46:in create'
actionpack (3.2.12) lib/action_controller/metal/implicit_render.rb:4:insend_action'
actionpack (3.2.12) lib/abstract_controller/base.rb:167:in process_action'
actionpack (3.2.12) lib/action_controller/metal/rendering.rb:10:inprocess_action'
actionpack (3.2.12) lib/abstract_controller/callbacks.rb:18:in block in process_action'
activesupport (3.2.12) lib/active_support/callbacks.rb:459:inblock in run_725368758838839659__process_action__1844081299291344106__callbacks'
activesupport (3.2.12) lib/active_support/callbacks.rb:215:in block in _conditional_callback_around_830'
activesupport (3.2.12) lib/active_support/callbacks.rb:326:inaround'
activesupport (3.2.12) lib/active_support/callbacks.rb:310:in _callback_around_785'
activesupport (3.2.12) lib/active_support/callbacks.rb:214:in_conditional_callback_around_830'
activesupport (3.2.12) lib/active_support/callbacks.rb:447:in _run__725368758838839659__process_action__1844081299291344106__callbacks'
activesupport (3.2.12) lib/active_support/callbacks.rb:405:in__run_callback'
activesupport (3.2.12) lib/active_support/callbacks.rb:385:in _run_process_action_callbacks'
activesupport (3.2.12) lib/active_support/callbacks.rb:81:inrun_callbacks'
actionpack (3.2.12) lib/abstract_controller/callbacks.rb:17:in process_action'
actionpack (3.2.12) lib/action_controller/metal/rescue.rb:29:inprocess_action'
actionpack (3.2.12) lib/action_controller/metal/instrumentation.rb:30:in block in process_action'
activesupport (3.2.12) lib/active_support/notifications.rb:123:inblock in instrument'
activesupport (3.2.12) lib/active_support/notifications/instrumenter.rb:20:in instrument'
activesupport (3.2.12) lib/active_support/notifications.rb:123:ininstrument'
actionpack (3.2.12) lib/action_controller/metal/instrumentation.rb:29:in process_action'
actionpack (3.2.12) lib/action_controller/metal/params_wrapper.rb:207:inprocess_action'
activerecord (3.2.12) lib/active_record/railties/controller_runtime.rb:18:in process_action'
actionpack (3.2.12) lib/abstract_controller/base.rb:121:inprocess'
actionpack (3.2.12) lib/abstract_controller/rendering.rb:45:in process'
actionpack (3.2.12) lib/action_controller/metal.rb:203:indispatch'
actionpack (3.2.12) lib/action_controller/metal/rack_delegation.rb:14:in dispatch'
actionpack (3.2.12) lib/action_controller/metal.rb:246:inblock in action'
actionpack (3.2.12) lib/action_dispatch/routing/route_set.rb:73:in call'
actionpack (3.2.12) lib/action_dispatch/routing/route_set.rb:73:indispatch'
actionpack (3.2.12) lib/action_dispatch/routing/route_set.rb:36:in call'
journey (1.0.4) lib/journey/router.rb:68:inblock in call'
journey (1.0.4) lib/journey/router.rb:56:in each'
journey (1.0.4) lib/journey/router.rb:56:incall'
actionpack (3.2.12) lib/action_dispatch/routing/route_set.rb:601:in call'
railties (3.2.12) lib/rails/engine.rb:479:incall'
railties (3.2.12) lib/rails/railtie/configurable.rb:30:in method_missing'
journey (1.0.4) lib/journey/router.rb:68:inblock in call'
journey (1.0.4) lib/journey/router.rb:56:in each'
journey (1.0.4) lib/journey/router.rb:56:incall'
actionpack (3.2.12) lib/action_dispatch/routing/route_set.rb:601:in call'
warden (1.2.1) lib/warden/manager.rb:35:inblock in call'
warden (1.2.1) lib/warden/manager.rb:34:in catch'
warden (1.2.1) lib/warden/manager.rb:34:incall'
actionpack (3.2.12) lib/action_dispatch/middleware/best_standards_support.rb:17:in call'
rack (1.4.5) lib/rack/etag.rb:23:incall'
rack (1.4.5) lib/rack/conditionalget.rb:35:in call'
actionpack (3.2.12) lib/action_dispatch/middleware/head.rb:14:incall'
actionpack (3.2.12) lib/action_dispatch/middleware/params_parser.rb:21:in call'
actionpack (3.2.12) lib/action_dispatch/middleware/flash.rb:242:incall'
rack (1.4.5) lib/rack/session/abstract/id.rb:210:in context'
rack (1.4.5) lib/rack/session/abstract/id.rb:205:incall'
actionpack (3.2.12) lib/action_dispatch/middleware/cookies.rb:341:in call'
activerecord (3.2.12) lib/active_record/query_cache.rb:64:incall'
activerecord (3.2.12) lib/active_record/connection_adapters/abstract/connection_pool.rb:479:in call'
actionpack (3.2.12) lib/action_dispatch/middleware/callbacks.rb:28:inblock in call'
activesupport (3.2.12) lib/active_support/callbacks.rb:405:in _run__4503270357500974277__call__1321220018304606217__callbacks'
activesupport (3.2.12) lib/active_support/callbacks.rb:405:in__run_callback'
activesupport (3.2.12) lib/active_support/callbacks.rb:385:in _run_call_callbacks'
activesupport (3.2.12) lib/active_support/callbacks.rb:81:inrun_callbacks'
actionpack (3.2.12) lib/action_dispatch/middleware/callbacks.rb:27:in call'
actionpack (3.2.12) lib/action_dispatch/middleware/reloader.rb:65:incall'
actionpack (3.2.12) lib/action_dispatch/middleware/remote_ip.rb:31:in call'
actionpack (3.2.12) lib/action_dispatch/middleware/debug_exceptions.rb:16:incall'
actionpack (3.2.12) lib/action_dispatch/middleware/show_exceptions.rb:56:in call'
railties (3.2.12) lib/rails/rack/logger.rb:32:incall_app'
railties (3.2.12) lib/rails/rack/logger.rb:18:in call'
actionpack (3.2.12) lib/action_dispatch/middleware/request_id.rb:22:incall'
rack (1.4.5) lib/rack/methodoverride.rb:21:in call'
rack (1.4.5) lib/rack/runtime.rb:17:incall'
activesupport (3.2.12) lib/active_support/cache/strategy/local_cache.rb:72:in call'
rack (1.4.5) lib/rack/lock.rb:15:incall'
actionpack (3.2.12) lib/action_dispatch/middleware/static.rb:62:in call'
rack-cache (1.2) lib/rack/cache/context.rb:136:inforward'
rack-cache (1.2) lib/rack/cache/context.rb:143:in pass'
rack-cache (1.2) lib/rack/cache/context.rb:155:ininvalidate'
rack-cache (1.2) lib/rack/cache/context.rb:71:in call!'
rack-cache (1.2) lib/rack/cache/context.rb:51:incall'
railties (3.2.12) lib/rails/engine.rb:479:in call'
railties (3.2.12) lib/rails/application.rb:223:incall'
rack (1.4.5) lib/rack/content_length.rb:14:in call'
railties (3.2.12) lib/rails/rack/log_tailer.rb:17:incall'
rack (1.4.5) lib/rack/handler/webrick.rb:59:in service'
/home/aslyusarchuk/.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/webrick/httpserver.rb:138:inservice'
/home/aslyusarchuk/.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/webrick/httpserver.rb:94:in run'
/home/aslyusarchuk/.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/webrick/server.rb:295:inblock in start_thread'
What I would like is for the event transition and the transition event handler to be executed within the same ActiveRecord transaction to guarantee that the state is properly represented.
I'm using Workflow along with ActiveRecord in an order processing application. I have an event, "confirm", that relieves available inventory and changes the order state from "Processing" to "Shipped". I know if there is a problem confirming inventory I can use halt! to prevent the state from updating to "Shipped". But, what I am concerned of is the rare case in which the error occurs in updating the state itself. If this were to happen, the inventory would confirm but the state would remain "Processing". Then, the same order would be confirmed again the next time the rake task runs which would double-relieve inventory.
I know I could try and catch the exception and try and reverse the effects, but things can start to get complicated when doing that. Is there currently a solution to run the entire event sequence in an ActiveRecord transaction?
Thank you for your help, and btw thanks for this gem... it's almost exactly what I was looking for.
Hello,
I've detected a few issues with Workflow::create_workflow_diagram and since I'm not experienced enough (yet) to do the fork/merge thing I will write them here, I hope you can incorporate them into the gem:
dot -Tpdf -o#{fname.gsub(/ /,'\ ')}.pdf #{fname.gsub(/ /,'\ ')}.dot
I hope it helps,
Gam
При добавлении последующего блюда в заказ, пропадают модификаторы у предыдущих блюд на странице заказа
I have the following workflow configuration:
# scheduled_change.rb
workflow do
# "The initial state of a workflow is the first state defined"
state :planning do
event :advance, :transitions_to => :planning_approved
event :skip_to_complete, :transitions_to => :completed
on_entry do
# send a "scheduled_change has been created" email
@subject = "A new Scheduled Change has been created. [not yet started]."
@subject += " '" + name + "'"
@subject += " planned on " + target_date.strftime('%m/%d/%Y')
@subject += " at " + target_time + " " + time_zone.name
@subject += ". Expected duration: " + duration.to_s + " hours."
@recipient = creator.email
send_email(@subject, @recipient, "created")
# send a "scheduled_change needs your approval" email
@subject = "A new Scheduled Change has been created NEEDING YOUR APPROVAL."
@subject += " '" + name + "'"
@subject += " planned on " + target_date.strftime('%m/%d/%Y')
@subject += " at " + target_time + " " + time_zone.name
@subject += ". Expected duration: " + duration.to_s + " hours."
@recipient = carrier.advocates.size > 0 ? carrier.advocates.map(&:email) : creator.email
logger.info "Sending NEEDING YOUR APPROVAL to #{@recipient.inspect}"
send_email(@subject, @recipient, "requesting_approval")
end
end
state :planning_approved do
event :advance, :transitions_to => :running
on_entry do
# send a "scheduled_change approved" email
@subject = "A new Scheduled Change has been approved."
@subject += " '" + name + "'"
@subject += " planned on " + target_date.strftime('%m/%d/%Y')
@subject += " at " + target_time + " " + time_zone.name
@subject += ". Expected duration: " + duration.to_s + " hours."
@recipient = carrier.advocates.size > 0 ? carrier.advocates.map(&:email) << creator.email : creator.email
send_email(@subject, @recipient, "created")
end
end
state :running do
event :advance, :transitions_to => :completed
on_entry do
# send a "scheduled_change has started" email
@recipient = SCHEDULED_CHANGE_BROADCAST_ADDRESS_EOS
@subject = "Scheduled Change #{name} has started"
@subject += ". Expected duration: " + duration.to_s + " hours."
send_email(@subject, @recipient, "status")
end
end
state :completed do
event :reset, :transitions_to => :planning
on_entry do
# send a "scheduled_change has completed" email
@recipient = SCHEDULED_CHANGE_BROADCAST_ADDRESS_EOS
@subject = "Scheduled Change #{name} has completed"
if scheduled_change_outcome
@subject += ". Result: " + scheduled_change_outcome.name.upcase + "."
end
status="completed"
send_email(@subject, @recipient, "status")
end
end
end
def send_email(_sub, _to, _type)
case _type
when "created"
ScheduledChangeMailer::deliver_scheduled_change_created_mail(_to, _sub, id)
when "requesting_approval"
ScheduledChangeMailer::deliver_scheduled_change_requesting_approval_mail(_to, _sub, id)
when "status"
ScheduledChangeMailer::deliver_scheduled_change_state_mail(_to, _sub, id)
end
end
But the code in :planning on_entry is no being executed. Can you please look over and see if you can tell me why?
Thanks! I appreciate your plugin!
Jason
Hi, I would like always when I update object move state back to awaiting_review.
Here is more info:
After creating new record I get the following error
"can not update on a new record object"
My versions of ruby and rails:
ruby 2.0.0p0 (2013-02-24 revision 39474) [x86_64-linux]
Rails 3.2.12
My logs:
activerecord (3.2.12) lib/active_record/persistence.rb:195:in update_column' workflow (1.0.0) lib/workflow.rb:348:in
persist_workflow_state'
workflow (1.0.0) lib/workflow.rb:214:in process_event!' workflow (1.0.0) lib/workflow.rb:154:in
block (4 levels) in workflow'
vendor/engines/ads/app/controllers/ads/postings_controller.rb:46:in create' actionpack (3.2.12) lib/action_controller/metal/implicit_render.rb:4:in
send_action'
actionpack (3.2.12) lib/abstract_controller/base.rb:167:in process_action' actionpack (3.2.12) lib/action_controller/metal/rendering.rb:10:in
process_action'
actionpack (3.2.12) lib/abstract_controller/callbacks.rb:18:in block in process_action' activesupport (3.2.12) lib/active_support/callbacks.rb:459:in
block in _run__725368758838839659__process_action__1844081299291344106__callbacks'
activesupport (3.2.12) lib/active_support/callbacks.rb:215:in block in _conditional_callback_around_830' activesupport (3.2.12) lib/active_support/callbacks.rb:326:in
around'
activesupport (3.2.12) lib/active_support/callbacks.rb:310:in _callback_around_785' activesupport (3.2.12) lib/active_support/callbacks.rb:214:in
_conditional_callback_around_830'
activesupport (3.2.12) lib/active_support/callbacks.rb:447:in _run__725368758838839659__process_action__1844081299291344106__callbacks' activesupport (3.2.12) lib/active_support/callbacks.rb:405:in
__run_callback'
activesupport (3.2.12) lib/active_support/callbacks.rb:385:in _run_process_action_callbacks' activesupport (3.2.12) lib/active_support/callbacks.rb:81:in
run_callbacks'
actionpack (3.2.12) lib/abstract_controller/callbacks.rb:17:in process_action' actionpack (3.2.12) lib/action_controller/metal/rescue.rb:29:in
process_action'
actionpack (3.2.12) lib/action_controller/metal/instrumentation.rb:30:in block in process_action' activesupport (3.2.12) lib/active_support/notifications.rb:123:in
block in instrument'
activesupport (3.2.12) lib/active_support/notifications/instrumenter.rb:20:in instrument' activesupport (3.2.12) lib/active_support/notifications.rb:123:in
instrument'
actionpack (3.2.12) lib/action_controller/metal/instrumentation.rb:29:in process_action' actionpack (3.2.12) lib/action_controller/metal/params_wrapper.rb:207:in
process_action'
activerecord (3.2.12) lib/active_record/railties/controller_runtime.rb:18:in process_action' actionpack (3.2.12) lib/abstract_controller/base.rb:121:in
process'
actionpack (3.2.12) lib/abstract_controller/rendering.rb:45:in process' actionpack (3.2.12) lib/action_controller/metal.rb:203:in
dispatch'
actionpack (3.2.12) lib/action_controller/metal/rack_delegation.rb:14:in dispatch' actionpack (3.2.12) lib/action_controller/metal.rb:246:in
block in action'
actionpack (3.2.12) lib/action_dispatch/routing/route_set.rb:73:in call' actionpack (3.2.12) lib/action_dispatch/routing/route_set.rb:73:in
dispatch'
actionpack (3.2.12) lib/action_dispatch/routing/route_set.rb:36:in call' journey (1.0.4) lib/journey/router.rb:68:in
block in call'
journey (1.0.4) lib/journey/router.rb:56:in each' journey (1.0.4) lib/journey/router.rb:56:in
call'
actionpack (3.2.12) lib/action_dispatch/routing/route_set.rb:601:in call' railties (3.2.12) lib/rails/engine.rb:479:in
call'
railties (3.2.12) lib/rails/railtie/configurable.rb:30:in method_missing' journey (1.0.4) lib/journey/router.rb:68:in
block in call'
journey (1.0.4) lib/journey/router.rb:56:in each' journey (1.0.4) lib/journey/router.rb:56:in
call'
actionpack (3.2.12) lib/action_dispatch/routing/route_set.rb:601:in call' warden (1.2.1) lib/warden/manager.rb:35:in
block in call'
warden (1.2.1) lib/warden/manager.rb:34:in catch' warden (1.2.1) lib/warden/manager.rb:34:in
call'
actionpack (3.2.12) lib/action_dispatch/middleware/best_standards_support.rb:17:in call' rack (1.4.5) lib/rack/etag.rb:23:in
call'
rack (1.4.5) lib/rack/conditionalget.rb:35:in call' actionpack (3.2.12) lib/action_dispatch/middleware/head.rb:14:in
call'
actionpack (3.2.12) lib/action_dispatch/middleware/params_parser.rb:21:in call' actionpack (3.2.12) lib/action_dispatch/middleware/flash.rb:242:in
call'
rack (1.4.5) lib/rack/session/abstract/id.rb:210:in context' rack (1.4.5) lib/rack/session/abstract/id.rb:205:in
call'
actionpack (3.2.12) lib/action_dispatch/middleware/cookies.rb:341:in call' activerecord (3.2.12) lib/active_record/query_cache.rb:64:in
call'
activerecord (3.2.12) lib/active_record/connection_adapters/abstract/connection_pool.rb:479:in call' actionpack (3.2.12) lib/action_dispatch/middleware/callbacks.rb:28:in
block in call'
activesupport (3.2.12) lib/active_support/callbacks.rb:405:in _run__4503270357500974277__call__1321220018304606217__callbacks' activesupport (3.2.12) lib/active_support/callbacks.rb:405:in
__run_callback'
activesupport (3.2.12) lib/active_support/callbacks.rb:385:in _run_call_callbacks' activesupport (3.2.12) lib/active_support/callbacks.rb:81:in
run_callbacks'
actionpack (3.2.12) lib/action_dispatch/middleware/callbacks.rb:27:in call' actionpack (3.2.12) lib/action_dispatch/middleware/reloader.rb:65:in
call'
actionpack (3.2.12) lib/action_dispatch/middleware/remote_ip.rb:31:in call' actionpack (3.2.12) lib/action_dispatch/middleware/debug_exceptions.rb:16:in
call'
actionpack (3.2.12) lib/action_dispatch/middleware/show_exceptions.rb:56:in call' railties (3.2.12) lib/rails/rack/logger.rb:32:in
call_app'
railties (3.2.12) lib/rails/rack/logger.rb:18:in call' actionpack (3.2.12) lib/action_dispatch/middleware/request_id.rb:22:in
call'
rack (1.4.5) lib/rack/methodoverride.rb:21:in call' rack (1.4.5) lib/rack/runtime.rb:17:in
call'
activesupport (3.2.12) lib/active_support/cache/strategy/local_cache.rb:72:in call' rack (1.4.5) lib/rack/lock.rb:15:in
call'
actionpack (3.2.12) lib/action_dispatch/middleware/static.rb:62:in call' rack-cache (1.2) lib/rack/cache/context.rb:136:in
forward'
rack-cache (1.2) lib/rack/cache/context.rb:143:in pass' rack-cache (1.2) lib/rack/cache/context.rb:155:in
invalidate'
rack-cache (1.2) lib/rack/cache/context.rb:71:in call!' rack-cache (1.2) lib/rack/cache/context.rb:51:in
call'
railties (3.2.12) lib/rails/engine.rb:479:in call' railties (3.2.12) lib/rails/application.rb:223:in
call'
rack (1.4.5) lib/rack/content_length.rb:14:in call' railties (3.2.12) lib/rails/rack/log_tailer.rb:17:in
call'
rack (1.4.5) lib/rack/handler/webrick.rb:59:in service' /home/aslyusarchuk/.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/webrick/httpserver.rb:138:in
service'
/home/aslyusarchuk/.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/webrick/httpserver.rb:94:in run' /home/aslyusarchuk/.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/webrick/server.rb:295:in
block in start_thread'
E.g., both of these would be expected to return true:
1.9.3p362 :011 > d.current_state == :new
=> true
1.9.3p362 :012 > [:new].include?(d.current_state)
=> false
I like this workflow module, however I'm concerned that it locks me into one state machine per model. The multiple workflow demo appears to be for alternate workflows, not parallel work flows. It's quite possible I would want to add a second unrelated workflow to a model and as far as I can tell this is not possible.
Would love to see:
workflow :billing_state do ...
workflow :activity_state do ...
Not a great example, but basically I like the syntax of this module but may end up using another (such as https://github.com/pluginaweek/state_machine) because this seems like an unnecessary restriction. I haven't looked at the code to see how feasible this would be.
I have a conditional halt in my model
def approve
halt! 'The invoice must be linked to a project' unless self.project
end
And in the controller I want to write something like below, so that it doesn't show the standard Workflow::TransitionHalted error page. (It doesn't work because @received_invoice.approve!
is always called) I know about the halted?
method but was wondering what the recommended way was to implement this in the controller.
def approve
@received_invoice = ReceivedInvoice.find(params[:id])
if @received_invoice.approve!
redirect_to received_invoice_url(@received_invoice), notice: 'Invoice approved'
else
redirect_to received_invoice_url(@received_invoice), notice: @received_invoice.halted_because
end
end
Is there some trick for non-active record persistence that I am missing?
I am using mongomapper and have added the :workflow_state attribute. I have also tried hooking on_transition and persist_workflow_state to preform a save manually, which actually does persist the state value but workflow does not seem to read that value on load of a record.
thanks,
chris
Looking through the code, I don't see any way to reference the last step. For example, I've got a branched workflow, and I'd like to be able to call reject at a step and have it go back to the previous step, which might be one of two possible things. I'm thinking that the best way to handle this might be to add another column called previous_workflow_state, and then update this using the on_transition hook. Sound reasonable?
Thanks!
If you have a workflow
workflow do
state :open do
event :archive, :transitions_to => :completed
event :order, :transitions_to => :ordered
end
state :ordered do
event :archive, :transitions_to => :completed
end
state :completed
end
def archive
#object event?
end
My question is can I have access of the object 'event' in the archive method? I would like to check the origin state from which the event was fired (ordered or open) and possibly the destination state and the event meta info.
Hey there,
In commit 07239f0 you started using update_attributes.
The difference with update_attribute (no S, http://apidock.com/rails/ActiveRecord/Persistence/update_attribute) is that the new method will validate the object, and make sure the attribute is not protected.
As a result, we now get "Can't mass-assign protected attributes: workflow_state". Just need to add an attr_accessible :workflow_state to the model...
Cheers :)
class Registration < ActiveRecord::Base
has_many :reservations, dependent: :destroy
has_many :events, through: :reservations, source: :event # Not work with workflow.
include Workflow
end
class Reservation < ActiveRecord::Base
belongs_to :registration
belongs_to :event
end
class Event < ActiveRecord::Base
has_many :reservations, dependent: :destroy
has_many :registrations, :through => :reservations
end
reg = Registration.find_by_id(267); reg.events
NoMethodError: undefined method quoted_table_name' for Workflow::Event:Class from /usr/local/lib/ruby/gems/1.9.1/gems/activerecord-2.3.8/lib/active_record/reflection.rb:187:in
quoted_table_name'
from /usr/local/lib/ruby/gems/1.9.1/gems/activerecord-2.3.8/lib/active_record/associations/has_many_through_association.rb:142:in construct_select' from /usr/local/lib/ruby/gems/1.9.1/gems/activerecord-2.3.8/lib/active_record/associations/has_many_through_association.rb:79:in
find_target'
from /usr/local/lib/ruby/gems/1.9.1/gems/activerecord-2.3.8/lib/active_record/associations/association_collection.rb:354:in load_target' from /usr/local/lib/ruby/gems/1.9.1/gems/activerecord-2.3.8/lib/active_record/associations/association_proxy.rb:140:in
inspect'
from /usr/local/bin/irb:12:in `
Using "class_name: '::Event'" works around this bug.
Back on 1.2 and 2.0.3 I used AASM and fellows - lately I've been trying out state_machine - but then I googled 'dynamic state machine' and Mountain View 'coughed up' this gem!
I'd like to allow users to setting up workflows themselves, selecting from a bunch of available events/transistions, and in the proces I18n'ifying the body of the work :)
One use case would be several groups of users processing workflows on the same codebase - but with totally different workflow use cases (multi-site)
One other use case would be users adjusting workflows to changes to business policies - like: we at Corp XYZ are now 'green' and thus our SourcingOrders should not print before they are emailed/faxed to our suppliers - unless the Order Total exceeds 1,000,000...
I'm working off of a generic abstract_action_base.rb model which gets inherited by models like Project, Product, Item, Partner, etc. and then (as STI models) like Partner being inherited by models like Customer, Supplier, Bank, etc
Ideally I'd like my abstract_action_base to provide the workflow_states as a Hash
{ sourcing: :requested, selling: :ordered, stock: :backlogged }
and all state_questions to work like Product.find(419).sourcing_requested? and persisted to ActiveRecord (AR) in a quasi-searchable fashion - something along the lines of:
workflow_states = '----1R--2O--3B----'
which would allow quick searches on workflow states like:
Product.where{ workflow_states.like_all [ '-1R-','-3B-'] }.where( "workflow_states like '%-2O-% " )
Workflow transitions would - again ideally - be persisted to AR as something like:
t.string :transition_label # what to label the transition and use as key for I18n.translations
t.text :guard # code to eval true/false guarding the transition on an instance
t.text :from_to # hash of transition like { drafted: { requested: :some_condition_attribute_or_method, archived: :other_condition_or_method }, requested: {} }
# persisted as serialized object
Am I totally loosing it here? Or does you (the reader) see dynamically formulated workflows fly at all?
I can't found any way to easily change state on a halt
class Model < AR::Base
include Workflow
workflow do
state :new do
event :process, :transitions_to => :ready
end
state :ready
state :fail
end
def process
if process_works_whell?
:ok
else
change_state :to => :fail
end
end
Is any way to do this?
Maybe an option as:
event :process, :transitions_to => :ready, :on_halt => :fail
Or more advanced:
event :process, :transitions_to => [:ready, :fail]
and in the code
halt(:to => :fail)
Or may i doing something wrong?
It would be very useful if there were operators able to respond about the overall state.
For example, having this:
class C
workflow do
state :a do
event :to_b, :transitions_to => :b
end
state :b do
event :to_c, :transitions_to => :c
end
state :c
end
...
end
And these instances:
inst_a.a? #=> true
inst_b.b? #=> true
inst_c.c? #=> true
It would be useful to have a method overcame?(state) like this:
inst_a.overcame?(:a) #=>false
inst_a.overcame?(:b) #=>false
inst_a.overcame?(:c) #=>false
inst_b.overcame?(:a) #=>true
inst_b.overcame?(:b) #=>false
inst_b.overcame?(:c) #=>false
inst_c.overcame?(:a) #=>true
inst_c.overcame?(:b) #=>true
inst_c.overcame?(:c) #=>false
and its counterpart to_reach?(state)
inst_a.to_reach?(:a) #=>false
inst_a.to_reach?(:b) #=>true
inst_a.to_reach?(:c) #=>true
inst_b.to_reach?(:a) #=>false
inst_b.to_reach?(:b) #=>false
inst_b.to_reach?(:c) #=>true
inst_c.to_reach?(:a) #=>false
inst_c.to_reach?(:b) #=>false
inst_c.to_reach?(:c) #=>false
of course this is a simple case in which these two methods would be deterministic since the diagram produced by the workflow is a tree and not a graph, but even just for those simple case, it would be useful to have these methods.
If the response cannot be determined, it could be thrown an exception.
These methods should be boolean, to be useful, not three-state, because the are intended to use with tree workflow...
Hi - beginner error here... but an uninformative diagnostic error message. I accidentally typed "transition_to" instead of "transitions_to". Which provokes a message about an undefined method "to_sym" for nil.Class. Not very obvious that there's a missing required parameter...
Can I suggest a one liner enhancement to the initialize function?
def initialize(name, transitions_to, meta = {}, &action)
raise "missing 'transitions_to' from workflow for '#{name}'" if transitions_to == nil
@name, @transitions_to, @meta, @action = name, transitions_to.to_sym, meta, action
end
Hi, I'm wondering what is the most appropriate way for handling 'undo' using workflow, for example using your 'article' illustration:
from 'being_reviewed' we can either accept or reject an article. If we choose to accept but we want to revert back to 'being_reviewed' is it better to?
1 give's each state an extra event for reverting, ex:
state :new do
event :submit, :transitions_to => :awaiting_review
end
state :awaiting_review do
event :review, :transitions_to => :being_reviewed
event :revert_new, :transitions_to => :new
end
state :being_reviewed do
event :accept, :transitions_to => :accepted
event :reject, :transitions_to => :rejected
event :revert_awaiting_review, :transitions_to => :awaiting_review
end
state :accepted do
event :revert_being_reviewed, :transitions_to => :being_reviewed
end
2 make use of external gem for handling undo, such as papertrail
3 some other way?
I need to run the same code before and after every transition. I believe on_transition corresponds to after_transition. It looks like ryan-allen already implemented this, but it is pretty old by now: https://github.com/ryan-allen/workflow
If I try to use strings instead of symbols for defining the events Workflow doesn't work properly.
e.g.
state 'new' do
event 'publish', ...
end
Then
object.can_publish?
always returns false where it should return true. When using symbols this works fine.
However using string is useful for validation:
validates :state, :presence => true, :inclusion => STATUSES
where STATUSES is the array of possible valid states
When the object is first created the state is a symbol so validation passes. Then when the saved and retrieved back again state is a string so it doesn't validate anymore.
I would be good to have full support for using strings as well as symbols.
это могла бы быть интересная фича... или я упустил что-то и для workflow есть возможность спроектировать графически и потом преобразовать в готовые rb файлы?
I have Ruby 1.9.1
I commented four lines of code (it begins at the line 180), as you recommended.
Need some help with this:
$ irb
irb(main):001:0> require 'rubygems'
=> false
irb(main):002:0> require 'workflow'
=> true
irb(main):003:0> class Article
irb(main):004:1> include Workflow
irb(main):005:1> workflow do
irb(main):006:2* state :new do
irb(main):007:3* event :submit, :transitions_to => :awaiting_review
irb(main):008:3> end
irb(main):009:2> state :awaiting_review do
irb(main):010:3* event :review, :transitions_to => :being_reviewed
irb(main):011:3> end
irb(main):012:2> state :being_reviewed do
irb(main):013:3* event :accept, :transitions_to => :accepted
irb(main):014:3> event :reject, :transitions_to => :rejected
irb(main):015:3> end
irb(main):016:2> state :accepted
irb(main):017:2> state :rejected
irb(main):018:2> end
irb(main):019:1> end
=> [#<Workflow::State:0x000000023bc040 @name=:new, @events={:submit=>#<Workflow::Event:0x000000023bbe10 @name=:submit, @transitions_to=:awaiting_review, @meta={}, @action=nil>}, @meta={}>, #<Workflow::State:0x000000023bbc88 @name=:awaiting_review, @events={:review=>#<Workflow::Event:0x000000023bba58 @name=:review, @transitions_to=:being_reviewed, @meta={}, @action=nil>}, @meta={}>, #<Workflow::State:0x000000023bb908 @name=:being_reviewed, @events={:accept=>#<Workflow::Event:0x000000023bb6d8 @name=:accept, @transitions_to=:accepted, @meta={}, @action=nil>, :reject=>#<Workflow::Event:0x000000023bb5c0 @name=:reject, @transitions_to=:rejected, @meta={}, @action=nil>}, @meta={}>, #<Workflow::State:0x000000023bb4a8 @name=:accepted, @events={}, @meta={}>, #<Workflow::State:0x000000023bb208 @name=:rejected, @events={}, @meta={}>]
irb(main):020:0> article = Article.new
=> #Article:0x000000023b0ce0
irb(main):021:0> article.accepted?
NameError: undefined local variable or method c' for #<Article:0x000000023b0ce0> from /usr/lib/ruby/gems/1.9.1/gems/workflow-0.6.0/lib/workflow.rb:188:in
spec'
from /usr/lib/ruby/gems/1.9.1/gems/workflow-0.6.0/lib/workflow.rb:135:in current_state' from /usr/lib/ruby/gems/1.9.1/gems/workflow-0.6.0/lib/workflow.rb:115:in
block (3 levels) in workflow'
from (irb):21
from /usr/bin/irb:12:in `
I also tried to use Workflow in Rails project, through script/console, but it shows me the same error.
Sorry, if I misunderstood something.
Is there a way to include activerecord-model validations in the can_ methods?
I have a requirement to do state-based validations. This keeps all of the form code tidy, since it can use the standard ActiveRecord error discovery.
I've been looking at the southdesign modification to do state-based validations, which is a good start, but for my purposes there are a few shortcomings:
1 - It always throws an exception - I can see why, because new_state! is already the "!" operator which would normally raise an exception in ActiveRecord, but in the workflow nomenclature it does not raise an exception under normal circumstances. I think it is preferable that halted? or valid? return false in the normal case (where, for validation purposes, invalid input should be considered a normal case).
2 - It does not take advantage of the new ActiveModel Validators (http://api.rubyonrails.org/classes/ActiveModel/Validations.html). This causes a deprecation warning in Rails 3, but also I think it doesn't capture the existing validation classes. No reason to be writing more of them!
So I think this is all easily managed, but my questions for discussion are:
1: Is there a reasonable syntax for indicating when to not throw/throw an exception for validation? I am thinking new_state! and new_state!(true), respectively, but this seems slightly kludgy. Comments?
2: Is there a good syntax for defining validations so they can follow the Rails3 validator syntax:
# validates :name, :presence => {:if => :foo?}, :length => {:maximum => 200, :minimum => 100}
For consistency, I am inclined to use:
event :accept, :transitions_to => :accepted do
validates :title, :presence
end
But I see in the README "that mixing the list of events and states with the blocks invoked for particular transitions leads to a bumpy and poorly readable code due to a deep nesting" so I am conflicted about the right way to handle this. Advice/comments?
Hi there,
I have ActiveRecord 3.2.10 with ruby 1.9.2-p290 and trying to get this working with workflow gem version 0.8.6.
Without active record, the transitions work fine, as soon as I add in ActiveRecord to the mix, it only created the initial object and records the initial state in the DB correctly, but any transition (whether it's a new instance or trying to resume it from the DB) fails. No error message provided, the object state stays in the original one.
I even tried to run your Booking class example (https://github.com/geekq/workflow/blob/master/test/multiple_workflows_test.rb) and that didn't work either.
Could you look into this please and/or advise what am I doing wrong?
Thanks and regards.
Máté
It would be great if we could test if a workflow is 'bigger/smaller or egal' than another according to the specification.
Workflow do
state :one do
event :next, :transitions_to => :two,
end
state :two do
event :back, :transitions_to => :one,
event :next, :transitions_to => :three,
end
state :three
end
We could check:
my_object.current_state > Object.workflow_spec.states[:two]
bigger ( state1 > state2): true if state2 cannot be reached (through any number of events/charges) from state1 BUT NOT the opposite (eg, :three > :one => true)
smaller ( state1 < state2): true if state2 can be reached from state1 BUT NOT the opposite (eg, :one > :three => true)
equal ( state1 == state2): true if state2 can be reached from state1 AND state1 can be reached from state2 (eg, :two == :one => true)
What do you think?
bowsersenior reported:
The per-object workflow has some issues, even on ruby 1.8.7!
Let me explain with reference to the working example:
http://github.com/geekq/workflow/blob/master/test/multiple_workflows_test.rb
The Booking class is an ActiveRecord model, but in fact, the ActiveRecordInstanceMethods are never loaded. The workflow_state is persisted only in a variable. I am not sure why this is happening, but it seems to be related to the use of metaclass.
Now on Ruby 1.9.2, where the bug referenced above has been fixed, the test fails, because ActiveRecordInstanceMethods ARE loaded, and no class method name workflow_column is found.
I fiddled around with this for a while trying to find a solution, but couldn't get anywhere. It seems like there should be a straightforward solution...
We have an Orders::RetailerOrder model and create_workflow_diagram tries to put the dot file in a sub folder + the dot file is invalid.
I was able to fix the problem by modifying the workflow_name :
workflow_name = "#{klass.name.tableize}_workflow".gsub(/\//, '')
In a Rails application of mine a model has a state :created
with an event :open, :transition_to => :opened
declaration.
A method of the model triggers the state change calling open!
. That worked well with the 0.8.1 gem but doesn't work anymore with the 0.8.6 one. Instead it ends up calling the open
method of open-uri, which happens to have been included somewhere by the framework and has a different number of arguments and returns an error which I saw in the browser.
An easy workaround would be to change the state name to something else but I set "0.8.1" in the Gemfile to be sure I have no other problems I still didn't find out. Furthermore, what if the next version of another gem "steals" one or more of my state names? (inadvertently, obviously, I don't think this is an attack vector). Devising perfectly safe state names is impossible.
The ideal solution is that the Workflow state prevails over any method name. That should make sense because a locally defined workflow should be expected to be in the local scope. Calling a far away method probably violates the least surprise principle.
So, if you had a separate interface for users to define workflows with name, transition actions, transition messages etc. This would create a limited-use rules engine.
Hi,
I have the following classes on my system:
class Event < ActiveRecord::Base
has_many :event_approval_requests
belongs_to :user
include Workflow
workflow do
state :pre_registered do
event :submit, :transitions_to => :awaiting_approval
end
state :awaiting_approval do
event :approve, :transitions_to => :approved
event :reject, :transitions_to => :rejected
end
state :approved do
event :submit, :transitions_to => :awaiting_approval
end
state :rejected do
event :submit, :transitions_to => :awaiting_approval
end
end
...
end
class EventApprovalRequest < ActiveRecord::Base belongs_to :user belongs_to :department belongs_to :event include Workflow workflow do state :pending do event :approve, :transitions_to => :approved event :reject, :transitions_to => :rejected end state :approved state :rejected end ... end
Note that the two classes have workflow definitions and one have a relationship with the other. In a view, when I try to access the event from the event_approval_request like below:
<%=h event_approval_request.event.title %>
I got the following error: NoMethodError in Event_approval_requests#index undefined method `find' for Workflow::Event:Class
this error doesn't occur if I access a non-workflow class, like below:
<%=h event_approval_request.user.first_name %>
I'm using JRuby 1.5.1, Linux Fedora 11, Rails 2.3.8 and workflow 0.6.0.
Could anyone help me? Is it really an issue?
Thanks in advance,
Gustavo
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.