troystribling-agent_xmpp 0.0.0 → 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. data/README.rdoc +397 -2
  2. data/Rakefile +33 -21
  3. data/VERSION +1 -1
  4. data/agent_xmpp.gemspec +78 -33
  5. data/lib/agent_xmpp.rb +11 -7
  6. data/lib/agent_xmpp/app.rb +0 -5
  7. data/lib/agent_xmpp/app/boot.rb +47 -33
  8. data/lib/agent_xmpp/app/controller.rb +135 -19
  9. data/lib/agent_xmpp/client.rb +6 -1
  10. data/lib/agent_xmpp/client/client.rb +22 -155
  11. data/lib/agent_xmpp/client/connection.rb +41 -250
  12. data/lib/agent_xmpp/client/message_delegate.rb +706 -0
  13. data/lib/agent_xmpp/client/message_pipe.rb +176 -0
  14. data/lib/agent_xmpp/client/publish_model.rb +65 -0
  15. data/lib/agent_xmpp/{app/format.rb → client/response.rb} +19 -24
  16. data/lib/agent_xmpp/client/roster_model.rb +139 -0
  17. data/lib/agent_xmpp/client/services_model.rb +97 -0
  18. data/lib/agent_xmpp/config.rb +45 -0
  19. data/lib/agent_xmpp/main.rb +126 -0
  20. data/lib/agent_xmpp/patches.rb +6 -2
  21. data/lib/agent_xmpp/patches/{standard_library_patches/array.rb → array.rb} +14 -7
  22. data/lib/agent_xmpp/patches/{standard_library_patches/float.rb → float.rb} +0 -0
  23. data/lib/agent_xmpp/patches/{standard_library_patches/hash.rb → hash.rb} +3 -3
  24. data/lib/agent_xmpp/patches/{standard_library_patches/object.rb → object.rb} +3 -3
  25. data/lib/agent_xmpp/patches/rexml.rb +69 -0
  26. data/lib/agent_xmpp/patches/{standard_library_patches/string.rb → string.rb} +5 -0
  27. data/lib/agent_xmpp/xmpp.rb +17 -0
  28. data/lib/agent_xmpp/xmpp/element.rb +159 -0
  29. data/lib/agent_xmpp/xmpp/error_response.rb +179 -0
  30. data/lib/agent_xmpp/xmpp/iq.rb +90 -0
  31. data/lib/agent_xmpp/xmpp/iq_command.rb +47 -0
  32. data/lib/agent_xmpp/xmpp/iq_disco.rb +205 -0
  33. data/lib/agent_xmpp/xmpp/iq_pubsub.rb +268 -0
  34. data/lib/agent_xmpp/xmpp/iq_roster.rb +183 -0
  35. data/lib/agent_xmpp/xmpp/iq_version.rb +89 -0
  36. data/lib/agent_xmpp/xmpp/jid.rb +150 -0
  37. data/lib/agent_xmpp/xmpp/message.rb +82 -0
  38. data/lib/agent_xmpp/xmpp/presence.rb +127 -0
  39. data/lib/agent_xmpp/xmpp/sasl.rb +241 -0
  40. data/lib/agent_xmpp/xmpp/stanza.rb +107 -0
  41. data/lib/agent_xmpp/xmpp/x_data.rb +269 -0
  42. data/test/app/app.rb +134 -0
  43. data/test/cases/test_application_message_processing.rb +65 -0
  44. data/test/cases/test_errors.rb +24 -0
  45. data/test/cases/test_presence_management.rb +139 -0
  46. data/test/cases/test_roster_management.rb +214 -0
  47. data/test/cases/test_service_discovery.rb +168 -0
  48. data/test/cases/test_session_management.rb +120 -0
  49. data/test/cases/test_version_discovery.rb +67 -0
  50. data/test/helpers/matchers.rb +23 -0
  51. data/test/helpers/mocks.rb +82 -0
  52. data/test/helpers/test_case_extensions.rb +45 -0
  53. data/test/helpers/test_client.rb +44 -0
  54. data/test/helpers/test_delegate.rb +61 -0
  55. data/test/helpers/test_helper.rb +91 -0
  56. data/test/messages/application_messages.rb +206 -0
  57. data/test/messages/error_messages.rb +35 -0
  58. data/test/messages/presence_messages.rb +66 -0
  59. data/test/messages/roster_messages.rb +126 -0
  60. data/test/messages/service_discovery_messages.rb +201 -0
  61. data/test/messages/session_messages.rb +158 -0
  62. data/test/messages/version_discovery_messages.rb +69 -0
  63. data/test/peer/peer.rb +36 -0
  64. data/test/xml_messages.txt +197 -0
  65. metadata +82 -42
  66. data/bin/agent_xmpp +0 -56
  67. data/lib/agent_xmpp/app/chat_message_body_controller.rb +0 -16
  68. data/lib/agent_xmpp/app/map.rb +0 -45
  69. data/lib/agent_xmpp/app/routes.rb +0 -77
  70. data/lib/agent_xmpp/app/view.rb +0 -49
  71. data/lib/agent_xmpp/client/parser.rb +0 -71
  72. data/lib/agent_xmpp/patches/standard_library_patches.rb +0 -5
  73. data/lib/agent_xmpp/patches/xmpp4r_patches.rb +0 -3
  74. data/lib/agent_xmpp/patches/xmpp4r_patches/command.rb +0 -26
  75. data/lib/agent_xmpp/patches/xmpp4r_patches/iq.rb +0 -26
  76. data/lib/agent_xmpp/patches/xmpp4r_patches/x_data.rb +0 -116
  77. data/lib/agent_xmpp/utils.rb +0 -2
  78. data/lib/agent_xmpp/utils/logger.rb +0 -24
  79. data/lib/agent_xmpp/utils/roster.rb +0 -23
  80. data/lib/agent_xmpp/version.rb +0 -6
  81. data/test/agent_xmpp_test.rb +0 -7
  82. data/test/test_helper.rb +0 -10
