minter-raccdoc 0.0.4 → 0.0.5

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/CHANGELOG CHANGED
@@ -1,3 +1,5 @@
1
+ v0.0.5 Full test coverage, bug fixes
2
+
1
3
  v0.0.4 Add readers for extra flags, add DAMMIT to post reading.
2
4
 
3
5
  v0.0.3 Documentation updates
data/Manifest CHANGED
@@ -1,9 +1,14 @@
1
1
  CHANGELOG
2
+ lib/raccdoc/connection.rb
3
+ lib/raccdoc/forum.rb
4
+ lib/raccdoc/post.rb
2
5
  lib/raccdoc.rb
3
6
  LICENSE
4
7
  Manifest
5
8
  raccdoc.gemspec
6
9
  Rakefile
7
- README
10
+ README.rdoc
8
11
  test/raccdoc_connection_test.rb
12
+ test/raccdoc_forum_test.rb
13
+ test/raccdoc_post_test.rb
9
14
  test/test_helper.rb
File without changes
@@ -0,0 +1,101 @@
1
+ module Raccdoc
2
+ class Connection
3
+
4
+ attr_reader :host
5
+
6
+ # Creates a new Raccdoc connection. By default, it connects to bbs.iscabbs.com, port 6145, which is the
7
+ # ISCABBS server.
8
+ #
9
+ # If a :user and :password is not supplied, a guest/anonymous login is done. Most, if not all, posting is
10
+ # restricted under this account.
11
+ #
12
+ # If the TCP connection fails, or the login is rejected, and exception is raised.
13
+ def initialize(param_args = {:host => 'bbs.iscabbs.com', :port => '6145', :user => nil, :password => nil})
14
+ args = {:host => 'bbs.iscabbs.com', :port => '6145', :user => nil, :password => nil }
15
+
16
+ args.merge!(param_args)
17
+ begin
18
+ @socket = TCPSocket.new(args[:host],args[:port])
19
+ rescue Errno::ECONNREFUSED
20
+ raise RuntimeError, "Could not connect to #{args[:host]}, port #{args[:port]}\n"
21
+ end
22
+
23
+ response = @socket.readline.chomp
24
+ unless response.match(/^2/)
25
+ raise RuntimeError, response
26
+ end
27
+
28
+ if (args[:user] && args[:password])
29
+ @socket.puts("LOGIN #{args[:user]}\t#{args[:password]}")
30
+ response = @socket.readline.chomp
31
+ unless response.match(/^2/)
32
+ raise RuntimeError, response
33
+ end
34
+ end
35
+ @host = args[:host]
36
+ end
37
+
38
+ # Closes the Raccdoc connection, then closes the socket.
39
+ def logout
40
+ unless @socket.closed?
41
+ @socket.puts "QUIT"
42
+
43
+ response = @socket.readline.chomp
44
+ unless response.match(/^2/)
45
+ raise RuntimeError, response
46
+ end
47
+ @socket.close
48
+ end
49
+ return true
50
+ end
51
+
52
+ # Tests the TCP socket to make sure that it's still connected to the remote server.
53
+ def connected?
54
+ @socket.closed? ? false : true
55
+ end
56
+
57
+ # Sets the active forum on the server to the specified forum name or ID, and returns a new Raccdoc::Forum object.
58
+ #
59
+ # The forum can be specified by number (0), or name ("Lobby" or "Program")
60
+ def jump(forum = 0)
61
+ Raccdoc::Forum.new(@socket, forum.to_s)
62
+ end
63
+
64
+ # Returns a hash of forum information. The key is the forum ID number, and the value is a hash containing the following data:
65
+ #
66
+ # * name - The forum name
67
+ # * lastnote - The earliest post ID number in the forum
68
+ # * admin - The handle of the forum admin
69
+ #
70
+ # The argument can be one of: ALL (default), PUBLIC, PRIVATE, TODO, JOINED, NAMED, THREADS
71
+ # depending on server support
72
+ #
73
+ # 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.
74
+ def forums(type="all")
75
+ @socket.puts "LIST #{type}"
76
+ forums = Hash.new
77
+
78
+ response = @socket.readline.chomp
79
+ unless response.match(/^3/)
80
+ raise RuntimeError, response
81
+ end
82
+
83
+ while line = @socket.readline.chomp
84
+ break if line.match(/^\.$/)
85
+ tmp = Hash.new
86
+ line.split(/\t/).each do |pair|
87
+ (key, value) = pair.split(/:/)
88
+ if key == "admin"
89
+ value = value.split('/')[1]
90
+ end
91
+ tmp[key.to_sym] = value
92
+ end
93
+ id = tmp[:topic].to_i
94
+ forums[id] = tmp
95
+ end
96
+
97
+ return forums
98
+ end
99
+
100
+ end
101
+ end
@@ -0,0 +1,202 @@
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 RuntimeError, 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
+ def noteids
54
+ noteids = Array.new
55
+ @socket.puts("XHDR noteno")
56
+ response = @socket.readline.chomp
57
+ unless response.match(/^3/)
58
+ raise RuntimeError, response
59
+ end
60
+
61
+ while line = @socket.readline.chomp
62
+ break if line.match(/^\.$/)
63
+ (tag,noteid) = line.split(/:/)
64
+ noteids.push(noteid.to_i)
65
+ end
66
+
67
+ return noteids
68
+ end
69
+
70
+ # Returns a hash of the forum information. Keys are:
71
+ #
72
+ # * from - The username of the user who last updated the FI
73
+ # * date - The date the forum information was last updated (a Time object)
74
+ # * body - The actual text of the FI
75
+ def forum_information
76
+ fi = Hash.new
77
+ @socket.puts("SHOW info")
78
+ response = @socket.readline.chomp
79
+ unless response.match(/^3/)
80
+ raise RuntimeError, response
81
+ end
82
+
83
+ # Get header information
84
+ while line = @socket.readline.chomp
85
+ break if line.match(/^$/)
86
+ (key, value) = line.split(/: /)
87
+ if key.downcase == "date"
88
+ value = Time.parse(value)
89
+ end
90
+ fi[key.downcase.to_sym] = value
91
+ end
92
+
93
+ body = ""
94
+ while line = @socket.readline
95
+ break if line.match(/^\.$/)
96
+ body << line
97
+ end
98
+ fi[:body] = body
99
+
100
+ return fi
101
+ end
102
+
103
+ # Sets the current user's first-unread pointer to the specified number. Any posts greater than that number will be listed as unread.
104
+ def first_unread=(postid = 0)
105
+ @socket.puts("SETRC #{postid.to_s}")
106
+ response = @socket.readline.chomp
107
+ unless response.match(/^2/)
108
+ raise RuntimeError, response
109
+ end
110
+ end
111
+
112
+ # Returns the post ID number of the last read post in the forum. Any posts greater than that number can be considered unread.
113
+ def first_unread
114
+ @socket.puts("SHOW rcval")
115
+ response = @socket.readline.chomp
116
+ unless response.match(/^2/)
117
+ raise RuntimeError, response
118
+ end
119
+ response.match(/\d+.*?:\s+(\d+)/)
120
+ return $1
121
+ end
122
+
123
+ # 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:
124
+ #
125
+ # * author - The username of the user who created the post
126
+ # * date - The date the post was created or, if the post is anonymous, the current date in UTC
127
+ # * subject - The first line of the post
128
+ # * size - The size of the post in bytes
129
+ # * authority - If the post was made with Sysop or Forum Manager status, this is set
130
+ #
131
+ # 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.
132
+ def post_headers(range = "")
133
+ @socket.puts("XHDR ALL #{range}")
134
+ response = @socket.readline.chomp
135
+ unless response.match(/^3/)
136
+ raise RuntimeError, response
137
+ end
138
+
139
+ posts = Hash.new
140
+
141
+ while (line = @socket.readline.chomp)
142
+ break if line.match(/^\.$/)
143
+ tmpdata = Hash.new
144
+ line.split(/\t/).each do |tuple|
145
+ (key, value) = tuple.split(/:/, 2)
146
+ tmpdata[key.downcase.to_sym] = value
147
+ end
148
+ tmpdata[:date] = tmpdata[:date] ? Time.parse(tmpdata[:date]) : Time.new.getgm
149
+ tmpdata[:author] = tmpdata[:"formal-author"] ? tmpdata[:"formal-author"].split('/')[1] : 'Anonymous'
150
+ posts[tmpdata[:noteno]] = tmpdata
151
+ end
152
+ return posts
153
+ end
154
+
155
+ # Returns a new Raccdoc::Post object for the specified post ID in the current forum.
156
+ def read(postid)
157
+ Post.new(@socket,postid)
158
+ end
159
+
160
+ # 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.
161
+ def read!(postid)
162
+ Post.new(@socket,postid,:dammit => true)
163
+ end
164
+
165
+ # Creates a new post in the current forum using the text provided in the argument.
166
+ #
167
+ # Returns a new Raccdoc::Post object that results from reading the newly created post.
168
+ def post(body)
169
+ @socket.puts("POST")
170
+ response = @socket.readline.chomp
171
+ unless response.match(/^3/)
172
+ raise RuntimeError, response
173
+ end
174
+
175
+ @socket.puts(body)
176
+ @socket.puts(".")
177
+
178
+ response = @socket.readline.chomp
179
+ if response.match(/^2/)
180
+ postid = response.split(/:\s+/)[1]
181
+ return Post.new(@socket,postid)
182
+ else
183
+ raise RuntimeError, response
184
+ end
185
+ end
186
+
187
+ # Checks to see if the currently logged in user has permission to post in the current forum.
188
+ #
189
+ # Returns true or false.
190
+ def post?
191
+ @socket.puts("OKAY POST")
192
+ response = @socket.readline.chomp
193
+ if response.match(/^2/)
194
+ return true
195
+ elsif response.match(/^4/)
196
+ return false
197
+ else
198
+ raise RuntimeError, response
199
+ end
200
+ end
201
+ end
202
+ end
@@ -0,0 +1,84 @@
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 RuntimeError, 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
+ post[:author] = post[:'formal-author'] ? post[:'formal-author'].split('/')[1] : 'Anonymous'
39
+
40
+ post[:body] = ""
41
+ while (line = @socket.readline)
42
+ break if line.match(/^\.$/)
43
+ post[:body] << line
44
+ end
45
+
46
+ @id = post[:id]
47
+ @date = post[:date]
48
+ @author = post[:author]
49
+ @body = post[:body]
50
+ @authority = post[:authority]
51
+ return post
52
+ end
53
+
54
+ # Checks to see if the currently-logged-in user has permission to delete the current post.
55
+ #
56
+ # Returns true or false
57
+ def delete?
58
+ @socket.puts("OKAY DELETE NOTE #{self.id}")
59
+ response = @socket.readline.chomp
60
+ if response.match(/^2/)
61
+ return true
62
+ elsif response.match(/^4/)
63
+ return false
64
+ else
65
+ raise RuntimeError, response
66
+ end
67
+ end
68
+
69
+ # Attempts to delete the current post.
70
+ #
71
+ # Returns true or false
72
+ def delete
73
+ @socket.puts("DELETE NOTE #{self.id}")
74
+ response = @socket.readline.chomp
75
+ if response.match(/^2/)
76
+ return true
77
+ elsif response.match(/^4/)
78
+ return false
79
+ else
80
+ raise RuntimeError, response
81
+ end
82
+ end
83
+ end
84
+ end
data/lib/raccdoc.rb CHANGED
@@ -1,389 +1,12 @@
1
1
  module Raccdoc
