@@ -2,16 +2,19 @@ module LiquidInterpolatable  | 
            ||
| 2 | 2 | 
                extend ActiveSupport::Concern  | 
            
| 3 | 3 | 
                 | 
            
| 4 | 4 | 
                def interpolate_options(options, payload)  | 
            
| 5 | 
                - duped_options = options.dup.tap do |duped_options|  | 
            |
| 6 | 
                - duped_options.each_pair do |key, value|  | 
            |
| 7 | 
                - if value.class == String  | 
            |
| 8 | 
                - duped_options[key] = Liquid::Template.parse(value).render(payload)  | 
            |
| 9 | 
                - else  | 
            |
| 10 | 
                - duped_options[key] = value  | 
            |
| 11 | 
                - end  | 
            |
| 5 | 
                + case options.class.to_s  | 
            |
| 6 | 
                + when 'String'  | 
            |
| 7 | 
                + Liquid::Template.parse(options).render(payload)  | 
            |
| 8 | 
                + when 'ActiveSupport::HashWithIndifferentAccess', 'Hash'  | 
            |
| 9 | 
                + duped_options = options.dup  | 
            |
| 10 | 
                + duped_options.each do |key, value|  | 
            |
| 11 | 
                + duped_options[key] = interpolate_options(value, payload)  | 
            |
| 12 | 
                + end  | 
            |
| 13 | 
                + when 'Array'  | 
            |
| 14 | 
                + options.collect do |value|  | 
            |
| 15 | 
                + interpolate_options(value, payload)  | 
            |
| 12 | 16 | 
                end  | 
            
| 13 | 17 | 
                end  | 
            
| 14 | 
                - duped_options  | 
            |
| 15 | 18 | 
                end  | 
            
| 16 | 19 | 
                 | 
            
| 17 | 20 | 
                def interpolate_string(string, payload)  | 
            
                @@ -2,6 +2,8 @@ require 'rturk'  | 
            ||
| 2 | 2 | 
                 | 
            
| 3 | 3 | 
                module Agents  | 
            
| 4 | 4 | 
                class HumanTaskAgent < Agent  | 
            
| 5 | 
                + include LiquidInterpolatable  | 
            |
| 6 | 
                +  | 
            |
| 5 | 7 | 
                default_schedule "every_10m"  | 
            
| 6 | 8 | 
                 | 
            
| 7 | 9 | 
                description <<-MD  | 
            
                @@ -16,7 +18,7 @@ module Agents  | 
            ||
| 16 | 18 | 
                 | 
            
| 17 | 19 | 
                # Example  | 
            
| 18 | 20 | 
                 | 
            
