robut 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,10 +2,10 @@ require 'yaml'
2
2
 
3
3
  # A store backed by a persistent on-disk yaml file.
4
4
  class Robut::Storage::YamlStore < Robut::Storage::Base
5
-
5
+
6
6
  class << self
7
7
 
8
- # The path to the file this store will persist to.
8
+ # The path to the file this store will persist to.
9
9
  attr_reader :file
10
10
 
11
11
  # Sets the path to the file this store will persist to, and forces
@@ -16,7 +16,7 @@ class Robut::Storage::YamlStore < Robut::Storage::Base
16
16
  @file
17
17
  end
18
18
 
19
- # Sets the key +k+ to the value +v+
19
+ # Sets the key +k+ to the value +v+
20
20
  def []=(k, v)
21
21
  internal[k] = v
22
22
  persist!
@@ -32,20 +32,26 @@ class Robut::Storage::YamlStore < Robut::Storage::Base
32
32
 
33
33
  # The internal in-memory representation of the yaml file
34
34
  def internal
35
- @internal ||= begin
36
- YAML.load_file(file) rescue {}
37
- end
35
+ @internal ||= load_from_file
38
36
  end
39
-
37
+
40
38
  # Persists the data in this store to disk. Throws an exception if
41
39
  # we don't have a file set.
42
40
  def persist!
43
41
  raise "Robut::Storage::YamlStore.file must be set" unless file
44
- f = File.open(file, "w")
45
- f.puts internal.to_yaml
46
- f.close
42
+ File.open(file, "w") do |f|
43
+ f.puts internal.to_yaml
44
+ end
47
45
  end
48
-
46
+
47
+ def load_from_file
48
+ begin
49
+ store = YAML.load_file(file)
50
+ rescue Errno::ENOENT
51
+ end
52
+
53
+ store || Hash.new
54
+ end
55
+
49
56
  end
50
-
51
57
  end
data/lib/robut/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  module Robut # :nodoc:
2
2
  # Robut's version number.
3
- VERSION = "0.2.1"
3
+ VERSION = "0.3.0"
4
4
  end
