auser-poolparty 1.2.2 → 1.2.3

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 (247) hide show
  1. data/VERSION.yml +1 -1
  2. data/bin/cloud +37 -48
  3. data/bin/cloud-bootstrap +33 -23
  4. data/bin/cloud-configure +33 -34
  5. data/bin/cloud-console +31 -9
  6. data/bin/cloud-contract +27 -11
  7. data/bin/cloud-expand +32 -19
  8. data/bin/cloud-list +31 -15
  9. data/bin/cloud-osxcopy +22 -12
  10. data/bin/cloud-provision +35 -24
  11. data/bin/cloud-setup-dev +31 -20
  12. data/bin/cloud-show +40 -21
  13. data/bin/cloud-ssh +24 -15
  14. data/bin/cloud-start +33 -19
  15. data/bin/cloud-terminate +33 -20
  16. data/bin/cloud-verify +38 -29
  17. data/bin/install-poolparty +4 -198
  18. data/bin/server-cloud-elections +1 -1
  19. data/examples/basic.rb +9 -9
  20. data/examples/fairchild.rb +1 -1
  21. data/examples/metavirt_cloud.rb +21 -0
  22. data/lib/poolparty/aska.rb +3 -0
  23. data/lib/poolparty/base_packages/haproxy.rb +19 -18
  24. data/lib/poolparty/base_packages/poolparty.rb +1 -1
  25. data/lib/poolparty/core/hash.rb +46 -0
  26. data/lib/poolparty/core/object.rb +21 -15
  27. data/lib/poolparty/core/string.rb +16 -0
  28. data/lib/poolparty/dependency_resolver/chef_resolver.rb +23 -6
  29. data/lib/poolparty/dependency_resolver/dependency_resolver.rb +1 -1
  30. data/lib/poolparty/dependency_resolver/dependency_resolver_cloud_extensions.rb +2 -2
  31. data/lib/poolparty/dependency_resolver/puppet_resolver.rb +10 -32
  32. data/lib/poolparty/helpers/optioner.rb +19 -14
  33. data/lib/poolparty/installers/base_installer.rb +113 -0
  34. data/lib/poolparty/installers/ec2.rb +141 -0
  35. data/lib/poolparty/installers/vmrun.rb +144 -0
  36. data/lib/poolparty/modules/callbacks.rb +6 -2
  37. data/lib/poolparty/modules/cloud_dsl.rb +2 -2
  38. data/lib/poolparty/modules/cloud_resourcer.rb +10 -50
  39. data/lib/poolparty/modules/file_writer.rb +2 -2
  40. data/lib/poolparty/modules/pinger.rb +4 -1
  41. data/lib/poolparty/modules/resourcing_dsl.rb +1 -29
  42. data/lib/poolparty/modules/searchable_paths.rb +3 -3
  43. data/lib/poolparty/monitors/monitor_rack.rb +1 -1
  44. data/lib/poolparty/net/remote_bases.rb +2 -0
  45. data/lib/poolparty/net/remote_instance.rb +6 -4
  46. data/lib/poolparty/net/remoter/interactive.rb +2 -3
  47. data/lib/poolparty/net/remoter_base.rb +54 -31
  48. data/lib/poolparty/net/remoter_bases/ec2/ec2.rb +49 -26
  49. data/lib/poolparty/net/remoter_bases/ec2/ec2_remote_instance.rb +6 -19
  50. data/lib/poolparty/net/remoter_bases/ec2/ec2_response_object.rb +4 -3
  51. data/lib/poolparty/net/remoter_bases/metavirt/metavirt.rb +99 -0
  52. data/lib/poolparty/net/remoter_bases/metavirt/metavirt_instance.rb +52 -0
  53. data/lib/poolparty/net/remoter_bases/vmrun/utilities/vm_disk.rb +1 -1
  54. data/lib/poolparty/net/remoter_bases/vmrun/utilities/vmx.rb +1 -1
  55. data/lib/poolparty/net/remoter_bases/vmrun/utilities/vmx_file.rb +117 -117
  56. data/lib/poolparty/net/remoter_bases/vmrun/vmrun.rb +36 -31
  57. data/lib/poolparty/net/remoter_bases/vmrun/vmrun_instance.rb +9 -5
  58. data/lib/poolparty/plugins/apache2/apache.rb +118 -73
  59. data/lib/poolparty/plugins/chef.rb +15 -17
  60. data/lib/poolparty/plugins/chef_deploy.rb +18 -4
  61. data/lib/poolparty/plugins/deploy_directory.rb +25 -16
  62. data/lib/poolparty/plugins/gem_package.rb +15 -5
  63. data/lib/poolparty/plugins/git.rb +44 -30
  64. data/lib/poolparty/plugins/line_in_file.rb +5 -1
  65. data/lib/poolparty/plugins/rails_deploy.rb +79 -26
  66. data/lib/poolparty/plugins/svn.rb +2 -2
  67. data/lib/poolparty/poolparty/cloud.rb +101 -47
  68. data/lib/poolparty/poolparty/default.rb +25 -23
  69. data/lib/poolparty/poolparty/key.rb +18 -27
  70. data/lib/poolparty/poolparty/neighborhoods.rb +1 -1
  71. data/lib/poolparty/poolparty/plugin.rb +17 -7
  72. data/lib/poolparty/poolparty/pool.rb +3 -3
  73. data/lib/poolparty/poolparty/poolparty_base_class.rb +41 -35
  74. data/lib/poolparty/poolparty/resource.rb +18 -44
  75. data/lib/poolparty/poolparty/service.rb +1 -5
  76. data/lib/poolparty/provision/boot_strapper.rb +24 -19
  77. data/lib/poolparty/provision/configurations/chef.rb +4 -4
  78. data/lib/poolparty/provision/dr_configure.rb +11 -10
  79. data/lib/poolparty/resources/cron.rb +7 -3
  80. data/lib/poolparty/resources/directory.rb +7 -0
  81. data/lib/poolparty/resources/exec.rb +2 -1
  82. data/lib/poolparty/resources/file.rb +23 -9
  83. data/lib/poolparty/resources/group.rb +21 -0
  84. data/lib/poolparty/resources/host.rb +2 -1
  85. data/lib/poolparty/resources/mount.rb +0 -4
  86. data/lib/poolparty/resources/remote_file.rb +1 -1
  87. data/lib/poolparty/resources/service.rb +2 -1
  88. data/lib/poolparty/resources/sshkey.rb +10 -12
  89. data/lib/poolparty/resources/symlink.rb +3 -5
  90. data/lib/poolparty/resources/user.rb +5 -0
  91. data/lib/poolparty/resources/variable.rb +1 -1
  92. data/lib/poolparty/templates/haproxy.conf +1 -1
  93. data/lib/poolparty/verification/verify.rb +4 -0
  94. data/lib/poolparty.rb +9 -4
  95. data/lib/poolpartycl.rb +1 -52
  96. data/spec/bin/bin_spec_helper.rb +1 -0
  97. data/spec/bin/server-list-active_spec.rb +3 -3
  98. data/spec/poolparty/core/object_spec.rb +9 -46
  99. data/spec/poolparty/dependency_resolver/dependency_resolver_cloud_extensions_spec.rb +18 -12
  100. data/spec/poolparty/fixtures/clouds.json +1 -1
  101. data/spec/poolparty/helpers/optioner_spec.rb +4 -11
  102. data/spec/poolparty/modules/cloud_resourcer_spec.rb +1 -1
  103. data/spec/poolparty/net/remote_instance_spec.rb +0 -1
  104. data/spec/poolparty/net/remoter_base_spec.rb +7 -11
  105. data/spec/poolparty/net/remoter_bases/ec2_mocks_and_stubs.rb +9 -11
  106. data/spec/poolparty/net/remoter_bases/ec2_remote_instance_spec.rb +6 -44
  107. data/spec/poolparty/net/remoter_bases/ec2_spec.rb +2 -3
  108. data/spec/poolparty/net/remoter_spec.rb +2 -3
  109. data/spec/poolparty/plugins/git_spec.rb +5 -8
  110. data/spec/poolparty/poolparty/cloud_spec.rb +17 -24
  111. data/spec/poolparty/poolparty/configurers/files/ruby_basic.rb +1 -3
  112. data/spec/poolparty/poolparty/configurers/ruby_spec.rb +1 -6
  113. data/spec/poolparty/poolparty/default_spec.rb +23 -22
  114. data/spec/poolparty/poolparty/example_spec.rb +6 -5
  115. data/spec/poolparty/poolparty/plugin_model_spec.rb +7 -4
  116. data/spec/poolparty/poolparty/pool_spec.rb +2 -9
  117. data/spec/poolparty/poolparty/resource_spec.rb +10 -24
  118. data/spec/poolparty/poolparty/script_spec.rb +1 -4
  119. data/spec/poolparty/poolparty/test_plugins/webserver.rb +0 -2
  120. data/spec/poolparty/resources/file_spec.rb +4 -4
  121. data/spec/poolparty/resources/service_spec.rb +1 -1
  122. data/spec/poolparty/spec_helper.rb +9 -38
  123. data/tasks/spec.rake +6 -2
  124. data/test/fixtures/test_key +1 -0
  125. data/test/poolparty/core/object_test.rb +29 -0
  126. data/test/poolparty/dependency_resolver/puppet_resolver_test.rb +1 -1
  127. data/test/poolparty/modules/cloud_dsl_test.rb +4 -4
  128. data/test/poolparty/net/remoter_bases/metavirt/metavirt_test.rb +55 -0
  129. data/test/poolparty/net/remoter_bases/vmrun/vmrun_test.rb +41 -14
  130. data/test/poolparty/plugins/chef_deploy_test.rb +37 -0
  131. data/test/poolparty/plugins/rails_deploy_test.rb +50 -0
  132. data/test/poolparty/poolparty/plugin_test.rb +18 -0
  133. data/test/poolparty/poolparty/poolparty_base_class_test.rb +41 -3
  134. data/test/test_helper.rb +5 -1
  135. data/vendor/gems/dslify/LICENSE +20 -0
  136. data/vendor/gems/dslify/README.rdoc +33 -0
  137. data/vendor/gems/dslify/Rakefile +56 -56
  138. data/vendor/gems/dslify/lib/dslify.rb +76 -5
  139. data/vendor/gems/dslify/test/dslify_test.rb +197 -0
  140. data/vendor/gems/dslify/test/test_helper.rb +7 -0
  141. data/vendor/gems/git-style-binaries/README.markdown +274 -0
  142. data/vendor/gems/{butterfly → git-style-binaries}/Rakefile +64 -62
  143. data/vendor/gems/git-style-binaries/VERSION.yml +4 -0
  144. data/vendor/gems/git-style-binaries/doc/EXAMPLES +1 -0
  145. data/vendor/gems/git-style-binaries/doc/poolparty-binaries.screenplay +45 -0
  146. data/vendor/gems/git-style-binaries/git-style-binaries.gemspec +69 -0
  147. data/vendor/gems/git-style-binaries/lib/ext/colorize.rb +198 -0
  148. data/vendor/gems/git-style-binaries/lib/ext/core.rb +16 -0
  149. data/vendor/gems/git-style-binaries/lib/git-style-binary/autorunner.rb +21 -0
  150. data/vendor/gems/git-style-binaries/lib/git-style-binary/command.rb +204 -0
  151. data/vendor/gems/git-style-binaries/lib/git-style-binary/commands/help.rb +32 -0
  152. data/vendor/gems/git-style-binaries/lib/git-style-binary/helpers/name_resolver.rb +78 -0
  153. data/vendor/gems/git-style-binaries/lib/git-style-binary/helpers/pager.rb +37 -0
  154. data/vendor/gems/git-style-binaries/lib/git-style-binary/parser.rb +223 -0
  155. data/vendor/gems/git-style-binaries/lib/git-style-binary.rb +74 -0
  156. data/vendor/gems/git-style-binaries/test/fixtures/flickr +4 -0
  157. data/vendor/gems/git-style-binaries/test/fixtures/flickr-download +17 -0
  158. data/vendor/gems/git-style-binaries/test/fixtures/wordpress +42 -0
  159. data/vendor/gems/git-style-binaries/test/fixtures/wordpress-categories +18 -0
  160. data/vendor/gems/git-style-binaries/test/fixtures/wordpress-list +18 -0
  161. data/vendor/gems/git-style-binaries/test/fixtures/wordpress-post +26 -0
  162. data/vendor/gems/git-style-binaries/test/git-style-binary/command_test.rb +17 -0
  163. data/vendor/gems/git-style-binaries/test/git_style_binary_test.rb +21 -0
  164. data/vendor/gems/git-style-binaries/test/running_binaries_test.rb +224 -0
  165. data/vendor/gems/git-style-binaries/test/shoulda_macros/matching_stdio.rb +13 -0
  166. data/vendor/gems/git-style-binaries/test/test_helper.rb +28 -0
  167. data/vendor/gems/parenting/lib/parenting/parenting.rb +13 -4
  168. data/vendor/gems/rest-client/README.rdoc +151 -0
  169. data/vendor/gems/rest-client/Rakefile +85 -0
  170. data/vendor/gems/rest-client/bin/restclient +87 -0
  171. data/vendor/gems/rest-client/lib/rest_client.rb +2 -0
  172. data/vendor/gems/rest-client/lib/restclient/exceptions.rb +84 -0
  173. data/vendor/gems/rest-client/lib/restclient/mixin/response.rb +43 -0
  174. data/vendor/gems/rest-client/lib/restclient/raw_response.rb +30 -0
  175. data/vendor/gems/rest-client/lib/restclient/request.rb +232 -0
  176. data/vendor/gems/rest-client/lib/restclient/resource.rb +146 -0
  177. data/vendor/gems/rest-client/lib/restclient/response.rb +20 -0
  178. data/vendor/gems/rest-client/lib/restclient.rb +93 -0
  179. data/vendor/gems/rest-client/rest-client.gemspec +21 -0
  180. data/vendor/gems/rest-client/spec/base.rb +4 -0
  181. data/vendor/gems/rest-client/spec/exceptions_spec.rb +54 -0
  182. data/vendor/gems/rest-client/spec/mixin/response_spec.rb +46 -0
  183. data/vendor/gems/rest-client/spec/raw_response_spec.rb +17 -0
  184. data/vendor/gems/rest-client/spec/request_spec.rb +442 -0
  185. data/vendor/gems/rest-client/spec/resource_spec.rb +75 -0
  186. data/vendor/gems/rest-client/spec/response_spec.rb +16 -0
  187. data/vendor/gems/rest-client/spec/restclient_spec.rb +53 -0
  188. data/vendor/gems/trollop/FAQ.txt +35 -0
  189. data/vendor/gems/trollop/History.txt +97 -0
  190. data/vendor/gems/trollop/Manifest.txt +7 -0
  191. data/vendor/gems/trollop/README.txt +40 -0
  192. data/vendor/gems/trollop/Rakefile +36 -0
  193. data/vendor/gems/trollop/lib/trollop.rb +735 -0
  194. data/vendor/gems/trollop/release-script.txt +13 -0
  195. data/vendor/gems/trollop/test/test_trollop.rb +1042 -0
  196. data/vendor/gems/trollop/www/index.html +167 -0
  197. metadata +100 -70
  198. data/bin/cloud-describe +0 -28
  199. data/bin/cloud-handle-load +0 -27
  200. data/bin/cloud-rsync +0 -28
  201. data/bin/cloud-spec +0 -40
  202. data/lib/poolparty/plugins/nanite.rb +0 -41
  203. data/lib/poolparty/plugins/runit.rb +0 -96
  204. data/vendor/gems/butterfly/History.txt +0 -4
  205. data/vendor/gems/butterfly/PostInstall.txt +0 -2
  206. data/vendor/gems/butterfly/README.rdoc +0 -48
  207. data/vendor/gems/butterfly/VERSION.yml +0 -4
  208. data/vendor/gems/butterfly/bin/flutter +0 -4
  209. data/vendor/gems/butterfly/butterfly.gemspec +0 -37
  210. data/vendor/gems/butterfly/examples/config.ru +0 -15
  211. data/vendor/gems/butterfly/examples/my_app.rb +0 -12
  212. data/vendor/gems/butterfly/lib/butterfly.rb +0 -14
  213. data/vendor/gems/butterfly/lib/handler.rb +0 -48
  214. data/vendor/gems/butterfly/lib/request.rb +0 -29
  215. data/vendor/gems/butterfly/lib/response.rb +0 -49
  216. data/vendor/gems/butterfly/script/console +0 -10
  217. data/vendor/gems/butterfly/script/destroy +0 -14
  218. data/vendor/gems/butterfly/script/generate +0 -14
  219. data/vendor/gems/butterfly/test/test_adapter_base.rb +0 -23
  220. data/vendor/gems/butterfly/test/test_butterfly_request.rb +0 -46
  221. data/vendor/gems/butterfly/test/test_butterfly_response.rb +0 -43
  222. data/vendor/gems/butterfly/test/test_butterfly_server.rb +0 -16
  223. data/vendor/gems/butterfly/test/test_default.rb +0 -12
  224. data/vendor/gems/butterfly/test/test_helper.rb +0 -6
  225. data/vendor/gems/dslify/History.txt +0 -4
  226. data/vendor/gems/dslify/Manifest.txt +0 -25
  227. data/vendor/gems/dslify/PostInstall.txt +0 -5
  228. data/vendor/gems/dslify/README.txt +0 -60
  229. data/vendor/gems/dslify/config/hoe.rb +0 -73
  230. data/vendor/gems/dslify/config/requirements.rb +0 -15
  231. data/vendor/gems/dslify/dslify.gemspec +0 -40
  232. data/vendor/gems/dslify/lib/dslify/dslify.rb +0 -76
  233. data/vendor/gems/dslify/lib/dslify/version.rb +0 -10
  234. data/vendor/gems/dslify/script/console +0 -10
  235. data/vendor/gems/dslify/script/destroy +0 -14
  236. data/vendor/gems/dslify/script/generate +0 -14
  237. data/vendor/gems/dslify/script/txt2html +0 -82
  238. data/vendor/gems/dslify/setup.rb +0 -1585
  239. data/vendor/gems/dslify/tasks/deployment.rake +0 -34
  240. data/vendor/gems/dslify/tasks/environment.rake +0 -7
  241. data/vendor/gems/dslify/tasks/website.rake +0 -17
  242. data/vendor/gems/dslify/test/test_dslify.rb +0 -138
  243. data/vendor/gems/dslify/website/index.html +0 -86
  244. data/vendor/gems/dslify/website/index.txt +0 -83
  245. data/vendor/gems/dslify/website/javascripts/rounded_corners_lite.inc.js +0 -285
  246. data/vendor/gems/dslify/website/stylesheets/screen.css +0 -138
  247. data/vendor/gems/dslify/website/template.html.erb +0 -48