| 19 | 
                - If created with an event, all HIT fields can contain interpolated values via [JSONPaths](http://goessner.net/articles/JsonPath/) placed between < and > characters.  | 
            |
| 21 | 
                + If created with an event, all HIT fields can contain interpolated values via [liquid templating](https://github.com/cantino/huginn/wiki/Formatting-Events-using-Liquid).  | 
            |
| 20 | 22 | 
                For example, if the incoming event was a Twitter event, you could make a HITT to rate its sentiment like this:  | 
            
| 21 | 23 | 
                 | 
            
| 22 | 24 | 
                           {
               | 
            
                @@ -25,7 +27,7 @@ module Agents  | 
            ||
| 25 | 27 | 
                             "hit": {
               | 
            
| 26 | 28 | 
                "assignments": 1,  | 
            
| 27 | 29 | 
                "title": "Sentiment evaluation",  | 
            
| 28 | 
                - "description": "Please rate the sentiment of this message: '<$.message>'",  | 
            |
| 30 | 
                +              "description": "Please rate the sentiment of this message: '{{message}}'",
               | 
            |
| 29 | 31 | 
                "reward": 0.05,  | 
            
| 30 | 32 | 
                "lifetime_in_seconds": "3600",  | 
            
| 31 | 33 | 
                "questions": [  | 
            
                @@ -83,7 +85,7 @@ module Agents  | 
            ||
| 83 | 85 | 
                "title": "Take a poll about some jokes",  | 
            
| 84 | 86 | 
                "instructions": "Please rank these jokes from most funny (5) to least funny (1)",  | 
            
| 85 | 87 | 
                "assignments": 3,  | 
            
| 86 | 
                - "row_template": "<$.joke>"  | 
            |
| 88 | 
                +              "row_template": "{{joke}}"
               | 
            |
| 87 | 89 | 
                },  | 
            
| 88 | 90 | 
                             "hit": {
               | 
            
| 89 | 91 | 
                "assignments": 5,  | 
            
                @@ -168,7 +170,7 @@ module Agents  | 
            ||
| 168 | 170 | 
                           {
               | 
            
| 169 | 171 | 
                'assignments' => 1,  | 
            
| 170 | 172 | 
                'title' => "Sentiment evaluation",  | 
            
| 171 | 
                - 'description' => "Please rate the sentiment of this message: '<$.message>'",  | 
            |
| 173 | 
                +            'description' => "Please rate the sentiment of this message: '{{message}}'",
               | 
            |
| 172 | 174 | 
                'reward' => 0.05,  | 
            
| 173 | 175 | 
                'lifetime_in_seconds' => 24 * 60 * 60,  | 
            
| 174 | 176 | 
                'questions' =>  | 
            
                @@ -332,7 +334,7 @@ module Agents  | 
            ||
| 332 | 334 | 
                                   'name' => "Item #{index + 1}",
               | 
            
| 333 | 335 | 
                'key' => index,  | 
            
| 334 | 336 | 
                'required' => "true",  | 
            
| 335 | 
                - 'question' => Utils.interpolate_jsonpaths(options['poll_options']['row_template'], assignments[index].answers),  | 
            |
| 337 | 
                + 'question' => interpolate_string(options['poll_options']['row_template'], assignments[index].answers),  | 
            |
| 336 | 338 | 
                'selections' => selections  | 
            
| 337 | 339 | 
                }  | 
            
| 338 | 340 | 
                end  | 
            
                @@ -387,9 +389,9 @@ module Agents  | 
            ||
| 387 | 389 | 
                 | 
            
| 388 | 390 | 
                     def create_hit(opts = {})
               | 
            
| 389 | 391 | 
                       payload = opts['payload'] || {}
               | 
            
| 390 | 
                - title = Utils.interpolate_jsonpaths(opts['title'], payload).strip  | 
            |
| 391 | 
                - description = Utils.interpolate_jsonpaths(opts['description'], payload).strip  | 
            |
| 392 | 
                - questions = Utils.recursively_interpolate_jsonpaths(opts['questions'], payload)  | 
            |
| 392 | 
                + title = interpolate_string(opts['title'], payload).strip  | 
            |
| 393 | 
                + description = interpolate_string(opts['description'], payload).strip  | 
            |
| 394 | 
                + questions = interpolate_options(opts['questions'], payload)  | 
            |
| 393 | 395 | 
                hit = RTurk::Hit.create(:title => title) do |hit|  | 
            
| 394 | 396 | 
                hit.max_assignments = (opts['assignments'] || 1).to_i  | 
            
| 395 | 397 | 
                hit.description = description  | 
            
                @@ -2,10 +2,12 @@ require 'pp'  | 
            ||
| 2 | 2 | 
                 | 
            
| 3 | 3 | 
                module Agents  | 
            
| 4 | 4 | 
                class PeakDetectorAgent < Agent  | 
            
| 5 | 
                + include LiquidInterpolatable  | 
            |
| 6 | 
                +  | 
            |
| 5 | 7 | 
                cannot_be_scheduled!  | 
            
| 6 | 8 | 
                 | 
            
| 7 | 9 | 
                description <<-MD  | 
            
| 8 | 
                - Use a PeakDetectorAgent to watch for peaks in an event stream. When a peak is detected, the resulting Event will have a payload message of `message`. You can include extractions in the message, for example: `I saw a bar of: <foo.bar>`  | 
            |
| 10 | 
                +      Use a PeakDetectorAgent to watch for peaks in an event stream.  When a peak is detected, the resulting Event will have a payload message of `message`.  You can include extractions in the message, for example: `I saw a bar of: {{foo.bar}}`, have a look at the [Wiki](https://github.com/cantino/huginn/wiki/Formatting-Events-using-Liquid) for details.
               | 
            |
| 9 | 11 | 
                 | 
            
| 10 | 12 | 
                The `value_path` value is a [JSONPaths](http://goessner.net/articles/JsonPath/) to the value of interest. `group_by_path` is a hash path that will be used to group values, if present.  | 
            
| 11 | 13 | 
                 | 
            
                @@ -67,7 +69,7 @@ module Agents  | 
            ||
| 67 | 69 | 
                if newest_value > average_value + std_multiple * standard_deviation  | 
            
| 68 | 70 | 
                memory['peaks'][group] << newest_time  | 
            
| 69 | 71 | 
                           memory['peaks'][group].reject! { |p| p <= newest_time - window_duration }
               | 
            
| 70 | 
                -          create_event :payload => { 'message' => options['message'], 'peak' => newest_value, 'peak_time' => newest_time, 'grouped_by' => group.to_s }
               | 
            |
| 72 | 
                +          create_event :payload => { 'message' => interpolate_string(options['message'], event.payload), 'peak' => newest_value, 'peak_time' => newest_time, 'grouped_by' => group.to_s }
               | 
            |
| 71 | 73 | 
                end  | 
            
| 72 | 74 | 
                end  | 
            
| 73 | 75 | 
                end  | 
            
                @@ -1,5 +1,7 @@  | 
            ||
| 1 | 1 | 
                module Agents  | 
            
| 2 | 2 | 
                class TriggerAgent < Agent  | 
            
| 3 | 
                + include LiquidInterpolatable  | 
            |
| 4 | 
                +  | 
            |
| 3 | 5 | 
                cannot_be_scheduled!  | 
            
| 4 | 6 | 
                 | 
            
| 5 | 7 | 
                VALID_COMPARISON_TYPES = %w[regex !regex field<value field<=value field==value field!=value field>=value field>value]  | 
            
                @@ -13,7 +15,7 @@ module Agents  | 
            ||
| 13 | 15 | 
                 | 
            
| 14 | 16 | 
                The `value` can be a single value or an array of values. In the case of an array, if one or more values match then the rule matches.  | 
            
| 15 | 17 | 
                 | 
            
| 16 | 
                - All rules must match for the Agent to match. The resulting Event will have a payload message of `message`. You can include extractions in the message, for example: `I saw a bar of: <foo.bar>`  | 
            |
| 18 | 
                + All rules must match for the Agent to match. The resulting Event will have a payload message of `message`. You can use liquid templating in the `message, have a look at the [Wiki](https://github.com/cantino/huginn/wiki/Formatting-Events-using-Liquid) for details.  | 
            |
| 17 | 19 | 
                 | 
            
| 18 | 20 | 
                Set `keep_event` to `true` if you'd like to re-emit the incoming event, optionally merged with 'message' when provided.  | 
            
| 19 | 21 | 
                 | 
            
                @@ -46,7 +48,7 @@ module Agents  | 
            ||
| 46 | 48 | 
                'value' => "foo\\d+bar",  | 
            
| 47 | 49 | 
                'path' => "topkey.subkey.subkey.goal",  | 
            
| 48 | 50 | 
                }],  | 
            
| 49 | 
                - 'message' => "Looks like your pattern matched in '<value>'!"  | 
            |
| 51 | 
                +        'message' => "Looks like your pattern matched in '{{value}}'!"
               | 
            |
| 50 | 52 | 
                }  | 
            
| 51 | 53 | 
                end  | 
            
| 52 | 54 | 
                 | 
            
                @@ -88,9 +90,9 @@ module Agents  | 
            ||
| 88 | 90 | 
                if match  | 
            
| 89 | 91 | 
                if keep_event?  | 
            
| 90 | 92 | 
                payload = event.payload.dup  | 
            
| 91 | 
                - payload['message'] = make_message(event[:payload]) if options['message'].present?  | 
            |
| 93 | 
                + payload['message'] = interpolate_string(options['message'], event.payload) if options['message'].present?  | 
            |
| 92 | 94 | 
                else  | 
            
| 93 | 
                -            payload = { 'message' => make_message(event[:payload]) }
               | 
            |
| 95 | 
                +            payload = { 'message' => interpolate_string(options['message'], event.payload) }
               | 
            |
| 94 | 96 | 
                end  | 
            
| 95 | 97 | 
                 | 
            
| 96 | 98 | 
                create_event :payload => payload  | 
            
                @@ -1,15 +0,0 @@  | 
            ||
| 1 | 
                -class MigrateHipchatAndEfAgentToLiquid < ActiveRecord::Migration  | 
            |
| 2 | 
                - def up  | 
            |
| 3 | 
                - Agent.where(:type => 'Agents::HipchatAgent').each do |agent|  | 
            |
| 4 | 
                - LiquidMigrator.convert_all_agent_options(agent)  | 
            |
| 5 | 
                - end  | 
            |
| 6 | 
                - Agent.where(:type => 'Agents::EventFormattingAgent').each do |agent|  | 
            |
| 7 | 
                -      agent.options['instructions'] = LiquidMigrator.convert_hash(agent.options['instructions'], {:merge_path_attributes => true, :leading_dollarsign_is_jsonpath => true})
               | 
            |
| 8 | 
                - agent.save  | 
            |
| 9 | 
                - end  | 
            |
| 10 | 
                - end  | 
            |
| 11 | 
                -  | 
            |
| 12 | 
                - def down  | 
            |
| 13 | 
                - raise ActiveRecord::IrreversibleMigration, "Cannot revert migration to Liquid templating"  | 
            |
| 14 | 
                - end  | 
            |
| 15 | 
                -end  | 
            
                @@ -1,11 +0,0 @@  | 
            ||
| 1 | 
                -class MigratePushbulletAgentToLiquid < ActiveRecord::Migration  | 
            |
| 2 | 
                - def up  | 
            |
| 3 | 
                - Agent.where(:type => 'Agents::PushbulletAgent').each do |agent|  | 
            |
| 4 | 
                - LiquidMigrator.convert_all_agent_options(agent)  | 
            |
| 5 | 
                - end  | 
            |
| 6 | 
                - end  | 
            |
| 7 | 
                -  | 
            |
| 8 | 
                - def down  | 
            |
| 9 | 
                - raise ActiveRecord::IrreversibleMigration, "Cannot revert migration to Liquid templating"  | 
            |
| 10 | 
                - end  | 
            |
| 11 | 
                -end  | 
            
                @@ -1,11 +0,0 @@  | 
            ||
| 1 | 
                -class MigrateJabberAgentToLiquid < ActiveRecord::Migration  | 
            |
| 2 | 
                - def up  | 
            |
| 3 | 
                - Agent.where(:type => 'Agents::JabberAgent').each do |agent|  | 
            |
| 4 | 
                - LiquidMigrator.convert_all_agent_options(agent)  | 
            |
| 5 | 
                - end  | 
            |
| 6 | 
                - end  | 
            |
| 7 | 
                -  | 
            |
| 8 | 
                - def down  | 
            |
| 9 | 
                - raise ActiveRecord::IrreversibleMigration, "Cannot revert migration to Liquid templating"  | 
            |
| 10 | 
                - end  | 
            |
| 11 | 
                -end  | 
            
                @@ -1,11 +0,0 @@  | 
            ||
| 1 | 
                -class MigrateDataOutputAgentToLiquid < ActiveRecord::Migration  | 
            |
| 2 | 
                - def up  | 
            |
| 3 | 
                - Agent.where(:type => 'Agents::DataOutputAgent').each do |agent|  | 
            |
| 4 | 
                - LiquidMigrator.convert_all_agent_options(agent)  | 
            |
| 5 | 
                - end  | 
            |
| 6 | 
                - end  | 
            |
| 7 | 
                -  | 
            |
| 8 | 
                - def down  | 
            |
| 9 | 
                - raise ActiveRecord::IrreversibleMigration, "Cannot revert migration to Liquid templating"  | 
            |
| 10 | 
                - end  | 
            |
| 11 | 
                -end  | 
            
                @@ -1,12 +0,0 @@  | 
            ||
| 1 | 
                -class MigrateTranslationAgentToLiquid < ActiveRecord::Migration  | 
            |
| 2 | 
                - def up  | 
            |
| 3 | 
                - Agent.where(:type => 'Agents::TranslationAgent').each do |agent|  | 
            |
| 4 | 
                -      agent.options['content'] = LiquidMigrator.convert_hash(agent.options['content'], {:merge_path_attributes => true, :leading_dollarsign_is_jsonpath => true})
               | 
            |
| 5 | 
                - agent.save  | 
            |
| 6 | 
                - end  | 
            |
| 7 | 
                - end  | 
            |
| 8 | 
                -  | 
            |
| 9 | 
                - def down  | 
            |
| 10 | 
                - raise ActiveRecord::IrreversibleMigration, "Cannot revert migration to Liquid templating"  | 
            |
| 11 | 
                - end  | 
            |
| 12 | 
                -end  | 
            
                @@ -1,14 +0,0 @@  | 
            ||
| 1 | 
                -class MigrateTwitterPublishAgentToLiquid < ActiveRecord::Migration  | 
            |
| 2 | 
                - def up  | 
            |
| 3 | 
                - Agent.where(:type => 'Agents::TwitterPublishAgent').each do |agent|  | 
            |
| 4 | 
                -      if (message = agent.options.delete('message_path')).present?
               | 
            |
| 5 | 
                -        agent.options['message'] = "{{#{message}}}"
               | 
            |
| 6 | 
                - agent.save  | 
            |
| 7 | 
                - end  | 
            |
| 8 | 
                - end  | 
            |
| 9 | 
                - end  | 
            |
| 10 | 
                -  | 
            |
| 11 | 
                - def down  | 
            |
| 12 | 
                - raise ActiveRecord::IrreversibleMigration, "Cannot revert migration to Liquid templating"  | 
            |
| 13 | 
                - end  | 
            |
| 14 | 
                -end  | 
            
                @@ -0,0 +1,45 @@  | 
            ||
| 1 | 
                +class MigrateAgentsToLiquidTemplating < ActiveRecord::Migration  | 
            |
| 2 | 
                + def up  | 
            |
| 3 | 
                + Agent.where(:type => 'Agents::HipchatAgent').each do |agent|  | 
            |
| 4 | 
                + LiquidMigrator.convert_all_agent_options(agent)  | 
            |
| 5 | 
                + end  | 
            |
| 6 | 
                + Agent.where(:type => 'Agents::EventFormattingAgent').each do |agent|  | 
            |
| 7 | 
                +      agent.options['instructions'] = LiquidMigrator.convert_hash(agent.options['instructions'], {:merge_path_attributes => true, :leading_dollarsign_is_jsonpath => true})
               | 
            |
| 8 | 
                + agent.save  | 
            |
| 9 | 
                + end  | 
            |
| 10 | 
                + Agent.where(:type => 'Agents::PushbulletAgent').each do |agent|  | 
            |
| 11 | 
                + LiquidMigrator.convert_all_agent_options(agent)  | 
            |
| 12 | 
                + end  | 
            |
| 13 | 
                + Agent.where(:type => 'Agents::JabberAgent').each do |agent|  | 
            |
| 14 | 
                + LiquidMigrator.convert_all_agent_options(agent)  | 
            |
| 15 | 
                + end  | 
            |
| 16 | 
                + Agent.where(:type => 'Agents::DataOutputAgent').each do |agent|  | 
            |
| 17 | 
                + LiquidMigrator.convert_all_agent_options(agent)  | 
            |
| 18 | 
                + end  | 
            |
| 19 | 
                + Agent.where(:type => 'Agents::TranslationAgent').each do |agent|  | 
            |
| 20 | 
                +      agent.options['content'] = LiquidMigrator.convert_hash(agent.options['content'], {:merge_path_attributes => true, :leading_dollarsign_is_jsonpath => true})
               | 
            |
| 21 | 
                + agent.save  | 
            |
| 22 | 
                + end  | 
            |
| 23 | 
                + Agent.where(:type => 'Agents::TwitterPublishAgent').each do |agent|  | 
            |
| 24 | 
                +      if (message = agent.options.delete('message_path')).present?
               | 
            |
| 25 | 
                +        agent.options['message'] = "{{#{message}}}"
               | 
            |
| 26 | 
                + agent.save  | 
            |
| 27 | 
                + end  | 
            |
| 28 | 
                + end  | 
            |
| 29 | 
                + Agent.where(:type => 'Agents::TriggerAgent').each do |agent|  | 
            |
| 30 | 
                + agent.options['message'] = LiquidMigrator.convert_make_message(agent.options['message'])  | 
            |
| 31 | 
                + agent.save  | 
            |
| 32 | 
                + end  | 
            |
| 33 | 
                + Agent.where(:type => 'Agents::PeakDetectorAgent').each do |agent|  | 
            |
| 34 | 
                + agent.options['message'] = LiquidMigrator.convert_make_message(agent.options['message'])  | 
            |
| 35 | 
                + agent.save  | 
            |
| 36 | 
                + end  | 
            |
| 37 | 
                + Agent.where(:type => 'Agents::HumanTaskAgent').each do |agent|  | 
            |
| 38 | 
                + LiquidMigrator.convert_all_agent_options(agent)  | 
            |
| 39 | 
                + end  | 
            |
| 40 | 
                + end  | 
            |
| 41 | 
                +  | 
            |
| 42 | 
                + def down  | 
            |
| 43 | 
                + raise ActiveRecord::IrreversibleMigration, "Cannot revert migration to Liquid templating"  | 
            |
| 44 | 
                + end  | 
            |
| 45 | 
                +end  | 
            
                @@ -22,7 +22,13 @@ module LiquidMigrator  | 
            ||
| 22 | 22 | 
                when 'ActiveSupport::HashWithIndifferentAccess'  | 
            
| 23 | 23 | 
                hash[key] = convert_hash(hash[key], options)  | 
            
| 24 | 24 | 
                when 'Array'  | 
            
| 25 | 
                -          hash[key] = hash[key].collect { |k| convert_string(k, options[:leading_dollarsign_is_jsonpath])}
               | 
            |
| 25 | 
                +          hash[key] = hash[key].collect { |k|
               | 
            |
| 26 | 
                + if k.class == String  | 
            |
| 27 | 
                + convert_string(k, options[:leading_dollarsign_is_jsonpath])  | 
            |
| 28 | 
                + else  | 
            |
| 29 | 
                + convert_hash(k, options)  | 
            |
| 30 | 
                + end  | 
            |
| 31 | 
                + }  | 
            |
| 26 | 32 | 
                end  | 
            
| 27 | 33 | 
                end  | 
            
| 28 | 34 | 
                # remove the unneeded *_path attributes  | 
            
                @@ -50,6 +56,10 @@ module LiquidMigrator  | 
            ||
| 50 | 56 | 
                end  | 
            
| 51 | 57 | 
                end  | 
            
| 52 | 58 | 
                 | 
            
| 59 | 
                + def self.convert_make_message(string)  | 
            |
| 60 | 
                +    string.gsub(/<([^>]+)>/, "{{\\1}}")
               | 
            |
| 61 | 
                + end  | 
            |
| 62 | 
                +  | 
            |
| 53 | 63 | 
                def self.convert_json_path(string, filter = "")  | 
            
| 54 | 64 | 
                check_path(string)  | 
            
| 55 | 65 | 
                if string.start_with? '$.'  | 
            
                @@ -56,6 +56,14 @@ describe LiquidMigrator do  | 
            ||
| 56 | 56 | 
                end  | 
            
| 57 | 57 | 
                end  | 
            
| 58 | 58 | 
                 | 
            
| 59 | 
                + describe "migrating the 'make_message' format" do  | 
            |
| 60 | 
                + it "should work" do  | 
            |
| 61 | 
                +      LiquidMigrator.convert_make_message('<message>').should == '{{message}}'
               | 
            |
| 62 | 
                +      LiquidMigrator.convert_make_message('<new.message>').should == '{{new.message}}'
               | 
            |
| 63 | 
                +      LiquidMigrator.convert_make_message('Hello <world>. How is <nested.life>').should == 'Hello {{world}}. How is {{nested.life}}'
               | 
            |
| 64 | 
                + end  | 
            |
| 65 | 
                + end  | 
            |
| 66 | 
                +  | 
            |
| 59 | 67 | 
                describe "migrating an actual agent" do  | 
            
| 60 | 68 | 
                before do  | 
            
| 61 | 69 | 
                       valid_params = {
               | 
            
                @@ -99,5 +107,50 @@ describe LiquidMigrator do  | 
            ||
| 99 | 107 | 
                       expect { LiquidMigrator.convert_all_agent_options(@agent) }.
               | 
            
| 100 | 108 | 
                         to raise_error("JSONPath '$.very.complex[*]' is too complex, please check your migration.")
               | 
            
| 101 | 109 | 
                end  | 
            
| 110 | 
                +  | 
            |
| 111 | 
                + it "should work with the human task agent" do  | 
            |
| 112 | 
                +      valid_params = {
               | 
            |
| 113 | 
                + 'expected_receive_period_in_days' => 2,  | 
            |
| 114 | 
                + 'trigger_on' => "event",  | 
            |
| 115 | 
                + 'hit' =>  | 
            |
| 116 | 
                +          {
               | 
            |
| 117 | 
                + 'assignments' => 1,  | 
            |
| 118 | 
                + 'title' => "Sentiment evaluation",  | 
            |
| 119 | 
                + 'description' => "Please rate the sentiment of this message: '<$.message>'",  | 
            |
| 120 | 
                + 'reward' => 0.05,  | 
            |
| 121 | 
                + 'lifetime_in_seconds' => 24 * 60 * 60,  | 
            |
| 122 | 
                + 'questions' =>  | 
            |
| 123 | 
                + [  | 
            |
| 124 | 
                +                {
               | 
            |
| 125 | 
                + 'type' => "selection",  | 
            |
| 126 | 
                + 'key' => "sentiment",  | 
            |
| 127 | 
                + 'name' => "Sentiment",  | 
            |
| 128 | 
                + 'required' => "true",  | 
            |
| 129 | 
                + 'question' => "Please select the best sentiment value:",  | 
            |
| 130 | 
                + 'selections' =>  | 
            |
| 131 | 
                + [  | 
            |
| 132 | 
                +                      { 'key' => "happy", 'text' => "Happy" },
               | 
            |
| 133 | 
                +                      { 'key' => "sad", 'text' => "Sad" },
               | 
            |
| 134 | 
                +                      { 'key' => "neutral", 'text' => "Neutral" }
               | 
            |
| 135 | 
                + ]  | 
            |
| 136 | 
                + },  | 
            |
| 137 | 
                +                {
               | 
            |
| 138 | 
                + 'type' => "free_text",  | 
            |
| 139 | 
                + 'key' => "feedback",  | 
            |
| 140 | 
                + 'name' => "Have any feedback for us?",  | 
            |
| 141 | 
                + 'required' => "false",  | 
            |
| 142 | 
                + 'question' => "Feedback",  | 
            |
| 143 | 
                + 'default' => "Type here...",  | 
            |
| 144 | 
                + 'min_length' => "2",  | 
            |
| 145 | 
                + 'max_length' => "2000"  | 
            |
| 146 | 
                + }  | 
            |
| 147 | 
                + ]  | 
            |
| 148 | 
                + }  | 
            |
| 149 | 
                + }  | 
            |
| 150 | 
                + @agent = Agents::HumanTaskAgent.new(:name => "somename", :options => valid_params)  | 
            |
| 151 | 
                + @agent.user = users(:jane)  | 
            |
| 152 | 
                + LiquidMigrator.convert_all_agent_options(@agent)  | 
            |
| 153 | 
                +      @agent.reload.options['hit']['description'].should == "Please rate the sentiment of this message: '{{message}}'"
               | 
            |
| 154 | 
                + end  | 
            |
| 102 | 155 | 
                end  | 
            
| 103 | 156 | 
                end  | 
            
                @@ -1,6 +1,9 @@  | 
            ||
| 1 | 1 | 
                require 'spec_helper'  | 
            
| 2 | 
                +require 'models/concerns/liquid_interpolatable'  | 
            |
| 2 | 3 | 
                 | 
            
| 3 | 4 | 
                describe Agents::HumanTaskAgent do  | 
            
| 5 | 
                + it_behaves_like LiquidInterpolatable  | 
            |
| 6 | 
                +  | 
            |
| 4 | 7 | 
                before do  | 
            
| 5 | 8 | 
                @checker = Agents::HumanTaskAgent.new(:name => "my human task agent")  | 
            
| 6 | 9 | 
                @checker.options = @checker.default_options  | 
            
                @@ -116,19 +119,19 @@ describe Agents::HumanTaskAgent do  | 
            ||
| 116 | 119 | 
                       @checker.options['poll_options'] = { 'title' => "Take a poll about jokes",
               | 
            
| 117 | 120 | 
                'instructions' => "Rank these by how funny they are",  | 
            
| 118 | 121 | 
                'assignments' => 3,  | 
            
| 119 | 
                - 'row_template' => "<$.joke>" }  | 
            |
| 122 | 
                +                                           'row_template' => "{{joke}}" }
               | 
            |
| 120 | 123 | 
                @checker.should be_valid  | 
            
| 121 | 124 | 
                       @checker.options['poll_options'] = { 'instructions' => "Rank these by how funny they are",
               | 
            
| 122 | 125 | 
                'assignments' => 3,  | 
            
| 123 | 
                - 'row_template' => "<$.joke>" }  | 
            |
| 126 | 
                +                                           'row_template' => "{{joke}}" }
               | 
            |
| 124 | 127 | 
                @checker.should_not be_valid  | 
            
| 125 | 128 | 
                       @checker.options['poll_options'] = { 'title' => "Take a poll about jokes",
               | 
            
| 126 | 129 | 
                'assignments' => 3,  | 
            
| 127 | 
                - 'row_template' => "<$.joke>" }  | 
            |
| 130 | 
                +                                           'row_template' => "{{joke}}" }
               | 
            |
| 128 | 131 | 
                @checker.should_not be_valid  | 
            
| 129 | 132 | 
                       @checker.options['poll_options'] = { 'title' => "Take a poll about jokes",
               | 
            
| 130 | 133 | 
                'instructions' => "Rank these by how funny they are",  | 
            
| 131 | 
                - 'row_template' => "<$.joke>" }  | 
            |
| 134 | 
                +                                           'row_template' => "{{joke}}" }
               | 
            |
| 132 | 135 | 
                @checker.should_not be_valid  | 
            
| 133 | 136 | 
                       @checker.options['poll_options'] = { 'title' => "Take a poll about jokes",
               | 
            
| 134 | 137 | 
                'instructions' => "Rank these by how funny they are",  | 
            
                @@ -207,9 +210,9 @@ describe Agents::HumanTaskAgent do  | 
            ||
| 207 | 210 | 
                 | 
            
| 208 | 211 | 
                describe "creating hits" do  | 
            
| 209 | 212 | 
                it "can create HITs based on events, interpolating their values" do  | 
            
| 210 | 
                - @checker.options['hit']['title'] = "Hi <.name>"  | 
            |
| 211 | 
                - @checker.options['hit']['description'] = "Make something for <.name>"  | 
            |
| 212 | 
                - @checker.options['hit']['questions'][0]['name'] = "<.name> Question 1"  | 
            |
| 213 | 
                +      @checker.options['hit']['title'] = "Hi {{name}}"
               | 
            |
| 214 | 
                +      @checker.options['hit']['description'] = "Make something for {{name}}"
               | 
            |
| 215 | 
                +      @checker.options['hit']['questions'][0]['name'] = "{{name}} Question 1"
               | 
            |
| 213 | 216 | 
                 | 
            
| 214 | 217 | 
                question_form = nil  | 
            
| 215 | 218 | 
                hitInterface = OpenStruct.new  | 
            
                @@ -232,7 +235,7 @@ describe Agents::HumanTaskAgent do  | 
            ||
| 232 | 235 | 
                end  | 
            
| 233 | 236 | 
                 | 
            
| 234 | 237 | 
                it "works without an event too" do  | 
            
| 235 | 
                - @checker.options['hit']['title'] = "Hi <.name>"  | 
            |
| 238 | 
                +      @checker.options['hit']['title'] = "Hi {{name}}"
               | 
            |
| 236 | 239 | 
                hitInterface = OpenStruct.new  | 
            
| 237 | 240 | 
                hitInterface.id = 123  | 
            
| 238 | 241 | 
                mock(hitInterface).question_form(instance_of Agents::HumanTaskAgent::AgentQuestionForm)  | 
            
                @@ -483,7 +486,7 @@ describe Agents::HumanTaskAgent do  | 
            ||
| 483 | 486 | 
                'title' => "Hi!",  | 
            
| 484 | 487 | 
                'instructions' => "hello!",  | 
            
| 485 | 488 | 
                'assignments' => 2,  | 
            
| 486 | 
                - 'row_template' => "This is <.sentiment>"  | 
            |
| 489 | 
                +          'row_template' => "This is {{sentiment}}"
               | 
            |
| 487 | 490 | 
                }  | 
            
| 488 | 491 | 
                @event.save!  | 
            
| 489 | 492 | 
                         mock(RTurk::GetReviewableHITs).create { mock!.hit_ids { %w[JH3132836336DHG JH39AA63836DHG JH39AA63836DH12345] } }
               | 
            
                @@ -1,6 +1,9 @@  | 
            ||
| 1 | 1 | 
                require 'spec_helper'  | 
            
| 2 | 
                +require 'models/concerns/liquid_interpolatable'  | 
            |
| 2 | 3 | 
                 | 
            
| 3 | 4 | 
                describe Agents::PeakDetectorAgent do  | 
            
| 5 | 
                + it_behaves_like LiquidInterpolatable  | 
            |
| 6 | 
                +  | 
            |
| 4 | 7 | 
                before do  | 
            
| 5 | 8 | 
                     @valid_params = {
               | 
            
| 6 | 9 | 
                'name' => "my peak detector agent",  | 
            
                @@ -1,6 +1,9 @@  | 
            ||
| 1 | 1 | 
                require 'spec_helper'  | 
            
| 2 | 
                +require 'models/concerns/liquid_interpolatable'  | 
            |
| 2 | 3 | 
                 | 
            
| 3 | 4 | 
                describe Agents::TriggerAgent do  | 
            
| 5 | 
                + it_behaves_like LiquidInterpolatable  | 
            |
| 6 | 
                +  | 
            |
| 4 | 7 | 
                before do  | 
            
| 5 | 8 | 
                     @valid_params = {
               | 
            
| 6 | 9 | 
                'name' => "my trigger agent",  | 
            
                @@ -11,7 +14,7 @@ describe Agents::TriggerAgent do  | 
            ||
| 11 | 14 | 
                'value' => "a\\db",  | 
            
| 12 | 15 | 
                'path' => "foo.bar.baz",  | 
            
| 13 | 16 | 
                }],  | 
            
| 14 | 
                - 'message' => "I saw '<foo.bar.baz>' from <name>"  | 
            |
| 17 | 
                +        'message' => "I saw '{{foo.bar.baz}}' from {{name}}"
               | 
            |
| 15 | 18 | 
                }  | 
            
| 16 | 19 | 
                }  | 
            
| 17 | 20 | 
                 | 
            
                @@ -20,7 +20,7 @@ shared_examples_for LiquidInterpolatable do  | 
            ||
| 20 | 20 | 
                 | 
            
| 21 | 21 | 
                describe "interpolating liquid templates" do  | 
            
| 22 | 22 | 
                it "should work" do  | 
            
| 23 | 
                -      @checker.send(:interpolate_options, @checker.options, @event.payload).should == {
               | 
            |
| 23 | 
                +      @checker.interpolate_options(@checker.options, @event.payload).should == {
               | 
            |
| 24 | 24 | 
                "normal" => "just some normal text",  | 
            
| 25 | 25 | 
                "variable" => "hello",  | 
            
| 26 | 26 | 
                "text" => "Some test with an embedded hello",  | 
            
                @@ -28,6 +28,26 @@ shared_examples_for LiquidInterpolatable do  | 
            ||
| 28 | 28 | 
                }  | 
            
| 29 | 29 | 
                end  | 
            
| 30 | 30 | 
                 | 
            
| 31 | 
                + it "hsould work with arrays", focus: true do  | 
            |
| 32 | 
                +      @checker.options = {"value" => ["{{variable}}", "Much array", "Hey, {{hello_world}}"]}
               | 
            |
| 33 | 
                +      @checker.interpolate_options(@checker.options, @event.payload).should == {
               | 
            |
| 34 | 
                + "value" => ["hello", "Much array", "Hey, Hello world"]  | 
            |
| 35 | 
                + }  | 
            |
| 36 | 
                + end  | 
            |
| 37 | 
                +  | 
            |
| 38 | 
                + it "should work recursively" do  | 
            |
| 39 | 
                +      @checker.options['hash'] = {'recursive' => "{{variable}}"}
               | 
            |
| 40 | 
                +      @checker.options['indifferent_hash'] = ActiveSupport::HashWithIndifferentAccess.new({'recursive' => "{{variable}}"})
               | 
            |
| 41 | 
                +      @checker.interpolate_options(@checker.options, @event.payload).should == {
               | 
            |
| 42 | 
                + "normal" => "just some normal text",  | 
            |
| 43 | 
                + "variable" => "hello",  | 
            |
| 44 | 
                + "text" => "Some test with an embedded hello",  | 
            |
| 45 | 
                + "escape" => "This should be Hello+world",  | 
            |
| 46 | 
                +          "hash" => {'recursive' => 'hello'},
               | 
            |
| 47 | 
                +          "indifferent_hash" => {'recursive' => 'hello'},
               | 
            |
| 48 | 
                + }  | 
            |
| 49 | 
                + end  | 
            |
| 50 | 
                +  | 
            |
| 31 | 51 | 
                it "should work for strings" do  | 
            
| 32 | 52 | 
                       @checker.send(:interpolate_string, "{{variable}}", @event.payload).should == "hello"
               | 
            
| 33 | 53 | 
                       @checker.send(:interpolate_string, "{{variable}} you", @event.payload).should == "hello you"
               |