gossip 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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,72 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Created by Brian Marick on 2007-09-16.
4
+ # Copyright (c) 2007. All rights reserved.
5
+
6
+
7
+ require 'tinder'
8
+ require 'gossip/crony'
9
+
10
+
11
+ module Gossip
12
+
13
+ # CampfireCrony adds a notice to a Campfire (http://www.campfirenow.com)
14
+ # chat room.
15
+ class CampfireCrony < Crony
16
+
17
+ def name; "campfire"; end
18
+ def symbol; :campfire; end
19
+
20
+ def command_line_description
21
+ ["-c", "--campfire",
22
+ "Control display to Campfire chat room.",
23
+ "Defaults to #{is_bff_by_default?}."]
24
+ end
25
+
26
+ def add_configuration_choices(builder)
27
+ builder.add_choice(:campfire_login,
28
+ :default => checked(:default_login)) { | command_line |
29
+ command_line.uses_option("--campfire-login LOGIN",
30
+ "Your Campfire login, an email address",
31
+ df(:default_login)
32
+ )
33
+ }
34
+
35
+ builder.add_choice(:campfire_password,
36
+ :default => checked(:default_password)) { | command_line |
37
+ command_line.uses_option("--campfire-password PASSWORD",
38
+ "Your Campfire password.",
39
+ df(:default_password)
40
+ )
41
+ }
42
+
43
+ builder.add_choice(:campfire_subdomain,
44
+ :default => checked(:default_subdomain)) { | command_line |
45
+ command_line.uses_option("--campfire-subdomain NAME",
46
+ "Your Campfire subdomain.",
47
+ "(As in 'subdomain.campfire.com')",
48
+ df(:default_subdomain)
49
+ )
50
+ }
51
+
52
+ builder.add_choice(:campfire_room,
53
+ :default => checked(:default_room)) { | command_line |
54
+ command_line.uses_option("--campfire-room NAME",
55
+ "A campfire room to receive the message.",
56
+ df(:default_room)
57
+ )
58
+ }
59
+
60
+ end
61
+
62
+ def hear(scandal, details)
63
+ connection = Tinder::Campfire.new(@user_choices[:campfire_subdomain])
64
+ connection.login(@user_choices[:campfire_login], @user_choices[:campfire_password])
65
+ raise StandardError, "Login to Campfire failed." unless connection.logged_in?
66
+ room = connection.find_room_by_name(@user_choices[:campfire_room])
67
+ room.paste([scandal, details].join("\n"))
68
+ connection.logout
69
+ end
70
+
71
+ end
72
+ end
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Created by Brian Marick on 2007-09-15.
4
+ # Copyright (c) 2007. All rights reserved.
5
+
6
+ require 'xmpp4r'
7
+ require 'gossip/crony'
8
+
9
+ module Gossip
10
+ class JabberCrony < Crony
11
+ include Jabber
12
+
13
+ def name; "jabber"; end
14
+ def symbol; :jabber; end
15
+
16
+ def command_line_description
17
+ ['-j', "--jabber",
18
+ "Control IM notification.",
19
+ "Defaults to #{is_bff_by_default?}."]
20
+ end
21
+
22
+ def add_configuration_choices(builder)
23
+ builder.add_choice(:jabber_to,
24
+ :default => checked(:default_to),
25
+ :type => [:string]) { | command_line |
26
+ command_line.uses_option("--jabber-to RECIPIENTS",
27
+ "Recipients of Jabber instant messages.",
28
+ "This can be a comma-separated list.",
29
+ df(:default_to)
30
+ )
31
+ }
32
+
33
+ builder.add_choice(:jabber_account,
34
+ :default => checked(:default_account)) { | command_line |
35
+ command_line.uses_option("--jabber-account SENDER",
36
+ "Your Jabber account.",
37
+ "Note that this includes the Jabber server.",
38
+ df(:default_account)
39
+ )
40
+ }
41
+
42
+ builder.add_choice(:jabber_password,
43
+ :default => checked(:default_password)) { | command_line |
44
+ command_line.uses_option("--jabber-password PASSWORD",
45
+ "Your Jabber password.",
46
+ df(:default_password)
47
+ )
48
+ }
49
+ end
50
+
51
+ def hear(scandal, details)
52
+ my_jid = JID.new(@user_choices[:jabber_account])
53
+ cl = Client.new(my_jid, false)
54
+ cl.connect
55
+ cl.auth(@user_choices[:jabber_password])
56
+ details = [scandal, details].join("\n")
57
+ @user_choices[:jabber_to].each do | recipient |
58
+ m = Message::new(recipient, details).
59
+ set_type(:normal).set_id('1').set_subject(scandal)
60
+ cl.send(m)
61
+ end
62
+ cl.close
63
+ end
64
+
65
+ end
66
+ end
@@ -0,0 +1,121 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Created by Brian Marick on 2007-09-15.
4
+ # Copyright (c) 2007. All rights reserved.
5
+
6
+ require 'net/smtp'
7
+
8
+ module Gossip
9
+
10
+ # This Crony sends email.
11
+ class SmtpCrony < Crony
12
+
13
+ def name; 'mail'; end
14
+ def symbol; :mail; end
15
+
16
+ def command_line_description
17
+ ['-m', "--mail",
18
+ "Control mail notification.",
19
+ "Defaults to #{is_bff_by_default?}."]
20
+ end
21
+
22
+ def add_configuration_choices(builder)
23
+ builder.add_choice(:mail_to,
24
+ :default => checked(:default_to),
25
+ :type => [:string]) { | command_line |
26
+ command_line.uses_option("--mail-to RECIPIENTS",
27
+ "Recipients of mail.",
28
+ "This can be a comma-separated list.",
29
+ df(:default_to)
30
+ )
31
+ }
32
+
33
+ builder.add_choice(:mail_from,
34
+ :default => checked(:default_from)) { | command_line |
35
+ command_line.uses_option("--mail-from SENDER",
36
+ "The sender of the mail (appears in From line).",
37
+ df(:default_from)
38
+ )
39
+ }
40
+
41
+ builder.add_choice(:mail_server,
42
+ :default => checked(:default_smtp_server)) { | command_line |
43
+ command_line.uses_option("--mail-server HOSTNAME",
44
+ "SMTP server. #{df(:default_smtp_server)}"
45
+ )
46
+ }
47
+
48
+ builder.add_choice(:mail_port,
49
+ :type => :integer,
50
+ :default => checked(:default_smtp_port)) { | command_line |
51
+ command_line.uses_option("--mail-port NUMBER",
52
+ "Mail port on that server.",
53
+ df(:default_smtp_port)
54
+ )
55
+ }
56
+
57
+ builder.add_choice(:mail_account,
58
+ :default => checked(:default_smtp_account)) { | command_line |
59
+ command_line.uses_option("--mail-account USERNAME",
60
+ "Your account name on the SMTP server.",
61
+ df(:default_smtp_account)
62
+ )
63
+ }
64
+
65
+ builder.add_choice(:mail_from_domain,
66
+ :default => checked(:default_from_domain)) { | command_line |
67
+ command_line.uses_option("--mail-from-domain HOSTNAME",
68
+ "The server the mail supposedly comes from.",
69
+ "(Not necessarily the SMTP server.)",
70
+ df(:default_from_domain)
71
+ )
72
+ }
73
+
74
+ builder.add_choice(:mail_password,
75
+ :default => checked(:default_smtp_password)) { | command_line |
76
+ command_line.uses_option("--mail-password HOSTNAME",
77
+ "Your password on the SMTP server.",
78
+ df(:default_smtp_password)
79
+ )
80
+ }
81
+
82
+ auth_types = ['plain', 'login', 'cram_md5']
83
+ builder.add_choice(:mail_authentication,
84
+ :type => auth_types,
85
+ :default => checked(:default_smtp_authentication)) { | command_line |
86
+ command_line.uses_option("--mail-authentication TYPE",
87
+ "The kind of authentication your SMTP server uses.",
88
+ "One of #{friendly_list('or', auth_types)}.",
89
+ df(:default_smtp_authentication)
90
+ )
91
+ }
92
+ end
93
+
94
+ def postprocess_user_choices
95
+ @user_choices[:mail_authentication] =
96
+ @user_choices[:mail_authentication].to_sym
97
+ end
98
+
99
+ def hear(scandal, details)
100
+ from = @user_choices[:mail_from]
101
+ to = @user_choices[:mail_to]
102
+ Net::SMTP.start(@user_choices[:mail_server], @user_choices[:mail_port],
103
+ @user_choices[:mail_from_domain],
104
+ @user_choices[:mail_account],
105
+ @user_choices[:mail_password],
106
+ @user_choices[:mail_authentication]) { | smtp |
107
+ mail = "
108
+ . From: #{from}
109
+ . To: #{to.join(', ')}
110
+ . Subject: [watchdog] #{scandal}
111
+ .
112
+ . #{details}
113
+ ".without_pretty_margin('.')
114
+ smtp.send_message(mail, from, to)
115
+ }
116
+ end
117
+ end
118
+
119
+
120
+
121
+ end
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Created by Brian Marick on 2007-09-15.
4
+ # Copyright (c) 2007. All rights reserved.
5
+
6
+ require 'gossip/crony'
7
+
8
+ module Gossip
9
+ class StdoutCrony < Crony
10
+ def name; "terminal"; end
11
+ def symbol; :standard_output; end
12
+
13
+ def command_line_description
14
+ ["-s", "--standard-output",
15
+ "Control display to terminal (standard output).",
16
+ "Defaults to #{is_bff_by_default?}."]
17
+ end
18
+
19
+ def hear(scandal, details)
20
+ all = [scandal, details].join($/)
21
+ puts all.indent_by(2)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Created by Brian Marick on 2007-09-19.
4
+ # Copyright (c) 2007. All rights reserved.
5
+
6
+
7
+ require 'gossip/crony'
8
+
9
+
10
+ module Gossip
11
+ # Right now, TracCrony is kind of a hack. It uses trac-admin(1) to
12
+ # edit a wiki page. If configured to do so, Trac will add wiki changes
13
+ # into the Timeline. OK, it's more than "kind of" a hack.
14
+ #
15
+ # Note that you must be running a command that uses a Trac crony on
16
+ # the Trac server itself.
17
+ #
18
+ # An alternative would be to use Mechanize. This was easier for now.
19
+ class TracCrony < Crony
20
+
21
+ def name; "trac"; end
22
+ def symbol; :trac; end
23
+
24
+ def command_line_description
25
+ ["-T", "--trac",
26
+ "Control display to Trac timeline.",
27
+ "Defaults to #{is_bff_by_default?}."]
28
+ end
29
+
30
+ def add_configuration_choices(builder)
31
+ builder.add_choice(:trac_admin_path,
32
+ :default => checked(:default_trac_admin_path)) { | command_line |
33
+ command_line.uses_option("--trac-admin-path PATH",
34
+ "Absolute pathname to trac-admin(1).",
35
+ df(:default_trac_admin_path)
36
+ )
37
+ }
38
+
39
+ builder.add_choice(:trac_environment_path,
40
+ :default => checked(:default_environment_path)) { | command_line |
41
+ command_line.uses_option("--trac-environment-path PATH",
42
+ "Your Trac environment.",
43
+ df(:default_environment_path)
44
+ )
45
+ }
46
+
47
+ builder.add_choice(:trac_page_name,
48
+ :default => checked(:default_page_name)) { | command_line |
49
+ command_line.uses_option("--trac-page-name PAGE",
50
+ "A Trac wiki page to receive the message.",
51
+ df(:default_page_name)
52
+ )
53
+ }
54
+
55
+ builder.add_choice(:trac_content_file,
56
+ :default => checked(:default_content_file)) { | command_line |
57
+ command_line.uses_option("--trac-content-file FILE",
58
+ "A file to use as the content of the page.",
59
+ df(:default_content_file)
60
+ )
61
+ }
62
+
63
+ end
64
+
65
+ def hear(scandal, details)
66
+ trac_admin = @user_choices[:trac_admin_path]
67
+ env = @user_choices[:trac_environment_path]
68
+ page = @user_choices[:trac_page_name]
69
+ file = @user_choices[:trac_content_file]
70
+ current = File.exist?(file) ? File.read(file) : ""
71
+ File.open(file, "w") do | io |
72
+ io.puts(scandal)
73
+ io.puts(details.gsub(/$/, '[[BR]]'))
74
+ io.puts('---------------')
75
+ io.puts current
76
+ end
77
+ exec = "#{trac_admin} #{env} wiki import #{page} #{file}"
78
+ `#{exec}`
79
+ end
80
+
81
+ end
82
+ end
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Created by Brian Marick on 2007-09-24.
4
+ # Copyright (c) 2007. All rights reserved.
5
+
6
+ require 'twitter'
7
+ require 'gossip/crony'
8
+
9
+
10
+ module Gossip
11
+
12
+ # TwitterCrony updates Twitter (http://www.twitter.com) status. Note:
13
+ # only the text of the scandal is used, not the details.
14
+ class TwitterCrony < Crony
15
+
16
+ def name; "twitter"; end
17
+ def symbol; :twitter; end
18
+
19
+ def command_line_description
20
+ ["-t", "--twitter",
21
+ "Control whether Twitter updates are made.",
22
+ "Defaults to #{is_bff_by_default?}."]
23
+ end
24
+
25
+ def add_configuration_choices(builder)
26
+ builder.add_choice(:twitter_login,
27
+ :default => checked(:default_login)) { | command_line |
28
+ command_line.uses_option("--twitter-login LOGIN",
29
+ "Your Twitter login",
30
+ df(:default_login)
31
+ )
32
+ }
33
+
34
+ builder.add_choice(:twitter_password,
35
+ :default => checked(:default_password)) { | command_line |
36
+ command_line.uses_option("--twitter-password PASSWORD",
37
+ "Your Twitter password.",
38
+ df(:default_password)
39
+ )
40
+ }
41
+
42
+ end
43
+
44
+ def hear(scandal, details)
45
+ twit = Twitter::Base.new(@user_choices[:twitter_login], @user_choices[:twitter_password])
46
+ twit.update(scandal)
47
+ end
48
+
49
+ end
50
+ end
@@ -0,0 +1,102 @@
1
+ #! /opt/local/bin/ruby
2
+ #
3
+ # Created by Brian Marick on 2007-09-15.
4
+ # Copyright (c) 2007. All rights reserved.
5
+
6
+ module Gossip
7
+
8
+ # This is the base class for the different Crony objects that the Preteen
9
+ # gossips with. A Crony object is something like mail, Twitter, Jabber, or
10
+ # anything that can accept a message and do something useful with it.
11
+ #
12
+ # To understand Crony, you should first understand GossipCommand and
13
+ # Preteen. It's helpful to understand the user-choices gem:
14
+ # http://http://user-choices.rubyforge.org
15
+ class Crony
16
+
17
+ # Each Crony will require some information like an account name, a
18
+ # password, etc. Those can be specified by the user. If not, these
19
+ # values are used. By convention, the keys in the hash begin with
20
+ # :default_. So not override initialize; the work is done elsewhere.
21
+ def initialize(defaults = {})
22
+ @defaults = defaults
23
+ end
24
+
25
+ # This does the work of accepting a message and doing something useful
26
+ # with it. Must be overridden. The _scandal_ is a short description.
27
+ # Some types of Crony (such as twitter) pay attention only to it, and
28
+ # ignore the _details_.
29
+ def hear(scandal, details)
30
+ subclass_responsibility
31
+ end
32
+
33
+ # This method returns a string identifying the Crony. It is used
34
+ # in error messages. Must be overridden.
35
+ def name
36
+ subclass_responsibility
37
+ end
38
+
39
+ # This symbol identifies the crony within Ruby code. Must be overridden.
40
+ def symbol
41
+ subclass_responsibility
42
+ end
43
+
44
+ # There is a single configuration choice used to include the Crony in the
45
+ # gossip or exclude her. This method returns an array of strings describing
46
+ # that option. It should be in the format used by OptionParser (optparse).
47
+ # Must be overridden.
48
+ def command_line_description
49
+ subclass_responsibility
50
+ end
51
+
52
+ # This method describes how the user can control the Crony's behavior
53
+ # in a config file or on the command-line. This method is called from
54
+ # UserChoices::Command#add_choices; see its documentation (in the
55
+ # user-choices gem). Or look at subclasses for examples.
56
+ #
57
+ # Don't bother overriding this if there are no configuration choices.
58
+ def add_configuration_choices(builder)
59
+ end
60
+
61
+ # This method is called from UserChoices::Command::postprocess_user_choices.
62
+ # See the documentation for the user-choices gem.
63
+ def postprocess_user_choices
64
+ end
65
+
66
+ attr_writer :is_bff_by_default, :user_choices # :nodoc:
67
+
68
+ # By default, will this Crony be told gossip?
69
+ def is_bff_by_default?; @is_bff_by_default; end
70
+
71
+ # Is this Crony, at this moment, a Best Friend Forever who will be
72
+ # told gossip by the Preteen? (is_bff_by_default? is used to find the
73
+ # starting value of this boolean.)
74
+ def is_bff?
75
+ @user_choices[symbol]
76
+ end
77
+
78
+ def add_bff_choice(builder) # :nodoc:
79
+ builder.add_choice(self.symbol,
80
+ :type => :boolean,
81
+ :default => is_bff_by_default?) { | command_line |
82
+ command_line.uses_switch(*command_line_description)
83
+ }
84
+ end
85
+
86
+ def checked(symbol) # :nodoc:
87
+ prog1(@defaults[symbol]) do | value |
88
+ if value.nil?
89
+ raise StandardError,
90
+ "#{symbol.inspect} is nil for #{name} - likely a typo in a configuration file.\n" +
91
+ @defaults.inspect
92
+ end
93
+ end
94
+ end
95
+
96
+ def df(symbol) # :nodoc:
97
+ "Defaults to #{checked(symbol).inspect}."
98
+ end
99
+ end
100
+
101
+ end
102
+