2
2
 
3
- VERSION = '0.0.4'
3
+ VERSION = '0.0.5'
4
4
  require 'socket'
5
5
  require 'time'
6
-
7
- class Connection
8
-
9
- attr_reader :host
10
-
11
- # Creates a new Raccdoc connection. By default, it connects to bbs.iscabbs.com, port 6145, which is the
12
- # ISCABBS server.
13
- #
14
- # If a :user and :password is not supplied, a guest/anonymous login is done. Most, if not all, posting is
15
- # restricted under this account.
16
- #
17
- # If the TCP connection fails, or the login is rejected, and exception is raised.
18
- def initialize(param_args = {:host => 'bbs.iscabbs.com', :port => '6145', :user => nil, :password => nil})
19
- args = {:host => 'bbs.iscabbs.com', :port => '6145', :user => nil, :password => nil }
20
-
21
- args.merge!(param_args)
22
- begin
23
- @socket = TCPSocket.new(args[:host],args[:port])
24
- rescue Errno::ECONNREFUSED
25
- raise RuntimeError, "Could not connect to #{args[:host]}, port #{args[:port]}\n"
26
- end
27
-
28
- response = @socket.readline.chomp
29
- unless response.match(/^2/)
30
- raise RuntimeError, response
31
- end
32
-
33
- if (args[:user] && args[:password])
34
- @socket.puts("LOGIN #{args[:user]}\t#{args[:password]}")
35
- response = @socket.readline.chomp
36
- unless response.match(/^2/)
37
- raise RuntimeError, response
38
- end
39
- end
40
- @host = args[:host]
41
- end
42
-
43
- # Closes the Raccdoc connection, then closes the socket.
44
- def logout
45
- unless @socket.closed?
46
- @socket.puts "QUIT"
47
-
48
- response = @socket.readline.chomp
49
- unless response.match(/^2/)
50
- raise RuntimeError, response
51
- end
52
- @socket.close
53
- end
54
- end
55
-
56
- # Tests the TCP socket to make sure that it's still connected to the remote server.
57
- def connected?
58
- @socket.closed? ? false : true
59
- end
60
-
61
- # Sets the active forum on the server to the specified forum name or ID, and returns a new Raccdoc::Forum object.
62
- #
63
- # The forum can be specified by number (0), or name ("Lobby" or "Program")
64
- def jump(forum = 0)
65
- Raccdoc::Forum.new(@socket, forum.to_s)
66
- end
67
-
68
- # Returns a hash of forum information. The key is the forum ID number, and the value is a hash containing the following data:
69
- #
70
- # * name - The forum name
71
- # * lastnote - The earliest post ID number in the forum
72
- # * admin - The handle of the forum admin
73
- #
74
- # The argument can be one of: ALL (default), PUBLIC, PRIVATE, TODO, JOINED, NAMED, THREADS
75
- # depending on server support
76
- #
77
- # 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.
78
- def forums(type="all")
79
- @socket.puts "LIST #{type}"
80
- forums = Hash.new
81
-
82
- response = @socket.readline.chomp
83
- unless response.match(/^3/)
84
- raise RuntimeError, response
85
- end
86
-
87
- while line = @socket.readline.chomp
88
- break if line.match(/^\.$/)
89
- tmp = Hash.new
90
- line.split(/\t/).each do |pair|
91
- (key, value) = pair.split(/:/)
92
- if key == "admin"
93
- value = value.split('/')[1]
94
- end
95
- tmp[key.to_sym] = value
96
- end
97
- id = tmp[:topic].to_i
98
- forums[id] = tmp
99
- end
100
-
101
- return forums
102
- end
103
-
104
- end
105
6
 
