xpflow 0.1b

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.
Files changed (74) hide show
  1. data/bin/xpflow +96 -0
  2. data/lib/colorado.rb +198 -0
  3. data/lib/json/add/core.rb +243 -0
  4. data/lib/json/add/rails.rb +8 -0
  5. data/lib/json/common.rb +423 -0
  6. data/lib/json/editor.rb +1369 -0
  7. data/lib/json/ext.rb +28 -0
  8. data/lib/json/pure/generator.rb +442 -0
  9. data/lib/json/pure/parser.rb +320 -0
  10. data/lib/json/pure.rb +15 -0
  11. data/lib/json/version.rb +8 -0
  12. data/lib/json.rb +62 -0
  13. data/lib/mime/types.rb +881 -0
  14. data/lib/mime-types.rb +3 -0
  15. data/lib/restclient/abstract_response.rb +106 -0
  16. data/lib/restclient/exceptions.rb +193 -0
  17. data/lib/restclient/net_http_ext.rb +55 -0
  18. data/lib/restclient/payload.rb +235 -0
  19. data/lib/restclient/raw_response.rb +34 -0
  20. data/lib/restclient/request.rb +316 -0
  21. data/lib/restclient/resource.rb +169 -0
  22. data/lib/restclient/response.rb +24 -0
  23. data/lib/restclient.rb +174 -0
  24. data/lib/xpflow/bash.rb +341 -0
  25. data/lib/xpflow/bundle.rb +113 -0
  26. data/lib/xpflow/cmdline.rb +249 -0
  27. data/lib/xpflow/collection.rb +122 -0
  28. data/lib/xpflow/concurrency.rb +79 -0
  29. data/lib/xpflow/data.rb +393 -0
  30. data/lib/xpflow/dsl.rb +816 -0
  31. data/lib/xpflow/engine.rb +574 -0
  32. data/lib/xpflow/ensemble.rb +135 -0
  33. data/lib/xpflow/events.rb +56 -0
  34. data/lib/xpflow/experiment.rb +65 -0
  35. data/lib/xpflow/exts/facter.rb +30 -0
  36. data/lib/xpflow/exts/g5k.rb +931 -0
  37. data/lib/xpflow/exts/g5k_use.rb +50 -0
  38. data/lib/xpflow/exts/gui.rb +140 -0
  39. data/lib/xpflow/exts/model.rb +155 -0
  40. data/lib/xpflow/graph.rb +1603 -0
  41. data/lib/xpflow/graph_xpflow.rb +251 -0
  42. data/lib/xpflow/import.rb +196 -0
  43. data/lib/xpflow/library.rb +349 -0
  44. data/lib/xpflow/logging.rb +153 -0
  45. data/lib/xpflow/manager.rb +147 -0
  46. data/lib/xpflow/nodes.rb +1250 -0
  47. data/lib/xpflow/runs.rb +773 -0
  48. data/lib/xpflow/runtime.rb +125 -0
  49. data/lib/xpflow/scope.rb +168 -0
  50. data/lib/xpflow/ssh.rb +186 -0
  51. data/lib/xpflow/stat.rb +50 -0
  52. data/lib/xpflow/stdlib.rb +381 -0
  53. data/lib/xpflow/structs.rb +369 -0
  54. data/lib/xpflow/taktuk.rb +193 -0
  55. data/lib/xpflow/templates/ssh-config.basic +14 -0
  56. data/lib/xpflow/templates/ssh-config.inria +18 -0
  57. data/lib/xpflow/templates/ssh-config.proxy +13 -0
  58. data/lib/xpflow/templates/taktuk +6590 -0
  59. data/lib/xpflow/templates/utils/batch +4 -0
  60. data/lib/xpflow/templates/utils/bootstrap +12 -0
  61. data/lib/xpflow/templates/utils/hostname +3 -0
  62. data/lib/xpflow/templates/utils/ping +3 -0
  63. data/lib/xpflow/templates/utils/rsync +12 -0
  64. data/lib/xpflow/templates/utils/scp +17 -0
  65. data/lib/xpflow/templates/utils/scp_many +8 -0
  66. data/lib/xpflow/templates/utils/ssh +3 -0
  67. data/lib/xpflow/templates/utils/ssh-interactive +4 -0
  68. data/lib/xpflow/templates/utils/taktuk +19 -0
  69. data/lib/xpflow/threads.rb +187 -0
  70. data/lib/xpflow/utils.rb +569 -0
  71. data/lib/xpflow/visual.rb +230 -0
  72. data/lib/xpflow/with_g5k.rb +7 -0
  73. data/lib/xpflow.rb +349 -0
  74. metadata +135 -0
