typhoeus 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/.gitignore +1 -0
  2. data/CHANGELOG.markdown +14 -1
  3. data/Gemfile +2 -0
  4. data/Gemfile.lock +2 -0
  5. data/LICENSE +20 -0
  6. data/README.textile +39 -5
  7. data/Rakefile +8 -5
  8. data/VERSION +1 -1
  9. data/examples/file.rb +12 -0
  10. data/examples/times.rb +40 -0
  11. data/ext/typhoeus/.gitignore +2 -1
  12. data/ext/typhoeus/native.c +1 -0
  13. data/ext/typhoeus/native.h +1 -0
  14. data/ext/typhoeus/typhoeus_easy.c +32 -7
  15. data/ext/typhoeus/typhoeus_easy.h +1 -0
  16. data/ext/typhoeus/typhoeus_form.c +59 -0
  17. data/ext/typhoeus/typhoeus_form.h +13 -0
  18. data/ext/typhoeus/typhoeus_multi.c +15 -29
  19. data/lib/typhoeus.rb +1 -0
  20. data/lib/typhoeus/easy.rb +70 -48
  21. data/lib/typhoeus/form.rb +47 -0
  22. data/lib/typhoeus/hydra.rb +40 -7
  23. data/lib/typhoeus/hydra/connect_options.rb +19 -3
  24. data/lib/typhoeus/multi.rb +7 -5
  25. data/lib/typhoeus/remote.rb +1 -1
  26. data/lib/typhoeus/remote_proxy_object.rb +2 -0
  27. data/lib/typhoeus/request.rb +15 -3
  28. data/lib/typhoeus/response.rb +16 -1
  29. data/spec/fixtures/placeholder.gif +0 -0
  30. data/spec/fixtures/placeholder.txt +1 -0
  31. data/spec/fixtures/placeholder.ukn +0 -0
  32. data/spec/servers/app.rb +9 -0
  33. data/spec/spec.opts +1 -0
  34. data/spec/spec_helper.rb +3 -0
  35. data/spec/typhoeus/easy_spec.rb +55 -6
  36. data/spec/typhoeus/filter_spec.rb +2 -2
  37. data/spec/typhoeus/form_spec.rb +106 -0
  38. data/spec/typhoeus/hydra_mock_spec.rb +1 -1
  39. data/spec/typhoeus/hydra_spec.rb +108 -38
  40. data/spec/typhoeus/multi_spec.rb +1 -1
  41. data/spec/typhoeus/normalized_header_hash_spec.rb +1 -1
  42. data/spec/typhoeus/remote_method_spec.rb +2 -2
  43. data/spec/typhoeus/remote_proxy_object_spec.rb +1 -1
  44. data/spec/typhoeus/remote_spec.rb +1 -1
  45. data/spec/typhoeus/request_spec.rb +31 -2
  46. data/spec/typhoeus/response_spec.rb +13 -1
  47. data/spec/typhoeus/utils_spec.rb +1 -1
  48. data/typhoeus.gemspec +23 -6
  49. metadata +39 -19
data/.gitignore CHANGED
@@ -1,3 +1,4 @@
1
1
  .*.sw?
2
2
  *.gem
3
3
  .bundle
4
+ .idea
@@ -1,5 +1,18 @@
1
+ Current Master
2
+ --------------
3
+ * Added extended proxy support [Zapotek, GH-46]
4
+ * eliminated compile time warnings by using proper type declarations [skaes, GH-54]
5
+ * fixed broken calls to rb_raise [skaes, GH-54]
6
+ * prevent leaking of curl easy handles when exceptions are raised (either from typhoeus itself or user callbacks) [skaes, GH-54]
7
+ * fixed Easy#timed_out? using curl return codes [skaes, GH-54]
8
+ * provide curl return codes and corresponding curl error messages on classes Easy and Request [skaes, GH-54]
9
+ * allow VCR to whitelist hosts in Typhoeus stubbing/mocking [myronmarston, GH-57]
10
+ * added timed_out? documentation, method to Response [dbalatero, GH-34]
11
+ * added abort to Hydra to prematurely stop a hydra.run [Zapotek]
12
+ * added file upload support for POST requests [jtarchie, GH-59]
13
+
1
14
  0.2.0
