curl-multi 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +3 -0
- data/License.txt +676 -0
- data/Manifest.txt +25 -0
- data/README.txt +7 -0
- data/Rakefile +4 -0
- data/config/hoe.rb +71 -0
- data/config/requirements.rb +17 -0
- data/lib/curl-multi/version.rb +23 -0
- data/lib/curl-multi.rb +372 -0
- data/lib/uri-extensions.rb +34 -0
- data/log/debug.log +0 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/script/txt2html +74 -0
- data/setup.rb +1585 -0
- data/tasks/deployment.rake +34 -0
- data/tasks/environment.rake +7 -0
- data/tasks/website.rake +17 -0
- data/test/test_curl-multi.rb +34 -0
- data/test/test_helper.rb +2 -0
- data/website/index.html +128 -0
- data/website/index.txt +64 -0
- data/website/javascripts/rounded_corners_lite.inc.js +285 -0
- data/website/stylesheets/screen.css +138 -0
- data/website/template.rhtml +48 -0
- metadata +94 -0
data/Manifest.txt
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
History.txt
|
2
|
+
License.txt
|
3
|
+
Manifest.txt
|
4
|
+
README.txt
|
5
|
+
Rakefile
|
6
|
+
config/hoe.rb
|
7
|
+
config/requirements.rb
|
8
|
+
lib/curl-multi.rb
|
9
|
+
lib/curl-multi/version.rb
|
10
|
+
lib/uri-extensions.rb
|
11
|
+
log/debug.log
|
12
|
+
script/destroy
|
13
|
+
script/generate
|
14
|
+
script/txt2html
|
15
|
+
setup.rb
|
16
|
+
tasks/deployment.rake
|
17
|
+
tasks/environment.rake
|
18
|
+
tasks/website.rake
|
19
|
+
test/test_curl-multi.rb
|
20
|
+
test/test_helper.rb
|
21
|
+
website/index.html
|
22
|
+
website/index.txt
|
23
|
+
website/javascripts/rounded_corners_lite.inc.js
|
24
|
+
website/stylesheets/screen.css
|
25
|
+
website/template.rhtml
|
data/README.txt
ADDED
data/Rakefile
ADDED
data/config/hoe.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'curl-multi/version'
|
2
|
+
|
3
|
+
AUTHOR = ['Kristjan Petursson', 'Keith Rarick']
|
4
|
+
EMAIL = "kr@essembly.com"
|
5
|
+
DESCRIPTION = 'Ruby bindings for the libcurl multi interface'
|
6
|
+
GEM_NAME = 'curl-multi' # what ppl will type to install your gem
|
7
|
+
RUBYFORGE_PROJECT = 'curl-multi' # The unix name for your project
|
8
|
+
HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
|
9
|
+
DOWNLOAD_PATH = "http://rubyforge.org/projects/#{RUBYFORGE_PROJECT}"
|
10
|
+
|
11
|
+
@config_file = "~/.rubyforge/user-config.yml"
|
12
|
+
@config = nil
|
13
|
+
RUBYFORGE_USERNAME = "unknown"
|
14
|
+
def rubyforge_username
|
15
|
+
unless @config
|
16
|
+
begin
|
17
|
+
@config = YAML.load(File.read(File.expand_path(@config_file)))
|
18
|
+
rescue
|
19
|
+
puts <<-EOS
|
20
|
+
ERROR: No rubyforge config file found: #{@config_file}
|
21
|
+
Run 'rubyforge setup' to prepare your env for access to Rubyforge
|
22
|
+
- See http://newgem.rubyforge.org/rubyforge.html for more details
|
23
|
+
EOS
|
24
|
+
exit
|
25
|
+
end
|
26
|
+
end
|
27
|
+
RUBYFORGE_USERNAME.replace @config["username"]
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
REV = nil
|
32
|
+
# UNCOMMENT IF REQUIRED:
|
33
|
+
# REV = `svn info`.each {|line| if line =~ /^Revision:/ then k,v = line.split(': '); break v.chomp; else next; end} rescue nil
|
34
|
+
VERS = Curl::Multi::VERSION::STRING + (REV ? ".#{REV}" : "")
|
35
|
+
RDOC_OPTS = ['--quiet', '--title', 'curl-multi documentation',
|
36
|
+
"--opname", "index.html",
|
37
|
+
"--line-numbers",
|
38
|
+
"--main", "README",
|
39
|
+
"--inline-source"]
|
40
|
+
|
41
|
+
class Hoe
|
42
|
+
def extra_deps
|
43
|
+
@extra_deps.reject! { |x| Array(x).first == 'hoe' }
|
44
|
+
@extra_deps
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Generate all the Rake tasks
|
49
|
+
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
50
|
+
hoe = Hoe.new(GEM_NAME, VERS) do |p|
|
51
|
+
p.author = AUTHOR
|
52
|
+
p.description = DESCRIPTION
|
53
|
+
p.email = EMAIL
|
54
|
+
p.summary = DESCRIPTION
|
55
|
+
p.url = HOMEPATH
|
56
|
+
p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
|
57
|
+
p.test_globs = ["test/**/test_*.rb"]
|
58
|
+
p.clean_globs |= ['**/.*.sw?', '*.gem', '.config', '**/.DS_Store']
|
59
|
+
|
60
|
+
# == Optional
|
61
|
+
p.changes = p.paragraphs_of("History.txt", 0..1).join("\\n\\n")
|
62
|
+
p.extra_deps = [['RubyInline', '>= 3.6.2'], ['curb', '>= 0.1.2']]
|
63
|
+
|
64
|
+
#p.spec_extras = {} # A hash of extra values to set in the gemspec.
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
CHANGES = hoe.paragraphs_of('History.txt', 0..1).join("\\n\\n")
|
69
|
+
PATH = (RUBYFORGE_PROJECT == GEM_NAME) ? RUBYFORGE_PROJECT : "#{RUBYFORGE_PROJECT}/#{GEM_NAME}"
|
70
|
+
hoe.remote_rdoc_dir = File.join(PATH.gsub(/^#{RUBYFORGE_PROJECT}\/?/,''), 'rdoc')
|
71
|
+
hoe.rsync_args = '-av --delete --ignore-errors'
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
include FileUtils
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
%w[rake hoe newgem rubigen].each do |req_gem|
|
6
|
+
begin
|
7
|
+
require req_gem
|
8
|
+
rescue LoadError
|
9
|
+
puts "This Rakefile requires the '#{req_gem}' RubyGem."
|
10
|
+
puts "Installation: gem install #{req_gem} -y"
|
11
|
+
exit
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
$:.unshift(File.join(File.dirname(__FILE__), %w[.. lib]))
|
16
|
+
|
17
|
+
require 'curl-multi'
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# curl-multi - Ruby bindings for the libcurl multi interface
|
2
|
+
# Copyright (C) 2007 Philotic, Inc.
|
3
|
+
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
|
17
|
+
module Curl #:nodoc:
|
18
|
+
class Multi #:nodoc:
|
19
|
+
module VERSION #:nodoc:
|
20
|
+
STRING = '0.1'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/curl-multi.rb
ADDED
@@ -0,0 +1,372 @@
|
|
1
|
+
# curl-multi - Ruby bindings for the libcurl multi interface
|
2
|
+
# Copyright (C) 2007 Philotic, Inc.
|
3
|
+
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
|
17
|
+
$:.unshift File.dirname(__FILE__)
|
18
|
+
|
19
|
+
require 'rubygems'
|
20
|
+
require 'inline'
|
21
|
+
require 'uri-extensions'
|
22
|
+
require 'curb'
|
23
|
+
|
24
|
+
module Curl
|
25
|
+
class Error < RuntimeError
|
26
|
+
def initialize(code)
|
27
|
+
super("Curl error #{code}")
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class HTTPError < RuntimeError
|
32
|
+
def initialize(status)
|
33
|
+
super("Curl HTTP error #{status}")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class MultiError < RuntimeError
|
38
|
+
attr_reader :errors
|
39
|
+
def initialize(errors)
|
40
|
+
@errors = errors
|
41
|
+
super("Multiple errors: #{errors}")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# An instance of this class collects the response for each curl easy handle.
|
46
|
+
class Req
|
47
|
+
CURLE_OK = 0
|
48
|
+
|
49
|
+
attr_reader :url, :body
|
50
|
+
|
51
|
+
def done(code, status)
|
52
|
+
@done = true
|
53
|
+
if code != CURLE_OK
|
54
|
+
@error = Error.new(code)
|
55
|
+
elsif status != 200
|
56
|
+
@error = HTTPError.new(status)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def done?() @done end
|
61
|
+
|
62
|
+
def failed?() !!@error end
|
63
|
+
|
64
|
+
def do_success() @success.call(body) end
|
65
|
+
|
66
|
+
def do_failure() @failure.call(@error) end
|
67
|
+
|
68
|
+
def inspect() "{Curl::Req #{url}}" end
|
69
|
+
alias_method :to_s, :inspect
|
70
|
+
|
71
|
+
def initialize(url, success, failure)
|
72
|
+
@url = url
|
73
|
+
@success = success
|
74
|
+
@failure = failure
|
75
|
+
@body = ''
|
76
|
+
@done = false
|
77
|
+
@error = nil
|
78
|
+
end
|
79
|
+
|
80
|
+
def add_chunk(chunk)
|
81
|
+
@body = body + chunk
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
class Multi
|
86
|
+
def size() @handles.size end
|
87
|
+
|
88
|
+
def inspect() '{Curl::Multi' + @handles.map{|h| ' '+h.url}.join + '}' end
|
89
|
+
alias_method :to_s, :inspect
|
90
|
+
|
91
|
+
def get(url, success, failure=lambda{})
|
92
|
+
add(url, nil, success, failure)
|
93
|
+
end
|
94
|
+
|
95
|
+
def post(url, params, success, failure=lambda{})
|
96
|
+
add(url, URI.escape_params(params), success, failure)
|
97
|
+
end
|
98
|
+
|
99
|
+
def select(rfds, wfds)
|
100
|
+
loop do
|
101
|
+
ready_rfds, ready_wfds = c_select(rfds.map{|s|s.fileno},
|
102
|
+
wfds.map{|s|s.fileno})
|
103
|
+
work() # Curl may or may not have work to do, but we can't tell.
|
104
|
+
return ready_rfds, ready_wfds if ready_rfds.any? or ready_wfds.any?
|
105
|
+
return [], [] if rfds == [] and wfds == []
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Do as much work as possible without blocking on the network. That is,
|
110
|
+
# read as much data as is ready and write as much data as we have buffer
|
111
|
+
# space for. If any complete responses arrive, call their handlers.
|
112
|
+
def work
|
113
|
+
perform()
|
114
|
+
|
115
|
+
done, @handles = @handles.partition{|h| h.done?}
|
116
|
+
failed, done = done.partition{|h| h.failed?}
|
117
|
+
|
118
|
+
errors = []
|
119
|
+
errors += post_process(done) {|x| x.do_success}
|
120
|
+
errors += post_process(failed) {|x| x.do_failure}
|
121
|
+
raise errors[0] if errors.size == 1
|
122
|
+
raise MultiError.new(errors) if errors.size > 1
|
123
|
+
|
124
|
+
:ok
|
125
|
+
end
|
126
|
+
|
127
|
+
def cleanup
|
128
|
+
@handles = []
|
129
|
+
:ok
|
130
|
+
end
|
131
|
+
|
132
|
+
def initialize
|
133
|
+
@handles = []
|
134
|
+
end
|
135
|
+
|
136
|
+
# Add a URL to the queue of items to fetch
|
137
|
+
def add(url, body, success, failure=lambda{})
|
138
|
+
while (h = add_to_curl(Req.new(url, success, failure), url, body)) == nil
|
139
|
+
select([], [])
|
140
|
+
end
|
141
|
+
@handles << h
|
142
|
+
work()
|
143
|
+
:ok
|
144
|
+
end
|
145
|
+
|
146
|
+
def post_process(handles)
|
147
|
+
errors = []
|
148
|
+
handles.each do |h|
|
149
|
+
begin
|
150
|
+
yield(h) # This ought not to raise anything.
|
151
|
+
rescue => ex
|
152
|
+
errors << ex
|
153
|
+
end
|
154
|
+
end
|
155
|
+
return errors
|
156
|
+
end
|
157
|
+
|
158
|
+
inline do |builder|
|
159
|
+
builder.include('<errno.h>')
|
160
|
+
if File.exists?('/usr/include/curl/curl.h')
|
161
|
+
builder.include('"/usr/include/curl/curl.h"')
|
162
|
+
else
|
163
|
+
builder.include('"/usr/local/include/curl/curl.h"')
|
164
|
+
end
|
165
|
+
|
166
|
+
builder.prefix <<-end
|
167
|
+
#define GET_MULTI_HANDLE(name) \
|
168
|
+
CURLM *(name);\
|
169
|
+
Data_Get_Struct(self, CURLM, (name));\
|
170
|
+
if (!(name)) rb_raise(rb_eTypeError,\
|
171
|
+
"Expected initialized curl multi handle")
|
172
|
+
|
173
|
+
#define CHECK(e) do {\
|
174
|
+
if (!(e)) rb_raise(rb_eTypeError, "curl error");\
|
175
|
+
} while (0)
|
176
|
+
|
177
|
+
#define CHECKN(e) CHECK(!(e))
|
178
|
+
|
179
|
+
ID id_initialize, id_done, id_add_chunk, id_size;
|
180
|
+
end
|
181
|
+
|
182
|
+
builder.c_raw_singleton <<-end
|
183
|
+
void c_curl_multi_cleanup(void *x)
|
184
|
+
{
|
185
|
+
curl_multi_cleanup(x);
|
186
|
+
}
|
187
|
+
end
|
188
|
+
|
189
|
+
# Creates a new CurlMulti handle
|
190
|
+
builder.c_singleton <<-end
|
191
|
+
VALUE new() {
|
192
|
+
CURLcode r;
|
193
|
+
VALUE inst;
|
194
|
+
|
195
|
+
/* can't think of a better place to put this */
|
196
|
+
id_initialize = rb_intern("initialize");
|
197
|
+
id_done = rb_intern("done");
|
198
|
+
id_add_chunk = rb_intern("add_chunk");
|
199
|
+
id_size = rb_intern("size");
|
200
|
+
|
201
|
+
/* must be called at least once before any other curl functions */
|
202
|
+
r = curl_global_init(CURL_GLOBAL_ALL);
|
203
|
+
CHECKN(r);
|
204
|
+
|
205
|
+
inst = Data_Wrap_Struct(self, 0, c_curl_multi_cleanup, curl_multi_init());
|
206
|
+
rb_funcall(inst, id_initialize, 0);
|
207
|
+
return inst;
|
208
|
+
}
|
209
|
+
end
|
210
|
+
|
211
|
+
# We tell CurlEasy handles to write to this function in the constructor.
|
212
|
+
# The self argument is a parameter we set up with CURLOPT_WRITEDATA that
|
213
|
+
# lets us pass whatever data we'd like into the write fn.
|
214
|
+
builder.c_raw_singleton <<-end
|
215
|
+
uint c_add_chunk(char *chunk, uint size, uint nmemb, VALUE rb_req) {
|
216
|
+
uint bytes = size * nmemb; /* Number of bytes of data */
|
217
|
+
if (bytes == 0) return 0;
|
218
|
+
|
219
|
+
/* This is (rubyInstance, methodId, numArgs, arg1, arg2...) */
|
220
|
+
rb_funcall(rb_req, id_add_chunk, 1, rb_str_new(chunk, bytes));
|
221
|
+
return bytes;
|
222
|
+
}
|
223
|
+
end
|
224
|
+
|
225
|
+
# Adds an easy handle to the multi handle's list
|
226
|
+
builder.c <<-end
|
227
|
+
VALUE add_to_curl(VALUE rb_req, VALUE url, VALUE body) {
|
228
|
+
CURLMcode r;
|
229
|
+
GET_MULTI_HANDLE(multi_handle);
|
230
|
+
|
231
|
+
/* We start getting errors if we have too many open connections at
|
232
|
+
once, so make a hard limit. */
|
233
|
+
if (FIX2INT(rb_funcall(self, id_size, 0)) > 500) return Qnil;
|
234
|
+
|
235
|
+
CURL *easy_handle = curl_easy_init();
|
236
|
+
char *c_url = StringValuePtr(url);
|
237
|
+
|
238
|
+
/* Pass it the URL */
|
239
|
+
curl_easy_setopt(easy_handle, CURLOPT_URL, c_url);
|
240
|
+
|
241
|
+
/* GET or POST? */
|
242
|
+
if (body != Qnil) {
|
243
|
+
char *c_body = StringValuePtr(body);
|
244
|
+
uint body_sz = RSTRING(body)->len;
|
245
|
+
curl_easy_setopt(easy_handle, CURLOPT_POST, 1);
|
246
|
+
curl_easy_setopt(easy_handle, CURLOPT_POSTFIELDS, c_body);
|
247
|
+
curl_easy_setopt(easy_handle, CURLOPT_POSTFIELDSIZE, body_sz);
|
248
|
+
}
|
249
|
+
|
250
|
+
/* Tell curl to use our callbacks */
|
251
|
+
curl_easy_setopt(easy_handle, CURLOPT_WRITEFUNCTION, c_add_chunk);
|
252
|
+
|
253
|
+
/* Make curl give us a ruby pointer in the callbacks */
|
254
|
+
curl_easy_setopt(easy_handle, CURLOPT_WRITEDATA, rb_req);
|
255
|
+
curl_easy_setopt(easy_handle, CURLOPT_PRIVATE, rb_req);
|
256
|
+
|
257
|
+
/* Add it to the multi handle */
|
258
|
+
r = curl_multi_add_handle(multi_handle, easy_handle);
|
259
|
+
CHECKN(r);
|
260
|
+
|
261
|
+
return rb_req;
|
262
|
+
}
|
263
|
+
end
|
264
|
+
|
265
|
+
# Wait until one of the given fds or one of curl's fds is ready.
|
266
|
+
# Return two arrays of fds that are ready.
|
267
|
+
builder.c <<-end
|
268
|
+
void c_select(VALUE rfda, VALUE wfda) {
|
269
|
+
int i, r, n = -1;
|
270
|
+
long timeout;
|
271
|
+
fd_set rfds, wfds, efds;
|
272
|
+
CURLMcode cr;
|
273
|
+
VALUE ready_rfda, ready_wfda;
|
274
|
+
struct timeval tv = {0, 0};
|
275
|
+
GET_MULTI_HANDLE(multi_handle);
|
276
|
+
|
277
|
+
FD_ZERO(&rfds);
|
278
|
+
FD_ZERO(&wfds);
|
279
|
+
FD_ZERO(&efds);
|
280
|
+
|
281
|
+
/* Put curl's fds into the sets. */
|
282
|
+
cr = curl_multi_fdset(multi_handle, &rfds, &wfds, &efds, &n);
|
283
|
+
CHECKN(cr);
|
284
|
+
|
285
|
+
/* Put the given fds into the sets. */
|
286
|
+
for (i = 0; i < RARRAY(rfda)->len; i++) {
|
287
|
+
int fd = FIX2INT(RARRAY(rfda)->ptr[i]);
|
288
|
+
FD_SET(fd, &rfds);
|
289
|
+
n = n > fd ? n : fd;
|
290
|
+
}
|
291
|
+
for (i = 0; i < RARRAY(wfda)->len; i++) {
|
292
|
+
int fd = FIX2INT(RARRAY(wfda)->ptr[i]);
|
293
|
+
FD_SET(fd, &wfds);
|
294
|
+
n = n > fd ? n : fd;
|
295
|
+
}
|
296
|
+
|
297
|
+
cr = curl_multi_timeout(multi_handle, &timeout);
|
298
|
+
CHECKN(cr);
|
299
|
+
|
300
|
+
tv.tv_sec = timeout / 1000;
|
301
|
+
tv.tv_usec = (timeout * 1000) % 1000000;
|
302
|
+
|
303
|
+
/* Wait */
|
304
|
+
r = select(n + 1, &rfds, &wfds, &efds, (timeout < 0) ? NULL : &tv);
|
305
|
+
if (r < 0) rb_raise(rb_eRuntimeError, "select(): %s", sys_errlist[errno]);
|
306
|
+
|
307
|
+
ready_rfda = rb_ary_new();
|
308
|
+
ready_wfda = rb_ary_new();
|
309
|
+
|
310
|
+
/* Collect the fds that are ready */
|
311
|
+
for (i = 0; i < RARRAY(rfda)->len; i++) {
|
312
|
+
VALUE fd = FIX2INT(RARRAY(rfda)->ptr[i]);
|
313
|
+
if (FD_ISSET(fd, &rfds)) rb_ary_push(ready_rfda, INT2FIX(fd));
|
314
|
+
}
|
315
|
+
for (i = 0; i < RARRAY(wfda)->len; i++) {
|
316
|
+
VALUE fd = FIX2INT(RARRAY(wfda)->ptr[i]);
|
317
|
+
if (FD_ISSET(fd, &wfds)) rb_ary_push(ready_wfda, INT2FIX(fd));
|
318
|
+
}
|
319
|
+
|
320
|
+
return rb_ary_new3(2, ready_rfda, ready_wfda);
|
321
|
+
}
|
322
|
+
end
|
323
|
+
|
324
|
+
# Basically just a wrapper for curl_multi_perform().
|
325
|
+
builder.c <<-end
|
326
|
+
void perform() {
|
327
|
+
CURLMsg *msg;
|
328
|
+
CURLcode er;
|
329
|
+
CURLMcode r;
|
330
|
+
int status;
|
331
|
+
int running;
|
332
|
+
GET_MULTI_HANDLE(multi_handle);
|
333
|
+
|
334
|
+
/* do some work */
|
335
|
+
do {
|
336
|
+
r = curl_multi_perform(multi_handle, &running);
|
337
|
+
} while (r == CURLM_CALL_MULTI_PERFORM);
|
338
|
+
CHECK(r == CURLM_OK);
|
339
|
+
|
340
|
+
/* check which ones are done and mark them as done */
|
341
|
+
while ((msg = curl_multi_info_read(multi_handle, &r))) {
|
342
|
+
VALUE rb_req;
|
343
|
+
CURL *easy_handle;
|
344
|
+
|
345
|
+
if (msg->msg != CURLMSG_DONE) continue;
|
346
|
+
|
347
|
+
/* Save out the easy handle, because the msg struct becomes invalid
|
348
|
+
* whene we call curl_easy_cleanup, curl_multi_remove_handle, or
|
349
|
+
* curl_easy_cleanup. */
|
350
|
+
easy_handle = msg->easy_handle;
|
351
|
+
|
352
|
+
r = curl_easy_getinfo(easy_handle, CURLINFO_PRIVATE, &rb_req);
|
353
|
+
CHECKN(r);
|
354
|
+
|
355
|
+
er = curl_easy_getinfo(easy_handle, CURLINFO_RESPONSE_CODE,
|
356
|
+
&status);
|
357
|
+
CHECKN(er);
|
358
|
+
|
359
|
+
rb_funcall(rb_req, id_done, 2, INT2FIX(msg->data.result),
|
360
|
+
INT2FIX(status));
|
361
|
+
|
362
|
+
r = curl_multi_remove_handle(DATA_PTR(self), easy_handle);
|
363
|
+
CHECKN(r);
|
364
|
+
|
365
|
+
/* Free the handle */
|
366
|
+
curl_easy_cleanup(easy_handle);
|
367
|
+
}
|
368
|
+
}
|
369
|
+
end
|
370
|
+
end
|
371
|
+
end
|
372
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# curl-multi - Ruby bindings for the libcurl multi interface
|
2
|
+
# Copyright (C) 2007 Philotic, Inc.
|
3
|
+
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
|
17
|
+
require 'uri'
|
18
|
+
|
19
|
+
module URI
|
20
|
+
# Fully escape the web parameters
|
21
|
+
def self.fully_escape(uri)
|
22
|
+
self.escape(uri, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.escaped_params_array(params)
|
26
|
+
params.map{|k,v| fully_escape(k.to_s) + '=' + fully_escape(v.to_s)}
|
27
|
+
end
|
28
|
+
|
29
|
+
# This returns a URL-encoded string suitable for use as a POST body or for
|
30
|
+
# pasting into a URL.
|
31
|
+
def self.escape_params(params)
|
32
|
+
escaped_params_array(params).join('&')
|
33
|
+
end
|
34
|
+
end
|
data/log/debug.log
ADDED
File without changes
|
data/script/destroy
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.join(File.dirname(__FILE__), '..')
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/destroy'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Destroy.new.run(ARGV)
|
data/script/generate
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.join(File.dirname(__FILE__), '..')
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/generate'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Generate.new.run(ARGV)
|
data/script/txt2html
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
begin
|
5
|
+
require 'newgem'
|
6
|
+
rescue LoadError
|
7
|
+
puts "\n\nGenerating the website requires the newgem RubyGem"
|
8
|
+
puts "Install: gem install newgem\n\n"
|
9
|
+
exit(1)
|
10
|
+
end
|
11
|
+
require 'redcloth'
|
12
|
+
require 'syntax/convertors/html'
|
13
|
+
require 'erb'
|
14
|
+
require File.dirname(__FILE__) + '/../lib/curl-multi/version.rb'
|
15
|
+
|
16
|
+
version = Curl::Multi::VERSION::STRING
|
17
|
+
download = 'http://rubyforge.org/projects/curl-multi'
|
18
|
+
|
19
|
+
class Fixnum
|
20
|
+
def ordinal
|
21
|
+
# teens
|
22
|
+
return 'th' if (10..19).include?(self % 100)
|
23
|
+
# others
|
24
|
+
case self % 10
|
25
|
+
when 1: return 'st'
|
26
|
+
when 2: return 'nd'
|
27
|
+
when 3: return 'rd'
|
28
|
+
else return 'th'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class Time
|
34
|
+
def pretty
|
35
|
+
return "#{mday}#{mday.ordinal} #{strftime('%B')} #{year}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def convert_syntax(syntax, source)
|
40
|
+
return Syntax::Convertors::HTML.for_syntax(syntax).convert(source).gsub(%r!^<pre>|</pre>$!,'')
|
41
|
+
end
|
42
|
+
|
43
|
+
if ARGV.length >= 1
|
44
|
+
src, template = ARGV
|
45
|
+
template ||= File.join(File.dirname(__FILE__), '/../website/template.rhtml')
|
46
|
+
|
47
|
+
else
|
48
|
+
puts("Usage: #{File.split($0).last} source.txt [template.rhtml] > output.html")
|
49
|
+
exit!
|
50
|
+
end
|
51
|
+
|
52
|
+
template = ERB.new(File.open(template).read)
|
53
|
+
|
54
|
+
title = nil
|
55
|
+
body = nil
|
56
|
+
File.open(src) do |fsrc|
|
57
|
+
title_text = fsrc.readline
|
58
|
+
body_text = fsrc.read
|
59
|
+
syntax_items = []
|
60
|
+
body_text.gsub!(%r!<(pre|code)[^>]*?syntax=['"]([^'"]+)[^>]*>(.*?)</\1>!m){
|
61
|
+
ident = syntax_items.length
|
62
|
+
element, syntax, source = $1, $2, $3
|
63
|
+
syntax_items << "<#{element} class='syntax'>#{convert_syntax(syntax, source)}</#{element}>"
|
64
|
+
"syntax-temp-#{ident}"
|
65
|
+
}
|
66
|
+
title = RedCloth.new(title_text).to_html.gsub(%r!<.*?>!,'').strip
|
67
|
+
body = RedCloth.new(body_text).to_html
|
68
|
+
body.gsub!(%r!(?:<pre><code>)?syntax-temp-(\d+)(?:</code></pre>)?!){ syntax_items[$1.to_i] }
|
69
|
+
end
|
70
|
+
stat = File.stat(src)
|
71
|
+
created = stat.ctime
|
72
|
+
modified = stat.mtime
|
73
|
+
|
74
|
+
$stdout << template.result(binding)
|