106
- class Forum
107
-
108
- attr_reader :id
109
- attr_reader :name
110
- attr_reader :admin
111
- attr_reader :anonymous
112
- attr_reader :private
113
-
114
- # Sets the active forum on the server to the specified forum number (0) or name (Lobby, Program), then returns a new Raccdoc::Forum object.
115
- #
116
- # There are five instance variables exposed through attr_reader:
117
- #
118
- # * id - The forum ID number
119
- # * name - The forum name
120
- # * admin - The name of the forum administrator
121
- # * anonymous - either true, false, or force, depending on whether the anonymous posting option is on, off, or required
122
- # * private - True if the forum is private (invite-only), false otherwise
123
- def initialize(socket, forum = 0)
124
- @socket = socket
125
- @socket.puts "TOPIC #{forum.to_s}"
126
- response = @socket.readline.chomp
127
- unless response.match(/^2/)
128
- raise RuntimeError, response
129
- end
130
-
131
- @headers = {}
132
- tuples = response.split(/\t/)
133
- tuples.delete_at(0)
134
- tuples.each do |pair|
135
- (key, value) = pair.split(/:/)
136
- @headers[key.downcase.to_sym] = value
137
- end
138
-
139
- flags = @headers[:flags].split('/')
140
-
141
- if flags.include?("forceanonymous")
142
- @anonymous = "force"
143
- elsif flags.include?("cananonymous")
144
- @anonymous = true
145
- else
146
- @anonymous = false
147
- end
148
-
149
- @private = flags.include?("private") ? true : false
150
-
151
- @id = @headers[:topic]
152
- @name = @headers[:name]
153
- @admin = @headers[:admin]
154
- end
155
-
156
- # Returns an array of all current post IDs in the forum.
157
- def noteids
158
- noteids = Array.new
159
- @socket.puts("XHDR noteno")
160
- response = @socket.readline.chomp
161
- unless response.match(/^3/)
162
- raise RuntimeError, response
163
- end
164
-
165
- while line = @socket.readline.chomp
166
- break if line.match(/^\.$/)
167
- (tag,noteid) = line.split(/:/)
168
- noteids.push(noteid.to_i)
169
- end
170
-
171
- return noteids
172
- end
173
-
174
- # Returns a hash of the forum information. Keys are:
175
- #
176
- # * from - The username of the user who last updated the FI
177
- # * date - The date the forum information was last updated (a Time object)
178
- # * body - The actual text of the FI
179
- def forum_information
180
- fi = Hash.new
181
- @socket.puts("SHOW info")
182
- response = @socket.readline.chomp
183
- unless response.match(/^3/)
184
- raise RuntimeError, response
185
- end
186
-
187
- # Get header information
188
- while line = @socket.readline.chomp
189
- break if line.match(/^$/)
190
- (key, value) = line.split(/: /)
191
- if key.downcase == "date"
192
- value = Time.parse(value)
193
- end
194
- fi[key.downcase.to_sym] = value
195
- end
196
-
197
- body = ""
198
- while line = @socket.readline
199
- break if line.match(/^\.$/)
200
- body << line
201
- end
202
- fi[:body] = body
203
-
204
- return fi
205
- end
206
-
207
- # Sets the current user's first-unread pointer to the specified number. Any posts greater than that number will be listed as unread.
208
- def first_unread=(postid = 0)
209
- @socket.puts("SETRC #{postid.to_s}")
210
- response = @socket.readline.chomp
211
- unless response.match(/^2/)
212
- raise RuntimeError, response
213
- end
214
- end
215
-
216
- # Returns the post ID number of the last read post in the forum. Any posts greater than that number can be considered unread.
217
- def first_unread
218
- @socket.puts("SHOW rcval")
219
- response = @socket.readline.chomp
220
- unless response.match(/^2/)
221
- raise RuntimeError, response
222
- end
223
- response.match(/\d+.*?:\s+(\d+)/)
224
- return $1
225
- end
226
-
227
- # 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:
228
- #
229
- # * author - The username of the user who created the post
230
- # * date - The date the post was created or, if the post is anonymous, the current date in UTC
231
- # * subject - The first line of the post
232
- # * size - The size of the post in bytes
233
- # * authority - If the post was made with Sysop or Forum Manager status, this is set
234
- #
235
- # 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.
236
- def post_headers(range = "")
237
- @socket.puts("XHDR ALL #{range}")
238
- response = @socket.readline.chomp
239
- unless response.match(/^3/)
240
- raise RuntimeError, response
241
- end
242
-
243
- posts = Hash.new
244
-
245
- while (line = @socket.readline.chomp)
246
- break if line.match(/^\.$/)
247
- tmpdata = Hash.new
248
- line.split(/\t/).each do |tuple|
249
- (key, value) = tuple.split(/:/, 2)
250
- tmpdata[key.downcase.to_sym] = value
251
- end
252
- tmpdata[:date] = tmpdata[:date] ? Time.parse(tmpdata[:date]) : Time.new.getgm
253
- tmpdata[:author] = tmpdata[:"formal-author"] ? tmpdata[:"formal-author"].split('/')[1] : 'Anonymous'
254
- posts[tmpdata[:noteno]] = tmpdata
255
- end
256
- return posts
257
- end
258
-
259
- # Returns a new Raccdoc::Post object for the specified post ID in the current forum.
260
- def read(postid)
261
- Post.new(@socket,postid)
262
- end
263
-
264
- # 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.
265
- def read!(postid)
266
- Post.new(@socket,postid,:dammit => true)
267
- end
268
-
269
- # Creates a new post in the current forum using the text provided in the argument.
270
- #
271
- # Returns a new Raccdoc::Post object that results from reading the newly created post.
272
- def post(body)
273
- @socket.puts("POST")
274
- response = @socket.readline.chomp
275
- unless response.match(/^3/)
276
- raise RuntimeError, response
277
- end
278
-
279
- @socket.puts(body)
280
- @socket.puts(".")
281
-
282
- response = @socket.readline.chomp
283
- if response.match(/^2/)
284
- postid = response.split(/:\s+/)[1]
285
- return Post.new(@socket,postid)
286
- else
287
- raise RuntimeError, response
288
- end
289
- end
290
-
291
- # Checks to see if the currently logged in user has permission to post in the current forum.
292
- #
293
- # Returns true or false.
294
- def post?
295
- @socket.puts("OKAY POST")
296
- response = @socket.readline.chomp
297
- if response.match(/^2/)
298
- return true
299
- elsif response.match(/^4/)
300
- return false
301
- else
302
- raise RuntimeError, response
303
- end
304
- end
305
- end
306
-
307
- class Post
308
-
309
- attr_reader :id
310
- attr_reader :date
311
- attr_reader :author
312
- attr_reader :body
313
- attr_reader :authority
314
-
315
- # Creates a new Raccdoc::Post object by reading the specified post. If no post is given, it returns the first unread post, if any.
316
- #
317
- # Useful information returned:
318
- #
319
- # * id - The numerical post ID
320
- # * date - The date the post was made (or the current date in UTC if the post is anonymous)
321
- # * author - The username of the user that created the post
322
- # * body - The textual body of the post.
323
- # * authority - either "Sysop" or "Forum Moderator" if the post was made with that header.
324
- def initialize(socket,postid,args={})
325
- @socket = socket
326
- dammit = args[:dammit] ? '' : 'DAMMIT'
327
- @socket.puts "READ #{postid.to_s} #{dammit}"
328
- response = @socket.readline.chomp
329
- unless response.match(/^3/)
330
- raise RuntimeError, response
331
- end
332
-
333
- # Get header information
334
- post = Hash.new
335
-
336
- while line = @socket.readline.chomp
337
- break if line.match(/^$/)
338
- (key,value) = line.split(/: /)
339
- post[key.downcase.to_sym] = value
340
- end
341
- post[:id] = postid
342
- post[:date] = post[:date] ? Time.parse(post[:date]) : Time.new.getgm
343
- post[:author] = post[:'formal-author'] ? post[:'formal-author'].split('/')[1] : 'Anonymous'
7
+ $:.unshift(File.dirname(__FILE__))
8
+ require 'raccdoc/connection'
9
+ require 'raccdoc/forum'
10
+ require 'raccdoc/post'
344
11
 
