Merge branch 'code-agent' of https://github.com/umarsheikh/huginn into umarsheikh-code-agent

Andrew Cantino 11 years ago
parent
commit
42c70af52a
3 changed files with 116 additions and 0 deletions
  1. 2 0
      Gemfile
  2. 6 0
      Gemfile.lock
  3. 108 0
      app/models/agents/code_agent.rb

+ 2 - 0
Gemfile

@@ -37,6 +37,8 @@ gem 'twitter-stream', '>=0.1.16'
37 37
 gem 'em-http-request'
38 38
 gem 'weibo_2'
39 39
 
40
+gem 'therubyracer'
41
+
40 42
 platforms :ruby_18 do
41 43
   gem 'system_timer'
42 44
   gem 'fastercsv'

+ 6 - 0
Gemfile.lock

@@ -124,6 +124,7 @@ GEM
124 124
       actionpack (>= 3.0.0)
125 125
       activesupport (>= 3.0.0)
126 126
     kramdown (1.1.0)
127
+    libv8 (3.16.14.3)
127 128
     mail (2.5.4)
128 129
       mime-types (~> 1.16)
129 130
       treetop (~> 1.4.8)
@@ -174,6 +175,7 @@ GEM
174 175
     rake (10.1.0)
175 176
     rdoc (3.12.2)
176 177
       json (~> 1.4)
178
+    ref (1.0.5)
177 179
     rest-client (1.6.7)
178 180
       mime-types (>= 1.16)
179 181
     rr (1.1.2)
@@ -224,6 +226,9 @@ GEM
224 226
     system_timer (1.2.4)
225 227
     term-ansicolor (1.2.2)
226 228
       tins (~> 0.8)
229
+    therubyracer (0.12.0)
230
+      libv8 (~> 3.16.14.0)
231
+      ref
227 232
     thor (0.18.1)
228 233
     tilt (1.4.1)
229 234
     tins (0.13.1)
@@ -300,6 +305,7 @@ DEPENDENCIES
300 305
   select2-rails
301 306
   shoulda-matchers
302 307
   system_timer
308
+  therubyracer
303 309
   twilio-ruby
304 310
   twitter
305 311
   twitter-stream (>= 0.1.16)

+ 108 - 0
app/models/agents/code_agent.rb

@@ -0,0 +1,108 @@
1
+require 'date'
2
+require 'cgi'
3
+module Agents
4
+  class CodeAgent < Agent
5
+    #cannot_receive_events!
6
+    #cannot_be_scheduled!
7
+    description <<-MD
8
+      Here is an agent that gives you the ability to specify your own code. We have already provided you
9
+      a javascript object that has read and write access to this agent's memory, and read access to events, options and the attributes of the agent.
10
+      We also provide you with a method to create events on the server.
11
+      You will be provided with an instance of the Agent object in javascript, with access to the above data.
12
+      You can create events based on your own logic.
13
+      Specifically, you have the following class, lets say, present is a string "js_code".
14
+
15
+          function Agent(e, o, agent){
16
+          this.events = JSON.parse(e);
17
+          this.options = JSON.parse(o);
18
+          this.agent = JSON.parse(agent);
19
+          }
20
+          Agent.prototype.check = function(){
21
+            // Implement me
22
+           }
23
+          Agent.prototype.receive = function(){
24
+            // Implement me
25
+          }
26
+      You need to provide the code for the Agent::check and Agent::receive function. You code will override any methods already present in the Agent if it has to, and you can use other methods as well with access to the agent properties. You need to at least provide the implementation of Agent.prototype.check and Agent.prototype.receive so that it can be called periodically, or it can execute when an event happens.
27
+
28
+      We will yield control to your implementation in the following way:
29
+
30
+          context.eval(js_code); //this is the code that declares the class Agent, and provides a global create_event method.
31
+          context.eval("a = new Agent(events, options, agent)")
32
+          context.eval(options['code'])
33
+          If you agent fires periodically {
34
+          context.eval("a.check();")
35
+          } else if your agent responds when an event happens {
36
+            context.eval("a.receive();")
37
+          }
38
+
39
+      If your agent responds to events it receive, you need to implement the receive() method, and if you agent fires periodically, you need to implement check(). If your agent does both, please implement both!
40
+
41
+    MD
42
+    def example_js
43
+    <<-H
44
+    function Agent(e, o, agent){
45
+    this.events = JSON.parse(e);
46
+    this.options = JSON.parse(o);
47
+    this.agent = JSON.parse(agent);
48
+    }
49
+    Agent.prototype.memory = function(key,value){
50
+      if (typeof(key) != "undefined" && typeof(value) != "undefined") {
51
+        var mem = JSON.parse(access_memory(JSON.stringify(key), JSON.stringify(value)));
52
+        return JSON.stringify(mem);
53
+      } else {
54
+        var mem = JSON.parse(access_memory());
55
+        return JSON.stringify(mem);
56
+      }
57
+    }
58
+    Agent.prototype.check = function(){
59
+    }
60
+    Agent.prototype.receive = function(){
61
+    }
62
+    H
63
+    end
64
+
65
+    def working?
66
+      return false if recent_error_logs?
67
+      if options['expected_update_period_in_days'].present?
68
+        return false unless event_created_within?(options['expected_update_period_in_days'])
69
+      end
70
+      if options['expected_receive_period_in_days'].present?
71
+        return false unless last_receive_at && last_receive_at > options['expected_receive_period_in_days'].to_i.days.ago
72
+      end
73
+      true
74
+    end
75
+
76
+    def execute_js(incoming_events, js_function = "check")
77
+      js_function = (js_function == "check" ? "check" : "receive")
78
+      context = V8::Context.new
79
+      context.eval(example_js)
80
+      context["create_event"] = lambda {|x,y| puts x; puts y; create_event payload: JSON.parse(y)}
81
+      context["access_memory"] = lambda {|a, x, y| x && y ? (memory[x] = y; memory.to_json) : memory.to_json }
82
+
83
+      context.eval(options['code']) # should override the run function.
84
+      a, e, o = [self.attributes.to_json, incoming_events.to_json, self.options.to_json]
85
+      string = "a = new Agent('#{e}','#{o}','#{a}');"
86
+      context.eval(string)
87
+      runner = "a.#{js_function}();"
88
+      context.eval(runner)
89
+    end
90
+
91
+    def check
92
+      execute_js("")
93
+    end
94
+
95
+    def receive(incoming_events)
96
+      execute_js(incoming_events)
97
+    end
98
+
99
+    def default_options
100
+      js_code = "Agent.prototype.check = function(){ var pd = JSON.stringify({memory: this.memory(), events: this.events, options: this.options});create_event(pd); };Agent.prototype.receive = function(){ var pd = JSON.stringify({memory: this.memory(), events: this.events, options: this.options});create_event(pd); }"
101
+      {
102
+        "code" => js_code,
103
+        'expected_receive_period_in_days' => "2",
104
+        'expected_update_period_in_days' => "2"
105
+      }
106
+    end
107
+  end
108
+end