Ruby on Rails environment workflow implementation
Advertisements
Here are my workflow how the implementation.
VC Writing with a workflow designer, this little software is relatively simple, including some simple drag and drop graphics and symbols, such as the beginning and end, a state of flow. For each state can set the permissions can be set for each flow condition. At my research in the field of workflow is not very deep, development of the designer on the principle of practicality, there is no special complexity of implementation. Be able to achieve at the basis of user needs how how easy to do.
Save the file to xml format
<?xml version="1.0" encoding="gb2312" ?>
<workflow>
<start right="" leave="" enter="@form.a2 = @user.truename
@form.c2 = @user.department.name" x1="97" y1="156" x2="247" y2="279" />
<end right="The archive " x1="969" y1="148" x2="1129" y2="285" enter="" />
<state name="Department Manager approval " right="Leadership " enter="" leave="" x1="343" y1="179" x2="453" y2="253" />
<state name="General Manager approval " right="Manager approval " enter="" leave="" x1="566" y1="34" x2="668" y2="98" />
<state name="Administrative approval " right="Administrative approval " enter="" leave="" x1="717" y1="191" x2="870" y2="244" />
<trasit name="" condition="" from="Start " to="Department Manager approval " />
<trasit name="Greater than or equal to 3 days " condition="@form.b5!=nil && @form.b5 >=3" from="Department Manager approval " to="General Manager approval " />
<trasit name="" condition="@form.b5 == nil || @form.b5 <3" from="Department Manager approval " to="Administrative approval " />
<trasit name="" condition="" from="General Manager approval " to="Administrative approval " />
<trasit name="" condition="" from="Administrative approval " to="End " />
</workflow>
And this document to the system, from Ruby to resolve the job flow
Analytical Workflow Ruby code (on the lib directory) as follows:
#Flow.rb
require 'rexml/document'
require "State"
require "Trasit"
require "Flow"
require "pp"
include REXML
class Flow
attr_accessor :name, :publish_time
attr_reader :trasits, :states
def initialize(name, xmlstr, publish_time)
@publish_time = publish_time
@name = name
#Storage of all States, including start and end, State placed in the first, the end state placed last
@states = Array.new
@trasits = Array.new
#Load an XML document
doc = Document.new(xmlstr)
#Start resolution doc documents
root = doc.root
#Resolution start status node
root.elements.each("start") {|element|
start = State.start
start.name = "Start "
start.enter = element.attributes["enter"].gbk
start.leave = element.attributes["leave"].gbk
start.right = element.attributes["right"].gbk
start.x1 = element.attributes["x1"].to_i
start.x2 = element.attributes["x2"].to_i
start.y1 = element.attributes["y1"].to_i
start.y2 = element.attributes["y2"].to_i
@states << start
break
}
#Resolve all status node
root.elements.each("state") {|element|
state = State.new
state.name = element.attributes["name"].gbk
state.right = element.attributes["right"].gbk
state.enter = element.attributes["enter"].gbk
state.leave = element.attributes["leave"].gbk
state.x1 = element.attributes["x1"].to_i
state.x2 = element.attributes["x2"].to_i
state.y1 = element.attributes["y1"].to_i
state.y2 = element.attributes["y2"].to_i
@states << state
}
#Resolution to end the State of the node
root.elements.each("end") {|element|
end_node = State.new
end_node.name = "End "
end_node.right = element.attributes["right"].gbk
end_node.enter = element.attributes["enter"].gbk
end_node.x1 = element.attributes["x1"].to_i
end_node.x2 = element.attributes["x2"].to_i
end_node.y1 = element.attributes["y1"].to_i
end_node.y2 = element.attributes["y2"].to_i
@states << end_node
}
#Resolve all circulation
root.elements.each("trasit") {|element|
from_name = element.attributes["from"].gbk
to_name = element.attributes["to"].gbk
for state in @states
if state.name == from_name
from_node = state
end
if state.name == to_name
to_node = state
end
end
trasit = Trasit.new(from_node, to_node)
trasit.name = element.attributes["name"].gbk
trasit.condition = element.attributes["condition"].gbk
from_node.trasits << trasit
to_node.guest_trasits << trasit
@trasits << trasit
}
end
def start
@states[0]
end
def get_state(name)
for state in @states
return state if state.name == name
end
nil
end
end #FlowMeta.rb
$LOAD_PATH.unshift(File.dirname(__FILE__))
require "Flow"
require "EncodeUtil"
class FlowMeta
class << self
def LoadAllFlows()
YtLog.info "loading all workflow..."
$Workflows.clear
flows = YtwgWorkflow.find(:all)
for flow in flows
#LoadWorkFlow(flow.name, flow.content.sub!('<?xml version="1.0" encoding="gb2312" ?>', ''))
LoadWorkFlow(flow.name, flow.content, flow.publish_time)
end
end
def LoadWorkFlow(name, str, publish_time=Time.new)
YtLog.info name
$Workflows[name] = Flow.new(name, str, publish_time)
end
def Remove(name)
$Workflows.delete(name)
end
end
end #State.rb
##The State in the workflow
require "Trasit"
class State
attr_accessor :name, :leave, :enter, :right, :trasits, :guest_trasits
attr_accessor :x1, :x2, :y1, :y2
def initialize
#From this state of the circulation
@trasits = Array.new
#From other States to this state of circulation
@guest_trasits = Array.new
end
def trasits
@trasits
end
def add_trasit(trasit)
@trasits << trasit
end
def add_guest_trasit(trasit)
@guest_trasits << trasit
end
class << self
def start
start = State.new
start.name = "Start "
start
end
end
end #Trasit.rb class Trasit attr_accessor :condition, :name, :from, :to #New conversion classes, from ,toAre State class object def initialize(from, to) @from = from @to = to end end
OK, analytic workflow even completed the mission, 250 line Ruby code, a small, customizable and high even if the workflow engine are completed. Here we look how to use this work flow.
Workflow engine automatically after the completion of the following users will think of flow at each point to see form interface come from? For this feature, I wrote a specialized form design and form analytic engine, analytic engine form xml format can be translated into html form format forms. This form more complex components beyond the scope of this discussion, let us not refer to the time being.
Say what the following database tables, in order to use the workflow engine required three tables set up
//The workflow table CREATE TABLE `ytwg_workflow` ( `id` int(11) NOT NULL auto_increment, `name` varchar(100) default NULL, //Workflow name `content` longtext, //Workflow content, the designer to save the XML file `publish_time` datetime default NULL, //Release time `formtable` varchar(30) default NULL, //Form data stored in the table, each workflow once separate building a database table storing form data `position` int(11) default NULL, //Sort position `reserved1` varchar(100) default NULL, `reserved2` varchar(100) default NULL, `reserved3` varchar(100) default NULL, `reserved4` varchar(100) default NULL, `reserved5` varchar(100) default NULL, `reserved6` varchar(100) default NULL, PRIMARY KEY (`id`) ) //The workflow status form interface tables CREATE TABLE `ytwg_stateinterface` ( `id` int(11) NOT NULL auto_increment, `flowid` int(11) default NULL, //The workflow ID `name` varchar(100) default NULL, //The state name `content` longtext, //Form, the form designer to save the XML file `publish_time` datetime default NULL, //Release time `reserved1` varchar(100) default NULL, `reserved2` varchar(100) default NULL, `reserved3` varchar(100) default NULL, `reserved4` varchar(100) default NULL, `reserved5` varchar(100) default NULL, `reserved6` varchar(100) default NULL, PRIMARY KEY (`id`) ) //Forms processing record table CREATE TABLE `ytwg_formhistory` ( `id` int(11) NOT NULL auto_increment, `userid` int(11) default NULL, //User ID `flowid` int(11) default NULL, //The workflow ID `formid` int(11) default NULL, //The form ID `process_time` datetime default NULL, //Processing time PRIMARY KEY (`id`) )
Each release of a job stream, the following should work for the dynamic creation of database tables, stored form data. Me through to this workflow template to publish a form to dynamically create the table.
Look below how to use workflow
#Publishing workflows
def create
stream = params[:ytwg_workflow][:content]
content = stream.read
name = stream.original_filename[0, stream.original_filename.index(".")]
if YtwgWorkflow.find(:all, :conditions=>"name='#{name}'").size > 0
flash[:error] = "Exists workflow, upload failed "
render :action => 'new'
return
end
@ytwg_workflow = YtwgWorkflow.new()
@ytwg_workflow.name = name
begin
@ytwg_workflow.content = content
rescue
flash[:error] = "Upload file illegal "
render :action => 'new'
end
@ytwg_workflow.publish_time = Time.new
if @ytwg_workflow.save
FlowMeta.LoadWorkFlow(@ytwg_workflow.name, @ytwg_workflow.content.sub!('<?xml version="1.0" encoding="gb2312" ?>', ''))
flash[:notice] = 'Add workflow success '
redirect_to :action => 'list'
else
flash[:error] = "Add workflow to fail "
render :action => 'new'
end
end
#Uploading table definition template, on the basis of this form dynamically generated database table
def upload_formtable
stream = params[:content]
content = stream.read
helper = XMLHelper.new
helper.ReadFromString(content)
formtable = helper.tables[0]
if !formtable
flash[:notice] = "Upload file format error "
redirect_to :action=>"listinterface"
return
end
conn = ActiveRecord::Base.connection
conn.create_table "ytwg_#{formtable.GetTableID}", :primary_key=>:id do |t|
t.column "userid", :integer #The ID of the process initiator
t.column "flowid", :integer #The ID of the workflow
Integer(0).upto(formtable.GetRowCount()-1) do |row|
next if formtable.IsEmptyRow(row)
Integer(0).upto(formtable.GetColumnCount()-1) do |col|
next if formtable.IsEmptyCol(col)
cell = formtable.GetCell(row, col)
next if !cell.IsStore || !cell.IsEffective
next if formtable.GetCellDBFieldName(row, col).downcase == "id"
t.column "_state", :string, :limit=>30
t.column "_madetime", :datetime
t.column "_lastprocesstime", :datetime
if cell.GetDataType == 1 #CCell.CtNumeric
t.column formtable.GetCellDBFieldName(row, col).downcase, :float
elsif cell.GetDataType == 0 #CCell.CtText
if cell.IsCheckWidth()
t.column formtable.GetCellDBFieldName(row, col).downcase, :string, {:limit=>cell.GetTextWidth}
else
t.column formtable.GetCellDBFieldName(row, col).downcase, :string, {:limit=>100}
end
elsif cell.GetDataType == 3 #CCell.CtDate
t.column formtable.GetCellDBFieldName(row, col).downcase, :datetime
end
end
end
end
flow = YtwgWorkflow.find(params[:id])
flow.formtable = formtable.GetTableID
flow.save
flash[:notice] = "Table successfully "
redirect_to :action=>"listinterface"
end
#Upload status node form interface
def uploadinterface
stream = params[:content]
content = stream.read
interfaces = YtwgStateinterface.find(:all, :conditions=>"flowid = #{params[:id]} and name = '#{params[:name]}'")
if interfaces.size > 0
interface = interfaces[0]
interface.publish_time = Time.new
else
interface = YtwgStateinterface.new
interface.flowid = params[:id]
interface.name = params[:name]
interface.publish_time = Time.new
end
interface.content = content #EncodeUtil.change("UTF-8", "GB2312", content)
interface.save
flash[:notice] = "Upload status interface successfully "
redirect_to :action=>"listinterface"
end
#The user clicks on a workflow connection, you see your own workflow has been initiated.
def show_form
@flow = YtwgWorkflow.find(params[:flowid])
YtwgForm.set_table_name("ytwg_" + @flow.formtable)
YtwgForm.reset_column_information()
form = YtwgForm.find(params[:formid])
interfaces = YtwgStateinterface.find(:all, :conditions=>"flowid=#{params[:flowid]} and name='#{form._state.split(',')[0]}'")
if interfaces.size > 0
helper = XMLHelper.new
helper.ReadFromString(interfaces[0].content)
@style = helper.StyleToHTML(helper.tables[0])
@html = helper.TableToEditHTML(helper.tables[0], helper.dictionFactory,
{:record=>form, :encoding=>"gb2312"})
@historys = YtwgFormhistory.find(:all, :conditions=>"flowid=#{params[:flowid]} and formid = #{params[:formid]}")
else
render :text=>"There is no upload workflow interface "
end
end
#User-initiated or approving a form
def write_form
@flow = YtwgWorkflow.find(params[:flowid])
YtwgForm.set_table_name("ytwg_" + @flow.formtable)
YtwgForm.reset_column_information()
if params[:formid]
form_record = YtwgForm.find(params[:formid])
state_name = form_record._state
else
form_record = YtwgForm.new
form_record._state = 'Start '
state_name = form_record._state
end
states = []
for state in form_record._state.split(',')
states << state if checkright(state)
end
if states.size > 0
state_name = states[0]
else
state_name = 'Start '
end
process = FlowProcess.new($Workflows[@flow.name], form_record, state_name)
process.user = session[:user]
process.signal_enter
interfaces = YtwgStateinterface.find(:all, :conditions=>"flowid=#{@flow.id} and name = '#{state_name}'")
if interfaces.size ==0
render :text=>"There is no upload start interface "
return
end
@start_interface = interfaces[0]
helper = XMLHelper.new
helper.ReadFromString(@start_interface.content)
@style = helper.StyleToHTML(helper.tables[0])
@html = helper.TableToEditHTML(helper.tables[0], helper.dictionFactory,
{:record=>form_record,:encoding=>"gb2312", :script=>helper.script})
@historys = YtwgFormhistory.find(:all, :conditions=>"flowid=#{params[:flowid]} and formid = #{params[:formid]}") if params[:formid]
end
#The user finished a form click the submit
def update_form
@flow = YtwgWorkflow.find(params[:id])
YtwgForm.set_table_name("ytwg_" + @flow.formtable)
YtwgForm.reset_column_information()
if params[:formid]
form = YtwgForm.find(params[:formid])
form.update_attributes(params[@flow.formtable])
states = []
for state in form._state.split(',')
states << state if check_state_right(@flow.name, state)
end
state_name = states[0]
else
form = YtwgForm.new(params[@flow.formtable])
form._madetime = Time.new
form._state = 'Start '
state_name = form._state
form.userid = session[:user].id
form.flowid = @flow.id
end
form._lastprocesstime = Time.new
process = FlowProcess.new($Workflows[@flow.name], form, state_name)
process.user = session[:user]
process.signal_leave
history = YtwgFormhistory.new
history.userid = session[:user].id
history.flowid = @flow.id
history.formid = form.id
history.process_time = Time.new
history.save
redirect_to :action=>'myform', :id=>params[:id]
end
#Wait for me to handle processes
def show_waiting_form
@forms = get_wait_form(params[:id])
render :layout=>false
end
#Get some kind of documents for the current login's approval of
def get_wait_form(flowid)
forms = []
flow = YtwgWorkflow.find(flowid)
if !flow.formtable || flow.formtable.size==0
return forms
end
YtwgForm.set_table_name("ytwg_" + flow.formtable)
YtwgForm.reset_column_information()
for state in $Workflows[flow.name].states
next if state.name == "End "
conditions = []
conditions << "_state='#{state.name}'"
#If you can transfer from more than one State to the State, wait for all State after you do this State can execute
if state.guest_trasits.size == 1 #Only you can go from one State to here
conditions << " _state like '%,#{state.name}'"
conditions << "_state like '#{state.name},%'"
end
if state.right == "Leadership "
all_forms = YtwgForm.find(:all, :conditions=>conditions.join(' or '), :order=>"id desc")
for form in all_forms
forms << form if YtwgUser.find(form.userid).department.leader_id == session[:user].id rescue nil
end
else
for right in state.right.split(',')
if checkright(right)
forms += YtwgForm.find(:all, :conditions=>conditions.join(' or '), :order=>"id desc")
end
end
end
end
forms.uniq!
return forms
end With the above function of the core, how to use the basic job flow even understand. In addition there are many additional features required to achieve small, such as export Excel, export PDF, at page display workflow flow chart (I use the VML implementation). Following is a registration form leave the form shows the interface:
The workflow application status:
The current workflow has not yet commercial, there is only one application scenario. The first few months of our company and bought a set of OA, at train salesmen run mouthed puff flower Manager 9800, we bought, and later found the implementation of workflow and simply can not use. Leave a simple application form can not be realized in a custom form and flow, but under me based on my workflow components, rapid development of a set of OA, after a few days on the last line, then side by side perfect, one-month After very little moved. At present, this company is quite satisfied with the OA. Although OA of my outstanding OA than domestic products have a big gap, but this set of workflow components, or at least be able to do the majority of occasions, can not meet for the occasion can also be flexible expansion.
Welcome to everyone for their valuable opinions, the field has at ROR Workflow friends worry everyone can explore.
Related Posts of Ruby on Rails environment workflow implementation
-
The EJB3 Persistence
EJB3 persistence with Hibernate is very similar to the mechanism: Environment: Server: JBOSS5.0 Database: MySQL5.0 1. Set up a data source First of all, in jboss-5.0.0.GA \ server \ default \ deploy, the establishment of a database used to connect the dat
-
hibernate to use the principle of
The use of hibernate, implementation of data persistence. Has the following several processes. One configuration database connection information. Hibernate.config 2 configuration mapping. 3 use: the use of the process are the following steps: 3.1: Ge ...
-
Servlet brief introduction
Servlet brief introduction: Servlet is a small application server Are used to complete the B / S architecture, the client requests the response to treatment Platform independence, performance, able to run thread Servlet API for Servlet provides the s ...
-
can not be represented as java.sql.Timestamp
Development of procedures for the use of hibernate when, some time there is no need to fill in the fields, but after the hibernate query time reported "Java.sql.SQLException: Value'0000-00-00 'can not be represented as java.sql.Timestamp ...
-
Spring2.0 + hibernate3.1 + log4j + mysql demo
applicationContext.xml Non-attachment jar package, necessary friends can send an email to todd.liangt @ gmail.com
-
Struts2 + hibernate + spring problem user log in
dao layer services layer action jsp <tr> <td align="center"> <b> user name: </ b> </ td> <td> <s: textfield name = "czyNumber" cssClass = "textstyle" theme = "simple" size = &q
-
Based on JDBC, JPA Annotation achieve simple CRUD Generic Dao
The origin of ideas are pretty long history of reasons: [Use iBATIS history] The use of iBATIS has been a long time, the system is to use the CRUD template tool to generate the code, although there are tools to generate, but looked at a lot of CRUD the Sq
-
Hibernate secondary cache
Hibernate cache: 2-bit cache, also known as process-level cache or SessionFactory level cache, secondary cache can be shared by all of the session Cache configuration and the use of: Will echcache.xml (the document code in hibernate package directory ...
-
Hibernate's lazy strategy
hibernate Lazy strategy can be used in: <class> tag, it can be true / false Tags can <PROPERTY> values true / false type of necessary tools to enhance <set> <list> can tag values true / false / extra <many-to-one> <on ...