345
- post[:body] = ""
346
- while (line = @socket.readline)
347
- break if line.match(/^\.$/)
348
- post[:body] << line
349
- end
350
-
351
- @id = post[:id]
352
- @date = post[:date]
353
- @author = post[:author]
354
- @body = post[:body]
355
- @authority = post[:authority]
356
- return post
357
- end
358
-
359
- # Checks to see if the currently-logged-in user has permission to delete the current post.
360
- #
361
- # Returns true or false
362
- def delete?
363
- @socket.puts("OKAY DELETE NOTE #{self.id}")
364
- response = @socket.readline.chomp
365
- if response.match(/^2/)
366
- return true
367
- elsif response.match(/^4/)
368
- return false
369
- else
370
- raise RuntimeError, response
371
- end
372
- end
373
-
374
- # Attempts to delete the current post.
375
- #
376
- # Returns true or false
377
- def delete
378
- @socket.puts("DELETE NOTE #{self.id}")
379
- response = @socket.readline.chomp
380
- if response.match(/^2/)
381
- return true
382
- elsif response.match(/^4/)
383
- return false
384
- else response.match(/^2/)
385
- raise RuntimeError, response
386
- end
387
- end
388
- end
389
12
  end
data/raccdoc.gemspec CHANGED
@@ -2,23 +2,23 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{raccdoc}
5
- s.version = "0.0.4"
5
+ s.version = "0.0.5"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["H. Wade Minter"]
9
9
  s.date = %q{2009-01-13}