@@ -0,0 +1,316 @@
1
+ require 'tempfile'
2
+ require 'mime/types'
3
+ require 'cgi'
4
+
5
+ module RestClient
6
+ # This class is used internally by RestClient to send the request, but you can also
7
+ # call it directly if you'd like to use a method not supported by the
8
+ # main API. For example:
9
+ #
10
+ # RestClient::Request.execute(:method => :head, :url => 'http://example.com')
11
+ #
12
+ # Mandatory parameters:
13
+ # * :method
14
+ # * :url
15
+ # Optional parameters (have a look at ssl and/or uri for some explanations):
16
+ # * :headers a hash containing the request headers
17
+ # * :cookies will replace possible cookies in the :headers
18
+ # * :user and :password for basic auth, will be replaced by a user/password available in the :url
19
+ # * :block_response call the provided block with the HTTPResponse as parameter
20
+ # * :raw_response return a low-level RawResponse instead of a Response
21
+ # * :max_redirects maximum number of redirections (default to 10)
22
+ # * :verify_ssl enable ssl verification, possible values are constants from OpenSSL::SSL
23
+ # * :timeout and :open_timeout passing in -1 will disable the timeout by setting the corresponding net timeout values to nil
24
+ # * :ssl_client_cert, :ssl_client_key, :ssl_ca_file
25
+ class Request
26
+
27
+ attr_reader :method, :url, :headers, :cookies,
28
+ :payload, :user, :password, :timeout, :max_redirects,
29
+ :open_timeout, :raw_response, :verify_ssl, :ssl_client_cert,
30
+ :ssl_client_key, :ssl_ca_file, :processed_headers, :args
31
+
32
+ def self.execute(args, & block)
33
+ new(args).execute(& block)
34
+ end
35
+
36
+ def initialize args
37
+ @method = args[:method] or raise ArgumentError, "must pass :method"
38
+ @headers = args[:headers] || {}
39
+ if args[:url]
40
+ @url = process_url_params(args[:url], headers)
41
+ else
42
+ raise ArgumentError, "must pass :url"
43
+ end
44
+ @cookies = @headers.delete(:cookies) || args[:cookies] || {}
45
+ @payload = Payload.generate(args[:payload])
46
+ @user = args[:user]
47
+ @password = args[:password]
48
+ @timeout = args[:timeout]
49
+ @open_timeout = args[:open_timeout]
50
+ @block_response = args[:block_response]
51
+ @raw_response = args[:raw_response] || false
52
+ @verify_ssl = args[:verify_ssl] || false
53
+ @ssl_client_cert = args[:ssl_client_cert] || nil
54
+ @ssl_client_key = args[:ssl_client_key] || nil
55
+ @ssl_ca_file = args[:ssl_ca_file] || nil
56
+ @tf = nil # If you are a raw request, this is your tempfile
57
+ @max_redirects = args[:max_redirects] || 10
58
+ @processed_headers = make_headers headers
59
+ @args = args
60
+ end
61
+
62
+ def execute & block
63
+ uri = parse_url_with_auth(url)
64
+ transmit uri, net_http_request_class(method).new(uri.request_uri, processed_headers), payload, & block
65
+ ensure
66
+ payload.close if payload
67
+ end
68
+
69
+ # Extract the query parameters and append them to the url
70
+ def process_url_params url, headers
71
+ url_params = {}
72
+ headers.delete_if do |key, value|
73
+ if 'params' == key.to_s.downcase && value.is_a?(Hash)
74
+ url_params.merge! value
75
+ true
76
+ else
77
+ false
78
+ end
79
+ end
80
+ unless url_params.empty?
81
+ query_string = url_params.collect { |k, v| "#{k.to_s}=#{CGI::escape(v.to_s)}" }.join('&')
82
+ url + "?#{query_string}"
83
+ else
84
+ url
85
+ end
86
+ end
87
+
88
+ def make_headers user_headers
89
+ unless @cookies.empty?
90
+ user_headers[:cookie] = @cookies.map { |(key, val)| "#{key.to_s}=#{CGI::unescape(val)}" }.sort.join('; ')
91
+ end
92
+ headers = stringify_headers(default_headers).merge(stringify_headers(user_headers))
93
+ headers.merge!(@payload.headers) if @payload
94
+ headers
95
+ end
96
+
97
+ def net_http_class
98
+ if RestClient.proxy
99
+ proxy_uri = URI.parse(RestClient.proxy)
100
+ Net::HTTP::Proxy(proxy_uri.host, proxy_uri.port, proxy_uri.user, proxy_uri.password)
101
+ else
102
+ Net::HTTP
103
+ end
104
+ end
105
+
106
+ def net_http_request_class(method)
107
+ Net::HTTP.const_get(method.to_s.capitalize)
108
+ end
109
+
110
+ def parse_url(url)
111
+ url = "http://#{url}" unless url.match(/^http/)
112
+ URI.parse(url)
113
+ end
114
+
115
+ def parse_url_with_auth(url)
116
+ uri = parse_url(url)
117
+ @user = CGI.unescape(uri.user) if uri.user
118
+ @password = CGI.unescape(uri.password) if uri.password
119
+ uri
120
+ end
121
+
122
+ def process_payload(p=nil, parent_key=nil)
123
+ unless p.is_a?(Hash)
124
+ p
125
+ else
126
+ @headers[:content_type] ||= 'application/x-www-form-urlencoded'
127
+ p.keys.map do |k|
128
+ key = parent_key ? "#{parent_key}[#{k}]" : k
129
+ if p[k].is_a? Hash
130
+ process_payload(p[k], key)
131
+ else
132
+ value = URI.escape(p[k].to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
133
+ "#{key}=#{value}"
134
+ end
135
+ end.join("&")
136
+ end
137
+ end
138
+
139
+ def transmit uri, req, payload, & block
140
+ setup_credentials req
141
+
142
+ net = net_http_class.new(uri.host, uri.port)
143
+ net.use_ssl = uri.is_a?(URI::HTTPS)
144
+ if (@verify_ssl == false) || (@verify_ssl == OpenSSL::SSL::VERIFY_NONE)
145
+ net.verify_mode = OpenSSL::SSL::VERIFY_NONE
146
+ elsif @verify_ssl.is_a? Integer
147
+ net.verify_mode = @verify_ssl
148
+ net.verify_callback = lambda do |preverify_ok, ssl_context|
149
+ if (!preverify_ok) || ssl_context.error != 0
150
+ err_msg = "SSL Verification failed -- Preverify: #{preverify_ok}, Error: #{ssl_context.error_string} (#{ssl_context.error})"
151
+ raise SSLCertificateNotVerified.new(err_msg)
152
+ end
153
+ true
154
+ end
155
+ end
156
+ net.cert = @ssl_client_cert if @ssl_client_cert
157
+ net.key = @ssl_client_key if @ssl_client_key
158
+ net.ca_file = @ssl_ca_file if @ssl_ca_file
159
+ net.read_timeout = @timeout if @timeout
160
+ net.open_timeout = @open_timeout if @open_timeout
161
+
162
+ # disable the timeout if the timeout value is -1
163
+ net.read_timeout = nil if @timeout == -1
164
+ net.out_timeout = nil if @open_timeout == -1
165
+
166
+ RestClient.before_execution_procs.each do |before_proc|
167
+ before_proc.call(req, args)
168
+ end
169
+
170
+ log_request
171
+
172
+ net.start do |http|
173
+ if @block_response
174
+ http.request(req, payload ? payload.to_s : nil, & @block_response)
175
+ else
176
+ res = http.request(req, payload ? payload.to_s : nil) { |http_response| fetch_body(http_response) }
177
+ log_response res
178
+ process_result res, & block
179
+ end
180
+ end
181
+ rescue EOFError
182
+ raise RestClient::ServerBrokeConnection
183
+ rescue Timeout::Error
184
+ raise RestClient::RequestTimeout
185
+ end
186
+
187
+ def setup_credentials(req)
188
+ req.basic_auth(user, password) if user
189
+ end
190
+
191
+ def fetch_body(http_response)
192
+ if @raw_response
193
+ # Taken from Chef, which as in turn...
194
+ # Stolen from http://www.ruby-forum.com/topic/166423
195
+ # Kudos to _why!
196
+ @tf = Tempfile.new("rest-client")
197
+ size, total = 0, http_response.header['Content-Length'].to_i
198
+ http_response.read_body do |chunk|
199
+ @tf.write chunk
200
+ size += chunk.size
201
+ if RestClient.log
202
+ if size == 0
203
+ RestClient.log << "#{@method} #{@url} done (0 length file\n)"
204
+ elsif total == 0
205
+ RestClient.log << "#{@method} #{@url} (zero content length)\n"
206
+ else
207
+ RestClient.log << "#{@method} #{@url} %d%% done (%d of %d)\n" % [(size * 100) / total, size, total]
208
+ end
209
+ end
210
+ end
211
+ @tf.close
212
+ @tf
213
+ else
214
+ http_response.read_body
215
+ end
216
+ http_response
217
+ end
218
+
219
+ def process_result res, & block
220
+ if @raw_response
221
+ # We don't decode raw requests
222
+ response = RawResponse.new(@tf, res, args)
223
+ else
224
+ response = Response.create(Request.decode(res['content-encoding'], res.body), res, args)
225
+ end
226
+
227
+ if block_given?
228
+ block.call(response, self, res, & block)
229
+ else
230
+ response.return!(self, res, & block)
231
+ end
232
+
233
+ end
234
+
235
+ def self.decode content_encoding, body
236
+ if (!body) || body.empty?
237
+ body
238
+ elsif content_encoding == 'gzip'
239
+ Zlib::GzipReader.new(StringIO.new(body)).read
240
+ elsif content_encoding == 'deflate'
241
+ begin
242
+ Zlib::Inflate.new.inflate body
243
+ rescue Zlib::DataError
244
+ # No luck with Zlib decompression. Let's try with raw deflate,
245
+ # like some broken web servers do.
246
+ Zlib::Inflate.new(-Zlib::MAX_WBITS).inflate body
247
+ end
248
+ else
249
+ body
250
+ end
251
+ end
252
+
253
+ def log_request
254
+ if RestClient.log
255
+ out = []
256
+ out << "RestClient.#{method} #{url.inspect}"
257
+ out << payload.short_inspect if payload
258
+ out << processed_headers.to_a.sort.map { |(k, v)| [k.inspect, v.inspect].join("=>") }.join(", ")
259
+ RestClient.log << out.join(', ') + "\n"
260
+ end
261
+ end
262
+
263
+ def log_response res
264
+ if RestClient.log
265
+ size = @raw_response ? File.size(@tf.path) : (res.body.nil? ? 0 : res.body.size)
266
+ RestClient.log << "# => #{res.code} #{res.class.to_s.gsub(/^Net::HTTP/, '')} | #{(res['Content-type'] || '').gsub(/;.*$/, '')} #{size} bytes\n"
267
+ end
268
+ end
269
+
270
+ # Return a hash of headers whose keys are capitalized strings
271
+ def stringify_headers headers
272
+ headers.inject({}) do |result, (key, value)|
273
+ if key.is_a? Symbol
274
+ key = key.to_s.split(/_/).map { |w| w.capitalize }.join('-')
275
+ end
276
+ if 'CONTENT-TYPE' == key.upcase
277
+ target_value = value.to_s
278
+ result[key] = MIME::Types.type_for_extension target_value
279
+ elsif 'ACCEPT' == key.upcase
280
+ # Accept can be composed of several comma-separated values
281
+ if value.is_a? Array
282
+ target_values = value
283
+ else
284
+ target_values = value.to_s.split ','
285
+ end
286
+ result[key] = target_values.map { |ext| MIME::Types.type_for_extension(ext.to_s.strip) }.join(', ')
287
+ else
288
+ result[key] = value.to_s
289
+ end
290
+ result
291
+ end
292
+ end
293
+
294
+ def default_headers
295
+ {:accept => '*/*; q=0.5, application/xml', :accept_encoding => 'gzip, deflate'}
296
+ end
297
+
298
+ end
299
+ end
300
+
301
+ module MIME
302
+ class Types
303
+
304
+ # Return the first found content-type for a value considered as an extension or the value itself
305
+ def type_for_extension ext
306
+ candidates = @extension_index[ext]
307
+ candidates.empty? ? ext : candidates[0].content_type
308
+ end
309
+
310
+ class << self
311
+ def type_for_extension ext
312
+ @__types__.type_for_extension ext
313
+ end
314
+ end
315
+ end
316
+ end
@@ -0,0 +1,169 @@
1
+ module RestClient
2
+ # A class that can be instantiated for access to a RESTful resource,
3
+ # including authentication.
4
+ #
5
+ # Example:
6
+ #
7
+ # resource = RestClient::Resource.new('http://some/resource')
8
+ # jpg = resource.get(:accept => 'image/jpg')
9
+ #
10
+ # With HTTP basic authentication:
11
+ #
12
+ # resource = RestClient::Resource.new('http://protected/resource', :user => 'user', :password => 'password')
13
+ # resource.delete
14
+ #
15
+ # With a timeout (seconds):
16
+ #
17
+ # RestClient::Resource.new('http://slow', :timeout => 10)
18
+ #
19
+ # With an open timeout (seconds):
20
+ #
21
+ # RestClient::Resource.new('http://behindfirewall', :open_timeout => 10)
22
+ #
23
+ # You can also use resources to share common headers. For headers keys,
24
+ # symbols are converted to strings. Example:
25
+ #
26
+ # resource = RestClient::Resource.new('http://some/resource', :headers => { :client_version => 1 })
27
+ #
28
+ # This header will be transported as X-Client-Version (notice the X prefix,
29
+ # capitalization and hyphens)
30
+ #
31
+ # Use the [] syntax to allocate subresources:
32
+ #
33
+ # site = RestClient::Resource.new('http://example.com', :user => 'adam', :password => 'mypasswd')
34
+ # site['posts/1/comments'].post 'Good article.', :content_type => 'text/plain'
35
+ #
36
+ class Resource
37
+ attr_reader :url, :options, :block
38
+
39
+ def initialize(url, options={}, backwards_compatibility=nil, &block)
40
+ @url = url
41
+ @block = block
42
+ if options.class == Hash
43
+ @options = options
44
+ else # compatibility with previous versions
45
+ @options = { :user => options, :password => backwards_compatibility }
46
+ end
47
+ end
48
+
49
+ def get(additional_headers={}, &block)
50
+ headers = (options[:headers] || {}).merge(additional_headers)
51
+ Request.execute(options.merge(
52
+ :method => :get,
53
+ :url => url,
54
+ :headers => headers), &(block || @block))
55
+ end
56
+
57
+ def head(additional_headers={}, &block)
58
+ headers = (options[:headers] || {}).merge(additional_headers)
59
+ Request.execute(options.merge(
60
+ :method => :head,
61
+ :url => url,
62
+ :headers => headers), &(block || @block))
63
+ end
64
+
65
+ def post(payload, additional_headers={}, &block)
66
+ headers = (options[:headers] || {}).merge(additional_headers)
67
+ Request.execute(options.merge(
68
+ :method => :post,
69
+ :url => url,
70
+ :payload => payload,
71
+ :headers => headers), &(block || @block))
72
+ end
73
+
74
+ def put(payload, additional_headers={}, &block)
75
+ headers = (options[:headers] || {}).merge(additional_headers)
76
+ Request.execute(options.merge(
77
+ :method => :put,
78
+ :url => url,
79
+ :payload => payload,
80
+ :headers => headers), &(block || @block))
81
+ end
82
+
83
+ def patch(payload, additional_headers={}, &block)
84
+ headers = (options[:headers] || {}).merge(additional_headers)
85
+ Request.execute(options.merge(
86
+ :method => :patch,
87
+ :url => url,
88
+ :payload => payload,
89
+ :headers => headers), &(block || @block))
90
+ end
91
+
92
+ def delete(additional_headers={}, &block)
93
+ headers = (options[:headers] || {}).merge(additional_headers)
94
+ Request.execute(options.merge(
95
+ :method => :delete,
96
+ :url => url,
97
+ :headers => headers), &(block || @block))
98
+ end
99
+
100
+ def to_s
101
+ url
102
+ end
103
+
104
+ def user
105
+ options[:user]
106
+ end
107
+
108
+ def password
109
+ options[:password]
110
+ end
111
+
112
+ def headers
113
+ options[:headers] || {}
114
+ end
115
+
116
+ def timeout
117
+ options[:timeout]
118
+ end
119
+
120
+ def open_timeout
121
+ options[:open_timeout]
122
+ end
123
+
124
+ # Construct a subresource, preserving authentication.
125
+ #
126
+ # Example:
127
+ #
128
+ # site = RestClient::Resource.new('http://example.com', 'adam', 'mypasswd')
129
+ # site['posts/1/comments'].post 'Good article.', :content_type => 'text/plain'
130
+ #
131
+ # This is especially useful if you wish to define your site in one place and
132
+ # call it in multiple locations:
133
+ #
134
+ # def orders
135
+ # RestClient::Resource.new('http://example.com/orders', 'admin', 'mypasswd')
136
+ # end
137
+ #
138
+ # orders.get # GET http://example.com/orders
139
+ # orders['1'].get # GET http://example.com/orders/1
140
+ # orders['1/items'].delete # DELETE http://example.com/orders/1/items
141
+ #
142
+ # Nest resources as far as you want:
143
+ #
144
+ # site = RestClient::Resource.new('http://example.com')
145
+ # posts = site['posts']
146
+ # first_post = posts['1']
147
+ # comments = first_post['comments']
148
+ # comments.post 'Hello', :content_type => 'text/plain'
149
+ #
150
+ def [](suburl, &new_block)
151
+ case
152
+ when block_given? then self.class.new(concat_urls(url, suburl), options, &new_block)
153
+ when block then self.class.new(concat_urls(url, suburl), options, &block)
154
+ else
155
+ self.class.new(concat_urls(url, suburl), options)
156
+ end
157
+ end
158
+
159
+ def concat_urls(url, suburl) # :nodoc:
160
+ url = url.to_s
161
+ suburl = suburl.to_s
162
+ if url.slice(-1, 1) == '/' or suburl.slice(0, 1) == '/'
163
+ url + suburl
164
+ else
165
+ "#{url}/#{suburl}"
166
+ end
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,24 @@
1
+ module RestClient
2
+
3
+ # A Response from RestClient, you can access the response body, the code or the headers.
4
+ #
5
+ module Response
6
+
7
+ include AbstractResponse
8
+
9
+ attr_accessor :args, :body, :net_http_res
10
+
11
+ def body
12
+ self
13
+ end
14
+
15
+ def Response.create body, net_http_res, args
16
+ result = body || ''
17
+ result.extend Response
18
+ result.net_http_res = net_http_res
19
+ result.args = args
20
+ result
21
+ end
22
+
23
+ end
24
+ end
data/lib/restclient.rb ADDED
@@ -0,0 +1,174 @@
1
+ require 'uri'
2
+ require 'zlib'
3
+ require 'stringio'
4
+
5
+ begin
6
+ require 'net/https'
7
+ rescue LoadError => e
8
+ raise e unless RUBY_PLATFORM =~ /linux/
9
+ raise LoadError, "no such file to load -- net/https. Try running apt-get install libopenssl-ruby"
10
+ end
11
+
12
+ require File.dirname(__FILE__) + '/restclient/exceptions'
13
+ require File.dirname(__FILE__) + '/restclient/request'
14
+ require File.dirname(__FILE__) + '/restclient/abstract_response'
15
+ require File.dirname(__FILE__) + '/restclient/response'
16
+ require File.dirname(__FILE__) + '/restclient/raw_response'
17
+ require File.dirname(__FILE__) + '/restclient/resource'
18
+ require File.dirname(__FILE__) + '/restclient/payload'
19
+ require File.dirname(__FILE__) + '/restclient/net_http_ext'
20
+
21
+ # This module's static methods are the entry point for using the REST client.
22
+ #
23
+ # # GET
24
+ # xml = RestClient.get 'http://example.com/resource'
25
+ # jpg = RestClient.get 'http://example.com/resource', :accept => 'image/jpg'
26
+ #
27
+ # # authentication and SSL
28
+ # RestClient.get 'https://user:password@example.com/private/resource'
29
+ #
30
+ # # POST or PUT with a hash sends parameters as a urlencoded form body
31
+ # RestClient.post 'http://example.com/resource', :param1 => 'one'
32
+ #
33
+ # # nest hash parameters
34
+ # RestClient.post 'http://example.com/resource', :nested => { :param1 => 'one' }
35
+ #
36
+ # # POST and PUT with raw payloads
37
+ # RestClient.post 'http://example.com/resource', 'the post body', :content_type => 'text/plain'
38
+ # RestClient.post 'http://example.com/resource.xml', xml_doc
39
+ # RestClient.put 'http://example.com/resource.pdf', File.read('my.pdf'), :content_type => 'application/pdf'
40
+ #
41
+ # # DELETE
42
+ # RestClient.delete 'http://example.com/resource'
43
+ #
44
+ # # retreive the response http code and headers
45
+ # res = RestClient.get 'http://example.com/some.jpg'
46
+ # res.code # => 200
47
+ # res.headers[:content_type] # => 'image/jpg'
48
+ #
49
+ # # HEAD
50
+ # RestClient.head('http://example.com').headers
51
+ #
52
+ # To use with a proxy, just set RestClient.proxy to the proper http proxy:
53
+ #
54
+ # RestClient.proxy = "http://proxy.example.com/"
55
+ #
56
+ # Or inherit the proxy from the environment:
57
+ #
58
+ # RestClient.proxy = ENV['http_proxy']
59
+ #
60
+ # For live tests of RestClient, try using http://rest-test.heroku.com, which echoes back information about the rest call:
61
+ #
62
+ # >> RestClient.put 'http://rest-test.heroku.com/resource', :foo => 'baz'
63
+ # => "PUT http://rest-test.heroku.com/resource with a 7 byte payload, content type application/x-www-form-urlencoded {\"foo\"=>\"baz\"}"
64
+ #
65
+ module RestClient
66
+
67
+ def self.get(url, headers={}, &block)
68
+ Request.execute(:method => :get, :url => url, :headers => headers, &block)
69
+ end
70
+
71
+ def self.post(url, payload, headers={}, &block)
72
+ Request.execute(:method => :post, :url => url, :payload => payload, :headers => headers, &block)
73
+ end
74
+
75
+ def self.patch(url, payload, headers={}, &block)
76
+ Request.execute(:method => :patch, :url => url, :payload => payload, :headers => headers, &block)
77
+ end
78
+
79
+ def self.put(url, payload, headers={}, &block)
80
+ Request.execute(:method => :put, :url => url, :payload => payload, :headers => headers, &block)
81
+ end
82
+
83
+ def self.delete(url, headers={}, &block)
84
+ Request.execute(:method => :delete, :url => url, :headers => headers, &block)
85
+ end
86
+
87
+ def self.head(url, headers={}, &block)
88
+ Request.execute(:method => :head, :url => url, :headers => headers, &block)
89
+ end
90
+
91
+ def self.options(url, headers={}, &block)
92
+ Request.execute(:method => :options, :url => url, :headers => headers, &block)
93
+ end
94
+
95
+ class << self
96
+ attr_accessor :proxy
97
+ end
98
+
99
+ # Setup the log for RestClient calls.
100
+ # Value should be a logger but can can be stdout, stderr, or a filename.
101
+ # You can also configure logging by the environment variable RESTCLIENT_LOG.
102
+ def self.log= log
103
+ @@log = create_log log
104
+ end
105
+
106
+ def self.version
107
+ version_path = File.dirname(__FILE__) + "/../VERSION"
108
+ return File.read(version_path).chomp if File.file?(version_path)
109
+ "0.0.0"
110
+ end
111
+
112
+ # Create a log that respond to << like a logger
113
+ # param can be 'stdout', 'stderr', a string (then we will log to that file) or a logger (then we return it)
114
+ def self.create_log param
115
+ if param
116
+ if param.is_a? String
117
+ if param == 'stdout'
118
+ stdout_logger = Class.new do
119
+ def << obj
120
+ STDOUT.puts obj
121
+ end
122
+ end
123
+ stdout_logger.new
124
+ elsif param == 'stderr'
125
+ stderr_logger = Class.new do
126
+ def << obj
127
+ STDERR.puts obj
128
+ end
129
+ end
130
+ stderr_logger.new
131
+ else
132
+ file_logger = Class.new do
133
+ attr_writer :target_file
134
+
135
+ def << obj
136
+ File.open(@target_file, 'a') { |f| f.puts obj }
137
+ end
138
+ end
139
+ logger = file_logger.new
140
+ logger.target_file = param
141
+ logger
142
+ end
143
+ else
144
+ param
145
+ end
146
+ end
147
+ end
148
+
149
+ @@env_log = create_log ENV['RESTCLIENT_LOG']
150
+
151
+ @@log = nil
152
+
153
+ def self.log # :nodoc:
154
+ @@env_log || @@log
155
+ end
156
+
157
+ @@before_execution_procs = []
158
+
159
+ # Add a Proc to be called before each request in executed.
160
+ # The proc parameters will be the http request and the request params.
161
+ def self.add_before_execution_proc &proc
162
+ @@before_execution_procs << proc
163
+ end
164
+
165
+ # Reset the procs to be called before each request is executed.
166
+ def self.reset_before_execution_procs
167
+ @@before_execution_procs = []
168
+ end
169
+
170
+ def self.before_execution_procs # :nodoc:
171
+ @@before_execution_procs
172
+ end
173
+
174
+ end