curl-multi 0.1
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/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)
|