10
10
  s.description = %q{A Ruby interface into the Raccdoc protocol}
11
11
  s.email = %q{minter@lunenburg.org}
12
- s.extra_rdoc_files = ["CHANGELOG", "lib/raccdoc.rb", "LICENSE", "README"]
13
- s.files = ["CHANGELOG", "lib/raccdoc.rb", "LICENSE", "Manifest", "raccdoc.gemspec", "Rakefile", "README", "test/raccdoc_connection_test.rb", "test/test_helper.rb"]
12
+ s.extra_rdoc_files = ["CHANGELOG", "lib/raccdoc/connection.rb", "lib/raccdoc/forum.rb", "lib/raccdoc/post.rb", "lib/raccdoc.rb", "LICENSE", "README.rdoc"]
13
+ s.files = ["CHANGELOG", "lib/raccdoc/connection.rb", "lib/raccdoc/forum.rb", "lib/raccdoc/post.rb", "lib/raccdoc.rb", "LICENSE", "Manifest", "raccdoc.gemspec", "Rakefile", "README.rdoc", "test/raccdoc_connection_test.rb", "test/raccdoc_forum_test.rb", "test/raccdoc_post_test.rb", "test/test_helper.rb"]
14
14
  s.has_rdoc = true
15
15
  s.homepage = %q{}