@@ -0,0 +1 @@
1
+ <?xml version="1.0"?><xml_api_reply version="1"><weather module_id="0" tab_id="0" mobile_row="0" mobile_zipped="1" row="0" section="0" ><problem_cause data=""/></weather></xml_api_reply>
@@ -0,0 +1 @@
1
+ <?xml version="1.0"?><xml_api_reply version="1"><weather module_id="0" tab_id="0" mobile_row="0" mobile_zipped="1" row="0" section="0" ><forecast_information><city data="Las Vegas, NV"/><postal_code data="Las Vegas"/><latitude_e6 data=""/><longitude_e6 data=""/><forecast_date data="2011-05-31"/><current_date_time data="2011-05-31 19:50:12 +0000"/><unit_system data="US"/></forecast_information><current_conditions><condition data="Mostly Cloudy"/><temp_f data="83"/><temp_c data="28"/><humidity data="Humidity: 8%"/><icon data="http://g0.gstatic.com/images/icons/onebox/weather_mostlycloudy-40.gif"/><wind_condition data="Wind: S at 9 mph"/></current_conditions><forecast_conditions><day_of_week data="Tue"/><low data="66"/><high data="91"/><icon data="http://g0.gstatic.com/images/icons/onebox/weather_partlycloudy-40.gif"/><condition data="Partly Cloudy"/></forecast_conditions><forecast_conditions><day_of_week data="Wed"/><low data="60"/><high data="86"/><icon data="http://g0.gstatic.com/images/icons/onebox/weather_partlycloudy-40.gif"/><condition data="Partly Cloudy"/></forecast_conditions><forecast_conditions><day_of_week data="Thu"/><low data="63"/><high data="80"/><icon data="http://g0.gstatic.com/images/icons/onebox/weather_sunny-40.gif"/><condition data="Sunny"/></forecast_conditions><forecast_conditions><day_of_week data="Fri"/><low data="70"/><high data="84"/><icon data="http://g0.gstatic.com/images/icons/onebox/weather_partlycloudy-40.gif"/><condition data="Partly Cloudy"/></forecast_conditions></weather></xml_api_reply>
@@ -0,0 +1 @@
1
+ <?xml version="1.0"?><xml_api_reply version="1"><weather module_id="0" tab_id="0" mobile_row="0" mobile_zipped="1" row="0" section="0" ><forecast_information><city data="Seattle, WA"/><postal_code data="Seattle"/><latitude_e6 data=""/><longitude_e6 data=""/><forecast_date data="2011-05-23"/><current_date_time data="2011-05-23 22:52:57 +0000"/><unit_system data="US"/></forecast_information><current_conditions><condition data="Mostly Cloudy"/><temp_f data="58"/><temp_c data="14"/><humidity data="Humidity: 45%"/><icon data="/ig/images/weather/mostly_cloudy.gif"/><wind_condition data="Wind: E at 7 mph"/></current_conditions><forecast_conditions><day_of_week data="Mon"/><low data="48"/><high data="59"/><icon data="/ig/images/weather/partly_cloudy.gif"/><condition data="Partly Cloudy"/></forecast_conditions><forecast_conditions><day_of_week data="Tue"/><low data="51"/><high data="67"/><icon data="/ig/images/weather/partly_cloudy.gif"/><condition data="Partly Cloudy"/></forecast_conditions><forecast_conditions><day_of_week data="Wed"/><low data="49"/><high data="61"/><icon data="/ig/images/weather/rain.gif"/><condition data="Showers"/></forecast_conditions><forecast_conditions><day_of_week data="Thu"/><low data="49"/><high data="57"/><icon data="/ig/images/weather/rain.gif"/><condition data="Showers"/></forecast_conditions></weather></xml_api_reply>
@@ -0,0 +1 @@
1
+ <?xml version="1.0"?><xml_api_reply version="1"><weather module_id="0" tab_id="0" mobile_row="0" mobile_zipped="1" row="0" section="0" ><forecast_information><city data="Tacoma, WA"/><postal_code data="tacoma"/><latitude_e6 data=""/><longitude_e6 data=""/><forecast_date data="2011-05-23"/><current_date_time data="2011-05-23 22:46:28 +0000"/><unit_system data="US"/></forecast_information><current_conditions><condition data="Cloudy"/><temp_f data="60"/><temp_c data="16"/><humidity data="Humidity: 51%"/><icon data="/ig/images/weather/cloudy.gif"/><wind_condition data="Wind: N at 4 mph"/></current_conditions><forecast_conditions><day_of_week data="Mon"/><low data="45"/><high data="61"/><icon data="/ig/images/weather/rain.gif"/><condition data="Showers"/></forecast_conditions><forecast_conditions><day_of_week data="Tue"/><low data="50"/><high data="67"/><icon data="/ig/images/weather/partly_cloudy.gif"/><condition data="Partly Cloudy"/></forecast_conditions><forecast_conditions><day_of_week data="Wed"/><low data="48"/><high data="59"/><icon data="/ig/images/weather/rain.gif"/><condition data="Showers"/></forecast_conditions><forecast_conditions><day_of_week data="Thu"/><low data="48"/><high data="57"/><icon data="/ig/images/weather/rain.gif"/><condition data="Showers"/></forecast_conditions></weather></xml_api_reply>
@@ -1,6 +1,7 @@
1
1
  require 'test_helper'
2
2
 
3
3
  class SimplePlugin
4
+ include Robut::Plugin
4
5
  attr_accessor :run
5
6
 
6
7
  def initialize(*args)
@@ -13,7 +14,9 @@ class SimplePlugin
13
14
  end
14
15
  end
15
16
 
16
- class ReplyToUserPlugin < Robut::Plugin::Base
17
+ class ReplyToUserPlugin
18
+ include Robut::Plugin
19
+
17
20
  def initialize(*args)