@@ -0,0 +1,232 @@
1
+ require 'tempfile'
2
+
3
+ module RestClient
4
+ # This class is used internally by RestClient to send the request, but you can also
5
+ # access it internally if you'd like to use a method not directly supported by the
6
+ # main API. For example:
7
+ #
8
+ # RestClient::Request.execute(:method => :head, :url => 'http://example.com')
9
+ #
10
+ class Request
11
+ attr_reader :method, :url, :payload, :headers,
12
+ :cookies, :user, :password, :timeout, :open_timeout,
13
+ :verify_ssl, :ssl_client_cert, :ssl_client_key, :ssl_ca_file,
14
+ :raw_response
15
+
16
+ def self.execute(args)
17
+ new(args).execute
18
+ end
19
+
20
+ def initialize(args)
21
+ @method = args[:method] or raise ArgumentError, "must pass :method"
22
+ @url = args[:url] or raise ArgumentError, "must pass :url"
23
+ @headers = args[:headers] || {}
24
+ @cookies = @headers.delete(:cookies) || args[:cookies] || {}
25
+ @payload = process_payload(args[:payload])
26
+ @user = args[:user]
27
+ @password = args[:password]
28
+ @timeout = args[:timeout]
29
+ @open_timeout = args[:open_timeout]
30
+ @raw_response = args[:raw_response] || false
31
+ @verify_ssl = args[:verify_ssl] || false
32
+ @ssl_client_cert = args[:ssl_client_cert] || nil
33
+ @ssl_client_key = args[:ssl_client_key] || nil
34
+ @ssl_ca_file = args[:ssl_ca_file] || nil
35
+ @tf = nil # If you are a raw request, this is your tempfile
36
+ end
37
+
38
+ def execute
39
+ execute_inner
40
+ rescue Redirect => e
41
+ @url = e.url
42
+ execute
43
+ end
44
+
45
+ def execute_inner
46
+ uri = parse_url_with_auth(url)
47
+ transmit uri, net_http_request_class(method).new(uri.request_uri, make_headers(headers)), payload
48
+ end
49
+
50
+ def make_headers(user_headers)
51
+ unless @cookies.empty?
52
+ user_headers[:cookie] = @cookies.map {|key, val| "#{key.to_s}=#{val}" }.join('; ')
53
+ end
54
+
55
+ default_headers.merge(user_headers).inject({}) do |final, (key, value)|
56
+ final[key.to_s.gsub(/_/, '-').capitalize] = value.to_s
57
+ final
58
+ end
59
+ end
60
+
61
+ def net_http_class
62
+ if RestClient.proxy
63
+ proxy_uri = URI.parse(RestClient.proxy)
64
+ Net::HTTP::Proxy(proxy_uri.host, proxy_uri.port, proxy_uri.user, proxy_uri.password)
65
+ else
66
+ Net::HTTP
67
+ end
68
+ end
69
+
70
+ def net_http_request_class(method)
71
+ Net::HTTP.const_get(method.to_s.capitalize)
72
+ end
73
+
74
+ def parse_url(url)
75
+ url = "http://#{url}" unless url.match(/^http/)
76
+ URI.parse(url)
77
+ end
78
+
79
+ def parse_url_with_auth(url)
80
+ uri = parse_url(url)
81
+ @user = uri.user if uri.user
82
+ @password = uri.password if uri.password
83
+ uri
84
+ end
85
+
86
+ def process_payload(p=nil, parent_key=nil)
87
+ unless p.is_a?(Hash)
88
+ p
89
+ else
90
+ @headers[:content_type] ||= 'application/x-www-form-urlencoded'
91
+ p.keys.map do |k|
92
+ key = parent_key ? "#{parent_key}[#{k}]" : k
93
+ if p[k].is_a? Hash
94
+ process_payload(p[k], key)
95
+ else
96
+ value = URI.escape(p[k].to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
97
+ "#{key}=#{value}"
98
+ end
99
+ end.join("&")
100
+ end
101
+ end
102
+
103
+ def transmit(uri, req, payload)
104
+ setup_credentials(req)
105
+
106
+ net = net_http_class.new(uri.host, uri.port)
107
+ net.use_ssl = uri.is_a?(URI::HTTPS)
108
+ net.verify_mode = OpenSSL::SSL::VERIFY_NONE if @verify_ssl == false
109
+ net.cert = @ssl_client_cert if @ssl_client_cert
110
+ net.key = @ssl_client_key if @ssl_client_key
111
+ net.ca_file = @ssl_ca_file if @ssl_ca_file
112
+ net.read_timeout = @timeout if @timeout
113
+ net.open_timeout = @open_timeout if @open_timeout
114
+
115
+ display_log request_log
116
+
117
+ net.start do |http|
118
+ res = http.request(req, payload) { |http_response| fetch_body(http_response) }
119
+ result = process_result(res)
120
+ display_log response_log(res)
121
+
122
+ if result.kind_of?(String) or @method == :head
123
+ Response.new(result, res)
124
+ elsif @raw_response
125
+ RawResponse.new(@tf, res)
126
+ else
127
+ nil
128
+ end
129
+ end
130
+ rescue EOFError
131
+ raise RestClient::ServerBrokeConnection
132
+ rescue Timeout::Error
133
+ raise RestClient::RequestTimeout
134
+ end
135
+
136
+ def setup_credentials(req)
137
+ req.basic_auth(user, password) if user
138
+ end
139
+
140
+ def fetch_body(http_response)
141
+ if @raw_response
142
+ # Taken from Chef, which as in turn...
143
+ # Stolen from http://www.ruby-forum.com/topic/166423
144
+ # Kudos to _why!
145
+ @tf = Tempfile.new("rest-client")
146
+ size, total = 0, http_response.header['Content-Length'].to_i
147
+ http_response.read_body do |chunk|
148
+ @tf.write(chunk)
149
+ size += chunk.size
150
+ if size == 0
151
+ display_log("#{@method} #{@url} done (0 length file)")
152
+ elsif total == 0
153
+ display_log("#{@method} #{@url} (zero content length)")
154
+ else
155
+ display_log("#{@method} #{@url} %d%% done (%d of %d)" % [(size * 100) / total, size, total])
156
+ end
157
+ end
158
+ @tf.close
159
+ @tf
160
+ else
161
+ http_response.read_body
162
+ end
163
+ http_response
164
+ end
165
+
166
+ def process_result(res)
167
+ if res.code =~ /\A2\d{2}\z/
168
+ # We don't decode raw requests
169
+ unless @raw_response
170
+ decode res['content-encoding'], res.body if res.body
171
+ end
172
+ elsif %w(301 302 303).include? res.code
173
+ url = res.header['Location']
174
+
175
+ if url !~ /^http/
176
+ uri = URI.parse(@url)
177
+ uri.path = "/#{url}".squeeze('/')
178
+ url = uri.to_s
179
+ end
180
+
181
+ raise Redirect, url
182
+ elsif res.code == "304"
183
+ raise NotModified, res
184
+ elsif res.code == "401"
185
+ raise Unauthorized, res
186
+ elsif res.code == "404"
187
+ raise ResourceNotFound, res
188
+ else
189
+ raise RequestFailed, res
190
+ end
191
+ end
192
+
193
+ def decode(content_encoding, body)
194
+ if content_encoding == 'gzip' and not body.empty?
195
+ Zlib::GzipReader.new(StringIO.new(body)).read
196
+ elsif content_encoding == 'deflate'
197
+ Zlib::Inflate.new.inflate(body)
198
+ else
199
+ body
200
+ end
201
+ end
202
+
203
+ def request_log
204
+ out = []
205
+ out << "RestClient.#{method} #{url.inspect}"
206
+ out << (payload.size > 100 ? "(#{payload.size} byte payload)".inspect : payload.inspect) if payload
207
+ out << headers.inspect.gsub(/^\{/, '').gsub(/\}$/, '') unless headers.empty?
208
+ out.join(', ')
209
+ end
210
+
211
+ def response_log(res)
212
+ size = @raw_response ? File.size(@tf.path) : res.body.size
213
+ "# => #{res.code} #{res.class.to_s.gsub(/^Net::HTTP/, '')} | #{(res['Content-type'] || '').gsub(/;.*$/, '')} #{size} bytes"
214
+ end
215
+
216
+ def display_log(msg)
217
+ return unless log_to = RestClient.log
218
+
219
+ if log_to == 'stdout'
220
+ STDOUT.puts msg
221
+ elsif log_to == 'stderr'
222
+ STDERR.puts msg
223
+ else
224
+ File.open(log_to, 'a') { |f| f.puts msg }
225
+ end
226
+ end
227
+
228
+ def default_headers
229
+ { :accept => 'application/xml', :accept_encoding => 'gzip, deflate' }
230
+ end
231
+ end
232
+ end
@@ -0,0 +1,146 @@
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
38
+
39
+ def initialize(url, options={}, backwards_compatibility=nil)
40
+ @url = url
41
+ if options.class == Hash
42
+ @options = options
43
+ else # compatibility with previous versions
44
+ @options = { :user => options, :password => backwards_compatibility }
45
+ end
46
+ end
47
+
48
+ def get(additional_headers={})
49
+ Request.execute(options.merge(
50
+ :method => :get,
51
+ :url => url,
52
+ :headers => headers.merge(additional_headers)
53
+ ))
54
+ end
55
+
56
+ def post(payload, additional_headers={})
57
+ Request.execute(options.merge(
58
+ :method => :post,
59
+ :url => url,
60
+ :payload => payload,
61
+ :headers => headers.merge(additional_headers)
62
+ ))
63
+ end
64
+
65
+ def put(payload, additional_headers={})
66
+ Request.execute(options.merge(
67
+ :method => :put,
68
+ :url => url,
69
+ :payload => payload,
70
+ :headers => headers.merge(additional_headers)
71
+ ))
72
+ end
73
+
74
+ def delete(additional_headers={})
75
+ Request.execute(options.merge(
76
+ :method => :delete,
77
+ :url => url,
78
+ :headers => headers.merge(additional_headers)
79
+ ))
80
+ end
81
+
82
+ def to_s
83
+ url
84
+ end
85
+
86
+ def user
87
+ options[:user]
88
+ end
89
+
90
+ def password
91
+ options[:password]
92
+ end
93
+
94
+ def headers
95
+ options[:headers] || {}
96
+ end
97
+
98
+ def timeout
99
+ options[:timeout]
100
+ end
101
+
102
+ def open_timeout
103
+ options[:open_timeout]
104
+ end
105
+
106
+ # Construct a subresource, preserving authentication.
107
+ #
108
+ # Example:
109
+ #
110
+ # site = RestClient::Resource.new('http://example.com', 'adam', 'mypasswd')
111
+ # site['posts/1/comments'].post 'Good article.', :content_type => 'text/plain'
112
+ #
113
+ # This is especially useful if you wish to define your site in one place and
114
+ # call it in multiple locations:
115
+ #
116
+ # def orders
117
+ # RestClient::Resource.new('http://example.com/orders', 'admin', 'mypasswd')
118
+ # end
119
+ #
120
+ # orders.get # GET http://example.com/orders
121
+ # orders['1'].get # GET http://example.com/orders/1
122
+ # orders['1/items'].delete # DELETE http://example.com/orders/1/items
123
+ #
124
+ # Nest resources as far as you want:
125
+ #
126
+ # site = RestClient::Resource.new('http://example.com')
127
+ # posts = site['posts']
128
+ # first_post = posts['1']
129
+ # comments = first_post['comments']
130
+ # comments.post 'Hello', :content_type => 'text/plain'
131
+ #
132
+ def [](suburl)
133
+ self.class.new(concat_urls(url, suburl), options)
134
+ end
135
+
136
+ def concat_urls(url, suburl) # :nodoc:
137
+ url = url.to_s
138
+ suburl = suburl.to_s
139
+ if url.slice(-1, 1) == '/' or suburl.slice(0, 1) == '/'
140
+ url + suburl
141
+ else
142
+ "#{url}/#{suburl}"
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,20 @@
1
+ require File.dirname(__FILE__) + '/mixin/response'
2
+
3
+ module RestClient
4
+ # The response from RestClient looks like a string, but is actually one of
5
+ # these. 99% of the time you're making a rest call all you care about is
6
+ # the body, but on the occassion you want to fetch the headers you can:
7
+ #
8
+ # RestClient.get('http://example.com').headers[:content_type]
9
+ #
10
+ class Response < String
11
+
12
+ include RestClient::Mixin::Response
13
+
14
+ def initialize(string, net_http_res)
15
+ @net_http_res = net_http_res
16
+ super(string || "")
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,93 @@
1
+ require 'uri'
2
+ require 'net/https'
3
+ require 'zlib'
4
+ require 'stringio'
5
+
6
+ require File.dirname(__FILE__) + '/restclient/request'
7
+ require File.dirname(__FILE__) + '/restclient/mixin/response'
8
+ require File.dirname(__FILE__) + '/restclient/response'
9
+ require File.dirname(__FILE__) + '/restclient/raw_response'
10
+ require File.dirname(__FILE__) + '/restclient/resource'
11
+ require File.dirname(__FILE__) + '/restclient/exceptions'
12
+
13
+ # This module's static methods are the entry point for using the REST client.
14
+ #
15
+ # # GET
16
+ # xml = RestClient.get 'http://example.com/resource'
17
+ # jpg = RestClient.get 'http://example.com/resource', :accept => 'image/jpg'
18
+ #
19
+ # # authentication and SSL
20
+ # RestClient.get 'https://user:password@example.com/private/resource'
21
+ #
22
+ # # POST or PUT with a hash sends parameters as a urlencoded form body
23
+ # RestClient.post 'http://example.com/resource', :param1 => 'one'
24
+ #
25
+ # # nest hash parameters
26
+ # RestClient.post 'http://example.com/resource', :nested => { :param1 => 'one' }
27
+ #
28
+ # # POST and PUT with raw payloads
29
+ # RestClient.post 'http://example.com/resource', 'the post body', :content_type => 'text/plain'
30
+ # RestClient.post 'http://example.com/resource.xml', xml_doc
31
+ # RestClient.put 'http://example.com/resource.pdf', File.read('my.pdf'), :content_type => 'application/pdf'
32
+ #
33
+ # # DELETE
34
+ # RestClient.delete 'http://example.com/resource'
35
+ #
36
+ # # retreive the response http code and headers
37
+ # res = RestClient.get 'http://example.com/some.jpg'
38
+ # res.code # => 200
39
+ # res.headers[:content_type] # => 'image/jpg'
40
+ #
41
+ # # HEAD
42
+ # RestClient.head('http://example.com').headers
43
+ #
44
+ # To use with a proxy, just set RestClient.proxy to the proper http proxy:
45
+ #
46
+ # RestClient.proxy = "http://proxy.example.com/"
47
+ #
48
+ # Or inherit the proxy from the environment:
49
+ #
50
+ # RestClient.proxy = ENV['http_proxy']
51
+ #
52
+ # For live tests of RestClient, try using http://rest-test.heroku.com, which echoes back information about the rest call:
53
+ #
54
+ # >> RestClient.put 'http://rest-test.heroku.com/resource', :foo => 'baz'
55
+ # => "PUT http://rest-test.heroku.com/resource with a 7 byte payload, content type application/x-www-form-urlencoded {\"foo\"=>\"baz\"}"
56
+ #
57
+ module RestClient
58
+ def self.get(url, headers={})
59
+ Request.execute(:method => :get, :url => url, :headers => headers)
60
+ end
61
+
62
+ def self.post(url, payload, headers={})
63
+ Request.execute(:method => :post, :url => url, :payload => payload, :headers => headers)
64
+ end
65
+
66
+ def self.put(url, payload, headers={})
67
+ Request.execute(:method => :put, :url => url, :payload => payload, :headers => headers)
68
+ end
69
+
70
+ def self.delete(url, headers={})
71
+ Request.execute(:method => :delete, :url => url, :headers => headers)
72
+ end
73
+
74
+ def self.head(url, headers={})
75
+ Request.execute(:method => :head, :url => url, :headers => headers)
76
+ end
77
+
78
+ class << self
79
+ attr_accessor :proxy
80
+ end
81
+
82
+ # Print log of RestClient calls. Value can be stdout, stderr, or a filename.
83
+ # You can also configure logging by the environment variable RESTCLIENT_LOG.
84
+ def self.log=(log)
85
+ @@log = log
86
+ end
87
+
88
+ def self.log # :nodoc:
89
+ return ENV['RESTCLIENT_LOG'] if ENV['RESTCLIENT_LOG']
90
+ return @@log if defined? @@log
91
+ nil
92
+ end
93
+ end
@@ -0,0 +1,21 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "rest-client"
3
+ s.version = "0.9.2"
4
+ s.summary = "Simple REST client for Ruby, inspired by microframework syntax for specifying actions."
5
+ s.description = "A simple REST client for Ruby, inspired by the Sinatra microframework style of specifying actions: get, put, post, delete."
6
+ s.author = "Adam Wiggins"
7
+ s.email = "adam@heroku.com"
8
+ s.rubyforge_project = "rest-client"
9
+ s.homepage = "http://rest-client.heroku.com/"
10
+ s.has_rdoc = true
11
+ s.platform = Gem::Platform::RUBY
12
+ s.files = %w(Rakefile README.rdoc rest-client.gemspec
13
+ lib/rest_client.rb lib/restclient.rb
14
+ lib/restclient/request.rb lib/restclient/response.rb
15
+ lib/restclient/exceptions.rb lib/restclient/resource.rb
16
+ spec/base.rb spec/request_spec.rb spec/response_spec.rb
17
+ spec/exceptions_spec.rb spec/resource_spec.rb spec/restclient_spec.rb
18
+ bin/restclient)
19
+ s.executables = ['restclient']
20
+ s.require_path = "lib"
21
+ end
@@ -0,0 +1,4 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+
4
+ require File.dirname(__FILE__) + '/../lib/restclient'
@@ -0,0 +1,54 @@
1
+ require File.dirname(__FILE__) + '/base'
2
+
3
+ describe RestClient::Exception do
4
+ it "sets the exception message to ErrorMessage" do
5
+ RestClient::ResourceNotFound.new.message.should == 'Resource not found'
6
+ end
7
+
8
+ it "contains exceptions in RestClient" do
9
+ RestClient::Unauthorized.new.should be_a_kind_of(RestClient::Exception)
10
+ RestClient::ServerBrokeConnection.new.should be_a_kind_of(RestClient::Exception)
11
+ end
12
+ end
13
+
14
+ describe RestClient::RequestFailed do
15
+ it "stores the http response on the exception" do
16
+ begin
17
+ raise RestClient::RequestFailed, :response
18
+ rescue RestClient::RequestFailed => e
19
+ e.response.should == :response
20
+ end
21
+ end
22
+
23
+ it "http_code convenience method for fetching the code as an integer" do
24
+ RestClient::RequestFailed.new(mock('res', :code => '502')).http_code.should == 502
25
+ end
26
+
27
+ it "shows the status code in the message" do
28
+ RestClient::RequestFailed.new(mock('res', :code => '502')).to_s.should match(/502/)
29
+ end
30
+ end
31
+
32
+ describe RestClient::ResourceNotFound do
33
+ it "also has the http response attached" do
34
+ begin
35
+ raise RestClient::ResourceNotFound, :response
36
+ rescue RestClient::ResourceNotFound => e
37
+ e.response.should == :response
38
+ end
39
+ end
40
+ end
41
+
42
+ describe "backwards compatibility" do
43
+ it "alias RestClient::Request::Redirect to RestClient::Redirect" do
44
+ RestClient::Request::Redirect.should == RestClient::Redirect
45
+ end
46
+
47
+ it "alias RestClient::Request::Unauthorized to RestClient::Unauthorized" do
48
+ RestClient::Request::Unauthorized.should == RestClient::Unauthorized
49
+ end
50
+
51
+ it "alias RestClient::Request::RequestFailed to RestClient::RequestFailed" do
52
+ RestClient::Request::RequestFailed.should == RestClient::RequestFailed
53
+ end
54
+ end
@@ -0,0 +1,46 @@
1
+ require File.dirname(__FILE__) + '/../base'
2
+
3
+ class MockResponse
4
+ include RestClient::Mixin::Response
5
+
6
+ def initialize(body, res)
7
+ @net_http_res = res
8
+ @body = @body
9
+ end
10
+ end
11
+
12
+ describe RestClient::Mixin::Response do
13
+ before do
14
+ @net_http_res = mock('net http response')
15
+ @response = MockResponse.new('abc', @net_http_res)
16
+ end
17
+
18
+ it "fetches the numeric response code" do
19
+ @net_http_res.should_receive(:code).and_return('200')
20
+ @response.code.should == 200
21
+ end
22
+
23
+ it "beautifies the headers by turning the keys to symbols" do
24
+ h = RestClient::Response.beautify_headers('content-type' => [ 'x' ])
25
+ h.keys.first.should == :content_type
26
+ end
27
+
28
+ it "beautifies the headers by turning the values to strings instead of one-element arrays" do
29
+ h = RestClient::Response.beautify_headers('x' => [ 'text/html' ] )
30
+ h.values.first.should == 'text/html'
31
+ end
32
+
33
+ it "fetches the headers" do
34
+ @net_http_res.should_receive(:to_hash).and_return('content-type' => [ 'text/html' ])
35
+ @response.headers.should == { :content_type => 'text/html' }
36
+ end
37
+
38
+ it "extracts cookies from response headers" do
39
+ @net_http_res.should_receive(:to_hash).and_return('set-cookie' => ['session_id=1; path=/'])
40
+ @response.cookies.should == { 'session_id' => '1' }
41
+ end
42
+
43
+ it "can access the net http result directly" do
44
+ @response.net_http_res.should == @net_http_res
45
+ end
46
+ end
@@ -0,0 +1,17 @@
1
+ require File.dirname(__FILE__) + '/base'
2
+
3
+ describe RestClient::RawResponse do
4
+ before do
5
+ @tf = mock("Tempfile", :read => "the answer is 42", :open => true)
6
+ @net_http_res = mock('net http response')
7
+ @response = RestClient::RawResponse.new(@tf, @net_http_res)
8
+ end
9
+
10
+ it "behaves like string" do
11
+ @response.to_s.should == 'the answer is 42'
12
+ end
13
+
14
+ it "exposes a Tempfile" do
15
+ @response.file.should == @tf
16
+ end
17
+ end