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,47 @@
1
+ #! /opt/local/bin/ruby
2
+ #
3
+ # Created by Brian Marick on 2007-09-15.
4
+ # Copyright (c) 2007. All rights reserved.
5
+
6
+
7
+
8
+ require 'thread'
9
+
10
+ class Queue # :nodoc:
11
+ def to_a
12
+ result = []
13
+ until empty?
14
+ result << self.pop
15
+ end
16
+ result
17
+ end
18
+ end
19
+
20
+ module Gossip
21
+ # This collects exceptions from several threads and packages them up
22
+ # into a single exception.
23
+ class MultiException < StandardError
24
+
25
+ attr_reader :traces
26
+
27
+ def initialize(exceptions)
28
+ messages = exceptions.collect { | ex | "#{ex.to_s} (#{ex.class})" }
29
+ super(messages.join("\n"))
30
+ @traces = exceptions.collect { | ex | ex.backtrace }
31
+ end
32
+
33
+ def self.reraise_with_combined_backtrace
34
+ yield
35
+ rescue MultiException => ex
36
+ ex.traces.each_with_index { | trace, index |
37
+ trace.unshift("Trace number #{index}:\n")
38
+ }
39
+ ex.set_backtrace(ex.traces.flatten)
40
+ raise
41
+ end
42
+
43
+
44
+ end
45
+
46
+ end
47
+
@@ -0,0 +1,86 @@
1
+ #! /opt/local/bin/ruby
2
+ #
3
+ # Created by Brian Marick on 2007-09-15.
4
+ # Copyright (c) 2007. All rights reserved.
5
+
6
+ require 'gossip/multi-exceptions'
7
+ require 'gossip/crony'
8
+
9
+ module Gossip
10
+
11
+ # A Preteen is someone who tells her Best Friends Forever some gossip.
12
+ # Each BFF is a Crony, but some Cronies can be out of favor during a
13
+ # particular invocation of a script. A Crony who's out of favor is not
14
+ # told the gossip.
15
+ class Preteen
16
+ include Gossip
17
+
18
+ attr_reader :cronies
19
+
20
+ # The second and third arguments are symbols that name Crony subclasses.
21
+ # (See Crony#symbol.) The first
22
+ # set will hear gossip; the second will not. The _cronymaker_ is a hash.
23
+ # It is indexed by crony symbol. The values are blocks that first load
24
+ # the Crony subclass's source, then return a new crony of that class.
25
+ def initialize(cronymaker = {}, bff =[], on_the_outs_forever_and_ever_until_tomorrow=[])
26
+ @cronies = []
27
+ (bff+on_the_outs_forever_and_ever_until_tomorrow).each do | name |
28
+ user_claims(cronymaker.has_key?(name)) {
29
+ "#{name.inspect} is not a known crony."
30
+ }
31
+ crony = cronymaker[name].call
32
+ crony.is_bff_by_default = bff.include?(name)
33
+ accept(crony)
34
+ end
35
+ end
36
+
37
+ def accept(*cronies) # :nodoc:
38
+ @cronies += cronies
39
+ end
40
+
41
+ # Tell Crony objects who are in favor (best friends forever) a _scandal_
42
+ # and its _details_. The _scandal_ should be short - think an email
43
+ # Subject line.
44
+ def tell_bffs(scandal, details)
45
+ simultaneously do | crony |
46
+ crony.hear(scandal, details) if crony.is_bff?
47
+ end
48
+ end
49
+
50
+
51
+
52
+ private
53
+
54
+ def annotate(exception, crony)
55
+ # Note: cannot raise exception.class because on Windows the
56
+ # exception message for Net errors will contain duplicate errors.
57
+ # This is annoying because with_pleasant_exceptions appends the
58
+ # exception class. So we have to append it ourselves.
59
+ annotated = StandardError.new("Complaint from #{crony.name}: #{exception.to_s} (#{exception.class})")
60
+ annotated.set_backtrace(exception.backtrace)
61
+ annotated
62
+ end
63
+
64
+
65
+ def simultaneously # execute block in thread
66
+ Thread.abort_on_exception = false
67
+ queue = Queue.new
68
+ threads = @cronies.collect do | crony |
69
+ Thread.new(crony) do | crony |
70
+ begin
71
+ yield(crony)
72
+ rescue Exception => ex
73
+ queue << annotate(ex, crony)
74
+ end
75
+ end
76
+ end
77
+ threads.each { | thread | thread.join }
78
+ # This peculiarness is the way to set the exception's
79
+ # backtrace with the combination of the backtraces of
80
+ # all saved exceptions (if any)
81
+ MultiException.reraise_with_combined_backtrace {
82
+ raise MultiException.new(queue.to_a) if queue.length > 0
83
+ }
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,94 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Created by Brian Marick on 2007-09-23.
4
+ # Copyright (c) 2007. All rights reserved.
5
+
6
+ # Suppose you want to use many of these example programs. Chances are, you
7
+ # want each one to use the same site-wide defaults. Change this config file,
8
+ # put it somewhere, and change the examples you use to load it.
9
+
10
+ # CHANGE: These arrays describe the destinations for gossip messages.
11
+ # Unless defaults are overridden, output will go only to the BFFS
12
+ # The difference between ON_THE_OUTS and TOTAL_OUTSIDERS is that the
13
+ # user can override ON_THE_OUTS. For example, the -j command-line
14
+ # option will cause messages to be sent to Jabber. A TOTAL_OUTSIDER
15
+ # can never be sent a message. This is useful because, for example,
16
+ # you don't even need to have the twitter gem on your system when
17
+ # :twitter is a TOTAL_OUTSIDER.
18
+
19
+ BFFS = [:standard_output] # By default, gossip with these
20
+ ON_THE_OUTS = [:mail, :jabber, :campfire, :twitter, :trac] # By default, do not gossip with these.
21
+ TOTAL_OUTSIDERS = [] # These will never ever gossiped with.
22
+
23
+
24
+ # CHANGE: The following sets those values the program will use unless the
25
+ # invoker overrides them on either (or both) the command line or YAML
26
+ # configuration file. If you do not intend to use one of the destinations
27
+ # (twitter, say), you can just ignore the values.
28
+
29
+ when_not_TOTALLY_out_of_the_social_scene :jabber do
30
+ require 'gossip/cronies/jabber'
31
+ JabberCrony.new(:default_to => ['listener@example.com', 'other@example.com'],
32
+ :default_account => 'watchdog@example.com',
33
+ :default_password => 'jabber password')
34
+ end
35
+
36
+ when_not_TOTALLY_out_of_the_social_scene :mail do
37
+ require 'gossip/cronies/smtp'
38
+ SmtpCrony.new(:default_to => ['listener@example.com', 'other@example.com'],
39
+ :default_from => 'watchdog@example.com',
40
+ :default_from_domain => 'localhost',
41
+ :default_smtp_server => 'example.com',
42
+ :default_smtp_port => 25,
43
+ :default_smtp_account => 'watchdog',
44
+ :default_smtp_password => 'mail password',
45
+ :default_smtp_authentication => 'login')
46
+ end
47
+
48
+ when_not_TOTALLY_out_of_the_social_scene :campfire do
49
+ require 'gossip/cronies/campfire'
50
+ CampfireCrony.new(:default_login => 'watchdog@example.com',
51
+ :default_password => 'campfire password',
52
+ :default_subdomain => 'project',
53
+ :default_room => 'activity')
54
+ end
55
+
56
+ when_not_TOTALLY_out_of_the_social_scene :trac do
57
+ require 'gossip/cronies/trac'
58
+ TracCrony.new(:default_trac_admin_path => '/usr/local/bin/trac-admin',
59
+ :default_environment_path => '/home/user/trac_env1',
60
+ :default_page_name => 'AnnouncingSuccessfulDeployment',
61
+ :default_content_file => '/home/user/tmp/deployment')
62
+ end
63
+
64
+ when_not_TOTALLY_out_of_the_social_scene :twitter do
65
+ require 'gossip/cronies/twitter'
66
+ TwitterCrony.new(:default_login => 'marick@exampler.com',
67
+ :default_password => 'twitter password')
68
+ end
69
+
70
+
71
+ when_not_TOTALLY_out_of_the_social_scene :standard_output do
72
+ require 'gossip/cronies/stdout'
73
+ StdoutCrony.new
74
+ end
75
+
76
+
77
+
78
+ # CHANGE: add_sources if, for example, you want to override defaults using an
79
+ # XML file instead of a YAML file.
80
+
81
+ class GossipCommand
82
+
83
+ # This is the name (in the home directory) of the config file that
84
+ # applies to all Gossip scripts.
85
+ def gossip_config_file; ".gossiprc"; end
86
+
87
+
88
+ # Determine how the user can override defaults.
89
+ def add_sources(builder)
90
+ builder.add_source(PosixCommandLineSource, :usage, *describe_all_but_options)
91
+ builder.add_source(YamlConfigFileSource, :from_file, script_config_file)
92
+ builder.add_source(YamlConfigFileSource, :from_file, gossip_config_file)
93
+ end
94
+ end
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Created by Brian Marick on 2007-09-17.
4
+ # Copyright (c) 2007. All rights reserved.
5
+
6
+ module Gossip
7
+
8
+ CronyMaker = {}
9
+
10
+ # Syntactic sugar to associate a symbol naming a Crony subclass
11
+ # with a block that knows how to load that class and create an object
12
+ # from it. The symbol must be one returned by Crony#symbol.
13
+
14
+ def when_not_TOTALLY_out_of_the_social_scene(crony, &crony_maker)
15
+ CronyMaker[crony] = crony_maker
16
+ end
17
+
18
+ end
@@ -0,0 +1,8 @@
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
+ Version = '0.3.0'
8
+ end
@@ -0,0 +1,58 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
+ <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
4
+ <head>
5
+ <title>Gossip</title>
6
+ <meta http-equiv="Content-Type" content="application/xhtml+xml" />
7
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
8
+ <link rel="stylesheet" type="text/css" media="all" href="css/main.css" />
9
+ </head>
10
+ <body>
11
+ <div id="pageBody">
12
+ <div id="outer-container">
13
+ <div id="container">
14
+ <div id="header">
15
+ <div id="headerLeft"></div>
16
+ <div id="headerRight"><h1>Gossip</h1><span class="highlight"><h2>tools for telling people things</h2></span></div>
17
+ </div>
18
+ <div id="headerNav">
19
+ <div id="centerNav">
20
+ <ul>
21
+ <li><a href="index.html">Home</a></li>
22
+ <li><a href="installation.html">Installation</a></li>
23
+ <li><a href="starting-to-use.html">Using Bundled Scripts</a></li>
24
+ <li><a href="writing-new-scripts.html">Writing Scripts</a></li>
25
+ <li><a href="rdoc">RDoc</a></li>
26
+ <li><a href="http://rubyforge.org/frs/?group_id=4455">Download</a></li>
27
+ </ul>
28
+ </div>
29
+ </div>
30
+ <div id="content">
31
+ <h2>The pieces</h2>
32
+ <p>
33
+ <img alt="A class diagram of sorts" src="images/classes.png" class="right" />
34
+ A gossip script doesn't do much more than configure a GossipCommand subclass and then tell it what to do. See "Using Bundled Scripts" for how configuration works.
35
+ </p>
36
+ <p>
37
+ Gossip uses preteens as its <a href="http://c2.com/xp/SystemMetaphor.html" title="System Metaphor">system metaphor</a>. However, the reader should not assume that the metaphor was inspired by any <em>particular</em> 11-year-old and her social circle. Nor does the use of the variable <code>sophie</code> in the tests mean anything at all. No sirree.
38
+ </p>
39
+
40
+ <p>
41
+ In any case, the GossipCommand's <code>execute</code> sends a Preteen a tell_bff message to have her tell some scandal and its details to her Best Friends Forever (her bffs). These bffs have names like <code>:twitter</code>, <code>:jabber</code>, or <code>:trac</code>. They are instances of the Crony class (more precisely, of a Crony subclass).
42
+ </p>
43
+
44
+ <p>
45
+ Notice that the "best" in "Best Friends Forever" is not to be taken literally. It is quite typical to have more than one bff. Similarly, "forever" is more an an aspiration than a literal fact. The set of bffs can change from one invocation of the script to another (according to changes in configuration files or command-line options).
46
+ </p>
47
+ <p>
48
+ Cronies who are not currently in the set of bffs do not hear the gossip.
49
+ </p>
50
+ <p>
51
+ Outside the world of the Preteen and her Cronies, there are&mdash;theoretically&mdash;other people. However, it is inconceivable that they could ever become a Crony, so they can be ignored. (That means that a person writing a script could provide no <code>--trac</code> option. You might do that if you don't use Trac on your project.)
52
+ </p>
53
+ </div>
54
+ </div>
55
+ </div>
56
+ </body>
57
+ </html>
58
+
@@ -0,0 +1,256 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
+ <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
4
+ <head>
5
+ <title>Gossip</title>
6
+ <meta http-equiv="Content-Type" content="application/xhtml+xml" />
7
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
8
+ <link rel="stylesheet" type="text/css" media="screen" href="css/main.css" />
9
+ </head>
10
+ <body>
11
+ <div id="pageBody">
12
+ <div id="outer-container">
13
+ <div id="container">
14
+ <div id="header">
15
+ <div id="headerLeft"></div>
16
+ <div id="headerRight"><h1>Gossip</h1><span class="highlight"><h2>tools for telling people things</h2></span></div>
17
+ </div>
18
+ <div id="headerNav">
19
+ <div id="centerNav">
20
+ <ul>
21
+ <li><a href="index.html">Home</a></li>
22
+ <li><a href="installation.html">Installation</a></li>
23
+ <li id="current"><a href="starting-to-use.html">Using Bundled Scripts</a></li>
24
+ <li><a href="writing-new-scripts.html">Writing Scripts</a></li>
25
+ <li><a href="rdoc">RDoc</a></li>
26
+ <li><a href="http://rubyforge.org/frs/?group_id=4455">Download</a></li>
27
+ </ul>
28
+ </div>
29
+ </div>
30
+ <div id="content">
31
+ <div id="inlineTOC">
32
+ <ul>
33
+ <li><a href="starting-to-use.html">Getting started</a></li>
34
+ <ul>
35
+ <li><a href="starting-to-use.html#definitions">Three important words</a></li>
36
+ <li><a href="starting-to-use.html#config">Your own private configuration</a></li>
37
+ </ul>
38
+ <li><a href="cronies.html">All cronies</a></li>
39
+ <ul>
40
+ <li><a href="cronies.html#campfire">campfire</a></li>
41
+ <li><a href="cronies.html#jabber">jabber</a></li>
42
+ <li><a href="cronies.html#mail">mail</a></li>
43
+ <li><a href="cronies.html#stdout">standard output</a></li>
44
+ <li><a href="cronies.html#trac">trac</a></li>
45
+ <li><a href="cronies.html#twitter">twitter</a></li>
46
+ </ul>
47
+ <li><a href="scripts.html">All scripts</a></li>
48
+ <ul>
49
+ <li><a href="scripts.html#fanout">fanout: broadcast a message</a></li>
50
+ <li><a href="scripts.html#watchdog">watchdog: report on a script</a></li>
51
+ <li><a href="scripts.html#svntell">svntell: Subversion post-commit</a></li>
52
+ </ul>
53
+ </ul>
54
+ </div>
55
+ <h2>The different cronies</h2>
56
+
57
+ <p>
58
+ Here are the capsule descriptions:
59
+ </p>
60
+
61
+ <ul>
62
+ <li>
63
+ <p>
64
+ The <strong><a href="#campfire">campfire crony</a></strong> talks to <a href="http://campfirenow.com/" title="Business chat for groups: Campfire">Campfire</a>, a browser-based chat room.
65
+ </p>
66
+ </li>
67
+ <li>
68
+ <p>
69
+ The <strong><a href="#jabber">jabber crony</a></strong> talks <a href="http://www.jabber.org/" title="Jabber: Open Instant Messaging and a Whole Lot More, Powered by XMPP">Jabber</a>, a chat protocol with servers that are easy to set up, making it nice for workgroups.
70
+ </p>
71
+ </li>
72
+ <li>
73
+ <p>
74
+ The <strong><a href="#mail">mail crony</a></strong> speaks to email servers via the SMTP protocol. SSL is not supported.
75
+ </p>
76
+ </li>
77
+ <li>
78
+ <p>
79
+ The <strong><a href="#stdout">standard output crony</a></strong> just sends mail to standard output (the current window). It's useful checking whether (for example), you're using <code>watchdog</code> correctly before blasting the results to the entire world.
80
+ </p>
81
+ </li>
82
+ <li>
83
+ <p>
84
+ The <strong><a href="#trac">trac crony</a></strong> puts a notice in a <a href="http://trac.edgewall.org/" title="The Trac Project - Trac">Trac</a> timeline.
85
+ </p>
86
+ </li>
87
+ <li>
88
+ <p>
89
+ The <strong><a href="#twitter">twitter crony</a></strong> sends an update to a <a href="http://twitter.com/home" title="Twitter">Twitter</a> account.
90
+ </p>
91
+ </li>
92
+ </ul>
93
+
94
+ <h2><a name="campfire">Campfire</a></h2>
95
+ <p>My configuration file turns the <code>campfile</code> crony off. I can turn it on with the <code>-c</code> (or <code>--campfire</code>) option, like this:
96
+
97
+ <pre>
98
+ % watchdog -c rake
99
+ </pre>
100
+
101
+ <p>
102
+ The result at wevouchfor.campfirenow.com looks like this:
103
+ </p>
104
+
105
+ <img class="center" src="images/campfire.png" />
106
+
107
+
108
+ <p>
109
+ The scandal is the first line of the message; the details are the rest.
110
+ </p>
111
+ <p>
112
+ To use the <code>campfire</code> crony, you must have a <a href="http://campfirenow.com/" title="Business chat for groups: Campfire">Campfire</a> account with a chat room set up. Set the following in a configuration file or on the command line:
113
+ </p>
114
+
115
+ <ul>
116
+ <li><strong>campfire-login</strong>: Usually an email address.</li>
117
+ <li><strong>campfire-password</strong></li>
118
+ <li><strong>campfire-subdomain</strong>: Each campfire group gets its own subdomain. Mine is <a href="http://wevouchfor.campfirenow.com/" title="We Vouch For...">http://wevouchfor.campfirenow.com</a>. So my value for this choice is <code>wevouchfor</code>.</li>
119
+ <li><strong>campfire-room</strong>: A group can have multiple rooms, each with its own name. The one I've used in this case is <code>wevouchfor activity</code>.</li>
120
+ </ul>
121
+
122
+ <h2><a name="jabber">Jabber</a></h2>
123
+ <p>
124
+ Here is an example of a continuous build server telling jabber (and perhaps other cronies) about a broken build:
125
+ </p>
126
+
127
+ <img class="center" src="images/jabber-big.png" />
128
+
129
+ <p>
130
+ The scandal is the first line of the message; the details are the rest.
131
+ </p>
132
+
133
+ <p>
134
+ To use the <code>jabber</code> crony, you must have an account on some <a href="http://www.jabber.org/" title="Jabber: Open Instant Messaging and a Whole Lot More, Powered by XMPP">Jabber</a> server. In this example, Jabber messages come from <code>huivoufor@jabber.se</code>.
135
+ </p>
136
+
137
+
138
+ <ul>
139
+ <li><strong>jabber-account</strong>: <code>huivoufor@jabber.se</code>, in this case.</li>
140
+ <li><strong>jabber-password</strong></li>
141
+ <li><strong>jabber-to</strong>: One or more accounts to sent to. In YAML, that would look like this:
142
+ <pre>
143
+ jabber-to:
144
+ - brianmarick@jabberplace.org
145
+ - someone@xmpphost.org
146
+ </pre>
147
+
148
+ <p>If you only want one account to receive the message, you can abbreviate
149
+ the YAML to this:
150
+ </p>
151
+ <pre>
152
+ jabber-to: brianmarick@jabberplace.org
153
+ </pre>
154
+
155
+ <p>One the command line, multiple accounts are separated by commas, like this:
156
+ <pre>
157
+ --jabber-to=fred@example.com,brianmarick@example.com
158
+ </pre>
159
+
160
+ </li>
161
+ </ul>
162
+
163
+ <h2><a name="mail">Mail</a></h2>
164
+ <p>
165
+ When mail is sent, the scandal is used as the subject line and the details are put into the body. Here are the choices you have to make:
166
+ </p>
167
+ <ul>
168
+ <li><strong>mail-from</strong>: What's put in the From line of the mail message.</li>
169
+ <li><strong>mail-to</strong>: The intended recipients.
170
+ <pre>
171
+ mail-to:
172
+ - marick@example.com
173
+ - person@example.org
174
+ </pre>
175
+
176
+ <p>If you only want one account to receive the mail, you can abbreviate
177
+ the YAML to this:
178
+ </p>
179
+ <pre>
180
+ mail-to: marick@example.com
181
+ </pre>
182
+
183
+ <p>One the command line, multiple accounts are separated by commas, like this:
184
+ <pre>
185
+ --mail-to=marick@example.com,person@example.com
186
+ </pre>
187
+ </li>
188
+ <li><strong>mail-server</strong>: An SMTP server.</li>
189
+ <li><strong>mail-port</strong></li>
190
+ <li><strong>mail-from-domain</strong>: The domain the mail claims to be from. Some SMTP servers look at this. The default is localhost.</li>
191
+ <li><strong>mail-account</strong>: An account on the server. Not necessarily the same as <code>mail-from</code>.</li>
192
+ <li><strong>mail-authentication</strong>: One of <code>plain</code>, <code>login</code>, or <code>cram_md5</code>.</li>
193
+ <li><strong>mail-password</strong></li>
194
+ </ul>
195
+
196
+ <h2><a name="stdout">Standard output</a></h2>
197
+ <p>This crony just prints the scandal and details to the current terminal window. There are no options.</p>
198
+ <h2><a name="trac">Trac</a></h2>
199
+ <p>
200
+ The <code>trac</code> crony puts a notice in the <a href="http://trac.edgewall.org/" title="The Trac Project - Trac">Trac</a> timeline, where it looks like this:
201
+ </p>
202
+
203
+ <img class="center" src="images/trac-bigger.png" />
204
+
205
+ <p>
206
+ The mechanism is a hack. To do it right, you'd have to write a plugin and enable XMP/RPC and whatnot. So the implementation is to edit a Wiki page, turning the scandal into a header and putting the details below it. That looks like this:
207
+ </p>
208
+
209
+ <img class="center" src="images/trac-detail.png" />
210
+
211
+ <p>
212
+ Then, if the Trac timeline is set to display changes to Wiki pages (configurable per-user on the Timeline page), the changed page's title will appear in the Timeline. You have to click through to see the scandal and details.
213
+ </p>
214
+
215
+ <p>
216
+ To further ease the implementation, the editing of the Wiki page is done on the server with the <code>trac-admin</code> command. That means the Gossip script has to run on the server too.
217
+ </p>
218
+
219
+ <p>It would be swell if someone were to contribute a less hackish implementation.
220
+ </p>
221
+ <p>
222
+ To use this implementation, you have to set the following:
223
+ </p>
224
+ <ul>
225
+ <li><strong>trac-admin-path</strong>: The full path of the <code>trac-admin</code> program.</li>
226
+ <li><strong>trac-environment-path</strong>: The full path of the Trac environment you're using (such as <code>/home/user/trac_env1</code>).</li>
227
+ <li><strong>trac-page-name</strong>: The name of the wiki page to change.</li>
228
+ <li><strong>trac-content-file</strong>: The wiki page is changed by copying this file over its contents. Before the Trac crony does that, it puts the scandal and details at the beginning.</li>
229
+ </ul>
230
+
231
+ <h2><a name="twitter">Twitter</a></h2>
232
+ <p>
233
+ Because <a href="http://twitter.com/home" title="Twitter">Twitter</a> is meant for short messages, only the scandal is sent:
234
+ </p>
235
+ <img class="center" src="images/twitter.png" />
236
+
237
+ <p>
238
+ To use the <code>twitter</code> crony, you need a Twitter account. Messages are sent to that account for others to view. The configuration options are:
239
+ </p>
240
+ <ul>
241
+ <li><strong>twitter-login</strong></li>
242
+ <li><strong>twitter-password</strong></li>
243
+ </ul>
244
+
245
+ <p style="text-align: center">
246
+ <a href="scripts.html">Next</a>: All scripts<br />
247
+ </p>
248
+
249
+
250
+ </div>
251
+ </div>
252
+ </div>
253
+
254
+ </body>
255
+ </html>
256
+