18
21
  super(*args)
19
22
  end
@@ -23,7 +26,9 @@ class ReplyToUserPlugin < Robut::Plugin::Base
23
26
  end
24
27
  end
25
28
 
26
- class ReplyToRoomPlugin < Robut::Plugin::Base
29
+ class ReplyToRoomPlugin
30
+ include Robut::Plugin
31
+
27
32
  def initialize(*args)
28
33
  super(*args)
29
34
  end
@@ -39,7 +44,7 @@ class ReplyMock
39
44
  def initialize
40
45
  @messages = []
41
46
  end
42
-
47
+
43
48
  def send(message)
44
49
  @messages << message
45
50
  end
@@ -96,7 +101,7 @@ class ConnectionTest < Test::Unit::TestCase
96
101
  })
97
102
  }
98
103
  })
99
-
104
+
100
105
  @connection.handle_message(plugins(@connection), Time.now, 'justin WEISS', 'Test Message')
101
106
  message = @connection.client.messages.first
102
107
  assert_equal(justin, message.to.to_s)
@@ -119,5 +124,5 @@ class ConnectionTest < Test::Unit::TestCase
119
124
  def plugins(connection, sender = nil)
120
125
  Robut::Plugin.plugins.map { |p| p.new(connection, sender) }
121
126
  end
122
-
127
+
123
128
  end