2
- ------------
15
+ ------
3
16
  * Fix warning in Request#headers from attr_accessor
4
17
  * Params with array values were not parsing into the format that rack expects
5
18
  [GH-39, smartocci]
data/Gemfile CHANGED
@@ -1,5 +1,7 @@
1
1
  source :rubygems
2
2
 
3
+ gem "mime-types"
4
+
3
5
  group :test do
4
6
  gem 'rspec', '1.3.1'
5
7
  gem 'jeweler'
@@ -10,6 +10,7 @@ GEM
10
10
  rubyforge (>= 2.0.0)
11
11
  json (1.4.6)
12
12
  json_pure (1.4.6)
13
+ mime-types (1.16)
13
14
  rack (1.2.1)
14
15
  rspec (1.3.1)
15
16
  rubyforge (2.0.4)
@@ -26,5 +27,6 @@ DEPENDENCIES
26
27
  diff-lcs
27
28
  jeweler
28
29
  json
30
+ mime-types
29
31
  rspec (= 1.3.1)
30
32
  sinatra
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 David Balatero
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.
@@ -18,7 +18,7 @@ h2. Installation
18
18
 
19
19
  Typhoeus requires you to have a current version of libcurl installed. I've tested this with 7.19.4 and higher.
20
20
  <pre>
21
- gem install typhoeus --source http://gemcutter.org
21
+ gem install typhoeus
22
22
  </pre>
23
23
  If you're on Debian or Ubuntu and getting errors while trying to install, it could be because you don't have the latest version of libcurl installed. Do this to fix:
24
24
  <pre>
@@ -29,7 +29,7 @@ There's also something built in so that if you have a super old version of curl
29
29
  gem install typhoeus --source http://gemcutter.org -- --with-curl=/usr/local/curl/7.19.7/
30
30
  </pre>
31
31
 
32
- Another problem could be if you are running Mac Ports and you have libcurl installed through there. You need to uninstall it for Typhoeus to work! The version in Mac Ports is old and doesn't play nice. You should "download curl":http://curl.haxx.se/download.html and build from source. Then you'll have to install the gem again.
32
+ -Another problem could be if you are running Mac Ports and you have libcurl installed through there. You need to uninstall it for Typhoeus to work! The version in Mac Ports is old and doesn't play nice. You should "download curl":http://curl.haxx.se/download.html and build from source. Then you'll have to install the gem again.- The current version of Mac Ports (7.21.2) works just fine.
33
33
 
34
34
  If you're still having issues, please let me know on "the mailing list":http://groups.google.com/group/typhoeus.
35
35
 
