raccdoc 0.0.10
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/CHANGELOG +1 -0
- data/LICENSE +20 -0
- data/Manifest +14 -0
- data/README.rdoc +92 -0
- data/Rakefile +32 -0
- data/VERSION +1 -0
- data/lib/raccdoc.rb +21 -0
- data/lib/raccdoc/connection.rb +108 -0
- data/lib/raccdoc/forum.rb +204 -0
- data/lib/raccdoc/post.rb +83 -0
- data/raccdoc.gemspec +58 -0
- data/test/raccdoc_connection_test.rb +89 -0
- data/test/raccdoc_forum_test.rb +167 -0
- data/test/raccdoc_post_test.rb +61 -0
- data/test/test_helper.rb +5 -0
- metadata +74 -0
data/CHANGELOG
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
For changes, see the github source at http://github.com/minter/raccdoc/
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 H. Wade Minter
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Manifest
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
CHANGELOG
|
2
|
+
lib/raccdoc/connection.rb
|
3
|
+
lib/raccdoc/forum.rb
|
4
|
+
lib/raccdoc/post.rb
|
5
|
+
lib/raccdoc.rb
|
6
|
+
LICENSE
|
7
|
+
Manifest
|
8
|
+
raccdoc.gemspec
|
9
|
+
Rakefile
|
10
|
+
README.rdoc
|
11
|
+
test/raccdoc_connection_test.rb
|
12
|
+
test/raccdoc_forum_test.rb
|
13
|
+
test/raccdoc_post_test.rb
|
14
|
+
test/test_helper.rb
|
data/README.rdoc
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
= Raccdoc
|
2
|
+
|
3
|
+
== DESCRIPTION
|
4
|
+
|
5
|
+
Raccdoc is an API into the data of several classic telnet-based BBS systems, including:
|
6
|
+
|
7
|
+
* ISCABBS[http://iscabbs.com/]
|
8
|
+
* InaraBBS[http://www.inarabbs.com/]
|
9
|
+
* Gestalt
|
10
|
+
|
11
|
+
This is a Ruby version of that API.
|
12
|
+
|
13
|
+
The Raccdoc API document can be found at http://dev.iscabbs.com/doku.php?id=raccdoc_protocol_documentation
|
14
|
+
|
15
|
+
== EXAMPLE
|
16
|
+
|
17
|
+
require 'raccdoc'
|
18
|
+
|
19
|
+
# Connect to the BBS as user Bugcrusher
|
20
|
+
bbs = Raccdoc::Connection.new(:host => 'bbs.iscabbs.com', :port => 6145, :user => 'Bugcrusher', :password => 'MyPass')
|
21
|
+
|
22
|
+
# Find out information on each forum
|
23
|
+
bbs.forums.each do |id, data|
|
24
|
+
print "On forum number #{id} named #{data[:name]}\n"
|
25
|
+
end
|
26
|
+
|
27
|
+
# Join the Babble forum
|
28
|
+
forum = bbs.jump("Babble")
|
29
|
+
|
30
|
+
# See if you have permission to post
|
31
|
+
print "Can you post in there? #{forum.post?}\n"
|
32
|
+
|
33
|
+
# Read post number 2280
|
34
|
+
post = forum.read(2280)
|
35
|
+
|
36
|
+
# Print out the post body for 2280
|
37
|
+
print "Body is #{post.body}\n"
|
38
|
+
|
39
|
+
# Make your own post
|
40
|
+
mypost = forum.post("Hello world!")
|
41
|
+
|
42
|
+
# Get information on it
|
43
|
+
print "My new post is id number #{mypost.id}\n"
|
44
|
+
|
45
|
+
# Now delete it
|
46
|
+
mypost.delete
|
47
|
+
|
48
|
+
# Log out
|
49
|
+
bbs.logout
|
50
|
+
|
51
|
+
== REQUIREMENTS
|
52
|
+
|
53
|
+
* Ruby 1.8
|
54
|
+
* An account on the BBS that you wish to access (unless doing Guest/read-only operations)
|
55
|
+
* Mocha (for tests)
|
56
|
+
|
57
|
+
== TODO & KNOWN ISSUES
|
58
|
+
|
59
|
+
* X-Message support
|
60
|
+
* Mail> support
|
61
|
+
|
62
|
+
== INSTALL
|
63
|
+
|
64
|
+
* gem sources -a http://gems.rubyforge.org/
|
65
|
+
* sudo gem install raccdoc
|
66
|
+
|
67
|
+
Then require 'raccdoc' in your Ruby program.
|
68
|
+
|
69
|
+
== LICENSE
|
70
|
+
|
71
|
+
(The MIT License)
|
72
|
+
|
73
|
+
Copyright (c) 2009 H. Wade Minter
|
74
|
+
|
75
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
76
|
+
a copy of this software and associated documentation files (the
|
77
|
+
'Software'), to deal in the Software without restriction, including
|
78
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
79
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
80
|
+
permit persons to whom the Software is furnished to do so, subject to
|
81
|
+
the following conditions:
|
82
|
+
|
83
|
+
The above copyright notice and this permission notice shall be
|
84
|
+
included in all copies or substantial portions of the Software.
|
85
|
+
|
86
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
87
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
88
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
89
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
90
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
91
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
92
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require './lib/raccdoc.rb'
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'jeweler'
|
5
|
+
Jeweler::Tasks.new do |gemspec|
|
6
|
+
gemspec.name = "raccdoc"
|
7
|
+
gemspec.summary = "Ruby interface into the Raccdoc BBS API"
|
8
|
+
gemspec.description = "A Ruby way to speak the Raccdoc protocol"
|
9
|
+
gemspec.email = "minter@lunenburg.org"
|
10
|
+
gemspec.homepage = "http://github.com/minter/raccdoc"
|
11
|
+
gemspec.authors = ["H. Wade Minter"]
|
12
|
+
end
|
13
|
+
Jeweler::GemcutterTasks.new
|
14
|
+
rescue LoadError
|
15
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
16
|
+
end
|
17
|
+
|
18
|
+
namespace :test do
|
19
|
+
desc 'Check test coverage'
|
20
|
+
task :coverage do
|
21
|
+
rm_f "coverage"
|
22
|
+
system("rcov -x '/Library/Ruby/Gems/1.8/gems/' --sort coverage #{File.join(File.dirname(__FILE__), 'test/*_test.rb')}")
|
23
|
+
system("open #{File.join(File.dirname(__FILE__), 'coverage/index.html')}") if PLATFORM['darwin']
|
24
|
+
end
|
25
|
+
|
26
|
+
desc 'Remove coverage products'
|
27
|
+
task :clobber_coverage do
|
28
|
+
rm_r 'coverage' rescue nil
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.10
|
data/lib/raccdoc.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
module Raccdoc
|
2
|
+
|
3
|
+
VERSION = '0.0.10'
|
4
|
+
require 'socket'
|
5
|
+
require 'time'
|
6
|
+
|
7
|
+
$:.unshift(File.dirname(__FILE__))
|
8
|
+
require 'raccdoc/connection'
|
9
|
+
require 'raccdoc/forum'
|
10
|
+
require 'raccdoc/post'
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
class InvalidLogin < StandardError
|
15
|
+
end
|
16
|
+
|
17
|
+
class ConnectionError < StandardError
|
18
|
+
end
|
19
|
+
|
20
|
+
class ResponseError < StandardError
|
21
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
module Raccdoc
|
2
|
+
class Connection
|
3
|
+
|
4
|
+
attr_reader :host
|
5
|
+
attr_reader :post
|
6
|
+
|
7
|
+
# Creates a new Raccdoc connection. By default, it connects to bbs.iscabbs.com, port 6145, which is the
|
8
|
+
# ISCABBS server.
|
9
|
+
#
|
10
|
+
# If a :user and :password is not supplied, a guest/anonymous login is done. Most, if not all, posting is
|
11
|
+
# restricted under this account.
|
12
|
+
#
|
13
|
+
# If the TCP connection fails, or the login is rejected, and exception is raised.
|
14
|
+
def initialize(param_args = {:host => 'bbs.iscabbs.com', :port => '6145', :user => nil, :password => nil})
|
15
|
+
args = {:host => 'bbs.iscabbs.com', :port => '6145', :user => nil, :password => nil }
|
16
|
+
|
17
|
+
args.merge!(param_args)
|
18
|
+
begin
|
19
|
+
@socket = TCPSocket.new(args[:host],args[:port])
|
20
|
+
rescue Errno::ECONNREFUSED
|
21
|
+
raise ConnectionError, "Could not connect to #{args[:host]}, port #{args[:port]}\n"
|
22
|
+
end
|
23
|
+
|
24
|
+
begin
|
25
|
+
response = @socket.readline.chomp
|
26
|
+
rescue EOFError
|
27
|
+
raise ConnectionError, "Got an unexpected EOF from the remote end"
|
28
|
+
end
|
29
|
+
|
30
|
+
unless response.match(/^2/)
|
31
|
+
raise ConnectionError, response
|
32
|
+
end
|
33
|
+
|
34
|
+
if (args[:user] && args[:password])
|
35
|
+
@socket.puts("LOGIN #{args[:user]}\t#{args[:password]}")
|
36
|
+
response = @socket.readline.chomp
|
37
|
+
unless response.match(/^2/)
|
38
|
+
raise InvalidLogin, response
|
39
|
+
end
|
40
|
+
end
|
41
|
+
@host = args[:host]
|
42
|
+
@port = args[:port]
|
43
|
+
end
|
44
|
+
|
45
|
+
# Closes the Raccdoc connection, then closes the socket.
|
46
|
+
def logout
|
47
|
+
unless @socket.closed?
|
48
|
+
@socket.puts "QUIT"
|
49
|
+
|
50
|
+
response = @socket.readline.chomp
|
51
|
+
unless response.match(/^2/)
|
52
|
+
raise ConnectionError, response
|
53
|
+
end
|
54
|
+
@socket.close
|
55
|
+
end
|
56
|
+
return true
|
57
|
+
end
|
58
|
+
|
59
|
+
# Tests the TCP socket to make sure that it's still connected to the remote server.
|
60
|
+
def connected?
|
61
|
+
@socket.closed? ? false : true
|
62
|
+
end
|
63
|
+
|
64
|
+
# Sets the active forum on the server to the specified forum name or ID, and returns a new Raccdoc::Forum object.
|
65
|
+
#
|
66
|
+
# The forum can be specified by number (0), or name ("Lobby" or "Program")
|
67
|
+
def jump(forum = 0)
|
68
|
+
Raccdoc::Forum.new(@socket, forum.to_s)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Returns a hash of forum information. The key is the forum ID number, and the value is a hash containing the following data:
|
72
|
+
#
|
73
|
+
# * name - The forum name
|
74
|
+
# * lastnote - The earliest post ID number in the forum
|
75
|
+
# * admin - The handle of the forum admin
|
76
|
+
#
|
77
|
+
# The argument can be one of: ALL (default), PUBLIC, PRIVATE, TODO, JOINED, NAMED, THREADS
|
78
|
+
# depending on server support
|
79
|
+
#
|
80
|
+
# Note that this does not return actual Raccdoc::Forum objects in the interest of saving resources, so you'll need to jump to the forum that you want.
|
81
|
+
def forums(type="all")
|
82
|
+
@socket.puts "LIST #{type}"
|
83
|
+
forums = Hash.new
|
84
|
+
|
85
|
+
response = @socket.readline.chomp
|
86
|
+
unless response.match(/^3/)
|
87
|
+
raise ResponseError, response
|
88
|
+
end
|
89
|
+
|
90
|
+
while line = @socket.readline.chomp
|
91
|
+
break if line.match(/^\.$/)
|
92
|
+
tmp = Hash.new
|
93
|
+
line.split(/\t/).each do |pair|
|
94
|
+
(key, value) = pair.split(/:/)
|
95
|
+
if key == "admin"
|
96
|
+
value = value.split('/')[1]
|
97
|
+
end
|
98
|
+
tmp[key.to_sym] = value
|
99
|
+
end
|
100
|
+
id = tmp[:topic].to_i
|
101
|
+
forums[id] = tmp
|
102
|
+
end
|
103
|
+
|
104
|
+
return forums
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,204 @@
|
|
1
|
+
module Raccdoc
|
2
|
+
class Forum
|
3
|
+
|
4
|
+
attr_reader :id
|
5
|
+
attr_reader :name
|
6
|
+
attr_reader :admin
|
7
|
+
attr_reader :anonymous
|
8
|
+
attr_reader :private
|
9
|
+
|
10
|
+
# Sets the active forum on the server to the specified forum number (0) or name (Lobby, Program), then returns a new Raccdoc::Forum object.
|
11
|
+
#
|
12
|
+
# There are five instance variables exposed through attr_reader:
|
13
|
+
#
|
14
|
+
# * id - The forum ID number
|
15
|
+
# * name - The forum name
|
16
|
+
# * admin - The name of the forum administrator
|
17
|
+
# * anonymous - either true, false, or force, depending on whether the anonymous posting option is on, off, or required
|
18
|
+
# * private - True if the forum is private (invite-only), false otherwise
|
19
|
+
def initialize(socket, forum = 0)
|
20
|
+
@socket = socket
|
21
|
+
@socket.puts "TOPIC #{forum.to_s}"
|
22
|
+
response = @socket.readline.chomp
|
23
|
+
unless response.match(/^2/)
|
24
|
+
raise ResponseError, response
|
25
|
+
end
|
26
|
+
|
27
|
+
@headers = {}
|
28
|
+
tuples = response.split(/\t/)
|
29
|
+
tuples.delete_at(0)
|
30
|
+
tuples.each do |pair|
|
31
|
+
(key, value) = pair.split(/:/)
|
32
|
+
@headers[key.downcase.to_sym] = value
|
33
|
+
end
|
34
|
+
|
35
|
+
flags = @headers[:flags].split(',')
|
36
|
+
|
37
|
+
if flags.include?("forceanonymous")
|
38
|
+
@anonymous = "force"
|
39
|
+
elsif flags.include?("cananonymous")
|
40
|
+
@anonymous = true
|
41
|
+
else
|
42
|
+
@anonymous = false
|
43
|
+
end
|
44
|
+
|
45
|
+
@private = flags.include?("private") ? true : false
|
46
|
+
|
47
|
+
@id = @headers[:topic]
|
48
|
+
@name = @headers[:name]
|
49
|
+
@admin = @headers[:admin].split('/')[1]
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns an array of all current post IDs in the forum.
|
53
|
+
#
|
54
|
+
# Takes an optional range string of the form oldest_noteid-newest_noteid. The oldest or newest can be omitted as needed.
|
55
|
+
def noteids(range = "")
|
56
|
+
noteids = Array.new
|
57
|
+
@socket.puts("XHDR noteno #{range.to_s}")
|
58
|
+
response = @socket.readline.chomp
|
59
|
+
unless response.match(/^3/)
|
60
|
+
raise ResponseError, response
|
61
|
+
end
|
62
|
+
|
63
|
+
while line = @socket.readline.chomp
|
64
|
+
break if line.match(/^\.$/)
|
65
|
+
(tag,noteid) = line.split(/:/)
|
66
|
+
noteids.push(noteid.to_i)
|
67
|
+
end
|
68
|
+
|
69
|
+
return noteids
|
70
|
+
end
|
71
|
+
|
72
|
+
# Returns a hash of the forum information. Keys are:
|
73
|
+
#
|
74
|
+
# * from - The username of the user who last updated the FI
|
75
|
+
# * date - The date the forum information was last updated (a Time object)
|
76
|
+
# * body - The actual text of the FI
|
77
|
+
def forum_information
|
78
|
+
fi = Hash.new
|
79
|
+
@socket.puts("SHOW info")
|
80
|
+
response = @socket.readline.chomp
|
81
|
+
unless response.match(/^3/)
|
82
|
+
raise ResponseError, response
|
83
|
+
end
|
84
|
+
|
85
|
+
# Get header information
|
86
|
+
while line = @socket.readline.chomp
|
87
|
+
break if line.match(/^$/)
|
88
|
+
(key, value) = line.split(/: /)
|
89
|
+
if key.downcase == "date"
|
90
|
+
value = Time.parse(value)
|
91
|
+
end
|
92
|
+
fi[key.downcase.to_sym] = value
|
93
|
+
end
|
94
|
+
|
95
|
+
body = ""
|
96
|
+
while line = @socket.readline
|
97
|
+
break if line.match(/^\.$/)
|
98
|
+
body << line
|
99
|
+
end
|
100
|
+
fi[:body] = body
|
101
|
+
|
102
|
+
return fi
|
103
|
+
end
|
104
|
+
|
105
|
+
# Sets the current user's first-unread pointer to the specified number. Any posts greater than that number will be listed as unread.
|
106
|
+
def first_unread=(postid = 0)
|
107
|
+
@socket.puts("SETRC #{postid.to_s}")
|
108
|
+
response = @socket.readline.chomp
|
109
|
+
unless response.match(/^2/)
|
110
|
+
raise ResponseError, response
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# Returns the post ID number of the last read post in the forum. Any posts greater than that number can be considered unread.
|
115
|
+
def first_unread
|
116
|
+
@socket.puts("SHOW rcval")
|
117
|
+
response = @socket.readline.chomp
|
118
|
+
unless response.match(/^2/)
|
119
|
+
raise ResponseError, response
|
120
|
+
end
|
121
|
+
response.match(/\d+.*?:\s+(\d+)/)
|
122
|
+
return $1
|
123
|
+
end
|
124
|
+
|
125
|
+
# Returns a hash of information about the posts in the forum. The hash is keyed off of the post ID, and the value is a hash containing the following information:
|
126
|
+
#
|
127
|
+
# * author - The username of the user who created the post
|
128
|
+
# * date - The date the post was created or, if the post is anonymous, the current date in UTC
|
129
|
+
# * subject - The first line of the post
|
130
|
+
# * size - The size of the post in bytes
|
131
|
+
# * authority - If the post was made with Sysop or Forum Manager status, this is set
|
132
|
+
#
|
133
|
+
# You may provide an optional range (in the form "start_id-end_id") to limit the number of posts returned. The default is to return all posts in the forum.
|
134
|
+
def post_headers(range = "")
|
135
|
+
@socket.puts("XHDR ALL #{range.to_s}")
|
136
|
+
response = @socket.readline.chomp
|
137
|
+
unless response.match(/^3/)
|
138
|
+
raise ResponseError, response
|
139
|
+
end
|
140
|
+
|
141
|
+
posts = Hash.new
|
142
|
+
|
143
|
+
while (line = @socket.readline.chomp)
|
144
|
+
break if line.match(/^\.$/)
|
145
|
+
tmpdata = Hash.new
|
146
|
+
line.split(/\t/).each do |tuple|
|
147
|
+
(key, value) = tuple.split(/:/, 2)
|
148
|
+
tmpdata[key.downcase.to_sym] = value
|
149
|
+
end
|
150
|
+
tmpdata[:date] = tmpdata[:date] ? Time.parse(tmpdata[:date]) : Time.new.getgm
|
151
|
+
tmpdata[:author] = tmpdata[:"formal-author"] ? tmpdata[:"formal-author"].split('/')[1] : 'Anonymous'
|
152
|
+
posts[tmpdata[:noteno]] = tmpdata
|
153
|
+
end
|
154
|
+
return posts
|
155
|
+
end
|
156
|
+
|
157
|
+
# Returns a new Raccdoc::Post object for the specified post ID in the current forum.
|
158
|
+
def read(postid)
|
159
|
+
Post.new(@socket,postid)
|
160
|
+
end
|
161
|
+
|
162
|
+
# Attempts to do a "DAMMIT" read, overriding anonymous and deleted flags. If you do not have permission to bypass those flags, this is equivalent to a plain read.
|
163
|
+
def read!(postid)
|
164
|
+
Post.new(@socket,postid,:dammit => true)
|
165
|
+
end
|
166
|
+
|
167
|
+
# Creates a new post in the current forum using the text provided in the argument.
|
168
|
+
#
|
169
|
+
# Returns a new Raccdoc::Post object that results from reading the newly created post.
|
170
|
+
def post(body)
|
171
|
+
@socket.puts("POST")
|
172
|
+
response = @socket.readline.chomp
|
173
|
+
unless response.match(/^3/)
|
174
|
+
raise ResponseError, response
|
175
|
+
end
|
176
|
+
|
177
|
+
@socket.puts(body)
|
178
|
+
@socket.puts(".")
|
179
|
+
|
180
|
+
response = @socket.readline.chomp
|
181
|
+
if response.match(/^2/)
|
182
|
+
postid = response.split(/:\s+/)[1]
|
183
|
+
return Post.new(@socket,postid)
|
184
|
+
else
|
185
|
+
raise ResponseError, response
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
# Checks to see if the currently logged in user has permission to post in the current forum.
|
190
|
+
#
|
191
|
+
# Returns true or false.
|
192
|
+
def post?
|
193
|
+
@socket.puts("OKAY POST")
|
194
|
+
response = @socket.readline.chomp
|
195
|
+
if response.match(/^2/)
|
196
|
+
return true
|
197
|
+
elsif response.match(/^4/)
|
198
|
+
return false
|
199
|
+
else
|
200
|
+
raise ResponseError, response
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
data/lib/raccdoc/post.rb
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
module Raccdoc
|
2
|
+
class Post
|
3
|
+
|
4
|
+
attr_reader :id
|
5
|
+
attr_reader :date
|
6
|
+
attr_reader :author
|
7
|
+
attr_reader :body
|
8
|
+
attr_reader :authority
|
9
|
+
|
10
|
+
# Creates a new Raccdoc::Post object by reading the specified post. If no post is given, it returns the first unread post, if any.
|
11
|
+
#
|
12
|
+
# Useful information returned:
|
13
|
+
#
|
14
|
+
# * id - The numerical post ID
|
15
|
+
# * date - The date the post was made (or the current date in UTC if the post is anonymous)
|
16
|
+
# * author - The username of the user that created the post
|
17
|
+
# * body - The textual body of the post.
|
18
|
+
# * authority - either "Sysop" or "Forum Moderator" if the post was made with that header.
|
19
|
+
def initialize(socket,postid,args={})
|
20
|
+
@socket = socket
|
21
|
+
dammit = args[:dammit] ? 'DAMMIT' : ''
|
22
|
+
@socket.puts "READ #{postid.to_s} #{dammit}"
|
23
|
+
response = @socket.readline.chomp
|
24
|
+
unless response.match(/^3/)
|
25
|
+
raise ResponseError, response
|
26
|
+
end
|
27
|
+
|
28
|
+
# Get header information
|
29
|
+
post = Hash.new
|
30
|
+
|
31
|
+
while line = @socket.readline.chomp
|
32
|
+
break if line.match(/^$/)
|
33
|
+
(key,value) = line.split(/: /)
|
34
|
+
post[key.downcase.to_sym] = value
|
35
|
+
end
|
36
|
+
post[:id] = postid
|
37
|
+
post[:date] = post[:date] ? Time.parse(post[:date]) : Time.new.getgm
|
38
|
+
|
39
|
+
post[:body] = ""
|
40
|
+
while (line = @socket.readline)
|
41
|
+
break if line.match(/^\.$/)
|
42
|
+
post[:body] << line
|
43
|
+
end
|
44
|
+
|
45
|
+
@id = post[:id]
|
46
|
+
@date = post[:date]
|
47
|
+
@author = post[:from]
|
48
|
+
@body = post[:body]
|
49
|
+
@authority = post[:authority]
|
50
|
+
return post
|
51
|
+
end
|
52
|
+
|
53
|
+
# Checks to see if the currently-logged-in user has permission to delete the current post.
|
54
|
+
#
|
55
|
+
# Returns true or false
|
56
|
+
def delete?
|
57
|
+
@socket.puts("OKAY DELETE NOTE #{self.id}")
|
58
|
+
response = @socket.readline.chomp
|
59
|
+
if response.match(/^2/)
|
60
|
+
return true
|
61
|
+
elsif response.match(/^4/)
|
62
|
+
return false
|
63
|
+
else
|
64
|
+
raise ResponseError, response
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Attempts to delete the current post.
|
69
|
+
#
|
70
|
+
# Returns true or false
|
71
|
+
def delete
|
72
|
+
@socket.puts("DELETE NOTE #{self.id}")
|
73
|
+
response = @socket.readline.chomp
|
74
|
+
if response.match(/^2/)
|
75
|
+
return true
|
76
|
+
elsif response.match(/^4/)
|
77
|
+
return false
|
78
|
+
else
|
79
|
+
raise ResponseError, response
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
data/raccdoc.gemspec
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{raccdoc}
|
8
|
+
s.version = "0.0.10"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["H. Wade Minter"]
|
12
|
+
s.date = %q{2009-10-08}
|
13
|
+
s.description = %q{A Ruby way to speak the Raccdoc protocol}
|
14
|
+
s.email = %q{minter@lunenburg.org}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".gitignore",
|
21
|
+
"CHANGELOG",
|
22
|
+
"LICENSE",
|
23
|
+
"Manifest",
|
24
|
+
"README.rdoc",
|
25
|
+
"Rakefile",
|
26
|
+
"VERSION",
|
27
|
+
"lib/raccdoc.rb",
|
28
|
+
"lib/raccdoc/connection.rb",
|
29
|
+
"lib/raccdoc/forum.rb",
|
30
|
+
"lib/raccdoc/post.rb",
|
31
|
+
"raccdoc.gemspec",
|
32
|
+
"test/raccdoc_connection_test.rb",
|
33
|
+
"test/raccdoc_forum_test.rb",
|
34
|
+
"test/raccdoc_post_test.rb",
|
35
|
+
"test/test_helper.rb"
|
36
|
+
]
|
37
|
+
s.homepage = %q{http://github.com/minter/raccdoc}
|
38
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
39
|
+
s.require_paths = ["lib"]
|
40
|
+
s.rubygems_version = %q{1.3.5}
|
41
|
+
s.summary = %q{Ruby interface into the Raccdoc BBS API}
|
42
|
+
s.test_files = [
|
43
|
+
"test/raccdoc_connection_test.rb",
|
44
|
+
"test/raccdoc_forum_test.rb",
|
45
|
+
"test/raccdoc_post_test.rb",
|
46
|
+
"test/test_helper.rb"
|
47
|
+
]
|
48
|
+
|
49
|
+
if s.respond_to? :specification_version then
|
50
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
51
|
+
s.specification_version = 3
|
52
|
+
|
53
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
54
|
+
else
|
55
|
+
end
|
56
|
+
else
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
class RaccdocConnectionTest < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
@socket = mock()
|
6
|
+
@socket.stubs(:puts)
|
7
|
+
@socket.expects(:readline).returns("200 Server started seed:Ci0B2YeMio8ftYjElTa+EQ==")
|
8
|
+
TCPSocket.expects(:new).returns(@socket)
|
9
|
+
@bbs = Raccdoc::Connection.new(:host => 'test.bbs.example')
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_valid_anonymous_connection
|
13
|
+
assert_equal @bbs.class, Raccdoc::Connection
|
14
|
+
assert_equal @bbs.host, "test.bbs.example"
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_connection_refused_anonymous
|
18
|
+
TCPSocket.expects(:new).with('test.bbs.example', '6145').raises(Errno::ECONNREFUSED)
|
19
|
+
assert_raise(ConnectionError, 'Could not connect to test.bbs.example, port 6145') do
|
20
|
+
bbs = Raccdoc::Connection.new(:host => 'test.bbs.example')
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_connection_bad_login
|
25
|
+
socket = mock()
|
26
|
+
socket.stubs(:readline).returns("200 Server started seed:Ci0B2YeMio8ftYjElTa+EQ==").then.returns("405 Cannot process login Invalid password")
|
27
|
+
socket.stubs(:puts)
|
28
|
+
TCPSocket.expects(:new).returns(socket)
|
29
|
+
assert_raise(InvalidLogin, '404 User does not exist') do
|
30
|
+
bbs = Raccdoc::Connection.new(:host => 'test.bbs.example', :user => 'Bugcrusher', :password => 'BadPassword')
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_connection_that_returns_unknown_status
|
35
|
+
socket = mock()
|
36
|
+
TCPSocket.expects(:new).returns(socket)
|
37
|
+
socket.expects(:readline).returns("999 Stuff is messed up")
|
38
|
+
assert_raise(ConnectionError) do
|
39
|
+
bbs = Raccdoc::Connection.new(:host => 'test.bbs.example')
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_logout_succeeds
|
44
|
+
@socket.stubs(:close)
|
45
|
+
@socket.stubs(:closed?).returns(false)
|
46
|
+
@socket.stubs(:readline).returns("201 Session ended")
|
47
|
+
result = @bbs.logout
|
48
|
+
assert_equal result, true
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_logout_fails
|
52
|
+
@socket.stubs(:close)
|
53
|
+
@socket.stubs(:closed?).returns(false)
|
54
|
+
@socket.stubs(:readline).returns("999 Something Broke")
|
55
|
+
assert_raise(ConnectionError) do
|
56
|
+
@bbs.logout
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_connected
|
61
|
+
@socket.stubs(:puts)
|
62
|
+
@socket.stubs(:closed?).returns(false)
|
63
|
+
@socket.stubs(:readline).returns("999 Something Broke")
|
64
|
+
assert_equal @bbs.connected?, true
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_forums_success
|
68
|
+
@socket.stubs(:readline).returns("301 Topic list follows").then.returns("topic:0 name:Lobby lastnote:2331 flags:nosubject,sparse admin:acct578247-oldisca/Elvis/(hidden)").then.returns(".")
|
69
|
+
forums = @bbs.forums
|
70
|
+
assert_equal forums.class, Hash
|
71
|
+
assert_equal forums.size, 1
|
72
|
+
assert_equal forums[0][:name], "Lobby"
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_forums_failure
|
76
|
+
@socket.stubs(:readline).returns("999 Stuff Broke")
|
77
|
+
assert_raise(ResponseError) do
|
78
|
+
forums = @bbs.forums
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_jump
|
83
|
+
@forum = mock()
|
84
|
+
@forum.expects(:name).returns("Lobby")
|
85
|
+
Raccdoc::Forum.expects(:new).returns(@forum)
|
86
|
+
forum = @bbs.jump(0)
|
87
|
+
assert_equal forum.name, "Lobby"
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,167 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
class RaccdocForumTest < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
@socket = mock()
|
6
|
+
@socket.stubs(:puts)
|
7
|
+
@socket.stubs(:readline).returns("204 Topic set to: topic:0 name:Lobby lastnote:2331 flags:nosubject,sparse admin:acct578247-oldisca/Elvis/(hidden) firstnote:2275")
|
8
|
+
@forum = Raccdoc::Forum.new(@socket, 0)
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_creation
|
12
|
+
assert_equal @forum.class, Raccdoc::Forum
|
13
|
+
assert_equal @forum.name, "Lobby"
|
14
|
+
assert_equal @forum.admin, "Elvis"
|
15
|
+
assert_equal @forum.id, "0"
|
16
|
+
assert_equal @forum.anonymous, false
|
17
|
+
assert_equal @forum.private, false
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_creation_fail
|
21
|
+
socket = mock()
|
22
|
+
socket.stubs(:puts)
|
23
|
+
socket.stubs(:readline).returns('999 Stuff broke')
|
24
|
+
assert_raise(ResponseError) do
|
25
|
+
forum = Raccdoc::Forum.new(socket,0)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_anonymous_allowed
|
30
|
+
socket = mock()
|
31
|
+
socket.stubs(:puts)
|
32
|
+
socket.stubs(:readline).returns("204 Topic set to: topic:173 name:Kama Sutra lastnote:57263 flags:nosubject,sparse,private,cananonymous admin:acct30177-oldisca/Kubla Khan/(hidden) firstnote:57111")
|
33
|
+
forum = Raccdoc::Forum.new(socket, 173)
|
34
|
+
assert_equal forum.anonymous, true
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_anonymous_forced
|
38
|
+
socket = mock()
|
39
|
+
socket.stubs(:puts)
|
40
|
+
socket.stubs(:readline).returns("204 Topic set to: topic:7 name:Weird Soup lastnote:864957 flags:nosubject,sparse,forceanonymous admin:acct576222-oldisca/Weird FM/(hidden) firstnote:864808")
|
41
|
+
forum = Raccdoc::Forum.new(socket,7)
|
42
|
+
assert_equal forum.anonymous, "force"
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_noteids
|
46
|
+
@socket.stubs(:readline).returns("306 Note headers follow").then.returns("noteno:2275").then.returns("noteno:2280").then.returns(".")
|
47
|
+
notes = @forum.noteids
|
48
|
+
assert_equal notes.class, Array
|
49
|
+
assert_equal notes.size, 2
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_noteids_failure
|
53
|
+
@socket.stubs(:readline).returns("999 Stuff Broke")
|
54
|
+
assert_raise(ResponseError) do
|
55
|
+
notes = @forum.noteids
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_forum_information
|
60
|
+
@socket.stubs(:readline).returns("303 Topic info follows").then.returns("From: Adonis").then.returns("Date: Fri, 30 May 2003 02:05:00 GMT").then.returns("").then.returns("This forum is where the Sysops post administrative announcements...").then.returns(".")
|
61
|
+
fi = @forum.forum_information
|
62
|
+
assert_equal fi[:from], "Adonis"
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_forum_information_failure
|
66
|
+
@socket.stubs(:readline).returns("999 Stuff broke")
|
67
|
+
assert_raise(ResponseError) do
|
68
|
+
fi = @forum.forum_information
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_set_first_unread
|
73
|
+
@socket.stubs(:readline).returns('206 Value set')
|
74
|
+
@forum.first_unread=2139
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_set_first_unread_fails
|
78
|
+
@socket.stubs(:readline).returns('999 Stuff Broke')
|
79
|
+
assert_raises(ResponseError) do
|
80
|
+
@forum.first_unread=99999
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_first_unread
|
85
|
+
@socket.stubs(:readline).returns("207 RC Value is: 2281")
|
86
|
+
first = @forum.first_unread
|
87
|
+
assert_equal first, "2281"
|
88
|
+
end
|
89
|
+
|
90
|
+
def test_first_unread_fails
|
91
|
+
@socket.stubs(:readline).returns("999 Stuff Broke")
|
92
|
+
assert_raises(ResponseError) do
|
93
|
+
first = @forum.first_unread
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_post_headers
|
98
|
+
@socket.stubs(:readline).returns("306 Note headers follow").then.returns("noteno:2275 formal-author:acct89144-oldisca/Devil Lady/(hidden) date:Sat, 16 Jun 2007 00:11:00 GMT subject:Have you ever wanted to give a little back? ISCA is size:670").then.returns("noteno:2280 formal-author:acct493910-oldisca/Bleeding Me/(hidden) date:Sat, 13 Oct 2007 18:28:00 GMT subject:Do you have an idea for a permanent forum but aren't sure size:465").then.returns(".")
|
99
|
+
headers = @forum.post_headers
|
100
|
+
assert_equal headers.class, Hash
|
101
|
+
assert_equal headers.size, 2
|
102
|
+
post = headers['2275']
|
103
|
+
assert_equal post.class, Hash
|
104
|
+
assert_equal post[:author], 'Devil Lady'
|
105
|
+
assert_equal post[:size], '670'
|
106
|
+
assert_equal post[:noteno], '2275'
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_post_headers_fail
|
110
|
+
@socket.stubs(:readline).returns("999 Stuff broke")
|
111
|
+
assert_raise(ResponseError) do
|
112
|
+
headers = @forum.post_headers
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def test_read
|
117
|
+
@socket.stubs(:readline).returns("302 Note body follows noteno:71493 size:66").then.returns("From: Lunenburg").then.returns("Formal-Name: acct123507-oldisca/Lunenburg/(hidden)").then.returns("Date: Wed, 06 Jun 2007 01:02:00 GMT").then.returns("").then.returns("Testing to see if posting via the ISCAweb interface still works.").then.returns(".")
|
118
|
+
post = @forum.read("9999")
|
119
|
+
assert_equal post.class, Raccdoc::Post
|
120
|
+
end
|
121
|
+
|
122
|
+
def test_read_dammit
|
123
|
+
@socket.stubs(:readline).returns("302 Note body follows noteno:71493 size:66").then.returns("From: Lunenburg").then.returns("Formal-Name: acct123507-oldisca/Lunenburg/(hidden)").then.returns("Date: Wed, 06 Jun 2007 01:02:00 GMT").then.returns("").then.returns("Testing to see if posting via the ISCAweb interface still works.").then.returns(".")
|
124
|
+
post = @forum.read!("9999")
|
125
|
+
assert_equal post.class, Raccdoc::Post
|
126
|
+
end
|
127
|
+
|
128
|
+
def test_create_post
|
129
|
+
@socket.stubs(:readline).returns('350 Send note body maxsize:50000').then.returns('205 Note posted as note: 71508').then.returns("302 Note body follows noteno:71493 size:66").then.returns("From: Lunenburg").then.returns("Formal-Name: acct123507-oldisca/Lunenburg/(hidden)").then.returns("Date: Wed, 06 Jun 2007 01:02:00 GMT").then.returns("").then.returns("This is a test").then.returns(".")
|
130
|
+
post = @forum.post("This is a test")
|
131
|
+
assert_equal post.class, Raccdoc::Post
|
132
|
+
assert_equal post.body, "This is a test"
|
133
|
+
end
|
134
|
+
|
135
|
+
def test_create_post_early_failure
|
136
|
+
@socket.stubs(:readline).returns('999 Stuff broke')
|
137
|
+
assert_raise(ResponseError) do
|
138
|
+
post = @forum.post("This is a test")
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def test_create_post_late_failure
|
143
|
+
@socket.stubs(:readline).returns('350 Send note body maxsize:50000').then.returns('999 Stuff broke')
|
144
|
+
assert_raise(ResponseError) do
|
145
|
+
post = @forum.post("This is a test")
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def test_okay_post
|
150
|
+
@socket.stubs(:readline).returns("211 Request okay")
|
151
|
+
assert_equal @forum.post?, true
|
152
|
+
end
|
153
|
+
|
154
|
+
def test_okay_post_denied
|
155
|
+
@socket.stubs(:readline).returns("408 Permission denied")
|
156
|
+
assert_equal @forum.post?, false
|
157
|
+
end
|
158
|
+
|
159
|
+
def test_okay_post_fail
|
160
|
+
@socket.stubs(:readline).returns("999 Stuff broke")
|
161
|
+
assert_raise(ResponseError) do
|
162
|
+
@forum.post?
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
|
167
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
class RaccdocPostTest < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
@socket = mock()
|
6
|
+
@socket.stubs(:puts)
|
7
|
+
@socket.stubs(:readline).returns("302 Note body follows noteno:71493 size:66").then.returns("From: Lunenburg").then.returns("Formal-Name: acct123507-oldisca/Lunenburg/(hidden)").then.returns("Date: Wed, 06 Jun 2007 01:02:00 GMT").then.returns("").then.returns("Testing to see if posting via the ISCAweb interface still works.").then.returns(".")
|
8
|
+
@post = Raccdoc::Post.new(@socket, 9999)
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_successful_post_object_creation
|
12
|
+
assert_equal @post.class, Raccdoc::Post
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_post_object_creation_failure
|
16
|
+
@socket.stubs(:readline).returns("410 No Such Note")
|
17
|
+
assert_raise(ResponseError) do
|
18
|
+
@post = Raccdoc::Post.new(@socket, 9999)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_delete_post
|
23
|
+
@socket.stubs(:readline).returns("208 Note deleted")
|
24
|
+
status = @post.delete
|
25
|
+
assert_equal status, true
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_delete_failure
|
29
|
+
@socket.stubs(:readline).returns("408 Permission denied")
|
30
|
+
status = @post.delete
|
31
|
+
assert_equal status, false
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_delete_exception
|
35
|
+
@socket.stubs(:readline).returns("999 Stuff Broke")
|
36
|
+
assert_raise(ResponseError) do
|
37
|
+
status = @post.delete
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_delete_permissions
|
42
|
+
@socket.stubs(:readline).returns("211 Request okay")
|
43
|
+
status = @post.delete?
|
44
|
+
assert_equal status, true
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_delete_permissions_failure
|
48
|
+
@socket.stubs(:readline).returns("408 Permission denied")
|
49
|
+
status = @post.delete?
|
50
|
+
assert_equal status, false
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_delete_permissions_exception
|
54
|
+
@socket.stubs(:readline).returns("999 Stuff broke")
|
55
|
+
assert_raise(ResponseError) do
|
56
|
+
status = @post.delete?
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: raccdoc
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.10
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- H. Wade Minter
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-10-08 00:00:00 -05:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: A Ruby way to speak the Raccdoc protocol
|
17
|
+
email: minter@lunenburg.org
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- LICENSE
|
24
|
+
- README.rdoc
|
25
|
+
files:
|
26
|
+
- .gitignore
|
27
|
+
- CHANGELOG
|
28
|
+
- LICENSE
|
29
|
+
- Manifest
|
30
|
+
- README.rdoc
|
31
|
+
- Rakefile
|
32
|
+
- VERSION
|
33
|
+
- lib/raccdoc.rb
|
34
|
+
- lib/raccdoc/connection.rb
|
35
|
+
- lib/raccdoc/forum.rb
|
36
|
+
- lib/raccdoc/post.rb
|
37
|
+
- raccdoc.gemspec
|
38
|
+
- test/raccdoc_connection_test.rb
|
39
|
+
- test/raccdoc_forum_test.rb
|
40
|
+
- test/raccdoc_post_test.rb
|
41
|
+
- test/test_helper.rb
|
42
|
+
has_rdoc: true
|
43
|
+
homepage: http://github.com/minter/raccdoc
|
44
|
+
licenses: []
|
45
|
+
|
46
|
+
post_install_message:
|
47
|
+
rdoc_options:
|
48
|
+
- --charset=UTF-8
|
49
|
+
require_paths:
|
50
|
+
- lib
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: "0"
|
56
|
+
version:
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: "0"
|
62
|
+
version:
|
63
|
+
requirements: []
|
64
|
+
|
65
|
+
rubyforge_project:
|
66
|
+
rubygems_version: 1.3.5
|
67
|
+
signing_key:
|
68
|
+
specification_version: 3
|
69
|
+
summary: Ruby interface into the Raccdoc BBS API
|
70
|
+
test_files:
|
71
|
+
- test/raccdoc_connection_test.rb
|
72
|
+
- test/raccdoc_forum_test.rb
|
73
|
+
- test/raccdoc_post_test.rb
|
74
|
+
- test/test_helper.rb
|