@@ -0,0 +1,75 @@
1
+ require 'test_helper'
2
+ require 'robut/plugin/alias'
3
+ require 'robut/plugin/echo'
4
+
5
+ class Robut::Plugin::AliasTest < Test::Unit::TestCase
6
+
7
+ def setup
8
+ @connection = Robut::ConnectionMock.new
9
+ @plugin = Robut::Plugin::Alias.new(@connection)
10
+ @plugin.aliases = {}
11
+ end
12
+
13
+ def test_aliases_this_to_that
14
+ @plugin.handle(Time.now, "@john", "@robut alias w weather?")
15
+ assert_equal 'weather?', @plugin.aliases['w']
16
+ end
17
+
18
+ def test_thinks_this_is_that
19
+ @plugin.handle(Time.now, "@john", "@robut alias this @robut echo that")
20
+ @plugin.handle(Time.now, "@john", "this")
21
+ message = @plugin.connection.messages.first
22
+ assert_equal "@john", message[1]
23
+ assert_equal "@robut echo that", message[2]
24
+ end
25
+
26
+ def test_doesnt_alias_when_it_shouldnt
27
+ @plugin.handle(Time.now, "@john", "@robut somthing alias w weather?")
28
+ assert @plugin.aliases.empty?
29
+ end
30
+
31
+ def test_can_alias_with_quotes
32
+ @plugin.handle(Time.now, "@john", '@robut alias "long string" "some other long string"')
33
+ assert_equal 'some other long string', @plugin.aliases['long string']
34
+ end
35
+
36
+ def test_can_apply_aliases_with_quotes
37
+ @plugin.handle(Time.now, "@john", '@robut alias "long string" "some other long string"')
38
+ @plugin.handle(Time.now, "@john", "long string")
39
+ message = @plugin.connection.messages.first
40
+ assert_equal "@john", message[1]
41
+ assert_equal "some other long string", message[2]
42
+ end
43
+
44
+ def test_aliases_can_be_removed
45
+ @plugin.handle(Time.now, "@john", "@robut alias this that")
46
+ @plugin.handle(Time.now, "@john", "@robut remove alias this")
47
+ assert @plugin.aliases.empty?
48
+ end
49
+
50
+ def test_remove_aliases_doesnt_choke_on_missing_key
51
+ @plugin.handle(Time.now, "@john", "@robut alias this that")
52
+ @plugin.handle(Time.now, "@john", "@robut remove alias")
53
+ assert_equal({'this' => 'that'}, @plugin.aliases)
54
+ end
55
+
56
+ def test_remove_alias_handles_quotes
57
+ @plugin.handle(Time.now, "@john", '@robut alias "long string" "that"')
58
+ @plugin.handle(Time.now, "@john", '@robut remove alias "long string"')
59
+ assert @plugin.aliases.empty?
60
+ end
61
+
62
+ def test_can_list_all_aliases
63
+ @plugin.handle(Time.now, "@john", "@robut alias this that")
64
+ @plugin.handle(Time.now, "@john", "@robut alias something something else")
65
+ @plugin.handle(Time.now, "@john", "@robut aliases")
66
+ assert_equal ["something => something else\nthis => that"], @plugin.connection.replies
67
+ end
68
+
69
+ def test_can_clear_aliases
70
+ @plugin.handle(Time.now, "@john", "@robut alias this that")
71
+ @plugin.handle(Time.now, "@john", "@robut clear aliases")
72
+ assert_equal({}, @plugin.aliases)
73
+ end
74
+
75
+ end
@@ -0,0 +1,45 @@
1
+ require 'test_helper'
2
+ require 'robut/plugin/echo'
3
+ require 'robut/plugin/help'
4
+
5
+ class Robut::Plugin::PluginWithoutHelp
6
+ include Robut::Plugin
7
+
8
+ def usage
9
+ super
10
+ end
11
+ end
12
+
13
+ class Robut::Plugin::HelpTest < Test::Unit::TestCase
14
+
15
+ def setup
16
+ @connection = Robut::ConnectionMock.new
17
+ Robut::Plugin.plugins << Robut::Plugin::Echo
18
+ Robut::Plugin.plugins << Robut::Plugin::Help
19
+ @plugin = Robut::Plugin::Help.new(@connection)
20
+ end
21
+
22
+ def teardown
23
+ Robut::Plugin.plugins = []
24
+ end
25
+
26
+ def test_help
27
+ @plugin.handle(Time.now, "@justin", "@robut help")
28
+ assert_equal [
29
+ "Supported commands:",
30
+ "@robut echo <message> - replies to the channel with <message>",
31
+ "@robut help - displays this message",
32
+ ], @plugin.connection.replies
33
+ end
34
+
35
+ def test_empty_help
36
+ Robut::Plugin.plugins << Robut::Plugin::PluginWithoutHelp
37
+ @plugin.handle(Time.now, "@justin", "@robut help")
38
+ assert_equal [
39
+ "Supported commands:",
40
+ "@robut echo <message> - replies to the channel with <message>",
41
+ "@robut help - displays this message",
42
+ ], @plugin.connection.replies
43
+ end
44
+ end
45
+
@@ -25,4 +25,9 @@ class Robut::Plugin::SayTest < Test::Unit::TestCase
25
25
  assert_equal [], @plugin.system_calls
26
26
  end
27
27
 
28
+ def test_strips_bad_chars
29
+ @plugin.handle(Time.now, "@john", "@robut say it's time for $%@ more_test 123 p\"")
30
+ assert_equal ["say its time for more test 123 p"], @plugin.system_calls
31
+ end
32
+
28
33
  end