@@ -79,6 +79,18 @@ response = Typhoeus::Request.post("http://localhost:3000/posts", :params => {:ti
79
79
  response = Typhoeus::Request.delete("http://localhost:3000/posts/1")
80
80
  </pre>
81
81
 
82
+ *Handling file uploads*
83
+ A File object can be passed as a param for a POST request to handle uploading files to the server. Typhoeus will upload the file as the original file name and use Mime::Types to set the content type.
84
+
85
+ <pre>
86
+ response = Typhoeus::Request.post("http://localhost:3000/posts",
87
+ :params => {
88
+ :title => "test post", :content => "this is my test",
89
+ :file => File.open("thesis.txt","r")
90
+ }
91
+ )
92
+ </pre>
93
+
82
94
  *Making Parallel Requests*
83
95
 
84
96
  <pre>
@@ -149,7 +161,7 @@ hydra.cache_getter do |request|
149
161
  end
150
162
  </pre>
151
163
 
152
- *Stubbing*
164
+ *Direct Stubbing*
153
165
  Hydra allows you to stub out specific urls and patters to avoid hitting remote servers while testing.
154
166
 
155
167
  <pre>
@@ -180,6 +192,15 @@ hydra = Typhoeus::Hydra.hydra
180
192
  hydra.stub(:get, "http://localhost:3000/users")
181
193
  </pre>
182
194
 
195
+ *Timeouts*
196
+
197
+ No exceptions are raised on HTTP timeouts. You can check whether a request timed out with the following methods:
198
+
199
+ <pre>
200
+ easy.timed_out? # for a raw Easy handle
201
+ response.timed_out? # for a Response handle
202
+ </pre>
203
+
183
204
  *Basic Authentication*
184
205
 
185
206
  <pre>
@@ -203,7 +224,9 @@ Typhoeus::Request.get("https://mail.google.com/mail", :disable_ssl_peer_verifica
203
224
  *LibCurl*
204
225
  Typhoeus also has a more raw libcurl interface. These are the Easy and Multi objects. If you're into accessing just the raw libcurl style, those are your best bet.
205
226
 
206
- SSL Certs can be provided to the Easy interface :
227
+ However, by using this raw interface, you do not get access to Hydra-specific features, such as stubbing/mocking.
228
+
229
+ SSL Certs can be provided to the Easy interface:
207
230
 
208
231
  <pre>
209
232
  e = Typhoeus::Easy.new
@@ -262,7 +285,7 @@ There's also an easy way to perform any kind of authentication via the quick req
262
285
  e = Typhoeus::Request.get("http://example.com",
263
286
  :username => 'username',
264
287
  :password => 'password',
265
- :method => :ntlm)
288
+ :auth_method => :ntlm)
266
289
  </pre>
267
290
 
268
291
  All methods listed above is available in a shorter form - :basic, :digest, :gssnegotiate, :ntlm, :digest_ie, :auto.
@@ -300,6 +323,17 @@ I set up a benchmark to test how the parallel performance works vs Ruby's built
300
323
 
301
324
  We can see from this that NET::HTTP performs as expected, taking 10 seconds to run 20 500ms requests. Typhoeus only takes 500ms (the time of the response that took the longest.) One other thing to note is that Typhoeus keeps a pool of libcurl Easy handles to use. For this benchmark I warmed the pool first. So if you test this out it may be a bit slower until the Easy handle pool has enough in it to run all the simultaneous requests. For some reason the easy handles can take quite some time to allocate.
302
325
 
326
+ h2. Running the specs
327
+
328
+ Running the specs requires a couple of Sinatra servers to be booted. Do this:
329
+
330
+ <pre>
331
+ ruby spec/servers/app.rb -p 3000
332
+ ruby spec/servers/app.rb -p 3001
333
+ ruby spec/servers/app.rb -p 3002
334
+ rake spec
335
+ </pre>
336
+
303
337
  h2. Next Steps
304
338
 
305
339
  * Add in ability to keep-alive requests and reuse them within hydra.
data/Rakefile CHANGED
@@ -1,6 +1,8 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+
1
3
  require "spec"
2
4
  require "spec/rake/spectask"
3
- require 'lib/typhoeus.rb'
5
+ require 'lib/typhoeus'
4
6
 
5
7
  begin
6
8
  require 'jeweler'
@@ -8,9 +10,10 @@ begin
8
10
  gemspec.name = "typhoeus"
9
11
  gemspec.summary = "A library for interacting with web services (and building SOAs) at blinding speed."
10
12
  gemspec.description = "Like a modern code version of the mythical beast with 100 serpent heads, Typhoeus runs HTTP requests in parallel while cleanly encapsulating handling logic."
11
- gemspec.email = "paul@pauldix.net"
12
- gemspec.homepage = "http://github.com/pauldix/typhoeus"
13
- gemspec.authors = ["Paul Dix"]
13
+ gemspec.email = "dbalatero@gmail.com"
14
+ gemspec.homepage = "http://github.com/dbalatero/typhoeus"
15
+ gemspec.authors = ["Paul Dix", "David Balatero"]
16
+ gemspec.add_dependency "mime-types"
14
17
  gemspec.add_development_dependency "rspec"
15
18
  gemspec.add_development_dependency "jeweler"
16
19
  gemspec.add_development_dependency "diff-lcs"
@@ -31,7 +34,7 @@ end
31
34
  task :install do
32
35
  rm_rf "*.gem"
33
36
  puts `gem build typhoeus.gemspec`
34
- puts `sudo gem install typhoeus-#{Typhoeus::VERSION}.gem`
37
+ puts `gem install typhoeus-#{Typhoeus::VERSION}.gem`
35
38
  end
36
39
 
37
40
  desc "Run all the tests"
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.0
1
+ 0.2.1
@@ -0,0 +1,12 @@
1
+ require 'rubygems'
2
+ require File.dirname(__FILE__) + '/../lib/typhoeus.rb'
3
+
4
+
5
+ response = Typhoeus::Request.post(
6
+ "http://video-feed.local",
7
+ :params => {
8
+ :file => File.new("file.rb")
9
+ }
10
+ )
11
+
12
+ puts response.inspect
@@ -0,0 +1,40 @@
1
+ require File.dirname(__FILE__) + '/../lib/typhoeus.rb'
2
+
3
+ hydra = Typhoeus::Hydra.new
4
+ hydra.disable_memoization
5
+
6
+ urls = [
7
+ 'http://google.com',
8
+ 'http://testphp.vulnweb.com',
9
+ 'http://demo.testfire.net',
10
+ 'http://example.net',
11
+ ]
12
+
13
+ 10.times {
14
+ |i|
15
+
16
+ req = Typhoeus::Request.new( urls[ i % urls.size] )
17
+ req.on_complete {
18
+ |res|
19
+ puts 'URL: ' + res.effective_url
20
+ puts 'Time: ' + res.time.to_s
21
+ puts 'Connect time: ' + res.connect_time.to_s
22
+ puts 'App connect time: ' + res.app_connect_time.to_s
23
+ puts 'Start transfer time: ' + res.start_transfer_time.to_s
24
+ puts 'Pre transfer time: ' + res.pretransfer_time.to_s
25
+ puts '-------------'
26
+ }
27
+
28
+ hydra.queue( req )
29
+ puts 'Queued: ' + req.url
30
+ }
31
+
32
+ puts
33
+ puts 'Harvesting responses...'
34
+ puts
35
+
36
+ hydra.run
37
+
38
+ puts
39
+ puts 'Done.'
40
+ puts
@@ -2,5 +2,6 @@ conftest.dSYM/*
2
2
  conftest.dSYM
3
3
  mkmf.log
4
4
  *.o
5
- native.bundleMakefile
5
+ native.bundle
6
+ Makefile
6
7
  *.bundle
@@ -8,4 +8,5 @@ void Init_native()
8
8
 
9
9
  init_typhoeus_easy();
10
10
  init_typhoeus_multi();
11
+ init_typhoeus_form();
11
12
  }
@@ -10,6 +10,7 @@ void Init_native();
10
10
  extern VALUE mTyphoeus;
11
11
  extern void init_typhoeus_easy();
12
12
  extern void init_typhoeus_multi();
13
+ extern void init_typhoeus_form();
13
14
 
14
15
  #endif
15
16
 
@@ -21,16 +21,27 @@ static VALUE easy_setopt_string(VALUE self, VALUE opt_name, VALUE parameter) {
21
21
  CurlEasy *curl_easy;
22
22
  Data_Get_Struct(self, CurlEasy, curl_easy);
23
23
 
24
- long opt = NUM2LONG(opt_name);
24
+ CURLoption opt = NUM2LONG(opt_name);
25
25
  curl_easy_setopt(curl_easy->curl, opt, StringValuePtr(parameter));
26
26
  return opt_name;
27
27
  }
28
28
 
29
+ static VALUE easy_setopt_form(VALUE self, VALUE opt_name, VALUE parameter) {
30
+ CurlEasy *curl_easy;
31
+ CurlForm *curl_form;
32
+ Data_Get_Struct(self, CurlEasy, curl_easy);
33
+ Data_Get_Struct(parameter, CurlForm, curl_form);
34
+
35
+ CURLoption opt = NUM2LONG(opt_name);
36
+ curl_easy_setopt(curl_easy->curl, opt, curl_form->first);
37
+ return opt_name;
38
+ }
39
+
29
40
  static VALUE easy_setopt_long(VALUE self, VALUE opt_name, VALUE parameter) {
30
41
  CurlEasy *curl_easy;
31
42
  Data_Get_Struct(self, CurlEasy, curl_easy);
32
43
 
33
- long opt = NUM2LONG(opt_name);
44
+ CURLoption opt = NUM2LONG(opt_name);
34
45
  curl_easy_setopt(curl_easy->curl, opt, NUM2LONG(parameter));
35
46
  return opt_name;
36
47
  }
@@ -40,7 +51,7 @@ static VALUE easy_getinfo_string(VALUE self, VALUE info) {
40
51
  CurlEasy *curl_easy;
41
52
  Data_Get_Struct(self, CurlEasy, curl_easy);
42
53
 
43
- long opt = NUM2LONG(info);
54
+ CURLoption opt = NUM2LONG(info);
44
55
  curl_easy_getinfo(curl_easy->curl, opt, &info_string);
45
56
 
46
57
  return rb_str_new2(info_string);
@@ -51,7 +62,7 @@ static VALUE easy_getinfo_long(VALUE self, VALUE info) {
51
62
  CurlEasy *curl_easy;
52
63
  Data_Get_Struct(self, CurlEasy, curl_easy);
53
64
 
54
- long opt = NUM2LONG(info);
65
+ CURLoption opt = NUM2LONG(info);
55
66
  curl_easy_getinfo(curl_easy->curl, opt, &info_long);
56
67
 
57
68
  return LONG2NUM(info_long);
@@ -62,7 +73,7 @@ static VALUE easy_getinfo_double(VALUE self, VALUE info) {
62
73
  CurlEasy *curl_easy;
63
74
  Data_Get_Struct(self, CurlEasy, curl_easy);
64
75
 
65
- long opt = NUM2LONG(info);
76
+ CURLoption opt = NUM2LONG(info);
66
77
  curl_easy_getinfo(curl_easy->curl, opt, &info_double);
67
78
 
68
79
  return rb_float_new(info_double);
@@ -70,8 +81,10 @@ static VALUE easy_getinfo_double(VALUE self, VALUE info) {
70
81
 
71
82
  static VALUE easy_perform(VALUE self) {
72
83
  CurlEasy *curl_easy;
84
+ CURLcode return_code;
73
85
  Data_Get_Struct(self, CurlEasy, curl_easy);
74
- curl_easy_perform(curl_easy->curl);
86
+ return_code = curl_easy_perform(curl_easy->curl);
87
+ rb_iv_set(self, "@curl_return_code", INT2FIX(return_code));
75
88
 
76
89
  return Qnil;
77
90
  }
@@ -166,7 +179,7 @@ static VALUE easy_escape(VALUE self, VALUE data, VALUE length) {
166
179
  CurlEasy *curl_easy;
167
180
  Data_Get_Struct(self, CurlEasy, curl_easy);
168
181
 
169
- return rb_str_new2(curl_easy_escape(curl_easy->curl, StringValuePtr(data), NUM2INT(length)));
182
+ return rb_str_new2(curl_easy_escape(curl_easy->curl, StringValuePtr(data), (int)NUM2INT(length)));
170
183
  }
171
184
 
172
185
  static VALUE version(VALUE self) {
@@ -188,11 +201,23 @@ static VALUE new(int argc, VALUE *argv, VALUE klass) {
188
201
  return easy;
189
202
  }
190
203
 
204
+ static VALUE curl_error_message(VALUE self) {
205
+ VALUE return_code = rb_iv_get(self, "@curl_return_code");
206
+ if (return_code == Qnil)
207
+ return Qnil;
208
+ else {
209
+ CURLcode rc = (CURLcode)FIX2INT(return_code);
210
+ return rb_str_new2(curl_easy_strerror(rc));
211
+ }
212
+ }
213
+
191
214
  void init_typhoeus_easy() {
192
215
  VALUE klass = cTyphoeusEasy = rb_define_class_under(mTyphoeus, "Easy", rb_cObject);
193
216
  idAppend = rb_intern("<<");
194
217
  rb_define_singleton_method(klass, "new", new, -1);
218
+ rb_define_method(klass, "curl_error_message", curl_error_message, 0);
195
219
  rb_define_private_method(klass, "easy_setopt_string", easy_setopt_string, 2);
220
+ rb_define_private_method(klass, "easy_setopt_form", easy_setopt_form, 2);
196
221
  rb_define_private_method(klass, "easy_setopt_long", easy_setopt_long, 2);
197
222
  rb_define_private_method(klass, "easy_getinfo_string", easy_getinfo_string, 1);
198
223
  rb_define_private_method(klass, "easy_getinfo_long", easy_getinfo_long, 1);
@@ -2,6 +2,7 @@
2
2
  #define TYPHOEUS_EASY
3
3
 
4
4
  #include <native.h>
5
+ #include <typhoeus_form.h>
5
6
 
6
7
  void init_typhoeus_easy();
7
8
  typedef struct {
@@ -0,0 +1,59 @@
1
+ #include <typhoeus_form.h>
2
+
3
+ VALUE cTyphoeusForm;
4
+
5
+ static void dealloc(CurlForm *curl_form) {
6
+ curl_formfree(curl_form->first);
7
+ free(curl_form);
8
+ }
9
+
10
+ static VALUE formadd_file(VALUE self,
11
+ VALUE name,
12
+ VALUE filename,
13
+ VALUE content_type,
14
+ VALUE path) {
15
+ CurlForm *curl_form;
16
+ Data_Get_Struct(self, CurlForm, curl_form);
17
+
18
+ return INT2NUM((long) curl_formadd(&curl_form->first, &curl_form->last,
19
+ CURLFORM_COPYNAME, RSTRING_PTR(name),
20
+ CURLFORM_NAMELENGTH, (long)RSTRING_LEN(name),
21
+ CURLFORM_FILENAME, RSTRING_PTR(filename),
22
+ CURLFORM_CONTENTTYPE, RSTRING_PTR(content_type),
23
+ CURLFORM_FILE, RSTRING_PTR(path),
24
+ CURLFORM_END
25
+ ));
26
+ }
27
+
28
+ static VALUE formadd_param(VALUE self, VALUE name, VALUE value) {
29
+ CurlForm *curl_form;
30
+ Data_Get_Struct(self, CurlForm, curl_form);
31
+
32
+ return INT2NUM((long)curl_formadd(&curl_form->first, &curl_form->last,
33
+ CURLFORM_COPYNAME, RSTRING_PTR(name),
34
+ CURLFORM_NAMELENGTH, (long)RSTRING_LEN(name),
35
+ CURLFORM_COPYCONTENTS, RSTRING_PTR(value),
36
+ CURLFORM_CONTENTSLENGTH, (long)RSTRING_LEN(value),
37
+ CURLFORM_END
38
+ ));
39
+ }
40
+
41
+ static VALUE new(int argc, VALUE *argv, VALUE klass) {
42
+ CurlForm *curl_form = ALLOC(CurlForm);
43
+ curl_form->first = NULL;
44
+ curl_form->last = NULL;
45
+
46
+ VALUE form = Data_Wrap_Struct(cTyphoeusForm, 0, dealloc, curl_form);
47
+
48
+ rb_obj_call_init(form, argc, argv);
49
+
50
+ return form;
51
+ }
52
+
53
+ void init_typhoeus_form() {
54
+ VALUE klass = cTyphoeusForm = rb_define_class_under(mTyphoeus, "Form", rb_cObject);
55
+
56
+ rb_define_singleton_method(klass, "new", new, -1);
57
+ rb_define_private_method(klass, "formadd_file", formadd_file, 4);
58
+ rb_define_private_method(klass, "formadd_param", formadd_param, 2);
59
+ }