Ruby on Rails environment workflow implementation

Workflow enterprises are essential to the development of an important component. With workflow, customer demand will greatly improve the speed of implementation, taking into account the development of efficiency, flexibility. Java has a number of stability in the area of workflow, Java has become the occupation of the development of a powerful enterprise-level aides. However, ROR field, there is no outstanding work flow occur. RubyForge has some of the work flow on projects, but a closer facie, are targeted at Java Workflow transplant, but can not be the extent practical. The face of this status quo, at my own in 2006 developed a small-scale Ruby job stream, although a small amount of code, but the practicality of it is true that for some real use cases can easily qualified, but also a strong supporting me to continue to the road before RORLine .

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.

Ruby on Rails environment workflow implementation

Save the file to xml format

<?xml version="1.0" encoding="gb2312" ?>
<workflow>
    <start right="" leave="" enter="@form.a2 = @user.truename&#x0D;&#x0A;@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 &amp;&amp; @form.b5 &gt;=3" from="Department Manager approval " to="General Manager approval " />
    <trasit name="" condition="@form.b5 == nil || @form.b5 &lt;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:

Ruby on Rails environment workflow implementation

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.

分类:Ruby 时间:2009-03-16 人气:1445
分享到:
blog comments powered by Disqus

相关文章

  • Let Ruby On Rails into the enterprise development 2009-03-15

    ROR developed by B / S has been 3 years, and early exposure to ROR in early 2006, when there are no Chinese data point, but saying "10 times higher than the Java development efficiency" Let me go look for ROR 4 English information A month later

  • Ruby on Rails and to use Action Web Service to create Web Services 2009-05-25

    Action Web Service Module will be at Ruby on Rails implementation of Web services. Action Web Service will be the creation of SOAP and XML-RPC Web Services in support of server-side protocol. You can use the Declaration and the Action Web Service rel

  • Ruby on Rails and J2EE: can they coexist? 2010-03-04

    Ruby on Rails is a relatively new Web application framework, built on the Ruby language on top. It was promoted as an alternative to existing enterprise frameworks, and its goal, in short, is for life, or at least Web development life easier. In this

  • Ruby on Rails: Overview of activity record (transfer) 2010-10-09

    Ruby on Rails to write with a database management system is very fast. Ruby on Rails reason why there is such a high productivity, flexibility is not only the syntax of Ruby, and all this is thanks to a large activity record (Active Record) has done.

  • In the Windows platform using Apache2.2 and Mongrel running Ruby on Rails 2009-03-02

    First, install Ruby, rails, mongrel and Apache2.2 Rubyforge download from the web site One-Click Ruby Install, run setup on installed ruby and rubygems. Run the command: gem install rails-y gem install mongrel-y gem install mongrel_service-y Installe

  • In the Linux platform to install and configure Ruby on Rails Detailed 2009-03-02

    ruby on rails recommend the production environment is running Linux / FreeBSD / Unix, or Unix family of operating systems, using lighttpd + FCGI solution. The following will be my Linux operating system, lighttpd + FCGI, MySQL database as an example,

  • Using Ruby on Rails and Action Web Service to create Web Services 2009-03-07

    Action Web Service module will be at Ruby on Rails implementation of Web services. Action Web Service will create the SOAP and XML-RPC Web Service Protocol server-side support. You can use the Declaration and the Action Web Service released the API.

  • Let Ruby On Rails into the Embedded Development 2009-03-17

    In the embedded Linux development, and C are a pair of golden partner, almost to occupy the vast majority of the domestic market and become the mainstream model of embedded development. At present, for some equipment to do configuration interface, ma

  • Ruby On Rails (1) hellowolrd 2009-03-22

    1. What is Ruby On Rails Ruby On Rails is a Ruby-based Web development framework. Ruby is an object-oriented scripting programming language. 2.Ruby has the following advantages: Explanation-based implementation, convenient and quick Ruby is the inter

iOS 开发

Android 开发

Python 开发

JAVA 开发

开发语言

PHP 开发

Ruby 开发

搜索

前端开发

数据库

开发工具

开放平台

Javascript 开发

.NET 开发

云计算

服务器

Copyright (C) codeweblog.com, All Rights Reserved.

CodeWeblog.com 版权所有 黔ICP备15002463号-1

processed in 0.291 (s). 12 q(s)