@@ -0,0 +1,100 @@
1
+ require 'test_helper'
2
+ require 'webmock/test_unit'
3
+ require 'time-warp'
4
+ require 'robut/plugin/weather'
5
+
6
+ class Robut::Plugin::WeatherTest < Test::Unit::TestCase
7
+
8
+ def setup
9
+ @connection = Robut::ConnectionMock.new
10
+ @plugin = Robut::Plugin::Weather.new(@connection)
11
+ end
12
+
13
+ def teardown
14
+ Robut::Plugin::Weather.default_location = nil
15
+ end
16
+
17
+ def test_handle_no_weather
18
+ @plugin.handle(Time.now, "John", "lunch?")
19
+ assert_equal( [], @plugin.connection.replies )
20
+
21
+ @plugin.handle(Time.now, "John", "?")
22
+ assert_equal( [], @plugin.connection.replies )
23
+ end
24
+
25
+ def test_handle_no_location_no_default
26
+ @plugin.handle(Time.now, "John", "weather?")
27
+ assert_equal( ["I don't have a default location!"], @plugin.connection.replies )
28
+ end
29
+
30
+ def test_handle_no_location_default_set
31
+ Robut::Plugin::Weather.default_location = "Seattle"
32
+ stub_request(:any, "http://www.google.com/ig/api?weather=Seattle").to_return(:body => File.open(File.expand_path("../../../fixtures/seattle.xml", __FILE__), "r").read)
33
+
34
+ @plugin.handle(Time.now, "John", "weather?")
35
+ assert_equal( ["Weather for Seattle, WA: Mostly Cloudy, 58F"], @plugin.connection.replies )
36
+ end
37
+
38
+ def test_handle_location
39
+ stub_request(:any, "http://www.google.com/ig/api?weather=tacoma").to_return(:body => File.open(File.expand_path("../../../fixtures/tacoma.xml", __FILE__), "r").read)
40
+
41
+ @plugin.handle(Time.now, "John", "tacoma weather?")
42
+ assert_equal( ["Weather for Tacoma, WA: Cloudy, 60F"], @plugin.connection.replies )
43
+ end
44
+
45
+ def test_no_question_mark
46
+ @plugin.handle(Time.now, "John", "seattle weather")
47
+ assert_equal( [], @plugin.connection.replies )
48
+ end
49
+
50
+ def test_handle_day
51
+ stub_request(:any, "http://www.google.com/ig/api?weather=Seattle").to_return(:body => File.open(File.expand_path("../../../fixtures/seattle.xml", __FILE__), "r").read)
52
+
53
+ pretend_now_is(2011,"may",23,17) do
54
+ @plugin.handle(Time.now, "John", "Seattle weather tuesday?")
55
+ assert_equal( ["Forecast for Seattle, WA on Tue: Partly Cloudy, High: 67F, Low: 51F"], @plugin.connection.replies )
56
+ end
57
+ end
58
+
59
+ def test_handle_tomorrow
60
+ stub_request(:any, "http://www.google.com/ig/api?weather=Seattle").to_return(:body => File.open(File.expand_path("../../../fixtures/seattle.xml", __FILE__), "r").read)
61
+
62
+ pretend_now_is(2011,"may",23,17) do
63
+ @plugin.handle(Time.now, "John", "Seattle weather tomorrow?")
64
+ assert_equal( ["Forecast for Seattle, WA on Tue: Partly Cloudy, High: 67F, Low: 51F"], @plugin.connection.replies )
65
+ end
66
+ end
67
+
68
+ def test_handle_today
69
+ stub_request(:any, "http://www.google.com/ig/api?weather=Seattle").to_return(:body => File.open(File.expand_path("../../../fixtures/seattle.xml", __FILE__), "r").read)
70
+
71
+ pretend_now_is(2011,"may",23,17) do
72
+ @plugin.handle(Time.now, "John", "Seattle weather today?")
73
+ assert_equal( ["Forecast for Seattle, WA on Mon: Partly Cloudy, High: 59F, Low: 48F"], @plugin.connection.replies )
74
+ end
75
+ end
76
+
77
+ def test_handle_multi_word_location
78
+ stub_request(:any, "http://www.google.com/ig/api?weather=Las%20Vegas").to_return(:body => File.open(File.expand_path("../../../fixtures/las_vegas.xml", __FILE__), "r").read)
79
+ @plugin.handle(Time.now, "John", "Las Vegas weather?")
80
+ assert_equal( ["Weather for Las Vegas, NV: Mostly Cloudy, 83F"], @plugin.connection.replies )
81
+ end
82
+
83
+ def test_handle_location_with_comma
84
+ stub_request(:any, "http://www.google.com/ig/api?weather=Las%20Vegas,%20NV").to_return(:body => File.open(File.expand_path("../../../fixtures/las_vegas.xml", __FILE__), "r").read)
85
+ @plugin.handle(Time.now, "John", "Las Vegas, NV weather?")
86
+ assert_equal( ["Weather for Las Vegas, NV: Mostly Cloudy, 83F"], @plugin.connection.replies )
87
+ end
88
+
89
+ def test_handle_bad_location
90
+ stub_request(:any, "http://www.google.com/ig/api?weather=asdf").to_return(:body => File.open(File.expand_path("../../../fixtures/bad_location.xml", __FILE__), "r").read)
91
+ @plugin.handle(Time.now, "John", "asdf weather?")
92
+ assert_equal( ["I don't recognize the location: \"asdf\""], @plugin.connection.replies )
93
+ end
94
+
95
+ def test_handle_bad_date
96
+ @plugin.handle(Time.now, "John", "Seattle weather asdf?")
97
+ assert_equal( ["I don't recognize the date: \"asdf\""], @plugin.connection.replies )
98
+ end
99
+
100
+ end
@@ -0,0 +1,30 @@
1
+ require 'test_helper'
2
+
3
+ class Robut::PluginTest < Test::Unit::TestCase
4
+
5
+ class HandrolledStubPlugin
6
+ include Robut::Plugin
7
+ end
8
+
9
+ def setup
10
+ @plugin = HandrolledStubPlugin.new(Robut::ConnectionMock.new)
11
+ end
12
+
13
+ def test_sent_to_me?
14
+ assert @plugin.sent_to_me?("@Robut hello there")
15
+ assert !@plugin.sent_to_me?("@Robuto hello there")
16
+ assert !@plugin.sent_to_me?("@David hello there")
17
+ assert @plugin.sent_to_me?("this is a @Robut message")
18
+ assert @plugin.sent_to_me?("this is a message to @robut")
19
+ assert !@plugin.sent_to_me?("this is a message to@robut")
20
+ end
21
+
22
+ def test_without_nick_robut_do_this
23
+ assert_equal "do this", @plugin.without_nick("@robut do this")
24
+ end
25
+
26
+ def test_without_nick_do_this_robut
27
+ assert_equal "do this @robut", @plugin.without_nick("do this @robut")
28
+ end
29
+
30
+ end
@@ -7,7 +7,7 @@ class Robut::Storage::YamlStoreTest < Test::Unit::TestCase
7
7
  @store = Robut::Storage::YamlStore
