gossip 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.
Files changed (63) hide show
  1. data/History.txt +9 -0
  2. data/LICENSE.txt +34 -0
  3. data/Manifest.txt +62 -0
  4. data/README.txt +6 -0
  5. data/Rakefile +137 -0
  6. data/examples/all-config-file-choices.yml +36 -0
  7. data/lib/gossip.rb +32 -0
  8. data/lib/gossip/command.rb +122 -0
  9. data/lib/gossip/cronies/campfire.rb +72 -0
  10. data/lib/gossip/cronies/jabber.rb +66 -0
  11. data/lib/gossip/cronies/smtp.rb +121 -0
  12. data/lib/gossip/cronies/stdout.rb +24 -0
  13. data/lib/gossip/cronies/trac.rb +82 -0
  14. data/lib/gossip/cronies/twitter.rb +50 -0
  15. data/lib/gossip/crony.rb +102 -0
  16. data/lib/gossip/multi-exceptions.rb +47 -0
  17. data/lib/gossip/preteen.rb +86 -0
  18. data/lib/gossip/site-config.rb +94 -0
  19. data/lib/gossip/social-universe.rb +18 -0
  20. data/lib/gossip/version.rb +8 -0
  21. data/pages/classes.html +58 -0
  22. data/pages/cronies.html +256 -0
  23. data/pages/css/LICENSE.txt +1 -0
  24. data/pages/css/Thumbs.db +0 -0
  25. data/pages/css/bg2.gif +0 -0
  26. data/pages/css/gossip5-header-flip.jpg +0 -0
  27. data/pages/css/left.gif +0 -0
  28. data/pages/css/left_on.gif +0 -0
  29. data/pages/css/main.css +242 -0
  30. data/pages/css/right.gif +0 -0
  31. data/pages/css/right_on.gif +0 -0
  32. data/pages/css/tvline.gif +0 -0
  33. data/pages/images/campfire.png +0 -0
  34. data/pages/images/classes.png +0 -0
  35. data/pages/images/deployment.png +0 -0
  36. data/pages/images/jabber-big.png +0 -0
  37. data/pages/images/jabber.png +0 -0
  38. data/pages/images/trac-bigger.png +0 -0
  39. data/pages/images/trac-detail.png +0 -0
  40. data/pages/images/twitter.png +0 -0
  41. data/pages/index.html +45 -0
  42. data/pages/installation.html +95 -0
  43. data/pages/scripts.html +166 -0
  44. data/pages/src/classes.graffle +0 -0
  45. data/pages/starting-to-use.html +200 -0
  46. data/pages/writing-new-scripts.html +38 -0
  47. data/scripts/fanout +64 -0
  48. data/scripts/svntell +71 -0
  49. data/scripts/watchdog +86 -0
  50. data/setup.rb +1585 -0
  51. data/test/script/fanout-slowtests.rb +40 -0
  52. data/test/script/svntell-slowtests.rb +40 -0
  53. data/test/script/util.rb +22 -0
  54. data/test/script/watchdog-slowtests.rb +56 -0
  55. data/test/unit/command-crony-interaction-tests.rb +116 -0
  56. data/test/unit/command-tests.rb +119 -0
  57. data/test/unit/crony-tests.rb +46 -0
  58. data/test/unit/multi-exception-tests.rb +70 -0
  59. data/test/unit/preteen-tests.rb +81 -0
  60. data/test/util/bff.rb +45 -0
  61. data/test/util/doghouse.rb +42 -0
  62. data/test/util/silly-little-test-program.rb +6 -0
  63. metadata +181 -0
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Created by Brian Marick on 2007-09-25.
4
+ # Copyright (c) 2007. All rights reserved.
5
+
6
+ require 'test/unit'
7
+ require 's4t-utils'
8
+ S4tUtils.set_test_paths(__FILE__)
9
+
10
+ require 'test/script/util'
11
+
12
+ unless S4tUtils.on_windows?
13
+ class TestFanoutCommandExecution < Test::Unit::TestCase
14
+ def test_scandal_and_details
15
+ as_script_test('.fanout.yml') do
16
+ result = `echo "some details" | ruby fanout --details arg "another arg"`
17
+ lines = result.split("\n")
18
+ assert_match(/arg another arg/, lines.first)
19
+ assert_match(/some details/, lines.last)
20
+ end
21
+ end
22
+
23
+ def test_scandal_alone
24
+ as_script_test('.fanout.yml') do
25
+ result = `ruby fanout --no-det arg "another arg"`
26
+ lines = result.split("\n")
27
+ assert_equal(1, lines.length)
28
+ assert_match(/arg another arg/, lines.first)
29
+ end
30
+ end
31
+
32
+ def test_details_are_default
33
+ as_script_test('.fanout.yml') do
34
+ results = `echo sloop | ruby fanout arg "another arg"`
35
+ assert_match(/sloop/, results)
36
+ end
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Created by Brian Marick on 2007-09-27.
4
+ # Copyright (c) 2007. All rights reserved.
5
+
6
+ #!/usr/bin/env ruby
7
+ #
8
+ # Created by Brian Marick on 2007-09-25.
9
+ # Copyright (c) 2007. All rights reserved.
10
+
11
+ require 'test/unit'
12
+ require 's4t-utils'
13
+ S4tUtils.set_test_paths(__FILE__)
14
+
15
+ require 'test/script/util'
16
+
17
+ unless S4tUtils.on_windows?
18
+ class TestFanoutCommandExecution < Test::Unit::TestCase
19
+ def test_revision_is_required
20
+ as_script_test('.svntell.yml') do
21
+ result = `ruby svntell --repository /svn 2>&1`
22
+ assert_match(/must choose a revision/, result)
23
+ end
24
+ end
25
+
26
+ def test_repository_is_required
27
+ as_script_test('.svntell.yml') do
28
+ result = `ruby svntell --revision 5 foo 2>&1`
29
+ assert_match(/must choose a repository/, result)
30
+ end
31
+ end
32
+
33
+ def test_svnlook_must_exist
34
+ as_script_test('.svntell.yml') do
35
+ result = `ruby svntell --svnlook /foo/bar --repository /svn --revision 5 foo 2>&1`
36
+ assert_match(%r{svnlook path '/foo/bar' does not exist.}, result)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Created by Brian Marick on 2007-09-25.
4
+ # Copyright (c) 2007. All rights reserved.
5
+
6
+
7
+ class Test::Unit::TestCase
8
+ include S4tUtils
9
+
10
+ def as_script_test(config_name)
11
+ yaml = %q{
12
+ standard-output: true
13
+ }
14
+
15
+ Dir.chdir(PACKAGE_ROOT + "/scripts") do
16
+ with_local_config_file(config_name, yaml) do
17
+ yield
18
+ end
19
+ end
20
+ end
21
+
22
+ end
@@ -0,0 +1,56 @@
1
+ require 'test/unit'
2
+ require 's4t-utils'
3
+ include S4tUtils
4
+ set_test_paths(__FILE__)
5
+
6
+ require 'test/script/util'
7
+ load "#{PACKAGE_ROOT}/scripts/watchdog"
8
+
9
+ class TestWatchdogCommand < Test::Unit::TestCase
10
+ include Gossip
11
+
12
+ class RandomWatchdogCommand < Watchdog
13
+ # Erase behaviors of no interest
14
+ def initialize; end
15
+ end
16
+
17
+
18
+ def test_command_name_ignores_ruby
19
+ dog = RandomWatchdogCommand.new
20
+ assert_equal('echo', dog.command_name(['echo', 'foo']))
21
+ assert_equal('echo.rb', dog.command_name(['ruby', 'echo.rb', 'foo']))
22
+ end
23
+
24
+ def test_command_name_is_just_basename
25
+ dog = RandomWatchdogCommand.new
26
+ assert_equal('echo.rb', dog.command_name(['ruby', '/usr/bin/echo.rb', 'foo']))
27
+ end
28
+
29
+ def test_timer
30
+ duration, result = RandomWatchdogCommand.new.time {
31
+ sleep 2
32
+ 5
33
+ }
34
+ # Rough check because time is inaccurate.
35
+ assert_true(duration >= 1.5)
36
+ assert_equal(5, result)
37
+ end
38
+
39
+
40
+ end
41
+
42
+ unless S4tUtils.on_windows?
43
+ class TestWatchdogCommandExecution < Test::Unit::TestCase
44
+ def test_command_line_only
45
+ as_script_test('.watchdog.yml') do
46
+ actual_string = `ruby watchdog ruby ../test/util/silly-little-test-program.rb 1 2`
47
+ assert_match(/Program silly-little-test-program.rb finished/, actual_string)
48
+ assert_match(/Duration: /, actual_string)
49
+ assert_match(%r{Command: ruby ../test/util/silly-little-test-program.rb 1 2}, actual_string)
50
+
51
+ assert_match(/I mostly write to standard output./, actual_string)
52
+ assert_match(/I also write to standard error./, actual_string)
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,116 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Created by Brian Marick on 2007-09-17.
4
+ # Copyright (c) 2007. All rights reserved.
5
+
6
+ require 'test/unit'
7
+ require 's4t-utils'
8
+ include S4tUtils
9
+ set_test_paths(__FILE__)
10
+
11
+
12
+ require 'gossip'
13
+
14
+ class ChoicesTests < Test::Unit::TestCase
15
+ include Gossip
16
+
17
+ def test_that_crony_presence_sets_switch
18
+ # Note that the cronymaker key and Crony name must be the same.
19
+ # That duplication is an artifact of trying to make the declarations
20
+ # in the scripts (like watchdog.rb) look nice.
21
+ crony_maker = { :winner => proc {@winner = Exhibitionist.named(:winner)},
22
+ :loser => proc {@loser = Exhibitionist.named(:loser)} }
23
+
24
+ sophie = Preteen.new(crony_maker, [:winner], [:loser])
25
+ cmd = SomeRandomCommand.new(sophie)
26
+
27
+ # Switch is initialized, with default value
28
+ assert_equal(true, cmd.user_choices[:winner])
29
+ assert_equal(false, cmd.user_choices[:loser])
30
+
31
+ # Shorthand for the default value
32
+ assert_true(@winner.is_bff_by_default?)
33
+ assert_false(@loser.is_bff_by_default?)
34
+
35
+ # Cronies share user choices with command.
36
+ assert_equal(@winner.user_choices, cmd.user_choices)
37
+ assert_equal(@loser.user_choices, cmd.user_choices)
38
+ end
39
+
40
+ def test_how_command_initializes_a_crony
41
+ crony_maker = { :bff => proc {Exhibitionist.named(:bff)}}
42
+ sophie = Preteen.new(crony_maker, [:bff], [])
43
+ SomeRandomCommand.new(sophie).execute
44
+ assert_equal(['command_line_description()', # This is the crony's only part in determining whether it's called by default
45
+ 'add_configuration_choices()',
46
+ # Command line and config files are read here
47
+ 'user_choices=()', # Give crony access to user choices
48
+ 'postprocess_user_choices()',
49
+ # Can now execute.
50
+ 'hear("boyfriend!", "Joshua")'
51
+ ], sophie.cronies[0].logged)
52
+ end
53
+
54
+
55
+ class Exhibitionist < Crony
56
+ attr_reader :logged, :user_choices, :symbol
57
+
58
+ def name; symbol.to_s; end
59
+
60
+ def self.named(sym); new(sym); end
61
+ def initialize(sym)
62
+ @symbol = sym
63
+ @logged = []
64
+ super
65
+ end
66
+
67
+ def user_choices=(value)
68
+ log("user_choices=")
69
+ @user_choices = value
70
+ end
71
+
72
+ def command_line_description()
73
+ log("command_line_description")
74
+ ['-e', "--exhibitionist", "Defaults to #{is_bff_by_default?}."]
75
+ end
76
+
77
+ def add_configuration_choices(builder)
78
+ log("add_configuration_choices");
79
+ end
80
+ def postprocess_user_choices;
81
+ log("postprocess_user_choices")
82
+ end
83
+
84
+ def hear(*args)
85
+ log('hear', *args)
86
+ end
87
+
88
+ private
89
+
90
+ def log(message, *args); @logged << how_called(message, *args); end
91
+
92
+ def how_called(message, *args)
93
+ args = args.collect { |a| a.inspect }
94
+ "#{message}(" + args.join(", ") + ")"
95
+ end
96
+
97
+ end
98
+
99
+
100
+ class SomeRandomCommand < Gossip::GossipCommand
101
+
102
+ attr_reader :user_choices
103
+
104
+ def add_sources(builder)
105
+ # There must be at least a command line.
106
+ builder.add_source(CommandLineSource, :usage,
107
+ "Usage: ruby #{$0} [options] program args...")
108
+ end
109
+
110
+ def execute
111
+ preteen.tell_bffs("boyfriend!", "Joshua")
112
+ end
113
+ end
114
+
115
+
116
+ end
@@ -0,0 +1,119 @@
1
+ require 'test/unit'
2
+ require 's4t-utils'
3
+ include S4tUtils
4
+ set_test_paths(__FILE__)
5
+
6
+ require 'gossip'
7
+
8
+ class CommandTests < Test::Unit::TestCase
9
+ include Gossip
10
+
11
+ class SomeRandomCommand < GossipCommand
12
+ def script_config_file; ".localrc"; end
13
+ def gossip_config_file; ".globalrc"; end
14
+
15
+ def usage; "Usage: ruby #{$0} [options] program args..."; end
16
+ def add_sources(builder)
17
+ builder.add_source(PosixCommandLineSource, :usage, *describe_all_but_options)
18
+ builder.add_source(YamlConfigFileSource, :from_file, ".sophie.yml")
19
+ builder.add_source(XmlConfigFileSource, :from_file, ".sophie.xml")
20
+ end
21
+
22
+ def execute
23
+ preteen.tell_bffs("boyfriend!", "Joshua")
24
+ preteen.cronies.collect { |crony| crony.value }
25
+ end
26
+ end
27
+
28
+ def setup
29
+ crony_maker = {
30
+ :bff => proc {
31
+ require PACKAGE_ROOT + '/test/util/bff'
32
+ OneTestCrony.new(:default_format_string => "scandal: %s, details: %s")
33
+ },
34
+ :doghouse => proc {
35
+ require PACKAGE_ROOT + '/test/util/doghouse'
36
+ AnotherTestCrony.new(:default_format_string => "%s: %s")
37
+ }
38
+ }
39
+
40
+ @sophie = Preteen.new(crony_maker, [:bff], [:doghouse])
41
+ end
42
+
43
+ def test_behavior_when_not_overridden_is_as_set
44
+ SomeRandomCommand.new(@sophie).execute
45
+ result = @sophie.cronies.collect { |crony| crony.value }
46
+ assert_equal(["scandal: boyfriend!, details: Joshua", "doghouse crony was not told!"],
47
+ result)
48
+ end
49
+
50
+
51
+ def test_that_config_files_can_change_who_gets_told
52
+ flip = %Q{
53
+ <sophie>
54
+ <bff>false</bff>
55
+ <doghouse>true</doghouse>
56
+ </sophie>
57
+ }
58
+ with_local_config_file('.sophie.xml', flip) {
59
+ SomeRandomCommand.new(@sophie).execute
60
+ result = @sophie.cronies.collect { |crony| crony.value }
61
+ assert_equal(["bff was not told!", "boyfriend!: Joshua"],
62
+ result)
63
+ }
64
+ end
65
+
66
+
67
+
68
+ def test_that_both_switches_and_options_can_be_overridden
69
+ reconcile = %Q{
70
+ doghouse: true
71
+ }
72
+
73
+ with_command_args('--bff-format=%s/%s --doghouse-form %s+%s') {
74
+ with_local_config_file('.sophie.yml', reconcile) {
75
+ SomeRandomCommand.new(@sophie).execute
76
+ result = @sophie.cronies.collect { |crony| crony.value }
77
+ assert_equal(["boyfriend!/Joshua", "boyfriend!+Joshua"],
78
+ result)
79
+ }
80
+ }
81
+ end
82
+
83
+ def test_typical_usage_lines
84
+ with_command_args('--help') do
85
+ output = capturing_stderr do
86
+ assert_wants_to_exit do
87
+ SomeRandomCommand.new(@sophie)
88
+ end
89
+ end
90
+ lines = output.split("\n")
91
+ assert_equal("Usage: ruby #{$0} [options] program args...", lines[0])
92
+ assert_match(/Site-wide defaults/, lines[1])
93
+ assert_match(/Override them in the '.localrc' or '.globalrc' files in your home folder./, lines[2])
94
+ end
95
+ end
96
+
97
+ class LongerUsageCommand < SomeRandomCommand
98
+ def usage; ['line 1', 'line 2']; end
99
+ end
100
+
101
+ def test_that_usage_lines_can_be_an_array
102
+ with_command_args('--help') do
103
+ output = capturing_stderr do
104
+ assert_wants_to_exit do
105
+ LongerUsageCommand.new(@sophie).execute
106
+ end
107
+ end
108
+ lines = output.split("\n")
109
+ assert_equal("line 1", lines[0])
110
+ assert_equal("line 2", lines[1])
111
+ assert_match(/Site-wide defaults/, lines[2])
112
+ end
113
+
114
+ end
115
+
116
+
117
+
118
+
119
+ end
@@ -0,0 +1,46 @@
1
+ require 'test/unit'
2
+ require 's4t-utils'
3
+ include S4tUtils
4
+ set_test_paths(__FILE__)
5
+
6
+ require 'gossip'
7
+
8
+ # Most of the crony behavior is interaction with Command, so see
9
+ # command-tests.rb
10
+
11
+ class ChoicesTests < Test::Unit::TestCase
12
+ include Gossip
13
+
14
+ class ConcreteCrony < Crony
15
+ def name; 'non-abstract'; end
16
+ def symbol; :sym; end
17
+ end
18
+
19
+ def test_crony_checks_for_typos
20
+ crony = ConcreteCrony.new(:key => 'value')
21
+
22
+ assert_equal('value', crony.checked(:key))
23
+ assert_raises_with_matching_message(StandardError, /:ky is nil.*#{crony.name}.*typo/) do
24
+ crony.checked(:ky)
25
+ end
26
+ end
27
+
28
+
29
+ def test_crony_can_describe_defaults
30
+ crony = ConcreteCrony.new(:key => 'value')
31
+ assert_equal('Defaults to "value".', crony.df(:key))
32
+
33
+ assert_raises_with_matching_message(StandardError, /:ky is nil.*#{crony.name}.*typo/) do
34
+ crony.checked(:ky)
35
+ end
36
+ end
37
+
38
+ def test_crony_friendship_depends_on_user_choices
39
+ crony = ConcreteCrony.new
40
+ crony.user_choices=({:sym => true})
41
+ assert_true(crony.is_bff?)
42
+ crony.user_choices=({:sym => false})
43
+ assert_false(crony.is_bff?)
44
+ end
45
+
46
+ end
@@ -0,0 +1,70 @@
1
+ require 'test/unit'
2
+ require 's4t-utils'
3
+ include S4tUtils
4
+ set_test_paths(__FILE__)
5
+
6
+ require 'gossip/multi-exceptions'
7
+
8
+ class QueueExtensionTests < Test::Unit::TestCase
9
+ def test_to_a
10
+ queue = Queue.new
11
+ queue << 1
12
+ assert_equal([1], queue.to_a)
13
+ end
14
+ end
15
+
16
+ class MultiExceptionTests < Test::Unit::TestCase
17
+ include Gossip
18
+
19
+ def two_exceptions
20
+ retval = []
21
+ [IndexError.new("index error"),
22
+ RuntimeError.new("runtime error")].each do | ex |
23
+ begin
24
+ raise ex # You have to raise to get a backtrace.
25
+ rescue Exception => caught
26
+ retval << caught
27
+ end
28
+ end
29
+ retval
30
+ end
31
+
32
+ def setup
33
+ @ex = MultiException.new(two_exceptions)
34
+ end
35
+
36
+
37
+ def test_multi_exception_aggregates_messages
38
+ messages = @ex.message.split("\n")
39
+ assert_match(/index error/, messages[0])
40
+ assert_match(/runtime error/, messages[1])
41
+ end
42
+
43
+ def test_multi_exception_includes_exception_classes_in_messages
44
+ messages = @ex.message.split("\n")
45
+ assert_match(/IndexError/, messages[0])
46
+ assert_match(/RuntimeError/, messages[1])
47
+ end
48
+
49
+ def test_multi_exception_aggregates_backtraces_in_traces_field
50
+ assert_equal(2, @ex.traces.length)
51
+ assert_equal(1, @ex.traces[0].grep(/setup/).length)
52
+ assert_equal(1, @ex.traces[1].grep(/setup/).length)
53
+ end
54
+
55
+ def test_combined_trace
56
+ begin
57
+ MultiException.reraise_with_combined_backtrace do
58
+ raise @ex
59
+ end
60
+ rescue MultiException => ex
61
+ assert_equal(1, ex.backtrace.grep(/Trace number 0:/).length)
62
+ assert_equal(1, ex.backtrace.grep(/Trace number 1:/).length)
63
+ # The raise in this method is wiped out.
64
+ assert_equal(0, ex.backtrace.grep(/test_combined_trace/).length)
65
+ # The raises in setup are preserved.
66
+ assert_equal(2, ex.backtrace.grep(/setup/).length)
67
+ end
68
+ end
69
+
70
+ end