@@ -1,6 +1,401 @@
1
- = agent_xmpp
1
+ = Agent XMPP
2
2
 
3
- Description goes here.
3
+ Agent XMPP is a simple DSL for writing XMPP automated clients that support Messaging, Ad-Hoc Commands and Publish Subscribe
4
+ Events. An application that responds to an Ad-Hoc command can be written with few lines of code.
5
+
6
+ # myapp.rb
7
+ require 'rubygems'
8
+ require 'agent_xmpp'
9
+
10
+ execute 'hello' do
11
+ 'Hello World'
12
+ end
13
+
14
+ Specify the application Jabber ID (JID), password and roster in <tt>agent_xmpp.yml</tt>.
15
+
16
+ jid: myapp@nowhere.com
17
+ password: pass
18
+ roster:
19
+ -
20
+ jid:you@home.com
21
+
22
+ Install the gem
23
+
24
+ sudo gem install troystribling-agent_xmpp --source=http://gems.github.com
25
+
26
+ Install the Gajim XMPP Client, http://www.gajim.org, and connect to <i>you@home.com</i> (Be warned that installing Gajim on OS X can be painful.
27
+ Linux is recommended. Also, for Ubuntu installs, download the tar ball from the site. The version in the Ubuntu repositories is
28
+ older and does not have a completed Ad-Hoc Command implementation).
29
+
30
+ Run the application
31
+
32
+ ruby myapp.rb
33
+
34
+ When started for the first time <tt>myapp.rb</tt> will automatically send buddy requests to all contacts specified
35
+ in <tt>agent_xmpp.yml</tt>. Accept the buddy request and _myapp_ will appear as a contact in the Gajim roster. Right click on _myapp_
36
+ and select _execute_ _commands_ from the drop down menu. A list of Ad-Hoc Commands will be displayed containing _hello_.
37
+ Select it and click the _forward_ _button_ to execute.
38
+
39
+ == More Examples
40
+
41
+ More examples can be found at http://gist.github.com/160338
42
+
43
+ == Ad-Hoc Command Response Payload
44
+
45
+ Agent XMPP will map scalars, arrays and hashes returned by <tt>execute</tt> blocks to jabber:x:data command
46
+ response payloads (see XEP-0004 http://xmpp.org/extensions/xep-0004.html for a description of jabber:x:data).
47
+ All scalars have <tt>to_s</tt> applied prior to building the command response payload.
48
+
49
+ execute 'scalar' do
50
+ 'scalar'
51
+ end
52
+
53
+ execute 'hash' do
54
+ {:a1 => 'v1', :a2 => 'v2'}
55
+ end
56
+
57
+ execute 'scalar_array' do
58
+ ['v1', 'v2','v3', 'v4']
59
+ end
60
+
61
+ execute 'hash_array' do
62
+ {:a1 => ['v11', 'v11'], :a2 => 'v12'}
63
+ end
64
+
65
+ execute 'array_hash' do
66
+ [{:a1 => 'v11', :a2 => 'v12'},
67
+ {:a1 => 'v21', :a2 => 'v22'},
68
+ {:a1 => 'v31', :a2 => 'v32'}]
69
+ end
70
+
71
+ execute 'array_hash_array' do
72
+ [{:a1 => ['v11', 'v11'], :a2 => 'v12'},
73
+ {:a1 => ['v21', 'v21'], :a2 => 'v22'},
74
+ {:a1 => ['v31', 'v31'], :a2 => 'v32'}]
75
+ end
76
+
77
+ a <tt>params</tt> structure is available within the <tt>execute</tt> block containing routing information.
78
+
79
+ == Send Command
80
+
81
+ Commands may be sent with or without a response callback,
82
+
83
+ command(:to=>'thatapp@a-place.com/ahost', :node=> 'hello') do |status, data|
84
+ puts "COMMAND RESPONSE: #{status}, #{data.inspect}"
85
+ end
86
+
87
+ command(:to=>'thatapp@a-place.com/ahost', :node=> 'bye')
88
+
89
+ and within <tt>execute</tt> blocks.
90
+
91
+ execute 'hash_hello' do
92
+ command(:to=>params[:from], :node=> 'hello') do |status, data|
93
+ puts "COMMAND RESPONSE: #{status}, #{data.inspect}"
94
+ end
95
+ {:a1 => 'v1', :a2 => 'v2'}
96
+ end
97
+
98
+ == Publish
99
+
100
+ Publish nodes are configured in <tt>agent_xmpp.yml</tt>.
101
+
102
+ jid: myapp@nowhere.com
103
+ password: pass
104
+ roster:
105
+ -
106
+ jid:you@home.com
107
+ publish:
108
+ -
109
+ node: time
110
+ title: "Curent Time"
111
+ -
112
+ node: alarm
113
+ title: "Alarms"
114
+
115
+ The nodes are created if they do not exist and publish methods are generated for each node.
116
+
117
+ publish_time('The time is:' + Time.now.to_s)
118
+
119
+ publish_alarm({:severity => :major, :description => "A really bad failure"})
120
+
121
+ Publish nodes discovered that are not in <tt>agent_xmpp.yml</tt> will be deleted.
122
+
123
+ == Publish Options
124
+
125
+ The following publish options are available with the indicated default values. The options may be changed in
126
+ <tt>agent_xmpp.yml</tt>.
127
+
128
+ :title => 'event',
129
+ :access_model => 'presence',
130
+ :publish_model => 'publishers',
131
+ :max_items => 20,
132
+ :deliver_notifications => 1,
133
+ :deliver_payloads => 1,
134
+ :persist_items => 1,
135
+ :subscribe => 1,
136
+ :notify_config => 0,
137
+ :notify_delete => 0,
138
+ :notify_retract => 0,
139
+
140
+ See http://xmpp.org/extensions/xep-0060.html#registrar-formtypes-config for a detailed description.
141
+
142
+ == Subscribe
143
+
144
+ Declare <tt>event</tt> blocks in <tt>myapp.rb</tt> to subscribe to published events .
145
+
146
+ # myapp.rb
147
+ require 'rubygems'
148
+ require 'agent_xmpp'
149
+
150
+ event 'someone@somewhere.com', 'time' do
151
+ message(:to=>'someone@somewhere.com', :body=>"Got the event at: " + Time.now.to_s)
152
+ end
153
+
154
+ Agent XMPP will verify subscription to the event and subscribe if required. Subscriptions
155
+ discovered that are not declared by an event block will be deleted.
156
+
157
+ == Receive Chat Messages
158
+
159
+ Declare <tt>chat</tt> blocks in <tt>myapp.rb</tt> to receive and respond to chat messages.
160
+
161
+ # myapp.rb
162
+ require 'rubygems'
163
+ require 'agent_xmpp'
164
+
165
+ chat do
166
+ params[:body].reverse
167
+ end
168
+
169
+ If the <tt>chat</tt> block returns a <tt>String</tt> a response will be sent to the message sender.
170
+
171
+ == Send Chat Messages
172
+
173
+ message(:to=>'thatapp@a-place.com/onahost', :body=>"Hello from #{AgentXmpp.jid.to_s} at " + Time.now.to_s)
174
+
175
+ == Roster
176
+
177
+ Roster groups may be specified in <tt>agent_xmpp.yml</tt>.
178
+
179
+ jid: myapp@nowhere.com
180
+ password: pass
181
+ roster:
182
+ -
183
+ jid:you@home.com
184
+ groups: [good group, owners]
185
+
186
+ -
187
+ jid: someone@somewhere.com
188
+ groups: [bad group]
189
+
190
+ Agent XMPP will update the roster maintained on the XMPP server to be consistent with to the roster specified in <tt>agent_xmpp.yml</tt>.
191
+
192
+ == Routing Priority
193
+
194
+ The routing priority may be configured in <tt>agent_xmpp.yml</tt>. The default value is 1. Valid values are between -127 and 128.
195
+ See http://xmpp.org/rfcs/rfc3921.html for a details.
196
+
197
+ jid: myapp@nowhere.com
198
+ password: pass
199
+ priority: 128
200
+ roster:
201
+ -
202
+ jid:you@home.com
203
+ groups: [good group, owners]
204
+
205
+ == Major Event Callbacks
206
+
207
+ Agent XMPP provides callbacks for applications to respond to major events that occur during execution.
208
+
209
+ # application starting
210
+ before_start{}
211
+
212
+ # connected to server
213
+ after_connected{|connection|}
214
+
215
+ # client restarts when disconnected form server
216
+ restarting_client{|connection|}
217
+
218
+ # a pubsub node was discovered at service
219
+ discovered_pubsub_node{|service, node|}
220
+
221
+ # command nodes were discovered at jid
222
+ discovered_command_nodes{|jid, nodes|}
223
+
224
+ # a presence message of status :available or :unavailable was received from jid
225
+ received_presence{|from, status|}
226
+
227
+ == Authentication
228
+
229
+ * Basic SASL
230
+
231
+ == Development with XMPP Clients
232
+
233
+ Ad-Hoc Commands, jabber:x:data Forms nor Service Discovery are widely supported by XMPP clients and I have not found
234
+ a client that supports Publish-Subscribe. Gajim http://www.gajim.org provides support for Ad-Hoc Commands and jabber:x:data Forms. Service Discovery,
235
+ which is useful for Publish-Subscibe development, is supported by Gajim, but Psi http://psi-im.org provides a much better
236
+ implementation. Both Gajim and Psi provide an interface for manual entry of XML messages. Since Publish-Subscribe is not supported on
237
+ the user interface manual entry of messages is required for development. Example messages can be found at http://gist.github.com/160344
238
+
239
+ == Logging
240
+
241
+ By default log messages are written to STDOUT. A log file can be specified with the -l option.
242
+
243
+ ruby mybot.rb -l file.log
244
+
245
+ The logger can be accessed and configured.
246
+
247
+ before_start do
248
+ AgentXmpp.logger.level = Logger::WARN
249
+ end
250
+
251
+ == Supported XEPs
252
+
253
+ XEP-0004 jabber:x:data Forms http://xmpp.org/extensions/xep-0004.html
254
+ XEP-0030 Service Discovery http://xmpp.org/extensions/xep-0030.html
255
+ XEP-0050 Ad-Hoc Commands http://xmpp.org/extensions/xep-0050.html
256
+ XEP-0060 Publish Subscribe http://xmpp.org/extensions/xep-0060.html
257
+ XEP-0092 Software Version http://xmpp.org/extensions/xep-0092.html
258
+
259
+ == Message Processing Callbacks
260
+
261
+ Message processing callbacks are available to applications to extend the message processing work flow. To receive
262
+ callbacks a delegate object must be provided that implements the callbacks of interest.
263
+
264
+ after_connected do |connection|
265
+ connection.add_delegate(YourDelegate)
266
+ end
267
+
268
+ === Connection
269
+
270
+ did_connect(connection)
271
+
272
+ did_disconnect(connection)
273
+
274
+ did_not_connect(connection)
275
+
276
+ === Authentication
277
+
278
+ did_bind(connection)
279
+
280
+ did_receive_preauthenticate_features(connection)
281
+
282
+ did_authenticate(connection)
283
+
284
+ did_not_authenticate(connection)
285
+
286
+ did_receive_postauthenticate_features(connection)
287
+
288
+ did_start_session(connection)
289
+
290
+ === Presence
291
+
292
+ did_receive_presence(connection, presence)
293
+
294
+ did_receive_presence_subscribe(connection, presence)
295
+
296
+ did_receive_presence_subscribed(connection, presence)
297
+
298
+ did_receive_presence_unavailable(connection, presence)
299
+
300
+ did_receive_presence_unsubscribed(connection, presence)
301
+
302
+ did_receive_presence_error(pipe, presence)
303
+
304
+ === Roster
305
+
306
+ did_receive_roster_result(connection, stanza)
307
+
308
+ did_receive_roster_set(connection, stanza)
309
+
310
+ did_receive_roster_item(connection, roster_item)
311
+
312
+ did_remove_roster_item(connection, roster_item)
313
+
314
+ did_receive_all_roster_items(connection)
315
+
316
+ did_receive_update_roster_item_result(connection, response)
317
+
318
+ did_receive_update_roster_item_error(connection, response)
319
+
320
+ did_receive_remove_roster_item(connection, response)
321
+
322
+ did_receive_remove_roster_item_error(connection, response)
323
+
324
+ === Service Discovery
325
+
326
+ did_receive_version_result(connection, version)
327
+
328
+ did_receive_version_get(connection, request)
329
+
330
+ did_receive_version_error(connection, error)
331
+
332
+ did_receive_discoinfo_get(connection, request)
333
+
334
+ did_receive_discoinfo_result(connection, discoinfo)
335
+
336
+ did_receive_discoinfo_error(connection, error)
337
+
338
+ did_receive_discoitems_result(connection, discoitems)
339
+
340
+ did_receive_discoitems_get(connection, request)
341
+
342
+ did_receive_discoitems_error(connection, result)
343
+
344
+ === Applications
345
+
346
+ did_receive_command_set(connection, stanza)
347
+
348
+ did_receive_message_chat(connection, stanza)
349
+
350
+ did_receive_message_normal(connection, stanza)
351
+
352
+ did_receive_pubsub_event(connection, event, to, from)
353
+
354
+ === PubSub
355
+
356
+ did_receive_publish_result(connection, result, node)
357
+
358
+ did_receive_publish_error(connection, result, node)
359
+
360
+ did_discover_pubsub_service(connection, jid, ident)
361
+
362
+ did_discover_pubsub_collection(connection, jid, node)
363
+
364
+ did_discover_pubsub_leaf(connection, jid, node)
365
+
366
+ did_discover_user_pubsub_root(pipe, pubsub, node)
367
+
368
+ did_receive_pubsub_subscriptions_result(connection, result)
369
+
370
+ did_receive_pubsub_subscriptions_error(connection, result)
371
+
372
+ did_receive_pubsub_affiliations_result(connection, result)
373
+
374
+ did_receive_pubsub_affiliations_error(connection, result)
375
+
376
+ did_discover_user_pubsub_root(connection, result)
377
+
378
+ did_receive_create_node_result(connection, node, result)
379
+
380
+ did_receive_create_node_error(connection, node, result)
381
+
382
+ did_receive_delete_node_result(connection, node, result)
383
+
384
+ did_receive_delete_node_error(connection, node, result)
385
+
386
+ did_receive_pubsub_subscribe_result(connection, result, node)
387
+
388
+ did_receive_pubsub_subscribe_error(connection, result, node)
389
+
390
+ did_receive_pubsub_subscribe_error_item_not_found(connection, result, node)
391
+
392
+ did_receive_pubsub_unsubscribe_result(connection, result, node)
393
+
394
+ did_receive_pubsub_unsubscribe_error(connection, result, node)
395
+
396
+ === ERRORS
397
+
398
+ did_receive_unsupported_message(connection, stanza)
4
399
 
5
400
  == Copyright
6
401
 
data/Rakefile CHANGED
@@ -1,26 +1,28 @@
1
1
  ############################################################################################################
2
+ $:.unshift('lib')
2
3
  require 'rubygems'
3
4
  require 'rake'
4
- require 'lib/agent_xmpp/version'
5
+ require 'agent_xmpp/config'
5
6
 
6
7
  #####-------------------------------------------------------------------------------------------------------
7
- require 'jeweler'
8
- Jeweler::Tasks.new do |gem|
9
- gem.name = "agent_xmpp"
10
- gem.summary = %Q{Agent XMPP is a ruby XMPP bot framework inspired by MVC web frameworks.}
11
- gem.email = "troy.stribling@gmail.com"
12
- gem.homepage = "http://github.com/troystribling/agent_xmpp"
13
- gem.authors = ["Troy Stribling"]
14
- gem.files.include %w(lib/jeweler/templates/.gitignore VERSION)
15
- gem.add_dependency('rake', '>= 0.8.3')
16
- gem.add_dependency('eventmachine', '= 0.12.6')
17
- gem.add_dependency('troystribling-evma_xmlpushparser', '= 0.0.1')
18
- gem.add_dependency('xmpp4r', '= 0.4')
19
- end
8
+ task :default => :test
20
9
 
21
10
  #####-------------------------------------------------------------------------------------------------------
22
- task :install => :build do
23
- %x[gem install pkg/agent_xmpp-#{AgentXmpp::VERSION}]
11
+ begin
12
+ require 'jeweler'
13
+ Jeweler::Tasks.new do |gem|
14
+ gem.name = "agent_xmpp"
15
+ gem.summary = %Q{Agent XMPP is a ruby XMPP bot framework inspired by MVC web frameworks.}
16
+ gem.email = "troy.stribling@gmail.com"
17
+ gem.homepage = "http://github.com/troystribling/agent_xmpp"
18
+ gem.authors = ["Troy Stribling"]
19
+ gem.files.include %w(lib/jeweler/templates/.gitignore VERSION)
20
+ gem.add_dependency('rake', '>= 0.8.3')
21
+ gem.add_dependency('eventmachine', '= 0.12.6')
22
+ gem.add_dependency('troystribling-evma_xmlpushparser', '= 0.0.1')
23
+ end
24
+ rescue LoadError
25
+ abort "jeweler is not available. In order to run test, you must: sudo gem install technicalpickles-jeweler --source=http://gems.github.com"
24
26
  end
25
27
 
26
28
  #####-------------------------------------------------------------------------------------------------------
@@ -28,11 +30,22 @@ task :uninstall do
28
30
  %x[gem uninstall agent_xmpp]
29
31
  end
30
32
 
33
+ #####-------------------------------------------------------------------------------------------------------
34
+ task :default => [:test]
35
+
31
36
  #####-------------------------------------------------------------------------------------------------------
32
37
  require 'rake/testtask'
33
38
  Rake::TestTask.new(:test) do |test|
34
- test.libs << 'lib' << 'test'
35
- test.pattern = 'test/**/*_test.rb'
39
+ test.libs << ['test/cases', 'test/helpers', 'test/messages']
40
+ test.pattern = 'test/cases/**/test_*.rb'
41
+ test.verbose = true
42
+ end
43
+
44
+ #####-------------------------------------------------------------------------------------------------------
45
+ Rake::TestTask.new(:test_case) do |test|
46
+ file = ENV["FILE"] || ''
47
+ test.libs << ['test/cases', 'test/helpers', 'test/messages']
48
+ test.test_files = ["test/cases/#{file}"]
36
49
  test.verbose = true
37
50
  end
38
51
 
@@ -41,17 +54,16 @@ begin
41
54
  require 'rcov/rcovtask'
42
55
  Rcov::RcovTask.new do |test|
43
56
  test.libs << 'test'
44
- test.pattern = 'test/**/*_test.rb'
57
+ test.pattern = 'test/**/test_*.rb'
45
58
  test.verbose = true
46
59
  end
47
60
  rescue LoadError
48
61
  task :rcov do
49
- abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
62
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov --source=http://gems.github.com"
50
63
  end
51
64
  end
52
65
 
53
66
  #####-------------------------------------------------------------------------------------------------------
54
- task :default => :test
55
67
  require 'rake/rdoctask'
56
68
  Rake::RDocTask.new do |rdoc|
57
69
  rdoc.rdoc_dir = 'rdoc'