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 file=File.new filename require file channel_module_name = File.basename(file).gsub('.rb','').split('_').map{|ea| ea.capitalize}.join('') channel_module= self.class.const_get(channel_module_name) puts "[channel_module.definition['module']] #{channel_module.definition["module"]}" puts "[channel_module] #{channel_module.inspect}" @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 @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? # puts "[activity] #{activity.to_s}" action = activity["action"] channel = activity["channel"] method = activity["method"] target = activity["target"] if match(target) values = message.body.merge(@credentials) # puts "[values] #{values}" # this maps the input values passed in with the templated defined in the workflow params = Hash.new activity["params"].each do |key,template| params[key]=Mustache.render(template,values) end # puts "[calling] #{channel}::#{method} (#{params.to_s})" 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 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 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