raccdoc 0.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|