16
- s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Raccdoc", "--main", "README"]
16
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Raccdoc", "--main", "README.rdoc"]
17
17
  s.require_paths = ["lib"]
18
18
  s.rubyforge_project = %q{raccdoc}
19
19
  s.rubygems_version = %q{1.3.1}
20
20
  s.summary = %q{A Ruby interface into the Raccdoc protocol}
21
- s.test_files = ["test/raccdoc_connection_test.rb", "test/test_helper.rb"]
21
+ s.test_files = ["test/raccdoc_connection_test.rb", "test/raccdoc_forum_test.rb", "test/raccdoc_post_test.rb", "test/test_helper.rb"]
22
22
 
23
23
  if s.respond_to? :specification_version then
24
24
  current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
@@ -3,14 +3,15 @@ require File.dirname(__FILE__) + '/test_helper'
3
3
  class RaccdocConnectionTest < Test::Unit::TestCase
4
4
  def setup
5
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')
6
10
  end
7
11
 
8
12
  def test_valid_anonymous_connection
9
- TCPSocket.expects(:new).returns(@socket)
10
- @socket.expects(:readline).returns("200 Server started seed:Ci0B2YeMio8ftYjElTa+EQ==")
11
- bbs = Raccdoc::Connection.new(:host => 'test.bbs.example')
12
- assert_equal bbs.class, Raccdoc::Connection
13
- assert_equal bbs.host, "test.bbs.example"
13
+ assert_equal @bbs.class, Raccdoc::Connection
14
+ assert_equal @bbs.host, "test.bbs.example"
14
15
  end
