require 'rubygems' require 'factor' require 'mustache' #require 'facter' module Factor module Runtime class Engine attr_accessor :channel_modules, :workflows, :message_bus # Engine needs modules that contain the code, workflows to run, and message bus for communication def initialize(username,token) @channel_modules=Hash.new @workflows = Hash.new @message_bus = MessageBus.new(username,token) @credentials = Hash.new @tags = Hash.new end def tag(key,value) @tags[key]=value end # load the channel by referencing the .rb file # the filename is lowercase with "_" for spaces # and the module inside must be camal cased to match # once loaded it is in the channel_modules Hash def load_channel filename filename = File.absolute_path(File.expand_path(filename.path)) if filename.is_a?(File) # just in case someone passes in a File not a String (i.e. me) require filename channel_module_name = File.basename(filename).gsub('.rb','').split('_').map{|ea| ea.capitalize}.join('') channel_module= self.class.const_get(channel_module_name) @channel_modules[channel_module.definition["module"]]=channel_module end def load_credentials credentials @credentials["credentials"] = credentials end # adds the workflow to the workflows list # the object must be a Workflow type def load_workflow workflow @workflows[workflow.name] = workflow end def logs routing_key="#", &code @message_bus.start do @message_bus.listen routing_key do |message| code.call message end end end def launch workflow, params instance_id=SecureRandom.hex @message_bus.start do message = Message.new message.position << "start" message.workflow=workflow message.add_values params message.workflow_instance_id= instance_id @message_bus.send_and_close message end instance_id end # start your engines. vroom vrooooom! def start begin @message_bus.start do @message_bus.listen do |message| if @workflows.include? message.workflow workflow = @workflows[message.workflow] activity = workflow.get_activity(message.position) if !activity.nil? action = activity["action"] channel = activity["channel"] method = activity["method"] target = activity["target"] if match(target) values = message.body.merge(@credentials) # this maps the input values passed in with the templated defined in the workflow puts "[activity-params] #{activity["params"]}" puts "[values] #{values}" params = render_template(activity["params"],values) puts "[params] #{params}" event = call_channel_method(channel,method,params) response_message = message.respond(event.params,event.class.name.split("::").last) @message_bus.send response_message end end else # workflow doesn't exist end end end rescue SystemExit, Interrupt "done" rescue Exception => ex ex end end def call_channel_method(channel_name,class_name,params) channel_module = @channel_modules[channel_name] command = channel_module.const_get(class_name) command.new.do_work(params) end private def render_template(source,values) #params = Hash.new if source.is_a?(Hash) params = Hash.new source.each do |key,template| params[key]=render_template(template,values) end else params = Mustache.render(source,values) end # activity_params.each do |key,template| # params[key]=Mustache.render(template,values) # end params end def match(target) return true if target==nil || target=="" facts={} #Facter.each {|k,v| facts[k]=v} @tags.each {|k,v| facts[k]=v} key,value=target.split("=") facts[key]==value end end end end