8
8
  @store.file = new_yaml_file
9
9
  end
10
-
10
+
11
11
  def teardown
12
12
  File.delete new_yaml_file if File.exists?(new_yaml_file)
13
13
  end
@@ -16,27 +16,32 @@ class Robut::Storage::YamlStoreTest < Test::Unit::TestCase
16
16
  assert_equal 'in the trunk', (@store['junk'] = 'in the trunk')
17
17
  assert_equal 'in the trunk', @store['junk']
18
18
  end
19
-
19
+
20
+ def test_load_an_empty_file
21
+ FileUtils.touch(new_yaml_file)
22
+ assert_equal nil, @store['non-exising-key']
23
+ end
24
+
20
25
  def test_read_from_file
21
26
  @store.file = test_yaml_file
22
27
  assert_equal 'bar', @store['foo']
23
28
  end
24
-
29
+
25
30
  def test_persists_to_file
26
31
  @store['pot'] = 'roast'
27
32
  assert File.exists?(new_yaml_file)
28
33
  yaml = YAML.load_file(new_yaml_file)
29
34
  assert_equal 'roast', yaml['pot']
30
35
  end
31
-
36
+
32
37
  private
33
-
38
+
34
39
  def test_yaml_file
35
40
  File.join(File.dirname(__FILE__), 'yaml_test.yml')
36
41
  end
37
-
42
+
38
43
  def new_yaml_file
39
44
  File.join(File.dirname(__FILE__), 'new_yaml_test.yml')
40
45
  end
41
-
42
- end
46
+
47
+ end