15
16
 
16
17
  def test_connection_refused_anonymous
@@ -40,66 +41,49 @@ class RaccdocConnectionTest < Test::Unit::TestCase
40
41
  end
41
42
 
42
43
  def test_logout_succeeds
43
- @socket.stubs(:puts)
44
44
  @socket.stubs(:close)
45
45
  @socket.stubs(:closed?).returns(false)
46
- TCPSocket.expects(:new).returns(@socket)
47
- @socket.stubs(:readline).returns("200 Server started seed:Ci0B2YeMio8ftYjElTa+EQ==").then.returns("201 Session ended")
48
- bbs = Raccdoc::Connection.new(:host => 'test.bbs.example')
49
- bbs.logout
46
+ @socket.stubs(:readline).returns("201 Session ended")
47
+ result = @bbs.logout
48
+ assert_equal result, true
50
49
  end
51
50
 
52
51
  def test_logout_fails
53
- @socket.stubs(:puts)
54
52
  @socket.stubs(:close)
55
53
  @socket.stubs(:closed?).returns(false)
56
- TCPSocket.expects(:new).returns(@socket)
57
- @socket.stubs(:readline).returns("200 Server started seed:Ci0B2YeMio8ftYjElTa+EQ==").then.returns("999 Something Broke")
58
- bbs = Raccdoc::Connection.new(:host => 'test.bbs.example')
54
+ @socket.stubs(:readline).returns("999 Something Broke")
59
55
  assert_raise(RuntimeError) do
60
- bbs.logout
56
+ @bbs.logout
61
57
  end
62
58
  end
63
59
 
64
60
  def test_connected
65
61
  @socket.stubs(:puts)
66
62
  @socket.stubs(:closed?).returns(false)
67
- TCPSocket.expects(:new).returns(@socket)
68
- @socket.stubs(:readline).returns("200 Server started seed:Ci0B2YeMio8ftYjElTa+EQ==").then.returns("999 Something Broke")
69
- bbs = Raccdoc::Connection.new(:host => 'test.bbs.example')
70
- assert_equal bbs.connected?, true
63
+ @socket.stubs(:readline).returns("999 Something Broke")
64
+ assert_equal @bbs.connected?, true
71
65
  end
