RISCfuture / autumn
- Source
- Commits
- Network (22)
- Issues (4)
- Downloads (3)
- Wiki (15)
- Graphs
-
Tree:
26f6cd2
Tim Morgan (author)
Fri May 29 17:52:21 -0700 2009
commit 26f6cd28d67c5b545cabe5cbc50770b3c99d51c0
tree 2d09abeb7b3592299a6afa1276d253a530afdfd2
parent 23191f4c84046283a1b8a53e204ad2bc601f408e
tree 2d09abeb7b3592299a6afa1276d253a530afdfd2
parent 23191f4c84046283a1b8a53e204ad2bc601f408e
autumn / README.textile
| c6d61e59 » | Tim Morgan | 2008-07-03 | 1 | h1. Autumn: A Ruby IRC Bot Framework | |
| d1777acd » | Tim Morgan | 2008-07-04 | 2 | ||
| f185a31b » | Tim Morgan | 2008-07-04 | 3 | *Version 3.0 (Jul 4, 2008)* | |
| c6d61e59 » | Tim Morgan | 2008-07-03 | 4 | ||
| 879b3971 » | Tim Morgan | 2008-07-22 | 5 | | Author | Tim Morgan (riscfuture@gmail.com) | | |
| c6d61e59 » | Tim Morgan | 2008-07-03 | 6 | | Copyright | Copyright (c)2007-2008 Tim Morgan | | |
| d5069e22 » | Tim Morgan | 2008-07-03 | 7 | | License | Distributed under the same terms as Ruby. Portions of this code are copyright (c)2004 David Heinemeier Hansson; please see <tt>libs/inheritable_attributes.rb</tt> for more information. | | |
| c6d61e59 » | Tim Morgan | 2008-07-03 | 8 | ||
| 9 | Autumn is a full-featured framework on top of which IRC bots (called "leaves") | ||||
| 10 | can be quickly and easily built. It features a very Ruby-like approach to | ||||
| 11 | bot-writing, a complete framework for loading and daemonizing your bots, | ||||
| 12 | multiple environment contexts, a database-backed model, and painless logging | ||||
| 13 | support. | ||||
| 14 | |||||
| 15 | h2. Requirements | ||||
| 16 | |||||
| 17 | Autumn requires "RubyGems":http://www.rubygems.org and the Daemons and Facets* | ||||
| 770dc2cb » | Tim Morgan | 2008-10-06 | 18 | gems, as well as some of the gems spun off from Facets. Install RubyGems then | |
| 49fe9a47 » | Tim Morgan | 2008-12-11 | 19 | run @sudo gem install daemons facets anise english@ in a command line in order | |
| 20 | to run Autumn. | ||||
| c6d61e59 » | Tim Morgan | 2008-07-03 | 21 | ||
| 22 | If you wish to use a database backend for your bot, you will need the DataMapper | ||||
| 23 | gem. To install, see the "DataMapper website":http://www.datamapper.org. | ||||
| 24 | |||||
| f185a31b » | Tim Morgan | 2008-07-04 | 25 | The included example bot Scorekeeper requires the DataMapper gem. It can | |
| 26 | optionally use the Chronic gem to enhance its textual date parsing. The other | ||||
| 27 | example bot, Insulter, is much simpler and can run under any Autumn | ||||
| 28 | configuration. | ||||
| c6d61e59 » | Tim Morgan | 2008-07-03 | 29 | ||
| 30 | h2. Directory Structure | ||||
| 31 | |||||
| 32 | An Autumn installation is like a Ruby on Rails installation: There is a | ||||
| 33 | certain directory structure where your files go. A lot of files and folders will | ||||
| 34 | seem confusing to people who have never used Autumn before, but bear with me. In | ||||
| 35 | a bit I will explain in detail what all of this stuff is. For now, here is an | ||||
| 36 | overview you can consult for future reference: | ||||
| 37 | |||||
| f185a31b » | Tim Morgan | 2008-07-04 | 38 | * *config/* - Configuration files and season definitions | |
| d5069e22 » | Tim Morgan | 2008-07-03 | 39 | ** global.yml - Universal settings that apply to every season | |
| f185a31b » | Tim Morgan | 2008-07-04 | 40 | *** *seasons/* - Contains directories for each season (see *Seasons*) | |
| 41 | **** *testing/* - Example season | ||||
| 42 | ***** database.yml - Example database configuration file | ||||
| 43 | ***** leaves.yml - Example bot configuration file | ||||
| 44 | ***** season.yml - Season configuration | ||||
| 45 | ***** stems.yml - Example IRC configuration file | ||||
| c6d61e59 » | Tim Morgan | 2008-07-03 | 46 | * *doc/* - HTML documentation generated by RDoc | |
| d5069e22 » | Tim Morgan | 2008-07-03 | 47 | ** *api/* - Autumn API documentation | |
| 48 | ** *leaves/* - Autumn leaves documentation | ||||
| f185a31b » | Tim Morgan | 2008-07-04 | 49 | * *leaves/* - Autumn leaves. Each subdirectory contains all the code and | |
| 50 | data for a leaf. | ||||
| 51 | ** *insulter/* - Very simple example leaf | ||||
| 52 | *** _See the *scorekeeper* directory_ | ||||
| 53 | ** *scorekeeper/* - Database-backed, full-featured example leaf | ||||
| 54 | *** config.yml - Optional leaf-global configuration options | ||||
| 55 | *** controller.rb - The leaf's controller object | ||||
| 56 | *** *data/* - Optional directory for data storage (not used by Autumn) | ||||
| 57 | *** *helpers/* - Modules that extend the controller and views | ||||
| 26f6cd28 » | Tim Morgan | 2009-05-29 | 58 | *** *lib/* - Library files loaded before the leaf | |
| f185a31b » | Tim Morgan | 2008-07-04 | 59 | *** *models/* - Active record-type database objects | |
| 60 | *** *tasks/* - Additional rake tasks for this leaf (see *Custom leaf tasks*) | ||||
| 61 | *** *views/* - ERb views for each of the leaf's commands | ||||
| c6d61e59 » | Tim Morgan | 2008-07-03 | 62 | * *libs/* - Autumn core code | |
| d5069e22 » | Tim Morgan | 2008-07-03 | 63 | ** channel_leaf.rb - A leaf subclass that can ignore messages from certain | |
| 64 | channels its in | ||||
| f185a31b » | Tim Morgan | 2008-07-04 | 65 | ** coder.rb - Used by script/generate to write out Ruby code | |
| d5069e22 » | Tim Morgan | 2008-07-03 | 66 | ** ctcp.rb - CTCP support library | |
| 67 | ** daemon.rb - Provides support for different kinds of IRC servers | ||||
| 0d2d62f1 » | Tim Morgan | 2008-07-05 | 68 | ** datamapper_hacks.rb - Some hacks to help DataMapper work with Autumn | |
| d5069e22 » | Tim Morgan | 2008-07-03 | 69 | ** foliater.rb - Instantiates and manages stems and leaves | |
| 70 | ** formatting.rb - Provides support for different kinds of IRC client text | ||||
| 71 | formatting and colorization | ||||
| f185a31b » | Tim Morgan | 2008-07-04 | 72 | ** generator.rb - Library used by script/generate | |
| d5069e22 » | Tim Morgan | 2008-07-03 | 73 | ** genesis.rb - Boots the Autumn environment | |
| 74 | ** inheritable_attributes.rb - Adds support for class-level inheritable | ||||
| 75 | attributes | ||||
| 76 | ** leaf.rb - The core bot superclass | ||||
| 77 | ** log_facade.rb - Simplifies logging for stems and leaves | ||||
| 78 | ** misc.rb - RubyCore class additions and other knick-knacks | ||||
| f185a31b » | Tim Morgan | 2008-07-04 | 79 | ** script.rb - Library used by script/generate and script/destroy | |
| d5069e22 » | Tim Morgan | 2008-07-03 | 80 | ** speciator.rb - Manages global, season, stem, and leaf configurations | |
| 81 | ** stem.rb - IRC client library | ||||
| f185a31b » | Tim Morgan | 2008-07-04 | 82 | ** stem_facade.rb - Additional methods to simplify the Stem class | |
| c6d61e59 » | Tim Morgan | 2008-07-03 | 83 | * *log/* - Directory where (most) Autumn logs are written (see the *Logs* | |
| 84 | section) | ||||
| 85 | * Rakefile - Contains the rake tasks used to control Autumn (see the *Tasks* | ||||
| 86 | section) | ||||
| 0d2d62f1 » | Tim Morgan | 2008-07-05 | 87 | * README - RDoc-formatted readme | |
| 88 | * README.textile - This file | ||||
| c6d61e59 » | Tim Morgan | 2008-07-03 | 89 | * *resources/* - Data files used by Autumn | |
| d5069e22 » | Tim Morgan | 2008-07-03 | 90 | ** *daemons/* - Data files describing different IRC server types | |
| c6d61e59 » | Tim Morgan | 2008-07-03 | 91 | * *script/* - Helper scripts for controlling Autumn | |
| d5069e22 » | Tim Morgan | 2008-07-03 | 92 | ** daemon - Runs Autumn as a daemon | |
| 93 | ** destroy - Destroys Autumn objects | ||||
| 94 | ** generate - Creates Autumn objects | ||||
| 95 | ** server - Starts Autumn | ||||
| f185a31b » | Tim Morgan | 2008-07-04 | 96 | * *shared/* - Shared code libraries available to all leaves | |
| c6d61e59 » | Tim Morgan | 2008-07-03 | 97 | * *tmp/* - Temporary files, such as PID files | |
| 98 | |||||
| 99 | h2. Configuring Autumn for Your First Launch | ||||
| 100 | |||||
| f185a31b » | Tim Morgan | 2008-07-04 | 101 | Before you can run Autumn and try out the example leaves, you'll need to set up | |
| 102 | a few things. Here are the steps: | ||||
| c6d61e59 » | Tim Morgan | 2008-07-03 | 103 | ||
| 104 | h3. Configure Your Testing Season | ||||
| 105 | |||||
| 106 | In Autumn, your leaves run in an environment, called a "season." Each season has | ||||
| 107 | different leaves and different settings for those leaves. By default, Autumn | ||||
| 108 | comes with a season called "testing" already set up for you. You can edit that | ||||
| 1bf60b40 » | Tim Morgan | 2008-07-05 | 109 | season or create a new one with @script/generate season [season name]@. The | |
| 110 | files for your season are stored in the <tt>config/seasons</tt> directory. | ||||
| c6d61e59 » | Tim Morgan | 2008-07-03 | 111 | ||
| 112 | First, edit the <tt>stems.yml</tt> file. This file stores information about your | ||||
| 113 | IRC connection. Edit it to connect to an IRC server of your choosing. For more | ||||
| f185a31b » | Tim Morgan | 2008-07-04 | 114 | information, see *Stems* below. | |
| c6d61e59 » | Tim Morgan | 2008-07-03 | 115 | ||
| 116 | Next, edit the <tt>database.yml</tt> file. As mentioned previously, Scorekeeper | ||||
| 117 | requires the DataMapper gem because it uses a persistent store. By default it's | ||||
| 118 | set up to use a MySQL database, but you can use PostgreSQL or SQLite 3 if you'd | ||||
| f185a31b » | Tim Morgan | 2008-07-04 | 119 | like. If you'd prefer not to install any of these database solutions, delete the | |
| 120 | <tt>database.yml</tt> file and remove the Scorekeeper leaf from the | ||||
| 121 | <tt>leaves.yml</tt> and <tt>stems.yml</tt> files. | ||||
| c6d61e59 » | Tim Morgan | 2008-07-03 | 122 | ||
| 0bde0a20 » | Tim Morgan | 2008-07-15 | 123 | If you do choose to set up a database, you will have to run @rake db:migrate@ | |
| 124 | after your <tt>database.yml</tt> file is configured and your database is | ||||
| 125 | created. | ||||
| 126 | |||||
| c6d61e59 » | Tim Morgan | 2008-07-03 | 127 | Lastly, view the <tt>leaves.yml</tt> file. You shouldn't have to make any | |
| 128 | changes to this file, but it's a good idea to look at it to see how leaves are | ||||
| 129 | configured. You can do the same with the <tt>season.yml</tt> file. See *Seasons* | ||||
| 130 | and *Leaves* below for more. | ||||
| 131 | |||||
| 132 | h3. Starting the Server | ||||
| 133 | |||||
| 134 | Run the shell command @script/server@ to start the server. After a short | ||||
| 135 | while, your leaf should appear in the channel you specified. You can type | ||||
| f185a31b » | Tim Morgan | 2008-07-04 | 136 | "!points Coolguy +5" and then "!points" to get started using Scorekeeper, or | |
| 137 | "!insult" to play with Insulter. Have some fun, and when you're satisfied, stop | ||||
| 138 | the server by typing "!quit". | ||||
| c6d61e59 » | Tim Morgan | 2008-07-03 | 139 | ||
| 140 | If you'd like to daemonize your server, you can use the shell commands | ||||
| 141 | @rake app:start@ and @rake app:stop@. For more information, see *Tasks* below. | ||||
| 142 | |||||
| 143 | h2. Making Your Own Leaf | ||||
| 144 | |||||
| 145 | Making your own leaf using Autumn is easy. In this tutorial, I'll show you how | ||||
| 146 | to make a simple Fortune bot that responds to a few basic commands. | ||||
| 147 | |||||
| 148 | h3. Step 1: Subclass Leaf | ||||
| 149 | |||||
| 150 | Create a new leaf by typing @script/generate leaf fortune@. This will create a | ||||
| f185a31b » | Tim Morgan | 2008-07-04 | 151 | <tt>fortune</tt> directory in the <tt>leaves</tt> directory, along with the bare | |
| 152 | bones of files needed within that directory. Edit the <tt>controller.rb</tt> | ||||
| 153 | file. First we'll create an array to hold our fortunes: | ||||
| c6d61e59 » | Tim Morgan | 2008-07-03 | 154 | ||
| 155 | <pre><code> | ||||
| 156 | FORTUNES = [ | ||||
| 157 | "You will make someone happy today.", | ||||
| 158 | "Someone you don't expect will be important to you today.", | ||||
| 159 | "Today will bring unexpected hardships." | ||||
| 160 | ] | ||||
| 161 | </code></pre> | ||||
| 162 | |||||
| 163 | As you can see, our 3 meager fortunes are stored in the @FORTUNES@ class | ||||
| 164 | constant. Now, we'll want it to respond to the "!fortune" command, and all you | ||||
| 165 | have to do is create a method called @fortune_command@ to make it work: | ||||
| 166 | |||||
| 167 | <pre><code> | ||||
| 168 | def fortune_command(stem, sender, reply_to, msg) | ||||
| 6a294b88 » | Tim Morgan | 2009-01-14 | 169 | FORTUNES.pick | |
| c6d61e59 » | Tim Morgan | 2008-07-03 | 170 | end | |
| 171 | </code></pre> | ||||
| 172 | |||||
| 6a294b88 » | Tim Morgan | 2009-01-14 | 173 | The @pick@ method is provided by Facets, so you may need to add a <code>require | |
| 174 | 'facets/random'</code> line at the top of your file. Our method returns a | ||||
| 175 | fortune at random, which is automatically transmitted to the channel or nick | ||||
| 176 | where the command was received. | ||||
| c6d61e59 » | Tim Morgan | 2008-07-03 | 177 | ||
| 178 | Of course, any self-respecting fortune bot announces its presence when it starts | ||||
| 1bf60b40 » | Tim Morgan | 2008-07-05 | 179 | up, so, in your @Controller@ class, override the @Autumn::Leaf#did_start_up@ | |
| 180 | method to display a cheerful greeting: | ||||
| c6d61e59 » | Tim Morgan | 2008-07-03 | 181 | ||
| 182 | <pre><code> | ||||
| 183 | def did_start_up | ||||
| 184 | stems.message 'FortuneBot at your service! Type "!fortune" to get your fortune!' | ||||
| 185 | end | ||||
| 186 | </code></pre> | ||||
| 187 | |||||
| 188 | ...and that's it! You now have a fully functional fortune bot featuring -- not | ||||
| 189 | two -- but _three_ unique and exciting fortunes! | ||||
| 190 | |||||
| 191 | (For more on that @stems.message@ bit, see *Stems*.) | ||||
| 192 | |||||
| 193 | h3. Step 2: Add the Leaf to Your Season | ||||
| 194 | |||||
| 195 | If you want, you can add the fortune bot to your <tt>leaves.yml</tt> and | ||||
| 196 | <tt>stems.yml</tt> files to try it out. Adding a leaf is easy; simply duplicate | ||||
| f185a31b » | Tim Morgan | 2008-07-04 | 197 | the structure used for another leaf's entry and change the values as | |
| 198 | appropriate. A typical two-leaf configuration will look like: | ||||
| c6d61e59 » | Tim Morgan | 2008-07-03 | 199 | ||
| 200 | <pre><code> | ||||
| 201 | Scorekeeper: | ||||
| 202 | class: Scorekeeper | ||||
| 203 | respond_to_private_messages: false | ||||
| 204 | Fortune: | ||||
| 205 | class: Fortune | ||||
| 206 | respond_to_private_messages: true | ||||
| 207 | </code></pre> | ||||
| 208 | |||||
| 1bf60b40 » | Tim Morgan | 2008-07-05 | 209 | As you notice, each leaf instance is given a name. In this example the name | |
| 210 | happens to be the same as the leaf's type name, but you could run two copies of | ||||
| 211 | a leaf like so: | ||||
| c6d61e59 » | Tim Morgan | 2008-07-03 | 212 | ||
| 213 | <pre><code> | ||||
| 214 | Fortune1: | ||||
| 215 | class: Fortune | ||||
| 216 | Fortune2: | ||||
| 217 | class: Fortune | ||||
| 218 | </code></pre> | ||||
| 219 | |||||
| f185a31b » | Tim Morgan | 2008-07-04 | 220 | This doesn't make a whole lot of sense for our fortune bot, but for more | |
| 221 | complicated bots it can be useful. | ||||
| 222 | |||||
| c6d61e59 » | Tim Morgan | 2008-07-03 | 223 | We've created the leaf, but we have to add it to the stem for it to work. | |
| 224 | (Remember, a stem is an IRC connection and a leaf is a bot.) So, in your | ||||
| 225 | <tt>stems.yml</tt> file, add an entry for this leaf. Your new config will appear | ||||
| 226 | something like: | ||||
| 227 | |||||
| 228 | <pre><code> | ||||
| 229 | Example: | ||||
| 230 | nick: Scorekeeper | ||||
| 231 | leaves: | ||||
| 232 | - Scorekeeper | ||||
| f185a31b » | Tim Morgan | 2008-07-04 | 233 | - Insulter | |
| c6d61e59 » | Tim Morgan | 2008-07-03 | 234 | - Fortune | |
| 235 | rejoin: true | ||||
| 236 | channel: somechannel | ||||
| 237 | server: irc.someserver.com | ||||
| 238 | </code></pre> | ||||
| 239 | |||||
| f185a31b » | Tim Morgan | 2008-07-04 | 240 | When you restart the server, the bot will come back online and will now also | |
| 241 | respond to the "!fortune" command. This is a helpful tutorial on how stems and | ||||
| 242 | leaves are separate. One leaf can have many stems, and one stem can have many | ||||
| 243 | leaves. You can combine these two entities however you need. | ||||
| 244 | |||||
| 245 | h3. Step 3: Upgrade to ERb Views | ||||
| 246 | |||||
| 247 | You've already learned that for your @[word]_command@-type methods, the bot | ||||
| 248 | responds with whatever string your method returns. For more complicated | ||||
| 249 | commands, however, you may want to upgrade to full view abstraction, a la Ruby | ||||
| 250 | on Rails. This is what the <tt>views</tt> directory is for. | ||||
| 251 | |||||
| 252 | If you place a <tt>.txt.erb</tt> file in the <tt>views</tt> directory named | ||||
| 253 | after your command, it will be parsed by ERb and rendered as the result. You can | ||||
| 254 | pass variables to the ERb parser by using the @Autumn::Leaf#var@ method. Let's | ||||
| 255 | upgrade our @fortune_command@ method for that: | ||||
| 256 | |||||
| 257 | <pre><code> | ||||
| 258 | def fortune_command(stem, sender, reply_to, msg) | ||||
| 6a294b88 » | Tim Morgan | 2009-01-14 | 259 | var :fortune => FORTUNES.pick | |
| f185a31b » | Tim Morgan | 2008-07-04 | 260 | end | |
| 261 | </code></pre> | ||||
| 262 | |||||
| 263 | We can then write a view, <tt>fortune.txt.erb</tt>, which will render the | ||||
| 264 | fortune: | ||||
| 265 | |||||
| 266 | <code><%= var :fortune %></code> | ||||
| 267 | |||||
| 268 | OK, so admittedly, this doesn't really get us anywhere, but for more complicated | ||||
| 269 | bots, this well help separate view and controller concerns. | ||||
| 270 | |||||
| 271 | For more information on view rendering, see the @Autumn::Leaf#render@ method. | ||||
| c6d61e59 » | Tim Morgan | 2008-07-03 | 272 | ||
| 273 | h2. Seasons | ||||
| 274 | |||||
| f185a31b » | Tim Morgan | 2008-07-04 | 275 | Each time you start Autumn, the process launches in a certain season (a.k.a. | |
| 276 | environment context). This season is defined in the <tt>config/global.yml</tt> | ||||
| 277 | file. You can temporarily override it by setting the @SEASON@ environment | ||||
| 278 | variable (e.g., @SEASON=production script/server@). | ||||
| c6d61e59 » | Tim Morgan | 2008-07-03 | 279 | ||
| 280 | It's important to realize that an season is just a name, nothing more. You can | ||||
| 281 | have as many seasons as you like, and name them anything that you like. Autumn | ||||
| 282 | will load the config files for the season you've indicated as active. Autumn | ||||
| 283 | doesn't really care if it's named "production" or "live" or | ||||
| 284 | "testing-on-jeffs-machine"; it's all the same to Autumn. | ||||
| 285 | |||||
| 286 | Your season's configuration is stored in the <tt>season.yml</tt> file within | ||||
| f185a31b » | Tim Morgan | 2008-07-04 | 287 | your season directory. Currently it supports one directive, @logging@. This sets | |
| 288 | the minimum log level (such as @debug@ or @warn@). If the log level is set to | ||||
| 289 | @debug@, it also enables console output parroting. (See the *Logging* section.) | ||||
| c6d61e59 » | Tim Morgan | 2008-07-03 | 290 | ||
| 291 | The power of seasons comes in custom configuration options. For instance, | ||||
| 292 | consider that you have a testing and production season. In your testing season, | ||||
| 293 | your <tt>season.yml</tt> file contains: | ||||
| 294 | |||||
| 295 | <code>dont_http: true</code> | ||||
| 296 | |||||
| 297 | and in production, it contains: | ||||
| 298 | |||||
| 299 | <code>dont_http: false</code> | ||||
| 300 | |||||
| 301 | Now, in your code, you might have a method like: | ||||
| 302 | |||||
| 303 | <pre><code> | ||||
| 304 | def scores_command(stem, sender, reply_to, msg) | ||||
| 305 | if options[:dont_http] then | ||||
| 306 | return "Some fake sports scores." | ||||
| 307 | else | ||||
| 308 | # go on the web and find real sports scores | ||||
| 309 | end | ||||
| 310 | end | ||||
| 311 | </code></pre> | ||||
| 312 | |||||
| 313 | h3. Standard Configuration Options | ||||
| 314 | |||||
| 315 | h4. Global | ||||
| 316 | |||||
| 317 | System-wide configuration is done in the <tt>config/global.yml</tt> file. It | ||||
| 318 | supports by default the following directives: | ||||
| 319 | |||||
| 320 | | @season@ | The season to launch in. | | ||||
| f185a31b » | Tim Morgan | 2008-07-04 | 321 | | @log_history@ | The number of historical logfiles to keep (default 10). | | |
| c6d61e59 » | Tim Morgan | 2008-07-03 | 322 | ||
| 323 | In addition, the following options are available (but cannot be set in the yml | ||||
| 324 | file): | ||||
| 325 | |||||
| 326 | | @root@ | The root directory of the Autumn installation. | | ||||
| d5069e22 » | Tim Morgan | 2008-07-03 | 327 | | @system_logger@ | The @Autumn::LogFacade@ instance that records system messages. | | |
| c6d61e59 » | Tim Morgan | 2008-07-03 | 328 | ||
| 329 | h4. Season | ||||
| 330 | |||||
| 331 | Season-specific configuration is done in the | ||||
| 332 | <tt>config/seasons/[season]/season.yml</tt> file. Currently it only supports one | ||||
| f185a31b » | Tim Morgan | 2008-07-04 | 333 | directive, @logging@, which takes log levels like @debug@ or @warn@. | |
| c6d61e59 » | Tim Morgan | 2008-07-03 | 334 | ||
| 335 | h4. Stem | ||||
| 336 | |||||
| 337 | Stem-specific configuration is done in the | ||||
| 338 | <tt>config/seasons/[season]/stems.yml</tt> file. It's important to note that | ||||
| 339 | stem and leaf configurations are completely independent of each other. (In other | ||||
| 340 | words, stem options do not override leaf options, nor vice versa.) Therefore, | ||||
| 341 | you generally won't add custom directives to the <tt>stems.yml</tt> file, | ||||
| 342 | because you generally won't be working with stems directly. The standard options | ||||
| 343 | are: | ||||
| 344 | |||||
| 345 | | @server@ | The address of the IRC server. | | ||||
| 346 | | @port@ | The IRC server port (default 6667). | | ||||
| 30d84bdc » | Tim Morgan | 2008-07-04 | 347 | | @local_ip@ | The IP address to connect on (for virtual hosting). | | |
| c6d61e59 » | Tim Morgan | 2008-07-03 | 348 | | @nick@ | The nick to request. | | |
| 349 | | @password@ | The nick's password, if it is registered. | | ||||
| 350 | | @channel@ | A channel to join. | | ||||
| 351 | | @channels@ | A list of channels to join. | | ||||
| 352 | | @leaf@ | The name of a leaf to run. | | ||||
| 353 | | @leaves@ | A list of leaves to run. (These are the names of leaf configurations in <tt>leaves.yml</tt>, not leaf subclasses.) | | ||||
| 354 | | @rejoin@ | If true, the stem will rejoin any channels it is kicked from. | | ||||
| 355 | | @server_password@ | The password for the IRC server, if necessary. | | ||||
| 356 | | @ssl@ | If true, the connection to the IRC server will be made over SSL. | | ||||
| 357 | | @server_type@ | The IRC server type. See <tt>resources/daemons</tt> for a list of valid server types. If you do not manually set this value, it will be guessed automatically. | | ||||
| 358 | | @case_sensitive_channel_names@ | If true, channel names will be compared with case sensitivity. | | ||||
| 359 | | @dont_ghost@ | If true, the stem will not try to GHOST a registered nick if it's taken. | | ||||
| 360 | | @ghost_without_password@ | If true, the stem will use the GHOST command without a password. Set this for servers that use some other form of nick authentication, such as hostname-based. | | ||||
| 361 | | @user@ | The username to send (optional). | | ||||
| 362 | | @name@ | The user's real name (optional). | | ||||
| 331c618e » | Tim Morgan | 2008-09-08 | 363 | | @throttle@ | If enabled, the stem will throttle large amounts of simultaneous messages. | | |
| 364 | | @throttle_rate@ | Sets the number of seconds that pass between consecutive PRIVMSG's when the leaf's output is throttled. | | ||||
| 365 | | @throttle_threshold@ | Sets the number of simultaneous messages that must be queued before the leaf begins throttling output. | | ||||
| 9d588fcb » | Tim Morgan | 2009-03-28 | 366 | | @nick_regex@ | The regular expression used to match nicknames in server messages. By default, it conforms to the RFC-1459 definition. | | |
| c6d61e59 » | Tim Morgan | 2008-07-03 | 367 | ||
| 368 | The @channel@ and @channels@ directives can also be used to specify a password | ||||
| 369 | for a password protected channel, like so: | ||||
| 370 | |||||
| 371 | <pre><code> | ||||
| 372 | channel: | ||||
| 373 | channelname: channelpassword | ||||
| 374 | </code></pre> | ||||
| 375 | |||||
| 376 | or | ||||
| 377 | |||||
| 378 | <pre><code> | ||||
| 379 | channels: | ||||
| 380 | - channel1: password1 | ||||
| 381 | - channel2: password2 | ||||
| 382 | </code></pre> | ||||
| 383 | |||||
| 384 | The @port@, @server_type@, and @channel@/@channels@ options are set in the | ||||
| 385 | config file but not available in the @options@ hash. They are accessed directly | ||||
| 386 | from attributes in the @Autumn::Stem@ instance, such as the @channels@ | ||||
| 387 | attribute. | ||||
| 388 | |||||
| 389 | h4. Leaf | ||||
| 390 | |||||
| 391 | Leaf-specific configuration is done in the | ||||
| f185a31b » | Tim Morgan | 2008-07-04 | 392 | <tt>config/seasons/[season]/leaves.yml</tt> file and the | |
| 393 | <tt>leaves/[leaf]/config.yml</tt> file, with the former taking precedence over | ||||
| 394 | the latter. As mentioned above, leaf and stem configurations are completely | ||||
| 395 | separate, so one does not override the other. The standard options are: | ||||
| c6d61e59 » | Tim Morgan | 2008-07-03 | 396 | ||
| bc495401 » | Tim Morgan | 2008-07-06 | 397 | | @class@ | The type of the leaf. It must be a subdirectory in the <tt>leaves</tt> directory. | | |
| c6d61e59 » | Tim Morgan | 2008-07-03 | 398 | | @command_prefix@ | The text that must precede each command. Defaults to "!". | | |
| 399 | | @respond_to_private_messages@ | If true, the leaf will parse commands in whispers, and respond over whispers to those commands. | | ||||
| 400 | | @database@ | A database connection to use (as defined in <tt>database.yml</tt>). By default Autumn will choose a connection named after your leaf. | | ||||
| d5069e22 » | Tim Morgan | 2008-07-03 | 401 | | @formatter@ | The name of a module in @Autumn::Formatting@ that will handle output formatting and colorization. This defaults to mIRC-style formatting. | | |
| c6d61e59 » | Tim Morgan | 2008-07-03 | 402 | ||
| 1555539b » | Tim Morgan | 2008-07-04 | 403 | In addition, the following options are available (but cannot be set in the yml | |
| 404 | file): | ||||
| 405 | |||||
| 406 | | @root@ | The root directory of the leaf installation. | | ||||
| 407 | |||||
| f185a31b » | Tim Morgan | 2008-07-04 | 408 | The <tt>leaves.yml</tt> file is optional. When not included, each leaf in the | |
| 409 | <tt>leaves</tt> directory will be automatically instantiated once. | ||||
| 410 | |||||
| c6d61e59 » | Tim Morgan | 2008-07-03 | 411 | h3. Custom Configuration Options | |
| 412 | |||||
| 413 | All configuration files support user-generated directives. You can set options | ||||
| 414 | at any level. Options at a more narrow level override those at a broader level. | ||||
| 415 | |||||
| 416 | Options are maintained and cataloged by the @Autumn::Speciator@ singleton. You | ||||
| f185a31b » | Tim Morgan | 2008-07-04 | 417 | could access the singleton directly, but most objects have an @options@ | |
| 418 | attribute providing simpler access to the Speciator. | ||||
| c6d61e59 » | Tim Morgan | 2008-07-03 | 419 | ||
| 420 | For example, to access options in a leaf, all you do is call, for example, | ||||
| 421 | @options[:my_custom_option]@. @my_custom_option@ can be set at the | ||||
| 422 | global, season, or leaf level. | ||||
| 423 | |||||
| 424 | h2. Leaves | ||||
| 425 | |||||
| 426 | The @Autumn::Leaf@ class has many tools to help you write your leaves. These | ||||
| 427 | include things like filters, helpers, loggers, and an easy to use IRC library. | ||||
| 428 | The @Autumn::Leaf@ and @Autumn::Stem@ class docs are the most thorough way of | ||||
| 429 | learning about each of these features, but I'll walk you through the basics | ||||
| 430 | here. | ||||
| 431 | |||||
| 432 | h3. The Many Methods of Leaf | ||||
| 433 | |||||
| 434 | By subclassing @Autumn::Leaf@, you gain access to a number of neat utilities. | ||||
| 435 | These generally come in three classes: IRC commands that have already been | ||||
| 436 | written for you, utility methods you can call, and invoked methods you can | ||||
| bc495401 » | Tim Morgan | 2008-07-06 | 437 | override. Utility methods do things like add filters. Invoked methods are called | |
| c6d61e59 » | Tim Morgan | 2008-07-03 | 438 | when certain events happen, like when your leaf starts up or when a private | |
| 439 | message is received. You override them in your leaf to customize how it responds | ||||
| 440 | to these events. | ||||
| 441 | |||||
| 442 | | *Invoked methods* | @will_start_up@, @did_start_up@, @did_receive_channel_message@, etc. | | ||||
| f185a31b » | Tim Morgan | 2008-07-04 | 443 | | *Utility methods* | @before_filter@, @database@, etc. | | |
| c6d61e59 » | Tim Morgan | 2008-07-03 | 444 | | *IRC commands* | @quit_command@, @reload_command@, @autumn_command@, etc. | | |
| 445 | |||||
| 446 | See the class docs for more information on these methods. | ||||
| 447 | |||||
| 448 | In addition, your leaf is designated as a listener for its @Autumn::Stem@ | ||||
| 449 | instances. In short, this means if you want even finer control over the IRC | ||||
| 450 | connection, you can implement listener methods. See the | ||||
| 451 | @Autumn::Stem#add_listener@ method for examples of such methods. | ||||
| 452 | |||||
| 453 | Finally, your leaf can implement methods that are broadcast by listener plugins. | ||||
| 454 | An example of such a plugin is the @Autumn::CTCP@ class, which is included in | ||||
| 455 | all stems by default. Visit its class docs to learn more about how to send and | ||||
| 456 | receive CTCP requests. | ||||
| 457 | |||||
| 458 | h3. Filters | ||||
| 459 | |||||
| 460 | Filters are methods that are run either before or after a command is executed. | ||||
| 461 | In the former case, they can also prevent the command from being run. This is | ||||
| 462 | useful for authentication, for instance: A filter could determine if someone is | ||||
| 463 | authorized to run a command, and prevent the command from being run if not. | ||||
| 464 | |||||
| 465 | Use filters to save yourself the effort of rewriting code that will run before | ||||
| 466 | or after a command is executed. Filter methods are named @[word]_filter@ and | ||||
| 467 | they are added to the filter chain using the @before_filter@ and @after_filter@ | ||||
| 468 | methods (like in Ruby on Rails). As an example, imagine you wanted your bot to | ||||
| 469 | say something after each command: | ||||
| 470 | |||||
| 471 | <pre><code> | ||||
| f185a31b » | Tim Morgan | 2008-07-04 | 472 | class Controller > Autumn::Leaf | |
| c6d61e59 » | Tim Morgan | 2008-07-03 | 473 | after_filter :outro | |
| 474 | |||||
| 475 | private | ||||
| 476 | |||||
| 477 | def outro_filter(stem, channel, sender, command, msg, opts) | ||||
| 478 | stem.message "This has been a production of OutroBot!", channel | ||||
| 479 | end | ||||
| 480 | end | ||||
| 481 | </code></pre> | ||||
| 482 | |||||
| 483 | The result of this is that after each command, the leaf will make a dramatic | ||||
| 484 | exit. (Why did I use @after_filter@ and not @before_filter@? Because as I said | ||||
| 485 | earlier, a @before_filter@ can stop the command from being executed; the only | ||||
| 486 | way we know for sure that the command was executed -- and therefore should be | ||||
| 487 | outroed -- is to use an @after_filter@.) | ||||
| 488 | |||||
| 489 | I made the @outro_filter@ method private because I felt it shouldn't be exposed | ||||
| 490 | to other classes; this is not a requirement of the filter framework, though. | ||||
| 491 | |||||
| 492 | Now let's say you wanted to prevent the command from being run in some cases. | ||||
| 74c31b40 » | Tim Morgan | 2008-07-15 | 493 | The most obvious application of this feature is authentication. Autumn already | |
| 494 | includes a robust authentication module, but for the sake of example, let's | ||||
| 495 | pretend you wanted to do your own authentication in your leaf. So, you write a | ||||
| d5069e22 » | Tim Morgan | 2008-07-03 | 496 | @before_filter@ to determine if the user is authenticated. @before_filter@ | |
| 497 | methods have return values; if they return false, the filter chain is halted and | ||||
| 498 | the command is suppressed. If you want to have your leaf display some sort of | ||||
| 499 | message (like "Nice try!"), you need to include that in your filter. | ||||
| c6d61e59 » | Tim Morgan | 2008-07-03 | 500 | ||
| 501 | As an example, here's a simple form of authentication that just checks a | ||||
| 502 | person's nick: | ||||
| 503 | |||||
| 504 | <pre><code> | ||||
| f185a31b » | Tim Morgan | 2008-07-04 | 505 | class Controller < Autumn::Leaf | |
| c6d61e59 » | Tim Morgan | 2008-07-03 | 506 | before_filter :authenticate, :only => :quit, :admin => 'Yournick' | |
| 507 | |||||
| 508 | def authenticate_filter(stem, channel, sender, command, msg, opts) | ||||
| 509 | sender == opts[:admin] | ||||
| 510 | end | ||||
| 511 | end | ||||
| 512 | </code></pre> | ||||
| 513 | |||||
| 514 | I'm introducing you to three new features with this sample: | ||||
| 515 | |||||
| 516 | * You can use the @:only@ option to limit your filter to certain commands. Note | ||||
| 517 | that you specify the _command_ name as a symbol, _not_ the method name (which | ||||
| 518 | would be @quit_command@ in this case). | ||||
| 519 | * You can pass your own options to @before_filter@ and @after_filter@; they are | ||||
| 520 | passed through to your method via the last parameter, @opts@. | ||||
| 521 | * The return value of a @before_filter@ is used to determine if the command | ||||
| 522 | should be run. So be careful that your method does not return @nil@ or @false@ | ||||
| 523 | unless you really mean for the command to be suppressed. | ||||
| 524 | |||||
| 525 | Both of these examples use the parameters sent to your filter method. They are, | ||||
| 526 | in order: | ||||
| 527 | |||||
| d5069e22 » | Tim Morgan | 2008-07-03 | 528 | # the @Autumn::Stem@ instance that received the command, | |
| 529 | # the name of the channel to which the command was sent (or @nil@ if it was a | ||||
| 530 | private message), | ||||
| 531 | # the sender hash, | ||||
| 532 | # the name of the command that was typed, as a symbol, | ||||
| 533 | # any additional parameters after the command (same as the @msg@ parameter in | ||||
| 534 | the <tt>[word]_command</tt> methods), | ||||
| 535 | # the custom options that were given to @before_filter@ or @after_filter@. | ||||
| c6d61e59 » | Tim Morgan | 2008-07-03 | 536 | ||
| 537 | There are two built-in options that you can specify for @before_filter@ and | ||||
| 538 | @after_filter@, and those are @only@ and @except@. They work just like in Rails: | ||||
| 539 | The @only@ option limits the filter to running only on the given command or list | ||||
| 540 | of commands, and the @except@ option prevents the filter from being run on the | ||||
| 541 | given command or list. All other options are passed to the filter for you to | ||||
| 542 | use. | ||||
| 543 | |||||
| 544 | Filters are run in the order they are added to the filter chain. Therefore, a | ||||
| 545 | superclass's filters will run before a subclass's filters, and filters added | ||||
| 546 | later in a class definition will be run after those added earlier. | ||||
| 547 | |||||
| 548 | If you subclass one of your leaves, it inherits your superclass's filters. The | ||||
| 549 | @Autumn::Leaf@ superclass does not have any filters by default, though by | ||||
| 550 | default new leaves come with a simple authentication filter that checks the | ||||
| 551 | user's privilege level. | ||||
| 552 | |||||
| 74c31b40 » | Tim Morgan | 2008-07-15 | 553 | h3. Authentication | |
| 554 | |||||
| 555 | You don't need to write a @before_filter@ as shown above, because Autumn already | ||||
| 556 | includes a robust authentication module. The @Autumn::Authentication@ module | ||||
| 557 | includes the @Base@ class and four different subclasses of it. Each of these | ||||
| 558 | subclasses handles a different type of authentication. You can choose the | ||||
| 559 | authentication strategy you want on a leaf-by-leaf basis or for a whole season. | ||||
| 560 | |||||
| 561 | To specify the kind of authentication you want, you must add an @authentication@ | ||||
| 562 | directive to your config. If you want to set it for an individual leaf, add it | ||||
| 563 | to the <tt>leaves.yml</tt> file. If you want all leaves to have the same | ||||
| 564 | authentication strategy, add it to the <tt>season.yml</tt> or | ||||
| 565 | <tt>global.yml</tt> file. | ||||
| 566 | |||||
| 567 | The @authentication@ directive should be a hash that, at a minimum, includes a | ||||
| 568 | key called @type@. This is the snake_cased name of subclass in | ||||
| 569 | @Autumn::Authentication@ that you wish to use. As an example, here is an entry | ||||
| 570 | for an Administrator bot in a <tt>leaves.yml</tt> file, with ops-based | ||||
| 571 | authentication. | ||||
| 572 | |||||
| 573 | <pre><code> | ||||
| 574 | Administrator: | ||||
| 575 | class: Administrator | ||||
| 576 | authentication: | ||||
| 577 | type: op | ||||
| 578 | </code></pre> | ||||
| 579 | |||||
| 580 | This will instantiate the @Autumn::Authentication::Op@ class for use with the | ||||
| 581 | Administrator bot. | ||||
| 582 | |||||
| 583 | Other authentication strategies may require additional information. For | ||||
| 584 | instance, if you want to used nick-based authentication, your | ||||
| 585 | <tt>leaves.yml</tt> file might look like: | ||||
| 586 | |||||
| 587 | <pre><code> | ||||
| 588 | Administrator: | ||||
| 589 | class: Administrator | ||||
| 590 | authentication: | ||||
| 591 | type: nick | ||||
| 592 | nick: MyNick | ||||
| 593 | </code></pre> | ||||
| 594 | |||||
| 595 | See the class docs for each subclass in @Autumn::Authentication@ for more info | ||||
| 596 | on how you should set up your configs. | ||||
| 597 | |||||
| c6d61e59 » | Tim Morgan | 2008-07-03 | 598 | h3. Persistent Stores | |
| 599 | |||||
| 600 | If you would like to use a persistent store for your leaf, you should install | ||||
| 601 | the DataMapper gem and a DataObjects gem for your database of choice (MySQL, | ||||
| 602 | PostgreSQL, or SQLite). DataMapper works almost identically to ActiveRecord, so | ||||
| 603 | if you have any Rails programming experience, you should be able to dive right | ||||
| 604 | in. | ||||
| 605 | |||||
| 606 | Once you've got DataMapper installed, you should create one or more database | ||||
| 607 | connections in your <tt>config/seasons/[season]/database.yml</tt> file. A sample | ||||
| 608 | database connection looks like: | ||||
| 609 | |||||
| 610 | <pre><code> | ||||
| 611 | connection_name: | ||||
| 612 | adapter: mysql | ||||
| 613 | host: localhost | ||||
| 614 | username: root | ||||
| 615 | password: pass | ||||
| 616 | database: database_name | ||||
| 617 | </code></pre> | ||||
| 618 | |||||
| 0d2d62f1 » | Tim Morgan | 2008-07-05 | 619 | or, in a smaller syntax: | |
| 620 | |||||
| 621 | <code>connection_name: mysql://root@pass:localhost/database_name</code> | ||||
| 622 | |||||
| c6d61e59 » | Tim Morgan | 2008-07-03 | 623 | If you are using the "sqlite3" adapter, the @database@ option is the path to the | |
| bc495401 » | Tim Morgan | 2008-07-06 | 624 | file where the data should be written (example: | |
| 625 | @leaves/fortune/data/my_database.db@). You can name your connection however you | ||||
| 626 | want, but you _should_ name it after either your leaf or your leaf subclass. | ||||
| 627 | |||||
| c6d61e59 » | Tim Morgan | 2008-07-03 | 628 | ||
| 0d2d62f1 » | Tim Morgan | 2008-07-05 | 629 | You should also create DataMapper model classes for each of your model objects. | |
| 630 | You can place them within your leaf's <tt>models</tt> directory. This works | ||||
| 631 | almost exactly the same as the <tt>app/models</tt> directory in Rails. | ||||
| c6d61e59 » | Tim Morgan | 2008-07-03 | 632 | ||
| 633 | Once your database, data models, and leaves have been configured, you can use | ||||
| 0d2d62f1 » | Tim Morgan | 2008-07-05 | 634 | the @rake db:migrate@ task to automatically populate your database. | |
| c6d61e59 » | Tim Morgan | 2008-07-03 | 635 | ||
| 636 | Now, unlike Rails, Autumn supports multiple database connections. Two leaves can | ||||
| 637 | use two different database connections, or share the same database connection. | ||||
| 638 | Because of this, it's important to understand how to manage your connections. | ||||
| 639 | Autumn tries to do this for you by guessing which connection belongs to which | ||||
| 640 | leaf, based on their names. | ||||
| 641 | |||||
| f185a31b » | Tim Morgan | 2008-07-04 | 642 | For example, imagine you have a leaf named "Fortune" and an instance of that | |
| 643 | leaf in <tt>leaves.yml</tt> named "MyFortune". If you name your database | ||||
| 644 | connection either "Fortune" or "MyFortune" (or "fortune" or "my_fortune"), it | ||||
| 645 | will automatically be associated with that leaf. What this means is that for the | ||||
| 646 | leaf's command methods (such as @about_command@) and invoked methods (such as | ||||
| 647 | @did_receive_private_message@), the database connection will already be set for | ||||
| 648 | you, and you can start using your DataMapper objects just like ActiveRecord | ||||
| 649 | objects. | ||||
| c6d61e59 » | Tim Morgan | 2008-07-03 | 650 | ||
| d5069e22 » | Tim Morgan | 2008-07-03 | 651 | If, on the other hand, you either *named your database connection differently | |
| 652 | from your leaf or subclass name* or you are *writing a method outside of the | ||||
| 653 | normal flow of leaf methods* (for instance, one that is directly called by a | ||||
| 654 | @Stem@, or a different listener), you will need to call the @database@ method | ||||
| 655 | and pass it a block containing your code. | ||||
| c6d61e59 » | Tim Morgan | 2008-07-03 | 656 | ||
| 657 | This is terribly confusing, so let me give you an example. Let's assume you've | ||||
| f185a31b » | Tim Morgan | 2008-07-04 | 658 | got a fortune bot running a leaf named "FortuneLeaf", so your | |
| 659 | <tt>leaves.yml</tt> configuration is: | ||||
| c6d61e59 » | Tim Morgan | 2008-07-03 | 660 | ||
| 661 | <pre><code> | ||||
| 662 | FortuneBot: | ||||
| 663 | class: FortuneLeaf | ||||
| 664 | </code></pre> | ||||
| 665 | |||||
| 666 | And you have a database connection for that leaf, named after the leaf's class: | ||||
| 667 | |||||
| 668 | <pre><code> | ||||
| 669 | fortune_leaf: | ||||
| 670 | adapter: sqlite3 | ||||
| 0d2d62f1 » | Tim Morgan | 2008-07-05 | 671 | database: leaves/fortune_leaf/data/development.db | |
| c6d61e59 » | Tim Morgan | 2008-07-03 | 672 | </code></pre> | |
| 673 | |||||
| 674 | Let's further assume you have a simple DataMapper object: | ||||
| 675 | |||||
| 676 | <pre><code> | ||||
| f185a31b » | Tim Morgan | 2008-07-04 | 677 | class Fortune | |
| 678 | include DataMapper::Resource | ||||
| 679 | property :id, Integer, :serial => true | ||||
| 680 | property :text, String | ||||
| c6d61e59 » | Tim Morgan | 2008-07-03 | 681 | end | |
| 682 | </code></pre> | ||||
| 683 | |||||
| 684 | Now, if we wanted to write a "!fortune" command, it would appear something like | ||||
| 685 | this: | ||||
| 686 | |||||
| 687 | <pre><code> | ||||
| 688 | def fortune_command(stem, sender, reply_to, msg) | ||||
| 689 | fortunes = Fortune.all | ||||
| 690 | fortunes[rand(fortunes.size)].text | ||||
| 691 | end | ||||
| 692 | </code></pre> | ||||
| 693 | |||||
| 694 | Autumn automatically knows to execute this DataMapper code in the correct | ||||
| 0d2d62f1 » | Tim Morgan | 2008-07-05 | 695 | database context. It knows this because your leaf's name is @FortuneLeaf@, and | |
| c6d61e59 » | Tim Morgan | 2008-07-03 | 696 | your database context is named the same. | |
| 697 | |||||
| 698 | But what if you wanted to use that connection for other leaves too, so you named | ||||
| 699 | it something like "local_database"? Now, Autumn won't be able to guess that you | ||||
| 700 | want to use that DB context, so you have to specify it manually: | ||||
| 701 | |||||
| 702 | <pre><code> | ||||
| 703 | def fortune_command(stem, sender, reply_to, msg) | ||||
| 704 | database(:local_database) do | ||||
| 705 | fortunes = Fortune.all | ||||
| 706 | return fortunes[rand(fortunes.size)].text | ||||
| 707 | end | ||||
| 708 | end | ||||
| 709 | </code></pre> | ||||
| 710 | |||||
| 711 | If that is too tedious, you can specify the database connection manually in the | ||||
| 712 | <tt>leaves.yml</tt> file: | ||||
| 713 | |||||
| 714 | <pre><code> | ||||
| 715 | FortuneBot: | ||||
| 716 | class: FortuneLeaf | ||||
| 717 | database: local_database | ||||
| 718 | </code></pre> | ||||
| 719 | |||||
| 720 | OK, now onto the second special case. Imagine you want your fortune bot to also | ||||
| 721 | send a fortune in response to a CTCP VERSION request. So, you'd implement a | ||||
| 722 | method like so: | ||||
| 723 | |||||
| 724 | <pre><code> | ||||
| 725 | def ctcp_version_request(handler, stem, sender, arguments) | ||||
| 726 | fortune = random_fortune # Loads a random fortune | ||||
| 727 | send_ctcp_reply stem, sender[:nick], 'VERSION', fortune.text | ||||
| 728 | end | ||||
| 729 | </code></pre> | ||||
| 730 | |||||
| 731 | This will break -- why? Because the @ctcp_version_request@ method is in the | ||||
| 732 | realm of the @Autumn::CTCP@ class, _not_ the @Autumn::Leaf@ class. (You can see | ||||
| 733 | this by investigating the CTCP class docs; it shows you what methods you can | ||||
| 0d2d62f1 » | Tim Morgan | 2008-07-05 | 734 | implement for CTCP support.) Basically, the @CTCP@ class calls your method | |
| c6d61e59 » | Tim Morgan | 2008-07-03 | 735 | directly, giving the @Autumn::Leaf@ class no chance to set up the database | |
| 736 | first. So to fix it, make a call to @database@ first: | ||||
| 737 | |||||
| 738 | <pre><code> | ||||
| 739 | def ctcp_version_request(handler, stem, sender, arguments) | ||||
| f185a31b » | Tim Morgan | 2008-07-04 | 740 | fortune = database { random_fortune } | |
| 741 | send_ctcp_reply stem, sender[:nick], 'VERSION', fortune.text | ||||
| c6d61e59 » | Tim Morgan | 2008-07-03 | 742 | end | |
| 743 | </code></pre> | ||||
| 744 | |||||
| 745 | This will execute those methods in the scope of the database connection guessed | ||||
| 746 | by @Autumn::Leaf@. Of course, you can manually pass in a connection name if | ||||
| 747 | necessary. | ||||
| 748 | |||||
| f215f4f1 » | Tim Morgan | 2008-07-08 | 749 | *Another important note:* You will need to make a call to @database@ in any | |
| 750 | child threads your leaf creates. The database context is not automatically | ||||
| 751 | carried over to such threads. | ||||
| 752 | |||||
| f185a31b » | Tim Morgan | 2008-07-04 | 753 | h3. Your Leaf's Module; or, "What Do I Do About Namespace Conflicts?" | |
| 754 | |||||
| 755 | So, if you have two database-backed leaves, it's entirely likely that both of | ||||
| 756 | them will use some sort of DataMapper resource named @Channel@, or something | ||||
| 757 | similar. You can't define the class @Channel@ twice in two different ways, so | ||||
| 758 | how do you deal with this? | ||||
| 759 | |||||
| 0d2d62f1 » | Tim Morgan | 2008-07-05 | 760 | The answer is: It's already dealt with for you. Go ahead and define the class | |
| f185a31b » | Tim Morgan | 2008-07-04 | 761 | twice. Or three times. | |
| 762 | |||||
| 763 | The longer explanation is: Secretly, behind the scenes, *all your leaf code is | ||||
| 764 | being cleverly loaded into a module named after your leaf*. So, when, in your | ||||
| 765 | <tt>controller.rb</tt> code, it says @class Controller < Autumn::Leaf@, you | ||||
| 766 | should read it as @class MyLeafName::Controller < Autumn::Leaf@. When you define | ||||
| 0d2d62f1 » | Tim Morgan | 2008-07-05 | 767 | your model with @class Channel@, it's really read as <code>class | |
| 768 | MyLeafName::Channel</code>. | ||||
| f185a31b » | Tim Morgan | 2008-07-04 | 769 | ||
| 770 | Don't worry about table names or associations or anything, either. Just go ahead | ||||
| 771 | and use it as if it weren't in a module. The <tt>libs/datamapper_hacks.rb</tt> | ||||
| 772 | file has all the necessary code changes to make this bit of trickery work. | ||||
| 773 | |||||
| c6d61e59 » | Tim Morgan | 2008-07-03 | 774 | h3. Using Support Modules | |
| 775 | |||||
| f185a31b » | Tim Morgan | 2008-07-04 | 776 | Helper modules placed in your leaf's <tt>helpers</tt> directory will | |
| 777 | automatically be loaded and included in your leaf controller and views. To | ||||
| 778 | create a helper module, place Ruby files to be loaded into the <tt>helpers</tt> | ||||
| 779 | directory. Make sure your helper modules' names end with the word "Helper". | ||||
| c6d61e59 » | Tim Morgan | 2008-07-03 | 780 | ||
| f185a31b » | Tim Morgan | 2008-07-04 | 781 | For instance, if your leaf's name is "Fortune", and you needed two helpers, a | |
| c6d61e59 » | Tim Morgan | 2008-07-03 | 782 | database helper and a network helper, you could create two modules named | |
| f185a31b » | Tim Morgan | 2008-07-04 | 783 | @DatabaseHelper@ and @NetworkHelper@. Any modules named in this fashion and | |
| 784 | placed in the <tt>helpers</tt> subdirectory will be loaded and appended to the | ||||
| 0d2d62f1 » | Tim Morgan | 2008-07-05 | 785 | controller and its views automatically. | |
| c6d61e59 » | Tim Morgan | 2008-07-03 | 786 | ||
| 26f6cd28 » | Tim Morgan | 2009-05-29 | 787 | h3. Loading libraries | |
| 788 | |||||
| 789 | Files placed in your leaf's <tt>lib</tt> directory will be loaded and run before | ||||
| 790 | your leaf's controller, helpers, and views are parsed. You can place any gem | ||||
| 791 | dependencies in this file, or preload any class definitions. | ||||
| 792 | |||||
| c6d61e59 » | Tim Morgan | 2008-07-03 | 793 | h3. Debugging Your Leaf | |
| 794 | |||||
| 795 | If you make a simple code change to your leaf, you can reload it without having | ||||
| 796 | to restart the whole process. See the @Autumn::Leaf#reload_command@ | ||||
| 797 | documentation for more information on when and how you can reload your leaf's | ||||
| 798 | code. | ||||
| 799 | |||||
| f185a31b » | Tim Morgan | 2008-07-04 | 800 | If an error occurs on a live production instance, it will be logged to the log | |
| 801 | file for your season. You can inspect the log file to determine what went wrong. | ||||
| c6d61e59 » | Tim Morgan | 2008-07-03 | 802 | ||
| 803 | If the error happens before the logger is available, oftentimes it will appear | ||||
| bc495401 » | Tim Morgan | 2008-07-06 | 804 | in the <tt>autumn.output</tt> or <tt>autumn.log</tt> files. These files are | |
| 805 | generated by the daemon library and note any uncaught exceptions or standard | ||||
| 806 | outs. They are in the <tt>tmp</tt> directory. | ||||
| c6d61e59 » | Tim Morgan | 2008-07-03 | 807 | ||
| 808 | The most tricky of errors can happen before the process is daemonized. If your | ||||
| 809 | process is quitting prematurely, and you don't see anything in either log file, | ||||
| 810 | consider running @script/server@, allowing you to see any exceptions for | ||||
| 811 | yourself. | ||||
| 812 | |||||
| 813 | Unfortunately, it's still possible that the bug might not appear when you do | ||||
| 814 | this, but only appear when the process is daemonized. In this situation, I'd | ||||
| 815 | recommend installing rdebug (@sudo gem install rdebug@) and stepping through the | ||||
| 816 | code to figure out what's going wrong. In particular, make sure you step into | ||||
| f185a31b » | Tim Morgan | 2008-07-04 | 817 | the @Foliater@'s @start_stems@ method, when it creates the new threads. It's | |
| c6d61e59 » | Tim Morgan | 2008-07-03 | 818 | possible your exception will rear its head once you step into that line of code. | |
| 819 | |||||
| 820 | h2. Stems | ||||
| 821 | |||||
| 822 | @Autumn::Stem@ is a full-featured IRC client library, written from the ground up | ||||
| 823 | for Autumn. It makes extensive use of implicit protocols, meaning that most | ||||
| 824 | features are accessed by implementing the methods you feel are necessary. | ||||
| 825 | |||||
| 826 | Most of the time, you will only work with stems indirectly via leaves. For | ||||
| 827 | instance, if you want an "!opped" command that returns true if the sender is an | ||||
| 828 | operator, it would look like this: | ||||
| 829 | |||||
| 830 | <pre><code> | ||||
| 831 | def opped_command(stem, sender, reply_to, msg) | ||||
| 832 | stem.channel_members[reply_to][sender[:nick]] == :operator ? "You are opped." : "You are not opped." | ||||
| 833 | end | ||||
| 834 | </code></pre> | ||||
| 835 | |||||
| 836 | Let's break this down. In order to figure out if someone is opped or not, we | ||||
| 837 | need three pieces of information: their nick, the channel they are in, and the | ||||
| 838 | IRC server they are connected to. | ||||
| 839 | |||||
| 840 | The @stem@ parameter contains the @Autumn::Stem@ instance that received this | ||||
| 841 | message. It is our link to that server. Through it we can perform IRC actions | ||||
| 842 | and make requests. | ||||
| 843 | |||||
| 844 | @Autumn::Stem@ includes an attribute @channel_members@, a hash of channels | ||||
| 845 | mapped to their members. The channel that received the message is passed via the | ||||
| bc495401 » | Tim Morgan | 2008-07-06 | 846 | @reply_to@ parameter. So we call @channel_members[reply_to]@ and we receive a | |
| c6d61e59 » | Tim Morgan | 2008-07-03 | 847 | hash of member names to their privilege levels. The @sender@ parameter contains | |
| 848 | information about the person who sent the command, including their nick. So we | ||||
| 849 | use their nick to resolve their privilege level. | ||||
| 850 | |||||
| 851 | Complicated? Sure it is. That's the price we pay for separating stems from | ||||
| 852 | leaves. But what if you, like probably 90% of the people out there who use | ||||
| 853 | Autumn, only have one stem? Why should you have to call the same damn stem each | ||||
| 854 | and every time? | ||||
| 855 | |||||
| 856 | Fortunately, your pleas are not in vain. For leaves that run off only one stem, | ||||
| 857 | the stem's methods are rolled right into the leaf. So, that "!opped" command | ||||
| 858 | method becomes: | ||||
| 859 | |||||
| 860 | <pre><code> | ||||
| 861 | def opped_command(stem, sender, reply_to, msg) | ||||
| 862 | channel_members[reply_to][sender[:nick]] == :operator ? "You are opped." : "You are not opped." | ||||
| 863 | end | ||||
| 864 | </code></pre> | ||||
| 865 | |||||
| 866 | OK, so it's not like a world-class improvement, but it helps. | ||||
| 867 | |||||
| 868 | The primary thing your leaf will probably do with a @Stem@ instance is use it to | ||||
| 869 | send messages, like so: | ||||
| 870 | |||||
| 871 | <pre><code> | ||||
| 872 | def about_command(stem, sender, reply_to, msg) | ||||
| 873 | stem.message "I am a pretty awesome bot!", reply_to | ||||
| 874 | end | ||||
| 875 | </code></pre> | ||||
| 876 | |||||
| 877 | Fortunately, if you just return a string, @Autumn::Leaf@ will automatically send | ||||
| 878 | it for you, simplifying our method: | ||||
| 879 | |||||
| 880 | <pre><code> | ||||
| 881 | def about_command(stem, sender, reply_to, msg) | ||||
| 882 | "I am a pretty awesome bot!" | ||||
| 883 | end | ||||
| 884 | </code></pre> | ||||
| 885 | |||||
| 886 | You would still interact with the stem directly if you wanted to do something | ||||
| 887 | like announce your leaf's presence to everyone. To do this, you'd have to send | ||||
| 888 | a message to every channel of every stem the leaf is a listener for: | ||||
| 889 | |||||
| 890 | <code>stems.each { |stem| stem.channels.each { |channel| stem.message "Hello!", channel } }</code> | ||||
| 891 | |||||
| 892 | But! @Autumn::Stem#message@ will automatically send a message to every channel | ||||
| 893 | if you don't specify any channels, simplifying our code to: | ||||
| 894 | |||||
| 895 | <code>stems.each { |stem| stem.message "Hello!" }</code> | ||||
| 896 | |||||
| d5069e22 » | Tim Morgan | 2008-07-03 | 897 | It gets even better. *You can call methods on the @stems@ array as if it were a | |
| 898 | stem itself!* This simplifies the line significantly: | ||||
| c6d61e59 » | Tim Morgan | 2008-07-03 | 899 | ||
| 900 | <code>stems.message "Hello!"</code> | ||||
| 901 | |||||
| 902 | Pretty nifty, huh? This also works for functions as well as methods; for | ||||
| 903 | instance, the @Autumn::Stem#ready?@ function, which returns true if a stem is | ||||
| 904 | ready: | ||||
| 905 | |||||
| 906 | <code>stems.ready? #=> [ true, true, false, true ]</code> (for example) | ||||
| 907 | |||||
| 908 | h3. The nitty-gritty of stems | ||||
| 909 | |||||
| 910 | The section above dealt with stems as they relate to leaves. But when would you | ||||
| 911 | need to deal with a stem directly? Generally, never. However, if you find that | ||||
| 912 | @Autumn::Leaf@ doesn't have what you need, you may have to turn to | ||||
| 913 | @Autumn::Stem@ to get the functionality you are looking for. So let's take a | ||||
| 914 | look at how Stem works. | ||||
| 915 | |||||
| f185a31b » | Tim Morgan | 2008-07-04 | 916 | A stem interacts with interested parties via the listener protocol. Your leaf | |
| c6d61e59 » | Tim Morgan | 2008-07-03 | 917 | signals its interest to a stem by calling @Autumn::Stem#add_listener@. When a | |
| 918 | leaf or any other object becomes a stem's listener, that stem then invokes | ||||
| 919 | methods on the listener whenever an IRC event occurs. | ||||
| 920 | |||||
| 921 | Let's take a simple example. Assume you wanted to build a basic textual IRC | ||||
| 922 | client using Stem. You'd first want to indicate that your client is a listener: | ||||
| 923 | |||||
| 924 | <pre><code> | ||||
| 925 | class MyClient | ||||
| 926 | def initialize(stem) | ||||
| 927 | @stem = stem | ||||
| 928 | @stem.add_listener self | ||||
| 929 | end | ||||
| 930 | end | ||||
| 931 | </code></pre> | ||||
| 932 | |||||
| 933 | Now the stem will send method calls to your @MyClient@ instance every time an | ||||
| 934 | IRC event occurs. None of these methods are required -- you can implement as few | ||||
| 935 | or as many as you want. The different methods that @Stem@ will send are | ||||
| 936 | documented in the @Autumn::Stem#add_listener@ method docs. One very important | ||||
| 937 | method is the @irc_privmsg_event@ method. Let's implement it: | ||||
| 938 | |||||
| 939 | <pre><code> | ||||
| 940 | def irc_privmsg_event(stem, sender, arguments) | ||||
| 941 | puts "#{arguments[:channel]} <#{sender[:nick]}> #{arguments[:message]}" | ||||
| 942 | end | ||||
| 943 | </code></pre> | ||||
| 944 | |||||
| 945 | Now we've got the most important part of our IRC client done -- receiving | ||||
| 946 | messages. | ||||
| 947 | |||||
| 948 | You can also send IRC events using stem. It's simple: Every IRC command (such as | ||||
| 949 | JOIN and PRIVMSG and MODE) has a corresponding method in @Stem@ (such as @join@ | ||||
| 950 | and @privmsg@ and @mode@). These methods aren't in the API docs because they're | ||||
| 951 | implemented using @method_missing@. Their arguments are exactly the same as the | ||||
| 952 | arguments the IRC command expects, and in the same order. | ||||
| 953 | |||||
| 954 | So how do we send a message? Well according to RFC-1459, the basic IRC spec, the | ||||
| 955 | PRIVMSG command takes two arguments: a list of receivers, and the text to be | ||||
| 956 | sent. So, we know our method call should look something like this: | ||||
| 957 | |||||
| 958 | @stem.privmsg recipient, message | ||||
| 959 | |||||
| 960 | Astute readers will note that the spec shows a _list_ of recipients, and indeed, | ||||
| 961 | you can call the method like so: | ||||
| 962 | |||||
| 963 | @stem.privmsg [ recipient1, recipient2 ], message | ||||
| 964 | |||||
| 965 | That's the basics of how @Autumn::Stem@ works, but there's one other thing worth | ||||
| 966 | mentioning, and that's listener plugins. The details are in the | ||||
| 967 | @Autumn::Stem#add_listener@ method docs, but the short of it is that these are | ||||
| 968 | special listeners that bestow their powers onto other listeners. | ||||
| 969 | |||||
| 970 | The best example of this is the @Autumn::CTCP@ class. This class is indeed a | ||||
| 971 | @Stem@ listener: It listens to PRIVMSG events from the stem, and checks them to | ||||
| 972 | see if they are CTCP requests. However, it _also_ gives you, the author of | ||||
| 973 | another listener (such as your leaf) the ability to implement methods according | ||||
| 974 | to _its_ protocol. | ||||
| 975 | |||||
| 976 | For example, say you wanted to respond to CTCP VERSION requests with your own | ||||
| 977 | version information. You do it like so: | ||||
| 978 | |||||
| 979 | <pre><code> | ||||
| 980 | def ctcp_version_request(handler, stem, sender, arguments) | ||||
| 981 | send_ctcp_reply stem, sender[:nick], 'VERSION', "AwesomeBot 2.0 by Sancho Sample" | ||||
| 982 | end | ||||
| 983 | </code></pre> | ||||
| 984 | |||||
| 985 | What's going on here? Because the @Autumn::CTCP@ class is a listener plugin, it | ||||
| 986 | is sending its own method calls as well as implementing @Stem@'s method calls. | ||||
| 987 | One such call is the @ctcp_version_request@ method, which you can see in the | ||||
| 988 | @CTCP@ class docs. Somewhere deep in the annals of @Autumn::Foliater@, there is | ||||
| 989 | some code similar to the following: | ||||
| 990 | |||||
| 991 | <pre><code> | ||||
| 992 | ctcp = Autumn::CTCP.new | ||||
| 993 | stem.add_listener ctcp | ||||
| 994 | </code></pre> | ||||
| 995 | |||||
| 996 | Thus, every stem comes pre-fab with a CTCP listener plugin. That plugin is | ||||
| 997 | intercepting PRIVMSG events and checking if they're CTCP requests. If they are, | ||||
| 998 | it is invoking methods, such as @ctcp_version_request@, in all of the stem's | ||||
| 999 | other listeners, among which is your leaf. Hopefully you understand how this all | ||||
| 1000 | fits together. | ||||
| 1001 | |||||
| 1002 | The lesson to take home here is two-fold: Firstly, if you'd like CTCP support in | ||||
| 1003 | your leaf, know that it's the @Autumn::CTCP@ class that is providing the method | ||||
| 1004 | calls to your leaf, not the @Autumn::Stem@ class. Secondly, this should | ||||
| 1005 | hopefully give you some ideas should you want to write your own listener plugin | ||||
| 1006 | to enhance @Stem@'s functionality. | ||||
| 1007 | |||||
| 1008 | h2. Autumn's Logging | ||||
| 1009 | |||||
| 1010 | Autumn uses Ruby's @Logger@ class to log; however, it uses @Autumn::LogFacade@ | ||||
| 1011 | to prepend additional information to each log entry. The @LogFacade@ class has | ||||
| 1012 | the exact same external API as @Logger@, so you can use it like a typical Ruby | ||||
| 1013 | or Ruby on Rails logger. Many objects (such as @Leaf@ and @Stem@) include a | ||||
| 1014 | @logger@ attribute: | ||||
| 1015 | |||||
| 1016 | <pre><code> | ||||
| 1017 | logger.debug "Debug statement" | ||||
| 1018 | logger.fatal $! | ||||
| 1019 | </code></pre> | ||||
| 1020 | |||||
| 1021 | See the @LogFacade@ class docs for details. | ||||
| 1022 | |||||
| 1023 | h2. Tasks | ||||
| 1024 | |||||
| 1025 | The included Rakefile contains a number of useful tasks to help you develop and | ||||
| 1026 | deploy your leaves. You can always get a list of tasks by typing @rake --tasks@. | ||||
| 1027 | The various commands you can run are: | ||||
| 1028 | |||||
| 1029 | Application tasks: | ||||
| 1030 | |||||
| 1031 | | @rake app:start@ | Starts the Autumn daemon in the background. | | ||||
| 1032 | | @rake app:stop@ | Stops the Autumn daemon. | | ||||
| 1033 | | @rake app:restart@ | Reloads the Autumn daemons. | | ||||
| 1034 | | @rake app:run@ | Starts the Autumn daemon in the foreground. | | ||||
| 1035 | | @rake app:zap@ | Forces the daemon to a stopped state. Use this command if your daemon is not running but <tt>script/daemon</tt> thinks it still is. | | ||||
| 1036 | |||||
| 1037 | Database tasks: | ||||
| 1038 | |||||
| 0d2d62f1 » | Tim Morgan | 2008-07-05 | 1039 | | @LEAF=[leaf name] rake db:migrate@ | Creates all the tables for a leaf, as specified by the leaf's model objects | | |
| c6d61e59 » | Tim Morgan | 2008-07-03 | 1040 | ||
| 1041 | Documentation tasks: | ||||
| 1042 | |||||
| 1043 | | @rake doc:api@ | Generates HTML documentation for Autumn, found in the <tt>doc/api</tt> directory. | | ||||
| 1044 | | @rake doc:leaves@ | Generates HTML documentation for your leaves, found in the <tt>doc/leaves</tt> directory. | | ||||
| 1045 | | @rake doc:clear@ | Removes all HTML documentation. | | ||||
| 1046 | |||||
| 1047 | Logging tasks: | ||||
| 1048 | |||||
| 1049 | | @rake log:clear@ | Clears the log files for all seasons. | | ||||
| 1050 | | @rake log:errors@ | Prints a list of error-level log messages for the current season, and uncaught exceptions in all seasons. | | ||||
| 1051 | |||||
| f185a31b » | Tim Morgan | 2008-07-04 | 1052 | h3. Custom leaf tasks | |
| 1053 | |||||
| 1054 | You can define your own leaf-specific tasks in the <tt>tasks</tt> subdirectory | ||||
| 1055 | within your leaf's directory. Any <tt>.rake</tt> files there will be loaded by | ||||
| 1056 | rake. The tasks will be added within a task-group named after your leaf. Use | ||||
| 1057 | Scorekeeper as an example: If you type @rake --tasks@, you'll see one other | ||||
| 1058 | task, @rake scorekeeper:scores@. The "scores" task is defined in the | ||||
| 1059 | <tt>leaves/scorekeeper/tasks/stats.rake</tt> file, and placed in the | ||||
| 1060 | "scorekeeper" task group by Autumn. | ||||
| 1061 | |||||
| 1062 | Also, if you open that file up, you'll notice that you have to refer to your | ||||
| 1063 | leaf's classes by their _full_ names, including the leaf module. (See *Your | ||||
| 1064 | Leaf's Module* if you're confused.) | ||||
| 1065 | |||||
| c6d61e59 » | Tim Morgan | 2008-07-03 | 1066 | h2. Scripts | |
| 1067 | |||||
| 0d204307 » | Tim Morgan | 2008-12-11 | 1068 | Autumn includes some scripts to help you control it. | |
| 1069 | |||||
| 1070 | h3. <tt>script/console</tt> | ||||
| 1071 | |||||
| 1072 | Bootstraps an IRb console with the Autumn environment configured. Stems and | ||||
| 1073 | leaves are accessile from the Foliater instance. DataMapper models can be used. | ||||
| 1074 | Does not start any stems (in other words, no actual server login occurs). | ||||
| 1075 | |||||
| 1076 | Usage: @script/console [options]@ | ||||
| 1077 | |||||
| 1078 | where [options] may contain: | ||||
| 1079 | |||||
| 1080 | | @--irb@ | Invoke a different Ruby terminal. | | ||||
| 1081 | |||||
| 1082 | You can alter the season by setting the @SEASON@ environment variable. | ||||
| c6d61e59 » | Tim Morgan | 2008-07-03 | 1083 | ||
| 1084 | h3. <tt>script/daemon</tt> | ||||
| 1085 | |||||
| 1086 | Controller for the Autumn daemon. Starts, stops, and manages the daemon. Must be | ||||
| 1087 | run from the Autumn root directory. | ||||
| 1088 | |||||
| 1089 | Usage: @script/daemon [command] [options] -- [application options]@ | ||||
| 1090 | |||||
| 1091 | where [command] is one of: | ||||
| 1092 | |||||
| 1093 | | @start@ | start an instance of the application | | ||||
| 1094 | | @stop@ | stop all instances of the application | | ||||
| 1095 | | @restart@ | stop all instances and restart them afterwards | | ||||
| 1096 | | @run@ | start the application and stay on top | | ||||
| 1097 | | @zap@ | set the application to a stopped state | | ||||
| 1098 | |||||
| 1099 | and where [options] may contain several of the following: | ||||
| 1100 | |||||
| 1101 | | @-t, --ontop@ | Stay on top (does not daemonize) | | ||||
| 1102 | | @-f, --force@ | Force operation | | ||||
| 1103 | |||||
| 1104 | Common options: | ||||
| 1105 | |||||
| 1106 | | @-h, --help@ | Show this message | | ||||
| 1107 | | @--version@ | Show version | | ||||
| 1108 | |||||
| 1109 | h3. <tt>script/destroy</tt> | ||||
| 1110 | |||||
| 1111 | Destroys the files for leaves, seasons, and other objects of the Autumn | ||||
| 1112 | framework. | ||||
| 1113 | |||||
| 1114 | Usage: @script/destroy [options] [object] [name]@ | ||||
| 1115 | |||||
| 1116 | | [object] | The object type to destroy. Valid types are "leaf" and "season". | | ||||
| 1117 | | [name] | The name of the object to destroy. For example, you can call @script/destroy leaf Scorekeeper@ to remove a leaf named Scorekeeper. | | ||||
| 1118 | |||||
| 1119 | | @--help, -h@ | Displays this usage information. | | ||||
| f185a31b » | Tim Morgan | 2008-07-04 | 1120 | | @--vcs, -c@ | Remove any created files or directories from the project's version control system. (Autodetects CVS, Git, and Subversion.) | | |
| c6d61e59 » | Tim Morgan | 2008-07-03 | 1121 | ||
| 1122 | h3. <tt>script/generate</tt> | ||||
| 1123 | |||||
| 1124 | Generates template files for leaves, seasons, and other Autumn objects. | ||||
| 1125 | |||||
| 1126 | Usage: @script/generate [options] [template] [name]@ | ||||
| 1127 | |||||
| 1128 | | [template] | The template to create. Valid templates are "leaf" and "season". | | ||||
| 1129 | | [name] | The name to give the created template. For example, you can call @script/generate leaf Scorekeeper@ to create a leaf named Scorekeeper. | | ||||
| 1130 | |||||
| 1131 | | @--help, -h@ | Displays this usage information. | | ||||
| f185a31b » | Tim Morgan | 2008-07-04 | 1132 | | @--vcs, -c@ | Add any created files or directories to the project's version control system. (Autodetects CVS, Git, and Subversion.) | | |
| c6d61e59 » | Tim Morgan | 2008-07-03 | 1133 | ||
| 1134 | h3. <tt>script/server</tt> | ||||
| 1135 | |||||
| 1136 | Runs Autumn from the command line. This script will not exit until all leaves | ||||
| 1137 | have exited. You can set the @SEASON@ environment variable to override the | ||||
| 1138 | season. | ||||
| 1139 | |||||
| 1140 | h2. Thread Safety | ||||
| 1141 | |||||
| 1142 | Autumn is a multi-threaded IRC client. When a message is received, a new thread | ||||
| 1143 | is spawned to process the message. In this thread, the message will be parsed, | ||||
| 1144 | and all listener hooks will be invoked, including your leaf's methods. The | ||||
| 1145 | thread will terminate once the message has been fully processed and all methods | ||||
| 1146 | invoked. | ||||
| 1147 | |||||
| 1148 | I have made every effort to ensure that @Autumn::Stem@ and @Autumn::Leaf@ are | ||||
| 1149 | thread-safe, as well as other relevant support classes such as @Autumn::CTCP@. | ||||
| 1150 | It is now in your hands to ensure your leaves are thread-safe! This basically | ||||
| 1151 | means recognizing that, while your leaf is churning away at whatever command it | ||||
| 1152 | received, things can and will change in the background. If your command requires | ||||
| 1153 | your leaf to have operator privileges, write your code under the assumption that | ||||
| 1154 | operator could be taken from your leaf in the middle of executing the command. | ||||
| 1155 | Write data in critical blocks, use transactions in your database calls ... you | ||||
| 1156 | know the deal. Don't assume things will be the same between one line of code and | ||||
| 1157 | the next. | ||||
| 1158 | |||||
| 1159 | h2. Getting Ready for Deployment | ||||
| 1160 | |||||
| 1161 | There's only a few things you need to do once your leaf is ready to greet | ||||
| 1162 | the Real World: | ||||
| 1163 | |||||
| d5069e22 » | Tim Morgan | 2008-07-03 | 1164 | # Create a new production season. Configure your stems, leaves, and database | |
| 1165 | as necessary for your production environment. | ||||
| 1166 | # In <tt>config/global.yml</tt>, set the season to your production season. | ||||
| 1167 | # If desired, in <tt>script/daemon</tt>, set the @:monitor@ option to true. | ||||
| 1168 | This will spawn a monitor process that will relaunch Autumn if it crashes. | ||||
| c6d61e59 » | Tim Morgan | 2008-07-03 | 1169 | ||
| 1170 | h2. Other Information | ||||
| 1171 | |||||
| 1172 | Please consult the "list of known bugs":http://github.com/RISCfuture/autumn/wikis/known-bugs | ||||
| 1173 | and "version history":http://github.com/RISCfuture/autumn/wikis/version-history | ||||
| 1174 | for more information. | ||||
| 1175 | |||||
| 1176 | *_Why do you require Facets?_, I hear you ask. Facets doesn't add any super | ||||
| 1177 | awesome new features to Ruby like Daemons or DataMapper does. It does, however, | ||||
| 1178 | improve code reuse, and I'm a big fan of that. Why should a million different | ||||
| 1179 | Ruby projects all write the same @Symbol#to_proc@ method or the same | ||||
| 1180 | @Hash#symbolize_keys@ method? I use Facets because that job has already been | ||||
| 1181 | done, and staying DRY means staying DRY _between_ codebases, not just within | ||||
| 1182 | them. | ||||
