troelskn-handsoap 0.5.2 → 0.5.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.
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