72
66
 
73
67
  def test_forums_success
74
- @socket.stubs(:puts)
75
- TCPSocket.expects(:new).returns(@socket)
76
- @socket.stubs(:readline).returns("200 Server started seed:Ci0B2YeMio8ftYjElTa+EQ==").then.returns("301 Topic list follows").then.returns("topic:0 name:Lobby lastnote:2331 flags:nosubject,sparse admin:acct578247-oldisca/Elvis/(hidden)").then.returns(".")
77
- bbs = Raccdoc::Connection.new(:host => 'test.bbs.example')
78
- forums = bbs.forums
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
79
70
  assert_equal forums.class, Hash
80
71
  assert_equal forums.size, 1
81
72
  assert_equal forums[0][:name], "Lobby"
82
73
  end
83
74
 
84
75
  def test_forums_failure
85
- @socket.stubs(:puts)
86
- TCPSocket.expects(:new).returns(@socket)
87
- @socket.stubs(:readline).returns("200 Server started seed:Ci0B2YeMio8ftYjElTa+EQ==").then.returns("999 Stuff Broke")
88
- bbs = Raccdoc::Connection.new(:host => 'test.bbs.example')
76
+ @socket.stubs(:readline).returns("999 Stuff Broke")
89
77
  assert_raise(RuntimeError) do
90
- forums = bbs.forums
78
+ forums = @bbs.forums
91
79
  end
92
80
  end
93
81
 
94
82
  def test_jump
95
- @socket.stubs(:puts)
96
83
  @forum = mock()
97
84
  @forum.expects(:name).returns("Lobby")
98
- TCPSocket.expects(:new).returns(@socket)
99
85
  Raccdoc::Forum.expects(:new).returns(@forum)
100
- @socket.stubs(:readline).returns("200 Server started seed:Ci0B2YeMio8ftYjElTa+EQ==")
101
- bbs = Raccdoc::Connection.new(:host => 'test.bbs.example')
102
- forum = bbs.jump(0)
86
+ forum = @bbs.jump(0)
103
87
  assert_equal forum.name, "Lobby"
104
88
  end
105
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(RuntimeError) 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(RuntimeError) 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(RuntimeError) 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(RuntimeError) 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(RuntimeError) 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(RuntimeError) 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(RuntimeError) 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(RuntimeError) 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(RuntimeError) 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(RuntimeError) 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(RuntimeError) 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(RuntimeError) do
56
+ status = @post.delete?
57
+ end
58
+ end
59
+
60
+
61
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: minter-raccdoc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - H. Wade Minter
@@ -29,18 +29,26 @@ extensions: []
29
29
 
30
30
  extra_rdoc_files:
31
31
  - CHANGELOG
32
+ - lib/raccdoc/connection.rb
33
+ - lib/raccdoc/forum.rb
34
+ - lib/raccdoc/post.rb
32
35
  - lib/raccdoc.rb
33
36
  - LICENSE
34
- - README
37
+ - README.rdoc
35
38
  files:
36
39
  - CHANGELOG
40
+ - lib/raccdoc/connection.rb
41
+ - lib/raccdoc/forum.rb
42
+ - lib/raccdoc/post.rb
37
43
  - lib/raccdoc.rb
38
44
  - LICENSE
39
45
  - Manifest
40
46
  - raccdoc.gemspec
41
47
  - Rakefile
42
- - README
48
+ - README.rdoc
43
49
  - test/raccdoc_connection_test.rb
50
+ - test/raccdoc_forum_test.rb
51
+ - test/raccdoc_post_test.rb
44
52
  - test/test_helper.rb
45
53
  has_rdoc: true
46
54
  homepage: ""
@@ -51,7 +59,7 @@ rdoc_options:
51
59
  - --title
52
60
  - Raccdoc
53
61
  - --main
54
- - README
62
+ - README.rdoc
55
63
  require_paths:
56
64
  - lib
57
65
  required_ruby_version: !ruby/object:Gem::Requirement
@@ -75,4 +83,6 @@ specification_version: 2
75
83
  summary: A Ruby interface into the Raccdoc protocol
76
84
  test_files:
77
85
  - test/raccdoc_connection_test.rb
86
+ - test/raccdoc_forum_test.rb
87
+ - test/raccdoc_post_test.rb
78
88
  - test/test_helper.rb