troelskn-handsoap 0.5.2 → 0.5.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.markdown CHANGED
@@ -15,6 +15,8 @@ Handsoap is a library for creating SOAP clients in Ruby.
15
15
 
16
16
  API docs are at [http://rdoc.info/projects/troelskn/handsoap](http://rdoc.info/projects/troelskn/handsoap)
17
17
 
18
+ Some usage information is to be found in [the wiki](http://wiki.github.com/troelskn/handsoap).
19
+
18
20
  ![Handsoap](http://ny-image0.etsy.com/il_430xN.68558416.jpg)
19
21
 
20
22
  Why
@@ -70,180 +72,26 @@ The protocol also contains a large and unwieldy specification of how to do the (
70
72
 
71
73
  Handsoap only supports RPC-style SOAP. This seems to be the most common style. It's probably possible to add support for Document-style with little effort, but until I see the need I'm not going there.
72
74
 
73
- API documnetation
75
+ API documentation
74
76
  ---
75
77
 
76
78
  In addition to this guide, there's autogenerated API documentation available at [http://rdoc.info/projects/troelskn/handsoap](http://rdoc.info/projects/troelskn/handsoap)
77
79
 
78
- The toolbox
80
+ Getting started
79
81
  ---
80
82
 
81
- The Handsoap toolbox consists of the following components.
82
-
83
- Handsoap can use either [curb](http://curb.rubyforge.org/) or [httpclient](http://dev.ctor.org/http-access2) for HTTP-connectivity. The former is recommended, and default, but for portability you might choose the latter. You usually don't need to interact at the HTTP-level, but if you do (for example, if you have to use SSL), you can.
83
+ For getting started with Handsoap, you should read [the guide in the wiki](http://wiki.github.com/troelskn/handsoap/recommendations).
84
84
 
85
- For parsing XML, Handsoap defaults to use [Nokogiri](http://github.com/tenderlove/nokogiri/tree/master). Handsoap has an abstraction layer, so that you can switch between REXML, Nokogiri and ruby-libxml. Besides providing portability between these parsers, Handsop also gives some helper functions that are meaningful when parsing SOAP envelopes.
86
-
87
- There is also a library for generating XML, which you'll use when mapping from Ruby to SOAP. It's quite similar to [Builder](http://builder.rubyforge.org/), but is tailored towards being used for writing SOAP-messages. The name of this library is `XmlMason` and it is included/part of Handsoap.
88
-
89
- Recommendations
85
+ The toolbox
90
86
  ---
91
87
 
92
- ###Workflow
93
-
94
- 1. You need a WSDL for the service you want to consume.
95
-
96
- 2. Figure out the url for the endpoint, as well as the protocol version. Put this in a config file.
97
- * To find the endpoint, look inside the wsdl, for `<soap:address location="..">`
98
-
99
- 3. Create a service class. Add endpoints and protocol. Alias needed namespace(s).
100
- * To find the namespace(s), look in the samples from soapUI. It will be imported as `v1`
101
- * Note that you can now use the provided generator to skip this step.
102
-
103
- 4. Open the wsdl in [soapUI](http://www.soapui.org/).
104
-
105
- 5. In soapUI, find a sample request for the method you want to use. Copy+paste the body-part.
106
-
107
- 6. Create a method in your service class (Use ruby naming convention)
108
-
109
- 7. Write Ruby-code (using XmlMason) to generate a request that is similar to the example from soapUI. (In your copy+paste buffer)
110
-
111
- 8. Write Ruby-code to parse the response (an XML-document) into Ruby data types.
112
-
113
- 9. Write an integration test to verify that your method works as expected. You can use soapUI to [generate a mock-service](http://www.soapui.org/userguide/mock/getting_started.html).
114
-
115
- Repeat point 5..9 for each method that you need to use.
116
- Between each iteration, you should refactor shared code into helper functions.
117
-
118
- ###Configuration
119
-
120
- If you use Rails, you should put the endpoint in a constant in the environment file. That way, you can have different endpoints for test/development/production/etc.
121
-
122
- If you don't use Rails, it's still a good idea to move this information to a config file.
123
-
124
- The configuration could look like this:
125
-
126
- # wsdl: http://example.org/ws/service?WSDL
127
- EXAMPLE_SERVICE_ENDPOINT = {
128
- :uri => 'http://example.org/ws/service',
129
- :version => 2
130
- }
131
-
132
- If you use Rails, you will need to load the gem from the `config/environment.rb` file, using:
133
-
134
- config.gem 'troelskn-handsoap', :lib => 'handsoap', :source => "http://gems.github.com"
135
-
136
- If you use the standard development environment of Rails, you may run into troubles with cached classes. Add the following line to the initializer:
137
-
138
- ActiveSupport::Dependencies.explicitly_unloadable_constants << 'Handsoap::Service'
139
-
140
- ###Generator
141
-
142
- From version 0.2.0 Handsoap sports a generator, that creates the service class + an integration test case. This is just a rough starting point for your service - You still have to fill out the actual mappings to/from xml, but at least it saves your some copy-pasting from this guide.
143
-
144
- To use the generator, create a Rails project and run the script `script/generate handsoap`, giving the url of the WSDL.
145
-
146
- ###Service class
147
-
148
- Put your service in a file under `app/models`. You should extend `Handsoap::Service`.
149
-
150
- You need to provide the endpoint and the SOAP version (1.1 or 1.2). If in doubt, use version 2.
151
-
152
- A service usually has a namespace for describing the message-body ([RPC/Litteral style](http://www.ibm.com/developerworks/webservices/library/ws-whichwsdl/#N1011F)). You should set this in the `on_create_document` handler. Likewise, the response returned *from* the server will contain elements that typically are defined in a single namespace relevant to the service. You can register this in the handler `on_response_document`.
153
-
154
- A typical service looks like the following:
155
-
156
- # -*- coding: utf-8 -*-
157
- require 'handsoap'
158
-
159
- class Example::FooService < Handsoap::Service
160
- endpoint EXAMPLE_SERVICE_ENDPOINT
161
- def on_create_document(doc)
162
- # register namespaces for the request
163
- doc.alias 'tns', "http://example.org/ws/spec"
164
- end
165
- def on_response_document(doc)
166
- # register namespaces for the response
167
- doc.add_namespace 'ns', 'http://example.org/ws/spec'
168
- end
169
-
170
- # public methods
171
- # todo
172
-
173
- private
174
- # helpers
175
- # todo
176
- end
177
-
178
- The above would go in the file `app/models/example/foo_service.rb`
179
-
180
- ###Integration tests
181
-
182
- Since you're writing mappings manually, it's a good idea to write tests that verify that the service works. If you use standard Rails with `Test::Unit`, you should put these in an integration-test.
183
-
184
- For the sample service above, you would create a file in `test/integration/example/foo_service.rb`, with the following content:
185
-
186
- # -*- coding: utf-8 -*-
187
- require 'test_helper'
188
-
189
- # Example::FooService.logger = $stdout
190
-
191
- class Example::FooServiceTest < Test::Unit::TestCase
192
- def test_update_icon
193
- icon = { :href => 'http://www.example.com/icon.jpg', :type => 'image/jpeg' }
194
- result = Example::FooService.update_icon!(icon)
195
- assert_equal icon.type, result.type
196
- end
197
- end
198
-
199
- Note the commented-out line. If you set a logger on the service-class, you can see exactly which XML goes forth and back, which is very useful for debugging.
200
-
201
- ###Methods
202
-
203
- You should use Ruby naming-conventions for methods names. If the method has side-effects, you should postfix it with an exclamation.
204
- Repeat code inside the invoke-block, should be refactored out to *builders*, and the response should be parsed with a *parser*.
205
-
206
- def update_icon!(icon)
207
- response = invoke("tns:UpdateIcon") do |message|
208
- build_icon!(message, icon)
209
- end
210
- parse_icon(response/"//icon").first)
211
- end
212
-
213
-
214
- ###Helpers
215
-
216
- You'll end up with two kinds of helpers; Ruby->XML transformers (aka. *builders*) and XML->Ruby transformers (aka. *parsers*).
217
- It's recommended that you stick to the following style/naming scheme:
218
-
219
- # icon -> xml
220
- def build_icon!(message, icon)
221
- message.add "icon" do |i|
222
- i.set_attr "href", icon[:href]
223
- i.set_attr "type", icon[:type]
224
- end
225
- end
226
-
227
- # xml -> icon
228
- def parse_icon(node)
229
- { :href => (node/"@href").to_s, :type => (node/"@type").to_s }
230
- end
88
+ The Handsoap toolbox consists of the following components.
231
89
 
232
- or, if you prefer, you can use a class to represent entities:
90
+ Handsoap can use either [curb](http://curb.rubyforge.org/), [Net::HTTP](http://www.ruby-doc.org/stdlib/libdoc/net/http/rdoc/index.html) or [httpclient](http://dev.ctor.org/http-access2) for HTTP-connectivity. The former is recommended, and default, but for portability you might choose one of the latter. You usually don't need to interact at the HTTP-level, but if you do (for example, if you have to use SSL), you can do so through a thin abstraction layer.
233
91
 
234
- # icon -> xml
235
- def build_icon!(message, icon)
236
- message.add "icon" do |i|
237
- i.set_attr "href", icon.href
238
- i.set_attr "type", icon.type
239
- end
240
- end
92
+ For parsing XML, Handsoap defaults to use [Nokogiri](http://github.com/tenderlove/nokogiri/tree/master). Handsoap has an abstraction layer, so that you can switch between REXML, Nokogiri and ruby-libxml. Besides providing portability between these parsers, Handsop also gives some helper functions that are meaningful when parsing SOAP envelopes.
241
93
 
242
- # xml -> icon
243
- def parse_icon(node)
244
- Icon.new :href => (node/"@href").to_s,
245
- :type => (node/"@type").to_s
246
- end
94
+ Finally, there is a library for generating XML, which you'll use when mapping from Ruby to SOAP. It's quite similar to [Builder](http://builder.rubyforge.org/), but is tailored towards being used for writing SOAP-messages. The name of this library is `XmlMason` and it is included/part of Handsoap.
247
95
 
248
96
  License
249
97
  ---
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
2
  :minor: 5
3
- :patch: 2
3
+ :patch: 3
4
4
  :major: 0
data/lib/handsoap/http.rb CHANGED
@@ -237,6 +237,29 @@ module Handsoap
237
237
  end
238
238
  end
239
239
 
240
+ # A mock driver for your testing needs.
241
+ #
242
+ # To use it, create a new instance and assign to +Handsoap::Http.drivers+. Then configure +Handsoap::Service+ to use it:
243
+ #
244
+ # Handsoap::Http.drivers[:mock] = Handsoap::Http::HttpMock.new :status => 200, :headers => headers, :content => body
245
+ # Handsoap.http_driver = :mock
246
+ #
247
+ # Remember that headers should use \r\n, rather than \n.
248
+ class HttpMock
249
+ attr_accessor :mock, :last_request, :is_loaded
250
+ def initialize(mock)
251
+ @mock = mock
252
+ @is_loaded = false
253
+ end
254
+ def load!
255
+ is_loaded = true
256
+ end
257
+ def send_http_request(request)
258
+ @last_request = request
259
+ Handsoap::Http.parse_http_part(mock[:headers], mock[:content], mock[:status], mock[:content_type])
260
+ end
261
+ end
262
+
240
263
  # Parses a raw http response into a +Response+ or +Part+ object.
241
264
  def self.parse_http_part(headers, body, status = nil, content_type = nil)
242
265
  if headers.kind_of? String
@@ -363,7 +386,7 @@ module Handsoap
363
386
  def self.parse_headers(raw)
364
387
  header = Hash.new([].freeze)
365
388
  field = nil
366
- raw.each {|line|
389
+ raw.gsub(/^(\r\n)+|(\r\n)+$/, '').each {|line|
367
390
  case line
368
391
  when /^([A-Za-z0-9!\#$%&'*+\-.^_`|~]+):\s*(.*?)\s*\z/om
369
392
  field, value = $1, $2
@@ -290,112 +290,6 @@ module Handsoap
290
290
  end
291
291
  return xml_string
292
292
  end
293
-
294
- # Parses a multipart http-response body into parts.
295
- # +boundary+ is a string of the boundary token.
296
- # +content_io+ is either a string or an IO. If it's an IO, then content_length must be specified.
297
- # +content_length+ (optional) is an integer, specifying the length of +content_io+
298
- #
299
- # This code is lifted from cgi.rb
300
- #
301
- def self.parse_multipart(boundary, content_io, content_length = nil)
302
- if content_io.kind_of? String
303
- content_length = content_io.length
304
- content_io = StringIO.new(content_io, 'r')
305
- elsif !(content_io.kind_of? IO) || content_length.nil?
306
- raise "Second argument must be String or IO with content_length"
307
- end
308
-
309
- boundary = "--" + boundary
310
- quoted_boundary = Regexp.quote(boundary, "n")
311
- buf = ""
312
- bufsize = 10 * 1024
313
- boundary_end = ""
314
-
315
- # start multipart/form-data
316
- content_io.binmode if defined? content_io.binmode
317
- boundary_size = boundary.size + "\r\n".size
318
- content_length -= boundary_size
319
- status = content_io.read(boundary_size)
320
- if nil == status
321
- raise EOFError, "no content body"
322
- elsif boundary + "\r\n" != status
323
- raise EOFError, "bad content body"
324
- end
325
-
326
- parts = []
327
-
328
- loop do
329
- head = nil
330
- if 10240 < content_length
331
- require "tempfile"
332
- body = Tempfile.new("CGI")
333
- else
334
- begin
335
- require "stringio"
336
- body = StringIO.new
337
- rescue LoadError
338
- require "tempfile"
339
- body = Tempfile.new("CGI")
340
- end
341
- end
342
- body.binmode if defined? body.binmode
343
-
344
- until head and /#{quoted_boundary}(?:\r\n|--)/n.match(buf)
345
-
346
- if (not head) and /\r\n\r\n/n.match(buf)
347
- buf = buf.sub(/\A((?:.|\n)*?\r\n)\r\n/n) do
348
- head = $1.dup
349
- ""
350
- end
351
- next
352
- end
353
-
354
- if head and ( ("\r\n" + boundary + "\r\n").size < buf.size )
355
- body.print buf[0 ... (buf.size - ("\r\n" + boundary + "\r\n").size)]
356
- buf[0 ... (buf.size - ("\r\n" + boundary + "\r\n").size)] = ""
357
- end
358
-
359
- c = if bufsize < content_length
360
- content_io.read(bufsize)
361
- else
362
- content_io.read(content_length)
363
- end
364
- if c.nil? || c.empty?
365
- raise EOFError, "bad content body"
366
- end
367
- buf.concat(c)
368
- content_length -= c.size
369
- end
370
-
371
- buf = buf.sub(/\A((?:.|\n)*?)(?:[\r\n]{1,2})?#{quoted_boundary}([\r\n]{1,2}|--)/n) do
372
- body.print $1
373
- if "--" == $2
374
- content_length = -1
375
- end
376
- boundary_end = $2.dup
377
- ""
378
- end
379
-
380
- body.rewind
381
- parts << {:head => head, :body => body.read(body.size)}
382
-
383
- # if body.kind_of? ::StringIO
384
- # parts << {:head => head, :body => body.string}
385
- # elsif body.kind_of? ::Tempfile
386
- # body.rewind
387
- # parts << {:head => head, :body => body.read(body.size)}
388
- # else
389
- # raise "body must be StringIO or Tempfile"
390
- # end
391
-
392
- break if buf.size == 0
393
- break if content_length == -1
394
- end
395
- raise EOFError, "bad boundary end of body part" unless boundary_end =~ /--/
396
- parts
397
- end
398
-
399
293
  end
400
294
 
401
295
  # Legacy/BC code here. This shouldn't be used in new applications.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: troelskn-handsoap
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.2
4
+ version: 0.5.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Troels Knak-Nielsen
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-08-13 00:00:00 -07:00
12
+ date: 2009-08-20 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies: []
15
15